From 97e8d68a05e1457950920c2e1c05d2a20392f918 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 30 Jun 2018 09:08:55 +0200 Subject: [PATCH 01/11] [DI] Fix dumping ignore-on-uninitialized references to synthetic services --- .../DependencyInjection/Dumper/PhpDumper.php | 6 +++-- .../Tests/ContainerBuilderTest.php | 15 ++++++++++++ .../Tests/Dumper/PhpDumperTest.php | 23 +++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 7c493f62e3..d7a345bd11 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -1898,8 +1898,10 @@ EOF; return '$this'; } - if ($this->container->hasDefinition($id) && ($definition = $this->container->getDefinition($id)) && !$definition->isSynthetic()) { - if (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) { + if ($this->container->hasDefinition($id) && $definition = $this->container->getDefinition($id)) { + if ($definition->isSynthetic()) { + $code = sprintf('$this->get(\'%s\'%s)', $id, null !== $reference ? ', '.$reference->getInvalidBehavior() : ''); + } elseif (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) { $code = 'null'; if (!$definition->isShared()) { return $code; diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index f2e8368455..d8e7203028 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -1434,6 +1434,21 @@ class ContainerBuilderTest extends TestCase $this->assertSame('via-argument', $container->get('foo')->class1->identifier); $this->assertSame('via-bindings', $container->get('foo')->class2->identifier); } + + public function testUninitializedSyntheticReference() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setPublic(true)->setSynthetic(true); + $container->register('bar', 'stdClass')->setPublic(true)->setShared(false) + ->setProperty('foo', new Reference('foo', ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)); + + $container->compile(); + + $this->assertEquals((object) array('foo' => null), $container->get('bar')); + + $container->set('foo', (object) array(123)); + $this->assertEquals((object) array('foo' => (object) array(123)), $container->get('bar')); + } } class FooClass diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 319561cf38..161bf18aaf 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -913,6 +913,29 @@ class PhpDumperTest extends TestCase $this->assertInstanceOf('stdClass', $container->get('bar')); } + public function testUninitializedSyntheticReference() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setPublic(true)->setSynthetic(true); + $container->register('bar', 'stdClass')->setPublic(true)->setShared(false) + ->setProperty('foo', new Reference('foo', ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE)); + + $container->compile(); + + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array( + 'class' => 'Symfony_DI_PhpDumper_Test_UninitializedSyntheticReference', + 'inline_class_loader_parameter' => 'inline_requires', + ))); + + $container = new \Symfony_DI_PhpDumper_Test_UninitializedSyntheticReference(); + + $this->assertEquals((object) array('foo' => null), $container->get('bar')); + + $container->set('foo', (object) array(123)); + $this->assertEquals((object) array('foo' => (object) array(123)), $container->get('bar')); + } + /** * @group legacy * @expectedDeprecation The "private" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead. From b24acb0579a26e0da68b0125a7ef6fe0d009bc27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Wed, 4 Jul 2018 18:09:16 +0200 Subject: [PATCH 02/11] [Workflow] Fixed BC break --- src/Symfony/Component/Workflow/Event/Event.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Workflow/Event/Event.php b/src/Symfony/Component/Workflow/Event/Event.php index a9e2086968..f77a20f778 100644 --- a/src/Symfony/Component/Workflow/Event/Event.php +++ b/src/Symfony/Component/Workflow/Event/Event.php @@ -40,7 +40,10 @@ class Event extends BaseEvent $this->subject = $subject; $this->marking = $marking; $this->transition = $transition; - if (is_string($workflow)) { + if (null === $workflow) { + @trigger_error(sprintf('Passing only three parameters to "%s" is deprecated since Symfony 4.1. Pass a %s instance as fourth parameter instead.', __METHOD__, WorkflowInterface::class), E_USER_DEPRECATED); + $this->workflowName = 'unnamed'; + } elseif (is_string($workflow)) { @trigger_error(sprintf('Passing a string as 4th parameter of "%s" is deprecated since Symfony 4.1. Pass a %s instance instead.', __METHOD__, WorkflowInterface::class), E_USER_DEPRECATED); $this->workflowName = $workflow; } elseif ($workflow instanceof WorkflowInterface) { From b4552373c15c0f3393fe74016159d43df94e9624 Mon Sep 17 00:00:00 2001 From: Valentin Date: Fri, 6 Jul 2018 11:57:10 +0300 Subject: [PATCH 03/11] AppBundle->App. --- .../SecurityBundle/Command/UserPasswordEncoderCommand.php | 8 ++++---- .../DependencyInjection/MainConfiguration.php | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php b/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php index cea8bd2e15..d5d03c0f71 100644 --- a/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php +++ b/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php @@ -75,7 +75,7 @@ Suppose that you have the following security configuration in your application: security: encoders: Symfony\Component\Security\Core\User\User: plaintext - AppBundle\Entity\User: bcrypt + App\Entity\User: bcrypt If you execute the command non-interactively, the first available configured @@ -87,16 +87,16 @@ generated to encode the password: Pass the full user class path as the second argument to encode passwords for your own entities: - php %command.full_name% --no-interaction [password] AppBundle\Entity\User + php %command.full_name% --no-interaction [password] App\Entity\User Executing the command interactively allows you to generate a random salt for encoding the password: - php %command.full_name% [password] AppBundle\Entity\User + php %command.full_name% [password] App\Entity\User In case your encoder doesn't require a salt, add the empty-salt option: - php %command.full_name% --empty-salt [password] AppBundle\Entity\User + php %command.full_name% --empty-salt [password] App\Entity\User EOF ) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php index a514ab04a8..0948b63442 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php @@ -410,8 +410,8 @@ class MainConfiguration implements ConfigurationInterface ->children() ->arrayNode('encoders') ->example(array( - 'AppBundle\Entity\User1' => 'bcrypt', - 'AppBundle\Entity\User2' => array( + 'App\Entity\User1' => 'bcrypt', + 'App\Entity\User2' => array( 'algorithm' => 'bcrypt', 'cost' => 13, ), From ddea90e97d47e6e69f0d9998b56191e5699f484d Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Fri, 6 Jul 2018 11:08:29 +0200 Subject: [PATCH 04/11] minor #27858 [Console] changed warning verbosity; fixes typo (adrian-enspired) This PR was merged into the 4.2-dev branch. Discussion ---------- [Console] changed warning verbosity; fixes typo | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes* | Fixed tickets | n/a | License | MIT | Doc PR | n/a * Tests pass, but I do not have an installation of MacOS to run tests on. Tests should be unaffected (the test is simply [skipped on MacOS](https://github.com/symfony/console/blob/master/Tests/Command/CommandTest.php#L345)). When a Console Command fails to change the process title on MacOS, a warning is issued to output. This warning is relevant to developers of Console applications, but to end users is largely meaningless and potentially confusing. This PR changes the verbosity of the warning to "very verbose" so it does not interrupt normal usage. I've also fixed a typo in the message ("get" vs. "set"). Commits ------- 86c771a changed warning verbosity; fixes typo --- src/Symfony/Component/Console/Command/Command.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index c8db0cff07..b1f31ce794 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -210,7 +210,10 @@ class Command if (function_exists('cli_set_process_title')) { if (!@cli_set_process_title($this->processTitle)) { if ('Darwin' === PHP_OS) { - $output->writeln('Running "cli_get_process_title" as an unprivileged user is not supported on MacOS.'); + $output->writeln( + 'Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.', + OutputInterface::VERBOSITY_VERY_VERBOSE + ); } else { cli_set_process_title($this->processTitle); } From 39cb2a99bad09ed82e199429bab269ccb8769ee1 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 5 Jul 2018 19:06:52 +0200 Subject: [PATCH 05/11] improve deprecation messages --- src/Symfony/Bundle/TwigBundle/Extension/ActionsExtension.php | 2 -- .../Component/Serializer/Normalizer/AbstractNormalizer.php | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Bundle/TwigBundle/Extension/ActionsExtension.php b/src/Symfony/Bundle/TwigBundle/Extension/ActionsExtension.php index 1cd134fbc7..ef259b338d 100644 --- a/src/Symfony/Bundle/TwigBundle/Extension/ActionsExtension.php +++ b/src/Symfony/Bundle/TwigBundle/Extension/ActionsExtension.php @@ -38,8 +38,6 @@ class ActionsExtension extends AbstractExtension if ($handler instanceof FragmentHandler) { $this->handler = $handler; } elseif ($handler instanceof ContainerInterface) { - @trigger_error('The ability to pass a ContainerInterface instance as a first argument to '.__METHOD__.' method is deprecated since Symfony 2.7 and will be removed in 3.0. Pass a FragmentHandler instance instead.', E_USER_DEPRECATED); - $this->handler = $handler->get('fragment.handler'); } else { throw new \BadFunctionCallException(sprintf('%s takes a FragmentHandler or a ContainerInterface object as its first argument.', __METHOD__)); diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 88f5f9273d..b1975122d1 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -158,7 +158,7 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N */ public function setCamelizedAttributes(array $camelizedAttributes) { - @trigger_error(sprintf('%s is deprecated since Symfony 2.7 and will be removed in 3.0. Use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 2.7 and will be removed in 3.0. Use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter instead.', __METHOD__), E_USER_DEPRECATED); if ($this->nameConverter && !$this->nameConverter instanceof CamelCaseToSnakeCaseNameConverter) { throw new LogicException(sprintf('%s cannot be called if a custom Name Converter is defined.', __METHOD__)); @@ -237,7 +237,7 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N */ protected function formatAttribute($attributeName) { - @trigger_error(sprintf('%s is deprecated since Symfony 2.7 and will be removed in 3.0. Use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter instead.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 2.7 and will be removed in 3.0. Use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter instead.', __METHOD__), E_USER_DEPRECATED); return $this->nameConverter ? $this->nameConverter->normalize($attributeName) : $attributeName; } From d28949b8469dc948ceb55dde8b9b6b1dee687327 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 7 Jul 2018 11:30:05 +0200 Subject: [PATCH 06/11] [HttpFoundation] don't encode cookie name for BC --- src/Symfony/Component/HttpFoundation/Response.php | 7 ++++++- .../Fixtures/response-functional/cookie_urlencode.expected | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index ba2ebbb11c..7c1edd5a5e 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -333,12 +333,17 @@ class Response } // headers - foreach ($this->headers->allPreserveCase() as $name => $values) { + foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) { foreach ($values as $value) { header($name.': '.$value, false, $this->statusCode); } } + // cookies + foreach ($this->headers->getCookies() as $cookie) { + header('Set-Cookie: '.$cookie->getName().strstr($cookie, '='), false, $this->statusCode); + } + // status header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode); diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.expected b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.expected index 4e9c4c075f..14e44a398a 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.expected +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/cookie_urlencode.expected @@ -4,7 +4,7 @@ Array [0] => Content-Type: text/plain; charset=utf-8 [1] => Cache-Control: no-cache, private [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT - [3] => Set-Cookie: %3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/ + [3] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/ [4] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/ ) shutdown From 948c841acf30b7d471b5d985ba0cb7dadd645c56 Mon Sep 17 00:00:00 2001 From: Valentin Date: Fri, 6 Jul 2018 11:59:10 +0300 Subject: [PATCH 07/11] [FrameworkBundle] Fixed phpdoc in MicroKernelTrait::configureRoutes() --- src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php b/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php index ab6163e3ef..5130071bcd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php @@ -28,7 +28,7 @@ trait MicroKernelTrait * Add or import routes into your application. * * $routes->import('config/routing.yml'); - * $routes->add('/admin', 'AppBundle:Admin:dashboard', 'admin_dashboard'); + * $routes->add('/admin', 'App\Controller\AdminController::dashboard', 'admin_dashboard'); * * @param RouteCollectionBuilder $routes */ From b1a612087f92c1981976c478b9bfc68a9795410f Mon Sep 17 00:00:00 2001 From: DerManoMann Date: Wed, 4 Jul 2018 16:15:49 +1200 Subject: [PATCH 08/11] [PropertyInfo] Fix dock block lookup fallback loop --- .../Extractor/PhpDocExtractor.php | 40 ++++++------ .../Tests/Extractor/PhpDocExtractorTest.php | 23 +++++++ .../Tests/Fixtures/DockBlockFallback.php | 64 +++++++++++++++++++ 3 files changed, 109 insertions(+), 18 deletions(-) create mode 100644 src/Symfony/Component/PropertyInfo/Tests/Fixtures/DockBlockFallback.php diff --git a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php index 5c3475ed0e..c500d5fea8 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php @@ -169,25 +169,21 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property $ucFirstProperty = ucfirst($property); - try { - switch (true) { - case $docBlock = $this->getDocBlockFromProperty($class, $property): - $data = array($docBlock, self::PROPERTY, null); - break; + switch (true) { + case $docBlock = $this->getDocBlockFromProperty($class, $property): + $data = array($docBlock, self::PROPERTY, null); + break; - case list($docBlock) = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR): - $data = array($docBlock, self::ACCESSOR, null); - break; + case list($docBlock) = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR): + $data = array($docBlock, self::ACCESSOR, null); + break; - case list($docBlock, $prefix) = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR): - $data = array($docBlock, self::MUTATOR, $prefix); - break; + case list($docBlock, $prefix) = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR): + $data = array($docBlock, self::MUTATOR, $prefix); + break; - default: - $data = array(null, null, null); - } - } catch (\InvalidArgumentException $e) { - $data = array(null, null, null); + default: + $data = array(null, null, null); } return $this->docBlocks[$propertyHash] = $data; @@ -210,7 +206,11 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property return; } - return $this->docBlockFactory->create($reflectionProperty, $this->contextFactory->createFromReflector($reflectionProperty->getDeclaringClass())); + try { + return $this->docBlockFactory->create($reflectionProperty, $this->contextFactory->createFromReflector($reflectionProperty->getDeclaringClass())); + } catch (\InvalidArgumentException $e) { + return null; + } } /** @@ -251,6 +251,10 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property return; } - return array($this->docBlockFactory->create($reflectionMethod, $this->contextFactory->createFromReflector($reflectionMethod)), $prefix); + try { + return array($this->docBlockFactory->create($reflectionMethod, $this->contextFactory->createFromReflector($reflectionMethod)), $prefix); + } catch (\InvalidArgumentException $e) { + return null; + } } } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php index 0b769c4531..080df2893a 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php @@ -184,6 +184,29 @@ class PhpDocExtractorTest extends TestCase { $this->assertNull($this->extractor->getShortDescription(EmptyDocBlock::class, 'foo')); } + + public function dockBlockFallbackTypesProvider() + { + return array( + 'pub' => array( + 'pub', array(new Type(Type::BUILTIN_TYPE_STRING)), + ), + 'protAcc' => array( + 'protAcc', array(new Type(Type::BUILTIN_TYPE_INT)), + ), + 'protMut' => array( + 'protMut', array(new Type(Type::BUILTIN_TYPE_BOOL)), + ), + ); + } + + /** + * @dataProvider dockBlockFallbackTypesProvider + */ + public function testDocBlockFallback($property, $types) + { + $this->assertEquals($types, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\DockBlockFallback', $property)); + } } class EmptyDocBlock diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DockBlockFallback.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DockBlockFallback.php new file mode 100644 index 0000000000..3f9c303b59 --- /dev/null +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DockBlockFallback.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyInfo\Tests\Fixtures; + +/** + * PhpDocExtractor should fallback from property -> accessor -> mutator when looking up dockblocks. + * + * @author Martin Rademacher + */ +class DockBlockFallback +{ + /** @var string $pub */ + public $pub = 'pub'; + + protected $protAcc; + protected $protMut; + + public function getPub() + { + return $this->pub; + } + + public function setPub($pub) + { + $this->pub = $pub; + } + + /** + * @return int + */ + public function getProtAcc() + { + return $this->protAcc; + } + + public function setProt($protAcc) + { + $this->protAcc = $protAcc; + } + + public function getProtMut() + { + return $this->protMut; + } + + /** + * @param bool $protMut + */ + public function setProtMut($protMut) + { + $this->protMut = $protMut; + } +} From ba7a4ca86345cf345f3eede6df4605f4f235753c Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Tue, 26 Jun 2018 07:18:26 -0400 Subject: [PATCH 09/11] [TwigBridge] Fix missing path and separators in loader paths list on debug:twig output --- .../Bridge/Twig/Command/DebugCommand.php | 14 +++- .../Twig/Tests/Command/DebugCommandTest.php | 81 +++++++++++++++++++ 2 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php diff --git a/src/Symfony/Bridge/Twig/Command/DebugCommand.php b/src/Symfony/Bridge/Twig/Command/DebugCommand.php index 1e45cf4d28..66ed296c3f 100644 --- a/src/Symfony/Bridge/Twig/Command/DebugCommand.php +++ b/src/Symfony/Bridge/Twig/Command/DebugCommand.php @@ -151,19 +151,27 @@ EOF } $rows = array(); + $firstNamespace = true; + $prevHasSeparator = false; foreach ($this->getLoaderPaths() as $namespace => $paths) { - if (count($paths) > 1) { + if (!$firstNamespace && !$prevHasSeparator && count($paths) > 1) { $rows[] = array('', ''); } + $firstNamespace = false; foreach ($paths as $path) { - $rows[] = array($namespace, '- '.$path); + $rows[] = array($namespace, $path.DIRECTORY_SEPARATOR); $namespace = ''; } if (count($paths) > 1) { $rows[] = array('', ''); + $prevHasSeparator = true; + } else { + $prevHasSeparator = false; } } - array_pop($rows); + if ($prevHasSeparator) { + array_pop($rows); + } $io->section('Loader Paths'); $io->table(array('Namespace', 'Paths'), $rows); diff --git a/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php b/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php new file mode 100644 index 0000000000..77d522f523 --- /dev/null +++ b/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Command; + +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Twig\Command\DebugCommand; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; +use Twig\Loader\FilesystemLoader; +use Twig\Environment; + +class DebugCommandTest extends TestCase +{ + public function testDebugCommand() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array(), array('decorated' => false)); + + $this->assertEquals(0, $ret, 'Returns 0 in case of success'); + $this->assertContains('Functions', trim($tester->getDisplay())); + } + + public function testLineSeparatorInLoaderPaths() + { + // these paths aren't realistic, + // they're configured to force the line separator + $tester = $this->createCommandTester(array( + 'Acme' => array('extractor', 'extractor'), + '!Acme' => array('extractor', 'extractor'), + FilesystemLoader::MAIN_NAMESPACE => array('extractor', 'extractor'), + )); + $ret = $tester->execute(array(), array('decorated' => false)); + $ds = DIRECTORY_SEPARATOR; + $loaderPaths = <<assertEquals(0, $ret, 'Returns 0 in case of success'); + $this->assertContains($loaderPaths, trim($tester->getDisplay(true))); + } + + private function createCommandTester(array $paths = array()) + { + $filesystemLoader = new FilesystemLoader(array(), dirname(__DIR__).'/Fixtures'); + foreach ($paths as $namespace => $relDirs) { + foreach ($relDirs as $relDir) { + $filesystemLoader->addPath($relDir, $namespace); + } + } + $command = new DebugCommand(new Environment($filesystemLoader)); + + $application = new Application(); + $application->add($command); + $command = $application->find('debug:twig'); + + return new CommandTester($command); + } +} From 6d4812e995dc990a06727bd8fd3f1ba6a178dd39 Mon Sep 17 00:00:00 2001 From: Oleg Golovakhin Date: Wed, 30 May 2018 23:05:54 +0300 Subject: [PATCH 10/11] [OptionResolver] resolve arrays --- .../OptionsResolver/OptionsResolver.php | 106 ++++++++---- .../Tests/OptionsResolverTest.php | 153 +++++++++++++++++- 2 files changed, 223 insertions(+), 36 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 95a492de94..a58f45c0a6 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -433,7 +433,7 @@ class OptionsResolver implements Options )); } - $this->allowedValues[$option] = is_array($allowedValues) ? $allowedValues : array($allowedValues); + $this->allowedValues[$option] = \is_array($allowedValues) ? $allowedValues : array($allowedValues); // Make sure the option is processed unset($this->resolved[$option]); @@ -785,14 +785,13 @@ class OptionsResolver implements Options } if (!$valid) { - throw new InvalidOptionsException(sprintf( - 'The option "%s" with value %s is expected to be of type '. - '"%s", but is of type "%s".', - $option, - $this->formatValue($value), - implode('" or "', $this->allowedTypes[$option]), - implode('|', array_keys($invalidTypes)) - )); + $keys = array_keys($invalidTypes); + + if (1 === \count($keys) && '[]' === substr($keys[0], -2)) { + throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but one of the elements is of type "%s".', $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), $keys[0])); + } + + throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but is of type "%s".', $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), implode('|', array_keys($invalidTypes)))); } } @@ -877,23 +876,8 @@ class OptionsResolver implements Options */ private function verifyTypes($type, $value, array &$invalidTypes) { - if ('[]' === substr($type, -2) && is_array($value)) { - $originalType = $type; - $type = substr($type, 0, -2); - $invalidValues = array_filter( // Filter out valid values, keeping invalid values in the resulting array - $value, - function ($value) use ($type) { - return !self::isValueValidType($type, $value); - } - ); - - if (!$invalidValues) { - return true; - } - - $invalidTypes[$this->formatTypeOf($value, $originalType)] = true; - - return false; + if (\is_array($value) && '[]' === substr($type, -2)) { + return $this->verifyArrayType($type, $value, $invalidTypes); } if (self::isValueValidType($type, $value)) { @@ -907,6 +891,46 @@ class OptionsResolver implements Options return false; } + /** + * @return bool + */ + private function verifyArrayType($type, array $value, array &$invalidTypes, $level = 0) + { + $type = substr($type, 0, -2); + + $suffix = '[]'; + while (\strlen($suffix) <= $level * 2) { + $suffix .= '[]'; + } + + if ('[]' === substr($type, -2)) { + $success = true; + foreach ($value as $item) { + if (!\is_array($item)) { + $invalidTypes[$this->formatTypeOf($item, null).$suffix] = true; + + return false; + } + + if (!$this->verifyArrayType($type, $item, $invalidTypes, $level + 1)) { + $success = false; + } + } + + return $success; + } + + foreach ($value as $item) { + if (!self::isValueValidType($type, $item)) { + $invalidTypes[$this->formatTypeOf($item, $type).$suffix] = $value; + + return false; + } + } + + return true; + } + /** * Returns whether a resolved option with the given name exists. * @@ -990,13 +1014,13 @@ class OptionsResolver implements Options while ('[]' === substr($type, -2)) { $type = substr($type, 0, -2); $value = array_shift($value); - if (!is_array($value)) { + if (!\is_array($value)) { break; } $suffix .= '[]'; } - if (is_array($value)) { + if (\is_array($value)) { $subTypes = array(); foreach ($value as $val) { $subTypes[$this->formatTypeOf($val, null)] = true; @@ -1006,7 +1030,7 @@ class OptionsResolver implements Options } } - return (is_object($value) ? get_class($value) : gettype($value)).$suffix; + return (\is_object($value) ? get_class($value) : gettype($value)).$suffix; } /** @@ -1022,19 +1046,19 @@ class OptionsResolver implements Options */ private function formatValue($value) { - if (is_object($value)) { + if (\is_object($value)) { return get_class($value); } - if (is_array($value)) { + if (\is_array($value)) { return 'array'; } - if (is_string($value)) { + if (\is_string($value)) { return '"'.$value.'"'; } - if (is_resource($value)) { + if (\is_resource($value)) { return 'resource'; } @@ -1078,4 +1102,20 @@ class OptionsResolver implements Options { return (function_exists($isFunction = 'is_'.$type) && $isFunction($value)) || $value instanceof $type; } + + /** + * @return array + */ + private function getInvalidValues(array $arrayValues, $type) + { + $invalidValues = array(); + + foreach ($arrayValues as $key => $value) { + if (!self::isValueValidType($type, $value)) { + $invalidValues[$key] = $value; + } + } + + return $invalidValues; + } } diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index 440af8b578..034f42a8f6 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -511,7 +511,7 @@ class OptionsResolverTest extends TestCase /** * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException - * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "DateTime[]". + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "DateTime[]". */ public function testResolveFailsIfInvalidTypedArray() { @@ -535,7 +535,7 @@ class OptionsResolverTest extends TestCase /** * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException - * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "integer|stdClass|array|DateTime[]". + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "stdClass[]". */ public function testResolveFailsIfTypedArrayContainsInvalidTypes() { @@ -552,7 +552,7 @@ class OptionsResolverTest extends TestCase /** * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException - * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but is of type "double[][]". + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "double[][]". */ public function testResolveFailsWithCorrectLevelsButWrongScalar() { @@ -1650,4 +1650,151 @@ class OptionsResolverTest extends TestCase count($this->resolver); } + + public function testNestedArrays() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + + $this->assertEquals(array( + 'foo' => array( + array( + 1, 2, + ), + ), + ), $this->resolver->resolve( + array( + 'foo' => array( + array(1, 2), + ), + ) + )); + } + + public function testNested2Arrays() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][][][]'); + + $this->assertEquals(array( + 'foo' => array( + array( + array( + array( + 1, 2, + ), + ), + ), + ), + ), $this->resolver->resolve( + array( + 'foo' => array( + array( + array( + array(1, 2), + ), + ), + ), + ) + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "float[][][][]", but one of the elements is of type "integer[][][][]". + */ + public function testNestedArraysException() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'float[][][][]'); + + $this->resolver->resolve( + array( + 'foo' => array( + array( + array( + array(1, 2), + ), + ), + ), + ) + ); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]". + */ + public function testNestedArrayException1() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + $this->resolver->resolve(array( + 'foo' => array( + array(1, true, 'str', array(2, 3)), + ), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]". + */ + public function testNestedArrayException2() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + $this->resolver->resolve(array( + 'foo' => array( + array(true, 'str', array(2, 3)), + ), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "string[][]". + */ + public function testNestedArrayException3() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'string[][][]'); + $this->resolver->resolve(array( + 'foo' => array( + array('str', array(1, 2)), + ), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "integer[][][]". + */ + public function testNestedArrayException4() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'string[][][]'); + $this->resolver->resolve(array( + 'foo' => array( + array( + array('str'), array(1, 2), ), + ), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[]", but one of the elements is of type "array[]". + */ + public function testNestedArrayException5() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'string[]'); + $this->resolver->resolve(array( + 'foo' => array( + array( + array('str'), array(1, 2), ), + ), + )); + } } From bacb9ed333a9f5f23e0872d9ea819c7a05eacdea Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 7 Jul 2018 17:53:36 +0200 Subject: [PATCH 11/11] [Console] fix CS --- src/Symfony/Component/Console/Command/Command.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index b1f31ce794..ba93677715 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -210,10 +210,7 @@ class Command if (function_exists('cli_set_process_title')) { if (!@cli_set_process_title($this->processTitle)) { if ('Darwin' === PHP_OS) { - $output->writeln( - 'Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.', - OutputInterface::VERBOSITY_VERY_VERBOSE - ); + $output->writeln('Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.', OutputInterface::VERBOSITY_VERY_VERBOSE); } else { cli_set_process_title($this->processTitle); }