Avatar ·

Ошибка "Maximum execution time of 30 seconds exceeded" при обработке JSON в PHP

Привет всем! Я новичок в PHP и столкнулся с проблемой. Пишу скрипт, который загружает большой JSON-файл (около 50 МБ) с данными о товарах, и обрабатывает его. Когда запускаю на локальном сервере (OpenServer), через 30 секунд получаю ошибку: "Fatal error: Maximum execution time of 30 seconds exceeded". Вот мой код: ```php ``` Я слышал, что можно увеличить время выполнения через php.ini, но это не помогает на хостинге. Может, есть другой способ? JSON мне нужен полностью, пропускать данные нельзя. Подскажите, как решить проблему без увеличения лимита времени? Или как правильно увеличить время, если это единственный вариант? Заранее спасибо! <<>>

Проблема и её корень

Ошибка 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(). Оптимизация базы данных (индексы, пакетная вставка) может сократить время в разы.