Библиотека собеса по PHP | вопросы с собеседований
Ir al canal en Telegram
Вопросы с собеседований по PHP и ответы на них. По рекламе: @proglib_adv Учиться у нас: clc.to/5JWKxA Для обратной связи: @proglibrary_feeedback_bot
Mostrar más3 114
Suscriptores
-324 horas
-67 días
-1930 días
Archivo de publicaciones
❓ Расскажите о паттерне Command
Command — это поведенческий паттерн, который превращает запрос в отдельный объект, содержащий всю информацию о запросе.
Простыми словами: вместо прямого вызова метода вы создаёте объект-команду, которую можно передать, поставить в очередь, отменить или повторить.
▪️ Когда использовать
— Нужен undo/redo
— Команды нужно ставить в очередь, логировать или выполнять отложенно
— Хотите отделить объект, инициирующий операцию, от объекта, выполняющего её
▪️ Минус
Усложняет код: каждая операция — отдельный класс.
❓ Расскажите о паттерне Observer
Observer — это поведенческий паттерн, который создаёт механизм подписки, позволяя одним объектам следить за изменениями в других.
Простыми словами: как подписка на Telegram-канал — когда выходит новый пост, все подписчики получают уведомление автоматически.
▪️ Когда использовать
— Изменение одного объекта требует обновления других, и набор зависимых объектов заранее неизвестен
— Хотите избежать жёсткой связи между компонентами
▪️ Минус
Подписчики оповещаются в непредсказуемом порядке; утечки памяти, если забыть отписаться.
❓ Расскажите о паттерне Proxy
Proxy — это структурный паттерн, который подставляет вместо реального объекта объект-заместитель, контролирующий доступ к оригиналу.
Простыми словами: прокси выглядит как оригинал, но перед вызовом может проверить права, закэшировать результат или создать объект лениво.
▪️ Виды прокси
— Protection Proxy — контроль доступа (Spring Security)
— Caching Proxy — кэширование результатов
— Lazy Proxy — отложенная инициализация (Hibernate lazy loading)
— Remote Proxy — доступ к удалённому объекту (RMI)
▪️ Proxy vs Decorator
— Proxy: контролирует жизненный цикл и доступ к объекту
— Decorator: добавляет поведение, не контролируя доступ
▪️ Минус
Увеличивает задержку отклика; усложняет отладку при нескольких слоях проксирования.
❓ Расскажите о паттерне Facade
Facade — это структурный паттерн, который предоставляет простой интерфейс к сложной подсистеме, скрывая внутреннюю сложность.
Простыми словами: один метод вместо десяти вызовов в разные сервисы. Пульт от телевизора — это фасад к электронике внутри.
▪️ Когда использовать
— Подсистема сложная, а клиенту нужен простой вход
— Хотите уменьшить связанность между слоями
— Типичный пример: сервисный слой в Spring-приложении
▪️ Минус
Фасад может стать God Object, если взять на себя слишком много логики.
💬 Обратная связь
Как часто вы проходите собеседования?
🔥 — Сейчас активно ищу работу
👍🏼 — Раз в несколько месяцев
❤️ — Раз в полгода-год
😁 — Не прохожу, уже работаю/ещё учусь
✔️ PHP-тест: клон, который мутирует оригинал
Пользователь нажал «Повторить заказ». Цены в его старом заказе изменились.
📦 Задание
Фича: кнопка «Повторить заказ» в личном кабинете. Копирует предыдущий заказ, применяет промокод, создаёт новый черновик. Оригинал помечается флагом repeated для аналитики.
Через неделю — тикет от бухгалтерии: суммы в старых заказах не сходятся с тем, что было при оплате. Проблема только у заказов, которые хотя бы раз «повторяли». Суммы занижены ровно на размер скидки по промокоду.
// src/Order/Order.php
class Order
{
public function __construct(
private ?int $id,
private int $userId,
private array $items,
private string $status,
private bool $repeated = false,
) {}
public function getId(): ?int { return $this->id; }
public function getItems(): array { return $this->items; }
public function getStatus(): string { return $this->status; }
public function resetForRepeat(): void
{
$this->id = null;
$this->status = 'draft';
$this->repeated = false;
}
public function markAsRepeated(): void
{
$this->repeated = true;
}
public function calculateTotal(): int
{
return array_sum(array_map(
fn(OrderItem $i) => $i->getSubtotal(),
$this->items
));
}
}
// src/Order/OrderItem.php
class OrderItem
{
public function __construct(
private int $productId,
private int $qty,
private int $price,
) {}
public function getProductId(): int { return $this->productId; }
public function getQty(): int { return $this->qty; }
public function getPrice(): int { return $this->price; }
public function getSubtotal(): int
{
return $this->qty * $this->price;
}
public function applyDiscount(int $percent): void
{
$this->price = (int) round(
$this->price * (1 - $percent / 100)
);
}
}
// src/Order/RepeatOrderHandler.php
class RepeatOrderHandler
{
public function __construct(
private OrderRepository $orders,
private PromoService $promo,
) {}
public function handle(int $originalId, ?string $promoCode): Order
{
$original = $this->orders->findById($originalId);
$copy = clone $original;
$copy->resetForRepeat();
if ($promoCode !== null) {
$discount = $this->promo->resolve($promoCode);
foreach ($copy->getItems() as $item) {
$item->applyDiscount($discount->percent);
}
}
$this->orders->save($copy);
$original->markAsRepeated();
$this->orders->save($original);
return $copy;
}
}
// src/Repository/OrderRepository.php
class OrderRepository
{
public function __construct(private PDO $pdo) {}
public function save(Order $order): void
{
if ($order->getId() === null) {
$this->insert($order);
} else {
$this->update($order);
}
}
private function update(Order $order): void
{
$this->pdo->prepare(
'UPDATE orders SET status = ?, repeated = ? WHERE id = ?'
)->execute([$order->getStatus(), (int) $order->isRepeated(), $order->getId()]);
$this->pdo->prepare('DELETE FROM order_items WHERE order_id = ?')
->execute([$order->getId()]);
foreach ($order->getItems() as $item) {
$this->pdo->prepare(
'INSERT INTO order_items (order_id, product_id, qty, price) VALUES (?, ?, ?, ?)'
)->execute([$order->getId(), $item->getProductId(), $item->getQty(), $item->getPrice()]);
}
}
}
🔹 Задачи
— Объяснить, каким образом цены в оригинальном заказе оказались изменены в базе
— Исправить код так, чтобы оригинал гарантированно не мутировал
Ставьте → 🔥 если нравится формат. Если нет → 🌚
💬 Решения пишите в комменты под спойлер — сравним подходы.❓ Расскажите о паттерне Decorator
Decorator — это структурный паттерн, позволяющий на лету расширять поведение объекта, не изменяя его класс. Исходный объект помещается внутрь другого — декоратора, — который перехватывает вызовы и дополняет их нужной функциональностью.
Принцип тот же, что у матрёшки: каждый новый слой добавляет что-то своё.
▪️ Когда использовать
— Нужно добавить поведение объекту без наследования
— Комбинации поведений непредсказуемы (логирование + кэш + метрики в любом порядке)
— Классический пример в JDK: InputStream → BufferedInputStream → GZIPInputStream
▪️ Decorator vs наследование
— Наследование: статическое, одно на класс
— Decorator: динамическое, можно комбинировать
▪️ Минус
Много мелких классов; отладка стека из нескольких обёрток может быть неудобной.
❓ Расскажите о паттерне Composite
Composite — это структурный паттерн, который позволяет сгруппировать объекты в древовидную структуру и работать с ней так же, как с единичным объектом.
Простыми словами: файл и папка с файлами обрабатываются одинаково — у обоих можно спросить размер, удалить, переместить.
▪️ Когда использовать
— Данные образуют древовидную структуру (файловая система, меню, оргструктура)
— Клиентский код должен одинаково работать с простыми и составными объектами
▪️ Минус
Трудно ограничить типы компонентов внутри композита — приходится проверять в runtime.
🧑💻 Приглашаем на открытый урок курса «PHP-разработчик. Продвинутый уровень» 19 мая в 20:00 — Работа с очередями в Laravel: от настройки до решения типичных проблем
Разберём, как очереди помогают ускорить приложение и вынести тяжёлые задачи в фон без потери стабильности. Обсудим настройку и обработку задач, типичные ошибки, повторы при сбоях, защиту от дублирования и мониторинг выполнения. Вы поймёте, как работают фоновые процессы в Laravel, научитесь правильно настраивать очереди и делать приложение быстрее и надёжнее.
🚀Открытый урок проходит в преддверии старта курса «PHP-разработчик. Продвинутый уровень» 19 мая в 20:00 МСК.
Регистрация: https://clc.to/6HSSeg
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
❓ Расскажите о паттерне Bridge
Bridge — это структурный паттерн, который разделяет абстракцию и реализацию так, чтобы они могли изменяться независимо друг от друга.
Простыми словами: вместо одной толстой иерархии наследования вы разбиваете её на две независимые — «что делать» и «как делать» — и связываете их композицией.
▪️ Когда использовать
— Есть две ортогональные оси изменения (тип × реализация)
— Хотите избежать «взрыва» подклассов
— Нужно переключать реализацию в runtime
▪️ Bridge vs Strategy
— Strategy: меняет один алгоритм внутри объекта
— Bridge: разделяет целые иерархии абстракции и реализации
▪️ Минус
Усложняет код, если оси изменения всего одна — тогда достаточно обычного полиморфизма.
❓ Расскажите о паттерне Builder
Builder — это порождающий паттерн, который позволяет создавать сложные объекты пошагово, разделяя процесс конструирования и представление.
Простыми словами: вместо конструктора с десятком параметров, вы собираете объект по частям, вызывая понятные методы.
▪️ Когда использовать
— Объект имеет множество параметров (особенно необязательных)
— Нужна immutable-объект с удобным созданием
— Хотите избежать telescoping constructor (конструктор в конструкторе)
▪️ Builder vs конструктор
— Конструктор: подходит, если параметров 2–3 и все обязательные
— Builder: если параметров больше 4 или есть необязательные
▪️ Минус
Дублирование полей между классом и билдером. Lombok @Builder решает это автоматически.
❓ Расскажите о паттерне Flyweight
Flyweight — это структурный паттерн, который позволяет вместить большее количество объектов в отведённую оперативную память за счёт разделения общего состояния между объектами.
Простыми словами: вместо создания тысяч похожих объектов, вы создаёте несколько объектов-легковесов с общими данными и передаёте уникальные данные извне при использовании.
▪️ Когда использовать
— Приложение создаёт огромное количество однотипных объектов
— Объекты потребляют много памяти
— Большую часть состояния можно вынести за пределы объекта
— Приложение не зависит от идентичности объектов (можно переиспользовать)
▪️ Минусы
— Усложнение кода из-за разделения состояния
— Flyweight должен быть immutable для безопасного переиспользования
🗓 14 мая в 19:00 (Мск) встречаемся в онлайне.
Тема: Почему AI-продукты на базе LLM ломаются и как сделать, чтобы работало.
В кружке выше Эмиль Сатаев рассказал, какие именно проблемы с LLM в проде будем разбирать.
Что в программе:
- Разберем реальные кейсы стартапов и ограничения LLM. - Обсудим рабочие архитектуры: RAG, human-in-the-loop, контроль качества. - Ответим на ваши вопросы и разберем кейсы участников.🎁 Бонусы: в конце вебинара подарим промокод на скидку 10.000 ₽ на курсы и разыграем подписки на полезные AI-сервисы. 👉 Зарегистрироваться на вебинар
❓ Расскажите о паттерне Prototype
Prototype — это порождающий паттерн, который позволяет копировать объекты, не вдаваясь в подробности их реализации.
Простыми словами: вместо создания объекта с нуля через конструктор, вы клонируете уже существующий экземпляр.
▪️ Когда использовать
— Создание объекта затратно (сложная инициализация, обращение к БД, парсинг файлов)
— Нужно избежать привязки к конкретным классам при создании копий
— Объекты различаются только состоянием, а не поведением
▪️ Важно: Shallow vs Deep Copy
Shallow copy — копируются только примитивы, ссылки на объекты остаются теми же
Deep copy — создаются копии вложенных объектов
▪️ Альтернативы clone
— Магический __clone()
— Статический метод: Report::copy($original)
— Сериализация: unserialize(serialize($obj))
▪️ Минус
Клонирование объектов с циклическими ссылками может быть сложным.
❓ Расскажите о паттерне Prototype
Prototype — это порождающий паттерн, который позволяет копировать объекты, не вдаваясь в подробности их реализации.
Простыми словами: вместо создания объекта с нуля через конструктор, вы клонируете уже существующий экземпляр.
▪️ Когда использовать
— Создание объекта затратно (сложная инициализация, обращение к БД, парсинг файлов)
— Нужно избежать привязки к конкретным классам при создании копий
— Объекты различаются только состоянием, а не поведением
▪️ Важно: Shallow vs Deep Copy
Shallow copy — копируются только примитивы, ссылки на объекты остаются теми же
Deep copy — создаются копии вложенных объектов
▪️ Альтернативы clone()
— Copy constructor: new Report(original)
— Статический метод: Report.copy(original)
— Сериализация: для очень сложных объектов
▪️ Минус
Клонирование объектов с циклическими ссылками может быть сложным.
✔️ PHP-тест: Идемпотентность, которой нет
Пользователь нажал «Оплатить» один раз. Деньги списались дважды.
📦 Задание
Фича: интеграция с платёжным шлюзом. Платёж создаётся на бэке, пользователь редиректится на страницу шлюза, после оплаты шлюз дёргает webhook. По webhook'у выдаётся доступ к продукту.
Жалобы начались через две недели после релиза. Только у части пользователей, только в часы пик. У некоторых списывалось дважды, у других доступ не выдавался вовсе.
// src/Payment/WebhookHandler.php
class WebhookHandler
{
public function __construct(
private PaymentRepository $payments,
private OrderRepository $orders,
private AccessService $access,
private Mailer $mailer,
) {}
public function handle(array $payload): void
{
$externalId = $payload['payment_id'];
$status = $payload['status'];
if ($status !== 'success') {
return;
}
$payment = $this->payments->findByExternalId($externalId);
if ($payment === null) {
return;
}
$order = $this->orders->findById($payment->orderId);
if ($order->status === 'paid') {
return;
}
$this->orders->markAsPaid($payment->orderId);
$this->access->grantForOrder($order);
$this->mailer->sendReceipt($order);
}
}
// src/Repository/OrderRepository.php
class OrderRepository
{
public function __construct(private PDO $pdo) {}
public function findById(int $id): Order
{
$stmt = $this->pdo->prepare(
'SELECT * FROM orders WHERE id = ?'
);
$stmt->execute([$id]);
return Order::fromRow($stmt->fetch(PDO::FETCH_ASSOC));
}
public function markAsPaid(int $orderId): void
{
$stmt = $this->pdo->prepare(
'UPDATE orders SET status = ? WHERE id = ?'
);
$stmt->execute(['paid', $orderId]);
}
}
🔹 Задачи
— Объяснить, как именно возникает двойное списание при одном нажатии пользователя
— Исправить WebhookHandler::handle
Ставьте → 🔥 если нравится формат. Если нет → 🌚
💬 Решения пишите в комменты под спойлер — сравним подходы.🦾 Почему ваши AI-продукты на базе LLM ломаются (и как это чинить)?
Выкатили ИИ-фичу в прод, а она галлюцинирует, падает или выдает мусор? Приглашаем на открытый вебинар, где разберем реальную боль внедрения LLM-агентов и научимся делать так, чтобы «всё работало».
🗓 Когда: 14 мая в 19:00 МСК
⏱️ Формат: 60 минут мяса + 30 минут ответов на ваши вопросы
🧑🏻💻 Кто вещает: Эмиль Сатаев — Backend Platform Developer (8+ лет в разработке). Человек, который своими руками внедряет LLM и агентные системы в реальные коммерческие сервисы.
🎁 Главный бонус для онлайна:
Только участникам прямого эфира подарим уникальный промокод на скидку 10.000 ₽ на большой курс AgentOps.
👉 Занять место на вебинаре
❓ Расскажите о паттерне Factory Method
Factory Method — порождающий паттерн, который определяет общий интерфейс для создания объектов в суперклассе, позволяя подклассам изменять тип создаваемых объектов.
🔹 Когда использовать:
Когда заранее неизвестны типы и зависимости объектов, или когда нужно делегировать создание объектов подклассам.
🔹 Как работает:
Создается абстрактный метод для создания объектов, а конкретные подклассы переопределяют его, возвращая нужные типы.
🔹 Плюсы:
— избавляет от привязки к конкретным классам;
— упрощает добавление новых типов продуктов;
— следует Open/Closed Principle.
❓ Расскажите о паттерне Adapter
Adapter (Адаптер) — это структурный паттерн проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе. Он выступает в роли "переходника" между двумя интерфейсами.
🔹 Когда использовать:
Когда нужно использовать существующий класс, но его интерфейс не соответствует требуемому. Например, при интеграции сторонних библиотек или работе с легаси-кодом.
