如果适配器 class 需要类型提示怎么办? (php)

What if adapter class needs to be type hinted? (php)

这是一个基本的工作示例:

class Test
{
    public function test()
    {
        return 'a';
    }
}

/**
 * @mixin Adapter
 */
class TestAdapter
{
    /**
     * @var Test
     */
    private $test;

    public function __construct(Test $test)
    {
        $this->test = $test;
    }

    public function __call($method, $args)
    {
        switch($method)
        {
            case 'test' :
                return 'decorated: '.$this->test();
            default :
                throw new \Exception('Unhandled method call: '.$method);
        }
    }
}

$test = new Test();
$testAdapter = new TestAdapter($test);
$testAdapter->test();

到目前为止一切顺利。但是,如果有人需要这个 Test 怎么办?如果抽象到位怎么办?

abstract class TestAbstract
{
    public abstract function t();
}

class Test extends TestAbstract
{
    public function t()
    {
        return 't';
    }

    public function test()
    {
        return 'test';
    }
}

class WannaTest
{
    public function __construct(Test $test)
    {
    }
}

这样:

$test = new Test();
$testAdapter = new TestAdapter($test);
$wannaTest = new WannaTest($testAdapter); // would throw fatal!

这行不通,因为 WannaTest 需要 Test。 当然我可以扩展 TestAdapter:

class TestAdapter extends Test
{
    public function t()
    {
        // now I cant mock it!
    }
}

但在那种情况下,如果我有 10 个抽象方法,我将不得不实现它们,即使只使用了其中一个。这样我就不能使用 __call 作为代理。所以有点臭。如何解决这个问题?删除类型提示不是一个选项...

您可以创建一个内联 class 来扩展 Test 并根据需要修饰方法。这是一个例子。

class TestDecorator //decorator sounds more appropriate
{

    public static function decorate(Test $test) {
       return new class($test) extends Test {
            private $test;
            public function __construct(Test $test) {
                 $this->test = $test;
            }
            public function test() { 
                return 'decorated: '.$this->test->test();
            }
        };

    }
}
$test = new Test(); 
$decorated = TestDecorator::decorate($test);
echo $decorated->test();

类型提示 Test 现在应该可以工作了,因为修饰的 class 确实扩展了 Test