diff --git a/.travis.yml b/.travis.yml index 8dec46a3d8d7c..5e3646df3fa82 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,5 +50,5 @@ install: script: - if [ "$deps" = "no" ]; then echo "$components" | parallel --gnu --keep-order 'echo -e "\\nRunning {} tests"; phpunit --exclude-group tty,benchmark,intl-data {} || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi; - if [ "$deps" = "no" ]; then echo -e "\\nRunning tests requiring tty"; phpunit --group tty || (echo -e "\\e[41mKO\\e[0m tty group" && $(exit 1)); fi; - - if [ "$deps" = "high" ]; then echo "$components" | parallel --gnu --keep-order -j10% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source update; phpunit --exclude-group tty,benchmark,intl-data || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi; + - if [ "$deps" = "high" ]; then echo "$components" | parallel --gnu --keep-order -j10% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source update; phpunit --exclude-group tty,benchmark,intl-data,legacy || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi; - if [ "$deps" = "low" ]; then echo "$components" | parallel --gnu --keep-order -j10% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source --prefer-lowest --prefer-stable update; phpunit --exclude-group tty,benchmark,intl-data || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi; diff --git a/UPGRADE-2.8.md b/UPGRADE-2.8.md new file mode 100644 index 0000000000000..966f1f4711132 --- /dev/null +++ b/UPGRADE-2.8.md @@ -0,0 +1,138 @@ +UPGRADE FROM 2.7 to 2.8 +======================= + +Form +---- + + * The "cascade_validation" option was deprecated. Use the "constraints" + option together with the `Valid` constraint instead. Contrary to + "cascade_validation", "constraints" must be set on the respective child forms, + not the parent form. + + Before: + + ```php + $form = $this->createForm('form', $article, array('cascade_validation' => true)) + ->add('author', new AuthorType()) + ->getForm(); + ``` + + After: + + ```php + use Symfony\Component\Validator\Constraints\Valid; + + $form = $this->createForm('form', $article) + ->add('author', new AuthorType(), array( + 'constraints' => new Valid(), + )) + ->getForm(); + ``` + + Alternatively, you can set the `Valid` constraint in the model itself: + + ```php + use Symfony\Component\Validator\Constraints as Assert; + + class Article + { + /** + * @Assert\Valid + */ + private $author; + } + ``` + +Translator +---------- + + * The `getMessages()` method of the `Symfony\Component\Translation\Translator` was deprecated and will be removed in + Symfony 3.0. You should use the `getCatalogue()` method of the `Symfony\Component\Translation\TranslatorBagInterface`. + + Before: + + ```php + $messages = $translator->getMessages(); + ``` + + After: + + ```php + $catalogue = $translator->getCatalogue($locale); + $messages = $catalogue->all(); + + while ($catalogue = $catalogue->getFallbackCatalogue()) { + $messages = array_replace_recursive($catalogue->all(), $messages); + } + ``` + +DependencyInjection +------------------- + + * The concept of scopes were deprecated, the deprecated methods are: + + - `Symfony\Component\DependencyInjection\ContainerBuilder::getScopes()` + - `Symfony\Component\DependencyInjection\ContainerBuilder::getScopeChildren()` + - `Symfony\Component\DependencyInjection\ContainerInterface::enterScope()` + - `Symfony\Component\DependencyInjection\ContainerInterface::leaveScope()` + - `Symfony\Component\DependencyInjection\ContainerInterface::addScope()` + - `Symfony\Component\DependencyInjection\ContainerInterface::hasScope()` + - `Symfony\Component\DependencyInjection\ContainerInterface::isScopeActive()` + - `Symfony\Component\DependencyInjection\Definition::setScope()` + - `Symfony\Component\DependencyInjection\Definition::getScope()` + - `Symfony\Component\DependencyInjection\Reference::isStrict()` + + Also, the `$scope` and `$strict` parameters of `Symfony\Component\DependencyInjection\ContainerInterface::set()` and `Symfony\Component\DependencyInjection\Reference` respectively were deprecated. + + * A new `shared` flag has been added to the service definition + in replacement of the `prototype` scope. + + Before: + + ```php + use Symfony\Component\DependencyInjection\ContainerBuilder; + + $container = new ContainerBuilder(); + $container + ->register('foo', 'stdClass') + ->setScope(ContainerBuilder::SCOPE_PROTOTYPE) + ; + ``` + + ```yml + services: + foo: + class: stdClass + scope: prototype + ``` + + ```xml + + + + ``` + + After: + + ```php + use Symfony\Component\DependencyInjection\ContainerBuilder; + + $container = new ContainerBuilder(); + $container + ->register('foo', 'stdClass') + ->setShared(false) + ; + ``` + + ```yml + services: + foo: + class: stdClass + shared: false + ``` + + ```xml + + + + ``` diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md index 6b6d6cad8d688..de9a230a55468 100644 --- a/UPGRADE-3.0.md +++ b/UPGRADE-3.0.md @@ -600,6 +600,86 @@ UPGRADE FROM 2.x to 3.0 * The `Resources/` directory was moved to `Core/Resources/` + * The `key` settings of `anonymous` and `remember_me` are renamed to `secret`. + + Before: + + ```yaml + security: + # ... + firewalls: + default: + # ... + anonymous: { key: "%secret%" } + remember_me: + key: "%secret%" + ``` + + ```xml + + + + + + + + + + + + ``` + + ```php + // ... + $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( + // ... + 'anonymous' => array('key' => '%secret%'), + 'remember_me' => array('key' => '%secret%'), + ), + )); + ``` + + After: + + ```yaml + security: + # ... + firewalls: + default: + # ... + anonymous: { secret: "%secret%" } + remember_me: + secret: "%secret%" + ``` + + ```xml + + + + + + + + + + + + ``` + + ```php + // ... + $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( + // ... + 'anonymous' => array('secret' => '%secret%'), + 'remember_me' => array('secret' => '%secret%'), + ), + )); + ``` + ### Translator * The `Translator::setFallbackLocale()` method has been removed in favor of diff --git a/composer.json b/composer.json index 115adbfc16f6a..1b7d12551747f 100644 --- a/composer.json +++ b/composer.json @@ -97,7 +97,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index ea8b88b101b62..b76704cba7154 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -20,16 +20,16 @@ "doctrine/common": "~2.3" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/stopwatch": "~2.2", - "symfony/dependency-injection": "~2.2", - "symfony/form": "~2.7,>=2.7.1", - "symfony/http-kernel": "~2.2", - "symfony/property-access": "~2.3", - "symfony/security": "~2.2", - "symfony/expression-language": "~2.2", - "symfony/validator": "~2.5,>=2.5.5", - "symfony/translation": "~2.0,>=2.0.5", + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/stopwatch": "~2.2|~3.0.0", + "symfony/dependency-injection": "~2.2|~3.0.0", + "symfony/form": "~2.7,>=2.7.1|~3.0.0", + "symfony/http-kernel": "~2.2|~3.0.0", + "symfony/property-access": "~2.3|~3.0.0", + "symfony/security": "~2.2|~3.0.0", + "symfony/expression-language": "~2.2|~3.0.0", + "symfony/validator": "~2.5,>=2.5.5|~3.0.0", + "symfony/translation": "~2.0,>=2.0.5|~3.0.0", "doctrine/data-fixtures": "1.0.*", "doctrine/dbal": "~2.2", "doctrine/orm": "~2.2,>=2.2.3" @@ -47,7 +47,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Bridge/Monolog/Handler/DebugHandler.php b/src/Symfony/Bridge/Monolog/Handler/DebugHandler.php index c959a5a94fcb7..f1046c96a6ad1 100644 --- a/src/Symfony/Bridge/Monolog/Handler/DebugHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/DebugHandler.php @@ -35,6 +35,7 @@ public function getLogs() 'priority' => $record['level'], 'priorityName' => $record['level_name'], 'context' => $record['context'], + 'channel' => isset($record['channel']) ? $record['channel'] : '', ); } diff --git a/src/Symfony/Bridge/Monolog/composer.json b/src/Symfony/Bridge/Monolog/composer.json index a3a4e374130e2..61b6ff02d3518 100644 --- a/src/Symfony/Bridge/Monolog/composer.json +++ b/src/Symfony/Bridge/Monolog/composer.json @@ -20,10 +20,10 @@ "monolog/monolog": "~1.11" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/http-kernel": "~2.4", - "symfony/console": "~2.4", - "symfony/event-dispatcher": "~2.2" + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/http-kernel": "~2.4|~3.0.0", + "symfony/console": "~2.4|~3.0.0", + "symfony/event-dispatcher": "~2.2|~3.0.0" }, "suggest": { "symfony/http-kernel": "For using the debugging handlers together with the response life cycle of the HTTP kernel.", @@ -36,7 +36,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Bridge/PhpUnit/composer.json b/src/Symfony/Bridge/PhpUnit/composer.json index 8412a277bae98..90ac8e5d9dd19 100644 --- a/src/Symfony/Bridge/PhpUnit/composer.json +++ b/src/Symfony/Bridge/PhpUnit/composer.json @@ -28,7 +28,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php index bba5055d0085d..076484567aae6 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php @@ -68,9 +68,9 @@ public function getProxyFactoryCode(Definition $definition, $id) { $instantiation = 'return'; - if (ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) { + if ($definition->isShared() && ContainerInterface::SCOPE_CONTAINER === $definition->getScope(false)) { $instantiation .= " \$this->services['$id'] ="; - } elseif (ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { + } elseif ($definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope(false)) { $instantiation .= " \$this->services['$id'] = \$this->scopedServices['$scope']['$id'] ="; } diff --git a/src/Symfony/Bridge/ProxyManager/composer.json b/src/Symfony/Bridge/ProxyManager/composer.json index 24a8edea01b45..b37bf7b875759 100644 --- a/src/Symfony/Bridge/ProxyManager/composer.json +++ b/src/Symfony/Bridge/ProxyManager/composer.json @@ -17,12 +17,12 @@ ], "require": { "php": ">=5.3.9", - "symfony/dependency-injection": "~2.3", + "symfony/dependency-injection": "~2.8|~3.0.0", "ocramius/proxy-manager": "~0.4|~1.0" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/config": "~2.3" + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/config": "~2.3|~3.0.0" }, "autoload": { "psr-4": { "Symfony\\Bridge\\ProxyManager\\": "" } @@ -30,7 +30,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Bridge/Swiftmailer/composer.json b/src/Symfony/Bridge/Swiftmailer/composer.json index 6d3fa5057cd9d..3e5fcc3bf837a 100644 --- a/src/Symfony/Bridge/Swiftmailer/composer.json +++ b/src/Symfony/Bridge/Swiftmailer/composer.json @@ -20,7 +20,7 @@ "swiftmailer/swiftmailer": ">=4.2.0,<6.0-dev" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7" + "symfony/phpunit-bridge": "~2.7|~3.0.0" }, "suggest": { "symfony/http-kernel": "" @@ -31,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } 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 a0828b38003fd..11182cf97d660 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 @@ -176,6 +176,11 @@ {{ block('form_widget_simple') }} {%- endblock email_widget -%} +{%- block range_widget -%} + {% set type = type|default('range') %} + {{- block('form_widget_simple') -}} +{%- endblock range_widget %} + {%- block button_widget -%} {%- if label is empty -%} {%- if label_format is not empty -%} @@ -314,7 +319,6 @@ {%- block widget_attributes -%} id="{{ id }}" name="{{ full_name }}" - {%- if read_only and attr.readonly is not defined %} readonly="readonly"{% endif -%} {%- if disabled %} disabled="disabled"{% endif -%} {%- if required %} required="required"{% endif -%} {%- for attrname, attrvalue in attr -%} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php index a9d161b2b909a..ef7f91bbe9e1f 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php @@ -29,6 +29,7 @@ class FormExtensionBootstrap3LayoutTest extends AbstractBootstrap3LayoutTest protected $testableFeatures = array( 'choice_attr', + 'choice_label_attr', ); protected function setUp() @@ -115,14 +116,4 @@ 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 - } } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php index 1bce43b83780c..d0696d7c2ccb5 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php @@ -30,6 +30,7 @@ class FormExtensionDivLayoutTest extends AbstractDivLayoutTest protected $testableFeatures = array( 'choice_attr', + 'choice_label_attr', ); protected function setUp() @@ -208,14 +209,4 @@ public static function themeInheritanceProvider() array(array('parent_label.html.twig'), array('child_label.html.twig')), ); } - - public function testRange() - { - // No-op for forward compatibility with AbstractLayoutTest 2.8 - } - - public function testRangeWithMinMaxValues() - { - // 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..fd5c1e8b1c614 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php @@ -29,6 +29,7 @@ class FormExtensionTableLayoutTest extends AbstractTableLayoutTest protected $testableFeatures = array( 'choice_attr', + 'choice_label_attr', ); protected function setUp() @@ -116,14 +117,4 @@ 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 - } } diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 1ad00a67b7ce8..605901bc8a5ae 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -20,21 +20,21 @@ "twig/twig": "~1.18" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/asset": "~2.7", - "symfony/finder": "~2.3", - "symfony/form": "~2.7,>=2.7.2", - "symfony/http-kernel": "~2.3", - "symfony/intl": "~2.3", - "symfony/routing": "~2.2", - "symfony/templating": "~2.1", - "symfony/translation": "~2.7", - "symfony/yaml": "~2.0,>=2.0.5", - "symfony/security": "~2.6", - "symfony/stopwatch": "~2.2", - "symfony/console": "~2.7", - "symfony/var-dumper": "~2.6", - "symfony/expression-language": "~2.4" + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/asset": "~2.7|~3.0.0", + "symfony/finder": "~2.3|~3.0.0", + "symfony/form": "~2.8", + "symfony/http-kernel": "~2.3|~3.0.0", + "symfony/intl": "~2.3|~3.0.0", + "symfony/routing": "~2.2|~3.0.0", + "symfony/templating": "~2.1|~3.0.0", + "symfony/translation": "~2.7|~3.0.0", + "symfony/yaml": "~2.0,>=2.0.5|~3.0.0", + "symfony/security": "~2.6|~3.0.0", + "symfony/stopwatch": "~2.2|~3.0.0", + "symfony/console": "~2.7|~3.0.0", + "symfony/var-dumper": "~2.6|~3.0.0", + "symfony/expression-language": "~2.4|~3.0.0" }, "suggest": { "symfony/finder": "", @@ -56,7 +56,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Bundle/DebugBundle/composer.json b/src/Symfony/Bundle/DebugBundle/composer.json index e7ccc9a4b11f1..66d77643479dd 100644 --- a/src/Symfony/Bundle/DebugBundle/composer.json +++ b/src/Symfony/Bundle/DebugBundle/composer.json @@ -17,15 +17,15 @@ ], "require": { "php": ">=5.3.9", - "symfony/http-kernel": "~2.6", - "symfony/twig-bridge": "~2.6", - "symfony/var-dumper": "~2.6" + "symfony/http-kernel": "~2.6|~3.0.0", + "symfony/twig-bridge": "~2.6|~3.0.0", + "symfony/var-dumper": "~2.6|~3.0.0" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/config": "~2.3", - "symfony/dependency-injection": "~2.3", - "symfony/web-profiler-bundle": "~2.3" + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/config": "~2.3|~3.0.0", + "symfony/dependency-injection": "~2.3|~3.0.0", + "symfony/web-profiler-bundle": "~2.3|~3.0.0" }, "suggest": { "symfony/config": "For service container configuration", @@ -37,7 +37,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php index 6af52feaaec57..44ebdeb36e7d4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php @@ -44,7 +44,8 @@ protected function configure() { $this ->setDefinition(array( - new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1:8000'), + new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1'), + new InputOption('port', 'p', InputOption::VALUE_REQUIRED, 'Address port number', '8000'), new InputOption('docroot', 'd', InputOption::VALUE_REQUIRED, 'Document root', null), new InputOption('router', 'r', InputOption::VALUE_REQUIRED, 'Path to custom router script'), )) @@ -99,9 +100,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $address = $input->getArgument('address'); if (false === strpos($address, ':')) { - $output->writeln('The address has to be of the form bind-address:port.'); - - return 1; + $address = $address.':'.$input->getOption('port'); } if ($this->isOtherServerProcessRunning($address)) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php index 04906317fa944..6300e9774a25d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php @@ -33,9 +33,11 @@ protected function configure() { $this ->setDefinition(array( - new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1:8000'), + new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1'), + new InputOption('port', 'p', InputOption::VALUE_REQUIRED, 'Address port number', '8000'), new InputOption('docroot', 'd', InputOption::VALUE_REQUIRED, 'Document root', null), new InputOption('router', 'r', InputOption::VALUE_REQUIRED, 'Path to custom router script'), + new InputOption('force', 'f', InputOption::VALUE_NONE, 'Force web server startup'), )) ->setName('server:start') ->setDescription('Starts PHP built-in web server in the background') @@ -106,13 +108,12 @@ protected function execute(InputInterface $input, OutputInterface $output) $address = $input->getArgument('address'); if (false === strpos($address, ':')) { - $output->writeln('The address has to be of the form bind-address:port.'); - - return 1; + $address = $address.':'.$input->getOption('port'); } - if ($this->isOtherServerProcessRunning($address)) { + if (!$input->getOption('force') && $this->isOtherServerProcessRunning($address)) { $output->writeln(sprintf('A process is already listening on http://%s.', $address)); + $output->writeln(sprintf('Use the --force option if the server process terminated unexpectedly to start a new web server process.')); return 1; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStopCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStopCommand.php index 9b0656c220b66..c40952059a294 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStopCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStopCommand.php @@ -14,6 +14,7 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; /** * Stops a background process running PHP's built-in web server. @@ -29,7 +30,8 @@ protected function configure() { $this ->setDefinition(array( - new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1:8000'), + new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1'), + new InputOption('port', 'p', InputOption::VALUE_REQUIRED, 'Address port number', '8000'), )) ->setName('server:stop') ->setDescription('Stops PHP\'s built-in web server that was started with the server:start command') @@ -53,6 +55,10 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $address = $input->getArgument('address'); + if (false === strpos($address, ':')) { + $address = $address.':'.$input->getOption('port'); + } + $lockFile = $this->getLockFile($address); if (!file_exists($lockFile)) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php index 30d44493d4c4b..c6be5ba47554f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php @@ -11,12 +11,15 @@ namespace Symfony\Bundle\FrameworkBundle\Command; +use Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader; +use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\Translation\Catalogue\MergeOperation; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Translation\Catalogue\MergeOperation; use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Translator; @@ -48,6 +51,7 @@ protected function configure() new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'The messages domain'), new InputOption('only-missing', null, InputOption::VALUE_NONE, 'Displays only missing messages'), new InputOption('only-unused', null, InputOption::VALUE_NONE, 'Displays only unused messages'), + new InputOption('all', null, InputOption::VALUE_NONE, 'Load messages from all registered bundles'), )) ->setDescription('Displays translation messages information') ->setHelp(<<php %command.full_name% en +You can display information about translations in all registered bundles in a specific locale: + + php %command.full_name% --all en + EOF ) ; @@ -92,7 +100,9 @@ protected function execute(InputInterface $input, OutputInterface $output) $locale = $input->getArgument('locale'); $domain = $input->getOption('domain'); + /** @var TranslationLoader $loader */ $loader = $this->getContainer()->get('translation.loader'); + /** @var Kernel $kernel */ $kernel = $this->getContainer()->get('kernel'); // Define Root Path to App folder @@ -109,30 +119,23 @@ protected function execute(InputInterface $input, OutputInterface $output) } catch (\InvalidArgumentException $e) { // such a bundle does not exist, so treat the argument as path $transPaths = array($input->getArgument('bundle').'/Resources/'); + if (!is_dir($transPaths[0])) { throw new \InvalidArgumentException(sprintf('"%s" is neither an enabled bundle nor a directory.', $transPaths[0])); } } + } elseif ($input->getOption('all')) { + foreach ($kernel->getBundles() as $bundle) { + $transPaths[] = $bundle->getPath().'/Resources/'; + $transPaths[] = sprintf('%s/Resources/%s/', $kernel->getRootDir(), $bundle->getName()); + } } // Extract used messages - $extractedCatalogue = new MessageCatalogue($locale); - foreach ($transPaths as $path) { - $path .= 'views'; - - if (is_dir($path)) { - $this->getContainer()->get('translation.extractor')->extract($path, $extractedCatalogue); - } - } + $extractedCatalogue = $this->extractMessages($locale, $transPaths); // Load defined messages - $currentCatalogue = new MessageCatalogue($locale); - foreach ($transPaths as $path) { - $path .= 'translations'; - if (is_dir($path)) { - $loader->loadMessages($path, $currentCatalogue); - } - } + $currentCatalogue = $this->loadCurrentMessages($locale, $transPaths, $loader); // Merge defined and extracted messages to get all message ids $mergeOperation = new MergeOperation($extractedCatalogue, $currentCatalogue); @@ -155,24 +158,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } // Load the fallback catalogues - $fallbackCatalogues = array(); - $translator = $this->getContainer()->get('translator'); - if ($translator instanceof Translator) { - foreach ($translator->getFallbackLocales() as $fallbackLocale) { - if ($fallbackLocale === $locale) { - continue; - } - - $fallbackCatalogue = new MessageCatalogue($fallbackLocale); - foreach ($transPaths as $path) { - $path = $path.'translations'; - if (is_dir($path)) { - $loader->loadMessages($path, $fallbackCatalogue); - } - } - $fallbackCatalogues[] = $fallbackCatalogue; - } - } + $fallbackCatalogues = $this->loadFallbackCatalogues($locale, $transPaths, $loader); // Display header line $headers = array('State', 'Domain', 'Id', sprintf('Message Preview (%s)', $locale)); @@ -265,4 +251,74 @@ private function sanitizeString($string, $length = 40) return $string; } + + /** + * @param string $locale + * @param array $transPaths + * + * @return MessageCatalogue + */ + private function extractMessages($locale, $transPaths) + { + $extractedCatalogue = new MessageCatalogue($locale); + foreach ($transPaths as $path) { + $path = $path.'views'; + if (is_dir($path)) { + $this->getContainer()->get('translation.extractor')->extract($path, $extractedCatalogue); + } + } + + return $extractedCatalogue; + } + + /** + * @param string $locale + * @param array $transPaths + * @param TranslationLoader $loader + * + * @return MessageCatalogue + */ + private function loadCurrentMessages($locale, $transPaths, TranslationLoader $loader) + { + $currentCatalogue = new MessageCatalogue($locale); + foreach ($transPaths as $path) { + $path = $path.'translations'; + if (is_dir($path)) { + $loader->loadMessages($path, $currentCatalogue); + } + } + + return $currentCatalogue; + } + + /** + * @param string $locale + * @param array $transPaths + * @param TranslationLoader $loader + * + * @return MessageCatalogue[] + */ + private function loadFallbackCatalogues($locale, $transPaths, TranslationLoader $loader) + { + $fallbackCatalogues = array(); + $translator = $this->getContainer()->get('translator'); + if ($translator instanceof Translator) { + foreach ($translator->getFallbackLocales() as $fallbackLocale) { + if ($fallbackLocale === $locale) { + continue; + } + + $fallbackCatalogue = new MessageCatalogue($fallbackLocale); + foreach ($transPaths as $path) { + $path = $path.'translations'; + if (is_dir($path)) { + $loader->loadMessages($path, $fallbackCatalogue); + } + } + $fallbackCatalogues[] = $fallbackCatalogue; + } + } + + return $fallbackCatalogues; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php index f61af1cc959ce..57780d2e52fd3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php @@ -213,12 +213,16 @@ private function getContainerDefinitionData(Definition $definition, $omitTags = { $data = array( 'class' => (string) $definition->getClass(), - 'scope' => $definition->getScope(), + 'scope' => $definition->getScope(false), 'public' => $definition->isPublic(), 'synthetic' => $definition->isSynthetic(), 'lazy' => $definition->isLazy(), ); + if (method_exists($definition, 'isShared')) { + $data['shared'] = $definition->isShared(); + } + if (method_exists($definition, 'isSynchronized')) { $data['synchronized'] = $definition->isSynchronized(false); } @@ -290,17 +294,27 @@ private function getEventDispatcherListenersData(EventDispatcherInterface $event { $data = array(); - $registeredListeners = $eventDispatcher->getListeners($event); + $registeredListeners = $eventDispatcher->getListeners($event, true); if (null !== $event) { - foreach ($registeredListeners as $listener) { - $data[] = $this->getCallableData($listener); + krsort($registeredListeners); + foreach ($registeredListeners as $priority => $listeners) { + foreach ($listeners as $listener) { + $listener = $this->getCallableData($listener); + $listener['priority'] = $priority; + $data[] = $listener; + } } } else { ksort($registeredListeners); foreach ($registeredListeners as $eventListened => $eventListeners) { - foreach ($eventListeners as $eventListener) { - $data[$eventListened][] = $this->getCallableData($eventListener); + krsort($eventListeners); + foreach ($eventListeners as $priority => $listeners) { + foreach ($listeners as $listener) { + $listener = $this->getCallableData($listener); + $listener['priority'] = $priority; + $data[$eventListened][] = $listener; + } } } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php index 84877461c832a..8f5437146bb57 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php @@ -113,7 +113,7 @@ protected function describeContainerService($service, array $options = array()) } elseif ($service instanceof Definition) { $this->describeContainerDefinition($service, $childOptions); } else { - $this->write(sprintf("**`%s`:** `%s`", $options['id'], get_class($service))); + $this->write(sprintf('**`%s`:** `%s`', $options['id'], get_class($service))); } } @@ -179,12 +179,16 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o protected function describeContainerDefinition(Definition $definition, array $options = array()) { $output = '- Class: `'.$definition->getClass().'`' - ."\n".'- Scope: `'.$definition->getScope().'`' + ."\n".'- Scope: `'.$definition->getScope(false).'`' ."\n".'- Public: '.($definition->isPublic() ? 'yes' : 'no') ."\n".'- Synthetic: '.($definition->isSynthetic() ? 'yes' : 'no') ."\n".'- Lazy: '.($definition->isLazy() ? 'yes' : 'no') ; + if (method_exists($definition, 'isShared')) { + $output .= "\n".'- Shared: '.($definition->isShared() ? 'yes' : 'no'); + } + if (method_exists($definition, 'isSynchronized')) { $output .= "\n".'- Synchronized: '.($definition->isSynchronized(false) ? 'yes' : 'no'); } @@ -269,21 +273,30 @@ protected function describeEventDispatcherListeners(EventDispatcherInterface $ev $this->write(sprintf('# %s', $title)."\n"); - $registeredListeners = $eventDispatcher->getListeners($event); + $registeredListeners = $eventDispatcher->getListeners($event, true); if (null !== $event) { - foreach ($registeredListeners as $order => $listener) { - $this->write("\n".sprintf('## Listener %d', $order + 1)."\n"); - $this->describeCallable($listener); + krsort($registeredListeners); + $order = 1; + foreach ($registeredListeners as $priority => $listeners) { + foreach ($listeners as $listener) { + $this->write("\n".sprintf('## Listener %d', $order++)."\n"); + $this->describeCallable($listener); + $this->write(sprintf('- Priority: `%d`', $priority)."\n"); + } } } else { ksort($registeredListeners); foreach ($registeredListeners as $eventListened => $eventListeners) { $this->write("\n".sprintf('## %s', $eventListened)."\n"); - - foreach ($eventListeners as $order => $eventListener) { - $this->write("\n".sprintf('### Listener %d', $order + 1)."\n"); - $this->describeCallable($eventListener); + krsort($eventListeners); + $order = 1; + foreach ($eventListeners as $priority => $listeners) { + foreach ($listeners as $listener) { + $this->write("\n".sprintf('### Listener %d', $order++)."\n"); + $this->describeCallable($listener); + $this->write(sprintf('- Priority: `%d`', $priority)."\n"); + } } } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index 22d0bde4d42e7..422e50ead2b83 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -261,10 +261,13 @@ protected function describeContainerDefinition(Definition $definition, array $op $description[] = 'Tags -'; } - $description[] = sprintf('Scope %s', $definition->getScope()); + $description[] = sprintf('Scope %s', $definition->getScope(false)); $description[] = sprintf('Public %s', $definition->isPublic() ? 'yes' : 'no'); $description[] = sprintf('Synthetic %s', $definition->isSynthetic() ? 'yes' : 'no'); $description[] = sprintf('Lazy %s', $definition->isLazy() ? 'yes' : 'no'); + if (method_exists($definition, 'isShared')) { + $description[] = sprintf('Shared %s', $definition->isShared() ? 'yes' : 'no'); + } if (method_exists($definition, 'isSynchronized')) { $description[] = sprintf('Synchronized %s', $definition->isSynchronized(false) ? 'yes' : 'no'); } @@ -336,33 +339,16 @@ protected function describeEventDispatcherListeners(EventDispatcherInterface $ev $this->writeText($this->formatSection('event_dispatcher', $label)."\n", $options); - $registeredListeners = $eventDispatcher->getListeners($event); + $registeredListeners = $eventDispatcher->getListeners($event, true); if (null !== $event) { $this->writeText("\n"); - $table = new Table($this->getOutput()); - $table->getStyle()->setCellHeaderFormat('%s'); - $table->setHeaders(array('Order', 'Callable')); - - foreach ($registeredListeners as $order => $listener) { - $table->addRow(array(sprintf('#%d', $order + 1), $this->formatCallable($listener))); - } - - $table->render(); + $this->renderEventListenerTable($registeredListeners); } else { ksort($registeredListeners); foreach ($registeredListeners as $eventListened => $eventListeners) { $this->writeText(sprintf("\n[Event] %s\n", $eventListened), $options); - - $table = new Table($this->getOutput()); - $table->getStyle()->setCellHeaderFormat('%s'); - $table->setHeaders(array('Order', 'Callable')); - - foreach ($eventListeners as $order => $eventListener) { - $table->addRow(array(sprintf('#%d', $order + 1), $this->formatCallable($eventListener))); - } - - $table->render(); + $this->renderEventListenerTable($eventListeners); } } } @@ -375,6 +361,26 @@ protected function describeCallable($callable, array $options = array()) $this->writeText($this->formatCallable($callable), $options); } + /** + * @param array $array + */ + private function renderEventListenerTable(array $eventListeners) + { + $table = new Table($this->getOutput()); + $table->getStyle()->setCellHeaderFormat('%s'); + $table->setHeaders(array('Order', 'Callable', 'Priority')); + + krsort($eventListeners); + $order = 1; + foreach ($eventListeners as $priority => $listeners) { + foreach ($listeners as $listener) { + $table->addRow(array(sprintf('#%d', $order++), $this->formatCallable($listener), $priority)); + } + } + + $table->render(); + } + /** * @param array $array * diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php index c37a9009fcff5..c9e6f7eb731a3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php @@ -362,10 +362,13 @@ private function getContainerDefinitionDocument(Definition $definition, $id = nu } } - $serviceXML->setAttribute('scope', $definition->getScope()); + $serviceXML->setAttribute('scope', $definition->getScope(false)); $serviceXML->setAttribute('public', $definition->isPublic() ? 'true' : 'false'); $serviceXML->setAttribute('synthetic', $definition->isSynthetic() ? 'true' : 'false'); $serviceXML->setAttribute('lazy', $definition->isLazy() ? 'true' : 'false'); + if (method_exists($definition, 'isShared')) { + $serviceXML->setAttribute('shared', $definition->isShared() ? 'true' : 'false'); + } if (method_exists($definition, 'isSynchronized')) { $serviceXML->setAttribute('synchronized', $definition->isSynchronized(false) ? 'true' : 'false'); } @@ -446,13 +449,9 @@ private function getEventDispatcherListenersDocument(EventDispatcherInterface $e $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($eventDispatcherXML = $dom->createElement('event-dispatcher')); - $registeredListeners = $eventDispatcher->getListeners($event); + $registeredListeners = $eventDispatcher->getListeners($event, true); if (null !== $event) { - foreach ($registeredListeners as $listener) { - $callableXML = $this->getCallableDocument($listener); - - $eventDispatcherXML->appendChild($eventDispatcherXML->ownerDocument->importNode($callableXML->childNodes->item(0), true)); - } + $this->appendEventListenerDocument($eventDispatcherXML, $registeredListeners); } else { ksort($registeredListeners); @@ -460,17 +459,30 @@ private function getEventDispatcherListenersDocument(EventDispatcherInterface $e $eventDispatcherXML->appendChild($eventXML = $dom->createElement('event')); $eventXML->setAttribute('name', $eventListened); - foreach ($eventListeners as $eventListener) { - $callableXML = $this->getCallableDocument($eventListener); - - $eventXML->appendChild($eventXML->ownerDocument->importNode($callableXML->childNodes->item(0), true)); - } + $this->appendEventListenerDocument($eventXML, $eventListeners); } } return $dom; } + /** + * @param DOMElement $element + * @param array $eventListeners + */ + private function appendEventListenerDocument(\DOMElement $element, array $eventListeners) + { + krsort($eventListeners); + foreach ($eventListeners as $priority => $listeners) { + foreach ($listeners as $listener) { + $callableXML = $this->getCallableDocument($listener); + $callableXML->childNodes->item(0)->setAttribute('priority', $priority); + + $element->appendChild($element->ownerDocument->importNode($callableXML->childNodes->item(0), true)); + } + } + } + /** * @param callable $callable * diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 655e95cf93061..977c0669c409c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -575,6 +575,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode) ->info('translator configuration') ->canBeEnabled() ->fixXmlConfig('fallback') + ->fixXmlConfig('path') ->children() ->arrayNode('fallbacks') ->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end() @@ -582,6 +583,9 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode) ->defaultValue(array('en')) ->end() ->booleanNode('logging')->defaultValue($this->debug)->end() + ->arrayNode('paths') + ->prototype('scalar')->end() + ->end() ->end() ->end() ->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 2183004d85315..08c7f1c54be7c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -692,6 +692,13 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder $dirs[] = $dir; } } + foreach ($config['paths'] as $dir) { + if (is_dir($dir)) { + $dirs[] = $dir; + } else { + throw new \UnexpectedValueException(sprintf('%s defined in translator.paths does not exist or is not a directory', $dir)); + } + } if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/translations')) { $dirs[] = $dir; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml index 7d1a588fbad34..10e8f807899fb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml @@ -115,6 +115,9 @@ + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml index 6b2f7c9688699..78a98177287db 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml @@ -45,6 +45,11 @@ + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index fa7aa2b2bd808..e1ca041e6114f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -183,6 +183,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml index 4e609a06e5d95..428ba0c8ee48a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml @@ -13,16 +13,16 @@ - + %test.client.parameters% - + - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml index ccfd44e5ca483..c0bf73a94e981 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml @@ -39,6 +39,16 @@ %validator.mapping.cache.prefix% + + + + + %validator.mapping.cache.prefix% + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/range_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/range_widget.html.php new file mode 100644 index 0000000000000..4c628f8e005bc --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/range_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'range')); 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 14e65a7991c53..ac5a481d0b55e 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 @@ -1,12 +1,11 @@ -id="escape($id) ?>" name="escape($full_name) ?>" readonly="readonly" -disabled="disabled" -required="required" +id="escape($id) ?>" name="escape($full_name) ?>" disabled="disabled" + required="required" $v): ?> -escape($k), $view->escape($view['translator']->trans($v, array(), $translation_domain))) ?> +escape($k), $view->escape($view['translator']->trans($v, array(), $translation_domain))) ?> -escape($k), $view->escape($k)) ?> +escape($k), $view->escape($k)) ?> -escape($k), $view->escape($v)) ?> +escape($k), $view->escape($v)) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php index 8a9800bc865bd..38dda10ba7d7c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php @@ -75,6 +75,9 @@ public function testLegacyDescribeSynchronizedServiceDefinition(Definition $defi $this->assertDescription($expectedDescription, $definition); } + /** + * @group legacy + */ public function provideLegacySynchronizedServiceDefinitionTestData() { return $this->getDescriptionTestData(ObjectsProvider::getLegacyContainerDefinitions()); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php index 94db08b5bdc91..810038909d90c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php @@ -114,6 +114,7 @@ public static function getContainerDefinitions() /** * @deprecated since version 2.7, to be removed in 3.0 + * * @internal */ public static function getLegacyContainerDefinitions() @@ -157,8 +158,8 @@ public static function getEventDispatchers() { $eventDispatcher = new EventDispatcher(); - $eventDispatcher->addListener('event1', 'global_function'); - $eventDispatcher->addListener('event1', function () { return 'Closure'; }); + $eventDispatcher->addListener('event1', 'global_function', 255); + $eventDispatcher->addListener('event1', function () { return 'Closure'; }, -1); $eventDispatcher->addListener('event2', new CallableClass()); return array('event_dispatcher_1' => $eventDispatcher); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index 47482433a9742..621ea4bdeec6c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -144,6 +144,7 @@ protected static function getBundleDefaultConfig() 'enabled' => false, 'fallbacks' => array('en'), 'logging' => true, + 'paths' => array(), ), 'validation' => array( 'enabled' => false, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php index 5022aeaf9f207..a035b56d70029 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php @@ -50,6 +50,7 @@ 'translator' => array( 'enabled' => true, 'fallback' => 'fr', + 'paths' => array('%kernel.root_dir%/Fixtures/translations'), ), 'validation' => array( 'enabled' => true, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/translations/test_paths.en.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/translations/test_paths.en.yml new file mode 100644 index 0000000000000..d4e682c47c9ca --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/translations/test_paths.en.yml @@ -0,0 +1,2 @@ +custom: + paths: test diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml index 5b16a59796091..bf4537b910e8b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -34,7 +34,9 @@ theme2 - + + %kernel.root_dir%/Fixtures/translations + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml index be1b41e25f894..47513b1f665b5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -39,6 +39,7 @@ framework: translator: enabled: true fallback: fr + paths: ['%kernel.root_dir%/Fixtures/translations'] validation: enabled: true cache: apc diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 213f0927077c7..edace5bf7a9ad 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -242,6 +242,11 @@ public function testTranslator() $files, '->registerTranslatorConfiguration() finds Security translation resources' ); + $this->assertContains( + strtr(__DIR__.'/Fixtures/translations/test_paths.en.yml', '/', DIRECTORY_SEPARATOR), + $files, + '->registerTranslatorConfiguration() finds translation resources in custom paths' + ); $calls = $container->getDefinition('translator.default')->getMethodCalls(); $this->assertEquals(array('fr'), $calls[0][1][0]); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.json index 047f4e8c16a48..9be35dad0705e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.json @@ -6,6 +6,7 @@ "public": true, "synthetic": false, "lazy": true, + "shared": true, "synchronized": false, "abstract": true, "file": null, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.md index 1c3b958bd92ca..de404d24d0f59 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.md @@ -12,6 +12,7 @@ definition_1 - Public: yes - Synthetic: no - Lazy: yes +- Shared: yes - Synchronized: no - Abstract: yes - Factory Class: `Full\Qualified\FactoryClass` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.xml index b21190dc7983e..59a1e85c6bb8a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.xml @@ -2,7 +2,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json index 3397fd67acd6e..c76d13ee4234d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json @@ -6,6 +6,7 @@ "public": true, "synthetic": false, "lazy": true, + "shared": true, "synchronized": false, "abstract": true, "file": null, @@ -21,6 +22,7 @@ "public": false, "synthetic": true, "lazy": false, + "shared": true, "synchronized": false, "abstract": false, "file": "\/path\/to\/file", diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md index b3018b80b7f3b..3a3de41c409e5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md @@ -12,6 +12,7 @@ definition_1 - Public: yes - Synthetic: no - Lazy: yes +- Shared: yes - Synchronized: no - Abstract: yes - Factory Class: `Full\Qualified\FactoryClass` @@ -25,6 +26,7 @@ definition_2 - Public: no - Synthetic: yes - Lazy: no +- Shared: yes - Synchronized: no - Abstract: no - File: `/path/to/file` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml index 7aecc4f629e7f..5ceee2772a993 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml @@ -2,10 +2,10 @@ - + - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.json index 53bf114e81e04..40a3da00963af 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.json @@ -6,6 +6,7 @@ "public": false, "synthetic": true, "lazy": false, + "shared": true, "synchronized": false, "abstract": false, "file": "\/path\/to\/file", diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.md index 56a2c390779aa..c0d4f11e33261 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.md @@ -12,6 +12,7 @@ definition_2 - Public: no - Synthetic: yes - Lazy: no +- Shared: yes - Synchronized: no - Abstract: no - File: `/path/to/file` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml index d6ac0b750b169..51bb9c254f545 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml @@ -1,6 +1,6 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.json index 3837b95cb89e9..6844d2d18076b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.json @@ -6,6 +6,7 @@ "public": false, "synthetic": true, "lazy": false, + "shared": true, "synchronized": false, "abstract": false, "file": "\/path\/to\/file", @@ -20,6 +21,7 @@ "public": false, "synthetic": true, "lazy": false, + "shared": true, "synchronized": false, "abstract": false, "file": "\/path\/to\/file", diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.md index 6577037f9c00f..551c9cb24b298 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.md @@ -12,6 +12,7 @@ definition_2 - Public: no - Synthetic: yes - Lazy: no +- Shared: yes - Synchronized: no - Abstract: no - File: `/path/to/file` @@ -30,6 +31,7 @@ definition_2 - Public: no - Synthetic: yes - Lazy: no +- Shared: yes - Synchronized: no - Abstract: no - File: `/path/to/file` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.xml index be9d2f015bd2c..01f324860885f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.xml @@ -1,12 +1,12 @@ - + - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.json index 8de781dfc45a5..92f1300b4bd51 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.json @@ -4,6 +4,7 @@ "public": true, "synthetic": false, "lazy": true, + "shared": true, "synchronized": false, "abstract": true, "file": null, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.md index 68d3569732c61..6c18a6c2bbf82 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.md @@ -3,6 +3,7 @@ - Public: yes - Synthetic: no - Lazy: yes +- Shared: yes - Synchronized: no - Abstract: yes - Factory Class: `Full\Qualified\FactoryClass` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.txt index af495497dd35d..4c37faccf29ad 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.txt +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.txt @@ -5,6 +5,7 @@ Public yes Synthetic no Lazy yes +Shared yes Synchronized no Abstract yes Factory Class Full\Qualified\FactoryClass diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.xml index 92a9bbd70bd30..ec8a8cefa9e47 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.xml @@ -1,4 +1,4 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.json index 9d58434c17e1b..22a094928a48a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.json @@ -4,6 +4,7 @@ "public": false, "synthetic": true, "lazy": false, + "shared": true, "synchronized": false, "abstract": false, "file": "\/path\/to\/file", diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.md index 6b2f14651d300..8668587994270 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.md @@ -3,6 +3,7 @@ - Public: no - Synthetic: yes - Lazy: no +- Shared: yes - Synchronized: no - Abstract: no - File: `/path/to/file` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.txt index 28a00d583b090..62fc7d2dc6c33 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.txt +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.txt @@ -8,6 +8,7 @@ Public no Synthetic yes Lazy no +Shared yes Synchronized no Abstract no Required File /path/to/file diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.xml index f128e522e5990..ce9b1d05220c6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.xml @@ -1,5 +1,5 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.json index e40e130d453cb..4b68f0cefc0e4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.json @@ -1,9 +1,11 @@ [ { "type": "function", - "name": "global_function" + "name": "global_function", + "priority": 255 }, { - "type": "closure" + "type": "closure", + "priority": -1 } ] diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.md index 206c44f717526..98b81ecdce422 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.md @@ -4,7 +4,9 @@ - Type: `function` - Name: `global_function` +- Priority: `255` ## Listener 2 - Type: `closure` +- Priority: `-1` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.txt index 22b17a19cfb91..45035d12d6228 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.txt +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.txt @@ -1,8 +1,8 @@ [event_dispatcher] Registered listeners for event event1 -+-------+-------------------+ -| Order | Callable | -+-------+-------------------+ -| #1 | global_function() | -| #2 | \Closure() | -+-------+-------------------+ ++-------+-------------------+----------+ +| Order | Callable | Priority | ++-------+-------------------+----------+ +| #1 | global_function() | 255 | +| #2 | \Closure() | -1 | ++-------+-------------------+----------+ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.xml index 4806f1f1280c7..bc03189af7b80 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.xml @@ -1,5 +1,5 @@ - - + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.json index 56fc7a4f1e546..30772d9a4a212 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.json @@ -2,16 +2,19 @@ "event1": [ { "type": "function", - "name": "global_function" + "name": "global_function", + "priority": 255 }, { - "type": "closure" + "type": "closure", + "priority": -1 } ], "event2": [ { "type": "object", - "name": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass" + "name": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass", + "priority": 0 } ] } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.md index ad4b79e3117fa..eb809789d5f17 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.md @@ -6,10 +6,12 @@ - Type: `function` - Name: `global_function` +- Priority: `255` ### Listener 2 - Type: `closure` +- Priority: `-1` ## event2 @@ -17,3 +19,4 @@ - Type: `object` - Name: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass` +- Priority: `0` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.txt index 95a5b4648e939..88e5dc9c89692 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.txt +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.txt @@ -1,16 +1,16 @@ [event_dispatcher] Registered listeners by event [Event] event1 -+-------+-------------------+ -| Order | Callable | -+-------+-------------------+ -| #1 | global_function() | -| #2 | \Closure() | -+-------+-------------------+ ++-------+-------------------+----------+ +| Order | Callable | Priority | ++-------+-------------------+----------+ +| #1 | global_function() | 255 | +| #2 | \Closure() | -1 | ++-------+-------------------+----------+ [Event] event2 -+-------+-----------------------------------------------------------------------------------+ -| Order | Callable | -+-------+-----------------------------------------------------------------------------------+ -| #1 | Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass::__invoke() | -+-------+-----------------------------------------------------------------------------------+ ++-------+-----------------------------------------------------------------------------------+----------+ +| Order | Callable | Priority | ++-------+-----------------------------------------------------------------------------------+----------+ +| #1 | Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass::__invoke() | 0 | ++-------+-----------------------------------------------------------------------------------+----------+ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.xml index 3e4b20d823798..d7443f9743666 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.xml @@ -1,10 +1,10 @@ - - + + - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.json index 6372d9e5b56df..b7a5dec87df72 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.json @@ -4,6 +4,7 @@ "public": true, "synthetic": false, "lazy": true, + "shared": true, "synchronized": true, "abstract": true, "file": null, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.md index d9832a1511ab2..f527ab9ff8749 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.md @@ -3,6 +3,7 @@ - Public: yes - Synthetic: no - Lazy: yes +- Shared: yes - Synchronized: yes - Abstract: yes - Factory Class: `Full\Qualified\FactoryClass` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.txt index 3d9cbb2077c3b..09340efcf5d82 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.txt +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.txt @@ -5,6 +5,7 @@ Public yes Synthetic no Lazy yes +Shared yes Synchronized yes Abstract yes Factory Class Full\Qualified\FactoryClass diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.xml index 75d0820244579..6088d9a21b5a8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_1.xml @@ -1,2 +1,2 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.json index 278a5bfed413b..bb0f5685f36a6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.json +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.json @@ -4,6 +4,7 @@ "public": false, "synthetic": true, "lazy": false, + "shared": true, "synchronized": false, "abstract": false, "file": "\/path\/to\/file", diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.md index f552debbf18bc..43227638d88a4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.md +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.md @@ -3,6 +3,7 @@ - Public: no - Synthetic: yes - Lazy: no +- Shared: yes - Synchronized: no - Abstract: no - File: `/path/to/file` diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.txt index 28a00d583b090..62fc7d2dc6c33 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.txt +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.txt @@ -8,6 +8,7 @@ Public no Synthetic yes Lazy no +Shared yes Synchronized no Abstract no Required File /path/to/file diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.xml index dd3e2e06d7174..7a2154487d1eb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/legacy_synchronized_service_definition_2.xml @@ -1,5 +1,5 @@ - + val1 diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php index 4af5b929cbb50..76c0ce46a2f7a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php @@ -29,6 +29,7 @@ class FormHelperDivLayoutTest extends AbstractDivLayoutTest protected $testableFeatures = array( 'choice_attr', + 'choice_label_attr', ); protected function getExtensions() @@ -128,14 +129,4 @@ public static function themeInheritanceProvider() array(array('TestBundle:Parent'), array('TestBundle:Child')), ); } - - public function testRange() - { - // No-op for forward compatibility with AbstractLayoutTest 2.8 - } - - public function testRangeWithMinMaxValues() - { - // 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..d87b128676cf1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php @@ -29,6 +29,7 @@ class FormHelperTableLayoutTest extends AbstractTableLayoutTest protected $testableFeatures = array( 'choice_attr', + 'choice_label_attr', ); protected function getExtensions() @@ -115,14 +116,4 @@ protected function setTheme(FormView $view, array $themes) { $this->engine->get('form')->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 - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 4b658ddc1b568..843a4cce7be4a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -17,36 +17,36 @@ ], "require": { "php": ">=5.3.9", - "symfony/asset": "~2.7", - "symfony/dependency-injection": "~2.6,>=2.6.2", + "symfony/asset": "~2.7|~3.0.0", + "symfony/dependency-injection": "~2.8", "symfony/config": "~2.4", - "symfony/event-dispatcher": "~2.5", - "symfony/http-foundation": "~2.4.9|~2.5,>=2.5.4", + "symfony/event-dispatcher": "~2.8|~3.0.0", + "symfony/http-foundation": "~2.4.9|~2.5,>=2.5.4|~3.0.0", "symfony/http-kernel": "~2.7", - "symfony/filesystem": "~2.3", - "symfony/routing": "~2.6,>2.6.4", - "symfony/security-core": "~2.6", - "symfony/security-csrf": "~2.6", - "symfony/stopwatch": "~2.3", - "symfony/templating": "~2.1", + "symfony/filesystem": "~2.3|~3.0.0", + "symfony/routing": "~2.8|~3.0.0", + "symfony/security-core": "~2.6|~3.0.0", + "symfony/security-csrf": "~2.6|~3.0.0", + "symfony/stopwatch": "~2.3|~3.0.0", + "symfony/templating": "~2.1|~3.0.0", "symfony/translation": "~2.7", "doctrine/annotations": "~1.0" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/browser-kit": "~2.4", - "symfony/console": "~2.7", - "symfony/css-selector": "~2.0,>=2.0.5", - "symfony/dom-crawler": "~2.0,>=2.0.5", - "symfony/finder": "~2.0,>=2.0.5", - "symfony/intl": "~2.3", - "symfony/security": "~2.6", - "symfony/form": "~2.7,>=2.7.2", - "symfony/class-loader": "~2.1", - "symfony/expression-language": "~2.6", - "symfony/process": "~2.0,>=2.0.5", - "symfony/validator": "~2.5", - "symfony/yaml": "~2.0,>=2.0.5" + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/browser-kit": "~2.4|~3.0.0", + "symfony/console": "~2.7|~3.0.0", + "symfony/css-selector": "~2.0,>=2.0.5|~3.0.0", + "symfony/dom-crawler": "~2.0,>=2.0.5|~3.0.0", + "symfony/finder": "~2.0,>=2.0.5|~3.0.0", + "symfony/intl": "~2.3|~3.0.0", + "symfony/security": "~2.6|~3.0.0", + "symfony/form": "~2.8", + "symfony/class-loader": "~2.1|~3.0.0", + "symfony/expression-language": "~2.6|~3.0.0", + "symfony/process": "~2.0,>=2.0.5|~3.0.0", + "symfony/validator": "~2.5|~3.0.0", + "symfony/yaml": "~2.0,>=2.0.5|~3.0.0" }, "suggest": { "symfony/console": "For using the console commands", @@ -62,7 +62,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md index f1adc832efe23..f893aa92236ab 100644 --- a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md @@ -1,12 +1,18 @@ CHANGELOG ========= +2.8.0 +----- + + * deprecated the `key` setting of `anonymous` and `remember_me` in favor of the + `secret` setting. + 2.6.0 ----- * Added the possibility to override the default success/failure handler to get the provider key and the options injected - * Deprecated the `security.context` service for the `security.token_storage` and + * Deprecated the `security.context` service for the `security.token_storage` and `security.authorization_checker` services. 2.4.0 diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSecurityVotersPass.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSecurityVotersPass.php index 497807174d24a..099b1b48b7261 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSecurityVotersPass.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSecurityVotersPass.php @@ -14,6 +14,7 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Exception\LogicException; /** * Adds all configured security voters to the access decision manager. @@ -40,6 +41,10 @@ public function process(ContainerBuilder $container) $voters = iterator_to_array($voters); ksort($voters); - $container->getDefinition('security.access.decision_manager')->replaceArgument(0, array_values($voters)); + if (!$voters) { + throw new LogicException('No security voters found. You need to tag at least one with "security.voter"'); + } + + $container->getDefinition('security.access.decision_manager')->addMethodCall('setVoters', array(array_values($voters))); } } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php index 1dea48728ed60..7b6ac4186ac03 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php @@ -286,8 +286,22 @@ private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $facto ->end() ->arrayNode('anonymous') ->canBeUnset() + ->beforeNormalization() + ->ifTrue(function ($v) { return isset($v['key']); }) + ->then(function ($v) { + if (isset($v['secret'])) { + throw new \LogicException('Cannot set both key and secret options for security.firewall.anonymous, use only secret instead.'); + } + + @trigger_error('security.firewall.anonymous.key is deprecated since version 2.8 and will be removed in 3.0. Use security.firewall.anonymous.secret instead.', E_USER_DEPRECATED); + + $v['secret'] = $v['key']; + + unset($v['key']); + }) + ->end() ->children() - ->scalarNode('key')->defaultValue(uniqid('', true))->end() + ->scalarNode('secret')->defaultValue(uniqid('', true))->end() ->end() ->end() ->arrayNode('switch_user') diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php index 7aa4f5baa03eb..d8321f52456a2 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php @@ -35,7 +35,7 @@ public function create(ContainerBuilder $container, $id, $config, $userProvider, $authProviderId = 'security.authentication.provider.rememberme.'.$id; $container ->setDefinition($authProviderId, new DefinitionDecorator('security.authentication.provider.rememberme')) - ->addArgument($config['key']) + ->addArgument($config['secret']) ->addArgument($id) ; @@ -56,7 +56,7 @@ public function create(ContainerBuilder $container, $id, $config, $userProvider, } $rememberMeServices = $container->setDefinition($rememberMeServicesId, new DefinitionDecorator($templateId)); - $rememberMeServices->replaceArgument(1, $config['key']); + $rememberMeServices->replaceArgument(1, $config['secret']); $rememberMeServices->replaceArgument(2, $id); if (isset($config['token_provider'])) { @@ -120,10 +120,25 @@ public function getKey() public function addConfiguration(NodeDefinition $node) { $node->fixXmlConfig('user_provider'); - $builder = $node->children(); + $builder = $node + ->beforeNormalization() + ->ifTrue(function ($v) { return isset($v['key']); }) + ->then(function ($v) { + if (isset($v['secret'])) { + throw new \LogicException('Cannot set both key and secret options for remember_me, use only secret instead.'); + } + + @trigger_error('remember_me.key is deprecated since version 2.8 and will be removed in 3.0. Use remember_me.secret instead.', E_USER_DEPRECATED); + + $v['secret'] = $v['key']; + + unset($v['key']); + }) + ->end() + ->children(); $builder - ->scalarNode('key')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('secret')->isRequired()->cannotBeEmpty()->end() ->scalarNode('token_provider')->end() ->arrayNode('user_providers') ->beforeNormalization() diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index ce5d3f3e118c9..4fce6ebca3472 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -410,7 +410,7 @@ private function createAuthenticationListeners($container, $id, $firewall, &$aut $listenerId = 'security.authentication.listener.anonymous.'.$id; $container ->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.anonymous')) - ->replaceArgument(1, $firewall['anonymous']['key']) + ->replaceArgument(1, $firewall['anonymous']['secret']) ; $listeners[] = new Reference($listenerId); @@ -418,7 +418,7 @@ private function createAuthenticationListeners($container, $id, $firewall, &$aut $providerId = 'security.authentication.provider.anonymous.'.$id; $container ->setDefinition($providerId, new DefinitionDecorator('security.authentication.provider.anonymous')) - ->replaceArgument(0, $firewall['anonymous']['key']) + ->replaceArgument(0, $firewall['anonymous']['secret']) ; $authenticationProviders[] = $providerId; diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig index 923be83810648..a495829d3e517 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig +++ b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig @@ -32,8 +32,7 @@ {% endset %} {% set icon %} - - {% if collector.user %}
{{ collector.user }}
{% endif %} + {{ collector.user }} {% endset %} {% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %} {% endblock %} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php index b16a46ff03457..4521c8cdcd227 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php @@ -71,7 +71,7 @@ 'x509' => true, 'remote_user' => true, 'logout' => true, - 'remember_me' => array('key' => 'TheKey'), + 'remember_me' => array('secret' => 'TheSecret'), ), 'host' => array( 'pattern' => '/test', diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/remember_me_options.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/remember_me_options.php index 61a2bc938f0d2..e0ca4f6dedf3e 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/remember_me_options.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/remember_me_options.php @@ -1,4 +1,5 @@ loadFromExtension('security', array( 'providers' => array( 'default' => array('id' => 'foo'), @@ -8,7 +9,7 @@ 'main' => array( 'form_login' => true, 'remember_me' => array( - 'key' => 'TheyKey', + 'secret' => 'TheSecret', 'catch_exceptions' => false, 'token_provider' => 'token_provider_id', ), diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml index 1a56aa88fda07..e5f5905fa7e0b 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml @@ -56,7 +56,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/remember_me_options.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/remember_me_options.xml index 1674756891575..b6ade91a07970 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/remember_me_options.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/remember_me_options.xml @@ -11,7 +11,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml index 93c231ea235f1..6b27806e564be 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml @@ -55,7 +55,7 @@ security: remote_user: true logout: true remember_me: - key: TheKey + secret: TheSecret host: pattern: /test host: foo\.example\.org diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/remember_me_options.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/remember_me_options.yml index 3a38b33c521c6..a521c8c6a803d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/remember_me_options.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/remember_me_options.yml @@ -7,6 +7,6 @@ security: main: form_login: true remember_me: - key: TheKey + secret: TheSecret catch_exceptions: false token_provider: token_provider_id diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginFormType.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginFormType.php index d76d8fd629bba..04d41752c83ea 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginFormType.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginFormType.php @@ -16,7 +16,7 @@ use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvent; -use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Security\Core\Security; @@ -29,14 +29,14 @@ */ class UserLoginFormType extends AbstractType { - private $request; + private $requestStack; /** - * @param Request $request A request instance + * @param RequestStack $requestStack A RequestStack instance */ - public function __construct(Request $request) + public function __construct(RequestStack $requestStack) { - $this->request = $request; + $this->requestStack = $requestStack; } /** @@ -50,7 +50,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) ->add('_target_path', 'hidden') ; - $request = $this->request; + $request = $this->requestStack->getCurrentRequest(); /* Note: since the Security component's form login listener intercepts * the POST request, this form will never really be bound to the 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 e1e2b0e883933..2b97bd5a66384 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml @@ -4,9 +4,8 @@ imports: services: csrf_form_login.form.type: class: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Form\UserLoginFormType - scope: request arguments: - - @request + - @request_stack tags: - { name: form.type, alias: user_login } diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 55c251a40be69..a929eb09b8887 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -17,25 +17,26 @@ ], "require": { "php": ">=5.3.9", - "symfony/security": "~2.7", + "symfony/security": "~2.8|~3.0.0", "symfony/http-kernel": "~2.2" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/browser-kit": "~2.4", - "symfony/console": "~2.7", - "symfony/css-selector": "~2.0,>=2.0.5", - "symfony/dependency-injection": "~2.6,>=2.6.6", - "symfony/dom-crawler": "~2.0,>=2.0.5", + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/browser-kit": "~2.4|~3.0.0", + "symfony/config": "~2.8|~3.0.0", + "symfony/console": "~2.7|~3.0.0", + "symfony/css-selector": "~2.0,>=2.0.5|~3.0.0", + "symfony/dependency-injection": "~2.6,>=2.6.6|~3.0.0", + "symfony/dom-crawler": "~2.0,>=2.0.5|~3.0.0", "symfony/form": "~2.7", "symfony/framework-bundle": "~2.7", - "symfony/http-foundation": "~2.3", - "symfony/twig-bundle": "~2.7", - "symfony/twig-bridge": "~2.7", - "symfony/process": "~2.0,>=2.0.5", - "symfony/validator": "~2.5", - "symfony/yaml": "~2.0,>=2.0.5", - "symfony/expression-language": "~2.6", + "symfony/http-foundation": "~2.4|~3.0.0", + "symfony/twig-bundle": "~2.7|~3.0.0", + "symfony/twig-bridge": "~2.7|~3.0.0", + "symfony/process": "~2.0,>=2.0.5|~3.0.0", + "symfony/validator": "~2.5|~3.0.0", + "symfony/yaml": "~2.0,>=2.0.5|~3.0.0", + "symfony/expression-language": "~2.6|~3.0.0", "doctrine/doctrine-bundle": "~1.2", "twig/twig": "~1.12", "ircmaxell/password-compat": "~1.0" @@ -46,7 +47,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheCacheWarmer.php b/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheCacheWarmer.php index 65827eba5a6b8..5dde9406914a4 100644 --- a/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheCacheWarmer.php +++ b/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheCacheWarmer.php @@ -11,9 +11,11 @@ namespace Symfony\Bundle\TwigBundle\CacheWarmer; +use Symfony\Component\Finder\Finder; use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplateFinderInterface; +use Symfony\Component\Templating\TemplateReference; /** * Generates the Twig cache for all templates. @@ -27,14 +29,16 @@ class TemplateCacheCacheWarmer implements CacheWarmerInterface { protected $container; protected $finder; + private $paths; /** * Constructor. * * @param ContainerInterface $container The dependency injection container * @param TemplateFinderInterface $finder The template paths cache warmer + * @param array $paths Additional twig paths to warm */ - public function __construct(ContainerInterface $container, TemplateFinderInterface $finder) + public function __construct(ContainerInterface $container, TemplateFinderInterface $finder, array $paths = array()) { // We don't inject the Twig environment directly as it depends on the // template locator (via the loader) which might be a cached one. @@ -42,6 +46,7 @@ public function __construct(ContainerInterface $container, TemplateFinderInterfa // has been warmed up $this->container = $container; $this->finder = $finder; + $this->paths = $paths; } /** @@ -53,7 +58,13 @@ public function warmUp($cacheDir) { $twig = $this->container->get('twig'); - foreach ($this->finder->findAllTemplates() as $template) { + $templates = $this->finder->findAllTemplates(); + + foreach ($this->paths as $path => $namespace) { + $templates = array_merge($templates, $this->findTemplatesInFolder($namespace, $path)); + } + + foreach ($templates as $template) { if ('twig' !== $template->get('engine')) { continue; } @@ -75,4 +86,32 @@ public function isOptional() { return true; } + + /** + * Find templates in the given directory. + * + * @param string $namespace The namespace for these templates + * @param string $dir The folder where to look for templates + * + * @return array An array of templates of type TemplateReferenceInterface + */ + private function findTemplatesInFolder($namespace, $dir) + { + if (!is_dir($dir)) { + return array(); + } + + $templates = array(); + $finder = new Finder(); + + foreach ($finder->files()->followLinks()->in($dir) as $file) { + $name = $file->getRelativePathname(); + $templates[] = new TemplateReference( + $namespace ? sprintf('@%s/%s', $namespace, $name) : $name, + 'twig' + ); + } + + return $templates; + } } diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php index 7b97e120baa4b..c0171219d0487 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\TwigBundle\DependencyInjection; use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Resource\FileExistenceResource; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; @@ -76,21 +77,29 @@ public function load(array $configs, ContainerBuilder $container) } } + $container->getDefinition('twig.cache_warmer')->replaceArgument(2, $config['paths']); + // register bundles as Twig namespaces foreach ($container->getParameter('kernel.bundles') as $bundle => $class) { - if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/'.$bundle.'/views')) { + $dir = $container->getParameter('kernel.root_dir').'/Resources/'.$bundle.'/views'; + if (is_dir($dir)) { $this->addTwigPath($twigFilesystemLoaderDefinition, $dir, $bundle); } + $container->addResource(new FileExistenceResource($dir)); $reflection = new \ReflectionClass($class); - if (is_dir($dir = dirname($reflection->getFileName()).'/Resources/views')) { + $dir = dirname($reflection->getFileName()).'/Resources/views'; + if (is_dir($dir)) { $this->addTwigPath($twigFilesystemLoaderDefinition, $dir, $bundle); } + $container->addResource(new FileExistenceResource($dir)); } - if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/views')) { + $dir = $container->getParameter('kernel.root_dir').'/Resources/views'; + if (is_dir($dir)) { $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($dir)); } + $container->addResource(new FileExistenceResource($dir)); if (!empty($config['globals'])) { $def = $container->getDefinition('twig'); diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml index 9e1a11777418c..c06a82f06ec64 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml @@ -48,6 +48,7 @@ + diff --git a/src/Symfony/Bundle/TwigBundle/composer.json b/src/Symfony/Bundle/TwigBundle/composer.json index 61c85d33e3ed0..1f73c781e9f3b 100644 --- a/src/Symfony/Bundle/TwigBundle/composer.json +++ b/src/Symfony/Bundle/TwigBundle/composer.json @@ -17,20 +17,20 @@ ], "require": { "php": ">=5.3.9", - "symfony/asset": "~2.7", - "symfony/twig-bridge": "~2.7", - "symfony/http-foundation": "~2.5", + "symfony/asset": "~2.7|~3.0.0", + "symfony/twig-bridge": "~2.7|~3.0.0", + "symfony/http-foundation": "~2.5|~3.0.0", "symfony/http-kernel": "~2.7" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/stopwatch": "~2.2", - "symfony/dependency-injection": "~2.6,>=2.6.6", - "symfony/expression-language": "~2.4", - "symfony/config": "~2.2", - "symfony/routing": "~2.1", - "symfony/templating": "~2.1", - "symfony/framework-bundle": "~2.7" + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/stopwatch": "~2.2|~3.0.0", + "symfony/dependency-injection": "~2.6,>=2.6.6|~3.0.0", + "symfony/expression-language": "~2.4|~3.0.0", + "symfony/config": "~2.8|~3.0.0", + "symfony/routing": "~2.1|~3.0.0", + "symfony/templating": "~2.1|~3.0.0", + "symfony/framework-bundle": "~2.7|~3.0.0" }, "autoload": { "psr-4": { "Symfony\\Bundle\\TwigBundle\\": "" } @@ -38,7 +38,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php index 2748910a19ae8..cada4ee6ca435 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php @@ -91,6 +91,10 @@ public function panelAction(Request $request, $token) $panel = $request->query->get('panel', 'request'); $page = $request->query->get('page', 'home'); + if ('latest' === $token && $latest = current($this->profiler->find(null, null, 1, null, null, null))) { + $token = $latest['token']; + } + if (!$profile = $this->profiler->loadProfile($token)) { return new Response($this->twig->render('@WebProfiler/Profiler/info.html.twig', array('about' => 'no_token', 'token' => $token)), 200, array('Content-Type' => 'text/html')); } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig index d25122b90bf27..485cb57c9bff6 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig @@ -69,8 +69,7 @@ {% set debug_status_class %}sf-toolbar-status sf-toolbar-status-{{ collector.debug ? 'green' : 'red' }}{% endset %} {% set icon %} - - {{ token }} + {{ token }} {% if 'n/a' != collector.appname or 'n/a' != collector.env %} {{ collector.appname }} 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 291a4eac110c0..a6707da12d77e 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig @@ -87,20 +87,30 @@ {% if collector.logs %} -
    + + + + + + + + {% set log_loop_index = 0 %} {% for log in collector.logs %} {% set is_deprecation = log.context.level is defined and log.context.type is defined and (constant('E_DEPRECATED') == log.context.type or constant('E_USER_DEPRECATED') == log.context.type) %} {% if priority == '-100' ? is_deprecation : log.priority >= priority %} {% set log_loop_index = log_loop_index + 1 %} -
  • - {{ logger.display_message(loop.index, log, is_deprecation) }} -
  • + + + + + + {% endif %} {% else %} -
  • No logs available for this priority.
  • + {% endfor %} - +
    #PriorityChannelMessage and context
    {{ log_loop_index }}{{ is_deprecation ? 'DEPRECATION' : log.priorityName }}{{ log.channel is defined ? log.channel }}{{ logger.display_message(loop.index, log, is_deprecation) }}
    No logs available for {{ priority }} priority.
    {% else %}

    No logs available. @@ -116,6 +126,8 @@ {% set stack = log.context.stack|default([]) %} {% set id = 'sf-call-stack-' ~ log_index %} + {{ log.message }} + {% if stack %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig index ca1c937545ad2..4d00a07d5fd8a 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig @@ -22,7 +22,6 @@ {{ collector.statuscode }} {{ request_handler }} - on {{ request_route }} {% endset %} {% set text %} {% spaceless %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig index d44685471798a..840e7cd1acc80 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig @@ -57,6 +57,14 @@ {% endblock %} {% block panelContent %} + {% set filter = request.query.get('state', '-1') %} + {% set filterOptions = { + '-1': '', + (constant('Symfony\\Component\\Translation\\DataCollectorTranslator::MESSAGE_DEFINED')): 'Defined messages', + (constant('Symfony\\Component\\Translation\\DataCollectorTranslator::MESSAGE_MISSING')): 'Missing messages', + (constant('Symfony\\Component\\Translation\\DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK')): 'Fallback messages', + } %} +

    Translation Stats

    @@ -72,6 +80,22 @@ + + + +
    Missing messages
    {{ collector.countMissings }}
    Filter +
    + + + +
    +
    @@ -83,19 +107,50 @@ Id Message Preview - {% for message in collector.messages %} + {% for message in collector.messages if message.state == filter or filter == '-1' %} {{ translator.state(message) }} {{ message.locale }} {{ message.domain }} {{ message.id }} + {% if message.count > 1 %}
    (used {{ message.count }} times){% endif %} + {% if message.transChoiceNumber is not null %}
    (use pluralization){% endif %} + + {% if message.parameters|length > 0 %} +
    + [ + + + - + Parameters + ] + + +
    + {% endif %} {{ message.translation }} {% endfor %} + + {% endblock %} {% macro state(translation) %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/info.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/info.html.twig index aeffb2cf256bb..9be617a3ecab7 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/info.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/info.html.twig @@ -25,10 +25,17 @@ The token already exists in the database.

    {% elseif about == 'no_token' %} -

    Token not found

    -

    - Token "{{ token }}" was not found in the database. -

    + {% if token == 'latest' %} +

    No profiles

    +

    + No profiles found in the database. +

    + {% else %} +

    Token not found

    +

    + Token "{{ token }}" was not found in the database. +

    + {% endif %} {% endif %} {% endblock %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig index 12728b964df0f..6df82890c180b 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig @@ -14,6 +14,7 @@ {% if profile %}
    View last 10 + View latest Profile for: {{ profile.method|upper }} {% if profile.method|upper in ['GET', 'HEAD'] %} 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 4972a0f75bf88..fd32565347953 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig @@ -179,7 +179,7 @@ pre, code { width: 250px; margin-left: -100%; } -#collector-content table td { +table td { background-color: white; } h1 { @@ -217,7 +217,7 @@ li { border-top-right-radius: 16px; line-height: 18px; } -a#resume-view-all { +a#resume-view-all, a#resume-view-latest { display: inline-block; padding: 0.2em 0.7em; margin-right: 0.5em; @@ -273,15 +273,15 @@ ul.alt li { ul.alt li.even { background: #f1f7e2; } -ul.alt li.error { +ul.alt li.error, tr.error td { background-color: #f66; margin-bottom: 1px; } -ul.alt li.warning { +ul.alt li.warning, tr.warning td { background-color: #ffcc00; margin-bottom: 1px; } -ul.alt li.scream, ul.alt li.scream strong { +ul.alt li.scream, ul.alt li.scream strong, tr.scream td, tr.scream strong { color: gray; } ul.sf-call-stack li { 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 092254643bf41..8d736a716dd36 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -133,7 +133,6 @@ .sf-toolbar-block .sf-toolbar-info-with-delimiter { border-right: 1px solid #999; padding-right: 5px; - margin-right: 5px; } .sf-toolbar-block .sf-toolbar-info { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php index 132315fd23c82..00d17cee42acd 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php @@ -18,7 +18,6 @@ use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; -use Symfony\Component\DependencyInjection\Scope; class WebProfilerExtensionTest extends TestCase { @@ -49,8 +48,6 @@ protected function setUp() $this->kernel = $this->getMock('Symfony\\Component\\HttpKernel\\KernelInterface'); $this->container = new ContainerBuilder(); - $this->container->addScope(new Scope('request')); - $this->container->register('request', 'Symfony\\Component\\HttpFoundation\\Request')->setScope('request'); $this->container->register('router', $this->getMockClass('Symfony\\Component\\Routing\\RouterInterface')); $this->container->register('twig', 'Twig_Environment'); $this->container->setParameter('kernel.bundles', array()); @@ -125,7 +122,6 @@ private function getDumpedContainer() eval('?>'.$dumper->dump(array('class' => $class))); $container = new $class(); - $container->enterScope('request'); $container->set('kernel', $this->kernel); return $container; diff --git a/src/Symfony/Bundle/WebProfilerBundle/composer.json b/src/Symfony/Bundle/WebProfilerBundle/composer.json index d4a2316d7230c..6c71f5da83840 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/composer.json +++ b/src/Symfony/Bundle/WebProfilerBundle/composer.json @@ -17,16 +17,16 @@ ], "require": { "php": ">=5.3.9", - "symfony/http-kernel": "~2.4", - "symfony/routing": "~2.2", - "symfony/twig-bridge": "~2.7" + "symfony/http-kernel": "~2.4|~3.0.0", + "symfony/routing": "~2.2|~3.0.0", + "symfony/twig-bridge": "~2.7|~3.0.0" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/config": "~2.2", - "symfony/console": "~2.3", - "symfony/dependency-injection": "~2.2", - "symfony/stopwatch": "~2.2" + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/config": "~2.2|~3.0.0", + "symfony/console": "~2.3|~3.0.0", + "symfony/dependency-injection": "~2.2|~3.0.0", + "symfony/stopwatch": "~2.2|~3.0.0" }, "autoload": { "psr-4": { "Symfony\\Bundle\\WebProfilerBundle\\": "" } @@ -34,7 +34,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Asset/composer.json b/src/Symfony/Component/Asset/composer.json index 88da5124794d4..5bc99e0a7a5c7 100644 --- a/src/Symfony/Component/Asset/composer.json +++ b/src/Symfony/Component/Asset/composer.json @@ -22,7 +22,7 @@ "symfony/http-foundation": "" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", + "symfony/phpunit-bridge": "~2.7|~3.0.0", "symfony/http-foundation": "~2.4" }, "autoload": { @@ -31,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/BrowserKit/composer.json b/src/Symfony/Component/BrowserKit/composer.json index 0cb629c085424..5146b781a5164 100644 --- a/src/Symfony/Component/BrowserKit/composer.json +++ b/src/Symfony/Component/BrowserKit/composer.json @@ -17,12 +17,12 @@ ], "require": { "php": ">=5.3.9", - "symfony/dom-crawler": "~2.0,>=2.0.5" + "symfony/dom-crawler": "~2.0,>=2.0.5|~3.0.0" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/process": "~2.0,>=2.0.5", - "symfony/css-selector": "~2.0,>=2.0.5" + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/process": "~2.0,>=2.0.5|~3.0.0", + "symfony/css-selector": "~2.0,>=2.0.5|~3.0.0" }, "suggest": { "symfony/process": "" @@ -33,7 +33,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/ClassLoader/composer.json b/src/Symfony/Component/ClassLoader/composer.json index 6d011b5082554..51f6358161b0a 100644 --- a/src/Symfony/Component/ClassLoader/composer.json +++ b/src/Symfony/Component/ClassLoader/composer.json @@ -20,15 +20,15 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/finder": "~2.0,>=2.0.5" + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/finder": "~2.0,>=2.0.5|~3.0.0" }, "autoload": { "psr-4": { "Symfony\\Component\\ClassLoader\\": "" } }, "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Config/Definition/ArrayNode.php b/src/Symfony/Component/Config/Definition/ArrayNode.php index 05ae1fdcd99e1..7c9c14594304c 100644 --- a/src/Symfony/Component/Config/Definition/ArrayNode.php +++ b/src/Symfony/Component/Config/Definition/ArrayNode.php @@ -29,6 +29,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface protected $addIfNotSet = false; protected $performDeepMerging = true; protected $ignoreExtraKeys = false; + protected $removeExtraKeys = true; protected $normalizeKeys = true; public function setNormalizeKeys($normalizeKeys) @@ -140,10 +141,12 @@ public function setPerformDeepMerging($boolean) * Whether extra keys should just be ignore without an exception. * * @param bool $boolean To allow extra keys + * @param bool $remove To remove extra keys */ - public function setIgnoreExtraKeys($boolean) + public function setIgnoreExtraKeys($boolean, $remove = true) { $this->ignoreExtraKeys = (bool) $boolean; + $this->removeExtraKeys = $this->ignoreExtraKeys && $remove; } /** @@ -300,6 +303,9 @@ protected function normalizeValue($value) if (isset($this->children[$name])) { $normalized[$name] = $this->children[$name]->normalize($val); unset($value[$name]); + } elseif (false === $this->removeExtraKeys) { + $normalized[$name] = $val; + unset($value[$name]); } } diff --git a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php index c64b2ecfdb91b..fb34cfa8f71f7 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php @@ -24,6 +24,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition { protected $performDeepMerging = true; protected $ignoreExtraKeys = false; + protected $removeExtraKeys = true; protected $children = array(); protected $prototype; protected $atLeastOne = false; @@ -284,11 +285,14 @@ public function performNoDeepMerging() * you want to send an entire configuration array through a special * tree that processes only part of the array. * + * @param bool $remove Whether to remove the extra keys + * * @return ArrayNodeDefinition */ - public function ignoreExtraKeys() + public function ignoreExtraKeys($remove = true) { $this->ignoreExtraKeys = true; + $this->removeExtraKeys = $remove; return $this; } @@ -393,7 +397,7 @@ protected function createNode() $node->addEquivalentValue(false, $this->falseEquivalent); $node->setPerformDeepMerging($this->performDeepMerging); $node->setRequired($this->required); - $node->setIgnoreExtraKeys($this->ignoreExtraKeys); + $node->setIgnoreExtraKeys($this->ignoreExtraKeys, $this->removeExtraKeys); $node->setNormalizeKeys($this->normalizeKeys); if (null !== $this->normalization) { diff --git a/src/Symfony/Component/Config/Resource/FileExistenceResource.php b/src/Symfony/Component/Config/Resource/FileExistenceResource.php new file mode 100644 index 0000000000000..4e68e8ac105be --- /dev/null +++ b/src/Symfony/Component/Config/Resource/FileExistenceResource.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * FileExistenceResource represents a resource stored on the filesystem. + * Freshness is only evaluated against resource creation or deletion. + * + * The resource can be a file or a directory. + * + * @author Charles-Henri Bruyand + */ +class FileExistenceResource implements ResourceInterface, \Serializable +{ + private $resource; + + private $exists; + + /** + * Constructor. + * + * @param string $resource The file path to the resource + */ + public function __construct($resource) + { + $this->resource = (string) $resource; + $this->exists = file_exists($resource); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->resource; + } + + /** + * {@inheritdoc} + */ + public function getResource() + { + return $this->resource; + } + + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + return file_exists($this->resource) === $this->exists; + } + + /** + * {@inheritdoc} + */ + public function serialize() + { + return serialize(array($this->resource, $this->exists)); + } + + /** + * {@inheritdoc} + */ + public function unserialize($serialized) + { + list($this->resource, $this->exists) = unserialize($serialized); + } +} diff --git a/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php b/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php index 291c2fd2cce06..c88fc13f07a7f 100644 --- a/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php @@ -50,6 +50,21 @@ public function testIgnoreExtraKeysNoException() $this->assertTrue(true, 'No exception was thrown when setIgnoreExtraKeys is true'); } + /** + * Tests that extra keys are not removed when + * ignoreExtraKeys second option is set to false. + * + * Related to testExceptionThrownOnUnrecognizedChild + */ + public function testIgnoreExtraKeysNotRemoved() + { + $node = new ArrayNode('roo'); + $node->setIgnoreExtraKeys(true, false); + + $data = array('foo' => 'bar'); + $this->assertSame($data, $node->normalize($data)); + } + /** * @dataProvider getPreNormalizationTests */ diff --git a/src/Symfony/Component/Config/Tests/Resource/FileExistenceResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/FileExistenceResourceTest.php new file mode 100644 index 0000000000000..56ee584b1a59b --- /dev/null +++ b/src/Symfony/Component/Config/Tests/Resource/FileExistenceResourceTest.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\Config\Tests\Resource; + +use Symfony\Component\Config\Resource\FileExistenceResource; + +class FileExistenceResourceTest extends \PHPUnit_Framework_TestCase +{ + protected $resource; + protected $file; + protected $time; + + protected function setUp() + { + $this->file = realpath(sys_get_temp_dir()).'/tmp.xml'; + $this->time = time(); + $this->resource = new FileExistenceResource($this->file); + } + + protected function tearDown() + { + if (file_exists($this->file)) { + unlink($this->file); + } + } + + public function testToString() + { + $this->assertSame($this->file, (string) $this->resource); + } + + public function testGetResource() + { + $this->assertSame($this->file, $this->resource->getResource(), '->getResource() returns the path to the resource'); + } + + public function testIsFreshWithExistingResource() + { + touch($this->file, $this->time); + $serialized = serialize(new FileExistenceResource($this->file)); + + $resource = unserialize($serialized); + $this->assertTrue($resource->isFresh($this->time), '->isFresh() returns true if the resource is still present'); + + unlink($this->file); + $resource = unserialize($serialized); + $this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource has been deleted'); + } + + public function testIsFreshWithAbsentResource() + { + $serialized = serialize(new FileExistenceResource($this->file)); + + $resource = unserialize($serialized); + $this->assertTrue($resource->isFresh($this->time), '->isFresh() returns true if the resource is still absent'); + + touch($this->file, $this->time); + $resource = unserialize($serialized); + $this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource has been created'); + } +} diff --git a/src/Symfony/Component/Config/composer.json b/src/Symfony/Component/Config/composer.json index a14d93572f5ed..b3ba2a29fccf3 100644 --- a/src/Symfony/Component/Config/composer.json +++ b/src/Symfony/Component/Config/composer.json @@ -17,10 +17,10 @@ ], "require": { "php": ">=5.3.9", - "symfony/filesystem": "~2.3" + "symfony/filesystem": "~2.3|~3.0.0" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7" + "symfony/phpunit-bridge": "~2.7|~3.0.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Config\\": "" } @@ -28,7 +28,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index e6642631c9423..5655c30f3fa45 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -284,6 +284,13 @@ public function setCode($code) throw new \InvalidArgumentException('Invalid callable provided to Command::setCode.'); } + if (PHP_VERSION_ID >= 50400 && $code instanceof \Closure) { + $r = new \ReflectionFunction($code); + if (null === $r->getClosureThis()) { + $code = \Closure::bind($code, $this); + } + } + $this->code = $code; return $this; diff --git a/src/Symfony/Component/Console/Helper/Table.php b/src/Symfony/Component/Console/Helper/Table.php index 9e86c856d05ee..275e23b87c864 100644 --- a/src/Symfony/Component/Console/Helper/Table.php +++ b/src/Symfony/Component/Console/Helper/Table.php @@ -205,24 +205,26 @@ public function setRow($column, array $row) public function render() { $this->calculateNumberOfColumns(); - $this->rows = $this->buildTableRows($this->rows); - $this->headers = $this->buildTableRows($this->headers); + $rows = $this->buildTableRows($this->rows); + $headers = $this->buildTableRows($this->headers); + + $this->calculateColumnsWidth(array_merge($headers, $rows)); $this->renderRowSeparator(); - if (!empty($this->headers)) { - foreach ($this->headers as $header) { + if (!empty($headers)) { + foreach ($headers as $header) { $this->renderRow($header, $this->style->getCellHeaderFormat()); $this->renderRowSeparator(); } } - foreach ($this->rows as $row) { + foreach ($rows as $row) { if ($row instanceof TableSeparator) { $this->renderRowSeparator(); } else { $this->renderRow($row, $this->style->getCellRowFormat()); } } - if (!empty($this->rows)) { + if (!empty($rows)) { $this->renderRowSeparator(); } @@ -246,7 +248,7 @@ private function renderRowSeparator() $markup = $this->style->getCrossingChar(); for ($column = 0; $column < $count; $column++) { - $markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->getColumnWidth($column)).$this->style->getCrossingChar(); + $markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->columnWidths[$column]).$this->style->getCrossingChar(); } $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup)); @@ -292,11 +294,11 @@ private function renderRow(array $row, $cellFormat) private function renderCell(array $row, $column, $cellFormat) { $cell = isset($row[$column]) ? $row[$column] : ''; - $width = $this->getColumnWidth($column); + $width = $this->columnWidths[$column]; if ($cell instanceof TableCell && $cell->getColspan() > 1) { // add the width of the following columns(numbers of colspan). foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) { - $width += $this->getColumnSeparatorWidth() + $this->getColumnWidth($nextColumn); + $width += $this->getColumnSeparatorWidth() + $this->columnWidths[$nextColumn]; } } @@ -509,21 +511,20 @@ private function getRowColumns($row) * * @return int */ - private function getColumnWidth($column) + private function calculateColumnsWidth($rows) { - if (isset($this->columnWidths[$column])) { - return $this->columnWidths[$column]; - } + for ($column = 0; $column < $this->numberOfColumns; $column++) { + $lengths = array(); + foreach ($rows as $row) { + if ($row instanceof TableSeparator) { + continue; + } - foreach (array_merge($this->headers, $this->rows) as $row) { - if ($row instanceof TableSeparator) { - continue; + $lengths[] = $this->getCellWidth($row, $column); } - $lengths[] = $this->getCellWidth($row, $column); + $this->columnWidths[$column] = max($lengths) + strlen($this->style->getCellRowContentFormat()) - 2; } - - return $this->columnWidths[$column] = max($lengths) + strlen($this->style->getCellRowContentFormat()) - 2; } /** diff --git a/src/Symfony/Component/Console/Tests/Command/CommandTest.php b/src/Symfony/Component/Console/Tests/Command/CommandTest.php index 7702ac8341d19..4b465f8aaaecd 100644 --- a/src/Symfony/Component/Console/Tests/Command/CommandTest.php +++ b/src/Symfony/Component/Console/Tests/Command/CommandTest.php @@ -284,6 +284,33 @@ public function testSetCode() $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay()); } + public function getSetCodeBindToClosureTests() + { + return array( + array(true, 'not bound to the command'), + array(false, 'bound to the command'), + ); + } + + /** @dataProvider getSetCodeBindToClosureTests */ + public function testSetCodeBindToClosure($previouslyBound, $expected) + { + if (PHP_VERSION_ID < 50400) { + $this->markTestSkipped('Test skipped, for PHP 5.4+ only.'); + } + + $code = createClosure(); + if ($previouslyBound) { + $code = $code->bindTo($this); + } + + $command = new \TestCommand(); + $command->setCode($code); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.$expected.PHP_EOL, $tester->getDisplay()); + } + public function testSetCodeWithNonClosureCallable() { $command = new \TestCommand(); @@ -333,3 +360,13 @@ public function testLegacyAsXml() $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/command_asxml.txt', $command->asXml(), '->asXml() returns an XML representation of the command'); } } + +// In order to get an unbound closure, we should create it outside a class +// scope. +function createClosure() +{ + return function(InputInterface $input, OutputInterface $output) + { + $output->writeln($this instanceof Command ? 'bound to the command' : 'not bound to the command'); + }; +} diff --git a/src/Symfony/Component/Console/Tests/Helper/TableTest.php b/src/Symfony/Component/Console/Tests/Helper/TableTest.php index 2a66caa046be5..de227949e95fa 100644 --- a/src/Symfony/Component/Console/Tests/Helper/TableTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/TableTest.php @@ -427,7 +427,7 @@ public function testRenderProvider() array('ISBN', 'Author'), array( array( - new TableCell("9971-5-0210-0", array('rowspan' => 3, 'colspan' => 1)), + new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 1)), 'Dante Alighieri', ), array(new TableSeparator()), @@ -554,6 +554,33 @@ public function testRowSeparator() $this->assertEquals($table, $table->addRow(new TableSeparator()), 'fluent interface on addRow() with a single TableSeparator() works'); } + public function testRenderMultiCalls() + { + $table = new Table($output = $this->getOutputStream()); + $table->setRows(array( + array(new TableCell('foo', array('colspan' => 2))), + )); + $table->render(); + $table->render(); + $table->render(); + + $expected = +<<assertEquals($expected, $this->getOutputContent($output)); + } + protected function getOutputStream() { return new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, false); diff --git a/src/Symfony/Component/Console/composer.json b/src/Symfony/Component/Console/composer.json index 16b7b81245275..706e5e5df7826 100644 --- a/src/Symfony/Component/Console/composer.json +++ b/src/Symfony/Component/Console/composer.json @@ -19,9 +19,9 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/event-dispatcher": "~2.1", - "symfony/process": "~2.1", + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/event-dispatcher": "~2.1|~3.0.0", + "symfony/process": "~2.1|~3.0.0", "psr/log": "~1.0" }, "suggest": { @@ -35,7 +35,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/CssSelector/composer.json b/src/Symfony/Component/CssSelector/composer.json index c7aebf7098550..e6d85af5ffbdc 100644 --- a/src/Symfony/Component/CssSelector/composer.json +++ b/src/Symfony/Component/CssSelector/composer.json @@ -23,7 +23,7 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7" + "symfony/phpunit-bridge": "~2.7|~3.0.0" }, "autoload": { "psr-4": { "Symfony\\Component\\CssSelector\\": "" } @@ -31,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index 2d8a9167b20b7..5af132ddc3260 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -100,6 +100,7 @@ class ErrorHandler private static $reservedMemory; private static $stackedErrors = array(); private static $stackedErrorLevels = array(); + private static $toStringException = null; /** * Same init value as thrownErrors. @@ -377,7 +378,10 @@ public function handleError($type, $message, $file, $line, array $context, array } if ($throw) { - if (($this->scopedErrors & $type) && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) { + if (null !== self::$toStringException) { + $throw = self::$toStringException; + self::$toStringException = null; + } elseif (($this->scopedErrors & $type) && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) { // Checking for class existence is a work around for https://bugs.php.net/42098 $throw = new ContextErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line, $context); } else { @@ -392,6 +396,47 @@ public function handleError($type, $message, $file, $line, array $context, array $throw->errorHandlerCanary = new ErrorHandlerCanary(); } + if (E_USER_ERROR & $type) { + $backtrace = $backtrace ?: $throw->getTrace(); + + for ($i = 1; isset($backtrace[$i]); ++$i) { + if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function']) + && '__toString' === $backtrace[$i]['function'] + && '->' === $backtrace[$i]['type'] + && !isset($backtrace[$i - 1]['class']) + && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function']) + ) { + // Here, we know trigger_error() has been called from __toString(). + // HHVM is fine with throwing from __toString() but PHP triggers a fatal error instead. + // A small convention allows working around the limitation: + // given a caught $e exception in __toString(), quitting the method with + // `return trigger_error($e, E_USER_ERROR);` allows this error handler + // to make $e get through the __toString() barrier. + + foreach ($context as $e) { + if (($e instanceof \Exception || $e instanceof \Throwable) && $e->__toString() === $message) { + if (1 === $i) { + // On HHVM + $throw = $e; + break; + } + self::$toStringException = $e; + + return true; + } + } + + if (1 < $i) { + // On PHP (not on HHVM), display the original error message instead of the default one. + $this->handleException($throw); + + // Stop the process by giving back the error to the native handler. + return false; + } + } + } + } + throw $throw; } diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php index 5d521a143478f..6555bae1be70a 100644 --- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php @@ -268,6 +268,33 @@ public function testHandleError() } } + public function testHandleUserError() + { + try { + $handler = ErrorHandler::register(); + $handler->throwAt(0, true); + + $e = null; + $x = new \Exception('Foo'); + + try { + $f = new Fixtures\ToStringThrower($x); + $f .= ''; // Trigger $f->__toString() + } catch (\Exception $e) { + } + + $this->assertSame($x, $e); + + restore_error_handler(); + restore_exception_handler(); + } catch (\Exception $e) { + restore_error_handler(); + restore_exception_handler(); + + throw $e; + } + } + public function testHandleException() { try { diff --git a/src/Symfony/Component/Debug/Tests/Fixtures/ToStringThrower.php b/src/Symfony/Component/Debug/Tests/Fixtures/ToStringThrower.php new file mode 100644 index 0000000000000..40a5fb7f8c852 --- /dev/null +++ b/src/Symfony/Component/Debug/Tests/Fixtures/ToStringThrower.php @@ -0,0 +1,24 @@ +exception = $e; + } + + public function __toString() + { + try { + throw $this->exception; + } catch (\Exception $e) { + // Using user_error() here is on purpose so we do not forget + // that this alias also should work alongside with trigger_error(). + return user_error($e, E_USER_ERROR); + } + } +} diff --git a/src/Symfony/Component/Debug/composer.json b/src/Symfony/Component/Debug/composer.json index 3b1568831177e..17c56460cb703 100644 --- a/src/Symfony/Component/Debug/composer.json +++ b/src/Symfony/Component/Debug/composer.json @@ -23,10 +23,10 @@ "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/class-loader": "~2.2", - "symfony/http-kernel": "~2.3.24|~2.5.9|~2.6,>=2.6.2", - "symfony/http-foundation": "~2.1" + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/class-loader": "~2.2|~3.0.0", + "symfony/http-kernel": "~2.3.24|~2.5.9|~2.6,>=2.6.2|~3.0.0", + "symfony/http-foundation": "~2.1|~3.0.0" }, "suggest": { "symfony/http-foundation": "", @@ -38,7 +38,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 427294319e57a..c13da0b280b3b 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -1,6 +1,14 @@ CHANGELOG ========= +2.8.0 +----- + + * allowed specifying a directory to recursively load all configuration files it contains + * deprecated the concept of scopes + * added `Definition::setShared()` and `Definition::isShared()` + * added ResettableContainerInterface to be able to reset the container to release memory on shutdown + 2.7.0 ----- diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php index 219e66313d13b..e54ee60abbcaf 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php @@ -25,6 +25,7 @@ * - non synthetic, non abstract services always have a class set * - synthetic services are always public * - synthetic services are always of non-prototype scope + * - shared services are always of non-prototype scope * * @author Johannes M. Schmitt */ @@ -46,10 +47,15 @@ public function process(ContainerBuilder $container) } // synthetic service has non-prototype scope - if ($definition->isSynthetic() && ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope()) { + if ($definition->isSynthetic() && ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope(false)) { throw new RuntimeException(sprintf('A synthetic service ("%s") cannot be of scope "prototype".', $id)); } + // shared service has non-prototype scope + if ($definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope(false)) { + throw new RuntimeException(sprintf('A shared service ("%s") cannot be of scope "prototype".', $id)); + } + if ($definition->getFactory() && ($definition->getFactoryClass(false) || $definition->getFactoryService(false) || $definition->getFactoryMethod(false))) { throw new RuntimeException(sprintf('A service ("%s") can use either the old or the new factory syntax, not both.', $id)); } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php index 3d4988d2e6653..b4526edec84b4 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php @@ -46,10 +46,10 @@ public function process(ContainerBuilder $container) { $this->container = $container; - $children = $this->container->getScopeChildren(); + $children = $this->container->getScopeChildren(false); $ancestors = array(); - $scopes = $this->container->getScopes(); + $scopes = $this->container->getScopes(false); foreach ($scopes as $name => $parent) { $ancestors[$name] = array($parent); @@ -65,7 +65,7 @@ public function process(ContainerBuilder $container) $this->currentId = $id; $this->currentDefinition = $definition; - $this->currentScope = $scope = $definition->getScope(); + $this->currentScope = $scope = $definition->getScope(false); if (ContainerInterface::SCOPE_CONTAINER === $scope) { $this->currentScopeChildren = array_keys($scopes); @@ -125,7 +125,7 @@ private function validateScope(Reference $reference, Definition $definition = nu return; } - if (!$reference->isStrict()) { + if (!$reference->isStrict(false)) { return; } @@ -133,7 +133,7 @@ private function validateScope(Reference $reference, Definition $definition = nu return; } - if ($this->currentScope === $scope = $definition->getScope()) { + if ($this->currentScope === $scope = $definition->getScope(false)) { return; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php index 026700d2263d6..1beaaf0c53ce0 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -48,27 +48,7 @@ public function process(ContainerBuilder $container) $this->formatter = $this->compiler->getLoggingFormatter(); $this->graph = $this->compiler->getServiceReferenceGraph(); - foreach ($container->getDefinitions() as $id => $definition) { - $this->currentId = $id; - - $definition->setArguments( - $this->inlineArguments($container, $definition->getArguments()) - ); - - $definition->setMethodCalls( - $this->inlineArguments($container, $definition->getMethodCalls()) - ); - - $definition->setProperties( - $this->inlineArguments($container, $definition->getProperties()) - ); - - $configurator = $this->inlineArguments($container, array($definition->getConfigurator())); - $definition->setConfigurator($configurator[0]); - - $factory = $this->inlineArguments($container, array($definition->getFactory())); - $definition->setFactory($factory[0]); - } + $container->setDefinitions($this->inlineArguments($container, $container->getDefinitions(), true)); } /** @@ -76,12 +56,16 @@ public function process(ContainerBuilder $container) * * @param ContainerBuilder $container The ContainerBuilder * @param array $arguments An array of arguments + * @param bool $isRoot If we are processing the root definitions or not * * @return array */ - private function inlineArguments(ContainerBuilder $container, array $arguments) + private function inlineArguments(ContainerBuilder $container, array $arguments, $isRoot = false) { foreach ($arguments as $k => $argument) { + if ($isRoot) { + $this->currentId = $k; + } if (is_array($argument)) { $arguments[$k] = $this->inlineArguments($container, $argument); } elseif ($argument instanceof Reference) { @@ -92,7 +76,7 @@ private function inlineArguments(ContainerBuilder $container, array $arguments) if ($this->isInlineableDefinition($container, $id, $definition = $container->getDefinition($id))) { $this->compiler->addLogMessage($this->formatter->formatInlineService($this, $id, $this->currentId)); - if (ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope()) { + if ($definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope(false)) { $arguments[$k] = $definition; } else { $arguments[$k] = clone $definition; @@ -102,6 +86,12 @@ private function inlineArguments(ContainerBuilder $container, array $arguments) $argument->setArguments($this->inlineArguments($container, $argument->getArguments())); $argument->setMethodCalls($this->inlineArguments($container, $argument->getMethodCalls())); $argument->setProperties($this->inlineArguments($container, $argument->getProperties())); + + $configurator = $this->inlineArguments($container, array($argument->getConfigurator())); + $argument->setConfigurator($configurator[0]); + + $factory = $this->inlineArguments($container, array($argument->getFactory())); + $argument->setFactory($factory[0]); } } @@ -119,7 +109,7 @@ private function inlineArguments(ContainerBuilder $container, array $arguments) */ private function isInlineableDefinition(ContainerBuilder $container, $id, Definition $definition) { - if (ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope()) { + if (!$definition->isShared() || ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope(false)) { return true; } @@ -152,6 +142,6 @@ private function isInlineableDefinition(ContainerBuilder $container, $id, Defini return false; } - return $container->getDefinition(reset($ids))->getScope() === $definition->getScope(); + return $container->getDefinition(reset($ids))->getScope(false) === $definition->getScope(false); } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php index 3fc6a1102862b..92bf97d9cffed 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php @@ -21,12 +21,13 @@ * merged Definition instance. * * @author Johannes M. Schmitt + * @author Nicolas Grekas */ class ResolveDefinitionTemplatesPass implements CompilerPassInterface { - private $container; private $compiler; private $formatter; + private $currentId; /** * Process the ContainerBuilder to replace DefinitionDecorator instances with their real Definition instances. @@ -35,44 +36,80 @@ class ResolveDefinitionTemplatesPass implements CompilerPassInterface */ public function process(ContainerBuilder $container) { - $this->container = $container; $this->compiler = $container->getCompiler(); $this->formatter = $this->compiler->getLoggingFormatter(); - foreach ($container->getDefinitions() as $id => $definition) { - // yes, we are specifically fetching the definition from the - // container to ensure we are not operating on stale data - $definition = $container->getDefinition($id); - if (!$definition instanceof DefinitionDecorator || $definition->isAbstract()) { - continue; - } + $container->setDefinitions($this->resolveArguments($container, $container->getDefinitions(), true)); + } - $this->resolveDefinition($id, $definition); + /** + * Resolves definition decorator arguments. + * + * @param ContainerBuilder $container The ContainerBuilder + * @param array $arguments An array of arguments + * @param bool $isRoot If we are processing the root definitions or not + * + * @return array + */ + private function resolveArguments(ContainerBuilder $container, array $arguments, $isRoot = false) + { + foreach ($arguments as $k => $argument) { + if ($isRoot) { + // yes, we are specifically fetching the definition from the + // container to ensure we are not operating on stale data + $arguments[$k] = $argument = $container->getDefinition($k); + $this->currentId = $k; + } + if (is_array($argument)) { + $arguments[$k] = $this->resolveArguments($container, $argument); + } elseif ($argument instanceof Definition) { + if ($argument instanceof DefinitionDecorator) { + $arguments[$k] = $argument = $this->resolveDefinition($container, $argument); + if ($isRoot) { + $container->setDefinition($k, $argument); + } + } + $argument->setArguments($this->resolveArguments($container, $argument->getArguments())); + $argument->setMethodCalls($this->resolveArguments($container, $argument->getMethodCalls())); + $argument->setProperties($this->resolveArguments($container, $argument->getProperties())); + + $configurator = $this->resolveArguments($container, array($argument->getConfigurator())); + $argument->setConfigurator($configurator[0]); + + $factory = $this->resolveArguments($container, array($argument->getFactory())); + $argument->setFactory($factory[0]); + } } + + return $arguments; } /** * Resolves the definition. * - * @param string $id The definition identifier + * @param ContainerBuilder $container The ContainerBuilder * @param DefinitionDecorator $definition * * @return Definition * * @throws \RuntimeException When the definition is invalid */ - private function resolveDefinition($id, DefinitionDecorator $definition) + private function resolveDefinition(ContainerBuilder $container, DefinitionDecorator $definition) { - if (!$this->container->hasDefinition($parent = $definition->getParent())) { - throw new RuntimeException(sprintf('The parent definition "%s" defined for definition "%s" does not exist.', $parent, $id)); + if (!$container->hasDefinition($parent = $definition->getParent())) { + throw new RuntimeException(sprintf('The parent definition "%s" defined for definition "%s" does not exist.', $parent, $this->currentId)); } - $parentDef = $this->container->getDefinition($parent); + $parentDef = $container->getDefinition($parent); if ($parentDef instanceof DefinitionDecorator) { - $parentDef = $this->resolveDefinition($parent, $parentDef); + $id = $this->currentId; + $this->currentId = $parent; + $parentDef = $this->resolveDefinition($container, $parentDef); + $container->setDefinition($parent, $parentDef); + $this->currentId = $id; } - $this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $id, $parent)); + $this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $this->currentId, $parent)); $def = new Definition(); // merge in parent definition @@ -161,12 +198,9 @@ private function resolveDefinition($id, DefinitionDecorator $definition) // these attributes are always taken from the child $def->setAbstract($definition->isAbstract()); - $def->setScope($definition->getScope()); + $def->setScope($definition->getScope(false), false); $def->setTags($definition->getTags()); - // set new definition on container - $this->container->setDefinition($id, $def); - return $def; } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php index c90d76f48adf5..3111d7f0916a3 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php @@ -68,7 +68,7 @@ private function processArguments(array $arguments) $defId = $this->getDefinitionId($id = (string) $argument); if ($defId !== $id) { - $arguments[$k] = new Reference($defId, $argument->getInvalidBehavior(), $argument->isStrict()); + $arguments[$k] = new Reference($defId, $argument->getInvalidBehavior(), $argument->isStrict(false)); } } } diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php index f0db5de6bc4e6..513fc9de0f45f 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php +++ b/src/Symfony/Component/DependencyInjection/Container.php @@ -13,6 +13,7 @@ use Symfony\Component\DependencyInjection\Exception\InactiveScopeException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; @@ -60,7 +61,7 @@ * * @api */ -class Container implements IntrospectableContainerInterface +class Container implements IntrospectableContainerInterface, ResettableContainerInterface { /** * @var ParameterBagInterface @@ -180,6 +181,8 @@ public function setParameter($name, $value) * Setting a service to null resets the service: has() returns false and get() * behaves in the same way as if the service was never created. * + * Note: The $scope parameter is deprecated since version 2.8 and will be removed in 3.0. + * * @param string $id The service identifier * @param object $service The service instance * @param string $scope The scope of the service @@ -191,6 +194,10 @@ public function setParameter($name, $value) */ public function set($id, $service, $scope = self::SCOPE_CONTAINER) { + if (!in_array($scope, array('container', 'request')) || ('request' === $scope && 'request' !== $id)) { + @trigger_error('The concept of container scopes is deprecated since version 2.8 and will be removed in 3.0. Omit the third parameter.', E_USER_DEPRECATED); + } + if (self::SCOPE_PROTOTYPE === $scope) { throw new InvalidArgumentException(sprintf('You cannot set service "%s" of scope "prototype".', $id)); } @@ -369,6 +376,18 @@ public function initialized($id) return isset($this->services[$id]) || array_key_exists($id, $this->services); } + /** + * {@inheritdoc} + */ + public function reset() + { + if (!empty($this->scopedServices)) { + throw new LogicException('Resetting the container is not allowed when a scope is active.'); + } + + $this->services = array(); + } + /** * Gets all service ids. * @@ -397,9 +416,15 @@ public function getServiceIds() * @throws InvalidArgumentException When the scope does not exist * * @api + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function enterScope($name) { + if ('request' !== $name) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + } + if (!isset($this->scopes[$name])) { throw new InvalidArgumentException(sprintf('The scope "%s" does not exist.', $name)); } @@ -445,9 +470,15 @@ public function enterScope($name) * @throws InvalidArgumentException if the scope is not active * * @api + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function leaveScope($name) { + if ('request' !== $name) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + } + if (!isset($this->scopedServices[$name])) { throw new InvalidArgumentException(sprintf('The scope "%s" is not active.', $name)); } @@ -492,12 +523,17 @@ public function leaveScope($name) * @throws InvalidArgumentException * * @api + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function addScope(ScopeInterface $scope) { $name = $scope->getName(); $parentScope = $scope->getParentName(); + if ('request' !== $name) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + } if (self::SCOPE_CONTAINER === $name || self::SCOPE_PROTOTYPE === $name) { throw new InvalidArgumentException(sprintf('The scope "%s" is reserved.', $name)); } @@ -526,9 +562,15 @@ public function addScope(ScopeInterface $scope) * @return bool * * @api + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function hasScope($name) { + if ('request' !== $name) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + } + return isset($this->scopes[$name]); } @@ -542,9 +584,13 @@ public function hasScope($name) * @return bool * * @api + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function isScopeActive($name) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + return isset($this->scopedServices[$name]); } @@ -571,4 +617,8 @@ public static function underscore($id) { return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr($id, '_', '.'))); } + + private function __clone() + { + } } diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index e697814bb10fd..5ac89cde57317 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -358,9 +358,15 @@ public function getCompiler() * @return array An array of scopes * * @api + * + * @deprecated since version 2.8, to be removed in 3.0. */ - public function getScopes() + public function getScopes($triggerDeprecationError = true) { + if ($triggerDeprecationError) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + } + return $this->scopes; } @@ -370,15 +376,23 @@ public function getScopes() * @return array An array of scope children. * * @api + * + * @deprecated since version 2.8, to be removed in 3.0. */ - public function getScopeChildren() + public function getScopeChildren($triggerDeprecationError = true) { + if ($triggerDeprecationError) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + } + return $this->scopeChildren; } /** * Sets a service. * + * Note: The $scope parameter is deprecated since version 2.8 and will be removed in 3.0. + * * @param string $id The service identifier * @param object $service The service instance * @param string $scope The scope @@ -1176,7 +1190,7 @@ private function callMethod($service, $call) */ private function shareService(Definition $definition, $service, $id) { - if (self::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { + if ($definition->isShared() && self::SCOPE_PROTOTYPE !== $scope = $definition->getScope(false)) { if (self::SCOPE_CONTAINER !== $scope && !isset($this->scopedServices[$scope])) { throw new InactiveScopeException($id, $scope); } diff --git a/src/Symfony/Component/DependencyInjection/ContainerInterface.php b/src/Symfony/Component/DependencyInjection/ContainerInterface.php index 19e800b3144da..39683a6d66751 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerInterface.php +++ b/src/Symfony/Component/DependencyInjection/ContainerInterface.php @@ -34,6 +34,8 @@ interface ContainerInterface /** * Sets a service. * + * Note: The $scope parameter is deprecated since version 2.8 and will be removed in 3.0. + * * @param string $id The service identifier * @param object $service The service instance * @param string $scope The scope of the service @@ -110,6 +112,8 @@ public function setParameter($name, $value); * @param string $name * * @api + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function enterScope($name); @@ -119,6 +123,8 @@ public function enterScope($name); * @param string $name * * @api + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function leaveScope($name); @@ -128,6 +134,8 @@ public function leaveScope($name); * @param ScopeInterface $scope * * @api + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function addScope(ScopeInterface $scope); @@ -139,6 +147,8 @@ public function addScope(ScopeInterface $scope); * @return bool * * @api + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function hasScope($name); @@ -152,6 +162,8 @@ public function hasScope($name); * @return bool * * @api + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function isScopeActive($name); } diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index 71447fab12ae8..4e99cca77ac08 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -29,6 +29,7 @@ class Definition private $factoryClass; private $factoryMethod; private $factoryService; + private $shared = true; private $scope = ContainerInterface::SCOPE_CONTAINER; private $properties = array(); private $calls = array(); @@ -94,6 +95,7 @@ public function getFactory() * @return Definition The current instance * * @api + * * @deprecated since version 2.6, to be removed in 3.0. */ public function setFactoryClass($factoryClass) @@ -111,6 +113,7 @@ public function setFactoryClass($factoryClass) * @return string|null The factory class name * * @api + * * @deprecated since version 2.6, to be removed in 3.0. */ public function getFactoryClass($triggerDeprecationError = true) @@ -130,6 +133,7 @@ public function getFactoryClass($triggerDeprecationError = true) * @return Definition The current instance * * @api + * * @deprecated since version 2.6, to be removed in 3.0. */ public function setFactoryMethod($factoryMethod) @@ -182,6 +186,7 @@ public function getDecoratedService() * @return string|null The factory method name * * @api + * * @deprecated since version 2.6, to be removed in 3.0. */ public function getFactoryMethod($triggerDeprecationError = true) @@ -201,6 +206,7 @@ public function getFactoryMethod($triggerDeprecationError = true) * @return Definition The current instance * * @api + * * @deprecated since version 2.6, to be removed in 3.0. */ public function setFactoryService($factoryService) @@ -218,6 +224,7 @@ public function setFactoryService($factoryService) * @return string|null The factory service id * * @api + * * @deprecated since version 2.6, to be removed in 3.0. */ public function getFactoryService($triggerDeprecationError = true) @@ -597,6 +604,34 @@ public function getFile() return $this->file; } + /** + * Sets if the service must be shared or not. + * + * @param bool $shared Whether the service must be shared or not + * + * @return Definition The current instance + * + * @api + */ + public function setShared($shared) + { + $this->shared = (bool) $shared; + + return $this; + } + + /** + * Whether this service is shared. + * + * @return bool + * + * @api + */ + public function isShared() + { + return $this->shared; + } + /** * Sets the scope of the service. * @@ -605,9 +640,19 @@ public function getFile() * @return Definition The current instance * * @api + * + * @deprecated since version 2.8, to be removed in 3.0. */ - public function setScope($scope) + public function setScope($scope, $triggerDeprecationError = true) { + if ($triggerDeprecationError) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + } + + if (ContainerInterface::SCOPE_PROTOTYPE === $scope) { + $this->setShared(false); + } + $this->scope = $scope; return $this; @@ -619,9 +664,15 @@ public function setScope($scope) * @return string * * @api + * + * @deprecated since version 2.8, to be removed in 3.0. */ - public function getScope() + public function getScope($triggerDeprecationError = true) { + if ($triggerDeprecationError) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + } + return $this->scope; } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php index 5f35a1e5bb116..70e19af52f55f 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php @@ -173,7 +173,7 @@ private function findNodes() } catch (ParameterNotFoundException $e) { } - $nodes[$id] = array('class' => str_replace('\\', '\\\\', $className), 'attributes' => array_merge($this->options['node.definition'], array('style' => ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope() ? 'filled' : 'dotted'))); + $nodes[$id] = array('class' => str_replace('\\', '\\\\', $className), 'attributes' => array_merge($this->options['node.definition'], array('style' => $definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope(false) ? 'filled' : 'dotted'))); $container->setDefinition($id, new Definition('stdClass')); } @@ -201,7 +201,7 @@ private function cloneContainer() $container->setDefinitions($this->container->getDefinitions()); $container->setAliases($this->container->getAliases()); $container->setResources($this->container->getResources()); - foreach ($this->container->getScopes() as $scope => $parentScope) { + foreach ($this->container->getScopes(false) as $scope => $parentScope) { $container->addScope(new Scope($scope, $parentScope)); } foreach ($this->container->getExtensions() as $extension) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index c59bc8d5b1290..40d4c31ea7c74 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -382,9 +382,9 @@ private function addServiceInstance($id, $definition) $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition); $instantiation = ''; - if (!$isProxyCandidate && ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) { + if (!$isProxyCandidate && $definition->isShared() && ContainerInterface::SCOPE_CONTAINER === $definition->getScope(false)) { $instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance'); - } elseif (!$isProxyCandidate && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { + } elseif (!$isProxyCandidate && $definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope(false)) { $instantiation = "\$this->services['$id'] = \$this->scopedServices['$scope']['$id'] = ".($simple ? '' : '$instance'); } elseif (!$simple) { $instantiation = '$instance'; @@ -578,7 +578,7 @@ private function addService($id, $definition) $return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryService(false), $definition->getFactoryMethod(false)); } - $scope = $definition->getScope(); + $scope = $definition->getScope(false); if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) { if ($return && 0 === strpos($return[count($return) - 1], '@return')) { $return[] = ''; @@ -589,7 +589,7 @@ private function addService($id, $definition) $return = implode("\n * ", $return); $doc = ''; - if (ContainerInterface::SCOPE_PROTOTYPE !== $scope) { + if ($definition->isShared() && ContainerInterface::SCOPE_PROTOTYPE !== $scope) { $doc .= <<container->getScopes()) > 0) { + if (count($scopes = $this->container->getScopes(false)) > 0) { $code .= "\n"; - $code .= ' $this->scopes = '.$this->dumpValue($scopes).";\n"; - $code .= ' $this->scopeChildren = '.$this->dumpValue($this->container->getScopeChildren()).";\n"; + $code .= " \$this->scopes = ".$this->dumpValue($scopes).";\n"; + $code .= " \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren(false)).";\n"; } $code .= $this->addMethodMap(); @@ -907,9 +907,9 @@ public function __construct() EOF; $code .= "\n"; - if (count($scopes = $this->container->getScopes()) > 0) { - $code .= ' $this->scopes = '.$this->dumpValue($scopes).";\n"; - $code .= ' $this->scopeChildren = '.$this->dumpValue($this->container->getScopeChildren()).";\n"; + if (count($scopes = $this->container->getScopes(false)) > 0) { + $code .= " \$this->scopes = ".$this->dumpValue($scopes).";\n"; + $code .= " \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren(false)).";\n"; } else { $code .= " \$this->scopes = array();\n"; $code .= " \$this->scopeChildren = array();\n"; diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index 40486c4595543..1f9183f418de8 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -126,7 +126,10 @@ private function addService($definition, $id, \DOMElement $parent) if ($definition->getFactoryService(false)) { $service->setAttribute('factory-service', $definition->getFactoryService(false)); } - if (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope()) { + if (!$definition->isShared()) { + $service->setAttribute('shared', 'false'); + } + if (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope(false)) { $service->setAttribute('scope', $scope); } if (!$definition->isPublic()) { @@ -283,7 +286,7 @@ private function convertParameters($parameters, $type, \DOMElement $parent, $key } elseif ($behaviour == ContainerInterface::IGNORE_ON_INVALID_REFERENCE) { $element->setAttribute('on-invalid', 'ignore'); } - if (!$value->isStrict()) { + if (!$value->isStrict(false)) { $element->setAttribute('strict', 'false'); } } elseif ($value instanceof Definition) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index 832929cd049c0..e1a5709dd32e2 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -128,7 +128,11 @@ private function addService($id, $definition) $code .= sprintf(" calls:\n%s\n", $this->dumper->dump($this->dumpValue($definition->getMethodCalls()), 1, 12)); } - if (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope()) { + if (!$definition->isShared()) { + $code .= " shared: false\n"; + } + + if (ContainerInterface::SCOPE_CONTAINER !== $scope = $definition->getScope(false)) { $code .= sprintf(" scope: %s\n", $scope); } @@ -212,7 +216,7 @@ private function addParameters() } /** - * Dumps callable to YAML format + * Dumps callable to YAML format. * * @param callable $callable * diff --git a/src/Symfony/Component/DependencyInjection/Loader/DirectoryLoader.php b/src/Symfony/Component/DependencyInjection/Loader/DirectoryLoader.php new file mode 100644 index 0000000000000..ffb8853011134 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Loader/DirectoryLoader.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader; + +use Symfony\Component\Config\Resource\DirectoryResource; + +/** + * DirectoryLoader is a recursive loader to go through directories. + * + * @author Sebastien Lavoie + */ +class DirectoryLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + public function load($file, $type = null) + { + $file = rtrim($file, '/'); + $path = $this->locator->locate($file); + $this->container->addResource(new DirectoryResource($path)); + + foreach (scandir($path) as $dir) { + if ('.' !== $dir[0]) { + if (is_dir($path.'/'.$dir)) { + $dir .= '/'; // append / to allow recursion + } + + $this->setCurrentDir($path); + + $this->import($dir, null, false, $path); + } + } + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + if ('directory' === $type) { + return true; + } + + return null === $type && is_string($resource) && '/' === substr($resource, -1); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index ebe330bd1b331..15b6e60366ea2 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -147,7 +147,7 @@ private function parseDefinition(\DOMElement $service, $file) $definition = new Definition(); } - foreach (array('class', 'scope', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'lazy', 'abstract') as $key) { + foreach (array('class', 'shared', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'lazy', 'abstract') as $key) { if ($value = $service->getAttribute($key)) { if (in_array($key, array('factory-class', 'factory-method', 'factory-service'))) { @trigger_error(sprintf('The "%s" attribute of service "%s" in file "%s" is deprecated since version 2.6 and will be removed in 3.0. Use the "factory" element instead.', $key, (string) $service->getAttribute('id'), $file), E_USER_DEPRECATED); @@ -157,6 +157,16 @@ private function parseDefinition(\DOMElement $service, $file) } } + if ($value = $service->getAttribute('scope')) { + $triggerDeprecation = 'request' !== (string) $service->getAttribute('id'); + + if ($triggerDeprecation) { + @trigger_error(sprintf('The "scope" attribute of service "%s" in file "%s" is deprecated since version 2.8 and will be removed in 3.0.', (string) $service->getAttribute('id'), $file), E_USER_DEPRECATED); + } + + $definition->setScope(XmlUtils::phpize($value), false); + } + if ($value = $service->getAttribute('synchronized')) { $triggerDeprecation = 'request' !== (string) $service->getAttribute('id'); diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index be2d3f130081f..3b4c5cda80768 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -163,8 +163,15 @@ private function parseDefinition($id, $service, $file) $definition->setClass($service['class']); } + if (isset($service['shared'])) { + $definition->setShared($service['shared']); + } + if (isset($service['scope'])) { - $definition->setScope($service['scope']); + if ('request' !== $id) { + @trigger_error(sprintf('The "scope" key of service "%s" in file "%s" is deprecated since version 2.8 and will be removed in 3.0.', $id, $file), E_USER_DEPRECATED); + } + $definition->setScope($service['scope'], false); } if (isset($service['synthetic'])) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index ac2cba7a80ce0..11f869c04ac22 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -87,6 +87,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Reference.php b/src/Symfony/Component/DependencyInjection/Reference.php index 88084880f80c4..6bb117abb67cf 100644 --- a/src/Symfony/Component/DependencyInjection/Reference.php +++ b/src/Symfony/Component/DependencyInjection/Reference.php @@ -27,6 +27,8 @@ class Reference /** * Constructor. * + * Note: The $strict parameter is deprecated since version 2.8 and will be removed in 3.0. + * * @param string $id The service identifier * @param int $invalidBehavior The behavior when the service does not exist * @param bool $strict Sets how this reference is validated @@ -64,9 +66,15 @@ public function getInvalidBehavior() * Returns true when this Reference is strict. * * @return bool + * + * @deprecated since version 2.8, to be removed in 3.0. */ - public function isStrict() + public function isStrict($triggerDeprecationError = true) { + if ($triggerDeprecationError) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + } + return $this->strict; } } diff --git a/src/Symfony/Component/DependencyInjection/ResettableContainerInterface.php b/src/Symfony/Component/DependencyInjection/ResettableContainerInterface.php new file mode 100644 index 0000000000000..b74e676245714 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/ResettableContainerInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +/** + * ResettableContainerInterface defines additional resetting functionality + * for containers, allowing to release shared services when the container is + * not needed anymore. + * + * @author Christophe Coevoet + */ +interface ResettableContainerInterface extends ContainerInterface +{ + /** + * Resets shared services from the container. + * + * The container is not intended to be used again after being reset in a normal workflow. This method is + * meant as a way to release references for ref-counting. + * A subsequent call to ContainerInterface::get will recreate a new instance of the shared service. + */ + public function reset(); +} diff --git a/src/Symfony/Component/DependencyInjection/Scope.php b/src/Symfony/Component/DependencyInjection/Scope.php index 161229e44bc58..c97c887584744 100644 --- a/src/Symfony/Component/DependencyInjection/Scope.php +++ b/src/Symfony/Component/DependencyInjection/Scope.php @@ -17,6 +17,8 @@ * @author Johannes M. Schmitt * * @api + * + * @deprecated since version 2.8, to be removed in 3.0. */ class Scope implements ScopeInterface { diff --git a/src/Symfony/Component/DependencyInjection/ScopeInterface.php b/src/Symfony/Component/DependencyInjection/ScopeInterface.php index 81ac67cc4d57e..69d57685a248d 100644 --- a/src/Symfony/Component/DependencyInjection/ScopeInterface.php +++ b/src/Symfony/Component/DependencyInjection/ScopeInterface.php @@ -17,6 +17,8 @@ * @author Johannes M. Schmitt * * @api + * + * @deprecated since version 2.8, to be removed in 3.0. */ interface ScopeInterface { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckDefinitionValidityPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckDefinitionValidityPassTest.php index 4e8efdc8b4fa3..e0639825f009f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckDefinitionValidityPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckDefinitionValidityPassTest.php @@ -30,6 +30,7 @@ public function testProcessDetectsSyntheticNonPublicDefinitions() /** * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @group legacy */ public function testProcessDetectsSyntheticPrototypeDefinitions() { @@ -39,6 +40,18 @@ public function testProcessDetectsSyntheticPrototypeDefinitions() $this->process($container); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @group legacy + */ + public function testProcessDetectsSharedPrototypeDefinitions() + { + $container = new ContainerBuilder(); + $container->register('a')->setShared(true)->setScope(ContainerInterface::SCOPE_PROTOTYPE); + + $this->process($container); + } + /** * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException */ diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckReferenceValidityPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckReferenceValidityPassTest.php index cd4448a96abf4..45cf279e374a8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckReferenceValidityPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckReferenceValidityPassTest.php @@ -19,6 +19,9 @@ class CheckReferenceValidityPassTest extends \PHPUnit_Framework_TestCase { + /** + * @group legacy + */ public function testProcessIgnoresScopeWideningIfNonStrictReference() { $container = new ContainerBuilder(); @@ -30,6 +33,7 @@ public function testProcessIgnoresScopeWideningIfNonStrictReference() /** * @expectedException \RuntimeException + * @group legacy */ public function testProcessDetectsScopeWidening() { @@ -40,6 +44,9 @@ public function testProcessDetectsScopeWidening() $this->process($container); } + /** + * @group legacy + */ public function testProcessIgnoresCrossScopeHierarchyReferenceIfNotStrict() { $container = new ContainerBuilder(); @@ -54,6 +61,7 @@ public function testProcessIgnoresCrossScopeHierarchyReferenceIfNotStrict() /** * @expectedException \RuntimeException + * @group legacy */ public function testProcessDetectsCrossScopeHierarchyReference() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php index 590ca4cfae2f9..038843416327c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php @@ -41,6 +41,29 @@ public function testProcess() $this->assertSame($container->getDefinition('inlinable.service'), $arguments[0]); } + public function testProcessDoesNotInlinesWhenAliasedServiceIsShared() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setPublic(false) + ; + $container->setAlias('moo', 'foo'); + + $container + ->register('service') + ->setArguments(array($ref = new Reference('foo'))) + ; + + $this->process($container); + + $arguments = $container->getDefinition('service')->getArguments(); + $this->assertSame($ref, $arguments[0]); + } + + /** + * @group legacy + */ public function testProcessDoesNotInlineWhenAliasedServiceIsNotOfPrototypeScope() { $container = new ContainerBuilder(); @@ -61,6 +84,38 @@ public function testProcessDoesNotInlineWhenAliasedServiceIsNotOfPrototypeScope( $this->assertSame($ref, $arguments[0]); } + public function testProcessDoesInlineNonSharedService() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setShared(false) + ; + $container + ->register('bar') + ->setPublic(false) + ->setShared(false) + ; + $container->setAlias('moo', 'bar'); + + $container + ->register('service') + ->setArguments(array(new Reference('foo'), $ref = new Reference('moo'), new Reference('bar'))) + ; + + $this->process($container); + + $arguments = $container->getDefinition('service')->getArguments(); + $this->assertEquals($container->getDefinition('foo'), $arguments[0]); + $this->assertNotSame($container->getDefinition('foo'), $arguments[0]); + $this->assertSame($ref, $arguments[1]); + $this->assertEquals($container->getDefinition('bar'), $arguments[2]); + $this->assertNotSame($container->getDefinition('bar'), $arguments[2]); + } + + /** + * @group legacy + */ public function testProcessDoesInlineServiceOfPrototypeScope() { $container = new ContainerBuilder(); @@ -188,6 +243,9 @@ public function testProcessDoesNotInlineReferenceWhenUsedByInlineFactory() $this->assertSame($ref, $args[0]); } + /** + * @group legacy + */ public function testProcessInlinesOnlyIfSameScope() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php index 845edd2c1419f..9f1cc87b1db1c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php @@ -79,6 +79,9 @@ public function testProcessDoesNotCopyAbstract() $this->assertFalse($def->isAbstract()); } + /** + * @group legacy + */ public function testProcessDoesNotCopyScope() { $container = new ContainerBuilder(); @@ -192,6 +195,42 @@ public function testSetLazyOnServiceIsParent() $this->assertTrue($container->getDefinition('child1')->isLazy()); } + public function testDeepDefinitionsResolving() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'parentClass'); + $container->register('sibling', 'siblingClass') + ->setConfigurator(new DefinitionDecorator('parent'), 'foo') + ->setFactory(array(new DefinitionDecorator('parent'), 'foo')) + ->addArgument(new DefinitionDecorator('parent')) + ->setProperty('prop', new DefinitionDecorator('parent')) + ->addMethodCall('meth', array(new DefinitionDecorator('parent'))) + ; + + $this->process($container); + + $configurator = $container->getDefinition('sibling')->getConfigurator(); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($configurator)); + $this->assertSame('parentClass', $configurator->getClass()); + + $factory = $container->getDefinition('sibling')->getFactory(); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($factory[0])); + $this->assertSame('parentClass', $factory[0]->getClass()); + + $argument = $container->getDefinition('sibling')->getArgument(0); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($argument)); + $this->assertSame('parentClass', $argument->getClass()); + + $properties = $container->getDefinition('sibling')->getProperties(); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($properties['prop'])); + $this->assertSame('parentClass', $properties['prop']->getClass()); + + $methodCalls = $container->getDefinition('sibling')->getMethodCalls(); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($methodCalls[0][1][0])); + $this->assertSame('parentClass', $methodCalls[0][1][0]->getClass()); + } + public function testSetDecoratedServiceOnServiceHasParent() { $container = new ContainerBuilder(); @@ -202,8 +241,6 @@ public function testSetDecoratedServiceOnServiceHasParent() ->setDecoratedService('foo', 'foo_inner') ; - $this->process($container); - $this->assertEquals(array('foo', 'foo_inner'), $container->getDefinition('child1')->getDecoratedService()); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInvalidReferencesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInvalidReferencesPassTest.php index 72058868d44ea..498baf82fc986 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInvalidReferencesPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInvalidReferencesPassTest.php @@ -61,6 +61,9 @@ public function testProcessRemovesPropertiesOnInvalid() $this->assertEquals(array(), $def->getProperties()); } + /** + * @group legacy + */ public function testStrictFlagIsPreserved() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 8155e0b7c6b87..7e5564c92b0ea 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -117,10 +117,21 @@ public function testGet() $this->assertEquals('Circular reference detected for service "baz", path: "baz".', $e->getMessage(), '->get() throws a LogicException if the service has a circular reference to itself'); } - $builder->register('foobar', 'stdClass')->setScope('container'); $this->assertTrue($builder->get('bar') === $builder->get('bar'), '->get() always returns the same instance if the service is shared'); } + /** + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::get + * @covers Symfony\Component\DependencyInjection\ContainerBuilder::setShared + */ + public function testNonSharedServicesReturnsDifferentInstances() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass')->setShared(false); + + $this->assertNotSame($builder->get('bar'), $builder->get('bar')); + } + /** * @covers \Symfony\Component\DependencyInjection\ContainerBuilder::get * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException @@ -143,6 +154,7 @@ public function testGetUnsetLoadingServiceWhenCreateServiceThrowsAnException() /** * @covers Symfony\Component\DependencyInjection\ContainerBuilder::get + * @group legacy */ public function testGetReturnsNullOnInactiveScope() { @@ -154,6 +166,7 @@ public function testGetReturnsNullOnInactiveScope() /** * @covers Symfony\Component\DependencyInjection\ContainerBuilder::get + * @group legacy */ public function testGetReturnsNullOnInactiveScopeWhenServiceIsCreatedByAMethod() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php index dd8358e929962..cffef3900802f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php @@ -171,6 +171,7 @@ public function testSetWithNullResetTheService() /** * @expectedException \InvalidArgumentException + * @group legacy */ public function testSetDoesNotAllowPrototypeScope() { @@ -180,6 +181,7 @@ public function testSetDoesNotAllowPrototypeScope() /** * @expectedException \RuntimeException + * @group legacy */ public function testSetDoesNotAllowInactiveScope() { @@ -188,6 +190,9 @@ public function testSetDoesNotAllowInactiveScope() $c->set('foo', new \stdClass(), 'foo'); } + /** + * @group legacy + */ public function testSetAlsoSetsScopedService() { $c = new Container(); @@ -200,6 +205,9 @@ public function testSetAlsoSetsScopedService() $this->assertSame($foo, $scoped['foo']['foo'], '->set() sets a scoped service'); } + /** + * @group legacy + */ public function testSetAlsoCallsSynchronizeService() { $c = new ProjectServiceContainer(); @@ -273,6 +281,7 @@ public function testGetCircularReference() /** * @covers Symfony\Component\DependencyInjection\Container::get + * @group legacy */ public function testGetReturnsNullOnInactiveScope() { @@ -311,6 +320,52 @@ public function testInitialized() $this->assertTrue($sc->initialized('alias'), '->initialized() returns true for alias if aliased service is initialized'); } + public function testReset() + { + $c = new Container(); + $c->set('bar', new \stdClass()); + + $c->reset(); + + $this->assertNull($c->get('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException + * @expectedExceptionMessage Resetting the container is not allowed when a scope is active. + * @group legacy + */ + public function testCannotResetInActiveScope() + { + $c = new Container(); + $c->addScope(new Scope('foo')); + $c->set('bar', new \stdClass()); + + $c->enterScope('foo'); + + $c->reset(); + } + + /** + * @group legacy + */ + public function testResetAfterLeavingScope() + { + $c = new Container(); + $c->addScope(new Scope('foo')); + $c->set('bar', new \stdClass()); + + $c->enterScope('foo'); + $c->leaveScope('foo'); + + $c->reset(); + + $this->assertNull($c->get('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + + /** + * @group legacy + */ public function testEnterLeaveCurrentScope() { $container = new ProjectServiceContainer(); @@ -336,6 +391,9 @@ public function testEnterLeaveCurrentScope() $this->assertSame($scopedFoo1, $scopedFoo3); } + /** + * @group legacy + */ public function testEnterLeaveScopeWithChildScopes() { $container = new Container(); @@ -366,6 +424,9 @@ public function testEnterLeaveScopeWithChildScopes() $this->assertFalse($container->has('a')); } + /** + * @group legacy + */ public function testEnterScopeRecursivelyWithInactiveChildScopes() { $container = new Container(); @@ -407,6 +468,9 @@ public function testEnterScopeRecursivelyWithInactiveChildScopes() $this->assertTrue($container->has('a')); } + /** + * @group legacy + */ public function testEnterChildScopeRecursively() { $container = new Container(); @@ -444,6 +508,7 @@ public function testEnterChildScopeRecursively() /** * @expectedException \InvalidArgumentException + * @group legacy */ public function testEnterScopeNotAdded() { @@ -453,6 +518,7 @@ public function testEnterScopeNotAdded() /** * @expectedException \RuntimeException + * @group legacy */ public function testEnterScopeDoesNotAllowInactiveParentScope() { @@ -462,6 +528,9 @@ public function testEnterScopeDoesNotAllowInactiveParentScope() $container->enterScope('bar'); } + /** + * @group legacy + */ public function testLeaveScopeNotActive() { $container = new Container(); @@ -486,7 +555,8 @@ public function testLeaveScopeNotActive() /** * @expectedException \InvalidArgumentException - * @dataProvider getBuiltInScopes + * @dataProvider getLegacyBuiltInScopes + * @group legacy */ public function testAddScopeDoesNotAllowBuiltInScopes($scope) { @@ -496,6 +566,7 @@ public function testAddScopeDoesNotAllowBuiltInScopes($scope) /** * @expectedException \InvalidArgumentException + * @group legacy */ public function testAddScopeDoesNotAllowExistingScope() { @@ -506,7 +577,8 @@ public function testAddScopeDoesNotAllowExistingScope() /** * @expectedException \InvalidArgumentException - * @dataProvider getInvalidParentScopes + * @dataProvider getLegacyInvalidParentScopes + * @group legacy */ public function testAddScopeDoesNotAllowInvalidParentScope($scope) { @@ -514,6 +586,9 @@ public function testAddScopeDoesNotAllowInvalidParentScope($scope) $c->addScope(new Scope('foo', $scope)); } + /** + * @group legacy + */ public function testAddScope() { $c = new Container(); @@ -529,6 +604,9 @@ public function testAddScope() $this->assertSame(array('foo' => array('bar', 'baz'), 'bar' => array('baz'), 'baz' => array()), $this->getField($c, 'scopeChildren')); } + /** + * @group legacy + */ public function testHasScope() { $c = new Container(); @@ -577,6 +655,9 @@ public function testGetThrowsExceptionOnServiceConfiguration() $this->assertFalse($c->initialized('throws_exception_on_service_configuration')); } + /** + * @group legacy + */ public function testIsScopeActive() { $c = new Container(); @@ -593,7 +674,7 @@ public function testIsScopeActive() $this->assertFalse($c->isScopeActive('foo')); } - public function getInvalidParentScopes() + public function getLegacyInvalidParentScopes() { return array( array(ContainerInterface::SCOPE_PROTOTYPE), @@ -601,7 +682,7 @@ public function getInvalidParentScopes() ); } - public function getBuiltInScopes() + public function getLegacyBuiltInScopes() { return array( array(ContainerInterface::SCOPE_CONTAINER), @@ -624,6 +705,16 @@ public function testAlias() $this->assertTrue($c->has('alias')); $this->assertSame($c->get('alias'), $c->get('bar')); } + + public function testThatCloningIsNotSupported() + { + $class = new \ReflectionClass('Symfony\Component\DependencyInjection\Container'); + $clone = $class->getMethod('__clone'); + if (PHP_VERSION_ID >= 540000) { + $this->assertFalse($class->isCloneable()); + } + $this->assertTrue($clone->isPrivate()); + } } class ProjectServiceContainer extends Container diff --git a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php index b501f11839017..d67f1d7224a20 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Tests; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ContainerInterface; class DefinitionTest extends \PHPUnit_Framework_TestCase { @@ -127,9 +128,34 @@ public function testSetGetFile() $this->assertEquals('foo', $def->getFile(), '->getFile() returns the file to include'); } + /** + * @covers Symfony\Component\DependencyInjection\Definition::setShared + * @covers Symfony\Component\DependencyInjection\Definition::isShared + */ + public function testSetIsShared() + { + $def = new Definition('stdClass'); + $this->assertTrue($def->isShared(), '->isShared() returns true by default'); + $this->assertSame($def, $def->setShared(false), '->setShared() implements a fluent interface'); + $this->assertFalse($def->isShared(), '->isShared() returns false if the instance must not be shared'); + } + + /** + * @group legacy + */ + public function testPrototypeScopedDefinitionAreNotShared() + { + $def = new Definition('stdClass'); + $def->setScope(ContainerInterface::SCOPE_PROTOTYPE); + + $this->assertFalse($def->isShared()); + $this->assertEquals(ContainerInterface::SCOPE_PROTOTYPE, $def->getScope()); + } + /** * @covers Symfony\Component\DependencyInjection\Definition::setScope * @covers Symfony\Component\DependencyInjection\Definition::getScope + * @group legacy */ public function testSetGetScope() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/GraphvizDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/GraphvizDumperTest.php index 5da11359fa8b5..1b75712b7952e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/GraphvizDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/GraphvizDumperTest.php @@ -81,9 +81,12 @@ public function testDumpWithUnresolvedParameter() $this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services17.dot')), $dumper->dump(), '->dump() dumps services'); } + /** + * @group legacy + */ public function testDumpWithScopes() { - $container = include self::$fixturesPath.'/containers/container18.php'; + $container = include self::$fixturesPath.'/containers/legacy-container18.php'; $dumper = new GraphvizDumper($container); $this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services18.dot')), $dumper->dump(), '->dump() dumps services'); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php index e97a2dda69ccb..d9b65a43fe261 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php @@ -28,12 +28,11 @@ $container ->register('bar', 'Bar\FooClass') ->setArguments(array('foo', new Reference('foo.baz'), new Parameter('foo_bar'))) - ->setScope('container') ->setConfigurator(array(new Reference('foo.baz'), 'configure')) ; $container ->register('foo_bar', '%foo_class%') - ->setScope('prototype') + ->setShared(false) ; $container->getParameterBag()->clear(); $container->getParameterBag()->add(array( @@ -93,7 +92,6 @@ $container ->register('new_factory', 'FactoryClass') ->setProperty('foo', 'bar') - ->setScope('container') ->setPublic(false) ; $container diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container18.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/legacy-container18.php similarity index 100% rename from src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container18.php rename to src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/legacy-container18.php diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/legacy-container9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/legacy-container9.php index 9f4210c9d5b7f..22dc44a4e4312 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/legacy-container9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/legacy-container9.php @@ -30,8 +30,13 @@ setFactoryService('foo.baz')-> setFactoryMethod('getInstance') ; +$container + ->register('foo_bar', '%foo_class%') + ->setScope('prototype') +; $container->getParameterBag()->clear(); $container->getParameterBag()->add(array( + 'foo_class' => 'Bar\FooClass', 'baz_class' => 'BazClass', 'foo' => 'bar', )); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/import/import.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/import/import.yml new file mode 100644 index 0000000000000..35ec4a0427136 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/import/import.yml @@ -0,0 +1,2 @@ +imports: + - { resource: ../recurse/ } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.ini b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.ini new file mode 100644 index 0000000000000..0984cdac770a4 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.ini @@ -0,0 +1,2 @@ +[parameters] + ini = ini diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.yml new file mode 100644 index 0000000000000..f98ef12ea3c65 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.yml @@ -0,0 +1,2 @@ +parameters: + yaml: yaml diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/simple.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/simple.php new file mode 100644 index 0000000000000..4750324ad1de3 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/simple.php @@ -0,0 +1,3 @@ +setParameter('php', 'php'); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/legacy-services9.dot b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/legacy-services9.dot index 4e8dfb977495e..ce52c6dcd01c4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/legacy-services9.dot +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/legacy-services9.dot @@ -6,6 +6,7 @@ digraph sc { node_foo [label="foo\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_foo_baz [label="foo.baz\nBazClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_factory_service [label="factory_service\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_foo_bar [label="foo_bar\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="dotted"]; node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"]; node_bar [label="bar\n\n", shape=record, fillcolor="#ff9999", style="filled"]; node_foo -> node_foo_baz [label="" style="filled"]; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/legacy-services6.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/legacy-services6.xml index 708e10fd5dcd7..c8e6e30bc9db6 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/legacy-services6.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/legacy-services6.xml @@ -6,6 +6,9 @@ + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/legacy-services9.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/legacy-services9.xml index 5692ba13ea202..dcb312a1e9f4c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/legacy-services9.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/legacy-services9.xml @@ -1,6 +1,7 @@ + Bar\FooClass BazClass bar @@ -32,5 +33,6 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml index 9eb7b8915e627..a1a320bd826d4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml @@ -6,9 +6,7 @@ - - - + %path%/foo.php diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml index c4ddc416b4636..f1e6e98efaf67 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml @@ -40,7 +40,7 @@ %foo_bar% - + %path%foo.php diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/legacy-services6.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/legacy-services6.yml index 46ac679940e13..2e702bff2f89b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/legacy-services6.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/legacy-services6.yml @@ -1,6 +1,9 @@ services: constructor: { class: FooClass, factory_method: getInstance } factory_service: { class: BazClass, factory_method: getInstance, factory_service: baz_factory } + scope.container: { class: FooClass, scope: container } + scope.custom: { class: FooClass, scope: custom } + scope.prototype: { class: FooClass, scope: prototype } request: class: Request synthetic: true diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/legacy-services9.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/legacy-services9.yml index 64d17262aa307..c7e80ea5b8c07 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/legacy-services9.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/legacy-services9.yml @@ -1,4 +1,5 @@ parameters: + foo_class: Bar\FooClass baz_class: BazClass foo: bar @@ -26,3 +27,7 @@ services: class: Bar factory_method: getInstance factory_service: foo.baz + foo_bar: + class: %foo_class% + shared: false + scope: prototype diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml index 78abf4d155521..2186620045cc9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml @@ -1,9 +1,7 @@ services: foo: { class: FooClass } baz: { class: BazClass } - scope.container: { class: FooClass, scope: container } - scope.custom: { class: FooClass, scope: custom } - scope.prototype: { class: FooClass, scope: prototype } + not_shared: { class: FooClass, shared: false } file: { class: FooClass, file: %path%/foo.php } arguments: { class: FooClass, arguments: [foo, @foo, [true, false]] } configurator1: { class: FooClass, configurator: sc_configure } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml index fdab85fc7e058..ddb5d3a96abdf 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml @@ -27,7 +27,7 @@ services: configurator: ['@foo.baz', configure] foo_bar: class: %foo_class% - scope: prototype + shared: false method_call1: class: Bar\FooClass file: %path%foo.php diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/DirectoryLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/DirectoryLoaderTest.php new file mode 100644 index 0000000000000..104976166bc83 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/DirectoryLoaderTest.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Loader; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\DependencyInjection\Loader\IniFileLoader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\Config\FileLocator; + +class DirectoryLoaderTest extends \PHPUnit_Framework_TestCase +{ + private static $fixturesPath; + + private $container; + private $loader; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); + } + + protected function setUp() + { + $locator = new FileLocator(self::$fixturesPath); + $this->container = new ContainerBuilder(); + $this->loader = new DirectoryLoader($this->container, $locator); + $resolver = new LoaderResolver(array( + new PhpFileLoader($this->container, $locator), + new IniFileLoader($this->container, $locator), + new YamlFileLoader($this->container, $locator), + $this->loader, + )); + $this->loader->setResolver($resolver); + } + + public function testDirectoryCanBeLoadedRecursively() + { + $this->loader->load('directory/'); + $this->assertEquals(array('ini' => 'ini', 'yaml' => 'yaml', 'php' => 'php'), $this->container->getParameterBag()->all(), '->load() takes a single directory'); + } + + public function testImports() + { + $this->loader->resolve('directory/import/import.yml')->load('directory/import/import.yml'); + $this->assertEquals(array('ini' => 'ini', 'yaml' => 'yaml'), $this->container->getParameterBag()->all(), '->load() takes a single file that imports a directory'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The file "foo" does not exist (in: + */ + public function testExceptionIsRaisedWhenDirectoryDoesNotExist() + { + $this->loader->load('foo/'); + } + + public function testSupports() + { + $loader = new DirectoryLoader(new ContainerBuilder(), new FileLocator()); + + $this->assertTrue($loader->supports('directory/'), '->supports("directory/") returns true'); + $this->assertTrue($loader->supports('directory/', 'directory'), '->supports("directory/", "directory") returns true'); + $this->assertFalse($loader->supports('directory'), '->supports("directory") returns false'); + $this->assertTrue($loader->supports('directory', 'directory'), '->supports("directory", "directory") returns true'); + $this->assertFalse($loader->supports('directory', 'foo'), '->supports("directory", "foo") returns false'); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 8bf524ae4f32f..581dc675fb3fd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -204,6 +204,9 @@ public function testLegacyLoadServices() $this->assertNull($services['factory_service']->getClass()); $this->assertEquals('baz_factory', $services['factory_service']->getFactoryService()); $this->assertEquals('getInstance', $services['factory_service']->getFactoryMethod()); + $this->assertEquals('container', $services['scope.container']->getScope()); + $this->assertEquals('custom', $services['scope.custom']->getScope()); + $this->assertEquals('prototype', $services['scope.prototype']->getScope()); $this->assertTrue($services['request']->isSynthetic(), '->load() parses the synthetic flag'); $this->assertTrue($services['request']->isSynchronized(), '->load() parses the synchronized flag'); $this->assertTrue($services['request']->isLazy(), '->load() parses the lazy flag'); @@ -217,11 +220,9 @@ public function testLoadServices() $loader->load('services6.xml'); $services = $container->getDefinitions(); $this->assertTrue(isset($services['foo']), '->load() parses elements'); + $this->assertFalse($services['not_shared']->isShared(), '->load() parses shared flag'); $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Definition', $services['foo'], '->load() converts element to Definition instances'); $this->assertEquals('FooClass', $services['foo']->getClass(), '->load() parses the class attribute'); - $this->assertEquals('container', $services['scope.container']->getScope()); - $this->assertEquals('custom', $services['scope.custom']->getScope()); - $this->assertEquals('prototype', $services['scope.prototype']->getScope()); $this->assertEquals('%path%/foo.php', $services['file']->getFile(), '->load() parses the file tag'); $this->assertEquals(array('foo', new Reference('foo'), array(true, false)), $services['arguments']->getArguments(), '->load() parses the argument tags'); $this->assertEquals('sc_configure', $services['configurator1']->getConfigurator(), '->load() parses the configurator tag'); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 28cf0ebf6252a..058f5b66dffd1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -133,6 +133,9 @@ public function testLegacyLoadServices() $this->assertEquals('BazClass', $services['factory_service']->getClass()); $this->assertEquals('baz_factory', $services['factory_service']->getFactoryService()); $this->assertEquals('getInstance', $services['factory_service']->getFactoryMethod()); + $this->assertEquals('container', $services['scope.container']->getScope()); + $this->assertEquals('custom', $services['scope.custom']->getScope()); + $this->assertEquals('prototype', $services['scope.prototype']->getScope()); $this->assertTrue($services['request']->isSynthetic(), '->load() parses the synthetic flag'); $this->assertTrue($services['request']->isSynchronized(), '->load() parses the synchronized flag'); $this->assertTrue($services['request']->isLazy(), '->load() parses the lazy flag'); @@ -146,11 +149,9 @@ public function testLoadServices() $loader->load('services6.yml'); $services = $container->getDefinitions(); $this->assertTrue(isset($services['foo']), '->load() parses service elements'); + $this->assertFalse($services['not_shared']->isShared(), '->load() parses the shared flag'); $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Definition', $services['foo'], '->load() converts service element to Definition instances'); $this->assertEquals('FooClass', $services['foo']->getClass(), '->load() parses the class attribute'); - $this->assertEquals('container', $services['scope.container']->getScope()); - $this->assertEquals('custom', $services['scope.custom']->getScope()); - $this->assertEquals('prototype', $services['scope.prototype']->getScope()); $this->assertEquals('%path%/foo.php', $services['file']->getFile(), '->load() parses the file tag'); $this->assertEquals(array('foo', new Reference('foo'), array(true, false)), $services['arguments']->getArguments(), '->load() parses the argument tags'); $this->assertEquals('sc_configure', $services['configurator1']->getConfigurator(), '->load() parses the configurator tag'); diff --git a/src/Symfony/Component/DependencyInjection/composer.json b/src/Symfony/Component/DependencyInjection/composer.json index bbfed43d45899..1f5d174d7dabd 100644 --- a/src/Symfony/Component/DependencyInjection/composer.json +++ b/src/Symfony/Component/DependencyInjection/composer.json @@ -19,10 +19,10 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/yaml": "~2.1", - "symfony/config": "~2.2", - "symfony/expression-language": "~2.6" + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/yaml": "~2.1|~3.0.0", + "symfony/config": "~2.2|~3.0.0", + "symfony/expression-language": "~2.6|~3.0.0" }, "conflict": { "symfony/expression-language": "<2.6" @@ -38,7 +38,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/DomCrawler/composer.json b/src/Symfony/Component/DomCrawler/composer.json index f3dc8c9bd429f..4b3b5f2df3e29 100644 --- a/src/Symfony/Component/DomCrawler/composer.json +++ b/src/Symfony/Component/DomCrawler/composer.json @@ -19,8 +19,8 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/css-selector": "~2.3" + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/css-selector": "~2.3|~3.0.0" }, "suggest": { "symfony/css-selector": "" @@ -31,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php b/src/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php index 76f0e387762a3..e71da1fb67163 100644 --- a/src/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php @@ -118,7 +118,7 @@ public function hasListeners($eventName = null) /** * @see EventDispatcherInterface::getListeners() */ - public function getListeners($eventName = null) + public function getListeners($eventName = null, $withPriorities = false) { if (null === $eventName) { foreach ($this->listenerIds as $serviceEventName => $args) { @@ -128,7 +128,7 @@ public function getListeners($eventName = null) $this->lazyLoad($eventName); } - return parent::getListeners($eventName); + return parent::getListeners($eventName, $withPriorities); } /** diff --git a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php index 7653ccfb7175f..0106f74f3a302 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php @@ -94,9 +94,9 @@ public function removeSubscriber(EventSubscriberInterface $subscriber) /** * {@inheritdoc} */ - public function getListeners($eventName = null) + public function getListeners($eventName = null, $withPriorities = false) { - return $this->dispatcher->getListeners($eventName); + return $this->dispatcher->getListeners($eventName, $withPriorities); } /** diff --git a/src/Symfony/Component/EventDispatcher/EventDispatcher.php b/src/Symfony/Component/EventDispatcher/EventDispatcher.php index 46c11100b3416..e3d587939f985 100644 --- a/src/Symfony/Component/EventDispatcher/EventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/EventDispatcher.php @@ -58,8 +58,12 @@ public function dispatch($eventName, Event $event = null) /** * @see EventDispatcherInterface::getListeners() */ - public function getListeners($eventName = null) + public function getListeners($eventName = null, $withPriorities = false) { + if (true === $withPriorities) { + return $eventName ? $this->listeners[$eventName] : array_filter($this->listeners); + } + if (null !== $eventName) { if (!isset($this->sorted[$eventName])) { $this->sortListeners($eventName); diff --git a/src/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php b/src/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php index 7ef9ece75718f..f979304e9cee2 100644 --- a/src/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php @@ -78,9 +78,9 @@ public function removeSubscriber(EventSubscriberInterface $subscriber) /** * {@inheritdoc} */ - public function getListeners($eventName = null) + public function getListeners($eventName = null, $withPriorities = false) { - return $this->dispatcher->getListeners($eventName); + return $this->dispatcher->getListeners($eventName, $withPriorities); } /** diff --git a/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php index 6f2fbcb9df9ad..18a4b3f794af8 100644 --- a/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php +++ b/src/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php @@ -92,6 +92,7 @@ public function testPreventDuplicateListenerService() /** * @expectedException \InvalidArgumentException + * @group legacy */ public function testTriggerAListenerServiceOutOfScope() { @@ -111,6 +112,9 @@ public function testTriggerAListenerServiceOutOfScope() $dispatcher->dispatch('onEvent'); } + /** + * @group legacy + */ public function testReEnteringAScope() { $event = new Event(); diff --git a/src/Symfony/Component/EventDispatcher/composer.json b/src/Symfony/Component/EventDispatcher/composer.json index d705862916250..b3d3f6e3fee82 100644 --- a/src/Symfony/Component/EventDispatcher/composer.json +++ b/src/Symfony/Component/EventDispatcher/composer.json @@ -19,11 +19,11 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/dependency-injection": "~2.6", - "symfony/expression-language": "~2.6", - "symfony/config": "~2.0,>=2.0.5", - "symfony/stopwatch": "~2.3", + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/dependency-injection": "~2.6|~3.0.0", + "symfony/expression-language": "~2.6|~3.0.0", + "symfony/config": "~2.0,>=2.0.5|~3.0.0", + "symfony/stopwatch": "~2.3|~3.0.0", "psr/log": "~1.0" }, "suggest": { @@ -36,7 +36,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/ExpressionLanguage/composer.json b/src/Symfony/Component/ExpressionLanguage/composer.json index 1eff04fa88947..125014fdbf0f9 100644 --- a/src/Symfony/Component/ExpressionLanguage/composer.json +++ b/src/Symfony/Component/ExpressionLanguage/composer.json @@ -19,7 +19,7 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7" + "symfony/phpunit-bridge": "~2.7|~3.0.0" }, "autoload": { "psr-4": { "Symfony\\Component\\ExpressionLanguage\\": "" } @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Filesystem/composer.json b/src/Symfony/Component/Filesystem/composer.json index 820d9f1bac675..2321690f97635 100644 --- a/src/Symfony/Component/Filesystem/composer.json +++ b/src/Symfony/Component/Filesystem/composer.json @@ -19,7 +19,7 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7" + "symfony/phpunit-bridge": "~2.7|~3.0.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Filesystem\\": "" } @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Finder/composer.json b/src/Symfony/Component/Finder/composer.json index 8135e158ad261..b9e363d21c0f9 100644 --- a/src/Symfony/Component/Finder/composer.json +++ b/src/Symfony/Component/Finder/composer.json @@ -19,7 +19,7 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7" + "symfony/phpunit-bridge": "~2.7|~3.0.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Finder\\": "" } @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index b1387bb2e6396..76b2da1da319d 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -1,6 +1,15 @@ CHANGELOG ========= +2.8.0 +----- + + * deprecated option "read_only" in favor of "attr['readonly']" + * added the html5 "range" FormType + * deprecated the "cascade_validation" option in favor of setting "constraints" + with the Valid constraint + * moved data trimming logic of TrimListener into StringUtil + 2.7.0 ----- diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php b/src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php index 8796acfc4d34d..7b0c3c59855f7 100644 --- a/src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php +++ b/src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php @@ -181,11 +181,16 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul /** * {@inheritdoc} */ - public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null) + public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null, $labelAttr = null) { // The input is not validated on purpose. This way, the decorated // factory may decide which input to accept and which not. +<<<<<<< HEAD + + $hash = self::generateHash(array($list, $preferredChoices, $label, $index, $groupBy, $attr, $labelAttr)); +======= $hash = self::generateHash(array($list, $preferredChoices, $label, $index, $groupBy, $attr)); +>>>>>>> my-2.8 if (!isset($this->views[$hash])) { $this->views[$hash] = $this->decoratedFactory->createView( @@ -194,7 +199,8 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, $label, $index, $groupBy, - $attr + $attr, + $labelAttr ); } diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/ChoiceListFactoryInterface.php b/src/Symfony/Component/Form/ChoiceList/Factory/ChoiceListFactoryInterface.php index 7933dd91d48d7..f069094c146d2 100644 --- a/src/Symfony/Component/Form/ChoiceList/Factory/ChoiceListFactoryInterface.php +++ b/src/Symfony/Component/Form/ChoiceList/Factory/ChoiceListFactoryInterface.php @@ -69,8 +69,12 @@ public function createListFromFlippedChoices($choices, $value = null); * argument. * * @param ChoiceLoaderInterface $loader The choice loader +<<<<<<< HEAD + * @param null|callable $value The callable generating the choice values +======= * @param null|callable $value The callable generating the choice * values +>>>>>>> my-2.8 * * @return ChoiceListInterface The choice list */ @@ -102,6 +106,20 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul * match the keys of the choices. The values should be arrays of HTML * attributes that should be added to the respective choice. * +<<<<<<< HEAD + * @param ChoiceListInterface $list The choice list + * @param null|array|callable $preferredChoices The preferred choices + * @param null|callable $label The callable generating + * the choice labels + * @param null|callable $index The callable generating + * the view indices + * @param null|array|\Traversable|callable $groupBy The callable generating + * the group names + * @param null|array|callable $attr The callable generating + * the HTML attributes + * @param null|array|callable $labelAttr The callable generating + * the HTML label attributes +======= * @param ChoiceListInterface $list The choice list * @param null|array|callable $preferredChoices The preferred choices * @param null|callable $label The callable generating the @@ -112,8 +130,9 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul * group names * @param null|array|callable $attr The callable generating the * HTML attributes +>>>>>>> my-2.8 * * @return ChoiceListView The choice list view */ - public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null); + public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null, $labelAttr = null); } diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php b/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php index 1433da2e8742e..28a8b9c4a6b5f 100644 --- a/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php +++ b/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php @@ -59,9 +59,14 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul /** * {@inheritdoc} */ - public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null) + public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null, $labelAttr = null) { // Backwards compatibility +<<<<<<< HEAD + if ($list instanceof LegacyChoiceListInterface && null === $preferredChoices + && null === $label && null === $index && null === $groupBy && null === $attr && null === $labelAttr) { + return new ChoiceListView($list->getRemainingViews(), $list->getPreferredViews()); +======= if ($list instanceof LegacyChoiceListAdapter && empty($preferredChoices) && null === $label && null === $index && null === $groupBy && null === $attr) { $mapToNonLegacyChoiceView = function (LegacyChoiceView $choiceView) { @@ -74,6 +79,7 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, array_map($mapToNonLegacyChoiceView, $adaptedList->getRemainingViews()), array_map($mapToNonLegacyChoiceView, $adaptedList->getPreferredViews()) ); +>>>>>>> my-2.8 } $preferredViews = array(); @@ -92,6 +98,29 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, $index = 0; } +<<<<<<< HEAD + // If $groupBy is not given, no grouping is done + if (empty($groupBy)) { + foreach ($choices as $key => $choice) { + self::addChoiceView( + $choice, + $key, + $label, + $values, + $index, + $attr, + $labelAttr, + $preferredChoices, + $preferredViews, + $otherViews + ); + } + + return new ChoiceListView($otherViews, $preferredViews); + } + +======= +>>>>>>> my-2.8 // If $groupBy is a callable, choices are added to the group with the // name returned by the callable. If the callable returns null, the // choice is not added to any group @@ -105,6 +134,7 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, $keys, $index, $attr, + $labelAttr, $preferredChoices, $preferredViews, $otherViews @@ -119,6 +149,7 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, $keys, $index, $attr, + $labelAttr, $preferredChoices, $preferredViews, $otherViews @@ -142,7 +173,11 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, return new ChoiceListView($otherViews, $preferredViews); } +<<<<<<< HEAD + private static function addChoiceView($choice, $key, $label, $values, &$index, $attr, $labelAttr, $isPreferred, &$preferredViews, &$otherViews) +======= private static function addChoiceView($choice, $value, $label, $keys, &$index, $attr, $isPreferred, &$preferredViews, &$otherViews) +>>>>>>> my-2.8 { // $value may be an integer or a string, since it's stored in the array // keys. We want to guarantee it's a string though. @@ -151,12 +186,17 @@ private static function addChoiceView($choice, $value, $label, $keys, &$index, $ $view = new ChoiceView( $choice, +<<<<<<< HEAD + // The attributes and labels attributes may be a callable or a mapping from choice indices +======= $value, // If the labels are null, use the original choice key by default null === $label ? (string) $key : (string) call_user_func($label, $choice, $key, $value), // The attributes may be a callable or a mapping from choice indices +>>>>>>> my-2.8 // to nested arrays - is_callable($attr) ? call_user_func($attr, $choice, $key, $value) : (isset($attr[$key]) ? $attr[$key] : array()) + is_callable($attr) ? call_user_func($attr, $choice, $key, $value) : (isset($attr[$key]) ? $attr[$key] : array()), + is_callable($labelAttr) ? call_user_func($labelAttr, $choice, $key, $value) : (isset($labelAttr[$key]) ? $labelAttr[$key] : array()) ); // $isPreferred may be null if no choices are preferred @@ -167,7 +207,11 @@ private static function addChoiceView($choice, $value, $label, $keys, &$index, $ } } +<<<<<<< HEAD + private static function addChoiceViewsGroupedBy($groupBy, $label, $choices, $values, &$index, $attr, $labelAttr, $isPreferred, &$preferredViews, &$otherViews) +======= private static function addChoiceViewsGroupedBy($groupBy, $label, $choices, $keys, &$index, $attr, $isPreferred, &$preferredViews, &$otherViews) +>>>>>>> my-2.8 { foreach ($groupBy as $key => $value) { // Add the contents of groups to new ChoiceGroupView instances @@ -182,6 +226,7 @@ private static function addChoiceViewsGroupedBy($groupBy, $label, $choices, $key $keys, $index, $attr, + $labelAttr, $isPreferred, $preferredViewsForGroup, $otherViewsForGroup @@ -206,6 +251,7 @@ private static function addChoiceViewsGroupedBy($groupBy, $label, $choices, $key $keys, $index, $attr, + $labelAttr, $isPreferred, $preferredViews, $otherViews @@ -213,7 +259,11 @@ private static function addChoiceViewsGroupedBy($groupBy, $label, $choices, $key } } +<<<<<<< HEAD + private static function addChoiceViewGroupedBy($groupBy, $choice, $key, $label, $values, &$index, $attr, $labelAttr, $isPreferred, &$preferredViews, &$otherViews) +======= private static function addChoiceViewGroupedBy($groupBy, $choice, $value, $label, $keys, &$index, $attr, $isPreferred, &$preferredViews, &$otherViews) +>>>>>>> my-2.8 { $groupLabel = call_user_func($groupBy, $choice, $keys[$value], $value); @@ -226,6 +276,7 @@ private static function addChoiceViewGroupedBy($groupBy, $choice, $value, $label $keys, $index, $attr, + $labelAttr, $isPreferred, $preferredViews, $otherViews @@ -250,6 +301,7 @@ private static function addChoiceViewGroupedBy($groupBy, $choice, $value, $label $keys, $index, $attr, + $labelAttr, $isPreferred, $preferredViews[$groupLabel]->choices, $otherViews[$groupLabel]->choices diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php b/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php index e86772fddc6e6..010e209926224 100644 --- a/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php +++ b/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php @@ -157,10 +157,11 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul * @param null|callable|string|PropertyPath $index The callable or path generating the view indices * @param null|array|\Traversable|callable|string|PropertyPath $groupBy The callable or path generating the group names * @param null|array|callable|string|PropertyPath $attr The callable or path generating the HTML attributes + * @param null|array|callable|string|PropertyPath $labelAttr The callable or path generating the HTML label attributes * * @return ChoiceListView The choice list view */ - public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null) + public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null, $labelAttr = null) { $accessor = $this->propertyAccessor; @@ -217,12 +218,22 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, $attr = new PropertyPath($attr); } + if (is_string($labelAttr)) { + $labelAttr = new PropertyPath($labelAttr); + } + if ($attr instanceof PropertyPath) { $attr = function ($choice) use ($accessor, $attr) { return $accessor->getValue($choice, $attr); }; } - return $this->decoratedFactory->createView($list, $preferredChoices, $label, $index, $groupBy, $attr); + if ($labelAttr instanceof PropertyPath) { + $labelAttr = function ($choice) use ($accessor, $labelAttr) { + return $accessor->getValue($choice, $labelAttr); + }; + } + + return $this->decoratedFactory->createView($list, $preferredChoices, $label, $index, $groupBy, $attr, $labelAttr); } } diff --git a/src/Symfony/Component/Form/ChoiceList/View/ChoiceView.php b/src/Symfony/Component/Form/ChoiceList/View/ChoiceView.php index 6e79d413cc019..5acca85b8bad5 100644 --- a/src/Symfony/Component/Form/ChoiceList/View/ChoiceView.php +++ b/src/Symfony/Component/Form/ChoiceList/View/ChoiceView.php @@ -74,19 +74,36 @@ class ChoiceView extends LegacyChoiceView * @var array */ public $attr; + /** + * Additional attributes for the HTML label tag. + * + * @var array + */ + public $labelAttr; /** * Creates a new choice view. * +<<<<<<< HEAD + * @param string $label The label displayed to humans + * @param string $value The view representation of the choice + * @param mixed $data The original choice + * @param array $attr Additional attributes for the HTML tag + * @param array $labelAttr Additional attributes for the HTML tag + */ + public function __construct($label, $value, $data, array $attr = array(), array $labelAttr = array()) +======= * @param mixed $data The original choice * @param string $value The view representation of the choice * @param string $label The label displayed to humans * @param array $attr Additional attributes for the HTML tag */ public function __construct($data, $value, $label, array $attr = array()) +>>>>>>> my-2.8 { parent::__construct($data, $value, $label); $this->attr = $attr; + $this->labelAttr = $labelAttr; } } diff --git a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php index 231994258e8d6..96e6c1961a803 100644 --- a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php +++ b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php @@ -63,6 +63,7 @@ protected function loadTypes() new Type\PasswordType(), new Type\PercentType(), new Type\RadioType(), + new Type\RangeType(), new Type\RepeatedType(), new Type\SearchType(), new Type\TextareaType(), diff --git a/src/Symfony/Component/Form/Extension/Core/EventListener/TrimListener.php b/src/Symfony/Component/Form/Extension/Core/EventListener/TrimListener.php index db291e0e5b669..260e2699f56f8 100644 --- a/src/Symfony/Component/Form/Extension/Core/EventListener/TrimListener.php +++ b/src/Symfony/Component/Form/Extension/Core/EventListener/TrimListener.php @@ -14,6 +14,7 @@ use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\Util\StringUtil; /** * Trims string data. @@ -30,11 +31,7 @@ public function preSubmit(FormEvent $event) return; } - if (null !== $result = @preg_replace('/^[\pZ\p{Cc}]+|[\pZ\p{Cc}]+$/u', '', $data)) { - $event->setData($result); - } else { - $event->setData(trim($data)); - } + $event->setData(StringUtil::trim($data)); } /** diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 386f27dbc91a0..cfc15e51f8995 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -13,7 +13,10 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator; +<<<<<<< HEAD +======= use Symfony\Component\Form\ChoiceList\LegacyChoiceListAdapter; +>>>>>>> my-2.8 use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView; use Symfony\Component\Form\ChoiceList\ChoiceListInterface; use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory; @@ -325,6 +328,7 @@ public function configureOptions(OptionsResolver $resolver) 'choice_name' => null, 'choice_value' => null, 'choice_attr' => null, + 'choice_label_attr' => null, 'preferred_choices' => array(), 'group_by' => null, 'empty_data' => $emptyData, @@ -353,6 +357,7 @@ public function configureOptions(OptionsResolver $resolver) $resolver->setAllowedTypes('choice_name', array('null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); $resolver->setAllowedTypes('choice_value', array('null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); $resolver->setAllowedTypes('choice_attr', array('null', 'array', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); + $resolver->setAllowedTypes('choice_label_attr', array('null', 'array', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); $resolver->setAllowedTypes('preferred_choices', array('array', '\Traversable', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); $resolver->setAllowedTypes('group_by', array('null', 'array', '\Traversable', 'string', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); } @@ -419,6 +424,7 @@ private function addSubForm(FormBuilderInterface $builder, $name, ChoiceView $ch 'value' => $choiceView->value, 'label' => $choiceView->label, 'attr' => $choiceView->attr, + 'label_attr' => $choiceView->labelAttr, 'translation_domain' => $options['translation_domain'], 'block_name' => 'entry', ); @@ -451,7 +457,8 @@ private function createChoiceListView(ChoiceListInterface $choiceList, array $op $options['choice_label'], $options['choice_name'], $options['group_by'], - $options['choice_attr'] + $options['choice_attr'], + $options['choice_label_attr'] ); } } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php b/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php index 599aa457b7343..32001e81bdc6a 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php @@ -29,7 +29,9 @@ public function buildForm(FormBuilderInterface $builder, array $options) if ($options['allow_add'] && $options['prototype']) { $prototype = $builder->create($options['prototype_name'], $options['type'], array_replace(array( 'label' => $options['prototype_name'].'label__', - ), $options['options'])); + ), $options['options'], array( + 'data' => $options['prototype_data'], + ))); $builder->setAttribute('prototype', $prototype->getForm()); } @@ -84,6 +86,7 @@ public function configureOptions(OptionsResolver $resolver) 'allow_add' => false, 'allow_delete' => false, 'prototype' => true, + 'prototype_data' => null, 'prototype_name' => '__name__', 'type' => 'text', 'options' => array(), diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php index 8590796bfa6e3..9d5be03bf6e59 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php @@ -71,7 +71,6 @@ public function buildView(FormView $view, FormInterface $form, array $options) parent::buildView($view, $form, $options); $name = $form->getName(); - $readOnly = $options['read_only']; if ($view->parent) { if ('' === $name) { @@ -79,13 +78,13 @@ public function buildView(FormView $view, FormInterface $form, array $options) } // Complex fields are read-only if they themselves or their parents are. - if (!$readOnly) { - $readOnly = $view->parent->vars['read_only']; + if (!isset($view->vars['attr']['readonly']) && isset($view->parent->vars['attr']['readonly']) && false !== $view->parent->vars['attr']['readonly']) { + $view->vars['attr']['readonly'] = true; } } $view->vars = array_replace($view->vars, array( - 'read_only' => $readOnly, + 'read_only' => isset($view->vars['attr']['readonly']) && false !== $view->vars['attr']['readonly'], // deprecated 'errors' => $form->getErrors(), 'valid' => $form->isSubmitted() ? $form->isValid() : true, 'value' => $form->getViewData(), @@ -185,12 +184,31 @@ public function configureOptions(OptionsResolver $resolver) return $attributes; }; + // BC for "read_only" option + $attrNormalizer = function (Options $options, array $attr) { + if (!isset($attr['readonly']) && $options['read_only']) { + $attr['readonly'] = true; + } + + return $attr; + }; + + $readOnlyNormalizer = function (Options $options, $readOnly) { + if (null !== $readOnly) { + @trigger_error('The form option "read_only" is deprecated since version 2.8 and will be removed in 3.0. Use "attr[\'readonly\']" instead.', E_USER_DEPRECATED); + + return $readOnly; + } + + return false; + }; + $resolver->setDefaults(array( 'data_class' => $dataClass, 'empty_data' => $emptyData, 'trim' => true, 'required' => true, - 'read_only' => false, + 'read_only' => null, // deprecated 'max_length' => null, 'pattern' => null, 'property_path' => null, @@ -209,6 +227,9 @@ public function configureOptions(OptionsResolver $resolver) 'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.', )); + $resolver->setNormalizer('attr', $attrNormalizer); + $resolver->setNormalizer('read_only', $readOnlyNormalizer); + $resolver->setAllowedTypes('label_attr', 'array'); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/RangeType.php b/src/Symfony/Component/Form/Extension/Core/Type/RangeType.php new file mode 100644 index 0000000000000..78909e643f5a7 --- /dev/null +++ b/src/Symfony/Component/Form/Extension/Core/Type/RangeType.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\Type; + +use Symfony\Component\Form\AbstractType; + +class RangeType extends AbstractType +{ + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'text'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'range'; + } +} diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php index 19e9dd5d7033e..25a8f3a3edace 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php +++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php @@ -13,6 +13,7 @@ use Symfony\Component\Form\FormInterface; use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Constraints\Valid; use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Exception\UnexpectedTypeException; @@ -63,6 +64,20 @@ public function validate($form, Constraint $constraint) // in the form $constraints = $config->getOption('constraints'); foreach ($constraints as $constraint) { + // For the "Valid" constraint, validate the data in all groups + if ($constraint instanceof Valid) { + if ($validator) { + $validator->atPath('data')->validate($form->getData(), $constraint, $groups); + } else { + // 2.4 API + $this->context->validateValue($form->getData(), $constraint, 'data', $groups); + } + + continue; + } + + // Otherwise validate a constraint only once for the first + // matching group foreach ($groups as $group) { if (in_array($group, $constraint->groups)) { if ($validator) { diff --git a/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php index 16030ae03ffb9..97c0d67997ece 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php +++ b/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php @@ -67,10 +67,18 @@ public function configureOptions(OptionsResolver $resolver) return is_object($constraints) ? array($constraints) : (array) $constraints; }; + $cascadeValidationNormalizer = function (Options $options, $cascadeValidation) { + if (null !== $cascadeValidation) { + @trigger_error('The "cascade_validation" option is deprecated since version 2.8 and will be removed in 3.0. Use "constraints" with a Valid constraint instead.', E_USER_DEPRECATED); + } + + return null === $cascadeValidation ? false : $cascadeValidation; + }; + $resolver->setDefaults(array( 'error_mapping' => array(), 'constraints' => array(), - 'cascade_validation' => false, + 'cascade_validation' => null, 'invalid_message' => 'This value is not valid.', 'invalid_message_parameters' => array(), 'allow_extra_fields' => false, @@ -78,6 +86,7 @@ public function configureOptions(OptionsResolver $resolver) )); $resolver->setNormalizer('constraints', $constraintsNormalizer); + $resolver->setNormalizer('cascade_validation', $cascadeValidationNormalizer); } /** diff --git a/src/Symfony/Component/Form/README.md b/src/Symfony/Component/Form/README.md index 47bcf55214721..efd6db95d885b 100644 --- a/src/Symfony/Component/Form/README.md +++ b/src/Symfony/Component/Form/README.md @@ -14,7 +14,7 @@ https://github.com/fabpot/Silex/blob/master/src/Silex/Provider/FormServiceProvid Documentation: -https://symfony.com/doc/2.7/book/forms.html +https://symfony.com/doc/2.8/book/forms.html Resources --------- diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php index 44f08b4fc12b7..c8a274ef8627d 100644 --- a/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php @@ -726,6 +726,45 @@ public function testSingleChoiceExpandedAttributes() ); } + public function testSingleChoiceExpandedLabelAttributes() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choice_label_attr' => array('Choice&B' => array('class' => 'foo&bar')), + 'multiple' => false, + 'expanded' => true, + )); + + $classPart = in_array('choice_label_attr', $this->testableFeatures) ? '[@class="foo&bar required"]' : ''; + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="radio"] + [ + ./label + [.="[trans]Choice&A[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + ] + ] + /following-sibling::div + [@class="radio"] + [ + ./label + '.$classPart.' + [.="[trans]Choice&B[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"] + ] +' + ); + } + public function testSingleChoiceExpandedWithPlaceholder() { $form = $this->factory->createNamed('name', 'choice', '&a', array( @@ -945,6 +984,55 @@ public function testMultipleChoiceExpandedAttributes() ); } + public function testMultipleChoiceExpandedLabelAttributes() + { + $form = $this->factory->createNamed('name', 'choice', array('&a', '&c'), array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B', '&c' => 'Choice&C'), + 'choice_label_attr' => array('Choice&B' => array('class' => 'foo&bar')), + 'multiple' => true, + 'expanded' => true, + 'required' => true, + )); + + $classPart = in_array('choice_label_attr', $this->testableFeatures) ? '[@class="foo&bar"]' : ''; + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="checkbox"] + [ + ./label + [.="[trans]Choice&A[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + ] + ] + /following-sibling::div + [@class="checkbox"] + [ + ./label + '.$classPart.' + [.="[trans]Choice&B[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)] + ] + ] + /following-sibling::div + [@class="checkbox"] + [ + ./label + [.="[trans]Choice&C[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"] + ] +' + ); + } + public function testCountry() { $form = $this->factory->createNamed('name', 'country', 'AT'); @@ -1447,7 +1535,10 @@ public function testHidden() ); } - public function testReadOnly() + /** + * @group legacy + */ + public function testLegacyReadOnly() { $form = $this->factory->createNamed('name', 'text', null, array( 'read_only' => true, @@ -1699,6 +1790,37 @@ public function testRadioWithValue() ); } + public function testRange() + { + $form = $this->factory->createNamed('name', 'range', 42, array('attr' => array('min' => 5))); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="range"] + [@name="name"] + [@value="42"] + [@min="5"] + [@class="my&class form-control"] +' + ); + } + + public function testRangeWithMinMaxValues() + { + $form = $this->factory->createNamed('name', 'range', 42, array('attr' => array('min' => 5, 'max' => 57))); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="range"] + [@name="name"] + [@value="42"] + [@min="5"] + [@max="57"] + [@class="my&class form-control"] +' + ); + } + public function testTextarea() { $form = $this->factory->createNamed('name', 'textarea', 'foo&bar', array( @@ -2012,14 +2134,13 @@ public function testWidgetAttributes() $form = $this->factory->createNamed('text', 'text', 'value', array( 'required' => true, 'disabled' => true, - 'read_only' => true, - 'attr' => array('maxlength' => 10, 'pattern' => '\d+', 'class' => 'foobar', 'data-foo' => 'bar'), + 'attr' => array('readonly' => true, 'maxlength' => 10, 'pattern' => '\d+', 'class' => 'foobar', 'data-foo' => 'bar'), )); $html = $this->renderWidget($form->createView()); // compare plain HTML to check the whitespace - $this->assertSame('', $html); + $this->assertSame('', $html); } public function testWidgetAttributeNameRepeatedIfTrue() diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php index e4b82063faac7..0859131de9909 100644 --- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -973,6 +973,31 @@ public function testSingleChoiceExpandedAttributes() ); } + public function testSingleChoiceExpandedLabelAttributes() + { + $form = $this->factory->createNamed('name', 'choice', '&a', array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B'), + 'choice_label_attr' => array('Choice&B' => array('class' => 'foo&bar')), + 'multiple' => false, + 'expanded' => true, + )); + + $classPart = in_array('choice_label_attr', $this->testableFeatures) ? '[@class="foo&bar required"]' : ''; + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] + /following-sibling::label[@for="name_1"]'.$classPart.'[.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=3] +' + ); + } + public function testSingleChoiceExpandedWithPlaceholder() { $form = $this->factory->createNamed('name', 'choice', '&a', array( @@ -1099,6 +1124,34 @@ public function testMultipleChoiceExpandedAttributes() ); } + public function testMultipleChoiceExpandedLabelAttributes() + { + $form = $this->factory->createNamed('name', 'choice', array('&a', '&c'), array( + 'choices' => array('&a' => 'Choice&A', '&b' => 'Choice&B', '&c' => 'Choice&C'), + 'choice_label_attr' => array('Choice&B' => array('class' => 'foo&bar')), + 'multiple' => true, + 'expanded' => true, + 'required' => true, + )); + + $classPart = in_array('choice_attr', $this->testableFeatures) ? '[@class="foo&bar"]' : ''; + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] + /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)] + /following-sibling::label[@for="name_1"]'.$classPart.'[.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] + /following-sibling::label[@for="name_2"][.="[trans]Choice&C[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=4] +' + ); + } + public function testCountry() { $form = $this->factory->createNamed('name', 'country', 'AT'); @@ -1597,7 +1650,10 @@ public function testHidden() ); } - public function testReadOnly() + /** + * @group legacy + */ + public function testLegacyReadOnly() { $form = $this->factory->createNamed('name', 'text', null, array( 'read_only' => true, @@ -1794,6 +1850,35 @@ public function testRadioWithValue() ); } + public function testRange() + { + $form = $this->factory->createNamed('name', 'range', 42, array('attr' => array('min' => 5))); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="range"] + [@name="name"] + [@value="42"] + [@min="5"] +' + ); + } + + public function testRangeWithMinMaxValues() + { + $form = $this->factory->createNamed('name', 'range', 42, array('attr' => array('min' => 5, 'max' => 57))); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="range"] + [@name="name"] + [@value="42"] + [@min="5"] + [@max="57"] +' + ); + } + public function testTextarea() { $form = $this->factory->createNamed('name', 'textarea', 'foo&bar', array( @@ -2204,14 +2289,13 @@ public function testWidgetAttributes() $form = $this->factory->createNamed('text', 'text', 'value', array( 'required' => true, 'disabled' => true, - 'read_only' => true, - 'attr' => array('maxlength' => 10, 'pattern' => '\d+', 'class' => 'foobar', 'data-foo' => 'bar'), + 'attr' => array('readonly' => true, 'maxlength' => 10, 'pattern' => '\d+', 'class' => 'foobar', 'data-foo' => 'bar'), )); $html = $this->renderWidget($form->createView()); // compare plain HTML to check the whitespace - $this->assertSame('', $html); + $this->assertSame('', $html); } public function testWidgetAttributeNameRepeatedIfTrue() diff --git a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/CachingFactoryDecoratorTest.php b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/CachingFactoryDecoratorTest.php index 9855c4a708d67..fb84b88cb0aad 100644 --- a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/CachingFactoryDecoratorTest.php +++ b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/CachingFactoryDecoratorTest.php @@ -529,73 +529,79 @@ public function testCreateViewDifferentGroupByClosure() public function testCreateViewSameAttributes() { $attr = array('class' => 'foobar'); + $labelAttr = array('class' => 'foobarbaz'); $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); $view = new \stdClass(); $this->decoratedFactory->expects($this->once()) ->method('createView') - ->with($list, null, null, null, null, $attr) + ->with($list, null, null, null, null, $attr, $labelAttr) ->will($this->returnValue($view)); - $this->assertSame($view, $this->factory->createView($list, null, null, null, null, $attr)); - $this->assertSame($view, $this->factory->createView($list, null, null, null, null, $attr)); + $this->assertSame($view, $this->factory->createView($list, null, null, null, null, $attr, $labelAttr)); + $this->assertSame($view, $this->factory->createView($list, null, null, null, null, $attr, $labelAttr)); } public function testCreateViewDifferentAttributes() { $attr1 = array('class' => 'foobar1'); $attr2 = array('class' => 'foobar2'); + $labelAttr1 = array('class' => 'label_foobar1'); + $labelAttr2 = array('class' => 'label_foobar2'); $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); $view1 = new \stdClass(); $view2 = new \stdClass(); $this->decoratedFactory->expects($this->at(0)) ->method('createView') - ->with($list, null, null, null, null, $attr1) + ->with($list, null, null, null, null, $attr1, $labelAttr1) ->will($this->returnValue($view1)); $this->decoratedFactory->expects($this->at(1)) ->method('createView') - ->with($list, null, null, null, null, $attr2) + ->with($list, null, null, null, null, $attr2, $labelAttr2) ->will($this->returnValue($view2)); - $this->assertSame($view1, $this->factory->createView($list, null, null, null, null, $attr1)); - $this->assertSame($view2, $this->factory->createView($list, null, null, null, null, $attr2)); + $this->assertSame($view1, $this->factory->createView($list, null, null, null, null, $attr1, $labelAttr1)); + $this->assertSame($view2, $this->factory->createView($list, null, null, null, null, $attr2, $labelAttr2)); } public function testCreateViewSameAttributesClosure() { $attr = function () {}; + $labelAttr = function () {}; $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); $view = new \stdClass(); $this->decoratedFactory->expects($this->once()) ->method('createView') - ->with($list, null, null, null, null, $attr) + ->with($list, null, null, null, null, $attr, $labelAttr) ->will($this->returnValue($view)); - $this->assertSame($view, $this->factory->createView($list, null, null, null, null, $attr)); - $this->assertSame($view, $this->factory->createView($list, null, null, null, null, $attr)); + $this->assertSame($view, $this->factory->createView($list, null, null, null, null, $attr, $labelAttr)); + $this->assertSame($view, $this->factory->createView($list, null, null, null, null, $attr, $labelAttr)); } public function testCreateViewDifferentAttributesClosure() { $attr1 = function () {}; $attr2 = function () {}; + $labelAttr1 = function () {}; + $labelAttr2 = function () {}; $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); $view1 = new \stdClass(); $view2 = new \stdClass(); $this->decoratedFactory->expects($this->at(0)) ->method('createView') - ->with($list, null, null, null, null, $attr1) + ->with($list, null, null, null, null, $attr1, $labelAttr1) ->will($this->returnValue($view1)); $this->decoratedFactory->expects($this->at(1)) ->method('createView') - ->with($list, null, null, null, null, $attr2) + ->with($list, null, null, null, null, $attr2, $labelAttr2) ->will($this->returnValue($view2)); - $this->assertSame($view1, $this->factory->createView($list, null, null, null, null, $attr1)); - $this->assertSame($view2, $this->factory->createView($list, null, null, null, null, $attr2)); + $this->assertSame($view1, $this->factory->createView($list, null, null, null, null, $attr1, $labelAttr1)); + $this->assertSame($view2, $this->factory->createView($list, null, null, null, null, $attr2, $labelAttr2)); } public function provideSameChoices() diff --git a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php index e8508ab06fe0b..ca8b9c7e09c3c 100644 --- a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php +++ b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php @@ -31,6 +31,10 @@ class DefaultChoiceListFactoryTest extends \PHPUnit_Framework_TestCase private $obj4; + private $obj5; + + private $obj6; + private $list; /** @@ -50,6 +54,8 @@ public function getScalarValue($choice) case 'b': return 'b'; case 'c': return '1'; case 'd': return '2'; + case 'e': return '0.1'; + case 'f': return '0.2'; } } @@ -73,6 +79,11 @@ public function getAttr($object) return $object->attr; } + public function getLabelAttr($object) + { + return $object->label_attr; + } + public function getGroup($object) { return $this->obj1 === $object || $this->obj2 === $object ? 'Group 1' : 'Group 2'; @@ -91,8 +102,10 @@ protected function setUp() $this->obj2 = (object) array('label' => 'B', 'index' => 'x', 'value' => 'b', 'preferred' => true, 'group' => 'Group 1', 'attr' => array('attr1' => 'value1')); $this->obj3 = (object) array('label' => 'C', 'index' => 'y', 'value' => 1, 'preferred' => true, 'group' => 'Group 2', 'attr' => array('attr2' => 'value2')); $this->obj4 = (object) array('label' => 'D', 'index' => 'z', 'value' => 2, 'preferred' => false, 'group' => 'Group 2', 'attr' => array()); + $this->obj5 = (object) array('label' => 'E', 'index' => 'u', 'value' => '', 'preferred' => true, 'group' => 'Group 3', 'attr' => array('label_attr2' => 'label_value2')); + $this->obj6 = (object) array('label' => 'F', 'index' => 'v', 'value' => '', 'preferred' => false, 'group' => 'Group 3', 'attr' => array('label_attr1' => 'label_value1')); $this->list = new ArrayChoiceList( - array('A' => $this->obj1, 'B' => $this->obj2, 'C' => $this->obj3, 'D' => $this->obj4) + array('A' => $this->obj1, 'B' => $this->obj2, 'C' => $this->obj3, 'D' => $this->obj4, 'E' => $this->obj5, 'F' => $this->obj6) ); $this->factory = new DefaultChoiceListFactory(); } @@ -108,7 +121,7 @@ public function testCreateFromChoicesEmpty() public function testCreateFromChoicesFlat() { $list = $this->factory->createListFromChoices( - array('A' => $this->obj1, 'B' => $this->obj2, 'C' => $this->obj3, 'D' => $this->obj4) + array('A' => $this->obj1, 'B' => $this->obj2, 'C' => $this->obj3, 'D' => $this->obj4, 'E' => $this->obj5, 'F' => $this->obj6) ); $this->assertObjectListWithGeneratedValues($list); @@ -117,7 +130,7 @@ public function testCreateFromChoicesFlat() public function testCreateFromChoicesFlatTraversable() { $list = $this->factory->createListFromChoices( - new \ArrayIterator(array('A' => $this->obj1, 'B' => $this->obj2, 'C' => $this->obj3, 'D' => $this->obj4)) + new \ArrayIterator(array('A' => $this->obj1, 'B' => $this->obj2, 'C' => $this->obj3, 'D' => $this->obj4, 'E' => $this->obj5, 'F' => $this->obj6)) ); $this->assertObjectListWithGeneratedValues($list); @@ -126,7 +139,7 @@ public function testCreateFromChoicesFlatTraversable() public function testCreateFromChoicesFlatValuesAsCallable() { $list = $this->factory->createListFromChoices( - array('A' => $this->obj1, 'B' => $this->obj2, 'C' => $this->obj3, 'D' => $this->obj4), + array('A' => $this->obj1, 'B' => $this->obj2, 'C' => $this->obj3, 'D' => $this->obj4, 'E' => $this->obj5, 'F' => $this->obj6), array($this, 'getValue') ); @@ -136,7 +149,7 @@ public function testCreateFromChoicesFlatValuesAsCallable() public function testCreateFromChoicesFlatValuesAsClosure() { $list = $this->factory->createListFromChoices( - array('A' => $this->obj1, 'B' => $this->obj2, 'C' => $this->obj3, 'D' => $this->obj4), + array('A' => $this->obj1, 'B' => $this->obj2, 'C' => $this->obj3, 'D' => $this->obj4, 'E' => $this->obj5, 'F' => $this->obj6), function ($object) { return $object->value; } ); @@ -149,6 +162,7 @@ public function testCreateFromChoicesGrouped() array( 'Group 1' => array('A' => $this->obj1, 'B' => $this->obj2), 'Group 2' => array('C' => $this->obj3, 'D' => $this->obj4), + 'Group 3' => array('E' => $this->obj5, 'F' => $this->obj6), ) ); @@ -161,6 +175,7 @@ public function testCreateFromChoicesGroupedTraversable() new \ArrayIterator(array( 'Group 1' => array('A' => $this->obj1, 'B' => $this->obj2), 'Group 2' => array('C' => $this->obj3, 'D' => $this->obj4), + 'Group 3' => array('E' => $this->obj5, 'F' => $this->obj6), )) ); @@ -173,6 +188,7 @@ public function testCreateFromChoicesGroupedValuesAsCallable() array( 'Group 1' => array('A' => $this->obj1, 'B' => $this->obj2), 'Group 2' => array('C' => $this->obj3, 'D' => $this->obj4), + 'Group 3' => array('E' => $this->obj5, 'F' => $this->obj6), ), array($this, 'getValue') ); @@ -186,6 +202,7 @@ public function testCreateFromChoicesGroupedValuesAsClosure() array( 'Group 1' => array('A' => $this->obj1, 'B' => $this->obj2), 'Group 2' => array('C' => $this->obj3, 'D' => $this->obj4), + 'Group 3' => array('E' => $this->obj5, 'F' => $this->obj6), ), function ($object) { return $object->value; } ); @@ -204,7 +221,7 @@ public function testCreateFromFlippedChoicesEmpty() public function testCreateFromFlippedChoicesFlat() { $list = $this->factory->createListFromFlippedChoices( - array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D') + array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D', 'e' => 'E', 'f' => 'F') ); $this->assertScalarListWithChoiceValues($list); @@ -213,7 +230,7 @@ public function testCreateFromFlippedChoicesFlat() public function testCreateFromFlippedChoicesFlatTraversable() { $list = $this->factory->createListFromFlippedChoices( - new \ArrayIterator(array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D')) + new \ArrayIterator(array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D', 'e' => 'E', 'f' => 'F')) ); $this->assertScalarListWithChoiceValues($list); @@ -222,7 +239,7 @@ public function testCreateFromFlippedChoicesFlatTraversable() public function testCreateFromFlippedChoicesFlatValuesAsCallable() { $list = $this->factory->createListFromFlippedChoices( - array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D'), + array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D', 'e' => 'E', 'f' => 'F'), array($this, 'getScalarValue') ); @@ -232,13 +249,15 @@ public function testCreateFromFlippedChoicesFlatValuesAsCallable() public function testCreateFromFlippedChoicesFlatValuesAsClosure() { $list = $this->factory->createListFromFlippedChoices( - array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D'), + array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D', 'e' => 'E', 'f' => 'F'), function ($choice) { switch ($choice) { case 'a': return 'a'; case 'b': return 'b'; case 'c': return '1'; case 'd': return '2'; + case 'e': return '0.1'; + case 'f': return '0.2'; } } ); @@ -252,6 +271,7 @@ public function testCreateFromFlippedChoicesGrouped() array( 'Group 1' => array('a' => 'A', 'b' => 'B'), 'Group 2' => array('c' => 'C', 'd' => 'D'), + 'Group 3' => array('e' => 'E', 'f' => 'F'), ) ); @@ -264,6 +284,7 @@ public function testCreateFromFlippedChoicesGroupedTraversable() new \ArrayIterator(array( 'Group 1' => array('a' => 'A', 'b' => 'B'), 'Group 2' => array('c' => 'C', 'd' => 'D'), + 'Group 3' => array('e' => 'E', 'f' => 'F'), )) ); @@ -276,6 +297,7 @@ public function testCreateFromFlippedChoicesGroupedValuesAsCallable() array( 'Group 1' => array('a' => 'A', 'b' => 'B'), 'Group 2' => array('c' => 'C', 'd' => 'D'), + 'Group 3' => array('e' => 'E', 'f' => 'F'), ), array($this, 'getScalarValue') ); @@ -289,6 +311,7 @@ public function testCreateFromFlippedChoicesGroupedValuesAsClosure() array( 'Group 1' => array('a' => 'A', 'b' => 'B'), 'Group 2' => array('c' => 'C', 'd' => 'D'), + 'Group 3' => array('e' => 'E', 'f' => 'F'), ), function ($choice) { switch ($choice) { @@ -296,6 +319,8 @@ function ($choice) { case 'b': return 'b'; case 'c': return '1'; case 'd': return '2'; + case 'e': return '0.1'; + case 'f': return '0.2'; } } ); @@ -328,10 +353,19 @@ public function testCreateViewFlat() $this->assertEquals(new ChoiceListView( array( +<<<<<<< HEAD + 0 => new ChoiceView('A', '0', $this->obj1), + 1 => new ChoiceView('B', '1', $this->obj2), + 2 => new ChoiceView('C', '2', $this->obj3), + 3 => new ChoiceView('D', '3', $this->obj4), + 4 => new ChoiceView('E', '4', $this->obj5), + 5 => new ChoiceView('F', '5', $this->obj6), +======= 0 => new ChoiceView($this->obj1, '0', 'A'), 1 => new ChoiceView($this->obj2, '1', 'B'), 2 => new ChoiceView($this->obj3, '2', 'C'), 3 => new ChoiceView($this->obj4, '3', 'D'), +>>>>>>> my-2.8 ), array() ), $view); } @@ -355,10 +389,19 @@ public function testCreateViewFlatPreferredChoicesEmptyArray() $this->assertEquals(new ChoiceListView( array( +<<<<<<< HEAD + 0 => new ChoiceView('A', '0', $this->obj1), + 1 => new ChoiceView('B', '1', $this->obj2), + 2 => new ChoiceView('C', '2', $this->obj3), + 3 => new ChoiceView('D', '3', $this->obj4), + 4 => new ChoiceView('E', '4', $this->obj3), + 5 => new ChoiceView('F', '5', $this->obj4), +======= 0 => new ChoiceView($this->obj1, '0', 'A'), 1 => new ChoiceView($this->obj2, '1', 'B'), 2 => new ChoiceView($this->obj3, '2', 'C'), 3 => new ChoiceView($this->obj4, '3', 'D'), +>>>>>>> my-2.8 ), array() ), $view); } @@ -460,6 +503,8 @@ function ($object, $key, $value) { case '1': return 'B'; case '2': return 'C'; case '3': return 'D'; + case '4': return 'E'; + case '5': return 'F'; } } ); @@ -505,6 +550,8 @@ function ($object, $key) { case 'B': return 'x'; case 'C': return 'y'; case 'D': return 'z'; + case 'E': return 'u'; + case 'F': return 'v'; } } ); @@ -524,6 +571,8 @@ function ($object, $key, $value) { case '1': return 'x'; case '2': return 'y'; case '3': return 'z'; + case '4': return 'u'; + case '5': return 'v'; } } ); @@ -539,8 +588,20 @@ public function testCreateViewFlatGroupByOriginalStructure() )); $view = $this->factory->createView( +<<<<<<< HEAD + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + array( + 'Group 1' => array('A' => true, 'B' => true), + 'Group 2' => array('C' => true, 'D' => true), + 'Group 3' => array('E' => true, 'F' => true), + ) +======= $list, array($this->obj2, $this->obj3) +>>>>>>> my-2.8 ); $this->assertGroupedView($view); @@ -553,7 +614,15 @@ public function testCreateViewFlatGroupByEmpty() array($this->obj2, $this->obj3), null, // label null, // index +<<<<<<< HEAD + new \ArrayIterator(array( + 'Group 1' => array('A' => true, 'B' => true), + 'Group 2' => array('C' => true, 'D' => true), + 'Group 3' => array('E' => true, 'F' => true), + )) +======= array() // ignored +>>>>>>> my-2.8 ); $this->assertFlatView($view); @@ -596,7 +665,13 @@ public function testCreateViewFlatGroupByAsClosure() null, // label null, // index function ($object) use ($obj1, $obj2) { +<<<<<<< HEAD + return $obj1 === $object || $obj2 === $object + ? 'Group 1' + : 'Group 2'; +======= return $obj1 === $object || $obj2 === $object ? 'Group 1' : 'Group 2'; +>>>>>>> my-2.8 } ); @@ -650,6 +725,24 @@ public function testCreateViewFlatAttrAsArray() $this->assertFlatViewWithAttr($view); } + public function testCreateViewFlatLabelAttrAsArray() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + null, // group + null, // attr + array( + 'E' => array('label_attr1' => 'label_value1'), + 'F' => array('label_attr2' => 'label_value2') + ) + ); + + $this->assertFlatViewWithLabelAttr($view); + } + public function testCreateViewFlatAttrEmpty() { $view = $this->factory->createView( @@ -664,6 +757,21 @@ public function testCreateViewFlatAttrEmpty() $this->assertFlatView($view); } + public function testCreateViewFlatLabelAttrEmpty() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + null, // group + null, // attr + array() + ); + + $this->assertFlatView($view); + } + public function testCreateViewFlatAttrAsCallable() { $view = $this->factory->createView( @@ -678,6 +786,21 @@ public function testCreateViewFlatAttrAsCallable() $this->assertFlatViewWithAttr($view); } + public function testCreateViewFlatLabelAttrAsCallable() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + null, // group + null, // attr + array($this, 'getLabelAttr') + ); + + $this->assertFlatViewWithLabelAttr($view); + } + public function testCreateViewFlatAttrAsClosure() { $view = $this->factory->createView( @@ -694,6 +817,23 @@ function ($object) { $this->assertFlatViewWithAttr($view); } + public function testCreateViewFlatLabelAttrAsClosure() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + null, // group + null, // attr + function ($object) { + return $object->label_attr; + } + ); + + $this->assertFlatViewWithLabelAttr($view); + } + public function testCreateViewFlatAttrClosureReceivesKey() { $view = $this->factory->createView( @@ -714,6 +854,27 @@ function ($object, $key) { $this->assertFlatViewWithAttr($view); } + public function testCreateViewFlatLabelAttrClosureReceivesKey() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + null, // group + null, // attr + function ($object, $key) { + switch ($key) { + case 'E': return array('label_attr2' => 'label_value2'); + case 'F': return array('label_attr1' => 'label_value1'); + default: return array(); + } + } + ); + + $this->assertFlatViewWithLabelAttr($view); + } + public function testCreateViewFlatAttrClosureReceivesValue() { $view = $this->factory->createView( @@ -734,9 +895,32 @@ function ($object, $key, $value) { $this->assertFlatViewWithAttr($view); } +<<<<<<< HEAD + public function testCreateViewFlatLabelAttrClosureReceivesValue() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + null, // group + function ($object, $key, $value) { + switch ($value) { + case '0.1': return array('label_attr1' => 'label_value1'); + case '0.2': return array('label_attr2' => 'label_value2'); + default: return array(); + } + } + ); + + $this->assertFlatViewWithLabelAttr($view); + } + +======= /** * @group legacy */ +>>>>>>> my-2.8 public function testCreateViewForLegacyChoiceList() { // legacy ChoiceList instances provide legacy ChoiceView objects @@ -763,6 +947,24 @@ private function assertScalarListWithChoiceValues(ChoiceListInterface $list) $this->assertSame(array('a', 'b', 'c', 'd'), $list->getValues()); $this->assertSame(array( +<<<<<<< HEAD + 'A' => 'a', + 'B' => 'b', + 'C' => 'c', + 'D' => 'd', + 'E' => 'e', + 'F' => 'f', + ), $list->getChoices()); + + $this->assertSame(array( + 'A' => 'a', + 'B' => 'b', + 'C' => 'c', + 'D' => 'd', + 'E' => 'e', + 'F' => 'f', + ), $list->getValues()); +======= 'a' => 'a', 'b' => 'b', 'c' => 'c', @@ -775,6 +977,7 @@ private function assertScalarListWithChoiceValues(ChoiceListInterface $list) 'c' => 'C', 'd' => 'D', ), $list->getOriginalKeys()); +>>>>>>> my-2.8 } private function assertObjectListWithGeneratedValues(ChoiceListInterface $list) @@ -782,6 +985,24 @@ private function assertObjectListWithGeneratedValues(ChoiceListInterface $list) $this->assertSame(array('0', '1', '2', '3'), $list->getValues()); $this->assertSame(array( +<<<<<<< HEAD + 'A' => $this->obj1, + 'B' => $this->obj2, + 'C' => $this->obj3, + 'D' => $this->obj4, + 'E' => $this->obj5, + 'F' => $this->obj6, + ), $list->getChoices()); + + $this->assertSame(array( + 'A' => '0', + 'B' => '1', + 'C' => '2', + 'D' => '3', + 'E' => '4', + 'F' => '5', + ), $list->getValues()); +======= 0 => $this->obj1, 1 => $this->obj2, 2 => $this->obj3, @@ -794,6 +1015,7 @@ private function assertObjectListWithGeneratedValues(ChoiceListInterface $list) 2 => 'C', 3 => 'D', ), $list->getOriginalKeys()); +>>>>>>> my-2.8 } private function assertScalarListWithCustomValues(ChoiceListInterface $list) @@ -801,6 +1023,24 @@ private function assertScalarListWithCustomValues(ChoiceListInterface $list) $this->assertSame(array('a', 'b', '1', '2'), $list->getValues()); $this->assertSame(array( +<<<<<<< HEAD + 'A' => 'a', + 'B' => 'b', + 'C' => 'c', + 'D' => 'd', + 'E' => 'e', + 'F' => 'f', + ), $list->getChoices()); + + $this->assertSame(array( + 'A' => 'a', + 'B' => 'b', + 'C' => '1', + 'D' => '2', + 'E' => '0.1', + 'F' => '0.2', + ), $list->getValues()); +======= 'a' => 'a', 'b' => 'b', 1 => 'c', @@ -813,6 +1053,7 @@ private function assertScalarListWithCustomValues(ChoiceListInterface $list) 1 => 'C', 2 => 'D', ), $list->getOriginalKeys()); +>>>>>>> my-2.8 } private function assertObjectListWithCustomValues(ChoiceListInterface $list) @@ -820,6 +1061,24 @@ private function assertObjectListWithCustomValues(ChoiceListInterface $list) $this->assertSame(array('a', 'b', '1', '2'), $list->getValues()); $this->assertSame(array( +<<<<<<< HEAD + 'A' => $this->obj1, + 'B' => $this->obj2, + 'C' => $this->obj3, + 'D' => $this->obj4, + 'E' => $this->obj5, + 'F' => $this->obj6, + ), $list->getChoices()); + + $this->assertSame(array( + 'A' => 'a', + 'B' => 'b', + 'C' => '1', + 'D' => '2', + 'E' => '0.1', + 'F' => '0.2', + ), $list->getValues()); +======= 'a' => $this->obj1, 'b' => $this->obj2, 1 => $this->obj3, @@ -832,6 +1091,7 @@ private function assertObjectListWithCustomValues(ChoiceListInterface $list) 1 => 'C', 2 => 'D', ), $list->getOriginalKeys()); +>>>>>>> my-2.8 } private function assertFlatView($view) @@ -841,8 +1101,16 @@ private function assertFlatView($view) 0 => new ChoiceView($this->obj1, '0', 'A'), 3 => new ChoiceView($this->obj4, '3', 'D'), ), array( +<<<<<<< HEAD + 1 => new ChoiceView('B', '1', $this->obj2), + 2 => new ChoiceView('C', '2', $this->obj3), + ), array( + 5 => new ChoiceView('E', '5', $this->obj5), + 4 => new ChoiceView('F', '4', $this->obj6), +======= 1 => new ChoiceView($this->obj2, '1', 'B'), 2 => new ChoiceView($this->obj3, '2', 'C'), +>>>>>>> my-2.8 ) ), $view); } @@ -851,11 +1119,22 @@ private function assertFlatViewWithCustomIndices($view) { $this->assertEquals(new ChoiceListView( array( +<<<<<<< HEAD + 'w' => new ChoiceView('A', '0', $this->obj1), + 'z' => new ChoiceView('D', '3', $this->obj4), + ), array( + 'x' => new ChoiceView('B', '1', $this->obj2), + 'y' => new ChoiceView('C', '2', $this->obj3), + ), array( + 'u' => new ChoiceView('E', '0.1', $this->obj4), + 'v' => new ChoiceView('F', '0.2', $this->obj5), +======= 'w' => new ChoiceView($this->obj1, '0', 'A'), 'z' => new ChoiceView($this->obj4, '3', 'D'), ), array( 'x' => new ChoiceView($this->obj2, '1', 'B'), 'y' => new ChoiceView($this->obj3, '2', 'C'), +>>>>>>> my-2.8 ) ), $view); } @@ -879,6 +1158,35 @@ private function assertFlatViewWithAttr($view) 'C', array('attr2' => 'value2') ), + ), array( + 5 => new ChoiceView('E', '5', $this->obj5), + 4 => new ChoiceView('F', '4', $this->obj6), + ) + ), $view); + } + + private function assertFlatViewWithLabelAttr($view) + { + $this->assertEquals(new ChoiceListView( + array( + 0 => new ChoiceView('A', '0', $this->obj1), + 3 => new ChoiceView('D', '3', $this->obj4), + ), array( + 1 => new ChoiceView('B', '1', $this->obj2), + 2 => new ChoiceView('C', '2', $this->obj3), + ), array( + 5 => new ChoiceView( + 'E', + '5', + $this->obj5, + array('label_attr2' => 'label_value2') + ), + 4 => new ChoiceView( + 'F', + '4', + $this->obj6, + array('label_attr1' => 'label_value1') + ), ) ), $view); } @@ -904,6 +1212,15 @@ private function assertGroupedView($view) 'Group 2', array(2 => new ChoiceView($this->obj3, '2', 'C')) ), + ), array( + 'Group 1' => new ChoiceGroupView( + 'Group 2', + array(5 => new ChoiceView('E', '5', $this->obj5)) + ), + 'Group 2' => new ChoiceGroupView( + 'Group 2', + array(4 => new ChoiceView('F', '4', $this->obj6)) + ), ) ), $view); } diff --git a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/PropertyAccessDecoratorTest.php b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/PropertyAccessDecoratorTest.php index 8697a9cac55d6..56ffbc7f95094 100644 --- a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/PropertyAccessDecoratorTest.php +++ b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/PropertyAccessDecoratorTest.php @@ -315,6 +315,28 @@ public function testCreateViewAttrAsPropertyPath() )); } + public function testCreateViewLabelAttrAsPropertyPath() + { + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, null, null, null, null, null, null, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($list, $preferred, $label, $index, $groupBy, $attr, $labelAttr) { + return $labelAttr((object) array('property' => 'label_attr')); + })); + + $this->assertSame('label_attr', $this->factory->createView( + $list, + null, // preferred choices + null, // label + null, // index + null, // groups + null, // attr + 'property' + )); + } + public function testCreateViewAttrAsPropertyPathInstance() { $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); @@ -335,4 +357,26 @@ public function testCreateViewAttrAsPropertyPathInstance() new PropertyPath('property') )); } + + public function testCreateViewLabelAttrAsPropertyPathInstance() + { + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, null, null, null, null, null, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($list, $preferred, $label, $index, $groupBy, $attr, $labelAttr) { + return $labelAttr((object) array('property' => 'label_attr')); + })); + + $this->assertSame('label_attr', $this->factory->createView( + $list, + null, // preferred choices + null, // label + null, // index + null, // groups + null, // attr + new PropertyPath('property') + )); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php index 3818c7861f234..959eb928d20eb 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php @@ -39,67 +39,4 @@ public function testTrimSkipNonStrings() $this->assertSame(1234, $event->getData()); } - - /** - * @dataProvider spaceProvider - */ - public function testTrimUtf8Separators($hex) - { - if (!function_exists('mb_convert_encoding')) { - $this->markTestSkipped('The "mb_convert_encoding" function is not available'); - } - - // Convert hexadecimal representation into binary - // H: hex string, high nibble first (UCS-2BE) - // *: repeat until end of string - $binary = pack('H*', $hex); - - // Convert UCS-2BE to UTF-8 - $symbol = mb_convert_encoding($binary, 'UTF-8', 'UCS-2BE'); - $symbol = $symbol."ab\ncd".$symbol; - - $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); - $event = new FormEvent($form, $symbol); - - $filter = new TrimListener(); - $filter->preSubmit($event); - - $this->assertSame("ab\ncd", $event->getData()); - } - - public function spaceProvider() - { - return array( - // separators - array('0020'), - array('00A0'), - array('1680'), -// array('180E'), - array('2000'), - array('2001'), - array('2002'), - array('2003'), - array('2004'), - array('2005'), - array('2006'), - array('2007'), - array('2008'), - array('2009'), - array('200A'), - array('2028'), - array('2029'), - array('202F'), - array('205F'), - array('3000'), - // controls - array('0009'), - array('000A'), - array('000B'), - array('000C'), - array('000D'), - array('0085'), - // zero width space -// array('200B'), - ); - } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php index 589ccdbb7ac21..ef00ea25bd84a 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php @@ -274,4 +274,19 @@ public function testPrototypeDefaultLabel() $this->assertSame('__test__label__', $form->createView()->vars['prototype']->vars['label']); } + + public function testPrototypeData() + { + $form = $this->factory->create('collection', array(), array( + 'type' => 'text', + 'allow_add' => true, + 'prototype' => true, + 'prototype_data' => 'foo', + 'options' => array( + 'data' => 'bar', + ), + )); + + $this->assertSame('foo', $form->createView()->vars['prototype']->vars['value']); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php index 2b4b255b0daab..0ebb554a6ac84 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php @@ -99,7 +99,10 @@ public function testSubmittedDataIsNotTrimmedBeforeTransformingIfNoTrimming() $this->assertEquals('reverse[ a ]', $form->getData()); } - public function testNonReadOnlyFormWithReadOnlyParentIsReadOnly() + /** + * @group legacy + */ + public function testLegacyNonReadOnlyFormWithReadOnlyParentIsReadOnly() { $view = $this->factory->createNamedBuilder('parent', 'form', null, array('read_only' => true)) ->add('child', 'form') @@ -109,7 +112,20 @@ public function testNonReadOnlyFormWithReadOnlyParentIsReadOnly() $this->assertTrue($view['child']->vars['read_only']); } - public function testReadOnlyFormWithNonReadOnlyParentIsReadOnly() + public function testNonReadOnlyFormWithReadOnlyParentIsReadOnly() + { + $view = $this->factory->createNamedBuilder('parent', 'form', null, array('attr' => array('readonly' => true))) + ->add('child', 'form') + ->getForm() + ->createView(); + + $this->assertTrue($view['child']->vars['attr']['readonly']); + } + + /** + * @group legacy + */ + public function testLegacyReadOnlyFormWithNonReadOnlyParentIsReadOnly() { $view = $this->factory->createNamedBuilder('parent', 'form') ->add('child', 'form', array('read_only' => true)) @@ -119,7 +135,20 @@ public function testReadOnlyFormWithNonReadOnlyParentIsReadOnly() $this->assertTrue($view['child']->vars['read_only']); } - public function testNonReadOnlyFormWithNonReadOnlyParentIsNotReadOnly() + public function testReadOnlyFormWithNonReadOnlyParentIsReadOnly() + { + $view = $this->factory->createNamedBuilder('parent', 'form') + ->add('child', 'form', array('attr' => array('readonly' => true))) + ->getForm() + ->createView(); + + $this->assertTrue($view['child']->vars['attr']['readonly']); + } + + /** + * @group legacy + */ + public function testLegacyNonReadOnlyFormWithNonReadOnlyParentIsNotReadOnly() { $view = $this->factory->createNamedBuilder('parent', 'form') ->add('child', 'form') @@ -129,6 +158,16 @@ public function testNonReadOnlyFormWithNonReadOnlyParentIsNotReadOnly() $this->assertFalse($view['child']->vars['read_only']); } + public function testNonReadOnlyFormWithNonReadOnlyParentIsNotReadOnly() + { + $view = $this->factory->createNamedBuilder('parent', 'form') + ->add('child', 'form') + ->getForm() + ->createView(); + + $this->assertArrayNotHasKey('readonly', $view['child']->vars['attr']); + } + public function testPassMaxLengthToView() { $form = $this->factory->create('form', null, array('attr' => array('maxlength' => 10))); diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php index ba40307c3a458..74d6095d17d59 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php @@ -18,9 +18,9 @@ use Symfony\Component\Form\Extension\Validator\Constraints\Form; use Symfony\Component\Form\Extension\Validator\Constraints\FormValidator; use Symfony\Component\Form\SubmitButtonBuilder; -use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Constraints\NotNull; use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\Constraints\Valid; use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest; use Symfony\Component\Validator\Validation; @@ -109,6 +109,52 @@ public function testValidateConstraints() $this->assertNoViolation(); } + public function testValidateIfParentWithCascadeValidation() + { + $object = $this->getMock('\stdClass'); + + $parent = $this->getBuilder('parent', null, array('cascade_validation' => true)) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $options = array('validation_groups' => array('group1', 'group2')); + $form = $this->getBuilder('name', '\stdClass', $options)->getForm(); + $parent->add($form); + + $form->setData($object); + + $this->expectValidateAt(0, 'data', $object, 'group1'); + $this->expectValidateAt(1, 'data', $object, 'group2'); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testValidateIfChildWithValidConstraint() + { + $object = $this->getMock('\stdClass'); + + $parent = $this->getBuilder('parent') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $options = array( + 'validation_groups' => array('group1', 'group2'), + 'constraints' => array(new Valid()), + ); + $form = $this->getBuilder('name', '\stdClass', $options)->getForm(); + $parent->add($form); + + $form->setData($object); + + $this->expectValidateAt(0, 'data', $object, array('group1', 'group2')); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + public function testDontValidateIfParentWithoutCascadeValidation() { $object = $this->getMock('\stdClass'); @@ -387,12 +433,13 @@ public function testUseValidationGroupOfClickedButton() { $object = $this->getMock('\stdClass'); - $parent = $this->getBuilder('parent', null, array('cascade_validation' => true)) + $parent = $this->getBuilder('parent') ->setCompound(true) ->setDataMapper($this->getDataMapper()) ->getForm(); $form = $this->getForm('name', '\stdClass', array( 'validation_groups' => 'form_group', + 'constraints' => array(new Valid()), )); $parent->add($form); @@ -402,7 +449,7 @@ public function testUseValidationGroupOfClickedButton() $parent->submit(array('name' => $object, 'submit' => '')); - $this->expectValidateAt(0, 'data', $object, 'button_group'); + $this->expectValidateAt(0, 'data', $object, array('button_group')); $this->validator->validate($form, new Form()); @@ -413,12 +460,13 @@ public function testDontUseValidationGroupOfUnclickedButton() { $object = $this->getMock('\stdClass'); - $parent = $this->getBuilder('parent', null, array('cascade_validation' => true)) + $parent = $this->getBuilder('parent') ->setCompound(true) ->setDataMapper($this->getDataMapper()) ->getForm(); $form = $this->getForm('name', '\stdClass', array( 'validation_groups' => 'form_group', + 'constraints' => array(new Valid()), )); $parent->add($form); @@ -428,7 +476,7 @@ public function testDontUseValidationGroupOfUnclickedButton() $form->setData($object); - $this->expectValidateAt(0, 'data', $object, 'form_group'); + $this->expectValidateAt(0, 'data', $object, array('form_group')); $this->validator->validate($form, new Form()); @@ -439,20 +487,18 @@ public function testUseInheritedValidationGroup() { $object = $this->getMock('\stdClass'); - $parentOptions = array( - 'validation_groups' => 'group', - 'cascade_validation' => true, - ); + $parentOptions = array('validation_groups' => 'group'); $parent = $this->getBuilder('parent', null, $parentOptions) ->setCompound(true) ->setDataMapper($this->getDataMapper()) ->getForm(); - $form = $this->getBuilder('name', '\stdClass')->getForm(); + $formOptions = array('constraints' => array(new Valid())); + $form = $this->getBuilder('name', '\stdClass', $formOptions)->getForm(); $parent->add($form); $form->setData($object); - $this->expectValidateAt(0, 'data', $object, 'group'); + $this->expectValidateAt(0, 'data', $object, array('group')); $this->validator->validate($form, new Form()); @@ -463,21 +509,18 @@ public function testUseInheritedCallbackValidationGroup() { $object = $this->getMock('\stdClass'); - $parentOptions = array( - 'validation_groups' => array($this, 'getValidationGroups'), - 'cascade_validation' => true, - ); + $parentOptions = array('validation_groups' => array($this, 'getValidationGroups')); $parent = $this->getBuilder('parent', null, $parentOptions) ->setCompound(true) ->setDataMapper($this->getDataMapper()) ->getForm(); - $form = $this->getBuilder('name', '\stdClass')->getForm(); + $formOptions = array('constraints' => array(new Valid())); + $form = $this->getBuilder('name', '\stdClass', $formOptions)->getForm(); $parent->add($form); $form->setData($object); - $this->expectValidateAt(0, 'data', $object, 'group1'); - $this->expectValidateAt(1, 'data', $object, 'group2'); + $this->expectValidateAt(0, 'data', $object, array('group1', 'group2')); $this->validator->validate($form, new Form()); @@ -492,19 +535,18 @@ public function testUseInheritedClosureValidationGroup() 'validation_groups' => function (FormInterface $form) { return array('group1', 'group2'); }, - 'cascade_validation' => true, ); $parent = $this->getBuilder('parent', null, $parentOptions) ->setCompound(true) ->setDataMapper($this->getDataMapper()) ->getForm(); - $form = $this->getBuilder('name', '\stdClass')->getForm(); + $formOptions = array('constraints' => array(new Valid())); + $form = $this->getBuilder('name', '\stdClass', $formOptions)->getForm(); $parent->add($form); $form->setData($object); - $this->expectValidateAt(0, 'data', $object, 'group1'); - $this->expectValidateAt(1, 'data', $object, 'group2'); + $this->expectValidateAt(0, 'data', $object, array('group1', 'group2')); $this->validator->validate($form, new Form()); diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php index 4ca10da50e88d..f24f8404418c2 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Form\Tests\Extension\Validator\Type; use Symfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension; +use Symfony\Component\Validator\Constraints\Valid; use Symfony\Component\Validator\ConstraintViolationList; class FormTypeValidatorExtensionTest extends BaseValidatorExtensionTest @@ -37,6 +38,33 @@ public function testSubmitValidatesData() $form->submit(array()); } + public function testValidConstraint() + { + $form = $this->createForm(array('constraints' => $valid = new Valid())); + + $this->assertSame(array($valid), $form->getConfig()->getOption('constraints')); + } + + /** + * @group legacy + */ + public function testCascadeValidationCanBeSetToTrue() + { + $form = $this->createForm(array('cascade_validation' => true)); + + $this->assertTrue($form->getConfig()->getOption('cascade_validation')); + } + + /** + * @group legacy + */ + public function testCascadeValidationCanBeSetToFalse() + { + $form = $this->createForm(array('cascade_validation' => false)); + + $this->assertFalse($form->getConfig()->getOption('cascade_validation')); + } + public function testValidatorInterfaceSinceSymfony25() { // Mock of ValidatorInterface since apiVersion 2.5 diff --git a/src/Symfony/Component/Form/Tests/Util/StringUtilTest.php b/src/Symfony/Component/Form/Tests/Util/StringUtilTest.php new file mode 100644 index 0000000000000..7836852100b35 --- /dev/null +++ b/src/Symfony/Component/Form/Tests/Util/StringUtilTest.php @@ -0,0 +1,72 @@ +assertEquals('Foo!', StringUtil::trim($data)); + } + + /** + * @dataProvider spaceProvider + */ + public function testTrimUtf8Separators($hex) + { + if (!function_exists('mb_convert_encoding')) { + $this->markTestSkipped('The "mb_convert_encoding" function is not available'); + } + + // Convert hexadecimal representation into binary + // H: hex string, high nibble first (UCS-2BE) + // *: repeat until end of string + $binary = pack('H*', $hex); + + // Convert UCS-2BE to UTF-8 + $symbol = mb_convert_encoding($binary, 'UTF-8', 'UCS-2BE'); + $symbol = $symbol."ab\ncd".$symbol; + + $this->assertSame("ab\ncd", StringUtil::trim($symbol)); + } + + public function spaceProvider() + { + return array( + // separators + array('0020'), + array('00A0'), + array('1680'), +// array('180E'), + array('2000'), + array('2001'), + array('2002'), + array('2003'), + array('2004'), + array('2005'), + array('2006'), + array('2007'), + array('2008'), + array('2009'), + array('200A'), + array('2028'), + array('2029'), + array('202F'), + array('205F'), + array('3000'), + // controls + array('0009'), + array('000A'), + array('000B'), + array('000C'), + array('000D'), + array('0085'), + // zero width space +// array('200B'), + ); + } +} diff --git a/src/Symfony/Component/Form/Util/StringUtil.php b/src/Symfony/Component/Form/Util/StringUtil.php new file mode 100644 index 0000000000000..2f51e74afcaac --- /dev/null +++ b/src/Symfony/Component/Form/Util/StringUtil.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Util; + +/** + * @author Issei Murasawa + * @author Bernhard Schussek + */ +class StringUtil +{ + /** + * This class should not be instantiated. + */ + private function __construct() + { + } + + /** + * Returns the trimmed data. + * + * @param string $string + * + * @return string + */ + public static function trim($string) + { + if (null !== $result = @preg_replace('/^[\pZ\p{Cc}]+|[\pZ\p{Cc}]+$/u', '', $string)) { + return $result; + } + + return trim($string); + } +} diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json index 21c6af679971e..37c1d933044f5 100644 --- a/src/Symfony/Component/Form/composer.json +++ b/src/Symfony/Component/Form/composer.json @@ -17,19 +17,19 @@ ], "require": { "php": ">=5.3.9", - "symfony/event-dispatcher": "~2.1", - "symfony/intl": "~2.3", + "symfony/event-dispatcher": "~2.1|~3.0.0", + "symfony/intl": "~2.3|~3.0.0", "symfony/options-resolver": "~2.6", - "symfony/property-access": "~2.3" + "symfony/property-access": "~2.3|~3.0.0" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", + "symfony/phpunit-bridge": "~2.7|~3.0.0", "doctrine/collections": "~1.0", - "symfony/validator": "~2.6,>=2.6.8", - "symfony/http-foundation": "~2.2", - "symfony/http-kernel": "~2.4", - "symfony/security-csrf": "~2.4", - "symfony/translation": "~2.0,>=2.0.5" + "symfony/validator": "~2.8|~3.0.0", + "symfony/http-foundation": "~2.2|~3.0.0", + "symfony/http-kernel": "~2.4|~3.0.0", + "symfony/security-csrf": "~2.4|~3.0.0", + "symfony/translation": "~2.0,>=2.0.5|~3.0.0" }, "conflict": { "symfony/doctrine-bridge": "<2.7", @@ -48,7 +48,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index 17fb98102bfa8..9f8c7ef29279c 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -202,9 +202,6 @@ public function __construct($content = '', $status = 200, $headers = array()) $this->setContent($content); $this->setStatusCode($status); $this->setProtocolVersion('1.0'); - if (!$this->headers->has('Date')) { - $this->setDate(new \DateTime(null, new \DateTimeZone('UTC'))); - } } /** @@ -333,6 +330,10 @@ public function sendHeaders() return $this; } + if (!$this->headers->has('Date')) { + $this->setDate(new \DateTime()); + } + // status header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode); @@ -644,7 +645,11 @@ public function mustRevalidate() */ public function getDate() { - return $this->headers->getDate('Date', new \DateTime()); + if (!$this->headers->has('Date')) { + $this->setDate(new \DateTime()); + } + + return $this->headers->getDate('Date'); } /** diff --git a/src/Symfony/Component/HttpFoundation/composer.json b/src/Symfony/Component/HttpFoundation/composer.json index 80f4436aca5a2..5670c17f8725d 100644 --- a/src/Symfony/Component/HttpFoundation/composer.json +++ b/src/Symfony/Component/HttpFoundation/composer.json @@ -19,8 +19,8 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/expression-language": "~2.4" + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/expression-language": "~2.4|~3.0.0" }, "autoload": { "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" }, @@ -29,7 +29,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index b9e6a52c13889..d70439bd0822c 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -21,7 +21,9 @@ use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; use Symfony\Component\DependencyInjection\Loader\IniFileLoader; 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; @@ -60,11 +62,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.7.3-DEV'; - const VERSION_ID = '20703'; + const VERSION = '2.8.0-DEV'; + const VERSION_ID = '20800'; const MAJOR_VERSION = '2'; - const MINOR_VERSION = '7'; - const RELEASE_VERSION = '3'; + const MINOR_VERSION = '8'; + const RELEASE_VERSION = '0'; const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '05/2018'; @@ -179,6 +181,10 @@ public function shutdown() $bundle->setContainer(null); } + if ($this->container instanceof ResettableContainerInterface) { + $this->container->reset(); + } + $this->container = null; } @@ -721,6 +727,7 @@ protected function getContainerLoader(ContainerInterface $container) new YamlFileLoader($container, $locator), new IniFileLoader($container, $locator), new PhpFileLoader($container, $locator), + new DirectoryLoader($container, $locator), new ClosureLoader($container), )); diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index a63b6b3f48545..d702fac80863f 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -17,28 +17,28 @@ ], "require": { "php": ">=5.3.9", - "symfony/event-dispatcher": "~2.6,>=2.6.7", - "symfony/http-foundation": "~2.5,>=2.5.4", + "symfony/event-dispatcher": "~2.6,>=2.6.7|~3.0.0", + "symfony/http-foundation": "~2.5,>=2.5.4|~3.0.0", "symfony/debug": "~2.6,>=2.6.2", "psr/log": "~1.0" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/browser-kit": "~2.3", - "symfony/class-loader": "~2.1", + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/browser-kit": "~2.3|~3.0.0", + "symfony/class-loader": "~2.1|~3.0.0", "symfony/config": "~2.7", - "symfony/console": "~2.3", - "symfony/css-selector": "~2.0,>=2.0.5", - "symfony/dependency-injection": "~2.2", - "symfony/dom-crawler": "~2.0,>=2.0.5", - "symfony/expression-language": "~2.4", - "symfony/finder": "~2.0,>=2.0.5", - "symfony/process": "~2.0,>=2.0.5", - "symfony/routing": "~2.2", - "symfony/stopwatch": "~2.3", - "symfony/templating": "~2.2", - "symfony/translation": "~2.0,>=2.0.5", - "symfony/var-dumper": "~2.6" + "symfony/console": "~2.3|~3.0.0", + "symfony/css-selector": "~2.0,>=2.0.5|~3.0.0", + "symfony/dependency-injection": "~2.8|~3.0.0", + "symfony/dom-crawler": "~2.0,>=2.0.5|~3.0.0", + "symfony/expression-language": "~2.4|~3.0.0", + "symfony/finder": "~2.0,>=2.0.5|~3.0.0", + "symfony/process": "~2.0,>=2.0.5|~3.0.0", + "symfony/routing": "~2.8|~3.0.0", + "symfony/stopwatch": "~2.3|~3.0.0", + "symfony/templating": "~2.2|~3.0.0", + "symfony/translation": "~2.0,>=2.0.5|~3.0.0", + "symfony/var-dumper": "~2.6|~3.0.0" }, "conflict": { "symfony/config": "<2.7" @@ -58,7 +58,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Intl/README.md b/src/Symfony/Component/Intl/README.md index 11bc73130c673..7d8d9eb871a66 100644 --- a/src/Symfony/Component/Intl/README.md +++ b/src/Symfony/Component/Intl/README.md @@ -22,4 +22,4 @@ You can run the unit tests with the following command: $ phpunit [0]: http://www.php.net/manual/en/intl.setup.php -[1]: https://symfony.com/doc/2.7/components/intl.html +[1]: https://symfony.com/doc/2.8/components/intl.html diff --git a/src/Symfony/Component/Intl/composer.json b/src/Symfony/Component/Intl/composer.json index 2702a314a4dda..142956485c688 100644 --- a/src/Symfony/Component/Intl/composer.json +++ b/src/Symfony/Component/Intl/composer.json @@ -27,8 +27,8 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/filesystem": "~2.1" + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/filesystem": "~2.1|~3.0.0" }, "suggest": { "ext-intl": "to use the component with locales other than \"en\"" @@ -41,7 +41,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Locale/composer.json b/src/Symfony/Component/Locale/composer.json index 6aafb8cd0dd2f..d3f9466bc8720 100644 --- a/src/Symfony/Component/Locale/composer.json +++ b/src/Symfony/Component/Locale/composer.json @@ -17,10 +17,10 @@ ], "require": { "php": ">=5.3.9", - "symfony/intl": "~2.3" + "symfony/intl": "~2.3|~3.0.0" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7" + "symfony/phpunit-bridge": "~2.7|~3.0.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Locale\\": "" } @@ -28,7 +28,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/OptionsResolver/composer.json b/src/Symfony/Component/OptionsResolver/composer.json index 3460fae0e2429..312d895dc1788 100644 --- a/src/Symfony/Component/OptionsResolver/composer.json +++ b/src/Symfony/Component/OptionsResolver/composer.json @@ -19,7 +19,7 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7" + "symfony/phpunit-bridge": "~2.7|~3.0.0" }, "autoload": { "psr-4": { "Symfony\\Component\\OptionsResolver\\": "" } @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Process/composer.json b/src/Symfony/Component/Process/composer.json index d0cae4d8e0494..52ae4cf37824a 100644 --- a/src/Symfony/Component/Process/composer.json +++ b/src/Symfony/Component/Process/composer.json @@ -19,7 +19,7 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7" + "symfony/phpunit-bridge": "~2.7|~3.0.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Process\\": "" } @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/PropertyAccess/composer.json b/src/Symfony/Component/PropertyAccess/composer.json index 5058e867a477c..c2f136ef0f62b 100644 --- a/src/Symfony/Component/PropertyAccess/composer.json +++ b/src/Symfony/Component/PropertyAccess/composer.json @@ -19,7 +19,7 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7" + "symfony/phpunit-bridge": "~2.7|~3.0.0" }, "autoload": { "psr-4": { "Symfony\\Component\\PropertyAccess\\": "" } @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Routing/CHANGELOG.md b/src/Symfony/Component/Routing/CHANGELOG.md index b5fd9db949d47..864ef68dda821 100644 --- a/src/Symfony/Component/Routing/CHANGELOG.md +++ b/src/Symfony/Component/Routing/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +2.8.0 +----- + + * allowed specifying a directory to recursively load all routing configuration files it contains + 2.5.0 ----- diff --git a/src/Symfony/Component/Routing/Loader/DirectoryLoader.php b/src/Symfony/Component/Routing/Loader/DirectoryLoader.php new file mode 100644 index 0000000000000..4bb5b31b60a82 --- /dev/null +++ b/src/Symfony/Component/Routing/Loader/DirectoryLoader.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Loader; + +use Symfony\Component\Config\Loader\FileLoader; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Config\Resource\DirectoryResource; + +class DirectoryLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + public function load($file, $type = null) + { + $path = $this->locator->locate($file); + + $collection = new RouteCollection(); + $collection->addResource(new DirectoryResource($path)); + + foreach (scandir($path) as $dir) { + if ('.' !== $dir[0]) { + $this->setCurrentDir($path); + $subPath = $path.'/'.$dir; + $subType = null; + + if (is_dir($subPath)) { + $subPath .= '/'; + $subType = 'directory'; + } + + $subCollection = $this->import($subPath, $subType, false, $path); + $collection->addCollection($subCollection); + } + } + + return $collection; + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + // only when type is forced to directory, not to conflict with AnnotationLoader + + return 'directory' === $type; + } +} diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/directory/recurse/routes1.yml b/src/Symfony/Component/Routing/Tests/Fixtures/directory/recurse/routes1.yml new file mode 100644 index 0000000000000..d078836625c6b --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Fixtures/directory/recurse/routes1.yml @@ -0,0 +1,2 @@ +route1: + path: /route/1 diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/directory/recurse/routes2.yml b/src/Symfony/Component/Routing/Tests/Fixtures/directory/recurse/routes2.yml new file mode 100644 index 0000000000000..938fb2457e9a3 --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Fixtures/directory/recurse/routes2.yml @@ -0,0 +1,2 @@ +route2: + path: /route/2 diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/directory/routes3.yml b/src/Symfony/Component/Routing/Tests/Fixtures/directory/routes3.yml new file mode 100644 index 0000000000000..088cfb4d4315e --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Fixtures/directory/routes3.yml @@ -0,0 +1,2 @@ +route3: + path: /route/3 diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/directory_import/import.yml b/src/Symfony/Component/Routing/Tests/Fixtures/directory_import/import.yml new file mode 100644 index 0000000000000..af829e58c7ffd --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Fixtures/directory_import/import.yml @@ -0,0 +1,3 @@ +_directory: + resource: "../directory" + type: directory diff --git a/src/Symfony/Component/Routing/Tests/Loader/DirectoryLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/DirectoryLoaderTest.php new file mode 100644 index 0000000000000..db29530564a29 --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Loader/DirectoryLoaderTest.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Loader; + +use Symfony\Component\Routing\Loader\DirectoryLoader; +use Symfony\Component\Routing\Loader\YamlFileLoader; +use Symfony\Component\Routing\Loader\AnnotationFileLoader; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Routing\RouteCollection; + +class DirectoryLoaderTest extends AbstractAnnotationLoaderTest +{ + private $loader; + private $reader; + + protected function setUp() + { + parent::setUp(); + + $locator = new FileLocator(); + $this->reader = $this->getReader(); + $this->loader = new DirectoryLoader($locator); + $resolver = new LoaderResolver(array( + new YamlFileLoader($locator), + new AnnotationFileLoader($locator, $this->getClassLoader($this->reader)), + $this->loader, + )); + $this->loader->setResolver($resolver); + } + + public function testLoadDirectory() + { + $collection = $this->loader->load(__DIR__.'/../Fixtures/directory', 'directory'); + $this->verifyCollection($collection); + } + + public function testImportDirectory() + { + $collection = $this->loader->load(__DIR__.'/../Fixtures/directory_import', 'directory'); + $this->verifyCollection($collection); + } + + private function verifyCollection(RouteCollection $collection) + { + $routes = $collection->all(); + + $this->assertCount(3, $routes, 'Three routes are loaded'); + $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes); + + for ($i = 1; $i <= 3; $i++) { + $this->assertSame('/route/'.$i, $routes['route'.$i]->getPath()); + } + } + + public function testSupports() + { + $fixturesDir = __DIR__.'/../Fixtures'; + + $this->assertFalse($this->loader->supports($fixturesDir), '->supports(*) returns false'); + + $this->assertTrue($this->loader->supports($fixturesDir, 'directory'), '->supports(*, "directory") returns true'); + $this->assertFalse($this->loader->supports($fixturesDir, 'foo'), '->supports(*, "foo") returns false'); + } +} diff --git a/src/Symfony/Component/Routing/composer.json b/src/Symfony/Component/Routing/composer.json index 31b63ae64aacc..b7d4c0aa2bdb8 100644 --- a/src/Symfony/Component/Routing/composer.json +++ b/src/Symfony/Component/Routing/composer.json @@ -19,11 +19,11 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/config": "~2.7", - "symfony/http-foundation": "~2.3", - "symfony/yaml": "~2.0,>=2.0.5", - "symfony/expression-language": "~2.4", + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/config": "~2.7|~3.0.0", + "symfony/http-foundation": "~2.3|~3.0.0", + "symfony/yaml": "~2.0,>=2.0.5|~3.0.0", + "symfony/expression-language": "~2.4|~3.0.0", "doctrine/annotations": "~1.0", "doctrine/common": "~2.2", "psr/log": "~1.0" @@ -43,7 +43,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Security/Acl/README.md b/src/Symfony/Component/Security/Acl/README.md index bff22093aa7bc..458820e812ebe 100644 --- a/src/Symfony/Component/Security/Acl/README.md +++ b/src/Symfony/Component/Security/Acl/README.md @@ -11,7 +11,7 @@ Resources Documentation: -https://symfony.com/doc/2.7/book/security.html +https://symfony.com/doc/2.8/book/security.html Tests ----- diff --git a/src/Symfony/Component/Security/Acl/composer.json b/src/Symfony/Component/Security/Acl/composer.json index 917c061d1bff6..b2ac560af9ad3 100644 --- a/src/Symfony/Component/Security/Acl/composer.json +++ b/src/Symfony/Component/Security/Acl/composer.json @@ -17,10 +17,10 @@ ], "require": { "php": ">=5.3.9", - "symfony/security-core": "~2.4" + "symfony/security-core": "~2.4|~3.0.0" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", + "symfony/phpunit-bridge": "~2.7|~3.0.0", "doctrine/common": "~2.2", "doctrine/dbal": "~2.2", "psr/log": "~1.0" @@ -36,7 +36,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index 22eb9cd0d1e6b..f2026929296a4 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -1,6 +1,16 @@ CHANGELOG ========= +2.8.0 +----- + + * deprecated `getKey()` of the `AnonymousToken`, `RememberMeToken` and `AbstractRememberMeServices` classes + in favor of `getSecret()`. + * deprecated `Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface`, use + `Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface` instead + * deprecated `Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface`, use + `Symfony\Component\Security\Http\Authentication\SimpleFormAuthenticatorInterface` instead + 2.7.0 ----- diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/AnonymousAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/AnonymousAuthenticationProvider.php index 7fbbf858ace91..ff3d15fc59308 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/AnonymousAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/AnonymousAuthenticationProvider.php @@ -22,16 +22,22 @@ */ class AnonymousAuthenticationProvider implements AuthenticationProviderInterface { - private $key; + /** + * Used to determine if the token is created by the application + * instead of a malicious client. + * + * @var string + */ + private $secret; /** * Constructor. * - * @param string $key The key shared with the authentication token + * @param string $secret The secret shared with the AnonymousToken */ - public function __construct($key) + public function __construct($secret) { - $this->key = $key; + $this->secret = $secret; } /** @@ -43,7 +49,7 @@ public function authenticate(TokenInterface $token) return; } - if ($this->key !== $token->getKey()) { + if ($this->secret !== $token->getSecret()) { throw new BadCredentialsException('The Token does not contain the expected key.'); } diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php index 82be1d13e9a99..f0a74eb9d2bbe 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/RememberMeAuthenticationProvider.php @@ -19,20 +19,20 @@ class RememberMeAuthenticationProvider implements AuthenticationProviderInterface { private $userChecker; - private $key; + private $secret; private $providerKey; /** * Constructor. * * @param UserCheckerInterface $userChecker An UserCheckerInterface interface - * @param string $key A key - * @param string $providerKey A provider key + * @param string $secret A secret + * @param string $providerKey A provider secret */ - public function __construct(UserCheckerInterface $userChecker, $key, $providerKey) + public function __construct(UserCheckerInterface $userChecker, $secret, $providerKey) { $this->userChecker = $userChecker; - $this->key = $key; + $this->secret = $secret; $this->providerKey = $providerKey; } @@ -45,14 +45,14 @@ public function authenticate(TokenInterface $token) return; } - if ($this->key !== $token->getKey()) { - throw new BadCredentialsException('The presented key does not match.'); + if ($this->secret !== $token->getSecret()) { + throw new BadCredentialsException('The presented secret does not match.'); } $user = $token->getUser(); $this->userChecker->checkPreAuth($user); - $authenticatedToken = new RememberMeToken($user, $this->providerKey, $this->key); + $authenticatedToken = new RememberMeToken($user, $this->providerKey, $this->secret); $authenticatedToken->setAttributes($token->getAttributes()); return $authenticatedToken; diff --git a/src/Symfony/Component/Security/Core/Authentication/SimpleFormAuthenticatorInterface.php b/src/Symfony/Component/Security/Core/Authentication/SimpleFormAuthenticatorInterface.php index 95ee881c18d82..ae2b58b19f12d 100644 --- a/src/Symfony/Component/Security/Core/Authentication/SimpleFormAuthenticatorInterface.php +++ b/src/Symfony/Component/Security/Core/Authentication/SimpleFormAuthenticatorInterface.php @@ -14,6 +14,8 @@ use Symfony\Component\HttpFoundation\Request; /** + * @deprecated Deprecated since version 2.8, to be removed in 3.0. Use the same interface from Security\Http\Authentication instead. + * * @author Jordi Boggiano */ interface SimpleFormAuthenticatorInterface extends SimpleAuthenticatorInterface diff --git a/src/Symfony/Component/Security/Core/Authentication/SimplePreAuthenticatorInterface.php b/src/Symfony/Component/Security/Core/Authentication/SimplePreAuthenticatorInterface.php index 6164e7d9860a7..c01f064765ea0 100644 --- a/src/Symfony/Component/Security/Core/Authentication/SimplePreAuthenticatorInterface.php +++ b/src/Symfony/Component/Security/Core/Authentication/SimplePreAuthenticatorInterface.php @@ -14,6 +14,8 @@ use Symfony\Component\HttpFoundation\Request; /** + * @deprecated Since version 2.8, to be removed in 3.0. Use the same interface from Security\Http\Authentication instead. + * * @author Jordi Boggiano */ interface SimplePreAuthenticatorInterface extends SimpleAuthenticatorInterface diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/AnonymousToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/AnonymousToken.php index 571816ca9ecb2..22fc611b2a16b 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/AnonymousToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/AnonymousToken.php @@ -20,20 +20,20 @@ */ class AnonymousToken extends AbstractToken { - private $key; + private $secret; /** * Constructor. * - * @param string $key The key shared with the authentication provider - * @param string $user The user - * @param RoleInterface[] $roles An array of roles + * @param string $secret A secret used to make sure the token is created by the app and not by a malicious client + * @param string $user The user + * @param RoleInterface[] $roles An array of roles */ - public function __construct($key, $user, array $roles = array()) + public function __construct($secret, $user, array $roles = array()) { parent::__construct($roles); - $this->key = $key; + $this->secret = $secret; $this->setUser($user); $this->setAuthenticated(true); } @@ -47,13 +47,23 @@ public function getCredentials() } /** - * Returns the key. - * - * @return string The Key + * @deprecated Since version 2.8, to be removed in 3.0. Use getSecret() instead. */ public function getKey() { - return $this->key; + @trigger_error(__method__.'() is deprecated since version 2.8 and will be removed in 3.0. Use getSecret() instead.', E_USER_DEPRECATED); + + return $this->getSecret(); + } + + /** + * Returns the secret. + * + * @return string + */ + public function getSecret() + { + return $this->secret; } /** @@ -61,7 +71,7 @@ public function getKey() */ public function serialize() { - return serialize(array($this->key, parent::serialize())); + return serialize(array($this->secret, parent::serialize())); } /** @@ -69,7 +79,7 @@ public function serialize() */ public function unserialize($serialized) { - list($this->key, $parentStr) = unserialize($serialized); + list($this->secret, $parentStr) = unserialize($serialized); parent::unserialize($parentStr); } } diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/RememberMeToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/RememberMeToken.php index 609fdad1c1fc6..60e36f29904f2 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/RememberMeToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/RememberMeToken.php @@ -20,7 +20,7 @@ */ class RememberMeToken extends AbstractToken { - private $key; + private $secret; private $providerKey; /** @@ -28,16 +28,16 @@ class RememberMeToken extends AbstractToken * * @param UserInterface $user * @param string $providerKey - * @param string $key + * @param string $secret A secret used to make sure the token is created by the app and not by a malicious client * * @throws \InvalidArgumentException */ - public function __construct(UserInterface $user, $providerKey, $key) + public function __construct(UserInterface $user, $providerKey, $secret) { parent::__construct($user->getRoles()); - if (empty($key)) { - throw new \InvalidArgumentException('$key must not be empty.'); + if (empty($secret)) { + throw new \InvalidArgumentException('$secret must not be empty.'); } if (empty($providerKey)) { @@ -45,7 +45,7 @@ public function __construct(UserInterface $user, $providerKey, $key) } $this->providerKey = $providerKey; - $this->key = $key; + $this->secret = $secret; $this->setUser($user); parent::setAuthenticated(true); @@ -64,9 +64,9 @@ public function setAuthenticated($authenticated) } /** - * Returns the provider key. + * Returns the provider secret. * - * @return string The provider key + * @return string The provider secret */ public function getProviderKey() { @@ -74,13 +74,23 @@ public function getProviderKey() } /** - * Returns the key. - * - * @return string The Key + * @deprecated Since version 2.8, to be removed in 3.0. Use getSecret() instead. */ public function getKey() { - return $this->key; + @trigger_error(__method__.'() is deprecated since version 2.8 and will be removed in 3.0. Use getSecret() instead.', E_USER_DEPRECATED); + + return $this->getSecret(); + } + + /** + * Returns the secret. + * + * @return string + */ + public function getSecret() + { + return $this->secret; } /** @@ -97,7 +107,7 @@ public function getCredentials() public function serialize() { return serialize(array( - $this->key, + $this->secret, $this->providerKey, parent::serialize(), )); @@ -108,7 +118,7 @@ public function serialize() */ public function unserialize($serialized) { - list($this->key, $this->providerKey, $parentStr) = unserialize($serialized); + list($this->secret, $this->providerKey, $parentStr) = unserialize($serialized); parent::unserialize($parentStr); } } diff --git a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php index b8b6a776e9b8e..61debe39cce27 100644 --- a/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php +++ b/src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php @@ -41,12 +41,8 @@ class AccessDecisionManager implements AccessDecisionManagerInterface * * @throws \InvalidArgumentException */ - public function __construct(array $voters, $strategy = self::STRATEGY_AFFIRMATIVE, $allowIfAllAbstainDecisions = false, $allowIfEqualGrantedDeniedDecisions = true) + public function __construct(array $voters = array(), $strategy = self::STRATEGY_AFFIRMATIVE, $allowIfAllAbstainDecisions = false, $allowIfEqualGrantedDeniedDecisions = true) { - if (!$voters) { - throw new \InvalidArgumentException('You must at least add one voter.'); - } - $strategyMethod = 'decide'.ucfirst($strategy); if (!is_callable(array($this, $strategyMethod))) { throw new \InvalidArgumentException(sprintf('The strategy "%s" is not supported.', $strategy)); @@ -58,6 +54,16 @@ public function __construct(array $voters, $strategy = self::STRATEGY_AFFIRMATIV $this->allowIfEqualGrantedDeniedDecisions = (bool) $allowIfEqualGrantedDeniedDecisions; } + /** + * Configures the voters. + * + * @param VoterInterface[] $voters An array of VoterInterface instances + */ + public function setVoters(array $voters) + { + $this->voters = $voters; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Security/Core/README.md b/src/Symfony/Component/Security/Core/README.md index b0d17494974e6..f1da5b183ae7d 100644 --- a/src/Symfony/Component/Security/Core/README.md +++ b/src/Symfony/Component/Security/Core/README.md @@ -11,7 +11,7 @@ Resources Documentation: -https://symfony.com/doc/2.7/book/security.html +https://symfony.com/doc/2.8/book/security.html Tests ----- diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/AnonymousAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/AnonymousAuthenticationProviderTest.php index 5a189b0ec6450..5b7174745ef50 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/AnonymousAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/AnonymousAuthenticationProviderTest.php @@ -37,7 +37,7 @@ public function testAuthenticateWhenKeyIsNotValid() { $provider = $this->getProvider('foo'); - $this->assertNull($provider->authenticate($this->getSupportedToken('bar'))); + $provider->authenticate($this->getSupportedToken('bar')); } public function testAuthenticate() @@ -50,9 +50,9 @@ public function testAuthenticate() protected function getSupportedToken($key) { - $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\AnonymousToken', array('getKey'), array(), '', false); + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\AnonymousToken', array('getSecret'), array(), '', false); $token->expects($this->any()) - ->method('getKey') + ->method('getSecret') ->will($this->returnValue($key)) ; diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php index a6fff4bfa9ba1..735d195d0c49c 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php @@ -36,10 +36,10 @@ public function testAuthenticateWhenTokenIsNotSupported() /** * @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException */ - public function testAuthenticateWhenKeysDoNotMatch() + public function testAuthenticateWhenSecretsDoNotMatch() { - $provider = $this->getProvider(null, 'key1'); - $token = $this->getSupportedToken(null, 'key2'); + $provider = $this->getProvider(null, 'secret1'); + $token = $this->getSupportedToken(null, 'secret2'); $provider->authenticate($token); } @@ -77,7 +77,7 @@ public function testAuthenticate() $this->assertEquals('', $authToken->getCredentials()); } - protected function getSupportedToken($user = null, $key = 'test') + protected function getSupportedToken($user = null, $secret = 'test') { if (null === $user) { $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); @@ -87,7 +87,7 @@ protected function getSupportedToken($user = null, $key = 'test') ->will($this->returnValue(array())); } - $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', array('getProviderKey'), array($user, 'foo', $key)); + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', array('getProviderKey'), array($user, 'foo', $secret)); $token ->expects($this->once()) ->method('getProviderKey') diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AnonymousTokenTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AnonymousTokenTest.php index b5cf00683aa5f..cac2039ffe2fe 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AnonymousTokenTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AnonymousTokenTest.php @@ -28,7 +28,7 @@ public function testConstructor() public function testGetKey() { $token = new AnonymousToken('foo', 'bar'); - $this->assertEquals('foo', $token->getKey()); + $this->assertEquals('foo', $token->getSecret()); } public function testGetCredentials() diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/RememberMeTokenTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/RememberMeTokenTest.php index 7449204533dbf..b83de4a984570 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/RememberMeTokenTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/RememberMeTokenTest.php @@ -22,7 +22,7 @@ public function testConstructor() $token = new RememberMeToken($user, 'fookey', 'foo'); $this->assertEquals('fookey', $token->getProviderKey()); - $this->assertEquals('foo', $token->getKey()); + $this->assertEquals('foo', $token->getSecret()); $this->assertEquals(array(new Role('ROLE_FOO')), $token->getRoles()); $this->assertSame($user, $token->getUser()); $this->assertTrue($token->isAuthenticated()); @@ -31,7 +31,7 @@ public function testConstructor() /** * @expectedException \InvalidArgumentException */ - public function testConstructorKeyCannotBeNull() + public function testConstructorSecretCannotBeNull() { new RememberMeToken( $this->getUser(), @@ -43,7 +43,7 @@ public function testConstructorKeyCannotBeNull() /** * @expectedException \InvalidArgumentException */ - public function testConstructorKeyCannotBeEmptyString() + public function testConstructorSecretCannotBeEmptyString() { new RememberMeToken( $this->getUser(), diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php index 3c970d148ae1d..bd876c729f1d8 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php @@ -46,14 +46,6 @@ public function testSupportsAttribute() $this->assertFalse($manager->supportsAttribute('foo')); } - /** - * @expectedException \InvalidArgumentException - */ - public function testSetVotersEmpty() - { - $manager = new AccessDecisionManager(array()); - } - /** * @expectedException \InvalidArgumentException */ diff --git a/src/Symfony/Component/Security/Core/composer.json b/src/Symfony/Component/Security/Core/composer.json index 38054df6bd96f..4d2405317c89f 100644 --- a/src/Symfony/Component/Security/Core/composer.json +++ b/src/Symfony/Component/Security/Core/composer.json @@ -19,12 +19,12 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/event-dispatcher": "~2.1", - "symfony/expression-language": "~2.6", - "symfony/http-foundation": "~2.4", - "symfony/translation": "~2.0,>=2.0.5", - "symfony/validator": "~2.5,>=2.5.5", + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/event-dispatcher": "~2.1|~3.0.0", + "symfony/expression-language": "~2.6|~3.0.0", + "symfony/http-foundation": "~2.4|~3.0.0", + "symfony/translation": "~2.0,>=2.0.5|~3.0.0", + "symfony/validator": "~2.5,>=2.5.5|~3.0.0", "psr/log": "~1.0", "ircmaxell/password-compat": "1.0.*" }, @@ -41,7 +41,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Security/Csrf/README.md b/src/Symfony/Component/Security/Csrf/README.md index 3ae550b4cc0a4..10f4784507d76 100644 --- a/src/Symfony/Component/Security/Csrf/README.md +++ b/src/Symfony/Component/Security/Csrf/README.md @@ -9,7 +9,7 @@ Resources Documentation: -https://symfony.com/doc/2.7/book/security.html +https://symfony.com/doc/2.8/book/security.html Tests ----- diff --git a/src/Symfony/Component/Security/Csrf/composer.json b/src/Symfony/Component/Security/Csrf/composer.json index a0de39dade3ee..20fd2ffebd42e 100644 --- a/src/Symfony/Component/Security/Csrf/composer.json +++ b/src/Symfony/Component/Security/Csrf/composer.json @@ -17,11 +17,11 @@ ], "require": { "php": ">=5.3.9", - "symfony/security-core": "~2.4" + "symfony/security-core": "~2.4|~3.0.0" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/http-foundation": "~2.1" + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/http-foundation": "~2.1|~3.0.0" }, "suggest": { "symfony/http-foundation": "For using the class SessionTokenStorage." @@ -32,7 +32,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Security/Http/Authentication/SimpleFormAuthenticatorInterface.php b/src/Symfony/Component/Security/Http/Authentication/SimpleFormAuthenticatorInterface.php new file mode 100644 index 0000000000000..112688c97ca6a --- /dev/null +++ b/src/Symfony/Component/Security/Http/Authentication/SimpleFormAuthenticatorInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Http\Authentication; + +use Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface as BaseSimpleFormAuthenticatorInterface; + +/** + * @author Jordi Boggiano + */ +interface SimpleFormAuthenticatorInterface extends BaseSimpleFormAuthenticatorInterface +{ +} diff --git a/src/Symfony/Component/Security/Http/Authentication/SimplePreAuthenticatorInterface.php b/src/Symfony/Component/Security/Http/Authentication/SimplePreAuthenticatorInterface.php new file mode 100644 index 0000000000000..afa8049508e4a --- /dev/null +++ b/src/Symfony/Component/Security/Http/Authentication/SimplePreAuthenticatorInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Http\Authentication; + +use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface as BaseSimplePreAuthenticatorInterface; + +/** + * @author Jordi Boggiano + */ +interface SimplePreAuthenticatorInterface extends BaseSimplePreAuthenticatorInterface +{ +} diff --git a/src/Symfony/Component/Security/Http/README.md b/src/Symfony/Component/Security/Http/README.md index 7619bfcb9c9d1..11f6f72a9c707 100644 --- a/src/Symfony/Component/Security/Http/README.md +++ b/src/Symfony/Component/Security/Http/README.md @@ -11,7 +11,7 @@ Resources Documentation: -https://symfony.com/doc/2.7/book/security.html +https://symfony.com/doc/2.8/book/security.html Tests ----- diff --git a/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php index 3673ff119714c..16810bd17d3e3 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php @@ -36,24 +36,24 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface protected $logger; protected $options; private $providerKey; - private $key; + private $secret; private $userProviders; /** * Constructor. * * @param array $userProviders - * @param string $key + * @param string $secret * @param string $providerKey * @param array $options * @param LoggerInterface $logger * * @throws \InvalidArgumentException */ - public function __construct(array $userProviders, $key, $providerKey, array $options = array(), LoggerInterface $logger = null) + public function __construct(array $userProviders, $secret, $providerKey, array $options = array(), LoggerInterface $logger = null) { - if (empty($key)) { - throw new \InvalidArgumentException('$key must not be empty.'); + if (empty($secret)) { + throw new \InvalidArgumentException('$secret must not be empty.'); } if (empty($providerKey)) { throw new \InvalidArgumentException('$providerKey must not be empty.'); @@ -63,7 +63,7 @@ public function __construct(array $userProviders, $key, $providerKey, array $opt } $this->userProviders = $userProviders; - $this->key = $key; + $this->secret = $secret; $this->providerKey = $providerKey; $this->options = $options; $this->logger = $logger; @@ -81,11 +81,21 @@ public function getRememberMeParameter() } /** - * @return string + * @deprecated Since version 2.8, to be removed in 3.0. Use getSecret() instead. */ public function getKey() { - return $this->key; + @trigger_error(__method__.'() is deprecated since version 2.8 and will be removed in 3.0. Use getSecret() instead.', E_USER_DEPRECATED); + + return $this->getSecret(); + } + + /** + * @return string + */ + public function getSecret() + { + return $this->secret; } /** @@ -122,7 +132,7 @@ final public function autoLogin(Request $request) $this->logger->info('Remember-me cookie accepted.'); } - return new RememberMeToken($user, $this->providerKey, $this->key); + return new RememberMeToken($user, $this->providerKey, $this->secret); } catch (CookieTheftException $e) { $this->cancelCookie($request); diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index 4fb7e09c147ff..3e465d68704d0 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -38,15 +38,15 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices * Constructor. * * @param array $userProviders - * @param string $key + * @param string $secret * @param string $providerKey * @param array $options * @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, $secret, $providerKey, array $options = array(), LoggerInterface $logger = null, SecureRandomInterface $secureRandom) { - parent::__construct($userProviders, $key, $providerKey, $options, $logger); + parent::__construct($userProviders, $secret, $providerKey, $options, $logger); $this->secureRandom = $secureRandom; } diff --git a/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php index d68ada5211a92..f6107ec40647b 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php @@ -121,6 +121,6 @@ protected function generateCookieValue($class, $username, $expires, $password) */ protected function generateCookieHash($class, $username, $expires, $password) { - return hash_hmac('sha256', $class.$username.$expires.$password, $this->getKey()); + return hash_hmac('sha256', $class.$username.$expires.$password, $this->getSecret()); } } diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php index 3450c1ea439b9..d99b56239f8a1 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php @@ -35,7 +35,7 @@ public function testHandleWithTokenStorageHavingAToken() ->method('authenticate') ; - $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheKey', null, $authenticationManager); + $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheSecret', null, $authenticationManager); $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false)); } @@ -48,14 +48,14 @@ public function testHandleWithTokenStorageHavingNoToken() ->will($this->returnValue(null)) ; - $anonymousToken = new AnonymousToken('TheKey', 'anon.', array()); + $anonymousToken = new AnonymousToken('TheSecret', 'anon.', array()); $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); $authenticationManager ->expects($this->once()) ->method('authenticate') ->with($this->callback(function ($token) { - return 'TheKey' === $token->getKey(); + return 'TheSecret' === $token->getSecret(); })) ->will($this->returnValue($anonymousToken)) ; @@ -66,7 +66,7 @@ public function testHandleWithTokenStorageHavingNoToken() ->with($anonymousToken) ; - $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheKey', null, $authenticationManager); + $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheSecret', null, $authenticationManager); $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false)); } @@ -81,7 +81,7 @@ public function testHandledEventIsLogged() $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); - $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheKey', $logger, $authenticationManager); + $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheSecret', $logger, $authenticationManager); $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false)); } } diff --git a/src/Symfony/Component/Security/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php b/src/Symfony/Component/Security/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php index 2225b6c55338e..5a6a83931e276 100644 --- a/src/Symfony/Component/Security/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php +++ b/src/Symfony/Component/Security/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php @@ -25,10 +25,10 @@ public function testGetRememberMeParameter() $this->assertEquals('foo', $service->getRememberMeParameter()); } - public function testGetKey() + public function testGetSecret() { $service = $this->getService(); - $this->assertEquals('fookey', $service->getKey()); + $this->assertEquals('foosecret', $service->getSecret()); } public function testAutoLoginReturnsNullWhenNoCookie() @@ -78,7 +78,7 @@ public function testAutoLogin() $returnedToken = $service->autoLogin($request); $this->assertSame($user, $returnedToken->getUser()); - $this->assertSame('fookey', $returnedToken->getKey()); + $this->assertSame('foosecret', $returnedToken->getSecret()); $this->assertSame('fookey', $returnedToken->getProviderKey()); } @@ -268,7 +268,7 @@ protected function getService($userProvider = null, $options = array(), $logger } return $this->getMockForAbstractClass('Symfony\Component\Security\Http\RememberMe\AbstractRememberMeServices', array( - array($userProvider), 'fookey', 'fookey', $options, $logger, + array($userProvider), 'foosecret', 'fookey', $options, $logger, )); } diff --git a/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php b/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php index 2fea626d7a333..889211c96d7d7 100644 --- a/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php +++ b/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php @@ -174,7 +174,7 @@ public function testAutoLogin() $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', $returnedToken); $this->assertSame($user, $returnedToken->getUser()); - $this->assertEquals('fookey', $returnedToken->getKey()); + $this->assertEquals('foosecret', $returnedToken->getSecret()); $this->assertTrue($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME)); } @@ -311,7 +311,7 @@ protected function getService($userProvider = null, $options = array(), $logger $userProvider = $this->getProvider(); } - return new PersistentTokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger, new SecureRandom(sys_get_temp_dir().'/_sf2.seed')); + return new PersistentTokenBasedRememberMeServices(array($userProvider), 'foosecret', 'fookey', $options, $logger, new SecureRandom(sys_get_temp_dir().'/_sf2.seed')); } protected function getProvider() diff --git a/src/Symfony/Component/Security/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php b/src/Symfony/Component/Security/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php index 8383cec5744fa..2a892c3376de5 100644 --- a/src/Symfony/Component/Security/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php +++ b/src/Symfony/Component/Security/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php @@ -140,7 +140,7 @@ public function testAutoLogin($username) $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', $returnedToken); $this->assertSame($user, $returnedToken->getUser()); - $this->assertEquals('fookey', $returnedToken->getKey()); + $this->assertEquals('foosecret', $returnedToken->getSecret()); } public function provideUsernamesForAutoLogin() @@ -264,7 +264,7 @@ protected function getService($userProvider = null, $options = array(), $logger $userProvider = $this->getProvider(); } - $service = new TokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger); + $service = new TokenBasedRememberMeServices(array($userProvider), 'foosecret', 'fookey', $options, $logger); return $service; } diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json index 7b08d002b4fbb..98bd8cdbf3b93 100644 --- a/src/Symfony/Component/Security/Http/composer.json +++ b/src/Symfony/Component/Security/Http/composer.json @@ -17,15 +17,15 @@ ], "require": { "php": ">=5.3.9", - "symfony/security-core": "~2.6", - "symfony/event-dispatcher": "~2.1", - "symfony/http-foundation": "~2.4", - "symfony/http-kernel": "~2.4" + "symfony/security-core": "~2.8|~3.0.0", + "symfony/event-dispatcher": "~2.1|~3.0.0", + "symfony/http-foundation": "~2.4|~3.0.0", + "symfony/http-kernel": "~2.4|~3.0.0" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/routing": "~2.2", - "symfony/security-csrf": "~2.4", + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/routing": "~2.2|~3.0.0", + "symfony/security-csrf": "~2.4|~3.0.0", "psr/log": "~1.0" }, "suggest": { @@ -38,7 +38,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Security/README.md b/src/Symfony/Component/Security/README.md index c41b673f796fa..d85cd9636f489 100644 --- a/src/Symfony/Component/Security/README.md +++ b/src/Symfony/Component/Security/README.md @@ -11,7 +11,7 @@ Resources Documentation: -https://symfony.com/doc/2.7/book/security.html +https://symfony.com/doc/2.8/book/security.html Tests ----- diff --git a/src/Symfony/Component/Security/composer.json b/src/Symfony/Component/Security/composer.json index aeea64af365d0..75abcf82e0647 100644 --- a/src/Symfony/Component/Security/composer.json +++ b/src/Symfony/Component/Security/composer.json @@ -17,9 +17,9 @@ ], "require": { "php": ">=5.3.9", - "symfony/event-dispatcher": "~2.2", - "symfony/http-foundation": "~2.1", - "symfony/http-kernel": "~2.4" + "symfony/event-dispatcher": "~2.2|~3.0.0", + "symfony/http-foundation": "~2.1|~3.0.0", + "symfony/http-kernel": "~2.4|~3.0.0" }, "replace": { "symfony/security-acl": "self.version", @@ -28,16 +28,16 @@ "symfony/security-http": "self.version" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/intl": "~2.3", - "symfony/routing": "~2.2", - "symfony/translation": "~2.0,>=2.0.5", - "symfony/validator": "~2.5,>=2.5.5", + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/intl": "~2.3|~3.0.0", + "symfony/routing": "~2.2|~3.0.0", + "symfony/translation": "~2.0,>=2.0.5|~3.0.0", + "symfony/validator": "~2.5,>=2.5.5|~3.0.0", "doctrine/common": "~2.2", "doctrine/dbal": "~2.2", "psr/log": "~1.0", "ircmaxell/password-compat": "~1.0", - "symfony/expression-language": "~2.6" + "symfony/expression-language": "~2.6|~3.0.0" }, "suggest": { "symfony/class-loader": "For using the ACL generateSql script", @@ -54,7 +54,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Serializer/Exception/BadMethodCallException.php b/src/Symfony/Component/Serializer/Exception/BadMethodCallException.php new file mode 100644 index 0000000000000..b2f3d61a8c963 --- /dev/null +++ b/src/Symfony/Component/Serializer/Exception/BadMethodCallException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Exception; + +class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface +{ +} diff --git a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php new file mode 100644 index 0000000000000..6e746f29fe2c1 --- /dev/null +++ b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Component\Serializer\Exception\BadMethodCallException; +use Symfony\Component\Serializer\Exception\InvalidArgumentException; +use Symfony\Component\Serializer\SerializerAwareInterface; +use Symfony\Component\Serializer\SerializerInterface; + +/** + * Denormalizes arrays of objects. + * + * @author Alexander M. Turek + */ +class ArrayDenormalizer implements DenormalizerInterface, SerializerAwareInterface +{ + /** + * @var SerializerInterface|DenormalizerInterface + */ + private $serializer; + + /** + * {@inheritdoc} + */ + public function denormalize($data, $class, $format = null, array $context = array()) + { + if ($this->serializer === null) { + throw new BadMethodCallException('Please set a serializer before calling denormalize()!'); + } + if (!is_array($data)) { + throw new InvalidArgumentException('Data expected to be an array, '.gettype($data).' given.'); + } + if (substr($class, -2) !== '[]') { + throw new InvalidArgumentException('Unsupported class: '.$class); + } + + $serializer = $this->serializer; + $class = substr($class, 0, -2); + + return array_map( + function ($data) use ($serializer, $class, $format, $context) { + return $serializer->denormalize($data, $class, $format, $context); + }, + $data + ); + } + + /** + * {@inheritdoc} + */ + public function supportsDenormalization($data, $type, $format = null) + { + return substr($type, -2) === '[]' + && $this->serializer->supportsDenormalization($data, substr($type, 0, -2), $format); + } + + /** + * {@inheritdoc} + */ + public function setSerializer(SerializerInterface $serializer) + { + if (!$serializer instanceof DenormalizerInterface) { + throw new InvalidArgumentException('Expected a serializer that also implements DenormalizerInterface.'); + } + + $this->serializer = $serializer; + } +} diff --git a/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php index 83c3c14384622..7df266a7a9708 100644 --- a/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php @@ -59,6 +59,10 @@ public function supportsNormalization($data, $format = null) */ public function supportsDenormalization($data, $type, $format = null) { + if (!class_exists($type)) { + return false; + } + $class = new \ReflectionClass($type); return $class->isSubclassOf('Symfony\Component\Serializer\Normalizer\DenormalizableInterface'); diff --git a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php index 44a71cf1b487f..c56d832d03bf2 100644 --- a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php @@ -136,7 +136,7 @@ public function supportsNormalization($data, $format = null) */ public function supportsDenormalization($data, $type, $format = null) { - return $this->supports($type); + return class_exists($type) && $this->supports($type); } /** diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php index c8e83d1f790f5..dbe54e4024579 100644 --- a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php @@ -135,7 +135,7 @@ public function supportsNormalization($data, $format = null) */ public function supportsDenormalization($data, $type, $format = null) { - return $this->supports($type); + return class_exists($type) && $this->supports($type); } /** diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ArrayDenormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ArrayDenormalizerTest.php new file mode 100644 index 0000000000000..23014f300ec7c --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ArrayDenormalizerTest.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Normalizer; + +use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; +use Symfony\Component\Serializer\SerializerInterface; + +class ArrayDenormalizerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ArrayDenormalizer + */ + private $denormalizer; + + /** + * @var SerializerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $serializer; + + protected function setUp() + { + $this->serializer = $this->getMock('Symfony\Component\Serializer\Serializer'); + $this->denormalizer = new ArrayDenormalizer(); + $this->denormalizer->setSerializer($this->serializer); + } + + public function testDenormalize() + { + $this->serializer->expects($this->at(0)) + ->method('denormalize') + ->with(array('foo' => 'one', 'bar' => 'two')) + ->will($this->returnValue(new ArrayDummy('one', 'two'))); + + $this->serializer->expects($this->at(1)) + ->method('denormalize') + ->with(array('foo' => 'three', 'bar' => 'four')) + ->will($this->returnValue(new ArrayDummy('three', 'four'))); + + $result = $this->denormalizer->denormalize( + array( + array('foo' => 'one', 'bar' => 'two'), + array('foo' => 'three', 'bar' => 'four'), + ), + __NAMESPACE__.'\ArrayDummy[]' + ); + + $this->assertEquals( + array( + new ArrayDummy('one', 'two'), + new ArrayDummy('three', 'four'), + ), + $result + ); + } + + public function testSupportsValidArray() + { + $this->serializer->expects($this->once()) + ->method('supportsDenormalization') + ->with($this->anything(), __NAMESPACE__.'\ArrayDummy', $this->anything()) + ->will($this->returnValue(true)); + + $this->assertTrue( + $this->denormalizer->supportsDenormalization( + array( + array('foo' => 'one', 'bar' => 'two'), + array('foo' => 'three', 'bar' => 'four'), + ), + __NAMESPACE__.'\ArrayDummy[]' + ) + ); + } + + public function testSupportsInvalidArray() + { + $this->serializer->expects($this->any()) + ->method('supportsDenormalization') + ->will($this->returnValue(false)); + + $this->assertFalse( + $this->denormalizer->supportsDenormalization( + array( + array('foo' => 'one', 'bar' => 'two'), + array('foo' => 'three', 'bar' => 'four'), + ), + __NAMESPACE__.'\InvalidClass[]' + ) + ); + } + + public function testSupportsNoArray() + { + $this->assertFalse( + $this->denormalizer->supportsDenormalization( + array('foo' => 'one', 'bar' => 'two'), + __NAMESPACE__.'\ArrayDummy' + ) + ); + } +} + +class ArrayDummy +{ + public $foo; + public $bar; + + public function __construct($foo, $bar) + { + $this->foo = $foo; + $this->bar = $bar; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index 68f70fcfe51ad..43dfe29e00714 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Serializer\Tests; +use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; +use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; +use Symfony\Component\Serializer\Normalizer\PropertyNormalizer; use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; @@ -220,6 +223,51 @@ public function testDecode() $result = $this->serializer->decode(json_encode($data), 'json'); $this->assertEquals($data, $result); } + + public function testSupportsArrayDeserialization() + { + $serializer = new Serializer( + array( + new GetSetMethodNormalizer(), + new PropertyNormalizer(), + new ObjectNormalizer(), + new CustomNormalizer(), + new ArrayDenormalizer(), + ), + array( + 'json' => new JsonEncoder(), + ) + ); + + $this->assertTrue( + $serializer->supportsDenormalization(array(), __NAMESPACE__.'\Model[]', 'json') + ); + } + + public function testDeserializeArray() + { + $jsonData = '[{"title":"foo","numbers":[5,3]},{"title":"bar","numbers":[2,8]}]'; + + $expectedData = array( + Model::fromArray(array('title' => 'foo', 'numbers' => array(5, 3))), + Model::fromArray(array('title' => 'bar', 'numbers' => array(2, 8))), + ); + + $serializer = new Serializer( + array( + new GetSetMethodNormalizer(), + new ArrayDenormalizer(), + ), + array( + 'json' => new JsonEncoder(), + ) + ); + + $this->assertEquals( + $expectedData, + $serializer->deserialize($jsonData, __NAMESPACE__.'\Model[]', 'json') + ); + } } class Model diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json index 0f28b518b8ac7..4ed7c2d7c1e9b 100644 --- a/src/Symfony/Component/Serializer/composer.json +++ b/src/Symfony/Component/Serializer/composer.json @@ -19,10 +19,10 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/yaml": "~2.0", - "symfony/config": "~2.2", - "symfony/property-access": "~2.3", + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/yaml": "~2.0|~3.0.0", + "symfony/config": "~2.2|~3.0.0", + "symfony/property-access": "~2.3|~3.0.0", "doctrine/annotations": "~1.0", "doctrine/cache": "~1.0" }, @@ -39,7 +39,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Stopwatch/composer.json b/src/Symfony/Component/Stopwatch/composer.json index d165e4e52c3cf..e001a17128445 100644 --- a/src/Symfony/Component/Stopwatch/composer.json +++ b/src/Symfony/Component/Stopwatch/composer.json @@ -19,7 +19,7 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7" + "symfony/phpunit-bridge": "~2.7|~3.0.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Stopwatch\\": "" } @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Templating/composer.json b/src/Symfony/Component/Templating/composer.json index 608d6d2cc7694..0e50a6d95aacb 100644 --- a/src/Symfony/Component/Templating/composer.json +++ b/src/Symfony/Component/Templating/composer.json @@ -19,7 +19,7 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", + "symfony/phpunit-bridge": "~2.7|~3.0.0", "psr/log": "~1.0" }, "suggest": { @@ -31,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Translation/CHANGELOG.md b/src/Symfony/Component/Translation/CHANGELOG.md index 157752ca77180..8b2661e9ba437 100644 --- a/src/Symfony/Component/Translation/CHANGELOG.md +++ b/src/Symfony/Component/Translation/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +2.8.0 +----- + + * deprecated Translator::getMessages(), use TranslatorBagInterface::getCatalogue() instead. + * added options 'as_tree', 'inline' to YamlFileDumper + 2.7.0 ----- diff --git a/src/Symfony/Component/Translation/DataCollector/TranslationDataCollector.php b/src/Symfony/Component/Translation/DataCollector/TranslationDataCollector.php index eb9d1e7333067..f33a7e5dd91b0 100644 --- a/src/Symfony/Component/Translation/DataCollector/TranslationDataCollector.php +++ b/src/Symfony/Component/Translation/DataCollector/TranslationDataCollector.php @@ -101,9 +101,14 @@ private function sanitizeCollectedMessages($messages) if (!isset($result[$messageId])) { $message['count'] = 1; + $message['parameters'] = !empty($message['parameters']) ? array($message['parameters']) : array(); $messages[$key]['translation'] = $this->sanitizeString($message['translation']); $result[$messageId] = $message; } else { + if (!empty($message['parameters'])) { + $result[$messageId]['parameters'][] = $message['parameters']; + } + $result[$messageId]['count']++; } diff --git a/src/Symfony/Component/Translation/DataCollectorTranslator.php b/src/Symfony/Component/Translation/DataCollectorTranslator.php index 813a85760ae14..a7478da3740e9 100644 --- a/src/Symfony/Component/Translation/DataCollectorTranslator.php +++ b/src/Symfony/Component/Translation/DataCollectorTranslator.php @@ -48,7 +48,7 @@ public function __construct(TranslatorInterface $translator) public function trans($id, array $parameters = array(), $domain = null, $locale = null) { $trans = $this->translator->trans($id, $parameters, $domain, $locale); - $this->collectMessage($locale, $domain, $id, $trans); + $this->collectMessage($locale, $domain, $id, $trans, $parameters); return $trans; } @@ -59,7 +59,7 @@ public function trans($id, array $parameters = array(), $domain = null, $locale public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) { $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale); - $this->collectMessage($locale, $domain, $id, $trans); + $this->collectMessage($locale, $domain, $id, $trans, $parameters, $number); return $trans; } @@ -112,9 +112,11 @@ public function getCollectedMessages() * @param string|null $locale * @param string|null $domain * @param string $id - * @param string $trans + * @param string $translation + * @param array|null $parameters + * @param int|null $number */ - private function collectMessage($locale, $domain, $id, $translation) + private function collectMessage($locale, $domain, $id, $translation, $parameters = array(), $number = null) { if (null === $domain) { $domain = 'messages'; @@ -146,6 +148,8 @@ private function collectMessage($locale, $domain, $id, $translation) 'domain' => $domain, 'id' => $id, 'translation' => $translation, + 'parameters' => $parameters, + 'transChoiceNumber' => $number, 'state' => $state, ); } diff --git a/src/Symfony/Component/Translation/Dumper/FileDumper.php b/src/Symfony/Component/Translation/Dumper/FileDumper.php index f2f17d64fde16..7620526f08e53 100644 --- a/src/Symfony/Component/Translation/Dumper/FileDumper.php +++ b/src/Symfony/Component/Translation/Dumper/FileDumper.php @@ -82,10 +82,26 @@ public function dump(MessageCatalogue $messages, $options = array()) } } // save file - file_put_contents($fullpath, $this->format($messages, $domain)); + file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options)); } } + /** + * Transforms a domain of a message catalogue to its string representation. + * + * Override this function in child class if $options is used for message formatting. + * + * @param MessageCatalogue $messages + * @param string $domain + * @param array $options + * + * @return string representation + */ + protected function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + { + return $this->format($messages, $domain); + } + /** * Transforms a domain of a message catalogue to its string representation. * diff --git a/src/Symfony/Component/Translation/Dumper/YamlFileDumper.php b/src/Symfony/Component/Translation/Dumper/YamlFileDumper.php index 870fb9838042d..e27f8b376dbed 100644 --- a/src/Symfony/Component/Translation/Dumper/YamlFileDumper.php +++ b/src/Symfony/Component/Translation/Dumper/YamlFileDumper.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Translation\Dumper; use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Util\ArrayConverter; use Symfony\Component\Yaml\Yaml; /** @@ -24,13 +25,31 @@ class YamlFileDumper extends FileDumper /** * {@inheritdoc} */ - protected function format(MessageCatalogue $messages, $domain) + protected function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) { if (!class_exists('Symfony\Component\Yaml\Yaml')) { throw new \LogicException('Dumping translations in the YAML format requires the Symfony Yaml component.'); } - return Yaml::dump($messages->all($domain)); + $data = $messages->all($domain); + + if (isset($options['as_tree']) && $options['as_tree']) { + $data = ArrayConverter::expandToTree($data); + } + + if (isset($options['inline']) && ($inline = (int) $options['inline']) > 0) { + return Yaml::dump($data, $inline); + } + + return Yaml::dump($data); + } + + /** + * {@inheritdoc} + */ + protected function format(MessageCatalogue $messages, $domain) + { + return $this->formatCatalogue($messages, $domain); } /** diff --git a/src/Symfony/Component/Translation/Loader/CsvFileLoader.php b/src/Symfony/Component/Translation/Loader/CsvFileLoader.php index fc927601d900e..b6f0966371df1 100644 --- a/src/Symfony/Component/Translation/Loader/CsvFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/CsvFileLoader.php @@ -11,9 +11,7 @@ namespace Symfony\Component\Translation\Loader; -use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; -use Symfony\Component\Config\Resource\FileResource; /** * CsvFileLoader loads translations from CSV files. @@ -22,7 +20,7 @@ * * @api */ -class CsvFileLoader extends ArrayLoader +class CsvFileLoader extends FileLoader { private $delimiter = ';'; private $enclosure = '"'; @@ -30,19 +28,9 @@ class CsvFileLoader extends ArrayLoader /** * {@inheritdoc} - * - * @api */ - public function load($resource, $locale, $domain = 'messages') + protected function loadResource($resource) { - if (!stream_is_local($resource)) { - throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); - } - - if (!file_exists($resource)) { - throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); - } - $messages = array(); try { @@ -70,13 +58,7 @@ public function load($resource, $locale, $domain = 'messages') } } - $catalogue = parent::load($messages, $locale, $domain); - - if (class_exists('Symfony\Component\Config\Resource\FileResource')) { - $catalogue->addResource(new FileResource($resource)); - } - - return $catalogue; + return $messages; } /** diff --git a/src/Symfony/Component/Translation/Loader/FileLoader.php b/src/Symfony/Component/Translation/Loader/FileLoader.php new file mode 100644 index 0000000000000..a7f24f41a6535 --- /dev/null +++ b/src/Symfony/Component/Translation/Loader/FileLoader.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Loader; + +use Symfony\Component\Translation\Exception\InvalidResourceException; +use Symfony\Component\Translation\Exception\NotFoundResourceException; +use Symfony\Component\Config\Resource\FileResource; + +/** + * @author Abdellatif Ait boudad + */ +abstract class FileLoader extends ArrayLoader +{ + /** + * {@inheritdoc} + */ + public function load($resource, $locale, $domain = 'messages') + { + if (!stream_is_local($resource)) { + throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); + } + + if (!file_exists($resource)) { + throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); + } + + $messages = $this->loadResource($resource); + + // empty resource + if (null === $messages) { + $messages = array(); + } + + // not an array + if (!is_array($messages)) { + throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource)); + } + + $catalogue = parent::load($messages, $locale, $domain); + + if (class_exists('Symfony\Component\Config\Resource\FileResource')) { + $catalogue->addResource(new FileResource($resource)); + } + + return $catalogue; + } + + /* + * @param string $resource + * + * @return array + * + * @throws InvalidResourceException If stream content has an invalid format. + */ + abstract protected function loadResource($resource); +} diff --git a/src/Symfony/Component/Translation/Loader/IniFileLoader.php b/src/Symfony/Component/Translation/Loader/IniFileLoader.php index 1b3a7b19118ff..11d9b272e0a39 100644 --- a/src/Symfony/Component/Translation/Loader/IniFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/IniFileLoader.php @@ -11,38 +11,18 @@ namespace Symfony\Component\Translation\Loader; -use Symfony\Component\Translation\Exception\InvalidResourceException; -use Symfony\Component\Translation\Exception\NotFoundResourceException; -use Symfony\Component\Config\Resource\FileResource; - /** * IniFileLoader loads translations from an ini file. * * @author stealth35 */ -class IniFileLoader extends ArrayLoader +class IniFileLoader extends FileLoader { /** * {@inheritdoc} */ - public function load($resource, $locale, $domain = 'messages') + protected function loadResource($resource) { - if (!stream_is_local($resource)) { - throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); - } - - if (!file_exists($resource)) { - throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); - } - - $messages = parse_ini_file($resource, true); - - $catalogue = parent::load($messages, $locale, $domain); - - if (class_exists('Symfony\Component\Config\Resource\FileResource')) { - $catalogue->addResource(new FileResource($resource)); - } - - return $catalogue; + return parse_ini_file($resource, true); } } diff --git a/src/Symfony/Component/Translation/Loader/JsonFileLoader.php b/src/Symfony/Component/Translation/Loader/JsonFileLoader.php index 81717f3d9418f..ce4e91ff4fbee 100644 --- a/src/Symfony/Component/Translation/Loader/JsonFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/JsonFileLoader.php @@ -12,29 +12,19 @@ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Translation\Exception\InvalidResourceException; -use Symfony\Component\Translation\Exception\NotFoundResourceException; -use Symfony\Component\Config\Resource\FileResource; /** * JsonFileLoader loads translations from an json file. * * @author singles */ -class JsonFileLoader extends ArrayLoader +class JsonFileLoader extends FileLoader { /** * {@inheritdoc} */ - public function load($resource, $locale, $domain = 'messages') + protected function loadResource($resource) { - if (!stream_is_local($resource)) { - throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); - } - - if (!file_exists($resource)) { - throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); - } - $messages = array(); if ($data = file_get_contents($resource)) { $messages = json_decode($data, true); @@ -44,14 +34,7 @@ public function load($resource, $locale, $domain = 'messages') } } - if (null === $messages) { - $messages = array(); - } - - $catalogue = parent::load($messages, $locale, $domain); - $catalogue->addResource(new FileResource($resource)); - - return $catalogue; + return $messages; } /** diff --git a/src/Symfony/Component/Translation/Loader/MoFileLoader.php b/src/Symfony/Component/Translation/Loader/MoFileLoader.php index 746fd52904347..c27a88e3d275e 100644 --- a/src/Symfony/Component/Translation/Loader/MoFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/MoFileLoader.php @@ -12,13 +12,11 @@ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Translation\Exception\InvalidResourceException; -use Symfony\Component\Translation\Exception\NotFoundResourceException; -use Symfony\Component\Config\Resource\FileResource; /** * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/) */ -class MoFileLoader extends ArrayLoader +class MoFileLoader extends FileLoader { /** * Magic used for validating the format of a MO file as well as @@ -43,48 +41,13 @@ class MoFileLoader extends ArrayLoader */ const MO_HEADER_SIZE = 28; - public function load($resource, $locale, $domain = 'messages') - { - if (!stream_is_local($resource)) { - throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); - } - - if (!file_exists($resource)) { - throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); - } - - $messages = $this->parse($resource); - - // empty file - if (null === $messages) { - $messages = array(); - } - - // not an array - if (!is_array($messages)) { - throw new InvalidResourceException(sprintf('The file "%s" must contain a valid mo file.', $resource)); - } - - $catalogue = parent::load($messages, $locale, $domain); - - if (class_exists('Symfony\Component\Config\Resource\FileResource')) { - $catalogue->addResource(new FileResource($resource)); - } - - return $catalogue; - } - /** * Parses machine object (MO) format, independent of the machine's endian it * was created on. Both 32bit and 64bit systems are supported. * - * @param resource $resource - * - * @return array - * - * @throws InvalidResourceException If stream content has an invalid format. + * {@inheritdoc} */ - private function parse($resource) + protected function loadResource($resource) { $stream = fopen($resource, 'r'); diff --git a/src/Symfony/Component/Translation/Loader/PhpFileLoader.php b/src/Symfony/Component/Translation/Loader/PhpFileLoader.php index 9ce2e7d2fa0de..88f4cdb113a08 100644 --- a/src/Symfony/Component/Translation/Loader/PhpFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/PhpFileLoader.php @@ -11,10 +11,6 @@ namespace Symfony\Component\Translation\Loader; -use Symfony\Component\Translation\Exception\InvalidResourceException; -use Symfony\Component\Translation\Exception\NotFoundResourceException; -use Symfony\Component\Config\Resource\FileResource; - /** * PhpFileLoader loads translations from PHP files returning an array of translations. * @@ -22,31 +18,13 @@ * * @api */ -class PhpFileLoader extends ArrayLoader +class PhpFileLoader extends FileLoader { /** * {@inheritdoc} - * - * @api */ - public function load($resource, $locale, $domain = 'messages') + protected function loadResource($resource) { - if (!stream_is_local($resource)) { - throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); - } - - if (!file_exists($resource)) { - throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); - } - - $messages = require $resource; - - $catalogue = parent::load($messages, $locale, $domain); - - if (class_exists('Symfony\Component\Config\Resource\FileResource')) { - $catalogue->addResource(new FileResource($resource)); - } - - return $catalogue; + return require $resource; } } diff --git a/src/Symfony/Component/Translation/Loader/PoFileLoader.php b/src/Symfony/Component/Translation/Loader/PoFileLoader.php index b5d12e9821643..84664d65a0acc 100644 --- a/src/Symfony/Component/Translation/Loader/PoFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/PoFileLoader.php @@ -11,47 +11,12 @@ namespace Symfony\Component\Translation\Loader; -use Symfony\Component\Translation\Exception\InvalidResourceException; -use Symfony\Component\Translation\Exception\NotFoundResourceException; -use Symfony\Component\Config\Resource\FileResource; - /** * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/) * @copyright Copyright (c) 2012, Clemens Tolboom */ -class PoFileLoader extends ArrayLoader +class PoFileLoader extends FileLoader { - public function load($resource, $locale, $domain = 'messages') - { - if (!stream_is_local($resource)) { - throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); - } - - if (!file_exists($resource)) { - throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); - } - - $messages = $this->parse($resource); - - // empty file - if (null === $messages) { - $messages = array(); - } - - // not an array - if (!is_array($messages)) { - throw new InvalidResourceException(sprintf('The file "%s" must contain a valid po file.', $resource)); - } - - $catalogue = parent::load($messages, $locale, $domain); - - if (class_exists('Symfony\Component\Config\Resource\FileResource')) { - $catalogue->addResource(new FileResource($resource)); - } - - return $catalogue; - } - /** * Parses portable object (PO) format. * @@ -93,11 +58,9 @@ public function load($resource, $locale, $domain = 'messages') * * Items with an empty id are ignored. * - * @param resource $resource - * - * @return array + * {@inheritdoc} */ - private function parse($resource) + protected function loadResource($resource) { $stream = fopen($resource, 'r'); diff --git a/src/Symfony/Component/Translation/Loader/YamlFileLoader.php b/src/Symfony/Component/Translation/Loader/YamlFileLoader.php index fb0946cc577f5..1abe8cc302c26 100644 --- a/src/Symfony/Component/Translation/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/YamlFileLoader.php @@ -12,8 +12,6 @@ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Translation\Exception\InvalidResourceException; -use Symfony\Component\Translation\Exception\NotFoundResourceException; -use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Yaml\Parser as YamlParser; use Symfony\Component\Yaml\Exception\ParseException; @@ -24,30 +22,20 @@ * * @api */ -class YamlFileLoader extends ArrayLoader +class YamlFileLoader extends FileLoader { private $yamlParser; /** * {@inheritdoc} - * - * @api */ - public function load($resource, $locale, $domain = 'messages') + protected function loadResource($resource) { - if (!stream_is_local($resource)) { - throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); - } - - if (!file_exists($resource)) { - throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); - } - - if (!class_exists('Symfony\Component\Yaml\Parser')) { - throw new \LogicException('Loading translations from the YAML format requires the Symfony Yaml component.'); - } - if (null === $this->yamlParser) { + if (!class_exists('Symfony\Component\Yaml\Parser')) { + throw new \LogicException('Loading translations from the YAML format requires the Symfony Yaml component.'); + } + $this->yamlParser = new YamlParser(); } @@ -57,22 +45,6 @@ public function load($resource, $locale, $domain = 'messages') throw new InvalidResourceException(sprintf('Error parsing YAML, invalid file "%s"', $resource), 0, $e); } - // empty file - if (null === $messages) { - $messages = array(); - } - - // not an array - if (!is_array($messages)) { - throw new InvalidResourceException(sprintf('The file "%s" must contain a YAML array.', $resource)); - } - - $catalogue = parent::load($messages, $locale, $domain); - - if (class_exists('Symfony\Component\Config\Resource\FileResource')) { - $catalogue->addResource(new FileResource($resource)); - } - - return $catalogue; + return $messages; } } diff --git a/src/Symfony/Component/Translation/README.md b/src/Symfony/Component/Translation/README.md index 2b008629578c3..fc6901a8ac33a 100644 --- a/src/Symfony/Component/Translation/README.md +++ b/src/Symfony/Component/Translation/README.md @@ -28,7 +28,7 @@ https://github.com/fabpot/Silex/blob/master/src/Silex/Provider/TranslationServic Documentation: -https://symfony.com/doc/2.7/book/translation.html +https://symfony.com/doc/2.8/book/translation.html You can run the unit tests with the following command: diff --git a/src/Symfony/Component/Translation/Tests/DataCollector/TranslationDataCollectorTest.php b/src/Symfony/Component/Translation/Tests/DataCollector/TranslationDataCollectorTest.php index 085d31267b3a4..3d1e86e22cbed 100644 --- a/src/Symfony/Component/Translation/Tests/DataCollector/TranslationDataCollectorTest.php +++ b/src/Symfony/Component/Translation/Tests/DataCollector/TranslationDataCollectorTest.php @@ -46,6 +46,8 @@ public function testCollect() 'locale' => 'en', 'domain' => 'messages', 'state' => DataCollectorTranslator::MESSAGE_DEFINED, + 'parameters' => array(), + 'transChoiceNumber' => null, ), array( 'id' => 'bar', @@ -53,6 +55,8 @@ public function testCollect() 'locale' => 'fr', 'domain' => 'messages', 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK, + 'parameters' => array(), + 'transChoiceNumber' => null, ), array( 'id' => 'choice', @@ -60,6 +64,8 @@ public function testCollect() 'locale' => 'en', 'domain' => 'messages', 'state' => DataCollectorTranslator::MESSAGE_MISSING, + 'parameters' => array('%count%' => 3), + 'transChoiceNumber' => 3, ), array( 'id' => 'choice', @@ -67,6 +73,17 @@ public function testCollect() 'locale' => 'en', 'domain' => 'messages', 'state' => DataCollectorTranslator::MESSAGE_MISSING, + 'parameters' => array('%count%' => 3), + 'transChoiceNumber' => 3, + ), + array( + 'id' => 'choice', + 'translation' => 'choice', + 'locale' => 'en', + 'domain' => 'messages', + 'state' => DataCollectorTranslator::MESSAGE_MISSING, + 'parameters' => array('%count%' => 4, '%foo%' => 'bar'), + 'transChoiceNumber' => 4, ), ); $expectedMessages = array( @@ -77,6 +94,8 @@ public function testCollect() 'domain' => 'messages', 'state' => DataCollectorTranslator::MESSAGE_DEFINED, 'count' => 1, + 'parameters' => array(), + 'transChoiceNumber' => null, ), array( 'id' => 'bar', @@ -85,6 +104,8 @@ public function testCollect() 'domain' => 'messages', 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK, 'count' => 1, + 'parameters' => array(), + 'transChoiceNumber' => null, ), array( 'id' => 'choice', @@ -92,7 +113,13 @@ public function testCollect() 'locale' => 'en', 'domain' => 'messages', 'state' => DataCollectorTranslator::MESSAGE_MISSING, - 'count' => 2, + 'count' => 3, + 'parameters' => array( + array('%count%' => 3), + array('%count%' => 3), + array('%count%' => 4, '%foo%' => 'bar'), + ), + 'transChoiceNumber' => 3, ), ); diff --git a/src/Symfony/Component/Translation/Tests/DataCollectorTranslatorTest.php b/src/Symfony/Component/Translation/Tests/DataCollectorTranslatorTest.php index 8e98ad31f43c8..31be3f2f15ebe 100644 --- a/src/Symfony/Component/Translation/Tests/DataCollectorTranslatorTest.php +++ b/src/Symfony/Component/Translation/Tests/DataCollectorTranslatorTest.php @@ -32,6 +32,7 @@ public function testCollectMessages() $collector->trans('bar'); $collector->transChoice('choice', 0); $collector->trans('bar_ru'); + $collector->trans('bar_ru', array('foo' => 'bar')); $expectedMessages = array(); $expectedMessages[] = array( @@ -40,6 +41,8 @@ public function testCollectMessages() 'locale' => 'en', 'domain' => 'messages', 'state' => DataCollectorTranslator::MESSAGE_DEFINED, + 'parameters' => array(), + 'transChoiceNumber' => null, ); $expectedMessages[] = array( 'id' => 'bar', @@ -47,6 +50,8 @@ public function testCollectMessages() 'locale' => 'fr', 'domain' => 'messages', 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK, + 'parameters' => array(), + 'transChoiceNumber' => null, ); $expectedMessages[] = array( 'id' => 'choice', @@ -54,6 +59,8 @@ public function testCollectMessages() 'locale' => 'en', 'domain' => 'messages', 'state' => DataCollectorTranslator::MESSAGE_MISSING, + 'parameters' => array(), + 'transChoiceNumber' => 0, ); $expectedMessages[] = array( 'id' => 'bar_ru', @@ -61,6 +68,17 @@ public function testCollectMessages() 'locale' => 'ru', 'domain' => 'messages', 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK, + 'parameters' => array(), + 'transChoiceNumber' => null, + ); + $expectedMessages[] = array( + 'id' => 'bar_ru', + 'translation' => 'bar (ru)', + 'locale' => 'ru', + 'domain' => 'messages', + 'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK, + 'parameters' => array('foo' => 'bar'), + 'transChoiceNumber' => null, ); $this->assertEquals($expectedMessages, $collector->getCollectedMessages()); diff --git a/src/Symfony/Component/Translation/Tests/Dumper/YamlFileDumperTest.php b/src/Symfony/Component/Translation/Tests/Dumper/YamlFileDumperTest.php index 3c68ade753114..161925cd23bf4 100644 --- a/src/Symfony/Component/Translation/Tests/Dumper/YamlFileDumperTest.php +++ b/src/Symfony/Component/Translation/Tests/Dumper/YamlFileDumperTest.php @@ -16,16 +16,38 @@ class YamlFileDumperTest extends \PHPUnit_Framework_TestCase { - public function testDump() + public function testTreeDump() { $catalogue = new MessageCatalogue('en'); - $catalogue->add(array('foo' => 'bar')); + $catalogue->add( + array( + 'foo.bar1' => 'value1', + 'foo.bar2' => 'value2', + )); + + $tempDir = sys_get_temp_dir(); + $dumper = new YamlFileDumper(); + $dumper->dump($catalogue, array('path' => $tempDir, 'as_tree' => true, 'inline' => 999)); + + $this->assertEquals(file_get_contents(__DIR__.'/../fixtures/messages.yml'), file_get_contents($tempDir.'/messages.en.yml')); + + unlink($tempDir.'/messages.en.yml'); + } + + public function testLinearDump() + { + $catalogue = new MessageCatalogue('en'); + $catalogue->add( + array( + 'foo.bar1' => 'value1', + 'foo.bar2' => 'value2', + )); $tempDir = sys_get_temp_dir(); $dumper = new YamlFileDumper(); $dumper->dump($catalogue, array('path' => $tempDir)); - $this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources.yml'), file_get_contents($tempDir.'/messages.en.yml')); + $this->assertEquals(file_get_contents(__DIR__.'/../fixtures/messages_linear.yml'), file_get_contents($tempDir.'/messages.en.yml')); unlink($tempDir.'/messages.en.yml'); } diff --git a/src/Symfony/Component/Translation/Tests/TranslatorTest.php b/src/Symfony/Component/Translation/Tests/TranslatorTest.php index cb361831883e9..e8e967139dac2 100644 --- a/src/Symfony/Component/Translation/Tests/TranslatorTest.php +++ b/src/Symfony/Component/Translation/Tests/TranslatorTest.php @@ -497,9 +497,10 @@ public function testTransChoiceFallbackWithNoTranslation() } /** + * @group legacy * @dataProvider dataProviderGetMessages */ - public function testGetMessages($resources, $locale, $expected) + public function testLegacyGetMessages($resources, $locale, $expected) { $locales = array_keys($resources); $_locale = null !== $locale ? $locale : reset($locales); diff --git a/src/Symfony/Component/Translation/Tests/Util/ArrayConverterTest.php b/src/Symfony/Component/Translation/Tests/Util/ArrayConverterTest.php new file mode 100644 index 0000000000000..9eea275be6b90 --- /dev/null +++ b/src/Symfony/Component/Translation/Tests/Util/ArrayConverterTest.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Util; + +use Symfony\Component\Translation\Util\ArrayConverter; + +class ArrayConverterTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider messsagesData + */ + public function testDump($input, $expectedOutput) + { + $this->assertEquals($expectedOutput, ArrayConverter::expandToTree($input)); + } + + public function messsagesData() + { + return array( + array( + // input + array( + 'foo1' => 'bar', + 'foo.bar' => 'value', + ), + // expected output + array( + 'foo1' => 'bar', + 'foo' => array('bar' => 'value'), + ), + ), + array( + // input + array( + 'foo.bar' => 'value1', + 'foo.bar.test' => 'value2', + ), + // expected output + array( + 'foo' => array( + 'bar' => 'value1', + 'bar.test' => 'value2', + ), + ), + ), + array( + // input + array( + 'foo.level2.level3.level4' => 'value1', + 'foo.level2' => 'value2', + 'foo.bar' => 'value3', + ), + // expected output + array( + 'foo' => array( + 'level2' => 'value2', + 'level2.level3.level4' => 'value1', + 'bar' => 'value3', + ), + ), + ), + ); + } +} diff --git a/src/Symfony/Component/Translation/Tests/fixtures/messages.yml b/src/Symfony/Component/Translation/Tests/fixtures/messages.yml new file mode 100644 index 0000000000000..d4f82d78106a9 --- /dev/null +++ b/src/Symfony/Component/Translation/Tests/fixtures/messages.yml @@ -0,0 +1,3 @@ +foo: + bar1: value1 + bar2: value2 diff --git a/src/Symfony/Component/Translation/Tests/fixtures/messages_linear.yml b/src/Symfony/Component/Translation/Tests/fixtures/messages_linear.yml new file mode 100644 index 0000000000000..6c1687d0a3698 --- /dev/null +++ b/src/Symfony/Component/Translation/Tests/fixtures/messages_linear.yml @@ -0,0 +1,2 @@ +foo.bar1: value1 +foo.bar2: value2 diff --git a/src/Symfony/Component/Translation/Translator.php b/src/Symfony/Component/Translation/Translator.php index 9b1b73550c57f..9832c3f31c0bd 100644 --- a/src/Symfony/Component/Translation/Translator.php +++ b/src/Symfony/Component/Translation/Translator.php @@ -289,9 +289,13 @@ protected function getLoaders() * @param string|null $locale Locale of translations, by default is current locale * * @return array[array] indexed by catalog + * + * @deprecated since version 2.8, to be removed in 3.0. Use TranslatorBagInterface::getCatalogue() method instead. */ public function getMessages($locale = null) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Use TranslatorBagInterface::getCatalogue() method instead.', E_USER_DEPRECATED); + $catalogue = $this->getCatalogue($locale); $messages = $catalogue->all(); while ($catalogue = $catalogue->getFallbackCatalogue()) { diff --git a/src/Symfony/Component/Translation/Util/ArrayConverter.php b/src/Symfony/Component/Translation/Util/ArrayConverter.php new file mode 100644 index 0000000000000..60a55e9d3709c --- /dev/null +++ b/src/Symfony/Component/Translation/Util/ArrayConverter.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Util; + +/** + * ArrayConverter generates tree like structure from a message catalogue. + * e.g. this + * 'foo.bar1' => 'test1', + * 'foo.bar2' => 'test2' + * converts to follows: + * foo: + * bar1: test1 + * bar2: test2. + * + * @author Gennady Telegin + */ +class ArrayConverter +{ + /** + * Converts linear messages array to tree-like array. + * For example this rray('foo.bar' => 'value') will be converted to array('foo' => array('bar' => 'value')). + * + * @param array $messages Linear messages array + * + * @return array Tree-like messages array + */ + public static function expandToTree(array $messages) + { + $tree = array(); + + foreach ($messages as $id => $value) { + $referenceToElement = &self::getElementByPath($tree, explode('.', $id)); + + $referenceToElement = $value; + + unset($referenceToElement); + } + + return $tree; + } + + private static function &getElementByPath(array &$tree, array $parts) + { + $elem = &$tree; + $parentOfElem = null; + + foreach ($parts as $i => $part) { + if (isset($elem[$part]) && is_string($elem[$part])) { + /* Process next case: + * 'foo': 'test1', + * 'foo.bar': 'test2' + * + * $tree['foo'] was string before we found array {bar: test2}. + * Treat new element as string too, e.g. add $tree['foo.bar'] = 'test2'; + */ + $elem = &$elem[ implode('.', array_slice($parts, $i)) ]; + break; + } + $parentOfElem = &$elem; + $elem = &$elem[$part]; + } + + if (is_array($elem) && count($elem) > 0 && $parentOfElem) { + /* Process next case: + * 'foo.bar': 'test1' + * 'foo': 'test2' + * + * $tree['foo'] was array = {bar: 'test1'} before we found string constant `foo`. + * Cancel treating $tree['foo'] as array and cancel back it expansion, + * e.g. make it $tree['foo.bar'] = 'test1' again. + */ + self::cancelExpand($parentOfElem, $part, $elem); + } + + return $elem; + } + + private static function cancelExpand(array &$tree, $prefix, array $node) + { + $prefix .= '.'; + + foreach ($node as $id => $value) { + if (is_string($value)) { + $tree[$prefix.$id] = $value; + } else { + self::cancelExpand($tree, $prefix.$id, $value); + } + } + } +} diff --git a/src/Symfony/Component/Translation/composer.json b/src/Symfony/Component/Translation/composer.json index 6f1053e66caf0..e428dd04f3e15 100644 --- a/src/Symfony/Component/Translation/composer.json +++ b/src/Symfony/Component/Translation/composer.json @@ -19,10 +19,10 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", + "symfony/phpunit-bridge": "~2.7|~3.0.0", "symfony/config": "~2.7", - "symfony/intl": "~2.3", - "symfony/yaml": "~2.2", + "symfony/intl": "~2.3|~3.0.0", + "symfony/yaml": "~2.2|~3.0.0", "psr/log": "~1.0" }, "conflict": { @@ -39,7 +39,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Validator/ConstraintViolationInterface.php b/src/Symfony/Component/Validator/ConstraintViolationInterface.php index 232fb5513f768..d772b45d1256c 100644 --- a/src/Symfony/Component/Validator/ConstraintViolationInterface.php +++ b/src/Symfony/Component/Validator/ConstraintViolationInterface.php @@ -130,7 +130,7 @@ public function getInvalidValue(); /** * Returns a machine-digestible error code for the violation. * - * @return mixed The error code. + * @return string|null The error code. */ public function getCode(); } diff --git a/src/Symfony/Component/Validator/Constraints/AbstractComparison.php b/src/Symfony/Component/Validator/Constraints/AbstractComparison.php index fb1f1f3ef7c75..78db943f74f14 100644 --- a/src/Symfony/Component/Validator/Constraints/AbstractComparison.php +++ b/src/Symfony/Component/Validator/Constraints/AbstractComparison.php @@ -18,6 +18,7 @@ * Used for the comparison of values. * * @author Daniel Holmes + * @author Bernhard Schussek */ abstract class AbstractComparison extends Constraint { diff --git a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php index 67d73a5ee25f8..ce0487403ba8a 100644 --- a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php +++ b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php @@ -60,12 +60,14 @@ public function validate($value, Constraint $constraint) ->setParameter('{{ value }}', $this->formatValue($value, self::OBJECT_TO_STRING | self::PRETTY_DATE)) ->setParameter('{{ compared_value }}', $this->formatValue($comparedValue, self::OBJECT_TO_STRING | self::PRETTY_DATE)) ->setParameter('{{ compared_value_type }}', $this->formatTypeOf($comparedValue)) + ->setCode($this->getErrorCode()) ->addViolation(); } else { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value, self::OBJECT_TO_STRING | self::PRETTY_DATE)) ->setParameter('{{ compared_value }}', $this->formatValue($comparedValue, self::OBJECT_TO_STRING | self::PRETTY_DATE)) ->setParameter('{{ compared_value_type }}', $this->formatTypeOf($comparedValue)) + ->setCode($this->getErrorCode()) ->addViolation(); } } @@ -80,4 +82,13 @@ public function validate($value, Constraint $constraint) * @return bool true if the relationship is valid, false otherwise */ abstract protected function compareValues($value1, $value2); + + /** + * Returns the error code used if the comparison fails. + * + * @return string|null The error code or `null` if no code should be set + */ + protected function getErrorCode() + { + } } diff --git a/src/Symfony/Component/Validator/Constraints/Blank.php b/src/Symfony/Component/Validator/Constraints/Blank.php index 766ce6c7bbbec..f74050a1c75b5 100644 --- a/src/Symfony/Component/Validator/Constraints/Blank.php +++ b/src/Symfony/Component/Validator/Constraints/Blank.php @@ -23,5 +23,11 @@ */ class Blank extends Constraint { + const NOT_BLANK_ERROR = '183ad2de-533d-4796-a439-6d3c3852b549'; + + protected static $errorNames = array( + self::NOT_BLANK_ERROR => 'NOT_BLANK_ERROR', + ); + public $message = 'This value should be blank.'; } diff --git a/src/Symfony/Component/Validator/Constraints/BlankValidator.php b/src/Symfony/Component/Validator/Constraints/BlankValidator.php index 2d26e4a91b808..2d76c77e8fc87 100644 --- a/src/Symfony/Component/Validator/Constraints/BlankValidator.php +++ b/src/Symfony/Component/Validator/Constraints/BlankValidator.php @@ -36,10 +36,12 @@ public function validate($value, Constraint $constraint) if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Blank::NOT_BLANK_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Blank::NOT_BLANK_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/CardScheme.php b/src/Symfony/Component/Validator/Constraints/CardScheme.php index 14f3b5d8cf0c1..40c32e879e83f 100644 --- a/src/Symfony/Component/Validator/Constraints/CardScheme.php +++ b/src/Symfony/Component/Validator/Constraints/CardScheme.php @@ -24,8 +24,8 @@ */ class CardScheme extends Constraint { - const NOT_NUMERIC_ERROR = 1; - const INVALID_FORMAT_ERROR = 2; + const NOT_NUMERIC_ERROR = 'a2ad9231-e827-485f-8a1e-ef4d9a6d5c2e'; + const INVALID_FORMAT_ERROR = 'a8faedbf-1c2f-4695-8d22-55783be8efed'; protected static $errorNames = array( self::NOT_NUMERIC_ERROR => 'NOT_NUMERIC_ERROR', diff --git a/src/Symfony/Component/Validator/Constraints/Choice.php b/src/Symfony/Component/Validator/Constraints/Choice.php index 39a64574d0d29..302df202b0991 100644 --- a/src/Symfony/Component/Validator/Constraints/Choice.php +++ b/src/Symfony/Component/Validator/Constraints/Choice.php @@ -23,9 +23,9 @@ */ class Choice extends Constraint { - const NO_SUCH_CHOICE_ERROR = 1; - const TOO_FEW_ERROR = 2; - const TOO_MANY_ERROR = 3; + const NO_SUCH_CHOICE_ERROR = '8e179f1b-97aa-4560-a02f-2a8b42e49df7'; + const TOO_FEW_ERROR = '11edd7eb-5872-4b6e-9f12-89923999fd0e'; + const TOO_MANY_ERROR = '9bd98e49-211c-433f-8630-fd1c2d0f08c3'; protected static $errorNames = array( self::NO_SUCH_CHOICE_ERROR => 'NO_SUCH_CHOICE_ERROR', diff --git a/src/Symfony/Component/Validator/Constraints/Collection.php b/src/Symfony/Component/Validator/Constraints/Collection.php index 708c8ed47730e..57fc93832fff3 100644 --- a/src/Symfony/Component/Validator/Constraints/Collection.php +++ b/src/Symfony/Component/Validator/Constraints/Collection.php @@ -23,8 +23,8 @@ */ class Collection extends Composite { - const MISSING_FIELD_ERROR = 1; - const NO_SUCH_FIELD_ERROR = 2; + const MISSING_FIELD_ERROR = '2fa2158c-2a7f-484b-98aa-975522539ff8'; + const NO_SUCH_FIELD_ERROR = '7703c766-b5d5-4cef-ace7-ae0dd82304e9'; protected static $errorNames = array( self::MISSING_FIELD_ERROR => 'MISSING_FIELD_ERROR', diff --git a/src/Symfony/Component/Validator/Constraints/Count.php b/src/Symfony/Component/Validator/Constraints/Count.php index a3e12fe1342d8..4fe97104692b1 100644 --- a/src/Symfony/Component/Validator/Constraints/Count.php +++ b/src/Symfony/Component/Validator/Constraints/Count.php @@ -24,8 +24,8 @@ */ class Count extends Constraint { - const TOO_FEW_ERROR = 1; - const TOO_MANY_ERROR = 2; + const TOO_FEW_ERROR = 'bef8e338-6ae5-4caf-b8e2-50e7b0579e69'; + const TOO_MANY_ERROR = '756b1212-697c-468d-a9ad-50dd783bb169'; protected static $errorNames = array( self::TOO_FEW_ERROR => 'TOO_FEW_ERROR', diff --git a/src/Symfony/Component/Validator/Constraints/Country.php b/src/Symfony/Component/Validator/Constraints/Country.php index ff6f3d0e0a539..c3e91fb123cf6 100644 --- a/src/Symfony/Component/Validator/Constraints/Country.php +++ b/src/Symfony/Component/Validator/Constraints/Country.php @@ -23,5 +23,11 @@ */ class Country extends Constraint { + const NO_SUCH_COUNTRY_ERROR = '8f900c12-61bd-455d-9398-996cd040f7f0'; + + protected static $errorNames = array( + self::NO_SUCH_COUNTRY_ERROR => 'NO_SUCH_COUNTRY_ERROR', + ); + public $message = 'This value is not a valid country.'; } diff --git a/src/Symfony/Component/Validator/Constraints/CountryValidator.php b/src/Symfony/Component/Validator/Constraints/CountryValidator.php index 8139adf943119..0e1d3851969f2 100644 --- a/src/Symfony/Component/Validator/Constraints/CountryValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CountryValidator.php @@ -50,10 +50,12 @@ public function validate($value, Constraint $constraint) if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Country::NO_SUCH_COUNTRY_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Country::NO_SUCH_COUNTRY_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Currency.php b/src/Symfony/Component/Validator/Constraints/Currency.php index c09fe88bf27e9..9f68afa46a4eb 100644 --- a/src/Symfony/Component/Validator/Constraints/Currency.php +++ b/src/Symfony/Component/Validator/Constraints/Currency.php @@ -18,10 +18,17 @@ * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) * * @author Miha Vrhovnik + * @author Bernhard Schussek * * @api */ class Currency extends Constraint { + const NO_SUCH_CURRENCY_ERROR = '69945ac1-2db4-405f-bec7-d2772f73df52'; + + protected static $errorNames = array( + self::NO_SUCH_CURRENCY_ERROR => 'NO_SUCH_CURRENCY_ERROR', + ); + public $message = 'This value is not a valid currency.'; } diff --git a/src/Symfony/Component/Validator/Constraints/CurrencyValidator.php b/src/Symfony/Component/Validator/Constraints/CurrencyValidator.php index 9c41dc4a100ee..e06e911b4f44c 100644 --- a/src/Symfony/Component/Validator/Constraints/CurrencyValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CurrencyValidator.php @@ -21,6 +21,7 @@ * Validates whether a value is a valid currency. * * @author Miha Vrhovnik + * @author Bernhard Schussek * * @api */ @@ -50,10 +51,12 @@ public function validate($value, Constraint $constraint) if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Currency::NO_SUCH_CURRENCY_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Currency::NO_SUCH_CURRENCY_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Date.php b/src/Symfony/Component/Validator/Constraints/Date.php index 2bc444f71af68..f960ac765d06f 100644 --- a/src/Symfony/Component/Validator/Constraints/Date.php +++ b/src/Symfony/Component/Validator/Constraints/Date.php @@ -23,8 +23,8 @@ */ class Date extends Constraint { - const INVALID_FORMAT_ERROR = 1; - const INVALID_DATE_ERROR = 2; + const INVALID_FORMAT_ERROR = '69819696-02ac-4a99-9ff0-14e127c4d1bc'; + const INVALID_DATE_ERROR = '3c184ce5-b31d-4de7-8b76-326da7b2be93'; protected static $errorNames = array( self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR', diff --git a/src/Symfony/Component/Validator/Constraints/DateTime.php b/src/Symfony/Component/Validator/Constraints/DateTime.php index ae67ff30efb4b..a1e9d22df73b5 100644 --- a/src/Symfony/Component/Validator/Constraints/DateTime.php +++ b/src/Symfony/Component/Validator/Constraints/DateTime.php @@ -23,9 +23,9 @@ */ class DateTime extends Constraint { - const INVALID_FORMAT_ERROR = 1; - const INVALID_DATE_ERROR = 2; - const INVALID_TIME_ERROR = 3; + const INVALID_FORMAT_ERROR = '1a9da513-2640-4f84-9b6a-4d99dcddc628'; + const INVALID_DATE_ERROR = 'd52afa47-620d-4d99-9f08-f4d85b36e33c'; + const INVALID_TIME_ERROR = '5e797c9d-74f7-4098-baa3-94390c447b27'; protected static $errorNames = array( self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR', diff --git a/src/Symfony/Component/Validator/Constraints/Email.php b/src/Symfony/Component/Validator/Constraints/Email.php index 36977177bdfd1..d1191a43137e4 100644 --- a/src/Symfony/Component/Validator/Constraints/Email.php +++ b/src/Symfony/Component/Validator/Constraints/Email.php @@ -23,9 +23,9 @@ */ class Email extends Constraint { - const INVALID_FORMAT_ERROR = 1; - const MX_CHECK_FAILED_ERROR = 2; - const HOST_CHECK_FAILED_ERROR = 3; + const INVALID_FORMAT_ERROR = 'bd79c0ab-ddba-46cc-a703-a7a4b08de310'; + const MX_CHECK_FAILED_ERROR = 'bf447c1c-0266-4e10-9c6c-573df282e413'; + const HOST_CHECK_FAILED_ERROR = '7da53a8b-56f3-4288-bb3e-ee9ede4ef9a1'; protected static $errorNames = array( self::INVALID_FORMAT_ERROR => 'STRICT_CHECK_FAILED_ERROR', diff --git a/src/Symfony/Component/Validator/Constraints/EqualTo.php b/src/Symfony/Component/Validator/Constraints/EqualTo.php index 8d3d7524ddf91..4b22c6dcca3e1 100644 --- a/src/Symfony/Component/Validator/Constraints/EqualTo.php +++ b/src/Symfony/Component/Validator/Constraints/EqualTo.php @@ -16,8 +16,15 @@ * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) * * @author Daniel Holmes + * @author Bernhard Schussek */ class EqualTo extends AbstractComparison { + const NOT_EQUAL_ERROR = '478618a7-95ba-473d-9101-cabd45e49115'; + + protected static $errorNames = array( + self::NOT_EQUAL_ERROR => 'NOT_EQUAL_ERROR', + ); + public $message = 'This value should be equal to {{ compared_value }}.'; } diff --git a/src/Symfony/Component/Validator/Constraints/EqualToValidator.php b/src/Symfony/Component/Validator/Constraints/EqualToValidator.php index 3739dbebfe7a5..fe1f3620fff29 100644 --- a/src/Symfony/Component/Validator/Constraints/EqualToValidator.php +++ b/src/Symfony/Component/Validator/Constraints/EqualToValidator.php @@ -15,6 +15,7 @@ * Validates values are equal (==). * * @author Daniel Holmes + * @author Bernhard Schussek */ class EqualToValidator extends AbstractComparisonValidator { @@ -25,4 +26,12 @@ protected function compareValues($value1, $value2) { return $value1 == $value2; } + + /** + * {@inheritdoc} + */ + protected function getErrorCode() + { + return EqualTo::NOT_EQUAL_ERROR; + } } diff --git a/src/Symfony/Component/Validator/Constraints/Expression.php b/src/Symfony/Component/Validator/Constraints/Expression.php index dfa242c31a40c..3329bd2494028 100644 --- a/src/Symfony/Component/Validator/Constraints/Expression.php +++ b/src/Symfony/Component/Validator/Constraints/Expression.php @@ -22,6 +22,12 @@ */ class Expression extends Constraint { + const EXPRESSION_FAILED_ERROR = '6b3befbc-2f01-4ddf-be21-b57898905284'; + + protected static $errorNames = array( + self::EXPRESSION_FAILED_ERROR => 'EXPRESSION_FAILED_ERROR', + ); + public $message = 'This value is not valid.'; public $expression; diff --git a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php index 15d51f9ff10c0..031bf420ce530 100644 --- a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php @@ -88,10 +88,12 @@ public function validate($value, Constraint $constraint) if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Expression::EXPRESSION_FAILED_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Expression::EXPRESSION_FAILED_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/File.php b/src/Symfony/Component/Validator/Constraints/File.php index f35f93cdb5e0f..c390819e4a62b 100644 --- a/src/Symfony/Component/Validator/Constraints/File.php +++ b/src/Symfony/Component/Validator/Constraints/File.php @@ -26,11 +26,11 @@ class File extends Constraint { // Check the Image constraint for clashes if adding new constants here - const NOT_FOUND_ERROR = 1; - const NOT_READABLE_ERROR = 2; - const EMPTY_ERROR = 3; - const TOO_LARGE_ERROR = 4; - const INVALID_MIME_TYPE_ERROR = 5; + const NOT_FOUND_ERROR = 'd2a3fb6e-7ddc-4210-8fbf-2ab345ce1998'; + const NOT_READABLE_ERROR = 'c20c92a4-5bfa-4202-9477-28e800e0f6ff'; + const EMPTY_ERROR = '5d743385-9775-4aa5-8ff5-495fb1e60137'; + const TOO_LARGE_ERROR = 'df8637af-d466-48c6-a59d-e7126250a654'; + const INVALID_MIME_TYPE_ERROR = '744f00bc-4389-4c74-92de-9a43cde55534'; protected static $errorNames = array( self::NOT_FOUND_ERROR => 'NOT_FOUND_ERROR', diff --git a/src/Symfony/Component/Validator/Constraints/GreaterThan.php b/src/Symfony/Component/Validator/Constraints/GreaterThan.php index ec7fafb3a4cd6..c2ca2dcb82f23 100644 --- a/src/Symfony/Component/Validator/Constraints/GreaterThan.php +++ b/src/Symfony/Component/Validator/Constraints/GreaterThan.php @@ -16,8 +16,15 @@ * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) * * @author Daniel Holmes + * @author Bernhard Schussek */ class GreaterThan extends AbstractComparison { + const TOO_LOW_ERROR = '778b7ae0-84d3-481a-9dec-35fdb64b1d78'; + + protected static $errorNames = array( + self::TOO_LOW_ERROR => 'TOO_LOW_ERROR', + ); + public $message = 'This value should be greater than {{ compared_value }}.'; } diff --git a/src/Symfony/Component/Validator/Constraints/GreaterThanOrEqual.php b/src/Symfony/Component/Validator/Constraints/GreaterThanOrEqual.php index 36fdd9c0975d2..9b3743d073219 100644 --- a/src/Symfony/Component/Validator/Constraints/GreaterThanOrEqual.php +++ b/src/Symfony/Component/Validator/Constraints/GreaterThanOrEqual.php @@ -16,8 +16,15 @@ * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) * * @author Daniel Holmes + * @author Bernhard Schussek */ class GreaterThanOrEqual extends AbstractComparison { + const TOO_LOW_ERROR = 'ea4e51d1-3342-48bd-87f1-9e672cd90cad'; + + protected static $errorNames = array( + self::TOO_LOW_ERROR => 'TOO_LOW_ERROR', + ); + public $message = 'This value should be greater than or equal to {{ compared_value }}.'; } diff --git a/src/Symfony/Component/Validator/Constraints/GreaterThanOrEqualValidator.php b/src/Symfony/Component/Validator/Constraints/GreaterThanOrEqualValidator.php index 2363204174642..e196e688f3476 100644 --- a/src/Symfony/Component/Validator/Constraints/GreaterThanOrEqualValidator.php +++ b/src/Symfony/Component/Validator/Constraints/GreaterThanOrEqualValidator.php @@ -15,6 +15,7 @@ * Validates values are greater than or equal to the previous (>=). * * @author Daniel Holmes + * @author Bernhard Schussek */ class GreaterThanOrEqualValidator extends AbstractComparisonValidator { @@ -25,4 +26,12 @@ protected function compareValues($value1, $value2) { return $value1 >= $value2; } + + /** + * {@inheritdoc} + */ + protected function getErrorCode() + { + return GreaterThanOrEqual::TOO_LOW_ERROR; + } } diff --git a/src/Symfony/Component/Validator/Constraints/GreaterThanValidator.php b/src/Symfony/Component/Validator/Constraints/GreaterThanValidator.php index fdcf0c1f94f6c..9029e8fc46a80 100644 --- a/src/Symfony/Component/Validator/Constraints/GreaterThanValidator.php +++ b/src/Symfony/Component/Validator/Constraints/GreaterThanValidator.php @@ -15,6 +15,7 @@ * Validates values are greater than the previous (>). * * @author Daniel Holmes + * @author Bernhard Schussek */ class GreaterThanValidator extends AbstractComparisonValidator { @@ -25,4 +26,12 @@ protected function compareValues($value1, $value2) { return $value1 > $value2; } + + /** + * {@inheritdoc} + */ + protected function getErrorCode() + { + return GreaterThan::TOO_LOW_ERROR; + } } diff --git a/src/Symfony/Component/Validator/Constraints/GroupSequenceProvider.php b/src/Symfony/Component/Validator/Constraints/GroupSequenceProvider.php index 3904473412c29..8a3fe6300f6eb 100644 --- a/src/Symfony/Component/Validator/Constraints/GroupSequenceProvider.php +++ b/src/Symfony/Component/Validator/Constraints/GroupSequenceProvider.php @@ -16,6 +16,8 @@ * * @Annotation * @Target({"CLASS", "ANNOTATION"}) + * + * @author Bernhard Schussek */ class GroupSequenceProvider { diff --git a/src/Symfony/Component/Validator/Constraints/Iban.php b/src/Symfony/Component/Validator/Constraints/Iban.php index 66ce09ae1a630..a0d6b0982d645 100644 --- a/src/Symfony/Component/Validator/Constraints/Iban.php +++ b/src/Symfony/Component/Validator/Constraints/Iban.php @@ -23,11 +23,11 @@ */ class Iban extends Constraint { - const TOO_SHORT_ERROR = 1; - const INVALID_COUNTRY_CODE_ERROR = 2; - const INVALID_CHARACTERS_ERROR = 3; - const INVALID_CASE_ERROR = 4; - const CHECKSUM_FAILED_ERROR = 5; + const TOO_SHORT_ERROR = '88e5e319-0aeb-4979-a27e-3d9ce0c16166'; + const INVALID_COUNTRY_CODE_ERROR = 'de78ee2c-bd50-44e2-aec8-3d8228aeadb9'; + const INVALID_CHARACTERS_ERROR = '8d3d85e4-784f-4719-a5bc-d9e40d45a3a5'; + const INVALID_CASE_ERROR = 'f4bf62fe-03ec-42af-a53b-68e21b1e7274'; + const CHECKSUM_FAILED_ERROR = 'b9401321-f9bf-4dcb-83c1-f31094440795'; protected static $errorNames = array( self::TOO_SHORT_ERROR => 'TOO_SHORT_ERROR', diff --git a/src/Symfony/Component/Validator/Constraints/IdenticalTo.php b/src/Symfony/Component/Validator/Constraints/IdenticalTo.php index 6d00286d23d6a..a7dadff833c5a 100644 --- a/src/Symfony/Component/Validator/Constraints/IdenticalTo.php +++ b/src/Symfony/Component/Validator/Constraints/IdenticalTo.php @@ -16,8 +16,15 @@ * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) * * @author Daniel Holmes + * @author Bernhard Schussek */ class IdenticalTo extends AbstractComparison { + const NOT_IDENTICAL_ERROR = '2a8cc50f-58a2-4536-875e-060a2ce69ed5'; + + protected static $errorNames = array( + self::NOT_IDENTICAL_ERROR => 'NOT_IDENTICAL_ERROR', + ); + public $message = 'This value should be identical to {{ compared_value_type }} {{ compared_value }}.'; } diff --git a/src/Symfony/Component/Validator/Constraints/IdenticalToValidator.php b/src/Symfony/Component/Validator/Constraints/IdenticalToValidator.php index a1867262aa304..304f71f19129d 100644 --- a/src/Symfony/Component/Validator/Constraints/IdenticalToValidator.php +++ b/src/Symfony/Component/Validator/Constraints/IdenticalToValidator.php @@ -15,6 +15,7 @@ * Validates values are identical (===). * * @author Daniel Holmes + * @author Bernhard Schussek */ class IdenticalToValidator extends AbstractComparisonValidator { @@ -25,4 +26,12 @@ protected function compareValues($value1, $value2) { return $value1 === $value2; } + + /** + * {@inheritdoc} + */ + protected function getErrorCode() + { + return IdenticalTo::NOT_IDENTICAL_ERROR; + } } diff --git a/src/Symfony/Component/Validator/Constraints/Image.php b/src/Symfony/Component/Validator/Constraints/Image.php index 904ef97b492b1..6d9d2c5046cbe 100644 --- a/src/Symfony/Component/Validator/Constraints/Image.php +++ b/src/Symfony/Component/Validator/Constraints/Image.php @@ -22,18 +22,16 @@ */ class Image extends File { - // Don't reuse values used in File - - const SIZE_NOT_DETECTED_ERROR = 10; - const TOO_WIDE_ERROR = 11; - const TOO_NARROW_ERROR = 12; - const TOO_HIGH_ERROR = 13; - const TOO_LOW_ERROR = 14; - const RATIO_TOO_BIG_ERROR = 15; - const RATIO_TOO_SMALL_ERROR = 16; - const SQUARE_NOT_ALLOWED_ERROR = 17; - const LANDSCAPE_NOT_ALLOWED_ERROR = 18; - const PORTRAIT_NOT_ALLOWED_ERROR = 19; + const SIZE_NOT_DETECTED_ERROR = '6d55c3f4-e58e-4fe3-91ee-74b492199956'; + const TOO_WIDE_ERROR = '7f87163d-878f-47f5-99ba-a8eb723a1ab2'; + const TOO_NARROW_ERROR = '9afbd561-4f90-4a27-be62-1780fc43604a'; + const TOO_HIGH_ERROR = '7efae81c-4877-47ba-aa65-d01ccb0d4645'; + const TOO_LOW_ERROR = 'aef0cb6a-c07f-4894-bc08-1781420d7b4c'; + const RATIO_TOO_BIG_ERROR = '70cafca6-168f-41c9-8c8c-4e47a52be643'; + const RATIO_TOO_SMALL_ERROR = '59b8c6ef-bcf2-4ceb-afff-4642ed92f12e'; + const SQUARE_NOT_ALLOWED_ERROR = '5d41425b-facb-47f7-a55a-de9fbe45cb46'; + const LANDSCAPE_NOT_ALLOWED_ERROR = '6f895685-7cf2-4d65-b3da-9029c5581d88'; + const PORTRAIT_NOT_ALLOWED_ERROR = '65608156-77da-4c79-a88c-02ef6d18c782'; // Include the mapping from the base class diff --git a/src/Symfony/Component/Validator/Constraints/Ip.php b/src/Symfony/Component/Validator/Constraints/Ip.php index 27f0b2d0fda1b..2633f467327cc 100644 --- a/src/Symfony/Component/Validator/Constraints/Ip.php +++ b/src/Symfony/Component/Validator/Constraints/Ip.php @@ -46,6 +46,8 @@ class Ip extends Constraint const V6_ONLY_PUBLIC = '6_public'; const ALL_ONLY_PUBLIC = 'all_public'; + const INVALID_IP_ERROR = 'b1b427ae-9f6f-41b0-aa9b-84511fbb3c5b'; + protected static $versions = array( self::V4, self::V6, @@ -64,6 +66,10 @@ class Ip extends Constraint self::ALL_ONLY_PUBLIC, ); + protected static $errorNames = array( + self::INVALID_IP_ERROR => 'INVALID_IP_ERROR', + ); + public $version = self::V4; public $message = 'This is not a valid IP address.'; diff --git a/src/Symfony/Component/Validator/Constraints/IpValidator.php b/src/Symfony/Component/Validator/Constraints/IpValidator.php index 8ec8068e79d58..7815510f0823f 100644 --- a/src/Symfony/Component/Validator/Constraints/IpValidator.php +++ b/src/Symfony/Component/Validator/Constraints/IpValidator.php @@ -99,10 +99,12 @@ public function validate($value, Constraint $constraint) if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Ip::INVALID_IP_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Ip::INVALID_IP_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/IsFalse.php b/src/Symfony/Component/Validator/Constraints/IsFalse.php index 7b1b72bd51110..945f3a2cde7a7 100644 --- a/src/Symfony/Component/Validator/Constraints/IsFalse.php +++ b/src/Symfony/Component/Validator/Constraints/IsFalse.php @@ -23,5 +23,11 @@ */ class IsFalse extends Constraint { + const NOT_FALSE_ERROR = 'd53a91b0-def3-426a-83d7-269da7ab4200'; + + protected static $errorNames = array( + self::NOT_FALSE_ERROR => 'NOT_FALSE_ERROR', + ); + public $message = 'This value should be false.'; } diff --git a/src/Symfony/Component/Validator/Constraints/IsFalseValidator.php b/src/Symfony/Component/Validator/Constraints/IsFalseValidator.php index f5215588f21e7..7e330e4dd7276 100644 --- a/src/Symfony/Component/Validator/Constraints/IsFalseValidator.php +++ b/src/Symfony/Component/Validator/Constraints/IsFalseValidator.php @@ -39,10 +39,12 @@ public function validate($value, Constraint $constraint) if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(IsFalse::NOT_FALSE_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(IsFalse::NOT_FALSE_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/IsNull.php b/src/Symfony/Component/Validator/Constraints/IsNull.php index 3e7fef112c72c..ea0b0febba139 100644 --- a/src/Symfony/Component/Validator/Constraints/IsNull.php +++ b/src/Symfony/Component/Validator/Constraints/IsNull.php @@ -23,5 +23,11 @@ */ class IsNull extends Constraint { + const NOT_NULL_ERROR = '60d2f30b-8cfa-4372-b155-9656634de120'; + + protected static $errorNames = array( + self::NOT_NULL_ERROR => 'NOT_NULL_ERROR', + ); + public $message = 'This value should be null.'; } diff --git a/src/Symfony/Component/Validator/Constraints/IsNullValidator.php b/src/Symfony/Component/Validator/Constraints/IsNullValidator.php index 162f6182fffcc..4a7ada83877c5 100644 --- a/src/Symfony/Component/Validator/Constraints/IsNullValidator.php +++ b/src/Symfony/Component/Validator/Constraints/IsNullValidator.php @@ -36,10 +36,12 @@ public function validate($value, Constraint $constraint) if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(IsNull::NOT_NULL_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(IsNull::NOT_NULL_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/IsTrue.php b/src/Symfony/Component/Validator/Constraints/IsTrue.php index c0be6b8272d6f..37049ebb3f48d 100644 --- a/src/Symfony/Component/Validator/Constraints/IsTrue.php +++ b/src/Symfony/Component/Validator/Constraints/IsTrue.php @@ -23,5 +23,11 @@ */ class IsTrue extends Constraint { + const NOT_TRUE_ERROR = '2beabf1c-54c0-4882-a928-05249b26e23b'; + + protected static $errorNames = array( + self::NOT_TRUE_ERROR => 'NOT_TRUE_ERROR', + ); + public $message = 'This value should be true.'; } diff --git a/src/Symfony/Component/Validator/Constraints/IsTrueValidator.php b/src/Symfony/Component/Validator/Constraints/IsTrueValidator.php index 206c63d9a7949..fc044ca59c252 100644 --- a/src/Symfony/Component/Validator/Constraints/IsTrueValidator.php +++ b/src/Symfony/Component/Validator/Constraints/IsTrueValidator.php @@ -40,10 +40,12 @@ public function validate($value, Constraint $constraint) if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(IsTrue::NOT_TRUE_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(IsTrue::NOT_TRUE_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Isbn.php b/src/Symfony/Component/Validator/Constraints/Isbn.php index 35cb82204e245..3c4251b39ea7c 100644 --- a/src/Symfony/Component/Validator/Constraints/Isbn.php +++ b/src/Symfony/Component/Validator/Constraints/Isbn.php @@ -23,11 +23,11 @@ */ class Isbn extends Constraint { - const TOO_SHORT_ERROR = 1; - const TOO_LONG_ERROR = 2; - const INVALID_CHARACTERS_ERROR = 3; - const CHECKSUM_FAILED_ERROR = 4; - const TYPE_NOT_RECOGNIZED_ERROR = 5; + const TOO_SHORT_ERROR = '949acbb0-8ef5-43ed-a0e9-032dfd08ae45'; + const TOO_LONG_ERROR = '3171387d-f80a-47b3-bd6e-60598545316a'; + const INVALID_CHARACTERS_ERROR = '23d21cea-da99-453d-98b1-a7d916fbb339'; + const CHECKSUM_FAILED_ERROR = '2881c032-660f-46b6-8153-d352d9706640'; + const TYPE_NOT_RECOGNIZED_ERROR = 'fa54a457-f042-441f-89c4-066ee5bdd3e1'; protected static $errorNames = array( self::TOO_SHORT_ERROR => 'TOO_SHORT_ERROR', diff --git a/src/Symfony/Component/Validator/Constraints/Issn.php b/src/Symfony/Component/Validator/Constraints/Issn.php index 39716a28cce30..a2fecdd35c386 100644 --- a/src/Symfony/Component/Validator/Constraints/Issn.php +++ b/src/Symfony/Component/Validator/Constraints/Issn.php @@ -22,12 +22,12 @@ */ class Issn extends Constraint { - const TOO_SHORT_ERROR = 1; - const TOO_LONG_ERROR = 2; - const MISSING_HYPHEN_ERROR = 3; - const INVALID_CHARACTERS_ERROR = 4; - const INVALID_CASE_ERROR = 5; - const CHECKSUM_FAILED_ERROR = 6; + const TOO_SHORT_ERROR = '6a20dd3d-f463-4460-8e7b-18a1b98abbfb'; + const TOO_LONG_ERROR = '37cef893-5871-464e-8b12-7fb79324833c'; + const MISSING_HYPHEN_ERROR = '2983286f-8134-4693-957a-1ec4ef887b15'; + const INVALID_CHARACTERS_ERROR = 'a663d266-37c2-4ece-a914-ae891940c588'; + const INVALID_CASE_ERROR = '7b6dd393-7523-4a6c-b84d-72b91bba5e1a'; + const CHECKSUM_FAILED_ERROR = 'b0f92dbc-667c-48de-b526-ad9586d43e85'; protected static $errorNames = array( self::TOO_SHORT_ERROR => 'TOO_SHORT_ERROR', diff --git a/src/Symfony/Component/Validator/Constraints/Language.php b/src/Symfony/Component/Validator/Constraints/Language.php index e7c29dc64b8ca..190885ce865b9 100644 --- a/src/Symfony/Component/Validator/Constraints/Language.php +++ b/src/Symfony/Component/Validator/Constraints/Language.php @@ -23,5 +23,11 @@ */ class Language extends Constraint { + const NO_SUCH_LANGUAGE_ERROR = 'ee65fec4-9a20-4202-9f39-ca558cd7bdf7'; + + protected static $errorNames = array( + self::NO_SUCH_LANGUAGE_ERROR => 'NO_SUCH_LANGUAGE_ERROR', + ); + public $message = 'This value is not a valid language.'; } diff --git a/src/Symfony/Component/Validator/Constraints/LanguageValidator.php b/src/Symfony/Component/Validator/Constraints/LanguageValidator.php index cc8581f819e0a..a0f89bbbee0a2 100644 --- a/src/Symfony/Component/Validator/Constraints/LanguageValidator.php +++ b/src/Symfony/Component/Validator/Constraints/LanguageValidator.php @@ -50,10 +50,12 @@ public function validate($value, Constraint $constraint) if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Language::NO_SUCH_LANGUAGE_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Language::NO_SUCH_LANGUAGE_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Length.php b/src/Symfony/Component/Validator/Constraints/Length.php index 8d004805cb106..356dea0db32ed 100644 --- a/src/Symfony/Component/Validator/Constraints/Length.php +++ b/src/Symfony/Component/Validator/Constraints/Length.php @@ -24,12 +24,14 @@ */ class Length extends Constraint { - const TOO_SHORT_ERROR = 1; - const TOO_LONG_ERROR = 2; + const TOO_SHORT_ERROR = '9ff3fdc4-b214-49db-8718-39c315e33d45'; + const TOO_LONG_ERROR = 'd94b19cc-114f-4f44-9cc4-4138e80a87b9'; + const INVALID_CHARACTERS_ERROR = '35e6a710-aa2e-4719-b58e-24b35749b767'; protected static $errorNames = array( self::TOO_SHORT_ERROR => 'TOO_SHORT_ERROR', self::TOO_LONG_ERROR => 'TOO_LONG_ERROR', + self::INVALID_CHARACTERS_ERROR => 'INVALID_CHARACTERS_ERROR', ); public $maxMessage = 'This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less.'; diff --git a/src/Symfony/Component/Validator/Constraints/LengthValidator.php b/src/Symfony/Component/Validator/Constraints/LengthValidator.php index bfa7b19f340b1..9597f3869ac1b 100644 --- a/src/Symfony/Component/Validator/Constraints/LengthValidator.php +++ b/src/Symfony/Component/Validator/Constraints/LengthValidator.php @@ -70,12 +70,14 @@ public function validate($value, Constraint $constraint) ->setParameter('{{ value }}', $this->formatValue($stringValue)) ->setParameter('{{ charset }}', $constraint->charset) ->setInvalidValue($value) + ->setCode(Length::INVALID_CHARACTERS_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->charsetMessage) ->setParameter('{{ value }}', $this->formatValue($stringValue)) ->setParameter('{{ charset }}', $constraint->charset) ->setInvalidValue($value) + ->setCode(Length::INVALID_CHARACTERS_ERROR) ->addViolation(); } diff --git a/src/Symfony/Component/Validator/Constraints/LessThan.php b/src/Symfony/Component/Validator/Constraints/LessThan.php index b116320037d5d..1bbb50878b1a6 100644 --- a/src/Symfony/Component/Validator/Constraints/LessThan.php +++ b/src/Symfony/Component/Validator/Constraints/LessThan.php @@ -16,8 +16,15 @@ * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) * * @author Daniel Holmes + * @author Bernhard Schussek */ class LessThan extends AbstractComparison { + const TOO_HIGH_ERROR = '079d7420-2d13-460c-8756-de810eeb37d2'; + + protected static $errorNames = array( + self::TOO_HIGH_ERROR => 'TOO_HIGH_ERROR', + ); + public $message = 'This value should be less than {{ compared_value }}.'; } diff --git a/src/Symfony/Component/Validator/Constraints/LessThanOrEqual.php b/src/Symfony/Component/Validator/Constraints/LessThanOrEqual.php index 7faca842215f9..d118942385c57 100644 --- a/src/Symfony/Component/Validator/Constraints/LessThanOrEqual.php +++ b/src/Symfony/Component/Validator/Constraints/LessThanOrEqual.php @@ -16,8 +16,15 @@ * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) * * @author Daniel Holmes + * @author Bernhard Schussek */ class LessThanOrEqual extends AbstractComparison { + const TOO_HIGH_ERROR = '079d7420-2d13-460c-8756-de810eeb37d2'; + + protected static $errorNames = array( + self::TOO_HIGH_ERROR => 'TOO_HIGH_ERROR', + ); + public $message = 'This value should be less than or equal to {{ compared_value }}.'; } diff --git a/src/Symfony/Component/Validator/Constraints/LessThanOrEqualValidator.php b/src/Symfony/Component/Validator/Constraints/LessThanOrEqualValidator.php index dcc93b2503792..54281eef5a673 100644 --- a/src/Symfony/Component/Validator/Constraints/LessThanOrEqualValidator.php +++ b/src/Symfony/Component/Validator/Constraints/LessThanOrEqualValidator.php @@ -15,6 +15,7 @@ * Validates values are less than or equal to the previous (<=). * * @author Daniel Holmes + * @author Bernhard Schussek */ class LessThanOrEqualValidator extends AbstractComparisonValidator { @@ -25,4 +26,12 @@ protected function compareValues($value1, $value2) { return $value1 <= $value2; } + + /** + * {@inheritdoc} + */ + protected function getErrorCode() + { + return LessThanOrEqual::TOO_HIGH_ERROR; + } } diff --git a/src/Symfony/Component/Validator/Constraints/LessThanValidator.php b/src/Symfony/Component/Validator/Constraints/LessThanValidator.php index 081316a588c90..ef7535fc99143 100644 --- a/src/Symfony/Component/Validator/Constraints/LessThanValidator.php +++ b/src/Symfony/Component/Validator/Constraints/LessThanValidator.php @@ -15,6 +15,7 @@ * Validates values are less than the previous (<). * * @author Daniel Holmes + * @author Bernhard Schussek */ class LessThanValidator extends AbstractComparisonValidator { @@ -25,4 +26,12 @@ protected function compareValues($value1, $value2) { return $value1 < $value2; } + + /** + * {@inheritdoc} + */ + protected function getErrorCode() + { + return LessThan::TOO_HIGH_ERROR; + } } diff --git a/src/Symfony/Component/Validator/Constraints/Locale.php b/src/Symfony/Component/Validator/Constraints/Locale.php index 12a55464a6359..bb85be246afd8 100644 --- a/src/Symfony/Component/Validator/Constraints/Locale.php +++ b/src/Symfony/Component/Validator/Constraints/Locale.php @@ -23,5 +23,11 @@ */ class Locale extends Constraint { + const NO_SUCH_LOCALE_ERROR = 'a0af4293-1f1a-4a1c-a328-979cba6182a2'; + + protected static $errorNames = array( + self::NO_SUCH_LOCALE_ERROR => 'NO_SUCH_LOCALE_ERROR', + ); + public $message = 'This value is not a valid locale.'; } diff --git a/src/Symfony/Component/Validator/Constraints/LocaleValidator.php b/src/Symfony/Component/Validator/Constraints/LocaleValidator.php index a5f69271e6e0b..daf404c141141 100644 --- a/src/Symfony/Component/Validator/Constraints/LocaleValidator.php +++ b/src/Symfony/Component/Validator/Constraints/LocaleValidator.php @@ -50,10 +50,12 @@ public function validate($value, Constraint $constraint) if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Locale::NO_SUCH_LOCALE_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Locale::NO_SUCH_LOCALE_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Luhn.php b/src/Symfony/Component/Validator/Constraints/Luhn.php index 24f5bc77ab76c..67f152d29dc53 100644 --- a/src/Symfony/Component/Validator/Constraints/Luhn.php +++ b/src/Symfony/Component/Validator/Constraints/Luhn.php @@ -25,8 +25,8 @@ */ class Luhn extends Constraint { - const INVALID_CHARACTERS_ERROR = 1; - const CHECKSUM_FAILED_ERROR = 2; + const INVALID_CHARACTERS_ERROR = 'dfad6d23-1b74-4374-929b-5cbb56fc0d9e'; + const CHECKSUM_FAILED_ERROR = '4d760774-3f50-4cd5-a6d5-b10a3299d8d3'; protected static $errorNames = array( self::INVALID_CHARACTERS_ERROR => 'INVALID_CHARACTERS_ERROR', diff --git a/src/Symfony/Component/Validator/Constraints/NotBlank.php b/src/Symfony/Component/Validator/Constraints/NotBlank.php index c578c6d81f3a0..f7242ce0d1836 100644 --- a/src/Symfony/Component/Validator/Constraints/NotBlank.php +++ b/src/Symfony/Component/Validator/Constraints/NotBlank.php @@ -23,5 +23,11 @@ */ class NotBlank extends Constraint { + const IS_BLANK_ERROR = 'c1051bb4-d103-4f74-8988-acbcafc7fdc3'; + + protected static $errorNames = array( + self::IS_BLANK_ERROR => 'IS_BLANK_ERROR', + ); + public $message = 'This value should not be blank.'; } diff --git a/src/Symfony/Component/Validator/Constraints/NotBlankValidator.php b/src/Symfony/Component/Validator/Constraints/NotBlankValidator.php index a435701cf2605..3714d7930ed26 100644 --- a/src/Symfony/Component/Validator/Constraints/NotBlankValidator.php +++ b/src/Symfony/Component/Validator/Constraints/NotBlankValidator.php @@ -36,10 +36,12 @@ public function validate($value, Constraint $constraint) if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(NotBlank::IS_BLANK_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(NotBlank::IS_BLANK_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/NotEqualTo.php b/src/Symfony/Component/Validator/Constraints/NotEqualTo.php index abd80920fb3b4..8c5abddaee422 100644 --- a/src/Symfony/Component/Validator/Constraints/NotEqualTo.php +++ b/src/Symfony/Component/Validator/Constraints/NotEqualTo.php @@ -16,8 +16,15 @@ * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) * * @author Daniel Holmes + * @author Bernhard Schussek */ class NotEqualTo extends AbstractComparison { + const IS_EQUAL_ERROR = 'aa2e33da-25c8-4d76-8c6c-812f02ea89dd'; + + protected static $errorNames = array( + self::IS_EQUAL_ERROR => 'IS_EQUAL_ERROR', + ); + public $message = 'This value should not be equal to {{ compared_value }}.'; } diff --git a/src/Symfony/Component/Validator/Constraints/NotEqualToValidator.php b/src/Symfony/Component/Validator/Constraints/NotEqualToValidator.php index 5710a85b93790..b80c5eaedab29 100644 --- a/src/Symfony/Component/Validator/Constraints/NotEqualToValidator.php +++ b/src/Symfony/Component/Validator/Constraints/NotEqualToValidator.php @@ -15,6 +15,7 @@ * Validates values are all unequal (!=). * * @author Daniel Holmes + * @author Bernhard Schussek */ class NotEqualToValidator extends AbstractComparisonValidator { @@ -25,4 +26,12 @@ protected function compareValues($value1, $value2) { return $value1 != $value2; } + + /** + * {@inheritdoc} + */ + protected function getErrorCode() + { + return NotEqualTo::IS_EQUAL_ERROR; + } } diff --git a/src/Symfony/Component/Validator/Constraints/NotIdenticalTo.php b/src/Symfony/Component/Validator/Constraints/NotIdenticalTo.php index fb4ef3f3c1836..4c9c63ea61e49 100644 --- a/src/Symfony/Component/Validator/Constraints/NotIdenticalTo.php +++ b/src/Symfony/Component/Validator/Constraints/NotIdenticalTo.php @@ -16,8 +16,15 @@ * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) * * @author Daniel Holmes + * @author Bernhard Schussek */ class NotIdenticalTo extends AbstractComparison { + const IS_IDENTICAL_ERROR = '4aaac518-0dda-4129-a6d9-e216b9b454a0'; + + protected static $errorNames = array( + self::IS_IDENTICAL_ERROR => 'IS_IDENTICAL_ERROR', + ); + public $message = 'This value should not be identical to {{ compared_value_type }} {{ compared_value }}.'; } diff --git a/src/Symfony/Component/Validator/Constraints/NotIdenticalToValidator.php b/src/Symfony/Component/Validator/Constraints/NotIdenticalToValidator.php index ed8dc1c0dd3fc..3ea8b5ac25ea4 100644 --- a/src/Symfony/Component/Validator/Constraints/NotIdenticalToValidator.php +++ b/src/Symfony/Component/Validator/Constraints/NotIdenticalToValidator.php @@ -15,6 +15,7 @@ * Validates values aren't identical (!==). * * @author Daniel Holmes + * @author Bernhard Schussek */ class NotIdenticalToValidator extends AbstractComparisonValidator { @@ -25,4 +26,12 @@ protected function compareValues($value1, $value2) { return $value1 !== $value2; } + + /** + * {@inheritdoc} + */ + protected function getErrorCode() + { + return NotIdenticalTo::IS_IDENTICAL_ERROR; + } } diff --git a/src/Symfony/Component/Validator/Constraints/NotNull.php b/src/Symfony/Component/Validator/Constraints/NotNull.php index 60416c76ec2d4..1701bf7bdcb3e 100644 --- a/src/Symfony/Component/Validator/Constraints/NotNull.php +++ b/src/Symfony/Component/Validator/Constraints/NotNull.php @@ -23,5 +23,11 @@ */ class NotNull extends Constraint { + const IS_NULL_ERROR = 'ad32d13f-c3d4-423b-909a-857b961eb720'; + + protected static $errorNames = array( + self::IS_NULL_ERROR => 'IS_NULL_ERROR', + ); + public $message = 'This value should not be null.'; } diff --git a/src/Symfony/Component/Validator/Constraints/NotNullValidator.php b/src/Symfony/Component/Validator/Constraints/NotNullValidator.php index a7a905ae14948..e43b7a4c5e2b8 100644 --- a/src/Symfony/Component/Validator/Constraints/NotNullValidator.php +++ b/src/Symfony/Component/Validator/Constraints/NotNullValidator.php @@ -13,6 +13,7 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Exception\UnexpectedTypeException; /** @@ -32,7 +33,17 @@ public function validate($value, Constraint $constraint) } if (null === $value) { - $this->context->addViolation($constraint->message); + if ($this->context instanceof ExecutionContextInterface) { + $this->context->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(NotNull::IS_NULL_ERROR) + ->addViolation(); + } else { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(NotNull::IS_NULL_ERROR) + ->addViolation(); + } } } } diff --git a/src/Symfony/Component/Validator/Constraints/Range.php b/src/Symfony/Component/Validator/Constraints/Range.php index a12afffbedb8c..26d2546cc2458 100644 --- a/src/Symfony/Component/Validator/Constraints/Range.php +++ b/src/Symfony/Component/Validator/Constraints/Range.php @@ -24,14 +24,32 @@ */ class Range extends Constraint { - const INVALID_VALUE_ERROR = 1; - const BEYOND_RANGE_ERROR = 2; - const BELOW_RANGE_ERROR = 3; + const INVALID_CHARACTERS_ERROR = 'ad9a9798-7a99-4df7-8ce9-46e416a1e60b'; + const TOO_HIGH_ERROR = '2d28afcb-e32e-45fb-a815-01c431a86a69'; + const TOO_LOW_ERROR = '76454e69-502c-46c5-9643-f447d837c4d5'; + + /** + * @deprecated Deprecated since version 2.8, to be removed in 3.0. Use + * {@link INVALID_CHARACTERS_ERROR} instead. + */ + const INVALID_VALUE_ERROR = self::INVALID_CHARACTERS_ERROR; + + /** + * @deprecated Deprecated since version 2.8, to be removed in 3.0. Use + * {@link TOO_HIGH_ERROR} instead. + */ + const BEYOND_RANGE_ERROR = self::TOO_HIGH_ERROR; + + /** + * @deprecated Deprecated since version 2.8, to be removed in 3.0. Use + * {@link TOO_LOW_ERROR} instead. + */ + const BELOW_RANGE_ERROR = self::TOO_LOW_ERROR; protected static $errorNames = array( - self::INVALID_VALUE_ERROR => 'INVALID_VALUE_ERROR', - self::BEYOND_RANGE_ERROR => 'BEYOND_RANGE_ERROR', - self::BELOW_RANGE_ERROR => 'BELOW_RANGE_ERROR', + self::INVALID_CHARACTERS_ERROR => 'INVALID_CHARACTERS_ERROR', + self::TOO_HIGH_ERROR => 'TOO_HIGH_ERROR', + self::TOO_LOW_ERROR => 'TOO_LOW_ERROR', ); public $minMessage = 'This value should be {{ limit }} or more.'; diff --git a/src/Symfony/Component/Validator/Constraints/RangeValidator.php b/src/Symfony/Component/Validator/Constraints/RangeValidator.php index 8f5fddac2ade9..05ef3b47c752b 100644 --- a/src/Symfony/Component/Validator/Constraints/RangeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/RangeValidator.php @@ -38,12 +38,12 @@ public function validate($value, Constraint $constraint) if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->invalidMessage) ->setParameter('{{ value }}', $this->formatValue($value, self::PRETTY_DATE)) - ->setCode(Range::INVALID_VALUE_ERROR) + ->setCode(Range::INVALID_CHARACTERS_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->invalidMessage) ->setParameter('{{ value }}', $this->formatValue($value, self::PRETTY_DATE)) - ->setCode(Range::INVALID_VALUE_ERROR) + ->setCode(Range::INVALID_CHARACTERS_ERROR) ->addViolation(); } @@ -72,13 +72,13 @@ public function validate($value, Constraint $constraint) $this->context->buildViolation($constraint->maxMessage) ->setParameter('{{ value }}', $this->formatValue($value, self::PRETTY_DATE)) ->setParameter('{{ limit }}', $this->formatValue($max, self::PRETTY_DATE)) - ->setCode(Range::BEYOND_RANGE_ERROR) + ->setCode(Range::TOO_HIGH_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->maxMessage) ->setParameter('{{ value }}', $this->formatValue($value, self::PRETTY_DATE)) ->setParameter('{{ limit }}', $this->formatValue($max, self::PRETTY_DATE)) - ->setCode(Range::BEYOND_RANGE_ERROR) + ->setCode(Range::TOO_HIGH_ERROR) ->addViolation(); } @@ -90,13 +90,13 @@ public function validate($value, Constraint $constraint) $this->context->buildViolation($constraint->minMessage) ->setParameter('{{ value }}', $this->formatValue($value, self::PRETTY_DATE)) ->setParameter('{{ limit }}', $this->formatValue($min, self::PRETTY_DATE)) - ->setCode(Range::BELOW_RANGE_ERROR) + ->setCode(Range::TOO_LOW_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->minMessage) ->setParameter('{{ value }}', $this->formatValue($value, self::PRETTY_DATE)) ->setParameter('{{ limit }}', $this->formatValue($min, self::PRETTY_DATE)) - ->setCode(Range::BELOW_RANGE_ERROR) + ->setCode(Range::TOO_LOW_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Regex.php b/src/Symfony/Component/Validator/Constraints/Regex.php index 3cdf5146fd407..e0a11b5c08553 100644 --- a/src/Symfony/Component/Validator/Constraints/Regex.php +++ b/src/Symfony/Component/Validator/Constraints/Regex.php @@ -23,6 +23,12 @@ */ class Regex extends Constraint { + const REGEX_FAILED_ERROR = 'de1e3db3-5ed4-4941-aae4-59f3667cc3a3'; + + protected static $errorNames = array( + self::REGEX_FAILED_ERROR => 'REGEX_FAILED_ERROR', + ); + public $message = 'This value is not valid.'; public $pattern; public $htmlPattern; diff --git a/src/Symfony/Component/Validator/Constraints/RegexValidator.php b/src/Symfony/Component/Validator/Constraints/RegexValidator.php index 45ba9793ef537..1edbde1923690 100644 --- a/src/Symfony/Component/Validator/Constraints/RegexValidator.php +++ b/src/Symfony/Component/Validator/Constraints/RegexValidator.php @@ -49,10 +49,12 @@ public function validate($value, Constraint $constraint) if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Regex::REGEX_FAILED_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Regex::REGEX_FAILED_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Time.php b/src/Symfony/Component/Validator/Constraints/Time.php index 7998c6f9519b6..e65ebc00d47ed 100644 --- a/src/Symfony/Component/Validator/Constraints/Time.php +++ b/src/Symfony/Component/Validator/Constraints/Time.php @@ -23,8 +23,8 @@ */ class Time extends Constraint { - const INVALID_FORMAT_ERROR = 1; - const INVALID_TIME_ERROR = 2; + const INVALID_FORMAT_ERROR = '9d27b2bb-f755-4fbf-b725-39b1edbdebdf'; + const INVALID_TIME_ERROR = '8532f9e1-84b2-4d67-8989-0818bc38533b'; protected static $errorNames = array( self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR', diff --git a/src/Symfony/Component/Validator/Constraints/Type.php b/src/Symfony/Component/Validator/Constraints/Type.php index fc4cc72eb685f..f8ff9d88bdcf8 100644 --- a/src/Symfony/Component/Validator/Constraints/Type.php +++ b/src/Symfony/Component/Validator/Constraints/Type.php @@ -23,6 +23,12 @@ */ class Type extends Constraint { + const INVALID_TYPE_ERROR = 'ba785a8c-82cb-4283-967c-3cf342181b40'; + + protected static $errorNames = array( + self::INVALID_TYPE_ERROR => 'INVALID_TYPE_ERROR', + ); + public $message = 'This value should be of type {{ type }}.'; public $type; diff --git a/src/Symfony/Component/Validator/Constraints/TypeValidator.php b/src/Symfony/Component/Validator/Constraints/TypeValidator.php index 30ad278de1b77..55a27bec3a953 100644 --- a/src/Symfony/Component/Validator/Constraints/TypeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/TypeValidator.php @@ -53,11 +53,13 @@ public function validate($value, Constraint $constraint) $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) ->setParameter('{{ type }}', $constraint->type) + ->setCode(Type::INVALID_TYPE_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) ->setParameter('{{ type }}', $constraint->type) + ->setCode(Type::INVALID_TYPE_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Component/Validator/Constraints/Url.php b/src/Symfony/Component/Validator/Constraints/Url.php index 7b8ef3fb626cf..6c8ebb7744619 100644 --- a/src/Symfony/Component/Validator/Constraints/Url.php +++ b/src/Symfony/Component/Validator/Constraints/Url.php @@ -23,6 +23,12 @@ */ class Url extends Constraint { + const INVALID_URL_ERROR = '57c2f299-1154-4870-89bb-ef3b1f5ad229'; + + protected static $errorNames = array( + self::INVALID_URL_ERROR => 'INVALID_URL_ERROR', + ); + public $message = 'This value is not a valid URL.'; public $dnsMessage = 'The host could not be resolved.'; public $protocols = array('http', 'https'); diff --git a/src/Symfony/Component/Validator/Constraints/UrlValidator.php b/src/Symfony/Component/Validator/Constraints/UrlValidator.php index b38f8dafab305..b308b20e6448f 100644 --- a/src/Symfony/Component/Validator/Constraints/UrlValidator.php +++ b/src/Symfony/Component/Validator/Constraints/UrlValidator.php @@ -63,10 +63,12 @@ public function validate($value, Constraint $constraint) if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Url::INVALID_URL_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Url::INVALID_URL_ERROR) ->addViolation(); } @@ -79,12 +81,14 @@ public function validate($value, Constraint $constraint) if (!checkdnsrr($host, 'ANY')) { if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->dnsMessage) - ->setParameter('{{ value }}', $this->formatValue($host)) - ->addViolation(); + ->setParameter('{{ value }}', $this->formatValue($host)) + ->setCode(Url::INVALID_URL_ERROR) + ->addViolation(); } else { $this->buildViolation($constraint->dnsMessage) - ->setParameter('{{ value }}', $this->formatValue($host)) - ->addViolation(); + ->setParameter('{{ value }}', $this->formatValue($host)) + ->setCode(Url::INVALID_URL_ERROR) + ->addViolation(); } } } diff --git a/src/Symfony/Component/Validator/Constraints/Uuid.php b/src/Symfony/Component/Validator/Constraints/Uuid.php index 3c67a3af0c163..83627800791c4 100644 --- a/src/Symfony/Component/Validator/Constraints/Uuid.php +++ b/src/Symfony/Component/Validator/Constraints/Uuid.php @@ -21,12 +21,12 @@ */ class Uuid extends Constraint { - const TOO_SHORT_ERROR = 1; - const TOO_LONG_ERROR = 2; - const INVALID_CHARACTERS_ERROR = 3; - const INVALID_HYPHEN_PLACEMENT_ERROR = 4; - const INVALID_VERSION_ERROR = 5; - const INVALID_VARIANT_ERROR = 6; + const TOO_SHORT_ERROR = 'aa314679-dac9-4f54-bf97-b2049df8f2a3'; + const TOO_LONG_ERROR = '494897dd-36f8-4d31-8923-71a8d5f3000d'; + const INVALID_CHARACTERS_ERROR = '51120b12-a2bc-41bf-aa53-cd73daf330d0'; + const INVALID_HYPHEN_PLACEMENT_ERROR = '98469c83-0309-4f5d-bf95-a496dcaa869c'; + const INVALID_VERSION_ERROR = '21ba13b4-b185-4882-ac6f-d147355987eb'; + const INVALID_VARIANT_ERROR = '164ef693-2b9d-46de-ad7f-836201f0c2db'; protected static $errorNames = array( self::TOO_SHORT_ERROR => 'TOO_SHORT_ERROR', diff --git a/src/Symfony/Component/Validator/README.md b/src/Symfony/Component/Validator/README.md index 9014ec67240ee..c29a26121d8f7 100644 --- a/src/Symfony/Component/Validator/README.md +++ b/src/Symfony/Component/Validator/README.md @@ -113,7 +113,7 @@ https://github.com/fabpot/Silex/blob/master/src/Silex/Provider/ValidatorServiceP Documentation: -https://symfony.com/doc/2.7/book/validation.html +https://symfony.com/doc/2.8/book/validation.html JSR-303 Specification: diff --git a/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php b/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php index 4013fd48fe0fc..f1e9000fd40cc 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/AbstractComparisonValidatorTestCase.php @@ -140,6 +140,7 @@ public function testInvalidComparisonToValue($dirtyValue, $dirtyValueAsString, $ ->setParameter('{{ value }}', $dirtyValueAsString) ->setParameter('{{ compared_value }}', $comparedValueString) ->setParameter('{{ compared_value_type }}', $comparedValueType) + ->setCode($this->getErrorCode()) ->assertRaised(); } @@ -170,4 +171,11 @@ abstract public function provideInvalidComparisons(); * @return Constraint */ abstract protected function createConstraint(array $options); + + /** + * @return string|null + */ + protected function getErrorCode() + { + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php index b5e2a7a97ddb0..5a5fa51a0af48 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php @@ -223,7 +223,7 @@ protected function expectValidateAt($i, $propertyPath, $value, $group) ->will($this->returnValue($validator)); $validator->expects($this->at(2 * $i + 1)) ->method('validate') - ->with($value, $this->logicalOr(null, array()), $group); + ->with($value, $this->logicalOr(null, array(), $this->isInstanceOf('\Symfony\Component\Validator\Constraints\Valid')), $group); } protected function expectValidateValueAt($i, $propertyPath, $value, $constraints, $group = null) diff --git a/src/Symfony/Component/Validator/Tests/Constraints/BlankValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/BlankValidatorTest.php index a7f3d7dd58406..17d8bfbc2e80b 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/BlankValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/BlankValidatorTest.php @@ -54,6 +54,7 @@ public function testInvalidValues($value, $valueAsString) $this->buildViolation('myMessage') ->setParameter('{{ value }}', $valueAsString) + ->setCode(Blank::NOT_BLANK_ERROR) ->assertRaised(); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CountryValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CountryValidatorTest.php index b13351181a43f..1f66de2aa95dd 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CountryValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CountryValidatorTest.php @@ -89,6 +89,7 @@ public function testInvalidCountries($country) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$country.'"') + ->setCode(Country::NO_SUCH_COUNTRY_ERROR) ->assertRaised(); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CurrencyValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CurrencyValidatorTest.php index e5bb060d9c903..c59fc638f70f6 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CurrencyValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CurrencyValidatorTest.php @@ -103,6 +103,7 @@ public function testInvalidCurrencies($currency) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$currency.'"') + ->setCode(Currency::NO_SUCH_CURRENCY_ERROR) ->assertRaised(); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/EqualToValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/EqualToValidatorTest.php index c20db1550ba27..47f1e483fb4b2 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/EqualToValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/EqualToValidatorTest.php @@ -35,6 +35,11 @@ protected function createConstraint(array $options) return new EqualTo($options); } + protected function getErrorCode() + { + return EqualTo::NOT_EQUAL_ERROR; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php index 3d4ef75978a6f..742c2027c5b7f 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php @@ -40,6 +40,7 @@ public function testExpressionIsEvaluatedWithNullValue() $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'null') + ->setCode(Expression::EXPRESSION_FAILED_ERROR) ->assertRaised(); } @@ -54,6 +55,7 @@ public function testExpressionIsEvaluatedWithEmptyStringValue() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '""') + ->setCode(Expression::EXPRESSION_FAILED_ERROR) ->assertRaised(); } @@ -87,6 +89,7 @@ public function testFailingExpressionAtObjectLevel() $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'object') + ->setCode(Expression::EXPRESSION_FAILED_ERROR) ->assertRaised(); } @@ -123,8 +126,9 @@ public function testFailingExpressionAtPropertyLevel() $this->validator->validate('2', $constraint); $this->buildViolation('myMessage') - ->setParameter('{{ value }}', '"2"') ->atPath('data') + ->setParameter('{{ value }}', '"2"') + ->setCode(Expression::EXPRESSION_FAILED_ERROR) ->assertRaised(); } @@ -167,8 +171,9 @@ public function testFailingExpressionAtNestedPropertyLevel() $this->validator->validate('2', $constraint); $this->buildViolation('myMessage') - ->setParameter('{{ value }}', '"2"') ->atPath('reference.data') + ->setParameter('{{ value }}', '"2"') + ->setCode(Expression::EXPRESSION_FAILED_ERROR) ->assertRaised(); } @@ -207,8 +212,9 @@ public function testFailingExpressionAtPropertyLevelWithoutRoot() $this->validator->validate('2', $constraint); $this->buildViolation('myMessage') - ->setParameter('{{ value }}', '"2"') ->atPath('') + ->setParameter('{{ value }}', '"2"') + ->setCode(Expression::EXPRESSION_FAILED_ERROR) ->assertRaised(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/FileTest.php b/src/Symfony/Component/Validator/Tests/Constraints/FileTest.php index fbd4b074260b9..482049343bbe3 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/FileTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/FileTest.php @@ -80,9 +80,9 @@ public function testMaxSizeCannotBeSetToInvalidValueAfterInitialization($maxSize * @dataProvider provideInValidSizes * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException */ - public function testInvalideMaxSize($maxSize) + public function testInvalidMaxSize($maxSize) { - $file = new File(array('maxSize' => $maxSize)); + new File(array('maxSize' => $maxSize)); } /** diff --git a/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorTest.php index 41708f65c966e..41899b2ef10da 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanOrEqualValidatorTest.php @@ -35,6 +35,11 @@ protected function createConstraint(array $options) return new GreaterThanOrEqual($options); } + protected function getErrorCode() + { + return GreaterThanOrEqual::TOO_LOW_ERROR; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanValidatorTest.php index 85a2b1dad18d5..080928c2e6d27 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/GreaterThanValidatorTest.php @@ -35,6 +35,11 @@ protected function createConstraint(array $options) return new GreaterThan($options); } + protected function getErrorCode() + { + return GreaterThan::TOO_LOW_ERROR; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IdenticalToValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IdenticalToValidatorTest.php index 4b71062f05935..63c5d0f64e087 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/IdenticalToValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/IdenticalToValidatorTest.php @@ -35,6 +35,11 @@ protected function createConstraint(array $options) return new IdenticalTo($options); } + protected function getErrorCode() + { + return IdenticalTo::NOT_IDENTICAL_ERROR; + } + public function provideAllValidComparisons() { $this->setDefaultTimezone('UTC'); diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IpValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IpValidatorTest.php index fc40e6104e14b..439d45cc050fc 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/IpValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/IpValidatorTest.php @@ -153,6 +153,7 @@ public function testInvalidIpsV4($ip) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$ip.'"') + ->setCode(Ip::INVALID_IP_ERROR) ->assertRaised(); } @@ -185,6 +186,7 @@ public function testInvalidPrivateIpsV4($ip) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$ip.'"') + ->setCode(Ip::INVALID_IP_ERROR) ->assertRaised(); } @@ -211,6 +213,7 @@ public function testInvalidReservedIpsV4($ip) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$ip.'"') + ->setCode(Ip::INVALID_IP_ERROR) ->assertRaised(); } @@ -237,6 +240,7 @@ public function testInvalidPublicIpsV4($ip) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$ip.'"') + ->setCode(Ip::INVALID_IP_ERROR) ->assertRaised(); } @@ -259,6 +263,7 @@ public function testInvalidIpsV6($ip) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$ip.'"') + ->setCode(Ip::INVALID_IP_ERROR) ->assertRaised(); } @@ -295,6 +300,7 @@ public function testInvalidPrivateIpsV6($ip) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$ip.'"') + ->setCode(Ip::INVALID_IP_ERROR) ->assertRaised(); } @@ -321,6 +327,7 @@ public function testInvalidReservedIpsV6($ip) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$ip.'"') + ->setCode(Ip::INVALID_IP_ERROR) ->assertRaised(); } @@ -346,6 +353,7 @@ public function testInvalidPublicIpsV6($ip) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$ip.'"') + ->setCode(Ip::INVALID_IP_ERROR) ->assertRaised(); } @@ -368,6 +376,7 @@ public function testInvalidIpsAll($ip) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$ip.'"') + ->setCode(Ip::INVALID_IP_ERROR) ->assertRaised(); } @@ -390,6 +399,7 @@ public function testInvalidPrivateIpsAll($ip) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$ip.'"') + ->setCode(Ip::INVALID_IP_ERROR) ->assertRaised(); } @@ -412,6 +422,7 @@ public function testInvalidReservedIpsAll($ip) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$ip.'"') + ->setCode(Ip::INVALID_IP_ERROR) ->assertRaised(); } @@ -434,6 +445,7 @@ public function testInvalidPublicIpsAll($ip) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$ip.'"') + ->setCode(Ip::INVALID_IP_ERROR) ->assertRaised(); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IsFalseValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IsFalseValidatorTest.php index a63d8466ad814..46cadecaf65af 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/IsFalseValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/IsFalseValidatorTest.php @@ -51,6 +51,7 @@ public function testTrueIsInvalid() $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'true') + ->setCode(IsFalse::NOT_FALSE_ERROR) ->assertRaised(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IsNullValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IsNullValidatorTest.php index 885048b9bd603..5a5575313b731 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/IsNullValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/IsNullValidatorTest.php @@ -47,6 +47,7 @@ public function testInvalidValues($value, $valueAsString) $this->buildViolation('myMessage') ->setParameter('{{ value }}', $valueAsString) + ->setCode(IsNull::NOT_NULL_ERROR) ->assertRaised(); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IsTrueValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IsTrueValidatorTest.php index a4f0a4aaeba43..1c5927da4bf4d 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/IsTrueValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/IsTrueValidatorTest.php @@ -51,6 +51,7 @@ public function testFalseIsInvalid() $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'false') + ->setCode(IsTrue::NOT_TRUE_ERROR) ->assertRaised(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LanguageValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LanguageValidatorTest.php index 6f7c3900e5351..56824b785bdbd 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LanguageValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LanguageValidatorTest.php @@ -89,6 +89,7 @@ public function testInvalidLanguages($language) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$language.'"') + ->setCode(Language::NO_SUCH_LANGUAGE_ERROR) ->assertRaised(); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LengthValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LengthValidatorTest.php index 24b63064d0084..75a3d6ee62845 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LengthValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LengthValidatorTest.php @@ -241,6 +241,7 @@ public function testOneCharset($value, $charset, $isValid) ->setParameter('{{ value }}', '"'.$value.'"') ->setParameter('{{ charset }}', $charset) ->setInvalidValue($value) + ->setCode(Length::INVALID_CHARACTERS_ERROR) ->assertRaised(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LessThanOrEqualValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LessThanOrEqualValidatorTest.php index 75181355109ff..8b1adef1806c1 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LessThanOrEqualValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LessThanOrEqualValidatorTest.php @@ -35,6 +35,11 @@ protected function createConstraint(array $options) return new LessThanOrEqual($options); } + protected function getErrorCode() + { + return LessThanOrEqual::TOO_HIGH_ERROR; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LessThanValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LessThanValidatorTest.php index d555870c120b1..c3dea687be826 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LessThanValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LessThanValidatorTest.php @@ -35,6 +35,11 @@ protected function createConstraint(array $options) return new LessThan($options); } + protected function getErrorCode() + { + return LessThan::TOO_HIGH_ERROR; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LocaleValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LocaleValidatorTest.php index e5e2f3009f9f9..ad27a74e2aa70 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LocaleValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LocaleValidatorTest.php @@ -91,6 +91,7 @@ public function testInvalidLocales($locale) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$locale.'"') + ->setCode(Locale::NO_SUCH_LOCALE_ERROR) ->assertRaised(); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/NotBlankValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/NotBlankValidatorTest.php index c248246e4383e..c7c081a63beb0 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/NotBlankValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/NotBlankValidatorTest.php @@ -58,6 +58,7 @@ public function testNullIsInvalid() $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'null') + ->setCode(NotBlank::IS_BLANK_ERROR) ->assertRaised(); } @@ -71,6 +72,7 @@ public function testBlankIsInvalid() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '""') + ->setCode(NotBlank::IS_BLANK_ERROR) ->assertRaised(); } @@ -84,6 +86,7 @@ public function testFalseIsInvalid() $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'false') + ->setCode(NotBlank::IS_BLANK_ERROR) ->assertRaised(); } @@ -97,6 +100,7 @@ public function testEmptyArrayIsInvalid() $this->buildViolation('myMessage') ->setParameter('{{ value }}', 'array') + ->setCode(NotBlank::IS_BLANK_ERROR) ->assertRaised(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/NotEqualToValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/NotEqualToValidatorTest.php index bc2c348efade0..1ee8a54ac35fe 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/NotEqualToValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/NotEqualToValidatorTest.php @@ -35,6 +35,11 @@ protected function createConstraint(array $options) return new NotEqualTo($options); } + protected function getErrorCode() + { + return NotEqualTo::IS_EQUAL_ERROR; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/NotIdenticalToValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/NotIdenticalToValidatorTest.php index 1fbd80663fadd..769e0946a3e9c 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/NotIdenticalToValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/NotIdenticalToValidatorTest.php @@ -35,6 +35,11 @@ protected function createConstraint(array $options) return new NotIdenticalTo($options); } + protected function getErrorCode() + { + return NotIdenticalTo::IS_IDENTICAL_ERROR; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/NotNullValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/NotNullValidatorTest.php index d338f31f797b2..a244f6382ba60 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/NotNullValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/NotNullValidatorTest.php @@ -55,6 +55,9 @@ public function testNullIsInvalid() $this->validator->validate(null, $constraint); - $this->buildViolation('myMessage')->assertRaised(); + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', 'null') + ->setCode(NotNull::IS_NULL_ERROR) + ->assertRaised(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php index 9b7056b548735..39266f24097db 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php @@ -117,7 +117,7 @@ public function testInvalidValuesMin($value, $formattedValue) $this->buildViolation('myMessage') ->setParameter('{{ value }}', $formattedValue) ->setParameter('{{ limit }}', 10) - ->setCode(Range::BELOW_RANGE_ERROR) + ->setCode(Range::TOO_LOW_ERROR) ->assertRaised(); } @@ -136,7 +136,7 @@ public function testInvalidValuesMax($value, $formattedValue) $this->buildViolation('myMessage') ->setParameter('{{ value }}', $formattedValue) ->setParameter('{{ limit }}', 20) - ->setCode(Range::BEYOND_RANGE_ERROR) + ->setCode(Range::TOO_HIGH_ERROR) ->assertRaised(); } @@ -157,7 +157,7 @@ public function testInvalidValuesCombinedMax($value, $formattedValue) $this->buildViolation('myMaxMessage') ->setParameter('{{ value }}', $formattedValue) ->setParameter('{{ limit }}', 20) - ->setCode(Range::BEYOND_RANGE_ERROR) + ->setCode(Range::TOO_HIGH_ERROR) ->assertRaised(); } @@ -178,7 +178,7 @@ public function testInvalidValuesCombinedMin($value, $formattedValue) $this->buildViolation('myMinMessage') ->setParameter('{{ value }}', $formattedValue) ->setParameter('{{ limit }}', 10) - ->setCode(Range::BELOW_RANGE_ERROR) + ->setCode(Range::TOO_LOW_ERROR) ->assertRaised(); } @@ -299,7 +299,7 @@ public function testInvalidDatesMin($value, $dateTimeAsString) $this->buildViolation('myMessage') ->setParameter('{{ value }}', $dateTimeAsString) ->setParameter('{{ limit }}', 'Mar 10, 2014, 12:00 AM') - ->setCode(Range::BELOW_RANGE_ERROR) + ->setCode(Range::TOO_LOW_ERROR) ->assertRaised(); } @@ -322,7 +322,7 @@ public function testInvalidDatesMax($value, $dateTimeAsString) $this->buildViolation('myMessage') ->setParameter('{{ value }}', $dateTimeAsString) ->setParameter('{{ limit }}', 'Mar 20, 2014, 12:00 AM') - ->setCode(Range::BEYOND_RANGE_ERROR) + ->setCode(Range::TOO_HIGH_ERROR) ->assertRaised(); } @@ -347,7 +347,7 @@ public function testInvalidDatesCombinedMax($value, $dateTimeAsString) $this->buildViolation('myMaxMessage') ->setParameter('{{ value }}', $dateTimeAsString) ->setParameter('{{ limit }}', 'Mar 20, 2014, 12:00 AM') - ->setCode(Range::BEYOND_RANGE_ERROR) + ->setCode(Range::TOO_HIGH_ERROR) ->assertRaised(); } @@ -372,7 +372,7 @@ public function testInvalidDatesCombinedMin($value, $dateTimeAsString) $this->buildViolation('myMinMessage') ->setParameter('{{ value }}', $dateTimeAsString) ->setParameter('{{ limit }}', 'Mar 10, 2014, 12:00 AM') - ->setCode(Range::BELOW_RANGE_ERROR) + ->setCode(Range::TOO_LOW_ERROR) ->assertRaised(); } @@ -397,7 +397,7 @@ public function testNonNumeric() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"abcd"') - ->setCode(Range::INVALID_VALUE_ERROR) + ->setCode(Range::INVALID_CHARACTERS_ERROR) ->assertRaised(); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/RegexValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/RegexValidatorTest.php index 61917e355cdda..88e69966b59ec 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/RegexValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/RegexValidatorTest.php @@ -84,6 +84,7 @@ public function testInvalidValues($value) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$value.'"') + ->setCode(Regex::REGEX_FAILED_ERROR) ->assertRaised(); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/TypeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/TypeValidatorTest.php index 4836928014edd..51bd992d8f81d 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/TypeValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/TypeValidatorTest.php @@ -59,6 +59,7 @@ public function testEmptyIsInvalidIfNoString() $this->buildViolation('myMessage') ->setParameter('{{ value }}', '""') ->setParameter('{{ type }}', 'integer') + ->setCode(Type::INVALID_TYPE_ERROR) ->assertRaised(); } @@ -126,6 +127,7 @@ public function testInvalidValues($value, $type, $valueAsString) $this->buildViolation('myMessage') ->setParameter('{{ value }}', $valueAsString) ->setParameter('{{ type }}', $type) + ->setCode(Type::INVALID_TYPE_ERROR) ->assertRaised(); } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php index 3358c7923a98d..edc4dc8aab07b 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php @@ -126,6 +126,7 @@ public function testInvalidUrls($url) $this->buildViolation('myMessage') ->setParameter('{{ value }}', '"'.$url.'"') + ->setCode(Url::INVALID_URL_ERROR) ->assertRaised(); } diff --git a/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilderInterface.php b/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilderInterface.php index 3dc270a154911..e0d8d22c91de1 100644 --- a/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilderInterface.php +++ b/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilderInterface.php @@ -93,7 +93,7 @@ public function setPlural($number); /** * Sets the violation code. * - * @param int $code The violation code + * @param string|null $code The violation code * * @return ConstraintViolationBuilderInterface This builder */ diff --git a/src/Symfony/Component/Validator/composer.json b/src/Symfony/Component/Validator/composer.json index b89fe1b134515..1e0bebe146a14 100644 --- a/src/Symfony/Component/Validator/composer.json +++ b/src/Symfony/Component/Validator/composer.json @@ -17,16 +17,16 @@ ], "require": { "php": ">=5.3.9", - "symfony/translation": "~2.4" + "symfony/translation": "~2.4|~3.0.0" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/http-foundation": "~2.1", - "symfony/intl": "~2.3", - "symfony/yaml": "~2.0,>=2.0.5", - "symfony/config": "~2.2", - "symfony/property-access": "~2.3", - "symfony/expression-language": "~2.4", + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/http-foundation": "~2.1|~3.0.0", + "symfony/intl": "~2.3|~3.0.0", + "symfony/yaml": "~2.0,>=2.0.5|~3.0.0", + "symfony/config": "~2.2|~3.0.0", + "symfony/property-access": "~2.3|~3.0.0", + "symfony/expression-language": "~2.4|~3.0.0", "doctrine/annotations": "~1.0", "doctrine/cache": "~1.0", "egulias/email-validator": "~1.2,>=1.2.1" @@ -48,7 +48,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/VarDumper/Caster/CutArrayStub.php b/src/Symfony/Component/VarDumper/Caster/CutArrayStub.php new file mode 100644 index 0000000000000..f2a803053a6c0 --- /dev/null +++ b/src/Symfony/Component/VarDumper/Caster/CutArrayStub.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +/** + * Represents a cut array. + * + * @author Nicolas Grekas + */ +class CutArrayStub extends CutStub +{ + public $preservedSubset; + + public function __construct(array $value, array $preservedKeys) + { + parent::__construct($value); + + $this->preservedSubset = array_intersect_key($value, array_flip($preservedKeys)); + $this->cut -= count($this->preservedSubset); + } +} diff --git a/src/Symfony/Component/VarDumper/Caster/SplCaster.php b/src/Symfony/Component/VarDumper/Caster/SplCaster.php index 79e8bb80b9afc..f4b1f47cf78ee 100644 --- a/src/Symfony/Component/VarDumper/Caster/SplCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/SplCaster.php @@ -20,6 +20,13 @@ */ class SplCaster { + private static $splFileObjectFlags = array( + \SplFileObject::DROP_NEW_LINE => 'DROP_NEW_LINE', + \SplFileObject::READ_AHEAD => 'READ_AHEAD', + \SplFileObject::SKIP_EMPTY => 'SKIP_EMPTY', + \SplFileObject::READ_CSV => 'READ_CSV', + ); + public static function castArrayObject(\ArrayObject $c, array $a, Stub $stub, $isNested) { $prefix = Caster::PREFIX_VIRTUAL; @@ -72,6 +79,93 @@ public static function castDoublyLinkedList(\SplDoublyLinkedList $c, array $a, S return $a; } + public static function castFileInfo(\SplFileInfo $c, array $a, Stub $stub, $isNested) + { + static $map = array( + 'path' => 'getPath', + 'filename' => 'getFilename', + 'basename' => 'getBasename', + 'pathname' => 'getPathname', + 'extension' => 'getExtension', + 'realPath' => 'getRealPath', + 'aTime' => 'getATime', + 'mTime' => 'getMTime', + 'cTime' => 'getCTime', + 'inode' => 'getInode', + 'size' => 'getSize', + 'perms' => 'getPerms', + 'owner' => 'getOwner', + 'group' => 'getGroup', + 'type' => 'getType', + 'writable' => 'isWritable', + 'readable' => 'isReadable', + 'executable' => 'isExecutable', + 'file' => 'isFile', + 'dir' => 'isDir', + 'link' => 'isLink', + 'linkTarget' => 'getLinkTarget', + ); + + $prefix = Caster::PREFIX_VIRTUAL; + + foreach ($map as $key => $accessor) { + try { + $a[$prefix.$key] = $c->$accessor(); + } catch (\Exception $e) { + } + } + + if (isset($a[$prefix.'perms'])) { + $a[$prefix.'perms'] = new ConstStub(sprintf('0%o', $a[$prefix.'perms']), $a[$prefix.'perms']); + } + + static $mapDate = array('aTime', 'mTime', 'cTime'); + foreach ($mapDate as $key) { + if (isset($a[$prefix.$key])) { + $a[$prefix.$key] = new ConstStub(date('Y-m-d H:i:s', $a[$prefix.$key]), $a[$prefix.$key]); + } + } + + return $a; + } + + public static function castFileObject(\SplFileObject $c, array $a, Stub $stub, $isNested) + { + static $map = array( + 'csvControl' => 'getCsvControl', + 'flags' => 'getFlags', + 'maxLineLen' => 'getMaxLineLen', + 'fstat' => 'fstat', + 'eof' => 'eof', + 'key' => 'key', + ); + + $prefix = Caster::PREFIX_VIRTUAL; + + foreach ($map as $key => $accessor) { + try { + $a[$prefix.$key] = $c->$accessor(); + } catch (\Exception $e) { + } + } + + if (isset($a[$prefix.'flags'])) { + $flagsArray = array(); + foreach (self::$splFileObjectFlags as $value => $name) { + if ($a[$prefix.'flags'] & $value) { + $flagsArray[] = $name; + } + } + $a[$prefix.'flags'] = new ConstStub(implode('|', $flagsArray), $a[$prefix.'flags']); + } + + if (isset($a[$prefix.'fstat'])) { + $a[$prefix.'fstat'] = new CutArrayStub($a[$prefix.'fstat'], array('dev', 'ino', 'nlink', 'rdev', 'blksize', 'blocks')); + } + + return $a; + } + public static function castFixedArray(\SplFixedArray $c, array $a, Stub $stub, $isNested) { $a += array( diff --git a/src/Symfony/Component/VarDumper/Caster/StubCaster.php b/src/Symfony/Component/VarDumper/Caster/StubCaster.php index ab0f52e55ae9d..542f8a19f4afb 100644 --- a/src/Symfony/Component/VarDumper/Caster/StubCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/StubCaster.php @@ -33,6 +33,11 @@ public static function castStub(Stub $c, array $a, Stub $stub, $isNested) } } + public static function castCutArray(CutArrayStub $c, array $a, Stub $stub, $isNested) + { + return $isNested ? $c->preservedSubset : $a; + } + public static function cutInternals($obj, array $a, Stub $stub, $isNested) { if ($isNested) { diff --git a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php index bdc2863437099..c19cf06dd4f7c 100644 --- a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php @@ -23,6 +23,7 @@ abstract class AbstractCloner implements ClonerInterface { public static $defaultCasters = array( 'Symfony\Component\VarDumper\Caster\CutStub' => 'Symfony\Component\VarDumper\Caster\StubCaster::castStub', + 'Symfony\Component\VarDumper\Caster\CutArrayStub' => 'Symfony\Component\VarDumper\Caster\StubCaster::castCutArray', 'Symfony\Component\VarDumper\Caster\ConstStub' => 'Symfony\Component\VarDumper\Caster\StubCaster::castStub', 'Closure' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castClosure', @@ -68,6 +69,10 @@ abstract class AbstractCloner implements ClonerInterface 'Symfony\Component\DependencyInjection\ContainerInterface' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals', 'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castThrowingCasterException', + 'PHPUnit_Framework_MockObject_MockObject' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals', + 'Prophecy\Prophecy\ProphecySubjectInterface' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals', + 'Mockery\MockInterface' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals', + 'PDO' => 'Symfony\Component\VarDumper\Caster\PdoCaster::castPdo', 'PDOStatement' => 'Symfony\Component\VarDumper\Caster\PdoCaster::castPdoStatement', @@ -79,6 +84,8 @@ abstract class AbstractCloner implements ClonerInterface 'ArrayObject' => 'Symfony\Component\VarDumper\Caster\SplCaster::castArrayObject', 'SplDoublyLinkedList' => 'Symfony\Component\VarDumper\Caster\SplCaster::castDoublyLinkedList', + 'SplFileInfo' => 'Symfony\Component\VarDumper\Caster\SplCaster::castFileInfo', + 'SplFileObject' => 'Symfony\Component\VarDumper\Caster\SplCaster::castFileObject', 'SplFixedArray' => 'Symfony\Component\VarDumper\Caster\SplCaster::castFixedArray', 'SplHeap' => 'Symfony\Component\VarDumper\Caster\SplCaster::castHeap', 'SplObjectStorage' => 'Symfony\Component\VarDumper\Caster\SplCaster::castObjectStorage', diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php index ecf776d70f416..abd1e076981ac 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php @@ -11,8 +11,6 @@ namespace Symfony\Component\VarDumper\Tests\Caster; -use Symfony\Component\VarDumper\Cloner\VarCloner; -use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Test\VarDumperTestCase; /** diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/SplCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/SplCasterTest.php new file mode 100644 index 0000000000000..0bcce0c4bb0bc --- /dev/null +++ b/src/Symfony/Component/VarDumper/Tests/Caster/SplCasterTest.php @@ -0,0 +1,124 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Caster; + +use Symfony\Component\VarDumper\Test\VarDumperTestCase; + +/** + * @author Grégoire Pineau + */ +class SplCasterTest extends VarDumperTestCase +{ + public function getCastFileInfoTests() + { + return array( + array(__FILE__, <<<'EOTXT' +SplFileInfo { +%Apath: "%s/Tests/Caster" + filename: "SplCasterTest.php" + basename: "SplCasterTest.php" + pathname: "%s/Tests/Caster/SplCasterTest.php" + extension: "php" + realPath: "%s/Tests/Caster/SplCasterTest.php" + aTime: %s-%s-%d %d:%d:%d + mTime: %s-%s-%d %d:%d:%d + cTime: %s-%s-%d %d:%d:%d + inode: %d + size: %d + perms: 0%d + owner: %d + group: %d + type: "file" + writable: true + readable: true + executable: false + file: true + dir: false + link: false +} +EOTXT + ), + array('https://google.com/about', <<<'EOTXT' +SplFileInfo { +%Apath: "https://google.com" + filename: "about" + basename: "about" + pathname: "https://google.com/about" + extension: "" + realPath: false + writable: false + readable: false + executable: false + file: false + dir: false + link: false +} +EOTXT + ), + ); + } + + /** @dataProvider getCastFileInfoTests */ + public function testCastFileInfo($file, $dump) + { + $this->assertDumpMatchesFormat($dump, new \SplFileInfo($file)); + } + + public function testCastFileObject() + { + $var = new \SplFileObject(__FILE__); + $var->setFlags(\SplFileObject::DROP_NEW_LINE | \SplFileObject::SKIP_EMPTY); + $dump = <<<'EOTXT' +SplFileObject { +%Apath: "%s/Tests/Caster" + filename: "SplCasterTest.php" + basename: "SplCasterTest.php" + pathname: "%s/Tests/Caster/SplCasterTest.php" + extension: "php" + realPath: "%s/Tests/Caster/SplCasterTest.php" + aTime: %s-%s-%d %d:%d:%d + mTime: %s-%s-%d %d:%d:%d + cTime: %s-%s-%d %d:%d:%d + inode: %d + size: %d + perms: 0%d + owner: %d + group: %d + type: "file" + writable: true + readable: true + executable: false + file: true + dir: false + link: false + csvControl: array:2 [ + 0 => "," + 1 => """ + ] + flags: DROP_NEW_LINE|SKIP_EMPTY + maxLineLen: 0 + fstat: array:26 [ + "dev" => %d + "ino" => %d + "nlink" => %d + "rdev" => 0 + "blksize" => %d + "blocks" => %d + …20 + ] + eof: false + key: 0 +} +EOTXT; + $this->assertDumpMatchesFormat($dump, $var); + } +} diff --git a/src/Symfony/Component/VarDumper/composer.json b/src/Symfony/Component/VarDumper/composer.json index 5fec14fb23fb0..50b06fdcd27ee 100644 --- a/src/Symfony/Component/VarDumper/composer.json +++ b/src/Symfony/Component/VarDumper/composer.json @@ -19,7 +19,7 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7" + "symfony/phpunit-bridge": "~2.7|~3.0.0" }, "suggest": { "ext-symfony_debug": "" @@ -31,7 +31,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/src/Symfony/Component/Yaml/composer.json b/src/Symfony/Component/Yaml/composer.json index b07b439c96ec4..177608a79fec4 100644 --- a/src/Symfony/Component/Yaml/composer.json +++ b/src/Symfony/Component/Yaml/composer.json @@ -19,7 +19,7 @@ "php": ">=5.3.9" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7" + "symfony/phpunit-bridge": "~2.7|~3.0.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Yaml\\": "" } @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } pFad - Phonifier reborn

    Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

    Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


    Alternative Proxies:

    Alternative Proxy

    pFad Proxy

    pFad v3 Proxy

    pFad v4 Proxy