Введение: Почему PHP так коварен для новичков?
PHP остается одним из самых популярных языков для веб-разработки, но его низкий порог входа часто приводит к формированию плохих привычек. Начинающие разработчики, вдохновленные простотой синтаксиса, часто забывают о безопасности, производительности и современных стандартах. В этой статье мы разберем топ ошибок начинающих PHP разработчиков, которые могут превратить ваш код в кошмар для поддержки и уязвимость для атак.
Мы не будем просто перечислять проблемы — вы увидите примеры кода на PHP с ошибками и их исправленные версии. Это поможет вам сразу применять правильные практики.
1. Игнорирование безопасности: SQL-инъекции и XSS
Самая критичная ошибка — доверие пользовательским данным. Начинающие часто вставляют переменные прямо в SQL-запросы или выводят их на страницу без фильтрации.
Ошибка: Конкатенация в SQL запросах
Классический пример уязвимости:
// НЕПРАВИЛЬНО: уязвимость для SQL-инъекции
$id = $_GET['id'];
$query = "SELECT * FROM users WHERE id = $id";
$result = mysqli_query($link, $query);
Злоумышленник может передать ?id=1 OR 1=1 и получить все данные из таблицы.
Решение: Подготовленные запросы (PDO)
Используйте PDO или mysqli с подготовленными выражениями:
// ПРАВИЛЬНО: PDO с подготовленным запросом
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(['id' => $_GET['id']]);
$result = $stmt->fetchAll();
Ошибка: Вывод данных без экранирования (XSS)
Если вы выводите данные из базы или $_GET напрямую, вы рискуете получить XSS-атаку:
// НЕПРАВИЛЬНО: XSS уязвимость
echo "Привет, " . $_GET['name'];
Решение: htmlspecialchars
Всегда экранируйте вывод:
// ПРАВИЛЬНО: экранирование HTML
echo "Привет, " . htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');
2. Использование устаревших конструкций и функций
Многие учебники 10-летней давности учат использовать mysql_* функции или короткие теги. Это не только небезопасно, но и может не работать на современных хостингах.
Ошибка: mysql_* функции
// НЕПРАВИЛЬНО: устаревшее расширение
$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('test', $link);
Это расширение удалено из PHP 7, и код просто упадет с ошибкой.
Решение: Переход на PDO или MySQLi
// ПРАВИЛЬНО: PDO - современный и безопасный подход
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
Ошибка: Короткие теги
// НЕПРАВИЛЬНО: может быть отключено в php.ini
<? echo "Hello"; ?>
Решение: Только полные теги
// ПРАВИЛЬНО: всегда используйте <?php
<?php echo "Hello"; ?>
3. Плохая работа с формами и данными
Начинающие разработчики часто забывают проверять, была ли форма отправлена, и используют $_REQUEST вместо конкретных суперглобальных массивов.
Ошибка: Отсутствие проверки метода отправки
// НЕПРАВИЛЬНО: код выполняется даже при GET запросе
$name = $_POST['name'];
// ... обработка
Решение: Проверка REQUEST_METHOD
// ПРАВИЛЬНО: проверяем, что форма отправлена
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['name'] ?? '';
// ... валидация и обработка
}
Ошибка: Использование $_REQUEST
Суперглобальный массив $_REQUEST объединяет GET, POST и COOKIE. Это опасно, так как злоумышленник может переопределить POST-данные через URL:
// НЕПРАВИЛЬНО: небезопасно
$action = $_REQUEST['action'];
Решение: Явное указание источника
// ПРАВИЛЬНО: используем конкретный массив
$action = $_POST['action'] ?? 'default';
4. Пренебрежение структурированием кода
Когда весь код написан в одном файле index.php — это прямой путь к "спагетти-коду", который невозможно поддерживать.
Ошибка: Вся логика в одном файле
// НЕПРАВИЛЬНО: смешиваем HTML, SQL и логику
<?php
$conn = mysqli_connect(...);
$result = mysqli_query($conn, "SELECT * FROM posts");
while($row = mysqli_fetch_assoc($result)) {
echo "<h2>" . $row['title'] . "</h2>";
echo "<p>" . $row['content'] . "</p>";
}
?>
Решение: Разделение логики и представления
Используйте простой MVC-подход или хотя бы разделите код на функции и шаблоны:
// ПРАВИЛЬНО: выносим логику в функции
function getPosts($pdo) {
$stmt = $pdo->query("SELECT * FROM posts");
return $stmt->fetchAll();
}
$posts = getPosts($pdo);
// В шаблоне (например, template.php):
// <?php foreach ($posts as $post): ?>
// <h2><?= htmlspecialchars($post['title']) ?></h2>
// <p><?=