我正在尝试使用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.
首先这个例子不打印字符串但返回函数,但这不是我的问题。
其次我无法访问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}.";
}
哪个来自Dog而非全球。
答案 0 :(得分:9)
好吧,你不能使用$ 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);
};
}
另一种选择是提供通用的getter:
class Dog {
public function __get($name) {
return isset($this->$name) ? $this->$name : null;
}
}
有关详细信息,请参阅:http://www.php.net/manual/en/language.oop5.magic.php
答案 1 :(得分: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
正如您所看到的,可能很容易弄乱私人数据......所以要小心。
答案 2 :(得分:2)
有意义的是,您无法访问对象的私有和受保护字段。通过明确地将$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()}.";
};
}
}
无论如何,你应该为encapsulation建立getter(和setter)。
另一个注意事项:您链接到的文章是在PHP 5.3的最终版本发布之前发布的。也许这个隐式对象传递被删除了。
答案 3 :(得分:0)
我在我的工作中使用这个create closure()将回调分成Classes:
<?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)
{//...
}
);
}
?>