2010-05-21 3 views
14

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

Я вижу здесь (листинг 7. Закрытие внутри объекта: http://www.ibm.com/developerworks/opensource/library/os-php-5.3new2/index.html), что в функции обратного вызова можно использовать $ 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}."; 
} 

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

+0

Нет, я не понял, что вы хотите. Если приветствие - это метод, видимость деактивируется публично, поэтому ваши два последних блока кода эквивалентны. – Artefacto

+0

Эта волшебная инъекция '$ this' в clojure была отброшена до того, как был выпущен PHP 5.3. Фактически, это было отложено для предстоящей версии PHP. Там будет что-то вроде 'Closure :: bindTo', которое можно использовать для привязки объектов как контекстов к лямбда. –

ответ

2

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

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


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

+0

Да, я знаю, что это не похоже на текущую версию. То, что я хочу, это создать новую функцию класса Dog, а затем в функции, к которой я могу получить доступ к $ this, без передачи $ self. – Charles

8

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

+0

Я бы добавил, что вы можете сделать поле доступным через отражение. – Artefacto

+1

Вы можете использовать '$ this' в закрытии с PHP 5.4 – dave1010

+0

Это старый ответ, который я знаю. Но стоит отметить, что Closure :: bind() (http://www.php.net/manual/en/closure.bind.php) также является жизнеспособным решением. –

4

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

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

0

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