определить замыкание как метод из класса

Я пытаюсь играть с 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}."; } 

Это от собаки, а не от глобального.

Ну, вся причина, по которой вы не можете использовать $ 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

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

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

Хорошо, что имеет смысл, что вы не можете получить доступ к закрытым и защищенным полям объекта. И, явно передавая $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. Возможно, эта неявная передача объекта была удалена.

Я использую this 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) {//... } ); } ?>