PHP Middle Developer: Углубленное изучение и архитектура

Этот курс поможет трансформировать базовые знания PHP в профессиональные навыки уровня Middle, фокусируясь на ООП, архитектуре и безопасности. Вы освоите современные инструменты разработки, стандарты PSR и научитесь писать чистый, тестируемый код.

1. Современный PHP: стандарты PSR, Composer и новые возможности языка версии 8+

Современный PHP: стандарты PSR, Composer и новые возможности языка версии 8+

Добро пожаловать на курс PHP Middle Developer. Переход от уровня Junior к Middle — это не просто накопление знаний о синтаксисе, это изменение мышления. Junior пишет код, который работает. Middle пишет код, который легко читать, поддерживать и масштабировать.

В этой вводной статье мы заложим фундамент профессиональной разработки: разберем экосистему Composer, стандарты PSR, которые делают ваш код понятным для других, и ключевые нововведения PHP 8.0, 8.1 и 8.2, изменившие облик языка.

Экосистема и управление зависимостями: Composer

Времена, когда библиотеки подключались через include и скачивались вручную с сайтов разработчиков, давно прошли. Сердцем современной PHP-разработки является Composer — пакетный менеджер уровня приложений.

!Схема взаимодействия Packagist, конфигурационных файлов Composer и папки vendor

Роль composer.json и composer.lock

Понимание разницы между этими двумя файлами — критический навык для Middle-разработчика.

composer.json — это манифест вашего проекта. Здесь вы указываете, какие библиотеки вам нужны и их примерные* версии (например, ^7.4 или ~1.2). composer.lock — это снимок конкретных* версий, установленных в данный момент.

Золотое правило: composer.lock всегда должен попадать в систему контроля версий (Git). Это гарантирует, что все разработчики в команде и продакшн-сервер используют абсолютно идентичные версии библиотек.

Автозагрузка (Autoloading)

Composer также решает проблему подключения файлов. Вместо десятков require_once, вы используете один файл:

Это работает благодаря стандартам PSR, о которых мы поговорим ниже.

Стандарты PSR: Язык, на котором говорят все

PHP долгое время называли «Диким Западом» из-за отсутствия строгих правил. Чтобы решить эту проблему, была создана группа PHP-FIG (PHP Framework Interop Group). Они разрабатывают рекомендации PSR (PHP Standards Recommendations).

Знание этих стандартов обязательно для прохождения любого серьезного собеседования.

