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:
Fabien Potencier 2013-08-14 15:08:25 +02:00
commit a67f5d03d1
38 changed files with 429 additions and 222 deletions

View File

@ -137,7 +137,9 @@ class CodeExtension extends \Twig_Extension
public function fileExcerpt($file, $line)
{
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
$code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code);
$content = preg_split('#<br />#', $code);

View File

@ -23,8 +23,6 @@ class FormEnctypeNode extends SearchAndRenderBlockNode
{
parent::compile($compiler);
$compiler->raw(";\n");
// 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)');
}

View File

@ -11,7 +11,7 @@
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\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@ -78,7 +78,7 @@ class Application extends BaseApplication
$container = $this->kernel->getContainer();
foreach ($this->all() as $command) {
if ($command instanceof ContainerAwareCommand) {
if ($command instanceof ContainerAwareInterface) {
$command->setContainer($container);
}
}

View File

@ -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
$code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code);
$content = preg_split('#<br />#', $code);

View File

@ -177,7 +177,7 @@ class ClassLoader
$classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className).'.php';
foreach ($this->prefixes as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
if ($class === strstr($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($dir.DIRECTORY_SEPARATOR.$classPath)) {
return $dir.DIRECTORY_SEPARATOR.$classPath;

View File

@ -139,6 +139,11 @@ class ErrorHandler
}
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);
}

View File

@ -20,6 +20,32 @@ use Symfony\Component\Debug\ErrorHandler;
*/
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()
{
$handler = ErrorHandler::register(3);

View File

@ -187,7 +187,7 @@ class Command
public function get($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]];

View File

