可调用和关闭

时间:2014-06-21 18:35:49

标签: php lambda closures

我创建了非常复杂的测试代码(在PHP 5.5.12中测试过):

<?php

class Test
{
    private $cached = null;

    public function __construct()
    {
        $this->cached = [];
        $this->cached[0] = 12;
    }

    function wrap($function, $index)
    {
        if (isset($this->cached[$index])) {
            return $this->cached[$index];
        }

        $result = call_user_func($function);

        return $result;
    }
}

class B
{
    public function run()
    {
        $x = 6;
        $obj = new Test();
        $value = $obj->wrap(
            function () use ($x) {
                return $this->test($x);
            },
            1
        );
        echo $value."<br />";
    }


    protected function test($x)
    {
        echo "I'm running ";
        return $x * $x;
    }
}

class C extends B
{
    public function run()
    {
        $x = 6;
        $obj = new Test();

        $myFunc = function () use ($x) {
        return $this->test($x);
        };

        $value = $obj->wrap($myFunc, 1);
        echo $value."<br />";
    }
}

class D extends B
{
    public function run()
    {
        $x = 6;
        $obj = new Test();

        $value = $obj->wrap(array($this, 'test'), 1);
        echo $value."<br />";
    }
}


$b = new B();
$b->run();

$c = new C();
$c->run();

$d = new D();
$d->run();

可能有代码的某些部分你可以说它可以做得更好但主要的是闭包功能和可调用。这些类以一种非常简单的方式模拟缓存系统。如果数据在缓存中,则从缓存中返回数据,否则调用获取数据的函数(当然,这个缓存系统不起作用,因为它不需要 - 它只是一个示例代码)。

问题:

1)为什么在使用对象$d时会收到以下警告:

call_user_func() expects parameter 1 to be a valid callback, cannot access protected method D::test() 

是否可以从父级启动受保护的方法?当我将此方法从protected更改为public时,可以毫无问题地启动它

2)正如您可能已经注意到我想使用函数的一些参数,我使用call_user_sync调用。不幸的是,当我调用call_user_func时,我不知道那些参数,所以在B类和C类中,我使用了闭包,我可以使用/传递额外的参数。我还有2个额外的问题:

  • 这是封闭有用和常用的方式吗?

  • 是否可以使用对象$d在不使用闭包的情况下将参数传递给测试方法,但在调用call_user_sync但在类D内时却不能?

1 个答案:

答案 0 :(得分:1)

重要的是要注意执行时的范围。您正在在正确的范围内创建回调,但是您在另一个对象中执行回调而无法访问protected方法(回调获取已在class Test不在class B的父母或子女中。

我在前一段时间编写自己的调度程序时遇到过这个问题。一种选择是在调度程序上设置“父”,并将调度程序作为回调上的参数之一传递。然后回调检查与=== $this的Dispatcher相关联的“父”,然后知道它有访问权并进入城镇。

你必须进行自己的访问检查,重点。