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

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

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

Для обратной связи: @proglibrary_feeedback_bot
Download Telegram
✔️ PHP-тест: Static state + OPcache + Preloading

Классический баг, который ломает прод и не воспроизводится локально 👇

📦 Задание

Команда добавила preloading в production для ускорения.

Через день в логах появились странные ошибки: у разных пользователей в одном запросе перемешиваются данные — кто-то видит чужие настройки, кто-то получает null там, где не должен.

На dev-машинах не воспроизводится. Найдите проблему в коде:
// preload.php 
require_once 'src/Config.php';
require_once 'src/UserContext.php';
require_once 'src/RequestPipeline.php';

// config/app.php
class Config
{
private static array $data = [];
private static bool $loaded = false;

public static function load(string $env): void
{
if (self::$loaded) {
return;
}
self::$data = parse_ini_file("config/{$env}.ini");
self::$loaded = true;
}

public static function get(string $key): mixed
{
return self::$data[$key] ?? null;
}
}

// src/UserContext.php
class UserContext
{
private static ?array $current = null;

public static function set(array $user): void
{
self::$current = $user;
}

public static function get(): ?array
{
return self::$current;
}

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

// src/RequestPipeline.php
class RequestPipeline
{
private static array $middlewareResults = [];

public static function addResult(string $key, mixed $value): void
{
self::$middlewareResults[$key] = $value;
}

public static function getResult(string $key): mixed
{
return self::$middlewareResults[$key] ?? null;
}

public static function reset(): void
{
self::$middlewareResults = [];
}
}

// public/index.php
Config::load($_ENV['APP_ENV'] ?? 'production');

$user = Auth::check($_SERVER['HTTP_AUTHORIZATION'] ?? '');
UserContext::set($user);

RequestPipeline::addResult('ip', $_SERVER['REMOTE_ADDR']);
RequestPipeline::addResult('ua', $_SERVER['HTTP_USER_AGENT'] ?? '');

$app->handle(ServerRequest::fromGlobals());


🔹 Задачи

— Объяснить, почему при preloading статические свойства классов ведут себя иначе, чем без него, и как это связано с жизненным циклом worker-процесса.
— Объяснить, чем классы отличаются по характеру утечки
— Предложить архитектурное решение: как правильно управлять request-scoped состоянием в long-running процессах

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

💬 Решения пишите в комменты под спойлер — сравним подходы.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥20👍2🤔2🥱1
Самый востребованный навык в ИТ в 2026-м — навык создания ИИ-агентов

Мы полностью переработали курс «Разработка AI-агентов» под реалии 2026 года. Никакой долгой теории — с самого начала пишем код. Обучать и делиться набитыми шишками будут эксперты-практики из Газпромбанка, Альфа-Банка и других бигтехов.

В программе:

— архитектура автономных систем с тестированием, ReAct-циклами и контролем токенов;
— практическая работа с актуальными фреймворками LangGraph, AutoGen, MCP и CrewAI;
— настройка продвинутого RAG для парсинга документов и точного поиска;
— внедрение решений с учётом действующего законодательства (152-ФЗ);
— дипломная работа, за основу которой можно взять свой рабочий проект или задачу, которую предложим мы.

Эксперты поделятся инсайтами из реального продакшна — тем, о чём вам никогда не расскажет ни одна нейросеть.

Запись первого открытого вебинара, на котором мы вместе с руководителем AI-направления в Альфа-Банке Полиной Полуниной пилили агента в прямом эфире.


Ах да, чуть не забыли! Дарим промокод AGENTSWEB на скидку 10 000 рублей и два курса сверху при покупке до 15 марта 🎁

Стать AI-инженером
🥱21
Что такое Composer?

Composer — это инструмент для управления зависимостями в PHP-приложениях. Он позволяет легко устанавливать, обновлять и управлять библиотеками и фреймворками, которые используются в проекте.

🔹 Основные функции Composer

▪️ Установка зависимостей: Composer позволяет определить необходимые библиотеки и их версии в файле composer.json, а затем установить их с помощью команды composer install.

▪️ Обновление зависимостей: после установки библиотек, можно обновить их до последних версий, указанных в файле composer.json, с помощью команды composer update.

▪️ Автозагрузка классов: Composer автоматически создает файл автозагрузки, который позволяет использовать классы из установленных зависимостей без необходимости явного подключения файлов.

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

▪️ Создание собственных пакетов: Composer позволяет создавать собственные пакеты для повторного использования в различных проектах и их публикации в репозитории Packagist для общего использования.
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍1🔥1
Зачем использовать yield вместо return с массивом?

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

Главный профит — память. Если обрабатываешь CSV на 500 тысяч строк, с массивом ты загрузишь всё в RAM разом. С генератором в памяти живёт одна строка. Генератор реализует интерфейс Iterator, с ним работает foreach. Минус — нельзя перемотать назад, обойти можно только один раз. Используй когда данных много, порядок линейный, и случайный доступ не нужен.
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍3🔥1
Кажется, мы окончательно перешли от игрушек к суровому AgentOps

Приглашаем на наш обновлённый курс по разработке ИИ-агентов. Никакой воды про «будущее нейросетей», только инженерный подход.

На курсе мы:

— пошагово строим готовые системы на LangGraph, CrewAI и MCP;
— настраиваем кэширование и роутинг, чтобы бот не сожрал токены;
— разбираемся со стейтом, учимся дебажить через time-travel и прикручиваем human-in-the-loop;
— выводим RAG в прод так, чтобы безопасники не завернули архитектуру из-за 152-ФЗ.

В пекло скучные лекции про общую инфраструктуру — сразу фокусируемся на агентных фреймворках и написании кода. Занятия ведут бывалые лиды из Газпромбанка и Альфы, набившие шишки на реальных задачах.

Кстати, на днях мы пилили агента в прямом эфире, если пропустили — есть запись вебинара.


Сегодня последний день, когда можно забрать курс по старым ценам. Базовый тариф сейчас стоит 49 000 ₽ (вместо 62 990 ₽), продвинутый трек — 99 000 ₽ (вместо 124 990 ₽). Если не хочется отдавать всю сумму сразу, есть рассрочка. Торопитесь — на потоке осталось всего 5 мест!

Зафиксировать цену и перейти к сборке своих агентов
Что такое PSR и зачем это нужно?

PSR (PHP Standards Recommendations) — набор стандартов от PHP-FIG, которые обеспечивают совместимость кода между проектами и пакетами.

Ключевые стандарты:

• PSR-1 / PSR-12 — стиль кода (отступы, имена классов, методов)
• PSR-4 — автозагрузка: пространство имён → путь к файлу
• PSR-3 — интерфейс логгера (LoggerInterface)
• PSR-7 — HTTP-сообщения (Request / Response)
• PSR-11 — контейнер зависимостей (ContainerInterface)
• PSR-15 — HTTP middleware

Без PSR библиотеки несовместимы, composer не может их объединить, а команды пишут в разных стилях. PSR — это общий язык PHP-экосистемы.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥1👾1
Что такое Dependency Injection?

DI — паттерн, при котором зависимости класса передаются извне, а не создаются внутри.

// Плохо — жёсткая связанность:
class OrderService {
private $mailer;
public function __construct() {
$this->mailer = new SmtpMailer(); // зависимость зашита внутри
}
}

// Хорошо — DI:
class OrderService {
public function __construct(private MailerInterface $mailer) {}
}


Зачем:


✔️ Легко подменить реализацию (например, для тестов — MockMailer)
✔️ Классы не знают о конкретных реализациях (зависимость от абстракции)
✔️ Код легче тестировать, поддерживать, расширять

DI Container (Symfony, Laravel) автоматизирует создание объектов и внедрение зависимостей по всему приложению.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍31🔥1
Что такое магические методы? Назови основные.

Магические методы — методы с двойным подчёркиванием, которые PHP вызывает автоматически в определённых ситуациях.

__construct() / __destruct() — создание и уничтожение объекта
__get($name) / __set($name, $value) — обращение к несуществующему свойству
__isset($name) / __unset($name) — isset() / unset() на несуществующем свойстве
__call($name, $args) — вызов несуществующего метода экземпляра
__callStatic($name, $args) — вызов несуществующего статического метода
__toString() — приведение объекта к строке
__invoke() — вызов объекта как функции
__clone() — после clone()
__sleep() / __wakeup() — перед serialize() / после unserialize()
__debugInfo() — что показывать в var_dump()
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
Как работают генераторы?

Генератор — функция с yield, которая возвращает значения по одному, не загружая всё в память.

function readLargeFile(string $file): \Generator {
$handle = fopen($file, 'r');
while (!feof($handle)) {
yield fgets($handle);
}
fclose($handle);
}

foreach (readLargeFile('10gb.log') as $line) {
process($line);
}


Без генератора file() загрузил бы весь файл в массив → OutOfMemoryError.

Генератор реализует интерфейс Iterator. Ключевые отличия от обычной функции:

• Выполнение приостанавливается на yield
• Возобновляется при следующем обращении к итератору
• yield from позволяет делегировать другому генератору

Применение: обработка больших файлов, пагинация из DB, потоковая генерация данных.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5😁3🔥2
✔️ PHP-тест: Typed Properties + Lazy Initialization + Объект в статике

Баг, который живёт в проде месяцами и проявляется только под нагрузкой 👇

📦 Задание

Команда пишет модуль биллинга. Ты написал быстрый класс-обёртку для работы с тарифами. Код прошёл ревью, всё работало на стейджинге. В проде через неделю начались жалобы: у части пользователей неправильно считается стоимость. Причём только в пиковые часы.

// src/Billing/TariffCalculator.php
class TariffCalculator
{
private static TariffConfig $config;
private static array $cache = [];

public static function init(array $rawConfig): void
{
self::$config = new TariffConfig($rawConfig);
}

public static function calculate(int $userId, int $units): float
{
$key = $userId . ':' . $units;

if (isset(self::$cache[$key])) {
return self::$cache[$key];
}

$price = self::$config->getBasePrice()
* $units
* self::$config->getUserMultiplier($userId);

self::$cache[$key] = $price;

return $price;
}

public static function resetCache(): void
{
self::$cache = [];
}
}

// src/Billing/TariffConfig.php
class TariffConfig
{
private float $basePrice;
private array $multipliers;

public function __construct(array $config)
{
$this->basePrice = (float) $config['base_price'];
$this->multipliers = $config['multipliers'] ?? [];
}

public function getBasePrice(): float
{
return $this->basePrice;
}

public function getUserMultiplier(int $userId): float
{
return $this->multipliers[$userId] ?? 1.0;
}
}

// bootstrap.php — вызывается один раз при старте воркера
TariffCalculator::init(loadConfigFromDB());

// Где-то в обработчике запроса
$price = TariffCalculator::calculate($user->id, $request->units);


🔹 Задачи

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

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

💬 Решения пишите в комменты под спойлер — сравним подходы.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥62👍1
Начать рассказывать интервьюеру, как вы ловко дёргаете ручки API через базовый LangChain.

Звучит как отличный план, да? Нет, это мгновенный отказ.

В свежем отчёте по рынку GPU говорится, что 54% компаний стопают ИИ-внедрения тупо из-за конских затрат на инфраструктуру. На серверах более 70% стоимости — это видеокарты. Поэтому на собесах сейчас спрашивают не про красивые промпты, а про жёсткую экономику агентов.

По сути, от вас ждут понимания, как лимитировать ресурсы на лету, роутить запросы и дебажить отказы через механизм time-travel в LangGraph. Если вы до сих пор собираете ботов в ноутбуках, гляньте обновлённый курс «Разработка ИИ-агентов» — фокус там смещён с игрушечных концепций на суровый энтерпрайз.

Что требуют от мидлов и выше:

— интеграция мультиагентных систем по стандарту MCP;
— суровый AgentOps: метрики, трейсинг, защита от деградации пайплайнов;
— локальный деплой Open Source под 152-ФЗ (без этого в финтех можно даже не стучаться).

Прямо сейчас можно урвать курс с увесистой скидкой (49 000 ₽ 62 990 ₽ за базовый тариф и 99 000 ₽ 124 990 ₽ за продвинутый трек), но стоит поторопиться — на потоке осталось всего 5 мест.

👉 Подтянуть архитектуру до уровня прода
1
Что такое сессии в PHP и какие у них уязвимости?

Сессия — механизм хранения данных между HTTP-запросами. PHP создаёт уникальный session_id, передаёт его клиенту в cookie (PHPSESSID), а данные хранит на сервере (файлы, Redis, DB).

session_start();
$_SESSION['user_id'] = $user->id;


Уязвимости:


⚠️ Session Fixation — атакующий подсовывает жертве известный session_id. Решение: session_regenerate_id(true) после логина.
⚠️ Session Hijacking — кража session_id через XSS или сниффинг. Решение: HTTPS, httponly cookie, SameSite.
⚠️ Предсказуемый session_id — в старых PHP. В современных — криптографически безопасный генератор.
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍1
Как предотвратить SQL-инъекции?

SQL-инъекция — подстановка вредоносного SQL через пользовательский ввод.

Плохо:
$query = "SELECT * FROM users WHERE name = '$name'";
// name = "' OR '1'='1" → сломает всё


Правильно — подготовленные выражения (prepared statements):

// PDO:
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute([':email' => $email]);

// MySQLi:
$stmt = $mysqli->prepare('SELECT * FROM users WHERE email = ?');
$stmt->bind_param('s', $email);
$stmt->execute();


Параметры никогда не попадают в SQL-текст — они передаются отдельно.

Дополнительно:

• Никогда не доверяй пользовательскому вводу
• Принцип минимальных привилегий для DB-пользователя
• ORM (Eloquent, Doctrine) использует prepared statements под капотом
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21
Чем отличается Optimistic Lock от Pessimistic Lock?

Pessimistic Lock — блокируем строку в БД на время транзакции. Никто другой не может её изменить до снятия блокировки.

