【问题标题】:PHP / Scope / CallbackPHP/范围/回调
【发布时间】:2011-03-22 14:02:15
【问题描述】:

类我的类{ $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]:第一个参数应该是一个有效的回调!

我的 $callback 值为空,我该如何解决这个问题? 限制:无法更改 myCallback 函数!

【问题讨论】:

    标签: php scope callback


    【解决方案1】:

    重要提示:这只能在 PHP >= 5.3 中实现。 OP 不使用 PHP 5.3,但我会在此处留下答案,以防有人遇到类似问题并使用 PHP 5.3。


    $callback 不是全局变量,它是方法范围内的局部变量。来自documentation

    闭包也可以从父作用域继承变量。 任何此类变量都必须在函数头中声明。从父作用域继承变量与使用全局变量是不一样的。全局变量存在于全局作用域中,无论执行什么函数都是一样的。闭包的父作用域是声明闭包的函数(不一定是调用它的函数)。

    利用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);
    

    【讨论】:

    • 如果不将闭包分配给变量,就无法定义闭包
    • @Gordon:好点,你是对的,至少当你想使用use时,你必须将它分配给一个变量。否则我会收到语法错误(或者我在做一些愚蠢的事情;))
    • @Felix 闭包和 Lambda 是对象。这就是为什么你必须分配它们。他们应该被传递。来自文档:匿名函数目前是使用 Closure 类实现的。 - 您还应该注意,它们仅在 5.3 之后才可用
    • 你有 PHP 5.2 的解决方案吗?
    • @Fabien:让你的测试函数成为 MyClass 中的一个方法
    【解决方案2】:

    只是为了好玩,另一个......(虽然没有测试)。

    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();
    

    【讨论】:

      【解决方案3】:

      除了在声明$myVariable 时缺少关键字,这根本不会引发错误,因为在test() 函数内部$bodyNULL,除非您在全局范围内定义它。换句话说,你的 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 检查其状态(as now shown by Philippe below

      【讨论】:

      • 哈,我应该看看那个;)无论如何,我也试过了,它确实有效 +1(删除了我的其他废话 cmets)
      猜你喜欢
      • 2011-07-27
      • 2010-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-04
      • 2014-02-14
      • 2014-01-11
      • 1970-01-01
      相关资源
      最近更新 更多