将闭包定义为类中的方法
Posted
技术标签:
【中文标题】将闭包定义为类中的方法【英文标题】:define a closure as method from class 【发布时间】:2011-02-22 07:18:14 【问题描述】:我正在尝试使用 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 而不是全局的。
【问题讨论】:
不,我不明白你想要什么。如果 greet 是一种方法,则可见性默认为 public,因此您的最后两个代码块是等效的。 在 PHP 5.3 发布之前,在 clojure 中注入$this
的魔法已被删除。实际上,它被推迟到即将发布的 PHP 版本。会有一些类似Closure::bindTo
的东西可以用来将对象作为上下文绑定到 lambda。
【参考方案1】:
嗯,你不能使用 $this 的全部原因是因为闭包是后台的一个对象(闭包类)。
有两种方法可以解决这个问题。首先,添加 __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
【讨论】:
我想补充一下,您可以通过反射使该字段可访问。 你可以在 PHP 5.4 的闭包中使用$this
这是我知道的旧答案。但值得注意的是 Closure::bind() (php.net/manual/en/closure.bind.php) 在这里也是一个可行的解决方案。【参考方案2】:
从 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】:您不能访问对象的私有和受保护字段是有道理的。通过将$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 的最终版本发布之前发布的。也许这个隐式对象传递被删除了。
【讨论】:
是的,我知道它不像当前版本。我想要的是创建一个 Dog 类的新函数,然后在该函数中我可以访问 $this 而无需传递 $self。【参考方案4】:我在工作中使用这个 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)
//...
);
?>
【讨论】:
以上是关于将闭包定义为类中的方法的主要内容,如果未能解决你的问题,请参考以下文章
Groovy闭包 Closure ( 闭包调用 与 call 方法关联 | 接口中定义 call() 方法 | 类中定义 call() 方法 | 代码示例 )
Groovy闭包 Closure ( 闭包调用 与 call 方法关联 | 接口中定义 call() 方法 | 类中定义 call() 方法 | 代码示例 )
Groovy闭包 Closure ( 闭包的 delegate 代理策略 | OWNER_FIRST | DELEGATE_FIRST | OWNER_ONLY | DELEGATE_ONLY )