diff --git a/.github/travis.php b/.github/travis.php index 2e255e995fdf..daaa858dc78b 100644 --- a/.github/travis.php +++ b/.github/travis.php @@ -46,8 +46,8 @@ $versions = @file_get_contents('https://packagist.org/packages/'.$package->name.'.json') ?: '{"package":{"versions":[]}}'; $versions = json_decode($versions); - foreach ($versions->package->versions as $version => $package) { - $packages[$package->name] += array($version => $package); + foreach ($versions->package->versions as $v => $package) { + $packages[$package->name] += array($v => $package); } } diff --git a/.travis.yml b/.travis.yml index 2833f3e63a64..64012caa6563 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,8 +26,9 @@ matrix: group: edge - php: 5.5 - php: 5.6 - env: deps=high - php: 7.0 + env: deps=high + - php: 7.1 env: deps=low fast_finish: true @@ -54,13 +55,13 @@ before_install: - if [[ ! $skip ]]; then echo session.gc_probability = 0 >> $INI_FILE; fi - if [[ ! $skip && $PHP = 5.* ]]; then echo extension = mongo.so >> $INI_FILE; fi - if [[ ! $skip && $PHP = 5.* ]]; then echo extension = memcache.so >> $INI_FILE; fi - - if [[ ! $skip && $PHP = 5.* ]]; then (echo yes | pecl install -f apcu-4.0.10 && echo apc.enable_cli = 1 >> $INI_FILE); fi - - if [[ ! $skip && $PHP = 7.* ]]; then (echo yes | pecl install -f apcu-5.1.2 && echo apc.enable_cli = 1 >> $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 [[ ! $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 - if [[ ! $skip && ! $PHP = hhvm* ]]; then echo extension = redis.so >> $INI_FILE; fi; - - if [[ ! $skip && ! $PHP = hhvm* ]]; then phpenv config-rm xdebug.ini; fi + - if [[ ! $skip && ! $PHP = hhvm* ]]; then phpenv config-rm xdebug.ini || echo "xdebug not available"; fi - if [[ ! $skip ]]; then composer self-update --stable; fi - if [[ ! $skip ]]; then cp .composer/* ~/.composer/; fi - if [[ ! $skip ]]; then ./phpunit install; fi diff --git a/CHANGELOG-3.0.md b/CHANGELOG-3.0.md index 12537a212ff3..a5027b10e289 100644 --- a/CHANGELOG-3.0.md +++ b/CHANGELOG-3.0.md @@ -7,6 +7,39 @@ in 3.0 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.0.0...v3.0.1 +* 3.0.8 (2016-06-30) + + * bug #19217 [HttpKernel] Inline ValidateRequestListener logic into HttpKernel (nicolas-grekas) + * bug #18688 [HttpFoundation] Warning when request has both Forwarded and X-Forwarded-For (magnusnordlander) + * bug #19173 [Console] Decouple SymfonyStyle from TableCell (ro0NL) + * bug #19189 [Console] Fix formatting of SymfonyStyle::comment() (chalasr) + * bug #19211 [Form] fix post max size translation type extension for >= 2.8 (Tobion) + * bug #17822 [WIP] [Form] fix `empty_data` option in expanded `ChoiceType` (HeahDude) + * bug #19134 Distinguish between first and subsequent progress bar displays (rquadling) + * bug #19061 [FORM] fix post_max_size_message translation (alt. 2) (David Badura) + * bug #19100 [Console] Fixed SymfonyQuestionHelper multi-choice with defaults (sstok) + * bug #18924 [DoctrineBridge] Don't use object IDs in DoctrineChoiceLoader when passing a value closure (webmozart) + * bug #19138 [DomCrawler] No more exception on field name with strange format (guiled, fabpot) + * bug #18935 [Form] Consider a violation even if the form is not submitted (egeloen) + * bug #19127 [Form] Add exception to FormRenderer about non-unique block names (enumag) + * bug #19118 [Process] Fix pipes cleaning on Windows (nicolas-grekas) + * bug #19128 Avoid phpunit 5.4 warnings on getMock (2.7+) (iltar) + * bug #19114 [HttpKernel] Dont close the reponse stream in debug (nicolas-grekas) + * bug #19101 [Session] fix PDO transaction aborted under PostgreSQL (Tobion) + * bug #18501 [HttpFoundation] changed MERGE queries (hjkl) + * bug #19062 [HttpFoundation] Fix UPSERT for PgSql >= 9.5 (nicolas-grekas) + * bug #18548 [Form] minor fixes in DateTime transformers (HeahDude) + * bug #18732 [PropertyAccess][DX] Enhance exception that say that some methods are missing if they don't (nykopol) + * bug #19048 [HttpFoundation] Use UPSERT for sessions stored in PgSql >= 9.5 (nicolas-grekas) + * bug #19038 Fix feature detection for IE (Alsciende) + * bug #18915 [DependencyInjection] force enabling the external XML entity loaders (xabbuh) + * bug #19020 [Form] Fixed collapsed choice attributes (HeahDude) + * bug #19028 [Yaml] properly count skipped comment lines (xabbuh) + * bug #19009 [WebProfilerBundle] Fix invalid CSS style (romainneutron) + * bug #17733 [Yaml] Fix wrong line number when comments are inserted in the middle of a block. (paradajozsef) + * bug #18911 Fixed singular of committee (peterrehm) + * bug #18971 Do not inject web debug toolbar on attachments (peterrehm) + * 3.0.7 (2016-06-06) * bug #18908 [DependencyInjection] force enabling the external XML entity loaders (xabbuh) diff --git a/CHANGELOG-3.1.md b/CHANGELOG-3.1.md index b85712f4262a..91fbe072b8cb 100644 --- a/CHANGELOG-3.1.md +++ b/CHANGELOG-3.1.md @@ -7,6 +7,42 @@ 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.3 (2016-07-30) + + * bug #19300 [HttpKernel] Use flock() for HttpCache's lock files (mpdude) + * bug #19428 [Process] Fix write access check for pipes on Windows (nicolas-grekas) + * bug #19442 [Cache] Fix default lifetime being ignored (nicolas-grekas) + * bug #19435 [Cache] Fix incorrect timestamps generated by FilesystemAdapter (nicolas-grekas) + * bug #19434 [Serializer] enable property info in framework bundle (David Badura) + * bug #19439 [DependencyInjection] Fixed deprecated default message template with XML (jeremyFreeAgent) + * bug #19397 [HttpFoundation] HttpCache refresh stale responses containing an ETag (maennchen) + * bug #19426 [Form] Fix the money form type render with Bootstrap3 (Th3Mouk) + * bug #19422 [DomCrawler] Inherit the namespace cache in subcrawlers (stof) + * bug #19425 [BrowserKit] Uppercase the "GET" method in redirects (jakzal) + * bug #19384 Fix PHP 7.1 related failures (nicolas-grekas) + * bug #19379 [VarDumper] Fix for PHP 7.1 (nicolas-grekas) + * bug #19342 Added class existence check if is_subclass_of() fails in compiler passes (SCIF) + * bug #19369 Fix the DBAL session handler version check for Postgresql (stof) + * bug #19368 [VarDumper] Fix dumping jsons casted as arrays (nicolas-grekas) + * bug #19334 [Security] Fix the retrieval of the last username when using forwarding (stof) + * bug #19352 [Serializer] Include the format in the cache key (dunglas) + * bug #19321 [HttpFoundation] Add OPTIONS and TRACE to the list of safe methods (dunglas) + * bug #19317 [BrowserKit] Update Client::getAbsoluteUri() for query string only URIs (georaldc) + * bug #19298 [ClassLoader] Fix declared classes being computed when not needed (nicolas-grekas) + * bug #19316 [Validator] Added additional MasterCard range to the CardSchemeValidator (Dennis Væversted) + * bug #19290 [HttpKernel] fixed internal subrequests having an if-modified-since-header (MalteWunsch) + * bug #19307 [Security] Fix deprecated usage of DigestAuthenticationEntryPoint::getKey() in DigestAuthenticationListener (Maxime STEINHAUSSER) + * bug #19309 [DoctrineBridge] added missing error code for constraint. (Koc) + * bug #19306 [Form] fixed bug - name in ButtonBuilder (cheprasov) + * bug #19292 [varDumper] Fix missing usage of ExceptionCaster::$traceArgs (nicolas-grekas) + * bug #19288 [VarDumper] Fix indentation trimming in ExceptionCaster (nicolas-grekas) + * bug #19267 [Validator] UuidValidator must accept a Uuid constraint. (hhamon) + * bug #19272 [Security] fixed DebugAccessDecisionManager::setVoters() (HeahDude) + * bug #19260 [Form] Fix depreciation triggers (tgalopin) + * bug #19186 Fix for #19183 to add support for new PHP MongoDB extension in sessions. (omanizer) + * bug #19253 [Console] Fix block() padding formatting after #19189 (chalasr) + * bug #19218 [Security][Guard] check if session exist before using it (pasdeloup) + * 3.1.2 (2016-06-30) * bug #19227 [DoctrineBridge] fixed default parameter value in UniqueEntityValidator (HeahDude) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 5fc732e26ff9..6f73ccb15047 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -8,8 +8,8 @@ Symfony is the result of the work of many people who made the code better - Nicolas Grekas (nicolas-grekas) - Bernhard Schussek (bschussek) - Tobias Schultze (tobion) - - Christophe Coevoet (stof) - Christian Flothmann (xabbuh) + - Christophe Coevoet (stof) - Jordi Boggiano (seldaek) - Victor Berchet (victor) - Johannes S (johannes) @@ -17,12 +17,12 @@ Symfony is the result of the work of many people who made the code better - Jakub Zalas (jakubzalas) - Ryan Weaver (weaverryan) - Javier Eguiluz (javier.eguiluz) - - Hugo Hamon (hhamon) - Kévin Dunglas (dunglas) + - Hugo Hamon (hhamon) - Abdellatif Ait boudad (aitboudad) - Pascal Borreli (pborreli) - - Joseph Bielawski (stloyd) - Wouter De Jong (wouterj) + - Joseph Bielawski (stloyd) - Romain Neutron (romain) - Karma Dordrak (drak) - Lukas Kahwe Smith (lsmith) @@ -34,16 +34,16 @@ Symfony is the result of the work of many people who made the code better - Grégoire Pineau (lyrixx) - Eriksen Costa (eriksencosta) - Sarah Khalil (saro0h) + - Jules Pietri (heah) - Jonathan Wage (jwage) - - Diego Saint Esteben (dosten) - Maxime Steinhausser (ogizanagi) + - Diego Saint Esteben (dosten) - Alexandre Salomé (alexandresalome) - William Durand (couac) - - Jules Pietri (heah) - ornicar + - Francis Besset (francisbesset) - stealth35 ‏ (stealth35) - Alexander Mols (asm89) - - Francis Besset (francisbesset) - Bulat Shakirzyanov (avalanche123) - Saša Stamenković (umpirsky) - Henrik Bjørnskov (henrikbjorn) @@ -52,21 +52,21 @@ Symfony is the result of the work of many people who made the code better - Konstantin Kudryashov (everzet) - Bilal Amarni (bamarni) - Florin Patan (florinpatan) - - Kevin Bond (kbond) - Peter Rehm (rpet) + - Kevin Bond (kbond) - Gábor Egyed (1ed) + - Ener-Getick (energetick) - Michel Weimerskirch (mweimerskirch) - Eric Clemmons (ericclemmons) + - Iltar van der Berg (kjarli) - Andrej Hudec (pulzarraider) - Christian Raue + - Charles Sarrazin (csarrazi) - Matthias Pigulla (mpdude) - Deni - Henrik Westphal (snc) - Dariusz Górecki (canni) - Arnout Boks (aboks) - - Iltar van der Berg (kjarli) - - Charles Sarrazin (csarrazi) - - Ener-Getick (energetick) - Douglas Greenshields (shieldo) - Lee McDermott - Brandon Turner @@ -82,49 +82,50 @@ Symfony is the result of the work of many people who made the code better - Antoine Hérault (herzult) - Arnaud Le Blanc (arnaud-lb) - Jérôme Tamarelle (gromnan) + - Paráda József (paradajozsef) - Michal Piotrowski (eventhorizon) - Tim Nagel (merk) - - Paráda József (paradajozsef) - Brice BERNARD (brikou) + - Robin Chalas (chalas_r) - Alexander M. Turek (derrabus) - Dariusz Ruminski - marc.weistroff - Issei Murasawa (issei_m) - lenar - Włodzimierz Gajda (gajdaw) + - Konstantin Myakshin (koc) + - Baptiste Clavié (talus) - Alexander Schwenn (xelaris) - Florian Voutzinos (florianv) - - Konstantin Myakshin (koc) - Colin Frei - Adrien Brault (adrienbrault) - Joshua Thijssen - - Baptiste Clavié (talus) - Peter Kokot (maastermedia) - excelwebzone - Jacob Dreesen (jdreesen) + - Jáchym Toušek (enumag) - Jérémy DERUSSÉ (jderusse) - Vladimir Reznichenko (kalessil) + - Tomáš Votruba (tomas_votruba) - Fabien Pennequin (fabienpennequin) - Gordon Franke (gimler) + - Eric GELOEN (gelo) - David Buchmann (dbu) - - Tomáš Votruba (tomas_votruba) - - Jáchym Toušek - Robert Schönthal (digitalkaoz) - Florian Lonqueu-Brochard (florianlb) - - Eric GELOEN (gelo) - Stefano Sala (stefano.sala) - Juti Noppornpitak (shiroyuki) + - Titouan Galopin (tgalopin) - Tigran Azatyan (tigranazatyan) - Sebastian Hörl (blogsh) - Daniel Gomes (danielcsgomes) - Hidenori Goto (hidenorigoto) + - Sebastiaan Stok (sstok) - Evgeniy (ewgraf) + - Tugdual Saunier (tucksaun) - Guilherme Blanco (guilhermeblanco) - Pablo Godel (pgodel) - - Titouan Galopin (tgalopin) - Jérémie Augustin (jaugustin) - - Sebastiaan Stok (sstok) - - Tugdual Saunier (tucksaun) - Andréia Bohner (andreia) - Rafael Dohms (rdohms) - Arnaud Kleinpeter (nanocom) @@ -141,6 +142,7 @@ 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 @@ -150,7 +152,6 @@ Symfony is the result of the work of many people who made the code better - Daniel Wehner - Possum - Dorian Villet (gnutix) - - Javier Spagnoletti (phansys) - Richard Miller (mr_r_miller) - Mario A. Alvarez Garcia (nomack84) - Dennis Benkert (denderello) @@ -179,6 +180,7 @@ Symfony is the result of the work of many people who made the code better - Sven Paulus (subsven) - Lars Strojny (lstrojny) - Rui Marinho (ruimarinho) + - Daniel Espendiller - Eugene Wissner - Julien Brochet (mewt) - Sergey Linnik (linniksa) @@ -212,9 +214,7 @@ Symfony is the result of the work of many people who made the code better - Kristen Gilden (kgilden) - Dawid Nowak - Pierre-Yves LEBECQ (pylebecq) - - Daniel Espendiller - Jakub Kucharovic (jkucharovic) - - Robin Chalas (chalas_r) - Eugene Leonovich (rybakit) - Filippo Tessarotto - Joseph Rouff (rouffj) @@ -238,6 +238,7 @@ Symfony is the result of the work of many people who made the code better - Roumen Damianoff (roumen) - Antonio J. García Lagar (ajgarlag) - Kim Hemsø Rasmussen (kimhemsoe) + - Christian Schmidt - Wouter Van Hecke - Peter Kruithof (pkruithof) - Michael Holm (hollo) @@ -245,6 +246,7 @@ Symfony is the result of the work of many people who made the code better - Hidde Wieringa (hiddewie) - Chris Smith (cs278) - Florian Klein (docteurklein) + - Oleg Voronkovich - Manuel Kiessling (manuelkiessling) - Daniel Wehner - Atsuhiro KUBO (iteman) @@ -294,7 +296,6 @@ Symfony is the result of the work of many people who made the code better - Inal DJAFAR (inalgnu) - Christian Gärtner (dagardner) - Tomasz Kowalczyk (thunderer) - - Christian Schmidt - François-Xavier de Guillebon (de-gui_f) - Damien Alexandre (damienalexandre) - Felix Labrecque @@ -322,6 +323,7 @@ Symfony is the result of the work of many people who made the code better - Endre Fejes - Tobias Naumann (tna) - Daniel Beyer + - Jhonny Lidfors (jhonne) - Shein Alexey - Baptiste Lafontaine - Joe Lencioni @@ -335,7 +337,6 @@ Symfony is the result of the work of many people who made the code better - Ivan Kurnosov - Xavier HAUSHERR - Albert Jessurum (ajessu) - - Oleg Voronkovich - Laszlo Korte - Pavel Batanov (scaytrase) - Miha Vrhovnik @@ -355,6 +356,7 @@ Symfony is the result of the work of many people who made the code better - Brian King - Michel Salib (michelsalib) - geoffrey + - Steffen Roßkamp - Valentin Jonovs (valentins-jonovs) - Jeanmonod David (jeanmonod) - Jan Schumann @@ -364,6 +366,7 @@ Symfony is the result of the work of many people who made the code better - Mihai Stancu - Olivier Dolbeau (odolbeau) - Jan Rosier (rosier) + - Magnus Nordlander (magnusnordlander) - vagrant - EdgarPE - Florian Pfitzer (marmelatze) @@ -379,6 +382,7 @@ Symfony is the result of the work of many people who made the code better - Christian Schmidt - Marek Štípek (maryo) - Marcin Sikoń (marphi) + - Roland Franssen (ro0) - Dominik Zogg (dominik.zogg) - Marek Pietrzak - Chad Sikorra (chadsikorra) @@ -390,6 +394,7 @@ Symfony is the result of the work of many people who made the code better - Zander Baldwin - Adam Harvey - Alex Bakhturin + - Alexander Obuhovich (aik099) - boombatower - Fabrice Bernhard (fabriceb) - Jérôme Macias (jeromemacias) @@ -412,10 +417,12 @@ Symfony is the result of the work of many people who made the code better - ondrowan - Barry vd. Heuvel (barryvdh) - Jerzy Zawadzki (jzawadzki) + - Théo FIDRY (theofidry) - Evan S Kaufman (evanskaufman) - mcben - Jérôme Vieilledent (lolautruche) - Maks Slesarenko + - Filip Procházka (fprochazka) - mmoreram - Markus Lanthaler (lanthaler) - Vicent Soria Durá (vicentgodella) @@ -440,7 +447,6 @@ Symfony is the result of the work of many people who made the code better - Benjamin Leveque (benji07) - Nate (frickenate) - jhonnyL - - Jhonny Lidfors (jhonne) - sasezaki - Dawid Pakuła (zulusx) - Florian Rey (nervo) @@ -503,6 +509,7 @@ Symfony is the result of the work of many people who made the code better - Maxime Douailin - Javier López (loalf) - Reinier Kip + - Geoffrey Brier (geoffrey-brier) - Dustin Dobervich (dustin10) - dantleech - Anne-Sophie Bachelard (annesophie) @@ -535,9 +542,11 @@ Symfony is the result of the work of many people who made the code better - Arturs Vonda - Sascha Grossenbacher - Szijarto Tamas + - David Badura (davidbadura) - Catalin Dan - Stephan Vock - Benjamin Zikarsky (bzikarsky) + - Anton Bakai - Simon Schick (simonsimcity) - redstar504 - Tristan Roussel @@ -556,7 +565,7 @@ Symfony is the result of the work of many people who made the code better - Richard van den Brand (ricbra) - develop - Mark Sonnabaum - - Alexander Obuhovich (aik099) + - Richard Quadling - jochenvdv - Arturas Smorgun (asarturas) - Alexander Volochnev (exelenz) @@ -573,6 +582,7 @@ Symfony is the result of the work of many people who made the code better - Baldur Rensch (brensch) - Vladyslav Petrovych - Alex Xandra Albert Sim + - Carson Full - Trent Steel (trsteel88) - Yuen-Chi Lian - Besnik Br @@ -589,6 +599,7 @@ Symfony is the result of the work of many people who made the code better - Leevi Graham (leevigraham) - Casper Valdemar Poulsen - Josiah (josiah) + - Joschi Kuphal - John Bohn (jbohn) - Marc Morera (mmoreram) - Andrew Hilobok (hilobok) @@ -618,7 +629,6 @@ Symfony is the result of the work of many people who made the code better - Adrien Lucas (adrienlucas) - James Michael DuPont - Tom Klingenberg - - Filip Procházka (fprochazka) - Christopher Hall (mythmakr) - Paul Kamer (pkamer) - Rafał Wrzeszcz (rafalwrzeszcz) @@ -637,7 +647,6 @@ Symfony is the result of the work of many people who made the code better - Neil Ferreira - Nathanael Noblet (gnat) - Dmitry Parnas (parnas) - - Théo FIDRY (theofidry) - Paul LE CORRE - DQNEO - Emanuele Iannone @@ -652,7 +661,6 @@ Symfony is the result of the work of many people who made the code better - Matt Robinson (inanimatt) - Aleksey Podskrebyshev - Calin Mihai Pristavu - - Steffen Roßkamp - David Marín Carreño (davefx) - Jörn Lang (j.lang) - Omar Yepez (oyepez003) @@ -677,6 +685,7 @@ Symfony is the result of the work of many people who made the code better - Don Pinkster - Maksim Muruev - Emil Einarsson + - Thomas Landauer - Thibault Duplessis - Marc Abramowitz - Martijn Evers @@ -697,7 +706,6 @@ Symfony is the result of the work of many people who made the code better - David Lima - Jérôme Vasseur - Brunet Laurent (lbrunet) - - Magnus Nordlander (magnusnordlander) - Mikhail Yurasov (mym) - LOUARDI Abdeltif (ouardisoft) - Robert Gruendler (pulse00) @@ -709,6 +717,7 @@ Symfony is the result of the work of many people who made the code better - Raul Fraile (raulfraile) - sensio - Patrick Kaufmann + - Piotr Stankowski - Reece Fowell (reecefowell) - Mátyás Somfai (smatyas) - stefan.r @@ -728,6 +737,7 @@ Symfony is the result of the work of many people who made the code better - Pieter - Michael Tibben - Sander Marechal + - Andre Rømcke (andrerom) - Radosław Benkel - ttomor - Mei Gwilym (meigwilym) @@ -743,6 +753,7 @@ Symfony is the result of the work of many people who made the code better - Danilo Silva - Zachary Tong (polyfractal) - Hryhorii Hrebiniuk + - Dennis Fridrich (dfridrich) - mcfedr (mcfedr) - hamza - dantleech @@ -752,6 +763,7 @@ Symfony is the result of the work of many people who made the code better - Guillaume Royer - Artem (digi) - boite + - MGDSoft - Vadim Tyukov (vatson) - Sortex - chispita @@ -804,10 +816,12 @@ Symfony is the result of the work of many people who made the code better - Benoit Garret - Thomas Royer (cydonia7) - DerManoMann + - Olaf Klischat - Jhonny Lidfors (jhonny) - Julien Bianchi (jubianchi) + - Robert Meijers - Marcin Chwedziak - - Roland Franssen (ro0) + - hjkl - Tony Cosentino (tony-co) - Rodrigo Díez Villamuera (rodrigodiez) - e-ivanov @@ -815,6 +829,7 @@ Symfony is the result of the work of many people who made the code better - Jeremy Bush - wizhippo - Viacheslav Sychov + - Carlos Ortega Huetos - rpg600 - Péter Buri (burci) - Davide Borsatto (davide.borsatto) @@ -834,6 +849,7 @@ Symfony is the result of the work of many people who made the code better - spdionis - Eduardo García Sanz (coma) - James Gilliland + - Rhodri Pugh (rodnaph) - David de Boer (ddeboer) - Gilles Doge (gido) - abulford @@ -855,7 +871,6 @@ Symfony is the result of the work of many people who made the code better - Juan Traverso - Philipp Strube - Christian Sciberras - - Anton Bakai - Clement Herreman (clemherreman) - Dan Ionut Dumitriu (danionut90) - Nyro (nyro) @@ -893,7 +908,6 @@ Symfony is the result of the work of many people who made the code better - azine - Dawid Sajdak - Ludek Stepan - - Geoffrey Brier - Aaron Stephens (astephens) - Craig Menning (cmenning) - Balázs Benyó (duplabe) @@ -917,6 +931,7 @@ Symfony is the result of the work of many people who made the code better - Cédric Lahouste (rapotor) - Samuel Vogel (samuelvogel) - Berat Doğan + - Juanmi Rodriguez Cerón - Anthony Ferrara - Klaas Cuvelier (kcuvelier) - Steve Frécinaux @@ -934,6 +949,7 @@ Symfony is the result of the work of many people who made the code better - Philip Frank - Lance McNearney - Giorgio Premi + - Ian Carroll - caponica - Matt Daum (daum) - Alberto Pirovano (geezmo) @@ -945,6 +961,7 @@ Symfony is the result of the work of many people who made the code better - Max Summe - WedgeSama - Felds Liscia + - Ahmed TAILOULOUTE (ahmedtai) - James Halsall (jaitsu) - Maxime Veber (nek-) - Sullivan SENECHAL @@ -968,6 +985,7 @@ Symfony is the result of the work of many people who made the code better - Max Romanovsky (maxromanovsky) - Mathieu Morlon - Daniel Tschinder + - Nykopol (nykopol) - Rafał Muszyński (rafmus90) - Timothy Anido (xanido) - Rick Prent @@ -1006,6 +1024,7 @@ Symfony is the result of the work of many people who made the code better - Konrad Mohrfeldt - Lance Chen - Andrey Astakhov (aast) + - Nikolay Labinskiy (e-moe) - kor3k kor3k (kor3k) - Stelian Mocanita (stelian) - Flavian (2much) @@ -1015,6 +1034,7 @@ Symfony is the result of the work of many people who made the code better - Hoffmann András - Olivier - pscheit + - Zdeněk Drahoš - Dan Harper - moldcraft - Ramon Kleiss (akathos) @@ -1137,6 +1157,7 @@ Symfony is the result of the work of many people who made the code better - Daan van Renterghem - Nicole Cordes - Bram Van der Sype (brammm) + - Guile (guile) - Julien Moulin (lizjulien) - Nikita Nefedov (nikita2206) - Mauro Foti (skler) @@ -1209,7 +1230,6 @@ Symfony is the result of the work of many people who made the code better - Haritz - Matthieu Prat - Grummfy - - Thomas Landauer - Filipe Guerra - Gerben Wijnja - Rowan Manning @@ -1260,6 +1280,7 @@ Symfony is the result of the work of many people who made the code better - Muriel (metalmumu) - Michael Pohlers (mick_the_big) - Cayetano Soriano Gallego (neoshadybeat) + - Jean Pasdeloup (pasdeloup) - Patrick McDougle (patrick-mcdougle) - Pablo Monterde Perez (plebs) - Jimmy Leger (redpanda) @@ -1298,7 +1319,6 @@ Symfony is the result of the work of many people who made the code better - Mantas Urnieža - Cas - Dusan Kasan - - Carson Full - Myke79 - Brian Debuire - Piers Warmers @@ -1332,6 +1352,7 @@ Symfony is the result of the work of many people who made the code better - Andras Ratz - andreabreu98 - Michael Schneider + - Cédric Bertolini - n-aleha - Şəhriyar İmanov - Kaipi Yann @@ -1358,6 +1379,7 @@ Symfony is the result of the work of many people who made the code better - Jon Cave - Sébastien HOUZE - Abdulkadir N. A. + - Ivan Menshykov - Yevgen Kovalienia - Lebnik - Sema @@ -1385,8 +1407,8 @@ Symfony is the result of the work of many people who made the code better - Oncle Tom - Christian Stocker - Dawid Nowak - - Richard Quadling - Karolis Daužickas + - Sergio Santoro - tirnanog06 - phc - Дмитрий Пацура @@ -1412,7 +1434,6 @@ Symfony is the result of the work of many people who made the code better - Loïc Vernet (coil) - Christoph Schaefer (cvschaefer) - Damon Jones (damon__jones) - - David Badura (davidbadura) - Daniel Londero (dlondero) - Sebastian Landwehr (dword123) - Adel ELHAIBA (eadel) @@ -1420,6 +1441,7 @@ Symfony is the result of the work of many people who made the code better - Elliot Anderson (elliot) - Sergey Zolotov (enleur) - Fabien D. (fabd) + - Carsten Eilers (fnc) - Sorin Gitlan (forapathy) - Yohan Giarelli (frequence-web) - Gerry Vandermaesen (gerryvdm) diff --git a/UPGRADE-3.1.md b/UPGRADE-3.1.md index ca60dcbf81d2..b9163cfff369 100644 --- a/UPGRADE-3.1.md +++ b/UPGRADE-3.1.md @@ -95,9 +95,9 @@ FrameworkBundle HttpKernel ---------- - * Passing objects as URI attributes to the ESI and SSI renderers has been + * Passing non-scalar values as URI attributes to the ESI and SSI renderers has been deprecated and will be removed in Symfony 4.0. The inline fragment - renderer should be used with object attributes. + renderer should be used with non-scalar attributes. * The `ControllerResolver::getArguments()` method has been deprecated and will be removed in 4.0. If you have your own `ControllerResolverInterface` diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index dbd9c35c44ff..98e8521520eb 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -84,9 +84,9 @@ FrameworkBundle HttpKernel ---------- - * Possibility to pass objects as URI attributes to the ESI and SSI renderers - has been removed. The inline fragment renderer should be used with object - attributes. + * Possibility to pass non-scalar values as URI attributes to the ESI and SSI + renderers has been removed. The inline fragment renderer should be used with + non-scalar attributes. * The `ControllerResolver::getArguments()` method has been removed. If you have your own `ControllerResolverInterface` implementation, you should diff --git a/composer.json b/composer.json index c1aed015914e..b6ac1e65d2b4 100644 --- a/composer.json +++ b/composer.json @@ -90,7 +90,8 @@ "phpdocumentor/reflection-docblock": "^3.0" }, "conflict": { - "phpdocumentor/reflection-docblock": "<3.0" + "phpdocumentor/reflection-docblock": "<3.0", + "phpdocumentor/type-resolver": "<0.2.0" }, "autoload": { "psr-4": { diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php index b04432bd49e4..a93b20eed5f4 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php @@ -314,10 +314,10 @@ protected function loadObjectManagerCacheDriver(array $objectManager, ContainerB /** * Loads a cache driver. * - * @param string $cacheDriverServiceId The cache driver name - * @param string $objectManagerName The object manager name - * @param array $cacheDriver The cache driver mapping - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container The ContainerBuilder instance + * @param string $cacheName The cache driver name + * @param string $objectManagerName The object manager name + * @param array $cacheDriver The cache driver mapping + * @param ContainerBuilder $container The ContainerBuilder instance * * @return string * diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php index 9d72c4b7ee12..11bb4d1135a9 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php @@ -110,15 +110,13 @@ abstract class RegisterMappingsPass implements CompilerPassInterface * @param string[] $managerParameters List of container parameters that could * hold the manager name. * @param string $driverPattern Pattern for the metadata driver service name - * @param string $enabledParameter Service container parameter that must be + * @param string|false $enabledParameter Service container parameter that must be * present to enable the mapping. Set to false * to not do any check, optional. * @param string $configurationPattern Pattern for the Configuration service name * @param string $registerAliasMethodName Name of Configuration class method to * register alias. * @param string[] $aliasMap Map of alias to namespace - * - * @since Support for bundle alias was added in Symfony 2.6 */ public function __construct($driver, array $namespaces, array $managerParameters, $driverPattern, $enabledParameter = false, $configurationPattern = '', $registerAliasMethodName = '', array $aliasMap = array()) { diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php index 72f961a501f0..9477d8265529 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php @@ -69,7 +69,7 @@ public function __construct($manager, $class, $idReader = null, $objectLoader = { // BC to be removed and replace with type hints in 4.0 if ($manager instanceof ChoiceListFactoryInterface) { - @trigger_error(sprintf('Passing a ChoiceListFactoryInterface to %s is deprecated since version 3.1 and will no longer be supported in 4.0. You should either call "%s::loadChoiceList" or override it to return a ChoiceListInterface.', __CLASS__, __CLASS__)); + @trigger_error(sprintf('Passing a ChoiceListFactoryInterface to %s is deprecated since version 3.1 and will no longer be supported in 4.0. You should either call "%s::loadChoiceList" or override it to return a ChoiceListInterface.', __CLASS__, __CLASS__), E_USER_DEPRECATED); // Provide a BC layer since $factory has changed // form first to last argument as of 3.1 diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php index 1f2d1e9e1ed7..cd966ea986f7 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php @@ -18,8 +18,6 @@ /** * A utility for reading object IDs. * - * @since 1.0 - * * @author Bernhard Schussek * * @internal This class is meant for internal use only. diff --git a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php b/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php index 92c7b6bf40af..d819ff0a6c51 100644 --- a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php +++ b/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php @@ -13,6 +13,7 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver\DriverException; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\Platforms\SQLServer2008Platform; /** @@ -241,9 +242,33 @@ private function getMergeSql() "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time;"; case 'sqlite' === $platform: return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)"; - case 'postgresql' === $platform && version_compare($this->con->getServerVersion(), '9.5', '>='): + case 'postgresql' === $platform && version_compare($this->getServerVersion(), '9.5', '>='): return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". "ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->timeCol)"; } } + + private function getServerVersion() + { + $params = $this->con->getParams(); + + // Explicit platform version requested (supersedes auto-detection), so we respect it. + if (isset($params['serverVersion'])) { + return $params['serverVersion']; + } + + $wrappedConnection = $this->con->getWrappedConnection(); + + if ($wrappedConnection instanceof ServerInfoAwareConnection) { + return $wrappedConnection->getServerVersion(); + } + + // Support DBAL 2.4 by accessing it directly when using PDO PgSQL + if ($wrappedConnection instanceof \PDO) { + return $wrappedConnection->getAttribute(\PDO::ATTR_SERVER_VERSION); + } + + // If we cannot guess the version, the empty string will mean we won't use the code for newer versions when doing version checks. + return ''; + } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php index d9ed107bda05..7299f106b896 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php @@ -166,6 +166,7 @@ public function testValidateUniqueness() ->atPath('property.path.name') ->setParameter('{{ value }}', 'Foo') ->setInvalidValue('Foo') + ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } @@ -190,6 +191,7 @@ public function testValidateCustomErrorPath() ->atPath('property.path.bar') ->setParameter('{{ value }}', 'Foo') ->setInvalidValue('Foo') + ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } @@ -242,6 +244,7 @@ public function testValidateUniquenessWithIgnoreNull() ->atPath('property.path.name') ->setParameter('{{ value }}', 'Foo') ->setInvalidValue('Foo') + ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } @@ -274,6 +277,7 @@ public function testValidateUniquenessWithValidCustomErrorPath() ->atPath('property.path.name2') ->setParameter('{{ value }}', 'Bar') ->setInvalidValue('Bar') + ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } @@ -407,6 +411,7 @@ public function testAssociatedEntity() ->atPath('property.path.single') ->setParameter('{{ value }}', $entity1) ->setInvalidValue($entity1) + ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } @@ -443,6 +448,7 @@ public function testValidateUniquenessNotToStringEntityWithAssociatedEntity() ->atPath('property.path.single') ->setParameter('{{ value }}', $expectedValue) ->setInvalidValue($expectedValue) + ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php index fc6e213bd772..315b1654e8f7 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php @@ -23,6 +23,8 @@ */ class UniqueEntity extends Constraint { + const NOT_UNIQUE_ERROR = '23bd9dbf-6b9b-41cd-a99e-4844bcf3077f'; + public $message = 'This value is already used.'; public $service = 'doctrine.orm.validator.unique'; public $em = null; @@ -31,6 +33,10 @@ class UniqueEntity extends Constraint public $errorPath = null; public $ignoreNull = true; + protected static $errorNames = array( + self::NOT_UNIQUE_ERROR => 'NOT_UNIQUE_ERROR', + ); + public function getRequiredOptions() { return array('fields'); diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index c85c344b9494..4fd8afff171b 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -135,6 +135,7 @@ public function validate($entity, Constraint $constraint) ->atPath($errorPath) ->setParameter('{{ value }}', $invalidValue) ->setInvalidValue($invalidValue) + ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->addViolation(); } } diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service_structure.txt b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service_structure.txt index 9b73cdeb70ca..496b0241a5ba 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service_structure.txt +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service_structure.txt @@ -22,5 +22,5 @@ class ProjectServiceContainer extends Container } } -class stdClass_%s extends \stdClass implements \ProxyManager\%s +class stdClass_%s extends %SstdClass implements \ProxyManager\%s {%a}%A diff --git a/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php b/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php index 7fc278758eb3..ea105e8d955e 100644 --- a/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php @@ -11,7 +11,6 @@ namespace Symfony\Bridge\Twig\Extension; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator; /** @@ -48,7 +47,7 @@ public function getFunctions() */ public function getLogoutPath($key = null) { - return $this->generator->getLogoutPath($key, UrlGeneratorInterface::ABSOLUTE_PATH); + return $this->generator->getLogoutPath($key); } /** @@ -60,7 +59,7 @@ public function getLogoutPath($key = null) */ public function getLogoutUrl($key = null) { - return $this->generator->getLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_URL); + return $this->generator->getLogoutUrl($key); } /** diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig index ef2035ab43f0..3e9f133d37b4 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig @@ -21,12 +21,12 @@ {% block money_widget -%}
- {% set prepend = '{{' == money_pattern[0:2] %} - {% if not prepend %} + {% set append = money_pattern starts with '{{' %} + {% if not append %} {{ money_pattern|replace({ '{{ widget }}':''}) }} {% endif %} {{- block('form_widget_simple') -}} - {% if prepend %} + {% if append %} {{ money_pattern|replace({ '{{ widget }}':''}) }} {% endif %}
diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 5aaba67d3bfb..4b9a6bd4e79d 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -33,7 +33,7 @@ "symfony/security-acl": "~2.8|~3.0", "symfony/stopwatch": "~2.8|~3.0", "symfony/console": "~2.8|~3.0", - "symfony/var-dumper": "~2.8|~3.0", + "symfony/var-dumper": "~2.8.9|~3.0.9|~3.1.3|~3.2", "symfony/expression-language": "~2.8|~3.0" }, "suggest": { diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php index 6ed81defdf17..cdafcbd69b10 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php @@ -47,7 +47,7 @@ protected function listBundles($output) } else { $output->writeln($title); $table = new Table($output); - $table->setHeaders($headers)->setRows($rows)->render($output); + $table->setHeaders($headers)->setRows($rows)->render(); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 9cd29a601976..08152d9ec4b4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -822,10 +822,10 @@ private function getValidatorMappingFiles(ContainerBuilder $container) if (is_dir($dir = $dirname.'/Resources/config/validation')) { foreach (Finder::create()->files()->in($dir)->name('*.xml') as $file) { - $files[0][] = $file->getRealpath(); + $files[0][] = $file->getRealPath(); } foreach (Finder::create()->files()->in($dir)->name('*.yml') as $file) { - $files[1][] = $file->getRealpath(); + $files[1][] = $file->getRealPath(); } $container->addResource(new DirectoryResource($dir)); @@ -965,13 +965,13 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder if (is_dir($dir = $dirname.'/Resources/config/serialization')) { foreach (Finder::create()->files()->in($dir)->name('*.xml') as $file) { - $definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader', array($file->getRealpath())); + $definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader', array($file->getRealPath())); $definition->setPublic(false); $serializerLoaders[] = $definition; } foreach (Finder::create()->files()->in($dir)->name('*.yml') as $file) { - $definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader', array($file->getRealpath())); + $definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader', array($file->getRealPath())); $definition->setPublic(false); $serializerLoaders[] = $definition; diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml index d5b5093fb648..370b364cd9e7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml @@ -21,6 +21,7 @@ null + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php index 6849f8fbd42e..4d2bb6e0ca3f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php @@ -68,6 +68,7 @@ 'enable_annotations' => true, 'name_converter' => 'serializer.name_converter.camel_case_to_snake_case', ), + 'property_info' => true, 'ide' => 'file%%link%%format', 'request' => array( 'formats' => array( diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml index e8e34d6e9c0d..84361e03a422 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -41,5 +41,6 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml index d345174e8b13..894cae4f7257 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -53,6 +53,7 @@ framework: enabled: true enable_annotations: true name_converter: serializer.name_converter.camel_case_to_snake_case + property_info: ~ ide: file%%link%%format request: formats: diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index a03235c1dd0b..57c138141193 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -462,6 +462,7 @@ public function testSerializerEnabled() $this->assertEquals('Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader', $argument[0]->getClass()); $this->assertNull($container->getDefinition('serializer.mapping.class_metadata_factory')->getArgument(1)); $this->assertEquals(new Reference('serializer.name_converter.camel_case_to_snake_case'), $container->getDefinition('serializer.normalizer.object')->getArgument(1)); + $this->assertEquals(new Reference('property_info', ContainerBuilder::IGNORE_ON_INVALID_REFERENCE), $container->getDefinition('serializer.normalizer.object')->getArgument(3)); } public function testRegisterSerializerExtractor() @@ -546,6 +547,7 @@ public function testSerializerCacheDisabled() /** * @group legacy + * @requires function Symfony\Bridge\PhpUnit\ErrorAssert::assertDeprecationsAreTriggered */ public function testDeprecatedSerializerCacheOption() { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/DelegatingEngineTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/DelegatingEngineTest.php index c5d2d8bbebed..0f887218137d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/DelegatingEngineTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/DelegatingEngineTest.php @@ -38,7 +38,7 @@ public function testGetExistingEngine() $delegatingEngine = new DelegatingEngine($container, array('engine.first', 'engine.second')); - $this->assertSame($secondEngine, $delegatingEngine->getEngine('template.php', array('foo' => 'bar'))); + $this->assertSame($secondEngine, $delegatingEngine->getEngine('template.php')); } /** @@ -55,7 +55,7 @@ public function testGetInvalidEngine() )); $delegatingEngine = new DelegatingEngine($container, array('engine.first', 'engine.second')); - $delegatingEngine->getEngine('template.php', array('foo' => 'bar')); + $delegatingEngine->getEngine('template.php'); } public function testRenderResponseWithFrameworkEngine() diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php index 8506b9ea6cf6..253d070561d4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php @@ -86,6 +86,7 @@ public function testParseValidNameWithNotFoundBundle() /** * @group legacy * @dataProvider provideAbsolutePaths + * @requires function Symfony\Bridge\PhpUnit\ErrorAssert::assertDeprecationsAreTriggered */ public function testAbsolutePathsAreDeprecated($name, $logicalName, $path, $ref) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/TranslationLoader.php b/src/Symfony/Bundle/FrameworkBundle/Translation/TranslationLoader.php index 0a00cf2b1958..b6377863afe4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Translation/TranslationLoader.php +++ b/src/Symfony/Bundle/FrameworkBundle/Translation/TranslationLoader.php @@ -58,7 +58,7 @@ public function loadMessages($directory, MessageCatalogue $catalogue) $extension = $catalogue->getLocale().'.'.$format; $files = $finder->files()->name('*.'.$extension)->in($directory); foreach ($files as $file) { - $domain = substr($file->getFileName(), 0, -1 * strlen($extension) - 1); + $domain = substr($file->getFilename(), 0, -1 * strlen($extension) - 1); $catalogue->addCatalogue($loader->load($file->getPathname(), $catalogue->getLocale(), $domain)); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index db6b04003914..80327f96ecde 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -55,7 +55,8 @@ "twig/twig": "~1.23|~2.0" }, "conflict": { - "phpdocumentor/reflection-docblock": "<3.0" + "phpdocumentor/reflection-docblock": "<3.0", + "phpdocumentor/type-resolver": "<0.2.0" }, "suggest": { "ext-apcu": "For best performance of the system caches", diff --git a/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php b/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php index 0ee259fb7b23..269e029a0858 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php @@ -48,7 +48,7 @@ public function testExists() ; $loader = new FilesystemLoader($locator, $parser); - return $this->assertTrue($loader->exists($template)); + $this->assertTrue($loader->exists($template)); } /** diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig index 14afbfca9590..98ea6c3265d2 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig @@ -160,7 +160,7 @@ /** * Query an element with a CSS selector. * - * @param string selector a CSS-selector-compatible query string + * @param {string} selector - a CSS-selector-compatible query string * * @return DOMElement|null */ diff --git a/src/Symfony/Component/Asset/Tests/PackagesTest.php b/src/Symfony/Component/Asset/Tests/PackagesTest.php index 81db37b996f2..0a78a8b4fa6e 100644 --- a/src/Symfony/Component/Asset/Tests/PackagesTest.php +++ b/src/Symfony/Component/Asset/Tests/PackagesTest.php @@ -14,8 +14,6 @@ use Symfony\Component\Asset\Package; use Symfony\Component\Asset\Packages; use Symfony\Component\Asset\VersionStrategy\StaticVersionStrategy; -use Symfony\Component\Asset\Exception\InvalidArgumentException; -use Symfony\Component\Asset\Exception\LogicException; class PackagesTest extends \PHPUnit_Framework_TestCase { @@ -57,7 +55,7 @@ public function testGetUrl() } /** - * @expectedException LogicException + * @expectedException \Symfony\Component\Asset\Exception\LogicException */ public function testNoDefaultPackage() { @@ -66,7 +64,7 @@ public function testNoDefaultPackage() } /** - * @expectedException InvalidArgumentException + * @expectedException \Symfony\Component\Asset\Exception\InvalidArgumentException */ public function testUndefinedPackage() { diff --git a/src/Symfony/Component/BrowserKit/Client.php b/src/Symfony/Component/BrowserKit/Client.php index 858acba9570c..278a767bf021 100644 --- a/src/Symfony/Component/BrowserKit/Client.php +++ b/src/Symfony/Component/BrowserKit/Client.php @@ -475,7 +475,7 @@ public function followRedirect() $request = $this->internalRequest; if (in_array($this->internalResponse->getStatus(), array(302, 303))) { - $method = 'get'; + $method = 'GET'; $files = array(); $content = null; } else { @@ -484,7 +484,7 @@ public function followRedirect() $content = $request->getContent(); } - if ('get' === strtolower($method)) { + if ('GET' === strtoupper($method)) { // Don't forward parameters for GET request as it should reach the redirection URI $parameters = array(); } else { @@ -542,9 +542,9 @@ protected function getAbsoluteUri($uri) return parse_url($currentUri, PHP_URL_SCHEME).':'.$uri; } - // anchor? - if (!$uri || '#' == $uri[0]) { - return preg_replace('/#.*?$/', '', $currentUri).$uri; + // anchor or query string parameters? + if (!$uri || '#' == $uri[0] || '?' == $uri[0]) { + return preg_replace('/[#?].*?$/', '', $currentUri).$uri; } if ('/' !== $uri[0]) { diff --git a/src/Symfony/Component/BrowserKit/Tests/ClientTest.php b/src/Symfony/Component/BrowserKit/Tests/ClientTest.php index 132d4bc51e43..e57cc4c06a6b 100644 --- a/src/Symfony/Component/BrowserKit/Tests/ClientTest.php +++ b/src/Symfony/Component/BrowserKit/Tests/ClientTest.php @@ -212,6 +212,15 @@ public function testRequestURIConversion() $client->request('GET', 'http://www.example.com/'); $client->request('GET', 'http'); $this->assertEquals('http://www.example.com/http', $client->getRequest()->getUri(), '->request() uses the previous request for relative URLs'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/foo'); + $client->request('GET', '?'); + $this->assertEquals('http://www.example.com/foo?', $client->getRequest()->getUri(), '->request() uses the previous request for ?'); + $client->request('GET', '?'); + $this->assertEquals('http://www.example.com/foo?', $client->getRequest()->getUri(), '->request() uses the previous request for ?'); + $client->request('GET', '?foo=bar'); + $this->assertEquals('http://www.example.com/foo?foo=bar', $client->getRequest()->getUri(), '->request() uses the previous request for ?'); } public function testRequestReferer() @@ -401,7 +410,7 @@ public function testFollowRedirectWithMaxRedirects() $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected'))); $client->request('POST', 'http://www.example.com/foo/foobar', array('name' => 'bar')); - $this->assertEquals('get', $client->getRequest()->getMethod(), '->followRedirect() uses a get for 302'); + $this->assertEquals('GET', $client->getRequest()->getMethod(), '->followRedirect() uses a GET for 302'); $this->assertEquals(array(), $client->getRequest()->getParameters(), '->followRedirect() does not submit parameters when changing the method'); } @@ -480,6 +489,26 @@ public function testGetMaxRedirects() $this->assertEquals(3, $client->getMaxRedirects(), '->getMaxRedirects() returns assigned value'); } + public function testFollowRedirectWithPostMethod() + { + $parameters = array('foo' => 'bar'); + $files = array('myfile.foo' => 'baz'); + $server = array('X_TEST_FOO' => 'bazbar'); + $content = 'foobarbaz'; + + $client = new TestClient(); + + $client->setNextResponse(new Response('', 307, array('Location' => 'http://www.example.com/redirected'))); + $client->request('POST', 'http://www.example.com/foo/foobar', $parameters, $files, $server, $content); + + $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() follows a redirect with POST method'); + $this->assertArrayHasKey('foo', $client->getRequest()->getParameters(), '->followRedirect() keeps parameters with POST method'); + $this->assertArrayHasKey('myfile.foo', $client->getRequest()->getFiles(), '->followRedirect() keeps files with POST method'); + $this->assertArrayHasKey('X_TEST_FOO', $client->getRequest()->getServer(), '->followRedirect() keeps $_SERVER with POST method'); + $this->assertEquals($content, $client->getRequest()->getContent(), '->followRedirect() keeps content with POST method'); + $this->assertEquals('POST', $client->getRequest()->getMethod(), '->followRedirect() keeps request method'); + } + public function testBack() { $client = new TestClient(); diff --git a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php index 305ffdfa14ec..2ec6db566c6c 100644 --- a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php @@ -53,7 +53,7 @@ function ($deferred, $namespace, &$expiredIds) { foreach ($deferred as $key => $item) { if (null === $item->expiry) { - $byLifetime[0][$namespace.$key] = $item->value; + $byLifetime[0 < $item->defaultLifetime ? $item->defaultLifetime : 0][$namespace.$key] = $item->value; } elseif ($item->expiry > $now) { $byLifetime[$item->expiry - $now][$namespace.$key] = $item->value; } else { diff --git a/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php b/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php index 48bfa15e9ef4..b9c1686b9b44 100644 --- a/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php @@ -151,6 +151,9 @@ public function save(CacheItemInterface $item) return false; } } + if (null === $expiry && 0 < $item["\0*\0defaultLifetime"]) { + $expiry = time() + $item["\0*\0defaultLifetime"]; + } $this->values[$key] = $value; $this->expiries[$key] = null !== $expiry ? $expiry : PHP_INT_MAX; diff --git a/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php b/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php index bad7c5eb3e6a..515ea6806d15 100644 --- a/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php @@ -126,7 +126,7 @@ protected function doDelete(array $ids) protected function doSave(array $values, $lifetime) { $ok = true; - $expiresAt = $lifetime ? time() + $lifetime : PHP_INT_MAX; + $expiresAt = time() + ($lifetime ?: 31557600); // 31557600s = 1 year $tmp = $this->directory.uniqid('', true); foreach ($values as $id => $value) { diff --git a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php index c6a7efe160d6..2ed895c873a3 100644 --- a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php @@ -144,6 +144,9 @@ private function doSave(CacheItemInterface $item, $method) } $item = (array) $item; $expiry = $item["\0*\0expiry"]; + if (null === $expiry && 0 < $item["\0*\0defaultLifetime"]) { + $expiry = time() + $item["\0*\0defaultLifetime"]; + } $innerItem = $item["\0*\0poolHash"] === $this->poolHash ? $item["\0*\0innerItem"] : $this->pool->getItem($this->namespace.$item["\0*\0key"]); $innerItem->set($item["\0*\0value"]); $innerItem->expiresAt(null !== $expiry ? \DateTime::createFromFormat('U', $expiry) : null); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/AbstractRedisAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/AbstractRedisAdapterTest.php index 8f1ebf655b35..579c5fbe91d5 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/AbstractRedisAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/AbstractRedisAdapterTest.php @@ -11,20 +11,19 @@ namespace Symfony\Component\Cache\Tests\Adapter; -use Cache\IntegrationTests\CachePoolTest; use Symfony\Component\Cache\Adapter\RedisAdapter; -abstract class AbstractRedisAdapterTest extends CachePoolTest +abstract class AbstractRedisAdapterTest extends AdapterTestCase { protected static $redis; - public function createCachePool() + public function createCachePool($defaultLifetime = 0) { if (defined('HHVM_VERSION')) { $this->skippedTests['testDeferredSaveWithoutCommit'] = 'Fails on HHVM'; } - return new RedisAdapter(self::$redis, str_replace('\\', '.', __CLASS__)); + return new RedisAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime); } public static function setupBeforeClass() diff --git a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php new file mode 100644 index 000000000000..acbefe498f42 --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Cache\IntegrationTests\CachePoolTest; + +abstract class AdapterTestCase extends CachePoolTest +{ + public function testDefaultLifeTime() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + + return; + } + + $cache = $this->createCachePool(2); + + $item = $cache->getItem('key.dlt'); + $item->set('value'); + $cache->save($item); + sleep(1); + + $item = $cache->getItem('key.dlt'); + $this->assertTrue($item->isHit()); + + sleep(2); + $item = $cache->getItem('key.dlt'); + $this->assertFalse($item->isHit()); + } +} diff --git a/src/Symfony/Component/Cache/Tests/Adapter/ApcuAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/ApcuAdapterTest.php index 1cd4269b45f1..1a735114c60c 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/ApcuAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/ApcuAdapterTest.php @@ -11,12 +11,11 @@ namespace Symfony\Component\Cache\Tests\Adapter; -use Cache\IntegrationTests\CachePoolTest; use Symfony\Component\Cache\Adapter\ApcuAdapter; -class ApcuAdapterTest extends CachePoolTest +class ApcuAdapterTest extends AdapterTestCase { - public function createCachePool() + public function createCachePool($defaultLifetime = 0) { if (defined('HHVM_VERSION')) { $this->skippedTests['testDeferredSaveWithoutCommit'] = 'Fails on HHVM'; @@ -28,7 +27,7 @@ public function createCachePool() $this->markTestSkipped('Fails transiently on Windows.'); } - return new ApcuAdapter(str_replace('\\', '.', __CLASS__)); + return new ApcuAdapter(str_replace('\\', '.', __CLASS__), $defaultLifetime); } public function testUnserializable() diff --git a/src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php index 8f073876462c..8059e8c961c4 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php @@ -11,21 +11,20 @@ namespace Symfony\Component\Cache\Tests\Adapter; -use Cache\IntegrationTests\CachePoolTest; use Symfony\Component\Cache\Adapter\ArrayAdapter; /** * @group time-sensitive */ -class ArrayAdapterTest extends CachePoolTest +class ArrayAdapterTest extends AdapterTestCase { protected $skippedTests = array( 'testDeferredSaveWithoutCommit' => 'Assumes a shared cache which ArrayAdapter is not.', 'testSaveWithoutExpire' => 'Assumes a shared cache which ArrayAdapter is not.', ); - public function createCachePool() + public function createCachePool($defaultLifetime = 0) { - return new ArrayAdapter(); + return new ArrayAdapter($defaultLifetime); } } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php index 64fb83ce45ad..b6500d5b5142 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Cache\Tests\Adapter; -use Cache\IntegrationTests\CachePoolTest; use Symfony\Component\Cache\Adapter\FilesystemAdapter; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Adapter\ChainAdapter; @@ -20,15 +19,15 @@ /** * @author Kévin Dunglas */ -class ChainAdapterTest extends CachePoolTest +class ChainAdapterTest extends AdapterTestCase { - public function createCachePool() + public function createCachePool($defaultLifetime = 0) { if (defined('HHVM_VERSION')) { $this->skippedTests['testDeferredSaveWithoutCommit'] = 'Fails on HHVM'; } - return new ChainAdapter(array(new ArrayAdapter(), new ExternalAdapter(), new FilesystemAdapter())); + return new ChainAdapter(array(new ArrayAdapter($defaultLifetime), new ExternalAdapter(), new FilesystemAdapter('', $defaultLifetime)), $defaultLifetime); } /** diff --git a/src/Symfony/Component/Cache/Tests/Adapter/DoctrineAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/DoctrineAdapterTest.php index cde91b359fe0..2efd769a24a6 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/DoctrineAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/DoctrineAdapterTest.php @@ -11,22 +11,21 @@ namespace Symfony\Component\Cache\Tests\Adapter; -use Cache\IntegrationTests\CachePoolTest; use Doctrine\Common\Cache\ArrayCache; use Symfony\Component\Cache\Adapter\DoctrineAdapter; /** * @group time-sensitive */ -class DoctrineAdapterTest extends CachePoolTest +class DoctrineAdapterTest extends AdapterTestCase { protected $skippedTests = array( 'testDeferredSaveWithoutCommit' => 'Assumes a shared cache which ArrayCache is not.', 'testSaveWithoutExpire' => 'Assumes a shared cache which ArrayCache is not.', ); - public function createCachePool() + public function createCachePool($defaultLifetime = 0) { - return new DoctrineAdapter(new ArrayCache()); + return new DoctrineAdapter(new ArrayCache(), '', $defaultLifetime); } } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/FilesystemAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/FilesystemAdapterTest.php index 418cbf9ebc29..3605ea837947 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/FilesystemAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/FilesystemAdapterTest.php @@ -11,21 +11,20 @@ namespace Symfony\Component\Cache\Tests\Adapter; -use Cache\IntegrationTests\CachePoolTest; use Symfony\Component\Cache\Adapter\FilesystemAdapter; /** * @group time-sensitive */ -class FilesystemAdapterTest extends CachePoolTest +class FilesystemAdapterTest extends AdapterTestCase { - public function createCachePool() + public function createCachePool($defaultLifetime = 0) { if (defined('HHVM_VERSION')) { $this->skippedTests['testDeferredSaveWithoutCommit'] = 'Fails on HHVM'; } - return new FilesystemAdapter('sf-cache'); + return new FilesystemAdapter('', $defaultLifetime); } public static function tearDownAfterClass() diff --git a/src/Symfony/Component/Cache/Tests/Adapter/NamespacedProxyAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/NamespacedProxyAdapterTest.php index 9b82d91a1af7..afa545ea050d 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/NamespacedProxyAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/NamespacedProxyAdapterTest.php @@ -19,8 +19,8 @@ */ class NamespacedProxyAdapterTest extends ProxyAdapterTest { - public function createCachePool() + public function createCachePool($defaultLifetime = 0) { - return new ProxyAdapter(new ArrayAdapter(), 'foo'); + return new ProxyAdapter(new ArrayAdapter(), 'foo', $defaultLifetime); } } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/ProxyAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/ProxyAdapterTest.php index 21f2a2bf586c..bef6862631ec 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/ProxyAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/ProxyAdapterTest.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Cache\Tests\Adapter; -use Cache\IntegrationTests\CachePoolTest; use Psr\Cache\CacheItemInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Adapter\ProxyAdapter; @@ -20,16 +19,16 @@ /** * @group time-sensitive */ -class ProxyAdapterTest extends CachePoolTest +class ProxyAdapterTest extends AdapterTestCase { protected $skippedTests = array( 'testDeferredSaveWithoutCommit' => 'Assumes a shared cache which ArrayAdapter is not.', 'testSaveWithoutExpire' => 'Assumes a shared cache which ArrayAdapter is not.', ); - public function createCachePool() + public function createCachePool($defaultLifetime = 0) { - return new ProxyAdapter(new ArrayAdapter()); + return new ProxyAdapter(new ArrayAdapter(), '', $defaultLifetime); } /** diff --git a/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php b/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php index 19d450b939be..3ec68c6b5eec 100644 --- a/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php +++ b/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php @@ -43,9 +43,9 @@ public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = self::$loaded[$name] = true; - $declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); - if ($adaptive) { + $declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); + // don't include already declared classes $classes = array_diff($classes, $declared); @@ -84,11 +84,14 @@ public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = } } - if (!$reload && is_file($cache)) { + if (!$reload && file_exists($cache)) { require_once $cache; return; } + if (!$adaptive) { + $declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); + } $files = array(); $content = ''; diff --git a/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php b/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php index e75ed34f469a..b07f6079ebd9 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php @@ -185,6 +185,48 @@ public function testTrueEnableEnabledNode($expected, $config, $message) ); } + public function testCanBeDisabled() + { + $node = new ArrayNodeDefinition('root'); + $node->canBeDisabled(); + + $this->assertTrue($this->getField($node, 'addDefaults')); + $this->assertEquals(array('enabled' => false), $this->getField($node, 'falseEquivalent')); + $this->assertEquals(array('enabled' => true), $this->getField($node, 'trueEquivalent')); + $this->assertEquals(array('enabled' => true), $this->getField($node, 'nullEquivalent')); + + $nodeChildren = $this->getField($node, 'children'); + $this->assertArrayHasKey('enabled', $nodeChildren); + + $enabledNode = $nodeChildren['enabled']; + $this->assertTrue($this->getField($enabledNode, 'default')); + $this->assertTrue($this->getField($enabledNode, 'defaultValue')); + } + + public function testIgnoreExtraKeys() + { + $node = new ArrayNodeDefinition('root'); + + $this->assertFalse($this->getField($node, 'ignoreExtraKeys')); + + $result = $node->ignoreExtraKeys(); + + $this->assertEquals($node, $result); + $this->assertTrue($this->getField($node, 'ignoreExtraKeys')); + } + + public function testNormalizeKeys() + { + $node = new ArrayNodeDefinition('root'); + + $this->assertTrue($this->getField($node, 'normalizeKeys')); + + $result = $node->normalizeKeys(false); + + $this->assertEquals($node, $result); + $this->assertFalse($this->getField($node, 'normalizeKeys')); + } + public function getEnableableNodeFixtures() { return array( diff --git a/src/Symfony/Component/Config/Tests/Definition/Builder/ExprBuilderTest.php b/src/Symfony/Component/Config/Tests/Definition/Builder/ExprBuilderTest.php index 147bf1324c88..ebb766eed63d 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Builder/ExprBuilderTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Builder/ExprBuilderTest.php @@ -150,6 +150,26 @@ public function testThenUnsetExpression() $this->assertEquals(array(), $this->finalizeTestBuilder($test)); } + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage You must specify an if part. + */ + public function testEndIfPartNotSpecified() + { + $this->getTestBuilder()->end(); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage You must specify a then part. + */ + public function testEndThenPartNotSpecified() + { + $builder = $this->getTestBuilder(); + $builder->ifPart = 'test'; + $builder->end(); + } + /** * Create a test treebuilder with a variable node, and init the validation. * diff --git a/src/Symfony/Component/Config/Util/XmlUtils.php b/src/Symfony/Component/Config/Util/XmlUtils.php index d8d4eaa3b17a..25d9b0a0abe5 100644 --- a/src/Symfony/Component/Config/Util/XmlUtils.php +++ b/src/Symfony/Component/Config/Util/XmlUtils.php @@ -123,7 +123,7 @@ public static function loadFile($file, $schemaOrCallable = null) * * @return array A PHP array */ - public static function convertDomElementToArray(\DomElement $element, $checkPrefix = true) + public static function convertDomElementToArray(\DOMElement $element, $checkPrefix = true) { $prefix = (string) $element->prefix; $empty = true; diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index da0f22fd5345..6434ef715284 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -210,7 +210,7 @@ public function getHelperSet() } /** - * Set an input definition set to be used with this application. + * Set an input definition to be used with this application. * * @param InputDefinition $definition The input definition */ @@ -352,6 +352,8 @@ public function register($name) /** * Adds an array of command objects. * + * If a Command is not enabled it will not be added. + * * @param Command[] $commands An array of commands */ public function addCommands(array $commands) @@ -365,10 +367,11 @@ public function addCommands(array $commands) * Adds a command object. * * If a command with the same name already exists, it will be overridden. + * If the command is not enabled it will not be added. * * @param Command $command A Command object * - * @return Command The registered command + * @return Command|null The registered command if enabled or null */ public function add(Command $command) { @@ -437,9 +440,9 @@ public function has($name) /** * Returns an array of all unique namespaces used by currently registered commands. * - * It does not returns the global namespace which always exists. + * It does not return the global namespace which always exists. * - * @return array An array of namespaces + * @return string[] An array of namespaces */ public function getNamespaces() { @@ -933,7 +936,7 @@ private function getSttyColumns() /** * Runs and parses mode CON if it's available, suppressing any error output. * - * @return string x or null if it could not be parsed + * @return string|null x or null if it could not be parsed */ private function getConsoleMode() { @@ -992,7 +995,7 @@ public function extractNamespace($name, $limit = null) * @param string $name The string * @param array|\Traversable $collection The collection * - * @return array A sorted array of similar string + * @return string[] A sorted array of similar string */ private function findAlternatives($name, $collection) { @@ -1091,7 +1094,7 @@ private function splitStringByWidth($string, $width) * * @param string $name The full name of the command * - * @return array The namespaces of the command + * @return string[] The namespaces of the command */ private function extractAllNamespaces($name) { diff --git a/src/Symfony/Component/Console/Helper/SymfonyQuestionHelper.php b/src/Symfony/Component/Console/Helper/SymfonyQuestionHelper.php index b17482556ee4..aeddb760e077 100644 --- a/src/Symfony/Component/Console/Helper/SymfonyQuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/SymfonyQuestionHelper.php @@ -67,7 +67,7 @@ protected function writePrompt(OutputInterface $output, Question $question) break; - case $question instanceof ChoiceQuestion && $question->isMultiSelect(): + case $question instanceof ChoiceQuestion && $question->isMultiselect(): $choices = $question->getChoices(); $default = explode(',', $default); diff --git a/src/Symfony/Component/Console/Helper/Table.php b/src/Symfony/Component/Console/Helper/Table.php index e66594021c77..7f2dd1b2380a 100644 --- a/src/Symfony/Component/Console/Helper/Table.php +++ b/src/Symfony/Component/Console/Helper/Table.php @@ -487,7 +487,7 @@ private function fillNextRows($rows, $line) } // create a two dimensional array (rowspan x colspan) - $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, ''), $unmergedRows); + $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, array()), $unmergedRows); foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { $value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : ''; $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, array('colspan' => $cell->getColspan())); diff --git a/src/Symfony/Component/Console/Style/SymfonyStyle.php b/src/Symfony/Component/Console/Style/SymfonyStyle.php index 59c5bbf87ed9..a9c57443427b 100644 --- a/src/Symfony/Component/Console/Style/SymfonyStyle.php +++ b/src/Symfony/Component/Console/Style/SymfonyStyle.php @@ -410,9 +410,16 @@ private function createBlock($messages, $type = null, $style = null, $prefix = ' } } + $firstLineIndex = 0; + if ($padding && $this->isDecorated()) { + $firstLineIndex = 1; + array_unshift($lines, ''); + $lines[] = ''; + } + foreach ($lines as $i => &$line) { if (null !== $type) { - $line = 0 === $i ? $type.$line : $lineIndentation.$line; + $line = $firstLineIndex === $i ? $type.$line : $lineIndentation.$line; } $line = $prefix.$line; @@ -423,11 +430,6 @@ private function createBlock($messages, $type = null, $style = null, $prefix = ' } } - if ($padding && $this->isDecorated()) { - array_unshift($lines, ''); - $lines[] = ''; - } - return $lines; } } diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 954d02eaad96..974056d19913 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -425,7 +425,7 @@ public function testFindAlternativeNamespace() $application->add(new \FooCommand()); $application->add(new \Foo1Command()); $application->add(new \Foo2Command()); - $application->add(new \foo3Command()); + $application->add(new \Foo3Command()); try { $application->find('Unknown-namespace:Unknown-command'); diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_16.php b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_16.php new file mode 100644 index 000000000000..f21fc10d9c71 --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_16.php @@ -0,0 +1,15 @@ +setDecorated(true); + $output = new SymfonyStyleWithForcedLineLength($input, $output); + $output->success( + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum', + 'TEST' + ); +}; diff --git a/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_16.txt b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_16.txt new file mode 100644 index 000000000000..a0d180165b87 --- /dev/null +++ b/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_16.txt @@ -0,0 +1,8 @@ + +  + [OK] Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore  + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo  + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.  + Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum  +  + diff --git a/src/Symfony/Component/Debug/Tests/MockExceptionHandler.php b/src/Symfony/Component/Debug/Tests/MockExceptionHandler.php index a85d2d15e713..2d6ce564d23a 100644 --- a/src/Symfony/Component/Debug/Tests/MockExceptionHandler.php +++ b/src/Symfony/Component/Debug/Tests/MockExceptionHandler.php @@ -13,7 +13,7 @@ use Symfony\Component\Debug\ExceptionHandler; -class MockExceptionHandler extends Exceptionhandler +class MockExceptionHandler extends ExceptionHandler { public $e; diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php index ffb8c01f0f76..cc6e0e71b365 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -153,9 +153,9 @@ public function getRemovingPasses() } /** - * Gets all passes for the Merge pass. + * Gets the Merge pass. * - * @return array An array of passes + * @return CompilerPassInterface The merge pass */ public function getMergePass() { diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index bd12c672ea5d..6b1381b01201 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -166,7 +166,7 @@ private function parseDefinition(\DOMElement $service, $file) } if ($deprecated = $this->getChildren($service, 'deprecated')) { - $definition->setDeprecated(true, $deprecated[0]->nodeValue); + $definition->setDeprecated(true, $deprecated[0]->nodeValue ?: null); } $definition->setArguments($this->getArgumentsAsPhp($service, 'argument')); @@ -585,7 +585,7 @@ private function loadFromExtensions(\DOMDocument $xml) * * @return array A PHP array */ - public static function convertDomElementToArray(\DomElement $element) + public static function convertDomElementToArray(\DOMElement $element) { return XmlUtils::convertDomElementToArray($element); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 25c4cb12c099..0679d310e569 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -58,6 +58,9 @@ public function testDefinitions() } } + /** + * @requires function Symfony\Bridge\PhpUnit\ErrorAssert::assertDeprecationsAreTriggered + */ public function testCreateDeprecatedService() { ErrorAssert::assertDeprecationsAreTriggered('The "deprecated_foo" service is deprecated. You should stop using it, as it will soon be removed.', function () { diff --git a/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php b/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php index 7905e7f3c6b9..423c5db2ec96 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php @@ -34,7 +34,7 @@ public function testCrossCheck($fixture, $type) $loaderClass = 'Symfony\\Component\\DependencyInjection\\Loader\\'.ucfirst($type).'FileLoader'; $dumperClass = 'Symfony\\Component\\DependencyInjection\\Dumper\\'.ucfirst($type).'Dumper'; - $tmp = tempnam('sf_service_container', 'sf'); + $tmp = tempnam(sys_get_temp_dir(), 'sf'); file_put_contents($tmp, file_get_contents(self::$fixturesPath.'/'.$type.'/'.$fixture)); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index b40c835d3fba..d155a9ac763e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -100,7 +100,7 @@ public function provideInvalidParameters() { return array( array(array('foo' => new Definition('stdClass'))), - array(array('foo' => new Expression('service("foo").foo() ~ (container.hasparameter("foo") ? parameter("foo") : "default")'))), + array(array('foo' => new Expression('service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")'))), array(array('foo' => new Reference('foo'))), array(array('foo' => new Variable('foo'))), ); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php index f499635604be..ba25dc3c9936 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php @@ -49,7 +49,7 @@ ->addMethodCall('setBar', array(new Reference('foo2', ContainerInterface::NULL_ON_INVALID_REFERENCE))) ->addMethodCall('setBar', array(new Reference('foo3', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))) ->addMethodCall('setBar', array(new Reference('foobaz', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))) - ->addMethodCall('setBar', array(new Expression('service("foo").foo() ~ (container.hasparameter("foo") ? parameter("foo") : "default")'))) + ->addMethodCall('setBar', array(new Expression('service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")'))) ; $container ->register('foo_with_inline', 'Foo') diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php index cde6104c86ee..058a25b63fb1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php @@ -299,7 +299,7 @@ protected function getMethodCall1Service() if ($this->has('foobaz')) { $instance->setBar($this->get('foobaz', ContainerInterface::NULL_ON_INVALID_REFERENCE)); } - $instance->setBar(($this->get("foo")->foo() . (($this->hasparameter("foo")) ? ($this->getParameter("foo")) : ("default")))); + $instance->setBar(($this->get("foo")->foo() . (($this->hasParameter("foo")) ? ($this->getParameter("foo")) : ("default")))); return $instance; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php index 73247e2c7539..bcbd4a67b6b9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -291,7 +291,7 @@ protected function getMethodCall1Service() $instance->setBar($this->get('foo')); $instance->setBar(NULL); - $instance->setBar(($this->get("foo")->foo() . (($this->hasparameter("foo")) ? ($this->getParameter("foo")) : ("default")))); + $instance->setBar(($this->get("foo")->foo() . (($this->hasParameter("foo")) ? ($this->getParameter("foo")) : ("default")))); return $instance; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml index ad738802e2d3..70981ff5450b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml @@ -30,7 +30,7 @@ - service("foo").foo() ~ (container.hasparameter("foo") ? parameter("foo") : "default") + service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default") diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml index d3974c07b01a..9ad09ad5918d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml @@ -56,7 +56,7 @@ - service("foo").foo() ~ (container.hasparameter("foo") ? parameter("foo") : "default") + service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default") diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_deprecated.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_deprecated.xml new file mode 100644 index 000000000000..c19a47adf517 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_deprecated.xml @@ -0,0 +1,11 @@ + + + + + + + + The "%service_id%" service is deprecated. + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml index 08a1df1e6bfc..dbd7b88f680a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml @@ -12,7 +12,7 @@ services: calls: - [ setBar, [] ] - [ setBar ] - - [ setBar, ['@=service("foo").foo() ~ (container.hasparameter("foo") ? parameter("foo") : "default")'] ] + - [ setBar, ['@=service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")'] ] method_call2: class: FooClass calls: diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml index 4a97dcadffa6..44a174b6edb5 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml @@ -36,7 +36,7 @@ services: - [setBar, ['@?foo2']] - [setBar, ['@?foo3']] - [setBar, ['@?foobaz']] - - [setBar, ['@=service("foo").foo() ~ (container.hasparameter("foo") ? parameter("foo") : "default")']] + - [setBar, ['@=service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")']] foo_with_inline: class: Foo diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index cb235ac81226..fefac300b4fa 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -239,7 +239,7 @@ public function testLoadServices() $this->assertEquals('sc_configure', $services['configurator1']->getConfigurator(), '->load() parses the configurator tag'); $this->assertEquals(array(new Reference('baz', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false), 'configure'), $services['configurator2']->getConfigurator(), '->load() parses the configurator tag'); $this->assertEquals(array('BazClass', 'configureStatic'), $services['configurator3']->getConfigurator(), '->load() parses the configurator tag'); - $this->assertEquals(array(array('setBar', array()), array('setBar', array(new Expression('service("foo").foo() ~ (container.hasparameter("foo") ? parameter("foo") : "default")')))), $services['method_call1']->getMethodCalls(), '->load() parses the method_call tag'); + $this->assertEquals(array(array('setBar', array()), array('setBar', array(new Expression('service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")')))), $services['method_call1']->getMethodCalls(), '->load() parses the method_call tag'); $this->assertEquals(array(array('setBar', array('foo', new Reference('foo'), array(true, false)))), $services['method_call2']->getMethodCalls(), '->load() parses the method_call tag'); $this->assertEquals('factory', $services['new_factory1']->getFactory(), '->load() parses the factory tag'); $this->assertEquals(array(new Reference('baz', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false), 'getClass'), $services['new_factory2']->getFactory(), '->load() parses the factory tag'); @@ -302,6 +302,21 @@ public function testParseTagWithEmptyNameThrowsException() $loader->load('tag_with_empty_name.xml'); } + public function testDeprecated() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_deprecated.xml'); + + $this->assertTrue($container->getDefinition('foo')->isDeprecated()); + $message = 'The "foo" service is deprecated. You should stop using it, as it will soon be removed.'; + $this->assertSame($message, $container->getDefinition('foo')->getDeprecationMessage('foo')); + + $this->assertTrue($container->getDefinition('bar')->isDeprecated()); + $message = 'The "bar" service is deprecated.'; + $this->assertSame($message, $container->getDefinition('bar')->getDeprecationMessage('bar')); + } + public function testConvertDomElementToArray() { $doc = new \DOMDocument('1.0'); @@ -544,6 +559,7 @@ public function testAutowire() /** * @group legacy + * @requires function Symfony\Bridge\PhpUnit\ErrorAssert::assertDeprecationsAreTriggered */ public function testAliasDefinitionContainsUnsupportedElements() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 1f79f1f8580d..eff657d1faab 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -135,7 +135,7 @@ public function testLoadServices() $this->assertEquals('sc_configure', $services['configurator1']->getConfigurator(), '->load() parses the configurator tag'); $this->assertEquals(array(new Reference('baz'), 'configure'), $services['configurator2']->getConfigurator(), '->load() parses the configurator tag'); $this->assertEquals(array('BazClass', 'configureStatic'), $services['configurator3']->getConfigurator(), '->load() parses the configurator tag'); - $this->assertEquals(array(array('setBar', array()), array('setBar', array()), array('setBar', array(new Expression('service("foo").foo() ~ (container.hasparameter("foo") ? parameter("foo") : "default")')))), $services['method_call1']->getMethodCalls(), '->load() parses the method_call tag'); + $this->assertEquals(array(array('setBar', array()), array('setBar', array()), array('setBar', array(new Expression('service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")')))), $services['method_call1']->getMethodCalls(), '->load() parses the method_call tag'); $this->assertEquals(array(array('setBar', array('foo', new Reference('foo'), array(true, false)))), $services['method_call2']->getMethodCalls(), '->load() parses the method_call tag'); $this->assertEquals('factory', $services['new_factory1']->getFactory(), '->load() parses the factory tag'); $this->assertEquals(array(new Reference('baz'), 'getClass'), $services['new_factory2']->getFactory(), '->load() parses the factory tag'); diff --git a/src/Symfony/Component/DependencyInjection/composer.json b/src/Symfony/Component/DependencyInjection/composer.json index a73f1452cf4f..5ec239010774 100644 --- a/src/Symfony/Component/DependencyInjection/composer.json +++ b/src/Symfony/Component/DependencyInjection/composer.json @@ -19,7 +19,7 @@ "php": ">=5.5.9" }, "require-dev": { - "symfony/yaml": "~2.8|~3.0", + "symfony/yaml": "~2.8.7|~3.0.7|~3.1.1|~3.2", "symfony/config": "~2.8|~3.0", "symfony/expression-language": "~2.8|~3.0" }, diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php index 3c81742b3446..316d0083c65a 100644 --- a/src/Symfony/Component/DomCrawler/Crawler.php +++ b/src/Symfony/Component/DomCrawler/Crawler.php @@ -1105,6 +1105,7 @@ private function createSubCrawler($nodes) $crawler = new static($nodes, $this->uri, $this->baseHref); $crawler->isHtml = $this->isHtml; $crawler->document = $this->document; + $crawler->namespaces = $this->namespaces; return $crawler; } diff --git a/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php b/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php index ebfe435f8712..326bfd184f49 100644 --- a/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php +++ b/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php @@ -97,9 +97,13 @@ public function process(ContainerBuilder $container) // We must assume that the class value has been correctly filled, even if the service is created by a factory $class = $container->getParameterBag()->resolveValue($def->getClass()); - $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface'; + if (!is_subclass_of($class, $interface)) { + if (!class_exists($class, false)) { + throw new \InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + } + throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); } diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index bc58b070e34d..72ab9bf8cf17 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -491,7 +491,7 @@ public function tempnam($dir, $prefix) // If no scheme or scheme is "file" or "gs" (Google Cloud) create temp file in local filesystem if (null === $scheme || 'file' === $scheme || 'gs' === $scheme) { - $tmpFile = tempnam($hierarchy, $prefix); + $tmpFile = @tempnam($hierarchy, $prefix); // If tempnam failed or no scheme return the filename otherwise prepend the scheme if (false !== $tmpFile) { diff --git a/src/Symfony/Component/Form/ButtonBuilder.php b/src/Symfony/Component/Form/ButtonBuilder.php index 00f04a8d81f7..23dced537f48 100644 --- a/src/Symfony/Component/Form/ButtonBuilder.php +++ b/src/Symfony/Component/Form/ButtonBuilder.php @@ -62,11 +62,12 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface */ public function __construct($name, array $options = array()) { - if (empty($name) && 0 != $name) { + $name = (string) $name; + if ('' === $name) { throw new InvalidArgumentException('Buttons cannot have empty names.'); } - $this->name = (string) $name; + $this->name = $name; $this->options = $options; } diff --git a/src/Symfony/Component/Form/ChoiceList/LazyChoiceList.php b/src/Symfony/Component/Form/ChoiceList/LazyChoiceList.php index 227c6e8f50e3..e8e37b359566 100644 --- a/src/Symfony/Component/Form/ChoiceList/LazyChoiceList.php +++ b/src/Symfony/Component/Form/ChoiceList/LazyChoiceList.php @@ -83,7 +83,7 @@ public function getChoices() // We can safely invoke the {@link ChoiceLoaderInterface} assuming it has the list // in cache when the lazy list is already loaded if ($this->loadedList !== $this->loader->loadChoiceList($this->value)) { - @trigger_error(sprintf('Caching the choice list in %s is deprecated since 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class)); + @trigger_error(sprintf('Caching the choice list in %s is deprecated since 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class), E_USER_DEPRECATED); } return $this->loadedList->getChoices(); @@ -106,7 +106,7 @@ public function getValues() if ($this->loaded) { // Check whether the loader has the same cache if ($this->loadedList !== $this->loader->loadChoiceList($this->value)) { - @trigger_error(sprintf('Caching the choice list in %s is deprecated since 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class)); + @trigger_error(sprintf('Caching the choice list in %s is deprecated since 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class), E_USER_DEPRECATED); } return $this->loadedList->getValues(); @@ -129,7 +129,7 @@ public function getStructuredValues() if ($this->loaded) { // Check whether the loader has the same cache if ($this->loadedList !== $this->loader->loadChoiceList($this->value)) { - @trigger_error(sprintf('Caching the choice list in %s is deprecated since 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class)); + @trigger_error(sprintf('Caching the choice list in %s is deprecated since 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class), E_USER_DEPRECATED); } return $this->loadedList->getStructuredValues(); @@ -152,7 +152,7 @@ public function getOriginalKeys() if ($this->loaded) { // Check whether the loader has the same cache if ($this->loadedList !== $this->loader->loadChoiceList($this->value)) { - @trigger_error(sprintf('Caching the choice list in %s is deprecated since 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class)); + @trigger_error(sprintf('Caching the choice list in %s is deprecated since 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class), E_USER_DEPRECATED); } return $this->loadedList->getOriginalKeys(); @@ -175,7 +175,7 @@ public function getChoicesForValues(array $values) if ($this->loaded) { // Check whether the loader has the same cache if ($this->loadedList !== $this->loader->loadChoiceList($this->value)) { - @trigger_error(sprintf('Caching the choice list in %s is deprecated since 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class)); + @trigger_error(sprintf('Caching the choice list in %s is deprecated since 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class), E_USER_DEPRECATED); } return $this->loadedList->getChoicesForValues($values); @@ -192,7 +192,7 @@ public function getValuesForChoices(array $choices) if ($this->loaded) { // Check whether the loader has the same cache if ($this->loadedList !== $this->loader->loadChoiceList($this->value)) { - @trigger_error(sprintf('Caching the choice list in %s is deprecated since 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class)); + @trigger_error(sprintf('Caching the choice list in %s is deprecated since 3.1 and will not happen in 4.0. Cache the list in the %s instead.', __CLASS__, ChoiceLoaderInterface::class), E_USER_DEPRECATED); } return $this->loadedList->getValuesForChoices($choices); diff --git a/src/Symfony/Component/Form/Extension/DataCollector/DataCollectorExtension.php b/src/Symfony/Component/Form/Extension/DataCollector/DataCollectorExtension.php index 70b347fa9af3..2f2e51c0901c 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/DataCollectorExtension.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/DataCollectorExtension.php @@ -17,8 +17,6 @@ /** * Extension for collecting data of the forms on a page. * - * @since 2.4 - * * @author Robert Schönthal * @author Bernhard Schussek */ diff --git a/src/Symfony/Component/Form/Extension/DataCollector/EventListener/DataCollectorListener.php b/src/Symfony/Component/Form/Extension/DataCollector/EventListener/DataCollectorListener.php index 8fd70f09b179..0bd33d36a5d1 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/EventListener/DataCollectorListener.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/EventListener/DataCollectorListener.php @@ -20,8 +20,6 @@ * Listener that invokes a data collector for the {@link FormEvents::POST_SET_DATA} * and {@link FormEvents::POST_SUBMIT} events. * - * @since 2.4 - * * @author Bernhard Schussek */ class DataCollectorListener implements EventSubscriberInterface diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php index d1375df8894b..a408dde2306c 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php @@ -20,8 +20,6 @@ /** * Data collector for {@link FormInterface} instances. * - * @since 2.4 - * * @author Robert Schönthal * @author Bernhard Schussek */ diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollectorInterface.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollectorInterface.php index a57693a99524..ee004a09f30b 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollectorInterface.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollectorInterface.php @@ -18,8 +18,6 @@ /** * Collects and structures information about forms. * - * @since 2.4 - * * @author Bernhard Schussek */ interface FormDataCollectorInterface extends DataCollectorInterface diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php index 9e88a3eb3b30..ec89c883643c 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php @@ -19,8 +19,6 @@ /** * Default implementation of {@link FormDataExtractorInterface}. * - * @since 2.4 - * * @author Bernhard Schussek */ class FormDataExtractor implements FormDataExtractorInterface diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractorInterface.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractorInterface.php index a5bb7d0ef30a..e0bc6dc0cacc 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractorInterface.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractorInterface.php @@ -17,8 +17,6 @@ /** * Extracts arrays of information out of forms. * - * @since 2.4 - * * @author Bernhard Schussek */ interface FormDataExtractorInterface diff --git a/src/Symfony/Component/Form/Extension/DataCollector/Proxy/ResolvedTypeDataCollectorProxy.php b/src/Symfony/Component/Form/Extension/DataCollector/Proxy/ResolvedTypeDataCollectorProxy.php index ede4873be384..fc3ba00ebd36 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/Proxy/ResolvedTypeDataCollectorProxy.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/Proxy/ResolvedTypeDataCollectorProxy.php @@ -21,8 +21,6 @@ /** * Proxy that invokes a data collector when creating a form and its view. * - * @since 2.4 - * * @author Bernhard Schussek */ class ResolvedTypeDataCollectorProxy implements ResolvedFormTypeInterface diff --git a/src/Symfony/Component/Form/Extension/DataCollector/Proxy/ResolvedTypeFactoryDataCollectorProxy.php b/src/Symfony/Component/Form/Extension/DataCollector/Proxy/ResolvedTypeFactoryDataCollectorProxy.php index f5f4ed2e2095..5051d0bb6390 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/Proxy/ResolvedTypeFactoryDataCollectorProxy.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/Proxy/ResolvedTypeFactoryDataCollectorProxy.php @@ -20,8 +20,6 @@ * Proxy that wraps resolved types into {@link ResolvedTypeDataCollectorProxy} * instances. * - * @since 2.4 - * * @author Bernhard Schussek */ class ResolvedTypeFactoryDataCollectorProxy implements ResolvedFormTypeFactoryInterface diff --git a/src/Symfony/Component/Form/Extension/DataCollector/Type/DataCollectorTypeExtension.php b/src/Symfony/Component/Form/Extension/DataCollector/Type/DataCollectorTypeExtension.php index e12403142296..966cb8503d52 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/Type/DataCollectorTypeExtension.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/Type/DataCollectorTypeExtension.php @@ -19,8 +19,6 @@ /** * Type extension for collecting data of a form with this type. * - * @since 2.4 - * * @author Robert Schönthal * @author Bernhard Schussek */ diff --git a/src/Symfony/Component/Form/FormErrorIterator.php b/src/Symfony/Component/Form/FormErrorIterator.php index 113de435d5ed..41a1297d30f5 100644 --- a/src/Symfony/Component/Form/FormErrorIterator.php +++ b/src/Symfony/Component/Form/FormErrorIterator.php @@ -27,8 +27,6 @@ * flatten the recursive structure into a flat list of errors. * * @author Bernhard Schussek - * - * @since 2.5 */ class FormErrorIterator implements \RecursiveIterator, \SeekableIterator, \ArrayAccess, \Countable { diff --git a/src/Symfony/Component/Form/FormInterface.php b/src/Symfony/Component/Form/FormInterface.php index 13b03f5118c1..9de802847ee8 100644 --- a/src/Symfony/Component/Form/FormInterface.php +++ b/src/Symfony/Component/Form/FormInterface.php @@ -102,9 +102,6 @@ public function all(); * * @return FormErrorIterator An iterator over the {@link FormError} * instances that where added to this form - * - * @since 2.5 Since version 2.5 this method returns a - * {@link FormErrorIterator} instance instead of an array */ public function getErrors($deep = false, $flatten = true); diff --git a/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php b/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php index 090eb8bc4f5a..75ddba621278 100644 --- a/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php +++ b/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php @@ -62,8 +62,6 @@ public function setMaxRunningTime($maxRunningTime) } /** - * @since Method available since Release 2.3.0 - * * @return int */ public function getMaxRunningTime() diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php index dc280e25dd92..8e337268964d 100644 --- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -47,7 +47,7 @@ protected function tearDown() parent::tearDown(); } - protected function assertXpathNodeValue(\DomElement $element, $expression, $nodeValue) + protected function assertXpathNodeValue(\DOMElement $element, $expression, $nodeValue) { $xpath = new \DOMXPath($element->ownerDocument); $nodeList = $xpath->evaluate($expression); diff --git a/src/Symfony/Component/Form/Tests/ButtonBuilderTest.php b/src/Symfony/Component/Form/Tests/ButtonBuilderTest.php new file mode 100644 index 000000000000..dc5d38b74486 --- /dev/null +++ b/src/Symfony/Component/Form/Tests/ButtonBuilderTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\ButtonBuilder; + +/** + * @author Alexander Cheprasov + */ +class ButtonBuilderTest extends \PHPUnit_Framework_TestCase +{ + public function getValidNames() + { + return array( + array('reset'), + array('submit'), + array('foo'), + array('0'), + array(0), + array('button[]'), + ); + } + + /** + * @dataProvider getValidNames + */ + public function testValidNames($name) + { + $this->assertInstanceOf('\Symfony\Component\Form\ButtonBuilder', new ButtonBuilder($name)); + } + + public function getInvalidNames() + { + return array( + array(''), + array(false), + array(null), + ); + } + + /** + * @dataProvider getInvalidNames + */ + public function testInvalidNames($name) + { + $this->setExpectedException( + '\Symfony\Component\Form\Exception\InvalidArgumentException', + 'Buttons cannot have empty names.' + ); + new ButtonBuilder($name); + } +} diff --git a/src/Symfony/Component/Form/Util/OrderedHashMap.php b/src/Symfony/Component/Form/Util/OrderedHashMap.php index 3aaa7d0451f1..78687032feac 100644 --- a/src/Symfony/Component/Form/Util/OrderedHashMap.php +++ b/src/Symfony/Component/Form/Util/OrderedHashMap.php @@ -63,8 +63,6 @@ * } * * @author Bernhard Schussek - * - * @since 2.2.6 */ class OrderedHashMap implements \ArrayAccess, \IteratorAggregate, \Countable { @@ -93,8 +91,6 @@ class OrderedHashMap implements \ArrayAccess, \IteratorAggregate, \Countable * Creates a new map. * * @param array $elements The elements to insert initially - * - * @since 2.2.6 */ public function __construct(array $elements = array()) { @@ -104,8 +100,6 @@ public function __construct(array $elements = array()) /** * {@inheritdoc} - * - * @since 2.2.6 */ public function offsetExists($key) { @@ -114,8 +108,6 @@ public function offsetExists($key) /** * {@inheritdoc} - * - * @since 2.2.6 */ public function offsetGet($key) { @@ -128,8 +120,6 @@ public function offsetGet($key) /** * {@inheritdoc} - * - * @since 2.2.6 */ public function offsetSet($key, $value) { @@ -140,7 +130,7 @@ public function offsetSet($key, $value) ? 0 // Imitate PHP's behavior of generating a key that equals // the highest existing integer key + 1 - : max($this->orderedKeys) + 1; + : 1 + (int) max($this->orderedKeys); } $this->orderedKeys[] = $key; @@ -151,8 +141,6 @@ public function offsetSet($key, $value) /** * {@inheritdoc} - * - * @since 2.2.6 */ public function offsetUnset($key) { @@ -170,8 +158,6 @@ public function offsetUnset($key) /** * {@inheritdoc} - * - * @since 2.2.6 */ public function getIterator() { @@ -180,8 +166,6 @@ public function getIterator() /** * {@inheritdoc} - * - * @since 2.2.6 */ public function count() { diff --git a/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php b/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php index f60662681489..32f591c86069 100644 --- a/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php +++ b/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php @@ -17,8 +17,6 @@ * This class is internal and should not be used. * * @author Bernhard Schussek - * - * @since 2.2.6 */ class OrderedHashMapIterator implements \Iterator { @@ -69,8 +67,6 @@ class OrderedHashMapIterator implements \Iterator * This array is managed by the corresponding * {@link OrderedHashMap} instance to support * recognizing the deletion of elements. - * - * @since 2.2.6 */ public function __construct(array &$elements, array &$orderedKeys, array &$managedCursors) { @@ -85,8 +81,6 @@ public function __construct(array &$elements, array &$orderedKeys, array &$manag /** * Removes the iterator's cursors from the managed cursors of the * corresponding {@link OrderedHashMap} instance. - * - * @since 2.2.6 */ public function __destruct() { @@ -96,9 +90,7 @@ public function __destruct() } /** - *{@inheritdoc} - * - * @since 2.2.6 + * {@inheritdoc} */ public function current() { @@ -107,8 +99,6 @@ public function current() /** * {@inheritdoc} - * - * @since 2.2.6 */ public function next() { @@ -124,9 +114,7 @@ public function next() } /** - *{@inheritdoc} - * - * @since 2.2.6 + * {@inheritdoc} */ public function key() { @@ -134,9 +122,7 @@ public function key() } /** - *{@inheritdoc} - * - * @since 2.2.6 + * {@inheritdoc} */ public function valid() { @@ -144,9 +130,7 @@ public function valid() } /** - *{@inheritdoc} - * - * @since 2.2.6 + * {@inheritdoc} */ public function rewind() { diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index b67f1b8c1ba3..e77c89bcde94 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1479,7 +1479,7 @@ public function isMethod($method) */ public function isMethodSafe() { - return in_array($this->getMethod(), array('GET', 'HEAD')); + return in_array($this->getMethod(), array('GET', 'HEAD', 'OPTIONS', 'TRACE')); } /** diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index c1b0a7a3fde8..d4584ed17c49 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -377,6 +377,12 @@ public function send() $this->sendHeaders(); $this->sendContent(); + if (function_exists('fastcgi_finish_request')) { + fastcgi_finish_request(); + } elseif ('cli' !== PHP_SAPI) { + static::closeOutputBuffers(0, true); + } + return $this; } diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php index f1df25d0a629..7efc1348c802 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php @@ -19,7 +19,7 @@ class MongoDbSessionHandler implements \SessionHandlerInterface { /** - * @var \Mongo + * @var \Mongo|\MongoClient|\MongoDB\Client */ private $mongo; @@ -61,15 +61,15 @@ class MongoDbSessionHandler implements \SessionHandlerInterface * If you use such an index, you can drop `gc_probability` to 0 since * no garbage-collection is required. * - * @param \Mongo|\MongoClient $mongo A MongoClient or Mongo instance - * @param array $options An associative array of field options + * @param \Mongo|\MongoClient|\MongoDB\Client $mongo A MongoDB\Client, MongoClient or Mongo instance + * @param array $options An associative array of field options * * @throws \InvalidArgumentException When MongoClient or Mongo instance not provided * @throws \InvalidArgumentException When "database" or "collection" not provided */ public function __construct($mongo, array $options) { - if (!($mongo instanceof \MongoClient || $mongo instanceof \Mongo)) { + if (!($mongo instanceof \MongoDB\Client || $mongo instanceof \MongoClient || $mongo instanceof \Mongo)) { throw new \InvalidArgumentException('MongoClient or Mongo instance required'); } @@ -108,7 +108,9 @@ public function close() */ public function destroy($sessionId) { - $this->getCollection()->remove(array( + $methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteOne' : 'remove'; + + $this->getCollection()->$methodName(array( $this->options['id_field'] => $sessionId, )); @@ -120,8 +122,10 @@ public function destroy($sessionId) */ public function gc($maxlifetime) { - $this->getCollection()->remove(array( - $this->options['expiry_field'] => array('$lt' => new \MongoDate()), + $methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteOne' : 'remove'; + + $this->getCollection()->$methodName(array( + $this->options['expiry_field'] => array('$lt' => $this->createDateTime()), )); return true; @@ -132,18 +136,28 @@ public function gc($maxlifetime) */ public function write($sessionId, $data) { - $expiry = new \MongoDate(time() + (int) ini_get('session.gc_maxlifetime')); + $expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime')); $fields = array( - $this->options['data_field'] => new \MongoBinData($data, \MongoBinData::BYTE_ARRAY), - $this->options['time_field'] => new \MongoDate(), + $this->options['time_field'] => $this->createDateTime(), $this->options['expiry_field'] => $expiry, ); - $this->getCollection()->update( + $options = array('upsert' => true); + + if ($this->mongo instanceof \MongoDB\Client) { + $fields[$this->options['data_field']] = new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY); + } else { + $fields[$this->options['data_field']] = new \MongoBinData($data, \MongoBinData::BYTE_ARRAY); + $options['multiple'] = false; + } + + $methodName = $this->mongo instanceof \MongoDB\Client ? 'updateOne' : 'update'; + + $this->getCollection()->$methodName( array($this->options['id_field'] => $sessionId), array('$set' => $fields), - array('upsert' => true, 'multiple' => false) + $options ); return true; @@ -156,10 +170,18 @@ public function read($sessionId) { $dbData = $this->getCollection()->findOne(array( $this->options['id_field'] => $sessionId, - $this->options['expiry_field'] => array('$gte' => new \MongoDate()), + $this->options['expiry_field'] => array('$gte' => $this->createDateTime()), )); - return null === $dbData ? '' : $dbData[$this->options['data_field']]->bin; + if (null === $dbData) { + return ''; + } + + if ($dbData[$this->options['data_field']] instanceof \MongoDB\BSON\Binary) { + return $dbData[$this->options['data_field']]->getData(); + } + + return $dbData[$this->options['data_field']]->bin; } /** @@ -179,10 +201,30 @@ private function getCollection() /** * Return a Mongo instance. * - * @return \Mongo + * @return \Mongo|\MongoClient|\MongoDB\Client */ protected function getMongo() { return $this->mongo; } + + /** + * Create a date object using the class appropriate for the current mongo connection. + * + * Return an instance of a MongoDate or \MongoDB\BSON\UTCDateTime + * + * @param int $seconds An integer representing UTC seconds since Jan 1 1970. Defaults to now. + */ + private function createDateTime($seconds = null) + { + if (null === $seconds) { + $seconds = time(); + } + + if ($this->mongo instanceof \MongoDB\Client) { + return new \MongoDB\BSON\UTCDateTime($seconds * 1000); + } + + return new \MongoDate($seconds); + } } diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 6739797e0f8b..b795ba24886c 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -1951,6 +1951,32 @@ public function getLongHostNames() array(str_repeat(':', 101)), ); } + + /** + * @dataProvider methodSafeProvider + */ + public function testMethodSafe($method, $safe) + { + $request = new Request(); + $request->setMethod($method); + $this->assertEquals($safe, $request->isMethodSafe()); + } + + public function methodSafeProvider() + { + 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', true), + array('TRACE', true), + array('CONNECT', false), + ); + } } class RequestContentProxy extends Request diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php index 3fef78b8ee85..eabc51e79fe9 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php @@ -15,7 +15,6 @@ /** * @author Markus Bachmann - * @requires extension mongo * @group time-sensitive */ class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase @@ -31,7 +30,15 @@ protected function setUp() { parent::setUp(); - $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient'; + if (!extension_loaded('mongo') && !extension_loaded('mongodb')) { + $this->markTestSkipped('The Mongo or MongoDB extension is required.'); + } + + if (phpversion('mongodb')) { + $mongoClass = 'MongoDB\Client'; + } else { + $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient'; + } $this->mongo = $this->getMockBuilder($mongoClass) ->disableOriginalConstructor() @@ -96,14 +103,28 @@ public function testRead() $this->assertArrayHasKey($this->options['expiry_field'], $criteria); $this->assertArrayHasKey('$gte', $criteria[$this->options['expiry_field']]); - $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']); - $this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout); - return array( + if (phpversion('mongodb')) { + $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$gte']); + $this->assertGreaterThanOrEqual(round(intval((string) $criteria[$this->options['expiry_field']]['$gte']) / 1000), $testTimeout); + } else { + $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']); + $this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout); + } + + $fields = array( $this->options['id_field'] => 'foo', - $this->options['data_field'] => new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY), - $this->options['id_field'] => new \MongoDate(), ); + + if (phpversion('mongodb')) { + $fields[$this->options['data_field']] = new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY); + $fields[$this->options['id_field']] = new \MongoDB\BSON\UTCDateTime(time() * 1000); + } else { + $fields[$this->options['data_field']] = new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY); + $fields[$this->options['id_field']] = new \MongoDate(); + } + + return $fields; })); $this->assertEquals('bar', $this->storage->read('foo')); @@ -120,11 +141,18 @@ public function testWrite() $data = array(); + $methodName = phpversion('mongodb') ? 'updateOne' : 'update'; + $collection->expects($this->once()) - ->method('update') + ->method($methodName) ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) { $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria); - $this->assertEquals(array('upsert' => true, 'multiple' => false), $options); + + if (phpversion('mongodb')) { + $this->assertEquals(array('upsert' => true), $options); + } else { + $this->assertEquals(array('upsert' => true, 'multiple' => false), $options); + } $data = $updateData['$set']; })); @@ -132,10 +160,17 @@ public function testWrite() $expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime'); $this->assertTrue($this->storage->write('foo', 'bar')); - $this->assertEquals('bar', $data[$this->options['data_field']]->bin); - $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); - $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]); - $this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec); + if (phpversion('mongodb')) { + $this->assertEquals('bar', $data[$this->options['data_field']]->getData()); + $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]); + $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]); + $this->assertGreaterThanOrEqual($expectedExpiry, round(intval((string) $data[$this->options['expiry_field']]) / 1000)); + } else { + $this->assertEquals('bar', $data[$this->options['data_field']]->bin); + $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); + $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]); + $this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec); + } } public function testWriteWhenUsingExpiresField() @@ -160,20 +195,33 @@ public function testWriteWhenUsingExpiresField() $data = array(); + $methodName = phpversion('mongodb') ? 'updateOne' : 'update'; + $collection->expects($this->once()) - ->method('update') + ->method($methodName) ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) { $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria); - $this->assertEquals(array('upsert' => true, 'multiple' => false), $options); + + if (phpversion('mongodb')) { + $this->assertEquals(array('upsert' => true), $options); + } else { + $this->assertEquals(array('upsert' => true, 'multiple' => false), $options); + } $data = $updateData['$set']; })); $this->assertTrue($this->storage->write('foo', 'bar')); - $this->assertEquals('bar', $data[$this->options['data_field']]->bin); - $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); - $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]); + if (phpversion('mongodb')) { + $this->assertEquals('bar', $data[$this->options['data_field']]->getData()); + $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]); + $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]); + } else { + $this->assertEquals('bar', $data[$this->options['data_field']]->bin); + $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); + $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]); + } } public function testReplaceSessionData() @@ -187,8 +235,10 @@ public function testReplaceSessionData() $data = array(); + $methodName = phpversion('mongodb') ? 'updateOne' : 'update'; + $collection->expects($this->exactly(2)) - ->method('update') + ->method($methodName) ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) { $data = $updateData; })); @@ -196,7 +246,11 @@ public function testReplaceSessionData() $this->storage->write('foo', 'bar'); $this->storage->write('foo', 'foobar'); - $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin); + if (phpversion('mongodb')) { + $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->getData()); + } else { + $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin); + } } public function testDestroy() @@ -208,8 +262,10 @@ public function testDestroy() ->with($this->options['database'], $this->options['collection']) ->will($this->returnValue($collection)); + $methodName = phpversion('mongodb') ? 'deleteOne' : 'remove'; + $collection->expects($this->once()) - ->method('remove') + ->method($methodName) ->with(array($this->options['id_field'] => 'foo')); $this->assertTrue($this->storage->destroy('foo')); @@ -224,11 +280,18 @@ public function testGc() ->with($this->options['database'], $this->options['collection']) ->will($this->returnValue($collection)); + $methodName = phpversion('mongodb') ? 'deleteOne' : 'remove'; + $collection->expects($this->once()) - ->method('remove') + ->method($methodName) ->will($this->returnCallback(function ($criteria) { - $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']); - $this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec); + if (phpversion('mongodb')) { + $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$lt']); + $this->assertGreaterThanOrEqual(time() - 1, round(intval((string) $criteria[$this->options['expiry_field']]['$lt']) / 1000)); + } else { + $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']); + $this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec); + } })); $this->assertTrue($this->storage->gc(1)); @@ -239,14 +302,23 @@ public function testGetConnection() $method = new \ReflectionMethod($this->storage, 'getMongo'); $method->setAccessible(true); - $mongoClass = (version_compare(phpversion('mongo'), '1.3.0', '<')) ? '\Mongo' : '\MongoClient'; + if (phpversion('mongodb')) { + $mongoClass = 'MongoDB\Client'; + } else { + $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient'; + } $this->assertInstanceOf($mongoClass, $method->invoke($this->storage)); } private function createMongoCollectionMock() { - $collection = $this->getMockBuilder('MongoCollection') + $collectionClass = 'MongoCollection'; + if (phpversion('mongodb')) { + $collectionClass = 'MongoDB\Collection'; + } + + $collection = $this->getMockBuilder($collectionClass) ->disableOriginalConstructor() ->getMock(); diff --git a/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmer.php b/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmer.php index 948b3ffd141c..dba35a639a46 100644 --- a/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmer.php +++ b/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmer.php @@ -20,7 +20,7 @@ abstract class CacheWarmer implements CacheWarmerInterface { protected function writeCacheFile($file, $content) { - $tmpFile = tempnam(dirname($file), basename($file)); + $tmpFile = @tempnam(dirname($file), basename($file)); if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) { @chmod($file, 0666 & ~umask()); diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/FragmentRendererPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/FragmentRendererPass.php index 4862b15fa9e4..1a41924f8027 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/FragmentRendererPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/FragmentRendererPass.php @@ -53,7 +53,12 @@ public function process(ContainerBuilder $container) $class = $container->getParameterBag()->resolveValue($def->getClass()); $interface = 'Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface'; + if (!is_subclass_of($class, $interface)) { + if (!class_exists($class, false)) { + throw new \InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + } + throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); } diff --git a/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php b/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php index 8292fa48a15b..0d4d26b6765c 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php +++ b/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php @@ -65,7 +65,7 @@ public function render($uri, Request $request, array $options = array()) { if (!$this->surrogate || !$this->surrogate->hasSurrogateCapability($request)) { if ($uri instanceof ControllerReference && $this->containsNonScalars($uri->attributes)) { - @trigger_error('Passing objects as part of URI attributes to the ESI and SSI rendering strategies is deprecated since version 3.1, and will be removed in 4.0. Use a different rendering strategy or pass scalar values.', E_USER_DEPRECATED); + @trigger_error('Passing non-scalar values as part of URI attributes to the ESI and SSI rendering strategies is deprecated since version 3.1, and will be removed in 4.0. Use a different rendering strategy or pass scalar values.', E_USER_DEPRECATED); } return $this->inlineStrategy->render($uri, $request, $options); diff --git a/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php b/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php index a6ab82ea28ef..a61b239c158b 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php +++ b/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php @@ -129,6 +129,8 @@ protected function createSubRequest($uri, Request $request) } $server['REMOTE_ADDR'] = '127.0.0.1'; + unset($server['HTTP_IF_MODIFIED_SINCE']); + unset($server['HTTP_IF_NONE_MATCH']); $subRequest = Request::create($uri, 'get', array(), $cookies, array(), $server); if ($request->headers->has('Surrogate-Capability')) { diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index 1c7684a91b78..2879018ccc2b 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -580,6 +580,9 @@ protected function lock(Request $request, Response $entry) */ protected function store(Request $request, Response $response) { + if (!$response->headers->has('Date')) { + $response->setDate(\DateTime::createFromFormat('U', time())); + } try { $this->store->write($request, $response); diff --git a/src/Symfony/Component/HttpKernel/HttpCache/Store.php b/src/Symfony/Component/HttpKernel/HttpCache/Store.php index 433b0ef29b4e..b57d4a774d5c 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/Store.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/Store.php @@ -38,7 +38,7 @@ class Store implements StoreInterface public function __construct($root) { $this->root = $root; - if (!is_dir($this->root) && !@mkdir($this->root, 0777, true) && !is_dir($this->root)) { + if (!file_exists($this->root) && !@mkdir($this->root, 0777, true) && !is_dir($this->root)) { throw new \RuntimeException(sprintf('Unable to create the store directory (%s).', $this->root)); } $this->keyCache = new \SplObjectStorage(); @@ -52,22 +52,15 @@ public function cleanup() { // unlock everything foreach ($this->locks as $lock) { - if (file_exists($lock)) { - @unlink($lock); - } + flock($lock, LOCK_UN); + fclose($lock); } - $error = error_get_last(); - if (1 === $error['type'] && false === headers_sent()) { - // send a 503 - header('HTTP/1.0 503 Service Unavailable'); - header('Retry-After: 10'); - echo '503 Service Unavailable'; - } + $this->locks = array(); } /** - * Locks the cache for a given Request. + * Tries to lock the cache for a given Request, without blocking. * * @param Request $request A Request instance * @@ -75,21 +68,24 @@ public function cleanup() */ public function lock(Request $request) { - $path = $this->getPath($this->getCacheKey($request).'.lck'); - if (!is_dir(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) { - return false; - } + $key = $this->getCacheKey($request); - $lock = @fopen($path, 'x'); - if (false !== $lock) { - fclose($lock); + if (!isset($this->locks[$key])) { + $path = $this->getPath($key); + if (!file_exists(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) { + return $path; + } + $h = fopen($path, 'cb'); + if (!flock($h, LOCK_EX | LOCK_NB)) { + fclose($h); - $this->locks[] = $path; + return $path; + } - return true; + $this->locks[$key] = $h; } - return !file_exists($path) ?: $path; + return true; } /** @@ -101,17 +97,37 @@ public function lock(Request $request) */ public function unlock(Request $request) { - $file = $this->getPath($this->getCacheKey($request).'.lck'); + $key = $this->getCacheKey($request); + + if (isset($this->locks[$key])) { + flock($this->locks[$key], LOCK_UN); + fclose($this->locks[$key]); + unset($this->locks[$key]); + + return true; + } - return is_file($file) ? @unlink($file) : false; + return false; } public function isLocked(Request $request) { - $path = $this->getPath($this->getCacheKey($request).'.lck'); - clearstatcache(true, $path); + $key = $this->getCacheKey($request); + + if (isset($this->locks[$key])) { + return true; // shortcut if lock held by this process + } + + if (!file_exists($path = $this->getPath($key))) { + return false; + } - return is_file($path); + $h = fopen($path, 'rb'); + flock($h, LOCK_EX | LOCK_NB, $wouldBlock); + flock($h, LOCK_UN); // release the lock we just acquired + fclose($h); + + return (bool) $wouldBlock; } /** @@ -144,7 +160,7 @@ public function lookup(Request $request) } list($req, $headers) = $match; - if (is_file($body = $this->getPath($headers['x-content-digest'][0]))) { + if (file_exists($body = $this->getPath($headers['x-content-digest'][0]))) { return $this->restoreResponse($headers, $body); } @@ -291,7 +307,7 @@ private function requestsMatch($vary, $env1, $env2) */ private function getMetadata($key) { - if (false === $entries = $this->load($key)) { + if (!$entries = $this->load($key)) { return array(); } @@ -307,7 +323,15 @@ private function getMetadata($key) */ public function purge($url) { - if (is_file($path = $this->getPath($this->getCacheKey(Request::create($url))))) { + $key = $this->getCacheKey(Request::create($url)); + + if (isset($this->locks[$key])) { + flock($this->locks[$key], LOCK_UN); + fclose($this->locks[$key]); + unset($this->locks[$key]); + } + + if (file_exists($path = $this->getPath($key))) { unlink($path); return true; @@ -327,7 +351,7 @@ private function load($key) { $path = $this->getPath($key); - return is_file($path) ? file_get_contents($path) : false; + return file_exists($path) ? file_get_contents($path) : false; } /** @@ -341,23 +365,36 @@ private function load($key) private function save($key, $data) { $path = $this->getPath($key); - if (!is_dir(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) { - return false; - } - $tmpFile = tempnam(dirname($path), basename($path)); - if (false === $fp = @fopen($tmpFile, 'wb')) { - return false; - } - @fwrite($fp, $data); - @fclose($fp); + if (isset($this->locks[$key])) { + $fp = $this->locks[$key]; + @ftruncate($fp, 0); + @fseek($fp, 0); + $len = @fwrite($fp, $data); + if (strlen($data) !== $len) { + @ftruncate($fp, 0); - if ($data != file_get_contents($tmpFile)) { - return false; - } + return false; + } + } else { + if (!file_exists(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) { + return false; + } - if (false === @rename($tmpFile, $path)) { - return false; + $tmpFile = tempnam(dirname($path), basename($path)); + if (false === $fp = @fopen($tmpFile, 'wb')) { + return false; + } + @fwrite($fp, $data); + @fclose($fp); + + if ($data != file_get_contents($tmpFile)) { + return false; + } + + if (false === @rename($tmpFile, $path)) { + return false; + } } @chmod($path, 0666 & ~umask()); diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 37e3e6ddfcb3..836d2c54c9bb 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.2'; - const VERSION_ID = 30102; + const VERSION = '3.1.3'; + const VERSION_ID = 30103; const MAJOR_VERSION = 3; const MINOR_VERSION = 1; - const RELEASE_VERSION = 2; + const RELEASE_VERSION = 3; const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '01/2017'; @@ -134,14 +134,6 @@ public function terminate(Request $request, Response $response) } if ($this->getHttpKernel() instanceof TerminableInterface) { - if (!$this->debug) { - if (function_exists('fastcgi_finish_request')) { - fastcgi_finish_request(); - } elseif ('cli' !== PHP_SAPI) { - Response::closeOutputBuffers(0, true); - } - } - $this->getHttpKernel()->terminate($request, $response); } } diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php index 332629734986..1fb9cc2037e5 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php @@ -28,6 +28,7 @@ public function testRenderFallbackToInlineStrategyIfEsiNotSupported() /** * @group legacy + * @requires function Symfony\Bridge\PhpUnit\ErrorAssert::assertDeprecationsAreTriggered */ public function testRenderFallbackWithObjectAttributesIsDeprecated() { diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php index 1f5adc2a61fe..b3ebe79cf3ef 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php @@ -226,6 +226,19 @@ public function testESIHeaderIsKeptInSubrequestWithTrustedHeaderDisabled() Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, $trustedHeaderName); } + + public function testHeadersPossiblyResultingIn304AreNotAssignedToSubrequest() + { + $expectedSubRequest = Request::create('/'); + if (Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP)) { + $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); + $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); + } + + $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest)); + $request = Request::create('/', 'GET', array(), array(), array(), array('HTTP_IF_MODIFIED_SINCE' => 'Fri, 01 Jan 2016 00:00:00 GMT', 'HTTP_IF_NONE_MATCH' => '*')); + $strategy->render('/', $request); + } } class Bar diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php index aa2a442459ae..0498f2194779 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php @@ -181,6 +181,31 @@ public function testRespondsWith304OnlyIfIfNoneMatchAndIfModifiedSinceBothMatch( $this->assertEquals(304, $this->response->getStatusCode()); } + public function testIncrementsMaxAgeWhenNoDateIsSpecifiedEventWhenUsingETag() + { + $this->setNextResponse( + 200, + array( + 'ETag' => '1234', + 'Cache-Control' => 'public, s-maxage=60', + ) + ); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + + sleep(2); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('fresh'); + $this->assertEquals(2, $this->response->headers->get('Age')); + } + public function testValidatesPrivateResponsesCachedOnTheClient() { $this->setNextResponse(200, array(), '', function ($request, $response) { diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTestCase.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTestCase.php index 1fcd2107519b..722e98760f11 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTestCase.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTestCase.php @@ -50,6 +50,9 @@ protected function setUp() protected function tearDown() { + if ($this->cache) { + $this->cache->getStore()->cleanup(); + } $this->kernel = null; $this->cache = null; $this->caches = null; diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php index 4198ce403134..bf4239beaea6 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php @@ -19,6 +19,10 @@ class StoreTest extends \PHPUnit_Framework_TestCase { protected $request; protected $response; + + /** + * @var Store + */ protected $store; protected function setUp() diff --git a/src/Symfony/Component/Intl/Util/IntlTestHelper.php b/src/Symfony/Component/Intl/Util/IntlTestHelper.php index cefb8db9919e..ecd5ed6b660d 100644 --- a/src/Symfony/Component/Intl/Util/IntlTestHelper.php +++ b/src/Symfony/Component/Intl/Util/IntlTestHelper.php @@ -31,7 +31,7 @@ class IntlTestHelper * * @param \PhpUnit_Framework_TestCase $testCase */ - public static function requireIntl(\PhpUnit_Framework_TestCase $testCase) + public static function requireIntl(\PHPUnit_Framework_TestCase $testCase) { // We only run tests if the version is *one specific version*. // This condition is satisfied if @@ -63,7 +63,7 @@ public static function requireIntl(\PhpUnit_Framework_TestCase $testCase) * * @param \PhpUnit_Framework_TestCase $testCase */ - public static function requireFullIntl(\PhpUnit_Framework_TestCase $testCase) + public static function requireFullIntl(\PHPUnit_Framework_TestCase $testCase) { // We only run tests if the intl extension is loaded... if (!Intl::isExtensionLoaded()) { @@ -92,7 +92,7 @@ public static function requireFullIntl(\PhpUnit_Framework_TestCase $testCase) * * @param \PhpUnit_Framework_TestCase $testCase */ - public static function require32Bit(\PhpUnit_Framework_TestCase $testCase) + public static function require32Bit(\PHPUnit_Framework_TestCase $testCase) { if (4 !== PHP_INT_SIZE) { $testCase->markTestSkipped('PHP 32 bit is required.'); @@ -104,7 +104,7 @@ public static function require32Bit(\PhpUnit_Framework_TestCase $testCase) * * @param \PhpUnit_Framework_TestCase $testCase */ - public static function require64Bit(\PhpUnit_Framework_TestCase $testCase) + public static function require64Bit(\PHPUnit_Framework_TestCase $testCase) { if (8 !== PHP_INT_SIZE) { $testCase->markTestSkipped('PHP 64 bit is required.'); diff --git a/src/Symfony/Component/Ldap/README.md b/src/Symfony/Component/Ldap/README.md index c2010d4b7518..b79d60b09579 100644 --- a/src/Symfony/Component/Ldap/README.md +++ b/src/Symfony/Component/Ldap/README.md @@ -14,7 +14,7 @@ previous version of the component will break with this version. Resources --------- - * [Documentation](https://symfony.com/doc/current/components/ldap/index.html) + * [Documentation](https://symfony.com/doc/current/components/ldap) * [Contributing](https://symfony.com/doc/current/contributing/index.html) * [Report issues](https://github.com/symfony/symfony/issues) and [send Pull Requests](https://github.com/symfony/symfony/pulls) diff --git a/src/Symfony/Component/Process/Pipes/WindowsPipes.php b/src/Symfony/Component/Process/Pipes/WindowsPipes.php index b97718b46686..7aa94b1ae642 100644 --- a/src/Symfony/Component/Process/Pipes/WindowsPipes.php +++ b/src/Symfony/Component/Process/Pipes/WindowsPipes.php @@ -52,27 +52,30 @@ public function __construct($input, $haveReadSupport) Process::STDERR => Process::ERR, ); $tmpDir = sys_get_temp_dir(); - if (!@fopen($file = $tmpDir.'\\sf_proc_00.check', 'wb')) { - throw new RuntimeException('A temporary file could not be opened to write the process output to, verify that your TEMP environment variable is writable'); - } - @unlink($file); + $error = 'unknown reason'; + set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); for ($i = 0;; ++$i) { foreach ($pipes as $pipe => $name) { $file = sprintf('%s\\sf_proc_%02X.%s', $tmpDir, $i, $name); - if (file_exists($file) && !@unlink($file)) { + if (file_exists($file) && !unlink($file)) { continue 2; } - $h = @fopen($file, 'xb'); + $h = fopen($file, 'xb'); + if (!$h && false === strpos($error, 'File exists')) { + restore_error_handler(); + throw new RuntimeException(sprintf('A temporary file could not be opened to write the process output: %s', $error)); + } if (!$h || !$this->fileHandles[$pipe] = fopen($file, 'rb')) { continue 2; } if (isset($this->files[$pipe])) { - @unlink($this->files[$pipe]); + unlink($this->files[$pipe]); } $this->files[$pipe] = $file; } break; } + restore_error_handler(); } parent::__construct($input); diff --git a/src/Symfony/Component/Process/Tests/ProcessTest.php b/src/Symfony/Component/Process/Tests/ProcessTest.php index cb49f6990fc9..317b5ef69f0f 100644 --- a/src/Symfony/Component/Process/Tests/ProcessTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessTest.php @@ -1384,7 +1384,7 @@ private function getProcess($commandline, $cwd = null, array $env = null, $input } catch (RuntimeException $e) { $this->assertSame('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.', $e->getMessage()); if ($enhance) { - $process->setEnhanceSigChildCompatibility(true); + $process->setEnhanceSigchildCompatibility(true); } else { self::$notEnhancedSigchild = true; } diff --git a/src/Symfony/Component/PropertyInfo/composer.json b/src/Symfony/Component/PropertyInfo/composer.json index dfc345fc5341..fdb1b4d3685e 100644 --- a/src/Symfony/Component/PropertyInfo/composer.json +++ b/src/Symfony/Component/PropertyInfo/composer.json @@ -32,7 +32,8 @@ "doctrine/annotations": "~1.0" }, "conflict": { - "phpdocumentor/reflection-docblock": "<3.0" + "phpdocumentor/reflection-docblock": "<3.0", + "phpdocumentor/type-resolver": "<0.2.0" }, "suggest": { "psr/cache-implementation": "To cache results", diff --git a/src/Symfony/Component/Security/Core/Authorization/DebugAccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/DebugAccessDecisionManager.php index 1a04bc1f6072..aa15443dd037 100644 --- a/src/Symfony/Component/Security/Core/Authorization/DebugAccessDecisionManager.php +++ b/src/Symfony/Component/Security/Core/Authorization/DebugAccessDecisionManager.php @@ -62,7 +62,7 @@ public function decide(TokenInterface $token, array $attributes, $object = null) */ public function setVoters(array $voters) { - if (!$this->manager instanceof AccessDecisionManager) { + if (!method_exists($this->manager, 'setVoters')) { return; } diff --git a/src/Symfony/Component/Security/Csrf/CsrfTokenManagerInterface.php b/src/Symfony/Component/Security/Csrf/CsrfTokenManagerInterface.php index bccabe6c9c0f..5936b6440e4d 100644 --- a/src/Symfony/Component/Security/Csrf/CsrfTokenManagerInterface.php +++ b/src/Symfony/Component/Security/Csrf/CsrfTokenManagerInterface.php @@ -14,8 +14,6 @@ /** * Manages CSRF tokens. * - * @since 2.4 - * * @author Bernhard Schussek */ interface CsrfTokenManagerInterface diff --git a/src/Symfony/Component/Security/Csrf/TokenGenerator/TokenGeneratorInterface.php b/src/Symfony/Component/Security/Csrf/TokenGenerator/TokenGeneratorInterface.php index 1405b84f0332..0ec2881774b9 100644 --- a/src/Symfony/Component/Security/Csrf/TokenGenerator/TokenGeneratorInterface.php +++ b/src/Symfony/Component/Security/Csrf/TokenGenerator/TokenGeneratorInterface.php @@ -14,8 +14,6 @@ /** * Generates CSRF tokens. * - * @since 2.4 - * * @author Bernhard Schussek */ interface TokenGeneratorInterface diff --git a/src/Symfony/Component/Security/Csrf/TokenGenerator/UriSafeTokenGenerator.php b/src/Symfony/Component/Security/Csrf/TokenGenerator/UriSafeTokenGenerator.php index f331803bd6c8..fb3f7e8339dc 100644 --- a/src/Symfony/Component/Security/Csrf/TokenGenerator/UriSafeTokenGenerator.php +++ b/src/Symfony/Component/Security/Csrf/TokenGenerator/UriSafeTokenGenerator.php @@ -14,8 +14,6 @@ /** * Generates CSRF tokens. * - * @since 2.4 - * * @author Bernhard Schussek */ class UriSafeTokenGenerator implements TokenGeneratorInterface diff --git a/src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php b/src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php index a237b55d0c77..5ce277411473 100644 --- a/src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php +++ b/src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php @@ -16,8 +16,6 @@ /** * Token storage that uses PHP's native session handling. * - * @since 2.4 - * * @author Bernhard Schussek */ class NativeSessionTokenStorage implements TokenStorageInterface diff --git a/src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php b/src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php index a6a6ea389426..37b33e6f2161 100644 --- a/src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php +++ b/src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php @@ -15,9 +15,7 @@ use Symfony\Component\Security\Csrf\Exception\TokenNotFoundException; /** - * Token storage that uses a Symfony2 Session object. - * - * @since 2.4 + * Token storage that uses a Symfony Session object. * * @author Bernhard Schussek */ diff --git a/src/Symfony/Component/Security/Csrf/TokenStorage/TokenStorageInterface.php b/src/Symfony/Component/Security/Csrf/TokenStorage/TokenStorageInterface.php index 5efe72f694d4..92386fbbda34 100644 --- a/src/Symfony/Component/Security/Csrf/TokenStorage/TokenStorageInterface.php +++ b/src/Symfony/Component/Security/Csrf/TokenStorage/TokenStorageInterface.php @@ -14,8 +14,6 @@ /** * Stores CSRF tokens. * - * @since 2.4 - * * @author Bernhard Schussek */ interface TokenStorageInterface diff --git a/src/Symfony/Component/Security/Guard/Authenticator/AbstractFormLoginAuthenticator.php b/src/Symfony/Component/Security/Guard/Authenticator/AbstractFormLoginAuthenticator.php index d10e486705a8..f99900b175ef 100644 --- a/src/Symfony/Component/Security/Guard/Authenticator/AbstractFormLoginAuthenticator.php +++ b/src/Symfony/Component/Security/Guard/Authenticator/AbstractFormLoginAuthenticator.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Guard\Authenticator; +use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\Security\Guard\AbstractGuardAuthenticator; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; @@ -45,7 +46,10 @@ abstract protected function getLoginUrl(); */ public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { - $request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception); + if ($request->getSession() instanceof SessionInterface) { + $request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception); + } + $url = $this->getLoginUrl(); return new RedirectResponse($url); @@ -65,12 +69,16 @@ public function onAuthenticationSuccess(Request $request, TokenInterface $token, @trigger_error(sprintf('The AbstractFormLoginAuthenticator::onAuthenticationSuccess() implementation was deprecated in Symfony 3.1 and will be removed in Symfony 4.0. You should implement this method yourself in %s and remove getDefaultSuccessRedirectUrl().', get_class($this)), E_USER_DEPRECATED); if (!method_exists($this, 'getDefaultSuccessRedirectUrl')) { - throw new \Exception(sprintf('You must implement onAuthenticationSuccess() or getDefaultSuccessRedirectURL() in %s.', get_class($this))); + throw new \Exception(sprintf('You must implement onAuthenticationSuccess() or getDefaultSuccessRedirectUrl() in %s.', get_class($this))); } - // if the user hits a secure page and start() was called, this was + $targetPath = null; + + // if the user hit a secure page and start() was called, this was // the URL they were on, and probably where you want to redirect to - $targetPath = $this->getTargetPath($request->getSession(), $providerKey); + if ($request->getSession() instanceof SessionInterface) { + $targetPath = $this->getTargetPath($request->getSession(), $providerKey); + } if (!$targetPath) { $targetPath = $this->getDefaultSuccessRedirectUrl(); diff --git a/src/Symfony/Component/Security/Guard/Tests/Authenticator/AbstractFormLoginAuthenticatorTest.php b/src/Symfony/Component/Security/Guard/Tests/Authenticator/AbstractFormLoginAuthenticatorTest.php deleted file mode 100644 index e86b5ad26488..000000000000 --- a/src/Symfony/Component/Security/Guard/Tests/Authenticator/AbstractFormLoginAuthenticatorTest.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Guard\Tests\Authenticator; - -use Symfony\Component\HttpFoundation\RedirectResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Security\Core\User\UserProviderInterface; -use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator; - -class AbstractFormLoginAuthenticatorTest extends \PHPUnit_Framework_TestCase -{ - /** - * @group legacy - */ - public function testLegacyWithLoginUrl() - { - $request = new Request(); - $request->setSession($this->getMock('Symfony\Component\HttpFoundation\Session\Session')); - - $authenticator = new LegacyFormLoginAuthenticator(); - /** @var RedirectResponse $actualResponse */ - $actualResponse = $authenticator->onAuthenticationSuccess( - $request, - $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'), - 'provider_key' - ); - - $this->assertEquals('/default_url', $actualResponse->getTargetUrl()); - } -} - -class LegacyFormLoginAuthenticator extends AbstractFormLoginAuthenticator -{ - protected function getDefaultSuccessRedirectUrl() - { - return '/default_url'; - } - - protected function getLoginUrl() - { - } - - public function getCredentials(Request $request) - { - } - - public function getUser($credentials, UserProviderInterface $userProvider) - { - } - - public function checkCredentials($credentials, UserInterface $user) - { - } -} diff --git a/src/Symfony/Component/Security/Guard/Tests/Authenticator/FormLoginAuthenticatorTest.php b/src/Symfony/Component/Security/Guard/Tests/Authenticator/FormLoginAuthenticatorTest.php new file mode 100644 index 000000000000..e35564b0a12c --- /dev/null +++ b/src/Symfony/Component/Security/Guard/Tests/Authenticator/FormLoginAuthenticatorTest.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Guard\Tests\Authenticator; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\User\UserProviderInterface; +use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator; + +/** + * @author Jean Pasdeloup + */ +class FormLoginAuthenticatorTest extends \PHPUnit_Framework_TestCase +{ + private $requestWithoutSession; + private $requestWithSession; + private $authenticator; + + const LOGIN_URL = 'http://login'; + const DEFAULT_SUCCESS_URL = 'http://defaultsuccess'; + const CUSTOM_SUCCESS_URL = 'http://customsuccess'; + + public function testAuthenticationFailureWithoutSession() + { + $failureResponse = $this->authenticator->onAuthenticationFailure($this->requestWithoutSession, new AuthenticationException()); + + $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\RedirectResponse', $failureResponse); + $this->assertEquals(self::LOGIN_URL, $failureResponse->getTargetUrl()); + } + + public function testAuthenticationFailureWithSession() + { + $this->requestWithSession->getSession() + ->expects($this->once()) + ->method('set'); + + $failureResponse = $this->authenticator->onAuthenticationFailure($this->requestWithSession, new AuthenticationException()); + + $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\RedirectResponse', $failureResponse); + $this->assertEquals(self::LOGIN_URL, $failureResponse->getTargetUrl()); + } + + /** + * @group legacy + */ + public function testAuthenticationSuccessWithoutSession() + { + $token = $this->getMockBuilder('Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface') + ->disableOriginalConstructor() + ->getMock(); + + $redirectResponse = $this->authenticator->onAuthenticationSuccess($this->requestWithoutSession, $token, 'providerkey'); + + $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\RedirectResponse', $redirectResponse); + $this->assertEquals(self::DEFAULT_SUCCESS_URL, $redirectResponse->getTargetUrl()); + } + + /** + * @group legacy + */ + public function testAuthenticationSuccessWithSessionButEmpty() + { + $token = $this->getMockBuilder('Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface') + ->disableOriginalConstructor() + ->getMock(); + $this->requestWithSession->getSession() + ->expects($this->once()) + ->method('get') + ->will($this->returnValue(null)); + + $redirectResponse = $this->authenticator->onAuthenticationSuccess($this->requestWithSession, $token, 'providerkey'); + + $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\RedirectResponse', $redirectResponse); + $this->assertEquals(self::DEFAULT_SUCCESS_URL, $redirectResponse->getTargetUrl()); + } + + /** + * @group legacy + */ + public function testAuthenticationSuccessWithSessionAndTarget() + { + $token = $this->getMockBuilder('Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface') + ->disableOriginalConstructor() + ->getMock(); + $this->requestWithSession->getSession() + ->expects($this->once()) + ->method('get') + ->will($this->returnValue(self::CUSTOM_SUCCESS_URL)); + + $redirectResponse = $this->authenticator->onAuthenticationSuccess($this->requestWithSession, $token, 'providerkey'); + + $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\RedirectResponse', $redirectResponse); + $this->assertEquals(self::CUSTOM_SUCCESS_URL, $redirectResponse->getTargetUrl()); + } + + public function testRememberMe() + { + $doSupport = $this->authenticator->supportsRememberMe(); + + $this->assertTrue($doSupport); + } + + public function testStartWithoutSession() + { + $failureResponse = $this->authenticator->start($this->requestWithoutSession, new AuthenticationException()); + + $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\RedirectResponse', $failureResponse); + $this->assertEquals(self::LOGIN_URL, $failureResponse->getTargetUrl()); + } + + public function testStartWithSession() + { + $failureResponse = $this->authenticator->start($this->requestWithSession, new AuthenticationException()); + + $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\RedirectResponse', $failureResponse); + $this->assertEquals(self::LOGIN_URL, $failureResponse->getTargetUrl()); + } + + protected function setUp() + { + $this->requestWithoutSession = new Request(array(), array(), array(), array(), array(), array()); + $this->requestWithSession = new Request(array(), array(), array(), array(), array(), array()); + + $session = $this->getMockBuilder('Symfony\\Component\\HttpFoundation\\Session\\SessionInterface') + ->disableOriginalConstructor() + ->getMock(); + $this->requestWithSession->setSession($session); + + $this->authenticator = new TestFormLoginAuthenticator(); + $this->authenticator + ->setLoginUrl(self::LOGIN_URL) + ->setDefaultSuccessRedirectUrl(self::DEFAULT_SUCCESS_URL) + ; + } + + protected function tearDown() + { + $this->request = null; + $this->requestWithSession = null; + } +} + +class TestFormLoginAuthenticator extends AbstractFormLoginAuthenticator +{ + private $loginUrl; + private $defaultSuccessRedirectUrl; + + /** + * @param mixed $defaultSuccessRedirectUrl + * + * @return TestFormLoginAuthenticator + */ + public function setDefaultSuccessRedirectUrl($defaultSuccessRedirectUrl) + { + $this->defaultSuccessRedirectUrl = $defaultSuccessRedirectUrl; + + return $this; + } + + /** + * @param mixed $loginUrl + * + * @return TestFormLoginAuthenticator + */ + public function setLoginUrl($loginUrl) + { + $this->loginUrl = $loginUrl; + + return $this; + } + + /** + * {@inheritdoc} + */ + protected function getLoginUrl() + { + return $this->loginUrl; + } + + /** + * {@inheritdoc} + */ + protected function getDefaultSuccessRedirectUrl() + { + return $this->defaultSuccessRedirectUrl; + } + + /** + * {@inheritdoc} + */ + public function getCredentials(Request $request) + { + return 'credentials'; + } + + /** + * {@inheritdoc} + */ + public function getUser($credentials, UserProviderInterface $userProvider) + { + return $userProvider->loadUserByUsername($credentials); + } + + /** + * {@inheritdoc} + */ + public function checkCredentials($credentials, UserInterface $user) + { + return true; + } +} diff --git a/src/Symfony/Component/Security/Http/Authentication/AuthenticationUtils.php b/src/Symfony/Component/Security/Http/Authentication/AuthenticationUtils.php index 4d5c71aa5c48..c6397e8ca1e3 100644 --- a/src/Symfony/Component/Security/Http/Authentication/AuthenticationUtils.php +++ b/src/Symfony/Component/Security/Http/Authentication/AuthenticationUtils.php @@ -65,7 +65,13 @@ public function getLastAuthenticationError($clearSession = true) */ public function getLastUsername() { - $session = $this->getRequest()->getSession(); + $request = $this->getRequest(); + + if ($request->attributes->has(Security::LAST_USERNAME)) { + return $request->attributes->get(Security::LAST_USERNAME); + } + + $session = $request->getSession(); return null === $session ? '' : $session->get(Security::LAST_USERNAME); } diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/DigestAuthenticationListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/DigestAuthenticationListenerTest.php new file mode 100644 index 000000000000..80b2dc41343a --- /dev/null +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/DigestAuthenticationListenerTest.php @@ -0,0 +1,79 @@ +calculateServerDigest($username, $realm, $password, $nc, $nonce, $cnonce, $qop, 'GET', $uri); + + $digestData = + 'username="'.$username.'", realm="'.$realm.'", nonce="'.$nonce.'", '. + 'uri="'.$uri.'", cnonce="'.$cnonce.'", nc='.$nc.', qop="'.$qop.'", '. + 'response="'.$serverDigest.'"' + ; + + $request = new Request(array(), array(), array(), array(), array(), array('PHP_AUTH_DIGEST' => $digestData)); + + $entryPoint = new DigestAuthenticationEntryPoint($realm, $secret); + + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user->method('getPassword')->willReturn($password); + + $providerKey = 'TheProviderKey'; + + $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'); + $tokenStorage + ->expects($this->once()) + ->method('getToken') + ->will($this->returnValue(null)) + ; + $tokenStorage + ->expects($this->once()) + ->method('setToken') + ->with($this->equalTo(new UsernamePasswordToken($user, $password, $providerKey))) + ; + + $userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + $userProvider->method('loadUserByUsername')->willReturn($user); + + $listener = new DigestAuthenticationListener($tokenStorage, $userProvider, $providerKey, $entryPoint); + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + $event + ->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + + $listener->handle($event); + } + + private function calculateServerDigest($username, $realm, $password, $nc, $nonce, $cnonce, $qop, $method, $uri) + { + $response = md5( + md5($username.':'.$realm.':'.$password).':'.$nonce.':'.$nc.':'.$cnonce.':'.$qop.':'.md5($method.':'.$uri) + ); + + return sprintf('username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc=%s, qop="%s", response="%s"', + $username, $realm, $nonce, $uri, $cnonce, $nc, $qop, $response + ); + } +} diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 088b01f9f14c..2a8018dc0b1b 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -56,7 +56,7 @@ public function supportsNormalization($data, $format = null) public function normalize($object, $format = null, array $context = array()) { if (!isset($context['cache_key'])) { - $context['cache_key'] = $this->getCacheKey($context); + $context['cache_key'] = $this->getCacheKey($format, $context); } if ($this->isCircularReference($object, $context)) { @@ -169,7 +169,7 @@ public function supportsDenormalization($data, $type, $format = null) public function denormalize($data, $class, $format = null, array $context = array()) { if (!isset($context['cache_key'])) { - $context['cache_key'] = $this->getCacheKey($context); + $context['cache_key'] = $this->getCacheKey($format, $context); } $allowedAttributes = $this->getAllowedAttributes($class, $context, true); $normalizedData = $this->prepareForDenormalization($data); @@ -336,14 +336,15 @@ private function isMaxDepthReached($class, $attribute, array &$context) /** * Gets the cache key to use. * - * @param array $context + * @param string|null $format + * @param array $context * * @return bool|string */ - private function getCacheKey(array $context) + private function getCacheKey($format, array $context) { try { - return md5(serialize($context)); + return md5($format.serialize($context)); } catch (\Exception $exception) { // The context cannot be serialized, skip the cache return false; diff --git a/src/Symfony/Component/Translation/DataCollectorTranslator.php b/src/Symfony/Component/Translation/DataCollectorTranslator.php index d507cdea629b..244672335806 100644 --- a/src/Symfony/Component/Translation/DataCollectorTranslator.php +++ b/src/Symfony/Component/Translation/DataCollectorTranslator.php @@ -126,14 +126,14 @@ private function collectMessage($locale, $domain, $id, $translation, $parameters } elseif ($catalogue->has($id, $domain)) { $state = self::MESSAGE_EQUALS_FALLBACK; - $fallbackCatalogue = $catalogue->getFallBackCatalogue(); + $fallbackCatalogue = $catalogue->getFallbackCatalogue(); while ($fallbackCatalogue) { if ($fallbackCatalogue->defines($id, $domain)) { $locale = $fallbackCatalogue->getLocale(); break; } - $fallbackCatalogue = $fallbackCatalogue->getFallBackCatalogue(); + $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue(); } } else { $state = self::MESSAGE_MISSING; diff --git a/src/Symfony/Component/Validator/Constraint.php b/src/Symfony/Component/Validator/Constraint.php index 61def982a836..4d6ab3eaac1d 100644 --- a/src/Symfony/Component/Validator/Constraint.php +++ b/src/Symfony/Component/Validator/Constraint.php @@ -207,8 +207,6 @@ public function __set($option, $value) * @throws InvalidOptionsException If an invalid option name is given * * @internal This method should not be used or overwritten in userland code. - * - * @since 2.6 */ public function __get($option) { @@ -296,8 +294,6 @@ public function getTargets() * @internal This method may be replaced by an implementation of * {@link \Serializable} in the future. Please don't use or * overwrite it. - * - * @since 2.6 */ public function __sleep() { diff --git a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php index 024e6698a57e..dbaa5cd0b22d 100644 --- a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php +++ b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php @@ -43,7 +43,7 @@ public function validate($value, Constraint $constraint) // the DateTime constructor: // http://php.net/manual/en/datetime.formats.php if (is_string($comparedValue)) { - if ($value instanceof \DatetimeImmutable) { + if ($value instanceof \DateTimeImmutable) { // If $value is immutable, convert the compared value to a // DateTimeImmutable too $comparedValue = new \DatetimeImmutable($comparedValue); diff --git a/src/Symfony/Component/Validator/Constraints/Callback.php b/src/Symfony/Component/Validator/Constraints/Callback.php index 103145ad2d24..4a43665b0db8 100644 --- a/src/Symfony/Component/Validator/Constraints/Callback.php +++ b/src/Symfony/Component/Validator/Constraints/Callback.php @@ -23,8 +23,6 @@ class Callback extends Constraint { /** * @var string|callable - * - * @since 2.4 */ public $callback; diff --git a/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php b/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php index 310003c48097..36a9d8ad276d 100644 --- a/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php @@ -73,8 +73,10 @@ class CardSchemeValidator extends ConstraintValidator '/^6[0-9]{11,18}$/', ), // All MasterCard numbers start with the numbers 51 through 55. All have 16 digits. + // October 2016 MasterCard numbers can also start with 222100 through 272099. 'MASTERCARD' => array( '/^5[1-5][0-9]{14}$/', + '/^2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12})$/', ), // All Visa card numbers start with a 4. New cards have 16 digits. Old cards have 13. 'VISA' => array( diff --git a/src/Symfony/Component/Validator/Constraints/Composite.php b/src/Symfony/Component/Validator/Constraints/Composite.php index 90c98620f005..ab8466bcfcbc 100644 --- a/src/Symfony/Component/Validator/Constraints/Composite.php +++ b/src/Symfony/Component/Validator/Constraints/Composite.php @@ -24,8 +24,6 @@ * let {@link getCompositeOption()} return the name of the property which * contains the nested constraints. * - * @since 2.6 - * * @author Bernhard Schussek */ abstract class Composite extends Constraint diff --git a/src/Symfony/Component/Validator/Constraints/File.php b/src/Symfony/Component/Validator/Constraints/File.php index 24c7495fae5c..341fbaf44077 100644 --- a/src/Symfony/Component/Validator/Constraints/File.php +++ b/src/Symfony/Component/Validator/Constraints/File.php @@ -88,20 +88,22 @@ public function __get($option) private function normalizeBinaryFormat($maxSize) { + $sizeInt = (int) $maxSize; + if (ctype_digit((string) $maxSize)) { - $this->maxSize = (int) $maxSize; + $this->maxSize = $sizeInt; $this->binaryFormat = null === $this->binaryFormat ? false : $this->binaryFormat; } elseif (preg_match('/^\d++k$/i', $maxSize)) { - $this->maxSize = $maxSize * 1000; + $this->maxSize = $sizeInt * 1000; $this->binaryFormat = null === $this->binaryFormat ? false : $this->binaryFormat; } elseif (preg_match('/^\d++M$/i', $maxSize)) { - $this->maxSize = $maxSize * 1000000; + $this->maxSize = $sizeInt * 1000000; $this->binaryFormat = null === $this->binaryFormat ? false : $this->binaryFormat; } elseif (preg_match('/^\d++Ki$/i', $maxSize)) { - $this->maxSize = $maxSize << 10; + $this->maxSize = $sizeInt << 10; $this->binaryFormat = null === $this->binaryFormat ? true : $this->binaryFormat; } elseif (preg_match('/^\d++Mi$/i', $maxSize)) { - $this->maxSize = $maxSize << 20; + $this->maxSize = $sizeInt << 20; $this->binaryFormat = null === $this->binaryFormat ? true : $this->binaryFormat; } else { throw new ConstraintDefinitionException(sprintf('"%s" is not a valid maximum size', $this->maxSize)); diff --git a/src/Symfony/Component/Validator/Constraints/Traverse.php b/src/Symfony/Component/Validator/Constraints/Traverse.php index 5811ad770ad6..9d8bc126d188 100644 --- a/src/Symfony/Component/Validator/Constraints/Traverse.php +++ b/src/Symfony/Component/Validator/Constraints/Traverse.php @@ -17,8 +17,6 @@ /** * @Annotation * - * @since 2.5 - * * @author Bernhard Schussek */ class Traverse extends Constraint diff --git a/src/Symfony/Component/Validator/Constraints/UuidValidator.php b/src/Symfony/Component/Validator/Constraints/UuidValidator.php index fbc899cdb63d..d83391e81551 100644 --- a/src/Symfony/Component/Validator/Constraints/UuidValidator.php +++ b/src/Symfony/Component/Validator/Constraints/UuidValidator.php @@ -64,6 +64,10 @@ public function validate($value, Constraint $constraint) return; } + if (!$constraint instanceof Uuid) { + throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Uuid'); + } + if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString'))) { throw new UnexpectedTypeException($value, 'string'); } @@ -179,10 +183,7 @@ private function validateStrict($value, Uuid $constraint) if ('-' === $value{$i}) { if ($i !== $h) { $this->context->buildViolation($constraint->message) - ->setParameter( - '{{ value }}', - $this->formatValue($value) - ) + ->setParameter('{{ value }}', $this->formatValue($value)) ->setCode(Uuid::INVALID_HYPHEN_PLACEMENT_ERROR) ->addViolation(); diff --git a/src/Symfony/Component/Validator/Context/ExecutionContext.php b/src/Symfony/Component/Validator/Context/ExecutionContext.php index 9233436bea74..8ff93e4620ce 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContext.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContext.php @@ -26,8 +26,6 @@ /** * The context used and created by {@link ExecutionContextFactory}. * - * @since 2.5 - * * @author Bernhard Schussek * * @see ExecutionContextInterface diff --git a/src/Symfony/Component/Validator/Context/ExecutionContextFactory.php b/src/Symfony/Component/Validator/Context/ExecutionContextFactory.php index f4f01d926e0f..8182c41f8c1c 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContextFactory.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContextFactory.php @@ -17,8 +17,6 @@ /** * Creates new {@link ExecutionContext} instances. * - * @since 2.5 - * * @author Bernhard Schussek * * @internal You should not instantiate or use this class. Code against diff --git a/src/Symfony/Component/Validator/Context/ExecutionContextFactoryInterface.php b/src/Symfony/Component/Validator/Context/ExecutionContextFactoryInterface.php index e4af667d2da6..f3ab3dd68a74 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContextFactoryInterface.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContextFactoryInterface.php @@ -19,8 +19,6 @@ * You can use a custom factory if you want to customize the execution context * that is passed through the validation run. * - * @since 2.5 - * * @author Bernhard Schussek */ interface ExecutionContextFactoryInterface diff --git a/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php b/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php index 2079d2223373..1b1452582dff 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php @@ -57,8 +57,6 @@ * cannot store a context and expect that the methods still return the same * results later on. * - * @since 2.5 - * * @author Bernhard Schussek */ interface ExecutionContextInterface diff --git a/src/Symfony/Component/Validator/Exception/UnsupportedMetadataException.php b/src/Symfony/Component/Validator/Exception/UnsupportedMetadataException.php index 97cc5ffe5d92..aff569b95750 100644 --- a/src/Symfony/Component/Validator/Exception/UnsupportedMetadataException.php +++ b/src/Symfony/Component/Validator/Exception/UnsupportedMetadataException.php @@ -12,8 +12,6 @@ namespace Symfony\Component\Validator\Exception; /** - * @since 2.5 - * * @author Bernhard Schussek */ class UnsupportedMetadataException extends InvalidArgumentException diff --git a/src/Symfony/Component/Validator/Mapping/CascadingStrategy.php b/src/Symfony/Component/Validator/Mapping/CascadingStrategy.php index 2b97bdd4ca90..c78fb42ad46c 100644 --- a/src/Symfony/Component/Validator/Mapping/CascadingStrategy.php +++ b/src/Symfony/Component/Validator/Mapping/CascadingStrategy.php @@ -27,8 +27,6 @@ * Although the constants currently represent a boolean switch, they are * implemented as bit mask in order to allow future extensions. * - * @since 2.5 - * * @author Bernhard Schussek * * @see TraversalStrategy diff --git a/src/Symfony/Component/Validator/Mapping/ClassMetadataInterface.php b/src/Symfony/Component/Validator/Mapping/ClassMetadataInterface.php index f76726f7882c..e48e241ab8d5 100644 --- a/src/Symfony/Component/Validator/Mapping/ClassMetadataInterface.php +++ b/src/Symfony/Component/Validator/Mapping/ClassMetadataInterface.php @@ -21,8 +21,6 @@ * by a group sequence for that class and whether instances of that class * should be traversed or not. * - * @since 2.5 - * * @author Bernhard Schussek * * @see MetadataInterface diff --git a/src/Symfony/Component/Validator/Mapping/Factory/MetadataFactoryInterface.php b/src/Symfony/Component/Validator/Mapping/Factory/MetadataFactoryInterface.php index c3fbb69a0efe..a70b94b93aa3 100644 --- a/src/Symfony/Component/Validator/Mapping/Factory/MetadataFactoryInterface.php +++ b/src/Symfony/Component/Validator/Mapping/Factory/MetadataFactoryInterface.php @@ -11,13 +11,12 @@ namespace Symfony\Component\Validator\Mapping\Factory; -use Symfony\Component\Validator\Exception; +use Symfony\Component\Validator\Exception\NoSuchMetadataException; +use Symfony\Component\Validator\Mapping\MetadataInterface; /** * Returns {@link \Symfony\Component\Validator\Mapping\MetadataInterface} instances for values. * - * @since 2.5 - * * @author Bernhard Schussek */ interface MetadataFactoryInterface @@ -29,7 +28,7 @@ interface MetadataFactoryInterface * * @return MetadataInterface The metadata for the value * - * @throws Exception\NoSuchMetadataException If no metadata exists for the given value + * @throws NoSuchMetadataException If no metadata exists for the given value */ public function getMetadataFor($value); diff --git a/src/Symfony/Component/Validator/Mapping/GenericMetadata.php b/src/Symfony/Component/Validator/Mapping/GenericMetadata.php index e7f558f294d2..147c021b2159 100644 --- a/src/Symfony/Component/Validator/Mapping/GenericMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/GenericMetadata.php @@ -21,8 +21,6 @@ * * This class supports serialization and cloning. * - * @since 2.5 - * * @author Bernhard Schussek */ class GenericMetadata implements MetadataInterface diff --git a/src/Symfony/Component/Validator/Mapping/MetadataInterface.php b/src/Symfony/Component/Validator/Mapping/MetadataInterface.php index 450b83e9a3c3..514beb94926e 100644 --- a/src/Symfony/Component/Validator/Mapping/MetadataInterface.php +++ b/src/Symfony/Component/Validator/Mapping/MetadataInterface.php @@ -23,8 +23,6 @@ * against their class' metadata and whether traversable objects should be * traversed or not. * - * @since 2.5 - * * @author Bernhard Schussek * * @see CascadingStrategy diff --git a/src/Symfony/Component/Validator/Mapping/PropertyMetadata.php b/src/Symfony/Component/Validator/Mapping/PropertyMetadata.php index 9a2a4aac1428..ac71be828541 100644 --- a/src/Symfony/Component/Validator/Mapping/PropertyMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/PropertyMetadata.php @@ -39,7 +39,7 @@ class PropertyMetadata extends MemberMetadata public function __construct($class, $name) { if (!property_exists($class, $name)) { - throw new ValidatorException(sprintf('Property %s does not exist in class %s', $name, $class)); + throw new ValidatorException(sprintf('Property "%s" does not exist in class "%s"', $name, $class)); } parent::__construct($class, $name, $name); diff --git a/src/Symfony/Component/Validator/Mapping/PropertyMetadataInterface.php b/src/Symfony/Component/Validator/Mapping/PropertyMetadataInterface.php index dcb61cc43268..059b142eda3b 100644 --- a/src/Symfony/Component/Validator/Mapping/PropertyMetadataInterface.php +++ b/src/Symfony/Component/Validator/Mapping/PropertyMetadataInterface.php @@ -21,8 +21,6 @@ * should be validated against their class' metadata and whether traversable * objects should be traversed or not. * - * @since 2.5 - * * @author Bernhard Schussek * * @see MetadataInterface diff --git a/src/Symfony/Component/Validator/Mapping/TraversalStrategy.php b/src/Symfony/Component/Validator/Mapping/TraversalStrategy.php index 8a09be1fac5e..c22469b1f2c8 100644 --- a/src/Symfony/Component/Validator/Mapping/TraversalStrategy.php +++ b/src/Symfony/Component/Validator/Mapping/TraversalStrategy.php @@ -23,8 +23,6 @@ * * The traversal strategy is ignored for arrays. Arrays are always iterated. * - * @since 2.1 - * * @author Bernhard Schussek * * @see CascadingStrategy diff --git a/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php index bc9cdd38174b..3772a4d506ae 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php @@ -21,8 +21,6 @@ use Symfony\Component\Validator\Mapping\PropertyMetadata; /** - * @since 2.5.3 - * * @author Bernhard Schussek */ abstract class AbstractConstraintValidatorTest extends \PHPUnit_Framework_TestCase diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CardSchemeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CardSchemeValidatorTest.php index 3740e11e1878..ea865e81ec35 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CardSchemeValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CardSchemeValidatorTest.php @@ -96,6 +96,12 @@ public function getValidNumbers() array('MAESTRO', '6594371785970435599'), array('MASTERCARD', '5555555555554444'), array('MASTERCARD', '5105105105105100'), + array('MASTERCARD', '2221005555554444'), + array('MASTERCARD', '2230000000000000'), + array('MASTERCARD', '2300000000000000'), + array('MASTERCARD', '2699999999999999'), + array('MASTERCARD', '2709999999999999'), + array('MASTERCARD', '2720995105105100'), array('VISA', '4111111111111111'), array('VISA', '4012888888881881'), array('VISA', '4222222222222'), @@ -123,6 +129,8 @@ public function getInvalidNumbers() array('AMEX', '000000000000', CardScheme::INVALID_FORMAT_ERROR), // a lone number array('DINERS', '3056930', CardScheme::INVALID_FORMAT_ERROR), // only first part of the number array('DISCOVER', '1117', CardScheme::INVALID_FORMAT_ERROR), // only last 4 digits + array('MASTERCARD', '2721001234567890', CardScheme::INVALID_FORMAT_ERROR), // Not assigned yet + array('MASTERCARD', '2220991234567890', CardScheme::INVALID_FORMAT_ERROR), // Not assigned yet ); } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CompositeTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CompositeTest.php index eab15ed3bb84..002b1336aab3 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CompositeTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CompositeTest.php @@ -32,8 +32,6 @@ public function getDefaultOption() } /** - * @since 2.6 - * * @author Bernhard Schussek */ class CompositeTest extends \PHPUnit_Framework_TestCase diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UuidValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UuidValidatorTest.php index d4a6f087b576..25916122426d 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/UuidValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/UuidValidatorTest.php @@ -38,6 +38,16 @@ public function testEmptyStringIsValid() $this->assertNoViolation(); } + /** + * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException + */ + public function testExpectsUuidConstraintCompatibleType() + { + $constraint = $this->getMockForAbstractClass('Symfony\\Component\\Validator\\Constraint'); + + $this->validator->validate('216fff40-98d9-11e3-a5e2-0800200c9a66', $constraint); + } + /** * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException */ diff --git a/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php b/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php index 2b3b1e5bc300..a205d3a21004 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php @@ -23,8 +23,6 @@ use Symfony\Component\Validator\Tests\Fixtures\Reference; /** - * @since 2.5 - * * @author Bernhard Schussek */ abstract class AbstractValidatorTest extends \PHPUnit_Framework_TestCase diff --git a/src/Symfony/Component/Validator/Util/PropertyPath.php b/src/Symfony/Component/Validator/Util/PropertyPath.php index 3ef8a8b1dc5d..52546c5ea9df 100644 --- a/src/Symfony/Component/Validator/Util/PropertyPath.php +++ b/src/Symfony/Component/Validator/Util/PropertyPath.php @@ -16,8 +16,6 @@ * * For more extensive functionality, use Symfony's PropertyAccess component. * - * @since 2.5 - * * @author Bernhard Schussek */ class PropertyPath diff --git a/src/Symfony/Component/Validator/Validator/ContextualValidatorInterface.php b/src/Symfony/Component/Validator/Validator/ContextualValidatorInterface.php index 048d6c7545f8..7d7ebe73b081 100644 --- a/src/Symfony/Component/Validator/Validator/ContextualValidatorInterface.php +++ b/src/Symfony/Component/Validator/Validator/ContextualValidatorInterface.php @@ -17,8 +17,6 @@ /** * A validator in a specific execution context. * - * @since 2.5 - * * @author Bernhard Schussek */ interface ContextualValidatorInterface diff --git a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php index 41de61eabf84..95dec4e39bcd 100644 --- a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php +++ b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php @@ -33,8 +33,6 @@ /** * Recursive implementation of {@link ContextualValidatorInterface}. * - * @since 2.5 - * * @author Bernhard Schussek */ class RecursiveContextualValidator implements ContextualValidatorInterface diff --git a/src/Symfony/Component/Validator/Validator/RecursiveValidator.php b/src/Symfony/Component/Validator/Validator/RecursiveValidator.php index 50d54a07423f..c79d0a77d490 100644 --- a/src/Symfony/Component/Validator/Validator/RecursiveValidator.php +++ b/src/Symfony/Component/Validator/Validator/RecursiveValidator.php @@ -20,8 +20,6 @@ /** * Recursive implementation of {@link ValidatorInterface}. * - * @since 2.5 - * * @author Bernhard Schussek */ class RecursiveValidator implements ValidatorInterface diff --git a/src/Symfony/Component/Validator/Validator/ValidatorInterface.php b/src/Symfony/Component/Validator/Validator/ValidatorInterface.php index 1cc9c6dd5594..e9576f5eceb3 100644 --- a/src/Symfony/Component/Validator/Validator/ValidatorInterface.php +++ b/src/Symfony/Component/Validator/Validator/ValidatorInterface.php @@ -19,8 +19,6 @@ /** * Validates PHP values against constraints. * - * @since 2.5 - * * @author Bernhard Schussek */ interface ValidatorInterface extends MetadataFactoryInterface diff --git a/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilder.php b/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilder.php index d7d3877aed28..bf887a08ec2b 100644 --- a/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilder.php +++ b/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilder.php @@ -20,8 +20,6 @@ /** * Default implementation of {@link ConstraintViolationBuilderInterface}. * - * @since 2.5 - * * @author Bernhard Schussek * * @internal You should not instantiate or use this class. Code against @@ -199,7 +197,7 @@ public function addViolation() $this->message, $this->plural, $this->parameters, - $this->translationDomain# + $this->translationDomain ); } catch (\InvalidArgumentException $e) { $translatedMessage = $this->translator->trans( diff --git a/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilderInterface.php b/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilderInterface.php index e02d61b432d1..60a654f79368 100644 --- a/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilderInterface.php +++ b/src/Symfony/Component/Validator/Violation/ConstraintViolationBuilderInterface.php @@ -19,8 +19,6 @@ * Finally, call {@link addViolation()} to add the violation to the current * execution context. * - * @since 2.5 - * * @author Bernhard Schussek */ interface ConstraintViolationBuilderInterface diff --git a/src/Symfony/Component/VarDumper/Caster/AmqpCaster.php b/src/Symfony/Component/VarDumper/Caster/AmqpCaster.php index 98eede22d55d..6a6fc9297082 100644 --- a/src/Symfony/Component/VarDumper/Caster/AmqpCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/AmqpCaster.php @@ -131,7 +131,7 @@ public static function castEnvelope(\AMQPEnvelope $c, array $a, Stub $stub, $isN $prefix.'contentType' => $c->getContentType(), $prefix.'contentEncoding' => $c->getContentEncoding(), $prefix.'type' => $c->getType(), - $prefix.'timestamp' => $c->getTimestamp(), + $prefix.'timestamp' => $c->getTimeStamp(), $prefix.'priority' => $c->getPriority(), $prefix.'expiration' => $c->getExpiration(), $prefix.'userId' => $c->getUserId(), diff --git a/src/Symfony/Component/VarDumper/Caster/Caster.php b/src/Symfony/Component/VarDumper/Caster/Caster.php index 4312e59352f1..db052c8498a1 100644 --- a/src/Symfony/Component/VarDumper/Caster/Caster.php +++ b/src/Symfony/Component/VarDumper/Caster/Caster.php @@ -45,6 +45,8 @@ public static function castObject($obj, \ReflectionClass $reflector) { if ($reflector->hasMethod('__debugInfo')) { $a = $obj->__debugInfo(); + } elseif ($obj instanceof \Closure) { + $a = array(); } else { $a = (array) $obj; } @@ -52,7 +54,7 @@ public static function castObject($obj, \ReflectionClass $reflector) if ($a) { $p = array_keys($a); foreach ($p as $i => $k) { - if (!isset($k[0]) || ("\0" !== $k[0] && !$reflector->hasProperty($k))) { + if (isset($k[0]) && "\0" !== $k[0] && !$reflector->hasProperty($k)) { $p[$i] = self::PREFIX_DYNAMIC.$k; } elseif (isset($k[16]) && "\0" === $k[16] && 0 === strpos($k, "\0class@anonymous\0")) { $p[$i] = "\0".$reflector->getParentClass().'@anonymous'.strrchr($k, "\0"); diff --git a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php index a5a8773e859f..c90f3ea209bf 100644 --- a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php @@ -198,7 +198,7 @@ private static function filterExceptionArray($xClass, array $a, $xPrefix, $filte 'file' => $a[Caster::PREFIX_PROTECTED.'file'], 'line' => $a[Caster::PREFIX_PROTECTED.'line'], )); - $a[$xPrefix.'trace'] = new TraceStub($trace); + $a[$xPrefix.'trace'] = new TraceStub($trace, self::$traceArgs); } if (empty($a[$xPrefix.'previous'])) { unset($a[$xPrefix.'previous']); @@ -217,19 +217,24 @@ private static function extractSource(array $srcArray, $line, $srcContext) } $ltrim = 0; - while (' ' === $src[0][$ltrim] || "\t" === $src[0][$ltrim]) { - $i = $srcContext << 1; - while ($i > 0 && $src[0][$ltrim] === $src[$i][$ltrim]) { - --$i; - } - if ($i) { - break; + do { + $pad = null; + for ($i = $srcContext << 1; $i >= 0; --$i) { + if (isset($src[$i][$ltrim]) && "\r" !== ($c = $src[$i][$ltrim]) && "\n" !== $c) { + if (null === $pad) { + $pad = $c; + } + if ((' ' !== $c && "\t" !== $c) || $pad !== $c) { + break; + } + } } ++$ltrim; - } - if ($ltrim) { + } while (0 > $i && null !== $pad); + + if (--$ltrim) { foreach ($src as $i => $line) { - $src[$i] = substr($line, $ltrim); + $src[$i] = isset($line[$ltrim]) && "\r" !== $line[$ltrim] ? substr($line, $ltrim) : ltrim($line, " \t"); } } diff --git a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php index bbedf037ed2e..76c069dd06be 100644 --- a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php @@ -58,7 +58,7 @@ public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested) } $prefix = Caster::PREFIX_DYNAMIC; - unset($a['name'], $a[$prefix.'0'], $a[$prefix.'this'], $a[$prefix.'parameter'], $a[Caster::PREFIX_VIRTUAL.'extra']); + unset($a['name'], $a[$prefix.'this'], $a[$prefix.'parameter'], $a[Caster::PREFIX_VIRTUAL.'extra']); return $a; } diff --git a/src/Symfony/Component/VarDumper/Cloner/VarCloner.php b/src/Symfony/Component/VarDumper/Cloner/VarCloner.php index 1f4d1e3dcc6f..270c4dcf3d38 100644 --- a/src/Symfony/Component/VarDumper/Cloner/VarCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/VarCloner.php @@ -28,7 +28,7 @@ protected function doClone($var) $i = 0; // Current iteration position in $queue $len = 1; // Length of $queue $pos = 0; // Number of cloned items past the first level - $refs = 0; // Hard references counter + $refsCounter = 0; // Hard references counter $queue = array(array($var)); // This breadth-first queue is the return value $arrayRefs = array(); // Map of queue indexes to stub array objects $hardRefs = array(); // Map of original zval hashes to stub objects @@ -60,27 +60,32 @@ protected function doClone($var) for ($i = 0; $i < $len; ++$i) { $indexed = true; // Whether the currently iterated array is numerically indexed or not $j = -1; // Position in the currently iterated array - $step = $queue[$i]; // Copy of the currently iterated array used for hard references detection - foreach ($step as $k => $v) { + $fromObjCast = array_keys($queue[$i]); + $fromObjCast = array_keys(array_flip($fromObjCast)) !== $fromObjCast; + $refs = $vals = $fromObjCast ? array_values($queue[$i]) : $queue[$i]; + foreach ($queue[$i] as $k => $v) { // $k is the original key // $v is the original value or a stub object in case of hard references - if ($indexed && $k !== ++$j) { + if ($k !== ++$j) { $indexed = false; } + if ($fromObjCast) { + $k = $j; + } if ($useExt) { - $zval = symfony_zval_info($k, $step); + $zval = symfony_zval_info($k, $refs); } else { - $step[$k] = $cookie; - if ($zval['zval_isref'] = $queue[$i][$k] === $cookie) { + $refs[$k] = $cookie; + if ($zval['zval_isref'] = $vals[$k] === $cookie) { $zval['zval_hash'] = $v instanceof Stub ? spl_object_hash($v) : null; } $zval['type'] = gettype($v); } if ($zval['zval_isref']) { - $queue[$i][$k] = &$stub; // Break hard references to make $queue completely + $vals[$k] = &$stub; // Break hard references to make $queue completely unset($stub); // independent from the original structure if (isset($hardRefs[$zval['zval_hash']])) { - $queue[$i][$k] = $useExt ? ($v = $hardRefs[$zval['zval_hash']]) : ($step[$k] = $v); + $vals[$k] = $useExt ? ($v = $hardRefs[$zval['zval_hash']]) : ($refs[$k] = $v); if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) { ++$v->value->refCount; } @@ -204,18 +209,18 @@ protected function doClone($var) if (isset($stub)) { if ($zval['zval_isref']) { if ($useExt) { - $queue[$i][$k] = $hardRefs[$zval['zval_hash']] = $v = new Stub(); + $vals[$k] = $hardRefs[$zval['zval_hash']] = $v = new Stub(); $v->value = $stub; } else { - $step[$k] = new Stub(); - $step[$k]->value = $stub; - $h = spl_object_hash($step[$k]); - $queue[$i][$k] = $hardRefs[$h] = &$step[$k]; + $refs[$k] = new Stub(); + $refs[$k]->value = $stub; + $h = spl_object_hash($refs[$k]); + $vals[$k] = $hardRefs[$h] = &$refs[$k]; $values[$h] = $v; } - $queue[$i][$k]->handle = ++$refs; + $vals[$k]->handle = ++$refsCounter; } else { - $queue[$i][$k] = $stub; + $vals[$k] = $stub; } if ($a) { @@ -243,19 +248,38 @@ protected function doClone($var) $stub = $a = null; } elseif ($zval['zval_isref']) { if ($useExt) { - $queue[$i][$k] = $hardRefs[$zval['zval_hash']] = new Stub(); - $queue[$i][$k]->value = $v; + $vals[$k] = $hardRefs[$zval['zval_hash']] = new Stub(); + $vals[$k]->value = $v; } else { - $step[$k] = $queue[$i][$k] = new Stub(); - $step[$k]->value = $v; - $h = spl_object_hash($step[$k]); - $hardRefs[$h] = &$step[$k]; + $refs[$k] = $vals[$k] = new Stub(); + $refs[$k]->value = $v; + $h = spl_object_hash($refs[$k]); + $hardRefs[$h] = &$refs[$k]; $values[$h] = $v; } - $queue[$i][$k]->handle = ++$refs; + $vals[$k]->handle = ++$refsCounter; + } + } + + if ($fromObjCast) { + $refs = $vals; + $vals = array(); + $j = -1; + foreach ($queue[$i] as $k => $v) { + foreach (array($k => $v) as $a => $v) { + } + if ($a !== $k) { + $vals = (object) $vals; + $vals->{$k} = $refs[++$j]; + $vals = (array) $vals; + } else { + $vals[$k] = $refs[++$j]; + } } } + $queue[$i] = $vals; + if (isset($arrayRefs[$i])) { if ($indexed) { $arrayRefs[$i]->class = Stub::ARRAY_INDEXED; @@ -291,7 +315,7 @@ private static function initHashMask() if (!empty($frame['line'])) { ob_start(); debug_zval_dump($obj); - self::$hashMask = substr(ob_get_clean(), 17); + self::$hashMask = (int) substr(ob_get_clean(), 17); } } diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php index e64f051e2c6a..16ecd1d0e310 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php @@ -210,8 +210,8 @@ public function testGenerator() executing: { Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo(): { %sGeneratorDemo.php:10: """ - yield 1;\n - }\n + yield 1;\n + }\n \n """ } diff --git a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php index 82392d884585..9e4eea5d3446 100644 --- a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php @@ -128,6 +128,45 @@ public function testXmlResource() ); } + public function testJsonCast() + { + $var = (array) json_decode('{"0":{},"1":null}'); + foreach ($var as &$v) { + } + $var[] = &$v; + $var[''] = 2; + + $this->assertDumpMatchesFormat( + << {} + "1" => &1 null + 0 => &1 null + "" => 2 +] +EOTXT + , + $var + ); + } + + public function testObjectCast() + { + $var = (object) array(1 => 1); + $var->{1} = 2; + + $this->assertDumpMatchesFormat( + << 2); } } -/* foo bar*/ +/* foo bar*/ /* twig source*/ /* */ diff --git a/src/Symfony/Component/VarDumper/Tests/VarClonerTest.php b/src/Symfony/Component/VarDumper/Tests/VarClonerTest.php index 31e3a40ae04b..1184f2c76a25 100644 --- a/src/Symfony/Component/VarDumper/Tests/VarClonerTest.php +++ b/src/Symfony/Component/VarDumper/Tests/VarClonerTest.php @@ -135,6 +135,72 @@ public function testClone() $this->assertStringMatchesFormat($expected, print_r($clone, true)); } + public function testJsonCast() + { + $data = (array) json_decode('{"1":{}}'); + + $cloner = new VarCloner(); + $clone = $cloner->cloneVar($data); + + $expected = <<<'EOTXT' +object(Symfony\Component\VarDumper\Cloner\Data)#%i (4) { + ["data":"Symfony\Component\VarDumper\Cloner\Data":private]=> + array(2) { + [0]=> + array(1) { + [0]=> + object(Symfony\Component\VarDumper\Cloner\Stub)#%i (7) { + ["type"]=> + string(5) "array" + ["class"]=> + string(5) "assoc" + ["value"]=> + int(1) + ["cut"]=> + int(0) + ["handle"]=> + int(0) + ["refCount"]=> + int(0) + ["position"]=> + int(1) + } + } + [1]=> + array(1) { + ["1"]=> + object(Symfony\Component\VarDumper\Cloner\Stub)#%i (7) { + ["type"]=> + string(6) "object" + ["class"]=> + string(8) "stdClass" + ["value"]=> + NULL + ["cut"]=> + int(0) + ["handle"]=> + int(%i) + ["refCount"]=> + int(0) + ["position"]=> + int(0) + } + } + } + ["maxDepth":"Symfony\Component\VarDumper\Cloner\Data":private]=> + int(20) + ["maxItemsPerDepth":"Symfony\Component\VarDumper\Cloner\Data":private]=> + int(-1) + ["useRefHandles":"Symfony\Component\VarDumper\Cloner\Data":private]=> + int(-1) +} + +EOTXT; + ob_start(); + var_dump($clone); + $this->assertStringMatchesFormat($expected, ob_get_clean()); + } + public function testCaster() { $cloner = new VarCloner(array( diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php index c231a6e37d0e..64c4404b5922 100644 --- a/src/Symfony/Component/Yaml/Tests/InlineTest.php +++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php @@ -255,6 +255,7 @@ public function getScalarIndicators() /** * @group legacy + * @requires function Symfony\Bridge\PhpUnit\ErrorAssert::assertDeprecationsAreTriggered * throws \Symfony\Component\Yaml\Exception\ParseException in 4.0 */ public function testParseUnquotedScalarStartingWithPercentCharacter() diff --git a/src/Symfony/Component/Yaml/Yaml.php b/src/Symfony/Component/Yaml/Yaml.php index 213ef898c166..2271480ff943 100644 --- a/src/Symfony/Component/Yaml/Yaml.php +++ b/src/Symfony/Component/Yaml/Yaml.php @@ -79,19 +79,19 @@ public static function parse($input, $flags = 0) } /** - * Dumps a PHP array to a YAML string. + * Dumps a PHP value to a YAML string. * * The dump method, when supplied with an array, will do its best * to convert the array into friendly YAML. * - * @param array $array PHP array + * @param mixed $input The PHP value * @param int $inline The level where you switch to inline YAML * @param int $indent The amount of spaces to use for indentation of nested nodes * @param int $flags A bit field of DUMP_* constants to customize the dumped YAML string * - * @return string A YAML string representing the original PHP array + * @return string A YAML string representing the original PHP value */ - public static function dump($array, $inline = 2, $indent = 4, $flags = 0) + public static function dump($input, $inline = 2, $indent = 4, $flags = 0) { if (is_bool($flags)) { @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); @@ -113,6 +113,6 @@ public static function dump($array, $inline = 2, $indent = 4, $flags = 0) $yaml = new Dumper($indent); - return $yaml->dump($array, $inline, 0, $flags); + return $yaml->dump($input, $inline, 0, $flags); } }