From 9cf90fbcbf234814a97ea28ef179dc9cc1412909 Mon Sep 17 00:00:00 2001 From: Evgeniy Sokolov Date: Tue, 10 Nov 2015 01:31:08 +0100 Subject: [PATCH 001/107] [2.3][Process] fix Proccess run with pts enabled --- src/Symfony/Component/Process/Process.php | 51 ++++++++++++++--------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index 1249962bb5a6e..bbafb4327912e 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -119,12 +119,12 @@ class Process /** * Constructor. * - * @param string $commandline The command line to run - * @param string|null $cwd The working directory or null to use the working dir of the current PHP process - * @param array|null $env The environment variables or null to inherit - * @param string|null $stdin The STDIN content - * @param integer|float|null $timeout The timeout in seconds or null to disable - * @param array $options An array of options for proc_open + * @param string $commandline The command line to run + * @param string|null $cwd The working directory or null to use the working dir of the current PHP process + * @param array|null $env The environment variables or null to inherit + * @param string|null $stdin The STDIN content + * @param int|float|null $timeout The timeout in seconds or null to disable + * @param array $options An array of options for proc_open * * @throws RuntimeException When proc_open is not installed * @@ -184,7 +184,7 @@ public function __clone() * @param callback|null $callback A PHP callback to run whenever there is some * output available on STDOUT or STDERR * - * @return integer The exit status code + * @return int The exit status code * * @throws RuntimeException When process can't be launch or is stopped * @@ -238,8 +238,20 @@ public function start($callback = null) } } + $ptsWorkaround = null; + + if (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { + // Workaround for the bug, when PTS functionality is enabled. + // @see : https://bugs.php.net/69442 + $ptsWorkaround = fopen('php://fd/0', 'r'); + } + $this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $this->env, $this->options); + if ($ptsWorkaround) { + fclose($ptsWorkaround); + } + if (!is_resource($this->process)) { throw new RuntimeException('Unable to launch a new process.'); } @@ -287,7 +299,7 @@ public function restart($callback = null) * * @param callback|null $callback A valid PHP callback * - * @return integer The exitcode of the process + * @return int The exitcode of the process * * @throws RuntimeException When process timed out * @throws RuntimeException When process stopped after receiving signal @@ -302,7 +314,7 @@ public function wait($callback = null) do { $this->checkTimeout(); $running = defined('PHP_WINDOWS_VERSION_BUILD') ? $this->isRunning() : $this->processPipes->hasOpenHandles(); - $close = !defined('PHP_WINDOWS_VERSION_BUILD') || !$running;; + $close = !defined('PHP_WINDOWS_VERSION_BUILD') || !$running; $this->readPipes(true, $close); } while ($running); @@ -324,7 +336,7 @@ public function wait($callback = null) /** * Returns the Pid (process identifier), if applicable. * - * @return integer|null The process id if running, null otherwise + * @return int|null The process id if running, null otherwise * * @throws RuntimeException In case --enable-sigchild is activated */ @@ -342,7 +354,8 @@ public function getPid() /** * Sends a posix signal to the process. * - * @param integer $signal A valid posix signal (see http://www.php.net/manual/en/pcntl.constants.php) + * @param int $signal A valid posix signal (see http://www.php.net/manual/en/pcntl.constants.php) + * * @return Process * * @throws LogicException In case the process is not running @@ -434,7 +447,7 @@ public function getIncrementalErrorOutput() /** * Returns the exit code returned by the process. * - * @return integer The exit status code + * @return int The exit status code * * @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled * @@ -508,7 +521,7 @@ public function hasBeenSignaled() * * It is only meaningful if hasBeenSignaled() returns true. * - * @return integer + * @return int * * @throws RuntimeException In case --enable-sigchild is activated * @@ -546,7 +559,7 @@ public function hasBeenStopped() * * It is only meaningful if hasBeenStopped() returns true. * - * @return integer + * @return int * * @api */ @@ -612,10 +625,10 @@ public function getStatus() /** * Stops the process. * - * @param integer|float $timeout The timeout in seconds - * @param integer $signal A posix signal to send in case the process has not stop at timeout, default is SIGKILL + * @param int|float $timeout The timeout in seconds + * @param int $signal A posix signal to send in case the process has not stop at timeout, default is SIGKILL * - * @return integer The exit-code of the process + * @return int The exit-code of the process * * @throws RuntimeException if the process got signaled */ @@ -704,7 +717,7 @@ public function getTimeout() * * To disable the timeout, set this value to null. * - * @param integer|float|null $timeout The timeout in seconds + * @param int|float|null $timeout The timeout in seconds * * @return self The current Process instance * @@ -728,7 +741,7 @@ public function setTimeout($timeout) /** * Enables or disables the TTY mode. * - * @param boolean $tty True to enabled and false to disable + * @param bool $tty True to enabled and false to disable * * @return self The current Process instance */ From 5f1980ba5ce72fab682b210103caab78969fc560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20S=C3=A1nchez?= Date: Wed, 11 Nov 2015 16:25:04 -0300 Subject: [PATCH 002/107] The following change adds support for Armenian pluralization. According to http://www.unicode.org/cldr/charts/27/supplemental/language_plural_rules.html#hy Armenian has 2 forms of pluralization. --- src/Symfony/Component/Translation/PluralizationRules.php | 1 + .../Component/Translation/Tests/PluralizationRulesTest.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Translation/PluralizationRules.php b/src/Symfony/Component/Translation/PluralizationRules.php index 6d91da7fc6c78..8b02773b5884a 100644 --- a/src/Symfony/Component/Translation/PluralizationRules.php +++ b/src/Symfony/Component/Translation/PluralizationRules.php @@ -132,6 +132,7 @@ public static function get($number, $locale) case 'fr': case 'gun': case 'hi': + case 'hy': case 'ln': case 'mg': case 'nso': diff --git a/src/Symfony/Component/Translation/Tests/PluralizationRulesTest.php b/src/Symfony/Component/Translation/Tests/PluralizationRulesTest.php index 066e07f5ab3f2..43c31672c2ce5 100644 --- a/src/Symfony/Component/Translation/Tests/PluralizationRulesTest.php +++ b/src/Symfony/Component/Translation/Tests/PluralizationRulesTest.php @@ -61,7 +61,7 @@ public function successLangcodes() { return array( array('1', array('ay','bo', 'cgg','dz','id', 'ja', 'jbo', 'ka','kk','km','ko','ky')), - array('2', array('nl', 'fr', 'en', 'de', 'de_GE')), + array('2', array('nl', 'fr', 'en', 'de', 'de_GE', 'hy', 'hy_AM')), array('3', array('be','bs','cs','hr')), array('4', array('cy','mt', 'sl')), array('5', array()), From 2d0af8e719c9615069a13adcdaf5ef38edb519fc Mon Sep 17 00:00:00 2001 From: Jakub Zalas Date: Fri, 13 Nov 2015 07:29:58 +0000 Subject: [PATCH 003/107] [Validator] Allow an empty path with a non empty fragment or a query --- src/Symfony/Component/Validator/Constraints/UrlValidator.php | 2 +- .../Validator/Tests/Constraints/UrlValidatorTest.php | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/Constraints/UrlValidator.php b/src/Symfony/Component/Validator/Constraints/UrlValidator.php index 5af71e6d824ee..5127f8b1660f6 100644 --- a/src/Symfony/Component/Validator/Constraints/UrlValidator.php +++ b/src/Symfony/Component/Validator/Constraints/UrlValidator.php @@ -33,7 +33,7 @@ class UrlValidator extends ConstraintValidator \] # a IPv6 address ) (:[0-9]+)? # a port (optional) - (/?|/\S+|\?|\#) # a /, nothing, a / with something, a query or a fragment + (/?|/\S+|\?\S*|\#\S*) # a /, nothing, a / with something, a query or a fragment $~ixu'; /** diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php index c08531339c2a4..93e9e8237ff0e 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php @@ -112,7 +112,11 @@ public function getValidUrls() array('http://username:password@symfony.com'), array('http://user-name@symfony.com'), array('http://symfony.com?'), + array('http://symfony.com?query=1'), + array('http://symfony.com/?query=1'), array('http://symfony.com#'), + array('http://symfony.com#fragment'), + array('http://symfony.com/#fragment'), ); } From 4923411062127aa20b038f74766f27515e0e7c41 Mon Sep 17 00:00:00 2001 From: Leo Feyer Date: Tue, 27 Oct 2015 16:04:34 +0100 Subject: [PATCH 004/107] Fix the server variables in the router_*.php files --- .../Bundle/FrameworkBundle/Resources/config/router_dev.php | 4 ++++ .../Bundle/FrameworkBundle/Resources/config/router_prod.php | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_dev.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_dev.php index ca0cd1ee1cefb..432ccff9204f1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_dev.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_dev.php @@ -33,6 +33,10 @@ $_SERVER = array_merge($_SERVER, $_ENV); $_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.'app_dev.php'; +// Since we are rewriting to app_dev.php, adjust SCRIPT_NAME and PHP_SELF accordingly +$_SERVER['SCRIPT_NAME'] = DIRECTORY_SEPARATOR.'app_dev.php'; +$_SERVER['PHP_SELF'] = DIRECTORY_SEPARATOR.'app_dev.php'; + require 'app_dev.php'; error_log(sprintf('%s:%d [%d]: %s', $_SERVER['REMOTE_ADDR'], $_SERVER['REMOTE_PORT'], http_response_code(), $_SERVER['REQUEST_URI']), 4); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_prod.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_prod.php index 1c6b99b866b74..97613a6248f71 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_prod.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_prod.php @@ -33,6 +33,10 @@ $_SERVER = array_merge($_SERVER, $_ENV); $_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.'app.php'; +// Since we are rewriting to app.php, adjust SCRIPT_NAME and PHP_SELF accordingly +$_SERVER['SCRIPT_NAME'] = DIRECTORY_SEPARATOR.'app.php'; +$_SERVER['PHP_SELF'] = DIRECTORY_SEPARATOR.'app.php'; + require 'app.php'; error_log(sprintf('%s:%d [%d]: %s', $_SERVER['REMOTE_ADDR'], $_SERVER['REMOTE_PORT'], http_response_code(), $_SERVER['REQUEST_URI']), 4); From 86b4c0536cac411cdbe7cef3c1c68f17c49d49ec Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 16 Nov 2015 13:50:22 +0100 Subject: [PATCH 005/107] bumped Symfony version to 2.8.0 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index ba96278837ac9..7c55172d395ae 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -60,12 +60,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.8.0-BETA1'; + const VERSION = '2.8.0-DEV'; const VERSION_ID = 20800; const MAJOR_VERSION = 2; const MINOR_VERSION = 8; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'BETA1'; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '05/2018'; const END_OF_LIFE = '05/2019'; From 9b0850511f74ccbe93a93eb215418c135dc21a62 Mon Sep 17 00:00:00 2001 From: Douglas Greenshields Date: Tue, 17 Nov 2015 00:41:09 +0000 Subject: [PATCH 006/107] [WebProfilerBundle] correct typo in show stack trace link --- .../Resources/views/Collector/logger.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig index 8ba4d7025ac63..361e684fbf938 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig @@ -162,7 +162,7 @@ {% endif %} {% if stack %} - + {% endif %} {% for index, call in stack if index > 1 %} From 39f09893b9abd87068a623b1041443b55253e0c3 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Tue, 17 Nov 2015 02:04:18 +0100 Subject: [PATCH 007/107] [Validator] [sl] BIC (SWIFT-BIC) validation constraint --- .../Validator/Resources/translations/validators.sl.xlf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf index 4f0e7c6364e37..834db4015e8e4 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf @@ -310,6 +310,10 @@ This value does not match the expected {{ charset }} charset. Ta vrednost se ne ujema s pričakovanim naborom znakov {{ charset }}. + + This is not a valid Business Identifier Code (BIC). + To ni veljavna identifikacijska koda podjetja (BIC). + From 02d2148f370d45f846e75e94c1335b667f9c8358 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 16 Nov 2015 20:24:50 +0100 Subject: [PATCH 008/107] [Form] Enhance some FormRegistry deprecation messages --- .../DependencyInjection/Compiler/FormPass.php | 2 +- src/Symfony/Component/Form/FormRegistry.php | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php index 4b36468aa3f0a..c5d639b808e18 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php @@ -67,7 +67,7 @@ public function process(ContainerBuilder $container) @trigger_error('The alias option of the form.type_extension tag is deprecated since version 2.8 and will be removed in 3.0. Use the extended_type option instead.', E_USER_DEPRECATED); $extendedType = $tag[0]['alias']; } else { - @trigger_error('The extended_type option of the form.type_extension tag is required since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + @trigger_error('The extended_type option of the form.type_extension tag is required since version 2.8.', E_USER_DEPRECATED); $extendedType = $serviceId; } diff --git a/src/Symfony/Component/Form/FormRegistry.php b/src/Symfony/Component/Form/FormRegistry.php index 66d5a1f7c2b38..a8995afe89f01 100644 --- a/src/Symfony/Component/Form/FormRegistry.php +++ b/src/Symfony/Component/Form/FormRegistry.php @@ -120,7 +120,7 @@ private function resolveAndAddType(FormTypeInterface $type) $hasCustomName = $name !== $fqcn; if ($parentType instanceof FormTypeInterface) { - @trigger_error('Returning a FormTypeInterface from FormTypeInterface::getParent() is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + @trigger_error(sprintf('Returning a FormTypeInterface from %s::getParent() is deprecated since version 2.8 and will be removed in 3.0. Return the fully-qualified type class name instead.', $fqcn), E_USER_DEPRECATED); $this->resolveAndAddType($parentType); $parentType = $parentType->getName(); @@ -128,14 +128,11 @@ private function resolveAndAddType(FormTypeInterface $type) if ($hasCustomName) { foreach ($this->extensions as $extension) { - $typeExtensions = array_merge( - $typeExtensions, - $extension->getTypeExtensions($name) - ); - } + if ($x = $extension->getTypeExtensions($name)) { + @trigger_error(sprintf('Returning a type name from %s::getExtendedType() is deprecated since version 2.8 and will be removed in 3.0. Return the fully-qualified type class name instead.', get_class($x[0])), E_USER_DEPRECATED); - if ($typeExtensions) { - @trigger_error('Returning a type name from FormTypeExtensionInterface::getExtendedType() is deprecated since version 2.8 and will be removed in 3.0. Return the fully-qualified type class name instead.', E_USER_DEPRECATED); + $typeExtensions = array_merge($typeExtensions, $x); + } } } From 1fab27b58cd9589115ce3668c1cc312c7d1280cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sat, 14 Nov 2015 12:27:50 +0100 Subject: [PATCH 009/107] [Serializer] ObjectNormalizer: don't serialize static methods and props --- .../Serializer/Normalizer/ObjectNormalizer.php | 5 ++++- .../Tests/Normalizer/ObjectNormalizerTest.php | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php index ba84ac717f075..fe1676fbf36fb 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php @@ -68,6 +68,7 @@ public function normalize($object, $format = null, array $context = array()) $reflClass = new \ReflectionClass($object); foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflMethod) { if ( + !$reflMethod->isStatic() && !$reflMethod->isConstructor() && !$reflMethod->isDestructor() && 0 === $reflMethod->getNumberOfRequiredParameters() @@ -86,7 +87,9 @@ public function normalize($object, $format = null, array $context = array()) // properties foreach ($reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $reflProperty) { - $attributes[$reflProperty->getName()] = true; + if (!$reflProperty->isStatic()) { + $attributes[$reflProperty->getName()] = true; + } } $attributes = array_keys($attributes); diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 1cadee52b196f..80a021d08fa25 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -456,6 +456,11 @@ public function testNoTraversableSupport() { $this->assertFalse($this->normalizer->supportsNormalization(new \ArrayObject())); } + + public function testNormalizeStatic() + { + $this->assertEquals(array('foo' => 'K'), $this->normalizer->normalize(new ObjectWithStaticPropertiesAndMethods())); + } } class ObjectDummy @@ -605,3 +610,14 @@ public function otherMethod() throw new \RuntimeException('Dummy::otherMethod() should not be called'); } } + +class ObjectWithStaticPropertiesAndMethods +{ + public $foo = 'K'; + public static $bar = 'A'; + + public static function getBaz() + { + return 'L'; + } +} From d4880c4785fce19106a93d74e904e9f0f630e015 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 18 Nov 2015 09:19:46 +0100 Subject: [PATCH 010/107] Add missing exclusions from phpunit.xml.dist --- phpunit.xml.dist | 4 ++++ src/Symfony/Bridge/Doctrine/phpunit.xml.dist | 2 ++ src/Symfony/Bridge/Monolog/phpunit.xml.dist | 2 ++ src/Symfony/Bridge/Propel1/phpunit.xml.dist | 2 ++ src/Symfony/Bridge/ProxyManager/phpunit.xml.dist | 1 + src/Symfony/Bridge/Twig/phpunit.xml.dist | 1 + src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist | 3 ++- src/Symfony/Bundle/SecurityBundle/phpunit.xml.dist | 3 ++- src/Symfony/Bundle/TwigBundle/phpunit.xml.dist | 3 ++- src/Symfony/Bundle/WebProfilerBundle/phpunit.xml.dist | 3 ++- src/Symfony/Component/BrowserKit/phpunit.xml.dist | 1 + src/Symfony/Component/ClassLoader/phpunit.xml.dist | 1 + src/Symfony/Component/Config/phpunit.xml.dist | 1 + src/Symfony/Component/Console/phpunit.xml.dist | 1 + src/Symfony/Component/CssSelector/phpunit.xml.dist | 1 + src/Symfony/Component/Debug/phpunit.xml.dist | 1 + src/Symfony/Component/DependencyInjection/phpunit.xml.dist | 1 + src/Symfony/Component/EventDispatcher/phpunit.xml.dist | 1 + src/Symfony/Component/Filesystem/phpunit.xml.dist | 2 ++ src/Symfony/Component/Finder/phpunit.xml.dist | 1 + src/Symfony/Component/Form/phpunit.xml.dist | 2 ++ src/Symfony/Component/HttpFoundation/phpunit.xml.dist | 1 + src/Symfony/Component/HttpKernel/phpunit.xml.dist | 1 + src/Symfony/Component/Intl/phpunit.xml.dist | 2 ++ src/Symfony/Component/Locale/phpunit.xml.dist | 1 + src/Symfony/Component/OptionsResolver/phpunit.xml.dist | 2 ++ src/Symfony/Component/Process/phpunit.xml.dist | 2 ++ src/Symfony/Component/PropertyAccess/phpunit.xml.dist | 2 ++ src/Symfony/Component/Routing/phpunit.xml.dist | 3 ++- src/Symfony/Component/Security/phpunit.xml.dist | 5 ++++- src/Symfony/Component/Serializer/phpunit.xml.dist | 3 ++- src/Symfony/Component/Stopwatch/phpunit.xml.dist | 1 + src/Symfony/Component/Templating/phpunit.xml.dist | 2 +- src/Symfony/Component/Translation/phpunit.xml.dist | 2 +- src/Symfony/Component/Validator/phpunit.xml.dist | 3 ++- src/Symfony/Component/Yaml/phpunit.xml.dist | 3 ++- 36 files changed, 59 insertions(+), 11 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index e9c709d14223a..4e4e411c3cfb4 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -38,6 +38,10 @@ ./src/Symfony/Bundle/*/Resources ./src/Symfony/Component/*/Resources ./src/Symfony/Component/*/*/Resources + ./src/Symfony/Bridge/*/vendor + ./src/Symfony/Bundle/*/vendor + ./src/Symfony/Component/*/vendor + ./src/Symfony/Component/*/*/vendor diff --git a/src/Symfony/Bridge/Doctrine/phpunit.xml.dist b/src/Symfony/Bridge/Doctrine/phpunit.xml.dist index 13409e6f0f6b5..c006d232219a4 100644 --- a/src/Symfony/Bridge/Doctrine/phpunit.xml.dist +++ b/src/Symfony/Bridge/Doctrine/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ @@ -21,6 +22,7 @@ ./Resources ./Tests + ./vendor diff --git a/src/Symfony/Bridge/Monolog/phpunit.xml.dist b/src/Symfony/Bridge/Monolog/phpunit.xml.dist index efd48709de90b..8a60f06a7a310 100644 --- a/src/Symfony/Bridge/Monolog/phpunit.xml.dist +++ b/src/Symfony/Bridge/Monolog/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ @@ -21,6 +22,7 @@ ./Resources ./Tests + ./vendor diff --git a/src/Symfony/Bridge/Propel1/phpunit.xml.dist b/src/Symfony/Bridge/Propel1/phpunit.xml.dist index 507e12596cbd4..d6d959c7b01c2 100644 --- a/src/Symfony/Bridge/Propel1/phpunit.xml.dist +++ b/src/Symfony/Bridge/Propel1/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ @@ -21,6 +22,7 @@ ./Resources ./Tests + ./vendor diff --git a/src/Symfony/Bridge/ProxyManager/phpunit.xml.dist b/src/Symfony/Bridge/ProxyManager/phpunit.xml.dist index 363805fdfa6ae..60980be9e531e 100644 --- a/src/Symfony/Bridge/ProxyManager/phpunit.xml.dist +++ b/src/Symfony/Bridge/ProxyManager/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ diff --git a/src/Symfony/Bridge/Twig/phpunit.xml.dist b/src/Symfony/Bridge/Twig/phpunit.xml.dist index d291324949f2d..10c0be1142712 100644 --- a/src/Symfony/Bridge/Twig/phpunit.xml.dist +++ b/src/Symfony/Bridge/Twig/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ diff --git a/src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist b/src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist index eb7c0b0a97a2f..1d25eeb3304c5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist +++ b/src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ @@ -19,9 +20,9 @@ ./ - ./vendor ./Resources ./Tests + ./vendor diff --git a/src/Symfony/Bundle/SecurityBundle/phpunit.xml.dist b/src/Symfony/Bundle/SecurityBundle/phpunit.xml.dist index 52f420ae5f2fe..a7fdc326c4571 100644 --- a/src/Symfony/Bundle/SecurityBundle/phpunit.xml.dist +++ b/src/Symfony/Bundle/SecurityBundle/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ @@ -19,8 +20,8 @@ ./ - ./Tests ./Resources + ./Tests ./vendor diff --git a/src/Symfony/Bundle/TwigBundle/phpunit.xml.dist b/src/Symfony/Bundle/TwigBundle/phpunit.xml.dist index 715b0bfa87f47..9a8c38f26ef33 100644 --- a/src/Symfony/Bundle/TwigBundle/phpunit.xml.dist +++ b/src/Symfony/Bundle/TwigBundle/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ @@ -19,8 +20,8 @@ ./ - ./Tests ./Resources + ./Tests ./vendor diff --git a/src/Symfony/Bundle/WebProfilerBundle/phpunit.xml.dist b/src/Symfony/Bundle/WebProfilerBundle/phpunit.xml.dist index 767f3e066b391..2bcccd6667a26 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/phpunit.xml.dist +++ b/src/Symfony/Bundle/WebProfilerBundle/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ @@ -19,8 +20,8 @@ ./ - ./Tests ./Resources + ./Tests ./vendor diff --git a/src/Symfony/Component/BrowserKit/phpunit.xml.dist b/src/Symfony/Component/BrowserKit/phpunit.xml.dist index d6ca28bf1c6b5..d76b2b98afd80 100644 --- a/src/Symfony/Component/BrowserKit/phpunit.xml.dist +++ b/src/Symfony/Component/BrowserKit/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ diff --git a/src/Symfony/Component/ClassLoader/phpunit.xml.dist b/src/Symfony/Component/ClassLoader/phpunit.xml.dist index a1b6c82c10161..4856db5be65d1 100644 --- a/src/Symfony/Component/ClassLoader/phpunit.xml.dist +++ b/src/Symfony/Component/ClassLoader/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ diff --git a/src/Symfony/Component/Config/phpunit.xml.dist b/src/Symfony/Component/Config/phpunit.xml.dist index 2156534e9ce52..3fe6fd87c8499 100644 --- a/src/Symfony/Component/Config/phpunit.xml.dist +++ b/src/Symfony/Component/Config/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ diff --git a/src/Symfony/Component/Console/phpunit.xml.dist b/src/Symfony/Component/Console/phpunit.xml.dist index 729c433aa69e8..ae0dcbeaba41c 100644 --- a/src/Symfony/Component/Console/phpunit.xml.dist +++ b/src/Symfony/Component/Console/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ diff --git a/src/Symfony/Component/CssSelector/phpunit.xml.dist b/src/Symfony/Component/CssSelector/phpunit.xml.dist index bc57cfcdfa8d8..14a320c873c11 100644 --- a/src/Symfony/Component/CssSelector/phpunit.xml.dist +++ b/src/Symfony/Component/CssSelector/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ diff --git a/src/Symfony/Component/Debug/phpunit.xml.dist b/src/Symfony/Component/Debug/phpunit.xml.dist index e91766065749a..20b0313f0cd89 100644 --- a/src/Symfony/Component/Debug/phpunit.xml.dist +++ b/src/Symfony/Component/Debug/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ diff --git a/src/Symfony/Component/DependencyInjection/phpunit.xml.dist b/src/Symfony/Component/DependencyInjection/phpunit.xml.dist index 17a217226da3d..86252d0456ba9 100644 --- a/src/Symfony/Component/DependencyInjection/phpunit.xml.dist +++ b/src/Symfony/Component/DependencyInjection/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ diff --git a/src/Symfony/Component/EventDispatcher/phpunit.xml.dist b/src/Symfony/Component/EventDispatcher/phpunit.xml.dist index b14fde575007d..ae0586e0b33de 100644 --- a/src/Symfony/Component/EventDispatcher/phpunit.xml.dist +++ b/src/Symfony/Component/EventDispatcher/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ diff --git a/src/Symfony/Component/Filesystem/phpunit.xml.dist b/src/Symfony/Component/Filesystem/phpunit.xml.dist index 32444185a1edc..d066ed7969868 100644 --- a/src/Symfony/Component/Filesystem/phpunit.xml.dist +++ b/src/Symfony/Component/Filesystem/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ @@ -20,6 +21,7 @@ ./ ./Tests + ./vendor diff --git a/src/Symfony/Component/Finder/phpunit.xml.dist b/src/Symfony/Component/Finder/phpunit.xml.dist index bc38ccaa45d1d..631e570b9479f 100644 --- a/src/Symfony/Component/Finder/phpunit.xml.dist +++ b/src/Symfony/Component/Finder/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ diff --git a/src/Symfony/Component/Form/phpunit.xml.dist b/src/Symfony/Component/Form/phpunit.xml.dist index 104aee4b0b52d..1c4acf476238d 100644 --- a/src/Symfony/Component/Form/phpunit.xml.dist +++ b/src/Symfony/Component/Form/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ @@ -19,6 +20,7 @@ ./ + ./Resources ./Tests ./vendor diff --git a/src/Symfony/Component/HttpFoundation/phpunit.xml.dist b/src/Symfony/Component/HttpFoundation/phpunit.xml.dist index b5b660a39433e..9ffdb43a2d744 100644 --- a/src/Symfony/Component/HttpFoundation/phpunit.xml.dist +++ b/src/Symfony/Component/HttpFoundation/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ diff --git a/src/Symfony/Component/HttpKernel/phpunit.xml.dist b/src/Symfony/Component/HttpKernel/phpunit.xml.dist index 5b17270141acd..17c48935c7158 100644 --- a/src/Symfony/Component/HttpKernel/phpunit.xml.dist +++ b/src/Symfony/Component/HttpKernel/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ diff --git a/src/Symfony/Component/Intl/phpunit.xml.dist b/src/Symfony/Component/Intl/phpunit.xml.dist index d40a1582bb889..9e7ce2f0b2642 100644 --- a/src/Symfony/Component/Intl/phpunit.xml.dist +++ b/src/Symfony/Component/Intl/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ @@ -25,6 +26,7 @@ ./ + ./Resources ./Tests ./vendor diff --git a/src/Symfony/Component/Locale/phpunit.xml.dist b/src/Symfony/Component/Locale/phpunit.xml.dist index 4633ca6f04375..0d9b637cc78d1 100644 --- a/src/Symfony/Component/Locale/phpunit.xml.dist +++ b/src/Symfony/Component/Locale/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ diff --git a/src/Symfony/Component/OptionsResolver/phpunit.xml.dist b/src/Symfony/Component/OptionsResolver/phpunit.xml.dist index 2398388768711..abf84614bcf76 100644 --- a/src/Symfony/Component/OptionsResolver/phpunit.xml.dist +++ b/src/Symfony/Component/OptionsResolver/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ @@ -21,6 +22,7 @@ ./Resources ./Tests + ./vendor diff --git a/src/Symfony/Component/Process/phpunit.xml.dist b/src/Symfony/Component/Process/phpunit.xml.dist index 07b617be4b5d2..788500084abf8 100644 --- a/src/Symfony/Component/Process/phpunit.xml.dist +++ b/src/Symfony/Component/Process/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ @@ -20,6 +21,7 @@ ./ ./Tests + ./vendor diff --git a/src/Symfony/Component/PropertyAccess/phpunit.xml.dist b/src/Symfony/Component/PropertyAccess/phpunit.xml.dist index 99858f7b95361..b0b20c1bd9460 100644 --- a/src/Symfony/Component/PropertyAccess/phpunit.xml.dist +++ b/src/Symfony/Component/PropertyAccess/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ @@ -21,6 +22,7 @@ ./Resources ./Tests + ./vendor diff --git a/src/Symfony/Component/Routing/phpunit.xml.dist b/src/Symfony/Component/Routing/phpunit.xml.dist index fae243c8152b0..b69f066ac1b2f 100644 --- a/src/Symfony/Component/Routing/phpunit.xml.dist +++ b/src/Symfony/Component/Routing/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ @@ -19,8 +20,8 @@ ./ - ./vendor ./Tests + ./vendor diff --git a/src/Symfony/Component/Security/phpunit.xml.dist b/src/Symfony/Component/Security/phpunit.xml.dist index 9a20f91498ae9..9d3cf4a02318d 100644 --- a/src/Symfony/Component/Security/phpunit.xml.dist +++ b/src/Symfony/Component/Security/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ @@ -19,8 +20,10 @@ ./ - ./vendor + ./Acl/Resources + ./Resources ./Tests + ./vendor diff --git a/src/Symfony/Component/Serializer/phpunit.xml.dist b/src/Symfony/Component/Serializer/phpunit.xml.dist index da0540137b19a..4799e3cf2f1dd 100644 --- a/src/Symfony/Component/Serializer/phpunit.xml.dist +++ b/src/Symfony/Component/Serializer/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ @@ -19,8 +20,8 @@ ./ - ./vendor ./Tests + ./vendor diff --git a/src/Symfony/Component/Stopwatch/phpunit.xml.dist b/src/Symfony/Component/Stopwatch/phpunit.xml.dist index 38078d25bb7ee..b16dcaebf9a47 100644 --- a/src/Symfony/Component/Stopwatch/phpunit.xml.dist +++ b/src/Symfony/Component/Stopwatch/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ diff --git a/src/Symfony/Component/Templating/phpunit.xml.dist b/src/Symfony/Component/Templating/phpunit.xml.dist index 3da1f5de1371c..109584e805db2 100644 --- a/src/Symfony/Component/Templating/phpunit.xml.dist +++ b/src/Symfony/Component/Templating/phpunit.xml.dist @@ -20,8 +20,8 @@ ./ - ./vendor ./Tests + ./vendor diff --git a/src/Symfony/Component/Translation/phpunit.xml.dist b/src/Symfony/Component/Translation/phpunit.xml.dist index 16cca4afb7c69..c25ec5eda6940 100644 --- a/src/Symfony/Component/Translation/phpunit.xml.dist +++ b/src/Symfony/Component/Translation/phpunit.xml.dist @@ -20,8 +20,8 @@ ./ - ./vendor ./Tests + ./vendor diff --git a/src/Symfony/Component/Validator/phpunit.xml.dist b/src/Symfony/Component/Validator/phpunit.xml.dist index 1bf4391c3c181..cf8c343863e5d 100644 --- a/src/Symfony/Component/Validator/phpunit.xml.dist +++ b/src/Symfony/Component/Validator/phpunit.xml.dist @@ -20,8 +20,9 @@ ./ - ./vendor + ./Resources ./Tests + ./vendor diff --git a/src/Symfony/Component/Yaml/phpunit.xml.dist b/src/Symfony/Component/Yaml/phpunit.xml.dist index 8f7741fe393e6..6bdbea16e6426 100644 --- a/src/Symfony/Component/Yaml/phpunit.xml.dist +++ b/src/Symfony/Component/Yaml/phpunit.xml.dist @@ -9,6 +9,7 @@ + ./Tests/ @@ -19,8 +20,8 @@ ./ - ./vendor ./Tests + ./vendor From d04984ad0593063a026257d2744e55ad5763c5a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Vasseur?= Date: Tue, 17 Nov 2015 17:20:32 +0100 Subject: [PATCH 011/107] Fix PropertyInfo extractor namespace in framework bundle --- .../Bundle/FrameworkBundle/Resources/config/property_info.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.xml index d4ce4f5d246f6..33a0a661f8991 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.xml @@ -13,7 +13,7 @@ - + From 01251455c0858d6beea032f343f542d0e81f25e2 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 16 Nov 2015 10:23:46 +0100 Subject: [PATCH 012/107] [ProxyManager] Tmp fix composer reqs issue in ZF --- composer.json | 1 + src/Symfony/Bridge/ProxyManager/composer.json | 1 + 2 files changed, 2 insertions(+) diff --git a/composer.json b/composer.json index 198070a402ee4..73f564c5fac37 100644 --- a/composer.json +++ b/composer.json @@ -70,6 +70,7 @@ "monolog/monolog": "~1.3", "propel/propel1": "~1.6", "ircmaxell/password-compat": "~1.0", + "zendframework/zend-stdlib": "~2.5", "ocramius/proxy-manager": "~0.3.1" }, "autoload": { diff --git a/src/Symfony/Bridge/ProxyManager/composer.json b/src/Symfony/Bridge/ProxyManager/composer.json index 82b119b164898..7efa4da4156e7 100644 --- a/src/Symfony/Bridge/ProxyManager/composer.json +++ b/src/Symfony/Bridge/ProxyManager/composer.json @@ -18,6 +18,7 @@ "require": { "php": ">=5.3.3", "symfony/dependency-injection": "~2.3", + "zendframework/zend-stdlib": "~2.5", "ocramius/proxy-manager": "~0.3.1" }, "require-dev": { From db5fbe0e236478e0b2cd829c18dc3fe1d6fa05ea Mon Sep 17 00:00:00 2001 From: Daniel Wehner Date: Wed, 18 Nov 2015 10:45:48 +0100 Subject: [PATCH 013/107] add it back --- src/Symfony/Component/HttpFoundation/Response.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index d67584f13974f..96d5055e00570 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -160,6 +160,7 @@ class Response 415 => 'Unsupported Media Type', 416 => 'Range Not Satisfiable', 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', // RFC2324 422 => 'Unprocessable Entity', // RFC4918 423 => 'Locked', // RFC4918 424 => 'Failed Dependency', // RFC4918 From c4068d923da7973011aa48a5ca86812e8d40119a Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Tue, 17 Nov 2015 16:13:39 -0500 Subject: [PATCH 014/107] Fix bug in windows detection --- src/Symfony/Component/Console/Style/SymfonyStyle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Console/Style/SymfonyStyle.php b/src/Symfony/Component/Console/Style/SymfonyStyle.php index 6e9c64fc2e9c3..365c03ae328f9 100644 --- a/src/Symfony/Component/Console/Style/SymfonyStyle.php +++ b/src/Symfony/Component/Console/Style/SymfonyStyle.php @@ -294,7 +294,7 @@ public function createProgressBar($max = 0) { $progressBar = parent::createProgressBar($max); - if ('\\' === DIRECTORY_SEPARATOR) { + if ('\\' !== DIRECTORY_SEPARATOR) { $progressBar->setEmptyBarCharacter('░'); // light shade character \u2591 $progressBar->setProgressCharacter(''); $progressBar->setBarCharacter('▓'); // dark shade character \u2593 From 15f9cf2740f1827cfb7ace60d6fc847f074f6fe8 Mon Sep 17 00:00:00 2001 From: Daniel Wehner Date: Wed, 18 Nov 2015 09:21:04 +0100 Subject: [PATCH 015/107] Fix call to undefined function json_last_error_message --- .../Serializer/Encoder/JsonDecode.php | 2 +- .../Serializer/Encoder/JsonEncode.php | 2 +- .../Tests/Encoder/JsonDecodeTest.php | 44 +++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Component/Serializer/Tests/Encoder/JsonDecodeTest.php diff --git a/src/Symfony/Component/Serializer/Encoder/JsonDecode.php b/src/Symfony/Component/Serializer/Encoder/JsonDecode.php index d4070c23040db..8925ec36f7f99 100644 --- a/src/Symfony/Component/Serializer/Encoder/JsonDecode.php +++ b/src/Symfony/Component/Serializer/Encoder/JsonDecode.php @@ -108,7 +108,7 @@ public function decode($data, $format, array $context = array()) } if (JSON_ERROR_NONE !== $this->lastError = json_last_error()) { - throw new UnexpectedValueException(json_last_error_message()); + throw new UnexpectedValueException(json_last_error_msg()); } return $decodedData; diff --git a/src/Symfony/Component/Serializer/Encoder/JsonEncode.php b/src/Symfony/Component/Serializer/Encoder/JsonEncode.php index aa7d64530e14d..454c0d6a11470 100644 --- a/src/Symfony/Component/Serializer/Encoder/JsonEncode.php +++ b/src/Symfony/Component/Serializer/Encoder/JsonEncode.php @@ -56,7 +56,7 @@ public function encode($data, $format, array $context = array()) $encodedJson = json_encode($data, $context['json_encode_options']); if (JSON_ERROR_NONE !== $this->lastError = json_last_error()) { - throw new UnexpectedValueException(json_last_error_message()); + throw new UnexpectedValueException(json_last_error_msg()); } return $encodedJson; diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/JsonDecodeTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/JsonDecodeTest.php new file mode 100644 index 0000000000000..03f2187b5576f --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Encoder/JsonDecodeTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Encoder; + +use Symfony\Component\Serializer\Encoder\JsonDecode; + +class JsonDecodeTest extends \PHPUnit_Framework_TestCase +{ + /** @var \Symfony\Component\Serializer\Encoder\JsonDecode */ + private $decoder; + + protected function setUp() + { + $this->decoder = new JsonDecode(true); + } + + public function testDecodeWithValidData() + { + $json = json_encode(array( + 'hello' => 'world', + )); + $result = $this->decoder->decode($json, 'json'); + $this->assertEquals(array( + 'hello' => 'world', + ), $result); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\UnexpectedValueException + */ + public function testDecodeWithInvalidData() + { + $result = $this->decoder->decode('kaboom!', 'json'); + } +} From 75aa6f68f820c4776f3172eb9eff137ba7bae704 Mon Sep 17 00:00:00 2001 From: Eugene Wissner Date: Tue, 17 Nov 2015 03:38:36 +0100 Subject: [PATCH 016/107] Fix undefined array $server --- src/Symfony/Component/HttpFoundation/Tests/RequestTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 2b54ef5f607d1..dcc1c2eb99132 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -570,7 +570,7 @@ public function testGetUserInfo() { $request = new Request(); - $server['PHP_AUTH_USER'] = 'fabien'; + $server = array('PHP_AUTH_USER' => 'fabien'); $request->initialize(array(), array(), array(), array(), array(), $server); $this->assertEquals('fabien', $request->getUserInfo()); From 7418d29ae97ed10a2299099be34cafe4fa04d56b Mon Sep 17 00:00:00 2001 From: FlorianLB Date: Mon, 16 Nov 2015 11:02:30 +0100 Subject: [PATCH 017/107] [Serializer] add missing unit tests related to Encoder --- .../Tests/Encoder/ChainDecoderTest.php | 77 +++++++++++ .../Tests/Encoder/ChainEncoderTest.php | 129 ++++++++++++++++++ .../Tests/Encoder/JsonDecodeTest.php | 56 ++++++-- .../Tests/Encoder/JsonEncodeTest.php | 59 ++++++++ 4 files changed, 308 insertions(+), 13 deletions(-) create mode 100644 src/Symfony/Component/Serializer/Tests/Encoder/ChainDecoderTest.php create mode 100644 src/Symfony/Component/Serializer/Tests/Encoder/ChainEncoderTest.php create mode 100644 src/Symfony/Component/Serializer/Tests/Encoder/JsonEncodeTest.php diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/ChainDecoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/ChainDecoderTest.php new file mode 100644 index 0000000000000..7f7392e6a45b1 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Encoder/ChainDecoderTest.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Encoder; + +use Symfony\Component\Serializer\Encoder\ChainDecoder; + +class ChainDecoderTest extends \PHPUnit_Framework_TestCase +{ + const FORMAT_1 = 'format1'; + const FORMAT_2 = 'format2'; + const FORMAT_3 = 'format3'; + + private $chainDecoder; + private $decoder1; + private $decoder2; + + protected function setUp() + { + $this->decoder1 = $this + ->getMockBuilder('Symfony\Component\Serializer\Encoder\DecoderInterface') + ->getMock(); + + $this->decoder1 + ->method('supportsDecoding') + ->will($this->returnValueMap(array( + array(self::FORMAT_1, true), + array(self::FORMAT_2, false), + array(self::FORMAT_3, false), + ))); + + $this->decoder2 = $this + ->getMockBuilder('Symfony\Component\Serializer\Encoder\DecoderInterface') + ->getMock(); + + $this->decoder2 + ->method('supportsDecoding') + ->will($this->returnValueMap(array( + array(self::FORMAT_1, false), + array(self::FORMAT_2, true), + array(self::FORMAT_3, false), + ))); + + $this->chainDecoder = new ChainDecoder(array($this->decoder1, $this->decoder2)); + } + + public function testSupportsDecoding() + { + $this->assertTrue($this->chainDecoder->supportsDecoding(self::FORMAT_1)); + $this->assertTrue($this->chainDecoder->supportsDecoding(self::FORMAT_2)); + $this->assertFalse($this->chainDecoder->supportsDecoding(self::FORMAT_3)); + } + + public function testDecode() + { + $this->decoder1->expects($this->never())->method('decode'); + $this->decoder2->expects($this->once())->method('decode'); + + $this->chainDecoder->decode('string_to_decode', self::FORMAT_2); + } + + /** + * @expectedException Symfony\Component\Serializer\Exception\RuntimeException + */ + public function testDecodeUnsupportedFormat() + { + $this->chainDecoder->decode('string_to_decode', self::FORMAT_3); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/ChainEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/ChainEncoderTest.php new file mode 100644 index 0000000000000..6d3436b33d753 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Encoder/ChainEncoderTest.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Encoder; + +use Symfony\Component\Serializer\Encoder\ChainEncoder; +use Symfony\Component\Serializer\Encoder\NormalizationAwareInterface; + +class ChainEncoderTest extends \PHPUnit_Framework_TestCase +{ + const FORMAT_1 = 'format1'; + const FORMAT_2 = 'format2'; + const FORMAT_3 = 'format3'; + + private $chainEncoder; + private $encoder1; + private $encoder2; + + protected function setUp() + { + $this->encoder1 = $this + ->getMockBuilder('Symfony\Component\Serializer\Encoder\EncoderInterface') + ->getMock(); + + $this->encoder1 + ->method('supportsEncoding') + ->will($this->returnValueMap(array( + array(self::FORMAT_1, true), + array(self::FORMAT_2, false), + array(self::FORMAT_3, false), + ))); + + $this->encoder2 = $this + ->getMockBuilder('Symfony\Component\Serializer\Encoder\EncoderInterface') + ->getMock(); + + $this->encoder2 + ->method('supportsEncoding') + ->will($this->returnValueMap(array( + array(self::FORMAT_1, false), + array(self::FORMAT_2, true), + array(self::FORMAT_3, false), + ))); + + $this->chainEncoder = new ChainEncoder(array($this->encoder1, $this->encoder2)); + } + + public function testSupportsEncoding() + { + $this->assertTrue($this->chainEncoder->supportsEncoding(self::FORMAT_1)); + $this->assertTrue($this->chainEncoder->supportsEncoding(self::FORMAT_2)); + $this->assertFalse($this->chainEncoder->supportsEncoding(self::FORMAT_3)); + } + + public function testEncode() + { + $this->encoder1->expects($this->never())->method('encode'); + $this->encoder2->expects($this->once())->method('encode'); + + $this->chainEncoder->encode(array('foo' => 123), self::FORMAT_2); + } + + /** + * @expectedException Symfony\Component\Serializer\Exception\RuntimeException + */ + public function testEncodeUnsupportedFormat() + { + $this->chainEncoder->encode(array('foo' => 123), self::FORMAT_3); + } + + public function testNeedsNormalizationBasic() + { + $this->assertTrue($this->chainEncoder->needsNormalization(self::FORMAT_1)); + $this->assertTrue($this->chainEncoder->needsNormalization(self::FORMAT_2)); + } + + /** + * @dataProvider booleanProvider + */ + public function testNeedsNormalizationChainNormalizationAware($bool) + { + $chainEncoder = $this + ->getMockBuilder('Symfony\Component\Serializer\Tests\Encoder\ChainNormalizationAwareEncoder') + ->getMock(); + + $chainEncoder->method('supportsEncoding')->willReturn(true); + $chainEncoder->method('needsNormalization')->willReturn($bool); + + $sut = new ChainEncoder(array($chainEncoder)); + + $this->assertEquals($bool, $sut->needsNormalization(self::FORMAT_1)); + } + + public function testNeedsNormalizationNormalizationAware() + { + $encoder = new NormalizationAwareEncoder(); + $sut = new ChainEncoder(array($encoder)); + + $this->assertFalse($sut->needsNormalization(self::FORMAT_1)); + } + + public function booleanProvider() + { + return array( + array(true), + array(false), + ); + } +} + +class ChainNormalizationAwareEncoder extends ChainEncoder implements NormalizationAwareInterface +{ +} + +class NormalizationAwareEncoder implements NormalizationAwareInterface +{ + public function supportsEncoding($format) + { + return true; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/JsonDecodeTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/JsonDecodeTest.php index 03f2187b5576f..97930115748d2 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/JsonDecodeTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/JsonDecodeTest.php @@ -12,33 +12,63 @@ namespace Symfony\Component\Serializer\Tests\Encoder; use Symfony\Component\Serializer\Encoder\JsonDecode; +use Symfony\Component\Serializer\Encoder\JsonEncoder; class JsonDecodeTest extends \PHPUnit_Framework_TestCase { /** @var \Symfony\Component\Serializer\Encoder\JsonDecode */ - private $decoder; + private $decode; protected function setUp() { - $this->decoder = new JsonDecode(true); + $this->decode = new JsonDecode(); } - public function testDecodeWithValidData() + public function testSupportsDecoding() { - $json = json_encode(array( - 'hello' => 'world', - )); - $result = $this->decoder->decode($json, 'json'); - $this->assertEquals(array( - 'hello' => 'world', - ), $result); + $this->assertTrue($this->decode->supportsDecoding(JsonEncoder::FORMAT)); + $this->assertFalse($this->decode->supportsDecoding('foobar')); } /** - * @expectedException \Symfony\Component\Serializer\Exception\UnexpectedValueException + * @dataProvider decodeProvider */ - public function testDecodeWithInvalidData() + public function testDecode($toDecode, $expected, $context) { - $result = $this->decoder->decode('kaboom!', 'json'); + $this->assertEquals( + $expected, + $this->decode->decode($toDecode, JsonEncoder::FORMAT, $context) + ); + } + + public function decodeProvider() + { + $stdClass = new \stdClass(); + $stdClass->foo = 'bar'; + + $assoc = array('foo' => 'bar'); + + return array( + array('{"foo": "bar"}', $stdClass, array()), + array('{"foo": "bar"}', $assoc, array('json_decode_associative' => true)), + ); + } + + /** + * @requires function json_last_error_msg + * @dataProvider decodeProviderException + * @expectedException Symfony\Component\Serializer\Exception\UnexpectedValueException + */ + public function testDecodeWithException($value) + { + $this->decode->decode($value, JsonEncoder::FORMAT); + } + + public function decodeProviderException() + { + return array( + array("{'foo': 'bar'}"), + array('kaboom!'), + ); } } diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/JsonEncodeTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/JsonEncodeTest.php new file mode 100644 index 0000000000000..3e6fb7b6ec34e --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Encoder/JsonEncodeTest.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Encoder; + +use Symfony\Component\Serializer\Encoder\JsonEncode; +use Symfony\Component\Serializer\Encoder\JsonEncoder; + +class JsonEncodeTest extends \PHPUnit_Framework_TestCase +{ + private $encoder; + + protected function setUp() + { + $this->encode = new JsonEncode(); + } + + public function testSupportsEncoding() + { + $this->assertTrue($this->encode->supportsEncoding(JsonEncoder::FORMAT)); + $this->assertFalse($this->encode->supportsEncoding('foobar')); + } + + /** + * @dataProvider encodeProvider + */ + public function testEncode($toEncode, $expected, $context) + { + $this->assertEquals( + $expected, + $this->encode->encode($toEncode, JsonEncoder::FORMAT, $context) + ); + } + + public function encodeProvider() + { + return array( + array(array(), '[]', array()), + array(array(), '{}', array('json_encode_options' => JSON_FORCE_OBJECT)), + ); + } + + /** + * @requires function json_last_error_msg + * @expectedException Symfony\Component\Serializer\Exception\UnexpectedValueException + */ + public function testEncodeWithError() + { + $this->encode->encode("\xB1\x31", JsonEncoder::FORMAT); + } +} From 9669238af66ce0daf3ec2ecba5918f9ce9767297 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 17 Nov 2015 17:45:51 +0100 Subject: [PATCH 018/107] [Process] Fix PhpProcess with phpdbg runtime --- composer.json | 2 +- phpunit | 10 ++++++---- src/Symfony/Bridge/ProxyManager/composer.json | 2 +- .../Component/Process/PhpExecutableFinder.php | 7 ++++--- src/Symfony/Component/Process/PhpProcess.php | 7 +++++++ src/Symfony/Component/Process/Process.php | 2 +- .../Process/Tests/AbstractProcessTest.php | 20 ++++--------------- .../Process/Tests/PhpExecutableFinderTest.php | 2 ++ .../Process/Tests/PhpProcessTest.php | 4 ++++ 9 files changed, 30 insertions(+), 26 deletions(-) diff --git a/composer.json b/composer.json index 73f564c5fac37..4d0757a371e69 100644 --- a/composer.json +++ b/composer.json @@ -70,7 +70,7 @@ "monolog/monolog": "~1.3", "propel/propel1": "~1.6", "ircmaxell/password-compat": "~1.0", - "zendframework/zend-stdlib": "~2.5", + "zendframework/zend-stdlib": "~2.2", "ocramius/proxy-manager": "~0.3.1" }, "autoload": { diff --git a/phpunit b/phpunit index 0b8e2e3d3c0f6..2ab4f25e75cc2 100755 --- a/phpunit +++ b/phpunit @@ -11,7 +11,7 @@ */ // Please update when phpunit needs to be reinstalled with fresh deps: -// Cache-Id-Version: 2015-11-09 12:13 UTC +// Cache-Id-Version: 2015-11-18 14:14 UTC use Symfony\Component\Process\ProcessUtils; @@ -23,12 +23,15 @@ $PHPUNIT_VERSION = PHP_VERSION_ID >= 70000 ? '5.0' : '4.8'; $PHPUNIT_DIR = __DIR__.'/.phpunit'; $PHP = defined('PHP_BINARY') ? PHP_BINARY : 'php'; $PHP = ProcessUtils::escapeArgument($PHP); +if ('phpdbg' === PHP_SAPI) { + $PHP .= ' -qrr'; +} $COMPOSER = file_exists($COMPOSER = __DIR__.'/composer.phar') || ($COMPOSER = rtrim('\\' === DIRECTORY_SEPARATOR ? `where.exe composer.phar` : `which composer.phar`)) ? $PHP.' '.ProcessUtils::escapeArgument($COMPOSER) : 'composer'; -if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__FILE__) !== @file_get_contents("$PHPUNIT_DIR/.md5")) { +if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__FILE__) !== @file_get_contents("$PHPUNIT_DIR/.$PHPUNIT_VERSION.md5")) { // Build a standalone phpunit without symfony/yaml $oldPwd = getcwd(); @@ -49,7 +52,6 @@ if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__ $zip->close(); chdir("phpunit-$PHPUNIT_VERSION"); passthru("$COMPOSER remove --no-update symfony/yaml"); - passthru("$COMPOSER require --no-update phpunit/phpunit-mock-objects \"<=3.0.0\""); passthru("$COMPOSER require --dev --no-update symfony/phpunit-bridge \">=2.8@dev\""); passthru("$COMPOSER install --prefer-source --no-progress --ansi"); file_put_contents('phpunit', <<nul': 'rm -rf %s', str_replace('/', DIRECTORY_SEPARATOR, "phpunit-$PHPUNIT_VERSION/vendor/symfony/phpunit-bridge"))); symlink(realpath('../src/Symfony/Bridge/PhpUnit'), "phpunit-$PHPUNIT_VERSION/vendor/symfony/phpunit-bridge"); } - file_put_contents('.md5', md5_file(__FILE__)); + file_put_contents(".$PHPUNIT_VERSION.md5", md5_file(__FILE__)); chdir($oldPwd); } diff --git a/src/Symfony/Bridge/ProxyManager/composer.json b/src/Symfony/Bridge/ProxyManager/composer.json index 7efa4da4156e7..5f9bd8a2afbaa 100644 --- a/src/Symfony/Bridge/ProxyManager/composer.json +++ b/src/Symfony/Bridge/ProxyManager/composer.json @@ -18,7 +18,7 @@ "require": { "php": ">=5.3.3", "symfony/dependency-injection": "~2.3", - "zendframework/zend-stdlib": "~2.5", + "zendframework/zend-stdlib": "~2.2", "ocramius/proxy-manager": "~0.3.1" }, "require-dev": { diff --git a/src/Symfony/Component/Process/PhpExecutableFinder.php b/src/Symfony/Component/Process/PhpExecutableFinder.php index f8f57cc536a25..e8b77b73b6c12 100644 --- a/src/Symfony/Component/Process/PhpExecutableFinder.php +++ b/src/Symfony/Component/Process/PhpExecutableFinder.php @@ -41,8 +41,8 @@ public function find($includeArgs = true) } // PHP_BINARY return the current sapi executable - if (defined('PHP_BINARY') && PHP_BINARY && in_array(PHP_SAPI, array('cli', 'cli-server')) && is_file(PHP_BINARY)) { - return PHP_BINARY; + if (defined('PHP_BINARY') && PHP_BINARY && in_array(PHP_SAPI, array('cli', 'cli-server', 'phpdbg')) && is_file(PHP_BINARY)) { + return PHP_BINARY.($includeArgs ? ' '.implode(' ', $this->findArguments()) : ''); } if ($php = getenv('PHP_PATH')) { @@ -76,9 +76,10 @@ public function findArguments() { $arguments = array(); - // HHVM support if (defined('HHVM_VERSION')) { $arguments[] = '--php'; + } elseif ('phpdbg' === PHP_SAPI) { + $arguments[] = '-qrr'; } return $arguments; diff --git a/src/Symfony/Component/Process/PhpProcess.php b/src/Symfony/Component/Process/PhpProcess.php index 1adbd977adf88..4a2a2625ffae0 100644 --- a/src/Symfony/Component/Process/PhpProcess.php +++ b/src/Symfony/Component/Process/PhpProcess.php @@ -39,6 +39,13 @@ public function __construct($script, $cwd = null, array $env = null, $timeout = if (false === $php = $executableFinder->find()) { $php = null; } + if ('phpdbg' === PHP_SAPI) { + $file = tempnam(sys_get_temp_dir(), 'dbg'); + file_put_contents($file, $script); + register_shutdown_function('unlink', $file); + $php .= ' '.ProcessUtils::escapeArgument($file); + $script = null; + } parent::__construct($php, $cwd, $env, $script, $timeout, $options); } diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index ae11f89c10601..1675ad33994aa 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -242,7 +242,7 @@ public function start($callback = null) if (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { // Workaround for the bug, when PTS functionality is enabled. // @see : https://bugs.php.net/69442 - $ptsWorkaround = fopen('php://fd/0', 'r'); + $ptsWorkaround = fopen(__FILE__, 'r'); } $this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $this->env, $this->options); diff --git a/src/Symfony/Component/Process/Tests/AbstractProcessTest.php b/src/Symfony/Component/Process/Tests/AbstractProcessTest.php index 62616eed347d4..1874290936d53 100644 --- a/src/Symfony/Component/Process/Tests/AbstractProcessTest.php +++ b/src/Symfony/Component/Process/Tests/AbstractProcessTest.php @@ -27,7 +27,7 @@ abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase public static function setUpBeforeClass() { $phpBin = new PhpExecutableFinder(); - self::$phpBin = $phpBin->find(); + self::$phpBin = 'phpdbg' === PHP_SAPI ? 'php' : $phpBin->find(); } public function testThatProcessDoesNotThrowWarningDuringRun() @@ -80,7 +80,7 @@ public function testStopWithTimeoutIsActuallyWorking() // exec is mandatory here since we send a signal to the process // see https://github.com/symfony/symfony/issues/5030 about prepending // command with exec - $p = $this->getProcess('exec php '.__DIR__.'/NonStopableProcess.php 3'); + $p = $this->getProcess('exec '.self::$phpBin.' '.__DIR__.'/NonStopableProcess.php 3'); $p->start(); usleep(100000); $start = microtime(true); @@ -410,7 +410,7 @@ public function testTTYCommand() $this->markTestSkipped('Windows does have /dev/tty support'); } - $process = $this->getProcess('echo "foo" >> /dev/null && php -r "usleep(100000);"'); + $process = $this->getProcess('echo "foo" >> /dev/null && '.self::$phpBin.' -r "usleep(100000);"'); $process->setTty(true); $process->start(); $this->assertTrue($process->isRunning()); @@ -633,7 +633,7 @@ public function testProcessThrowsExceptionWhenExternallySignaled() $termSignal = defined('SIGKILL') ? SIGKILL : 9; - $process = $this->getProcess('exec php -r "while (true) {}"'); + $process = $this->getProcess('exec '.self::$phpBin.' -r "while (true) {}"'); $process->start(); posix_kill($process->getPid(), $termSignal); @@ -659,18 +659,6 @@ public function testRestart() $this->assertNotEquals($process1->getOutput(), $process2->getOutput()); } - public function testPhpDeadlock() - { - $this->markTestSkipped('Can cause PHP to hang'); - - // Sleep doesn't work as it will allow the process to handle signals and close - // file handles from the other end. - $process = $this->getProcess(self::$phpBin.' -r "while (true) {}"'); - $process->start(); - - // PHP will deadlock when it tries to cleanup $process - } - public function testRunProcessWithTimeout() { $timeout = 0.5; diff --git a/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php b/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php index cd4abedc9df73..28bafe38ea1c6 100644 --- a/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php +++ b/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php @@ -68,6 +68,8 @@ public function testFindArguments() if (defined('HHVM_VERSION')) { $this->assertEquals($f->findArguments(), array('--php'), '::findArguments() returns HHVM arguments'); + } elseif ('phpdbg' === PHP_SAPI) { + $this->assertEquals($f->findArguments(), array('-qrr'), '::findArguments() returns phpdbg arguments'); } else { $this->assertEquals($f->findArguments(), array(), '::findArguments() returns no arguments'); } diff --git a/src/Symfony/Component/Process/Tests/PhpProcessTest.php b/src/Symfony/Component/Process/Tests/PhpProcessTest.php index 5dc546cc1ce6e..2cf79aa1a6d15 100644 --- a/src/Symfony/Component/Process/Tests/PhpProcessTest.php +++ b/src/Symfony/Component/Process/Tests/PhpProcessTest.php @@ -30,6 +30,10 @@ public function testNonBlockingWorks() public function testCommandLine() { + if ('phpdbg' === PHP_SAPI) { + $this->markTestSkipped('phpdbg SAPI is not supported by this test.'); + } + $process = new PhpProcess(<< Date: Wed, 18 Nov 2015 19:46:46 +0100 Subject: [PATCH 019/107] [Form] Fix ResolvedFormType deprecation notices --- src/Symfony/Component/Form/ResolvedFormType.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Form/ResolvedFormType.php b/src/Symfony/Component/Form/ResolvedFormType.php index b334acff3c655..d637d223a4631 100644 --- a/src/Symfony/Component/Form/ResolvedFormType.php +++ b/src/Symfony/Component/Form/ResolvedFormType.php @@ -71,12 +71,12 @@ public function __construct(FormTypeInterface $innerType, array $typeExtensions // Anyone else should only override getBlockPrefix() if they actually // want to have a different block prefix than the default one if ($isOldOverwritten && !$isNewOverwritten) { - @trigger_error(get_class($this->innerType).': The FormTypeInterface::getName() method is deprecated since version 2.8 and will be removed in 3.0. Remove it from your classes. Use getBlockPrefix() if you want to customize the template block prefix. This method will be added to the FormTypeInterface with Symfony 3.0.', E_USER_DEPRECATED); + @trigger_error(get_class($innerType).': The FormTypeInterface::getName() method is deprecated since version 2.8 and will be removed in 3.0. Remove it from your classes. Use getBlockPrefix() if you want to customize the template block prefix. This method will be added to the FormTypeInterface with Symfony 3.0.', E_USER_DEPRECATED); } $blockPrefix = $innerType->getBlockPrefix(); } else { - @trigger_error(get_class($this->innerType).': The FormTypeInterface::getBlockPrefix() method will be added in version 3.0. You should extend AbstractType or add it to your implementation.', E_USER_DEPRECATED); + @trigger_error(get_class($innerType).': The FormTypeInterface::getBlockPrefix() method will be added in version 3.0. You should extend AbstractType or add it to your implementation.', E_USER_DEPRECATED); // Deal with classes that don't extend AbstractType // Calculate block prefix from the FQCN by default From 04599125238d23e0c5388fbbcc6067055c970f62 Mon Sep 17 00:00:00 2001 From: Richard Date: Thu, 19 Nov 2015 10:04:56 +0100 Subject: [PATCH 020/107] [WebProfilerBundle] Fix minitoolbar height --- .../Resources/views/Profiler/toolbar.css.twig | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig index 2cb44f87335bb..0a269ea3f772f 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -2,12 +2,20 @@ background-color: #222; bottom: 0; display: none; - height: 36px; - padding: 5px 6px 0; + height: 30px; + padding: 6px 6px 0; position: fixed; right: 0; z-index: 99999; } +.sf-minitoolbar a { + display: block; +} +.sf-minitoolbar svg, +.sf-minitoolbar img { + max-height: 24px; + max-width: 24px; +} .sf-toolbarreset * { -webkit-box-sizing: content-box; @@ -36,7 +44,8 @@ } .sf-toolbarreset svg, .sf-toolbarreset img { - max-height: 20px; + max-height: 24px; + max-width: 24px; } .sf-toolbarreset .hide-button { From 6e015e7e0e18eacf8eec3940f15440a921458783 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 19 Nov 2015 09:34:27 +0100 Subject: [PATCH 021/107] Remove tmp addition of zend-stdlib --- composer.json | 1 - src/Symfony/Bridge/ProxyManager/composer.json | 1 - 2 files changed, 2 deletions(-) diff --git a/composer.json b/composer.json index 4d0757a371e69..198070a402ee4 100644 --- a/composer.json +++ b/composer.json @@ -70,7 +70,6 @@ "monolog/monolog": "~1.3", "propel/propel1": "~1.6", "ircmaxell/password-compat": "~1.0", - "zendframework/zend-stdlib": "~2.2", "ocramius/proxy-manager": "~0.3.1" }, "autoload": { diff --git a/src/Symfony/Bridge/ProxyManager/composer.json b/src/Symfony/Bridge/ProxyManager/composer.json index 5f9bd8a2afbaa..82b119b164898 100644 --- a/src/Symfony/Bridge/ProxyManager/composer.json +++ b/src/Symfony/Bridge/ProxyManager/composer.json @@ -18,7 +18,6 @@ "require": { "php": ">=5.3.3", "symfony/dependency-injection": "~2.3", - "zendframework/zend-stdlib": "~2.2", "ocramius/proxy-manager": "~0.3.1" }, "require-dev": { From f15e6e0ba924096edd170926ec09a9af6da514f4 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 19 Nov 2015 13:33:23 +0100 Subject: [PATCH 022/107] [Process] Fix trailing space in PHP binary finder --- src/Symfony/Component/Process/PhpExecutableFinder.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Process/PhpExecutableFinder.php b/src/Symfony/Component/Process/PhpExecutableFinder.php index e8b77b73b6c12..fb297825fe364 100644 --- a/src/Symfony/Component/Process/PhpExecutableFinder.php +++ b/src/Symfony/Component/Process/PhpExecutableFinder.php @@ -35,14 +35,17 @@ public function __construct() */ public function find($includeArgs = true) { + $args = $this->findArguments(); + $args = $includeArgs && $args ? ' '.implode(' ', $args) : ''; + // HHVM support if (defined('HHVM_VERSION')) { - return (getenv('PHP_BINARY') ?: PHP_BINARY).($includeArgs ? ' '.implode(' ', $this->findArguments()) : ''); + return (getenv('PHP_BINARY') ?: PHP_BINARY).$args; } // PHP_BINARY return the current sapi executable if (defined('PHP_BINARY') && PHP_BINARY && in_array(PHP_SAPI, array('cli', 'cli-server', 'phpdbg')) && is_file(PHP_BINARY)) { - return PHP_BINARY.($includeArgs ? ' '.implode(' ', $this->findArguments()) : ''); + return PHP_BINARY.$args; } if ($php = getenv('PHP_PATH')) { From 331a0469c10af2a229d411828fa316ea408706c8 Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Tue, 20 Oct 2015 14:28:37 +0200 Subject: [PATCH 023/107] [DependencyInjection] Unescape parameters for all types of injection --- .../DependencyInjection/ContainerBuilder.php | 4 ++-- .../Tests/ContainerBuilderTest.php | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 2c4460f394a09..6c52c47b651a9 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -895,7 +895,7 @@ public function createService(Definition $definition, $id, $tryProxy = true) $this->callMethod($service, $call); } - $properties = $this->resolveServices($parameterBag->resolveValue($definition->getProperties())); + $properties = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties()))); foreach ($properties as $name => $value) { $service->$name = $value; } @@ -1054,7 +1054,7 @@ private function callMethod($service, $call) } } - call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->resolveValue($call[1]))); + call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])))); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index e215b9d00a394..3a369d9d2ed18 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -305,6 +305,24 @@ public function testCreateServiceMethodCalls() $this->assertEquals(array('bar', $builder->get('bar')), $builder->get('foo1')->bar, '->createService() replaces the values in the method calls arguments'); } + public function testCreateServiceMethodCallsWithEscapedParam() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass'); + $builder->register('foo1', 'FooClass')->addMethodCall('setBar', array(array('%%unescape_it%%'))); + $builder->setParameter('value', 'bar'); + $this->assertEquals(array('%unescape_it%'), $builder->get('foo1')->bar, '->createService() replaces the values in the method calls arguments'); + } + + public function testCreateServiceProperties() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass'); + $builder->register('foo1', 'FooClass')->setProperty('bar', array('%value%', new Reference('bar'), '%%unescape_it%%')); + $builder->setParameter('value', 'bar'); + $this->assertEquals(array('bar', $builder->get('bar'), '%unescape_it%'), $builder->get('foo1')->bar, '->createService() replaces the values in the properties'); + } + public function testCreateServiceConfigurator() { $builder = new ContainerBuilder(); From eec6fbc768a3c3b5348641f241f1f4a9d90a303b Mon Sep 17 00:00:00 2001 From: Daniel Wehner Date: Wed, 18 Nov 2015 11:39:29 +0100 Subject: [PATCH 024/107] Sent out a status text for unknown HTTP headers. --- src/Symfony/Component/HttpFoundation/Response.php | 2 +- src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index 3c37187cd1e6b..ad55f8772af47 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -412,7 +412,7 @@ public function setStatusCode($code, $text = null) } if (null === $text) { - $this->statusText = isset(self::$statusTexts[$code]) ? self::$statusTexts[$code] : ''; + $this->statusText = isset(self::$statusTexts[$code]) ? self::$statusTexts[$code] : 'unknown status'; return $this; } diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php index 72e143f9da438..f0539079c949c 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php @@ -647,7 +647,7 @@ public function getStatusCodeFixtures() array('200', null, 'OK'), array('200', false, ''), array('200', 'foo', 'foo'), - array('199', null, ''), + array('199', null, 'unknown status'), array('199', false, ''), array('199', 'foo', 'foo'), ); From 576f8029177e7454d8e4c851c2ece123b24ad706 Mon Sep 17 00:00:00 2001 From: ogizanagi Date: Thu, 19 Nov 2015 23:03:24 +0100 Subject: [PATCH 025/107] [Process] PhpExecutableFinder: add regression test --- .../Process/Tests/PhpExecutableFinderTest.php | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php b/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php index 28bafe38ea1c6..87d0efe9ebf1f 100644 --- a/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php +++ b/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php @@ -43,7 +43,27 @@ public function testFindWithPhpPath() } /** - * tests find() with the env var PHP_PATH. + * tests find() with the constant PHP_BINARY. + * + * @requires PHP 5.4 + */ + public function testFind() + { + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('Should not be executed in HHVM context.'); + } + + $f = new PhpExecutableFinder(); + + $current = PHP_BINARY; + $args = 'phpdbg' === PHP_SAPI ? ' -qrr' : ''; + + $this->assertEquals($current.$args, $f->find(), '::find() returns the executable PHP'); + $this->assertEquals($current, $f->find(false), '::find() returns the executable PHP'); + } + + /** + * tests find() with the env var / constant PHP_BINARY with HHVM. */ public function testFindWithHHVM() { From a591ba3d8085587c2892fe7716a9966e1119bef5 Mon Sep 17 00:00:00 2001 From: Joshua Thijssen Date: Fri, 20 Nov 2015 11:06:22 +0100 Subject: [PATCH 026/107] Added translation for BIC validator --- .../Validator/Resources/translations/validators.nl.xlf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf index fe3346973e8b4..371ad9741e612 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.nl.xlf @@ -306,6 +306,10 @@ This value does not match the expected {{ charset }} charset. Deze waarde is niet in de verwachte tekencodering {{ charset }}. + + This is not a valid Business Identifier Code (BIC). + Dit is geen geldige bedrijfsidentificatiecode (BIC/SWIFT). + From baad4da9b72d2d0af14a3be433a453f22d106870 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 20 Nov 2015 13:11:01 +0100 Subject: [PATCH 027/107] [HttpKernel] Don't reset on shutdown but in FrameworkBundle/Test/KernelTestCase --- src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php | 5 +++++ src/Symfony/Component/HttpKernel/Kernel.php | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php index 04f934c4fe525..aa8b66929c7ce 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Test; +use Symfony\Component\DependencyInjection\ResettableContainerInterface; use Symfony\Component\Finder\Finder; use Symfony\Component\HttpKernel\KernelInterface; @@ -171,7 +172,11 @@ protected static function createKernel(array $options = array()) protected static function ensureKernelShutdown() { if (null !== static::$kernel) { + $container = static::$kernel->getContainer(); static::$kernel->shutdown(); + if ($container instanceof ResettableContainerInterface) { + $container->reset(); + } } } diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 7c55172d395ae..2b573fe860471 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -23,7 +23,6 @@ use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; use Symfony\Component\DependencyInjection\Loader\ClosureLoader; -use Symfony\Component\DependencyInjection\ResettableContainerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Bundle\BundleInterface; @@ -171,10 +170,6 @@ public function shutdown() $bundle->setContainer(null); } - if ($this->container instanceof ResettableContainerInterface) { - $this->container->reset(); - } - $this->container = null; } From 357dea1ea82fb31d246ca71b404de2e49f2b4bd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20J=2E=20Garc=C3=ADa=20Lagar?= Date: Fri, 20 Nov 2015 14:32:45 +0100 Subject: [PATCH 028/107] Add Spanish translation for BIC validator --- .../Validator/Resources/translations/validators.es.xlf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf index d874573a7afa0..1fa59dda6ad46 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf @@ -310,6 +310,10 @@ This value does not match the expected {{ charset }} charset. La codificación de caracteres para este valor debería ser {{ charset }}. + + This is not a valid Business Identifier Code (BIC). + No es un Código de Identificación Bancaria (BIC) válido. + From 8feb9ef080b6c04bd40cdec4d0af030002be4cc0 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 5 Nov 2015 15:58:36 -0500 Subject: [PATCH 029/107] [Routing] Changing RouteCollectionBuilder::import() behavior to add to the builder --- .../Component/Routing/RouteCollectionBuilder.php | 12 ++++++++---- .../Routing/Tests/RouteCollectionBuilderTest.php | 7 +++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Routing/RouteCollectionBuilder.php b/src/Symfony/Component/Routing/RouteCollectionBuilder.php index 700ee37234963..a54a049d27d12 100644 --- a/src/Symfony/Component/Routing/RouteCollectionBuilder.php +++ b/src/Symfony/Component/Routing/RouteCollectionBuilder.php @@ -49,16 +49,17 @@ public function __construct(LoaderInterface $loader = null) /** * Import an external routing resource and returns the RouteCollectionBuilder. * - * $routes->mount('/blog', $routes->import('blog.yml')); + * $routes->import('blog.yml', '/blog'); * - * @param mixed $resource - * @param string $type + * @param mixed $resource + * @param string|null $prefix + * @param string $type * * @return RouteCollectionBuilder * * @throws FileLoaderLoadException */ - public function import($resource, $type = null) + public function import($resource, $prefix = '/', $type = null) { /** @var RouteCollection $collection */ $collection = $this->load($resource, $type); @@ -73,6 +74,9 @@ public function import($resource, $type = null) $builder->addResource($resource); } + // mount into this builder + $this->mount($prefix, $builder); + return $builder; } diff --git a/src/Symfony/Component/Routing/Tests/RouteCollectionBuilderTest.php b/src/Symfony/Component/Routing/Tests/RouteCollectionBuilderTest.php index c19fdcbf9ff69..e3a98e3b72698 100644 --- a/src/Symfony/Component/Routing/Tests/RouteCollectionBuilderTest.php +++ b/src/Symfony/Component/Routing/Tests/RouteCollectionBuilderTest.php @@ -45,7 +45,7 @@ public function testImport() // import the file! $routes = new RouteCollectionBuilder($loader); - $importedRoutes = $routes->import('admin_routing.yml', 'yaml'); + $importedRoutes = $routes->import('admin_routing.yml', '/', 'yaml'); // we should get back a RouteCollectionBuilder $this->assertInstanceOf('Symfony\Component\Routing\RouteCollectionBuilder', $importedRoutes); @@ -56,6 +56,9 @@ public function testImport() $this->assertSame($originalRoute, $route); // should return file_resource.yml, which is in the original collection $this->assertCount(1, $addedCollection->getResources()); + + // make sure the routes were imported into the top-level builder + $this->assertCount(1, $routes->build()); } /** @@ -285,7 +288,7 @@ public function testFlushSetsPrefixedWithMultipleLevels() ->method('load') ->will($this->returnValue($importedCollection)); // import this from the /admin route builder - $adminRoutes->mount('/imported', $adminRoutes->import('admin.yml')); + $adminRoutes->import('admin.yml', '/imported'); $collection = $routes->build(); $this->assertEquals('/admin/dashboard', $collection->get('admin_dashboard')->getPath(), 'Routes before mounting have the prefix'); From ca9f446f035ed62976185187306cf1ca7ffa0573 Mon Sep 17 00:00:00 2001 From: Jelle Kapitein Date: Mon, 23 Nov 2015 09:33:29 +0100 Subject: [PATCH 030/107] =?UTF-8?q?[WebProfilerBundle]=20Added=20a=20top?= =?UTF-8?q?=20left=20border=20radius=20to=20the=20minified=20to=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Resources/views/Profiler/toolbar.css.twig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig index 0a269ea3f772f..c0456f61310fb 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -1,5 +1,6 @@ .sf-minitoolbar { background-color: #222; + border-top-left-radius: 4px; bottom: 0; display: none; height: 30px; @@ -8,6 +9,7 @@ right: 0; z-index: 99999; } + .sf-minitoolbar a { display: block; } @@ -357,6 +359,8 @@ /* Override the setting when the toolbar is on the top */ {% if position == 'top' %} .sf-minitoolbar { + border-bottom-left-radius: 4px; + border-top-left-radius: 0; bottom: auto; right: 0; top: 0; From f1fd7686c5fdc191a599d1e77d9748365f8a19cf Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 10 Nov 2015 16:18:07 +0100 Subject: [PATCH 031/107] fix potential timing attack issue --- ...PersistentTokenBasedRememberMeServices.php | 3 +- .../TokenBasedRememberMeServices.php | 28 ++----------------- 2 files changed, 4 insertions(+), 27 deletions(-) diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index f800668a5e2b7..0fffbfe4f992d 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -21,6 +21,7 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Util\SecureRandomInterface; use Psr\Log\LoggerInterface; +use Symfony\Component\Security\Core\Util\StringUtils; /** * Concrete implementation of the RememberMeServicesInterface which needs @@ -90,7 +91,7 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request) list($series, $tokenValue) = $cookieParts; $persistentToken = $this->tokenProvider->loadTokenBySeries($series); - if ($persistentToken->getTokenValue() !== $tokenValue) { + if (!StringUtils::equals($persistentToken->getTokenValue(), $tokenValue)) { throw new CookieTheftException('This token was already used. The account is possibly compromised.'); } diff --git a/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php index de662fb3d8b90..1aea5fd76d78d 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php @@ -17,6 +17,7 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\Util\StringUtils; /** * Concrete implementation of the RememberMeServicesInterface providing @@ -53,7 +54,7 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request) throw new \RuntimeException(sprintf('The UserProviderInterface implementation must return an instance of UserInterface, but returned "%s".', get_class($user))); } - if (true !== $this->compareHashes($hash, $this->generateCookieHash($class, $username, $expires, $user->getPassword()))) { + if (!StringUtils::equals($this->generateCookieHash($class, $username, $expires, $user->getPassword()), $hash)) { throw new AuthenticationException('The cookie\'s hash is invalid.'); } @@ -64,31 +65,6 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request) return $user; } - /** - * Compares two hashes using a constant-time algorithm to avoid (remote) - * timing attacks. - * - * This is the same implementation as used in the BasePasswordEncoder. - * - * @param string $hash1 The first hash - * @param string $hash2 The second hash - * - * @return bool true if the two hashes are the same, false otherwise - */ - private function compareHashes($hash1, $hash2) - { - if (strlen($hash1) !== $c = strlen($hash2)) { - return false; - } - - $result = 0; - for ($i = 0; $i < $c; ++$i) { - $result |= ord($hash1[$i]) ^ ord($hash2[$i]); - } - - return 0 === $result; - } - /** * {@inheritdoc} */ From 557ea17eeb21817f3c18147b56c86172794a75df Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 10 Nov 2015 16:41:37 +0100 Subject: [PATCH 032/107] mitigate CSRF timing attack vulnerability --- .../Csrf/CsrfProvider/DefaultCsrfProvider.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php b/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php index 31ea45f3d5261..ab62df9a04b6c 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php +++ b/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Form\Extension\Csrf\CsrfProvider; +use Symfony\Component\Security\Core\Util\StringUtils; + /** * Default implementation of CsrfProviderInterface. * @@ -54,6 +56,16 @@ public function generateCsrfToken($intention) */ public function isCsrfTokenValid($intention, $token) { + $expectedToken = $this->generateCsrfToken($intention); + + if (function_exists('hash_equals')) { + return hash_equals($expectedToken, $token); + } + + if (class_exists('Symfony\Component\Security\Core\Util\StringUtils')) { + return StringUtils::equals($expectedToken, $token); + } + return $token === $this->generateCsrfToken($intention); } From 819aa54fe489a403c0a3fe00fd0840ed53a15609 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 10 Nov 2015 16:51:36 +0100 Subject: [PATCH 033/107] prevent timing attacks in digest auth listener --- .../Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php | 2 +- .../Security/Http/Firewall/DigestAuthenticationListener.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php b/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php index ab62df9a04b6c..f323de0fd2d40 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php +++ b/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php @@ -66,7 +66,7 @@ public function isCsrfTokenValid($intention, $token) return StringUtils::equals($expectedToken, $token); } - return $token === $this->generateCsrfToken($intention); + return $token === $expectedToken; } /** diff --git a/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php index a88250bff386c..5e1159f733105 100644 --- a/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php @@ -13,6 +13,7 @@ use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; +use Symfony\Component\Security\Core\Util\StringUtils; use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint; use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; @@ -99,7 +100,7 @@ public function handle(GetResponseEvent $event) return; } - if ($serverDigestMd5 !== $digestAuth->getResponse()) { + if (!StringUtils::equals($serverDigestMd5, $digestAuth->getResponse())) { if (null !== $this->logger) { $this->logger->debug(sprintf('Expected response: "%s" but received: "%s"; is AuthenticationDao returning clear text passwords?', $serverDigestMd5, $digestAuth->getResponse())); } From f88e600833b6822db5873e25deaefd14948e4878 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 5 Nov 2015 23:29:27 +0100 Subject: [PATCH 034/107] migrate session after remember me authentication --- .../Http/Firewall/RememberMeListener.php | 8 +++ .../Http/Firewall/RememberMeListenerTest.php | 63 +++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php b/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php index 942e53787a1bc..52a231c70e104 100644 --- a/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php @@ -20,6 +20,7 @@ use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\SecurityEvents; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy; /** * RememberMeListener implements authentication capabilities via a cookie. @@ -33,6 +34,7 @@ class RememberMeListener implements ListenerInterface private $authenticationManager; private $logger; private $dispatcher; + private $sessionStrategy; /** * Constructor. @@ -50,6 +52,7 @@ public function __construct(SecurityContextInterface $securityContext, RememberM $this->authenticationManager = $authenticationManager; $this->logger = $logger; $this->dispatcher = $dispatcher; + $this->sessionStrategy = new SessionAuthenticationStrategy(SessionAuthenticationStrategy::MIGRATE); } /** @@ -70,6 +73,11 @@ public function handle(GetResponseEvent $event) try { $token = $this->authenticationManager->authenticate($token); + + if ($request->hasSession() && $request->getSession()->isStarted()) { + $this->sessionStrategy->onAuthentication($request, $token); + } + $this->securityContext->setToken($token); if (null !== $this->dispatcher) { diff --git a/src/Symfony/Component/Security/Tests/Http/Firewall/RememberMeListenerTest.php b/src/Symfony/Component/Security/Tests/Http/Firewall/RememberMeListenerTest.php index 067cacb6cbdad..ad96243d47905 100644 --- a/src/Symfony/Component/Security/Tests/Http/Firewall/RememberMeListenerTest.php +++ b/src/Symfony/Component/Security/Tests/Http/Firewall/RememberMeListenerTest.php @@ -138,6 +138,69 @@ public function testOnCoreSecurity() $listener->handle($event); } + public function testSessionStrategy() + { + list($listener, $tokenStorage, $service, $manager) = $this->getListener(false, true, true); + + $tokenStorage + ->expects($this->once()) + ->method('getToken') + ->will($this->returnValue(null)) + ; + + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $service + ->expects($this->once()) + ->method('autoLogin') + ->will($this->returnValue($token)) + ; + + $tokenStorage + ->expects($this->once()) + ->method('setToken') + ->with($this->equalTo($token)) + ; + + $manager + ->expects($this->once()) + ->method('authenticate') + ->will($this->returnValue($token)) + ; + + $session = $this->getMock('\Symfony\Component\HttpFoundation\Session\SessionInterface'); + $session + ->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(true)) + ; + $session + ->expects($this->once()) + ->method('migrate') + ; + + $request = $this->getMock('\Symfony\Component\HttpFoundation\Request'); + $request + ->expects($this->any()) + ->method('hasSession') + ->will($this->returnValue(true)) + ; + + $request + ->expects($this->any()) + ->method('getSession') + ->will($this->returnValue($session)) + ; + + $event = $this->getGetResponseEvent(); + $event + ->expects($this->once()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + + $listener->handle($event); + } + protected function getGetResponseEvent() { return $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); From d12cf1904c92ef2525b55da79ae09c800e875171 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 23 Nov 2015 11:17:36 +0100 Subject: [PATCH 035/107] fixed tests --- .../DependencyInjection/Tests/ContainerBuilderTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 86d9103d998df..d470ed386ff80 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -324,7 +324,7 @@ public function testCreateServiceMethodCallsWithEscapedParam() { $builder = new ContainerBuilder(); $builder->register('bar', 'stdClass'); - $builder->register('foo1', 'FooClass')->addMethodCall('setBar', array(array('%%unescape_it%%'))); + $builder->register('foo1', 'Bar\FooClass')->addMethodCall('setBar', array(array('%%unescape_it%%'))); $builder->setParameter('value', 'bar'); $this->assertEquals(array('%unescape_it%'), $builder->get('foo1')->bar, '->createService() replaces the values in the method calls arguments'); } @@ -333,7 +333,7 @@ public function testCreateServiceProperties() { $builder = new ContainerBuilder(); $builder->register('bar', 'stdClass'); - $builder->register('foo1', 'FooClass')->setProperty('bar', array('%value%', new Reference('bar'), '%%unescape_it%%')); + $builder->register('foo1', 'Bar\FooClass')->setProperty('bar', array('%value%', new Reference('bar'), '%%unescape_it%%')); $builder->setParameter('value', 'bar'); $this->assertEquals(array('bar', $builder->get('bar'), '%unescape_it%'), $builder->get('foo1')->bar, '->createService() replaces the values in the properties'); } From 3aee6b93e2072dd1c22a922eb30ad30dd8ade737 Mon Sep 17 00:00:00 2001 From: toretto460 Date: Mon, 23 Nov 2015 10:52:19 +0100 Subject: [PATCH 036/107] Fix: Resolve tempdir symlink, not working on OSX --- src/Symfony/Component/Filesystem/Tests/FilesystemTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index ea38e17df567e..d8d3cf33e94e4 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -1042,8 +1042,8 @@ public function testTempnamOnUnwritableFallsBackToSysTmp() $dirname = $scheme.$this->workspace.DIRECTORY_SEPARATOR.'does_not_exist'; $filename = $this->filesystem->tempnam($dirname, 'bar'); - - $this->assertStringStartsWith(rtrim($scheme.sys_get_temp_dir(), DIRECTORY_SEPARATOR), $filename); + $realTempDir = realpath(sys_get_temp_dir()); + $this->assertStringStartsWith(rtrim($scheme.$realTempDir, DIRECTORY_SEPARATOR), $filename); $this->assertFileExists($filename); // Tear down From 2dbb75aef49c39ac680a31f44514f94c29de681d Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 23 Nov 2015 11:39:33 +0100 Subject: [PATCH 037/107] removed usage of the deprecated StringUtils::equals() method --- .../Csrf/CsrfProvider/DefaultCsrfProvider.php | 12 +----------- .../Http/Firewall/DigestAuthenticationListener.php | 3 +-- .../PersistentTokenBasedRememberMeServices.php | 3 +-- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php b/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php index 7e25a122c6f74..def181d217ae6 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php +++ b/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php @@ -11,8 +11,6 @@ namespace Symfony\Component\Form\Extension\Csrf\CsrfProvider; -use Symfony\Component\Security\Core\Util\StringUtils; - @trigger_error('The '.__NAMESPACE__.'\DefaultCsrfProvider is deprecated since version 2.4 and will be removed in version 3.0. Use the \Symfony\Component\Security\Csrf\TokenStorage\NativeSessionTokenStorage class instead.', E_USER_DEPRECATED); /** @@ -65,15 +63,7 @@ public function isCsrfTokenValid($intention, $token) { $expectedToken = $this->generateCsrfToken($intention); - if (function_exists('hash_equals')) { - return hash_equals($expectedToken, $token); - } - - if (class_exists('Symfony\Component\Security\Core\Util\StringUtils')) { - return StringUtils::equals($expectedToken, $token); - } - - return $token === $expectedToken; + return hash_equals($expectedToken, $token); } /** diff --git a/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php index f2048fdbe41a9..41e5d6d759533 100644 --- a/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Security\Http\Firewall; use Symfony\Component\Security\Core\User\UserProviderInterface; -use Symfony\Component\Security\Core\Util\StringUtils; use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint; use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; @@ -100,7 +99,7 @@ public function handle(GetResponseEvent $event) return; } - if (!StringUtils::equals($serverDigestMd5, $digestAuth->getResponse())) { + if (!hash_equals($serverDigestMd5, $digestAuth->getResponse())) { if (null !== $this->logger) { $this->logger->debug('Unexpected response from the DigestAuth received; is the header returning a clear text passwords?', array('expected' => $serverDigestMd5, 'received' => $digestAuth->getResponse())); } diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index cf92a8c2f3f0e..807a4a72a69f4 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -21,7 +21,6 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Util\SecureRandomInterface; use Psr\Log\LoggerInterface; -use Symfony\Component\Security\Core\Util\StringUtils; /** * Concrete implementation of the RememberMeServicesInterface which needs @@ -94,7 +93,7 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request) list($series, $tokenValue) = $cookieParts; $persistentToken = $this->tokenProvider->loadTokenBySeries($series); - if (!StringUtils::equals($persistentToken->getTokenValue(), $tokenValue)) { + if (!hash_equals($persistentToken->getTokenValue(), $tokenValue)) { throw new CookieTheftException('This token was already used. The account is possibly compromised.'); } From 4e44295288affd971a553241a2a66a4cf431d2d6 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 23 Nov 2015 11:42:57 +0100 Subject: [PATCH 038/107] updated CHANGELOG for 2.3.35 --- CHANGELOG-2.3.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG-2.3.md b/CHANGELOG-2.3.md index 9c9d593aa08c7..4974aa08b3a2c 100644 --- a/CHANGELOG-2.3.md +++ b/CHANGELOG-2.3.md @@ -7,6 +7,25 @@ in 2.3 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.3.0...v2.3.1 +* 2.3.35 (2015-11-23) + + * security #16631 CVE-2015-8124: Session Fixation in the "Remember Me" Login Feature (xabbuh) + * security #16630 CVE-2015-8125: Potential Remote Timing Attack Vulnerability in Security Remember-Me Service (xabbuh) + * bug #16588 Sent out a status text for unknown HTTP headers. (dawehner) + * bug #16295 [DependencyInjection] Unescape parameters for all types of injection (Nicofuma) + * bug #16574 [Process] Fix PhpProcess with phpdbg runtime (nicolas-grekas) + * bug #16352 Fix the server variables in the router_*.php files (leofeyer) + * bug #16537 [Validator] Allow an empty path with a non empty fragment or a query (jakzal) + * bug #16528 [Translation] Add support for Armenian pluralization. (marcosdsanchez) + * bug #16510 [Process] fix Proccess run with pts enabled (ewgRa) + * bug #16292 fix race condition at mkdir (#16258) (ewgRa) + * bug #16462 [PropertyAccess] Fix dynamic property accessing. (dunglas) + * bug #16294 [PropertyAccess] Major performance improvement (dunglas) + * bug #16331 fixed Twig deprecation notices (fabpot) + * bug #16306 [DoctrineBridge] Fix issue which prevent the profiler to explain a query (Baachi) + * bug #16359 Use mb_detect_encoding with $strict = true (nicolas-grekas) + * bug #16144 [Security] don't allow to install the split Security packages (xabbuh) + * 2.3.34 (2015-10-27) * bug #16288 [Process] Inherit env vars by default in PhpProcess (nicolas-grekas) From 6836bc34b7e60da66014c8842718b5566abe36fa Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 23 Nov 2015 11:44:00 +0100 Subject: [PATCH 039/107] update CONTRIBUTORS for 2.3.35 --- CONTRIBUTORS.md | 52 +++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index b78fd1ff5ee94..9b30d35c1a0ae 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -51,19 +51,19 @@ Symfony is the result of the work of many people who made the code better - Florin Patan (florinpatan) - Eric Clemmons (ericclemmons) - Andrej Hudec (pulzarraider) - - Deni - Maxime Steinhausser (ogizanagi) + - Deni - Henrik Westphal (snc) - Dariusz Górecki (canni) - Gábor Egyed (1ed) - Christian Raue - Arnout Boks (aboks) - - Michel Weimerskirch (mweimerskirch) - Kevin Bond (kbond) + - Michel Weimerskirch (mweimerskirch) + - Douglas Greenshields (shieldo) - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) - - Douglas Greenshields (shieldo) - Daniel Holmes (dholmes) - Bart van den Burg (burgov) - Jordan Alliot (jalliot) @@ -85,15 +85,17 @@ Symfony is the result of the work of many people who made the code better - Adrien Brault (adrienbrault) - excelwebzone - Jacob Dreesen (jdreesen) + - Michal Piotrowski (eventhorizon) + - Peter Kokot (maastermedia) - Fabien Pennequin (fabienpennequin) - Peter Rehm (rpet) - - Peter Kokot (maastermedia) + - Pierre du Plessis (pierredup) - Alexander Schwenn (xelaris) - Gordon Franke (gimler) - Robert Schönthal (digitalkaoz) - Jérémy DERUSSÉ (jderusse) - Dariusz Ruminski - - Michal Piotrowski (eventhorizon) + - Joshua Thijssen - Stefano Sala (stefano.sala) - David Buchmann (dbu) - Issei Murasawa (issei_m) @@ -103,7 +105,6 @@ Symfony is the result of the work of many people who made the code better - Sebastian Hörl (blogsh) - Daniel Gomes (danielcsgomes) - Hidenori Goto (hidenorigoto) - - Joshua Thijssen - Guilherme Blanco (guilhermeblanco) - Pablo Godel (pgodel) - Vladimir Reznichenko (kalessil) @@ -116,6 +117,7 @@ Symfony is the result of the work of many people who made the code better - Clemens Tolboom - Helmer Aaviksoo - Baptiste Clavié (talus) + - Tugdual Saunier (tucksaun) - Hiromi Hishida (77web) - Matthieu Ouellette-Vachon (maoueh) - Michał Pipa (michal.pipa) @@ -124,8 +126,7 @@ Symfony is the result of the work of many people who made the code better - Artur Kotyrba - Rouven Weßling (realityking) - Charles Sarrazin (csarrazi) - - Pierre du Plessis (pierredup) - - Tugdual Saunier (tucksaun) + - Warnar Boekkooi (boekkooi) - Andréia Bohner (andreia) - Dmitrii Chekaliuk (lazyhammer) - Clément JOBEILI (dator) @@ -159,15 +160,17 @@ Symfony is the result of the work of many people who made the code better - Justin Hileman (bobthecow) - Michele Orselli (orso) - Sven Paulus (subsven) - - Warnar Boekkooi (boekkooi) - Lars Strojny (lstrojny) + - Daniel Wehner - Rui Marinho (ruimarinho) + - Evgeniy (ewgraf) - Julien Brochet (mewt) - Sergey Linnik (linniksa) - Jáchym Toušek - Marcel Beerta (mazen) - Vincent AUBERT (vincent) - julien pauli (jpauli) + - Florian Lonqueu-Brochard (florianlb) - Francois Zaninotto - Alexander Kotynia (olden) - Daniel Tschinder @@ -185,6 +188,7 @@ Symfony is the result of the work of many people who made the code better - Eugene Leonovich (rybakit) - Joseph Rouff (rouffj) - Félix Labrecque (woodspire) + - Tomáš Votruba (tomas_votruba) - GordonsLondon - Jan Sorgalla (jsor) - Ray @@ -201,17 +205,19 @@ Symfony is the result of the work of many people who made the code better - Beau Simensen (simensen) - Robert Kiss (kepten) - Ruben Gonzalez (rubenrua) + - Marcos Sánchez - Kim Hemsø Rasmussen (kimhemsoe) - Diego Saint Esteben (dosten) - - Florian Lonqueu-Brochard (florianlb) - Tom Van Looy (tvlooy) - Wouter Van Hecke - Peter Kruithof (pkruithof) - Michael Holm (hollo) - Marc Weistroff (futurecat) + - Blanchon Vincent (blanchonvincent) - Dawid Nowak - Kristen Gilden (kgilden) - Chris Smith (cs278) + - Richard van Laak (rvanlaak) - Florian Klein (docteurklein) - Manuel Kiessling (manuelkiessling) - Daniel Wehner @@ -245,7 +251,7 @@ Symfony is the result of the work of many people who made the code better - Giorgio Premi - Erin Millard - Matthew Lewinski (lewinski) - - Marcos Sánchez + - Antonio J. García Lagar (ajgarlag) - alquerci - Francesco Levorato - Vitaliy Zakharov (zakharovvi) @@ -255,14 +261,11 @@ Symfony is the result of the work of many people who made the code better - Christian Gärtner (dagardner) - Tomasz Kowalczyk (thunderer) - François-Xavier de Guillebon (de-gui_f) - - Daniel Wehner - Felix Labrecque - Yaroslav Kiliba - Stepan Anchugov (kix) - Terje Bråten - - Evgeniy (ewgraf) - Robbert Klarenbeek (robbertkl) - - Blanchon Vincent (blanchonvincent) - hossein zolfi (ocean) - Clément Gautier (clementgautier) - Eduardo Gulias (egulias) @@ -285,6 +288,7 @@ Symfony is the result of the work of many people who made the code better - Shein Alexey - Joe Lencioni - Daniel Tschinder + - Diego Agulló (aeoris) - Kai - Lee Rowlands - Maximilian Reichel (phramz) @@ -313,8 +317,9 @@ Symfony is the result of the work of many people who made the code better - Thomas Lallement (raziel057) - Jan Schumann - Niklas Fiekas + - Mark Challoner (markchalloner) + - Markus Bachmann (baachi) - lancergr - - Antonio J. García Lagar (ajgarlag) - Olivier Dolbeau (odolbeau) - Roumen Damianoff (roumen) - vagrant @@ -365,9 +370,10 @@ Symfony is the result of the work of many people who made the code better - Daniel Beyer - Andrew Udvare (audvare) - alexpods - - Richard van Laak (rvanlaak) + - Tristan Darricau (nicofuma) - Erik Trapman (eriktrapman) - De Cock Xavier (xdecock) + - Possum - Scott Arciszewski - Norbert Orzechowicz (norzechowicz) - Matthijs van den Bos (matthijs) @@ -381,6 +387,7 @@ Symfony is the result of the work of many people who made the code better - Dawid Pakuła (zulusx) - Florian Rey (nervo) - Rodrigo Borrego Bernabé (rodrigobb) + - Leo Feyer - MatTheCat - John Bafford (jbafford) - Denis Gorbachev (starfall) @@ -410,7 +417,6 @@ Symfony is the result of the work of many people who made the code better - Christopher Davis (chrisguitarguy) - alcaeus - vitaliytv - - Markus Bachmann (baachi) - Sebastian Blum - aubx - Ricky Su (ricky) @@ -475,6 +481,7 @@ Symfony is the result of the work of many people who made the code better - Tiago Brito (blackmx) - Richard van den Brand (ricbra) - develop + - Hidde Wieringa - Mark Sonnabaum - Alexander Obuhovich (aik099) - jochenvdv @@ -496,7 +503,6 @@ Symfony is the result of the work of many people who made the code better - avorobiev - Venu - Lars Vierbergen - - Mark Challoner - Dennis Hotson - Andrew Tchircoff (andrewtch) - michaelwilliams @@ -534,7 +540,6 @@ Symfony is the result of the work of many people who made the code better - Rafał Wrzeszcz (rafalwrzeszcz) - Reen Lokum - Martin Parsiegla (spea) - - Possum - Denis Charrier (brucewouaigne) - Quentin Schuler - Pierre Vanliefland (pvanliefland) @@ -558,12 +563,10 @@ Symfony is the result of the work of many people who made the code better - Patrick Allaert - Gustavo Falco (gfalco) - Matt Robinson (inanimatt) - - Tristan Darricau (nicofuma) - Aleksey Podskrebyshev - Steffen Roßkamp - David Marín Carreño (davefx) - Jörn Lang (j.lang) - - Leo Feyer - mwsaz - Benoît Bourgeois - corphi @@ -589,7 +592,6 @@ Symfony is the result of the work of many people who made the code better - Balazs Csaba (balazscsaba2006) - Harry Walter (haswalt) - Johnson Page (jwpage) - - Tomáš Votruba (tomas_votruba) - Michael Roterman (wtfzdotnet) - Arno Geurts - Adán Lobato (adanlobato) @@ -638,6 +640,7 @@ Symfony is the result of the work of many people who made the code better - Jérôme Vasseur - Sander Coolen (scoolen) - Nicolas Le Goff (nlegoff) + - Anne-Sophie Bachelard (annesophie) - Manuele Menozzi - Anton Babenko (antonbabenko) - Irmantas Šiupšinskas (irmantas) @@ -921,7 +924,6 @@ Symfony is the result of the work of many people who made the code better - Shane Preece (shane) - wusuopu - povilas - - Diego Agulló - Alessandro Tagliapietra (alex88) - Biji (biji) - Gunnar Lium (gunnarlium) @@ -1082,7 +1084,6 @@ Symfony is the result of the work of many people who made the code better - grifx - Robert Campbell - Matt Lehner - - Hidde Wieringa - Hein Zaw Htet™ - Ruben Kruiswijk - Michael J @@ -1106,7 +1107,6 @@ Symfony is the result of the work of many people who made the code better - Alan Chen - Maerlyn - Even André Fiskvik - - Diego Agulló - Dane Powell - Gerrit Drost - Lenar Lõhmus @@ -1219,6 +1219,7 @@ Symfony is the result of the work of many people who made the code better - Sam Williams - Adrian Philipp - James Michael DuPont + - Eugene Wissner - Kasperki - Tammy D - Ondrej Slinták @@ -1276,6 +1277,7 @@ Symfony is the result of the work of many people who made the code better - arduanov - sualko - Nicolas Roudaire + - Jérôme Vasseur - Alfonso (afgar) - Andreas Forsblom (aforsblo) - Alex Olmos (alexolmos) From 3b790dd0da995bd2c3da495e5ed2ee0a8f723d29 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 23 Nov 2015 11:44:06 +0100 Subject: [PATCH 040/107] updated VERSION for 2.3.35 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index d0defb1c9cfc7..be3ea8d1ee900 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -58,12 +58,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.3.35-DEV'; + const VERSION = '2.3.35'; const VERSION_ID = 20335; const MAJOR_VERSION = 2; const MINOR_VERSION = 3; const RELEASE_VERSION = 35; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; /** * Constructor. From 03eb2e909d1e1f86f1785c308cdc2eadb8c86930 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 23 Nov 2015 12:27:40 +0100 Subject: [PATCH 041/107] bumped Symfony version to 2.3.36 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index be3ea8d1ee900..ff5b5906b3a80 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -58,12 +58,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.3.35'; - const VERSION_ID = 20335; + const VERSION = '2.3.36-DEV'; + const VERSION_ID = 20336; const MAJOR_VERSION = 2; const MINOR_VERSION = 3; - const RELEASE_VERSION = 35; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = 36; + const EXTRA_VERSION = 'DEV'; /** * Constructor. From d386f9c302ae876d0e733655383656bca996cc47 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 23 Nov 2015 12:57:09 +0100 Subject: [PATCH 042/107] updated CHANGELOG for 2.7.7 --- CHANGELOG-2.7.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/CHANGELOG-2.7.md b/CHANGELOG-2.7.md index 05bf4d856d835..19e9036c82bb2 100644 --- a/CHANGELOG-2.7.md +++ b/CHANGELOG-2.7.md @@ -7,6 +7,34 @@ in 2.7 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.7.0...v2.7.1 +* 2.7.7 (2015-11-23) + + * security #16631 CVE-2015-8124: Session Fixation in the "Remember Me" Login Feature (xabbuh) + * security #16630 CVE-2015-8125: Potential Remote Timing Attack Vulnerability in Security Remember-Me Service (xabbuh) + * bug #16588 Sent out a status text for unknown HTTP headers. (dawehner) + * bug #16295 [DependencyInjection] Unescape parameters for all types of injection (Nicofuma) + * bug #16574 [Process] Fix PhpProcess with phpdbg runtime (nicolas-grekas) + * bug #16578 [Console] Fix bug in windows detection (kbond) + * bug #16546 [Serializer] ObjectNormalizer: don't serialize static methods and props (dunglas) + * bug #16352 Fix the server variables in the router_*.php files (leofeyer) + * bug #16537 [Validator] Allow an empty path with a non empty fragment or a query (jakzal) + * bug #16528 [Translation] Add support for Armenian pluralization. (marcosdsanchez) + * bug #16510 [Process] fix Proccess run with pts enabled (ewgRa) + * bug #16292 fix race condition at mkdir (#16258) (ewgRa) + * bug #15945 [Form] trigger deprecation warning when using empty_value (xabbuh) + * bug #16384 [FrameworkBundle] JsonDescriptor - encode container params only once (xabbuh) + * bug #16480 [VarDumper] Fix PHP7 type-hints compat (nicolas-grekas) + * bug #16463 [PropertyAccess] Port of the performance optimization from 2.3 (dunglas) + * bug #16462 [PropertyAccess] Fix dynamic property accessing. (dunglas) + * bug #16454 [Serializer] GetSetNormalizer shouldn't set/get static methods (boekkooi) + * bug #16453 [Serializer] PropertyNormalizer shouldn't set static properties (boekkooi) + * bug #16471 [VarDumper] Fix casting for ReflectionParameter (nicolas-grekas) + * bug #16294 [PropertyAccess] Major performance improvement (dunglas) + * bug #16331 fixed Twig deprecation notices (fabpot) + * bug #16306 [DoctrineBridge] Fix issue which prevent the profiler to explain a query (Baachi) + * bug #16359 Use mb_detect_encoding with $strict = true (nicolas-grekas) + * bug #16144 [Security] don't allow to install the split Security packages (xabbuh) + * 2.7.6 (2015-10-27) * bug #16338 [VarDumper] Fix anonymous class dumping (nicolas-grekas) From d99f2410b34f670c7e572b0e8a10028c37848dab Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 23 Nov 2015 12:57:49 +0100 Subject: [PATCH 043/107] updated VERSION for 2.7.7 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 2e7666d83d4e2..c29f9ac93d8fd 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -58,12 +58,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.7.7-DEV'; + const VERSION = '2.7.7'; const VERSION_ID = 20707; const MAJOR_VERSION = 2; const MINOR_VERSION = 7; const RELEASE_VERSION = 7; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '05/2018'; const END_OF_LIFE = '05/2019'; From fc0ee0efd5d0369feeae7ff5a9cc5eebb586ba50 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 23 Nov 2015 13:27:27 +0100 Subject: [PATCH 044/107] bumped Symfony version to 2.7.8 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index c29f9ac93d8fd..18425bee1b641 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -58,12 +58,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.7.7'; - const VERSION_ID = 20707; + const VERSION = '2.7.8-DEV'; + const VERSION_ID = 20708; const MAJOR_VERSION = 2; const MINOR_VERSION = 7; - const RELEASE_VERSION = 7; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = 8; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '05/2018'; const END_OF_LIFE = '05/2019'; From 07d627de0f9f9838d8896ab0243f80e4d3d6f966 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 23 Nov 2015 12:07:13 -0500 Subject: [PATCH 045/107] Tweaking deprecation message to include class name --- .../Component/Config/Resource/BCResourceInterfaceChecker.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Config/Resource/BCResourceInterfaceChecker.php b/src/Symfony/Component/Config/Resource/BCResourceInterfaceChecker.php index 631755e8fe926..565ff8bb50e8b 100644 --- a/src/Symfony/Component/Config/Resource/BCResourceInterfaceChecker.php +++ b/src/Symfony/Component/Config/Resource/BCResourceInterfaceChecker.php @@ -29,7 +29,7 @@ public function supports(ResourceInterface $metadata) public function isFresh(ResourceInterface $resource, $timestamp) { - @trigger_error('Resource checking through ResourceInterface::isFresh() is deprecated since 2.8 and will be removed in 3.0', E_USER_DEPRECATED); + @trigger_error(sprintf('The class "%s" is performing resource checking through ResourceInterface::isFresh(), which is deprecated since 2.8 and will be removed in 3.0', get_class($resource)), E_USER_DEPRECATED); return parent::isFresh($resource, $timestamp); // For now, $metadata features the isFresh() method, so off we go (quack quack) } From dfda5ced7c83004d62ac50f2557e1b39aa5395e6 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 24 Nov 2015 08:24:59 +0100 Subject: [PATCH 046/107] [travis] Disable xdebug on PHP7 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 62754a68af3a8..c347943fb48a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,7 +36,7 @@ before_install: - echo "memory_limit = -1" >> $INI_FILE - echo "session.gc_probability = 0" >> $INI_FILE - if [ "$deps" != "skip" ]; then composer self-update; fi; - - if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then phpenv config-rm xdebug.ini; fi; + - if [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then phpenv config-rm xdebug.ini; fi; - if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then echo "extension = mongo.so" >> $INI_FILE; fi; - if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then echo "extension = memcache.so" >> $INI_FILE; fi; - if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then (echo yes | pecl install -f apcu-4.0.7 && echo "apc.enable_cli = 1" >> $INI_FILE) || echo "Let's continue without apcu extension"; fi; From a21a0166c692d1cbb71a9aa0c68f68b6a20215f1 Mon Sep 17 00:00:00 2001 From: Abdellatif Ait boudad Date: Mon, 23 Nov 2015 22:18:49 +0000 Subject: [PATCH 047/107] [DI][autowiring] throw exception when many services use the same class. --- .../Compiler/AutowirePass.php | 2 +- .../Tests/Compiler/AutowirePassTest.php | 62 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index 81470a8398eed..ec7880be89b95 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -227,7 +227,7 @@ private function set($type, $id) */ private function createAutowiredDefinition(\ReflectionClass $typeHint, $id) { - if (!$typeHint->isInstantiable()) { + if (isset($this->notGuessableTypes[$typeHint->name]) || !$typeHint->isInstantiable()) { throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s".', $typeHint->name, $id)); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index cc29ea93e02f2..f1ed72e94a4cd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -117,6 +117,56 @@ public function testTypeCollision() $pass->process($container); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" for the service "a". + */ + public function testTypeNotGuessable() + { + $container = new ContainerBuilder(); + + $container->register('a1', __NAMESPACE__.'\Foo'); + $container->register('a2', __NAMESPACE__.'\Foo'); + $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgument'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\A" for the service "a". + */ + public function testTypeNotGuessableWithSubclass() + { + $container = new ContainerBuilder(); + + $container->register('a1', __NAMESPACE__.'\B'); + $container->register('a2', __NAMESPACE__.'\B'); + $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgumentForSubclass'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + + public function testTypeNotGuessableWithTypeSet() + { + $container = new ContainerBuilder(); + + $container->register('a1', __NAMESPACE__.'\Foo'); + $container->register('a2', __NAMESPACE__.'\Foo')->addAutowiringType(__NAMESPACE__.'\Foo'); + $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgument'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(1, $container->getDefinition('a')->getArguments()); + $this->assertEquals('a2', (string) $container->getDefinition('a')->getArgument(0)); + } + public function testWithTypeSet() { $container = new ContainerBuilder(); @@ -335,3 +385,15 @@ public function __construct(Dunglas $k, NotARealClass $r) { } } +class NotGuessableArgument +{ + public function __construct(Foo $k) + { + } +} +class NotGuessableArgumentForSubclass +{ + public function __construct(A $k) + { + } +} From fdfa1767b916bf48bcbc162b25af7d3d8ad29edc Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 24 Nov 2015 10:07:02 +0100 Subject: [PATCH 048/107] Minor design tweaks for the Logs and Doctrine profiler panels --- .../Resources/views/Profiler/profiler.css.twig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig index 2f057958ca473..0e0ffe52ddf59 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig @@ -808,6 +808,16 @@ table.logs .metadata strong { color: #222; } +table.logs .sf-call-stack { + margin: 1em 0 1em 1.5em; +} +table.logs .sf-call-stack li { + margin-bottom: 5px; +} +table.logs .sf-call-stack abbr { + border: none; +} + {# Doctrine panel ========================================================================= #} .sql-runnable { @@ -815,6 +825,11 @@ table.logs .metadata strong { margin: .5em 0; padding: 1em; } +.queries-table pre { + {{ mixins.break_long_words|raw }} + margin: 0; + white-space: pre-wrap; +} {# Dump panel ========================================================================= #} From 01c08fccf4853fe204b620266f8551de18b31c50 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 24 Nov 2015 12:55:28 +0100 Subject: [PATCH 049/107] [Debug] Ensure class declarations are loaded only once --- src/Symfony/Component/Debug/DebugClassLoader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php index 8c797744d7b05..95ccc8798e8c1 100644 --- a/src/Symfony/Component/Debug/DebugClassLoader.php +++ b/src/Symfony/Component/Debug/DebugClassLoader.php @@ -147,7 +147,7 @@ public function loadClass($class) try { if ($this->isFinder) { if ($file = $this->classLoader[0]->findFile($class)) { - require $file; + require_once $file; } } else { call_user_func($this->classLoader, $class); From d3c6d93dff9761cacc811976656574985fb37009 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 24 Nov 2015 14:06:05 +0100 Subject: [PATCH 050/107] [Security] Revert changes made between 2.7 and 2.8-beta --- UPGRADE-2.8.md | 34 +------ .../Authorization/Voter/AbstractVoter.php | 89 +++---------------- .../Authorization/Voter/AbstractVoterTest.php | 74 +-------------- .../Authorization/Voter/Fixtures/MyVoter.php | 27 ++++++ 4 files changed, 43 insertions(+), 181 deletions(-) create mode 100644 src/Symfony/Component/Security/Core/Tests/Authorization/Voter/Fixtures/MyVoter.php diff --git a/UPGRADE-2.8.md b/UPGRADE-2.8.md index ae8bb7fa4ac98..ec4c4ba44719f 100644 --- a/UPGRADE-2.8.md +++ b/UPGRADE-2.8.md @@ -442,38 +442,8 @@ FrameworkBundle Security -------- - * The AbstractToken::isGranted() method was deprecated. Instead, - override the voteOnAttribute() method. This method has one small - difference: it's passed the TokenInterface instead of the user: - - Before: - - ```php - class MyCustomVoter extends AbstractVoter - { - // ... - - protected function isGranted($attribute, $object, $user = null) - { - // ... - } - } - ``` - - After: - - ```php - class MyCustomVoter extends AbstractVoter - { - // ... - - protected function voteOnAttribute($attribute, $object, TokenInterface $token) - { - $user = $token->getUser(); - // ... - } - } - ``` + * The `VoterInterface::supportsClass` and `supportsAttribute` methods were + deprecated and will be removed from the interface in 3.0. Config ------ diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php index 7b04222eae921..5dcf787c9968c 100644 --- a/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/AbstractVoter.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Security\Core\Authorization\Voter; +@trigger_error('The '.__NAMESPACE__.'\AbstractVoter class is deprecated since version 2.8, to be removed in 3.0. Upgrade to Symfony\Component\Security\Core\Authorization\Voter\Voter instead.', E_USER_DEPRECATED); + use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; @@ -18,6 +20,8 @@ * Abstract Voter implementation that reduces boilerplate code required to create a custom Voter. * * @author Roman Marintšenko + * + * @deprecated since version 2.8, to be removed in 3.0. Upgrade to Symfony\Component\Security\Core\Authorization\Voter\Voter instead. */ abstract class AbstractVoter implements VoterInterface { @@ -26,8 +30,6 @@ abstract class AbstractVoter implements VoterInterface */ public function supportsAttribute($attribute) { - @trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.', E_USER_DEPRECATED); - return in_array($attribute, $this->getSupportedAttributes()); } @@ -36,8 +38,6 @@ public function supportsAttribute($attribute) */ public function supportsClass($class) { - @trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.', E_USER_DEPRECATED); - foreach ($this->getSupportedClasses() as $supportedClass) { if ($supportedClass === $class || is_subclass_of($class, $supportedClass)) { return true; @@ -62,7 +62,7 @@ public function supportsClass($class) */ public function vote(TokenInterface $token, $object, array $attributes) { - if (!$object) { + if (!$object || !$this->supportsClass(get_class($object))) { return self::ACCESS_ABSTAIN; } @@ -70,14 +70,14 @@ public function vote(TokenInterface $token, $object, array $attributes) $vote = self::ACCESS_ABSTAIN; foreach ($attributes as $attribute) { - if (!$this->supports($attribute, $object)) { + if (!$this->supportsAttribute($attribute)) { continue; } // as soon as at least one attribute is supported, default is to deny access $vote = self::ACCESS_DENIED; - if ($this->voteOnAttribute($attribute, $object, $token)) { + if ($this->isGranted($attribute, $object, $token->getUser())) { // grant access as soon as at least one voter returns a positive response return self::ACCESS_GRANTED; } @@ -86,62 +86,19 @@ public function vote(TokenInterface $token, $object, array $attributes) return $vote; } - /** - * Determines if the attribute and object are supported by this voter. - * - * This method will become abstract in 3.0. - * - * @param string $attribute An attribute - * @param string $object The object to secure - * - * @return bool True if the attribute and object is supported, false otherwise - */ - protected function supports($attribute, $object) - { - @trigger_error('The getSupportedClasses and getSupportedAttributes methods are deprecated since version 2.8 and will be removed in version 3.0. Overwrite supports instead.', E_USER_DEPRECATED); - - $classIsSupported = false; - foreach ($this->getSupportedClasses() as $supportedClass) { - if ($object instanceof $supportedClass) { - $classIsSupported = true; - break; - } - } - - if (!$classIsSupported) { - return false; - } - - if (!in_array($attribute, $this->getSupportedAttributes())) { - return false; - } - - return true; - } - /** * Return an array of supported classes. This will be called by supportsClass. * * @return array an array of supported classes, i.e. array('Acme\DemoBundle\Model\Product') - * - * @deprecated since version 2.8, to be removed in 3.0. Use supports() instead. */ - protected function getSupportedClasses() - { - @trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.', E_USER_DEPRECATED); - } + abstract protected function getSupportedClasses(); /** * Return an array of supported attributes. This will be called by supportsAttribute. * * @return array an array of supported attributes, i.e. array('CREATE', 'READ') - * - * @deprecated since version 2.8, to be removed in 3.0. Use supports() instead. */ - protected function getSupportedAttributes() - { - @trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.', E_USER_DEPRECATED); - } + abstract protected function getSupportedAttributes(); /** * Perform a single access check operation on a given attribute, object and (optionally) user @@ -154,33 +111,7 @@ protected function getSupportedAttributes() * @param object $object * @param UserInterface|string $user * - * @deprecated This method will be removed in 3.0 - override voteOnAttribute instead. - * - * @return bool - */ - protected function isGranted($attribute, $object, $user = null) - { - // forces isGranted() or voteOnAttribute() to be overridden - throw new \BadMethodCallException(sprintf('You must override the voteOnAttribute() method in "%s".', get_class($this))); - } - - /** - * Perform a single access check operation on a given attribute, object and token. - * It is safe to assume that $attribute and $object's class pass supports method call. - * - * This method will become abstract in 3.0. - * - * @param string $attribute - * @param object $object - * @param TokenInterface $token - * * @return bool */ - protected function voteOnAttribute($attribute, $object, TokenInterface $token) - { - // the user should override this method, and not rely on the deprecated isGranted() - @trigger_error(sprintf("The AbstractVoter::isGranted() method is deprecated since 2.8 and won't be called anymore in 3.0. Override voteOnAttribute() in %s instead.", get_class($this)), E_USER_DEPRECATED); - - return $this->isGranted($attribute, $object, $token->getUser()); - } + abstract protected function isGranted($attribute, $object, $user = null); } diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/AbstractVoterTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/AbstractVoterTest.php index 5ea77320cf610..b537c1b2effc9 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/AbstractVoterTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/AbstractVoterTest.php @@ -11,10 +11,11 @@ namespace Symfony\Component\Security\Core\Tests\Authorization\Voter; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; +/** + * @group legacy + */ class AbstractVoterTest extends \PHPUnit_Framework_TestCase { protected $token; @@ -50,75 +51,8 @@ public function getTests() */ public function testVote(array $attributes, $expectedVote, $object, $message) { - $voter = new AbstractVoterTest_Voter(); + $voter = new Fixtures\MyVoter(); $this->assertEquals($expectedVote, $voter->vote($this->token, $object, $attributes), $message); } - - /** - * @dataProvider getTests - * @group legacy - */ - public function testVoteLegacy(array $attributes, $expectedVote, $object, $message) - { - $voter = new AbstractVoterTest_LegacyVoter(); - - $this->assertEquals($expectedVote, $voter->vote($this->token, $object, $attributes), $message); - } - - /** - * @group legacy - * @expectedException \BadMethodCallException - */ - public function testNoOverriddenMethodsThrowsException() - { - $voter = new AbstractVoterTest_NothingImplementedVoter(); - $voter->vote($this->token, new \stdClass(), array('EDIT')); - } -} - -class AbstractVoterTest_Voter extends AbstractVoter -{ - protected function voteOnAttribute($attribute, $object, TokenInterface $token) - { - return 'EDIT' === $attribute; - } - - protected function supports($attribute, $object) - { - return $object instanceof \stdClass && in_array($attribute, array('EDIT', 'CREATE')); - } -} - -class AbstractVoterTest_LegacyVoter extends AbstractVoter -{ - protected function getSupportedClasses() - { - return array('stdClass'); - } - - protected function getSupportedAttributes() - { - return array('EDIT', 'CREATE'); - } - - protected function isGranted($attribute, $object, $user = null) - { - return 'EDIT' === $attribute; - } -} - -class AbstractVoterTest_NothingImplementedVoter extends AbstractVoter -{ - protected function getSupportedClasses() - { - return array('stdClass'); - } - - protected function getSupportedAttributes() - { - return array('EDIT', 'CREATE'); - } - - // this is a bad voter that hasn't overridden isGranted or voteOnAttribute } diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/Fixtures/MyVoter.php b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/Fixtures/MyVoter.php new file mode 100644 index 0000000000000..b75f79851be99 --- /dev/null +++ b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/Fixtures/MyVoter.php @@ -0,0 +1,27 @@ + Date: Thu, 19 Nov 2015 15:20:40 +0100 Subject: [PATCH 051/107] [Security] Deprecate "AbstractVoter" in favor of "Voter" --- UPGRADE-2.8.md | 4 + .../Core/Authorization/Voter/Voter.php | 85 +++++++++++++++++++ .../Tests/Authorization/Voter/VoterTest.php | 70 +++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 src/Symfony/Component/Security/Core/Authorization/Voter/Voter.php create mode 100644 src/Symfony/Component/Security/Core/Tests/Authorization/Voter/VoterTest.php diff --git a/UPGRADE-2.8.md b/UPGRADE-2.8.md index ec4c4ba44719f..ba7a13617af3f 100644 --- a/UPGRADE-2.8.md +++ b/UPGRADE-2.8.md @@ -442,6 +442,10 @@ FrameworkBundle Security -------- + * The `AbstractVoter` class was deprecated. Instead, extend the `Voter` class and + move your voting logic in the `supports($attribute, $subject)` and + `voteOnAttribute($attribute, $object, TokenInterface $token)` methods. + * The `VoterInterface::supportsClass` and `supportsAttribute` methods were deprecated and will be removed from the interface in 3.0. diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/Voter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/Voter.php new file mode 100644 index 0000000000000..8d36fd8f8c919 --- /dev/null +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/Voter.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Authorization\Voter; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * Voter is an abstract default implementation of a voter. + * + * @author Roman Marintšenko + * @author Grégoire Pineau + */ +abstract class Voter implements VoterInterface +{ + /** + * {@inheritdoc} + */ + public function supportsAttribute($attribute) + { + throw new \BadMethodCallException('supportsAttribute method is deprecated since version 2.8, to be removed in 3.0'); + } + + /** + * {@inheritdoc} + */ + public function supportsClass($class) + { + throw new \BadMethodCallException('supportsClass method is deprecated since version 2.8, to be removed in 3.0'); + } + + /** + * {@inheritdoc} + */ + public function vote(TokenInterface $token, $object, array $attributes) + { + // abstain vote by default in case none of the attributes are supported + $vote = self::ACCESS_ABSTAIN; + + foreach ($attributes as $attribute) { + if (!$this->supports($attribute, $object)) { + continue; + } + + // as soon as at least one attribute is supported, default is to deny access + $vote = self::ACCESS_DENIED; + + if ($this->voteOnAttribute($attribute, $object, $token)) { + // grant access as soon as at least one attribute returns a positive response + return self::ACCESS_GRANTED; + } + } + + return $vote; + } + + /** + * Determines if the attribute and subject are supported by this voter. + * + * @param string $attribute An attribute + * @param mixed $subject The subject to secure, e.g. an object the user wants to access or any other PHP type + * + * @return bool True if the attribute and subject are supported, false otherwise + */ + abstract protected function supports($attribute, $subject); + + /** + * Perform a single access check operation on a given attribute, subject and token. + * + * @param string $attribute + * @param mixed $subject + * @param TokenInterface $token + * + * @return bool + */ + abstract protected function voteOnAttribute($attribute, $subject, TokenInterface $token); +} diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/VoterTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/VoterTest.php new file mode 100644 index 0000000000000..4bac44d981893 --- /dev/null +++ b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/VoterTest.php @@ -0,0 +1,70 @@ + + * + * 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\Authorization\Voter; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; +use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; + +class VoterTest extends \PHPUnit_Framework_TestCase +{ + protected $token; + + protected function setUp() + { + $this->token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + } + + public function getTests() + { + return array( + array(array('EDIT'), VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if attribute and class are supported and attribute grants access'), + array(array('CREATE'), VoterInterface::ACCESS_DENIED, new \stdClass(), 'ACCESS_DENIED if attribute and class are supported and attribute does not grant access'), + + array(array('DELETE', 'EDIT'), VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if one attribute is supported and grants access'), + array(array('DELETE', 'CREATE'), VoterInterface::ACCESS_DENIED, new \stdClass(), 'ACCESS_DENIED if one attribute is supported and denies access'), + + array(array('CREATE', 'EDIT'), VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if one attribute grants access'), + + array(array('DELETE'), VoterInterface::ACCESS_ABSTAIN, new \stdClass(), 'ACCESS_ABSTAIN if no attribute is supported'), + + array(array('EDIT'), VoterInterface::ACCESS_ABSTAIN, $this, 'ACCESS_ABSTAIN if class is not supported'), + + array(array('EDIT'), VoterInterface::ACCESS_ABSTAIN, null, 'ACCESS_ABSTAIN if object is null'), + + array(array(), VoterInterface::ACCESS_ABSTAIN, new \stdClass(), 'ACCESS_ABSTAIN if no attributes were provided'), + ); + } + + /** + * @dataProvider getTests + */ + public function testVote(array $attributes, $expectedVote, $object, $message) + { + $voter = new VoterTest_Voter(); + + $this->assertEquals($expectedVote, $voter->vote($this->token, $object, $attributes), $message); + } +} + +class VoterTest_Voter extends Voter +{ + protected function voteOnAttribute($attribute, $object, TokenInterface $token) + { + return 'EDIT' === $attribute; + } + + protected function supports($attribute, $object) + { + return $object instanceof \stdClass && in_array($attribute, array('EDIT', 'CREATE')); + } +} From 062d707820de675378fa887640b1840f0b298ea8 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 24 Nov 2015 21:31:58 +0100 Subject: [PATCH 052/107] [Yaml] sync changelog and upgrade files --- UPGRADE-2.8.md | 8 ++++++++ UPGRADE-3.0.md | 5 +++++ src/Symfony/Component/Yaml/CHANGELOG.md | 3 ++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/UPGRADE-2.8.md b/UPGRADE-2.8.md index ae8bb7fa4ac98..67e1c0e66f18a 100644 --- a/UPGRADE-2.8.md +++ b/UPGRADE-2.8.md @@ -505,3 +505,11 @@ Config Additionally, if you have implemented cache validation strategies *using* `isFresh()` yourself, you should have a look at the new cache validation system based on `ResourceChecker`s. + +Yaml +---- + + * Deprecated usage of a colon in an unquoted mapping value + * Deprecated usage of `@`, `` ` ``, `|`, and `>` at the beginning of an unquoted string + * Deprecated non-escaped \ in double-quoted strings when parsing Yaml + ("Foo\Var" is not valid whereas "Foo\\Var" is) diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md index 65386359a5adf..5b86950fc694d 100644 --- a/UPGRADE-3.0.md +++ b/UPGRADE-3.0.md @@ -1228,6 +1228,11 @@ UPGRADE FROM 2.x to 3.0 ### Yaml + * Using a colon in an unquoted mapping value leads to a `ParseException`. + * Starting an unquoted string with `@`, `` ` ``, `|`, or `>` leads to a `ParseException`. + * Deprecated non-escaped \ in double-quoted strings when parsing Yaml + ("Foo\Var" is not valid whereas "Foo\\Var" is) + * The ability to pass file names to `Yaml::parse()` has been removed. Before: diff --git a/src/Symfony/Component/Yaml/CHANGELOG.md b/src/Symfony/Component/Yaml/CHANGELOG.md index a6a8612f3776a..ccfdb1a792c61 100644 --- a/src/Symfony/Component/Yaml/CHANGELOG.md +++ b/src/Symfony/Component/Yaml/CHANGELOG.md @@ -4,7 +4,8 @@ CHANGELOG 2.8.0 ----- - * Deprecated usage of @ and \` at the beginning of an unquoted string + * Deprecated usage of a colon in an unquoted mapping value + * Deprecated usage of @, \`, | and > at the beginning of an unquoted string * Deprecated non-escaped \ in double-quoted strings when parsing Yaml ("Foo\Var" is not valid whereas "Foo\\Var" is) From 2edebbf2aebc98649183e171e8042b93f6dedc8a Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Tue, 24 Nov 2015 23:11:35 +0100 Subject: [PATCH 053/107] add composer exclude-from-classmap for new 2.8 components --- src/Symfony/Component/Ldap/composer.json | 5 ++++- src/Symfony/Component/PropertyInfo/composer.json | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Ldap/composer.json b/src/Symfony/Component/Ldap/composer.json index 21e4adea9618b..bdbba744b3647 100644 --- a/src/Symfony/Component/Ldap/composer.json +++ b/src/Symfony/Component/Ldap/composer.json @@ -21,7 +21,10 @@ "ext-ldap": "*" }, "autoload": { - "psr-4": { "Symfony\\Component\\Ldap\\": "" } + "psr-4": { "Symfony\\Component\\Ldap\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "minimum-stability": "dev", "extra": { diff --git a/src/Symfony/Component/PropertyInfo/composer.json b/src/Symfony/Component/PropertyInfo/composer.json index ee34325b7663e..d7861d5b03d88 100644 --- a/src/Symfony/Component/PropertyInfo/composer.json +++ b/src/Symfony/Component/PropertyInfo/composer.json @@ -39,7 +39,10 @@ "symfony/serializer": "To use Serializer metadata" }, "autoload": { - "psr-4": { "Symfony\\Component\\PropertyInfo\\": "" } + "psr-4": { "Symfony\\Component\\PropertyInfo\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "minimum-stability": "dev", "extra": { From 0e0b904ff201d35c5275df32c81f70027d408dd6 Mon Sep 17 00:00:00 2001 From: Restless-ET Date: Wed, 30 Sep 2015 18:42:26 +0100 Subject: [PATCH 054/107] [Translation][Form] Do not translate form labels and placeholders when 'translation_domain' is false --- .../views/Form/form_div_layout.html.twig | 12 +-- .../views/Form/foundation_5_layout.html.twig | 4 +- .../views/Form/button_attributes.html.php | 2 +- .../views/Form/button_widget.html.php | 2 +- .../Form/choice_widget_collapsed.html.php | 2 +- .../views/Form/widget_attributes.html.php | 2 +- .../Form/widget_container_attributes.html.php | 2 +- .../Tests/AbstractBootstrap3LayoutTest.php | 83 ++++++++++++++++ .../Form/Tests/AbstractLayoutTest.php | 97 +++++++++++++++++++ 9 files changed, 193 insertions(+), 13 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig index 89f9910890879..81b6dcee9135d 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig @@ -57,7 +57,7 @@ {%- endif -%} {% if placeholder is not none -%} - + {%- endif %} {%- if preferred_choices|length > 0 -%} {% set options = preferred_choices %} @@ -253,7 +253,7 @@ {% endif %} {{ widget|raw }} - {{ label|trans({}, translation_domain) }} + {{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }} {%- endblock checkbox_radio_label %} diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_attributes.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_attributes.html.php index ac1077a205ae3..2be960d0e179c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_attributes.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_attributes.html.php @@ -1,7 +1,7 @@ id="escape($id) ?>" name="escape($full_name) ?>" disabled="disabled" $v): ?> -escape($k), $view->escape($view['translator']->trans($v, array(), $translation_domain))) ?> +escape($k), $view->escape(false !== $translation_domain ? $view['translator']->trans($v, array(), $translation_domain) : $v)) ?> escape($k), $view->escape($k)) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_widget.html.php index 9dac32fc994c0..4b63876984506 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_widget.html.php @@ -1,4 +1,4 @@ $name, '%id%' => $id)) : $view['form']->humanize($name); } ?> - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_collapsed.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_collapsed.html.php index fe4fbdb348a32..29ea388010758 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_collapsed.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_collapsed.html.php @@ -7,7 +7,7 @@ )) ?> multiple="multiple" > - + 0): ?> block($form, 'choice_widget_options', array('choices' => $preferred_choices)) ?> 0 && null !== $separator): ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php index ac5a481d0b55e..3fefa47c15c99 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php @@ -2,7 +2,7 @@ required="required" $v): ?> -escape($k), $view->escape($view['translator']->trans($v, array(), $translation_domain))) ?> +escape($k), $view->escape(false !== $translation_domain ? $view['translator']->trans($v, array(), $translation_domain) : $v)) ?> escape($k), $view->escape($k)) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_container_attributes.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_container_attributes.html.php index 327925a537196..dc2e5ebea84e6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_container_attributes.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_container_attributes.html.php @@ -1,7 +1,7 @@ id="escape($id) ?>" $v): ?> -escape($k), $view->escape($view['translator']->trans($v, array(), $translation_domain))) ?> +escape($k), $view->escape(false !== $translation_domain ? $view['translator']->trans($v, array(), $translation_domain) : $v)) ?> escape($k), $view->escape($k)) ?> diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php index 13da0c486071a..d952b58412c68 100644 --- a/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php @@ -254,6 +254,32 @@ public function testSingleChoiceWithoutTranslation() ); } + public function testSingleChoiceWithPlaceholderWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'multiple' => false, + 'expanded' => false, + 'required' => false, + 'translation_domain' => false, + 'placeholder' => 'Placeholder&Not&Translated', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [not(@required)] + [ + ./option[@value=""][not(@selected)][not(@disabled)][.="Placeholder&Not&Translated"] + /following-sibling::option[@value="&a"][@selected="selected"][.="Choice&A"] + /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"] + ] + [count(./option)=3] +' + ); + } + public function testSingleChoiceAttributes() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( @@ -771,6 +797,52 @@ public function testSingleChoiceExpandedWithPlaceholder() ); } + public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'multiple' => false, + 'expanded' => true, + 'translation_domain' => false, + 'placeholder' => 'Placeholder&Not&Translated', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="radio"] + [ + ./label + [.=" Placeholder&Not&Translated"] + [ + ./input[@type="radio"][@name="name"][@id="name_placeholder"][not(@checked)] + ] + ] + /following-sibling::div + [@class="radio"] + [ + ./label + [.=" Choice&A"] + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@checked] + ] + ] + /following-sibling::div + [@class="radio"] + [ + ./label + [.=" Choice&B"] + [ + ./input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"] + ] +' + ); + } + public function testSingleChoiceExpandedWithBooleanValue() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', true, array( @@ -2023,6 +2095,17 @@ public function testButton() ); } + public function testButtonlabelWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ButtonType', null, array( + 'translation_domain' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), + '/button[@type="button"][@name="name"][.="Name"][@class="my&class btn"]' + ); + } + public function testSubmit() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\SubmitType'); diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php index 8d5d9ea8a2065..f44aed122b3cf 100644 --- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -381,6 +381,25 @@ public function testLabelFormatOverriddenOption() ); } + public function testLabelWithoutTranslationOnButton() + { + $form = $this->factory->createNamedBuilder('myform', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'translation_domain' => false, + )) + ->add('mybutton', 'Symfony\Component\Form\Extension\Core\Type\ButtonType') + ->getForm(); + $view = $form->get('mybutton')->createView(); + $html = $this->renderWidget($view); + + $this->assertMatchesXpath($html, +'/button + [@type="button"] + [@name="myform[mybutton]"] + [.="Mybutton"] +' + ); + } + public function testLabelFormatOnButton() { $form = $this->factory->createNamedBuilder('myform') @@ -549,6 +568,31 @@ public function testSingleChoiceWithoutTranslation() ); } + public function testSingleChoiceWithPlaceholderWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'multiple' => false, + 'expanded' => false, + 'required' => false, + 'translation_domain' => false, + 'placeholder' => 'Placeholder&Not&Translated', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value=""][not(@selected)][not(@disabled)][.="Placeholder&Not&Translated"] + /following-sibling::option[@value="&a"][@selected="selected"][.="Choice&A"] + /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"] + ] + [count(./option)=3] +' + ); + } + public function testSingleChoiceAttributes() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( @@ -998,6 +1042,32 @@ public function testSingleChoiceExpandedWithPlaceholder() ); } + public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'multiple' => false, + 'expanded' => true, + 'translation_domain' => false, + 'placeholder' => 'Placeholder&Not&Translated', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="radio"][@name="name"][@id="name_placeholder"][not(@checked)] + /following-sibling::label[@for="name_placeholder"][.="Placeholder&Not&Translated"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_0"][@checked] + /following-sibling::label[@for="name_0"][.="Choice&A"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] + /following-sibling::label[@for="name_1"][.="Choice&B"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=4] +' + ); + } + public function testSingleChoiceExpandedWithBooleanValue() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', true, array( @@ -2141,6 +2211,17 @@ public function testButtonLabelIsEmpty() $this->assertSame('', $this->renderLabel($form->createView())); } + public function testButtonlabelWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ButtonType', null, array( + 'translation_domain' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), + '/button[@type="button"][@name="name"][.="Name"]' + ); + } + public function testSubmit() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\SubmitType'); @@ -2349,4 +2430,20 @@ public function testTranslatedAttributes() $this->assertMatchesXpath($html, '/form//input[@title="[trans]Foo[/trans]"]'); $this->assertMatchesXpath($html, '/form//input[@placeholder="[trans]Bar[/trans]"]'); } + + public function testAttributesNotTranslatedWhenTranslationDomainIsFalse() + { + $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'translation_domain' => false, + )) + ->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('attr' => array('title' => 'Foo'))) + ->add('lastName', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('attr' => array('placeholder' => 'Bar'))) + ->getForm() + ->createView(); + + $html = $this->renderForm($view); + + $this->assertMatchesXpath($html, '/form//input[@title="Foo"]'); + $this->assertMatchesXpath($html, '/form//input[@placeholder="Bar"]'); + } } From cf284742ff0b6fc5f06ebc36250650f64853a625 Mon Sep 17 00:00:00 2001 From: Michal Piotrowski Date: Sun, 8 Nov 2015 16:53:13 +0100 Subject: [PATCH 055/107] fix unused variable warning --- src/Symfony/Bridge/PhpUnit/ClockMock.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/PhpUnit/ClockMock.php b/src/Symfony/Bridge/PhpUnit/ClockMock.php index 4b745e38e87fc..b81fee965880a 100644 --- a/src/Symfony/Bridge/PhpUnit/ClockMock.php +++ b/src/Symfony/Bridge/PhpUnit/ClockMock.php @@ -66,7 +66,7 @@ public static function microtime($asFloat = false) return self::$now; } - return sprintf("%0.6f %d\n", $now - (int) $now, (int) self::$now); + return sprintf("%0.6f %d\n", self::$now - (int) self::$now, (int) self::$now); } public static function register($class) From 09f489530ba67312b0d7a2a5d4926838ea4d9f0a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 25 Nov 2015 09:53:09 +0100 Subject: [PATCH 056/107] Fixes the stack traces of the deprecation logs --- .../Resources/views/Collector/logger.html.twig | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig index 361e684fbf938..3fb596b0960d2 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig @@ -169,6 +169,7 @@ {% if index == 2 %}
    {% endif %} + {% if call.class is defined %} {% set from = call.class|abbr_class ~ '::' ~ call.function|abbr_method() %} {% elseif call.function is defined %} @@ -179,7 +180,14 @@ {% set from = '-' %} {% endif %} -
  • Called from {{ call.file is defined and call.line is defined ? call.file|format_file(call.line, from) : from|raw }}
  • + {% set file_name = (call.file is defined and call.line is defined) ? call.file|replace({'\\': '/'})|split('/')|last %} + +
  • + {{ from|raw }} + {% if file_name %} + (called from {{ call.file|format_file(call.line, file_name)|raw }}) + {% endif %} +
  • {% if index == stack|length - 1 %}
From 0113ac3ce23825781b782570cccaa5905d0846ea Mon Sep 17 00:00:00 2001 From: Jelte Steijaert Date: Fri, 30 Oct 2015 10:20:47 +0100 Subject: [PATCH 057/107] Bug #16343 [Router] Too many Routes ? --- .../Generator/Dumper/PhpGeneratorDumper.php | 5 ++- .../Dumper/PhpGeneratorDumperTest.php | 34 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php b/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php index 9f26cad837d87..1cd0f19197ca4 100644 --- a/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php +++ b/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php @@ -53,7 +53,7 @@ public function dump(array $options = array()) */ class {$options['class']} extends {$options['base_class']} { - private static \$declaredRoutes = {$this->generateDeclaredRoutes()}; + private static \$declaredRoutes; /** * Constructor. @@ -62,6 +62,9 @@ public function __construct(RequestContext \$context, LoggerInterface \$logger = { \$this->context = \$context; \$this->logger = \$logger; + if (null === self::\$declaredRoutes) { + self::\$declaredRoutes = {$this->generateDeclaredRoutes()}; + } } {$this->generateGenerateMethod()} diff --git a/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php b/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php index 43ef624ddf896..393aa066f597e 100644 --- a/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php +++ b/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php @@ -34,6 +34,11 @@ class PhpGeneratorDumperTest extends \PHPUnit_Framework_TestCase */ private $testTmpFilepath; + /** + * @var string + */ + private $largeTestTmpFilepath; + protected function setUp() { parent::setUp(); @@ -41,7 +46,9 @@ protected function setUp() $this->routeCollection = new RouteCollection(); $this->generatorDumper = new PhpGeneratorDumper($this->routeCollection); $this->testTmpFilepath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'php_generator.'.$this->getName().'.php'; + $this->largeTestTmpFilepath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'php_generator.'.$this->getName().'.large.php'; @unlink($this->testTmpFilepath); + @unlink($this->largeTestTmpFilepath); } protected function tearDown() @@ -76,6 +83,33 @@ public function testDumpWithRoutes() $this->assertEquals($relativeUrlWithoutParameter, '/app.php/testing2'); } + public function testDumpWithTooManyRoutes() + { + $this->routeCollection->add('Test', new Route('/testing/{foo}')); + for ( $i = 0; $i < 32769; ++$i ) { + $this->routeCollection->add('route_'.$i, new Route('/route_'.$i)); + } + $this->routeCollection->add('Test2', new Route('/testing2')); + + $data = $this->generatorDumper->dump(array( + 'class' => 'ProjectLargeUrlGenerator', + )); + file_put_contents($this->largeTestTmpFilepath, $data); + include $this->largeTestTmpFilepath; + + $projectUrlGenerator = new \ProjectLargeUrlGenerator(new RequestContext('/app.php')); + + $absoluteUrlWithParameter = $projectUrlGenerator->generate('Test', array('foo' => 'bar'), UrlGeneratorInterface::ABSOLUTE_URL); + $absoluteUrlWithoutParameter = $projectUrlGenerator->generate('Test2', array(), UrlGeneratorInterface::ABSOLUTE_URL); + $relativeUrlWithParameter = $projectUrlGenerator->generate('Test', array('foo' => 'bar'), UrlGeneratorInterface::ABSOLUTE_PATH); + $relativeUrlWithoutParameter = $projectUrlGenerator->generate('Test2', array(), UrlGeneratorInterface::ABSOLUTE_PATH); + + $this->assertEquals($absoluteUrlWithParameter, 'http://localhost/app.php/testing/bar'); + $this->assertEquals($absoluteUrlWithoutParameter, 'http://localhost/app.php/testing2'); + $this->assertEquals($relativeUrlWithParameter, '/app.php/testing/bar'); + $this->assertEquals($relativeUrlWithoutParameter, '/app.php/testing2'); + } + /** * @expectedException \InvalidArgumentException */ From 4a17c9e6afcc83982dfb5af79f4adb82bf806643 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 25 Nov 2015 18:16:22 +0100 Subject: [PATCH 058/107] [ClassLoader] Fix parsing namespace when token_get_all() is missing --- src/Symfony/Component/ClassLoader/ClassCollectionLoader.php | 4 ++-- .../Component/ClassLoader/Tests/ClassCollectionLoaderTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php b/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php index 88bd500f1d2c8..6fdd0a4b6b5b9 100644 --- a/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php +++ b/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php @@ -137,8 +137,8 @@ public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = public static function fixNamespaceDeclarations($source) { if (!function_exists('token_get_all') || !self::$useTokenizer) { - if (preg_match('/namespace(.*?)\s*;/', $source)) { - $source = preg_replace('/namespace(.*?)\s*;/', "namespace$1\n{", $source)."}\n"; + if (preg_match('/(^|\s)namespace(.*?)\s*;/', $source)) { + $source = preg_replace('/(^|\s)namespace(.*?)\s*;/', "$1namespace$2\n{", $source)."}\n"; } return $source; diff --git a/src/Symfony/Component/ClassLoader/Tests/ClassCollectionLoaderTest.php b/src/Symfony/Component/ClassLoader/Tests/ClassCollectionLoaderTest.php index 2d78941538191..5019f26ee84c8 100644 --- a/src/Symfony/Component/ClassLoader/Tests/ClassCollectionLoaderTest.php +++ b/src/Symfony/Component/ClassLoader/Tests/ClassCollectionLoaderTest.php @@ -205,7 +205,7 @@ public function getFixNamespaceDeclarationsDataWithoutTokenizer() array("namespace Bar ;\nclass Foo {}\n", "namespace Bar\n{\nclass Foo {}\n}\n"), array("namespace Foo\Bar;\nclass Foo {}\n", "namespace Foo\Bar\n{\nclass Foo {}\n}\n"), array("namespace Foo\Bar\Bar\n{\nclass Foo {}\n}\n", "namespace Foo\Bar\Bar\n{\nclass Foo {}\n}\n"), - array("namespace\n{\nclass Foo {}\n}\n", "namespace\n{\nclass Foo {}\n}\n"), + array("\nnamespace\n{\nclass Foo {}\n\$namespace=123;}\n", "\nnamespace\n{\nclass Foo {}\n\$namespace=123;}\n"), ); } From 171208627dee9c657f1fa6f4bee560a9519bf178 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 25 Nov 2015 19:15:39 +0100 Subject: [PATCH 059/107] Always enable clock-mock for HttpFoundation --- phpunit.xml.dist | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 4e4e411c3cfb4..010114e0b9f35 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -45,4 +45,14 @@ + + + + + + Symfony\Component\HttpFoundation + + + + From 5013f9895f621b72798b0dd0d951e2a319e78df9 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 26 Nov 2015 07:58:58 +0100 Subject: [PATCH 060/107] [HttpFoundation] Workaround HHVM rewriting HTTP response line --- src/Symfony/Component/HttpFoundation/Response.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index ad55f8772af47..914e54fbb3dd7 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -268,9 +268,6 @@ public function sendHeaders() return $this; } - // status - header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)); - // headers foreach ($this->headers->allPreserveCase() as $name => $values) { foreach ($values as $value) { @@ -278,6 +275,9 @@ public function sendHeaders() } } + // status + header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode); + // cookies foreach ($this->headers->getCookies() as $cookie) { setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly()); From 7aac273701f2aca450703fd37233a86de3730d5e Mon Sep 17 00:00:00 2001 From: Peter Rehm Date: Wed, 25 Nov 2015 10:54:34 +0100 Subject: [PATCH 061/107] Added the renamed options of the collection type to the upgrade information --- UPGRADE-2.8.md | 8 ++++++++ UPGRADE-3.0.md | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/UPGRADE-2.8.md b/UPGRADE-2.8.md index 67e1c0e66f18a..1271097a88c4d 100644 --- a/UPGRADE-2.8.md +++ b/UPGRADE-2.8.md @@ -213,6 +213,14 @@ Form } } ``` + + * The option "options" of the CollectionType has been renamed to "entry_options". + The usage of the option "options" is deprecated and will be removed in Symfony 3.0. + + * The option "type" of the CollectionType has been renamed to "entry_type". + The usage of the option "type" is deprecated and will be removed in Symfony 3.0. + As a value for the option you should provide the fully-qualified class name (FQCN) + now as well. * Passing type instances to `Form::add()`, `FormBuilder::add()` and the `FormFactory::create*()` methods is deprecated and will not be supported diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md index 5b86950fc694d..d5533f2b4b813 100644 --- a/UPGRADE-3.0.md +++ b/UPGRADE-3.0.md @@ -264,6 +264,12 @@ UPGRADE FROM 2.x to 3.0 // ... } ``` + + * The option "options" of the CollectionType has been renamed to "entry_options". + + * The option "type" of the CollectionType has been renamed to "entry_type". + As a value for the option you must provide the fully-qualified class name (FQCN) + now as well. * The `FormIntegrationTestCase` and `FormPerformanceTestCase` classes were moved form the `Symfony\Component\Form\Tests` namespace to the `Symfony\Component\Form\Test` namespace. From f495410d25e0ce7c2bf503be91b1776b4e2359ff Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Thu, 26 Nov 2015 10:11:54 +0100 Subject: [PATCH 062/107] [Form] Disabled view data validation if "data_class" is set to null --- src/Symfony/Component/Form/Form.php | 18 ++++-------------- .../Component/Form/Tests/SimpleFormTest.php | 10 +++++----- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index a277b580c5f2a..0f68dc851a58f 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -356,21 +356,11 @@ public function setData($modelData) if (!FormUtil::isEmpty($viewData)) { $dataClass = $this->config->getDataClass(); - $actualType = is_object($viewData) ? 'an instance of class '.get_class($viewData) : 'a(n) '.gettype($viewData); - - if (null === $dataClass && is_object($viewData) && !$viewData instanceof \ArrayAccess) { - $expectedType = 'scalar, array or an instance of \ArrayAccess'; - - throw new LogicException( - 'The form\'s view data is expected to be of type '.$expectedType.', '. - 'but is '.$actualType.'. You '. - 'can avoid this error by setting the "data_class" option to '. - '"'.get_class($viewData).'" or by adding a view transformer '. - 'that transforms '.$actualType.' to '.$expectedType.'.' - ); - } - if (null !== $dataClass && !$viewData instanceof $dataClass) { + $actualType = is_object($viewData) + ? 'an instance of class '.get_class($viewData) + : 'a(n) '.gettype($viewData); + throw new LogicException( 'The form\'s view data is expected to be an instance of class '. $dataClass.', but is '.$actualType.'. You can avoid this error '. diff --git a/src/Symfony/Component/Form/Tests/SimpleFormTest.php b/src/Symfony/Component/Form/Tests/SimpleFormTest.php index 6fb24a8cdc9c7..ae174f045d773 100644 --- a/src/Symfony/Component/Form/Tests/SimpleFormTest.php +++ b/src/Symfony/Component/Form/Tests/SimpleFormTest.php @@ -840,19 +840,19 @@ public function testGetPropertyPathDefaultsToIndexedNameIfDataClassOfFirstParent $this->assertEquals(new PropertyPath('[name]'), $form->getPropertyPath()); } - /** - * @expectedException \Symfony\Component\Form\Exception\LogicException - */ - public function testViewDataMustNotBeObjectIfDataClassIsNull() + public function testViewDataMayBeObjectIfDataClassIsNull() { + $object = new \stdClass(); $config = new FormConfigBuilder('name', null, $this->dispatcher); $config->addViewTransformer(new FixedDataTransformer(array( '' => '', - 'foo' => new \stdClass(), + 'foo' => $object, ))); $form = new Form($config); $form->setData('foo'); + + $this->assertSame($object, $form->getViewData()); } public function testViewDataMayBeArrayAccessIfDataClassIsNull() From 12a152b6664f2143eddab218bcf22fa39e998261 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 26 Nov 2015 10:39:31 +0100 Subject: [PATCH 063/107] [appveyor] Workaround transient segfault when APCu is enabled --- .travis.yml | 2 +- appveyor.yml | 4 ++-- phpunit | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index c347943fb48a0..c2933ed76e9d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,7 @@ before_install: - if [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then phpenv config-rm xdebug.ini; fi; - if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then echo "extension = mongo.so" >> $INI_FILE; fi; - if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then echo "extension = memcache.so" >> $INI_FILE; fi; - - if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then (echo yes | pecl install -f apcu-4.0.7 && echo "apc.enable_cli = 1" >> $INI_FILE) || echo "Let's continue without apcu extension"; fi; + - if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then (echo yes | pecl install -f apcu-4.0.8 && echo "apc.enable_cli = 1" >> $INI_FILE) || echo "Let's continue without apcu extension"; fi; - if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then pecl install -f memcached-2.1.0 || echo "Let's continue without memcached extension"; fi; - if [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then php -i; else hhvm --php -r 'print_r($_SERVER);print_r(ini_get_all());'; fi; - if [ "$deps" != "skip" ]; then ./phpunit install; fi; diff --git a/appveyor.yml b/appveyor.yml index 5237ef69e4e63..45a9e7e9908d7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -26,8 +26,8 @@ install: - IF %PHP%==1 cd ext - IF %PHP%==1 appveyor DownloadFile http://nebm.ist.utl.pt/~glopes/misc/intl_win/php_intl-3.0.0-5.3-nts-vc9-x86.zip - IF %PHP%==1 7z x php_intl-3.0.0-5.3-nts-vc9-x86.zip -y >nul - - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/pecl/releases/apcu/4.0.7/php_apcu-4.0.7-5.3-nts-vc9-x86.zip - - IF %PHP%==1 7z x php_apcu-4.0.7-5.3-nts-vc9-x86.zip -y >nul + - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/pecl/releases/apcu/4.0.8/php_apcu-4.0.8-5.3-nts-vc9-x86.zip + - IF %PHP%==1 7z x php_apcu-4.0.8-5.3-nts-vc9-x86.zip -y >nul - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/pecl/releases/memcache/3.0.8/php_memcache-3.0.8-5.3-nts-vc9-x86.zip - IF %PHP%==1 7z x php_memcache-3.0.8-5.3-nts-vc9-x86.zip -y >nul - IF %PHP%==1 del /Q *.zip diff --git a/phpunit b/phpunit index 2ab4f25e75cc2..3a3b3dd881ef4 100755 --- a/phpunit +++ b/phpunit @@ -164,7 +164,8 @@ if (isset($argv[1]) && 'symfony' === $argv[1]) { unlink($file); } - if ($procStatus) { + // Fail on any individual component failures but ignore STATUS_STACK_BUFFER_OVERRUN (-1073740791) on Windows when APCu is enabled + if ($procStatus && ('\\' !== DIRECTORY_SEPARATOR || !extension_loaded('apcu') || !ini_get('apc.enable_cli') || -1073740791 !== $procStatus)) { $exit = 1; echo "\033[41mKO\033[0m $component\n\n"; } else { From 91496229ab2cc518ae8467105eb91e0bb03b738f Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Thu, 26 Nov 2015 09:12:29 +0100 Subject: [PATCH 064/107] [Form] Fixed wrong usages of the "text" type --- src/Symfony/Component/Form/Form.php | 2 +- src/Symfony/Component/Form/Tests/CompoundFormTest.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index a277b580c5f2a..1d177d96b2ab1 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -910,7 +910,7 @@ public function add($child, $type = null, array $options = array()) $options['auto_initialize'] = false; if (null === $type && null === $this->config->getDataClass()) { - $type = 'text'; + $type = 'Symfony\Component\Form\Extension\Core\Type\TextType'; } if (null === $type) { diff --git a/src/Symfony/Component/Form/Tests/CompoundFormTest.php b/src/Symfony/Component/Form/Tests/CompoundFormTest.php index a306647234dd9..0a9ed8e67078f 100644 --- a/src/Symfony/Component/Form/Tests/CompoundFormTest.php +++ b/src/Symfony/Component/Form/Tests/CompoundFormTest.php @@ -172,13 +172,13 @@ public function testAddUsingNameAndType() $this->factory->expects($this->once()) ->method('createNamed') - ->with('foo', 'text', null, array( + ->with('foo', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array( 'bar' => 'baz', 'auto_initialize' => false, )) ->will($this->returnValue($child)); - $this->form->add('foo', 'text', array('bar' => 'baz')); + $this->form->add('foo', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('bar' => 'baz')); $this->assertTrue($this->form->has('foo')); $this->assertSame($this->form, $child->getParent()); @@ -191,14 +191,14 @@ public function testAddUsingIntegerNameAndType() $this->factory->expects($this->once()) ->method('createNamed') - ->with('0', 'text', null, array( + ->with('0', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array( 'bar' => 'baz', 'auto_initialize' => false, )) ->will($this->returnValue($child)); // in order to make casting unnecessary - $this->form->add(0, 'text', array('bar' => 'baz')); + $this->form->add(0, 'Symfony\Component\Form\Extension\Core\Type\TextType', array('bar' => 'baz')); $this->assertTrue($this->form->has(0)); $this->assertSame($this->form, $child->getParent()); @@ -211,7 +211,7 @@ public function testAddWithoutType() $this->factory->expects($this->once()) ->method('createNamed') - ->with('foo', 'text') + ->with('foo', 'Symfony\Component\Form\Extension\Core\Type\TextType') ->will($this->returnValue($child)); $this->form->add('foo'); From 5764a00a763efcc18f7230c21e87d5cf137b3a8a Mon Sep 17 00:00:00 2001 From: Wouter J Date: Sat, 21 Nov 2015 17:49:57 +0100 Subject: [PATCH 065/107] [Console] Fix bug with overloading --- .../Bundle/FrameworkBundle/Command/ServerStartCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php index 309ede6d068d3..f743cfbe3312e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php @@ -74,7 +74,7 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $output = new SymfonyStyle($input, $output); + $output = new SymfonyStyle($input, $cliOutput = $output); if (!extension_loaded('pcntl')) { $output->error(array( @@ -85,7 +85,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if ($output->ask('Do you want to execute server:run immediately? [Yn] ', true)) { $command = $this->getApplication()->find('server:run'); - return $command->run($input, $output); + return $command->run($input, $cliOutput); } return 1; From 41df3fce32a8bd489fba2e562645deed1f4e5860 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Thu, 26 Nov 2015 18:08:26 +0100 Subject: [PATCH 066/107] [Form] Deprecated TimezoneType::getTimezones() --- UPGRADE-2.8.md | 3 +++ UPGRADE-3.0.md | 3 +++ .../Component/Form/Extension/Core/Type/TimezoneType.php | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/UPGRADE-2.8.md b/UPGRADE-2.8.md index 67e1c0e66f18a..216f1844e78b0 100644 --- a/UPGRADE-2.8.md +++ b/UPGRADE-2.8.md @@ -251,6 +251,9 @@ Form
``` + + * The `TimezoneType::getTimezones()` method was deprecated and will be removed + in Symfony 3.0. You should not use this method. Translator ---------- diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md index 5b86950fc694d..d422af0a58563 100644 --- a/UPGRADE-3.0.md +++ b/UPGRADE-3.0.md @@ -311,6 +311,9 @@ UPGRADE FROM 2.x to 3.0 * The `Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList` class has been removed in favor of `Symfony\Component\Form\ChoiceList\ArrayChoiceList`. + + * The `TimezoneType::getTimezones()` method was removed. You should not use + this method. ### FrameworkBundle diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php index 3277a1838636c..5e3f4a385aa21 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php @@ -67,9 +67,13 @@ public function getBlockPrefix() * overhead. * * @return array The timezone choices + * + * @deprecated Deprecated since version 2.8 */ public static function getTimezones() { + @trigger_error('The TimezoneType::getTimezones() method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + if (null === static::$timezones) { static::$timezones = array(); From 5386752bf9cf9fe4fc7ba8aff8f8466b60d2fa04 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Thu, 26 Nov 2015 18:36:46 +0100 Subject: [PATCH 067/107] [Form] Deprecated ArrayKeyChoiceList --- UPGRADE-2.8.md | 3 +++ UPGRADE-3.0.md | 3 +++ src/Symfony/Component/Form/ChoiceList/ArrayKeyChoiceList.php | 2 ++ .../Component/Form/Tests/ChoiceList/ArrayKeyChoiceListTest.php | 2 ++ 4 files changed, 10 insertions(+) diff --git a/UPGRADE-2.8.md b/UPGRADE-2.8.md index 67e1c0e66f18a..95a3c6b77fb91 100644 --- a/UPGRADE-2.8.md +++ b/UPGRADE-2.8.md @@ -251,6 +251,9 @@ Form
``` + + * The class `ArrayKeyChoiceList` was deprecated and will be removed in Symfony + 3.0. Use `ArrayChoiceList` instead. Translator ---------- diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md index 5b86950fc694d..d6a056a634336 100644 --- a/UPGRADE-3.0.md +++ b/UPGRADE-3.0.md @@ -312,6 +312,9 @@ UPGRADE FROM 2.x to 3.0 * The `Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList` class has been removed in favor of `Symfony\Component\Form\ChoiceList\ArrayChoiceList`. + * The `Symfony\Component\Form\ChoiceList\ArrayKeyChoiceList` class has been removed in + favor of `Symfony\Component\Form\ChoiceList\ArrayChoiceList`. + ### FrameworkBundle * The `config:debug`, `container:debug`, `router:debug`, `translation:debug` diff --git a/src/Symfony/Component/Form/ChoiceList/ArrayKeyChoiceList.php b/src/Symfony/Component/Form/ChoiceList/ArrayKeyChoiceList.php index 7c3c107d0f720..fa2e3973f5867 100644 --- a/src/Symfony/Component/Form/ChoiceList/ArrayKeyChoiceList.php +++ b/src/Symfony/Component/Form/ChoiceList/ArrayKeyChoiceList.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Form\ChoiceList; +@trigger_error('The '.__NAMESPACE__.'\ArrayKeyChoiceList class is deprecated since version 2.8 and will be removed in 3.0. Use '.__NAMESPACE__.'\ArrayChoiceList instead.', E_USER_DEPRECATED); + use Symfony\Component\Form\Exception\InvalidArgumentException; /** diff --git a/src/Symfony/Component/Form/Tests/ChoiceList/ArrayKeyChoiceListTest.php b/src/Symfony/Component/Form/Tests/ChoiceList/ArrayKeyChoiceListTest.php index 5cbadf6e0fe7b..1f1643158d48d 100644 --- a/src/Symfony/Component/Form/Tests/ChoiceList/ArrayKeyChoiceListTest.php +++ b/src/Symfony/Component/Form/Tests/ChoiceList/ArrayKeyChoiceListTest.php @@ -15,6 +15,8 @@ /** * @author Bernhard Schussek + * + * @group legacy */ class ArrayKeyChoiceListTest extends AbstractChoiceListTest { From 478375d66424edfe8c9b40cd7f36ed784fba3b62 Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Thu, 26 Nov 2015 18:53:25 +0100 Subject: [PATCH 068/107] [DI] remove useless condition around unset --- src/Symfony/Component/DependencyInjection/Container.php | 5 +---- src/Symfony/Component/DependencyInjection/Definition.php | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php index 443a65fb4183a..fb0e31911b8cb 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php +++ b/src/Symfony/Component/DependencyInjection/Container.php @@ -312,10 +312,7 @@ public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE $service = $this->$method(); } catch (\Exception $e) { unset($this->loading[$id]); - - if (array_key_exists($id, $this->services)) { - unset($this->services[$id]); - } + unset($this->services[$id]); if ($e instanceof InactiveScopeException && self::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) { return; diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index e2b40ff4b9703..690daa9c6ab02 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -409,9 +409,7 @@ public function hasTag($name) */ public function clearTag($name) { - if (isset($this->tags[$name])) { - unset($this->tags[$name]); - } + unset($this->tags[$name]); return $this; } From 55f84a3369091e4fcf718472952a6bcb689c491c Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Thu, 26 Nov 2015 20:19:29 +0100 Subject: [PATCH 069/107] [SecurityBundle] disable the init:acl command if ACL is not used --- .../Bundle/SecurityBundle/Command/InitAclCommand.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Symfony/Bundle/SecurityBundle/Command/InitAclCommand.php b/src/Symfony/Bundle/SecurityBundle/Command/InitAclCommand.php index fff5b1e929503..14271dc459a08 100644 --- a/src/Symfony/Bundle/SecurityBundle/Command/InitAclCommand.php +++ b/src/Symfony/Bundle/SecurityBundle/Command/InitAclCommand.php @@ -23,6 +23,18 @@ */ class InitAclCommand extends ContainerAwareCommand { + /** + * {@inheritdoc} + */ + public function isEnabled() + { + if (!$this->getContainer()->has('security.acl.dbal.connection')) { + return false; + } + + return parent::isEnabled(); + } + /** * {@inheritdoc} */ From 123a300ad72d77f9d8e34a3ab44449b47be0eb49 Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Wed, 25 Nov 2015 14:52:03 +0100 Subject: [PATCH 070/107] [Debug] fix readme: DebugClassLoader moved to debug itself --- src/Symfony/Component/Debug/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Debug/README.md b/src/Symfony/Component/Debug/README.md index 67e6d6c2785c6..82c6e30322de4 100644 --- a/src/Symfony/Component/Debug/README.md +++ b/src/Symfony/Component/Debug/README.md @@ -15,6 +15,7 @@ Debug::enable(); You can also use the tools individually: ```php +use Symfony\Component\Debug\DebugClassLoader; use Symfony\Component\Debug\ErrorHandler; use Symfony\Component\Debug\ExceptionHandler; @@ -25,11 +26,9 @@ if ('cli' !== php_sapi_name()) { ini_set('display_errors', 1); } ErrorHandler::register(); +DebugClassLoader::enable(); ``` -Note that the `Debug::enable()` call also registers the debug class loader -from the Symfony ClassLoader component when available. - This component can optionally take advantage of the features of the HttpKernel and HttpFoundation components. From acef3a3b340797ff24bafe2b8aa189bb9571b010 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Thu, 26 Nov 2015 22:00:12 +0100 Subject: [PATCH 071/107] CS: remove impossible default argument value --- src/Symfony/Component/Asset/UrlPackage.php | 5 +++-- .../Form/Extension/DataCollector/FormDataCollector.php | 4 ++-- .../HttpKernel/Tests/Controller/ControllerResolverTest.php | 2 +- src/Symfony/Component/Security/Acl/Domain/Acl.php | 2 +- .../RememberMe/PersistentTokenBasedRememberMeServices.php | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Asset/UrlPackage.php b/src/Symfony/Component/Asset/UrlPackage.php index 6381a9c1cdbdf..3626ec843c48b 100644 --- a/src/Symfony/Component/Asset/UrlPackage.php +++ b/src/Symfony/Component/Asset/UrlPackage.php @@ -39,10 +39,11 @@ class UrlPackage extends Package private $sslPackage; /** - * @param string|array $baseUrls Base asset URLs + * @param string|string[] $baseUrls Base asset URLs * @param VersionStrategyInterface $versionStrategy The version strategy + * @param ContextInterface|null $context Context */ - public function __construct($baseUrls = array(), VersionStrategyInterface $versionStrategy, ContextInterface $context = null) + public function __construct($baseUrls, VersionStrategyInterface $versionStrategy, ContextInterface $context = null) { parent::__construct($versionStrategy, $context); diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php index 4fb08772d7de4..d1375df8894b8 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php @@ -217,7 +217,7 @@ public function getData() return $this->data; } - private function recursiveBuildPreliminaryFormTree(FormInterface $form, &$output = null, array &$outputByHash) + private function recursiveBuildPreliminaryFormTree(FormInterface $form, &$output, array &$outputByHash) { $hash = spl_object_hash($form); @@ -236,7 +236,7 @@ private function recursiveBuildPreliminaryFormTree(FormInterface $form, &$output } } - private function recursiveBuildFinalFormTree(FormInterface $form = null, FormView $view, &$output = null, array &$outputByHash) + private function recursiveBuildFinalFormTree(FormInterface $form = null, FormView $view, &$output, array &$outputByHash) { $viewHash = spl_object_hash($view); $formHash = null; diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php index 32db4e47adc5a..c298c84ec0059 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php @@ -224,7 +224,7 @@ protected function controllerMethod2($foo, $bar = null) { } - protected function controllerMethod3($foo, $bar = null, $foobar) + protected function controllerMethod3($foo, $bar, $foobar) { } diff --git a/src/Symfony/Component/Security/Acl/Domain/Acl.php b/src/Symfony/Component/Security/Acl/Domain/Acl.php index f417c8fda0475..fb70738ae7f65 100644 --- a/src/Symfony/Component/Security/Acl/Domain/Acl.php +++ b/src/Symfony/Component/Security/Acl/Domain/Acl.php @@ -56,7 +56,7 @@ class Acl implements AuditableAclInterface, NotifyPropertyChanged * @param array $loadedSids * @param bool $entriesInheriting */ - public function __construct($id, ObjectIdentityInterface $objectIdentity, PermissionGrantingStrategyInterface $permissionGrantingStrategy, array $loadedSids = array(), $entriesInheriting) + public function __construct($id, ObjectIdentityInterface $objectIdentity, PermissionGrantingStrategyInterface $permissionGrantingStrategy, array $loadedSids, $entriesInheriting) { $this->id = $id; $this->objectIdentity = $objectIdentity; diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index 4fb7e09c147ff..eac17d4df2590 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -44,7 +44,7 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices * @param LoggerInterface $logger * @param SecureRandomInterface $secureRandom */ - public function __construct(array $userProviders, $key, $providerKey, array $options = array(), LoggerInterface $logger = null, SecureRandomInterface $secureRandom) + public function __construct(array $userProviders, $key, $providerKey, array $options, LoggerInterface $logger = null, SecureRandomInterface $secureRandom) { parent::__construct($userProviders, $key, $providerKey, $options, $logger); From 90f3b941d34d9c868bc21a096f96045207e493df Mon Sep 17 00:00:00 2001 From: hainey Date: Fri, 27 Nov 2015 13:33:49 +0900 Subject: [PATCH 072/107] Fixed the wrong source name and the ja translation --- .../Component/Form/Resources/translations/validators.ja.xlf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Form/Resources/translations/validators.ja.xlf b/src/Symfony/Component/Form/Resources/translations/validators.ja.xlf index 2e8585a75c0c8..0db5eddbe68a6 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.ja.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.ja.xlf @@ -11,8 +11,8 @@ アップロードされたファイルが大きすぎます。小さなファイルで再度アップロードしてください。 - The CSRF token is invalid. - CSRFトークンが無効です。 + The CSRF token is invalid. Please try to resubmit the form. + CSRFトークンが無効です、再送信してください。 From 8588a4f63b718eff02554e766388f1ca05acc35c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 27 Nov 2015 09:21:43 +0100 Subject: [PATCH 073/107] [Process] Don't catch RuntimeException when it complicates tests debugging --- .../Process/Tests/SimpleProcessTest.php | 47 ++++++++----------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/src/Symfony/Component/Process/Tests/SimpleProcessTest.php b/src/Symfony/Component/Process/Tests/SimpleProcessTest.php index a52cd437a882b..4419581167a8d 100644 --- a/src/Symfony/Component/Process/Tests/SimpleProcessTest.php +++ b/src/Symfony/Component/Process/Tests/SimpleProcessTest.php @@ -152,46 +152,37 @@ public function testSignalWithWrongNonIntSignal() public function testStopTerminatesProcessCleanly() { - try { - $process = $this->getProcess(self::$phpBin.' -r "echo \'foo\'; sleep(1); echo \'bar\';"'); - $process->run(function () use ($process) { - $process->stop(); - }); - } catch (\RuntimeException $e) { - $this->fail('A call to stop() is not expected to cause wait() to throw a RuntimeException'); - } + $process = $this->getProcess(self::$phpBin.' -r "echo \'foo\'; sleep(1); echo \'bar\';"'); + $process->run(function () use ($process) { + $process->stop(); + }); + $this->assertTrue(true, 'A call to stop() is not expected to cause wait() to throw a RuntimeException'); } public function testKillSignalTerminatesProcessCleanly() { $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.'); - try { - $process = $this->getProcess(self::$phpBin.' -r "echo \'foo\'; sleep(1); echo \'bar\';"'); - $process->run(function () use ($process) { - if ($process->isRunning()) { - $process->signal(defined('SIGKILL') ? SIGKILL : 9); - } - }); - } catch (\RuntimeException $e) { - $this->fail('A call to signal() is not expected to cause wait() to throw a RuntimeException'); - } + $process = $this->getProcess(self::$phpBin.' -r "echo \'foo\'; sleep(1); echo \'bar\';"'); + $process->run(function () use ($process) { + if ($process->isRunning()) { + $process->signal(defined('SIGKILL') ? SIGKILL : 9); + } + }); + $this->assertTrue(true, 'A call to signal() is not expected to cause wait() to throw a RuntimeException'); } public function testTermSignalTerminatesProcessCleanly() { $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.'); - try { - $process = $this->getProcess(self::$phpBin.' -r "echo \'foo\'; sleep(1); echo \'bar\';"'); - $process->run(function () use ($process) { - if ($process->isRunning()) { - $process->signal(defined('SIGTERM') ? SIGTERM : 15); - } - }); - } catch (\RuntimeException $e) { - $this->fail('A call to signal() is not expected to cause wait() to throw a RuntimeException'); - } + $process = $this->getProcess(self::$phpBin.' -r "echo \'foo\'; sleep(1); echo \'bar\';"'); + $process->run(function () use ($process) { + if ($process->isRunning()) { + $process->signal(defined('SIGTERM') ? SIGTERM : 15); + } + }); + $this->assertTrue(true, 'A call to signal() is not expected to cause wait() to throw a RuntimeException'); } public function testStopWithTimeoutIsActuallyWorking() From 5d7678e4326a574c3d3fa20eadaa59ad9a245dbf Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Thu, 26 Nov 2015 11:53:06 +0100 Subject: [PATCH 074/107] [Form] Deprecated setting "choices_as_values" to "false" --- .../Form/ChoiceList/ArrayChoiceList.php | 37 ++++ .../Form/Extension/Core/Type/ChoiceType.php | 9 + .../Form/Extension/Core/Type/CountryType.php | 3 +- .../Form/Extension/Core/Type/CurrencyType.php | 3 +- .../Form/Extension/Core/Type/DateType.php | 16 +- .../Form/Extension/Core/Type/LanguageType.php | 3 +- .../Form/Extension/Core/Type/LocaleType.php | 3 +- .../Form/Extension/Core/Type/TimeType.php | 9 +- .../Form/Extension/Core/Type/TimezoneType.php | 46 ++++- .../Tests/ChoiceList/ArrayChoiceListTest.php | 48 ++++- .../ChoiceToValueTransformerTest.php | 6 +- .../ChoicesToValuesTransformerTest.php | 6 +- .../Extension/Core/Type/ChoiceTypeTest.php | 178 +++++++++++++++--- 13 files changed, 314 insertions(+), 53 deletions(-) diff --git a/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php b/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php index 156735b81751e..b1e6b3bf06b5c 100644 --- a/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php +++ b/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php @@ -74,6 +74,12 @@ public function __construct($choices, $value = null) $choices = iterator_to_array($choices); } + if (null === $value && $this->castableToString($choices)) { + $value = function ($choice) { + return (string) $choice; + }; + } + if (null !== $value) { // If a deterministic value generator was passed, use it later $this->valueCallback = $value; @@ -207,4 +213,35 @@ protected function flatten(array $choices, $value, &$choicesByValues, &$keysByVa $structuredValues[$key] = $choiceValue; } } + + /** + * Checks whether the given choices can be cast to strings without + * generating duplicates. + * + * @param array $choices The choices. + * @param array|null $cache The cache for previously checked entries. Internal + * + * @return bool Returns true if the choices can be cast to strings and + * false otherwise. + */ + private function castableToString(array $choices, array &$cache = array()) + { + foreach ($choices as $choice) { + if (is_array($choice)) { + if (!$this->castableToString($choice, $cache)) { + return false; + } + + continue; + } elseif (!is_scalar($choice)) { + return false; + } elseif (isset($cache[(string) $choice])) { + return false; + } + + $cache[(string) $choice] = true; + } + + return true; + } } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index c325521d36174..de20b174abc03 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -281,6 +281,14 @@ public function configureOptions(OptionsResolver $resolver) return $choiceListFactory->createListFromChoices($choices, $options['choice_value']); }; + $choicesAsValuesNormalizer = function (Options $options, $choicesAsValues) { + if (true !== $choicesAsValues) { + @trigger_error('The value "false" for the "choices_as_values" option is deprecated since version 2.8 and will not be supported anymore in 3.0. Set this option to "true" and flip the contents of the "choices" option instead.', E_USER_DEPRECATED); + } + + return $choicesAsValues; + }; + $placeholderNormalizer = function (Options $options, $placeholder) { if (!is_object($options['empty_value']) || !$options['empty_value'] instanceof \Exception) { @trigger_error('The form option "empty_value" is deprecated since version 2.6 and will be removed in 3.0. Use "placeholder" instead.', E_USER_DEPRECATED); @@ -343,6 +351,7 @@ public function configureOptions(OptionsResolver $resolver) $resolver->setNormalizer('choice_list', $choiceListNormalizer); $resolver->setNormalizer('placeholder', $placeholderNormalizer); $resolver->setNormalizer('choice_translation_domain', $choiceTranslationDomainNormalizer); + $resolver->setNormalizer('choices_as_values', $choicesAsValuesNormalizer); $resolver->setAllowedTypes('choice_list', array('null', 'Symfony\Component\Form\ChoiceList\ChoiceListInterface', 'Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface')); $resolver->setAllowedTypes('choices', array('null', 'array', '\Traversable')); diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php b/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php index 45da8d509a56a..2f114d9aa2f6b 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php @@ -23,7 +23,8 @@ class CountryType extends AbstractType public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choices' => Intl::getRegionBundle()->getCountryNames(), + 'choices' => array_flip(Intl::getRegionBundle()->getCountryNames()), + 'choices_as_values' => true, 'choice_translation_domain' => false, )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php b/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php index 7a26316e01a47..1608890d4a9ad 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php @@ -23,7 +23,8 @@ class CurrencyType extends AbstractType public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choices' => Intl::getCurrencyBundle()->getCurrencyNames(), + 'choices' => array_flip(Intl::getCurrencyBundle()->getCurrencyNames()), + 'choices_as_values' => true, 'choice_translation_domain' => false, )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php index df5835b8b37f6..921b24ff0fd69 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php @@ -93,12 +93,15 @@ public function buildForm(FormBuilderInterface $builder, array $options) if ('choice' === $options['widget']) { // Only pass a subset of the options to children $yearOptions['choices'] = $this->formatTimestamps($formatter, '/y+/', $this->listYears($options['years'])); + $yearOptions['choices_as_values'] = true; $yearOptions['placeholder'] = $options['placeholder']['year']; $yearOptions['choice_translation_domain'] = $options['choice_translation_domain']['year']; $monthOptions['choices'] = $this->formatTimestamps($formatter, '/[M|L]+/', $this->listMonths($options['months'])); + $monthOptions['choices_as_values'] = true; $monthOptions['placeholder'] = $options['placeholder']['month']; $monthOptions['choice_translation_domain'] = $options['choice_translation_domain']['month']; $dayOptions['choices'] = $this->formatTimestamps($formatter, '/d+/', $this->listDays($options['days'])); + $dayOptions['choices_as_values'] = true; $dayOptions['placeholder'] = $options['placeholder']['day']; $dayOptions['choice_translation_domain'] = $options['choice_translation_domain']['day']; } @@ -297,6 +300,7 @@ private function formatTimestamps(\IntlDateFormatter $formatter, $regex, array $ { $pattern = $formatter->getPattern(); $timezone = $formatter->getTimezoneId(); + $formattedTimestamps = array(); if ($setTimeZone = PHP_VERSION_ID >= 50500 || method_exists($formatter, 'setTimeZone')) { $formatter->setTimeZone('UTC'); @@ -307,8 +311,8 @@ private function formatTimestamps(\IntlDateFormatter $formatter, $regex, array $ if (preg_match($regex, $pattern, $matches)) { $formatter->setPattern($matches[0]); - foreach ($timestamps as $key => $timestamp) { - $timestamps[$key] = $formatter->format($timestamp); + foreach ($timestamps as $timestamp => $choice) { + $formattedTimestamps[$formatter->format($timestamp)] = $choice; } // I'd like to clone the formatter above, but then we get a @@ -322,7 +326,7 @@ private function formatTimestamps(\IntlDateFormatter $formatter, $regex, array $ $formatter->setTimeZoneId($timezone); } - return $timestamps; + return $formattedTimestamps; } private function listYears(array $years) @@ -331,7 +335,7 @@ private function listYears(array $years) foreach ($years as $year) { if (false !== $y = gmmktime(0, 0, 0, 6, 15, $year)) { - $result[$year] = $y; + $result[$y] = $year; } } @@ -343,7 +347,7 @@ private function listMonths(array $months) $result = array(); foreach ($months as $month) { - $result[$month] = gmmktime(0, 0, 0, $month, 15); + $result[gmmktime(0, 0, 0, $month, 15)] = $month; } return $result; @@ -354,7 +358,7 @@ private function listDays(array $days) $result = array(); foreach ($days as $day) { - $result[$day] = gmmktime(0, 0, 0, 5, $day); + $result[gmmktime(0, 0, 0, 5, $day)] = $day; } return $result; diff --git a/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php b/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php index 6fc6031af11c2..da5cbc75e435f 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php @@ -23,7 +23,8 @@ class LanguageType extends AbstractType public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choices' => Intl::getLanguageBundle()->getLanguageNames(), + 'choices' => array_flip(Intl::getLanguageBundle()->getLanguageNames()), + 'choices_as_values' => true, 'choice_translation_domain' => false, )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php b/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php index a6d42f8c917ee..25ae92ded903e 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php @@ -23,7 +23,8 @@ class LocaleType extends AbstractType public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choices' => Intl::getLocaleBundle()->getLocaleNames(), + 'choices' => array_flip(Intl::getLocaleBundle()->getLocaleNames()), + 'choices_as_values' => true, 'choice_translation_domain' => false, )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php index ed9132863a0f8..1ab2030b1d174 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php @@ -63,20 +63,22 @@ public function buildForm(FormBuilderInterface $builder, array $options) $hours = $minutes = array(); foreach ($options['hours'] as $hour) { - $hours[$hour] = str_pad($hour, 2, '0', STR_PAD_LEFT); + $hours[str_pad($hour, 2, '0', STR_PAD_LEFT)] = $hour; } // Only pass a subset of the options to children $hourOptions['choices'] = $hours; + $hourOptions['choices_as_values'] = true; $hourOptions['placeholder'] = $options['placeholder']['hour']; $hourOptions['choice_translation_domain'] = $options['choice_translation_domain']['hour']; if ($options['with_minutes']) { foreach ($options['minutes'] as $minute) { - $minutes[$minute] = str_pad($minute, 2, '0', STR_PAD_LEFT); + $minutes[str_pad($minute, 2, '0', STR_PAD_LEFT)] = $minute; } $minuteOptions['choices'] = $minutes; + $minuteOptions['choices_as_values'] = true; $minuteOptions['placeholder'] = $options['placeholder']['minute']; $minuteOptions['choice_translation_domain'] = $options['choice_translation_domain']['minute']; } @@ -85,10 +87,11 @@ public function buildForm(FormBuilderInterface $builder, array $options) $seconds = array(); foreach ($options['seconds'] as $second) { - $seconds[$second] = str_pad($second, 2, '0', STR_PAD_LEFT); + $seconds[str_pad($second, 2, '0', STR_PAD_LEFT)] = $second; } $secondOptions['choices'] = $seconds; + $secondOptions['choices_as_values'] = true; $secondOptions['placeholder'] = $options['placeholder']['second']; $secondOptions['choice_translation_domain'] = $options['choice_translation_domain']['second']; } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php index 3277a1838636c..a3ec7bf744821 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php @@ -23,13 +23,21 @@ class TimezoneType extends AbstractType */ private static $timezones; + /** + * Stores the available timezone choices. + * + * @var array + */ + private static $flippedTimezones; + /** * {@inheritdoc} */ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choices' => self::getTimezones(), + 'choices' => self::getFlippedTimezones(), + 'choices_as_values' => true, 'choice_translation_domain' => false, )); } @@ -93,4 +101,40 @@ public static function getTimezones() return static::$timezones; } + + /** + * Returns the timezone choices. + * + * The choices are generated from the ICU function + * \DateTimeZone::listIdentifiers(). They are cached during a single request, + * so multiple timezone fields on the same page don't lead to unnecessary + * overhead. + * + * @return array The timezone choices + */ + private static function getFlippedTimezones() + { + if (null === self::$timezones) { + self::$timezones = array(); + + foreach (\DateTimeZone::listIdentifiers() as $timezone) { + $parts = explode('/', $timezone); + + if (count($parts) > 2) { + $region = $parts[0]; + $name = $parts[1].' - '.$parts[2]; + } elseif (count($parts) > 1) { + $region = $parts[0]; + $name = $parts[1]; + } else { + $region = 'Other'; + $name = $parts[0]; + } + + self::$timezones[$region][str_replace('_', ' ', $name)] = $timezone; + } + } + + return self::$timezones; + } } diff --git a/src/Symfony/Component/Form/Tests/ChoiceList/ArrayChoiceListTest.php b/src/Symfony/Component/Form/Tests/ChoiceList/ArrayChoiceListTest.php index 50d4df8a9b7cb..03cb7fce5705a 100644 --- a/src/Symfony/Component/Form/Tests/ChoiceList/ArrayChoiceListTest.php +++ b/src/Symfony/Component/Form/Tests/ChoiceList/ArrayChoiceListTest.php @@ -65,6 +65,40 @@ public function testCreateChoiceListWithValueCallback() $this->assertSame(array(1 => ':foo', 2 => ':baz'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => 'baz'))); } + public function testCreateChoiceListWithoutValueCallbackAndDuplicateFreeToStringChoices() + { + $choiceList = new ArrayChoiceList(array(2 => 'foo', 7 => 'bar', 10 => 123)); + + $this->assertSame(array('foo', 'bar', '123'), $choiceList->getValues()); + $this->assertSame(array('foo' => 'foo', 'bar' => 'bar', '123' => 123), $choiceList->getChoices()); + $this->assertSame(array('foo' => 2, 'bar' => 7, '123' => 10), $choiceList->getOriginalKeys()); + $this->assertSame(array(1 => 'foo', 2 => 123), $choiceList->getChoicesForValues(array(1 => 'foo', 2 => '123'))); + $this->assertSame(array(1 => 'foo', 2 => '123'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => 123))); + } + + public function testCreateChoiceListWithoutValueCallbackAndToStringDuplicates() + { + $choiceList = new ArrayChoiceList(array(2 => 'foo', 7 => '123', 10 => 123)); + + $this->assertSame(array('0', '1', '2'), $choiceList->getValues()); + $this->assertSame(array('0' => 'foo', '1' => '123', '2' => 123), $choiceList->getChoices()); + $this->assertSame(array('0' => 2, '1' => 7, '2' => 10), $choiceList->getOriginalKeys()); + $this->assertSame(array(1 => 'foo', 2 => 123), $choiceList->getChoicesForValues(array(1 => '0', 2 => '2'))); + $this->assertSame(array(1 => '0', 2 => '2'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => 123))); + } + + public function testCreateChoiceListWithoutValueCallbackAndMixedChoices() + { + $object = new \stdClass(); + $choiceList = new ArrayChoiceList(array(2 => 'foo', 5 => array(7 => '123'), 10 => $object)); + + $this->assertSame(array('0', '1', '2'), $choiceList->getValues()); + $this->assertSame(array('0' => 'foo', '1' => '123', '2' => $object), $choiceList->getChoices()); + $this->assertSame(array('0' => 2, '1' => 7, '2' => 10), $choiceList->getOriginalKeys()); + $this->assertSame(array(1 => 'foo', 2 => $object), $choiceList->getChoicesForValues(array(1 => '0', 2 => '2'))); + $this->assertSame(array(1 => '0', 2 => '2'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => $object))); + } + public function testCreateChoiceListWithGroupedChoices() { $choiceList = new ArrayChoiceList(array( @@ -72,15 +106,15 @@ public function testCreateChoiceListWithGroupedChoices() 'Group 2' => array('C' => 'c', 'D' => 'd'), )); - $this->assertSame(array('0', '1', '2', '3'), $choiceList->getValues()); + $this->assertSame(array('a', 'b', 'c', 'd'), $choiceList->getValues()); $this->assertSame(array( - 'Group 1' => array('A' => '0', 'B' => '1'), - 'Group 2' => array('C' => '2', 'D' => '3'), + 'Group 1' => array('A' => 'a', 'B' => 'b'), + 'Group 2' => array('C' => 'c', 'D' => 'd'), ), $choiceList->getStructuredValues()); - $this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd'), $choiceList->getChoices()); - $this->assertSame(array(0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D'), $choiceList->getOriginalKeys()); - $this->assertSame(array(1 => 'a', 2 => 'b'), $choiceList->getChoicesForValues(array(1 => '0', 2 => '1'))); - $this->assertSame(array(1 => '0', 2 => '1'), $choiceList->getValuesForChoices(array(1 => 'a', 2 => 'b'))); + $this->assertSame(array('a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd'), $choiceList->getChoices()); + $this->assertSame(array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D'), $choiceList->getOriginalKeys()); + $this->assertSame(array(1 => 'a', 2 => 'b'), $choiceList->getChoicesForValues(array(1 => 'a', 2 => 'b'))); + $this->assertSame(array(1 => 'a', 2 => 'b'), $choiceList->getValuesForChoices(array(1 => 'a', 2 => 'b'))); } public function testCompareChoicesByIdentityByDefault() diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php index c58d072f47434..f60ef05abc507 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php @@ -20,7 +20,7 @@ class ChoiceToValueTransformerTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $list = new ArrayChoiceList(array('', 0, 'X')); + $list = new ArrayChoiceList(array('', false, 'X')); $this->transformer = new ChoiceToValueTransformer($list); } @@ -35,7 +35,7 @@ public function transformProvider() return array( // more extensive test set can be found in FormUtilTest array('', '0'), - array(0, '1'), + array(false, '1'), ); } @@ -53,7 +53,7 @@ public function reverseTransformProvider() // values are expected to be valid choice keys already and stay // the same array('0', ''), - array('1', 0), + array('1', false), array('2', 'X'), ); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php index a7dc40aca225f..f7747aaccd0f1 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php @@ -20,7 +20,7 @@ class ChoicesToValuesTransformerTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $list = new ArrayChoiceList(array('A', 'B', 'C')); + $list = new ArrayChoiceList(array('', false, 'X')); $this->transformer = new ChoicesToValuesTransformer($list); } @@ -31,7 +31,7 @@ protected function tearDown() public function testTransform() { - $in = array('A', 'B', 'C'); + $in = array('', false, 'X'); $out = array('0', '1', '2'); $this->assertSame($out, $this->transformer->transform($in)); @@ -54,7 +54,7 @@ public function testReverseTransform() { // values are expected to be valid choices and stay the same $in = array('0', '1', '2'); - $out = array('A', 'B', 'C'); + $out = array('', false, 'X'); $this->assertSame($out, $this->transformer->reverseTransform($in)); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php index a8bf8edf12655..732c2e86362a0 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -18,14 +18,14 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase { private $choices = array( - 'a' => 'Bernhard', - 'b' => 'Fabien', - 'c' => 'Kris', - 'd' => 'Jon', - 'e' => 'Roman', + 'Bernhard' => 'a', + 'Fabien' => 'b', + 'Kris' => 'c', + 'Jon' => 'd', + 'Roman' => 'e', ); - private $numericChoices = array( + private $numericChoicesFlipped = array( 0 => 'Bernhard', 1 => 'Fabien', 2 => 'Kris', @@ -36,6 +36,18 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase private $objectChoices; protected $groupedChoices = array( + 'Symfony' => array( + 'Bernhard' => 'a', + 'Fabien' => 'b', + 'Kris' => 'c', + ), + 'Doctrine' => array( + 'Jon' => 'd', + 'Roman' => 'e', + ), + ); + + protected $groupedChoicesFlipped = array( 'Symfony' => array( 'a' => 'Bernhard', 'b' => 'Fabien', @@ -109,7 +121,9 @@ public function testChoiceLoaderOptionExpectsChoiceLoaderInterface() public function testChoiceListAndChoicesCanBeEmpty() { - $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType'); + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'choices_as_values' => true, + )); } public function testExpandedChoicesOptionsTurnIntoChildren() @@ -117,6 +131,20 @@ public function testExpandedChoicesOptionsTurnIntoChildren() $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, + )); + + $this->assertCount(count($this->choices), $form, 'Each choice should become a new field'); + } + + /** + * @group legacy + */ + public function testExpandedFlippedChoicesOptionsTurnIntoChildren() + { + $form = $this->factory->create('choice', null, array( + 'expanded' => true, + 'choices' => array_flip($this->choices), )); $this->assertCount(count($this->choices), $form, 'Each choice should become a new field'); @@ -129,6 +157,7 @@ public function testPlaceholderPresentOnNonRequiredExpandedSingleChoice() 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $this->assertTrue(isset($form['placeholder'])); @@ -142,6 +171,7 @@ public function testPlaceholderNotPresentIfRequired() 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $this->assertFalse(isset($form['placeholder'])); @@ -155,6 +185,7 @@ public function testPlaceholderNotPresentIfMultiple() 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $this->assertFalse(isset($form['placeholder'])); @@ -168,9 +199,10 @@ public function testPlaceholderNotPresentIfEmptyChoice() 'expanded' => true, 'required' => false, 'choices' => array( - '' => 'Empty', - 1 => 'Not empty', + 'Empty' => '', + 'Not empty' => 1, ), + 'choices_as_values' => true, )); $this->assertFalse(isset($form['placeholder'])); @@ -182,6 +214,29 @@ public function testExpandedChoicesOptionsAreFlattened() $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'expanded' => true, 'choices' => $this->groupedChoices, + 'choices_as_values' => true, + )); + + $flattened = array(); + foreach ($this->groupedChoices as $choices) { + $flattened = array_merge($flattened, array_keys($choices)); + } + + $this->assertCount($form->count(), $flattened, 'Each nested choice should become a new field, not the groups'); + + foreach ($flattened as $value => $choice) { + $this->assertTrue($form->has($value), 'Flattened choice is named after it\'s value'); + } + } + + /** + * @group legacy + */ + public function testExpandedChoicesFlippedOptionsAreFlattened() + { + $form = $this->factory->create('choice', null, array( + 'expanded' => true, + 'choices' => $this->groupedChoicesFlipped, )); $flattened = array(); @@ -229,6 +284,7 @@ public function testExpandedCheckboxesAreNeverRequired() 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); foreach ($form as $child) { @@ -243,6 +299,7 @@ public function testExpandedRadiosAreRequiredIfChoiceChildIsRequired() 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); foreach ($form as $child) { @@ -257,6 +314,7 @@ public function testExpandedRadiosAreNotRequiredIfChoiceChildIsNotRequired() 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); foreach ($form as $child) { @@ -270,6 +328,7 @@ public function testSubmitSingleNonExpanded() 'multiple' => false, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('b'); @@ -285,6 +344,7 @@ public function testSubmitSingleNonExpandedInvalidChoice() 'multiple' => false, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('foobar'); @@ -300,6 +360,7 @@ public function testSubmitSingleNonExpandedNull() 'multiple' => false, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(null); @@ -318,6 +379,7 @@ public function testSubmitSingleNonExpandedNullNoChoices() 'multiple' => false, 'expanded' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(null); @@ -333,6 +395,7 @@ public function testSubmitSingleNonExpandedEmpty() 'multiple' => false, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(''); @@ -348,8 +411,9 @@ public function testSubmitSingleNonExpandedEmptyExplicitEmptyChoice() 'multiple' => false, 'expanded' => false, 'choices' => array( - 'EMPTY_CHOICE' => 'Empty', + 'Empty' => 'EMPTY_CHOICE', ), + 'choices_as_values' => true, 'choice_value' => function () { return ''; }, @@ -371,6 +435,7 @@ public function testSubmitSingleNonExpandedEmptyNoChoices() 'multiple' => false, 'expanded' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(''); @@ -386,6 +451,7 @@ public function testSubmitSingleNonExpandedFalse() 'multiple' => false, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(false); @@ -404,6 +470,7 @@ public function testSubmitSingleNonExpandedFalseNoChoices() 'multiple' => false, 'expanded' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(false); @@ -465,6 +532,7 @@ public function testSubmitMultipleNonExpanded() 'multiple' => true, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array('a', 'b')); @@ -480,6 +548,7 @@ public function testSubmitMultipleNonExpandedEmpty() 'multiple' => true, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array()); @@ -498,6 +567,7 @@ public function testSubmitMultipleNonExpandedEmptyNoChoices() 'multiple' => true, 'expanded' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(array()); @@ -513,6 +583,7 @@ public function testSubmitMultipleNonExpandedInvalidScalarChoice() 'multiple' => true, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('foobar'); @@ -528,6 +599,7 @@ public function testSubmitMultipleNonExpandedInvalidArrayChoice() 'multiple' => true, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array('a', 'foobar')); @@ -588,6 +660,7 @@ public function testSubmitSingleExpandedRequired() 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('b'); @@ -616,6 +689,7 @@ public function testSubmitSingleExpandedRequiredInvalidChoice() 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('foobar'); @@ -644,6 +718,7 @@ public function testSubmitSingleExpandedNonRequired() 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('b'); @@ -674,6 +749,7 @@ public function testSubmitSingleExpandedNonRequiredInvalidChoice() 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('foobar'); @@ -702,6 +778,7 @@ public function testSubmitSingleExpandedRequiredNull() 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(null); @@ -733,6 +810,7 @@ public function testSubmitSingleExpandedRequiredNullNoChoices() 'expanded' => true, 'required' => true, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(null); @@ -750,6 +828,7 @@ public function testSubmitSingleExpandedRequiredEmpty() 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(''); @@ -781,6 +860,7 @@ public function testSubmitSingleExpandedRequiredEmptyNoChoices() 'expanded' => true, 'required' => true, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(''); @@ -798,6 +878,7 @@ public function testSubmitSingleExpandedRequiredFalse() 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(false); @@ -829,6 +910,7 @@ public function testSubmitSingleExpandedRequiredFalseNoChoices() 'expanded' => true, 'required' => true, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(false); @@ -846,6 +928,7 @@ public function testSubmitSingleExpandedNonRequiredNull() 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(null); @@ -879,6 +962,7 @@ public function testSubmitSingleExpandedNonRequiredNullNoChoices() 'expanded' => true, 'required' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(null); @@ -896,6 +980,7 @@ public function testSubmitSingleExpandedNonRequiredEmpty() 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(''); @@ -929,6 +1014,7 @@ public function testSubmitSingleExpandedNonRequiredEmptyNoChoices() 'expanded' => true, 'required' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(''); @@ -946,6 +1032,7 @@ public function testSubmitSingleExpandedNonRequiredFalse() 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(false); @@ -979,6 +1066,7 @@ public function testSubmitSingleExpandedNonRequiredFalseNoChoices() 'expanded' => true, 'required' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(false); @@ -995,9 +1083,10 @@ public function testSubmitSingleExpandedWithEmptyChild() 'multiple' => false, 'expanded' => true, 'choices' => array( - '' => 'Empty', - 1 => 'Not empty', + 'Empty' => '', + 'Not empty' => 1, ), + 'choices_as_values' => true, )); $form->submit(''); @@ -1075,12 +1164,15 @@ public function testLegacySubmitSingleExpandedObjectChoices() $this->assertNull($form[4]->getViewData()); } - public function testSubmitSingleExpandedNumericChoices() + /** + * @group legacy + */ + public function testSubmitSingleExpandedNumericChoicesFlipped() { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'multiple' => false, 'expanded' => true, - 'choices' => $this->numericChoices, + 'choices' => $this->numericChoicesFlipped, )); $form->submit('1'); @@ -1106,6 +1198,7 @@ public function testSubmitMultipleExpanded() 'multiple' => true, 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array('a', 'c')); @@ -1133,6 +1226,7 @@ public function testSubmitMultipleExpandedInvalidScalarChoice() 'multiple' => true, 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('foobar'); @@ -1160,6 +1254,7 @@ public function testSubmitMultipleExpandedInvalidArrayChoice() 'multiple' => true, 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array('a', 'foobar')); @@ -1187,6 +1282,7 @@ public function testSubmitMultipleExpandedEmpty() 'multiple' => true, 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array()); @@ -1215,6 +1311,7 @@ public function testSubmitMultipleExpandedEmptyNoChoices() 'multiple' => true, 'expanded' => true, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(array()); @@ -1229,10 +1326,11 @@ public function testSubmitMultipleExpandedWithEmptyChild() 'multiple' => true, 'expanded' => true, 'choices' => array( - '' => 'Empty', - 1 => 'Not Empty', - 2 => 'Not Empty 2', + 'Empty' => '', + 'Not Empty' => 1, + 'Not Empty 2' => 2, ), + 'choices_as_values' => true, )); $form->submit(array('', '2')); @@ -1312,12 +1410,15 @@ public function testLegacySubmitMultipleExpandedObjectChoices() $this->assertNull($form[4]->getViewData()); } + /** + * @group legacy + */ public function testSubmitMultipleExpandedNumericChoices() { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'multiple' => true, 'expanded' => true, - 'choices' => $this->numericChoices, + 'choices' => $this->numericChoicesFlipped, )); $form->submit(array('1', '2')); @@ -1373,16 +1474,18 @@ public function testMultipleSelectedObjectChoices() $this->assertFalse($selectedChecker($view->vars['choices'][1]->value, $view->vars['value'])); } - /* + /** * We need this functionality to create choice fields for Boolean types, - * e.g. false => 'No', true => 'Yes' + * e.g. false => 'No', true => 'Yes'. + * + * @group legacy */ public function testSetDataSingleNonExpandedAcceptsBoolean() { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'multiple' => false, 'expanded' => false, - 'choices' => $this->numericChoices, + 'choices' => $this->numericChoicesFlipped, )); $form->setData(false); @@ -1392,12 +1495,15 @@ public function testSetDataSingleNonExpandedAcceptsBoolean() $this->assertTrue($form->isSynchronized()); } + /** + * @group legacy + */ public function testSetDataMultipleNonExpandedAcceptsBoolean() { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'multiple' => true, 'expanded' => false, - 'choices' => $this->numericChoices, + 'choices' => $this->numericChoicesFlipped, )); $form->setData(array(false, true)); @@ -1411,6 +1517,7 @@ public function testPassRequiredToView() { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1422,6 +1529,7 @@ public function testPassNonRequiredToView() $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1433,6 +1541,7 @@ public function testPassMultipleToView() $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'multiple' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1444,6 +1553,7 @@ public function testPassExpandedToView() $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1454,6 +1564,7 @@ public function testPassChoiceTranslationDomainToView() { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1464,6 +1575,7 @@ public function testChoiceTranslationDomainWithTrueValueToView() { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'choices' => $this->choices, + 'choices_as_values' => true, 'choice_translation_domain' => true, )); $view = $form->createView(); @@ -1475,6 +1587,7 @@ public function testDefaultChoiceTranslationDomainIsSameAsTranslationDomainToVie { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'choices' => $this->choices, + 'choices_as_values' => true, 'translation_domain' => 'foo', )); $view = $form->createView(); @@ -1488,7 +1601,10 @@ public function testInheritChoiceTranslationDomainFromParent() ->createNamedBuilder('parent', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( 'translation_domain' => 'domain', )) - ->add('child', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType') + ->add('child', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array( + 'choices' => array(), + 'choices_as_values' => true, + )) ->getForm() ->createView(); @@ -1501,6 +1617,7 @@ public function testPlaceholderIsNullByDefaultIfRequired() 'multiple' => false, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1513,6 +1630,7 @@ public function testPlaceholderIsEmptyStringByDefaultIfNotRequired() 'multiple' => false, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1530,6 +1648,7 @@ public function testPassPlaceholderToView($multiple, $expanded, $required, $plac 'required' => $required, 'placeholder' => $placeholder, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1549,6 +1668,7 @@ public function testPassEmptyValueBC($multiple, $expanded, $required, $placehold 'required' => $required, 'empty_value' => $placeholder, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1568,7 +1688,8 @@ public function testDontPassPlaceholderIfContainedInChoices($multiple, $expanded 'expanded' => $expanded, 'required' => $required, 'placeholder' => $placeholder, - 'choices' => array('a' => 'A', '' => 'Empty'), + 'choices' => array('A' => 'a', 'Empty' => ''), + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1622,9 +1743,10 @@ public function getOptionsWithPlaceholder() public function testPassChoicesToView() { - $choices = array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D'); + $choices = array('A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd'); $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'choices' => $choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1638,9 +1760,10 @@ public function testPassChoicesToView() public function testPassPreferredChoicesToView() { - $choices = array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D'); + $choices = array('A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd'); $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'choices' => $choices, + 'choices_as_values' => true, 'preferred_choices' => array('b', 'd'), )); $view = $form->createView(); @@ -1659,6 +1782,7 @@ public function testPassHierarchicalChoicesToView() { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'choices' => $this->groupedChoices, + 'choices_as_values' => true, 'preferred_choices' => array('b', 'd'), )); $view = $form->createView(); @@ -1710,6 +1834,7 @@ public function testAdjustFullNameForMultipleNonExpanded() 'multiple' => true, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1721,6 +1846,7 @@ public function testInitializeWithEmptyChoices() { $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'choices' => array(), + 'choices_as_values' => true, )); } From 3ab8189080738bbe55227698faf0334f35c64081 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Thu, 26 Nov 2015 11:53:06 +0100 Subject: [PATCH 075/107] [Form] Deprecated setting "choices_as_values" to "false" --- .../Form/ChoiceList/ArrayChoiceList.php | 37 ++++ .../Form/Extension/Core/Type/CountryType.php | 3 +- .../Form/Extension/Core/Type/CurrencyType.php | 3 +- .../Form/Extension/Core/Type/DateType.php | 16 +- .../Form/Extension/Core/Type/LanguageType.php | 3 +- .../Form/Extension/Core/Type/LocaleType.php | 3 +- .../Form/Extension/Core/Type/TimeType.php | 9 +- .../Form/Extension/Core/Type/TimezoneType.php | 46 ++++- .../Tests/ChoiceList/ArrayChoiceListTest.php | 48 ++++- .../ChoiceToValueTransformerTest.php | 6 +- .../ChoicesToValuesTransformerTest.php | 6 +- .../Extension/Core/Type/ChoiceTypeTest.php | 169 +++++++++++++++--- 12 files changed, 298 insertions(+), 51 deletions(-) diff --git a/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php b/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php index 156735b81751e..b1e6b3bf06b5c 100644 --- a/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php +++ b/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php @@ -74,6 +74,12 @@ public function __construct($choices, $value = null) $choices = iterator_to_array($choices); } + if (null === $value && $this->castableToString($choices)) { + $value = function ($choice) { + return (string) $choice; + }; + } + if (null !== $value) { // If a deterministic value generator was passed, use it later $this->valueCallback = $value; @@ -207,4 +213,35 @@ protected function flatten(array $choices, $value, &$choicesByValues, &$keysByVa $structuredValues[$key] = $choiceValue; } } + + /** + * Checks whether the given choices can be cast to strings without + * generating duplicates. + * + * @param array $choices The choices. + * @param array|null $cache The cache for previously checked entries. Internal + * + * @return bool Returns true if the choices can be cast to strings and + * false otherwise. + */ + private function castableToString(array $choices, array &$cache = array()) + { + foreach ($choices as $choice) { + if (is_array($choice)) { + if (!$this->castableToString($choice, $cache)) { + return false; + } + + continue; + } elseif (!is_scalar($choice)) { + return false; + } elseif (isset($cache[(string) $choice])) { + return false; + } + + $cache[(string) $choice] = true; + } + + return true; + } } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php b/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php index 30ee0a0f9e89c..19395a82fe5af 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php @@ -23,7 +23,8 @@ class CountryType extends AbstractType public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choices' => Intl::getRegionBundle()->getCountryNames(), + 'choices' => array_flip(Intl::getRegionBundle()->getCountryNames()), + 'choices_as_values' => true, 'choice_translation_domain' => false, )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php b/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php index b473d139e6566..71b660f9bed63 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php @@ -23,7 +23,8 @@ class CurrencyType extends AbstractType public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choices' => Intl::getCurrencyBundle()->getCurrencyNames(), + 'choices' => array_flip(Intl::getCurrencyBundle()->getCurrencyNames()), + 'choices_as_values' => true, 'choice_translation_domain' => false, )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php index fb3e9a7182fc8..b1eb4382ed3ec 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php @@ -88,10 +88,13 @@ public function buildForm(FormBuilderInterface $builder, array $options) if ('choice' === $options['widget']) { // Only pass a subset of the options to children $yearOptions['choices'] = $this->formatTimestamps($formatter, '/y+/', $this->listYears($options['years'])); + $yearOptions['choices_as_values'] = true; $yearOptions['placeholder'] = $options['placeholder']['year']; $monthOptions['choices'] = $this->formatTimestamps($formatter, '/[M|L]+/', $this->listMonths($options['months'])); + $monthOptions['choices_as_values'] = true; $monthOptions['placeholder'] = $options['placeholder']['month']; $dayOptions['choices'] = $this->formatTimestamps($formatter, '/d+/', $this->listDays($options['days'])); + $dayOptions['choices_as_values'] = true; $dayOptions['placeholder'] = $options['placeholder']['day']; } @@ -262,6 +265,7 @@ private function formatTimestamps(\IntlDateFormatter $formatter, $regex, array $ { $pattern = $formatter->getPattern(); $timezone = $formatter->getTimezoneId(); + $formattedTimestamps = array(); if ($setTimeZone = PHP_VERSION_ID >= 50500 || method_exists($formatter, 'setTimeZone')) { $formatter->setTimeZone('UTC'); @@ -272,8 +276,8 @@ private function formatTimestamps(\IntlDateFormatter $formatter, $regex, array $ if (preg_match($regex, $pattern, $matches)) { $formatter->setPattern($matches[0]); - foreach ($timestamps as $key => $timestamp) { - $timestamps[$key] = $formatter->format($timestamp); + foreach ($timestamps as $timestamp => $choice) { + $formattedTimestamps[$formatter->format($timestamp)] = $choice; } // I'd like to clone the formatter above, but then we get a @@ -287,7 +291,7 @@ private function formatTimestamps(\IntlDateFormatter $formatter, $regex, array $ $formatter->setTimeZoneId($timezone); } - return $timestamps; + return $formattedTimestamps; } private function listYears(array $years) @@ -296,7 +300,7 @@ private function listYears(array $years) foreach ($years as $year) { if (false !== $y = gmmktime(0, 0, 0, 6, 15, $year)) { - $result[$year] = $y; + $result[$y] = $year; } } @@ -308,7 +312,7 @@ private function listMonths(array $months) $result = array(); foreach ($months as $month) { - $result[$month] = gmmktime(0, 0, 0, $month, 15); + $result[gmmktime(0, 0, 0, $month, 15)] = $month; } return $result; @@ -319,7 +323,7 @@ private function listDays(array $days) $result = array(); foreach ($days as $day) { - $result[$day] = gmmktime(0, 0, 0, 5, $day); + $result[gmmktime(0, 0, 0, 5, $day)] = $day; } return $result; diff --git a/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php b/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php index 9d071eb8b03ee..1fc0ed1b676f7 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php @@ -23,7 +23,8 @@ class LanguageType extends AbstractType public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choices' => Intl::getLanguageBundle()->getLanguageNames(), + 'choices' => array_flip(Intl::getLanguageBundle()->getLanguageNames()), + 'choices_as_values' => true, 'choice_translation_domain' => false, )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php b/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php index f09f5a62f1e29..1631dc431ad7b 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php @@ -23,7 +23,8 @@ class LocaleType extends AbstractType public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choices' => Intl::getLocaleBundle()->getLocaleNames(), + 'choices' => array_flip(Intl::getLocaleBundle()->getLocaleNames()), + 'choices_as_values' => true, 'choice_translation_domain' => false, )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php index 8002f0b4ee7c1..57c9a44c8f7e5 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php @@ -58,19 +58,21 @@ public function buildForm(FormBuilderInterface $builder, array $options) $hours = $minutes = array(); foreach ($options['hours'] as $hour) { - $hours[$hour] = str_pad($hour, 2, '0', STR_PAD_LEFT); + $hours[str_pad($hour, 2, '0', STR_PAD_LEFT)] = $hour; } // Only pass a subset of the options to children $hourOptions['choices'] = $hours; + $hourOptions['choices_as_values'] = true; $hourOptions['placeholder'] = $options['placeholder']['hour']; if ($options['with_minutes']) { foreach ($options['minutes'] as $minute) { - $minutes[$minute] = str_pad($minute, 2, '0', STR_PAD_LEFT); + $minutes[str_pad($minute, 2, '0', STR_PAD_LEFT)] = $minute; } $minuteOptions['choices'] = $minutes; + $minuteOptions['choices_as_values'] = true; $minuteOptions['placeholder'] = $options['placeholder']['minute']; } @@ -78,10 +80,11 @@ public function buildForm(FormBuilderInterface $builder, array $options) $seconds = array(); foreach ($options['seconds'] as $second) { - $seconds[$second] = str_pad($second, 2, '0', STR_PAD_LEFT); + $seconds[str_pad($second, 2, '0', STR_PAD_LEFT)] = $second; } $secondOptions['choices'] = $seconds; + $secondOptions['choices_as_values'] = true; $secondOptions['placeholder'] = $options['placeholder']['second']; } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php index 82c07e2f121ba..13c27f9da8c7f 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php @@ -23,13 +23,21 @@ class TimezoneType extends AbstractType */ private static $timezones; + /** + * Stores the available timezone choices. + * + * @var array + */ + private static $flippedTimezones; + /** * {@inheritdoc} */ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choices' => self::getTimezones(), + 'choices' => self::getFlippedTimezones(), + 'choices_as_values' => true, 'choice_translation_domain' => false, )); } @@ -85,4 +93,40 @@ public static function getTimezones() return static::$timezones; } + + /** + * Returns the timezone choices. + * + * The choices are generated from the ICU function + * \DateTimeZone::listIdentifiers(). They are cached during a single request, + * so multiple timezone fields on the same page don't lead to unnecessary + * overhead. + * + * @return array The timezone choices + */ + private static function getFlippedTimezones() + { + if (null === self::$timezones) { + self::$timezones = array(); + + foreach (\DateTimeZone::listIdentifiers() as $timezone) { + $parts = explode('/', $timezone); + + if (count($parts) > 2) { + $region = $parts[0]; + $name = $parts[1].' - '.$parts[2]; + } elseif (count($parts) > 1) { + $region = $parts[0]; + $name = $parts[1]; + } else { + $region = 'Other'; + $name = $parts[0]; + } + + self::$timezones[$region][str_replace('_', ' ', $name)] = $timezone; + } + } + + return self::$timezones; + } } diff --git a/src/Symfony/Component/Form/Tests/ChoiceList/ArrayChoiceListTest.php b/src/Symfony/Component/Form/Tests/ChoiceList/ArrayChoiceListTest.php index 50d4df8a9b7cb..03cb7fce5705a 100644 --- a/src/Symfony/Component/Form/Tests/ChoiceList/ArrayChoiceListTest.php +++ b/src/Symfony/Component/Form/Tests/ChoiceList/ArrayChoiceListTest.php @@ -65,6 +65,40 @@ public function testCreateChoiceListWithValueCallback() $this->assertSame(array(1 => ':foo', 2 => ':baz'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => 'baz'))); } + public function testCreateChoiceListWithoutValueCallbackAndDuplicateFreeToStringChoices() + { + $choiceList = new ArrayChoiceList(array(2 => 'foo', 7 => 'bar', 10 => 123)); + + $this->assertSame(array('foo', 'bar', '123'), $choiceList->getValues()); + $this->assertSame(array('foo' => 'foo', 'bar' => 'bar', '123' => 123), $choiceList->getChoices()); + $this->assertSame(array('foo' => 2, 'bar' => 7, '123' => 10), $choiceList->getOriginalKeys()); + $this->assertSame(array(1 => 'foo', 2 => 123), $choiceList->getChoicesForValues(array(1 => 'foo', 2 => '123'))); + $this->assertSame(array(1 => 'foo', 2 => '123'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => 123))); + } + + public function testCreateChoiceListWithoutValueCallbackAndToStringDuplicates() + { + $choiceList = new ArrayChoiceList(array(2 => 'foo', 7 => '123', 10 => 123)); + + $this->assertSame(array('0', '1', '2'), $choiceList->getValues()); + $this->assertSame(array('0' => 'foo', '1' => '123', '2' => 123), $choiceList->getChoices()); + $this->assertSame(array('0' => 2, '1' => 7, '2' => 10), $choiceList->getOriginalKeys()); + $this->assertSame(array(1 => 'foo', 2 => 123), $choiceList->getChoicesForValues(array(1 => '0', 2 => '2'))); + $this->assertSame(array(1 => '0', 2 => '2'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => 123))); + } + + public function testCreateChoiceListWithoutValueCallbackAndMixedChoices() + { + $object = new \stdClass(); + $choiceList = new ArrayChoiceList(array(2 => 'foo', 5 => array(7 => '123'), 10 => $object)); + + $this->assertSame(array('0', '1', '2'), $choiceList->getValues()); + $this->assertSame(array('0' => 'foo', '1' => '123', '2' => $object), $choiceList->getChoices()); + $this->assertSame(array('0' => 2, '1' => 7, '2' => 10), $choiceList->getOriginalKeys()); + $this->assertSame(array(1 => 'foo', 2 => $object), $choiceList->getChoicesForValues(array(1 => '0', 2 => '2'))); + $this->assertSame(array(1 => '0', 2 => '2'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => $object))); + } + public function testCreateChoiceListWithGroupedChoices() { $choiceList = new ArrayChoiceList(array( @@ -72,15 +106,15 @@ public function testCreateChoiceListWithGroupedChoices() 'Group 2' => array('C' => 'c', 'D' => 'd'), )); - $this->assertSame(array('0', '1', '2', '3'), $choiceList->getValues()); + $this->assertSame(array('a', 'b', 'c', 'd'), $choiceList->getValues()); $this->assertSame(array( - 'Group 1' => array('A' => '0', 'B' => '1'), - 'Group 2' => array('C' => '2', 'D' => '3'), + 'Group 1' => array('A' => 'a', 'B' => 'b'), + 'Group 2' => array('C' => 'c', 'D' => 'd'), ), $choiceList->getStructuredValues()); - $this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd'), $choiceList->getChoices()); - $this->assertSame(array(0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D'), $choiceList->getOriginalKeys()); - $this->assertSame(array(1 => 'a', 2 => 'b'), $choiceList->getChoicesForValues(array(1 => '0', 2 => '1'))); - $this->assertSame(array(1 => '0', 2 => '1'), $choiceList->getValuesForChoices(array(1 => 'a', 2 => 'b'))); + $this->assertSame(array('a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd'), $choiceList->getChoices()); + $this->assertSame(array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D'), $choiceList->getOriginalKeys()); + $this->assertSame(array(1 => 'a', 2 => 'b'), $choiceList->getChoicesForValues(array(1 => 'a', 2 => 'b'))); + $this->assertSame(array(1 => 'a', 2 => 'b'), $choiceList->getValuesForChoices(array(1 => 'a', 2 => 'b'))); } public function testCompareChoicesByIdentityByDefault() diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php index c58d072f47434..f60ef05abc507 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php @@ -20,7 +20,7 @@ class ChoiceToValueTransformerTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $list = new ArrayChoiceList(array('', 0, 'X')); + $list = new ArrayChoiceList(array('', false, 'X')); $this->transformer = new ChoiceToValueTransformer($list); } @@ -35,7 +35,7 @@ public function transformProvider() return array( // more extensive test set can be found in FormUtilTest array('', '0'), - array(0, '1'), + array(false, '1'), ); } @@ -53,7 +53,7 @@ public function reverseTransformProvider() // values are expected to be valid choice keys already and stay // the same array('0', ''), - array('1', 0), + array('1', false), array('2', 'X'), ); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php index a7dc40aca225f..f7747aaccd0f1 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php @@ -20,7 +20,7 @@ class ChoicesToValuesTransformerTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $list = new ArrayChoiceList(array('A', 'B', 'C')); + $list = new ArrayChoiceList(array('', false, 'X')); $this->transformer = new ChoicesToValuesTransformer($list); } @@ -31,7 +31,7 @@ protected function tearDown() public function testTransform() { - $in = array('A', 'B', 'C'); + $in = array('', false, 'X'); $out = array('0', '1', '2'); $this->assertSame($out, $this->transformer->transform($in)); @@ -54,7 +54,7 @@ public function testReverseTransform() { // values are expected to be valid choices and stay the same $in = array('0', '1', '2'); - $out = array('A', 'B', 'C'); + $out = array('', false, 'X'); $this->assertSame($out, $this->transformer->reverseTransform($in)); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php index 215ddac936a54..f2a1f73e49aa7 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -18,14 +18,14 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase { private $choices = array( - 'a' => 'Bernhard', - 'b' => 'Fabien', - 'c' => 'Kris', - 'd' => 'Jon', - 'e' => 'Roman', + 'Bernhard' => 'a', + 'Fabien' => 'b', + 'Kris' => 'c', + 'Jon' => 'd', + 'Roman' => 'e', ); - private $numericChoices = array( + private $numericChoicesFlipped = array( 0 => 'Bernhard', 1 => 'Fabien', 2 => 'Kris', @@ -36,6 +36,18 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase private $objectChoices; protected $groupedChoices = array( + 'Symfony' => array( + 'Bernhard' => 'a', + 'Fabien' => 'b', + 'Kris' => 'c', + ), + 'Doctrine' => array( + 'Jon' => 'd', + 'Roman' => 'e', + ), + ); + + protected $groupedChoicesFlipped = array( 'Symfony' => array( 'a' => 'Bernhard', 'b' => 'Fabien', @@ -107,6 +119,20 @@ public function testExpandedChoicesOptionsTurnIntoChildren() $form = $this->factory->create('choice', null, array( 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, + )); + + $this->assertCount(count($this->choices), $form, 'Each choice should become a new field'); + } + + /** + * @group legacy + */ + public function testExpandedFlippedChoicesOptionsTurnIntoChildren() + { + $form = $this->factory->create('choice', null, array( + 'expanded' => true, + 'choices' => array_flip($this->choices), )); $this->assertCount(count($this->choices), $form, 'Each choice should become a new field'); @@ -119,6 +145,7 @@ public function testPlaceholderPresentOnNonRequiredExpandedSingleChoice() 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $this->assertTrue(isset($form['placeholder'])); @@ -132,6 +159,7 @@ public function testPlaceholderNotPresentIfRequired() 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $this->assertFalse(isset($form['placeholder'])); @@ -145,6 +173,7 @@ public function testPlaceholderNotPresentIfMultiple() 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $this->assertFalse(isset($form['placeholder'])); @@ -158,9 +187,10 @@ public function testPlaceholderNotPresentIfEmptyChoice() 'expanded' => true, 'required' => false, 'choices' => array( - '' => 'Empty', - 1 => 'Not empty', + 'Empty' => '', + 'Not empty' => 1, ), + 'choices_as_values' => true, )); $this->assertFalse(isset($form['placeholder'])); @@ -172,6 +202,29 @@ public function testExpandedChoicesOptionsAreFlattened() $form = $this->factory->create('choice', null, array( 'expanded' => true, 'choices' => $this->groupedChoices, + 'choices_as_values' => true, + )); + + $flattened = array(); + foreach ($this->groupedChoices as $choices) { + $flattened = array_merge($flattened, array_keys($choices)); + } + + $this->assertCount($form->count(), $flattened, 'Each nested choice should become a new field, not the groups'); + + foreach ($flattened as $value => $choice) { + $this->assertTrue($form->has($value), 'Flattened choice is named after it\'s value'); + } + } + + /** + * @group legacy + */ + public function testExpandedChoicesFlippedOptionsAreFlattened() + { + $form = $this->factory->create('choice', null, array( + 'expanded' => true, + 'choices' => $this->groupedChoicesFlipped, )); $flattened = array(); @@ -219,6 +272,7 @@ public function testExpandedCheckboxesAreNeverRequired() 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); foreach ($form as $child) { @@ -233,6 +287,7 @@ public function testExpandedRadiosAreRequiredIfChoiceChildIsRequired() 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); foreach ($form as $child) { @@ -247,6 +302,7 @@ public function testExpandedRadiosAreNotRequiredIfChoiceChildIsNotRequired() 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); foreach ($form as $child) { @@ -260,6 +316,7 @@ public function testSubmitSingleNonExpanded() 'multiple' => false, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('b'); @@ -275,6 +332,7 @@ public function testSubmitSingleNonExpandedInvalidChoice() 'multiple' => false, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('foobar'); @@ -290,6 +348,7 @@ public function testSubmitSingleNonExpandedNull() 'multiple' => false, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(null); @@ -308,6 +367,7 @@ public function testSubmitSingleNonExpandedNullNoChoices() 'multiple' => false, 'expanded' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(null); @@ -323,6 +383,7 @@ public function testSubmitSingleNonExpandedEmpty() 'multiple' => false, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(''); @@ -338,8 +399,9 @@ public function testSubmitSingleNonExpandedEmptyExplicitEmptyChoice() 'multiple' => false, 'expanded' => false, 'choices' => array( - 'EMPTY_CHOICE' => 'Empty', + 'Empty' => 'EMPTY_CHOICE', ), + 'choices_as_values' => true, 'choice_value' => function () { return ''; }, @@ -361,6 +423,7 @@ public function testSubmitSingleNonExpandedEmptyNoChoices() 'multiple' => false, 'expanded' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(''); @@ -376,6 +439,7 @@ public function testSubmitSingleNonExpandedFalse() 'multiple' => false, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(false); @@ -394,6 +458,7 @@ public function testSubmitSingleNonExpandedFalseNoChoices() 'multiple' => false, 'expanded' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(false); @@ -455,6 +520,7 @@ public function testSubmitMultipleNonExpanded() 'multiple' => true, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array('a', 'b')); @@ -470,6 +536,7 @@ public function testSubmitMultipleNonExpandedEmpty() 'multiple' => true, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array()); @@ -488,6 +555,7 @@ public function testSubmitMultipleNonExpandedEmptyNoChoices() 'multiple' => true, 'expanded' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(array()); @@ -503,6 +571,7 @@ public function testSubmitMultipleNonExpandedInvalidScalarChoice() 'multiple' => true, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('foobar'); @@ -518,6 +587,7 @@ public function testSubmitMultipleNonExpandedInvalidArrayChoice() 'multiple' => true, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array('a', 'foobar')); @@ -578,6 +648,7 @@ public function testSubmitSingleExpandedRequired() 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('b'); @@ -606,6 +677,7 @@ public function testSubmitSingleExpandedRequiredInvalidChoice() 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('foobar'); @@ -634,6 +706,7 @@ public function testSubmitSingleExpandedNonRequired() 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('b'); @@ -664,6 +737,7 @@ public function testSubmitSingleExpandedNonRequiredInvalidChoice() 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('foobar'); @@ -692,6 +766,7 @@ public function testSubmitSingleExpandedRequiredNull() 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(null); @@ -723,6 +798,7 @@ public function testSubmitSingleExpandedRequiredNullNoChoices() 'expanded' => true, 'required' => true, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(null); @@ -740,6 +816,7 @@ public function testSubmitSingleExpandedRequiredEmpty() 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(''); @@ -771,6 +848,7 @@ public function testSubmitSingleExpandedRequiredEmptyNoChoices() 'expanded' => true, 'required' => true, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(''); @@ -788,6 +866,7 @@ public function testSubmitSingleExpandedRequiredFalse() 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(false); @@ -819,6 +898,7 @@ public function testSubmitSingleExpandedRequiredFalseNoChoices() 'expanded' => true, 'required' => true, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(false); @@ -836,6 +916,7 @@ public function testSubmitSingleExpandedNonRequiredNull() 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(null); @@ -869,6 +950,7 @@ public function testSubmitSingleExpandedNonRequiredNullNoChoices() 'expanded' => true, 'required' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(null); @@ -886,6 +968,7 @@ public function testSubmitSingleExpandedNonRequiredEmpty() 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(''); @@ -919,6 +1002,7 @@ public function testSubmitSingleExpandedNonRequiredEmptyNoChoices() 'expanded' => true, 'required' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(''); @@ -936,6 +1020,7 @@ public function testSubmitSingleExpandedNonRequiredFalse() 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(false); @@ -969,6 +1054,7 @@ public function testSubmitSingleExpandedNonRequiredFalseNoChoices() 'expanded' => true, 'required' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(false); @@ -985,9 +1071,10 @@ public function testSubmitSingleExpandedWithEmptyChild() 'multiple' => false, 'expanded' => true, 'choices' => array( - '' => 'Empty', - 1 => 'Not empty', + 'Empty' => '', + 'Not empty' => 1, ), + 'choices_as_values' => true, )); $form->submit(''); @@ -1065,12 +1152,15 @@ public function testLegacySubmitSingleExpandedObjectChoices() $this->assertNull($form[4]->getViewData()); } - public function testSubmitSingleExpandedNumericChoices() + /** + * @group legacy + */ + public function testSubmitSingleExpandedNumericChoicesFlipped() { $form = $this->factory->create('choice', null, array( 'multiple' => false, 'expanded' => true, - 'choices' => $this->numericChoices, + 'choices' => $this->numericChoicesFlipped, )); $form->submit('1'); @@ -1096,6 +1186,7 @@ public function testSubmitMultipleExpanded() 'multiple' => true, 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array('a', 'c')); @@ -1123,6 +1214,7 @@ public function testSubmitMultipleExpandedInvalidScalarChoice() 'multiple' => true, 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('foobar'); @@ -1150,6 +1242,7 @@ public function testSubmitMultipleExpandedInvalidArrayChoice() 'multiple' => true, 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array('a', 'foobar')); @@ -1177,6 +1270,7 @@ public function testSubmitMultipleExpandedEmpty() 'multiple' => true, 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array()); @@ -1205,6 +1299,7 @@ public function testSubmitMultipleExpandedEmptyNoChoices() 'multiple' => true, 'expanded' => true, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(array()); @@ -1219,10 +1314,11 @@ public function testSubmitMultipleExpandedWithEmptyChild() 'multiple' => true, 'expanded' => true, 'choices' => array( - '' => 'Empty', - 1 => 'Not Empty', - 2 => 'Not Empty 2', + 'Empty' => '', + 'Not Empty' => 1, + 'Not Empty 2' => 2, ), + 'choices_as_values' => true, )); $form->submit(array('', '2')); @@ -1302,12 +1398,15 @@ public function testLegacySubmitMultipleExpandedObjectChoices() $this->assertNull($form[4]->getViewData()); } + /** + * @group legacy + */ public function testSubmitMultipleExpandedNumericChoices() { $form = $this->factory->create('choice', null, array( 'multiple' => true, 'expanded' => true, - 'choices' => $this->numericChoices, + 'choices' => $this->numericChoicesFlipped, )); $form->submit(array('1', '2')); @@ -1363,16 +1462,18 @@ public function testMultipleSelectedObjectChoices() $this->assertFalse($selectedChecker($view->vars['choices'][1]->value, $view->vars['value'])); } - /* + /** * We need this functionality to create choice fields for Boolean types, - * e.g. false => 'No', true => 'Yes' + * e.g. false => 'No', true => 'Yes'. + * + * @group legacy */ public function testSetDataSingleNonExpandedAcceptsBoolean() { $form = $this->factory->create('choice', null, array( 'multiple' => false, 'expanded' => false, - 'choices' => $this->numericChoices, + 'choices' => $this->numericChoicesFlipped, )); $form->setData(false); @@ -1382,12 +1483,15 @@ public function testSetDataSingleNonExpandedAcceptsBoolean() $this->assertTrue($form->isSynchronized()); } + /** + * @group legacy + */ public function testSetDataMultipleNonExpandedAcceptsBoolean() { $form = $this->factory->create('choice', null, array( 'multiple' => true, 'expanded' => false, - 'choices' => $this->numericChoices, + 'choices' => $this->numericChoicesFlipped, )); $form->setData(array(false, true)); @@ -1401,6 +1505,7 @@ public function testPassRequiredToView() { $form = $this->factory->create('choice', null, array( 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1412,6 +1517,7 @@ public function testPassNonRequiredToView() $form = $this->factory->create('choice', null, array( 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1423,6 +1529,7 @@ public function testPassMultipleToView() $form = $this->factory->create('choice', null, array( 'multiple' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1434,6 +1541,7 @@ public function testPassExpandedToView() $form = $this->factory->create('choice', null, array( 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1444,6 +1552,7 @@ public function testPassChoiceTranslationDomainToView() { $form = $this->factory->create('choice', null, array( 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1454,6 +1563,7 @@ public function testChoiceTranslationDomainWithTrueValueToView() { $form = $this->factory->create('choice', null, array( 'choices' => $this->choices, + 'choices_as_values' => true, 'choice_translation_domain' => true, )); $view = $form->createView(); @@ -1465,6 +1575,7 @@ public function testDefaultChoiceTranslationDomainIsSameAsTranslationDomainToVie { $form = $this->factory->create('choice', null, array( 'choices' => $this->choices, + 'choices_as_values' => true, 'translation_domain' => 'foo', )); $view = $form->createView(); @@ -1491,6 +1602,7 @@ public function testPlaceholderIsNullByDefaultIfRequired() 'multiple' => false, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1503,6 +1615,7 @@ public function testPlaceholderIsEmptyStringByDefaultIfNotRequired() 'multiple' => false, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1520,6 +1633,7 @@ public function testPassPlaceholderToView($multiple, $expanded, $required, $plac 'required' => $required, 'placeholder' => $placeholder, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1539,6 +1653,7 @@ public function testPassEmptyValueBC($multiple, $expanded, $required, $placehold 'required' => $required, 'empty_value' => $placeholder, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1558,7 +1673,8 @@ public function testDontPassPlaceholderIfContainedInChoices($multiple, $expanded 'expanded' => $expanded, 'required' => $required, 'placeholder' => $placeholder, - 'choices' => array('a' => 'A', '' => 'Empty'), + 'choices' => array('A' => 'a', 'Empty' => ''), + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1612,9 +1728,10 @@ public function getOptionsWithPlaceholder() public function testPassChoicesToView() { - $choices = array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D'); + $choices = array('A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd'); $form = $this->factory->create('choice', null, array( 'choices' => $choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1628,9 +1745,10 @@ public function testPassChoicesToView() public function testPassPreferredChoicesToView() { - $choices = array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D'); + $choices = array('A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd'); $form = $this->factory->create('choice', null, array( 'choices' => $choices, + 'choices_as_values' => true, 'preferred_choices' => array('b', 'd'), )); $view = $form->createView(); @@ -1649,6 +1767,7 @@ public function testPassHierarchicalChoicesToView() { $form = $this->factory->create('choice', null, array( 'choices' => $this->groupedChoices, + 'choices_as_values' => true, 'preferred_choices' => array('b', 'd'), )); $view = $form->createView(); @@ -1700,6 +1819,7 @@ public function testAdjustFullNameForMultipleNonExpanded() 'multiple' => true, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1711,6 +1831,7 @@ public function testInitializeWithEmptyChoices() { $this->factory->createNamed('name', 'choice', null, array( 'choices' => array(), + 'choices_as_values' => true, )); } From f4f082ee6daafe2fcafcc4d4a25af8aeca1a3df4 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 27 Nov 2015 11:11:22 +0100 Subject: [PATCH 076/107] [HttpFoundation] Deprecate $deep parameter on ParameterBag --- .../Component/HttpFoundation/ParameterBag.php | 25 +++++++++++++++---- .../Component/HttpFoundation/Request.php | 2 +- .../HttpFoundation/Tests/ParameterBagTest.php | 14 +++++------ 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/ParameterBag.php b/src/Symfony/Component/HttpFoundation/ParameterBag.php index 35b4869533e46..4560322671470 100644 --- a/src/Symfony/Component/HttpFoundation/ParameterBag.php +++ b/src/Symfony/Component/HttpFoundation/ParameterBag.php @@ -90,7 +90,7 @@ public function add(array $parameters = array()) */ public function get($key, $default = null, $deep = false) { - if (true === $deep) { + if ($deep) { @trigger_error('Using paths to find deeper items in '.__METHOD__.' is deprecated since version 2.8 and will be removed in 3.0. Filter the returned value in your own code instead.', E_USER_DEPRECATED); } @@ -214,7 +214,7 @@ public function getAlnum($key, $default = '', $deep = false) public function getDigits($key, $default = '', $deep = false) { // we need to remove - and + because they're allowed in the filter - return str_replace(array('-', '+'), '', $this->filter($key, $default, $deep, FILTER_SANITIZE_NUMBER_INT)); + return str_replace(array('-', '+'), '', $this->filter($key, $default, FILTER_SANITIZE_NUMBER_INT, array(), $deep)); } /** @@ -242,7 +242,7 @@ public function getInt($key, $default = 0, $deep = false) */ public function getBoolean($key, $default = false, $deep = false) { - return $this->filter($key, $default, $deep, FILTER_VALIDATE_BOOLEAN); + return $this->filter($key, $default, FILTER_VALIDATE_BOOLEAN, array(), $deep); } /** @@ -250,16 +250,31 @@ public function getBoolean($key, $default = false, $deep = false) * * @param string $key Key. * @param mixed $default Default = null. - * @param bool $deep Default = false. * @param int $filter FILTER_* constant. * @param mixed $options Filter options. + * @param bool $deep Default = false. * * @see http://php.net/manual/en/function.filter-var.php * * @return mixed */ - public function filter($key, $default = null, $deep = false, $filter = FILTER_DEFAULT, $options = array()) + public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = array(), $deep = false) { + static $filters = null; + + if (null === $filters) { + foreach (filter_list() as $tmp) { + $filters[filter_id($tmp)] = 1; + } + } + if (is_bool($filter) || !isset($filters[$filter]) || is_array($deep)) { + @trigger_error('Passing the $deep boolean as 3rd argument to the '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Remove it altogether as the $deep argument will be removed in 3.0.', E_USER_ERROR); + $tmp = $deep; + $deep = $filter; + $filter = $options; + $options = $tmp; + } + $value = $this->get($key, $default, $deep); // Always turn $options into an array - this allows filter_var option shortcuts. diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index edc77df10d6de..de36953cecef5 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -724,7 +724,7 @@ public static function getHttpMethodParameterOverride() */ public function get($key, $default = null, $deep = false) { - if (true === $deep) { + if ($deep) { @trigger_error('Using paths to find deeper items in '.__METHOD__.' is deprecated since version 2.8 and will be removed in 3.0. Filter the returned value in your own code instead.', E_USER_DEPRECATED); } diff --git a/src/Symfony/Component/HttpFoundation/Tests/ParameterBagTest.php b/src/Symfony/Component/HttpFoundation/Tests/ParameterBagTest.php index d797fb3bbd27b..06a6cbf413c8f 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ParameterBagTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ParameterBagTest.php @@ -172,26 +172,26 @@ public function testFilter() $this->assertEmpty($bag->filter('nokey'), '->filter() should return empty by default if no key is found'); - $this->assertEquals('0123', $bag->filter('digits', '', false, FILTER_SANITIZE_NUMBER_INT), '->filter() gets a value of parameter as integer filtering out invalid characters'); + $this->assertEquals('0123', $bag->filter('digits', '', FILTER_SANITIZE_NUMBER_INT), '->filter() gets a value of parameter as integer filtering out invalid characters'); - $this->assertEquals('example@example.com', $bag->filter('email', '', false, FILTER_VALIDATE_EMAIL), '->filter() gets a value of parameter as email'); + $this->assertEquals('example@example.com', $bag->filter('email', '', FILTER_VALIDATE_EMAIL), '->filter() gets a value of parameter as email'); - $this->assertEquals('http://example.com/foo', $bag->filter('url', '', false, FILTER_VALIDATE_URL, array('flags' => FILTER_FLAG_PATH_REQUIRED)), '->filter() gets a value of parameter as URL with a path'); + $this->assertEquals('http://example.com/foo', $bag->filter('url', '', FILTER_VALIDATE_URL, array('flags' => FILTER_FLAG_PATH_REQUIRED)), '->filter() gets a value of parameter as URL with a path'); // This test is repeated for code-coverage - $this->assertEquals('http://example.com/foo', $bag->filter('url', '', false, FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED), '->filter() gets a value of parameter as URL with a path'); + $this->assertEquals('http://example.com/foo', $bag->filter('url', '', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED), '->filter() gets a value of parameter as URL with a path'); - $this->assertFalse($bag->filter('dec', '', false, FILTER_VALIDATE_INT, array( + $this->assertFalse($bag->filter('dec', '', FILTER_VALIDATE_INT, array( 'flags' => FILTER_FLAG_ALLOW_HEX, 'options' => array('min_range' => 1, 'max_range' => 0xff), )), '->filter() gets a value of parameter as integer between boundaries'); - $this->assertFalse($bag->filter('hex', '', false, FILTER_VALIDATE_INT, array( + $this->assertFalse($bag->filter('hex', '', FILTER_VALIDATE_INT, array( 'flags' => FILTER_FLAG_ALLOW_HEX, 'options' => array('min_range' => 1, 'max_range' => 0xff), )), '->filter() gets a value of parameter as integer between boundaries'); - $this->assertEquals(array('bang'), $bag->filter('array', '', false), '->filter() gets a value of parameter as an array'); + $this->assertEquals(array('bang'), $bag->filter('array', ''), '->filter() gets a value of parameter as an array'); } public function testGetIterator() From 62eba7c426ac0db6bde810a090410eab4bd2292e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 27 Nov 2015 10:24:30 +0100 Subject: [PATCH 077/107] [Form+SecurityBundle] Trigger deprecation for csrf_provider+intention options --- .../DependencyInjection/MainConfiguration.php | 4 ++++ .../Security/Factory/FormLoginFactory.php | 19 +++++++++++++++-- .../MainConfigurationTest.php | 4 ++-- .../Functional/app/CsrfFormLogin/config.yml | 4 ++-- .../Csrf/Type/FormTypeCsrfExtension.php | 21 +++++++++++++++++-- 5 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php index c07794b6b4727..c85c3df907c38 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php @@ -242,6 +242,8 @@ private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $facto ->beforeNormalization() ->ifTrue(function ($v) { return isset($v['csrf_provider']); }) ->then(function ($v) { + @trigger_error("Setting the 'csrf_provider' configuration key on a security firewall is deprecated since version 2.8 and will be removed in 3.0. Use the 'csrf_token_generator' configuration key instead.", E_USER_DEPRECATED); + $v['csrf_token_generator'] = $v['csrf_provider']; unset($v['csrf_provider']); @@ -251,6 +253,8 @@ private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $facto ->beforeNormalization() ->ifTrue(function ($v) { return isset($v['intention']); }) ->then(function ($v) { + @trigger_error("Setting the 'intention' configuration key on a security firewall is deprecated since version 2.8 and will be removed in 3.0. Use the 'csrf_token_id' key instead.", E_USER_DEPRECATED); + $v['csrf_token_id'] = $v['intention']; unset($v['intention']); diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php index ac9523c507208..c3a19e3f7edf4 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php @@ -48,8 +48,23 @@ public function addConfiguration(NodeDefinition $node) parent::addConfiguration($node); $node + ->beforeNormalization() + ->ifTrue(function ($v) { return isset($v['csrf_provider']) && isset($v['csrf_token_generator']); }) + ->thenInvalid("You should define a value for only one of 'csrf_provider' and 'csrf_token_generator' on a security firewall. Use 'csrf_token_generator' as this replaces 'csrf_provider'.") + ->end() + ->beforeNormalization() + ->ifTrue(function ($v) { return isset($v['csrf_provider']); }) + ->then(function ($v) { + @trigger_error("Setting the 'csrf_provider' configuration key on a security firewall is deprecated since version 2.8 and will be removed in 3.0. Use the 'csrf_token_generator' configuration key instead.", E_USER_DEPRECATED); + + $v['csrf_token_generator'] = $v['csrf_provider']; + unset($v['csrf_provider']); + + return $v; + }) + ->end() ->children() - ->scalarNode('csrf_provider')->cannotBeEmpty()->end() + ->scalarNode('csrf_token_generator')->cannotBeEmpty()->end() ->end() ; } @@ -78,7 +93,7 @@ protected function createListener($container, $id, $config, $userProvider) $container ->getDefinition($listenerId) - ->addArgument(isset($config['csrf_provider']) ? new Reference($config['csrf_provider']) : null) + ->addArgument(isset($config['csrf_token_generator']) ? new Reference($config['csrf_token_generator']) : null) ; return $listenerId; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php index 9d8009ea8a9e0..990632f2b683d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php @@ -74,8 +74,8 @@ public function testCsrfAliases() 'firewalls' => array( 'stub' => array( 'logout' => array( - 'csrf_provider' => 'a_token_generator', - 'intention' => 'a_token_id', + 'csrf_token_generator' => 'a_token_generator', + 'csrf_token_id' => 'a_token_id', ), ), ), diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml index ffcc9352d8260..5a00ac329895d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml @@ -36,12 +36,12 @@ security: username_parameter: "user_login[username]" password_parameter: "user_login[password]" csrf_parameter: "user_login[_token]" - csrf_provider: security.csrf.token_manager + csrf_token_generator: security.csrf.token_manager anonymous: ~ logout: path: /logout_path target: / - csrf_provider: security.csrf.token_manager + csrf_token_generator: security.csrf.token_manager access_control: - { path: .*, roles: IS_AUTHENTICATED_FULLY } diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php index cf5038ae1f6cd..34a0144f4e8a4 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php +++ b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php @@ -123,6 +123,10 @@ public function configureOptions(OptionsResolver $resolver) { // BC clause for the "intention" option $csrfTokenId = function (Options $options) { + if (null !== $options['intention']) { + @trigger_error('The form option "intention" is deprecated since version 2.8 and will be removed in 3.0. Use "csrf_token_id" instead.', E_USER_DEPRECATED); + } + return $options['intention']; }; @@ -137,15 +141,28 @@ public function configureOptions(OptionsResolver $resolver) : new CsrfProviderAdapter($options['csrf_provider']); }; + $defaultTokenManager = $this->defaultTokenManager; + $csrfProviderNormalizer = function (Options $options, $csrfProvider) use ($defaultTokenManager) { + if (null !== $csrfProvider) { + @trigger_error('The form option "csrf_provider" is deprecated since version 2.8 and will be removed in 3.0. Use "csrf_token_manager" instead.', E_USER_DEPRECATED); + + return $csrfProvider; + } + + return $defaultTokenManager; + }; + $resolver->setDefaults(array( 'csrf_protection' => $this->defaultEnabled, 'csrf_field_name' => $this->defaultFieldName, 'csrf_message' => 'The CSRF token is invalid. Please try to resubmit the form.', 'csrf_token_manager' => $csrfTokenManager, 'csrf_token_id' => $csrfTokenId, - 'csrf_provider' => $this->defaultTokenManager, - 'intention' => null, + 'csrf_provider' => null, // deprecated + 'intention' => null, // deprecated )); + + $resolver->setNormalizer('csrf_provider', $csrfProviderNormalizer); } /** From 2a16e87aecd8482e38235b083a89acf115542bb3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 27 Nov 2015 14:24:26 +0100 Subject: [PATCH 078/107] [Form] Fix missing choices_as_values=true in tests --- .../Tests/AbstractBootstrap3LayoutTest.php | 77 ++++++++++++------- .../Form/Tests/AbstractDivLayoutTest.php | 3 +- .../Form/Tests/AbstractLayoutTest.php | 77 ++++++++++++------- 3 files changed, 104 insertions(+), 53 deletions(-) diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php index 7ee2d5b352723..3bafd0ea24591 100644 --- a/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php @@ -212,7 +212,8 @@ public function testCheckboxWithValue() public function testSingleChoice() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'multiple' => false, 'expanded' => false, )); @@ -234,7 +235,8 @@ public function testSingleChoice() public function testSingleChoiceWithoutTranslation() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'multiple' => false, 'expanded' => false, 'choice_translation_domain' => false, @@ -257,7 +259,8 @@ public function testSingleChoiceWithoutTranslation() public function testSingleChoiceAttributes() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), 'multiple' => false, 'expanded' => false, @@ -282,7 +285,8 @@ public function testSingleChoiceAttributes() public function testSingleChoiceWithPreferred() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'preferred_choices' => array('&b'), 'multiple' => false, 'expanded' => false, @@ -306,7 +310,8 @@ public function testSingleChoiceWithPreferred() public function testSingleChoiceWithPreferredAndNoSeparator() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'preferred_choices' => array('&b'), 'multiple' => false, 'expanded' => false, @@ -329,7 +334,8 @@ public function testSingleChoiceWithPreferredAndNoSeparator() public function testSingleChoiceWithPreferredAndBlankSeparator() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'preferred_choices' => array('&b'), 'multiple' => false, 'expanded' => false, @@ -353,7 +359,8 @@ public function testSingleChoiceWithPreferredAndBlankSeparator() public function testChoiceWithOnlyPreferred() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'preferred_choices' => array('&a', '&b'), 'multiple' => false, 'expanded' => false, @@ -370,7 +377,8 @@ public function testChoiceWithOnlyPreferred() public function testSingleChoiceNonRequired() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'required' => false, 'multiple' => false, 'expanded' => false, @@ -394,7 +402,8 @@ public function testSingleChoiceNonRequired() public function testSingleChoiceNonRequiredNoneSelected() { $form = $this->factory->createNamed('name', 'choice', null, array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'required' => false, 'multiple' => false, 'expanded' => false, @@ -418,7 +427,8 @@ public function testSingleChoiceNonRequiredNoneSelected() public function testSingleChoiceNonRequiredWithPlaceholder() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'multiple' => false, 'expanded' => false, 'required' => false, @@ -443,7 +453,8 @@ public function testSingleChoiceNonRequiredWithPlaceholder() public function testSingleChoiceRequiredWithPlaceholder() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'required' => true, 'multiple' => false, 'expanded' => false, @@ -468,7 +479,8 @@ public function testSingleChoiceRequiredWithPlaceholder() public function testSingleChoiceRequiredWithPlaceholderViaView() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'required' => true, 'multiple' => false, 'expanded' => false, @@ -493,9 +505,10 @@ public function testSingleChoiceGrouped() { $form = $this->factory->createNamed('name', 'choice', '&a', array( 'choices' => array( - 'Group&1' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), - 'Group&2' => array('&c' => 'Choice&C'), + 'Group&1' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'Group&2' => array('Choice&C' => '&c'), ), + 'choices_as_values' => true, 'multiple' => false, 'expanded' => false, )); @@ -523,7 +536,8 @@ public function testSingleChoiceGrouped() public function testMultipleChoice() { $form = $this->factory->createNamed('name', 'choice', array('&a'), array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'required' => true, 'multiple' => true, 'expanded' => false, @@ -547,7 +561,8 @@ public function testMultipleChoice() public function testMultipleChoiceAttributes() { $form = $this->factory->createNamed('name', 'choice', array('&a'), array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), 'required' => true, 'multiple' => true, @@ -574,7 +589,8 @@ public function testMultipleChoiceAttributes() public function testMultipleChoiceSkipsPlaceholder() { $form = $this->factory->createNamed('name', 'choice', array('&a'), array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'multiple' => true, 'expanded' => false, 'placeholder' => 'Test&Me', @@ -597,7 +613,8 @@ public function testMultipleChoiceSkipsPlaceholder() public function testMultipleChoiceNonRequired() { $form = $this->factory->createNamed('name', 'choice', array('&a'), array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'required' => false, 'multiple' => true, 'expanded' => false, @@ -620,7 +637,8 @@ public function testMultipleChoiceNonRequired() public function testSingleChoiceExpanded() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'multiple' => false, 'expanded' => true, )); @@ -655,7 +673,8 @@ public function testSingleChoiceExpanded() public function testSingleChoiceExpandedWithoutTranslation() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'multiple' => false, 'expanded' => true, 'choice_translation_domain' => false, @@ -691,7 +710,8 @@ public function testSingleChoiceExpandedWithoutTranslation() public function testSingleChoiceExpandedAttributes() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), 'multiple' => false, 'expanded' => true, @@ -729,7 +749,8 @@ public function testSingleChoiceExpandedAttributes() public function testSingleChoiceExpandedWithPlaceholder() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'multiple' => false, 'expanded' => true, 'placeholder' => 'Test&Me', @@ -774,7 +795,8 @@ public function testSingleChoiceExpandedWithPlaceholder() public function testSingleChoiceExpandedWithBooleanValue() { $form = $this->factory->createNamed('name', 'choice', true, array( - 'choices' => array('1' => 'Choice&A', '0' => 'Choice&B'), + 'choices' => array('Choice&A' => '1', 'Choice&B' => '0'), + 'choices_as_values' => true, 'multiple' => false, 'expanded' => true, )); @@ -809,7 +831,8 @@ public function testSingleChoiceExpandedWithBooleanValue() public function testMultipleChoiceExpanded() { $form = $this->factory->createNamed('name', 'choice', array('&a', '&c'), array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B', '&c' => 'Choice&C'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'choices_as_values' => true, 'multiple' => true, 'expanded' => true, 'required' => true, @@ -854,7 +877,8 @@ public function testMultipleChoiceExpanded() public function testMultipleChoiceExpandedWithoutTranslation() { $form = $this->factory->createNamed('name', 'choice', array('&a', '&c'), array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B', '&c' => 'Choice&C'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'choices_as_values' => true, 'multiple' => true, 'expanded' => true, 'required' => true, @@ -900,7 +924,8 @@ public function testMultipleChoiceExpandedWithoutTranslation() public function testMultipleChoiceExpandedAttributes() { $form = $this->factory->createNamed('name', 'choice', array('&a', '&c'), array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B', '&c' => 'Choice&C'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'choices_as_values' => true, 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), 'multiple' => true, 'expanded' => true, diff --git a/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php index 45b2f311c2942..7eeeac0a029cc 100644 --- a/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php @@ -692,7 +692,8 @@ public function testCollectionRowWithCustomBlock() public function testChoiceRowWithCustomBlock() { $form = $this->factory->createNamedBuilder('name_c', 'choice', 'a', array( - 'choices' => array('a' => 'ChoiceA', 'b' => 'ChoiceB'), + 'choices' => array('ChoiceA' => 'a', 'ChoiceB' => 'b'), + 'choices_as_values' => true, 'expanded' => true, )) ->getForm(); diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php index 64ecec9b49dfa..0939a2b003a9c 100644 --- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -497,7 +497,8 @@ public function testCheckboxWithValue() public function testSingleChoice() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'multiple' => false, 'expanded' => false, )); @@ -530,7 +531,8 @@ public function testSingleChoice() public function testSingleChoiceWithoutTranslation() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'multiple' => false, 'expanded' => false, 'choice_translation_domain' => false, @@ -552,7 +554,8 @@ public function testSingleChoiceWithoutTranslation() public function testSingleChoiceAttributes() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), 'multiple' => false, 'expanded' => false, @@ -576,7 +579,8 @@ public function testSingleChoiceAttributes() public function testSingleChoiceWithPreferred() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'preferred_choices' => array('&b'), 'multiple' => false, 'expanded' => false, @@ -599,7 +603,8 @@ public function testSingleChoiceWithPreferred() public function testSingleChoiceWithPreferredAndNoSeparator() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'preferred_choices' => array('&b'), 'multiple' => false, 'expanded' => false, @@ -621,7 +626,8 @@ public function testSingleChoiceWithPreferredAndNoSeparator() public function testSingleChoiceWithPreferredAndBlankSeparator() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'preferred_choices' => array('&b'), 'multiple' => false, 'expanded' => false, @@ -644,7 +650,8 @@ public function testSingleChoiceWithPreferredAndBlankSeparator() public function testChoiceWithOnlyPreferred() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'preferred_choices' => array('&a', '&b'), 'multiple' => false, 'expanded' => false, @@ -660,7 +667,8 @@ public function testChoiceWithOnlyPreferred() public function testSingleChoiceNonRequired() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'required' => false, 'multiple' => false, 'expanded' => false, @@ -683,7 +691,8 @@ public function testSingleChoiceNonRequired() public function testSingleChoiceNonRequiredNoneSelected() { $form = $this->factory->createNamed('name', 'choice', null, array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'required' => false, 'multiple' => false, 'expanded' => false, @@ -706,7 +715,8 @@ public function testSingleChoiceNonRequiredNoneSelected() public function testSingleChoiceNonRequiredWithPlaceholder() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'multiple' => false, 'expanded' => false, 'required' => false, @@ -730,7 +740,8 @@ public function testSingleChoiceNonRequiredWithPlaceholder() public function testSingleChoiceRequiredWithPlaceholder() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'required' => true, 'multiple' => false, 'expanded' => false, @@ -757,7 +768,8 @@ public function testSingleChoiceRequiredWithPlaceholder() public function testSingleChoiceRequiredWithPlaceholderViaView() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'required' => true, 'multiple' => false, 'expanded' => false, @@ -784,9 +796,10 @@ public function testSingleChoiceGrouped() { $form = $this->factory->createNamed('name', 'choice', '&a', array( 'choices' => array( - 'Group&1' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), - 'Group&2' => array('&c' => 'Choice&C'), + 'Group&1' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'Group&2' => array('Choice&C' => '&c'), ), + 'choices_as_values' => true, 'multiple' => false, 'expanded' => false, )); @@ -813,7 +826,8 @@ public function testSingleChoiceGrouped() public function testMultipleChoice() { $form = $this->factory->createNamed('name', 'choice', array('&a'), array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'required' => true, 'multiple' => true, 'expanded' => false, @@ -836,7 +850,8 @@ public function testMultipleChoice() public function testMultipleChoiceAttributes() { $form = $this->factory->createNamed('name', 'choice', array('&a'), array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), 'required' => true, 'multiple' => true, @@ -862,7 +877,8 @@ public function testMultipleChoiceAttributes() public function testMultipleChoiceSkipsPlaceholder() { $form = $this->factory->createNamed('name', 'choice', array('&a'), array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'multiple' => true, 'expanded' => false, 'placeholder' => 'Test&Me', @@ -884,7 +900,8 @@ public function testMultipleChoiceSkipsPlaceholder() public function testMultipleChoiceNonRequired() { $form = $this->factory->createNamed('name', 'choice', array('&a'), array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'required' => false, 'multiple' => true, 'expanded' => false, @@ -906,7 +923,8 @@ public function testMultipleChoiceNonRequired() public function testSingleChoiceExpanded() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'multiple' => false, 'expanded' => true, )); @@ -928,7 +946,8 @@ public function testSingleChoiceExpanded() public function testSingleChoiceExpandedWithoutTranslation() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'multiple' => false, 'expanded' => true, 'choice_translation_domain' => false, @@ -951,7 +970,8 @@ public function testSingleChoiceExpandedWithoutTranslation() public function testSingleChoiceExpandedAttributes() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), 'multiple' => false, 'expanded' => true, @@ -976,7 +996,8 @@ public function testSingleChoiceExpandedAttributes() public function testSingleChoiceExpandedWithPlaceholder() { $form = $this->factory->createNamed('name', 'choice', '&a', array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choices_as_values' => true, 'multiple' => false, 'expanded' => true, 'placeholder' => 'Test&Me', @@ -1001,7 +1022,8 @@ public function testSingleChoiceExpandedWithPlaceholder() public function testSingleChoiceExpandedWithBooleanValue() { $form = $this->factory->createNamed('name', 'choice', true, array( - 'choices' => array('1' => 'Choice&A', '0' => 'Choice&B'), + 'choices' => array('Choice&A' => '1', 'Choice&B' => '0'), + 'choices_as_values' => true, 'multiple' => false, 'expanded' => true, )); @@ -1023,7 +1045,8 @@ public function testSingleChoiceExpandedWithBooleanValue() public function testMultipleChoiceExpanded() { $form = $this->factory->createNamed('name', 'choice', array('&a', '&c'), array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B', '&c' => 'Choice&C'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'choices_as_values' => true, 'multiple' => true, 'expanded' => true, 'required' => true, @@ -1048,7 +1071,8 @@ public function testMultipleChoiceExpanded() public function testMultipleChoiceExpandedWithoutTranslation() { $form = $this->factory->createNamed('name', 'choice', array('&a', '&c'), array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B', '&c' => 'Choice&C'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'choices_as_values' => true, 'multiple' => true, 'expanded' => true, 'required' => true, @@ -1074,7 +1098,8 @@ public function testMultipleChoiceExpandedWithoutTranslation() public function testMultipleChoiceExpandedAttributes() { $form = $this->factory->createNamed('name', 'choice', array('&a', '&c'), array( - 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B', '&c' => 'Choice&C'), + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'choices_as_values' => true, 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), 'multiple' => true, 'expanded' => true, From 1179f0727bfe8425642abda290477098e911b46a Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Thu, 26 Nov 2015 15:25:37 +0100 Subject: [PATCH 079/107] [Form] Fixed: Duplicate choice labels are remembered when using "choices_as_values" = false --- .../Form/Extension/Core/Type/ChoiceType.php | 72 ++++++++++++++++++- .../Extension/Core/Type/ChoiceTypeTest.php | 17 +++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 528834d1e3cb8..4822bcf68b571 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -238,6 +238,7 @@ public function finishView(FormView $view, FormInterface $form, array $options) */ public function configureOptions(OptionsResolver $resolver) { + $choiceLabels = array(); $choiceListFactory = $this->choiceListFactory; $emptyData = function (Options $options) { @@ -252,6 +253,44 @@ public function configureOptions(OptionsResolver $resolver) return $options['required'] ? null : ''; }; + // BC closure, to be removed in 3.0 + $choicesNormalizer = function (Options $options, $choices) use (&$choiceLabels) { + // Unset labels from previous invocations + $choiceLabels = array(); + + // This closure is irrelevant when "choices_as_values" is set to true + if ($options['choices_as_values']) { + return $choices; + } + + ChoiceType::normalizeLegacyChoices($choices, $choiceLabels); + + return $choices; + }; + + // BC closure, to be removed in 3.0 + $choiceLabel = function (Options $options) use (&$choiceLabels) { + // If the choices contain duplicate labels, the normalizer of the + // "choices" option stores them in the $choiceLabels variable + + // Trigger the normalizer + $options->offsetGet('choices'); + + // Pick labels from $choiceLabels if available + // Don't invoke count() to avoid creating a copy of the array (yet) + if ($choiceLabels) { + // Don't pass the labels by reference. We do want to create a + // copy here so that every form has an own version of that + // variable (contrary to the global reference shared by all + // forms) + return function ($choice, $key) use ($choiceLabels) { + return $choiceLabels[$key]; + }; + } + + return; + }; + $choiceListNormalizer = function (Options $options, $choiceList) use ($choiceListFactory) { if ($choiceList) { @trigger_error('The "choice_list" option is deprecated since version 2.7 and will be removed in 3.0. Use "choice_loader" instead.', E_USER_DEPRECATED); @@ -322,7 +361,7 @@ public function configureOptions(OptionsResolver $resolver) 'choices' => array(), 'choices_as_values' => false, 'choice_loader' => null, - 'choice_label' => null, + 'choice_label' => $choiceLabel, 'choice_name' => null, 'choice_value' => null, 'choice_attr' => null, @@ -340,6 +379,7 @@ public function configureOptions(OptionsResolver $resolver) 'choice_translation_domain' => true, )); + $resolver->setNormalizer('choices', $choicesNormalizer); $resolver->setNormalizer('choice_list', $choiceListNormalizer); $resolver->setNormalizer('placeholder', $placeholderNormalizer); $resolver->setNormalizer('choice_translation_domain', $choiceTranslationDomainNormalizer); @@ -454,4 +494,34 @@ private function createChoiceListView(ChoiceListInterface $choiceList, array $op $options['choice_attr'] ); } + + /** + * When "choices_as_values" is set to false, the choices are in the keys and + * their labels in the values. Labels may occur twice. The form component + * flips the choices array in the new implementation, so duplicate labels + * are lost. Store them in a utility array that is used from the + * "choice_label" closure by default. + * + * @param array $choices The choice labels indexed by choices. + * Labels are replaced by generated keys. + * @param array $choiceLabels The array that receives the choice labels + * indexed by generated keys. + * @param int|null $nextKey The next generated key. + * + * @internal Public only to be accessible from closures on PHP 5.3. Don't + * use this method, as it may be removed without notice. + */ + public static function normalizeLegacyChoices(array &$choices, array &$choiceLabels, &$nextKey = 0) + { + foreach ($choices as $choice => &$choiceLabel) { + if (is_array($choiceLabel)) { + self::normalizeLegacyChoices($choiceLabel, $choiceLabels, $nextKey); + continue; + } + + $choiceLabels[$nextKey] = $choiceLabel; + $choices[$choice] = $nextKey; + ++$nextKey; + } + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php index 215ddac936a54..c1c1f9a327cde 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -1694,6 +1694,23 @@ public function testPassChoiceDataToView() ), $view->vars['choices']); } + /** + * @group legacy + */ + public function testDuplicateChoiceLabels() + { + $form = $this->factory->create('choice', null, array( + 'choices' => array('a' => 'A', 'b' => 'B', 'c' => 'A'), + )); + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView('a', 'a', 'A'), + new ChoiceView('b', 'b', 'B'), + new ChoiceView('c', 'c', 'A'), + ), $view->vars['choices']); + } + public function testAdjustFullNameForMultipleNonExpanded() { $form = $this->factory->createNamed('name', 'choice', null, array( From 5a88fb619fec8bbe7778f8e8b80c45d1de0984cc Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 27 Nov 2015 14:36:40 +0100 Subject: [PATCH 080/107] [Bridge\PhpUnit] Display the stack trace of a deprecation on-demand --- .../PhpUnit/DeprecationErrorHandler.php | 35 +++++++++++++++---- src/Symfony/Bridge/PhpUnit/README.md | 9 ++++- .../Form/ChoiceList/ArrayKeyChoiceList.php | 2 ++ 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php index 063723d32db29..81f59beeccde2 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php @@ -47,16 +47,37 @@ public static function register($mode = false) // No-op } - if (0 !== error_reporting()) { - $group = 'unsilenced'; - $ref = &$deprecations[$group][$msg]['count']; - ++$ref; - } elseif (isset($trace[$i]['object']) || isset($trace[$i]['class'])) { + if (isset($trace[$i]['object']) || isset($trace[$i]['class'])) { $class = isset($trace[$i]['object']) ? get_class($trace[$i]['object']) : $trace[$i]['class']; $method = $trace[$i]['function']; - $group = 0 === strpos($method, 'testLegacy') || 0 === strpos($method, 'provideLegacy') || 0 === strpos($method, 'getLegacy') || strpos($class, '\Legacy') || in_array('legacy', \PHPUnit_Util_Test::getGroups($class, $method), true) ? 'legacy' : 'remaining'; + if (0 !== error_reporting()) { + $group = 'unsilenced'; + } elseif (0 === strpos($method, 'testLegacy') + || 0 === strpos($method, 'provideLegacy') + || 0 === strpos($method, 'getLegacy') + || strpos($class, '\Legacy') + || in_array('legacy', \PHPUnit_Util_Test::getGroups($class, $method), true) + ) { + $group = 'legacy'; + } else { + $group = 'remaining'; + } + + if (isset($mode[0]) && '/' === $mode[0] && preg_match($mode, $class.'::'.$method)) { + $e = new \Exception($msg); + $r = new \ReflectionProperty($e, 'trace'); + $r->setAccessible(true); + $r->setValue($e, array_slice($trace, 1, $i)); + echo "\n".ucfirst($group).' deprecation triggered by '.$class.'::'.$method.':'; + echo "\n".$msg; + echo "\nStack trace:"; + echo "\n".str_replace(' '.getcwd().DIRECTORY_SEPARATOR, ' ', $e->getTraceAsString()); + echo "\n"; + + exit(1); + } if ('legacy' !== $group && 'weak' !== $mode) { $ref = &$deprecations[$group][$msg]['count']; ++$ref; @@ -78,7 +99,7 @@ public static function register($mode = false) restore_error_handler(); self::register($mode); } - } else { + } elseif (!isset($mode[0]) || '/' !== $mode[0]) { self::$isRegistered = true; if (self::hasColorSupport()) { $colorize = function ($str, $red) { diff --git a/src/Symfony/Bridge/PhpUnit/README.md b/src/Symfony/Bridge/PhpUnit/README.md index 7b3a7ef67762c..229e802be43cf 100644 --- a/src/Symfony/Bridge/PhpUnit/README.md +++ b/src/Symfony/Bridge/PhpUnit/README.md @@ -8,7 +8,8 @@ It comes with the following features: * disable the garbage collector; * enforce a consistent `C` locale; * auto-register `class_exists` to load Doctrine annotations; - * print a user deprecation notices summary at the end of the test suite. + * print a user deprecation notices summary at the end of the test suite; + * display the stack trace of a deprecation on-demand. By default any non-legacy-tagged or any non-@-silenced deprecation notices will make tests fail. @@ -51,3 +52,9 @@ You have to decide either to: * update your code to not use deprecated interfaces anymore, thus gaining better forward compatibility; * or move them to the **Legacy** section (by using one of the above way). + +In you need to inspect the stack trace of a particular deprecation triggered by +one of your unit tests, you can set the `SYMFONY_DEPRECATIONS_HELPER` env var to +a regexp that matches this test case's `class::method` name. For example, +`SYMFONY_DEPRECATIONS_HELPER=/^MyTest::testMethod$/ phpunit` will stop your test +suite once a deprecation is triggered by the `MyTest::testMethod` test. diff --git a/src/Symfony/Component/Form/ChoiceList/ArrayKeyChoiceList.php b/src/Symfony/Component/Form/ChoiceList/ArrayKeyChoiceList.php index fa2e3973f5867..e2e68f16f2db2 100644 --- a/src/Symfony/Component/Form/ChoiceList/ArrayKeyChoiceList.php +++ b/src/Symfony/Component/Form/ChoiceList/ArrayKeyChoiceList.php @@ -41,6 +41,8 @@ * ``` * * @author Bernhard Schussek + * + * @deprecated since version 2.8, to be removed in 3.0. Use ArrayChoiceList instead. */ class ArrayKeyChoiceList extends ArrayChoiceList { From 273ed2557362b157d303f5d4cb49a440a76c5fe6 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 25 Nov 2015 13:40:04 +0100 Subject: [PATCH 081/107] [Yaml] more fixes to changelog and upgrade files --- UPGRADE-2.8.md | 16 ++++++++++++++-- UPGRADE-3.0.md | 17 +++++++++++++++-- src/Symfony/Component/Yaml/CHANGELOG.md | 16 ++++++++++++++-- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/UPGRADE-2.8.md b/UPGRADE-2.8.md index 67e1c0e66f18a..651c637155972 100644 --- a/UPGRADE-2.8.md +++ b/UPGRADE-2.8.md @@ -511,5 +511,17 @@ Yaml * Deprecated usage of a colon in an unquoted mapping value * Deprecated usage of `@`, `` ` ``, `|`, and `>` at the beginning of an unquoted string - * Deprecated non-escaped \ in double-quoted strings when parsing Yaml - ("Foo\Var" is not valid whereas "Foo\\Var" is) + * When surrounding strings with double-quotes, you must now escape `\` characters. Not + escaping those characters (when surrounded by double-quotes) is deprecated. + + Before: + + ```yml + class: "Foo\Var" + ``` + + After: + + ```yml + class: "Foo\\Var" + ``` diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md index 5b86950fc694d..14992c149f427 100644 --- a/UPGRADE-3.0.md +++ b/UPGRADE-3.0.md @@ -1230,8 +1230,21 @@ UPGRADE FROM 2.x to 3.0 * Using a colon in an unquoted mapping value leads to a `ParseException`. * Starting an unquoted string with `@`, `` ` ``, `|`, or `>` leads to a `ParseException`. - * Deprecated non-escaped \ in double-quoted strings when parsing Yaml - ("Foo\Var" is not valid whereas "Foo\\Var" is) + * When surrounding strings with double-quotes, you must now escape `\` characters. Not + escaping those characters (when surrounded by double-quotes) leads to a `ParseException`. + + Before: + + ```yml + class: "Foo\Var" + ``` + + After: + + ```yml + class: "Foo\\Var" + ``` + * The ability to pass file names to `Yaml::parse()` has been removed. diff --git a/src/Symfony/Component/Yaml/CHANGELOG.md b/src/Symfony/Component/Yaml/CHANGELOG.md index ccfdb1a792c61..f55b57047ebef 100644 --- a/src/Symfony/Component/Yaml/CHANGELOG.md +++ b/src/Symfony/Component/Yaml/CHANGELOG.md @@ -6,8 +6,20 @@ CHANGELOG * Deprecated usage of a colon in an unquoted mapping value * Deprecated usage of @, \`, | and > at the beginning of an unquoted string - * Deprecated non-escaped \ in double-quoted strings when parsing Yaml - ("Foo\Var" is not valid whereas "Foo\\Var" is) + * When surrounding strings with double-quotes, you must now escape `\` characters. Not + escaping those characters (when surrounded by double-quotes) is deprecated. + + Before: + + ```yml + class: "Foo\Var" + ``` + + After: + + ```yml + class: "Foo\\Var" + ``` 2.1.0 ----- From 1cec08b844d818ae2dead2b09ec74bcf47c9608c Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 27 Nov 2015 23:51:43 +0100 Subject: [PATCH 082/107] [PhpUnitBridge] fix typo --- src/Symfony/Bridge/PhpUnit/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/PhpUnit/README.md b/src/Symfony/Bridge/PhpUnit/README.md index 229e802be43cf..9d7ca89838c6e 100644 --- a/src/Symfony/Bridge/PhpUnit/README.md +++ b/src/Symfony/Bridge/PhpUnit/README.md @@ -53,7 +53,7 @@ You have to decide either to: forward compatibility; * or move them to the **Legacy** section (by using one of the above way). -In you need to inspect the stack trace of a particular deprecation triggered by +In case you need to inspect the stack trace of a particular deprecation triggered by one of your unit tests, you can set the `SYMFONY_DEPRECATIONS_HELPER` env var to a regexp that matches this test case's `class::method` name. For example, `SYMFONY_DEPRECATIONS_HELPER=/^MyTest::testMethod$/ phpunit` will stop your test From 444b6eff0a5a8a80d260324ca9eff146750db407 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 28 Nov 2015 00:24:42 +0100 Subject: [PATCH 083/107] test legacy CSRF configuration options --- .../MainConfigurationTest.php | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php index 990632f2b683d..915c2326ca594 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php @@ -91,6 +91,32 @@ public function testCsrfAliases() $this->assertEquals('a_token_id', $processedConfig['firewalls']['stub']['logout']['csrf_token_id']); } + /** + * @group legacy + */ + public function testLegacyCsrfAliases() + { + $config = array( + 'firewalls' => array( + 'stub' => array( + 'logout' => array( + 'csrf_provider' => 'a_token_generator', + 'intention' => 'a_token_id', + ), + ), + ), + ); + $config = array_merge(static::$minimalConfig, $config); + + $processor = new Processor(); + $configuration = new MainConfiguration(array(), array()); + $processedConfig = $processor->processConfiguration($configuration, array($config)); + $this->assertTrue(isset($processedConfig['firewalls']['stub']['logout']['csrf_token_generator'])); + $this->assertEquals('a_token_generator', $processedConfig['firewalls']['stub']['logout']['csrf_token_generator']); + $this->assertTrue(isset($processedConfig['firewalls']['stub']['logout']['csrf_token_id'])); + $this->assertEquals('a_token_id', $processedConfig['firewalls']['stub']['logout']['csrf_token_id']); + } + /** * @expectedException \InvalidArgumentException */ From 59b782a200e9cd03082a931dd00e45d01cd0e444 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 28 Nov 2015 10:05:13 +0100 Subject: [PATCH 084/107] [ci] Force update of ./phpunit deps --- phpunit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit b/phpunit index 3a3b3dd881ef4..78710c12744ce 100755 --- a/phpunit +++ b/phpunit @@ -11,7 +11,7 @@ */ // Please update when phpunit needs to be reinstalled with fresh deps: -// Cache-Id-Version: 2015-11-18 14:14 UTC +// Cache-Id-Version: 2015-11-28 09:05 UTC use Symfony\Component\Process\ProcessUtils; From 62b3cb280557022500a865db8ac39f6c4b8d1064 Mon Sep 17 00:00:00 2001 From: Michal Piotrowski Date: Wed, 25 Nov 2015 23:16:51 +0100 Subject: [PATCH 085/107] AssetBundle - fix docs --- src/Symfony/Component/Asset/Package.php | 3 +++ .../Component/Asset/VersionStrategy/StaticVersionStrategy.php | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/Symfony/Component/Asset/Package.php b/src/Symfony/Component/Asset/Package.php index 43bdcf21db68a..77b1c934eb172 100644 --- a/src/Symfony/Component/Asset/Package.php +++ b/src/Symfony/Component/Asset/Package.php @@ -60,6 +60,9 @@ protected function getContext() return $this->context; } + /** + * @return VersionStrategyInterface + */ protected function getVersionStrategy() { return $this->versionStrategy; diff --git a/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php b/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php index 6028eb57fe5dd..857cf9432bfa3 100644 --- a/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php +++ b/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php @@ -21,6 +21,10 @@ class StaticVersionStrategy implements VersionStrategyInterface private $version; private $format; + /** + * @param string $version Version number + * @param string $format Url format + */ public function __construct($version, $format = null) { $this->version = $version; From 683f0f7315eca62de4ce270b9eeea4c0f51173f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sat, 14 Nov 2015 13:20:58 +0100 Subject: [PATCH 086/107] [Serializer] Improve ObjectNormalizer performance --- .../Normalizer/ObjectNormalizer.php | 99 ++++++++++++------- .../Tests/Normalizer/ObjectNormalizerTest.php | 14 ++- 2 files changed, 76 insertions(+), 37 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php index fe1676fbf36fb..c731dd7a97f17 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php @@ -26,6 +26,8 @@ */ class ObjectNormalizer extends AbstractNormalizer { + private static $attributesCache = array(); + /** * @var PropertyAccessorInterface */ @@ -58,42 +60,7 @@ public function normalize($object, $format = null, array $context = array()) } $data = array(); - $attributes = $this->getAllowedAttributes($object, $context, true); - - // If not using groups, detect manually - if (false === $attributes) { - $attributes = array(); - - // methods - $reflClass = new \ReflectionClass($object); - foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflMethod) { - if ( - !$reflMethod->isStatic() && - !$reflMethod->isConstructor() && - !$reflMethod->isDestructor() && - 0 === $reflMethod->getNumberOfRequiredParameters() - ) { - $name = $reflMethod->getName(); - - if (strpos($name, 'get') === 0 || strpos($name, 'has') === 0) { - // getters and hassers - $attributes[lcfirst(substr($name, 3))] = true; - } elseif (strpos($name, 'is') === 0) { - // issers - $attributes[lcfirst(substr($name, 2))] = true; - } - } - } - - // properties - foreach ($reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $reflProperty) { - if (!$reflProperty->isStatic()) { - $attributes[$reflProperty->getName()] = true; - } - } - - $attributes = array_keys($attributes); - } + $attributes = $this->getAttributes($object, $context); foreach ($attributes as $attribute) { if (in_array($attribute, $this->ignoredAttributes)) { @@ -162,4 +129,64 @@ public function denormalize($data, $class, $format = null, array $context = arra return $object; } + + /** + * Gets and caches attributes for this class and context. + * + * @param object $object + * @param array $context + * + * @return array + */ + private function getAttributes($object, array $context) + { + $key = sprintf('%s-%s', get_class($object), serialize($context)); + + if (isset(self::$attributesCache[$key])) { + return self::$attributesCache[$key]; + } + + $allowedAttributes = $this->getAllowedAttributes($object, $context, true); + + if (false !== $allowedAttributes) { + return self::$attributesCache[$key] = $allowedAttributes; + } + + // If not using groups, detect manually + $attributes = array(); + + // methods + $reflClass = new \ReflectionClass($object); + foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflMethod) { + if ( + $reflMethod->getNumberOfRequiredParameters() !== 0 || + $reflMethod->isStatic() || + $reflMethod->isConstructor() || + $reflMethod->isDestructor() + ) { + continue; + } + + $name = $reflMethod->getName(); + + if (strpos($name, 'get') === 0 || strpos($name, 'has') === 0) { + // getters and hassers + $attributes[lcfirst(substr($name, 3))] = true; + } elseif (strpos($name, 'is') === 0) { + // issers + $attributes[lcfirst(substr($name, 2))] = true; + } + } + + // properties + foreach ($reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $reflProperty) { + if ($reflProperty->isStatic()) { + continue; + } + + $attributes[$reflProperty->getName()] = true; + } + + return self::$attributesCache[$key] = array_keys($attributes); + } } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 80a021d08fa25..ec676efe1690e 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -29,7 +29,7 @@ class ObjectNormalizerTest extends \PHPUnit_Framework_TestCase { /** - * @var ObjectNormalizerTest + * @var ObjectNormalizer */ private $normalizer; /** @@ -239,6 +239,18 @@ public function testGroupsDenormalize() $this->assertEquals($obj, $normalized); } + public function testNormalizeNoPropertyInGroup() + { + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + $this->normalizer = new ObjectNormalizer($classMetadataFactory); + $this->normalizer->setSerializer($this->serializer); + + $obj = new GroupDummy(); + $obj->setFoo('foo'); + + $this->assertEquals(array(), $this->normalizer->normalize($obj, null, array('groups' => array('notExist')))); + } + public function testGroupsNormalizeWithNameConverter() { $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); From a35d3d44d0426a25683d2875208abb23b411a180 Mon Sep 17 00:00:00 2001 From: Pieter Date: Tue, 27 Oct 2015 13:04:29 +0200 Subject: [PATCH 087/107] [WIP] [Form] [TwigBridge] Bootstrap horizontal theme missing tests --- .../bootstrap_3_horizontal_layout.html.twig | 10 +- ...xtensionBootstrap3HorizontalLayoutTest.php | 118 +++++++++++++ ...AbstractBootstrap3HorizontalLayoutTest.php | 157 ++++++++++++++++++ 3 files changed, 279 insertions(+), 6 deletions(-) create mode 100644 src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php create mode 100644 src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig index 767e2798f3ee5..e997615d11378 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig @@ -25,15 +25,13 @@ col-sm-2 {# Rows #} {% block form_row -%} -{% spaceless %}
- {{ form_label(form) }} + {{- form_label(form) -}}
- {{ form_widget(form) }} - {{ form_errors(form) }} + {{- form_widget(form) -}} + {{- form_errors(form) -}}
-
-{% endspaceless %} +{##} {%- endblock form_row %} {% block checkbox_row -%} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php new file mode 100644 index 0000000000000..bf26dda05be92 --- /dev/null +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension; + +use Symfony\Bridge\Twig\Extension\FormExtension; +use Symfony\Bridge\Twig\Form\TwigRenderer; +use Symfony\Bridge\Twig\Form\TwigRendererEngine; +use Symfony\Bridge\Twig\Extension\TranslationExtension; +use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubTranslator; +use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubFilesystemLoader; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Tests\AbstractBootstrap3HorizontalLayoutTest; + +class FormExtensionBootstrap3HorizontalLayoutTest extends AbstractBootstrap3HorizontalLayoutTest +{ + /** + * @var FormExtension + */ + protected $extension; + + protected $testableFeatures = array( + 'choice_attr', + ); + + protected function setUp() + { + parent::setUp(); + + $rendererEngine = new TwigRendererEngine(array( + 'bootstrap_3_horizontal_layout.html.twig', + 'custom_widgets.html.twig', + )); + $renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')); + + $this->extension = new FormExtension($renderer); + + $loader = new StubFilesystemLoader(array( + __DIR__.'/../../Resources/views/Form', + __DIR__.'/Fixtures/templates/form', + )); + + $environment = new \Twig_Environment($loader, array('strict_variables' => true)); + $environment->addExtension(new TranslationExtension(new StubTranslator())); + $environment->addExtension($this->extension); + + $this->extension->initRuntime($environment); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->extension = null; + } + + protected function renderForm(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->renderBlock($view, 'form', $vars); + } + + protected function renderEnctype(FormView $view) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'enctype'); + } + + protected function renderLabel(FormView $view, $label = null, array $vars = array()) + { + if ($label !== null) { + $vars += array('label' => $label); + } + + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'label', $vars); + } + + protected function renderErrors(FormView $view) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'errors'); + } + + protected function renderWidget(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'widget', $vars); + } + + protected function renderRow(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'row', $vars); + } + + protected function renderRest(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'rest', $vars); + } + + protected function renderStart(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->renderBlock($view, 'form_start', $vars); + } + + protected function renderEnd(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->renderBlock($view, 'form_end', $vars); + } + + protected function setTheme(FormView $view, array $themes) + { + $this->extension->renderer->setTheme($view, $themes); + } +} diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php new file mode 100644 index 0000000000000..1273fa505bd2c --- /dev/null +++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php @@ -0,0 +1,157 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +abstract class AbstractBootstrap3HorizontalLayoutTest extends AbstractBootstrap3LayoutTest +{ + public function testLabelOnForm() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType'); + $view = $form->createView(); + $this->renderWidget($view, array('label' => 'foo')); + $html = $this->renderLabel($view); + + $this->assertMatchesXpath($html, +'/label + [@class="col-sm-2 control-label required"] + [.="[trans]Name[/trans]"] +' + ); + } + + public function testLabelDoesNotRenderFieldAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $html = $this->renderLabel($form->createView(), null, array( + 'attr' => array( + 'class' => 'my&class', + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="col-sm-2 control-label required"] +' + ); + } + + public function testLabelWithCustomAttributesPassedDirectly() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $html = $this->renderLabel($form->createView(), null, array( + 'label_attr' => array( + 'class' => 'my&class', + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="my&class col-sm-2 control-label required"] +' + ); + } + + public function testLabelWithCustomTextAndCustomAttributesPassedDirectly() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $html = $this->renderLabel($form->createView(), 'Custom label', array( + 'label_attr' => array( + 'class' => 'my&class', + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="my&class col-sm-2 control-label required"] + [.="[trans]Custom label[/trans]"] +' + ); + } + + public function testLabelWithCustomTextAsOptionAndCustomAttributesPassedDirectly() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array( + 'label' => 'Custom label', + )); + $html = $this->renderLabel($form->createView(), null, array( + 'label_attr' => array( + 'class' => 'my&class', + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="my&class col-sm-2 control-label required"] + [.="[trans]Custom label[/trans]"] +' + ); + } + + public function testStartTag() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => 'http://example.com/directory', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('
', $html); + } + + public function testStartTagWithOverriddenVars() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'put', + 'action' => 'http://example.com/directory', + )); + + $html = $this->renderStart($form->createView(), array( + 'method' => 'post', + 'action' => 'http://foo.com/directory', + )); + + $this->assertSame('', $html); + } + + public function testStartTagForMultipartForm() + { + $form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => 'http://example.com/directory', + )) + ->add('file', 'Symfony\Component\Form\Extension\Core\Type\FileType') + ->getForm(); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + public function testStartTagWithExtraAttributes() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => 'http://example.com/directory', + )); + + $html = $this->renderStart($form->createView(), array( + 'attr' => array('class' => 'foobar'), + )); + + $this->assertSame('', $html); + } +} From 982710ff18b9dc592cbdba8900db4a60f9daad98 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Thu, 22 Oct 2015 12:12:04 +0200 Subject: [PATCH 088/107] [HttpKernel] clearstatcache() so the Cache sees when a .lck file has been released --- src/Symfony/Component/HttpKernel/HttpCache/Store.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/HttpCache/Store.php b/src/Symfony/Component/HttpKernel/HttpCache/Store.php index 4901e2cf297aa..15a956d4d4908 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/Store.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/Store.php @@ -106,7 +106,10 @@ public function unlock(Request $request) public function isLocked(Request $request) { - return is_file($this->getPath($this->getCacheKey($request).'.lck')); + $path = $this->getPath($this->getCacheKey($request).'.lck'); + clearstatcache(true, $path); + + return is_file($path); } /** From f94c6d412646c1ba4ea8ed8fdaac5b49ce84d6e7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 28 Nov 2015 13:49:09 +0100 Subject: [PATCH 089/107] [Bundle\Twig] Fix lowet form dep --- src/Symfony/Bridge/Twig/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 32fcb84a080d9..5b002979a88df 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -22,7 +22,7 @@ "require-dev": { "symfony/asset": "~2.7|~3.0.0", "symfony/finder": "~2.3|~3.0.0", - "symfony/form": "~2.8", + "symfony/form": "~2.8,>2.8-BETA1", "symfony/http-kernel": "~2.8|~3.0.0", "symfony/polyfill-intl-icu": "~1.0", "symfony/routing": "~2.2|~3.0.0", From 04508658b52274dfd7a6d7502d565c764e569e5d Mon Sep 17 00:00:00 2001 From: Jakub Zalas Date: Sat, 28 Nov 2015 11:32:42 +0000 Subject: [PATCH 090/107] [Security][SecurityBundle] Use csrf_token_id instead of deprecated intention --- UPGRADE-2.8.md | 9 +++++++++ src/Symfony/Bundle/SecurityBundle/CHANGELOG.md | 1 + .../Security/Factory/FormLoginFactory.php | 2 +- .../DependencyInjection/SecurityExtension.php | 2 +- .../CsrfFormLoginBundle/Form/UserLoginType.php | 4 ++-- src/Symfony/Component/Security/CHANGELOG.md | 2 ++ .../Security/Http/Firewall/LogoutListener.php | 14 ++++++++++++-- .../Firewall/SimpleFormAuthenticationListener.php | 14 ++++++++++++-- .../UsernamePasswordFormAuthenticationListener.php | 14 ++++++++++++-- .../Http/Tests/Firewall/LogoutListenerTest.php | 2 +- 10 files changed, 53 insertions(+), 11 deletions(-) diff --git a/UPGRADE-2.8.md b/UPGRADE-2.8.md index bfd2aacc3ea84..80fef5d8c25f3 100644 --- a/UPGRADE-2.8.md +++ b/UPGRADE-2.8.md @@ -455,6 +455,15 @@ Security * The `VoterInterface::supportsClass` and `supportsAttribute` methods were deprecated and will be removed from the interface in 3.0. + * The `intention` option is deprecated for all the authentication listeners, + and will be removed in 3.0. Use the `csrf_token_id` option instead. + +SecurityBundle +-------------- + + * The `intention` firewall listener setting is deprecated, and will be removed in 3.0. + Use the `csrf_token_id` option instead. + Config ------ diff --git a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md index 21083ddbc92c0..11873ee9fae7d 100644 --- a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * deprecated the `key` setting of `anonymous`, `remember_me` and `http_digest` in favor of the `secret` setting. + * deprecated the `intention` firewall listener setting in favor of the `csrf_token_id`. 2.6.0 ----- diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php index c3a19e3f7edf4..aa81aa8b92f63 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php @@ -29,7 +29,7 @@ public function __construct() $this->addOption('username_parameter', '_username'); $this->addOption('password_parameter', '_password'); $this->addOption('csrf_parameter', '_csrf_token'); - $this->addOption('intention', 'authenticate'); + $this->addOption('csrf_token_id', 'authenticate'); $this->addOption('post_only', true); } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 5914e3fe2be33..68bbbc4fd374a 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -299,7 +299,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.logout_listener')); $listener->replaceArgument(3, array( 'csrf_parameter' => $firewall['logout']['csrf_parameter'], - 'intention' => $firewall['logout']['csrf_token_id'], + 'csrf_token_id' => $firewall['logout']['csrf_token_id'], 'logout_path' => $firewall['logout']['path'], )); $listeners[] = new Reference($listenerId); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php index 48b87fbecbfc1..0f4cd6bb844eb 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php @@ -79,12 +79,12 @@ public function buildForm(FormBuilderInterface $builder, array $options) */ public function configureOptions(OptionsResolver $resolver) { - /* Note: the form's intention must correspond to that for the form login + /* Note: the form's csrf_token_id must correspond to that for the form login * listener in order for the CSRF token to validate successfully. */ $resolver->setDefaults(array( - 'intention' => 'authenticate', + 'csrf_token_id' => 'authenticate', )); } } diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index 84fe742b729cc..b33f053a0b049 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -18,6 +18,8 @@ CHANGELOG `Symfony\Component\Security\Core\Authorization\Voter\VoterInterface`. * deprecated `getSupportedAttributes()` and `getSupportedClasses()` methods of `Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter`, use `supports()` instead. + * deprecated the `intention` option for all the authentication listeners, + use the `csrf_token_id` option instead. 2.7.0 ----- diff --git a/src/Symfony/Component/Security/Http/Firewall/LogoutListener.php b/src/Symfony/Component/Security/Http/Firewall/LogoutListener.php index 6211ee0323c71..e19d39cc2950d 100644 --- a/src/Symfony/Component/Security/Http/Firewall/LogoutListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/LogoutListener.php @@ -57,11 +57,21 @@ public function __construct(TokenStorageInterface $tokenStorage, HttpUtils $http throw new InvalidArgumentException('The CSRF token manager should be an instance of CsrfProviderInterface or CsrfTokenManagerInterface.'); } + if (isset($options['intention'])) { + if (isset($options['csrf_token_id'])) { + throw new \InvalidArgumentException(sprintf('You should only define an option for one of "intention" or "csrf_token_id" for the "%s". Use the "csrf_token_id" as it replaces "intention".', __CLASS__)); + } + + @trigger_error('The "intention" option for the '.__CLASS__.' is deprecated since version 2.8 and will be removed in 3.0. Use the "csrf_token_id" option instead.', E_USER_DEPRECATED); + + $options['csrf_token_id'] = $options['intention']; + } + $this->tokenStorage = $tokenStorage; $this->httpUtils = $httpUtils; $this->options = array_merge(array( 'csrf_parameter' => '_csrf_token', - 'intention' => 'logout', + 'csrf_token_id' => 'logout', 'logout_path' => '/logout', ), $options); $this->successHandler = $successHandler; @@ -101,7 +111,7 @@ public function handle(GetResponseEvent $event) if (null !== $this->csrfTokenManager) { $csrfToken = ParameterBagUtils::getRequestParameterValue($request, $this->options['csrf_parameter']); - if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['intention'], $csrfToken))) { + if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['csrf_token_id'], $csrfToken))) { throw new LogoutException('Invalid CSRF token.'); } } diff --git a/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php index fedaa4e62f00e..436376360be3a 100644 --- a/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php @@ -70,6 +70,16 @@ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationM throw new InvalidArgumentException('The CSRF token manager should be an instance of CsrfProviderInterface or CsrfTokenManagerInterface.'); } + if (isset($options['intention'])) { + if (isset($options['csrf_token_id'])) { + throw new \InvalidArgumentException(sprintf('You should only define an option for one of "intention" or "csrf_token_id" for the "%s". Use the "csrf_token_id" as it replaces "intention".', __CLASS__)); + } + + @trigger_error('The "intention" option for the '.__CLASS__.' is deprecated since version 2.8 and will be removed in 3.0. Use the "csrf_token_id" option instead.', E_USER_DEPRECATED); + + $options['csrf_token_id'] = $options['intention']; + } + $this->simpleAuthenticator = $simpleAuthenticator; $this->csrfTokenManager = $csrfTokenManager; @@ -77,7 +87,7 @@ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationM 'username_parameter' => '_username', 'password_parameter' => '_password', 'csrf_parameter' => '_csrf_token', - 'intention' => 'authenticate', + 'csrf_token_id' => 'authenticate', 'post_only' => true, ), $options); @@ -104,7 +114,7 @@ protected function attemptAuthentication(Request $request) if (null !== $this->csrfTokenManager) { $csrfToken = ParameterBagUtils::getRequestParameterValue($request, $this->options['csrf_parameter']); - if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['intention'], $csrfToken))) { + if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['csrf_token_id'], $csrfToken))) { throw new InvalidCsrfTokenException('Invalid CSRF token.'); } } diff --git a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php index d20ab19f62940..24c3ca6509c26 100644 --- a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php @@ -48,11 +48,21 @@ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationM throw new InvalidArgumentException('The CSRF token manager should be an instance of CsrfProviderInterface or CsrfTokenManagerInterface.'); } + if (isset($options['intention'])) { + if (isset($options['csrf_token_id'])) { + throw new \InvalidArgumentException(sprintf('You should only define an option for one of "intention" or "csrf_token_id" for the "%s". Use the "csrf_token_id" as it replaces "intention".', __CLASS__)); + } + + @trigger_error('The "intention" option for the '.__CLASS__.' is deprecated since version 2.8 and will be removed in 3.0. Use the "csrf_token_id" option instead.', E_USER_DEPRECATED); + + $options['csrf_token_id'] = $options['intention']; + } + parent::__construct($tokenStorage, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, array_merge(array( 'username_parameter' => '_username', 'password_parameter' => '_password', 'csrf_parameter' => '_csrf_token', - 'intention' => 'authenticate', + 'csrf_token_id' => 'authenticate', 'post_only' => true, ), $options), $logger, $dispatcher); @@ -79,7 +89,7 @@ protected function attemptAuthentication(Request $request) if (null !== $this->csrfTokenManager) { $csrfToken = ParameterBagUtils::getRequestParameterValue($request, $this->options['csrf_parameter']); - if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['intention'], $csrfToken))) { + if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['csrf_token_id'], $csrfToken))) { throw new InvalidCsrfTokenException('Invalid CSRF token.'); } } diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/LogoutListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/LogoutListenerTest.php index 15c996e6261a5..367c810f51f39 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/LogoutListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/LogoutListenerTest.php @@ -213,7 +213,7 @@ private function getListener($successHandler = null, $tokenManager = null) $successHandler ?: $this->getSuccessHandler(), $options = array( 'csrf_parameter' => '_csrf_token', - 'intention' => 'logout', + 'csrf_token_id' => 'logout', 'logout_path' => '/logout', 'target_url' => '/', ), From 96afff6a058deb9ea9d36fab3b1741ea9502943b Mon Sep 17 00:00:00 2001 From: WouterJ Date: Wed, 4 Nov 2015 14:58:00 +0100 Subject: [PATCH 091/107] [SecurityBundle] Fix disabling of RoleHierarchyVoter when passing empty hierarchy --- .../DependencyInjection/SecurityExtension.php | 2 +- .../SecurityExtensionTest.php | 27 +++++++++++++++++++ .../Voter/RoleHierarchyVoterTest.php | 15 +++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 68bbbc4fd374a..5f45b1f794bdb 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -168,7 +168,7 @@ private function configureDbalAclProvider(array $config, ContainerBuilder $conta */ private function createRoleHierarchy($config, ContainerBuilder $container) { - if (!isset($config['role_hierarchy'])) { + if (!isset($config['role_hierarchy']) || 0 === count($config['role_hierarchy'])) { $container->removeDefinition('security.access.role_hierarchy_voter'); return; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php index 4dd33020c572d..859f5b4e2d73f 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php @@ -94,6 +94,33 @@ public function testFirewallWithInvalidUserProvider() $container->compile(); } + public function testDisableRoleHierarchyVoter() + { + $container = $this->getRawContainer(); + + $container->loadFromExtension('security', array( + 'providers' => array( + 'default' => array('id' => 'foo'), + ), + + 'role_hierarchy' => null, + + 'firewalls' => array( + 'some_firewall' => array( + 'pattern' => '/.*', + 'http_basic' => null, + ), + ), + )); + + $container->compile(); + + $admDefinition = $container->getDefinition('security.access.decision_manager'); + $registeredVoters = array_map('strval', $admDefinition->getArgument(0)); + + $this->assertNotContains('security.access.role_hierarchy_voter', $registeredVoters); + } + protected function getRawContainer() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/RoleHierarchyVoterTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/RoleHierarchyVoterTest.php index c50ecf38c587d..4b03bacd784a5 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/RoleHierarchyVoterTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/RoleHierarchyVoterTest.php @@ -33,4 +33,19 @@ public function getVoteTests() array(array('ROLE_FOO'), array('ROLE_FOOBAR'), VoterInterface::ACCESS_GRANTED), )); } + + /** + * @dataProvider getVoteWithEmptyHierarchyTests + */ + public function testVoteWithEmptyHierarchy($roles, $attributes, $expected) + { + $voter = new RoleHierarchyVoter(new RoleHierarchy(array())); + + $this->assertSame($expected, $voter->vote($this->getToken($roles), null, $attributes)); + } + + public function getVoteWithEmptyHierarchyTests() + { + return parent::getVoteTests(); + } } From 6001644ffe6faa41c036c49b29213ae1ba99fd70 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 28 Nov 2015 17:51:37 +0100 Subject: [PATCH 092/107] [Form] Fix tests and reference usage --- .../Form/Extension/Core/Type/ChoiceType.php | 43 ++++++++++--------- ...AbstractBootstrap3HorizontalLayoutTest.php | 20 ++++----- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 4822bcf68b571..1465551b906a6 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -238,7 +238,7 @@ public function finishView(FormView $view, FormInterface $form, array $options) */ public function configureOptions(OptionsResolver $resolver) { - $choiceLabels = array(); + $choiceLabels = (object) array('labels' => array()); $choiceListFactory = $this->choiceListFactory; $emptyData = function (Options $options) { @@ -254,9 +254,9 @@ public function configureOptions(OptionsResolver $resolver) }; // BC closure, to be removed in 3.0 - $choicesNormalizer = function (Options $options, $choices) use (&$choiceLabels) { + $choicesNormalizer = function (Options $options, $choices) use ($choiceLabels) { // Unset labels from previous invocations - $choiceLabels = array(); + $choiceLabels->labels = array(); // This closure is irrelevant when "choices_as_values" is set to true if ($options['choices_as_values']) { @@ -269,7 +269,7 @@ public function configureOptions(OptionsResolver $resolver) }; // BC closure, to be removed in 3.0 - $choiceLabel = function (Options $options) use (&$choiceLabels) { + $choiceLabel = function (Options $options) use ($choiceLabels) { // If the choices contain duplicate labels, the normalizer of the // "choices" option stores them in the $choiceLabels variable @@ -277,14 +277,15 @@ public function configureOptions(OptionsResolver $resolver) $options->offsetGet('choices'); // Pick labels from $choiceLabels if available - // Don't invoke count() to avoid creating a copy of the array (yet) - if ($choiceLabels) { + if ($choiceLabels->labels) { // Don't pass the labels by reference. We do want to create a // copy here so that every form has an own version of that - // variable (contrary to the global reference shared by all + // variable (contrary to the $choiceLabels object shared by all // forms) - return function ($choice, $key) use ($choiceLabels) { - return $choiceLabels[$key]; + $labels = $choiceLabels->labels; + + return function ($choice, $key) use ($labels) { + return $labels[$key]; }; } @@ -502,26 +503,26 @@ private function createChoiceListView(ChoiceListInterface $choiceList, array $op * are lost. Store them in a utility array that is used from the * "choice_label" closure by default. * - * @param array $choices The choice labels indexed by choices. - * Labels are replaced by generated keys. - * @param array $choiceLabels The array that receives the choice labels - * indexed by generated keys. - * @param int|null $nextKey The next generated key. + * @param array $choices The choice labels indexed by choices. + * Labels are replaced by generated keys. + * @param object $choiceLabels The object that receives the choice labels + * indexed by generated keys. + * @param int $nextKey The next generated key. * * @internal Public only to be accessible from closures on PHP 5.3. Don't - * use this method, as it may be removed without notice. + * use this method as it may be removed without notice and will be in 3.0. */ - public static function normalizeLegacyChoices(array &$choices, array &$choiceLabels, &$nextKey = 0) + public static function normalizeLegacyChoices(array &$choices, $choiceLabels, &$nextKey = 0) { - foreach ($choices as $choice => &$choiceLabel) { + foreach ($choices as $choice => $choiceLabel) { if (is_array($choiceLabel)) { - self::normalizeLegacyChoices($choiceLabel, $choiceLabels, $nextKey); + $choiceLabel = ''; // Dereference $choices[$choice] + self::normalizeLegacyChoices($choices[$choice], $choiceLabels, $nextKey); continue; } - $choiceLabels[$nextKey] = $choiceLabel; - $choices[$choice] = $nextKey; - ++$nextKey; + $choiceLabels->labels[$nextKey] = $choiceLabel; + $choices[$choice] = $nextKey++; } } } diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php index 1273fa505bd2c..0fcfd60e47c1c 100644 --- a/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php @@ -15,7 +15,7 @@ abstract class AbstractBootstrap3HorizontalLayoutTest extends AbstractBootstrap3 { public function testLabelOnForm() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType'); + $form = $this->factory->createNamed('name', 'date'); $view = $form->createView(); $this->renderWidget($view, array('label' => 'foo')); $html = $this->renderLabel($view); @@ -30,7 +30,7 @@ public function testLabelOnForm() public function testLabelDoesNotRenderFieldAttributes() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form = $this->factory->createNamed('name', 'text'); $html = $this->renderLabel($form->createView(), null, array( 'attr' => array( 'class' => 'my&class', @@ -47,7 +47,7 @@ public function testLabelDoesNotRenderFieldAttributes() public function testLabelWithCustomAttributesPassedDirectly() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form = $this->factory->createNamed('name', 'text'); $html = $this->renderLabel($form->createView(), null, array( 'label_attr' => array( 'class' => 'my&class', @@ -64,7 +64,7 @@ public function testLabelWithCustomAttributesPassedDirectly() public function testLabelWithCustomTextAndCustomAttributesPassedDirectly() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form = $this->factory->createNamed('name', 'text'); $html = $this->renderLabel($form->createView(), 'Custom label', array( 'label_attr' => array( 'class' => 'my&class', @@ -82,7 +82,7 @@ public function testLabelWithCustomTextAndCustomAttributesPassedDirectly() public function testLabelWithCustomTextAsOptionAndCustomAttributesPassedDirectly() { - $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array( + $form = $this->factory->createNamed('name', 'text', null, array( 'label' => 'Custom label', )); $html = $this->renderLabel($form->createView(), null, array( @@ -102,7 +102,7 @@ public function testLabelWithCustomTextAsOptionAndCustomAttributesPassedDirectly public function testStartTag() { - $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + $form = $this->factory->create('form', null, array( 'method' => 'get', 'action' => 'http://example.com/directory', )); @@ -114,7 +114,7 @@ public function testStartTag() public function testStartTagWithOverriddenVars() { - $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + $form = $this->factory->create('form', null, array( 'method' => 'put', 'action' => 'http://example.com/directory', )); @@ -129,11 +129,11 @@ public function testStartTagWithOverriddenVars() public function testStartTagForMultipartForm() { - $form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + $form = $this->factory->createBuilder('form', null, array( 'method' => 'get', 'action' => 'http://example.com/directory', )) - ->add('file', 'Symfony\Component\Form\Extension\Core\Type\FileType') + ->add('file', 'file') ->getForm(); $html = $this->renderStart($form->createView()); @@ -143,7 +143,7 @@ public function testStartTagForMultipartForm() public function testStartTagWithExtraAttributes() { - $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + $form = $this->factory->create('form', null, array( 'method' => 'get', 'action' => 'http://example.com/directory', )); From 873a5417a8a440f9d4834f0e160fe7954977c9e2 Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Sat, 28 Nov 2015 23:15:59 +0100 Subject: [PATCH 093/107] [Console] do not encode backslashes in console default description --- src/Symfony/Component/Console/Descriptor/TextDescriptor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Console/Descriptor/TextDescriptor.php b/src/Symfony/Component/Console/Descriptor/TextDescriptor.php index fffd0e8486a2b..dce6af9f6d77c 100644 --- a/src/Symfony/Component/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Component/Console/Descriptor/TextDescriptor.php @@ -187,10 +187,10 @@ protected function describeApplication(Application $application, array $options private function formatDefaultValue($default) { if (PHP_VERSION_ID < 50400) { - return str_replace('\/', '/', json_encode($default)); + return str_replace(array('\/', '\\\\'), array('/', '\\'), json_encode($default)); } - return json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + return str_replace('\\\\', '\\', json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); } /** From 3f89b2c71484be0422568e5283ed95a70010a0bb Mon Sep 17 00:00:00 2001 From: Charles Sarrazin Date: Sat, 28 Nov 2015 23:09:12 +0100 Subject: [PATCH 094/107] Marked the Ldap component as internal and removed Ldap constants polyfill --- .../Component/Ldap/Exception/ConnectionException.php | 2 ++ src/Symfony/Component/Ldap/Exception/LdapException.php | 2 ++ src/Symfony/Component/Ldap/LdapClient.php | 2 ++ src/Symfony/Component/Ldap/LdapClientInterface.php | 5 ++--- src/Symfony/Component/Ldap/README.md | 10 +++++++--- .../Provider/LdapBindAuthenticationProvider.php | 2 +- .../Component/Security/Core/User/LdapUserProvider.php | 2 +- 7 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Ldap/Exception/ConnectionException.php b/src/Symfony/Component/Ldap/Exception/ConnectionException.php index 80d9af51ea911..d5023c5d02d7c 100644 --- a/src/Symfony/Component/Ldap/Exception/ConnectionException.php +++ b/src/Symfony/Component/Ldap/Exception/ConnectionException.php @@ -15,6 +15,8 @@ * ConnectionException is throw if binding to ldap can not be established. * * @author Grégoire Pineau + * + * @internal */ class ConnectionException extends \RuntimeException { diff --git a/src/Symfony/Component/Ldap/Exception/LdapException.php b/src/Symfony/Component/Ldap/Exception/LdapException.php index 213625b021b2b..ef3bd929bb852 100644 --- a/src/Symfony/Component/Ldap/Exception/LdapException.php +++ b/src/Symfony/Component/Ldap/Exception/LdapException.php @@ -15,6 +15,8 @@ * LdapException is throw if php ldap module is not loaded. * * @author Grégoire Pineau + * + * @internal */ class LdapException extends \RuntimeException { diff --git a/src/Symfony/Component/Ldap/LdapClient.php b/src/Symfony/Component/Ldap/LdapClient.php index ebb263d5cf0c5..0a8fa22c16c6f 100644 --- a/src/Symfony/Component/Ldap/LdapClient.php +++ b/src/Symfony/Component/Ldap/LdapClient.php @@ -18,6 +18,8 @@ * @author Grégoire Pineau * @author Francis Besset * @author Charles Sarrazin + * + * @internal */ class LdapClient implements LdapClientInterface { diff --git a/src/Symfony/Component/Ldap/LdapClientInterface.php b/src/Symfony/Component/Ldap/LdapClientInterface.php index 65dd03b38f8dc..dcdc0818da10b 100644 --- a/src/Symfony/Component/Ldap/LdapClientInterface.php +++ b/src/Symfony/Component/Ldap/LdapClientInterface.php @@ -18,12 +18,11 @@ * * @author Grégoire Pineau * @author Charles Sarrazin + * + * @internal */ interface LdapClientInterface { - const LDAP_ESCAPE_FILTER = 0x01; - const LDAP_ESCAPE_DN = 0x02; - /** * Return a connection bound to the ldap. * diff --git a/src/Symfony/Component/Ldap/README.md b/src/Symfony/Component/Ldap/README.md index 751ce6ad2511d..6de60f10bbb9e 100644 --- a/src/Symfony/Component/Ldap/README.md +++ b/src/Symfony/Component/Ldap/README.md @@ -1,10 +1,14 @@ Ldap Component -============= +============== A Ldap client for PHP on top of PHP's ldap extension. -This component also provides a stub for the missing -`ldap_escape` function in PHP versions lower than 5.6. +Disclaimer +---------- + +This component is currently marked as internal, as it +still needs some work. Breaking changes will be introduced +in the next minor version of Symfony. Documentation ------------- diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php index fab7d80a284e8..adc42ef3b38f5 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php @@ -74,7 +74,7 @@ protected function checkAuthentication(UserInterface $user, UsernamePasswordToke $password = $token->getCredentials(); try { - $username = $this->ldap->escape($username, '', LdapClientInterface::LDAP_ESCAPE_DN); + $username = $this->ldap->escape($username, '', LDAP_ESCAPE_DN); $dn = str_replace('{username}', $username, $this->dnString); $this->ldap->bind($dn, $password); diff --git a/src/Symfony/Component/Security/Core/User/LdapUserProvider.php b/src/Symfony/Component/Security/Core/User/LdapUserProvider.php index ec699fc022739..988a595f58b00 100644 --- a/src/Symfony/Component/Security/Core/User/LdapUserProvider.php +++ b/src/Symfony/Component/Security/Core/User/LdapUserProvider.php @@ -57,7 +57,7 @@ public function loadUserByUsername($username) { try { $this->ldap->bind($this->searchDn, $this->searchPassword); - $username = $this->ldap->escape($username, '', LdapClientInterface::LDAP_ESCAPE_FILTER); + $username = $this->ldap->escape($username, '', LDAP_ESCAPE_FILTER); $query = str_replace('{username}', $username, $this->defaultSearch); $search = $this->ldap->find($this->baseDn, $query); } catch (ConnectionException $e) { From b272ab569f0f7b4b1e44905d22dbba1c304bcc50 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 28 Nov 2015 23:24:25 +0100 Subject: [PATCH 095/107] Make sure security.role_hierarchy.roles always exists --- .../Bundle/SecurityBundle/Resources/config/security.xml | 1 + .../Tests/DependencyInjection/SecurityExtensionTest.php | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index a729b421f4b09..bbcae99853aa2 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -47,6 +47,7 @@ Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator Symfony\Component\Security\Core\Authorization\ExpressionLanguage + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php index 859f5b4e2d73f..dce30c758b976 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php @@ -115,10 +115,7 @@ public function testDisableRoleHierarchyVoter() $container->compile(); - $admDefinition = $container->getDefinition('security.access.decision_manager'); - $registeredVoters = array_map('strval', $admDefinition->getArgument(0)); - - $this->assertNotContains('security.access.role_hierarchy_voter', $registeredVoters); + $this->assertFalse($container->hasDefinition('security.access.role_hierarchy_voter')); } protected function getRawContainer() From 7d9237200fc6eb986f5f3e9fb6740e1ec29bfeaf Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 29 Nov 2015 10:45:59 +0100 Subject: [PATCH 096/107] [Bridge/Doctrine] Fix legacy tests --- .../ChoiceList/AbstractEntityChoiceListCompositeIdTest.php | 4 ++++ .../AbstractEntityChoiceListSingleAssociationToIntIdTest.php | 4 ++++ .../ChoiceList/AbstractEntityChoiceListSingleIntIdTest.php | 4 ++++ .../ChoiceList/AbstractEntityChoiceListSingleStringIdTest.php | 4 ++++ .../Tests/Form/ChoiceList/AbstractEntityChoiceListTest.php | 4 ++++ .../Form/ChoiceList/LoadedEntityChoiceListCompositeIdTest.php | 4 ++++ .../LoadedEntityChoiceListSingleAssociationToIntIdTest.php | 4 ++++ .../Form/ChoiceList/LoadedEntityChoiceListSingleIntIdTest.php | 4 ++++ .../ChoiceList/LoadedEntityChoiceListSingleStringIdTest.php | 4 ++++ .../ChoiceList/UnloadedEntityChoiceListCompositeIdTest.php | 4 ++++ ...nloadedEntityChoiceListCompositeIdWithQueryBuilderTest.php | 4 ++++ .../UnloadedEntityChoiceListSingleAssociationToIntIdTest.php | 4 ++++ ...ChoiceListSingleAssociationToIntIdWithQueryBuilderTest.php | 4 ++++ .../ChoiceList/UnloadedEntityChoiceListSingleIntIdTest.php | 4 ++++ ...nloadedEntityChoiceListSingleIntIdWithQueryBuilderTest.php | 4 ++++ .../ChoiceList/UnloadedEntityChoiceListSingleStringIdTest.php | 4 ++++ ...adedEntityChoiceListSingleStringIdWithQueryBuilderTest.php | 4 ++++ 17 files changed, 68 insertions(+) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListCompositeIdTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListCompositeIdTest.php index 5980d9c734c54..8bf2fb804f939 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListCompositeIdTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListCompositeIdTest.php @@ -13,6 +13,10 @@ use Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity; +if (!class_exists('Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest')) { + return; +} + /** * @author Bernhard Schussek */ diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListSingleAssociationToIntIdTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListSingleAssociationToIntIdTest.php index 7324f721ec340..02e117d7baec4 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListSingleAssociationToIntIdTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListSingleAssociationToIntIdTest.php @@ -15,6 +15,10 @@ use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity; use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList; +if (!class_exists('Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest')) { + return; +} + /** * Test choices generated from an entity with a primary foreign key. * diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListSingleIntIdTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListSingleIntIdTest.php index 74af66db360e2..85ea10a451c22 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListSingleIntIdTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListSingleIntIdTest.php @@ -13,6 +13,10 @@ use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity; +if (!class_exists('Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest')) { + return; +} + /** * @author Bernhard Schussek */ diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListSingleStringIdTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListSingleStringIdTest.php index 56b4c21319826..1bd5ac8de4695 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListSingleStringIdTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListSingleStringIdTest.php @@ -13,6 +13,10 @@ use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity; +if (!class_exists('Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest')) { + return; +} + /** * @author Bernhard Schussek */ diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListTest.php index 4f3d54a30f15f..b2d5b4a888b37 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/AbstractEntityChoiceListTest.php @@ -16,6 +16,10 @@ use Doctrine\ORM\Tools\SchemaTool; use Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest; +if (!class_exists('Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest')) { + return; +} + /** * @author Bernhard Schussek */ diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/LoadedEntityChoiceListCompositeIdTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/LoadedEntityChoiceListCompositeIdTest.php index a2ee7cdc8a64f..21fa4c56a0b57 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/LoadedEntityChoiceListCompositeIdTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/LoadedEntityChoiceListCompositeIdTest.php @@ -11,6 +11,10 @@ namespace Symfony\Bridge\Doctrine\Tests\Form\ChoiceList; +if (!class_exists('Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest')) { + return; +} + /** * @author Bernhard Schussek * @group legacy diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/LoadedEntityChoiceListSingleAssociationToIntIdTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/LoadedEntityChoiceListSingleAssociationToIntIdTest.php index 60e3797bac865..73344be670eba 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/LoadedEntityChoiceListSingleAssociationToIntIdTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/LoadedEntityChoiceListSingleAssociationToIntIdTest.php @@ -11,6 +11,10 @@ namespace Symfony\Bridge\Doctrine\Tests\Form\ChoiceList; +if (!class_exists('Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest')) { + return; +} + /** * @author Premi Giorgio * @author Bernhard Schussek diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/LoadedEntityChoiceListSingleIntIdTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/LoadedEntityChoiceListSingleIntIdTest.php index f655784004fbb..f64e0cec3e538 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/LoadedEntityChoiceListSingleIntIdTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/LoadedEntityChoiceListSingleIntIdTest.php @@ -11,6 +11,10 @@ namespace Symfony\Bridge\Doctrine\Tests\Form\ChoiceList; +if (!class_exists('Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest')) { + return; +} + /** * @author Bernhard Schussek * @group legacy diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/LoadedEntityChoiceListSingleStringIdTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/LoadedEntityChoiceListSingleStringIdTest.php index 629b399ac36a7..d0b04b8013656 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/LoadedEntityChoiceListSingleStringIdTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/LoadedEntityChoiceListSingleStringIdTest.php @@ -11,6 +11,10 @@ namespace Symfony\Bridge\Doctrine\Tests\Form\ChoiceList; +if (!class_exists('Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest')) { + return; +} + /** * @author Bernhard Schussek * @group legacy diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListCompositeIdTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListCompositeIdTest.php index 114eee661efe5..305f365075ebe 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListCompositeIdTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListCompositeIdTest.php @@ -11,6 +11,10 @@ namespace Symfony\Bridge\Doctrine\Tests\Form\ChoiceList; +if (!class_exists('Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest')) { + return; +} + /** * @author Bernhard Schussek * @group legacy diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListCompositeIdWithQueryBuilderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListCompositeIdWithQueryBuilderTest.php index 422295feb1e04..75e36f90cf4e3 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListCompositeIdWithQueryBuilderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListCompositeIdWithQueryBuilderTest.php @@ -14,6 +14,10 @@ use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList; use Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader; +if (!class_exists('Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest')) { + return; +} + /** * @author Bernhard Schussek * @group legacy diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleAssociationToIntIdTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleAssociationToIntIdTest.php index 20267bd020775..ee3afeb89909f 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleAssociationToIntIdTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleAssociationToIntIdTest.php @@ -11,6 +11,10 @@ namespace Symfony\Bridge\Doctrine\Tests\Form\ChoiceList; +if (!class_exists('Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest')) { + return; +} + /** * @author Premi Giorgio * @author Bernhard Schussek diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleAssociationToIntIdWithQueryBuilderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleAssociationToIntIdWithQueryBuilderTest.php index 45ab799585cb6..1b637949bbd09 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleAssociationToIntIdWithQueryBuilderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleAssociationToIntIdWithQueryBuilderTest.php @@ -14,6 +14,10 @@ use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList; use Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader; +if (!class_exists('Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest')) { + return; +} + /** * @author Premi Giorgio * @author Bernhard Schussek diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleIntIdTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleIntIdTest.php index 0668c09bdb63f..0197f19b3b1fa 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleIntIdTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleIntIdTest.php @@ -11,6 +11,10 @@ namespace Symfony\Bridge\Doctrine\Tests\Form\ChoiceList; +if (!class_exists('Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest')) { + return; +} + /** * @author Bernhard Schussek * @group legacy diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleIntIdWithQueryBuilderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleIntIdWithQueryBuilderTest.php index c093782ff0ec7..4e75cf7491cca 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleIntIdWithQueryBuilderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleIntIdWithQueryBuilderTest.php @@ -14,6 +14,10 @@ use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList; use Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader; +if (!class_exists('Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest')) { + return; +} + /** * @author Bernhard Schussek * @group legacy diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleStringIdTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleStringIdTest.php index 39363bae26a25..3e9847f0e2eff 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleStringIdTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleStringIdTest.php @@ -11,6 +11,10 @@ namespace Symfony\Bridge\Doctrine\Tests\Form\ChoiceList; +if (!class_exists('Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest')) { + return; +} + /** * @author Bernhard Schussek * @group legacy diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleStringIdWithQueryBuilderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleStringIdWithQueryBuilderTest.php index 23329e80df3c2..51adaabba80db 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleStringIdWithQueryBuilderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/UnloadedEntityChoiceListSingleStringIdWithQueryBuilderTest.php @@ -14,6 +14,10 @@ use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList; use Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader; +if (!class_exists('Symfony\Component\Form\Tests\Extension\Core\ChoiceList\AbstractChoiceListTest')) { + return; +} + /** * @author Bernhard Schussek * @group legacy From beb8525fe4baa35f9885601acb239b448857a59c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 29 Nov 2015 10:55:05 +0100 Subject: [PATCH 097/107] [Bridge/Twig] Fix lowest form dep --- src/Symfony/Bridge/Twig/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 7ce35a9fac00b..77239821df374 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -22,7 +22,7 @@ "require-dev": { "symfony/asset": "~2.7", "symfony/finder": "~2.3", - "symfony/form": "~2.7,>=2.7.6", + "symfony/form": "~2.7.8|~2.8,>2.8-BETA1", "symfony/http-kernel": "~2.3", "symfony/intl": "~2.3", "symfony/routing": "~2.2", From 613804d0cf8871af0e3c151978f5a92f7c274d37 Mon Sep 17 00:00:00 2001 From: Martin Hujer Date: Sun, 29 Nov 2015 08:43:05 +0100 Subject: [PATCH 098/107] [Yaml] minor CS cleaning --- src/Symfony/Component/Yaml/Parser.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 9dffa2781113e..94a1628afaa49 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -311,7 +311,7 @@ private function getNextEmbedBlock($indentation = null, $inSequence = false) if (null === $indentation) { $newIndent = $this->getCurrentLineIndentation(); - $unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem($this->currentLine); + $unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem(); if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) { throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine); @@ -337,7 +337,7 @@ private function getNextEmbedBlock($indentation = null, $inSequence = false) return; } - $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem($this->currentLine); + $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem(); // Comments must not be removed inside a block scalar $removeCommentsPattern = '~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~'; @@ -350,7 +350,7 @@ private function getNextEmbedBlock($indentation = null, $inSequence = false) $removeComments = !preg_match($removeCommentsPattern, $this->currentLine); } - if ($isItUnindentedCollection && !$this->isStringUnIndentedCollectionItem($this->currentLine) && $newIndent === $indent) { + if ($isItUnindentedCollection && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) { $this->moveToPreviousLine(); break; } @@ -653,7 +653,7 @@ private function isNextLineUnIndentedCollection() if ( $this->getCurrentLineIndentation() == $currentIndentation && - $this->isStringUnIndentedCollectionItem($this->currentLine) + $this->isStringUnIndentedCollectionItem() ) { $ret = true; } From 4819a702d7af8dc43a395d7686d5873a6a439d13 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 29 Nov 2015 17:07:42 +0100 Subject: [PATCH 099/107] [Bridge/Doctrine+Ldap] Fix tests --- .../Doctrine/Tests/Form/Type/EntityTypeTest.php | 16 ++++++++-------- .../LdapBindAuthenticationProviderTest.php | 3 +++ .../Core/Tests/User/LdapUserProviderTest.php | 3 +++ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php index fb78d65ef1813..81d00fc7354ba 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php @@ -995,13 +995,13 @@ public function testLoaderCaching() 'property3' => 2, )); - $choiceList1 = $form->get('property1')->getConfig()->getOption('choice_list'); - $choiceList2 = $form->get('property2')->getConfig()->getOption('choice_list'); - $choiceList3 = $form->get('property3')->getConfig()->getOption('choice_list'); + $choiceLoader1 = $form->get('property1')->getConfig()->getOption('choice_loader'); + $choiceLoader2 = $form->get('property2')->getConfig()->getOption('choice_loader'); + $choiceLoader3 = $form->get('property3')->getConfig()->getOption('choice_loader'); - $this->assertInstanceOf('Symfony\Component\Form\ChoiceList\ChoiceListInterface', $choiceList1); - $this->assertSame($choiceList1, $choiceList2); - $this->assertSame($choiceList1, $choiceList3); + $this->assertInstanceOf('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface', $choiceLoader1); + $this->assertSame($choiceLoader1, $choiceLoader2); + $this->assertSame($choiceLoader1, $choiceLoader3); } public function testCacheChoiceLists() @@ -1024,8 +1024,8 @@ public function testCacheChoiceLists() 'choice_label' => 'name', )); - $this->assertInstanceOf('Symfony\Component\Form\ChoiceList\ChoiceListInterface', $field1->getConfig()->getOption('choice_list')); - $this->assertSame($field1->getConfig()->getOption('choice_list'), $field2->getConfig()->getOption('choice_list')); + $this->assertInstanceOf('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface', $field1->getConfig()->getOption('choice_loader')); + $this->assertSame($field1->getConfig()->getOption('choice_loader'), $field2->getConfig()->getOption('choice_loader')); } /** diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php index f1b5c0355ff66..844bceff019cb 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php @@ -16,6 +16,9 @@ use Symfony\Component\Security\Core\User\User; use Symfony\Component\Ldap\Exception\ConnectionException; +/** + * @requires extension ldap + */ class LdapBindAuthenticationProviderTest extends \PHPUnit_Framework_TestCase { /** diff --git a/src/Symfony/Component/Security/Core/Tests/User/LdapUserProviderTest.php b/src/Symfony/Component/Security/Core/Tests/User/LdapUserProviderTest.php index f56648b9f1aaa..9b126e95180f3 100644 --- a/src/Symfony/Component/Security/Core/Tests/User/LdapUserProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/User/LdapUserProviderTest.php @@ -14,6 +14,9 @@ use Symfony\Component\Security\Core\User\LdapUserProvider; use Symfony\Component\Ldap\Exception\ConnectionException; +/** + * @requires extension ldap + */ class LdapUserProviderTest extends \PHPUnit_Framework_TestCase { /** From ef61e5481f736cf3ff67dd661282fcbd2a2c1504 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 29 Nov 2015 17:56:39 +0100 Subject: [PATCH 100/107] Forward compatibility with AbstractLayout* 2.8 tests --- ...xtensionBootstrap3HorizontalLayoutTest.php | 35 +++++++++++++++++++ .../FormExtensionBootstrap3LayoutTest.php | 25 +++++++++++++ .../Extension/FormExtensionDivLayoutTest.php | 25 +++++++++++++ .../FormExtensionTableLayoutTest.php | 25 +++++++++++++ .../Helper/FormHelperDivLayoutTest.php | 25 +++++++++++++ .../Helper/FormHelperTableLayoutTest.php | 25 +++++++++++++ 6 files changed, 160 insertions(+) diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php index bf26dda05be92..c7195ab577820 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php @@ -115,4 +115,39 @@ protected function setTheme(FormView $view, array $themes) { $this->extension->renderer->setTheme($view, $themes); } + + public function testRange() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testRangeWithMinMaxValues() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testLabelWithoutTranslationOnButton() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testSingleChoiceWithPlaceholderWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testButtonlabelWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testAttributesNotTranslatedWhenTranslationDomainIsFalse() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php index a9d161b2b909a..406c1cef16bf4 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php @@ -125,4 +125,29 @@ public function testRangeWithMinMaxValues() { // No-op for forward compatibility with AbstractLayoutTest 2.8 } + + public function testLabelWithoutTranslationOnButton() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testSingleChoiceWithPlaceholderWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testButtonlabelWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testAttributesNotTranslatedWhenTranslationDomainIsFalse() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php index 1bce43b83780c..9e39ac35dc421 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php @@ -218,4 +218,29 @@ public function testRangeWithMinMaxValues() { // No-op for forward compatibility with AbstractLayoutTest 2.8 } + + public function testLabelWithoutTranslationOnButton() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testSingleChoiceWithPlaceholderWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testButtonlabelWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testAttributesNotTranslatedWhenTranslationDomainIsFalse() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php index 555ea306fca89..7fa88eef00c7e 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php @@ -126,4 +126,29 @@ public function testRangeWithMinMaxValues() { // No-op for forward compatibility with AbstractLayoutTest 2.8 } + + public function testLabelWithoutTranslationOnButton() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testSingleChoiceWithPlaceholderWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testButtonlabelWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testAttributesNotTranslatedWhenTranslationDomainIsFalse() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php index 4af5b929cbb50..217fd5e50e237 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php @@ -138,4 +138,29 @@ public function testRangeWithMinMaxValues() { // No-op for forward compatibility with AbstractLayoutTest 2.8 } + + public function testLabelWithoutTranslationOnButton() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testSingleChoiceWithPlaceholderWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testButtonlabelWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testAttributesNotTranslatedWhenTranslationDomainIsFalse() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php index 1bf641fe1b93f..99bfba0c4e7cc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php @@ -125,4 +125,29 @@ public function testRangeWithMinMaxValues() { // No-op for forward compatibility with AbstractLayoutTest 2.8 } + + public function testLabelWithoutTranslationOnButton() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testSingleChoiceWithPlaceholderWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testButtonlabelWithoutTranslation() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } + + public function testAttributesNotTranslatedWhenTranslationDomainIsFalse() + { + // No-op for forward compatibility with AbstractLayoutTest 2.8 + } } From 21270585a64836fc4d8772976c4b5aa5a346d71e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 29 Nov 2015 19:05:35 +0100 Subject: [PATCH 101/107] [Yaml] look for colon in parsed inline string Looking for a colon in an unquoted mapping value can lead to falsely reported parse errors (e.g. when a comment after the mapping value contains a colon). --- src/Symfony/Component/Yaml/Parser.php | 16 +++++++++------- src/Symfony/Component/Yaml/Tests/ParserTest.php | 10 ++++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index e595b404d4ed7..a5c0e674c3d5a 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -473,15 +473,17 @@ private function parseValue($value, $exceptionOnInvalidType, $objectSupport, $ob return $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers)); } - if ('mapping' === $context && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($value, ': ')) { - @trigger_error(sprintf('Using a colon in an unquoted mapping value in line %d is deprecated since Symfony 2.8 and will throw a ParseException in 3.0.', $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED); + try { + $parsedValue = Inline::parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs); - // to be thrown in 3.0 - // throw new ParseException('A colon cannot be used in an unquoted mapping value.'); - } + if ('mapping' === $context && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) { + @trigger_error(sprintf('Using a colon in an unquoted mapping value in line %d is deprecated since Symfony 2.8 and will throw a ParseException in 3.0.', $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED); - try { - return Inline::parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs); + // to be thrown in 3.0 + // throw new ParseException('A colon cannot be used in an unquoted mapping value.'); + } + + return $parsedValue; } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index e4c02d77b7979..d5a4c35b85cbf 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -808,6 +808,16 @@ public function testColonInMappingValueException() restore_error_handler(); } + + public function testColonInMappingValueExceptionNotTriggeredByColonInComment() + { + $yaml = <<assertSame(array('foo' => array('bar' => 'foobar')), $this->parser->parse($yaml)); + } } class B From 9afbea27e9f8b7d9a976df14a0e40ee88d7214de Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 30 Nov 2015 09:52:07 +0100 Subject: [PATCH 102/107] [Process] Fix signaling/stopping logic on Windows --- src/Symfony/Component/Process/Process.php | 33 +++++++++++------------ 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index 1675ad33994aa..676cddc67eea4 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -647,22 +647,14 @@ public function getStatus() * Stops the process. * * @param int|float $timeout The timeout in seconds - * @param int $signal A POSIX signal to send in case the process has not stop at timeout, default is SIGKILL + * @param int $signal A POSIX signal to send in case the process has not stop at timeout, default is SIGKILL (9) * * @return int The exit-code of the process - * - * @throws RuntimeException if the process got signaled */ public function stop($timeout = 10, $signal = null) { $timeoutMicro = microtime(true) + $timeout; if ($this->isRunning()) { - if ('\\' === DIRECTORY_SEPARATOR && !$this->isSigchildEnabled()) { - exec(sprintf('taskkill /F /T /PID %d 2>&1', $this->getPid()), $output, $exitCode); - if ($exitCode > 0) { - throw new RuntimeException('Unable to kill the process'); - } - } // given `SIGTERM` may not be defined and that `proc_terminate` uses the constant value and not the constant itself, we use the same here $this->doSignal(15, false); do { @@ -670,13 +662,9 @@ public function stop($timeout = 10, $signal = null) } while ($this->isRunning() && microtime(true) < $timeoutMicro); if ($this->isRunning() && !$this->isSigchildEnabled()) { - if (null !== $signal || defined('SIGKILL')) { - // avoid exception here : - // process is supposed to be running, but it might have stop - // just after this line. - // in any case, let's silently discard the error, we can not do anything - $this->doSignal($signal ?: SIGKILL, false); - } + // Avoid exception here: process is supposed to be running, but it might have stopped just + // after this line. In any case, let's silently discard the error, we cannot do anything. + $this->doSignal($signal ?: 9, false); } } @@ -1200,7 +1188,18 @@ private function doSignal($signal, $throwException) return false; } - if (true !== @proc_terminate($this->process, $signal)) { + if ('\\' === DIRECTORY_SEPARATOR) { + exec(sprintf('taskkill /F /T /PID %d 2>&1', $this->getPid()), $output, $exitCode); + if ($exitCode) { + if ($throwException) { + throw new RuntimeException(sprintf('Unable to kill the process (%s).', implode(' ', $output))); + } + + return false; + } + } + + if (true !== @proc_terminate($this->process, $signal) && '\\' !== DIRECTORY_SEPARATOR) { if ($throwException) { throw new RuntimeException(sprintf('Error while sending signal `%s`.', $signal)); } From 346943e2f51fa491a58ca1a2185e5987797f8346 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 30 Nov 2015 11:54:38 +0100 Subject: [PATCH 103/107] add subject variable to expression context --- UPGRADE-2.8.md | 3 +++ .../Security/Core/Authorization/Voter/ExpressionVoter.php | 1 + 2 files changed, 4 insertions(+) diff --git a/UPGRADE-2.8.md b/UPGRADE-2.8.md index 797e2a5015b0a..883ef576d328a 100644 --- a/UPGRADE-2.8.md +++ b/UPGRADE-2.8.md @@ -448,6 +448,9 @@ FrameworkBundle Security -------- + * The `object` variable passed to expressions evaluated by the `ExpressionVoter` + is deprecated. Instead use the new `subject` variable. + * The `AbstractVoter` class was deprecated. Instead, extend the `Voter` class and move your voting logic in the `supports($attribute, $subject)` and `voteOnAttribute($attribute, $object, TokenInterface $token)` methods. diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php index 98b8f50f15d03..96a7ece998cce 100644 --- a/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php @@ -102,6 +102,7 @@ private function getVariables(TokenInterface $token, $object) 'token' => $token, 'user' => $token->getUser(), 'object' => $object, + 'subject' => $object, 'roles' => array_map(function ($role) { return $role->getRole(); }, $roles), 'trust_resolver' => $this->trustResolver, ); From 0a54d098a9c72a6a82aa6fd49f62fc260be84657 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Mon, 30 Nov 2015 14:33:56 +0100 Subject: [PATCH 104/107] Fix BC for the default root form name The block prefix is used, to match the previous behavior when using a custom block prefix. --- src/Symfony/Component/Form/FormFactory.php | 22 ++++++-- .../Component/Form/Tests/FormFactoryTest.php | 51 ++++++++++++++++--- 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Component/Form/FormFactory.php b/src/Symfony/Component/Form/FormFactory.php index e00d8ab4fad29..5e92baa64f925 100644 --- a/src/Symfony/Component/Form/FormFactory.php +++ b/src/Symfony/Component/Form/FormFactory.php @@ -74,15 +74,29 @@ public function createBuilder($type = 'Symfony\Component\Form\Extension\Core\Typ $typeName = $type->getName(); } } elseif ($type instanceof FormTypeInterface) { - // BC - $typeName = $type->getName(); + if (method_exists($type, 'getBlockPrefix')) { + // As of Symfony 3.0, the block prefix of the type is used as + // default name + $name = $type->getBlockPrefix(); + } else { + // BC + $typeName = $type->getName(); + } } elseif (is_string($type)) { - // BC - $typeName = $type; + $resolvedType = $this->registry->getType($type); + if (method_exists($resolvedType, 'getBlockPrefix')) { + // As of Symfony 3.0, the block prefix of the type is used as + // default name + $name = $resolvedType->getBlockPrefix(); + } else { + // BC + $typeName = $type; + } } else { throw new UnexpectedTypeException($type, 'string, Symfony\Component\Form\ResolvedFormTypeInterface or Symfony\Component\Form\FormTypeInterface'); } + // BC when there is no block prefix if (null === $name) { if (false === strpos($typeName, '\\')) { // No FQCN - leave unchanged for BC diff --git a/src/Symfony/Component/Form/Tests/FormFactoryTest.php b/src/Symfony/Component/Form/Tests/FormFactoryTest.php index 1f65c3c0c1499..0ec0b40590e78 100644 --- a/src/Symfony/Component/Form/Tests/FormFactoryTest.php +++ b/src/Symfony/Component/Form/Tests/FormFactoryTest.php @@ -302,13 +302,52 @@ public function testCreateThrowsUnderstandableException() $this->factory->create(new \stdClass()); } + public function testCreateUsesBlockPrefixIfTypeGivenAsString() + { + $options = array('a' => '1', 'b' => '2'); + $resolvedOptions = array('a' => '2', 'b' => '3'); + + // the interface does not have the method, so use the real class + $resolvedType = $this->getMockBuilder('Symfony\Component\Form\ResolvedFormType') + ->disableOriginalConstructor() + ->getMock(); + + $resolvedType->expects($this->any()) + ->method('getBlockPrefix') + ->willReturn('TYPE_PREFIX'); + + $this->registry->expects($this->any()) + ->method('getType') + ->with('TYPE') + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'TYPE_PREFIX', $options) + ->will($this->returnValue($this->builder)); + + $this->builder->expects($this->any()) + ->method('getOptions') + ->will($this->returnValue($resolvedOptions)); + + $resolvedType->expects($this->once()) + ->method('buildForm') + ->with($this->builder, $resolvedOptions); + + $this->builder->expects($this->once()) + ->method('getForm') + ->will($this->returnValue('FORM')); + + $this->assertSame('FORM', $this->factory->create('TYPE', null, $options)); + } + public function testCreateUsesTypeNameIfTypeGivenAsString() { $options = array('a' => '1', 'b' => '2'); $resolvedOptions = array('a' => '2', 'b' => '3'); $resolvedType = $this->getMockResolvedType(); - $this->registry->expects($this->once()) + $this->registry->expects($this->any()) ->method('getType') ->with('TYPE') ->will($this->returnValue($resolvedType)); @@ -339,7 +378,7 @@ public function testCreateStripsNamespaceOffTypeName() $resolvedOptions = array('a' => '2', 'b' => '3'); $resolvedType = $this->getMockResolvedType(); - $this->registry->expects($this->once()) + $this->registry->expects($this->any()) ->method('getType') ->with('Vendor\Name\Space\UserForm') ->will($this->returnValue($resolvedType)); @@ -370,7 +409,7 @@ public function testLegacyCreateStripsNamespaceOffTypeNameAccessByFQCN() $resolvedOptions = array('a' => '2', 'b' => '3'); $resolvedType = $this->getMockResolvedType(); - $this->registry->expects($this->once()) + $this->registry->expects($this->any()) ->method('getType') ->with('userform') ->will($this->returnValue($resolvedType)); @@ -401,7 +440,7 @@ public function testCreateStripsTypeSuffixOffTypeName() $resolvedOptions = array('a' => '2', 'b' => '3'); $resolvedType = $this->getMockResolvedType(); - $this->registry->expects($this->once()) + $this->registry->expects($this->any()) ->method('getType') ->with('Vendor\Name\Space\UserType') ->will($this->returnValue($resolvedType)); @@ -432,7 +471,7 @@ public function testCreateDoesNotStripTypeSuffixIfResultEmpty() $resolvedOptions = array('a' => '2', 'b' => '3'); $resolvedType = $this->getMockResolvedType(); - $this->registry->expects($this->once()) + $this->registry->expects($this->any()) ->method('getType') ->with('Vendor\Name\Space\Type') ->will($this->returnValue($resolvedType)); @@ -463,7 +502,7 @@ public function testCreateConvertsTypeToUnderscoreSyntax() $resolvedOptions = array('a' => '2', 'b' => '3'); $resolvedType = $this->getMockResolvedType(); - $this->registry->expects($this->once()) + $this->registry->expects($this->any()) ->method('getType') ->with('Vendor\Name\Space\MyProfileHTMLType') ->will($this->returnValue($resolvedType)); From fd8e8829020cd86b40b1ace92ad3e67961c220a5 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 30 Nov 2015 16:00:22 +0100 Subject: [PATCH 105/107] [Form] Cleanup --- src/Symfony/Component/Form/FormFactory.php | 47 ++++++------------- .../Component/Form/Tests/FormFactoryTest.php | 18 +++++++ 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/Symfony/Component/Form/FormFactory.php b/src/Symfony/Component/Form/FormFactory.php index 5e92baa64f925..f7160d550742d 100644 --- a/src/Symfony/Component/Form/FormFactory.php +++ b/src/Symfony/Component/Form/FormFactory.php @@ -62,48 +62,29 @@ public function createForProperty($class, $property, $data = null, array $option public function createBuilder($type = 'Symfony\Component\Form\Extension\Core\Type\FormType', $data = null, array $options = array()) { $name = null; - $typeName = null; if ($type instanceof ResolvedFormTypeInterface) { - if (method_exists($type, 'getBlockPrefix')) { - // As of Symfony 3.0, the block prefix of the type is used as - // default name - $name = $type->getBlockPrefix(); - } else { - // BC - $typeName = $type->getName(); - } + $typeObject = $type; } elseif ($type instanceof FormTypeInterface) { - if (method_exists($type, 'getBlockPrefix')) { - // As of Symfony 3.0, the block prefix of the type is used as - // default name - $name = $type->getBlockPrefix(); - } else { - // BC - $typeName = $type->getName(); - } + $typeObject = $type; } elseif (is_string($type)) { - $resolvedType = $this->registry->getType($type); - if (method_exists($resolvedType, 'getBlockPrefix')) { - // As of Symfony 3.0, the block prefix of the type is used as - // default name - $name = $resolvedType->getBlockPrefix(); - } else { - // BC - $typeName = $type; - } + $typeObject = $this->registry->getType($type); + $name = $type; } else { throw new UnexpectedTypeException($type, 'string, Symfony\Component\Form\ResolvedFormTypeInterface or Symfony\Component\Form\FormTypeInterface'); } - // BC when there is no block prefix - if (null === $name) { - if (false === strpos($typeName, '\\')) { - // No FQCN - leave unchanged for BC - $name = $typeName; - } else { + if (method_exists($typeObject, 'getBlockPrefix')) { + // As of Symfony 3.0, the block prefix of the type is used as default name + $name = $typeObject->getBlockPrefix(); + } else { + // BC when there is no block prefix + if (null === $name) { + $name = $typeObject->getName(); + } + if (false !== strpos($name, '\\')) { // FQCN - $name = StringUtil::fqcnToBlockPrefix($typeName); + $name = StringUtil::fqcnToBlockPrefix($name); } } diff --git a/src/Symfony/Component/Form/Tests/FormFactoryTest.php b/src/Symfony/Component/Form/Tests/FormFactoryTest.php index 0ec0b40590e78..32b6e73394c7c 100644 --- a/src/Symfony/Component/Form/Tests/FormFactoryTest.php +++ b/src/Symfony/Component/Form/Tests/FormFactoryTest.php @@ -341,6 +341,9 @@ public function testCreateUsesBlockPrefixIfTypeGivenAsString() $this->assertSame('FORM', $this->factory->create('TYPE', null, $options)); } + /** + * @group legacy + */ public function testCreateUsesTypeNameIfTypeGivenAsString() { $options = array('a' => '1', 'b' => '2'); @@ -372,6 +375,9 @@ public function testCreateUsesTypeNameIfTypeGivenAsString() $this->assertSame('FORM', $this->factory->create('TYPE', null, $options)); } + /** + * @group legacy + */ public function testCreateStripsNamespaceOffTypeName() { $options = array('a' => '1', 'b' => '2'); @@ -403,6 +409,9 @@ public function testCreateStripsNamespaceOffTypeName() $this->assertSame('FORM', $this->factory->create('Vendor\Name\Space\UserForm', null, $options)); } + /** + * @group legacy + */ public function testLegacyCreateStripsNamespaceOffTypeNameAccessByFQCN() { $options = array('a' => '1', 'b' => '2'); @@ -434,6 +443,9 @@ public function testLegacyCreateStripsNamespaceOffTypeNameAccessByFQCN() $this->assertSame('FORM', $this->factory->create('userform', null, $options)); } + /** + * @group legacy + */ public function testCreateStripsTypeSuffixOffTypeName() { $options = array('a' => '1', 'b' => '2'); @@ -465,6 +477,9 @@ public function testCreateStripsTypeSuffixOffTypeName() $this->assertSame('FORM', $this->factory->create('Vendor\Name\Space\UserType', null, $options)); } + /** + * @group legacy + */ public function testCreateDoesNotStripTypeSuffixIfResultEmpty() { $options = array('a' => '1', 'b' => '2'); @@ -496,6 +511,9 @@ public function testCreateDoesNotStripTypeSuffixIfResultEmpty() $this->assertSame('FORM', $this->factory->create('Vendor\Name\Space\Type', null, $options)); } + /** + * @group legacy + */ public function testCreateConvertsTypeToUnderscoreSyntax() { $options = array('a' => '1', 'b' => '2'); From 5be63e5ad03be56f74553b84392064097d469306 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 30 Nov 2015 18:25:03 +0100 Subject: [PATCH 106/107] updated CHANGELOG for 2.8.0 --- CHANGELOG-2.8.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/CHANGELOG-2.8.md b/CHANGELOG-2.8.md index f36da583f128a..34168508a3cb1 100644 --- a/CHANGELOG-2.8.md +++ b/CHANGELOG-2.8.md @@ -7,6 +7,57 @@ in 2.8 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.8.0...v2.8.1 +* 2.8.0 (2015-11-30) + + * bug #16758 Fix BC for the default root form name (stof) + * bug #16753 [Process] Fix signaling/stopping logic on Windows (nicolas-grekas) + * feature #16755 [Security] add subject variable to expression context (xabbuh) + * bug #16642 [DI][autowiring] throw exception when many services use the same class. (aitboudad) + * bug #16745 [Yaml] look for colon in parsed inline string (xabbuh) + * bug #16733 [Console] do not encode backslashes in console default description (Tobion) + * feature #16735 [WIP] [Ldap] Marked the Ldap component as internal (csarrazi) + * bug #16734 Make sure security.role_hierarchy.roles always exists (WouterJ) + * feature #16722 [Security][SecurityBundle] Use csrf_token_id instead of deprecated intention (jakzal) + * bug #16312 [HttpKernel] clearstatcache() so the Cache sees when a .lck file has been released (mpdude) + * bug #16351 [WIP] [Form] [TwigBridge] Bootstrap horizontal theme missing tests (pieter2627) + * bug #16685 [Form] Fixed: Duplicate choice labels are remembered when using "choices_as_values" = false (webmozart) + * feature #16709 [Bridge\PhpUnit] Display the stack trace of a deprecation on-demand (nicolas-grekas) + * bug #16704 [Form+SecurityBundle] Trigger deprecation for csrf_provider+intention options (nicolas-grekas) + * feature #16706 [HttpFoundation] Deprecate $deep parameter on ParameterBag (nicolas-grekas) + * bug #16705 [Form] Deprecated setting "choices_as_values" to "false" (webmozart) + * feature #16690 [Form] Deprecated ArrayKeyChoiceList (webmozart) + * feature #16687 [Form] Deprecated TimezoneType::getTimezones() (webmozart) + * bug #16681 [Form] Deprecated setting "choices_as_values" to "false" (webmozart) + * bug #16695 [SecurityBundle] disable the init:acl command if ACL is not used (Tobion) + * bug #16677 [Form] Fixed wrong usages of the "text" type (webmozart) + * bug #16679 [Form] Disabled view data validation if "data_class" is set to null (webmozart) + * bug #16621 [Console] Fix bug with $output overloading (WouterJ) + * feature #16601 [Security] Deprecate "AbstractVoter" in favor of "Voter" (nicolas-grekas, lyrixx) + * bug #16676 [HttpFoundation] Workaround HHVM rewriting HTTP response line (nicolas-grekas) + * bug #16668 [ClassLoader] Fix parsing namespace when token_get_all() is missing (nicolas-grekas) + * bug #16386 Bug #16343 [Router] Too many Routes ? (jelte) + * bug #16498 fix unused variable warning (eventhorizonpl) + * feature #16031 [Translation][Form] Do not translate form labels and placeholders when 'translation_domain' is false (Restless-ET) + * bug #16651 [Debug] Ensure class declarations are loaded only once (nicolas-grekas) + * security #16631 CVE-2015-8124: Session Fixation in the "Remember Me" Login Feature (xabbuh) + * security #16630 CVE-2015-8125: Potential Remote Timing Attack Vulnerability in Security Remember-Me Service (xabbuh) + * bug #16633 [Filesystem] Fixed failing test due to tempdir symlink (toretto460) + * bug #16609 [HttpKernel] Don't reset on shutdown but in FrameworkBundle/Test/KernelTestCase (nicolas-grekas) + * bug #16477 [Routing] Changing RouteCollectionBuilder::import() behavior to add to the builder (weaverryan) + * bug #16588 Sent out a status text for unknown HTTP headers. (dawehner) + * bug #16295 [DependencyInjection] Unescape parameters for all types of injection (Nicofuma) + * bug #16377 [WebProfilerBundle] Fix minitoolbar height (rvanlaak) + * bug #16585 Add support for HTTP status code 418 back (dawehner) + * bug #16574 [Process] Fix PhpProcess with phpdbg runtime (nicolas-grekas) + * bug #16581 Fix call to undefined function json_last_error_message (dawehner) + * bug #16573 [FrameworkBundle] Fix PropertyInfo extractor namespace in framework bundle (jvasseur) + * bug #16578 [Console] Fix bug in windows detection (kbond) + * bug #16546 [Serializer] ObjectNormalizer: don't serialize static methods and props (dunglas) + * bug #16352 Fix the server variables in the router_*.php files (leofeyer) + * bug #16537 [Validator] Allow an empty path with a non empty fragment or a query (jakzal) + * bug #16528 [Translation] Add support for Armenian pluralization. (marcosdsanchez) + * bug #16510 [Process] fix Proccess run with pts enabled (ewgRa) + * 2.8.0-BETA1 (2015-11-16) * feature #16156 [Filesystem] Changed dumpFile to allow dumping to streams (markchalloner, pierredup) From a62dbb8e7fd57409a7917b1bc7a891d24ebc7b23 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 30 Nov 2015 18:25:56 +0100 Subject: [PATCH 107/107] updated VERSION for 2.8.0 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 2b573fe860471..c53d4f43a962c 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,12 +59,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.8.0-DEV'; + const VERSION = '2.8.0'; const VERSION_ID = 20800; const MAJOR_VERSION = 2; const MINOR_VERSION = 8; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '05/2018'; const END_OF_LIFE = '05/2019';