Библиотека собеса по PHP | вопросы с собеседований
3.15K subscribers
195 photos
6 videos
137 links
Вопросы с собеседований по PHP и ответы на них.

По рекламе: @proglib_adv

Учиться у нас: https://proglib.io/w/9f3affba

Для обратной связи: @proglibrary_feeedback_bot
Download Telegram
Symfony основан на конфигурации или соглашениях?

Symfony — это фреймворк, основанный на соглашениях. Документ Coding Standards иллюстрирует нормы кодирования для проектов Symfony, а также внутренних и сторонних пакетов. Он определяет стандарты кодирования и соглашения, используемые в ядре фреймворка, чтобы сделать его более единообразным и предсказуемым.
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍1🔥1
Осталось всего 4 места на курс по ИИ-агентам. 30 апреля закрываем набор окончательно.

В ГС честно рассказали:
— Кому курс не подойдет;
— Какой хардкор в программе (LangGraph, AutoGen, CrewAI);
— Как мы даем токены, чтобы вы не тратили свои деньги.

🏃‍♀️ Записаться, пока есть места
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱2
Что такое Bundles в Symfony?

В Symfony, Bundle — это структурированный набор файлов и директорий, реализующих определённую функцию или набор связанных функций. Бандлы являются основными единицами организации, позволяя упаковывать повторно используемый код и делиться им между несколькими проектами.

Ключевые аспекты

1️⃣ Структура и организация

Бандлы предоставляют способ организовать код в логические единицы. Каждый бандл обычно включает:

— Контроллеры
— Модели/Сущности
— Сервисы
— Конфигурационные файлы
— Шаблоны (файлы Twig)
— Информацию о маршрутах


2️⃣ Повторное использование

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

3️⃣ Разделение

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

4️⃣ Сторонние бандлы

У Symfony есть богатая экосистема сторонних бандлов, доступных через такие платформы, как Packagist. Эти бандлы можно легко интегрировать в проект для добавления различных функций, таких как аутентификация пользователей, обработка платежей или интеграция с API.
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍2🔥1
✔️ PHP-тест: Утечка сессии между пользователями

Баг не воспроизводится локально. На проде один юзер видит чужие данные 👇

📦 Задание

Стартап запустил SaaS. Первые две недели тишина. А потом в поддержку прилетело: «Я зашёл в кабинет и увидел чужой аккаунт». В коде сессий и авторизации не трогали давно, там всё стабильно.

// src/Auth/UserSession.php
class UserSession
{
private static ?User $currentUser = null;

public static function set(User $user): void
{
self::$currentUser = $user;
}

public static function get(): ?User
{
return self::$currentUser;
}

public static function clear(): void
{
self::$currentUser = null;
}
}

// src/Middleware/AuthMiddleware.php
class AuthMiddleware
{
public function __construct(
private readonly UserRepository $userRepository,
private readonly JWTService $jwt,
) {}

public function handle(Request $request, callable $next): Response
{
$token = $request->headers->get('Authorization');

if (!$token) {
return new Response(status: 401);
}

$payload = $this->jwt->decode(str_replace('Bearer ', '', $token));
$user = $this->userRepository->find($payload['sub']);

UserSession::set($user);

return $next($request);
}
}

// src/Controller/DashboardController.php
class DashboardController
{
public function index(): Response
{
$user = UserSession::get();

return new Response(
body: $this->renderDashboard($user),
);
}
}

// src/Console/CacheWarmupCommand.php
class CacheWarmupCommand
{
public function execute(): void
{
$users = $this->userRepository->findAll();

foreach ($users as $user) {
UserSession::set($user);
$this->warmupForUser($user);
}

// прогрев завершён
}
}


🔹 Задачи

— Объяснить механизм утечки
— Объяснить, как CacheWarmupCommand триггерит баг и при каком race window
— Переписать UserSession так, чтобы устранить проблему архитектурно, а не патчем

Ставьте → 🔥 если нравится формат. Если нет → 🌚

💬 Решения пишите в комменты под спойлер — сравним подходы.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14🤔5🌚2👍1🥱1
Расскажите о паттерне Strategy?

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

Когда использовать:

есть несколько способов выполнения одной операции;
нужно избежать множественных if-else или switch;
алгоритмы должны выбираться в runtime.

Преимущества: соблюдение Open/Closed Principle, устранение условных операторов, гибкость выбора алгоритма
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥5
Расскажите о паттерне Singleton?

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

