Вызываемые и замыкания

#php #lambda #closures

#php #лямбда — выражение #закрытие #лямбда #замыкания

Вопрос:

Я создал не очень сложный тестовый код (протестирован в 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) Почему при использовании object $d я получаю следующее предупреждение:

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

и возможно ли запустить защищенный метод из родительского? Когда я меняю этот метод с защищенного на общедоступный, он может быть запущен без проблем

2) Как вы, наверное, заметили, я хочу использовать некоторые аргументы для функции, которую я вызываю с помощью call_user_sync . К сожалению, я не знаю этих параметров при вызове call_user_func , поэтому в классах B и C я использовал замыкания, где я могу использовать / передавать дополнительные параметры. У меня есть 2 дополнительных вопроса, связанных с этим:

  • является ли это способом, при котором замыкания полезны и обычно используются?

  • возможно ли с помощью object $d передавать параметры в метод тестирования без использования замыканий, но не при вызове call_user_sync , а внутри класса D ?

Комментарии:

1. возможно ли запустить защищенный метод из родительского? Это не то, что вы делаете. Вы не запускаете защищенный метод из родительского. Вы пытаетесь вызвать защищенный метод Test экземпляра внутри метода D экземпляра.

2. Вы уверены? В моем коде я вижу, что пытаюсь вызвать защищенный метод test B класса (который является родительским для D класса), и когда я меняю этот метод на общедоступный, этот метод запускается

3. Вы можете использовать array($this, 'method') с частными / защищенными методами только при использовании в том же контексте. Когда вы создаете объект $obj внутри функции и передаете обратный array($this, 'test') вызов этой объектной функции, он передается без контекста. Внутри Test::wrap функции обратный вызов вызывается как $objectOfClassD->test(1) , а не $this->test(1) как, и метод D::test() защищен. Когда вы D::test() публикуете его, он доступен из любого контекста, и предупреждение не генерируется.

Ответ №1:

Важно отметить область действия во время выполнения. Вы создаете обратный вызов в правильной области, но вы выполняете обратный вызов в другом объекте без доступа к protected методу (обратный вызов get выполняется class Test не в родительском или дочернем элементе class B .

Я столкнулся с этой проблемой некоторое время назад, когда писал свой собственный класс диспетчера. Одним из вариантов было установить «родительский» для диспетчера и передать диспетчеру в качестве одного из параметров при обратном вызове. Затем обратный вызов проверяет «родительский», связанный с диспетчером === $this , а затем знает, что у него есть доступ, и отправляется в город.

Вы должны выполнить свою собственную проверку доступа, вот в чем дело.