  SELECT * FROM orders WHERE id = 1 FOR UPDATE;


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

Optimistic Lock — блокировки нет. У записи есть поле version. При обновлении проверяем, что версия не изменилась:

  UPDATE orders SET status = 'paid', version = 6
WHERE id = 1 AND version = 5;


Если affected_rows = 0 — кто-то успел раньше, делаем retry или возвращаем ошибку.

Применять когда: конфликты редки, операции долгие (нельзя держать блокировку), высокий параллелизм.
Минус: нужен retry-механизм, сложнее реализовать корректно.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5👍2
Объясни разницу между индексом B-Tree и Hash?

B-Tree (сбалансированное дерево) — стандартный тип индекса в InnoDB. Узлы дерева хранят ключи в отсортированном порядке.

Поддерживает:

— точный поиск: WHERE id = 5
— диапазоны: WHERE id BETWEEN 5 AND 10
— сортировку: ORDER BY id
— префиксный поиск: WHERE name LIKE 'Ali%'

Hash-индекс — хранит хэш значения ключа. Поиск O(1), но только точное равенство. Не поддерживает диапазоны, сортировку, LIKE.
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍1
Что такое Covering Index и как он ускоряет запрос?

Covering Index — индекс, который содержит все столбцы, необходимые для выполнения запроса. MySQL отвечает прямо из индекса, не обращаясь к основной таблице (heap/clustered index).

