PHP использует статические методы в объектном контексте

У меня есть следующий код (например, это действительно мой настоящий код):

<?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

http://php.net/ReflectionClass

Нам может понадобиться дополнительная информация о объявлении 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(); }