🔹 Основные характеристики:

приватный конструктор запрещает создание экземпляров извне;
статический метод getInstance() возвращает единственный экземпляр;
статическое поле для хранения экземпляра.

🔹 Когда использовать:

объект содержит глобальное состояние;
создание объекта ресурсозатратно;
нужен единый координатор действий;
логически должен быть один экземпляр.
Please open Telegram to view this post
VIEW IN TELEGRAM
1🔥3👍21
Расскажите о паттерне Adapter

Adapter (Адаптер) — это структурный паттерн проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе. Он выступает в роли "переходника" между двумя интерфейсами.

🔹 Когда использовать:

Когда нужно использовать существующий класс, но его интерфейс не соответствует требуемому. Например, при интеграции сторонних библиотек или работе с легаси-кодом.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥21
Расскажите о паттерне Factory Method

Factory Method — порождающий паттерн, который определяет общий интерфейс для создания объектов в суперклассе, позволяя подклассам изменять тип создаваемых объектов.

🔹 Когда использовать:

Когда заранее неизвестны типы и зависимости объектов, или когда нужно делегировать создание объектов подклассам.

🔹 Как работает:

Создается абстрактный метод для создания объектов, а конкретные подклассы переопределяют его, возвращая нужные типы.

🔹 Плюсы:

избавляет от привязки к конкретным классам;
упрощает добавление новых типов продуктов;
следует Open/Closed Principle.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥21👍1👏1
🦾 Почему ваши AI-продукты на базе LLM ломаются (и как это чинить)?

Выкатили ИИ-фичу в прод, а она галлюцинирует, падает или выдает мусор? Приглашаем на открытый вебинар, где разберем реальную боль внедрения LLM-агентов и научимся делать так, чтобы «всё работало».

🗓 Когда: 14 мая в 19:00 МСК
⏱️ Формат: 60 минут мяса + 30 минут ответов на ваши вопросы

🧑🏻‍💻 Кто вещает: Эмиль Сатаев — Backend Platform Developer (8+ лет в разработке). Человек, который своими руками внедряет LLM и агентные системы в реальные коммерческие сервисы.

🎁 Главный бонус для онлайна:
Только участникам прямого эфира подарим уникальный промокод на скидку 10.000 ₽ на большой курс AgentOps.

👉 Занять место на вебинаре
🌚3
✔️ 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

Ставьте → 🔥 если нравится формат. Если нет → 🌚

💬 Решения пишите в комменты под спойлер — сравним подходы.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥103👍2🌚1
Расскажите о паттерне Prototype

Prototype — это порождающий паттерн, который позволяет копировать объекты, не вдаваясь в подробности их реализации.

Простыми словами: вместо создания объекта с нуля через конструктор, вы клонируете уже существующий экземпляр.

▪️ Когда использовать

Создание объекта затратно (сложная инициализация, обращение к БД, парсинг файлов)
Нужно избежать привязки к конкретным классам при создании копий
Объекты различаются только состоянием, а не поведением

▪️ Важно: Shallow vs Deep Copy

Shallow copy — копируются только примитивы, ссылки на объекты остаются теми же
Deep copy — создаются копии вложенных объектов

▪️ Альтернативы clone

— Магический __clone()
— Статический метод: Report::copy($original)
— Сериализация: unserialize(serialize($obj))

▪️ Минус

Клонирование объектов с циклическими ссылками может быть сложным.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥21👍1
🗓 14 мая в 19:00 (Мск) встречаемся в онлайне.

Тема: Почему AI-продукты на базе LLM ломаются и как сделать, чтобы работало.

В кружке выше Эмиль Сатаев рассказал, какие именно проблемы с LLM в проде будем разбирать.

Что в программе:
- Разберем реальные кейсы стартапов и ограничения LLM.
- Обсудим рабочие архитектуры: RAG, human-in-the-loop, контроль качества.
- Ответим на ваши вопросы и разберем кейсы участников.


🎁 Бонусы: в конце вебинара подарим промокод на скидку 10.000 ₽ на курсы и разыграем подписки на полезные AI-сервисы.

👉 Зарегистрироваться на вебинар
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱2
Расскажите о паттерне Flyweight

Flyweight — это структурный паттерн, который позволяет вместить большее количество объектов в отведённую оперативную память за счёт разделения общего состояния между объектами.

