那些Python党踩过的PHP坑

一看题目貌似本文要准备吐槽PHP,但遇到“坑”主要是因为个人经验不足。

JSON反序列化 json_decode

函数 json_decode 默认反序列化的结果是对象。Python党在做PHP开发用到这个方法时,很可能会跳进这个坑,认为结果应该是个数组,因为Python中json.loads返回的是一个字典。 json_decode 的第二个参数 $assoc 可用来指定反序列化的结果为数组。

文档:http://php.net/manual/zh/function.json-decode.php


数组序列化

Python党初学PHP,可能类比于Python的列表和字典,认为PHP中明确区分索引数组和关联数组。但:

PHP 实际并不区分索引数组和关联数组,都是一种有序映射。

虽然很多时候索引数组和关联数组在表现上是不一样的,比如对以下两个数组进行序列化:

<?php
$arrA = array('a', 'b', 'c');
echo json_encode($arrA) . "\n";

$arrB = array('a' => 1, 'b' => 2, 'c' => 3);
echo json_encode($arrB) . "\n";

结果为:

["a","b","c"]
{"a":1,"b":2,"c":3}

如果把序列化的结果作为JSON输出到前端,对于JS来说,是两种不同的数据结构:数组和对象。

其中 $arrA 的初始化值是 array(0 => 'a', 1 => 'b', 2 => 'c') 的简写方式。

再来看另一段程序:

$arrA = array('a', 'b', 'c');
echo json_encode($arrA) . "\n";

$arrA = array(0 => 'a', 1 => 'b', 2 => 'c');
echo json_encode($arrA) . "\n";

$arrA = array(1 => 'a', 2 => 'b', 3 => 'c');
echo json_encode($arrA) . "\n";

$arrA = array(0 => 'a', 2 => 'b', 3 => 'c');
echo json_encode($arrA) . "\n";

输出为:

["a","b","c"]
["a","b","c"]
{"1":"a","2":"b","3":"c"}
{"0":"a","2":"b","3":"c"}

从输出可以知道:仅当数组的key是从0开始的整数,并且key连续不间断,对其序列化的结果才是JS中的数组,也就是通常认为的PHP索引数组。

这个坑可能在某些时候不小心就掉进去了,比如我曾对一个索引数组应用array_filter函数操作,然后json_encode序列化输出到前端,认为前端反序列化出来的是一个数组,没想到却是个对象。

函数array_filter的文档说明:

array array_filter ( array $input [, callable $callback = "" ] )

依次将 input 数组中的每个值传递到 callback 函数。如果 callback 函数返回 TRUE,则 input 数组的当前值会被包含在返回的结果数组中。数组的键名保留不变。

注意是对值进行filter,结果中值原本对应的key不变,而不是使用新的key。

文档:http://php.net/manual/zh/language.types.array.phphttp://php.net/manual/zh/function.array-filter.php


引用传递

先来看一段代码:

<?php
class CommonUtils {
    static $TASK_INTERVAL_SEMANTIC_INT_MAPPER = array(
        'PER_DAY' => 86400,
        'TWO_DAY' => 172800,
        'PER_WEEK' => 604800,
        'TWO_WEEK' => 1209600,
        'PER_MONTH' => 2592000,
    );
}
$ha = array_pop(array_keys(CommonUtils::$TASK_INTERVAL_SEMANTIC_INT_MAPPER));
var_dump($ha);

error_reporting = E_ALL 时,上面的代码会抛出一个严格模式错误:Only variables should be passed by reference 。这是为什么呢?

PHP文档中说明只有三种内容可以通过引用传递:

  • 变量,例如 foo($a)
  • New 语句,例如 foo(new foobar())
  • 从函数中返回的引用,例如:
<?php
function &bar()
{
    $a = 5;
    return $a;
}
foo(bar());
?>

任何其它表达式都不能通过引用传递,结果未定义。

上面代码中 $ha = array_pop(array_keys(CommonUtils::$TASK_INTERVAL_SEMANTIC_INT_MAPPER)); 一行,调用了 array_poparray_keysarray_pop 的函数签名为:mixed array_pop ( array &$array ) ,但 array_keys 函数调用返回的是一个array值,不属于上面列出三种内容,所以会抛错。但 这个情况下这种错误 的级别不同版本的PHP也不一样:

  • 自 PHP 5.0.5 起导致致命错误
  • 自 PHP 5.1.1 起导致严格模式错误
  • 自 PHP 7.0 起导致 notice 信息

但如果直接给 array_pop 传递一个数组值,则会导致致命错误,如:

<?php
array_pop(array(1, 2, 3, 4));

会抛出错误:

PHP Fatal error:  Only variables can be passed by reference in /Users/xiayf/test.php on line 2

文档:http://php.net/manual/zh/language.references.php