Вопросы по PHP на собеседовании: Middle — часть 2/3 с разбором ответов
Это часть 2 из 3. ← Часть 1: Junior | Часть 3: Senior →
TL;DR: 8 вопросов для Middle PHP-разработчиков — SOLID, copy-on-write, генераторы, OPcache, паттерны, DI, PSR, жизненный цикл запроса. С кодом и разбором ошибок кандидатов. По данным собеседований 2024–2026.
Как пользоваться: Открой перед собеседованием → найди свой уровень → попробуй ответить до чтения разбора → проверь follow-up. Слабые места — в тренажёр.
Это вторая часть серии. Первая — Junior, третья — Senior. Middle-вопросы — это уже не «что такое», а «как работает под капотом» и «почему именно так».
По статистике с easyoffer.ru, SOLID спрашивают у 41% кандидатов на PHP-позиции, а паттерны проектирования — ещё у 35%. Готовься к обоим.
Содержание
- 1. Объясните принципы SOLID на примерах PHP
- 2. Как работает copy-on-write в PHP?
- 3. Что такое генераторы и зачем нужен yield?
- 4. Как работает OPcache и что такое JIT?
- 5. Назовите основные паттерны проектирования
- 6. Что такое Dependency Injection?
- 7. Что такое PSR стандарты?
- 8. Опишите жизненный цикл HTTP-запроса в PHP
1. Объясните принципы SOLID на примерах PHP
SOLID — один из самых частых вопросов для Middle (41% собеседований по данным easyoffer.ru). Интервьюер хочет не определения, а умение применять принципы в реальном коде.
S — Single Responsibility: один класс — одна причина для изменения.
// Плохо: класс и сохраняет, и форматирует отчёт
class Report {
public function generate(): string { /* ... */ }
public function saveToFile(string $path): void { /* ... */ }
public function sendByEmail(string $to): void { /* ... */ }
}
// Хорошо: каждый класс — одна ответственность
class ReportGenerator {
public function generate(): string { /* ... */ }
}
class ReportSaver {
public function saveToFile(Report $report, string $path): void { /* ... */ }
}
O — Open/Closed: открыт для расширения, закрыт для модификации.
L — Liskov Substitution: наследник должен работать везде, где работает родитель, без сюрпризов.
I — Interface Segregation: много маленьких интерфейсов лучше одного большого.
// Плохо
interface Worker {
public function code(): void;
public function test(): void;
public function managePeople(): void; // Junior не управляет людьми
}
// Хорошо
interface Coder {
public function code(): void;
}
interface Tester {
public function test(): void;
}
interface Manager {
public function managePeople(): void;
}
D — Dependency Inversion: зависи от абстракций, не от конкретных реализаций.
Типичная ошибка: Кандидат перечисляет расшифровку аббревиатуры, но не может показать нарушение принципа в реальном коде и предложить рефакторинг.
Follow-up: Покажите пример нарушения Liskov Substitution Principle. (Классический пример: Square extends Rectangle, где setWidth() ломает ожидания.)
2. Как работает copy-on-write в PHP?
Показывает глубину понимания внутренностей PHP. По отзывам на Хабре, этот вопрос отличает мидла от джуна — джун обычно не знает.
Copy-on-write (COW) — оптимизация памяти. Когда переменная присваивается другой, PHP не копирует данные сразу. Копирование происходит только при изменении одной из переменных.
$a = str_repeat('x', 1_000_000); // 1MB строка
$b = $a; // НЕ копирует — $b указывает на те же данные
// memory: ~1MB
$b .= 'y'; // ТЕПЕРЬ копирует, потому что $b изменился
// memory: ~2MB
Внутри PHP использует счётчик ссылок refcount в структуре zval. Пока refcount > 1 и данные не изменяются — все переменные ссылаются на одну область памяти.
$array = range(1, 100_000);
$copy = $array; // refcount = 2, данные общие
// Без COW это бы удвоило потребление памяти
$copy[] = 'new'; // Теперь copy получает свою копию
Это критично для:
- Передачи больших массивов в функции (по значению — но без COW копирование было бы дорогим)
- Работы с коллекциями данных
- Понимания, почему
foreachможет вести себя неожиданно
Типичная ошибка: Кандидат думает, что передача массива в функцию всегда создаёт копию — на самом деле копия создаётся только при модификации (благодаря COW).
Follow-up: Что произойдёт с COW, если передать переменную по ссылке &? (COW отключается — PHP сразу создаёт отдельный zval с is_ref = true.)
3. Что такое генераторы и зачем нужен yield?
Проверяет, умеет ли кандидат работать с большими объёмами данных, не расходуя всю память. Частый вопрос на Middle по данным разборов PHP-собеседований 2024–2026.
Генератор — функция, которая использует yield вместо return. Она не выполняется целиком, а «отдаёт» значения по одному, приостанавливая выполнение.
// Без генератора — все 1 млн строк в памяти
function readAllLines(string $file): array {
return file($file); // потребляет ~100MB для большого файла
}
// С генератором — одна строка за раз
function readLines(string $file): Generator {
$handle = fopen($file, 'r');
while (($line = fgets($handle)) !== false) {
yield $line;
}
fclose($handle);
}
// Использование
foreach (readLines('huge_file.csv') as $line) {
processLine($line); // в памяти — одна строка
}
Ключевые особенности:
- Генератор возвращает объект
Generator, который реализуетIterator yieldможет и отдавать, и принимать значения ($value = yield $result)yield fromделегирует выполнение другому генератору- Генератор можно итерировать только один раз
// yield from — делегирование
function innerGenerator(): Generator {
yield 1;
yield 2;
}
function outerGenerator(): Generator {
yield 0;
yield from innerGenerator();
yield 3;
}
// Результат: 0, 1, 2, 3
Типичная ошибка: Кандидат знает синтаксис yield, но не может объяснить, когда генератор лучше обычного массива и в чём выигрыш по памяти.
Follow-up: Можно ли использовать return внутри генератора? (Да, с PHP 7.0 — значение доступно через $generator->getReturn() после завершения итерации.)
Хорошо, мы разобрали работу PHP под капотом. Дальше — инфраструктурные вопросы: OPcache, паттерны и стандарты.
4. Как работает OPcache и что такое JIT?
Проверяет понимание production-инфраструктуры PHP. Мидл должен понимать, почему PHP быстрый в production и медленный в dev-режиме.
OPcache — встроенное расширение PHP, которое кэширует скомпилированный байткод в разделяемой памяти. Без OPcache PHP при каждом запросе:
- Читает
.phpфайл - Лексический анализ → токены
- Парсинг → AST (Abstract Syntax Tree)
- Компиляция → байткод (opcodes)
- Выполнение байткода в Zend VM
С OPcache шаги 1–4 выполняются один раз, дальше байткод берётся из памяти.
JIT (Just-In-Time, PHP 8.0+) — компилирует горячий байткод в машинный код CPU. Добавляет шаг после OPcache:
; php.ini
opcache.enable=1
opcache.jit=1255
opcache.jit_buffer_size=100M
Когда JIT даёт эффект: математика, обработка изображений, ML — CPU-bound задачи. Для типичного веб-приложения (I/O-bound) JIT даёт минимальный прирост, потому что основное время тратится на базу данных и сеть.
Типичная ошибка: Кандидат думает, что JIT ускоряет любое PHP-приложение. На практике для Laravel/Symfony прирост от JIT — 1-5%, основной выигрыш даёт OPcache.
Follow-up: Почему OPcache нужно сбрасывать при деплое? (Потому что закэшированный байткод не обновится при изменении файлов. Решение: opcache_reset() или opcache.validate_timestamps=0 с opcache_reset() в deploy-скрипте.)
5. Назовите основные паттерны проектирования
Паттерны спрашивают у 35% кандидатов на PHP-позиции. Интервьюер хочет не UML-диаграммы, а понимание, когда какой паттерн применять.
Три категории: порождающие, структурные, поведенческие. Для Middle достаточно знать 5–6 ключевых:
Strategy — выбор алгоритма в рантайме:
interface PaymentStrategy {
public function pay(float $amount): bool;
}
class CreditCardPayment implements PaymentStrategy {
public function pay(float $amount): bool {
// логика оплаты картой
return true;
}
}
class PayPalPayment implements PaymentStrategy {
public function pay(float $amount): bool {
// логика PayPal
return true;
}
}
class OrderService {
public function checkout(Order $order, PaymentStrategy $payment): void {
$payment->pay($order->getTotal());
}
}
Factory Method — создание объектов без указания конкретного класса.
Observer — уведомление зависимых объектов об изменениях (Laravel Events — реализация Observer).
Decorator — добавление поведения без изменения класса (Middleware в Laravel — цепочка декораторов).
Repository — абстракция доступа к данным (Eloquent — ActiveRecord, но часто оборачивают в Repository).
Singleton — единственный экземпляр класса. Важно: в PHP Singleton считается антипаттерном, потому что создаёт скрытые зависимости и усложняет тестирование. Лучше использовать DI-контейнер.
Типичная ошибка: Кандидат описывает Singleton как «полезный паттерн» и не знает, почему в PHP-сообществе его считают антипаттерном.
Follow-up: Почему Singleton затрудняет unit-тестирование? (Глобальное состояние — нельзя подменить зависимость моком. DI решает эту проблему.)
6. Что такое Dependency Injection?
DI — один из ключевых архитектурных вопросов для Middle. Все современные PHP-фреймворки построены на DI-контейнерах, по данным обсуждений на Reddit и Хабре за 2024–2026.
Dependency Injection — паттерн, при котором объект получает свои зависимости извне, а не создаёт их сам.
// Плохо — жёсткая связь (tight coupling)
class UserService {
private MySQLDatabase $db;
public function __construct() {
$this->db = new MySQLDatabase(); // создаёт сам
}
}
// Хорошо — DI через конструктор
class UserService {
public function __construct(
private DatabaseInterface $db // получает извне
) {}
public function getUser(int $id): ?User {
return $this->db->find('users', $id);
}
}
Три типа DI:
- Constructor injection — через конструктор (самый распространённый)
- Method injection — через метод
- Property injection — через публичное свойство (не рекомендуется)
DI Container (IoC Container) — автоматически разрешает и внедряет зависимости. Laravel Service Container, Symfony DI — примеры.
// Laravel — автоматическое разрешение
class OrderController extends Controller {
public function __construct(
private OrderService $orderService, // Laravel сам создаст
private PaymentGateway $payment // и это тоже
) {}
}
Типичная ошибка: Кандидат путает DI (паттерн) и DI Container (инструмент). DI можно делать руками, без контейнера.
Follow-up: Чем DI отличается от Service Locator? (DI — зависимости приходят извне. Service Locator — объект сам запрашивает зависимости из реестра. DI предпочтительнее, потому что зависимости явные.)
Последние два вопроса — стандарты и протокол. Их задают реже, но знание PSR и понимание жизненного цикла запроса показывают зрелость разработчика.
7. Что такое PSR стандарты?
Проверяет, знает ли кандидат о стандартах PHP-сообщества. По отзывам на Хабре, незнание PSR на Middle-собеседовании — красный флаг для интервьюера.
PSR (PHP Standards Recommendations) — стандарты, разработанные PHP-FIG (Framework Interoperability Group). Ключевые:
PSR-1 / PSR-12 — стиль кода. PascalCase для классов, camelCase для методов, 4 пробела для отступов. PSR-12 расширяет PSR-1 с правилами для PHP 7+.
PSR-4 — автозагрузка классов. Маппинг namespace → директория. Composer реализует PSR-4.
// composer.json
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
App\Services\PaymentService → src/Services/PaymentService.php
PSR-7 — HTTP-сообщения. Стандартные интерфейсы для Request/Response. Используют Slim, Laminas.
PSR-11 — Container Interface. Единый интерфейс для DI-контейнеров.
PSR-3 — Logger Interface. Psr\Log\LoggerInterface — реализуют Monolog, все фреймворки.
Типичная ошибка: Кандидат знает только «PSR — это стандарты кода» и не может назвать конкретные PSR кроме стиля кодирования.
Follow-up: Какой PSR реализует Composer для автозагрузки? (PSR-4. Ранее был PSR-0, но он устарел.)
8. Опишите жизненный цикл HTTP-запроса в PHP
Показывает целостное понимание работы PHP-приложения. Часто задают как заключительный вопрос на Middle по отзывам на Glassdoor.
Классический жизненный цикл (на примере nginx + PHP-FPM):
- Клиент отправляет HTTP-запрос
- Nginx принимает запрос, определяет, что это PHP, и передаёт в PHP-FPM через FastCGI
- PHP-FPM выбирает свободный worker-процесс
- Worker загружает скрипт (или берёт байткод из OPcache)
- Выполняется
index.php→ bootstrap фреймворка → routing → controller → response - Ответ возвращается через PHP-FPM → nginx → клиенту
- Worker освобождается — PHP очищает память (shared-nothing architecture)
Shared-nothing architecture — ключевая особенность PHP. Каждый запрос начинается с чистого состояния. Нет утечек памяти между запросами (в отличие от Node.js или Go). Это и преимущество (простота, надёжность), и ограничение (нет persistent connections без расширений).
// Типичный index.php в Laravel
require __DIR__.'/../vendor/autoload.php'; // PSR-4
$app = require_once __DIR__.'/../bootstrap/app.php';
$kernel = $app->make(Kernel::class);
$response = $kernel->handle(
$request = Request::capture()
);
$response->send();
$kernel->terminate($request, $response);
Типичная ошибка: Кандидат описывает только «запрос приходит, ответ уходит» без понимания роли PHP-FPM, OPcache и shared-nothing архитектуры.
Follow-up: Что такое PHP-FPM и чем он отличается от mod_php? (PHP-FPM — FastCGI Process Manager, отдельный процесс. mod_php — модуль Apache, PHP работает внутри Apache. FPM быстрее и масштабируемее.)
Итого Middle: 8 вопросов. Фокус — SOLID, работа PHP под капотом (COW, OPcache, JIT), паттерны, DI, стандарты. Если можешь объяснить почему, а не только что — ты готов.
Как готовиться
- SOLID — возьми свой код, найди нарушения. Рефакторинг собственного кода учит лучше любой книги.
- Внутренности PHP — прочитай серию статей Никиты Попова (nikic) о внутренностях PHP. Это лучший источник.
- Паттерны — DesignPatternsPHP — документация с примерами на PHP.
- DI — попробуй написать мини-приложение без фреймворка, с ручным DI. Потом замени на контейнер.
Как попробовать Sobes AI
Sobes AI — ассистент, который помогает на реальных технических собеседованиях в реальном времени.
- Зайди на sobesai.app
- Скачай и установи приложение
- Запусти во время подготовки — задавай вопросы из этой статьи
- Sobes AI подскажет, если забудешь разницу между Factory и Strategy
- На реальном собесе — работает как невидимый помощник
Это часть 2 из 3. ← Часть 1: Junior | Часть 3: Senior →
Готовитесь к собеседованию?
Sobes AI слушает вопросы интервьюера и генерирует ответы в реальном времени.
Скачать Sobes AI