Каждый PHP-разработчик пережил этот момент. Код отлично работает на локальном хосте. Вы переходите к производству. Что-то тихо ломается — ни ошибок, ни журнала, просто неправильный вывод и несколько потерянных часов на отслеживание проблемы.
Это не ошибки из учебника. Они взяты из реальных проектов PHP — систем входа в систему, магазинов электронной коммерции, панелей администратора, школьных порталов и API. Ошибки, которые проходят проверку кода, потому что на первый взгляд выглядят нормально.
Это Часть 2 , охватывающая ошибки с №11 по №20. Каждый из них включает неработающий код, правильное исправление и четкое объяснение того, что на самом деле идет не так на работающем сервере.
Ошибка № 11 — бесконечный цикл из-за отсутствующего приращения счетчика.
// ❌ Wrong
$i = 0;
while($i < 10) {
echo $i;
// forgot $i++
}
// ✅ Correct
$i = 0;
while($i < 10) {
echo $i;
$i++;
}
Забытый $i++ полностью блокирует процесс PHP. На общем сервере это приведет к отключению приложения для всех пользователей до тех пор, пока не истечет время ожидания запроса. Если цикл также запрашивает базу данных, вы запускаете тысячи повторяющихся запросов. Одна недостающая строка — огромные последствия.
Ошибка № 12 — сбой сравнения строк из-за регистра букв.
// ❌ Wrong — "admin" or "ADMIN" won't match
if($_POST['role'] == "Admin") { ... }
// ✅ Correct
if(strtolower($_POST['role']) == "admin") { ... }
По умолчанию сравнения строк PHP чувствительны к регистру. Пользователи отправляют "admin" , "Admin" или "ADMIN" — все это разные строки в PHP. Если ваш контроль доступа зависит от этого сравнения, некоторым пользователям будет ошибочно отказано или неверно предоставлен доступ. Перед сравнением выполните нормализацию с помощью strtolower() и сохраните значения в нижнем регистре в базе данных.
Ошибка № 13 — приведение целых чисел, обрезающее десятичные дроби при делении.
// ❌ Wrong
$result = 5 / 2;
echo (int)$result; // Output: 2 (not 2.5)
// ✅ Correct
echo $result; // 2.5
echo number_format($result, 2); // 2.50
PHP возвращает 2.5 вместо 5 / 2 — это правильно. Но (int) усекается, а не округляется. Таким образом, 2.9 становится 2 , 9.99 становится 9 . В биллинговых системах, расчетах GST или итоговых суммах корзин это приводит к реальным финансовым ошибкам в каждом счете. Используйте round() для округления и number_format() для вывода на экран.
Ошибка № 14 — декодирование JSON возвращает объект вместо массива.
// ❌ Wrong
$data = json_decode($jsonString);
echo $data['name']; // Fatal error: Cannot use object as array
// ✅ Correct
$data = json_decode($jsonString, true);
echo $data['name']; // Works
// Also add error checking
if(json_last_error() !== JSON_ERROR_NONE) {
error_log('JSON decode failed: ' . json_last_error_msg());
}
По умолчанию json_decode() возвращает объект stdClass . Вам нужна нотация -> для объектов и [] для массивов — их смешивание приводит к фатальной ошибке. Передайте true в качестве второго аргумента, чтобы всегда получать ассоциативный массив. Ответы API также могут быть искажены, поэтому всегда проверяйте json_last_error() после декодирования.
Ошибка № 15 — печать пользовательского ввода непосредственно в HTML (уязвимость XSS).
// ❌ Wrong
echo "Hello " . $_GET['name'];
// ✅ Correct
echo "Hello " . htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');
Отображение $_GET['name'] без экранирования означает, что злоумышленник может внедрить тег <script> на вашу страницу. Этот сценарий выполняется в браузере каждого посетителя. URL-адрес типа ?name=<script>alert(document.cookie)</script> — это все, что нужно.
htmlspecialchars() преобразует < , > , " , ' и & в безопасные объекты HTML. Всегда используйте ENT_QUOTES и 'UTF-8' . Это не подлежит обсуждению — никогда не печатайте пользовательский ввод в HTML без него.
Ошибка № 16 — Относительные пути включения ломаются на разных серверах.
// ❌ Wrong — works locally, breaks on server
include("includes/header.php");
// ✅ Correct — always works
include(__DIR__ . "/includes/header.php");
Относительные пути разрешаются на основе текущего рабочего каталога, а не собственного каталога файла. На локальном хосте с XAMPP это может сработать. На работающем сервере Apache или Nginx с другими настройками виртуального хоста тот же путь завершается автоматически с ошибкой «Нет такого файла или каталога».
__DIR__ — это магическая константа PHP, которая всегда возвращает абсолютный путь к каталогу текущего файла, независимо от того, откуда вызывается сценарий.
Ошибка № 17 — повторяющиеся записи базы данных при повторной отправке формы.
// ❌ Wrong — direct insert, no duplicate check
$sql = "INSERT INTO users (email) VALUES ('$email')";
// ✅ Correct — check first, then insert
$stmt = $pdo->prepare("SELECT id FROM users WHERE email = ?");
$stmt->execute([$email]);
if(!$stmt->fetch()) {
$insert = $pdo->prepare("INSERT INTO users (email) VALUES (?)");
$insert->execute([$email]);
}
Две реальные причины: пользователи дважды нажимают кнопку отправки при медленном соединении и браузеры повторно отправляют данные POST при обновлении страницы. Для исправления требуется два уровня — проверка на стороне PHP перед вставкой, плюс ограничение уровня базы данных UNIQUE , чтобы одновременные запросы не могли проскользнуть одновременно:
ALTER TABLE users ADD UNIQUE (email);
После успешной отправки всегда перенаправляйте: header("Location: success.php"); exit;
Ошибка № 18 — array_merge сброс цифровых клавиш.
$a = [1 => "one", 2 => "two"];
$b = [2 => "TWO", 3 => "three"];
// ❌ Wrong — keys reset to 0, 1, 2, 3
print_r(array_merge($a, $b));
// ✅ Correct — preserve keys (left side wins on conflict)
$result = $a + $b;
// ✅ Or — right side overwrites left on conflict
$result = array_replace($a, $b);
array_merge() переиндексирует все числовые ключи с нуля. Если вы объединяете массивы конфигурации или работаете с результатами базы данных, связанными с идентификаторами, это автоматически уничтожает вашу структуру ключей без каких-либо предупреждений или ошибок. Оператор + сохраняет ключи; array_replace() сохраняет ключи и позволяет правильному массиву побеждать в конфликтах.
Ошибка № 19 — в рабочей среде не настроено ведение журнала ошибок.
// ❌ Wrong — errors disappear completely
// php.ini: display_errors = Off
// (no error_log path configured)
// ✅ Correct
// Development:
ini_set('display_errors', 1);
error_reporting(E_ALL);
// Production:
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', '/var/log/php/error.log');
error_reporting(E_ALL);
Отключение display_errors в рабочей среде является правильным — вы не хотите, чтобы трассировки стека были видны пользователям или злоумышленникам. Но многие разработчики на этом останавливаются. Результат: ошибки происходят незаметно, пользователи жалуются, а у вас нет сведений о том, что и когда сломалось. Всегда настраивайте log_errors и указывайте путь error_log . Такие инструменты, как Sentry или Bugsnag, выдают оповещения в режиме реального времени с полной трассировкой стека — профессиональный стандарт для мониторинга производства.
Ошибка № 20 — foreach не изменяет исходный массив.
// ❌ Wrong — only modifies a copy
$prices = [100, 200, 300];
foreach($prices as $price) {
$price = $price * 1.18;
}
print_r($prices); // Still [100, 200, 300]
// ✅ Correct — reference with &
foreach($prices as &$price) {
$price = $price * 1.18;
}
unset($price); // ⚠️ Critical — must unset after the loop
print_r($prices); // [118, 236, 354]
// ✅ Alternative — array_map (no reference needed)
$prices = array_map(fn($p) => $p * 1.18, $prices);
foreach по умолчанию работает с копией каждого элемента. & делает ссылку на фактический элемент массива. unset($price) после цикла не является обязательным — после цикла $price по-прежнему указывает на последний элемент. Любое последующее использование этого имени переменной в той же области незаметно испортит ваш массив. Эта ошибка может жить в кодовой базе месяцами, прежде чем кто-нибудь ее заметит.
Краткий справочник
| Ошибка | Проблема | Риск |
|---|---|---|
| #11 | Бесконечный цикл | Сбой сервера |
| #12 | Сравнение с учетом регистра | Сбой контроля доступа |
| #13 | Десятичное число усекается до целого числа | Финансовая ошибка |
| #14 | JSON возвращает объект, а не массив | Неустранимая ошибка |
| #15 | XSS-уязвимость | Нарушение безопасности |
| #16 | Включить разрывы путей на сервере | Неустранимая ошибка включения |
| #17 | Дублирующиеся записи базы данных | Повреждение данных |
| #18 | Ключи массива сбрасываются после слияния | Потеряна ключевая структура |
| #19 | Ошибки, невидимые в производстве | Тихие неудачи |
| #20 | Исходный массив не изменен | Обработаны неправильные данные |
Что общего у этих ошибок
Большинство из них не выдают ошибок . Прощающая природа PHP означает, что код выполняется, но он просто не делает правильных действий. Никакого предупреждения. Никакого сбоя. Просто молчаливое неправильное поведение, на отслеживание которого уходят часы. Именно это делает их опасными в производстве.
Разработчики, которые обнаруживают такие ошибки раньше, — это те, кто уже обжигался ими раньше или кто изучал реальные ошибки, прежде чем обжечься. Знания синтаксиса недостаточно: понимание режимов сбоя — это то, что отличает младший код от готового к эксплуатации кода.
Прочитайте полную статью с подробными пояснениями:
👉 10 ошибок PHP, которые ломают реальные проекты — и как их исправить (часть 2)
2
Часть 3 посвящена ошибкам №21–30 — обработке сеансов, безопасности загрузки файлов, ошибкам даты/часового пояса и ошибкам подключения к базе данных.
Комментарии (0)
Пока нет комментариев — будьте первым.