2012-05-04 2 views
1

Я пытаюсь установить некоторые PHP-методы, которые могут быть вызваны в инстанцированных и статических контекстах. Каковы хорошие способы сделать это? Например, я хочу, чтобы быть в состоянии сделать: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 они делают что-то подобное. Я прочитал некоторые связанные questions from a while back, которые указывают, что использование 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; 
     } 


} 

идей?

ответ

2

http://www.php.net/manual/en/domdocument.loadxml.php делает это, но при вызове из статического контекста выдает E_STRICT.

Грубо говоря, создание статического и вызываемого экземпляром метода будет работать, но эта функция, вероятно, будет удалена. Возможно, есть еще один способ сделать то, что вам нужно?

редактирования: Функциональность может быть смоделирована с __call, как вы говорите, не бросая E_STRICT, но вам не нужны два класса:

<? //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() 
); 

?> 
+0

Именно поэтому я спрашиваю. – ryanve

+0

черт его, но маскировка! Что делать, если я хочу, чтобы класс базы данных назывался статическим одноэлементным в целом, но с возможностью создания экземпляра в случае, если мне нужны 2 соединения? –

+0

@Cory, но '__call'" срабатывает только при вызове недоступных методов в контексте объекта. " Но статические методы ** ** доступны в контексте объекта, поэтому я не думаю, что он срабатывал бы, если метод не существовал. Мне нравится идея. – ryanve

0

Это является единственным надежным решением. Он работает с 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, но выдает ошибку E_DEPRECATED с сообщением «Non-статический метод Foo :: бар() не должен вызываться статически» при запуске в PHP 7.0. Проблема заключается не в 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'); 

НЕ РАБОТАЕТ: Это бросает фатальную ошибку «Не удается переобъявить Foo :: бар()» как в PHP 5.6 и PHP 7.0, но это было бы идеально, если вы могли бы сделать это таким образом, вместо ,

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

Возможно, в будущих версиях после удаления устаревшего использования мы сможем это сделать.