@ -33,6 +33,7 @@ abstract class BaseType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->setDisabled($options['disabled']);
$builder->setAutoInitialize($options['auto_initialize']);
}
/**
@ -112,6 +113,7 @@ abstract class BaseType extends AbstractType
'label' => null,
'attr' => array(),
'translation_domain' => null,
'auto_initialize' => true,
));
$resolver->setAllowedTypes(array(

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\ButtonTypeInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* A form button.
@ -35,4 +36,16 @@ class ButtonType extends BaseType implements ButtonTypeInterface
{
return 'button';
}
/**
* {@inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
parent::setDefaultOptions($resolver);
$resolver->setDefaults(array(
'auto_initialize' => false,
));
}
}

View File

@ -55,7 +55,6 @@ class FormType extends BaseType
->setDataMapper($options['compound'] ? new PropertyPathMapper($this->propertyAccessor) : null)
->setMethod($options['method'])
->setAction($options['action'])
->setAutoInitialize($options['auto_initialize'])
;
if ($options['trim']) {
@ -188,7 +187,6 @@ class FormType extends BaseType
// According to RFC 2396 (http://www.ietf.org/rfc/rfc2396.txt)
// section 4.2., empty URIs are considered same-document references
'action' => '',
'auto_initialize' => true,
));
$resolver->setAllowedTypes(array(

View File

@ -51,4 +51,13 @@ class SubmitTypeTest extends TypeTestCase
$this->assertTrue($button->isClicked());
}
public function testSubmitCanBeAddedToForm()
{
$form = $this->factory
->createBuilder('form')
->getForm();
$this->assertSame($form, $form->add('send', 'submit'));
}
}

View File

@ -112,10 +112,11 @@ class InlineFragmentRenderer extends RoutableFragmentRenderer
// Sub-request object will point to localhost as client ip and real client ip
// will be included into trusted header for client ip
try {
$trustedHeaderName = Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP);
$currentXForwardedFor = $request->headers->get($trustedHeaderName, '');
if ($trustedHeaderName = Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP)) {
$currentXForwardedFor = $request->headers->get($trustedHeaderName, '');
$server['HTTP_'.$trustedHeaderName] = ($currentXForwardedFor ? $currentXForwardedFor.', ' : '').$request->getClientIp();
$server['HTTP_'.$trustedHeaderName] = ($currentXForwardedFor ? $currentXForwardedFor.', ' : '').$request->getClientIp();
}
} catch (\InvalidArgumentException $e) {
// Do nothing
}

View File

@ -35,7 +35,7 @@ interface ProfilerStorageInterface
/**
* 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
*

View File

@ -85,6 +85,26 @@ class InlineFragmentRendererTest extends \PHPUnit_Framework_TestCase
$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
*/
@ -165,8 +185,11 @@ class InlineFragmentRendererTest extends \PHPUnit_Framework_TestCase
{
$expectedSubRequest = Request::create('/');
$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
@ -181,6 +204,16 @@ class InlineFragmentRendererTest extends \PHPUnit_Framework_TestCase
$request->headers->set('Surrogate-Capability', 'abc="ESI/1.0"');
$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 {

View File

@ -40,6 +40,7 @@ class Process
// Timeout Precision in seconds.
const TIMEOUT_PRECISION = 0.2;
private $callback;
private $commandline;
private $cwd;
private $env;
@ -169,6 +170,7 @@ class Process
public function __clone()
{
$this->callback = null;
$this->exitcode = null;
$this->fallbackExitcode = null;
$this->processInformation = null;
@ -204,7 +206,7 @@ class Process
{
$this->start($callback);
return $this->wait($callback);
return $this->wait();
}
/**
@ -241,7 +243,7 @@ class Process
$this->stderr = '';
$this->incrementalOutputOffset = 0;
$this->incrementalErrorOutputOffset = 0;
$callback = $this->buildCallback($callback);
$this->callback = $this->buildCallback($callback);
$descriptors = $this->getDescriptors();
$commandline = $this->commandline;
@ -264,75 +266,9 @@ class Process
stream_set_blocking($pipe, false);
}
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($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;
$this->writePipes();
$this->updateStatus(false);
$this->checkTimeout();
}
/**
@ -378,55 +314,15 @@ class Process
*/
public function wait($callback = null)
{
$this->updateStatus();
$callback = $this->buildCallback($callback);
while ($this->pipes || (defined('PHP_WINDOWS_VERSION_BUILD') && $this->fileHandles)) {
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(false);
if (null !== $callback) {
$this->callback = $this->buildCallback($callback);
}
$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->isSigchildEnabled()) {
throw new RuntimeException('The process has been signaled.');
@ -441,8 +337,6 @@ class Process
usleep(1000);
}
$exitcode = proc_close($this->process);
if ($this->processInformation['signaled']) {
if ($this->isSigchildEnabled()) {
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']));
}
$this->exitcode = $this->processInformation['running'] ? $exitcode : $this->processInformation['exitcode'];
if (-1 == $this->exitcode && null !== $this->fallbackExitcode) {
$this->exitcode = $this->fallbackExitcode;
}
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.');
}
$this->updateStatus();
$this->updateStatus(false);
return $this->isRunning() ? $this->processInformation['pid'] : null;
}
@ -514,7 +402,7 @@ class Process
*/
public function getOutput()
{
$this->updateOutput();
$this->readPipes(false);
return $this->stdout;
}
@ -546,7 +434,7 @@ class Process
*/
public function getErrorOutput()
{
$this->updateErrorOutput();
$this->readPipes(false);
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');
}
$this->updateStatus();
$this->updateStatus(false);
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');
}
$this->updateStatus();
$this->updateStatus(false);
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');
}
$this->updateStatus();
$this->updateStatus(false);
return $this->processInformation['termsig'];
}
@ -675,7 +563,7 @@ class Process
*/
public function hasBeenStopped()
{
$this->updateStatus();
$this->updateStatus(false);
return $this->processInformation['stopped'];
}
@ -691,7 +579,7 @@ class Process
*/
public function getStopSignal()
{
$this->updateStatus();
$this->updateStatus(false);
return $this->processInformation['stopsig'];
}
@ -707,7 +595,7 @@ class Process
return false;
}
$this->updateStatus();
$this->updateStatus(false);
return $this->processInformation['running'];
}
@ -729,7 +617,7 @@ class Process
*/
public function isTerminated()
{
$this->updateStatus();
$this->updateStatus(false);
return $this->status == self::STATUS_TERMINATED;
}
@ -743,7 +631,7 @@ class Process
*/
public function getStatus()
{
$this->updateStatus();
$this->updateStatus(false);
return $this->status;
}
@ -760,12 +648,10 @@ class Process
*/
public function stop($timeout = 10, $signal = null)
{
$timeoutMicro = (int) $timeout*1E6;
$timeoutMicro = microtime(true) + $timeout;
if ($this->isRunning()) {
proc_terminate($this->process);
$time = 0;
while (1 == $this->isRunning() && $time < $timeoutMicro) {
$time += 1000;
while ($this->isRunning() && microtime(true) < $timeoutMicro) {
usleep(1000);
}
@ -775,20 +661,7 @@ class Process
}
}
foreach ($this->pipes as $pipe) {
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->updateStatus(false);
}
$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) {
return;
}
$this->readPipes($blocking);
$this->processInformation = proc_get_status($this->process);
$this->captureExitCode();
if (!$this->processInformation['running']) {
$this->close();
$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.
*
* @param callable $callback A valid PHP callback
* @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;
foreach ($fh as $type => $fileHandle) {
@ -1261,7 +1113,7 @@ class Process
$data = fread($fileHandle, 8192);
if (strlen($data) > 0) {
$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))) {
fclose($fileHandle);
@ -1304,4 +1156,177 @@ class Process
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;
}
}

