- PHP Static Methods and Properties
- Introduction to PHP static methods and properties
- Static methods
- Static properties
- self vs. $this
- PHP static methods and properties example
- Summary
- Готовимся к собеседованию по PHP: ключевое слово «static»
- Значение первое — статическая локальная переменная
- Значение второе — статические свойства и методы классов
- Значение третье, кажущееся самым сложным — позднее статическое связывание
PHP Static Methods and Properties
Summary: in this tutorial, you will learn about PHP static methods and static properties and understand the differences between the $this and self .
Introduction to PHP static methods and properties
So far, you have learned how to define a class that consists of methods and properties. To use the methods and properties of the class, you create an object and access these methods and properties via the object.
Since these methods and properties are bound to an instance of the class, they are called instance methods and properties.
PHP allows you to access the methods and properties in the context of a class rather than an object. Such methods and properties are class methods and properties.
Class methods and class properties are called static methods and properties.
Static methods
To define a static method, you place the static keyword in front of the function keyword as follows:
class MyClass < public static function staticMethod() < >>
Code language: HTML, XML (xml)
Since a static method is bound to a class, not an individual instance of the class, you cannot access $this inside the method. However, you can access a special variable called self . The self variable means the current class.
The following shows how to call a static method from the inside of the class:
self::staticMethod(arguments);
Code language: PHP (php)
To call a static method from the outside of the class, you use the following syntax:
className::staticMethod(arguments)
Code language: JavaScript (javascript)
MyClass::staticMethod()
Code language: CSS (css)
The following example defines the HttpRequest class that has a static method uri() that returns the URI of the current HTTP request:
class HttpRequest < public static function uri(): string < return strtolower($_SERVER['REQUEST_URI']); > >
Code language: PHP (php)
The following calls the uri() static method of HttpRequest class:
echo HttpRequest::uri();
Code language: PHP (php)
If the current HTTP request is https://www.phptutorial.net/php-static-method/ , the uri() method will return /php-static-method/ .
Static properties
To define a static property, you also use the static keyword:
public static $staticProperty;
Code language: PHP (php)
class MyClass < public static $staticProperty; public static function staticMethod() < >>
Code language: PHP (php)
To access a public static property outside of the class, you also use the class name with the :: operator:
MyClass::$staticProperty;
Code language: PHP (php)
Like the static methods, to access static properties from within the class, you use the self instead of $this as follows:
self::staticProperty
Code language: PHP (php)
self vs. $this
The following table illustrates the differences between the self and $this :
$this | self |
---|---|
Represents an instance of the class or object | Represents a class |
Always begin with a dollar ($) sign | Never begin with a dollar( $ ) sign |
Is followed by the object operator ( -> ) | Is followed by the :: operator |
The property name after the object operator ( -> ) does not have the dollar ($) sign, e.g., $this->property . | The static property name after the :: operator always has the dollar ( $ ) sign. |
PHP static methods and properties example
Suppose that you want to create an App class for your web application. And the App class should have one and only one instance during the lifecycle of the application. In other words, the App should be a singleton.
The following illustrates how to define the App class by using the static methods and properties:
class App < private static $app = null; private function __construct() < >public static function get() : App < if (!self::$app) < self::$app = new App(); > return self::$app; > public function bootstrap(): void < echo 'App is bootstrapping. '; > >
Code language: HTML, XML (xml)
First, define a static property called $app and initialize its value to null :
private static $app = null;
Code language: PHP (php)
Second, make the constructor private so that the class cannot be instantiated from the outside:
private function __construct() Code language: PHP (php)
Third, define a static method called get() that returns an instance of the App class:
public static function get() : App < if (!self::$app) < self::$app = new App(); > return self::$app; >
Code language: PHP (php)
The get() method creates an instance of the App if it has not been created, otherwise, it just simply returns the App’s instance. Notice that the get() method uses the self to access the $app static property.
Fourth, the bootstrap() method is just for demonstration purposes. In practice, you can place the code that bootstraps the application in this method.
The following shows how to use the App class:
$app = App::get(); $app->bootstrap();
Code language: HTML, XML (xml)
In this code, we called the get() static method from the App class and invoked the bootstrap() method of the App ‘s instance.
Summary
- Static methods and properties are bound to a class, not individual objects of the class.
- Use the static keyword to define static methods and properties.
- Use the self keyword to access static methods and properties within the class.
Готовимся к собеседованию по 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(), которая сообщит вам — в контексте какого класса в данный момент работает ваш код.
Удачных собеседований!