代码之家  ›  专栏  ›  技术社区  ›  Charles

将闭包定义为类中的方法

  •  14
  • Charles  · 技术社区  · 15 年前

    我想玩p5.3和close。

    我看到这里(清单7。对象内部的闭合: http://www.ibm.com/developerworks/opensource/library/os-php-5.3new2/index.html )可以在回调函数中使用$this,但不能。所以我尝试给$this作为使用变量:

    $self = $this;
    $foo = function() use($self) { //do something with $self }
    

    因此,要使用相同的示例:

    class Dog
    {
    private $_name;
    protected $_color;
    
    public function __construct($name, $color)
    {
         $this->_name = $name;
         $this->_color = $color;
    }
    public function greet($greeting)
    {
         $self = $this;
         return function() use ($greeting, $self) {
             echo "$greeting, I am a {$self->_color} dog named {$self->_name}.";
         };
    }
    }
    
    $dog = new Dog("Rover","red");
    $dog->greet("Hello");
    
    Output:
    Hello, I am a red dog named Rover.
    

    首先,这个示例不打印字符串,而是返回函数,但这不是我的问题。

    其次,我不能访问private或protected,因为回调函数是全局函数,而不是在dog对象的上下文中。这不是我的问题。与以下内容相同:

    function greet($greeting, $object) {
        echo "$greeting, I am a {$self->_color} dog named {$self->_name}.";
    }
    

    我想要:

    public function greet($greeting) {
        echo "$greeting, I am a {$self->_color} dog named {$self->_name}.";
    }
    

    它来自狗而不是全球。

    4 回复  |  直到 10 年前
        1
  •  9
  •   ircmaxell    15 年前

    好吧,不能使用$this的全部原因是因为闭包是后台的一个对象(闭包类)。

    有两种方法可以解决这个问题。首先,是添加uuInvoke方法(如果调用$obj(),则调用此方法)。

    class Dog {
    
        public function __invoke($method) {
            $args = func_get_args();
            array_shift($args); //get rid of the method name
            if (is_callable(array($this, $method))) {
                return call_user_func_array(array($this, $method), $args);
            } else {
                throw new BadMethodCallException('Unknown method: '.$method);
            }
        }
    
        public function greet($greeting) {
            $self = $this;
            return function() use ($greeting, $self) {
                $self('do_greet', $greeting);
            };
        }
    
        protected function do_greet($greeting) {
            echo "$greeting, I am a {$this->_color} dog named {$this->_name}.";
        }
    }
    

    如果您希望在修改主机对象时不更改闭包,则只需将返回函数更改为如下内容:

    public function greet($greeting) {
        $self = (clone) $this;
        return function() use ($greeting, $self) {
            $self('do_greet', $greeting);
        };
    }
    

    另一种选择是提供一个通用getter:

    class Dog {
    
        public function __get($name) {
            return isset($this->$name) ? $this->$name : null;
        }
    
    }
    

    有关详细信息,请参阅: http://www.php.net/manual/en/language.oop5.magic.php

        2
  •  4
  •   Matthew    13 年前

    从php 5.4.0 alpha1开始,您可以访问 $this 从对象实例的上下文中:

    <?php
    class Dog
    {
      private $_name;
      protected $_color;
    
      public function __construct($name, $color)
      {
        $this->_name = $name;
        $this->_color = $color;
      }
    
      public function greet($greeting)
      {
        $func = function() use ($greeting) {
          echo "$greeting, I am a {$this->_color} dog named {$this->_name}.";
        };
    
        $func();
      }
    }
    
    $dog = new Dog("Rover","red");
    $dog->greet("Hello");
    

    您也可以这样做:

    $dog = new Dog("Rover", "red");
    $getname = Closure::bind($dog, function() { return $this->_name; });
    echo $getname(); // Rover
    

    正如你所看到的,很容易就弄乱了私人数据…所以要小心。

        3
  •  2
  •   Felix Kling    15 年前

    好吧,你不能访问对象的私有和受保护字段是有意义的。通过明确传递 $self 对于您的函数,它被视为普通对象。
    您应该创建getter以访问这些值,即:

    class Dog
    {
        private $_name;
        protected $_color;
    
        public function __construct($name, $color)
        {
             $this->_name = $name;
            $this->_color = $color;
        }
        public function getName() {
            return $this->_name;
        }
        public function getColor() {
            return $this->_color;
        }
        public function greet($greeting)
        {
             $self = $this;
             return function() use ($greeting, $self) {
                 echo "$greeting, I am a {$self->getColor()} dog named {$self->getName()}.";
             };
        }
    }
    

    无论如何,您应该创建getter(和setter),因为 encapsulation .


    另一个注意事项:您链接到的文章是在php 5.3的最终版本发布之前发布的。可能这个隐式对象传递被删除了。

        4
  •  0
  •   diyism    10 年前

    我在工作中使用这个create_closure()将回调分离到类中:

    <?php
    function create_closure($fun, $args, $uses)
             {$params=explode(',', trim($args.','.$uses, ','));
              $str_params='';
              foreach ($params as $v)
                      {$v=trim($v, ' &$');
                       $str_params.='\''.$v.'\'=>&$'.$v.', ';
                      }
              return "return function({$args}) use ({$uses}) {{$fun}(array({$str_params}));};";
             }
    ?>
    

    例子:

    <?php
    $loop->addPeriodicTimer(1, eval(create_closure('pop_message', '$timer', '$cache_key, $n, &$response, &$redis_client')));
    
    function pop_message($params)
             {extract($params, EXTR_REFS);
              $redis_client->ZRANGE($cache_key, 0, $n)
                                ->then(//normal
                                       function($data) use ($cache_key, $n, &$timer, &$response, &$redis_client)
                                       {//...
                                       },
                                       //exception
                                       function ($e) use (&$timer, &$response, &$redis_client)
                                       {//...
                                       }
                                      );
             }
    ?>