Merge branch '4.1'
* 4.1: [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 [FrameworkBundle] Fixed phpdoc in MicroKernelTrait::configureRoutes() [HttpFoundation] don't encode cookie name for BC improve deprecation messages minor #27858 [Console] changed warning verbosity; fixes typo (adrian-enspired) AppBundle->App. [Workflow] Fixed BC break [DI] Fix dumping ignore-on-uninitialized references to synthetic services
This commit is contained in:
commit
ffab7d6d68
@ -118,19 +118,27 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
$rows = array();
|
$rows = array();
|
||||||
|
$firstNamespace = true;
|
||||||
|
$prevHasSeparator = false;
|
||||||
foreach ($this->getLoaderPaths() as $namespace => $paths) {
|
foreach ($this->getLoaderPaths() as $namespace => $paths) {
|
||||||
if (count($paths) > 1) {
|
if (!$firstNamespace && !$prevHasSeparator && count($paths) > 1) {
|
||||||
$rows[] = array('', '');
|
$rows[] = array('', '');
|
||||||
}
|
}
|
||||||
|
$firstNamespace = false;
|
||||||
foreach ($paths as $path) {
|
foreach ($paths as $path) {
|
||||||
$rows[] = array($namespace, '- '.$path);
|
$rows[] = array($namespace, $path.DIRECTORY_SEPARATOR);
|
||||||
$namespace = '';
|
$namespace = '';
|
||||||
}
|
}
|
||||||
if (count($paths) > 1) {
|
if (count($paths) > 1) {
|
||||||
$rows[] = array('', '');
|
$rows[] = array('', '');
|
||||||
|
$prevHasSeparator = true;
|
||||||
|
} else {
|
||||||
|
$prevHasSeparator = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
array_pop($rows);
|
if ($prevHasSeparator) {
|
||||||
|
array_pop($rows);
|
||||||
|
}
|
||||||
$io->section('Loader Paths');
|
$io->section('Loader Paths');
|
||||||
$io->table(array('Namespace', 'Paths'), $rows);
|
$io->table(array('Namespace', 'Paths'), $rows);
|
||||||
$messages = $this->buildWarningMessages($this->findWrongBundleOverrides());
|
$messages = $this->buildWarningMessages($this->findWrongBundleOverrides());
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -28,7 +28,7 @@ trait MicroKernelTrait
|
|||||||
* Add or import routes into your application.
|
* Add or import routes into your application.
|
||||||
*
|
*
|
||||||
* $routes->import('config/routing.yml');
|
* $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
|
* @param RouteCollectionBuilder $routes
|
||||||
*/
|
*/
|
||||||
|
@ -70,7 +70,7 @@ Suppose that you have the following security configuration in your application:
|
|||||||
security:
|
security:
|
||||||
encoders:
|
encoders:
|
||||||
Symfony\Component\Security\Core\User\User: plaintext
|
Symfony\Component\Security\Core\User\User: plaintext
|
||||||
AppBundle\Entity\User: bcrypt
|
App\Entity\User: bcrypt
|
||||||
</comment>
|
</comment>
|
||||||
|
|
||||||
If you execute the command non-interactively, the first available configured
|
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
|
Pass the full user class path as the second argument to encode passwords for
|
||||||
your own entities:
|
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
|
Executing the command interactively allows you to generate a random salt for
|
||||||
encoding the password:
|
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:
|
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
|
EOF
|
||||||
)
|
)
|
||||||
|
@ -363,8 +363,8 @@ class MainConfiguration implements ConfigurationInterface
|
|||||||
->children()
|
->children()
|
||||||
->arrayNode('encoders')
|
->arrayNode('encoders')
|
||||||
->example(array(
|
->example(array(
|
||||||
'AppBundle\Entity\User1' => 'bcrypt',
|
'App\Entity\User1' => 'bcrypt',
|
||||||
'AppBundle\Entity\User2' => array(
|
'App\Entity\User2' => array(
|
||||||
'algorithm' => 'bcrypt',
|
'algorithm' => 'bcrypt',
|
||||||
'cost' => 13,
|
'cost' => 13,
|
||||||
),
|
),
|
||||||
|
@ -220,10 +220,7 @@ class Command
|
|||||||
if (function_exists('cli_set_process_title')) {
|
if (function_exists('cli_set_process_title')) {
|
||||||
if (!@cli_set_process_title($this->processTitle)) {
|
if (!@cli_set_process_title($this->processTitle)) {
|
||||||
if ('Darwin' === PHP_OS) {
|
if ('Darwin' === PHP_OS) {
|
||||||
$output->writeln(
|
$output->writeln('<comment>Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.</comment>', OutputInterface::VERBOSITY_VERY_VERBOSE);
|
||||||
'<comment>Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.</comment>',
|
|
||||||
OutputInterface::VERBOSITY_VERY_VERBOSE
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
cli_set_process_title($this->processTitle);
|
cli_set_process_title($this->processTitle);
|
||||||
}
|
}
|
||||||
|
@ -1680,8 +1680,10 @@ EOF;
|
|||||||
return '$this';
|
return '$this';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->container->hasDefinition($id) && ($definition = $this->container->getDefinition($id)) && !$definition->isSynthetic()) {
|
if ($this->container->hasDefinition($id) && $definition = $this->container->getDefinition($id)) {
|
||||||
if (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) {
|
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';
|
$code = 'null';
|
||||||
if (!$definition->isShared()) {
|
if (!$definition->isShared()) {
|
||||||
return $code;
|
return $code;
|
||||||
|
@ -1399,6 +1399,21 @@ class ContainerBuilderTest extends TestCase
|
|||||||
$this->assertSame('via-bindings', $container->get('foo')->class2->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'));
|
||||||
|
}
|
||||||
|
|
||||||
public function testIdCanBeAnObjectAsLongAsItCanBeCastToString()
|
public function testIdCanBeAnObjectAsLongAsItCanBeCastToString()
|
||||||
{
|
{
|
||||||
$id = new Reference('another_service');
|
$id = new Reference('another_service');
|
||||||
|
@ -960,6 +960,29 @@ class PhpDumperTest extends TestCase
|
|||||||
$this->assertInstanceOf('stdClass', $container->get('bar'));
|
$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.
|
* This test checks the trigger of a deprecation note and should not be removed in major releases.
|
||||||
*
|
*
|
||||||
|
@ -329,12 +329,17 @@ class Response
|
|||||||
}
|
}
|
||||||
|
|
||||||
// headers
|
// headers
|
||||||
foreach ($this->headers->allPreserveCase() as $name => $values) {
|
foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) {
|
||||||
foreach ($values as $value) {
|
foreach ($values as $value) {
|
||||||
header($name.': '.$value, false, $this->statusCode);
|
header($name.': '.$value, false, $this->statusCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cookies
|
||||||
|
foreach ($this->headers->getCookies() as $cookie) {
|
||||||
|
header('Set-Cookie: '.$cookie->getName().strstr($cookie, '='), false, $this->statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
// status
|
// status
|
||||||
header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode);
|
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
|
[0] => Content-Type: text/plain; charset=utf-8
|
||||||
[1] => Cache-Control: no-cache, private
|
[1] => Cache-Control: no-cache, private
|
||||||
[2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
|
[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=/
|
[4] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/
|
||||||
)
|
)
|
||||||
shutdown
|
shutdown
|
||||||
|
@ -490,7 +490,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
|
// Make sure the option is processed
|
||||||
unset($this->resolved[$option]);
|
unset($this->resolved[$option]);
|
||||||
@ -843,14 +843,13 @@ class OptionsResolver implements Options
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!$valid) {
|
if (!$valid) {
|
||||||
throw new InvalidOptionsException(sprintf(
|
$keys = array_keys($invalidTypes);
|
||||||
'The option "%s" with value %s is expected to be of type '.
|
|
||||||
'"%s", but is of type "%s".',
|
if (1 === \count($keys) && '[]' === substr($keys[0], -2)) {
|
||||||
$option,
|
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]));
|
||||||
$this->formatValue($value),
|
}
|
||||||
implode('" or "', $this->allowedTypes[$option]),
|
|
||||||
implode('|', array_keys($invalidTypes))
|
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))));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -939,32 +938,10 @@ class OptionsResolver implements Options
|
|||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function verifyTypes(string $type, $value, array &$invalidTypes): bool
|
||||||
* @param string $type
|
|
||||||
* @param mixed $value
|
|
||||||
* @param array &$invalidTypes
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function verifyTypes($type, $value, array &$invalidTypes)
|
|
||||||
{
|
{
|
||||||
if ('[]' === substr($type, -2) && is_array($value)) {
|
if (\is_array($value) && '[]' === substr($type, -2)) {
|
||||||
$originalType = $type;
|
return $this->verifyArrayType($type, $value, $invalidTypes);
|
||||||
$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 (self::isValueValidType($type, $value)) {
|
if (self::isValueValidType($type, $value)) {
|
||||||
@ -978,6 +955,43 @@ class OptionsResolver implements Options
|
|||||||
return false;
|
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.
|
* Returns whether a resolved option with the given name exists.
|
||||||
*
|
*
|
||||||
@ -1058,13 +1072,13 @@ class OptionsResolver implements Options
|
|||||||
while ('[]' === substr($type, -2)) {
|
while ('[]' === substr($type, -2)) {
|
||||||
$type = substr($type, 0, -2);
|
$type = substr($type, 0, -2);
|
||||||
$value = array_shift($value);
|
$value = array_shift($value);
|
||||||
if (!is_array($value)) {
|
if (!\is_array($value)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$suffix .= '[]';
|
$suffix .= '[]';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_array($value)) {
|
if (\is_array($value)) {
|
||||||
$subTypes = array();
|
$subTypes = array();
|
||||||
foreach ($value as $val) {
|
foreach ($value as $val) {
|
||||||
$subTypes[$this->formatTypeOf($val, null)] = true;
|
$subTypes[$this->formatTypeOf($val, null)] = true;
|
||||||
@ -1074,7 +1088,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1088,19 +1102,19 @@ class OptionsResolver implements Options
|
|||||||
*/
|
*/
|
||||||
private function formatValue($value): string
|
private function formatValue($value): string
|
||||||
{
|
{
|
||||||
if (is_object($value)) {
|
if (\is_object($value)) {
|
||||||
return get_class($value);
|
return get_class($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_array($value)) {
|
if (\is_array($value)) {
|
||||||
return 'array';
|
return 'array';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_string($value)) {
|
if (\is_string($value)) {
|
||||||
return '"'.$value.'"';
|
return '"'.$value.'"';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_resource($value)) {
|
if (\is_resource($value)) {
|
||||||
return 'resource';
|
return 'resource';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1136,8 +1150,21 @@ class OptionsResolver implements Options
|
|||||||
return implode(', ', $values);
|
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;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -664,7 +664,7 @@ class OptionsResolverTest extends TestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
|
* @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()
|
public function testResolveFailsIfInvalidTypedArray()
|
||||||
{
|
{
|
||||||
@ -688,7 +688,7 @@ class OptionsResolverTest extends TestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
|
* @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()
|
public function testResolveFailsIfTypedArrayContainsInvalidTypes()
|
||||||
{
|
{
|
||||||
@ -705,7 +705,7 @@ class OptionsResolverTest extends TestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
|
* @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()
|
public function testResolveFailsWithCorrectLevelsButWrongScalar()
|
||||||
{
|
{
|
||||||
@ -1767,4 +1767,151 @@ class OptionsResolverTest extends TestCase
|
|||||||
|
|
||||||
count($this->resolver);
|
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);
|
$ucFirstProperty = ucfirst($property);
|
||||||
|
|
||||||
try {
|
switch (true) {
|
||||||
switch (true) {
|
case $docBlock = $this->getDocBlockFromProperty($class, $property):
|
||||||
case $docBlock = $this->getDocBlockFromProperty($class, $property):
|
$data = array($docBlock, self::PROPERTY, null);
|
||||||
$data = array($docBlock, self::PROPERTY, null);
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case list($docBlock) = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR):
|
case list($docBlock) = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR):
|
||||||
$data = array($docBlock, self::ACCESSOR, null);
|
$data = array($docBlock, self::ACCESSOR, null);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case list($docBlock, $prefix) = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR):
|
case list($docBlock, $prefix) = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR):
|
||||||
$data = array($docBlock, self::MUTATOR, $prefix);
|
$data = array($docBlock, self::MUTATOR, $prefix);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$data = array(null, null, null);
|
$data = array(null, null, null);
|
||||||
}
|
|
||||||
} catch (\InvalidArgumentException $e) {
|
|
||||||
$data = array(null, null, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->docBlocks[$propertyHash] = $data;
|
return $this->docBlocks[$propertyHash] = $data;
|
||||||
@ -194,7 +190,11 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property
|
|||||||
return null;
|
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
|
private function getDocBlockFromMethod(string $class, string $ucFirstProperty, int $type): ?array
|
||||||
@ -226,6 +226,10 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property
|
|||||||
return null;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -218,6 +218,29 @@ class PhpDocExtractorTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->assertNull($this->extractor->getShortDescription(EmptyDocBlock::class, 'foo'));
|
$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
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -40,7 +40,10 @@ class Event extends BaseEvent
|
|||||||
$this->subject = $subject;
|
$this->subject = $subject;
|
||||||
$this->marking = $marking;
|
$this->marking = $marking;
|
||||||
$this->transition = $transition;
|
$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);
|
@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;
|
$this->workflowName = $workflow;
|
||||||
} elseif ($workflow instanceof WorkflowInterface) {
|
} elseif ($workflow instanceof WorkflowInterface) {
|
||||||
|
Reference in New Issue
Block a user