最新消息:

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

PHP admin 2949浏览 0评论

本文参考 http://www.laruence.com/2009/04/24/695.html

在数组中,数组的键key可以是integer或者string,许多人在使用非数字键的时候,习惯不给键加引号,比如:

<?php
$a = array("a"=>"aa","b"=>"bb");
echo $a[a]; //没加引号 $a["a"]
?>

这段代码可以执行,但是会报NOTICE,Notice: Use of undefined constant a – assumed ‘a’ 。但是许多人在php.ini配置文件中设置了 error_reporting = E_ALL & ~E_NOTICE ,导致看不到这个NOTICE。

        不加引号情况下,不谈代码NOTICE带来的性能问题,那我们来看看会带来什么不可预期的错误。
        先看opcode序列,来看看两段代码执行的操作。
        加引号的情况:
        
  
      未加引号情况:不加引号的情况下,多了一步 FETCH_CONSTANT操作,也就是查找常量。
       我们可以看出,其实根据NOTICE提示也可以得到,    PHP会把没加引号的键当做常量去获取,如果找不到,抛出NOTICE,然后在根据常量名生成一个字符串,然后继续再作为数组的键。
这个时候就会导致一个不可预期的问题,就是如果你define定义了一个同名常量,会出现什么呢:
<?php
     $array = array('a'=>"aaa" , "b"=>"bbb" , "c"=>"ccc");
     define("define_key" , "a"); //定义常量 define_key ,值为 a
     echo $array[define_key]; // 输出 aaa</div>
     $array[define_key] = "change";</div>
     echo $array["a"]; // 输出 "change"
?>
数组非数字键一定要加引号!
     但是还没结束,又会遇到一个问题,当我们在字符串中引入数组的时候,此时加上引号则会语法报错,比如:
<?php
     $array = array('a'=>"aaa" , "b"=>"bbb" , "c"=>"ccc");
     $str = "variable value is $array['a']"; // 会报错
     // 语法错误
     // Parse error: syntax error, unexpected T_ENCAPSED_AND_WHITESPACE, expecting T_STRING or T_VARIABLE or T_NUM_STRING
     $str1 = "variable value is {$array['a']}"; // 这样就不会报错了
     $str2 = "variable value is $array[a]"; //在字符串中引入非数字键,不加引号也不会报错
?>
———————————————————————————————————————-
来, 我们一起来看看:
    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);//分配空间,生成字符串
    }
    ....

转载请注明:jinglingshu的博客 » php数组非数字键名加引号的必要性!

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址