Библиотека собеса по PHP | вопросы с собеседований
Kanalga Telegram’da o‘tish
Вопросы с собеседований по PHP и ответы на них. По рекламе: @tproger_sales_bot Учиться у нас: clc.to/5JWKxA Для обратной связи: @proglibrary_feeedback_bot
Ko'proq ko'rsatish3 072
Obunachilar
-324 soatlar
-127 kun
-2330 kun
Postlar arxiv
✔️ PHP-тест: список, который иногда становится объектом
Мобилка падает на парсинге ответа. Только у части пользователей. У остальных — всё ок.
📦 Задание
Эндпоинт /api/products/available отдаёт товары в наличии. Бэкенд не трогали месяц. Внезапно прилетает баг от мобильной команды: «приложение крашится при разборе items, иногда это массив, иногда какой-то объект с числовыми ключами». На стейджинге не воспроизводится — там тестовые данные, все товары в наличии.
// src/Api/ProductController.php
class ProductController
{
public function __construct(
private readonly ProductRepository $repository,
) {}
public function available(): JsonResponse
{
$products = $this->repository->findAll();
$available = array_filter(
$products,
fn (Product $p): bool => $p->getStock() > 0,
);
return new JsonResponse([
'items' => $available,
'count' => count($available),
]);
}
}
🔹 Задачи
— Объяснить, почему форма ответа items зависит от данных: то массив, то объект
— Объяснить, при каком наборе товаров json_encode отдаёт {"0":…, "2":…} вместо […]
— Исправить так, чтобы items гарантированно сериализовался как JSON-массив
Ставьте → 🔥 если нравится формат. Если нет → 🌚
💬 Решения пишите в комменты под спойлер — сравним подходы.⚡️ Продолжаем знакомить вас с экспертами курса AgentOps!
— Сергей Нотевский расскажет, как выстроить FinOps для AI-продуктов: оптимизировать затраты на разработку и продакшен, внедрить model routing, semantic cache и систему алертов для контроля расходов
— Эмиль Сатаев разберет Context Engineering: управление контекстом, защиту от prompt injection, работу с длинными контекстами и построение безопасного пайплайна входа для AI-систем
— Михаил Бондаревский покажет, как подготовить инфраструктуру для AI-агентов: Docker, sandboxing, streaming, docker-compose и воспроизводимое окружение для разработки и продакшена
— Мурат Хажгериев расскажет про Enterprise Integrations & MCP: когда MCP действительно нужен, как подключать внешние сервисы и реализовывать интеграции с OAuth2 delegation
— Герман Сабиров разберет Governance & Compliance для AI-систем: data flow, audit logs, требования 152-ФЗ, локализацию данных и построение compliance-подхода на уровне архитектуры
Курс для backend-разработчиков, тимлидов и LLM инженеров о том, как внедрять AI-логику в бэкенд IT-продуктов и сохранять стабильность сервиса.
👉 Изучить обновленную программу AgentOps и занять место.
❓ Может ли абстрактный класс содержать частный(private) метод?
Да, в абстрактном классе можно иметь частный метод.
Этот частный метод будет отображаться ТОЛЬКО этому абстрактному классу. Это означает, что он должен будет использоваться каким-либо другим конкретным методом в абстрактном классе.
Дочерние классы не смогут вызвать его напрямую.
💬 Обратная связь
Текущий уровень сложности вопросов?
🔥 — Слишком просто, хочу сложнее
👍🏼 — В самый раз
❤️ — Иногда сложновато
😁 — Часто не понимаю
❓ Расскажите о паттерне Interpreter
Interpreter — это поведенческий паттерн, который определяет грамматику простого языка и интерпретатор для его предложений.
Простыми словами: вы описываете правила «мини-языка» в виде классов, и каждый класс умеет вычислить свою часть выражения. Как калькулятор, который разбирает «3 + 5 * 2».
▪️ Когда использовать
— Есть простой язык или набор правил, которые нужно интерпретировать
— Грамматика стабильна, но выражений — много
▪️ Минус
Для сложных грамматик дерево классов разрастается и становится неуправляемым — лучше использовать парсер-генераторы (ANTLR).
До 31 мая можно забрать любой курс Proglib Academy со скидкой 40%
Если давно хотели прокачаться в Python, ML, алгоритмах или AI-агентах, сейчас самое время выбрать программу и начать обучение по сниженной цене.
🎁 Разработка AI-агентов — от 49.000 ₽ (вместо 69.000 ₽)
Практический курс по разработке AI-агентов для автоматизации задач, работы и собственных проектов
🎁 Курс AgentOps — 129.000 ₽ (вместо 149.000 ₽)
Для разработчиков и LLM-инженеров, которые хотят внедрять AI-логику в бэкенд и сохранять стабильность сервиса.
🎁 Математика для разработки AI-моделей — 23.990 ₽ (вместо 31.990 ₽)
Практическая база по математике для анализа данных, ML и дальнейшего развития в AI.
🎁 Математика для Data Science — от 29.990 ₽ (вместо 39.990 ₽)
Курс для тех, кто хочет решать задачи, которые дают на собеседованиях на позицию дата-сайентиста в бигтехе.
🎁 ML для старта в Data Science — 28.990 ₽ (вместо 38.990 ₽)
Разберётесь в машинном обучении: от базовых понятий и линейных моделей до ансамблей, бустинга и рекомендательных систем.
🎁 Основы IT для непрограммистов — 16.990 ₽ (вместо 28.990 ₽)
Курс для IT-рекрутеров, маркетологов, проджектов, продактов и всех, кто работает с IT, но не пишет код.
🎁 Архитектуры и шаблоны проектирования — 27.990 ₽ (вместо 37.900 ₽)
Освоите основные паттерны проектирования и прокачаете навыки архитектора программного обеспечения.
🎁 Специалист по ИИ — 89.000 ₽ (вместо 113.900 ₽)
Курс для тех, кто хочет получить профессию в сфере ИИ, собрать портфолио из 5 проектов и научиться разрабатывать сложных AI-агентов.
🎁 Алгоритмы и структуры данных — 33.990 ₽ (вместо 57.990 ₽)
Подготовитесь к алгоритмическим собеседованиям, разберёте структуры данных и научитесь писать более эффективный код.
🎁 Программирование на языке Python — 27.990 ₽ (вместо 47.390 ₽)
Освоите Python на практике: без сухой теории, с пошаговой прокачкой навыков и итоговым проектом в портфолио.
🙌 Выбирайте курс по ссылке, оставляйте заявку, и менеджер поможет подобрать программу под ваши цели — https://clc.to/SALE40
❓ Расскажите о паттерне Visitor
Visitor — это поведенческий паттерн, который позволяет добавлять новые операции к объектам, не изменяя их классы.
Простыми словами: налоговый инспектор (visitor) приходит в разные компании и выполняет проверку — компании не меняются, а новые виды проверок добавляются легко.
▪️ Когда использовать
— Нужно выполнить операцию над группой разнородных объектов
— Новые операции добавляются часто, а новые типы элементов — редко
▪️ Минус
Visitor нужно обновлять при добавлении нового типа элемента — нарушается Open/Closed Principle для элементов.
❓ Расскажите о паттерне Memento
Memento — это поведенческий паттерн, который позволяет сохранять и восстанавливать прежнее состояние объекта, не нарушая инкапсуляцию.
Простыми словами: Ctrl+Z в любом редакторе — где-то хранится снимок предыдущего состояния, к которому можно откатиться.
▪️ Когда использовать
— Нужна функция отмены/отката (undo)
— Нужно сохранять контрольные точки состояния
— Прямой доступ к полям объекта нарушил бы инкапсуляцию
▪️ Memento vs Command
— Command: хранит действие и умеет его отменить
— Memento: хранит полный снимок состояния
▪️ Минус
Может потреблять много памяти, если состояние объекта большое и снимки создаются часто.
❓ Расскажите о паттерне Mediator
Mediator — это поведенческий паттерн, который убирает прямые связи между компонентами, заставляя их общаться через посредника.
Простыми словами: диспетчер в аэропорту — самолёты не переговариваются друг с другом, а общаются через башню управления.
▪️ Когда использовать
— Компоненты слишком сильно связаны друг с другом
— Хотите переиспользовать компоненты в других контекстах
▪️ Минус
Медиатор может стать God Object, сконцентрировав слишком много логики.
❓ Расскажите о паттерне Iterator
Iterator — это поведенческий паттерн, который даёт возможность последовательно обходить элементы коллекции, не раскрывая её внутреннее устройство.
Простыми словами: вы проходите по элементам через «окошко» (hasNext / next), не зная — это массив, дерево или база данных за ним.
▪️ Когда использовать
— Нужен единый способ обхода для разных структур данных
— Хотите скрыть сложность обхода (дерево, граф, пагинация)
❓ Расскажите о паттерне Chain of Responsibility
Chain of Responsibility — это поведенческий паттерн, который позволяет передавать запрос по цепочке обработчиков. Каждый обработчик решает: обработать запрос или передать дальше.
Простыми словами: как эскалация тикета: L1 → L2 → L3 саппорт. Каждый уровень либо решает проблему, либо передаёт выше.
▪️ Когда использовать
— Набор обработчиков и их порядок определяется динамически
— Запрос должен быть обработан одним из нескольких объектов, но каким — неизвестно заранее
▪️ Минус
Запрос может пройти всю цепочку и не быть обработанным; сложно отладить длинную цепочку.
💬 Обратная связь
Последние посты все по единой теме паттернов.
Удобно ли, если посты будут иногда такими едиными блоками?
🔥 — Удобно
👍🏼 — Без разницы
😁 — Скука смертная, хочется разнообразия
❓ Расскажите о паттерне State
State — это поведенческий паттерн, который позволяет объекту менять своё поведение при изменении внутреннего состояния. Извне кажется, что объект сменил свой класс.
Простыми словами: банкомат ведёт себя по-разному в зависимости от состояния: ожидание карты → ввод PIN → выбор операции. Одна и та же кнопка делает разные вещи.
▪️ Когда использовать
— Объект ведёт себя по-разному в зависимости от состояния
— Много if/switch по статусу — признак, что нужен State
— Количество состояний может расти
▪️ State vs Strategy
— Strategy: клиент выбирает алгоритм
— State: объект сам переключает поведение при смене состояния
▪️ Минус
Избыточен, если состояний всего 2–3 и переходы простые.
❓ Расскажите о паттерне 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: динамическое, можно комбинировать
▪️ Минус
Много мелких классов; отладка стека из нескольких обёрток может быть неудобной.
