Merge branch '2.3' into 2.4

* 2.3: (35 commits)
  [Form] Fix PHPDoc for builder setData methods The underlying data variable is typed as mixed whereas the methods paramers where typed as array.
  fixed CS
  [Intl] Improved bundle reader implementations
  [Console] guarded against invalid aliases
  switch before_script to before_install and script to install
  fixed typo
  [HttpFoundation] Request - URI - comment improvements
  [Security] Added more tests
  remove `service` parameter type from XSD
  [Intl] Added exception handler to command line scripts
  [Intl] Fixed a few bugs in TextBundleWriter
  [Intl] Updated icu.ini up to ICU 53
  [Intl] Removed non-working $fallback argument from ArrayAccessibleResourceBundle
  Use separated function to resolve command and related arguments
  [SwiftmailerBridge] Bump allowed versions of swiftmailer
  [FrameworkBundle] Remove invalid markup
  [Intl] Added "internal" tag to all classes under Symfony\Component\Intl\ResourceBundle
  Remove routes for removed WebProfiler actions
  [Security] Fix usage of unexistent method in DoctrineAclCache.
  backport more error information from 2.6 to 2.3
  ...

Conflicts:
	.travis.yml
	src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
	src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
	src/Symfony/Component/HttpKernel/Kernel.php
	src/Symfony/Component/Process/PhpExecutableFinder.php
This commit is contained in:
Fabien Potencier 2014-09-17 11:45:32 +02:00
commit 1be7acd100
101 changed files with 1431 additions and 442 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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": ""

View File

@ -101,15 +101,34 @@ EOF
->locateResource(sprintf('@FrameworkBundle/Resources/config/router_%s.php', $env))
;
if (!file_exists($router)) {
$output->writeln(sprintf('<error>The given router script "%s" does not exist</error>', $router));
return 1;
}
$router = realpath($router);
$output->writeln(sprintf("Server running on <info>http://%s</info>\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('<error>Built-in server terminated unexpectedly</error>');
if (OutputInterface::VERBOSITY_VERBOSE > $output->getVerbosity()) {
$output->writeln('<error>Run the command again with -v option for more details</error>');
}
}
return $process->getExitCode();
}
}

View File

@ -1,10 +1,6 @@
<div <?php echo $view['form']->block($form, 'widget_container_attributes') ?>>
<?php if (!$form->parent && $errors): ?>
<tr>
<td colspan="2">
<?php echo $view['form']->errors($form) ?>
</td>
</tr>
<?php echo $view['form']->errors($form) ?>
<?php endif ?>
<?php echo $view['form']->block($form, 'form_rows') ?>
<?php echo $view['form']->rest($form) ?>

View File

@ -1,6 +1,10 @@
<table <?php echo $view['form']->block($form, 'widget_container_attributes') ?>>
<?php if (!$form->parent): ?>
<?php echo $view['form']->errors($form) ?>
<?php if (!$form->parent && $errors): ?>
<tr>
<td colspan="2">
<?php echo $view['form']->errors($form) ?>
</td>
</tr>
<?php endif ?>
<?php echo $view['form']->block($form, 'form_rows') ?>
<?php echo $view['form']->rest($form) ?>

View File

@ -24,14 +24,6 @@
<default key="_controller">web_profiler.controller.profiler:infoAction</default>
</route>
<route id="_profiler_import" pattern="/import">
<default key="_controller">web_profiler.controller.profiler:importAction</default>
</route>
<route id="_profiler_export" pattern="/export/{token}.txt">
<default key="_controller">web_profiler.controller.profiler:exportAction</default>
</route>
<route id="_profiler_phpinfo" pattern="/phpinfo">
<default key="_controller">web_profiler.controller.profiler:phpinfoAction</default>
</route>

View File

@ -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);
}

View File

@ -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)
{

View File

@ -156,7 +156,6 @@
<xsd:simpleType name="parameter_type">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="collection" />
<xsd:enumeration value="service" />
<xsd:enumeration value="string" />
<xsd:enumeration value="constant" />
</xsd:restriction>

View File

@ -23,7 +23,6 @@
<parameter>bar</parameter>
</parameter>
</parameter>
<parameter key="foo_bar" type="service" id="foo_bar" />
<parameter key="MixedCase" type="collection"> <!-- Should be lower cased -->
<parameter key="MixedCaseKey">value</parameter> <!-- Should stay mixed case -->
</parameter>

View File

@ -0,0 +1,4 @@
services:
method_call1:
class: FooClass
calls: foo

View File

@ -0,0 +1,2 @@
imports:
- foo.yml

View File

@ -0,0 +1,2 @@
imports:
foo:bar

View File

@ -0,0 +1,2 @@
parameters:
foo:bar

View File

@ -0,0 +1,2 @@
services:
foo: bar

View File

@ -0,0 +1 @@
services: foo

View File

@ -0,0 +1,6 @@
services:
foo_service:
class: FooClass
tags:
# tag is not an array
- foo

View File

@ -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%',

View File

@ -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'));

View File

@ -417,7 +417,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
*
* This method should not be invoked.
*
* @param array $data
* @param mixed $data
*
* @throws BadMethodCallException
*/

View File

@ -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;

View File

@ -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());
}
/**

View File

@ -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.
*/

