Press "Enter" to skip to content

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

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

  $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);//分配空间,生成字符串
}
....

27 Comments

  1. andy
    andy November 27, 2020

    php8 已经强制了

  2. 夜雨声烦
    夜雨声烦 November 27, 2015

    请问为什么在字符串中对于单引号的键名用复杂写法会解析错误,我一直没找到比较好的解释。

  3. 我 浏览在线超过 3 今天小时,但我从来没有发现任何有趣的文章,像你这样的。 这是够漂亮值得我。 在我看来 ,如果所有网站主像你一样和博客取得了良好的内容,则网络将多有用的比以往任何时候。 ,并与
    我一直在探索为一点点任何高品质或博客 帖子在此 排序
    区 。探索在雅虎我最后偶然发现了这个网站 。 留学 这个信息所以我很 高兴到展示 我 一个令人难以置信 良好不可思议的感觉,我发现
    刚我需要的东西。我最 肯定无疑将使务必到唐吨 把你的心 网站 提供 一目了然 定期

  4. zhao
    zhao March 7, 2012

    为什么我测试的时候感觉两者差不到好多呢。。。

  5. gan
    gan November 1, 2011

    确实,看了这个以后,发现在开发环节打开日志是多么的必要。

  6. fortruth
    fortruth September 18, 2009

    现在就改习惯!

  7. cnan
    cnan September 12, 2009

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

  8. reeze
    reeze June 11, 2009

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

  9. jackywdx
    jackywdx May 16, 2009

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

  10. 阳阳
    阳阳 May 11, 2009

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

  11. zhangYufeng
    zhangYufeng May 4, 2009

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

  12. Microwish
    Microwish April 30, 2009

    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(从运行时间判断应该是有的)?为什么?

    • Microwish
      Microwish April 30, 2009

      为什么代码贴不全……
      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;

  13. Microwish
    Microwish April 30, 2009

    $string = “variable value is {$array[‘key’]}”;
    $string = “varialbe value is $array[key]”;
    $string = ‘variable value is ‘.$array[‘key’];
    以上三者比较呢?从laruence能想到的各方面

  14. zwws
    zwws April 26, 2009

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

  15. zvaly
    zvaly April 26, 2009

    好文,在下自惭形秽啊

  16. 易邻
    易邻 April 25, 2009

    兄弟,这个
    $ time php -f bad.php
    real 0m0.037s
    user 0m0.018s
    sys 0m0.018s
    是用什么跑出来的,软件么?麻烦告知一下,感谢!

    • 雪候鸟
      雪候鸟 April 25, 2009

      linux shell command.

  17. 思臣
    思臣 April 25, 2009

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

  18. linvo
    linvo April 24, 2009

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

  19. rzhome
    rzhome April 24, 2009

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

Comments are closed.