diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index cb9b8a69c1638..1bad363eca810 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,6 @@ | Q | A | ------------- | --- -| Branch? | 3.4 or master / 2.7, 2.8, 3.2 or 3.3 +| Branch? | 3.4 or master / 2.7, 2.8 or 3.3 | Bug fix? | yes/no | New feature? | yes/no | BC breaks? | yes/no diff --git a/.travis.yml b/.travis.yml index da1bacca590e5..711fa49c74155 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,15 +56,38 @@ before_install: export PHPUNIT_X="$PHPUNIT --exclude-group tty,benchmark,intl-data" export COMPOSER_UP='composer update --no-progress --no-suggest --ansi' + nanoseconds() { + local cmd="date" + local format="+%s%N" + local os=$(uname) + if hash gdate > /dev/null 2>&1; then + cmd="gdate" + elif [[ "$os" = Darwin ]]; then + format="+%s000000000" + fi + $cmd -u $format + } + export -f nanoseconds + # tfold is a helper to create folded reports tfold () { - title=$1 - fold=$(echo $title | sed -r 's/[^-_A-Za-z\d]+/./g') + local title=$1 + local fold=$(echo $title | sed -r 's/[^-_A-Za-z0-9]+/./g') shift - echo -e "travis_fold:start:$fold\\n\\e[1;34m$title\\e[0m" - bash -xc "$*" 2>&1 && + local id=$(printf %08x $(( RANDOM * RANDOM ))) + local start=$(nanoseconds) + echo -e "travis_fold:start:$fold" + echo -e "travis_time:start:$id" + echo -e "\\e[1;34m$title\\e[0m" + + bash -xc "$*" 2>&1 + local ok=$? + local end=$(nanoseconds) + echo -e "\\ntravis_time:end:$id:start=$start,finish=$end,duration=$(($end-$start))" + (exit $ok) && echo -e "\\e[32mOK\\e[0m $title\\n\\ntravis_fold:end:$fold" || - ( echo -e "\\e[41mKO\\e[0m $title\\n" && exit 1 ) + echo -e "\\e[41mKO\\e[0m $title\\n" + (exit $ok) } export -f tfold diff --git a/CHANGELOG-3.2.md b/CHANGELOG-3.2.md index 1a4d44e19981e..544ae4b071c06 100644 --- a/CHANGELOG-3.2.md +++ b/CHANGELOG-3.2.md @@ -7,6 +7,22 @@ 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.13 (2017-08-01) + + * bug #22244 [Console] Fix passing options with defaultCommand (Jakub Sacha) + * bug #23684 [Debug] Missing escape in debug output (c960657) + * bug #23654 [DI] Fix using private services in expressions (nicolas-grekas) + * bug #23662 [VarDumper] Adapt to php 7.2 changes (nicolas-grekas) + * bug #23649 [Form][TwigBridge] Don't render _method in form_rest() for a child form (fmarchalemisys) + * bug #23023 [DoctrineBridge][PropertyInfo] Added support for Doctrine Embeddables (vudaltsov) + * bug #23619 [Validator] Fix IbanValidator for ukrainian IBANs (paroe) + * bug #23586 Fix case sensitive sameSite cookie (mikefrancis) + * bug #23238 [Security] ensure the 'route' index is set before attempting to use it (gsdevme) + * bug #23330 [WebProfilerBundle] Fix full sized dump hovering in toolbar (ogizanagi) + * bug #23580 Fix login redirect when referer contains a query string (fabpot) + * bug #23558 [FrameworkBundle] fix ValidatorCacheWarmer: use serializing ArrayAdapter (dmaicher) + * bug #23574 [VarDumper] Move locale sniffing to dump() time (nicolas-grekas) + * 3.2.12 (2017-07-17) * bug #23549 [PropertyInfo] conflict for phpdocumentor/reflection-docblock 3.2 (xabbuh) diff --git a/CHANGELOG-3.3.md b/CHANGELOG-3.3.md index e005c1dabe0d0..185d1432cc409 100644 --- a/CHANGELOG-3.3.md +++ b/CHANGELOG-3.3.md @@ -7,6 +7,46 @@ in 3.3 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v3.3.0...v3.3.1 +* 3.3.7 (2017-08-28) + + * bug #24009 [DI] Fix tracking env vars when merging configs (bis) (nicolas-grekas) + * bug #23952 [PhpUnitBridge] install PHPUnit 6 on PHP 7.2 (xabbuh) + * bug #23985 [Cache] Workaround zend.detect_unicode + zend.multibyte (nicolas-grekas) + * bug #23989 [Debug] Remove false-positive check in DebugClassLoader (nicolas-grekas) + * bug #23983 [VarDumper] Strengthen dumped JS (nicolas-grekas) + * bug #23982 [VarDumper] Strengthen dumped JS (nicolas-grekas) + * bug #23925 [Validator] Fix use of GroupSequenceProvider in child classes (linniksa) + * bug #23971 [Cache] Fix lazy Memcached connections (nicolas-grekas) + * bug #23970 [Cache] Fix >30 days expirations with Memcached (nicolas-grekas) + * bug #23949 [Dotenv] Get env using $_SERVER to work with fastcgi_param and workaround thread safety issues (nicolas-grekas) + * bug #23799 [Dotenv][WebServerBundle] Override previously loaded variables (voronkovich) + * bug #23676 [WebProfilerBundle] Re add missing link to the controller (lyrixx) + * bug #23870 [DI] Use GlobResource for non-tracked directories (vudaltsov) + * bug #23945 [Validator] Fix Greek translation (azhurb) + * bug #23940 [DI] Fix resolving env vars when compiling a ContainerBuilder (nicolas-grekas) + * bug #23903 [DI] Fix merging of env vars in configs (nicolas-grekas) + * bug #23825 Revert "feature #21038 [FrameworkBundle] deprecated cache:clear with warmup (fabpot)" (nicolas-grekas) + * bug #23899 [DI] Fix reading env vars from fastcgi params (nicolas-grekas) + * bug #23909 [Console] Initialize lazily to render exceptions properly (nicolas-grekas) + * bug #23878 [VarDumper] play nice with open_basedir when looking for composer.json (nicolas-grekas) + * bug #23897 Allow phpdocumentor/reflection-docblock 4 (derrabus) + * bug #23865 [Workflow] fixed InvalidDefinitionException message for StateMachineValidator (fmata) + * bug #23856 [DI] Fix dumping abstract with YamlDumper (nicolas-grekas) + * bug #23848 restrict reflection doc block (ElectricMaxxx) + * bug #23854 [DI] Fix YamlDumper not dumping abstract and autoconfigure (nicolas-grekas) + * bug #23752 Ignore memcached missing key error on session destroy (jderusse) + * bug #23829 Fixed the exception page design in responsive mode (javiereguiluz) + * bug #23828 [Console] Log exit codes as debug messages instead of errors (haroldiedema) + * bug #23763 [Cache] Hash cache key on save (lstrojny) + * bug #23806 [Profiler] Fix request_collector check in main layout (ogizanagi) + * bug #23658 [HttpFoundation] Generate safe fallback filename for wrongly encoded filename (xelaris) + * bug #23776 [FrameworkBundle] Warmup annotations for bundle-less controllers and entities (nicolas-grekas) + * bug #23783 Avoid infinite loops when profiler data is malformed (javiereguiluz) + * bug #23638 [FrameworkBundle][Workflow] better errors when security deps are missing (xabbuh) + * bug #23729 [Bridge\ProxyManager] Dont call __destruct() on non-instantiated services (nicolas-grekas) + * bug #23703 Bump minimal PHP version to ^5.5.9|>=7.0.8 (nicolas-grekas) + * bug #23755 [Config] Fix checking class existence freshness (nicolas-grekas) + * 3.3.6 (2017-08-01) * bug #22244 [Console] Fix passing options with defaultCommand (Jakub Sacha) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 8b1eec6264271..dcdf73e19ecae 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -20,11 +20,11 @@ Symfony is the result of the work of many people who made the code better - Javier Eguiluz (javier.eguiluz) - Hugo Hamon (hhamon) - Maxime Steinhausser (ogizanagi) - - Abdellatif Ait boudad (aitboudad) - Robin Chalas (chalas_r) + - Abdellatif Ait boudad (aitboudad) + - Grégoire Pineau (lyrixx) - Romain Neutron (romain) - Pascal Borreli (pborreli) - - Grégoire Pineau (lyrixx) - Wouter De Jong (wouterj) - Joseph Bielawski (stloyd) - Karma Dordrak (drak) @@ -34,8 +34,8 @@ Symfony is the result of the work of many people who made the code better - Jean-François Simon (jfsimon) - Benjamin Eberlei (beberlei) - Igor Wiedler (igorw) - - Eriksen Costa (eriksencosta) - Roland Franssen (ro0) + - Eriksen Costa (eriksencosta) - Jules Pietri (heah) - Sarah Khalil (saro0h) - Guilhem Niot (energetick) @@ -66,6 +66,7 @@ Symfony is the result of the work of many people who made the code better - Eric Clemmons (ericclemmons) - Charles Sarrazin (csarrazi) - Christian Raue + - Konstantin Myakshin (koc) - Arnout Boks (aboks) - Deni - Henrik Westphal (snc) @@ -73,7 +74,7 @@ Symfony is the result of the work of many people who made the code better - Jáchym Toušek (enumag) - Titouan Galopin (tgalopin) - Douglas Greenshields (shieldo) - - Konstantin Myakshin (koc) + - Dany Maillard (maidmaid) - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) @@ -89,23 +90,22 @@ 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) + - Issei Murasawa (issei_m) - Arnaud Le Blanc (arnaud-lb) - Maxime STEINHAUSSER - Alexander M. Turek (derrabus) - Michal Piotrowski (eventhorizon) - - Dany Maillard (maidmaid) - - Issei Murasawa (issei_m) - Tim Nagel (merk) + - Yonel Ceruto González (yonelceruto) - Brice BERNARD (brikou) - Baptiste Clavié (talus) - Vladimir Reznichenko (kalessil) - marc.weistroff - - Yonel Ceruto González (yonelceruto) - lenar + - Tobias Nyholm (tobias) - Włodzimierz Gajda (gajdaw) - Alexander Schwenn (xelaris) - Jacob Dreesen (jdreesen) - - Tobias Nyholm (tobias) - Florian Voutzinos (florianv) - Colin Frei - Adrien Brault (adrienbrault) @@ -132,13 +132,13 @@ Symfony is the result of the work of many people who made the code better - Daniel Gomes (danielcsgomes) - Hidenori Goto (hidenorigoto) - Guilherme Blanco (guilhermeblanco) + - David Maicher (dmaicher) - Pablo Godel (pgodel) - Jérémie Augustin (jaugustin) - Andréia Bohner (andreia) - Rafael Dohms (rdohms) - Arnaud Kleinpeter (nanocom) - jwdeitch - - David Maicher (dmaicher) - Mikael Pajunen - Joel Wurtz (brouznouf) - Jérôme Vasseur (jvasseur) @@ -176,6 +176,7 @@ Symfony is the result of the work of many people who made the code better - Benjamin Dulau (dbenjamin) - James Halsall (jaitsu) - Mathieu Lemoine (lemoinem) + - Christian Schmidt - Andreas Hucks (meandmymonkey) - Noel Guilbert (noel) - Stepan Anchugov (kix) @@ -201,7 +202,6 @@ Symfony is the result of the work of many people who made the code better - John Kary (johnkary) - Justin Hileman (bobthecow) - Blanchon Vincent (blanchonvincent) - - Christian Schmidt - Michele Orselli (orso) - Tom Van Looy (tvlooy) - Sven Paulus (subsven) @@ -238,6 +238,7 @@ Symfony is the result of the work of many people who made the code better - Katsuhiro OGAWA - Patrick McDougle (patrick-mcdougle) - Alif Rachmawadi + - Alessandro Chitolina - Kristen Gilden (kgilden) - Pierre-Yves LEBECQ (pylebecq) - Jordan Samouh (jordansamouh) @@ -263,6 +264,8 @@ Symfony is the result of the work of many people who made the code better - Pavel Batanov (scaytrase) - Nikita Konstantinov - Wodor Wodorski + - Rob Frawley 2nd (robfrawley) + - Gregor Harlan (gharlan) - Thomas Lallement (raziel057) - Giorgio Premi - Matthieu Napoli (mnapoli) @@ -278,7 +281,6 @@ Symfony is the result of the work of many people who made the code better - Marc Weistroff (futurecat) - Christian Schmidt - Hidde Wieringa (hiddewie) - - Alessandro Chitolina - Chad Sikorra (chadsikorra) - Chris Smith (cs278) - Florian Klein (docteurklein) @@ -315,7 +317,6 @@ Symfony is the result of the work of many people who made the code better - Thierry Thuon (lepiaf) - Ricard Clau (ricardclau) - Mark Challoner (markchalloner) - - Gregor Harlan (gharlan) - Gennady Telegin (gtelegin) - Ben Davies (bendavies) - Erin Millard @@ -324,7 +325,6 @@ Symfony is the result of the work of many people who made the code better - Magnus Nordlander (magnusnordlander) - alquerci - Francesco Levorato - - Rob Frawley 2nd (robfrawley) - Vitaliy Zakharov (zakharovvi) - Tobias Sjösten (tobiassjosten) - Gyula Sallai (salla) @@ -341,6 +341,7 @@ Symfony is the result of the work of many people who made the code better - Thomas Calvet (fancyweb) - Niels Keurentjes (curry684) - JhonnyL + - David Badura (davidbadura) - hossein zolfi (ocean) - Clément Gautier (clementgautier) - Eduardo Gulias (egulias) @@ -430,7 +431,6 @@ Symfony is the result of the work of many people who made the code better - Christian Wahler - Gintautas Miselis - Rob Bast - - David Badura (davidbadura) - Zander Baldwin - Adam Harvey - Maxime Veber (nek-) @@ -545,6 +545,7 @@ Symfony is the result of the work of many people who made the code better - Max Rath (drak3) - Stéphane Escandell (sescandell) - Konstantin S. M. Möllers (ksmmoellers) + - James Johnston - Sinan Eldem - Alexandre Dupuy (satchette) - Andre Rømcke (andrerom) @@ -593,6 +594,7 @@ Symfony is the result of the work of many people who made the code better - Ulumuddin Yunus (joenoez) - Luc Vieillescazes (iamluc) - Johann Saunier (prophet777) + - Valentin Udaltsov (vudaltsov) - Michael Devery (mickadoo) - Antoine Corcy - Artur Eshenbrener @@ -911,6 +913,7 @@ Symfony is the result of the work of many people who made the code better - Alex Demchenko (pilot) - Tadas Gliaubicas (tadcka) - Benoit Garret + - Jakub Sacha - DerManoMann - Olaf Klischat - orlovv @@ -1175,6 +1178,7 @@ Symfony is the result of the work of many people who made the code better - Malte Wunsch - wusuopu - povilas + - Gavin Staniforth - Alessandro Tagliapietra (alex88) - Biji (biji) - Gunnar Lium (gunnarlium) @@ -1236,6 +1240,7 @@ Symfony is the result of the work of many people who made the code better - flack - izzyp - František Bereň + - Mike Francis - Christoph Nissle (derstoffel) - Ionel Scutelnicu (ionelscutelnicu) - Nicolas Tallefourtané (nicolab) @@ -1246,6 +1251,7 @@ Symfony is the result of the work of many people who made the code better - jjanvier - Julius Beckmann - Romain Dorgueil + - Christopher Parotat - Grayson Koonce (breerly) - Fabien LUCAS (flucas2) - Indra Gunawan (indragunawan) @@ -1537,11 +1543,13 @@ Symfony is the result of the work of many people who made the code better - Ladislav Tánczos - Brian Freytag - Skorney + - fmarchalemisys - mieszko4 - Steve Preston - Neophy7e - bokonet - Arrilot + - Shaun Simmons - Markus Staab - Pierre-Louis LAUNAY - djama @@ -1570,6 +1578,7 @@ Symfony is the result of the work of many people who made the code better - Penny Leach - Richard Trebichavský - g123456789l + - Jonathan Vollebregt - oscartv - DanSync - Peter Zwosta @@ -1682,7 +1691,6 @@ Symfony is the result of the work of many people who made the code better - Moritz Kraft (userfriendly) - Víctor Mateo (victormateo) - Vincent (vincent1870) - - Valentin Udaltsov (vudaltsov) - Eugene Babushkin (warl) - Wouter Sioen (wouter_sioen) - Xavier Amado (xamado) @@ -1704,6 +1712,7 @@ Symfony is the result of the work of many people who made the code better - Sergey Fedotov - Michael - fh-github@fholzhauer.de + - AbdElKader Bouadjadja - Jan Emrich - Mark Topper - Xavier REN diff --git a/UPGRADE-3.3.md b/UPGRADE-3.3.md index e43c365e6c5b6..3cff95d8506ce 100644 --- a/UPGRADE-3.3.md +++ b/UPGRADE-3.3.md @@ -165,9 +165,6 @@ Form FrameworkBundle --------------- - * The `cache:clear` command should always be called with the `--no-warmup` option. - Warmup should be done via the `cache:warmup` command. - * [BC BREAK] The "framework.trusted_proxies" configuration option and the corresponding "kernel.trusted_proxies" parameter have been removed. Use the Request::setTrustedProxies() method in your front controller instead. diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index 505ed8968a37b..b1bfc214837a4 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -116,8 +116,8 @@ DependencyInjection * Using unsupported options to configure service aliases raises an exception. - * Setting or unsetting a private service with the `Container::set()` method is - no longer supported. Only public services can be set or unset. + * Setting or unsetting a service with the `Container::set()` method is + no longer supported. Only synthetic services can be set or unset. * Checking the existence of a private service with the `Container::has()` method is no longer supported and will return `false`. @@ -223,9 +223,6 @@ Form FrameworkBundle --------------- - * The `cache:clear` command does not warmup the cache anymore. Warmup should - be done via the `cache:warmup` command. - * The "framework.trusted_proxies" configuration option and the corresponding "kernel.trusted_proxies" parameter have been removed. Use the `Request::setTrustedProxies()` method in your front controller instead. * The default value of the `framework.workflows.[name].type` configuration options is now `state_machine`. diff --git a/composer.json b/composer.json index cf55aeb8ef072..ff894a349e227 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "ext-xml": "*", "doctrine/common": "~2.4", "fig/link-util": "^1.0", @@ -98,11 +98,11 @@ "symfony/phpunit-bridge": "~3.2", "symfony/polyfill-apcu": "~1.1", "symfony/security-acl": "~2.8|~3.0", - "phpdocumentor/reflection-docblock": "^3.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0", "sensio/framework-extra-bundle": "^3.0.2" }, "conflict": { - "phpdocumentor/reflection-docblock": "<3.0||>=3.2.0", + "phpdocumentor/reflection-docblock": "<3.0||>=3.2.0,<3.2.2", "phpdocumentor/type-resolver": "<0.2.0", "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" }, diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index 67854035ce6b8..8876d794a1100 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "doctrine/common": "~2.4", "symfony/polyfill-mbstring": "~1.0" }, diff --git a/src/Symfony/Bridge/Monolog/composer.json b/src/Symfony/Bridge/Monolog/composer.json index d60949aab872c..23240155d267f 100644 --- a/src/Symfony/Bridge/Monolog/composer.json +++ b/src/Symfony/Bridge/Monolog/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "monolog/monolog": "~1.19", "symfony/http-kernel": "~2.8|~3.0" }, diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit index 4683682a1bf6e..c19d30f4af9e6 100755 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit @@ -15,8 +15,17 @@ error_reporting(-1); -// PHPUnit 4.8 does not support PHP 7, while 5.1 requires PHP 5.6+ -$PHPUNIT_VERSION = PHP_VERSION_ID >= 50600 ? getenv('SYMFONY_PHPUNIT_VERSION') ?: '5.7' : '4.8'; +if (PHP_VERSION_ID >= 70200) { + // PHPUnit 6 is required for PHP 7.2+ + $PHPUNIT_VERSION = getenv('SYMFONY_PHPUNIT_VERSION') ?: '6.3'; +} elseif (PHP_VERSION_ID >= 50600) { + // PHPUnit 4 does not support PHP 7 + $PHPUNIT_VERSION = getenv('SYMFONY_PHPUNIT_VERSION') ?: '5.7'; +} else { + // PHPUnit 5.1 requires PHP 5.6+ + $PHPUNIT_VERSION = '4.8'; +} + $oldPwd = getcwd(); $PHPUNIT_DIR = getenv('SYMFONY_PHPUNIT_DIR') ?: (__DIR__.'/.phpunit'); $PHP = defined('PHP_BINARY') ? PHP_BINARY : 'php'; diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/LazyLoadingValueHolderFactoryV1.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/LazyLoadingValueHolderFactoryV1.php new file mode 100644 index 0000000000000..3298b84d46278 --- /dev/null +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/LazyLoadingValueHolderFactoryV1.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\ProxyManager\LazyProxy\Instantiator; + +use ProxyManager\Factory\LazyLoadingValueHolderFactory as BaseFactory; +use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\LazyLoadingValueHolderGenerator; + +/** + * @internal + */ +class LazyLoadingValueHolderFactoryV1 extends BaseFactory +{ + private $generatorV1; + + /** + * {@inheritdoc} + */ + protected function getGenerator() + { + return $this->generatorV1 ?: $this->generatorV1 = new LazyLoadingValueHolderGenerator(); + } +} diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/LazyLoadingValueHolderFactoryV2.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/LazyLoadingValueHolderFactoryV2.php new file mode 100644 index 0000000000000..f41fc20b5d523 --- /dev/null +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/LazyLoadingValueHolderFactoryV2.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\ProxyManager\LazyProxy\Instantiator; + +use ProxyManager\ProxyGenerator\ProxyGeneratorInterface; +use ProxyManager\Factory\LazyLoadingValueHolderFactory as BaseFactory; +use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\LazyLoadingValueHolderGenerator; + +/** + * @internal + */ +class LazyLoadingValueHolderFactoryV2 extends BaseFactory +{ + private $generator; + + /** + * {@inheritdoc} + */ + protected function getGenerator(): ProxyGeneratorInterface + { + return $this->generator ?: $this->generator = new LazyLoadingValueHolderGenerator(); + } +} diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php index 0101026794c7c..33fc49e1012d9 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php @@ -36,7 +36,11 @@ public function __construct() $config = new Configuration(); $config->setGeneratorStrategy(new EvaluatingGeneratorStrategy()); - $this->factory = new LazyLoadingValueHolderFactory($config); + if (method_exists('ProxyManager\Version', 'getVersion')) { + $this->factory = new LazyLoadingValueHolderFactoryV2($config); + } else { + $this->factory = new LazyLoadingValueHolderFactoryV1($config); + } } /** diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/LazyLoadingValueHolderGenerator.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/LazyLoadingValueHolderGenerator.php new file mode 100644 index 0000000000000..1d9432f622b41 --- /dev/null +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/LazyLoadingValueHolderGenerator.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper; + +use ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator as BaseGenerator; +use Zend\Code\Generator\ClassGenerator; + +/** + * @internal + */ +class LazyLoadingValueHolderGenerator extends BaseGenerator +{ + /** + * {@inheritdoc} + */ + public function generate(\ReflectionClass $originalClass, ClassGenerator $classGenerator) + { + parent::generate($originalClass, $classGenerator); + + if ($classGenerator->hasMethod('__destruct')) { + $destructor = $classGenerator->getMethod('__destruct'); + $body = $destructor->getBody(); + $newBody = preg_replace('/^(\$this->initializer[a-zA-Z0-9]++) && .*;\n\nreturn (\$this->valueHolder)/', '$1 || $2', $body); + + if ($body === $newBody) { + throw new \UnexpectedValueException(sprintf('Unexpected lazy-proxy format generated for method %s::__destruct()', $originalClass->name)); + } + + $destructor->setBody($newBody); + } + } +} diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php index b91bfeb922cf8..c2dd94a73a656 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php @@ -13,7 +13,6 @@ use ProxyManager\Generator\ClassGenerator; use ProxyManager\GeneratorStrategy\BaseGeneratorStrategy; -use ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface; diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php index b634a69488a34..858e9d76b64c9 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php @@ -39,6 +39,9 @@ public function testCreateProxyServiceWithRuntimeInstantiator() /* @var $foo1 \ProxyManager\Proxy\LazyLoadingInterface|\ProxyManager\Proxy\ValueHolderInterface */ $foo1 = $builder->get('foo1'); + $foo1->__destruct(); + $this->assertSame(0, $foo1::$destructorCount); + $this->assertSame($foo1, $builder->get('foo1'), 'The same proxy is retrieved on multiple subsequent calls'); $this->assertInstanceOf('\ProxyManagerBridgeFooClass', $foo1); $this->assertInstanceOf('\ProxyManager\Proxy\LazyLoadingInterface', $foo1); @@ -50,5 +53,8 @@ public function testCreateProxyServiceWithRuntimeInstantiator() $this->assertTrue($foo1->isProxyInitialized()); $this->assertInstanceOf('\ProxyManagerBridgeFooClass', $foo1->getWrappedValueHolderValue()); $this->assertNotInstanceOf('\ProxyManager\Proxy\LazyLoadingInterface', $foo1->getWrappedValueHolderValue()); + + $foo1->__destruct(); + $this->assertSame(1, $foo1::$destructorCount); } } diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/includes/foo.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/includes/foo.php index 16c898a370845..8ffc5be9af40a 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/includes/foo.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/includes/foo.php @@ -2,6 +2,8 @@ class ProxyManagerBridgeFooClass { + public static $destructorCount = 0; + public $foo; public $moo; @@ -38,4 +40,9 @@ public function setBar($value = null) { $this->bar = $value; } + + public function __destruct() + { + ++self::$destructorCount; + } } diff --git a/src/Symfony/Bridge/ProxyManager/composer.json b/src/Symfony/Bridge/ProxyManager/composer.json index 42751d000e59d..43a674eac9b25 100644 --- a/src/Symfony/Bridge/ProxyManager/composer.json +++ b/src/Symfony/Bridge/ProxyManager/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/dependency-injection": "~2.8|~3.0", "ocramius/proxy-manager": "~0.4|~1.0|~2.0" }, diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 8fe41945b72aa..e00e3dc7fad35 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "twig/twig": "~1.34|~2.4" }, "require-dev": { diff --git a/src/Symfony/Bundle/DebugBundle/DebugBundle.php b/src/Symfony/Bundle/DebugBundle/DebugBundle.php index 335ec5abd2541..b00c06af78289 100644 --- a/src/Symfony/Bundle/DebugBundle/DebugBundle.php +++ b/src/Symfony/Bundle/DebugBundle/DebugBundle.php @@ -27,8 +27,10 @@ public function boot() $container = $this->container; // This code is here to lazy load the dump stack. This default - // configuration for CLI mode is overridden in HTTP mode on - // 'kernel.request' event + // configuration is overridden in CLI mode on 'console.command' event. + // The dump data collector is used by default, so dump output is sent to + // the WDT. In a CLI context, if dump is used too soon, the data collector + // will buffer it, and release it at the end of the script. VarDumper::setHandler(function ($var) use ($container) { $dumper = $container->get('data_collector.dump'); $cloner = $container->get('var_dumper.cloner'); diff --git a/src/Symfony/Bundle/DebugBundle/composer.json b/src/Symfony/Bundle/DebugBundle/composer.json index 77e2d1c5621ba..15a8611363ad5 100644 --- a/src/Symfony/Bundle/DebugBundle/composer.json +++ b/src/Symfony/Bundle/DebugBundle/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "ext-xml": "*", "symfony/http-kernel": "~2.8|~3.0", "symfony/twig-bridge": "~2.8|~3.0", diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index ba704e00f2a7a..d89c4dc254134 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -14,7 +14,6 @@ CHANGELOG the same helpers as the `Controller` class, but does not allow accessing the dependency injection container, in order to encourage explicit dependency declarations. * Added support for the `controller.service_arguments` tag, for injecting services into controllers' actions - * Deprecated `cache:clear` with warmup (always call it with `--no-warmup`) * Changed default configuration for assets/forms/validation/translation/serialization/csrf from `canBeEnabled()` to `canBeDisabled()` when Flex is used diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php index 2331afecb4e84..ca119f198d5c1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php @@ -15,6 +15,7 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Finder\Finder; @@ -56,6 +57,10 @@ protected function execute(InputInterface $input, OutputInterface $output) { $io = new SymfonyStyle($input, $output); + if (Kernel::VERSION_ID >= 30400) { + throw new \LogicException('The "cache:clear" command in Symfony 3.3 is incompatible with HttpKernel 3.4, please upgrade "symfony/framework-bundle" or downgrade "symfony/http-kernel".'); + } + $realCacheDir = $this->getContainer()->getParameter('kernel.cache_dir'); // the old cache dir name must not be longer than the real one to avoid exceeding // the maximum length of a directory or file path within it (esp. Windows MAX_PATH) @@ -77,12 +82,6 @@ protected function execute(InputInterface $input, OutputInterface $output) if ($input->getOption('no-warmup')) { $filesystem->rename($realCacheDir, $oldCacheDir); } else { - $warning = 'Calling cache:clear without the --no-warmup option is deprecated since version 3.3. Cache warmup should be done with the cache:warmup command instead.'; - - @trigger_error($warning, E_USER_DEPRECATED); - - $io->warning($warning); - $this->warmupCache($input, $output, $realCacheDir, $oldCacheDir); } @@ -132,8 +131,6 @@ private function warmupCache(InputInterface $input, OutputInterface $output, $re * @param string $warmupDir * @param string $realCacheDir * @param bool $enableOptionalWarmers - * - * @internal to be removed in 4.0 */ protected function warmup($warmupDir, $realCacheDir, $enableOptionalWarmers = true) { @@ -200,8 +197,6 @@ protected function warmup($warmupDir, $realCacheDir, $enableOptionalWarmers = tr * @param string $warmupDir * * @return KernelInterface - * - * @internal to be removed in 4.0 */ protected function getTempKernel(KernelInterface $parent, $namespace, $parentClass, $warmupDir) { diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.php new file mode 100644 index 0000000000000..349d78cfc4fed --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\LogicException; + +/** + * @author Christian Flothmann + */ +class WorkflowGuardListenerPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasParameter('workflow.has_guard_listeners')) { + return; + } + + $container->getParameterBag()->remove('workflow.has_guard_listeners'); + + if (!$container->has('security.token_storage')) { + throw new LogicException('The "security.token_storage" service is needed to be able to use the workflow guard listener.'); + } + + if (!$container->has('security.authorization_checker')) { + throw new LogicException('The "security.authorization_checker" service is needed to be able to use the workflow guard listener.'); + } + + if (!$container->has('security.authentication.trust_resolver')) { + throw new LogicException('The "security.authentication.trust_resolver" service is needed to be able to use the workflow guard listener.'); + } + + if (!$container->has('security.role_hierarchy')) { + throw new LogicException('The "security.role_hierarchy" service is needed to be able to use the workflow guard listener.'); + } + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 131db0ae86343..d84255d94e3c9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -46,6 +46,7 @@ use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface; use Symfony\Component\PropertyInfo\PropertyListExtractorInterface; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; +use Symfony\Component\Security\Core\Security; use Symfony\Component\Serializer\Encoder\CsvEncoder; use Symfony\Component\Serializer\Encoder\DecoderInterface; use Symfony\Component\Serializer\Encoder\EncoderInterface; @@ -242,8 +243,8 @@ public function load(array $configs, ContainerBuilder $container) } $this->addAnnotatedClassesToCompile(array( - '**Bundle\\Controller\\', - '**Bundle\\Entity\\', + '**\\Controller\\', + '**\\Entity\\', // Added explicitly so that we don't rely on the class map being dumped to make it work 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller', @@ -597,6 +598,10 @@ private function registerWorkflowConfiguration(array $workflows, ContainerBuilde throw new LogicException('Cannot guard workflows as the ExpressionLanguage component is not installed.'); } + if (!class_exists(Security::class)) { + throw new LogicException('Cannot guard workflows as the Security component is not installed.'); + } + $eventName = sprintf('workflow.%s.guard.%s', $name, $transitionName); $guard->addTag('kernel.event_listener', array('event' => $eventName, 'method' => 'onTransition')); $configuration[$eventName] = $config['guard']; @@ -612,6 +617,7 @@ private function registerWorkflowConfiguration(array $workflows, ContainerBuilde )); $container->setDefinition(sprintf('%s.listener.guard', $workflowId), $guard); + $container->setParameter('workflow.has_guard_listeners', true); } } } @@ -1273,7 +1279,7 @@ private function registerSecurityCsrfConfiguration(array $config, ContainerBuild private function registerSerializerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { if (class_exists('Symfony\Component\Serializer\Normalizer\DataUriNormalizer')) { - // Run after serializer.normalizer.object + // Run before serializer.normalizer.object $definition = $container->register('serializer.normalizer.data_uri', DataUriNormalizer::class); $definition->setPublic(false); $definition->addTag('serializer.normalizer', array('priority' => -920)); @@ -1340,7 +1346,7 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $fileRecorder('yml', $file); } - if ($container->fileExists($dir = $dirname.'/Resources/config/serialization')) { + if ($container->fileExists($dir = $dirname.'/Resources/config/serialization', '/^$/')) { $this->registerMappingFilesFromDir($dir, $fileRecorder); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php index 99ab06b337772..104795e5593c3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php @@ -28,6 +28,7 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationExtractorPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationDumperPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\WorkflowGuardListenerPass; use Symfony\Component\Config\DependencyInjection\ConfigCachePass; use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass; use Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass; @@ -109,6 +110,7 @@ public function build(ContainerBuilder $container) $this->addCompilerPassIfExists($container, ValidateWorkflowsPass::class); $container->addCompilerPass(new CachePoolClearerPass(), PassConfig::TYPE_AFTER_REMOVING); $this->addCompilerPassIfExists($container, FormPass::class); + $container->addCompilerPass(new WorkflowGuardListenerPass()); if ($container->getParameter('kernel.debug')) { $container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php index b9fe63ec5bbab..71f2ed551b668 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php @@ -20,6 +20,7 @@ use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Finder\Finder; +use Symfony\Component\HttpKernel\Kernel; class CacheClearCommandTest extends TestCase { @@ -43,15 +44,23 @@ protected function tearDown() $this->fs->remove($this->rootDir); } - /** - * @group legacy - */ public function testCacheIsFreshAfterCacheClearedWithWarmup() { $input = new ArrayInput(array('cache:clear')); $application = new Application($this->kernel); $application->setCatchExceptions(false); + if (Kernel::VERSION_ID >= 30400) { + $expectedMsg = 'The "cache:clear" command in Symfony 3.3 is incompatible with HttpKernel 3.4, please upgrade "symfony/framework-bundle" or downgrade "symfony/http-kernel".'; + + if (method_exists($this, 'expectException')) { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage($expectedMsg); + } else { + $this->setExpectedException(\LogicException::class, $expectedMsg); + } + } + $application->doRun($input, new NullOutput()); // Ensure that all *.meta files are fresh diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/WorkflowGuardListenerPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/WorkflowGuardListenerPassTest.php new file mode 100644 index 0000000000000..fead9e28f26d0 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/WorkflowGuardListenerPassTest.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\WorkflowGuardListenerPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; +use Symfony\Component\Security\Core\Role\RoleHierarchy; +use Symfony\Component\Workflow\EventListener\GuardListener; + +class WorkflowGuardListenerPassTest extends TestCase +{ + private $container; + private $compilerPass; + + protected function setUp() + { + $this->container = new ContainerBuilder(); + $this->container->register('foo.listener.guard', GuardListener::class); + $this->container->register('bar.listener.guard', GuardListener::class); + $this->compilerPass = new WorkflowGuardListenerPass(); + } + + public function testListenersAreNotRemovedIfParameterIsNotSet() + { + $this->compilerPass->process($this->container); + + $this->assertTrue($this->container->hasDefinition('foo.listener.guard')); + $this->assertTrue($this->container->hasDefinition('bar.listener.guard')); + } + + public function testParameterIsRemovedWhenThePassIsProcessed() + { + $this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard')); + + try { + $this->compilerPass->process($this->container); + } catch (LogicException $e) { + // Here, we are not interested in the exception handling. This is tested further down. + } + + $this->assertFalse($this->container->hasParameter('workflow.has_guard_listeners')); + } + + public function testListenersAreNotRemovedIfAllDependenciesArePresent() + { + $this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard')); + $this->container->register('security.token_storage', TokenStorageInterface::class); + $this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class); + $this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class); + $this->container->register('security.role_hierarchy', RoleHierarchy::class); + + $this->compilerPass->process($this->container); + + $this->assertTrue($this->container->hasDefinition('foo.listener.guard')); + $this->assertTrue($this->container->hasDefinition('bar.listener.guard')); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException + * @expectedExceptionMessage The "security.token_storage" service is needed to be able to use the workflow guard listener. + */ + public function testListenersAreRemovedIfTheTokenStorageServiceIsNotPresent() + { + $this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard')); + $this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class); + $this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class); + $this->container->register('security.role_hierarchy', RoleHierarchy::class); + + $this->compilerPass->process($this->container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException + * @expectedExceptionMessage The "security.authorization_checker" service is needed to be able to use the workflow guard listener. + */ + public function testListenersAreRemovedIfTheAuthorizationCheckerServiceIsNotPresent() + { + $this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard')); + $this->container->register('security.token_storage', TokenStorageInterface::class); + $this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class); + $this->container->register('security.role_hierarchy', RoleHierarchy::class); + + $this->compilerPass->process($this->container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException + * @expectedExceptionMessage The "security.authentication.trust_resolver" service is needed to be able to use the workflow guard listener. + */ + public function testListenersAreRemovedIfTheAuthenticationTrustResolverServiceIsNotPresent() + { + $this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard')); + $this->container->register('security.token_storage', TokenStorageInterface::class); + $this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class); + $this->container->register('security.role_hierarchy', RoleHierarchy::class); + + $this->compilerPass->process($this->container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException + * @expectedExceptionMessage The "security.role_hierarchy" service is needed to be able to use the workflow guard listener. + */ + public function testListenersAreRemovedIfTheRoleHierarchyServiceIsNotPresent() + { + $this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard')); + $this->container->register('security.token_storage', TokenStorageInterface::class); + $this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class); + $this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class); + + $this->compilerPass->process($this->container); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 1105daa373f43..f542a5a2a8f9a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "ext-xml": "*", "symfony/cache": "~3.3", "symfony/class-loader": "~3.2", @@ -55,7 +55,7 @@ "symfony/property-info": "~3.3", "symfony/web-link": "~3.3", "doctrine/annotations": "~1.0", - "phpdocumentor/reflection-docblock": "^3.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0", "twig/twig": "~1.34|~2.4", "sensio/framework-extra-bundle": "^3.0.2" }, diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 2c9a641e503d7..48de2c03ef46a 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "ext-xml": "*", "symfony/security": "~3.3", "symfony/dependency-injection": "~3.3", diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php index e8c10786c457d..a1ae0caba145d 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php @@ -13,6 +13,7 @@ use Symfony\Bridge\Twig\Extension\WebLinkExtension; use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Resource\FileExistenceResource; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; @@ -118,9 +119,10 @@ public function load(array $configs, ContainerBuilder $container) } } - if ($container->fileExists($dir = $container->getParameter('kernel.root_dir').'/Resources/views', false)) { + if (file_exists($dir = $container->getParameter('kernel.root_dir').'/Resources/views')) { $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($dir)); } + $container->addResource(new FileExistenceResource($dir)); if (!empty($config['globals'])) { $def = $container->getDefinition('twig'); @@ -178,13 +180,15 @@ private function getBundleHierarchy(ContainerBuilder $container) ); } - if ($container->fileExists($dir = $container->getParameter('kernel.root_dir').'/Resources/'.$name.'/views', false)) { + if (file_exists($dir = $container->getParameter('kernel.root_dir').'/Resources/'.$name.'/views')) { $bundleHierarchy[$name]['paths'][] = $dir; } + $container->addResource(new FileExistenceResource($dir)); - if ($container->fileExists($dir = $bundle['path'].'/Resources/views', false)) { + if (file_exists($dir = $bundle['path'].'/Resources/views')) { $bundleHierarchy[$name]['paths'][] = $dir; } + $container->addResource(new FileExistenceResource($dir)); if (null === $bundle['parent']) { continue; diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig b/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig index 9d03015f2f04e..84112fad6ed94 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig +++ b/src/Symfony/Bundle/TwigBundle/Resources/views/exception.css.twig @@ -80,7 +80,7 @@ header .container { display: flex; justify-content: space-between; } .exception-hierarchy .icon svg { height: 13px; width: 13px; vertical-align: -2px; } .exception-without-message .exception-message-wrapper { display: none; } -.exception-message-wrapper .container { display: flex; align-items: flex-start; min-height: 70px; padding: 10px 0 8px; } +.exception-message-wrapper .container { display: flex; align-items: flex-start; min-height: 70px; padding: 10px 15px 8px; } .exception-message { flex-grow: 1; } .exception-message, .exception-message a { color: #FFF; font-size: 21px; font-weight: 400; margin: 0; } .exception-message.long { font-size: 18px; } @@ -107,11 +107,11 @@ header .container { display: flex; justify-content: space-between; } .trace-line .icon svg { height: 16px; width: 16px; } .trace-line-header { padding-left: 36px; } -.trace-file-path, .trace-file-path a { color: #999; color: #795da3; color: #B0413E; color: #222; font-size: 13px; } +.trace-file-path, .trace-file-path a { color: #222; font-size: 13px; } .trace-class { color: #B0413E; } .trace-type { padding: 0 2px; } -.trace-method { color: #B0413E; color: #222; font-weight: bold; color: #B0413E; } -.trace-arguments { color: #222; color: #999; font-weight: normal; color: #795da3; color: #777; padding-left: 2px; } +.trace-method { color: #B0413E; font-weight: bold; } +.trace-arguments { color: #777; font-weight: normal; padding-left: 2px; } .trace-code { background: #FFF; font-size: 12px; margin: 10px 10px 2px 10px; padding: 10px; overflow-x: auto; white-space: nowrap; } .trace-code ol { margin: 0; float: left; } diff --git a/src/Symfony/Bundle/TwigBundle/composer.json b/src/Symfony/Bundle/TwigBundle/composer.json index 1b3cc91c1969c..1a0908a580a83 100644 --- a/src/Symfony/Bundle/TwigBundle/composer.json +++ b/src/Symfony/Bundle/TwigBundle/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/config": "~3.2", "symfony/twig-bridge": "^3.3", "symfony/http-foundation": "~2.8|~3.0", 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 4fc6a82c58298..5fe45a0f4ed39 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig @@ -1,21 +1,19 @@ {% extends '@WebProfiler/Profiler/layout.html.twig' %} {% block toolbar %} + {% import _self as helper %} {% set request_handler %} - {% import _self as helper %} {{ helper.set_handler(collector.controller) }} {% endset %} {% if collector.redirect %} {% set redirect_handler %} - {% import _self as helper %} {{ helper.set_handler(collector.redirect.controller, collector.redirect.route, 'GET' != collector.redirect.method ? collector.redirect.method) }} {% endset %} {% endif %} {% if collector.forward|default(false) %} {% set forward_handler %} - {% import _self as helper %} {{ helper.set_handler(collector.forward.controller) }} {% endset %} {% endif %} @@ -108,6 +106,12 @@ {% endblock %} {% block panel %} + {% import _self as helper %} + +

