Jednoduchá ochrana běhu funkce před ukončením na max execution time.

25. 9. 2024

Kdo již řešil nějaký import/export dat, nejspíše se setkal s problémem krátkého limitu pro běh scriptu. Jako nejrychlejší řešení se nám nabízí úprava konfigurace PHP nebo úplné vypnutí limitu na začátku scriptu. Ale, značné prodloužení času běhu nebo jeho vypnutí přináší s sebou bezpečnostní riziko. Nezastavitelný script na pozadí může vést k nadměrnému vytěžování výpočetních prostředků.

V případě zpracování úkonů nad iteracemi můžeme jednotlivé průchody časově monitorovat a pokusit se včas legálně ukončit běh před vypršením časového limitu.

// Inicializujeme si základní proměnné s kterými budeme pracovat dále
$maxExecutionTime = (int)ini_get('max_execution_time');
$estimateCycleTime = 0;
$startTime = microtime(true);

// Pro ukázku využijeme “nekonečný” cyklus se simulovaným úkonem trvající 10 sekund
while (true) {
   sleep(10);

   // Vypočítáme aktuální čas celého běhu
   $currentRunTime = microtime(true) - $startTime;

   // Ukončení můžeme provést buď pevně danou konstantou, nebo můžeme měřit čas
   // jednoho průchodu a zkusit využít co nejdelší úsek časového limitu běhu (má to ale problém).
   if ($estimateCycleTime === 0) {
       $estimateCycleTime = $currentRunTime;
   }

   // Provedeme kontrolu zda se blíží čas pro zastavení iterace. Od maximálního
   // limitu odečítám čas jednoho průchodu který se pravděpodobně už nevejde do okna.
   if (($maxExecutionTime - $estimateCycleTime) < $currentRunTime) {
       echo 'Time is end';
       break;
   }
}

Včasné ukončení na základě výpočtu jednoho průchodu je vhodné pro případy kdy máme velké množství průchodů které potřebujeme odbavit v co nejmenším počtu nových spuštění a každá operace v jednom průchodu je zároveň časově podobně náročná. Pokud se jednotlivé průchody časovou náročností liší je nutné k času jednoho průchodu přidat koeficient. Další možností je použít pevně definovaný čas:

$beforeEndTime = 1;

if (($maxExecutionTime - $beforeEndTime) < $currentRunTime) {
    echo 'Time is end';
    break;
}

Pokud po iteraci pokračuje script dále kde například ukončujeme spojení s API endpointem, zavíráme soubor či provádíme jiné operace, tak nesmíme zapomenout tento čas připočítat.

Nejnovější příspěvky