diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index c9c3b75340557..d35bbdeb69e23 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,10 +1,10 @@ | Q | A | ------------- | --- -| Branch? | master / 2.7, 2.8 or 3.2 +| Branch? | master / 2.7, 2.8 or 3.2 | Bug fix? | yes/no -| New feature? | yes/no +| New feature? | yes/no | BC breaks? | yes/no -| Deprecations? | yes/no +| Deprecations? | yes/no | Tests pass? | yes/no | Fixed tickets | #... | License | MIT diff --git a/.travis.yml b/.travis.yml index 93ec75cd0a4e8..7205200d85e1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ env: matrix: include: # Use the newer stack for HHVM as HHVM does not support Precise anymore since a long time and so Precise has an outdated version - - php: hhvm-3.15 + - php: hhvm-3.18 sudo: required dist: trusty group: edge @@ -50,6 +50,8 @@ before_install: - if [[ ! $skip ]]; then echo date.timezone = Europe/Paris >> $INI_FILE; fi - if [[ ! $skip ]]; then echo memory_limit = -1 >> $INI_FILE; fi - if [[ ! $skip ]]; then echo session.gc_probability = 0 >> $INI_FILE; fi + - if [[ ! $skip ]]; then echo opcache.enable_cli = 1 >> $INI_FILE; fi + - if [[ ! $skip ]]; then echo hhvm.jit = 0 >> $INI_FILE; fi - if [[ ! $skip && $PHP = 5.* ]]; then echo extension = mongo.so >> $INI_FILE; fi - if [[ ! $skip && $PHP = 5.* ]]; then echo extension = memcache.so >> $INI_FILE; fi - if [[ ! $skip && $PHP = 5.* ]]; then (echo yes | pecl install -f apcu-4.0.11 && echo apc.enable_cli = 1 >> $INI_FILE); fi @@ -77,7 +79,7 @@ install: - export COMPOSER_ROOT_VERSION=$SYMFONY_VERSION.x-dev - if [[ ! $skip && $deps ]]; then export SYMFONY_DEPRECATIONS_HELPER=weak; fi - if [[ ! $skip && $deps ]]; then mv composer.json.phpunit composer.json; fi - - if [[ ! $skip ]]; then composer update; fi + - if [[ ! $skip ]]; then composer update --no-suggest; fi - if [[ ! $skip ]]; then ./phpunit install; fi - if [[ ! $skip && ! $PHP = hhvm* ]]; then php -i; else hhvm --php -r 'print_r($_SERVER);print_r(ini_get_all());'; fi @@ -88,5 +90,5 @@ script: - if [[ ! $deps && ! $PHP = hhvm* ]]; then echo -e "\\nRunning tests requiring tty"; $PHPUNIT --group tty; fi - if [[ ! $deps && $PHP = hhvm* ]]; then $PHPUNIT --exclude-group benchmark,intl-data; fi - if [[ ! $deps && $PHP = ${MIN_PHP%.*} ]]; then echo -e "1\\n0" | xargs -I{} sh -c 'echo "\\nPHP --enable-sigchild enhanced={}" && ENHANCE_SIGCHLD={} php-$MIN_PHP/sapi/cli/php .phpunit/phpunit-4.8/phpunit --colors=always src/Symfony/Component/Process/'; fi - - if [[ $deps = high ]]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer update --no-progress --ansi; $PHPUNIT --exclude-group tty,benchmark,intl-data'$LEGACY"$REPORT"; fi - - if [[ $deps = low ]]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer update --no-progress --ansi --prefer-lowest --prefer-stable; $PHPUNIT --exclude-group tty,benchmark,intl-data'"$REPORT"; fi + - if [[ $deps = high ]]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer update --no-progress --no-suggest --ansi; $PHPUNIT --exclude-group tty,benchmark,intl-data'$LEGACY"$REPORT"; fi + - if [[ $deps = low ]]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer update --no-progress --no-suggest --ansi --prefer-lowest --prefer-stable; $PHPUNIT --exclude-group tty,benchmark,intl-data'"$REPORT"; fi diff --git a/CHANGELOG-2.7.md b/CHANGELOG-2.7.md index 5e137e4366aa7..bc4c0fa89d5fc 100644 --- a/CHANGELOG-2.7.md +++ b/CHANGELOG-2.7.md @@ -7,6 +7,37 @@ in 2.7 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.7.0...v2.7.1 +* 2.7.26 (2017-04-04) + + * bug #22229 [ExpressionLanguage] Provide the expression in syntax errors (k0pernikus, stof) + * bug #22240 [DI] Fix fatal error at ContainerBuilder::compile() if config is not installed (chalasr) + * bug #22140 [Form] Improve the exceptions when trying to get the data in a PRE_SET_DATA listener and the data has not already been set (fancyweb) + * bug #22217 [Console] Fix table cell styling (ro0NL) + * bug #22194 [Console] CommandTester: disable color support detection (julienfalque) + * bug #22188 [Console] Revised exception rendering (ro0NL) + * bug #22154 [WebProfilerBundle] Normalize whitespace in exceptions passed in headers (curry684) + * bug #22142 [Console] Escape exception messages in renderException (chalasr) + * bug #22172 Fix port usage in server:status command (alcaeus) + * bug #22164 [Bridge\Doctrine] Fix change breaking doctrine-bundle test suite (nicolas-grekas) + * bug #22133 [Filesystem] normalize paths before making them relative (xabbuh) + * bug #22138 [HttpFoundation][bugfix] $bags should always be initialized (MacDada) + * bug #21810 #21809 [SecurityBundle] bugfix: if security provider's name contains upper cases then container didn't compile (Antanas Arvasevicius) + * bug #19778 [Security] Fixed roles serialization on token from user object (eko) + * bug #22022 [Validator] fix URL validator to detect non supported chars according to RFC 3986 (e-moe) + * bug #21968 Fixed pathinfo calculation for requests starting with a question mark. (syzygymsu) + * bug #21846 [HttpFoundation] Fix Request::getHost() when having several hosts in X_FORWARDED_HOST (nicolas-grekas) + * bug #21208 [Validator] Add object handling of invalid constraints in Composite (SenseException) + * bug #22044 [Serializer] [XML] Ignore Process Instruction (jordscream) + * bug #22079 [HttpKernel] Fixed bug with purging of HTTPS URLs (ausi) + * bug #21523 #20411 fix Yaml parsing for very long quoted strings (RichardBradley) + * bug #22001 [Doctrine Bridge] fix priority for doctrine event listeners (dmaicher) + * bug #21981 [Console] Use proper line endings in BufferedOutput (julienfalque) + * bug #21957 [Form] Choice type int values (BC Fix) (mcfedr) + * bug #21923 [travis] Test with hhvm 3.18 (nicolas-grekas) + * bug #21823 dumpFile(), preserve existing file permissions (chs2) + * bug #21865 [Security] context listener: hardening user provider handling (xabbuh) + * bug #21883 [HttpKernel] fix Kernel name when stored in a directory starting with a number (fabpot) + * 2.7.25 (2017-03-06) * bug #21671 [Serializer] Xml encoder throws exception for valid data (gr1ev0us) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 73387b9f7bb1a..90b929e5efccb 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -23,25 +23,26 @@ Symfony is the result of the work of many people who made the code better - Pascal Borreli (pborreli) - Wouter De Jong (wouterj) - Romain Neutron (romain) + - Grégoire Pineau (lyrixx) - Joseph Bielawski (stloyd) - Karma Dordrak (drak) - Lukas Kahwe Smith (lsmith) - - Grégoire Pineau (lyrixx) - Martin Hasoň (hason) - - Jeremy Mikola (jmikola) + - Robin Chalas (chalas_r) - Maxime Steinhausser (ogizanagi) + - Jeremy Mikola (jmikola) - Jean-François Simon (jfsimon) - Benjamin Eberlei (beberlei) - Igor Wiedler (igorw) - - Robin Chalas (chalas_r) - Eriksen Costa (eriksencosta) - Jules Pietri (heah) - Sarah Khalil (saro0h) - Jonathan Wage (jwage) + - Guilhem Niot (energetick) - Diego Saint Esteben (dosten) + - Roland Franssen (ro0) - Alexandre Salomé (alexandresalome) - William Durand (couac) - - Guilhem Niot (energetick) - ornicar - Francis Besset (francisbesset) - stealth35 ‏ (stealth35) @@ -50,20 +51,20 @@ Symfony is the result of the work of many people who made the code better - Peter Rehm (rpet) - Saša Stamenković (umpirsky) - Henrik Bjørnskov (henrikbjorn) - - Roland Franssen (ro0) - Miha Vrhovnik - Iltar van der Berg (kjarli) - Diego Saint Esteben (dii3g0) - Konstantin Kudryashov (everzet) - Bilal Amarni (bamarni) - Florin Patan (florinpatan) + - Matthias Pigulla (mpdude) - Kevin Bond (kbond) - Andrej Hudec (pulzarraider) - Gábor Egyed (1ed) - Michel Weimerskirch (mweimerskirch) - Eric Clemmons (ericclemmons) - Charles Sarrazin (csarrazi) - - Matthias Pigulla (mpdude) + - Pierre du Plessis (pierredup) - Christian Raue - Arnout Boks (aboks) - Deni @@ -71,39 +72,38 @@ Symfony is the result of the work of many people who made the code better - Dariusz Górecki (canni) - Titouan Galopin (tgalopin) - Douglas Greenshields (shieldo) - - Pierre du Plessis (pierredup) - Konstantin Myakshin (koc) - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) + - Jáchym Toušek (enumag) - Graham Campbell (graham) - Daniel Holmes (dholmes) - Toni Uebernickel (havvg) - Bart van den Burg (burgov) - Jordan Alliot (jalliot) + - Jérémy DERUSSÉ (jderusse) - John Wards (johnwards) - Fran Moreno (franmomu) - - Jáchym Toušek (enumag) - Antoine Hérault (herzult) - Paráda József (paradajozsef) - Dariusz Ruminski - Arnaud Le Blanc (arnaud-lb) - Jérôme Tamarelle (gromnan) + - Maxime STEINHAUSSER - Michal Piotrowski (eventhorizon) - Tim Nagel (merk) - - Maxime STEINHAUSSER - Issei Murasawa (issei_m) - Brice BERNARD (brikou) - Alexander M. Turek (derrabus) + - Baptiste Clavié (talus) - marc.weistroff - lenar - Włodzimierz Gajda (gajdaw) - - Baptiste Clavié (talus) - Vladimir Reznichenko (kalessil) - Alexander Schwenn (xelaris) - Florian Voutzinos (florianv) - Colin Frei - - Jérémy DERUSSÉ (jderusse) - Adrien Brault (adrienbrault) - Joshua Thijssen - Peter Kokot (maastermedia) @@ -148,6 +148,7 @@ Symfony is the result of the work of many people who made the code better - Teoh Han Hui (teohhanhui) - Clemens Tolboom - Helmer Aaviksoo + - Grégoire Paris (greg0ire) - Hiromi Hishida (77web) - Richard van Laak (rvanlaak) - Matthieu Ouellette-Vachon (maoueh) @@ -160,7 +161,7 @@ Symfony is the result of the work of many people who made the code better - Warnar Boekkooi (boekkooi) - Dmitrii Chekaliuk (lazyhammer) - Clément JOBEILI (dator) - - Grégoire Paris (greg0ire) + - Dawid Nowak - Possum - Dorian Villet (gnutix) - Richard Miller (mr_r_miller) @@ -168,6 +169,7 @@ Symfony is the result of the work of many people who made the code better - Dennis Benkert (denderello) - Benjamin Dulau (dbenjamin) - Mathieu Lemoine (lemoinem) + - Chris Wilkinson (thewilkybarkid) - Andreas Hucks (meandmymonkey) - Noel Guilbert (noel) - Lars Strojny (lstrojny) @@ -191,16 +193,15 @@ Symfony is the result of the work of many people who made the code better - John Kary (johnkary) - Justin Hileman (bobthecow) - Blanchon Vincent (blanchonvincent) - - Chris Wilkinson (thewilkybarkid) - Christian Schmidt - Michele Orselli (orso) - Tom Van Looy (tvlooy) - Sven Paulus (subsven) - Rui Marinho (ruimarinho) - SpacePossum - - Dawid Nowak - Eugene Wissner - Julien Brochet (mewt) + - Julien Falque (julienfalque) - Tristan Darricau (nicofuma) - Sergey Linnik (linniksa) - Michaël Perrin (michael.perrin) @@ -211,6 +212,7 @@ Symfony is the result of the work of many people who made the code better - julien pauli (jpauli) - Lorenz Schori - Sébastien Lavoie (lavoiesl) + - David Maicher (dmaicher) - Francois Zaninotto - Alexander Kotynia (olden) - Daniel Tschinder @@ -236,7 +238,6 @@ Symfony is the result of the work of many people who made the code better - Uwe Jäger (uwej711) - Eugene Leonovich (rybakit) - Filippo Tessarotto - - Julien Falque (julienfalque) - Joseph Rouff (rouffj) - Félix Labrecque (woodspire) - GordonsLondon @@ -258,7 +259,6 @@ Symfony is the result of the work of many people who made the code better - Beau Simensen (simensen) - Michael Hirschler (mvhirsch) - Robert Kiss (kepten) - - David Maicher (dmaicher) - Roumen Damianoff (roumen) - Antonio J. García Lagar (ajgarlag) - Kim Hemsø Rasmussen (kimhemsoe) @@ -396,6 +396,7 @@ Symfony is the result of the work of many people who made the code better - EdgarPE - Florian Pfitzer (marmelatze) - Asier Illarramendi (doup) + - Andreas Braun - Chris Sedlmayr (catchamonkey) - Seb Koelen - Dany Maillard (maidmaid) @@ -415,6 +416,7 @@ Symfony is the result of the work of many people who made the code better - Gintautas Miselis - Rob Bast - David Badura (davidbadura) + - Jordan Samouh (jordansamouh) - Zander Baldwin - Adam Harvey - Alex Bakhturin @@ -459,11 +461,13 @@ Symfony is the result of the work of many people who made the code better - Jakub Škvára (jskvara) - Andrew Udvare (audvare) - alexpods + - Nikolay Labinskiy (e-moe) - Arjen van der Meijden - Michele Locati - Dariusz Ruminski - Erik Trapman (eriktrapman) - De Cock Xavier (xdecock) + - Arthur de Moulins (4rthem) - Almog Baku (almogbaku) - Scott Arciszewski - Norbert Orzechowicz (norzechowicz) @@ -510,8 +514,10 @@ Symfony is the result of the work of many people who made the code better - Disquedur - Michiel Boeckaert (milio) - Geoffrey Tran (geoff) + - Romain Pierre (romain-pierre) - Jan Behrens - Mantas Var (mvar) + - Frank de Jonge (frenkynet) - Sebastian Krebs - Jean-Christophe Cuvelier [Artack] - Christopher Davis (chrisguitarguy) @@ -539,8 +545,8 @@ Symfony is the result of the work of many people who made the code better - Maxime Douailin - Jean Pasdeloup (pasdeloup) - Benjamin Cremer (bcremer) + - Thierry Thuon (lepiaf) - Javier López (loalf) - - Andreas Braun - Reinier Kip - Geoffrey Brier (geoffrey-brier) - Dustin Dobervich (dustin10) @@ -553,6 +559,7 @@ Symfony is the result of the work of many people who made the code better - Kamil Kokot (pamil) - Aurimas Niekis (gcds) - Max Grigorian (maxakawizard) + - mcfedr (mcfedr) - Rostyslav Kinash - Maciej Malarz (malarzm) - Daisuke Ohata @@ -570,6 +577,7 @@ Symfony is the result of the work of many people who made the code better - Denis Brumann (dbrumann) - Quentin de Longraye (quentinus95) - Chris Heng (gigablah) + - Richard Bradley - Ulumuddin Yunus (joenoez) - Luc Vieillescazes (iamluc) - Johann Saunier (prophet777) @@ -629,6 +637,7 @@ Symfony is the result of the work of many people who made the code better - Besnik Br - Dariusz Ruminski - Joshua Nye + - Claudio Zizza - Dave Marshall (davedevelopment) - avorobiev - Venu @@ -647,6 +656,7 @@ Symfony is the result of the work of many people who made the code better - John Bohn (jbohn) - Marc Morera (mmoreram) - Andrew Hilobok (hilobok) + - Noah Heck (myesain) - Christian Soronellas (theunic) - Yosmany Garcia (yosmanyga) - Wouter de Wild @@ -654,17 +664,18 @@ Symfony is the result of the work of many people who made the code better - Degory Valentine - Benoit Lévêque (benoit_leveque) - Jeroen Fiege (fieg) - - Arthur de Moulins (4rthem) - Krzysiek Łabuś - Xavier Lacot (xavier) - possum - Denis Zunke (donalberto) + - Ahmed TAILOULOUTE (ahmedtai) - Olivier Maisonneuve (olineuve) - Masterklavi - Francis Turmel (fturmel) - Nikita Nefedov (nikita2206) - cgonzalez - Ben + - Vincent Composieux (eko) - Jayson Xu (superjavason) - Jaik Dean (jaikdean) - fago @@ -675,6 +686,7 @@ Symfony is the result of the work of many people who made the code better - James Michael DuPont - Tom Klingenberg - Christopher Hall (mythmakr) + - Patrick Dawkins (pjcdawkins) - Paul Kamer (pkamer) - Rafał Wrzeszcz (rafalwrzeszcz) - Reen Lokum @@ -720,6 +732,7 @@ Symfony is the result of the work of many people who made the code better - corphi - grizlik - Derek ROTH + - Dmytro Boiko (eagle) - Shin Ohno (ganchiku) - Geert De Deckere (geertdd) - Jan Kramer (jankramer) @@ -782,7 +795,6 @@ Symfony is the result of the work of many people who made the code better - Phan Thanh Ha (haphan) - Chris Jones (leek) - Colin O'Dell (colinodell) - - Frank de Jonge (frenkynet) - xaav - Mahmoud Mostafa (mahmoud) - Alessandro Lai @@ -808,7 +820,6 @@ Symfony is the result of the work of many people who made the code better - Zachary Tong (polyfractal) - Hryhorii Hrebiniuk - Dennis Fridrich (dfridrich) - - mcfedr (mcfedr) - hamza - dantleech - Bastien DURAND (deamon) @@ -841,6 +852,7 @@ Symfony is the result of the work of many people who made the code better - Troy McCabe - Ville Mattila - ilyes kooli + - gr1ev0us - Boris Vujicic (boris.vujicic) - Max Beutel - Antanas Arvasevicius @@ -864,10 +876,12 @@ Symfony is the result of the work of many people who made the code better - Martynas Narbutas - Bailey Parker - Eddie Jaoude + - Antanas Arvasevicius - Haritz Iturbe (hizai) - Nerijus Arlauskas (nercury) - SPolischook - Diego Sapriza + - Anton A. Sumin - Joan Cruz - inspiran - Cristobal Dabed @@ -964,6 +978,7 @@ Symfony is the result of the work of many people who made the code better - Aharon Perkel - matze - Abdul.Mohsen B. A. A + - Martin Auswöger - Benoît Burnichon - pthompson - Malaney J. Hill @@ -1009,7 +1024,9 @@ Symfony is the result of the work of many people who made the code better - Klaas Cuvelier (kcuvelier) - markusu49 - Steve Frécinaux + - Jules Lamur - ShiraNai7 + - Markus Fasselt (digilist) - Vašek Purchart (vasek-purchart) - Janusz Jabłoński (yanoosh) - Sandro Hopf @@ -1034,17 +1051,16 @@ Symfony is the result of the work of many people who made the code better - Luis Galeas - Martin Pärtel - George Mponos (gmponos) - - Noah Heck (myesain) - Patrick Daley (padrig) - Xavier Briand (xavierbriand) - Max Summe - WedgeSama - Felds Liscia - - Ahmed TAILOULOUTE (ahmedtai) - Maxime Veber (nek-) - Sullivan SENECHAL - Tadcka - Beth Binkovitz + - Gonzalo Míguez - Romain Geissler - Adrien Moiruad - Tomaz Ahlin @@ -1104,7 +1120,6 @@ Symfony is the result of the work of many people who made the code better - Konrad Mohrfeldt - Lance Chen - Andrew (drew) - - Nikolay Labinskiy (e-moe) - kor3k kor3k (kor3k) - Stelian Mocanita (stelian) - Flavian (2much) @@ -1143,11 +1158,9 @@ Symfony is the result of the work of many people who made the code better - victoria - Francisco Facioni (fran6co) - Iwan van Staveren (istaveren) - - Thierry Thuon (lepiaf) - Povilas S. (povilas) - pborreli - Eric Caron - - Richard Bradley - 2manypeople - Wing - Thomas Bibb @@ -1171,6 +1184,7 @@ Symfony is the result of the work of many people who made the code better - Michal Gebauer - Gleb Sidora - David Stone + - Niels Keurentjes (curry684) - Jovan Perovic (jperovic) - Pablo Maria Martelletti (pmartelletti) - Yassine Guedidi (yguedidi) @@ -1228,6 +1242,7 @@ Symfony is the result of the work of many people who made the code better - Brian Graham (incognito) - Kevin Vergauwen (innocenzo) - Alessio Baglio (ioalessio) + - Johannes Müller (johmue) - Jordi Llonch (jordillonch) - Cédric Dugat (ph3nol) - Philip Dahlstrøm (phidah) @@ -1289,6 +1304,7 @@ Symfony is the result of the work of many people who made the code better - Jérémy M (th3mouk) - Vincent LEFORT (vlefort) - Sadicov Vladimir (xtech) + - Kevin EMO (zarcox) - Alexander Zogheb - Rémi Blaise - Joel Marcey @@ -1301,7 +1317,6 @@ Symfony is the result of the work of many people who made the code better - adenkejawen - Ari Pringle (apringle) - Dan Ordille (dordille) - - Dmytro Boiko (eagle) - Jan Eichhorn (exeu) - Grégory Pelletier (ip512) - John Nickell (jrnickell) @@ -1350,7 +1365,6 @@ Symfony is the result of the work of many people who made the code better - ddebree - Tomas Liubinas - Alex - - Patrick Dawkins - Klaas Naaijkens - Daniel González Cerviño - Rafał @@ -1363,7 +1377,6 @@ Symfony is the result of the work of many people who made the code better - David Joos (djoos) - Denis Klementjev (dklementjev) - Tomáš Polívka (draczris) - - Vincent Composieux (eko) - Franz Liedke (franzliedke) - Christophe BECKER (goabonga) - gondo (gondo) @@ -1399,6 +1412,7 @@ Symfony is the result of the work of many people who made the code better - Curtis - Gabriel Moreira - Alexey Popkov + - ChS - Joseph Deray - Damian Sromek - Ben @@ -1437,6 +1451,7 @@ Symfony is the result of the work of many people who made the code better - znerol - Christian Eikermann - Antonio Angelino + - Matt Fields - Shawn Iwinski - Niklas Keller - Vladimir Sazhin @@ -1562,6 +1577,7 @@ Symfony is the result of the work of many people who made the code better - ibasaw (ibasaw) - Vladislav Krupenkin (ideea) - Imangazaliev Muhammad (imangazaliev) + - j0k (j0k) - joris de wit (jdewit) - Jérémy CROMBEZ (jeremy) - Jose Manuel Gonzalez (jgonzalez) diff --git a/README.md b/README.md index 36a466f2c8f04..1ec5983c5ca21 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Community --------- * [Join the Symfony Community][11] and meet other members at the [Symfony events][12]. -* [Get Symfony support][13] on StackOverflow, Slack, IRC, etc. +* [Get Symfony support][13] on Stack Overflow, Slack, IRC, etc. * Follow us on [GitHub][14], [Twitter][15] and [Facebook][16]. Contributing diff --git a/appveyor.yml b/appveyor.yml index 5f3953ed8acca..86a943c77c42e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -53,7 +53,7 @@ install: - copy /Y .composer\* %APPDATA%\Composer\ - php .github/build-packages.php "HEAD^" src\Symfony\Bridge\PhpUnit - IF %APPVEYOR_REPO_BRANCH%==master (SET COMPOSER_ROOT_VERSION=dev-master) ELSE (SET COMPOSER_ROOT_VERSION=%APPVEYOR_REPO_BRANCH%.x-dev) - - php composer.phar update --no-progress --ansi + - php composer.phar update --no-progress --no-suggest --ansi - php phpunit install test_script: diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php index 94f72fd8c8c05..cb4a4816db70a 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php @@ -11,27 +11,30 @@ namespace Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; /** * Registers event listeners and subscribers to the available doctrine connections. * * @author Jeremy Mikola * @author Alexander + * @author David Maicher */ class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface { + /** + * @var string|string[] + */ private $connections; - private $container; private $eventManagers; private $managerTemplate; private $tagPrefix; /** - * Constructor. - * * @param string $connections Parameter ID for connections * @param string $managerTemplate sprintf() template for generating the event * manager's service ID for a connection name @@ -53,105 +56,109 @@ public function process(ContainerBuilder $container) return; } - $taggedSubscribers = $container->findTaggedServiceIds($this->tagPrefix.'.event_subscriber'); - $taggedListeners = $container->findTaggedServiceIds($this->tagPrefix.'.event_listener'); - - if (empty($taggedSubscribers) && empty($taggedListeners)) { - return; - } - - $this->container = $container; $this->connections = $container->getParameter($this->connections); - $sortFunc = function ($a, $b) { - $a = isset($a['priority']) ? $a['priority'] : 0; - $b = isset($b['priority']) ? $b['priority'] : 0; - - return $a > $b ? -1 : 1; - }; + $this->addTaggedSubscribers($container); + $this->addTaggedListeners($container); + } - if (!empty($taggedSubscribers)) { - $subscribersPerCon = $this->groupByConnection($taggedSubscribers); - foreach ($subscribersPerCon as $con => $subscribers) { - $em = $this->getEventManager($con); + private function addTaggedSubscribers(ContainerBuilder $container) + { + $subscriberTag = $this->tagPrefix.'.event_subscriber'; + $taggedSubscribers = $this->findAndSortTags($subscriberTag, $container); - uasort($subscribers, $sortFunc); - foreach ($subscribers as $id => $instance) { - if ($container->getDefinition($id)->isAbstract()) { - throw new \InvalidArgumentException(sprintf('The abstract service "%s" cannot be tagged as a doctrine event subscriber.', $id)); - } + foreach ($taggedSubscribers as $taggedSubscriber) { + $id = $taggedSubscriber[0]; + $taggedSubscriberDef = $container->getDefinition($id); - $em->addMethodCall('addEventSubscriber', array(new Reference($id))); - } + if ($taggedSubscriberDef->isAbstract()) { + throw new InvalidArgumentException(sprintf('The abstract service "%s" cannot be tagged as a doctrine event subscriber.', $id)); } - } - if (!empty($taggedListeners)) { - $listenersPerCon = $this->groupByConnection($taggedListeners, true); - foreach ($listenersPerCon as $con => $listeners) { - $em = $this->getEventManager($con); - - uasort($listeners, $sortFunc); - foreach ($listeners as $id => $instance) { - if ($container->getDefinition($id)->isAbstract()) { - throw new \InvalidArgumentException(sprintf('The abstract service "%s" cannot be tagged as a doctrine event listener.', $id)); - } - - $em->addMethodCall('addEventListener', array( - array_unique($instance['event']), - isset($instance['lazy']) && $instance['lazy'] ? $id : new Reference($id), - )); + $tag = $taggedSubscriber[1]; + $connections = isset($tag['connection']) ? array($tag['connection']) : array_keys($this->connections); + foreach ($connections as $con) { + if (!isset($this->connections[$con])) { + throw new RuntimeException(sprintf('The Doctrine connection "%s" referenced in service "%s" does not exist. Available connections names: %s', $con, $taggedSubscriber, implode(', ', array_keys($this->connections)))); } + + $this->getEventManagerDef($container, $con)->addMethodCall('addEventSubscriber', array(new Reference($id))); } } } - private function groupByConnection(array $services, $isListener = false) + private function addTaggedListeners(ContainerBuilder $container) { - $grouped = array(); - foreach ($allCons = array_keys($this->connections) as $con) { - $grouped[$con] = array(); - } + $listenerTag = $this->tagPrefix.'.event_listener'; + $taggedListeners = $this->findAndSortTags($listenerTag, $container); + + foreach ($taggedListeners as $taggedListener) { + $id = $taggedListener[0]; + $taggedListenerDef = $container->getDefinition($taggedListener[0]); + if ($taggedListenerDef->isAbstract()) { + throw new InvalidArgumentException(sprintf('The abstract service "%s" cannot be tagged as a doctrine event listener.', $id)); + } - foreach ($services as $id => $instances) { - foreach ($instances as $instance) { - if ($isListener) { - if (!isset($instance['event'])) { - throw new \InvalidArgumentException(sprintf('Doctrine event listener "%s" must specify the "event" attribute.', $id)); - } - $instance['event'] = array($instance['event']); - - if (isset($instance['lazy']) && $instance['lazy']) { - $this->container->getDefinition($id)->setPublic(true); - } + $tag = $taggedListener[1]; + if (!isset($tag['event'])) { + throw new InvalidArgumentException(sprintf('Doctrine event listener "%s" must specify the "event" attribute.', $id)); + } + + $connections = isset($tag['connection']) ? array($tag['connection']) : array_keys($this->connections); + foreach ($connections as $con) { + if (!isset($this->connections[$con])) { + throw new RuntimeException(sprintf('The Doctrine connection "%s" referenced in service "%s" does not exist. Available connections names: %s', $con, $id, implode(', ', array_keys($this->connections)))); } - $cons = isset($instance['connection']) ? array($instance['connection']) : $allCons; - foreach ($cons as $con) { - if (!isset($grouped[$con])) { - throw new \RuntimeException(sprintf('The Doctrine connection "%s" referenced in service "%s" does not exist. Available connections names: %s', $con, $id, implode(', ', array_keys($this->connections)))); - } - - if ($isListener && isset($grouped[$con][$id])) { - $grouped[$con][$id]['event'] = array_merge($grouped[$con][$id]['event'], $instance['event']); - } else { - $grouped[$con][$id] = $instance; - } + if ($lazy = !empty($tag['lazy'])) { + $taggedListenerDef->setPublic(true); } + + // we add one call per event per service so we have the correct order + $this->getEventManagerDef($container, $con)->addMethodCall('addEventListener', array(array($tag['event']), $lazy ? $id : new Reference($id))); } } + } - return $grouped; + private function getEventManagerDef(ContainerBuilder $container, $name) + { + if (!isset($this->eventManagers[$name])) { + $this->eventManagers[$name] = $container->getDefinition(sprintf($this->managerTemplate, $name)); + } + + return $this->eventManagers[$name]; } - private function getEventManager($name) + /** + * Finds and orders all service tags with the given name by their priority. + * + * The order of additions must be respected for services having the same priority, + * and knowing that the \SplPriorityQueue class does not respect the FIFO method, + * we should not use this class. + * + * @see https://bugs.php.net/bug.php?id=53710 + * @see https://bugs.php.net/bug.php?id=60926 + * + * @param string $tagName + * @param ContainerBuilder $container + * + * @return array + */ + private function findAndSortTags($tagName, ContainerBuilder $container) { - if (null === $this->eventManagers) { - $this->eventManagers = array(); - foreach ($this->connections as $n => $id) { - $this->eventManagers[$n] = $this->container->getDefinition(sprintf($this->managerTemplate, $n)); + $sortedTags = array(); + + foreach ($container->findTaggedServiceIds($tagName) as $serviceId => $tags) { + foreach ($tags as $attributes) { + $priority = isset($attributes['priority']) ? $attributes['priority'] : 0; + $sortedTags[$priority][] = array($serviceId, $attributes); } } - return $this->eventManagers[$name]; + if ($sortedTags) { + krsort($sortedTags); + $sortedTags = call_user_func_array('array_merge', $sortedTags); + } + + return $sortedTags; } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php index 757fdc07e6f35..7e99a7d9356c2 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php @@ -15,6 +15,7 @@ use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterEventListenersAndSubscribersPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; class RegisterEventListenersAndSubscribersPassTest extends TestCase { @@ -56,12 +57,18 @@ public function testProcessEventListenersWithPriorities() $container ->register('a', 'stdClass') + ->setPublic(false) + ->addTag('doctrine.event_listener', array( + 'event' => 'bar', + )) ->addTag('doctrine.event_listener', array( 'event' => 'foo', 'priority' => -5, )) ->addTag('doctrine.event_listener', array( - 'event' => 'bar', + 'event' => 'foo_bar', + 'priority' => 3, + 'lazy' => true, )) ; $container @@ -70,12 +77,34 @@ public function testProcessEventListenersWithPriorities() 'event' => 'foo', )) ; + $container + ->register('c', 'stdClass') + ->addTag('doctrine.event_listener', array( + 'event' => 'foo_bar', + 'priority' => 4, + )) + ; $this->process($container); - $this->assertEquals(array('b', 'a'), $this->getServiceOrder($container, 'addEventListener')); - - $calls = $container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls(); - $this->assertEquals(array('foo', 'bar'), $calls[1][1][0]); + $methodCalls = $container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls(); + + $this->assertEquals( + array( + array('addEventListener', array(array('foo_bar'), new Reference('c'))), + array('addEventListener', array(array('foo_bar'), new Reference('a'))), + array('addEventListener', array(array('bar'), new Reference('a'))), + array('addEventListener', array(array('foo'), new Reference('b'))), + array('addEventListener', array(array('foo'), new Reference('a'))), + ), + $methodCalls + ); + + // not lazy so must be reference + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Reference', $methodCalls[0][1][1]); + + // lazy so id instead of reference and must mark service public + $this->assertSame('a', $methodCalls[1][1][1]); + $this->assertTrue($container->getDefinition('a')->isPublic()); } public function testProcessEventListenersWithMultipleConnections() @@ -88,15 +117,86 @@ public function testProcessEventListenersWithMultipleConnections() 'event' => 'onFlush', )) ; + + $container + ->register('b', 'stdClass') + ->addTag('doctrine.event_listener', array( + 'event' => 'onFlush', + 'connection' => 'default', + )) + ; + + $container + ->register('c', 'stdClass') + ->addTag('doctrine.event_listener', array( + 'event' => 'onFlush', + 'connection' => 'second', + )) + ; + $this->process($container); - $callsDefault = $container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls(); + $this->assertEquals( + array( + array('addEventListener', array(array('onFlush'), new Reference('a'))), + array('addEventListener', array(array('onFlush'), new Reference('b'))), + ), + $container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls() + ); + + $this->assertEquals( + array( + array('addEventListener', array(array('onFlush'), new Reference('a'))), + array('addEventListener', array(array('onFlush'), new Reference('c'))), + ), + $container->getDefinition('doctrine.dbal.second_connection.event_manager')->getMethodCalls() + ); + } - $this->assertEquals('addEventListener', $callsDefault[0][0]); - $this->assertEquals(array('onFlush'), $callsDefault[0][1][0]); + public function testProcessEventSubscribersWithMultipleConnections() + { + $container = $this->createBuilder(true); - $callsSecond = $container->getDefinition('doctrine.dbal.second_connection.event_manager')->getMethodCalls(); - $this->assertEquals($callsDefault, $callsSecond); + $container + ->register('a', 'stdClass') + ->addTag('doctrine.event_subscriber', array( + 'event' => 'onFlush', + )) + ; + + $container + ->register('b', 'stdClass') + ->addTag('doctrine.event_subscriber', array( + 'event' => 'onFlush', + 'connection' => 'default', + )) + ; + + $container + ->register('c', 'stdClass') + ->addTag('doctrine.event_subscriber', array( + 'event' => 'onFlush', + 'connection' => 'second', + )) + ; + + $this->process($container); + + $this->assertEquals( + array( + array('addEventSubscriber', array(new Reference('a'))), + array('addEventSubscriber', array(new Reference('b'))), + ), + $container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls() + ); + + $this->assertEquals( + array( + array('addEventSubscriber', array(new Reference('a'))), + array('addEventSubscriber', array(new Reference('c'))), + ), + $container->getDefinition('doctrine.dbal.second_connection.event_manager')->getMethodCalls() + ); } public function testProcessEventSubscribersWithPriorities() @@ -133,11 +233,17 @@ public function testProcessEventSubscribersWithPriorities() ; $this->process($container); - $serviceOrder = $this->getServiceOrder($container, 'addEventSubscriber'); - $unordered = array_splice($serviceOrder, 0, 3); - sort($unordered); - $this->assertEquals(array('c', 'd', 'e'), $unordered); - $this->assertEquals(array('b', 'a'), $serviceOrder); + + $this->assertEquals( + array( + array('addEventSubscriber', array(new Reference('c'))), + array('addEventSubscriber', array(new Reference('d'))), + array('addEventSubscriber', array(new Reference('e'))), + array('addEventSubscriber', array(new Reference('b'))), + array('addEventSubscriber', array(new Reference('a'))), + ), + $container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls() + ); } public function testProcessNoTaggedServices() @@ -157,26 +263,6 @@ private function process(ContainerBuilder $container) $pass->process($container); } - private function getServiceOrder(ContainerBuilder $container, $method) - { - $order = array(); - foreach ($container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls() as $call) { - list($name, $arguments) = $call; - if ($method !== $name) { - continue; - } - - if ('addEventListener' === $name) { - $order[] = (string) $arguments[1]; - continue; - } - - $order[] = (string) $arguments[0]; - } - - return $order; - } - private function createBuilder($multipleConnections = false) { $container = new ContainerBuilder(); diff --git a/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php b/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php index 89ac999f20301..1c9c82bc129e6 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php @@ -25,5 +25,7 @@ public function testConstruct() { $connection = $this->getMockBuilder('Doctrine\DBAL\Connection')->disableOriginalConstructor()->getMock(); $handler = new DbalSessionHandler($connection); + + $this->assertInstanceOf('Symfony\Bridge\Doctrine\HttpFoundation\DbalSessionHandler', $handler); } } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php index 2951e4471f731..8abcc8cd5677b 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php @@ -118,36 +118,36 @@ protected function setTheme(FormView $view, array $themes) public function testRange() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testRangeWithMinMaxValues() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testLabelWithoutTranslationOnButton() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testSingleChoiceWithPlaceholderWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testButtonlabelWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testAttributesNotTranslatedWhenTranslationDomainIsFalse() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php index d26714530c954..0236dd76e1e46 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php @@ -118,36 +118,36 @@ protected function setTheme(FormView $view, array $themes) public function testRange() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testRangeWithMinMaxValues() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testLabelWithoutTranslationOnButton() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testSingleChoiceWithPlaceholderWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testButtonlabelWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testAttributesNotTranslatedWhenTranslationDomainIsFalse() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php index 7e4c55e984b4a..d07e585a89234 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php @@ -105,7 +105,11 @@ public function testThemeBlockInheritanceUsingDynamicExtend() $renderer = $this->extension->renderer; $renderer->setTheme($view, array('page_dynamic_extends.html.twig')); - $renderer->searchAndRenderBlock($view, 'row'); + + $this->assertMatchesXpath( + $renderer->searchAndRenderBlock($view, 'row'), + '/div/label[text()="child"]' + ); } public function isSelectedChoiceProvider() @@ -211,36 +215,36 @@ public static function themeInheritanceProvider() public function testRange() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testRangeWithMinMaxValues() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testLabelWithoutTranslationOnButton() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testSingleChoiceWithPlaceholderWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testButtonlabelWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testAttributesNotTranslatedWhenTranslationDomainIsFalse() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php index 6509c405e7bc9..9c95db6d5fd9d 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php @@ -119,36 +119,36 @@ protected function setTheme(FormView $view, array $themes) public function testRange() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testRangeWithMinMaxValues() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testLabelWithoutTranslationOnButton() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testSingleChoiceWithPlaceholderWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testButtonlabelWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testAttributesNotTranslatedWhenTranslationDomainIsFalse() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php index fa5c537a0c97a..58871896c651d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php @@ -31,7 +31,7 @@ protected function configure() { $this ->setDefinition(array( - new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1:8000'), + new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1'), new InputOption('port', 'p', InputOption::VALUE_REQUIRED, 'Address port number', '8000'), )) ->setName('server:status') diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index bc1eac93e6dac..a782a3f0748bb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -224,7 +224,7 @@ public function testTranslator() $this->assertEquals('translator.default', (string) $container->getAlias('translator'), '->registerTranslatorConfiguration() redefines translator service from identity to real translator'); $options = $container->getDefinition('translator.default')->getArgument(3); - $files = array_map(function ($resource) { return realpath($resource); }, $options['resource_files']['en']); + $files = array_map('realpath', $options['resource_files']['en']); $ref = new \ReflectionClass('Symfony\Component\Validator\Validation'); $this->assertContains( strtr(dirname($ref->getFileName()).'/Resources/translations/validators.en.xlf', '/', DIRECTORY_SEPARATOR), diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php index 415e031189c3a..166d8a33919df 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php @@ -134,18 +134,4 @@ public function getConfigs() array('config.yml', false), ); } - - protected function setUp() - { - parent::setUp(); - - $this->deleteTmpDir('SessionTest'); - } - - protected function tearDown() - { - parent::tearDown(); - - $this->deleteTmpDir('SessionTest'); - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/WebTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/WebTestCase.php index 50d4cfa4db269..2861297fe0256 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/WebTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/WebTestCase.php @@ -13,7 +13,6 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as BaseWebTestCase; use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\HttpKernel\Kernel; class WebTestCase extends BaseWebTestCase { @@ -23,9 +22,19 @@ public static function assertRedirect($response, $location) self::assertEquals('http://localhost'.$location, $response->headers->get('Location')); } - protected function deleteTmpDir($testCase) + public static function setUpBeforeClass() { - if (!file_exists($dir = sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$testCase)) { + static::deleteTmpDir(); + } + + public static function tearDownAfterClass() + { + static::deleteTmpDir(); + } + + protected static function deleteTmpDir() + { + if (!file_exists($dir = sys_get_temp_dir().'/'.static::getVarDir())) { return; } @@ -49,10 +58,16 @@ protected static function createKernel(array $options = array()) } return new $class( + static::getVarDir(), $options['test_case'], isset($options['root_config']) ? $options['root_config'] : 'config.yml', - isset($options['environment']) ? $options['environment'] : 'frameworkbundletest'.strtolower($options['test_case']), + isset($options['environment']) ? $options['environment'] : strtolower(static::getVarDir().$options['test_case']), isset($options['debug']) ? $options['debug'] : true ); } + + protected static function getVarDir() + { + return substr(strrchr(get_called_class(), '\\'), 1); + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php index b07d44fe22be5..8e14b53a7472d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php @@ -46,14 +46,16 @@ */ class AppKernel extends Kernel { + private $varDir; private $testCase; private $rootConfig; - public function __construct($testCase, $rootConfig, $environment, $debug) + public function __construct($varDir, $testCase, $rootConfig, $environment, $debug) { if (!is_dir(__DIR__.'/'.$testCase)) { throw new \InvalidArgumentException(sprintf('The test case "%s" does not exist.', $testCase)); } + $this->varDir = $varDir; $this->testCase = $testCase; $fs = new Filesystem(); @@ -81,12 +83,12 @@ public function getRootDir() public function getCacheDir() { - return sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$this->testCase.'/cache/'.$this->environment; + return sys_get_temp_dir().'/'.$this->varDir.'/'.$this->testCase.'/cache/'.$this->environment; } public function getLogDir() { - return sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$this->testCase.'/logs'; + return sys_get_temp_dir().'/'.$this->varDir.'/'.$this->testCase.'/logs'; } public function registerContainerConfiguration(LoaderInterface $loader) @@ -96,13 +98,13 @@ public function registerContainerConfiguration(LoaderInterface $loader) public function serialize() { - return serialize(array($this->testCase, $this->rootConfig, $this->getEnvironment(), $this->isDebug())); + return serialize(array($this->varDir, $this->testCase, $this->rootConfig, $this->getEnvironment(), $this->isDebug())); } public function unserialize($str) { $a = unserialize($str); - $this->__construct($a[0], $a[1], $a[2], $a[3]); + $this->__construct($a[0], $a[1], $a[2], $a[3], $a[4]); } protected function getKernelParameters() diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php index 217fd5e50e237..c745818b1e72b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php @@ -131,36 +131,36 @@ public static function themeInheritanceProvider() public function testRange() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testRangeWithMinMaxValues() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testLabelWithoutTranslationOnButton() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testSingleChoiceWithPlaceholderWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testButtonlabelWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testAttributesNotTranslatedWhenTranslationDomainIsFalse() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php index 99bfba0c4e7cc..3448a56fa4d2d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php @@ -118,36 +118,36 @@ protected function setTheme(FormView $view, array $themes) public function testRange() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testRangeWithMinMaxValues() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testLabelWithoutTranslationOnButton() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testSingleChoiceWithPlaceholderWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testButtonlabelWithoutTranslation() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } public function testAttributesNotTranslatedWhenTranslationDomainIsFalse() { - // No-op for forward compatibility with AbstractLayoutTest 2.8 + $this->markTestIncomplete('No-op for forward compatibility with AbstractLayoutTest 2.8'); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/StopwatchHelperTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/StopwatchHelperTest.php index cf7b627a499a6..686c655aac8f2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/StopwatchHelperTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/StopwatchHelperTest.php @@ -30,11 +30,10 @@ public function testDevEnvironment() public function testProdEnvironment() { $helper = new StopwatchHelper(null); + $helper->start('foo'); - try { - $helper->start('foo'); - } catch (\BadMethodCallException $e) { - $this->fail('Assumed stopwatch is not called when not provided'); - } + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateTest.php index c49010bfdc357..1f6f51dd3351d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateTest.php @@ -19,21 +19,9 @@ class TemplateTest extends TestCase /** * @dataProvider getTemplateToPathProvider */ - public function testGetPathForTemplatesInABundle($template, $path) + public function testGetPathForTemplate($template, $path) { - if ($template->get('bundle')) { - $this->assertEquals($template->getPath(), $path); - } - } - - /** - * @dataProvider getTemplateToPathProvider - */ - public function testGetPathForTemplatesOutOfABundle($template, $path) - { - if (!$template->get('bundle')) { - $this->assertEquals($template->getPath(), $path); - } + $this->assertSame($template->getPath(), $path); } public function getTemplateToPathProvider() diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 36c16e0dbc7d9..2fd12c9436c39 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -509,7 +509,7 @@ private function createUserProviders($config, ContainerBuilder $container) // Parses a tag and returns the id for the related user provider service private function createUserDaoProvider($name, $provider, ContainerBuilder $container) { - $name = $this->getUserProviderId(strtolower($name)); + $name = $this->getUserProviderId($name); // Doctrine Entity and In-memory DAO provider are managed by factories foreach ($this->userProviderFactories as $factory) { @@ -533,7 +533,7 @@ private function createUserDaoProvider($name, $provider, ContainerBuilder $conta if (isset($provider['chain'])) { $providers = array(); foreach ($provider['chain']['providers'] as $providerName) { - $providers[] = new Reference($this->getUserProviderId(strtolower($providerName))); + $providers[] = new Reference($this->getUserProviderId($providerName)); } $container @@ -548,7 +548,7 @@ private function createUserDaoProvider($name, $provider, ContainerBuilder $conta private function getUserProviderId($name) { - return 'security.user.provider.concrete.'.$name; + return 'security.user.provider.concrete.'.strtolower($name); } private function createExceptionListener($container, $config, $id, $defaultEntryPoint, $stateless) diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php index ebb22fddac833..d273fb05942b3 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php @@ -73,7 +73,7 @@ public function testFirewalls() foreach (array_keys($arguments[1]) as $contextId) { $contextDef = $container->getDefinition($contextId); $arguments = $contextDef->getArguments(); - $listeners[] = array_map(function ($ref) { return (string) $ref; }, $arguments['index_0']); + $listeners[] = array_map('strval', $arguments['index_0']); } $this->assertEquals(array( diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php index 80211f5251b08..2d95d35065137 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php @@ -108,14 +108,4 @@ public function getConfigs() array('routes_as_path.yml'), ); } - - public static function setUpBeforeClass() - { - parent::deleteTmpDir('CsrfFormLogin'); - } - - public static function tearDownAfterClass() - { - parent::deleteTmpDir('CsrfFormLogin'); - } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FirewallEntryPointTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FirewallEntryPointTest.php index 30d0935ffddd7..8179c2e942a6c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FirewallEntryPointTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FirewallEntryPointTest.php @@ -43,14 +43,4 @@ public function testItUsesTheConfiguredEntryPointFromTheExceptionListenerWithFor "Custom entry point wasn't started" ); } - - public static function setUpBeforeClass() - { - parent::deleteTmpDir('FirewallEntryPoint'); - } - - public static function tearDownAfterClass() - { - parent::deleteTmpDir('FirewallEntryPoint'); - } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php index 2f19f3f8a1a9a..c2e766e9acca1 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php @@ -113,14 +113,4 @@ public function getConfigs() array('routes_as_path.yml'), ); } - - public static function setUpBeforeClass() - { - parent::deleteTmpDir('StandardFormLogin'); - } - - public static function tearDownAfterClass() - { - parent::deleteTmpDir('StandardFormLogin'); - } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php index 14c317966e21a..cb600764eaf42 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php @@ -76,14 +76,4 @@ public function getLocales() { return array(array('en'), array('de')); } - - public static function setUpBeforeClass() - { - parent::deleteTmpDir('StandardFormLogin'); - } - - public static function tearDownAfterClass() - { - parent::deleteTmpDir('StandardFormLogin'); - } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php index c7db437819a1c..3abfd9585f159 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php @@ -116,14 +116,4 @@ public function getConfigs() { return array(array('config.yml'), array('routes_as_path.yml')); } - - public static function setUpBeforeClass() - { - parent::deleteTmpDir('StandardFormLogin'); - } - - public static function tearDownAfterClass() - { - parent::deleteTmpDir('StandardFormLogin'); - } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php index db4c51c5f064d..039a5b325bad6 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php @@ -40,20 +40,6 @@ class SetAclCommandTest extends WebTestCase const OBJECT_CLASS = 'Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AclBundle\Entity\Car'; const SECURITY_CLASS = 'Symfony\Component\Security\Core\User\User'; - protected function setUp() - { - parent::setUp(); - - $this->deleteTmpDir('Acl'); - } - - protected function tearDown() - { - parent::tearDown(); - - $this->deleteTmpDir('Acl'); - } - public function testSetAclUser() { $objectId = 1; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php index e5079c3283aac..f12e95671be2c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php @@ -70,14 +70,4 @@ protected function createAuthenticatedClient($username) return $client; } - - public static function setUpBeforeClass() - { - parent::deleteTmpDir('StandardFormLogin'); - } - - public static function tearDownAfterClass() - { - parent::deleteTmpDir('StandardFormLogin'); - } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php index 33da9028a3daf..8bace799a37a8 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php @@ -13,7 +13,6 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as BaseWebTestCase; use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\HttpKernel\Kernel; class WebTestCase extends BaseWebTestCase { @@ -23,9 +22,19 @@ public static function assertRedirect($response, $location) self::assertEquals('http://localhost'.$location, $response->headers->get('Location')); } - protected static function deleteTmpDir($testCase) + public static function setUpBeforeClass() { - if (!file_exists($dir = sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$testCase)) { + static::deleteTmpDir(); + } + + public static function tearDownAfterClass() + { + static::deleteTmpDir(); + } + + protected static function deleteTmpDir() + { + if (!file_exists($dir = sys_get_temp_dir().'/'.static::getVarDir())) { return; } @@ -49,10 +58,16 @@ protected static function createKernel(array $options = array()) } return new $class( + static::getVarDir(), $options['test_case'], isset($options['root_config']) ? $options['root_config'] : 'config.yml', - isset($options['environment']) ? $options['environment'] : 'securitybundletest'.strtolower($options['test_case']), + isset($options['environment']) ? $options['environment'] : strtolower(static::getVarDir().$options['test_case']), isset($options['debug']) ? $options['debug'] : true ); } + + protected static function getVarDir() + { + return substr(strrchr(get_called_class(), '\\'), 1); + } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php index b828c5acfd91b..c448b7c172d60 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php @@ -46,14 +46,16 @@ */ class AppKernel extends Kernel { + private $varDir; private $testCase; private $rootConfig; - public function __construct($testCase, $rootConfig, $environment, $debug) + public function __construct($varDir, $testCase, $rootConfig, $environment, $debug) { if (!is_dir(__DIR__.'/'.$testCase)) { throw new \InvalidArgumentException(sprintf('The test case "%s" does not exist.', $testCase)); } + $this->varDir = $varDir; $this->testCase = $testCase; $fs = new Filesystem(); @@ -71,7 +73,7 @@ public function __construct($testCase, $rootConfig, $environment, $debug) public function getName() { if (null === $this->name) { - $this->name = parent::getName().md5($this->rootConfig); + $this->name = parent::getName().substr(md5($this->rootConfig), -16); } return $this->name; @@ -93,12 +95,12 @@ public function getRootDir() public function getCacheDir() { - return sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$this->testCase.'/cache/'.$this->environment; + return sys_get_temp_dir().'/'.$this->varDir.'/'.$this->testCase.'/cache/'.$this->environment; } public function getLogDir() { - return sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$this->testCase.'/logs'; + return sys_get_temp_dir().'/'.$this->varDir.'/'.$this->testCase.'/logs'; } public function registerContainerConfiguration(LoaderInterface $loader) @@ -108,13 +110,13 @@ public function registerContainerConfiguration(LoaderInterface $loader) public function serialize() { - return serialize(array($this->testCase, $this->rootConfig, $this->getEnvironment(), $this->isDebug())); + return serialize(array($this->varDir, $this->testCase, $this->rootConfig, $this->getEnvironment(), $this->isDebug())); } public function unserialize($str) { $a = unserialize($str); - $this->__construct($a[0], $a[1], $a[2], $a[3]); + $this->__construct($a[0], $a[1], $a[2], $a[3], $a[4]); } protected function getKernelParameters() diff --git a/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php b/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php index 2dc0b618d5302..317dcc9a44dda 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php +++ b/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php @@ -68,7 +68,7 @@ public function onKernelResponse(FilterResponseEvent $event) $this->urlGenerator->generate('_profiler', array('token' => $response->headers->get('X-Debug-Token')), UrlGeneratorInterface::ABSOLUTE_URL) ); } catch (\Exception $e) { - $response->headers->set('X-Debug-Error', get_class($e).': '.$e->getMessage()); + $response->headers->set('X-Debug-Error', get_class($e).': '.preg_replace('/\s+/', ' ', $e->getMessage())); } } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php index 89d6e932f118f..b730afe345aa4 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php @@ -245,6 +245,27 @@ public function testThrowingUrlGenerator() $this->assertEquals('Exception: foo', $response->headers->get('X-Debug-Error')); } + public function testThrowingErrorCleanup() + { + $response = new Response(); + $response->headers->set('X-Debug-Token', 'xxxxxxxx'); + + $urlGenerator = $this->getUrlGeneratorMock(); + $urlGenerator + ->expects($this->once()) + ->method('generate') + ->with('_profiler', array('token' => 'xxxxxxxx')) + ->will($this->throwException(new \Exception("This\nmultiline\r\ntabbed text should\tcome out\r on\n \ta single plain\r\nline"))) + ; + + $event = new FilterResponseEvent($this->getKernelMock(), $this->getRequestMock(), HttpKernelInterface::MASTER_REQUEST, $response); + + $listener = new WebDebugToolbarListener($this->getTwigMock(), false, WebDebugToolbarListener::ENABLED, 'bottom', $urlGenerator); + $listener->onKernelResponse($event); + + $this->assertEquals('Exception: This multiline tabbed text should come out on a single plain line', $response->headers->get('X-Debug-Error')); + } + protected function getRequestMock($isXmlHttpRequest = false, $requestFormat = 'html', $hasSession = true) { $request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')->setMethods(array('getSession', 'isXmlHttpRequest', 'getRequestFormat'))->disableOriginalConstructor()->getMock(); diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index a9351a589720c..bc5e9ee66c09b 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -13,6 +13,7 @@ use Symfony\Component\Console\Descriptor\TextDescriptor; use Symfony\Component\Console\Descriptor\XmlDescriptor; +use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Helper\DebugFormatterHelper; use Symfony\Component\Console\Helper\ProcessHelper; use Symfony\Component\Console\Helper\QuestionHelper; @@ -649,12 +650,11 @@ public function renderException($e, $output) if (defined('HHVM_VERSION') && $width > 1 << 31) { $width = 1 << 31; } - $formatter = $output->getFormatter(); $lines = array(); foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) { foreach ($this->splitStringByWidth($line, $width - 4) as $line) { // pre-format lines to get the right string length - $lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4; + $lineLength = $this->stringWidth($line) + 4; $lines[] = array($line, $lineLength); $len = max($lineLength, $len); @@ -662,15 +662,15 @@ public function renderException($e, $output) } $messages = array(); - $messages[] = $emptyLine = $formatter->format(sprintf('%s', str_repeat(' ', $len))); - $messages[] = $formatter->format(sprintf('%s%s', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title))))); + $messages[] = $emptyLine = sprintf('%s', str_repeat(' ', $len)); + $messages[] = sprintf('%s%s', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title)))); foreach ($lines as $line) { - $messages[] = $formatter->format(sprintf(' %s %s', $line[0], str_repeat(' ', $len - $line[1]))); + $messages[] = sprintf(' %s %s', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1])); } $messages[] = $emptyLine; $messages[] = ''; - $output->writeln($messages, OutputInterface::OUTPUT_RAW); + $output->writeln($messages); if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { $output->writeln('Exception trace:'); diff --git a/src/Symfony/Component/Console/Helper/Helper.php b/src/Symfony/Component/Console/Helper/Helper.php index 90979d5b9d5e3..b429e630c7217 100644 --- a/src/Symfony/Component/Console/Helper/Helper.php +++ b/src/Symfony/Component/Console/Helper/Helper.php @@ -109,6 +109,11 @@ public static function formatMemory($memory) } public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, $string) + { + return self::strlen(self::removeDecoration($formatter, $string)); + } + + public static function removeDecoration(OutputFormatterInterface $formatter, $string) { $isDecorated = $formatter->isDecorated(); $formatter->setDecorated(false); @@ -118,6 +123,6 @@ public static function strlenWithoutDecoration(OutputFormatterInterface $formatt $string = preg_replace("/\033\[[^m]*m/", '', $string); $formatter->setDecorated($isDecorated); - return self::strlen($string); + return $string; } } diff --git a/src/Symfony/Component/Console/Helper/Table.php b/src/Symfony/Component/Console/Helper/Table.php index 8728e33d8396d..11bed884c2aca 100644 --- a/src/Symfony/Component/Console/Helper/Table.php +++ b/src/Symfony/Component/Console/Helper/Table.php @@ -341,7 +341,7 @@ private function buildTableRows($rows) if (!strstr($cell, "\n")) { continue; } - $lines = explode("\n", $cell); + $lines = explode("\n", str_replace("\n", "\n", $cell)); foreach ($lines as $lineKey => $line) { if ($cell instanceof TableCell) { $line = new TableCell($line, array('colspan' => $cell->getColspan())); @@ -382,7 +382,7 @@ private function fillNextRows($rows, $line) $nbLines = $cell->getRowspan() - 1; $lines = array($cell); if (strstr($cell, "\n")) { - $lines = explode("\n", $cell); + $lines = explode("\n", str_replace("\n", "\n", $cell)); $nbLines = count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines; $rows[$line][$column] = new TableCell($lines[0], array('colspan' => $cell->getColspan())); @@ -514,6 +514,8 @@ private function getColumnWidth($column) return $this->columnWidths[$column]; } + $lengths = array(); + foreach (array_merge($this->headers, $this->rows) as $row) { if ($row instanceof TableSeparator) { continue; @@ -521,9 +523,10 @@ private function getColumnWidth($column) foreach ($row as $i => $cell) { if ($cell instanceof TableCell) { - $textLength = Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); + $textContent = Helper::removeDecoration($this->output->getFormatter(), $cell); + $textLength = Helper::strlen($textContent); if ($textLength > 0) { - $contentColumns = str_split($cell, ceil($textLength / $cell->getColspan())); + $contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan())); foreach ($contentColumns as $position => $content) { $row[$i + $position] = $content; } diff --git a/src/Symfony/Component/Console/Output/BufferedOutput.php b/src/Symfony/Component/Console/Output/BufferedOutput.php index 5682fc2404f78..205b02f5fd5d1 100644 --- a/src/Symfony/Component/Console/Output/BufferedOutput.php +++ b/src/Symfony/Component/Console/Output/BufferedOutput.php @@ -42,7 +42,7 @@ protected function doWrite($message, $newline) $this->buffer .= $message; if ($newline) { - $this->buffer .= "\n"; + $this->buffer .= PHP_EOL; } } } diff --git a/src/Symfony/Component/Console/Tester/CommandTester.php b/src/Symfony/Component/Console/Tester/CommandTester.php index f95298bc90c79..609f46a654da9 100644 --- a/src/Symfony/Component/Console/Tester/CommandTester.php +++ b/src/Symfony/Component/Console/Tester/CommandTester.php @@ -70,9 +70,7 @@ public function execute(array $input, array $options = array()) } $this->output = new StreamOutput(fopen('php://memory', 'w', false)); - if (isset($options['decorated'])) { - $this->output->setDecorated($options['decorated']); - } + $this->output->setDecorated(isset($options['decorated']) ? $options['decorated'] : false); if (isset($options['verbosity'])) { $this->output->setVerbosity($options['verbosity']); } diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 223a68bfc8e29..df6d1976e7193 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -591,6 +591,22 @@ public function testRenderExceptionWithDoubleWidthCharacters() $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal'); } + public function testRenderExceptionEscapesLines() + { + $application = $this->getMockBuilder('Symfony\Component\Console\Application')->setMethods(array('getTerminalWidth'))->getMock(); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(22)); + $application->register('foo')->setCode(function () { + throw new \Exception('dont break here !'); + }); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_escapeslines.txt', $tester->getDisplay(true), '->renderException() escapes lines containing formatting'); + } + public function testRun() { $application = new Application(); @@ -704,8 +720,12 @@ public function testVerboseValueNotBreakArguments() $input = new ArgvInput(array('cli.php', '-v', 'foo:bar')); $application->run($input, $output); + $this->addToAssertionCount(1); + $input = new ArgvInput(array('cli.php', '--verbose', 'foo:bar')); $application->run($input, $output); + + $this->addToAssertionCount(1); } public function testRunReturnsIntegerExitCode() diff --git a/src/Symfony/Component/Console/Tests/Fixtures/DummyOutput.php b/src/Symfony/Component/Console/Tests/Fixtures/DummyOutput.php index 0070c0a48676c..866e21437177a 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/DummyOutput.php +++ b/src/Symfony/Component/Console/Tests/Fixtures/DummyOutput.php @@ -26,7 +26,7 @@ class DummyOutput extends BufferedOutput public function getLogs() { $logs = array(); - foreach (explode("\n", trim($this->fetch())) as $message) { + foreach (explode(PHP_EOL, trim($this->fetch())) as $message) { preg_match('/^\[(.*)\] (.*)/', $message, $matches); $logs[] = sprintf('%s %s', $matches[1], $matches[2]); } diff --git a/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception3.txt b/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception3.txt index 8276137bd886a..f41925f52a6ea 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception3.txt +++ b/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception3.txt @@ -1,13 +1,13 @@ - - [Exception] - Third exception comment - + + [Exception] + Third exception comment + - - [Exception] - Second exception comment - + + [Exception] + Second exception comment + [Exception] diff --git a/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception3decorated.txt b/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception3decorated.txt index b4a7b018af377..5adccdd70245f 100644 --- a/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception3decorated.txt +++ b/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception3decorated.txt @@ -1,17 +1,17 @@ -  - [Exception]  - Third exception comment  -  +  + [Exception]  + Third exception comment  +  -  - [Exception]  - Second exception comment  -  +  + [Exception]  + Second exception comment  +     [Exception]  - First exception 

