- Difference between static:: and $this::
- PHP static methods
- 2 Answers 2
- Готовимся к собеседованию по PHP: ключевое слово «static»
- Значение первое — статическая локальная переменная
- Значение второе — статические свойства и методы классов
- Значение третье, кажущееся самым сложным — позднее статическое связывание
Difference between static:: and $this::
Which returns test2 when static::TEST is used and test1 when self::TEST is used. But it also returns test2 when $this::TEST is used. static::TEST can be used inside a static method, whereas $this::TEST requires an instance before being used (so non-usable in static methods). But if one cannot use $this:: in static methods, static:: can be used in non-static methods (like in the example). So, what is the difference between static:: and $this:: in a non static method? Optional complete test
class OneStatic extends AOne < public function test() < return static::TEST; >> class TwoStatic extends OneStatic < const TEST = "test2"; >class OneSelf extends AOne < public function test() < return self::TEST; >> class TwoSelf extends OneSelf < const TEST = "test2"; >class OneThis extends AOne < public function test() < return $this::TEST; >> class TwoThis extends OneThis < const TEST = "test2"; >$objects = array( 'one, static::' => new OneStatic(), 'two, static::' => new TwoStatic(), 'one, self::' => new OneSelf(), 'two, self::' => new TwoSelf(), 'one, $this::' => new OneThis(), 'two, $this::' => new TwoThis(), ); $results = array(); foreach ($objects as $name=>$object) $results[$name] = $object->test(); var_dump($results); ?>
- ‘one, static::’ => ‘test1’
- ‘two, static::’ => ‘test2’
- ‘one, self::’ => ‘test1’
- ‘two, self::’ => ‘test1’
- ‘one, $this::’ => ‘test1’
- ‘two, $this::’ => ‘test2’
So self refers to the class where the method is defined, but there’s no difference between $this:: and static:: in these non static methods.
PHP static methods
I understand that static methods have no access to state of instance objects of their class types and hence referencing $this inside them results in an error.But objects can reference static methods using object to member operator ->
$para1 = $obj->para1; $para2 = $obj->para2; $obj->staticMethod($para1, $para2);
How is this last example possible when statics are resolved in static context. If someone can explain to me the general behaviour of statics in php code. you can even talk about C related concepts if it will help.
Think of a static method as a global function that is invoked using a funky syntax. The same about class static members. The only thing that makes them different than global objects (functions, variables) is the visibility specifiers (protected, private).
2 Answers 2
Since you state that you already understand what static means, I’ll skip over that.
However, it may still be good to reference PHP’s documentation on the static keyword. In particular the following two alerts are important (and hard to glance over, really).
Caution In PHP 5, calling non-static methods statically generates an E_STRICT level warning.
And this one (italic emphasis mine).
Warning In PHP 7, calling non-static methods statically is deprecated, and will generate an E_DEPRECATED warning. Support for calling non-static methods statically may be removed in the future.
So, to cut a long story short: yes, your example will run (for now), because the PHP interpreter will try to fix up your mistake for you. You should however never do this. What the PHP interpreter will do is:
Say your $obj is of type Foo . Then it will read
$obj->staticMethod($para1, $para2);
conclude that staticMethod is static and instead execute
Foo::staticMethod($para1, $para2);
It is of course perfectly fine to pass parameters that are properties of an instance of Foo . It doesn’t matter to staticMethod where the parameters come from.
To elaborate a bit more on why this works, while using $this in a static method is not allowed.
You can think of normal methods as static functions that have one extra: they receive an implicit parameter $this . The value of $this is simply the object on which the method is called. Thus, $obj->do($a, $b, $c) is equivalent to calling Foo::do($obj, $a, $b, $c) and naming the first argument of do , $this . This is convenient, because we can now easily define methods that work on an instance of an object without having to explicitly state over and over again that this instance is a parameter of our methods. Great.
Now back to static functions. The only difference with normal methods is that they do not receive this implicit $this parameter. Thus, using $this inside of them is invalid. Not because it is forbidden, but because it does not reference anything. PHP does not (and cannot) have a clue what $this should refer to.
Another way to look at it. Say that our Foo class has two properties: $para1 and $para2 , both numbers. Say that you write a method that returns the sum of these numbers. One way is to do this:
public static function sum($para1, $para2)
Great. Works. However, it is annoying to have to call it like this
$sum = Foo::sum($obj->para1, $obj->para2);
So, this is what methods are for!
public function sum(/* implicit $this parameter */) < // write looking up the properties once inside the function, instead // of having to write it every time we call the function! return $this->para1 + $this->para2; > // . $sum = $obj->sum(); // $obj is passed implicitly as $this
Because static functions do not receive an implicit $this parameter, using $this inside of them is like trying to use $undefined when you have never defined it. Thus, invalid.
Готовимся к собеседованию по PHP: ключевое слово «static»
Не секрет, что на собеседованиях любят задавать каверзные вопросы. Не всегда адекватные, не всегда имеющие отношение к реальности, но факт остается фактом — задают. Конечно, вопрос вопросу рознь, и иногда вопрос, на первый взгляд кажущийся вам дурацким, на самом деле направлен на проверку того, насколько хорошо вы знаете язык, на котором пишете.
Попробуем разобрать «по косточкам» один из таких вопросов — что значит слово «static» в PHP и зачем оно применяется?
Ключевое слово static имеет в PHP три различных значения. Разберем их в хронологическом порядке, как они появлялись в языке.
Значение первое — статическая локальная переменная
function foo() < $a = 0; echo $a; $a = $a + 1; >foo(); // 0 foo(); // 0 foo(); // 0
В PHP переменные локальны. Это значит, что переменная, определенная и получившая значение внутри функции (метода), существует только во время выполнения этой функции (метода). При выходе из метода локальная переменная уничтожается, а при повторном входе — создается заново. В коде выше такой локальной переменной является переменная $a — она существует только внутри функции foo() и каждый раз при вызове этой функции создается заново. Инкремент переменной в этом коде бессмысленен, поскольку на следующей же строчке кода функция закончит свою работу и значение переменной будет потеряно. Сколько бы раз мы не вызвали функцию foo(), она всегда будет выводить 0…
Однако всё меняется, если мы перед присваиванием поставим ключевое слово static:
function foo() < static $a = 0; echo $a; $a = $a + 1; >foo(); // 0 foo(); // 1 foo(); // 2
- Присваивание выполняется только один раз, при первом вызове функции
- Значение помеченной таким образом переменной сохраняется после окончания работы функции
- При последующих вызовах функции вместо присваивания переменная получает сохраненное ранее значение
Подводные камни статических переменных
Разумеется, как всегда в PHP, не обходится без «подводных камней».
Камень первый — статической переменной присваивать можно только константы или константные выражения. Вот такой код:
с неизбежностью приведет к ошибке парсера. К счастью, начиная с версии 5.6 стало допустимым присвоение не только констант, но и константных выражений (например — «1+2» или «[1, 2, 3]»), то есть таких выражений, которые не зависят от другого кода и могут быть вычислены на этапе компиляции
Камень второй — методы существуют в единственном экземпляре.
Тут всё чуть сложнее. Для понимания сути приведу код:
class A < public function foo() < static $x = 0; echo ++$x; >> $a1 = new A; $a2 = new A; $a1->foo(); // 1 $a2->foo(); // 2 $a1->foo(); // 3 $a2->foo(); // 4
Вопреки интуитивному ожиданию «разные объекты — разные методы» мы наглядно видим на этом примере, что динамические методы в PHP «не размножаются». Даже если у нас будет сто объектов этого класса, метод будет существовать лишь в одном экземпляре, просто при каждом вызове в него будет пробрасываться разный $this.
Такое поведение может быть неожиданным для неподготовленного к нему разработчика и послужить источником ошибок. Нужно заметить, что наследование класса (и метода) приводит к тому, что всё-таки создается новый метод:
class A < public function foo() < static $x = 0; echo ++$x; >> class B extends A < >$a1 = new A; $b1 = new B; $a1->foo(); // 1 $b1->foo(); // 1 $a1->foo(); // 2 $b1->foo(); // 2
Вывод: динамические методы в PHP существуют в контексте классов, а не объектов. И только лишь в рантайме происходит подстановка «$this = текущий_объект»
Значение второе — статические свойства и методы классов
В объектной модели PHP существует возможность задавать свойства и методы не только для объектов — экземпляров класса, но и для класса в целом. Для этого тоже служит ключевое слово static:
class A < public static $x = 'foo'; public static function test() < return 42; >> echo A::$x; // 'foo' echo A::test(); // 42
Для доступа к таким свойствам и методам используются конструкции с двойным двоеточием («Paamayim Nekudotayim»), такие как ИМЯ_КЛАССА::$имяПеременной и ИМЯ_КЛАССА:: имяМетода().
Само собой разумеется, что у статических свойств и статических методов есть свои особенности и свои «подводные камни», которые нужно знать.
Особенность первая, банальная — нет $this. Собственно это проистекает из самого определения статического метода — поскольку он связан с классом, а не объектом, в нём недоступна псевдопеременная $this, указывающая в динамических методах на текущий объект. Что совершенно логично.
Однако, нужно знать, что в отличие от других языков, PHP не определяет ситуацию «в статическом методе написано $this» на этапе парсинга или компиляции. Подобная ошибка может возникнуть только в рантайме, если вы попытаетесь выполнить код с $this внутри статического метода.
не приведет ни к каким ошибкам, до тех пор, пока вы не попытаетесь использовать метод foo() неподобающим образом:
(и сразу получите «Fatal error: Using $this when not in object context»)
Особенность вторая — static не аксиома!
Вот так, да. Статический метод, если он не содержит в коде $this, вполне можно вызывать в динамическом контексте, как метод объекта. Это не является ошибкой в PHP.
Динамический метод, не использующий $this, можно выполнять в статическом контексте. Однако вы получите предупреждение «Non-static method A::foo() should not be called statically» уровня E_STRICT. Тут решать вам — или строго следовать стандартам кода, или подавлять предупреждения. Первое, разумеется, предпочтительнее.
И кстати, всё написанное выше относится только к методам. Использование статического свойства через «->» невозможно и ведет к фатальной ошибке.
Значение третье, кажущееся самым сложным — позднее статическое связывание
Разработчики языка PHP не остановились на двух значениях ключевого слова «static» и в версии 5.3 добавили еще одну «фичу» языка, которая реализована тем же самым словом! Она называется «позднее статическое связывание» или LSB (Late Static Binding).
Понять суть LSB проще всего на несложных примерах:
class Model < public static $table = 'table'; public static function getTable() < return self::$table; >> echo Model::getTable(); // 'table'
Ключевое слово self в PHP всегда значит «имя класса, где это слово написано». В данном случае self заменяется на класс Model, а self::$table — на Model::$table.
Такая языковая возможность называется «ранним статическим связыванием». Почему ранним? Потому что связывание self и конкретного имени класса происходит не в рантайме, а на более ранних этапах — парсинга и компиляции кода. Ну а «статическое» — потому что речь идет о статических свойствах и методах.
class Model < public static $table = 'table'; public static function getTable() < return self::$table; >> class User extends Model < public static $table = 'users'; >echo User::getTable(); // 'table'
Теперь вы понимаете, почему PHP ведёт себя в этой ситуации неинтуитивно. self был связан с классом Model тогда, когда о классе User еще ничего не было известно, поэтому и указывает на Model.
Для решения этой дилеммы был придуман механизм связывания «позднего», на этапе рантайма. Работает он очень просто — достаточно вместо слова «self» написать «static» и связь будет установлена с тем классом, который вызывает данный код, а не с тем, где он написан:
class Model < public static $table = 'table'; public static function getTable() < return static::$table; >> class User extends Model < public static $table = 'users'; >echo User::getTable(); // 'users'
Это и есть загадочное «позднее статическое связывание».
Нужно отметить, что для большего удобства в PHP кроме слова «static» есть еще специальная функция get_called_class(), которая сообщит вам — в контексте какого класса в данный момент работает ваш код.
Удачных собеседований!