在PHP

时间:2016-05-02 22:20:57

标签: php reflection types decorator

我想创建一个类型安全的通用日志记录decorator

我有许多repositories(接口),需要一个装饰器来捕获它们可能抛出的异常,将它们传递给LoggerInterface的实例,然后重新抛出它们。可以为每个人创建一个专门的装饰器和测试,虽然这很麻烦(尤其是测试很好),而且我宁愿避免。

使用__call创建更通用的装饰器是第一种想到的方法。但是,这会导致对象实例无法实现其装饰的存储库接口。这在我的项目中是不行的。有没有办法告诉PHP它确实实现了这个界面,比如使用了一些魔法reflection

我已阅读" how to implement a decorator in PHP?"和" Best way to implement a decorator pattern for method result caching in PHP"这里是stackoverflow,它们概述了专用方法和通用方法,但两者都没有提供以类型安全方式执行通用方法的指示。自那些问题发布以来已经过了一段时间,所以可能情况发生了变化。我使用的是PHP 7,如果需要可以使用PHP 7.1。

PHPUnit_MockObject允许通过PHPUnit中熟悉的getMock方法调用的相同代码构建实现接口的对象。这可以是通用装饰器的基础。但是,这需要在生产代码中使用模拟库。此外,这个库internally uses eval可以完成它的工作。这使我的项目失去了资格。

3 个答案:

答案 0 :(得分:1)

另一种方法是在运行时动态创建修饰类。不幸的是PHP doesn't allow this out of the box。如果runkit扩展程序不是一个选项,您可以模拟Doctrine ORM的作用:DecoratorFactory执行以下步骤:

  • 在原始类上使用Reflection来获取所有接口及其方法,
  • 生成一个包含PHP代码的文件,该代码包含实现所有接口的修饰代理类
  • 包含生成的代理类
  • 使用原始类实例化生成的代理类并返回代理类。

请参阅http://www.doctrine-project.org/api/orm/2.4/source-class-Doctrine.ORM.Tools.EntityGenerator.html

上的示例

答案 1 :(得分:0)

您将获得的唯一真正的方法是声明一个完全实现必要接口的基础Decorator类,然后根据它进行扩展。例如:

interface FooInterface {
    function doFoo();
    function doMoreFoo();
}

class Foo implements FooInterface {
    public function __construct() {}
    public function doFoo() {}
    public function moreFoo() {}
}

class FooDecoratorBase implements FooInterface {
    protected $foo;
    public function __construct(FooInterface $foo) { $this->foo = $foo; }
    public function doFoo() { $this->foo->doFoo(); }
    public function moreFoo() { $this->foo->moreFoo(); }
}

class ExtraFooDecorator extends FooDecoratorBase {
    public function extraFoo() {}
}

虽然我不能自己引用这些细节,但是其他PHP开发者告诉我,我认为在生产代码中使用Reflections是一个坏主意,因为它们的设计考虑了测试/调试,不是表现。

答案 2 :(得分:0)

“Generic TypeParameters”在PHP (except Facebook's HHVM variation)中不可用。所以这是不可能的。 answer of @Sammitch是你要走的路,如果你真的想走这条路。

但是,您正在尝试解决其他问题。

装饰器模式充当包装器。您可以使用它,如果您想为现有类添加其他功能,而不更改类本身(或它的对象状态)

请考虑一下,你的装饰师将如何:

function DoSomeOperation() {
    try {
        return $this->decoratedObject->DoSomeOperation(); //Object is injected in Decorators Constructor
    catch (\Exception $ex) {
        Logger::Log($ex);
        throw;
    }
}

为什么不在存储库中没有装饰器的情况下进行简单操作:

function DoSomeOperation() {
    try {
        //Do The Logic
    catch (\Exception $ex) {
        Logger::Log($ex);
        throw;
    }
}

但也许PDO对象的装饰器/包装器,你真正想拥有的是什么? (ᵔᴥᵔ)

相关问题