View File

@ -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'),

View File

@ -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]'),

View File

@ -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()
*

View File

@ -236,9 +236,9 @@ class Esi
throw new \RuntimeException('Unable to process an ESI tag without a "src" attribute.');
}
return sprintf('<?php echo $this->esi->handle($this, \'%s\', \'%s\', %s) ?>'."\n",
$options['src'],
isset($options['alt']) ? $options['alt'] : null,
return sprintf('<?php echo $this->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'
);
}

View File

@ -103,6 +103,11 @@ class EsiTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('foo <?php echo $this->esi->handle($this, \'...\', \'alt\', true) ?>'."\n", $response->getContent());
$this->assertEquals('ESI', $response->headers->get('x-body-eval'));
$response = new Response('foo <esi:comment text="some comment" /><esi:include src="foo\'" alt="bar\'" onerror="continue" />');
$esi->process($request, $response);
$this->assertEquals("foo <?php echo \$this->esi->handle(\$this, 'foo\\'', 'bar\\'', true) ?>"."\n", $response->getContent());
$response = new Response('foo <esi:include src="..." />');
$esi->process($request, $response);

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 <bschussek@gmail.com>
*/
class MissingResourceException extends RuntimeException
{
}

View File

@ -0,0 +1,19 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 <bschussek@gmail.com>
*/
class ResourceBundleNotFoundException extends RuntimeException
{
}

View File

@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 <bschussek@gmail.com>
*/
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)));
}
}

View File

@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 <bschussek@gmail.com>
*
* @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() {}
}

View File

