最近写模板又遇到问题了,同样的代码在测试机上正常工作,换到线上又出错了,后来一看,是php7的锅......

php7+中isset()函数在判断类成员是否设置时的行为与php5.6有所不同。 我们先来看看php的官方git commit message


PHP 7 has fixed a bug with __isset which affects both the native isset and empty methods. This causes specific issues 
with checking isset or empty on relations in Eloquent. In PHP 7 checking if a property exists on an unloaded relation, 
for example isset($this->relation->id) is always returning false because unlike PHP 5.6, PHP 7 is now checking the offset of each attribute before chaining to the next one. In PHP 5.6 it would eager load the relation 
without checking the offset. This change brings back the intended behavior of the core Eloquent model __isset method 
for PHP 7 so it works like it did in PHP 5.6.

For reference, please check the following link, specifically Nikita Popov's comment (core PHP dev) - https://bugs.php.net/bug.php?id=69659

假设我们有如下php代码

<?php

class Post
{
    protected $attributes = ['foo' => 'bar'];

    public function __get($key)
    {
        if (isset($this->attributes[$key])) {
            return $this->attributes[$key];
        }
    }
}

$post = new Post();
echo isset($post->foo);  // false

我们会发现无论如何isset总是返回false
因为变量foo不是类Post的成员
也就是说,PHP7开始,isset执行前不会先执行->
也就是__get()函数

当然也可使用魔术方法__isset解决此问题


<?PHP
class Post
{
    protected $attributes = ['foo' => 'bar'];

    public function __get($key)
    {
        if (isset($this->attributes[$key])) {
            return $this->attributes[$key];
        }
    }

    public function __isset($key)
    {
        if (isset($this->attributes[$key])) {
            return true;
        }

        return false;
    }
}

$post = new Post();
echo isset($post->foo);   //true

此时我们可以看到isset()返回了true
因为我们对类Post执行isset()时调用了我们定义的__isset()函数,而__isset()函数中的isset()是可以访问foo的,故此处返回true

当遇到isset($widget->fields->$var)
这样的写法时
php5.6 和以前会先执行$widget->fields->$var
php7则会在每次调用前执行isset
于是typecho常用的判断自定义字段是否为空的代码
isset($widget->fields->$var)
总是返回false
网上大多数判断typecho自定义字段是否设置的教程都直接使用isset函数,当然会在php7的线上环境下出问题啊。
从兼容性角度考虑,建议还是写

if ($widget->fields->$var !== NULL){
    //code...
}

比较保险。