diff --git a/CHANGELOG-4.0.md b/CHANGELOG-4.0.md index f1c98b1874d2a..2a4c1db371283 100644 --- a/CHANGELOG-4.0.md +++ b/CHANGELOG-4.0.md @@ -7,6 +7,35 @@ in 4.0 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v4.0.0...v4.0.1 +* 4.0.1 (2017-12-05) + + * bug #25304 [Bridge/PhpUnit] Prefer $_SERVER['argv'] over $argv (ricknox) + * bug #25272 [SecurityBundle] fix setLogoutOnUserChange calls for context listeners (dmaicher) + * bug #25282 [DI] Register singly-implemented interfaces when doing PSR-4 discovery (nicolas-grekas) + * bug #25274 [Security] Adding a GuardAuthenticatorHandler alias (weaverryan) + * bug #25308 [FrameworkBundle] Fix a bug where a color tag will be shown when passing an antislash (Simperfit) + * bug #25278 Fix for missing whitespace control modifier in form layout (kubawerlos) + * bug #25306 [Form][TwigBridge] Fix collision between view properties and form fields (yceruto) + * bug #25305 [Form][TwigBridge] Fix collision between view properties and form fields (yceruto) + * bug #25236 [Form][TwigBridge] Fix collision between view properties and form fields (yceruto) + * bug #25312 [DI] Fix deep-inlining of non-shared refs (nicolas-grekas) + * bug #25309 [Yaml] parse newlines in quoted multiline strings (xabbuh) + * bug #25313 [DI] Fix missing unset leading to false-positive circular ref (nicolas-grekas) + * bug #25268 [DI] turn $private to protected in dumped container, to make cache:clear BC (nicolas-grekas) + * bug #25285 [DI] Throw an exception if Expression Language is not installed (sroze) + * bug #25241 [Yaml] do not eagerly filter comment lines (xabbuh) + * bug #25284 [DI] Cast ids to string, as done on 3.4 (nicolas-grekas, sroze) + * bug #25297 [Validator] Fixed the @Valid(groups={"group"}) against null exception case (vudaltsov) + * bug #25255 [Console][DI] Fail gracefully (nicolas-grekas) + * bug #25264 [DI] Trigger deprecation when setting a to-be-private synthetic service (nicolas-grekas) + * bug #25258 [link] Prevent warnings when running link with 2.7 (dunglas) + * bug #25244 [DI] Add missing deprecation when fetching private services from ContainerBuilder (nicolas-grekas) + * bug #24750 [Validator] ExpressionValidator should use OBJECT_TO_STRING (Simperfit) + * bug #25247 [DI] Fix false-positive circular exception (nicolas-grekas) + * bug #25226 [HttpKernel] Fix issue when resetting DumpDataCollector (Pierstoval) + * bug #25230 Use a more specific file for detecting the bridge (greg0ire) + * bug #25232 [WebProfilerBundle] [TwigBundle] Fix Profiler breaking XHTML pages (tistre) + * 4.0.0 (2017-11-30) * bug #25220 [HttpFoundation] Add Session::isEmpty(), fix MockFileSessionStorage to behave like the native one (nicolas-grekas) diff --git a/link b/link index f4070998e72b5..a5030d0683379 100755 --- a/link +++ b/link @@ -35,11 +35,14 @@ if (!is_dir("$argv[1]/vendor/symfony")) { } $sfPackages = array('symfony/symfony' => __DIR__); + +$filesystem = new Filesystem(); foreach (glob(__DIR__.'/src/Symfony/{Bundle,Bridge,Component,Component/Security}/*', GLOB_BRACE | GLOB_ONLYDIR | GLOB_NOSORT) as $dir) { - $sfPackages[json_decode(file_get_contents("$dir/composer.json"))->name] = $dir; + if ($filesystem->exists($composer = "$dir/composer.json")) { + $sfPackages[json_decode(file_get_contents($composer))->name] = $dir; + } } -$filesystem = new Filesystem(); foreach (glob("$argv[1]/vendor/symfony/*", GLOB_ONLYDIR | GLOB_NOSORT) as $dir) { $package = 'symfony/'.basename($dir); if (is_link($dir)) { @@ -57,3 +60,7 @@ foreach (glob("$argv[1]/vendor/symfony/*", GLOB_ONLYDIR | GLOB_NOSORT) as $dir) $filesystem->symlink($sfDir, $dir); echo "\"$package\" has been linked to \"$sfPackages[$package]\".".PHP_EOL; } + +foreach (glob("$argv[1]/var/cache/*") as $cacheDir) { + $filesystem->remove($cacheDir); +} diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit index 562d018124a37..edf11e7a3ef38 100755 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit @@ -27,7 +27,7 @@ if (PHP_VERSION_ID >= 70200) { } $root = __DIR__; -while (!file_exists($root.'/composer.json') || file_exists($root.'/bin/simple-phpunit')) { +while (!file_exists($root.'/composer.json') || file_exists($root.'/DeprecationErrorHandler.php')) { if ($root === dirname($root)) { break; } @@ -59,7 +59,12 @@ if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__ passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? '(del /S /F /Q %s & rmdir %1$s) >nul': 'rm -rf %s', "phpunit-$PHPUNIT_VERSION")); } if (extension_loaded('openssl') && ini_get('allow_url_fopen') && !isset($_SERVER['http_proxy']) && !isset($_SERVER['https_proxy'])) { - stream_copy_to_stream(fopen("https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip", 'rb'), fopen("$PHPUNIT_VERSION.zip", 'wb')); + $remoteZip = "https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip"; + $remoteZipStream = @fopen($remoteZip, 'rb'); + if (!$remoteZipStream) { + throw new \RuntimeException("Could not find $remoteZip"); + } + stream_copy_to_stream($remoteZipStream, fopen("$PHPUNIT_VERSION.zip", 'wb')); } else { @unlink("$PHPUNIT_VERSION.zip"); passthru("wget https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip"); @@ -119,6 +124,9 @@ EOPHP } +global $argv, $argc; +$argv = isset($_SERVER['argv']) ? $_SERVER['argv'] : array(); +$argc = isset($_SERVER['argc']) ? $_SERVER['argc'] : 0; $components = array(); $cmd = array_map('escapeshellarg', $argv); $exit = 0; diff --git a/src/Symfony/Bridge/Twig/Extension/FormExtension.php b/src/Symfony/Bridge/Twig/Extension/FormExtension.php index 4ca968bca9a8c..e2ddbaf47fdaf 100644 --- a/src/Symfony/Bridge/Twig/Extension/FormExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/FormExtension.php @@ -13,6 +13,7 @@ use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser; use Symfony\Component\Form\ChoiceList\View\ChoiceView; +use Symfony\Component\Form\FormView; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; use Twig\TwigFunction; @@ -72,6 +73,7 @@ public function getTests() { return array( new TwigTest('selectedchoice', 'Symfony\Bridge\Twig\Extension\twig_is_selected_choice'), + new TwigTest('rootform', 'Symfony\Bridge\Twig\Extension\twig_is_root_form'), ); } @@ -103,3 +105,11 @@ function twig_is_selected_choice(ChoiceView $choice, $selectedValue) return $choice->value === $selectedValue; } + +/** + * @internal + */ +function twig_is_root_form(FormView $formView) +{ + return null === $formView->parent; +} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig index 64fc2a210ee3e..0e198aa517cae 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig @@ -140,12 +140,12 @@ {% block form_errors -%} {% if errors|length > 0 -%} - {% if form.parent %}{% else %}
{% endif %} + {% if form is not rootform %}{% else %}
{% endif %}
    {%- for error in errors -%}
  • {{ error.message }}
  • {%- endfor -%}