View File

@ -41,11 +41,15 @@ class ProcessUtils
//@see https://bugs.php.net/bug.php?id=43784
//@see https://bugs.php.net/bug.php?id=49446
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
if ('' === $argument) {
return escapeshellarg($argument);
}
$escapedArgument = '';
foreach (preg_split('/([%"])/i', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) {
if ('"' == $part) {
if ('"' === $part) {
$escapedArgument .= '\\"';
} elseif ('%' == $part) {
} elseif ('%' === $part) {
$escapedArgument .= '^%';
} else {
$escapedArgument .= escapeshellarg($part);

View File

@ -66,6 +66,23 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
$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
*
@ -255,7 +272,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
public function testStop()
{
$process = $this->getProcess('php -r "while (true) {}"');
$process = $this->getProcess('php -r "sleep(4);"');
$process->start();
$this->assertTrue($process->isRunning());
$process->stop();
@ -271,7 +288,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
public function testIsNotSuccessful()
{
$process = $this->getProcess('php -r "while (true) {}"');
$process = $this->getProcess('php -r "sleep(4);"');
$process->start();
$this->assertTrue($process->isRunning());
$process->stop();
@ -317,7 +334,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
$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->stop();
$this->assertTrue($process->hasBeenSignaled());
@ -332,7 +349,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
// SIGTERM is only defined if pcntl extension is present
$termSignal = defined('SIGTERM') ? SIGTERM : 15;
$process = $this->getProcess('php -r "while (true) {}"');
$process = $this->getProcess('php -r "sleep(4);"');
$process->start();
$process->stop();
@ -506,6 +523,24 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
$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
*/

View File

@ -30,6 +30,7 @@ class ProcessUtilsTest extends \PHPUnit_Framework_TestCase
array('"foo bar"', 'foo bar'),
array('^%"path"^%', '%path%'),
array('"<|>"\\"" "\\""\'f"', '<|>" "\'f'),
array('""', ''),
);
}
@ -37,6 +38,7 @@ class ProcessUtilsTest extends \PHPUnit_Framework_TestCase
array("'foo bar'", 'foo bar'),
array("'%path%'", '%path%'),
array("'<|>\" \"'\\''f'", '<|>" "\'f'),
array("''", ''),
);
}
}

View File

@ -138,6 +138,11 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
$this->markTestSkipped('Retrieving Pid is not supported in sigchild environment');
}
public function testExitCodeIsAvailableAfterSignal()
{
$this->markTestSkipped('Signal is not supported in sigchild environment');
}
/**
* {@inheritdoc}
*/

View File

@ -98,6 +98,11 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
$this->markTestSkipped('Retrieving Pid is not supported in sigchild environment');
}
public function testExitCodeIsAvailableAfterSignal()
{
$this->markTestSkipped('Signal is not supported in sigchild environment');
}
/**
* {@inheritdoc}
*/

View File

@ -12,7 +12,7 @@
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>
*

View File

