我正在尝试设置一些可在实例化和静态上下文中调用的 PHP 方法。有哪些好的方法可以做到这一点?例如我希望能够做到:
Foo::bar($item);
foo($item)->bar();
我可以设置两个单独的类,让每个函数修改 thisArg 并委托给另一个函数,但似乎必须有更好的方法。我能想到的只用一堂课来做到这一点的唯一方法是这样的:
function foo($item = null) {
return $item instanceof Foo ? $item : new Foo($item);
}
class Foo {
protected $wrapped;
public function __construct($item = null) {
$this->wrapped = $item;
}
public function get() {
return $this->wrapped;
}
public function bar($item = null) {
isset($this) and $item = &$this->wrapped;
// do stuff with $item
return isset($this) ? $this : $item;
}
}
如果你看一下 underscore.php 的代码,他们会做类似的事情。我不久前读过一些相关的问题,其中指出使用
isset($this)
来确定上下文可能会引发警告,但它似乎工作正常......对此有任何更新的想法吗?另一种可能性是创建两个类,一个类具有方法的所有静态版本,然后第二个类使用 __call 委托给静态方法,例如:
class _Foo
{
protected $wrapped;
public function __construct($item = null) {
$this->wrapped = $item;
}
public function __call($method_name, $args) {
array_unshift($args, $this->wrapped);
$this->wrapped = call_user_func_array('Foo::' . $method_name, $args);
return $this;
}
}
想法?
这是唯一可靠的解决方案。它适用于 5.3+(除了底部的内联对象实例化),但有点笨拙。
class foo {
protected function __call($method, $args) {
if ($method == 'bar') {
return $this->bar($args[0]);
}
}
protected function bar($baz) {
return "object context: $baz\n";
}
public static function __callStatic($method, $args) {
if ($method == 'bar') {
return self::barStatic($args[0]);
}
}
protected static function barStatic($baz) {
return "static context: $baz\n";
}
}
echo foo::bar('baz');
echo (new foo())->bar('baz');
不推荐: 以下代码在 PHP 5.6 中有效,但在 PHP 7.0 中运行时会抛出
E_DEPRECATED
错误,并显示消息“非静态方法 foo::bar() 不应静态调用”。问题不在于您所说的 isset($this)
,而在于让单个函数执行双重任务:它要么是静态的,要么不是。 PHP 7.0 中仍然支持它,但您不应该依赖它。
class foo {
public function bar($baz) {
if (isset($this)) {
return "object context: $baz\n";
} else {
return "static context: $baz\n";
}
}
}
echo foo::bar('baz');
echo (new foo())->bar('baz');
不起作用: 这会在 PHP 5.6 和 PHP 7.0 中引发致命错误“Cannot redeclare foo::bar()”,但如果您可以这样做,那就更理想了。
class foo {
public function bar($baz) {
return "object context: $baz\n";
}
public static function bar($baz) {
return "static context: $baz\n";
}
}
echo foo::bar('baz');
echo (new foo())->bar('baz');
也许在未来的版本中,一旦删除了已弃用的用法,我们就可以做到这一点。