en
Feedback
Библиотека пхпшника | PHP, Laravel, Symfony, CodeIgniter

Библиотека пхпшника | PHP, Laravel, Symfony, CodeIgniter

Open in Telegram

Все самое полезное для пхпшника в одном канале. По рекламе: @proglib_adv Учиться у нас: clc.to/M561SQ Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a5d13cd6fa92100ee6f68b

Show more

📈 Analytical overview of Telegram channel Библиотека пхпшника | PHP, Laravel, Symfony, CodeIgniter

Channel Библиотека пхпшника | PHP, Laravel, Symfony, CodeIgniter (@phpproglib) in the Russian language segment is an active participant. Currently, the community unites 10 522 subscribers, ranking 11 314 in the Technologies & Applications category and 60 447 in the Russia region.

📊 Audience metrics and dynamics

Since its creation on невідомо, the project has demonstrated rapid growth, gathering an audience of 10 522 subscribers.

According to the latest data from 26 August, 2026, the channel demonstrates stable activity. Although there has been a change in the number of participants by -63 over the last 30 days and by -1 over the last 24 hours, overall reach remains high.

  • Verification status: Not verified
  • Engagement rate (ER): The average audience engagement rate is 19.12%. Within the first 24 hours after publication, content typically collects 9.47% reactions from the total number of subscribers.
  • Post reach: On average, each post receives 2 012 views. Within the first day, a publication typically gains 997 views.
  • Reactions and interaction: The audience actively supports content: the average number of reactions per post is 15.
  • Thematic interests: Content is focused on key topics such as php, laravel, пхпшника, artisan, api.

📝 Description and content policy

The author describes the resource as a platform for expressing subjective opinions:
Все самое полезное для пхпшника в одном канале. По рекламе: @proglib_adv Учиться у нас: clc.to/M561SQ Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a5d13cd6fa92100ee6f68b

Thanks to the high frequency of updates (latest data received on 27 August, 2026), the channel maintains relevance and a high level of publication reach. Analytics show that the audience actively interacts with content, making it an important point of influence in the Technologies & Applications category.

10 522
Subscribers
-124 hours
-157 days
-6330 days
Posts Archive
Какой признак характерен для архитектуры CQRS?
Anonymous voting

📊 Doctrine vs Eloquent Их сравнивают как два способа работать с БД. На самом деле это два разных взгляда на то, что такое доменная модель. Eloquent — Active Record. Модель знает о базе данных. Она сама сохраняет себя, сама удаляет, сама строит запросы. Это удобно и это цена: бизнес-логика и слой персистентности перемешаны с рождения.
// Eloquent — модель и есть запись в таблице
$user = User::where('email', $email)->firstOrFail();
$user->status = 'active';
$user->save();
Doctrine — Data Mapper. Модель ничего не знает о БД. EntityManager знает, как сохранить объект. Объект остаётся чистым PHP-классом.
// Doctrine — модель не знает про persist
$user = $this->repository->findByEmail($email);
$user->activate(); // метод в домене, не save()
$this->em->flush();
✔️ Где Eloquent выигрывает — CRUD-приложения, где домен тонкий — Быстрый старт, меньше конфигурации — Команда знакома с Laravel — не надо учить новый mental model Где Eloquent начинает мешать — Сложные агрегаты с инвариантами — Хочешь тестировать доменную логику без БД — Один объект — несколько таблиц или наоборот ✔️ Где Doctrine оправдан — Богатая доменная модель (DDD, агрегаты, value objects) — Нужна полная изоляция домена от инфраструктуры — Сложные маппинги, наследование, polymorphic relations с контролем Главный сигнал: если ты ловишь себя на том, что пишешь методы в Eloquent-модели, которые делают $this->save() — домен начинает протекать. Это не баг Eloquent, это его дизайн. Просто решай осознанно. 🐸 Библиотека пхпшника #элементарный_выбор

🔒 Prepared statements Prepared statements защищают от инъекций. Но также их можно осознанно использовать для оптимизации. Когда вы выполняете один и тот же запрос в цикле — БД каждый раз парсит и строит план выполнения заново. Плохо:
foreach ($userIds as $id) {
    $pdo->query("SELECT * FROM users WHERE id = $id"); // парсинг + план на каждой итерации
}
Хорошо — prepare один раз, execute много раз:
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
 
