msgbartop
PHP源码分析,Zend引擎分析,Web相关技术研究,Web技术分享–左手代码 右手诗
msgbarbottom

24 Apr 09 数组非数字键名引号的必要性

我看到过很多人操作数组的时候, 对于数组中的非数字键名不使用引号,

  $array[key] = $value;

我可以理解有些人可能会觉得这样的代码很”整洁”, 并且也能正常执行.
更甚至,如果他很”幸运的”php配置的好:

error_reporting = ~E_NOTICE

他也许永远都沉浸在自己的”整洁”风格中, 看不到任何的NOTICE提示, 也不会意识到, 他这么做, 能损失多少的性能~

来, 我们一起来看看:

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;
   }
?>

分别看运行时间(多次平均时间):
加引号的:

$ time php -f good.php

real    0m0.013s
user    0m0.005s
sys     0m0.007s

不加引号的:

$ time php -f bad.php

PHP Notice:  Use of undefined constant bad - assumed 'bad' in /home/huixinchen/tmp/bad.php
on line (此处省略999行NOTICE)
real    0m0.100s
user    0m0.020s
sys     0m0.029s

看看,差别有多大?
哦, 或许我们应该模拟一下那些”幸运的”人们的情况, 去掉花费在记录NOTICE的开销, 看看~

$ time php -f bad.php

real    0m0.037s
user    0m0.018s
sys     0m0.018s

我们可以看出, 基本上, 使用引号,和不使用引号的效率损失在3倍以上

那么, 这些效率损失到哪里去了呢?

我们分别看下, 俩个文件生成的OPCODE序列:

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

我们可以看出(其实,根据NOTICE的提示也知道), PHP会把没有引号引起来的键名当作是常量去获取, 当找不到的时候, 抛出一个NOTICE, 然后再根据”常量明”生成一个字符串, 然后再讲这个字符串做为键名继续~

聪明的你一定会想到, 可能会出现如下不可预期的错误:

define('key_name' , 'laruence');
....
//省略很多行代码
$array[key_name] = 2; //变成了 $array['laruence'] = 2;
//这样的错误, 你会很郁闷吧?

明白了么? 数组中的非数字键的键名一定要有引号啊~
哦, 还记得有人会说, 那在字符串变量替换的时候, 写引号会导致错误,
恩, 标准写法:

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

我很赞同:”be lazy”, 但是, lazy也是应该有原则的.

最后, 好的代码,不应该通过关闭error_reporting来伪装.

附注, FETCH_CONSTANT OPCODE中找不到常量的相关逻辑:

....
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;//获取"常量"名字符串
       zval_copy_ctor(&EX_T(opline->result.u.var).tmp_var);//分配空间,生成字符串
}
....

Related Posts:

Tags: , , , ,

Reader's Comments

  1. |

    优化在于一点一滴,好文章学习了。

  2. |

    支持!终于知道缘由了:)

  3. |

    学习了~

  4. |

    看来,追求高效率的code,细节很重要啊!

  5. |

    兄弟,这个
    $ time php -f bad.php

    real 0m0.037s
    user 0m0.018s
    sys 0m0.018s
    是用什么跑出来的,软件么?麻烦告知一下,感谢!

  6. |

    linux shell command.

  7. |

    好文,在下自惭形秽啊

  8. |

    呵呵,一直都在控制习惯。:)

  9. |

    $string = “variable value is {$array['key']}”;

    $string = “varialbe value is $array[key]“;

    $string = ‘variable value is ‘.$array['key'];

    以上三者比较呢?从laruence能想到的各方面

  10. |

    laruence,我这暂时没法查看opcode……

    <?php
    error_reporting(E_ALL);

    $arr['k'] = ‘V’;
    $i = 0;
    while(++$i

    time一下,第一行是最快的。第三行涉及到字符串连接,可以不考虑了。第二行,在error_reporting(E_ALL)时,也没有任何Notice,想问,此种情况(双引号字符串中,hash的key不加引号),opcode中,是否还有FETCH_CONSTANT(从运行时间判断应该是有的)?为什么?

  11. |

    为什么代码贴不全……

    error_reporting(E_ALL);

    $arr['k'] = ‘V’;
    $i = 0;
    while(++$i < 1000)
    $s = “value is {$arr['k']}”;
    //$s = “value is $arr[k]“;
    //$s = ‘value is ‘.$arr['k'];
    echo “$s\n”;

    exit;

  12. |

    学习……还好一直在用引号。

  13. |

    雪候鸟 我能加你QQ吗~ 我是搞PHP的~ 有一年多了~
    现在注重代码优化~

  14. |

    哦,原来这样,明白原理了。

  15. |

    上次见一个同事这样用,原来还有这等问题啊。。学习了。

  16. |

    还有半年就毕业了,刚把哥你的博文整理了几篇源代码分析的,打印去了。
    以前的21php是你的来吗?我记得曾经在哪见过“乙酉年识互联网,丁亥年入雅虎, 翌年入百度。 ”。

  17. |

    现在就改习惯!

Leave a Comment

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Click to hear an audio file of the anti-spam word