diff --git a/.php_cs b/.php_cs index 02d15784e65b1..77701dd97db39 100644 --- a/.php_cs +++ b/.php_cs @@ -6,6 +6,7 @@ return Symfony\CS\Config\Config::create() ->fixers(array( 'long_array_syntax', 'php_unit_construct', + 'php_unit_dedicate_assert', )) ->finder( Symfony\CS\Finder\DefaultFinder::create() diff --git a/.travis.yml b/.travis.yml index d9bfbbbe0c6ec..2f310a2dd3179 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,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.12 + - php: hhvm-stable sudo: required dist: trusty group: edge @@ -28,7 +28,7 @@ matrix: - php: 5.6 - php: 7.0 env: deps=high - - php: 7.0 + - php: 7.1 env: deps=low fast_finish: true @@ -56,7 +56,7 @@ before_install: - 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 - - if [[ ! $skip && $PHP = 7.0 ]]; then (echo yes | pecl install -f apcu-5.1.5 && echo apc.enable_cli = 1 >> $INI_FILE); fi + - if [[ ! $skip && $PHP = 7.* ]]; then (echo yes | pecl install -f apcu-5.1.6 && echo apc.enable_cli = 1 >> $INI_FILE); fi - if [[ ! $deps && $PHP = 5.* ]]; then (cd src/Symfony/Component/Debug/Resources/ext && phpize && ./configure && make && echo extension = $(pwd)/modules/symfony_debug.so >> $INI_FILE); fi - if [[ ! $skip && $PHP = 5.* ]]; then pecl install -f memcached-2.1.0; fi - if [[ ! $skip && ! $PHP = hhvm* ]]; then echo extension = ldap.so >> $INI_FILE; fi diff --git a/CHANGELOG-3.1.md b/CHANGELOG-3.1.md index 33ad4d31965d3..07d0350ddfada 100644 --- a/CHANGELOG-3.1.md +++ b/CHANGELOG-3.1.md @@ -7,6 +7,36 @@ in 3.1 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/v3.1.0...v3.1.1 +* 3.1.6 (2016-10-27) + + * bug #20291 [Yaml] Fix 7.1 compat (nicolas-grekas) + * bug #20289 Fix edge case with StreamedResponse where headers are sent twice (Nicofuma) + * bug #20267 [DependencyInjection] A decorated service should not keep the autowiring types (chalasr) + * bug #20278 [DependencyInjection] merge tags instead of completely replacing them (xabbuh) + * bug #20271 Changes related to Twig 1.27 (fabpot) + * bug #20252 Trim constant values in XmlFileLoader (lstrojny) + * bug #20239 [HttpKernel] Fix a regression in the RequestDataCollector (jakzal) + * bug #20253 [TwigBridge] Use non-deprecated Twig_Node::getTemplateLine() (fabpot) + * bug #20243 [WebProfilerBundle][btn-link] add `cursor: pointer` (aitboudad) + * bug #20175 [VarDumper] Fix source links with latests Twig versions (nicolas-grekas) + * bug #20235 [DomCrawler] Allow pipe (|) character in link tags when using Xpath expressions (klausi, nicolas-grekas) + * bug #20224 [Twig] removed deprecations added in Twig 1.27 (fabpot) + * bug #19478 fixed Filesystem:makePathRelative and added 2 more testcases (muhammedeminakbulut) + * bug #20218 [HttpFoundation] no 304 response if method is not cacheable (xabbuh) + * bug #20207 [DependencyInjection] move tags from decorated to decorating service (xabbuh) + * bug #20205 [HttpCache] fix: do not cache OPTIONS request (dmaicher) + * bug #20146 [Validator] Prevent infinite loop in PropertyMetadata (wesleylancel) + * bug #20184 [FrameworkBundle] Convert null prefix to an empty string in translation:update (chalasr) + * bug #20154 [PropertyInfo] Fix edge cases in ReflectionExtractor (nicolas-grekas) + * bug #19725 [Security] $attributes can be anything, but RoleVoter assumes strings (Jonatan Männchen) + * bug #20127 [HttpFoundation] JSONP callback validation (ro0NL) + * bug #20163 add missing use statement (xabbuh) + * bug #19961 [Console] Escape question text and default value in SymfonyStyle::ask() (chalasr) + * bug #20141 [Console] Fix validation of empty values using SymfonyQuestionHelper::ask() (chalasr) + * bug #20147 [FrameworkBundle] Alter container class instead of kernel name in cache:clear command (nicolas-grekas) + * bug #20156 Fix event annotation for arguments resolving event (Koc) + * bug #20152 [HttpKernel] Fix nullable types handling (nicolas-grekas) + * 3.1.5 (2016-10-03) * bug #20102 [Validator] Url validator not validating hosts ending in a number (gwkunze) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 7cbed979c4960..b0646cc1303cd 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -16,8 +16,8 @@ Symfony is the result of the work of many people who made the code better - Kris Wallsmith (kriswallsmith) - Jakub Zalas (jakubzalas) - Ryan Weaver (weaverryan) - - Javier Eguiluz (javier.eguiluz) - Kévin Dunglas (dunglas) + - Javier Eguiluz (javier.eguiluz) - Hugo Hamon (hhamon) - Abdellatif Ait boudad (aitboudad) - Pascal Borreli (pborreli) @@ -35,8 +35,8 @@ Symfony is the result of the work of many people who made the code better - Eriksen Costa (eriksencosta) - Sarah Khalil (saro0h) - Jules Pietri (heah) - - Jonathan Wage (jwage) - Maxime Steinhausser (ogizanagi) + - Jonathan Wage (jwage) - Diego Saint Esteben (dosten) - Alexandre Salomé (alexandresalome) - William Durand (couac) @@ -49,22 +49,22 @@ Symfony is the result of the work of many people who made the code better - Henrik Bjørnskov (henrikbjorn) - Miha Vrhovnik - Diego Saint Esteben (dii3g0) + - Ener-Getick (energetick) - Konstantin Kudryashov (everzet) - Bilal Amarni (bamarni) - Florin Patan (florinpatan) - - Ener-Getick (energetick) - Peter Rehm (rpet) - Iltar van der Berg (kjarli) + - Robin Chalas (chalas_r) - Kevin Bond (kbond) - Andrej Hudec (pulzarraider) - Gábor Egyed (1ed) - Michel Weimerskirch (mweimerskirch) - Eric Clemmons (ericclemmons) + - Charles Sarrazin (csarrazi) - Matthias Pigulla (mpdude) - Christian Raue - Arnout Boks (aboks) - - Charles Sarrazin (csarrazi) - - Robin Chalas (chalas_r) - Deni - Henrik Westphal (snc) - Dariusz Górecki (canni) @@ -72,22 +72,22 @@ Symfony is the result of the work of many people who made the code better - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) + - Graham Campbell (graham) + - Titouan Galopin (tgalopin) - Daniel Holmes (dholmes) - Pierre du Plessis (pierredup) + - Konstantin Myakshin (koc) - Bart van den Burg (burgov) - Jordan Alliot (jalliot) - - Graham Campbell (graham) - John Wards (johnwards) - Toni Uebernickel (havvg) - Fran Moreno (franmomu) - Antoine Hérault (herzult) + - Paráda József (paradajozsef) - Arnaud Le Blanc (arnaud-lb) - Jérôme Tamarelle (gromnan) - - Paráda József (paradajozsef) - - Titouan Galopin (tgalopin) - Michal Piotrowski (eventhorizon) - Tim Nagel (merk) - - Konstantin Myakshin (koc) - Brice BERNARD (brikou) - Alexander M. Turek (derrabus) - Dariusz Ruminski @@ -105,6 +105,7 @@ Symfony is the result of the work of many people who made the code better - excelwebzone - Jacob Dreesen (jdreesen) - Jáchym Toušek (enumag) + - Roland Franssen (ro0) - Jérémy DERUSSÉ (jderusse) - Vladimir Reznichenko (kalessil) - Tomáš Votruba (tomas_votruba) @@ -112,6 +113,7 @@ Symfony is the result of the work of many people who made the code better - Gordon Franke (gimler) - Eric GELOEN (gelo) - David Buchmann (dbu) + - Tugdual Saunier (tucksaun) - Théo FIDRY (theofidry) - Robert Schönthal (digitalkaoz) - Florian Lonqueu-Brochard (florianlb) @@ -123,7 +125,6 @@ Symfony is the result of the work of many people who made the code better - Daniel Gomes (danielcsgomes) - Hidenori Goto (hidenorigoto) - Sebastiaan Stok (sstok) - - Tugdual Saunier (tucksaun) - Guilherme Blanco (guilhermeblanco) - Pablo Godel (pgodel) - Jérémie Augustin (jaugustin) @@ -133,6 +134,7 @@ Symfony is the result of the work of many people who made the code better - Joel Wurtz (brouznouf) - Philipp Wahala (hifi) - Vyacheslav Pavlov + - Javier Spagnoletti (phansys) - Richard Shank (iampersistent) - Thomas Rabaix (rande) - Vincent AUBERT (vincent) @@ -144,10 +146,10 @@ Symfony is the result of the work of many people who made the code better - Richard van Laak (rvanlaak) - Matthieu Ouellette-Vachon (maoueh) - Michał Pipa (michal.pipa) - - Javier Spagnoletti (phansys) - Amal Raghav (kertz) - Jonathan Ingram (jonathaningram) - Artur Kotyrba + - Teoh Han Hui (teohhanhui) - Warnar Boekkooi (boekkooi) - Dmitrii Chekaliuk (lazyhammer) - Clément JOBEILI (dator) @@ -158,11 +160,12 @@ Symfony is the result of the work of many people who made the code better - Mario A. Alvarez Garcia (nomack84) - Dennis Benkert (denderello) - Benjamin Dulau (dbenjamin) + - Mathieu Lemoine (lemoinem) - Andreas Hucks (meandmymonkey) - Noel Guilbert (noel) + - Yonel Ceruto González (yonelceruto) - Stepan Anchugov (kix) - bronze1man - - Roland Franssen (ro0) - sun (sun) - Larry Garfield (crell) - Martin Schuhfuß (usefulthink) @@ -188,14 +191,13 @@ Symfony is the result of the work of many people who made the code better - Dawid Nowak - Eugene Wissner - Julien Brochet (mewt) - - Yonel Ceruto González (yonelceruto) + - jeremyFreeAgent (jeremyfreeagent) - Sergey Linnik (linniksa) - Michaël Perrin (michael.perrin) - Marcel Beerta (mazen) - Loïc Faugeron - Jannik Zschiesche (apfelbox) - Marco Pivetta (ocramius) - - Teoh Han Hui (teohhanhui) - julien pauli (jpauli) - Michael Lee (zerustech) - Lorenz Schori @@ -216,6 +218,7 @@ Symfony is the result of the work of many people who made the code better - Xavier Perez - Arjen Brouwer (arjenjb) - Katsuhiro OGAWA + - James Halsall (jaitsu) - Alif Rachmawadi - Kristen Gilden (kgilden) - Pierre-Yves LEBECQ (pylebecq) @@ -226,10 +229,10 @@ Symfony is the result of the work of many people who made the code better - Joseph Rouff (rouffj) - Félix Labrecque (woodspire) - GordonsLondon - - jeremyFreeAgent (jeremyfreeagent) - Jan Sorgalla (jsor) - Ray - Grégoire Paris (greg0ire) + - Leo Feyer - Chekote - Tobias Nyholm (tobias) - Thomas Adam @@ -252,12 +255,14 @@ Symfony is the result of the work of many people who made the code better - Peter Kruithof (pkruithof) - Michael Holm (hollo) - Marc Weistroff (futurecat) + - SpacePossum - Hidde Wieringa (hiddewie) - Chris Smith (cs278) - Florian Klein (docteurklein) - Oleg Voronkovich - Manuel Kiessling (manuelkiessling) - Daniel Wehner + - Tristan Darricau (nicofuma) - Atsuhiro KUBO (iteman) - Andrew Moore (finewolf) - Bertrand Zuchuat (garfield-fr) @@ -266,7 +271,6 @@ Symfony is the result of the work of many people who made the code better - Thomas Tourlourat (armetiz) - Andrey Esaulov (andremaha) - Grégoire Passault (gregwar) - - Leo Feyer - Ismael Ambrosi (iambrosi) - Uwe Jäger (uwej711) - Aurelijus Valeiša (aurelijus) @@ -315,14 +319,11 @@ Symfony is the result of the work of many people who made the code better - JhonnyL - hossein zolfi (ocean) - Clément Gautier (clementgautier) - - James Halsall (jaitsu) - Eduardo Gulias (egulias) - giulio de donato (liuggio) - Stéphane PY (steph_py) - Philipp Kräutli (pkraeutli) - Kirill chEbba Chebunin (chebba) - - Tristan Darricau (nicofuma) - - SpacePossum - Greg Thornton (xdissent) - Costin Bereveanu (schniper) - Loïc Chardonnet (gnusat) @@ -390,13 +391,13 @@ Symfony is the result of the work of many people who made the code better - Christian Schmidt - Marcin Sikoń (marphi) - Dominik Zogg (dominik.zogg) + - Maxime STEINHAUSSER - Marek Pietrzak - Chad Sikorra (chadsikorra) - - Mathieu Lemoine - franek (franek) - Christian Wahler - - Mathieu Lemoine - Gintautas Miselis + - Rob Bast - David Badura (davidbadura) - Zander Baldwin - Adam Harvey @@ -414,6 +415,7 @@ Symfony is the result of the work of many people who made the code better - Sebastian Bergmann - Pablo Díez (pablodip) - Kevin McBride + - Sergio Santoro - Philipp Rieber (bicpi) - Manuel de Ruiter (manuel) - Eduardo Oliveira (entering) @@ -545,10 +547,10 @@ Symfony is the result of the work of many people who made the code better - Luc Vieillescazes (iamluc) - Johann Saunier (prophet777) - Antoine Corcy - - Rob Bast - Artur Eshenbrener - Arturs Vonda - Sascha Grossenbacher + - David Maicher (dmaicher) - Szijarto Tamas - Catalin Dan - Stephan Vock @@ -559,9 +561,10 @@ Symfony is the result of the work of many people who made the code better - Tristan Roussel - Cameron Porter - Hossein Bukhamsin + - Oliver Hoff - Disparity - origaminal - - Maxime STEINHAUSSER + - Matteo Beccati (matteobeccati) - Paweł Wacławczyk (pwc) - Oleg Zinchenko (cystbear) - Johannes Klauss (cloppy) @@ -581,7 +584,6 @@ Symfony is the result of the work of many people who made the code better - Michael Piecko - yclian - twifty - - Sergio Santoro - Peter Ward - Dominik Ritter (dritter) - Sebastian Grodzicki (sgrodzicki) @@ -604,6 +606,7 @@ Symfony is the result of the work of many people who made the code better - Andrew Tchircoff (andrewtch) - michaelwilliams - 1emming + - Victor Bocharsky (bocharsky_bw) - Leevi Graham (leevigraham) - Casper Valdemar Poulsen - Josiah (josiah) @@ -651,6 +654,7 @@ Symfony is the result of the work of many people who made the code better - Fabian Vogler (fabian) - Korvin Szanto - Alaattin Kahramanlar (alaattin) + - Sergey Zolotov (enleur) - Maksim Kotlyar (makasim) - Neil Ferreira - Nathanael Noblet (gnat) @@ -722,6 +726,7 @@ Symfony is the result of the work of many people who made the code better - Benoît Merlet (trompette) - Koen Kuipers - datibbaw + - Erik Saunier (snickers) - Rootie - Raul Fraile (raulfraile) - sensio @@ -731,6 +736,7 @@ Symfony is the result of the work of many people who made the code better - Mátyás Somfai (smatyas) - stefan.r - Valérian Galliat + - Rikijs Murgs - Alexandru Furculita (afurculita) - Ben Ramsey (ramsey) - Christian Jul Jensen @@ -810,6 +816,7 @@ Symfony is the result of the work of many people who made the code better - Benjamin Paap (benjaminpaap) - Christian - Sergii Smertin (nfx) + - hugofonseca (fonsecas72) - Bailey Parker - Eddie Jaoude - Haritz Iturbe (hizai) @@ -837,7 +844,6 @@ Symfony is the result of the work of many people who made the code better - Rodrigo Díez Villamuera (rodrigodiez) - e-ivanov - Jochen Bayer (jocl) - - Matteo Beccati (matteobeccati) - Jeremy Bush - wizhippo - Viacheslav Sychov @@ -887,7 +893,6 @@ Symfony is the result of the work of many people who made the code better - Christian Sciberras - Clement Herreman (clemherreman) - Dan Ionut Dumitriu (danionut90) - - David Maicher (dmaicher) - Nyro (nyro) - Marco - Marc Torres @@ -983,7 +988,6 @@ Symfony is the result of the work of many people who made the code better - Tadcka - Beth Binkovitz - Romain Geissler - - Oliver Hoff - Tomaz Ahlin - Benjamin Cremer (bcremer) - Marcus Stöhr (dafish) @@ -1012,6 +1016,7 @@ Symfony is the result of the work of many people who made the code better - BilgeXA - r1pp3rj4ck - Robert Queck + - Peter Bouwdewijn - mlively - Amine Matmati - Fabian Steiner (fabstei) @@ -1040,6 +1045,7 @@ Symfony is the result of the work of many people who made the code better - Konrad Mohrfeldt - Lance Chen - Andrey Astakhov (aast) + - Andrew (drew) - Nikolay Labinskiy (e-moe) - kor3k kor3k (kor3k) - Stelian Mocanita (stelian) @@ -1203,7 +1209,6 @@ Symfony is the result of the work of many people who made the code better - J Bruni - Alexey Prilipko - bertillon - - Victor Bocharsky (bocharsky_bw) - Yannick Bensacq (cibou) - Luca Genuzio (genuzio) - Hans Nilsson (hansnilsson) @@ -1236,6 +1241,7 @@ Symfony is the result of the work of many people who made the code better - Julien DIDIER (juliendidier) - Martin Mayer (martin) - Grzegorz Łukaszewicz (newicz) + - Götz Gottwald - Veres Lajos - grifx - Robert Campbell @@ -1462,8 +1468,8 @@ Symfony is the result of the work of many people who made the code better - Adel ELHAIBA (eadel) - Damián Nohales (eagleoneraptor) - Elliot Anderson (elliot) - - Sergey Zolotov (enleur) - Fabien D. (fabd) + - Fabien Bourigault (fbourigault) - Carsten Eilers (fnc) - Sorin Gitlan (forapathy) - Yohan Giarelli (frequence-web) @@ -1498,6 +1504,7 @@ Symfony is the result of the work of many people who made the code better - Paul Andrieux (paulandrieux) - Paweł Szczepanek (pauluz) - Philippe Degeeter (pdegeeter) + - Pedro Miguel Maymone de Resende (pedroresende) - Christian López Espínola (penyaskito) - Petr Jaroš (petajaros) - Philipp Hoffmann (philipphoffmann) @@ -1522,6 +1529,7 @@ Symfony is the result of the work of many people who made the code better - Moritz Kraft (userfriendly) - Víctor Mateo (victormateo) - Vincent (vincent1870) + - Valentin Udaltsov (vudaltsov) - Eugene Babushkin (warl) - Wouter Sioen (wouter_sioen) - Xavier Amado (xamado) @@ -1559,6 +1567,5 @@ Symfony is the result of the work of many people who made the code better - Pierre Geyer (ptheg) - Sam Fleming (sam_fleming) - Thomas BERTRAND (sevrahk) - - Erik Saunier (snickers) - Matej Žilák (teo_sk) - Vladislav Vlastovskiy (vlastv) diff --git a/README.md b/README.md index b9129dc317679..ecd3b61781c8b 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Running Symfony Tests Information on how to run the Symfony test suite can be found in the [Running Symfony Tests][6] section. -[1]: https://symfony.com/get_started +[1]: https://symfony.com/doc/current/quick_tour/index.html [2]: https://symfony.com/doc/current/ [3]: https://symfony.com/doc/current/contributing/code/index.html [4]: https://symfony.com/doc/current/contributing/code/patches.html#check-list diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md index 81b9a2a92675b..fc81eae24fc3b 100644 --- a/UPGRADE-3.0.md +++ b/UPGRADE-3.0.md @@ -1,6 +1,35 @@ UPGRADE FROM 2.x to 3.0 ======================= +# Table of Contents + +- [ClassLoader](#classloader) +- [Config](#config) +- [Console](#console) +- [DependencyInjection](#dependencyinjection) +- [DoctrineBridge](#doctrinebridge) +- [DomCrawler](#domcrawler) +- [EventDispatcher](#eventdispatcher) +- [Form](#form) +- [FrameworkBundle](#frameworkbundle) +- [HttpFoundation](#httpfoundation) +- [HttpKernel](#httpkernel) +- [Locale](#locale) +- [Monolog Bridge](#monolog-bridge) +- [Process](#process) +- [PropertyAccess](#propertyaccess) +- [Routing](#routing) +- [Security](#security) +- [SecurityBundle](#securitybundle) +- [Serializer](#serializer) +- [Swiftmailer Bridge](#swiftmailer-bridge) +- [Translator](#translator) +- [Twig Bridge](#twig-bridge) +- [TwigBundle](#twigbundle) +- [Validator](#validator) +- [WebProfiler](#webprofiler) +- [Yaml](#yaml) + ### ClassLoader * The `UniversalClassLoader` class has been removed in favor of @@ -381,6 +410,58 @@ UPGRADE FROM 2.x to 3.0 $form = $this->createForm(MyType::class); ``` + * Passing custom data to forms now needs to be done + through the options resolver. + + In the controller: + + Before: + ```php + $form = $this->createForm(new MyType($variable), $entity, array( + 'action' => $this->generateUrl('action_route'), + 'method' => 'PUT', + )); + ``` + After: + ```php + $form = $this->createForm(MyType::class, $entity, array( + 'action' => $this->generateUrl('action_route'), + 'method' => 'PUT', + 'custom_value' => $variable, + )); + ``` + In the form type: + + Before: + ```php + class MyType extends AbstractType + { + private $value; + + public function __construct($variableValue) + { + $this->value = $value; + } + // ... + } + ``` + + After: + ```php + public function buildForm(FormBuilderInterface $builder, array $options) + { + $value = $options['custom_value']; + // ... + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults(array( + 'custom_value' => null, + )); + } + ``` + * The alias option of the `form.type_extension` tag was removed in favor of the `extended_type`/`extended-type` option. @@ -1805,6 +1886,8 @@ UPGRADE FROM 2.x to 3.0 ### HttpFoundation + * The precedence of parameters returned from `Request::get()` changed from "GET, PATH, BODY" to "PATH, GET, BODY" + * `Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface` no longer implements the `IteratorAggregate` interface. Use the `all()` method instead of iterating over the flash bag. * Removed the feature that allowed finding deep items in `ParameterBag::get()`. diff --git a/composer.json b/composer.json index 2c32fe2d087ac..fcbb0475f03fa 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "require": { "php": ">=5.5.9", "doctrine/common": "~2.4", - "twig/twig": "~1.26|~2.0", + "twig/twig": "~1.27|~2.0", "psr/cache": "~1.0", "psr/log": "~1.0", "symfony/polyfill-intl-icu": "~1.0", diff --git a/src/Symfony/Bridge/Twig/Command/LintCommand.php b/src/Symfony/Bridge/Twig/Command/LintCommand.php index 1f43afcdc44f0..abcfcccaee86a 100644 --- a/src/Symfony/Bridge/Twig/Command/LintCommand.php +++ b/src/Symfony/Bridge/Twig/Command/LintCommand.php @@ -142,7 +142,7 @@ private function validate(\Twig_Environment $twig, $template, $file) try { $temporaryLoader = new \Twig_Loader_Array(array((string) $file => $template)); $twig->setLoader($temporaryLoader); - $nodeTree = $twig->parse($twig->tokenize($template, (string) $file)); + $nodeTree = $twig->parse($twig->tokenize(new \Twig_Source($template, (string) $file))); $twig->compile($nodeTree); $twig->setLoader($realLoader); } catch (\Twig_Error $e) { diff --git a/src/Symfony/Bridge/Twig/Node/DumpNode.php b/src/Symfony/Bridge/Twig/Node/DumpNode.php index a7c152944618f..a66781e36550a 100644 --- a/src/Symfony/Bridge/Twig/Node/DumpNode.php +++ b/src/Symfony/Bridge/Twig/Node/DumpNode.php @@ -65,7 +65,7 @@ public function compile(\Twig_Compiler $compiler) ->write('\Symfony\Component\VarDumper\VarDumper::dump(array('."\n") ->indent(); foreach ($values as $node) { - $compiler->addIndentation(); + $compiler->write(''); if ($node->hasAttribute('name')) { $compiler ->string($node->getAttribute('name')) diff --git a/src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php b/src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php index 8c42bcd3c2650..9cb964865ea19 100644 --- a/src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php +++ b/src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php @@ -37,7 +37,7 @@ public function compile(\Twig_Compiler $compiler) // the variables in the third argument $label = $arguments[1]; $variables = isset($arguments[2]) ? $arguments[2] : null; - $lineno = $label->getLine(); + $lineno = $label->getTemplateLine(); if ($label instanceof \Twig_Node_Expression_Constant) { // If the label argument is given as a constant, we can either diff --git a/src/Symfony/Bridge/Twig/Node/TransNode.php b/src/Symfony/Bridge/Twig/Node/TransNode.php index 114b38c441123..7b2f9c090405f 100644 --- a/src/Symfony/Bridge/Twig/Node/TransNode.php +++ b/src/Symfony/Bridge/Twig/Node/TransNode.php @@ -109,18 +109,18 @@ protected function compileString(\Twig_Node $body, \Twig_Node_Expression_Array $ preg_match_all('/(?getLine()); + $key = new \Twig_Node_Expression_Constant('%'.$var.'%', $body->getTemplateLine()); if (!$vars->hasElement($key)) { if ('count' === $var && $this->hasNode('count')) { $vars->addElement($this->getNode('count'), $key); } else { - $varExpr = new \Twig_Node_Expression_Name($var, $body->getLine()); + $varExpr = new \Twig_Node_Expression_Name($var, $body->getTemplateLine()); $varExpr->setAttribute('ignore_strict_check', $ignoreStrictCheck); $vars->addElement($varExpr, $key); } } } - return array(new \Twig_Node_Expression_Constant(str_replace('%%', '%', trim($msg)), $body->getLine()), $vars); + return array(new \Twig_Node_Expression_Constant(str_replace('%%', '%', trim($msg)), $body->getTemplateLine()), $vars); } } diff --git a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php index 3e850350fd144..b226d91adfe48 100644 --- a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php +++ b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php @@ -50,10 +50,10 @@ protected function doEnterNode(\Twig_Node $node, \Twig_Environment $env) return $node; } else { $var = $env->getParser()->getVarName(); - $name = new \Twig_Node_Expression_AssignName($var, $node->getLine()); - $this->scope->set('domain', new \Twig_Node_Expression_Name($var, $node->getLine())); + $name = new \Twig_Node_Expression_AssignName($var, $node->getTemplateLine()); + $this->scope->set('domain', new \Twig_Node_Expression_Name($var, $node->getTemplateLine())); - return new \Twig_Node_Set(false, new \Twig_Node(array($name)), new \Twig_Node(array($node->getNode('expr'))), $node->getLine()); + return new \Twig_Node_Set(false, new \Twig_Node(array($name)), new \Twig_Node(array($node->getNode('expr'))), $node->getTemplateLine()); } } @@ -71,7 +71,7 @@ protected function doEnterNode(\Twig_Node $node, \Twig_Environment $env) } else { if (!$arguments->hasNode($ind)) { if (!$arguments->hasNode($ind - 1)) { - $arguments->setNode($ind - 1, new \Twig_Node_Expression_Array(array(), $node->getLine())); + $arguments->setNode($ind - 1, new \Twig_Node_Expression_Array(array(), $node->getTemplateLine())); } $arguments->setNode($ind, $this->scope->get('domain')); diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php index 1c1ac645c89f8..9733cd7b8ace6 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php @@ -23,7 +23,7 @@ public function testEscaping($template, $mustBeEscaped) $twig = new \Twig_Environment($this->getMock('Twig_LoaderInterface'), array('debug' => true, 'cache' => false, 'autoescape' => 'html', 'optimizations' => 0)); $twig->addExtension(new RoutingExtension($this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface'))); - $nodes = $twig->parse($twig->tokenize($template)); + $nodes = $twig->parse($twig->tokenize(new \Twig_Source($template, ''))); $this->assertSame($mustBeEscaped, $nodes->getNode('body')->getNode(0)->getNode('expr') instanceof \Twig_Node_Expression_Filter); } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php index a02edc777a1bf..0235c4da327a0 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php @@ -36,7 +36,7 @@ public function testTrans($template, $expected, array $variables = array()) $twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false)); $twig->addExtension(new TranslationExtension(new Translator('en', new MessageSelector()))); - echo $twig->compile($twig->parse($twig->tokenize($twig->getLoader()->getSource('index'), 'index')))."\n\n"; + echo $twig->compile($twig->parse($twig->tokenize(new \Twig_Source($twig->getLoader()->getSource('index'), 'index'))))."\n\n"; $this->assertEquals($expected, $this->getTemplate($template)->render($variables)); } diff --git a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TwigNodeProvider.php b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TwigNodeProvider.php index 0e401f62ea8b9..502cad38dec0b 100644 --- a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TwigNodeProvider.php +++ b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TwigNodeProvider.php @@ -25,7 +25,7 @@ public static function getModule($content) new \Twig_Node_Expression_Array(array(), 0), new \Twig_Node_Expression_Array(array(), 0), null, - null + new \Twig_Source('', '') ); } diff --git a/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php b/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php index 2986cd1258d63..6b6a92abf1434 100644 --- a/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php +++ b/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php @@ -23,7 +23,7 @@ public function testCompile($source, $expected) { $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); $env->addTokenParser(new FormThemeTokenParser()); - $stream = $env->tokenize($source); + $stream = $env->tokenize(new \Twig_Source($source, '')); $parser = new \Twig_Parser($env); $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)); diff --git a/src/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php b/src/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php index 7ea1a2c414647..fa61a2f1486c5 100644 --- a/src/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php +++ b/src/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php @@ -64,7 +64,7 @@ public function parse(\Twig_Token $token) $body = $this->parser->subparse(array($this, 'decideTransChoiceFork'), true); if (!$body instanceof \Twig_Node_Text && !$body instanceof \Twig_Node_Expression) { - throw new \Twig_Error_Syntax('A message inside a transchoice tag must be a simple text.', $body->getLine(), $stream->getFilename()); + throw new \Twig_Error_Syntax('A message inside a transchoice tag must be a simple text.', $body->getTemplateLine(), $stream->getSourceContext()->getName()); } $stream->expect(\Twig_Token::BLOCK_END_TYPE); diff --git a/src/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php b/src/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php index 06472d0b42174..4c8e7d3eeea38 100644 --- a/src/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php +++ b/src/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php @@ -55,7 +55,7 @@ public function parse(\Twig_Token $token) $stream->next(); $locale = $this->parser->getExpressionParser()->parseExpression(); } elseif (!$stream->test(\Twig_Token::BLOCK_END_TYPE)) { - throw new \Twig_Error_Syntax('Unexpected token. Twig was looking for the "with", "from", or "into" keyword.', $stream->getCurrent()->getLine(), $stream->getFilename()); + throw new \Twig_Error_Syntax('Unexpected token. Twig was looking for the "with", "from", or "into" keyword.', $stream->getCurrent()->getLine(), $stream->getSourceContext()->getName()); } } @@ -64,7 +64,7 @@ public function parse(\Twig_Token $token) $body = $this->parser->subparse(array($this, 'decideTransFork'), true); if (!$body instanceof \Twig_Node_Text && !$body instanceof \Twig_Node_Expression) { - throw new \Twig_Error_Syntax('A message inside a trans tag must be a simple text.', $body->getLine(), $stream->getFilename()); + throw new \Twig_Error_Syntax('A message inside a trans tag must be a simple text.', $body->getTemplateLine(), $stream->getSourceContext()->getName()); } $stream->expect(\Twig_Token::BLOCK_END_TYPE); diff --git a/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php b/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php index 2a9450c885837..917687ad34eee 100644 --- a/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php +++ b/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php @@ -62,9 +62,9 @@ public function extract($resource, MessageCatalogue $catalogue) $this->extractTemplate(file_get_contents($file->getPathname()), $catalogue); } catch (\Twig_Error $e) { if ($file instanceof SplFileInfo) { - $e->setTemplateFile($file->getRelativePathname()); + $e->setTemplateName($file->getRelativePathname()); } elseif ($file instanceof \SplFileInfo) { - $e->setTemplateFile($file->getRealPath()); + $e->setTemplateName($file->getRealPath()); } throw $e; @@ -85,7 +85,7 @@ protected function extractTemplate($template, MessageCatalogue $catalogue) $visitor = $this->twig->getExtension('Symfony\Bridge\Twig\Extension\TranslationExtension')->getTranslationNodeVisitor(); $visitor->enable(); - $this->twig->parse($this->twig->tokenize($template)); + $this->twig->parse($this->twig->tokenize(new \Twig_Source($template, ''))); foreach ($visitor->getMessages() as $message) { $catalogue->set(trim($message[0]), $this->prefix.trim($message[0]), $message[1] ?: $this->defaultDomain); diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 51939d0eed1ce..f61a045f57189 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": ">=5.5.9", - "twig/twig": "~1.26|~2.0" + "twig/twig": "~1.27|~2.0" }, "require-dev": { "symfony/asset": "~2.8|~3.0", diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php index eb8bbbadb2891..f926b4153bcd5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php @@ -163,22 +163,13 @@ protected function warmup($warmupDir, $realCacheDir, $enableOptionalWarmers = tr file_put_contents($file, $content); } - // fix references to kernel/container related classes - $fileSearch = $tempKernel->getName().ucfirst($tempKernel->getEnvironment()).'*'; - $search = array( - $tempKernel->getName().ucfirst($tempKernel->getEnvironment()), - sprintf('\'kernel.name\' => \'%s\'', $tempKernel->getName()), - sprintf('key="kernel.name">%s<', $tempKernel->getName()), - ); - $replace = array( - $realKernel->getName().ucfirst($realKernel->getEnvironment()), - sprintf('\'kernel.name\' => \'%s\'', $realKernel->getName()), - sprintf('key="kernel.name">%s<', $realKernel->getName()), - ); - foreach (Finder::create()->files()->name($fileSearch)->in($warmupDir) as $file) { - $content = str_replace($search, $replace, file_get_contents($file)); - file_put_contents(str_replace($search, $replace, $file), $content); - unlink($file); + // fix references to container's class + $tempContainerClass = get_class($tempKernel->getContainer()); + $realContainerClass = get_class($realKernel->getContainer()); + foreach (Finder::create()->files()->name($tempContainerClass.'*')->in($warmupDir) as $file) { + $content = str_replace($tempContainerClass, $realContainerClass, file_get_contents($file)); + file_put_contents($file, $content); + rename($file, str_replace(DIRECTORY_SEPARATOR.$tempContainerClass, DIRECTORY_SEPARATOR.$realContainerClass, $file)); } // remove temp kernel file after cache warmed up @@ -201,8 +192,8 @@ protected function getTempKernel(KernelInterface $parent, $namespace, $parentCla // the temp kernel class name must have the same length than the real one // to avoid the many problems in serialized resources files $class = substr($parentClass, 0, -1).'_'; - // the temp kernel name must be changed too - $name = var_export(substr($parent->getName(), 0, -1).'_', true); + // the temp container class must be changed too + $containerClass = var_export(substr(get_class($parent->getContainer()), 0, -1).'_', true); $code = <<getArgument('locale')); $io->comment('Parsing templates...'); + $prefix = $input->getOption('prefix'); $extractor = $this->getContainer()->get('translation.extractor'); - $extractor->setPrefix($input->getOption('prefix')); + $extractor->setPrefix(null === $prefix ? '' : $prefix); foreach ($transPaths as $path) { $path .= 'views'; if (is_dir($path)) { diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php index 800a32e2f30df..497ea08a4bf4e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php @@ -13,6 +13,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; class AddConstraintValidatorsPass implements CompilerPassInterface { diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 08152d9ec4b4b..2888ce4e5ba10 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -356,7 +356,7 @@ private function registerRouterConfiguration(array $config, ContainerBuilder $co $loader->load('routing.xml'); $container->setParameter('router.resource', $config['resource']); - $container->setParameter('router.cache_class_prefix', $container->getParameter('kernel.name').ucfirst($container->getParameter('kernel.environment'))); + $container->setParameter('router.cache_class_prefix', $container->getParameter('kernel.container_class')); $router = $container->findDefinition('router.default'); $argument = $router->getArgument(2); $argument['strict_requirements'] = $config['strict_requirements']; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php index 44e3b5d113020..ba969c64ae1f4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php @@ -81,6 +81,6 @@ public function testCacheIsFreshAfterCacheClearedWithWarmup() } } $this->assertTrue($found, 'Kernel file should present as resource'); - $this->assertRegExp(sprintf('/\'kernel.name\'\s*=>\s*\'%s\'/', $this->kernel->getName()), file_get_contents($containerFile), 'kernel.name is properly set on the dumped container'); + $this->assertRegExp(sprintf('/\'kernel.container_class\'\s*=>\s*\'%s\'/', get_class($this->kernel->getContainer())), file_get_contents($containerFile), 'kernel.container_class is properly set on the dumped container'); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php index d488405544d32..ab0bc985703ad 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php @@ -90,26 +90,6 @@ public function testProcessThrowAnExceptionIfTheServiceIsNotASubclassOfCommand() $container->compile(); } - - public function testHttpKernelRegisterCommandsIngoreCommandAsAService() - { - $container = new ContainerBuilder(); - $container->addCompilerPass(new AddConsoleCommandPass()); - $definition = new Definition('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\MyCommand'); - $definition->addTag('console.command'); - $container->setDefinition('my-command', $definition); - $container->compile(); - - $application = $this->getMock('Symfony\Component\Console\Application'); - // Never called, because it's the - // Symfony\Bundle\FrameworkBundle\Console\Application that register - // commands as a service - $application->expects($this->never())->method('add'); - - $bundle = new ExtensionPresentBundle(); - $bundle->setContainer($container); - $bundle->registerCommands($application); - } } class MyCommand extends Command diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 57c1381411939..6f3149f083957 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -11,7 +11,6 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection; -use Symfony\Bridge\PhpUnit\ErrorAssert; use Symfony\Bundle\FrameworkBundle\Tests\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension; use Symfony\Component\Cache\Adapter\ApcuAdapter; @@ -547,16 +546,17 @@ public function testSerializerCacheDisabled() /** * @group legacy - * @requires function Symfony\Bridge\PhpUnit\ErrorAssert::assertDeprecationsAreTriggered + * @expectedDeprecation The "framework.serializer.cache" option is deprecated %s. */ public function testDeprecatedSerializerCacheOption() { - ErrorAssert::assertDeprecationsAreTriggered('The "framework.serializer.cache" option is deprecated', function () { - $container = $this->createContainerFromFile('serializer_legacy_cache', array('kernel.debug' => true, 'kernel.container_class' => __CLASS__)); + $container = $this->createContainerFromFile('serializer_legacy_cache', array('kernel.debug' => true, 'kernel.container_class' => __CLASS__)); - $this->assertFalse($container->hasDefinition('serializer.mapping.cache_class_metadata_factory')); - $this->assertEquals(new Reference('foo'), $container->getDefinition('serializer.mapping.class_metadata_factory')->getArgument(1)); - }); + $this->assertFalse($container->hasDefinition('serializer.mapping.cache_class_metadata_factory')); + $this->assertTrue($container->hasDefinition('serializer.mapping.class_metadata_factory')); + + $cache = $container->getDefinition('serializer.mapping.class_metadata_factory')->getArgument(1); + $this->assertEquals(new Reference('foo'), $cache); } public function testAssetHelperWhenAssetsAreEnabled() @@ -635,6 +635,7 @@ protected function createContainer(array $data = array()) 'kernel.environment' => 'test', 'kernel.name' => 'kernel', 'kernel.root_dir' => __DIR__, + 'kernel.container_class' => 'testContainer', ), $data))); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ProfilerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ProfilerTest.php index a8741124fda7f..2d422b0292081 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ProfilerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ProfilerTest.php @@ -30,7 +30,7 @@ public function testProfilerIsDisabled($insulate) $client->enableProfiler(); $crawler = $client->request('GET', '/profiler'); $profile = $client->getProfile(); - $this->assertTrue(is_object($profile)); + $this->assertInternalType('object', $profile); $client->request('GET', '/profiler'); $this->assertFalse($client->getProfile()); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php index 253d070561d4b..9c19087264425 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php @@ -11,7 +11,6 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Templating; -use Symfony\Bridge\PhpUnit\ErrorAssert; use Symfony\Bundle\FrameworkBundle\Tests\TestCase; use Symfony\Bundle\FrameworkBundle\Templating\TemplateNameParser; use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; @@ -86,17 +85,15 @@ public function testParseValidNameWithNotFoundBundle() /** * @group legacy * @dataProvider provideAbsolutePaths - * @requires function Symfony\Bridge\PhpUnit\ErrorAssert::assertDeprecationsAreTriggered + * @expectedDeprecation Absolute template path support is deprecated since Symfony 3.1 and will be removed in 4.0. */ public function testAbsolutePathsAreDeprecated($name, $logicalName, $path, $ref) { - ErrorAssert::assertDeprecationsAreTriggered('Absolute template path support is deprecated since Symfony 3.1 and will be removed in 4.0.', function () use ($name, $logicalName, $path, $ref) { - $template = $this->parser->parse($name); + $template = $this->parser->parse($name); - $this->assertSame($ref->getLogicalName(), $template->getLogicalName()); - $this->assertSame($logicalName, $template->getLogicalName()); - $this->assertSame($path, $template->getPath()); - }); + $this->assertSame($ref->getLogicalName(), $template->getLogicalName()); + $this->assertSame($logicalName, $template->getLogicalName()); + $this->assertSame($path, $template->getPath()); } public function provideAbsolutePaths() diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 64a1ad32e650c..ca986735e2dcd 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -39,7 +39,7 @@ "symfony/yaml": "~2.8|~3.0", "symfony/expression-language": "~2.8|~3.0", "doctrine/doctrine-bundle": "~1.4", - "twig/twig": "~1.26|~2.0" + "twig/twig": "~1.27|~2.0" }, "suggest": { "symfony/security-acl": "For using the ACL functionality of this bundle" diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php index 380dfe0ebd65d..c0661d9e70b67 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php @@ -120,7 +120,7 @@ private function addTwigOptions(ArrayNodeDefinition $rootNode) $rootNode ->fixXmlConfig('path') ->children() - ->variableNode('autoescape')->defaultValue('filename')->end() + ->variableNode('autoescape')->defaultValue('name')->end() ->scalarNode('autoescape_service')->defaultNull()->end() ->scalarNode('autoescape_service_method')->defaultNull()->end() ->scalarNode('base_template_class')->example('Twig_Template')->cannotBeEmpty()->end() diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php index a99125a21c149..5abe06d9bb29d 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php @@ -111,7 +111,7 @@ public function testLoadDefaultTemplateEscapingGuesserConfiguration($format) $this->compileContainer($container); $options = $container->getDefinition('twig')->getArgument(1); - $this->assertEquals('filename', $options['autoescape']); + $this->assertEquals('name', $options['autoescape']); } public function testGlobalsWithDifferentTypesAndValues() diff --git a/src/Symfony/Bundle/TwigBundle/Tests/Functional/CacheWarmingTest.php b/src/Symfony/Bundle/TwigBundle/Tests/Functional/CacheWarmingTest.php index 56fd7f4245949..26ae8306b9927 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/Functional/CacheWarmingTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/Functional/CacheWarmingTest.php @@ -28,7 +28,7 @@ public function testCacheIsProperlyWarmedWhenTemplatingIsAvailable() $warmer->enableOptionalWarmers(); $warmer->warmUp($kernel->getCacheDir()); - $this->assertTrue(file_exists($kernel->getCacheDir().'/twig')); + $this->assertFileExists($kernel->getCacheDir().'/twig'); } public function testCacheIsProperlyWarmedWhenTemplatingIsDisabled() @@ -40,7 +40,7 @@ public function testCacheIsProperlyWarmedWhenTemplatingIsDisabled() $warmer->enableOptionalWarmers(); $warmer->warmUp($kernel->getCacheDir()); - $this->assertTrue(file_exists($kernel->getCacheDir().'/twig')); + $this->assertFileExists($kernel->getCacheDir().'/twig'); } protected function setUp() diff --git a/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php b/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php index 269e029a0858b..9804c08a1923b 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php @@ -17,7 +17,7 @@ class FilesystemLoaderTest extends TestCase { - public function testGetSource() + public function testGetSourceContext() { $parser = $this->getMock('Symfony\Component\Templating\TemplateNameParserInterface'); $locator = $this->getMock('Symfony\Component\Config\FileLocatorInterface'); @@ -30,10 +30,10 @@ public function testGetSource() $loader->addPath(__DIR__.'/../DependencyInjection/Fixtures/Resources/views', 'namespace'); // Twig-style - $this->assertEquals("This is a layout\n", $loader->getSource('@namespace/layout.html.twig')); + $this->assertEquals("This is a layout\n", $loader->getSourceContext('@namespace/layout.html.twig')->getCode()); // Symfony-style - $this->assertEquals("This is a layout\n", $loader->getSource('TwigBundle::layout.html.twig')); + $this->assertEquals("This is a layout\n", $loader->getSourceContext('TwigBundle::layout.html.twig')->getCode()); } public function testExists() diff --git a/src/Symfony/Bundle/TwigBundle/TwigEngine.php b/src/Symfony/Bundle/TwigBundle/TwigEngine.php index 1e0d6139aecbf..5fa22cfcbae06 100644 --- a/src/Symfony/Bundle/TwigBundle/TwigEngine.php +++ b/src/Symfony/Bundle/TwigBundle/TwigEngine.php @@ -51,8 +51,8 @@ public function render($name, array $parameters = array()) } catch (\Twig_Error $e) { if ($name instanceof TemplateReference) { try { - // try to get the real file name of the template where the error occurred - $e->setTemplateFile(sprintf('%s', $this->locator->locate($this->parser->parse($e->getTemplateFile())))); + // try to get the real name of the template where the error occurred + $e->setTemplateName(sprintf('%s', $this->locator->locate($this->parser->parse($e->getTemplateName())))); } catch (\Exception $e2) { } } diff --git a/src/Symfony/Bundle/TwigBundle/composer.json b/src/Symfony/Bundle/TwigBundle/composer.json index 2e2b74a06f4e2..89b16de7f81eb 100644 --- a/src/Symfony/Bundle/TwigBundle/composer.json +++ b/src/Symfony/Bundle/TwigBundle/composer.json @@ -21,7 +21,7 @@ "symfony/twig-bridge": "~2.8|~3.0", "symfony/http-foundation": "~2.8|~3.0", "symfony/http-kernel": "~2.8|~3.0", - "twig/twig": "~1.26|~2.0" + "twig/twig": "~1.27|~2.0" }, "require-dev": { "symfony/stopwatch": "~2.8|~3.0", diff --git a/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php b/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php index 44a3ba79d744d..b0181afe87c12 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php @@ -127,7 +127,11 @@ protected function templateExists($template) } try { - $loader->getSource($template); + if ($loader instanceof \Twig_SourceContextLoaderInterface) { + $loader->getSourceContext($template); + } else { + $loader->getSource($template); + } return true; } catch (\Twig_Error_Loader $e) { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig index eeab7f909dd8d..ddf147ca2e2df 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig @@ -133,6 +133,7 @@ button { outline: none; border: 0; padding: 0; + cursor: pointer; } .btn-link:hover { text-decoration: underline; diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/Profiler/TemplateManagerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/Profiler/TemplateManagerTest.php index 29238a21c439b..c9b199ea18b93 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/Profiler/TemplateManagerTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/Profiler/TemplateManagerTest.php @@ -31,11 +31,6 @@ class TemplateManagerTest extends TestCase */ protected $profiler; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $profile; - /** * @var \Symfony\Bundle\WebProfilerBundle\Profiler\TemplateManager */ @@ -129,11 +124,7 @@ public function profileHasCollectorCallback($panel) protected function mockProfile() { - $this->profile = $this->getMockBuilder('Symfony\Component\HttpKernel\Profiler\Profile') - ->disableOriginalConstructor() - ->getMock(); - - return $this->profile; + return $this->getMockBuilder('Symfony\Component\HttpKernel\Profiler\Profile')->disableOriginalConstructor()->getMock(); } protected function mockTwigEnvironment() @@ -144,9 +135,12 @@ protected function mockTwigEnvironment() ->method('loadTemplate') ->will($this->returnValue('loadedTemplate')); - $this->twigEnvironment->expects($this->any()) - ->method('getLoader') - ->will($this->returnValue($this->getMock('\Twig_LoaderInterface'))); + if (interface_exists('\Twig_SourceContextLoaderInterface')) { + $loader = $this->getMock('\Twig_SourceContextLoaderInterface'); + } else { + $loader = $this->getMock('\Twig_LoaderInterface'); + } + $this->twigEnvironment->expects($this->any())->method('getLoader')->will($this->returnValue($loader)); return $this->twigEnvironment; } diff --git a/src/Symfony/Component/Console/Helper/SymfonyQuestionHelper.php b/src/Symfony/Component/Console/Helper/SymfonyQuestionHelper.php index aeddb760e077d..88351d1641d7d 100644 --- a/src/Symfony/Component/Console/Helper/SymfonyQuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/SymfonyQuestionHelper.php @@ -18,6 +18,7 @@ use Symfony\Component\Console\Question\ConfirmationQuestion; use Symfony\Component\Console\Question\Question; use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Console\Formatter\OutputFormatter; /** * Symfony Style Guide compliant question helper. @@ -35,11 +36,11 @@ public function ask(InputInterface $input, OutputInterface $output, Question $qu $question->setValidator(function ($value) use ($validator) { if (null !== $validator) { $value = $validator($value); - } - - // make required - if (!is_array($value) && !is_bool($value) && 0 === strlen($value)) { - throw new LogicException('A value is required.'); + } else { + // make required + if (!is_array($value) && !is_bool($value) && 0 === strlen($value)) { + throw new LogicException('A value is required.'); + } } return $value; @@ -53,7 +54,7 @@ public function ask(InputInterface $input, OutputInterface $output, Question $qu */ protected function writePrompt(OutputInterface $output, Question $question) { - $text = $question->getQuestion(); + $text = OutputFormatter::escape($question->getQuestion()); $default = $question->getDefault(); switch (true) { @@ -75,18 +76,18 @@ protected function writePrompt(OutputInterface $output, Question $question) $default[$key] = $choices[trim($value)]; } - $text = sprintf(' %s [%s]:', $text, implode(', ', $default)); + $text = sprintf(' %s [%s]:', $text, OutputFormatter::escape(implode(', ', $default))); break; case $question instanceof ChoiceQuestion: $choices = $question->getChoices(); - $text = sprintf(' %s [%s]:', $text, $choices[$default]); + $text = sprintf(' %s [%s]:', $text, OutputFormatter::escape($choices[$default])); break; default: - $text = sprintf(' %s [%s]:', $text, $default); + $text = sprintf(' %s [%s]:', $text, OutputFormatter::escape($default)); } $output->writeln($text); diff --git a/src/Symfony/Component/Console/Input/InputDefinition.php b/src/Symfony/Component/Console/Input/InputDefinition.php index e9944db984be6..85b778b228627 100644 --- a/src/Symfony/Component/Console/Input/InputDefinition.php +++ b/src/Symfony/Component/Console/Input/InputDefinition.php @@ -281,6 +281,9 @@ public function getOption($name) /** * Returns true if an InputOption object exists by name. * + * This method can't be used to check if the user included the option when + * executing the command (use getOption() instead). + * * @param string $name The InputOption name * * @return bool true if the InputOption object exists, false otherwise diff --git a/src/Symfony/Component/Console/Tests/Helper/SymfonyQuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/SymfonyQuestionHelperTest.php index 032e153f068f1..91f51245acd7e 100644 --- a/src/Symfony/Component/Console/Tests/Helper/SymfonyQuestionHelperTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/SymfonyQuestionHelperTest.php @@ -6,6 +6,7 @@ use Symfony\Component\Console\Helper\HelperSet; use Symfony\Component\Console\Helper\SymfonyQuestionHelper; use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Question\Question; use Symfony\Component\Console\Question\ChoiceQuestion; /** @@ -73,6 +74,33 @@ public function testAskChoice() $this->assertOutputContains('What is your favorite superhero? [Superman, Batman]', $output); } + public function testAskReturnsNullIfValidatorAllowsIt() + { + $questionHelper = new SymfonyQuestionHelper(); + $questionHelper->setInputStream($this->getInputStream("\n")); + $question = new Question('What is your favorite superhero?'); + $question->setValidator(function ($value) { return $value; }); + $this->assertNull($questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAskEscapeDefaultValue() + { + $helper = new SymfonyQuestionHelper(); + $helper->setInputStream($this->getInputStream('\\')); + $helper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), new Question('Can I have a backslash?', '\\')); + + $this->assertOutputContains('Can I have a backslash? [\]', $output); + } + + public function testAskEscapeLabel() + { + $helper = new SymfonyQuestionHelper(); + $helper->setInputStream($this->getInputStream('sure')); + $helper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), new Question('Do you want a \?')); + + $this->assertOutputContains('Do you want a \?', $output); + } + protected function getInputStream($input) { $stream = fopen('php://memory', 'r+', false); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php index 24008ca75dfc9..8edb717b4cc40 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php @@ -51,10 +51,14 @@ public function process(ContainerBuilder $container) $public = $alias->isPublic(); $container->setAlias($renamedId, new Alias((string) $alias, false)); } else { - $definition = $container->getDefinition($inner); - $public = $definition->isPublic(); - $definition->setPublic(false); - $container->setDefinition($renamedId, $definition); + $decoratedDefinition = $container->getDefinition($inner); + $definition->setTags(array_merge($decoratedDefinition->getTags(), $definition->getTags())); + $definition->setAutowiringTypes(array_merge($decoratedDefinition->getAutowiringTypes(), $definition->getAutowiringTypes())); + $public = $decoratedDefinition->isPublic(); + $decoratedDefinition->setPublic(false); + $decoratedDefinition->setTags(array()); + $decoratedDefinition->setAutowiringTypes(array()); + $container->setDefinition($renamedId, $decoratedDefinition); } $container->setAlias($inner, new Alias($id, $public)); diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index 608e5825658d4..c80265d73f3cd 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -664,7 +664,7 @@ public function isAutowired() /** * Sets autowired. * - * @param $autowired + * @param bool $autowired * * @return Definition The current instance */ diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 6b1381b01201d..32b62820190b1 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -391,7 +391,7 @@ private function getArgumentsAsPhp(\DOMElement $node, $name, $lowercase = true) $arguments[$key] = $arg->nodeValue; break; case 'constant': - $arguments[$key] = constant($arg->nodeValue); + $arguments[$key] = constant(trim($arg->nodeValue)); break; default: $arguments[$key] = XmlUtils::phpize($arg->nodeValue); @@ -505,13 +505,13 @@ private function validateAlias(\DOMElement $alias, $file) { foreach ($alias->attributes as $name => $node) { if (!in_array($name, array('alias', 'id', 'public'))) { - @trigger_error(sprintf('Using the attribute "%s" is deprecated for alias definition "%s" in "%s". Allowed attributes are "alias", "id" and "public". The XmlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported attributes.', $name, $alias->getAttribute('id'), $file), E_USER_DEPRECATED); + @trigger_error(sprintf('Using the attribute "%s" is deprecated for the service "%s" which is defined as an alias in "%s". Allowed attributes for service aliases are "alias", "id" and "public". The XmlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported attributes.', $name, $alias->getAttribute('id'), $file), E_USER_DEPRECATED); } } foreach ($alias->childNodes as $child) { if ($child instanceof \DOMElement && $child->namespaceURI === self::NS) { - @trigger_error(sprintf('Using the element "%s" is deprecated for alias definition "%s" in "%s". The XmlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported elements.', $child->localName, $alias->getAttribute('id'), $file), E_USER_DEPRECATED); + @trigger_error(sprintf('Using the element "%s" is deprecated for the service "%s" which is defined as an alias in "%s". The XmlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported elements.', $child->localName, $alias->getAttribute('id'), $file), E_USER_DEPRECATED); } } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 82bbf901828d4..569c8f4e028b9 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -180,7 +180,7 @@ private function parseDefinition($id, $service, $file) foreach ($service as $key => $value) { if (!in_array($key, array('alias', 'public'))) { - @trigger_error(sprintf('The configuration key "%s" is unsupported for alias definition "%s" in "%s". Allowed configuration keys are "alias" and "public". The YamlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported attributes.', $key, $id, $file), E_USER_DEPRECATED); + @trigger_error(sprintf('The configuration key "%s" is unsupported for the service "%s" which is defined as an alias in "%s". Allowed configuration keys for service aliases are "alias" and "public". The YamlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported attributes.', $key, $id, $file), E_USER_DEPRECATED); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php index 2ffa767805c72..fbdd3af372149 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php @@ -124,6 +124,46 @@ public function testProcessWithPriority() $this->assertNull($quxDefinition->getDecoratedService()); } + public function testProcessMovesTagsFromDecoratedDefinitionToDecoratingDefinition() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setTags(array('bar' => array('attr' => 'baz'))) + ; + $container + ->register('baz') + ->setTags(array('foobar' => array('attr' => 'bar'))) + ->setDecoratedService('foo') + ; + + $this->process($container); + + $this->assertEmpty($container->getDefinition('baz.inner')->getTags()); + $this->assertEquals(array('bar' => array('attr' => 'baz'), 'foobar' => array('attr' => 'bar')), $container->getDefinition('baz')->getTags()); + } + + public function testProcessMergesAutowiringTypesInDecoratingDefinitionAndRemoveThemFromDecoratedDefinition() + { + $container = new ContainerBuilder(); + + $container + ->register('parent') + ->addAutowiringType('Bar') + ; + + $container + ->register('child') + ->setDecoratedService('parent') + ->addAutowiringType('Foo') + ; + + $this->process($container); + + $this->assertEquals(array('Bar', 'Foo'), $container->getDefinition('child')->getAutowiringTypes()); + $this->assertEmpty($container->getDefinition('child.inner')->getAutowiringTypes()); + } + protected function process(ContainerBuilder $container) { $repeatedPass = new DecoratorServicePass(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php index d31dcfcd7f468..3ca0df4a77342 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php @@ -337,8 +337,11 @@ public function testProcessMergeAutowiringTypes() $this->process($container); - $def = $container->getDefinition('child'); - $this->assertEquals(array('Foo', 'Bar'), $def->getAutowiringTypes()); + $childDef = $container->getDefinition('child'); + $this->assertEquals(array('Foo', 'Bar'), $childDef->getAutowiringTypes()); + + $parentDef = $container->getDefinition('parent'); + $this->assertSame(array('Foo'), $parentDef->getAutowiringTypes()); } public function testProcessResolvesAliases() diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 71918229600a0..dab10132aec8c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -14,7 +14,6 @@ require_once __DIR__.'/Fixtures/includes/classes.php'; require_once __DIR__.'/Fixtures/includes/ProjectExtension.php'; -use Symfony\Bridge\PhpUnit\ErrorAssert; use Symfony\Component\Config\Resource\ResourceInterface; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -59,18 +58,18 @@ public function testDefinitions() } /** - * @requires function Symfony\Bridge\PhpUnit\ErrorAssert::assertDeprecationsAreTriggered + * @group legacy + * @expectedDeprecation The "deprecated_foo" service is deprecated. You should stop using it, as it will soon be removed. */ public function testCreateDeprecatedService() { - ErrorAssert::assertDeprecationsAreTriggered('The "deprecated_foo" service is deprecated. You should stop using it, as it will soon be removed.', function () { - $definition = new Definition('stdClass'); - $definition->setDeprecated(true); + $definition = new Definition('stdClass'); + $definition->setDeprecated(true); - $builder = new ContainerBuilder(); - $builder->setDefinition('deprecated_foo', $definition); - $builder->get('deprecated_foo'); - }); + $builder = new ContainerBuilder(); + $builder->setDefinition('deprecated_foo', $definition); + $builder->compile(); + $builder->get('deprecated_foo'); } public function testRegister() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index a351c62ebc881..9cf528d5ca9c9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -11,7 +11,6 @@ namespace Symfony\Component\DependencyInjection\Tests\Loader; -use Symfony\Bridge\PhpUnit\ErrorAssert; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; @@ -558,23 +557,17 @@ public function testAutowire() /** * @group legacy - * @requires function Symfony\Bridge\PhpUnit\ErrorAssert::assertDeprecationsAreTriggered + * @expectedDeprecation Using the attribute "class" is deprecated for the service "bar" which is defined as an alias %s. + * @expectedDeprecation Using the element "tag" is deprecated for the service "bar" which is defined as an alias %s. + * @expectedDeprecation Using the element "factory" is deprecated for the service "bar" which is defined as an alias %s. */ public function testAliasDefinitionContainsUnsupportedElements() { - $deprecations = array( - 'Using the attribute "class" is deprecated for alias definition "bar"', - 'Using the element "tag" is deprecated for alias definition "bar"', - 'Using the element "factory" is deprecated for alias definition "bar"', - ); - - ErrorAssert::assertDeprecationsAreTriggered($deprecations, function () { - $container = new ContainerBuilder(); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('legacy_invalid_alias_definition.xml'); + $loader->load('legacy_invalid_alias_definition.xml'); - $this->assertTrue($container->has('bar')); - }); + $this->assertTrue($container->has('bar')); } } diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php index 316d0083c65a0..e404964fc661d 100644 --- a/src/Symfony/Component/DomCrawler/Crawler.php +++ b/src/Symfony/Component/DomCrawler/Crawler.php @@ -940,29 +940,54 @@ private function relativize($xpath) { $expressions = array(); - $unionPattern = '/\|(?![^\[]*\])/'; // An expression which will never match to replace expressions which cannot match in the crawler // We cannot simply drop $nonMatchingExpression = 'a[name() = "b"]'; - // Split any unions into individual expressions. - foreach (preg_split($unionPattern, $xpath) as $expression) { - $expression = trim($expression); - $parenthesis = ''; + $xpathLen = strlen($xpath); + $openedBrackets = 0; + $startPosition = strspn($xpath, " \t\n\r\0\x0B"); + + for ($i = $startPosition; $i <= $xpathLen; ++$i) { + $i += strcspn($xpath, '"\'[]|', $i); + + if ($i < $xpathLen) { + switch ($xpath[$i]) { + case '"': + case "'": + if (false === $i = strpos($xpath, $xpath[$i], $i + 1)) { + return $xpath; // The XPath expression is invalid + } + continue 2; + case '[': + ++$openedBrackets; + continue 2; + case ']': + --$openedBrackets; + continue 2; + } + } + if ($openedBrackets) { + continue; + } - // If the union is inside some braces, we need to preserve the opening braces and apply - // the change only inside it. - if (preg_match('/^[\(\s*]+/', $expression, $matches)) { - $parenthesis = $matches[0]; - $expression = substr($expression, strlen($parenthesis)); + if ($startPosition < $xpathLen && '(' === $xpath[$startPosition]) { + // If the union is inside some braces, we need to preserve the opening braces and apply + // the change only inside it. + $j = 1 + strspn($xpath, "( \t\n\r\0\x0B", $startPosition + 1); + $parenthesis = substr($xpath, $startPosition, $j); + $startPosition += $j; + } else { + $parenthesis = ''; } + $expression = rtrim(substr($xpath, $startPosition, $i - $startPosition)); if (0 === strpos($expression, 'self::*/')) { $expression = './'.substr($expression, 8); } // add prefix before absolute element selector - if (empty($expression)) { + if ('' === $expression) { $expression = $nonMatchingExpression; } elseif (0 === strpos($expression, '//')) { $expression = 'descendant-or-self::'.substr($expression, 2); @@ -975,7 +1000,7 @@ private function relativize($xpath) } elseif ('/' === $expression[0] || '.' === $expression[0] || 0 === strpos($expression, 'self::')) { $expression = $nonMatchingExpression; } elseif (0 === strpos($expression, 'descendant::')) { - $expression = 'descendant-or-self::'.substr($expression, strlen('descendant::')); + $expression = 'descendant-or-self::'.substr($expression, 12); } elseif (preg_match('/^(ancestor|ancestor-or-self|attribute|following|following-sibling|namespace|parent|preceding|preceding-sibling)::/', $expression)) { // the fake root has no parent, preceding or following nodes and also no attributes (even no namespace attributes) $expression = $nonMatchingExpression; @@ -983,9 +1008,16 @@ private function relativize($xpath) $expression = 'self::'.$expression; } $expressions[] = $parenthesis.$expression; + + if ($i === $xpathLen) { + return implode(' | ', $expressions); + } + + $i += strspn($xpath, " \t\n\r\0\x0B", $i + 1); + $startPosition = $i + 1; } - return implode(' | ', $expressions); + return $xpath; // The XPath expression is invalid } /** diff --git a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php index b54563b7c64fa..d4bb0f6686fc2 100755 --- a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php @@ -430,6 +430,7 @@ public function testFilterXpathComplexQueries() $this->assertCount(5, $crawler->filterXPath('(//a | //div)//img')); $this->assertCount(7, $crawler->filterXPath('((//a | //div)//img | //ul)')); $this->assertCount(7, $crawler->filterXPath('( ( //a | //div )//img | //ul )')); + $this->assertCount(1, $crawler->filterXPath("//a[./@href][((./@id = 'Klausi|Claudiu' or normalize-space(string(.)) = 'Klausi|Claudiu' or ./@title = 'Klausi|Claudiu' or ./@rel = 'Klausi|Claudiu') or .//img[./@alt = 'Klausi|Claudiu'])]")); } public function testFilterXPath() @@ -596,7 +597,7 @@ public function testFilterXPathWithSelfAxes() $this->assertCount(0, $crawler->filterXPath('self::a'), 'The fake root node has no "real" element name'); $this->assertCount(0, $crawler->filterXPath('self::a/img'), 'The fake root node has no "real" element name'); - $this->assertCount(9, $crawler->filterXPath('self::*/a')); + $this->assertCount(10, $crawler->filterXPath('self::*/a')); } public function testFilter() @@ -1079,6 +1080,8 @@ public function createTestCrawler($uri = null) GetLink + Klausi|Claudiu +
diff --git a/src/Symfony/Component/EventDispatcher/EventDispatcher.php b/src/Symfony/Component/EventDispatcher/EventDispatcher.php index 8cd5692c43b1b..2c977f4731291 100644 --- a/src/Symfony/Component/EventDispatcher/EventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/EventDispatcher.php @@ -82,7 +82,7 @@ public function getListenerPriority($eventName, $listener) } foreach ($this->listeners[$eventName] as $priority => $listeners) { - if (false !== ($key = array_search($listener, $listeners, true))) { + if (false !== in_array($listener, $listeners, true)) { return $priority; } } diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index c934d83bf66d5..34af820432efa 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -369,10 +369,14 @@ public function makePathRelative($endPath, $startPath) } // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels) - $depth = count($startPathArr) - $index; + if (count($startPathArr) === 1 && $startPathArr[0] === '') { + $depth = 0; + } else { + $depth = count($startPathArr) - $index; + } // When we need to traverse from the start, and we are starting from a root path, don't add '../' - if ('/' === $startPath[0] && 0 === $index && 1 === $depth) { + if ('/' === $startPath[0] && 0 === $index && 0 === $depth) { $traverser = ''; } else { // Repeated "../" for each level need to reach the common path diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index 58523c22154cc..db0fe5aa2ac05 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -840,6 +840,8 @@ public function providePathsForMakePathRelative() array('/a/aab/bb/', '/a/aa/', '../aab/bb/'), array('/a/aab/bb/', '/', 'a/aab/bb/'), array('/a/aab/bb/', '/b/aab', '../../a/aab/bb/'), + array('/aab/bb', '/aa', '../aab/bb/'), + array('/aab', '/aa', '../aab/'), ); if ('\\' === DIRECTORY_SEPARATOR) { diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/BaseValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/BaseValidatorExtensionTest.php index 6a15bbfa9e6b6..01f20c4127cf1 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/BaseValidatorExtensionTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/BaseValidatorExtensionTest.php @@ -58,7 +58,7 @@ public function testValidationGroupsCanBeSetToCallback() 'validation_groups' => array($this, 'testValidationGroupsCanBeSetToCallback'), )); - $this->assertTrue(is_callable($form->getConfig()->getOption('validation_groups'))); + $this->assertInternalType('callable', $form->getConfig()->getOption('validation_groups')); } public function testValidationGroupsCanBeSetToClosure() @@ -67,7 +67,7 @@ public function testValidationGroupsCanBeSetToClosure() 'validation_groups' => function (FormInterface $form) { }, )); - $this->assertTrue(is_callable($form->getConfig()->getOption('validation_groups'))); + $this->assertInternalType('callable', $form->getConfig()->getOption('validation_groups')); } abstract protected function createForm(array $options = array()); diff --git a/src/Symfony/Component/HttpFoundation/JsonResponse.php b/src/Symfony/Component/HttpFoundation/JsonResponse.php index c183372e2d9c3..fd0de6cf013c8 100644 --- a/src/Symfony/Component/HttpFoundation/JsonResponse.php +++ b/src/Symfony/Component/HttpFoundation/JsonResponse.php @@ -81,11 +81,19 @@ public static function create($data = null, $status = 200, $headers = array()) public function setCallback($callback = null) { if (null !== $callback) { - // taken from http://www.geekality.net/2011/08/03/valid-javascript-identifier/ - $pattern = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*+$/u'; + // partially token from http://www.geekality.net/2011/08/03/valid-javascript-identifier/ + // partially token from https://github.com/willdurand/JsonpCallbackValidator + // JsonpCallbackValidator is released under the MIT License. See https://github.com/willdurand/JsonpCallbackValidator/blob/v1.1.0/LICENSE for details. + // (c) William Durand + $pattern = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*(?:\[(?:"(?:\\\.|[^"\\\])*"|\'(?:\\\.|[^\'\\\])*\'|\d+)\])*?$/u'; + $reserved = array( + 'break', 'do', 'instanceof', 'typeof', 'case', 'else', 'new', 'var', 'catch', 'finally', 'return', 'void', 'continue', 'for', 'switch', 'while', + 'debugger', 'function', 'this', 'with', 'default', 'if', 'throw', 'delete', 'in', 'try', 'class', 'enum', 'extends', 'super', 'const', 'export', + 'import', 'implements', 'let', 'private', 'public', 'yield', 'interface', 'package', 'protected', 'static', 'null', 'true', 'false', + ); $parts = explode('.', $callback); foreach ($parts as $part) { - if (!preg_match($pattern, $part)) { + if (!preg_match($pattern, $part) || in_array($part, $reserved, true)) { throw new \InvalidArgumentException('The callback name is not valid.'); } } diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index f22ccf3d12d06..85131e7d5544f 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1482,6 +1482,16 @@ public function isMethodSafe() return in_array($this->getMethod(), array('GET', 'HEAD', 'OPTIONS', 'TRACE')); } + /** + * Checks whether the method is cacheable or not. + * + * @return bool + */ + public function isMethodCacheable() + { + return in_array($this->getMethod(), array('GET', 'HEAD')); + } + /** * Returns the request body content. * diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index d4584ed17c494..723118f046036 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -1008,7 +1008,7 @@ public function setVary($headers, $replace = true) */ public function isNotModified(Request $request) { - if (!$request->isMethodSafe()) { + if (!$request->isMethodCacheable()) { return false; } diff --git a/src/Symfony/Component/HttpFoundation/StreamedResponse.php b/src/Symfony/Component/HttpFoundation/StreamedResponse.php index b021d3c405a28..b3feb031f4c78 100644 --- a/src/Symfony/Component/HttpFoundation/StreamedResponse.php +++ b/src/Symfony/Component/HttpFoundation/StreamedResponse.php @@ -28,6 +28,7 @@ class StreamedResponse extends Response { protected $callback; protected $streamed; + private $headersSent; /** * Constructor. @@ -44,6 +45,7 @@ public function __construct(callable $callback = null, $status = 200, $headers = $this->setCallback($callback); } $this->streamed = false; + $this->headersSent = false; } /** @@ -70,6 +72,22 @@ public function setCallback(callable $callback) $this->callback = $callback; } + /** + * {@inheritdoc} + * + * This method only sends the headers once. + */ + public function sendHeaders() + { + if ($this->headersSent) { + return; + } + + $this->headersSent = true; + + parent::sendHeaders(); + } + /** * {@inheritdoc} * diff --git a/src/Symfony/Component/HttpFoundation/Tests/JsonResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/JsonResponseTest.php index 1dd2e60c062dd..15f19eca6636f 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/JsonResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/JsonResponseTest.php @@ -225,6 +225,14 @@ public function testSetContentJsonSerializeError() JsonResponse::create($serializable); } + + public function testSetComplexCallback() + { + $response = JsonResponse::create(array('foo' => 'bar')); + $response->setCallback('ಠ_ಠ["foo"].bar[0]'); + + $this->assertEquals('/**/ಠ_ಠ["foo"].bar[0]({"foo":"bar"});', $response->getContent()); + } } if (interface_exists('JsonSerializable')) { diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index c437e71e6da6b..4c39b66311bfa 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -1987,6 +1987,32 @@ public function methodSafeProvider() array('CONNECT', false), ); } + + /** + * @dataProvider methodCacheableProvider + */ + public function testMethodCacheable($method, $chacheable) + { + $request = new Request(); + $request->setMethod($method); + $this->assertEquals($chacheable, $request->isMethodCacheable()); + } + + public function methodCacheableProvider() + { + return array( + array('HEAD', true), + array('GET', true), + array('POST', false), + array('PUT', false), + array('PATCH', false), + array('DELETE', false), + array('PURGE', false), + array('OPTIONS', false), + array('TRACE', false), + array('CONNECT', false), + ); + } } class RequestContentProxy extends Request diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DefaultValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DefaultValueResolver.php index 0962dab885fe9..1af03651c3799 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DefaultValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DefaultValueResolver.php @@ -27,7 +27,7 @@ final class DefaultValueResolver implements ArgumentValueResolverInterface */ public function supports(Request $request, ArgumentMetadata $argument) { - return $argument->hasDefaultValue() || $argument->isNullable(); + return $argument->hasDefaultValue() || ($argument->isNullable() && !$argument->isVariadic()); } /** diff --git a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadata.php b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadata.php index 72c294f3a3d7c..32316a8d519e7 100644 --- a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadata.php +++ b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadata.php @@ -40,7 +40,7 @@ public function __construct($name, $type, $isVariadic, $hasDefaultValue, $defaul $this->isVariadic = $isVariadic; $this->hasDefaultValue = $hasDefaultValue; $this->defaultValue = $defaultValue; - $this->isNullable = (bool) $isNullable; + $this->isNullable = $isNullable || null === $type || ($hasDefaultValue && null === $defaultValue); } /** @@ -88,7 +88,7 @@ public function hasDefaultValue() } /** - * Returns whether the argument is nullable in PHP 7.1 or higher. + * Returns whether the argument accepts null values. * * @return bool */ diff --git a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php index efe5966894495..d1e7af206804b 100644 --- a/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php +++ b/src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php @@ -58,7 +58,7 @@ public function createArgumentMetadata($controller) } foreach ($reflection->getParameters() as $param) { - $arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param), $this->isVariadic($param), $this->hasDefaultValue($param), $this->getDefaultValue($param), $this->isNullable($param)); + $arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param), $this->isVariadic($param), $this->hasDefaultValue($param), $this->getDefaultValue($param), $param->allowsNull()); } return $arguments; @@ -88,23 +88,6 @@ private function hasDefaultValue(\ReflectionParameter $parameter) return $parameter->isDefaultValueAvailable(); } - /** - * Returns if the argument is allowed to be null but is still mandatory. - * - * @param \ReflectionParameter $parameter - * - * @return bool - */ - private function isNullable(\ReflectionParameter $parameter) - { - if ($this->supportsParameterType) { - return null !== ($type = $parameter->getType()) && $type->allowsNull(); - } - - // fallback for supported php 5.x versions - return $this->hasDefaultValue($parameter) && null === $this->getDefaultValue($parameter); - } - /** * Returns a default value if available. * @@ -127,24 +110,20 @@ private function getDefaultValue(\ReflectionParameter $parameter) private function getType(\ReflectionParameter $parameter) { if ($this->supportsParameterType) { - return $parameter->hasType() ? (string) $parameter->getType() : null; - } - - if ($parameter->isArray()) { - return 'array'; - } - - if ($parameter->isCallable()) { - return 'callable'; + if (!$type = $parameter->getType()) { + return; + } + $typeName = $type instanceof \ReflectionNamedType ? $type->getName() : $type->__toString(); + if ('array' === $typeName && !$type->isBuiltin()) { + // Special case for HHVM with variadics + return; + } + + return $typeName; } - try { - $refClass = $parameter->getClass(); - } catch (\ReflectionException $e) { - // mandatory; extract it from the exception message - return str_replace(array('Class ', ' does not exist'), '', $e->getMessage()); + if (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $parameter, $info)) { + return $info[1]; } - - return $refClass ? $refClass->getName() : null; } } diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php index 9e39e7493dab0..792d8bdbe7384 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php @@ -92,21 +92,24 @@ public function dump(Data $data) break; } elseif (isset($trace[$i]['object']) && $trace[$i]['object'] instanceof \Twig_Template) { - $info = $trace[$i]['object']; - $name = $info->getTemplateName(); - $src = method_exists($info, 'getSource') ? $info->getSource() : $info->getEnvironment()->getLoader()->getSource($name); - $info = $info->getDebugInfo(); - if (null !== $src && isset($info[$trace[$i - 1]['line']])) { - $file = false; + $template = $trace[$i]['object']; + $name = $template->getTemplateName(); + $src = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : false); + $info = $template->getDebugInfo(); + if (isset($info[$trace[$i - 1]['line']])) { $line = $info[$trace[$i - 1]['line']]; - $src = explode("\n", $src); - $fileExcerpt = array(); + $file = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getPath() : false; - for ($i = max($line - 3, 1), $max = min($line + 3, count($src)); $i <= $max; ++$i) { - $fileExcerpt[] = ''.$this->htmlEncode($src[$i - 1]).''; - } + if ($src) { + $src = explode("\n", $src); + $fileExcerpt = array(); + + for ($i = max($line - 3, 1), $max = min($line + 3, count($src)); $i <= $max; ++$i) { + $fileExcerpt[] = ''.$this->htmlEncode($src[$i - 1]).''; + } - $fileExcerpt = '
    '.implode("\n", $fileExcerpt).'
