PHP7中的isset

升級 php7 後 isset 不太對了

 

公司升級 php7 後出現了一個問題

類似這樣 isset($post->user->name) 始終爲 false

之前的 php 5.6 就很正常

laravel 版本是 5.1.35(很久沒升級了)

先看看 isset

isset 用來檢測變量是否設置

首先我們來看官方的一個例子

大致上是下面這個意思

<?php
class Post
{
 protected $attributes = ['content' => 'foobar'];
 public function __get($key)
 {
 if (isset($this->attributes[$key])) {
 return $this->attributes[$key];
 }
 }
}
$post = new Post();
echo isset($post->content);  // false

上面這個例子將永遠返回 false,因爲 foo 並不是 Post 的屬性,而是 __get 取出來的

魔術方法 __isset

那麼怎麼解決上面那個問題呢?使用魔術方法

<?PHP
class Post
{
 protected $attributes = ['content' => 'foobar'];
 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->content);   //true

類似 Eloquent 的例子

看着 laravel 5.1.35 的代碼,我們自己寫一個簡單的例子

先有一個 Model,簡單的實現。__get,__set,__isset

class Model
{
 // 存放屬性
 protected $attributes = [];
 // 存放關係
 protected $relations = [];
 public function __get($key)
 {
 if( isset($this->attributes[$key]) ) {
 return $this->attributes[$key];
 }
 // 找到關聯的對象,放在關係裏面
 if (method_exists($this, $key)) {
 $relation = $this->$method();   
 return $this->relations[$method] = $relation;
 }
 }
 public function __set($k, $v)
 {
 $this->attributes[$k] = $v;
 }
 public function __isset($key)
 {
 if (isset($this->attributes[$key]) || isset($this->relations[$key])) {
 return true;
 }
 return false;
 }
} 

然後我們定義一個 Post Moel 和一個 User Moel

class Post extends Model
{
 protected function user()
 {
 $user = new User();
 $user->name = 'user name';
 return $user;
 }
}
class User extends Model
{
}

好了來驗證一下 isset

$post = new Post();
echo 'isset 發帖用戶:';
echo isset($post->user) ? 'true' : 'false';  // false
echo PHP_EOL;
echo 'isset 發帖用戶的名字:';
echo isset($post->user->name) ? 'true' : 'false';  // false
echo PHP_EOL;
echo '發帖用戶的名字:';
echo $post->user->name;    // user name
echo PHP_EOL;
echo '再次判斷 isset 發帖用戶的名字:';
echo isset($post->user->name) ? 'true' : 'false';   // true
echo PHP_EOL;

答案

分析上面的結果,感覺像是 php 7 isset 方法對對象的判斷有了變化,如果先執行一次,$post->user->name,也就是將 user 放在 post 的 relations 中,這樣 isset ($post->user) 爲 true,隨後 isset ($post->user->name) 才爲 true。

最後在 Eloquent model 的 git log 中 找到了答案,

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

大致上是 php7 isset 判斷的時候,會依次判斷。php5.6 則會預加載關係。其實 laravel 也早在 5 月份就做了相關的處理,所以升級 laravel 後,自然也就沒有這個問題了。

更多學習內容請訪問:

騰訊T3-T4標準精品PHP架構師教程目錄大全,只要你看完保證薪資上升一個臺階(持續更新)

以上內容希望幫助到大家,很多PHPer在進階的時候總會遇到一些問題和瓶頸,業務代碼寫多了沒有方向感,不知道該從那裏入手去提升,對此我整理了一些資料,包括但不限於:分佈式架構、高可擴展、高性能、高併發、服務器性能調優、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql優化、shell腳本、Docker、微服務、Nginx等多個知識點高級進階乾貨需要的可以免費分享給大家,需要的可以加入我的官方羣點擊此處

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章