Библиотека PHP программиста 👨🏼‍💻👩‍💻
2.06K subscribers
370 photos
173 videos
7 files
395 links
Полезные материалы по PHP, Laravel, Symfony, CodeIgniter, CakePHP, Phalcon

По всем вопросам @evgenycarter
Download Telegram
Сегодня я покажу вам, как безопасно работать с пользовательским вводом в PHP — тема старая, но до сих пор частая причина уязвимостей.

Часто вижу в коде что-то вроде:


$id = $_GET['id'];
$query = "SELECT * FROM users WHERE id = $id";


Если вы всё ещё так делаете — пора остановиться. Это прямое приглашение для SQL-инъекций. Даже если вы думаете "ну я же intval() применяю", это не решение.

Правильный подход — использовать подготовленные выражения (prepared statements). Пример на PDO:


$id = $_GET['id'];
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(['id' => $id]);
$user = $stmt->fetch();


Если вы используете mysqli, там тоже есть prepare() — не игнорируйте его. Никогда не вставляйте переменные напрямую в SQL-запросы.

Также стоит помнить: фильтрация ≠ экранирование, и экранирование ≠ защита. Настоящая защита — это изоляция данных от SQL с помощью prepare().

✍️ А как вы валидируете и обрабатываете пользовательские данные? Используете ли какую-то свою обёртку или ORM? Поделитесь в комментариях.

👉 @php_lib
👍4
Сегодня я хочу поделиться приёмом, который сильно упростил мне жизнь при отладке в PHP.

🛠️ Как дебажить сложные объекты в Laravel удобно?

Когда ты работаешь с коллекциями, запросами или вложенными структурами, dd() или dump() часто захламляют вывод. Но есть куда более изящный способ:


logger($someObject);


Этот метод не прерывает выполнение скрипта, в отличие от dd(), и пишет в storage/logs/laravel.log. И вот тут начинается магия:

📌 Хочешь, чтобы лог был читаемым? Добавь кастомный формат:


logger()->info('User data', [$user->toArray()]);


Или ещё лучше — используй Laravel-помощники вроде tap():


tap($response, fn($res) => logger($res->toArray()));


💡 Это особенно удобно, когда ты хочешь просто взглянуть на данные, не роняя приложение.


🔥 Бонус: в .env можно задать LOG_LEVEL=debug, и ты увидишь всё, даже что делает сам Laravel под капотом.

Такие мелочи превращают обычную отладку в аккуратный контроль за происходящим в приложении.

А как ты обычно дебажишь в Laravel — через dd() или есть свои фишки?

👉 @php_lib
👍6
Сегодня я покажу вам, как элегантно избавиться от жёсткой зависимости от фреймворков в ваших PHP-проектах. Особенно это актуально, если вы работаете с Laravel, Symfony или любым другим тяжеловесом, и со временем начинаете чувствовать, что проект стал слишком «завязан» на конкретную экосистему.

💡 Проблема: вы используете, скажем, Illuminate\Http\Request, Illuminate\Support\Collection, или ContainerInterface из Laravel — и всё, вы уже внутри. Попробуйте вынести бизнес-логику в отдельный пакет — и он сразу неработоспособен вне Laravel.

🎯 Решение: разделяйте инфраструктуру и домен.
Вот несколько практических шагов:

1. Вводите интерфейсы в домене.
Вместо Illuminate\Http\Request — свой App\Contracts\RequestInterface. Это можно реализовать через адаптер или DTO.

2. Убирайте фреймворк из бизнес-логики.
Доменный код не должен знать про контроллеры, контейнеры и фасады. Он должен работать с чистыми данными, переданными через интерфейсы или value-объекты.

3. Интеграция на уровне Application Layer.
Фреймворк используется для "склейки" — он передаёт данные в домен и получает результат. Например, Laravel Controller вызывает сервис, но сервис не зависит от Laravel.

4. Проверяйте независимость.
Попробуйте запустить доменную часть проекта в консольном скрипте или тесте вне Laravel — если всё работает, вы на правильном пути.

📦 В результате вы получаете код, который легко тестировать, переносить между проектами, использовать вне фреймворка — хоть в Laravel, хоть в Symfony, хоть в чистом PHP.