Простыми словами: вместо создания тысяч похожих объектов, вы создаёте несколько объектов-легковесов с общими данными и передаёте уникальные данные извне при использовании.

▪️ Когда использовать

Приложение создаёт огромное количество однотипных объектов
Объекты потребляют много памяти
Большую часть состояния можно вынести за пределы объекта
Приложение не зависит от идентичности объектов (можно переиспользовать)

▪️ Минусы

Усложнение кода из-за разделения состояния
Flyweight должен быть immutable для безопасного переиспользования
Please open Telegram to view this post
VIEW IN TELEGRAM
👍41🔥1
Расскажите о паттерне Builder

Builder — это порождающий паттерн, который позволяет создавать сложные объекты пошагово, разделяя процесс конструирования и представление.

Простыми словами: вместо конструктора с десятком параметров, вы собираете объект по частям, вызывая понятные методы.

▪️ Когда использовать

Объект имеет множество параметров (особенно необязательных)
Нужна immutable-объект с удобным созданием
Хотите избежать telescoping constructor (конструктор в конструкторе)

▪️ Builder vs конструктор

— Конструктор: подходит, если параметров 2–3 и все обязательные
— Builder: если параметров больше 4 или есть необязательные

▪️ Минус

Дублирование полей между классом и билдером. Lombok @Builder решает это автоматически.
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍1🔥1
Расскажите о паттерне Bridge

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

Простыми словами: вместо одной толстой иерархии наследования вы разбиваете её на две независимые — «что делать» и «как делать» — и связываете их композицией.

▪️ Когда использовать

Есть две ортогональные оси изменения (тип × реализация)
Хотите избежать «взрыва» подклассов
Нужно переключать реализацию в runtime

▪️ Bridge vs Strategy

— Strategy: меняет один алгоритм внутри объекта
— Bridge: разделяет целые иерархии абстракции и реализации

▪️ Минус

Усложняет код, если оси изменения всего одна — тогда достаточно обычного полиморфизма.
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍1🔥1
Расскажите о паттерне Composite

Composite — это структурный паттерн, который позволяет сгруппировать объекты в древовидную структуру и работать с ней так же, как с единичным объектом.

Простыми словами: файл и папка с файлами обрабатываются одинаково — у обоих можно спросить размер, удалить, переместить.

▪️ Когда использовать

Данные образуют древовидную структуру (файловая система, меню, оргструктура)
Клиентский код должен одинаково работать с простыми и составными объектами

▪️ Минус

Трудно ограничить типы компонентов внутри композита — приходится проверять в runtime.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍43🔥1
Расскажите о паттерне Decorator

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

Принцип тот же, что у матрёшки: каждый новый слой добавляет что-то своё.

▪️ Когда использовать

Нужно добавить поведение объекту без наследования
Комбинации поведений непредсказуемы (логирование + кэш + метрики в любом порядке)
Классический пример в JDK: InputStream → BufferedInputStream → GZIPInputStream

▪️ Decorator vs наследование

— Наследование: статическое, одно на класс
— Decorator: динамическое, можно комбинировать

▪️ Минус

Много мелких классов; отладка стека из нескольких обёрток может быть неудобной.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍31🔥1
✔️ 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()]);
}
}
}


🔹 Задачи

— Объяснить, каким образом цены в оригинальном заказе оказались изменены в базе
— Исправить код так, чтобы оригинал гарантированно не мутировал

Ставьте → 🔥 если нравится формат. Если нет → 🌚

💬 Решения пишите в комменты под спойлер — сравним подходы.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍21🌚1
💬 Обратная связь

Как часто вы проходите собеседования?

🔥 — Сейчас активно ищу работу
👍🏼 — Раз в несколько месяцев
❤️ — Раз в полгода-год
😁 — Не прохожу, уже работаю/ещё учусь
Please open Telegram to view this post
VIEW IN TELEGRAM
😁25🔥153👍3😢1
Расскажите о паттерне Facade

Facade — это структурный паттерн, который предоставляет простой интерфейс к сложной подсистеме, скрывая внутреннюю сложность.

Простыми словами: один метод вместо десяти вызовов в разные сервисы. Пульт от телевизора — это фасад к электронике внутри.

▪️ Когда использовать

Подсистема сложная, а клиенту нужен простой вход
Хотите уменьшить связанность между слоями
Типичный пример: сервисный слой в Spring-приложении

▪️ Минус

Фасад может стать God Object, если взять на себя слишком много логики.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3👍2👏1