PHP / Scope / Callback - PullRequest
       1

PHP / Scope / Callback

4 голосов
/ 29 июля 2010

class myClass {$ myVariable = 'myCallback';

    function myFunction() {
        $body = false;
        $callback = $this->myVariable;

        function test($handle, $line) {
            global $body, $callback;

            if ($body) {
                call_user_func($callback, $line);
            }

            if ($line === "\r\n") {
                $body = true;
            }

            return strlen($line);
        }

        ...
        curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'test');
        ...
    }
}

function myCallback($data) {
    print $data;
}

$myCls = new myClass();
$myCls->myFunction();

Предупреждение: call_user_func () [function.call-user-func]: первый аргумент должен быть допустимым обратным вызовом!

Мое значение обратного вызова $ пусто, как я могу решить эту проблему?Ограничение: функция myCallback не может быть изменена!

Ответы [ 3 ]

10 голосов
/ 29 июля 2010

Важно: Это возможно только в PHP> = 5.3.ОП не использует PHP 5.3, но я оставлю здесь ответ, если у кого-то есть подобная проблема и используется PHP 5.3.


$callback не является глобальной переменной, она локальна в методахобъем.Из документации :

Замыкания также могут наследовать переменные из родительской области. Любые такие переменные должны быть объявлены в заголовке функции.Наследование переменных из родительской области - это не то же самое, что использование глобальных переменных. Глобальные переменные существуют в глобальной области, которая одинакова независимо от выполняемой функции.Родительская область действия замыкания - это функция, в которой было объявлено замыкание (не обязательно функция, из которой оно было вызвано).

Используйте use (и назначьте функцию переменной):

$test = function($handle, $line) use ($callback, $body){

    if ($body) {
        call_user_func($callback, $line);
    }

    if ($line === "\r\n") {
        $body = true;
    }

    return strlen($line);
};

и позже:

curl_setopt($ch, CURLOPT_WRITEFUNCTION, $test);
2 голосов
/ 29 июля 2010

Просто для удовольствия еще один ... (хотя и не проверял).

class myClass
{
    protected $callback = 'myCallback';
    protected $body = false;

    public function myFunction()
    {
        ...
        curl_setopt($ch, CURLOPT_WRITEFUNCTION, array($this, 'test'));
        ...
    }


    public function test($handle, $line)
    {
        if ($this->body) {
            call_user_func($this->callback, $line);
        }

        if ($line === "\r\n") {
            $body = true;
        }

        return strlen($line);
    }
}

function myCallback($data) {
    print $data;
}

$myCls = new myClass();
$myCls->myFunction();
2 голосов
/ 29 июля 2010

За исключением отсутствующего ключевого слова при объявлении $myVariable, это вообще не должно выдавать ошибку, потому что внутри test() функция $body равна NULL, если вы не определили ее в глобальной области видимости. Другими словами, ваш myCallback никогда не должен вызываться. Очевидно, вы определили $body в глобальной области видимости и сделали это отличным примером того, почему использование глобальных переменных приведет к всевозможным неожиданным действиям. Если вы определили бы $callback в глобальной области видимости для хранения «myCallback», это должно сработать, но вам не нужны глобальные переменные.

И сделайте себе одолжение и избавьтесь от функции в методе:

class myClass {

    protected $_myVariable = 'myCallback';

    public function myFunction()
    {
        // calling callback that calls callback (should make you think ;))
        // that would be your curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'test');
        echo call_user_func_array(
            array($this, '_test'),
            array('body', 'foo', 'bar'));
    }

    protected function _test($body, $handle, $line)
    {
        if ($body) {
            call_user_func($this->_myVariable, $line);
        }
        if ($line === "\r\n") {
            $body = true;
        }
        return strlen($line);
    }
}

Функция test() теперь является методом внутри вашего класса. Это намного понятнее и проще в обслуживании, чем помещение потока кода в какую-либо функцию внутри метода. Обратите внимание, что я передаю $body в качестве первого аргумента. Я не знаю, как cURL принимает обратные вызовы или что он передает им. Если вы не можете сделать $body первым аргументом, сделайте его проверкой члена класса на предмет его состояния с помощью $this->body (, как теперь показано Филиппом ниже )

...