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

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

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

Для обратной связи: @proglibrary_feeedback_bot
Download Telegram
Что происходит при использовании команды cache:clear и как это влияет на кэшированные данные в случае использования Redis с тэгами?

Команда php artisan cache:clear очищает весь кэш, включая данные в Redis, но если используется кэширование с тегами, то команда очистит только данные, связанные с общими ключами. Теги управляются отдельно, поэтому их нужно очищать вручную, используя cache:tags() для работы с конкретными группами данных.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥21
Что такое событийно-ориентированная архитектура?

Event-driven architecture — это когда компоненты общаются через события, не зная друг о друге напрямую.

// Событие:
class UserRegistered {
public function __construct(public readonly User $user) {}
}

// Listener:
class SendWelcomeEmail {
public function handle(UserRegistered $event): void {
$this->mailer->send($event->user->email, 'Welcome!');
}
}

// Диспетчер:
$dispatcher->dispatch(new UserRegistered($user));


Зачем

✔️ Слабая связанность (UserService не знает о SendWelcomeEmail)
✔️ Легко добавить новый listener без изменения существующего кода (Open/Closed)
✔️ Можно делать асинхронные listeners (через очередь)

В Laravel: Event / Listener, EventServiceProvider.
В Symfony: EventDispatcher, декларация через атрибуты.

Подводный камень: сложно трейсить цепочку — одно событие вызывает другое.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥21
Что такое N+1 проблема и как её решить?

N+1 — классическая проблема производительности ORM.

// N+1: 1 запрос за постами + N запросов за автором каждого поста:
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name; // запрос на каждой итерации!
}

100 постов = 101 запрос к БД.


Решение — Eager Loading:

// Laravel:
$posts = Post::with('author')->get(); // 2 запроса: posts + authors IN (...)

// Doctrine:
$posts = $em->createQuery('SELECT p, a FROM Post p JOIN FETCH p.author a')->getResult();


Как обнаружить

• Laravel Debugbar / Telescope — показывает все запросы
• Логирование медленных запросов в MySQL
• Профилировщик (Blackfire, Xdebug)
Please open Telegram to view this post
VIEW IN TELEGRAM
👍52🔥1
Что такое наследование?

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

Наследование реализуется с помощью ключевого слова extends. Подкласс может расширять или переопределять поведение суперкласса, а также добавлять новые поля и методы. Важно помнить, что в PHP класс может наследоваться только от одного суперкласса.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥21
✔️ PHP-тест: Race condition

Код прошёл нагрузочное тестирование. На проде деньги задвоились 👇

📦 Задание


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

// src/Bonus/BonusTransferService.php
class BonusTransferService
{
public function __construct(
private PDO $pdo,
private BonusRepository $repo,
) {}

public function transfer(int $fromId, int $toId, int $amount): void
{
$this->pdo->beginTransaction();

try {
$fromBalance = $this->repo->getBalance($fromId);

if ($fromBalance < $amount) {
throw new InsufficientFundsException();
}

$this->repo->debit($fromId, $amount);
$this->repo->credit($toId, $amount);

$this->pdo->commit();

} catch (Throwable $e) {
$this->pdo->rollBack();
throw $e;
}
}
}

// src/Bonus/BonusRepository.php
class BonusRepository
{
public function __construct(private PDO $pdo) {}

public function getBalance(int $userId): int
{
$stmt = $this->pdo->prepare(
'SELECT balance FROM bonus_accounts WHERE user_id = ?'
);
$stmt->execute([$userId]);
return (int) $stmt->fetchColumn();
}

public function debit(int $userId, int $amount): void
{
$stmt = $this->pdo->prepare(
'UPDATE bonus_accounts SET balance = balance - ? WHERE user_id = ?'
);
$stmt->execute([$amount, $userId]);
}

public function credit(int $userId, int $amount): void
{
$stmt = $this->pdo->prepare(
'UPDATE bonus_accounts SET balance = balance + ? WHERE user_id = ?'
);
$stmt->execute([$amount, $userId]);
}
}


🔹 Задачи

— Объяснить, как именно происходит race condition в этом коде
— Почему транзакция здесь не защищает от проблемы
— Исправить getBalance так, чтобы устранить race condition

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