✍️ Я лично начал писать всю бизнес-логику как Composer-пакеты без зависимости от Laravel. Это не просто — особенно поначалу — но это окупается на 100%.

Как вы решаете проблему зависимости от фреймворков? Поделитесь опытом 👇

👉 @php_lib
👍5🤔2💩1
Сегодня расскажу про одну из самых частых болей в любом проекте на Laravel — отладку запросов с Eloquent и N+1 проблемой.

Ты наверняка сталкивался с ситуацией: всё работает, но чуть-чуть замедляется. Начинаешь копать и видишь кучу повторяющихся SQL-запросов. Это классическая N+1 проблема — когда вместо одного запроса Laravel делает десятки.

👀 Как это проявляется?

У тебя есть, например, список постов и у каждого поста автор:


$posts = Post::all();

foreach ($posts as $post) {
echo $post->user->name;
}


Laravel выполнит 1 запрос на посты и N запросов на пользователей. А должен был бы — 2 запроса максимум.

🛠 Решение простое — eager loading:


$posts = Post::with('user')->get();


Теперь Laravel сначала загрузит всех постов, а потом сразу всех юзеров одним вторым запросом.



📦 Но как быстро выявить такие ошибки?

Используй Laravel Debugbar или Clockwork. Они наглядно показывают все SQL-запросы и сразу виден N+1.

Ещё один вариант — в проде подключить Laravel Telescope, если это безопасно, или поставить простую логику, которая логирует количество SQL-запросов на каждый HTTP-запрос.


⚠️ Совет: всегда используй with() при выборке данных для списков, особенно если в шаблоне дергаешь связанные модели.

👉 @php_lib
👍5
Сегодня я расскажу вам о том, как настроить эффективное логирование ошибок в PHP-приложениях с помощью библиотеки Monolog.

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

1. Группировать логи по каналам
Вы можете создавать каналы для разных частей приложения:


use Monolog\Logger;
use Monolog\Handler\StreamHandler;

$dbLogger = new Logger('database');
$dbLogger->pushHandler(new StreamHandler(__DIR__.'/logs/db.log', Logger::WARNING));

2. Использовать разные обработчики (handlers)
Отправка ошибок в файлы, на почту или в Slack:


use Monolog\Handler\NativeMailerHandler;
$emailHandler = new NativeMailerHandler('you@domain.com', 'DB Error', 'app@domain.com', Logger::ERROR);
$dbLogger->pushHandler($emailHandler);

3. Добавлять процессоры (processors)
Автоматически дополнять каждый лог юзер-агентом, IP и временем выполнения:


use Monolog\Processor\WebProcessor;
$dbLogger->pushProcessor(new WebProcessor());

4. Форматировать вывод
Пример простого форматера:


use Monolog\Formatter\LineFormatter;
$formatter = new LineFormatter("[%datetime%] %channel%.%level_name%: %message% %context%\n");
$handler->setFormatter($formatter);


🎯 Совет на практике: заведите отдельные файлы логов под разные уровни важности (INFO, WARNING, ERROR), чтобы при поиске нужных записей достаточно было открыть один файл.

А как вы организуете логи в своих проектах? Делитесь опытом в комментариях!

👉 @php_lib
👍51👎1
Сегодня расскажу вам про одну простую, но очень полезную привычку для любого PHP-разработчика — вести чек-листы по задачам прямо в коде.

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

Я просто добавляю короткие TODO-комментарии в нужных местах кода. Например:


// TODO: Проверить валидацию email после изменений
// TODO: Перевести этот блок на отдельный сервис


Каждый раз перед коммитом быстро пробегаюсь по проекту поиском по "TODO" — и вижу все свои маленькие хвосты. А когда задач становится много, использую расширения для IDE (например, "TODO Highlight" в VSCode) — и визуально сразу видно все невыполненные задачи.

Почему это работает:

* Не надо держать всё в голове.
* Любой разработчик быстро вникает в текущие проблемы кода.
* Легче планировать рефакторинг.
* Сложно забыть про важную доработку.

Советую прямо сегодня попробовать добавить пару своих TODO в код. Дай знать, пользуешься ли ты таким приёмом или предпочитаешь другие методы? Пиши в комментариях!

👉 @php_lib
👍7🙈2🌚1
💡Совет по Laravel

