Merge branch '4.0' into 4.1
* 4.0: [Console] fix CS [OptionResolver] resolve arrays [TwigBridge] Fix missing path and separators in loader paths list on debug:twig output [PropertyInfo] Fix dock block lookup fallback loop [HttpFoundation] don't encode cookie name for BC improve deprecation messages minor #27858 [Console] changed warning verbosity; fixes typo (adrian-enspired) AppBundle->App. [DI] Fix dumping ignore-on-uninitialized references to synthetic services
This commit is contained in:
commit
88f704684d
@ -108,19 +108,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);
|
||||
|
||||
|
81
src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php
Normal file
81
src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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 = <<<TXT
|
||||
Loader Paths
|
||||
------------
|
||||
|
||||
----------- ------------
|
||||
Namespace Paths
|
||||
----------- ------------
|
||||
@Acme extractor$ds
|
||||
extractor$ds
|
||||
|
||||
@!Acme extractor$ds
|
||||
extractor$ds
|
||||
|
||||
(None) extractor$ds
|
||||
extractor$ds
|
||||
----------- ------------
|
||||
TXT;
|
||||
|
||||
$this->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);
|
||||
}
|
||||
}
|
@ -70,7 +70,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
|
||||
</comment>
|
||||
|
||||
If you execute the command non-interactively, the first available configured
|
||||
@ -82,16 +82,16 @@ generated to encode the password:
|
||||
Pass the full user class path as the second argument to encode passwords for
|
||||
your own entities:
|
||||
|
||||
<info>php %command.full_name% --no-interaction [password] AppBundle\Entity\User</info>
|
||||
<info>php %command.full_name% --no-interaction [password] App\Entity\User</info>
|
||||
|
||||
Executing the command interactively allows you to generate a random salt for
|
||||
encoding the password:
|
||||
|
||||
<info>php %command.full_name% [password] AppBundle\Entity\User</info>
|
||||
<info>php %command.full_name% [password] App\Entity\User</info>
|
||||
|
||||
In case your encoder doesn't require a salt, add the <comment>empty-salt</comment> option:
|
||||
|
||||
<info>php %command.full_name% --empty-salt [password] AppBundle\Entity\User</info>
|
||||
<info>php %command.full_name% --empty-salt [password] App\Entity\User</info>
|
||||
|
||||
EOF
|
||||
)
|
||||
|
@ -363,8 +363,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,
|
||||
),
|
||||
|
@ -220,7 +220,7 @@ class Command
|
||||
if (function_exists('cli_set_process_title')) {
|
||||
if (!@cli_set_process_title($this->processTitle)) {
|
||||
if ('Darwin' === PHP_OS) {
|
||||
$output->writeln('<comment>Running "cli_get_process_title" as an unprivileged user is not supported on MacOS.</comment>');
|
||||
$output->writeln('<comment>Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.</comment>', OutputInterface::VERBOSITY_VERY_VERBOSE);
|
||||
} else {
|
||||
cli_set_process_title($this->processTitle);
|
||||
}
|
||||
|
@ -1662,8 +1662,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;
|
||||
|
@ -1399,6 +1399,21 @@ class ContainerBuilderTest extends TestCase
|
||||
$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'));
|
||||
}
|
||||
|
||||
public function testIdCanBeAnObjectAsLongAsItCanBeCastToString()
|
||||
{
|
||||
$id = new Reference('another_service');
|
||||
|
@ -953,6 +953,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'));
|
||||
}
|
||||
|
||||
/**
|
||||
* This test checks the trigger of a deprecation note and should not be removed in major releases.
|
||||
*
|
||||
|
@ -329,12 +329,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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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))));
|
||||
}
|
||||
}
|
||||
|
||||
@ -868,32 +867,10 @@ class OptionsResolver implements Options
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param mixed $value
|
||||
* @param array &$invalidTypes
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function verifyTypes($type, $value, array &$invalidTypes)
|
||||
private function verifyTypes(string $type, $value, array &$invalidTypes): bool
|
||||
{
|
||||
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 +884,43 @@ class OptionsResolver implements Options
|
||||
return false;
|
||||
}
|
||||
|
||||
private function verifyArrayType(string $type, array $value, array &$invalidTypes, int $level = 0): bool
|
||||
{
|
||||
$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.
|
||||
*
|
||||
@ -987,13 +1001,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;
|
||||
@ -1003,7 +1017,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1017,19 +1031,19 @@ class OptionsResolver implements Options
|
||||
*/
|
||||
private function formatValue($value): string
|
||||
{
|
||||
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';
|
||||
}
|
||||
|
||||
@ -1065,8 +1079,21 @@ class OptionsResolver implements Options
|
||||
return implode(', ', $values);
|
||||
}
|
||||
|
||||
private static function isValueValidType($type, $value)
|
||||
private static function isValueValidType(string $type, $value): bool
|
||||
{
|
||||
return (function_exists($isFunction = 'is_'.$type) && $isFunction($value)) || $value instanceof $type;
|
||||
}
|
||||
|
||||
private function getInvalidValues(array $arrayValues, string $type): array
|
||||
{
|
||||
$invalidValues = array();
|
||||
|
||||
foreach ($arrayValues as $key => $value) {
|
||||
if (!self::isValueValidType($type, $value)) {
|
||||
$invalidValues[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $invalidValues;
|
||||
}
|
||||
}
|
||||
|
@ -483,7 +483,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()
|
||||
{
|
||||
@ -507,7 +507,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()
|
||||
{
|
||||
@ -524,7 +524,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()
|
||||
{
|
||||
@ -1586,4 +1586,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), ),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -161,25 +161,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;
|
||||
@ -194,7 +190,11 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property
|
||||
return null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private function getDocBlockFromMethod(string $class, string $ucFirstProperty, int $type): ?array
|
||||
@ -226,6 +226,10 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property
|
||||
return null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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 <mano@radebatz.net>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user