  CREATE INDEX idx_user_status ON orders(user_id, status, created_at);

SELECT status, created_at FROM orders WHERE user_id = 5;


Все три поля запроса есть в индексе → MySQL читает только индекс. В EXPLAIN увидишь: Using index.

Без covering index: MySQL находит строки через индекс, затем делает дополнительный lookup в основную таблицу за остальными столбцами (random I/O). При большом количестве строк это медленно.

Правило проектирования индексов: сначала столбцы из WHERE и JOIN, потом из ORDER BY, потом из SELECT. Порядок в составном индексе критичен — MySQL использует индекс слева направо и останавливается на первом неиспользованном столбце.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍32🔥2
Что такое CQRS? Почему его часто используют вместе с Event Sourcing?

CQRS (Command Query Responsibility Segregation) — разделение модели на две:

🔹 Command side (запись) — принимает команды (CreateOrder, CancelOrder), изменяет состояние, не возвращает данные (или только ID).

🔹 Query side (чтение) — принимает запросы, возвращает данные, никогда не изменяет состояние.

Зачем разделять: модели чтения и записи имеют разные требования. Write-модель — богатый домен с инвариантами и валидацией. Read-модель — денормализованные данные, оптимизированные под конкретный экран/API.

Связь с Event Sourcing: когда command side сохраняет событие, это событие обновляет read-модели (проекции). Проекции — денормализованные таблицы или документы, заточенные под конкретные запросы.

OrderCreated → обновить проекцию "список заказов"
OrderCreated → обновить проекцию "аналитика по дням"

Без Event Sourcing CQRS тоже применяется: просто два разных репозитория — один для записи (доменные объекты), другой для чтения (DTO, raw SQL).
Please open Telegram to view this post
VIEW IN TELEGRAM
👍32
✔️ PHP-тест: Exception handling + PDO транзакции + молчаливая потеря данных

Код выглядит аккуратно. Но данные теряются, и никто не знает почему 👇

📦 Задание

Есть сервис для обработки платежей. Код покрыт тестами, транзакции есть, ошибки логируются. На проде раз в несколько дней часть платежей пропадает — в БД нет записи, в логах нет ошибок, пользователь уверен что оплатил.

// src/Payment/PaymentService.php
class PaymentService
{
public function __construct(
private PDO $pdo,
private Logger $logger,
private Notifier $notifier,
) {}

public function process(PaymentDTO $dto): bool
{
try {
$this->pdo->beginTransaction();

$paymentId = $this->insertPayment($dto);
$this->updateBalance($dto->userId, $dto->amount);
$this->insertAuditLog($paymentId, $dto);

$this->pdo->commit();

$this->notifier->sendReceipt($dto->userId, $paymentId);

return true;

} catch (NotificationException $e) {
$this->logger->warning('Receipt failed', ['error' => $e->getMessage()]);
return true;

} catch (Throwable $e) {
$this->logger->error('Payment failed', ['error' => $e->getMessage()]);
$this->pdo->rollBack();
return false;
}
}

private function insertPayment(PaymentDTO $dto): int
{
$stmt = $this->pdo->prepare(
'INSERT INTO payments (user_id, amount, status) VALUES (?, ?, ?)'
);
$stmt->execute([$dto->userId, $dto->amount, 'pending']);
return (int) $this->pdo->lastInsertId();
}

private function updateBalance(int $userId, float $amount): void
{
$stmt = $this->pdo->prepare(
'UPDATE balances SET amount = amount - ? WHERE user_id = ?'
);
$stmt->execute([$amount, $userId]);

if ($stmt->rowCount() === 0) {
throw new \RuntimeException("Balance record not found for user $userId");
}
}

private function insertAuditLog(int $paymentId, PaymentDTO $dto): void
{
// Пишем в отдельную audit БД через отдельное соединение
$this->auditPdo->prepare(
'INSERT INTO audit_log (payment_id, user_id, amount) VALUES (?, ?, ?)'
);
// ... execute
}
}


🔹 Задачи

— Найти сценарий, при котором платёж коммитится в БД, но return true не доходит до контроллера — и данные считаются потерянными
— Объяснить проблему
— Предложить исправленную структуру

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

💬 Решения пишите в комменты под спойлер — сравним подходы.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15
😱 Если ваш продукт не умеет отдавать данные в формате, понятном AI-агенту, то вас просто не существует

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

Как адаптировать продукт и не исчезнуть из выдачи:

— интегрировать MCP и A2A-взаимодействие, чтобы агенты могли вас читать;
— научиться контролировать стоимость (лимиты, кэш, роутинг между моделями);
— настроить AgentOps: трейсинг, логирование и отлов регрессий.

Всё это ждёт вас на обновлённом курсе «Разработка AI-агентов». Мы специально сделали фокус на утилитарном инжиниринге и production-ready решениях.

Кстати, до 29 марта можно забрать курс с большой скидкой, и стоит поторопиться — мест на потоке всё меньше.

Зафиксировать цену и начать деплоить агентов без слива бюджета 👈
😁5
В чём основное отличие Docker от виртуальной машины?

Основное отличие в уровне виртуализации.

Виртуальная машина виртуализирует железо целиком: у неё есть собственная ОС со всеми компонентами, гипервизор, ядро. Это тяжеловесно — VM может весить гигабайты и стартовать минутами.

Docker виртуализирует только уровень приложения. Контейнеры используют ядро хостовой ОС, изолируясь через namespaces и cgroups. Они легковесны — образ может весить десятки мегабайт, запускается за секунды.

🔹 На практике это означает

— Docker быстрее и экономнее по ресурсам.
— VM даёт полную изоляцию и может запускать разные ОС на одном хосте.
— Для микросервисов обычно выбирают Docker, для полной изоляции окружений — VM.
Please open Telegram to view this post
VIEW IN TELEGRAM
🥰31👍1
Что такое DDD?

DDD (Domain-Driven Design) — подход к проектированию, при котором структура кода отражает структуру бизнес-домена.

Основные строительные блоки:

Entity — объект с уникальной идентичностью. Два объекта с одним ID — один и тот же объект, даже если остальные поля разные. Пример: User, Order.

Value Object — объект без идентичности, определяется своими атрибутами. Иммутабелен. Пример: Money(100, 'USD'), Email('alice@example.com'). Два Money(100, 'USD') — одно и то же значение.

Aggregate — кластер связанных сущностей с одним корнем (Aggregate Root). Все изменения внутри агрегата — только через корень. Граница транзакции = граница агрегата. Пример: Order содержит OrderItems, но только Order — корень.

Domain Service — бизнес-операция, которая не принадлежит ни одной сущности. Пример: TransferService(fromAccount, toAccount, amount).

Repository — абстракция доступа к хранилищу для агрегатов. Один репозиторий — один агрегат.

Domain Event — факт, произошедший в домене. OrderPlaced, PaymentFailed.

Bounded Context — явная граница, внутри которой модель имеет единое значение. User в контексте Billing ≠ User в контексте Shipping.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍54🤔1