diff --git a/.travis.yml b/.travis.yml
index 9d5b2fff64..14c7704c4c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,16 +14,19 @@ matrix:
services: mongodb
-before_script:
+before_install:
- travis_retry sudo apt-get install parallel
- sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then echo "" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini; fi;'
- sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
- sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ] && [ $(php -r "echo PHP_MINOR_VERSION;") -le 4 ]; then echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
- sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
- sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
- - COMPOSER_ROOT_VERSION=dev-master composer --prefer-source --dev install
+ - sudo locale-gen fr_FR.UTF-8 && sudo update-locale
# - sh -c 'if [ "$TRAVIS_PHP_VERSION" != "5.3.3" ]; then phpunit --self-update; fi;'
+install:
+ - COMPOSER_ROOT_VERSION=dev-master composer --prefer-source install
+
script:
- ls -d src/Symfony/*/* | parallel --gnu --keep-order 'echo "Running {} tests"; phpunit --exclude-group tty,benchmark {};'
- echo "Running tests requiring tty"; phpunit --group tty
diff --git a/CHANGELOG-2.3.md b/CHANGELOG-2.3.md
index c132013aa0..833ded8052 100644
--- a/CHANGELOG-2.3.md
+++ b/CHANGELOG-2.3.md
@@ -7,6 +7,59 @@ 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.19 (2014-09-03)
+
+ * security #11832 CVE-2014-6072 (fabpot)
+ * security #11831 CVE-2014-5245 (stof)
+ * security #11830 CVE-2014-4931 (aitboudad, Jérémy Derussé)
+ * security #11829 CVE-2014-6061 (damz, fabpot)
+ * security #11828 CVE-2014-5244 (nicolas-grekas, larowlan)
+ * bug #10197 [FrameworkBundle] PhpExtractor bugfix and improvements (mtibben)
+ * bug #11772 [Filesystem] Add FTP stream wrapper context option to enable overwrite (Damian Sromek)
+ * bug #11788 [Yaml] fixed mapping keys containing a quoted # (hvt, fabpot)
+ * bug #11160 [DoctrineBridge] Abstract Doctrine Subscribers with tags (merk)
+ * bug #11768 [ClassLoader] Add a __call() method to XcacheClassLoader (tstoeckler)
+ * bug #11726 [Filesystem Component] mkdir race condition fix #11626 (kcassam)
+ * bug #11677 [YAML] resolve variables in inlined YAML (xabbuh)
+ * bug #11639 [DependencyInjection] Fixed factory service not within the ServiceReferenceGraph. (boekkooi)
+ * bug #11778 [Validator] Fixed wrong translations for Collection constraints (samicemalone)
+ * bug #11756 [DependencyInjection] fix @return anno created by PhpDumper (jakubkulhan)
+ * bug #11711 [DoctrineBridge] Fix empty parameter logging in the dbal logger (jakzal)
+ * bug #11692 [DomCrawler] check for the correct field type (xabbuh)
+ * bug #11672 [Routing] fix handling of nullable XML attributes (xabbuh)
+ * bug #11624 [DomCrawler] fix the axes handling in a bc way (xabbuh)
+ * bug #11676 [Form] Fixed #11675 ValueToDuplicatesTransformer accept "0" value (Nek-)
+ * bug #11695 [Validators] Fixed failing tests requiring ICU 52.1 which are skipped otherwise (webmozart)
+ * bug #11529 [WebProfilerBundle] Fixed double height of canvas (hason)
+ * bug #11641 [WebProfilerBundle ] Fix toolbar vertical alignment (blaugueux)
+ * bug #11559 [Validator] Convert objects to string in comparison validators (webmozart)
+ * feature #11510 [HttpFoundation] MongoDbSessionHandler supports auto expiry via configurable expiry_field (catchamonkey)
+ * bug #11408 [HttpFoundation] Update QUERY_STRING when overrideGlobals (yguedidi)
+ * bug #11633 [FrameworkBundle] add missing attribute to XSD (xabbuh)
+ * bug #11601 [Validator] Allow basic auth in url when using UrlValidator. (blaugueux)
+ * bug #11609 [Console] fixed style creation when providing an unknown tag option (fabpot)
+ * bug #10914 [HttpKernel] added an analyze of environment parameters for built-in server (mauchede)
+ * bug #11598 [Finder] Shell escape and windows support (Gordon Franke, gimler)
+ * bug #11499 [BrowserKit] Fixed relative redirects for ambiguous paths (pkruithof)
+ * bug #11516 [BrowserKit] Fix browser kit redirect with ports (dakota)
+ * bug #11545 [Bundle][FrameworkBundle] built-in server: exit when docroot does not exist (xabbuh)
+ * bug #11560 Plural fix (1emming)
+ * bug #11558 [DependencyInjection] Fixed missing 'factory-class' attribute in XmlDumper output (kerdany)
+ * bug #11548 [Component][DomCrawler] fix axes handling in Crawler::filterXPath() (xabbuh)
+ * bug #11422 [DependencyInjection] Self-referenced 'service_container' service breaks garbage collection (sun)
+ * bug #11428 [Serializer] properly handle null data when denormalizing (xabbuh)
+ * bug #10687 [Validator] Fixed string conversion in constraint violations (eagleoneraptor, webmozart)
+ * bug #11475 [EventDispatcher] don't count empty listeners (xabbuh)
+ * bug #11436 fix signal handling in wait() on calls to stop() (xabbuh, romainneutron)
+ * bug #11469 [BrowserKit] Fixed server HTTP_HOST port uri conversion (bcremer, fabpot)
+ * bug #11425 Fix issue described in #11421 (Ben, ben-rosio)
+ * bug #11423 Pass a Scope instance instead of a scope name when cloning a container in the GrahpvizDumper (jakzal)
+ * bug #11120 [Process] Reduce I/O load on Windows platform (romainneutron)
+ * bug #11342 [Form] Check if IntlDateFormatter constructor returned a valid object before using it (romainneutron)
+ * bug #11411 [Validator] Backported #11410 to 2.3: Object initializers are called only once per object (webmozart)
+ * bug #11403 [Translator][FrameworkBundle] Added @ to the list of allowed chars in Translator (takeit)
+ * bug #11381 [Process] Use correct test for empty string in UnixPipes (whs, romainneutron)
+
* 2.3.18 (2014-07-15)
* [Security] Forced validate of locales passed to the translator
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index ca9e441c93..a9526431c1 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -18,38 +18,39 @@ Symfony2 is the result of the work of many people who made the code better
- Joseph Bielawski (stloyd)
- Ryan Weaver (weaverryan)
- Lukas Kahwe Smith (lsmith)
- - Jeremy Mikola (jmikola)
- Romain Neutron (romain)
+ - Jeremy Mikola (jmikola)
- Jean-François Simon (jfsimon)
- Benjamin Eberlei (beberlei)
- Igor Wiedler (igorw)
- Hugo Hamon (hhamon)
- - Eriksen Costa (eriksencosta)
- Martin Hasoň (hason)
+ - Eriksen Costa (eriksencosta)
- Jonathan Wage (jwage)
- Alexandre Salomé (alexandresalome)
- William Durand (couac)
- ornicar
- stealth35 (stealth35)
+ - Grégoire Pineau (lyrixx)
- Alexander Mols (asm89)
- Bulat Shakirzyanov (avalanche123)
- - Grégoire Pineau (lyrixx)
+ - Nicolas Grekas (nicolas-grekas)
- Francis Besset (francisbesset)
- Saša Stamenković (umpirsky)
- Miha Vrhovnik
- Henrik Bjørnskov (henrikbjorn)
- Konstantin Kudryashov (everzet)
+ - Wouter De Jong (wouterj)
+ - Christian Flothmann (xabbuh)
- Bilal Amarni (bamarni)
- Florin Patan (florinpatan)
- - Wouter De Jong (wouterj)
- Eric Clemmons (ericclemmons)
- - Nicolas Grekas (nicolas-grekas)
- Andrej Hudec (pulzarraider)
- Deni
- Henrik Westphal (snc)
- Dariusz Górecki (canni)
- - Arnout Boks (aboks)
- Christian Raue
+ - Arnout Boks (aboks)
- Michel Weimerskirch (mweimerskirch)
- Lee McDermott
- Brandon Turner
@@ -61,11 +62,11 @@ Symfony2 is the result of the work of many people who made the code better
- Bart van den Burg (burgov)
- Antoine Hérault (herzult)
- Toni Uebernickel (havvg)
- - Arnaud Le Blanc (arnaud-lb)
- Luis Cordova (cordoval)
+ - Arnaud Le Blanc (arnaud-lb)
+ - Tim Nagel (merk)
- Kevin Bond (kbond)
- Brice BERNARD (brikou)
- - Tim Nagel (merk)
- marc.weistroff
- lenar
- Włodzimierz Gajda (gajdaw)
@@ -74,25 +75,25 @@ Symfony2 is the result of the work of many people who made the code better
- Florian Voutzinos (florianv)
- Fabien Pennequin (fabienpennequin)
- Jacob Dreesen (jdreesen)
+ - Adrien Brault (adrienbrault)
- Gábor Egyed (1ed)
- Ait Boudad Abdellatif (aitboudad)
- - Adrien Brault (adrienbrault)
- Michal Piotrowski (eventhorizon)
+ - Gordon Franke (gimler)
- Robert Schönthal (digitalkaoz)
- Juti Noppornpitak (shiroyuki)
- Sebastian Hörl (blogsh)
- Daniel Gomes (danielcsgomes)
- Hidenori Goto (hidenorigoto)
- Peter Kokot (maastermedia)
- - Christian Flothmann (xabbuh)
- - Jérémie Augustin (jaugustin)
- David Buchmann (dbu)
+ - Pablo Godel (pgodel)
+ - Jérémie Augustin (jaugustin)
+ - Rafael Dohms (rdohms)
- Jérôme Tamarelle (gromnan)
- Tigran Azatyan (tigranazatyan)
- Javier Eguiluz (javier.eguiluz)
- - Rafael Dohms (rdohms)
- Richard Shank (iampersistent)
- - Gordon Franke (gimler)
- Eric GELOEN (gelo)
- Helmer Aaviksoo
- Sebastiaan Stok (sstok)
@@ -103,7 +104,6 @@ Symfony2 is the result of the work of many people who made the code better
- Jonathan Ingram (jonathaningram)
- Artur Kotyrba
- Guilherme Blanco (guilhermeblanco)
- - Pablo Godel (pgodel)
- Clemens Tolboom
- Dmitrii Chekaliuk (lazyhammer)
- Clément JOBEILI (dator)
@@ -114,10 +114,13 @@ Symfony2 is the result of the work of many people who made the code better
- Dennis Benkert (denderello)
- Rouven Weßling (realityking)
- Benjamin Dulau (dbenjamin)
+ - Stefano Sala (stefano.sala)
- Andreas Hucks (meandmymonkey)
+ - Andréia Bohner (andreia)
- Noel Guilbert (noel)
- Charles Sarrazin (csarrazi)
- bronze1man
+ - sun (sun)
- Larry Garfield (crell)
- Martin Schuhfuß (usefulthink)
- Thomas Rabaix (rande)
@@ -131,10 +134,9 @@ Symfony2 is the result of the work of many people who made the code better
- jeff
- Justin Hileman (bobthecow)
- Sven Paulus (subsven)
- - Andréia Bohner (andreia)
+ - Lars Strojny (lstrojny)
- Joel Wurtz (brouznouf)
- Rui Marinho (ruimarinho)
- - sun (sun)
- Julien Brochet (mewt)
- Tugdual Saunier (tucksaun)
- Sergey Linnik (linniksa)
@@ -152,12 +154,11 @@ Symfony2 is the result of the work of many people who made the code better
- Katsuhiro OGAWA
- Peter Rehm (rpet)
- Alif Rachmawadi
+ - Pierre-Yves LEBECQ (pylebecq)
- Matthias Pigulla (mpdude)
- - Stefano Sala (stefano.sala)
- Joseph Rouff (rouffj)
- Félix Labrecque (woodspire)
- GordonsLondon
- - Lars Strojny (lstrojny)
- Jan Sorgalla (jsor)
- Ray
- Chekote
@@ -168,14 +169,17 @@ Symfony2 is the result of the work of many people who made the code better
- Beau Simensen (simensen)
- Robert Kiss (kepten)
- Kim Hemsø Rasmussen (kimhemsoe)
+ - Tom Van Looy (tvlooy)
- Wouter Van Hecke
+ - Peter Kruithof (pkruithof)
- Michael Holm (hollo)
- Marc Weistroff (futurecat)
- - Pierre-Yves LEBECQ (pylebecq)
- Roman Marintšenko (inori)
- Chris Smith (cs278)
- Florian Klein (docteurklein)
- Manuel Kiessling (manuelkiessling)
+ - Atsuhiro KUBO (iteman)
+ - Issei Murasawa (issei_m)
- Bertrand Zuchuat (garfield-fr)
- Gabor Toth (tgabi333)
- realmfoo
@@ -184,6 +188,7 @@ Symfony2 is the result of the work of many people who made the code better
- Grégoire Passault (gregwar)
- Uwe Jäger (uwej711)
- Aurelijus Valeiša (aurelijus)
+ - Jan Decavele (jandc)
- Gustavo Piltcher
- Stepan Tanasiychuk (stfalcon)
- Tiago Ribeiro (fixe)
@@ -191,10 +196,12 @@ Symfony2 is the result of the work of many people who made the code better
- Adrian Rudnik (kreischweide)
- Francesc Rosàs (frosas)
- Julien Galenski (ruian)
+ - Eugene Leonovich (rybakit)
- Bongiraud Dominique
- janschoenherr
- Marco Pivetta (ocramius)
- Ricard Clau (ricardclau)
+ - Jérémy DERUSSÉ (jderusse)
- Kévin Dunglas (dunglas)
- Erin Millard
- Matthew Lewinski (lewinski)
@@ -204,8 +211,6 @@ Symfony2 is the result of the work of many people who made the code better
- Gyula Sallai (salla)
- Inal DJAFAR (inalgnu)
- Christian Gärtner (dagardner)
- - Tom Van Looy (tvlooy)
- - Peter Kruithof (pkruithof)
- Felix Labrecque
- Yaroslav Kiliba
- Sébastien Lavoie (lavoiesl)
@@ -219,18 +224,17 @@ Symfony2 is the result of the work of many people who made the code better
- Philipp Kräutli (pkraeutli)
- Kirill chEbba Chebunin (chebba)
- Greg Thornton (xdissent)
- - Atsuhiro KUBO (iteman)
- Grégoire Paris (greg0ire)
- julien pauli (jpauli)
- Costin Bereveanu (schniper)
- Loïc Chardonnet (gnusat)
- Marek Kalnik (marekkalnik)
+ - Andrew Moore (finewolf)
- Tamas Szijarto
- Pavel Volokitin (pvolok)
- Tobias Naumann (tna)
- Ismael Ambrosi (iambrosi)
- Shein Alexey
- - Issei Murasawa (issei_m)
- hacfi (hifi)
- Joe Lencioni
- Kai
@@ -240,7 +244,6 @@ Symfony2 is the result of the work of many people who made the code better
- Miha Vrhovnik
- Alessandro Desantis
- hubert lecorche (hlecorche)
- - Eugene Leonovich (rybakit)
- Oscar Cubo Medina (ocubom)
- Karel Souffriau
- Christophe L. (christophelau)
@@ -264,6 +267,8 @@ Symfony2 is the result of the work of many people who made the code better
- Tobias Sjösten (tobiassjosten)
- vagrant
- Asier Illarramendi (doup)
+ - Chris Sedlmayr (catchamonkey)
+ - Seb Koelen
- Christoph Mewes (xrstf)
- Vitaliy Tverdokhlib (vitaliytv)
- Dirk Pahl (dirkaholic)
@@ -289,15 +294,17 @@ Symfony2 is the result of the work of many people who made the code better
- Iker Ibarguren (ikerib)
- Ricardo Oliveira (ricardolotr)
- ondrowan
- - Andrew Moore (finewolf)
- Vyacheslav Salakhutdinov (megazoll)
+ - Daniel Wehner
- Evan S Kaufman (evanskaufman)
- mcben
- Maks Slesarenko
+ - mmoreram
- Markus Lanthaler (lanthaler)
- Vicent Soria Durá (vicentgodella)
- Chris Wilkinson (thewilkybarkid)
- Ioan Negulescu
+ - Jakub Škvára (jskvara)
- Andrew Udvare (audvare)
- alexpods
- Erik Trapman (eriktrapman)
@@ -326,11 +333,13 @@ Symfony2 is the result of the work of many people who made the code better
- Pavel Campr (pcampr)
- Geoffrey Tran (geoff)
- Jan Behrens
+ - Sebastian Krebs
+ - Christopher Davis (chrisguitarguy)
- Thomas Lallement (raziel057)
- alcaeus
- vitaliytv
- Markus Bachmann (baachi)
- - Jérémy DERUSSÉ (jderusse)
+ - Sebastian Blum
- aubx
- Ricky Su (ricky)
- Gildas Quéméner (gquemener)
@@ -342,6 +351,7 @@ Symfony2 is the result of the work of many people who made the code better
- Chris Boden (cboden)
- Pierre du Plessis (pierredup)
- Josip Kruslin
+ - Hany el-Kerdany
- Wang Jingyu
- Åsmund Garfors
- Javier López (loalf)
@@ -368,6 +378,7 @@ Symfony2 is the result of the work of many people who made the code better
- Sascha Grossenbacher
- Ben Davies (bendavies)
- Simon Schick (simonsimcity)
+ - redstar504
- Hossein Bukhamsin
- Paweł Wacławczyk (pwc)
- Oleg Zinchenko (cystbear)
@@ -375,10 +386,12 @@ Symfony2 is the result of the work of many people who made the code better
- Johannes Klauss (cloppy)
- Evan Villemez
- fzerorubigd
+ - Thomas Ploch
- Benjamin Grandfond (benjamin)
- Tiago Brito (blackmx)
- Richard van den Brand (ricbra)
- develop
+ - Vincent AUBERT (vincent)
- Tomasz Kowalczyk (thunderer)
- Mark Sonnabaum
- Mathieu Lemoine
@@ -390,8 +403,8 @@ Symfony2 is the result of the work of many people who made the code better
- yclian
- Pascal Helfenstein
- Baldur Rensch (brensch)
+ - Barry vd. Heuvel (barryvdh)
- Alex Xandra Albert Sim
- - Daniel Wehner
- Yuen-Chi Lian
- Besnik Br
- Joshua Nye
@@ -406,8 +419,8 @@ Symfony2 is the result of the work of many people who made the code better
- Josiah (josiah)
- Marek Štípek (maryo)
- John Bohn (jbohn)
- - Jakub Škvára (jskvara)
- Andrew Hilobok (hilobok)
+ - Matthieu Auger (matthieuauger)
- Christian Soronellas (theunic)
- Jérôme Vieilledent (lolautruche)
- Degory Valentine
@@ -418,8 +431,11 @@ Symfony2 is the result of the work of many people who made the code better
- Olivier Maisonneuve (olineuve)
- Francis Turmel (fturmel)
- cgonzalez
+ - Ben
- Jayson Xu (superjavason)
+ - Tobias Nyholm (tobias)
- Jaik Dean (jaikdean)
+ - Harm van Tilborg
- Jan Prieser
- James Michael DuPont
- Tom Klingenberg
@@ -436,6 +452,7 @@ Symfony2 is the result of the work of many people who made the code better
- Maksim Kotlyar (makasim)
- Neil Ferreira
- Dmitry Parnas (parnas)
+ - Emanuele Iannone
- Tony Malzhacker
- Cyril Quintin (cyqui)
- Gerard van Helden (drm)
@@ -454,6 +471,7 @@ Symfony2 is the result of the work of many people who made the code better
- abdul malik ikhsan (samsonasik)
- Sarah Khalil (saro0h)
- Timothée Barray (tyx)
+ - Benjamin Laugueux (yzalis)
- Christian Morgan
- Alexander Miehe (engerim)
- Titouan Galopin (tgalopin)
@@ -468,6 +486,7 @@ Symfony2 is the result of the work of many people who made the code better
- Michael Roterman (wtfzdotnet)
- Arno Geurts
- Adán Lobato (adanlobato)
+ - Matthew Davis (mdavis1982)
- Maks
- Gábor Tóth
- Daniel Cestari
@@ -486,6 +505,7 @@ Symfony2 is the result of the work of many people who made the code better
- Christian Jul Jensen
- The Whole Life to Learn
- Liverbool (liverbool)
+ - Sam Malone
- Phan Thanh Ha (haphan)
- Chris Jones (leek)
- Colin O'Dell (colinodell)
@@ -499,11 +519,13 @@ Symfony2 is the result of the work of many people who made the code better
- fabios
- Sander Coolen (scoolen)
- Nicolas Le Goff (nlegoff)
+ - Ariel Ferrandini (aferrandini)
- Manuele Menozzi
- Anton Babenko (antonbabenko)
- Irmantas Šiupšinskas (irmantas)
- Danilo Silva
- Zachary Tong (polyfractal)
+ - Hryhorii Hrebiniuk
- dantleech
- Tero Alén (tero)
- DerManoMann
@@ -524,11 +546,14 @@ Symfony2 is the result of the work of many people who made the code better
- Jeremy David (jeremy.david)
- Troy McCabe
- Ville Mattila
+ - Boris Vujicic (boris.vujicic)
- Max Beutel
- Catalin Dan
+ - Warnar Boekkooi
- Piotr Antosik (antek88)
- Artem Lopata
- Marcos Quesada (marcos_quesada)
+ - Matthew Vickery (mattvick)
- Dan Finnie
- Ken Marfilla (marfillaster)
- benatespina (benatespina)
@@ -548,7 +573,6 @@ Symfony2 is the result of the work of many people who made the code better
- Daniel Mecke (daniel_mecke)
- Matteo Giachino (matteosister)
- Alex Demchenko (pilot)
- - Vincent AUBERT (vincent)
- Benoit Garret
- DerManoMann
- Asmir Mustafic (goetas)
@@ -573,6 +597,7 @@ Symfony2 is the result of the work of many people who made the code better
- Luc Vieillescazes (iamluc)
- Eduardo García Sanz (coma)
- David de Boer (ddeboer)
+ - Gilles Doge (gido)
- Brooks Boyd
- Roger Webb
- Dmitriy Simushev
@@ -596,7 +621,9 @@ Symfony2 is the result of the work of many people who made the code better
- heccjj
- Alexandre Melard
- Sergey Yuferev
+ - Tobias Stöckler
- Mario Young
+ - Jakub Kulhan
- Mo Di (modi)
- Jeroen van den Enden (stoefke)
- Quique Porta (quiqueporta)
@@ -604,6 +631,7 @@ Symfony2 is the result of the work of many people who made the code better
- ConneXNL
- Aharon Perkel
- Abdul.Mohsen B. A. A
+ - Benoît Burnichon
- Malaney J. Hill
- Cédric Girard (enk_)
- Oriol Mangas Abellan (oriolman)
@@ -640,12 +668,14 @@ Symfony2 is the result of the work of many people who made the code better
- Ilya Biryukov
- Jason Desrosiers
- m.chwedziak
+ - Endre Fejes
- Lance McNearney
- Giorgio Premi
- caponica
- Matt Daum (daum)
- Alberto Pirovano (geezmo)
- Pete Mitchell (peterjmit)
+ - Tom Corrigan (tomcorrigan)
- Martin Pärtel
- Evgeniy (ewgraf)
- Patrick Daley (padrig)
@@ -653,9 +683,11 @@ Symfony2 is the result of the work of many people who made the code better
- Max Summe
- WedgeSama
- Felds Liscia
+ - Maxime Veber (nek-)
- Tadcka
- Beth Binkovitz
- Romain Geissler
+ - Benjamin Cremer (bcremer)
- Marcus Stöhr (dafish)
- Emmanuel Vella (emmanuel.vella)
- Carsten Nielsen (phreaknerd)
@@ -665,8 +697,8 @@ Symfony2 is the result of the work of many people who made the code better
- Adrien Samson (adriensamson)
- Samuel Gordalina (gordalina)
- Max Romanovsky (maxromanovsky)
+ - Rafał Muszyński (rafmus90)
- Timothy Anido (xanido)
- - Sebastian Krebs
- Rick Prent
- Martin Eckhardt
- Jon Gotlin (jongotlin)
@@ -689,7 +721,6 @@ Symfony2 is the result of the work of many people who made the code better
- Sebastian Ionescu
- Thomas Ploch
- Simon Neidhold
- - Seb Koelen
- Kevin Dew
- James Cowgill
- Jeremy Livingston (jeremylivingston)
@@ -707,6 +738,7 @@ Symfony2 is the result of the work of many people who made the code better
- Hoffmann András
- Olivier
- pscheit
+ - moldcraft
- Ramon Kleiss (akathos)
- Nicolas Badey (nico-b)
- Shane Preece (shane)
@@ -717,7 +749,6 @@ Symfony2 is the result of the work of many people who made the code better
- Gunnar Lium (gunnarlium)
- Tiago Garcia (tiagojsag)
- Bouke Haarsma
- - Harm van Tilborg
- Martin Eckhardt
- Denis Zunke
- Jonathan Poston
@@ -748,12 +779,15 @@ Symfony2 is the result of the work of many people who made the code better
- Michal Gebauer
- Gleb Sidora
- David Stone
+ - Javier Spagnoletti (phansys)
- Pablo Maria Martelletti (pmartelletti)
+ - Yassine Guedidi (yguedidi)
- Luis Muñoz
- Andreas
- Strate
- Thomas Chmielowiec
- Andrey Ryaguzov
+ - Manatsawin Hanmongkolchai
- Gunther Konig
- František Bereň
- Christoph Nissle (derstoffel)
@@ -766,11 +800,13 @@ Symfony2 is the result of the work of many people who made the code better
- Grayson Koonce (breerly)
- Benjamin Zikarsky (bzikarsky)
- Matt Robinson (inanimatt)
+ - Karim Cassam Chenaï (ka)
- Nicolas Bastien (nicolas_bastien)
- Andy Stanberry
- Luiz “Felds” Liscia
- Thomas Rothe
- alefranz
+ - avi123
- alsar
- Mike Meier
- Warwick
@@ -798,10 +834,10 @@ Symfony2 is the result of the work of many people who made the code better
- Daan van Renterghem
- Bram Van der Sype (brammm)
- Julien Moulin (lizjulien)
- - Matthieu Auger (matthieuauger)
- Kevin Decherf
- dened
- Sam Ward
+ - Walther Lalk
- devel
- Trevor Suarez
- gedrox
@@ -858,6 +894,7 @@ Symfony2 is the result of the work of many people who made the code better
- Klaas Naaijkens
- Rafał
- Adria Lopez (adlpz)
+ - Rosio (ben-rosio)
- Masao Maeda (brtriver)
- Darius Leskauskas (darles)
- Dave Hulbert (dave1010)
@@ -871,7 +908,6 @@ Symfony2 is the result of the work of many people who made the code better
- Ian Jenkins (jenkoian)
- Jorge Martin (jorgemartind)
- Kevin Herrera (kherge)
- - Matthew Davis (mdavis1982)
- Muriel (metalmumu)
- Michaël Perrin (michael.perrin)
- Pablo Monterde Perez (plebs)
@@ -904,6 +940,7 @@ Symfony2 is the result of the work of many people who made the code better
- goohib
- Xavier HAUSHERR
- Cas
+ - Maxime Douailin
- Myke79
- Brian Debuire
- Sylvain Lorinet
@@ -928,6 +965,7 @@ Symfony2 is the result of the work of many people who made the code better
- cmfcmf
- Drew Butler
- Steve Müller
+ - Andras Ratz
- andreabreu98
- Thomas Schulz
- Michael Schneider
@@ -939,12 +977,12 @@ Symfony2 is the result of the work of many people who made the code better
- Ondrej Slinták
- vlechemin
- Brian Corrigan
+ - Brian Freytag
- Skorney
- mieszko4
- datibbaw
- Markus Staab
- Pierre-Louis LAUNAY
- - Thomas Ploch
- djama
- Eduardo Conceição
- Jon Cave
@@ -960,6 +998,7 @@ Symfony2 is the result of the work of many people who made the code better
- Sergiy Sokolenko
- dinitrol
- Penny Leach
+ - g123456789l
- oscartv
- DanSync
- Peter Zwosta
@@ -982,7 +1021,6 @@ Symfony2 is the result of the work of many people who made the code better
- Daniel Basten (axhm3a)
- Bill Hance (billhance)
- Bernd Matzner (bmatzner)
- - Chris Sedlmayr (catchamonkey)
- Choong Wei Tjeng (choonge)
- Kousuke Ebihara (co3k)
- Loïc Vernet (coil)
@@ -991,6 +1029,7 @@ Symfony2 is the result of the work of many people who made the code better
- Damon Jones (damon__jones)
- Daniel Londero (dlondero)
- Adel ELHAIBA (eadel)
+ - Damián Nohales (eagleoneraptor)
- Elliot Anderson (elliot)
- Fabien D. (fabd)
- Sorin Gitlan (forapathy)
@@ -1006,11 +1045,14 @@ Symfony2 is the result of the work of many people who made the code better
- Jorge Maiden (jorgemaiden)
- Justin Rainbow (jrainbow)
- JuntaTom (juntatom)
+ - Johnson Page (jwpage)
- Sébastien Armand (khepin)
- Krzysztof Menżyk (krymen)
- samuel laulhau (lalop)
- Laurent Bachelier (laurentb)
+ - Jérôme Parmentier (lctrs)
- Matthieu Moquet (mattketmo)
+ - Morgan Auchede (mauchede)
- Moritz Borgmann (mborgmann)
- Matt Drollette (mdrollette)
- Adam Monsen (meonkeys)
@@ -1053,8 +1095,10 @@ Symfony2 is the result of the work of many people who made the code better
- Philipp Scheit
- max
- Mohamed Karnichi (amiral)
+ - Jeroen De Dauw (jeroendedauw)
- Muharrem Demirci (mdemirci)
- Evgeny Z (meze)
+ - Michiel Boeckaert (milio)
- Nicolas de Marqué (nicola)
- Kevin (oxfouzer)
- Pierre Geyer (ptheg)
diff --git a/src/Symfony/Bridge/Swiftmailer/composer.json b/src/Symfony/Bridge/Swiftmailer/composer.json
index 8890da191a..f6d0024b34 100644
--- a/src/Symfony/Bridge/Swiftmailer/composer.json
+++ b/src/Symfony/Bridge/Swiftmailer/composer.json
@@ -17,7 +17,7 @@
],
"require": {
"php": ">=5.3.3",
- "swiftmailer/swiftmailer": ">=4.2.0,<5.1-dev"
+ "swiftmailer/swiftmailer": ">=4.2.0,<6.0-dev"
},
"suggest": {
"symfony/http-kernel": ""
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php
index 3535167910..bbb701ba4a 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php
@@ -101,15 +101,34 @@ EOF
->locateResource(sprintf('@FrameworkBundle/Resources/config/router_%s.php', $env))
;
+ if (!file_exists($router)) {
+ $output->writeln(sprintf('The given router script "%s" does not exist ', $router));
+
+ return 1;
+ }
+
+ $router = realpath($router);
+
$output->writeln(sprintf("Server running on http://%s \n", $input->getArgument('address')));
$builder = new ProcessBuilder(array(PHP_BINARY, '-S', $input->getArgument('address'), $router));
$builder->setWorkingDirectory($documentRoot);
$builder->setTimeout(null);
- $builder->getProcess()->run(function ($type, $buffer) use ($output) {
+ $process = $builder->getProcess();
+ $process->run(function ($type, $buffer) use ($output) {
if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
$output->write($buffer);
}
});
+
+ if (!$process->isSuccessful()) {
+ $output->writeln('Built-in server terminated unexpectedly ');
+
+ if (OutputInterface::VERBOSITY_VERBOSE > $output->getVerbosity()) {
+ $output->writeln('Run the command again with -v option for more details ');
+ }
+ }
+
+ return $process->getExitCode();
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget_compound.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget_compound.html.php
index 45a821032e..7a4f7cd51f 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget_compound.html.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget_compound.html.php
@@ -1,10 +1,6 @@
block($form, 'widget_container_attributes') ?>>
parent && $errors): ?>
-
-
- errors($form) ?>
-
-
+ errors($form) ?>
block($form, 'form_rows') ?>
rest($form) ?>
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_widget_compound.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_widget_compound.html.php
index a4878e57db..20b9668aa4 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_widget_compound.html.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_widget_compound.html.php
@@ -1,6 +1,10 @@
block($form, 'widget_container_attributes') ?>>
- parent): ?>
- errors($form) ?>
+ parent && $errors): ?>
+
+
+ errors($form) ?>
+
+
block($form, 'form_rows') ?>
rest($form) ?>
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.xml b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.xml
index 1819e481c8..69f934e53f 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.xml
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.xml
@@ -24,14 +24,6 @@
web_profiler.controller.profiler:infoAction
-
- web_profiler.controller.profiler:importAction
-
-
-
- web_profiler.controller.profiler:exportAction
-
-
web_profiler.controller.profiler:phpinfoAction
diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php
index ebab4ad669..4f7e5c6af7 100644
--- a/src/Symfony/Component/Console/Command/Command.php
+++ b/src/Symfony/Component/Console/Command/Command.php
@@ -491,11 +491,11 @@ class Command
$placeholders = array(
'%command.name%',
- '%command.full_name%'
+ '%command.full_name%',
);
$replacements = array(
$name,
- $_SERVER['PHP_SELF'].' '.$name
+ $_SERVER['PHP_SELF'].' '.$name,
);
return str_replace($placeholders, $replacements, $this->getHelp());
@@ -504,7 +504,7 @@ class Command
/**
* Sets the aliases for the command.
*
- * @param array $aliases An array of aliases for the command
+ * @param string[] $aliases An array of aliases for the command
*
* @return Command The current instance
*
@@ -514,6 +514,10 @@ class Command
*/
public function setAliases($aliases)
{
+ if (!is_array($aliases) && !$aliases instanceof \Traversable) {
+ throw new \InvalidArgumentException('$aliases must be an array or an instance of \Traversable');
+ }
+
foreach ($aliases as $alias) {
$this->validateName($alias);
}
diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
index 4f399a59fc..4f042689ce 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
@@ -56,6 +56,10 @@ class YamlFileLoader extends FileLoader
// parameters
if (isset($content['parameters'])) {
+ if (!is_array($content['parameters'])) {
+ throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in %s. Check your YAML syntax.', $file));
+ }
+
foreach ($content['parameters'] as $key => $value) {
$this->container->setParameter($key, $this->resolveServices($value));
}
@@ -93,7 +97,15 @@ class YamlFileLoader extends FileLoader
return;
}
+ if (!is_array($content['imports'])) {
+ throw new InvalidArgumentException(sprintf('The "imports" key should contain an array in %s. Check your YAML syntax.', $file));
+ }
+
foreach ($content['imports'] as $import) {
+ if (!is_array($import)) {
+ throw new InvalidArgumentException(sprintf('The values in the "imports" key should be arrays in %s. Check your YAML syntax.', $file));
+ }
+
$this->setCurrentDir(dirname($file));
$this->import($import['resource'], null, isset($import['ignore_errors']) ? (bool) $import['ignore_errors'] : false, $file);
}
@@ -111,6 +123,10 @@ class YamlFileLoader extends FileLoader
return;
}
+ if (!is_array($content['services'])) {
+ throw new InvalidArgumentException(sprintf('The "services" key should contain an array in %s. Check your YAML syntax.', $file));
+ }
+
foreach ($content['services'] as $id => $service) {
$this->parseDefinition($id, $service, $file);
}
@@ -131,7 +147,13 @@ class YamlFileLoader extends FileLoader
$this->container->setAlias($id, substr($service, 1));
return;
- } elseif (isset($service['alias'])) {
+ }
+
+ if (!is_array($service)) {
+ throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" but %s found for service "%s" in %s. Check your YAML syntax.', gettype($service), $id, $file));
+ }
+
+ if (isset($service['alias'])) {
$public = !array_key_exists('public', $service) || (bool) $service['public'];
$this->container->setAlias($id, new Alias($service['alias'], $public));
@@ -205,6 +227,10 @@ class YamlFileLoader extends FileLoader
}
if (isset($service['calls'])) {
+ if (!is_array($service['calls'])) {
+ throw new InvalidArgumentException(sprintf('Parameter "calls" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
+ }
+
foreach ($service['calls'] as $call) {
$args = isset($call[1]) ? $this->resolveServices($call[1]) : array();
$definition->addMethodCall($call[0], $args);
@@ -213,10 +239,14 @@ class YamlFileLoader extends FileLoader
if (isset($service['tags'])) {
if (!is_array($service['tags'])) {
- throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s.', $id, $file));
+ throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
}
foreach ($service['tags'] as $tag) {
+ if (!is_array($tag)) {
+ throw new InvalidArgumentException(sprintf('A "tags" entry must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
+ }
+
if (!isset($tag['name'])) {
throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in %s.', $id, $file));
}
@@ -226,7 +256,7 @@ class YamlFileLoader extends FileLoader
foreach ($tag as $attribute => $value) {
if (!is_scalar($value) && null !== $value) {
- throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in %s.', $id, $name, $attribute, $file));
+ throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in %s. Check your YAML syntax.', $id, $name, $attribute, $file));
}
}
@@ -280,7 +310,7 @@ class YamlFileLoader extends FileLoader
}
if (!is_array($content)) {
- throw new InvalidArgumentException(sprintf('The service file "%s" is not valid.', $file));
+ throw new InvalidArgumentException(sprintf('The service file "%s" is not valid. It should contain an array. Check your YAML syntax.', $file));
}
foreach (array_keys($content) as $namespace) {
@@ -306,9 +336,9 @@ class YamlFileLoader extends FileLoader
/**
* Resolves services.
*
- * @param string $value
+ * @param string|array $value
*
- * @return Reference
+ * @return array|string|Reference
*/
private function resolveServices($value)
{
diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd
index f7b578767b..034880fc88 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd
+++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd
@@ -156,7 +156,6 @@
-
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services2.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services2.xml
index 43cf86c547..f1ac14dd5d 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services2.xml
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services2.xml
@@ -23,7 +23,6 @@
bar
-
value
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_calls.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_calls.yml
new file mode 100644
index 0000000000..3f34b07eb0
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_calls.yml
@@ -0,0 +1,4 @@
+services:
+ method_call1:
+ class: FooClass
+ calls: foo
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_import.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_import.yml
new file mode 100644
index 0000000000..0765dc8dd0
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_import.yml
@@ -0,0 +1,2 @@
+imports:
+ - foo.yml
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_imports.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_imports.yml
new file mode 100644
index 0000000000..1ce9d57c6c
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_imports.yml
@@ -0,0 +1,2 @@
+imports:
+ foo:bar
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_parameters.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_parameters.yml
new file mode 100644
index 0000000000..bbd13ac0b3
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_parameters.yml
@@ -0,0 +1,2 @@
+parameters:
+ foo:bar
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_service.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_service.yml
new file mode 100644
index 0000000000..811af3c0ef
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_service.yml
@@ -0,0 +1,2 @@
+services:
+ foo: bar
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_services.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_services.yml
new file mode 100644
index 0000000000..cfbf17ce5f
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_services.yml
@@ -0,0 +1 @@
+services: foo
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag4.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag4.yml
new file mode 100644
index 0000000000..e8e99395b1
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag4.yml
@@ -0,0 +1,6 @@
+services:
+ foo_service:
+ class: FooClass
+ tags:
+ # tag is not an array
+ - foo
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
index b1f32e283e..f166f742d8 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
@@ -110,7 +110,6 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
'a string',
array('foo', 'bar'),
),
- 'foo_bar' => new Reference('foo_bar'),
'mixedcase' => array('MixedCaseKey' => 'value'),
'constant' => PHP_EOL,
);
@@ -147,7 +146,6 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
'a string',
array('foo', 'bar'),
),
- 'foo_bar' => new Reference('foo_bar'),
'mixedcase' => array('MixedCaseKey' => 'value'),
'constant' => PHP_EOL,
'bar' => '%foo%',
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
index 37abca7483..83a7ea4f66 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
@@ -13,7 +13,6 @@ namespace Symfony\Component\DependencyInjection\Tests\Loader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
-use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
@@ -70,6 +69,29 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
}
}
+ /**
+ * @dataProvider provideInvalidFiles
+ * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
+ */
+ public function testLoadInvalidFile($file)
+ {
+ $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
+
+ $loader->load($file.'.yml');
+ }
+
+ public function provideInvalidFiles()
+ {
+ return array(
+ array('bad_parameters'),
+ array('bad_imports'),
+ array('bad_import'),
+ array('bad_services'),
+ array('bad_service'),
+ array('bad_calls'),
+ );
+ }
+
public function testLoadParameters()
{
$container = new ContainerBuilder();
@@ -169,7 +191,7 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
}
- public function testNonArrayTagThrowsException()
+ public function testNonArrayTagsThrowsException()
{
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
try {
@@ -181,6 +203,16 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
}
}
+ /**
+ * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
+ * @expectedExceptionMessage A "tags" entry must be an array for service
+ */
+ public function testNonArrayTagThrowsException()
+ {
+ $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
+ $loader->load('badtag4.yml');
+ }
+
public function testTagWithoutNameThrowsException()
{
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
diff --git a/src/Symfony/Component/Form/ButtonBuilder.php b/src/Symfony/Component/Form/ButtonBuilder.php
index 69730d9d2e..806510d586 100644
--- a/src/Symfony/Component/Form/ButtonBuilder.php
+++ b/src/Symfony/Component/Form/ButtonBuilder.php
@@ -417,7 +417,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
*
* This method should not be invoked.
*
- * @param array $data
+ * @param mixed $data
*
* @throws BadMethodCallException
*/
diff --git a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php
index 662443ed72..1cb1baf025 100644
--- a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php
+++ b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php
@@ -70,9 +70,17 @@ class ViolationPath implements \IteratorAggregate, PropertyPathInterface
break;
}
- $this->elements[] = $elements[$i];
- $this->isIndex[] = true;
- $this->mapsForm[] = true;
+ // All the following index items (regardless if .children is
+ // explicitly used) are children and grand-children
+ for (; $i < $l && $path->isIndex($i); ++$i) {
+ $this->elements[] = $elements[$i];
+ $this->isIndex[] = true;
+ $this->mapsForm[] = true;
+ }
+
+ // Rewind the pointer as the last element above didn't match
+ // (even if the pointer was moved forward)
+ --$i;
} elseif ('data' === $elements[$i] && $path->isProperty($i)) {
// Skip element "data"
++$i;
diff --git a/src/Symfony/Component/Form/FormBuilder.php b/src/Symfony/Component/Form/FormBuilder.php
index 434afa861a..e73c03fd7a 100644
--- a/src/Symfony/Component/Form/FormBuilder.php
+++ b/src/Symfony/Component/Form/FormBuilder.php
@@ -242,7 +242,7 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
}
- return new \ArrayIterator($this->children);
+ return new \ArrayIterator($this->all());
}
/**
diff --git a/src/Symfony/Component/Form/FormConfigBuilderInterface.php b/src/Symfony/Component/Form/FormConfigBuilderInterface.php
index da2da0b668..50793a2c49 100644
--- a/src/Symfony/Component/Form/FormConfigBuilderInterface.php
+++ b/src/Symfony/Component/Form/FormConfigBuilderInterface.php
@@ -212,7 +212,7 @@ interface FormConfigBuilderInterface extends FormConfigInterface
/**
* Sets the initial data of the form.
*
- * @param array $data The data of the form in application format.
+ * @param mixed $data The data of the form in application format.
*
* @return self The configuration object.
*/
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php
index a84d5b951a..315b565062 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php
@@ -708,14 +708,14 @@ class ViolationMapperTest extends \PHPUnit_Framework_TestCase
array(self::LEVEL_2, 'address', '[address]', 'street', '[office][street]', 'data[address][office][street].prop'),
// Edge cases which must not occur
- array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'children[address][street]'),
- array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'children[address][street].prop'),
- array(self::LEVEL_1, 'address', 'address', 'street', '[street]', 'children[address][street]'),
- array(self::LEVEL_1, 'address', 'address', 'street', '[street]', 'children[address][street].prop'),
- array(self::LEVEL_1, 'address', '[address]', 'street', 'street', 'children[address][street]'),
- array(self::LEVEL_1, 'address', '[address]', 'street', 'street', 'children[address][street].prop'),
- array(self::LEVEL_1, 'address', '[address]', 'street', '[street]', 'children[address][street]'),
- array(self::LEVEL_1, 'address', '[address]', 'street', '[street]', 'children[address][street].prop'),
+ array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address][street]'),
+ array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address][street].prop'),
+ array(self::LEVEL_2, 'address', 'address', 'street', '[street]', 'children[address][street]'),
+ array(self::LEVEL_2, 'address', 'address', 'street', '[street]', 'children[address][street].prop'),
+ array(self::LEVEL_2, 'address', '[address]', 'street', 'street', 'children[address][street]'),
+ array(self::LEVEL_2, 'address', '[address]', 'street', 'street', 'children[address][street].prop'),
+ array(self::LEVEL_2, 'address', '[address]', 'street', '[street]', 'children[address][street]'),
+ array(self::LEVEL_2, 'address', '[address]', 'street', '[street]', 'children[address][street].prop'),
array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'children[person].children[address].children[street]'),
array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'children[person].children[address].data.street'),
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationPathTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationPathTest.php
index 2dab4ffad7..fdbf747f20 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationPathTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationPathTest.php
@@ -30,7 +30,8 @@ class ViolationPathTest extends \PHPUnit_Framework_TestCase
)),
array('children[address][street]', array(
array('address', true, true),
- ), 'children[address]'),
+ array('street', true, true),
+ ), 'children[address].children[street]'),
array('children[address].data', array(
array('address', true, true),
), 'children[address]'),
diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php
index 06d98c8580..98c747306c 100644
--- a/src/Symfony/Component/HttpFoundation/Request.php
+++ b/src/Symfony/Component/HttpFoundation/Request.php
@@ -1033,9 +1033,9 @@ class Request
}
/**
- * Returns the requested URI.
+ * Returns the requested URI (path and query string).
*
- * @return string The raw URI (i.e. not urldecoded)
+ * @return string The raw URI (i.e. not URI decoded)
*
* @api
*/
@@ -1062,9 +1062,9 @@ class Request
}
/**
- * Generates a normalized URI for the Request.
+ * Generates a normalized URI (URL) for the Request.
*
- * @return string A normalized URI for the Request
+ * @return string A normalized URI (URL) for the Request
*
* @see getQueryString()
*
diff --git a/src/Symfony/Component/HttpKernel/HttpCache/Esi.php b/src/Symfony/Component/HttpKernel/HttpCache/Esi.php
index 3ba9ecd818..9dd99d64cc 100644
--- a/src/Symfony/Component/HttpKernel/HttpCache/Esi.php
+++ b/src/Symfony/Component/HttpKernel/HttpCache/Esi.php
@@ -236,9 +236,9 @@ class Esi
throw new \RuntimeException('Unable to process an ESI tag without a "src" attribute.');
}
- return sprintf('esi->handle($this, \'%s\', \'%s\', %s) ?>'."\n",
- $options['src'],
- isset($options['alt']) ? $options['alt'] : null,
+ return sprintf('esi->handle($this, %s, %s, %s) ?>'."\n",
+ var_export($options['src'], true),
+ var_export(isset($options['alt']) ? $options['alt'] : '', true),
isset($options['onerror']) && 'continue' == $options['onerror'] ? 'true' : 'false'
);
}
diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/EsiTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/EsiTest.php
index c509706389..ad400c69ae 100644
--- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/EsiTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/EsiTest.php
@@ -103,6 +103,11 @@ class EsiTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('foo esi->handle($this, \'...\', \'alt\', true) ?>'."\n", $response->getContent());
$this->assertEquals('ESI', $response->headers->get('x-body-eval'));
+ $response = new Response('foo ');
+ $esi->process($request, $response);
+
+ $this->assertEquals("foo esi->handle(\$this, 'foo\\'', 'bar\\'', true) ?>"."\n", $response->getContent());
+
$response = new Response('foo ');
$esi->process($request, $response);
diff --git a/src/Symfony/Component/Intl/Exception/MissingResourceException.php b/src/Symfony/Component/Intl/Exception/MissingResourceException.php
new file mode 100644
index 0000000000..e2eb3f210e
--- /dev/null
+++ b/src/Symfony/Component/Intl/Exception/MissingResourceException.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Exception;
+
+/**
+ * Thrown when an invalid entry of a resource bundle was requested.
+ *
+ * @author Bernhard Schussek
+ */
+class MissingResourceException extends RuntimeException
+{
+}
diff --git a/src/Symfony/Component/Intl/Exception/ResourceBundleNotFoundException.php b/src/Symfony/Component/Intl/Exception/ResourceBundleNotFoundException.php
new file mode 100644
index 0000000000..59da5ec0d5
--- /dev/null
+++ b/src/Symfony/Component/Intl/Exception/ResourceBundleNotFoundException.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Exception;
+
+/**
+ * @author Bernhard Schussek
+ */
+class ResourceBundleNotFoundException extends RuntimeException
+{
+}
diff --git a/src/Symfony/Component/Intl/Exception/UnexpectedTypeException.php b/src/Symfony/Component/Intl/Exception/UnexpectedTypeException.php
new file mode 100644
index 0000000000..645d4926da
--- /dev/null
+++ b/src/Symfony/Component/Intl/Exception/UnexpectedTypeException.php
@@ -0,0 +1,25 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl\Exception;
+
+/**
+ * Thrown when a method argument had an unexpected type.
+ *
+ * @author Bernhard Schussek
+ */
+class UnexpectedTypeException extends InvalidArgumentException
+{
+ public function __construct($value, $expectedType)
+ {
+ parent::__construct(sprintf('Expected argument of type "%s", "%s" given', $expectedType, is_object($value) ? get_class($value) : gettype($value)));
+ }
+}
diff --git a/src/Symfony/Component/Intl/Locale.php b/src/Symfony/Component/Intl/Locale.php
new file mode 100644
index 0000000000..d7e0d33e84
--- /dev/null
+++ b/src/Symfony/Component/Intl/Locale.php
@@ -0,0 +1,48 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Intl;
+
+/**
+ * Provides access to locale-related data.
+ *
+ * @author Bernhard Schussek
+ *
+ * @internal
+ */
+final class Locale extends \Locale
+{
+ /**
+ * Returns the fallback locale for a given locale, if any
+ *
+ * @param string $locale The ICU locale code to find the fallback for.
+ *
+ * @return string|null The ICU locale code of the fallback locale, or null
+ * if no fallback exists
+ */
+ public static function getFallback($locale)
+ {
+ if (false === $pos = strrpos($locale, '_')) {
+ if ('root' === $locale) {
+ return;
+ }
+
+ return 'root';
+ }
+
+ return substr($locale, 0, $pos);
+ }
+
+ /**
+ * This class must not be instantiated.
+ */
+ private function __construct() {}
+}
diff --git a/src/Symfony/Component/Intl/ResourceBundle/AbstractBundle.php b/src/Symfony/Component/Intl/ResourceBundle/AbstractBundle.php
index 1aaadee20b..53be149d8d 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/AbstractBundle.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/AbstractBundle.php
@@ -17,6 +17,8 @@ use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface
* Base class for {@link ResourceBundleInterface} implementations.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
abstract class AbstractBundle implements ResourceBundleInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompiler.php b/src/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompiler.php
index 174aa179f4..8249f66d0d 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompiler.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompiler.php
@@ -17,6 +17,8 @@ use Symfony\Component\Intl\Exception\RuntimeException;
* Compiles .txt resource bundles to binary .res files.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
class BundleCompiler implements BundleCompilerInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompilerInterface.php b/src/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompilerInterface.php
index 6184ea3eb1..d9cde44d82 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompilerInterface.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Compiler/BundleCompilerInterface.php
@@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle\Compiler;
* Compiles a resource bundle.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
interface BundleCompilerInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/CurrencyBundle.php b/src/Symfony/Component/Intl/ResourceBundle/CurrencyBundle.php
index eec6710c4d..bcd486e996 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/CurrencyBundle.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/CurrencyBundle.php
@@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle;
* Default implementation of {@link CurrencyBundleInterface}.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
class CurrencyBundle extends AbstractBundle implements CurrencyBundleInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/LanguageBundle.php b/src/Symfony/Component/Intl/ResourceBundle/LanguageBundle.php
index b09381fa34..060b214ae8 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/LanguageBundle.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/LanguageBundle.php
@@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle;
* Default implementation of {@link LanguageBundleInterface}.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
class LanguageBundle extends AbstractBundle implements LanguageBundleInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/LocaleBundle.php b/src/Symfony/Component/Intl/ResourceBundle/LocaleBundle.php
index 0a7ed806b4..b1d72a3539 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/LocaleBundle.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/LocaleBundle.php
@@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle;
* Default implementation of {@link LocaleBundleInterface}.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
class LocaleBundle extends AbstractBundle implements LocaleBundleInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Reader/AbstractBundleReader.php b/src/Symfony/Component/Intl/ResourceBundle/Reader/AbstractBundleReader.php
deleted file mode 100644
index c30693ac57..0000000000
--- a/src/Symfony/Component/Intl/ResourceBundle/Reader/AbstractBundleReader.php
+++ /dev/null
@@ -1,42 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Intl\ResourceBundle\Reader;
-
-/**
- * Base class for {@link BundleReaderInterface} implementations.
- *
- * @author Bernhard Schussek
- */
-abstract class AbstractBundleReader implements BundleReaderInterface
-{
- /**
- * {@inheritdoc}
- */
- public function getLocales($path)
- {
- $extension = '.' . $this->getFileExtension();
- $locales = glob($path . '/*' . $extension);
-
- // Remove file extension and sort
- array_walk($locales, function (&$locale) use ($extension) { $locale = basename($locale, $extension); });
- sort($locales);
-
- return $locales;
- }
-
- /**
- * Returns the extension of locale files in this bundle.
- *
- * @return string The file extension (without leading dot).
- */
- abstract protected function getFileExtension();
-}
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Reader/BinaryBundleReader.php b/src/Symfony/Component/Intl/ResourceBundle/Reader/BinaryBundleReader.php
index 16ea1dd3aa..77b86aee42 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Reader/BinaryBundleReader.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Reader/BinaryBundleReader.php
@@ -11,15 +11,17 @@
namespace Symfony\Component\Intl\ResourceBundle\Reader;
-use Symfony\Component\Intl\Exception\RuntimeException;
+use Symfony\Component\Intl\Exception\ResourceBundleNotFoundException;
use Symfony\Component\Intl\ResourceBundle\Util\ArrayAccessibleResourceBundle;
/**
* Reads binary .res resource bundles.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
-class BinaryBundleReader extends AbstractBundleReader implements BundleReaderInterface
+class BinaryBundleReader implements BundleReaderInterface
{
/**
* {@inheritdoc}
@@ -29,28 +31,39 @@ class BinaryBundleReader extends AbstractBundleReader implements BundleReaderInt
// Point for future extension: Modify this class so that it works also
// if the \ResourceBundle class is not available.
try {
- $bundle = new \ResourceBundle($locale, $path);
+ // Never enable fallback. We want to know if a bundle cannot be found
+ $bundle = new \ResourceBundle($locale, $path, false);
} catch (\Exception $e) {
// HHVM compatibility: constructor throws on invalid resource
$bundle = null;
}
+ // The bundle is NULL if the path does not look like a resource bundle
+ // (i.e. contain a bunch of *.res files)
if (null === $bundle) {
- throw new RuntimeException(sprintf(
- 'Could not load the resource bundle "%s/%s.res".',
+ throw new ResourceBundleNotFoundException(sprintf(
+ 'The resource bundle "%s/%s.res" could not be found.',
$path,
$locale
));
}
+ // Other possible errors are U_USING_FALLBACK_WARNING and U_ZERO_ERROR,
+ // which are OK for us.
return new ArrayAccessibleResourceBundle($bundle);
}
/**
* {@inheritdoc}
*/
- protected function getFileExtension()
+ public function getLocales($path)
{
- return 'res';
+ $locales = glob($path.'/*.res');
+
+ // Remove file extension and sort
+ array_walk($locales, function (&$locale) { $locale = basename($locale, '.res'); });
+ sort($locales);
+
+ return $locales;
}
}
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Reader/BufferedBundleReader.php b/src/Symfony/Component/Intl/ResourceBundle/Reader/BufferedBundleReader.php
index f005eeb4ac..19f6d672af 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Reader/BufferedBundleReader.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Reader/BufferedBundleReader.php
@@ -15,6 +15,8 @@ use Symfony\Component\Intl\ResourceBundle\Util\RingBuffer;
/**
* @author Bernhard Schussek
+ *
+ * @internal
*/
class BufferedBundleReader implements BundleReaderInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Reader/BundleReaderInterface.php b/src/Symfony/Component/Intl/ResourceBundle/Reader/BundleReaderInterface.php
index bc485cd526..c11d90e046 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Reader/BundleReaderInterface.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Reader/BundleReaderInterface.php
@@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle\Reader;
* Reads resource bundle files.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
interface BundleReaderInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Reader/PhpBundleReader.php b/src/Symfony/Component/Intl/ResourceBundle/Reader/PhpBundleReader.php
index 663bcc9d78..2082916827 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Reader/PhpBundleReader.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Reader/PhpBundleReader.php
@@ -11,29 +11,27 @@
namespace Symfony\Component\Intl\ResourceBundle\Reader;
-use Symfony\Component\Intl\Exception\InvalidArgumentException;
+use Symfony\Component\Intl\Exception\ResourceBundleNotFoundException;
use Symfony\Component\Intl\Exception\RuntimeException;
/**
* Reads .php resource bundles.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
-class PhpBundleReader extends AbstractBundleReader implements BundleReaderInterface
+class PhpBundleReader implements BundleReaderInterface
{
/**
* {@inheritdoc}
*/
public function read($path, $locale)
{
- if ('en' !== $locale) {
- throw new InvalidArgumentException('Only the locale "en" is supported.');
- }
-
- $fileName = $path . '/' . $locale . '.php';
+ $fileName = $path.'/'.$locale.'.php';
if (!file_exists($fileName)) {
- throw new RuntimeException(sprintf(
+ throw new ResourceBundleNotFoundException(sprintf(
'The resource bundle "%s/%s.php" does not exist.',
$path,
$locale
@@ -54,8 +52,14 @@ class PhpBundleReader extends AbstractBundleReader implements BundleReaderInterf
/**
* {@inheritdoc}
*/
- protected function getFileExtension()
+ public function getLocales($path)
{
- return 'php';
+ $locales = glob($path.'/*.php');
+
+ // Remove file extension and sort
+ array_walk($locales, function (&$locale) { $locale = basename($locale, '.php'); });
+ sort($locales);
+
+ return $locales;
}
}
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReader.php b/src/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReader.php
index 4b71c9bcb2..01ec26190a 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReader.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReader.php
@@ -11,14 +11,20 @@
namespace Symfony\Component\Intl\ResourceBundle\Reader;
+use Symfony\Component\Intl\Exception\MissingResourceException;
+use Symfony\Component\Intl\Exception\OutOfBoundsException;
+use Symfony\Component\Intl\Exception\ResourceBundleNotFoundException;
+use Symfony\Component\Intl\Locale;
use Symfony\Component\Intl\ResourceBundle\Util\RecursiveArrayAccess;
/**
- * A structured reader wrapping an existing resource bundle reader.
+ * Default implementation of {@link StructuredBundleReaderInterface}.
*
* @author Bernhard Schussek
*
* @see StructuredResourceBundleBundleReaderInterface
+ *
+ * @internal
*/
class StructuredBundleReader implements StructuredBundleReaderInterface
{
@@ -27,6 +33,13 @@ class StructuredBundleReader implements StructuredBundleReaderInterface
*/
private $reader;
+ /**
+ * A mapping of locale aliases to locales
+ *
+ * @var array
+ */
+ private $localeAliases = array();
+
/**
* Creates an entry reader based on the given resource bundle reader.
*
@@ -37,6 +50,21 @@ class StructuredBundleReader implements StructuredBundleReaderInterface
$this->reader = $reader;
}
+ /**
+ * Stores a mapping of locale aliases to locales.
+ *
+ * This mapping is used when reading entries and merging them with their
+ * fallback locales. If an entry is read for a locale alias (e.g. "mo")
+ * that points to a locale with a fallback locale ("ro_MD"), the reader
+ * can continue at the correct fallback locale ("ro").
+ *
+ * @param array $localeAliases A mapping of locale aliases to locales
+ */
+ public function setLocaleAliases($localeAliases)
+ {
+ $this->localeAliases = $localeAliases;
+ }
+
/**
* {@inheritdoc}
*/
@@ -48,66 +76,117 @@ class StructuredBundleReader implements StructuredBundleReaderInterface
/**
* {@inheritdoc}
*/
- public function getLocales($path)
+ public function readEntry($path, $locale, array $indices, $fallback = true)
{
- return $this->reader->getLocales($path);
+ $entry = null;
+ $isMultiValued = false;
+ $readSucceeded = false;
+ $exception = null;
+ $currentLocale = $locale;
+ $testedLocales = array();
+
+ while (null !== $currentLocale) {
+ // Resolve any aliases to their target locales
+ if (isset($this->localeAliases[$currentLocale])) {
+ $currentLocale = $this->localeAliases[$currentLocale];
+ }
+
+ try {
+ $data = $this->reader->read($path, $currentLocale);
+ $currentEntry = RecursiveArrayAccess::get($data, $indices);
+ $readSucceeded = true;
+
+ $isCurrentTraversable = $currentEntry instanceof \Traversable;
+ $isCurrentMultiValued = $isCurrentTraversable || is_array($currentEntry);
+
+ // Return immediately if fallback is disabled or we are dealing
+ // with a scalar non-null entry
+ if (!$fallback || (!$isCurrentMultiValued && null !== $currentEntry)) {
+ return $currentEntry;
+ }
+
+ // =========================================================
+ // Fallback is enabled, entry is either multi-valued or NULL
+ // =========================================================
+
+ // If entry is multi-valued, convert to array
+ if ($isCurrentTraversable) {
+ $currentEntry = iterator_to_array($currentEntry);
+ }
+
+ // If previously read entry was multi-valued too, merge them
+ if ($isCurrentMultiValued && $isMultiValued) {
+ $currentEntry = array_merge($currentEntry, $entry);
+ }
+
+ // Keep the previous entry if the current entry is NULL
+ if (null !== $currentEntry) {
+ $entry = $currentEntry;
+ }
+
+ // If this or the previous entry was multi-valued, we are dealing
+ // with a merged, multi-valued entry now
+ $isMultiValued = $isMultiValued || $isCurrentMultiValued;
+ } catch (ResourceBundleNotFoundException $e) {
+ // Continue if there is a fallback locale for the current
+ // locale
+ $exception = $e;
+ } catch (OutOfBoundsException $e) {
+ // Remember exception and rethrow if we cannot find anything in
+ // the fallback locales either
+ $exception = $e;
+ }
+
+ // Remember which locales we tried
+ $testedLocales[] = $currentLocale.'.res';
+
+ // Check whether fallback is allowed
+ if (!$fallback) {
+ break;
+ }
+
+ // Then determine fallback locale
+ $currentLocale = Locale::getFallback($currentLocale);
+ }
+
+ // Multi-valued entry was merged
+ if ($isMultiValued) {
+ return $entry;
+ }
+
+ // Entry is still NULL, but no read error occurred
+ if ($readSucceeded) {
+ return $entry;
+ }
+
+ // Entry is still NULL, read error occurred. Throw an exception
+ // containing the detailed path and locale
+ $errorMessage = sprintf(
+ 'Couldn\'t read the indices [%s] from "%s/%s.res".',
+ implode('][', $indices),
+ $path,
+ $locale
+ );
+
+ // Append fallback locales, if any
+ if (count($testedLocales) > 1) {
+ // Remove original locale
+ array_shift($testedLocales);
+
+ $errorMessage .= sprintf(
+ ' The indices also couldn\'t be found in the fallback locale(s) "%s".',
+ implode('", "', $testedLocales)
+ );
+ }
+
+ throw new MissingResourceException($errorMessage, 0, $exception);
}
/**
* {@inheritdoc}
*/
- public function readEntry($path, $locale, array $indices, $fallback = true)
+ public function getLocales($path)
{
- $data = $this->reader->read($path, $locale);
-
- $entry = RecursiveArrayAccess::get($data, $indices);
- $multivalued = is_array($entry) || $entry instanceof \Traversable;
-
- if (!($fallback && (null === $entry || $multivalued))) {
- return $entry;
- }
-
- if (null !== ($fallbackLocale = $this->getFallbackLocale($locale))) {
- $parentEntry = $this->readEntry($path, $fallbackLocale, $indices, true);
-
- if ($entry || $parentEntry) {
- $multivalued = $multivalued || is_array($parentEntry) || $parentEntry instanceof \Traversable;
-
- if ($multivalued) {
- if ($entry instanceof \Traversable) {
- $entry = iterator_to_array($entry);
- }
-
- if ($parentEntry instanceof \Traversable) {
- $parentEntry = iterator_to_array($parentEntry);
- }
-
- $entry = array_merge(
- $parentEntry ?: array(),
- $entry ?: array()
- );
- } else {
- $entry = null === $entry ? $parentEntry : $entry;
- }
- }
- }
-
- return $entry;
- }
-
- /**
- * Returns the fallback locale for a given locale, if any
- *
- * @param string $locale The locale to find the fallback for.
- *
- * @return string|null The fallback locale, or null if no parent exists
- */
- private function getFallbackLocale($locale)
- {
- if (false === $pos = strrpos($locale, '_')) {
- return;
- }
-
- return substr($locale, 0, $pos);
+ return $this->reader->getLocales($path);
}
}
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReaderInterface.php b/src/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReaderInterface.php
index 5d435c485c..19bb3fb49b 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReaderInterface.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Reader/StructuredBundleReaderInterface.php
@@ -11,10 +11,14 @@
namespace Symfony\Component\Intl\ResourceBundle\Reader;
+use Symfony\Component\Intl\Exception\MissingResourceException;
+
/**
* Reads individual entries of a resource file.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
interface StructuredBundleReaderInterface extends BundleReaderInterface
{
@@ -43,8 +47,9 @@ interface StructuredBundleReaderInterface extends BundleReaderInterface
* in the requested locale.
*
* @return mixed Returns an array or {@link \ArrayAccess} instance for
- * complex data, a scalar value for simple data and NULL
- * if the given path could not be accessed.
+ * complex data and a scalar value for simple data.
+ *
+ * @throws MissingResourceException If the indices cannot be accessed
*/
public function readEntry($path, $locale, array $indices, $fallback = true);
}
diff --git a/src/Symfony/Component/Intl/ResourceBundle/RegionBundle.php b/src/Symfony/Component/Intl/ResourceBundle/RegionBundle.php
index bbfbedeed9..0a4cce8a97 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/RegionBundle.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/RegionBundle.php
@@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle;
* Default implementation of {@link RegionBundleInterface}.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
class RegionBundle extends AbstractBundle implements RegionBundleInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Transformer/BundleTransformer.php b/src/Symfony/Component/Intl/ResourceBundle/Transformer/BundleTransformer.php
index 0692d6fe50..c613486f18 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Transformer/BundleTransformer.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Transformer/BundleTransformer.php
@@ -19,6 +19,8 @@ use Symfony\Component\Intl\ResourceBundle\Writer\PhpBundleWriter;
* Compiles a number of resource bundles based on predefined compilation rules.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
class BundleTransformer
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContext.php b/src/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContext.php
index cdc1951b96..2b336e8138 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContext.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContext.php
@@ -18,6 +18,8 @@ use Symfony\Component\Intl\ResourceBundle\Compiler\BundleCompilerInterface;
* Default implementation of {@link CompilationContextInterface}.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
class CompilationContext implements CompilationContextInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContextInterface.php b/src/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContextInterface.php
index f05c28079a..cd13ace6f5 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContextInterface.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Transformer/CompilationContextInterface.php
@@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle\Transformer;
* Stores contextual information for resource bundle compilation.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
interface CompilationContextInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/CurrencyBundleTransformationRule.php b/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/CurrencyBundleTransformationRule.php
index 95783b3b06..d38cfc7b70 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/CurrencyBundleTransformationRule.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/CurrencyBundleTransformationRule.php
@@ -21,6 +21,8 @@ use Symfony\Component\Intl\Util\IcuVersion;
* The rule for compiling the currency bundle.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
class CurrencyBundleTransformationRule implements TransformationRuleInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LanguageBundleTransformationRule.php b/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LanguageBundleTransformationRule.php
index 5e6f901849..7d78d74880 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LanguageBundleTransformationRule.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LanguageBundleTransformationRule.php
@@ -20,6 +20,8 @@ use Symfony\Component\Intl\Util\IcuVersion;
* The rule for compiling the language bundle.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
class LanguageBundleTransformationRule implements TransformationRuleInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LocaleBundleTransformationRule.php b/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LocaleBundleTransformationRule.php
index af4dfea557..22ec911372 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LocaleBundleTransformationRule.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LocaleBundleTransformationRule.php
@@ -21,6 +21,8 @@ use Symfony\Component\Intl\ResourceBundle\Writer\TextBundleWriter;
* The rule for compiling the locale bundle.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
class LocaleBundleTransformationRule implements TransformationRuleInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/RegionBundleTransformationRule.php b/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/RegionBundleTransformationRule.php
index 52fdbed8c3..300ad02563 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/RegionBundleTransformationRule.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/RegionBundleTransformationRule.php
@@ -20,6 +20,8 @@ use Symfony\Component\Intl\Util\IcuVersion;
* The rule for compiling the region bundle.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
class RegionBundleTransformationRule implements TransformationRuleInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/TransformationRuleInterface.php b/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/TransformationRuleInterface.php
index 3965e0d2b7..f02bf285d9 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/TransformationRuleInterface.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/TransformationRuleInterface.php
@@ -18,6 +18,8 @@ use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface;
* Contains instruction for compiling a resource bundle.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
interface TransformationRuleInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContext.php b/src/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContext.php
index 25ab68dbfc..de2604e0b7 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContext.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContext.php
@@ -15,6 +15,8 @@ use Symfony\Component\Filesystem\Filesystem;
/**
* @author Bernhard Schussek
+ *
+ * @internal
*/
class StubbingContext implements StubbingContextInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContextInterface.php b/src/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContextInterface.php
index dc49255620..596ee1bb7e 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContextInterface.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Transformer/StubbingContextInterface.php
@@ -13,6 +13,8 @@ namespace Symfony\Component\Intl\ResourceBundle\Transformer;
/**
* @author Bernhard Schussek
+ *
+ * @internal
*/
interface StubbingContextInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Util/ArrayAccessibleResourceBundle.php b/src/Symfony/Component/Intl/ResourceBundle/Util/ArrayAccessibleResourceBundle.php
index 9a4cccb461..44f1b2c580 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Util/ArrayAccessibleResourceBundle.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Util/ArrayAccessibleResourceBundle.php
@@ -20,6 +20,8 @@ use Symfony\Component\Intl\Exception\BadMethodCallException;
* This class can be removed once that bug is fixed.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
class ArrayAccessibleResourceBundle implements \ArrayAccess, \IteratorAggregate, \Countable
{
@@ -30,16 +32,16 @@ class ArrayAccessibleResourceBundle implements \ArrayAccess, \IteratorAggregate,
$this->bundleImpl = $bundleImpl;
}
- public function get($offset, $fallback = null)
+ public function get($offset)
{
- $value = $this->bundleImpl->get($offset, $fallback);
+ $value = $this->bundleImpl->get($offset);
return $value instanceof \ResourceBundle ? new static($value) : $value;
}
public function offsetExists($offset)
{
- return null !== $this->bundleImpl[$offset];
+ return null !== $this->bundleImpl->get($offset);
}
public function offsetGet($offset)
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Util/RecursiveArrayAccess.php b/src/Symfony/Component/Intl/ResourceBundle/Util/RecursiveArrayAccess.php
index 5257e9c1ca..0c22550401 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Util/RecursiveArrayAccess.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Util/RecursiveArrayAccess.php
@@ -11,19 +11,35 @@
namespace Symfony\Component\Intl\ResourceBundle\Util;
+use Symfony\Component\Intl\Exception\OutOfBoundsException;
+
/**
* @author Bernhard Schussek
+ *
+ * @internal
*/
class RecursiveArrayAccess
{
public static function get($array, array $indices)
{
foreach ($indices as $index) {
- if (!$array instanceof \ArrayAccess && !is_array($array)) {
- return;
+ // Use array_key_exists() for arrays, isset() otherwise
+ if (is_array($array)) {
+ if (array_key_exists($index, $array)) {
+ $array = $array[$index];
+ continue;
+ }
+ } elseif ($array instanceof \ArrayAccess) {
+ if (isset($array[$index])) {
+ $array = $array[$index];
+ continue;
+ }
}
-
- $array = $array[$index];
+
+ throw new OutOfBoundsException(sprintf(
+ 'The index %s does not exist.',
+ $index
+ ));
}
return $array;
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Util/RingBuffer.php b/src/Symfony/Component/Intl/ResourceBundle/Util/RingBuffer.php
index 7ccbd1e702..59cfdaac90 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Util/RingBuffer.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Util/RingBuffer.php
@@ -21,6 +21,8 @@ use Symfony\Component\Intl\Exception\OutOfBoundsException;
* then the second and so on.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
class RingBuffer implements \ArrayAccess
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Writer/BundleWriterInterface.php b/src/Symfony/Component/Intl/ResourceBundle/Writer/BundleWriterInterface.php
index cc3b958657..f612aacae7 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Writer/BundleWriterInterface.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Writer/BundleWriterInterface.php
@@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle\Writer;
* Writes resource bundle files.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
interface BundleWriterInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Writer/PhpBundleWriter.php b/src/Symfony/Component/Intl/ResourceBundle/Writer/PhpBundleWriter.php
index d2688b49bc..d5a30b907d 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Writer/PhpBundleWriter.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Writer/PhpBundleWriter.php
@@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle\Writer;
* Writes .php resource bundles.
*
* @author Bernhard Schussek
+ *
+ * @internal
*/
class PhpBundleWriter implements BundleWriterInterface
{
diff --git a/src/Symfony/Component/Intl/ResourceBundle/Writer/TextBundleWriter.php b/src/Symfony/Component/Intl/ResourceBundle/Writer/TextBundleWriter.php
index c2fff38c7b..2aa702089f 100644
--- a/src/Symfony/Component/Intl/ResourceBundle/Writer/TextBundleWriter.php
+++ b/src/Symfony/Component/Intl/ResourceBundle/Writer/TextBundleWriter.php
@@ -11,26 +11,31 @@
namespace Symfony\Component\Intl\ResourceBundle\Writer;
+use Symfony\Component\Intl\Exception\UnexpectedTypeException;
+
/**
* Writes .txt resource bundles.
*
- * The resulting files can be converted to binary .res files using the
- * {@link \Symfony\Component\Intl\ResourceBundle\Transformer\BundleCompiler}.
+ * The resulting files can be converted to binary .res files using a
+ * {@link \Symfony\Component\Intl\ResourceBundle\Compiler\BundleCompilerInterface}
+ * implementation.
*
* @author Bernhard Schussek
*
* @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
+ *
+ * @internal
*/
class TextBundleWriter implements BundleWriterInterface
{
/**
* {@inheritdoc}
*/
- public function write($path, $locale, $data)
+ public function write($path, $locale, $data, $fallback = true)
{
$file = fopen($path.'/'.$locale.'.txt', 'w');
- $this->writeResourceBundle($file, $locale, $data);
+ $this->writeResourceBundle($file, $locale, $data, $fallback);
fclose($file);
}
@@ -41,14 +46,16 @@ class TextBundleWriter implements BundleWriterInterface
* @param resource $file The file handle to write to.
* @param string $bundleName The name of the bundle.
* @param mixed $value The value of the node.
+ * @param bool $fallback Whether the resource bundle should be merged
+ * with the fallback locale.
*
* @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
*/
- private function writeResourceBundle($file, $bundleName, $value)
+ private function writeResourceBundle($file, $bundleName, $value, $fallback)
{
fwrite($file, $bundleName);
- $this->writeTable($file, $value, 0);
+ $this->writeTable($file, $value, 0, $fallback);
fwrite($file, "\n");
}
@@ -72,16 +79,25 @@ class TextBundleWriter implements BundleWriterInterface
return;
}
+ if ($value instanceof \Traversable) {
+ $value = iterator_to_array($value);
+ }
+
if (is_array($value)) {
- if (count($value) === count(array_filter($value, 'is_int'))) {
+ $intValues = count($value) === count(array_filter($value, 'is_int'));
+
+ $keys = array_keys($value);
+
+ // check that the keys are 0-indexed and ascending
+ $intKeys = $keys === range(0, count($keys) - 1);
+
+ if ($intValues && $intKeys) {
$this->writeIntVector($file, $value, $indentation);
return;
}
- $keys = array_keys($value);
-
- if (count($keys) === count(array_filter($keys, 'is_int'))) {
+ if ($intKeys) {
$this->writeArray($file, $value, $indentation);
return;
@@ -180,16 +196,35 @@ class TextBundleWriter implements BundleWriterInterface
/**
* Writes a "table" node.
*
- * @param resource $file The file handle to write to.
- * @param array $value The value of the node.
- * @param int $indentation The number of levels to indent.
+ * @param resource $file The file handle to write to.
+ * @param array|\Traversable $value The value of the node.
+ * @param int $indentation The number of levels to indent.
+ * @param bool $fallback Whether the table should be merged
+ * with the fallback locale.
+ *
+ * @throws UnexpectedTypeException When $value is not an array and not a
+ * \Traversable instance.
*/
- private function writeTable($file, array $value, $indentation)
+ private function writeTable($file, $value, $indentation, $fallback = true)
{
+ if (!is_array($value) && !$value instanceof \Traversable) {
+ throw new UnexpectedTypeException($value, 'array or \Traversable');
+ }
+
+ if (!$fallback) {
+ fwrite($file, ":table(nofallback)");
+ }
+
fwrite($file, "{\n");
foreach ($value as $key => $entry) {
fwrite($file, str_repeat(' ', $indentation + 1));
+
+ // escape colons, otherwise they are interpreted as resource types
+ if (false !== strpos($key, ':') || false !== strpos($key, ' ')) {
+ $key = '"'.$key.'"';
+ }
+
fwrite($file, $key);
$this->writeResource($file, $entry, $indentation + 1);
diff --git a/src/Symfony/Component/Intl/Resources/bin/common.php b/src/Symfony/Component/Intl/Resources/bin/common.php
index eb7643dfbd..1e25265d57 100644
--- a/src/Symfony/Component/Intl/Resources/bin/common.php
+++ b/src/Symfony/Component/Intl/Resources/bin/common.php
@@ -11,11 +11,11 @@
define('LINE_WIDTH', 75);
-define('LINE', str_repeat('-', LINE_WIDTH) . "\n");
+define('LINE', str_repeat('-', LINE_WIDTH)."\n");
function bailout($message)
{
- echo wordwrap($message, LINE_WIDTH) . " Aborting.\n";
+ echo wordwrap($message, LINE_WIDTH)." Aborting.\n";
exit(1);
}
@@ -31,7 +31,7 @@ function centered($text)
{
$padding = (int) ((LINE_WIDTH - strlen($text))/2);
- return str_repeat(' ', $padding) . $text;
+ return str_repeat(' ', $padding).$text;
}
function cd($dir)
@@ -47,7 +47,7 @@ function run($command)
if (0 !== $status) {
$output = implode("\n", $output);
- echo "Error while running:\n " . getcwd() . '$ ' . $command . "\nOutput:\n" . LINE . "$output\n" . LINE;
+ echo "Error while running:\n ".getcwd().'$ '.$command."\nOutput:\n".LINE."$output\n".LINE;
bailout("\"$command\" failed.");
}
@@ -55,10 +55,10 @@ function run($command)
function get_icu_version_from_genrb($genrb)
{
- exec($genrb . ' --version 2>&1', $output, $status);
+ exec($genrb.' --version 2>&1', $output, $status);
if (0 !== $status) {
- bailout($genrb . ' failed.');
+ bailout($genrb.' failed.');
}
if (!preg_match('/ICU version ([\d\.]+)/', implode('', $output), $matches)) {
@@ -67,3 +67,27 @@ function get_icu_version_from_genrb($genrb)
return $matches[1];
}
+
+set_exception_handler(function (\Exception $exception) {
+ echo "\n";
+
+ $cause = $exception;
+ $root = true;
+
+ while (null !== $cause) {
+ if (!$root) {
+ echo "Caused by\n";
+ }
+
+ echo get_class($cause).": ".$cause->getMessage()."\n";
+ echo "\n";
+ echo $cause->getFile().":".$cause->getLine()."\n";
+ foreach ($cause->getTrace() as $trace) {
+ echo $trace['file'].":".$trace['line']."\n";
+ }
+ echo "\n";
+
+ $cause = $cause->getPrevious();
+ $root = false;
+ }
+});
diff --git a/src/Symfony/Component/Intl/Resources/bin/icu.ini b/src/Symfony/Component/Intl/Resources/bin/icu.ini
index 7f1ad90a6a..adfb71643d 100644
--- a/src/Symfony/Component/Intl/Resources/bin/icu.ini
+++ b/src/Symfony/Component/Intl/Resources/bin/icu.ini
@@ -8,3 +8,5 @@
49 = http://source.icu-project.org/repos/icu/icu/tags/release-49-1-2/source
50 = http://source.icu-project.org/repos/icu/icu/tags/release-50-1-2/source
51 = http://source.icu-project.org/repos/icu/icu/tags/release-51-2/source
+52 = http://source.icu-project.org/repos/icu/icu/tags/release-52-1/source
+53 = http://source.icu-project.org/repos/icu/icu/tags/release-53-1/source
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/AbstractBundleReaderTest.php b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/AbstractBundleReaderTest.php
deleted file mode 100644
index 2da7f90de4..0000000000
--- a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/AbstractBundleReaderTest.php
+++ /dev/null
@@ -1,64 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Intl\Tests\ResourceBundle\Reader;
-
-use Symfony\Component\Filesystem\Filesystem;
-
-/**
- * @author Bernhard Schussek
- */
-class AbstractBundleReaderTest extends \PHPUnit_Framework_TestCase
-{
- private $directory;
-
- /**
- * @var Filesystem
- */
- private $filesystem;
-
- /**
- * @var \PHPUnit_Framework_MockObject_MockObject
- */
- private $reader;
-
- protected function setUp()
- {
- $this->directory = sys_get_temp_dir() . '/AbstractBundleReaderTest/' . rand(1000, 9999);
- $this->filesystem = new Filesystem();
- $this->reader = $this->getMockForAbstractClass('Symfony\Component\Intl\ResourceBundle\Reader\AbstractBundleReader');
-
- $this->filesystem->mkdir($this->directory);
- }
-
- protected function tearDown()
- {
- $this->filesystem->remove($this->directory);
- }
-
- public function testGetLocales()
- {
- $this->filesystem->touch($this->directory . '/en.foo');
- $this->filesystem->touch($this->directory . '/de.foo');
- $this->filesystem->touch($this->directory . '/fr.foo');
- $this->filesystem->touch($this->directory . '/bo.txt');
- $this->filesystem->touch($this->directory . '/gu.bin');
- $this->filesystem->touch($this->directory . '/s.lol');
-
- $this->reader->expects($this->any())
- ->method('getFileExtension')
- ->will($this->returnValue('foo'));
-
- $sortedLocales = array('de', 'en', 'fr');
-
- $this->assertSame($sortedLocales, $this->reader->getLocales($this->directory));
- }
-}
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/BinaryBundleReaderTest.php b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/BinaryBundleReaderTest.php
index 3aefbae7fd..526424d1a3 100644
--- a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/BinaryBundleReaderTest.php
+++ b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/BinaryBundleReaderTest.php
@@ -33,19 +33,61 @@ class BinaryBundleReaderTest extends \PHPUnit_Framework_TestCase
public function testReadReturnsArrayAccess()
{
- $data = $this->reader->read(__DIR__ . '/Fixtures', 'en');
+ $data = $this->reader->read(__DIR__.'/Fixtures/res', 'ro');
$this->assertInstanceOf('\ArrayAccess', $data);
$this->assertSame('Bar', $data['Foo']);
$this->assertFalse(isset($data['ExistsNot']));
}
+ public function testReadFollowsAlias()
+ {
+ // "alias" = "ro"
+ $data = $this->reader->read(__DIR__.'/Fixtures/res', 'alias');
+
+ $this->assertInstanceOf('\ArrayAccess', $data);
+ $this->assertSame('Bar', $data['Foo']);
+ $this->assertFalse(isset($data['ExistsNot']));
+ }
+
+ public function testReadDoesNotFollowFallback()
+ {
+ // "ro_MD" -> "ro"
+ $data = $this->reader->read(__DIR__.'/Fixtures/res', 'ro_MD');
+
+ $this->assertInstanceOf('\ArrayAccess', $data);
+ $this->assertSame('Bam', $data['Baz']);
+ $this->assertFalse(isset($data['Foo']));
+ $this->assertNull($data['Foo']);
+ $this->assertFalse(isset($data['ExistsNot']));
+ }
+
+ public function testReadDoesNotFollowFallbackAlias()
+ {
+ // "mo" = "ro_MD" -> "ro"
+ $data = $this->reader->read(__DIR__.'/Fixtures/res', 'mo');
+
+ $this->assertInstanceOf('\ArrayAccess', $data);
+ $this->assertSame('Bam', $data['Baz'], 'data from the aliased locale can be accessed');
+ $this->assertFalse(isset($data['Foo']));
+ $this->assertNull($data['Foo']);
+ $this->assertFalse(isset($data['ExistsNot']));
+ }
+
/**
- * @expectedException \Symfony\Component\Intl\Exception\RuntimeException
+ * @expectedException \Symfony\Component\Intl\Exception\ResourceBundleNotFoundException
*/
public function testReadFailsIfNonExistingLocale()
{
- $this->reader->read(__DIR__ . '/Fixtures', 'foo');
+ $this->reader->read(__DIR__.'/Fixtures/res', 'foo');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Intl\Exception\ResourceBundleNotFoundException
+ */
+ public function testReadFailsIfNonExistingFallbackLocale()
+ {
+ $this->reader->read(__DIR__.'/Fixtures/res', 'ro_AT');
}
/**
@@ -53,6 +95,6 @@ class BinaryBundleReaderTest extends \PHPUnit_Framework_TestCase
*/
public function testReadFailsIfNonExistingDirectory()
{
- $this->reader->read(__DIR__ . '/foo', 'en');
+ $this->reader->read(__DIR__.'/foo', 'ro');
}
}
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/build.sh b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/build.sh
new file mode 100755
index 0000000000..50513e7a94
--- /dev/null
+++ b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/build.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+if [ -z "$ICU_BUILD_DIR" ]; then
+ echo "Please set the ICU_BUILD_DIR environment variable"
+ exit
+fi
+
+if [ ! -d "$ICU_BUILD_DIR" ]; then
+ echo "The directory $ICU_BUILD_DIR pointed at by ICU_BUILD_DIR does not exist"
+ exit
+fi
+
+DIR=`dirname $0`
+
+rm $DIR/res/*.res
+
+LD_LIBRARY_PATH=$ICU_BUILD_DIR/lib $ICU_BUILD_DIR/bin/genrb -d $DIR/res $DIR/txt/*.txt
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/php/en.php b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/php/en.php
new file mode 100644
index 0000000000..f2b06a91ad
--- /dev/null
+++ b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/php/en.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return array(
+ 'Foo' => 'Bar',
+);
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/res/alias.res b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/res/alias.res
new file mode 100644
index 0000000000..4f0ab7eaa3
Binary files /dev/null and b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/res/alias.res differ
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/res/mo.res b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/res/mo.res
new file mode 100644
index 0000000000..3f8911a731
Binary files /dev/null and b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/res/mo.res differ
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/res/ro.res b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/res/ro.res
new file mode 100644
index 0000000000..c78e9045bf
Binary files /dev/null and b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/res/ro.res differ
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/res/ro_MD.res b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/res/ro_MD.res
new file mode 100644
index 0000000000..c8b0810ec6
Binary files /dev/null and b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/res/ro_MD.res differ
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/res/root.res b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/res/root.res
new file mode 100644
index 0000000000..81ba7eaedb
Binary files /dev/null and b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/res/root.res differ
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/txt/alias.txt b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/txt/alias.txt
new file mode 100644
index 0000000000..d6e216f4cb
--- /dev/null
+++ b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/txt/alias.txt
@@ -0,0 +1,3 @@
+alias{
+ "%%ALIAS"{"ro"}
+}
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/txt/mo.txt b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/txt/mo.txt
new file mode 100644
index 0000000000..3ce23bcc63
--- /dev/null
+++ b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/txt/mo.txt
@@ -0,0 +1,3 @@
+mo{
+ "%%ALIAS"{"ro_MD"}
+}
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/txt/ro.txt b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/txt/ro.txt
new file mode 100644
index 0000000000..80d28889cf
--- /dev/null
+++ b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/txt/ro.txt
@@ -0,0 +1,3 @@
+ro{
+ Foo{"Bar"}
+}
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/txt/ro_MD.txt b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/txt/ro_MD.txt
new file mode 100644
index 0000000000..fcbb3bc07d
--- /dev/null
+++ b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/txt/ro_MD.txt
@@ -0,0 +1,3 @@
+ro_MD{
+ Baz{"Bam"}
+}
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/txt/root.txt b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/txt/root.txt
new file mode 100644
index 0000000000..4d8265997f
--- /dev/null
+++ b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/Fixtures/txt/root.txt
@@ -0,0 +1,6 @@
+root{
+ /**
+ * so genrb doesn't issue warnings
+ */
+ ___{""}
+}
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/PhpBundleReaderTest.php b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/PhpBundleReaderTest.php
index 2fee35599f..fd5a0843c1 100644
--- a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/PhpBundleReaderTest.php
+++ b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/PhpBundleReaderTest.php
@@ -30,7 +30,7 @@ class PhpBundleReaderTest extends \PHPUnit_Framework_TestCase
public function testReadReturnsArray()
{
- $data = $this->reader->read(__DIR__ . '/Fixtures', 'en');
+ $data = $this->reader->read(__DIR__.'/Fixtures/php', 'en');
$this->assertTrue(is_array($data));
$this->assertSame('Bar', $data['Foo']);
@@ -38,11 +38,11 @@ class PhpBundleReaderTest extends \PHPUnit_Framework_TestCase
}
/**
- * @expectedException \Symfony\Component\Intl\Exception\InvalidArgumentException
+ * @expectedException \Symfony\Component\Intl\Exception\ResourceBundleNotFoundException
*/
- public function testReadFailsIfLocaleOtherThanEn()
+ public function testReadFailsIfNonExistingLocale()
{
- $this->reader->read(__DIR__ . '/Fixtures', 'foo');
+ $this->reader->read(__DIR__.'/Fixtures/php', 'foo');
}
/**
@@ -50,7 +50,7 @@ class PhpBundleReaderTest extends \PHPUnit_Framework_TestCase
*/
public function testReadFailsIfNonExistingDirectory()
{
- $this->reader->read(__DIR__ . '/foo', 'en');
+ $this->reader->read(__DIR__.'/foo', 'en');
}
/**
@@ -58,6 +58,6 @@ class PhpBundleReaderTest extends \PHPUnit_Framework_TestCase
*/
public function testReadFailsIfNotAFile()
{
- $this->reader->read(__DIR__ . '/Fixtures/NotAFile', 'en');
+ $this->reader->read(__DIR__.'/Fixtures/NotAFile', 'en');
}
}
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/StructuredBundleReaderTest.php b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/StructuredBundleReaderTest.php
index 600236eb3e..e51e7ef2ee 100644
--- a/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/StructuredBundleReaderTest.php
+++ b/src/Symfony/Component/Intl/Tests/ResourceBundle/Reader/StructuredBundleReaderTest.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\Intl\Tests\ResourceBundle\Reader;
+use Symfony\Component\Intl\Exception\ResourceBundleNotFoundException;
use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReader;
/**
@@ -30,73 +31,146 @@ class StructuredBundleReaderTest extends \PHPUnit_Framework_TestCase
*/
private $readerImpl;
+ private static $data = array(
+ 'Entries' => array(
+ 'Foo' => 'Bar',
+ 'Bar' => 'Baz',
+ ),
+ 'Foo' => 'Bar',
+ 'Version' => '2.0',
+ );
+
+ private static $fallbackData = array(
+ 'Entries' => array(
+ 'Foo' => 'Foo',
+ 'Bam' => 'Lah',
+ ),
+ 'Baz' => 'Foo',
+ 'Version' => '1.0',
+ );
+
+ private static $mergedData = array(
+ // no recursive merging -> too complicated
+ 'Entries' => array(
+ 'Foo' => 'Bar',
+ 'Bar' => 'Baz',
+ ),
+ 'Baz' => 'Foo',
+ 'Version' => '2.0',
+ 'Foo' => 'Bar',
+ );
+
protected function setUp()
{
$this->readerImpl = $this->getMock('Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface');
$this->reader = new StructuredBundleReader($this->readerImpl);
}
- public function testGetLocales()
+ public function testForwardCallToRead()
{
- $locales = array('en', 'de', 'fr');
-
- $this->readerImpl->expects($this->once())
- ->method('getLocales')
- ->with(self::RES_DIR)
- ->will($this->returnValue($locales));
-
- $this->assertSame($locales, $this->reader->getLocales(self::RES_DIR));
- }
-
- public function testRead()
- {
- $data = array('foo', 'bar');
-
$this->readerImpl->expects($this->once())
->method('read')
- ->with(self::RES_DIR, 'en')
- ->will($this->returnValue($data));
+ ->with(self::RES_DIR, 'root')
+ ->will($this->returnValue(self::$data));
- $this->assertSame($data, $this->reader->read(self::RES_DIR, 'en'));
+ $this->assertSame(self::$data, $this->reader->read(self::RES_DIR, 'root'));
}
- public function testReadEntryNoParams()
+ public function testReadEntireDataFileIfNoIndicesGiven()
{
- $data = array('foo', 'bar');
-
- $this->readerImpl->expects($this->once())
+ $this->readerImpl->expects($this->at(0))
->method('read')
->with(self::RES_DIR, 'en')
- ->will($this->returnValue($data));
+ ->will($this->returnValue(self::$data));
- $this->assertSame($data, $this->reader->readEntry(self::RES_DIR, 'en', array()));
+ $this->readerImpl->expects($this->at(1))
+ ->method('read')
+ ->with(self::RES_DIR, 'root')
+ ->will($this->returnValue(self::$fallbackData));
+
+ $this->assertSame(self::$mergedData, $this->reader->readEntry(self::RES_DIR, 'en', array()));
}
- public function testReadEntryWithParam()
+ public function testReadExistingEntry()
{
- $data = array('Foo' => array('Bar' => 'Baz'));
-
$this->readerImpl->expects($this->once())
->method('read')
- ->with(self::RES_DIR, 'en')
- ->will($this->returnValue($data));
+ ->with(self::RES_DIR, 'root')
+ ->will($this->returnValue(self::$data));
- $this->assertSame('Baz', $this->reader->readEntry(self::RES_DIR, 'en', array('Foo', 'Bar')));
+ $this->assertSame('Bar', $this->reader->readEntry(self::RES_DIR, 'root', array('Entries', 'Foo')));
}
- public function testReadEntryWithUnresolvablePath()
+ /**
+ * @expectedException \Symfony\Component\Intl\Exception\MissingResourceException
+ */
+ public function testReadNonExistingEntry()
{
- $data = array('Foo' => 'Baz');
-
$this->readerImpl->expects($this->once())
->method('read')
- ->with(self::RES_DIR, 'en')
- ->will($this->returnValue($data));
+ ->with(self::RES_DIR, 'root')
+ ->will($this->returnValue(self::$data));
- $this->assertNull($this->reader->readEntry(self::RES_DIR, 'en', array('Foo', 'Bar')));
+ $this->reader->readEntry(self::RES_DIR, 'root', array('Entries', 'NonExisting'));
}
- public function readMergedEntryProvider()
+ public function testFallbackIfEntryDoesNotExist()
+ {
+ $this->readerImpl->expects($this->at(0))
+ ->method('read')
+ ->with(self::RES_DIR, 'en_GB')
+ ->will($this->returnValue(self::$data));
+
+ $this->readerImpl->expects($this->at(1))
+ ->method('read')
+ ->with(self::RES_DIR, 'en')
+ ->will($this->returnValue(self::$fallbackData));
+
+ $this->assertSame('Lah', $this->reader->readEntry(self::RES_DIR, 'en_GB', array('Entries', 'Bam')));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Intl\Exception\MissingResourceException
+ */
+ public function testDontFallbackIfEntryDoesNotExistAndFallbackDisabled()
+ {
+ $this->readerImpl->expects($this->once())
+ ->method('read')
+ ->with(self::RES_DIR, 'en_GB')
+ ->will($this->returnValue(self::$data));
+
+ $this->reader->readEntry(self::RES_DIR, 'en_GB', array('Entries', 'Bam'), false);
+ }
+
+ public function testFallbackIfLocaleDoesNotExist()
+ {
+ $this->readerImpl->expects($this->at(0))
+ ->method('read')
+ ->with(self::RES_DIR, 'en_GB')
+ ->will($this->throwException(new ResourceBundleNotFoundException()));
+
+ $this->readerImpl->expects($this->at(1))
+ ->method('read')
+ ->with(self::RES_DIR, 'en')
+ ->will($this->returnValue(self::$fallbackData));
+
+ $this->assertSame('Lah', $this->reader->readEntry(self::RES_DIR, 'en_GB', array('Entries', 'Bam')));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Intl\Exception\MissingResourceException
+ */
+ public function testDontFallbackIfLocaleDoesNotExistAndFallbackDisabled()
+ {
+ $this->readerImpl->expects($this->once())
+ ->method('read')
+ ->with(self::RES_DIR, 'en_GB')
+ ->will($this->throwException(new ResourceBundleNotFoundException()));
+
+ $this->reader->readEntry(self::RES_DIR, 'en_GB', array('Entries', 'Bam'), false);
+ }
+
+ public function provideMergeableValues()
{
return array(
array('foo', null, 'foo'),
@@ -110,114 +184,182 @@ class StructuredBundleReaderTest extends \PHPUnit_Framework_TestCase
}
/**
- * @dataProvider readMergedEntryProvider
+ * @dataProvider provideMergeableValues
*/
- public function testReadMergedEntryNoParams($childData, $parentData, $result)
+ public function testMergeDataWithFallbackData($childData, $parentData, $result)
{
- $this->readerImpl->expects($this->at(0))
+ if (null === $childData || is_array($childData)) {
+ $this->readerImpl->expects($this->at(0))
+ ->method('read')
+ ->with(self::RES_DIR, 'en')
+ ->will($this->returnValue($childData));
+
+ $this->readerImpl->expects($this->at(1))
+ ->method('read')
+ ->with(self::RES_DIR, 'root')
+ ->will($this->returnValue($parentData));
+ } else {
+ $this->readerImpl->expects($this->once())
+ ->method('read')
+ ->with(self::RES_DIR, 'en')
+ ->will($this->returnValue($childData));
+ }
+
+ $this->assertSame($result, $this->reader->readEntry(self::RES_DIR, 'en', array(), true));
+ }
+
+ /**
+ * @dataProvider provideMergeableValues
+ */
+ public function testDontMergeDataIfFallbackDisabled($childData, $parentData, $result)
+ {
+ $this->readerImpl->expects($this->once())
->method('read')
->with(self::RES_DIR, 'en_GB')
->will($this->returnValue($childData));
- if (null === $childData || is_array($childData)) {
- $this->readerImpl->expects($this->at(1))
- ->method('read')
- ->with(self::RES_DIR, 'en')
- ->will($this->returnValue($parentData));
- }
-
- $this->assertSame($result, $this->reader->readEntry(self::RES_DIR, 'en_GB', array(), true));
+ $this->assertSame($childData, $this->reader->readEntry(self::RES_DIR, 'en_GB', array(), false));
}
/**
- * @dataProvider readMergedEntryProvider
+ * @dataProvider provideMergeableValues
*/
- public function testReadMergedEntryWithParams($childData, $parentData, $result)
+ public function testMergeExistingEntryWithExistingFallbackEntry($childData, $parentData, $result)
{
- $this->readerImpl->expects($this->at(0))
- ->method('read')
- ->with(self::RES_DIR, 'en_GB')
- ->will($this->returnValue(array('Foo' => array('Bar' => $childData))));
-
if (null === $childData || is_array($childData)) {
- $this->readerImpl->expects($this->at(1))
+ $this->readerImpl->expects($this->at(0))
->method('read')
->with(self::RES_DIR, 'en')
+ ->will($this->returnValue(array('Foo' => array('Bar' => $childData))));
+
+ $this->readerImpl->expects($this->at(1))
+ ->method('read')
+ ->with(self::RES_DIR, 'root')
->will($this->returnValue(array('Foo' => array('Bar' => $parentData))));
+ } else {
+ $this->readerImpl->expects($this->once())
+ ->method('read')
+ ->with(self::RES_DIR, 'en')
+ ->will($this->returnValue(array('Foo' => array('Bar' => $childData))));
}
- $this->assertSame($result, $this->reader->readEntry(self::RES_DIR, 'en_GB', array('Foo', 'Bar'), true));
- }
-
- public function testReadMergedEntryWithUnresolvablePath()
- {
- $this->readerImpl->expects($this->at(0))
- ->method('read')
- ->with(self::RES_DIR, 'en_GB')
- ->will($this->returnValue(array('Foo' => 'Baz')));
-
- $this->readerImpl->expects($this->at(1))
- ->method('read')
- ->with(self::RES_DIR, 'en')
- ->will($this->returnValue(array('Foo' => 'Bar')));
-
- $this->assertNull($this->reader->readEntry(self::RES_DIR, 'en_GB', array('Foo', 'Bar'), true));
- }
-
- public function testReadMergedEntryWithUnresolvablePathInParent()
- {
- $this->readerImpl->expects($this->at(0))
- ->method('read')
- ->with(self::RES_DIR, 'en_GB')
- ->will($this->returnValue(array('Foo' => array('Bar' => array('three')))));
-
- $this->readerImpl->expects($this->at(1))
- ->method('read')
- ->with(self::RES_DIR, 'en')
- ->will($this->returnValue(array('Foo' => 'Bar')));
-
- $result = array('three');
-
- $this->assertSame($result, $this->reader->readEntry(self::RES_DIR, 'en_GB', array('Foo', 'Bar'), true));
- }
-
- public function testReadMergedEntryWithUnresolvablePathInChild()
- {
- $this->readerImpl->expects($this->at(0))
- ->method('read')
- ->with(self::RES_DIR, 'en_GB')
- ->will($this->returnValue(array('Foo' => 'Baz')));
-
- $this->readerImpl->expects($this->at(1))
- ->method('read')
- ->with(self::RES_DIR, 'en')
- ->will($this->returnValue(array('Foo' => array('Bar' => array('one', 'two')))));
-
- $result = array('one', 'two');
-
- $this->assertSame($result, $this->reader->readEntry(self::RES_DIR, 'en_GB', array('Foo', 'Bar'), true));
+ $this->assertSame($result, $this->reader->readEntry(self::RES_DIR, 'en', array('Foo', 'Bar'), true));
}
/**
- * @dataProvider readMergedEntryProvider
+ * @dataProvider provideMergeableValues
*/
- public function testReadMergedEntryWithTraversables($childData, $parentData, $result)
+ public function testMergeNonExistingEntryWithExistingFallbackEntry($childData, $parentData, $result)
+ {
+ $this->readerImpl->expects($this->at(0))
+ ->method('read')
+ ->with(self::RES_DIR, 'en_GB')
+ ->will($this->returnValue(array('Foo' => 'Baz')));
+
+ $this->readerImpl->expects($this->at(1))
+ ->method('read')
+ ->with(self::RES_DIR, 'en')
+ ->will($this->returnValue(array('Foo' => array('Bar' => $parentData))));
+
+ $this->assertSame($parentData, $this->reader->readEntry(self::RES_DIR, 'en_GB', array('Foo', 'Bar'), true));
+ }
+
+ /**
+ * @dataProvider provideMergeableValues
+ */
+ public function testMergeExistingEntryWithNonExistingFallbackEntry($childData, $parentData, $result)
+ {
+ if (null === $childData || is_array($childData)) {
+ $this->readerImpl->expects($this->at(0))
+ ->method('read')
+ ->with(self::RES_DIR, 'en_GB')
+ ->will($this->returnValue(array('Foo' => array('Bar' => $childData))));
+
+ $this->readerImpl->expects($this->at(1))
+ ->method('read')
+ ->with(self::RES_DIR, 'en')
+ ->will($this->returnValue(array('Foo' => 'Bar')));
+ } else {
+ $this->readerImpl->expects($this->once())
+ ->method('read')
+ ->with(self::RES_DIR, 'en_GB')
+ ->will($this->returnValue(array('Foo' => array('Bar' => $childData))));
+ }
+
+ $this->assertSame($childData, $this->reader->readEntry(self::RES_DIR, 'en_GB', array('Foo', 'Bar'), true));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Intl\Exception\MissingResourceException
+ */
+ public function testFailIfEntryFoundNeitherInParentNorChild()
+ {
+ $this->readerImpl->expects($this->at(0))
+ ->method('read')
+ ->with(self::RES_DIR, 'en_GB')
+ ->will($this->returnValue(array('Foo' => 'Baz')));
+
+ $this->readerImpl->expects($this->at(1))
+ ->method('read')
+ ->with(self::RES_DIR, 'en')
+ ->will($this->returnValue(array('Foo' => 'Bar')));
+
+ $this->reader->readEntry(self::RES_DIR, 'en_GB', array('Foo', 'Bar'), true);
+ }
+
+ /**
+ * @dataProvider provideMergeableValues
+ */
+ public function testMergeTraversables($childData, $parentData, $result)
{
$parentData = is_array($parentData) ? new \ArrayObject($parentData) : $parentData;
$childData = is_array($childData) ? new \ArrayObject($childData) : $childData;
- $this->readerImpl->expects($this->at(0))
- ->method('read')
- ->with(self::RES_DIR, 'en_GB')
- ->will($this->returnValue(array('Foo' => array('Bar' => $childData))));
-
if (null === $childData || $childData instanceof \ArrayObject) {
+ $this->readerImpl->expects($this->at(0))
+ ->method('read')
+ ->with(self::RES_DIR, 'en_GB')
+ ->will($this->returnValue(array('Foo' => array('Bar' => $childData))));
+
$this->readerImpl->expects($this->at(1))
->method('read')
->with(self::RES_DIR, 'en')
->will($this->returnValue(array('Foo' => array('Bar' => $parentData))));
+ } else {
+ $this->readerImpl->expects($this->once())
+ ->method('read')
+ ->with(self::RES_DIR, 'en_GB')
+ ->will($this->returnValue(array('Foo' => array('Bar' => $childData))));
}
$this->assertSame($result, $this->reader->readEntry(self::RES_DIR, 'en_GB', array('Foo', 'Bar'), true));
}
+
+ /**
+ * @dataProvider provideMergeableValues
+ */
+ public function testFollowLocaleAliases($childData, $parentData, $result)
+ {
+ $this->reader->setLocaleAliases(array('mo' => 'ro_MD'));
+
+ if (null === $childData || is_array($childData)) {
+ $this->readerImpl->expects($this->at(0))
+ ->method('read')
+ ->with(self::RES_DIR, 'ro_MD')
+ ->will($this->returnValue(array('Foo' => array('Bar' => $childData))));
+
+ // Read fallback locale of aliased locale ("ro_MD" -> "ro")
+ $this->readerImpl->expects($this->at(1))
+ ->method('read')
+ ->with(self::RES_DIR, 'ro')
+ ->will($this->returnValue(array('Foo' => array('Bar' => $parentData))));
+ } else {
+ $this->readerImpl->expects($this->once())
+ ->method('read')
+ ->with(self::RES_DIR, 'ro_MD')
+ ->will($this->returnValue(array('Foo' => array('Bar' => $childData))));
+ }
+
+ $this->assertSame($result, $this->reader->readEntry(self::RES_DIR, 'mo', array('Foo', 'Bar'), true));
+ }
}
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.txt b/src/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.txt
index 0ee0d7f2f5..09c1275fa5 100644
--- a/src/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.txt
+++ b/src/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.txt
@@ -14,6 +14,22 @@ en{
2,
3,
}
+ NotAnIntVector{
+ 0:int{0}
+ 2:int{1}
+ 1:int{2}
+ 3:int{3}
+ }
+ IntVectorWithStringKeys{
+ a:int{0}
+ b:int{1}
+ c:int{2}
+ }
+ TableWithIntKeys{
+ 0:int{0}
+ 1:int{1}
+ 3:int{3}
+ }
FalseBoolean{"false"}
TrueBoolean{"true"}
Null{""}
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en_nofallback.txt b/src/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en_nofallback.txt
new file mode 100644
index 0000000000..85386f2074
--- /dev/null
+++ b/src/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en_nofallback.txt
@@ -0,0 +1,3 @@
+en_nofallback:table(nofallback){
+ Entry{"Value"}
+}
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/escaped.txt b/src/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/escaped.txt
new file mode 100644
index 0000000000..6669bfdd83
--- /dev/null
+++ b/src/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/escaped.txt
@@ -0,0 +1,4 @@
+escaped{
+ "EntryWith:Colon"{"Value"}
+ "Entry With Spaces"{"Value"}
+}
diff --git a/src/Symfony/Component/Intl/Tests/ResourceBundle/Writer/TextBundleWriterTest.php b/src/Symfony/Component/Intl/Tests/ResourceBundle/Writer/TextBundleWriterTest.php
index cbe0c8d8bf..f42b2738d7 100644
--- a/src/Symfony/Component/Intl/Tests/ResourceBundle/Writer/TextBundleWriterTest.php
+++ b/src/Symfony/Component/Intl/Tests/ResourceBundle/Writer/TextBundleWriterTest.php
@@ -36,7 +36,7 @@ class TextBundleWriterTest extends \PHPUnit_Framework_TestCase
protected function setUp()
{
$this->writer = new TextBundleWriter();
- $this->directory = sys_get_temp_dir() . '/TextBundleWriterTest/' . rand(1000, 9999);
+ $this->directory = sys_get_temp_dir().'/TextBundleWriterTest/'.rand(1000, 9999);
$this->filesystem = new Filesystem();
$this->filesystem->mkdir($this->directory);
@@ -54,6 +54,9 @@ class TextBundleWriterTest extends \PHPUnit_Framework_TestCase
'Array' => array('foo', 'bar', array('Key' => 'value')),
'Integer' => 5,
'IntVector' => array(0, 1, 2, 3),
+ 'NotAnIntVector' => array(0 => 0, 2 => 1, 1 => 2, 3 => 3),
+ 'IntVectorWithStringKeys' => array('a' => 0, 'b' => 1, 'c' => 2),
+ 'TableWithIntKeys' => array(0 => 0, 1 => 1, 3 => 3),
'FalseBoolean' => false,
'TrueBoolean' => true,
'Null' => null,
@@ -62,6 +65,51 @@ class TextBundleWriterTest extends \PHPUnit_Framework_TestCase
'Entry2' => 'String',
));
- $this->assertFileEquals(__DIR__ . '/Fixtures/en.txt', $this->directory . '/en.txt');
+ $this->assertFileEquals(__DIR__.'/Fixtures/en.txt', $this->directory.'/en.txt');
+ }
+
+ public function testWriteTraversable()
+ {
+ $this->writer->write($this->directory, 'en', new \ArrayIterator(array(
+ 'Entry1' => new \ArrayIterator(array(
+ 'Array' => array('foo', 'bar', array('Key' => 'value')),
+ 'Integer' => 5,
+ 'IntVector' => array(0, 1, 2, 3),
+ 'NotAnIntVector' => array(0 => 0, 2 => 1, 1 => 2, 3 => 3),
+ 'IntVectorWithStringKeys' => array('a' => 0, 'b' => 1, 'c' => 2),
+ 'TableWithIntKeys' => array(0 => 0, 1 => 1, 3 => 3),
+ 'FalseBoolean' => false,
+ 'TrueBoolean' => true,
+ 'Null' => null,
+ 'Float' => 1.23,
+ )),
+ 'Entry2' => 'String',
+ )));
+
+ $this->assertFileEquals(__DIR__.'/Fixtures/en.txt', $this->directory.'/en.txt');
+ }
+
+ public function testWriteNoFallback()
+ {
+ $data = array(
+ 'Entry' => 'Value'
+ );
+
+ $this->writer->write($this->directory, 'en_nofallback', $data, $fallback = false);
+
+ $this->assertFileEquals(__DIR__.'/Fixtures/en_nofallback.txt', $this->directory.'/en_nofallback.txt');
+ }
+
+ public function testEscapeKeysIfNecessary()
+ {
+ $this->writer->write($this->directory, 'escaped', array(
+ // Keys with colons must be escaped, otherwise the part after the
+ // colon is interpreted as resource type
+ 'EntryWith:Colon' => 'Value',
+ // Keys with spaces must be escaped
+ 'Entry With Spaces' => 'Value',
+ ));
+
+ $this->assertFileEquals(__DIR__.'/Fixtures/escaped.txt', $this->directory.'/escaped.txt');
}
}
diff --git a/src/Symfony/Component/Process/PhpExecutableFinder.php b/src/Symfony/Component/Process/PhpExecutableFinder.php
index 9001f41095..7854487a26 100644
--- a/src/Symfony/Component/Process/PhpExecutableFinder.php
+++ b/src/Symfony/Component/Process/PhpExecutableFinder.php
@@ -29,13 +29,15 @@ class PhpExecutableFinder
/**
* Finds The PHP executable.
*
+ * @param bool $includeArgs Whether or not include command arguments
+ *
* @return string|false The PHP executable path or false if it cannot be found
*/
- public function find()
+ public function find($includeArgs = true)
{
// HHVM support
- if (defined('HHVM_VERSION') && false !== $hhvm = getenv('PHP_BINARY')) {
- return $hhvm;
+ if (defined('HHVM_VERSION')) {
+ return (false !== ($hhvm = getenv('PHP_BINARY')) ? $hhvm : PHP_BINARY).($includeArgs ? ' '.implode(' ', $this->findArguments()) : '');
}
// PHP_BINARY return the current sapi executable
@@ -64,4 +66,21 @@ class PhpExecutableFinder
return $this->executableFinder->find('php', false, $dirs);
}
+
+ /**
+ * Finds the PHP executable arguments.
+ *
+ * @return array The PHP executable arguments
+ */
+ public function findArguments()
+ {
+ $arguments = array();
+
+ // HHVM support
+ if (defined('HHVM_VERSION')) {
+ $arguments[] = '--php';
+ }
+
+ return $arguments;
+ }
}
diff --git a/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php b/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php
index df48c4f556..e16f3f8bed 100644
--- a/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php
+++ b/src/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php
@@ -34,10 +34,43 @@ class PhpExecutableFinderTest extends \PHPUnit_Framework_TestCase
//not executable PHP_PATH
putenv('PHP_PATH=/not/executable/php');
$this->assertFalse($f->find(), '::find() returns false for not executable PHP');
+ $this->assertFalse($f->find(false), '::find() returns false for not executable PHP');
//executable PHP_PATH
putenv('PHP_PATH='.$current);
$this->assertEquals($f->find(), $current, '::find() returns the executable PHP');
+ $this->assertEquals($f->find(false), $current, '::find() returns the executable PHP');
+ }
+
+ /**
+ * tests find() with the env var PHP_PATH
+ */
+ public function testFindWithHHVM()
+ {
+ if (!defined('HHVM_VERSION')) {
+ $this->markTestSkipped('Should be executed in HHVM context.');
+ }
+
+ $f = new PhpExecutableFinder();
+
+ $current = $f->find();
+
+ $this->assertEquals($f->find(), $current.' --php', '::find() returns the executable PHP');
+ $this->assertEquals($f->find(false), $current, '::find() returns the executable PHP');
+ }
+
+ /**
+ * tests find() with the env var PHP_PATH
+ */
+ public function testFindArguments()
+ {
+ $f = new PhpExecutableFinder();
+
+ if (defined('HHVM_VERSION')) {
+ $this->assertEquals($f->findArguments(), array('--php'), '::findArguments() returns HHVM arguments');
+ } else {
+ $this->assertEquals($f->findArguments(), array(), '::findArguments() returns no arguments');
+ }
}
/**
diff --git a/src/Symfony/Component/Security/Acl/Domain/DoctrineAclCache.php b/src/Symfony/Component/Security/Acl/Domain/DoctrineAclCache.php
index 9e14af5652..0c69773c47 100644
--- a/src/Symfony/Component/Security/Acl/Domain/DoctrineAclCache.php
+++ b/src/Symfony/Component/Security/Acl/Domain/DoctrineAclCache.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Security\Acl\Domain;
use Doctrine\Common\Cache\Cache;
+use Doctrine\Common\Cache\CacheProvider;
use Symfony\Component\Security\Acl\Model\AclCacheInterface;
use Symfony\Component\Security\Acl\Model\AclInterface;
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
@@ -55,7 +56,9 @@ class DoctrineAclCache implements AclCacheInterface
*/
public function clearCache()
{
- $this->cache->deleteByPrefix($this->prefix);
+ if ($this->cache instanceof CacheProvider) {
+ $this->cache->deleteAll();
+ }
}
/**
diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php
index b0557a3dcb..0db50cf7c2 100644
--- a/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php
+++ b/src/Symfony/Component/Security/Core/Tests/Authorization/AccessDecisionManagerTest.php
@@ -73,6 +73,48 @@ class AccessDecisionManagerTest extends \PHPUnit_Framework_TestCase
$this->assertSame($expected, $manager->decide($token, array('ROLE_FOO')));
}
+ /**
+ * @dataProvider getStrategiesWith2RolesTests
+ */
+ public function testStrategiesWith2Roles($token, $strategy, $voter, $expected)
+ {
+ $manager = new AccessDecisionManager(array($voter), $strategy);
+
+ $this->assertSame($expected, $manager->decide($token, array('ROLE_FOO', 'ROLE_BAR')));
+ }
+
+ public function getStrategiesWith2RolesTests()
+ {
+ $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
+
+ return array(
+ array($token, 'affirmative', $this->getVoter(VoterInterface::ACCESS_DENIED), false),
+ array($token, 'affirmative', $this->getVoter(VoterInterface::ACCESS_GRANTED), true),
+
+ array($token, 'consensus', $this->getVoter(VoterInterface::ACCESS_DENIED), false),
+ array($token, 'consensus', $this->getVoter(VoterInterface::ACCESS_GRANTED), true),
+
+ array($token, 'unanimous', $this->getVoterFor2Roles($token, VoterInterface::ACCESS_DENIED, VoterInterface::ACCESS_DENIED), false),
+ array($token, 'unanimous', $this->getVoterFor2Roles($token, VoterInterface::ACCESS_DENIED, VoterInterface::ACCESS_GRANTED), false),
+ array($token, 'unanimous', $this->getVoterFor2Roles($token, VoterInterface::ACCESS_GRANTED, VoterInterface::ACCESS_DENIED), false),
+ array($token, 'unanimous', $this->getVoterFor2Roles($token, VoterInterface::ACCESS_GRANTED, VoterInterface::ACCESS_GRANTED), true),
+ );
+ }
+
+ protected function getVoterFor2Roles($token, $vote1, $vote2)
+ {
+ $voter = $this->getMock('Symfony\Component\Security\Core\Authorization\Voter\VoterInterface');
+ $voter->expects($this->exactly(2))
+ ->method('vote')
+ ->will($this->returnValueMap(array(
+ array($token, null, array("ROLE_FOO"),$vote1),
+ array($token, null, array("ROLE_BAR"),$vote2),
+ )))
+ ;
+
+ return $voter;
+ }
+
public function getStrategyTests()
{
return array(
diff --git a/src/Symfony/Component/Security/Core/Tests/Util/StringUtilsTest.php b/src/Symfony/Component/Security/Core/Tests/Util/StringUtilsTest.php
index 89da98de66..e0366a52c1 100644
--- a/src/Symfony/Component/Security/Core/Tests/Util/StringUtilsTest.php
+++ b/src/Symfony/Component/Security/Core/Tests/Util/StringUtilsTest.php
@@ -13,11 +13,49 @@ namespace Symfony\Component\Security\Core\Tests\Util;
use Symfony\Component\Security\Core\Util\StringUtils;
+/**
+ * Data from PHP.net's hash_equals tests
+ */
class StringUtilsTest extends \PHPUnit_Framework_TestCase
{
- public function testEquals()
+ public function dataProviderTrue()
{
- $this->assertTrue(StringUtils::equals('password', 'password'));
- $this->assertFalse(StringUtils::equals('password', 'foo'));
+ return array(
+ array('same', 'same'),
+ array('', ''),
+ array(123, 123),
+ array(null, ''),
+ array(null, null),
+ );
+ }
+
+ public function dataProviderFalse()
+ {
+ return array(
+ array('not1same', 'not2same'),
+ array('short', 'longer'),
+ array('longer', 'short'),
+ array('', 'notempty'),
+ array('notempty', ''),
+ array(123, 'NaN'),
+ array('NaN', 123),
+ array(null, 123),
+ );
+ }
+
+ /**
+ * @dataProvider dataProviderTrue
+ */
+ public function testEqualsTrue($known, $user)
+ {
+ $this->assertTrue(StringUtils::equals($known, $user));
+ }
+
+ /**
+ * @dataProvider dataProviderFalse
+ */
+ public function testEqualsFalse($known, $user)
+ {
+ $this->assertFalse(StringUtils::equals($known, $user));
}
}
diff --git a/src/Symfony/Component/Security/Core/Util/StringUtils.php b/src/Symfony/Component/Security/Core/Util/StringUtils.php
index 5e130375b7..acf8e9eed8 100644
--- a/src/Symfony/Component/Security/Core/Util/StringUtils.php
+++ b/src/Symfony/Component/Security/Core/Util/StringUtils.php
@@ -27,6 +27,7 @@ class StringUtils
* Compares two strings.
*
* This method implements a constant-time algorithm to compare strings.
+ * Regardless of the used implementation, it will leak length information.
*
* @param string $knownString The string of known length to compare against
* @param string $userInput The string that the user can control
@@ -35,6 +36,13 @@ class StringUtils
*/
public static function equals($knownString, $userInput)
{
+ $knownString = (string) $knownString;
+ $userInput = (string) $userInput;
+
+ if (function_exists('hash_equals')) {
+ return hash_equals($knownString, $userInput);
+ }
+
$knownLen = strlen($knownString);
$userLen = strlen($userInput);
@@ -45,7 +53,7 @@ class StringUtils
$result = $knownLen - $userLen;
// Note that we ALWAYS iterate over the user-supplied length
- // This is to prevent leaking length information
+ // This is to mitigate leaking length information
for ($i = 0; $i < $userLen; $i++) {
$result |= (ord($knownString[$i]) ^ ord($userInput[$i]));
}
diff --git a/src/Symfony/Component/Translation/Dumper/XliffFileDumper.php b/src/Symfony/Component/Translation/Dumper/XliffFileDumper.php
index 96c0c8a387..9b3237fda1 100644
--- a/src/Symfony/Component/Translation/Dumper/XliffFileDumper.php
+++ b/src/Symfony/Component/Translation/Dumper/XliffFileDumper.php
@@ -47,8 +47,11 @@ class XliffFileDumper extends FileDumper
$s = $translation->appendChild($dom->createElement('source'));
$s->appendChild($dom->createTextNode($source));
+ // Does the target contain characters requiring a CDATA section?
+ $text = 1 === preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target);
+
$t = $translation->appendChild($dom->createElement('target'));
- $t->appendChild($dom->createTextNode($target));
+ $t->appendChild($text);
$xliffBody->appendChild($translation);
}
diff --git a/src/Symfony/Component/Translation/Tests/Dumper/XliffFileDumperTest.php b/src/Symfony/Component/Translation/Tests/Dumper/XliffFileDumperTest.php
index bef31358c5..5eddb56968 100644
--- a/src/Symfony/Component/Translation/Tests/Dumper/XliffFileDumperTest.php
+++ b/src/Symfony/Component/Translation/Tests/Dumper/XliffFileDumperTest.php
@@ -19,13 +19,20 @@ class XliffFileDumperTest extends \PHPUnit_Framework_TestCase
public function testDump()
{
$catalogue = new MessageCatalogue('en');
- $catalogue->add(array('foo' => 'bar', 'key' => ''));
+ $catalogue->add(array(
+ 'foo' => 'bar',
+ 'key' => '',
+ 'key.with.cdata' => ' & ',
+ ));
$tempDir = sys_get_temp_dir();
$dumper = new XliffFileDumper();
$dumper->dump($catalogue, array('path' => $tempDir));
- $this->assertEquals(file_get_contents(__DIR__.'/../fixtures/resources-clean.xlf'), file_get_contents($tempDir.'/messages.en.xlf'));
+ $this->assertSame(
+ file_get_contents(__DIR__.'/../fixtures/resources-clean.xlf'),
+ file_get_contents($tempDir.'/messages.en.xlf')
+ );
unlink($tempDir.'/messages.en.xlf');
}
diff --git a/src/Symfony/Component/Translation/Tests/fixtures/resources-clean.xlf b/src/Symfony/Component/Translation/Tests/fixtures/resources-clean.xlf
index 464b079200..c5f83efe60 100644
--- a/src/Symfony/Component/Translation/Tests/fixtures/resources-clean.xlf
+++ b/src/Symfony/Component/Translation/Tests/fixtures/resources-clean.xlf
@@ -10,6 +10,10 @@
key
+
+ key.with.cdata
+ & ]]>
+