static
В PHP ключевое слово static определяет свойства и методы уровня класса, доступные без создания экземпляра, а также сохраняет локальные переменные.
Ключевое слово static в PHP
Ключевое слово static в PHP имеет два принципиально разных применения, и с самого начала полезно разграничить их:
- Члены класса —
staticобозначает свойство или метод, принадлежащие самому классу, а не отдельному объекту. Доступ к ним осуществляется без создания экземпляра, и все экземпляры разделяют одно и то же значение. - Статические локальные переменные — внутри функции
staticзаставляет локальную переменную сохранять своё значение между вызовами вместо того, чтобы сбрасываться каждый раз.
В этой главе рассматриваются оба варианта, а также связанные ключевые слова self, static:: и parent::, которые почти всегда встречаются рядом со статическими членами. Если классы и объекты для вас в новинку, сначала прочитайте Классы и объекты.
Статические члены класса
Обычное свойство живёт в каждом объекте. Статическое свойство живёт в классе — существует ровно одна копия, которую разделяют все экземпляры, и она доступна даже при отсутствии экземпляров.
Базовый синтаксис
<?php
class MyClass {
public static $myProperty = "Hello, world!";
public static function myMethod() {
return self::$myProperty;
}
}Обращение к статическому члену выполняется через оператор разрешения области видимости ::, а не через объектный оператор ->:
<?php
echo MyClass::$myProperty; // Hello, world!
echo MyClass::myMethod(); // Hello, world!Внутри класса обращайтесь к собственным статическим членам через self:: (или static::, объясняется ниже) — никогда через $this, так как статический метод может выполняться вообще без объекта.
Для подробного ознакомления смотрите Статические свойства и Статические методы.
Статические локальные переменные
Это же ключевое слово делает совершенно другое внутри функции: оно заставляет переменную сохраняться между вызовами. Без static локальная переменная переинициализируется при каждом вызове; с ним начальное значение устанавливается один раз, а переменная сохраняет то, что содержала в прошлый раз.
<?php
function counter() {
static $count = 0; // initialised only on the first call
$count++;
return $count;
}
echo counter(); // 1
echo counter(); // 2
echo counter(); // 3Это удобно для кэширования дорогостоящего результата или подсчёта количества вызовов функции — без утечки глобальной переменной.
Примеры
Рассмотрим несколько практических примеров статических членов класса:
Примеры использования ключевого слова static в PHP
<?php
// Example 1
class Counter
{
public static $count = 0;
public static function increment()
{
self::$count++;
}
}
Counter::increment();
Counter::increment();
echo Counter::$count . PHP_EOL;
// Example 2
class User
{
public static $name;
public static function setName($name)
{
self::$name = $name;
}
}
User::setName("John Doe");
echo User::$name;В Counter статическое свойство $count является общим, поэтому два вызова increment() накапливают значение до 2. В User метод setName() сохраняет данные в самом классе, а не в каком-либо объекте.
self и static (позднее статическое связывание)
При обращении к статическому члену из метода есть два варианта, и разница имеет значение в контексте наследования:
self::разрешается на этапе компиляции — всегда указывает на класс, в котором написан код.static::использует позднее статическое связывание — разрешается на этапе выполнения к классу, который был фактически вызван.
<?php
class Base {
public static function create() {
return new static(); // runtime class
}
public static function createSelf() {
return new self(); // always Base
}
}
class Child extends Base {}
echo get_class(Child::create()); // Child
echo "\n";
echo get_class(Child::createSelf()); // BaseИспользуйте static::, когда подкласс должен иметь возможность переопределить поведение или быть инстанцированным — приведённый выше шаблон является основой фабричных методов. Используйте self::, когда вы намеренно имеете в виду этот класс. Для вызова статического метода родителя используйте parent::. Смотрите Наследование для более широкого контекста.
Типичные сценарии использования
- Счётчики и общее состояние — единственное значение, отслеживаемое по всем экземплярам, как в примере с
Counter. - Утилиты и вспомогательные методы — не имеющие состояния функции, сгруппированные под классом, например
Math::clamp(), вызываемые без объекта. - Фабричные методы —
User::fromArray($data)возвращает настроенный экземпляр;new static()сохраняет совместимость с подклассами. - Синглтоны и простые кэши — статическое свойство хранит единственный экземпляр или мемоизированный результат.
Для именованных неизменяемых общих значений предпочтите константу класса вместо статического свойства.
Подводные камни
- Нет
$thisв статических методах. Статический метод может быть вызван без объекта, поэтому$thisтам не определён. Обращение к состоянию экземпляра из статического метода — признак неудачного проектирования. - Статические свойства — это квазиглобальное общее состояние. Поскольку все экземпляры разделяют одну копию, изменение её из любого места влияет на всех — это легко ведёт к скрытым зависимостям и нарушению изоляции тестов. Используйте осознанно.
::для статики,->для экземпляров.MyClass::$prop(статическое) против$obj->prop(экземпляра). Обратите внимание, что$остаётся при имени свойства в статической форме:Counter::$count, а неCounter::count.- Статические локальные переменные принадлежат функции, а не вызову. Два рекурсивных вызова разделяют одну и ту же переменную
static, что иногда может удивить.
Заключение
Ключевое слово static выполняет две задачи: определяет свойства и методы уровня класса, разделяемые всеми экземплярами и доступные без объекта, а также заставляет локальную переменную функции сохраняться между вызовами. Комбинируйте его с self::, static:: и parent::, чтобы точно контролировать, к какому классу будут разрешаться статические вызовы. Используйте статические члены для подлинно общего состояния и вспомогательных функций без состояния — но обращайтесь с общими изменяемыми статическими данными с той же осторожностью, что и с глобальными переменными.