diff --git a/.travis.yml b/.travis.yml index 0e626582b9e79..423d9260b6a9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,6 +51,7 @@ before_install: # A sigchild-enabled-PHP is used to test the Process component on the lowest PHP matrix line - if [[ ! $deps && $PHP = ${MIN_PHP%.*} && ! -d php-$MIN_PHP/sapi ]]; then wget http://museum.php.net/php5/php-$MIN_PHP.tar.bz2 -O - | tar -xj; (cd php-$MIN_PHP; ./configure --enable-sigchild --enable-pcntl; make -j2); fi - if [[ ! $PHP = hhvm* ]]; then INI_FILE=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; else INI_FILE=/etc/hhvm/php.ini; fi + - if [[ ! $skip ]]; then echo date.timezone = Europe/Paris >> $INI_FILE; fi - if [[ ! $skip ]]; then echo memory_limit = -1 >> $INI_FILE; fi - if [[ ! $skip ]]; then echo session.gc_probability = 0 >> $INI_FILE; fi - if [[ ! $skip ]]; then echo opcache.enable_cli = 1 >> $INI_FILE; fi diff --git a/CHANGELOG-3.1.md b/CHANGELOG-3.1.md index 07d0350ddfada..228cbff557b8e 100644 --- a/CHANGELOG-3.1.md +++ b/CHANGELOG-3.1.md @@ -7,6 +7,33 @@ 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.7 (2016-11-21) + + * bug #20550 [YAML] Fix processing timestamp strings with timezone (myesain) + * bug #20543 [DI] Fix error when trying to resolve a DefinitionDecorator (nicolas-grekas) + * bug #20544 [PhpUnitBridge] Fix time-sensitive tests that use data providers (julienfalque) + * bug #20484 bumped min version of Twig to 1.28 (fabpot) + * bug #20519 [Debug] Remove GLOBALS from exception context to avoid endless recursion (Seldaek) + * bug #20455 [ClassLoader] Fix ClassCollectionLoader inlining with __halt_compiler (giosh94mhz) + * bug #20307 [Form] Fix Date\TimeType marked as invalid on request with single_text and zero seconds (LuisDeimos) + * bug #20480 [FrameworkBundle] Register the ArrayDenormalizer (dunglas) + * bug #20286 [Serializer] Fix DataUriNormalizer's regex (dunglas) + * bug #20466 [Translation] fixed nested fallback catalogue using multiple locales. (aitboudad) + * bug #20465 [#18637][TranslationDebug] workaround for getFallbackLocales. (aitboudad) + * bug #20453 [Cache] Make directory hashing case insensitive (nicolas-grekas) + * bug #20440 [TwigBridge][TwigBundle][HttpKernel] prefer getSourceContext() over getSource() (xabbuh) + * bug #20287 Properly format value in UniqueEntityValidator (alcaeus) + * bug #20422 [Translation][fallback] add missing resources in parent catalogues. (aitboudad) + * bug #20378 [Form] Fixed show float values as choice value in ChoiceType (yceruto) + * bug #20294 Improved the design of the metrics in the profiler (javiereguiluz) + * bug #20375 [HttpFoundation][Session] Fix memcache session handler (klandaika) + * bug #20377 [Console] Fix infinite loop on missing input (chalasr) + * bug #20372 [Console] simplified code (fabpot) + * bug #20342 [Form] Fix UrlType transforms valid protocols (ogizanagi) + * bug #20292 Enhance GAE compat by removing some realpath() (nicolas-grekas) + * bug #20326 [VarDumper] Fix dumping Twig source in stack traces (nicolas-grekas) + * bug #20321 Compatibility with Twig 1.27 (xkobal) + * 3.1.6 (2016-10-27) * bug #20291 [Yaml] Fix 7.1 compat (nicolas-grekas) diff --git a/CHANGELOG-3.2.md b/CHANGELOG-3.2.md index 80a81116bae33..b2de770775c93 100644 --- a/CHANGELOG-3.2.md +++ b/CHANGELOG-3.2.md @@ -7,9 +7,32 @@ in 3.2 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.2.0...v3.2.1 -* 3.2.0-RC1 (2016-11-17) +* 3.2.0-RC2 (2016-11-27) - * + * bug #20601 [FrameworkBundle] Don't rely on any parent definition for "cache.annotations" (nicolas-grekas) + * bug #20638 Fix legacy tests that do not trigger any depreciation (julienfalque) + * bug #20374 [FrameworkBundle] Improve performance of ControllerNameParser (enumag) + * bug #20474 [Routing] Fail properly when a route parameter name cannot be used as a PCRE subpattern name (fancyweb) + * bug #20616 [Bridge/Doctrine] Use cache.prefix.seed parameter for generating cache namespace (nicolas-grekas) + * bug #20566 [DI] Initialize properties before method calls (ro0NL) + * bug #20583 [Workflow] Fixed graphviz dumper for state machine (lyrixx) + * bug #20621 [HttpKernel] Fix exception when serializing request attributes (nicolas-grekas) + * bug #20609 [DI] Fixed custom services definition BC break introduced in ec7e70fb… (kiler129) + * bug #20598 [DI] Aliases should preserve the aliased invalid behavior (nicolas-grekas) + * bug #20600 [Process] Fix process continuing after reached timeout using getIterator() (chalasr) + * bug #20603 [HttpKernel] Deprecate checking for cacheable HTTP methods in Request::isMethodSafe() (nicolas-grekas) + * bug #20602 [HttpKernel] Revert BC breaking change of Request::isMethodSafe() (nicolas-grekas) + * bug #20610 [FrameworkBundle] Add framework.cache.prefix_seed for predictible cache key prefixes (nicolas-grekas) + * bug #20595 [WebProfilerBundle] Fix deprecated uses of profiler_dump (nicolas-grekas) + * bug #20589 [SecurityBundle] Fix FirewallConfig nullable arguments (ogizanagi) + * bug #20590 [DI] Allow null as default env value (sroze) + * bug #20499 [Doctrine][Form] support large integers (xabbuh) + * bug #20559 [FrameworkBundle] Avoid warming up the validator cache for non-existent class (Seldaek) + * bug #20576 [Process] Do feat test before enabling TTY mode (nicolas-grekas) + * bug #20577 [FrameworkBundle] Mark cache.default_*_provider services private (nicolas-grekas) + * bug #20550 [YAML] Fix processing timestamp strings with timezone (myesain) + * bug #20543 [DI] Fix error when trying to resolve a DefinitionDecorator (nicolas-grekas) + * bug #20544 [PhpUnitBridge] Fix time-sensitive tests that use data providers (julienfalque) * 3.2.0-RC1 (2016-11-17) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 41c883159c8e2..c5fb26e0e91d8 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -22,16 +22,16 @@ Symfony is the result of the work of many people who made the code better - Abdellatif Ait boudad (aitboudad) - Pascal Borreli (pborreli) - Wouter De Jong (wouterj) - - Joseph Bielawski (stloyd) - Romain Neutron (romain) + - Joseph Bielawski (stloyd) - Karma Dordrak (drak) - Lukas Kahwe Smith (lsmith) - Martin Hasoň (hason) - Jeremy Mikola (jmikola) - Jean-François Simon (jfsimon) + - Grégoire Pineau (lyrixx) - Benjamin Eberlei (beberlei) - Igor Wiedler (igorw) - - Grégoire Pineau (lyrixx) - Eriksen Costa (eriksencosta) - Jules Pietri (heah) - Sarah Khalil (saro0h) @@ -45,11 +45,11 @@ Symfony is the result of the work of many people who made the code better - stealth35 ‏ (stealth35) - Alexander Mols (asm89) - Bulat Shakirzyanov (avalanche123) + - Robin Chalas (chalas_r) - Saša Stamenković (umpirsky) - Henrik Bjørnskov (henrikbjorn) - Miha Vrhovnik - Diego Saint Esteben (dii3g0) - - Robin Chalas (chalas_r) - Ener-Getick (energetick) - Konstantin Kudryashov (everzet) - Bilal Amarni (bamarni) @@ -84,19 +84,19 @@ Symfony is the result of the work of many people who made the code better - Fran Moreno (franmomu) - Antoine Hérault (herzult) - Paráda József (paradajozsef) + - Roland Franssen (ro0) + - Dariusz Ruminski - Jáchym Toušek (enumag) - Arnaud Le Blanc (arnaud-lb) - Jérôme Tamarelle (gromnan) - Michal Piotrowski (eventhorizon) - Tim Nagel (merk) - - Dariusz Ruminski - Brice BERNARD (brikou) - Alexander M. Turek (derrabus) - marc.weistroff - Issei Murasawa (issei_m) - lenar - Włodzimierz Gajda (gajdaw) - - Roland Franssen (ro0) - Baptiste Clavié (talus) - Alexander Schwenn (xelaris) - Florian Voutzinos (florianv) @@ -132,6 +132,7 @@ Symfony is the result of the work of many people who made the code better - Rafael Dohms (rdohms) - Arnaud Kleinpeter (nanocom) - jwdeitch + - Tobias Nyholm (tobias) - Joel Wurtz (brouznouf) - Philipp Wahala (hifi) - Vyacheslav Pavlov @@ -145,12 +146,14 @@ Symfony is the result of the work of many people who made the code better - Clemens Tolboom - Helmer Aaviksoo - Hiromi Hishida (77web) + - Yonel Ceruto González (yonelceruto) - Richard van Laak (rvanlaak) - Matthieu Ouellette-Vachon (maoueh) - Michał Pipa (michal.pipa) - Amal Raghav (kertz) - Jonathan Ingram (jonathaningram) - Artur Kotyrba + - jeremyFreeAgent (Jérémy Romey) (jeremyfreeagent) - Warnar Boekkooi (boekkooi) - Dmitrii Chekaliuk (lazyhammer) - Clément JOBEILI (dator) @@ -160,13 +163,12 @@ Symfony is the result of the work of many people who made the code better - Richard Miller (mr_r_miller) - Mario A. Alvarez Garcia (nomack84) - Dennis Benkert (denderello) - - jeremyFreeAgent (Jérémy Romey) (jeremyfreeagent) - Benjamin Dulau (dbenjamin) - Mathieu Lemoine (lemoinem) - Andreas Hucks (meandmymonkey) - Noel Guilbert (noel) - Lars Strojny (lstrojny) - - Yonel Ceruto González (yonelceruto) + - Maxime STEINHAUSSER - Stepan Anchugov (kix) - bronze1man - sun (sun) @@ -236,7 +238,6 @@ Symfony is the result of the work of many people who made the code better - Grégoire Paris (greg0ire) - Leo Feyer - Chekote - - Tobias Nyholm (tobias) - Thomas Adam - Albert Casademont (acasademont) - Jhonny Lidfors (jhonne) @@ -245,6 +246,7 @@ Symfony is the result of the work of many people who made the code better - Nikita Konstantinov - Wodor Wodorski - Thomas Lallement (raziel057) + - Giorgio Premi - Matthieu Napoli (mnapoli) - Beau Simensen (simensen) - Michael Hirschler (mvhirsch) @@ -295,7 +297,6 @@ Symfony is the result of the work of many people who made the code better - Mark Challoner (markchalloner) - Gregor Harlan (gharlan) - Gennady Telegin (gtelegin) - - Giorgio Premi - Ben Davies (bendavies) - Erin Millard - Artur Melo (restless) @@ -393,7 +394,6 @@ Symfony is the result of the work of many people who made the code better - Christian Schmidt - Marcin Sikoń (marphi) - Dominik Zogg (dominik.zogg) - - Maxime STEINHAUSSER - Marek Pietrzak - Chad Sikorra (chadsikorra) - franek (franek) @@ -439,6 +439,7 @@ Symfony is the result of the work of many people who made the code better - Vicent Soria Durá (vicentgodella) - Nicolas Dewez (nicolas_dewez) - Anthony Ferrara + - Victor Bocharsky (bocharsky_bw) - Ioan Negulescu - Jakub Škvára (jskvara) - Andrew Udvare (audvare) @@ -611,7 +612,6 @@ Symfony is the result of the work of many people who made the code better - Andrew Tchircoff (andrewtch) - michaelwilliams - 1emming - - Victor Bocharsky (bocharsky_bw) - Leevi Graham (leevigraham) - Jordan Deitch - Casper Valdemar Poulsen @@ -769,6 +769,7 @@ Symfony is the result of the work of many people who made the code better - fabios - Sander Coolen (scoolen) - Nicolas Le Goff (nlegoff) + - Andreas Braun - Ben Oman - Manuele Menozzi - Anton Babenko (antonbabenko) @@ -858,6 +859,7 @@ Symfony is the result of the work of many people who made the code better - rpg600 - Péter Buri (burci) - Davide Borsatto (davide.borsatto) + - Indra Gunawan (guind) - kaiwa - Charles Sanquer (csanquer) - Albert Ganiev (helios-ag) @@ -969,6 +971,7 @@ Symfony is the result of the work of many people who made the code better - Sandro Hopf - Łukasz Makuch - George Giannoulopoulos + - Luis Ramirez (luisdeimos) - Daniel Richter (richtermeister) - ChrisC - Ilya Biryukov @@ -985,6 +988,7 @@ Symfony is the result of the work of many people who made the code better - Pete Mitchell (peterjmit) - Tom Corrigan (tomcorrigan) - Martin Pärtel + - Noah Heck (myesain) - Patrick Daley (padrig) - Xavier Briand (xavierbriand) - Max Summe @@ -1041,6 +1045,7 @@ Symfony is the result of the work of many people who made the code better - Sebastian Ionescu - Thomas Ploch - Simon Neidhold + - Xavier HAUSHERR - Valentin VALCIU - Kevin Dew - James Cowgill @@ -1163,6 +1168,7 @@ Symfony is the result of the work of many people who made the code better - Koalabaerchen - michalmarcinkowski - Warwick + - VJ - Chris - JakeFr - Simon Sargeant @@ -1236,6 +1242,7 @@ Symfony is the result of the work of many people who made the code better - Joel Marcey - David Christmann - root + - Wouter J - James Hudson - Tom Maguire - David Zuelke @@ -1312,6 +1319,7 @@ Symfony is the result of the work of many people who made the code better - Jelle Bekker (jbekker) - Ian Jenkins (jenkoian) - Jorge Martin (jorgemartind) + - Julien Falque (julienfalque) - Kevin Herrera (kherge) - Luis Ramón López López (lrlopez) - Muriel (metalmumu) @@ -1379,6 +1387,7 @@ Symfony is the result of the work of many people who made the code better - Jörg Rühl - wesleyh - sergey + - Karim Miladi - Michael Genereux - patrick-mcdougle - Dariusz Czech @@ -1559,6 +1568,7 @@ Symfony is the result of the work of many people who made the code better - Sergey Fedotov - Michael - fh-github@fholzhauer.de + - Jan Emrich - Mark Topper - Xavier REN - Zander Baldwin diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md index a93cfbf2f09e2..866bd981cce79 100644 --- a/UPGRADE-3.0.md +++ b/UPGRADE-3.0.md @@ -253,6 +253,9 @@ UPGRADE FROM 2.x to 3.0 closures, but the closure is now resolved in the type instead of in the loader. + * Using the entity provider with a Doctrine repository implementing `UserProviderInterface` is not supported anymore. + You should make the repository implement `UserLoaderInterface` instead. + ### EventDispatcher * The method `getListenerPriority($eventName, $listener)` has been added to the diff --git a/appveyor.yml b/appveyor.yml index 4f7133a0df779..63f7319e191ee 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -29,7 +29,7 @@ install: - cd .. - copy /Y php.ini-development php.ini-min - echo max_execution_time=1200 >> php.ini-min - - echo date.timezone="UTC" >> php.ini-min + - echo date.timezone="America/Los_Angeles" >> php.ini-min - echo extension_dir=ext >> php.ini-min - copy /Y php.ini-min php.ini-max - echo zend_extension=php_opcache.dll >> php.ini-max diff --git a/src/Symfony/Bridge/Doctrine/CHANGELOG.md b/src/Symfony/Bridge/Doctrine/CHANGELOG.md index f187e970f98fe..3dcb8770ec8cc 100644 --- a/src/Symfony/Bridge/Doctrine/CHANGELOG.md +++ b/src/Symfony/Bridge/Doctrine/CHANGELOG.md @@ -18,6 +18,12 @@ CHANGELOG * removed passing a query builder closure to `ORMQueryBuilderLoader` * removed `loader` and `property` options of the `DoctrineType` +2.8.0 +----- + + * deprecated using the entity provider with a Doctrine repository implementing UserProviderInterface + * added UserLoaderInterface for loading users through Doctrine. + 2.7.0 ----- diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php index 3a6a877e1fc4e..b9cac9f71845a 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php @@ -384,9 +384,14 @@ protected function loadCacheDriver($cacheName, $objectManagerName, array $cacheD if (!isset($cacheDriver['namespace'])) { // generate a unique namespace for the given application - $env = $container->getParameter('kernel.root_dir').$container->getParameter('kernel.environment'); - $hash = hash('sha256', $env); - $namespace = 'sf2'.$this->getMappingResourceExtension().'_'.$objectManagerName.'_'.$hash; + if ($container->hasParameter('cache.prefix.seed')) { + $seed = '.'.$container->getParameterBag()->resolveValue($container->getParameter('cache.prefix.seed')); + } else { + $seed = '_'.$container->getParameter('kernel.root_dir'); + } + $seed .= '.'.$container->getParameter('kernel.name').'.'.$container->getParameter('kernel.environment').'.'.$container->getParameter('kernel.debug'); + $hash = hash('sha256', $seed); + $namespace = 'sf_'.$this->getMappingResourceExtension().'_'.$objectManagerName.'_'.$hash; $cacheDriver['namespace'] = $namespace; } diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php index 9531b892f2465..6501f8393e994 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php @@ -70,7 +70,7 @@ public function getEntitiesByIds($identifier, array $values) // Filter out non-integer values (e.g. ""). If we don't, some // databases such as PostgreSQL fail. $values = array_values(array_filter($values, function ($v) { - return (string) $v === (string) (int) $v; + return (string) $v === (string) (int) $v || ctype_digit($v); })); } elseif ('guid' === $metadata->getTypeOfField($identifier)) { $parameterType = Connection::PARAM_STR_ARRAY; diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php index 4bfb789c97cdf..a14b786b8d737 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php @@ -67,7 +67,7 @@ public function testFilterNonIntegerValues() $query->expects($this->once()) ->method('setParameter') - ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', array(1, 2, 3), Connection::PARAM_INT_ARRAY) + ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', array(1, 2, 3, '9223372036854775808'), Connection::PARAM_INT_ARRAY) ->willReturn($query); $qb = $this->getMockBuilder('Doctrine\ORM\QueryBuilder') @@ -83,7 +83,7 @@ public function testFilterNonIntegerValues() ->from('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity', 'e'); $loader = new ORMQueryBuilderLoader($qb); - $loader->getEntitiesByIds('id', array(1, '', 2, 3, 'foo')); + $loader->getEntitiesByIds('id', array(1, '', 2, 3, 'foo', '9223372036854775808')); } public function testEmbeddedIdentifierName() diff --git a/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php b/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php index a8977312bdae7..0d873732636a9 100644 --- a/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php +++ b/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php @@ -25,8 +25,8 @@ class SymfonyTestsListener extends \PHPUnit_Framework_BaseTestListener private $skippedFile = false; private $wasSkipped = array(); private $isSkipped = array(); - private $expectedDeprecations; - private $gatheredDeprecations; + private $expectedDeprecations = array(); + private $gatheredDeprecations = array(); private $previousErrorHandler; /** @@ -181,7 +181,8 @@ public function endTest(\PHPUnit_Framework_Test $test, $time) $test->getTestResultObject()->addFailure($test, $e, $time); } - $this->expectedDeprecations = $this->gatheredDeprecations = $this->previousErrorHandler = null; + $this->expectedDeprecations = $this->gatheredDeprecations = array(); + $this->previousErrorHandler = null; } if (-2 < $this->state && $test instanceof \PHPUnit_Framework_TestCase) { $groups = \PHPUnit_Util_Test::getGroups(get_class($test), $test->getName(false)); diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php index 2e1c86e508998..ce5104dc41cc5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php @@ -68,7 +68,9 @@ public function warmUp($cacheDir) foreach ($this->extractSupportedLoaders($loaders) as $loader) { foreach ($loader->getMappedClasses() as $mappedClass) { - $metadataFactory->getMetadataFor($mappedClass); + if ($metadataFactory->hasMetadataFor($mappedClass)) { + $metadataFactory->getMetadataFor($mappedClass); + } } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php index 39f7ac2f541c3..22811c6558894 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php @@ -47,6 +47,10 @@ protected function configure() php %command.full_name% framework php %command.full_name% FrameworkBundle +For dumping a specific option, add its path as second argument: + + php %command.full_name% framework serializer.enabled + EOF ) ; diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php index ddfb987ff1937..40604e3d809db 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php @@ -15,6 +15,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Workflow\Dumper\GraphvizDumper; +use Symfony\Component\Workflow\Dumper\StateMachineGraphvizDumper; use Symfony\Component\Workflow\Marking; use Symfony\Component\Workflow\Workflow; @@ -60,13 +61,14 @@ protected function execute(InputInterface $input, OutputInterface $output) $serviceId = $input->getArgument('name'); if ($container->has('workflow.'.$serviceId)) { $workflow = $container->get('workflow.'.$serviceId); + $dumper = new GraphvizDumper(); } elseif ($container->has('state_machine.'.$serviceId)) { $workflow = $container->get('state_machine.'.$serviceId); + $dumper = new StateMachineGraphvizDumper(); } else { throw new \InvalidArgumentException(sprintf('No service found for "workflow.%1$s" nor "state_machine.%1$s".', $serviceId)); } - $dumper = new GraphvizDumper(); $marking = new Marking(); foreach ($input->getArgument('marking') as $place) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php index 374d0eba10ff6..989dc8cd23d7c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php @@ -46,11 +46,12 @@ public function __construct(KernelInterface $kernel) */ public function parse($controller) { - $originalController = $controller; - if (3 !== count($parts = explode(':', $controller))) { + $parts = explode(':', $controller); + if (3 !== count($parts) || in_array('', $parts, true)) { throw new \InvalidArgumentException(sprintf('The "%s" controller is not a valid "a:b:c" controller string.', $controller)); } + $originalController = $controller; list($bundle, $controller, $action) = $parts; $controller = str_replace('/', '\\', $controller); $bundles = array(); diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolClearerPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolClearerPass.php index 146c3c8c73416..c859a6ba900a4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolClearerPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolClearerPass.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; +use Symfony\Component\Cache\Adapter\AbstractAdapter; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; @@ -25,6 +26,8 @@ final class CachePoolClearerPass implements CompilerPassInterface */ public function process(ContainerBuilder $container) { + $container->getParameterBag()->remove('cache.prefix.seed'); + foreach ($container->findTaggedServiceIds('cache.pool') as $id => $attributes) { foreach (array_reverse($attributes) as $attr) { if (isset($attr['clearer'])) { @@ -36,5 +39,22 @@ public function process(ContainerBuilder $container) } } } + + if (!$container->has('cache.annotations')) { + return; + } + $factory = array(AbstractAdapter::class, 'createSystemCache'); + $annotationsPool = $container->getDefinition('cache.annotations'); + if ($factory !== $annotationsPool->getFactory() || 4 !== count($annotationsPool->getArguments())) { + return; + } + if ($container->has('monolog.logger.cache')) { + $annotationsPool->addArgument(new Reference('monolog.logger.cache')); + } elseif ($container->has('cache.system')) { + $systemPool = $container->getDefinition('cache.system'); + if ($factory === $systemPool->getFactory() && 5 <= count($systemArgs = $systemPool->getArguments())) { + $annotationsPool->addArgument($systemArgs[4]); + } + } } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php index c856c0206a5d7..593b7782c66b0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php @@ -29,13 +29,12 @@ class CachePoolPass implements CompilerPassInterface */ public function process(ContainerBuilder $container) { - $namespaceSuffix = ''; - - foreach (array('name', 'root_dir', 'environment', 'debug') as $key) { - if ($container->hasParameter('kernel.'.$key)) { - $namespaceSuffix .= '.'.$container->getParameter('kernel.'.$key); - } + if ($container->hasParameter('cache.prefix.seed')) { + $seed = '.'.$container->getParameterBag()->resolveValue($container->getParameter('cache.prefix.seed')); + } else { + $seed = '_'.$container->getParameter('kernel.root_dir'); } + $seed .= '.'.$container->getParameter('kernel.name').'.'.$container->getParameter('kernel.environment').'.'.$container->getParameter('kernel.debug'); $aliases = $container->getAliases(); $attributes = array( @@ -55,7 +54,7 @@ public function process(ContainerBuilder $container) } } if (!isset($tags[0]['namespace'])) { - $tags[0]['namespace'] = $this->getNamespace($namespaceSuffix, $id); + $tags[0]['namespace'] = $this->getNamespace($seed, $id); } if (isset($tags[0]['clearer'])) { $clearer = strtolower($tags[0]['clearer']); @@ -87,9 +86,9 @@ public function process(ContainerBuilder $container) } } - private function getNamespace($namespaceSuffix, $id) + private function getNamespace($seed, $id) { - return substr(str_replace('/', '-', base64_encode(hash('sha256', $id.$namespaceSuffix, true))), 0, 10); + return substr(str_replace('/', '-', base64_encode(hash('sha256', $id.$seed, true))), 0, 10); } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index f33085f8724a8..5c6208e6e46f2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -667,6 +667,10 @@ private function addCacheSection(ArrayNodeDefinition $rootNode) ->addDefaultsIfNotSet() ->fixXmlConfig('pool') ->children() + ->scalarNode('prefix_seed') + ->info('Used to namespace cache keys when using several apps with the same shared backend') + ->example('my-application-name') + ->end() ->scalarNode('app') ->info('App related cache pools configuration') ->defaultValue('cache.adapter.filesystem') diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 5c009962eabfa..a122bf87b2a4f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1238,13 +1238,18 @@ private function registerPropertyInfoConfiguration(array $config, ContainerBuild private function registerCacheConfiguration(array $config, ContainerBuilder $container) { $version = substr(str_replace('/', '-', base64_encode(hash('sha256', uniqid(mt_rand(), true), true))), 0, 22); + $container->getDefinition('cache.annotations')->replaceArgument(2, $version); $container->getDefinition('cache.adapter.apcu')->replaceArgument(2, $version); $container->getDefinition('cache.adapter.system')->replaceArgument(2, $version); $container->getDefinition('cache.adapter.filesystem')->replaceArgument(2, $config['directory']); + if (isset($config['prefix_seed'])) { + $container->setParameter('cache.prefix.seed', $config['prefix_seed']); + } foreach (array('doctrine', 'psr6', 'redis') as $name) { if (isset($config[$name = 'default_'.$name.'_provider'])) { $container->setAlias('cache.'.$name, Compiler\CachePoolPass::getServiceProvider($container, $config[$name])); + $container->getAlias('cache.'.$name)->setPublic(false); } } foreach (array('app', 'system') as $name) { diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php index 72873aa6e97e8..4c20d146cb4bf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php @@ -93,12 +93,12 @@ public function build(ContainerBuilder $container) $container->addCompilerPass(new SerializerPass()); $container->addCompilerPass(new PropertyInfoPass()); $container->addCompilerPass(new ControllerArgumentValueResolverPass()); - $container->addCompilerPass(new CachePoolPass()); + $container->addCompilerPass(new CachePoolPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 32); $container->addCompilerPass(new ValidateWorkflowsPass()); $container->addCompilerPass(new CachePoolClearerPass(), PassConfig::TYPE_AFTER_REMOVING); if ($container->getParameter('kernel.debug')) { - $container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -1); + $container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32); $container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING); $container->addCompilerPass(new ContainerBuilderDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING); $container->addCompilerPass(new CompilerDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml index 461b40455a8c5..80cb00ada9652 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml @@ -22,8 +22,13 @@ - - + + + + + 0 + + %kernel.cache_dir%/pools diff --git a/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php b/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php index 68ec13a46499e..9783117f0b7f6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php +++ b/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php @@ -20,7 +20,7 @@ * DelegatingLoader delegates route loading to other loaders using a loader resolver. * * This implementation resolves the _controller attribute from the short notation - * to the fully-qualified form (from a:b:c to class:method). + * to the fully-qualified form (from a:b:c to class::method). * * @author Fabien Potencier */ @@ -75,15 +75,17 @@ public function load($resource, $type = null) } foreach ($collection->all() as $route) { - if ($controller = $route->getDefault('_controller')) { - try { - $controller = $this->parser->parse($controller); - } catch (\InvalidArgumentException $e) { - // unable to optimize unknown notation - } + if (!$controller = $route->getDefault('_controller')) { + continue; + } - $route->setDefault('_controller', $controller); + try { + $controller = $this->parser->parse($controller); + } catch (\InvalidArgumentException $e) { + // unable to optimize unknown notation } + + $route->setDefault('_controller', $controller); } return $collection; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolClearerPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolClearerPassTest.php index 62a41962316e2..38a2d38761e3b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolClearerPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolClearerPassTest.php @@ -24,6 +24,10 @@ class CachePoolClearerPassTest extends \PHPUnit_Framework_TestCase public function testPoolRefsAreWeak() { $container = new ContainerBuilder(); + $container->setParameter('kernel.debug', false); + $container->setParameter('kernel.name', 'app'); + $container->setParameter('kernel.environment', 'prod'); + $container->setParameter('kernel.root_dir', 'foo'); $publicPool = new Definition(); $publicPool->addArgument('namespace'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php index 53cb95837f7e8..192e1493d0c38 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php @@ -29,6 +29,10 @@ protected function setUp() public function testNamespaceArgumentIsReplaced() { $container = new ContainerBuilder(); + $container->setParameter('kernel.debug', false); + $container->setParameter('kernel.name', 'app'); + $container->setParameter('kernel.environment', 'prod'); + $container->setParameter('kernel.root_dir', 'foo'); $adapter = new Definition(); $adapter->setAbstract(true); $adapter->addTag('cache.pool'); @@ -41,12 +45,16 @@ public function testNamespaceArgumentIsReplaced() $this->cachePoolPass->process($container); - $this->assertSame('kRFqMp5odS', $cachePool->getArgument(0)); + $this->assertSame('C42Pcl9VBJ', $cachePool->getArgument(0)); } public function testArgsAreReplaced() { $container = new ContainerBuilder(); + $container->setParameter('kernel.debug', false); + $container->setParameter('kernel.name', 'app'); + $container->setParameter('kernel.environment', 'prod'); + $container->setParameter('cache.prefix.seed', 'foo'); $cachePool = new Definition(); $cachePool->addTag('cache.pool', array( 'provider' => 'foobar', @@ -61,7 +69,7 @@ public function testArgsAreReplaced() $this->assertInstanceOf(Reference::class, $cachePool->getArgument(0)); $this->assertSame('foobar', (string) $cachePool->getArgument(0)); - $this->assertSame('kRFqMp5odS', $cachePool->getArgument(1)); + $this->assertSame('KO3xHaFEZU', $cachePool->getArgument(1)); $this->assertSame(3, $cachePool->getArgument(2)); } @@ -72,6 +80,10 @@ public function testArgsAreReplaced() public function testThrowsExceptionWhenCachePoolTagHasUnknownAttributes() { $container = new ContainerBuilder(); + $container->setParameter('kernel.debug', false); + $container->setParameter('kernel.name', 'app'); + $container->setParameter('kernel.environment', 'prod'); + $container->setParameter('kernel.root_dir', 'foo'); $adapter = new Definition(); $adapter->setAbstract(true); $adapter->addTag('cache.pool'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Validation/Resources/person.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Validation/Resources/person.xml index 72cc30dc662ec..b23760ae4556c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Validation/Resources/person.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Validation/Resources/person.xml @@ -15,4 +15,15 @@ + + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 5c88548bcff1d..faa28fc1757aa 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -267,6 +267,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a { $config = $container->setDefinition($configId, new DefinitionDecorator('security.firewall.config')); $config->replaceArgument(0, $id); + $config->replaceArgument(1, $firewall['user_checker']); // Matcher $matcher = null; @@ -279,8 +280,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a $matcher = $this->createRequestMatcher($container, $pattern, $host, $methods); } - $config->replaceArgument(1, (string) $matcher); - $config->replaceArgument(2, $firewall['user_checker']); + $config->replaceArgument(2, $matcher ? (string) $matcher : null); $config->replaceArgument(3, $firewall['security']); // Security disabled? @@ -306,6 +306,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a // Channel listener $listeners[] = new Reference('security.channel_listener'); + $contextKey = null; // Context serializer listener if (false === $firewall['stateless']) { $contextKey = $id; @@ -313,11 +314,11 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a $contextKey = $firewall['context']; } - $config->replaceArgument(6, $contextKey); - $listeners[] = new Reference($this->createContextListener($container, $contextKey)); } + $config->replaceArgument(6, $contextKey); + // Logout listener if (isset($firewall['logout'])) { $listenerKeys[] = 'logout'; @@ -399,12 +400,8 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a // Exception listener $exceptionListener = new Reference($this->createExceptionListener($container, $firewall, $id, $configuredEntryPoint ?: $defaultEntryPoint, $firewall['stateless'])); - if (isset($firewall['access_denied_handler'])) { - $config->replaceArgument(8, $firewall['access_denied_handler']); - } - if (isset($firewall['access_denied_url'])) { - $config->replaceArgument(9, $firewall['access_denied_url']); - } + $config->replaceArgument(8, isset($firewall['access_denied_handler']) ? $firewall['access_denied_handler'] : null); + $config->replaceArgument(9, isset($firewall['access_denied_url']) ? $firewall['access_denied_url'] : null); $container->setAlias(new Alias('security.user_checker.'.$id, false), $firewall['user_checker']); diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index 8def320482486..54e573440299c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -116,8 +116,8 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallConfig.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallConfig.php index a36ff955130d1..3cfb11404ccf8 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallConfig.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallConfig.php @@ -17,8 +17,8 @@ final class FirewallConfig { private $name; - private $requestMatcher; private $userChecker; + private $requestMatcher; private $securityEnabled; private $stateless; private $provider; @@ -30,8 +30,8 @@ final class FirewallConfig /** * @param string $name - * @param string $requestMatcher * @param string $userChecker + * @param string|null $requestMatcher * @param bool $securityEnabled * @param bool $stateless * @param string|null $provider @@ -41,11 +41,11 @@ final class FirewallConfig * @param string|null $accessDeniedUrl * @param string[] $listeners */ - public function __construct($name, $requestMatcher, $userChecker, $securityEnabled = true, $stateless = false, $provider = null, $context = null, $entryPoint = null, $accessDeniedHandler = null, $accessDeniedUrl = null, $listeners = array()) + public function __construct($name, $userChecker, $requestMatcher = null, $securityEnabled = true, $stateless = false, $provider = null, $context = null, $entryPoint = null, $accessDeniedHandler = null, $accessDeniedUrl = null, $listeners = array()) { $this->name = $name; - $this->requestMatcher = $requestMatcher; $this->userChecker = $userChecker; + $this->requestMatcher = $requestMatcher; $this->securityEnabled = $securityEnabled; $this->stateless = $stateless; $this->provider = $provider; @@ -62,7 +62,8 @@ public function getName() } /** - * @return string The request matcher service id + * @return string|null The request matcher service id or null if neither the request matcher, pattern or host + * options were provided */ public function getRequestMatcher() { diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php index 9d00c121e5160..81b815e4d973e 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php @@ -27,10 +27,6 @@ class FirewallContext public function __construct(array $listeners, ExceptionListener $exceptionListener = null, FirewallConfig $config = null) { - if (null === $config) { - @trigger_error(sprintf('"%s()" expects an instance of "%s" as third argument since version 3.2 and will trigger an error in 4.0 if not provided.', __METHOD__, FirewallConfig::class), E_USER_DEPRECATED); - } - $this->listeners = $listeners; $this->exceptionListener = $exceptionListener; $this->config = $config; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php index 34e3ad56114ac..bbce61a75813b 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php @@ -20,7 +20,9 @@ abstract class CompleteConfigurationTest extends \PHPUnit_Framework_TestCase { private static $containerCache = array(); - abstract protected function loadFromFile(ContainerBuilder $container, $file); + abstract protected function getLoader(ContainerBuilder $container); + + abstract protected function getFileExtension(); public function testRolesHierarchy() { @@ -78,18 +80,21 @@ public function testFirewalls() $this->assertEquals(array( array( 'simple', - 'security.request_matcher.707b20193d4cb9f2718114abcbebb32af48f948484fc166a03482f49bf14f25e271f72c7', 'security.user_checker', + 'security.request_matcher.707b20193d4cb9f2718114abcbebb32af48f948484fc166a03482f49bf14f25e271f72c7', false, ), array( 'secure', - '', 'security.user_checker', + null, true, true, 'security.user.provider.concrete.default', + null, 'security.authentication.form_entry_point.secure', + null, + null, array( 'logout', 'switch_user', @@ -104,13 +109,15 @@ public function testFirewalls() ), array( 'host', - 'security.request_matcher.dda8b565689ad8509623ee68fb2c639cd81cd4cb339d60edbaf7d67d30e6aa09bd8c63c3', 'security.user_checker', + 'security.request_matcher.dda8b565689ad8509623ee68fb2c639cd81cd4cb339d60edbaf7d67d30e6aa09bd8c63c3', true, false, 'security.user.provider.concrete.default', 'host', 'security.authentication.basic_entry_point.host', + null, + null, array( 'http_basic', 'anonymous', @@ -118,13 +125,15 @@ public function testFirewalls() ), array( 'with_user_checker', - '', 'app.user_checker', + null, true, false, 'security.user.provider.concrete.default', 'with_user_checker', 'security.authentication.basic_entry_point.with_user_checker', + null, + null, array( 'http_basic', 'anonymous', @@ -327,6 +336,8 @@ public function testUserCheckerConfigWithNoCheckers() protected function getContainer($file) { + $file = $file.'.'.$this->getFileExtension(); + if (isset(self::$containerCache[$file])) { return self::$containerCache[$file]; } @@ -336,7 +347,7 @@ protected function getContainer($file) $bundle = new SecurityBundle(); $bundle->build($container); // Attach all default factories - $this->loadFromFile($container, $file); + $this->getLoader($container)->load($file); $container->getCompilerPassConfig()->setOptimizationPasses(array()); $container->getCompilerPassConfig()->setRemovingPasses(array()); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpCompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpCompleteConfigurationTest.php index 131c38d5e50b7..6495485d1bb87 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpCompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpCompleteConfigurationTest.php @@ -17,9 +17,13 @@ class PhpCompleteConfigurationTest extends CompleteConfigurationTest { - protected function loadFromFile(ContainerBuilder $container, $file) + protected function getLoader(ContainerBuilder $container) { - $loadXml = new PhpFileLoader($container, new FileLocator(__DIR__.'/Fixtures/php')); - $loadXml->load($file.'.php'); + return new PhpFileLoader($container, new FileLocator(__DIR__.'/Fixtures/php')); + } + + protected function getFileExtension() + { + return 'php'; } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCompleteConfigurationTest.php index cf6833a22ab61..2399f5ee460dc 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCompleteConfigurationTest.php @@ -17,9 +17,13 @@ class XmlCompleteConfigurationTest extends CompleteConfigurationTest { - protected function loadFromFile(ContainerBuilder $container, $file) + protected function getLoader(ContainerBuilder $container) { - $loadXml = new XmlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/xml')); - $loadXml->load($file.'.xml'); + return new XmlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/xml')); + } + + protected function getFileExtension() + { + return 'xml'; } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlCompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlCompleteConfigurationTest.php index 568b8623ea007..d5d5e693abdcd 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlCompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlCompleteConfigurationTest.php @@ -17,9 +17,13 @@ class YamlCompleteConfigurationTest extends CompleteConfigurationTest { - protected function loadFromFile(ContainerBuilder $container, $file) + protected function getLoader(ContainerBuilder $container) { - $loadXml = new YamlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/yml')); - $loadXml->load($file.'.yml'); + return new YamlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/yml')); + } + + protected function getFileExtension() + { + return 'yml'; } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallConfigTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallConfigTest.php index eb6eeeb8dd588..a6fdeeed3768a 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallConfigTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallConfigTest.php @@ -32,8 +32,8 @@ public function testGetters() $config = new FirewallConfig( 'foo_firewall', - $options['request_matcher'], $options['user_checker'], + $options['request_matcher'], $options['security'], $options['stateless'], $options['provider'], diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallContextTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallContextTest.php index 098e2c51f80dd..e0790bf23c776 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallContextTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallContextTest.php @@ -20,7 +20,7 @@ class FirewallContextTest extends \PHPUnit_Framework_TestCase { public function testGetters() { - $config = new FirewallConfig('main', 'request_matcher', 'user_checker'); + $config = new FirewallConfig('main', 'user_checker', 'request_matcher'); $exceptionListener = $this ->getMockBuilder(ExceptionListener::class) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php index d77eb279f4a69..3ea170d7ea3ea 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php @@ -86,11 +86,11 @@ private function getTraces(RequestDataCollector $request, $method) { $traceRequest = Request::create( $request->getPathInfo(), - $request->getRequestServer()->get('REQUEST_METHOD'), - $request->getRequestAttributes()->all(), - $request->getRequestCookies()->all(), + $request->getRequestServer(true)->get('REQUEST_METHOD'), array(), - $request->getRequestServer()->all() + $request->getRequestCookies(true)->all(), + array(), + $request->getRequestServer(true)->all() ); $context = $this->matcher->getContext(); diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig index 277a046789e48..cdf2839c6dcd2 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig @@ -154,7 +154,7 @@ {% endif %}