'; + $fileExcerpt = '
    '.implode("\n", $fileExcerpt).'
'; + } } break; } diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php index fd66b38cd2592..f274ac79c2c71 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php @@ -374,7 +374,7 @@ protected function parseController($controller) ); } - return (string) $controller ?: 'n/a'; + return is_string($controller) ? $controller : 'n/a'; } private function getCookieHeader($name, $value, $expires, $path, $domain, $secure, $httponly) diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index 18680e4cca851..3e7ff0aab8251 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -184,7 +184,7 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ if (!$request->isMethodSafe()) { $response = $this->invalidate($request, $catch); - } elseif ($request->headers->has('expect')) { + } elseif ($request->headers->has('expect') || !$request->isMethodCacheable()) { $response = $this->pass($request, $catch); } else { $response = $this->lookup($request, $catch); diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index f3a22fa06ab57..e6f9f4ab3da40 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -59,11 +59,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '3.1.5'; - const VERSION_ID = 30105; + const VERSION = '3.1.6'; + const VERSION_ID = 30106; const MAJOR_VERSION = 3; const MINOR_VERSION = 1; - const RELEASE_VERSION = 5; + const RELEASE_VERSION = 6; const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '01/2017'; diff --git a/src/Symfony/Component/HttpKernel/KernelEvents.php b/src/Symfony/Component/HttpKernel/KernelEvents.php index fd5ec796a8433..3e961737b844e 100644 --- a/src/Symfony/Component/HttpKernel/KernelEvents.php +++ b/src/Symfony/Component/HttpKernel/KernelEvents.php @@ -73,10 +73,9 @@ final class KernelEvents * The CONTROLLER_ARGUMENTS event occurs once controller arguments have been resolved. * * This event allows you to change the arguments that will be passed to - * the controller. The event listener method receives a - * Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent instance. + * the controller. * - * @Event + * @Event("Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent") * * @var string */ diff --git a/src/Symfony/Component/HttpKernel/Tests/Bundle/BundleTest.php b/src/Symfony/Component/HttpKernel/Tests/Bundle/BundleTest.php index 57568d3936c10..ee0c3128f1a2c 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Bundle/BundleTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Bundle/BundleTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\HttpKernel\Tests\Bundle; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionNotValidBundle\ExtensionNotValidBundle; use Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\ExtensionPresentBundle; use Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionAbsentBundle\ExtensionAbsentBundle; @@ -51,4 +52,18 @@ public function testGetContainerExtensionWithInvalidClass() $bundle = new ExtensionNotValidBundle(); $bundle->getContainerExtension(); } + + public function testHttpKernelRegisterCommandsIgnoresCommandsThatAreRegisteredAsServices() + { + $container = new ContainerBuilder(); + $container->register('console.command.Symfony_Component_HttpKernel_Tests_Fixtures_ExtensionPresentBundle_Command_FooCommand', 'Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\Command\FooCommand'); + + $application = $this->getMock('Symfony\Component\Console\Application'); + // add() is never called when the found command classes are already registered as services + $application->expects($this->never())->method('add'); + + $bundle = new ExtensionPresentBundle(); + $bundle->setContainer($container); + $bundle->registerCommands($application); + } } diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php index a1b9a1c78d857..d09bad891102b 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php @@ -229,6 +229,7 @@ public function testCreateControllerCanReturnAnyCallable() /** * @requires PHP 7.1 + * @group legacy */ public function testGetNullableArguments() { @@ -244,6 +245,7 @@ public function testGetNullableArguments() /** * @requires PHP 7.1 + * @group legacy */ public function testGetNullableArgumentsWithDefaults() { diff --git a/src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php b/src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php index 49931f052c82e..1ca9a3c798947 100644 --- a/src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php @@ -121,7 +121,7 @@ public function testNullableTypesSignature() new ArgumentMetadata('foo', 'string', false, false, null, true), new ArgumentMetadata('bar', \stdClass::class, false, false, null, true), new ArgumentMetadata('baz', 'string', false, true, 'value', true), - new ArgumentMetadata('mandatory', null, false, false, null), + new ArgumentMetadata('mandatory', null, false, false, null, true), ), $arguments); } diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php index eef00d4a024dd..df5d974fd8c30 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\HttpKernel\Tests\DataCollector; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; @@ -70,16 +71,28 @@ public function testKernelResponseDoesNotStartSession() } /** - * Test various types of controller callables. + * @dataProvider provideControllerCallables */ - public function testControllerInspection() + public function testControllerInspection($name, $callable, $expected) + { + $c = new RequestDataCollector(); + $request = $this->createRequest(); + $response = $this->createResponse(); + $this->injectController($c, $callable, $request); + $c->collect($request, $response); + + $this->assertSame($expected, $c->getController(), sprintf('Testing: %s', $name)); + } + + public function provideControllerCallables() { // make sure we always match the line number $r1 = new \ReflectionMethod($this, 'testControllerInspection'); $r2 = new \ReflectionMethod($this, 'staticControllerMethod'); $r3 = new \ReflectionClass($this); + // test name, callable, expected - $controllerTests = array( + return array( array( '"Regular" callable', array($this, 'testControllerInspection'), @@ -168,15 +181,17 @@ function () { return 'foo'; }, ), ), ); + } + + public function testItIgnoresInvalidCallables() + { + $request = $this->createRequestWithSession(); + $response = new RedirectResponse('/'); $c = new RequestDataCollector(); - $request = $this->createRequest(); - $response = $this->createResponse(); - foreach ($controllerTests as $controllerTest) { - $this->injectController($c, $controllerTest[1], $request); - $c->collect($request, $response); - $this->assertSame($controllerTest[2], $c->getController(), sprintf('Testing: %s', $controllerTest[0])); - } + $c->collect($request, $response); + + $this->assertSame('n/a', $c->getController()); } protected function createRequest() @@ -191,6 +206,16 @@ protected function createRequest() return $request; } + private function createRequestWithSession() + { + $request = $this->createRequest(); + $request->attributes->set('_controller', 'Foo::bar'); + $request->setSession(new Session(new MockArraySessionStorage())); + $request->getSession()->start(); + + return $request; + } + protected function createResponse() { $response = new Response(); diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php index 9d08fa562ec21..0ac736f32eda5 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php @@ -11,7 +11,6 @@ namespace Symfony\Component\HttpKernel\Tests\Fragment; -use Symfony\Bridge\PhpUnit\ErrorAssert; use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\HttpKernel\Fragment\EsiFragmentRenderer; use Symfony\Component\HttpKernel\HttpCache\Esi; @@ -28,16 +27,14 @@ public function testRenderFallbackToInlineStrategyIfEsiNotSupported() /** * @group legacy - * @requires function Symfony\Bridge\PhpUnit\ErrorAssert::assertDeprecationsAreTriggered + * @expectedDeprecation Passing non-scalar values as part of URI attributes to the ESI and SSI rendering strategies is deprecated %s. */ public function testRenderFallbackWithObjectAttributesIsDeprecated() { - ErrorAssert::assertDeprecationsAreTriggered('Passing non-scalar values as part of URI attributes to the ESI and SSI rendering strategies is deprecated', function () { - $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy(true), new UriSigner('foo')); - $request = Request::create('/'); - $reference = new ControllerReference('main_controller', array('foo' => array('a' => array(), 'b' => new \stdClass())), array()); - $strategy->render($reference, $request); - }); + $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy(true), new UriSigner('foo')); + $request = Request::create('/'); + $reference = new ControllerReference('main_controller', array('foo' => array('a' => array(), 'b' => new \stdClass())), array()); + $strategy->render($reference, $request); } public function testRender() diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php index 08deb53d30336..d5cf8ca7ae70d 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php @@ -1261,6 +1261,21 @@ public function testEsiCacheRemoveValidationHeadersIfEmbeddedResponses() $this->assertNull($this->response->getETag()); $this->assertNull($this->response->getLastModified()); } + + public function testDoesNotCacheOptionsRequest() + { + $this->setNextResponse(200, array('Cache-Control' => 'public, s-maxage=60'), 'get'); + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + + $this->setNextResponse(200, array('Cache-Control' => 'public, s-maxage=60'), 'options'); + $this->request('OPTIONS', '/'); + $this->assertHttpKernelIsCalled(); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertSame('get', $this->response->getContent()); + } } class TestKernel implements HttpKernelInterface diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index fce3075710226..ba9539789862c 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -18,7 +18,7 @@ "require": { "php": ">=5.5.9", "symfony/event-dispatcher": "~2.8|~3.0", - "symfony/http-foundation": "~2.8.8|~3.0.8|~3.1.2|~3.2", + "symfony/http-foundation": "~2.8.13|~3.1.6|~3.2", "symfony/debug": "~2.8|~3.0", "psr/log": "~1.0" }, diff --git a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php index 4fa445c932d11..54b74ab3660e3 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php @@ -230,7 +230,7 @@ private function getDocBlockFromProperty($class, $property) // Use a ReflectionProperty instead of $class to get the parent class if applicable try { $reflectionProperty = new \ReflectionProperty($class, $property); - } catch (\ReflectionException $reflectionException) { + } catch (\ReflectionException $e) { return; } @@ -263,7 +263,7 @@ private function getDocBlockFromMethod($class, $ucFirstProperty, $type) ) { break; } - } catch (\ReflectionException $reflectionException) { + } catch (\ReflectionException $e) { // Try the next prefix if the method doesn't exist } } diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php index 1d3d3a2083ca8..7fefbde2536a1 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php @@ -17,7 +17,7 @@ use Symfony\Component\PropertyInfo\Type; /** - * Extracts PHP informations using the reflection API. + * Extracts data using the reflection API. * * @author Kévin Dunglas */ @@ -44,6 +44,13 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp */ public static $arrayMutatorPrefixes = array('add', 'remove'); + private $supportsParameterType; + + public function __construct() + { + $this->supportsParameterType = method_exists('ReflectionParameter', 'getType'); + } + /** * {@inheritdoc} */ @@ -51,7 +58,7 @@ public function getProperties($class, array $context = array()) { try { $reflectionClass = new \ReflectionClass($class); - } catch (\ReflectionException $reflectionException) { + } catch (\ReflectionException $e) { return; } @@ -134,68 +141,33 @@ private function extractFromMutator($class, $property) $reflectionParameters = $reflectionMethod->getParameters(); $reflectionParameter = $reflectionParameters[0]; - $arrayMutator = in_array($prefix, self::$arrayMutatorPrefixes); - - if (method_exists($reflectionParameter, 'getType') && $reflectionType = $reflectionParameter->getType()) { - $fromReflectionType = $this->extractFromReflectionType($reflectionType); - - if (!$arrayMutator) { - return array($fromReflectionType); + if ($this->supportsParameterType) { + if (!$reflectionType = $reflectionParameter->getType()) { + return; } + $type = $this->extractFromReflectionType($reflectionType); - $phpType = Type::BUILTIN_TYPE_ARRAY; - $collectionKeyType = new Type(Type::BUILTIN_TYPE_INT); - $collectionValueType = $fromReflectionType; - } - - if ($reflectionParameter->isArray()) { - $phpType = Type::BUILTIN_TYPE_ARRAY; - $collection = true; - } - - if ($arrayMutator) { - $collection = true; - $nullable = false; - $collectionNullable = $reflectionParameter->allowsNull(); - } else { - $nullable = $reflectionParameter->allowsNull(); - $collectionNullable = false; - } - - if (!isset($collection)) { - $collection = false; - } - - if (method_exists($reflectionParameter, 'isCallable') && $reflectionParameter->isCallable()) { - $phpType = Type::BUILTIN_TYPE_CALLABLE; - } - - if ($typeHint = $reflectionParameter->getClass()) { - if ($collection) { - $phpType = Type::BUILTIN_TYPE_ARRAY; - $collectionKeyType = new Type(Type::BUILTIN_TYPE_INT); - $collectionValueType = new Type(Type::BUILTIN_TYPE_OBJECT, $collectionNullable, $typeHint->name); + // HHVM reports variadics with "array" but not builtin type hints + if (!$reflectionType->isBuiltin() && Type::BUILTIN_TYPE_ARRAY === $type->getBuiltinType()) { + return; + } + } elseif (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $reflectionParameter, $info)) { + if (Type::BUILTIN_TYPE_ARRAY === $info[1]) { + $type = new Type(Type::BUILTIN_TYPE_ARRAY, $reflectionParameter->allowsNull(), null, true); + } elseif (Type::BUILTIN_TYPE_CALLABLE === $info[1]) { + $type = new Type(Type::BUILTIN_TYPE_CALLABLE, $reflectionParameter->allowsNull()); } else { - $phpType = Type::BUILTIN_TYPE_OBJECT; - $typeClass = $typeHint->name; + $type = new Type(Type::BUILTIN_TYPE_OBJECT, $reflectionParameter->allowsNull(), $info[1]); } + } else { + return; } - // Nothing useful extracted - if (!isset($phpType)) { - return; + if (in_array($prefix, self::$arrayMutatorPrefixes)) { + $type = new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type); } - return array( - new Type( - $phpType, - $nullable, - isset($typeClass) ? $typeClass : null, - $collection, - isset($collectionKeyType) ? $collectionKeyType : null, - isset($collectionValueType) ? $collectionValueType : null - ), - ); + return array($type); } /** @@ -213,7 +185,7 @@ private function extractFromAccessor($class, $property) return; } - if (method_exists($reflectionMethod, 'getReturnType') && $reflectionType = $reflectionMethod->getReturnType()) { + if ($this->supportsParameterType && $reflectionType = $reflectionMethod->getReturnType()) { return array($this->extractFromReflectionType($reflectionType)); } @@ -231,15 +203,15 @@ private function extractFromAccessor($class, $property) */ private function extractFromReflectionType(\ReflectionType $reflectionType) { - $phpTypeOrClass = method_exists($reflectionType, 'getName') ? $reflectionType->getName() : (string) $reflectionType; + $phpTypeOrClass = $reflectionType instanceof \ReflectionNamedType ? $reflectionType->getName() : $reflectionType->__toString(); $nullable = $reflectionType->allowsNull(); - if ($reflectionType->isBuiltin()) { - if (Type::BUILTIN_TYPE_ARRAY === $phpTypeOrClass) { - $type = new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true); - } else { - $type = new Type($phpTypeOrClass, $nullable); - } + if (Type::BUILTIN_TYPE_ARRAY === $phpTypeOrClass) { + $type = new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true); + } elseif ('void' === $phpTypeOrClass) { + $type = new Type(Type::BUILTIN_TYPE_NULL, $nullable); + } elseif ($reflectionType->isBuiltin()) { + $type = new Type($phpTypeOrClass, $nullable); } else { $type = new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $phpTypeOrClass); } @@ -261,7 +233,7 @@ private function isPublicProperty($class, $property) $reflectionProperty = new \ReflectionProperty($class, $property); return $reflectionProperty->isPublic(); - } catch (\ReflectionException $reflectionExcetion) { + } catch (\ReflectionException $e) { // Return false if the property doesn't exist } @@ -290,7 +262,7 @@ private function getAccessorMethod($class, $property) if (0 === $reflectionMethod->getNumberOfRequiredParameters()) { return array($reflectionMethod, $prefix); } - } catch (\ReflectionException $reflectionException) { + } catch (\ReflectionException $e) { // Return null if the property doesn't exist } } @@ -319,7 +291,7 @@ private function getMutatorMethod($class, $property) if ($reflectionMethod->getNumberOfParameters() >= 1) { return array($reflectionMethod, $prefix); } - } catch (\ReflectionException $reflectionException) { + } catch (\ReflectionException $e) { // Try the next prefix if the method doesn't exist } } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php index 10b056220d539..8a828c21d66be 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php @@ -107,6 +107,7 @@ public function php71TypesProvider() { return array( array('foo', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true))), + array('buz', array(new Type(Type::BUILTIN_TYPE_NULL))), array('bar', array(new Type(Type::BUILTIN_TYPE_INT, true))), array('baz', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING)))), array('donotexist', null), diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php71Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php71Dummy.php index d93c67a3d38bd..e72d376c492fa 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php71Dummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Php71Dummy.php @@ -20,6 +20,10 @@ public function getFoo(): ?array { } + public function getBuz(): void + { + } + public function setBar(?int $bar) { } diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/RoleVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/RoleVoter.php index b017c81334a5d..d5f31760793d8 100644 --- a/src/Symfony/Component/Security/Core/Authorization/Voter/RoleVoter.php +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/RoleVoter.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Core\Authorization\Voter; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Role\RoleInterface; /** * RoleVoter votes if any attribute starts with a given prefix. @@ -41,7 +42,11 @@ public function vote(TokenInterface $token, $subject, array $attributes) $roles = $this->extractRoles($token); foreach ($attributes as $attribute) { - if (0 !== strpos($attribute, $this->prefix)) { + if ($attribute instanceof RoleInterface) { + $attribute = $attribute->getRole(); + } + + if (!is_string($attribute) || 0 !== strpos($attribute, $this->prefix)) { continue; } diff --git a/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/RoleVoterTest.php b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/RoleVoterTest.php index 9982bdfb1895b..45535ca427b22 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/RoleVoterTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authorization/Voter/RoleVoterTest.php @@ -36,6 +36,12 @@ public function getVoteTests() array(array('ROLE_FOO'), array('ROLE_FOO'), VoterInterface::ACCESS_GRANTED), array(array('ROLE_FOO'), array('FOO', 'ROLE_FOO'), VoterInterface::ACCESS_GRANTED), array(array('ROLE_BAR', 'ROLE_FOO'), array('ROLE_FOO'), VoterInterface::ACCESS_GRANTED), + + // Test mixed Types + array(array(), array(array()), VoterInterface::ACCESS_ABSTAIN), + array(array(), array(new \stdClass()), VoterInterface::ACCESS_ABSTAIN), + array(array('ROLE_BAR'), array(new Role('ROLE_BAR')), VoterInterface::ACCESS_GRANTED), + array(array('ROLE_BAR'), array(new Role('ROLE_FOO')), VoterInterface::ACCESS_DENIED), ); } diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php index f68807165d794..1063a334a65f1 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php @@ -90,7 +90,7 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) if (isset($data['max_depth'])) { if (!is_int($data['max_depth'])) { - throw new MappingException('The "max_depth" value must an integer in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName()); + throw new MappingException('The "max_depth" value must be an integer in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName()); } $attributeMetadata->setMaxDepth($data['max_depth']); diff --git a/src/Symfony/Component/Translation/Tests/Dumper/FileDumperTest.php b/src/Symfony/Component/Translation/Tests/Dumper/FileDumperTest.php index eb733df9456f7..2b4b163a758f0 100644 --- a/src/Symfony/Component/Translation/Tests/Dumper/FileDumperTest.php +++ b/src/Symfony/Component/Translation/Tests/Dumper/FileDumperTest.php @@ -46,7 +46,7 @@ public function testDumpBackupsFileIfExisting() $dumper = new ConcreteFileDumper(); $dumper->dump($catalogue, array('path' => $tempDir)); - $this->assertTrue(file_exists($backupFile)); + $this->assertFileExists($backupFile); @unlink($file); @unlink($backupFile); @@ -65,7 +65,7 @@ public function testDumpCreatesNestedDirectoriesAndFile() $dumper->setRelativePathTemplate('test/translations/%domain%.%locale%.%extension%'); $dumper->dump($catalogue, array('path' => $tempDir)); - $this->assertTrue(file_exists($file)); + $this->assertFileExists($file); @unlink($file); @rmdir($translationsDir); diff --git a/src/Symfony/Component/Validator/Mapping/PropertyMetadata.php b/src/Symfony/Component/Validator/Mapping/PropertyMetadata.php index ac71be828541d..d12701cb44aba 100644 --- a/src/Symfony/Component/Validator/Mapping/PropertyMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/PropertyMetadata.php @@ -58,8 +58,14 @@ public function getPropertyValue($object) */ protected function newReflectionMember($objectOrClassName) { + $originalClass = is_string($objectOrClassName) ? $objectOrClassName : get_class($objectOrClassName); + while (!property_exists($objectOrClassName, $this->getName())) { $objectOrClassName = get_parent_class($objectOrClassName); + + if (false === $objectOrClassName) { + throw new ValidatorException(sprintf('Property "%s" does not exist in class "%s".', $this->getName(), $originalClass)); + } } $member = new \ReflectionProperty($objectOrClassName, $this->getName()); diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf index 143fc166682c7..62779e19df978 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf @@ -310,6 +310,10 @@ This value does not match the expected {{ charset }} charset. Tato hodnota neodpovídá očekávané znakové sadě {{ charset }}. + + This is not a valid Business Identifier Code (BIC). + Tato hodnota není platný identifikační kód podniku (BIC). + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf index 93f196585e963..8ddb66d9c0b6f 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sk.xlf @@ -310,6 +310,10 @@ This value does not match the expected {{ charset }} charset. Táto hodnota nezodpovedá očakávanej znakovej sade {{ charset }}. + + This is not a valid Business Identifier Code (BIC). + Táto hodnota nie je platný identifikačný kód podniku (BIC). + diff --git a/src/Symfony/Component/Validator/Tests/Mapping/PropertyMetadataTest.php b/src/Symfony/Component/Validator/Tests/Mapping/PropertyMetadataTest.php index f411d950e1708..e0ff920fe1ef3 100644 --- a/src/Symfony/Component/Validator/Tests/Mapping/PropertyMetadataTest.php +++ b/src/Symfony/Component/Validator/Tests/Mapping/PropertyMetadataTest.php @@ -42,4 +42,14 @@ public function testGetPropertyValueFromOverriddenPrivateProperty() $this->assertTrue($metadata->isPublic($entity)); $this->assertEquals('Overridden data', $metadata->getPropertyValue($entity)); } + + public function testGetPropertyValueFromRemovedProperty() + { + $entity = new Entity('foobar'); + $metadata = new PropertyMetadata(self::CLASSNAME, 'internal'); + $metadata->name = 'test'; + + $this->setExpectedException('Symfony\Component\Validator\Exception\ValidatorException'); + $metadata->getPropertyValue($entity); + } } diff --git a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php index 95dec4e39bcd4..f71f649d471d6 100644 --- a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php +++ b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php @@ -761,7 +761,7 @@ private function validateGenericNode($value, $object, $cacheKey, MetadataInterfa * @param int $traversalStrategy The strategy used for * traversing the value * @param GroupSequence $groupSequence The group sequence - * @param string[]|null $cascadedGroup The group that should + * @param string|null $cascadedGroup The group that should * be passed to cascaded * objects instead of * the group sequence diff --git a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php index c90f3ea209bfb..d4f628e58b61f 100644 --- a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php @@ -150,15 +150,19 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $is if (!empty($f['class']) && is_subclass_of($f['class'], 'Twig_Template') && method_exists($f['class'], 'getDebugInfo')) { $template = isset($f['object']) ? $f['object'] : new $f['class'](new \Twig_Environment(new \Twig_Loader_Filesystem())); - - try { - $templateName = $template->getTemplateName(); - $templateSrc = explode("\n", method_exists($template, 'getSource') ? $template->getSource() : $template->getEnvironment()->getLoader()->getSource($templateName)); - $templateInfo = $template->getDebugInfo(); - if (isset($templateInfo[$f['line']])) { + $templateName = $template->getTemplateName(); + $templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : ''); + $templateInfo = $template->getDebugInfo(); + if (isset($templateInfo[$f['line']])) { + if (method_exists($template, 'getSourceContext')) { + $templateName = $template->getSourceContext()->getPath() ?: $templateName; + } + if ($templateSrc) { + $templateSrc = explode("\n", $templateSrc); $src[$templateName.':'.$templateInfo[$f['line']]] = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext); + } else { + $src[$templateName] = $templateInfo[$f['line']]; } - } catch (\Twig_Error_Loader $e) { } } } else { diff --git a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php index 2780c86a41485..3724b399279ab 100644 --- a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php @@ -73,7 +73,7 @@ public static function castType(\ReflectionType $c, array $a, Stub $stub, $isNes $prefix = Caster::PREFIX_VIRTUAL; $a += array( - $prefix.'name' => method_exists('ReflectionType', 'getName') ? $c->getName() : $c->__toString(), + $prefix.'name' => $c instanceof \ReflectionNamedType ? $c->getName() : $c->__toString(), $prefix.'allowsNull' => $c->allowsNull(), $prefix.'isBuiltin' => $c->isBuiltin(), ); @@ -157,7 +157,9 @@ public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, arra )); if (isset($a[$prefix.'returnType'])) { - $a[$prefix.'returnType'] = method_exists('ReflectionType', 'getName') ? $a[$prefix.'returnType']->getName() : $a[$prefix.'returnType']->__toString(); + $v = $a[$prefix.'returnType']; + $v = $v instanceof \ReflectionNamedType ? $v->getName() : $v->__toString(); + $a[$prefix.'returnType'] = $a[$prefix.'returnType']->allowsNull() ? '?'.$v : $v; } if (isset($a[$prefix.'this'])) { $a[$prefix.'this'] = new CutStub($a[$prefix.'this']); @@ -216,21 +218,12 @@ public static function castParameter(\ReflectionParameter $c, array $a, Stub $st 'allowsNull' => 'allowsNull', )); - try { - if (method_exists($c, 'hasType')) { - if ($c->hasType()) { - $a[$prefix.'typeHint'] = method_exists('ReflectionType', 'getName') ? $c->getType()->getName() : $c->getType()->__toString(); - } - } else { - $v = explode(' ', $c->__toString(), 6); - if (isset($v[5]) && 0 === strspn($v[4], '.&$')) { - $a[$prefix.'typeHint'] = $v[4]; - } - } - } catch (\ReflectionException $e) { - if (preg_match('/^Class ([^ ]++) does not exist$/', $e->getMessage(), $m)) { - $a[$prefix.'typeHint'] = $m[1]; + if (method_exists($c, 'getType')) { + if ($v = $c->getType()) { + $a[$prefix.'typeHint'] = $v instanceof \ReflectionNamedType ? $v->getName() : $v->__toString(); } + } elseif (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $c, $v)) { + $a[$prefix.'typeHint'] = $v[1]; } if (!isset($a[$prefix.'typeHint'])) { unset($a[$prefix.'allowsNull']); @@ -245,7 +238,7 @@ public static function castParameter(\ReflectionParameter $c, array $a, Stub $st unset($a[$prefix.'allowsNull']); } } catch (\ReflectionException $e) { - if (isset($a[$prefix.'typeHint']) && $c->allowsNull() && !method_exists('ReflectionType', 'getName')) { + if (isset($a[$prefix.'typeHint']) && $c->allowsNull() && !class_exists('ReflectionNamedType', false)) { $a[$prefix.'default'] = null; unset($a[$prefix.'allowsNull']); } diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php new file mode 100644 index 0000000000000..d4d10b310af61 --- /dev/null +++ b/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Caster; + +use Symfony\Component\VarDumper\Caster\FrameStub; +use Symfony\Component\VarDumper\Test\VarDumperTestTrait; + +class ExceptionCasterTest extends \PHPUnit_Framework_TestCase +{ + use VarDumperTestTrait; + + /** + * @requires function Twig_Template::getSourceContext + */ + public function testFrameWithTwig() + { + require_once dirname(__DIR__).'/Fixtures/Twig.php'; + + $f = array( + new FrameStub(array( + 'file' => dirname(__DIR__).'/Fixtures/Twig.php', + 'line' => 19, + 'class' => '__TwigTemplate_VarDumperFixture_u75a09', + 'object' => new \__TwigTemplate_VarDumperFixture_u75a09(new \Twig_Environment(new \Twig_Loader_Filesystem())), + )), + new FrameStub(array( + 'file' => dirname(__DIR__).'/Fixtures/Twig.php', + 'line' => 19, + 'class' => '__TwigTemplate_VarDumperFixture_u75a09', + 'object' => new \__TwigTemplate_VarDumperFixture_u75a09(new \Twig_Environment(new \Twig_Loader_Filesystem()), null), + )), + ); + + $expectedDump = <<<'EODUMP' +array:2 [ + 0 => { + class: "__TwigTemplate_VarDumperFixture_u75a09" + object: __TwigTemplate_VarDumperFixture_u75a09 { + %A + } + src: { + %sTwig.php:19: """ + // line 2\n + throw new \Exception('Foobar');\n + }\n + """ + bar.twig:2: """ + foo bar\n + twig source\n + \n + """ + } + } + 1 => { + class: "__TwigTemplate_VarDumperFixture_u75a09" + object: __TwigTemplate_VarDumperFixture_u75a09 { + %A + } + src: { + %sTwig.php:19: """ + // line 2\n + throw new \Exception('Foobar');\n + }\n + """ + foo.twig:2: """ + foo bar\n + twig source\n + \n + """ + } + } +] + +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $f); + } +} diff --git a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php index 9b00afd8048d2..cf2d00d41a85e 100644 --- a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php @@ -228,6 +228,9 @@ public function testFlags() putenv('DUMP_STRING_LENGTH='); } + /** + * @requires function Twig_Template::getSourceContext + */ public function testThrowingCaster() { $out = fopen('php://memory', 'r+b'); @@ -262,19 +265,6 @@ public function testThrowingCaster() rewind($out); $out = stream_get_contents($out); - if (method_exists($twig, 'getSource')) { - $twig = <<assertStringMatchesFormat( <<displayWithErrorHandling() ==> __TwigTemplate_VarDumperFixture_u75a09->doDisplay(): { src: { diff --git a/src/Symfony/Component/VarDumper/Tests/Fixtures/Twig.php b/src/Symfony/Component/VarDumper/Tests/Fixtures/Twig.php index f25aac6553bba..7ffdd2bd54a63 100644 --- a/src/Symfony/Component/VarDumper/Tests/Fixtures/Twig.php +++ b/src/Symfony/Component/VarDumper/Tests/Fixtures/Twig.php @@ -3,14 +3,14 @@ /* foo.twig */ class __TwigTemplate_VarDumperFixture_u75a09 extends Twig_Template { - public function __construct(Twig_Environment $env) + private $filename; + + public function __construct(Twig_Environment $env, $filename = 'bar.twig') { parent::__construct($env); - $this->parent = false; - - $this->blocks = array( - ); + $this->blocks = array(); + $this->filename = $filename; } protected function doDisplay(array $context, array $blocks = array()) @@ -26,14 +26,11 @@ public function getTemplateName() public function getDebugInfo() { - return array (19 => 2); + return array(19 => 2); } - public function getSource() + public function getSourceContext() { - return " foo bar - twig source - -"; + return new Twig_Source(" foo bar\n twig source\n\n", 'foo.twig', $this->filename); } } diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 6853e5a0ba2cc..68ff14983f3ae 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -317,7 +317,7 @@ public static function parseScalar($scalar, $flags = 0, $delimiters = null, $str } if ($output && '%' === $output[0]) { - @trigger_error(sprintf('Not quoting the scalar "%s" starting with the "%%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.' , $output), E_USER_DEPRECATED); + @trigger_error(sprintf('Not quoting the scalar "%s" starting with the "%%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.', $output), E_USER_DEPRECATED); } if ($evaluate) { @@ -606,7 +606,10 @@ private static function evaluateScalar($scalar, $flags, $references = array()) return (float) str_replace(',', '', $scalar); case preg_match(self::getTimestampRegex(), $scalar): if (Yaml::PARSE_DATETIME & $flags) { - return new \DateTime($scalar, new \DateTimeZone('UTC')); + $date = new \DateTime($scalar); + $date->setTimeZone(new \DateTimeZone('UTC')); + + return $date; } $timeZone = date_default_timezone_get(); diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php index 859a03e8014d8..d6d7790dda413 100644 --- a/src/Symfony/Component/Yaml/Tests/InlineTest.php +++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Yaml\Tests; -use Symfony\Bridge\PhpUnit\ErrorAssert; use Symfony\Component\Yaml\Inline; use Symfony\Component\Yaml\Yaml; @@ -255,14 +254,12 @@ public function getScalarIndicators() /** * @group legacy - * @requires function Symfony\Bridge\PhpUnit\ErrorAssert::assertDeprecationsAreTriggered + * @expectedDeprecation Not quoting the scalar "%bar " starting with the "%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0. * throws \Symfony\Component\Yaml\Exception\ParseException in 4.0 */ public function testParseUnquotedScalarStartingWithPercentCharacter() { - ErrorAssert::assertDeprecationsAreTriggered('Not quoting the scalar "%foo " starting with the "%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.', function () { - Inline::parse('{ foo: %foo }'); - }); + Inline::parse('{ foo: %bar }'); } /** @@ -510,7 +507,7 @@ public function testParseTimestampAsDateTimeObject($yaml, $year, $month, $day, $ $expected = new \DateTime($yaml); $expected->setTimeZone(new \DateTimeZone('UTC')); $expected->setDate($year, $month, $day); - $expected->setTime($hour, $minute, $second); + @$expected->setTime($hour, $minute, $second, 1000000 * ($second - (int) $second)); $this->assertEquals($expected, Inline::parse($yaml, Yaml::PARSE_DATETIME)); } @@ -518,9 +515,9 @@ public function testParseTimestampAsDateTimeObject($yaml, $year, $month, $day, $ public function getTimestampTests() { return array( - 'canonical' => array('2001-12-15T02:59:43.1Z', 2001, 12, 15, 2, 59, 43), - 'ISO-8601' => array('2001-12-15t21:59:43.10-05:00', 2001, 12, 16, 2, 59, 43), - 'spaced' => array('2001-12-15 21:59:43.10 -5', 2001, 12, 16, 2, 59, 43), + 'canonical' => array('2001-12-15T02:59:43.1Z', 2001, 12, 15, 2, 59, 43.1), + 'ISO-8601' => array('2001-12-15t21:59:43.10-05:00', 2001, 12, 16, 2, 59, 43.1), + 'spaced' => array('2001-12-15 21:59:43.10 -5', 2001, 12, 16, 2, 59, 43.1), 'date' => array('2001-12-15', 2001, 12, 15, 0, 0, 0), ); } @@ -533,7 +530,7 @@ public function testParseNestedTimestampListAsDateTimeObject($yaml, $year, $mont $expected = new \DateTime($yaml); $expected->setTimeZone(new \DateTimeZone('UTC')); $expected->setDate($year, $month, $day); - $expected->setTime($hour, $minute, $second); + @$expected->setTime($hour, $minute, $second, 1000000 * ($second - (int) $second)); $expectedNested = array('nested' => array($expected)); $yamlNested = "{nested: [$yaml]}";