Знаешь ли ты, что можно выбрасывать исключения условно всего в одну строку с помощью throw_if() — идеально для лаконичного кода в контроллерах и сервисах.

👉 @php_lib
👍7🥴3🔥1
💡Совет по Laravel

Строишь URL с динамическими параметрами для HTTP-запроса?
Не нужно вручную склеивать длинную строку!

Используй URI-шаблоны с методом withUrlParameters() — так код станет чище и понятнее при работе с API.

https://laravel.com/docs/12.x/http-client#uri-templates

👉 @php_lib
👍5
strlen() vs mb_strlen(): Почему 71% PHP-разработчиков ошибаются

В этой статье разберем, как PHP работает со строками и почему даже простой вызов strlen() может привести к неожиданным результатам. На примере крайне простой задачи "что выведет echo strlen('привет!');" посмотрим, что и как влияет на подсчет длины строки, заглянем внутрь реализации strlen() и развенчаем миф о строках как массивах.

https://habr.com/ru/articles/919192/

👉 @php_lib
👍4
Сегодня я покажу вам, как быстро разрулить хаос в проекте с кастомным фреймворком, где нет нормального логирования.

Ситуация: клиент дал старый проект, ошибок — куча, но логов нет вообще. В коде try-catch, а в catch — пусто. Что делать?

Вот мой быстрый чеклист, который спасал не раз:


1. Глобальный обработчик ошибок и исключений


set_error_handler(function ($errno, $errstr, $errfile, $errline) {
error_log("[PHP ERROR] $errstr in $errfile:$errline");
return false;
});

set_exception_handler(function (Throwable $e) {
error_log("[UNCAUGHT EXCEPTION] " . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
});


Добавил в index.php, и уже вижу, что именно валится.


2. Простой логгер на каждый чих

Создал файл logger.php:


function log_debug($msg) {
file_put_contents(__DIR__.'/debug.log', date('[Y-m-d H:i:s] ') . $msg . PHP_EOL, FILE_APPEND);
}


В нужных местах пишу log_debug('что-то случилось');. Дёшево и сердито, зато работает.


3. Прокинул заголовки и запросы

В самых первых строках запроса:


log_debug("REQUEST: " . $_SERVER['REQUEST_METHOD'] . ' ' . $_SERVER['REQUEST_URI']);
log_debug("HEADERS: " . json_encode(getallheaders()));
log_debug("POST: " . json_encode($_POST));


Теперь видно, с чем именно клиент ломает мой старый код.


Не всегда есть время подтягивать Monolog и делать всё красиво. Иногда нужно быстро найти, где горит — и потушить. А уже потом наводить красоту.

А вы как действуете в таких ситуациях? Есть свои приёмы?

👉 @php_lib
👍11😁1💯1
Laravel lazy() против get()

Знаешь ли ты… что можно обрабатывать большие объёмы данных из базы с помощью lazy() — это гораздо эффективнее по памяти, чем get().

👉 @php_lib
👍61
💡Совет по Laravel

Знаешь ли ты… о методе skip в планировщике Laravel?

С помощью skip() можно указать, когда команда не должна выполняться.

👉 @php_lib
👍7🤣1
Простой, но полезный приём для дебага в Laravel — логирование SQL-запросов в человекочитаемом виде.

По умолчанию DB::listen() в Laravel выводит запрос с плейсхолдерами ?, а значения параметров идут отдельно. Это не всегда удобно — сложно быстро понять, что реально выполняется.

Вот сниппет, который собирает полный SQL-запрос с подставленными значениями:


DB::listen(function ($query) {
$sql = $query->sql;
foreach ($query->bindings as $binding) {
$binding = is_numeric($binding) ? $binding : "'{$binding}'";
$sql = preg_replace('/\?/', $binding, $sql, 1);
}
logger()->info("Executed query: {$sql}");
});


📌 Пример:


select * from users where email = 'john@example.com' and age > 18


Очень удобно, когда ты хочешь понять, почему условие WHERE не сработало или почему запрос медленный.

Можно кинуть это в AppServiceProvider::boot(), и на локальной машине всегда видеть, что реально уходит в базу.

👉 @php_lib
👍13
💡 Совет для Laravel‑разработчиков

Используйте Crypt facade — он уже есть в Laravel:

- Работает на вашем APP_KEY
- Шифрует данные через AES‑256‑CBC
- Защищает от подделки (MAC) 🔐

Храните API‑токены, пароли и секреты безопасно — прямо из коробки.

👉 @php_lib
👍5
Совет по PHP/Laravel 🚀

Когда у вас есть два случая для кастомных исключений, относящихся к одному и тому же домену, вместо создания нескольких классов исключений можно использовать один класс с разными методами!

Так код будет чище и лучше структурирован! 💪🔥

👉 @php_lib
👍51🍓1
Как правильно работать с большими массивами данных в PHP, чтобы не убить память и сервер.

Когда мы пишем скрипты, которые обрабатывают тысячи (или миллионы) строк — будь то импорт из CSV, выгрузка из БД или массовая обработка, — часто допускается ошибка: всё грузится в память сразу. В итоге сервер умирает от нехватки памяти, а скрипт - от таймаута.

Что делать?

1. Частями, а не всё сразу
Например, если вы работаете с PDO:


$stmt = $pdo->query("SELECT * FROM big_table");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
process($row); // Обрабатываем каждую строку на лету
}


