diff --git a/CHANGELOG-2.2.md b/CHANGELOG-2.2.md
index 2c7a93f004..de5c5a51f4 100644
--- a/CHANGELOG-2.2.md
+++ b/CHANGELOG-2.2.md
@@ -7,6 +7,41 @@ in 2.2 minor versions.
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.2.0...v2.2.1
+* 2.2.6 (2013-08-26)
+
+ * f936b41: clearToken exception is thrown at wrong place.
+ * d0faf55: [Locale] Fixed: StubLocale::setDefault() throws no exception when "en" is passed
+ * 566d79c: [Yaml] fixed embedded folded string parsing
+ * 0951b8d: [Translation] Fixed regression: When only one rule is passed to transChoice(), this rule should be used
+ * 4563f1b: [Yaml] Fix comment containing a colon on a scalar line being parsed as a hash.
+ * 7e87eb1: fixed request format when forwarding a request
+ * ccaaedf: [Form] PropertyPathMapper::mapDataToForms() *always* calls setData() on every child to ensure that all *_DATA events were fired when the initialization phase is over (except for virtual forms)
+ * 00bc270: [Form] Fixed: submit() reacts to dynamic modifications of the form children
+ * 05fdb12: Fixed issue #6932 - Inconsistent locale handling in subrequests
+ * b3c3159: fixed locale of sub-requests when explicitely set by the developer (refs #8821)
+ * b72bc0b: [Locale] fixed build-data exit code in case of an error
+ * 9bb7a3d: fixed request format of sub-requests when explicitely set by the developer (closes #8787)
+ * fa35597: Sets _format attribute only if it wasn't set previously by the user.
+ * f946108: fixed the format of the request used to render an exception
+ * 51022c3: Fix typo in the check_path validator
+ * 5f7219e: added a missing use statement (closes #8808)
+ * 262879d: fix for Process:isSuccessful()
+ * 0723c10: [Process] Use a consistent way to reset data of the process latest run
+ * 85a9c9d: [HttpFoundation] Fixed removing a nonexisting namespaced attribute.
+ * 191d320: [Validation] Fixed IdentityTranslator to pass correct Locale to MessageSelector
+ * c6ecd83: SwiftMailerHandler in Monolog bridge now able to react to kernel.terminate event
+ * 99adcf1: {HttpFoundation] [Session] fixed session compatibility with memcached/redis session storage
+ * ab9a96b: Fixes for hasParameterOption and getParameterOption methods of ArgvInput
+ * dbd0855: Added sleep() workaround for windows php rename bug
+ * fa769a2: [Process] Add more precision to Process::stop timeout
+ * 3ef517b: [Process] Fix #8739
+ * 18896d5a: [Validator] fixed the wrong isAbstract() check against the class (fixed #8589)
+ * e8e76ec: [TwigBridge] Prevent code extension to display warning
+ * 1a73b44: added missing support for the new output API in PHP 5.4+
+ * e0c7d3d: Fixed bug introduced in #8675
+ * 0b965fb: made the filesystem loader compatible with Twig 2.0
+ * 322f880: replaced deprecated Twig features
+
* 2.2.5 (2013-08-07)
* c35cc5b: added trusted hosts check
diff --git a/CHANGELOG-2.3.md b/CHANGELOG-2.3.md
index db170b4f9a..6916ef76ee 100644
--- a/CHANGELOG-2.3.md
+++ b/CHANGELOG-2.3.md
@@ -7,6 +7,55 @@ in 2.3 minor versions.
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.3.0...v2.3.1
+* 2.3.4 (2013-08-27)
+
+ * f936b41: clearToken exception is thrown at wrong place.
+ * ea480bd: [Form] Fixed Form::all() signature for PHP 5.3.3
+ * e1f40f2: [Locale] Fixed: Locale::setDefault() throws no exception when "en" is passed
+ * d0faf55: [Locale] Fixed: StubLocale::setDefault() throws no exception when "en" is passed
+ * 566d79c: [Yaml] fixed embedded folded string parsing
+ * 33b0a17: [Validator] fixed Boolean handling in XML constraint mappings (closes #5603)
+ * 0951b8d: [Translation] Fixed regression: When only one rule is passed to transChoice(), this rule should be used
+ * 4563f1b: [Yaml] Fix comment containing a colon on a scalar line being parsed as a hash.
+ * 7e87eb1: fixed request format when forwarding a request
+ * 07d14e5: [Form] Removed exception in Button::setData(): setData() is now always called for all elements in the form tree during the initialization of the tree
+ * ccaaedf: [Form] PropertyPathMapper::mapDataToForms() *always* calls setData() on every child to ensure that all *_DATA events were fired when the initialization phase is over (except for virtual forms)
+ * 00bc270: [Form] Fixed: submit() reacts to dynamic modifications of the form children
+ * c4636e1: added a functional test for locale handling in sub-requests
+ * 05fdb12: Fixed issue #6932 - Inconsistent locale handling in subrequests
+ * b3c3159: fixed locale of sub-requests when explicitely set by the developer (refs #8821)
+ * 9bb7a3d: fixed request format of sub-requests when explicitely set by the developer (closes #8787)
+ * fa35597: Sets _format attribute only if it wasn't set previously by the user.
+ * f946108: fixed the format of the request used to render an exception
+ * 51022c3: Fix typo in the check_path validator
+ * 5f7219e: added a missing use statement (closes #8808)
+ * 262879d: fix for Process:isSuccessful()
+ * 0723c10: [Process] Use a consistent way to reset data of the process latest run
+ * 85a9c9d: [HttpFoundation] Fixed removing a nonexisting namespaced attribute.
+ * 191d320: [Validation] Fixed IdentityTranslator to pass correct Locale to MessageSelector
+ * c6ecd83: SwiftMailerHandler in Monolog bridge now able to react to kernel.terminate event
+ * 99adcf1: {HttpFoundation] [Session] fixed session compatibility with memcached/redis session storage
+ * ab9a96b: Fixes for hasParameterOption and getParameterOption methods of ArgvInput
+ * dbd0855: Added sleep() workaround for windows php rename bug
+ * c342715: [Form] Fixed: Added "validation_groups" option to submit button
+ * fa01e6b: [Process] Fix for #8754 (Timed-out processes are successful)
+ * 909fab6: [Process] Fix #8742 : Signal-terminated processes are not successful
+ * fa769a2: [Process] Add more precision to Process::stop timeout
+ * 3ef517b: [Process] Fix #8739
+ * 572ba68: [TwigBridge] removed superflous ; when rendering form_enctype() (closes #8660)
+ * 18896d5a: [Validator] fixed the wrong isAbstract() check against the class (fixed #8589)
+ * e8e76ec: [TwigBridge] Prevent code extension to display warning
+ * 96aec0f: Fix internal sub-request creation
+ * 6ed0fdf: [Form] Moved auto_initialize option to the BaseType
+ * e47657d: Make sure ContextErrorException is loaded during compile time errors
+ * 98f6969: Fix empty process argument escaping on Windows
+ * 1a73b44: added missing support for the new output API in PHP 5.4+
+ * e0c7d3d: Fixed bug introduced in #8675
+ * 0b965fb: made the filesystem loader compatible with Twig 2.0
+ * 8fa0453: [Intl] Updated stubs to reflect ICU 51.2
+ * 322f880: replaced deprecated Twig features
+ * 48338fc: Ignore null value in comparison validators
+
* 2.3.3 (2013-08-07)
* c35cc5b: added trusted hosts check
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 5633e1af40..b5189836fe 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -23,10 +23,11 @@ Symfony2 is the result of the work of many people who made the code better
- Benjamin Eberlei (beberlei)
- Jakub Zalas (jakubzalas)
- Hugo Hamon (hhamon)
- - Eriksen Costa (eriksencosta)
- Martin Hasoň (hason)
- - Jonathan Wage (jwage)
+ - Eriksen Costa (eriksencosta)
- William Durand (couac)
+ - Jonathan Wage (jwage)
+ - Romain Neutron (romain)
- Alexandre Salomé (alexandresalome)
- ornicar
- stealth35 (stealth35)
@@ -36,7 +37,6 @@ Symfony2 is the result of the work of many people who made the code better
- Henrik Bjørnskov (henrikbjorn)
- Miha Vrhovnik
- Bilal Amarni (bamarni)
- - Romain Neutron (romain)
- Florin Patan (florinpatan)
- Konstantin Kudryashov (everzet)
- Saša Stamenković (umpirsky)
@@ -44,13 +44,13 @@ Symfony2 is the result of the work of many people who made the code better
- Eric Clemmons (ericclemmons)
- Dariusz Górecki (canni)
- Henrik Westphal (snc)
+ - Grégoire Pineau (lyrixx)
- Deni
- Marc Weistroff (futurecat)
- Jordan Alliot (jalliot)
- Arnout Boks (aboks)
- Hidenori Goto (hidenorigoto)
- Fran Moreno (franmomu)
- - Grégoire Pineau (lyrixx)
- Andrej Hudec (pulzarraider)
- Lee McDermott
- Brandon Turner
@@ -69,8 +69,8 @@ Symfony2 is the result of the work of many people who made the code better
- lenar
- Fabien Pennequin (fabienpennequin)
- excelwebzone
- - woodspire
- Douglas Greenshields (shieldo)
+ - woodspire
- Mario A. Alvarez Garcia (nomack84)
- Kevin Bond (kbond)
- Richard Miller (mr_r_miller)
@@ -91,27 +91,27 @@ Symfony2 is the result of the work of many people who made the code better
- Larry Garfield (crell)
- Arnaud Kleinpeter (nanocom)
- Jonathan Ingram (jonathaningram)
+ - Wouter De Jong (wouterj)
- Sebastiaan Stok (sstok)
+ - Helmer Aaviksoo
- Javier Eguiluz (javier.eguiluz)
- Matthieu Ouellette-Vachon (maoueh)
- Amal Raghav (kertz)
- Artur Kotyrba
- Pablo Godel (pgodel)
- - Helmer Aaviksoo
+ - Dmitrii Chekaliuk (lazyhammer)
- Clément JOBEILI (dator)
- Hiromi Hishida (77web)
- Julien Brochet (mewt)
- Rafael Dohms (rdohms)
- - Wouter De Jong (wouterj)
+ - Dennis Benkert (denderello)
- Benjamin Dulau (dbenjamin)
- Andreas Hucks (meandmymonkey)
- Noel Guilbert (noel)
- - Dmitrii Chekaliuk (lazyhammer)
- Dorian Villet (gnutix)
- Guilherme Blanco (guilhermeblanco)
- Martin Schuhfuß (usefulthink)
- Thomas Rabaix (rande)
- - Dennis Benkert (denderello)
- Marcel Beerta (mazen)
- Albert Casademont (acasademont)
- Matthieu Bontemps (mbontemps)
@@ -194,9 +194,12 @@ Symfony2 is the result of the work of many people who made the code better
- Peter Kruithof (pkruithof)
- Kristen Gilden (kgilden)
- hossein zolfi (ocean)
+ - Philipp Kräutli (pkraeutli)
- Christian Flothmann (xabbuh)
- Greg Thornton (xdissent)
+ - Atsuhiro KUBO (iteman)
- Lars Strojny
+ - Loïc Chardonnet (gnusat)
- Costin Bereveanu (schniper)
- Markus Lanthaler (lanthaler)
- Jérôme Vieilledent (lolautruche)
@@ -262,6 +265,7 @@ Symfony2 is the result of the work of many people who made the code better
- mcben
- Maks Slesarenko
- Vicent Soria Durá (vicentgodella)
+ - Andrew Udvare
- alexpods
- Erik Trapman (eriktrapman)
- De Cock Xavier (xdecock)
@@ -274,7 +278,6 @@ Symfony2 is the result of the work of many people who made the code better
- Luis Cordova (cordoval)
- Michaël Perrin (michael.perrin)
- sasezaki
- - Loïc Chardonnet (gnusat)
- Xavier HAUSHERR
- Steven Surowiec
- Marek Kalnik (marekkalnik)
@@ -339,13 +342,14 @@ Symfony2 is the result of the work of many people who made the code better
- Johannes Klauss (cloppy)
- fzerorubigd
- develop
- - Atsuhiro KUBO (iteman)
+ - Tomasz Kowalczyk (thunderer)
- Samy Dindane (dinduks)
- yclian
- Pascal Helfenstein
- Matt Daum (daum)
- Baldur Rensch
- Alex Xandra Albert Sim
+ - Gabor Toth (tgabi333)
- Yuen-Chi Lian
- Joshua Nye
- avorobiev
@@ -363,8 +367,8 @@ Symfony2 is the result of the work of many people who made the code better
- Christian Soronellas Vallespí (theunic)
- Benjamin Grandfond (benjamin)
- Degory Valentine
+ - hacfi (hifi)
- Krzysiek Łabuś
- - Andrew Udvare
- Xavier Lacot (xavier)
- Olivier Maisonneuve
- Iwan van Staveren (istaveren)
@@ -381,7 +385,6 @@ Symfony2 is the result of the work of many people who made the code better
- Reen Lokum
- Pierre Vanliefland (pvanliefland)
- Martin Parsiegla (spea)
- - Philipp Kräutli (pkraeutli)
- Stefano Sala (stefano.sala)
- frost-nzcr4
- Abhoryo
@@ -398,6 +401,7 @@ Symfony2 is the result of the work of many people who made the code better
- Gustavo Falco (gfalco)
- Matt Robinson (inanimatt)
- Bob den Otter (bopp)
+ - David Marín Carreño (davefx)
- Jörn Lang (j.lang)
- julien pauli (jpauli)
- mwsaz
@@ -408,6 +412,7 @@ Symfony2 is the result of the work of many people who made the code better
- Derek ROTH
- Shin Ohno (ganchiku)
- Drew Butler (nodrew)
+ - Christian Morgan
- Alexander Miehe (engerim)
- Titouan Galopin (tgalopin)
- Don Pinkster
@@ -473,6 +478,7 @@ Symfony2 is the result of the work of many people who made the code better
- Dan Finnie
- Martijn Evers
- Benjamin Paap (benjaminpaap)
+ - Christian
- Sergii Smertin (nfx)
- Eddie Jaoude
- Nerijus Arlauskas
@@ -485,7 +491,6 @@ Symfony2 is the result of the work of many people who made the code better
- matteo giachino
- Daniel Mecke (daniel_mecke)
- Alex Demchenko (pilot)
- - Tomasz Kowalczyk (thunderer)
- Vincent AUBERT (vincent)
- Benoit Garret
- DerManoMann
@@ -501,6 +506,7 @@ Symfony2 is the result of the work of many people who made the code better
- Neil Katin
- peter
- Gustavo Adrian
+ - Nikita Konstantinov
- Brooks Boyd
- Roger Webb
- Nicolas Fabre (nfabre)
@@ -525,15 +531,18 @@ Symfony2 is the result of the work of many people who made the code better
- Sebastian Göttschkes (sgoettschkes)
- erikaheidi
- Pierre Tachoire
+ - marcj
- Ludek Stepan
- Balázs Benyó (duplabe)
- Marc Morera (mmoreram)
+ - Daniel Wehner
- Saem Ghani
- Sebastian Utz
- Keri Henare (kerihenare)
- Cédric Lahouste (rapotor)
- Anthony Ferrara
- Janusz Jablonski
+ - ShiraNai7
- George Giannoulopoulos
- Chris Wilkinson (thewilkybarkid)
- Ilya Biryukov
@@ -541,13 +550,13 @@ Symfony2 is the result of the work of many people who made the code better
- m.chwedziak
- Lance McNearney
- Alberto Pirovano (geezmo)
- - Philipp W (hifi)
- - Gabor Toth (tgabi333)
- Martin Pärtel
- Xavier Briand (xavierbriand)
- Evan Kaufman
- Romain Geissler
+ - Charles Sarrazin (csarrazi)
- Marcus Stöhr (dafish)
+ - Emmanuel Vella (emmanuel.vella)
- Carsten Nielsen (phreaknerd)
- Jay Severson
- René Kerner
@@ -580,6 +589,7 @@ Symfony2 is the result of the work of many people who made the code better
- jskvara
- Mephistofeles
- Hoffmann András
+ - Olivier
- pscheit
- Ramon Kleiss (akathos)
- Nicolas Badey (nico-b)
@@ -602,6 +612,7 @@ Symfony2 is the result of the work of many people who made the code better
- Andrey Esaulov (andremaha)
- hicham ELGUAROUANI (hiiimoo)
- Paul Seiffert (seiffert)
+ - Vasily (sirian)
- Stefan Koopmanschap (skoop)
- Ivan Kurnosov
- stloyd
@@ -672,7 +683,6 @@ Symfony2 is the result of the work of many people who made the code better
- cyrillej
- Alex Pods
- timaschew
- - Christian Morgan
- Ian Phillips
- Haritz
- Grummfy
@@ -696,7 +706,6 @@ Symfony2 is the result of the work of many people who made the code better
- Rafał
- Masao Maeda (brtriver)
- Dave Marshall (davedevelopment)
- - David Marín Carreño (davefx)
- Denis Klementjev (dklementjev)
- Kévin Dunglas (dunglas)
- Vincent Composieux (eko)
@@ -737,11 +746,14 @@ Symfony2 is the result of the work of many people who made the code better
- Christian Eikermann
- Antonio Angelino
- Vladimir Sazhin
+ - Jáchym Toušek
- Vyacheslav Slinko
- Johannes
- Jörg Rühl
- patrick-mcdougle
+ - Daniel Basten
- Giacomo Gallico
+ - Steve Müller
- andreabreu98
- Michael Schneider
- Jerome Tamarelle
@@ -787,6 +799,7 @@ Symfony2 is the result of the work of many people who made the code better
- Besnik Br
- sualko
- Nicolas Roudaire
+ - Lee Rowlands
- Alex Olmos (alexolmos)
- Juan Ases García (ases)
- Bernd Matzner (bmatzner)
@@ -802,10 +815,12 @@ Symfony2 is the result of the work of many people who made the code better
- Yohan Giarelli (frequence-web)
- Massimiliano Arione (garak)
- Vladislav Krupenkin (ideea)
+ - Jaik Dean (jaikdean)
- joris de wit (jdewit)
- Jérémy CROMBEZ (jeremy)
- Jorge Maiden (jorgemaiden)
- Justin Rainbow (jrainbow)
+ - JuntaTom (juntatom)
- Sébastien Armand (khepin)
- Krzysztof Menżyk (krymen)
- Martin Ledgard (le6o)
@@ -819,8 +834,10 @@ Symfony2 is the result of the work of many people who made the code better
- Rich Sage (richsage)
- Ruud Kamphuis (ruudk)
- Sarah Khalil (saro0h)
+ - scourgen hung (scourgen)
- Sebastian Busch (sebu)
- Andrea Giuliano (shark)
+ - Julien Sanchez (sumbobyboys)
- Markus Tacker (tacker)
- Tyler Stroud (tystr)
- Víctor Mateo (victormateo)
diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php
index 4364019345..789255554a 100644
--- a/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php
+++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php
@@ -51,10 +51,10 @@ class RuntimeInstantiator implements InstantiatorInterface
return $this->factory->createProxy(
$definition->getClass(),
function (&$wrappedInstance, LazyLoadingInterface $proxy) use ($realInstantiator) {
- $proxy->setProxyInitializer(null);
-
$wrappedInstance = call_user_func($realInstantiator);
+ $proxy->setProxyInitializer(null);
+
return true;
}
);
diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php
index 05f3ae1bff..fd64f97e6f 100644
--- a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php
+++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php
@@ -75,10 +75,10 @@ class ProxyDumper implements DumperInterface
$instantiation new $proxyClass(
function (&\$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface \$proxy) use (\$container) {
- \$proxy->setProxyInitializer(null);
-
\$wrappedInstance = \$container->$methodName(false);
+ \$proxy->setProxyInitializer(null);
+
return true;
}
);
diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service.php
index e2158b8f9b..aba65e85f1 100644
--- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service.php
+++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service.php
@@ -50,10 +50,10 @@ class LazyServiceProjectServiceContainer extends Container
return $this->services['foo'] = new stdClass_c1d194250ee2e2b7d2eab8b8212368a8(
function (& $wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) use ($container) {
- $proxy->setProxyInitializer(null);
-
$wrappedInstance = $container->getFooService(false);
+ $proxy->setProxyInitializer(null);
+
return true;
}
);
diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service_structure.txt b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service_structure.txt
index 4205e71086..332370c87e 100644
--- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service_structure.txt
+++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service_structure.txt
@@ -10,10 +10,10 @@ class ProjectServiceContainer extends Container
return $this->services['foo'] = new stdClass_%s(
function (&$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) use ($container) {
- $proxy->setProxyInitializer(null);
-
$wrappedInstance = $container->getFooService(false);
+ $proxy->setProxyInitializer(null);
+
return true;
}
);
diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php
index 5fefbfac8c..7823c63be6 100644
--- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php
+++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php
@@ -74,7 +74,7 @@ class ProxyDumperTest extends \PHPUnit_Framework_TestCase
'%wif ($lazyLoad) {%w$container = $this;%wreturn $this->services[\'foo\'] = new '
. 'SymfonyBridgeProxyManagerLazyProxyTestsInstantiatorProxyDumperTest_%s(%wfunction '
. '(&$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) use ($container) {'
- . '%w$proxy->setProxyInitializer(null);%w$wrappedInstance = $container->getFooService(false);'
+ . '%w$wrappedInstance = $container->getFooService(false);%w$proxy->setProxyInitializer(null);'
. '%wreturn true;%w}%w);%w}%w',
$code
);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/translation.html.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/translation.html.php
index 48ea9fdb08..23631b9943 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/translation.html.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/translation.html.php
@@ -1,2 +1,3 @@
This template is used for translation message extraction tests
-trans('new key') ?>
+trans('single-quoted key') ?>
+trans("double-quoted key") ?>
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php
index 4305f0e69c..d639f01806 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php
@@ -28,8 +28,9 @@ class PhpExtractorTest extends TestCase
$extractor->extract(__DIR__.'/../Fixtures/Resources/views/', $catalogue);
// Assert
- $this->assertCount(1, $catalogue->all('messages'), '->extract() should find 1 translation');
- $this->assertTrue($catalogue->has('new key'), '->extract() should find at leat "new key" message');
- $this->assertEquals('prefixnew key', $catalogue->get('new key'), '->extract() should apply "prefix" as prefix');
+ $this->assertCount(2, $catalogue->all('messages'), '->extract() should find 1 translation');
+ $this->assertTrue($catalogue->has('single-quoted key'), '->extract() should find the "single-quoted key" message');
+ $this->assertTrue($catalogue->has('double-quoted key'), '->extract() should find the "double-quoted key" message');
+ $this->assertEquals('prefixsingle-quoted key', $catalogue->get('single-quoted key'), '->extract() should apply "prefix" as prefix');
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php b/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php
index 1eb36052b3..1b8bfe44a7 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php
@@ -111,7 +111,7 @@ class PhpExtractor implements ExtractorInterface
}
}
- $message = trim($message, '\'');
+ $message = trim($message, '\'"');
if ($message) {
$catalog->set($message, $this->prefix.$message);
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
index abf1b80efc..ad75ac14a3 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
@@ -13,6 +13,7 @@ namespace Symfony\Bundle\SecurityBundle\DependencyInjection;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
+use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Alias;
@@ -413,7 +414,7 @@ class SecurityExtension extends Extension
}
if (false === $hasListeners) {
- throw new \LogicException(sprintf('No authentication listener registered for firewall "%s".', $id));
+ throw new InvalidConfigurationException(sprintf('No authentication listener registered for firewall "%s".', $id));
}
return array($listeners, $defaultEntryPoint);
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php
new file mode 100644
index 0000000000..2e55643c70
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php
@@ -0,0 +1,200 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Reference;
+
+use Symfony\Component\DependencyInjection\Parameter;
+use Symfony\Bundle\SecurityBundle\SecurityBundle;
+use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+abstract class CompleteConfigurationTest extends \PHPUnit_Framework_TestCase
+{
+ abstract protected function loadFromFile(ContainerBuilder $container, $file);
+
+ public function testRolesHierarchy()
+ {
+ $container = $this->getContainer('container1');
+ $this->assertEquals(array(
+ 'ROLE_ADMIN' => array('ROLE_USER'),
+ 'ROLE_SUPER_ADMIN' => array('ROLE_USER', 'ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH'),
+ 'ROLE_REMOTE' => array('ROLE_USER', 'ROLE_ADMIN'),
+ ), $container->getParameter('security.role_hierarchy.roles'));
+ }
+
+ public function testUserProviders()
+ {
+ $container = $this->getContainer('container1');
+
+ $providers = array_values(array_filter($container->getServiceIds(), function ($key) { return 0 === strpos($key, 'security.user.provider.concrete'); }));
+
+ $expectedProviders = array(
+ 'security.user.provider.concrete.default',
+ 'security.user.provider.concrete.default_foo',
+ 'security.user.provider.concrete.digest',
+ 'security.user.provider.concrete.digest_foo',
+ 'security.user.provider.concrete.basic',
+ 'security.user.provider.concrete.basic_foo',
+ 'security.user.provider.concrete.basic_bar',
+ 'security.user.provider.concrete.service',
+ 'security.user.provider.concrete.chain',
+ );
+
+ $this->assertEquals(array(), array_diff($expectedProviders, $providers));
+ $this->assertEquals(array(), array_diff($providers, $expectedProviders));
+
+ // chain provider
+ $this->assertEquals(array(array(
+ new Reference('security.user.provider.concrete.service'),
+ new Reference('security.user.provider.concrete.basic'),
+ )), $container->getDefinition('security.user.provider.concrete.chain')->getArguments());
+ }
+
+ public function testFirewalls()
+ {
+ $container = $this->getContainer('container1');
+
+ $arguments = $container->getDefinition('security.firewall.map')->getArguments();
+ $listeners = array();
+ foreach (array_keys($arguments[1]) as $contextId) {
+ $contextDef = $container->getDefinition($contextId);
+ $arguments = $contextDef->getArguments();
+ $listeners[] = array_map(function ($ref) { return (string) $ref; }, $arguments['index_0']);
+ }
+
+ $this->assertEquals(array(
+ array(),
+ array(
+ 'security.channel_listener',
+ 'security.logout_listener.secure',
+ 'security.authentication.listener.x509.secure',
+ 'security.authentication.listener.form.secure',
+ 'security.authentication.listener.basic.secure',
+ 'security.authentication.listener.digest.secure',
+ 'security.authentication.listener.anonymous.secure',
+ 'security.access_listener',
+ 'security.authentication.switchuser_listener.secure',
+ ),
+ ), $listeners);
+ }
+
+ public function testAccess()
+ {
+ $container = $this->getContainer('container1');
+
+ $rules = array();
+ foreach ($container->getDefinition('security.access_map')->getMethodCalls() as $call) {
+ if ($call[0] == 'add') {
+ $rules[] = array((string) $call[1][0], $call[1][1], $call[1][2]);
+ }
+ }
+
+ $matcherIds = array();
+ foreach ($rules as $rule) {
+ list($matcherId, $roles, $channel) = $rule;
+ $requestMatcher = $container->getDefinition($matcherId);
+
+ $this->assertFalse(isset($matcherIds[$matcherId]));
+ $matcherIds[$matcherId] = true;
+
+ $i = count($matcherIds);
+ if (1 === $i) {
+ $this->assertEquals(array('ROLE_USER'), $roles);
+ $this->assertEquals('https', $channel);
+ $this->assertEquals(
+ array('/blog/524', null, array('GET', 'POST')),
+ $requestMatcher->getArguments()
+ );
+ } elseif (2 === $i) {
+ $this->assertEquals(array('IS_AUTHENTICATED_ANONYMOUSLY'), $roles);
+ $this->assertNull($channel);
+ $this->assertEquals(
+ array('/blog/.*'),
+ $requestMatcher->getArguments()
+ );
+ }
+ }
+ }
+
+ public function testMerge()
+ {
+ $container = $this->getContainer('merge');
+
+ $this->assertEquals(array(
+ 'FOO' => array('MOO'),
+ 'ADMIN' => array('USER'),
+ ), $container->getParameter('security.role_hierarchy.roles'));
+ }
+
+ public function testEncoders()
+ {
+ $container = $this->getContainer('container1');
+
+ $this->assertEquals(array(array(
+ 'JMS\FooBundle\Entity\User1' => array(
+ 'class' => new Parameter('security.encoder.plain.class'),
+ 'arguments' => array(false),
+ ),
+ 'JMS\FooBundle\Entity\User2' => array(
+ 'class' => new Parameter('security.encoder.digest.class'),
+ 'arguments' => array('sha1', false, 5),
+ ),
+ 'JMS\FooBundle\Entity\User3' => array(
+ 'class' => new Parameter('security.encoder.digest.class'),
+ 'arguments' => array('md5', true, 5000),
+ ),
+ 'JMS\FooBundle\Entity\User4' => new Reference('security.encoder.foo'),
+ 'JMS\FooBundle\Entity\User5' => array(
+ 'class' => new Parameter('security.encoder.pbkdf2.class'),
+ 'arguments' => array('sha1', false, 5, 30),
+ ),
+ 'JMS\FooBundle\Entity\User6' => array(
+ 'class' => new Parameter('security.encoder.bcrypt.class'),
+ 'arguments' => array(15),
+ ),
+ )), $container->getDefinition('security.encoder_factory.generic')->getArguments());
+ }
+
+ public function testAcl()
+ {
+ $container = $this->getContainer('container1');
+
+ $this->assertTrue($container->hasDefinition('security.acl.dbal.provider'));
+ $this->assertEquals('security.acl.dbal.provider', (string) $container->getAlias('security.acl.provider'));
+ }
+
+ public function testCustomAclProvider()
+ {
+ $container = $this->getContainer('custom_acl_provider');
+
+ $this->assertFalse($container->hasDefinition('security.acl.dbal.provider'));
+ $this->assertEquals('foo', (string) $container->getAlias('security.acl.provider'));
+ }
+
+ protected function getContainer($file)
+ {
+ $container = new ContainerBuilder();
+ $security = new SecurityExtension();
+ $container->registerExtension($security);
+
+ $bundle = new SecurityBundle();
+ $bundle->build($container); // Attach all default factories
+ $this->loadFromFile($container, $file);
+
+ $container->getCompilerPassConfig()->setOptimizationPasses(array());
+ $container->getCompilerPassConfig()->setRemovingPasses(array());
+ $container->compile();
+
+ return $container;
+ }
+}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/invalid_check_path.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/invalid_check_path.php
deleted file mode 100644
index 62e1acbc6c..0000000000
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/invalid_check_path.php
+++ /dev/null
@@ -1,16 +0,0 @@
-loadFromExtension('security', array(
- 'providers' => array(
- 'default' => array('id' => 'foo'),
- ),
-
- 'firewalls' => array(
- 'some_firewall' => array(
- 'pattern' => '/secured_area/.*',
- 'form_login' => array(
- 'check_path' => '/some_area/login_check',
- )
- )
- )
-));
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/invalid_check_path.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/invalid_check_path.xml
deleted file mode 100644
index f507d7037a..0000000000
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/invalid_check_path.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/invalid_check_path.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/invalid_check_path.yml
deleted file mode 100644
index dcdef71202..0000000000
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/invalid_check_path.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-security:
- providers:
- default: { id: foo }
-
- firewalls:
- some_firewall:
- pattern: /secured_area/.*
- form_login:
- check_path: /some_area/login_check
\ No newline at end of file
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php
similarity index 96%
rename from src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/ConfigurationTest.php
rename to src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php
index 0925e8b759..047821cfdb 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/ConfigurationTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php
@@ -14,7 +14,7 @@ namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection;
use Symfony\Bundle\SecurityBundle\DependencyInjection\MainConfiguration;
use Symfony\Component\Config\Definition\Processor;
-class ConfigurationTest extends \PHPUnit_Framework_TestCase
+class MainConfigurationTest extends \PHPUnit_Framework_TestCase
{
/**
* The minimal, required config needed to not have any required validation
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpSecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpCompleteConfigurationTest.php
similarity index 90%
rename from src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpSecurityExtensionTest.php
rename to src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpCompleteConfigurationTest.php
index fc54797fc3..131c38d5e5 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpSecurityExtensionTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpCompleteConfigurationTest.php
@@ -15,7 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\Config\FileLocator;
-class PhpSecurityExtensionTest extends SecurityExtensionTest
+class PhpCompleteConfigurationTest extends CompleteConfigurationTest
{
protected function loadFromFile(ContainerBuilder $container, $file)
{
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php
index 7060b05c04..0605a1619e 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php
@@ -11,199 +11,81 @@
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection;
-use Symfony\Component\DependencyInjection\Reference;
-
-use Symfony\Component\DependencyInjection\Parameter;
-use Symfony\Bundle\SecurityBundle\SecurityBundle;
use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension;
+use Symfony\Bundle\SecurityBundle\SecurityBundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
-abstract class SecurityExtensionTest extends \PHPUnit_Framework_TestCase
+class SecurityExtensionTest extends \PHPUnit_Framework_TestCase
{
- abstract protected function loadFromFile(ContainerBuilder $container, $file);
-
- public function testRolesHierarchy()
+ /**
+ * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
+ * @expectedExceptionMessage The check_path "/some_area/login_check" for login method "form_login" is not matched by the firewall pattern "/secured_area/.*".
+ */
+ public function testInvalidCheckPath()
{
- $container = $this->getContainer('container1');
- $this->assertEquals(array(
- 'ROLE_ADMIN' => array('ROLE_USER'),
- 'ROLE_SUPER_ADMIN' => array('ROLE_USER', 'ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH'),
- 'ROLE_REMOTE' => array('ROLE_USER', 'ROLE_ADMIN'),
- ), $container->getParameter('security.role_hierarchy.roles'));
- }
+ $container = $this->getRawContainer();
- public function testUserProviders()
- {
- $container = $this->getContainer('container1');
-
- $providers = array_values(array_filter($container->getServiceIds(), function ($key) { return 0 === strpos($key, 'security.user.provider.concrete'); }));
-
- $expectedProviders = array(
- 'security.user.provider.concrete.default',
- 'security.user.provider.concrete.default_foo',
- 'security.user.provider.concrete.digest',
- 'security.user.provider.concrete.digest_foo',
- 'security.user.provider.concrete.basic',
- 'security.user.provider.concrete.basic_foo',
- 'security.user.provider.concrete.basic_bar',
- 'security.user.provider.concrete.service',
- 'security.user.provider.concrete.chain',
- );
-
- $this->assertEquals(array(), array_diff($expectedProviders, $providers));
- $this->assertEquals(array(), array_diff($providers, $expectedProviders));
-
- // chain provider
- $this->assertEquals(array(array(
- new Reference('security.user.provider.concrete.service'),
- new Reference('security.user.provider.concrete.basic'),
- )), $container->getDefinition('security.user.provider.concrete.chain')->getArguments());
- }
-
- public function testFirewalls()
- {
- $container = $this->getContainer('container1');
-
- $arguments = $container->getDefinition('security.firewall.map')->getArguments();
- $listeners = array();
- foreach (array_keys($arguments[1]) as $contextId) {
- $contextDef = $container->getDefinition($contextId);
- $arguments = $contextDef->getArguments();
- $listeners[] = array_map(function ($ref) { return (string) $ref; }, $arguments['index_0']);
- }
-
- $this->assertEquals(array(
- array(),
- array(
- 'security.channel_listener',
- 'security.logout_listener.secure',
- 'security.authentication.listener.x509.secure',
- 'security.authentication.listener.form.secure',
- 'security.authentication.listener.basic.secure',
- 'security.authentication.listener.digest.secure',
- 'security.authentication.listener.anonymous.secure',
- 'security.access_listener',
- 'security.authentication.switchuser_listener.secure',
+ $container->loadFromExtension('security', array(
+ 'providers' => array(
+ 'default' => array('id' => 'foo'),
),
- ), $listeners);
- }
- public function testAccess()
- {
- $container = $this->getContainer('container1');
+ 'firewalls' => array(
+ 'some_firewall' => array(
+ 'pattern' => '/secured_area/.*',
+ 'form_login' => array(
+ 'check_path' => '/some_area/login_check',
+ )
+ )
+ )
+ ));
- $rules = array();
- foreach ($container->getDefinition('security.access_map')->getMethodCalls() as $call) {
- if ($call[0] == 'add') {
- $rules[] = array((string) $call[1][0], $call[1][1], $call[1][2]);
- }
- }
-
- $matcherIds = array();
- foreach ($rules as $rule) {
- list($matcherId, $roles, $channel) = $rule;
- $requestMatcher = $container->getDefinition($matcherId);
-
- $this->assertFalse(isset($matcherIds[$matcherId]));
- $matcherIds[$matcherId] = true;
-
- $i = count($matcherIds);
- if (1 === $i) {
- $this->assertEquals(array('ROLE_USER'), $roles);
- $this->assertEquals('https', $channel);
- $this->assertEquals(
- array('/blog/524', null, array('GET', 'POST')),
- $requestMatcher->getArguments()
- );
- } elseif (2 === $i) {
- $this->assertEquals(array('IS_AUTHENTICATED_ANONYMOUSLY'), $roles);
- $this->assertNull($channel);
- $this->assertEquals(
- array('/blog/.*'),
- $requestMatcher->getArguments()
- );
- }
- }
- }
-
- public function testMerge()
- {
- $container = $this->getContainer('merge');
-
- $this->assertEquals(array(
- 'FOO' => array('MOO'),
- 'ADMIN' => array('USER'),
- ), $container->getParameter('security.role_hierarchy.roles'));
- }
-
- public function testEncoders()
- {
- $container = $this->getContainer('container1');
-
- $this->assertEquals(array(array(
- 'JMS\FooBundle\Entity\User1' => array(
- 'class' => new Parameter('security.encoder.plain.class'),
- 'arguments' => array(false),
- ),
- 'JMS\FooBundle\Entity\User2' => array(
- 'class' => new Parameter('security.encoder.digest.class'),
- 'arguments' => array('sha1', false, 5),
- ),
- 'JMS\FooBundle\Entity\User3' => array(
- 'class' => new Parameter('security.encoder.digest.class'),
- 'arguments' => array('md5', true, 5000),
- ),
- 'JMS\FooBundle\Entity\User4' => new Reference('security.encoder.foo'),
- 'JMS\FooBundle\Entity\User5' => array(
- 'class' => new Parameter('security.encoder.pbkdf2.class'),
- 'arguments' => array('sha1', false, 5, 30),
- ),
- 'JMS\FooBundle\Entity\User6' => array(
- 'class' => new Parameter('security.encoder.bcrypt.class'),
- 'arguments' => array(15),
- ),
- )), $container->getDefinition('security.encoder_factory.generic')->getArguments());
- }
-
- public function testAcl()
- {
- $container = $this->getContainer('container1');
-
- $this->assertTrue($container->hasDefinition('security.acl.dbal.provider'));
- $this->assertEquals('security.acl.dbal.provider', (string) $container->getAlias('security.acl.provider'));
- }
-
- public function testCustomAclProvider()
- {
- $container = $this->getContainer('custom_acl_provider');
-
- $this->assertFalse($container->hasDefinition('security.acl.dbal.provider'));
- $this->assertEquals('foo', (string) $container->getAlias('security.acl.provider'));
+ $container->compile();
}
/**
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
- * @expectedExceptionMessage not matched by the firewall pattern
+ * @expectedExceptionMessage No authentication listener registered for firewall "some_firewall"
*/
- public function testInvalidCheckPath()
+ public function testFirewallWithoutAuthenticationListener()
{
- $container = $this->getContainer('invalid_check_path');
+ $container = $this->getRawContainer();
+
+ $container->loadFromExtension('security', array(
+ 'providers' => array(
+ 'default' => array('id' => 'foo'),
+ ),
+
+ 'firewalls' => array(
+ 'some_firewall' => array(
+ 'pattern' => '/.*',
+ )
+ )
+ ));
+
+ $container->compile();
}
- protected function getContainer($file)
+ protected function getRawContainer()
{
$container = new ContainerBuilder();
$security = new SecurityExtension();
$container->registerExtension($security);
$bundle = new SecurityBundle();
- $bundle->build($container); // Attach all default factories
- $this->loadFromFile($container, $file);
+ $bundle->build($container);
$container->getCompilerPassConfig()->setOptimizationPasses(array());
$container->getCompilerPassConfig()->setRemovingPasses(array());
- $container->compile();
return $container;
}
+
+ protected function getContainer()
+ {
+ $containter = $this->getRawContainer();
+ $containter->compile();
+
+ return $containter;
+ }
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlSecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCompleteConfigurationTest.php
similarity index 90%
rename from src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlSecurityExtensionTest.php
rename to src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCompleteConfigurationTest.php
index 6ce0489f2e..cf6833a22a 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlSecurityExtensionTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCompleteConfigurationTest.php
@@ -15,7 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\Config\FileLocator;
-class XmlSecurityExtensionTest extends SecurityExtensionTest
+class XmlCompleteConfigurationTest extends CompleteConfigurationTest
{
protected function loadFromFile(ContainerBuilder $container, $file)
{
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlSecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlCompleteConfigurationTest.php
similarity index 90%
rename from src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlSecurityExtensionTest.php
rename to src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlCompleteConfigurationTest.php
index f5fabe0257..568b8623ea 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlSecurityExtensionTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlCompleteConfigurationTest.php
@@ -15,7 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\Config\FileLocator;
-class YamlSecurityExtensionTest extends SecurityExtensionTest
+class YamlCompleteConfigurationTest extends CompleteConfigurationTest
{
protected function loadFromFile(ContainerBuilder $container, $file)
{
diff --git a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php
index 8a7636c7e8..3be55e64c1 100644
--- a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php
+++ b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php
@@ -167,7 +167,7 @@ class ViolationMapper implements ViolationMapperInterface
// Skip forms inheriting their parent data when iterating the children
$childIterator = new \RecursiveIteratorIterator(
- new InheritDataAwareIterator($form->all())
+ new InheritDataAwareIterator($form)
);
// Make the path longer until we find a matching child
diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php
index 556c190d8e..f7fa834b35 100644
--- a/src/Symfony/Component/Form/Form.php
+++ b/src/Symfony/Component/Form/Form.php
@@ -19,6 +19,7 @@ use Symfony\Component\Form\Exception\LogicException;
use Symfony\Component\Form\Exception\OutOfBoundsException;
use Symfony\Component\Form\Util\FormUtil;
use Symfony\Component\Form\Util\InheritDataAwareIterator;
+use Symfony\Component\Form\Util\OrderedHashMap;
use Symfony\Component\PropertyAccess\PropertyPath;
/**
@@ -73,9 +74,9 @@ class Form implements \IteratorAggregate, FormInterface
/**
* The children of this form
- * @var FormInterface[] An array of FormInterface instances
+ * @var FormInterface[] A map of FormInterface instances
*/
- private $children = array();
+ private $children;
/**
* The errors of this form
@@ -164,6 +165,7 @@ class Form implements \IteratorAggregate, FormInterface
}
$this->config = $config;
+ $this->children = new OrderedHashMap();
}
public function __clone()
@@ -370,9 +372,9 @@ class Form implements \IteratorAggregate, FormInterface
// even if the form is compound.
if (count($this->children) > 0) {
// Update child forms from the data
- $childrenIterator = new InheritDataAwareIterator($this->children);
- $childrenIterator = new \RecursiveIteratorIterator($childrenIterator);
- $this->config->getDataMapper()->mapDataToForms($viewData, $childrenIterator);
+ $iterator = new InheritDataAwareIterator($this->children);
+ $iterator = new \RecursiveIteratorIterator($iterator);
+ $this->config->getDataMapper()->mapDataToForms($viewData, $iterator);
}
if ($dispatcher->hasListeners(FormEvents::POST_SET_DATA)) {
@@ -536,10 +538,7 @@ class Form implements \IteratorAggregate, FormInterface
$submittedData = array();
}
- for (reset($this->children); false !== current($this->children); next($this->children)) {
- $child = current($this->children);
- $name = key($this->children);
-
+ foreach ($this->children as $name => $child) {
if (array_key_exists($name, $submittedData) || $clearMissing) {
$child->submit(isset($submittedData[$name]) ? $submittedData[$name] : null, $clearMissing);
unset($submittedData[$name]);
@@ -587,9 +586,9 @@ class Form implements \IteratorAggregate, FormInterface
// descendants that inherit this form's data.
// These descendants will not be submitted normally (see the check
// for $this->config->getInheritData() above)
- $childrenIterator = new InheritDataAwareIterator($this->children);
- $childrenIterator = new \RecursiveIteratorIterator($childrenIterator);
- $this->config->getDataMapper()->mapFormsToData($childrenIterator, $viewData);
+ $iterator = new InheritDataAwareIterator($this->children);
+ $iterator = new \RecursiveIteratorIterator($iterator);
+ $this->config->getDataMapper()->mapFormsToData($iterator, $viewData);
}
$modelData = null;
@@ -765,9 +764,9 @@ class Form implements \IteratorAggregate, FormInterface
/**
* {@inheritdoc}
*/
- public function &all()
+ public function all()
{
- return $this->children;
+ return iterator_to_array($this->children);
}
/**
@@ -836,10 +835,9 @@ class Form implements \IteratorAggregate, FormInterface
$child->setParent($this);
if (!$this->lockSetData && $this->defaultDataSet && !$this->config->getInheritData()) {
- $children = array($child);
- $childrenIterator = new InheritDataAwareIterator($children);
- $childrenIterator = new \RecursiveIteratorIterator($childrenIterator);
- $this->config->getDataMapper()->mapDataToForms($viewData, $childrenIterator);
+ $iterator = new InheritDataAwareIterator(new \ArrayIterator(array($child)));
+ $iterator = new \RecursiveIteratorIterator($iterator);
+ $this->config->getDataMapper()->mapDataToForms($viewData, $iterator);
}
return $this;
@@ -940,11 +938,11 @@ class Form implements \IteratorAggregate, FormInterface
/**
* Returns the iterator for this group.
*
- * @return \ArrayIterator
+ * @return \Iterator
*/
public function getIterator()
{
- return new \ArrayIterator($this->children);
+ return $this->children;
}
/**
diff --git a/src/Symfony/Component/Form/Tests/Util/InheritDataAwareIteratorTest.php b/src/Symfony/Component/Form/Tests/Util/InheritDataAwareIteratorTest.php
deleted file mode 100644
index 19a0940bc0..0000000000
--- a/src/Symfony/Component/Form/Tests/Util/InheritDataAwareIteratorTest.php
+++ /dev/null
@@ -1,122 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Form\Tests\Util;
-
-use Symfony\Component\Form\Util\InheritDataAwareIterator;
-
-/**
- * @author Bernhard Schussek
- */
-class InheritDataAwareIteratorTest extends \PHPUnit_Framework_TestCase
-{
- public function testSupportDynamicModification()
- {
- $form = $this->getMockForm('form');
- $formToBeAdded = $this->getMockForm('added');
- $formToBeRemoved = $this->getMockForm('removed');
-
- $forms = array('form' => $form, 'removed' => $formToBeRemoved);
- $iterator = new InheritDataAwareIterator($forms);
-
- $iterator->rewind();
- $this->assertTrue($iterator->valid());
- $this->assertSame('form', $iterator->key());
- $this->assertSame($form, $iterator->current());
-
- // dynamic modification
- unset($forms['removed']);
- $forms['added'] = $formToBeAdded;
-
- // continue iteration
- $iterator->next();
- $this->assertTrue($iterator->valid());
- $this->assertSame('added', $iterator->key());
- $this->assertSame($formToBeAdded, $iterator->current());
-
- // end of array
- $iterator->next();
- $this->assertFalse($iterator->valid());
- }
-
- public function testSupportDynamicModificationInRecursiveCall()
- {
- $inheritingForm = $this->getMockForm('inheriting');
- $form = $this->getMockForm('form');
- $formToBeAdded = $this->getMockForm('added');
- $formToBeRemoved = $this->getMockForm('removed');
-
- $inheritingForm->getConfig()->expects($this->any())
- ->method('getInheritData')
- ->will($this->returnValue(true));
-
- $inheritingForm->add($form);
- $inheritingForm->add($formToBeRemoved);
-
- $forms = array('inheriting' => $inheritingForm);
- $iterator = new InheritDataAwareIterator($forms);
-
- $iterator->rewind();
- $this->assertTrue($iterator->valid());
- $this->assertSame('inheriting', $iterator->key());
- $this->assertSame($inheritingForm, $iterator->current());
- $this->assertTrue($iterator->hasChildren());
-
- // enter nested iterator
- $nestedIterator = $iterator->getChildren();
- $this->assertSame('form', $nestedIterator->key());
- $this->assertSame($form, $nestedIterator->current());
- $this->assertFalse($nestedIterator->hasChildren());
-
- // dynamic modification
- $inheritingForm->remove('removed');
- $inheritingForm->add($formToBeAdded);
-
- // continue iteration - nested iterator discovers change in the form
- $nestedIterator->next();
- $this->assertTrue($nestedIterator->valid());
- $this->assertSame('added', $nestedIterator->key());
- $this->assertSame($formToBeAdded, $nestedIterator->current());
-
- // end of array
- $nestedIterator->next();
- $this->assertFalse($nestedIterator->valid());
- }
-
- /**
- * @param string $name
- *
- * @return \PHPUnit_Framework_MockObject_MockObject
- */
- protected function getMockForm($name = 'name')
- {
- $config = $this->getMock('Symfony\Component\Form\FormConfigInterface');
-
- $config->expects($this->any())
- ->method('getName')
- ->will($this->returnValue($name));
- $config->expects($this->any())
- ->method('getCompound')
- ->will($this->returnValue(true));
- $config->expects($this->any())
- ->method('getDataMapper')
- ->will($this->returnValue($this->getMock('Symfony\Component\Form\DataMapperInterface')));
- $config->expects($this->any())
- ->method('getEventDispatcher')
- ->will($this->returnValue($this->getMock('Symfony\Component\EventDispatcher\EventDispatcher')));
-
- return $this->getMockBuilder('Symfony\Component\Form\Form')
- ->setConstructorArgs(array($config))
- ->disableArgumentCloning()
- ->setMethods(array('getViewData'))
- ->getMock();
- }
-}
diff --git a/src/Symfony/Component/Form/Tests/Util/OrderedHashMapTest.php b/src/Symfony/Component/Form/Tests/Util/OrderedHashMapTest.php
new file mode 100644
index 0000000000..60703c5b3b
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Util/OrderedHashMapTest.php
@@ -0,0 +1,487 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Util;
+
+use Symfony\Component\Form\Util\OrderedHashMap;
+
+/**
+ * @author Bernhard Schussek
+ */
+class OrderedHashMapTest extends \PHPUnit_Framework_TestCase
+{
+ public function testGet()
+ {
+ $map = new OrderedHashMap();
+ $map['first'] = 1;
+
+ $this->assertSame(1, $map['first']);
+ }
+
+ /**
+ * @expectedException \OutOfBoundsException
+ */
+ public function testGetNonExistingFails()
+ {
+ $map = new OrderedHashMap();
+
+ $map['first'];
+ }
+
+ public function testInsertStringKeys()
+ {
+ $map = new OrderedHashMap();
+ $map['first'] = 1;
+ $map['second'] = 2;
+
+ $this->assertSame(array('first' => 1, 'second' => 2), iterator_to_array($map));
+ }
+
+ public function testInsertNullKeys()
+ {
+ $map = new OrderedHashMap();
+ $map[] = 1;
+ $map['foo'] = 2;
+ $map[] = 3;
+
+ $this->assertSame(array(0 => 1, 'foo' => 2, 1 => 3), iterator_to_array($map));
+ }
+
+ /**
+ * Updates should not change the position of an element, otherwise we could
+ * turn foreach loops into endless loops if they change the current
+ * element:
+ *
+ * foreach ($map as $index => $value) {
+ * $map[$index] = $value + 1;
+ * }
+ *
+ * And we don't want this, right? :)
+ */
+ public function testUpdateDoesNotChangeElementPosition()
+ {
+ $map = new OrderedHashMap();
+ $map['first'] = 1;
+ $map['second'] = 2;
+ $map['first'] = 1;
+
+ $this->assertSame(array('first' => 1, 'second' => 2), iterator_to_array($map));
+ }
+
+ public function testIsset()
+ {
+ $map = new OrderedHashMap();
+ $map['first'] = 1;
+
+ $this->assertTrue(isset($map['first']));
+ }
+
+ public function testIssetReturnsFalseForNonExisting()
+ {
+ $map = new OrderedHashMap();
+
+ $this->assertFalse(isset($map['first']));
+ }
+
+ public function testIssetReturnsFalseForNull()
+ {
+ $map = new OrderedHashMap();
+ $map['first'] = null;
+
+ $this->assertFalse(isset($map['first']));
+ }
+
+ public function testUnset()
+ {
+ $map = new OrderedHashMap();
+ $map['first'] = 1;
+ $map['second'] = 2;
+
+ unset($map['first']);
+
+ $this->assertSame(array('second' => 2), iterator_to_array($map));
+ }
+
+ public function testUnsetNonExistingSucceeds()
+ {
+ $map = new OrderedHashMap();
+
+ unset($map['first']);
+ }
+
+ public function testEmptyIteration()
+ {
+ $map = new OrderedHashMap();
+ $it = $map->getIterator();
+
+ $it->rewind();
+ $this->assertFalse($it->valid());
+ $this->assertNull($it->key());
+ $this->assertNull($it->current());
+ }
+
+ public function testIterationSupportsInsertion()
+ {
+ $map = new OrderedHashMap(array('first' => 1));
+ $it = $map->getIterator();
+
+ $it->rewind();
+ $this->assertTrue($it->valid());
+ $this->assertSame('first', $it->key());
+ $this->assertSame(1, $it->current());
+
+ // dynamic modification
+ $map['added'] = 2;
+
+ // iterator is unchanged
+ $this->assertTrue($it->valid());
+ $this->assertSame('first', $it->key());
+ $this->assertSame(1, $it->current());
+
+ // continue iteration
+ $it->next();
+ $this->assertTrue($it->valid());
+ $this->assertSame('added', $it->key());
+ $this->assertSame(2, $it->current());
+
+ // end of map
+ $it->next();
+ $this->assertFalse($it->valid());
+ $this->assertNull($it->key());
+ $this->assertNull($it->current());
+ }
+
+ public function testIterationSupportsDeletionAndInsertion()
+ {
+ $map = new OrderedHashMap(array('first' => 1, 'removed' => 2));
+ $it = $map->getIterator();
+
+ $it->rewind();
+ $this->assertTrue($it->valid());
+ $this->assertSame('first', $it->key());
+ $this->assertSame(1, $it->current());
+
+ // dynamic modification
+ unset($map['removed']);
+ $map['added'] = 3;
+
+ // iterator is unchanged
+ $this->assertTrue($it->valid());
+ $this->assertSame('first', $it->key());
+ $this->assertSame(1, $it->current());
+
+ // continue iteration
+ $it->next();
+ $this->assertTrue($it->valid());
+ $this->assertSame('added', $it->key());
+ $this->assertSame(3, $it->current());
+
+ // end of map
+ $it->next();
+ $this->assertFalse($it->valid());
+ $this->assertNull($it->key());
+ $this->assertNull($it->current());
+ }
+
+ public function testIterationSupportsDeletionOfCurrentElement()
+ {
+ $map = new OrderedHashMap(array('removed' => 1, 'next' => 2));
+ $it = $map->getIterator();
+
+ $it->rewind();
+ $this->assertTrue($it->valid());
+ $this->assertSame('removed', $it->key());
+ $this->assertSame(1, $it->current());
+
+ unset($map['removed']);
+
+ // iterator is unchanged
+ $this->assertTrue($it->valid());
+ $this->assertSame('removed', $it->key());
+ $this->assertSame(1, $it->current());
+
+ // continue iteration
+ $it->next();
+ $this->assertTrue($it->valid());
+ $this->assertSame('next', $it->key());
+ $this->assertSame(2, $it->current());
+
+ // end of map
+ $it->next();
+ $this->assertFalse($it->valid());
+ $this->assertNull($it->key());
+ $this->assertNull($it->current());
+ }
+
+ public function testIterationIgnoresReplacementOfCurrentElement()
+ {
+ $map = new OrderedHashMap(array('replaced' => 1, 'next' => 2));
+ $it = $map->getIterator();
+
+ $it->rewind();
+ $this->assertTrue($it->valid());
+ $this->assertSame('replaced', $it->key());
+ $this->assertSame(1, $it->current());
+
+ $map['replaced'] = 3;
+
+ // iterator is unchanged
+ $this->assertTrue($it->valid());
+ $this->assertSame('replaced', $it->key());
+ $this->assertSame(1, $it->current());
+
+ // continue iteration
+ $it->next();
+ $this->assertTrue($it->valid());
+ $this->assertSame('next', $it->key());
+ $this->assertSame(2, $it->current());
+
+ // end of map
+ $it->next();
+ $this->assertFalse($it->valid());
+ $this->assertNull($it->key());
+ $this->assertNull($it->current());
+ }
+
+ public function testIterationSupportsDeletionOfCurrentAndLastElement()
+ {
+ $map = new OrderedHashMap(array('removed' => 1));
+ $it = $map->getIterator();
+
+ $it->rewind();
+ $this->assertTrue($it->valid());
+ $this->assertSame('removed', $it->key());
+ $this->assertSame(1, $it->current());
+
+ unset($map['removed']);
+
+ // iterator is unchanged
+ $this->assertTrue($it->valid());
+ $this->assertSame('removed', $it->key());
+ $this->assertSame(1, $it->current());
+
+ // end of map
+ $it->next();
+ $this->assertFalse($it->valid());
+ $this->assertNull($it->key());
+ $this->assertNull($it->current());
+ }
+
+ public function testIterationIgnoresReplacementOfCurrentAndLastElement()
+ {
+ $map = new OrderedHashMap(array('replaced' => 1));
+ $it = $map->getIterator();
+
+ $it->rewind();
+ $this->assertTrue($it->valid());
+ $this->assertSame('replaced', $it->key());
+ $this->assertSame(1, $it->current());
+
+ $map['replaced'] = 2;
+
+ // iterator is unchanged
+ $this->assertTrue($it->valid());
+ $this->assertSame('replaced', $it->key());
+ $this->assertSame(1, $it->current());
+
+ // end of map
+ $it->next();
+ $this->assertFalse($it->valid());
+ $this->assertNull($it->key());
+ $this->assertNull($it->current());
+ }
+
+ public function testIterationSupportsDeletionOfPreviousElement()
+ {
+ $map = new OrderedHashMap(array('removed' => 1, 'next' => 2, 'onemore' => 3));
+ $it = $map->getIterator();
+
+ $it->rewind();
+ $this->assertTrue($it->valid());
+ $this->assertSame('removed', $it->key());
+ $this->assertSame(1, $it->current());
+
+ // continue iteration
+ $it->next();
+ $this->assertTrue($it->valid());
+ $this->assertSame('next', $it->key());
+ $this->assertSame(2, $it->current());
+
+ unset($map['removed']);
+
+ // iterator is unchanged
+ $this->assertTrue($it->valid());
+ $this->assertSame('next', $it->key());
+ $this->assertSame(2, $it->current());
+
+ // continue iteration
+ $it->next();
+ $this->assertTrue($it->valid());
+ $this->assertSame('onemore', $it->key());
+ $this->assertSame(3, $it->current());
+
+ // end of map
+ $it->next();
+ $this->assertFalse($it->valid());
+ $this->assertNull($it->key());
+ $this->assertNull($it->current());
+ }
+
+ public function testIterationIgnoresReplacementOfPreviousElement()
+ {
+ $map = new OrderedHashMap(array('replaced' => 1, 'next' => 2, 'onemore' => 3));
+ $it = $map->getIterator();
+
+ $it->rewind();
+ $this->assertTrue($it->valid());
+ $this->assertSame('replaced', $it->key());
+ $this->assertSame(1, $it->current());
+
+ // continue iteration
+ $it->next();
+ $this->assertTrue($it->valid());
+ $this->assertSame('next', $it->key());
+ $this->assertSame(2, $it->current());
+
+ $map['replaced'] = 4;
+
+ // iterator is unchanged
+ $this->assertTrue($it->valid());
+ $this->assertSame('next', $it->key());
+ $this->assertSame(2, $it->current());
+
+ // continue iteration
+ $it->next();
+ $this->assertTrue($it->valid());
+ $this->assertSame('onemore', $it->key());
+ $this->assertSame(3, $it->current());
+
+ // end of map
+ $it->next();
+ $this->assertFalse($it->valid());
+ $this->assertNull($it->key());
+ $this->assertNull($it->current());
+ }
+
+ public function testIterationSupportsDeletionOfMultiplePreviousElements()
+ {
+ $map = new OrderedHashMap(array('removed' => 1, 'alsoremoved' => 2, 'next' => 3, 'onemore' => 4));
+ $it = $map->getIterator();
+
+ $it->rewind();
+ $this->assertTrue($it->valid());
+ $this->assertSame('removed', $it->key());
+ $this->assertSame(1, $it->current());
+
+ // continue iteration
+ $it->next();
+ $this->assertTrue($it->valid());
+ $this->assertSame('alsoremoved', $it->key());
+ $this->assertSame(2, $it->current());
+
+ // continue iteration
+ $it->next();
+ $this->assertTrue($it->valid());
+ $this->assertSame('next', $it->key());
+ $this->assertSame(3, $it->current());
+
+ unset($map['removed'], $map['alsoremoved']);
+
+ // iterator is unchanged
+ $this->assertTrue($it->valid());
+ $this->assertSame('next', $it->key());
+ $this->assertSame(3, $it->current());
+
+ // continue iteration
+ $it->next();
+ $this->assertTrue($it->valid());
+ $this->assertSame('onemore', $it->key());
+ $this->assertSame(4, $it->current());
+
+ // end of map
+ $it->next();
+ $this->assertFalse($it->valid());
+ $this->assertNull($it->key());
+ $this->assertNull($it->current());
+ }
+
+ public function testParallelIteration()
+ {
+ $map = new OrderedHashMap(array('first' => 1, 'second' => 2));
+ $it1 = $map->getIterator();
+ $it2 = $map->getIterator();
+
+ $it1->rewind();
+ $this->assertTrue($it1->valid());
+ $this->assertSame('first', $it1->key());
+ $this->assertSame(1, $it1->current());
+
+ $it2->rewind();
+ $this->assertTrue($it2->valid());
+ $this->assertSame('first', $it2->key());
+ $this->assertSame(1, $it2->current());
+
+ // 1: continue iteration
+ $it1->next();
+ $this->assertTrue($it1->valid());
+ $this->assertSame('second', $it1->key());
+ $this->assertSame(2, $it1->current());
+
+ // 2: remains unchanged
+ $this->assertTrue($it2->valid());
+ $this->assertSame('first', $it2->key());
+ $this->assertSame(1, $it2->current());
+
+ // 1: advance to end of map
+ $it1->next();
+ $this->assertFalse($it1->valid());
+ $this->assertNull($it1->key());
+ $this->assertNull($it1->current());
+
+ // 2: remains unchanged
+ $this->assertTrue($it2->valid());
+ $this->assertSame('first', $it2->key());
+ $this->assertSame(1, $it2->current());
+
+ // 2: continue iteration
+ $it2->next();
+ $this->assertTrue($it2->valid());
+ $this->assertSame('second', $it2->key());
+ $this->assertSame(2, $it2->current());
+
+ // 1: remains unchanged
+ $this->assertFalse($it1->valid());
+ $this->assertNull($it1->key());
+ $this->assertNull($it1->current());
+
+ // 2: advance to end of map
+ $it2->next();
+ $this->assertFalse($it2->valid());
+ $this->assertNull($it2->key());
+ $this->assertNull($it2->current());
+
+ // 1: remains unchanged
+ $this->assertFalse($it1->valid());
+ $this->assertNull($it1->key());
+ $this->assertNull($it1->current());
+ }
+
+ public function testCount()
+ {
+ $map = new OrderedHashMap();
+ $map[] = 1;
+ $map['foo'] = 2;
+ unset($map[0]);
+ $map[] = 3;
+
+ $this->assertSame(2, count($map));
+ }
+}
diff --git a/src/Symfony/Component/Form/Util/OrderedHashMap.php b/src/Symfony/Component/Form/Util/OrderedHashMap.php
new file mode 100644
index 0000000000..bf5b08cda6
--- /dev/null
+++ b/src/Symfony/Component/Form/Util/OrderedHashMap.php
@@ -0,0 +1,190 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Util;
+
+/**
+ * A hash map which keeps track of deletions and additions.
+ *
+ * Like in associative arrays, elements can be mapped to integer or string keys.
+ * Unlike associative arrays, the map keeps track of the order in which keys
+ * were added and removed. This order is reflected during iteration.
+ *
+ * The map supports concurrent modification during iteration. That means that
+ * you can insert and remove elements from within a foreach loop and the
+ * iterator will reflect those changes accordingly.
+ *
+ * While elements that are added during the loop are recognized by the iterator,
+ * changed elements are not. Otherwise the loop could be infinite if each loop
+ * changes the current element:
+ *
+ * $map = new OrderedHashMap();
+ * $map[1] = 1;
+ * $map[2] = 2;
+ * $map[3] = 3;
+ *
+ * foreach ($map as $index => $value) {
+ * echo "$index: $value\n"
+ * if (1 === $index) {
+ * $map[1] = 4;
+ * $map[] = 5;
+ * }
+ * }
+ *
+ * print_r(iterator_to_array($map));
+ *
+ * // => 1: 1
+ * // 2: 2
+ * // 3: 3
+ * // 4: 5
+ * // Array
+ * // (
+ * // [1] => 4
+ * // [2] => 2
+ * // [3] => 3
+ * // [4] => 5
+ * // )
+ *
+ * The map also supports multiple parallel iterators. That means that you can
+ * nest foreach loops without affecting each other's iteration:
+ *
+ * foreach ($map as $index => $value) {
+ * foreach ($map as $index2 => $value2) {
+ * // ...
+ * }
+ * }
+ *
+ * @author Bernhard Schussek
+ *
+ * @since 2.2.6
+ */
+class OrderedHashMap implements \ArrayAccess, \IteratorAggregate, \Countable
+{
+ /**
+ * The elements of the map, indexed by their keys.
+ *
+ * @var array
+ */
+ private $elements = array();
+
+ /**
+ * The keys of the map in the order in which they were inserted or changed.
+ *
+ * @var array
+ */
+ private $orderedKeys = array();
+
+ /**
+ * References to the cursors of all open iterators.
+ *
+ * @var array
+ */
+ private $managedCursors = array();
+
+ /**
+ * Creates a new map.
+ *
+ * @param array $elements The elements to insert initially.
+ *
+ * @since 2.2.6
+ */
+ public function __construct(array $elements = array())
+ {
+ $this->elements = $elements;
+ $this->orderedKeys = array_keys($elements);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @since 2.2.6
+ */
+ public function offsetExists($key)
+ {
+ return isset($this->elements[$key]);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @since 2.2.6
+ */
+ public function offsetGet($key)
+ {
+ if (!isset($this->elements[$key])) {
+ throw new \OutOfBoundsException('The offset "' . $key . '" does not exist.');
+ }
+
+ return $this->elements[$key];
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @since 2.2.6
+ */
+ public function offsetSet($key, $value)
+ {
+ if (null === $key || !isset($this->elements[$key])) {
+ if (null === $key) {
+ $key = array() === $this->orderedKeys
+ // If the array is empty, use 0 as key
+ ? 0
+ // Imitate PHP's behavior of generating a key that equals
+ // the highest existing integer key + 1
+ : max($this->orderedKeys) + 1;
+ }
+
+ $this->orderedKeys[] = $key;
+ }
+
+ $this->elements[$key] = $value;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @since 2.2.6
+ */
+ public function offsetUnset($key)
+ {
+ if (false !== ($position = array_search($key, $this->orderedKeys))) {
+ array_splice($this->orderedKeys, $position, 1);
+ unset($this->elements[$key]);
+
+ foreach ($this->managedCursors as $i => $cursor) {
+ if ($cursor >= $position) {
+ --$this->managedCursors[$i];
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @since 2.2.6
+ */
+ public function getIterator()
+ {
+ return new OrderedHashMapIterator($this->elements, $this->orderedKeys, $this->managedCursors);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @since 2.2.6
+ */
+ public function count()
+ {
+ return count($this->elements);
+ }
+}
diff --git a/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php b/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php
new file mode 100644
index 0000000000..87cf6a9d39
--- /dev/null
+++ b/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php
@@ -0,0 +1,163 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Util;
+
+/**
+ * Iterator for {@link OrderedHashMap} objects.
+ *
+ * This class is internal and should not be used.
+ *
+ * @author Bernhard Schussek
+ *
+ * @since 2.2.6
+ */
+class OrderedHashMapIterator implements \Iterator
+{
+ /**
+ * @var array
+ */
+ private $elements;
+
+ /**
+ * @var array
+ */
+ private $orderedKeys;
+
+ /**
+ * @var integer
+ */
+ private $cursor;
+
+ /**
+ * @var integer
+ */
+ private $cursorId;
+
+ /**
+ * @var array
+ */
+ private $managedCursors;
+
+ /**
+ * @var string|integer|null
+ */
+ private $key;
+
+ /**
+ * @var mixed
+ */
+ private $current;
+
+ /**
+ * Creates a new iterator.
+ *
+ * @param array $elements The elements of the map, indexed by their
+ * keys.
+ * @param array $orderedKeys The keys of the map in the order in which
+ * they should be iterated.
+ * @param array $managedCursors An array from which to reference the
+ * iterator's cursor as long as it is alive.
+ * This array is managed by the corresponding
+ * {@link OrderedHashMap} instance to support
+ * recognizing the deletion of elements.
+ *
+ * @since 2.2.6
+ */
+ public function __construct(array &$elements, array &$orderedKeys, array &$managedCursors)
+ {
+ $this->elements = &$elements;
+ $this->orderedKeys = &$orderedKeys;
+ $this->managedCursors = &$managedCursors;
+ $this->cursorId = count($managedCursors);
+
+ $this->managedCursors[$this->cursorId] = &$this->cursor;
+ }
+
+ /**
+ * Removes the iterator's cursors from the managed cursors of the
+ * corresponding {@link OrderedHashMap} instance.
+ *
+ * @since 2.2.6
+ */
+ public function __destruct()
+ {
+ // Use array_splice() instead of isset() to prevent holes in the
+ // array indices, which would break the initialization of $cursorId
+ array_splice($this->managedCursors, $this->cursorId, 1);
+ }
+
+ /**
+ *{@inheritdoc}
+ *
+ * @since 2.2.6
+ */
+ public function current()
+ {
+ return $this->current;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @since 2.2.6
+ */
+ public function next()
+ {
+ ++$this->cursor;
+
+ if (isset($this->orderedKeys[$this->cursor])) {
+ $this->key = $this->orderedKeys[$this->cursor];
+ $this->current = $this->elements[$this->key];
+ } else {
+ $this->key = null;
+ $this->current = null;
+ }
+ }
+
+ /**
+ *{@inheritdoc}
+ *
+ * @since 2.2.6
+ */
+ public function key()
+ {
+ return $this->key;
+ }
+
+ /**
+ *{@inheritdoc}
+ *
+ * @since 2.2.6
+ */
+ public function valid()
+ {
+ return null !== $this->key;
+ }
+
+ /**
+ *{@inheritdoc}
+ *
+ * @since 2.2.6
+ */
+ public function rewind()
+ {
+ $this->cursor = 0;
+
+ if (isset($this->orderedKeys[0])) {
+ $this->key = $this->orderedKeys[0];
+ $this->current = $this->elements[$this->key];
+ } else {
+ $this->key = null;
+ $this->current = null;
+ }
+ }
+}
diff --git a/src/Symfony/Component/Form/Util/ReferencingArrayIterator.php b/src/Symfony/Component/Form/Util/ReferencingArrayIterator.php
deleted file mode 100644
index 9bb64d79d2..0000000000
--- a/src/Symfony/Component/Form/Util/ReferencingArrayIterator.php
+++ /dev/null
@@ -1,78 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Form\Util;
-
-/**
- * Iterator that traverses an array.
- *
- * Contrary to {@link \ArrayIterator}, this iterator recognizes changes in the
- * original array during iteration.
- *
- * @author Bernhard Schussek
- */
-class ReferencingArrayIterator implements \Iterator
-{
- /**
- * @var array
- */
- private $array;
-
- /**
- * Creates a new iterator.
- *
- * @param array $array An array
- */
- public function __construct(array &$array)
- {
- $this->array = &$array;
- }
-
- /**
- *{@inheritdoc}
- */
- public function current()
- {
- return current($this->array);
- }
-
- /**
- *{@inheritdoc}
- */
- public function next()
- {
- next($this->array);
- }
-
- /**
- *{@inheritdoc}
- */
- public function key()
- {
- return key($this->array);
- }
-
- /**
- *{@inheritdoc}
- */
- public function valid()
- {
- return null !== key($this->array);
- }
-
- /**
- *{@inheritdoc}
- */
- public function rewind()
- {
- reset($this->array);
- }
-}
diff --git a/src/Symfony/Component/Form/Util/VirtualFormAwareIterator.php b/src/Symfony/Component/Form/Util/VirtualFormAwareIterator.php
index 708726dedf..581e3540e4 100644
--- a/src/Symfony/Component/Form/Util/VirtualFormAwareIterator.php
+++ b/src/Symfony/Component/Form/Util/VirtualFormAwareIterator.php
@@ -14,9 +14,6 @@ namespace Symfony\Component\Form\Util;
/**
* Iterator that traverses an array of forms.
*
- * Contrary to {@link \ArrayIterator}, this iterator recognizes changes in the
- * original array during iteration.
- *
* You can wrap the iterator into a {@link \RecursiveIterator} in order to
* enter any child form that inherits its parent's data and iterate the children
* of that form as well.
@@ -26,14 +23,14 @@ namespace Symfony\Component\Form\Util;
* @deprecated Deprecated since version 2.3, to be removed in 3.0. Use
* {@link InheritDataAwareIterator} instead.
*/
-class VirtualFormAwareIterator extends ReferencingArrayIterator implements \RecursiveIterator
+class VirtualFormAwareIterator extends \IteratorIterator implements \RecursiveIterator
{
/**
* {@inheritdoc}
*/
public function getChildren()
{
- return new static($this->current()->all());
+ return new static($this->current());
}
/**
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php
index 4d32094b41..2a8753064f 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php
@@ -28,7 +28,7 @@ class MemcachedSessionHandlerTest extends \PHPUnit_Framework_TestCase
protected function setUp()
{
if (!class_exists('Memcached')) {
- $this->markTestSkipped('Skipped tests Memcache class is not present');
+ $this->markTestSkipped('Skipped tests Memcached class is not present');
}
$this->memcached = $this->getMock('Memcached');
diff --git a/src/Symfony/Component/Intl/Locale/Locale.php b/src/Symfony/Component/Intl/Locale/Locale.php
index cca4e9e4f1..f16d937b02 100644
--- a/src/Symfony/Component/Intl/Locale/Locale.php
+++ b/src/Symfony/Component/Intl/Locale/Locale.php
@@ -312,6 +312,8 @@ class Locale
*/
public static function setDefault($locale)
{
- throw new MethodNotImplementedException(__METHOD__);
+ if ('en' !== $locale) {
+ throw new MethodNotImplementedException(__METHOD__);
+ }
}
}
diff --git a/src/Symfony/Component/Intl/Tests/Locale/LocaleTest.php b/src/Symfony/Component/Intl/Tests/Locale/LocaleTest.php
index 7e9571874f..2a5d5d2db7 100644
--- a/src/Symfony/Component/Intl/Tests/Locale/LocaleTest.php
+++ b/src/Symfony/Component/Intl/Tests/Locale/LocaleTest.php
@@ -150,6 +150,11 @@ class LocaleTest extends AbstractLocaleTest
$this->call('setDefault', 'pt_BR');
}
+ public function testSetDefaultAcceptsEn()
+ {
+ $this->call('setDefault', 'en');
+ }
+
protected function call($methodName)
{
$args = array_slice(func_get_args(), 1);
diff --git a/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php b/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php
index e248c6de5e..fdc2e8c75c 100644
--- a/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php
@@ -63,7 +63,7 @@ abstract class AbstractPreAuthenticatedListener implements ListenerInterface
try {
list($user, $credentials) = $this->getPreAuthenticatedData($request);
} catch (BadCredentialsException $exception) {
- $this->clearToken();
+ $this->clearToken($exception);
return;
}
@@ -91,21 +91,23 @@ abstract class AbstractPreAuthenticatedListener implements ListenerInterface
$this->dispatcher->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $loginEvent);
}
} catch (AuthenticationException $failed) {
- $this->clearToken();
+ $this->clearToken($failed);
}
}
/**
* Clears a PreAuthenticatedToken for this provider (if present)
+ *
+ * @param AuthenticationException $exception
*/
- protected function clearToken()
+ private function clearToken(AuthenticationException $exception)
{
$token = $this->securityContext->getToken();
if ($token instanceof PreAuthenticatedToken && $this->providerKey === $token->getProviderKey()) {
$this->securityContext->setToken(null);
if (null !== $this->logger) {
- $this->logger->info(sprintf("Cleared security context due to exception: %s", $failed->getMessage()));
+ $this->logger->info(sprintf("Cleared security context due to exception: %s", $exception->getMessage()));
}
}
}
diff --git a/src/Symfony/Component/Translation/MessageSelector.php b/src/Symfony/Component/Translation/MessageSelector.php
index 387c964d0d..8b416e947c 100644
--- a/src/Symfony/Component/Translation/MessageSelector.php
+++ b/src/Symfony/Component/Translation/MessageSelector.php
@@ -15,6 +15,7 @@ namespace Symfony\Component\Translation;
* MessageSelector.
*
* @author Fabien Potencier
+ * @author Bernhard Schussek
*
* @api
*/
@@ -73,7 +74,14 @@ class MessageSelector
}
$position = PluralizationRules::get($number, $locale);
+
if (!isset($standardRules[$position])) {
+ // when there's exactly one rule given, and that rule is a standard
+ // rule, use this rule
+ if (1 === count($parts) && isset($standardRules[0])) {
+ return $standardRules[0];
+ }
+
throw new \InvalidArgumentException(sprintf('Unable to choose a translation for "%s" with locale "%s". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %%count%% apples").', $message, $locale));
}
diff --git a/src/Symfony/Component/Translation/Tests/IdentityTranslatorTest.php b/src/Symfony/Component/Translation/Tests/IdentityTranslatorTest.php
index ce60995426..cec702a1b9 100644
--- a/src/Symfony/Component/Translation/Tests/IdentityTranslatorTest.php
+++ b/src/Symfony/Component/Translation/Tests/IdentityTranslatorTest.php
@@ -79,12 +79,14 @@ class IdentityTranslatorTest extends \PHPUnit_Framework_TestCase
public function getTransChoiceTests()
{
return array(
- array('There is no apple', '{0} There is no apple|{1} There is one apple|]1,Inf] There are %count% apples', 0, array('%count%' => 0)),
- array('There is one apple', '{0} There is no apple|{1} There is one apple|]1,Inf] There are %count% apples', 1, array('%count%' => 1)),
- array('There are 10 apples', '{0} There is no apple|{1} There is one apple|]1,Inf] There are %count% apples', 10, array('%count%' => 10)),
+ array('There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0, array('%count%' => 0)),
+ array('There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1, array('%count%' => 1)),
+ array('There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10, array('%count%' => 10)),
array('There are 0 apples', 'There is 1 apple|There are %count% apples', 0, array('%count%' => 0)),
array('There is 1 apple', 'There is 1 apple|There are %count% apples', 1, array('%count%' => 1)),
array('There are 10 apples', 'There is 1 apple|There are %count% apples', 10, array('%count%' => 10)),
+ // custom validation messages may be coded with a fixed value
+ array('There are 2 apples', 'There are 2 apples', 2, array('%count%' => 2)),
);
}
}
diff --git a/src/Symfony/Component/Translation/Tests/MessageSelectorTest.php b/src/Symfony/Component/Translation/Tests/MessageSelectorTest.php
index 74956294a1..d5a4f3e3d5 100644
--- a/src/Symfony/Component/Translation/Tests/MessageSelectorTest.php
+++ b/src/Symfony/Component/Translation/Tests/MessageSelectorTest.php
@@ -25,43 +25,61 @@ class MessageSelectorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, $selector->choose($id, $number, 'en'));
}
- /**
- * @expectedException InvalidArgumentException
- */
- public function testChooseWhenNoEnoughChoices()
+ public function testReturnMessageIfExactlyOneStandardRuleIsGiven()
{
$selector = new MessageSelector();
- $selector->choose('foo', 10, 'en');
+ $this->assertEquals('There are two apples', $selector->choose('There are two apples', 2, 'en'));
+ }
+
+ /**
+ * @dataProvider getNonMatchingMessages
+ * @expectedException \InvalidArgumentException
+ */
+ public function testThrowExceptionIfMatchingMessageCannotBeFound($id, $number)
+ {
+ $selector = new MessageSelector();
+
+ $selector->choose($id, $number, 'en');
+ }
+
+ public function getNonMatchingMessages()
+ {
+ return array(
+ array('{0} There are no apples|{1} There is one apple', 2),
+ array('{1} There is one apple|]1,Inf] There are %count% apples', 0),
+ array('{1} There is one apple|]2,Inf] There are %count% apples', 2),
+ array('{0} There are no apples|There is one apple', 2),
+ );
}
public function getChooseTests()
{
return array(
- array('There is no apples', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', 0),
- array('There is no apples', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', 0),
- array('There is no apples', '{0}There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', 0),
+ array('There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0),
+ array('There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0),
+ array('There are no apples', '{0}There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0),
- array('There is one apple', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', 1),
+ array('There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1),
- array('There is %count% apples', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', 10),
- array('There is %count% apples', '{0} There is no apples|{1} There is one apple|]1,Inf]There is %count% apples', 10),
- array('There is %count% apples', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', 10),
+ array('There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10),
+ array('There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf]There are %count% apples', 10),
+ array('There are %count% apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10),
- array('There is %count% apples', 'There is one apple|There is %count% apples', 0),
- array('There is one apple', 'There is one apple|There is %count% apples', 1),
- array('There is %count% apples', 'There is one apple|There is %count% apples', 10),
+ array('There are %count% apples', 'There is one apple|There are %count% apples', 0),
+ array('There is one apple', 'There is one apple|There are %count% apples', 1),
+ array('There are %count% apples', 'There is one apple|There are %count% apples', 10),
- array('There is %count% apples', 'one: There is one apple|more: There is %count% apples', 0),
- array('There is one apple', 'one: There is one apple|more: There is %count% apples', 1),
- array('There is %count% apples', 'one: There is one apple|more: There is %count% apples', 10),
+ array('There are %count% apples', 'one: There is one apple|more: There are %count% apples', 0),
+ array('There is one apple', 'one: There is one apple|more: There are %count% apples', 1),
+ array('There are %count% apples', 'one: There is one apple|more: There are %count% apples', 10),
- array('There is no apples', '{0} There is no apples|one: There is one apple|more: There is %count% apples', 0),
- array('There is one apple', '{0} There is no apples|one: There is one apple|more: There is %count% apples', 1),
- array('There is %count% apples', '{0} There is no apples|one: There is one apple|more: There is %count% apples', 10),
+ array('There are no apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 0),
+ array('There is one apple', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 1),
+ array('There are %count% apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 10),
- array('', '{0}|{1} There is one apple|]1,Inf] There is %count% apples', 0),
- array('', '{0} There is no apples|{1}|]1,Inf] There is %count% apples', 1),
+ array('', '{0}|{1} There is one apple|]1,Inf] There are %count% apples', 0),
+ array('', '{0} There are no apples|{1}|]1,Inf] There are %count% apples', 1),
// Indexed only tests which are Gettext PoFile* compatible strings.
array('There are %count% apples', 'There is one apple|There are %count% apples', 0),
@@ -69,12 +87,12 @@ class MessageSelectorTest extends \PHPUnit_Framework_TestCase
array('There are %count% apples', 'There is one apple|There are %count% apples', 2),
// Tests for float numbers
- array('There is almost one apple', '{0} There is no apples|]0,1[ There is almost one apple|{1} There is one apple|[1,Inf] There is more than one apple', 0.7),
- array('There is one apple', '{0} There is no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1),
- array('There is more than one apple', '{0} There is no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1.7),
- array('There is no apples', '{0} There is no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0),
- array('There is no apples', '{0} There is no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0.0),
- array('There is no apples', '{0.0} There is no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0),
+ array('There is almost one apple', '{0} There are no apples|]0,1[ There is almost one apple|{1} There is one apple|[1,Inf] There is more than one apple', 0.7),
+ array('There is one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1),
+ array('There is more than one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1.7),
+ array('There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0),
+ array('There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0.0),
+ array('There are no apples', '{0.0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0),
);
}
}
diff --git a/src/Symfony/Component/Translation/Tests/TranslatorTest.php b/src/Symfony/Component/Translation/Tests/TranslatorTest.php
index fb843d727f..12837516f3 100644
--- a/src/Symfony/Component/Translation/Tests/TranslatorTest.php
+++ b/src/Symfony/Component/Translation/Tests/TranslatorTest.php
@@ -237,9 +237,9 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
public function getTransChoiceTests()
{
return array(
- array('Il y a 0 pomme', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
- array('Il y a 1 pomme', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
- array('Il y a 10 pommes', '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
+ array('Il y a 0 pomme', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
+ array('Il y a 1 pomme', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
+ array('Il y a 10 pommes', '{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples', '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
array('Il y a 0 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
array('Il y a 1 pomme', 'There is one apple|There is %count% apples', 'Il y a %count% pomme|Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
@@ -249,11 +249,11 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
array('Il y a 1 pomme', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
array('Il y a 10 pommes', 'one: There is one apple|more: There is %count% apples', 'one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
- array('Il n\'y a aucune pomme', '{0} There is no apple|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
- array('Il y a 1 pomme', '{0} There is no apple|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
- array('Il y a 10 pommes', '{0} There is no apple|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
+ array('Il n\'y a aucune pomme', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
+ array('Il y a 1 pomme', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 1, array('%count%' => 1), 'fr', ''),
+ array('Il y a 10 pommes', '{0} There are no apples|one: There is one apple|more: There is %count% apples', '{0} Il n\'y a aucune pomme|one: Il y a %count% pomme|more: Il y a %count% pommes', 10, array('%count%' => 10), 'fr', ''),
- array('Il y a 0 pomme', new String('{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples'), '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
+ array('Il y a 0 pomme', new String('{0} There are no appless|{1} There is one apple|]1,Inf] There is %count% apples'), '[0,1] Il y a %count% pomme|]1,Inf] Il y a %count% pommes', 0, array('%count%' => 0), 'fr', ''),
);
}
@@ -277,16 +277,15 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
}
- /**
- * @expectedException \InvalidArgumentException
- */
public function testTransChoiceFallbackWithNoTranslation()
{
$translator = new Translator('ru', new MessageSelector());
$translator->setFallbackLocales(array('en'));
$translator->addLoader('array', new ArrayLoader());
- $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
+ // consistent behavior with Translator::trans(), which returns the string
+ // unchanged if it can't be found
+ $this->assertEquals('some_message2', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
}
}
diff --git a/src/Symfony/Component/Validator/Mapping/Loader/XmlFileLoader.php b/src/Symfony/Component/Validator/Mapping/Loader/XmlFileLoader.php
index 9b0958829e..cad247e883 100644
--- a/src/Symfony/Component/Validator/Mapping/Loader/XmlFileLoader.php
+++ b/src/Symfony/Component/Validator/Mapping/Loader/XmlFileLoader.php
@@ -166,7 +166,10 @@ class XmlFileLoader extends FileLoader
$value = array();
}
} else {
- $value = trim($node);
+ $value = XmlUtils::phpize($node);
+ if (is_string($value)) {
+ $value = trim($value);
+ }
}
$options[(string) $node['name']] = $value;
diff --git a/src/Symfony/Component/Validator/Tests/Mapping/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/Validator/Tests/Mapping/Loader/XmlFileLoaderTest.php
index 1677b81b1f..7c6e355bd1 100644
--- a/src/Symfony/Component/Validator/Tests/Mapping/Loader/XmlFileLoaderTest.php
+++ b/src/Symfony/Component/Validator/Tests/Mapping/Loader/XmlFileLoaderTest.php
@@ -16,6 +16,7 @@ use Symfony\Component\Validator\Constraints\Collection;
use Symfony\Component\Validator\Constraints\NotNull;
use Symfony\Component\Validator\Constraints\Range;
use Symfony\Component\Validator\Constraints\Choice;
+use Symfony\Component\Validator\Constraints\Regex;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\Loader\XmlFileLoader;
use Symfony\Component\Validator\Tests\Fixtures\ConstraintA;
@@ -68,6 +69,22 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, $metadata);
}
+ public function testLoadClassMetadataWithNonStrings()
+ {
+ $loader = new XmlFileLoader(__DIR__.'/constraint-mapping-non-strings.xml');
+ $metadata = new ClassMetadata('Symfony\Component\Validator\Tests\Fixtures\Entity');
+
+ $loader->loadClassMetadata($metadata);
+
+ $expected = new ClassMetadata('Symfony\Component\Validator\Tests\Fixtures\Entity');
+ $expected->addPropertyConstraint('firstName', new Regex(array('pattern' => '/^1/', 'match' => false)));
+
+ $properties = $metadata->getPropertyMetadata('firstName');
+ $constraints = $properties[0]->getConstraints();
+
+ $this->assertFalse($constraints[0]->match);
+ }
+
public function testLoadGroupSequenceProvider()
{
$loader = new XmlFileLoader(__DIR__.'/constraint-mapping.xml');
diff --git a/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping-non-strings.xml b/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping-non-strings.xml
new file mode 100644
index 0000000000..dfd5edddc5
--- /dev/null
+++ b/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping-non-strings.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ Symfony\Component\Validator\Tests\Fixtures\
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping.xml b/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping.xml
index 946200ba11..dfac70d9cf 100644
--- a/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping.xml
+++ b/src/Symfony/Component/Validator/Tests/Mapping/Loader/constraint-mapping.xml
@@ -78,7 +78,7 @@
-
+