- {% if form.parent %}{% else %}
{% endif %} + {% if form is not rootform %}
{% else %}
{% endif %} {%- endif %} {%- endblock form_errors %} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig index 96ca4be4d68a7..4e4e9facc3a22 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig @@ -173,7 +173,7 @@ {% block form_errors -%} {%- if errors|length > 0 -%} -
+
    {%- for error in errors -%}
  • {{ error.message }}
  • diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig index 52525c061ccba..2a7d58c0f878e 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig @@ -15,7 +15,7 @@ {%- block form_widget_compound -%}
    - {%- if form.parent is empty -%} + {%- if form is rootform -%} {{ form_errors(form) }} {%- endif -%} {{- block('form_rows') -}} @@ -347,9 +347,9 @@ {% if not child.rendered %} {{- form_row(child) -}} {% endif %} - {%- endfor %} + {%- endfor -%} - {% if not form.methodRendered and form.parent is null %} + {% if not form.methodRendered and form is rootform %} {%- do form.setMethodRendered() -%} {% set method = method|upper %} {%- if method in ["GET", "POST"] -%} @@ -361,7 +361,7 @@ {%- if form_method != method -%} {%- endif -%} - {% endif %} + {% endif -%} {% endblock form_rest %} {# Support #} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig index c7b3a4365b51b..39274c6c8d058 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig @@ -31,7 +31,7 @@ {%- block form_widget_compound -%} - {%- if form.parent is empty and errors|length > 0 -%} + {%- if form is rootform and errors|length > 0 -%}
    {{- form_errors(form) -}} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig index d34161ad0c669..3035689cc9dff 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig @@ -318,11 +318,11 @@ {% block form_errors -%} {% if errors|length > 0 -%} - {% if form.parent %}{% else %}
    {% endif %} + {% if form is not rootform %}{% else %}
    {% endif %} {%- for error in errors -%} {{ error.message }} {% if not loop.last %}, {% endif %} {%- endfor -%} - {% if form.parent %}{% else %}
    {% endif %} + {% if form is not rootform %}
    {% else %}
    {% endif %} {%- endif %} {%- endblock form_errors %} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php index 75d6f1e0504b8..22a1413f38cbc 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php @@ -149,6 +149,22 @@ public function testStartTagHasActionAttributeWhenActionIsZero() $this->assertSame('
    ', $html); } + public function isRootFormProvider() + { + return array( + array(true, new FormView()), + array(false, new FormView(new FormView())), + ); + } + + /** + * @dataProvider isRootFormProvider + */ + public function testIsRootForm($expected, FormView $formView) + { + $this->assertSame($expected, \Symfony\Bridge\Twig\Extension\twig_is_root_form($formView)); + } + protected function renderForm(FormView $view, array $vars = array()) { return (string) $this->renderer->renderBlock($view, 'form', $vars); diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 94bcd323960bc..98977ab8a6ec7 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -24,7 +24,7 @@ "symfony/asset": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", "symfony/finder": "~3.4|~4.0", - "symfony/form": "~3.4-beta4|~4.0-beta4", + "symfony/form": "~3.4|~4.0", "symfony/http-foundation": "~3.4|~4.0", "symfony/http-kernel": "~3.4|~4.0", "symfony/polyfill-intl-icu": "~1.0", diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php index 64fcc5c8157a1..3c352b4c051a2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php @@ -90,7 +90,7 @@ protected function execute(InputInterface $input, OutputInterface $output) array('Xdebug', extension_loaded('xdebug') ? 'true' : 'false'), ); - if ($dotenv = self::getDotEnvVars()) { + if ($dotenv = self::getDotenvVars()) { $rows = array_merge($rows, array( new TableSeparator(), array('Environment (.env)'), @@ -129,7 +129,7 @@ private static function isExpired(string $date): bool return false !== $date && new \DateTime() > $date->modify('last day of this month 23:59:59'); } - private static function getDotEnvVars(): array + private static function getDotenvVars(): array { $vars = array(); foreach (explode(',', getenv('SYMFONY_DOTENV_VARS')) as $name) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index e4bb3f66a7516..63513bfa430b5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor; +use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\DependencyInjection\Alias; @@ -226,7 +227,8 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o $rawOutput = isset($options['raw_text']) && $options['raw_text']; foreach ($this->sortServiceIds($serviceIds) as $serviceId) { $definition = $this->resolveServiceDefinition($builder, $serviceId); - $styledServiceId = $rawOutput ? $serviceId : sprintf('%s', $serviceId); + + $styledServiceId = $rawOutput ? $serviceId : sprintf('%s', OutputFormatter::escape($serviceId)); if ($definition instanceof Definition) { if ($showTag) { foreach ($definition->getTag($showTag) as $key => $tag) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php index 7555f8f7aa025..4a5beeb5b8f71 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php @@ -252,7 +252,7 @@ private function createRedirectController($httpPort = null, $httpsPort = null) return new RedirectController(null, $httpPort, $httpsPort); } - public function assertRedirectUrl(Response $returnResponse, $expectedUrl) + private function assertRedirectUrl(Response $returnResponse, $expectedUrl) { $this->assertTrue($returnResponse->isRedirect($expectedUrl), "Expected: $expectedUrl\nGot: ".$returnResponse->headers->get('Location')); } diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 39c90360cb964..054d05c40b2bd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -21,7 +21,7 @@ "symfony/cache": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", "symfony/config": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4-beta4|~4.0-beta4", + "symfony/event-dispatcher": "~3.4|~4.0", "symfony/http-foundation": "~3.4|~4.0", "symfony/http-kernel": "~3.4|~4.0", "symfony/polyfill-mbstring": "~1.0", diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php index 0ca7e7f4b7f51..23fd3e03a35b6 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php @@ -199,7 +199,7 @@ private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $facto ->scalarNode('context')->cannotBeEmpty()->end() ->booleanNode('logout_on_user_change') ->defaultTrue() - ->info('When true, it will trigger a logout for the user if something has changed.') + ->info('When true, it will trigger a logout for the user if something has changed. Note: No-Op option since 4.0. Will always be true.') ->end() ->arrayNode('logout') ->treatTrueLike(array()) @@ -290,17 +290,6 @@ private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $facto return $firewall; }) ->end() - ->validate() - ->ifTrue(function ($v) { - return (isset($v['stateless']) && true === $v['stateless']) || (isset($v['security']) && false === $v['security']); - }) - ->then(function ($v) { - // this option doesn't change behavior when true when stateless, so prevent deprecations - $v['logout_on_user_change'] = true; - - return $v; - }) - ->end() ; } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 4b5270659e7b5..02d1eaeff574b 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -181,8 +181,6 @@ private function createFirewalls($config, ContainerBuilder $container) $customUserChecker = true; } - $contextListenerDefinition->addMethodCall('setLogoutOnUserChange', array($firewall['logout_on_user_change'])); - $configId = 'security.firewall.map.config.'.$name; list($matcher, $listeners, $exceptionListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds, $configId); diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml index ce6021823ba74..8e6133528c4bd 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml @@ -13,6 +13,8 @@ + + /*/*registerExtension(new TwigExtension()); $container->loadFromExtension('twig', array()); + $container->setAlias('test.twig.extension.debug.stopwatch', 'twig.extension.debug.stopwatch')->setPublic(true); $this->compileContainer($container); - $tokenParsers = $container->get('twig.extension.debug.stopwatch')->getTokenParsers(); + $tokenParsers = $container->get('test.twig.extension.debug.stopwatch')->getTokenParsers(); $stopwatchIsAvailable = new \ReflectionProperty($tokenParsers[0], 'stopwatchIsAvailable'); $stopwatchIsAvailable->setAccessible(true); diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php index 1f6644d5ddc30..ee9e72f7a1355 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php @@ -368,7 +368,7 @@ public function openAction(Request $request) $filename = $this->baseDir.DIRECTORY_SEPARATOR.$file; - if (preg_match("'(^|[/\\\\])\.\.?([/\\\\]|$)'", $file) || !is_readable($filename)) { + if (preg_match("'(^|[/\\\\])\.'", $file) || !is_readable($filename)) { throw new NotFoundHttpException(sprintf('The file "%s" cannot be opened.', $file)); } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig index 5edfee1e049f4..e83e95ae134f6 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig @@ -1,6 +1,6 @@ {# This file is partially duplicated in TwigBundle/Resources/views/base_js.html.twig. If you make any change in this file, verify the same change is needed in the other file. #} -/*/* {{ include('@WebProfiler/Profiler/toolbar.css.twig') }} -/*/*getMockBuilder('Symfony\Component\Routing\Generator\UrlGeneratorInterface')->getMock(); + $twig = $this->getMockBuilder('Twig\Environment')->disableOriginalConstructor()->getMock(); + $profiler = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Profiler\Profiler') + ->disableOriginalConstructor() + ->getMock(); + + $controller = new ProfilerController($urlGenerator, $profiler, $twig, array(), null, __DIR__.'/../..'); + + try { + $response = $controller->openAction(Request::create('/_wdt/open', Request::METHOD_GET, array('file' => $path))); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertTrue($isAllowed); + } catch (NotFoundHttpException $e) { + $this->assertFalse($isAllowed); + } + } + + public function getOpenFileCases() + { + return array( + array('README.md', true), + array('composer.json', true), + array('Controller/ProfilerController.php', true), + array('.gitignore', false), + array('../TwigBundle/README.md', false), + array('Controller/../README.md', false), + array('Controller/./ProfilerController.php', false), + ); + } + /** * @dataProvider provideCspVariants */ diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index 2845613b330b6..64d8fd6d19e4e 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -39,6 +39,8 @@ use Symfony\Component\Console\Event\ConsoleTerminateEvent; use Symfony\Component\Console\Exception\CommandNotFoundException; use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\Debug\Exception\FatalThrowableError; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -116,6 +118,25 @@ public function run(InputInterface $input = null, OutputInterface $output = null $output = new ConsoleOutput(); } + $renderException = function ($e) use ($output) { + if (!$e instanceof \Exception) { + $e = class_exists(FatalThrowableError::class) ? new FatalThrowableError($e) : new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()); + } + if ($output instanceof ConsoleOutputInterface) { + $this->renderException($e, $output->getErrorOutput()); + } else { + $this->renderException($e, $output); + } + }; + if ($phpHandler = set_exception_handler($renderException)) { + restore_exception_handler(); + if (!is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) { + $debugHandler = true; + } elseif ($debugHandler = $phpHandler[0]->setExceptionHandler($renderException)) { + $phpHandler[0]->setExceptionHandler($debugHandler); + } + } + $this->configureIO($input, $output); try { @@ -125,11 +146,7 @@ public function run(InputInterface $input = null, OutputInterface $output = null throw $e; } - if ($output instanceof ConsoleOutputInterface) { - $this->renderException($e, $output->getErrorOutput()); - } else { - $this->renderException($e, $output); - } + $renderException($e); $exitCode = $e->getCode(); if (is_numeric($exitCode)) { @@ -140,6 +157,12 @@ public function run(InputInterface $input = null, OutputInterface $output = null } else { $exitCode = 1; } + } finally { + if (!$phpHandler) { + restore_exception_handler(); + } elseif (!$debugHandler) { + $phpHandler[0]->setExceptionHandler(null); + } } if ($this->autoExit) { diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php index 967d6bb41cfee..e40d668d86fd3 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php @@ -14,6 +14,7 @@ use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\ExpressionLanguage; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -148,6 +149,10 @@ private function getDefinitionId(string $id): ?string private function getExpressionLanguage() { if (null === $this->expressionLanguage) { + if (!class_exists(ExpressionLanguage::class)) { + throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); + } + $providers = $this->container->getExpressionLanguageProviders(); $this->expressionLanguage = new ExpressionLanguage(null, $providers, function ($arg) { if ('""' === substr_replace($arg, '', 1, -1)) { diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php index 8fdbd8e2b5681..517085eb75fce 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -13,6 +13,7 @@ use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Reference; /** @@ -23,6 +24,7 @@ class InlineServiceDefinitionsPass extends AbstractRecursivePass implements RepeatablePassInterface { private $repeatedPass; + private $cloningIds = array(); /** * {@inheritdoc} @@ -41,17 +43,43 @@ protected function processValue($value, $isRoot = false) // Reference found in ArgumentInterface::getValues() are not inlineable return $value; } - if ($value instanceof Reference && $this->container->hasDefinition($id = (string) $value)) { - $definition = $this->container->getDefinition($id); - if ($this->isInlineableDefinition($id, $definition, $this->container->getCompiler()->getServiceReferenceGraph())) { - $this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId)); - - return $definition->isShared() ? $definition : clone $definition; + if ($value instanceof Definition && $this->cloningIds) { + if ($value->isShared()) { + return $value; } + $value = clone $value; + } + + if (!$value instanceof Reference || !$this->container->hasDefinition($id = (string) $value)) { + return parent::processValue($value, $isRoot); + } + + $definition = $this->container->getDefinition($id); + + if (!$this->isInlineableDefinition($id, $definition, $this->container->getCompiler()->getServiceReferenceGraph())) { + return $value; } - return parent::processValue($value, $isRoot); + $this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId)); + + if ($definition->isShared()) { + return $definition; + } + + if (isset($this->cloningIds[$id])) { + $ids = array_keys($this->cloningIds); + $ids[] = $id; + + throw new ServiceCircularReferenceException($id, array_slice($ids, array_search($id, $ids))); + } + + $this->cloningIds[$id] = true; + try { + return $this->processValue($definition); + } finally { + unset($this->cloningIds[$id]); + } } /** diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 7ee66c05f015b..42e03f4785b33 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -467,6 +467,8 @@ public function getCompiler() */ public function set($id, $service) { + $id = (string) $id; + if ($this->isCompiled() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) { // setting a synthetic service on a compiled container is alright throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a compiled container is not allowed.', $id)); @@ -484,7 +486,7 @@ public function set($id, $service) */ public function removeDefinition($id) { - if (isset($this->definitions[$id])) { + if (isset($this->definitions[$id = (string) $id])) { unset($this->definitions[$id]); $this->removedIds[$id] = true; } @@ -499,6 +501,8 @@ public function removeDefinition($id) */ public function has($id) { + $id = (string) $id; + return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id); } @@ -519,6 +523,10 @@ public function has($id) */ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { + if ($this->isCompiled() && isset($this->removedIds[$id = (string) $id]) && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) { + return parent::get($id); + } + return $this->doGet($id, $invalidBehavior); } @@ -715,6 +723,12 @@ public function compile(bool $resolveEnvPlaceholders = false) } parent::compile(); + + foreach ($this->definitions + $this->aliasDefinitions as $id => $definition) { + if (!$definition->isPublic() || $definition->isPrivate()) { + $this->removedIds[$id] = true; + } + } } /** @@ -769,6 +783,8 @@ public function setAliases(array $aliases) */ public function setAlias($alias, $id) { + $alias = (string) $alias; + if (is_string($id)) { $id = new Alias($id); } elseif (!$id instanceof Alias) { @@ -791,7 +807,7 @@ public function setAlias($alias, $id) */ public function removeAlias($alias) { - if (isset($this->aliasDefinitions[$alias])) { + if (isset($this->aliasDefinitions[$alias = (string) $alias])) { unset($this->aliasDefinitions[$alias]); $this->removedIds[$alias] = true; } @@ -806,7 +822,7 @@ public function removeAlias($alias) */ public function hasAlias($id) { - return isset($this->aliasDefinitions[$id]); + return isset($this->aliasDefinitions[$id = (string) $id]); } /** @@ -830,6 +846,8 @@ public function getAliases() */ public function getAlias($id) { + $id = (string) $id; + if (!isset($this->aliasDefinitions[$id])) { throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id)); } @@ -918,6 +936,8 @@ public function setDefinition($id, Definition $definition) throw new BadMethodCallException('Adding definition to a compiled container is not allowed'); } + $id = (string) $id; + unset($this->aliasDefinitions[$id], $this->removedIds[$id]); return $this->definitions[$id] = $definition; @@ -932,7 +952,7 @@ public function setDefinition($id, Definition $definition) */ public function hasDefinition($id) { - return isset($this->definitions[$id]); + return isset($this->definitions[(string) $id]); } /** @@ -946,6 +966,8 @@ public function hasDefinition($id) */ public function getDefinition($id) { + $id = (string) $id; + if (!isset($this->definitions[$id])) { throw new ServiceNotFoundException($id); } @@ -966,6 +988,8 @@ public function getDefinition($id) */ public function findDefinition($id) { + $id = (string) $id; + while (isset($this->aliasDefinitions[$id])) { $id = (string) $this->aliasDefinitions[$id]; } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index f6204169bd96f..b4416b849f3bc 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -224,12 +224,16 @@ public function dump(array $options = array()) {$namespaceLine} // This file has been auto-generated by the Symfony Dependency Injection Component for internal use. -if (!class_exists(\\Container{$hash}\\{$options['class']}::class, false)) { - require __DIR__.'/Container{$hash}/{$options['class']}.php'; +if (\\class_exists(\\Container{$hash}\\{$options['class']}::class, false)) { + // no-op +} elseif (!include __DIR__.'/Container{$hash}/{$options['class']}.php') { + touch(__DIR__.'/Container{$hash}.legacy'); + + return; } -if (!class_exists({$options['class']}::class, false)) { - class_alias(\\Container{$hash}\\{$options['class']}::class, {$options['class']}::class, false); +if (!\\class_exists({$options['class']}::class, false)) { + \\class_alias(\\Container{$hash}\\{$options['class']}::class, {$options['class']}::class, false); } return new \\Container{$hash}\\{$options['class']}(); @@ -424,13 +428,13 @@ private function addServiceInclude(string $cId, Definition $definition, \SplObje } foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) { - $code .= sprintf(" require_once %s;\n", $file); + $code .= sprintf(" include_once %s;\n", $file); } } foreach ($inlinedDefinitions as $def) { if ($file = $def->getFile()) { - $code .= sprintf(" require_once %s;\n", $this->dumpValue($file)); + $code .= sprintf(" include_once %s;\n", $this->dumpValue($file)); } } @@ -472,7 +476,7 @@ private function addServiceInlinedDefinitions(string $id, Definition $definition // $b = new ServiceB(); // $a = new ServiceA(ServiceB $b); // $b->setServiceA(ServiceA $a); - if ($this->hasReference($id, array($def->getArguments(), $def->getFactory()))) { + if (isset($inlinedDefinition[$definition]) && $this->hasReference($id, array($def->getArguments(), $def->getFactory()))) { throw new ServiceCircularReferenceException($id, array($id)); } @@ -874,7 +878,11 @@ class $class extends $baseClass { private \$parameters; private \$targetDirs = array(); - private \$privates = array(); + + /*{$this->docStar} + * @internal but protected for BC on cache:clear + */ + protected \$privates = array(); public function __construct() { @@ -1058,7 +1066,7 @@ private function addAliases(): string return $code." );\n"; } - private function addInlineRequires() :string + private function addInlineRequires(): string { if (!$this->hotPathTag || !$this->inlineRequires) { return ''; @@ -1082,7 +1090,7 @@ private function addInlineRequires() :string foreach ($lineage as $file) { if (!isset($this->inlinedRequires[$file])) { $this->inlinedRequires[$file] = true; - $code .= sprintf(" require_once %s;\n", $file); + $code .= sprintf(" include_once %s;\n", $file); } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php index 55809e517852b..42cccaa186659 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php @@ -56,10 +56,25 @@ public function registerClasses(Definition $prototype, $namespace, $resource, $e $classes = $this->findClasses($namespace, $resource, $exclude); // prepare for deep cloning - $prototype = serialize($prototype); + $serializedPrototype = serialize($prototype); + $interfaces = array(); + $singlyImplemented = array(); foreach ($classes as $class) { - $this->setDefinition($class, unserialize($prototype)); + if (interface_exists($class, false)) { + $interfaces[] = $class; + } else { + $this->setDefinition($class, unserialize($serializedPrototype)); + foreach (class_implements($class, false) as $interface) { + $singlyImplemented[$interface] = isset($singlyImplemented[$interface]) ? false : $class; + } + } + } + foreach ($interfaces as $interface) { + if (!empty($singlyImplemented[$interface])) { + $this->container->setAlias($interface, $singlyImplemented[$interface]) + ->setPublic(false); + } } } @@ -129,7 +144,7 @@ private function findClasses($namespace, $pattern, $excludePattern) throw new InvalidArgumentException(sprintf('Expected to find class "%s" in file "%s" while importing services from resource "%s", but it was not found! Check the namespace prefix used with the resource.', $class, $path, $pattern)); } - if ($r->isInstantiable()) { + if ($r->isInstantiable() || $r->isInterface()) { $classes[] = $class; } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php index 9630cca14f015..bb60af4d19e64 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php @@ -111,6 +111,60 @@ public function testProcessInlinesMixedServicesLoop() $this->assertEquals($container->getDefinition('foo')->getArgument(0), $container->getDefinition('bar')); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + * @expectedExceptionMessage Circular reference detected for service "bar", path: "bar -> foo -> bar". + */ + public function testProcessThrowsOnNonSharedLoops() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->addArgument(new Reference('bar')) + ->setShared(false) + ; + $container + ->register('bar') + ->setShared(false) + ->addMethodCall('setFoo', array(new Reference('foo'))) + ; + + $this->process($container); + } + + public function testProcessNestedNonSharedServices() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->addArgument(new Reference('bar1')) + ->addArgument(new Reference('bar2')) + ; + $container + ->register('bar1') + ->setShared(false) + ->addArgument(new Reference('baz')) + ; + $container + ->register('bar2') + ->setShared(false) + ->addArgument(new Reference('baz')) + ; + $container + ->register('baz') + ->setShared(false) + ; + + $this->process($container); + + $baz1 = $container->getDefinition('foo')->getArgument(0)->getArgument(0); + $baz2 = $container->getDefinition('foo')->getArgument(1)->getArgument(0); + + $this->assertEquals($container->getDefinition('baz'), $baz1); + $this->assertEquals($container->getDefinition('baz'), $baz2); + $this->assertNotSame($baz1, $baz2); + } + public function testProcessInlinesIfMultipleReferencesButAllFromTheSameDefinition() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 1a52ebf5f9b22..4942121e188e3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -733,6 +733,7 @@ public function testEnvInId() PsrContainerInterface::class => true, ContainerInterface::class => true, 'baz_%env(BAR)%' => true, + 'bar_%env(BAR)%' => true, ); $this->assertSame($expected, $container->getRemovedIds()); @@ -1237,6 +1238,9 @@ public function testAlmostCircular($visibility) $this->assertSame($foo2, $foo2->bar->foobar->foo); $this->assertSame(array(), (array) $container->get('foobar4')); + + $foo5 = $container->get('foo5'); + $this->assertSame($foo5, $foo5->bar->foo); } public function provideAlmostCircular() @@ -1306,6 +1310,24 @@ public function testArgumentsHaveHigherPriorityThanBindings() $this->assertSame('via-argument', $container->get('foo')->class1->identifier); $this->assertSame('via-bindings', $container->get('foo')->class2->identifier); } + + public function testIdCanBeAnObjectAsLongAsItCanBeCastToString() + { + $id = new Reference('another_service'); + $aliasId = new Reference('alias_id'); + + $container = new ContainerBuilder(); + $container->set($id, new \stdClass()); + $container->setAlias($aliasId, 'another_service'); + + $this->assertTrue($container->has('another_service')); + $this->assertTrue($container->has($id)); + $this->assertTrue($container->hasAlias('alias_id')); + $this->assertTrue($container->hasAlias($aliasId)); + + $container->removeAlias($aliasId); + $container->removeDefinition($id); + } } class FooClass diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 8dadbb8fbfd01..8380cd02bcda7 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -781,6 +781,9 @@ public function testAlmostCircular($visibility) $this->assertSame($foo2, $foo2->bar->foobar->foo); $this->assertSame(array(), (array) $container->get('foobar4')); + + $foo5 = $container->get('foo5'); + $this->assertSame($foo5, $foo5->bar->foo); } public function provideAlmostCircular() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/Foo.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/Foo.php index ee533fecd9019..b6690a8d2680a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/Foo.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/Foo.php @@ -2,13 +2,13 @@ namespace Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype; -class Foo +class Foo implements FooInterface, Sub\BarInterface { public function __construct($bar = null) { } - function setFoo(self $foo) + public function setFoo(self $foo) { } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/FooInterface.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/FooInterface.php new file mode 100644 index 0000000000000..1855dcfc59e0e --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/Prototype/FooInterface.php @@ -0,0 +1,7 @@ +register('foobar4', 'stdClass')->setPublic(true) ->addArgument(new Reference('foo4')); +// loop on the constructor of a setter-injected dep with property + +$container->register('foo5', 'stdClass')->setPublic(true) + ->setProperty('bar', new Reference('bar5')); + +$container->register('bar5', 'stdClass')->setPublic($public) + ->addArgument(new Reference('foo5')) + ->setProperty('foo', new Reference('foo5')); + return $container; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php index cbc17d74df3b7..a04d80affcec1 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php @@ -20,7 +20,11 @@ class Container extends \Symfony\Component\DependencyInjection\Dump\AbstractCont { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php index bb312436dc837..f25c59b81596f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php index ef88c481583af..31c4475ec7dab 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php index 4a1fbb901c313..9e193f5c9b0ee 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php index d17073ae0b2ed..8c90280d272a2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php index 80635ad02dbb0..673c9d54bbeca 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php index baa8a5eeb207e..090a77dd3c2c3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php index 700275f1e6b96..2089bfe6386c3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php @@ -18,7 +18,11 @@ class Symfony_DI_PhpDumper_Test_EnvParameters extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php index 4ca6299d7435d..1c70b0ee8d06b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php index 0d60c3699cba6..dc7da1c274e5c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt index 579bf285ad643..29f544eaaf3c7 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_as_files.txt @@ -231,7 +231,7 @@ use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; // This file has been auto-generated by the Symfony Dependency Injection Component for internal use. // Returns the public 'method_call1' shared service. -require_once ($this->targetDirs[0].'/Fixtures/includes/foo.php'); +include_once ($this->targetDirs[0].'/Fixtures/includes/foo.php'); $this->services['method_call1'] = $instance = new \Bar\FooClass(); @@ -309,7 +309,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { @@ -483,12 +487,16 @@ class ProjectServiceContainer extends Container // This file has been auto-generated by the Symfony Dependency Injection Component for internal use. -if (!class_exists(\Container%s\ProjectServiceContainer::class, false)) { - require __DIR__.'/Container%s/ProjectServiceContainer.php'; +if (\class_exists(\Container%s\ProjectServiceContainer::class, false)) { + // no-op +} elseif (!include __DIR__.'/Container%s/ProjectServiceContainer.php') { + touch(__DIR__.'/Container%s.legacy'); + + return; } -if (!class_exists(ProjectServiceContainer::class, false)) { - class_alias(\Container%s\ProjectServiceContainer::class, ProjectServiceContainer::class, false); +if (!\class_exists(ProjectServiceContainer::class, false)) { + \class_alias(\Container%s\ProjectServiceContainer::class, ProjectServiceContainer::class, false); } return new \Container%s\ProjectServiceContainer(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php index 72e06b3416f69..a45456163f796 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { @@ -340,7 +344,7 @@ protected function getLazyContextIgnoreInvalidRefService() */ protected function getMethodCall1Service() { - require_once '%path%foo.php'; + include_once '%path%foo.php'; $this->services['method_call1'] = $instance = new \Bar\FooClass(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php index 76af8a5484861..4de6bfc233193 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php @@ -18,7 +18,11 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Private extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { @@ -28,6 +32,7 @@ public function __construct() 'bar3' => 'getBar3Service', 'foo' => 'getFooService', 'foo2' => 'getFoo2Service', + 'foo5' => 'getFoo5Service', 'foobar4' => 'getFoobar4Service', ); @@ -56,6 +61,7 @@ public function getRemovedIds() 'Psr\\Container\\ContainerInterface' => true, 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, 'bar' => true, + 'bar5' => true, 'foo4' => true, 'foobar' => true, 'foobar2' => true, @@ -125,6 +131,24 @@ protected function getFoo2Service() return $this->services['foo2'] = new \FooCircular($a); } + /** + * Gets the public 'foo5' shared service. + * + * @return \stdClass + */ + protected function getFoo5Service() + { + $this->services['foo5'] = $instance = new \stdClass(); + + $a = new \stdClass(($this->services['foo5'] ?? $this->getFoo5Service())); + + $a->foo = $instance; + + $instance->bar = $a; + + return $instance; + } + /** * Gets the public 'foobar4' shared service. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php index a552a22b43358..79a0c11cc15c3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_public.php @@ -18,7 +18,11 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Public extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { @@ -26,9 +30,11 @@ public function __construct() $this->methodMap = array( 'bar' => 'getBarService', 'bar3' => 'getBar3Service', + 'bar5' => 'getBar5Service', 'foo' => 'getFooService', 'foo2' => 'getFoo2Service', 'foo4' => 'getFoo4Service', + 'foo5' => 'getFoo5Service', 'foobar' => 'getFoobarService', 'foobar2' => 'getFoobar2Service', 'foobar3' => 'getFoobar3Service', @@ -93,6 +99,26 @@ protected function getBar3Service() return $instance; } + /** + * Gets the public 'bar5' shared service. + * + * @return \stdClass + */ + protected function getBar5Service() + { + $a = ($this->services['foo5'] ?? $this->getFoo5Service()); + + if (isset($this->services['bar5'])) { + return $this->services['bar5']; + } + + $this->services['bar5'] = $instance = new \stdClass($a); + + $instance->foo = $a; + + return $instance; + } + /** * Gets the public 'foo' shared service. * @@ -139,6 +165,20 @@ protected function getFoo4Service() return $instance; } + /** + * Gets the public 'foo5' shared service. + * + * @return \stdClass + */ + protected function getFoo5Service() + { + $this->services['foo5'] = $instance = new \stdClass(); + + $instance->bar = ($this->services['bar5'] ?? $this->getBar5Service()); + + return $instance; + } + /** * Gets the public 'foobar' shared service. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_array_params.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_array_params.php index cd7bc61ceb015..0e4e9ea239ee6 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_array_params.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_array_params.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_base64_env.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_base64_env.php index 83aee3007b6fe..8af802f70dab3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_base64_env.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_base64_env.php @@ -18,7 +18,11 @@ class Symfony_DI_PhpDumper_Test_Base64Parameters extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php index b6afb3cf15580..4100dcdd2b914 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_env_in_id.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php index 05bae361195d4..a0092b0160f9a 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_requires.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { @@ -37,10 +41,10 @@ public function __construct() $this->aliases = array(); - require_once $this->targetDirs[1].'/includes/HotPath/I1.php'; - require_once $this->targetDirs[1].'/includes/HotPath/P1.php'; - require_once $this->targetDirs[1].'/includes/HotPath/T1.php'; - require_once $this->targetDirs[1].'/includes/HotPath/C1.php'; + include_once $this->targetDirs[1].'/includes/HotPath/I1.php'; + include_once $this->targetDirs[1].'/includes/HotPath/P1.php'; + include_once $this->targetDirs[1].'/includes/HotPath/T1.php'; + include_once $this->targetDirs[1].'/includes/HotPath/C1.php'; } public function reset() @@ -95,8 +99,8 @@ protected function getC1Service() */ protected function getC2Service() { - require_once $this->targetDirs[1].'/includes/HotPath/C3.php'; - require_once $this->targetDirs[1].'/includes/HotPath/C2.php'; + include_once $this->targetDirs[1].'/includes/HotPath/C3.php'; + include_once $this->targetDirs[1].'/includes/HotPath/C2.php'; return $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2(new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3()); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php index b8aba31b1ea8b..59bbce3a995c4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php index 404944a3369b5..86315e2ebaffc 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_frozen.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php index efaa0fb1d9839..5caf9104dd34d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_private_in_expression.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php index 61b0b7294799f..012a36023b0f8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_rot13_env.php @@ -18,7 +18,11 @@ class Symfony_DI_PhpDumper_Test_Rot13Parameters extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php index 934cad86b5feb..1c223c3e65b96 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php @@ -18,7 +18,11 @@ class ProjectServiceContainer extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php index 446d2ae482e77..0f5090c80bebe 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_uninitialized_ref.php @@ -18,7 +18,11 @@ class Symfony_DI_PhpDumper_Test_Uninitialized_Reference extends Container { private $parameters; private $targetDirs = array(); - private $privates = array(); + + /** + * @internal but protected for BC on cache:clear + */ + protected $privates = array(); public function __construct() { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php index 6544145c4e0e0..4cba7443e8ce9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php @@ -11,10 +11,12 @@ namespace Symfony\Component\DependencyInjection\Tests\Loader; +use Psr\Container\ContainerInterface as PsrContainerInterface; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Loader\LoaderResolver; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Loader\FileLoader; @@ -25,7 +27,10 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\OtherDir\AnotherSub\DeeperBaz; use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\OtherDir\Baz; +use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo; +use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\FooInterface; use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\Bar; +use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\BarInterface; class FileLoaderTest extends TestCase { @@ -91,6 +96,14 @@ public function testRegisterClasses() array('service_container', Bar::class), array_keys($container->getDefinitions()) ); + $this->assertEquals( + array( + PsrContainerInterface::class, + ContainerInterface::class, + BarInterface::class, + ), + array_keys($container->getAliases()) + ); } public function testRegisterClassesWithExclude() @@ -111,6 +124,43 @@ public function testRegisterClassesWithExclude() $this->assertTrue($container->has(Baz::class)); $this->assertFalse($container->has(Foo::class)); $this->assertFalse($container->has(DeeperBaz::class)); + + $this->assertEquals( + array( + PsrContainerInterface::class, + ContainerInterface::class, + BarInterface::class, + ), + array_keys($container->getAliases()) + ); + } + + public function testNestedRegisterClasses() + { + $container = new ContainerBuilder(); + $loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath.'/Fixtures')); + + $prototype = new Definition(); + $prototype->setPublic(true)->setPrivate(true); + $loader->registerClasses($prototype, 'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\\', 'Prototype/*'); + + $this->assertTrue($container->has(Bar::class)); + $this->assertTrue($container->has(Baz::class)); + $this->assertTrue($container->has(Foo::class)); + + $this->assertEquals( + array( + PsrContainerInterface::class, + ContainerInterface::class, + FooInterface::class, + ), + array_keys($container->getAliases()) + ); + + $alias = $container->getAlias(FooInterface::class); + $this->assertSame(Foo::class, (string) $alias); + $this->assertFalse($alias->isPublic()); + $this->assertFalse($alias->isPrivate()); } /** diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json index 7ce9f496cd0fe..cf2d7ecbd306e 100644 --- a/src/Symfony/Component/Form/composer.json +++ b/src/Symfony/Component/Form/composer.json @@ -33,7 +33,7 @@ "symfony/http-kernel": "~3.4|~4.0", "symfony/security-csrf": "~3.4|~4.0", "symfony/translation": "~3.4|~4.0", - "symfony/var-dumper": "~3.4-beta3|~4.0-beta3" + "symfony/var-dumper": "~3.4|~4.0" }, "conflict": { "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", diff --git a/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php b/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php index 7b0aa4b5a226e..6f25f0bf23134 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ContainerControllerResolver.php @@ -92,7 +92,7 @@ protected function instantiateController($class) } catch (\ArgumentCountError $e) { } - if ($this->container instanceof Container && in_array($class, $this->container->getRemovedIds(), true)) { + if ($this->container instanceof Container && isset($this->container->getRemovedIds()[$class])) { throw new \LogicException(sprintf('Controller "%s" cannot be fetched from the container because it is private. Did you forget to tag the service with "controller.service_arguments"?', $class), 0, $e); } diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php index 5ec4e5f23f9e0..e0297ea395967 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php @@ -166,7 +166,9 @@ public function collect(Request $request, Response $response, \Exception $except public function reset() { - $this->stopwatch->reset(); + if ($this->stopwatch) { + $this->stopwatch->reset(); + } $this->data = array(); $this->dataCount = 0; $this->isCollected = false; diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 2a6d8a4fd17e8..39efec369d7f8 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -63,11 +63,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl private $requestStackSize = 0; private $resetServices = false; - const VERSION = '4.0.0'; - const VERSION_ID = 40000; + const VERSION = '4.0.1'; + const VERSION_ID = 40001; const MAJOR_VERSION = 4; const MINOR_VERSION = 0; - const RELEASE_VERSION = 0; + const RELEASE_VERSION = 1; const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '07/2018'; @@ -356,7 +356,7 @@ public function getContainer() */ public function setAnnotatedClassCache(array $annotatedClasses) { - file_put_contents(($this->warmupDir ?: $this->getCacheDir()).'/annotations.map', sprintf('warmupDir ?: $this->getCacheDir()).'/annotations.map', sprintf('warmupDir ?: $this->getCacheDir(); $cache = new ConfigCache($cacheDir.'/'.$class.'.php', $this->debug); if ($fresh = $cache->isFresh()) { - $this->container = require $cache->getPath(); + // Silence E_WARNING to ignore "include" failures - don't use "@" to prevent silencing fatal errors + $errorLevel = error_reporting(\E_ALL ^ \E_WARNING); + try { + $this->container = include $cache->getPath(); + } finally { + error_reporting($errorLevel); + } $fresh = \is_object($this->container); } if (!$fresh) { @@ -460,7 +466,7 @@ protected function initializeContainer() $collectedLogs = array(); $previousHandler = set_error_handler(function ($type, $message, $file, $line) use (&$collectedLogs, &$previousHandler) { if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) { - return $previousHandler ? $previousHandler($type, $message, $file, $line) : false; + return $previousHandler ? $previousHandler($type & ~E_WARNING, $message, $file, $line) : E_WARNING === $type; } if (isset($collectedLogs[$message])) { @@ -487,23 +493,27 @@ protected function initializeContainer() 'count' => 1, ); }); + } else { + $errorLevel = error_reporting(\E_ALL ^ \E_WARNING); } try { $container = null; $container = $this->buildContainer(); $container->compile(); + + $oldContainer = file_exists($cache->getPath()) && is_object($oldContainer = include $cache->getPath()) ? new \ReflectionClass($oldContainer) : false; } finally { if ($this->debug) { restore_error_handler(); file_put_contents($cacheDir.'/'.$class.'Deprecations.log', serialize(array_values($collectedLogs))); file_put_contents($cacheDir.'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : ''); + } else { + error_reporting($errorLevel); } } - $oldContainer = file_exists($cache->getPath()) && is_object($oldContainer = @include $cache->getPath()) ? new \ReflectionClass($oldContainer) : false; - $this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass()); $this->container = require $cache->getPath(); } @@ -519,13 +529,13 @@ protected function initializeContainer() // old container files are not removed immediately, // but on a next dump of the container. $oldContainerDir = dirname($oldContainer->getFileName()); - foreach (glob(dirname($oldContainerDir).'/*.legacyContainer') as $legacyContainer) { - if ($oldContainerDir.'.legacyContainer' !== $legacyContainer && @unlink($legacyContainer)) { + foreach (glob(dirname($oldContainerDir).'/*.legacy') as $legacyContainer) { + if ($oldContainerDir.'.legacy' !== $legacyContainer && @unlink($legacyContainer)) { (new Filesystem())->remove(substr($legacyContainer, 0, -16)); } } - touch($oldContainerDir.'.legacyContainer'); + touch($oldContainerDir.'.legacy'); } if ($this->container->has('cache_warmer')) { diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php index b3fa081a63c88..8624269be80e5 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ContainerControllerResolverTest.php @@ -130,7 +130,7 @@ public function testNonConstructController() $container->expects($this->atLeastOnce()) ->method('getRemovedIds') ->with() - ->will($this->returnValue(array(ImpossibleConstructController::class))) + ->will($this->returnValue(array(ImpossibleConstructController::class => true))) ; $resolver = $this->createControllerResolver(null, $container); diff --git a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php index 955b1cf83ba57..8461a180c8c31 100644 --- a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php @@ -521,7 +521,7 @@ public function testKernelReset() $this->assertTrue(get_class($kernel->getContainer()) !== $containerClass); $this->assertFileExists($containerFile); - $this->assertFileExists(dirname($containerFile).'.legacyContainer'); + $this->assertFileExists(dirname($containerFile).'.legacy'); } public function testKernelPass() diff --git a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php index 888f68a72b635..874a7e290b776 100644 --- a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php +++ b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php @@ -257,7 +257,7 @@ class NumberFormatter * @throws MethodArgumentValueNotImplementedException When the $style is not supported * @throws MethodArgumentNotImplementedException When the pattern value is different than null */ - public function __construct(?string $locale = 'en', string $style = null, $pattern = null) + public function __construct(?string $locale = 'en', int $style = null, $pattern = null) { if ('en' !== $locale && null !== $locale) { throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported'); diff --git a/src/Symfony/Component/Security/Csrf/composer.json b/src/Symfony/Component/Security/Csrf/composer.json index 4e03fc7db0fb9..0521b86e83e1a 100644 --- a/src/Symfony/Component/Security/Csrf/composer.json +++ b/src/Symfony/Component/Security/Csrf/composer.json @@ -20,10 +20,10 @@ "symfony/security-core": "~3.4|~4.0" }, "require-dev": { - "symfony/http-foundation": "~3.4-beta5|~4.0-beta5" + "symfony/http-foundation": "~3.4|~4.0" }, "conflict": { - "symfony/http-foundation": "<3.4-beta5|~4.0,<4.0-beta5" + "symfony/http-foundation": "<3.4" }, "suggest": { "symfony/http-foundation": "For using the class SessionTokenStorage." diff --git a/src/Symfony/Component/Security/composer.json b/src/Symfony/Component/Security/composer.json index a3a9717665373..a7299f616d47b 100644 --- a/src/Symfony/Component/Security/composer.json +++ b/src/Symfony/Component/Security/composer.json @@ -18,7 +18,7 @@ "require": { "php": "^7.1.3", "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/http-foundation": "~3.4-beta5|~4.0-beta5", + "symfony/http-foundation": "~3.4|~4.0", "symfony/http-kernel": "~3.4|~4.0", "symfony/property-access": "~3.4|~4.0" }, diff --git a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php index be05f8f8a0af0..ce77d9ba843a4 100644 --- a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php @@ -45,7 +45,7 @@ public function validate($value, Constraint $constraint) if (!$this->getExpressionLanguage()->evaluate($constraint->expression, $variables)) { $this->context->buildViolation($constraint->message) - ->setParameter('{{ value }}', $this->formatValue($value)) + ->setParameter('{{ value }}', $this->formatValue($value, self::OBJECT_TO_STRING)) ->setCode(Expression::EXPRESSION_FAILED_ERROR) ->addViolation(); } diff --git a/src/Symfony/Component/Validator/Constraints/ValidValidator.php b/src/Symfony/Component/Validator/Constraints/ValidValidator.php index b2f1f1c5a06b9..be5fbc12660ba 100644 --- a/src/Symfony/Component/Validator/Constraints/ValidValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ValidValidator.php @@ -26,6 +26,10 @@ public function validate($value, Constraint $constraint) throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Valid'); } + if (null === $value) { + return; + } + $this->context ->getValidator() ->inContext($this->context) diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php index e4316243e5580..07f17d648aa39 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Validator\Constraints\ExpressionValidator; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; use Symfony\Component\Validator\Tests\Fixtures\Entity; +use Symfony\Component\Validator\Tests\Fixtures\ToString; class ExpressionValidatorTest extends ConstraintValidatorTestCase { @@ -87,6 +88,40 @@ public function testFailingExpressionAtObjectLevel() ->assertRaised(); } + public function testSucceedingExpressionAtObjectLevelWithToString() + { + $constraint = new Expression('this.data == 1'); + + $object = new ToString(); + $object->data = '1'; + + $this->setObject($object); + + $this->validator->validate($object, $constraint); + + $this->assertNoViolation(); + } + + public function testFailingExpressionAtObjectLevelWithToString() + { + $constraint = new Expression(array( + 'expression' => 'this.data == 1', + 'message' => 'myMessage', + )); + + $object = new ToString(); + $object->data = '2'; + + $this->setObject($object); + + $this->validator->validate($object, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', 'toString') + ->setCode(Expression::EXPRESSION_FAILED_ERROR) + ->assertRaised(); + } + public function testSucceedingExpressionAtPropertyLevel() { $constraint = new Expression('value == this.data'); diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ValidValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ValidValidatorTest.php index f95650d359b3b..c4ccf1551f2a0 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ValidValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ValidValidatorTest.php @@ -20,6 +20,18 @@ public function testPropertyPathsArePassedToNestedContexts() $this->assertSame('fooBar.fooBarBaz.foo', $violations->get(0)->getPropertyPath()); } + public function testNullValues() + { + $validatorBuilder = new ValidatorBuilder(); + $validator = $validatorBuilder->enableAnnotationMapping()->getValidator(); + + $foo = new Foo(); + $foo->fooBar = null; + $violations = $validator->validate($foo, null, array('nested')); + + $this->assertCount(0, $violations); + } + protected function createValidator() { return new ValidValidator(); diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/ToString.php b/src/Symfony/Component/Validator/Tests/Fixtures/ToString.php new file mode 100644 index 0000000000000..714fdb9e98f5f --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Fixtures/ToString.php @@ -0,0 +1,22 @@ + + * + * 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 ToString +{ + public $data; + + public function __toString() + { + return 'toString'; + } +} diff --git a/src/Symfony/Component/Workflow/Definition.php b/src/Symfony/Component/Workflow/Definition.php index a8bc0806e2906..1469881de59ca 100644 --- a/src/Symfony/Component/Workflow/Definition.php +++ b/src/Symfony/Component/Workflow/Definition.php @@ -82,7 +82,7 @@ private function setInitialPlace(string $place = null) private function addPlace(string $place) { - if (!preg_match('{^[\w\d_-]+$}', $place)) { + if (!preg_match('{^[\w_-]+$}', $place)) { throw new InvalidArgumentException(sprintf('The place "%s" contains invalid characters.', $place)); } diff --git a/src/Symfony/Component/Workflow/DefinitionBuilder.php b/src/Symfony/Component/Workflow/DefinitionBuilder.php index f663c16d1a255..16d66286efb4c 100644 --- a/src/Symfony/Component/Workflow/DefinitionBuilder.php +++ b/src/Symfony/Component/Workflow/DefinitionBuilder.php @@ -77,7 +77,7 @@ public function setInitialPlace($place) */ public function addPlace($place) { - if (!preg_match('{^[\w\d_-]+$}', $place)) { + if (!preg_match('{^[\w_-]+$}', $place)) { throw new InvalidArgumentException(sprintf('The place "%s" contains invalid characters.', $place)); } diff --git a/src/Symfony/Component/Workflow/Transition.php b/src/Symfony/Component/Workflow/Transition.php index b9f52d8e10910..9b482ac843cb2 100644 --- a/src/Symfony/Component/Workflow/Transition.php +++ b/src/Symfony/Component/Workflow/Transition.php @@ -30,7 +30,7 @@ class Transition */ public function __construct(string $name, $froms, $tos) { - if (!preg_match('{^[\w\d_-]+$}', $name)) { + if (!preg_match('{^[\w_-]+$}', $name)) { throw new InvalidArgumentException(sprintf('The transition "%s" contains invalid characters.', $name)); } diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index cc1487002eb73..e77fa8c5fa0cb 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -585,21 +585,10 @@ private function getNextEmbedBlock(int $indentation = null, bool $inSequence = f continue; } - // we ignore "comment" lines only when we are not inside a scalar block - if (empty($blockScalarIndentations) && $this->isCurrentLineComment()) { - // remember ignored comment lines (they are used later in nested - // parser calls to determine real line numbers) - // - // CAUTION: beware to not populate the global property here as it - // will otherwise influence the getRealCurrentLineNb() call here - // for consecutive comment lines and subsequent embedded blocks - $this->locallySkippedLineNumbers[] = $this->getRealCurrentLineNb(); - - continue; - } - if ($indent >= $newIndent) { $data[] = substr($this->currentLine, $newIndent); + } elseif ($this->isCurrentLineComment()) { + $data[] = $this->currentLine; } elseif (0 == $indent) { $this->moveToPreviousLine(); @@ -695,6 +684,8 @@ private function parseValue(string $value, int $flags, string $context) return Inline::parse($value, $flags, $this->refs); } + $lines = array(); + while ($this->moveToNextLine()) { // unquoted strings end before the first unindented line if (null === $quotation && 0 === $this->getCurrentLineIndentation()) { @@ -703,7 +694,7 @@ private function parseValue(string $value, int $flags, string $context) break; } - $value .= ' '.trim($this->currentLine); + $lines[] = trim($this->currentLine); // quoted string values end with a line that is terminated with the quotation character if ('' !== $this->currentLine && substr($this->currentLine, -1) === $quotation) { @@ -711,6 +702,21 @@ private function parseValue(string $value, int $flags, string $context) } } + for ($i = 0, $linesCount = count($lines), $previousLineBlank = false; $i < $linesCount; ++$i) { + if ('' === $lines[$i]) { + $value .= "\n"; + $previousLineBlank = true; + } elseif ($previousLineBlank) { + $value .= $lines[$i]; + $previousLineBlank = false; + } else { + $value .= ' '.$lines[$i]; + $previousLineBlank = false; + } + } + + Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); + $parsedValue = Inline::parse($value, $flags, $this->refs); if ('mapping' === $context && is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) { diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index b3b1465aaf379..f46d2ce9f749c 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -1433,6 +1433,38 @@ public function testMultiLineQuotedStringWithTrailingBackslash() $this->assertSame(array('foobar' => 'foobar'), $this->parser->parse($yaml)); } + public function testCommentCharactersInMultiLineQuotedStrings() + { + $yaml = << array( + 'foobar' => 'foo #bar', + 'bar' => 'baz', + ), + ); + + $this->assertSame($expected, $this->parser->parse($yaml)); + } + + public function testBlankLinesInQuotedMultiLineString() + { + $yaml = << "foo\nbar", + ); + + $this->assertSame($expected, $this->parser->parse($yaml)); + } + public function testParseMultiLineUnquotedString() { $yaml = << pFad - Phonifier reborn

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

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


    Alternative Proxies:

    Alternative Proxy

    pFad Proxy

    pFad v3 Proxy

    pFad v4 Proxy