diff --git a/.php_cs.dist b/.php_cs.dist index 9b8d082116..a9919c636f 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -8,9 +8,10 @@ return PhpCsFixer\Config::create() ->setRules(array( '@Symfony' => true, '@Symfony:risky' => true, + '@PHPUnit48Migration:risky' => true, + 'php_unit_no_expectation_annotation' => false, // part of `PHPUnitXYMigration:risky` ruleset, to be enabled when PHPUnit 4.x support will be dropped, as we don't want to rewrite exceptions handling twice 'array_syntax' => array('syntax' => 'long'), 'protected_to_private' => false, - 'php_unit_dedicate_assert' => array('target' => '3.5'), )) ->setRiskyAllowed(true) ->setFinder( diff --git a/src/Symfony/Bridge/PhpUnit/composer.json b/src/Symfony/Bridge/PhpUnit/composer.json index 47f6d7b5e8..24a48b600d 100644 --- a/src/Symfony/Bridge/PhpUnit/composer.json +++ b/src/Symfony/Bridge/PhpUnit/composer.json @@ -41,6 +41,10 @@ "extra": { "branch-alias": { "dev-master": "3.4-dev" + }, + "thanks": { + "name": "phpunit/phpunit", + "url": "https://github.com/sebastianbergmann/phpunit" } } } diff --git a/src/Symfony/Bundle/WebServerBundle/Resources/router.php b/src/Symfony/Bundle/WebServerBundle/Resources/router.php index 187be0b836..a366b381aa 100644 --- a/src/Symfony/Bundle/WebServerBundle/Resources/router.php +++ b/src/Symfony/Bundle/WebServerBundle/Resources/router.php @@ -30,7 +30,7 @@ if (is_file($_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.$_SERVER['SCRIPT_NAME' return false; } -$script = getenv('APP_FRONT_CONTROLLER') ?: 'index.php'; +$script = isset($_ENV['APP_FRONT_CONTROLLER']) ? $_ENV['APP_FRONT_CONTROLLER'] : 'index.php'; $_SERVER = array_merge($_SERVER, $_ENV); $_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.$script; diff --git a/src/Symfony/Bundle/WebServerBundle/WebServer.php b/src/Symfony/Bundle/WebServerBundle/WebServer.php index e3425ec8bb..beb5190b72 100644 --- a/src/Symfony/Bundle/WebServerBundle/WebServer.php +++ b/src/Symfony/Bundle/WebServerBundle/WebServer.php @@ -146,11 +146,11 @@ class WebServer private function createServerProcess(WebServerConfig $config) { $finder = new PhpExecutableFinder(); - if (false === $binary = $finder->find()) { + if (false === $binary = $finder->find(false)) { throw new \RuntimeException('Unable to find the PHP binary.'); } - $process = new Process(array($binary, '-S', $config->getAddress(), $config->getRouter())); + $process = new Process(array_merge(array($binary), $finder->findArguments(), array('-dvariables_order=EGPCS', '-S', $config->getAddress(), $config->getRouter()))); $process->setWorkingDirectory($config->getDocumentRoot()); $process->setTimeout(null); diff --git a/src/Symfony/Bundle/WebServerBundle/WebServerConfig.php b/src/Symfony/Bundle/WebServerBundle/WebServerConfig.php index 6c17d110fe..8b88eaecad 100644 --- a/src/Symfony/Bundle/WebServerBundle/WebServerConfig.php +++ b/src/Symfony/Bundle/WebServerBundle/WebServerConfig.php @@ -32,7 +32,7 @@ class WebServerConfig throw new \InvalidArgumentException(sprintf('Unable to find the front controller under "%s" (none of these files exist: %s).', $documentRoot, implode(', ', $this->getFrontControllerFileNames($env)))); } - putenv('APP_FRONT_CONTROLLER='.$file); + $_ENV['APP_FRONT_CONTROLLER'] = $file; $this->documentRoot = $documentRoot; $this->env = $env; diff --git a/src/Symfony/Bundle/WebServerBundle/composer.json b/src/Symfony/Bundle/WebServerBundle/composer.json index f51be62350..87272bba7b 100644 --- a/src/Symfony/Bundle/WebServerBundle/composer.json +++ b/src/Symfony/Bundle/WebServerBundle/composer.json @@ -21,7 +21,7 @@ "symfony/console": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", "symfony/http-kernel": "~3.3|~4.0", - "symfony/process": "~3.3|~4.0" + "symfony/process": "~3.3.14|^3.4.2|^4.0.2" }, "autoload": { "psr-4": { "Symfony\\Bundle\\WebServerBundle\\": "" }, diff --git a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php index 82344fceae..9b877efb08 100644 --- a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php +++ b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php @@ -71,7 +71,7 @@ trait MemcachedTrait * * @return \Memcached * - * @throws \ErrorEception When invalid options or servers are provided + * @throws \ErrorException When invalid options or servers are provided */ public static function createConnection($servers, array $options = array()) { diff --git a/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php b/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php index 3ea97802dc..64c0f5578a 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php +++ b/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php @@ -186,7 +186,7 @@ class ExprBuilder */ public function thenInvalid($message) { - $this->thenPart = function ($v) use ($message) {throw new \InvalidArgumentException(sprintf($message, json_encode($v))); }; + $this->thenPart = function ($v) use ($message) { throw new \InvalidArgumentException(sprintf($message, json_encode($v))); }; return $this; } diff --git a/src/Symfony/Component/Config/ResourceCheckerConfigCache.php b/src/Symfony/Component/Config/ResourceCheckerConfigCache.php index 10257d5dd4..0a79b84c1c 100644 --- a/src/Symfony/Component/Config/ResourceCheckerConfigCache.php +++ b/src/Symfony/Component/Config/ResourceCheckerConfigCache.php @@ -158,7 +158,7 @@ class ResourceCheckerConfigCache implements ConfigCacheInterface $meta = false; $signalingException = new \UnexpectedValueException(); $prevUnserializeHandler = ini_set('unserialize_callback_func', ''); - $prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context) use (&$prevErrorHandler, $signalingException) { + $prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = array()) use (&$prevErrorHandler, $signalingException) { if (E_WARNING === $type && 'Class __PHP_Incomplete_Class has no unserializer' === $msg) { throw $signalingException; } diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index f3bc0d5964..be283aad16 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -580,6 +580,7 @@ class Application { $this->init(); + $aliases = array(); $allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands); $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name); $commands = preg_grep('{^'.$expr.'}', $allCommands); @@ -612,14 +613,15 @@ class Application // filter out aliases for commands which are already on the list if (count($commands) > 1) { $commandList = $this->commandLoader ? array_merge(array_flip($this->commandLoader->getNames()), $this->commands) : $this->commands; - $commands = array_unique(array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) { + $commands = array_unique(array_filter($commands, function ($nameOrAlias) use ($commandList, $commands, &$aliases) { $commandName = $commandList[$nameOrAlias] instanceof Command ? $commandList[$nameOrAlias]->getName() : $nameOrAlias; + $aliases[$nameOrAlias] = $commandName; return $commandName === $nameOrAlias || !in_array($commandName, $commands); })); } - $exact = in_array($name, $commands, true); + $exact = in_array($name, $commands, true) || isset($aliases[$name]); if (count($commands) > 1 && !$exact) { $usableWidth = $this->terminal->getWidth() - 10; $abbrevs = array_values($commands); diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 4e1f8811b2..f207ce0d4f 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -57,6 +57,8 @@ class ApplicationTest extends TestCase require_once self::$fixturesPath.'/BarBucCommand.php'; require_once self::$fixturesPath.'/FooSubnamespaced1Command.php'; require_once self::$fixturesPath.'/FooSubnamespaced2Command.php'; + require_once self::$fixturesPath.'/TestTiti.php'; + require_once self::$fixturesPath.'/TestToto.php'; } protected function normalizeLineBreaks($text) @@ -283,6 +285,14 @@ class ApplicationTest extends TestCase $application->findNamespace('f'); } + public function testFindNonAmbiguous() + { + $application = new Application(); + $application->add(new \TestTiti()); + $application->add(new \TestToto()); + $this->assertEquals('test-toto', $application->find('test')->getName()); + } + /** * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException * @expectedExceptionMessage There are no commands defined in the "bar" namespace. diff --git a/src/Symfony/Component/Console/Tests/Fixtures/TestTiti.php b/src/Symfony/Component/Console/Tests/Fixtures/TestTiti.php new file mode 100644 index 0000000000..72e29d2a0a --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/TestTiti.php @@ -0,0 +1,21 @@ +setName('test-titi') + ->setDescription('The test:titi command') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $output->write('test-titi'); + } +} diff --git a/src/Symfony/Component/Console/Tests/Fixtures/TestToto.php b/src/Symfony/Component/Console/Tests/Fixtures/TestToto.php new file mode 100644 index 0000000000..f14805db68 --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/TestToto.php @@ -0,0 +1,22 @@ +setName('test-toto') + ->setDescription('The test-toto command') + ->setAliases(array('test')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $output->write('test-toto'); + } +} diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 98f414783f..b8ac23bc84 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1990,6 +1990,7 @@ class Request 'js' => array('application/javascript', 'application/x-javascript', 'text/javascript'), 'css' => array('text/css'), 'json' => array('application/json', 'application/x-json'), + 'jsonld' => array('application/ld+json'), 'xml' => array('text/xml', 'application/xml', 'application/x-xml'), 'rdf' => array('application/rdf+xml'), 'atom' => array('application/atom+xml'), diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 3c4a13acf4..178c729001 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -372,6 +372,7 @@ class RequestTest extends TestCase array('js', array('application/javascript', 'application/x-javascript', 'text/javascript')), array('css', array('text/css')), array('json', array('application/json', 'application/x-json')), + array('jsonld', array('application/ld+json')), array('xml', array('text/xml', 'application/xml', 'application/x-xml')), array('rdf', array('application/rdf+xml')), array('atom', array('application/atom+xml')), @@ -968,6 +969,26 @@ class RequestTest extends TestCase $request->getClientIps(); } + /** + * @dataProvider getClientIpsWithConflictingHeadersProvider + */ + public function testGetClientIpsOnlyXHttpForwardedForTrusted($httpForwarded, $httpXForwardedFor) + { + $request = new Request(); + + $server = array( + 'REMOTE_ADDR' => '88.88.88.88', + 'HTTP_FORWARDED' => $httpForwarded, + 'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor, + ); + + Request::setTrustedProxies(array('88.88.88.88'), Request::HEADER_X_FORWARDED_FOR); + + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertSame(array_reverse(explode(',', $httpXForwardedFor)), $request->getClientIps()); + } + public function getClientIpsWithConflictingHeadersProvider() { // $httpForwarded $httpXForwardedFor diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index 22af36e7c4..08963bcbaa 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -326,12 +326,20 @@ class Process implements \IteratorAggregate // @see : https://bugs.php.net/69442 $ptsWorkaround = fopen(__FILE__, 'r'); } + if (defined('HHVM_VERSION')) { + $envPairs = $env; + } else { + $envPairs = array(); + foreach ($env as $k => $v) { + $envPairs[] = $k.'='.$v; + } + } if (!is_dir($this->cwd)) { @trigger_error('The provided cwd does not exist. Command is currently ran against getcwd(). This behavior is deprecated since version 3.4 and will be removed in 4.0.', E_USER_DEPRECATED); } - $this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $env, $this->options); + $this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $envPairs, $this->options); if (!is_resource($this->process)) { throw new RuntimeException('Unable to launch a new process.'); @@ -1722,15 +1730,11 @@ class Process implements \IteratorAggregate private function getDefaultEnv() { - if (\PHP_VERSION_ID >= 70100) { - $env = getenv(); - } else { - $env = array(); + $env = array(); - foreach ($_SERVER as $k => $v) { - if (is_string($v) && false !== $v = getenv($k)) { - $env[$k] = $v; - } + foreach ($_SERVER as $k => $v) { + if (is_string($v) && false !== $v = getenv($k)) { + $env[$k] = $v; } } diff --git a/src/Symfony/Component/Process/Tests/ProcessTest.php b/src/Symfony/Component/Process/Tests/ProcessTest.php index 914959b58c..fccf0dc0f3 100644 --- a/src/Symfony/Component/Process/Tests/ProcessTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessTest.php @@ -1441,14 +1441,14 @@ class ProcessTest extends TestCase public function testEnvIsInherited() { - $process = $this->getProcessForCode('echo serialize($_SERVER);', null, array('BAR' => 'BAZ')); + $process = $this->getProcessForCode('echo serialize($_SERVER);', null, array('BAR' => 'BAZ', 'EMPTY' => '')); putenv('FOO=BAR'); $_ENV['FOO'] = 'BAR'; $process->run(); - $expected = array('BAR' => 'BAZ', 'FOO' => 'BAR'); + $expected = array('BAR' => 'BAZ', 'EMPTY' => '', 'FOO' => 'BAR'); $env = array_intersect_key(unserialize($process->getOutput()), $expected); $this->assertEquals($expected, $env); diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index c23229dd86..d0db3946e4 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -245,7 +245,7 @@ class PropertyAccessor implements PropertyAccessorInterface /** * @internal */ - public static function handleError($type, $message, $file, $line, $context) + public static function handleError($type, $message, $file, $line, $context = array()) { if (E_RECOVERABLE_ERROR === $type) { self::throwInvalidArgumentException($message, debug_backtrace(false), 1); diff --git a/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php index b56b10f2dc..6a15d8da90 100644 --- a/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php @@ -57,7 +57,7 @@ class CustomNormalizer implements NormalizerInterface, DenormalizerInterface, Se } /** - * Checks if the given class implements the NormalizableInterface. + * Checks if the given class implements the DenormalizableInterface. * * @param mixed $data Data to denormalize from * @param string $type The class to which the data should be denormalized diff --git a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php index 5e0a0b1531..e324692700 100644 --- a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php @@ -212,7 +212,7 @@ abstract class AbstractCloner implements ClonerInterface */ public function cloneVar($var, $filter = 0) { - $this->prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context) { + $this->prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = array()) { if (E_RECOVERABLE_ERROR === $type || E_USER_ERROR === $type) { // Cloner never dies throw new \ErrorException($msg, 0, $type, $file, $line);