Видимость: public, private, protected
До сих пор все свойства были public: их мог прочитать и изменить любой код программы. Для счётчика посещений это удобно, а для банковского счёта опасно. Если свойство $balance открыто, любая строка может написать $acc->balance = -500, и счёт уйдёт в минус в обход всех проверок.
В доме тоже есть разные двери. В прихожую заходит любой гость, а в спальню — только жильцы. Гость может попросить хозяина что-то принести, но сам по комнатам не ходит.
В классе гости — это код снаружи класса, а жильцы — методы самого класса. Давайте закроем баланс от гостей.
private закрывает свойство
<?php
class Account
{
public function __construct(private int $balance) {}
public function getBalance(): int
{
return $this->balance;
}
}
$acc = new Account(50);
echo $acc->getBalance(), "\n"; // 50Разберём по шагам:
private int $balanceв конструкторе объявляет продвинутое свойство, закрытое словомprivate.- Метод
getBalance()написан внутри класса, поэтому видит$this->balanceи возвращает его. - Код снаружи просит баланс через открытый метод, и
echoпечатает50.
Слова public и private — модификаторы видимости. Модификатор видимости — слово перед свойством или методом, которое говорит, какой код может к нему обращаться. В PHP их три.
public
Свойство или метод доступны из любого места программы. Так объявляют то, чем остальной код пользуется, работая с объектом: методы вроде getBalance().
private
Обращаться может только код внутри этого класса. Так закрывают данные, за которыми класс следит сам. Модификатор ставят и перед методом: метод private можно вызвать только из других методов класса.
protected
Для кода снаружи protected закрывает так же, как private. Разница между ними появляется, только когда один класс строят на основе другого.
Геттеры и сеттеры
Метод getBalance() — геттер. Геттер — открытый метод, который отдаёт значение закрытого свойства. Сеттер — открытый метод, который меняет закрытое свойство и при этом проверяет новое значение.
Сеттер для счёта мог бы называться setBalance(int $amount) и не принимать отрицательную сумму. Тогда класс сам следит, чтобы баланс не ушёл в минус, и обойти эту проверку снаружи нельзя.
С PHP 8.4 права на чтение и запись можно разделить и без геттера: свойство public private(set) int $balance читает любой код, а меняет только сам класс.
На собеседовании спрашивают: «Зачем закрывать свойства, если можно оставить их открытыми?» Ответ:
- класс сам проверяет каждое изменение и не даёт объекту попасть в неверное состояние;
- код снаружи зависит только от открытых методов, поэтому устройство класса можно менять, не трогая этот код.
Закрыто для кода, а не для объекта
Здесь картинка с домом неточна. private закрывает свойство от кода снаружи класса, а не от других объектов. Метод класса может читать закрытые свойства любого объекта этого класса, не только своего:
<?php
class Wallet
{
public function __construct(private int $cash) {}
public function hasMoreThan(Wallet $other): bool
{
return $this->cash > $other->cash;
}
}
$mine = new Wallet(30);
var_dump($mine->hasMoreThan(new Wallet(20))); // bool(true)Метод hasMoreThan написан внутри Wallet, поэтому читает и $this->cash, и $other->cash. Ответ «private видно только внутри своего объекта» на собеседовании считают ошибкой.
Частая ошибка
Закрытое свойство читают снаружи класса:
echo $acc->balance, "\n"; // ✗Fatal error: Uncaught Error: Cannot access private property Account::$balanceЭто ошибка Error: строки выше успели выполниться, а на этой программа останавливается. Запись $acc->balance = 100; снаружи заканчивается той же ошибкой. Снаружи к закрытому свойству обращаются только через открытые методы: $acc->getBalance().
Главное
privateзакрывает свойство или метод от кода снаружи класса, аpublicоткрывает для всех.- Снаружи закрытое свойство читают и меняют через открытые методы — геттер и сеттер.
privateпроверяется по классу: метод класса видит закрытые свойства любого объекта этого класса.- Чтение или запись
private-свойства снаружи — ошибкаError, и программа останавливается.