Merge branch '2.3'
* 2.3: [Process] Revert change [Process] Fix #8746 : slowness added in unit tests since #8741 [Process] Fix #8742 : Signal-terminated processes are not successful corrected English grammar (s/does not exists/does not exist) [Process] Add more precision to Process::stop timeout [Process] Avoid zombie process in case of unit tests failure [Process] Fix #8739 [Process] Add failing test for #8739 [Process] Fix CS [TwigBridge] removed superflous ; when rendering form_enctype() (closes #8660) Fixed documentation grammar for AuthenticationManagerInterface::authenticate() [Validator] fixed the wrong isAbstract() check against the class (fixed #8589) [TwigBridge] Prevent code extension to display warning Fix internal sub-request creation [FrameworkBundle] made code more generic [Form] Moved auto_initialize option to the BaseType Use strstr instead of strpos Make sure ContextErrorException is loaded during compile time errors Fix empty process argument escaping on Windows Ignore null value in comparison validators Conflicts: src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php src/Symfony/Component/Process/Process.php
This commit is contained in:
commit
a67f5d03d1
@ -137,7 +137,9 @@ class CodeExtension extends \Twig_Extension
|
|||||||
public function fileExcerpt($file, $line)
|
public function fileExcerpt($file, $line)
|
||||||
{
|
{
|
||||||
if (is_readable($file)) {
|
if (is_readable($file)) {
|
||||||
$code = highlight_file($file, true);
|
// highlight_file could throw warnings
|
||||||
|
// see https://bugs.php.net/bug.php?id=25725
|
||||||
|
$code = @highlight_file($file, true);
|
||||||
// remove main code/span tags
|
// remove main code/span tags
|
||||||
$code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code);
|
$code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code);
|
||||||
$content = preg_split('#<br />#', $code);
|
$content = preg_split('#<br />#', $code);
|
||||||
|
@ -23,8 +23,6 @@ class FormEnctypeNode extends SearchAndRenderBlockNode
|
|||||||
{
|
{
|
||||||
parent::compile($compiler);
|
parent::compile($compiler);
|
||||||
|
|
||||||
$compiler->raw(";\n");
|
|
||||||
|
|
||||||
// Uncomment this as soon as the deprecation note should be shown
|
// Uncomment this as soon as the deprecation note should be shown
|
||||||
// $compiler->write('trigger_error(\'The helper form_enctype(form) is deprecated since version 2.3 and will be removed in 3.0. Use form_start(form) instead.\', E_USER_DEPRECATED)');
|
// $compiler->write('trigger_error(\'The helper form_enctype(form) is deprecated since version 2.3 and will be removed in 3.0. Use form_start(form) instead.\', E_USER_DEPRECATED)');
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
namespace Symfony\Bundle\FrameworkBundle\Console;
|
namespace Symfony\Bundle\FrameworkBundle\Console;
|
||||||
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
|
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||||
use Symfony\Component\Console\Application as BaseApplication;
|
use Symfony\Component\Console\Application as BaseApplication;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
@ -78,7 +78,7 @@ class Application extends BaseApplication
|
|||||||
$container = $this->kernel->getContainer();
|
$container = $this->kernel->getContainer();
|
||||||
|
|
||||||
foreach ($this->all() as $command) {
|
foreach ($this->all() as $command) {
|
||||||
if ($command instanceof ContainerAwareCommand) {
|
if ($command instanceof ContainerAwareInterface) {
|
||||||
$command->setContainer($container);
|
$command->setContainer($container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,7 +131,9 @@ class CodeHelper extends Helper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$code = highlight_file($file, true);
|
// highlight_file could throw warnings
|
||||||
|
// see https://bugs.php.net/bug.php?id=25725
|
||||||
|
$code = @highlight_file($file, true);
|
||||||
// remove main code/span tags
|
// remove main code/span tags
|
||||||
$code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code);
|
$code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code);
|
||||||
$content = preg_split('#<br />#', $code);
|
$content = preg_split('#<br />#', $code);
|
||||||
|
@ -177,7 +177,7 @@ class ClassLoader
|
|||||||
$classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className).'.php';
|
$classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className).'.php';
|
||||||
|
|
||||||
foreach ($this->prefixes as $prefix => $dirs) {
|
foreach ($this->prefixes as $prefix => $dirs) {
|
||||||
if (0 === strpos($class, $prefix)) {
|
if ($class === strstr($class, $prefix)) {
|
||||||
foreach ($dirs as $dir) {
|
foreach ($dirs as $dir) {
|
||||||
if (file_exists($dir.DIRECTORY_SEPARATOR.$classPath)) {
|
if (file_exists($dir.DIRECTORY_SEPARATOR.$classPath)) {
|
||||||
return $dir.DIRECTORY_SEPARATOR.$classPath;
|
return $dir.DIRECTORY_SEPARATOR.$classPath;
|
||||||
|
@ -139,6 +139,11 @@ class ErrorHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->displayErrors && error_reporting() & $level && $this->level & $level) {
|
if ($this->displayErrors && error_reporting() & $level && $this->level & $level) {
|
||||||
|
// make sure the ContextErrorException class is loaded (https://bugs.php.net/bug.php?id=65322)
|
||||||
|
if (!class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) {
|
||||||
|
require __DIR__.'/Exception/ContextErrorException.php';
|
||||||
|
}
|
||||||
|
|
||||||
throw new ContextErrorException(sprintf('%s: %s in %s line %d', isset($this->levels[$level]) ? $this->levels[$level] : $level, $message, $file, $line), 0, $level, $file, $line, $context);
|
throw new ContextErrorException(sprintf('%s: %s in %s line %d', isset($this->levels[$level]) ? $this->levels[$level] : $level, $message, $file, $line), 0, $level, $file, $line, $context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,32 @@ use Symfony\Component\Debug\ErrorHandler;
|
|||||||
*/
|
*/
|
||||||
class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
|
public function testCompileTimeError()
|
||||||
|
{
|
||||||
|
// the ContextErrorException must not be loaded for this test to work
|
||||||
|
if (class_exists('Symfony\Component\Debug\Exception\ContextErrorException', false)) {
|
||||||
|
$this->markTestSkipped('The ContextErrorException class is already loaded.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$handler = ErrorHandler::register(E_ALL | E_STRICT);
|
||||||
|
$displayErrors = ini_get('display_errors');
|
||||||
|
ini_set('display_errors', '1');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// trigger compile time error
|
||||||
|
eval(<<<'PHP'
|
||||||
|
class _BaseCompileTimeError { function foo() {} }
|
||||||
|
class _CompileTimeError extends _BaseCompileTimeError { function foo($invalid) {} }
|
||||||
|
PHP
|
||||||
|
);
|
||||||
|
} catch(\Exception $e) {
|
||||||
|
// if an exception is thrown, the test passed
|
||||||
|
}
|
||||||
|
|
||||||
|
ini_set('display_errors', $displayErrors);
|
||||||
|
restore_error_handler();
|
||||||
|
}
|
||||||
|
|
||||||
public function testConstruct()
|
public function testConstruct()
|
||||||
{
|
{
|
||||||
$handler = ErrorHandler::register(3);
|
$handler = ErrorHandler::register(3);
|
||||||
|
@ -187,7 +187,7 @@ class Command
|
|||||||
public function get($label)
|
public function get($label)
|
||||||
{
|
{
|
||||||
if (!isset($this->labels[$label])) {
|
if (!isset($this->labels[$label])) {
|
||||||
throw new \RuntimeException(sprintf('Label "%s" does not exists.', $label));
|
throw new \RuntimeException(sprintf('Label "%s" does not exist.', $label));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->bits[$this->labels[$label]];
|
return $this->bits[$this->labels[$label]];
|
||||||
|
@ -33,6 +33,7 @@ abstract class BaseType extends AbstractType
|
|||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
{
|
{
|
||||||
$builder->setDisabled($options['disabled']);
|
$builder->setDisabled($options['disabled']);
|
||||||
|
$builder->setAutoInitialize($options['auto_initialize']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -112,6 +113,7 @@ abstract class BaseType extends AbstractType
|
|||||||
'label' => null,
|
'label' => null,
|
||||||
'attr' => array(),
|
'attr' => array(),
|
||||||
'translation_domain' => null,
|
'translation_domain' => null,
|
||||||
|
'auto_initialize' => true,
|
||||||
));
|
));
|
||||||
|
|
||||||
$resolver->setAllowedTypes(array(
|
$resolver->setAllowedTypes(array(
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||||
|
|
||||||
use Symfony\Component\Form\ButtonTypeInterface;
|
use Symfony\Component\Form\ButtonTypeInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A form button.
|
* A form button.
|
||||||
@ -35,4 +36,16 @@ class ButtonType extends BaseType implements ButtonTypeInterface
|
|||||||
{
|
{
|
||||||
return 'button';
|
return 'button';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setDefaultOptions(OptionsResolverInterface $resolver)
|
||||||
|
{
|
||||||
|
parent::setDefaultOptions($resolver);
|
||||||
|
|
||||||
|
$resolver->setDefaults(array(
|
||||||
|
'auto_initialize' => false,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,6 @@ class FormType extends BaseType
|
|||||||
->setDataMapper($options['compound'] ? new PropertyPathMapper($this->propertyAccessor) : null)
|
->setDataMapper($options['compound'] ? new PropertyPathMapper($this->propertyAccessor) : null)
|
||||||
->setMethod($options['method'])
|
->setMethod($options['method'])
|
||||||
->setAction($options['action'])
|
->setAction($options['action'])
|
||||||
->setAutoInitialize($options['auto_initialize'])
|
|
||||||
;
|
;
|
||||||
|
|
||||||
if ($options['trim']) {
|
if ($options['trim']) {
|
||||||
@ -188,7 +187,6 @@ class FormType extends BaseType
|
|||||||
// According to RFC 2396 (http://www.ietf.org/rfc/rfc2396.txt)
|
// According to RFC 2396 (http://www.ietf.org/rfc/rfc2396.txt)
|
||||||
// section 4.2., empty URIs are considered same-document references
|
// section 4.2., empty URIs are considered same-document references
|
||||||
'action' => '',
|
'action' => '',
|
||||||
'auto_initialize' => true,
|
|
||||||
));
|
));
|
||||||
|
|
||||||
$resolver->setAllowedTypes(array(
|
$resolver->setAllowedTypes(array(
|
||||||
|
@ -51,4 +51,13 @@ class SubmitTypeTest extends TypeTestCase
|
|||||||
|
|
||||||
$this->assertTrue($button->isClicked());
|
$this->assertTrue($button->isClicked());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSubmitCanBeAddedToForm()
|
||||||
|
{
|
||||||
|
$form = $this->factory
|
||||||
|
->createBuilder('form')
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$this->assertSame($form, $form->add('send', 'submit'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,10 +112,11 @@ class InlineFragmentRenderer extends RoutableFragmentRenderer
|
|||||||
// Sub-request object will point to localhost as client ip and real client ip
|
// Sub-request object will point to localhost as client ip and real client ip
|
||||||
// will be included into trusted header for client ip
|
// will be included into trusted header for client ip
|
||||||
try {
|
try {
|
||||||
$trustedHeaderName = Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP);
|
if ($trustedHeaderName = Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP)) {
|
||||||
$currentXForwardedFor = $request->headers->get($trustedHeaderName, '');
|
$currentXForwardedFor = $request->headers->get($trustedHeaderName, '');
|
||||||
|
|
||||||
$server['HTTP_'.$trustedHeaderName] = ($currentXForwardedFor ? $currentXForwardedFor.', ' : '').$request->getClientIp();
|
$server['HTTP_'.$trustedHeaderName] = ($currentXForwardedFor ? $currentXForwardedFor.', ' : '').$request->getClientIp();
|
||||||
|
}
|
||||||
} catch (\InvalidArgumentException $e) {
|
} catch (\InvalidArgumentException $e) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ interface ProfilerStorageInterface
|
|||||||
/**
|
/**
|
||||||
* Reads data associated with the given token.
|
* Reads data associated with the given token.
|
||||||
*
|
*
|
||||||
* The method returns false if the token does not exists in the storage.
|
* The method returns false if the token does not exist in the storage.
|
||||||
*
|
*
|
||||||
* @param string $token A token
|
* @param string $token A token
|
||||||
*
|
*
|
||||||
|
@ -85,6 +85,26 @@ class InlineFragmentRendererTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals('bar', $response->getContent());
|
$this->assertEquals('bar', $response->getContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testRenderWithTrustedHeaderDisabled()
|
||||||
|
{
|
||||||
|
$trustedHeaderName = Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP);
|
||||||
|
|
||||||
|
Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, '');
|
||||||
|
|
||||||
|
$kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
|
||||||
|
$kernel
|
||||||
|
->expects($this->any())
|
||||||
|
->method('handle')
|
||||||
|
->with(Request::create('/'))
|
||||||
|
;
|
||||||
|
|
||||||
|
$strategy = new InlineFragmentRenderer($kernel);
|
||||||
|
|
||||||
|
$strategy->render('/', Request::create('/'));
|
||||||
|
|
||||||
|
Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, $trustedHeaderName);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \RuntimeException
|
* @expectedException \RuntimeException
|
||||||
*/
|
*/
|
||||||
@ -165,8 +185,11 @@ class InlineFragmentRendererTest extends \PHPUnit_Framework_TestCase
|
|||||||
{
|
{
|
||||||
$expectedSubRequest = Request::create('/');
|
$expectedSubRequest = Request::create('/');
|
||||||
$expectedSubRequest->headers->set('Surrogate-Capability', 'abc="ESI/1.0"');
|
$expectedSubRequest->headers->set('Surrogate-Capability', 'abc="ESI/1.0"');
|
||||||
$expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1'));
|
|
||||||
$expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1');
|
if (Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP)) {
|
||||||
|
$expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1'));
|
||||||
|
$expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1');
|
||||||
|
}
|
||||||
|
|
||||||
$kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
|
$kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
|
||||||
$kernel
|
$kernel
|
||||||
@ -181,6 +204,16 @@ class InlineFragmentRendererTest extends \PHPUnit_Framework_TestCase
|
|||||||
$request->headers->set('Surrogate-Capability', 'abc="ESI/1.0"');
|
$request->headers->set('Surrogate-Capability', 'abc="ESI/1.0"');
|
||||||
$strategy->render('/', $request);
|
$strategy->render('/', $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testESIHeaderIsKeptInSubrequestWithTrustedHeaderDisabled()
|
||||||
|
{
|
||||||
|
$trustedHeaderName = Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP);
|
||||||
|
Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, '');
|
||||||
|
|
||||||
|
$this->testESIHeaderIsKeptInSubrequest();
|
||||||
|
|
||||||
|
Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, $trustedHeaderName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Bar {
|
class Bar {
|
||||||
|
@ -40,6 +40,7 @@ class Process
|
|||||||
// Timeout Precision in seconds.
|
// Timeout Precision in seconds.
|
||||||
const TIMEOUT_PRECISION = 0.2;
|
const TIMEOUT_PRECISION = 0.2;
|
||||||
|
|
||||||
|
private $callback;
|
||||||
private $commandline;
|
private $commandline;
|
||||||
private $cwd;
|
private $cwd;
|
||||||
private $env;
|
private $env;
|
||||||
@ -169,6 +170,7 @@ class Process
|
|||||||
|
|
||||||
public function __clone()
|
public function __clone()
|
||||||
{
|
{
|
||||||
|
$this->callback = null;
|
||||||
$this->exitcode = null;
|
$this->exitcode = null;
|
||||||
$this->fallbackExitcode = null;
|
$this->fallbackExitcode = null;
|
||||||
$this->processInformation = null;
|
$this->processInformation = null;
|
||||||
@ -204,7 +206,7 @@ class Process
|
|||||||
{
|
{
|
||||||
$this->start($callback);
|
$this->start($callback);
|
||||||
|
|
||||||
return $this->wait($callback);
|
return $this->wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -241,7 +243,7 @@ class Process
|
|||||||
$this->stderr = '';
|
$this->stderr = '';
|
||||||
$this->incrementalOutputOffset = 0;
|
$this->incrementalOutputOffset = 0;
|
||||||
$this->incrementalErrorOutputOffset = 0;
|
$this->incrementalErrorOutputOffset = 0;
|
||||||
$callback = $this->buildCallback($callback);
|
$this->callback = $this->buildCallback($callback);
|
||||||
$descriptors = $this->getDescriptors();
|
$descriptors = $this->getDescriptors();
|
||||||
|
|
||||||
$commandline = $this->commandline;
|
$commandline = $this->commandline;
|
||||||
@ -264,75 +266,9 @@ class Process
|
|||||||
stream_set_blocking($pipe, false);
|
stream_set_blocking($pipe, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->tty) {
|
$this->writePipes();
|
||||||
$this->status = self::STATUS_TERMINATED;
|
$this->updateStatus(false);
|
||||||
|
$this->checkTimeout();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null === $this->stdin) {
|
|
||||||
fclose($this->pipes[0]);
|
|
||||||
unset($this->pipes[0]);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$writePipes = array($this->pipes[0]);
|
|
||||||
unset($this->pipes[0]);
|
|
||||||
$stdinLen = strlen($this->stdin);
|
|
||||||
$stdinOffset = 0;
|
|
||||||
|
|
||||||
while ($writePipes) {
|
|
||||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
|
||||||
$this->processFileHandles($callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
$r = $this->pipes;
|
|
||||||
$w = $writePipes;
|
|
||||||
$e = null;
|
|
||||||
|
|
||||||
if (false === $n = @stream_select($r, $w, $e, 0, ceil(static::TIMEOUT_PRECISION * 1E6))) {
|
|
||||||
// if a system call has been interrupted, forget about it, let's try again
|
|
||||||
if ($this->hasSystemCallBeenInterrupted()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// nothing has changed, let's wait until the process is ready
|
|
||||||
if (0 === $n) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($w) {
|
|
||||||
$written = fwrite($writePipes[0], (binary) substr($this->stdin, $stdinOffset), 8192);
|
|
||||||
if (false !== $written) {
|
|
||||||
$stdinOffset += $written;
|
|
||||||
}
|
|
||||||
if ($stdinOffset >= $stdinLen) {
|
|
||||||
fclose($writePipes[0]);
|
|
||||||
$writePipes = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($r as $pipe) {
|
|
||||||
$type = array_search($pipe, $this->pipes);
|
|
||||||
$data = fread($pipe, 8192);
|
|
||||||
if (strlen($data) > 0) {
|
|
||||||
call_user_func($callback, $type == 1 ? self::OUT : self::ERR, $data);
|
|
||||||
}
|
|
||||||
if (false === $data || feof($pipe)) {
|
|
||||||
fclose($pipe);
|
|
||||||
unset($this->pipes[$type]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->checkTimeout();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->updateStatus();
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -378,55 +314,15 @@ class Process
|
|||||||
*/
|
*/
|
||||||
public function wait($callback = null)
|
public function wait($callback = null)
|
||||||
{
|
{
|
||||||
$this->updateStatus();
|
$this->updateStatus(false);
|
||||||
$callback = $this->buildCallback($callback);
|
if (null !== $callback) {
|
||||||
while ($this->pipes || (defined('PHP_WINDOWS_VERSION_BUILD') && $this->fileHandles)) {
|
$this->callback = $this->buildCallback($callback);
|
||||||
if (defined('PHP_WINDOWS_VERSION_BUILD') && $this->fileHandles) {
|
|
||||||
$this->processFileHandles($callback, !$this->pipes);
|
|
||||||
}
|
|
||||||
$this->checkTimeout();
|
|
||||||
|
|
||||||
if ($this->pipes) {
|
|
||||||
$r = $this->pipes;
|
|
||||||
$w = null;
|
|
||||||
$e = null;
|
|
||||||
|
|
||||||
// let's have a look if something changed in streams
|
|
||||||
if (false === $n = @stream_select($r, $w, $e, 0, ceil(static::TIMEOUT_PRECISION * 1E6))) {
|
|
||||||
// if a system call has been interrupted, forget about it, let's try again
|
|
||||||
// otherwise, an error occured, let's reset pipes
|
|
||||||
if (!$this->hasSystemCallBeenInterrupted()) {
|
|
||||||
$this->pipes = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// nothing has changed
|
|
||||||
if (0 === $n) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($r as $pipe) {
|
|
||||||
$type = array_search($pipe, $this->pipes);
|
|
||||||
$data = fread($pipe, 8192);
|
|
||||||
|
|
||||||
if (strlen($data) > 0) {
|
|
||||||
// last exit code is output and caught to work around --enable-sigchild
|
|
||||||
if (3 == $type) {
|
|
||||||
$this->fallbackExitcode = (int) $data;
|
|
||||||
} else {
|
|
||||||
call_user_func($callback, $type == 1 ? self::OUT : self::ERR, $data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (false === $data || feof($pipe)) {
|
|
||||||
fclose($pipe);
|
|
||||||
unset($this->pipes[$type]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$this->updateStatus();
|
while ($this->pipes || (defined('PHP_WINDOWS_VERSION_BUILD') && $this->fileHandles)) {
|
||||||
|
$this->checkTimeout();
|
||||||
|
$this->readPipes(true);
|
||||||
|
}
|
||||||
|
$this->updateStatus(false);
|
||||||
if ($this->processInformation['signaled']) {
|
if ($this->processInformation['signaled']) {
|
||||||
if ($this->isSigchildEnabled()) {
|
if ($this->isSigchildEnabled()) {
|
||||||
throw new RuntimeException('The process has been signaled.');
|
throw new RuntimeException('The process has been signaled.');
|
||||||
@ -441,8 +337,6 @@ class Process
|
|||||||
usleep(1000);
|
usleep(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
$exitcode = proc_close($this->process);
|
|
||||||
|
|
||||||
if ($this->processInformation['signaled']) {
|
if ($this->processInformation['signaled']) {
|
||||||
if ($this->isSigchildEnabled()) {
|
if ($this->isSigchildEnabled()) {
|
||||||
throw new RuntimeException('The process has been signaled.');
|
throw new RuntimeException('The process has been signaled.');
|
||||||
@ -451,12 +345,6 @@ class Process
|
|||||||
throw new RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig']));
|
throw new RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig']));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->exitcode = $this->processInformation['running'] ? $exitcode : $this->processInformation['exitcode'];
|
|
||||||
|
|
||||||
if (-1 == $this->exitcode && null !== $this->fallbackExitcode) {
|
|
||||||
$this->exitcode = $this->fallbackExitcode;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->exitcode;
|
return $this->exitcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,7 +361,7 @@ class Process
|
|||||||
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.');
|
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->updateStatus();
|
$this->updateStatus(false);
|
||||||
|
|
||||||
return $this->isRunning() ? $this->processInformation['pid'] : null;
|
return $this->isRunning() ? $this->processInformation['pid'] : null;
|
||||||
}
|
}
|
||||||
@ -514,7 +402,7 @@ class Process
|
|||||||
*/
|
*/
|
||||||
public function getOutput()
|
public function getOutput()
|
||||||
{
|
{
|
||||||
$this->updateOutput();
|
$this->readPipes(false);
|
||||||
|
|
||||||
return $this->stdout;
|
return $this->stdout;
|
||||||
}
|
}
|
||||||
@ -546,7 +434,7 @@ class Process
|
|||||||
*/
|
*/
|
||||||
public function getErrorOutput()
|
public function getErrorOutput()
|
||||||
{
|
{
|
||||||
$this->updateErrorOutput();
|
$this->readPipes(false);
|
||||||
|
|
||||||
return $this->stderr;
|
return $this->stderr;
|
||||||
}
|
}
|
||||||
@ -585,7 +473,7 @@ class Process
|
|||||||
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method');
|
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->updateStatus();
|
$this->updateStatus(false);
|
||||||
|
|
||||||
return $this->exitcode;
|
return $this->exitcode;
|
||||||
}
|
}
|
||||||
@ -637,7 +525,7 @@ class Process
|
|||||||
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
|
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->updateStatus();
|
$this->updateStatus(false);
|
||||||
|
|
||||||
return $this->processInformation['signaled'];
|
return $this->processInformation['signaled'];
|
||||||
}
|
}
|
||||||
@ -659,7 +547,7 @@ class Process
|
|||||||
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
|
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->updateStatus();
|
$this->updateStatus(false);
|
||||||
|
|
||||||
return $this->processInformation['termsig'];
|
return $this->processInformation['termsig'];
|
||||||
}
|
}
|
||||||
@ -675,7 +563,7 @@ class Process
|
|||||||
*/
|
*/
|
||||||
public function hasBeenStopped()
|
public function hasBeenStopped()
|
||||||
{
|
{
|
||||||
$this->updateStatus();
|
$this->updateStatus(false);
|
||||||
|
|
||||||
return $this->processInformation['stopped'];
|
return $this->processInformation['stopped'];
|
||||||
}
|
}
|
||||||
@ -691,7 +579,7 @@ class Process
|
|||||||
*/
|
*/
|
||||||
public function getStopSignal()
|
public function getStopSignal()
|
||||||
{
|
{
|
||||||
$this->updateStatus();
|
$this->updateStatus(false);
|
||||||
|
|
||||||
return $this->processInformation['stopsig'];
|
return $this->processInformation['stopsig'];
|
||||||
}
|
}
|
||||||
@ -707,7 +595,7 @@ class Process
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->updateStatus();
|
$this->updateStatus(false);
|
||||||
|
|
||||||
return $this->processInformation['running'];
|
return $this->processInformation['running'];
|
||||||
}
|
}
|
||||||
@ -729,7 +617,7 @@ class Process
|
|||||||
*/
|
*/
|
||||||
public function isTerminated()
|
public function isTerminated()
|
||||||
{
|
{
|
||||||
$this->updateStatus();
|
$this->updateStatus(false);
|
||||||
|
|
||||||
return $this->status == self::STATUS_TERMINATED;
|
return $this->status == self::STATUS_TERMINATED;
|
||||||
}
|
}
|
||||||
@ -743,7 +631,7 @@ class Process
|
|||||||
*/
|
*/
|
||||||
public function getStatus()
|
public function getStatus()
|
||||||
{
|
{
|
||||||
$this->updateStatus();
|
$this->updateStatus(false);
|
||||||
|
|
||||||
return $this->status;
|
return $this->status;
|
||||||
}
|
}
|
||||||
@ -760,12 +648,10 @@ class Process
|
|||||||
*/
|
*/
|
||||||
public function stop($timeout = 10, $signal = null)
|
public function stop($timeout = 10, $signal = null)
|
||||||
{
|
{
|
||||||
$timeoutMicro = (int) $timeout*1E6;
|
$timeoutMicro = microtime(true) + $timeout;
|
||||||
if ($this->isRunning()) {
|
if ($this->isRunning()) {
|
||||||
proc_terminate($this->process);
|
proc_terminate($this->process);
|
||||||
$time = 0;
|
while ($this->isRunning() && microtime(true) < $timeoutMicro) {
|
||||||
while (1 == $this->isRunning() && $time < $timeoutMicro) {
|
|
||||||
$time += 1000;
|
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -775,20 +661,7 @@ class Process
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->pipes as $pipe) {
|
$this->updateStatus(false);
|
||||||
fclose($pipe);
|
|
||||||
}
|
|
||||||
$this->pipes = array();
|
|
||||||
|
|
||||||
$exitcode = proc_close($this->process);
|
|
||||||
$this->exitcode = -1 === $this->processInformation['exitcode'] ? $exitcode : $this->processInformation['exitcode'];
|
|
||||||
|
|
||||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
|
||||||
foreach ($this->fileHandles as $fileHandle) {
|
|
||||||
fclose($fileHandle);
|
|
||||||
}
|
|
||||||
$this->fileHandles = array();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$this->status = self::STATUS_TERMINATED;
|
$this->status = self::STATUS_TERMINATED;
|
||||||
|
|
||||||
@ -1190,43 +1063,23 @@ class Process
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the status of the process.
|
* Updates the status of the process, reads pipes.
|
||||||
|
*
|
||||||
|
* @param Boolean $blocking Whether to use a clocking read call.
|
||||||
*/
|
*/
|
||||||
protected function updateStatus()
|
protected function updateStatus($blocking)
|
||||||
{
|
{
|
||||||
if (self::STATUS_STARTED !== $this->status) {
|
if (self::STATUS_STARTED !== $this->status) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->readPipes($blocking);
|
||||||
|
|
||||||
$this->processInformation = proc_get_status($this->process);
|
$this->processInformation = proc_get_status($this->process);
|
||||||
|
$this->captureExitCode();
|
||||||
if (!$this->processInformation['running']) {
|
if (!$this->processInformation['running']) {
|
||||||
|
$this->close();
|
||||||
$this->status = self::STATUS_TERMINATED;
|
$this->status = self::STATUS_TERMINATED;
|
||||||
if (-1 !== $this->processInformation['exitcode']) {
|
|
||||||
$this->exitcode = $this->processInformation['exitcode'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the current error output of the process (STDERR).
|
|
||||||
*/
|
|
||||||
protected function updateErrorOutput()
|
|
||||||
{
|
|
||||||
if (isset($this->pipes[self::STDERR]) && is_resource($this->pipes[self::STDERR])) {
|
|
||||||
$this->addErrorOutput(stream_get_contents($this->pipes[self::STDERR]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the current output of the process (STDOUT).
|
|
||||||
*/
|
|
||||||
protected function updateOutput()
|
|
||||||
{
|
|
||||||
if (defined('PHP_WINDOWS_VERSION_BUILD') && isset($this->fileHandles[self::STDOUT]) && is_resource($this->fileHandles[self::STDOUT])) {
|
|
||||||
fseek($this->fileHandles[self::STDOUT], $this->readBytes[self::STDOUT]);
|
|
||||||
$this->addOutput(stream_get_contents($this->fileHandles[self::STDOUT]));
|
|
||||||
} elseif (isset($this->pipes[self::STDOUT]) && is_resource($this->pipes[self::STDOUT])) {
|
|
||||||
$this->addOutput(stream_get_contents($this->pipes[self::STDOUT]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1250,10 +1103,9 @@ class Process
|
|||||||
/**
|
/**
|
||||||
* Handles the windows file handles fallbacks.
|
* Handles the windows file handles fallbacks.
|
||||||
*
|
*
|
||||||
* @param callable $callback A valid PHP callback
|
|
||||||
* @param Boolean $closeEmptyHandles if true, handles that are empty will be assumed closed
|
* @param Boolean $closeEmptyHandles if true, handles that are empty will be assumed closed
|
||||||
*/
|
*/
|
||||||
private function processFileHandles($callback, $closeEmptyHandles = false)
|
private function processFileHandles($closeEmptyHandles = false)
|
||||||
{
|
{
|
||||||
$fh = $this->fileHandles;
|
$fh = $this->fileHandles;
|
||||||
foreach ($fh as $type => $fileHandle) {
|
foreach ($fh as $type => $fileHandle) {
|
||||||
@ -1261,7 +1113,7 @@ class Process
|
|||||||
$data = fread($fileHandle, 8192);
|
$data = fread($fileHandle, 8192);
|
||||||
if (strlen($data) > 0) {
|
if (strlen($data) > 0) {
|
||||||
$this->readBytes[$type] += strlen($data);
|
$this->readBytes[$type] += strlen($data);
|
||||||
call_user_func($callback, $type == 1 ? self::OUT : self::ERR, $data);
|
call_user_func($this->callback, $type == 1 ? self::OUT : self::ERR, $data);
|
||||||
}
|
}
|
||||||
if (false === $data || ($closeEmptyHandles && '' === $data && feof($fileHandle))) {
|
if (false === $data || ($closeEmptyHandles && '' === $data && feof($fileHandle))) {
|
||||||
fclose($fileHandle);
|
fclose($fileHandle);
|
||||||
@ -1304,4 +1156,177 @@ class Process
|
|||||||
|
|
||||||
return $timeout;
|
return $timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads pipes, executes callback.
|
||||||
|
*
|
||||||
|
* @param Boolean $blocking Whether to use blocking calls or not.
|
||||||
|
*/
|
||||||
|
private function readPipes($blocking)
|
||||||
|
{
|
||||||
|
if (defined('PHP_WINDOWS_VERSION_BUILD') && $this->fileHandles) {
|
||||||
|
$this->processFileHandles(!$this->pipes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->pipes) {
|
||||||
|
$r = $this->pipes;
|
||||||
|
$w = null;
|
||||||
|
$e = null;
|
||||||
|
|
||||||
|
// let's have a look if something changed in streams
|
||||||
|
if (false === $n = @stream_select($r, $w, $e, 0, $blocking ? ceil(self::TIMEOUT_PRECISION * 1E6) : 0)) {
|
||||||
|
// if a system call has been interrupted, forget about it, let's try again
|
||||||
|
// otherwise, an error occured, let's reset pipes
|
||||||
|
if (!$this->hasSystemCallBeenInterrupted()) {
|
||||||
|
$this->pipes = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing has changed
|
||||||
|
if (0 === $n) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->processReadPipes($r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes data to pipes.
|
||||||
|
*
|
||||||
|
* @param Boolean $blocking Whether to use blocking calls or not.
|
||||||
|
*/
|
||||||
|
private function writePipes()
|
||||||
|
{
|
||||||
|
if ($this->tty) {
|
||||||
|
$this->status = self::STATUS_TERMINATED;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $this->stdin) {
|
||||||
|
fclose($this->pipes[0]);
|
||||||
|
unset($this->pipes[0]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$writePipes = array($this->pipes[0]);
|
||||||
|
unset($this->pipes[0]);
|
||||||
|
$stdinLen = strlen($this->stdin);
|
||||||
|
$stdinOffset = 0;
|
||||||
|
|
||||||
|
while ($writePipes) {
|
||||||
|
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||||
|
$this->processFileHandles();
|
||||||
|
}
|
||||||
|
|
||||||
|
$r = $this->pipes;
|
||||||
|
$w = $writePipes;
|
||||||
|
$e = null;
|
||||||
|
|
||||||
|
if (false === $n = @stream_select($r, $w, $e, 0, $blocking ? ceil(static::TIMEOUT_PRECISION * 1E6) : 0)) {
|
||||||
|
// if a system call has been interrupted, forget about it, let's try again
|
||||||
|
if ($this->hasSystemCallBeenInterrupted()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing has changed, let's wait until the process is ready
|
||||||
|
if (0 === $n) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($w) {
|
||||||
|
$written = fwrite($writePipes[0], (binary) substr($this->stdin, $stdinOffset), 8192);
|
||||||
|
if (false !== $written) {
|
||||||
|
$stdinOffset += $written;
|
||||||
|
}
|
||||||
|
if ($stdinOffset >= $stdinLen) {
|
||||||
|
fclose($writePipes[0]);
|
||||||
|
$writePipes = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->processReadPipes($r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes read pipes, executes callback on it.
|
||||||
|
*
|
||||||
|
* @param array $pipes
|
||||||
|
*/
|
||||||
|
private function processReadPipes(array $pipes)
|
||||||
|
{
|
||||||
|
foreach ($pipes as $pipe) {
|
||||||
|
$type = array_search($pipe, $this->pipes);
|
||||||
|
$data = fread($pipe, 8192);
|
||||||
|
|
||||||
|
if (strlen($data) > 0) {
|
||||||
|
// last exit code is output and caught to work around --enable-sigchild
|
||||||
|
if (3 == $type) {
|
||||||
|
$this->fallbackExitcode = (int) $data;
|
||||||
|
} else {
|
||||||
|
call_user_func($this->callback, $type == 1 ? self::OUT : self::ERR, $data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (false === $data || feof($pipe)) {
|
||||||
|
fclose($pipe);
|
||||||
|
unset($this->pipes[$type]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Captures the exitcode if mentioned in the process informations.
|
||||||
|
*/
|
||||||
|
private function captureExitCode()
|
||||||
|
{
|
||||||
|
if (isset($this->processInformation['exitcode']) && -1 != $this->processInformation['exitcode']) {
|
||||||
|
$this->exitcode = $this->processInformation['exitcode'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes process resource, closes file handles, sets the exitcode.
|
||||||
|
*
|
||||||
|
* @return Integer The exitcode
|
||||||
|
*/
|
||||||
|
private function close()
|
||||||
|
{
|
||||||
|
foreach ($this->pipes as $pipe) {
|
||||||
|
fclose($pipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->pipes = null;
|
||||||
|
$exitcode = -1;
|
||||||
|
|
||||||
|
if (is_resource($this->process)) {
|
||||||
|
$exitcode = proc_close($this->process);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->exitcode = $this->exitcode !== null ? $this->exitcode : -1;
|
||||||
|
$this->exitcode = -1 != $exitcode ? $exitcode : $this->exitcode;
|
||||||
|
|
||||||
|
if (-1 == $this->exitcode && null !== $this->fallbackExitcode) {
|
||||||
|
$this->exitcode = $this->fallbackExitcode;
|
||||||
|
} elseif (-1 === $this->exitcode && $this->processInformation['signaled'] && 0 < $this->processInformation['termsig']) {
|
||||||
|
// if process has been signaled, no exitcode but a valid termsig, apply unix convention
|
||||||
|
$this->exitcode = 128 + $this->processInformation['termsig'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||||
|
foreach ($this->fileHandles as $fileHandle) {
|
||||||
|
fclose($fileHandle);
|
||||||
|
}
|
||||||
|
$this->fileHandles = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->exitcode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,11 +41,15 @@ class ProcessUtils
|
|||||||
//@see https://bugs.php.net/bug.php?id=43784
|
//@see https://bugs.php.net/bug.php?id=43784
|
||||||
//@see https://bugs.php.net/bug.php?id=49446
|
//@see https://bugs.php.net/bug.php?id=49446
|
||||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||||
|
if ('' === $argument) {
|
||||||
|
return escapeshellarg($argument);
|
||||||
|
}
|
||||||
|
|
||||||
$escapedArgument = '';
|
$escapedArgument = '';
|
||||||
foreach (preg_split('/([%"])/i', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) {
|
foreach (preg_split('/([%"])/i', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) {
|
||||||
if ('"' == $part) {
|
if ('"' === $part) {
|
||||||
$escapedArgument .= '\\"';
|
$escapedArgument .= '\\"';
|
||||||
} elseif ('%' == $part) {
|
} elseif ('%' === $part) {
|
||||||
$escapedArgument .= '^%';
|
$escapedArgument .= '^%';
|
||||||
} else {
|
} else {
|
||||||
$escapedArgument .= escapeshellarg($part);
|
$escapedArgument .= escapeshellarg($part);
|
||||||
|
@ -66,6 +66,23 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertLessThan(1.8, $duration);
|
$this->assertLessThan(1.8, $duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCallbacksAreExecutedWithStart()
|
||||||
|
{
|
||||||
|
$data = '';
|
||||||
|
|
||||||
|
$process = $this->getProcess('echo "foo";sleep 1;echo "foo"');
|
||||||
|
$process->start(function ($type, $buffer) use (&$data) {
|
||||||
|
$data .= $buffer;
|
||||||
|
});
|
||||||
|
|
||||||
|
$start = microtime(true);
|
||||||
|
while ($process->isRunning()) {
|
||||||
|
usleep(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertEquals("foo\nfoo\n", $data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tests results from sub processes
|
* tests results from sub processes
|
||||||
*
|
*
|
||||||
@ -255,7 +272,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
public function testStop()
|
public function testStop()
|
||||||
{
|
{
|
||||||
$process = $this->getProcess('php -r "while (true) {}"');
|
$process = $this->getProcess('php -r "sleep(4);"');
|
||||||
$process->start();
|
$process->start();
|
||||||
$this->assertTrue($process->isRunning());
|
$this->assertTrue($process->isRunning());
|
||||||
$process->stop();
|
$process->stop();
|
||||||
@ -271,7 +288,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
public function testIsNotSuccessful()
|
public function testIsNotSuccessful()
|
||||||
{
|
{
|
||||||
$process = $this->getProcess('php -r "while (true) {}"');
|
$process = $this->getProcess('php -r "sleep(4);"');
|
||||||
$process->start();
|
$process->start();
|
||||||
$this->assertTrue($process->isRunning());
|
$this->assertTrue($process->isRunning());
|
||||||
$process->stop();
|
$process->stop();
|
||||||
@ -317,7 +334,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->markTestSkipped('Windows does not support POSIX signals');
|
$this->markTestSkipped('Windows does not support POSIX signals');
|
||||||
}
|
}
|
||||||
|
|
||||||
$process = $this->getProcess('php -r "while (true) {}"');
|
$process = $this->getProcess('php -r "sleep(4);"');
|
||||||
$process->start();
|
$process->start();
|
||||||
$process->stop();
|
$process->stop();
|
||||||
$this->assertTrue($process->hasBeenSignaled());
|
$this->assertTrue($process->hasBeenSignaled());
|
||||||
@ -332,7 +349,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
|||||||
// SIGTERM is only defined if pcntl extension is present
|
// SIGTERM is only defined if pcntl extension is present
|
||||||
$termSignal = defined('SIGTERM') ? SIGTERM : 15;
|
$termSignal = defined('SIGTERM') ? SIGTERM : 15;
|
||||||
|
|
||||||
$process = $this->getProcess('php -r "while (true) {}"');
|
$process = $this->getProcess('php -r "sleep(4);"');
|
||||||
$process->start();
|
$process->start();
|
||||||
$process->stop();
|
$process->stop();
|
||||||
|
|
||||||
@ -506,6 +523,24 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals('Caught SIGUSR1', $process->getOutput());
|
$this->assertEquals('Caught SIGUSR1', $process->getOutput());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testExitCodeIsAvailableAfterSignal()
|
||||||
|
{
|
||||||
|
$this->verifyPosixIsEnabled();
|
||||||
|
|
||||||
|
$process = $this->getProcess('sleep 4');
|
||||||
|
$process->start();
|
||||||
|
$process->signal(SIGKILL);
|
||||||
|
|
||||||
|
while ($process->isRunning()) {
|
||||||
|
usleep(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertFalse($process->isRunning());
|
||||||
|
$this->assertTrue($process->hasBeenSignaled());
|
||||||
|
$this->assertFalse($process->isSuccessful());
|
||||||
|
$this->assertEquals(137, $process->getExitCode());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException Symfony\Component\Process\Exception\LogicException
|
* @expectedException Symfony\Component\Process\Exception\LogicException
|
||||||
*/
|
*/
|
||||||
|
@ -30,6 +30,7 @@ class ProcessUtilsTest extends \PHPUnit_Framework_TestCase
|
|||||||
array('"foo bar"', 'foo bar'),
|
array('"foo bar"', 'foo bar'),
|
||||||
array('^%"path"^%', '%path%'),
|
array('^%"path"^%', '%path%'),
|
||||||
array('"<|>"\\"" "\\""\'f"', '<|>" "\'f'),
|
array('"<|>"\\"" "\\""\'f"', '<|>" "\'f'),
|
||||||
|
array('""', ''),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ class ProcessUtilsTest extends \PHPUnit_Framework_TestCase
|
|||||||
array("'foo bar'", 'foo bar'),
|
array("'foo bar'", 'foo bar'),
|
||||||
array("'%path%'", '%path%'),
|
array("'%path%'", '%path%'),
|
||||||
array("'<|>\" \"'\\''f'", '<|>" "\'f'),
|
array("'<|>\" \"'\\''f'", '<|>" "\'f'),
|
||||||
|
array("''", ''),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,6 +138,11 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
|||||||
$this->markTestSkipped('Retrieving Pid is not supported in sigchild environment');
|
$this->markTestSkipped('Retrieving Pid is not supported in sigchild environment');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testExitCodeIsAvailableAfterSignal()
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Signal is not supported in sigchild environment');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -98,6 +98,11 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
|
|||||||
$this->markTestSkipped('Retrieving Pid is not supported in sigchild environment');
|
$this->markTestSkipped('Retrieving Pid is not supported in sigchild environment');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testExitCodeIsAvailableAfterSignal()
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Signal is not supported in sigchild environment');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
namespace Symfony\Component\Routing\Exception;
|
namespace Symfony\Component\Routing\Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception thrown when a route does not exists
|
* Exception thrown when a route does not exist
|
||||||
*
|
*
|
||||||
* @author Alexandre Salomé <alexandre.salome@gmail.com>
|
* @author Alexandre Salomé <alexandre.salome@gmail.com>
|
||||||
*
|
*
|
||||||
|
@ -23,7 +23,7 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
|||||||
interface AuthenticationManagerInterface
|
interface AuthenticationManagerInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Attempts to authenticates a TokenInterface object.
|
* Attempts to authenticate a TokenInterface object.
|
||||||
*
|
*
|
||||||
* @param TokenInterface $token The TokenInterface instance to authenticate
|
* @param TokenInterface $token The TokenInterface instance to authenticate
|
||||||
*
|
*
|
||||||
|
@ -45,7 +45,7 @@ class ChainLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
{
|
{
|
||||||
$loader = new ProjectTemplateLoader1(array($this->loader1, $this->loader2));
|
$loader = new ProjectTemplateLoader1(array($this->loader1, $this->loader2));
|
||||||
$this->assertFalse($loader->load(new TemplateReference('bar', 'php')), '->load() returns false if the template is not found');
|
$this->assertFalse($loader->load(new TemplateReference('bar', 'php')), '->load() returns false if the template is not found');
|
||||||
$this->assertFalse($loader->load(new TemplateReference('foo', 'php')), '->load() returns false if the template does not exists for the given renderer');
|
$this->assertFalse($loader->load(new TemplateReference('foo', 'php')), '->load() returns false if the template does not exist for the given renderer');
|
||||||
$this->assertInstanceOf(
|
$this->assertInstanceOf(
|
||||||
'Symfony\Component\Templating\Storage\FileStorage',
|
'Symfony\Component\Templating\Storage\FileStorage',
|
||||||
$loader->load(new TemplateReference('foo.php', 'php')),
|
$loader->load(new TemplateReference('foo.php', 'php')),
|
||||||
|
@ -61,7 +61,7 @@ class FilesystemLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$loader = new ProjectTemplateLoader2($pathPattern);
|
$loader = new ProjectTemplateLoader2($pathPattern);
|
||||||
$loader->setDebugger($debugger = new \Symfony\Component\Templating\Tests\Fixtures\ProjectTemplateDebugger());
|
$loader->setDebugger($debugger = new \Symfony\Component\Templating\Tests\Fixtures\ProjectTemplateDebugger());
|
||||||
$this->assertFalse($loader->load(new TemplateReference('foo.xml', 'php')), '->load() returns false if the template does not exists for the given engine');
|
$this->assertFalse($loader->load(new TemplateReference('foo.xml', 'php')), '->load() returns false if the template does not exist for the given engine');
|
||||||
$this->assertTrue($debugger->hasMessage('Failed loading template'), '->load() logs a "Failed loading template" message if the template is not found');
|
$this->assertTrue($debugger->hasMessage('Failed loading template'), '->load() logs a "Failed loading template" message if the template is not found');
|
||||||
|
|
||||||
$loader = new ProjectTemplateLoader2(array(self::$fixturesPath.'/null/%name%', $pathPattern));
|
$loader = new ProjectTemplateLoader2(array(self::$fixturesPath.'/null/%name%', $pathPattern));
|
||||||
|
@ -26,6 +26,10 @@ abstract class AbstractComparisonValidator extends ConstraintValidator
|
|||||||
*/
|
*/
|
||||||
public function validate($value, Constraint $constraint)
|
public function validate($value, Constraint $constraint)
|
||||||
{
|
{
|
||||||
|
if (null === $value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!$this->compareValues($value, $constraint->value, $constraint)) {
|
if (!$this->compareValues($value, $constraint->value, $constraint)) {
|
||||||
$this->context->addViolation($constraint->message, array(
|
$this->context->addViolation($constraint->message, array(
|
||||||
'{{ value }}' => $this->valueToString($constraint->value),
|
'{{ value }}' => $this->valueToString($constraint->value),
|
||||||
|
@ -31,9 +31,13 @@ class StaticMethodLoader implements LoaderInterface
|
|||||||
/** @var \ReflectionClass $reflClass */
|
/** @var \ReflectionClass $reflClass */
|
||||||
$reflClass = $metadata->getReflectionClass();
|
$reflClass = $metadata->getReflectionClass();
|
||||||
|
|
||||||
if (!$reflClass->isInterface() && !$reflClass->isAbstract() && $reflClass->hasMethod($this->methodName)) {
|
if (!$reflClass->isInterface() && $reflClass->hasMethod($this->methodName)) {
|
||||||
$reflMethod = $reflClass->getMethod($this->methodName);
|
$reflMethod = $reflClass->getMethod($this->methodName);
|
||||||
|
|
||||||
|
if ($reflMethod->isAbstract()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!$reflMethod->isStatic()) {
|
if (!$reflMethod->isStatic()) {
|
||||||
throw new MappingException(sprintf('The method %s::%s should be static', $reflClass->name, $this->methodName));
|
throw new MappingException(sprintf('The method %s::%s should be static', $reflClass->name, $this->methodName));
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,8 @@ class EqualToValidatorTest extends AbstractComparisonValidatorTestCase
|
|||||||
array(3, 3),
|
array(3, 3),
|
||||||
array(3, '3'),
|
array(3, '3'),
|
||||||
array('a', 'a'),
|
array('a', 'a'),
|
||||||
array(new \DateTime('2000-01-01'), new \DateTime('2000-01-01'))
|
array(new \DateTime('2000-01-01'), new \DateTime('2000-01-01')),
|
||||||
|
array(null, 1),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ class GreaterThanOrEqualValidatorTest extends AbstractComparisonValidatorTestCas
|
|||||||
array(new \DateTime('2000/01/01'), new \DateTime('2000/01/01')),
|
array(new \DateTime('2000/01/01'), new \DateTime('2000/01/01')),
|
||||||
array('a', 'a'),
|
array('a', 'a'),
|
||||||
array('z', 'a'),
|
array('z', 'a'),
|
||||||
|
array(null, 1),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,8 @@ class GreaterThanValidatorTest extends AbstractComparisonValidatorTestCase
|
|||||||
return array(
|
return array(
|
||||||
array(2, 1),
|
array(2, 1),
|
||||||
array(new \DateTime('2005/01/01'), new \DateTime('2001/01/01')),
|
array(new \DateTime('2005/01/01'), new \DateTime('2001/01/01')),
|
||||||
array('333', '22')
|
array('333', '22'),
|
||||||
|
array(null, 1),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,8 @@ class IdenticalToValidatorTest extends AbstractComparisonValidatorTestCase
|
|||||||
return array(
|
return array(
|
||||||
array(3, 3),
|
array(3, 3),
|
||||||
array('a', 'a'),
|
array('a', 'a'),
|
||||||
array($date, $date)
|
array($date, $date),
|
||||||
|
array(null, 1),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ class LessThanOrEqualValidatorTest extends AbstractComparisonValidatorTestCase
|
|||||||
array(new \DateTime('2000-01-01'), new \DateTime('2020-01-01')),
|
array(new \DateTime('2000-01-01'), new \DateTime('2020-01-01')),
|
||||||
array('a', 'a'),
|
array('a', 'a'),
|
||||||
array('a', 'z'),
|
array('a', 'z'),
|
||||||
|
array(null, 1),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,8 @@ class LessThanValidatorTest extends AbstractComparisonValidatorTestCase
|
|||||||
return array(
|
return array(
|
||||||
array(1, 2),
|
array(1, 2),
|
||||||
array(new \DateTime('2000-01-01'), new \DateTime('2010-01-01')),
|
array(new \DateTime('2000-01-01'), new \DateTime('2010-01-01')),
|
||||||
array('22', '333')
|
array('22', '333'),
|
||||||
|
array(null, 1),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,8 @@ class NotEqualToValidatorTest extends AbstractComparisonValidatorTestCase
|
|||||||
return array(
|
return array(
|
||||||
array(1, 2),
|
array(1, 2),
|
||||||
array('22', '333'),
|
array('22', '333'),
|
||||||
array(new \DateTime('2001-01-01'), new \DateTime('2000-01-01'))
|
array(new \DateTime('2001-01-01'), new \DateTime('2000-01-01')),
|
||||||
|
array(null, 1),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,8 @@ class NotIdenticalToValidatorTest extends AbstractComparisonValidatorTestCase
|
|||||||
array('2', 2),
|
array('2', 2),
|
||||||
array('22', '333'),
|
array('22', '333'),
|
||||||
array(new \DateTime('2001-01-01'), new \DateTime('2000-01-01')),
|
array(new \DateTime('2001-01-01'), new \DateTime('2000-01-01')),
|
||||||
array(new \DateTime('2000-01-01'), new \DateTime('2000-01-01'))
|
array(new \DateTime('2000-01-01'), new \DateTime('2000-01-01')),
|
||||||
|
array(null, 1),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ class GetterMetadataTest extends \PHPUnit_Framework_TestCase
|
|||||||
public function testGetPropertyValueFromPublicGetter()
|
public function testGetPropertyValueFromPublicGetter()
|
||||||
{
|
{
|
||||||
// private getters don't work yet because ReflectionMethod::setAccessible()
|
// private getters don't work yet because ReflectionMethod::setAccessible()
|
||||||
// does not exists yet in a stable PHP release
|
// does not exist yet in a stable PHP release
|
||||||
|
|
||||||
$entity = new Entity('foobar');
|
$entity = new Entity('foobar');
|
||||||
$metadata = new GetterMetadata(self::CLASSNAME, 'internal');
|
$metadata = new GetterMetadata(self::CLASSNAME, 'internal');
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Symfony\Component\Validator\Tests\Mapping\Loader;
|
||||||
|
|
||||||
|
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
||||||
|
|
||||||
|
abstract class AbstractMethodStaticLoader
|
||||||
|
{
|
||||||
|
abstract public static function loadMetadata(ClassMetadata $metadata);
|
||||||
|
}
|
@ -66,13 +66,28 @@ class StaticMethodLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertCount(0, $metadata->getConstraints());
|
$this->assertCount(0, $metadata->getConstraints());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLoadClassMetadataIgnoresAbstractClasses()
|
public function testLoadClassMetadataInAbstractClasses()
|
||||||
{
|
{
|
||||||
$loader = new StaticMethodLoader('loadMetadata');
|
$loader = new StaticMethodLoader('loadMetadata');
|
||||||
$metadata = new ClassMetadata(__NAMESPACE__.'\AbstractStaticLoader');
|
$metadata = new ClassMetadata(__NAMESPACE__.'\AbstractStaticLoader');
|
||||||
|
|
||||||
$loader->loadClassMetadata($metadata);
|
$loader->loadClassMetadata($metadata);
|
||||||
|
|
||||||
|
$this->assertCount(1, $metadata->getConstraints());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLoadClassMetadataIgnoresAbstractMethods()
|
||||||
|
{
|
||||||
|
$loader = new StaticMethodLoader('loadMetadata');
|
||||||
|
try {
|
||||||
|
include __DIR__ . '/AbstractMethodStaticLoader.php';
|
||||||
|
$this->fail('AbstractMethodStaticLoader should produce a strict standard error.');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
$metadata = new ClassMetadata(__NAMESPACE__.'\AbstractMethodStaticLoader');
|
||||||
|
$loader->loadClassMetadata($metadata);
|
||||||
|
|
||||||
$this->assertCount(0, $metadata->getConstraints());
|
$this->assertCount(0, $metadata->getConstraints());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,6 +101,7 @@ abstract class AbstractStaticLoader
|
|||||||
{
|
{
|
||||||
public static function loadMetadata(ClassMetadata $metadata)
|
public static function loadMetadata(ClassMetadata $metadata)
|
||||||
{
|
{
|
||||||
|
$metadata->addConstraint(new ConstraintA());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user