Request Headers

- {{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestheaders, labels: ['Header', 'Value'] }, with_context = false) }} + {{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestheaders, labels: ['Header', 'Value'], maxDepth: 1 }, with_context = false) }}

Request Content

@@ -183,7 +183,7 @@

Response Headers

- {{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.responseheaders, labels: ['Header', 'Value'] }, with_context = false) }} + {{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.responseheaders, labels: ['Header', 'Value'], maxDepth: 1 }, with_context = false) }}
diff --git a/src/Symfony/Component/Cache/Adapter/FilesystemAdapterTrait.php b/src/Symfony/Component/Cache/Adapter/FilesystemAdapterTrait.php index faf6e50f0f948..2f6e43e6379fa 100644 --- a/src/Symfony/Component/Cache/Adapter/FilesystemAdapterTrait.php +++ b/src/Symfony/Component/Cache/Adapter/FilesystemAdapterTrait.php @@ -15,6 +15,8 @@ /** * @author Nicolas Grekas + * + * @internal */ trait FilesystemAdapterTrait { diff --git a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php index 4bc80ee30022e..c3cbd3bef7e54 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php @@ -28,8 +28,6 @@ public function testDefaultLifeTime() { if (isset($this->skippedTests[__FUNCTION__])) { $this->markTestSkipped($this->skippedTests[__FUNCTION__]); - - return; } $cache = $this->createCachePool(2); @@ -51,8 +49,6 @@ public function testNotUnserializable() { if (isset($this->skippedTests[__FUNCTION__])) { $this->markTestSkipped($this->skippedTests[__FUNCTION__]); - - return; } $cache = $this->createCachePool(); diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index ae81fe36fdef6..58a38048f7530 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -424,7 +424,7 @@ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INV } if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) { - return $this->get($this->aliasDefinitions[$id]); + return $this->get($this->aliasDefinitions[$id], $invalidBehavior); } try { @@ -835,6 +835,10 @@ public function findDefinition($id) */ private function createService(Definition $definition, $id, $tryProxy = true) { + if ($definition instanceof DefinitionDecorator) { + throw new RuntimeException(sprintf('Constructing service "%s" from a parent definition is not supported at build time.', $id)); + } + if ($definition->isSynthetic()) { throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id)); } @@ -897,15 +901,15 @@ private function createService(Definition $definition, $id, $tryProxy = true) $this->shareService($definition, $service, $id); } - foreach ($definition->getMethodCalls() as $call) { - $this->callMethod($service, $call); - } - $properties = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties()))); foreach ($properties as $name => $value) { $service->$name = $value; } + foreach ($definition->getMethodCalls() as $call) { + $this->callMethod($service, $call); + } + if ($callable = $definition->getConfigurator()) { if (is_array($callable)) { $callable[0] = $parameterBag->resolveValue($callable[0]); diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 58666ef9478cc..46878eb7af51c 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -347,8 +347,8 @@ private function addServiceInlinedDefinitions($id, $definition) $code .= $this->addNewInstance($sDefinition, '$'.$name, ' = ', $id); if (!$this->hasReference($id, $sDefinition->getMethodCalls(), true) && !$this->hasReference($id, $sDefinition->getProperties(), true)) { - $code .= $this->addServiceMethodCalls(null, $sDefinition, $name); $code .= $this->addServiceProperties(null, $sDefinition, $name); + $code .= $this->addServiceMethodCalls(null, $sDefinition, $name); $code .= $this->addServiceConfigurator(null, $sDefinition, $name); } @@ -517,8 +517,8 @@ private function addServiceInlinedDefinitionsSetup($id, Definition $definition) } $name = (string) $this->definitionVariables->offsetGet($iDefinition); - $code .= $this->addServiceMethodCalls(null, $iDefinition, $name); $code .= $this->addServiceProperties(null, $iDefinition, $name); + $code .= $this->addServiceMethodCalls(null, $iDefinition, $name); $code .= $this->addServiceConfigurator(null, $iDefinition, $name); } @@ -678,8 +678,8 @@ private function addService($id, Definition $definition) $this->addServiceInlinedDefinitions($id, $definition). $this->addServiceInstance($id, $definition). $this->addServiceInlinedDefinitionsSetup($id, $definition). - $this->addServiceMethodCalls($id, $definition). $this->addServiceProperties($id, $definition). + $this->addServiceMethodCalls($id, $definition). $this->addServiceConfigurator($id, $definition). $this->addServiceReturn($id, $definition) ; diff --git a/src/Symfony/Component/DependencyInjection/ParameterBag/EnvPlaceholderParameterBag.php b/src/Symfony/Component/DependencyInjection/ParameterBag/EnvPlaceholderParameterBag.php index 46d2a739dc5e5..d20e53531aa3b 100644 --- a/src/Symfony/Component/DependencyInjection/ParameterBag/EnvPlaceholderParameterBag.php +++ b/src/Symfony/Component/DependencyInjection/ParameterBag/EnvPlaceholderParameterBag.php @@ -41,8 +41,8 @@ public function get($name) if ($this->has($name)) { $defaultValue = parent::get($name); - if (!is_scalar($defaultValue)) { - throw new RuntimeException(sprintf('The default value of an env() parameter must be scalar, but "%s" given to "%s".', gettype($defaultValue), $name)); + if (null !== $defaultValue && !is_scalar($defaultValue)) { + throw new RuntimeException(sprintf('The default value of an env() parameter must be scalar or null, but "%s" given to "%s".', gettype($defaultValue), $name)); } } @@ -96,8 +96,8 @@ public function resolve() } if (is_numeric($default = $this->parameters[$name])) { $this->parameters[$name] = (string) $default; - } elseif (null !== $default && !is_string($default)) { - throw new RuntimeException(sprintf('The default value of env parameter "%s" must be string or null, %s given.', $env, gettype($default))); + } elseif (null !== $default && !is_scalar($default)) { + throw new RuntimeException(sprintf('The default value of env parameter "%s" must be scalar or null, %s given.', $env, gettype($default))); } } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index a833b9baf1755..155da75fb7958 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -20,6 +20,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Loader\ClosureLoader; @@ -27,6 +28,7 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition; use Symfony\Component\ExpressionLanguage\Expression; class ContainerBuilderTest extends \PHPUnit_Framework_TestCase @@ -265,6 +267,18 @@ public function testSetReplacesAlias() $this->assertSame($foo, $builder->get('alias'), '->set() replaces an existing alias'); } + public function testAliasesKeepInvalidBehavior() + { + $builder = new ContainerBuilder(); + + $aliased = new Definition('stdClass'); + $aliased->addMethodCall('setBar', array(new Reference('bar', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))); + $builder->setDefinition('aliased', $aliased); + $builder->setAlias('alias', 'aliased'); + + $this->assertEquals(new \stdClass(), $builder->get('alias')); + } + public function testAddGetCompilerPass() { $builder = new ContainerBuilder(); @@ -412,6 +426,28 @@ public function testResolveServices() $this->assertEquals($builder->get('foo'), $builder->resolveServices(new Expression('service("foo")')), '->resolveServices() resolves expressions'); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Constructing service "foo" from a parent definition is not supported at build time. + */ + public function testResolveServicesWithDecoratedDefinition() + { + $builder = new ContainerBuilder(); + $builder->setDefinition('grandpa', new Definition('stdClass')); + $builder->setDefinition('parent', new DefinitionDecorator('grandpa')); + $builder->setDefinition('foo', new DefinitionDecorator('parent')); + + $builder->get('foo'); + } + + public function testResolveServicesWithCustomDefinitionClass() + { + $builder = new ContainerBuilder(); + $builder->setDefinition('foo', new CustomDefinition('stdClass')); + + $this->assertInstanceOf('stdClass', $builder->get('foo')); + } + public function testMerge() { $container = new ContainerBuilder(new ParameterBag(array('bar' => 'foo'))); @@ -760,6 +796,20 @@ public function testLazyLoadedService() $this->assertTrue($classInList); } + public function testInitializePropertiesBeforeMethodCalls() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass'); + $container->register('bar', 'MethodCallClass') + ->setProperty('simple', 'bar') + ->setProperty('complex', new Reference('foo')) + ->addMethodCall('callMe'); + + $container->compile(); + + $this->assertTrue($container->get('bar')->callPassed(), '->compile() initializes properties before method calls'); + } + public function testAutowiring() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index f6d4cdd6ac7c7..b4197d4326db3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -321,4 +321,23 @@ public function testInlinedDefinitionReferencingServiceContainer() $dumper = new PhpDumper($container); $this->assertStringEqualsFile(self::$fixturesPath.'/php/services13.php', $dumper->dump(), '->dump() dumps inline definitions which reference service_container'); } + + public function testInitializePropertiesBeforeMethodCalls() + { + require_once self::$fixturesPath.'/includes/classes.php'; + + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass'); + $container->register('bar', 'MethodCallClass') + ->setProperty('simple', 'bar') + ->setProperty('complex', new Reference('foo')) + ->addMethodCall('callMe'); + $container->compile(); + + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Properties_Before_Method_Calls'))); + + $container = new \Symfony_DI_PhpDumper_Test_Properties_Before_Method_Calls(); + $this->assertTrue($container->get('bar')->callPassed(), '->dump() initializes properties before method calls'); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CustomDefinition.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CustomDefinition.php new file mode 100644 index 0000000000000..65eea2106ed70 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CustomDefinition.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Fixtures; + +use Symfony\Component\DependencyInjection\Definition; + +class CustomDefinition extends Definition +{ +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php index 70c3d275b8898..48b687c1f4e53 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php @@ -59,3 +59,20 @@ public function __construct(BarClass $bar) $this->bar = $bar; } } + +class MethodCallClass +{ + public $simple; + public $complex; + private $callPassed = false; + + public function callMe() + { + $this->callPassed = is_scalar($this->simple) && is_object($this->complex); + } + + public function callPassed() + { + return $this->callPassed; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php index 7c6760d2d2b3c..306375d1b5541 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php @@ -228,11 +228,11 @@ protected function getFooService() $this->services['foo'] = $instance = \Bar\FooClass::getInstance('foo', $a, array($this->getParameter('foo') => 'foo is '.$this->getParameter('foo').'', 'foobar' => $this->getParameter('foo')), true, $this); - $instance->setBar($this->get('bar')); - $instance->initialize(); $instance->foo = 'bar'; $instance->moo = $a; $instance->qux = array($this->getParameter('foo') => 'foo is '.$this->getParameter('foo').'', 'foobar' => $this->getParameter('foo')); + $instance->setBar($this->get('bar')); + $instance->initialize(); sc_configure($instance); return $instance; @@ -425,8 +425,8 @@ protected function getInlinedService() { $this->services['inlined'] = $instance = new \Bar(); - $instance->setBaz($this->get('baz')); $instance->pub = 'pub'; + $instance->setBaz($this->get('baz')); 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 2ccadfe16d384..f8b263c2a01f3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -224,11 +224,11 @@ protected function getFooService() $this->services['foo'] = $instance = \Bar\FooClass::getInstance('foo', $a, array('bar' => 'foo is bar', 'foobar' => 'bar'), true, $this); - $instance->setBar($this->get('bar')); - $instance->initialize(); $instance->foo = 'bar'; $instance->moo = $a; $instance->qux = array('bar' => 'foo is bar', 'foobar' => 'bar'); + $instance->setBar($this->get('bar')); + $instance->initialize(); sc_configure($instance); return $instance; @@ -275,8 +275,8 @@ protected function getFooWithInlineService() $this->services['foo_with_inline'] = $instance = new \Foo(); - $a->setBaz($this->get('baz')); $a->pub = 'pub'; + $a->setBaz($this->get('baz')); $instance->setBar($a); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 7d5f95df14889..5a166d963dc67 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -131,8 +131,8 @@ public function testLoadImports() { $container = new ContainerBuilder(); $resolver = new LoaderResolver(array( - new IniFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')), - new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')), + new IniFileLoader($container, new FileLocator(self::$fixturesPath.'/ini')), + new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yml')), $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')), )); $loader->setResolver($resolver); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index dccce3ac2e17a..fabaa4859c578 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -104,8 +104,8 @@ public function testLoadImports() { $container = new ContainerBuilder(); $resolver = new LoaderResolver(array( - new IniFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')), - new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')), + new IniFileLoader($container, new FileLocator(self::$fixturesPath.'/ini')), + new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')), new PhpFileLoader($container, new FileLocator(self::$fixturesPath.'/php')), $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')), )); diff --git a/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/EnvPlaceholderParameterBagTest.php b/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/EnvPlaceholderParameterBagTest.php index 1a24b3d6e2663..c898038e39402 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/EnvPlaceholderParameterBagTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/EnvPlaceholderParameterBagTest.php @@ -130,7 +130,7 @@ public function testResolveEnvAllowsNull() /** * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage The default value of env parameter "ARRAY_VAR" must be string or null, array given. + * @expectedExceptionMessage The default value of env parameter "ARRAY_VAR" must be scalar or null, array given. */ public function testResolveThrowsOnBadDefaultValue() { @@ -139,4 +139,26 @@ public function testResolveThrowsOnBadDefaultValue() $bag->set('env(Array_Var)', array()); $bag->resolve(); } + + public function testGetEnvAllowsNull() + { + $bag = new EnvPlaceholderParameterBag(); + $bag->set('env(NULL_VAR)', null); + $bag->get('env(NULL_VAR)'); + $bag->resolve(); + + $this->assertNull($bag->all()['env(null_var)']); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage The default value of an env() parameter must be scalar or null, but "array" given to "env(ARRAY_VAR)". + */ + public function testGetThrowsOnBadDefaultValue() + { + $bag = new EnvPlaceholderParameterBag(); + $bag->set('env(ARRAY_VAR)', array()); + $bag->get('env(ARRAY_VAR)'); + $bag->resolve(); + } } diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php index ea26b4df6625d..33a15900cad5e 100644 --- a/src/Symfony/Component/DomCrawler/Crawler.php +++ b/src/Symfony/Component/DomCrawler/Crawler.php @@ -930,7 +930,7 @@ public static function xpathLiteral($s) } } - return sprintf('concat(%s)', implode($parts, ', ')); + return sprintf('concat(%s)', implode(', ', $parts)); } /** diff --git a/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php b/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php index c429417c25f80..c479daa75ee78 100644 --- a/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php +++ b/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php @@ -155,11 +155,11 @@ public function setValue($value) /** * Adds a choice to the current ones. * - * This method should only be used internally. - * * @param \DOMElement $node * * @throws \LogicException When choice provided is not multiple nor radio + * + * @internal */ public function addChoice(\DOMElement $node) { diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index 1aeac3ad264f0..c1ec8a4139114 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -1388,7 +1388,7 @@ public function testDumpFileWithFileScheme() $scheme = 'file://'; $filename = $scheme.$this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt'; - $this->filesystem->dumpFile($filename, 'bar', null); + $this->filesystem->dumpFile($filename, 'bar'); $this->assertFileExists($filename); $this->assertSame('bar', file_get_contents($filename)); @@ -1399,7 +1399,7 @@ public function testDumpFileWithZlibScheme() $scheme = 'compress.zlib://'; $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt'; - $this->filesystem->dumpFile($filename, 'bar', null); + $this->filesystem->dumpFile($filename, 'bar'); // Zlib stat uses file:// wrapper so remove scheme $this->assertFileExists(str_replace($scheme, '', $filename)); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php index 3295e3d85fe8c..af6443ac84054 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php @@ -87,7 +87,7 @@ public function testTransformWithRounding($input, $output, $roundingMode) public function testReverseTransform() { // Since we test against "de_AT", we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); @@ -109,7 +109,7 @@ public function testReverseTransformEmpty() public function testReverseTransformWithGrouping() { // Since we test against "de_DE", we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_DE'); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php index 99e4c8a574cf2..0fa2df05641a4 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php @@ -19,7 +19,7 @@ class MoneyToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase public function testTransform() { // Since we test against "de_AT", we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); @@ -47,7 +47,7 @@ public function testTransformEmpty() public function testReverseTransform() { // Since we test against "de_AT", we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php index 2452796a6935f..306cd225b20f2 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php @@ -42,7 +42,7 @@ public function provideTransformations() public function testTransform($from, $to, $locale) { // Since we test against other locales, we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault($locale); @@ -68,7 +68,7 @@ public function provideTransformationsWithGrouping() public function testTransformWithGrouping($from, $to, $locale) { // Since we test against other locales, we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault($locale); @@ -80,7 +80,7 @@ public function testTransformWithGrouping($from, $to, $locale) public function testTransformWithScale() { // Since we test against "de_AT", we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); @@ -185,7 +185,7 @@ public function transformWithRoundingProvider() public function testTransformWithRounding($scale, $input, $output, $roundingMode) { // Since we test against "de_AT", we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); @@ -197,7 +197,7 @@ public function testTransformWithRounding($scale, $input, $output, $roundingMode public function testTransformDoesNotRoundIfNoScale() { // Since we test against "de_AT", we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); @@ -212,7 +212,7 @@ public function testTransformDoesNotRoundIfNoScale() public function testReverseTransform($to, $from, $locale) { // Since we test against other locales, we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault($locale); @@ -227,7 +227,7 @@ public function testReverseTransform($to, $from, $locale) public function testReverseTransformWithGrouping($to, $from, $locale) { // Since we test against other locales, we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault($locale); @@ -242,7 +242,7 @@ public function testReverseTransformWithGrouping($to, $from, $locale) public function testReverseTransformWithGroupingAndFixedSpaces() { // Since we test against other locales, we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('ru'); @@ -254,7 +254,7 @@ public function testReverseTransformWithGroupingAndFixedSpaces() public function testReverseTransformWithGroupingButWithoutGroupSeparator() { // Since we test against "de_AT", we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); @@ -374,7 +374,7 @@ public function testReverseTransformDoesNotRoundIfNoScale() public function testDecimalSeparatorMayBeDotIfGroupingSeparatorIsNotDot() { // Since we test against other locales, we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('fr'); $transformer = new NumberToLocalizedStringTransformer(null, true); @@ -394,7 +394,7 @@ public function testDecimalSeparatorMayBeDotIfGroupingSeparatorIsNotDot() public function testDecimalSeparatorMayNotBeDotIfGroupingSeparatorIsDot() { // Since we test against "de_DE", we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_DE'); @@ -409,7 +409,7 @@ public function testDecimalSeparatorMayNotBeDotIfGroupingSeparatorIsDot() public function testDecimalSeparatorMayNotBeDotIfGroupingSeparatorIsDotWithNoGroupSep() { // Since we test against "de_DE", we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_DE'); @@ -421,7 +421,7 @@ public function testDecimalSeparatorMayNotBeDotIfGroupingSeparatorIsDotWithNoGro public function testDecimalSeparatorMayBeDotIfGroupingSeparatorIsDotButNoGroupingUsed() { // Since we test against other locales, we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('fr'); $transformer = new NumberToLocalizedStringTransformer(); @@ -433,7 +433,7 @@ public function testDecimalSeparatorMayBeDotIfGroupingSeparatorIsDotButNoGroupin public function testDecimalSeparatorMayBeCommaIfGroupingSeparatorIsNotComma() { // Since we test against other locales, we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('bg'); $transformer = new NumberToLocalizedStringTransformer(null, true); @@ -585,7 +585,7 @@ public function testReverseTransformDisallowsCenteredExtraCharacters() public function testReverseTransformDisallowsCenteredExtraCharactersMultibyte() { // Since we test against other locales, we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('ru'); @@ -601,7 +601,7 @@ public function testReverseTransformDisallowsCenteredExtraCharactersMultibyte() public function testReverseTransformIgnoresTrailingSpacesInExceptionMessage() { // Since we test against other locales, we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('ru'); @@ -628,7 +628,7 @@ public function testReverseTransformDisallowsTrailingExtraCharacters() public function testReverseTransformDisallowsTrailingExtraCharactersMultibyte() { // Since we test against other locales, we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('ru'); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php index 99f06741b90d7..c0447656f3e51 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php @@ -53,7 +53,7 @@ public function testTransformWithInteger() public function testTransformWithScale() { // Since we test against "de_AT", we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); @@ -92,7 +92,7 @@ public function testReverseTransformWithInteger() public function testReverseTransformWithScale() { // Since we test against "de_AT", we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php index fb7c4b0bde6f8..e006e075d4c78 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php @@ -19,7 +19,7 @@ class CountryTypeTest extends TestCase { protected function setUp() { - IntlTestHelper::requireIntl($this); + IntlTestHelper::requireIntl($this, false); parent::setUp(); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php index 25e7fdddb9488..cd894a52c3260 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php @@ -19,7 +19,7 @@ class CurrencyTypeTest extends TestCase { protected function setUp() { - IntlTestHelper::requireIntl($this); + IntlTestHelper::requireIntl($this, false); parent::setUp(); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php index 1ee36fe0d6660..9b33db2a654ac 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php @@ -70,7 +70,7 @@ public function testSubmitFromSingleTextDateTimeWithDefaultFormat() public function testSubmitFromSingleTextDateTime() { // we test against "de_AT", so we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); @@ -91,7 +91,7 @@ public function testSubmitFromSingleTextDateTime() public function testSubmitFromSingleTextString() { // we test against "de_AT", so we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); @@ -112,7 +112,7 @@ public function testSubmitFromSingleTextString() public function testSubmitFromSingleTextTimestamp() { // we test against "de_AT", so we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); @@ -135,7 +135,7 @@ public function testSubmitFromSingleTextTimestamp() public function testSubmitFromSingleTextRaw() { // we test against "de_AT", so we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); @@ -399,7 +399,7 @@ public function testThrowExceptionIfDaysIsInvalid() public function testSetDataWithNegativeTimezoneOffsetStringInput() { // we test against "de_AT", so we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); @@ -421,7 +421,7 @@ public function testSetDataWithNegativeTimezoneOffsetStringInput() public function testSetDataWithNegativeTimezoneOffsetDateTimeInput() { // we test against "de_AT", so we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); @@ -495,7 +495,7 @@ public function testMonthsOptionShortFormat() public function testMonthsOptionLongFormat() { // we test against "de_AT", so we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); @@ -515,7 +515,7 @@ public function testMonthsOptionLongFormat() public function testMonthsOptionLongFormatWithDifferentTimezone() { // we test against "de_AT", so we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); @@ -621,7 +621,7 @@ public function testIsPartiallyFilledReturnsTrueIfChoiceAndDayEmpty() public function testPassDatePatternToView() { // we test against "de_AT", so we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); @@ -634,7 +634,7 @@ public function testPassDatePatternToView() public function testPassDatePatternToViewDifferentFormat() { // we test against "de_AT", so we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); @@ -682,7 +682,7 @@ public function testDontPassDatePatternIfText() public function testDatePatternFormatWithQuotedStrings() { // we test against "es_ES", so we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('es_ES'); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php index dd1a7c549f082..c5a6236d1b13e 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php @@ -18,7 +18,7 @@ class IntegerTypeTest extends TestCase { protected function setUp() { - IntlTestHelper::requireIntl($this); + IntlTestHelper::requireIntl($this, false); parent::setUp(); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php index cb59fa2001f48..ca03ef3205aa3 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php @@ -19,7 +19,7 @@ class LanguageTypeTest extends TestCase { protected function setUp() { - IntlTestHelper::requireIntl($this); + IntlTestHelper::requireIntl($this, false); parent::setUp(); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php index 8c56bcc9584f3..2a46755bdc64e 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php @@ -19,7 +19,7 @@ class LocaleTypeTest extends TestCase { protected function setUp() { - IntlTestHelper::requireIntl($this); + IntlTestHelper::requireIntl($this, false); parent::setUp(); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php index bd01f8eda2396..3938454c3a4a4 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php @@ -20,7 +20,7 @@ protected function setUp() { // we test against different locales, so we need the full // implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); parent::setUp(); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php index 1020260d96849..b8675d2684679 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php @@ -21,7 +21,7 @@ protected function setUp() parent::setUp(); // we test against "de_DE", so we need the full implementation - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_DE'); } diff --git a/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php b/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php index 32f591c860698..534d74ea6f0e4 100644 --- a/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php +++ b/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php @@ -14,9 +14,9 @@ /** * Iterator for {@link OrderedHashMap} objects. * - * This class is internal and should not be used. - * * @author Bernhard Schussek + * + * @internal */ class OrderedHashMapIterator implements \Iterator { diff --git a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php index a39984dffa717..f95d90c364051 100644 --- a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php +++ b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php @@ -190,7 +190,7 @@ public function prepare(Request $request) if (!$this->headers->has('Accept-Ranges')) { // Only accept ranges on safe HTTP methods - $this->headers->set('Accept-Ranges', $request->isMethodSafe() ? 'bytes' : 'none'); + $this->headers->set('Accept-Ranges', $request->isMethodSafe(false) ? 'bytes' : 'none'); } if (!$this->headers->has('Content-Type')) { diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index f857ac74fc3be..4c383ec69da20 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1475,10 +1475,22 @@ public function isMethod($method) /** * Checks whether or not the method is safe. * + * @see https://tools.ietf.org/html/rfc7231#section-4.2.1 + * + * @param bool $andCacheable Adds the additional condition that the method should be cacheable. True by default. + * * @return bool */ - public function isMethodSafe() + public function isMethodSafe(/* $andCacheable = true */) { + if (!func_num_args() || func_get_arg(0)) { + // This deprecation should be turned into a BadMethodCallException in 4.0 (without adding the argument in the signature) + // then setting $andCacheable to false should be deprecated in 4.1 + @trigger_error('Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is deprecated since version 3.2 and will throw an exception in 4.0. Disable checking only for cacheable methods by calling the method with `false` as first argument or use the Request::isMethodCacheable() instead.', E_USER_DEPRECATED); + + return in_array($this->getMethod(), array('GET', 'HEAD')); + } + return in_array($this->getMethod(), array('GET', 'HEAD', 'OPTIONS', 'TRACE')); } @@ -1495,6 +1507,8 @@ public function isMethodIdempotent() /** * Checks whether the method is cacheable or not. * + * @see https://tools.ietf.org/html/rfc7231#section-4.2.3 + * * @return bool */ public function isMethodCacheable() diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 238be7601d321..9e8e5fe71c750 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -1994,7 +1994,7 @@ public function testMethodSafe($method, $safe) { $request = new Request(); $request->setMethod($method); - $this->assertEquals($safe, $request->isMethodSafe()); + $this->assertEquals($safe, $request->isMethodSafe(false)); } public function methodSafeProvider() @@ -2013,6 +2013,17 @@ public function methodSafeProvider() ); } + /** + * @group legacy + * @expectedDeprecation Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is deprecated since version 3.2 and will throw an exception in 4.0. Disable checking only for cacheable methods by calling the method with `false` as first argument or use the Request::isMethodCacheable() instead. + */ + public function testMethodSafeChecksCacheable() + { + $request = new Request(); + $request->setMethod('OPTIONS'); + $this->assertFalse($request->isMethodSafe()); + } + /** * @dataProvider methodCacheableProvider */ diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php index 1f8538a9f2522..7bca1ddfe199f 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php @@ -130,7 +130,7 @@ private function decorateVar($var) return new ClassStub($var); } } - if (false !== strpos($var, DIRECTORY_SEPARATOR) && false === strpos($var, '://') && file_exists($var)) { + if (false !== strpos($var, DIRECTORY_SEPARATOR) && false === strpos($var, '://') && false === strpos($var, "\0") && is_file($var)) { return new LinkStub($var); } } diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php index f0c18c51e51e7..55c2f0e6f50a4 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php @@ -12,10 +12,8 @@ namespace Symfony\Component\HttpKernel\DataCollector; use Symfony\Component\HttpFoundation\ParameterBag; -use Symfony\Component\HttpFoundation\HeaderBag; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Event\FilterControllerEvent; @@ -54,14 +52,11 @@ public function collect(Request $request, Response $response, \Exception $except $attributes = array(); $route = ''; foreach ($request->attributes->all() as $key => $value) { - if ('_route' === $key && is_object($value)) { - $attributes[$key] = $this->cloneVar($value->getPath()); - } else { - $attributes[$key] = $this->cloneVar($value); - } - if ('_route' === $key) { $route = is_object($value) ? $value->getPath() : $value; + $attributes[$key] = $route; + } else { + $attributes[$key] = $value; } } @@ -97,8 +92,8 @@ public function collect(Request $request, Response $response, \Exception $except 'content_type' => $response->headers->get('Content-Type', 'text/html'), 'status_text' => isset(Response::$statusTexts[$statusCode]) ? Response::$statusTexts[$statusCode] : '', 'status_code' => $statusCode, - 'request_query' => array_map(array($this, 'cloneVar'), $request->query->all()), - 'request_request' => array_map(array($this, 'cloneVar'), $request->request->all()), + 'request_query' => $request->query->all(), + 'request_request' => $request->request->all(), 'request_headers' => $request->headers->all(), 'request_server' => $request->server->all(), 'request_cookies' => $request->cookies->all(), @@ -125,6 +120,18 @@ public function collect(Request $request, Response $response, \Exception $except $this->data['request_request']['_password'] = '******'; } + foreach ($this->data as $key => $value) { + if (!is_array($value)) { + continue; + } + if ('request_headers' === $key || 'response_headers' === $key) { + $value = array_map(function ($v) { return isset($v[1]) ? $v : $v[0]; }, $value); + } + if ('request_server' !== $key && 'request_cookies' !== $key) { + $this->data[$key] = array_map(array($this, 'cloneVar'), $value); + } + } + if (isset($this->controllers[$request])) { $this->data['controller'] = $this->parseController($this->controllers[$request]); unset($this->controllers[$request]); @@ -170,17 +177,17 @@ public function getRequestQuery() public function getRequestHeaders() { - return new HeaderBag($this->data['request_headers']); + return new ParameterBag($this->data['request_headers']); } - public function getRequestServer() + public function getRequestServer($raw = false) { - return new ParameterBag($this->data['request_server']); + return new ParameterBag($raw ? $this->data['request_server'] : array_map(array($this, 'cloneVar'), $this->data['request_server'])); } - public function getRequestCookies() + public function getRequestCookies($raw = false) { - return new ParameterBag($this->data['request_cookies']); + return new ParameterBag($raw ? $this->data['request_cookies'] : array_map(array($this, 'cloneVar'), $this->data['request_cookies'])); } public function getRequestAttributes() @@ -190,7 +197,7 @@ public function getRequestAttributes() public function getResponseHeaders() { - return new ResponseHeaderBag($this->data['response_headers']); + return new ParameterBag($this->data['response_headers']); } public function getSessionMetadata() @@ -264,7 +271,17 @@ public function getIdentifier() */ public function getRouteParams() { - return isset($this->data['request_attributes']['_route_params']) ? $this->data['request_attributes']['_route_params'] : $this->cloneVar(array()); + if (!isset($this->data['request_attributes']['_route_params'])) { + return array(); + } + + $data = $this->data['request_attributes']['_route_params']; + $params = array(); + foreach ($data->getRawData()[1] as $k => $v) { + $params[$k] = $data->seek($k); + } + + return $params; } /** diff --git a/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php b/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php index 9fdaccfaf6857..37bf15c3a084f 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php @@ -81,7 +81,7 @@ public function onKernelRequest(GetResponseEvent $event) protected function validateRequest(Request $request) { // is the Request safe? - if (!$request->isMethodSafe()) { + if (!$request->isMethodSafe(false)) { throw new AccessDeniedHttpException(); } diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index 3e7ff0aab8251..686f0b852c202 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -182,7 +182,7 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ } $this->traces[$request->getMethod().' '.$path] = array(); - if (!$request->isMethodSafe()) { + if (!$request->isMethodSafe(false)) { $response = $this->invalidate($request, $catch); } elseif ($request->headers->has('expect') || !$request->isMethodCacheable()) { $response = $this->pass($request, $catch); diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index e4ca24c4851cf..0bd98579b5a7a 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -58,12 +58,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '3.2.0-RC1'; + const VERSION = '3.2.0-RC2'; const VERSION_ID = 30200; const MAJOR_VERSION = 3; const MINOR_VERSION = 2; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'RC1'; + const EXTRA_VERSION = 'RC2'; const END_OF_MAINTENANCE = '07/2017'; const END_OF_LIFE = '01/2018'; diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php index 2ae1c5c09bb49..245cd4c159693 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php @@ -39,7 +39,7 @@ public function testCollect() $attributes = $c->getRequestAttributes(); $this->assertSame('request', $c->getName()); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\HeaderBag', $c->getRequestHeaders()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $c->getRequestHeaders()); $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $c->getRequestServer()); $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $c->getRequestCookies()); $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $attributes); @@ -47,13 +47,13 @@ public function testCollect() $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $c->getRequestQuery()); $this->assertSame('html', $c->getFormat()); $this->assertEquals('foobar', $c->getRoute()); - $this->assertEquals($cloner->cloneVar(array('name' => 'foo')), $c->getRouteParams()); + $this->assertEquals(array('name' => $cloner->cloneVar(array('name' => 'foo'))->seek('name')), $c->getRouteParams()); $this->assertSame(array(), $c->getSessionAttributes()); $this->assertSame('en', $c->getLocale()); $this->assertEquals($cloner->cloneVar($request->attributes->get('resource')), $attributes->get('resource')); $this->assertEquals($cloner->cloneVar($request->attributes->get('object')), $attributes->get('object')); - $this->assertInstanceOf('Symfony\Component\HttpFoundation\HeaderBag', $c->getResponseHeaders()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $c->getResponseHeaders()); $this->assertSame('OK', $c->getStatusText()); $this->assertSame(200, $c->getStatusCode()); $this->assertSame('application/json', $c->getContentType()); diff --git a/src/Symfony/Component/Intl/Tests/Collator/Verification/CollatorTest.php b/src/Symfony/Component/Intl/Tests/Collator/Verification/CollatorTest.php index 5da56cc9af8c8..378463cac854e 100644 --- a/src/Symfony/Component/Intl/Tests/Collator/Verification/CollatorTest.php +++ b/src/Symfony/Component/Intl/Tests/Collator/Verification/CollatorTest.php @@ -24,7 +24,7 @@ class CollatorTest extends AbstractCollatorTest { protected function setUp() { - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); parent::setUp(); } diff --git a/src/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php b/src/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php index 931aa2bad0754..621fd33e5d7f9 100644 --- a/src/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php +++ b/src/Symfony/Component/Intl/Tests/DateFormatter/AbstractIntlDateFormatterTest.php @@ -832,9 +832,7 @@ protected function getDateTime($timestamp, $timeZone) { $dateTime = new \DateTime(); $dateTime->setTimestamp(null === $timestamp ? time() : $timestamp); - if (null !== $timeZone) { - $dateTime->setTimezone(new \DateTimeZone($timeZone)); - } + $dateTime->setTimezone(new \DateTimeZone($timeZone ?: getenv('TZ') ?: 'UTC')); return $dateTime; } diff --git a/src/Symfony/Component/Intl/Tests/DateFormatter/Verification/IntlDateFormatterTest.php b/src/Symfony/Component/Intl/Tests/DateFormatter/Verification/IntlDateFormatterTest.php index 7eca02e2072ae..a5d20638c1ebe 100644 --- a/src/Symfony/Component/Intl/Tests/DateFormatter/Verification/IntlDateFormatterTest.php +++ b/src/Symfony/Component/Intl/Tests/DateFormatter/Verification/IntlDateFormatterTest.php @@ -25,13 +25,15 @@ class IntlDateFormatterTest extends AbstractIntlDateFormatterTest { protected function setUp() { - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); parent::setUp(); } protected function getDateFormatter($locale, $datetype, $timetype, $timezone = null, $calendar = IntlDateFormatter::GREGORIAN, $pattern = null) { + IntlTestHelper::requireFullIntl($this, '55.1'); + if (!$formatter = new \IntlDateFormatter($locale, $datetype, $timetype, $timezone, $calendar, $pattern)) { throw new \InvalidArgumentException(intl_get_error_message()); } diff --git a/src/Symfony/Component/Intl/Tests/Globals/Verification/IntlGlobalsTest.php b/src/Symfony/Component/Intl/Tests/Globals/Verification/IntlGlobalsTest.php index c46033d8805a9..b5cd1c13c32ff 100644 --- a/src/Symfony/Component/Intl/Tests/Globals/Verification/IntlGlobalsTest.php +++ b/src/Symfony/Component/Intl/Tests/Globals/Verification/IntlGlobalsTest.php @@ -24,7 +24,7 @@ class IntlGlobalsTest extends AbstractIntlGlobalsTest { protected function setUp() { - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); parent::setUp(); } diff --git a/src/Symfony/Component/Intl/Tests/Locale/Verification/LocaleTest.php b/src/Symfony/Component/Intl/Tests/Locale/Verification/LocaleTest.php index 39d4f3cb03b36..adfec280a7997 100644 --- a/src/Symfony/Component/Intl/Tests/Locale/Verification/LocaleTest.php +++ b/src/Symfony/Component/Intl/Tests/Locale/Verification/LocaleTest.php @@ -24,7 +24,7 @@ class LocaleTest extends AbstractLocaleTest { protected function setUp() { - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); parent::setUp(); } diff --git a/src/Symfony/Component/Intl/Tests/NumberFormatter/Verification/NumberFormatterTest.php b/src/Symfony/Component/Intl/Tests/NumberFormatter/Verification/NumberFormatterTest.php index 28e6fe9090302..181b489c1564d 100644 --- a/src/Symfony/Component/Intl/Tests/NumberFormatter/Verification/NumberFormatterTest.php +++ b/src/Symfony/Component/Intl/Tests/NumberFormatter/Verification/NumberFormatterTest.php @@ -22,7 +22,7 @@ class NumberFormatterTest extends AbstractNumberFormatterTest { protected function setUp() { - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, '55.1'); parent::setUp(); } @@ -32,6 +32,13 @@ public function testCreate() $this->assertInstanceOf('\NumberFormatter', \NumberFormatter::create('en', \NumberFormatter::DECIMAL)); } + public function testGetTextAttribute() + { + IntlTestHelper::requireFullIntl($this); + + parent::testGetTextAttribute(); + } + protected function getNumberFormatter($locale = 'en', $style = null, $pattern = null) { return new \NumberFormatter($locale, $style, $pattern); diff --git a/src/Symfony/Component/Intl/Util/IntlTestHelper.php b/src/Symfony/Component/Intl/Util/IntlTestHelper.php index ecd5ed6b660d6..71fb4acdd72fa 100644 --- a/src/Symfony/Component/Intl/Util/IntlTestHelper.php +++ b/src/Symfony/Component/Intl/Util/IntlTestHelper.php @@ -28,19 +28,21 @@ class IntlTestHelper { /** * Should be called before tests that work fine with the stub implementation. - * - * @param \PhpUnit_Framework_TestCase $testCase */ - public static function requireIntl(\PHPUnit_Framework_TestCase $testCase) + public static function requireIntl(\PHPUnit_Framework_TestCase $testCase, $minimumIcuVersion = null) { + if (null === $minimumIcuVersion) { + $minimumIcuVersion = Intl::getIcuStubVersion(); + } + // We only run tests if the version is *one specific version*. // This condition is satisfied if // // * the intl extension is loaded with version Intl::getIcuStubVersion() // * the intl extension is not loaded - if (IcuVersion::compare(Intl::getIcuVersion(), Intl::getIcuStubVersion(), '!=', 1)) { - $testCase->markTestSkipped('ICU version '.Intl::getIcuStubVersion().' is required.'); + if (($minimumIcuVersion || defined('HHVM_VERSION_ID')) && IcuVersion::compare(Intl::getIcuVersion(), $minimumIcuVersion, '!=', 1)) { + $testCase->markTestSkipped('ICU version '.$minimumIcuVersion.' is required.'); } // Normalize the default locale in case this is not done explicitly @@ -60,24 +62,15 @@ public static function requireIntl(\PHPUnit_Framework_TestCase $testCase) /** * Should be called before tests that require a feature-complete intl * implementation. - * - * @param \PhpUnit_Framework_TestCase $testCase */ - public static function requireFullIntl(\PHPUnit_Framework_TestCase $testCase) + public static function requireFullIntl(\PHPUnit_Framework_TestCase $testCase, $minimumIcuVersion = null) { // We only run tests if the intl extension is loaded... if (!Intl::isExtensionLoaded()) { $testCase->markTestSkipped('Extension intl is required.'); } - // ... and only if the version is *one specific version* - if (IcuVersion::compare(Intl::getIcuVersion(), Intl::getIcuStubVersion(), '!=', 1)) { - $testCase->markTestSkipped('ICU version '.Intl::getIcuStubVersion().' is required.'); - } - - // Normalize the default locale in case this is not done explicitly - // in the test - \Locale::setDefault('en'); + self::requireIntl($testCase, $minimumIcuVersion); // Consequently, tests will // @@ -89,8 +82,6 @@ public static function requireFullIntl(\PHPUnit_Framework_TestCase $testCase) /** * Skips the test unless the current system has a 32bit architecture. - * - * @param \PhpUnit_Framework_TestCase $testCase */ public static function require32Bit(\PHPUnit_Framework_TestCase $testCase) { @@ -101,8 +92,6 @@ public static function require32Bit(\PHPUnit_Framework_TestCase $testCase) /** * Skips the test unless the current system has a 64bit architecture. - * - * @param \PhpUnit_Framework_TestCase $testCase */ public static function require64Bit(\PHPUnit_Framework_TestCase $testCase) { diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index 39493d72e01ab..6282f17be2296 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -573,6 +573,7 @@ public function getIterator($flags = 0) yield self::OUT => ''; } + $this->checkTimeout(); $this->readPipesForOutput(__FUNCTION__, $blocking); } } @@ -992,8 +993,16 @@ public function setTty($tty) if ('\\' === DIRECTORY_SEPARATOR && $tty) { throw new RuntimeException('TTY mode is not supported on Windows platform.'); } - if ($tty && (!file_exists('/dev/tty') || !is_readable('/dev/tty'))) { - throw new RuntimeException('TTY mode requires /dev/tty to be readable.'); + if ($tty) { + static $isTtySupported; + + if (null === $isTtySupported) { + $isTtySupported = (bool) @proc_open('echo 1 >/dev/null', array(array('file', '/dev/tty', 'r'), array('file', '/dev/tty', 'w'), array('file', '/dev/tty', 'w')), $pipes); + } + + if (!$isTtySupported) { + throw new RuntimeException('TTY mode requires /dev/tty to be read/writable.'); + } } $this->tty = (bool) $tty; @@ -1281,7 +1290,7 @@ public static function isPtySupported() return $result = false; } - return $result = (bool) @proc_open('echo 1', array(array('pty'), array('pty'), array('pty')), $pipes); + return $result = (bool) @proc_open('echo 1 >/dev/null', array(array('pty'), array('pty'), array('pty')), $pipes); } /** diff --git a/src/Symfony/Component/Process/Tests/ProcessTest.php b/src/Symfony/Component/Process/Tests/ProcessTest.php index c0bc6d48598f1..811c98f15e7d8 100644 --- a/src/Symfony/Component/Process/Tests/ProcessTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessTest.php @@ -747,6 +747,27 @@ public function testRunProcessWithTimeout() throw $e; } + /** + * @expectedException \Symfony\Component\Process\Exception\ProcessTimedOutException + * @expectedExceptionMessage exceeded the timeout of 0.1 seconds. + */ + public function testIterateOverProcessWithTimeout() + { + $process = $this->getProcess(self::$phpBin.' -r "sleep(30);"'); + $process->setTimeout(0.1); + $start = microtime(true); + try { + $process->start(); + foreach ($process as $buffer); + $this->fail('A RuntimeException should have been raised'); + } catch (RuntimeException $e) { + } + + $this->assertLessThan(15, microtime(true) - $start); + + throw $e; + } + public function testCheckTimeoutOnNonStartedProcess() { $process = $this->getProcess('echo foo'); diff --git a/src/Symfony/Component/Routing/RouteCompiler.php b/src/Symfony/Component/Routing/RouteCompiler.php index 211325d20fb08..aa7a75e0b009b 100644 --- a/src/Symfony/Component/Routing/RouteCompiler.php +++ b/src/Symfony/Component/Routing/RouteCompiler.php @@ -28,13 +28,21 @@ class RouteCompiler implements RouteCompilerInterface */ const SEPARATORS = '/,;.:-_~+*=@|'; + /** + * The maximum supported length of a PCRE subpattern name + * http://pcre.org/current/doc/html/pcre2pattern.html#SEC16. + * + * @internal + */ + const VARIABLE_MAXIMUM_LENGTH = 32; + /** * {@inheritdoc} * * @throws \InvalidArgumentException If a path variable is named _fragment * @throws \LogicException If a variable is referenced more than once - * @throws \DomainException If a variable name is numeric because PHP raises an error for such - * subpatterns in PCRE and thus would break matching, e.g. "(?P<123>.+)". + * @throws \DomainException If a variable name starts with a digit or if it is too long to be successfully used as + * a PCRE subpattern. */ public static function compile(Route $route) { @@ -121,13 +129,19 @@ private static function compilePattern(Route $route, $pattern, $isHost) } $isSeparator = '' !== $precedingChar && false !== strpos(static::SEPARATORS, $precedingChar); - if (is_numeric($varName)) { - throw new \DomainException(sprintf('Variable name "%s" cannot be numeric in route pattern "%s". Please use a different name.', $varName, $pattern)); + // A PCRE subpattern name must start with a non-digit. Also a PHP variable cannot start with a digit so the + // variable would not be usable as a Controller action argument. + if (preg_match('/^\d/', $varName)) { + throw new \DomainException(sprintf('Variable name "%s" cannot start with a digit in route pattern "%s". Please use a different name.', $varName, $pattern)); } if (in_array($varName, $variables)) { throw new \LogicException(sprintf('Route pattern "%s" cannot reference variable name "%s" more than once.', $pattern, $varName)); } + if (strlen($varName) > self::VARIABLE_MAXIMUM_LENGTH) { + throw new \DomainException(sprintf('Variable name "%s" cannot be longer than %s characters in route pattern "%s". Please use a shorter name.', $varName, self::VARIABLE_MAXIMUM_LENGTH, $pattern)); + } + if ($isSeparator && $precedingText !== $precedingChar) { $tokens[] = array('text', substr($precedingText, 0, -strlen($precedingChar))); } elseif (!$isSeparator && strlen($precedingText) > 0) { diff --git a/src/Symfony/Component/Routing/Tests/RouteCompilerTest.php b/src/Symfony/Component/Routing/Tests/RouteCompilerTest.php index 79e94a22dc993..3213bf2163e06 100644 --- a/src/Symfony/Component/Routing/Tests/RouteCompilerTest.php +++ b/src/Symfony/Component/Routing/Tests/RouteCompilerTest.php @@ -283,16 +283,16 @@ public function testRouteWithFragmentAsPathParameter() } /** - * @dataProvider getNumericVariableNames + * @dataProvider getVariableNamesStartingWithADigit * @expectedException \DomainException */ - public function testRouteWithNumericVariableName($name) + public function testRouteWithVariableNameStartingWithADigit($name) { $route = new Route('/{'.$name.'}'); $route->compile(); } - public function getNumericVariableNames() + public function getVariableNamesStartingWithADigit() { return array( array('09'), @@ -371,6 +371,15 @@ public function provideCompileWithHostData() ), ); } + + /** + * @expectedException \DomainException + */ + public function testRouteWithTooLongVariableName() + { + $route = new Route(sprintf('/{%s}', str_repeat('a', RouteCompiler::VARIABLE_MAXIMUM_LENGTH + 1))); + $route->compile(); + } } class Utf8RouteCompiler extends RouteCompiler diff --git a/src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentToken.php b/src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentToken.php index d85572d0e07db..76873fc603cd7 100644 --- a/src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentToken.php @@ -12,9 +12,9 @@ namespace Symfony\Component\Security\Core\Authentication\RememberMe; /** - * This class is only used by PersistentTokenRememberMeServices internally. - * * @author Johannes M. Schmitt + * + * @internal */ final class PersistentToken implements PersistentTokenInterface { diff --git a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php index 98f5ac04be303..3c9604ea7436b 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php @@ -220,7 +220,7 @@ private function startAuthentication(Request $request, AuthenticationException $ protected function setTargetPath(Request $request) { // session isn't required when using HTTP basic authentication mechanism for example - if ($request->hasSession() && $request->isMethodSafe() && !$request->isXmlHttpRequest()) { + if ($request->hasSession() && $request->isMethodSafe(false) && !$request->isXmlHttpRequest()) { $this->saveTargetPath($request->getSession(), $this->providerKey, $request->getUri()); } } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php index e68dfe335eb29..6bf6642ff0529 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php @@ -77,9 +77,9 @@ public function testDenormalize() public function testDenormalizeUsingFormatPassedInContext() { - $this->assertEquals(new \DateTimeImmutable('2016/01/01', new \DateTimeZone('UTC')), $this->normalizer->denormalize('2016.01.01', \DateTimeInterface::class, null, array(DateTimeNormalizer::FORMAT_KEY => 'Y.m.d|'))); - $this->assertEquals(new \DateTimeImmutable('2016/01/01', new \DateTimeZone('UTC')), $this->normalizer->denormalize('2016.01.01', \DateTimeImmutable::class, null, array(DateTimeNormalizer::FORMAT_KEY => 'Y.m.d|'))); - $this->assertEquals(new \DateTime('2016/01/01', new \DateTimeZone('UTC')), $this->normalizer->denormalize('2016.01.01', \DateTime::class, null, array(DateTimeNormalizer::FORMAT_KEY => 'Y.m.d|'))); + $this->assertEquals(new \DateTimeImmutable('2016/01/01'), $this->normalizer->denormalize('2016.01.01', \DateTimeInterface::class, null, array(DateTimeNormalizer::FORMAT_KEY => 'Y.m.d|'))); + $this->assertEquals(new \DateTimeImmutable('2016/01/01'), $this->normalizer->denormalize('2016.01.01', \DateTimeImmutable::class, null, array(DateTimeNormalizer::FORMAT_KEY => 'Y.m.d|'))); + $this->assertEquals(new \DateTime('2016/01/01'), $this->normalizer->denormalize('2016.01.01', \DateTime::class, null, array(DateTimeNormalizer::FORMAT_KEY => 'Y.m.d|'))); } /** diff --git a/src/Symfony/Component/Translation/Tests/IdentityTranslatorTest.php b/src/Symfony/Component/Translation/Tests/IdentityTranslatorTest.php index 352dd318dc73c..648c54afd153d 100644 --- a/src/Symfony/Component/Translation/Tests/IdentityTranslatorTest.php +++ b/src/Symfony/Component/Translation/Tests/IdentityTranslatorTest.php @@ -60,7 +60,7 @@ public function testGetSetLocale() public function testGetLocaleReturnsDefaultLocaleIfNotSet() { // in order to test with "pt_BR" - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); $translator = new IdentityTranslator(); diff --git a/src/Symfony/Component/Validator/ConstraintValidator.php b/src/Symfony/Component/Validator/ConstraintValidator.php index ece4400065c6a..965a888af0981 100644 --- a/src/Symfony/Component/Validator/ConstraintValidator.php +++ b/src/Symfony/Component/Validator/ConstraintValidator.php @@ -36,7 +36,7 @@ abstract class ConstraintValidator implements ConstraintValidatorInterface const OBJECT_TO_STRING = 2; /** - * @var ExecutionContextInterface + * @var ExecutionContextInterface2Dot5 */ protected $context; diff --git a/src/Symfony/Component/Validator/README.md b/src/Symfony/Component/Validator/README.md index 9dfbffb3f545a..3ccb2901adeac 100644 --- a/src/Symfony/Component/Validator/README.md +++ b/src/Symfony/Component/Validator/README.md @@ -7,7 +7,7 @@ The Validator component provides tools to validate values following the Resources --------- - * [Documentation](https://symfony.com/doc/current/book/validation.html) + * [Documentation](https://symfony.com/doc/current/components/validator.html) * [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/Validator/Resources/translations/validators.id.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.id.xlf index 742f4a163ebd6..c7061470dc16a 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.id.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.id.xlf @@ -36,11 +36,11 @@ This field was not expected. - Bidang ini tidak diharapkan. + Ruas ini tidak diharapkan. This field is missing. - Bidang ini hilang. + Ruas ini hilang. This value is not a valid date. @@ -52,15 +52,15 @@ This value is not a valid email address. - Nilai ini bukan alamat email yang sah. + Nilai ini bukan alamat surel yang sah. The file could not be found. - Berkas tidak ditemukan. + Berkas tidak dapat ditemukan. The file is not readable. - Berkas tidak bisa dibaca. + Berkas tidak dapat dibaca. The file is too large ({{ size }} {{ suffix }}). Allowed maximum size is {{ limit }} {{ suffix }}. @@ -68,7 +68,7 @@ The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}. - Jenis berkas ({{ type }}) tidak sah. Jenis berkas yang diijinkan adalah {{ types }}. + Jenis berkas ({{ type }}) tidak sah. Jenis berkas yang diizinkan adalah {{ types }}. This value should be {{ limit }} or less. @@ -116,7 +116,7 @@ The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}. - Ukuran berkas terlalu besar. Ukuran maksimum yang diijinkan adalah {{ limit }} {{ suffix }}. + Ukuran berkas terlalu besar. Ukuran maksimum yang diizinkan adalah {{ limit }} {{ suffix }}. The file is too large. @@ -132,7 +132,7 @@ This file is not a valid image. - Berkas ini tidak termasuk gambar. + Berkas ini tidak termasuk citra. This is not a valid IP address. @@ -156,23 +156,23 @@ The size of the image could not be detected. - Ukuran dari gambar tidak bisa dideteksi. + Ukuran dari citra tidak bisa dideteksi. The image width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px. - Lebar gambar terlalu besar ({{ width }}px). Ukuran lebar maksimum adalah {{ max_width }}px. + Lebar citra terlalu besar ({{ width }}px). Ukuran lebar maksimum adalah {{ max_width }}px. The image width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px. - Lebar gambar terlalu kecil ({{ width }}px). Ukuran lebar minimum yang diharapkan adalah {{ min_width }}px. + Lebar citra terlalu kecil ({{ width }}px). Ukuran lebar minimum yang diharapkan adalah {{ min_width }}px. The image height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px. - Tinggi gambar terlalu besar ({{ height }}px). Ukuran tinggi maksimum adalah {{ max_height }}px. + Tinggi citra terlalu besar ({{ height }}px). Ukuran tinggi maksimum adalah {{ max_height }}px. The image height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px. - Tinggi gambar terlalu kecil ({{ height }}px). Ukuran tinggi minimum yang diharapkan adalah {{ min_height }}px. + Tinggi citra terlalu kecil ({{ height }}px). Ukuran tinggi minimum yang diharapkan adalah {{ min_height }}px. This value should be the user's current password. @@ -278,6 +278,38 @@ This value should not be identical to {{ compared_value_type }} {{ compared_value }}. Nilai ini seharusnya tidak identik dengan {{ compared_value_type }} {{ compared_value }}. + + The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. + Rasio citra terlalu besar ({{ ratio }}). Rasio maksimum yang diizinkan adalah {{ max_ratio }}. + + + The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. + Rasio citra terlalu kecil ({{ ratio }}). Rasio minimum yang diharapkan adalah {{ min_ratio }}. + + + The image is square ({{ width }}x{{ height }}px). Square images are not allowed. + Citra persegi ({{ width }}x{{ height }}px). Citra persegi tidak diizinkan. + + + The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed. + Citra berorientasi lanskap ({{ width }}x{{ height }}px). Citra berorientasi lanskap tidak diizinkan. + + + The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. + Citra berorientasi potret ({{ width }}x{{ height }}px). Citra berorientasi potret tidak diizinkan. + + + An empty file is not allowed. + Berkas kosong tidak diizinkan. + + + The host could not be resolved. + Host tidak dapat diselesaikan. + + + This value does not match the expected {{ charset }} charset. + Nilai ini tidak memenuhi set karakter {{ charset }} yang diharapkan. + diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CountryValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CountryValidatorTest.php index 1dcef8c5cf39a..044a742db630a 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CountryValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CountryValidatorTest.php @@ -92,7 +92,7 @@ public function getInvalidCountries() public function testValidateUsingCountrySpecificLocale() { // in order to test with "en_GB" - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('en_GB'); diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CurrencyValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CurrencyValidatorTest.php index 720bf7604ae2e..1286ff31f89f3 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CurrencyValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CurrencyValidatorTest.php @@ -60,7 +60,7 @@ public function testValidCurrencies($currency) **/ public function testValidCurrenciesWithCountrySpecificLocale($currency) { - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('en_GB'); diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LanguageValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LanguageValidatorTest.php index ba6433a1bdfa3..2980f2a233193 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LanguageValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LanguageValidatorTest.php @@ -91,7 +91,7 @@ public function getInvalidLanguages() public function testValidateUsingCountrySpecificLocale() { - IntlTestHelper::requireFullIntl($this); + IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('fr_FR'); $existingLanguage = 'en'; diff --git a/src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php b/src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php index 916c4bd460622..3681b6f1391a8 100644 --- a/src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php +++ b/src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php @@ -26,7 +26,7 @@ */ class GraphvizDumper implements DumperInterface { - private static $defaultOptions = array( + protected static $defaultOptions = array( 'graph' => array('ratio' => 'compress', 'rankdir' => 'LR'), 'node' => array('fontsize' => 9, 'fontname' => 'Arial', 'color' => '#333333', 'fillcolor' => 'lightblue', 'fixedsize' => true, 'width' => 1), 'edge' => array('fontsize' => 9, 'fontname' => 'Arial', 'color' => '#333333', 'arrowhead' => 'normal', 'arrowsize' => 0.5), @@ -58,7 +58,10 @@ public function dump(Definition $definition, Marking $marking = null, array $opt .$this->endDot(); } - private function findPlaces(Definition $definition, Marking $marking = null) + /** + * @internal + */ + protected function findPlaces(Definition $definition, Marking $marking = null) { $places = array(); @@ -79,7 +82,10 @@ private function findPlaces(Definition $definition, Marking $marking = null) return $places; } - private function findTransitions(Definition $definition) + /** + * @internal + */ + protected function findTransitions(Definition $definition) { $transitions = array(); @@ -93,37 +99,38 @@ private function findTransitions(Definition $definition) return $transitions; } - private function addPlaces(array $places) + /** + * @internal + */ + protected function addPlaces(array $places) { $code = ''; foreach ($places as $id => $place) { - $code .= sprintf(" place_%s [label=\"%s\", shape=circle%s];\n", - $this->dotize($id), - $id, - $this->addAttributes($place['attributes']) - ); + $code .= sprintf(" place_%s [label=\"%s\", shape=circle%s];\n", $this->dotize($id), $id, $this->addAttributes($place['attributes'])); } return $code; } - private function addTransitions(array $transitions) + /** + * @internal + */ + protected function addTransitions(array $transitions) { $code = ''; foreach ($transitions as $place) { - $code .= sprintf(" transition_%s [label=\"%s\", shape=box%s];\n", - $this->dotize($place['name']), - $place['name'], - $this->addAttributes($place['attributes']) - ); + $code .= sprintf(" transition_%s [label=\"%s\", shape=box%s];\n", $this->dotize($place['name']), $place['name'], $this->addAttributes($place['attributes'])); } return $code; } - private function findEdges(Definition $definition) + /** + * @internal + */ + protected function findEdges(Definition $definition) { $dotEdges = array(); @@ -147,7 +154,10 @@ private function findEdges(Definition $definition) return $dotEdges; } - private function addEdges($edges) + /** + * @internal + */ + protected function addEdges(array $edges) { $code = ''; @@ -163,7 +173,10 @@ private function addEdges($edges) return $code; } - private function startDot(array $options) + /** + * @internal + */ + protected function startDot(array $options) { return sprintf("digraph workflow {\n %s\n node [%s];\n edge [%s];\n\n", $this->addOptions($options['graph']), @@ -172,12 +185,23 @@ private function startDot(array $options) ); } - private function endDot() + /** + * @internal + */ + protected function endDot() { return "}\n"; } - private function addAttributes($attributes) + /** + * @internal + */ + protected function dotize($id) + { + return strtolower(preg_replace('/[^\w]/i', '_', $id)); + } + + private function addAttributes(array $attributes) { $code = array(); @@ -188,7 +212,7 @@ private function addAttributes($attributes) return $code ? ', '.implode(', ', $code) : ''; } - private function addOptions($options) + private function addOptions(array $options) { $code = array(); @@ -198,9 +222,4 @@ private function addOptions($options) return implode(' ', $code); } - - private function dotize($id) - { - return strtolower(preg_replace('/[^\w]/i', '_', $id)); - } } diff --git a/src/Symfony/Component/Workflow/Dumper/StateMachineGraphvizDumper.php b/src/Symfony/Component/Workflow/Dumper/StateMachineGraphvizDumper.php new file mode 100644 index 0000000000000..9f68e1daf72f3 --- /dev/null +++ b/src/Symfony/Component/Workflow/Dumper/StateMachineGraphvizDumper.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Workflow\Dumper; + +use Symfony\Component\Workflow\Definition; +use Symfony\Component\Workflow\Marking; + +class StateMachineGraphvizDumper extends GraphvizDumper +{ + /** + * {@inheritdoc} + * + * Dumps the workflow as a graphviz graph. + * + * Available options: + * + * * graph: The default options for the whole graph + * * node: The default options for nodes (places) + * * edge: The default options for edges + */ + public function dump(Definition $definition, Marking $marking = null, array $options = array()) + { + $places = $this->findPlaces($definition, $marking); + $edges = $this->findEdges($definition); + + $options = array_replace_recursive(self::$defaultOptions, $options); + + return $this->startDot($options) + .$this->addPlaces($places) + .$this->addEdges($edges) + .$this->endDot() + ; + } + + /** + * @internal + */ + protected function findEdges(Definition $definition) + { + $edges = array(); + + foreach ($definition->getTransitions() as $transition) { + foreach ($transition->getFroms() as $from) { + foreach ($transition->getTos() as $to) { + $edges[$from][] = array( + 'name' => $transition->getName(), + 'to' => $to, + ); + } + } + } + + return $edges; + } + + /** + * @internal + */ + protected function addEdges(array $edges) + { + $code = ''; + + foreach ($edges as $id => $edges) { + foreach ($edges as $edge) { + $code .= sprintf(" place_%s -> place_%s [label=\"%s\" style=\"%s\"];\n", $this->dotize($id), $this->dotize($edge['to']), $edge['name'], 'solid'); + } + } + + return $code; + } +} diff --git a/src/Symfony/Component/Workflow/Tests/Dumper/GraphvizDumperTest.php b/src/Symfony/Component/Workflow/Tests/Dumper/GraphvizDumperTest.php index 2b90ea8d5f197..01927b209c2ff 100644 --- a/src/Symfony/Component/Workflow/Tests/Dumper/GraphvizDumperTest.php +++ b/src/Symfony/Component/Workflow/Tests/Dumper/GraphvizDumperTest.php @@ -20,7 +20,7 @@ public function setUp() /** * @dataProvider provideWorkflowDefinitionWithoutMarking */ - public function testGraphvizDumperWithoutMarking($definition, $expected) + public function testDumpWithoutMarking($definition, $expected) { $dump = $this->dumper->dump($definition); @@ -30,7 +30,7 @@ public function testGraphvizDumperWithoutMarking($definition, $expected) /** * @dataProvider provideWorkflowDefinitionWithMarking */ - public function testWorkflowWithMarking($definition, $marking, $expected) + public function testDumpWithMarking($definition, $marking, $expected) { $dump = $this->dumper->dump($definition, $marking); @@ -40,9 +40,9 @@ public function testWorkflowWithMarking($definition, $marking, $expected) public function provideWorkflowDefinitionWithMarking() { yield array( - $this->createComplexWorkflow(), + $this->createComplexWorkflowDefinition(), new Marking(array('b' => 1)), - $this->createComplexWorkflowDumpWithMarking(), + $this->createComplexWorkflowDefinitionDumpWithMarking(), ); yield array( @@ -54,11 +54,11 @@ public function provideWorkflowDefinitionWithMarking() public function provideWorkflowDefinitionWithoutMarking() { - yield array($this->createComplexWorkflow(), $this->provideComplexWorkflowDumpWithoutMarking()); + yield array($this->createComplexWorkflowDefinition(), $this->provideComplexWorkflowDumpWithoutMarking()); yield array($this->createSimpleWorkflowDefinition(), $this->provideSimpleWorkflowDumpWithoutMarking()); } - public function createComplexWorkflowDumpWithMarking() + public function createComplexWorkflowDefinitionDumpWithMarking() { return 'digraph workflow { ratio="compress" rankdir="LR" diff --git a/src/Symfony/Component/Workflow/Tests/Dumper/StateMachineGraphvizDumperTest.php b/src/Symfony/Component/Workflow/Tests/Dumper/StateMachineGraphvizDumperTest.php new file mode 100644 index 0000000000000..c9a49b36f71e1 --- /dev/null +++ b/src/Symfony/Component/Workflow/Tests/Dumper/StateMachineGraphvizDumperTest.php @@ -0,0 +1,74 @@ +dumper = new StateMachineGraphvizDumper(); + } + + public function testDumpWithoutMarking() + { + $definition = $this->createComplexStateMachineDefinition(); + + $dump = $this->dumper->dump($definition); + + $expected = <<<'EOGRAPH' +digraph workflow { + ratio="compress" rankdir="LR" + node [fontsize="9" fontname="Arial" color="#333333" fillcolor="lightblue" fixedsize="1" width="1"]; + edge [fontsize="9" fontname="Arial" color="#333333" arrowhead="normal" arrowsize="0.5"]; + + place_a [label="a", shape=circle, style="filled"]; + place_b [label="b", shape=circle]; + place_c [label="c", shape=circle]; + place_d [label="d", shape=circle]; + place_a -> place_b [label="t1" style="solid"]; + place_d -> place_b [label="t1" style="solid"]; + place_b -> place_c [label="t2" style="solid"]; + place_b -> place_d [label="t3" style="solid"]; +} + +EOGRAPH; + + $this->assertEquals($expected, $dump); + } + + public function testDumpWithMarking() + { + $definition = $this->createComplexStateMachineDefinition(); + $marking = new Marking(array('b' => 1)); + + $expected = <<<'EOGRAPH' +digraph workflow { + ratio="compress" rankdir="LR" + node [fontsize="9" fontname="Arial" color="#333333" fillcolor="lightblue" fixedsize="1" width="1"]; + edge [fontsize="9" fontname="Arial" color="#333333" arrowhead="normal" arrowsize="0.5"]; + + place_a [label="a", shape=circle, style="filled"]; + place_b [label="b", shape=circle, color="#FF0000", shape="doublecircle"]; + place_c [label="c", shape=circle]; + place_d [label="d", shape=circle]; + place_a -> place_b [label="t1" style="solid"]; + place_d -> place_b [label="t1" style="solid"]; + place_b -> place_c [label="t2" style="solid"]; + place_b -> place_d [label="t3" style="solid"]; +} + +EOGRAPH; + + $dump = $this->dumper->dump($definition, $marking); + + $this->assertEquals($expected, $dump); + } +} diff --git a/src/Symfony/Component/Workflow/Tests/StateMachineTest.php b/src/Symfony/Component/Workflow/Tests/StateMachineTest.php index 6aa3c60fc1cc6..7dca9a824d7c1 100644 --- a/src/Symfony/Component/Workflow/Tests/StateMachineTest.php +++ b/src/Symfony/Component/Workflow/Tests/StateMachineTest.php @@ -2,21 +2,16 @@ namespace Symfony\Component\Workflow\Tests; -use Symfony\Component\Workflow\Definition; use Symfony\Component\Workflow\Marking; use Symfony\Component\Workflow\StateMachine; -use Symfony\Component\Workflow\Transition; class StateMachineTest extends \PHPUnit_Framework_TestCase { + use WorkflowBuilderTrait; + public function testCan() { - $places = array('a', 'b', 'c', 'd'); - $transitions[] = new Transition('t1', 'a', 'b'); - $transitions[] = new Transition('t1', 'd', 'b'); - $transitions[] = new Transition('t2', 'b', 'c'); - $transitions[] = new Transition('t3', 'b', 'd'); - $definition = new Definition($places, $transitions); + $definition = $this->createComplexStateMachineDefinition(); $net = new StateMachine($definition); $subject = new \stdClass(); @@ -29,47 +24,18 @@ public function testCan() $subject->marking = 'b'; $this->assertFalse($net->can($subject, 't1')); - - // The graph looks like: - // - // +-------------------------------+ - // v | - // +---+ +----+ +----+ +----+ +---+ +----+ - // | a | --> | t1 | --> | b | --> | t3 | --> | d | --> | t1 | - // +---+ +----+ +----+ +----+ +---+ +----+ - // | - // | - // v - // +----+ +----+ - // | t2 | --> | c | - // +----+ +----+ } public function testCanWithMultipleTransition() { - $places = array('a', 'b', 'c'); - $transitions[] = new Transition('t1', 'a', 'b'); - $transitions[] = new Transition('t2', 'a', 'c'); - $definition = new Definition($places, $transitions); + $definition = $this->createComplexStateMachineDefinition(); $net = new StateMachine($definition); $subject = new \stdClass(); - // If you are in place "a" you should be able to apply "t1" and "t2" - $subject->marking = 'a'; - $this->assertTrue($net->can($subject, 't1')); + // If you are in place "b" you should be able to apply "t1" and "t2" + $subject->marking = 'b'; $this->assertTrue($net->can($subject, 't2')); - - // The graph looks like: - // - // +----+ +----+ +---+ - // | a | --> | t1 | --> | b | - // +----+ +----+ +---+ - // | - // | - // v - // +----+ +----+ - // | t2 | --> | c | - // +----+ +----+ + $this->assertTrue($net->can($subject, 't3')); } } diff --git a/src/Symfony/Component/Workflow/Tests/Validator/WorkflowValidatorTest.php b/src/Symfony/Component/Workflow/Tests/Validator/WorkflowValidatorTest.php index 1b5fa67fdb083..30d2551fa1b0f 100644 --- a/src/Symfony/Component/Workflow/Tests/Validator/WorkflowValidatorTest.php +++ b/src/Symfony/Component/Workflow/Tests/Validator/WorkflowValidatorTest.php @@ -15,7 +15,7 @@ class WorkflowValidatorTest extends \PHPUnit_Framework_TestCase */ public function testSinglePlaceWorkflowValidatorAndComplexWorkflow() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); (new WorkflowValidator(true))->validate($definition, 'foo'); } diff --git a/src/Symfony/Component/Workflow/Tests/WorkflowBuilderTrait.php b/src/Symfony/Component/Workflow/Tests/WorkflowBuilderTrait.php index d7b8de530445c..5e8db29061295 100644 --- a/src/Symfony/Component/Workflow/Tests/WorkflowBuilderTrait.php +++ b/src/Symfony/Component/Workflow/Tests/WorkflowBuilderTrait.php @@ -7,7 +7,7 @@ trait WorkflowBuilderTrait { - private function createComplexWorkflow() + private function createComplexWorkflowDefinition() { $places = range('a', 'g'); @@ -33,7 +33,7 @@ private function createComplexWorkflow() // +----+ +----+ +----+ +----+ } - public function createSimpleWorkflowDefinition() + private function createSimpleWorkflowDefinition() { $places = range('a', 'c'); @@ -42,5 +42,38 @@ public function createSimpleWorkflowDefinition() $transitions[] = new Transition('t2', 'b', 'c'); return new Definition($places, $transitions); + + // The graph looks like: + // +---+ +----+ +---+ +----+ +---+ + // | a | --> | t1 | --> | b | --> | t2 | --> | c | + // +---+ +----+ +---+ +----+ +---+ + } + + private function createComplexStateMachineDefinition() + { + $places = array('a', 'b', 'c', 'd'); + + $transitions[] = new Transition('t1', 'a', 'b'); + $transitions[] = new Transition('t1', 'd', 'b'); + $transitions[] = new Transition('t2', 'b', 'c'); + $transitions[] = new Transition('t3', 'b', 'd'); + + $definition = new Definition($places, $transitions); + + return $definition; + + // The graph looks like: + // t1 + // +------------------+ + // v | + // +---+ t1 +-----+ t2 +---+ | + // | a | ----> | b | ----> | c | | + // +---+ +-----+ +---+ | + // | | + // | t3 | + // v | + // +-----+ | + // | d | -------------+ + // +-----+ } } diff --git a/src/Symfony/Component/Workflow/Tests/WorkflowTest.php b/src/Symfony/Component/Workflow/Tests/WorkflowTest.php index 0a886752d5992..cb5256f412933 100644 --- a/src/Symfony/Component/Workflow/Tests/WorkflowTest.php +++ b/src/Symfony/Component/Workflow/Tests/WorkflowTest.php @@ -56,7 +56,7 @@ public function testGetMarkingWithImpossiblePlace() public function testGetMarkingWithEmptyInitialMarking() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $workflow = new Workflow($definition, new MultipleStateMarkingStore()); @@ -70,7 +70,7 @@ public function testGetMarkingWithEmptyInitialMarking() public function testGetMarkingWithExistingMarking() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $subject->marking = array('b' => 1, 'c' => 1); @@ -89,7 +89,7 @@ public function testGetMarkingWithExistingMarking() */ public function testCanWithUnexistingTransition() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $workflow = new Workflow($definition, new MultipleStateMarkingStore()); @@ -99,7 +99,7 @@ public function testCanWithUnexistingTransition() public function testCan() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $workflow = new Workflow($definition, new MultipleStateMarkingStore()); @@ -110,7 +110,7 @@ public function testCan() public function testCanWithGuard() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $eventDispatcher = new EventDispatcher(); @@ -128,7 +128,7 @@ public function testCanWithGuard() */ public function testApplyWithImpossibleTransition() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $workflow = new Workflow($definition, new MultipleStateMarkingStore()); @@ -138,7 +138,7 @@ public function testApplyWithImpossibleTransition() public function testApply() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $workflow = new Workflow($definition, new MultipleStateMarkingStore()); @@ -153,7 +153,7 @@ public function testApply() public function testApplyWithEventDispatcher() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $eventDispatcher = new EventDispatcherMock(); @@ -187,7 +187,7 @@ public function testApplyWithEventDispatcher() public function testGetEnabledTransitions() { - $definition = $this->createComplexWorkflow(); + $definition = $this->createComplexWorkflowDefinition(); $subject = new \stdClass(); $subject->marking = null; $eventDispatcher = new EventDispatcher(); diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 37a0c8967b270..c6e5830bd5480 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -648,10 +648,8 @@ private static function evaluateScalar($scalar, $flags, $references = array()) return (float) str_replace(array(',', '_'), '', $scalar); case preg_match(self::getTimestampRegex(), $scalar): if (Yaml::PARSE_DATETIME & $flags) { - $date = new \DateTime($scalar); - $date->setTimeZone(new \DateTimeZone('UTC')); - - return $date; + // When no timezone is provided in the parsed date, YAML spec says we must assume UTC. + return new \DateTime($scalar, new \DateTimeZone('UTC')); } $timeZone = date_default_timezone_get(); diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php index 3b580129c4fb7..33bda676f4dec 100644 --- a/src/Symfony/Component/Yaml/Tests/InlineTest.php +++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php @@ -561,7 +561,7 @@ public function testParseTimestampAsUnixTimestampByDefault($yaml, $year, $month, /** * @dataProvider getTimestampTests */ - public function testParseTimestampAsDateTimeObject($yaml, $year, $month, $day, $hour, $minute, $second) + public function testParseTimestampAsDateTimeObject($yaml, $year, $month, $day, $hour, $minute, $second, $timezone) { $expected = new \DateTime($yaml); $expected->setTimeZone(new \DateTimeZone('UTC')); @@ -573,16 +573,18 @@ public function testParseTimestampAsDateTimeObject($yaml, $year, $month, $day, $ $expected->setTime($hour, $minute, $second); } - $this->assertEquals($expected, Inline::parse($yaml, Yaml::PARSE_DATETIME)); + $date = Inline::parse($yaml, Yaml::PARSE_DATETIME); + $this->assertEquals($expected, $date); + $this->assertSame($timezone, $date->format('O')); } public function getTimestampTests() { return array( - 'canonical' => array('2001-12-15T02:59:43.1Z', 2001, 12, 15, 2, 59, 43.1), - 'ISO-8601' => array('2001-12-15t21:59:43.10-05:00', 2001, 12, 16, 2, 59, 43.1), - 'spaced' => array('2001-12-15 21:59:43.10 -5', 2001, 12, 16, 2, 59, 43.1), - 'date' => array('2001-12-15', 2001, 12, 15, 0, 0, 0), + 'canonical' => array('2001-12-15T02:59:43.1Z', 2001, 12, 15, 2, 59, 43.1, '+0000'), + 'ISO-8601' => array('2001-12-15t21:59:43.10-05:00', 2001, 12, 16, 2, 59, 43.1, '-0500'), + 'spaced' => array('2001-12-15 21:59:43.10 -5', 2001, 12, 16, 2, 59, 43.1, '-0500'), + 'date' => array('2001-12-15', 2001, 12, 15, 0, 0, 0, '+0000'), ); } @@ -594,7 +596,11 @@ public function testParseNestedTimestampListAsDateTimeObject($yaml, $year, $mont $expected = new \DateTime($yaml); $expected->setTimeZone(new \DateTimeZone('UTC')); $expected->setDate($year, $month, $day); - @$expected->setTime($hour, $minute, $second, 1000000 * ($second - (int) $second)); + if (PHP_VERSION_ID >= 70100) { + $expected->setTime($hour, $minute, $second, 1000000 * ($second - (int) $second)); + } else { + $expected->setTime($hour, $minute, $second); + } $expectedNested = array('nested' => array($expected)); $yamlNested = "{nested: [$yaml]}"; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy