Avatar ·

В чем разница между public, private и protected в PHP?

Привет всем! Я только начал изучать объектно-ориентированное программирование в PHP и никак не могу понять разницу между модификаторами доступа public, private и protected. Вроде бы читал документацию, но на практике всё равно путаюсь. Вот у меня есть простой класс: ```php class Car { public $color = 'red'; private $engineStatus = 'off'; protected $fuelLevel = 100; public function startEngine() { $this->engineStatus = 'on'; } private function checkFuel() { return $this->fuelLevel > 0; } protected function refuel($liters) { $this->fuelLevel += $liters; } } ``` Я создаю объект: ```php $car = new Car(); echo $car->color; // Работает echo $car->engineStatus; // Ошибка! $car->refuel(20); // Тоже не работает ``` Почему я не могу обратиться к свойствам engineStatus и fuelLevel напрямую? И в чем отличие protected от private, если они оба ограничивают доступ? Объясните пожалуйста на пальцах, с примерами, когда что использовать. Заранее спасибо! <<>>

Основная идея модификаторов доступа

Модификаторы доступа (public, private, protected) — это механизм инкапсуляции, который контролирует, откуда можно обращаться к свойствам и методам класса. Представьте себе настоящую машину: у вас есть руль и педали (public — доступны всем), капот (protected — доступен владельцу и механику), и внутренности двигателя (private — только для самого двигателя).

Public — доступен отовсюду

Свойства и методы с модификатором public видны и доступны из любого места: из самого класса, из дочерних классов, и из внешнего кода (глобальной области видимости).

class Car {
    public $color = 'red';
    
    public function honk() {
        echo "Beep!";
    }
}

$car = new Car();
$car->color = 'blue'; // Работает
$car->honk(); // Работает

Private — только внутри класса

Свойства и методы private доступны только внутри того класса, где они объявлены. Даже дочерние классы не имеют к ним доступа. Это самый строгий уровень защиты.

class Car {
    private $engineSerialNumber = 'XYZ123';
    
    private function checkEngine() {
        return $this->engineSerialNumber;
    }
    
    public function getEngineInfo() {
        // Можно вызвать private метод внутри класса
        return $this->checkEngine();
    }
}

class SportsCar extends Car {
    public function tryAccess() {
        // Ошибка! Нельзя обратиться к private свойству родителя
        // echo $this->engineSerialNumber;
        // Ошибка! Нельзя вызвать private метод родителя
        // $this->checkEngine();
    }
}

$car = new Car();
// echo $car->engineSerialNumber; // Ошибка!
echo $car->getEngineInfo(); // Работает через публичный метод

Protected — доступен внутри класса и в наследниках

Модификатор protected — золотая середина. Свойства и методы видны внутри самого класса и внутри всех дочерних классов, но недоступны из внешнего кода.

class Car {
    protected $fuelLevel = 100;
    
    protected function refuel($liters) {
        $this->fuelLevel += $liters;
    }
    
    public function drive($distance) {
        $this->fuelLevel -= $distance * 0.1;
    }
}

class ElectricCar extends Car {
    public function charge($hours) {
        // Можно обратиться к protected свойству родителя
        $this->fuelLevel = $hours * 20;
        
        // Можно вызвать protected метод родителя
        // $this->refuel(10); // Тоже работало бы, если бы метод был
    }
}

$tesla = new ElectricCar();
// $tesla->fuelLevel = 50; // Ошибка! Нельзя извне
$tesla->drive(100); // Работает — публичный метод
$tesla->charge(5); // Работает — публичный метод дочернего класса

Когда что использовать?

  • public — для API класса: методы, которые должны вызывать пользователи вашего класса (например, startEngine(), getPrice()). Свойства лучше делать private/protected и предоставлять доступ через геттеры/сеттеры.
  • private — для внутренних деталей реализации, которые не должны быть видны никому, даже наследникам (например, хранение пароля, внутренние вычисления).
  • protected — когда вы создаете класс, который предполагается расширять (наследовать), и хотите дать дочерним классам доступ к определенным данным или методам, но скрыть их от внешнего мира.

Почему это важно?

Инкапсуляция позволяет:

  • Скрывать сложность реализации
  • Защищать данные от некорректного изменения
  • Обеспечивать гибкость — вы можете менять внутреннюю реализацию, не ломая код, который использует ваш класс
  • Создавать четкие контракты между классами

Попробуйте переписать ваш класс Car с правильным использованием модификаторов: сделайте все свойства private/protected, а доступ к ним организуйте через публичные методы (геттеры и сеттеры). Это сделает ваш код более надежным и удобным для поддержки.

<<>>

Краткое резюме (шпаргалка)

Если совсем просто, то запомните три правила:

  • public = "видно всем" (как витрина магазина)
  • private = "видно только мне" (как личный дневник)
  • protected = "видно мне и моим детям" (как семейный альбом)

Пример с реальным кодом

Давайте перепишем ваш класс правильно:

class Car {
    private $color;
    private $engineStatus = 'off';
    private $fuelLevel = 100;
    
    public function __construct($color) {
        $this->setColor($color);
    }
    
    public function getColor() {
        return $this->color;
    }
    
    protected function setColor($color) {
        // Можно добавить валидацию
        $allowedColors = ['red', 'blue', 'black'];
        if (in_array($color, $allowedColors)) {
            $this->color = $color;
        }
    }
    
    public function startEngine() {
        if ($this->fuelLevel > 0) {
            $this->engineStatus = 'on';
            echo "Engine started!";
        } else {
            echo "No fuel!";
        }
    }
    
    private function checkEngineInternal() {
        // Только внутри класса
        return $this->engineStatus;
    }
}

$car = new Car('red');
echo $car->getColor(); // red
$car->startEngine(); // Engine started!

// $car->setColor('green'); // Ошибка! protected
// $car->fuelLevel = 0; // Ошибка! private

Обратите внимание: все свойства теперь private, а доступ к ним — через публичные методы. Это позволяет контролировать, какие значения можно устанавливать (например, проверять цвет) и как их получать.

Совет: на начальном этапе делайте все свойства private, а методы — public, пока не поймете, что нужно защитить что-то от наследников (тогда используйте protected). Это убережет вас от ошибок и сделает код более предсказуемым.