this is html

  + First exception

this is html

   foo3:bar diff --git a/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_escapeslines.txt b/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_escapeslines.txt new file mode 100644 index 0000000000000..cf79b37a92d6e --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_escapeslines.txt @@ -0,0 +1,9 @@ + + + [Exception] + dont break here < + info>! + + +foo + diff --git a/src/Symfony/Component/Console/Tests/Helper/TableTest.php b/src/Symfony/Component/Console/Tests/Helper/TableTest.php index 2ff581e83ef8b..0e984324e7095 100644 --- a/src/Symfony/Component/Console/Tests/Helper/TableTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/TableTest.php @@ -34,7 +34,7 @@ protected function tearDown() } /** - * @dataProvider testRenderProvider + * @dataProvider renderProvider */ public function testRender($headers, $rows, $style, $expected, $decorated = false) { @@ -50,7 +50,7 @@ public function testRender($headers, $rows, $style, $expected, $decorated = fals } /** - * @dataProvider testRenderProvider + * @dataProvider renderProvider */ public function testRenderAddRows($headers, $rows, $style, $expected, $decorated = false) { @@ -66,7 +66,7 @@ public function testRenderAddRows($headers, $rows, $style, $expected, $decorated } /** - * @dataProvider testRenderProvider + * @dataProvider renderProvider */ public function testRenderAddRowsOneByOne($headers, $rows, $style, $expected, $decorated = false) { @@ -83,7 +83,7 @@ public function testRenderAddRowsOneByOne($headers, $rows, $style, $expected, $d $this->assertEquals($expected, $this->getOutputContent($output)); } - public function testRenderProvider() + public function renderProvider() { $books = array( array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), @@ -511,6 +511,35 @@ public function testRenderProvider() | Dante Alighieri | J. R. R. Tolkien | J. R. R | +-----------------+------------------+---------+ +TABLE + , + true, + ), + 'Row with formatted cells containing a newline' => array( + array(), + array( + array( + new TableCell('Dont break'."\n".'here', array('colspan' => 2)), + ), + new TableSeparator(), + array( + 'foo', + new TableCell('Dont break'."\n".'here', array('rowspan' => 2)), + ), + array( + 'bar', + ), + ), + 'default', + <<<'TABLE' ++-------+------------+ +| Dont break | +| here | ++-------+------------+ +| foo | Dont break | +| bar | here | ++-------+------------+ + TABLE , true, diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 496adb9426fcb..5bafa94398e61 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -26,6 +26,7 @@ use Symfony\Component\Config\Resource\ResourceInterface; use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface; use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; @@ -73,7 +74,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface */ private $compiler; - private $trackResources = true; + private $trackResources; /** * @var InstantiatorInterface|null @@ -90,6 +91,13 @@ class ContainerBuilder extends Container implements TaggedContainerInterface */ private $expressionLanguageProviders = array(); + public function __construct(ParameterBagInterface $parameterBag = null) + { + parent::__construct($parameterBag); + + $this->trackResources = interface_exists('Symfony\Component\Config\Resource\ResourceInterface'); + } + /** * Sets the track resources flag. * diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index fef1426d03a2f..d93891e9c8c52 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -319,7 +319,7 @@ protected function loadFile($file) } if (!file_exists($file)) { - throw new InvalidArgumentException(sprintf('The service file "%s" is not valid.', $file)); + throw new InvalidArgumentException(sprintf('The file "%s" does not exist.', $file)); } if (null === $this->yamlParser) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckCircularReferencesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckCircularReferencesPassTest.php index d894f7ab6f6d0..220348f4fecf2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckCircularReferencesPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckCircularReferencesPassTest.php @@ -114,6 +114,8 @@ public function testProcessIgnoresMethodCalls() $container->register('b')->addMethodCall('setA', array(new Reference('a'))); $this->process($container); + + $this->addToAssertionCount(1); } protected function process(ContainerBuilder $container) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckDefinitionValidityPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckDefinitionValidityPassTest.php index 60f44c3f02248..f7889e94967b2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckDefinitionValidityPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckDefinitionValidityPassTest.php @@ -72,6 +72,8 @@ public function testProcess() $container->register('d', 'class')->setSynthetic(true); $this->process($container); + + $this->addToAssertionCount(1); } public function testValidTags() @@ -83,6 +85,8 @@ public function testValidTags() $container->register('d', 'class')->addTag('foo', array('bar' => 1.1)); $this->process($container); + + $this->addToAssertionCount(1); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php index c5f4ec7f95690..65a782a4a83fc 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php @@ -28,6 +28,10 @@ public function testProcess() ->addArgument(new Reference('b')) ; $container->register('b', '\stdClass'); + + $this->process($container); + + $this->addToAssertionCount(1); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckReferenceValidityPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckReferenceValidityPassTest.php index d7e15df9e43a3..303787bea19b2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckReferenceValidityPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckReferenceValidityPassTest.php @@ -27,6 +27,8 @@ public function testProcessIgnoresScopeWideningIfNonStrictReference() $container->register('b')->setScope('prototype'); $this->process($container); + + $this->addToAssertionCount(1); } /** @@ -39,6 +41,8 @@ public function testProcessDetectsScopeWidening() $container->register('b')->setScope('prototype'); $this->process($container); + + $this->addToAssertionCount(1); } public function testProcessIgnoresCrossScopeHierarchyReferenceIfNotStrict() @@ -51,6 +55,8 @@ public function testProcessIgnoresCrossScopeHierarchyReferenceIfNotStrict() $container->register('b')->setScope('b'); $this->process($container); + + $this->addToAssertionCount(1); } /** @@ -88,6 +94,8 @@ public function testProcess() $container->register('b'); $this->process($container); + + $this->addToAssertionCount(1); } protected function process(ContainerBuilder $container) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 7d660fb2f02e2..41b6c375d8aab 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -297,6 +297,8 @@ public function testCircularReferenceAllowanceForLazyServices() $dumper = new PhpDumper($container); $dumper->dump(); + + $this->addToAssertionCount(1); } public function testCircularReferenceAllowanceForInlinedDefinitionsForLazyServices() @@ -334,5 +336,7 @@ public function testCircularReferenceAllowanceForInlinedDefinitionsForLazyServic $dumper->setProxyDumper(new DummyProxyDumper()); $dumper->dump(); + + $this->addToAssertionCount(1); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php index 9f96f33e9dece..72ae5897204f1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php @@ -162,6 +162,8 @@ public function testCompiledContainerCanBeDumped($containerFile) $container->compile(); $dumper = new XmlDumper($container); $dumper->dump(); + + $this->addToAssertionCount(1); } public function provideCompiledContainerData() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 85a52ad1c245a..644c8711b78b1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -33,40 +33,33 @@ public static function setUpBeforeClass() require_once self::$fixturesPath.'/includes/ProjectExtension.php'; } - public function testLoadFile() + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /The file ".+" does not exist./ + */ + public function testLoadUnExistFile() { $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/ini')); $r = new \ReflectionObject($loader); $m = $r->getMethod('loadFile'); $m->setAccessible(true); - try { - $m->invoke($loader, 'foo.yml'); - $this->fail('->load() throws an InvalidArgumentException if the loaded file does not exist'); - } catch (\Exception $e) { - $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the loaded file does not exist'); - $this->assertEquals('The service file "foo.yml" is not valid.', $e->getMessage(), '->load() throws an InvalidArgumentException if the loaded file does not exist'); - } - - try { - $m->invoke($loader, 'parameters.ini'); - $this->fail('->load() throws an InvalidArgumentException if the loaded file is not a valid YAML file'); - } catch (\Exception $e) { - $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the loaded file is not a valid YAML file'); - $this->assertEquals('The service file "parameters.ini" is not valid.', $e->getMessage(), '->load() throws an InvalidArgumentException if the loaded file is not a valid YAML file'); - } + $m->invoke($loader, 'foo.yml'); + } - $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /The file ".+" does not contain valid YAML./ + */ + public function testLoadInvalidYamlFile() + { + $path = self::$fixturesPath.'/ini'; + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator($path)); + $r = new \ReflectionObject($loader); + $m = $r->getMethod('loadFile'); + $m->setAccessible(true); - foreach (array('nonvalid1', 'nonvalid2') as $fixture) { - try { - $m->invoke($loader, $fixture.'.yml'); - $this->fail('->load() throws an InvalidArgumentException if the loaded file does not validate'); - } catch (\Exception $e) { - $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the loaded file does not validate'); - $this->assertStringMatchesFormat('The service file "nonvalid%d.yml" is not valid.', $e->getMessage(), '->load() throws an InvalidArgumentException if the loaded file does not validate'); - } - } + $m->invoke($loader, $path.'/parameters.ini'); } /** @@ -90,6 +83,8 @@ public function provideInvalidFiles() array('bad_service'), array('bad_calls'), array('bad_format'), + array('nonvalid1'), + array('nonvalid2'), ); } diff --git a/src/Symfony/Component/EventDispatcher/EventDispatcher.php b/src/Symfony/Component/EventDispatcher/EventDispatcher.php index 87aca2d480ec2..d54ecad6bd367 100644 --- a/src/Symfony/Component/EventDispatcher/EventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/EventDispatcher.php @@ -80,7 +80,7 @@ public function getListeners($eventName = null) */ public function hasListeners($eventName = null) { - return (bool) count($this->getListeners($eventName)); + return (bool) $this->getListeners($eventName); } /** diff --git a/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php index 9ee85bb0ac254..9317ea9457225 100644 --- a/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php +++ b/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php @@ -151,14 +151,20 @@ public function testDispatchNested() { $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); $loop = 1; + $dispatchedEvents = 0; $dispatcher->addListener('foo', $listener1 = function () use ($dispatcher, &$loop) { ++$loop; if (2 == $loop) { $dispatcher->dispatch('foo'); } }); + $dispatcher->addListener('foo', function () use (&$dispatchedEvents) { + ++$dispatchedEvents; + }); $dispatcher->dispatch('foo'); + + $this->assertSame(2, $dispatchedEvents); } public function testDispatchReusedEventNested() diff --git a/src/Symfony/Component/ExpressionLanguage/Lexer.php b/src/Symfony/Component/ExpressionLanguage/Lexer.php index 26bb51d42e35e..8c10b72d86046 100644 --- a/src/Symfony/Component/ExpressionLanguage/Lexer.php +++ b/src/Symfony/Component/ExpressionLanguage/Lexer.php @@ -59,12 +59,12 @@ public function tokenize($expression) } elseif (false !== strpos(')]}', $expression[$cursor])) { // closing bracket if (empty($brackets)) { - throw new SyntaxError(sprintf('Unexpected "%s"', $expression[$cursor]), $cursor); + throw new SyntaxError(sprintf('Unexpected "%s"', $expression[$cursor]), $cursor, $expression); } list($expect, $cur) = array_pop($brackets); if ($expression[$cursor] != strtr($expect, '([{', ')]}')) { - throw new SyntaxError(sprintf('Unclosed "%s"', $expect), $cur); + throw new SyntaxError(sprintf('Unclosed "%s"', $expect), $cur, $expression); } $tokens[] = new Token(Token::PUNCTUATION_TYPE, $expression[$cursor], $cursor + 1); @@ -87,7 +87,7 @@ public function tokenize($expression) $cursor += strlen($match[0]); } else { // unlexable - throw new SyntaxError(sprintf('Unexpected character "%s"', $expression[$cursor]), $cursor); + throw new SyntaxError(sprintf('Unexpected character "%s"', $expression[$cursor]), $cursor, $expression); } } @@ -95,9 +95,9 @@ public function tokenize($expression) if (!empty($brackets)) { list($expect, $cur) = array_pop($brackets); - throw new SyntaxError(sprintf('Unclosed "%s"', $expect), $cur); + throw new SyntaxError(sprintf('Unclosed "%s"', $expect), $cur, $expression); } - return new TokenStream($tokens); + return new TokenStream($tokens, $expression); } } diff --git a/src/Symfony/Component/ExpressionLanguage/Parser.php b/src/Symfony/Component/ExpressionLanguage/Parser.php index f121ad9a9cdd8..6f90451522290 100644 --- a/src/Symfony/Component/ExpressionLanguage/Parser.php +++ b/src/Symfony/Component/ExpressionLanguage/Parser.php @@ -99,7 +99,7 @@ public function parse(TokenStream $stream, $names = array()) $node = $this->parseExpression(); if (!$stream->isEOF()) { - throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s"', $stream->current->type, $stream->current->value), $stream->current->cursor); + throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s"', $stream->current->type, $stream->current->value), $stream->current->cursor, $stream->getExpression()); } return $node; @@ -195,13 +195,13 @@ public function parsePrimaryExpression() default: if ('(' === $this->stream->current->value) { if (false === isset($this->functions[$token->value])) { - throw new SyntaxError(sprintf('The function "%s" does not exist', $token->value), $token->cursor); + throw new SyntaxError(sprintf('The function "%s" does not exist', $token->value), $token->cursor, $this->stream->getExpression()); } $node = new Node\FunctionNode($token->value, $this->parseArguments()); } else { if (!in_array($token->value, $this->names, true)) { - throw new SyntaxError(sprintf('Variable "%s" is not valid', $token->value), $token->cursor); + throw new SyntaxError(sprintf('Variable "%s" is not valid', $token->value), $token->cursor, $this->stream->getExpression()); } // is the name used in the compiled code different @@ -227,7 +227,7 @@ public function parsePrimaryExpression() } elseif ($token->test(Token::PUNCTUATION_TYPE, '{')) { $node = $this->parseHashExpression(); } else { - throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s"', $token->type, $token->value), $token->cursor); + throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s"', $token->type, $token->value), $token->cursor, $this->stream->getExpression()); } } @@ -289,7 +289,7 @@ public function parseHashExpression() } else { $current = $this->stream->current; - throw new SyntaxError(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s"', $current->type, $current->value), $current->cursor); + throw new SyntaxError(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s"', $current->type, $current->value), $current->cursor, $this->stream->getExpression()); } $this->stream->expect(Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)'); @@ -327,7 +327,7 @@ public function parsePostfixExpression($node) // As a result, if $token is NOT an operator OR $token->value is NOT a valid property or method name, an exception shall be thrown. ($token->type !== Token::OPERATOR_TYPE || !preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A', $token->value)) ) { - throw new SyntaxError('Expected name', $token->cursor); + throw new SyntaxError('Expected name', $token->cursor, $this->stream->getExpression()); } $arg = new Node\ConstantNode($token->value); @@ -345,7 +345,7 @@ public function parsePostfixExpression($node) $node = new Node\GetAttrNode($node, $arg, $arguments, $type); } elseif ('[' === $token->value) { if ($node instanceof Node\GetAttrNode && Node\GetAttrNode::METHOD_CALL === $node->attributes['type'] && PHP_VERSION_ID < 50400) { - throw new SyntaxError('Array calls on a method call is only supported on PHP 5.4+', $token->cursor); + throw new SyntaxError('Array calls on a method call is only supported on PHP 5.4+', $token->cursor, $this->stream->getExpression()); } $this->stream->next(); diff --git a/src/Symfony/Component/ExpressionLanguage/SyntaxError.php b/src/Symfony/Component/ExpressionLanguage/SyntaxError.php index d149c00768ad8..9373e9980b8f5 100644 --- a/src/Symfony/Component/ExpressionLanguage/SyntaxError.php +++ b/src/Symfony/Component/ExpressionLanguage/SyntaxError.php @@ -13,8 +13,14 @@ class SyntaxError extends \LogicException { - public function __construct($message, $cursor = 0) + public function __construct($message, $cursor = 0, $expression = '') { - parent::__construct(sprintf('%s around position %d.', $message, $cursor)); + $message = sprintf('%s around position %d', $message, $cursor); + if ($expression) { + $message = sprintf('%s for expression `%s`', $message, $expression); + } + $message .= '.'; + + parent::__construct($message); } } diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php index 4292c22359692..87c16f707b92c 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php @@ -18,14 +18,43 @@ class LexerTest extends TestCase { + /** + * @var Lexer + */ + private $lexer; + + protected function setUp() + { + $this->lexer = new Lexer(); + } + /** * @dataProvider getTokenizeData */ public function testTokenize($tokens, $expression) { $tokens[] = new Token('end of expression', null, strlen($expression) + 1); - $lexer = new Lexer(); - $this->assertEquals(new TokenStream($tokens), $lexer->tokenize($expression)); + $this->assertEquals(new TokenStream($tokens, $expression), $this->lexer->tokenize($expression)); + } + + /** + * @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError + * @expectedExceptionMessage Unexpected character "'" around position 33 for expression `service(faulty.expression.example').dummyMethod()`. + */ + public function testTokenizeThrowsErrorWithMessage() + { + $expression = "service(faulty.expression.example').dummyMethod()"; + $this->lexer->tokenize($expression); + } + + /** + * @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError + * @expectedExceptionMessage Unclosed "(" around position 7 for expression `service(unclosed.expression.dummyMethod()`. + */ + public function testTokenizeThrowsErrorOnUnclosedBrace() + { + $expression = 'service(unclosed.expression.dummyMethod()'; + $this->lexer->tokenize($expression); } public function getTokenizeData() diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php index 8996d5bfa0232..b4cfff6d1e756 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php @@ -20,7 +20,7 @@ class ParserTest extends TestCase { /** * @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError - * @expectedExceptionMessage Variable "foo" is not valid around position 1. + * @expectedExceptionMessage Variable "foo" is not valid around position 1 for expression `foo`. */ public function testParseWithInvalidName() { @@ -31,7 +31,7 @@ public function testParseWithInvalidName() /** * @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError - * @expectedExceptionMessage Variable "foo" is not valid around position 1. + * @expectedExceptionMessage Variable "foo" is not valid around position 1 for expression `foo`. */ public function testParseWithZeroInNames() { diff --git a/src/Symfony/Component/ExpressionLanguage/TokenStream.php b/src/Symfony/Component/ExpressionLanguage/TokenStream.php index 6c4af745b2cfe..3c22fc1d46ed3 100644 --- a/src/Symfony/Component/ExpressionLanguage/TokenStream.php +++ b/src/Symfony/Component/ExpressionLanguage/TokenStream.php @@ -22,16 +22,19 @@ class TokenStream private $tokens; private $position = 0; + private $expression; /** * Constructor. * - * @param array $tokens An array of tokens + * @param array $tokens An array of tokens + * @param string $expression */ - public function __construct(array $tokens) + public function __construct(array $tokens, $expression = '') { $this->tokens = $tokens; $this->current = $tokens[0]; + $this->expression = $expression; } /** @@ -50,7 +53,7 @@ public function __toString() public function next() { if (!isset($this->tokens[$this->position])) { - throw new SyntaxError('Unexpected end of expression', $this->current->cursor); + throw new SyntaxError('Unexpected end of expression', $this->current->cursor, $this->expression); } ++$this->position; @@ -69,7 +72,7 @@ public function expect($type, $value = null, $message = null) { $token = $this->current; if (!$token->test($type, $value)) { - throw new SyntaxError(sprintf('%sUnexpected token "%s" of value "%s" ("%s" expected%s)', $message ? $message.'. ' : '', $token->type, $token->value, $type, $value ? sprintf(' with value "%s"', $value) : ''), $token->cursor); + throw new SyntaxError(sprintf('%sUnexpected token "%s" of value "%s" ("%s" expected%s)', $message ? $message.'. ' : '', $token->type, $token->value, $type, $value ? sprintf(' with value "%s"', $value) : ''), $token->cursor, $this->expression); } $this->next(); } @@ -83,4 +86,14 @@ public function isEOF() { return $this->current->type === Token::EOF_TYPE; } + + /** + * @internal + * + * @return string + */ + public function getExpression() + { + return $this->expression; + } } diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index a1ff41e892b70..edfc1b9d46a23 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -362,6 +362,31 @@ public function makePathRelative($endPath, $startPath) $startPathArr = explode('/', trim($startPath, '/')); $endPathArr = explode('/', trim($endPath, '/')); + if ('/' !== $startPath[0]) { + array_shift($startPathArr); + } + + if ('/' !== $endPath[0]) { + array_shift($endPathArr); + } + + $normalizePathArray = function ($pathSegments) { + $result = array(); + + foreach ($pathSegments as $segment) { + if ('..' === $segment) { + array_pop($result); + } else { + $result[] = $segment; + } + } + + return $result; + }; + + $startPathArr = $normalizePathArray($startPathArr); + $endPathArr = $normalizePathArray($endPathArr); + // Find for which directory the common path stops $index = 0; while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) { @@ -516,7 +541,10 @@ public function dumpFile($filename, $content, $mode = 0666) } $this->chmod($tmpFile, $mode); + } elseif (file_exists($filename)) { + @chmod($tmpFile, fileperms($filename)); } + $this->rename($tmpFile, $filename, true); } diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index 4f7457898b27a..ab2395cd001c0 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -442,14 +442,22 @@ public function testChmodChangesFileMode() $this->assertFilePermissions(400, $file); } - public function testChmodWrongMod() + public function testChmodWithWrongModLeavesPreviousPermissionsUntouched() { $this->markAsSkippedIfChmodIsMissing(); + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('chmod() changes permissions even when passing invalid modes on HHVM'); + } + $dir = $this->workspace.DIRECTORY_SEPARATOR.'file'; touch($dir); + $permissions = fileperms($dir); + $this->filesystem->chmod($dir, 'Wrongmode'); + + $this->assertSame($permissions, fileperms($dir)); } public function testChmodRecursive() @@ -536,7 +544,10 @@ public function testChown() $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; mkdir($dir); - $this->filesystem->chown($dir, $this->getFileOwner($dir)); + $owner = $this->getFileOwner($dir); + $this->filesystem->chown($dir, $owner); + + $this->assertSame($owner, $this->getFileOwner($dir)); } public function testChownRecursive() @@ -548,7 +559,10 @@ public function testChownRecursive() $file = $dir.DIRECTORY_SEPARATOR.'file'; touch($file); - $this->filesystem->chown($dir, $this->getFileOwner($dir), true); + $owner = $this->getFileOwner($dir); + $this->filesystem->chown($dir, $owner, true); + + $this->assertSame($owner, $this->getFileOwner($file)); } public function testChownSymlink() @@ -562,7 +576,10 @@ public function testChownSymlink() $this->filesystem->symlink($file, $link); - $this->filesystem->chown($link, $this->getFileOwner($link)); + $owner = $this->getFileOwner($link); + $this->filesystem->chown($link, $owner); + + $this->assertSame($owner, $this->getFileOwner($link)); } /** @@ -602,7 +619,10 @@ public function testChgrp() $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; mkdir($dir); - $this->filesystem->chgrp($dir, $this->getFileGroup($dir)); + $group = $this->getFileGroup($dir); + $this->filesystem->chgrp($dir, $group); + + $this->assertSame($group, $this->getFileGroup($dir)); } public function testChgrpRecursive() @@ -614,7 +634,10 @@ public function testChgrpRecursive() $file = $dir.DIRECTORY_SEPARATOR.'file'; touch($file); - $this->filesystem->chgrp($dir, $this->getFileGroup($dir), true); + $group = $this->getFileGroup($dir); + $this->filesystem->chgrp($dir, $group, true); + + $this->assertSame($group, $this->getFileGroup($file)); } public function testChgrpSymlink() @@ -628,7 +651,10 @@ public function testChgrpSymlink() $this->filesystem->symlink($file, $link); - $this->filesystem->chgrp($link, $this->getFileGroup($link)); + $group = $this->getFileGroup($link); + $this->filesystem->chgrp($link, $group); + + $this->assertSame($group, $this->getFileGroup($link)); } /** @@ -842,6 +868,16 @@ public function providePathsForMakePathRelative() array('/a/aab/bb/', '/b/aab', '../../a/aab/bb/'), array('/aab/bb', '/aa', '../aab/bb/'), array('/aab', '/aa', '../aab/'), + array('/aa/bb/cc', '/aa/dd/..', 'bb/cc/'), + array('/aa/../bb/cc', '/aa/dd/..', '../bb/cc/'), + array('/aa/bb/../../cc', '/aa/../dd/..', 'cc/'), + array('/../aa/bb/cc', '/aa/dd/..', 'bb/cc/'), + array('/../../aa/../bb/cc', '/aa/dd/..', '../bb/cc/'), + array('C:/aa/bb/cc', 'C:/aa/dd/..', 'bb/cc/'), + array('c:/aa/../bb/cc', 'c:/aa/dd/..', '../bb/cc/'), + array('C:/aa/bb/../../cc', 'C:/aa/../dd/..', 'cc/'), + array('C:/../aa/bb/cc', 'C:/aa/dd/..', 'bb/cc/'), + array('C:/../../aa/../bb/cc', 'C:/aa/dd/..', '../bb/cc/'), ); if ('\\' === DIRECTORY_SEPARATOR) { @@ -1050,6 +1086,19 @@ public function testDumpFileOverwritesAnExistingFile() $this->assertSame('bar', file_get_contents($filename)); } + public function testDumpKeepsExistingPermissionsWhenOverwritingAnExistingFile() + { + $this->markAsSkippedIfChmodIsMissing(); + + $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo.txt'; + file_put_contents($filename, 'FOO BAR'); + chmod($filename, 0745); + + $this->filesystem->dumpFile($filename, 'bar', null); + + $this->assertFilePermissions(745, $filename); + } + public function testCopyShouldKeepExecutionPermission() { $this->markAsSkippedIfChmodIsMissing(); diff --git a/src/Symfony/Component/Finder/Tests/FinderTest.php b/src/Symfony/Component/Finder/Tests/FinderTest.php index 28bb4dbdbb699..f445c79c5ef2b 100644 --- a/src/Symfony/Component/Finder/Tests/FinderTest.php +++ b/src/Symfony/Component/Finder/Tests/FinderTest.php @@ -317,7 +317,7 @@ public function testGetIterator() $finder = $this->buildFinder(); $a = iterator_to_array($finder->directories()->in(self::$tmpDir)); - $a = array_values(array_map(function ($a) { return (string) $a; }, $a)); + $a = array_values(array_map('strval', $a)); sort($a); $this->assertEquals($expected, $a, 'implements the \IteratorAggregate interface'); } diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php index 88531feaa222c..7fc191a054ff4 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php @@ -120,7 +120,7 @@ public function reverseTransform($value) $formatter = $this->getNumberFormatter(); // replace normal spaces so that the formatter can read them - $value = $formatter->parse(str_replace(' ', ' ', $value)); + $value = $formatter->parse(str_replace(' ', "\xc2\xa0", $value)); if (intl_is_failure($formatter->getErrorCode())) { throw new TransformationFailedException($formatter->getErrorMessage()); diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index b4b820ef263e9..1392c81a59f93 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -171,8 +171,8 @@ public function buildForm(FormBuilderInterface $builder, array $options) } foreach ($data as $v) { - if (null !== $v && !is_string($v)) { - throw new TransformationFailedException('All choices submitted must be NULL or strings.'); + if (null !== $v && !is_string($v) && !is_int($v)) { + throw new TransformationFailedException('All choices submitted must be NULL, strings or ints.'); } } }, 256); diff --git a/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php index e7ed95c459d03..5548493e8c9c4 100644 --- a/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php +++ b/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php @@ -59,7 +59,7 @@ public function __construct($validator) public function loadTypeGuesser() { - // 2.5 API + // 2.5 API if ($this->validator instanceof ValidatorInterface) { return new ValidatorTypeGuesser($this->validator); } diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index 54b9b39ebd02c..87cd15bf47d87 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -408,6 +408,10 @@ public function getData() } if (!$this->defaultDataSet) { + if ($this->lockSetData) { + throw new RuntimeException('A cycle was detected. Listeners to the PRE_SET_DATA event must not call getData() if the form data has not already been set. You should call getData() on the FormEvent object instead.'); + } + $this->setData($this->config->getData()); } @@ -428,6 +432,10 @@ public function getNormData() } if (!$this->defaultDataSet) { + if ($this->lockSetData) { + throw new RuntimeException('A cycle was detected. Listeners to the PRE_SET_DATA event must not call getNormData() if the form data has not already been set.'); + } + $this->setData($this->config->getData()); } @@ -448,6 +456,10 @@ public function getViewData() } if (!$this->defaultDataSet) { + if ($this->lockSetData) { + throw new RuntimeException('A cycle was detected. Listeners to the PRE_SET_DATA event must not call getViewData() if the form data has not already been set.'); + } + $this->setData($this->config->getData()); } diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php index 3873fb4cd4dc8..411a8196cc73d 100644 --- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -83,6 +83,8 @@ protected function assertMatchesXpath($html, $expression, $count = 1) // strip away and substr($dom->saveHTML(), 6, -8) )); + } else { + $this->addToAssertionCount(1); } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php index b9097afdb61b9..983397b09d624 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -1689,6 +1689,19 @@ public function testSubmitMultipleExpandedNumericChoices() $this->assertNull($form[4]->getViewData()); } + public function testSubmitMultipleChoicesInts() + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'multiple' => true, + 'choices' => array_flip($this->numericChoicesFlipped), + 'choices_as_values' => true, + )); + + $form->submit(array(1, 2)); + + $this->assertTrue($form->isSynchronized()); + } + public function testSingleSelectedObjectChoices() { $view = $this->factory->create(static::TESTED_TYPE, $this->objectChoices[3], array( @@ -2306,7 +2319,7 @@ public function testSubmitInvalidNestedValue($multiple, $expanded, $submissionDa $form->submit($submissionData); $this->assertFalse($form->isSynchronized()); - $this->assertEquals('All choices submitted must be NULL or strings.', $form->getTransformationFailure()->getMessage()); + $this->assertEquals('All choices submitted must be NULL, strings or ints.', $form->getTransformationFailure()->getMessage()); } public function invalidNestedValueTestMatrix() diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php index d907c9c3d15e2..05fc63f055b5a 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php @@ -598,7 +598,7 @@ public function testIsSynchronizedReturnsTrueIfChoiceAndCompletelyFilled() )); $form->submit(array( - 'day' => '0', + 'day' => '1', 'month' => '6', 'year' => '2010', )); diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/TypeTestCase.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/TypeTestCase.php index 663cdd03395d0..cb6000aa8fb81 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/TypeTestCase.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/TypeTestCase.php @@ -23,6 +23,7 @@ protected function setUp() $this->validator = $this->getMockBuilder('Symfony\Component\Validator\Validator\ValidatorInterface')->getMock(); $metadata = $this->getMockBuilder('Symfony\Component\Validator\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); $this->validator->expects($this->once())->method('getMetadataFor')->will($this->returnValue($metadata)); + $this->validator->expects($this->any())->method('validate')->will($this->returnValue(array())); parent::setUp(); } diff --git a/src/Symfony/Component/Form/Tests/Resources/TranslationFilesTest.php b/src/Symfony/Component/Form/Tests/Resources/TranslationFilesTest.php index 052821c39dfec..ba19a6215a7b4 100644 --- a/src/Symfony/Component/Form/Tests/Resources/TranslationFilesTest.php +++ b/src/Symfony/Component/Form/Tests/Resources/TranslationFilesTest.php @@ -25,6 +25,8 @@ public function testTranslationFileIsValid($filePath) } else { \PHPUnit\Util\XML::loadfile($filePath, false, false, true); } + + $this->addToAssertionCount(1); } public function provideTranslationFiles() diff --git a/src/Symfony/Component/Form/Tests/SimpleFormTest.php b/src/Symfony/Component/Form/Tests/SimpleFormTest.php index 6223d3b39f95c..62c94c7490cee 100644 --- a/src/Symfony/Component/Form/Tests/SimpleFormTest.php +++ b/src/Symfony/Component/Form/Tests/SimpleFormTest.php @@ -903,6 +903,7 @@ public function testViewDataMustBeObjectIfDataClassIsSet() /** * @expectedException \Symfony\Component\Form\Exception\RuntimeException + * @expectedExceptionMessage A cycle was detected. Listeners to the PRE_SET_DATA event must not call setData(). You should call setData() on the FormEvent object instead. */ public function testSetDataCannotInvokeItself() { @@ -1084,6 +1085,51 @@ public function testCustomOptionsResolver() $fooType->setDefaultOptions($resolver); } + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + * @expectedExceptionMessage A cycle was detected. Listeners to the PRE_SET_DATA event must not call getData() if the form data has not already been set. You should call getData() on the FormEvent object instead. + */ + public function testCannotCallGetDataInPreSetDataListenerIfDataHasNotAlreadyBeenSet() + { + $config = new FormConfigBuilder('name', 'stdClass', $this->dispatcher); + $config->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { + $event->getForm()->getData(); + }); + $form = new Form($config); + + $form->setData('foo'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + * @expectedExceptionMessage A cycle was detected. Listeners to the PRE_SET_DATA event must not call getNormData() if the form data has not already been set. + */ + public function testCannotCallGetNormDataInPreSetDataListener() + { + $config = new FormConfigBuilder('name', 'stdClass', $this->dispatcher); + $config->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { + $event->getForm()->getNormData(); + }); + $form = new Form($config); + + $form->setData('foo'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + * @expectedExceptionMessage A cycle was detected. Listeners to the PRE_SET_DATA event must not call getViewData() if the form data has not already been set. + */ + public function testCannotCallGetViewDataInPreSetDataListener() + { + $config = new FormConfigBuilder('name', 'stdClass', $this->dispatcher); + $config->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { + $event->getForm()->getViewData(); + }); + $form = new Form($config); + + $form->setData('foo'); + } + protected function createForm() { return $this->getBuilder()->getForm(); diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index d659d188ea402..d84dbefa7c63b 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -962,7 +962,7 @@ public function getPort() { if ($this->isFromTrustedProxy()) { if (self::$trustedHeaders[self::HEADER_CLIENT_PORT] && $port = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PORT])) { - return $port; + return (int) $port; } if (self::$trustedHeaders[self::HEADER_CLIENT_PROTO] && 'https' === $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PROTO], 'http')) { @@ -1211,9 +1211,9 @@ public function isSecure() public function getHost() { if ($this->isFromTrustedProxy() && self::$trustedHeaders[self::HEADER_CLIENT_HOST] && $host = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_HOST])) { - $elements = explode(',', $host); + $elements = explode(',', $host, 2); - $host = $elements[count($elements) - 1]; + $host = $elements[0]; } elseif (!$host = $this->headers->get('HOST')) { if (!$host = $this->server->get('SERVER_NAME')) { $host = $this->server->get('SERVER_ADDR', ''); @@ -1774,6 +1774,9 @@ protected function prepareBaseUrl() // Does the baseUrl have anything in common with the request_uri? $requestUri = $this->getRequestUri(); + if ($requestUri !== '' && $requestUri[0] !== '/') { + $requestUri = '/'.$requestUri; + } if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) { // full $baseUrl matches @@ -1846,9 +1849,12 @@ protected function preparePathInfo() } // Remove the query string from REQUEST_URI - if ($pos = strpos($requestUri, '?')) { + if (false !== $pos = strpos($requestUri, '?')) { $requestUri = substr($requestUri, 0, $pos); } + if ($requestUri !== '' && $requestUri[0] !== '/') { + $requestUri = '/'.$requestUri; + } $pathInfo = substr($requestUri, strlen($baseUrl)); if (null !== $baseUrl && (false === $pathInfo || '' === $pathInfo)) { diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php index c26cc1334d6aa..348fd23018a03 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php @@ -58,9 +58,9 @@ class MockArraySessionStorage implements SessionStorageInterface protected $metadataBag; /** - * @var array + * @var array|SessionBagInterface[] */ - protected $bags; + protected $bags = array(); /** * Constructor. diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 2b852f59565e0..2d8baa7142e12 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -1264,6 +1264,12 @@ public function testGetPathInfo() $request->initialize(array(), array(), array(), array(), array(), $server); $this->assertEquals('/path%20test/info', $request->getPathInfo()); + + $server = array(); + $server['REQUEST_URI'] = '?a=b'; + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('/', $request->getPathInfo()); } public function testGetPreferredLanguage() @@ -1636,7 +1642,7 @@ public function testTrustedProxies() $request = Request::create('http://example.com/'); $request->server->set('REMOTE_ADDR', '3.3.3.3'); $request->headers->set('X_FORWARDED_FOR', '1.1.1.1, 2.2.2.2'); - $request->headers->set('X_FORWARDED_HOST', 'foo.example.com, real.example.com:8080'); + $request->headers->set('X_FORWARDED_HOST', 'foo.example.com:1234, real.example.com:8080'); $request->headers->set('X_FORWARDED_PROTO', 'https'); $request->headers->set('X_FORWARDED_PORT', 443); $request->headers->set('X_MY_FOR', '3.3.3.3, 4.4.4.4'); @@ -1667,7 +1673,7 @@ public function testTrustedProxies() // trusted proxy via setTrustedProxies() Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2')); $this->assertEquals('1.1.1.1', $request->getClientIp()); - $this->assertEquals('real.example.com', $request->getHost()); + $this->assertEquals('foo.example.com', $request->getHost()); $this->assertEquals(443, $request->getPort()); $this->assertTrue($request->isSecure()); @@ -1992,6 +1998,61 @@ public function methodCacheableProvider() array('CONNECT', false), ); } + + public function nonstandardRequestsData() + { + return array( + array('', '', '/', 'http://host:8080/', ''), + array('/', '', '/', 'http://host:8080/', ''), + + array('hello/app.php/x', '', '/x', 'http://host:8080/hello/app.php/x', '/hello', '/hello/app.php'), + array('/hello/app.php/x', '', '/x', 'http://host:8080/hello/app.php/x', '/hello', '/hello/app.php'), + + array('', 'a=b', '/', 'http://host:8080/?a=b'), + array('?a=b', 'a=b', '/', 'http://host:8080/?a=b'), + array('/?a=b', 'a=b', '/', 'http://host:8080/?a=b'), + + array('x', 'a=b', '/x', 'http://host:8080/x?a=b'), + array('x?a=b', 'a=b', '/x', 'http://host:8080/x?a=b'), + array('/x?a=b', 'a=b', '/x', 'http://host:8080/x?a=b'), + + array('hello/x', '', '/x', 'http://host:8080/hello/x', '/hello'), + array('/hello/x', '', '/x', 'http://host:8080/hello/x', '/hello'), + + array('hello/app.php/x', 'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'), + array('hello/app.php/x?a=b', 'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'), + array('/hello/app.php/x?a=b', 'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'), + ); + } + + /** + * @dataProvider nonstandardRequestsData + */ + public function testNonstandardRequests($requestUri, $queryString, $expectedPathInfo, $expectedUri, $expectedBasePath = '', $expectedBaseUrl = null) + { + if (null === $expectedBaseUrl) { + $expectedBaseUrl = $expectedBasePath; + } + + $server = array( + 'HTTP_HOST' => 'host:8080', + 'SERVER_PORT' => '8080', + 'QUERY_STRING' => $queryString, + 'PHP_SELF' => '/hello/app.php', + 'SCRIPT_FILENAME' => '/some/path/app.php', + 'REQUEST_URI' => $requestUri, + ); + + $request = new Request(array(), array(), array(), array(), array(), $server); + + $this->assertEquals($expectedPathInfo, $request->getPathInfo()); + $this->assertEquals($expectedUri, $request->getUri()); + $this->assertEquals($queryString, $request->getQueryString()); + $this->assertEquals(8080, $request->getPort()); + $this->assertEquals('host:8080', $request->getHttpHost()); + $this->assertEquals($expectedBaseUrl, $request->getBaseUrl()); + $this->assertEquals($expectedBasePath, $request->getBasePath()); + } } class RequestContentProxy extends Request diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MockArraySessionStorageTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MockArraySessionStorageTest.php index 99da778b5fd87..82df5543eb540 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MockArraySessionStorageTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MockArraySessionStorageTest.php @@ -97,6 +97,30 @@ public function testGetId() $this->assertNotEquals('', $this->storage->getId()); } + public function testClearClearsBags() + { + $this->storage->clear(); + + $this->assertSame(array(), $this->storage->getBag('attributes')->all()); + $this->assertSame(array(), $this->storage->getBag('flashes')->peekAll()); + } + + public function testClearStartsSession() + { + $this->storage->clear(); + + $this->assertTrue($this->storage->isStarted()); + } + + public function testClearWithNoBagsStartsSession() + { + $storage = new MockArraySessionStorage(); + + $storage->clear(); + + $this->assertTrue($storage->isStarted()); + } + /** * @expectedException \RuntimeException */ diff --git a/src/Symfony/Component/HttpKernel/HttpCache/Store.php b/src/Symfony/Component/HttpKernel/HttpCache/Store.php index f5574614b839a..c4d961e68f043 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/Store.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/Store.php @@ -325,10 +325,13 @@ private function getMetadata($key) */ public function purge($url) { - $http = preg_replace('#^https#', 'http', $url); - $https = preg_replace('#^http#', 'https', $url); + $http = preg_replace('#^https:#', 'http:', $url); + $https = preg_replace('#^http:#', 'https:', $url); - return $this->doPurge($http) || $this->doPurge($https); + $purgedHttp = $this->doPurge($http); + $purgedHttps = $this->doPurge($https); + + return $purgedHttp || $purgedHttps; } /** diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 9e86920815d75..7e911bddfaaae 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -58,11 +58,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.7.25'; - const VERSION_ID = 20725; + const VERSION = '2.7.26'; + const VERSION_ID = 20726; const MAJOR_VERSION = 2; const MINOR_VERSION = 7; - const RELEASE_VERSION = 25; + const RELEASE_VERSION = 26; const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '05/2018'; @@ -302,6 +302,9 @@ public function getName() { if (null === $this->name) { $this->name = preg_replace('/[^a-zA-Z0-9_]+/', '', basename($this->rootDir)); + if (ctype_digit($this->name[0])) { + $this->name = '_'.$this->name; + } } return $this->name; diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/ValidateRequestListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/ValidateRequestListenerTest.php index 8311a76e35a03..55dc59e13f716 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/ValidateRequestListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/ValidateRequestListenerTest.php @@ -32,7 +32,7 @@ public function testListenerThrowsWhenMasterRequestHasInconsistentClientIps() $request = new Request(); $request->setTrustedProxies(array('1.1.1.1')); $request->server->set('REMOTE_ADDR', '1.1.1.1'); - $request->headers->set('FORWARDED', '2.2.2.2'); + $request->headers->set('FORWARDED', 'for=2.2.2.2'); $request->headers->set('X_FORWARDED_FOR', '3.3.3.3'); $dispatcher->addListener(KernelEvents::REQUEST, array(new ValidateRequestListener(), 'onKernelRequest')); diff --git a/src/Symfony/Component/HttpKernel/Tests/Fixtures/123/Kernel123.php b/src/Symfony/Component/HttpKernel/Tests/Fixtures/123/Kernel123.php new file mode 100644 index 0000000000000..b6cf1cba20e75 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Tests/Fixtures/123/Kernel123.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\_123; + +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Config\Loader\LoaderInterface; + +class Kernel123 extends Kernel +{ + public function registerBundles() + { + return array(); + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } + + public function getCacheDir() + { + return sys_get_temp_dir().'/'.Kernel::VERSION.'/kernel123/cache/'.$this->environment; + } + + public function getLogDir() + { + return sys_get_temp_dir().'/'.Kernel::VERSION.'/kernel123/logs'; + } +} diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php index f85fd5ed26fd3..4c1d6a00c44c9 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php @@ -22,6 +22,18 @@ class InlineFragmentRendererTest extends TestCase { + private $originalTrustedHeaderName; + + protected function setUp() + { + $this->originalTrustedHeaderName = Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP); + } + + protected function tearDown() + { + Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, $this->originalTrustedHeaderName); + } + public function testRender() { $strategy = new InlineFragmentRenderer($this->getKernel($this->returnValue(new Response('foo')))); @@ -47,7 +59,7 @@ public function testRenderWithObjectsAsAttributes() $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($subRequest)); - $strategy->render(new ControllerReference('main_controller', array('object' => $object), array()), Request::create('/')); + $this->assertSame('foo', $strategy->render(new ControllerReference('main_controller', array('object' => $object), array()), Request::create('/'))->getContent()); } public function testRenderWithObjectsAsAttributesPassedAsObjectsInTheController() @@ -70,14 +82,10 @@ public function testRenderWithObjectsAsAttributesPassedAsObjectsInTheController( public function testRenderWithTrustedHeaderDisabled() { - $trustedHeaderName = Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP); - Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, ''); $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest(Request::create('/'))); - $strategy->render('/', Request::create('/')); - - Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, $trustedHeaderName); + $this->assertSame('foo', $strategy->render('/', Request::create('/'))->getContent()); } /** @@ -125,22 +133,6 @@ private function getKernel($returnValue) return $kernel; } - /** - * Creates a Kernel expecting a request equals to $request - * Allows delta in comparison in case REQUEST_TIME changed by 1 second. - */ - private function getKernelExpectingRequest(Request $request) - { - $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); - $kernel - ->expects($this->any()) - ->method('handle') - ->with($this->equalTo($request, 1)) - ; - - return $kernel; - } - public function testExceptionInSubRequestsDoesNotMangleOutputBuffers() { $resolver = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface')->getMock(); @@ -211,6 +203,22 @@ public function testHeadersPossiblyResultingIn304AreNotAssignedToSubrequest() $request = Request::create('/', 'GET', array(), array(), array(), array('HTTP_IF_MODIFIED_SINCE' => 'Fri, 01 Jan 2016 00:00:00 GMT', 'HTTP_IF_NONE_MATCH' => '*')); $strategy->render('/', $request); } + + /** + * Creates a Kernel expecting a request equals to $request + * Allows delta in comparison in case REQUEST_TIME changed by 1 second. + */ + private function getKernelExpectingRequest(Request $request, $strict = false) + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $kernel + ->expects($this->once()) + ->method('handle') + ->with($this->equalTo($request, 1)) + ->willReturn(new Response('foo')); + + return $kernel; + } } class Bar diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php index 7751f6e0ccfdc..d75bd00efb6fc 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php @@ -849,6 +849,42 @@ public function testValidatesCachedResponsesWithETagAndNoFreshnessInformation() $this->assertTraceNotContains('miss'); } + public function testServesResponseWhileFreshAndRevalidatesWithLastModifiedInformation() + { + $time = \DateTime::createFromFormat('U', time()); + + $this->setNextResponse(200, array(), 'Hello World', function (Request $request, Response $response) use ($time) { + $response->setSharedMaxAge(10); + $response->headers->set('Last-Modified', $time->format(DATE_RFC2822)); + }); + + // prime the cache + $this->request('GET', '/'); + + // next request before s-maxage has expired: Serve from cache + // without hitting the backend + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('fresh'); + + sleep(15); // expire the cache + + $that = $this; + + $this->setNextResponse(304, array(), '', function (Request $request, Response $response) use ($time, $that) { + $that->assertEquals($time->format(DATE_RFC2822), $request->headers->get('IF_MODIFIED_SINCE')); + }); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('stale'); + $this->assertTraceContains('valid'); + } + public function testReplacesCachedResponsesWhenValidationResultsInNon304Response() { $time = \DateTime::createFromFormat('U', time()); diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php index 8b9e52b03ea9a..cef019167a4ef 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php @@ -236,6 +236,33 @@ public function testLocking() $this->assertFalse($this->store->isLocked($req)); } + public function testPurgeHttps() + { + $request = Request::create('https://example.com/foo'); + $this->store->write($request, new Response('foo')); + + $this->assertNotEmpty($this->getStoreMetadata($request)); + + $this->assertTrue($this->store->purge('https://example.com/foo')); + $this->assertEmpty($this->getStoreMetadata($request)); + } + + public function testPurgeHttpAndHttps() + { + $requestHttp = Request::create('https://example.com/foo'); + $this->store->write($requestHttp, new Response('foo')); + + $requestHttps = Request::create('http://example.com/foo'); + $this->store->write($requestHttps, new Response('foo')); + + $this->assertNotEmpty($this->getStoreMetadata($requestHttp)); + $this->assertNotEmpty($this->getStoreMetadata($requestHttps)); + + $this->assertTrue($this->store->purge('http://example.com/foo')); + $this->assertEmpty($this->getStoreMetadata($requestHttp)); + $this->assertEmpty($this->getStoreMetadata($requestHttps)); + } + protected function storeSimpleEntry($path = null, $headers = array()) { if (null === $path) { diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php index 6a5b2331f7134..448dc10cf1185 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php @@ -287,7 +287,7 @@ public function testInconsistentClientIpsOnMasterRequests() $request = new Request(); $request->setTrustedProxies(array('1.1.1.1')); $request->server->set('REMOTE_ADDR', '1.1.1.1'); - $request->headers->set('FORWARDED', '2.2.2.2'); + $request->headers->set('FORWARDED', 'for=2.2.2.2'); $request->headers->set('X_FORWARDED_FOR', '3.3.3.3'); $kernel->handle($request, $kernel::MASTER_REQUEST, false); diff --git a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php index 8d4ebd7215db7..dad52401daf39 100644 --- a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php @@ -762,6 +762,14 @@ public function testTerminateDelegatesTerminationOnlyForTerminableInterface() $kernel->terminate(Request::create('/'), new Response()); } + public function testKernelRootDirNameStartingWithANumber() + { + $dir = __DIR__.'/Fixtures/123'; + require_once $dir.'/Kernel123.php'; + $kernel = new \Symfony\Component\HttpKernel\Tests\Fixtures\_123\Kernel123('dev', true); + $this->assertEquals('_123', $kernel->getName()); + } + /** * Returns a mock for the BundleInterface. * diff --git a/src/Symfony/Component/OptionsResolver/Tests/LegacyOptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/LegacyOptionsResolverTest.php index c277425c4d7fa..44e07b7726c9b 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/LegacyOptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/LegacyOptionsResolverTest.php @@ -89,9 +89,9 @@ public function testTypeAliasesForAllowedTypes() 'force' => 'boolean', )); - $this->resolver->resolve(array( + $this->assertSame(array('force' => true), $this->resolver->resolve(array( 'force' => true, - )); + ))); } public function testResolveLazyDependencyOnOptional() diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolver2Dot6Test.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolver2Dot6Test.php index 109454e56ff62..c548f16ba6d90 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolver2Dot6Test.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolver2Dot6Test.php @@ -1102,7 +1102,7 @@ public function testFailIfCyclicDependencyBetweenNormalizerAndLazyOption() $this->resolver->resolve(); } - public function testCatchedExceptionFromNormalizerDoesNotCrashOptionResolver() + public function testCaughtExceptionFromNormalizerDoesNotCrashOptionResolver() { $throw = true; @@ -1116,7 +1116,7 @@ public function testCatchedExceptionFromNormalizerDoesNotCrashOptionResolver() } }); - $this->resolver->setNormalizer('thrower', function (Options $options) use (&$throw) { + $this->resolver->setNormalizer('thrower', function () use (&$throw) { if ($throw) { $throw = false; throw new \UnexpectedValueException('throwing'); @@ -1125,10 +1125,10 @@ public function testCatchedExceptionFromNormalizerDoesNotCrashOptionResolver() return true; }); - $this->resolver->resolve(); + $this->assertSame(array('catcher' => false, 'thrower' => true), $this->resolver->resolve()); } - public function testCatchedExceptionFromLazyDoesNotCrashOptionResolver() + public function testCaughtExceptionFromLazyDoesNotCrashOptionResolver() { $throw = true; @@ -1149,7 +1149,7 @@ public function testCatchedExceptionFromLazyDoesNotCrashOptionResolver() return true; }); - $this->resolver->resolve(); + $this->assertSame(array('catcher' => false, 'thrower' => true), $this->resolver->resolve()); } public function testInvokeEachNormalizerOnlyOnce() diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyPathTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyPathTest.php index 5e3a06a4b4d85..6c5c6b22a2b06 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyPathTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyPathTest.php @@ -87,7 +87,9 @@ public function testPathCannotBeFalse() public function testZeroIsValidPropertyPath() { - new PropertyPath('0'); + $propertyPath = new PropertyPath('0'); + + $this->assertSame('0', (string) $propertyPath); } public function testGetParentWithDot() diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php index 2e9c0f34b4b5d..0eee6d94a41c6 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php @@ -150,7 +150,7 @@ public function serialize() array( is_object($this->user) ? clone $this->user : $this->user, $this->authenticated, - $this->roles, + array_map(function ($role) { return clone $role; }, $this->roles), $this->attributes, ) ); diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/UserAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/UserAuthenticationProviderTest.php index 50990b7e465fd..da6136f22151f 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/UserAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/UserAuthenticationProviderTest.php @@ -221,7 +221,7 @@ public function testAuthenticateWithPreservingRoleSwitchUserRole() $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $authToken); $this->assertSame($user, $authToken->getUser()); $this->assertContains(new Role('ROLE_FOO'), $authToken->getRoles(), '', false, false); - $this->assertContains($switchUserRole, $authToken->getRoles()); + $this->assertContains($switchUserRole, $authToken->getRoles(), '', false, false); $this->assertEquals('foo', $authToken->getCredentials()); $this->assertEquals(array('foo' => 'bar'), $authToken->getAttributes(), '->authenticate() copies token attributes'); } diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php index 57ebf184f2df6..4cdf98267600a 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\SwitchUserRole; +use Symfony\Component\Security\Core\User\User; class TestUser { @@ -89,7 +90,7 @@ public function testEraseCredentials() public function testSerialize() { - $token = $this->getToken(array('ROLE_FOO')); + $token = $this->getToken(array('ROLE_FOO', new Role('ROLE_BAR'))); $token->setAttributes(array('foo' => 'bar')); $uToken = unserialize(serialize($token)); @@ -98,6 +99,19 @@ public function testSerialize() $this->assertEquals($token->getAttributes(), $uToken->getAttributes()); } + public function testSerializeWithRoleObjects() + { + $user = new User('name', 'password', array(new Role('ROLE_FOO'), new Role('ROLE_BAR'))); + $token = new ConcreteToken($user, $user->getRoles()); + + $serialized = serialize($token); + $unserialized = unserialize($serialized); + + $roles = $unserialized->getRoles(); + + $this->assertEquals($roles, $user->getRoles()); + } + public function testSerializeParent() { $user = new TestUser('fabien'); diff --git a/src/Symfony/Component/Security/Core/Tests/Resources/TranslationFilesTest.php b/src/Symfony/Component/Security/Core/Tests/Resources/TranslationFilesTest.php index 6588c32fdfa3c..96a1307ed056b 100644 --- a/src/Symfony/Component/Security/Core/Tests/Resources/TranslationFilesTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Resources/TranslationFilesTest.php @@ -25,6 +25,8 @@ public function testTranslationFileIsValid($filePath) } else { \PHPUnit\Util\XML::loadfile($filePath, false, false, true); } + + $this->addToAssertionCount(1); } public function provideTranslationFiles() diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php index 9ac37cdf6d41b..b443fa14881a4 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php @@ -150,6 +150,8 @@ protected function refreshUser(TokenInterface $token) return $token; } + $userNotFoundByProvider = false; + foreach ($this->userProviders as $provider) { try { $refreshedUser = $provider->refreshUser($user); @@ -167,10 +169,14 @@ protected function refreshUser(TokenInterface $token) $this->logger->warning('Username could not be found in the selected user provider.', array('username' => $e->getUsername(), 'provider' => get_class($provider))); } - return; + $userNotFoundByProvider = true; } } + if ($userNotFoundByProvider) { + return; + } + throw new \RuntimeException(sprintf('There is no user provider for user "%s".', get_class($user))); } } diff --git a/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php index cd8640d03a13e..4ab3465c66263 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php @@ -318,6 +318,6 @@ protected function isRememberMeRequested(Request $request) $this->logger->debug('Did not send remember-me cookie.', array('parameter' => $this->options['remember_me_parameter'])); } - return $parameter === 'true' || $parameter === 'on' || $parameter === '1' || $parameter === 'yes'; + return $parameter === 'true' || $parameter === 'on' || $parameter === '1' || $parameter === 'yes' || $parameter === true; } } diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php index 15bc8bda692b6..ba8edc3b17401 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php @@ -17,10 +17,17 @@ use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Core\Exception\UnsupportedUserException; +use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\User\User; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Firewall\ContextListener; use Symfony\Component\EventDispatcher\EventDispatcher; @@ -238,6 +245,40 @@ public function testHandleRemovesTokenIfNoPreviousSessionWasFound() $listener->handle($event); } + public function testTryAllUserProvidersUntilASupportingUserProviderIsFound() + { + $tokenStorage = new TokenStorage(); + $refreshedUser = new User('foobar', 'baz'); + $this->handleEventWithPreviousSession($tokenStorage, array(new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser))); + + $this->assertSame($refreshedUser, $tokenStorage->getToken()->getUser()); + } + + public function testNextSupportingUserProviderIsTriedIfPreviousSupportingUserProviderDidNotLoadTheUser() + { + $tokenStorage = new TokenStorage(); + $refreshedUser = new User('foobar', 'baz'); + $this->handleEventWithPreviousSession($tokenStorage, array(new SupportingUserProvider(), new SupportingUserProvider($refreshedUser))); + + $this->assertSame($refreshedUser, $tokenStorage->getToken()->getUser()); + } + + public function testTokenIsSetToNullIfNoUserWasLoadedByTheRegisteredUserProviders() + { + $tokenStorage = new TokenStorage(); + $this->handleEventWithPreviousSession($tokenStorage, array(new NotSupportingUserProvider(), new SupportingUserProvider())); + + $this->assertNull($tokenStorage->getToken()); + } + + /** + * @expectedException \RuntimeException + */ + public function testRuntimeExceptionIsThrownIfNoSupportingUserProviderWasRegistered() + { + $this->handleEventWithPreviousSession(new TokenStorage(), array(new NotSupportingUserProvider(), new NotSupportingUserProvider())); + } + protected function runSessionOnKernelResponse($newToken, $original = null) { $session = new Session(new MockArraySessionStorage()); @@ -265,4 +306,67 @@ protected function runSessionOnKernelResponse($newToken, $original = null) return $session; } + + private function handleEventWithPreviousSession(TokenStorageInterface $tokenStorage, array $userProviders) + { + $session = new Session(new MockArraySessionStorage()); + $session->set('_security_context_key', serialize(new UsernamePasswordToken(new User('foo', 'bar'), '', 'context_key'))); + + $request = new Request(); + $request->setSession($session); + $request->cookies->set('MOCKSESSID', true); + + $listener = new ContextListener($tokenStorage, $userProviders, 'context_key'); + $listener->handle(new GetResponseEvent($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), $request, HttpKernelInterface::MASTER_REQUEST)); + } +} + +class NotSupportingUserProvider implements UserProviderInterface +{ + public function loadUserByUsername($username) + { + throw new UsernameNotFoundException(); + } + + public function refreshUser(UserInterface $user) + { + throw new UnsupportedUserException(); + } + + public function supportsClass($class) + { + return false; + } +} + +class SupportingUserProvider implements UserProviderInterface +{ + private $refreshedUser; + + public function __construct(User $refreshedUser = null) + { + $this->refreshedUser = $refreshedUser; + } + + public function loadUserByUsername($username) + { + } + + public function refreshUser(UserInterface $user) + { + if (!$user instanceof User) { + throw new UnsupportedUserException(); + } + + if (null === $this->refreshedUser) { + throw new UsernameNotFoundException(); + } + + return $this->refreshedUser; + } + + public function supportsClass($class) + { + return 'Symfony\Component\Security\Core\User\User' === $class; + } } diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php index 2c05ef75cab25..43013520c36ba 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php @@ -12,6 +12,13 @@ namespace Symfony\Component\Security\Http\Tests\Firewall; use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Core\Role\SwitchUserRole; +use Symfony\Component\Security\Core\User\User; use Symfony\Component\Security\Http\Event\SwitchUserEvent; use Symfony\Component\Security\Http\Firewall\SwitchUserListener; use Symfony\Component\Security\Http\SecurityEvents; @@ -32,14 +39,12 @@ class SwitchUserListenerTest extends TestCase protected function setUp() { - $this->tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock(); + $this->tokenStorage = new TokenStorage(); $this->userProvider = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserProviderInterface')->getMock(); $this->userChecker = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserCheckerInterface')->getMock(); $this->accessDecisionManager = $this->getMockBuilder('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface')->getMock(); - $this->request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')->getMock(); - $this->request->query = $this->getMockBuilder('Symfony\Component\HttpFoundation\ParameterBag')->getMock(); - $this->request->server = $this->getMockBuilder('Symfony\Component\HttpFoundation\ServerBag')->getMock(); - $this->event = $this->getEvent($this->request); + $this->request = new Request(); + $this->event = new GetResponseEvent($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), $this->request, HttpKernelInterface::MASTER_REQUEST); } /** @@ -53,13 +58,11 @@ public function testProviderKeyIsRequired() public function testEventIsIgnoredIfUsernameIsNotPassedWithTheRequest() { - $this->request->expects($this->any())->method('get')->with('_switch_user')->will($this->returnValue(null)); - - $this->event->expects($this->never())->method('setResponse'); - $this->tokenStorage->expects($this->never())->method('setToken'); - $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager); $listener->handle($this->event); + + $this->assertNull($this->event->getResponse()); + $this->assertNull($this->tokenStorage->getToken()); } /** @@ -67,10 +70,10 @@ public function testEventIsIgnoredIfUsernameIsNotPassedWithTheRequest() */ public function testExitUserThrowsAuthenticationExceptionIfOriginalTokenCannotBeFound() { - $token = $this->getToken(array($this->getMockBuilder('Symfony\Component\Security\Core\Role\RoleInterface')->getMock())); + $token = new UsernamePasswordToken('username', '', 'key', array('ROLE_FOO')); - $this->tokenStorage->expects($this->any())->method('getToken')->will($this->returnValue($token)); - $this->request->expects($this->any())->method('get')->with('_switch_user')->will($this->returnValue('_exit')); + $this->tokenStorage->setToken($token); + $this->request->query->set('_switch_user', '_exit'); $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager); $listener->handle($this->event); @@ -78,29 +81,19 @@ public function testExitUserThrowsAuthenticationExceptionIfOriginalTokenCannotBe public function testExitUserUpdatesToken() { - $originalToken = $this->getToken(); - $role = $this->getMockBuilder('Symfony\Component\Security\Core\Role\SwitchUserRole') - ->disableOriginalConstructor() - ->getMock(); - $role->expects($this->any())->method('getSource')->will($this->returnValue($originalToken)); - - $this->tokenStorage->expects($this->any()) - ->method('getToken') - ->will($this->returnValue($this->getToken(array($role)))); - - $this->request->expects($this->any())->method('get')->with('_switch_user')->will($this->returnValue('_exit')); - $this->request->expects($this->any())->method('getUri')->will($this->returnValue('/')); - $this->request->query->expects($this->once())->method('remove', '_switch_user'); - $this->request->query->expects($this->any())->method('all')->will($this->returnValue(array())); - $this->request->server->expects($this->once())->method('set')->with('QUERY_STRING', ''); - - $this->tokenStorage->expects($this->once()) - ->method('setToken')->with($originalToken); - $this->event->expects($this->once()) - ->method('setResponse')->with($this->isInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse')); + $originalToken = new UsernamePasswordToken('username', '', 'key', array()); + $this->tokenStorage->setToken(new UsernamePasswordToken('username', '', 'key', array(new SwitchUserRole('ROLE_PREVIOUS', $originalToken)))); + + $this->request->query->set('_switch_user', '_exit'); $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager); $listener->handle($this->event); + + $this->assertSame(array(), $this->request->query->all()); + $this->assertSame('', $this->request->server->get('QUERY_STRING')); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $this->event->getResponse()); + $this->assertSame($this->request->getUri(), $this->event->getResponse()->getTargetUrl()); + $this->assertSame($originalToken, $this->tokenStorage->getToken()); } public function testExitUserDispatchesEventWithRefreshedUser() @@ -113,38 +106,9 @@ public function testExitUserDispatchesEventWithRefreshedUser() ->method('refreshUser') ->with($originalUser) ->willReturn($refreshedUser); - $originalToken = $this->getToken(); - $originalToken - ->expects($this->any()) - ->method('getUser') - ->willReturn($originalUser); - $role = $this - ->getMockBuilder('Symfony\Component\Security\Core\Role\SwitchUserRole') - ->disableOriginalConstructor() - ->getMock(); - $role->expects($this->any())->method('getSource')->willReturn($originalToken); - $this - ->tokenStorage - ->expects($this->any()) - ->method('getToken') - ->willReturn($this->getToken(array($role))); - $this - ->request - ->expects($this->any()) - ->method('get') - ->with('_switch_user') - ->willReturn('_exit'); - $this - ->request - ->expects($this->any()) - ->method('getUri') - ->willReturn('/'); - $this - ->request - ->query - ->expects($this->any()) - ->method('all') - ->will($this->returnValue(array())); + $originalToken = new UsernamePasswordToken($originalUser, '', 'key'); + $this->tokenStorage->setToken(new UsernamePasswordToken('username', '', 'key', array(new SwitchUserRole('ROLE_PREVIOUS', $originalToken)))); + $this->request->query->set('_switch_user', '_exit'); $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock(); $dispatcher @@ -166,41 +130,9 @@ public function testExitUserDoesNotDispatchEventWithStringUser() ->userProvider ->expects($this->never()) ->method('refreshUser'); - $originalToken = $this->getToken(); - $originalToken - ->expects($this->any()) - ->method('getUser') - ->willReturn($originalUser); - $role = $this - ->getMockBuilder('Symfony\Component\Security\Core\Role\SwitchUserRole') - ->disableOriginalConstructor() - ->getMock(); - $role - ->expects($this->any()) - ->method('getSource') - ->willReturn($originalToken); - $this - ->tokenStorage - ->expects($this->any()) - ->method('getToken') - ->willReturn($this->getToken(array($role))); - $this - ->request - ->expects($this->any()) - ->method('get') - ->with('_switch_user') - ->willReturn('_exit'); - $this - ->request - ->query - ->expects($this->any()) - ->method('all') - ->will($this->returnValue(array())); - $this - ->request - ->expects($this->any()) - ->method('getUri') - ->willReturn('/'); + $originalToken = new UsernamePasswordToken($originalUser, '', 'key'); + $this->tokenStorage->setToken(new UsernamePasswordToken('username', '', 'key', array(new SwitchUserRole('ROLE_PREVIOUS', $originalToken)))); + $this->request->query->set('_switch_user', '_exit'); $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock(); $dispatcher @@ -217,10 +149,10 @@ public function testExitUserDoesNotDispatchEventWithStringUser() */ public function testSwitchUserIsDisallowed() { - $token = $this->getToken(array($this->getMockBuilder('Symfony\Component\Security\Core\Role\RoleInterface')->getMock())); + $token = new UsernamePasswordToken('username', '', 'key', array('ROLE_FOO')); - $this->tokenStorage->expects($this->any())->method('getToken')->will($this->returnValue($token)); - $this->request->expects($this->any())->method('get')->with('_switch_user')->will($this->returnValue('kuba')); + $this->tokenStorage->setToken($token); + $this->request->query->set('_switch_user', 'kuba'); $this->accessDecisionManager->expects($this->once()) ->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH')) @@ -232,17 +164,11 @@ public function testSwitchUserIsDisallowed() public function testSwitchUser() { - $token = $this->getToken(array($this->getMockBuilder('Symfony\Component\Security\Core\Role\RoleInterface')->getMock())); - $user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); - $user->expects($this->any())->method('getRoles')->will($this->returnValue(array())); + $token = new UsernamePasswordToken('username', '', 'key', array('ROLE_FOO')); + $user = new User('username', 'password', array()); - $this->tokenStorage->expects($this->any())->method('getToken')->will($this->returnValue($token)); - $this->request->expects($this->any())->method('get')->with('_switch_user')->will($this->returnValue('kuba')); - $this->request->query->expects($this->once())->method('remove', '_switch_user'); - $this->request->query->expects($this->any())->method('all')->will($this->returnValue(array())); - - $this->request->expects($this->any())->method('getUri')->will($this->returnValue('/')); - $this->request->server->expects($this->once())->method('set')->with('QUERY_STRING', ''); + $this->tokenStorage->setToken($token); + $this->request->query->set('_switch_user', 'kuba'); $this->accessDecisionManager->expects($this->once()) ->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH')) @@ -253,25 +179,26 @@ public function testSwitchUser() ->will($this->returnValue($user)); $this->userChecker->expects($this->once()) ->method('checkPostAuth')->with($user); - $this->tokenStorage->expects($this->once()) - ->method('setToken')->with($this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken')); $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager); $listener->handle($this->event); + + $this->assertSame(array(), $this->request->query->all()); + $this->assertSame('', $this->request->server->get('QUERY_STRING')); + $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $this->tokenStorage->getToken()); } public function testSwitchUserKeepsOtherQueryStringParameters() { - $token = $this->getToken(array($this->getMockBuilder('Symfony\Component\Security\Core\Role\RoleInterface')->getMock())); - $user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); - $user->expects($this->any())->method('getRoles')->will($this->returnValue(array())); + $token = new UsernamePasswordToken('username', '', 'key', array('ROLE_FOO')); + $user = new User('username', 'password', array()); - $this->tokenStorage->expects($this->any())->method('getToken')->will($this->returnValue($token)); - $this->request->expects($this->any())->method('get')->with('_switch_user')->will($this->returnValue('kuba')); - $this->request->query->expects($this->once())->method('remove', '_switch_user'); - $this->request->query->expects($this->any())->method('all')->will($this->returnValue(array('page' => 3, 'section' => 2))); - $this->request->expects($this->any())->method('getUri')->will($this->returnValue('/')); - $this->request->server->expects($this->once())->method('set')->with('QUERY_STRING', 'page=3§ion=2'); + $this->tokenStorage->setToken($token); + $this->request->query->replace(array( + '_switch_user' => 'kuba', + 'page' => 3, + 'section' => 2, + )); $this->accessDecisionManager->expects($this->once()) ->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH')) @@ -282,33 +209,11 @@ public function testSwitchUserKeepsOtherQueryStringParameters() ->will($this->returnValue($user)); $this->userChecker->expects($this->once()) ->method('checkPostAuth')->with($user); - $this->tokenStorage->expects($this->once()) - ->method('setToken')->with($this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken')); $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager); $listener->handle($this->event); - } - - private function getEvent($request) - { - $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent') - ->disableOriginalConstructor() - ->getMock(); - - $event->expects($this->any()) - ->method('getRequest') - ->will($this->returnValue($request)); - - return $event; - } - - private function getToken(array $roles = array()) - { - $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); - $token->expects($this->any()) - ->method('getRoles') - ->will($this->returnValue($roles)); - return $token; + $this->assertSame('page=3§ion=2', $this->request->server->get('QUERY_STRING')); + $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $this->tokenStorage->getToken()); } } diff --git a/src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php b/src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php index eb0379f97b4f5..3d0e63b6fe1b9 100644 --- a/src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php +++ b/src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php @@ -144,7 +144,7 @@ public function testCheckRequestPath() // Plus must not decoded to space $this->assertTrue($utils->checkRequestPath($this->getRequest('/foo+bar'), '/foo+bar')); // Checking unicode - $this->assertTrue($utils->checkRequestPath($this->getRequest(urlencode('/вход')), '/вход')); + $this->assertTrue($utils->checkRequestPath($this->getRequest('/'.urlencode('вход')), '/вход')); } public function testCheckRequestPathWithUrlMatcherAndResourceNotFound() diff --git a/src/Symfony/Component/Security/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php b/src/Symfony/Component/Security/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php index 386927e3d50b9..1850d093dd4f6 100644 --- a/src/Symfony/Component/Security/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php +++ b/src/Symfony/Component/Security/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php @@ -251,6 +251,7 @@ public function getPositiveRememberMeParameterValues() array('1'), array('on'), array('yes'), + array(true), ); } diff --git a/src/Symfony/Component/Security/Tests/Resources/TranslationFilesTest.php b/src/Symfony/Component/Security/Tests/Resources/TranslationFilesTest.php index 5e959b30d4fbc..30b9aed6ec873 100644 --- a/src/Symfony/Component/Security/Tests/Resources/TranslationFilesTest.php +++ b/src/Symfony/Component/Security/Tests/Resources/TranslationFilesTest.php @@ -25,6 +25,8 @@ public function testTranslationFileIsValid($filePath) } else { \PHPUnit\Util\XML::loadfile($filePath, false, false, true); } + + $this->addToAssertionCount(1); } public function provideTranslationFiles() diff --git a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php index 0bd85b0247961..40f61167b3698 100644 --- a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php @@ -92,14 +92,16 @@ public function decode($data, $format, array $context = array()) throw new UnexpectedValueException($error->message); } + $rootNode = null; foreach ($dom->childNodes as $child) { if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) { throw new UnexpectedValueException('Document types are not allowed.'); } + if (!$rootNode && $child->nodeType !== XML_PI_NODE) { + $rootNode = $child; + } } - $rootNode = $dom->firstChild; - // todo: throw an exception if the root node name is not correctly configured (bc) if ($rootNode->hasChildNodes()) { @@ -329,6 +331,10 @@ private function parseXmlValue(\DOMNode $node) $value = array(); foreach ($node->childNodes as $subnode) { + if ($subnode->nodeType === XML_PI_NODE) { + continue; + } + $val = $this->parseXml($subnode); if ('item' === $subnode->nodeName && isset($val['@key'])) { diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php index 4e83f3464f200..e036fbbaf486b 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php @@ -17,7 +17,6 @@ use Symfony\Component\Serializer\Tests\Fixtures\ScalarDummy; use Symfony\Component\Serializer\Encoder\XmlEncoder; use Symfony\Component\Serializer\Serializer; -use Symfony\Component\Serializer\Exception\UnexpectedValueException; use Symfony\Component\Serializer\Normalizer\CustomNormalizer; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; @@ -371,6 +370,44 @@ public function testDecodeArray() $this->assertEquals($expected, $this->encoder->decode($source, 'xml')); } + public function testDecodeXMLWithProcessInstruction() + { + $source = <<<'XML' + + + + + + foo + + a + b + + val + val + bar + + title1 + + + + title2 + + + + Ed + + + + 1 + + ?> +XML; + $obj = $this->getObject(); + + $this->assertEquals(get_object_vars($obj), $this->encoder->decode($source, 'xml')); + } + public function testDecodeIgnoreWhiteSpace() { $source = <<<'XML' @@ -435,23 +472,12 @@ public function testDecodeInvalidXml() $this->encoder->decode('', 'xml'); } + /** + * @expectedException \Symfony\Component\Serializer\Exception\UnexpectedValueException + */ public function testPreventsComplexExternalEntities() { - $oldCwd = getcwd(); - chdir(__DIR__); - - try { - $this->encoder->decode(']>&test;', 'xml'); - chdir($oldCwd); - - $this->fail('No exception was thrown.'); - } catch (\Exception $e) { - chdir($oldCwd); - - if (!$e instanceof UnexpectedValueException) { - $this->fail('Expected UnexpectedValueException'); - } - } + $this->encoder->decode(']>&test;', 'xml'); } public function testDecodeEmptyXml() diff --git a/src/Symfony/Component/Translation/Tests/PluralizationRulesTest.php b/src/Symfony/Component/Translation/Tests/PluralizationRulesTest.php index 8e499fd6edace..8a6723ea9cb1a 100644 --- a/src/Symfony/Component/Translation/Tests/PluralizationRulesTest.php +++ b/src/Symfony/Component/Translation/Tests/PluralizationRulesTest.php @@ -65,7 +65,6 @@ public function successLangcodes() array('2', array('nl', 'fr', 'en', 'de', 'de_GE', 'hy', 'hy_AM')), array('3', array('be', 'bs', 'cs', 'hr')), array('4', array('cy', 'mt', 'sl')), - array('5', array()), array('6', array('ar')), ); } @@ -86,7 +85,6 @@ public function failingLangcodes() array('3', array('cbs')), array('4', array('gd', 'kw')), array('5', array('ga')), - array('6', array()), ); } diff --git a/src/Symfony/Component/Translation/Tests/TranslatorTest.php b/src/Symfony/Component/Translation/Tests/TranslatorTest.php index f02eb57b358fe..960d8f4d3c549 100644 --- a/src/Symfony/Component/Translation/Tests/TranslatorTest.php +++ b/src/Symfony/Component/Translation/Tests/TranslatorTest.php @@ -156,6 +156,7 @@ public function testSetFallbackValidLocales($locale) $translator = new Translator($locale, new MessageSelector()); $translator->setFallbackLocales(array('fr', $locale)); // no assertion. this method just asserts that no exception is thrown + $this->addToAssertionCount(1); } public function testTransWithFallbackLocale() @@ -187,6 +188,7 @@ public function testAddResourceValidLocales($locale) $translator = new Translator('fr', new MessageSelector()); $translator->addResource('array', array('foo' => 'foofoo'), $locale); // no assertion. this method just asserts that no exception is thrown + $this->addToAssertionCount(1); } public function testAddResourceAfterTrans() @@ -390,6 +392,7 @@ public function testTransChoiceValidLocale($locale) $translator->transChoice('foo', 1, array(), '', $locale); // no assertion. this method just asserts that no exception is thrown + $this->addToAssertionCount(1); } public function getTransFileTests() diff --git a/src/Symfony/Component/Validator/Constraints/Composite.php b/src/Symfony/Component/Validator/Constraints/Composite.php index ab8466bcfcbc4..d1391bb0724af 100644 --- a/src/Symfony/Component/Validator/Constraints/Composite.php +++ b/src/Symfony/Component/Validator/Constraints/Composite.php @@ -67,6 +67,10 @@ public function __construct($options = null) foreach ($nestedConstraints as $constraint) { if (!$constraint instanceof Constraint) { + if (is_object($constraint)) { + $constraint = get_class($constraint); + } + throw new ConstraintDefinitionException(sprintf('The value %s is not an instance of Constraint in constraint %s', $constraint, get_class($this))); } diff --git a/src/Symfony/Component/Validator/Constraints/UrlValidator.php b/src/Symfony/Component/Validator/Constraints/UrlValidator.php index 401f7d8bdd648..db7244f40ce7b 100644 --- a/src/Symfony/Component/Validator/Constraints/UrlValidator.php +++ b/src/Symfony/Component/Validator/Constraints/UrlValidator.php @@ -34,7 +34,9 @@ class UrlValidator extends ConstraintValidator \] # an IPv6 address ) (:[0-9]+)? # a port (optional) - (/?|/\S+|\?\S*|\#\S*) # a /, nothing, a / with something, a query or a fragment + (?:/ (?:[\pL\pN\-._\~!$&\'()*+,;=:@]|%%[0-9A-Fa-f]{2})* )* # a path + (?:\? (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )? # a query (optional) + (?:\# (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )? # a fragment (optional) $~ixu'; /** diff --git a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php index fe5e6e51e2ec4..f895ad40402dc 100644 --- a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php @@ -299,9 +299,8 @@ public function addPropertyConstraints($property, array $constraints) * The name of the getter is assumed to be the name of the property with an * uppercased first letter and either the prefix "get" or "is". * - * @param string $property The name of the property - * @param Constraint $constraint The constraint - * @param string|null $method The method that is called to retrieve the value being validated (null for auto-detection) + * @param string $property The name of the property + * @param Constraint $constraint The constraint * * @return $this */ diff --git a/src/Symfony/Component/Validator/Tests/ConstraintTest.php b/src/Symfony/Component/Validator/Tests/ConstraintTest.php index 1b1219664402b..a05741490fdde 100644 --- a/src/Symfony/Component/Validator/Tests/ConstraintTest.php +++ b/src/Symfony/Component/Validator/Tests/ConstraintTest.php @@ -115,7 +115,9 @@ public function testRequiredOptionsMustBeDefined() public function testRequiredOptionsPassed() { - new ConstraintC(array('option1' => 'default')); + $constraint = new ConstraintC(array('option1' => 'default')); + + $this->assertSame('default', $constraint->option1); } public function testGroupsAreConvertedToArray() @@ -140,7 +142,9 @@ public function testAllowsSettingZeroRequiredPropertyValue() public function testCanCreateConstraintWithNoDefaultOptionAndEmptyArray() { - new ConstraintB(array()); + $constraint = new ConstraintB(array()); + + $this->assertSame(array(Constraint::PROPERTY_CONSTRAINT, Constraint::CLASS_CONSTRAINT), $constraint->getTargets()); } public function testGetTargetsCanBeString() diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CallbackValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CallbackValidatorTest.php index 6bd7735f0d7d5..e87c9c4752a2e 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CallbackValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CallbackValidatorTest.php @@ -330,7 +330,9 @@ public function testConstraintGetTargets() // Should succeed. Needed when defining constraints as annotations. public function testNoConstructorArguments() { - new Callback(); + $constraint = new Callback(); + + $this->assertSame(array(Constraint::CLASS_CONSTRAINT, Constraint::PROPERTY_CONSTRAINT), $constraint->getTargets()); } public function testAnnotationInvocationSingleValued() diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CompositeTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CompositeTest.php index 6a8530723fb05..df4255007c5ca 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CompositeTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CompositeTest.php @@ -125,6 +125,17 @@ public function testFailIfNoConstraint() )); } + /** + * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException + */ + public function testFailIfNoConstraintObject() + { + new ConcreteComposite(array( + new NotNull(array('groups' => 'Default')), + new \ArrayObject(), + )); + } + /** * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException */ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/GroupSequenceTest.php b/src/Symfony/Component/Validator/Tests/Constraints/GroupSequenceTest.php index 6326d0514ba3e..f298cd27e6257 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/GroupSequenceTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/GroupSequenceTest.php @@ -94,5 +94,7 @@ public function testLegacyUnsetIgnoresNonExistingKeys() // should not fail unset($sequence[2]); + + $this->assertCount(2, $sequence); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php index 98494c66bf361..39f1708cf18ad 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php @@ -128,6 +128,7 @@ public function getValidUrls() array('http://symfony.com#'), array('http://symfony.com#fragment'), array('http://symfony.com/#fragment'), + array('http://symfony.com/#one_more%20test'), ); } @@ -167,6 +168,9 @@ public function getInvalidUrls() array('http://:password@@symfony.com'), array('http://username:passwordsymfony.com'), array('http://usern@me:password@symfony.com'), + array('http://example.com/exploit.html?'), + array('http://example.com/exploit.html?hel lo'), + array('http://example.com/exploit.html?not_a%hex'), ); } diff --git a/src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php b/src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php index 8ca3c9fbcb606..9a23e8cf355a0 100644 --- a/src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php +++ b/src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php @@ -245,19 +245,23 @@ public function testSerialize() public function testGroupSequencesWorkIfContainingDefaultGroup() { $this->metadata->setGroupSequence(array('Foo', $this->metadata->getDefaultGroup())); + + $this->assertInstanceOf('Symfony\Component\Validator\Constraints\GroupSequence', $this->metadata->getGroupSequence()); } + /** + * @expectedException \Symfony\Component\Validator\Exception\GroupDefinitionException + */ public function testGroupSequencesFailIfNotContainingDefaultGroup() { - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\Validator\Exception\GroupDefinitionException'); - $this->metadata->setGroupSequence(array('Foo', 'Bar')); } + /** + * @expectedException \Symfony\Component\Validator\Exception\GroupDefinitionException + */ public function testGroupSequencesFailIfContainingDefault() { - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\Validator\Exception\GroupDefinitionException'); - $this->metadata->setGroupSequence(array('Foo', $this->metadata->getDefaultGroup(), Constraint::DEFAULT_GROUP)); } diff --git a/src/Symfony/Component/Validator/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php b/src/Symfony/Component/Validator/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php index 1ebce65cc2029..b5d1a9dc84d4d 100644 --- a/src/Symfony/Component/Validator/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php +++ b/src/Symfony/Component/Validator/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php @@ -167,7 +167,11 @@ public function testMetadataCacheWithRuntimeConstraint() $metadata = $factory->getMetadataFor(self::PARENT_CLASS); $metadata->addConstraint(new Callback(function () {})); + $this->assertCount(3, $metadata->getConstraints()); + $metadata = $factory->getMetadataFor(self::CLASS_NAME); + + $this->assertCount(6, $metadata->getConstraints()); } public function testGroupsFromParent() diff --git a/src/Symfony/Component/Validator/Tests/Resources/TranslationFilesTest.php b/src/Symfony/Component/Validator/Tests/Resources/TranslationFilesTest.php index d33351352777c..96311cfeff326 100644 --- a/src/Symfony/Component/Validator/Tests/Resources/TranslationFilesTest.php +++ b/src/Symfony/Component/Validator/Tests/Resources/TranslationFilesTest.php @@ -25,6 +25,8 @@ public function testTranslationFileIsValid($filePath) } else { \PHPUnit\Util\XML::loadfile($filePath, false, false, true); } + + $this->addToAssertionCount(1); } public function provideTranslationFiles() diff --git a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php index 8452590d63dad..b6c0c28f50eb7 100644 --- a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php @@ -64,7 +64,7 @@ public static function castThrowingCasterException(ThrowingCasterException $e, a $prefix = Caster::PREFIX_PROTECTED; $xPrefix = "\0Exception\0"; - if (isset($a[$xPrefix.'previous'], $a[$xPrefix.'trace'][0])) { + if (isset($a[$xPrefix.'previous'], $a[$xPrefix.'trace'][0]) && $a[$xPrefix.'previous'] instanceof \Exception) { $b = (array) $a[$xPrefix.'previous']; $b[$xPrefix.'trace'][0] += array( 'file' => $b[$prefix.'file'], diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index aa1833745a95b..2255379a974c6 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -149,8 +149,8 @@ public static function dump($value, $exceptionOnInvalidType = false, $objectSupp case Escaper::requiresDoubleQuoting($value): return Escaper::escapeWithDoubleQuotes($value); case Escaper::requiresSingleQuoting($value): - case preg_match(self::getHexRegex(), $value): - case preg_match(self::getTimestampRegex(), $value): + case Parser::preg_match(self::getHexRegex(), $value): + case Parser::preg_match(self::getTimestampRegex(), $value): return Escaper::escapeWithSingleQuotes($value); default: return $value; @@ -242,10 +242,10 @@ public static function parseScalar($scalar, $delimiters = null, $stringDelimiter $i += strlen($output); // remove comments - if (preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) { + if (Parser::preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) { $output = substr($output, 0, $match[0][1]); } - } elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) { + } elseif (Parser::preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) { $output = $match[1]; $i += strlen($output); } else { @@ -272,7 +272,7 @@ public static function parseScalar($scalar, $delimiters = null, $stringDelimiter */ private static function parseQuotedScalar($scalar, &$i) { - if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) { + if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) { throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i))); } @@ -520,16 +520,16 @@ private static function evaluateScalar($scalar, $references = array()) return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw); case is_numeric($scalar): - case preg_match(self::getHexRegex(), $scalar): + case Parser::preg_match(self::getHexRegex(), $scalar): return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar; case '.inf' === $scalarLower: case '.nan' === $scalarLower: return -log(0); case '-.inf' === $scalarLower: return log(0); - case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar): + case Parser::preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar): return (float) str_replace(',', '', $scalar); - case preg_match(self::getTimestampRegex(), $scalar): + case Parser::preg_match(self::getTimestampRegex(), $scalar): $timeZone = date_default_timezone_get(); date_default_timezone_set('UTC'); $time = strtotime($scalar); diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 46968b5d8a2f7..345bcbdac1731 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -61,7 +61,7 @@ public function __construct($offset = 0, $totalNumberOfLines = null, array $skip */ public function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false) { - if (!preg_match('//u', $value)) { + if (false === preg_match('//u', $value)) { throw new ParseException('The YAML value does not appear to be valid UTF-8.'); } $this->currentLineNb = -1; @@ -92,13 +92,13 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport = } $isRef = $mergeNode = false; - if (preg_match('#^\-((?P\s+)(?P.+?))?\s*$#u', $this->currentLine, $values)) { + if (self::preg_match('#^\-((?P\s+)(?P.+))?$#u', rtrim($this->currentLine), $values)) { if ($context && 'mapping' == $context) { throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine); } $context = 'sequence'; - if (isset($values['value']) && preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { + if (isset($values['value']) && self::preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { $isRef = $matches['ref']; $values['value'] = $matches['value']; } @@ -108,7 +108,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport = $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $exceptionOnInvalidType, $objectSupport, $objectForMap); } else { if (isset($values['leadspaces']) - && preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P.+?))?\s*$#u', $values['value'], $matches) + && self::preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P.+))?$#u', rtrim($values['value']), $matches) ) { // this is a compact notation element, add to next block and parse $block = $values['value']; @@ -124,7 +124,10 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport = if ($isRef) { $this->refs[$isRef] = end($data); } - } elseif (preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P.+?))?\s*$#u', $this->currentLine, $values) && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'")))) { + } elseif ( + self::preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P.+))?$#u', rtrim($this->currentLine), $values) + && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'"))) + ) { if ($context && 'sequence' == $context) { throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine); } @@ -191,7 +194,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport = $data += $parsed; // array union } } - } elseif (isset($values['value']) && preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { + } elseif (isset($values['value']) && self::preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { $isRef = $matches['ref']; $values['value'] = $matches['value']; } @@ -254,27 +257,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport = return $value; } - switch (preg_last_error()) { - case PREG_INTERNAL_ERROR: - $error = 'Internal PCRE error.'; - break; - case PREG_BACKTRACK_LIMIT_ERROR: - $error = 'pcre.backtrack_limit reached.'; - break; - case PREG_RECURSION_LIMIT_ERROR: - $error = 'pcre.recursion_limit reached.'; - break; - case PREG_BAD_UTF8_ERROR: - $error = 'Malformed UTF-8 data.'; - break; - case PREG_BAD_UTF8_OFFSET_ERROR: - $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.'; - break; - default: - $error = 'Unable to parse.'; - } - - throw new ParseException($error, $this->getRealCurrentLineNb() + 1, $this->currentLine); + throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine); } } @@ -508,7 +491,7 @@ private function parseValue($value, $exceptionOnInvalidType, $objectSupport, $ob return $this->refs[$value]; } - if (preg_match('/^'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) { + if (self::preg_match('/^'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) { $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : ''; return $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers)); @@ -554,7 +537,7 @@ private function parseBlockScalar($style, $chomping = '', $indentation = 0) // determine indentation if not specified if (0 === $indentation) { - if (preg_match('/^ +/', $this->currentLine, $matches)) { + if (self::preg_match('/^ +/', $this->currentLine, $matches)) { $indentation = strlen($matches[0]); } } @@ -565,7 +548,7 @@ private function parseBlockScalar($style, $chomping = '', $indentation = 0) while ( $notEOF && ( $isCurrentLineBlank || - preg_match($pattern, $this->currentLine, $matches) + self::preg_match($pattern, $this->currentLine, $matches) ) ) { if ($isCurrentLineBlank && strlen($this->currentLine) > $indentation) { @@ -653,10 +636,7 @@ private function isNextLineIndented() return false; } - $ret = false; - if ($this->getCurrentLineIndentation() > $currentIndentation) { - $ret = true; - } + $ret = $this->getCurrentLineIndentation() > $currentIndentation; $this->moveToPreviousLine(); @@ -757,14 +737,7 @@ private function isNextLineUnIndentedCollection() return false; } - $ret = false; - if ( - $this->getCurrentLineIndentation() == $currentIndentation - && - $this->isStringUnIndentedCollectionItem() - ) { - $ret = true; - } + $ret = $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem(); $this->moveToPreviousLine(); @@ -788,6 +761,48 @@ private function isStringUnIndentedCollectionItem() */ private function isBlockScalarHeader() { - return (bool) preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine); + return (bool) self::preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine); + } + + /** + * A local wrapper for `preg_match` which will throw a ParseException if there + * is an internal error in the PCRE engine. + * + * This avoids us needing to check for "false" every time PCRE is used + * in the YAML engine + * + * @throws ParseException on a PCRE internal error + * + * @see preg_last_error() + * + * @internal + */ + public static function preg_match($pattern, $subject, &$matches = null, $flags = 0, $offset = 0) + { + if (false === $ret = preg_match($pattern, $subject, $matches, $flags, $offset)) { + switch (preg_last_error()) { + case PREG_INTERNAL_ERROR: + $error = 'Internal PCRE error.'; + break; + case PREG_BACKTRACK_LIMIT_ERROR: + $error = 'pcre.backtrack_limit reached.'; + break; + case PREG_RECURSION_LIMIT_ERROR: + $error = 'pcre.recursion_limit reached.'; + break; + case PREG_BAD_UTF8_ERROR: + $error = 'Malformed UTF-8 data.'; + break; + case PREG_BAD_UTF8_OFFSET_ERROR: + $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.'; + break; + default: + $error = 'Error.'; + } + + throw new ParseException($error); + } + + return $ret; } } diff --git a/src/Symfony/Component/Yaml/Tests/DumperTest.php b/src/Symfony/Component/Yaml/Tests/DumperTest.php index c27ac5cc5c701..6a1b3ac3d6059 100644 --- a/src/Symfony/Component/Yaml/Tests/DumperTest.php +++ b/src/Symfony/Component/Yaml/Tests/DumperTest.php @@ -210,23 +210,25 @@ public function testEscapedEscapeSequencesInQuotedScalar($input, $expected) 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\\�", '"\t\\\\�"'), - 'line-separator' => array("\t\\L", '"\t\\\\L"'), - 'paragraph-separator' => array("\t\\P", '"\t\\\\P"'), + 'empty string' => array('', "''"), + 'null' => array("\x0", '"\\0"'), + 'bell' => array("\x7", '"\\a"'), + 'backspace' => array("\x8", '"\\b"'), + 'horizontal-tab' => array("\t", '"\\t"'), + 'line-feed' => array("\n", '"\\n"'), + 'vertical-tab' => array("\v", '"\\v"'), + 'form-feed' => array("\xC", '"\\f"'), + 'carriage-return' => array("\r", '"\\r"'), + 'escape' => array("\x1B", '"\\e"'), + 'space' => array(' ', "' '"), + 'double-quote' => array('"', "'\"'"), + 'slash' => array('/', '/'), + 'backslash' => array('\\', '\\'), + 'next-line' => array("\xC2\x85", '"\\N"'), + 'non-breaking-space' => array("\xc2\xa0", '"\\_"'), + 'line-separator' => array("\xE2\x80\xA8", '"\\L"'), + 'paragraph-separator' => array("\xE2\x80\xA9", '"\\P"'), + 'colon' => array(':', "':'"), ); } diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 2ab83f44cc437..d6e5f775139ea 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -17,6 +17,7 @@ class ParserTest extends TestCase { + /** @var Parser */ protected $parser; protected function setUp() @@ -1146,6 +1147,17 @@ public function parserThrowsExceptionWithCorrectLineNumberProvider() ), ); } + + public function testCanParseVeryLongValue() + { + $longStringWithSpaces = str_repeat('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ', 20000); + $trickyVal = array('x' => $longStringWithSpaces); + + $yamlString = Yaml::dump($trickyVal); + $arrayFromYaml = $this->parser->parse($yamlString); + + $this->assertEquals($trickyVal, $arrayFromYaml); + } } class B