diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 2d64894f41..94f0fabcc4 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,7 +4,7 @@ | Bug fix? | yes/no | New feature? | yes/no | BC breaks? | no -| Deprecations? | yes/no +| Deprecations? | yes/no | Tests pass? | yes | Fixed tickets | #... | License | MIT diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 0b1fbb47f3..1034172772 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -43,11 +43,11 @@ Symfony is the result of the work of many people who made the code better - Diego Saint Esteben (dosten) - Alexandre Salomé (alexandresalome) - William Durand (couac) + - Hamza Amrouche (simperfit) - ornicar - Francis Besset (francisbesset) - Iltar van der Berg (kjarli) - stealth35 ‏ (stealth35) - - Hamza Amrouche (simperfit) - Alexander Mols (asm89) - Yonel Ceruto (yonelceruto) - Bulat Shakirzyanov (avalanche123) @@ -57,8 +57,8 @@ Symfony is the result of the work of many people who made the code better - Henrik Bjørnskov (henrikbjorn) - Miha Vrhovnik - Diego Saint Esteben (dii3g0) - - Dany Maillard (maidmaid) - Pierre du Plessis (pierredup) + - Dany Maillard (maidmaid) - Konstantin Kudryashov (everzet) - Kevin Bond (kbond) - Bilal Amarni (bamarni) @@ -66,6 +66,7 @@ Symfony is the result of the work of many people who made the code better - Jérémy DERUSSÉ (jderusse) - Florin Patan (florinpatan) - Samuel ROZE (sroze) + - Tobias Nyholm (tobias) - Gábor Egyed (1ed) - Michel Weimerskirch (mweimerskirch) - Andrej Hudec (pulzarraider) @@ -75,7 +76,6 @@ Symfony is the result of the work of many people who made the code better - Titouan Galopin (tgalopin) - Konstantin Myakshin (koc) - Christian Raue - - Tobias Nyholm (tobias) - Arnout Boks (aboks) - Deni - Henrik Westphal (snc) @@ -200,6 +200,7 @@ Symfony is the result of the work of many people who made the code better - Matthieu Bontemps (mbontemps) - apetitpa - Pierre Minnieur (pminnieur) + - Jannik Zschiesche (apfelbox) - fivestar - Dominique Bongiraud - Jeremy Livingston (jeremylivingston) @@ -224,7 +225,6 @@ Symfony is the result of the work of many people who made the code better - Marcel Beerta (mazen) - gadelat (gadelat) - Loïc Faugeron - - Jannik Zschiesche (apfelbox) - Hidde Wieringa (hiddewie) - Marco Pivetta (ocramius) - Rob Frawley 2nd (robfrawley) @@ -697,6 +697,7 @@ Symfony is the result of the work of many people who made the code better - Nykopol (nykopol) - Jordan Deitch - Casper Valdemar Poulsen + - Remon van de Kamp - Josiah (josiah) - Joschi Kuphal - John Bohn (jbohn) @@ -736,6 +737,7 @@ Symfony is the result of the work of many people who made the code better - Adrien Lucas (adrienlucas) - Zhuravlev Alexander (scif) - James Michael DuPont + - Xavier HAUSHERR - Tom Klingenberg - Christopher Hall (mythmakr) - Patrick Dawkins (pjcdawkins) @@ -1102,7 +1104,6 @@ Symfony is the result of the work of many people who made the code better - Pierre Tachoire (krichprollsch) - Marc J. Schmidt (marcjs) - Marco Jantke - - Remon van de Kamp - Saem Ghani - Clément LEFEBVRE - Conrad Kleinespel @@ -1225,7 +1226,6 @@ Symfony is the result of the work of many people who made the code better - Sebastian Ionescu - Thomas Ploch - Simon Neidhold - - Xavier HAUSHERR - Valentin VALCIU - Jeremiah VALERIE - Kevin Dew @@ -1573,6 +1573,7 @@ Symfony is the result of the work of many people who made the code better - Vladimir Chernyshev (volch) - Yorkie Chadwick (yorkie76) - GuillaumeVerdon + - Philipp Keck - Ondrej Mirtes - akimsko - Youpie @@ -1630,6 +1631,7 @@ Symfony is the result of the work of many people who made the code better - Jordan Hoff - znerol - Christian Eikermann + - Kai Eichinger - Antonio Angelino - Matt Fields - Niklas Keller diff --git a/phpunit.xml.dist b/phpunit.xml.dist index e44535a81a..59ec772525 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -65,6 +65,7 @@ Cache\IntegrationTests Doctrine\Common\Cache Symfony\Component\Cache + Symfony\Component\Cache\Tests\Fixtures Symfony\Component\Cache\Traits Symfony\Component\Console Symfony\Component\HttpFoundation diff --git a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php index 86d96f08a1..a46617db08 100644 --- a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php +++ b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php @@ -104,7 +104,7 @@ class DoctrineOrmTypeGuesser implements FormTypeGuesserInterface $classMetadata = $classMetadatas[0]; // Check whether the field exists and is nullable or not - if ($classMetadata->hasField($property)) { + if (isset($classMetadata->fieldMappings[$property])) { if (!$classMetadata->isNullable($property) && Type::BOOLEAN !== $classMetadata->getTypeOfField($property)) { return new ValueGuess(true, Guess::HIGH_CONFIDENCE); } @@ -133,7 +133,7 @@ class DoctrineOrmTypeGuesser implements FormTypeGuesserInterface public function guessMaxLength($class, $property) { $ret = $this->getMetadata($class); - if ($ret && $ret[0]->hasField($property) && !$ret[0]->hasAssociation($property)) { + if ($ret && isset($ret[0]->fieldMappings[$property]) && !$ret[0]->hasAssociation($property)) { $mapping = $ret[0]->getFieldMapping($property); if (isset($mapping['length'])) { @@ -152,7 +152,7 @@ class DoctrineOrmTypeGuesser implements FormTypeGuesserInterface public function guessPattern($class, $property) { $ret = $this->getMetadata($class); - if ($ret && $ret[0]->hasField($property) && !$ret[0]->hasAssociation($property)) { + if ($ret && isset($ret[0]->fieldMappings[$property]) && !$ret[0]->hasAssociation($property)) { if (in_array($ret[0]->getTypeOfField($property), array(Type::DECIMAL, Type::FLOAT))) { return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE); } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php index 12296729e2..0eda4a3ba6 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php @@ -33,21 +33,20 @@ class DoctrineOrmTypeGuesserTest extends TestCase // Simple field, not nullable $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); - $classMetadata->expects($this->once())->method('hasField')->with('field')->will($this->returnValue(true)); + $classMetadata->fieldMappings['field'] = true; $classMetadata->expects($this->once())->method('isNullable')->with('field')->will($this->returnValue(false)); $return[] = array($classMetadata, new ValueGuess(true, Guess::HIGH_CONFIDENCE)); // Simple field, nullable $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); - $classMetadata->expects($this->once())->method('hasField')->with('field')->will($this->returnValue(true)); + $classMetadata->fieldMappings['field'] = true; $classMetadata->expects($this->once())->method('isNullable')->with('field')->will($this->returnValue(true)); $return[] = array($classMetadata, new ValueGuess(false, Guess::MEDIUM_CONFIDENCE)); // One-to-one, nullable (by default) $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); - $classMetadata->expects($this->once())->method('hasField')->with('field')->will($this->returnValue(false)); $classMetadata->expects($this->once())->method('isAssociationWithSingleJoinColumn')->with('field')->will($this->returnValue(true)); $mapping = array('joinColumns' => array(array())); @@ -57,7 +56,6 @@ class DoctrineOrmTypeGuesserTest extends TestCase // One-to-one, nullable (explicit) $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); - $classMetadata->expects($this->once())->method('hasField')->with('field')->will($this->returnValue(false)); $classMetadata->expects($this->once())->method('isAssociationWithSingleJoinColumn')->with('field')->will($this->returnValue(true)); $mapping = array('joinColumns' => array(array('nullable' => true))); @@ -67,7 +65,6 @@ class DoctrineOrmTypeGuesserTest extends TestCase // One-to-one, not nullable $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); - $classMetadata->expects($this->once())->method('hasField')->with('field')->will($this->returnValue(false)); $classMetadata->expects($this->once())->method('isAssociationWithSingleJoinColumn')->with('field')->will($this->returnValue(true)); $mapping = array('joinColumns' => array(array('nullable' => false))); @@ -77,7 +74,6 @@ class DoctrineOrmTypeGuesserTest extends TestCase // One-to-many, no clue $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); - $classMetadata->expects($this->once())->method('hasField')->with('field')->will($this->returnValue(false)); $classMetadata->expects($this->once())->method('isAssociationWithSingleJoinColumn')->with('field')->will($this->returnValue(false)); $return[] = array($classMetadata, null); diff --git a/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php b/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php index db388ab70b..a921582dba 100644 --- a/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php +++ b/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php @@ -54,8 +54,7 @@ class TwigExtractor extends AbstractFileExtractor implements ExtractorInterface */ public function extract($resource, MessageCatalogue $catalogue) { - $files = $this->extractFiles($resource); - foreach ($files as $file) { + foreach ($this->extractFiles($resource) as $file) { try { $this->extractTemplate(file_get_contents($file->getPathname()), $catalogue); } catch (Error $e) { diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index e2557ebc47..402c4ed9a8 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -23,7 +23,7 @@ "symfony/asset": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", "symfony/finder": "~3.4|~4.0", - "symfony/form": "^3.4.5|^4.0.5", + "symfony/form": "^3.4.7|^4.0.7", "symfony/http-foundation": "~3.4|~4.0", "symfony/http-kernel": "~3.4|~4.0", "symfony/polyfill-intl-icu": "~1.0", @@ -41,7 +41,7 @@ "symfony/workflow": "~3.4|~4.0" }, "conflict": { - "symfony/form": "<3.4.5|<4.0.5,>=4.0", + "symfony/form": "<3.4.7|<4.0.7,>=4.0", "symfony/console": "<3.4" }, "suggest": { diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AnnotationsCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AnnotationsCacheWarmer.php index e5839b7690..db6fe4ba61 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AnnotationsCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AnnotationsCacheWarmer.php @@ -28,17 +28,19 @@ class AnnotationsCacheWarmer extends AbstractPhpFileCacheWarmer { private $annotationReader; private $excludeRegexp; + private $debug; /** * @param Reader $annotationReader * @param string $phpArrayFile The PHP file where annotations are cached * @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered annotations are cached */ - public function __construct(Reader $annotationReader, string $phpArrayFile, CacheItemPoolInterface $fallbackPool, string $excludeRegexp = null) + public function __construct(Reader $annotationReader, string $phpArrayFile, CacheItemPoolInterface $fallbackPool, string $excludeRegexp = null, bool $debug = false) { parent::__construct($phpArrayFile, $fallbackPool); $this->annotationReader = $annotationReader; $this->excludeRegexp = $excludeRegexp; + $this->debug = $debug; } /** @@ -53,7 +55,7 @@ class AnnotationsCacheWarmer extends AbstractPhpFileCacheWarmer } $annotatedClasses = include $annotatedClassPatterns; - $reader = new CachedReader($this->annotationReader, new DoctrineProvider($arrayAdapter)); + $reader = new CachedReader($this->annotationReader, new DoctrineProvider($arrayAdapter), $this->debug); foreach ($annotatedClasses as $class) { if (null !== $this->excludeRegexp && preg_match($this->excludeRegexp, $class)) { diff --git a/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php b/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php index 5877caaf59..063097d9b4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php +++ b/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php @@ -11,7 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\HttpCache; -use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\HttpCache\HttpCache as BaseHttpCache; use Symfony\Component\HttpKernel\HttpCache\Esi; use Symfony\Component\HttpKernel\HttpCache\Store; @@ -23,16 +23,16 @@ use Symfony\Component\HttpFoundation\Response; * * @author Fabien Potencier */ -abstract class HttpCache extends BaseHttpCache +class HttpCache extends BaseHttpCache { protected $cacheDir; protected $kernel; /** - * @param HttpKernelInterface $kernel An HttpKernelInterface instance - * @param string $cacheDir The cache directory (default used if null) + * @param KernelInterface $kernel A KernelInterface instance + * @param string $cacheDir The cache directory (default used if null) */ - public function __construct(HttpKernelInterface $kernel, string $cacheDir = null) + public function __construct(KernelInterface $kernel, string $cacheDir = null) { $this->kernel = $kernel; $this->cacheDir = $cacheDir; diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.xml index 6e8cc4f9e6..2b4ea42962 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.xml @@ -38,6 +38,7 @@ %kernel.cache_dir%/annotations.php #^Symfony\\(?:Component\\HttpKernel\\|Bundle\\FrameworkBundle\\Controller\\(?!AbstractController$|Controller$))# + %kernel.debug% diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php new file mode 100644 index 0000000000..b32274e7e7 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php @@ -0,0 +1,103 @@ +cacheDir = sys_get_temp_dir().'/'.uniqid(); + $fs = new Filesystem(); + $fs->mkdir($this->cacheDir); + parent::setUp(); + } + + protected function tearDown() + { + $fs = new Filesystem(); + $fs->remove($this->cacheDir); + parent::tearDown(); + } + + public function testAnnotationsCacheWarmerWithDebugDisabled() + { + file_put_contents($this->cacheDir.'/annotations.map', sprintf('cacheDir, __FUNCTION__); + $reader = new AnnotationReader(); + $fallbackPool = new ArrayAdapter(); + $warmer = new AnnotationsCacheWarmer( + $reader, + $cacheFile, + $fallbackPool, + null + ); + $warmer->warmUp($this->cacheDir); + $this->assertFileExists($cacheFile); + + // Assert cache is valid + $reader = new CachedReader( + $this->getReadOnlyReader(), + new DoctrineProvider(new PhpArrayAdapter($cacheFile, new NullAdapter())) + ); + $refClass = new \ReflectionClass($this); + $reader->getClassAnnotations($refClass); + $reader->getMethodAnnotations($refClass->getMethod(__FUNCTION__)); + $reader->getPropertyAnnotations($refClass->getProperty('cacheDir')); + } + + public function testAnnotationsCacheWarmerWithDebugEnabled() + { + file_put_contents($this->cacheDir.'/annotations.map', sprintf('cacheDir, __FUNCTION__); + $reader = new AnnotationReader(); + $fallbackPool = new ArrayAdapter(); + $warmer = new AnnotationsCacheWarmer( + $reader, + $cacheFile, + $fallbackPool, + null, + true + ); + $warmer->warmUp($this->cacheDir); + $this->assertFileExists($cacheFile); + // Assert cache is valid + $reader = new CachedReader( + $this->getReadOnlyReader(), + new DoctrineProvider(new PhpArrayAdapter($cacheFile, new NullAdapter())), + true + ); + $refClass = new \ReflectionClass($this); + $reader->getClassAnnotations($refClass); + $reader->getMethodAnnotations($refClass->getMethod(__FUNCTION__)); + $reader->getPropertyAnnotations($refClass->getProperty('cacheDir')); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|Reader + */ + private function getReadOnlyReader() + { + $readerMock = $this->getMockBuilder('Doctrine\Common\Annotations\Reader')->getMock(); + $readerMock->expects($this->exactly(0))->method('getClassAnnotations'); + $readerMock->expects($this->exactly(0))->method('getClassAnnotation'); + $readerMock->expects($this->exactly(0))->method('getMethodAnnotations'); + $readerMock->expects($this->exactly(0))->method('getMethodAnnotation'); + $readerMock->expects($this->exactly(0))->method('getPropertyAnnotations'); + $readerMock->expects($this->exactly(0))->method('getPropertyAnnotation'); + + return $readerMock; + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SimpleFormFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SimpleFormFactory.php index 1f45ab1a44..c3be9ebd6d 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SimpleFormFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SimpleFormFactory.php @@ -55,6 +55,7 @@ class SimpleFormFactory extends FormLoginFactory ->replaceArgument(0, new Reference($config['authenticator'])) ->replaceArgument(1, new Reference($userProviderId)) ->replaceArgument(2, $id) + ->replaceArgument(3, new Reference('security.user_checker.'.$id)) ; return $provider; diff --git a/src/Symfony/Component/BrowserKit/Cookie.php b/src/Symfony/Component/BrowserKit/Cookie.php index a2201c9ce1..480f02b948 100644 --- a/src/Symfony/Component/BrowserKit/Cookie.php +++ b/src/Symfony/Component/BrowserKit/Cookie.php @@ -44,14 +44,14 @@ class Cookie /** * Sets a cookie. * - * @param string $name The cookie name - * @param string $value The value of the cookie - * @param string $expires The time the cookie expires - * @param string $path The path on the server in which the cookie will be available on - * @param string $domain The domain that the cookie is available - * @param bool $secure Indicates that the cookie should only be transmitted over a secure HTTPS connection from the client - * @param bool $httponly The cookie httponly flag - * @param bool $encodedValue Whether the value is encoded or not + * @param string $name The cookie name + * @param string $value The value of the cookie + * @param string|null $expires The time the cookie expires + * @param string|null $path The path on the server in which the cookie will be available on + * @param string $domain The domain that the cookie is available + * @param bool $secure Indicates that the cookie should only be transmitted over a secure HTTPS connection from the client + * @param bool $httponly The cookie httponly flag + * @param bool $encodedValue Whether the value is encoded or not */ public function __construct(string $name, ?string $value, string $expires = null, string $path = null, string $domain = '', bool $secure = false, bool $httponly = true, bool $encodedValue = false) { @@ -112,8 +112,8 @@ class Cookie /** * Creates a Cookie instance from a Set-Cookie header value. * - * @param string $cookie A Set-Cookie header value - * @param string $url The base URL + * @param string $cookie A Set-Cookie header value + * @param string|null $url The base URL * * @return static * @@ -242,7 +242,7 @@ class Cookie /** * Gets the expires time of the cookie. * - * @return string The cookie expires time + * @return string|null The cookie expires time */ public function getExpiresTime() { diff --git a/src/Symfony/Component/BrowserKit/CookieJar.php b/src/Symfony/Component/BrowserKit/CookieJar.php index 232cefc8b6..7a4d64c10e 100644 --- a/src/Symfony/Component/BrowserKit/CookieJar.php +++ b/src/Symfony/Component/BrowserKit/CookieJar.php @@ -43,32 +43,21 @@ class CookieJar { $this->flushExpiredCookies(); - if (!empty($domain)) { - foreach ($this->cookieJar as $cookieDomain => $pathCookies) { - if ($cookieDomain) { - $cookieDomain = '.'.ltrim($cookieDomain, '.'); - if ($cookieDomain != substr('.'.$domain, -strlen($cookieDomain))) { - continue; - } - } - - foreach ($pathCookies as $cookiePath => $namedCookies) { - if ($cookiePath != substr($path, 0, strlen($cookiePath))) { - continue; - } - if (isset($namedCookies[$name])) { - return $namedCookies[$name]; - } + foreach ($this->cookieJar as $cookieDomain => $pathCookies) { + if ($cookieDomain && $domain) { + $cookieDomain = '.'.ltrim($cookieDomain, '.'); + if ($cookieDomain !== substr('.'.$domain, -\strlen($cookieDomain))) { + continue; } } - return; - } - - // avoid relying on this behavior that is mainly here for BC reasons - foreach ($this->cookieJar as $cookies) { - if (isset($cookies[$path][$name])) { - return $cookies[$path][$name]; + foreach ($pathCookies as $cookiePath => $namedCookies) { + if (0 !== strpos($path, $cookiePath)) { + continue; + } + if (isset($namedCookies[$name])) { + return $namedCookies[$name]; + } } } } diff --git a/src/Symfony/Component/BrowserKit/Tests/CookieJarTest.php b/src/Symfony/Component/BrowserKit/Tests/CookieJarTest.php index 9c9e122e86..3117e5ce47 100644 --- a/src/Symfony/Component/BrowserKit/Tests/CookieJarTest.php +++ b/src/Symfony/Component/BrowserKit/Tests/CookieJarTest.php @@ -237,6 +237,8 @@ class CookieJarTest extends TestCase $this->assertEquals($cookie1, $cookieJar->get('foo', '/test', 'example.com')); $this->assertEquals($cookie2, $cookieJar->get('foo1', '/', 'example.com')); $this->assertEquals($cookie2, $cookieJar->get('foo1', '/bar', 'example.com')); + + $this->assertEquals($cookie2, $cookieJar->get('foo1', '/bar')); } public function testCookieWithWildcardDomain() diff --git a/src/Symfony/Component/Cache/Tests/Adapter/DoctrineAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/DoctrineAdapterTest.php index 93ec982438..8d4dfe2858 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/DoctrineAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/DoctrineAdapterTest.php @@ -11,8 +11,8 @@ namespace Symfony\Component\Cache\Tests\Adapter; -use Doctrine\Common\Cache\ArrayCache; use Symfony\Component\Cache\Adapter\DoctrineAdapter; +use Symfony\Component\Cache\Tests\Fixtures\ArrayCache; /** * @group time-sensitive diff --git a/src/Symfony/Component/Cache/Tests/Fixtures/ArrayCache.php b/src/Symfony/Component/Cache/Tests/Fixtures/ArrayCache.php new file mode 100644 index 0000000000..1a6157e822 --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/Fixtures/ArrayCache.php @@ -0,0 +1,52 @@ +doContains($id) ? $this->data[$id][0] : false; + } + + protected function doContains($id) + { + if (!isset($this->data[$id])) { + return false; + } + + $expiry = $this->data[$id][1]; + + return !$expiry || time() <= $expiry || !$this->doDelete($id); + } + + protected function doSave($id, $data, $lifeTime = 0) + { + $this->data[$id] = array($data, $lifeTime ? time() + $lifeTime : false); + + return true; + } + + protected function doDelete($id) + { + unset($this->data[$id]); + + return true; + } + + protected function doFlush() + { + $this->data = array(); + + return true; + } + + protected function doGetStats() + { + return null; + } +} diff --git a/src/Symfony/Component/Cache/Tests/Simple/DoctrineCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/DoctrineCacheTest.php index 0a185297ab..127c96858c 100644 --- a/src/Symfony/Component/Cache/Tests/Simple/DoctrineCacheTest.php +++ b/src/Symfony/Component/Cache/Tests/Simple/DoctrineCacheTest.php @@ -11,8 +11,8 @@ namespace Symfony\Component\Cache\Tests\Simple; -use Doctrine\Common\Cache\ArrayCache; use Symfony\Component\Cache\Simple\DoctrineCache; +use Symfony\Component\Cache\Tests\Fixtures\ArrayCache; /** * @group time-sensitive diff --git a/src/Symfony/Component/Cache/phpunit.xml.dist b/src/Symfony/Component/Cache/phpunit.xml.dist index 6b5c79331d..9b3c30d76f 100644 --- a/src/Symfony/Component/Cache/phpunit.xml.dist +++ b/src/Symfony/Component/Cache/phpunit.xml.dist @@ -39,6 +39,7 @@ Cache\IntegrationTests Doctrine\Common\Cache Symfony\Component\Cache + Symfony\Component\Cache\Tests\Fixtures Symfony\Component\Cache\Traits diff --git a/src/Symfony/Component/Config/Definition/ArrayNode.php b/src/Symfony/Component/Config/Definition/ArrayNode.php index fa2faea7fe..5f51a5da41 100644 --- a/src/Symfony/Component/Config/Definition/ArrayNode.php +++ b/src/Symfony/Component/Config/Definition/ArrayNode.php @@ -153,9 +153,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface } /** - * Sets the node Name. - * - * @param string $name The node's name + * {@inheritdoc} */ public function setName($name) { @@ -163,9 +161,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface } /** - * Checks if the node has a default value. - * - * @return bool + * {@inheritdoc} */ public function hasDefaultValue() { @@ -173,11 +169,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface } /** - * Retrieves the default value. - * - * @return array The default value - * - * @throws \RuntimeException if the node has no default value + * {@inheritdoc} */ public function getDefaultValue() { diff --git a/src/Symfony/Component/Config/Definition/BaseNode.php b/src/Symfony/Component/Config/Definition/BaseNode.php index b0a32c1b35..d942b28cc7 100644 --- a/src/Symfony/Component/Config/Definition/BaseNode.php +++ b/src/Symfony/Component/Config/Definition/BaseNode.php @@ -41,7 +41,7 @@ abstract class BaseNode implements NodeInterface */ public function __construct(?string $name, NodeInterface $parent = null, string $pathSeparator = self::DEFAULT_PATH_SEPARATOR) { - if (false !== strpos($name, $pathSeparator)) { + if (false !== strpos($name = (string) , $pathSeparator)) { throw new \InvalidArgumentException('The name must not contain "'.$pathSeparator.'".'); } @@ -185,9 +185,7 @@ abstract class BaseNode implements NodeInterface } /** - * Checks if this node is required. - * - * @return bool + * {@inheritdoc} */ public function isRequired() { @@ -218,9 +216,7 @@ abstract class BaseNode implements NodeInterface } /** - * Returns the name of this node. - * - * @return string The Node's name + * {@inheritdoc} */ public function getName() { @@ -228,9 +224,7 @@ abstract class BaseNode implements NodeInterface } /** - * Retrieves the path of this node. - * - * @return string The Node's path + * {@inheritdoc} */ public function getPath() { @@ -242,14 +236,7 @@ abstract class BaseNode implements NodeInterface } /** - * Merges two values together. - * - * @param mixed $leftSide - * @param mixed $rightSide - * - * @return mixed The merged value - * - * @throws ForbiddenOverwriteException + * {@inheritdoc} */ final public function merge($leftSide, $rightSide) { @@ -269,11 +256,7 @@ abstract class BaseNode implements NodeInterface } /** - * Normalizes a value, applying all normalization closures. - * - * @param mixed $value Value to normalize - * - * @return mixed The normalized value + * {@inheritdoc} */ final public function normalize($value) { @@ -321,14 +304,7 @@ abstract class BaseNode implements NodeInterface } /** - * Finalizes a value, applying all finalization closures. - * - * @param mixed $value The value to finalize - * - * @return mixed The finalized value - * - * @throws Exception - * @throws InvalidConfigurationException + * {@inheritdoc} */ final public function finalize($value) { diff --git a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php index c39bd0ad11..febcc67c7c 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php @@ -48,7 +48,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition } /** - * Sets a custom children builder. + * {@inheritdoc} */ public function setBuilder(NodeBuilder $builder) { @@ -56,9 +56,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition } /** - * Returns a builder to add children nodes. - * - * @return NodeBuilder + * {@inheritdoc} */ public function children() { @@ -366,17 +364,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition } /** - * Appends a node definition. - * - * $node = new ArrayNodeDefinition() - * ->children() - * ->scalarNode('foo')->end() - * ->scalarNode('baz')->end() - * ->end() - * ->append($this->getBarNodeDefinition()) - * ; - * - * @return $this + * {@inheritdoc} */ public function append(NodeDefinition $node) { diff --git a/src/Symfony/Component/Config/Definition/Builder/BuilderAwareInterface.php b/src/Symfony/Component/Config/Definition/Builder/BuilderAwareInterface.php index 8efbb58e21..f30b8736cf 100644 --- a/src/Symfony/Component/Config/Definition/Builder/BuilderAwareInterface.php +++ b/src/Symfony/Component/Config/Definition/Builder/BuilderAwareInterface.php @@ -18,5 +18,8 @@ namespace Symfony\Component\Config\Definition\Builder; */ interface BuilderAwareInterface { + /** + * Sets a custom children builder. + */ public function setBuilder(NodeBuilder $builder); } diff --git a/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php b/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php index bf43861fc1..ea2e42c1c6 100644 --- a/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php +++ b/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php @@ -143,8 +143,8 @@ class NodeBuilder implements NodeParentInterface /** * Creates a child node. * - * @param string $name The name of the node - * @param string $type The type of the node + * @param string|null $name The name of the node + * @param string $type The type of the node * * @return NodeDefinition The child node * diff --git a/src/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php b/src/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php index c75de68cab..41c52c235e 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php +++ b/src/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php @@ -21,9 +21,26 @@ namespace Symfony\Component\Config\Definition\Builder; interface ParentNodeDefinitionInterface extends BuilderAwareInterface { /** + * Returns a builder to add children nodes. + * * @return NodeBuilder */ public function children(); + /** + * Appends a node definition. + * + * Usage: + * + * $node = $parentNode + * ->children() + * ->scalarNode('foo')->end() + * ->scalarNode('baz')->end() + * ->append($this->getBarNodeDefinition()) + * ->end() + * ; + * + * @return $this + */ public function append(NodeDefinition $node); } diff --git a/src/Symfony/Component/Config/Definition/NodeInterface.php b/src/Symfony/Component/Config/Definition/NodeInterface.php index b9bddc4938..45f1f681c1 100644 --- a/src/Symfony/Component/Config/Definition/NodeInterface.php +++ b/src/Symfony/Component/Config/Definition/NodeInterface.php @@ -11,6 +11,10 @@ namespace Symfony\Component\Config\Definition; +use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + /** * Common Interface among all nodes. * @@ -59,11 +63,13 @@ interface NodeInterface public function getDefaultValue(); /** - * Normalizes the supplied value. + * Normalizes a value. * * @param mixed $value The value to normalize * * @return mixed The normalized value + * + * @throws InvalidTypeException if the value type is invalid */ public function normalize($value); @@ -73,7 +79,10 @@ interface NodeInterface * @param mixed $leftSide * @param mixed $rightSide * - * @return mixed The merged values + * @return mixed The merged value + * + * @throws ForbiddenOverwriteException if the configuration path cannot be overwritten + * @throws InvalidTypeException if the value type is invalid */ public function merge($leftSide, $rightSide); @@ -83,6 +92,9 @@ interface NodeInterface * @param mixed $value The value to finalize * * @return mixed The finalized value + * + * @throws InvalidTypeException if the value type is invalid + * @throws InvalidConfigurationException if the value is invalid configuration */ public function finalize($value); } diff --git a/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php b/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php index c576198b54..d183a60525 100644 --- a/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php +++ b/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php @@ -102,9 +102,7 @@ class PrototypedArrayNode extends ArrayNode } /** - * Checks if the node has a default value. - * - * @return bool + * {@inheritdoc} */ public function hasDefaultValue() { @@ -126,12 +124,10 @@ class PrototypedArrayNode extends ArrayNode } /** - * Retrieves the default value. + * {@inheritdoc} * * The default value could be either explicited or derived from the prototype * default value. - * - * @return array The default value */ public function getDefaultValue() { diff --git a/src/Symfony/Component/Config/Definition/VariableNode.php b/src/Symfony/Component/Config/Definition/VariableNode.php index a9c35284cd..0cd84c72bf 100644 --- a/src/Symfony/Component/Config/Definition/VariableNode.php +++ b/src/Symfony/Component/Config/Definition/VariableNode.php @@ -27,9 +27,6 @@ class VariableNode extends BaseNode implements PrototypeNodeInterface protected $defaultValue; protected $allowEmptyValue = true; - /** - * {@inheritdoc} - */ public function setDefaultValue($value) { $this->defaultValueSet = true; diff --git a/src/Symfony/Component/Config/Tests/ConfigCacheFactoryTest.php b/src/Symfony/Component/Config/Tests/ConfigCacheFactoryTest.php index c523e5cd5e..24e3224ce5 100644 --- a/src/Symfony/Component/Config/Tests/ConfigCacheFactoryTest.php +++ b/src/Symfony/Component/Config/Tests/ConfigCacheFactoryTest.php @@ -20,7 +20,7 @@ class ConfigCacheFactoryTest extends TestCase * @expectedException \InvalidArgumentException * @expectedExceptionMessage Invalid type for callback argument. Expected callable, but got "object". */ - public function testCachWithInvalidCallback() + public function testCacheWithInvalidCallback() { $cacheFactory = new ConfigCacheFactory(true); diff --git a/src/Symfony/Component/Console/Descriptor/DescriptorInterface.php b/src/Symfony/Component/Console/Descriptor/DescriptorInterface.php index 3929b6d9ed..5d3339a9e6 100644 --- a/src/Symfony/Component/Console/Descriptor/DescriptorInterface.php +++ b/src/Symfony/Component/Console/Descriptor/DescriptorInterface.php @@ -21,7 +21,7 @@ use Symfony\Component\Console\Output\OutputInterface; interface DescriptorInterface { /** - * Describes an InputArgument instance. + * Describes an object if supported. * * @param OutputInterface $output * @param object $object diff --git a/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerPatterns.php b/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerPatterns.php index dffb079d75..5f16ac48f8 100644 --- a/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerPatterns.php +++ b/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerPatterns.php @@ -46,7 +46,7 @@ class TokenizerPatterns $this->nonAsciiPattern = '[^\x00-\x7F]'; $this->nmCharPattern = '[_a-z0-9-]|'.$this->escapePattern.'|'.$this->nonAsciiPattern; $this->nmStartPattern = '[_a-z]|'.$this->escapePattern.'|'.$this->nonAsciiPattern; - $this->identifierPattern = '(?:'.$this->nmStartPattern.')(?:'.$this->nmCharPattern.')*'; + $this->identifierPattern = '-?(?:'.$this->nmStartPattern.')(?:'.$this->nmCharPattern.')*'; $this->hashPattern = '#((?:'.$this->nmCharPattern.')+)'; $this->numberPattern = '[+-]?(?:[0-9]*\.[0-9]+|[0-9]+)'; $this->quotedStringPattern = '([^\n\r\f%s]|'.$this->stringEscapePattern.')*'; diff --git a/src/Symfony/Component/CssSelector/Tests/Parser/ParserTest.php b/src/Symfony/Component/CssSelector/Tests/Parser/ParserTest.php index 37a3ef1d58..53b35a9547 100644 --- a/src/Symfony/Component/CssSelector/Tests/Parser/ParserTest.php +++ b/src/Symfony/Component/CssSelector/Tests/Parser/ParserTest.php @@ -186,6 +186,7 @@ class ParserTest extends TestCase array('foo:after', 'Element[foo]', 'after'), array('foo::selection', 'Element[foo]', 'selection'), array('lorem#ipsum ~ a#b.c[href]:empty::selection', 'CombinedSelector[Hash[Element[lorem]#ipsum] ~ Pseudo[Attribute[Class[Hash[Element[a]#b].c][href]]:empty]]', 'selection'), + array('video::-webkit-media-controls', 'Element[video]', '-webkit-media-controls'), ); } diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index 00149b38e5..5dbdd69338 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -547,14 +547,15 @@ class ErrorHandler } } } + $exceptionHandler = $this->exceptionHandler; + $this->exceptionHandler = null; try { - if (null !== $this->exceptionHandler) { - return \call_user_func($this->exceptionHandler, $exception); + if (null !== $exceptionHandler) { + return \call_user_func($exceptionHandler, $exception); } $handlerException = $handlerException ?: $exception; } catch (\Throwable $handlerException) { } - $this->exceptionHandler = null; if ($exception === $handlerException) { self::$reservedMemory = null; // Disable the fatal error handler throw $exception; // Give back $exception to the native handler diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php index 0d57204481..afe16ea4f7 100644 --- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php @@ -464,4 +464,17 @@ class ErrorHandlerTest extends TestCase $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $args[0]); $this->assertStringStartsWith("Attempted to load class \"Foo\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage()); } + + /** + * @expectedException \Exception + */ + public function testCustomExceptionHandler() + { + $handler = new ErrorHandler(); + $handler->setExceptionHandler(function ($e) use ($handler) { + $handler->handleException($e); + }); + + $handler->handleException(new \Exception()); + } } diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php index 1e1c4a7848..7b4d5170c1 100644 --- a/src/Symfony/Component/DomCrawler/Crawler.php +++ b/src/Symfony/Component/DomCrawler/Crawler.php @@ -648,7 +648,7 @@ class Crawler implements \Countable, \IteratorAggregate } } - $data[] = $count > 1 ? $elements : $elements[0]; + $data[] = 1 === $count ? $elements[0] : $elements; } return $data; diff --git a/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php b/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php index 6d321e0282..4d3aebfba5 100644 --- a/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php +++ b/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php @@ -97,14 +97,14 @@ class ChoiceFormField extends FormField } /** - * Ticks a checkbox. + * Unticks a checkbox. * * @throws \LogicException When the type provided is not correct */ public function untick() { if ('checkbox' !== $this->type) { - throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type)); + throw new \LogicException(sprintf('You cannot untick "%s" as it is not a checkbox (%s).', $this->name, $this->type)); } $this->setValue(false); @@ -113,7 +113,7 @@ class ChoiceFormField extends FormField /** * Sets the value of the field. * - * @param string $value The value of the field + * @param string|array $value The value of the field * * @throws \InvalidArgumentException When value type provided is not correct */ diff --git a/src/Symfony/Component/DomCrawler/Field/InputFormField.php b/src/Symfony/Component/DomCrawler/Field/InputFormField.php index 090913efa3..1c3c84d721 100644 --- a/src/Symfony/Component/DomCrawler/Field/InputFormField.php +++ b/src/Symfony/Component/DomCrawler/Field/InputFormField.php @@ -32,11 +32,12 @@ class InputFormField extends FormField throw new \LogicException(sprintf('An InputFormField can only be created from an input or button tag (%s given).', $this->node->nodeName)); } - if ('checkbox' === strtolower($this->node->getAttribute('type'))) { + $type = strtolower($this->node->getAttribute('type')); + if ('checkbox' === $type) { throw new \LogicException('Checkboxes should be instances of ChoiceFormField.'); } - if ('file' === strtolower($this->node->getAttribute('type'))) { + if ('file' === $type) { throw new \LogicException('File inputs should be instances of FileFormField.'); } diff --git a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php index ec8512e4ef..a8dc525e55 100644 --- a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php @@ -413,6 +413,7 @@ EOF $this->assertEquals(array('One', 'Two', 'Three'), $crawler->extract('_text'), '->extract() returns an array of extracted data from the node list'); $this->assertEquals(array(array('One', 'first'), array('Two', ''), array('Three', '')), $crawler->extract(array('_text', 'class')), '->extract() returns an array of extracted data from the node list'); + $this->assertEquals(array(array(), array(), array()), $crawler->extract(array()), '->extract() returns empty arrays if the attribute list is empty'); $this->assertEquals(array(), $this->createTestCrawler()->filterXPath('//ol')->extract('_text'), '->extract() returns an empty array if the node list is empty'); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php index 9a8c7d427b..1beb6ab806 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php @@ -80,6 +80,7 @@ class FormType extends BaseType } } + $formConfig = $form->getConfig(); $view->vars = array_replace($view->vars, array( 'errors' => $form->getErrors(), 'valid' => $form->isSubmitted() ? $form->isValid() : true, @@ -88,9 +89,9 @@ class FormType extends BaseType 'required' => $form->isRequired(), 'size' => null, 'label_attr' => $options['label_attr'], - 'compound' => $form->getConfig()->getCompound(), - 'method' => $form->getConfig()->getMethod(), - 'action' => $form->getConfig()->getAction(), + 'compound' => $formConfig->getCompound(), + 'method' => $formConfig->getMethod(), + 'action' => $formConfig->getAction(), 'submitted' => $form->isSubmitted(), )); } diff --git a/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php b/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php index 410eedc2ae..bd116d2a71 100644 --- a/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php +++ b/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php @@ -52,9 +52,7 @@ class ValidationListener implements EventSubscriberInterface if ($form->isRoot()) { // Validate the form in group "Default" - $violations = $this->validator->validate($form); - - foreach ($violations as $violation) { + foreach ($this->validator->validate($form) as $violation) { // Allow the "invalid" constraint to be put onto // non-synchronized forms // ConstraintViolation::getConstraint() must not expect to provide a constraint as long as Symfony\Component\Validator\ExecutionContext exists (before 3.0) diff --git a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php index bde7458258..207015b2ce 100644 --- a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php +++ b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php @@ -253,12 +253,8 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface $classMetadata = $this->metadataFactory->getMetadataFor($class); if ($classMetadata instanceof ClassMetadataInterface && $classMetadata->hasPropertyMetadata($property)) { - $memberMetadatas = $classMetadata->getPropertyMetadata($property); - - foreach ($memberMetadatas as $memberMetadata) { - $constraints = $memberMetadata->getConstraints(); - - foreach ($constraints as $constraint) { + foreach ($classMetadata->getPropertyMetadata($property) as $memberMetadata) { + foreach ($memberMetadata->getConstraints() as $constraint) { if ($guess = $closure($constraint)) { $guesses[] = $guess; } diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php index 6e448c413a..beabaa21cd 100644 --- a/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap4LayoutTest.php @@ -166,10 +166,10 @@ abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest ./span[@class="mb-0 d-block"] [./span[.="[trans]Error[/trans]"]] [./span[.="[trans]Error 1[/trans]"]] - + /following-sibling::span[@class="mb-0 d-block"] [./span[.="[trans]Error[/trans]"]] - [./span[.="[trans]Error 2[/trans]"]] + [./span[.="[trans]Error 2[/trans]"]] ] [count(./span)=2] ' diff --git a/src/Symfony/Component/Intl/Intl.php b/src/Symfony/Component/Intl/Intl.php index 3444c10f83..e552b994fd 100644 --- a/src/Symfony/Component/Intl/Intl.php +++ b/src/Symfony/Component/Intl/Intl.php @@ -15,6 +15,7 @@ use Symfony\Component\Intl\Data\Bundle\Reader\JsonBundleReader; use Symfony\Component\Intl\Data\Bundle\Reader\BufferedBundleReader; use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReader; use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface; +use Symfony\Component\Intl\Data\Provider\LocaleDataProvider; use Symfony\Component\Intl\Data\Provider\ScriptDataProvider; use Symfony\Component\Intl\ResourceBundle\CurrencyBundle; use Symfony\Component\Intl\ResourceBundle\CurrencyBundleInterface; @@ -259,6 +260,11 @@ final class Intl new JsonBundleReader(), self::BUFFER_SIZE )); + $localeDataProvider = new LocaleDataProvider( + self::getDataDirectory().'/'.self::LOCALE_DIR, + self::$entryReader + ); + self::$entryReader->setLocaleAliases($localeDataProvider->getAliases()); } return self::$entryReader; diff --git a/src/Symfony/Component/Intl/Tests/IntlTest.php b/src/Symfony/Component/Intl/Tests/IntlTest.php new file mode 100644 index 0000000000..d77655b77d --- /dev/null +++ b/src/Symfony/Component/Intl/Tests/IntlTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Tests; + +use Symfony\Component\Intl\Intl; +use PHPUnit\Framework\TestCase; + +class IntlTest extends TestCase +{ + /** + * @requires extension intl + */ + public function testIsExtensionLoadedChecksIfIntlExtensionIsLoaded() + { + $this->assertTrue(Intl::isExtensionLoaded()); + } + + public function testGetCurrencyBundleCreatesTheCurrencyBundle() + { + $this->assertInstanceOf('Symfony\Component\Intl\ResourceBundle\CurrencyBundleInterface', Intl::getCurrencyBundle()); + } + + public function testGetLanguageBundleCreatesTheLanguageBundle() + { + $this->assertInstanceOf('Symfony\Component\Intl\ResourceBundle\LanguageBundleInterface', Intl::getLanguageBundle()); + } + + public function testGetLocaleBundleCreatesTheLocaleBundle() + { + $this->assertInstanceOf('Symfony\Component\Intl\ResourceBundle\LocaleBundleInterface', Intl::getLocaleBundle()); + } + + public function testGetRegionBundleCreatesTheRegionBundle() + { + $this->assertInstanceOf('Symfony\Component\Intl\ResourceBundle\LocaleBundleInterface', Intl::getLocaleBundle()); + } + + public function testGetIcuVersionReadsTheVersionOfInstalledIcuLibrary() + { + $this->assertStringMatchesFormat('%d.%d', Intl::getIcuVersion()); + } + + public function testGetIcuDataVersionReadsTheVersionOfInstalledIcuData() + { + $this->assertStringMatchesFormat('%d.%d', Intl::getIcuDataVersion()); + } + + public function testGetIcuStubVersionReadsTheVersionOfBundledStubs() + { + $this->assertStringMatchesFormat('%d.%d', Intl::getIcuStubVersion()); + } + + public function testGetDataDirectoryReturnsThePathToIcuData() + { + $this->assertTrue(is_dir(Intl::getDataDirectory())); + } + + /** + * @requires extension intl + */ + public function testLocaleAliasesAreLoaded() + { + \Locale::setDefault('zh_TW'); + $countryNameZhTw = Intl::getRegionBundle()->getCountryName('AD'); + + \Locale::setDefault('zh_Hant_TW'); + $countryNameHantZhTw = Intl::getRegionBundle()->getCountryName('AD'); + + \Locale::setDefault('zh'); + $countryNameZh = Intl::getRegionBundle()->getCountryName('AD'); + + $this->assertSame($countryNameZhTw, $countryNameHantZhTw, 'zh_TW is an alias to zh_Hant_TW'); + $this->assertNotSame($countryNameZh, $countryNameZhTw, 'zh_TW does not fall back to zh'); + } +} diff --git a/src/Symfony/Component/Process/PhpExecutableFinder.php b/src/Symfony/Component/Process/PhpExecutableFinder.php index ef79065ff7..1916274faf 100644 --- a/src/Symfony/Component/Process/PhpExecutableFinder.php +++ b/src/Symfony/Component/Process/PhpExecutableFinder.php @@ -47,7 +47,7 @@ class PhpExecutableFinder $args = $includeArgs && $args ? ' '.implode(' ', $args) : ''; // PHP_BINARY return the current sapi executable - if (PHP_BINARY && in_array(PHP_SAPI, array('cli', 'cli-server', 'phpdbg')) && is_file(PHP_BINARY)) { + if (PHP_BINARY && in_array(PHP_SAPI, array('cli', 'cli-server', 'phpdbg'))) { return PHP_BINARY.$args; } diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php index d3f5ad6291..0c16c96aeb 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/SimpleAuthenticationProvider.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Security\Core\Authentication\Provider; +use Symfony\Component\Security\Core\User\UserChecker; +use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\SimpleAuthenticatorInterface; @@ -24,23 +26,29 @@ class SimpleAuthenticationProvider implements AuthenticationProviderInterface private $simpleAuthenticator; private $userProvider; private $providerKey; + private $userChecker; - public function __construct(SimpleAuthenticatorInterface $simpleAuthenticator, UserProviderInterface $userProvider, string $providerKey) + public function __construct(SimpleAuthenticatorInterface $simpleAuthenticator, UserProviderInterface $userProvider, string $providerKey, UserCheckerInterface $userChecker = null) { $this->simpleAuthenticator = $simpleAuthenticator; $this->userProvider = $userProvider; $this->providerKey = $providerKey; + $this->userChecker = $userChecker ?: new UserChecker(); } public function authenticate(TokenInterface $token) { $authToken = $this->simpleAuthenticator->authenticateToken($token, $this->userProvider, $this->providerKey); - if ($authToken instanceof TokenInterface) { - return $authToken; + if (!$authToken instanceof TokenInterface) { + throw new AuthenticationException('Simple authenticator failed to return an authenticated token.'); } - throw new AuthenticationException('Simple authenticator failed to return an authenticated token.'); + $user = $authToken->getUser(); + $this->userChecker->checkPreAuth($user); + $this->userChecker->checkPostAuth($user); + + return $authToken; } public function supports(TokenInterface $token) diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/SimpleAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/SimpleAuthenticationProviderTest.php new file mode 100644 index 0000000000..1e7069c1fa --- /dev/null +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/SimpleAuthenticationProviderTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Tests\Authentication\Provider; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\Exception\DisabledException; +use Symfony\Component\Security\Core\Authentication\Provider\SimpleAuthenticationProvider; +use Symfony\Component\Security\Core\Exception\LockedException; + +class SimpleAuthenticationProviderTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\Security\Core\Exception\DisabledException + */ + public function testAuthenticateWhenPreChecksFails() + { + $user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); + + $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); + $token->expects($this->any()) + ->method('getUser') + ->will($this->returnValue($user)); + + $userChecker = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserCheckerInterface')->getMock(); + $userChecker->expects($this->once()) + ->method('checkPreAuth') + ->will($this->throwException(new DisabledException())); + + $authenticator = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\SimpleAuthenticatorInterface')->getMock(); + $authenticator->expects($this->once()) + ->method('authenticateToken') + ->will($this->returnValue($token)); + + $provider = $this->getProvider($authenticator, null, $userChecker); + + $provider->authenticate($token); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\LockedException + */ + public function testAuthenticateWhenPostChecksFails() + { + $user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); + + $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); + $token->expects($this->any()) + ->method('getUser') + ->will($this->returnValue($user)); + + $userChecker = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserCheckerInterface')->getMock(); + $userChecker->expects($this->once()) + ->method('checkPostAuth') + ->will($this->throwException(new LockedException())); + + $authenticator = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\SimpleAuthenticatorInterface')->getMock(); + $authenticator->expects($this->once()) + ->method('authenticateToken') + ->will($this->returnValue($token)); + + $provider = $this->getProvider($authenticator, null, $userChecker); + + $provider->authenticate($token); + } + + protected function getProvider($simpleAuthenticator = null, $userProvider = null, $userChecker = null, $key = 'test') + { + if (null === $userChecker) { + $userChecker = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserCheckerInterface')->getMock(); + } + if (null === $simpleAuthenticator) { + $simpleAuthenticator = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\SimpleAuthenticatorInterface')->getMock(); + } + if (null === $userProvider) { + $userProvider = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserProviderInterface')->getMock(); + } + + return new SimpleAuthenticationProvider($simpleAuthenticator, $userProvider, $key, $userChecker); + } +} diff --git a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php index caba1fcb20..dd0dd1fa46 100644 --- a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php @@ -47,7 +47,7 @@ class ClassMetadata extends GenericMetadata implements ClassMetadataInterface public $defaultGroup; /** - * @var MemberMetadata[] + * @var MemberMetadata[][] * * @internal This property is public in order to reduce the size of the * class' serialized representation. Do not access it. Use