определить замыкание как метод из класса - PullRequest
14 голосов
/ 21 мая 2010

Я пытаюсь играть с php5.3 и закрытием.

Я вижу здесь (Листинг 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.

Прежде всего этот пример не печатает строку, а возвращает функцию, но это не моя проблема.

Во-вторых, я не могу получить доступ к приватному или защищенному, потому что функция обратного вызова является глобальной функцией, а не в контексте объекта 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}.";
}

Который из Dog, а не глобальный.

Ответы [ 4 ]

9 голосов
/ 21 мая 2010

Ну, единственная причина, по которой вы не можете использовать $ this, заключается в том, что замыкание является объектом на заднем плане (класс Closure).

Есть два способа обойти это. Сначала добавьте метод __invoke (что вызывается, если вы вызываете $ 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);
    };
}

Другой вариант - предоставить универсальный метод получения:

class Dog {

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

}

Для получения дополнительной информации см .: http://www.php.net/manual/en/language.oop5.magic.php

4 голосов
/ 29 июня 2011

Начиная с 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

Как видите, можно легко связываться с личными данными ... так что будьте осторожны.

2 голосов
/ 21 мая 2010

Хорошо, имеет смысл, что вы не можете получить доступ к закрытым и защищенным полям объекта. И явно передавая $self вашей функции, она обрабатывается как обычный объект.
Вы должны создать геттеры для доступа к этим значениям, то есть:

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()}.";
         };
    }
}

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


Еще одно примечание: статья, на которую вы ссылаетесь, была опубликована до выпуска окончательной версии PHP 5.3. Возможно, эта неявная передача объекта была удалена.

0 голосов
/ 10 июня 2014

Я использую этот 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)
                                   {//...
                                   }
                                  );
         }
?>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...