diff --git a/UPGRADE-3.4.md b/UPGRADE-3.4.md index e5b6434c37..a06a8cd515 100644 --- a/UPGRADE-3.4.md +++ b/UPGRADE-3.4.md @@ -63,6 +63,12 @@ Debug * Support for stacked errors in the `ErrorHandler` is deprecated and will be removed in Symfony 4.0. +EventDispatcher +--------------- + + * Implementing `TraceableEventDispatcherInterface` without the `reset()` method + is deprecated and will be unsupported in 4.0. + Filesystem ---------- @@ -234,6 +240,9 @@ HttpFoundation * `NativeSessionStorage::setSaveHandler()` now takes an instance of `\SessionHandlerInterface` as argument. Not passing it is deprecated and will throw a `TypeError` in 4.0. + * Using `Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler` with the legacy mongo extension + has been deprecated and will be removed in 4.0. Use it with the mongodb/mongodb package and ext-mongodb instead. + HttpKernel ---------- @@ -270,6 +279,10 @@ HttpKernel * The `Symfony\Component\HttpKernel\Config\EnvParametersResource` class has been deprecated and will be removed in 4.0. + * Implementing `DataCollectorInterface` without a `reset()` method has been deprecated and will be unsupported in 4.0. + + * Implementing `DebugLoggerInterface` without a `clear()` method has been deprecated and will be unsupported in 4.0. + * The `ChainCacheClearer::add()` method has been deprecated and will be removed in 4.0, inject the list of clearers as a constructor argument instead. @@ -295,6 +308,9 @@ Security * Deprecated the HTTP digest authentication: `NonceExpiredException`, `DigestAuthenticationListener` and `DigestAuthenticationEntryPoint` will be removed in 4.0. Use another authentication system like `http_basic` instead. + + * The `GuardAuthenticatorInterface` has been deprecated and will be removed in 4.0. + Use `AuthenticatorInterface` instead. SecurityBundle -------------- @@ -320,11 +336,11 @@ SecurityBundle * Deprecated the HTTP digest authentication: `HttpDigestFactory` will be removed in 4.0. Use another authentication system like `http_basic` instead. - + * Deprecated setting the `switch_user.stateless` option to false when the firewall is `stateless`. Setting it to false will have no effect in 4.0. - * Not configuring explicitly the provider on a firewall is ambiguous when there is more than one registered provider. + * Not configuring explicitly the provider on a firewall is ambiguous when there is more than one registered provider. Using the first configured provider is deprecated since 3.4 and will throw an exception on 4.0. Explicitly configure the provider to use on your firewalls. diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index b70805db77..c3592007f2 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -192,6 +192,8 @@ EventDispatcher * The `ContainerAwareEventDispatcher` class has been removed. Use `EventDispatcher` with closure factories instead. + * The `reset()` method has been added to `TraceableEventDispatcherInterface`. + ExpressionLanguage ------------------ @@ -540,6 +542,9 @@ HttpFoundation * `NativeSessionStorage::setSaveHandler()` now requires an instance of `\SessionHandlerInterface` as argument. + * The `Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler` does not work with the legacy + mongo extension anymore. It requires mongodb/mongodb package and ext-mongodb. + HttpKernel ---------- @@ -611,6 +616,10 @@ HttpKernel * The `Symfony\Component\HttpKernel\Config\EnvParametersResource` class has been removed. + * The `reset()` method has been added to `Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface`. + + * The `clear()` method has been added to `Symfony\Component\HttpKernel\Log\DebugLoggerInterface`. + * The `ChainCacheClearer::add()` method has been removed, inject the list of clearers as a constructor argument instead. @@ -673,6 +682,9 @@ Security `DigestAuthenticationListener` and `DigestAuthenticationEntryPoint` classes have been removed. Use another authentication system like `http_basic` instead. + * The `GuardAuthenticatorInterface` interface has been removed. + Use `AuthenticatorInterface` instead. + SecurityBundle -------------- @@ -693,10 +705,10 @@ SecurityBundle * Removed the HTTP digest authentication system. The `HttpDigestFactory` class has been removed. Use another authentication system like `http_basic` instead. - + * The `switch_user.stateless` option is now always true if the firewall is stateless. - * Not configuring explicitly the provider on a firewall is ambiguous when there is more than one registered provider. + * Not configuring explicitly the provider on a firewall is ambiguous when there is more than one registered provider. The first configured provider is not used anymore and an exception is thrown instead. Explicitly configure the provider to use on your firewalls. diff --git a/composer.json b/composer.json index ec77b4742f..e23be4afa8 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "ext-xml": "*", "doctrine/common": "~2.4", "fig/link-util": "^1.0", - "twig/twig": "~1.34|~2.4", + "twig/twig": "^1.35|^2.4.4", "psr/cache": "~1.0", "psr/container": "^1.0", "psr/link": "^1.0", diff --git a/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php b/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php index 62c6a2381a..bca53ef409 100644 --- a/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php +++ b/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php @@ -28,6 +28,10 @@ class DoctrineDataCollector extends DataCollector private $registry; private $connections; private $managers; + + /** + * @var DebugStack[] + */ private $loggers = array(); public function __construct(ManagerRegistry $registry) @@ -65,6 +69,16 @@ class DoctrineDataCollector extends DataCollector ); } + public function reset() + { + $this->data = array(); + + foreach ($this->loggers as $logger) { + $logger->queries = array(); + $logger->currentQuery = 0; + } + } + public function getManagers() { return $this->data['managers']; diff --git a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php b/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php index 6ae9c469bb..6036636f5c 100644 --- a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php +++ b/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php @@ -11,6 +11,8 @@ namespace Symfony\Bridge\Doctrine\HttpFoundation; +@trigger_error(sprintf('The class %s is deprecated since version 3.4 and will be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler instead.', DbalSessionHandler::class), E_USER_DEPRECATED); + use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver\DriverException; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; @@ -25,6 +27,8 @@ use Doctrine\DBAL\Platforms\SQLServer2008Platform; * @author Fabien Potencier * @author Johannes M. Schmitt * @author Tobias Schultze + * + * @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler instead. */ class DbalSessionHandler implements \SessionHandlerInterface { diff --git a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandlerSchema.php b/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandlerSchema.php index 978373da0c..7af50a6507 100644 --- a/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandlerSchema.php +++ b/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandlerSchema.php @@ -11,12 +11,16 @@ namespace Symfony\Bridge\Doctrine\HttpFoundation; +@trigger_error(sprintf('The class %s is deprecated since version 3.4 and will be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler::createTable instead.', DbalSessionHandlerSchema::class), E_USER_DEPRECATED); + use Doctrine\DBAL\Schema\Schema; /** * DBAL Session Storage Schema. * * @author Johannes M. Schmitt + * + * @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler::createTable instead. */ final class DbalSessionHandlerSchema extends Schema { diff --git a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php index 5d1ff967ad..9239fd31af 100644 --- a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php +++ b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php @@ -12,9 +12,10 @@ namespace Symfony\Bridge\Doctrine; use ProxyManager\Proxy\LazyLoadingInterface; +use Psr\Container\ContainerInterface; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerAwareInterface; -use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface; use Doctrine\Common\Persistence\AbstractManagerRegistry; /** @@ -24,7 +25,21 @@ use Doctrine\Common\Persistence\AbstractManagerRegistry; */ abstract class ManagerRegistry extends AbstractManagerRegistry implements ContainerAwareInterface { - use ContainerAwareTrait; + /** + * @var ContainerInterface + */ + protected $container; + + /** + * @deprecated since version 3.4, to be removed in 4.0 alongside with the ContainerAwareInterface type. + * @final since version 3.4 + */ + public function setContainer(SymfonyContainerInterface $container = null) + { + @trigger_error(sprintf('The "%s()" method is deprecated since version 3.4 and will be removed in 4.0. Inject a PSR-11 container using the constructor instead.', __METHOD__), E_USER_DEPRECATED); + + $this->container = $container; + } /** * {@inheritdoc} diff --git a/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php b/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php index 8cbd2c3eac..cc20869c7c 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php @@ -101,6 +101,20 @@ class DoctrineDataCollectorTest extends TestCase $this->assertTrue($collectedQueries['default'][1]['explainable']); } + public function testReset() + { + $queries = array( + array('sql' => 'SELECT * FROM table1', 'params' => array(), 'types' => array(), 'executionMS' => 1), + ); + $c = $this->createCollector($queries); + $c->collect(new Request(), new Response()); + + $c->reset(); + $c->collect(new Request(), new Response()); + + $this->assertEquals(array('default' => array()), $c->getQueries()); + } + /** * @dataProvider paramProvider */ diff --git a/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php b/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php index 1c9c82bc12..8d46bf9e63 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php @@ -18,6 +18,8 @@ use Symfony\Bridge\Doctrine\HttpFoundation\DbalSessionHandler; * Test class for DbalSessionHandler. * * @author Drak + * + * @group legacy */ class DbalSessionHandlerTest extends TestCase { diff --git a/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php b/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php index fa3037a609..d1596a1968 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php @@ -31,7 +31,7 @@ class ManagerRegistryTest extends TestCase $container = new \LazyServiceProjectServiceContainer(); $registry = new TestManagerRegistry('name', array(), array('defaultManager' => 'foo'), 'defaultConnection', 'defaultManager', 'proxyInterfaceName'); - $registry->setContainer($container); + $registry->setTestContainer($container); $foo = $container->get('foo'); $foo->bar = 123; @@ -46,6 +46,11 @@ class ManagerRegistryTest extends TestCase class TestManagerRegistry extends ManagerRegistry { + public function setTestContainer($container) + { + $this->container = $container; + } + public function getAliasNamespace($alias) { return 'Foo'; diff --git a/src/Symfony/Bridge/Monolog/Logger.php b/src/Symfony/Bridge/Monolog/Logger.php index ec6434fe79..7cd75c5ec1 100644 --- a/src/Symfony/Bridge/Monolog/Logger.php +++ b/src/Symfony/Bridge/Monolog/Logger.php @@ -45,6 +45,16 @@ class Logger extends BaseLogger implements DebugLoggerInterface return 0; } + /** + * {@inheritdoc} + */ + public function clear() + { + if (($logger = $this->getDebugLogger()) && method_exists($logger, 'clear')) { + $logger->clear(); + } + } + /** * Returns a DebugLoggerInterface instance if one is registered with this logger. * diff --git a/src/Symfony/Bridge/Monolog/Processor/DebugProcessor.php b/src/Symfony/Bridge/Monolog/Processor/DebugProcessor.php index 22a4faac5c..8774045192 100644 --- a/src/Symfony/Bridge/Monolog/Processor/DebugProcessor.php +++ b/src/Symfony/Bridge/Monolog/Processor/DebugProcessor.php @@ -55,4 +55,13 @@ class DebugProcessor implements DebugLoggerInterface { return $this->errorCount; } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->records = array(); + $this->errorCount = 0; + } } diff --git a/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php b/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php index 6c04762404..d94b1d66fe 100644 --- a/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php @@ -78,4 +78,17 @@ class LoggerTest extends TestCase $this->assertEquals('test', $record['message']); $this->assertEquals(Logger::INFO, $record['priority']); } + + public function testClear() + { + $handler = new TestHandler(); + $logger = new Logger('test', array($handler)); + $logger->pushProcessor(new DebugProcessor()); + + $logger->addInfo('test'); + $logger->clear(); + + $this->assertEmpty($logger->getLogs()); + $this->assertSame(0, $logger->countErrors()); + } } diff --git a/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php index 4babd06060..d1bcb5ad1a 100644 --- a/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php +++ b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php @@ -44,6 +44,16 @@ class TwigDataCollector extends DataCollector implements LateDataCollectorInterf { } + /** + * {@inheritdoc} + */ + public function reset() + { + $this->profile->reset(); + $this->computed = null; + $this->data = array(); + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 4593db62ed..492f6c2d39 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": "^7.1.3", - "twig/twig": "~1.34|~2.4" + "twig/twig": "^1.35|^2.4.4" }, "require-dev": { "fig/link-util": "^1.0", diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index 1969a4f642..48cc46fa8a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -81,6 +81,7 @@ CHANGELOG and `YamlLintCommand` classes have been marked as final * Added `asset.request_context.base_path` and `asset.request_context.secure` parameters to provide a default request context in case the stack is empty (similar to `router.request_context.*` parameters) + * Display environment variables managed by `Dotenv` in `AboutCommand` 3.3.0 ----- diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php index 1006943a23..801eabb9b6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php @@ -36,7 +36,19 @@ class AboutCommand extends Command */ protected function configure() { - $this->setDescription('Displays information about the current project'); + $this + ->setDescription('Displays information about the current project') + ->setHelp(<<<'EOT' +The %command.name% command displays information about the current Symfony project. + +The PHP section displays important configuration that could affect your application. The values might +be different between web and CLI. + +The Environment section displays the current environment variables managed by Symfony Dotenv. It will not +be shown if no variables were found. The values might be different between web and CLI. +EOT + ) + ; } /** @@ -49,7 +61,7 @@ class AboutCommand extends Command /** @var $kernel KernelInterface */ $kernel = $this->getApplication()->getKernel(); - $io->table(array(), array( + $rows = array( array('Symfony'), new TableSeparator(), array('Version', Kernel::VERSION), @@ -76,7 +88,19 @@ class AboutCommand extends Command array('OPcache', extension_loaded('Zend OPcache') && ini_get('opcache.enable') ? 'true' : 'false'), array('APCu', extension_loaded('apcu') && ini_get('apc.enabled') ? 'true' : 'false'), array('Xdebug', extension_loaded('xdebug') ? 'true' : 'false'), - )); + ); + + if ($dotenv = self::getDotEnvVars()) { + $rows = array_merge($rows, array( + new TableSeparator(), + array('Environment (.env)'), + new TableSeparator(), + ), array_map(function ($value, $name) { + return array($name, $value); + }, $dotenv, array_keys($dotenv))); + } + + $io->table(array(), $rows); } private static function formatPath($path, $baseDir = null) @@ -104,4 +128,16 @@ class AboutCommand extends Command return false !== $date && new \DateTime() > $date->modify('last day of this month 23:59:59'); } + + private static function getDotEnvVars() + { + $vars = array(); + foreach (explode(',', getenv('SYMFONY_DOTENV_VARS')) as $name) { + if ('' !== $name && false !== $value = getenv($name)) { + $vars[$name] = $value; + } + } + + return $vars; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php index 870965201e..11161d46eb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php @@ -26,30 +26,6 @@ abstract class Controller implements ContainerAwareInterface use ContainerAwareTrait; use ControllerTrait; - /** - * Returns true if the service id is defined. - * - * @param string $id The service id - * - * @return bool true if the service id is defined, false otherwise - */ - protected function has($id) - { - return $this->container->has($id); - } - - /** - * Gets a container service by its id. - * - * @param string $id The service id - * - * @return object The service - */ - protected function get($id) - { - return $this->container->get($id); - } - /** * Gets a container configuration parameter by its name. * diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php index b88ad0cc7d..a688abbaba 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php @@ -48,13 +48,7 @@ class ControllerResolver extends ContainerControllerResolver $resolvedController = parent::createController($controller); if (1 === substr_count($controller, ':') && is_array($resolvedController)) { - if ($resolvedController[0] instanceof ContainerAwareInterface) { - $resolvedController[0]->setContainer($this->container); - } - - if ($resolvedController[0] instanceof AbstractController && null !== $previousContainer = $resolvedController[0]->setContainer($this->container)) { - $resolvedController[0]->setContainer($previousContainer); - } + $resolvedController[0] = $this->configureController($resolvedController[0]); } return $resolvedController; @@ -65,9 +59,19 @@ class ControllerResolver extends ContainerControllerResolver */ protected function instantiateController($class) { - $controller = parent::instantiateController($class); + return $this->configureController(parent::instantiateController($class)); + } + private function configureController($controller) + { if ($controller instanceof ContainerAwareInterface) { + // @deprecated switch, to be removed in 4.0 where these classes + // won't implement ContainerAwareInterface anymore + switch (\get_class($controller)) { + case RedirectController::class: + case TemplateController::class: + return $controller; + } $controller->setContainer($this->container); } if ($controller instanceof AbstractController && null !== $previousContainer = $controller->setContainer($this->container)) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php index 77a0288308..b3d668e6f1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php @@ -39,6 +39,34 @@ use Doctrine\Bundle\DoctrineBundle\Registry; */ trait ControllerTrait { + /** + * Returns true if the service id is defined. + * + * @param string $id The service id + * + * @return bool true if the service id is defined, false otherwise + * + * @final since version 3.4 + */ + protected function has($id) + { + return $this->container->has($id); + } + + /** + * Gets a container service by its id. + * + * @param string $id The service id + * + * @return object The service + * + * @final since version 3.4 + */ + protected function get($id) + { + return $this->container->get($id); + } + /** * Generates a URL from the given parameters. * @@ -49,6 +77,8 @@ trait ControllerTrait * @return string The generated URL * * @see UrlGeneratorInterface + * + * @final since version 3.4 */ protected function generateUrl($route, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) { @@ -63,6 +93,8 @@ trait ControllerTrait * @param array $query An array of query parameters * * @return Response A Response instance + * + * @final since version 3.4 */ protected function forward($controller, array $path = array(), array $query = array()) { @@ -81,6 +113,8 @@ trait ControllerTrait * @param int $status The status code to use for the Response * * @return RedirectResponse + * + * @final since version 3.4 */ protected function redirect($url, $status = 302) { @@ -95,6 +129,8 @@ trait ControllerTrait * @param int $status The status code to use for the Response * * @return RedirectResponse + * + * @final since version 3.4 */ protected function redirectToRoute($route, array $parameters = array(), $status = 302) { @@ -110,6 +146,8 @@ trait ControllerTrait * @param array $context Context to pass to serializer when using serializer component * * @return JsonResponse + * + * @final since version 3.4 */ protected function json($data, $status = 200, $headers = array(), $context = array()) { @@ -132,6 +170,8 @@ trait ControllerTrait * @param string $disposition Disposition of response ("attachment" is default, other type is "inline") * * @return BinaryFileResponse + * + * @final since version 3.4 */ protected function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT) { @@ -148,6 +188,8 @@ trait ControllerTrait * @param string $message The message * * @throws \LogicException + * + * @final since version 3.4 */ protected function addFlash($type, $message) { @@ -167,6 +209,8 @@ trait ControllerTrait * @return bool * * @throws \LogicException + * + * @final since version 3.4 */ protected function isGranted($attributes, $subject = null) { @@ -186,6 +230,8 @@ trait ControllerTrait * @param string $message The message passed to the exception * * @throws AccessDeniedException + * + * @final since version 3.4 */ protected function denyAccessUnlessGranted($attributes, $subject = null, $message = 'Access Denied.') { @@ -205,6 +251,8 @@ trait ControllerTrait * @param array $parameters An array of parameters to pass to the view * * @return string The rendered view + * + * @final since version 3.4 */ protected function renderView($view, array $parameters = array()) { @@ -227,14 +275,16 @@ trait ControllerTrait * @param Response $response A response instance * * @return Response A Response instance + * + * @final since version 3.4 */ protected function render($view, array $parameters = array(), Response $response = null) { if ($this->container->has('templating')) { - return $this->container->get('templating')->renderResponse($view, $parameters, $response); - } - - if (!$this->container->has('twig')) { + $content = $this->container->get('templating')->render($view, $parameters); + } elseif ($this->container->has('twig')) { + $content = $this->container->get('twig')->render($view, $parameters); + } else { throw new \LogicException('You can not use the "render" method if the Templating Component or the Twig Bundle are not available.'); } @@ -242,7 +292,7 @@ trait ControllerTrait $response = new Response(); } - $response->setContent($this->container->get('twig')->render($view, $parameters)); + $response->setContent($content); return $response; } @@ -255,6 +305,8 @@ trait ControllerTrait * @param StreamedResponse $response A response instance * * @return StreamedResponse A StreamedResponse instance + * + * @final since version 3.4 */ protected function stream($view, array $parameters = array(), StreamedResponse $response = null) { @@ -294,6 +346,8 @@ trait ControllerTrait * @param \Exception|null $previous The previous exception * * @return NotFoundHttpException + * + * @final since version 3.4 */ protected function createNotFoundException($message = 'Not Found', \Exception $previous = null) { @@ -311,6 +365,8 @@ trait ControllerTrait * @param \Exception|null $previous The previous exception * * @return AccessDeniedException + * + * @final since version 3.4 */ protected function createAccessDeniedException($message = 'Access Denied.', \Exception $previous = null) { @@ -325,6 +381,8 @@ trait ControllerTrait * @param array $options Options for the form * * @return Form + * + * @final since version 3.4 */ protected function createForm($type, $data = null, array $options = array()) { @@ -338,6 +396,8 @@ trait ControllerTrait * @param array $options Options for the form * * @return FormBuilder + * + * @final since version 3.4 */ protected function createFormBuilder($data = null, array $options = array()) { @@ -350,6 +410,8 @@ trait ControllerTrait * @return Registry * * @throws \LogicException If DoctrineBundle is not available + * + * @final since version 3.4 */ protected function getDoctrine() { @@ -368,6 +430,8 @@ trait ControllerTrait * @throws \LogicException If SecurityBundle is not available * * @see TokenInterface::getUser() + * + * @final since version 3.4 */ protected function getUser() { @@ -394,6 +458,8 @@ trait ControllerTrait * @param string $token The actual token sent with the request that should be validated * * @return bool + * + * @final since version 3.4 */ protected function isCsrfTokenValid($id, $token) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php index b2cb1d1acc..6edee88ce5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php @@ -12,7 +12,7 @@ namespace Symfony\Bundle\FrameworkBundle\Controller; use Symfony\Component\DependencyInjection\ContainerAwareInterface; -use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -23,10 +23,37 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface; * Redirects a request to another URL. * * @author Fabien Potencier + * + * @final since version 3.4 */ class RedirectController implements ContainerAwareInterface { - use ContainerAwareTrait; + /** + * @deprecated since version 3.4, to be removed in 4.0 + */ + protected $container; + + private $router; + private $httpPort; + private $httpsPort; + + public function __construct(UrlGeneratorInterface $router = null, $httpPort = null, $httpsPort = null) + { + $this->router = $router; + $this->httpPort = $httpPort; + $this->httpsPort = $httpsPort; + } + + /** + * @deprecated since version 3.4, to be removed in 4.0 alongside with the ContainerAwareInterface type. + */ + public function setContainer(ContainerInterface $container = null) + { + @trigger_error(sprintf('The "%s()" method is deprecated since version 3.4 and will be removed in 4.0. Inject an UrlGeneratorInterface using the constructor instead.', __METHOD__), E_USER_DEPRECATED); + + $this->container = $container; + $this->router = $container->get('router'); + } /** * Redirects to another route with the given name. @@ -61,7 +88,7 @@ class RedirectController implements ContainerAwareInterface } } - return new RedirectResponse($this->container->get('router')->generate($route, $attributes, UrlGeneratorInterface::ABSOLUTE_URL), $permanent ? 301 : 302); + return new RedirectResponse($this->router->generate($route, $attributes, UrlGeneratorInterface::ABSOLUTE_URL), $permanent ? 301 : 302); } /** @@ -115,8 +142,11 @@ class RedirectController implements ContainerAwareInterface if (null === $httpPort) { if ('http' === $request->getScheme()) { $httpPort = $request->getPort(); - } elseif ($this->container->hasParameter('request_listener.http_port')) { + } elseif ($this->container && $this->container->hasParameter('request_listener.http_port')) { + @trigger_error(sprintf('Passing the http port as a container parameter is deprecated since Symfony 3.4 and won\'t be possible in 4.0. Pass it to the constructor of the "%s" class instead.', __CLASS__), E_USER_DEPRECATED); $httpPort = $this->container->getParameter('request_listener.http_port'); + } else { + $httpPort = $this->httpPort; } } @@ -127,8 +157,11 @@ class RedirectController implements ContainerAwareInterface if (null === $httpsPort) { if ('https' === $request->getScheme()) { $httpsPort = $request->getPort(); - } elseif ($this->container->hasParameter('request_listener.https_port')) { + } elseif ($this->container && $this->container->hasParameter('request_listener.https_port')) { + @trigger_error(sprintf('Passing the https port as a container parameter is deprecated since Symfony 3.4 and won\'t be possible in 4.0. Pass it to the constructor of the "%s" class instead.', __CLASS__), E_USER_DEPRECATED); $httpsPort = $this->container->getParameter('request_listener.https_port'); + } else { + $httpsPort = $this->httpsPort; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php index 42ebd33bad..5d13355208 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php @@ -12,17 +12,48 @@ namespace Symfony\Bundle\FrameworkBundle\Controller; use Symfony\Component\DependencyInjection\ContainerAwareInterface; -use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Templating\EngineInterface; +use Twig\Environment; /** * TemplateController. * * @author Fabien Potencier + * + * @final since version 3.4 */ class TemplateController implements ContainerAwareInterface { - use ContainerAwareTrait; + /** + * @deprecated since version 3.4, to be removed in 4.0 + */ + protected $container; + + private $twig; + private $templating; + + public function __construct(Environment $twig = null, EngineInterface $templating = null) + { + $this->twig = $twig; + $this->templating = $templating; + } + + /** + * @deprecated since version 3.4, to be removed in 4.0 alongside with the ContainerAwareInterface type. + */ + public function setContainer(ContainerInterface $container = null) + { + @trigger_error(sprintf('The "%s()" method is deprecated since version 3.4 and will be removed in 4.0. Inject a Twig Environment or an EngineInterface using the constructor instead.', __METHOD__), E_USER_DEPRECATED); + + if ($container->has('templating')) { + $this->templating = $container->get('templating'); + } elseif ($container->has('twig')) { + $this->twig = $container->get('twig'); + } + $this->container = $container; + } /** * Renders a template. @@ -36,10 +67,10 @@ class TemplateController implements ContainerAwareInterface */ public function templateAction($template, $maxAge = null, $sharedAge = null, $private = null) { - if ($this->container->has('templating')) { - $response = $this->container->get('templating')->renderResponse($template); - } elseif ($this->container->has('twig')) { - $response = new Response($this->container->get('twig')->render($template)); + if ($this->templating) { + $response = new Response($this->templating->render($template)); + } elseif ($this->twig) { + $response = new Response($this->twig->render($template)); } else { throw new \LogicException('You can not use the TemplateController if the Templating Component or the Twig Bundle are not available.'); } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 786faeba07..2194175297 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -466,9 +466,9 @@ class FrameworkExtension extends Extension $container->setParameter('profiler.storage.dsn', $config['dsn']); - if (!$config['collect']) { - $container->getDefinition('profiler')->addMethodCall('disable', array()); - } + $container->getDefinition('profiler') + ->addArgument($config['collect']) + ->addTag('kernel.reset', array('method' => 'reset')); } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml index cfb11e0ae4..21c891881e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml @@ -104,5 +104,16 @@ %kernel.project_dir% %kernel.debug% + + + + %request_listener.http_port% + %request_listener.https_port% + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php index 3feabfd12e..8321ebd5b4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php @@ -451,7 +451,7 @@ abstract class ControllerTraitTest extends TestCase public function testRenderTemplating() { $templating = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface')->getMock(); - $templating->expects($this->once())->method('renderResponse')->willReturn(new Response('bar')); + $templating->expects($this->once())->method('render')->willReturn('bar'); $container = new Container(); $container->set('templating', $templating); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php index 14b6e4428e..a95083a382 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php @@ -15,6 +15,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Bundle\FrameworkBundle\Controller\RedirectController; use Symfony\Bundle\FrameworkBundle\Tests\TestCase; @@ -66,23 +67,14 @@ class RedirectControllerTest extends TestCase $request->attributes = new ParameterBag($attributes); - $router = $this->getMockBuilder('Symfony\Component\Routing\RouterInterface')->getMock(); + $router = $this->getMockBuilder(UrlGeneratorInterface::class)->getMock(); $router ->expects($this->once()) ->method('generate') ->with($this->equalTo($route), $this->equalTo($expectedAttributes)) ->will($this->returnValue($url)); - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - - $container - ->expects($this->once()) - ->method('get') - ->with($this->equalTo('router')) - ->will($this->returnValue($router)); - - $controller = new RedirectController(); - $controller->setContainer($container); + $controller = new RedirectController($router); $returnResponse = $controller->redirectAction($request, $route, $permanent, $ignoreAttributes); @@ -130,7 +122,7 @@ class RedirectControllerTest extends TestCase $this->assertEquals(302, $returnResponse->getStatusCode()); } - public function testUrlRedirectDefaultPortParameters() + public function testUrlRedirectDefaultPorts() { $host = 'www.example.com'; $baseUrl = '/base'; @@ -151,6 +143,30 @@ class RedirectControllerTest extends TestCase $this->assertRedirectUrl($returnValue, $expectedUrl); } + /** + * @group legacy + */ + public function testUrlRedirectDefaultPortParameters() + { + $host = 'www.example.com'; + $baseUrl = '/base'; + $path = '/redirect-path'; + $httpPort = 1080; + $httpsPort = 1443; + + $expectedUrl = "https://$host:$httpsPort$baseUrl$path"; + $request = $this->createRequestObject('http', $host, $httpPort, $baseUrl); + $controller = $this->createLegacyRedirectController(null, $httpsPort); + $returnValue = $controller->urlRedirectAction($request, $path, false, 'https'); + $this->assertRedirectUrl($returnValue, $expectedUrl); + + $expectedUrl = "http://$host:$httpPort$baseUrl$path"; + $request = $this->createRequestObject('https', $host, $httpPort, $baseUrl); + $controller = $this->createLegacyRedirectController($httpPort); + $returnValue = $controller->urlRedirectAction($request, $path, false, 'http'); + $this->assertRedirectUrl($returnValue, $expectedUrl); + } + public function urlRedirectProvider() { return array( @@ -256,6 +272,14 @@ class RedirectControllerTest extends TestCase } private function createRedirectController($httpPort = null, $httpsPort = null) + { + return new RedirectController(null, $httpPort, $httpsPort); + } + + /** + * @deprecated + */ + private function createLegacyRedirectController($httpPort = null, $httpsPort = null) { $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/TemplateControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/TemplateControllerTest.php index 6afb5e3203..497c112eed 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/TemplateControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/TemplateControllerTest.php @@ -12,8 +12,8 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; use Symfony\Bundle\FrameworkBundle\Controller\TemplateController; +use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface; use Symfony\Bundle\FrameworkBundle\Tests\TestCase; -use Symfony\Component\HttpFoundation\Response; /** * @author Kévin Dunglas @@ -25,6 +25,29 @@ class TemplateControllerTest extends TestCase $twig = $this->getMockBuilder('Twig\Environment')->disableOriginalConstructor()->getMock(); $twig->expects($this->once())->method('render')->willReturn('bar'); + $controller = new TemplateController($twig); + + $this->assertEquals('bar', $controller->templateAction('mytemplate')->getContent()); + } + + public function testTemplating() + { + $templating = $this->getMockBuilder(EngineInterface::class)->getMock(); + $templating->expects($this->once())->method('render')->willReturn('bar'); + + $controller = new TemplateController(null, $templating); + + $this->assertEquals('bar', $controller->templateAction('mytemplate')->getContent()); + } + + /** + * @group legacy + */ + public function testLegacyTwig() + { + $twig = $this->getMockBuilder('Twig\Environment')->disableOriginalConstructor()->getMock(); + $twig->expects($this->once())->method('render')->willReturn('bar'); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); $container->expects($this->at(0))->method('has')->will($this->returnValue(false)); $container->expects($this->at(1))->method('has')->will($this->returnValue(true)); @@ -36,10 +59,13 @@ class TemplateControllerTest extends TestCase $this->assertEquals('bar', $controller->templateAction('mytemplate')->getContent()); } - public function testTemplating() + /** + * @group legacy + */ + public function testLegacyTemplating() { $templating = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface')->getMock(); - $templating->expects($this->once())->method('renderResponse')->willReturn(new Response('bar')); + $templating->expects($this->once())->method('render')->willReturn('bar'); $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); $container->expects($this->at(0))->method('has')->willReturn(true); @@ -57,12 +83,7 @@ class TemplateControllerTest extends TestCase */ public function testNoTwigNorTemplating() { - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('has')->willReturn(false); - $container->expects($this->at(1))->method('has')->willReturn(false); - $controller = new TemplateController(); - $controller->setContainer($container); $controller->templateAction('mytemplate')->getContent(); } diff --git a/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php b/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php index dbe7d65c29..3b928e0338 100644 --- a/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php +++ b/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php @@ -194,6 +194,14 @@ class SecurityDataCollector extends DataCollector implements LateDataCollectorIn } } + /** + * {@inheritdoc} + */ + public function reset() + { + $this->data = array(); + } + public function lateCollect() { $this->data = $this->cloneVar($this->data); diff --git a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php index cb71ed3fd8..9dd6068351 100644 --- a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php @@ -254,19 +254,12 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, R $f = $this->getTagsByKey; $tagsByKey = $f($items); - $deletedTags = $this->deferred = array(); + $this->deferred = array(); $tagVersions = $this->getTagVersions($tagsByKey); $f = $this->createCacheItem; foreach ($tagsByKey as $key => $tags) { - if ($tags) { - $this->pool->saveDeferred($f(static::TAGS_PREFIX.$key, array_intersect_key($tagVersions, $tags), $items[$key])); - } else { - $deletedTags[] = static::TAGS_PREFIX.$key; - } - } - if ($deletedTags) { - $this->pool->deleteItems($deletedTags); + $this->pool->saveDeferred($f(static::TAGS_PREFIX.$key, array_intersect_key($tagVersions, $tags), $items[$key])); } } diff --git a/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php b/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php index 07ddaf3f47..62d502f01f 100644 --- a/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php +++ b/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php @@ -53,6 +53,15 @@ class CacheDataCollector extends DataCollector implements LateDataCollectorInter $this->data['total']['statistics'] = $this->calculateTotalStatistics(); } + public function reset() + { + $this->data = array(); + foreach ($this->instances as $instance) { + // Calling getCalls() will clear the calls. + $instance->getCalls(); + } + } + public function lateCollect() { $this->data = $this->cloneVar($this->data); diff --git a/src/Symfony/Component/Config/ResourceCheckerConfigCache.php b/src/Symfony/Component/Config/ResourceCheckerConfigCache.php index 421db6408a..ab8c99eead 100644 --- a/src/Symfony/Component/Config/ResourceCheckerConfigCache.php +++ b/src/Symfony/Component/Config/ResourceCheckerConfigCache.php @@ -132,6 +132,10 @@ class ResourceCheckerConfigCache implements ConfigCacheInterface // discard chmod failure (some filesystem may not support it) } } + + if (\function_exists('opcache_invalidate') && ini_get('opcache.enable')) { + @opcache_invalidate($this->file, true); + } } /** diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php index 1d4b0f1769..281ba3ec39 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php +++ b/src/Symfony/Component/DependencyInjection/Container.php @@ -152,8 +152,8 @@ class Container implements ResettableContainerInterface throw new InvalidArgumentException('You cannot set service "service_container".'); } - if (isset($this->fileMap[$id]) || isset($this->methodMap[$id])) { - throw new InvalidArgumentException(sprintf('You cannot set the pre-defined service "%s".', $id)); + if (isset($this->services[$id]) && (isset($this->fileMap[$id]) || isset($this->methodMap[$id]))) { + throw new InvalidArgumentException(sprintf('The "%s" service is already initialized, you cannot replace it.', $id)); } if (isset($this->aliases[$id])) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index ac277bcab4..fa6abaf015 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -889,26 +889,17 @@ EOF; $code .= <<docStar} - * {@inheritdoc} - */ public function reset() { \$this->privates = array(); parent::reset(); } - /*{$this->docStar} - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /*{$this->docStar} - * {@inheritdoc} - */ public function isCompiled() { return true; @@ -919,9 +910,6 @@ EOF; if ($this->asFiles) { $code .= <<docStar} - * {@inheritdoc} - */ protected function load(\$file, \$lazyLoad = true) { return require \$file; @@ -1052,9 +1040,6 @@ EOF; $code = <<<'EOF' - /** - * {@inheritdoc} - */ public function getParameter($name) { $name = (string) $name; @@ -1069,9 +1054,6 @@ EOF; return $this->parameters[$name]; } - /** - * {@inheritdoc} - */ public function hasParameter($name) { $name = (string) $name; @@ -1079,17 +1061,11 @@ EOF; return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } - /** - * {@inheritdoc} - */ public function setParameter($name, $value) { throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } - /** - * {@inheritdoc} - */ public function getParameterBag() { if (null === $this->parameterBag) { @@ -1104,9 +1080,6 @@ EOF; } EOF; - if ('' === $this->docStar) { - $code = str_replace('/**', '/*', $code); - } if ($dynamicPhp) { $loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, count($dynamicPhp), false)), '', 8); diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php index 513dff8450..f3c49317e3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php @@ -165,6 +165,36 @@ class ContainerTest extends TestCase $this->assertSame($foo, $c->get('alias'), '->set() replaces an existing alias'); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The "bar" service is already initialized, you cannot replace it. + */ + public function testSetWithNullOnInitializedPredefinedService() + { + $sc = new Container(); + $sc->set('foo', new \stdClass()); + $sc->set('foo', null); + $this->assertFalse($sc->has('foo'), '->set() with null service resets the service'); + + $sc = new ProjectServiceContainer(); + $sc->get('bar'); + $sc->set('bar', null); + $this->assertTrue($sc->has('bar'), '->set() with null service resets the pre-defined service'); + } + + public function testSetWithNullOnUninitializedPredefinedService() + { + $sc = new Container(); + $sc->set('foo', new \stdClass()); + $sc->get('foo', null); + $sc->set('foo', null); + $this->assertFalse($sc->has('foo'), '->set() with null service resets the service'); + + $sc = new ProjectServiceContainer(); + $sc->set('bar', null); + $this->assertTrue($sc->has('bar'), '->set() with null service resets the pre-defined service'); + } + public function testGet() { $sc = new ProjectServiceContainer(); @@ -362,16 +392,6 @@ class ContainerTest extends TestCase $c->get('internal_dependency'); $c->get('internal'); } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - * @expectedExceptionMessage You cannot set the pre-defined service "bar". - */ - public function testReplacingAPreDefinedService() - { - $c = new ProjectServiceContainer(); - $c->set('bar', new \stdClass()); - } } class ProjectServiceContainer extends Container diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index e36faf79b4..86fa9ed0b3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -286,7 +286,7 @@ class PhpDumperTest extends TestCase /** * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - * @expectedExceptionMessage You cannot set the pre-defined service "bar". + * @expectedExceptionMessage The "bar2" service is already initialized, you cannot replace it. */ public function testOverrideServiceWhenUsingADumpedContainer() { @@ -294,7 +294,8 @@ class PhpDumperTest extends TestCase require_once self::$fixturesPath.'/includes/foo.php'; $container = new \ProjectServiceContainer(); - $container->set('bar', $bar = new \stdClass()); + $container->get('bar2'); + $container->set('bar2', new \stdClass()); } /** 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 0d40fc4aa3..e30589d8b0 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php @@ -29,26 +29,17 @@ class Container extends AbstractContainer $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php index 2f633a41dc..aa828a36ca 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1.php @@ -27,26 +27,17 @@ class ProjectServiceContainer extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php index 031888238e..cd792aba59 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services10.php @@ -32,26 +32,17 @@ class ProjectServiceContainer extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; @@ -67,9 +58,6 @@ class ProjectServiceContainer extends Container return $this->services['test'] = new \stdClass(array('only dot' => '.', 'concatenation as value' => '.\'\'.', 'concatenation from the start value' => '\'\'.', '.' => 'dot as a key', '.\'\'.' => 'concatenation as a key', '\'\'.' => 'concatenation from the start key', 'optimize concatenation' => 'string1-string2', 'optimize concatenation with empty string' => 'string1string2', 'optimize concatenation from the start' => 'start', 'optimize concatenation at the end' => 'end')); } - /** - * {@inheritdoc} - */ public function getParameter($name) { $name = (string) $name; @@ -84,9 +72,6 @@ class ProjectServiceContainer extends Container return $this->parameters[$name]; } - /** - * {@inheritdoc} - */ public function hasParameter($name) { $name = (string) $name; @@ -94,17 +79,11 @@ class ProjectServiceContainer extends Container return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } - /** - * {@inheritdoc} - */ public function setParameter($name, $value) { throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } - /** - * {@inheritdoc} - */ public function getParameterBag() { if (null === $this->parameterBag) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php index 4b0b560d1c..44b2f25fc9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php @@ -36,26 +36,17 @@ class ProjectServiceContainer extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; @@ -71,9 +62,6 @@ class ProjectServiceContainer extends Container return $this->services['test'] = new \stdClass(('wiz'.$this->targetDirs[1]), array(('wiz'.$this->targetDirs[1]) => ($this->targetDirs[2].'/'))); } - /** - * {@inheritdoc} - */ public function getParameter($name) { $name = (string) $name; @@ -88,9 +76,6 @@ class ProjectServiceContainer extends Container return $this->parameters[$name]; } - /** - * {@inheritdoc} - */ public function hasParameter($name) { $name = (string) $name; @@ -98,17 +83,11 @@ class ProjectServiceContainer extends Container return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } - /** - * {@inheritdoc} - */ public function setParameter($name, $value) { throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } - /** - * {@inheritdoc} - */ public function getParameterBag() { if (null === $this->parameterBag) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php index f4dcb4ac56..9086f38625 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services13.php @@ -30,26 +30,17 @@ class ProjectServiceContainer extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php index 304b115faa..36371414cf 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php @@ -31,26 +31,17 @@ class ProjectServiceContainer extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php index eaf16eb370..162ded9719 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php @@ -30,26 +30,17 @@ class ProjectServiceContainer extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php index 760de17225..2412fe6139 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services26.php @@ -36,26 +36,17 @@ class Symfony_DI_PhpDumper_Test_EnvParameters extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; @@ -73,9 +64,6 @@ class Symfony_DI_PhpDumper_Test_EnvParameters extends Container return $this->services['test'] = new $class($this->getEnv('Bar'), 'foo'.$this->getEnv('string:FOO').'baz', $this->getEnv('int:Baz')); } - /** - * {@inheritdoc} - */ public function getParameter($name) { $name = (string) $name; @@ -90,9 +78,6 @@ class Symfony_DI_PhpDumper_Test_EnvParameters extends Container return $this->parameters[$name]; } - /** - * {@inheritdoc} - */ public function hasParameter($name) { $name = (string) $name; @@ -100,17 +85,11 @@ class Symfony_DI_PhpDumper_Test_EnvParameters extends Container return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } - /** - * {@inheritdoc} - */ public function setParameter($name, $value) { throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } - /** - * {@inheritdoc} - */ public function getParameterBag() { if (null === $this->parameterBag) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services31.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services31.php index a03fdf1db8..20f3bea379 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services31.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services31.php @@ -30,22 +30,26 @@ class ProjectServiceContainer extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; } +<<<<<<< HEAD +======= + public function isFrozen() + { + @trigger_error(sprintf('The %s() method is deprecated since version 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED); + + return true; + } + +>>>>>>> 3.4 /** * Gets the 'foo' service. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php index 3bf1376c22..d2ecd29ebd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services33.php @@ -31,26 +31,17 @@ class ProjectServiceContainer extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php index 1c4dfc9cc0..601c79ebda 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php @@ -29,34 +29,22 @@ class ProjectServiceContainer extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; } - /** - * {@inheritdoc} - */ public function getParameter($name) { $name = (string) $name; @@ -71,9 +59,6 @@ class ProjectServiceContainer extends Container return $this->parameters[$name]; } - /** - * {@inheritdoc} - */ public function hasParameter($name) { $name = (string) $name; @@ -81,17 +66,11 @@ class ProjectServiceContainer extends Container return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } - /** - * {@inheritdoc} - */ public function setParameter($name, $value) { throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } - /** - * {@inheritdoc} - */ public function getParameterBag() { if (null === $this->parameterBag) { 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 87bce1a39d..931b99ac35 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 @@ -351,34 +351,22 @@ class Container%s extends Container ); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; } - /** - * {@inheritdoc} - */ protected function load($file, $lazyLoad = true) { return require $file; @@ -394,9 +382,6 @@ class Container%s extends Container return new \Bar\FooClass(($this->services['deprecated_service'] ?? $this->load(__DIR__.'/getDeprecatedServiceService.php'))); } - /** - * {@inheritdoc} - */ public function getParameter($name) { $name = (string) $name; @@ -411,9 +396,6 @@ class Container%s extends Container return $this->parameters[$name]; } - /** - * {@inheritdoc} - */ public function hasParameter($name) { $name = (string) $name; @@ -421,17 +403,11 @@ class Container%s extends Container return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } - /** - * {@inheritdoc} - */ public function setParameter($name, $value) { throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } - /** - * {@inheritdoc} - */ public function getParameterBag() { if (null === $this->parameterBag) { 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 c1dda1e37c..63ca4e25ed 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -56,26 +56,17 @@ class ProjectServiceContainer extends Container ); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; @@ -395,9 +386,6 @@ class ProjectServiceContainer extends Container return $this->privates['factory_simple'] = new \SimpleFactoryClass('foo'); } - /** - * {@inheritdoc} - */ public function getParameter($name) { $name = (string) $name; @@ -412,9 +400,6 @@ class ProjectServiceContainer extends Container return $this->parameters[$name]; } - /** - * {@inheritdoc} - */ public function hasParameter($name) { $name = (string) $name; @@ -422,17 +407,11 @@ class ProjectServiceContainer extends Container return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } - /** - * {@inheritdoc} - */ public function setParameter($name, $value) { throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } - /** - * {@inheritdoc} - */ public function getParameterBag() { if (null === $this->parameterBag) { 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 108620c13c..c4d1c95034 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 @@ -36,26 +36,17 @@ class ProjectServiceContainer extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; @@ -75,9 +66,6 @@ class ProjectServiceContainer extends Container return $instance; } - /** - * {@inheritdoc} - */ public function getParameter($name) { $name = (string) $name; @@ -92,9 +80,6 @@ class ProjectServiceContainer extends Container return $this->parameters[$name]; } - /** - * {@inheritdoc} - */ public function hasParameter($name) { $name = (string) $name; @@ -102,17 +87,11 @@ class ProjectServiceContainer extends Container return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } - /** - * {@inheritdoc} - */ public function setParameter($name, $value) { throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } - /** - * {@inheritdoc} - */ public function getParameterBag() { if (null === $this->parameterBag) { 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 4263e17f20..28b462fd46 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 @@ -29,34 +29,22 @@ class Symfony_DI_PhpDumper_Test_Base64Parameters extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; } - /** - * {@inheritdoc} - */ public function getParameter($name) { $name = (string) $name; @@ -71,9 +59,6 @@ class Symfony_DI_PhpDumper_Test_Base64Parameters extends Container return $this->parameters[$name]; } - /** - * {@inheritdoc} - */ public function hasParameter($name) { $name = (string) $name; @@ -81,17 +66,11 @@ class Symfony_DI_PhpDumper_Test_Base64Parameters extends Container return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } - /** - * {@inheritdoc} - */ public function setParameter($name, $value) { throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } - /** - * {@inheritdoc} - */ public function getParameterBag() { if (null === $this->parameterBag) { 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 f003e2c40a..58dbe354fa 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_locator.php @@ -37,26 +37,17 @@ class ProjectServiceContainer extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; 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 07f1135d95..a5a44a8d95 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 @@ -31,26 +31,17 @@ class ProjectServiceContainer extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; 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 f003ee140f..14c7633668 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 @@ -30,26 +30,17 @@ class ProjectServiceContainer extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; 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 f62aff2d9f..84b89ad34e 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 @@ -33,26 +33,17 @@ class Symfony_DI_PhpDumper_Test_Rot13Parameters extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; @@ -80,9 +71,6 @@ class Symfony_DI_PhpDumper_Test_Rot13Parameters extends Container })); } - /** - * {@inheritdoc} - */ public function getParameter($name) { $name = (string) $name; @@ -97,9 +85,6 @@ class Symfony_DI_PhpDumper_Test_Rot13Parameters extends Container return $this->parameters[$name]; } - /** - * {@inheritdoc} - */ public function hasParameter($name) { $name = (string) $name; @@ -107,17 +92,11 @@ class Symfony_DI_PhpDumper_Test_Rot13Parameters extends Container return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); } - /** - * {@inheritdoc} - */ public function setParameter($name, $value) { throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } - /** - * {@inheritdoc} - */ public function getParameterBag() { if (null === $this->parameterBag) { 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 9757130d25..d883ef3f0d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php @@ -31,26 +31,17 @@ class ProjectServiceContainer extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; 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 70eb3b3829..fe6ec93827 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 @@ -32,26 +32,17 @@ class Symfony_DI_PhpDumper_Test_Uninitialized_Reference extends Container $this->aliases = array(); } - /** - * {@inheritdoc} - */ public function reset() { $this->privates = array(); parent::reset(); } - /** - * {@inheritdoc} - */ public function compile() { throw new LogicException('You cannot compile a dumped container that was already compiled.'); } - /** - * {@inheritdoc} - */ public function isCompiled() { return true; diff --git a/src/Symfony/Component/EventDispatcher/CHANGELOG.md b/src/Symfony/Component/EventDispatcher/CHANGELOG.md index 9b8eb26c4d..cde9a680a5 100644 --- a/src/Symfony/Component/EventDispatcher/CHANGELOG.md +++ b/src/Symfony/Component/EventDispatcher/CHANGELOG.md @@ -6,6 +6,11 @@ CHANGELOG * removed the `ContainerAwareEventDispatcher` class +3.4.0 +----- + + * Implementing `TraceableEventDispatcherInterface` without the `reset()` method has been deprecated. + 3.3.0 ----- diff --git a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php index 3f1035e13d..2fb795f77b 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php @@ -212,6 +212,11 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface return $notCalled; } + public function reset() + { + $this->called = array(); + } + /** * Proxies all method calls to the original event dispatcher. * diff --git a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php index 5483e81506..f0212753be 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php +++ b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php @@ -15,6 +15,8 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * @author Fabien Potencier + * + * @method reset() Resets the trace. */ interface TraceableEventDispatcherInterface extends EventDispatcherInterface { diff --git a/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php index a1cf6708b3..53a3421afa 100644 --- a/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php +++ b/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php @@ -124,6 +124,21 @@ class TraceableEventDispatcherTest extends TestCase $this->assertEquals(array(), $tdispatcher->getNotCalledListeners()); } + public function testClearCalledListeners() + { + $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $tdispatcher->addListener('foo', function () {}, 5); + + $tdispatcher->dispatch('foo'); + $tdispatcher->reset(); + + $listeners = $tdispatcher->getNotCalledListeners(); + $this->assertArrayHasKey('stub', $listeners['foo.closure']); + unset($listeners['foo.closure']['stub']); + $this->assertEquals(array(), $tdispatcher->getCalledListeners()); + $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'pretty' => 'closure', 'priority' => 5)), $listeners); + } + public function testGetCalledListenersNested() { $tdispatcher = null; diff --git a/src/Symfony/Component/Filesystem/CHANGELOG.md b/src/Symfony/Component/Filesystem/CHANGELOG.md index 41a894b0be..901d18a3e5 100644 --- a/src/Symfony/Component/Filesystem/CHANGELOG.md +++ b/src/Symfony/Component/Filesystem/CHANGELOG.md @@ -6,11 +6,15 @@ CHANGELOG * removed `LockHandler` +3.4.0 +----- + + * support for passing relative paths to `Filesystem::makePathRelative()` is deprecated and will be removed in 4.0 + 3.3.0 ----- * added `appendToFile()` to append contents to existing files - * support for passing relative paths to `Filesystem::makePathRelative()` is deprecated and will be removed in 4.0 3.2.0 ----- diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php index 41428eff95..078aed2e86 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php @@ -74,11 +74,8 @@ class FormDataCollector extends DataCollector implements FormDataCollectorInterf } $this->dataExtractor = $dataExtractor; - $this->data = array( - 'forms' => array(), - 'forms_by_hash' => array(), - 'nb_errors' => 0, - ); + + $this->reset(); } /** @@ -88,6 +85,15 @@ class FormDataCollector extends DataCollector implements FormDataCollectorInterf { } + public function reset() + { + $this->data = array( + 'forms' => array(), + 'forms_by_hash' => array(), + 'nb_errors' => 0, + ); + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php index 24c0f8e871..7e591d414e 100644 --- a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php @@ -695,6 +695,36 @@ class FormDataCollectorTest extends TestCase $this->assertFalse(isset($child21Data['has_children_error']), 'The leaf data does not contains "has_children_error" property.'); } + public function testReset() + { + $form = $this->createForm('my_form'); + + $this->dataExtractor->expects($this->any()) + ->method('extractConfiguration') + ->will($this->returnValue(array())); + $this->dataExtractor->expects($this->any()) + ->method('extractDefaultData') + ->will($this->returnValue(array())); + $this->dataExtractor->expects($this->any()) + ->method('extractSubmittedData') + ->with($form) + ->will($this->returnValue(array('errors' => array('baz')))); + + $this->dataCollector->buildPreliminaryFormTree($form); + $this->dataCollector->collectSubmittedData($form); + + $this->dataCollector->reset(); + + $this->assertSame( + array( + 'forms' => array(), + 'forms_by_hash' => array(), + 'nb_errors' => 0, + ), + $this->dataCollector->getData() + ); + } + private function createForm($name) { $builder = new FormBuilder($name, null, $this->dispatcher, $this->factory); diff --git a/src/Symfony/Component/HttpFoundation/CHANGELOG.md b/src/Symfony/Component/HttpFoundation/CHANGELOG.md index 46d5ac73ae..80619edb83 100644 --- a/src/Symfony/Component/HttpFoundation/CHANGELOG.md +++ b/src/Symfony/Component/HttpFoundation/CHANGELOG.md @@ -29,6 +29,7 @@ CHANGELOG * deprecated the `NativeSessionHandler` class, * deprecated the `AbstractProxy`, `NativeProxy` and `SessionHandlerProxy` classes, * deprecated setting session save handlers that do not implement `\SessionHandlerInterface` in `NativeSessionStorage::setSaveHandler()` + * deprecated using `MongoDbSessionHandler` with the legacy mongo extension; use it with the mongodb/mongodb package and ext-mongodb instead 3.3.0 ----- diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php index 80ecc1cc3c..20e1a897e4 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php @@ -12,7 +12,12 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; /** + * Session handler using the mongodb/mongodb package and MongoDB driver extension. + * * @author Markus Bachmann + * + * @see https://packagist.org/packages/mongodb/mongodb + * @see http://php.net/manual/en/set.mongodb.php */ class MongoDbSessionHandler implements \SessionHandlerInterface { @@ -57,14 +62,18 @@ class MongoDbSessionHandler implements \SessionHandlerInterface * If you use such an index, you can drop `gc_probability` to 0 since * no garbage-collection is required. * - * @param \Mongo|\MongoClient|\MongoDB\Client $mongo A MongoDB\Client, MongoClient or Mongo instance - * @param array $options An associative array of field options + * @param \MongoDB\Client $mongo A MongoDB\Client instance + * @param array $options An associative array of field options * * @throws \InvalidArgumentException When MongoClient or Mongo instance not provided * @throws \InvalidArgumentException When "database" or "collection" not provided */ public function __construct($mongo, array $options) { + if ($mongo instanceof \MongoClient || $mongo instanceof \Mongo) { + @trigger_error(sprintf('Using %s with the legacy mongo extension is deprecated as of 3.4 and will be removed in 4.0. Use it with the mongodb/mongodb package and ext-mongodb instead.', __CLASS__), E_USER_DEPRECATED); + } + if (!($mongo instanceof \MongoDB\Client || $mongo instanceof \MongoClient || $mongo instanceof \Mongo)) { throw new \InvalidArgumentException('MongoClient or Mongo instance required'); } diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php index 74366863f7..89a8c3d0cb 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php @@ -17,6 +17,7 @@ use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandl /** * @author Markus Bachmann * @group time-sensitive + * @requires extension mongodb */ class MongoDbSessionHandlerTest extends TestCase { @@ -31,21 +32,11 @@ class MongoDbSessionHandlerTest extends TestCase { parent::setUp(); - if (extension_loaded('mongodb')) { - if (!class_exists('MongoDB\Client')) { - $this->markTestSkipped('The mongodb/mongodb package is required.'); - } - } elseif (!extension_loaded('mongo')) { - $this->markTestSkipped('The Mongo or MongoDB extension is required.'); + if (!class_exists('MongoDB\Client')) { + $this->markTestSkipped('The mongodb/mongodb package is required.'); } - if (phpversion('mongodb')) { - $mongoClass = 'MongoDB\Client'; - } else { - $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient'; - } - - $this->mongo = $this->getMockBuilder($mongoClass) + $this->mongo = $this->getMockBuilder('MongoDB\Client') ->disableOriginalConstructor() ->getMock(); @@ -307,23 +298,12 @@ class MongoDbSessionHandlerTest extends TestCase $method = new \ReflectionMethod($this->storage, 'getMongo'); $method->setAccessible(true); - if (phpversion('mongodb')) { - $mongoClass = 'MongoDB\Client'; - } else { - $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient'; - } - - $this->assertInstanceOf($mongoClass, $method->invoke($this->storage)); + $this->assertInstanceOf('MongoDB\Client', $method->invoke($this->storage)); } private function createMongoCollectionMock() { - $collectionClass = 'MongoCollection'; - if (phpversion('mongodb')) { - $collectionClass = 'MongoDB\Collection'; - } - - $collection = $this->getMockBuilder($collectionClass) + $collection = $this->getMockBuilder('MongoDB\Collection') ->disableOriginalConstructor() ->getMock(); diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index 1683e2da43..419e783ca4 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -38,7 +38,9 @@ CHANGELOG * deprecated the `ChainCacheClearer::add()` method * deprecated the `CacheaWarmerAggregate::add()` and `setWarmers()` methods * made `CacheWarmerAggregate` and `ChainCacheClearer` classes final - + * added the possibility to reset the profiler to its initial state + * deprecated data collectors without a `reset()` method + * deprecated implementing `DebugLoggerInterface` without a `clear()` method 3.3.0 ----- diff --git a/src/Symfony/Component/HttpKernel/DataCollector/AjaxDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/AjaxDataCollector.php index b8405d5945..370a874fe5 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/AjaxDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/AjaxDataCollector.php @@ -26,6 +26,11 @@ class AjaxDataCollector extends DataCollector // all collecting is done client side } + public function reset() + { + // all collecting is done client side + } + public function getName() { return 'ajax'; diff --git a/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php index 965342191b..0a6aa82b26 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php @@ -95,6 +95,14 @@ class ConfigDataCollector extends DataCollector implements LateDataCollectorInte } } + /** + * {@inheritdoc} + */ + public function reset() + { + $this->data = array(); + } + public function lateCollect() { $this->data = $this->cloneVar($this->data); diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php index 2820ad5b28..c0a0c0a982 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php @@ -18,6 +18,8 @@ use Symfony\Component\HttpFoundation\Response; * DataCollectorInterface. * * @author Fabien Potencier + * + * @method reset() Resets this data collector to its initial state. */ interface DataCollectorInterface { diff --git a/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php index 3d75f322d8..ab44405edb 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php @@ -27,6 +27,9 @@ class EventDataCollector extends DataCollector implements LateDataCollectorInter public function __construct(EventDispatcherInterface $dispatcher = null) { + if ($dispatcher instanceof TraceableEventDispatcherInterface && !method_exists($dispatcher, 'reset')) { + @trigger_error(sprintf('Implementing "%s" without the "reset()" method is deprecated since version 3.4 and will be unsupported in 4.0 for class "%s".', TraceableEventDispatcherInterface::class, \get_class($dispatcher)), E_USER_DEPRECATED); + } $this->dispatcher = $dispatcher; } @@ -41,6 +44,19 @@ class EventDataCollector extends DataCollector implements LateDataCollectorInter ); } + public function reset() + { + $this->data = array(); + + if ($this->dispatcher instanceof TraceableEventDispatcherInterface) { + if (!method_exists($this->dispatcher, 'reset')) { + return; // @deprecated + } + + $this->dispatcher->reset(); + } + } + public function lateCollect() { if ($this->dispatcher instanceof TraceableEventDispatcherInterface) { diff --git a/src/Symfony/Component/HttpKernel/DataCollector/ExceptionDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/ExceptionDataCollector.php index 9fe826446b..7a25f14921 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/ExceptionDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/ExceptionDataCollector.php @@ -34,6 +34,14 @@ class ExceptionDataCollector extends DataCollector } } + /** + * {@inheritdoc} + */ + public function reset() + { + $this->data = array(); + } + /** * Checks if the exception is not null. * diff --git a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php index 92fd0da71e..ee645d53e8 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php @@ -29,6 +29,10 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte public function __construct($logger = null, $containerPathPrefix = null) { if (null !== $logger && $logger instanceof DebugLoggerInterface) { + if (!method_exists($logger, 'clear')) { + @trigger_error(sprintf('Implementing "%s" without the "clear()" method is deprecated since version 3.4 and will be unsupported in 4.0 for class "%s".', DebugLoggerInterface::class, \get_class($logger)), E_USER_DEPRECATED); + } + $this->logger = $logger; } @@ -43,6 +47,17 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte // everything is done as late as possible } + /** + * {@inheritdoc} + */ + public function reset() + { + if ($this->logger && method_exists($this->logger, 'clear')) { + $this->logger->clear(); + } + $this->data = array(); + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php index b7f61f46fd..8d8cc1a04d 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php @@ -23,10 +23,7 @@ class MemoryDataCollector extends DataCollector implements LateDataCollectorInte { public function __construct() { - $this->data = array( - 'memory' => 0, - 'memory_limit' => $this->convertToBytes(ini_get('memory_limit')), - ); + $this->reset(); } /** @@ -37,6 +34,17 @@ class MemoryDataCollector extends DataCollector implements LateDataCollectorInte $this->updateMemoryUsage(); } + /** + * {@inheritdoc} + */ + public function reset() + { + $this->data = array( + 'memory' => 0, + 'memory_limit' => $this->convertToBytes(ini_get('memory_limit')), + ); + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php index 7049844f9e..d27940266a 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php @@ -156,6 +156,12 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter $this->data = $this->cloneVar($this->data); } + public function reset() + { + $this->data = array(); + $this->controllers = new \SplObjectStorage(); + } + public function getMethod() { return $this->data['method']; diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RouterDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RouterDataCollector.php index 76d9623461..59f77c4ba2 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/RouterDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/RouterDataCollector.php @@ -23,17 +23,14 @@ use Symfony\Component\HttpKernel\Event\FilterControllerEvent; */ class RouterDataCollector extends DataCollector { + /** + * @var \SplObjectStorage + */ protected $controllers; public function __construct() { - $this->controllers = new \SplObjectStorage(); - - $this->data = array( - 'redirect' => false, - 'url' => null, - 'route' => null, - ); + $this->reset(); } /** @@ -53,6 +50,17 @@ class RouterDataCollector extends DataCollector unset($this->controllers[$request]); } + public function reset() + { + $this->controllers = new \SplObjectStorage(); + + $this->data = array( + 'redirect' => false, + 'url' => null, + 'route' => null, + ); + } + protected function guessRoute(Request $request, $controller) { return 'n/a'; diff --git a/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php index 2d39156e69..6588304935 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php @@ -49,6 +49,14 @@ class TimeDataCollector extends DataCollector implements LateDataCollectorInterf ); } + /** + * {@inheritdoc} + */ + public function reset() + { + $this->data = array(); + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/HttpKernel/Log/DebugLoggerInterface.php b/src/Symfony/Component/HttpKernel/Log/DebugLoggerInterface.php index 5635a2184f..f0606d3b0e 100644 --- a/src/Symfony/Component/HttpKernel/Log/DebugLoggerInterface.php +++ b/src/Symfony/Component/HttpKernel/Log/DebugLoggerInterface.php @@ -15,6 +15,8 @@ namespace Symfony\Component\HttpKernel\Log; * DebugLoggerInterface. * * @author Fabien Potencier + * + * @method clear() Removes all log records. */ interface DebugLoggerInterface { diff --git a/src/Symfony/Component/HttpKernel/Profiler/Profiler.php b/src/Symfony/Component/HttpKernel/Profiler/Profiler.php index 2580b4fffa..85a71641be 100644 --- a/src/Symfony/Component/HttpKernel/Profiler/Profiler.php +++ b/src/Symfony/Component/HttpKernel/Profiler/Profiler.php @@ -40,6 +40,11 @@ class Profiler */ private $logger; + /** + * @var bool + */ + private $initiallyEnabled = true; + /** * @var bool */ @@ -48,11 +53,13 @@ class Profiler /** * @param ProfilerStorageInterface $storage A ProfilerStorageInterface instance * @param LoggerInterface $logger A LoggerInterface instance + * @param bool $enable The initial enabled state */ - public function __construct(ProfilerStorageInterface $storage, LoggerInterface $logger = null) + public function __construct(ProfilerStorageInterface $storage, LoggerInterface $logger = null, $enable = true) { $this->storage = $storage; $this->logger = $logger; + $this->initiallyEnabled = $this->enabled = (bool) $enable; } /** @@ -188,6 +195,18 @@ class Profiler return $profile; } + public function reset() + { + foreach ($this->collectors as $collector) { + if (!method_exists($collector, 'reset')) { + continue; + } + + $collector->reset(); + } + $this->enabled = $this->initiallyEnabled; + } + /** * Gets the Collectors associated with this profiler. * @@ -218,6 +237,10 @@ class Profiler */ public function add(DataCollectorInterface $collector) { + if (!method_exists($collector, 'reset')) { + @trigger_error(sprintf('Implementing "%s" without the "reset()" method is deprecated since version 3.4 and will be unsupported in 4.0 for class "%s".', DataCollectorInterface::class, \get_class($collector)), E_USER_DEPRECATED); + } + $this->collectors[$collector->getName()] = $collector; } diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/ExceptionDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/ExceptionDataCollectorTest.php index afad9f58af..178f1f01a3 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/ExceptionDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/ExceptionDataCollectorTest.php @@ -37,4 +37,23 @@ class ExceptionDataCollectorTest extends TestCase $this->assertSame('exception', $c->getName()); $this->assertSame($trace, $c->getTrace()); } + + public function testCollectWithoutException() + { + $c = new ExceptionDataCollector(); + $c->collect(new Request(), new Response()); + + $this->assertFalse($c->hasException()); + } + + public function testReset() + { + $c = new ExceptionDataCollector(); + + $c->collect(new Request(), new Response(), new \Exception()); + $c->reset(); + $c->collect(new Request(), new Response()); + + $this->assertFalse($c->hasException()); + } } diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php index 62bf2c00c7..3dec3bd7f8 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php @@ -19,7 +19,10 @@ class LoggerDataCollectorTest extends TestCase { public function testCollectWithUnexpectedFormat() { - $logger = $this->getMockBuilder('Symfony\Component\HttpKernel\Log\DebugLoggerInterface')->getMock(); + $logger = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Log\DebugLoggerInterface') + ->setMethods(array('countErrors', 'getLogs', 'clear')) + ->getMock(); $logger->expects($this->once())->method('countErrors')->will($this->returnValue('foo')); $logger->expects($this->exactly(2))->method('getLogs')->will($this->returnValue(array())); @@ -43,7 +46,10 @@ class LoggerDataCollectorTest extends TestCase */ public function testCollect($nb, $logs, $expectedLogs, $expectedDeprecationCount, $expectedScreamCount, $expectedPriorities = null) { - $logger = $this->getMockBuilder('Symfony\Component\HttpKernel\Log\DebugLoggerInterface')->getMock(); + $logger = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Log\DebugLoggerInterface') + ->setMethods(array('countErrors', 'getLogs', 'clear')) + ->getMock(); $logger->expects($this->once())->method('countErrors')->will($this->returnValue($nb)); $logger->expects($this->exactly(2))->method('getLogs')->will($this->returnValue($logs)); @@ -70,6 +76,18 @@ class LoggerDataCollectorTest extends TestCase } } + public function testReset() + { + $logger = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Log\DebugLoggerInterface') + ->setMethods(array('countErrors', 'getLogs', 'clear')) + ->getMock(); + $logger->expects($this->once())->method('clear'); + + $c = new LoggerDataCollector($logger); + $c->reset(); + } + public function getCollectTestData() { yield 'simple log' => array( diff --git a/src/Symfony/Component/HttpKernel/Tests/Fixtures/DataCollector/CloneVarDataCollector.php b/src/Symfony/Component/HttpKernel/Tests/Fixtures/DataCollector/CloneVarDataCollector.php index 867ccdce57..89dec36af4 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fixtures/DataCollector/CloneVarDataCollector.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fixtures/DataCollector/CloneVarDataCollector.php @@ -29,6 +29,11 @@ class CloneVarDataCollector extends DataCollector $this->data = $this->cloneVar($this->varToClone); } + public function reset() + { + $this->data = array(); + } + public function getData() { return $this->data; diff --git a/src/Symfony/Component/HttpKernel/Tests/Fixtures/TestEventDispatcher.php b/src/Symfony/Component/HttpKernel/Tests/Fixtures/TestEventDispatcher.php index da7ef5bd60..ca2e6a693d 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fixtures/TestEventDispatcher.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fixtures/TestEventDispatcher.php @@ -25,4 +25,8 @@ class TestEventDispatcher extends EventDispatcher implements TraceableEventDispa { return array('bar'); } + + public function reset() + { + } } diff --git a/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php b/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php index 1a6f54636a..243c3c5c5a 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\HttpKernel\Tests\Profiler; use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector; use Symfony\Component\HttpKernel\Profiler\FileProfilerStorage; use Symfony\Component\HttpKernel\Profiler\Profiler; @@ -40,6 +41,19 @@ class ProfilerTest extends TestCase $this->assertSame('bar', $profile->getCollector('request')->getRequestQuery()->all()['foo']->getValue()); } + public function testReset() + { + $collector = $this->getMockBuilder(DataCollectorInterface::class) + ->setMethods(['collect', 'getName', 'reset']) + ->getMock(); + $collector->expects($this->any())->method('getName')->willReturn('mock'); + $collector->expects($this->once())->method('reset'); + + $profiler = new Profiler($this->storage); + $profiler->add($collector); + $profiler->reset(); + } + public function testFindWorksWithDates() { $profiler = new Profiler($this->storage); diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index 7f231f3a42..c8aece0140 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -26,6 +26,7 @@ CHANGELOG requests. * deprecated HTTP digest authentication * Added a new password encoder for the Argon2i hashing algorithm + * deprecated `GuardAuthenticatorInterface` in favor of `AuthenticatorInterface` 3.3.0 ----- diff --git a/src/Symfony/Component/Security/Guard/AbstractGuardAuthenticator.php b/src/Symfony/Component/Security/Guard/AbstractGuardAuthenticator.php index 609d772e19..5eceb8470a 100644 --- a/src/Symfony/Component/Security/Guard/AbstractGuardAuthenticator.php +++ b/src/Symfony/Component/Security/Guard/AbstractGuardAuthenticator.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Guard; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken; @@ -19,8 +20,20 @@ use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken; * * @author Ryan Weaver */ -abstract class AbstractGuardAuthenticator implements GuardAuthenticatorInterface +abstract class AbstractGuardAuthenticator implements AuthenticatorInterface { + /** + * {@inheritdoc} + * + * @deprecated since version 3.4, to be removed in 4.0 + */ + public function supports(Request $request) + { + @trigger_error(sprintf('The "%s()" method is deprecated since version 3.4 and will be removed in 4.0. Implement the "%s::supports()" method in class "%s" instead.', __METHOD__, AuthenticatorInterface::class, get_class($this)), E_USER_DEPRECATED); + + return true; + } + /** * Shortcut to create a PostAuthenticationGuardToken for you, if you don't really * care about which authenticated token you're using. diff --git a/src/Symfony/Component/Security/Guard/AuthenticatorInterface.php b/src/Symfony/Component/Security/Guard/AuthenticatorInterface.php new file mode 100644 index 0000000000..f68b8d6f43 --- /dev/null +++ b/src/Symfony/Component/Security/Guard/AuthenticatorInterface.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Guard; + +use Symfony\Component\HttpFoundation\Request; + +/** + * The interface for all "guard" authenticators. + * + * The methods on this interface are called throughout the guard authentication + * process to give you the power to control most parts of the process from + * one location. + * + * @author Ryan Weaver + * @author Amaury Leroux de Lens + */ +interface AuthenticatorInterface extends GuardAuthenticatorInterface +{ + /** + * Does the authenticator support the given Request? + * + * If this returns false, the authenticator will be skipped. + * + * @param Request $request + * + * @return bool + */ + public function supports(Request $request); + + /** + * Get the authentication credentials from the request and return them + * as any type (e.g. an associate array). + * + * Whatever value you return here will be passed to getUser() and checkCredentials() + * + * For example, for a form login, you might: + * + * return array( + * 'username' => $request->request->get('_username'), + * 'password' => $request->request->get('_password'), + * ); + * + * Or for an API token that's on a header, you might use: + * + * return array('api_key' => $request->headers->get('X-API-TOKEN')); + * + * @param Request $request + * + * @return mixed Any non-null value + * + * @throws \UnexpectedValueException If null is returned + */ + public function getCredentials(Request $request); +} diff --git a/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php b/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php index 41a37e6fef..4ec0b8f326 100644 --- a/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php +++ b/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php @@ -15,9 +15,10 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\Security\Guard\GuardAuthenticatorHandler; +use Symfony\Component\Security\Guard\GuardAuthenticatorInterface; use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; -use Symfony\Component\Security\Guard\GuardAuthenticatorInterface; +use Symfony\Component\Security\Guard\AuthenticatorInterface; use Psr\Log\LoggerInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; @@ -28,6 +29,7 @@ use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; * Authentication listener for the "guard" system. * * @author Ryan Weaver + * @author Amaury Leroux de Lens */ class GuardAuthenticationListener implements ListenerInterface { @@ -39,11 +41,11 @@ class GuardAuthenticationListener implements ListenerInterface private $rememberMeServices; /** - * @param GuardAuthenticatorHandler $guardHandler The Guard handler - * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance - * @param string $providerKey The provider (i.e. firewall) key - * @param iterable|GuardAuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider - * @param LoggerInterface $logger A LoggerInterface instance + * @param GuardAuthenticatorHandler $guardHandler The Guard handler + * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance + * @param string $providerKey The provider (i.e. firewall) key + * @param iterable|AuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider + * @param LoggerInterface $logger A LoggerInterface instance */ public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, $providerKey, $guardAuthenticators, LoggerInterface $logger = null) { @@ -100,12 +102,29 @@ class GuardAuthenticationListener implements ListenerInterface $this->logger->debug('Calling getCredentials() on guard configurator.', array('firewall_key' => $this->providerKey, 'authenticator' => get_class($guardAuthenticator))); } + // abort the execution of the authenticator if it doesn't support the request + if ($guardAuthenticator instanceof AuthenticatorInterface) { + if (!$guardAuthenticator->supports($request)) { + return; + } + // as there was a support for given request, + // authenticator is expected to give not-null credentials. + $credentialsCanBeNull = false; + } else { + // deprecated since version 3.4, to be removed in 4.0 + $credentialsCanBeNull = true; + } + // allow the authenticator to fetch authentication info from the request $credentials = $guardAuthenticator->getCredentials($request); - // allow null to be returned to skip authentication if (null === $credentials) { - return; + // deprecated since version 3.4, to be removed in 4.0 + if ($credentialsCanBeNull) { + return; + } + + throw new \UnexpectedValueException(sprintf('The return value of "%s::getCredentials()" must not be null. Return false from "%s::supports()" instead.', get_class($guardAuthenticator), get_class($guardAuthenticator))); } // create a token with the unique key, so that the provider knows which authenticator to use @@ -172,10 +191,10 @@ class GuardAuthenticationListener implements ListenerInterface * Checks to see if remember me is supported in the authenticator and * on the firewall. If it is, the RememberMeServicesInterface is notified. * - * @param GuardAuthenticatorInterface $guardAuthenticator - * @param Request $request - * @param TokenInterface $token - * @param Response $response + * @param AuthenticatorInterface $guardAuthenticator + * @param Request $request + * @param TokenInterface $token + * @param Response $response */ private function triggerRememberMe(GuardAuthenticatorInterface $guardAuthenticator, Request $request, TokenInterface $token, Response $response = null) { diff --git a/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php index 5e1351dcc2..abe263d888 100644 --- a/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php +++ b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php @@ -29,6 +29,8 @@ use Symfony\Component\Security\Http\SecurityEvents; * can be called directly (e.g. for manual authentication) or overridden. * * @author Ryan Weaver + * + * @final since version 3.4 */ class GuardAuthenticatorHandler { @@ -61,10 +63,10 @@ class GuardAuthenticatorHandler /** * Returns the "on success" response for the given GuardAuthenticator. * - * @param TokenInterface $token - * @param Request $request - * @param GuardAuthenticatorInterface $guardAuthenticator - * @param string $providerKey The provider (i.e. firewall) key + * @param TokenInterface $token + * @param Request $request + * @param AuthenticatorInterface $guardAuthenticator + * @param string $providerKey The provider (i.e. firewall) key * * @return null|Response */ @@ -88,10 +90,10 @@ class GuardAuthenticatorHandler * Convenience method for authenticating the user and returning the * Response *if any* for success. * - * @param UserInterface $user - * @param Request $request - * @param GuardAuthenticatorInterface $authenticator - * @param string $providerKey The provider (i.e. firewall) key + * @param UserInterface $user + * @param Request $request + * @param AuthenticatorInterface $authenticator + * @param string $providerKey The provider (i.e. firewall) key * * @return Response|null */ @@ -110,10 +112,10 @@ class GuardAuthenticatorHandler * Handles an authentication failure and returns the Response for the * GuardAuthenticator. * - * @param AuthenticationException $authenticationException - * @param Request $request - * @param GuardAuthenticatorInterface $guardAuthenticator - * @param string $providerKey The key of the firewall + * @param AuthenticationException $authenticationException + * @param Request $request + * @param AuthenticatorInterface $guardAuthenticator + * @param string $providerKey The key of the firewall * * @return null|Response */ diff --git a/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php b/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php index 307d70f9e9..0d11573c03 100644 --- a/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php +++ b/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php @@ -28,6 +28,8 @@ use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface * one location. * * @author Ryan Weaver + * + * @deprecated since version 3.4, to be removed in 4.0. Use AuthenticatorInterface instead */ interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface { diff --git a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php index 90b9580a3a..2f2678035f 100644 --- a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php @@ -14,7 +14,7 @@ namespace Symfony\Component\Security\Guard\Provider; use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; -use Symfony\Component\Security\Guard\GuardAuthenticatorInterface; +use Symfony\Component\Security\Guard\AuthenticatorInterface; use Symfony\Component\Security\Guard\Token\GuardTokenInterface; use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; use Symfony\Component\Security\Core\User\UserCheckerInterface; @@ -32,7 +32,7 @@ use Symfony\Component\Security\Core\Exception\AuthenticationExpiredException; class GuardAuthenticationProvider implements AuthenticationProviderInterface { /** - * @var GuardAuthenticatorInterface[] + * @var AuthenticatorInterface[] */ private $guardAuthenticators; private $userProvider; @@ -40,10 +40,10 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface private $userChecker; /** - * @param iterable|GuardAuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationListener - * @param UserProviderInterface $userProvider The user provider - * @param string $providerKey The provider (i.e. firewall) key - * @param UserCheckerInterface $userChecker + * @param iterable|AuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationListener + * @param UserProviderInterface $userProvider The user provider + * @param string $providerKey The provider (i.e. firewall) key + * @param UserCheckerInterface $userChecker */ public function __construct($guardAuthenticators, UserProviderInterface $userProvider, $providerKey, UserCheckerInterface $userChecker) { @@ -101,7 +101,7 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface // instances that will be checked if you have multiple firewalls. } - private function authenticateViaGuard(GuardAuthenticatorInterface $guardAuthenticator, PreAuthenticationGuardToken $token) + private function authenticateViaGuard($guardAuthenticator, PreAuthenticationGuardToken $token) { // get the user from the GuardAuthenticator $user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider); diff --git a/src/Symfony/Component/Security/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php b/src/Symfony/Component/Security/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php index 943ca49247..5af9f130f8 100644 --- a/src/Symfony/Component/Security/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php @@ -14,12 +14,16 @@ namespace Symfony\Component\Security\Guard\Tests\Firewall; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Guard\AuthenticatorInterface; use Symfony\Component\Security\Guard\Firewall\GuardAuthenticationListener; +use Symfony\Component\Security\Guard\GuardAuthenticatorInterface; use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; use Symfony\Component\Security\Core\Exception\AuthenticationException; /** * @author Ryan Weaver + * @author Amaury Leroux de Lens */ class GuardAuthenticationListenerTest extends TestCase { @@ -32,11 +36,16 @@ class GuardAuthenticationListenerTest extends TestCase public function testHandleSuccess() { - $authenticator = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); - $authenticateToken = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); + $authenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); + $authenticateToken = $this->getMockBuilder(TokenInterface::class)->getMock(); $providerKey = 'my_firewall'; $credentials = array('username' => 'weaverryan', 'password' => 'all_your_base'); + + $authenticator + ->expects($this->once()) + ->method('supports') + ->willReturn(true); $authenticator ->expects($this->once()) ->method('getCredentials') @@ -82,10 +91,14 @@ class GuardAuthenticationListenerTest extends TestCase public function testHandleSuccessStopsAfterResponseIsSet() { - $authenticator1 = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); - $authenticator2 = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); + $authenticator1 = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); + $authenticator2 = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); // mock the first authenticator to fail, and set a Response + $authenticator1 + ->expects($this->once()) + ->method('supports') + ->willReturn(true); $authenticator1 ->expects($this->once()) ->method('getCredentials') @@ -112,10 +125,15 @@ class GuardAuthenticationListenerTest extends TestCase public function testHandleSuccessWithRememberMe() { - $authenticator = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); - $authenticateToken = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); + $authenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); + $authenticateToken = $this->getMockBuilder(TokenInterface::class)->getMock(); $providerKey = 'my_firewall_with_rememberme'; + $authenticator + ->expects($this->once()) + ->method('supports') + ->with($this->equalTo($this->request)) + ->willReturn(true); $authenticator ->expects($this->once()) ->method('getCredentials') @@ -155,10 +173,14 @@ class GuardAuthenticationListenerTest extends TestCase public function testHandleCatchesAuthenticationException() { - $authenticator = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); + $authenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); $providerKey = 'my_firewall2'; $authException = new AuthenticationException('Get outta here crazy user with a bad password!'); + $authenticator + ->expects($this->once()) + ->method('supports') + ->willReturn(true); $authenticator ->expects($this->once()) ->method('getCredentials') @@ -185,6 +207,96 @@ class GuardAuthenticationListenerTest extends TestCase $listener->handle($this->event); } + /** + * @group legacy + */ + public function testLegacyInterfaceNullCredentials() + { + $authenticatorA = $this->getMockBuilder(GuardAuthenticatorInterface::class)->getMock(); + $providerKey = 'my_firewall3'; + + $authenticatorA + ->expects($this->once()) + ->method('getCredentials') + ->will($this->returnValue(null)); + + // this is not called + $this->authenticationManager + ->expects($this->never()) + ->method('authenticate'); + + $this->guardAuthenticatorHandler + ->expects($this->never()) + ->method('handleAuthenticationSuccess'); + + $listener = new GuardAuthenticationListener( + $this->guardAuthenticatorHandler, + $this->authenticationManager, + $providerKey, + array($authenticatorA), + $this->logger + ); + + $listener->handle($this->event); + } + + /** + * @group legacy + */ + public function testLegacyInterfaceKeepsWorking() + { + $authenticator = $this->getMockBuilder(GuardAuthenticatorInterface::class)->getMock(); + $authenticateToken = $this->getMockBuilder(TokenInterface::class)->getMock(); + $providerKey = 'my_firewall'; + + $credentials = array('username' => 'weaverryan', 'password' => 'all_your_base'); + + $authenticator + ->expects($this->once()) + ->method('getCredentials') + ->with($this->equalTo($this->request)) + ->will($this->returnValue($credentials)); + + // a clone of the token that should be created internally + $uniqueGuardKey = 'my_firewall_0'; + $nonAuthedToken = new PreAuthenticationGuardToken($credentials, $uniqueGuardKey); + + $this->authenticationManager + ->expects($this->once()) + ->method('authenticate') + ->with($this->equalTo($nonAuthedToken)) + ->will($this->returnValue($authenticateToken)); + + $this->guardAuthenticatorHandler + ->expects($this->once()) + ->method('authenticateWithToken') + ->with($authenticateToken, $this->request); + + $this->guardAuthenticatorHandler + ->expects($this->once()) + ->method('handleAuthenticationSuccess') + ->with($authenticateToken, $this->request, $authenticator, $providerKey); + + $listener = new GuardAuthenticationListener( + $this->guardAuthenticatorHandler, + $this->authenticationManager, + $providerKey, + array($authenticator), + $this->logger + ); + + $listener->setRememberMeServices($this->rememberMeServices); + // should never be called - our handleAuthenticationSuccess() does not return a Response + $this->rememberMeServices + ->expects($this->never()) + ->method('loginSuccess'); + + $listener->handle($this->event); + } + + /** + * @group legacy + */ public function testReturnNullToSkipAuth() { $authenticatorA = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); @@ -220,6 +332,62 @@ class GuardAuthenticationListenerTest extends TestCase $listener->handle($this->event); } + public function testSupportsReturnFalseSkipAuth() + { + $authenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); + $providerKey = 'my_firewall4'; + + $authenticator + ->expects($this->once()) + ->method('supports') + ->will($this->returnValue(false)); + + // this is not called + $authenticator + ->expects($this->never()) + ->method('getCredentials'); + + $listener = new GuardAuthenticationListener( + $this->guardAuthenticatorHandler, + $this->authenticationManager, + $providerKey, + array($authenticator), + $this->logger + ); + + $listener->handle($this->event); + } + + /** + * @expectedException \UnexpectedValueException + */ + public function testReturnNullFromGetCredentials() + { + $authenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); + $providerKey = 'my_firewall4'; + + $authenticator + ->expects($this->once()) + ->method('supports') + ->will($this->returnValue(true)); + + // this will raise exception + $authenticator + ->expects($this->once()) + ->method('getCredentials') + ->will($this->returnValue(null)); + + $listener = new GuardAuthenticationListener( + $this->guardAuthenticatorHandler, + $this->authenticationManager, + $providerKey, + array($authenticator), + $this->logger + ); + + $listener->handle($this->event); + } + protected function setUp() { $this->authenticationManager = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager') diff --git a/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php b/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php index b46bc4a78d..c67f38e9ef 100644 --- a/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php @@ -14,6 +14,7 @@ namespace Symfony\Component\Security\Guard\Tests; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Guard\AuthenticatorInterface; use Symfony\Component\Security\Guard\GuardAuthenticatorHandler; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; @@ -128,7 +129,7 @@ class GuardAuthenticatorHandlerTest extends TestCase $this->dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock(); $this->token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); $this->request = new Request(array(), array(), array(), array(), array(), array()); - $this->guardAuthenticator = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); + $this->guardAuthenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); } protected function tearDown() diff --git a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php index ed3920533f..9d8301fce8 100644 --- a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php @@ -12,6 +12,9 @@ namespace Symfony\Component\Security\Guard\Tests\Provider; use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Guard\AuthenticatorInterface; use Symfony\Component\Security\Guard\Provider\GuardAuthenticationProvider; use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken; @@ -28,6 +31,68 @@ class GuardAuthenticationProviderTest extends TestCase { $providerKey = 'my_cool_firewall'; + $authenticatorA = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); + $authenticatorB = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); + $authenticatorC = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); + $authenticators = array($authenticatorA, $authenticatorB, $authenticatorC); + + // called 2 times - for authenticator A and B (stops on B because of match) + $this->preAuthenticationToken->expects($this->exactly(2)) + ->method('getGuardProviderKey') + // it will return the "1" index, which will match authenticatorB + ->will($this->returnValue('my_cool_firewall_1')); + + $enteredCredentials = array( + 'username' => '_weaverryan_test_user', + 'password' => 'guard_auth_ftw', + ); + $this->preAuthenticationToken->expects($this->atLeastOnce()) + ->method('getCredentials') + ->will($this->returnValue($enteredCredentials)); + + // authenticators A and C are never called + $authenticatorA->expects($this->never()) + ->method('getUser'); + $authenticatorC->expects($this->never()) + ->method('getUser'); + + $mockedUser = $this->getMockBuilder(UserInterface::class)->getMock(); + $authenticatorB->expects($this->once()) + ->method('getUser') + ->with($enteredCredentials, $this->userProvider) + ->will($this->returnValue($mockedUser)); + // checkCredentials is called + $authenticatorB->expects($this->once()) + ->method('checkCredentials') + ->with($enteredCredentials, $mockedUser) + // authentication works! + ->will($this->returnValue(true)); + $authedToken = $this->getMockBuilder(TokenInterface::class)->getMock(); + $authenticatorB->expects($this->once()) + ->method('createAuthenticatedToken') + ->with($mockedUser, $providerKey) + ->will($this->returnValue($authedToken)); + + // user checker should be called + $this->userChecker->expects($this->once()) + ->method('checkPreAuth') + ->with($mockedUser); + $this->userChecker->expects($this->once()) + ->method('checkPostAuth') + ->with($mockedUser); + + $provider = new GuardAuthenticationProvider($authenticators, $this->userProvider, $providerKey, $this->userChecker); + $actualAuthedToken = $provider->authenticate($this->preAuthenticationToken); + $this->assertSame($authedToken, $actualAuthedToken); + } + + /** + * @group legacy + */ + public function testLegacyAuthenticate() + { + $providerKey = 'my_cool_firewall'; + $authenticatorA = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); $authenticatorB = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); $authenticatorC = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); diff --git a/src/Symfony/Component/Security/Guard/Token/PreAuthenticationGuardToken.php b/src/Symfony/Component/Security/Guard/Token/PreAuthenticationGuardToken.php index abbe985c9e..e1b39417d7 100644 --- a/src/Symfony/Component/Security/Guard/Token/PreAuthenticationGuardToken.php +++ b/src/Symfony/Component/Security/Guard/Token/PreAuthenticationGuardToken.php @@ -29,7 +29,7 @@ class PreAuthenticationGuardToken extends AbstractToken implements GuardTokenInt /** * @param mixed $credentials - * @param string $guardProviderKey Unique key that bind this token to a specific GuardAuthenticatorInterface + * @param string $guardProviderKey Unique key that bind this token to a specific AuthenticatorInterface */ public function __construct($credentials, $guardProviderKey) { diff --git a/src/Symfony/Component/Translation/DataCollector/TranslationDataCollector.php b/src/Symfony/Component/Translation/DataCollector/TranslationDataCollector.php index 1696fc71ff..da45cce31e 100644 --- a/src/Symfony/Component/Translation/DataCollector/TranslationDataCollector.php +++ b/src/Symfony/Component/Translation/DataCollector/TranslationDataCollector.php @@ -58,6 +58,14 @@ class TranslationDataCollector extends DataCollector implements LateDataCollecto { } + /** + * {@inheritdoc} + */ + public function reset() + { + $this->data = array(); + } + /** * @return array */ diff --git a/src/Symfony/Component/Validator/DataCollector/ValidatorDataCollector.php b/src/Symfony/Component/Validator/DataCollector/ValidatorDataCollector.php index d2b3f46262..aa8c61f96b 100644 --- a/src/Symfony/Component/Validator/DataCollector/ValidatorDataCollector.php +++ b/src/Symfony/Component/Validator/DataCollector/ValidatorDataCollector.php @@ -19,6 +19,7 @@ use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface; use Symfony\Component\Validator\Validator\TraceableValidator; use Symfony\Component\VarDumper\Caster\Caster; use Symfony\Component\VarDumper\Caster\ClassStub; +use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Cloner\Stub; /** @@ -31,10 +32,7 @@ class ValidatorDataCollector extends DataCollector implements LateDataCollectorI public function __construct(TraceableValidator $validator) { $this->validator = $validator; - $this->data = array( - 'calls' => array(), - 'violations_count' => 0, - ); + $this->reset(); } /** @@ -45,6 +43,15 @@ class ValidatorDataCollector extends DataCollector implements LateDataCollectorI // Everything is collected once, on kernel terminate. } + public function reset() + { + $this->validator->reset(); + $this->data = array( + 'calls' => $this->cloneVar(array()), + 'violations_count' => 0, + ); + } + /** * {@inheritdoc} */ @@ -52,16 +59,22 @@ class ValidatorDataCollector extends DataCollector implements LateDataCollectorI { $collected = $this->validator->getCollectedData(); $this->data['calls'] = $this->cloneVar($collected); - $this->data['violations_count'] += array_reduce($collected, function ($previous, $item) { - return $previous += count($item['violations']); + $this->data['violations_count'] = array_reduce($collected, function ($previous, $item) { + return $previous + count($item['violations']); }, 0); } + /** + * @return Data + */ public function getCalls() { return $this->data['calls']; } + /** + * @return int + */ public function getViolationsCount() { return $this->data['violations_count']; diff --git a/src/Symfony/Component/Validator/Tests/DataCollector/ValidatorDataCollectorTest.php b/src/Symfony/Component/Validator/Tests/DataCollector/ValidatorDataCollectorTest.php index 811a55829a..c078d28350 100644 --- a/src/Symfony/Component/Validator/Tests/DataCollector/ValidatorDataCollectorTest.php +++ b/src/Symfony/Component/Validator/Tests/DataCollector/ValidatorDataCollectorTest.php @@ -50,6 +50,33 @@ class ValidatorDataCollectorTest extends TestCase $this->assertCount(2, $call['violations']); } + public function testReset() + { + $originalValidator = $this->createMock(ValidatorInterface::class); + $validator = new TraceableValidator($originalValidator); + + $collector = new ValidatorDataCollector($validator); + + $violations = new ConstraintViolationList(array( + $this->createMock(ConstraintViolation::class), + $this->createMock(ConstraintViolation::class), + )); + $originalValidator->method('validate')->willReturn($violations); + + $validator->validate(new \stdClass()); + + $collector->lateCollect(); + $collector->reset(); + + $this->assertCount(0, $collector->getCalls()); + $this->assertSame(0, $collector->getViolationsCount()); + + $collector->lateCollect(); + + $this->assertCount(0, $collector->getCalls()); + $this->assertSame(0, $collector->getViolationsCount()); + } + protected function createMock($classname) { return $this->getMockBuilder($classname)->disableOriginalConstructor()->getMock(); diff --git a/src/Symfony/Component/Validator/Validator/TraceableValidator.php b/src/Symfony/Component/Validator/Validator/TraceableValidator.php index 019559ae00..96134e2767 100644 --- a/src/Symfony/Component/Validator/Validator/TraceableValidator.php +++ b/src/Symfony/Component/Validator/Validator/TraceableValidator.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Validator\Validator; -use Symfony\Component\Validator\ConstraintViolationList; use Symfony\Component\Validator\Context\ExecutionContextInterface; /** @@ -30,13 +29,18 @@ class TraceableValidator implements ValidatorInterface } /** - * @return ConstraintViolationList[] + * @return array */ public function getCollectedData() { return $this->collectedData; } + public function reset() + { + $this->collectedData = array(); + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/VarDumper/CHANGELOG.md b/src/Symfony/Component/VarDumper/CHANGELOG.md index 254b74fb52..6ece28fcb6 100644 --- a/src/Symfony/Component/VarDumper/CHANGELOG.md +++ b/src/Symfony/Component/VarDumper/CHANGELOG.md @@ -16,6 +16,7 @@ CHANGELOG ----- * added `AbstractCloner::setMinDepth()` function to ensure minimum tree depth + * deprecated `MongoCaster` 2.7.0 ----- diff --git a/src/Symfony/Component/VarDumper/Caster/MongoCaster.php b/src/Symfony/Component/VarDumper/Caster/MongoCaster.php index 92258f06fa..2219386bc2 100644 --- a/src/Symfony/Component/VarDumper/Caster/MongoCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/MongoCaster.php @@ -13,10 +13,14 @@ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; +@trigger_error('The '.__NAMESPACE__.'\MongoCaster class is deprecated since version 3.4 and will be removed in 4.0.', E_USER_DEPRECATED); + /** * Casts classes from the MongoDb extension to array representation. * * @author Nicolas Grekas + * + * @deprecated since version 3.4, to be removed in 4.0. */ class MongoCaster { diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 6d3e0ecd65..3eea19a3df 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -39,14 +39,17 @@ class Inline * @param int|null $parsedLineNumber * @param string|null $parsedFilename */ - public static function initialize($flags, $parsedLineNumber = 0, $parsedFilename = null) + public static function initialize($flags, $parsedLineNumber = null, $parsedFilename = null) { self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags); self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags); self::$objectForMap = (bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags); self::$constantSupport = (bool) (Yaml::PARSE_CONSTANT & $flags); self::$parsedFilename = $parsedFilename; - self::$parsedLineNumber = $parsedLineNumber; + + if (null !== $parsedLineNumber) { + self::$parsedLineNumber = $parsedLineNumber; + } } /** diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index a48f3bb8ec..ceea93772c 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -158,6 +158,8 @@ class Parser throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } + Inline::initialize($flags, $this->getRealCurrentLineNb(), $this->filename); + $isRef = $mergeNode = false; if (self::preg_match('#^\-((?P\s+)(?P.+))?$#u', rtrim($this->currentLine), $values)) { if ($context && 'mapping' == $context) { @@ -209,7 +211,6 @@ class Parser } $context = 'mapping'; - Inline::initialize($flags, $this->getRealCurrentLineNb(), $this->filename); try { $key = Inline::parseScalar($values['key']); } catch (ParseException $e) { @@ -228,7 +229,7 @@ class Parser $key = (string) $key; } - if ('<<' === $key) { + if ('<<' === $key && (!isset($values['value']) || !self::preg_match('#^&(?P[^ ]+)#u', $values['value'], $refMatches))) { $mergeNode = true; $allowOverwrite = true; if (isset($values['value'][0]) && '*' === $values['value'][0]) { @@ -285,7 +286,7 @@ class Parser $data += $parsed; // array union } } - } elseif (isset($values['value']) && self::preg_match('#^&(?P[^ ]++) *+(?P.*)#u', $values['value'], $matches)) { + } elseif ('<<' !== $key && isset($values['value']) && self::preg_match('#^&(?P[^ ]++) *+(?P.*)#u', $values['value'], $matches)) { $isRef = $matches['ref']; $values['value'] = $matches['value']; } @@ -293,7 +294,7 @@ class Parser $subTag = null; if ($mergeNode) { // Merge keys - } elseif (!isset($values['value']) || '' === $values['value'] || 0 === strpos($values['value'], '#') || (null !== $subTag = $this->getLineTag($values['value'], $flags))) { + } elseif (!isset($values['value']) || '' === $values['value'] || 0 === strpos($values['value'], '#') || (null !== $subTag = $this->getLineTag($values['value'], $flags)) || '<<' === $key) { // hash // if next line is less indented or equal, then it means that the current value is null if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) { @@ -312,9 +313,12 @@ class Parser // remember the parsed line number here in case we need it to provide some contexts in error messages below $realCurrentLineNbKey = $this->getRealCurrentLineNb(); $value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $flags); - // Spec: Keys MUST be unique; first one wins. - // But overwriting is allowed when a merge node is used in current block. - if ($allowOverwrite || !isset($data[$key])) { + if ('<<' === $key) { + $this->refs[$refMatches['ref']] = $value; + $data += $value; + } elseif ($allowOverwrite || !isset($data[$key])) { + // Spec: Keys MUST be unique; first one wins. + // But overwriting is allowed when a merge node is used in current block. if (null !== $subTag) { $data[$key] = new TaggedValue($subTag, $value); } else { @@ -350,7 +354,6 @@ class Parser // 1-liner optionally followed by newline(s) if (is_string($value) && $this->lines[0] === trim($value)) { try { - Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); $value = Inline::parse($this->lines[0], $flags, $this->refs); } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); @@ -366,6 +369,7 @@ class Parser if (0 === $this->currentLineNb) { $parseError = false; $previousLineWasNewline = false; + $previousLineWasTerminatedWithBackslash = false; $value = ''; foreach ($this->lines as $line) { @@ -383,13 +387,25 @@ class Parser if ('' === trim($parsedLine)) { $value .= "\n"; - $previousLineWasNewline = true; - } elseif ($previousLineWasNewline) { + } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) { + $value .= ' '; + } + + if ('' !== trim($parsedLine) && '\\' === substr($parsedLine, -1)) { + $value .= ltrim(substr($parsedLine, 0, -1)); + } elseif ('' !== trim($parsedLine)) { $value .= trim($parsedLine); + } + + if ('' === trim($parsedLine)) { + $previousLineWasNewline = true; + $previousLineWasTerminatedWithBackslash = false; + } elseif ('\\' === substr($parsedLine, -1)) { $previousLineWasNewline = false; + $previousLineWasTerminatedWithBackslash = true; } else { - $value .= ' '.trim($parsedLine); $previousLineWasNewline = false; + $previousLineWasTerminatedWithBackslash = false; } } catch (ParseException $e) { $parseError = true; @@ -398,7 +414,7 @@ class Parser } if (!$parseError) { - return trim($value); + return Inline::parse(trim($value)); } } @@ -690,7 +706,6 @@ class Parser } } - 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 13e916146d..93ccfacb2a 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -1422,6 +1422,17 @@ EOT; $this->assertSame(array('foo' => 'bar baz foobar foo', 'bar' => 'baz'), $this->parser->parse($yaml)); } + public function testMultiLineQuotedStringWithTrailingBackslash() + { + $yaml = <<assertSame(array('foobar' => 'foobar'), $this->parser->parse($yaml)); + } + public function testParseMultiLineUnquotedString() { $yaml = <<parser->parseFile($file); } + + public function testParseReferencesOnMergeKeys() + { + $yaml = << array( + 'a' => 'foo', + 'b' => 'bar', + 'c' => 'baz', + ), + 'mergekeyderef' => array( + 'd' => 'quux', + 'b' => 'bar', + 'c' => 'baz', + ), + ); + + $this->assertSame($expected, $this->parser->parse($yaml)); + } } class B