Действия с идентификатором в запросах PHP
Основные подходы к обработке id
Наиболее эффективное решение: комбинация фильтрации и подготовленных запросов
Для безопасной обработки параметра id в PHP рекомендуется использовать функцию filter_input с фильтром FILTER_VALIDATE_INT и подготовленные запросы PDO или MySQLi. Это защищает от SQL инъекций и некорректных данных.
<?
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
if ($id === false || $id === null) {
http_response_code(400);
echo json_encode(['error' => 'Invalid or missing id']);
exit;
}
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->prepare('SELECT * FROM articles WHERE id = :id');
$stmt->execute([':id' => $id]);
$article = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$article) {
http_response_code(404);
echo json_encode(['error' => 'Article not found']);
exit;
}
echo json_encode($article);
?>
Этот подход гарантирует, что в запрос попадет только целое число, и все строки экранированы через prepared statement.
Как получить id из URL без дополнительных проверок?
Простой способ - использовать суперглобальный массив $_GET напрямую:
$id = $_GET['id'];Однако при отсутствии параметра возникнет warning, а значение может быть произвольной строкой.
Проблемы:
- Ошибка уровня E_WARNING при отсутствующем ключе.
- SQL инъекция при прямом использовании в запросе.
- XSS уязвимость при выводе без экранирования.
Как проверить существование и тип id с помощью isset и is_numeric?
Можно использовать комбинацию isset и is_numeric:
if (isset($_GET['id']) && is_numeric($_GET['id'])) {
$id = $_GET['id'];
} else {
// обработка ошибки
}Этот метод отсекает случаи, когда id отсутствует или не является числом (включая числа в строковом формате).
Проблемы:
- is_numeric допускает шестнадцатеричные строки (0x1A) и числа с плавающей точкой, что может быть нежелательно.
- id все еще может содержать пробелы или другие символы, если число представлено строкой.
Как преобразовать id в целое число с помощью intval?
Приведение к целому числу отбрасывает все нечисловые символы:
$id = intval($_GET['id'] ?? 0);Если id не передан, по умолчанию используется 0. Это позволяет избежать warning, но может привести к выполнению операций с id=0.
Проблемы:
- Потеря информации: строка 'abc123' станет 0, хотя пользователь мог ошибиться.
- Не проверяется факт наличия id, что может скрыть ошибку.
- 0 может быть валидным id в некоторых системах, что затрудняет отладку.
Как использовать регулярное выражение для проверки id?
Регулярные выражения позволяют задать точный формат id, например, только цифры:
if (preg_match('/^\d+$/', $_GET['id'])) {
$id = $_GET['id'];
}Подходит для идентификаторов, состоящих только из цифр.
Проблемы:
- Избыточная сложность для простых чисел.
- Не учитывает большие числа, выходящие за границы int.
Как обработать id из POST запроса?
Для модификации данных id часто передается через POST:
$id = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT);Аналогично GET, но источник INPUT_POST.
Проблемы:
- Необходимо различать методы запроса (GET для чтения, POST для создания/обновления).
Как обработать массив id (несколько идентификаторов)?
Иногда id передается как список через запятую или массив в URL:
// URL: /action.php?id=1,2,3
$ids = array_map('intval', explode(',', $_GET['id']));
// Или через множественные параметры: /action.php?id[]=1&id[]=2
$ids = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT, ['flags' => FILTER_REQUIRE_ARRAY]);Это удобно для массовых операций.
Проблемы:
- Необходимость дополнительной валидации каждого элемента.
- Возможность SQL инъекции при неправильной обработке массива.
Как использовать filter_var для уже полученного значения?
Можно сначала проверить isset, а затем применить filter_var:
$id = isset($_GET['id']) ? filter_var($_GET['id'], FILTER_VALIDATE_INT) : null;Похоже на filter_input, но не использует второй параметр ввода.
Проблемы:
- Не проверяет наличие ключа в источнике, если он не задан.
Как обработать id в RESTful маршрутах (ЧПУ)?
При использовании чистых URL, id извлекается из сегмента пути:
// URL: /articles/123
$url = $_SERVER['REQUEST_URI'];
preg_match('/\/articles\/(\d+)/', $url, $matches);
$id = $matches[1] ?? null;Это требует парсинга URL.
Проблемы:
- Необходимость написания роутера.
- Сложность с разными методами.
Расширенные примеры использования параметра id
Пример 1: Полный RESTful скрипт с PDO и валидацией
<?
header('Content-Type: application/json; charset=utf-8');
$method = $_SERVER['REQUEST_METHOD'];
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
if ($id === false || $id === null) {
http_response_code(400);
echo json_encode(['error' => 'Неверный или отсутствующий id']);
exit;
}
try {
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'user', 'pass', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
if ($method === 'GET') {
$stmt = $pdo->prepare('SELECT * FROM articles WHERE id = :id');
$stmt->execute([':id' => $id]);
$article = $stmt->fetch();
if (!$article) {
http_response_code(404);
echo json_encode(['error' => 'Статья не найдена']);
exit;
}
echo json_encode($article);
} elseif ($method === 'DELETE') {
$stmt = $pdo->prepare('DELETE FROM articles WHERE id = :id');
$stmt->execute([':id' => $id]);
if ($stmt->rowCount() === 0) {
http_response_code(404);
echo json_encode(['error' => 'Статья не найдена']);
exit;
}
echo json_encode(['success' => true, 'deleted_id' => $id]);
} else {
http_response_code(405);
echo json_encode(['error' => 'Метод не поддерживается']);
}
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => 'Внутренняя ошибка сервера']);
}
?>
Результат выполнения GET запроса к /action.php?id=1:
{
"id": 1,
"title": "Основы PHP",
"content": "PHP - скриптовый язык..."
}
Пример 2: Извлечение id из пути с помощью parse_url
$url = $_SERVER['REQUEST_URI'];
$path = parse_url($url, PHP_URL_PATH);
// Ожидаем формат /api/articles/123
if (preg_match('#/api/articles/(\d+)#', $path, $matches)) {
$id = (int) $matches[1];
} else {
// Ошибка маршрута
http_response_code(404);
exit;
}
Пример 3: Обработка массива id через GET с разделителем запятая
$idString = filter_input(INPUT_GET, 'ids', FILTER_SANITIZE_STRING);
if ($idString) {
$ids = array_map('intval', explode(',', $idString));
$ids = array_filter($ids, fn($v) => $v > 0); // удаляем нули
if (empty($ids)) {
http_response_code(400);
echo json_encode(['error' => 'Нет корректных id']);
exit;
}
// Строим запрос с плейсхолдерами
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$stmt = $pdo->prepare("DELETE FROM articles WHERE id IN ($placeholders)");
$stmt->execute($ids);
echo json_encode(['deleted' => $stmt->rowCount()]);
}
Результат для /action.php?ids=1,2,3:
{"deleted": 3}
Пример 4: Валидация UUID в качестве id
function validateUuid(string $uuid): bool {
return preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $uuid) === 1;
}
$id = $_GET['uuid'] ?? '';
if (!validateUuid($id)) {
http_response_code(400);
echo json_encode(['error' => 'Неверный формат UUID']);
exit;
}
// Использование в запросе
$stmt = $pdo->prepare('SELECT * FROM users WHERE uuid = :uuid');
$stmt->execute([':uuid' => $id]);
Пример 5: Обработка id с учетом прав доступа (сессия)
session_start();
if (!isset($_SESSION['user_id'])) {
http_response_code(401);
exit;
}
$targetId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
// Пользователь может редактировать только свои записи
$stmt = $pdo->prepare('SELECT user_id FROM articles WHERE id = :id');
$stmt->execute([':id' => $targetId]);
$article = $stmt->fetch();
if (!$article || $article['user_id'] !== $_SESSION['user_id']) {
http_response_code(403);
echo json_encode(['error' => 'Доступ запрещен']);
exit;
}
// продолжение операции
Пример 6: Использование filter_input_array для всех параметров
$args = [
'id' => FILTER_VALIDATE_INT,
'action' => FILTER_SANITIZE_STRING
];
$inputs = filter_input_array(INPUT_GET, $args);
if ($inputs === null || $inputs['id'] === false) {
// ошибка валидации
}
$id = $inputs['id'];
$action = $inputs['action'];
Результат filter_input_array для URL ?id=123&action=view:
Array
(
[id] => 123
[action] => "view"
)