首页 > 解决方案 > PHP - 有什么方法可以区分 unset 和 null 吗?

问题描述

考虑以下代码:

class Test {
    public $definedButNotSet;
}

$Test = new Test();

var_dump($Test->definedButNotSet=== null); // true
var_dump(isset($Test->definedButNotSet)); // false

$Test->definedButNotSet = null;
var_dump(isset($Test->definedButNotSet)); // false

在我看来,PHP 隐式地将定义的变量设置为 null。有什么办法可以规避这一点,并区分明确设置为null的变量和仅定义但未设置为任何值的变量?

更新

我基本上想看看在运行时definedButNotSet变量是否被更新。所以我对以下代码的预期结果是:

$Test = new Test();
var_dump(isset($Test->definedButNotSet)); // false

$Test->definedButNotSet = null;
var_dump(isset($Test->definedButNotSet)); // true expected here but php returns false

差异确实很重要的实际用例,基本上这也是我的用例:更新数据库中的行时,我想更新表的行,用户仅在调用更新方法时才更改。为此,我必须知道,用户是否隐式修改了表示表中行的类中的任何变量。

我正在运行一个自定义 ORM,目前它失败了,如果我在数据库中插入一行,其中一列将 default_timestamp 方法设置为默认值,并且在同一运行时,我尝试再次更新同一行,因为数据库设置值没有反映在我的类实例中,因此在更新时 PHP 向他发送他的值为 null,这是不允许的。

标签: php

解决方案


稍微挑战一下问题的框架,您要做的是区分对同一属性的两种类型的写入:一种由您的 ORM 自动触发,另一种由用户手动触发。将在插入时提供默认值的值是其中的一个子集,但从数据库检索的值也可能需要与用户提供的值进行不同的处理(例如,跟踪是否需要更新)。

做到这一点的方法是将属性设为私有,并提供 getter 和 setter,或者使用魔法__get__set方法(以及@propertyIDE 使用的注释)模拟属性访问器。然后,您可以在初始化代码之外存储已写入哪些属性的映射。

一个简单且可能有问题的实现来显示总体思路:

/**
 * This doc-block will be read by IDEs and provide auto-complete
 * @property int|null $id
 * @property string|null $name
 */
class Test {
    private ?int $id = null;
    private ?string $name = null;

    private array $updatedFields = [];

    /**
     * @internal Only to be called by the ORM
     */
    public function construct(?int $id, ?string $name) {
        $this->id = $id;
        $this->name = $name;
    }

    public function __get(string $property) {
        if ( property_exists($property, $this) ) {
             return $this->$property;
        }
        else {
             throw new LogicError("No such property '$property'");
        }
    }

    public function __set(string $property, $newValue) {
        if ( property_exists($property, $this) ) {
             $this->$property = $newValue;
             $this->updatedFields[ $property ] = true;
        }
        else {
             throw new LogicError("No such property '$property'");
        }
    }

    /**
     * Standard meaning of isset() for external code 
     */
    public function __isset(string $property) {
        if ( property_exists($property, $this) ) {
             return isset($this->$property);
        }
        else {
             throw new LogicError("No such property '$property'");
        }
    }

    /**
     * Special function for ORM code to determine which fields have changed
     */
    public function hasBeenWritten(string $property): bool {
        if ( property_exists($property, $this) ) {
             return $this->updatedFields[ $property ] ?? false;
        }
        else {
             throw new LogicError("No such property '$property'");
        }
    }
}

推荐阅读