diff --git a/UPGRADE-3.3.md b/UPGRADE-3.3.md index 40d864222a..d20f403d5a 100644 --- a/UPGRADE-3.3.md +++ b/UPGRADE-3.3.md @@ -84,7 +84,7 @@ DependencyInjection * Autowiring services based on the types they implement is deprecated and won't be supported in version 4.0. Rename (or alias) your services to their FQCN id to make them autowirable. - * [BC BREAK] The `NullDumper` class has been made final + * The `NullDumper` class has been made final * [BC BREAK] `_defaults` and `_instanceof` are now reserved service names in Yaml configurations. Please rename any services with that names. diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 07df3ff889..3fc769c90b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -69,16 +69,11 @@ use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader; use Symfony\Component\Routing\Loader\AnnotationFileLoader; use Symfony\Component\Security\Core\Security; -use Symfony\Component\Serializer\Encoder\CsvEncoder; use Symfony\Component\Serializer\Encoder\DecoderInterface; use Symfony\Component\Serializer\Encoder\EncoderInterface; -use Symfony\Component\Serializer\Encoder\YamlEncoder; use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory; -use Symfony\Component\Serializer\Normalizer\DataUriNormalizer; use Symfony\Component\Serializer\Normalizer\DateIntervalNormalizer; -use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; -use Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Stopwatch\Stopwatch; use Symfony\Component\Translation\Command\XliffLintCommand as BaseXliffLintCommand; @@ -1533,39 +1528,6 @@ class FrameworkExtension extends Extension */ private function registerSerializerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { - if (class_exists('Symfony\Component\Serializer\Normalizer\DataUriNormalizer')) { - // Run before serializer.normalizer.object - $definition = $container->register('serializer.normalizer.data_uri', DataUriNormalizer::class); - $definition->setPublic(false); - $definition->addTag('serializer.normalizer', array('priority' => -920)); - } - - if (class_exists('Symfony\Component\Serializer\Normalizer\DateTimeNormalizer')) { - // Run before serializer.normalizer.object - $definition = $container->register('serializer.normalizer.datetime', DateTimeNormalizer::class); - $definition->setPublic(false); - $definition->addTag('serializer.normalizer', array('priority' => -910)); - } - - if (class_exists('Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer')) { - // Run before serializer.normalizer.object - $definition = $container->register('serializer.normalizer.json_serializable', JsonSerializableNormalizer::class); - $definition->setPublic(false); - $definition->addTag('serializer.normalizer', array('priority' => -900)); - } - - if (class_exists(YamlEncoder::class) && defined('Symfony\Component\Yaml\Yaml::DUMP_OBJECT')) { - $definition = $container->register('serializer.encoder.yaml', YamlEncoder::class); - $definition->setPublic(false); - $definition->addTag('serializer.encoder'); - } - - if (class_exists(CsvEncoder::class)) { - $definition = $container->register('serializer.encoder.csv', CsvEncoder::class); - $definition->setPublic(false); - $definition->addTag('serializer.encoder'); - } - $loader->load('serializer.xml'); if (!class_exists(DateIntervalNormalizer::class)) { @@ -1630,7 +1592,7 @@ class FrameworkExtension extends Extension $container->getDefinition('serializer.mapping.class_metadata_factory')->replaceArgument( 1, new Reference($config['cache']) ); - } elseif (!$container->getParameter('kernel.debug') && class_exists(CacheClassMetadataFactory::class)) { + } elseif (!$container->getParameter('kernel.debug')) { $cacheMetadataFactory = new Definition( CacheClassMetadataFactory::class, array( diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml index 16edff5446..42dc667530 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml @@ -30,6 +30,21 @@ + + + + + + + + + + + + + + + null @@ -93,6 +108,14 @@ + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index 80f54afd20..50122ed194 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -166,12 +166,11 @@ class ConfigurationTest extends TestCase */ public function testInvalidAssetsConfiguration(array $assetConfig, $expectedMessage) { - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}( - InvalidConfigurationException::class, - $expectedMessage - ); - if (method_exists($this, 'expectExceptionMessage')) { + if (method_exists($this, 'expectException')) { + $this->expectException(InvalidConfigurationException::class); $this->expectExceptionMessage($expectedMessage); + } else { + $this->setExpectedException(InvalidConfigurationException::class, $expectedMessage); } $processor = new Processor(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 89f8f48182..e5d23ad7d6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -36,7 +36,6 @@ use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; use Symfony\Component\Serializer\Normalizer\DateIntervalNormalizer; use Symfony\Component\Serializer\Serializer; -use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory; use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader; use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader; use Symfony\Component\Serializer\Normalizer\DataUriNormalizer; @@ -764,10 +763,6 @@ abstract class FrameworkExtensionTest extends TestCase public function testDataUriNormalizerRegistered() { - if (!class_exists('Symfony\Component\Serializer\Normalizer\DataUriNormalizer')) { - $this->markTestSkipped('The DataUriNormalizer has been introduced in the Serializer Component version 3.1.'); - } - $container = $this->createContainerFromFile('full'); $definition = $container->getDefinition('serializer.normalizer.data_uri'); @@ -794,10 +789,6 @@ abstract class FrameworkExtensionTest extends TestCase public function testDateTimeNormalizerRegistered() { - if (!class_exists('Symfony\Component\Serializer\Normalizer\DateTimeNormalizer')) { - $this->markTestSkipped('The DateTimeNormalizer has been introduced in the Serializer Component version 3.1.'); - } - $container = $this->createContainerFromFile('full'); $definition = $container->getDefinition('serializer.normalizer.datetime'); @@ -809,10 +800,6 @@ abstract class FrameworkExtensionTest extends TestCase public function testJsonSerializableNormalizerRegistered() { - if (!class_exists('Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer')) { - $this->markTestSkipped('The JsonSerializableNormalizer has been introduced in the Serializer Component version 3.1.'); - } - $container = $this->createContainerFromFile('full'); $definition = $container->getDefinition('serializer.normalizer.json_serializable'); @@ -835,10 +822,6 @@ abstract class FrameworkExtensionTest extends TestCase public function testSerializerCacheActivated() { - if (!class_exists(CacheClassMetadataFactory::class) || !method_exists(XmlFileLoader::class, 'getMappedClasses') || !method_exists(YamlFileLoader::class, 'getMappedClasses')) { - $this->markTestSkipped('The Serializer default cache warmer has been introduced in the Serializer Component version 3.2.'); - } - $container = $this->createContainerFromFile('serializer_enabled'); $this->assertTrue($container->hasDefinition('serializer.mapping.cache_class_metadata_factory')); diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php index 667acfc350..3fedcbddc0 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php @@ -43,7 +43,14 @@ class ExtensionPass implements CompilerPassInterface if ($container->has('form.extension')) { $container->getDefinition('twig.extension.form')->addTag('twig.extension'); $reflClass = new \ReflectionClass('Symfony\Bridge\Twig\Extension\FormExtension'); - $container->getDefinition('twig.loader.native_filesystem')->addMethodCall('addPath', array(dirname(dirname($reflClass->getFileName())).'/Resources/views/Form')); + + $coreThemePath = dirname(dirname($reflClass->getFileName())).'/Resources/views/Form'; + $container->getDefinition('twig.loader.native_filesystem')->addMethodCall('addPath', array($coreThemePath)); + + $paths = $container->getDefinition('twig.cache_warmer')->getArgument(2); + $paths[$coreThemePath] = null; + $container->getDefinition('twig.cache_warmer')->replaceArgument(2, $paths); + $container->getDefinition('twig.template_iterator')->replaceArgument(2, $paths); } if ($container->has('router')) { diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php index 3beb9b5cf9..f9443ba098 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php @@ -113,6 +113,7 @@ class TwigExtension extends Extension } } + // paths are modified in ExtensionPass if forms are enabled $container->getDefinition('twig.cache_warmer')->replaceArgument(2, $config['paths']); $container->getDefinition('twig.template_iterator')->replaceArgument(2, $config['paths']); diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php index 58565606c7..372f74f6f9 100644 --- a/src/Symfony/Component/Debug/DebugClassLoader.php +++ b/src/Symfony/Component/Debug/DebugClassLoader.php @@ -166,18 +166,11 @@ class DebugClassLoader } // Don't trigger deprecations for classes in the same vendor - if (2 > $len = 1 + (strpos($name, '\\', 1 + strpos($name, '\\')) ?: strpos($name, '_'))) { + if (2 > $len = 1 + (strpos($name, '\\') ?: strpos($name, '_'))) { $len = 0; $ns = ''; } else { - switch ($ns = substr($name, 0, $len)) { - case 'Symfony\Bridge\\': - case 'Symfony\Bundle\\': - case 'Symfony\Component\\': - $ns = 'Symfony\\'; - $len = strlen($ns); - break; - } + $ns = substr($name, 0, $len); } // Detect annotations on the class diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php index 5d201dff1e..a54b580e4e 100644 --- a/src/Symfony/Component/DomCrawler/Crawler.php +++ b/src/Symfony/Component/DomCrawler/Crawler.php @@ -1071,7 +1071,7 @@ class Crawler implements \Countable, \IteratorAggregate } /** - * @return \ArrayIterator + * @return \ArrayIterator|\DOMElement[] */ public function getIterator() { diff --git a/src/Symfony/Component/Form/Resources/translations/validators.sv.xlf b/src/Symfony/Component/Form/Resources/translations/validators.sv.xlf index 4e2518b16b..2a5e57ca81 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.sv.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.sv.xlf @@ -11,8 +11,8 @@ Den uppladdade filen var för stor. Försök ladda upp en mindre fil. - The CSRF token is invalid. - CSRF-symbolen är inte giltig. + The CSRF token is invalid. Please try to resubmit the form. + CSRF-elementet är inte giltigt. Försök att skicka formuläret igen. diff --git a/src/Symfony/Component/Form/Tests/ButtonBuilderTest.php b/src/Symfony/Component/Form/Tests/ButtonBuilderTest.php index 15df850796..014a5ae179 100644 --- a/src/Symfony/Component/Form/Tests/ButtonBuilderTest.php +++ b/src/Symfony/Component/Form/Tests/ButtonBuilderTest.php @@ -13,6 +13,7 @@ namespace Symfony\Component\Form\Tests; use PHPUnit\Framework\TestCase; use Symfony\Component\Form\ButtonBuilder; +use Symfony\Component\Form\Exception\InvalidArgumentException; /** * @author Alexander Cheprasov @@ -53,10 +54,12 @@ class ButtonBuilderTest extends TestCase */ public function testInvalidNames($name) { - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}( - '\Symfony\Component\Form\Exception\InvalidArgumentException', - 'Buttons cannot have empty names.' - ); + if (method_exists($this, 'expectException')) { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Buttons cannot have empty names.'); + } else { + $this->setExpectedException(InvalidArgumentException::class, 'Buttons cannot have empty names.'); + } new ButtonBuilder($name); } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateIntervalToArrayTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateIntervalToArrayTransformerTest.php index 25ad31f866..81e6b3bc6c 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateIntervalToArrayTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateIntervalToArrayTransformerTest.php @@ -181,7 +181,12 @@ class DateIntervalToArrayTransformerTest extends DateIntervalTestCase 'minutes' => '', 'seconds' => '6', ); - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}(TransformationFailedException::class, 'This amount of "minutes" is invalid'); + if (method_exists($this, 'expectException')) { + $this->expectException(TransformationFailedException::class); + $this->expectExceptionMessage('This amount of "minutes" is invalid'); + } else { + $this->setExpectedException(TransformationFailedException::class, 'This amount of "minutes" is invalid'); + } $transformer->reverseTransform($input); } @@ -191,7 +196,12 @@ class DateIntervalToArrayTransformerTest extends DateIntervalTestCase $input = array( 'invert' => '1', ); - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}(TransformationFailedException::class, 'The value of "invert" must be boolean'); + if (method_exists($this, 'expectException')) { + $this->expectException(TransformationFailedException::class); + $this->expectExceptionMessage('The value of "invert" must be boolean'); + } else { + $this->setExpectedException(TransformationFailedException::class, 'The value of "invert" must be boolean'); + } $transformer->reverseTransform($input); } diff --git a/src/Symfony/Component/Form/Tests/Util/OrderedHashMapTest.php b/src/Symfony/Component/Form/Tests/Util/OrderedHashMapTest.php index 89735ea618..fe922e9239 100644 --- a/src/Symfony/Component/Form/Tests/Util/OrderedHashMapTest.php +++ b/src/Symfony/Component/Form/Tests/Util/OrderedHashMapTest.php @@ -56,6 +56,15 @@ class OrderedHashMapTest extends TestCase $this->assertSame(array(0 => 1, 'foo' => 2, 1 => 3), iterator_to_array($map)); } + public function testInsertLooselyEqualKeys() + { + $map = new OrderedHashMap(); + $map['1 as a string'] = '1 as a string'; + $map[1] = 1; + + $this->assertSame(array('1 as a string' => '1 as a string', 1 => 1), iterator_to_array($map)); + } + /** * Updates should not change the position of an element, otherwise we could * turn foreach loops into endless loops if they change the current @@ -111,6 +120,17 @@ class OrderedHashMapTest extends TestCase $this->assertSame(array('second' => 2), iterator_to_array($map)); } + public function testUnsetFromLooselyEqualKeysHashMap() + { + $map = new OrderedHashMap(); + $map['1 as a string'] = '1 as a string'; + $map[1] = 1; + + unset($map[1]); + + $this->assertSame(array('1 as a string' => '1 as a string'), iterator_to_array($map)); + } + public function testUnsetNonExistingSucceeds() { $map = new OrderedHashMap(); diff --git a/src/Symfony/Component/Form/Util/OrderedHashMap.php b/src/Symfony/Component/Form/Util/OrderedHashMap.php index 78687032fe..24ec0d5299 100644 --- a/src/Symfony/Component/Form/Util/OrderedHashMap.php +++ b/src/Symfony/Component/Form/Util/OrderedHashMap.php @@ -133,7 +133,7 @@ class OrderedHashMap implements \ArrayAccess, \IteratorAggregate, \Countable : 1 + (int) max($this->orderedKeys); } - $this->orderedKeys[] = $key; + $this->orderedKeys[] = (string) $key; } $this->elements[$key] = $value; @@ -144,7 +144,7 @@ class OrderedHashMap implements \ArrayAccess, \IteratorAggregate, \Countable */ public function offsetUnset($key) { - if (false !== ($position = array_search($key, $this->orderedKeys))) { + if (false !== ($position = array_search((string) $key, $this->orderedKeys))) { array_splice($this->orderedKeys, $position, 1); unset($this->elements[$key]); diff --git a/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php b/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php index 7305f51640..11562475d8 100644 --- a/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php +++ b/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php @@ -118,7 +118,13 @@ class OrderedHashMapIterator implements \Iterator */ public function key() { - return $this->key; + if (null === $this->key) { + return null; + } + + $array = array($this->key => null); + + return key($array); } /** diff --git a/src/Symfony/Component/HttpFoundation/AcceptHeader.php b/src/Symfony/Component/HttpFoundation/AcceptHeader.php index 99be6768f9..c6fd85f947 100644 --- a/src/Symfony/Component/HttpFoundation/AcceptHeader.php +++ b/src/Symfony/Component/HttpFoundation/AcceptHeader.php @@ -153,7 +153,7 @@ class AcceptHeader private function sort() { if (!$this->sorted) { - uasort($this->items, function ($a, $b) { + uasort($this->items, function (AcceptHeaderItem $a, AcceptHeaderItem $b) { $qA = $a->getQuality(); $qB = $b->getQuality(); diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 0f69b0d02b..06144543d1 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -144,7 +144,7 @@ class Request public $headers; /** - * @var string + * @var string|resource */ protected $content; @@ -221,7 +221,6 @@ class Request protected static $requestFactory; private $isHostValid = true; - private $isClientIpsValid = true; private $isForwardedValid = true; private static $trustedHeaderSet = -1; @@ -1887,6 +1886,9 @@ class Request // Does the baseUrl have anything in common with the request_uri? $requestUri = $this->getRequestUri(); + if ($requestUri !== '' && $requestUri[0] !== '/') { + $requestUri = '/'.$requestUri; + } if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) { // full $baseUrl matches @@ -1959,9 +1961,12 @@ class Request } // Remove the query string from REQUEST_URI - if ($pos = strpos($requestUri, '?')) { + if (false !== $pos = strpos($requestUri, '?')) { $requestUri = substr($requestUri, 0, $pos); } + if ($requestUri !== '' && $requestUri[0] !== '/') { + $requestUri = '/'.$requestUri; + } $pathInfo = substr($requestUri, strlen($baseUrl)); if (null !== $baseUrl && (false === $pathInfo || '' === $pathInfo)) { diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php index 19bf6e9bca..bccfbf611f 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -413,7 +413,7 @@ class PdoSessionHandler extends AbstractSessionHandler $this->gcCalled = false; // delete the session records that have expired - $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol < :time"; + $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol < :time - $this->timeCol"; $stmt = $this->pdo->prepare($sql); $stmt->bindValue(':time', time(), \PDO::PARAM_INT); diff --git a/src/Symfony/Component/HttpFoundation/StreamedResponse.php b/src/Symfony/Component/HttpFoundation/StreamedResponse.php index 329cea12dd..92868d33e4 100644 --- a/src/Symfony/Component/HttpFoundation/StreamedResponse.php +++ b/src/Symfony/Component/HttpFoundation/StreamedResponse.php @@ -84,7 +84,7 @@ class StreamedResponse extends Response public function sendHeaders() { if ($this->headersSent) { - return; + return $this; } $this->headersSent = true; @@ -102,7 +102,7 @@ class StreamedResponse extends Response public function sendContent() { if ($this->streamed) { - return; + return $this; } $this->streamed = true; diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 47459c79af..3c4a13acf4 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -1286,6 +1286,12 @@ class RequestTest extends TestCase $request->initialize(array(), array(), array(), array(), array(), $server); $this->assertEquals('/path%20test/info', $request->getPathInfo()); + + $server = array(); + $server['REQUEST_URI'] = '?a=b'; + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('/', $request->getPathInfo()); } public function testGetParameterPrecedence() @@ -2218,6 +2224,61 @@ class RequestTest extends TestCase 'trusted with partially-broken via' => array('HTTP/2.0', true, '1.0 fred, foo', 'HTTP/1.0'), ); } + + public function nonstandardRequestsData() + { + return array( + array('', '', '/', 'http://host:8080/', ''), + array('/', '', '/', 'http://host:8080/', ''), + + array('hello/app.php/x', '', '/x', 'http://host:8080/hello/app.php/x', '/hello', '/hello/app.php'), + array('/hello/app.php/x', '', '/x', 'http://host:8080/hello/app.php/x', '/hello', '/hello/app.php'), + + array('', 'a=b', '/', 'http://host:8080/?a=b'), + array('?a=b', 'a=b', '/', 'http://host:8080/?a=b'), + array('/?a=b', 'a=b', '/', 'http://host:8080/?a=b'), + + array('x', 'a=b', '/x', 'http://host:8080/x?a=b'), + array('x?a=b', 'a=b', '/x', 'http://host:8080/x?a=b'), + array('/x?a=b', 'a=b', '/x', 'http://host:8080/x?a=b'), + + array('hello/x', '', '/x', 'http://host:8080/hello/x', '/hello'), + array('/hello/x', '', '/x', 'http://host:8080/hello/x', '/hello'), + + array('hello/app.php/x', 'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'), + array('hello/app.php/x?a=b', 'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'), + array('/hello/app.php/x?a=b', 'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'), + ); + } + + /** + * @dataProvider nonstandardRequestsData + */ + public function testNonstandardRequests($requestUri, $queryString, $expectedPathInfo, $expectedUri, $expectedBasePath = '', $expectedBaseUrl = null) + { + if (null === $expectedBaseUrl) { + $expectedBaseUrl = $expectedBasePath; + } + + $server = array( + 'HTTP_HOST' => 'host:8080', + 'SERVER_PORT' => '8080', + 'QUERY_STRING' => $queryString, + 'PHP_SELF' => '/hello/app.php', + 'SCRIPT_FILENAME' => '/some/path/app.php', + 'REQUEST_URI' => $requestUri, + ); + + $request = new Request(array(), array(), array(), array(), array(), $server); + + $this->assertEquals($expectedPathInfo, $request->getPathInfo()); + $this->assertEquals($expectedUri, $request->getUri()); + $this->assertEquals($queryString, $request->getQueryString()); + $this->assertEquals(8080, $request->getPort()); + $this->assertEquals('host:8080', $request->getHttpHost()); + $this->assertEquals($expectedBaseUrl, $request->getBaseUrl()); + $this->assertEquals($expectedBasePath, $request->getBasePath()); + } } class RequestContentProxy extends Request diff --git a/src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.php index 1e35eb88b9..c2ded996fa 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.php @@ -112,4 +112,15 @@ class StreamedResponseTest extends TestCase $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response); $this->assertEquals(204, $response->getStatusCode()); } + + public function testReturnThis() + { + $response = new StreamedResponse(function () {}); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendContent()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendContent()); + + $response = new StreamedResponse(function () {}); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendHeaders()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendHeaders()); + } } diff --git a/src/Symfony/Component/HttpKernel/UriSigner.php b/src/Symfony/Component/HttpKernel/UriSigner.php index c3df23197c..28459b4ecd 100644 --- a/src/Symfony/Component/HttpKernel/UriSigner.php +++ b/src/Symfony/Component/HttpKernel/UriSigner.php @@ -58,10 +58,6 @@ class UriSigner /** * Checks that a URI contains the correct hash. * - * The query string parameter must be the last one - * (as it is generated that way by the sign() method, it should - * never be a problem). - * * @param string $uri A signed URI * * @return bool True if the URI is signed correctly, false otherwise diff --git a/src/Symfony/Component/Process/Tests/ProcessFailedExceptionTest.php b/src/Symfony/Component/Process/Tests/ProcessFailedExceptionTest.php index 5f739158c9..25712af7de 100644 --- a/src/Symfony/Component/Process/Tests/ProcessFailedExceptionTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessFailedExceptionTest.php @@ -29,10 +29,12 @@ class ProcessFailedExceptionTest extends TestCase ->method('isSuccessful') ->will($this->returnValue(true)); - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}( - '\InvalidArgumentException', - 'Expected a failed process, but the given process was successful.' - ); + if (method_exists($this, 'expectException')) { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Expected a failed process, but the given process was successful.'); + } else { + $this->setExpectedException(\InvalidArgumentException::class, 'Expected a failed process, but the given process was successful.'); + } new ProcessFailedException($process); } diff --git a/src/Symfony/Component/PropertyInfo/Tests/TypeTest.php b/src/Symfony/Component/PropertyInfo/Tests/TypeTest.php index 9266f9808f..cd1cab9167 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/TypeTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/TypeTest.php @@ -37,6 +37,12 @@ class TypeTest extends TestCase $this->assertEquals(Type::BUILTIN_TYPE_STRING, $collectionValueType->getBuiltinType()); } + public function testIterable() + { + $type = new Type('iterable'); + $this->assertSame('iterable', $type->getBuiltinType()); + } + /** * @expectedException \InvalidArgumentException * @expectedExceptionMessage "foo" is not a valid PHP type. diff --git a/src/Symfony/Component/PropertyInfo/Type.php b/src/Symfony/Component/PropertyInfo/Type.php index 5d05a48f09..d7fc6869c7 100644 --- a/src/Symfony/Component/PropertyInfo/Type.php +++ b/src/Symfony/Component/PropertyInfo/Type.php @@ -29,6 +29,7 @@ class Type const BUILTIN_TYPE_ARRAY = 'array'; const BUILTIN_TYPE_NULL = 'null'; const BUILTIN_TYPE_CALLABLE = 'callable'; + const BUILTIN_TYPE_ITERABLE = 'iterable'; /** * List of PHP builtin types. @@ -45,6 +46,7 @@ class Type self::BUILTIN_TYPE_ARRAY, self::BUILTIN_TYPE_CALLABLE, self::BUILTIN_TYPE_NULL, + self::BUILTIN_TYPE_ITERABLE, ); /** @@ -104,7 +106,7 @@ class Type /** * Gets built-in type. * - * Can be bool, int, float, string, array, object, resource, null or callback. + * Can be bool, int, float, string, array, object, resource, null, callback or iterable. * * @return string */ diff --git a/src/Symfony/Component/Routing/RouteCollectionBuilder.php b/src/Symfony/Component/Routing/RouteCollectionBuilder.php index 54bd86b7ea..6e43267983 100644 --- a/src/Symfony/Component/Routing/RouteCollectionBuilder.php +++ b/src/Symfony/Component/Routing/RouteCollectionBuilder.php @@ -318,10 +318,10 @@ class RouteCollectionBuilder $routeCollection->addCollection($subCollection); } + } - foreach ($this->resources as $resource) { - $routeCollection->addResource($resource); - } + foreach ($this->resources as $resource) { + $routeCollection->addResource($resource); } return $routeCollection; diff --git a/src/Symfony/Component/Routing/Tests/RouteCollectionBuilderTest.php b/src/Symfony/Component/Routing/Tests/RouteCollectionBuilderTest.php index 058a100d1f..6fc592affc 100644 --- a/src/Symfony/Component/Routing/Tests/RouteCollectionBuilderTest.php +++ b/src/Symfony/Component/Routing/Tests/RouteCollectionBuilderTest.php @@ -12,7 +12,9 @@ namespace Symfony\Component\Routing\Tests; use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Routing\Loader\YamlFileLoader; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollectionBuilder; @@ -59,7 +61,18 @@ class RouteCollectionBuilderTest extends TestCase $this->assertCount(1, $addedCollection->getResources()); // make sure the routes were imported into the top-level builder + $routeCollection = $routes->build(); $this->assertCount(1, $routes->build()); + $this->assertCount(1, $routeCollection->getResources()); + } + + public function testImportAddResources() + { + $routeCollectionBuilder = new RouteCollectionBuilder(new YamlFileLoader(new FileLocator(array(__DIR__.'/Fixtures/')))); + $routeCollectionBuilder->import('file_resource.yml'); + $routeCollection = $routeCollectionBuilder->build(); + + $this->assertCount(1, $routeCollection->getResources()); } /** diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/AnonymousAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/AnonymousAuthenticationProvider.php index c54b64df87..8c4ee9a475 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/AnonymousAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/AnonymousAuthenticationProvider.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Core\Authentication\Provider; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; @@ -44,7 +45,7 @@ class AnonymousAuthenticationProvider implements AuthenticationProviderInterface public function authenticate(TokenInterface $token) { if (!$this->supports($token)) { - return; + throw new AuthenticationException('The token is not supported by this authentication provider.'); } if ($this->secret !== $token->getSecret()) { diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php index b871f1f5a3..d03fcb460f 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php @@ -13,6 +13,7 @@ namespace Symfony\Component\Security\Core\Authentication\Provider; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\User\UserCheckerInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; @@ -51,7 +52,7 @@ class PreAuthenticatedAuthenticationProvider implements AuthenticationProviderIn public function authenticate(TokenInterface $token) { if (!$this->supports($token)) { - return; + throw new AuthenticationException('The token is not supported by this authentication provider.'); } if (!$user = $token->getUser()) { diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php index 397dd61111..f8ba7476ba 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php @@ -14,6 +14,7 @@ namespace Symfony\Component\Security\Core\Authentication\Provider; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; +use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; class RememberMeAuthenticationProvider implements AuthenticationProviderInterface @@ -40,7 +41,7 @@ class RememberMeAuthenticationProvider implements AuthenticationProviderInterfac public function authenticate(TokenInterface $token) { if (!$this->supports($token)) { - return; + throw new AuthenticationException('The token is not supported by this authentication provider.'); } if ($this->secret !== $token->getSecret()) { @@ -49,6 +50,7 @@ class RememberMeAuthenticationProvider implements AuthenticationProviderInterfac $user = $token->getUser(); $this->userChecker->checkPreAuth($user); + $this->userChecker->checkPostAuth($user); $authenticatedToken = new RememberMeToken($user, $this->providerKey, $this->secret); $authenticatedToken->setAttributes($token->getAttributes()); diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/UserAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/UserAuthenticationProvider.php index 738c09c427..5a06a96b5a 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/UserAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/UserAuthenticationProvider.php @@ -56,7 +56,7 @@ abstract class UserAuthenticationProvider implements AuthenticationProviderInter public function authenticate(TokenInterface $token) { if (!$this->supports($token)) { - return; + throw new AuthenticationException('The token is not supported by this authentication provider.'); } $username = $token->getUsername(); diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/AnonymousAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/AnonymousAuthenticationProviderTest.php index 5ded85b073..e6c0f6853a 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/AnonymousAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/AnonymousAuthenticationProviderTest.php @@ -24,11 +24,15 @@ class AnonymousAuthenticationProviderTest extends TestCase $this->assertFalse($provider->supports($this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock())); } + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException + * @expectedExceptionMessage The token is not supported by this authentication provider. + */ public function testAuthenticateWhenTokenIsNotSupported() { $provider = $this->getProvider('foo'); - $this->assertNull($provider->authenticate($this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock())); + $provider->authenticate($this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock()); } /** diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/PreAuthenticatedAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/PreAuthenticatedAuthenticationProviderTest.php index 5a78d0ebb9..5a6b04d5b4 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/PreAuthenticatedAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/PreAuthenticatedAuthenticationProviderTest.php @@ -36,11 +36,15 @@ class PreAuthenticatedAuthenticationProviderTest extends TestCase $this->assertFalse($provider->supports($token)); } + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException + * @expectedExceptionMessage The token is not supported by this authentication provider. + */ public function testAuthenticateWhenTokenIsNotSupported() { $provider = $this->getProvider(); - $this->assertNull($provider->authenticate($this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock())); + $provider->authenticate($this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock()); } /** diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php index 4865cb177d..497f315c33 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php @@ -26,12 +26,16 @@ class RememberMeAuthenticationProviderTest extends TestCase $this->assertFalse($provider->supports($this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock())); } + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException + * @expectedExceptionMessage The token is not supported by this authentication provider. + */ public function testAuthenticateWhenTokenIsNotSupported() { $provider = $this->getProvider(); $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); - $this->assertNull($provider->authenticate($token)); + $provider->authenticate($token); } /** diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/UserAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/UserAuthenticationProviderTest.php index da6136f221..a08ca3f813 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/UserAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/UserAuthenticationProviderTest.php @@ -29,11 +29,15 @@ class UserAuthenticationProviderTest extends TestCase $this->assertFalse($provider->supports($this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock())); } + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException + * @expectedExceptionMessage The token is not supported by this authentication provider. + */ public function testAuthenticateWhenTokenIsNotSupported() { $provider = $this->getProvider(); - $this->assertNull($provider->authenticate($this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock())); + $provider->authenticate($this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock()); } /** diff --git a/src/Symfony/Component/Serializer/Encoder/YamlEncoder.php b/src/Symfony/Component/Serializer/Encoder/YamlEncoder.php index 41ac04f209..3ac1af679a 100644 --- a/src/Symfony/Component/Serializer/Encoder/YamlEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/YamlEncoder.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Serializer\Encoder; +use Symfony\Component\Serializer\Exception\RuntimeException; use Symfony\Component\Yaml\Dumper; use Symfony\Component\Yaml\Parser; @@ -29,6 +30,10 @@ class YamlEncoder implements EncoderInterface, DecoderInterface public function __construct(Dumper $dumper = null, Parser $parser = null, array $defaultContext = array()) { + if (!class_exists(Dumper::class)) { + throw new RuntimeException('The YamlEncoder class requires the "Yaml" component. Install "symfony/yaml" to use it.'); + } + $this->dumper = $dumper ?: new Dumper(); $this->parser = $parser ?: new Parser(); $this->defaultContext = array_merge($this->defaultContext, $defaultContext); diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php index 87fdfca11c..dac7f06218 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php @@ -15,6 +15,7 @@ use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; +use Symfony\Component\Serializer\Exception\RuntimeException; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; @@ -32,6 +33,10 @@ class ObjectNormalizer extends AbstractObjectNormalizer public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyAccessorInterface $propertyAccessor = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null) { + if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccess')) { + throw new RuntimeException('The ObjectNormalizer class requires the "PropertyAccess" component. Install "symfony/property-access" to use it.'); + } + parent::__construct($classMetadataFactory, $nameConverter, $propertyTypeExtractor); $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor(); diff --git a/src/Symfony/Component/Validator/Constraints/UrlValidator.php b/src/Symfony/Component/Validator/Constraints/UrlValidator.php index e8517d889e..216759cda9 100644 --- a/src/Symfony/Component/Validator/Constraints/UrlValidator.php +++ b/src/Symfony/Component/Validator/Constraints/UrlValidator.php @@ -23,7 +23,7 @@ class UrlValidator extends ConstraintValidator { const PATTERN = '~^ (%s):// # protocol - (([\pL\pN-]+:)?([\pL\pN-]+)@)? # basic auth + (([\.\pL\pN-]+:)?([\.\pL\pN-]+)@)? # basic auth ( ([\pL\pN\pS-\.])+(\.?([\pL\pN]|xn\-\-[\pL\pN-]+)+\.?) # a domain name | # or diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php index 633dcf6d57..13cacb0f3b 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php @@ -116,6 +116,9 @@ class UrlValidatorTest extends ConstraintValidatorTestCase array('http://xn--d1abbgf6aiiy.xn--p1ai/'), array('http://☎.com/'), array('http://username:password@symfony.com'), + array('http://user.name:password@symfony.com'), + array('http://username:pass.word@symfony.com'), + array('http://user.name:pass.word@symfony.com'), array('http://user-name@symfony.com'), array('http://symfony.com?'), array('http://symfony.com?query=1'),