foreach ($userIds as $id) {
    $stmt->execute([':id' => $id]);
    $user = $stmt->fetch();
}
БД кеширует план выполнения запроса\. На 10 000 итераций разница ощутима\. Ещё лучше — batch-запрос:
$placeholders = implode(',', array_fill(0, count($userIds), '?'));
$stmt = $pdo->prepare("SELECT * FROM users WHERE id IN ($placeholders)");
$stmt->execute($userIds);
$users = $stmt->fetchAll();
// Один запрос вместо N
Библиотека пхпшника #vardump

✔️ PHP-тест: Generators + Memory leak + Batch-обработка «Мы перешли на генераторы, чтобы не грузить память» — но память всё равно растёт 👇 📦 Задание Команда переписала импорт CSV на генераторы — раньше падало с OOM на файлах больше 500 МБ. После рефакторинга память перестала расти... на стейджинге. На проде с реальными файлами на 2M+ строк потребление всё равно ползёт вверх.
// src/Import/CsvImporter.php
class CsvImporter
{
    private array $processedIds = [];
    private array $errors       = [];
    private int   $totalRows    = 0;

    public function import(string $filePath): ImportResult
    {
        foreach ($this->readRows($filePath) as $row) {
            $this->totalRows++;

            try {
                $id = $this->processRow($row);
                $this->processedIds[] = $id;
            } catch (RowException $e) {
                $this->errors[] = [
                    'row'     => $this->totalRows,
                    'message' => $e->getMessage(),
                    'data'    => $row,
                ];
            }
        }

        return new ImportResult($this->processedIds, $this->errors, $this->totalRows);
    }

    private function readRows(string $filePath): \Generator
    {
        $handle = fopen($filePath, 'r');
        $headers = fgetcsv($handle);

        while (($raw = fgetcsv($handle)) !== false) {
            yield array_combine($headers, $raw);
        }

        fclose($handle);
    }

    private function processRow(array $row): int
    {
        // Валидация, маппинг, вставка в БД
        // Возвращает inserted ID
        return $this->repository->upsert($row);
    }
}

// src/Import/ImportResult.php
class ImportResult
{
    public function __construct(
        public readonly array $processedIds,
        public readonly array $errors,
        public readonly int   $totalRows,
    ) {}
}
🔹 Задачи — Найти все источники роста памяти — Объяснить, почему проблема не воспроизводится на стейджинге — Предложить решение Ставьте → 🔥 если нравится формат. Если нет → 🌚 💬 Решения пишите в комменты под спойлер — сравним подходы.

📊 Queue vs Cron Оба инструмента про «выполнить что-то не прямо сейчас». Но они решают разные задачи, и смешивать их не стоит. Cron — планировщик. Он про время. «Запусти это в 03:00». «Запусти каждые 5 минут». Ему всё равно, что именно запускать — он просто триггерит процесс по расписанию. Queue — очередь. Она про события и нагрузку. «Отправь письмо, когда пользователь зарегистрировался». «Обработай 10 000 строк CSV асинхронно». Очередь не знает про время, она знает про задачи. 🔹 Где Cron уместен — Агрегация данных за период (ночные отчёты) — Очистка устаревших записей — Синхронизация с внешним источником по расписанию — Задачи, которые должны выполняться независимо от пользовательской активности 🔹 Где Queue уместен — Всё, что пользователь спровоцировал, но ждать не должен — Задачи с потенциальными ретраями (внешний API может упасть) — Параллельная обработка (несколько воркеров) — Отправка уведомлений, генерация файлов, вебхуки Если ты пишешь cron-задачу, которая ищет записи со статусом "pending" — ты уже строишь очередь. Используй нормальную: Redis + Laravel Horizon, RabbitMQ, SQS, Beanstalkd. Комбинация: cron запускает job, который кладёт задачи в очередь. Это нормально — диспетчер по расписанию и обработчики асинхронно. 🐸 Библиотека пхпшника #элементарный_выбор

✌🏻 У нас две новости — хорошая и плохая! Хорошая: Ваших знаний, скорее всего, хватит, чтобы собрать рабочую демку AI-агента в Colab. 🫡 Плохая: Вы вряд ли выведете его в прод, не обанкротившись на токенах и не слив базу. 🤯 Для защиты от таких сценариев мы полностью пересобрали курс «Разработка AI-агентов». Теперь внутри плотная работа с экономикой ресурсов, дебаг через time-travel в LangGraph, извлечение данных из кривых сканов для RAG и комплаенс по 152-ФЗ.
Если всё ещё сомневаетесь, послушайте голосовое от спикера курса Влада Прошинского, где он объясняет, как правильно тестировать агентов перед релизом.
Программа курса, полный состав спикеров и другие подробности 👈🏻 ВАЖНО! До 5 апреля на курс действует скидка, но свободные места могут закончиться раньше.

