Статические элементы PHP: методы и свойства

Раздел: Веб-разработка на PHP -> Статические методы

Статические методы и свойства в PHP

Статические методы и свойства принадлежат классу, а не его экземплярам. Они используются для хранения данных, общих для всех объектов, или для реализации утилитарных функций без создания экземпляра. В PHP статические члены объявляются ключевым словом static.

Наиболее эффективный способ применения статического свойства и метода - подсчёт количества созданных объектов класса. Это позволяет отслеживать число экземпляров без глобальных переменных.


class Counter {
    public static int $count = 0;

    public function __construct() {
        self::$count++;
    }

    public static function getCount(): int {
        return self::$count;
    }
}

$obj1 = new Counter();
$obj2 = new Counter();
$obj3 = new Counter();
echo Counter::getCount(); // Результат: 3
  

Пояснение: свойство $count увеличивается при каждом вызове конструктора. Доступ к нему осуществляется через self:: внутри класса или через Counter:: снаружи. Метод getCount() статический, поэтому его можно вызвать без создания объекта.

Типичная проблема: попытка получить доступ к статическому свойству через $this внутри статического метода. Решение - использовать self:: или имя класса.

Как хранить данные, общие для всех экземпляров класса?

Используется статическое свойство для кэширования данных, например, результатов запросов к внешнему API.


class DataProvider {
    private static array $cache = [];

    public static function getData(string $key): mixed {
        if (!isset(self::$cache[$key])) {
            // имитация загрузки
            self::$cache[$key] = "Данные для {$key}";
        }
        return self::$cache[$key];
    }

    public static function clearCache(): void {
        self::$cache = [];
    }
}

echo DataProvider::getData('user_1'); // Данные для user_1
echo DataProvider::getData('user_1'); // из кэша
  

Проблема: статический кэш живёт в течение всего скрипта, что может привести к утечке памяти. Рекомендуется использовать ограничение размера кэша.

Как гарантировать единственный экземпляр класса (синглтон)?

Статическое свойство хранит единственный объект, метод getInstance() возвращает его или создаёт при первом вызове.


class Singleton {
    private static ?Singleton $instance = null;

    private function __construct() {}

    public static function getInstance(): Singleton {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}

$obj1 = Singleton::getInstance();
$obj2 = Singleton::getInstance();
var_dump($obj1 === $obj2); // bool(true)
  

Ошибка: если конструктор не объявлен приватным, класс можно создать через new Singleton(). Решение - сделать конструктор закрытым и использовать статический метод.

Как обеспечить вызов метода из класса наследника при наследовании статических методов?

Используется позднее статическое связывание (static::) вместо self::.


class Base {
    public static function who(): void {
        echo __CLASS__;
    }

    public static function testSelf(): void {
        self::who();
    }

    public static function testStatic(): void {
        static::who();
    }
}

class Child extends Base {
    public static function who(): void {
        echo __CLASS__;
    }
}

Child::testSelf();   // Base
Child::testStatic(); // Child
  

Частая путаница: self:: всегда ссылается на класс, где определён метод, а static:: - на фактически вызванный класс. Использование self:: в унаследованном классе может привести к неожиданным результатам.

Статические члены полезны для реализации фабрик, одиночек, утилитных библиотек и кэширования. Однако их чрезмерное использование усложняет тестирование и нарушает инкапсуляцию.

Расширенные примеры статических методов и свойств

Пример 1: Статический счётчик экземпляров с наследованием

Демонстрация работы static:: для подсчёта объектов каждого подкласса отдельно.

Пример

class BaseCounter {
    public static int $count = 0;

    public function __construct() {
        static::$count++;
    }

    public static function getCount(): int {
        return static::$count;
    }
}

class First extends BaseCounter {}
class Second extends BaseCounter {}

$f1 = new First();
$f2 = new First();
$s1 = new Second();

echo 'First count: ' . First::getCount(); // 2
echo 'Second count: ' . Second::getCount(); // 1
echo 'Base count: ' . BaseCounter::getCount(); // 0
First count: 2
Second count: 1
Base count: 0

Пояснение: каждое обращение к static::$count в конструкторе увеличивает счётчик именно того класса, экземпляр которого создаётся. Если бы использовался self::$count, все экземпляры увеличивали бы счётчик базового класса.

Пример 2: Статическая фабрика для создания объектов

Статический метод возвращает экземпляр нужного подкласса в зависимости от переданного параметра.

Пример

abstract class Animal {
    abstract public function speak(): string;

    public static function create(string $type): Animal {
        return match ($type) {
            'dog' => new Dog(),
            'cat' => new Cat(),
            default => throw new InvalidArgumentException("Unknown type: {$type}")
        };
    }
}

class Dog extends Animal {
    public function speak(): string {
        return 'Woof!';
    }
}

class Cat extends Animal {
    public function speak(): string {
        return 'Meow!';
    }
}

$animal = Animal::create('dog');
echo $animal->speak();
Woof!

Такой подход упрощает добавление новых типов без изменения клиентского кода.

Пример 3: Статические методы в трейтах

Трейты могут содержать статические методы, которые включаются в класс.

Пример

trait Timestampable {
    protected static string $createdAt = '';

    public static function setCreatedAt(string $time): void {
        self::$createdAt = $time;
    }

    public static function getCreatedAt(): string {
        return self::$createdAt;
    }
}

class Article {
    use Timestampable;
}

Article::setCreatedAt('2025-03-10');
echo Article::getCreatedAt();
2025-03-10

Трейты удобны для добавления статического поведения в различные классы без наследования.

Пример 4: Статические константы и их переопределение

Константы в PHP не могут быть изменены, но могут быть объявлены с разными значениями в наследниках (через const).

Пример

class Base {
    public const NAME = 'Base Class';

    public static function getName(): string {
        return static::NAME; // позднее связывание для констант доступно с PHP 8.1
    }
}

class Child extends Base {
    // переопределение константы (разрешено)
}
// Для версий до 8.1: константы не поддерживают static::, но можно использовать статические свойства

В современных версиях (8.1+) константы можно переопределять в наследниках, и static:: будет возвращать актуальное значение.

Пример 5: Вызов нестатического метода из статического контекста (ошибка)

Демонстрация типичной ошибки и способ её обхода.

Пример

class User {
    private string $name;

    public function __construct(string $name) {
        $this->name = $name;
    }

    public function getName(): string {
        return $this->name;
    }

    public static function staticMethod(): void {
        // echo $this->getName(); // Fatal error: Using $this when not in object context
        // Для доступа к нестатическому методу нужен экземпляр
        $user = new self('Guest');
        echo $user->getName();
    }
}

User::staticMethod();
Guest

Из статического метода можно создать новый объект и вызвать его нестатические методы, но использовать $this нельзя.

Статические методы и свойства в PHP - comments

En
Index php static (php)