💬 Решения пишите в комменты под спойлер — сравним подходы.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥17🤔3👍2
Какие существуют проблемы в многопоточной среде?

Основные проблемы многопоточности:

1️⃣ Race Conditionкогда результат работы зависит от порядка выполнения потоков. Например, два потока одновременно изменяют одну переменную без синхронизации.

2️⃣ Deadlockвзаимная блокировка, когда потоки ждут друг друга. Классика: поток А держит ресурс 1 и ждёт ресурс 2, а поток Б держит ресурс 2 и ждёт ресурс 1.

2️⃣ Livelockпотоки активны, но не могут продолжить работу, постоянно реагируя на действия друг друга. Например, как два человека в коридоре, которые одновременно пытаются уступить дорогу.

4️⃣ Starvationпоток никогда не получает доступ к ресурсу из-за того, что другие потоки постоянно его перехватывают.

5️⃣ Memory Visibility изменения, сделанные одним потоком, могут быть не видны другим из-за кэширования в CPU.
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍4🔥1
💬 Обратная связь

Стоит ли спойлерить часть ответа как тут, чтобы было время подумать самостоятельно?

🔥 — Да
😁 — Нет
🤔 — Без разницы
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥23😁4🤔3
Как работает Service Container в Laravel и чем bind() отличается от singleton()?

Service Container — это IoC-контейнер, который управляет зависимостями и их разрешением.

// bind() — каждый раз создаёт новый экземпляр
app()->bind(PaymentService::class, function ($app) {
return new PaymentService($app->make(HttpClient::class));
});

// singleton() — создаёт один раз, дальше отдаёт тот же объект
app()->singleton(CacheManager::class, function ($app) {
return new CacheManager(config('cache'));
});


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

→ bind() — если объект имеет состояние, которое должно быть свежим на каждый запрос. Например, корзина пользователя.

→ singleton() — если объект stateless или его инициализация дорогая. Например, коннект к внешнему API, логгер.
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍1🔥1
Объясни разницу между Gate и Policy в Laravel?

Оба инструмента для авторизации, но с разной областью применения.

Gateпростые одиночные проверки, не привязанные к модели:

// Определяем в AuthServiceProvider
Gate::define('access-admin-panel', function (User $user) {
return $user->is_admin;
});

// Проверяем
if (Gate::allows('access-admin-panel')) { ... }
// или в контроллере
$this->authorize('access-admin-panel');


Policy класс с набором правил для конкретной модели:

// php artisan make:policy PostPolicy --model=Post
class PostPolicy
{
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id;
}

public function delete(User $user, Post $post): bool
{
return $user->id === $post->user_id || $user->is_admin;
}
}

// Использование
$this->authorize('update', $post);


Правило выбора

— Нет модели → Gate
— Есть модель, несколько действий → Policy
Please open Telegram to view this post
VIEW IN TELEGRAM
👍81🔥1
Что такое Job Chaining и Job Batching в Laravel?

Оба механизма для организации нескольких фоновых задач, но логика разная.

Job Chaining — задачи выполняются строго последовательно. Если одна упала, цепочка останавливается:

Bus::chain([
new ProcessPayment($order),
new SendInvoice($order),
new NotifyWarehouse($order),
])->dispatch();


Job Batching — задачи выполняются параллельно, можно отслеживать прогресс и реагировать на завершение всей группы:

$batch = Bus::batch([
new ImportRow($rows->chunk(100)[0]),
new ImportRow($rows->chunk(100)[1]),
new ImportRow($rows->chunk(100)[2]),
])
->then(fn (Batch $batch) => Log::info('Импорт завершён'))
->catch(fn (Batch $batch, Throwable $e) => Log::error('Ошибка'))
->finally(fn (Batch $batch) => Cache::forget('import-lock'))
->dispatch();

// Можно следить за прогрессом
$batch->progress(); // процент выполнения


Когда что

— Независимые задачи, нужна скорость и прогресс → Batch
— Зависимость между задачами, порядок важен → Chain
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍3🔥1
Как правильно протестировать?

У вас есть два класса:

class A {
public function __construct(private B $b) {}

public function doSomething(): string {
return $this->b->getValue();
}
}

class B {
public function getValue(): string {
return 'real';
}
}


Нужно написать юнит-тест для A::doSomething(), не трогая класс B. Как это сделать правильно?