Так данные не будут храниться в огромном массиве — обработка идёт построчно.

2. Используйте генераторы
Генераторы (yield) позволяют работать с большими данными лениво:


function getRows($pdo) {
$stmt = $pdo->query("SELECT * FROM big_table");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
yield $row;
}
}

foreach (getRows($pdo) as $row) {
process($row);
}


Память почти не растёт — магия генераторов.

3. Разбивайте на батчи
Иногда эффективнее брать кусками:


$limit = 1000;
for ($offset = 0; ; $offset += $limit) {
$rows = $pdo->query("SELECT * FROM big_table LIMIT $limit OFFSET $offset")->fetchAll();
if (!$rows) break;
foreach ($rows as $row) {
process($row);
}
}


4. Не забывайте про стриминг файлов
Если это CSV или JSON — читайте построчно через fgets() или stream_get_line(), а не file_get_contents().

Такие подходы сильно упрощают жизнь и экономят ресурсы.
А вы как работаете с большими данными? Используете генераторы или предпочитаете батчи?

👉 @php_lib
👍81
💡Совет по Laravel: Проверка дат с помощью Carbon

Уверен, тебе хоть раз приходилось проверять, истекла ли дата или находится в будущем.
Так как Laravel под капотом использует Carbon, у тебя есть доступ к множеству удобных методов, которые позволяют делать это элегантно 🚀

👉 @php_lib
👍5
Media is too big
VIEW IN TELEGRAM
Полный курс Laravel Swagger. REST API Laravel

00:00:00 Введение
00:04:25 Делаем заготовку
00:16:20 Установка Swagger
00:22:18 Общие аннотации и настройки Swagger
00:28:01 CRUD со Swagger
01:03:03 Установка JWT Auth
01:13:08 Авторизация и защищенные роуты в Swagger
01:20:20 Бонус. Нестандартные тело запроса и ответа

источник

👉 @php_lib
👍7
Сегодня хочу поделиться полезной практикой, которая реально экономит время при работе с чужим кодом — быстрая навигация и понимание проекта через composer.json.

Когда попадаешь в новый проект, чаще всего первым делом начинаешь блуждать по папкам, читать контроллеры, роуты, сервисы… Но есть более быстрый способ понять архитектуру - сразу открыть composer.json.

Что можно сразу понять:

- Какие библиотеки используются - а значит, какие фреймворки, ORM, инструменты кеширования или логирования ждут нас внутри.
- Какая версия PHP нужна — помогает избежать сюрпризов с несовместимыми функциями.
- Автозагрузка - раздел autoload и autoload-dev сразу подскажет, где искать код и как он структурирован. Например, если вижу App\\ => src/, я знаю, что основной код живёт в src.
- Скрипты - в scripts иногда прячутся удобные команды для деплоя, генерации, тестов, которые можно запускать через composer run-script.

Я часто делаю так: открываю composer.json, сразу строю для себя “карту” проекта и понимаю, где искать бизнес-логику и какие инструменты уже встроены.

А вы как знакомитесь с новым проектом? С чего начинаете?

👉 @php_lib
👍61🤷‍♂1