为了强制执行max_execution_time限制,PHP必须跟踪特定脚本所使用的CPU时间。

是否有一种方法可以在脚本中访问它?我希望在测试中包含一些关于实际PHP中消耗了多少CPU的日志记录(当脚本等待数据库时,时间不会增加)。

我用的是Linux机顶盒。


当前回答

当PHP中有闭包功能时,为什么我们不从中获益呢?

function startTime(){
    $startTime = microtime(true);
    return function () use ($startTime){
        return microtime(true) - $startTime;
    };
}

现在,在上述函数的帮助下,我们可以像这样跟踪时间

$stopTime = startTime();
//some code block or line
$elapsedTime = $stopTime();

每次调用startTime函数都会启动一个单独的时间跟踪器。所以你可以想启动多少就启动多少,也可以在任何你想要的地方停止它们。

其他回答

作为一种替代方法,你可以把这一行放在你的代码块中,并检查php日志,对于非常慢的函数,它非常有用:

trigger_error("Task done at ". strftime('%H:%m:%S', time()), E_USER_NOTICE); 

严肃的调试使用XDebug + Cachegrind,请参见https://blog.nexcess.net/2011/01/29/diagnosing-slow-php-execution-with-xdebug-and-kcachegrind/

要显示分钟和秒,您可以使用:

    $startTime = microtime(true);
    $endTime = microtime(true);
    $diff = round($endTime - $startTime);
    $minutes = floor($diff / 60); //only minutes
    $seconds = $diff % 60;//remaining seconds, using modulo operator
    echo "script execution time: minutes:$minutes, seconds:$seconds"; //value in seconds
<?php
// Randomize sleeping time
usleep(mt_rand(100, 10000));

// As of PHP 5.4.0, REQUEST_TIME_FLOAT is available in the $_SERVER superglobal array.
// It contains the timestamp of the start of the request with microsecond precision.
$time = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];

echo "Did nothing in $time seconds\n";
?>

我创建了一个ExecutionTime类的phihag答案,你可以使用开箱即用:

class ExecutionTime
{
     private $startTime;
     private $endTime;

     public function start(){
         $this->startTime = getrusage();
     }

     public function end(){
         $this->endTime = getrusage();
     }

     private function runTime($ru, $rus, $index) {
         return ($ru["ru_$index.tv_sec"]*1000 + intval($ru["ru_$index.tv_usec"]/1000))
     -  ($rus["ru_$index.tv_sec"]*1000 + intval($rus["ru_$index.tv_usec"]/1000));
     }    

     public function __toString(){
         return "This process used " . $this->runTime($this->endTime, $this->startTime, "utime") .
        " ms for its computations\nIt spent " . $this->runTime($this->endTime, $this->startTime, "stime") .
        " ms in system calls\n";
     }
 }

用法:

$executionTime = new ExecutionTime();
$executionTime->start();
// code
$executionTime->end();
echo $executionTime;

注意:在PHP 5中,getrusage函数只适用于类unix系统。从PHP 7开始,它也可以在Windows上工作。

我写了一个检查剩余执行时间的函数。

警告:执行时间计数在Windows和Linux平台上是不同的。

/**
 * Check if more that `$miliseconds` ms remains
 * to error `PHP Fatal error:  Maximum execution time exceeded`
 * 
 * @param int $miliseconds
 * @return bool
 */
function isRemainingMaxExecutionTimeBiggerThan($miliseconds = 5000) {
    $max_execution_time = ini_get('max_execution_time');
    if ($max_execution_time === 0) {
        // No script time limitation
        return true;
    }
    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
        // On Windows: The real time is measured.
        $spendMiliseconds = (microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"]) * 1000;
    } else {
        // On Linux: Any time spent on activity that happens outside the execution
        //           of the script such as system calls using system(), stream operations
        //           database queries, etc. is not included.
        //           @see http://php.net/manual/en/function.set-time-limit.php
        $resourceUsages = getrusage();
        $spendMiliseconds = $resourceUsages['ru_utime.tv_sec'] * 1000 + $resourceUsages['ru_utime.tv_usec'] / 1000;
    }
    $remainingMiliseconds = $max_execution_time * 1000 - $spendMiliseconds;
    return ($remainingMiliseconds >= $miliseconds);
}

使用:

while (true) {
    // so something

    if (!isRemainingMaxExecutionTimeBiggerThan(5000)) {
        // Time to die.
        // Safely close DB and done the iteration.
    }
}