Создаём мок через PHPUnit, он реализует интерфейс (или наследует класс) и позволяет изолировать зависимость:

$mockB = $this->createMock(B::class);
$mockB->method('getValue')->willReturn('mocked');

$a = new A($mockB);
$this->assertSame('mocked', $a->doSomething());


Почему работает: createMock() генерирует анонимный класс, расширяющий B. PHP позволяет передать его туда, где ожидается B.
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍1
PHP использует подсчёт ссылок для сборки мусора. В каком случае это может не сработать?

PHP уничтожает объект, когда его refcount падает до 0. Но есть исключение — циклические ссылки: объект A ссылается на B, B — на A. Каждый держит счётчик ≥ 1, хотя оба недостижимы из кода.

Для этого существует Cycle Collector (zval garbage collector). Он запускается не сразу, а когда буфер подозрительных zval заполняется (~10 000 узлов по умолчанию). До его запуска объекты в цикле живут в памяти, даже если логически мертвы.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥2🥱1
🏃‍♀️ Мы собрали бесплатный мега-гайд по ии-агентам 👇

В первой части постов навалили жесткой базы, чтобы вправить мозги на место. Во второй дали конкретные инструменты, фреймворки и пошаговые инструкции, что нужно кодить прямо сейчас.

Часть 1. Введение, юзкейсы и реальность
Разбираемся с терминами, снимаем розовые очки и смотрим, где ИИ реально приносит бабки, а где только жжет нервы:

1. «Так что вообще считается AI-агентом?»
2. «Где тут бот, а где уже AI-агент?»
3. «Не надо пихать AI-агента в каждую задачу»
4. «Что уже можно спокойно делать через AI-агентов?»
5. «А что через AI-агентов пока лучше не трогать?»

Часть 2. Изнанка, ошибки и архитектура
Как всё это устроено под капотом, чтобы не слить бюджет и не наломать дров на старте:

6. «Можно ли просто сесть вечером и собрать себе AI-агента?»
7. «С чего вообще начать, если хочется попробовать AI-агентов»
8. «Почему AI-агент может внезапно начать творить дичь»
9. «Где AI-агенты реально экономят время, а где только добавляют возни»
10. «Почему они жрут столько денег?»

Часть 3. Хардкорная практика (Что делать руками)
Хватит теории. Открываем ноут, запускаем Cursor и делаем нормальные, отказоустойчивые системы:

11. «Почему одного промпта мало?»
12. «Почему AI-агенту мало просто “дать доступ к данным”»
13. «Если не следить за AI-агентом, он быстро начинает жить своей жизнью»
14. «Собрать демку легко. Но как же сделать нормально»
15. «Как сделать, чтобы это не развалилось через неделю?»

👍 Сохраняйте пост в избранное, чтобы не потерять.

🤫 А завтра стартует наш курс по ии-агентам
Please open Telegram to view this post
VIEW IN TELEGRAM
😁2
Чем SplDoublyLinkedList, SplMinHeap и SplFixedArray отличаются от обычного array?

PHP array — это хэш-таблица с упорядоченными ключами. Универсальна, но каждый элемент занимает ~400–500 байт в zval + hashtable bucket. SPL-структуры — специализированные контейнеры с фиксированной семантикой и меньшим оверхедом.

SplFixedArray — C-массив фиксированного размера. Занимает в 3–4 раза меньше памяти. Оправдан, когда размер известен заранее.

// array: ~400 MB
$arr = array_fill(0, 1_000_000, 0);

// SplFixedArray: ~90 MB
$fixed = new SplFixedArray(1_000_000);


SplDoublyLinkedList / SplStack / SplQueue — двусвязный список под капотом. Выигрывает при частых вставках и удалениях в середину. При случайном доступе по индексу — проигрывает array: O(n) против O(1).

SplMinHeap / SplMaxHeap — бинарная куча. Классический кейс — приоритетная очередь с извлечением минимума/максимума за O(log n).

$heap = new SplMinHeap();
$heap->insert(5);
$heap->insert(1);
$heap->insert(3);

// Всегда достаёт минимум за O(log n)
echo $heap->extract(); // 1
echo $heap->extract(); // 3

⚠️ На что обратить внимание на практике