@ -23,7 +23,7 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException;
interface AuthenticationManagerInterface
{
/**
* Attempts to authenticates a TokenInterface object.
* Attempts to authenticate a TokenInterface object.
*
* @param TokenInterface $token The TokenInterface instance to authenticate
*

View File

@ -45,7 +45,7 @@ class ChainLoaderTest extends \PHPUnit_Framework_TestCase
{
$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('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(
'Symfony\Component\Templating\Storage\FileStorage',
$loader->load(new TemplateReference('foo.php', 'php')),

View File

@ -61,7 +61,7 @@ class FilesystemLoaderTest extends \PHPUnit_Framework_TestCase
$loader = new ProjectTemplateLoader2($pathPattern);
$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');
$loader = new ProjectTemplateLoader2(array(self::$fixturesPath.'/null/%name%', $pathPattern));

View File

@ -26,6 +26,10 @@ abstract class AbstractComparisonValidator extends ConstraintValidator
*/
public function validate($value, Constraint $constraint)
{
if (null === $value) {
return;
}
if (!$this->compareValues($value, $constraint->value, $constraint)) {
$this->context->addViolation($constraint->message, array(
'{{ value }}' => $this->valueToString($constraint->value),

View File

@ -31,9 +31,13 @@ class StaticMethodLoader implements LoaderInterface
/** @var \ReflectionClass $reflClass */
$reflClass = $metadata->getReflectionClass();
if (!$reflClass->isInterface() && !$reflClass->isAbstract() && $reflClass->hasMethod($this->methodName)) {
if (!$reflClass->isInterface() && $reflClass->hasMethod($this->methodName)) {
$reflMethod = $reflClass->getMethod($this->methodName);
if ($reflMethod->isAbstract()) {
return false;
}
if (!$reflMethod->isStatic()) {
throw new MappingException(sprintf('The method %s::%s should be static', $reflClass->name, $this->methodName));
}

View File

@ -39,7 +39,8 @@ class EqualToValidatorTest extends AbstractComparisonValidatorTestCase
array(3, 3),
array(3, '3'),
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),
);
}

View File

@ -41,6 +41,7 @@ class GreaterThanOrEqualValidatorTest extends AbstractComparisonValidatorTestCas
array(new \DateTime('2000/01/01'), new \DateTime('2000/01/01')),
array('a', 'a'),
array('z', 'a'),
array(null, 1),
);
}

View File

@ -37,7 +37,8 @@ class GreaterThanValidatorTest extends AbstractComparisonValidatorTestCase
return array(
array(2, 1),
array(new \DateTime('2005/01/01'), new \DateTime('2001/01/01')),
array('333', '22')
array('333', '22'),
array(null, 1),
);
}

View File

@ -40,7 +40,8 @@ class IdenticalToValidatorTest extends AbstractComparisonValidatorTestCase
return array(
array(3, 3),
array('a', 'a'),
array($date, $date)
array($date, $date),
array(null, 1),
);
}

View File

@ -41,6 +41,7 @@ class LessThanOrEqualValidatorTest extends AbstractComparisonValidatorTestCase
array(new \DateTime('2000-01-01'), new \DateTime('2020-01-01')),
array('a', 'a'),
array('a', 'z'),
array(null, 1),
);
}

View File

@ -37,7 +37,8 @@ class LessThanValidatorTest extends AbstractComparisonValidatorTestCase
return array(
array(1, 2),
array(new \DateTime('2000-01-01'), new \DateTime('2010-01-01')),
array('22', '333')
array('22', '333'),
array(null, 1),
);
}

View File

@ -38,7 +38,8 @@ class NotEqualToValidatorTest extends AbstractComparisonValidatorTestCase
return array(
array(1, 2),
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),
);
}

View File

@ -40,7 +40,8 @@ class NotIdenticalToValidatorTest extends AbstractComparisonValidatorTestCase
array('2', 2),
array('22', '333'),
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),
);
}

View File

@ -28,7 +28,7 @@ class GetterMetadataTest extends \PHPUnit_Framework_TestCase
public function testGetPropertyValueFromPublicGetter()
{
// 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');
$metadata = new GetterMetadata(self::CLASSNAME, 'internal');

View File

@ -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);
}

View File

@ -66,13 +66,28 @@ class StaticMethodLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertCount(0, $metadata->getConstraints());
}
public function testLoadClassMetadataIgnoresAbstractClasses()
public function testLoadClassMetadataInAbstractClasses()
{
$loader = new StaticMethodLoader('loadMetadata');
$metadata = new ClassMetadata(__NAMESPACE__.'\AbstractStaticLoader');
$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());
}
}
@ -86,6 +101,7 @@ abstract class AbstractStaticLoader
{
public static function loadMetadata(ClassMetadata $metadata)
{
$metadata->addConstraint(new ConstraintA());
}
}