Вызов нестатического метода с двойным двоеточием (::)
Почему я не могу использовать нестатический метод с синтаксисом статических методов (class::method)? Это какая-то проблема конфигурации?
class Teste {
public function fun1() {
echo 'fun1';
}
public static function fun2() {
echo "static fun2" ;
}
}
Teste::fun1(); // why?
Teste::fun2(); //ok - is a static method
11 ответов
PHP очень слаб в использовании статических и нестатических методов. Одна вещь, которую я здесь не вижу, это то, что если вы вызываете нестатический метод, ns
статически из нестатического метода класса C
, $this
внутри ns
будет ссылаться на ваш случай C
,
class A
{
public function test()
{
echo $this->name;
}
}
class C
{
public function q()
{
$this->name = 'hello';
A::test();
}
}
$c = new C;
$c->q();// prints hello
Это на самом деле ошибка какого-то рода, если у вас есть строгие отчеты об ошибках, но не иначе.
Это известная "причуда" PHP. Предназначено для предотвращения обратного распространения, чтобы выяснить, действительно ли мы когда-то создавали объект или нет (помните, что PHP интерпретируется, а не компилируется). Однако доступ к любому нестатическому члену через оператор разрешения области действия, если объект не был создан, вызовет фатальную ошибку.
Предоставлено PHP.net:
class User {
const GIVEN = 1; // class constants can't be labeled static nor assigned visibility
public $a=2;
public static $b=3;
public function me(){
echo "print me";
}
public static function you() {
echo "print you";
}
}
class myUser extends User {
}
// Are properties and methods instantiated to an object of a class, & are they accessible?
//$object1= new User(); // uncomment this line with each of the following lines individually
//echo $object1->GIVEN . "</br>"; // yields nothing
//echo $object1->GIVE . "</br>"; // deliberately misnamed, still yields nothing
//echo $object1->User::GIVEN . "</br>"; // yields nothing
//echo $object1->a . "</br>"; // yields 2
//echo $object1->b . "</br>"; // yields nothing
//echo $object1->me() . "</br>"; // yields print me
//echo $object1->you() . "</br>"; // yields print you
// Are properties and methods instantiated to an object of a child class, & are accessible?
//$object2= new myUser(); // uncomment this line with each of the following lines individually
//echo $object2->GIVEN . "</br>"; // yields nothing
//echo $object2->a . "</br>"; // yields 2
//echo $object2->b . "</br>"; // yields nothing
//echo $object2->me() . "</br>"; // yields print me
//echo $object2->you() . "</br>"; // yields print you
// Are the properties and methods accessible directly in the class?
//echo User::GIVEN . "</br>"; // yields 1
//echo User::$a . "</br>"; // yields fatal error since it is not static
//echo User::$b . "</br>"; // yields 3
//echo User::me() . "</br>"; // yields print me
//echo User::you() . "</br>"; // yields print you
// Are the properties and methods copied to the child class and are they accessible?
//echo myUser::GIVEN . "</br>"; // yields 1
//echo myUser::$a . "</br>"; // yields fatal error since it is not static
//echo myUser::$b . "</br>"; // yields 3
//echo myUser::me() . "</br>"; // yields print me
//echo myUser::you() . "</br>"; // yields print you
?>
Это обратная совместимость с PHP 4. В PHP 4 вы не могли различать метод объекта и глобальную функцию, написанную как метод статического класса. Поэтому оба работали.
Однако с изменениями в объектной модели с PHP 5 - http://php.net/oop5 - было введено ключевое слово static.
И затем, начиная с PHP 5.1.3, вы получаете правильные строгие стандартные предупреждения о таких, как:
Строгие стандарты: нестатический метод Foo::bar() не должен вызываться статически
И / Или:
Строгие стандарты: Нестатический метод Foo::bar() не должен вызываться статически, предполагая $this из несовместимого контекста
который вы должны были включить для вашей настройки разработки. Так что это просто обратная совместимость со временем, когда язык не мог достаточно отличаться, поэтому он был "определен" во время выполнения.
В настоящее время вы можете определить его уже в коде, однако код не сломается, если вы все еще называете его "неправильным".
Демонстрация для запуска сообщений об ошибках и отображения измененного поведения в разных версиях PHP: http://3v4l.org/8WRQH
В PHP 4 не было статического ключевого слова (в контексте объявления функции), но все же разрешалось вызывать методы статически с ::
, Это продолжалось в PHP 5 для обратной совместимости.
Вы можете сделать это, но ваш код будет ошибкой, если вы используете $this
в функции под названием fun1()
Предупреждение В PHP 7 статический вызов нестатических методов не рекомендуется, и генерируется предупреждение E_DEPRECATED. Поддержка статического вызова нестатических методов может быть удалена в будущем.
Начиная с PHP8, это больше не работает.
Возможность статического вызова нестатических методов была окончательно удалена в PHP 8.
Возможность статического вызова нестатических методов удалена. Таким образом
is_callable()
завершится ошибкой при проверке нестатического метода с именем класса (необходимо проверять экземпляр объекта).
Первоначально он устарел в PHP 7.
Статические вызовы методов, не объявленных статическими, устарели и могут быть удалены в будущем.
Не уверен, почему PHP позволяет это, но вы не хотите привыкать делать это. Ваш пример работает только потому, что он не пытается получить доступ к нестатическим свойствам класса.
Что-то простое, как:
<?php
class Foo {
private $color;
public function bar() {
echo 'before';
$this->color = "blue";
echo 'after';
}
}
Foo::bar();
приведет к фатальной ошибке
В большинстве языков вам понадобится экземпляр класса для выполнения методов экземпляра. Похоже, что PHP создаст временный экземпляр при вызове метода экземпляра с помощью оператора разрешения области.
Я заметил, что если вы вызываете нестатический метод self::test() из класса, предупреждение для строгого стандарта не выдается, как при вызове Class::test(). Я считаю, что это не связано с LSB, так как мой класс не был расширен (проверено на PHP 5.5)?
Один из способов статического и нестатического вызова одного и того же метода - использование магических методов.
__call
а также
__callStatic
.
Класс FluentMath (код ниже) - это пример, в котором вы можете вызывать методы
add
или же
subtract
как статически, так и нет:
$res1 = FluentMath::add(5) // add method called statically
->add(3) // add method called non-statically
->subtract(2)
->result();
$res2 = FluentMath::subtract(1)->add(10)->result();
FluentMath класс
class FluentMath
{
private $result = 0;
public function __call($method, $args)
{
return $this->call($method, $args);
}
public static function __callStatic($method, $args)
{
return (new static())->call($method, $args);
}
private function call($method, $args)
{
if (! method_exists($this , '_' . $method)) {
throw new Exception('Call undefined method ' . $method);
}
return $this->{'_' . $method}(...$args);
}
private function _add($num)
{
$this->result += $num;
return $this;
}
private function _subtract($num)
{
$this->result -= $num;
return $this;
}
public function result()
{
return $this->result;
}
}
Если вам нужно полное объяснение того, как работает этот класс, проверьте: