ООП в PHP с нуля: классы, наследование, интерфейсы
За полчаса вы напишете первый класс, наследника и интерфейс на PHP 8.x, запустите их в консоли и увидите результат. Разберём синтаксис, который часто путают новички.

К концу этого материала у вас будет три рабочих файла: класс, его наследник и интерфейс, который они реализуют. Всё запускается из консоли одной командой, результат виден сразу. Займёт минут тридцать, если PHP уже установлен. Пишем под PHP 8.1 и новее — часть синтаксиса (например, продвижение свойств через конструктор и enum) в старых версиях не работает.
Что нужно до начала
Проверьте, что PHP установлен и версия подходит. Откройте терминал и выполните:
php -v
Должна появиться строка вида PHP 8.1.27 или выше. Если команда не найдена — PHP не установлен или не добавлен в PATH; поставьте его через пакетный менеджер вашей системы или скачайте с php.net.
- PHP 8.1+ — обязательно, иначе часть примеров не запустится.
- Любой текстовый редактор — подойдёт VS Code, PhpStorm, даже Блокнот.
- Отдельная пустая папка под файлы урока — чтобы не путаться.
Composer, веб-сервер и базы данных для этого урока не нужны. Всё гоняем через php имя_файла.php.
Шаг 1. Первый класс
Класс — это описание того, из чего состоит объект (свойства) и что он умеет (методы). Создайте файл user.php и вставьте целиком:
<?php
class User
{
// Продвижение свойств: PHP сам создаст private-свойства
// $name и $email из параметров конструктора (PHP 8.0+)
public function __construct(
private string $name,
private string $email,
) {}
// Метод — функция, привязанная к объекту
public function greet(): string
{
return "Привет, я {$this->name}, почта: {$this->email}";
}
}
// Создаём объект класса
$user = new User('Анна', 'anna@example.com');
echo $user->greet() . PHP_EOL;
Запустите:
php user.php
Проверка: в консоли появится строка Привет, я Анна, почта: anna@example.com. Если так — класс работает.
Ключевое слово $this внутри методов ссылается на текущий объект. private означает, что снаружи класса к свойству напрямую не обратиться — доступ только через методы.
Шаг 2. Наследование
Наследник расширяет родительский класс: получает все его свойства и методы, может добавить свои и переопределить существующие. Создайте admin.php:
<?php
require 'user.php'; // подключаем класс User из шага 1
class Admin extends User
{
public function __construct(
string $name,
string $email,
private array $permissions = [],
) {
// Вызываем конструктор родителя — иначе name и email не заполнятся
parent::__construct($name, $email);
}
public function can(string $action): bool
{
return in_array($action, $this->permissions, true);
}
}
$admin = new Admin('Борис', 'boris@example.com', ['delete', 'ban']);
echo $admin->greet() . PHP_EOL; // метод унаследован от User
echo ($admin->can('ban') ? 'может банить' : 'не может') . PHP_EOL;
php admin.php
Проверка: увидите две строки — приветствие (метод достался от User) и может банить. Значит, наследование сработало.
Главная ошибка новичка здесь — забыть parent::__construct(...). Если конструктор родителя не вызвать, поля name и email останутся незаполненными и greet() вернёт мусор или ошибку.
Наследование — не способ переиспользовать код «по знакомству». Наследуйте только когда наследник действительно является частным случаем родителя:Admin— этоUser. Если связь надуманная, лучше композиция: одно поле-объект вместо цепочки классов.
Шаг 3. Интерфейс
Интерфейс — это контракт: список методов, которые класс обязан реализовать, без самой реализации. Он отвечает на вопрос «что объект умеет», не говоря «как». Создайте notify.php:
<?php
interface Notifiable
{
// Только сигнатура, без тела
public function sendNotification(string $message): void;
}
class EmailUser implements Notifiable
{
public function __construct(private string $email) {}
public function sendNotification(string $message): void
{
echo "Письмо на {$this->email}: {$message}" . PHP_EOL;
}
}
class SmsUser implements Notifiable
{
public function __construct(private string $phone) {}
public function sendNotification(string $message): void
{
echo "SMS на {$this->phone}: {$message}" . PHP_EOL;
}
}
// Функция принимает любой объект, реализующий Notifiable
function broadcast(Notifiable $target, string $text): void
{
$target->sendNotification($text);
}
broadcast(new EmailUser('kate@example.com'), 'Заказ отправлен');
broadcast(new SmsUser('+70000000000'), 'Заказ отправлен');
php notify.php
Проверка: две строки — одна про письмо, другая про SMS. Функция broadcast ничего не знает о конкретных классах, ей важно только, что оба реализуют Notifiable. Это и есть смысл интерфейса: код работает с контрактом, а не с конкретной реализацией.
Чем интерфейс отличается от наследования
Разница на практике сбивает новичков чаще всего. Короткая шпаргалка:
| Признак | extends (наследование) | implements (интерфейс) |
|---|---|---|
| Сколько сразу | Только один класс-родитель | Несколько интерфейсов через запятую |
| Есть ли готовый код | Да, наследуется реализация | Нет, только сигнатуры методов |
| Смысл связи | «является частным случаем» | «умеет делать» |
| Свойства | Наследуются | Только константы, свойств нет |
Один класс может одновременно наследоваться от родителя и реализовывать несколько интерфейсов: class Admin extends User implements Notifiable, Loggable.
Если не получилось
PHP Parse error: syntax error, unexpected tokenрядом с конструктором — скорее всего, у вас PHP ниже 8.0 и продвижение свойств не поддерживается. Проверьтеphp -vи обновите PHP.Fatal error: Uncaught Error: Class "User" not found— забылиrequire 'user.php'или запускаете файл не из той папки. Запускайте из директории, где лежат оба файла.Fatal error: Class Admin contains 1 abstract method and must be declared abstract— вы указалиimplements, но не описали в классе один из методов интерфейса. Реализуйте все методы контракта с точно такой же сигнатурой.Cannot access private property— пытаетесь обратиться кprivate-свойству снаружи класса. Сделайте геттер-метод или объявите свойствоpublic, если доступ действительно нужен.- Typed property must not be accessed before initialization — свойство объявлено с типом, но не заполнено. В наследнике это чаще всего забытый
parent::__construct().
Что дальше
Три кирпича — класс, наследник, интерфейс — вы уже собрали. Следующие шаги в том же направлении:
- Абстрактные классы (
abstract class) — середина между обычным классом и интерфейсом: часть методов с реализацией, часть — только сигнатуры. - Трейты (
trait) — способ переиспользовать методы между несвязанными классами без наследования. - Автозагрузка через Composer — чтобы не писать
requireна каждый файл вручную. Разберите PSR-4 в документации Composer. - Модификаторы
readonlyиenum— появились в PHP 8.1, помогают описывать неизменяемые данные и наборы значений.
Область видимости, магические методы и статические свойства — отдельные темы, здесь мы их сознательно не трогали. Начните с абстрактных классов: они логично продолжают то, что вы уже написали в шаге 3.
Репетитор 1—11 класс и ВУЗ: Математика и физика методом наведения на мысль, помощь с ДЗ.
Спросить за 8 ₽Источники: PHP Manual — Classes and Objects, PHP Manual — Object Interfaces
Частые вопросы
У меня PHP 7.4, примеры не запускаются. Что делать?
Когда выбирать наследование, а когда интерфейс?
Можно ли наследоваться сразу от нескольких классов?
Зачем вообще private, если можно сделать всё public?
Ошибка «must be declared abstract» — я же всё написал. Почему?
Нужен ли Composer для работы с ООП?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.