У меня есть следующий код (например, это действительно мой настоящий код):
<?php class Foobar { public static function foo() { exit('foo'); } }
Когда я запускаю $foobar = new FooBar; $foobar->foo()
$foobar = new FooBar; $foobar->foo()
отображает foo
.
Почему PHP попытался использовать статический метод в контексте объекта? Есть ли способ избежать этого?
Хорошо, вы, ребята, не получили мою проблему: я знаю различия между статическими и нестационарными методами и как их называть. Это все мое дело, если я вызываю $foobar->foo()
, почему PHP пытается запустить статический метод?
Примечание. Я запускаю PHP 5.4.4, сообщение об ошибках в E_ALL
.
Чтобы вызвать статический метод, вы не используете:
$foobar = new FooBar; $foobar->foo()
Ты звонишь
FooBar::foo();
В руководстве PHP говорится …
Объявление свойств класса или методов как статических делает их доступными без необходимости создания экземпляра класса. Свойство, объявленное как статическое, не может быть доступно с помощью экземпляра объекта класса (хотя может использоваться статический метод).
Вот почему вы можете вызвать метод в экземпляре, хотя это не то, что вы намеревались сделать.
Независимо от того, статический или статический метод статический или не экземпляр, вы не можете получить $this
в статическом методе.
http://php.net/manual/en/language.oop5.static.php
Вы можете проверить, находитесь ли вы в статическом контексте, хотя я бы поставил под сомнение, является ли это излишним …
class Foobar { public static function foo() { $backtrace = debug_backtrace(); if ($backtrace[1]['type'] == '::') { exit('foo'); } } }
Еще одно замечание – я считаю, что метод всегда выполняется в статическом контексте, даже если он вызывается в экземпляре. Я рад, что исправил это, если ошибаюсь.
Поскольку PHP – довольно прощающий язык, вы можете предотвратить это поведение по умолчанию, перегрузив __callStatic
и, возможно, использовать рефлексы для проверки области метода.
http://php.net/manual/en/language.oop5.overloading.php#object.call
Нам может понадобиться дополнительная информация о объявлении FooBar. Очевидно, вы не можете объявить два метода foo (), даже если это статический метод, а другой – метод экземпляра:
class FooBar { public static function foo() { return 'I am FooBar::foo().'; } public function foo() { return 'I am FooBar->foo().'; } } // result to Fatal error: Cannot redeclare FooBar::foo()
Итак, я полагаю, что вы хотите достичь магического __call()
, например:
class FooBar { public $foo = 'I am FooBar->foo().'; // yes we can have a property with the same name than a method // this is the static method that we want to avoid public static function foo() { return 'I am FooBar::foo().'; } // this is the method that should be call public function __call( $method , $arguments = array() ) { if( isset( $this->$method ) ) // or anything else { return $this->$method; // or anything else } else { // restore fatal error trigger_error( sprintf( 'Call to undefined method %s::%s()' , get_class( $this ) , $method ) , E_USER_ERROR ); } } }
Чтобы достичь этого, посмотрите на этот фрагмент кода:
$foobar = new FooBar; try { // safe way to detect if a method is static // @see http://php.net/manual/en/class.reflectionmethod.php $rfx = new ReflectionMethod( get_class( $foobar ).'::foo' ); if( $rfx->isStatic() ) { // the method exists and is static // but we do not want to call it directly // why not involving the magic public method `__call()`? // sounds like a plan... echo $foobar->__call( 'foo' ); } else { // the method exists and is not static echo $foobar->foo(); } } catch( ReflectionException $e ) { // the method does not exist, let's do some kind of magic echo $foobar->foo(); }