ahmedzidan/php-core

禁用 PHP 对象的默认行为。

1.0.1 2022-03-20 21:05 UTC

This package is auto-updated.

Last update: 2024-09-21 03:22:09 UTC


README

Latest Stable Version License Travis CI build status AppVeyor CI build status

Coveralls branch Scrutinizer Code Climate: GPA Total Downloads

核心

核心 库提供了 PHP 核心中缺失的最基本功能(并且很可能永远不会加入其中):禁用默认 PHP 对象行为的辅助函数(以 traits 的形式)。

安装

打开终端,进入您的项目目录,并执行以下命令以将此软件包添加到您的依赖项

composer require fleshgrinder/core

此命令要求您已全局安装 Composer,如 Composer 文档中的安装章节所述。

使用

解除魔法

可以使用 Disenchant trait 来禁用对魔法方法 __get__isset__set__unset 的支持。PHP 中的每个对象默认都支持这些方法以进行动态属性管理。然而,在几乎所有情况下,这些特性都是不希望出现的。尽管支持它们不会导致严重错误,但可能会导致奇怪的行为。特别是在值对象的情况下,当使用 PHP 的等号运算符(==)确定相等性时。

假设我们有一个简单的值对象

final class ValueObject {
    private $value;
    public function __construct($value) {
        $this->value = $value;
    }
}

使用等号运算符确定两个实例是否相等非常直接,比较也是一样

$v0 = new ValueObject(0);
$v1 = new ValueObject(1);

var_dump(
    $v0 < $v1,  // true
    $v0 <= $v1, // true
    $v0 == $v1, // false
    $v0 >= $v1, // false
    $v0 > $v1   // false
);

如果我们现在给 $v0 添加一个属性,结果将不同

$v0 = new ValueObject(0);
$v0->x = 42;
$v1 = new ValueObject(1);

var_dump(
    $v0 < $v1,  // false
    $v0 <= $v1, // false
    $v0 == $v1, // false
    $v0 >= $v1, // true
    $v0 > $v1   // true
);

当然,这些运算符不应该被使用,而应该使用专门的函数,如 EqualableComparable,但这个 trait 在几乎不增加成本的情况下为整个程序增加了额外的安全性。

这个 trait 非常有用的其他情况是如果您的对象依赖于它们拥有的属性。例如,如果某处使用了 get_object_vars。然而,请注意,并不是所有奇怪的行为都能通过引入这个 trait 来修复。例如,仍然可以通过精心制作的 unserialize 字符串动态添加属性。这是因为在这种情况下 PHP 不会调用这些魔法方法,而是直接将属性添加到对象中。请在 __wakeupunserialize 方法中验证您的对象。

不可克隆

可以使用 Uncloneable trait 来禁用在客户端代码中支持 clone 关键字。对于由于技术原因无法克隆的对象,例如包含某种资源(如数据库连接)的对象,或者因为它们没有意义(如任何不可变实现,如值对象)而不应该被克隆的对象,这是一个好主意。

在这个 trait 中,魔法方法 __clone 被定义为 finalprotected,这确保了使用该 trait 的类的子类无法更改该契约。同时,它允许使用该 trait 的类在内部使用 clone 功能提供写时复制支持,而不会引起更改;如下例所示

final class URI {
    use Fleshgrinder\Core\Uncloneable;

    // ...

    public function withFragment(string $fragment): URI {
        $clone = clone $this;
        $clone->fragment = $fragment;
        return $clone;
    }
}

另一个有趣的用例是与建造者模式配对的友元类,用于提供不可变实体。

abstract class EntityFriend {
    use Fleshgrinder\Core\Uncloneable;

    protected $value;

    protected function setValue(T $value): void {
        $this->value = $value;
    }
}

final class Entity extends EntityFriend {
    public function getValue(): T {
        return $this->value;
    }
}

final class EntityBuilder extends EntityFriend {
    private $entity;

    public function __construct() {
        $this->entity = new Entity;
    }

    public function build(): Entity {
        return clone $this->entity;
    }

    public function setValue(T $value): void {
        $this->entity->setValue($value);
    }
}

不可构造

可以使用Unconstructable特性来禁用客户端代码中对new关键字的调用。几乎总是建议禁用多次构造函数调用禁用多次构造函数调用并强制实际构造函数方法的参数不变。当然,类需要命名构造函数,否则构建将无法进行。

final class SomeClass {
    use Fleshgrinder\Core\Unconstructable;

    public static function new(): self {
        return new static;
    }
}

另一个用例是final abstract类,这在PHP中是不可用的。

final class AbstractFinalClass {
    use Fleshgrinder\Core\Unconstructable;

    public static function f() { }

    public static function f′() { }

    // ...
}

不可变

Immutable是所有其他特性的组合,仅为了方便而提供。它最好用于任何类型的不可变类,正如其名称所暗示的那样。

测试

打开终端,进入项目目录,并执行以下命令以使用您本地安装的PHP可执行文件运行PHPUnit测试。

make

如果系统上没有make,您也可以执行以下两个命令。

composer install
composer test