PageRenderTime 82ms CodeModel.GetById 40ms app.highlight 6ms RepoModel.GetById 35ms app.codeStats 0ms

/_posts/2015-12-13-php-empty-keys.md

https://github.com/robertopedroso/robertopedroso.github.com
Markdown | 45 lines | 33 code | 12 blank | 0 comment | 0 complexity | 647626a0d65cf245be72700f5ab44ce4 MD5 | raw file
 1---
 2layout: post
 3title: PHP Gotchas: Null Array Keys
 4---
 5
 6At my current job, we provide an Account API through which customers can modify their account settings, including date and timezone settings. One morning, a bug report comes in saying that the timezone setting cannot be changed. Every other field appears to work, but the timezone setting never updates.
 7
 8After a few hours of digging, I had ruled out every bit of code between the API endpoint and the moment the SQL update query is generated. CodeIgniter provides a simple ActiveRecord implementation for generating queries from associative arrays:
 9
10```php
11$table_id = 12345;
12$data = ['name' => 'Roberto Pedroso', 'timezone' => 'EST'];
13$this->db->where('id', $table_id);
14$this->db->update('mytable', $fields);
15```
16
17Their QueryBuilder class is well tested. I was doubtful that a CI bug was to blame. So I inspected the output, which was producing T-SQL like this:
18
19```sql
20UPDATE mytable
21SET name = 'Roberto Pedroso'
22WHERE id = 12345
23```
24
25Why isn't timezone in there? Is this somehow a CI bug? That's when I inspected the array being used to generate the query.
26
27```php
28$data = [
29    'name' => 'Roberto Pedroso',
30    '' => 'New York',
31    'timezone' => 'EST'
32];
33```
34
35I was dumbstruck -- I had no idea PHP allows array keys to be empty strings. I eventually discovered the cause, which only doubled my bewilderment. See, all our Account API input passes through a customized input sanitizer. It eventually performs an operation like this:
36
37```php
38$fields[$key] = $value;
39```
40
41Simple enough, right? Except there was a bug related to an edge case that caused $key to never get set. PHP threw a warning but happily chugged along, substuting NULL for our key. It then cast null to an empty string and added that element to the array, resulting in the bizarre array we saw before.
42
43And what did CodeIgniter do? The best it could: rather than produce invalid SQL, it simply stopped transcending that array upon seeing an empty key and spit out a query.
44
4515 minutes later I had a patch, but the bitter taste of PHP's betrayal persisted...