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

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

Відкрити в Telegram

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

Показати більше

📈 Аналітичний огляд Telegram-каналу Библиотека пхпшника | PHP, Laravel, Symfony, CodeIgniter

Канал Библиотека пхпшника | PHP, Laravel, Symfony, CodeIgniter (@phpproglib) у мовному сегменті Російська є активним учасником. На даний момент спільнота об'єднує 10 523 підписників, посідаючи 11 314 місце в категорії Технології та додатки та 60 447 місце у регіоні Росія.

📊 Показники аудиторії та динаміка

З моменту свого створення невідомо, проект продемонстрував стрімке зростання, зібравши аудиторію у 10 523 підписників.

За останніми даними від 26 серпня, 2026, канал демонструє стабільну активність. Хоча за останні 30 днів спостерігається зміна кількості учасників на -63, а за останні 24 години на -1, загальне охоплення залишається високим.

  • Статус верифікації: Не верифікований
  • Рівень залученості (ER): Середній показник залученості аудиторії становить 19.12%. Протягом перших 24 годин після публікації контент зазвичай збирає 9.47% реакцій від загальної кількості підписників.
  • Охоплення публікацій: В середньому кожен допис отримує 2 012 переглядів. Протягом першої доби публікація в середньому набирає 997 переглядів.
  • Реакції та взаємодія: Аудиторія активно підтримує контент: середня кількість реакцій на один пост – 15.
  • Тематичні інтереси: Контент зосереджений навколо ключових тем, таких як php, laravel, пхпшника, artisan, api.

📝 Опис та контентна політика

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

Завдяки високій частоті оновлень (останні дані отримано 27 серпня, 2026), канал підтримує актуальність та високий рівень охоплення публікацій. Аналітика показує, що аудиторія активно взаємодіє з контентом, що робить його важливою точкою впливу в категорії Технології та додатки.

10 523
Підписники
-124 години
-157 днів
-6330 день
Архів дописів
Какой признак характерен для архитектуры 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 комментов. Что думаете, пишите в комменты? #междусобойчик