Ошибка "Maximum execution time of 30 seconds exceeded" при обработке JSON в PHP
Проблема и её корень
Ошибка Maximum execution time of 30 seconds exceeded возникает, когда PHP-скрипт выполняется дольше установленного лимита (по умолчанию 30 секунд). В вашем случае — из-за загрузки и обработки большого JSON-файла. Простое увеличение лимита — не лучшее решение, так как на многих хостингах это запрещено, а при росте данных ошибка вернётся.
Правильные решения
1. Увеличение лимита времени (если доступно)
Если на вашем сервере разрешено, используйте функцию set_time_limit() в начале скрипта. Она сбрасывает счётчик выполнения при каждом вызове.
<?php
// Устанавливаем лимит в 0 (без ограничения) или большое значение
set_time_limit(300); // 5 минут
$json = file_get_contents('big_data.json');
$data = json_decode($json, true);
// ... остальной код
?>
2. Потоковая обработка JSON (рекомендуется)
Вместо загрузки всего файла в память используйте генераторы и потоковое чтение. Для больших JSON-массивов отлично подходит библиотека JsonMachine (устанавливается через Composer). Она не загружает весь файл, а читает элементы по одному.
<?php
require 'vendor/autoload.php';
use JsonMachine\JsonMachine;
// Читаем JSON потоково
$items = JsonMachine::fromFile('big_data.json');
foreach ($items as $item) {
$result = processItem($item);
saveToDatabase($result);
// Сбрасываем таймер на каждой итерации
set_time_limit(30);
}
echo "Готово!";
?>
3. Разделение на чанки (chunks) вручную
Если библиотека недоступна, можно читать файл частями и обрабатывать каждую часть отдельно, используя fgets() или stream_get_line().
<?php
$handle = fopen('big_data.json', 'r');
$buffer = '';
$depth = 0;
$inString = false;
while (!feof($handle)) {
$char = fgetc($handle);
$buffer .= $char;
if ($char === '"' && !$inString) {
$inString = true;
} elseif ($char === '"' && $inString) {
$inString = false;
} elseif (!$inString) {
if ($char === '{') $depth++;
if ($char === '}') $depth--;
}
if ($depth === 0 && strlen($buffer) > 1000) {
// Нашли полный объект — обрабатываем
$item = json_decode($buffer, true);
if ($item) {
processItem($item);
saveToDatabase($item);
set_time_limit(30);
}
$buffer = '';
}
}
fclose($handle);
?>
4. Использование очередей задач (для сложных случаев)
Если обработка очень долгая (минуты/часы), лучше использовать фоновые очереди: RabbitMQ, Beanstalkd или Redis с php-resque. Скрипт будет разбит на маленькие задачи, которые выполняются по одной.
Что выбрать?
- Для разовой задачи — потоковая обработка через
JsonMachine. - Для частого использования — очередь задач.
- Увеличение лимита — только как временное решение.
Всегда старайтесь не загружать большие данные в память целиком. Это экономит ресурсы и предотвращает ошибки времени выполнения.
<<Альтернативный подход: оптимизация самого JSON
Если вы контролируете источник данных, можно изменить формат JSON, чтобы ускорить обработку. Например, разбить один большой файл на несколько маленьких (по 1000 записей) и обрабатывать их последовательно.
<?php
// Генерация чанков из большого файла
$chunkSize = 1000;
$items = json_decode(file_get_contents('big_data.json'), true);
$chunks = array_chunk($items, $chunkSize);
foreach ($chunks as $index => $chunk) {
file_put_contents("chunk_{$index}.json", json_encode($chunk));
}
?>
Затем обрабатывайте каждый чанк отдельно:
<?php
for ($i = 0; $i < count($chunks); $i++) {
$data = json_decode(file_get_contents("chunk_{$i}.json"), true);
foreach ($data as $item) {
processItem($item);
saveToDatabase($item);
}
set_time_limit(30); // сбрасываем таймер после каждого чанка
}
?>
Этот метод не требует установки дополнительных библиотек и подходит, если файл уже загружен на сервер. Главный минус — нужно место на диске для хранения чанков.
Также проверьте, не замедляет ли обработку сам код processItem(). Оптимизация базы данных (индексы, пакетная вставка) может сократить время в разы.