From ecc3df5aae49298368f0fcc193b65a07baefd9df Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 30 Mar 2015 20:17:55 +0200 Subject: [PATCH 01/20] let Travis builds fail when PHP 7 jobs fail --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b27111e496..e06aab56b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,6 @@ matrix: - php: 5.6 env: deps=high - php: nightly - allow_failures: - - php: nightly fast_finish: true services: mongodb From 75c98cba039b851835ef86034cea2be299d24aac Mon Sep 17 00:00:00 2001 From: Diego Saint Esteben Date: Thu, 30 Jul 2015 22:41:40 -0300 Subject: [PATCH 02/20] Added a way to define the priority of service decoration --- .../DependencyInjection/CHANGELOG.md | 1 + .../Compiler/DecoratorServicePass.php | 12 +++++- .../DependencyInjection/Definition.php | 7 ++-- .../DefinitionDecorator.php | 4 +- .../DependencyInjection/Dumper/XmlDumper.php | 5 ++- .../DependencyInjection/Dumper/YamlDumper.php | 5 ++- .../Loader/XmlFileLoader.php | 3 +- .../Loader/YamlFileLoader.php | 3 +- .../schema/dic/services/services-1.0.xsd | 1 + .../Compiler/DecoratorServicePassTest.php | 42 +++++++++++++++++++ .../ResolveDefinitionTemplatesPassTest.php | 2 +- .../Tests/DefinitionTest.php | 11 ++++- .../Tests/Fixtures/xml/services6.xml | 1 + .../Tests/Fixtures/yaml/services6.yml | 4 ++ .../Tests/Loader/XmlFileLoaderTest.php | 5 ++- .../Tests/Loader/YamlFileLoaderTest.php | 5 ++- 16 files changed, 94 insertions(+), 17 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index c13da0b280..02cb2d50ec 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -8,6 +8,7 @@ CHANGELOG * 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 + * added a way to define the priority of service decoration 2.7.0 ----- diff --git a/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php index ef0a19c6a7..f80d705a9b 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php @@ -19,18 +19,28 @@ use Symfony\Component\DependencyInjection\Alias; * * @author Christophe Coevoet * @author Fabien Potencier + * @author Diego Saint Esteben */ class DecoratorServicePass implements CompilerPassInterface { public function process(ContainerBuilder $container) { + $definitions = new \SplPriorityQueue(); + $order = PHP_INT_MAX; + foreach ($container->getDefinitions() as $id => $definition) { if (!$decorated = $definition->getDecoratedService()) { continue; } + $definitions->insert(array($id, $definition), array($decorated[2], --$order)); + } + + foreach ($definitions as $arr) { + list($id, $definition) = $arr; + list($inner, $renamedId) = $definition->getDecoratedService(); + $definition->setDecoratedService(null); - list($inner, $renamedId) = $decorated; if (!$renamedId) { $renamedId = $id.'.inner'; } diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index 4e99cca77a..49d705cf7d 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -150,12 +150,13 @@ class Definition * * @param null|string $id The decorated service id, use null to remove decoration * @param null|string $renamedId The new decorated service id + * @param int $priority The priority of decoration * * @return Definition The current instance * * @throws InvalidArgumentException In case the decorated service id and the new decorated service id are equals. */ - public function setDecoratedService($id, $renamedId = null) + public function setDecoratedService($id, $renamedId = null, $priority = 0) { if ($renamedId && $id == $renamedId) { throw new \InvalidArgumentException(sprintf('The decorated service inner name for "%s" must be different than the service name itself.', $id)); @@ -164,7 +165,7 @@ class Definition if (null === $id) { $this->decoratedService = null; } else { - $this->decoratedService = array($id, $renamedId); + $this->decoratedService = array($id, $renamedId, (int) $priority); } return $this; @@ -173,7 +174,7 @@ class Definition /** * Gets the service that decorates this service. * - * @return null|array An array composed of the decorated service id and the new id for it, null if no service is decorated + * @return null|array An array composed of the decorated service id, the new id for it and the priority of decoration, null if no service is decorated */ public function getDecoratedService() { diff --git a/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php b/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php index f5a1485a5f..7764079d51 100644 --- a/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php +++ b/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php @@ -173,11 +173,11 @@ class DefinitionDecorator extends Definition /** * {@inheritdoc} */ - public function setDecoratedService($id, $renamedId = null) + public function setDecoratedService($id, $renamedId = null, $priority = 0) { $this->changes['decorated_service'] = true; - return parent::setDecoratedService($id, $renamedId); + return parent::setDecoratedService($id, $renamedId, $priority); } /** diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index 1f9183f418..aec8d1fe7d 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -145,11 +145,14 @@ class XmlDumper extends Dumper $service->setAttribute('lazy', 'true'); } if (null !== $decorated = $definition->getDecoratedService()) { - list($decorated, $renamedId) = $decorated; + list($decorated, $renamedId, $priority) = $decorated; $service->setAttribute('decorates', $decorated); if (null !== $renamedId) { $service->setAttribute('decoration-inner-name', $renamedId); } + if (0 !== $priority) { + $service->setAttribute('decoration-priority', $priority); + } } foreach ($definition->getTags() as $name => $tags) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index e1a5709dd3..bce1b6a88f 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -137,11 +137,14 @@ class YamlDumper extends Dumper } if (null !== $decorated = $definition->getDecoratedService()) { - list($decorated, $renamedId) = $decorated; + list($decorated, $renamedId, $priority) = $decorated; $code .= sprintf(" decorates: %s\n", $decorated); if (null !== $renamedId) { $code .= sprintf(" decoration_inner_name: %s\n", $renamedId); } + if (0 !== $priority) { + $code .= sprintf(" decoration_priority: %s\n", $priority); + } } if ($callable = $definition->getFactory()) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 15b6e60366..de2ea9852a 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -245,7 +245,8 @@ class XmlFileLoader extends FileLoader if ($value = $service->getAttribute('decorates')) { $renameId = $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null; - $definition->setDecoratedService($value, $renameId); + $priority = $service->hasAttribute('decoration-priority') ? $service->getAttribute('decoration-priority') : 0; + $definition->setDecoratedService($value, $renameId, $priority); } return $definition; diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 3b4c5cda80..ce04d10afa 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -290,7 +290,8 @@ class YamlFileLoader extends FileLoader if (isset($service['decorates'])) { $renameId = isset($service['decoration_inner_name']) ? $service['decoration_inner_name'] : null; - $definition->setDecoratedService($service['decorates'], $renameId); + $priority = isset($service['decoration_priority']) ? $service['decoration_priority'] : 0; + $definition->setDecoratedService($service['decorates'], $renameId, $priority); } $this->container->setDefinition($id, $definition); 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 11f869c04a..15bdd791dc 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 @@ -101,6 +101,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php index e17961ac99..952f7a0bd8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php @@ -73,6 +73,48 @@ class DecoratorServicePassTest extends \PHPUnit_Framework_TestCase $this->assertNull($fooExtendedDefinition->getDecoratedService()); } + public function testProcessWithPriority() + { + $container = new ContainerBuilder(); + $fooDefinition = $container + ->register('foo') + ->setPublic(false) + ; + $barDefinition = $container + ->register('bar') + ->setPublic(true) + ->setDecoratedService('foo') + ; + $bazDefinition = $container + ->register('baz') + ->setPublic(true) + ->setDecoratedService('foo', null, 5) + ; + $quxDefinition = $container + ->register('qux') + ->setPublic(true) + ->setDecoratedService('foo', null, 3) + ; + + $this->process($container); + + $this->assertEquals('bar', $container->getAlias('foo')); + $this->assertFalse($container->getAlias('foo')->isPublic()); + + $this->assertSame($fooDefinition, $container->getDefinition('baz.inner')); + $this->assertFalse($container->getDefinition('baz.inner')->isPublic()); + + $this->assertEquals('qux', $container->getAlias('bar.inner')); + $this->assertFalse($container->getAlias('bar.inner')->isPublic()); + + $this->assertEquals('baz', $container->getAlias('qux.inner')); + $this->assertFalse($container->getAlias('qux.inner')->isPublic()); + + $this->assertNull($barDefinition->getDecoratedService()); + $this->assertNull($bazDefinition->getDecoratedService()); + $this->assertNull($quxDefinition->getDecoratedService()); + } + protected function process(ContainerBuilder $container) { $repeatedPass = new DecoratorServicePass(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php index 9f1cc87b1d..2180483e2b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php @@ -241,7 +241,7 @@ class ResolveDefinitionTemplatesPassTest extends \PHPUnit_Framework_TestCase ->setDecoratedService('foo', 'foo_inner') ; - $this->assertEquals(array('foo', 'foo_inner'), $container->getDefinition('child1')->getDecoratedService()); + $this->assertEquals(array('foo', 'foo_inner', 0), $container->getDefinition('child1')->getDecoratedService()); } protected function process(ContainerBuilder $container) diff --git a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php index d67f1d7224..e8df122953 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php @@ -56,16 +56,23 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase public function testSetGetDecoratedService() { + $def = new Definition('stdClass'); + $this->assertNull($def->getDecoratedService()); + $def->setDecoratedService('foo', 'foo.renamed', 5); + $this->assertEquals(array('foo', 'foo.renamed', 5), $def->getDecoratedService()); + $def->setDecoratedService(null); + $this->assertNull($def->getDecoratedService()); + $def = new Definition('stdClass'); $this->assertNull($def->getDecoratedService()); $def->setDecoratedService('foo', 'foo.renamed'); - $this->assertEquals(array('foo', 'foo.renamed'), $def->getDecoratedService()); + $this->assertEquals(array('foo', 'foo.renamed', 0), $def->getDecoratedService()); $def->setDecoratedService(null); $this->assertNull($def->getDecoratedService()); $def = new Definition('stdClass'); $def->setDecoratedService('foo'); - $this->assertEquals(array('foo', null), $def->getDecoratedService()); + $this->assertEquals(array('foo', null, 0), $def->getDecoratedService()); $def->setDecoratedService(null); $this->assertNull($def->getDecoratedService()); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml index a1a320bd82..4595c668c7 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml @@ -47,6 +47,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml index 2186620045..6a377c91d3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml @@ -26,6 +26,10 @@ services: decorator_service_with_name: decorates: decorated decoration_inner_name: decorated.pif-pouf + decorator_service_with_name_and_priority: + decorates: decorated + decoration_inner_name: decorated.pif-pouf + decoration_priority: 5 new_factory1: { class: FooBarClass, factory: factory} new_factory2: { class: FooBarClass, factory: [@baz, getClass]} new_factory3: { class: FooBarClass, factory: [BazClass, getInstance]} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 581dc675fb..b717113bb4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -242,8 +242,9 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase $this->assertEquals('foo', (string) $aliases['another_alias_for_foo']); $this->assertFalse($aliases['another_alias_for_foo']->isPublic()); - $this->assertEquals(array('decorated', null), $services['decorator_service']->getDecoratedService()); - $this->assertEquals(array('decorated', 'decorated.pif-pouf'), $services['decorator_service_with_name']->getDecoratedService()); + $this->assertEquals(array('decorated', null, 0), $services['decorator_service']->getDecoratedService()); + $this->assertEquals(array('decorated', 'decorated.pif-pouf', 0), $services['decorator_service_with_name']->getDecoratedService()); + $this->assertEquals(array('decorated', 'decorated.pif-pouf', 5), $services['decorator_service_with_name_and_priority']->getDecoratedService()); } public function testParsesTags() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 058f5b66df..45f1cb496d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -171,8 +171,9 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase $this->assertEquals('foo', (string) $aliases['another_alias_for_foo']); $this->assertFalse($aliases['another_alias_for_foo']->isPublic()); - $this->assertEquals(array('decorated', null), $services['decorator_service']->getDecoratedService()); - $this->assertEquals(array('decorated', 'decorated.pif-pouf'), $services['decorator_service_with_name']->getDecoratedService()); + $this->assertEquals(array('decorated', null, 0), $services['decorator_service']->getDecoratedService()); + $this->assertEquals(array('decorated', 'decorated.pif-pouf', 0), $services['decorator_service_with_name']->getDecoratedService()); + $this->assertEquals(array('decorated', 'decorated.pif-pouf', 5), $services['decorator_service_with_name_and_priority']->getDecoratedService()); } public function testLoadFactoryShortSyntax() From 07b38dea1f76bdba9800e9106b9db2e2d0a65ef2 Mon Sep 17 00:00:00 2001 From: Alexandru Furculita Date: Tue, 3 Mar 2015 00:52:39 +0200 Subject: [PATCH 03/20] Better Iban Validation Added more country-based tests, added new error type The error type `NOT_SUPPORTED_COUNTRY_CODE_ERROR` has been added for the IBANs from unsupported countries, for which we don't have defined a format. We will not check anymore if a country code against the Intl component. The tests have been completed with more tests for each contry we have formats defined. The 4 character length check and the case check has been removed. The message code constants `TOO_SHORT_ERROR` and `INVALID_CASE_ERROR` has been deprecated --- .../Component/Validator/Constraints/Iban.php | 6 + .../Validator/Constraints/IbanValidator.php | 205 +++++++++--- .../Tests/Constraints/IbanValidatorTest.php | 297 ++++++++++++++++-- 3 files changed, 437 insertions(+), 71 deletions(-) diff --git a/src/Symfony/Component/Validator/Constraints/Iban.php b/src/Symfony/Component/Validator/Constraints/Iban.php index 66ce09ae1a..c4dc9856c6 100644 --- a/src/Symfony/Component/Validator/Constraints/Iban.php +++ b/src/Symfony/Component/Validator/Constraints/Iban.php @@ -23,11 +23,15 @@ use Symfony\Component\Validator\Constraint; */ class Iban extends Constraint { + /** @deprecated, to be removed in 3.0. */ const TOO_SHORT_ERROR = 1; const INVALID_COUNTRY_CODE_ERROR = 2; const INVALID_CHARACTERS_ERROR = 3; + /** @deprecated, to be removed in 3.0. */ const INVALID_CASE_ERROR = 4; const CHECKSUM_FAILED_ERROR = 5; + const INVALID_FORMAT_ERROR = 6; + const NOT_SUPPORTED_COUNTRY_CODE_ERROR = 7; protected static $errorNames = array( self::TOO_SHORT_ERROR => 'TOO_SHORT_ERROR', @@ -35,6 +39,8 @@ class Iban extends Constraint self::INVALID_CHARACTERS_ERROR => 'INVALID_CHARACTERS_ERROR', self::INVALID_CASE_ERROR => 'INVALID_CASE_ERROR', self::CHECKSUM_FAILED_ERROR => 'CHECKSUM_FAILED_ERROR', + self::INVALID_FORMAT_ERROR => 'INVALID_FORMAT_ERROR', + self::NOT_SUPPORTED_COUNTRY_CODE_ERROR => 'NOT_SUPPORTED_COUNTRY_CODE_ERROR', ); public $message = 'This is not a valid International Bank Account Number (IBAN).'; diff --git a/src/Symfony/Component/Validator/Constraints/IbanValidator.php b/src/Symfony/Component/Validator/Constraints/IbanValidator.php index 65c22ff9c0..72ae002675 100644 --- a/src/Symfony/Component/Validator/Constraints/IbanValidator.php +++ b/src/Symfony/Component/Validator/Constraints/IbanValidator.php @@ -25,6 +25,118 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException; */ class IbanValidator extends ConstraintValidator { + /** + * IBAN country specific formats. + * + * The first 2 characters from an IBAN format are the two-character ISO country code. + * The following 2 characters represent the check digits calculated from the rest of the IBAN characters. + * The rest are up to thirty alphanumeric characters for + * a BBAN (Basic Bank Account Number) which has a fixed length per country and, + * included within it, a bank identifier with a fixed position and a fixed length per country + * + * @link http://www.swift.com/dsp/resources/documents/IBAN_Registry.pdf + * + * @var array + */ + private static $formats = array( + 'AD' => 'AD\d{2}\d{4}\d{4}[\dA-Z]{12}', // Andorra + 'AE' => 'AE\d{2}\d{3}\d{16}', // United Arab Emirates + 'AL' => 'AL\d{2}\d{8}[\dA-Z]{16}', // Albania + 'AO' => 'AO\d{2}\d{21}', // Angola + 'AT' => 'AT\d{2}\d{5}\d{11}', // Austria + 'AX' => 'FI\d{2}\d{6}\d{7}\d{1}', // Aland Islands + 'AZ' => 'AZ\d{2}[A-Z]{4}[\dA-Z]{20}', // Azerbaijan + 'BA' => 'BA\d{2}\d{3}\d{3}\d{8}\d{2}', // Bosnia and Herzegovina + 'BE' => 'BE\d{2}\d{3}\d{7}\d{2}', // Belgium + 'BF' => 'BF\d{2}\d{23}', // Burkina Faso + 'BG' => 'BG\d{2}[A-Z]{4}\d{4}\d{2}[\dA-Z]{8}', // Bulgaria + 'BH' => 'BH\d{2}[A-Z]{4}[\dA-Z]{14}', // Bahrain + 'BI' => 'BI\d{2}\d{12}', // Burundi + 'BJ' => 'BJ\d{2}[A-Z]{1}\d{23}', // Benin + 'BL' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Saint Barthelemy + 'BR' => 'BR\d{2}\d{8}\d{5}\d{10}[A-Z][\dA-Z]', // Brazil + 'CG' => 'CG\d{2}\d{23}', // Congo + 'CH' => 'CH\d{2}\d{5}[\dA-Z]{12}', // Switzerland + 'CI' => 'CI\d{2}[A-Z]{1}\d{23}', // Ivory Coast + 'CM' => 'CM\d{2}\d{23}', // Cameron + 'CR' => 'CR\d{2}\d{3}\d{14}', // Costa Rica + 'CV' => 'CV\d{2}\d{21}', // Cape Verde + 'CY' => 'CY\d{2}\d{3}\d{5}[\dA-Z]{16}', // Cyprus + 'CZ' => 'CZ\d{2}\d{20}', // Czech Republic + 'DE' => 'DE\d{2}\d{8}\d{10}', // Germany + 'DO' => 'DO\d{2}[\dA-Z]{4}\d{20}', // Dominican Republic + 'DK' => 'DK\d{2}\d{4}\d{10}', // Denmark + 'DZ' => 'DZ\d{2}\d{20}', // Algeria + 'EE' => 'EE\d{2}\d{2}\d{2}\d{11}\d{1}', // Estonia + 'ES' => 'ES\d{2}\d{4}\d{4}\d{1}\d{1}\d{10}', // Spain (also includes Canary Islands, Ceuta and Melilla) + 'FI' => 'FI\d{2}\d{6}\d{7}\d{1}', // Finland + 'FO' => 'FO\d{2}\d{4}\d{9}\d{1}', // Faroe Islands + 'FR' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // France + 'GF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // French Guyana + 'GB' => 'GB\d{2}[A-Z]{4}\d{6}\d{8}', // United Kingdom of Great Britain and Northern Ireland + 'GE' => 'GE\d{2}[A-Z]{2}\d{16}', // Georgia + 'GI' => 'GI\d{2}[A-Z]{4}[\dA-Z]{15}', // Gibraltar + 'GL' => 'GL\d{2}\d{4}\d{9}\d{1}', // Greenland + 'GP' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Guadeloupe + 'GR' => 'GR\d{2}\d{3}\d{4}[\dA-Z]{16}', // Greece + 'GT' => 'GT\d{2}[\dA-Z]{4}[\dA-Z]{20}', // Guatemala + 'HR' => 'HR\d{2}\d{7}\d{10}', // Croatia + 'HU' => 'HU\d{2}\d{3}\d{4}\d{1}\d{15}\d{1}', // Hungary + 'IE' => 'IE\d{2}[A-Z]{4}\d{6}\d{8}', // Ireland + 'IL' => 'IL\d{2}\d{3}\d{3}\d{13}', // Israel + 'IR' => 'IR\d{2}\d{22}', // Iran + 'IS' => 'IS\d{2}\d{4}\d{2}\d{6}\d{10}', // Iceland + 'IT' => 'IT\d{2}[A-Z]{1}\d{5}\d{5}[\dA-Z]{12}', // Italy + 'JO' => 'JO\d{2}[A-Z]{4}\d{4}[\dA-Z]{18}', // Jordan + 'KW' => 'KW\d{2}[A-Z]{4}\d{22}', // KUWAIT + 'KZ' => 'KZ\d{2}\d{3}[\dA-Z]{13}', // Kazakhstan + 'LB' => 'LB\d{2}\d{4}[\dA-Z]{20}', // LEBANON + 'LI' => 'LI\d{2}\d{5}[\dA-Z]{12}', // Liechtenstein (Principality of) + 'LT' => 'LT\d{2}\d{5}\d{11}', // Lithuania + 'LU' => 'LU\d{2}\d{3}[\dA-Z]{13}', // Luxembourg + 'LV' => 'LV\d{2}[A-Z]{4}[\dA-Z]{13}', // Latvia + 'MC' => 'MC\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Monaco + 'MD' => 'MD\d{2}[\dA-Z]{2}[\dA-Z]{18}', // Moldova + 'ME' => 'ME\d{2}\d{3}\d{13}\d{2}', // Montenegro + 'MF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Saint Martin (French part) + 'MG' => 'MG\d{2}\d{23}', // Madagascar + 'MK' => 'MK\d{2}\d{3}[\dA-Z]{10}\d{2}', // Macedonia, Former Yugoslav Republic of + 'ML' => 'ML\d{2}[A-Z]{1}\d{23}', // Mali + 'MQ' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Martinique + 'MR' => 'MR13\d{5}\d{5}\d{11}\d{2}', // Mauritania + 'MT' => 'MT\d{2}[A-Z]{4}\d{5}[\dA-Z]{18}', // Malta + 'MU' => 'MU\d{2}[A-Z]{4}\d{2}\d{2}\d{12}\d{3}[A-Z]{3}', // Mauritius + 'MZ' => 'MZ\d{2}\d{21}', // Mozambique + 'NC' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // New Caledonia + 'NL' => 'NL\d{2}[A-Z]{4}\d{10}', // The Netherlands + 'NO' => 'NO\d{2}\d{4}\d{6}\d{1}', // Norway + 'PF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // French Polynesia + 'PK' => 'PK\d{2}[A-Z]{4}[\dA-Z]{16}', // Pakistan + 'PL' => 'PL\d{2}\d{8}\d{16}', // Poland + 'PM' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Saint Pierre et Miquelon + 'PS' => 'PS\d{2}[A-Z]{4}[\dA-Z]{21}', // Palestine, State of + 'PT' => 'PT\d{2}\d{4}\d{4}\d{11}\d{2}', // Portugal (plus Azores and Madeira) + 'QA' => 'QA\d{2}[A-Z]{4}[\dA-Z]{21}', // Qatar + 'RE' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Reunion + 'RO' => 'RO\d{2}[A-Z]{4}[\dA-Z]{16}', // Romania + 'RS' => 'RS\d{2}\d{3}\d{13}\d{2}', // Serbia + 'SA' => 'SA\d{2}\d{2}[\dA-Z]{18}', // Saudi Arabia + 'SE' => 'SE\d{2}\d{3}\d{16}\d{1}', // Sweden + 'SI' => 'SI\d{2}\d{5}\d{8}\d{2}', // Slovenia + 'SK' => 'SK\d{2}\d{4}\d{6}\d{10}', // Slovak Republic + 'SM' => 'SM\d{2}[A-Z]{1}\d{5}\d{5}[\dA-Z]{12}', // San Marino + 'SN' => 'SN\d{2}[A-Z]{1}\d{23}', // Senegal + 'TF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // French Southern Territories + 'TL' => 'TL\d{2}\d{3}\d{14}\d{2}', // Timor-Leste + 'TN' => 'TN59\d{2}\d{3}\d{13}\d{2}', // Tunisia + 'TR' => 'TR\d{2}\d{5}[\dA-Z]{1}[\dA-Z]{16}', // Turkey + 'UA' => 'UA\d{2}[A-Z]{6}[\dA-Z]{19}', // Ukraine + 'VG' => 'VG\d{2}[A-Z]{4}\d{16}', // Virgin Islands, British + 'WF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Wallis and Futuna Islands + 'XK' => 'XK\d{2}\d{4}\d{10}\d{2}', // Republic of Kosovo + 'YT' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Mayotte + ); + /** * {@inheritdoc} */ @@ -44,44 +156,10 @@ class IbanValidator extends ConstraintValidator $value = (string) $value; - // Remove spaces - $canonicalized = str_replace(' ', '', $value); + // Remove spaces and convert to uppercase + $canonicalized = str_replace(' ', '', strtoupper($value)); - // The IBAN must have at least 4 characters... - if (strlen($canonicalized) < 4) { - if ($this->context instanceof ExecutionContextInterface) { - $this->context->buildViolation($constraint->message) - ->setParameter('{{ value }}', $this->formatValue($value)) - ->setCode(Iban::TOO_SHORT_ERROR) - ->addViolation(); - } else { - $this->buildViolation($constraint->message) - ->setParameter('{{ value }}', $this->formatValue($value)) - ->setCode(Iban::TOO_SHORT_ERROR) - ->addViolation(); - } - - return; - } - - // ...start with a country code... - if (!ctype_alpha($canonicalized{0}) || !ctype_alpha($canonicalized{1})) { - if ($this->context instanceof ExecutionContextInterface) { - $this->context->buildViolation($constraint->message) - ->setParameter('{{ value }}', $this->formatValue($value)) - ->setCode(Iban::INVALID_COUNTRY_CODE_ERROR) - ->addViolation(); - } else { - $this->buildViolation($constraint->message) - ->setParameter('{{ value }}', $this->formatValue($value)) - ->setCode(Iban::INVALID_COUNTRY_CODE_ERROR) - ->addViolation(); - } - - return; - } - - // ...contain only digits and characters... + // The IBAN must contain only digits and characters... if (!ctype_alnum($canonicalized)) { if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->message) @@ -98,17 +176,54 @@ class IbanValidator extends ConstraintValidator return; } - // ...and contain uppercase characters only - if ($canonicalized !== strtoupper($canonicalized)) { + // ...start with a two-letter country code + $countryCode = substr($canonicalized, 0, 2); + + if (!ctype_alpha($countryCode)) { if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) - ->setCode(Iban::INVALID_CASE_ERROR) + ->setCode(Iban::INVALID_COUNTRY_CODE_ERROR) ->addViolation(); } else { $this->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) - ->setCode(Iban::INVALID_CASE_ERROR) + ->setCode(Iban::INVALID_COUNTRY_CODE_ERROR) + ->addViolation(); + } + + return; + } + + // ...have a format available + if (!array_key_exists($countryCode, self::$formats)) { + if ($this->context instanceof ExecutionContextInterface) { + $this->context->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Iban::NOT_SUPPORTED_COUNTRY_CODE_ERROR) + ->addViolation(); + } else { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Iban::NOT_SUPPORTED_COUNTRY_CODE_ERROR) + ->addViolation(); + } + + return; + } + + // ...and have a valid format + if (!preg_match('/^'.self::$formats[$countryCode].'$/', $canonicalized) + ) { + if ($this->context instanceof ExecutionContextInterface) { + $this->context->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Iban::INVALID_FORMAT_ERROR) + ->addViolation(); + } else { + $this->buildViolation($constraint->message) + ->setParameter('{{ value }}', $this->formatValue($value)) + ->setCode(Iban::INVALID_FORMAT_ERROR) ->addViolation(); } @@ -125,12 +240,12 @@ class IbanValidator extends ConstraintValidator // data type, so we store it in a string instead. // e.g. 0076 2011 6238 5295 7 CH93 // -> 0076 2011 6238 5295 7 121893 - $checkSum = $this->toBigInt($canonicalized); + $checkSum = self::toBigInt($canonicalized); // Do a modulo-97 operation on the large integer // We cannot use PHP's modulo operator, so we calculate the // modulo step-wisely instead - if (1 !== $this->bigModulo97($checkSum)) { + if (1 !== self::bigModulo97($checkSum)) { if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) @@ -145,7 +260,7 @@ class IbanValidator extends ConstraintValidator } } - private function toBigInt($string) + private static function toBigInt($string) { $chars = str_split($string); $bigInt = ''; @@ -165,7 +280,7 @@ class IbanValidator extends ConstraintValidator return $bigInt; } - private function bigModulo97($bigInt) + private static function bigModulo97($bigInt) { $parts = str_split($bigInt, 7); $rest = 0; diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IbanValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IbanValidatorTest.php index ab9839a238..e9deb11de4 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/IbanValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/IbanValidatorTest.php @@ -57,8 +57,8 @@ class IbanValidatorTest extends AbstractConstraintValidatorTest array('CH9300762011623852957'), // Switzerland without spaces array('CH93 0076 2011 6238 5295 7'), // Switzerland with multiple spaces - //Country list - //http://www.rbs.co.uk/corporate/international/g0/guide-to-international-business/regulatory-information/iban/iban-example.ashx + // Country list + // http://www.rbs.co.uk/corporate/international/g0/guide-to-international-business/regulatory-information/iban/iban-example.ashx array('AL47 2121 1009 0000 0002 3569 8741'), //Albania array('AD12 0001 2030 2003 5910 0100'), //Andorra @@ -114,14 +114,17 @@ class IbanValidatorTest extends AbstractConstraintValidatorTest array('TN59 1000 6035 1835 9847 8831'), //Tunisia array('TR33 0006 1005 1978 6457 8413 26'), //Turkey array('AE07 0331 2345 6789 0123 456'), //UAE - array('GB 12 CPBK 0892 9965 0449 91'), //United Kingdom + array('GB12 CPBK 0892 9965 0449 91'), //United Kingdom //Extended country list //http://www.nordea.com/Our+services/International+products+and+services/Cash+Management/IBAN+countries/908462.html + // http://www.swift.com/dsp/resources/documents/IBAN_Registry.pdf array('AO06000600000100037131174'), //Angola array('AZ21NABZ00000000137010001944'), //Azerbaijan array('BH29BMAG1299123456BH00'), //Bahrain array('BJ11B00610100400271101192591'), //Benin + array('BR9700360305000010009795493P1'), // Brazil + array('BR1800000000141455123924100C2'), // Brazil array('VG96VPVG0000012345678901'), //British Virgin Islands array('BF1030134020015400945000643'), //Burkina Faso array('BI43201011067444'), //Burundi @@ -135,6 +138,7 @@ class IbanValidatorTest extends AbstractConstraintValidatorTest array('IR580540105180021273113007'), //Iran array('IL620108000000099999999'), //Israel array('CI05A00060174100178530011852'), //Ivory Coast + array('JO94CBJO0010000000000131000302'), // Jordan array('KZ176010251000042993'), //Kazakhstan array('KW74NBOK0000000000001000372151'), //Kuwait array('LB30099900000001001925579115'), //Lebanon @@ -144,9 +148,12 @@ class IbanValidatorTest extends AbstractConstraintValidatorTest array('MU17BOMM0101101030300200000MUR'), //Mauritius array('MZ59000100000011834194157'), //Mozambique array('PS92PALS000000000400123456702'), //Palestinian Territory + array('QA58DOHB00001234567890ABCDEFG'), //Qatar + array('XK051212012345678906'), //Republic of Kosovo array('PT50000200000163099310355'), //Sao Tome and Principe array('SA0380000000608010167519'), //Saudi Arabia array('SN12K00100152000025690007542'), //Senegal + array('TL380080012345678910157'), //Timor-Leste array('TN5914207207100707129648'), //Tunisia array('TR330006100519786457841326'), //Turkey array('AE260211000000230064016'), //United Arab Emirates @@ -154,9 +161,268 @@ class IbanValidatorTest extends AbstractConstraintValidatorTest } /** - * @dataProvider getInvalidIbans + * @dataProvider getIbansWithInvalidFormat */ - public function testInvalidIbans($iban, $code) + public function testIbansWithInvalidFormat($iban) + { + $this->assertViolationRaised($iban, Iban::INVALID_FORMAT_ERROR); + } + + public function getIbansWithInvalidFormat() + { + return array( + array('AL47 2121 1009 0000 0002 3569 874'), //Albania + array('AD12 0001 2030 2003 5910 010'), //Andorra + array('AT61 1904 3002 3457 320'), //Austria + array('AZ21 NABZ 0000 0000 1370 1000 194'), //Azerbaijan + array('AZ21 N1BZ 0000 0000 1370 1000 1944'), //Azerbaijan + array('BH67 BMAG 0000 1299 1234 5'), //Bahrain + array('BH67 B2AG 0000 1299 1234 56'), //Bahrain + array('BE62 5100 0754 7061 2'), //Belgium + array('BA39 1290 0794 0102 8494 4'), //Bosnia and Herzegovina + array('BG80 BNBG 9661 1020 3456 7'), //Bulgaria + array('BG80 B2BG 9661 1020 3456 78'), //Bulgaria + array('HR12 1001 0051 8630 0016 01'), //Croatia + array('CY17 0020 0128 0000 0012 0052 7600 1'), //Cyprus + array('CZ65 0800 0000 1920 0014 5399 1'), //Czech Republic + array('DK50 0040 0440 1162 431'), //Denmark + array('EE38 2200 2210 2014 5685 1'), //Estonia + array('FO97 5432 0388 8999 441'), //Faroe Islands + array('FI21 1234 5600 0007 851'), //Finland + array('FR14 2004 1010 0505 0001 3M02 6061'), //France + array('GE29 NB00 0000 0101 9049 171'), //Georgia + array('DE89 3704 0044 0532 0130 001'), //Germany + array('GI75 NWBK 0000 0000 7099 4531'), //Gibraltar + array('GR16 0110 1250 0000 0001 2300 6951'), //Greece + array('GL56 0444 9876 5432 101'), //Greenland + array('HU42 1177 3016 1111 1018 0000 0000 1'), //Hungary + array('IS14 0159 2600 7654 5510 7303 391'), //Iceland + array('IE29 AIBK 9311 5212 3456 781'), //Ireland + array('IL62 0108 0000 0009 9999 9991'), //Israel + array('IT40 S054 2811 1010 0000 0123 4561'), //Italy + array('LV80 BANK 0000 4351 9500 11'), //Latvia + array('LB62 0999 0000 0001 0019 0122 9114 1'), //Lebanon + array('LI21 0881 0000 2324 013A A1'), //Liechtenstein + array('LT12 1000 0111 0100 1000 1'), //Lithuania + array('LU28 0019 4006 4475 0000 1'), //Luxembourg + array('MK072 5012 0000 0589 84 1'), //Macedonia + array('MT84 MALT 0110 0001 2345 MTLC AST0 01SA'), //Malta + array('MU17 BOMM 0101 1010 3030 0200 000M URA'), //Mauritius + array('MD24 AG00 0225 1000 1310 4168 1'), //Moldova + array('MC93 2005 2222 1001 1223 3M44 5551'), //Monaco + array('ME25 5050 0001 2345 6789 511'), //Montenegro + array('NL39 RABO 0300 0652 641'), //Netherlands + array('NO93 8601 1117 9471'), //Norway + array('PK36 SCBL 0000 0011 2345 6702 1'), //Pakistan + array('PL60 1020 1026 0000 0422 7020 1111 1'), //Poland + array('PT50 0002 0123 1234 5678 9015 41'), //Portugal + array('RO49 AAAA 1B31 0075 9384 0000 1'), //Romania + array('SM86 U032 2509 8000 0000 0270 1001'), //San Marino + array('SA03 8000 0000 6080 1016 7519 1'), //Saudi Arabia + array('RS35 2600 0560 1001 6113 791'), //Serbia + array('SK31 1200 0000 1987 4263 7541 1'), //Slovak Republic + array('SI56 1910 0000 0123 4381'), //Slovenia + array('ES80 2310 0001 1800 0001 2345 1'), //Spain + array('SE35 5000 0000 0549 1000 0003 1'), //Sweden + array('CH93 0076 2011 6238 5295 71'), //Switzerland + array('TN59 1000 6035 1835 9847 8831 1'), //Tunisia + array('TR33 0006 1005 1978 6457 8413 261'), //Turkey + array('AE07 0331 2345 6789 0123 4561'), //UAE + array('GB12 CPBK 0892 9965 0449 911'), //United Kingdom + + //Extended country list + array('AO060006000001000371311741'), //Angola + array('AZ21NABZ000000001370100019441'), //Azerbaijan + array('BH29BMAG1299123456BH001'), //Bahrain + array('BJ11B006101004002711011925911'), //Benin + array('BR9700360305000010009795493P11'), // Brazil + array('BR1800000000141455123924100C21'), // Brazil + array('VG96VPVG00000123456789011'), //British Virgin Islands + array('BF10301340200154009450006431'), //Burkina Faso + array('BI432010110674441'), //Burundi + array('CM21100030010005000006053061'), //Cameroon + array('CV640003000045470691101761'), //Cape Verde + array('FR76300070001100099700049421'), //Central African Republic + array('CG52300110002021512345678901'), //Congo + array('CR05152020010262840661'), //Costa Rica + array('DO28BAGR000000012124536113241'), //Dominican Republic + array('GT82TRAJ010200000012100296901'), //Guatemala + array('IR5805401051800212731130071'), //Iran + array('IL6201080000000999999991'), //Israel + array('CI05A000601741001785300118521'), //Ivory Coast + array('JO94CBJO00100000000001310003021'), // Jordan + array('KZ1760102510000429931'), //Kazakhstan + array('KW74NBOK00000000000010003721511'), //Kuwait + array('LB300999000000010019255791151'), //Lebanon + array('MG46000050300101019140160561'), //Madagascar + array('ML03D008901700010021200004471'), //Mali + array('MR13000120000100000020373721'), //Mauritania + array('MU17BOMM0101101030300200000MUR1'), //Mauritius + array('MZ590001000000118341941571'), //Mozambique + array('PS92PALS0000000004001234567021'), //Palestinian Territory + array('QA58DOHB00001234567890ABCDEFG1'), //Qatar + array('XK0512120123456789061'), //Republic of Kosovo + array('PT500002000001630993103551'), //Sao Tome and Principe + array('SA03800000006080101675191'), //Saudi Arabia + array('SN12K001001520000256900075421'), //Senegal + array('TL3800800123456789101571'), //Timor-Leste + array('TN59142072071007071296481'), //Tunisia + array('TR3300061005197864578413261'), //Turkey + array('AE2602110000002300640161'), //United Arab Emirates + ); + } + + /** + * @dataProvider getIbansWithValidFormatButIncorrectChecksum + */ + public function testIbansWithValidFormatButIncorrectChecksum($iban) + { + $this->assertViolationRaised($iban, Iban::CHECKSUM_FAILED_ERROR); + } + + public function getIbansWithValidFormatButIncorrectChecksum() + { + return array( + array('AL47 2121 1009 0000 0002 3569 8742'), //Albania + array('AD12 0001 2030 2003 5910 0101'), //Andorra + array('AT61 1904 3002 3457 3202'), //Austria + array('AZ21 NABZ 0000 0000 1370 1000 1945'), //Azerbaijan + array('BH67 BMAG 0000 1299 1234 57'), //Bahrain + array('BE62 5100 0754 7062'), //Belgium + array('BA39 1290 0794 0102 8495'), //Bosnia and Herzegovina + array('BG80 BNBG 9661 1020 3456 79'), //Bulgaria + array('HR12 1001 0051 8630 0016 1'), //Croatia + array('CY17 0020 0128 0000 0012 0052 7601'), //Cyprus + array('CZ65 0800 0000 1920 0014 5398'), //Czech Republic + array('DK50 0040 0440 1162 44'), //Denmark + array('EE38 2200 2210 2014 5684'), //Estonia + array('FO97 5432 0388 8999 43'), //Faroe Islands + array('FI21 1234 5600 0007 84'), //Finland + array('FR14 2004 1010 0505 0001 3M02 605'), //France + array('GE29 NB00 0000 0101 9049 16'), //Georgia + array('DE89 3704 0044 0532 0130 01'), //Germany + array('GI75 NWBK 0000 0000 7099 452'), //Gibraltar + array('GR16 0110 1250 0000 0001 2300 694'), //Greece + array('GL56 0444 9876 5432 11'), //Greenland + array('HU42 1177 3016 1111 1018 0000 0001'), //Hungary + array('IS14 0159 2600 7654 5510 7303 38'), //Iceland + array('IE29 AIBK 9311 5212 3456 79'), //Ireland + array('IL62 0108 0000 0009 9999 998'), //Israel + array('IT40 S054 2811 1010 0000 0123 457'), //Italy + array('LV80 BANK 0000 4351 9500 2'), //Latvia + array('LB62 0999 0000 0001 0019 0122 9115'), //Lebanon + array('LI21 0881 0000 2324 013A B'), //Liechtenstein + array('LT12 1000 0111 0100 1001'), //Lithuania + array('LU28 0019 4006 4475 0001'), //Luxembourg + array('MK072 5012 0000 0589 85'), //Macedonia + array('MT84 MALT 0110 0001 2345 MTLC AST0 01T'), //Malta + array('MU17 BOMM 0101 1010 3030 0200 000M UP'), //Mauritius + array('MD24 AG00 0225 1000 1310 4169'), //Moldova + array('MC93 2005 2222 1001 1223 3M44 554'), //Monaco + array('ME25 5050 0001 2345 6789 52'), //Montenegro + array('NL39 RABO 0300 0652 65'), //Netherlands + array('NO93 8601 1117 948'), //Norway + array('PK36 SCBL 0000 0011 2345 6703'), //Pakistan + array('PL60 1020 1026 0000 0422 7020 1112'), //Poland + array('PT50 0002 0123 1234 5678 9015 5'), //Portugal + array('RO49 AAAA 1B31 0075 9384 0001'), //Romania + array('SM86 U032 2509 8000 0000 0270 101'), //San Marino + array('SA03 8000 0000 6080 1016 7518'), //Saudi Arabia + array('RS35 2600 0560 1001 6113 78'), //Serbia + array('SK31 1200 0000 1987 4263 7542'), //Slovak Republic + array('SI56 1910 0000 0123 439'), //Slovenia + array('ES80 2310 0001 1800 0001 2346'), //Spain + array('SE35 5000 0000 0549 1000 0004'), //Sweden + array('CH93 0076 2011 6238 5295 8'), //Switzerland + array('TN59 1000 6035 1835 9847 8832'), //Tunisia + array('TR33 0006 1005 1978 6457 8413 27'), //Turkey + array('AE07 0331 2345 6789 0123 457'), //UAE + array('GB12 CPBK 0892 9965 0449 92'), //United Kingdom + + //Extended country list + array('AO06000600000100037131175'), //Angola + array('AZ21NABZ00000000137010001945'), //Azerbaijan + array('BH29BMAG1299123456BH01'), //Bahrain + array('BJ11B00610100400271101192592'), //Benin + array('BR9700360305000010009795493P2'), // Brazil + array('BR1800000000141455123924100C3'), // Brazil + array('VG96VPVG0000012345678902'), //British Virgin Islands + array('BF1030134020015400945000644'), //Burkina Faso + array('BI43201011067445'), //Burundi + array('CM2110003001000500000605307'), //Cameroon + array('CV64000300004547069110177'), //Cape Verde + array('FR7630007000110009970004943'), //Central African Republic + array('CG5230011000202151234567891'), //Congo + array('CR0515202001026284067'), //Costa Rica + array('DO28BAGR00000001212453611325'), //Dominican Republic + array('GT82TRAJ01020000001210029691'), //Guatemala + array('IR580540105180021273113008'), //Iran + array('IL620108000000099999998'), //Israel + array('CI05A00060174100178530011853'), //Ivory Coast + array('JO94CBJO0010000000000131000303'), // Jordan + array('KZ176010251000042994'), //Kazakhstan + array('KW74NBOK0000000000001000372152'), //Kuwait + array('LB30099900000001001925579116'), //Lebanon + array('MG4600005030010101914016057'), //Madagascar + array('ML03D00890170001002120000448'), //Mali + array('MR1300012000010000002037373'), //Mauritania + array('MU17BOMM0101101030300200000MUP'), //Mauritius + array('MZ59000100000011834194158'), //Mozambique + array('PS92PALS000000000400123456703'), //Palestinian Territory + array('QA58DOHB00001234567890ABCDEFH'), //Qatar + array('XK051212012345678907'), //Republic of Kosovo + array('PT50000200000163099310356'), //Sao Tome and Principe + array('SA0380000000608010167518'), //Saudi Arabia + array('SN12K00100152000025690007543'), //Senegal + array('TL380080012345678910158'), //Timor-Leste + array('TN5914207207100707129649'), //Tunisia + array('TR330006100519786457841327'), //Turkey + array('AE260211000000230064017'), //United Arab Emirates + ); + } + + /** + * @dataProvider getUnsupportedCountryCodes + */ + public function testIbansWithUnsupportedCountryCode($countryCode) + { + $this->assertViolationRaised($countryCode.'260211000000230064016', Iban::NOT_SUPPORTED_COUNTRY_CODE_ERROR); + } + + public function getUnsupportedCountryCodes() + { + return array( + array('AG'), + array('AI'), + array('AQ'), + array('AS'), + array('AW'), + ); + } + + public function testIbansWithInvalidCharacters() + { + $this->assertViolationRaised('CH930076201162385295]', Iban::INVALID_CHARACTERS_ERROR); + } + + /** + * @dataProvider getIbansWithInvalidCountryCode + */ + public function testIbansWithInvalidCountryCode($iban) + { + $this->assertViolationRaised($iban, Iban::INVALID_COUNTRY_CODE_ERROR); + } + + public function getIbansWithInvalidCountryCode() + { + return array( + array('0750447346'), + array('2X0750447346'), + array('A20750447346'), + ); + } + + private function assertViolationRaised($iban, $code) { $constraint = new Iban(array( 'message' => 'myMessage', @@ -169,25 +435,4 @@ class IbanValidatorTest extends AbstractConstraintValidatorTest ->setCode($code) ->assertRaised(); } - - public function getInvalidIbans() - { - return array( - array('CH93 0076 2011 6238 5295', Iban::CHECKSUM_FAILED_ERROR), - array('CH930076201162385295', Iban::CHECKSUM_FAILED_ERROR), - array('GB29 RBOS 6016 1331 9268 19', Iban::CHECKSUM_FAILED_ERROR), - array('CH930072011623852957', Iban::CHECKSUM_FAILED_ERROR), - array('NL39 RASO 0300 0652 64', Iban::CHECKSUM_FAILED_ERROR), - array('NO93 8601117 947', Iban::CHECKSUM_FAILED_ERROR), - array('CY170020 128 0000 0012 0052 7600', Iban::CHECKSUM_FAILED_ERROR), - array('foo', Iban::TOO_SHORT_ERROR), - array('123', Iban::TOO_SHORT_ERROR), - array('0750447346', Iban::INVALID_COUNTRY_CODE_ERROR), - array('CH930076201162385295]', Iban::INVALID_CHARACTERS_ERROR), - - //Ibans with lower case values are invalid - array('Ae260211000000230064016', Iban::INVALID_CASE_ERROR), - array('ae260211000000230064016', Iban::INVALID_CASE_ERROR), - ); - } } From 8b63d6209d7426b79c79fb3a9e88436db634ae92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Perrin?= Date: Tue, 28 Jul 2015 11:41:56 +0200 Subject: [PATCH 04/20] [Console] Use readline for user input when available This allows to use arrow keys in the terminal instead of having weird characters --- src/Symfony/Component/Console/CHANGELOG.md | 6 ++++ .../Console/Helper/QuestionHelper.php | 30 +++++++++++++++---- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Console/CHANGELOG.md b/src/Symfony/Component/Console/CHANGELOG.md index 07254c6720..4e29d2ed45 100644 --- a/src/Symfony/Component/Console/CHANGELOG.md +++ b/src/Symfony/Component/Console/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +2.8.0 +----- + + * use readline for user input in the question helper when available to allow + the use of arrow keys + 2.6.0 ----- diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index 9e4fa02f2c..17605fe64c 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -127,11 +127,7 @@ class QuestionHelper extends Helper } if (false === $ret) { - $ret = fgets($inputStream, 4096); - if (false === $ret) { - throw new \RuntimeException('Aborted'); - } - $ret = trim($ret); + $ret = $this->readFromInput($inputStream); } } else { $ret = trim($this->autocomplete($output, $question, $inputStream)); @@ -423,6 +419,30 @@ class QuestionHelper extends Helper return self::$shell; } + /** + * Reads user input. + * + * @param resource $stream The input stream + * + * @return string User input + * + * @throws \RuntimeException + */ + private function readFromInput($stream) + { + if (STDIN === $stream && function_exists('readline')) { + $ret = readline(); + } else { + $ret = fgets($stream, 4096); + + if (false === $ret) { + throw new \RuntimeException('Aborted'); + } + } + + return trim($ret); + } + /** * Returns whether Stty is available or not. * From 05348991c263e714aac848b119e99d2f8ff77507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Perrin?= Date: Tue, 28 Jul 2015 14:45:38 +0200 Subject: [PATCH 05/20] [Console] Fix Symfony coding standards violations --- src/Symfony/Component/Console/Helper/QuestionHelper.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index 17605fe64c..dc68fe6e71 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -77,7 +77,7 @@ class QuestionHelper extends Helper } /** - * Returns the helper's input stream + * Returns the helper's input stream. * * @return resource */ @@ -146,7 +146,7 @@ class QuestionHelper extends Helper * Outputs the question prompt. * * @param OutputInterface $output - * @param Question $question + * @param Question $question */ protected function writePrompt(OutputInterface $output, Question $question) { @@ -218,7 +218,7 @@ class QuestionHelper extends Helper // Backspace Character if ("\177" === $c) { if (0 === $numMatches && 0 !== $i) { - $i--; + --$i; // Move cursor backwards $output->write("\033[1D"); } @@ -271,7 +271,7 @@ class QuestionHelper extends Helper } else { $output->write($c); $ret .= $c; - $i++; + ++$i; $numMatches = 0; $ofs = 0; From e0a1294a44ca4ba902af6af4b4ddfc0dabf8a30d Mon Sep 17 00:00:00 2001 From: Thomas Lallement Date: Fri, 20 Mar 2015 10:13:42 +0100 Subject: [PATCH 06/20] [Form] Add flexibility for EntityType --- .../Form/ChoiceList/ORMQueryBuilderLoader.php | 12 ++++++++++-- .../Doctrine/Tests/Form/Type/EntityTypeTest.php | 13 +++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php index 6e16240d29..8796d5e072 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php @@ -30,7 +30,7 @@ class ORMQueryBuilderLoader implements EntityLoaderInterface * * This property should only be accessed through queryBuilder. * - * @var QueryBuilder + * @var QueryBuilder|null */ private $queryBuilder; @@ -68,7 +68,7 @@ class ORMQueryBuilderLoader implements EntityLoaderInterface $queryBuilder = $queryBuilder($manager->getRepository($class)); - if (!$queryBuilder instanceof QueryBuilder) { + if (null !== $queryBuilder && !$queryBuilder instanceof QueryBuilder) { throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder'); } } @@ -81,6 +81,10 @@ class ORMQueryBuilderLoader implements EntityLoaderInterface */ public function getEntities() { + if (null === $this->queryBuilder) { + return array(); + } + return $this->queryBuilder->getQuery()->execute(); } @@ -89,6 +93,10 @@ class ORMQueryBuilderLoader implements EntityLoaderInterface */ public function getEntitiesByIds($identifier, array $values) { + if (null === $this->queryBuilder) { + return array(); + } + $qb = clone ($this->queryBuilder); $alias = current($qb->getRootAliases()); $parameter = 'ORMQueryBuilderLoader_getEntitiesByIds_'.$identifier; diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php index 3223996b90..307b68fd32 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php @@ -212,6 +212,19 @@ class EntityTypeTest extends TypeTestCase $field->submit('2'); } + + public function testConfigureQueryBuilderWithClosureReturningNull() + { + $field = $this->factory->createNamed('name', 'entity', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'query_builder' => function () { + return null; + }, + )); + + $this->assertEquals(array(), $field->createView()->vars['choices']); + } public function testSetDataSingleNull() { From d60b2bba601f72a7f43f0df65a3bd4f63a7749a8 Mon Sep 17 00:00:00 2001 From: Johnny Peck Date: Wed, 24 Jun 2015 01:08:56 -0400 Subject: [PATCH 07/20] Update EngineInterface.php Grammer in doc comment. --- src/Symfony/Component/Templating/EngineInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Templating/EngineInterface.php b/src/Symfony/Component/Templating/EngineInterface.php index a28da649f3..d694d71e40 100644 --- a/src/Symfony/Component/Templating/EngineInterface.php +++ b/src/Symfony/Component/Templating/EngineInterface.php @@ -14,7 +14,7 @@ namespace Symfony\Component\Templating; /** * EngineInterface is the interface each engine must implement. * - * All methods relies on a template name. A template name is a + * All methods rely on a template name. A template name is a * "logical" name for the template, and as such it does not refer to * a path on the filesystem (in fact, the template can be stored * anywhere, like in a database). From c3146592d9dbf6750db067ce1785908da463118d Mon Sep 17 00:00:00 2001 From: nuncanada Date: Sat, 20 Jun 2015 21:23:51 -0300 Subject: [PATCH 08/20] Fixing DbalSessionHandler to work with a Oracle "limitation" or bug? --- .../Doctrine/HttpFoundation/DbalSessionHandler.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php b/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php index 88260a5270..5b749fc5bc 100644 --- a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php +++ b/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php @@ -159,6 +159,14 @@ class DbalSessionHandler implements \SessionHandlerInterface $mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); $mergeStmt->bindParam(':data', $encoded, \PDO::PARAM_STR); $mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT); + + //Oracle has a bug that will intermitently happen if you + //have only 1 bind on a CLOB field for 2 different statements + //(INSERT and UPDATE in this case) + if ('oracle' == $this->con->getDatabasePlatform()->getName()) { + $mergeStmt->bindParam(':data2', $encoded, \PDO::PARAM_STR); + } + $mergeStmt->execute(); return true; @@ -224,7 +232,7 @@ class DbalSessionHandler implements \SessionHandlerInterface // DUAL is Oracle specific dummy table return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ". "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". - "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time"; + "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data2, $this->timeCol = :time"; case $this->con->getDatabasePlatform() instanceof SQLServer2008Platform: // MERGE is only available since SQL Server 2008 and must be terminated by semicolon // It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx From e23eb56ffe8cd613fe1afb84fc3b8b443f221cba Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 1 Aug 2015 18:50:08 +0200 Subject: [PATCH 09/20] [Locale] Fix Intl requirement --- src/Symfony/Component/Locale/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Locale/composer.json b/src/Symfony/Component/Locale/composer.json index 6aafb8cd0d..3def58490c 100644 --- a/src/Symfony/Component/Locale/composer.json +++ b/src/Symfony/Component/Locale/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": ">=5.3.9", - "symfony/intl": "~2.3" + "symfony/intl": "~2.7" }, "require-dev": { "symfony/phpunit-bridge": "~2.7" From 9b7d4c7613b5b7e351e32c6f60c08377388150d4 Mon Sep 17 00:00:00 2001 From: Walter Dal Mut Date: Sat, 14 Feb 2015 14:38:59 +0100 Subject: [PATCH 10/20] Annotated routes with a variadic parameter There are no variadic default values and that generate a fatal error. --- .../AnnotatedClasses/VariadicClass.php | 19 +++++++++++++++++++ .../Tests/Loader/AnnotationFileLoaderTest.php | 14 ++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/VariadicClass.php diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/VariadicClass.php b/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/VariadicClass.php new file mode 100644 index 0000000000..f0fe7f095e --- /dev/null +++ b/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/VariadicClass.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses; + +class VariadicClass +{ + public function routeAction(...$params) + { + } +} diff --git a/src/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php index f0a8a0e329..94b6d02981 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php @@ -13,6 +13,7 @@ namespace Symfony\Component\Routing\Tests\Loader; use Symfony\Component\Routing\Loader\AnnotationFileLoader; use Symfony\Component\Config\FileLocator; +use Symfony\Component\Routing\Annotation\Route; class AnnotationFileLoaderTest extends AbstractAnnotationLoaderTest { @@ -34,6 +35,19 @@ class AnnotationFileLoaderTest extends AbstractAnnotationLoaderTest $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooClass.php'); } + /** + * @requires PHP 5.6 + */ + public function testLoadVariadic() + { + $route = new Route(["path" => "/path/to/{id}"]); + $this->reader->expects($this->once())->method('getClassAnnotation'); + $this->reader->expects($this->once())->method('getMethodAnnotations') + ->will($this->returnValue([$route])); + + $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/VariadicClass.php'); + } + public function testSupports() { $fixture = __DIR__.'/../Fixtures/annotated.php'; From 73c5eff44d77d8970a4f472c81cfd197c9878dac Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Sat, 1 Aug 2015 21:33:42 +0200 Subject: [PATCH 11/20] Fix the retrieval of the default value for variadic arguments --- .../Component/Routing/Loader/AnnotationClassLoader.php | 2 +- .../VariadicClass.php | 2 +- .../Routing/Tests/Loader/AnnotationClassLoaderTest.php | 1 + .../Routing/Tests/Loader/AnnotationFileLoaderTest.php | 6 +++--- 4 files changed, 6 insertions(+), 5 deletions(-) rename src/Symfony/Component/Routing/Tests/Fixtures/{AnnotatedClasses => OtherAnnotatedClasses}/VariadicClass.php (81%) diff --git a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php index e35f807b4c..11d8f34d5a 100644 --- a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php +++ b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php @@ -180,7 +180,7 @@ abstract class AnnotationClassLoader implements LoaderInterface $defaults = array_replace($globals['defaults'], $annot->getDefaults()); foreach ($method->getParameters() as $param) { - if (!isset($defaults[$param->getName()]) && $param->isOptional()) { + if (!isset($defaults[$param->getName()]) && $param->isDefaultValueAvailable()) { $defaults[$param->getName()] = $param->getDefaultValue(); } } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/VariadicClass.php b/src/Symfony/Component/Routing/Tests/Fixtures/OtherAnnotatedClasses/VariadicClass.php similarity index 81% rename from src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/VariadicClass.php rename to src/Symfony/Component/Routing/Tests/Fixtures/OtherAnnotatedClasses/VariadicClass.php index f0fe7f095e..729c9b4d07 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/VariadicClass.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/OtherAnnotatedClasses/VariadicClass.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses; +namespace Symfony\Component\Routing\Tests\Fixtures\OtherAnnotatedClasses; class VariadicClass { diff --git a/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php index 07efaf7778..67d07c3f5d 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php @@ -16,6 +16,7 @@ use Symfony\Component\Routing\Annotation\Route; class AnnotationClassLoaderTest extends AbstractAnnotationLoaderTest { protected $loader; + private $reader; protected function setUp() { diff --git a/src/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php index 94b6d02981..9a83994f9a 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php @@ -40,12 +40,12 @@ class AnnotationFileLoaderTest extends AbstractAnnotationLoaderTest */ public function testLoadVariadic() { - $route = new Route(["path" => "/path/to/{id}"]); + $route = new Route(array('path' => '/path/to/{id}')); $this->reader->expects($this->once())->method('getClassAnnotation'); $this->reader->expects($this->once())->method('getMethodAnnotations') - ->will($this->returnValue([$route])); + ->will($this->returnValue(array($route))); - $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/VariadicClass.php'); + $this->loader->load(__DIR__.'/../Fixtures/OtherAnnotatedClasses/VariadicClass.php'); } public function testSupports() From 4fefd3d1e776ca00d10edc4df18b196518f27150 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Sun, 2 Aug 2015 01:52:06 +0200 Subject: [PATCH 12/20] Remove the duplicated rendering of deprecation messages in the profiler --- .../Resources/views/Collector/logger.html.twig | 2 -- 1 file changed, 2 deletions(-) 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 5053657e2b..33ec18e58c 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig @@ -120,8 +120,6 @@ {% set stack = log.context.stack|default([]) %} {% set id = 'sf-call-stack-' ~ log_index %} - {{ log.message }} - {% if stack %} From eb08baa9fcb6db3863c795ea54e324ced68d56ea Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 2 Aug 2015 10:01:11 +0200 Subject: [PATCH 13/20] Fix "[Form] Add flexibility for EntityType" This fixes commit e0a1294a44ca4ba902af6af4b4ddfc0dabf8a30d. --- .../Form/ChoiceList/ORMQueryBuilderLoader.php | 12 ++---------- src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php | 2 +- .../Doctrine/Tests/Form/Type/EntityTypeTest.php | 2 +- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php index 8796d5e072..6e16240d29 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php @@ -30,7 +30,7 @@ class ORMQueryBuilderLoader implements EntityLoaderInterface * * This property should only be accessed through queryBuilder. * - * @var QueryBuilder|null + * @var QueryBuilder */ private $queryBuilder; @@ -68,7 +68,7 @@ class ORMQueryBuilderLoader implements EntityLoaderInterface $queryBuilder = $queryBuilder($manager->getRepository($class)); - if (null !== $queryBuilder && !$queryBuilder instanceof QueryBuilder) { + if (!$queryBuilder instanceof QueryBuilder) { throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder'); } } @@ -81,10 +81,6 @@ class ORMQueryBuilderLoader implements EntityLoaderInterface */ public function getEntities() { - if (null === $this->queryBuilder) { - return array(); - } - return $this->queryBuilder->getQuery()->execute(); } @@ -93,10 +89,6 @@ class ORMQueryBuilderLoader implements EntityLoaderInterface */ public function getEntitiesByIds($identifier, array $values) { - if (null === $this->queryBuilder) { - return array(); - } - $qb = clone ($this->queryBuilder); $alias = current($qb->getRootAliases()); $parameter = 'ORMQueryBuilderLoader_getEntitiesByIds_'.$identifier; diff --git a/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php b/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php index 3a80ab0327..7e0c6cb6cd 100644 --- a/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php +++ b/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php @@ -30,7 +30,7 @@ class EntityType extends DoctrineType if (is_callable($queryBuilder)) { $queryBuilder = call_user_func($queryBuilder, $options['em']->getRepository($options['class'])); - if (!$queryBuilder instanceof QueryBuilder) { + if (null !== $queryBuilder && !$queryBuilder instanceof QueryBuilder) { throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder'); } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php index 307b68fd32..999883eb8d 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php @@ -215,7 +215,7 @@ class EntityTypeTest extends TypeTestCase public function testConfigureQueryBuilderWithClosureReturningNull() { - $field = $this->factory->createNamed('name', 'entity', null, array( + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => function () { From 68f0818a6cab387217caa699d14240bd8f41d709 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sun, 2 Aug 2015 13:28:24 +0200 Subject: [PATCH 14/20] Allow to define Enum nodes with 1 single element --- UPGRADE-2.8.md | 20 +++++++++++++++++++ .../Component/Config/Definition/EnumNode.php | 4 ++-- .../Config/Tests/Definition/EnumNodeTest.php | 15 +++++++++++++- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/UPGRADE-2.8.md b/UPGRADE-2.8.md index 2c18e58f50..655acee0de 100644 --- a/UPGRADE-2.8.md +++ b/UPGRADE-2.8.md @@ -399,3 +399,23 @@ FrameworkBundle session: cookie_httponly: false ``` + +Config +------ + +The edge case of defining just one value for nodes of type Enum is now allowed: + +```php +$rootNode + ->children() + ->enumNode('variable') + ->values(array('value')) + ->end() + ->end() +; +``` + +Before: `InvalidArgumentException` (variable must contain at least two +distinct elements). +After: the code will work as expected and it will restrict the values of the +`variable` option to just `value`. diff --git a/src/Symfony/Component/Config/Definition/EnumNode.php b/src/Symfony/Component/Config/Definition/EnumNode.php index 224871ab6f..9b4c4156e3 100644 --- a/src/Symfony/Component/Config/Definition/EnumNode.php +++ b/src/Symfony/Component/Config/Definition/EnumNode.php @@ -25,8 +25,8 @@ class EnumNode extends ScalarNode public function __construct($name, NodeInterface $parent = null, array $values = array()) { $values = array_unique($values); - if (count($values) <= 1) { - throw new \InvalidArgumentException('$values must contain at least two distinct elements.'); + if (empty($values)) { + throw new \InvalidArgumentException('$values must contain at least one element.'); } parent::__construct($name, $parent); diff --git a/src/Symfony/Component/Config/Tests/Definition/EnumNodeTest.php b/src/Symfony/Component/Config/Tests/Definition/EnumNodeTest.php index 2b84de6b09..654d5050dd 100644 --- a/src/Symfony/Component/Config/Tests/Definition/EnumNodeTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/EnumNodeTest.php @@ -23,10 +23,23 @@ class EnumNodeTest extends \PHPUnit_Framework_TestCase /** * @expectedException \InvalidArgumentException + * @expectedExceptionMessage $values must contain at least one element. */ + public function testConstructionWithNoValues() + { + new EnumNode('foo', null, array()); + } + public function testConstructionWithOneValue() { - new EnumNode('foo', null, array('foo', 'foo')); + $node = new EnumNode('foo', null, array('foo')); + $this->assertSame('foo', $node->finalize('foo')); + } + + public function testConstructionWithOneDistinctValue() + { + $node = new EnumNode('foo', null, array('foo', 'foo')); + $this->assertSame('foo', $node->finalize('foo')); } /** From 6c22f0af24deb033d98fc59455f4f31317987c2e Mon Sep 17 00:00:00 2001 From: Valentin VALCIU Date: Sun, 12 Jul 2015 00:06:45 +0300 Subject: [PATCH 15/20] [HttpFoundation] fixed the check of 'proxy-revalidate' in Response::mustRevalidate() --- .../Component/HttpFoundation/Response.php | 2 +- .../HttpFoundation/Tests/ResponseTest.php | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index 9b399189b4..df36c5a931 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -587,7 +587,7 @@ class Response */ public function mustRevalidate() { - return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->has('proxy-revalidate'); + return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->hasCacheControlDirective('proxy-revalidate'); } /** diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php index 7dc3330912..730ba09b43 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php @@ -92,6 +92,22 @@ class ResponseTest extends ResponseTestCase $this->assertFalse($response->mustRevalidate()); } + public function testMustRevalidateWithMustRevalidateCacheControlHeader() + { + $response = new Response(); + $response->headers->set('cache-control', 'must-revalidate'); + + $this->assertTrue($response->mustRevalidate()); + } + + public function testMustRevalidateWithProxyRevalidateCacheControlHeader() + { + $response = new Response(); + $response->headers->set('cache-control', 'proxy-revalidate'); + + $this->assertTrue($response->mustRevalidate()); + } + public function testSetNotModified() { $response = new Response(); From 8d2b8881f53c144f09dabf3915efe6e74c58d204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Tue, 4 Aug 2015 12:17:13 +0200 Subject: [PATCH 16/20] do not remove space between attributes This piece of code adds a space then removes it immediately. One could think that only the space after the last element of the loop is removed, but this is not the case. Space between loop elements are also removed. --- .../Bridge/Twig/Resources/views/Form/form_div_layout.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 243c9b1879..5e5390b6d7 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 @@ -304,7 +304,7 @@ {%- block widget_container_attributes -%} {% if id is not empty %}id="{{ id }}" {% endif %} - {%- for attrname, attrvalue in attr %}{{ attrname }}="{{ attrvalue }}" {%- endfor -%} + {%- for attrname, attrvalue in attr %}{{ attrname }}="{{ attrvalue }}" {% endfor -%} {%- endblock widget_container_attributes -%} {%- block button_attributes -%} From f5ca270e7568e10586c4a1f1c67459e35df1c610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Perrin?= Date: Tue, 4 Aug 2015 17:59:05 +0200 Subject: [PATCH 17/20] [Console] Handle false return value from readline "readline()" returns "false" when the user presses CTRL+D. --- src/Symfony/Component/Console/Helper/QuestionHelper.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index dc68fe6e71..53a684a81b 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -434,10 +434,10 @@ class QuestionHelper extends Helper $ret = readline(); } else { $ret = fgets($stream, 4096); + } - if (false === $ret) { - throw new \RuntimeException('Aborted'); - } + if (false === $ret) { + throw new \RuntimeException('Aborted'); } return trim($ret); From ce2a3717fdc1474bbcba4c53f51f498d4caf0b85 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 5 Aug 2015 12:11:53 +0200 Subject: [PATCH 18/20] [travis] Build phpunit with local components --- .travis.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index b27111e496..9c138eb499 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,12 +42,16 @@ before_install: - if [ "$TRAVIS_BRANCH" = "master" ]; then export COMPOSER_ROOT_VERSION=dev-master; else export COMPOSER_ROOT_VERSION="$TRAVIS_BRANCH".x-dev; fi; install: - - if [ "$deps" = "no" ]; then composer --prefer-source install; fi; + - composer --prefer-source install + - composer require --no-update phpunit/phpunit '*' + - composer require --no-update phpunit/phpunit-mock-objects '2.3.0' # See https://github.com/sebastianbergmann/phpunit-mock-objects/issues/223 + - composer update --prefer-stable --prefer-source phpunit/phpunit - components=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n') - if [ "$deps" != "no" ]; then php .travis.php $TRAVIS_COMMIT_RANGE $TRAVIS_BRANCH $components; fi; + - PHPUNIT="$(readlink -f ./vendor/bin/phpunit) --colors=always" 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" = "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; + - 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" = "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; From 7fdba25e4fc92e4b3add0d515061d681691c9872 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 5 Aug 2015 17:43:06 +0200 Subject: [PATCH 19/20] Clean wrong whitespaces --- .../Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php b/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php index 5b749fc5bc..faa623823b 100644 --- a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php +++ b/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php @@ -161,10 +161,10 @@ class DbalSessionHandler implements \SessionHandlerInterface $mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT); //Oracle has a bug that will intermitently happen if you - //have only 1 bind on a CLOB field for 2 different statements + //have only 1 bind on a CLOB field for 2 different statements //(INSERT and UPDATE in this case) if ('oracle' == $this->con->getDatabasePlatform()->getName()) { - $mergeStmt->bindParam(':data2', $encoded, \PDO::PARAM_STR); + $mergeStmt->bindParam(':data2', $encoded, \PDO::PARAM_STR); } $mergeStmt->execute(); From 4dd0de6694a78010ea943d2729e7017dc13003bb Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 6 Aug 2015 09:52:28 +0200 Subject: [PATCH 20/20] [travis] Build standalone phpunit without symfony/yaml --- .travis.yml | 15 +++++++-------- src/Symfony/Bundle/TwigBundle/composer.json | 1 + 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5fe7a05e58..5a8e57121b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,20 +36,19 @@ before_install: - if [[ "$TRAVIS_PHP_VERSION" != "nightly" ]] && [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]] && [ $(php -r "echo PHP_MINOR_VERSION;") -le 4 ]; then echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi; - if [[ "$TRAVIS_PHP_VERSION" != "nightly" ]] && [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then (pecl install -f memcached-2.1.0 && echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini) || echo "Let's continue without memcache extension"; fi; - if [[ "$TRAVIS_PHP_VERSION" != "nightly" ]] && [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then php -i; fi; + # Build a standalone phpunit without symfony/yaml and that works around https://github.com/sebastianbergmann/phpunit-mock-objects/issues/223 + - (mkdir phpunit && cd phpunit && wget https://github.com/sebastianbergmann/phpunit/archive/4.7.zip && unzip 4.7.zip && cd phpunit-4.7 && composer remove --no-update symfony/yaml && composer require --prefer-source phpunit/phpunit-mock-objects '2.3.0') + - export PHPUNIT="$(readlink -f ./phpunit/phpunit-4.7/phpunit) --colors=always" # Set the COMPOSER_ROOT_VERSION to the right version according to the branch being built - if [ "$TRAVIS_BRANCH" = "master" ]; then export COMPOSER_ROOT_VERSION=dev-master; else export COMPOSER_ROOT_VERSION="$TRAVIS_BRANCH".x-dev; fi; install: - - composer --prefer-source install - - composer require --no-update phpunit/phpunit '*' - - composer require --no-update phpunit/phpunit-mock-objects '2.3.0' # See https://github.com/sebastianbergmann/phpunit-mock-objects/issues/223 - - composer update --prefer-stable --prefer-source phpunit/phpunit + - if [ "$deps" = "no" ]; then composer --prefer-source install; fi; - components=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n') - if [ "$deps" != "no" ]; then php .travis.php $TRAVIS_COMMIT_RANGE $TRAVIS_BRANCH $components; fi; - - PHPUNIT="$(readlink -f ./vendor/bin/phpunit) --colors=always" 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 "$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" = "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; + - 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" = "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/src/Symfony/Bundle/TwigBundle/composer.json b/src/Symfony/Bundle/TwigBundle/composer.json index 6426ffa12a..a9b9bfeaac 100644 --- a/src/Symfony/Bundle/TwigBundle/composer.json +++ b/src/Symfony/Bundle/TwigBundle/composer.json @@ -25,6 +25,7 @@ "symfony/stopwatch": "~2.2", "symfony/dependency-injection": "~2.2", "symfony/config": "~2.2", + "symfony/yaml": "~2.3", "symfony/framework-bundle": "~2.1" }, "autoload": {