Топ ошибок начинающих PHP разработчиков: продвинутый уровень

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Топ ошибок начинающих PHP разработчиков: продвинутый уровень

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

1. Неправильное управление сессиями и утечки памяти

Одна из самых коварных ошибок — это неправильная работа с сессиями. Многие разработчики хранят в сессиях большие объёмы данных (например, результаты выборок из БД или сериализованные объекты). Это не только замедляет работу, но и может привести к утечкам памяти на сервере.

Проблема: Сессия блокируется на время выполнения скрипта. Если вы храните в ней 10 МБ данных, каждый запрос будет читать и записывать этот объём, а при параллельных запросах — выстраиваться в очередь.

Пример плохого кода:

<?php
session_start();
// Загружаем в сессию массив из 10000 записей
$_SESSION['big_data'] = $db->query("SELECT * FROM logs")->fetchAll();
?>

Решение: Используйте сессии только для идентификации (user_id, токен). Все остальные данные храните в кэше (Redis, Memcached) или в базе данных.

<?php
session_start();
$_SESSION['user_id'] = $user->id;
// Данные получаем по требованию
$cache = new RedisCache();
$data = $cache->get('user_data_' . $user->id);
if (!$data) {
    $data = $db->query("SELECT * FROM logs WHERE user_id = ?", [$user->id])->fetchAll();
    $cache->set('user_data_' . $user->id, $data, 3600);
}
?>

2. Неправильное кэширование и игнорирование инвалидации

Вторая по популярности ошибка — это кэширование "навсегда". Разработчики ставят время жизни кэша (TTL) в 24 часа или больше, а потом удивляются, почему пользователи видят устаревшие данные. Ещё хуже — кэширование без учёта изменений в базе данных.

Проблема: Если вы кэшируете список товаров на 1 час, а администратор изменил цену — пользователи увидят старую цену ещё 60 минут.

Пример плохого кода:

<?php
$cacheKey = 'product_list';
$products = $cache->get($cacheKey);
if (!$products) {
    $products = $db->query("SELECT * FROM products")->fetchAll();
    $cache->set($cacheKey, $products, 86400); // 24 часа
}
?>

Решение: Используйте тегирование кэша или принудительную инвалидацию при изменении данных.

<?php
// При обновлении товара
function updateProduct($id, $data) {
    $db->update("products", $data, ["id" => $id]);
    // Инвалидируем кэш
    $cache->delete('product_list');
    $cache->delete('product_' . $id);
}

// При чтении
$products = $cache->remember('product_list', 3600, function() use ($db) {
    return $db->query("SELECT * FROM products WHERE active = 1")->fetchAll();
});
?>

3. SQL-инъекции через "безопасные" функции

Многие разработчики считают, что если они используют mysqli_real_escape_string() или addslashes(), то они защищены от SQL-инъекций. Это опасное заблуждение. Эти функции не защищают от всех типов атак, особенно при работе с LIKE, ORDER BY и LIMIT.

Проблема: Функция mysqli_real_escape_string() не экранирует символы для оператора LIKE (например, % и _). Если вы подставляете пользовательский ввод в LIKE, злоумышленник может получить доступ к данным, которые не должен видеть.

Пример уязвимости:

<?php
$search = $_GET['q'];
$safe = mysqli_real_escape_string($conn, $search);
$query = "SELECT * FROM users WHERE username LIKE '%$safe%'";
// Если $search = "%", то будут выбраны все пользователи
?>

Решение: Используйте только подготовленные запросы (PDO или MySQLi с параметрами). Для LIKE дополнительно экранируйте символы % и _.

<?php
$search = $_GET['q'];
// Экранируем спецсимволы LIKE
$search = str_replace(['%', '_'], ['\%', '\_'], $search);
$stmt = $pdo->prepare("SELECT * FROM users WHERE username LIKE ?");
$stmt->execute(['%' . $search . '%']);
?>

4. Игнорирование типов данных и строгих сравнений

PHP — язык с динамической типизацией, и это часто приводит к неожиданным ошибкам. Например, сравнение строки с числом, или использование == вместо === может дать совершенно не тот результат, который вы ожидаете.

Проблема: Функция in_array() по умолчанию использует нестрогое сравнение. Если вы проверяете, есть ли ID в массиве, то значение "0" может совпасть с пустой строкой.

Пример ошибки:

<?php
$allowedIds = [1, 2, 3];
$inputId = "0"; // Пришло из формы
if (in_array($inputId, $allowedIds)) {
    // Это условие выполнится! Потому что "0" == 0, а 0 == false
    echo "Доступ разрешён";
}
?>

Решение: Всегда используйте строгие сравнения и явно указывайте третий параметр true в in_array().

<?php
$allowedIds = [1, 2, 3];
$inputId = (int)$_POST['id']; // Явно приводим к int
if (in_array($inputId, $allowedIds, true)) {
    echo "Д                

Блоги

Рекомендаций по книгам