Ключевые стандарты для повседневной работы

  • PSR-1 и PSR-12 (Стиль кодирования)
  • Определяют, как должен выглядеть код: где ставить скобки, как называть классы (StudlyCaps) и методы (camelCase). Соблюдение этих правил позволяет разработчику из одной команды легко читать код другой команды.

  • PSR-4 (Автозагрузка)
  • Самый важный стандарт архитектуры. Он связывает пространство имен (namespace) с файловой структурой. Пример конфигурации в composer.json: Это означает, что класс App\Service\UserCreator должен физически находиться в файле src/Service/UserCreator.php. Composer берет на себя всю магию по поиску и подключению этого файла.

  • PSR-3 (Логирование) и PSR-7 (HTTP-сообщения)
  • Эти стандарты описывают интерфейсы. Если вы используете логгер, совместимый с PSR-3 (например, Monolog), вы можете легко заменить его на любой другой PSR-3 логгер, не переписывая код приложения.

    > Стандарты — это не ограничение свободы, а способ избавиться от рутины принятия мелких решений, чтобы сосредоточиться на архитектуре.

    Эволюция языка: PHP 8+

    Версия PHP 8.0 стала переломным моментом. Язык стал строже, быстрее (благодаря JIT-компиляции, хотя для веб-запросов это не всегда заметно) и, главное, выразительнее. Рассмотрим инструменты, которые вы должны использовать ежедневно.

    1. Constructor Property Promotion (Продвижение свойств конструктора)

    Раньше мы писали много шаблонного кода (boilerplate) для инициализации свойств класса.

    До PHP 8:

    В PHP 8+:

    Это сокращает код в разы, делая DTO (Data Transfer Objects) и сервисы компактными и читаемыми.

    2. Union Types (Объединение типов)

    PHP становится все более типизированным. Теперь мы можем указывать, что переменная может принимать несколько типов, без использования PHPDoc аннотаций.

    Это делает код самодокументируемым и избавляет от необходимости передавать null или дефолтные значения для промежуточных аргументов.

    4. Выражение Match

    match — это «старший брат» оператора switch. В отличие от switch, match: * Возвращает значение (это выражение, а не управляющая конструкция). * Использует строгое сравнение (===), а не мягкое (==). * Не требует break.

    Пример:

    5. Nullsafe оператор

    Частая проблема в PHP — цепочка вызовов, где один из элементов может быть null. Раньше приходилось писать много проверок if (user->getAddress() !== null ...).

    Теперь это делается элегантно:

    Если this) { self::Active => 'green', self::Banned => 'red', self::Pending => 'yellow', }; } }

    // Использование function updateStatus(User status) { ... } php readonly class UserProfile { public function __construct( public string uid ) {} } `` Свойства такого класса можно инициализировать только один раз. Любая попытка изменить их позже приведет к фатальной ошибке.

    Заключение

    Современный PHP — это строгий, типизированный и быстрый язык. Использование Composer и следование стандартам PSR обеспечивает профессиональный подход к разработке, а новые возможности синтаксиса PHP 8+ позволяют писать код, который легче читать и сложнее сломать.

    В следующей статье мы углубимся в объектно-ориентированное программирование и разберем принципы SOLID, которые помогут нам правильно организовывать классы и зависимости.

    2. Глубокое погружение в ООП: принципы SOLID, паттерны проектирования и абстракции

    Глубокое погружение в ООП: принципы SOLID, паттерны проектирования и абстракции

    В предыдущей статье мы настроили окружение, разобрались с Composer и новыми фичами PHP 8. Теперь пришло время поговорить о том, что отличает «кодера» от инженера — об архитектуре.

    На уровне Middle вы перестаете думать категориями «как заставить это работать». Вы начинаете думать: «как сделать так, чтобы это не сломалось через полгода, когда мы захотим добавить новую фичу». Ответ кроется в правильном использовании абстракций, соблюдении принципов SOLID и применении паттернов проектирования.

    Сила абстракций: Интерфейсы против Реализаций

    Главная мантра объектно-ориентированного проектирования: программируйте на уровне интерфейсов, а не реализаций.

    Когда ваш код зависит от конкретного класса (например, MySQLConnection), вы жестко связываете компоненты. Если завтра бизнес решит перейти на PostgreSQL или Redis, вам придется переписывать весь код, где использовался new MySQLConnection.

    Абстракция (интерфейс) — это контракт. Она говорит что делает объект, но не как.

    !Диаграмма показывает, как один интерфейс может иметь множество различных реализаций, позволяя подменять их без изменения основного кода.

    Пример плохой зависимости:

    Пример правильной абстракции:

    Теперь ReportService все равно, куда пишутся данные: в файл, в облако или в /dev/null.

    SOLID: Пять столпов чистой архитектуры

    SOLID — это акроним, описывающий пять принципов, соблюдение которых делает код гибким и поддерживаемым. Знание этих принципов — обязательное требование для Middle PHP Developer.

    S — Single Responsibility Principle (Принцип единственной ответственности)

    У класса должна быть только одна причина для изменения.

    Это не значит, что у класса должен быть только один метод. Это значит, что класс должен решать одну бизнес-задачу.

    Если ваш класс User и сохраняет данные в БД, и валидирует email, и отправляет приветственное письмо — это нарушение SRP. Это «Божественный объект» (God Object).

    Как исправить: * User — хранит состояние (DTO/Entity). * UserRepository — работает с БД. * UserValidator — проверяет данные. * EmailService — шлет письма.

    O — Open/Closed Principle (Принцип открытости/закрытости)

    Программные сущности должны быть открыты для расширения, но закрыты для модификации.

    Вы должны иметь возможность добавить новую функциональность, не переписывая старый работающий код.

    Представьте, что у вас есть класс расчета доставки:

    2. Factory Method (Фабричный метод)

    Порождающий паттерн. Определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс инстанцировать.

    Применение: Когда заранее неизвестно, объекты каких типов нужно создавать. Например, в зависимости от конфига создавать Логгер в файл или в базу данных.

    3. Decorator (Декоратор)

    Структурный паттерн. Позволяет динамически добавлять объектам новую функциональность, оборачивая их в полезные «обертки».

    !Визуализация показывает, как вызов проходит через цепочку оберток, каждая из которых может добавить свое поведение до или после вызова основного объекта.

    Пример: У вас есть сервис, возвращающий текст. Вы хотите добавить кэширование, а затем логирование. Вместо того чтобы писать один монструозный класс, вы создаете CachingDecorator и LoggingDecorator, которые оборачивают базовый сервис.

    Заключение

    Переход к Middle-уровню требует смены парадигмы. Вы перестаете писать код линейно и начинаете строить систему из кирпичиков-абстракций.

    Соблюдение SOLID и использование паттернов может показаться избыточным для простых скриптов («Hello World»), но в реальных проектах со сложной бизнес-логикой это единственный способ избежать превращения кода в «спагетти», которое невозможно поддерживать.

    В следующей статье мы рассмотрим, как гарантировать работоспособность этой архитектуры с помощью автоматизированного тестирования (Unit и Integration тесты).

    3. Работа с данными: продвинутый SQL, PDO и проектирование архитектуры баз данных

    Работа с данными: продвинутый SQL, PDO и проектирование архитектуры баз данных

    В предыдущих статьях мы разобрали современные стандарты PHP и принципы объектно-ориентированного проектирования (SOLID). Однако даже самая красивая архитектура кода бессильна, если слой работы с данными спроектирован неверно. Для Middle-разработчика база данных — это не «черный ящик», куда мы просто складываем информацию, а сложный инженерный механизм, требующий тонкой настройки.

    В этой статье мы выйдем за рамки простых SELECT * и научимся работать с данными профессионально: обеспечивать их целостность, защищать от взлома и оптимизировать производительность.

    PDO: Безопасность и абстракция

    PHP Data Objects (PDO) — это стандарт де-факто для работы с базами данных в PHP. В отличие от устаревших расширений mysql_ или специфичных mysqli, PDO предоставляет единый интерфейс для доступа к различным СУБД (MySQL, PostgreSQL, SQLite и др.).

    Подготовленные выражения (Prepared Statements)

    Главная угроза безопасности веб-приложений — SQL-инъекции. Они происходят, когда данные пользователя конкатенируются (склеиваются) с SQL-запросом напрямую.

    Неправильно (уязвимость):

    Если злоумышленник передаст в email строку ' OR '1'='1, он получит доступ ко всем пользователям.

    Правильно (PDO):

    Механизм работает так: сначала база данных получает шаблон запроса, компилирует его и строит план выполнения. Данные передаются позже, отдельно от кода запроса. СУБД воспринимает их строго как данные, а не как исполняемые команды.

    Обработка ошибок

    По умолчанию PDO может молча возвращать false при ошибках. Для профессиональной разработки необходимо включить режим исключений. Это позволяет ловить ошибки базы данных через try-catch, как и любые другие ошибки в PHP.

    Параметр PDO::ATTR_EMULATE_PREPARES => false важен: он заставляет использовать настоящие подготовленные выражения на стороне БД, а не эмуляцию средствами PHP, что повышает безопасность и производительность.

    Транзакции и ACID

    Представьте ситуацию: вы переводите деньги со счета А на счет Б. Деньги списались со счета А, но в этот момент сервер упал, и на счет Б они не зачислились. Деньги исчезли. Чтобы этого избежать, используются транзакции.

    Транзакция — это группа последовательных операций с базой данных, которая представляет собой логическую единицу работы.

    Транзакции должны соответствовать требованиям ACID:

  • Atomicity (Атомарность): Транзакция выполняется целиком или не выполняется вовсе. Нельзя выполнить половину транзакции.
  • Consistency (Согласованность): Транзакция переводит базу данных из одного корректного состояния в другое. Нарушение ограничений (constraints) отменит транзакцию.
  • Isolation (Изолированность): Параллельные транзакции не должны влиять друг на друга (уровень изоляции можно настраивать).
  • Durability (Надежность): Если транзакция зафиксирована (commit), изменения сохранены навсегда, даже при сбое питания.
  • Пример реализации в PHP

    Если у вас 100 статей, вы сделаете 101 запрос к базе данных. Это убивает производительность.

    Решение (Eager Loading / JOIN): Получить все данные одним запросом:

    Или, если используется подход с двумя запросами (как в некоторых ORM):

  • Получить все статьи.
  • Собрать все author_id.
  • SELECT * FROM authors WHERE id IN (1, 2, 5, ...).
  • EXPLAIN: Анализ запросов

    Как понять, используется ли индекс? Используйте команду EXPLAIN перед вашим запросом в SQL-клиенте.

    Смотрите на колонки: * type: если видите ALL, значит идет полное сканирование таблицы (плохо). Если ref или eq_ref — используется индекс (хорошо). * key: название индекса, который решила использовать БД. * rows: примерное количество строк, которые БД пришлось просмотреть.

    Заключение

    Работа с данными на уровне Middle — это баланс. Баланс между нормализацией и скоростью чтения, между количеством индексов и скоростью записи. Использование PDO с подготовленными выражениями защищает ваше приложение, транзакции гарантируют целостность данных, а понимание индексов и EXPLAIN позволяет вашему приложению масштабироваться.

    В следующей части курса мы перейдем к построению REST API и узнаем, как правильно отдавать эти данные внешнему миру.

    4. Веб-разработка и архитектура приложений: паттерн MVC, REST API и HTTP протокол

    Веб-разработка и архитектура приложений: паттерн MVC, REST API и HTTP протокол

    Мы прошли долгий путь: настроили окружение, изучили современные стандарты PHP, углубились в ООП и научились эффективно работать с базами данных. Теперь у нас есть мощный «двигатель» (бизнес-логика) и надежное «хранилище» (база данных). Пришло время построить «интерфейс управления» и научить наше приложение общаться с внешним миром.

    В этой статье мы разберем фундаментальные принципы веб-разработки: как устроен протокол HTTP, как организовать код с помощью паттерна MVC и как спроектировать правильный REST API.

    Протокол HTTP: Язык интернета

    Многие Junior-разработчики воспринимают HTTP просто как транспорт: «отправил запрос — получил ответ». Middle-разработчик должен понимать семантику протокола. HTTP (HyperText Transfer Protocol) — это протокол прикладного уровня, работающий по модели «клиент-сервер».

    !Визуализация цикла запрос-ответ с указанием основных компонентов каждого этапа.

    Структура запроса и ответа

    Каждый HTTP-запрос состоит из трех ключевых частей:

  • Стартовая строка: Метод (GET, POST...) и URI (адрес ресурса).
  • Заголовки (Headers): Метаданные (например, Content-Type: application/json или Authorization: Bearer ...).
  • Тело (Body): Данные запроса (отсутствует у GET-запросов).
  • Семантика методов HTTP

    Выбор правильного метода — это вопрос архитектурной гигиены.

    * GET: Получение данных. Безопасный метод, не меняет состояние сервера. * POST: Создание нового ресурса. Не идемпотентный метод (об этом ниже). * PUT: Полная замена ресурса. Если ресурса нет, он может быть создан. * PATCH: Частичное обновление ресурса. Мы отправляем только те поля, которые изменились. * DELETE: Удаление ресурса.

    Коды состояния (Status Codes)

    Возвращать всегда 200 OK с текстом ошибки в JSON {"error": "Bad Request"} — это дурной тон и признак непрофессионализма. Используйте коды по назначению:

    * 2xx (Success): 200 OK (успех), 201 Created (ресурс создан). * 3xx (Redirection): 301 Moved Permanently (постоянный редирект), 302 Found (временный). * 4xx (Client Error): 400 Bad Request (ошибка валидации), 401 Unauthorized (нужна авторизация), 403 Forbidden (доступ запрещен, даже если авторизован), 404 Not Found. * 5xx (Server Error): 500 Internal Server Error (упал код), 502 Bad Gateway (ошибка Nginx/PHP-FPM).

    Паттерн MVC: Разделяй и властвуй

    MVC (Model-View-Controller) — это архитектурный паттерн, который отделяет данные приложения от пользовательского интерфейса и управляющей логики.

    !Схема взаимодействия компонентов MVC, показывающая разделение ответственности.

    1. Model (Модель)

    Это не просто класс, расширяющий ActiveRecord или Entity. Модель — это вся бизнес-логика вашего приложения. Она включает в себя:

    * Сущности (Entities). * Сервисы (Services). * Репозитории (Repositories). * Валидаторы.

    Модель ничего не знает о том, как она будет отображаться (HTML или JSON) и откуда пришли данные.

    2. View (Представление)

    Отвечает за отображение данных. В классическом веб-приложении это HTML-шаблоны (Blade, Twig). В API-приложении роль View выполняет слой трансформации данных (например, API Resources в Laravel), который превращает объекты PHP в JSON.

    3. Controller (Контроллер)

    Это «клей» или дирижер. Его задача:

  • Принять HTTP-запрос.
  • Извлечь из него данные.
  • Вызвать нужный метод Модели (Сервиса).
  • Получить результат и отдать его в View.
  • > Золотое правило: «Thin Controller, Fat Model» (Тонкий контроллер, толстая модель). Контроллер не должен содержать бизнес-логики, SQL-запросов или сложной валидации.

    Пример «Тонкого контроллера»:

    Архитектура REST API

    REST (Representational State Transfer) — это архитектурный стиль взаимодействия компонентов распределенного приложения в сети. Это не стандарт (как W3C), а набор ограничений и рекомендаций.

    Ресурсы (Resources)

    В REST все строится вокруг ресурсов. Ресурс — это существительное.

    * Плохо: POST /api/createUser, GET /api/getBooks * Хорошо: POST /api/users, GET /api/books

    Действие определяется HTTP-методом, а не URL.

    Идемпотентность (Idempotency)

    Это понятие пришло из математики и информатики, и Middle-разработчик обязан его понимать. Операция считается идемпотентной, если её многократное выполнение приводит к тому же результату, что и однократное.

    Математически это можно записать так:

    Где — это функция (операция), а — состояние системы. Это означает, что применение функции к результату её же выполнения не меняет результат.

    В контексте HTTP:

    * GET, PUT, DELETE — идемпотентны. Если вы отправите запрос на удаление пользователя DELETE /users/1 пять раз подряд, результат будет один: пользователя с ID 1 не существует (первый раз он удалится, остальные разы — уже удален, состояние системы идентично). * POSTне идемпотентен. Если вы отправите POST /users пять раз, вы создадите 5 разных пользователей. * PATCH — обычно не идемпотентен (зависит от реализации).

    Stateless (Отсутствие состояния)

    Сервер не должен хранить информацию о состоянии клиента между запросами. Каждый запрос должен содержать всю необходимую информацию для его обработки (например, JWT-токен в заголовке Authorization).

    Это критически важно для масштабирования. Если состояние хранится в сессии на конкретном сервере, вы не сможете просто добавить второй сервер и балансировщик нагрузки, так как пользователь «потеряется» при переключении между серверами.

    HATEOAS

    Hypermedia as the Engine of Application State. Это высший уровень зрелости REST (уровень 3 по модели Ричардсона). Идея в том, что API возвращает не только данные, но и ссылки на действия, которые можно с ними совершить.

    Пример ответа:

    На практике HATEOAS используется редко из-за сложности реализации на клиенте, но знать о нем нужно.

    Заключение

    Понимание HTTP, MVC и REST — это база, на которой строится любой современный бэкенд. Правильное использование методов HTTP делает API предсказуемым. Соблюдение MVC делает код чистым. А принципы REST позволяют системе масштабироваться.

    В следующей статье мы перейдем к практике и рассмотрим популярные PHP-фреймворки (Symfony и Laravel), которые реализуют эти паттерны «из коробки», и научимся выбирать инструмент под задачу.

    5. Качество кода и безопасность: Unit-тестирование, профилирование и защита от уязвимостей

    Качество кода и безопасность: Unit-тестирование, профилирование и защита от уязвимостей

    В предыдущих статьях мы построили надежный фундамент: настроили окружение, спроектировали базу данных и создали REST API на основе паттерна MVC. Ваше приложение работает, данные сохраняются, пользователи регистрируются. Но готово ли оно к продакшену?

    Уровень Middle Developer отличается от Junior не только умением писать код, но и умением отвечать за его качество. В этой статье мы разберем три кита профессиональной разработки: автоматическое тестирование, анализ производительности и безопасность.

    1. Автоматическое тестирование: Страховка для вашего кода

    Многие новички считают тесты пустой тратой времени. «Я проверил в браузере, все работает», — говорят они. Но что произойдет через месяц, когда вы измените логику в одном классе, а сломается функционал в совершенно другом месте? Ручное тестирование не масштабируется.

    Пирамида тестирования

    Правильная стратегия тестирования строится в виде пирамиды.

    !Схема пирамиды тестирования: много быстрых модульных тестов в основании и мало медленных E2E тестов на вершине.

  • Unit-тесты (Модульные): Проверяют самый маленький кусок кода (обычно метод класса) в изоляции. Они быстрые и их должно быть много.
  • Интеграционные тесты: Проверяют, как модули работают вместе (например, взаимодействие Сервиса и Базы данных).
  • E2E (End-to-End): Эмулируют действия реального пользователя в браузере.
  • Мы сосредоточимся на Unit-тестировании с использованием PHPUnit, так как это основа качества кода.

    Анатомия Unit-теста

    Хороший тест следует паттерну AAA: Arrange (Подготовка), Act (Действие), Assert (Проверка).

    Представим, у нас есть сервис расчета скидок:

    Используя Mock-объекты, мы тестируем только логику UserService, изолируя его от Mailer.

    Метрики качества: Цикломатическая сложность

    Как понять, что код сложен для тестирования? Существует метрика — Цикломатическая сложность. Она показывает количество линейно независимых маршрутов через программный код.

    Формула вычисления:

    Где: * — цикломатическая сложность. * — количество ребер (переходов) в графе потока управления. * — количество узлов (блоков кода) в графе потока управления. * — количество компонентов связности (обычно 1 для одного метода).

    Простыми словами: каждый if, for, while, case увеличивает сложность. Если , метод считается сложным, его трудно покрыть тестами, и его следует разбить на части (рефакторинг).

    2. Статический анализ кода

    Тесты запускают код. Но можно найти ошибки еще до запуска. Для этого используются инструменты статического анализа, такие как PHPStan или Psalm.

    Они читают ваш код, проверяют типы данных и находят потенциальные баги: * Вызов метода на null. * Передача аргумента неверного типа. * Использование несуществующих переменных.

    В composer.json это выглядит так:

    Чем выше уровень (от 0 до 9), тем строже проверки. Middle-разработчик должен стремиться к прохождению максимального уровня.

    3. Профилирование: Борьба за миллисекунды

    «Код работает медленно» — это субъективное ощущение. Инженер оперирует фактами. Чтобы найти узкое место, используется профилирование.

    Xdebug Profiler

    Мы уже знаем Xdebug как отладчик. Но он умеет работать и как профилировщик. Он записывает каждый вызов функции, сколько времени она заняла и сколько памяти потребила.

    Результат профилирования (файл cachegrind) можно открыть в программе (например, QCacheGrind) и увидеть дерево вызовов.

    Типичные проблемы производительности:

  • N+1 проблема в SQL (мы обсуждали это в статье про БД).
  • Загрузка больших файлов в память (вместо использования потоков/Streams).
  • Лишние циклы или неоптимальные алгоритмы.
  • > Преждевременная оптимизация — корень всех зол. Сначала сделайте код чистым и рабочим, затем профилируйте, и только потом оптимизируйте узкие места.

    4. Безопасность: OWASP и защита данных

    Безопасность — это не фича, которую можно «прикрутить» в конце. Это процесс. Рассмотрим ключевые уязвимости из списка OWASP Top 10.

    XSS (Cross-Site Scripting)

    Атака, при которой злоумышленник внедряет вредоносный JS-код на страницу, которую просматривают другие пользователи.

    Пример: Пользователь вводит в комментарий: <script>alert(document.cookie)</script>. Если вы выведете это как echo string, ENT_QUOTES, 'UTF-8'). * В шаблонизаторах (Twig, Blade): {{ hash = password_hash(inputPassword, $hash)) { // Пароль верный } ``

    Заключение

    Качество кода — это совокупность факторов. Это тесты, которые гарантируют, что бизнес-логика верна. Это статический анализ, который предотвращает глупые ошибки. Это профилирование, которое делает приложение быстрым. И это безопасность, которая защищает данные пользователей.

    Теперь, когда вы умеете писать надежный, безопасный и тестируемый код, вы готовы к следующему шагу — автоматизации процессов доставки кода на сервер. В следующей части мы поговорим про CI/CD, Docker и деплой.