→ SplFixedArray не поддерживает строковые ключи и array_* функции — только числовые индексы
→ В PHP 8.1+ SplFixedArray реализует IteratorAggregate — работает в foreach без обёрток
→ Для задач "top-K элементов" или Dijkstra — SplMinHeap бьёт usort по всем фронтам
→ В большинстве бизнес-задач обычный array быстрее за счёт CPU-кэша — SPL оправдан при сотнях тысяч элементов
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2🤔1
Что такое Lazy Collections в Laravel?

Обычная Collection загружает всё в память сразу. При работе с большими объёмами данных — это проблема.

//  Загрузит ВСЕ записи в память
$users = User::all()->filter(...)->map(...);

// Lazy Collection — обрабатывает по одной записи через генератор
User::cursor()->filter(function (User $user) {
return $user->is_active;
})->each(function (User $user) {
ProcessUser::dispatch($user);
});


cursor() использует PDO fetchRow под капотом — в памяти одновременно одна запись.

Lazy Collection из файла:

// Обработка огромного CSV без OutOfMemoryError
$collection = LazyCollection::make(function () {
$handle = fopen('huge_file.csv', 'r');
while ($row = fgetcsv($handle)) {
yield $row;
}
});

$collection->skip(1)->chunk(100)->each(function ($rows) {
ImportBatch::dispatch($rows->toArray());
});


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

10k+ записей в обработке → cursor() + LazyCollection
Файлы, стримы, внешние API с пагинацией → LazyCollection с генератором

Важно: методы типа count() и last() материализуют коллекцию. Их лучше избегать в lazy-контексте.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍41🔥1
В чём разница между static и self в PHP?

self — ссылается на класс, в котором метод определён (compile-time).
static — ссылается на класс, из которого метод вызван (runtime, late static binding).

class A {
public static function create(): static {
return new static(); // вернёт экземпляр B, если вызвано из B
}
}

class B extends A {}

B::create(); // → объект B, не A
Please open Telegram to view this post
VIEW IN TELEGRAM
👍53🔥2
Что такое WeakReference и WeakMap?

WeakReference — ссылка на объект, не увеличивающая счётчик ссылок. Объект может быть уничтожен GC, тогда ->get() вернёт null.

WeakMap (PHP 8.0) — map с объектами в роли ключей, тоже не удерживает объекты от GC. При уничтожении объекта-ключа запись автоматически исчезает.

$map = new WeakMap();
$obj = new stdClass();
$map[$obj] = 'data';
unset($obj);
// запись в $map исчезла сама


Можно применять: кэши метаданных об объектах (атрибуты, вычисленные значения), без риска утечки памяти. Активно используется в Symfony, Doctrine.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3👍1
Ты деплоишь новую версию. Половина серверов на старом коде, половина на новом. Как не сломать пользователей?

Три зоны ответственности — три правила:

🔹 БД — только аддитивные миграции. Не удалять, не переименовывать колонки в том же деплое. Сначала добавь — потом убери старое отдельным деплоем.
🔹 API — не меняй формат ответа, только расширяй. Сломал контракт — версионируй (/v2/).
🔹 Очереди — не меняй свойства Job-классов, только добавляй. Старый воркер может достать джоб от нового кода.

Главное: feature flags вместо big bang деплоя. Код едет отдельно от включения фичи.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🔥41
Как отревьюить большой PR?

Большой PR — это архитектурная проблема, но раз он уже есть, вот стратегия:

🔹 Начинать не с первого файла в списке, а с точки входа — контроллера, команды или сервиса, где меняется логика.
🔹 До открытия первого файла — ответить на три вопроса: что должно измениться в поведении системы, что не должно, и где граница между старым и новым.
🔹 Тесты читать первыми, т.к. они документируют намерение автора лучше, чем любой комментарий.
🔹 Искать не ошибки, а допущения. Где код предполагает, что данные всегда валидны? Что очередь не упадёт? Что транзакция атомарна? Именно здесь прячутся баги, которые выстреливают через месяц.
🔹 Замечания по стилю без линтера — не тема для PR-комментариев. Если в проекте нет phpcs или php-cs-fixer, лучше их настроить, чем воевать в ревью.
Please open Telegram to view this post
VIEW IN TELEGRAM
6🔥4👍2
Symfony основан на конфигурации или соглашениях?

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