⌨️ Топ-вакансий по PHP за неделю PHP разработчик - от 150 000 до 200 000 ₽ - удалёнка Senior Fullstack PHP разработчик - от 250 000 до 350 000 ₽ - удалёнка Lead PHP Developer - до 400 000 ₽ - удалёнка ➡️ Еще больше топовых вакансий — в нашем канале PHP Jobs

📊 Repository Pattern vs Query Builder Repository — это не про «обернуть Eloquent в класс». Это про то, что доменный код не должен знать, откуда берутся данные. Плохой Repository, который можно часто встретить:
class UserRepository {
    public function findActive(): Collection {
        return User::where('status', 'active')->get();
    }
 
    public function findByEmailAndStatus(string $email, string $status): ?User {
        return User::where('email', $email)
                   ->where('status', $status)
                   ->first();
    }
    // ...ещё 40 методов под каждый запрос
}
Это не Repository — это коллекция запросов в обёртке. Домен по-прежнему диктует методы через свои нужды, а репозиторий распухает. Хороший Repository работает с агрегатами и скрывает детали хранения:
interface UserRepository {
    public function findById(UserId $id): ?User;
    public function findByEmail(Email $email): ?User;
    public function save(User $user): void;
    public function remove(User $user): void;
}
Всё остальное — спецификации, критерии, query objects. Или честный Query Builder там, где это не домен. Где Query Builder напрямую оправдан: — Read-модели (CQRS: команды через домен, запросы — прямо в БД) — Отчёты, дашборды, агрегации — там нет смысла гидрировать объекты — Простые CRUD-экраны без доменной логики
// Это нормально для read-модели
$stats = DB::table('orders')
    ->selectRaw('status, COUNT(*) as count, SUM(amount) as total')
    ->groupBy('status')
    ->get();
Пытаться пропустить это через Repository с Order-сущностями — оверинжиниринг. Правило: Repository живёт на границе домена. Если у тебя нет домена, нет смысла в Repository. Если домен есть, Repository скрывает инфраструктуру, а не просто переносит запросы в другой файл. 🐸 Библиотека пхпшника #элементарный_выбор

📊 str_contains / str_starts_with vs strpos strpos возвращает int|false. Это создаёт одну конкретную проблему: проверку на false нужно делать строго:
// Классическая ошибка
if (strpos($url, 'admin')) {
    // Не выполнится, если 'admin' в позиции 0
}
 
// Правильно
if (strpos($url, 'admin') !== false) {
    // Окей
}
Этот паттерн !== false живёт в кодбазах как ритуал, смысл которого половина команды уже не помнит. PHP 8.0 дал str_contains, str_starts_with, str_ends_with. Они возвращают bool. Просто bool.
if (str_contains($url, 'admin')) { ... }
if (str_starts_with($route, '/api')) { ... }
if (str_ends_with($file, '.blade.php')) { ... }
Никакого !== false. Никакой магии с позицией 0. Что осталось за strpos: — Нужна позиция вхождения, а не факт наличия — Нужен offset для поиска со смещением — substr_count, substr_replace всё ещё работают с позициями
$pos = strpos($text, '{{');
$end = strpos($text, '}}', $pos);
// Здесь strpos оправдан — тебе нужны числа
Про производительность: str_contains не медленнее strpos на коротких строках. На длинных — профилируй конкретный кейс, не гадай. Короткий вывод: str_contains и компания — это не синтаксический сахар. Это правильный тип возврата для правильного вопроса. 🐸 Библиотека пхпшника #элементарный_выбор

✔️ 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 не доходит до контроллера — и данные считаются потерянными — Объяснить проблему — Предложить исправленную структуру Ставьте → 🔥 если нравится формат. Если нет → 🌚 💬 Решения пишите в комменты под спойлер — сравним подходы.

🧠 array_map vs foreach Многие считают array_map более "функциональным" и современным. Но есть нюанс. Бенчмарк на массиве из 100 000 элементов:
// array_map — ~12ms
$result = array_map(fn($x) => $x * 2, $items);
 
// foreach — ~6ms
$result = [];
foreach ($items as $x) {
    $result[] = $x * 2;
}
foreach в 1.5–2 раза быстрее на больших массивах, потому что array_map создаёт замыкание и имеет overhead на каждый вызов callback. Когда всё равно использовать array_map? Когда важна читаемость, а массив маленький (до ~1000 элементов) — разница незаметна. Не оптимизируйте там, где это не нужно.
// Читаемо и достаточно быстро для небольших коллекций
$emails = array_map(fn($u) => $u->email, $users);
💡 Правило: сначала профилируйте, потом оптимизируйте. Blackfire и Xdebug в помощь. Библиотека пхпшника #vardump

