Métodos PHP que funcionam em contextos instanciados e estáticos?

Estou tentando configurar alguns methods PHP que são chamáveis ​​em contextos instanciados e estáticos. Quais são algumas boas maneiras de fazer isso? Por exemplo, eu quero ser capaz de fazer:

Foo::bar($item); foo($item)->bar(); 

Eu poderia configurar duas classs separadas e ter cada function modificar o thisArg e delegar para o outro, mas parece que tem que haver uma maneira melhor. A única maneira que eu poderia pensar em fazê-lo com apenas uma class seria algo assim:

 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; } } 

Se você olhar para o código de underscore.php eles fazem algo assim. Eu li algumas perguntas relacionadas de um tempo atrás, que indicam que usar isset($this) para determinar o contexto pode levantar um aviso, mas parece funcionar bem … qualquer pensamento atualizado sobre isso? Outra possibilidade é poder fazer duas classs, uma com toda a versão estática dos methods e, em seguida, uma segunda class que use __call para delegar no método estático como:

 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; } } 

Idéias?

http://www.php.net/manual/en/domdocument.loadxml.php faz isso, mas quando chamado a partir de um contexto estático, emite um E_STRICT .

Aproximadamente, fazer um método estático e instável por chamada funcionará por enquanto, mas esse recurso provavelmente será removido. Talvez haja outra maneira de realizar o que você precisa?

editar: A funcionalidade pode ser simulada com __call como você menciona sem jogar um E_STRICT , mas você não precisa de duas classs:

 < ? //PHP 5.4+ class mightBeInstance { public function __call($name, $arguments) { if ($name === 'doSomething') { return static::doSomething($this); } } public static function doSomething(mightBeInstance $item = null) { if ($item === null) { $item = new static(); } $item->didSomething = true; // Or whatnot return $item; } } var_dump( (new mightBeInstance)->doSomething(), mightBeInstance::doSomething() ); ?> 

Esta é a única solução confiável. Funciona com 5.3+ (exceto para essa instanciação de object em linha na parte inferior), mas é um pouco complicado.

 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'); 

NÃO É RECOMENDADO: O seguinte funciona no PHP 5.6, mas lança um erro E_DEPRECATED com a mensagem “Método não estático foo :: bar () não deve ser chamado estaticamente” quando executado no PHP 7.0. O problema não é com o isset($this) como você diz, mas sim com uma única function dobrar o dever: é estático ou não. Ainda é suportado no PHP 7.0, mas você não deve confiar nisso.

 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'); 

NÃO TRABALHA: Isso lança um erro fatal “Não é possível redecar o foo :: bar ()” em PHP 5.6 e no PHP 7.0, mas seria ideal se você pudesse fazê-lo dessa maneira.

 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'); 

Talvez em versões futuras uma vez que o uso obsoleto tenha sido removido, poderemos fazer isso.