依赖注入没有所有方法的对象

时间:2018-01-10 22:09:55

标签: php dependency-injection

所以上课:

class ToBeUsed
{
    private $a;

    public function setSomething($a)
    {
        $this->a = $a;
    }

    public function getSomething()
    {
        return $this->a;
    }
}

它的beign创建和更新:

$obj = new ToBeUsed();
$obj->setSomething('a');

并传递给另一个对象

class UseIt
{
    /**
     * @var ToBeUsed
     */
    private $obj;

    public function __construct(ToBeUsed $obj)
    {
        $this->obj = $obj;
    }

    public function work()
    {
        $this->obj->getSomething();
        $this->obj->setSomething(); //// !!!!! THIS IS BAD!
    }
}

现在是一个经典的DI示例,除了传递的对象应该是" dulled" - 只允许使用某些方法。例如。允许使用getSomething(),但setSomething()不允许。什么模式/实践可以逃脱它?曾经有friend个类是C,但它的Php ......

2 个答案:

答案 0 :(得分:1)

我可能会对Interfaces做一些事情,但它并没有阻止使用方法表单。但是"他们" (无论他们是谁)将在接口之外使用$obj

像这样:

class ToBeUsed implements ToBeUsedInterface
{
    private $a;

    public function getSomething()
    {
        return $this->a;
    }

    public function setSomething($a)
    {
        $this->a = $a;
    }
}

interface ToBeUsedInterface{
    public function getSomething(); 
}

class UseIt
{
    /**
     * @var ToBeUsed
     */
    private $obj;

    public function __construct(ToBeUsedInterface $obj)
    {
        $this->obj = $obj;
    }

    public function work()
    {
        $this->obj->getSomething();
        $this->obj->setSomething(); //This now exists outside of the interface for $obj
    }
}

就IDE而言,这也会阻止方法自动完成。

我能想到的唯一另一件事(除了另一个答案)是将方法设置为protected,然后使用ReflectionMethod来改变生存能力,当你想要使用它时。

另一个选项是使用反射

class ToBeUsed
{
    private $a;

    public function getSomething()
    {
        return $this->a;
    }

    protected function setSomething($a)
    {
        $this->a = $a;
    }
}

$ToBeUsed = new ToBeUsed();

$ReflectionMethod = new ReflectionMethod($ToBeUsed, 'setSomething');
$ReflectionMethod->setAccessible(true);
$ReflectionMethod->invoke($ToBeUsed, 'foo');

echo $ToBeUsed->getSomething();

输出:

foo

你可以看到它live here

显然感觉它在正常情况下受到保护,它无法在UseIt内使用。如果我打算将它用于任何数量的代码,我会扩展或包装Reflection类。只是为了使调用更简洁,像这样:

class MyReflector
{
    public static function invoke($class, $method, ...$args)
    {
        $ReflectionMethod = new ReflectionMethod($class, $method);
        $ReflectionMethod->setAccessible(true);
        $ReflectionMethod->invokeArgs($class, $args);
    }
}

$ToBeUsed = new ToBeUsed();

MyReflector::invoke($ToBeUsed,'setSomething', 'foo');

请注意我很喜欢变量...$arg,它适用于PHP 5.6+它只是让你做

 MyReflector::invoke($ToBeUsed,'setSomething', 'foo', 'bar');

$args在第一个示例中为['foo','bar'],它只是['foo'],可以用于invokeArgs第二个参数,它接受一个数组传递给实际方法的参数。

答案 1 :(得分:1)

class ToBeUsed
{
    private $a;

    public function setSomething($a)
    {
        $dbg = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,2);
        if(count($dbg) > 1){
            return;
        }
        $this->a = $a;
    }

    public function getSomething()
    {
        return $this->a;
    }
}

class UseIt
{
    /**
     * @var ToBeUsed
     */
    private $obj;

    public function __construct(ToBeUsed $obj)
    {
        $this->obj = $obj;
    }

    public function work()
    {
        echo $this->obj->getSomething().PHP_EOL; // a
        $this->obj->setSomething('b'); // this does nothing
        echo $this->obj->getSomething().PHP_EOL; // a
    }
}

$obj = new ToBeUsed();
$obj->setSomething('a');

$obj2 = new UseIt($obj);
$obj2->work();

或者,您可以对debug_backtrace()输出执行更复杂的检查。