PHP trait:有没有一种正确的方法来确保使用ttraits的类扩展了包含某种方法的超类?

2022-08-30 22:25:51

PHP 手动 http://php.net/manual/en/language.oop5.traits.php 状态的示例 #2

<?php
class Base {
    public function sayHello() {
        echo 'Hello ';
    }
}

trait SayWorld {
    public function sayHello() {
        parent::sayHello();
        echo 'World!';
    }
}

class MyHelloWorld extends Base {
    use SayWorld;
}

$o = new MyHelloWorld();
$o->sayHello();
?>

这是正确的代码,但在该上下文中使用是不安全的。假设我写了我自己的“hello world”类,它不继承任何其他类:parent::

<?php
class MyOwnHelloWorld
{
    use SayWorld;
}
?>

在我调用该方法之前,此代码不会产生任何错误。这很糟糕。sayHello()

另一方面,如果特征需要使用某种方法,我可以将此方法编写为抽象方法,这很好,因为它可以确保在编译时正确使用该特征。但这不适用于父类:

<?php
trait SayWorld
{
    public function sayHelloWorld()
    {
        $this->sayHello();
        echo 'World!';
    }

    public abstract function sayHello(); // compile-time safety

}

所以我的问题是:有没有办法确保(在编译时,而不是在运行时)使用某种特征的类将有方法?parent::sayHello()


答案 1

没有。实际上,这个例子非常糟糕,因为引入特征的目的是在不依赖继承的情况下将相同的功能引入许多类,并且使用不仅要求类具有父级,而且还应该具有特定的方法。parent

另外,调用在编译时不会被检查,你可以定义一个简单的类,它不会在其方法中使用父调用来扩展任何东西,在调用这些方法之一之前,它将工作。parent


答案 2

您可以检查$this是否扩展了特定类或实现了特定接口:

interface SayHelloInterface {
    public function sayHello();
}

trait SayWorldTrait {
    public function sayHello() {
        if (!in_array('SayHello', class_parents($this))) {
            throw new \LogicException('SayWorldTrait may be used only in classes that extends SayHello.');
        }
        if (!$this instanceof SayHelloInterface) {
            throw new \LogicException('SayWorldTrait may be used only in classes that implements SayHelloInterface.');
        }
        parent::sayHello();
        echo 'World!';
    }
}

class SayHello {
    public function sayHello() {
        echo 'Hello ';
    }
}

class First extends SayHello {
    use SayWorldTrait;
}

class Second implements SayHelloInterface {
    use SayWorldTrait;
}

try {
    $test = new First();
    $test->sayHello(); // throws logic exception because the First class does not implements SayHelloInterface
} catch(\Exception $e) {
    echo $e->getMessage();
}

try {
    $test = new Second();
    $test->sayHello(); // throws logic exception because the Second class does not extends SayHello
} catch(\Exception $e) {
    echo $e->getMessage();
}

推荐