PHP: полный мануал с примерами, функциями и ООП
Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Дерево руководства
Глава 195 · От основ к практике
ООП в PHP: от объекта с правилом до сервисного клиента
Зачем нужны объекты, где уместны наследование и композиция и как читать ::.
Класс и объект: зачем объединять состояние с правилами
Для одного вычисления класс часто избыточен. Польза появляется, когда одно состояние живёт дольше одной строки и к нему применяются правила: заказ меняет статус, остаток резервируется, API-клиент хранит адрес и токен. Класс даёт этим данным имя и не позволяет обходить согласованные действия.
- PHP находит объявление класса
Product— это описание, а не готовый товар. newвыделяет новый объект и передаёт два аргумента в__construct().- Параметры с
publicодновременно объявляют и заполняют свойства. - Переменная
$teaхранит ссылку на созданный объект; стрелка вызывает его поведение.
<?php
final class Product
{
public function __construct(
public string $name,
public int $priceKopecks,
) {}
public function priceLabel(): string
{
return number_format($this->priceKopecks / 100, 2, '.', ' ') . ' ₽';
}
}
$tea = new Product('Чай', 34990);
echo $tea->name, ': ', $tea->priceLabel(), "\n";
Ожидаемый результат
Чай: 349.90 ₽
class описывает форму и поведение, new создаёт конкретный объект, __construct() принимает обязательное начальное состояние. Стрелка читает свойство или вызывает метод объекта; $this внутри метода означает именно текущий товар.
<?php
final class Stock
{
public function __construct(
private int $quantity,
) {
if ($quantity < 0) {
throw new InvalidArgumentException('Остаток не может быть отрицательным');
}
}
public function reserve(int $amount): void
{
if ($amount < 1 || $amount > $this->quantity) {
throw new DomainException('Недостаточно товара');
}
$this->quantity -= $amount;
}
public function quantity(): int
{
return $this->quantity;
}
}
$stock = new Stock(5);
$stock->reserve(2);
echo 'Осталось: ', $stock->quantity(), "\n";
Ожидаемый результат
Осталось: 3
Закрытое свойство нельзя случайно изменить снаружи. Все изменения проходят через метод, который проверяет правило предметной области. Объект полезен здесь не ради короткого кода, а ради одного места, где защищается допустимое состояние.
| Элемент | Что делает | Частая ошибка |
|---|---|---|
| class Product | Объявляет класс; имя регистрируется PHP | Считать объявление уже созданным объектом |
| new Product(...) | Создаёт экземпляр и вызывает конструктор | Создавать неполный объект и дописывать обязательные поля позже |
| $this | Текущий объект внутри нестатического метода | Использовать вне объектного контекста |
| -> | Свойство или нестатический метод экземпляра | Путать со статическим :: |
| public/private/protected | Граница доступа к члену | Делать все свойства public и обходить правила |
| final | Запрещает наследование класса или переопределение метода | Считать, что final делает объект неизменяемым |
Имя класса подчиняется правилам идентификаторов и пространств имён. Объект можно создать по переменному имени класса, а строку полного имени безопасно получить через Product::class. Типизированное свойство без начального значения нельзя читать до инициализации. Readonly-класс делает объявленные свойства readonly и запрещает динамические свойства, но не делает вложенные объекты неизменяемыми.
Синтаксис свойств, методов, конструкторов и областей видимости подробно раскрыт в главе о классах и объектах.
Каждый самостоятельный пример запускается отдельно: скачайте его архив, распакуйте целиком и выполните php example.php. Сравните вывод с открытым блоком результата. Контекстные примеры честно отмечены и требуют указанного SAPI, расширения или сервиса.
Наследование, интерфейс и композиция: разные задачи
Наследование выражает отношение «является разновидностью» и позволяет дочернему классу получить доступные члены родителя. PHP поддерживает одного родителя через extends и несколько интерфейсов через implements. Переопределённый метод должен оставаться совместимым: код, ожидающий родителя, не должен ломаться на наследнике.
<?php
interface Notifier
{
public function send(string $message): string;
}
abstract class NamedNotifier implements Notifier
{
public function __construct(protected string $recipient) {}
protected function prefix(): string
{
return '[' . $this->recipient . '] ';
}
}
final class TelegramNotifier extends NamedNotifier
{
public function send(string $message): string
{
return $this->prefix() . $message;
}
}
function notifyPaidOrder(Notifier $notifier, int $orderId): void
{
echo $notifier->send("Заказ #$orderId оплачен"), "\n";
}
notifyPaidOrder(new TelegramNotifier('@shop'), 42);
Ожидаемый результат
[@shop] Заказ #42 оплачен
Потребитель зависит от маленького интерфейса, а общий код имени получателя вынесен в базовый класс. Наследник обязан сохранить совместимый публичный договор. Если общих данных и поведения нет, одной реализации интерфейса обычно достаточно.
Композиция выражает отношение «использует». Для клиента API это обычно точнее: клиент Bitrix24 использует HTTP-транспорт, но не является разновидностью Guzzle. Зависимость через конструктор легко заменить тестовой фикстурой и не тащит весь публичный API библиотеки в ваш класс.
<?php
interface Transport
{
public function post(string $method, array $parameters): array;
}
final class FixtureTransport implements Transport
{
public function post(string $method, array $parameters): array
{
return ['method' => $method, 'items' => [['ID' => 7, 'TITLE' => 'Заявка']]];
}
}
final class B24Client
{
public function __construct(private Transport $transport) {}
public function listMethod(array $parameters, string $method): array
{
$response = $this->transport->post($method, $parameters);
return $response['items'];
}
}
$client = new B24Client(new FixtureTransport());
$result = $client->listMethod(['select' => ['ID', 'TITLE']], 'crm.deal.list');
echo $result[0]['ID'], ': ', $result[0]['TITLE'], "\n";
Ожидаемый результат
7: Заявка
Транспорт подставлен через конструктор, поэтому клиент не наследуется от HTTP-библиотеки и не привязан к её внутренним деталям. В рабочем адаптере вместо фикстуры будет Guzzle или cURL, а бизнес-код сохранит короткий вызов listMethod(). Это композиция: объект получает нужный инструмент как зависимость.
Полный разбор абстракций, трейтов и жизненного цикла находится в практической программе ООП.
Оператор разрешения области видимости ::
Название Paamayim Nekudotayim — историческое имя двух двоеточий в сообщениях парсера. Практический смысл проще: оператор выбирает член на уровне класса или специальной области self, parent, static.
<?php
final class Money
{
public const CURRENCY = 'RUB';
private function __construct(public int $kopecks) {}
public static function fromRubles(int $rubles): self
{
return new self($rubles * 100);
}
}
$price = Money::fromRubles(350);
echo Money::CURRENCY, ' ', $price->kopecks, "\n";
echo Money::class, "\n";
Ожидаемый результат
RUB 35000
Money
:: обращается к члену самого класса: константе, статическому свойству или статическому методу. Money::class возвращает полное имя класса как строку и не создаёт объект. Для обычного метода конкретного экземпляра используется ->.
| Запись | Откуда берётся член | Когда применять |
|---|---|---|
| Order::STATUS_PAID | Конкретный названный класс | Константа класса |
| Order::create() | Конкретный названный класс | Осмысленная статическая фабрика |
| self::method() | Класс, где записан код | Реализация не должна меняться из-за наследника |
| parent::method() | Непосредственный родитель | Дополнить реализацию родителя |
| static::method() | Класс позднего вызова | Полиморфное статическое поведение |
| Order::class | Полное имя класса как строка | Конфигурация, контейнер, логирование |
<?php
class Document
{
public static function selfName(): string
{
return self::class;
}
public static function calledName(): string
{
return static::class;
}
}
final class Invoice extends Document {}
echo Invoice::selfName(), "\n";
echo Invoice::calledName(), "\n";
Ожидаемый результат
Document
Invoice
self закреплено за классом, где записан метод. static использует класс фактического позднего вызова. parent:: явно обращается к реализации родителя. Позднее статическое связывание полезно фабрикам и наследуемым конфигурациям, но не должно скрывать зависимости приложения в глобальном статическом состоянии.
Источники и обсуждения
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