@ -17,6 +17,8 @@ use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReaderInterface
* Base class for {@link ResourceBundleInterface} implementations.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
abstract class AbstractBundle implements ResourceBundleInterface
{

View File

@ -17,6 +17,8 @@ use Symfony\Component\Intl\Exception\RuntimeException;
* Compiles .txt resource bundles to binary .res files.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class BundleCompiler implements BundleCompilerInterface
{

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle\Compiler;
* Compiles a resource bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
interface BundleCompilerInterface
{

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle;
* Default implementation of {@link CurrencyBundleInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class CurrencyBundle extends AbstractBundle implements CurrencyBundleInterface
{

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle;
* Default implementation of {@link LanguageBundleInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class LanguageBundle extends AbstractBundle implements LanguageBundleInterface
{

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle;
* Default implementation of {@link LocaleBundleInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class LocaleBundle extends AbstractBundle implements LocaleBundleInterface
{

View File

@ -1,42 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 <bschussek@gmail.com>
*/
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();
}

View File

@ -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 <bschussek@gmail.com>
*
* @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;
}
}

View File

@ -15,6 +15,8 @@ use Symfony\Component\Intl\ResourceBundle\Util\RingBuffer;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class BufferedBundleReader implements BundleReaderInterface
{

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle\Reader;
* Reads resource bundle files.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
interface BundleReaderInterface
{

View File

@ -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 <bschussek@gmail.com>
*
* @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;
}
}

View File

@ -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 <bschussek@gmail.com>
*
* @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);
}
}

View File

@ -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 <bschussek@gmail.com>
*
* @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);
}

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle;
* Default implementation of {@link RegionBundleInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class RegionBundle extends AbstractBundle implements RegionBundleInterface
{

View File

@ -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 <bschussek@gmail.com>
*
* @internal
*/
class BundleTransformer
{

View File

@ -18,6 +18,8 @@ use Symfony\Component\Intl\ResourceBundle\Compiler\BundleCompilerInterface;
* Default implementation of {@link CompilationContextInterface}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class CompilationContext implements CompilationContextInterface
{

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle\Transformer;
* Stores contextual information for resource bundle compilation.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
interface CompilationContextInterface
{

View File

@ -21,6 +21,8 @@ use Symfony\Component\Intl\Util\IcuVersion;
* The rule for compiling the currency bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class CurrencyBundleTransformationRule implements TransformationRuleInterface
{

View File

@ -20,6 +20,8 @@ use Symfony\Component\Intl\Util\IcuVersion;
* The rule for compiling the language bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class LanguageBundleTransformationRule implements TransformationRuleInterface
{

View File

@ -21,6 +21,8 @@ use Symfony\Component\Intl\ResourceBundle\Writer\TextBundleWriter;
* The rule for compiling the locale bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class LocaleBundleTransformationRule implements TransformationRuleInterface
{

View File

@ -20,6 +20,8 @@ use Symfony\Component\Intl\Util\IcuVersion;
* The rule for compiling the region bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class RegionBundleTransformationRule implements TransformationRuleInterface
{

View File

@ -18,6 +18,8 @@ use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface;
* Contains instruction for compiling a resource bundle.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
interface TransformationRuleInterface
{

View File

@ -15,6 +15,8 @@ use Symfony\Component\Filesystem\Filesystem;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class StubbingContext implements StubbingContextInterface
{

View File

@ -13,6 +13,8 @@ namespace Symfony\Component\Intl\ResourceBundle\Transformer;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
interface StubbingContextInterface
{

View File

@ -20,6 +20,8 @@ use Symfony\Component\Intl\Exception\BadMethodCallException;
* This class can be removed once that bug is fixed.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @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)

View File

@ -11,19 +11,35 @@
namespace Symfony\Component\Intl\ResourceBundle\Util;
use Symfony\Component\Intl\Exception\OutOfBoundsException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @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;

View File

@ -21,6 +21,8 @@ use Symfony\Component\Intl\Exception\OutOfBoundsException;
* then the second and so on.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class RingBuffer implements \ArrayAccess
{

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle\Writer;
* Writes resource bundle files.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
interface BundleWriterInterface
{

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\Intl\ResourceBundle\Writer;
* Writes .php resource bundles.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class PhpBundleWriter implements BundleWriterInterface
{

View File

@ -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 <bschussek@gmail.com>
*
* @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);

View File

@ -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;
}
});

View File

@ -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

View File

@ -1,64 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 <bschussek@gmail.com>
*/
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));
}
}

View File

@ -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');
}
}

View File

@ -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

View File

@ -0,0 +1,14 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
return array(
'Foo' => 'Bar',
);

View File

@ -0,0 +1,3 @@
alias{
"%%ALIAS"{"ro"}
}

View File

@ -0,0 +1,3 @@
mo{
"%%ALIAS"{"ro_MD"}
}

View File

@ -0,0 +1,3 @@
ro{
Foo{"Bar"}
}

View File

@ -0,0 +1,3 @@
ro_MD{
Baz{"Bam"}
}

View File

@ -0,0 +1,6 @@
root{
/**
* so genrb doesn't issue warnings
*/
___{""}
}

View File

@ -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');
}
}

View File

@ -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));
}
}

View File

@ -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{""}

View File

@ -0,0 +1,3 @@
en_nofallback:table(nofallback){
Entry{"Value"}
}

View File

@ -0,0 +1,4 @@
escaped{
"EntryWith:Colon"{"Value"}
"Entry With Spaces"{"Value"}
}

View File

@ -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');
}
}

View File

@ -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;
}
}

View File

@ -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');
}
}
/**

View File

@ -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();
}
}
/**

View File

@ -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(

View File

@ -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));
}
}

View File

@ -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]));
}

View File

@ -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);
}

View File

@ -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' => '<source> & <target>',
));
$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');
}

View File

@ -10,6 +10,10 @@
<source>key</source>
<target></target>
</trans-unit>
<trans-unit id="18e6a493872558d949b4c16ea1fa6ab6" resname="key.with.cdata">
<source>key.with.cdata</source>
<target><![CDATA[<source> & <target>]]></target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -26,13 +26,13 @@ class Escaper
// first to ensure proper escaping because str_replace operates iteratively
// on the input arrays. This ordering of the characters avoids the use of strtr,
// which performs more slowly.
private static $escapees = array('\\\\', '\\"', '"',
private static $escapees = array('\\', '\\\\', '\\"', '"',
"\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07",
"\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f",
"\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17",
"\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f",
"\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9");
private static $escaped = array('\\"', '\\\\', '\\"',
private static $escaped = array('\\\\', '\\"', '\\\\', '\\"',
"\\0", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\a",
"\\b", "\\t", "\\n", "\\v", "\\f", "\\r", "\\x0e", "\\x0f",
"\\x10", "\\x11", "\\x12", "\\x13", "\\x14", "\\x15", "\\x16", "\\x17",

View File

@ -392,7 +392,7 @@ class Inline
*
* @return string A YAML string
*
* @throws ParseException when object parsing support was disabled and the parser detected a PHP object
* @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved
*/
private static function evaluateScalar($scalar, $references = array())
{
@ -406,6 +406,11 @@ class Inline
$value = substr($scalar, 1);
}
// an unquoted *
if (false === $value || '' === $value) {
throw new ParseException('A reference must contain at least one character.');
}
if (!array_key_exists($value, $references)) {
throw new ParseException(sprintf('Reference "%s" does not exist.', $value));
}

