adds ability to define an idle timeout
This commit is contained in:
parent
b788094e63
commit
b922ba22e5
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Process\Exception;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* Exception that is thrown when a process times out.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class ProcessTimedOutException extends RuntimeException
|
||||
{
|
||||
const TYPE_GENERAL = 1;
|
||||
const TYPE_IDLE = 2;
|
||||
|
||||
private $process;
|
||||
private $timeoutType;
|
||||
|
||||
public function __construct(Process $process, $timeoutType)
|
||||
{
|
||||
$this->process = $process;
|
||||
$this->timeoutType = $timeoutType;
|
||||
|
||||
parent::__construct(sprintf(
|
||||
'The process "%s" exceeded the timeout of %s seconds.',
|
||||
$process->getCommandLine(),
|
||||
$this->getExceededTimeout()
|
||||
));
|
||||
}
|
||||
|
||||
public function getProcess()
|
||||
{
|
||||
return $this->process;
|
||||
}
|
||||
|
||||
public function isGeneralTimeout()
|
||||
{
|
||||
return $this->timeoutType === self::TYPE_GENERAL;
|
||||
}
|
||||
|
||||
public function isIdleTimeout()
|
||||
{
|
||||
return $this->timeoutType === self::TYPE_IDLE;
|
||||
}
|
||||
|
||||
public function getExceededTimeout()
|
||||
{
|
||||
switch ($this->timeoutType) {
|
||||
case self::TYPE_GENERAL:
|
||||
return $this->process->getTimeout();
|
||||
|
||||
case self::TYPE_IDLE:
|
||||
return $this->process->getIdleTimeout();
|
||||
|
||||
default:
|
||||
throw new \LogicException(sprintf('Unknown timeout type "%d".', $this->timeoutType));
|
||||
}
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Process;
|
||||
|
||||
use Symfony\Component\Process\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Process\Exception\LogicException;
|
||||
use Symfony\Component\Process\Exception\ProcessTimedOutException;
|
||||
use Symfony\Component\Process\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
@ -44,7 +45,9 @@ class Process
|
||||
private $env;
|
||||
private $stdin;
|
||||
private $starttime;
|
||||
private $lastOutputTime;
|
||||
private $timeout;
|
||||
private $idleTimeout;
|
||||
private $options;
|
||||
private $exitcode;
|
||||
private $fallbackExitcode;
|
||||
@ -231,7 +234,7 @@ class Process
|
||||
throw new RuntimeException('Process is already running');
|
||||
}
|
||||
|
||||
$this->starttime = microtime(true);
|
||||
$this->starttime = $this->lastOutputTime = microtime(true);
|
||||
$this->stdout = '';
|
||||
$this->stderr = '';
|
||||
$this->incrementalOutputOffset = 0;
|
||||
@ -795,6 +798,7 @@ class Process
|
||||
*/
|
||||
public function addOutput($line)
|
||||
{
|
||||
$this->lastOutputTime = microtime(true);
|
||||
$this->stdout .= $line;
|
||||
}
|
||||
|
||||
@ -805,6 +809,7 @@ class Process
|
||||
*/
|
||||
public function addErrorOutput($line)
|
||||
{
|
||||
$this->lastOutputTime = microtime(true);
|
||||
$this->stderr .= $line;
|
||||
}
|
||||
|
||||
@ -835,19 +840,29 @@ class Process
|
||||
/**
|
||||
* Gets the process timeout.
|
||||
*
|
||||
* @return integer|null The timeout in seconds or null if it's disabled
|
||||
* @return float|null The timeout in seconds or null if it's disabled
|
||||
*/
|
||||
public function getTimeout()
|
||||
{
|
||||
return $this->timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the process idle timeout.
|
||||
*
|
||||
* @return float|null
|
||||
*/
|
||||
public function getIdleTimeout()
|
||||
{
|
||||
return $this->idleTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the process timeout.
|
||||
*
|
||||
* To disable the timeout, set this value to null.
|
||||
*
|
||||
* @param float|null $timeout The timeout in seconds
|
||||
* @param integer|float|null $timeout The timeout in seconds
|
||||
*
|
||||
* @return self The current Process instance
|
||||
*
|
||||
@ -855,19 +870,23 @@ class Process
|
||||
*/
|
||||
public function setTimeout($timeout)
|
||||
{
|
||||
if (null === $timeout) {
|
||||
$this->timeout = null;
|
||||
$this->timeout = $this->validateTimeout($timeout);
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
$timeout = (float) $timeout;
|
||||
|
||||
if ($timeout < 0) {
|
||||
throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.');
|
||||
}
|
||||
|
||||
$this->timeout = $timeout;
|
||||
/**
|
||||
* Sets the process idle timeout.
|
||||
*
|
||||
* @param integer|float|null $timeout
|
||||
*
|
||||
* @return self The current Process instance.
|
||||
*
|
||||
* @throws InvalidArgumentException if the timeout is negative
|
||||
*/
|
||||
public function setIdleTimeout($timeout)
|
||||
{
|
||||
$this->idleTimeout = $this->validateTimeout($timeout);
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -1078,7 +1097,13 @@ class Process
|
||||
if (0 < $this->timeout && $this->timeout < microtime(true) - $this->starttime) {
|
||||
$this->stop(0);
|
||||
|
||||
throw new RuntimeException('The process timed-out.');
|
||||
throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_GENERAL);
|
||||
}
|
||||
|
||||
if (0 < $this->idleTimeout && $this->idleTimeout < microtime(true) - $this->lastOutputTime) {
|
||||
$this->stop(0);
|
||||
|
||||
throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1253,4 +1278,26 @@ class Process
|
||||
// stream_select returns false when the `select` system call is interrupted by an incoming signal
|
||||
return isset($lastError['message']) && false !== stripos($lastError['message'], 'interrupted system call');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and returns the filtered timeout.
|
||||
*
|
||||
* @param integer|float|null $timeout
|
||||
*
|
||||
* @return float|null
|
||||
*/
|
||||
private function validateTimeout($timeout)
|
||||
{
|
||||
if (null === $timeout) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$timeout = (float) $timeout;
|
||||
|
||||
if ($timeout < 0) {
|
||||
throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.');
|
||||
}
|
||||
|
||||
return $timeout;
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Symfony\Component\Process\Tests;
|
||||
|
||||
use Symfony\Component\Process\Exception\ProcessTimedOutException;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Symfony\Component\Process\Exception\RuntimeException;
|
||||
|
||||
@ -429,6 +430,45 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertLessThan($timeout + $precision, $duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group idle-timeout
|
||||
*/
|
||||
public function testIdleTimeout()
|
||||
{
|
||||
$process = $this->getProcess('sleep 3');
|
||||
$process->setTimeout(10);
|
||||
$process->setIdleTimeout(1);
|
||||
|
||||
try {
|
||||
$process->run();
|
||||
|
||||
$this->fail('A timeout exception was expected.');
|
||||
} catch (ProcessTimedOutException $ex) {
|
||||
$this->assertTrue($ex->isIdleTimeout());
|
||||
$this->assertFalse($ex->isGeneralTimeout());
|
||||
$this->assertEquals(1.0, $ex->getExceededTimeout());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @group idle-timeout
|
||||
*/
|
||||
public function testIdleTimeoutNotExceededWhenOutputIsSent()
|
||||
{
|
||||
$process = $this->getProcess('echo "foo"; sleep 1; echo "foo"; sleep 1; echo "foo"; sleep 1; echo "foo"; sleep 5;');
|
||||
$process->setTimeout(5);
|
||||
$process->setIdleTimeout(3);
|
||||
|
||||
try {
|
||||
$process->run();
|
||||
$this->fail('A timeout exception was expected.');
|
||||
} catch (ProcessTimedOutException $ex) {
|
||||
$this->assertTrue($ex->isGeneralTimeout());
|
||||
$this->assertFalse($ex->isIdleTimeout());
|
||||
$this->assertEquals(5.0, $ex->getExceededTimeout());
|
||||
}
|
||||
}
|
||||
|
||||
public function testGetPid()
|
||||
{
|
||||
$process = $this->getProcess('php -r "sleep(1);"');
|
||||
|
Reference in New Issue
Block a user