Разбор методов тестирования главного файла index.php в веб-разработке
Основные подходы к тестированию файла index.php
В статье рассматриваются способы проверки входной страницы PHP-приложения. Файл index.php отвечает за обработку первоначального запроса. Тесты помогают обнаружить синтаксические ошибки, неверные заголовки, некорректное содержимое и проблемы с окружением. Все примеры построены вокруг простого файла index.php, который выводит HTML-разметку или текстовую строку.
Как организовать автоматическую проверку index.php с помощью встроенного сервера и cURL?
Основной способ подходит для регулярного тестирования без ручного открытия браузера. Скрипт test_index.php запускает встроенный сервер PHP, отправляет запрос и сверяет ответ с ожиданиями.
<?php
$port = 8080;
$host = '127.0.0.1';
$docRoot = __DIR__;
$cmd = 'php -S ' . $host . ':' . $port . ' -t ' . escapeshellarg($docRoot) . ' > /dev/null 2>&1 & echo $!';
exec($cmd, $output);
$pid = (int) $output[0];
usleep(300000);
$ch = curl_init('http://' . $host . ':' . $port . '/index.php');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
exec('kill ' . $pid);
if ($httpCode !== 200) {
exit('Ошибка: HTTP-статус ' . $httpCode);
}
if (strpos($response, 'Работает') === false) {
exit('Ошибка: нужная строка не найдена');
}
echo 'Тест успешно завершен';
Test ru index php (тестирование index.php)
Тест успешно завершен
Пошаговое пояснение:
- Команда exec запускает сервер в фоновом режиме и записывает идентификатор процесса в переменную $pid.
- Пауза usleep даёт серверу время для старта.
- Функция curl_init создаёт запрос к файлу index.php.
- Опция CURLOPT_HEADER добавляет HTTP-заголовки к телу ответа, чтобы проверить не только содержимое, но и статус.
- После запроса процесс сервера завершается командой kill.
- Сравнение статуса и тела ответа выявляет отклонения.
Частая проблема: порт уже используется. В такой ситуации сервер не стартует, и $pid содержит идентификатор чужого процесса. Перед запуском следует освободить порт или указать другой параметр. Ещё одна сложность: на Windows команда kill заменяется на taskkill /F /PID.
Как проверить HTTP-заголовки и код ответа вручную?
Для быстрой диагностики достаточно запустить встроенный сервер и выполнить запрос curl с ключом -I.
php -S 127.0.0.1:8000 -t /путь/к/index.php
В другом терминале:
curl -I http://127.0.0.1:8000/index.php
HTTP/1.1 200 OK Host: 127.0.0.1:8000 Date: ... Content-Type: text/html; charset=UTF-8
Цель: проверить заголовки, не дожидаясь загрузки всего содержимого. Используется при предварительном тестировании разметки и редиректов.
Ошибка: если файл index.php вызывает функцию header после вывода текста, сервер отправляет предупреждение и заголовок не изменяется. Способ решения: перенести все вызовы header в начало скрипта.
Как проверить синтаксис файла index.php без запуска веб-сервера?
Ошибка в коде часто обнаруживается только после открытия страницы. Встроенная команда php -l выполняет синтаксический анализ без исполнения.
php -l index.php
No syntax errors detected in index.php
Такой способ применяется в конвейерах непрерывной интеграции перед запуском функциональных тестов. Если в файле есть пропущенная точка с запятой или неверная скобка, результат команды отклоняется от ожидаемого.
Проблема: php -l не выполняет код, поэтому не замечает ошибки времени выполнения, такие как обращение к несуществующей функции. Для их обнаружения нужны другие виды тестов.
Как протестировать POST-запрос к index.php?
Если скрипт обрабатывает формы, то без проверки POST-данных не обойтись. Скрипт с файловым дескриптором stream_context_create отправляет запрос и читает ответ.
<?php
$postData = http_build_query(['name' => 'Тестовый пользователь']);
$context = stream_context_create([
'http' => [
'method' => 'POST',
'header' => 'Content-Type: application/x-www-form-urlencoded',
'content' => $postData,
],
]);
$result = file_get_contents('http://127.0.0.1:8000/index.php', false, $context);
if ($result === false) { echo 'Запрос не выполнен'; } else { echo $result; }
Цель: проверить обработку данных формы и ответ скрипта. Такой подход подойдёт для окружения, где нет cURL.
Распространённая ошибка: директива allow_url_fopen отключена в php.ini. Тогда file_get_contents возвращает false и появляется предупреждение. Проверку нужно выполнять с включённой настройкой или через cURL.
Как включить PHPUnit в процесс тестирования index.php?
PHPUnit предоставляет структуру для написания повторяющихся проверок. Тест класса IndexPageTest запускает встроенный сервер в setUp и останавливает его в tearDown.
<?php
class IndexPageTest extends PHPUnit\Framework\TestCase
{
protected static $pid;
public static function setUpBeforeClass(): void
{
$cmd = 'php -S 127.0.0.1:8081 -t ' . escapeshellarg(__DIR__) . ' > /dev/null 2>&1 & echo $!';
exec($cmd, $output);
self::$pid = (int)$output[0];
usleep(300000);
}
public static function tearDownAfterClass(): void
{
exec('kill ' . self::$pid);
}
public function testHomePageContainsExpectedText(): void
{
$content = file_get_contents('http://127.0.0.1:8081/index.php');
$this->assertStringContainsString('Работает', $content);
}
public function testHomePageReturnsOkStatus(): void
{
$headers = get_headers('http://127.0.0.1:8081/index.php');
$this->assertStringContainsString('200', $headers[0]);
}
}
Цель: интеграция в CI-систему. Каждый тестовый метод изолирован и может проверять отдельную часть ответа.
Типичная ошибка: сервер не останавливается после выполнения тестов. Это приводит к конфликту порта при следующем запуске. Необходимо предусмотреть остановку процесса в tearDownAfterClass.
Как выполнить тестирование маршрутизации в index.php?
Когда index.php обрабатывает разные пути через параметр запроса, полезно использовать router-скрипт. Встроенный сервер PHP поддерживает флаг -S с указанием файла маршрутизации.
php -S 127.0.0.1:8082 router.php
router.php выглядит так:
<?php
if (file_exists(__DIR__ . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH))) {
return false;
}
$_SERVER['SCRIPT_NAME'] = '/index.php';
require 'index.php';
Цель: имитация веб-сервера с поддержкой чистых URL. Используется при разработке REST API или страниц с многоуровневой маршрутизацией.
Ошибка: если файл index.php использует переменную $_GET['route'], то в router.php надо сохранить исходный REQUEST_URI. Иначе маршрут потеряется. Способ: принудительно присвоить значение функцией parse_url, например $_GET['route'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
Расширенная подборка примеров для тестирования index.php
Дополнительные сценарии помогают охватить ситуации, которые не видны при простой проверке. Каждый пример сопровождается кодом и результатом его работы.
Как проверить несколько сценариев работы index.php за один запуск?
Скрипт запускает сервер один раз, а затем выполняет массив запросов с ожидаемыми статусами и фрагментами ответа.
<?php
$host = '127.0.0.1';
$port = 8070;
$docRoot = __DIR__;
exec('php -S ' . $host . ':' . $port . ' -t ' . escapeshellarg($docRoot) . ' > /dev/null 2>&1 & echo $!', $out);
$pid = (int)$out[0];
usleep(500000);
$tests = [
['url' => '/index.php?page=home', 'status' => 200, 'needle' => 'Домашняя'],
['url' => '/index.php?page=about', 'status' => 200, 'needle' => 'О проекте'],
['url' => '/missing.php', 'status' => 404, 'needle' => 'Не найдено'],
];
foreach ($tests as $test) {
$ch = curl_init($host . ':' . $port . $test['url']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($status !== $test['status'] || strpos($body, $test['needle']) === false) {
echo 'Провал: ' . $test['url'] . PHP_EOL;
exit(1);
}
echo 'Успех: ' . $test['url'] . PHP_EOL;
}
exec('kill ' . $pid);
Успех: /index.php?page=home Успех: /index.php?page=about Успех: /missing.php
Такой набор удобен для регрессионного тестирования. При добавлении новой страницы достаточно дописать элемент массива.
Важная деталь: если index.php использует относительные пути, рабочая директория может отличаться. Тогда тест не найдёт файлы подключения. Способ: задать chdir(__DIR__) в начале скрипта.
Как проверить JSON-ответ и заголовок Content-Type?
Для страниц, которые возвращают данные в формате JSON, требуется проверка и структуры, и заголовка.
<?php
$ch = curl_init('http://127.0.0.1:8071/index.php?format=json');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Accept: application/json']);
$response = curl_exec($ch);
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
$decoded = json_decode($response, true);
if (stripos($contentType, 'application/json') === false) {
exit('Неправильный Content-Type');
}
if (!isset($decoded['status'])) {
exit('JSON не содержит поле status');
}
echo 'JSON-ответ корректен';
JSON-ответ корректен
Такой подход применяется при тестировании AJAX-обработчиков и API-методов, размещённых в index.php.
Ошибка: index.php отправляет HTML с предупреждением PHP перед JSON. Тогда json_decode возвращает null. Нужно проверить ошибки в логах и отключить вывод предупреждений.
Как отправить HTTP-запрос без cURL и file_get_contents?
Если нет ни cURL, ни включённого allow_url_fopen, используется сокет. Через stream_socket_client отправляется текстовое HTTP-сообщение.
<?php
$socket = stream_socket_client('tcp://127.0.0.1:8072', $errno, $errstr, 5);
if (!$socket) {
exit($errstr);
}
$request = "GET /index.php HTTP/1.1\r\n";
$request .= "Host: 127.0.0.1\r\n";
$request .= "Connection: close\r\n\r\n";
fwrite($socket, $request);
$response = '';
while (!feof($socket)) {
$response .= fgets($socket);
}
fclose($socket);
$parts = explode("\r\n\r\n", $response, 2);
$headers = $parts[0];
$body = isset($parts[1]) ? $parts[1] : '';
if (strpos($headers, '200 OK') === false) {
exit('Статус не 200');
}
if (strpos($body, 'Работает') === false) {
exit('Контент не совпадает');
}
echo 'Сокет-тест пройден';
Сокет-тест пройден
Этот метод даёт полный контроль над передаваемыми данными и подходит для отладки HTTP-протокола на низком уровне.
Сложность: если сервер использует постоянные соединения, без заголовка Connection: close ответ может не завершиться. Цикл while в этом случае выполняется вечно.
Как проверить работу сессии и куки в index.php?
Куки проверяются через CURLOPT_COOKIEJAR и CURLOPT_COOKIEFILE. Это позволяет сохранять идентификатор сессии между запросами.
<?php
$jarFile = tempnam(sys_get_temp_dir(), 'cookie');
$ch = curl_init('http://127.0.0.1:8073/index.php?session=start');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, $jarFile);
$first = curl_exec($ch);
curl_close($ch);
$ch = curl_init('http://127.0.0.1:8073/index.php?session=check');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, $jarFile);
$second = curl_exec($ch);
curl_close($ch);
unlink($jarFile);
if (strpos($first, 'SID') === false || strpos($second, 'OK') === false) {
exit('Сессия не работает');
}
echo 'Сессия и куки исправны';
Сессия и куки исправны
Случай использования: проверка авторизации, корзины или форм с защитой от CSRF.
Типичная ошибка: если не указать CURLOPT_COOKIEJAR, второй запрос не получит куки, и сессия не восстановится. Нужно обязательно передать один и тот же файл.
Как проверить поведение index.php при одновременных запросах?
Конкурентные запросы выявляют состояния гонки и проблемы с блокировками. curl_multi позволяет отправить несколько запросов параллельно.
<?php
$requests = [];
$mh = curl_multi_init();
for ($i = 0; $i < 5; $i++) {
$ch = curl_init('http://127.0.0.1:8074/index.php?q=' . $i);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_multi_add_handle($mh, $ch);
$requests[] = $ch;
}
$running = null;
do {
curl_multi_exec($mh, $running);
} while ($running > 0);
foreach ($requests as $ch) {
$content = curl_multi_getcontent($ch);
if (strpos($content, 'ОК') === false) {
echo 'Ошибка в одном из ответов';
exit(1);
}
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
curl_multi_close($mh);
echo 'Все параллельные запросы завершились успешно';
Все параллельные запросы завершились успешно
Этот пример используется при нагрузочном тестировании одиночного файла index.php и проверке отсутствия взаимных блокировок.
Опасность: при работе с общей файловой сессией одновременные запросы могут портить данные. Тест показывает необходимость использовать блокировки или отдельные механизмы хранения.