我喜欢jQuery / Javascript通过闭包扩展功能的方式。是否有可能在PHP 5.3中做类似的事情?
class Foo
{
public $bar;
}
$foo = new Foo;
$foo->bar = function($baz) { echo strtoupper($baz); };
$foo->bar('lorem ipsum dolor sit amet');
// LOREM IPSUM DOLOR SIT AMET
[edit]在我的问题中混淆了'it'和'is'。 HEH。
我下载了5.3a3,它确实有效!
class Foo
{
protected $bar;
public $baz;
public function __construct($closure)
{
$this->bar = $closure;
}
public function __call($method, $args)
{
$closure = $this->$method;
call_user_func_array($closure, $args);
}
}
$foo = new Foo(function($name) { echo "Hello, $name!\n"; });
$foo->bar('Mon');
// Hello, Mon!
$foo->baz = function($s) { echo strtoupper($s); };
$foo->baz('the quick brown fox jumps over the lazy dog');
// THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG
答案 0 :(得分:4)
PHP 5.3将允许创建lambda函数和闭包,这将允许您实现您的代码示例。来自PHP RFC:
function & (parameters) use (lexical vars) { body }
该函数作为参考返回,可以作为回调传递。例如,创建一个函数:
$lambda = function () { echo "Hello World!\n"; };
传递lambda函数作为回调:
function replace_spaces ($text) {
$replacement = function ($matches) {
return str_replace ($matches[1], ' ', ' ').' ';
};
return preg_replace_callback ('/( +) /', $replacement, $text);
}
Closures还允许您从本地范围导入变量:
$map = function ($text) use ($search, $replacement) {
if (strpos ($text, $search) > 50) {
return str_replace ($search, $replacement, $text);
} else {
return $text;
}
};
其中$ search和$ replacement都在闭包之外声明,$ text是函数参数。
答案 1 :(得分:3)
您可以利用__call
方法重新路由不存在的方法调用。在您的类中添加一个注册表,以便您可以动态添加/删除方法。调用外部函数的约定是第一个参数是函数绑定的对象。
clas Foo {
private $registry;
public function __call($alias, $args) {
if (array_key_exists($alias, $this->registry)) {
// prepend list of parameters with $this object
array_unshift($args, $this);
return call_user_func_array($this->registry[$alias], $args)
}
}
public function register($method, $alias = null) {
$alias or $alias = $method; // use original method name if no alias
$this->registry[$alias] = $method;
}
public function unregister($alias) {
unset($this->registry[$alias]);
}
}
想象一下函数clone_object
,它返回克隆对象的数组。第一个参数是要克隆的对象,第二个参数是克隆的数量。
function clone_object($foo, $n) {
$a = array();
while ($n--) {
$a[] = clone $foo;
}
return $a;
}
现在,通过这种方式,您可以将方法clone_object
注入到类Foo
中,并为其指定别名bar
:
$f = new Foo();
$f->register('clone_object', 'bar');
$f->bar(5); // this will return array of 5 cloned objects
在最后一行调用方法bar
将导致重定向到函数clone_object
。请注意,调用约定会将参数$this
插入参数列表中,因此(5)
将更改为($this, 5)
。
需要注意的是外部函数不能用于私有/受保护的属性和方法。
最后一句话,如果你能想到一个不会动态改变对象的解决方案,你应该使用它。以上是黑色巫术,应谨慎使用。
答案 2 :(得分:1)
PHP可以通过create_function()。虽然语法几乎完全没用,但语法仍然是十字架。
似乎PHP5.3+正在获得closures and lambda's!
但也来自RFC
Lambda函数/闭包不是在运行时通过其他方法动态扩展类的方法。还有其他几种可能性,包括已经存在的_ _call语义。
答案 3 :(得分:1)
正确的匿名函数/闭包仅在PHP 5.3中可用,暂时不会广泛使用 - some useful information。我认为不可能做你想做的事。
您可以在一定程度上使用create_function()
:
$func = create_function('$baz', ' echo strtoupper($baz); ');
$func("hello");
但是,以下情况并不像您期望的那样有效:
$foo = new Foo;
$foo->bar = create_function('$baz', ' echo strtoupper($baz); ');
$foo->bar('lorem ipsum dolor sit amet');
你需要做的是:
call_user_func($foo->bar, 'lorem ipsum dolor sit amet');
此外,您不能在创建的函数中使用$this
,因为它的范围与方法不同。
修改强>
当我在写我的时候编辑他时,我不小心重复了Martijn的一些回答。为了完整起见,我会完整地保留我的答案
其他编辑
你也可能会做类似
的事情class Foo
{
public $bar;
protected $anonFuncs = array();
public function create_method($name, $params, $code) {
if (strlen($params)) $params .= ',';
$params .= '$self';
$this->anonFuncs[$name] = create_function($params, $code);
}
public function __call($name, $args) {
if (!isset($this->anonFuncs[$name])) {
trigger_error("No method $name", E_USER_ERROR);
}
$args[] = $this;
return call_user_func_array($this->anonFuncs[$name], $args);
}
}
$foo = new Foo;
$foo->create_method('bar', '$baz', ' echo strtoupper($baz) . get_class($self); ');
$foo->bar('lorem ipsum dolor sit amet');
答案 4 :(得分:0)
使用create函数可以执行以下操作:
$Dynamic->lambda = create_function('$var', 'return $var." world."; ');
$classic = $Dynamic->lambda("hello"); // classic would contain "hello world."
类代码包含类似的内容:
public function __set($methodname, $function)
{
$this->methods[$methodname] = $function;
}
public function __get($methodname)
{
return $this->methods[$methodname];
}
public function __call($method, $args)
{
$funct = $this->{$method};
//$args[] = $this; --to add the current object as a parameter option
return call_user_func_array($funct, $args);
}
注意:您将无法在create function中使用对象作用域($this
变量),因为基本上这只是在全局命名空间中使用create_function的对象接口。但是,您可以轻松地将对象的实例附加到参数列表并将其拉入类中,但是再次您只能访问引入类的公共方法。
答案 5 :(得分:0)
自PHP5.4发布以来,您可以做更多的事情:
答案 6 :(得分:0)
我在我的工作中使用这个create closure()将回调分成Classes:
<?php
function create_closure($fun, $args, $uses)
{$params=explode(',', $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)
{//...
}
);
}
?>