+ {{ helper.set_handler(collector.controller) }} +

+

Request

@@ -268,7 +272,7 @@ {% for child in profile.children %}

- {{- child.getcollector('request').identifier -}} + {{ helper.set_handler(child.getcollector('request').controller) }} (token = {{ child.token }})

diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig index bd097d3cbc49b..822323315e37d 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig @@ -20,7 +20,7 @@ {% set request_collector = profile.collectors.request|default(false) %} - {% if request_collector is defined and request_collector.redirect -%} + {% if request_collector and request_collector.redirect -%} {%- set redirect = request_collector.redirect -%} {%- set controller = redirect.controller -%} {%- set redirect_route = '@' ~ redirect.route %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/composer.json b/src/Symfony/Bundle/WebProfilerBundle/composer.json index 626ac4d307d51..12b168150c58d 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/composer.json +++ b/src/Symfony/Bundle/WebProfilerBundle/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/http-kernel": "~3.2", "symfony/polyfill-php70": "~1.0", "symfony/routing": "~2.8|~3.0", diff --git a/src/Symfony/Bundle/WebServerBundle/WebServer.php b/src/Symfony/Bundle/WebServerBundle/WebServer.php index 8edbe2bf56ec2..e3425ec8bb13a 100644 --- a/src/Symfony/Bundle/WebServerBundle/WebServer.php +++ b/src/Symfony/Bundle/WebServerBundle/WebServer.php @@ -154,6 +154,11 @@ private function createServerProcess(WebServerConfig $config) $process->setWorkingDirectory($config->getDocumentRoot()); $process->setTimeout(null); + if (in_array('APP_ENV', explode(',', getenv('SYMFONY_DOTENV_VARS')))) { + $process->setEnv(array('APP_ENV' => false)); + $process->inheritEnvironmentVariables(); + } + return $process; } diff --git a/src/Symfony/Bundle/WebServerBundle/composer.json b/src/Symfony/Bundle/WebServerBundle/composer.json index 829e110c35206..d1c64e970fcca 100644 --- a/src/Symfony/Bundle/WebServerBundle/composer.json +++ b/src/Symfony/Bundle/WebServerBundle/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/console": "~3.3", "symfony/http-kernel": "~3.3", "symfony/process": "~3.3" diff --git a/src/Symfony/Component/Asset/composer.json b/src/Symfony/Component/Asset/composer.json index 8ed8d9d725212..39ccf2c00170d 100644 --- a/src/Symfony/Component/Asset/composer.json +++ b/src/Symfony/Component/Asset/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "suggest": { "symfony/http-foundation": "" diff --git a/src/Symfony/Component/BrowserKit/composer.json b/src/Symfony/Component/BrowserKit/composer.json index a18d66e81ea73..b6b7cb01016ab 100644 --- a/src/Symfony/Component/BrowserKit/composer.json +++ b/src/Symfony/Component/BrowserKit/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/dom-crawler": "~2.8|~3.0" }, "require-dev": { diff --git a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php index 26b33e683e2ff..9bc05fd2b30fd 100644 --- a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php @@ -55,19 +55,20 @@ function ($key, $value, $isHit) use ($defaultLifetime) { null, CacheItem::class ); + $getId = function ($key) { return $this->getId((string) $key); }; $this->mergeByLifetime = \Closure::bind( - function ($deferred, $namespace, &$expiredIds) { + function ($deferred, $namespace, &$expiredIds) use ($getId) { $byLifetime = array(); $now = time(); $expiredIds = array(); foreach ($deferred as $key => $item) { if (null === $item->expiry) { - $byLifetime[0 < $item->defaultLifetime ? $item->defaultLifetime : 0][$namespace.$key] = $item->value; + $byLifetime[0 < $item->defaultLifetime ? $item->defaultLifetime : 0][$getId($key)] = $item->value; } elseif ($item->expiry > $now) { - $byLifetime[$item->expiry - $now][$namespace.$key] = $item->value; + $byLifetime[$item->expiry - $now][$getId($key)] = $item->value; } else { - $expiredIds[] = $namespace.$key; + $expiredIds[] = $getId($key); } } diff --git a/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php b/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php index ea102ddbf75fb..7e9686a01855e 100644 --- a/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php @@ -38,6 +38,7 @@ public function __construct($file, AdapterInterface $fallbackPool) { $this->file = $file; $this->fallbackPool = $fallbackPool; + $this->zendDetectUnicode = ini_get('zend.detect_unicode'); $this->createCacheItem = \Closure::bind( function ($key, $value, $isHit) { $item = new CacheItem(); diff --git a/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php b/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php index fee500dbfc7e2..96846e69ba29c 100644 --- a/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php @@ -35,5 +35,6 @@ public function __construct($namespace = '', $defaultLifetime = 0, $directory = $e = new \Exception(); $this->includeHandler = function () use ($e) { throw $e; }; + $this->zendDetectUnicode = ini_get('zend.detect_unicode'); } } diff --git a/src/Symfony/Component/Cache/Simple/PhpArrayCache.php b/src/Symfony/Component/Cache/Simple/PhpArrayCache.php index c65b8fffaac41..92180e63168bc 100644 --- a/src/Symfony/Component/Cache/Simple/PhpArrayCache.php +++ b/src/Symfony/Component/Cache/Simple/PhpArrayCache.php @@ -34,6 +34,7 @@ public function __construct($file, CacheInterface $fallbackPool) { $this->file = $file; $this->fallbackPool = $fallbackPool; + $this->zendDetectUnicode = ini_get('zend.detect_unicode'); } /** diff --git a/src/Symfony/Component/Cache/Simple/PhpFilesCache.php b/src/Symfony/Component/Cache/Simple/PhpFilesCache.php index 810b80f81275c..dd92a8a03bd67 100644 --- a/src/Symfony/Component/Cache/Simple/PhpFilesCache.php +++ b/src/Symfony/Component/Cache/Simple/PhpFilesCache.php @@ -35,5 +35,6 @@ public function __construct($namespace = '', $defaultLifetime = 0, $directory = $e = new \Exception(); $this->includeHandler = function () use ($e) { throw $e; }; + $this->zendDetectUnicode = ini_get('zend.detect_unicode'); } } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php index c3cbd3bef7e54..faecda830cd7c 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php @@ -45,6 +45,26 @@ public function testDefaultLifeTime() $this->assertFalse($item->isHit()); } + public function testExpiration() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + $cache = $this->createCachePool(); + $cache->save($cache->getItem('k1')->set('v1')->expiresAfter(2)); + $cache->save($cache->getItem('k2')->set('v2')->expiresAfter(366 * 86400)); + + sleep(3); + $item = $cache->getItem('k1'); + $this->assertFalse($item->isHit()); + $this->assertNull($item->get(), "Item's value must be null when isHit() is false."); + + $item = $cache->getItem('k2'); + $this->assertTrue($item->isHit()); + $this->assertSame('v2', $item->get()); + } + public function testNotUnserializable() { if (isset($this->skippedTests[__FUNCTION__])) { diff --git a/src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php index 82b41c3b4d870..00e129398ed9a 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php @@ -17,7 +17,6 @@ class MemcachedAdapterTest extends AdapterTestCase { protected $skippedTests = array( - 'testExpiration' => 'Testing expiration slows down the test suite', 'testHasItemReturnsFalseWhenDeferredItemIsExpired' => 'Testing expiration slows down the test suite', 'testDefaultLifeTime' => 'Testing expiration slows down the test suite', ); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php index ae0edb7d11dd6..134dba7c90444 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php @@ -22,6 +22,7 @@ class PhpArrayAdapterTest extends AdapterTestCase { protected $skippedTests = array( 'testBasicUsage' => 'PhpArrayAdapter is read-only.', + 'testBasicUsageWithLongKey' => 'PhpArrayAdapter is read-only.', 'testClear' => 'PhpArrayAdapter is read-only.', 'testClearWithDeferredItems' => 'PhpArrayAdapter is read-only.', 'testDeleteItem' => 'PhpArrayAdapter is read-only.', diff --git a/src/Symfony/Component/Cache/Tests/Simple/PhpArrayCacheTest.php b/src/Symfony/Component/Cache/Tests/Simple/PhpArrayCacheTest.php index 3016ac560ed06..57361905f8869 100644 --- a/src/Symfony/Component/Cache/Tests/Simple/PhpArrayCacheTest.php +++ b/src/Symfony/Component/Cache/Tests/Simple/PhpArrayCacheTest.php @@ -21,6 +21,8 @@ class PhpArrayCacheTest extends CacheTestCase { protected $skippedTests = array( + 'testBasicUsageWithLongKey' => 'PhpArrayCache does no writes', + 'testDelete' => 'PhpArrayCache does no writes', 'testDeleteMultiple' => 'PhpArrayCache does no writes', 'testDeleteMultipleGenerator' => 'PhpArrayCache does no writes', @@ -57,6 +59,7 @@ protected function tearDown() FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache'); } } + public function createSimpleCache() { return new PhpArrayCacheWrapper(self::$file, new NullCache()); diff --git a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php index c2832946f98c2..8a836bac18c45 100644 --- a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php +++ b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php @@ -30,6 +30,7 @@ trait MemcachedTrait ); private $client; + private $lazyClient; public static function isSupported() { @@ -41,14 +42,18 @@ private function init(\Memcached $client, $namespace, $defaultLifetime) if (!static::isSupported()) { throw new CacheException('Memcached >= 2.2.0 is required'); } - $opt = $client->getOption(\Memcached::OPT_SERIALIZER); - if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) { - throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".'); + if (get_class($client) === 'Memcached') { + $opt = $client->getOption(\Memcached::OPT_SERIALIZER); + if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) { + throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".'); + } + $this->maxIdLength -= strlen($client->getOption(\Memcached::OPT_PREFIX_KEY)); + $this->client = $client; + } else { + $this->lazyClient = $client; } - $this->maxIdLength -= strlen($client->getOption(\Memcached::OPT_PREFIX_KEY)); parent::__construct($namespace, $defaultLifetime); - $this->client = $client; } /** @@ -187,7 +192,11 @@ public static function createConnection($servers, array $options = array()) */ protected function doSave(array $values, $lifetime) { - return $this->checkResultCode($this->client->setMulti($values, $lifetime)); + if ($lifetime && $lifetime > 30 * 86400) { + $lifetime += time(); + } + + return $this->checkResultCode($this->getClient()->setMulti($values, $lifetime)); } /** @@ -197,7 +206,7 @@ protected function doFetch(array $ids) { $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback'); try { - return $this->checkResultCode($this->client->getMulti($ids)); + return $this->checkResultCode($this->getClient()->getMulti($ids)); } catch (\Error $e) { throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()); } finally { @@ -210,7 +219,7 @@ protected function doFetch(array $ids) */ protected function doHave($id) { - return false !== $this->client->get($id) || $this->checkResultCode(\Memcached::RES_SUCCESS === $this->client->getResultCode()); + return false !== $this->getClient()->get($id) || $this->checkResultCode(\Memcached::RES_SUCCESS === $this->client->getResultCode()); } /** @@ -219,7 +228,7 @@ protected function doHave($id) protected function doDelete(array $ids) { $ok = true; - foreach ($this->checkResultCode($this->client->deleteMulti($ids)) as $result) { + foreach ($this->checkResultCode($this->getClient()->deleteMulti($ids)) as $result) { if (\Memcached::RES_SUCCESS !== $result && \Memcached::RES_NOTFOUND !== $result) { $ok = false; } @@ -233,7 +242,7 @@ protected function doDelete(array $ids) */ protected function doClear($namespace) { - return $this->checkResultCode($this->client->flush()); + return $this->checkResultCode($this->getClient()->flush()); } private function checkResultCode($result) @@ -246,4 +255,24 @@ private function checkResultCode($result) throw new CacheException(sprintf('MemcachedAdapter client error: %s.', strtolower($this->client->getResultMessage()))); } + + /** + * @return \Memcached + */ + private function getClient() + { + if ($this->client) { + return $this->client; + } + + $opt = $this->lazyClient->getOption(\Memcached::OPT_SERIALIZER); + if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) { + throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".'); + } + if ('' !== $prefix = (string) $this->lazyClient->getOption(\Memcached::OPT_PREFIX_KEY)) { + throw new CacheException(sprintf('MemcachedAdapter: "prefix_key" option must be empty when using proxified connections, "%s" given.', $prefix)); + } + + return $this->client = $this->lazyClient; + } } diff --git a/src/Symfony/Component/Cache/Traits/PhpArrayTrait.php b/src/Symfony/Component/Cache/Traits/PhpArrayTrait.php index 2d2c7db3d014f..ccc48886d0cc3 100644 --- a/src/Symfony/Component/Cache/Traits/PhpArrayTrait.php +++ b/src/Symfony/Component/Cache/Traits/PhpArrayTrait.php @@ -25,6 +25,7 @@ trait PhpArrayTrait private $file; private $values; private $fallbackPool; + private $zendDetectUnicode; /** * Store an array of cached values. @@ -106,7 +107,7 @@ public function warmUp(array $values) @rename($tmpFile, $this->file); - $this->values = (include $this->file) ?: array(); + $this->initialize(); } /** @@ -126,6 +127,15 @@ public function clear() */ private function initialize() { - $this->values = file_exists($this->file) ? (include $this->file ?: array()) : array(); + if ($this->zendDetectUnicode) { + $zmb = ini_set('zend.detect_unicode', 0); + } + try { + $this->values = file_exists($this->file) ? (include $this->file ?: array()) : array(); + } finally { + if ($this->zendDetectUnicode) { + ini_set('zend.detect_unicode', $zmb); + } + } } } diff --git a/src/Symfony/Component/Cache/Traits/PhpFilesTrait.php b/src/Symfony/Component/Cache/Traits/PhpFilesTrait.php index 259caf0c74168..d40a947badd21 100644 --- a/src/Symfony/Component/Cache/Traits/PhpFilesTrait.php +++ b/src/Symfony/Component/Cache/Traits/PhpFilesTrait.php @@ -25,6 +25,7 @@ trait PhpFilesTrait use FilesystemCommonTrait; private $includeHandler; + private $zendDetectUnicode; public static function isSupported() { @@ -39,6 +40,9 @@ protected function doFetch(array $ids) $values = array(); $now = time(); + if ($this->zendDetectUnicode) { + $zmb = ini_set('zend.detect_unicode', 0); + } set_error_handler($this->includeHandler); try { foreach ($ids as $id) { @@ -54,6 +58,9 @@ protected function doFetch(array $ids) } } finally { restore_error_handler(); + if ($this->zendDetectUnicode) { + ini_set('zend.detect_unicode', $zmb); + } } foreach ($values as $id => $value) { diff --git a/src/Symfony/Component/Cache/composer.json b/src/Symfony/Component/Cache/composer.json index 5c0df4b2a598a..f32117b038479 100644 --- a/src/Symfony/Component/Cache/composer.json +++ b/src/Symfony/Component/Cache/composer.json @@ -20,7 +20,7 @@ "psr/simple-cache-implementation": "1.0" }, "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "psr/cache": "~1.0", "psr/log": "~1.0", "psr/simple-cache": "^1.0" diff --git a/src/Symfony/Component/ClassLoader/composer.json b/src/Symfony/Component/ClassLoader/composer.json index a1dfc3fa0e096..d1b10ef649f46 100644 --- a/src/Symfony/Component/ClassLoader/composer.json +++ b/src/Symfony/Component/ClassLoader/composer.json @@ -17,7 +17,7 @@ ], "minimum-stability": "dev", "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "require-dev": { "symfony/finder": "~2.8|~3.0", diff --git a/src/Symfony/Component/Config/Resource/ClassExistenceResource.php b/src/Symfony/Component/Config/Resource/ClassExistenceResource.php index 623124ab8d988..e3fd095b6008d 100644 --- a/src/Symfony/Component/Config/Resource/ClassExistenceResource.php +++ b/src/Symfony/Component/Config/Resource/ClassExistenceResource.php @@ -65,7 +65,7 @@ public function isFresh($timestamp) { $loaded = class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false); - if (null !== $exists = &self::$existsCache[$this->resource]) { + if (null !== $exists = &self::$existsCache[(int) (0 >= $timestamp)][$this->resource]) { $exists = $exists || $loaded; } elseif (!$exists = $loaded) { if (!self::$autoloadLevel++) { @@ -76,6 +76,11 @@ public function isFresh($timestamp) try { $exists = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false); + } catch (\ReflectionException $e) { + if (0 >= $timestamp) { + unset(self::$existsCache[1][$this->resource]); + throw $e; + } } finally { self::$autoloadedClass = $autoloadedClass; if (!--self::$autoloadLevel) { diff --git a/src/Symfony/Component/Config/Tests/Fixtures/BadParent.php b/src/Symfony/Component/Config/Tests/Fixtures/BadParent.php new file mode 100644 index 0000000000000..68d7296ed8696 --- /dev/null +++ b/src/Symfony/Component/Config/Tests/Fixtures/BadParent.php @@ -0,0 +1,7 @@ +assertTrue($res->isFresh(time())); + } + + /** + * @expectedException \ReflectionException + * @expectedExceptionMessage Class Symfony\Component\Config\Tests\Fixtures\MissingParent not found + */ + public function testBadParentWithNoTimestamp() + { + $res = new ClassExistenceResource(BadParent::class, false); + $res->isFresh(0); + } + public function testConditionalClass() { $res = new ClassExistenceResource(ConditionalClass::class, false); diff --git a/src/Symfony/Component/Config/composer.json b/src/Symfony/Component/Config/composer.json index 7a51c91ed591a..f2bff19662b41 100644 --- a/src/Symfony/Component/Config/composer.json +++ b/src/Symfony/Component/Config/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/filesystem": "~2.8|~3.0" }, "require-dev": { diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index 8fa8bf5e1197c..66b42ffcd8160 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -72,6 +72,7 @@ class Application private $terminal; private $defaultCommand; private $singleCommand; + private $initialized; /** * @param string $name The name of the application @@ -83,12 +84,6 @@ public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') $this->version = $version; $this->terminal = new Terminal(); $this->defaultCommand = 'list'; - $this->helperSet = $this->getDefaultHelperSet(); - $this->definition = $this->getDefaultInputDefinition(); - - foreach ($this->getDefaultCommands() as $command) { - $this->add($command); - } } public function setDispatcher(EventDispatcherInterface $dispatcher) @@ -195,10 +190,11 @@ public function doRun(InputInterface $input, OutputInterface $output) if (!$name) { $name = $this->defaultCommand; - $this->definition->setArguments(array_merge( - $this->definition->getArguments(), + $definition = $this->getDefinition(); + $definition->setArguments(array_merge( + $definition->getArguments(), array( - 'command' => new InputArgument('command', InputArgument::OPTIONAL, $this->definition->getArgument('command')->getDescription(), $name), + 'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name), ) )); } @@ -248,6 +244,10 @@ public function setHelperSet(HelperSet $helperSet) */ public function getHelperSet() { + if (!$this->helperSet) { + $this->helperSet = $this->getDefaultHelperSet(); + } + return $this->helperSet; } @@ -268,6 +268,10 @@ public function setDefinition(InputDefinition $definition) */ public function getDefinition() { + if (!$this->definition) { + $this->definition = $this->getDefaultInputDefinition(); + } + if ($this->singleCommand) { $inputDefinition = $this->definition; $inputDefinition->setArguments(); @@ -424,6 +428,8 @@ public function addCommands(array $commands) */ public function add(Command $command) { + $this->init(); + $command->setApplication($this); if (!$command->isEnabled()) { @@ -456,6 +462,8 @@ public function add(Command $command) */ public function get($name) { + $this->init(); + if (!isset($this->commands[$name])) { throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name)); } @@ -483,6 +491,8 @@ public function get($name) */ public function has($name) { + $this->init(); + return isset($this->commands[$name]); } @@ -560,6 +570,8 @@ public function findNamespace($namespace) */ public function find($name) { + $this->init(); + $allCommands = array_keys($this->commands); $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name); $commands = preg_grep('{^'.$expr.'}', $allCommands); @@ -626,6 +638,8 @@ public function find($name) */ public function all($namespace = null) { + $this->init(); + if (null === $namespace) { return $this->commands; } @@ -1139,4 +1153,16 @@ private function extractAllNamespaces($name) return $namespaces; } + + private function init() + { + if ($this->initialized) { + return; + } + $this->initialized = true; + + foreach ($this->getDefaultCommands() as $command) { + $this->add($command); + } + } } diff --git a/src/Symfony/Component/Console/EventListener/ErrorListener.php b/src/Symfony/Component/Console/EventListener/ErrorListener.php index 8e35d97dfd489..3774f9e6666d4 100644 --- a/src/Symfony/Component/Console/EventListener/ErrorListener.php +++ b/src/Symfony/Component/Console/EventListener/ErrorListener.php @@ -59,10 +59,10 @@ public function onConsoleTerminate(ConsoleTerminateEvent $event) } if (!$inputString = $this->getInputString($event)) { - return $this->logger->error('The console exited with code "{code}"', array('code' => $exitCode)); + return $this->logger->debug('The console exited with code "{code}"', array('code' => $exitCode)); } - $this->logger->error('Command "{command}" exited with code "{code}"', array('command' => $inputString, 'code' => $exitCode)); + $this->logger->debug('Command "{command}" exited with code "{code}"', array('command' => $inputString, 'code' => $exitCode)); } public static function getSubscribedEvents() diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 6c06025697b2a..f12b1b4e1882c 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -1040,6 +1040,9 @@ public function testRunDispatchesAllEventsWithExceptionInListener() $this->assertContains('before.error.after.', $tester->getDisplay()); } + /** + * @requires PHP 7 + */ public function testRunWithError() { $application = new Application(); @@ -1160,6 +1163,7 @@ public function testErrorIsRethrownIfNotHandledByConsoleErrorEvent() } /** + * @requires PHP 7 * @expectedException \LogicException * @expectedExceptionMessage error */ @@ -1181,6 +1185,9 @@ public function testRunWithErrorAndDispatcher() $this->assertContains('before.dym.error.after.', $tester->getDisplay(), 'The PHP Error did not dispached events'); } + /** + * @requires PHP 7 + */ public function testRunDispatchesAllEventsWithError() { $application = new Application(); @@ -1198,6 +1205,9 @@ public function testRunDispatchesAllEventsWithError() $this->assertContains('before.dym.error.after.', $tester->getDisplay(), 'The PHP Error did not dispached events'); } + /** + * @requires PHP 7 + */ public function testRunWithErrorFailingStatusCode() { $application = new Application(); diff --git a/src/Symfony/Component/Console/Tests/DependencyInjection/AddConsoleCommandPassTest.php b/src/Symfony/Component/Console/Tests/DependencyInjection/AddConsoleCommandPassTest.php index 0cf4631754522..3423c5d9e92e9 100644 --- a/src/Symfony/Component/Console/Tests/DependencyInjection/AddConsoleCommandPassTest.php +++ b/src/Symfony/Component/Console/Tests/DependencyInjection/AddConsoleCommandPassTest.php @@ -16,7 +16,6 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\HttpKernel\Bundle\Bundle; class AddConsoleCommandPassTest extends TestCase { @@ -122,7 +121,3 @@ public function testProcessPrivateServicesWithSameCommand() class MyCommand extends Command { } - -class ExtensionPresentBundle extends Bundle -{ -} diff --git a/src/Symfony/Component/Console/Tests/EventListener/ErrorListenerTest.php b/src/Symfony/Component/Console/Tests/EventListener/ErrorListenerTest.php index c857a97d0b9bc..17eaae09084c8 100644 --- a/src/Symfony/Component/Console/Tests/EventListener/ErrorListenerTest.php +++ b/src/Symfony/Component/Console/Tests/EventListener/ErrorListenerTest.php @@ -61,7 +61,7 @@ public function testOnConsoleTerminateForNonZeroExitCodeWritesToLog() $logger = $this->getLogger(); $logger ->expects($this->once()) - ->method('error') + ->method('debug') ->with('Command "{command}" exited with code "{code}"', array('command' => 'test:run', 'code' => 255)) ; @@ -74,7 +74,7 @@ public function testOnConsoleTerminateForZeroExitCodeDoesNotWriteToLog() $logger = $this->getLogger(); $logger ->expects($this->never()) - ->method('error') + ->method('debug') ; $listener = new ErrorListener($logger); @@ -97,7 +97,7 @@ public function testAllKindsOfInputCanBeLogged() $logger = $this->getLogger(); $logger ->expects($this->exactly(3)) - ->method('error') + ->method('debug') ->with('Command "{command}" exited with code "{code}"', array('command' => 'test:run --foo=bar', 'code' => 255)) ; @@ -112,7 +112,7 @@ public function testCommandNameIsDisplayedForNonStringableInput() $logger = $this->getLogger(); $logger ->expects($this->once()) - ->method('error') + ->method('debug') ->with('Command "{command}" exited with code "{code}"', array('command' => 'test:run', 'code' => 255)) ; diff --git a/src/Symfony/Component/Console/composer.json b/src/Symfony/Component/Console/composer.json index db76bf3b2c7a7..415b9e096d2ac 100644 --- a/src/Symfony/Component/Console/composer.json +++ b/src/Symfony/Component/Console/composer.json @@ -16,13 +16,12 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-mbstring": "~1.0", "symfony/debug": "~2.8|~3.0" }, "require-dev": { "symfony/config": "~3.3", - "symfony/http-kernel": "~2.8|~3.0", "symfony/event-dispatcher": "~2.8|~3.0", "symfony/dependency-injection": "~3.3", "symfony/filesystem": "~2.8|~3.0", diff --git a/src/Symfony/Component/CssSelector/composer.json b/src/Symfony/Component/CssSelector/composer.json index e94cfbded0d33..9661f70ea317d 100644 --- a/src/Symfony/Component/CssSelector/composer.json +++ b/src/Symfony/Component/CssSelector/composer.json @@ -20,7 +20,7 @@ } ], "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "autoload": { "psr-4": { "Symfony\\Component\\CssSelector\\": "" }, diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php index 2e1d71808e132..3a8b7d247ea98 100644 --- a/src/Symfony/Component/Debug/DebugClassLoader.php +++ b/src/Symfony/Component/Debug/DebugClassLoader.php @@ -26,6 +26,7 @@ class DebugClassLoader { private $classLoader; private $isFinder; + private $loaded = array(); private static $caseCheck; private static $final = array(); private static $finalMethods = array(); @@ -139,9 +140,10 @@ public function loadClass($class) ErrorHandler::stackErrors(); try { - if ($this->isFinder) { + if ($this->isFinder && !isset($this->loaded[$class])) { + $this->loaded[$class] = true; if ($file = $this->classLoader[0]->findFile($class)) { - require_once $file; + require $file; } } else { call_user_func($this->classLoader, $class); diff --git a/src/Symfony/Component/Debug/ExceptionHandler.php b/src/Symfony/Component/Debug/ExceptionHandler.php index d84cfdd496d38..0ecd2a5347f27 100644 --- a/src/Symfony/Component/Debug/ExceptionHandler.php +++ b/src/Symfony/Component/Debug/ExceptionHandler.php @@ -320,11 +320,11 @@ public function getStylesheet(FlattenException $exception) .trace-message { font-size: 14px; font-weight: normal; margin: .5em 0 0; } - .trace-file-path, .trace-file-path a { margin-top: 3px; color: #999; color: #795da3; color: #B0413E; color: #222; font-size: 13px; } + .trace-file-path, .trace-file-path a { color: #222; margin-top: 3px; font-size: 13px; } .trace-class { color: #B0413E; } .trace-type { padding: 0 2px; } - .trace-method { color: #B0413E; color: #222; font-weight: bold; color: #B0413E; } - .trace-arguments { color: #222; color: #999; font-weight: normal; color: #795da3; color: #777; padding-left: 2px; } + .trace-method { color: #B0413E; font-weight: bold; } + .trace-arguments { color: #777; font-weight: normal; padding-left: 2px; } @media (min-width: 575px) { .hidden-xs-down { display: initial; } diff --git a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php index f1e3fb7c611b5..af88d17fd2f3b 100644 --- a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php +++ b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php @@ -59,6 +59,23 @@ public function testIdempotence() $this->fail('DebugClassLoader did not register'); } + /** + * @expectedException \Exception + * @expectedExceptionMessage boo + */ + public function testThrowingClass() + { + try { + class_exists(__NAMESPACE__.'\Fixtures\Throwing'); + $this->fail('Exception expected'); + } catch (\Exception $e) { + $this->assertSame('boo', $e->getMessage()); + } + + // the second call also should throw + class_exists(__NAMESPACE__.'\Fixtures\Throwing'); + } + public function testUnsilencing() { if (\PHP_VERSION_ID >= 70000) { @@ -124,6 +141,7 @@ class ChildTestingStacking extends TestingStacking { function foo($bar) {} } /** * @expectedException \RuntimeException + * @expectedExceptionMessage Case mismatch between loaded and declared class names */ public function testNameCaseMismatch() { @@ -145,6 +163,7 @@ class_exists(__NAMESPACE__.'\Fixtures\CaseMismatch', true); /** * @expectedException \RuntimeException + * @expectedExceptionMessage Case mismatch between loaded and declared class names */ public function testPsr4CaseMismatch() { diff --git a/src/Symfony/Component/Debug/Tests/Fixtures/Throwing.php b/src/Symfony/Component/Debug/Tests/Fixtures/Throwing.php new file mode 100644 index 0000000000000..21e0aba17d358 --- /dev/null +++ b/src/Symfony/Component/Debug/Tests/Fixtures/Throwing.php @@ -0,0 +1,3 @@ +=5.5.9", + "php": "^5.5.9|>=7.0.8", "psr/log": "~1.0" }, "conflict": { diff --git a/src/Symfony/Component/DependencyInjection/ChildDefinition.php b/src/Symfony/Component/DependencyInjection/ChildDefinition.php index 5701caa474e57..862be880badfb 100644 --- a/src/Symfony/Component/DependencyInjection/ChildDefinition.php +++ b/src/Symfony/Component/DependencyInjection/ChildDefinition.php @@ -33,7 +33,7 @@ public function __construct($parent) } /** - * Returns the Definition being decorated. + * Returns the Definition to inherit from. * * @return string */ @@ -43,7 +43,7 @@ public function getParent() } /** - * Sets the Definition being decorated. + * Sets the Definition to inherit from. * * @param string $parent * diff --git a/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php b/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php index 9434ac70b543b..3e4acf5664e76 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php @@ -13,7 +13,9 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface; +use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; +use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; /** * Merges extension configs into the container builder. @@ -43,9 +45,14 @@ public function process(ContainerBuilder $container) // this extension was not called continue; } - $config = $container->getParameterBag()->resolveValue($config); + $resolvingBag = $container->getParameterBag(); + if ($resolvingBag instanceof EnvPlaceholderParameterBag && $extension instanceof Extension) { + // create a dedicated bag so that we can track env vars per-extension + $resolvingBag = new MergeExtensionConfigurationParameterBag($resolvingBag); + } + $config = $resolvingBag->resolveValue($config); - $tmpContainer = new ContainerBuilder($container->getParameterBag()); + $tmpContainer = new ContainerBuilder($resolvingBag); $tmpContainer->setResourceTracking($container->isTrackingResources()); $tmpContainer->addObjectResource($extension); if ($extension instanceof ConfigurationExtensionInterface && null !== $configuration = $extension->getConfiguration($config, $tmpContainer)) { @@ -58,6 +65,11 @@ public function process(ContainerBuilder $container) $extension->load($config, $tmpContainer); + if ($resolvingBag instanceof MergeExtensionConfigurationParameterBag) { + // don't keep track of env vars that are *overridden* when configs are merged + $resolvingBag->freezeAfterProcessing($extension); + } + $container->merge($tmpContainer); $container->getParameterBag()->add($parameters); } @@ -66,3 +78,52 @@ public function process(ContainerBuilder $container) $container->addAliases($aliases); } } + +/** + * @internal + */ +class MergeExtensionConfigurationParameterBag extends EnvPlaceholderParameterBag +{ + private $processedEnvPlaceholders; + + public function __construct(parent $parameterBag) + { + parent::__construct($parameterBag->all()); + $this->mergeEnvPlaceholders($parameterBag); + } + + public function freezeAfterProcessing(Extension $extension) + { + $this->processedEnvPlaceholders = array(); + $this->processMergedConfig($extension->getProcessedConfigs(), parent::getEnvPlaceholders()); + } + + /** + * {@inheritdoc} + */ + public function getEnvPlaceholders() + { + return null !== $this->processedEnvPlaceholders ? $this->processedEnvPlaceholders : parent::getEnvPlaceholders(); + } + + private function processMergedConfig($value, array $envPlaceholders) + { + if (is_array($value)) { + foreach ($value as $k => $v) { + $this->processMergedConfig($k, $envPlaceholders); + $this->processMergedConfig($v, $envPlaceholders); + } + } elseif (is_string($value)) { + foreach ($envPlaceholders as $env => $placeholders) { + foreach ($placeholders as $placeholder) { + if (false !== stripos($value, $placeholder)) { + $this->processedEnvPlaceholders[$env] = $placeholders; + break; + } + } + } + } + + return $value; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveEnvPlaceholdersPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveEnvPlaceholdersPass.php new file mode 100644 index 0000000000000..8e44008317c27 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveEnvPlaceholdersPass.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; + +/** + * Replaces env var placeholders by their current values. + */ +class ResolveEnvPlaceholdersPass extends AbstractRecursivePass +{ + protected function processValue($value, $isRoot = false) + { + if (is_string($value)) { + return $this->container->resolveEnvPlaceholders($value, true); + } + if ($value instanceof Definition) { + $changes = $value->getChanges(); + if (isset($changes['class'])) { + $value->setClass($this->container->resolveEnvPlaceholders($value->getClass(), true)); + } + if (isset($changes['file'])) { + $value->setFile($this->container->resolveEnvPlaceholders($value->getFile(), true)); + } + } + + $value = parent::processValue($value, $isRoot); + + if ($value && is_array($value)) { + $value = array_combine($this->container->resolveEnvPlaceholders(array_keys($value), true), $value); + } + + return $value; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php index bf9f83bbe80d0..59c5e390223ca 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php @@ -54,20 +54,17 @@ protected function processValue($value, $isRoot = false) $value->setArguments($arguments); - if ($public = $value->isPublic()) { - $value->setPublic(false); - } $id = 'service_locator.'.md5(serialize($value)); if ($isRoot) { if ($id !== $this->currentId) { - $this->container->setAlias($id, new Alias($this->currentId, $public)); + $this->container->setAlias($id, new Alias($this->currentId, false)); } return $value; } - $this->container->setDefinition($id, $value); + $this->container->setDefinition($id, $value->setPublic(false)); return new Reference($id); } diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php index f435a99baec0f..6eceb0defeba9 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php +++ b/src/Symfony/Component/DependencyInjection/Container.php @@ -111,7 +111,7 @@ public function isCompiled() /** * Returns true if the container parameter bag are frozen. * - * Deprecated since 3.3, to be removed in 4.0. + * @deprecated since version 3.3, to be removed in 4.0. * * @return bool true if the container parameter bag are frozen, false otherwise */ @@ -427,9 +427,9 @@ public static function underscore($id) /** * Fetches a variable from the environment. * - * @param string The name of the environment variable + * @param string $name The name of the environment variable * - * @return scalar The value to use for the provided environment variable name + * @return mixed The value to use for the provided environment variable name * * @throws EnvNotFoundException When the environment variable is not found and has no default value */ @@ -438,10 +438,13 @@ protected function getEnv($name) if (isset($this->envCache[$name]) || array_key_exists($name, $this->envCache)) { return $this->envCache[$name]; } + if (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) { + return $this->envCache[$name] = $_SERVER[$name]; + } if (isset($_ENV[$name])) { return $this->envCache[$name] = $_ENV[$name]; } - if (false !== $env = getenv($name)) { + if (false !== ($env = getenv($name)) && null !== $env) { // null is a possible value because of thread safety issues return $this->envCache[$name] = $env; } if (!$this->hasParameter("env($name)")) { diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index c824cc959de9d..698080b9715e8 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -18,6 +18,7 @@ use Symfony\Component\DependencyInjection\Compiler\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\Compiler\ResolveEnvPlaceholdersPass; use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\LogicException; @@ -407,9 +408,13 @@ public function fileExists($path, $trackContents = true) return $exists; } - if ($trackContents && is_dir($path)) { - $this->addResource(new DirectoryResource($path, is_string($trackContents) ? $trackContents : null)); - } elseif ($trackContents || is_dir($path)) { + if (is_dir($path)) { + if ($trackContents) { + $this->addResource(new DirectoryResource($path, is_string($trackContents) ? $trackContents : null)); + } else { + $this->addResource(new GlobResource($path, '/*', false)); + } + } elseif ($trackContents) { $this->addResource(new FileResource($path)); } @@ -636,10 +641,16 @@ public function merge(ContainerBuilder $container) } if ($this->getParameterBag() instanceof EnvPlaceholderParameterBag && $container->getParameterBag() instanceof EnvPlaceholderParameterBag) { + $envPlaceholders = $container->getParameterBag()->getEnvPlaceholders(); $this->getParameterBag()->mergeEnvPlaceholders($container->getParameterBag()); + } else { + $envPlaceholders = array(); } foreach ($container->envCounters as $env => $count) { + if (!$count && !isset($envPlaceholders[$env])) { + continue; + } if (!isset($this->envCounters[$env])) { $this->envCounters[$env] = $count; } else { @@ -729,9 +740,7 @@ public function compile(/*$resolveEnvPlaceholders = false*/) $bag = $this->getParameterBag(); if ($resolveEnvPlaceholders && $bag instanceof EnvPlaceholderParameterBag) { - $this->parameterBag = new ParameterBag($this->resolveEnvPlaceholders($bag->all(), true)); - $this->envPlaceholders = $bag->getEnvPlaceholders(); - $this->parameterBag = $bag = new ParameterBag($this->resolveEnvPlaceholders($this->parameterBag->all())); + $compiler->addPass(new ResolveEnvPlaceholdersPass(), PassConfig::TYPE_AFTER_REMOVING, -1000); } $compiler->compile($this); @@ -744,9 +753,15 @@ public function compile(/*$resolveEnvPlaceholders = false*/) $this->extensionConfigs = array(); - parent::compile(); + if ($bag instanceof EnvPlaceholderParameterBag) { + if ($resolveEnvPlaceholders) { + $this->parameterBag = new ParameterBag($this->resolveEnvPlaceholders($bag->all(), true)); + } - $this->envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : array(); + $this->envPlaceholders = $bag->getEnvPlaceholders(); + } + + parent::compile(); } /** @@ -868,8 +883,8 @@ public function getAlias($id) * This methods allows for simple registration of service definition * with a fluid interface. * - * @param string $id The service identifier - * @param string $class The service class + * @param string $id The service identifier + * @param string $class|null The service class * * @return Definition A Definition instance */ @@ -1316,7 +1331,14 @@ public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs } else { $resolved = sprintf($format, $env); } - $value = str_ireplace($placeholder, $resolved, $value); + if ($placeholder === $value) { + $value = $resolved; + } else { + if (!is_string($resolved) && !is_numeric($resolved)) { + throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "env(%s)" of type %s inside string value "%s".', $env, gettype($resolved), $value)); + } + $value = str_ireplace($placeholder, $resolved, $value); + } $usedEnvs[$env] = $env; $this->envCounters[$env] = isset($this->envCounters[$env]) ? 1 + $this->envCounters[$env] : 1; } @@ -1391,6 +1413,28 @@ public static function getServiceConditionals($value) return $services; } + /** + * {@inheritdoc} + */ + protected function getEnv($name) + { + $value = parent::getEnv($name); + + if (!is_string($value) || !$this->getParameterBag() instanceof EnvPlaceholderParameterBag) { + return $value; + } + + foreach ($this->getParameterBag()->getEnvPlaceholders() as $env => $placeholders) { + if (isset($placeholders[$value])) { + $bag = new ParameterBag($this->getParameterBag()->all()); + + return $bag->unescapeValue($bag->get("env($name)")); + } + } + + return $value; + } + /** * Retrieves the currently set proxy instantiator or instantiates one. * diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index 68b3368da2ecc..2900ddc24d269 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -71,6 +71,8 @@ public function getChanges() /** * Sets the tracked changes for the Definition object. * + * @param array $changes An array of changes for this Definition + * * @return $this */ public function setChanges(array $changes) @@ -188,6 +190,13 @@ public function setArguments(array $arguments) return $this; } + /** + * Sets the properties to define when creating the service. + * + * @param array $properties + * + * @return $this + */ public function setProperties(array $properties) { $this->properties = $properties; @@ -195,11 +204,24 @@ public function setProperties(array $properties) return $this; } + /** + * Gets the properties to define when creating the service. + * + * @return array + */ public function getProperties() { return $this->properties; } + /** + * Sets a specific property. + * + * @param string $name + * @param mixed $value + * + * @return $this + */ public function setProperty($name, $value) { $this->properties[$name] = $value; @@ -222,7 +244,7 @@ public function addArgument($argument) } /** - * Sets a specific argument. + * Replaces a specific argument. * * @param int|string $index * @param mixed $argument @@ -250,6 +272,14 @@ public function replaceArgument($index, $argument) return $this; } + /** + * Sets a specific argument. + * + * @param int|string $key + * @param mixed $value + * + * @return $this + */ public function setArgument($key, $value) { $this->arguments[$key] = $value; @@ -373,6 +403,8 @@ public function getMethodCalls() * Sets the definition templates to conditionally apply on the current definition, keyed by parent interface/class. * * @param $instanceof ChildDefinition[] + * + * @return $this */ public function setInstanceofConditionals(array $instanceof) { @@ -778,7 +810,7 @@ public function isAutowired() } /** - * Sets autowired. + * Enables/disables autowiring. * * @param bool $autowired * diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index e71df2af24c14..9567fee56a583 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -116,6 +116,14 @@ private function addService($id, $definition) $code .= sprintf(" autowiring_types:\n%s", $autowiringTypesCode); } + if ($definition->isAutoconfigured()) { + $code .= " autoconfigure: true\n"; + } + + if ($definition->isAbstract()) { + $code .= " abstract: true\n"; + } + if ($definition->isLazy()) { $code .= " lazy: true\n"; } diff --git a/src/Symfony/Component/DependencyInjection/Extension/Extension.php b/src/Symfony/Component/DependencyInjection/Extension/Extension.php index 117ee58c111f4..d3d382ae449de 100644 --- a/src/Symfony/Component/DependencyInjection/Extension/Extension.php +++ b/src/Symfony/Component/DependencyInjection/Extension/Extension.php @@ -25,6 +25,8 @@ */ abstract class Extension implements ExtensionInterface, ConfigurationExtensionInterface { + private $processedConfigs = array(); + /** * {@inheritdoc} */ @@ -91,7 +93,19 @@ final protected function processConfiguration(ConfigurationInterface $configurat { $processor = new Processor(); - return $processor->processConfiguration($configuration, $configs); + return $this->processedConfigs[] = $processor->processConfiguration($configuration, $configs); + } + + /** + * @internal + */ + final public function getProcessedConfigs() + { + try { + return $this->processedConfigs; + } finally { + $this->processedConfigs = array(); + } } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/MergeExtensionConfigurationPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/MergeExtensionConfigurationPassTest.php index b35521d206204..033e6c00fc342 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/MergeExtensionConfigurationPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/MergeExtensionConfigurationPassTest.php @@ -17,6 +17,7 @@ use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; class MergeExtensionConfigurationPassTest extends TestCase @@ -55,13 +56,10 @@ public function testExpressionLanguageProviderForwarding() public function testExtensionConfigurationIsTrackedByDefault() { - $extension = $this->getMockBuilder('Symfony\\Component\\DependencyInjection\\Extension\\Extension')->getMock(); - $extension->expects($this->once()) + $extension = $this->getMockBuilder(FooExtension::class)->setMethods(array('getConfiguration'))->getMock(); + $extension->expects($this->exactly(2)) ->method('getConfiguration') ->will($this->returnValue(new FooConfiguration())); - $extension->expects($this->any()) - ->method('getAlias') - ->will($this->returnValue('foo')); $container = new ContainerBuilder(new ParameterBag()); $container->registerExtension($extension); @@ -72,12 +70,58 @@ public function testExtensionConfigurationIsTrackedByDefault() $this->assertContains(new FileResource(__FILE__), $container->getResources(), '', false, false); } + + public function testOverriddenEnvsAreMerged() + { + $container = new ContainerBuilder(); + $container->registerExtension(new FooExtension()); + $container->prependExtensionConfig('foo', array('bar' => '%env(FOO)%')); + $container->prependExtensionConfig('foo', array('bar' => '%env(BAR)%', 'baz' => '%env(BAZ)%')); + + $pass = new MergeExtensionConfigurationPass(); + $pass->process($container); + + $this->assertSame(array('FOO', 'BAZ'), array_keys($container->getParameterBag()->getEnvPlaceholders())); + $this->assertSame(array('BAZ' => 1, 'FOO' => 0), $container->getEnvCounters()); + } } class FooConfiguration implements ConfigurationInterface { public function getConfigTreeBuilder() { - return new TreeBuilder(); + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('foo'); + $rootNode + ->children() + ->scalarNode('bar')->end() + ->scalarNode('baz')->end() + ->end(); + + return $treeBuilder; + } +} + +class FooExtension extends Extension +{ + public function getAlias() + { + return 'foo'; + } + + public function getConfiguration(array $config, ContainerBuilder $container) + { + return new FooConfiguration(); + } + + public function load(array $configs, ContainerBuilder $container) + { + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + + if (isset($config['baz'])) { + $container->getParameterBag()->get('env(BOZ)'); + $container->resolveEnvPlaceholders($config['baz']); + } } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 51397309b7dce..efe9840e42c40 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -603,29 +603,55 @@ public function testMergeThrowsExceptionForDuplicateAutomaticInstanceofDefinitio public function testResolveEnvValues() { $_ENV['DUMMY_ENV_VAR'] = 'du%%y'; + $_SERVER['DUMMY_SERVER_VAR'] = 'ABC'; + $_SERVER['HTTP_DUMMY_VAR'] = 'DEF'; $container = new ContainerBuilder(); - $container->setParameter('bar', '%% %env(DUMMY_ENV_VAR)%'); + $container->setParameter('bar', '%% %env(DUMMY_ENV_VAR)% %env(DUMMY_SERVER_VAR)% %env(HTTP_DUMMY_VAR)%'); + $container->setParameter('env(HTTP_DUMMY_VAR)', '123'); - $this->assertSame('%% du%%%%y', $container->resolveEnvPlaceholders('%bar%', true)); + $this->assertSame('%% du%%%%y ABC 123', $container->resolveEnvPlaceholders('%bar%', true)); - unset($_ENV['DUMMY_ENV_VAR']); + unset($_ENV['DUMMY_ENV_VAR'], $_SERVER['DUMMY_SERVER_VAR'], $_SERVER['HTTP_DUMMY_VAR']); } public function testCompileWithResolveEnv() { - $_ENV['DUMMY_ENV_VAR'] = 'du%%y'; + putenv('DUMMY_ENV_VAR=du%%y'); + $_SERVER['DUMMY_SERVER_VAR'] = 'ABC'; + $_SERVER['HTTP_DUMMY_VAR'] = 'DEF'; $container = new ContainerBuilder(); $container->setParameter('env(FOO)', 'Foo'); - $container->setParameter('bar', '%% %env(DUMMY_ENV_VAR)%'); + $container->setParameter('env(DUMMY_ENV_VAR)', 'GHI'); + $container->setParameter('bar', '%% %env(DUMMY_ENV_VAR)% %env(DUMMY_SERVER_VAR)% %env(HTTP_DUMMY_VAR)%'); $container->setParameter('foo', '%env(FOO)%'); + $container->setParameter('baz', '%foo%'); + $container->setParameter('env(HTTP_DUMMY_VAR)', '123'); + $container->register('teatime', 'stdClass') + ->setProperty('foo', '%env(DUMMY_ENV_VAR)%') + ; $container->compile(true); - $this->assertSame('% du%%y', $container->getParameter('bar')); - $this->assertSame('Foo', $container->getParameter('foo')); + $this->assertSame('% du%%y ABC 123', $container->getParameter('bar')); + $this->assertSame('Foo', $container->getParameter('baz')); + $this->assertSame('du%%y', $container->get('teatime')->foo); + + unset($_SERVER['DUMMY_SERVER_VAR'], $_SERVER['HTTP_DUMMY_VAR']); + putenv('DUMMY_ENV_VAR'); + } - unset($_ENV['DUMMY_ENV_VAR']); + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage A string value must be composed of strings and/or numbers, but found parameter "env(ARRAY)" of type array inside string value "ABC %env(ARRAY)%". + */ + public function testCompileWithArrayResolveEnv() + { + $bag = new TestingEnvPlaceholderParameterBag(); + $container = new ContainerBuilder($bag); + $container->setParameter('foo', '%env(ARRAY)%'); + $container->setParameter('bar', 'ABC %env(ARRAY)%'); + $container->compile(true); } /** @@ -1119,3 +1145,11 @@ public function __construct(A $a) { } } + +class TestingEnvPlaceholderParameterBag extends EnvPlaceholderParameterBag +{ + public function get($name) + { + return 'env(array)' === strtolower($name) ? array(123) : parent::get($name); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php index 22277c7b7a85f..eaaad0c75ca58 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php @@ -12,8 +12,10 @@ namespace Symfony\Component\DependencyInjection\Tests\Dumper; use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Dumper\YamlDumper; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Parser; @@ -64,6 +66,16 @@ public function testDumpAutowireData() $this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services24.yml', $dumper->dump()); } + public function testDumpLoad() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_dump_load.yml'); + + $dumper = new YamlDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services_dump_load.yml', $dumper->dump()); + } + private function assertEqualYamlStructure($expected, $yaml, $message = '') { $parser = new Parser(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_dump_load.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_dump_load.yml new file mode 100644 index 0000000000000..43b0c7d58a00f --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_dump_load.yml @@ -0,0 +1,14 @@ + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + synthetic: true + foo: + autoconfigure: true + abstract: true + Psr\Container\ContainerInterface: + alias: service_container + public: false + Symfony\Component\DependencyInjection\ContainerInterface: + alias: service_container + public: false diff --git a/src/Symfony/Component/DependencyInjection/composer.json b/src/Symfony/Component/DependencyInjection/composer.json index 8c88d4f02a790..0ce0ffeeaf57b 100644 --- a/src/Symfony/Component/DependencyInjection/composer.json +++ b/src/Symfony/Component/DependencyInjection/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "psr/container": "^1.0" }, "require-dev": { diff --git a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php index 39b4a7aefbd5e..f7b0857309ebe 100644 --- a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php @@ -397,7 +397,7 @@ public function testText() public function testHtml() { $this->assertEquals('Bar', $this->createTestCrawler()->filterXPath('//a[5]')->html()); - $this->assertEquals('', trim($this->createTestCrawler()->filterXPath('//form[@id="FooFormId"]')->html())); + $this->assertEquals('', trim(preg_replace('~>\s+<~', '><', $this->createTestCrawler()->filterXPath('//form[@id="FooFormId"]')->html()))); try { $this->createTestCrawler()->filterXPath('//ol')->html(); diff --git a/src/Symfony/Component/DomCrawler/composer.json b/src/Symfony/Component/DomCrawler/composer.json index 84df31fdeae2f..8835234c1171c 100644 --- a/src/Symfony/Component/DomCrawler/composer.json +++ b/src/Symfony/Component/DomCrawler/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { diff --git a/src/Symfony/Component/Dotenv/Dotenv.php b/src/Symfony/Component/Dotenv/Dotenv.php index 2e907b8c980b8..910a1622f303b 100644 --- a/src/Symfony/Component/Dotenv/Dotenv.php +++ b/src/Symfony/Component/Dotenv/Dotenv.php @@ -60,20 +60,36 @@ public function load($path/*, ...$paths*/) /** * Sets values as environment variables (via putenv, $_ENV, and $_SERVER). * - * Note that existing environment variables are never overridden. + * Note that existing environment variables are not overridden. * * @param array $values An array of env variables */ public function populate($values) { + $loadedVars = array_flip(explode(',', getenv('SYMFONY_DOTENV_VARS'))); + unset($loadedVars['']); + foreach ($values as $name => $value) { - if (isset($_ENV[$name]) || isset($_SERVER[$name]) || false !== getenv($name)) { + $notHttpName = 0 !== strpos($name, 'HTTP_'); + // don't check existence with getenv() because of thread safety issues + if (!isset($loadedVars[$name]) && (isset($_ENV[$name]) || (isset($_SERVER[$name]) && $notHttpName))) { continue; } putenv("$name=$value"); $_ENV[$name] = $value; - $_SERVER[$name] = $value; + if ($notHttpName) { + $_SERVER[$name] = $value; + } + + $loadedVars[$name] = true; + } + + if ($loadedVars) { + $loadedVars = implode(',', array_keys($loadedVars)); + putenv("SYMFONY_DOTENV_VARS=$loadedVars"); + $_ENV['SYMFONY_DOTENV_VARS'] = $loadedVars; + $_SERVER['SYMFONY_DOTENV_VARS'] = $loadedVars; } } @@ -351,7 +367,15 @@ private function resolveVariables($value) } $name = $matches[3]; - $value = isset($this->values[$name]) ? $this->values[$name] : (isset($_ENV[$name]) ? $_ENV[$name] : (string) getenv($name)); + if (isset($this->values[$name])) { + $value = $this->values[$name]; + } elseif (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) { + $value = $_SERVER[$name]; + } elseif (isset($_ENV[$name])) { + $value = $_ENV[$name]; + } else { + $value = (string) getenv($name); + } if (!$matches[2] && isset($matches[4])) { $value .= '}'; diff --git a/src/Symfony/Component/Dotenv/Tests/DotenvTest.php b/src/Symfony/Component/Dotenv/Tests/DotenvTest.php index 47598030a87f1..ce7d3a93396b2 100644 --- a/src/Symfony/Component/Dotenv/Tests/DotenvTest.php +++ b/src/Symfony/Component/Dotenv/Tests/DotenvTest.php @@ -208,10 +208,79 @@ public function testServerSuperglobalIsNotOverriden() public function testEnvVarIsNotOverriden() { putenv('TEST_ENV_VAR=original_value'); + $_SERVER['TEST_ENV_VAR'] = 'original_value'; $dotenv = new DotEnv(); $dotenv->populate(array('TEST_ENV_VAR' => 'new_value')); $this->assertSame('original_value', getenv('TEST_ENV_VAR')); } + + public function testHttpVarIsPartiallyOverriden() + { + $_SERVER['HTTP_TEST_ENV_VAR'] = 'http_value'; + + $dotenv = new DotEnv(); + $dotenv->populate(array('HTTP_TEST_ENV_VAR' => 'env_value')); + + $this->assertSame('env_value', getenv('HTTP_TEST_ENV_VAR')); + $this->assertSame('env_value', $_ENV['HTTP_TEST_ENV_VAR']); + $this->assertSame('http_value', $_SERVER['HTTP_TEST_ENV_VAR']); + } + + public function testMemorizingLoadedVarsNamesInSpecialVar() + { + // Special variable not exists + unset($_ENV['SYMFONY_DOTENV_VARS']); + unset($_SERVER['SYMFONY_DOTENV_VARS']); + putenv('SYMFONY_DOTENV_VARS'); + + unset($_ENV['APP_DEBUG']); + unset($_SERVER['APP_DEBUG']); + putenv('APP_DEBUG'); + unset($_ENV['DATABASE_URL']); + unset($_SERVER['DATABASE_URL']); + putenv('DATABASE_URL'); + + $dotenv = new DotEnv(); + $dotenv->populate(array('APP_DEBUG' => '1', 'DATABASE_URL' => 'mysql://root@localhost/db')); + + $this->assertSame('APP_DEBUG,DATABASE_URL', getenv('SYMFONY_DOTENV_VARS')); + + // Special variable has a value + $_ENV['SYMFONY_DOTENV_VARS'] = 'APP_ENV'; + $_SERVER['SYMFONY_DOTENV_VARS'] = 'APP_ENV'; + putenv('SYMFONY_DOTENV_VARS=APP_ENV'); + + $_ENV['APP_DEBUG'] = '1'; + $_SERVER['APP_DEBUG'] = '1'; + putenv('APP_DEBUG=1'); + unset($_ENV['DATABASE_URL']); + unset($_SERVER['DATABASE_URL']); + putenv('DATABASE_URL'); + + $dotenv = new DotEnv(); + $dotenv->populate(array('APP_DEBUG' => '0', 'DATABASE_URL' => 'mysql://root@localhost/db')); + $dotenv->populate(array('DATABASE_URL' => 'sqlite:///somedb.sqlite')); + + $this->assertSame('APP_ENV,DATABASE_URL', getenv('SYMFONY_DOTENV_VARS')); + } + + public function testOverridingEnvVarsWithNamesMemorizedInSpecialVar() + { + putenv('SYMFONY_DOTENV_VARS=FOO,BAR,BAZ'); + + putenv('FOO=foo'); + putenv('BAR=bar'); + putenv('BAZ=baz'); + putenv('DOCUMENT_ROOT=/var/www'); + + $dotenv = new DotEnv(); + $dotenv->populate(array('FOO' => 'foo1', 'BAR' => 'bar1', 'BAZ' => 'baz1', 'DOCUMENT_ROOT' => '/boot')); + + $this->assertSame('foo1', getenv('FOO')); + $this->assertSame('bar1', getenv('BAR')); + $this->assertSame('baz1', getenv('BAZ')); + $this->assertSame('/var/www', getenv('DOCUMENT_ROOT')); + } } diff --git a/src/Symfony/Component/Dotenv/composer.json b/src/Symfony/Component/Dotenv/composer.json index 020342bdd4a96..0e6eda933f3fc 100644 --- a/src/Symfony/Component/Dotenv/composer.json +++ b/src/Symfony/Component/Dotenv/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "require-dev": { "symfony/process": "^3.2" diff --git a/src/Symfony/Component/EventDispatcher/composer.json b/src/Symfony/Component/EventDispatcher/composer.json index faa0429e2d1a0..994e8ca64ad3c 100644 --- a/src/Symfony/Component/EventDispatcher/composer.json +++ b/src/Symfony/Component/EventDispatcher/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "require-dev": { "symfony/dependency-injection": "~3.3", diff --git a/src/Symfony/Component/ExpressionLanguage/composer.json b/src/Symfony/Component/ExpressionLanguage/composer.json index d9d95f00cf4e4..cd8933d6be356 100644 --- a/src/Symfony/Component/ExpressionLanguage/composer.json +++ b/src/Symfony/Component/ExpressionLanguage/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/cache": "~3.1" }, "autoload": { diff --git a/src/Symfony/Component/Filesystem/composer.json b/src/Symfony/Component/Filesystem/composer.json index 715a34f7d86bc..122b6cea016d8 100644 --- a/src/Symfony/Component/Filesystem/composer.json +++ b/src/Symfony/Component/Filesystem/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "autoload": { "psr-4": { "Symfony\\Component\\Filesystem\\": "" }, diff --git a/src/Symfony/Component/Finder/composer.json b/src/Symfony/Component/Finder/composer.json index b57dd8bcda24e..8eed8f53e5be5 100644 --- a/src/Symfony/Component/Finder/composer.json +++ b/src/Symfony/Component/Finder/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "autoload": { "psr-4": { "Symfony\\Component\\Finder\\": "" }, diff --git a/src/Symfony/Component/Form/DependencyInjection/FormPass.php b/src/Symfony/Component/Form/DependencyInjection/FormPass.php index 55e4cc1239b2f..88574558b7b7a 100644 --- a/src/Symfony/Component/Form/DependencyInjection/FormPass.php +++ b/src/Symfony/Component/Form/DependencyInjection/FormPass.php @@ -16,7 +16,6 @@ use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Reference; @@ -53,12 +52,12 @@ public function process(ContainerBuilder $container) if (new IteratorArgument(array()) != $definition->getArgument(2)) { return; } - $definition->replaceArgument(0, $this->processFormTypes($container, $definition)); + $definition->replaceArgument(0, $this->processFormTypes($container)); $definition->replaceArgument(1, $this->processFormTypeExtensions($container)); $definition->replaceArgument(2, $this->processFormTypeGuessers($container)); } - private function processFormTypes(ContainerBuilder $container, Definition $definition) + private function processFormTypes(ContainerBuilder $container) { // Get service locator argument $servicesMap = array(); diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php index 943ad3fba5f3d..378edf563b972 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php @@ -85,9 +85,6 @@ public function __construct(FormDataExtractorInterface $dataExtractor) */ public function collect(Request $request, Response $response, \Exception $exception = null) { - if (70000 <= \PHP_VERSION_ID && \PHP_VERSION_ID < 70008) { - @trigger_error('A bug in PHP 7.0.0 to 7.0.7 is breaking the Form panel, please upgrade to 7.0.8 or higher.', E_USER_DEPRECATED); - } } /** diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json index 85250b87f357b..a88c0d64eec09 100644 --- a/src/Symfony/Component/Form/composer.json +++ b/src/Symfony/Component/Form/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/event-dispatcher": "~2.8|~3.0", "symfony/intl": "^2.8.18|^3.2.5", "symfony/options-resolver": "~2.8|~3.0", diff --git a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php index 5f18aa93094ec..dcf5dcfee64b4 100644 --- a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php +++ b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php @@ -150,7 +150,7 @@ public function setAutoEtag() * Sets the Content-Disposition header with the given filename. * * @param string $disposition ResponseHeaderBag::DISPOSITION_INLINE or ResponseHeaderBag::DISPOSITION_ATTACHMENT - * @param string $filename Optionally use this filename instead of the real name of the file + * @param string $filename Optionally use this UTF-8 encoded filename instead of the real name of the file * @param string $filenameFallback A fallback filename, containing only ASCII characters. Defaults to an automatically encoded filename * * @return $this @@ -162,7 +162,7 @@ public function setContentDisposition($disposition, $filename = '', $filenameFal } if ('' === $filenameFallback && (!preg_match('/^[\x20-\x7e]*$/', $filename) || false !== strpos($filename, '%'))) { - $encoding = mb_detect_encoding($filename, null, true); + $encoding = mb_detect_encoding($filename, null, true) ?: '8bit'; for ($i = 0, $filenameLength = mb_strlen($filename, $encoding); $i < $filenameLength; ++$i) { $char = mb_substr($filename, $i, 1, $encoding); diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.php index 962a3878d9767..4e490a05d4ce0 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.php @@ -95,7 +95,9 @@ public function write($sessionId, $data) */ public function destroy($sessionId) { - return $this->memcache->delete($this->prefix.$sessionId); + $this->memcache->delete($this->prefix.$sessionId); + + return true; } /** diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php index 76b08e2db944c..67a49ad6f5e2a 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php @@ -101,7 +101,9 @@ public function write($sessionId, $data) */ public function destroy($sessionId) { - return $this->memcached->delete($this->prefix.$sessionId); + $result = $this->memcached->delete($this->prefix.$sessionId); + + return $result || $this->memcached->getResultCode() == \Memcached::RES_NOTFOUND; } /** diff --git a/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php index 89e078ca2a935..1b9e58991cc6d 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php @@ -69,6 +69,17 @@ public function testSetContentDispositionGeneratesSafeFallbackFilename() $this->assertSame('attachment; filename="f__.html"; filename*=utf-8\'\'f%C3%B6%C3%B6.html', $response->headers->get('Content-Disposition')); } + public function testSetContentDispositionGeneratesSafeFallbackFilenameForWronglyEncodedFilename() + { + $response = new BinaryFileResponse(__FILE__); + + $iso88591EncodedFilename = utf8_decode('föö.html'); + $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $iso88591EncodedFilename); + + // the parameter filename* is invalid in this case (rawurldecode('f%F6%F6') does not provide a UTF-8 string but an ISO-8859-1 encoded one) + $this->assertSame('attachment; filename="f__.html"; filename*=utf-8\'\'f%F6%F6.html', $response->headers->get('Content-Disposition')); + } + /** * @dataProvider provideRanges */ diff --git a/src/Symfony/Component/HttpFoundation/composer.json b/src/Symfony/Component/HttpFoundation/composer.json index dfa25f79ec405..a964975ecb3f2 100644 --- a/src/Symfony/Component/HttpFoundation/composer.json +++ b/src/Symfony/Component/HttpFoundation/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-mbstring": "~1.1" }, "require-dev": { diff --git a/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php b/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php index c3772b688e548..4fea1168aa47f 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php @@ -107,8 +107,7 @@ public function onKernelTerminate(PostResponseEvent $event) { // attach children to parents foreach ($this->profiles as $request) { - // isset call should be removed when requestStack is required - if (isset($this->parents[$request]) && null !== $parentRequest = $this->parents[$request]) { + if (null !== $parentRequest = $this->parents[$request]) { if (isset($this->profiles[$parentRequest])) { $this->profiles[$parentRequest]->addChild($this->profiles[$request]); } diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 0372a86cf7cbb..815841ec6c84f 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -61,11 +61,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface private $projectDir; - const VERSION = '3.3.6'; - const VERSION_ID = 30306; + const VERSION = '3.3.7'; + const VERSION_ID = 30307; const MAJOR_VERSION = 3; const MINOR_VERSION = 3; - const RELEASE_VERSION = 6; + const RELEASE_VERSION = 7; const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '01/2018'; diff --git a/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php b/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php index bd8761f5dd8a8..e24b2e0183684 100644 --- a/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php +++ b/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php @@ -142,11 +142,19 @@ public function write(Profile $profile) } } + $profileToken = $profile->getToken(); + // when there are errors in sub-requests, the parent and/or children tokens + // may equal the profile token, resulting in infinite loops + $parentToken = $profile->getParentToken() !== $profileToken ? $profile->getParentToken() : null; + $childrenToken = array_filter(array_map(function ($p) use ($profileToken) { + return $profileToken !== $p->getToken() ? $p->getToken() : null; + }, $profile->getChildren())); + // Store profile $data = array( - 'token' => $profile->getToken(), - 'parent' => $profile->getParentToken(), - 'children' => array_map(function ($p) { return $p->getToken(); }, $profile->getChildren()), + 'token' => $profileToken, + 'parent' => $parentToken, + 'children' => $childrenToken, 'data' => $profile->getCollectors(), 'ip' => $profile->getIp(), 'method' => $profile->getMethod(), diff --git a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php index d2a27d03aaf37..052e5766d6c7c 100644 --- a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpKernel\Bundle\BundleInterface; use Symfony\Component\HttpKernel\Config\EnvParametersResource; use Symfony\Component\HttpKernel\Kernel; @@ -26,6 +27,12 @@ class KernelTest extends TestCase { + public static function tearDownAfterClass() + { + $fs = new Filesystem(); + $fs->remove(__DIR__.'/Fixtures/cache'); + } + public function testConstructor() { $env = 'test_env'; diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index babc8663b6487..cc543c6d854fb 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/event-dispatcher": "~2.8|~3.0", "symfony/http-foundation": "~3.3", "symfony/debug": "~2.8|~3.0", diff --git a/src/Symfony/Component/Inflector/composer.json b/src/Symfony/Component/Inflector/composer.json index 79f4ed788f62b..df6c12f518746 100644 --- a/src/Symfony/Component/Inflector/composer.json +++ b/src/Symfony/Component/Inflector/composer.json @@ -23,7 +23,7 @@ } ], "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "autoload": { "psr-4": { "Symfony\\Component\\Inflector\\": "" }, diff --git a/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php b/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php index 84b20abf77ee8..4521b06d43936 100644 --- a/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php +++ b/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php @@ -32,17 +32,15 @@ public function read($path, $locale) if (!file_exists($fileName)) { throw new ResourceBundleNotFoundException(sprintf( - 'The resource bundle "%s/%s.json" does not exist.', - $path, - $locale + 'The resource bundle "%s" does not exist.', + $fileName )); } if (!is_file($fileName)) { throw new RuntimeException(sprintf( - 'The resource bundle "%s/%s.json" is not a file.', - $path, - $locale + 'The resource bundle "%s" is not a file.', + $fileName )); } @@ -50,9 +48,8 @@ public function read($path, $locale) if (null === $data) { throw new RuntimeException(sprintf( - 'The resource bundle "%s/%s.json" contains invalid JSON: %s', - $path, - $locale, + 'The resource bundle "%s" contains invalid JSON: %s', + $fileName, json_last_error_msg() )); } diff --git a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php index 21eec04e0cef7..12a76fb056b68 100644 --- a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php +++ b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php @@ -357,9 +357,9 @@ public function formatCurrency($value, $currency) /** * Format a number. * - * @param number $value The value to format - * @param int $type Type of the formatting, one of the format type constants - * Only type NumberFormatter::TYPE_DEFAULT is currently supported. + * @param int|float $value The value to format + * @param int $type Type of the formatting, one of the format type constants + * Only type NumberFormatter::TYPE_DEFAULT is currently supported. * * @return bool|string The formatted value or false on error * diff --git a/src/Symfony/Component/Intl/composer.json b/src/Symfony/Component/Intl/composer.json index dea362ed22040..452582bb8fecb 100644 --- a/src/Symfony/Component/Intl/composer.json +++ b/src/Symfony/Component/Intl/composer.json @@ -24,7 +24,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-intl-icu": "~1.0" }, "require-dev": { diff --git a/src/Symfony/Component/Ldap/composer.json b/src/Symfony/Component/Ldap/composer.json index a2f580f4319fc..5cc3ce11e8c6d 100644 --- a/src/Symfony/Component/Ldap/composer.json +++ b/src/Symfony/Component/Ldap/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-php56": "~1.0", "symfony/options-resolver": "~2.8|~3.0", "ext-ldap": "*" diff --git a/src/Symfony/Component/OptionsResolver/composer.json b/src/Symfony/Component/OptionsResolver/composer.json index a751730af3819..4311b59c78172 100644 --- a/src/Symfony/Component/OptionsResolver/composer.json +++ b/src/Symfony/Component/OptionsResolver/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "autoload": { "psr-4": { "Symfony\\Component\\OptionsResolver\\": "" }, diff --git a/src/Symfony/Component/Process/composer.json b/src/Symfony/Component/Process/composer.json index f899c52b2b818..a6d7c7c464bb0 100644 --- a/src/Symfony/Component/Process/composer.json +++ b/src/Symfony/Component/Process/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "autoload": { "psr-4": { "Symfony\\Component\\Process\\": "" }, diff --git a/src/Symfony/Component/PropertyAccess/composer.json b/src/Symfony/Component/PropertyAccess/composer.json index 508dd78cc70f4..011ffa7783e46 100644 --- a/src/Symfony/Component/PropertyAccess/composer.json +++ b/src/Symfony/Component/PropertyAccess/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-php70": "~1.0", "symfony/inflector": "~3.1" }, diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php index d0eb3eed06c49..f41056050870c 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php @@ -72,6 +72,7 @@ public function typesProvider() array('donotexist', null, null, null), array('staticGetter', null, null, null), array('staticSetter', null, null, null), + array('emptyVar', null, null, null), ); } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php index 573528c012f55..905b50d5dff86 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php @@ -41,6 +41,7 @@ public function testGetProperties() 'B', 'Guid', 'g', + 'emptyVar', 'foo', 'foo2', 'foo3', @@ -122,37 +123,63 @@ public function php71TypesProvider() ); } - public function testIsReadable() + /** + * @dataProvider getReadableProperties + */ + public function testIsReadable($property, $expected) + { + $this->assertSame( + $expected, + $this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property, array()) + ); + } + + public function getReadableProperties() + { + return array( + array('bar', false), + array('baz', false), + array('parent', true), + array('a', true), + array('b', false), + array('c', true), + array('d', true), + array('e', false), + array('f', false), + array('Id', true), + array('id', true), + array('Guid', true), + array('guid', false), + ); + } + + /** + * @dataProvider getWritableProperties + */ + public function testIsWritable($property, $expected) { - $this->assertFalse($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'bar', array())); - $this->assertFalse($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'baz', array())); - $this->assertTrue($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'parent', array())); - $this->assertTrue($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'a', array())); - $this->assertFalse($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'b', array())); - $this->assertTrue($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'c', array())); - $this->assertTrue($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'd', array())); - $this->assertFalse($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'e', array())); - $this->assertFalse($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'f', array())); - $this->assertTrue($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'Id', array())); - $this->assertTrue($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'id', array())); - $this->assertTrue($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'Guid', array())); - $this->assertFalse($this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'guid', array())); + $this->assertSame( + $expected, + $this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property, array()) + ); } - public function testIsWritable() + public function getWritableProperties() { - $this->assertFalse($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'bar', array())); - $this->assertFalse($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'baz', array())); - $this->assertTrue($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'parent', array())); - $this->assertFalse($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'a', array())); - $this->assertTrue($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'b', array())); - $this->assertFalse($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'c', array())); - $this->assertFalse($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'd', array())); - $this->assertTrue($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'e', array())); - $this->assertTrue($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'f', array())); - $this->assertFalse($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'Id', array())); - $this->assertTrue($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'Guid', array())); - $this->assertFalse($this->extractor->isWritable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', 'guid', array())); + return array( + array('bar', false), + array('baz', false), + array('parent', true), + array('a', false), + array('b', true), + array('c', false), + array('d', false), + array('e', true), + array('f', true), + array('Id', false), + array('Guid', true), + array('guid', false), + ); } public function testSingularize() diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php index d358bae13ad61..4e558eca014e5 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php @@ -68,6 +68,13 @@ class Dummy extends ParentDummy */ public $g; + /** + * This should not be removed. + * + * @var + */ + public $emptyVar; + public static function getStatic() { } diff --git a/src/Symfony/Component/PropertyInfo/composer.json b/src/Symfony/Component/PropertyInfo/composer.json index 4d6cac3ef98d9..d068a5ff6a3c3 100644 --- a/src/Symfony/Component/PropertyInfo/composer.json +++ b/src/Symfony/Component/PropertyInfo/composer.json @@ -23,18 +23,18 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/inflector": "~3.1" }, "require-dev": { "symfony/serializer": "~2.8|~3.0", "symfony/cache": "~3.1", "symfony/dependency-injection": "~3.3", - "phpdocumentor/reflection-docblock": "^3.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0", "doctrine/annotations": "~1.0" }, "conflict": { - "phpdocumentor/reflection-docblock": "<3.0||>=3.2.0", + "phpdocumentor/reflection-docblock": "<3.0||>=3.2.0,<3.2.2", "phpdocumentor/type-resolver": "<0.2.0", "symfony/dependency-injection": "<3.3" }, diff --git a/src/Symfony/Component/Routing/composer.json b/src/Symfony/Component/Routing/composer.json index 38069dc4e9ab0..bb4f6a554bd7b 100644 --- a/src/Symfony/Component/Routing/composer.json +++ b/src/Symfony/Component/Routing/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "require-dev": { "symfony/config": "~2.8|~3.0", diff --git a/src/Symfony/Component/Security/Core/composer.json b/src/Symfony/Component/Security/Core/composer.json index 3e045fb227545..09db36781a2d0 100644 --- a/src/Symfony/Component/Security/Core/composer.json +++ b/src/Symfony/Component/Security/Core/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-php56": "~1.0" }, "require-dev": { diff --git a/src/Symfony/Component/Security/Csrf/composer.json b/src/Symfony/Component/Security/Csrf/composer.json index 913cc458b2e2a..46cca3a0abab0 100644 --- a/src/Symfony/Component/Security/Csrf/composer.json +++ b/src/Symfony/Component/Security/Csrf/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-php56": "~1.0", "symfony/polyfill-php70": "~1.0", "symfony/security-core": "~2.8|~3.0" diff --git a/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php b/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php index fef4a04fb9702..307d70f9e973e 100644 --- a/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php +++ b/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php @@ -153,6 +153,7 @@ public function onAuthenticationSuccess(Request $request, TokenInterface $token, * done by having a _remember_me checkbox in your form, but * can be configured by the "always_remember_me" and "remember_me_parameter" * parameters under the "remember_me" firewall key + * D) The onAuthenticationSuccess method returns a Response object * * @return bool */ diff --git a/src/Symfony/Component/Security/Guard/composer.json b/src/Symfony/Component/Security/Guard/composer.json index 4bf473a89fa93..b21039a275cd6 100644 --- a/src/Symfony/Component/Security/Guard/composer.json +++ b/src/Symfony/Component/Security/Guard/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/security-core": "~2.8|~3.0", "symfony/security-http": "~3.1" }, diff --git a/src/Symfony/Component/Security/Http/SecurityEvents.php b/src/Symfony/Component/Security/Http/SecurityEvents.php index 550acb4246eee..e1adf1b59edc3 100644 --- a/src/Symfony/Component/Security/Http/SecurityEvents.php +++ b/src/Symfony/Component/Security/Http/SecurityEvents.php @@ -14,8 +14,11 @@ final class SecurityEvents { /** - * The INTERACTIVE_LOGIN event occurs after a user is logged in - * interactively for authentication based on http, cookies or X509. + * The INTERACTIVE_LOGIN event occurs after a user has actively logged + * into your website. It is important to distinguish this action from + * non-interactive authentication methods, such as: + * - authentication based on your session. + * - authentication using a HTTP basic or HTTP digest header. * * @Event("Symfony\Component\Security\Http\Event\InteractiveLoginEvent") * diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json index b1458eaa93c7a..595364436723c 100644 --- a/src/Symfony/Component/Security/Http/composer.json +++ b/src/Symfony/Component/Security/Http/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/security-core": "~3.2", "symfony/event-dispatcher": "~2.8|~3.0", "symfony/http-foundation": "~2.8|~3.0", diff --git a/src/Symfony/Component/Security/composer.json b/src/Symfony/Component/Security/composer.json index e6835fe883e2e..f39bfcf8ccb58 100644 --- a/src/Symfony/Component/Security/composer.json +++ b/src/Symfony/Component/Security/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/event-dispatcher": "~2.8|~3.0", "symfony/http-foundation": "~2.8|~3.0", "symfony/http-kernel": "~3.3", diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/YamlEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/YamlEncoderTest.php index c602039667394..0e280245ec2ee 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/YamlEncoderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/YamlEncoderTest.php @@ -61,7 +61,9 @@ public function testContext() $obj = new \stdClass(); $obj->bar = 2; - $this->assertEquals(" foo: !php/object:O:8:\"stdClass\":1:{s:3:\"bar\";i:2;}\n", $encoder->encode(array('foo' => $obj), 'yaml')); + $legacyTag = " foo: !php/object:O:8:\"stdClass\":1:{s:3:\"bar\";i:2;}\n"; + $spacedTag = " foo: !php/object 'O:8:\"stdClass\":1:{s:3:\"bar\";i:2;}'\n"; + $this->assertThat($encoder->encode(array('foo' => $obj), 'yaml'), $this->logicalOr($this->equalTo($legacyTag), $this->equalTo($spacedTag))); $this->assertEquals(' { foo: null }', $encoder->encode(array('foo' => $obj), 'yaml', array('yaml_inline' => 0, 'yaml_indent' => 2, 'yaml_flags' => 0))); $this->assertEquals(array('foo' => $obj), $encoder->decode('foo: !php/object:O:8:"stdClass":1:{s:3:"bar";i:2;}', 'yaml')); $this->assertEquals(array('foo' => null), $encoder->decode('foo: !php/object:O:8:"stdClass":1:{s:3:"bar";i:2;}', 'yaml', array('yaml_flags' => 0))); diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json index 37dd110bc3f25..e4ee63a730bc0 100644 --- a/src/Symfony/Component/Serializer/composer.json +++ b/src/Symfony/Component/Serializer/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "require-dev": { "symfony/yaml": "~3.3", @@ -28,7 +28,7 @@ "doctrine/annotations": "~1.0", "symfony/dependency-injection": "~3.2", "doctrine/cache": "~1.0", - "phpdocumentor/reflection-docblock": "~3.0" + "phpdocumentor/reflection-docblock": "^3.0|^4.0" }, "conflict": { "symfony/dependency-injection": "<3.2", diff --git a/src/Symfony/Component/Stopwatch/composer.json b/src/Symfony/Component/Stopwatch/composer.json index 0b40c8d102d0d..06194a018693e 100644 --- a/src/Symfony/Component/Stopwatch/composer.json +++ b/src/Symfony/Component/Stopwatch/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "autoload": { "psr-4": { "Symfony\\Component\\Stopwatch\\": "" }, diff --git a/src/Symfony/Component/Templating/composer.json b/src/Symfony/Component/Templating/composer.json index 7b1d0fb03bf5e..6b34d63a4ebe4 100644 --- a/src/Symfony/Component/Templating/composer.json +++ b/src/Symfony/Component/Templating/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "require-dev": { "psr/log": "~1.0" diff --git a/src/Symfony/Component/Translation/composer.json b/src/Symfony/Component/Translation/composer.json index e107e2538a15d..f9ed96dc62446 100644 --- a/src/Symfony/Component/Translation/composer.json +++ b/src/Symfony/Component/Translation/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { diff --git a/src/Symfony/Component/Validator/Constraints/File.php b/src/Symfony/Component/Validator/Constraints/File.php index a1c831ecab2ae..4d4c68d7fd9fb 100644 --- a/src/Symfony/Component/Validator/Constraints/File.php +++ b/src/Symfony/Component/Validator/Constraints/File.php @@ -18,6 +18,8 @@ * @Annotation * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) * + * @property int $maxSize + * * @author Bernhard Schussek */ class File extends Constraint diff --git a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php index af32dae4ae8e8..3381076f3e48c 100644 --- a/src/Symfony/Component/Validator/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/Validator/Mapping/ClassMetadata.php @@ -339,6 +339,10 @@ public function addGetterMethodConstraints($property, $method, array $constraint */ public function mergeConstraints(ClassMetadata $source) { + if ($source->isGroupSequenceProvider()) { + $this->setGroupSequenceProvider(true); + } + foreach ($source->getConstraints() as $constraint) { $this->addConstraint(clone $constraint); } diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf index 4fa0d42220500..a3199bcc9d79e 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.el.xlf @@ -100,7 +100,7 @@ This value is not valid. - Αυτή η τιμή δεν είναι έκγυρη. + Αυτή η τιμή δεν είναι έγκυρη. This value is not a valid time. @@ -136,11 +136,11 @@ This is not a valid IP address. - Αυτό δεν είναι μια έκγυρη διεύθυνση IP. + Αυτό δεν είναι μια έγκυρη διεύθυνση IP. This value is not a valid language. - Αυτή η τιμή δεν αντιστοιχεί σε μια έκγυρη γλώσσα. + Αυτή η τιμή δεν αντιστοιχεί σε μια έγκυρη γλώσσα. This value is not a valid locale. @@ -148,7 +148,7 @@ This value is not a valid country. - Αυτή η τιμή δεν αντιστοιχεί σε μια έκγυρη χώρα. + Αυτή η τιμή δεν αντιστοιχεί σε μια έγκυρη χώρα. This value is already used. diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/GroupSequenceProviderChildEntity.php b/src/Symfony/Component/Validator/Tests/Fixtures/GroupSequenceProviderChildEntity.php new file mode 100644 index 0000000000000..be7191f9b6e1d --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Fixtures/GroupSequenceProviderChildEntity.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Fixtures; + +class GroupSequenceProviderChildEntity extends GroupSequenceProviderEntity +{ +} diff --git a/src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php b/src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php index 9a23e8cf355a0..a3f8a8c1ec8a3 100644 --- a/src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php +++ b/src/Symfony/Component/Validator/Tests/Mapping/ClassMetadataTest.php @@ -24,6 +24,7 @@ class ClassMetadataTest extends TestCase const CLASSNAME = 'Symfony\Component\Validator\Tests\Fixtures\Entity'; const PARENTCLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityParent'; const PROVIDERCLASS = 'Symfony\Component\Validator\Tests\Fixtures\GroupSequenceProviderEntity'; + const PROVIDERCHILDCLASS = 'Symfony\Component\Validator\Tests\Fixtures\GroupSequenceProviderChildEntity'; protected $metadata; @@ -301,6 +302,17 @@ public function testGroupSequenceProvider() $this->assertTrue($metadata->isGroupSequenceProvider()); } + public function testMergeConstraintsMergesGroupSequenceProvider() + { + $parent = new ClassMetadata(self::PROVIDERCLASS); + $parent->setGroupSequenceProvider(true); + + $metadata = new ClassMetadata(self::PROVIDERCHILDCLASS); + $metadata->mergeConstraints($parent); + + $this->assertTrue($metadata->isGroupSequenceProvider()); + } + /** * https://github.com/symfony/symfony/issues/11604. */ @@ -309,13 +321,3 @@ public function testGetPropertyMetadataReturnsEmptyArrayWithoutConfiguredMetadat $this->assertCount(0, $this->metadata->getPropertyMetadata('foo'), '->getPropertyMetadata() returns an empty collection if no metadata is configured for the given property'); } } - -class ParentClass -{ - public $example = 0; -} - -class ChildClass extends ParentClass -{ - public $example = 1; // overrides parent property of same name -} diff --git a/src/Symfony/Component/Validator/composer.json b/src/Symfony/Component/Validator/composer.json index ca441954b894f..3fd2d05fcabc4 100644 --- a/src/Symfony/Component/Validator/composer.json +++ b/src/Symfony/Component/Validator/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-mbstring": "~1.0", "symfony/translation": "~2.8|~3.0" }, diff --git a/src/Symfony/Component/VarDumper/Caster/LinkStub.php b/src/Symfony/Component/VarDumper/Caster/LinkStub.php index 1a9aa419d148f..eb9bc85c914f9 100644 --- a/src/Symfony/Component/VarDumper/Caster/LinkStub.php +++ b/src/Symfony/Component/VarDumper/Caster/LinkStub.php @@ -89,7 +89,11 @@ private function getComposerRoot($file, &$inVendor) } $parent = $dir; - while (!file_exists($parent.'/composer.json')) { + while (!@file_exists($parent.'/composer.json')) { + if (!@file_exists($parent)) { + // open_basedir restriction in effect + break; + } if ($parent === dirname($parent)) { return self::$composerRoots[$dir] = false; } diff --git a/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php b/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php index 83f6aa7c317c3..0fbd213abe987 100644 --- a/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php @@ -159,7 +159,8 @@ public function dump(Data $data, $output = null) /** * Dumps the current line. * - * @param int $depth The recursive depth in the dumped structure for the line being dumped + * @param int $depth The recursive depth in the dumped structure for the line being dumped, + * or -1 to signal the end-of-dump to the line dumper callable */ protected function dumpLine($depth) { diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index cf94b1af93039..90596f2551329 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -155,10 +155,10 @@ protected function getDumpHeader() function toggle(a, recursive) { var s = a.nextSibling || {}, oldClass = s.className, arrow, newClass; - if ('sf-dump-compact' == oldClass) { + if (/\bsf-dump-compact\b/.test(oldClass)) { arrow = '▼'; newClass = 'sf-dump-expanded'; - } else if ('sf-dump-expanded' == oldClass) { + } else if (/\bsf-dump-expanded\b/.test(oldClass)) { arrow = '▶'; newClass = 'sf-dump-compact'; } else { @@ -166,13 +166,13 @@ function toggle(a, recursive) { } a.lastChild.innerHTML = arrow; - s.className = newClass; + s.className = s.className.replace(/\bsf-dump-(compact|expanded)\b/, newClass); if (recursive) { try { a = s.querySelectorAll('.'+oldClass); for (s = 0; s < a.length; ++s) { - if (a[s].className !== newClass) { + if (-1 == a[s].className.indexOf(newClass)) { a[s].className = newClass; a[s].previousSibling.lastChild.innerHTML = arrow; } @@ -187,7 +187,7 @@ function toggle(a, recursive) { function collapse(a, recursive) { var s = a.nextSibling || {}, oldClass = s.className; - if ('sf-dump-expanded' == oldClass) { + if (/\bsf-dump-expanded\b/.test(oldClass)) { toggle(a, recursive); return true; @@ -199,7 +199,7 @@ function collapse(a, recursive) { function expand(a, recursive) { var s = a.nextSibling || {}, oldClass = s.className; - if ('sf-dump-compact' == oldClass) { + if (/\bsf-dump-compact\b/.test(oldClass)) { toggle(a, recursive); return true; @@ -254,8 +254,8 @@ function highlight(root, activeNode, nodes) { function resetHighlightedNodes(root) { Array.from(root.querySelectorAll('.sf-dump-str, .sf-dump-key, .sf-dump-public, .sf-dump-protected, .sf-dump-private')).forEach(function (strNode) { - strNode.className = strNode.className.replace(/\b sf-dump-highlight\b/, ''); - strNode.className = strNode.className.replace(/\b sf-dump-highlight-active\b/, ''); + strNode.className = strNode.className.replace(/\bsf-dump-highlight\b/, ''); + strNode.className = strNode.className.replace(/\bsf-dump-highlight-active\b/, ''); }); } @@ -334,7 +334,7 @@ function xpathString(str) { if (f && t && f[0] !== t[0]) { r.innerHTML = r.innerHTML.replace(new RegExp('^'+f[0].replace(rxEsc, '\\$1'), 'mg'), t[0]); } - if ('sf-dump-compact' == r.className) { + if (/\bsf-dump-compact\b/.test(r.className)) { toggle(s, isCtrlKey(e)); } } @@ -352,7 +352,7 @@ function xpathString(str) { } else if (/\bsf-dump-str-toggle\b/.test(a.className)) { e.preventDefault(); e = a.parentNode.parentNode; - e.className = e.className.replace(/sf-dump-str-(expand|collapse)/, a.parentNode.className); + e.className = e.className.replace(/\bsf-dump-str-(expand|collapse)\b/, a.parentNode.className); } }); @@ -378,6 +378,7 @@ function xpathString(str) { a.title = (a.title ? a.title+'\n[' : '[')+keyHint+'+click] Expand all children'; a.innerHTML += ''; a.className += ' sf-dump-toggle'; + x = 1; if ('sf-dump' != elt.parentNode.className) { x += elt.parentNode.getAttribute('data-depth')/1; @@ -386,7 +387,7 @@ function xpathString(str) { if (x > options.maxDepth) { toggle(a); } - } else if ('sf-dump-ref' == elt.className && (a = elt.getAttribute('href'))) { + } else if (/\bsf-dump-ref\b/.test(elt.className) && (a = elt.getAttribute('href'))) { a = a.substr(1); elt.className += ' '+a; @@ -518,8 +519,7 @@ function showCurrent(state) Array.from(search.querySelectorAll('.sf-dump-search-input-next, .sf-dump-search-input-previous')).forEach(function (btn) { addEventListener(btn, 'click', function (e) { e.preventDefault(); - var direction = -1 !== e.target.className.indexOf('next') ? 'next' : 'previous'; - 'next' === direction ? state.next() : state.previous(); + -1 !== e.target.className.indexOf('next') ? state.next() : state.previous(); searchInput.focus(); collapseAll(root); showCurrent(state); diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/RedisCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/RedisCasterTest.php index af038192f5c8d..80acdb66798f9 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/RedisCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/RedisCasterTest.php @@ -61,8 +61,7 @@ public function testConnected() EODUMP; } else { $xCast = <<<'EODUMP' -Redis { - +"socket": Redis Socket Buffer resource +Redis {%A isConnected: true host: "127.0.0.1" port: 6379 diff --git a/src/Symfony/Component/VarDumper/composer.json b/src/Symfony/Component/VarDumper/composer.json index 930896026ae53..ae74aa4901a45 100644 --- a/src/Symfony/Component/VarDumper/composer.json +++ b/src/Symfony/Component/VarDumper/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { diff --git a/src/Symfony/Component/WebLink/composer.json b/src/Symfony/Component/WebLink/composer.json index 336feaf006031..a3df726a63b94 100644 --- a/src/Symfony/Component/WebLink/composer.json +++ b/src/Symfony/Component/WebLink/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "fig/link-util": "^1.0", "psr/link": "^1.0" }, diff --git a/src/Symfony/Component/Workflow/Validator/StateMachineValidator.php b/src/Symfony/Component/Workflow/Validator/StateMachineValidator.php index 16948f6769b6e..8fe5eecc4a22a 100644 --- a/src/Symfony/Component/Workflow/Validator/StateMachineValidator.php +++ b/src/Symfony/Component/Workflow/Validator/StateMachineValidator.php @@ -31,7 +31,7 @@ public function validate(Definition $definition, $name) // Make sure that each transition has exactly one FROM $froms = $transition->getFroms(); if (1 !== count($froms)) { - throw new InvalidDefinitionException(sprintf('A transition in StateMachine can only have one input. But the transition "%s" in StateMachine "%s" has %d inputs.', $transition->getName(), $name, count($transition->getTos()))); + throw new InvalidDefinitionException(sprintf('A transition in StateMachine can only have one input. But the transition "%s" in StateMachine "%s" has %d inputs.', $transition->getName(), $name, count($froms))); } // Enforcing uniqueness of the names of transitions starting at each node diff --git a/src/Symfony/Component/Workflow/composer.json b/src/Symfony/Component/Workflow/composer.json index 2e5825f48d0fb..7feef3fe983d6 100644 --- a/src/Symfony/Component/Workflow/composer.json +++ b/src/Symfony/Component/Workflow/composer.json @@ -20,7 +20,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/property-access": "~2.3|~3.0" }, "require-dev": { diff --git a/src/Symfony/Component/Yaml/composer.json b/src/Symfony/Component/Yaml/composer.json index 698b5ced17364..2103d6cfd0092 100644 --- a/src/Symfony/Component/Yaml/composer.json +++ b/src/Symfony/Component/Yaml/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "require-dev": { "symfony/console": "~2.8|~3.0"