View File

@ -93,7 +93,7 @@ class Parser
$c = $this->getRealCurrentLineNb() + 1;
$parser = new Parser($c);
$parser->refs =& $this->refs;
$data[] = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport);
$data[] = $parser->parse($this->getNextEmbedBlock(null, true), $exceptionOnInvalidType, $objectSupport);
} else {
if (isset($values['leadspaces'])
&& ' ' == $values['leadspaces']
@ -281,15 +281,20 @@ class Parser
/**
* Returns the next embed block of YAML.
*
* @param int $indentation The indent level at which the block is to be read, or null for default
* @param int $indentation The indent level at which the block is to be read, or null for default
* @param bool $inSequence True if the enclosing data structure is a sequence
*
* @return string A YAML string
*
* @throws ParseException When indentation problem are detected
*/
private function getNextEmbedBlock($indentation = null)
private function getNextEmbedBlock($indentation = null, $inSequence = false)
{
$this->moveToNextLine();
$oldLineIndentation = $this->getCurrentLineIndentation();
if (!$this->moveToNextLine()) {
return;
}
if (null === $indentation) {
$newIndent = $this->getCurrentLineIndentation();
@ -305,6 +310,14 @@ class Parser
$data = array(substr($this->currentLine, $newIndent));
if ($inSequence && $oldLineIndentation === $newIndent && '-' === $data[0][0]) {
// the previous line contained a dash but no item content, this line is a sequence item with the same indentation
// and therefore no nested list or mapping
$this->moveToPreviousLine();
return;
}
$isItUnindentedCollection = $this->isStringUnIndentedCollectionItem($this->currentLine);
// Comments must not be removed inside a string block (ie. after a line ending with "|")

View File

@ -199,6 +199,37 @@ EOF;
{
$this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, true, false);
}
/**
* @dataProvider getEscapeSequences
*/
public function testEscapedEscapeSequencesInQuotedScalar($input, $expected)
{
$this->assertEquals($expected, $this->dumper->dump($input));
}
public function getEscapeSequences()
{
return array(
'null' => array("\t\\0", '"\t\\\\0"'),
'bell' => array("\t\\a", '"\t\\\\a"'),
'backspace' => array("\t\\b", '"\t\\\\b"'),
'horizontal-tab' => array("\t\\t", '"\t\\\\t"'),
'line-feed' => array("\t\\n", '"\t\\\\n"'),
'vertical-tab' => array("\t\\v", '"\t\\\\v"'),
'form-feed' => array("\t\\f", '"\t\\\\f"'),
'carriage-return' => array("\t\\r", '"\t\\\\r"'),
'escape' => array("\t\\e", '"\t\\\\e"'),
'space' => array("\t\\ ", '"\t\\\\ "'),
'double-quote' => array("\t\\\"", '"\t\\\\\\""'),
'slash' => array("\t\\/", '"\t\\\\/"'),
'backslash' => array("\t\\\\", '"\t\\\\\\\\"'),
'next-line' => array("\t\\N", '"\t\\\\N"'),
'non-breaking-space' => array("\t\\<EFBFBD>", '"\t\\\\<5C>"'),
'line-separator' => array("\t\\L", '"\t\\\\L"'),
'paragraph-separator' => array("\t\\P", '"\t\\\\P"'),
);
}
}
class A

View File

@ -11,6 +11,30 @@ yaml: |
php: |
array('apple', 'banana', 'carrot')
---
test: Sequence With Item Being Null In The Middle
brief: |
You can specify a list in YAML by placing each
member of the list on a new line with an opening
dash. These lists are called sequences.
yaml: |
- apple
-
- carrot
php: |
array('apple', null, 'carrot')
---
test: Sequence With Last Item Being Null
brief: |
You can specify a list in YAML by placing each
member of the list on a new line with an opening
dash. These lists are called sequences.
yaml: |
- apple
- banana
-
php: |
array('apple', 'banana', null)
---
test: Nested Sequences
brief: |
You can include a sequence within another

Some files were not shown because too many files have changed in this diff Show More