#байтовый_юмор
#байтовый_юмор

😱 Если ваш продукт не умеет отдавать данные в формате, понятном AI-агенту, то вас просто не существует Скрипт не будет кликать по красивым кнопкам в браузере, он уйдёт к конкуренту с нормальным API. Перестроить архитектуру под машинных клиентов — это уже не хайп, а необходимое условие сохранения конкурентоспособности. Как адаптировать продукт и не исчезнуть из выдачи: — интегрировать MCP и A2A-взаимодействие, чтобы агенты могли вас читать; — научиться контролировать стоимость (лимиты, кэш, роутинг между моделями); — настроить AgentOps: трейсинг, логирование и отлов регрессий. Всё это ждёт вас на обновлённом курсе «Разработка AI-агентов». Мы специально сделали фокус на утилитарном инжиниринге и production-ready решениях. Кстати, до 29 марта можно забрать курс с большой скидкой, и стоит поторопиться — мест на потоке всё меньше. Зафиксировать цену и начать деплоить агентов без слива бюджета 👈

 ♻️ Redis-кеш с AI Настроить кеш через Redis несложно. Но зачем копировать код из старого проекта, бояться что-то потерять, когда можно сгенерировать эту часть. Промпт, который закроет этот вопрос:
Ты Senior PHP-разработчик. Сгенерируй модуль кеширования для Laravel с Redis, с учётом всех современных паттернов, устойчивости и тестирования. Требования: Использование Redis через phpredis или predis (с учётом конфигурации в .env) Кеширование с разными TTL для разных типов данных (разделение по Cache Store) 2 сервиса с кешированием: Стратегия cache-aside (чтение из кеша и обновление вручную) Стратегия write-through (автоматическое обновление кеша при записи данных) Обработка инвалидации кеша при изменении данных Кастомная обработка ошибок: Fallback на in-memory или file cache (без исключений при недоступности Redis) Логирование ошибок кеширования с уведомлением Применение circuit breaker и retry паттернов при сете ошибок Redis Защита от слишком частых запросов через кеширование с TTL и expiration для разных кешей Изоляция кеш-слоя с использованием сервисов и репозиториев (не Cache фасад в контроллерах) Настройки в .env и конфигурации кеша Простой и понятный конфиг с разделением кешей для разных типов данных Юнит-тесты с использованием Testcontainers для запуска Redis в Docker Тестирование fallback-механизмов Мокирование с имитацией недоступности Redis Тесты на использование кеша с разными TTL Стек: PHP 8.1+, Laravel 9.x, Redis, PHPUnit, Docker, Testcontainers
Библиотека пхпшника

🧠 PHP-лайфхак Хочешь ловить баги до запуска кода? Используй статический анализ. Это инструмент, который проверяет твой PHP-код на ошибки, несоответствия типам и подозрительные конструкции — без выполнения программы. 🔍 Как это работает 🧩 Установи PHPStan: composer require --dev phpstan/phpstan Запусти анализ: ./vendor/bin/phpstan analyse src --level=max --level=max включает максимально строгую проверку. Можно использовать уровни от 0 до 9 — начни с 5–6, чтобы не утонуть в предупреждениях. ⚡️ Что даёт PHPStan / Psalm 🐞 Находит ошибки типов ещё до запуска кода 🚫 Предупреждает о неиспользуемых переменных и мёртвом коде 🧩 Помогает навести порядок в архитектуре 🔒 Снижает риск багов при рефакторинге 💡 Интеграция с Composer и CI/CD Добавь команду в composer.json: "scripts": { "analyze": "phpstan analyse src --level=max" } Теперь можно запускать проверку просто: composer analyze А в CI — добавить шаг перед деплоем, чтобы ни один коммит с ошибками типов не ушёл в прод. Библиотека пхпшника

⌨️ Топ-вакансий по PHP за неделю PHP Developer (Laravel) - 150 000 - 200 000₽ - офис/гибрид/удалёнка (Ульяновск) Backend-разработчик (PHP) - до 220 000 ₽ - офис/гибрид (Москва) Старший fullstack-разработчик PHP (Laravel) - от 300 000 до 350 000 ₽ - удалёнка ➡️ Еще больше топовых вакансий — в нашем канале PHP Jobs

💬 Давайте обсудим К статье на Хабре «Я два месяца платил 300к человеку, который тихо скармливал мои задачи в ChatGPT» уже почти 900 комментов. Что думаете, пишите в комменты? #междусобойчик