Merge branch '2.4'
* 2.4: (52 commits) Fix #8205 : Deprecate file mode update when calling dumpFile Fix #10437: Catch exceptions when reloading a no-cache request Fix libxml_use_internal_errors and libxml_disable_entity_loader usage removed ini check to make uploadedfile work on gae Update OptionsResolver.php fixed comment in forms.xml file Clean KernelInterface docblocks Cast the group name as a string Fixed doc of InitAclCommand [Form] Fix "Array was modified outside object" in ResizeFormListener. Fix IBAN validator [Process] Remove unreachable code + avoid skipping tests in sigchild environment Fixed bug that incorrectly causes the "required" attribute to be omitted from select even though it contains the "multiple" attribute Added travis_retry to .travis.yml [Process] fix some typos and refactor some code [Process] Fix unit tests in sigchild disabled environment [Process] Trow exceptions in case a Process method is supposed to be called after termination fixed typo [Process] fixed fatal errors in getOutput and getErrorOutput when process was not started [Process] Fix escaping on Windows ... Conflicts: src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php src/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php src/Symfony/Component/Process/Process.php src/Symfony/Component/Process/ProcessPipes.php src/Symfony/Component/Process/Tests/AbstractProcessTest.php
This commit is contained in:
commit
3baa43b44e
@ -15,7 +15,7 @@ matrix:
|
||||
services: mongodb
|
||||
|
||||
before_script:
|
||||
- sudo apt-get install parallel
|
||||
- travis_retry sudo apt-get install parallel
|
||||
- sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then echo "" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini; fi;'
|
||||
- sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
|
||||
- sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ] && [ $(php -r "echo PHP_MINOR_VERSION;") -le 4 ]; then echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
|
||||
|
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2004-2013 Fabien Potencier
|
||||
Copyright (c) 2004-2014 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -67,7 +67,7 @@
|
||||
|
||||
{% block choice_widget_collapsed %}
|
||||
{% spaceless %}
|
||||
{% if required and empty_value is none and not empty_value_in_choices %}
|
||||
{% if required and empty_value is none and not empty_value_in_choices and not multiple %}
|
||||
{% set required = false %}
|
||||
{% endif %}
|
||||
<select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
|
||||
|
@ -41,7 +41,7 @@
|
||||
"symfony/yaml": "For using the YamlExtension",
|
||||
"symfony/security": "For using the SecurityExtension",
|
||||
"symfony/stopwatch": "For using the StopwatchExtension",
|
||||
"symfony/expression": "For using the ExpressionExtension"
|
||||
"symfony/expression-language": "For using the ExpressionExtension"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": { "Symfony\\Bridge\\Twig\\": "" }
|
||||
|
@ -67,6 +67,9 @@ router script using <info>--router</info> option:
|
||||
|
||||
<info>%command.full_name% --router=app/config/router.php</info>
|
||||
|
||||
Specifing a router script is required when the used environment is not "dev" or
|
||||
"prod".
|
||||
|
||||
See also: http://www.php.net/manual/en/features.commandline.webserver.php
|
||||
|
||||
EOF
|
||||
@ -85,7 +88,13 @@ EOF
|
||||
$output->writeln('<error>Running PHP built-in server in production environment is NOT recommended!</error>');
|
||||
}
|
||||
|
||||
$output->writeln(sprintf("Server running on <info>%s</info>\n", $input->getArgument('address')));
|
||||
$output->writeln(sprintf("Server running on <info>http://%s</info>\n", $input->getArgument('address')));
|
||||
|
||||
$router = $input->getOption('router') ?: $this
|
||||
->getContainer()
|
||||
->get('kernel')
|
||||
->locateResource(sprintf('@FrameworkBundle/Resources/config/router_%s.php', $env))
|
||||
;
|
||||
|
||||
if (defined('HHVM_VERSION')) {
|
||||
$builder = $this->createHhvmProcessBuilder($input, $output, $env);
|
||||
|
@ -20,12 +20,12 @@ class CompilerDebugDumpPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
$filename = self::getCompilerLogFilename($container);
|
||||
|
||||
$filesystem = new Filesystem();
|
||||
$filesystem->dumpFile(
|
||||
self::getCompilerLogFilename($container),
|
||||
implode("\n", $container->getCompiler()->getLog()),
|
||||
0666 & ~umask()
|
||||
);
|
||||
$filesystem->dumpFile($filename, implode("\n", $container->getCompiler()->getLog()), null);
|
||||
// discard chmod failure (some filesystem may not support it)
|
||||
@chmod($filename, 0666 & ~umask());
|
||||
}
|
||||
|
||||
public static function getCompilerLogFilename(ContainerInterface $container)
|
||||
|
@ -28,11 +28,10 @@ class ContainerBuilderDebugDumpPass implements CompilerPassInterface
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
$dumper = new XmlDumper($container);
|
||||
$filename = $container->getParameter('debug.container.dump');
|
||||
$filesystem = new Filesystem();
|
||||
$filesystem->dumpFile(
|
||||
$container->getParameter('debug.container.dump'),
|
||||
$dumper->dump(),
|
||||
0666 & ~umask()
|
||||
);
|
||||
$filesystem->dumpFile($filename, $dumper->dump(), null);
|
||||
// discard chmod failure (some filesystem may not support it)
|
||||
@chmod($filename, 0666 & ~umask());
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
We don't need to be able to add more extensions.
|
||||
* more types can be registered with the form.type tag
|
||||
* more type extensions can be registered with the form.type_extension tag
|
||||
* more type_guessers can be registered with the form.type.type_guesser tag
|
||||
* more type_guessers can be registered with the form.type_guesser tag
|
||||
-->
|
||||
<argument type="service" id="form.extension" />
|
||||
</argument>
|
||||
|
@ -1,6 +1,9 @@
|
||||
<select
|
||||
<?php if ($required && $empty_value === null && $empty_value_in_choices === false && $multiple === false):
|
||||
$required = false;
|
||||
endif; ?>
|
||||
<?php echo $view['form']->block($form, 'widget_attributes', array(
|
||||
'required' => $required && (null !== $empty_value || $empty_value_in_choices)
|
||||
'required' => $required
|
||||
)) ?>
|
||||
<?php if ($multiple): ?> multiple="multiple"<?php endif ?>
|
||||
>
|
||||
|
@ -24,7 +24,7 @@ use Doctrine\DBAL\Schema\SchemaException;
|
||||
class InitAclCommand extends ContainerAwareCommand
|
||||
{
|
||||
/**
|
||||
* @see Command
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
@ -47,7 +47,7 @@ EOF
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Command::execute()
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
|
@ -96,7 +96,7 @@ class Cookie
|
||||
throw new \UnexpectedValueException(sprintf('The cookie expiration time "%s" is not valid.'), $this->expires);
|
||||
}
|
||||
|
||||
$cookie .= '; expires='.substr($dateTime->format(self::$dateFormats[0]), 0, -5);
|
||||
$cookie .= '; expires='.str_replace('+0000', '', $dateTime->format(self::$dateFormats[0]));
|
||||
}
|
||||
|
||||
if ('' !== $this->domain) {
|
||||
|
@ -95,10 +95,12 @@ class ConfigCache
|
||||
{
|
||||
$mode = 0666 & ~umask();
|
||||
$filesystem = new Filesystem();
|
||||
$filesystem->dumpFile($this->file, $content, $mode);
|
||||
$filesystem->dumpFile($this->file, $content, null);
|
||||
@chmod($this->file, $mode);
|
||||
|
||||
if (null !== $metadata && true === $this->debug) {
|
||||
$filesystem->dumpFile($this->getMetaFile(), serialize($metadata), $mode);
|
||||
$filesystem->dumpFile($this->getMetaFile(), serialize($metadata), null);
|
||||
@chmod($this->getMetaFile(), $mode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,6 @@ class DelegatingLoader extends Loader
|
||||
*/
|
||||
public function supports($resource, $type = null)
|
||||
{
|
||||
return false === $this->resolver->resolve($resource, $type) ? false : true;
|
||||
return false !== $this->resolver->resolve($resource, $type);
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +80,7 @@ class XmlUtilsTest extends \PHPUnit_Framework_TestCase
|
||||
array(array('foo' => null), '<foo />'),
|
||||
array(array('foo' => 'bar'), '<foo>bar</foo>'),
|
||||
array(array('foo' => array('foo' => 'bar')), '<foo foo="bar"/>'),
|
||||
array(array('foo' => array('foo' => 0)), '<foo><foo>0</foo></foo>'),
|
||||
array(array('foo' => array('foo' => 'bar')), '<foo><foo>bar</foo></foo>'),
|
||||
array(array('foo' => array('foo' => 'bar', 'value' => 'text')), '<foo foo="bar">text</foo>'),
|
||||
array(array('foo' => array('attr' => 'bar', 'foo' => 'text')), '<foo attr="bar"><foo>text</foo></foo>'),
|
||||
@ -132,6 +133,45 @@ class XmlUtilsTest extends \PHPUnit_Framework_TestCase
|
||||
array(6, '0b0110'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testLoadEmptyXmlFile()
|
||||
{
|
||||
$file = __DIR__.'/../Fixtures/foo.xml';
|
||||
$this->setExpectedException('InvalidArgumentException', 'File '.$file.' does not contain valid XML, it is empty.');
|
||||
XmlUtils::loadFile($file);
|
||||
}
|
||||
|
||||
// test for issue https://github.com/symfony/symfony/issues/9731
|
||||
public function testLoadWrongEmptyXMLWithErrorHandler()
|
||||
{
|
||||
$originalDisableEntities = libxml_disable_entity_loader(false);
|
||||
$errorReporting = error_reporting(-1);
|
||||
|
||||
set_error_handler(function ($errno, $errstr) {
|
||||
throw new \Exception($errstr, $errno);
|
||||
});
|
||||
|
||||
$file = __DIR__.'/../Fixtures/foo.xml';
|
||||
try {
|
||||
XmlUtils::loadFile($file);
|
||||
$this->fail('An exception should have been raised');
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$this->assertEquals(sprintf('File %s does not contain valid XML, it is empty.', $file), $e->getMessage());
|
||||
}
|
||||
|
||||
restore_error_handler();
|
||||
error_reporting($errorReporting);
|
||||
|
||||
$disableEntities = libxml_disable_entity_loader(true);
|
||||
libxml_disable_entity_loader($disableEntities);
|
||||
|
||||
libxml_disable_entity_loader($originalDisableEntities);
|
||||
|
||||
$this->assertFalse($disableEntities);
|
||||
|
||||
// should not throw an exception
|
||||
XmlUtils::loadFile(__DIR__.'/../Fixtures/Util/valid.xml', __DIR__.'/../Fixtures/Util/schema.xsd');
|
||||
}
|
||||
}
|
||||
|
||||
interface Validator
|
||||
|
@ -40,13 +40,18 @@ class XmlUtils
|
||||
*/
|
||||
public static function loadFile($file, $schemaOrCallable = null)
|
||||
{
|
||||
$content = @file_get_contents($file);
|
||||
if ('' === trim($content)) {
|
||||
throw new \InvalidArgumentException(sprintf('File %s does not contain valid XML, it is empty.', $file));
|
||||
}
|
||||
|
||||
$internalErrors = libxml_use_internal_errors(true);
|
||||
$disableEntities = libxml_disable_entity_loader(true);
|
||||
libxml_clear_errors();
|
||||
|
||||
$dom = new \DOMDocument();
|
||||
$dom->validateOnParse = true;
|
||||
if (!$dom->loadXML(file_get_contents($file), LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) {
|
||||
if (!$dom->loadXML($content, LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) {
|
||||
libxml_disable_entity_loader($disableEntities);
|
||||
|
||||
throw new \InvalidArgumentException(implode("\n", static::getXmlErrors($internalErrors)));
|
||||
@ -132,7 +137,7 @@ class XmlUtils
|
||||
$nodeValue = false;
|
||||
foreach ($element->childNodes as $node) {
|
||||
if ($node instanceof \DOMText) {
|
||||
if (trim($node->nodeValue)) {
|
||||
if ('' !== trim($node->nodeValue)) {
|
||||
$nodeValue = trim($node->nodeValue);
|
||||
$empty = false;
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
|
||||
$translator->registerExtension(new HtmlExtension($translator));
|
||||
$document = new \DOMDocument();
|
||||
$document->strictErrorChecking = false;
|
||||
libxml_use_internal_errors(true);
|
||||
$internalErrors = libxml_use_internal_errors(true);
|
||||
$document->loadHTMLFile(__DIR__.'/Fixtures/ids.html');
|
||||
$document = simplexml_import_dom($document);
|
||||
$elements = $document->xpath($translator->cssToXPath($css));
|
||||
@ -59,6 +59,8 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertTrue(in_array($element->attributes()->id, $elementsId));
|
||||
}
|
||||
}
|
||||
libxml_clear_errors();
|
||||
libxml_use_internal_errors($internalErrors);
|
||||
}
|
||||
|
||||
/** @dataProvider getHtmlShakespearTestData */
|
||||
|
@ -151,7 +151,7 @@ class Crawler extends \SplObjectStorage
|
||||
*/
|
||||
public function addHtmlContent($content, $charset = 'UTF-8')
|
||||
{
|
||||
$current = libxml_use_internal_errors(true);
|
||||
$internalErrors = libxml_use_internal_errors(true);
|
||||
$disableEntities = libxml_disable_entity_loader(true);
|
||||
|
||||
$dom = new \DOMDocument('1.0', $charset);
|
||||
@ -171,9 +171,11 @@ class Crawler extends \SplObjectStorage
|
||||
}
|
||||
}
|
||||
|
||||
@$dom->loadHTML($content);
|
||||
if ('' !== trim($content)) {
|
||||
@$dom->loadHTML($content);
|
||||
}
|
||||
|
||||
libxml_use_internal_errors($current);
|
||||
libxml_use_internal_errors($internalErrors);
|
||||
libxml_disable_entity_loader($disableEntities);
|
||||
|
||||
$this->addDocument($dom);
|
||||
@ -215,14 +217,18 @@ class Crawler extends \SplObjectStorage
|
||||
$content = str_replace('xmlns', 'ns', $content);
|
||||
}
|
||||
|
||||
$current = libxml_use_internal_errors(true);
|
||||
$internalErrors = libxml_use_internal_errors(true);
|
||||
$disableEntities = libxml_disable_entity_loader(true);
|
||||
|
||||
$dom = new \DOMDocument('1.0', $charset);
|
||||
$dom->validateOnParse = true;
|
||||
@$dom->loadXML($content, LIBXML_NONET);
|
||||
|
||||
libxml_use_internal_errors($current);
|
||||
if ('' !== trim($content)) {
|
||||
// remove the default namespace to make XPath expressions simpler
|
||||
@$dom->loadXML(str_replace('xmlns', 'ns', $content), LIBXML_NONET);
|
||||
}
|
||||
|
||||
libxml_use_internal_errors($internalErrors);
|
||||
libxml_disable_entity_loader($disableEntities);
|
||||
|
||||
$this->addDocument($dom);
|
||||
|
@ -143,8 +143,13 @@ class Form extends Link implements \ArrayAccess
|
||||
*/
|
||||
public function getPhpValues()
|
||||
{
|
||||
$qs = http_build_query($this->getValues(), '', '&');
|
||||
parse_str($qs, $values);
|
||||
$values = array();
|
||||
foreach ($this->getValues() as $name => $value) {
|
||||
$qs = http_build_query(array($name => $value), '', '&');
|
||||
parse_str($qs, $expandedValue);
|
||||
$varName = substr($name, 0, strlen(key($expandedValue)));
|
||||
$values = array_replace_recursive($values, array($varName => current($expandedValue)));
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
@ -161,8 +166,13 @@ class Form extends Link implements \ArrayAccess
|
||||
*/
|
||||
public function getPhpFiles()
|
||||
{
|
||||
$qs = http_build_query($this->getFiles(), '', '&');
|
||||
parse_str($qs, $values);
|
||||
$values = array();
|
||||
foreach ($this->getFiles() as $name => $value) {
|
||||
$qs = http_build_query(array($name => $value), '', '&');
|
||||
parse_str($qs, $expandedValue);
|
||||
$varName = substr($name, 0, strlen(key($expandedValue)));
|
||||
$values = array_replace_recursive($values, array($varName => current($expandedValue)));
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
@ -414,8 +424,7 @@ class Form extends Link implements \ArrayAccess
|
||||
|
||||
// restore the original name of the input node
|
||||
$this->button->setAttribute('name', $name);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$this->set(new Field\InputFormField($document->importNode($this->button, true)));
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ class CrawlerTest extends \PHPUnit_Framework_TestCase
|
||||
*/
|
||||
public function testAddHtmlContentWithErrors()
|
||||
{
|
||||
libxml_use_internal_errors(true);
|
||||
$internalErrors = libxml_use_internal_errors(true);
|
||||
|
||||
$crawler = new Crawler();
|
||||
$crawler->addHtmlContent(<<<EOF
|
||||
@ -150,7 +150,7 @@ EOF
|
||||
$this->assertEquals("Tag nav invalid\n", $errors[0]->message);
|
||||
|
||||
libxml_clear_errors();
|
||||
libxml_use_internal_errors(false);
|
||||
libxml_use_internal_errors($internalErrors);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -180,7 +180,7 @@ EOF
|
||||
*/
|
||||
public function testAddXmlContentWithErrors()
|
||||
{
|
||||
libxml_use_internal_errors(true);
|
||||
$internalErrors = libxml_use_internal_errors(true);
|
||||
|
||||
$crawler = new Crawler();
|
||||
$crawler->addXmlContent(<<<EOF
|
||||
@ -198,7 +198,7 @@ EOF
|
||||
$this->assertTrue(count(libxml_get_errors()) > 1);
|
||||
|
||||
libxml_clear_errors();
|
||||
libxml_use_internal_errors(false);
|
||||
libxml_use_internal_errors($internalErrors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -411,6 +411,12 @@ class FormTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$form = $this->createForm('<form><input type="text" name="foo[bar]" value="foo" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
|
||||
$this->assertEquals(array('foo' => array('bar' => 'foo'), 'bar' => 'bar'), $form->getPhpValues(), '->getPhpValues() converts keys with [] to arrays');
|
||||
|
||||
$form = $this->createForm('<form><input type="text" name="fo.o[ba.r]" value="foo" /><input type="text" name="ba r" value="bar" /><input type="submit" /></form>');
|
||||
$this->assertEquals(array('fo.o' => array('ba.r' => 'foo'), 'ba r' => 'bar'), $form->getPhpValues(), '->getPhpValues() preserves periods and spaces in names');
|
||||
|
||||
$form = $this->createForm('<form><input type="text" name="fo.o[ba.r][]" value="foo" /><input type="text" name="fo.o[ba.r][ba.z]" value="bar" /><input type="submit" /></form>');
|
||||
$this->assertEquals(array('fo.o' => array('ba.r' => array('foo', 'ba.z' => 'bar'))), $form->getPhpValues(), '->getPhpValues() preserves periods and spaces in names recursively');
|
||||
}
|
||||
|
||||
public function testGetFiles()
|
||||
@ -438,6 +444,12 @@ class FormTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
|
||||
$this->assertEquals(array('foo' => array('bar' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), $form->getPhpFiles(), '->getPhpFiles() converts keys with [] to arrays');
|
||||
|
||||
$form = $this->createForm('<form method="post"><input type="file" name="f.o o[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
|
||||
$this->assertEquals(array('f.o o' => array('bar' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), $form->getPhpFiles(), '->getPhpFiles() preserves periods and spaces in names');
|
||||
|
||||
$form = $this->createForm('<form method="post"><input type="file" name="f.o o[bar][ba.z]" /><input type="file" name="f.o o[bar][]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
|
||||
$this->assertEquals(array('f.o o' => array('bar' => array('ba.z' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0), array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)))), $form->getPhpFiles(), '->getPhpFiles() preserves periods and spaces in names recursively');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,11 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
2.3.12
|
||||
------
|
||||
|
||||
* deprecated dumpFile() file mode argument.
|
||||
|
||||
2.3.0
|
||||
-----
|
||||
|
||||
|
@ -433,10 +433,11 @@ class Filesystem
|
||||
/**
|
||||
* Atomically dumps content into a file.
|
||||
*
|
||||
* @param string $filename The file to be written to.
|
||||
* @param string $content The data to write into the file.
|
||||
* @param integer $mode The file mode (octal).
|
||||
* @throws IOException If the file cannot be written to.
|
||||
* @param string $filename The file to be written to.
|
||||
* @param string $content The data to write into the file.
|
||||
* @param null|integer $mode The file mode (octal). If null, file permissions are not modified
|
||||
* Deprecated since version 2.3.12, to be removed in 3.0.
|
||||
* @throws IOException If the file cannot be written to.
|
||||
*/
|
||||
public function dumpFile($filename, $content, $mode = 0666)
|
||||
{
|
||||
@ -455,7 +456,9 @@ class Filesystem
|
||||
}
|
||||
|
||||
$this->rename($tmpFile, $filename, true);
|
||||
$this->chmod($filename, $mode);
|
||||
if (null !== $mode) {
|
||||
$this->chmod($filename, $mode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -879,6 +879,21 @@ class FilesystemTest extends FilesystemTestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testDumpFileWithNullMode()
|
||||
{
|
||||
$filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt';
|
||||
|
||||
$this->filesystem->dumpFile($filename, 'bar', null);
|
||||
|
||||
$this->assertFileExists($filename);
|
||||
$this->assertSame('bar', file_get_contents($filename));
|
||||
|
||||
// skip mode check on Windows
|
||||
if (!defined('PHP_WINDOWS_VERSION_MAJOR')) {
|
||||
$this->assertEquals(600, $this->getFilePermissions($filename));
|
||||
}
|
||||
}
|
||||
|
||||
public function testDumpFileOverwritesAnExistingFile()
|
||||
{
|
||||
$filename = $this->workspace.DIRECTORY_SEPARATOR.'foo.txt';
|
||||
|
@ -128,11 +128,13 @@ class ObjectChoiceList extends ChoiceList
|
||||
if (null === $group) {
|
||||
$groupedChoices[$i] = $choice;
|
||||
} else {
|
||||
if (!isset($groupedChoices[$group])) {
|
||||
$groupedChoices[$group] = array();
|
||||
$groupName = (string) $group;
|
||||
|
||||
if (!isset($groupedChoices[$groupName])) {
|
||||
$groupedChoices[$groupName] = array();
|
||||
}
|
||||
|
||||
$groupedChoices[$group][$i] = $choice;
|
||||
$groupedChoices[$groupName][$i] = $choice;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,11 +163,17 @@ class ResizeFormListener implements EventSubscriberInterface
|
||||
// The data mapper only adds, but does not remove items, so do this
|
||||
// here
|
||||
if ($this->allowDelete) {
|
||||
foreach ($data as $name => $childData) {
|
||||
$toDelete = array();
|
||||
|
||||
foreach ($data as $name => $child) {
|
||||
if (!$form->has($name)) {
|
||||
unset($data[$name]);
|
||||
$toDelete[] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($toDelete as $name) {
|
||||
unset($data[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
$event->setData($data);
|
||||
|
@ -636,6 +636,7 @@ abstract class AbstractLayoutTest extends \Symfony\Component\Form\Test\FormInteg
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'choice', array('&a'), array(
|
||||
'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'),
|
||||
'required' => true,
|
||||
'multiple' => true,
|
||||
'expanded' => false,
|
||||
));
|
||||
@ -643,6 +644,7 @@ abstract class AbstractLayoutTest extends \Symfony\Component\Form\Test\FormInteg
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/select
|
||||
[@name="name[]"]
|
||||
[@required="required"]
|
||||
[@multiple="multiple"]
|
||||
[
|
||||
./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
|
||||
|
@ -250,7 +250,20 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals(array(), $event->getData());
|
||||
}
|
||||
|
||||
public function testOnSubmitDealsWithIteratorAggregate()
|
||||
public function testOnSubmitDealsWithObjectBackedIteratorAggregate()
|
||||
{
|
||||
$this->form->add($this->getForm('1'));
|
||||
|
||||
$data = new \ArrayObject(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
||||
$event = new FormEvent($this->form, $data);
|
||||
$listener = new ResizeFormListener('text', array(), false, true);
|
||||
$listener->onSubmit($event);
|
||||
|
||||
$this->assertArrayNotHasKey(0, $event->getData());
|
||||
$this->assertArrayNotHasKey(2, $event->getData());
|
||||
}
|
||||
|
||||
public function testOnSubmitDealsWithArrayBackedIteratorAggregate()
|
||||
{
|
||||
$this->form->add($this->getForm('1'));
|
||||
|
||||
|
@ -91,10 +91,6 @@ class UploadedFile extends File
|
||||
*/
|
||||
public function __construct($path, $originalName, $mimeType = null, $size = null, $error = null, $test = false)
|
||||
{
|
||||
if (!ini_get('file_uploads')) {
|
||||
throw new FileException(sprintf('Unable to create UploadedFile because "file_uploads" is disabled in your php.ini file (%s)', get_cfg_var('cfg_file_path')));
|
||||
}
|
||||
|
||||
$this->originalName = $this->getName($originalName);
|
||||
$this->mimeType = $mimeType ?: 'application/octet-stream';
|
||||
$this->size = $size;
|
||||
@ -108,7 +104,7 @@ class UploadedFile extends File
|
||||
* Returns the original file name.
|
||||
*
|
||||
* It is extracted from the request from which the file has been uploaded.
|
||||
* Then is should not be considered as a safe value.
|
||||
* Then it should not be considered as a safe value.
|
||||
*
|
||||
* @return string|null The original name
|
||||
*
|
||||
@ -123,7 +119,7 @@ class UploadedFile extends File
|
||||
* Returns the original file extension
|
||||
*
|
||||
* It is extracted from the original file name that was uploaded.
|
||||
* Then is should not be considered as a safe value.
|
||||
* Then it should not be considered as a safe value.
|
||||
*
|
||||
* @return string The extension
|
||||
*/
|
||||
@ -181,7 +177,7 @@ class UploadedFile extends File
|
||||
* Returns the file size.
|
||||
*
|
||||
* It is extracted from the request from which the file has been uploaded.
|
||||
* Then is should not be considered as a safe value.
|
||||
* Then it should not be considered as a safe value.
|
||||
*
|
||||
* @return integer|null The file size
|
||||
*
|
||||
|
@ -1048,11 +1048,16 @@ class Response
|
||||
*/
|
||||
public function getVary()
|
||||
{
|
||||
if (!$vary = $this->headers->get('Vary')) {
|
||||
if (!$vary = $this->headers->get('Vary', null, false)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return is_array($vary) ? $vary : preg_split('/[\s,]+/', $vary);
|
||||
$ret = array();
|
||||
foreach ($vary as $item) {
|
||||
$ret = array_merge($ret, preg_split('/[\s,]+/', $item));
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -341,6 +341,16 @@ class ResponseTest extends ResponseTestCase
|
||||
$response = new Response();
|
||||
$response->headers->set('Vary', 'Accept-Language,User-Agent, X-Foo');
|
||||
$this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->getVary() parses multiple header name values separated by commas');
|
||||
|
||||
$vary = array('Accept-Language', 'User-Agent', 'X-foo');
|
||||
|
||||
$response = new Response();
|
||||
$response->headers->set('Vary', $vary);
|
||||
$this->assertEquals($vary, $response->getVary(), '->getVary() parses multiple header name values in arrays');
|
||||
|
||||
$response = new Response();
|
||||
$response->headers->set('Vary', 'Accept-Language, User-Agent, X-foo');
|
||||
$this->assertEquals($vary, $response->getVary(), '->getVary() parses multiple header name values in arrays');
|
||||
}
|
||||
|
||||
public function testSetVary()
|
||||
@ -353,7 +363,7 @@ class ResponseTest extends ResponseTestCase
|
||||
$this->assertEquals(array('Accept-Language', 'User-Agent'), $response->getVary(), '->setVary() replace the vary header by default');
|
||||
|
||||
$response->setVary('X-Foo', false);
|
||||
$this->assertEquals(array('Accept-Language', 'User-Agent'), $response->getVary(), '->setVary() doesn\'t change the Vary header if replace is set to false');
|
||||
$this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->setVary() doesn\'t wipe out earlier Vary headers if replace is set to false');
|
||||
}
|
||||
|
||||
public function testDefaultContentType()
|
||||
|
@ -18,6 +18,9 @@
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/expression-language": "~2.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": { "Symfony\\Component\\HttpFoundation\\": "" },
|
||||
"classmap": [ "Symfony/Component/HttpFoundation/Resources/stubs" ]
|
||||
|
4
src/Symfony/Component/HttpKernel/.gitignore
vendored
4
src/Symfony/Component/HttpKernel/.gitignore
vendored
@ -1,5 +1,5 @@
|
||||
vendor/
|
||||
composer.lock
|
||||
phpunit.xml
|
||||
Tests/ProjectContainer.php
|
||||
Tests/classes.map
|
||||
Tests/Fixtures/cache/
|
||||
Tests/Fixtures/logs/
|
||||
|
@ -307,7 +307,7 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
|
||||
}
|
||||
}
|
||||
|
||||
$cookie .= '; expires='.substr(\DateTime::createFromFormat('U', $expires, new \DateTimeZone('UTC'))->format('D, d-M-Y H:i:s T'), 0, -5);
|
||||
$cookie .= '; expires='.str_replace('+0000', '', \DateTime::createFromFormat('U', $expires, new \DateTimeZone('GMT'))->format('D, d-M-Y H:i:s T'));
|
||||
}
|
||||
|
||||
if ($domain) {
|
||||
|
@ -308,7 +308,7 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
|
||||
if ($this->options['allow_reload'] && $request->isNoCache()) {
|
||||
$this->record($request, 'reload');
|
||||
|
||||
return $this->fetch($request);
|
||||
return $this->fetch($request, $catch);
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -27,7 +27,7 @@ use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
interface KernelInterface extends HttpKernelInterface, \Serializable
|
||||
{
|
||||
/**
|
||||
* Returns an array of bundles to registers.
|
||||
* Returns an array of bundles to register.
|
||||
*
|
||||
* @return BundleInterface[] An array of bundle instances.
|
||||
*
|
||||
@ -36,7 +36,7 @@ interface KernelInterface extends HttpKernelInterface, \Serializable
|
||||
public function registerBundles();
|
||||
|
||||
/**
|
||||
* Loads the container configuration
|
||||
* Loads the container configuration.
|
||||
*
|
||||
* @param LoaderInterface $loader A LoaderInterface instance
|
||||
*
|
||||
@ -125,7 +125,7 @@ interface KernelInterface extends HttpKernelInterface, \Serializable
|
||||
public function locateResource($name, $dir = null, $first = true);
|
||||
|
||||
/**
|
||||
* Gets the name of the kernel
|
||||
* Gets the name of the kernel.
|
||||
*
|
||||
* @return string The kernel name
|
||||
*
|
||||
|
@ -923,6 +923,17 @@ class HttpCacheTest extends HttpCacheTestCase
|
||||
$this->assertExceptionsAreCaught();
|
||||
}
|
||||
|
||||
public function testShouldCatchExceptionsWhenReloadingAndNoCacheRequest()
|
||||
{
|
||||
$this->catchExceptions();
|
||||
|
||||
$this->setNextResponse();
|
||||
$this->cacheConfig['allow_reload'] = true;
|
||||
$this->request('GET', '/', array(), array(), false, array('Pragma' => 'no-cache'));
|
||||
|
||||
$this->assertExceptionsAreCaught();
|
||||
}
|
||||
|
||||
public function testShouldNotCatchExceptions()
|
||||
{
|
||||
$this->catchExceptions(false);
|
||||
|
@ -103,7 +103,7 @@ class HttpCacheTestCase extends \PHPUnit_Framework_TestCase
|
||||
$this->assertFalse($this->kernel->isCatchingExceptions());
|
||||
}
|
||||
|
||||
public function request($method, $uri = '/', $server = array(), $cookies = array(), $esi = false)
|
||||
public function request($method, $uri = '/', $server = array(), $cookies = array(), $esi = false, $headers = array())
|
||||
{
|
||||
if (null === $this->kernel) {
|
||||
throw new \LogicException('You must call setNextResponse() before calling request().');
|
||||
@ -118,6 +118,7 @@ class HttpCacheTestCase extends \PHPUnit_Framework_TestCase
|
||||
$this->esi = $esi ? new Esi() : null;
|
||||
$this->cache = new HttpCache($this->kernel, $this->store, $this->esi, $this->cacheConfig);
|
||||
$this->request = Request::create($uri, $method, array(), $cookies, array(), $server);
|
||||
$this->request->headers->add($headers);
|
||||
|
||||
$this->response = $this->cache->handle($this->request, HttpKernelInterface::MASTER_REQUEST, $this->catch);
|
||||
|
||||
|
@ -276,7 +276,7 @@ class OptionsResolver implements OptionsResolverInterface
|
||||
ksort($diff);
|
||||
|
||||
throw new MissingOptionsException(sprintf(
|
||||
count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.',
|
||||
count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.',
|
||||
implode('", "', array_keys($diff))
|
||||
));
|
||||
}
|
||||
|
@ -61,8 +61,8 @@ class Process
|
||||
private $enhanceSigchildCompatibility;
|
||||
private $process;
|
||||
private $status = self::STATUS_READY;
|
||||
private $incrementalOutputOffset;
|
||||
private $incrementalErrorOutputOffset;
|
||||
private $incrementalOutputOffset = 0;
|
||||
private $incrementalErrorOutputOffset = 0;
|
||||
private $tty;
|
||||
private $pty;
|
||||
|
||||
@ -193,7 +193,8 @@ class Process
|
||||
*
|
||||
* @return integer The exit status code
|
||||
*
|
||||
* @throws RuntimeException When process can't be launch or is stopped
|
||||
* @throws RuntimeException When process can't be launched
|
||||
* @throws RuntimeException When process stopped after receiving signal
|
||||
* @throws LogicException In case a callback is provided and output has been disabled
|
||||
*
|
||||
* @api
|
||||
@ -244,7 +245,7 @@ class Process
|
||||
*
|
||||
* @return Process The process itself
|
||||
*
|
||||
* @throws RuntimeException When process can't be launch or is stopped
|
||||
* @throws RuntimeException When process can't be launched
|
||||
* @throws RuntimeException When process is already running
|
||||
* @throws LogicException In case a callback is provided and output has been disabled
|
||||
*/
|
||||
@ -265,7 +266,11 @@ class Process
|
||||
$commandline = $this->commandline;
|
||||
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD') && $this->enhanceWindowsCompatibility) {
|
||||
$commandline = 'cmd /V:ON /E:ON /C "'.$commandline.'"';
|
||||
$commandline = 'cmd /V:ON /E:ON /C "('.$commandline.')"';
|
||||
foreach ($this->processPipes->getFiles() as $offset => $filename) {
|
||||
$commandline .= ' '.$offset.'>'.$filename;
|
||||
}
|
||||
|
||||
if (!isset($this->options['bypass_shell'])) {
|
||||
$this->options['bypass_shell'] = true;
|
||||
}
|
||||
@ -281,8 +286,6 @@ class Process
|
||||
$this->processPipes->unblock();
|
||||
|
||||
if ($this->tty) {
|
||||
$this->status = self::STATUS_TERMINATED;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -301,7 +304,7 @@ class Process
|
||||
*
|
||||
* @return Process The new process
|
||||
*
|
||||
* @throws RuntimeException When process can't be launch or is stopped
|
||||
* @throws RuntimeException When process can't be launched
|
||||
* @throws RuntimeException When process is already running
|
||||
*
|
||||
* @see start()
|
||||
@ -331,9 +334,12 @@ class Process
|
||||
*
|
||||
* @throws RuntimeException When process timed out
|
||||
* @throws RuntimeException When process stopped after receiving signal
|
||||
* @throws LogicException When process is not yet started
|
||||
*/
|
||||
public function wait($callback = null)
|
||||
{
|
||||
$this->requireProcessIsStarted(__FUNCTION__);
|
||||
|
||||
$this->updateStatus(false);
|
||||
if (null !== $callback) {
|
||||
$this->callback = $this->buildCallback($callback);
|
||||
@ -351,10 +357,6 @@ class Process
|
||||
}
|
||||
|
||||
if ($this->processInformation['signaled']) {
|
||||
if ($this->isSigchildEnabled()) {
|
||||
throw new RuntimeException('The process has been signaled.');
|
||||
}
|
||||
|
||||
throw new RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig']));
|
||||
}
|
||||
|
||||
@ -383,6 +385,7 @@ class Process
|
||||
* Sends a POSIX signal to the process.
|
||||
*
|
||||
* @param integer $signal A valid POSIX signal (see http://www.php.net/manual/en/pcntl.constants.php)
|
||||
*
|
||||
* @return Process
|
||||
*
|
||||
* @throws LogicException In case the process is not running
|
||||
@ -391,17 +394,7 @@ class Process
|
||||
*/
|
||||
public function signal($signal)
|
||||
{
|
||||
if (!$this->isRunning()) {
|
||||
throw new LogicException('Can not send signal on a non running process.');
|
||||
}
|
||||
|
||||
if ($this->isSigchildEnabled()) {
|
||||
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
|
||||
}
|
||||
|
||||
if (true !== @proc_terminate($this->process, $signal)) {
|
||||
throw new RuntimeException(sprintf('Error while sending signal `%d`.', $signal));
|
||||
}
|
||||
$this->doSignal($signal, true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -460,7 +453,8 @@ class Process
|
||||
*
|
||||
* @return string The process output
|
||||
*
|
||||
* @throws LogicException in case the output has been disabled.
|
||||
* @throws LogicException in case the output has been disabled
|
||||
* @throws LogicException In case the process is not started
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
@ -470,6 +464,8 @@ class Process
|
||||
throw new LogicException('Output has been disabled.');
|
||||
}
|
||||
|
||||
$this->requireProcessIsStarted(__FUNCTION__);
|
||||
|
||||
$this->readPipes(false, defined('PHP_WINDOWS_VERSION_BUILD') ? !$this->processInformation['running'] : true);
|
||||
|
||||
return $this->stdout;
|
||||
@ -481,12 +477,15 @@ class Process
|
||||
* In comparison with the getOutput method which always return the whole
|
||||
* output, this one returns the new output since the last call.
|
||||
*
|
||||
* @throws LogicException in case the output has been disabled.
|
||||
* @throws LogicException in case the output has been disabled
|
||||
* @throws LogicException In case the process is not started
|
||||
*
|
||||
* @return string The process output since the last call
|
||||
*/
|
||||
public function getIncrementalOutput()
|
||||
{
|
||||
$this->requireProcessIsStarted(__FUNCTION__);
|
||||
|
||||
$data = $this->getOutput();
|
||||
|
||||
$latest = substr($data, $this->incrementalOutputOffset);
|
||||
@ -513,7 +512,8 @@ class Process
|
||||
*
|
||||
* @return string The process error output
|
||||
*
|
||||
* @throws LogicException in case the output has been disabled.
|
||||
* @throws LogicException in case the output has been disabled
|
||||
* @throws LogicException In case the process is not started
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
@ -523,6 +523,8 @@ class Process
|
||||
throw new LogicException('Output has been disabled.');
|
||||
}
|
||||
|
||||
$this->requireProcessIsStarted(__FUNCTION__);
|
||||
|
||||
$this->readPipes(false, defined('PHP_WINDOWS_VERSION_BUILD') ? !$this->processInformation['running'] : true);
|
||||
|
||||
return $this->stderr;
|
||||
@ -535,12 +537,15 @@ class Process
|
||||
* whole error output, this one returns the new error output since the last
|
||||
* call.
|
||||
*
|
||||
* @throws LogicException in case the output has been disabled.
|
||||
* @throws LogicException in case the output has been disabled
|
||||
* @throws LogicException In case the process is not started
|
||||
*
|
||||
* @return string The process error output since the last call
|
||||
*/
|
||||
public function getIncrementalErrorOutput()
|
||||
{
|
||||
$this->requireProcessIsStarted(__FUNCTION__);
|
||||
|
||||
$data = $this->getErrorOutput();
|
||||
|
||||
$latest = substr($data, $this->incrementalErrorOutputOffset);
|
||||
@ -565,7 +570,7 @@ class Process
|
||||
/**
|
||||
* Returns the exit code returned by the process.
|
||||
*
|
||||
* @return integer The exit status code
|
||||
* @return null|integer The exit status code, null if the Process is not terminated
|
||||
*
|
||||
* @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
|
||||
*
|
||||
@ -574,7 +579,7 @@ class Process
|
||||
public function getExitCode()
|
||||
{
|
||||
if ($this->isSigchildEnabled() && !$this->enhanceSigchildCompatibility) {
|
||||
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(false);
|
||||
@ -588,14 +593,18 @@ class Process
|
||||
* This method relies on the Unix exit code status standardization
|
||||
* and might not be relevant for other operating systems.
|
||||
*
|
||||
* @return string A string representation for the exit status code
|
||||
* @return null|string A string representation for the exit status code, null if the Process is not terminated.
|
||||
*
|
||||
* @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled
|
||||
*
|
||||
* @see http://tldp.org/LDP/abs/html/exitcodes.html
|
||||
* @see http://en.wikipedia.org/wiki/Unix_signal
|
||||
*/
|
||||
public function getExitCodeText()
|
||||
{
|
||||
$exitcode = $this->getExitCode();
|
||||
if (null === $exitcode = $this->getExitCode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
return isset(self::$exitCodes[$exitcode]) ? self::$exitCodes[$exitcode] : 'Unknown error';
|
||||
}
|
||||
@ -620,13 +629,16 @@ class Process
|
||||
* @return Boolean
|
||||
*
|
||||
* @throws RuntimeException In case --enable-sigchild is activated
|
||||
* @throws LogicException In case the process is not terminated
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function hasBeenSignaled()
|
||||
{
|
||||
$this->requireProcessIsTerminated(__FUNCTION__);
|
||||
|
||||
if ($this->isSigchildEnabled()) {
|
||||
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(false);
|
||||
@ -642,13 +654,16 @@ class Process
|
||||
* @return integer
|
||||
*
|
||||
* @throws RuntimeException In case --enable-sigchild is activated
|
||||
* @throws LogicException In case the process is not terminated
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getTermSignal()
|
||||
{
|
||||
$this->requireProcessIsTerminated(__FUNCTION__);
|
||||
|
||||
if ($this->isSigchildEnabled()) {
|
||||
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(false);
|
||||
@ -663,10 +678,14 @@ class Process
|
||||
*
|
||||
* @return Boolean
|
||||
*
|
||||
* @throws LogicException In case the process is not terminated
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function hasBeenStopped()
|
||||
{
|
||||
$this->requireProcessIsTerminated(__FUNCTION__);
|
||||
|
||||
$this->updateStatus(false);
|
||||
|
||||
return $this->processInformation['stopped'];
|
||||
@ -679,10 +698,14 @@ class Process
|
||||
*
|
||||
* @return integer
|
||||
*
|
||||
* @throws LogicException In case the process is not terminated
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getStopSignal()
|
||||
{
|
||||
$this->requireProcessIsTerminated(__FUNCTION__);
|
||||
|
||||
$this->updateStatus(false);
|
||||
|
||||
return $this->processInformation['stopsig'];
|
||||
@ -754,6 +777,12 @@ class Process
|
||||
{
|
||||
$timeoutMicro = microtime(true) + $timeout;
|
||||
if ($this->isRunning()) {
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD') && !$this->isSigchildEnabled()) {
|
||||
exec(sprintf("taskkill /F /T /PID %d 2>&1", $this->getPid()), $output, $exitCode);
|
||||
if ($exitCode > 0) {
|
||||
throw new RuntimeException('Unable to kill the process');
|
||||
}
|
||||
}
|
||||
proc_terminate($this->process);
|
||||
do {
|
||||
usleep(1000);
|
||||
@ -761,7 +790,11 @@ class Process
|
||||
|
||||
if ($this->isRunning() && !$this->isSigchildEnabled()) {
|
||||
if (null !== $signal || defined('SIGKILL')) {
|
||||
$this->signal($signal ?: SIGKILL);
|
||||
// avoid exception here :
|
||||
// process is supposed to be running, but it might have stop
|
||||
// just after this line.
|
||||
// in any case, let's silently discard the error, we can not do anything
|
||||
$this->doSignal($signal ?: SIGKILL, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -771,8 +804,6 @@ class Process
|
||||
$this->close();
|
||||
}
|
||||
|
||||
$this->status = self::STATUS_TERMINATED;
|
||||
|
||||
return $this->exitcode;
|
||||
}
|
||||
|
||||
@ -1110,6 +1141,10 @@ class Process
|
||||
*/
|
||||
public function checkTimeout()
|
||||
{
|
||||
if ($this->status !== self::STATUS_STARTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (null !== $this->timeout && $this->timeout < microtime(true) - $this->starttime) {
|
||||
$this->stop(0);
|
||||
|
||||
@ -1203,7 +1238,7 @@ class Process
|
||||
/**
|
||||
* Updates the status of the process, reads pipes.
|
||||
*
|
||||
* @param Boolean $blocking Whether to use a clocking read call.
|
||||
* @param Boolean $blocking Whether to use a blocking read call.
|
||||
*/
|
||||
protected function updateStatus($blocking)
|
||||
{
|
||||
@ -1218,7 +1253,6 @@ class Process
|
||||
|
||||
if (!$this->processInformation['running']) {
|
||||
$this->close();
|
||||
$this->status = self::STATUS_TERMINATED;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1303,17 +1337,17 @@ class Process
|
||||
*/
|
||||
private function close()
|
||||
{
|
||||
$exitcode = -1;
|
||||
|
||||
$this->processPipes->close();
|
||||
if (is_resource($this->process)) {
|
||||
$exitcode = proc_close($this->process);
|
||||
} else {
|
||||
$exitcode = -1;
|
||||
}
|
||||
|
||||
$this->exitcode = $this->exitcode !== null ? $this->exitcode : -1;
|
||||
$this->exitcode = -1 != $exitcode ? $exitcode : $this->exitcode;
|
||||
$this->exitcode = -1 !== $exitcode ? $exitcode : (null !== $this->exitcode ? $this->exitcode : -1);
|
||||
$this->status = self::STATUS_TERMINATED;
|
||||
|
||||
if (-1 == $this->exitcode && null !== $this->fallbackExitcode) {
|
||||
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
|
||||
@ -1340,4 +1374,73 @@ class Process
|
||||
$this->incrementalOutputOffset = 0;
|
||||
$this->incrementalErrorOutputOffset = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a POSIX signal to the process.
|
||||
*
|
||||
* @param integer $signal A valid POSIX signal (see http://www.php.net/manual/en/pcntl.constants.php)
|
||||
* @param Boolean $throwException Whether to throw exception in case signal failed
|
||||
*
|
||||
* @return Boolean True if the signal was sent successfully, false otherwise
|
||||
*
|
||||
* @throws LogicException In case the process is not running
|
||||
* @throws RuntimeException In case --enable-sigchild is activated
|
||||
* @throws RuntimeException In case of failure
|
||||
*/
|
||||
private function doSignal($signal, $throwException)
|
||||
{
|
||||
if (!$this->isRunning()) {
|
||||
if ($throwException) {
|
||||
throw new LogicException('Can not send signal on a non running process.');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isSigchildEnabled()) {
|
||||
if ($throwException) {
|
||||
throw new RuntimeException('This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (true !== @proc_terminate($this->process, $signal)) {
|
||||
if ($throwException) {
|
||||
throw new RuntimeException(sprintf('Error while sending signal `%s`.', $signal));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the process is running or terminated, throws a LogicException if the process has a not started.
|
||||
*
|
||||
* @param $functionName The function name that was called.
|
||||
*
|
||||
* @throws LogicException If the process has not run.
|
||||
*/
|
||||
private function requireProcessIsStarted($functionName)
|
||||
{
|
||||
if (!$this->isStarted()) {
|
||||
throw new LogicException(sprintf('Process must be started before calling %s.', $functionName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the process is terminated, throws a LogicException if the process has a status different than `terminated`.
|
||||
*
|
||||
* @param $functionName The function name that was called.
|
||||
*
|
||||
* @throws LogicException If the process is not yet terminated.
|
||||
*/
|
||||
private function requireProcessIsTerminated($functionName)
|
||||
{
|
||||
if (!$this->isTerminated()) {
|
||||
throw new LogicException(sprintf('Process must be terminated before calling %s.', $functionName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ class ProcessPipes
|
||||
/** @var array */
|
||||
public $pipes = array();
|
||||
/** @var array */
|
||||
private $files = array();
|
||||
/** @var array */
|
||||
private $fileHandles = array();
|
||||
/** @var array */
|
||||
private $readBytes = array();
|
||||
@ -31,6 +33,8 @@ class ProcessPipes
|
||||
/** @var Boolean */
|
||||
private $ptyMode;
|
||||
|
||||
const CHUNK_SIZE = 16384;
|
||||
|
||||
public function __construct($useFiles, $ttyMode, $ptyMode = false)
|
||||
{
|
||||
$this->useFiles = (Boolean) $useFiles;
|
||||
@ -40,20 +44,21 @@ class ProcessPipes
|
||||
// Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big.
|
||||
// Workaround for this problem is to use temporary files instead of pipes on Windows platform.
|
||||
//
|
||||
// Please note that this work around prevents hanging but
|
||||
// another issue occurs : In some race conditions, some data may be
|
||||
// lost or corrupted.
|
||||
//
|
||||
// @see https://bugs.php.net/bug.php?id=51800
|
||||
if ($this->useFiles) {
|
||||
$this->fileHandles = array(
|
||||
Process::STDOUT => tmpfile(),
|
||||
$this->files = array(
|
||||
Process::STDOUT => tempnam(sys_get_temp_dir(), 'sf_proc_stdout'),
|
||||
Process::STDERR => tempnam(sys_get_temp_dir(), 'sf_proc_stderr'),
|
||||
);
|
||||
if (false === $this->fileHandles[Process::STDOUT]) {
|
||||
throw new RuntimeException('A temporary file could not be opened to write the process output to, verify that your TEMP environment variable is writable');
|
||||
foreach ($this->files as $offset => $file) {
|
||||
$this->fileHandles[$offset] = fopen($this->files[$offset], 'rb');
|
||||
if (false === $this->fileHandles[$offset]) {
|
||||
throw new RuntimeException('A temporary file could not be opened to write the process output to, verify that your TEMP environment variable is writable');
|
||||
}
|
||||
}
|
||||
$this->readBytes = array(
|
||||
Process::STDOUT => 0,
|
||||
Process::STDERR => 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -61,6 +66,7 @@ class ProcessPipes
|
||||
public function __destruct()
|
||||
{
|
||||
$this->close();
|
||||
$this->removeFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,11 +124,13 @@ class ProcessPipes
|
||||
}
|
||||
|
||||
if ($this->useFiles) {
|
||||
// We're not using pipe on Windows platform as it hangs (https://bugs.php.net/bug.php?id=51800)
|
||||
// We're not using file handles as it can produce corrupted output https://bugs.php.net/bug.php?id=65650
|
||||
// So we redirect output within the commandline and pass the nul device to the process
|
||||
return array(
|
||||
array('pipe', 'r'),
|
||||
$this->fileHandles[Process::STDOUT],
|
||||
// Use a file handle only for STDOUT. Using for both STDOUT and STDERR would trigger https://bugs.php.net/bug.php?id=65650
|
||||
array('pipe', 'w'),
|
||||
array('file', 'NUL', 'w'),
|
||||
array('file', 'NUL', 'w'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -147,6 +155,20 @@ class ProcessPipes
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of filenames indexed by their related stream in case these pipes use temporary files.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFiles()
|
||||
{
|
||||
if ($this->useFiles) {
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data in file handles and pipes.
|
||||
*
|
||||
@ -254,7 +276,7 @@ class ProcessPipes
|
||||
$data = '';
|
||||
$dataread = null;
|
||||
while (!feof($fileHandle)) {
|
||||
if (false !== $dataread = fread($fileHandle, 16392)) {
|
||||
if (false !== $dataread = fread($fileHandle, self::CHUNK_SIZE)) {
|
||||
$data .= $dataread;
|
||||
}
|
||||
}
|
||||
@ -310,9 +332,13 @@ class ProcessPipes
|
||||
|
||||
foreach ($r as $pipe) {
|
||||
$type = array_search($pipe, $this->pipes);
|
||||
$data = fread($pipe, 8192);
|
||||
|
||||
if (strlen($data) > 0) {
|
||||
$data = '';
|
||||
while ($dataread = fread($pipe, self::CHUNK_SIZE)) {
|
||||
$data .= $dataread;
|
||||
}
|
||||
|
||||
if ($data) {
|
||||
$read[$type] = $data;
|
||||
}
|
||||
|
||||
@ -337,4 +363,17 @@ class ProcessPipes
|
||||
// stream_select returns false when the `select` system call is interrupted by an incoming signal
|
||||
return isset($lastError['message']) && false !== stripos($lastError['message'], 'interrupted system call');
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes temporary files
|
||||
*/
|
||||
private function removeFiles()
|
||||
{
|
||||
foreach ($this->files as $filename) {
|
||||
if (file_exists($filename)) {
|
||||
@unlink($filename);
|
||||
}
|
||||
}
|
||||
$this->files = array();
|
||||
}
|
||||
}
|
||||
|
@ -46,19 +46,34 @@ class ProcessUtils
|
||||
}
|
||||
|
||||
$escapedArgument = '';
|
||||
foreach (preg_split('/([%"])/i', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) {
|
||||
$quote = false;
|
||||
foreach (preg_split('/(")/i', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) {
|
||||
if ('"' === $part) {
|
||||
$escapedArgument .= '\\"';
|
||||
} elseif ('%' === $part) {
|
||||
$escapedArgument .= '^%';
|
||||
} elseif (self::isSurroundedBy($part, '%')) {
|
||||
// Avoid environment variable expansion
|
||||
$escapedArgument .= '^%"'.substr($part, 1, -1).'"^%';
|
||||
} else {
|
||||
$escapedArgument .= escapeshellarg($part);
|
||||
// escape trailing backslash
|
||||
if ('\\' === substr($part, -1)) {
|
||||
$part .= '\\';
|
||||
}
|
||||
$quote = true;
|
||||
$escapedArgument .= $part;
|
||||
}
|
||||
}
|
||||
if ($quote) {
|
||||
$escapedArgument = '"'.$escapedArgument.'"';
|
||||
}
|
||||
|
||||
return $escapedArgument;
|
||||
}
|
||||
|
||||
return escapeshellarg($argument);
|
||||
}
|
||||
|
||||
private static function isSurroundedBy($arg, $char)
|
||||
{
|
||||
return 2 < strlen($arg) && $char === $arg[0] && $char === $arg[strlen($arg) - 1];
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ namespace Symfony\Component\Process\Tests;
|
||||
use Symfony\Component\Process\Exception\ProcessTimedOutException;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Symfony\Component\Process\Exception\RuntimeException;
|
||||
use Symfony\Component\Process\ProcessPipes;
|
||||
|
||||
/**
|
||||
* @author Robert Schönthal <seroscho@googlemail.com>
|
||||
@ -81,6 +82,34 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertLessThan(1.8, $duration);
|
||||
}
|
||||
|
||||
public function testAllOutputIsActuallyReadOnTermination()
|
||||
{
|
||||
// this code will result in a maximum of 2 reads of 8192 bytes by calling
|
||||
// start() and isRunning(). by the time getOutput() is called the process
|
||||
// has terminated so the internal pipes array is already empty. normally
|
||||
// the call to start() will not read any data as the process will not have
|
||||
// generated output, but this is non-deterministic so we must count it as
|
||||
// a possibility. therefore we need 2 * ProcessPipes::CHUNK_SIZE plus
|
||||
// another byte which will never be read.
|
||||
$expectedOutputSize = ProcessPipes::CHUNK_SIZE * 2 + 2;
|
||||
|
||||
$code = sprintf('echo str_repeat(\'*\', %d);', $expectedOutputSize);
|
||||
$p = $this->getProcess(sprintf('php -r %s', escapeshellarg($code)));
|
||||
|
||||
$p->start();
|
||||
// Let's wait enough time for process to finish...
|
||||
// Here we don't call Process::run or Process::wait to avoid any read of pipes
|
||||
usleep(500000);
|
||||
|
||||
if ($p->isRunning()) {
|
||||
$this->markTestSkipped('Process execution did not complete in the required time frame');
|
||||
}
|
||||
|
||||
$o = $p->getOutput();
|
||||
|
||||
$this->assertEquals($expectedOutputSize, strlen($o));
|
||||
}
|
||||
|
||||
public function testCallbacksAreExecutedWithStart()
|
||||
{
|
||||
$data = '';
|
||||
@ -167,7 +196,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testGetErrorOutput()
|
||||
{
|
||||
$p = new Process(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }')));
|
||||
$p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }')));
|
||||
|
||||
$p->run();
|
||||
$this->assertEquals(3, preg_match_all('/ERROR/', $p->getErrorOutput(), $matches));
|
||||
@ -175,7 +204,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testGetIncrementalErrorOutput()
|
||||
{
|
||||
$p = new Process(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { usleep(50000); file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }')));
|
||||
$p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { usleep(100000); file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }')));
|
||||
|
||||
$p->start();
|
||||
while ($p->isRunning()) {
|
||||
@ -186,7 +215,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testFlushErrorOutput()
|
||||
{
|
||||
$p = new Process(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }')));
|
||||
$p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }')));
|
||||
|
||||
$p->run();
|
||||
$p->clearErrorOutput();
|
||||
@ -195,7 +224,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testGetOutput()
|
||||
{
|
||||
$p = new Process(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) {echo \' foo \';$n++; usleep(500); }')));
|
||||
$p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) {echo \' foo \';$n++; usleep(500); }')));
|
||||
|
||||
$p->run();
|
||||
$this->assertEquals(3, preg_match_all('/foo/', $p->getOutput(), $matches));
|
||||
@ -203,7 +232,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testGetIncrementalOutput()
|
||||
{
|
||||
$p = new Process(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) { echo \' foo \'; usleep(50000); $n++; }')));
|
||||
$p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) { echo \' foo \'; usleep(50000); $n++; }')));
|
||||
|
||||
$p->start();
|
||||
while ($p->isRunning()) {
|
||||
@ -214,7 +243,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testFlushOutput()
|
||||
{
|
||||
$p = new Process(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) {echo \' foo \';$n++;}')));
|
||||
$p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) {echo \' foo \';$n++;}')));
|
||||
|
||||
$p->run();
|
||||
$p->clearOutput();
|
||||
@ -240,11 +269,32 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
$this->markTestSkipped('Windows does have /dev/tty support');
|
||||
}
|
||||
|
||||
$process = $this->getProcess('echo "foo" >> /dev/null && php -r "usleep(100000);"');
|
||||
$process->setTTY(true);
|
||||
$process->start();
|
||||
$this->assertTrue($process->isRunning());
|
||||
$process->wait();
|
||||
|
||||
$this->assertSame(Process::STATUS_TERMINATED, $process->getStatus());
|
||||
}
|
||||
|
||||
public function testTTYCommandExitCode()
|
||||
{
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
$this->markTestSkipped('Windows does have /dev/tty support');
|
||||
}
|
||||
|
||||
$process = $this->getProcess('echo "foo" >> /dev/null');
|
||||
$process->setTTY(true);
|
||||
$process->run();
|
||||
|
||||
$this->assertSame(Process::STATUS_TERMINATED, $process->getStatus());
|
||||
$this->assertTrue($process->isSuccessful());
|
||||
}
|
||||
|
||||
public function testExitCodeTextIsNullWhenExitCodeIsNull()
|
||||
{
|
||||
$process = $this->getProcess('');
|
||||
$this->assertNull($process->getExitCodeText());
|
||||
}
|
||||
|
||||
public function testPTYCommand()
|
||||
@ -292,11 +342,12 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testStartIsNonBlocking()
|
||||
{
|
||||
$process = $this->getProcess('php -r "sleep(4);"');
|
||||
$process = $this->getProcess('php -r "usleep(500000);"');
|
||||
$start = microtime(true);
|
||||
$process->start();
|
||||
$end = microtime(true);
|
||||
$this->assertLessThan(1 , $end-$start);
|
||||
$this->assertLessThan(0.2, $end-$start);
|
||||
$process->wait();
|
||||
}
|
||||
|
||||
public function testUpdateStatus()
|
||||
@ -331,7 +382,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$process = $this->getProcess('php -m');
|
||||
$process->run();
|
||||
$this->assertEquals(0, $process->getExitCode());
|
||||
$this->assertSame(0, $process->getExitCode());
|
||||
}
|
||||
|
||||
public function testStatus()
|
||||
@ -383,10 +434,10 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testIsNotSuccessful()
|
||||
{
|
||||
$process = $this->getProcess('php -r "sleep(4);"');
|
||||
$process = $this->getProcess('php -r "usleep(500000);throw new \Exception(\'BOUM\');"');
|
||||
$process->start();
|
||||
$this->assertTrue($process->isRunning());
|
||||
$process->stop();
|
||||
$process->wait();
|
||||
$this->assertFalse($process->isSuccessful());
|
||||
}
|
||||
|
||||
@ -477,7 +528,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
$process1->run();
|
||||
$process2 = $process1->restart();
|
||||
|
||||
usleep(300000); // wait for output
|
||||
$process2->wait(); // wait for output
|
||||
|
||||
// Ensure that both processed finished and the output is numeric
|
||||
$this->assertFalse($process1->isRunning());
|
||||
@ -504,7 +555,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
public function testRunProcessWithTimeout()
|
||||
{
|
||||
$timeout = 0.5;
|
||||
$process = $this->getProcess('php -r "sleep(3);"');
|
||||
$process = $this->getProcess('php -r "usleep(600000);"');
|
||||
$process->setTimeout($timeout);
|
||||
$start = microtime(true);
|
||||
try {
|
||||
@ -518,6 +569,19 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertLessThan($timeout + Process::TIMEOUT_PRECISION, $duration);
|
||||
}
|
||||
|
||||
public function testCheckTimeoutOnNonStartedProcess()
|
||||
{
|
||||
$process = $this->getProcess('php -r "sleep(3);"');
|
||||
$process->checkTimeout();
|
||||
}
|
||||
|
||||
public function testCheckTimeoutOnTerminatedProcess()
|
||||
{
|
||||
$process = $this->getProcess('php -v');
|
||||
$process->run();
|
||||
$process->checkTimeout();
|
||||
}
|
||||
|
||||
public function testCheckTimeoutOnStartedProcess()
|
||||
{
|
||||
$timeout = 0.5;
|
||||
@ -584,8 +648,9 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testStartAfterATimeout()
|
||||
{
|
||||
$process = $this->getProcess('php -r "while (true) {echo \'\'; usleep(1000); }"');
|
||||
$process->setTimeout(0.2);
|
||||
$process = $this->getProcess('php -r "$n = 1000; while ($n--) {echo \'\'; usleep(1000); }"');
|
||||
$process->setTimeout(0.1);
|
||||
|
||||
try {
|
||||
$process->run();
|
||||
$this->fail('An exception should have been raised.');
|
||||
@ -599,10 +664,10 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testGetPid()
|
||||
{
|
||||
$process = $this->getProcess('php -r "sleep(1);"');
|
||||
$process = $this->getProcess('php -r "usleep(500000);"');
|
||||
$process->start();
|
||||
$this->assertGreaterThan(0, $process->getPid());
|
||||
$process->stop();
|
||||
$process->wait();
|
||||
}
|
||||
|
||||
public function testGetPidIsNullBeforeStart()
|
||||
@ -662,6 +727,55 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
$process->signal(SIGHUP);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideMethodsThatNeedARunningProcess
|
||||
*/
|
||||
public function testMethodsThatNeedARunningProcess($method)
|
||||
{
|
||||
$process = $this->getProcess('php -m');
|
||||
$this->setExpectedException('Symfony\Component\Process\Exception\LogicException', sprintf('Process must be started before calling %s.', $method));
|
||||
call_user_func(array($process, $method));
|
||||
}
|
||||
|
||||
public function provideMethodsThatNeedARunningProcess()
|
||||
{
|
||||
return array(
|
||||
array('getOutput'),
|
||||
array('getIncrementalOutput'),
|
||||
array('getErrorOutput'),
|
||||
array('getIncrementalErrorOutput'),
|
||||
array('wait'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideMethodsThatNeedATerminatedProcess
|
||||
*/
|
||||
public function testMethodsThatNeedATerminatedProcess($method)
|
||||
{
|
||||
$process = $this->getProcess('php -r "sleep(1);"');
|
||||
$process->start();
|
||||
try {
|
||||
call_user_func(array($process, $method));
|
||||
$process->stop(0);
|
||||
$this->fail('A LogicException must have been thrown');
|
||||
} catch (\Exception $e) {
|
||||
$this->assertInstanceOf('Symfony\Component\Process\Exception\LogicException', $e);
|
||||
$this->assertEquals(sprintf('Process must be terminated before calling %s.', $method), $e->getMessage());
|
||||
}
|
||||
$process->stop(0);
|
||||
}
|
||||
|
||||
public function provideMethodsThatNeedATerminatedProcess()
|
||||
{
|
||||
return array(
|
||||
array('hasBeenSignaled'),
|
||||
array('getTermSignal'),
|
||||
array('hasBeenStopped'),
|
||||
array('getStopSignal'),
|
||||
);
|
||||
}
|
||||
|
||||
private function verifyPosixIsEnabled()
|
||||
{
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
@ -787,6 +901,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$p = $this->getProcess('php -r "usleep(500000);"');
|
||||
$p->disableOutput();
|
||||
$p->start();
|
||||
$this->setExpectedException('Symfony\Component\Process\Exception\LogicException', 'Output has been disabled.');
|
||||
call_user_func(array($p, $fetchMethod));
|
||||
}
|
||||
|
@ -138,13 +138,13 @@ class ProcessBuilderTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testShouldEscapeArguments()
|
||||
{
|
||||
$pb = new ProcessBuilder(array('%path%', 'foo " bar'));
|
||||
$pb = new ProcessBuilder(array('%path%', 'foo " bar', '%baz%baz'));
|
||||
$proc = $pb->getProcess();
|
||||
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
$this->assertSame('^%"path"^% "foo "\\"" bar"', $proc->getCommandLine());
|
||||
$this->assertSame('^%"path"^% "foo \\" bar" "%baz%baz"', $proc->getCommandLine());
|
||||
} else {
|
||||
$this->assertSame("'%path%' 'foo \" bar'", $proc->getCommandLine());
|
||||
$this->assertSame("'%path%' 'foo \" bar' '%baz%baz'", $proc->getCommandLine());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,18 +27,22 @@ class ProcessUtilsTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
return array(
|
||||
array('"\"php\" \"-v\""', '"php" "-v"'),
|
||||
array('"foo bar"', 'foo bar'),
|
||||
array('^%"path"^%', '%path%'),
|
||||
array('"<|>"\\"" "\\""\'f"', '<|>" "\'f'),
|
||||
array('"<|>\\" \\"\'f"', '<|>" "\'f'),
|
||||
array('""', ''),
|
||||
array('"with\trailingbs\\\\"', 'with\trailingbs\\'),
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
array("'\"php\" \"-v\"'", '"php" "-v"'),
|
||||
array("'foo bar'", 'foo bar'),
|
||||
array("'%path%'", '%path%'),
|
||||
array("'<|>\" \"'\\''f'", '<|>" "\'f'),
|
||||
array("''", ''),
|
||||
array("'with\\trailingbs\\'", 'with\trailingbs\\'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
{
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
|
||||
*/
|
||||
public function testGetExitCode()
|
||||
{
|
||||
@ -23,6 +24,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
|
||||
*/
|
||||
public function testGetExitCodeIsNullOnStart()
|
||||
{
|
||||
@ -31,6 +33,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
|
||||
*/
|
||||
public function testGetExitCodeIsNullOnWhenStartingAgain()
|
||||
{
|
||||
@ -39,6 +42,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
|
||||
*/
|
||||
public function testExitCodeCommandFailed()
|
||||
{
|
||||
@ -47,6 +51,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage his PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
|
||||
*/
|
||||
public function testMustRun()
|
||||
{
|
||||
@ -71,6 +76,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage his PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
|
||||
*/
|
||||
public function testProcessWithTermSignal()
|
||||
{
|
||||
@ -79,6 +85,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage his PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
|
||||
*/
|
||||
public function testProcessIsNotSignaled()
|
||||
{
|
||||
@ -87,6 +94,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage his PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
|
||||
*/
|
||||
public function testProcessWithoutTermSignal()
|
||||
{
|
||||
@ -95,6 +103,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
|
||||
*/
|
||||
public function testCheckTimeoutOnStartedProcess()
|
||||
{
|
||||
@ -103,6 +112,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.
|
||||
*/
|
||||
public function testGetPid()
|
||||
{
|
||||
@ -111,6 +121,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.
|
||||
*/
|
||||
public function testGetPidIsNullBeforeStart()
|
||||
{
|
||||
@ -119,6 +130,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.
|
||||
*/
|
||||
public function testGetPidIsNullAfterRun()
|
||||
{
|
||||
@ -127,6 +139,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
|
||||
*/
|
||||
public function testExitCodeText()
|
||||
{
|
||||
@ -138,6 +151,16 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
|
||||
*/
|
||||
public function testExitCodeTextIsNullWhenExitCodeIsNull()
|
||||
{
|
||||
parent::testExitCodeTextIsNullWhenExitCodeIsNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
|
||||
*/
|
||||
public function testIsSuccessful()
|
||||
{
|
||||
@ -146,6 +169,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
|
||||
*/
|
||||
public function testIsSuccessfulOnlyAfterTerminated()
|
||||
{
|
||||
@ -154,6 +178,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
|
||||
*/
|
||||
public function testIsNotSuccessful()
|
||||
{
|
||||
@ -162,6 +187,16 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.
|
||||
*/
|
||||
public function testTTYCommandExitCode()
|
||||
{
|
||||
parent::testTTYCommandExitCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process can not be signaled.
|
||||
*/
|
||||
public function testSignal()
|
||||
{
|
||||
@ -170,6 +205,7 @@ class SigchildDisabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
|
||||
*/
|
||||
public function testProcessWithoutTermSignalIsNotSignaled()
|
||||
{
|
||||
|
@ -15,6 +15,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
|
||||
{
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
|
||||
*/
|
||||
public function testProcessIsSignaledIfStopped()
|
||||
{
|
||||
@ -23,6 +24,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
|
||||
*/
|
||||
public function testProcessWithTermSignal()
|
||||
{
|
||||
@ -31,6 +33,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
|
||||
*/
|
||||
public function testProcessIsNotSignaled()
|
||||
{
|
||||
@ -39,6 +42,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
|
||||
*/
|
||||
public function testProcessWithoutTermSignal()
|
||||
{
|
||||
@ -47,6 +51,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.
|
||||
*/
|
||||
public function testGetPid()
|
||||
{
|
||||
@ -55,6 +60,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.
|
||||
*/
|
||||
public function testGetPidIsNullBeforeStart()
|
||||
{
|
||||
@ -63,6 +69,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.
|
||||
*/
|
||||
public function testGetPidIsNullAfterRun()
|
||||
{
|
||||
@ -79,6 +86,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process can not be signaled.
|
||||
*/
|
||||
public function testSignal()
|
||||
{
|
||||
@ -87,6 +95,7 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
* @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.
|
||||
*/
|
||||
public function testProcessWithoutTermSignalIsNotSignaled()
|
||||
{
|
||||
@ -103,6 +112,14 @@ class SigchildEnabledProcessTest extends AbstractProcessTest
|
||||
$this->markTestSkipped('Signal is not supported in sigchild environment');
|
||||
}
|
||||
|
||||
public function testStartAfterATimeout()
|
||||
{
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
$this->markTestSkipped('Restarting a timed-out process on Windows is not supported in sigchild environment');
|
||||
}
|
||||
parent::testStartAfterATimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -27,106 +27,123 @@ class SimpleProcessTest extends AbstractProcessTest
|
||||
|
||||
public function testGetExitCode()
|
||||
{
|
||||
$this->skipIfPHPSigchild();
|
||||
$this->skipIfPHPSigchild(); // This test use exitcode that is not available in this case
|
||||
parent::testGetExitCode();
|
||||
}
|
||||
|
||||
public function testExitCodeCommandFailed()
|
||||
{
|
||||
$this->skipIfPHPSigchild();
|
||||
$this->skipIfPHPSigchild(); // This test use exitcode that is not available in this case
|
||||
parent::testExitCodeCommandFailed();
|
||||
}
|
||||
|
||||
public function testProcessIsSignaledIfStopped()
|
||||
{
|
||||
$this->skipIfPHPSigchild();
|
||||
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
|
||||
parent::testProcessIsSignaledIfStopped();
|
||||
}
|
||||
|
||||
public function testProcessWithTermSignal()
|
||||
{
|
||||
$this->skipIfPHPSigchild();
|
||||
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
|
||||
parent::testProcessWithTermSignal();
|
||||
}
|
||||
|
||||
public function testProcessIsNotSignaled()
|
||||
{
|
||||
$this->skipIfPHPSigchild();
|
||||
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
|
||||
parent::testProcessIsNotSignaled();
|
||||
}
|
||||
|
||||
public function testProcessWithoutTermSignal()
|
||||
{
|
||||
$this->skipIfPHPSigchild();
|
||||
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
|
||||
parent::testProcessWithoutTermSignal();
|
||||
}
|
||||
|
||||
public function testExitCodeText()
|
||||
{
|
||||
$this->skipIfPHPSigchild();
|
||||
$this->skipIfPHPSigchild(); // This test use exitcode that is not available in this case
|
||||
parent::testExitCodeText();
|
||||
}
|
||||
|
||||
public function testIsSuccessful()
|
||||
{
|
||||
$this->skipIfPHPSigchild();
|
||||
$this->skipIfPHPSigchild(); // This test use PID that is not available in this case
|
||||
parent::testIsSuccessful();
|
||||
}
|
||||
|
||||
public function testIsNotSuccessful()
|
||||
{
|
||||
$this->skipIfPHPSigchild();
|
||||
$this->skipIfPHPSigchild(); // This test use PID that is not available in this case
|
||||
parent::testIsNotSuccessful();
|
||||
}
|
||||
|
||||
public function testGetPid()
|
||||
{
|
||||
$this->skipIfPHPSigchild();
|
||||
$this->skipIfPHPSigchild(); // This test use PID that is not available in this case
|
||||
parent::testGetPid();
|
||||
}
|
||||
|
||||
public function testGetPidIsNullBeforeStart()
|
||||
{
|
||||
$this->skipIfPHPSigchild();
|
||||
$this->skipIfPHPSigchild(); // This test use PID that is not available in this case
|
||||
parent::testGetPidIsNullBeforeStart();
|
||||
}
|
||||
|
||||
public function testGetPidIsNullAfterRun()
|
||||
{
|
||||
$this->skipIfPHPSigchild();
|
||||
$this->skipIfPHPSigchild(); // This test use PID that is not available in this case
|
||||
parent::testGetPidIsNullAfterRun();
|
||||
}
|
||||
|
||||
public function testSignal()
|
||||
{
|
||||
$this->skipIfPHPSigchild();
|
||||
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
|
||||
parent::testSignal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\LogicException
|
||||
*/
|
||||
public function testProcessWithoutTermSignalIsNotSignaled()
|
||||
{
|
||||
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved');
|
||||
parent::testProcessWithoutTermSignalIsNotSignaled();
|
||||
}
|
||||
|
||||
public function testProcessThrowsExceptionWhenExternallySignaled()
|
||||
{
|
||||
$this->skipIfPHPSigchild(); // This test use PID that is not available in this case
|
||||
parent::testProcessThrowsExceptionWhenExternallySignaled();
|
||||
}
|
||||
|
||||
public function testExitCodeIsAvailableAfterSignal()
|
||||
{
|
||||
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
|
||||
parent::testExitCodeIsAvailableAfterSignal();
|
||||
}
|
||||
|
||||
public function testSignalProcessNotRunning()
|
||||
{
|
||||
$this->skipIfPHPSigchild();
|
||||
$this->setExpectedException('Symfony\Component\Process\Exception\LogicException', 'Can not send signal on a non running process.');
|
||||
parent::testSignalProcessNotRunning();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
*/
|
||||
public function testSignalWithWrongIntSignal()
|
||||
{
|
||||
$this->skipIfPHPSigchild();
|
||||
if ($this->enabledSigchild) {
|
||||
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
|
||||
} else {
|
||||
$this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'Error while sending signal `-4`.');
|
||||
}
|
||||
parent::testSignalWithWrongIntSignal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Process\Exception\RuntimeException
|
||||
*/
|
||||
public function testSignalWithWrongNonIntSignal()
|
||||
{
|
||||
$this->skipIfPHPSigchild();
|
||||
if ($this->enabledSigchild) {
|
||||
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
|
||||
} else {
|
||||
$this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'Error while sending signal `Céphalopodes`.');
|
||||
}
|
||||
parent::testSignalWithWrongNonIntSignal();
|
||||
}
|
||||
|
||||
@ -144,4 +161,11 @@ class SimpleProcessTest extends AbstractProcessTest
|
||||
$this->markTestSkipped('Your PHP has been compiled with --enable-sigchild, this test can not be executed');
|
||||
}
|
||||
}
|
||||
|
||||
private function expectExceptionIfPHPSigchild($classname, $message)
|
||||
{
|
||||
if ($this->enabledSigchild) {
|
||||
$this->setExpectedException($classname, $message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,6 @@ use Symfony\Component\Security\Http\HttpUtils;
|
||||
/**
|
||||
* Class with the default authentication success handling logic.
|
||||
*
|
||||
* Can be optionally be extended from by the developer to alter the behaviour
|
||||
* while keeping the default behaviour.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
* @author Alexander <iam.asm89@gmail.com>
|
||||
|
@ -58,10 +58,10 @@ class Firewall implements EventSubscriberInterface
|
||||
}
|
||||
|
||||
// register listeners for this firewall
|
||||
list($listeners, $exception) = $this->map->getListeners($event->getRequest());
|
||||
if (null !== $exception) {
|
||||
$this->exceptionListeners[$event->getRequest()] = $exception;
|
||||
$exception->register($this->dispatcher);
|
||||
list($listeners, $exceptionListener) = $this->map->getListeners($event->getRequest());
|
||||
if (null !== $exceptionListener) {
|
||||
$this->exceptionListeners[$event->getRequest()] = $exceptionListener;
|
||||
$exceptionListener->register($this->dispatcher);
|
||||
}
|
||||
|
||||
// initiate the listener chain
|
||||
|
@ -68,6 +68,10 @@ class XmlEncoder extends SerializerAwareEncoder implements EncoderInterface, Dec
|
||||
*/
|
||||
public function decode($data, $format, array $context = array())
|
||||
{
|
||||
if ('' === trim($data)) {
|
||||
throw new UnexpectedValueException('Invalid XML data, it can not be empty.');
|
||||
}
|
||||
|
||||
$internalErrors = libxml_use_internal_errors(true);
|
||||
$disableEntities = libxml_disable_entity_loader(true);
|
||||
libxml_clear_errors();
|
||||
@ -79,6 +83,8 @@ class XmlEncoder extends SerializerAwareEncoder implements EncoderInterface, Dec
|
||||
libxml_disable_entity_loader($disableEntities);
|
||||
|
||||
if ($error = libxml_get_last_error()) {
|
||||
libxml_clear_errors();
|
||||
|
||||
throw new UnexpectedValueException($error->message);
|
||||
}
|
||||
|
||||
|
@ -347,6 +347,12 @@ class XmlEncoderTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testDecodeEmptyXml()
|
||||
{
|
||||
$this->setExpectedException('Symfony\Component\Serializer\Exception\UnexpectedValueException', 'Invalid XML data, it can not be empty.');
|
||||
$this->encoder->decode(' ', 'xml');
|
||||
}
|
||||
|
||||
protected function getXmlSource()
|
||||
{
|
||||
return '<?xml version="1.0"?>'."\n".
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
use Symfony\Component\Config\Util\XmlUtils;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
use Symfony\Component\Translation\Exception\InvalidResourceException;
|
||||
use Symfony\Component\Translation\Exception\NotFoundResourceException;
|
||||
@ -40,12 +41,15 @@ class QtFileLoader implements LoaderInterface
|
||||
throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
|
||||
}
|
||||
|
||||
$dom = new \DOMDocument();
|
||||
$current = libxml_use_internal_errors(true);
|
||||
if (!@$dom->load($resource, defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0)) {
|
||||
throw new InvalidResourceException(implode("\n", $this->getXmlErrors()));
|
||||
try {
|
||||
$dom = XmlUtils::loadFile($resource);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
throw new InvalidResourceException(sprintf('Unable to load "%s".', $resource), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
$internalErrors = libxml_use_internal_errors(true);
|
||||
libxml_clear_errors();
|
||||
|
||||
$xpath = new \DOMXPath($dom);
|
||||
$nodes = $xpath->evaluate('//TS/context/name[text()="'.$domain.'"]');
|
||||
|
||||
@ -67,33 +71,8 @@ class QtFileLoader implements LoaderInterface
|
||||
$catalogue->addResource(new FileResource($resource));
|
||||
}
|
||||
|
||||
libxml_use_internal_errors($current);
|
||||
libxml_use_internal_errors($internalErrors);
|
||||
|
||||
return $catalogue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XML errors of the internal XML parser
|
||||
*
|
||||
* @return array An array of errors
|
||||
*/
|
||||
private function getXmlErrors()
|
||||
{
|
||||
$errors = array();
|
||||
foreach (libxml_get_errors() as $error) {
|
||||
$errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
|
||||
LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
|
||||
$error->code,
|
||||
trim($error->message),
|
||||
$error->file ? $error->file : 'n/a',
|
||||
$error->line,
|
||||
$error->column
|
||||
);
|
||||
}
|
||||
|
||||
libxml_clear_errors();
|
||||
libxml_use_internal_errors(false);
|
||||
|
||||
return $errors;
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Symfony\Component\Translation\Loader;
|
||||
|
||||
use Symfony\Component\Config\Util\XmlUtils;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
use Symfony\Component\Translation\Exception\InvalidResourceException;
|
||||
use Symfony\Component\Translation\Exception\NotFoundResourceException;
|
||||
@ -86,27 +87,13 @@ class XliffFileLoader implements LoaderInterface
|
||||
*/
|
||||
private function parseFile($file)
|
||||
{
|
||||
try {
|
||||
$dom = XmlUtils::loadFile($file);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
throw new InvalidResourceException(sprintf('Unable to load "%s": %s', $file, $e->getMessage()), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
$internalErrors = libxml_use_internal_errors(true);
|
||||
$disableEntities = libxml_disable_entity_loader(true);
|
||||
libxml_clear_errors();
|
||||
|
||||
$dom = new \DOMDocument();
|
||||
$dom->validateOnParse = true;
|
||||
if (!@$dom->loadXML(file_get_contents($file), LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) {
|
||||
libxml_disable_entity_loader($disableEntities);
|
||||
|
||||
throw new InvalidResourceException(implode("\n", $this->getXmlErrors($internalErrors)));
|
||||
}
|
||||
|
||||
libxml_disable_entity_loader($disableEntities);
|
||||
|
||||
foreach ($dom->childNodes as $child) {
|
||||
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
|
||||
libxml_use_internal_errors($internalErrors);
|
||||
|
||||
throw new InvalidResourceException('Document types are not allowed.');
|
||||
}
|
||||
}
|
||||
|
||||
$location = str_replace('\\', '/', __DIR__).'/schema/dic/xliff-core/xml.xsd';
|
||||
$parts = explode('/', $location);
|
||||
|
@ -56,4 +56,12 @@ class QtFileLoaderTest extends \PHPUnit_Framework_TestCase
|
||||
$resource = __DIR__.'/../fixtures/invalid-xml-resources.xlf';
|
||||
$loader->load($resource, 'en', 'domain1');
|
||||
}
|
||||
|
||||
public function testLoadEmptyResource()
|
||||
{
|
||||
$loader = new QtFileLoader();
|
||||
$resource = __DIR__.'/../fixtures/empty.xlf';
|
||||
$this->setExpectedException('Symfony\Component\Translation\Exception\InvalidResourceException', sprintf('Unable to load "%s".', $resource));
|
||||
$loader->load($resource, 'en', 'domain1');
|
||||
}
|
||||
}
|
||||
|
@ -103,4 +103,12 @@ class XliffFileLoaderTest extends \PHPUnit_Framework_TestCase
|
||||
$loader = new XliffFileLoader();
|
||||
$loader->load(__DIR__.'/../fixtures/withdoctype.xlf', 'en', 'domain1');
|
||||
}
|
||||
|
||||
public function testParseEmptyFile()
|
||||
{
|
||||
$loader = new XliffFileLoader();
|
||||
$resource = __DIR__.'/../fixtures/empty.xlf';
|
||||
$this->setExpectedException('Symfony\Component\Translation\Exception\InvalidResourceException', sprintf('Unable to load "%s":', $resource));
|
||||
$loader->load($resource, 'en', 'domain1');
|
||||
}
|
||||
}
|
||||
|
0
src/Symfony/Component/Translation/Tests/fixtures/empty.xlf
vendored
Normal file
0
src/Symfony/Component/Translation/Tests/fixtures/empty.xlf
vendored
Normal file
@ -36,7 +36,7 @@ class IbanValidator extends ConstraintValidator
|
||||
}
|
||||
|
||||
// An IBAN without a country code is not an IBAN.
|
||||
if (0 === preg_match('/[A-Za-z]/', $value)) {
|
||||
if (0 === preg_match('/[A-Z]/', $value)) {
|
||||
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
|
||||
|
||||
return;
|
||||
@ -55,7 +55,7 @@ class IbanValidator extends ConstraintValidator
|
||||
.strval(ord($teststring{1}) - 55)
|
||||
.substr($teststring, 2, 2);
|
||||
|
||||
$teststring = preg_replace_callback('/[A-Za-z]/', function ($letter) {
|
||||
$teststring = preg_replace_callback('/[A-Z]/', function ($letter) {
|
||||
return intval(ord(strtolower($letter[0])) - 87);
|
||||
}, $teststring);
|
||||
|
||||
|
@ -182,6 +182,10 @@ class IbanValidatorTest extends \PHPUnit_Framework_TestCase
|
||||
array('foo'),
|
||||
array('123'),
|
||||
array('0750447346'),
|
||||
|
||||
//Ibans with lower case values are invalid
|
||||
array('Ae260211000000230064016'),
|
||||
array('ae260211000000230064016')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ EOF;
|
||||
} elseif (isset($test['todo']) && $test['todo']) {
|
||||
// TODO
|
||||
} else {
|
||||
$expected = eval('return '.trim($test['php']).';');
|
||||
eval('$expected = '.trim($test['php']).';');
|
||||
|
||||
$this->assertEquals($expected, $this->parser->parse($this->dumper->dump($expected, 10)), $test['test']);
|
||||
}
|
||||
|
@ -62,9 +62,9 @@ class ParserTest extends \PHPUnit_Framework_TestCase
|
||||
if (isset($test['todo']) && $test['todo']) {
|
||||
// TODO
|
||||
} else {
|
||||
$expected = var_export(eval('return '.trim($test['php']).';'), true);
|
||||
eval('$expected = '.trim($test['php']).';');
|
||||
|
||||
$tests[] = array($file, $expected, $test['yaml'], $test['test']);
|
||||
$tests[] = array($file, var_export($expected, true), $test['yaml'], $test['test']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user