这是使用 php 7 个断言的 Class 不变性的有效示例吗?

Is this a valid example of Class invariances using php 7 asserts?

我正在尝试更好地理解里氏原理使用的 Class 不变性。

我知道某些语言 like D have native support to invariant,但是,在 PHP 中使用断言,我尝试结合魔术方法和断言:

<?php

class Person {
    protected string $name;
    protected string $nickName;
    protected function testContract(){
        assert(($this->name != $this->nickName));
    }
    public function __construct(string $name, string $nickName){
        $this->name = $name;
        $this->nickName = $nickName;
    }
    public function __set($name, $value){
        $this->testContract();
        $this->$name = $value;
    }
    public function __get($name){
        $this->testContract();
        return $this->$name;
    }
}

class GoodPerson extends Person {
    public function getFullName(){
        return $this->name." ".$this->nickName. "!!!";
    }
}

class BadPerson extends Person {
    protected function testContract(){
        assert(($this->name != ""));
    }
}

$gp = new GoodPerson("João", "Joãozinho");
echo $gp->nickName;
echo $gp->getFullName();
$bp = new BadPerson("João", "João");
echo $bp->nickName;

我可以使用断言创建合约吗?

没有

来自PHP Documentation

  • 断言应仅用作调试功能
  • 断言不应用于输入参数检查等正常运行时操作

BadPerson 是 Liskov 的 Class 继承不变性违规的有效示例吗?

Preconditions cannot be strengthened in a subtype.

但是你的代码没有任何意义

首先,只有在您尝试设置或获取动态 属性 时才会调用您的 testContract 方法,它会检查您通过 constructor

public function __set($name, $value){
    $this->testContract();
    $this->$name = $value;
}

这里基本上是测试构造函数的参数,但是在魔术方法中(__set)

因此,为了使检查有效,您需要像这样调用 __set

$gp = new BadPerson("João", "Joãozinho");
$gp->name = ''; // This line here invokes __set magic method

所以你真正需要做的是摆脱 testContract 并将检查放在基础 class 构造函数中。为什么?因为您的属性是 protected,所以客户设置它们的唯一机会是通过构造函数

public function __construct(string $name, string $nickName){
    if ($name != "" && $name != $nickName)
        throw new Exception('Name must not be empty and must not equal Nickname');

    $this->name = $name;
    $this->nickName = $nickName;
}

GoodPerson 是 Liskov 的 Classe 不变性的有效示例吗?