Press "Enter" to skip to content

Why Non-Numeric Array Keys Must Be Quoted

I've seen many people who, when working with arrays, don't quote the non-numeric key names:

  $array[key] = $value;

I can understand that some people might find such code "tidy", and it does run fine.
Even more so, if they are "lucky" enough to have PHP configured like this:

error_reporting = ~E_NOTICE

they may forever stay immersed in their "tidy" style, never seeing any NOTICE, and never realizing how much performance they lose by doing this.
Come, let's look at it together:

good.php:
<?php
   $array = array();
   $i = 0;
   while(++$i < 1000){
       $array['good'] = 2;
   }
?>
bad.php:
<?php
   $array = array();
   $i = 0;
   while(++$i < 1000){
       $array[good] = 2;
   }
?>

Now look at the run times (averaged over multiple runs):
With quotes:

$ time php -f good.php
real    0m0.013s
user    0m0.005s
sys     0m0.007s

Without quotes:

$ time php -f bad.php
PHP Notice:  Use of undefined constant bad - assumed 'bad' in /home/huixinchen/tmp/bad.php
on line (999 lines of NOTICE omitted here)
real    0m0.100s
user    0m0.020s
sys     0m0.029s

See how big the difference is?
Oh, perhaps we should simulate the situation of those "lucky" people — remove the overhead spent recording the NOTICE, and see.

$ time php -f bad.php
real    0m0.037s
user    0m0.018s
sys     0m0.018s

We can see that, basically, the efficiency loss between using quotes and not using quotes is more than threefold.
So where does this efficiency loss go?
Let's look at the OPCODE sequences generated by the two files:
good.php :

filename:       /home/huixinchen/tmp/good.php
compiled vars:  !0 = $array, !1 = $i
line     #  op                           fetch          ext  return  operands
-------------------------------------------------------------------------------
   2     0  INIT_ARRAY                                       ~0
         1  ASSIGN                                                   !0, ~0
   3     2  ASSIGN                                                   !1, 0
   4     3  PRE_INC                                          $3      !1
         4  IS_SMALLER                                       ~4      $3, 1000
         5  JMPZ                                                     ~4, ->9
   5     6  ZEND_ASSIGN_DIM                                          !0, 'good'
         7  ZEND_OP_DATA                                             2, $6
   6     8  JMP                                                      ->3
   8     9  RETURN                                                   1
        10* ZEND_HANDLE_EXCEPTION

bad.php :

filename:       /home/huixinchen/tmp/bad.php
compiled vars:  !0 = $array, !1 = $i
line     #  op                           fetch          ext  return  operands
-------------------------------------------------------------------------------
   2     0  INIT_ARRAY                                       ~0
         1  ASSIGN                                                   !0, ~0
   3     2  ASSIGN                                                   !1, 0
   4     3  PRE_INC                                          $3      !1
         4  IS_SMALLER                                       ~4      $3, 1000
         5  JMPZ                                                     ~4, ->10
   5     6  FETCH_CONSTANT                                   ~5      'bad'
         7  ZEND_ASSIGN_DIM                                          !0, ~5
         8  ZEND_OP_DATA                                             2, $7
   6     9  JMP                                                      ->3
   8    10  RETURN                                                   1
        11* ZEND_HANDLE_EXCEPTION

We can see (and indeed the NOTICE tells us as much) that PHP treats an unquoted key name as a constant to be fetched; when it can't find one, it throws a NOTICE, then generates a string from the "constant name", and continues using that string as the key.
You, being clever, will surely realize this can lead to the following unpredictable error:

define('key_name' , 'laruence');
....
//many lines of code omitted
$array[key_name] = 2; //becomes $array['laruence'] = 2;
//An error like this would leave you quite puzzled, wouldn't it?

Get it? Non-numeric key names in arrays must always be quoted.
Oh, I remember some people will say that when interpolating in a double-quoted string, writing quotes causes an error.
Well, the standard way to write it:

$string = "variable value is {$array['key']}"

I'm all for "be lazy" — but being lazy should still have its principles.
Finally, good code should not be disguised by turning off error_reporting.
As a footnote, here is the relevant logic in the FETCH_CONSTANT OPCODE for when the constant isn't found:

....
if (!zend_get_constant(opline->op2.u.constant.value.str.val,
     opline->op2.u.constant.value.str.len, &EX_T(opline->result.u.var).tmp_var TSRMLS_CC)) {
       zend_error(E_NOTICE, "Use of undefined constant %s - assumed '%s'",
                opline->op2.u.constant.value.str.val,
                opline->op2.u.constant.value.str.val);
       EX_T(opline->result.u.var).tmp_var = opline->op2.u.constant;//get the "constant" name string
       zval_copy_ctor(&EX_T(opline->result.u.var).tmp_var);//allocate space, generate the string
}
....

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.