php class static variable
Static variables in PHP
I have found different information regarding static variables in PHP but nothing that actually explains what it is and how it really works.
I have read that when used within a class that a static property cannot be used by any object instantiated by that class and that a static method can be used by an object instantiated by the class?
However, I have been trying to research what a static variable does within a function that is not in a class. Also, does a static variable within a function work somewhat like closure in javascript or am I totally off in this assumption?
5 Answers 5
I have read that when used within a class that a static property cannot be used by any object instantiated by that class
It depends on what you mean by that. eg:
and that a static method can be used by an object instantiated by the class.
Yes, an instantiated object belonging to the class can access a static method.
The keyword static in the context of classes behave somewhat like static class variables in other languages. A member (method or variable) declared static is associated with the class and rather than an instance of that class. Thus, you can access it without an instance of the class (eg: in the example above, I could use Foo::$my_var )
However, I have been trying to research what a static variable does within a function that is not in a class.
Also, does a static variable within a function work somewhat like closure in javascript or am I totally off in this assumption.
Outside of classes (ie: in functions), a static variable is a variable that doesn’t lose its value when the function exits. So in sense, yes, they work like closures in JavaScript.
But unlike JS closures, there’s only one value for the variable that’s maintained across different invocations of the same function. From the PHP manual’s example:
PHP, статические переменные внутри методов класса и история одного бага
Вообще я разработчик фронтенда. Но порой приходится работать и с серверной частью. Команда у нас небольшая, и когда все настоящие бэкенд-программисты заняты, бывает быстрее реализовать какой-то метод самому. А иногда мы садимся вместе поработать над задачами, чтобы не терять времени на перегон коммитов туда-сюда. Недавно во время одного из таких раундов парного программирования мы с товарищем по команде столкнулись с багом, который меня так впечатлил, что я решил с вами поделиться.
Итак, когда после обеда я подошёл к своему коллеге Роману parpalak, он как раз закончил приводить в порядок юнит-тесты, и запустил всю пачку. Один из тестов выкинул исключение и упал. Ага, подумали мы, сейчас исправим баг. Запустили тест в одиночестве, вне пакета, и он прошёл успешно.
Прежде чем сбросить с себя послеобеденную дремоту, мы запустили Codeception ещё несколько раз. В пакете тест падал, в одиночку проходил, в пакете падал…
Фаталка Call to private method вылетала из метода, преобразующего объект сущности в массив для отправки клиенту. Недавно механизм этого процесса немного изменился, но ещё не все классы отрефакторили, поэтому в методе стоит проверка, переопределён ли метод, возвращающий список необходимых полей (это старый способ), в дочернем классе. Если нет, список полей формируется через рефлексию (это новый способ), и вызываются соответствующие геттеры. В нашем случае один из геттеров был объявлен как private, и, соответственно, недоступен из базового класса. Всё это выглядит примерно так:
— А что, рефлексия такая тяжёлая операция? — спросил я.
— Ну да, — кивнул Роман.
— Как такое может быть? — спросил я. — Статическая переменная внутри метода шарится при наследовании? Почему тогда мы раньше этого не заметили?
Роман был озадачен не меньше моего. Пока я ходил за кофе, он набросал небольшой юнит-тест с имитацией нашей иерархии классов, но он не падал. Мы что-то упускали из виду. Статическая переменная вела себя неправильно, не так, как мы ожидали, но не во всех случаях, и мы не могли понять, почему. Гугление по запросу «php static variable inside class method» не давало ничего путного, кроме того, что статические переменные — это нехорошо. Well, duh!
Теперь Роман пошёл за кофе, а я в задумчивости открыл PHP-песочницу и написал самый простой код:
простой пример 1
простой пример 2
простой пример 3
Вот оно! Роман как раз вернулся, и я, довольный собой, продемонстрировал свои наработки. Ему понадобилось всего несколько нажатий на клавиатуру в PHPStorm, чтобы отрефакторить участок со статической переменной в отдельный метод:
простой пример 4
В итоге мы объявили метод hasOriginalClientProps как protected и снабдили пространным комментарием.
Анализ
Время не ждало, и мы перешли к дальнейшим задачам, но всё же такое поведение озадачивало. Я решил разобраться, почему же PHP ведёт себя именно таким образом. В документации не удалось нарыть ничего, кроме неясных намёков. Ниже я попробую восстановить картину происходящего, основываясь на вдумчивом чтении PHP Internals Book, PHP Wiki, изучении исходников и информации о том, как реализуются объекты в других языках программирования.
Лично мне такое копирование показалось довольно тяжеловесным. Видимо, разработчики PHP подумали так же и отказались от копирования для приватных методов. Ведь они же всё равно не видны из дочерних и родительских классов! Вон, мы даже фаталку в самом начале рассказа словили из-за этого. Поэтому приватный метод существует в единственном экземпляре по всей иерархии классов, и статические переменные в нём тоже существует в единственном контексте. Поэтому и не заработал пример 4.
Такое поведение повторяется на всех версиях PHP, которые я попробовал в песочнице, начиная с мохнатой 5.0.4.
Почему же баг в коде нашего проекта раньше никак не давал о себе знать? Видимо, сущности редко создавались разнотипными группами, а если и создавались — то рефакторили их одновременно. А вот при прогоне тестов в один запуск скрипта попали два объекта, работающие через разные механизмы, и один из них испортил другому состояние.
Выводы
(ведь в каждой серьёзной статье должны быть выводы)
На этой воодушевляющей ноте я прощаюсь с вами. Надеюсь, что эта статья поможет кому-то избежать тех граблей, на которые наступила мы. Спасибо за внимание!
Готовимся к собеседованию по PHP: ключевое слово «static»
Не секрет, что на собеседованиях любят задавать каверзные вопросы. Не всегда адекватные, не всегда имеющие отношение к реальности, но факт остается фактом — задают. Конечно, вопрос вопросу рознь, и иногда вопрос, на первый взгляд кажущийся вам дурацким, на самом деле направлен на проверку того, насколько хорошо вы знаете язык, на котором пишете.
Попробуем разобрать «по косточкам» один из таких вопросов — что значит слово «static» в PHP и зачем оно применяется?
Ключевое слово static имеет в PHP три различных значения. Разберем их в хронологическом порядке, как они появлялись в языке.
Значение первое — статическая локальная переменная
Однако всё меняется, если мы перед присваиванием поставим ключевое слово static:
Подводные камни статических переменных
Разумеется, как всегда в PHP, не обходится без «подводных камней».
Камень первый — статической переменной присваивать можно только константы или константные выражения. Вот такой код:
с неизбежностью приведет к ошибке парсера. К счастью, начиная с версии 5.6 стало допустимым присвоение не только констант, но и константных выражений (например — «1+2» или «[1, 2, 3]»), то есть таких выражений, которые не зависят от другого кода и могут быть вычислены на этапе компиляции
Камень второй — методы существуют в единственном экземпляре.
Тут всё чуть сложнее. Для понимания сути приведу код:
Такое поведение может быть неожиданным для неподготовленного к нему разработчика и послужить источником ошибок. Нужно заметить, что наследование класса (и метода) приводит к тому, что всё-таки создается новый метод:
Вывод: динамические методы в PHP существуют в контексте классов, а не объектов. И только лишь в рантайме происходит подстановка «$this = текущий_объект»
Значение второе — статические свойства и методы классов
В объектной модели PHP существует возможность задавать свойства и методы не только для объектов — экземпляров класса, но и для класса в целом. Для этого тоже служит ключевое слово static:
Для доступа к таким свойствам и методам используются конструкции с двойным двоеточием («Paamayim Nekudotayim»), такие как ИМЯ_КЛАССА::$имяПеременной и ИМЯ_КЛАССА:: имяМетода().
Само собой разумеется, что у статических свойств и статических методов есть свои особенности и свои «подводные камни», которые нужно знать.
Особенность вторая — static не аксиома!
Обратное не совсем верно:
И кстати, всё написанное выше относится только к методам. Использование статического свойства через «->» невозможно и ведет к фатальной ошибке.
Значение третье, кажущееся самым сложным — позднее статическое связывание
Разработчики языка PHP не остановились на двух значениях ключевого слова «static» и в версии 5.3 добавили еще одну «фичу» языка, которая реализована тем же самым словом! Она называется «позднее статическое связывание» или LSB (Late Static Binding).
Понять суть LSB проще всего на несложных примерах:
Ключевое слово self в PHP всегда значит «имя класса, где это слово написано». В данном случае self заменяется на класс Model, а self::$table — на Model::$table.
Такая языковая возможность называется «ранним статическим связыванием». Почему ранним? Потому что связывание self и конкретного имени класса происходит не в рантайме, а на более ранних этапах — парсинга и компиляции кода. Ну а «статическое» — потому что речь идет о статических свойствах и методах.
Немного изменим наш код:
Теперь вы понимаете, почему PHP ведёт себя в этой ситуации неинтуитивно. self был связан с классом Model тогда, когда о классе User еще ничего не было известно, поэтому и указывает на Model.
Для решения этой дилеммы был придуман механизм связывания «позднего», на этапе рантайма. Работает он очень просто — достаточно вместо слова «self» написать «static» и связь будет установлена с тем классом, который вызывает данный код, а не с тем, где он написан:
Это и есть загадочное «позднее статическое связывание».
Нужно отметить, что для большего удобства в PHP кроме слова «static» есть еще специальная функция get_called_class(), которая сообщит вам — в контексте какого класса в данный момент работает ваш код.
Php class static variable
Эта страница описывает использование ключевого слова static для определения статических методов и свойств. static также может использоваться для определения статических переменных и позднего статического связывания. Для получения информации о таком применении ключевого слова static обратитесь по вышеуказанным страницам.
Объявление свойств и методов класса статическими позволяет обращаться к ним без создания экземпляра класса. К ним также можно получить доступ статически в созданном экземпляре объекта класса.
Статические методы
Пример #1 Пример статического метода
Foo :: aStaticMethod ();
$classname = ‘Foo’ ;
$classname :: aStaticMethod ();
?>
Статические свойства
Пример #2 Пример статического свойства
Результат выполнения данного примера в PHP 8 аналогичен:
User Contributed Notes 28 notes
Here statically accessed property prefer property of the class for which it is called. Where as self keyword enforces use of current class only. Refer the below example:
public function static_test ()<
This is also possible:
You misunderstand the meaning of inheritance : there is no duplication of members when you inherit from a base class. Members are shared through inheritance, and can be accessed by derived classes according to visibility (public, protected, private).
The difference between static and non static members is only that a non static member is tied to an instance of a class although a static member is tied to the class, and not to a particular instance.
That is, a static member is shared by all instances of a class although a non static member exists for each instance of class.
class Derived extends Base
<
public function __construct()
<
$this->a = 0;
parent::$b = 0;
>
public function f()
<
$this->a++;
parent::$b++;
>
>
$i1 = new Derived;
$i2 = new Derived;
To check if a method declared in a class is static or not, you can us following code. PHP5 has a Reflection Class, which is very helpful.
It is important to understand the behavior of static properties in the context of class inheritance:
— Static properties defined in both parent and child classes will hold DISTINCT values for each class. Proper use of self:: vs. static:: are crucial inside of child methods to reference the intended static property.
— Static properties defined ONLY in the parent class will share a COMMON value.
declare( strict_types = 1 );
$a = new staticparent ;
$a = new staticchild ;
It should be noted that in ‘Example #2’, you can also call a variably defined static method as follows:
Static variables are shared between sub classes
class Child1 extends MyParent <
class Child2 extends MyParent <
To check if a function was called statically or not, you’ll need to do:
(I’ll add this to the manual soon).
The static keyword can still be used (in a non-oop way) inside a function. So if you need a value stored with your class, but it is very function specific, you can use this:
echo aclass::b(); //24
echo aclass::b(); //36
echo aclass::b(); //48
echo aclass::$d; //fatal error
Starting with php 5.3 you can get use of new features of static keyword. Here’s an example of abstract singleton class:
abstract class Singleton <
/**
* Prevent direct object creation
*/
final private function __construct ()
/**
* Prevent object cloning
*/
final private function __clone ()
On PHP 5.2.x or previous you might run into problems initializing static variables in subclasses due to the lack of late static binding:
If the init() method looks the same for (almost) all subclasses there should be no need to implement init() in every subclass and by that producing redundant code.
Solution 1:
Turn everything into non-static. BUT: This would produce redundant data on every object of the class.
Short example on a DataRecord class without error checking:
Regarding the initialization of complex static variables in a class, you can emulate a static constructor by creating a static function named something like init() and calling it immediately after the class definition.
/*
this is the example to use new class with static method..
i hope it help
*/
It’s come to my attention that you cannot use a static member in an HEREDOC string. The following code
function __construct()
<
echo
Inheritance with the static elements is a nightmare in php. Consider the following code:
class DerivedClassOne extends BaseClass <
>
class DerivedClassTwo extends BaseClass <
>
At this point I think it is a big pity inheritance does not work in case of static variables/methods. Keep this in mind and save your time when debugging.
Hi, here’s my simple Singleton example, i think it can be useful for someone. You can use this pattern to connect to the database for example.
$objA = MySingleton :: getInstance (); // Object created!
$objB = MySingleton :: getInstance ();