<?php /* * @package bfNetwork * @copyright Copyright (C) 2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023 Blue Flame Digital Solutions Ltd. All rights reserved. * @license GNU General Public License version 3 or later * * @see https://mySites.guru/ * @see https://www.phil-taylor.com/ * * @author Phil Taylor / Blue Flame Digital Solutions Limited. * * bfNetwork is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * bfNetwork is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package. If not, see http://www.gnu.org/licenses/ * * If you have any questions regarding this code, please contact [email protected] */ /** * @copyright Copyright (c)2010-2014 Nicholas K. Dionysopoulos * @license GNU General Public License version 3, or later * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /** * The Timer class is used to intelligently prevent timeout errors when performing long operations. */ class AcuTimer { /** * Maximum execution time allowance per step. */ private int|float|null $max_exec_time = null; /** * Minimum execution time per step. * * @var int */ private $min_exec_time = null; /** * Timestamp of execution start. * * @var int */ private $start_time = null; /** * Public constructor, creates the timer object and calculates the execution time limits. * * @param array $params The configuration parameters for the timer * * @return AcuTimer */ public function __construct($params = []) { if (! is_array($params)) { $params = []; } $defaultParams = [ 'max_exec_time' => 14, 'min_exec_time' => 1, 'run_time_bias' => 75, ]; $params = array_merge($defaultParams, $params); // Initialize start time $this->start_time = $this->microtime_float(); // Store the minimum execution time $this->min_exec_time = $params['min_exec_time']; // Get configured max time per step and bias $config_max_exec_time = $params['max_exec_time']; $bias = $params['run_time_bias'] / 100; // Get PHP's maximum execution time (our upper limit) if (@function_exists('ini_get')) { $php_max_exec_time = @ini_get('maximum_execution_time'); if ((! is_numeric($php_max_exec_time)) || (0 == $php_max_exec_time)) { // If we have no time limit, set a hard limit of about 10 seconds // (safe for Apache and IIS timeouts, verbose enough for users) $php_max_exec_time = 14; } } else { // If ini_get is not available, use a rough default $php_max_exec_time = 14; } // Apply an arbitrary correction to counter CMS load time $php_max_exec_time = $php_max_exec_time - max($php_max_exec_time * 0.1, 1); // Apply bias $php_max_exec_time = $php_max_exec_time * $bias; $config_max_exec_time = $config_max_exec_time * $bias; // Use the most appropriate time limit value if ($config_max_exec_time > $php_max_exec_time) { $this->max_exec_time = $php_max_exec_time; } else { $this->max_exec_time = $config_max_exec_time; } } /** * Wake-up function to reset internal timer when we get unserialized. */ public function __wakeup() { // Re-initialize start time on wake-up $this->start_time = $this->microtime_float(); } /** * Gets the number of seconds left, before we hit the "must stop" threshold. * * @return float The time left in decimal seconds */ public function getTimeLeft() { return $this->max_exec_time - $this->getRunningTime(); } /** * Gets the time elapsed since object creation/unserialization, effectively how long you have been processing data * since you instantiated AcuTimer. * * @return float The number of elapsed time in decimal seconds */ public function getRunningTime() { return $this->microtime_float() - $this->start_time; } /** * Returns the current timestamp in decimal seconds. * * @return float Current timestamp in decimal seconds */ private function microtime_float() { [$usec, $sec] = explode(' ', microtime()); return (float) $usec + (float) $sec; } /** * Enforce the minimum execution time. Call this at the end of your long processing to make sure that it doesn't * take less time than the minimum execution time. This is used to avoid being blocked by overzealous server * protection solutions. */ public function enforce_min_exec_time() { // Try to get a sane value for PHP's maximum_execution_time INI parameter if (@function_exists('ini_get')) { $php_max_exec = @ini_get('maximum_execution_time'); } else { $php_max_exec = 10; } if (('' == $php_max_exec) || (0 == $php_max_exec)) { $php_max_exec = 10; } // Decrease $php_max_exec time by 500 msec we need (approx.) to tear down // the application, as well as another 500msec added for rounding // error purposes. Also make sure this is never going to be less than 0. $php_max_exec = max($php_max_exec * 1000 - 1000, 0); // Get the "minimum execution time per step" Akeeba Backup configuration variable $minexectime = $this->min_exec_time; if (! is_numeric($minexectime)) { $minexectime = 0; } // Make sure we are not over PHP's time limit! if ($minexectime > $php_max_exec) { $minexectime = $php_max_exec; } // Get current running time $elapsed_time = $this->getRunningTime() * 1000; // Only run a sleep delay if we haven't reached the minexectime execution time if (($minexectime > $elapsed_time) && ($elapsed_time > 0)) { $sleep_msec = $minexectime - $elapsed_time; if (function_exists('usleep')) { usleep(1000 * $sleep_msec); } elseif (function_exists('time_nanosleep')) { $sleep_sec = floor($sleep_msec / 1000); $sleep_nsec = 1_000_000 * ($sleep_msec - ($sleep_sec * 1000)); time_nanosleep($sleep_sec, $sleep_nsec); } elseif (function_exists('time_sleep_until')) { $until_timestamp = time() + $sleep_msec / 1000; time_sleep_until($until_timestamp); } elseif (function_exists('sleep')) { $sleep_sec = ceil($sleep_msec / 1000); sleep($sleep_sec); } } elseif ($elapsed_time > 0) { // No sleep required, even if user configured us to be able to do so. } } /** * Reset the timer. It should only be used in CLI mode! */ public function resetTime() { $this->start_time = $this->microtime_float(); } }