Merge branch '4.2'

* 4.2: (27 commits)
  [VarExporter] dont call userland code with uninitialized objects
  Fix typos in doc blocks
  [Debug] ignore underscore vs backslash namespaces in DebugClassLoader
  [TwigBridge][Form] Prevent multiple rendering of form collection prototypes
  [FrameworkBundle] fix describing routes with no controllers
  [DI] move RegisterServiceSubscribersPass before DecoratorServicePass
  Update ValidationListener.php
  [Yaml] ensures that the mb_internal_encoding is reset to its initial value
  [Messenger] Restore message handlers laziness
  [WebLink] Fixed documentation link
  [Security] getTargetPath of TargetPathTrait must return string or null
  [Hackday][Serializer] Deserialization ignores argument type hint from phpdoc for array in constructor argument
  Optimize perf by replacing call_user_func with dynamic vars
  [Cache] Fix dsn parsing
  [Routing] fix dumping same-path routes with placeholders
  [WebProfilerBundle][TwigBundle] CSS fixes
  Add a docblock for FormFactoryInterface
  [Security] defer log message in guard authenticator
  [Validator] Added IBAN format for Vatican City State
  merge conflicts
  ...
This commit is contained in:
Nicolas Grekas 2018-12-13 13:43:21 +01:00
commit df4ad4e7d4
102 changed files with 541 additions and 237 deletions

View File

@ -213,7 +213,7 @@ abstract class DoctrineType extends AbstractType implements ResetInterface
// for equal query builders // for equal query builders
$queryBuilderNormalizer = function (Options $options, $queryBuilder) { $queryBuilderNormalizer = function (Options $options, $queryBuilder) {
if (\is_callable($queryBuilder)) { if (\is_callable($queryBuilder)) {
$queryBuilder = \call_user_func($queryBuilder, $options['em']->getRepository($options['class'])); $queryBuilder = $queryBuilder($options['em']->getRepository($options['class']));
} }
return $queryBuilder; return $queryBuilder;

View File

@ -29,7 +29,7 @@ class EntityType extends DoctrineType
// for equal query builders // for equal query builders
$queryBuilderNormalizer = function (Options $options, $queryBuilder) { $queryBuilderNormalizer = function (Options $options, $queryBuilder) {
if (\is_callable($queryBuilder)) { if (\is_callable($queryBuilder)) {
$queryBuilder = \call_user_func($queryBuilder, $options['em']->getRepository($options['class'])); $queryBuilder = $queryBuilder($options['em']->getRepository($options['class']));
if (null !== $queryBuilder && !$queryBuilder instanceof QueryBuilder) { if (null !== $queryBuilder && !$queryBuilder instanceof QueryBuilder) {
throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder'); throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');

View File

@ -102,7 +102,7 @@ class ServerLogHandler extends AbstractHandler
{ {
if ($this->processors) { if ($this->processors) {
foreach ($this->processors as $processor) { foreach ($this->processors as $processor) {
$record = \call_user_func($processor, $record); $record = $processor($record);
} }
} }

View File

@ -43,7 +43,7 @@ class RuntimeInstantiator implements InstantiatorInterface
return $this->factory->createProxy( return $this->factory->createProxy(
$this->factory->getGenerator()->getProxifiedClass($definition), $this->factory->getGenerator()->getProxifiedClass($definition),
function (&$wrappedInstance, LazyLoadingInterface $proxy) use ($realInstantiator) { function (&$wrappedInstance, LazyLoadingInterface $proxy) use ($realInstantiator) {
$wrappedInstance = \call_user_func($realInstantiator); $wrappedInstance = $realInstantiator();
$proxy->setProxyInitializer(null); $proxy->setProxyInitializer(null);

View File

@ -28,7 +28,7 @@
{%- endblock form_widget_compound -%} {%- endblock form_widget_compound -%}
{%- block collection_widget -%} {%- block collection_widget -%}
{% if prototype is defined %} {% if prototype is defined and not prototype.rendered %}
{%- set attr = attr|merge({'data-prototype': form_row(prototype) }) -%} {%- set attr = attr|merge({'data-prototype': form_row(prototype) }) -%}
{% endif %} {% endif %}
{{- block('form_widget') -}} {{- block('form_widget') -}}

View File

@ -57,7 +57,7 @@ class TextDescriptor extends Descriptor
if ($showControllers) { if ($showControllers) {
$controller = $route->getDefault('_controller'); $controller = $route->getDefault('_controller');
$row[] = $this->formatCallable($controller); $row[] = $controller ? $this->formatCallable($controller) : '';
} }
$tableRows[] = $row; $tableRows[] = $row;

View File

@ -15,7 +15,6 @@ use Doctrine\Common\Persistence\ManagerRegistry;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface; use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface;
@ -27,6 +26,7 @@ use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Templating\EngineInterface; use Symfony\Component\Templating\EngineInterface;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
use Twig\Environment; use Twig\Environment;
/** /**

View File

@ -50,7 +50,7 @@ class RedirectController
* @param string $route The route name to redirect to * @param string $route The route name to redirect to
* @param bool $permanent Whether the redirection is permanent * @param bool $permanent Whether the redirection is permanent
* @param bool|array $ignoreAttributes Whether to ignore attributes or an array of attributes to ignore * @param bool|array $ignoreAttributes Whether to ignore attributes or an array of attributes to ignore
* @param bool $keepRequestMethod Wheter redirect action should keep HTTP request method * @param bool $keepRequestMethod Whether redirect action should keep HTTP request method
* *
* @throws HttpException In case the route name is empty * @throws HttpException In case the route name is empty
*/ */
@ -94,7 +94,7 @@ class RedirectController
* @param string|null $scheme The URL scheme (null to keep the current one) * @param string|null $scheme The URL scheme (null to keep the current one)
* @param int|null $httpPort The HTTP port (null to keep the current one for the same scheme or the default configured port) * @param int|null $httpPort The HTTP port (null to keep the current one for the same scheme or the default configured port)
* @param int|null $httpsPort The HTTPS port (null to keep the current one for the same scheme or the default configured port) * @param int|null $httpsPort The HTTPS port (null to keep the current one for the same scheme or the default configured port)
* @param bool $keepRequestMethod Wheter redirect action should keep HTTP request method * @param bool $keepRequestMethod Whether redirect action should keep HTTP request method
* *
* @throws HttpException In case the path is empty * @throws HttpException In case the path is empty
*/ */

View File

@ -37,7 +37,6 @@ use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
@ -1602,12 +1601,7 @@ class FrameworkExtension extends Extension
$senders[$sender] = new Reference($senderAliases[$sender] ?? $sender); $senders[$sender] = new Reference($senderAliases[$sender] ?? $sender);
} }
$sendersId = 'messenger.senders.'.$message; $messageToSendersMapping[$message] = new IteratorArgument($senders);
$container->register($sendersId, RewindableGenerator::class)
->setFactory('current')
->addArgument(array(new IteratorArgument($senders)));
$messageToSendersMapping[$message] = new Reference($sendersId);
$messagesToSendAndHandle[$message] = $messageConfiguration['send_and_handle']; $messagesToSendAndHandle[$message] = $messageConfiguration['send_and_handle'];
} }

View File

@ -651,10 +651,11 @@ abstract class FrameworkExtensionTest extends TestCase
); );
$this->assertSame($messageToSendAndHandleMapping, $senderLocatorDefinition->getArgument(1)); $this->assertSame($messageToSendAndHandleMapping, $senderLocatorDefinition->getArgument(1));
$sendersMapping = $senderLocatorDefinition->getArgument(0);
$this->assertEquals(array( $this->assertEquals(array(
'amqp' => new Reference('messenger.transport.amqp'), 'amqp' => new Reference('messenger.transport.amqp'),
'audit' => new Reference('audit'), 'audit' => new Reference('audit'),
), $container->getDefinition('messenger.senders.'.DummyMessage::class)->getArgument(0)[0]->getValues()); ), $sendersMapping[DummyMessage::class]->getValues());
} }
/** /**

View File

@ -1,7 +1,7 @@
{# This file is based on WebProfilerBundle/Resources/views/Profiler/profiler.css.twig. {# This file is based on WebProfilerBundle/Resources/views/Profiler/profiler.css.twig.
If you make any change in this file, verify the same change is needed in the other file. #} If you make any change in this file, verify the same change is needed in the other file. #}
:root { :root {
--font-sans-serif: 'Helvetica, Arial, sans-serif'; --font-sans-serif: Helvetica, Arial, sans-serif;
--page-background: #f9f9f9; --page-background: #f9f9f9;
--color-text: #222; --color-text: #222;
/* when updating any of these colors, do the same in toolbar.css.twig */ /* when updating any of these colors, do the same in toolbar.css.twig */

View File

@ -6,7 +6,7 @@
*{-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0} *{-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}
:root { :root {
--font-sans-serif: 'Helvetica, Arial, sans-serif'; --font-sans-serif: Helvetica, Arial, sans-serif;
--page-background: #f9f9f9; --page-background: #f9f9f9;
--color-text: #222; --color-text: #222;
--color-muted: #999; --color-muted: #999;
@ -42,7 +42,7 @@
.theme-dark { .theme-dark {
--page-background: #36393e; --page-background: #36393e;
--color-text: #e0e0e0; --color-text: #e0e0e0;
--color-muted: #999; --color-muted: #777;
--tab-background: #555; --tab-background: #555;
--tab-color: #ccc; --tab-color: #ccc;
--tab-active-background: #888; --tab-active-background: #888;
@ -59,7 +59,6 @@
--table-header: #555; --table-header: #555;
--shadow: 0px 0px 1px rgba(32, 32, 32, .2); --shadow: 0px 0px 1px rgba(32, 32, 32, .2);
--border: 1px solid #666; --border: 1px solid #666;
--color-muted: #777;
--base-0: #2e3136; --base-0: #2e3136;
--base-1: #444; --base-1: #444;
--base-2: #666; --base-2: #666;
@ -80,7 +79,7 @@ body {
color: var(--base-6); color: var(--base-6);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
font_family: var(--font-sans-serif); font-family: var(--font-sans-serif);
font-size: 14px; font-size: 14px;
line-height: 1.4; line-height: 1.4;
} }
@ -157,7 +156,7 @@ code, pre {
{# Buttons (the colors of this element don't change based on the selected theme) {# Buttons (the colors of this element don't change based on the selected theme)
------------------------------------------------------------------------- #} ------------------------------------------------------------------------- #}
button { button {
font_family: var(--font-sans-serif); font-family: var(--font-sans-serif);
} }
.btn { .btn {
background: #777; background: #777;
@ -296,7 +295,7 @@ table tbody td.num-col {
text-align: center; text-align: center;
} }
.font-normal { .font-normal {
font_family: var(--font-sans-serif); font-family: var(--font-sans-serif);
font-size: 14px; font-size: 14px;
} }
.help { .help {
@ -1158,7 +1157,7 @@ pre.sf-dump, pre.sf-dump .sf-dump-default {
{# Search Results page {# Search Results page
========================================================================= #} ========================================================================= #}
#search-results td { #search-results td {
font_family: var(--font-sans-serif); font-family: var(--font-sans-serif);
vertical-align: middle; vertical-align: middle;
} }

View File

@ -146,13 +146,13 @@ class PhpArrayAdapterWrapper extends PhpArrayAdapter
public function save(CacheItemInterface $item) public function save(CacheItemInterface $item)
{ {
\call_user_func(\Closure::bind(function () use ($item) { (\Closure::bind(function () use ($item) {
$key = $item->getKey(); $key = $item->getKey();
$this->keys[$key] = $id = \count($this->values); $this->keys[$key] = $id = \count($this->values);
$this->data[$key] = $this->values[$id] = $item->get(); $this->data[$key] = $this->values[$id] = $item->get();
$this->warmUp($this->data); $this->warmUp($this->data);
list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6)); list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6));
}, $this, PhpArrayAdapter::class)); }, $this, PhpArrayAdapter::class))();
return true; return true;
} }

View File

@ -62,9 +62,9 @@ class CacheItemTest extends TestCase
$this->assertSame($item, $item->tag('foo')); $this->assertSame($item, $item->tag('foo'));
$this->assertSame($item, $item->tag(array('bar', 'baz'))); $this->assertSame($item, $item->tag(array('bar', 'baz')));
\call_user_func(\Closure::bind(function () use ($item) { (\Closure::bind(function () use ($item) {
$this->assertSame(array('foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz'), $item->newMetadata[CacheItem::METADATA_TAGS]); $this->assertSame(array('foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz'), $item->newMetadata[CacheItem::METADATA_TAGS]);
}, $this, CacheItem::class)); }, $this, CacheItem::class))();
} }
/** /**

View File

@ -134,11 +134,11 @@ class PhpArrayCacheWrapper extends PhpArrayCache
public function set($key, $value, $ttl = null) public function set($key, $value, $ttl = null)
{ {
\call_user_func(\Closure::bind(function () use ($key, $value) { (\Closure::bind(function () use ($key, $value) {
$this->data[$key] = $value; $this->data[$key] = $value;
$this->warmUp($this->data); $this->warmUp($this->data);
list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6)); list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6));
}, $this, PhpArrayCache::class)); }, $this, PhpArrayCache::class))();
return true; return true;
} }
@ -148,13 +148,13 @@ class PhpArrayCacheWrapper extends PhpArrayCache
if (!\is_array($values) && !$values instanceof \Traversable) { if (!\is_array($values) && !$values instanceof \Traversable) {
return parent::setMultiple($values, $ttl); return parent::setMultiple($values, $ttl);
} }
\call_user_func(\Closure::bind(function () use ($values) { (\Closure::bind(function () use ($values) {
foreach ($values as $key => $value) { foreach ($values as $key => $value) {
$this->data[$key] = $value; $this->data[$key] = $value;
} }
$this->warmUp($this->data); $this->warmUp($this->data);
list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6)); list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6));
}, $this, PhpArrayCache::class)); }, $this, PhpArrayCache::class))();
return true; return true;
} }

View File

@ -96,7 +96,7 @@ trait RedisTrait
return 'file:'.($m[1] ?? ''); return 'file:'.($m[1] ?? '');
}, $dsn); }, $dsn);
if (false === $params = parse_url($dsn)) { if (false === $params = parse_url($params)) {
throw new InvalidArgumentException(sprintf('Invalid Redis DSN: %s', $dsn)); throw new InvalidArgumentException(sprintf('Invalid Redis DSN: %s', $dsn));
} }

View File

@ -43,7 +43,7 @@ class ConfigCacheFactory implements ConfigCacheFactoryInterface
$cache = new ConfigCache($file, $this->debug); $cache = new ConfigCache($file, $this->debug);
if (!$cache->isFresh()) { if (!$cache->isFresh()) {
\call_user_func($callback, $cache); $callback($cache);
} }
return $cache; return $cache;

View File

@ -155,15 +155,15 @@ class ReflectionClassResource implements SelfCheckingResourceInterface, \Seriali
if (interface_exists(EventSubscriberInterface::class, false) && $class->isSubclassOf(EventSubscriberInterface::class)) { if (interface_exists(EventSubscriberInterface::class, false) && $class->isSubclassOf(EventSubscriberInterface::class)) {
yield EventSubscriberInterface::class; yield EventSubscriberInterface::class;
yield print_r(\call_user_func(array($class->name, 'getSubscribedEvents')), true); yield print_r($class->name::getSubscribedEvents(), true);
} }
if (interface_exists(LegacyServiceSubscriberInterface::class, false) && $class->isSubclassOf(LegacyServiceSubscriberInterface::class)) { if (interface_exists(LegacyServiceSubscriberInterface::class, false) && $class->isSubclassOf(LegacyServiceSubscriberInterface::class)) {
yield LegacyServiceSubscriberInterface::class; yield LegacyServiceSubscriberInterface::class;
yield print_r(\call_user_func(array($class->name, 'getSubscribedServices')), true); yield print_r(array($class->name, 'getSubscribedServices')(), true);
} elseif (interface_exists(ServiceSubscriberInterface::class, false) && $class->isSubclassOf(ServiceSubscriberInterface::class)) { } elseif (interface_exists(ServiceSubscriberInterface::class, false) && $class->isSubclassOf(ServiceSubscriberInterface::class)) {
yield ServiceSubscriberInterface::class; yield ServiceSubscriberInterface::class;
yield print_r(\call_user_func(array($class->name, 'getSubscribedServices')), true); yield print_r($class->name::getSubscribedServices(), true);
} }
} }
} }

View File

@ -40,7 +40,7 @@ class ResourceCheckerConfigCacheFactory implements ConfigCacheFactoryInterface
$cache = new ResourceCheckerConfigCache($file, $this->resourceCheckers); $cache = new ResourceCheckerConfigCache($file, $this->resourceCheckers);
if (!$cache->isFresh()) { if (!$cache->isFresh()) {
\call_user_func($callback, $cache); $callback($cache);
} }
return $cache; return $cache;

View File

@ -80,7 +80,7 @@ class XmlUtils
$e = null; $e = null;
if (\is_callable($schemaOrCallable)) { if (\is_callable($schemaOrCallable)) {
try { try {
$valid = \call_user_func($schemaOrCallable, $dom, $internalErrors); $valid = $schemaOrCallable($dom, $internalErrors);
} catch (\Exception $e) { } catch (\Exception $e) {
$valid = false; $valid = false;
} }

View File

@ -250,7 +250,7 @@ class Command
$input->validate(); $input->validate();
if ($this->code) { if ($this->code) {
$statusCode = \call_user_func($this->code, $input, $output); $statusCode = ($this->code)($input, $output);
} else { } else {
$statusCode = $this->execute($input, $output); $statusCode = $this->execute($input, $output);
} }

View File

@ -136,7 +136,7 @@ class ProcessHelper extends Helper
$output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type)); $output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type));
if (null !== $callback) { if (null !== $callback) {
\call_user_func($callback, $type, $buffer); $callback($type, $buffer);
} }
}; };
} }

View File

@ -496,7 +496,7 @@ final class ProgressBar
$regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i"; $regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i";
$callback = function ($matches) { $callback = function ($matches) {
if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) { if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) {
$text = \call_user_func($formatter, $this, $this->output); $text = $formatter($this, $this->output);
} elseif (isset($this->messages[$matches[1]])) { } elseif (isset($this->messages[$matches[1]])) {
$text = $this->messages[$matches[1]]; $text = $this->messages[$matches[1]];
} else { } else {

View File

@ -196,7 +196,7 @@ class ProgressIndicator
$this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self) { $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self) {
if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) { if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) {
return \call_user_func($formatter, $self); return $formatter($self);
} }
return $matches[0]; return $matches[0];

View File

@ -381,7 +381,7 @@ class QuestionHelper extends Helper
} }
try { try {
return \call_user_func($question->getValidator(), $interviewer()); return $question->getValidator()($interviewer());
} catch (RuntimeException $e) { } catch (RuntimeException $e) {
throw $e; throw $e;
} catch (\Exception $error) { } catch (\Exception $error) {

View File

@ -155,7 +155,7 @@ class Translator implements TranslatorInterface
throw new ExpressionErrorException(sprintf('Node "%s" not supported.', $node->getNodeName())); throw new ExpressionErrorException(sprintf('Node "%s" not supported.', $node->getNodeName()));
} }
return \call_user_func($this->nodeTranslators[$node->getNodeName()], $node, $this); return $this->nodeTranslators[$node->getNodeName()]($node, $this);
} }
/** /**
@ -167,7 +167,7 @@ class Translator implements TranslatorInterface
throw new ExpressionErrorException(sprintf('Combiner "%s" not supported.', $combiner)); throw new ExpressionErrorException(sprintf('Combiner "%s" not supported.', $combiner));
} }
return \call_user_func($this->combinationTranslators[$combiner], $this->nodeToXPath($xpath), $this->nodeToXPath($combinedXpath)); return $this->combinationTranslators[$combiner]($this->nodeToXPath($xpath), $this->nodeToXPath($combinedXpath));
} }
/** /**
@ -179,7 +179,7 @@ class Translator implements TranslatorInterface
throw new ExpressionErrorException(sprintf('Function "%s" not supported.', $function->getName())); throw new ExpressionErrorException(sprintf('Function "%s" not supported.', $function->getName()));
} }
return \call_user_func($this->functionTranslators[$function->getName()], $xpath, $function); return $this->functionTranslators[$function->getName()]($xpath, $function);
} }
/** /**
@ -191,7 +191,7 @@ class Translator implements TranslatorInterface
throw new ExpressionErrorException(sprintf('Pseudo-class "%s" not supported.', $pseudoClass)); throw new ExpressionErrorException(sprintf('Pseudo-class "%s" not supported.', $pseudoClass));
} }
return \call_user_func($this->pseudoClassTranslators[$pseudoClass], $xpath); return $this->pseudoClassTranslators[$pseudoClass]($xpath);
} }
/** /**
@ -203,7 +203,7 @@ class Translator implements TranslatorInterface
throw new ExpressionErrorException(sprintf('Attribute matcher operator "%s" not supported.', $operator)); throw new ExpressionErrorException(sprintf('Attribute matcher operator "%s" not supported.', $operator));
} }
return \call_user_func($this->attributeMatchingTranslators[$operator], $xpath, $attribute, $value); return $this->attributeMatchingTranslators[$operator]($xpath, $attribute, $value);
} }
/** /**

View File

@ -151,7 +151,7 @@ class DebugClassLoader
require $file; require $file;
} }
} else { } else {
\call_user_func($this->classLoader, $class); ($this->classLoader)($class);
$file = false; $file = false;
} }
} finally { } finally {
@ -218,7 +218,7 @@ class DebugClassLoader
$len = 0; $len = 0;
$ns = ''; $ns = '';
} else { } else {
$ns = \substr($class, 0, $len); $ns = \str_replace('_', '\\', \substr($class, 0, $len));
} }
// Detect annotations on the class // Detect annotations on the class
@ -249,13 +249,13 @@ class DebugClassLoader
if (!isset(self::$checkedClasses[$use])) { if (!isset(self::$checkedClasses[$use])) {
$this->checkClass($use); $this->checkClass($use);
} }
if (isset(self::$deprecated[$use]) && \strncmp($ns, $use, $len)) { if (isset(self::$deprecated[$use]) && \strncmp($ns, \str_replace('_', '\\', $use), $len)) {
$type = class_exists($class, false) ? 'class' : (interface_exists($class, false) ? 'interface' : 'trait'); $type = class_exists($class, false) ? 'class' : (interface_exists($class, false) ? 'interface' : 'trait');
$verb = class_exists($use, false) || interface_exists($class, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses'); $verb = class_exists($use, false) || interface_exists($class, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses');
$deprecations[] = sprintf('The "%s" %s %s "%s" that is deprecated%s.', $class, $type, $verb, $use, self::$deprecated[$use]); $deprecations[] = sprintf('The "%s" %s %s "%s" that is deprecated%s.', $class, $type, $verb, $use, self::$deprecated[$use]);
} }
if (isset(self::$internal[$use]) && \strncmp($ns, $use, $len)) { if (isset(self::$internal[$use]) && \strncmp($ns, \str_replace('_', '\\', $use), $len)) {
$deprecations[] = sprintf('The "%s" %s is considered internal%s. It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $class); $deprecations[] = sprintf('The "%s" %s is considered internal%s. It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $class);
} }
} }

View File

@ -562,7 +562,7 @@ class ErrorHandler
$this->exceptionHandler = null; $this->exceptionHandler = null;
try { try {
if (null !== $exceptionHandler) { if (null !== $exceptionHandler) {
return \call_user_func($exceptionHandler, $exception); return $exceptionHandler($exception);
} }
$handlerException = $handlerException ?: $exception; $handlerException = $handlerException ?: $exception;
} catch (\Throwable $handlerException) { } catch (\Throwable $handlerException) {

View File

@ -144,7 +144,7 @@ class ExceptionHandler
$this->caughtBuffer = null; $this->caughtBuffer = null;
try { try {
\call_user_func($this->handler, $exception); ($this->handler)($exception);
$this->caughtLength = $caughtLength; $this->caughtLength = $caughtLength;
} catch (\Exception $e) { } catch (\Exception $e) {
if (!$caughtLength) { if (!$caughtLength) {

View File

@ -52,11 +52,11 @@ class PassConfig
new ValidateEnvPlaceholdersPass(), new ValidateEnvPlaceholdersPass(),
new ResolveChildDefinitionsPass(), new ResolveChildDefinitionsPass(),
new ServiceLocatorTagPass(), new ServiceLocatorTagPass(),
new RegisterServiceSubscribersPass(),
new DecoratorServicePass(), new DecoratorServicePass(),
new ResolveParameterPlaceHoldersPass(false), new ResolveParameterPlaceHoldersPass(false),
new ResolveFactoryClassPass(), new ResolveFactoryClassPass(),
new CheckDefinitionValidityPass(), new CheckDefinitionValidityPass(),
new RegisterServiceSubscribersPass(),
new ResolveNamedArgumentsPass(), new ResolveNamedArgumentsPass(),
new AutowireRequiredMethodsPass(), new AutowireRequiredMethodsPass(),
new ResolveBindingsPass(), new ResolveBindingsPass(),

View File

@ -1162,7 +1162,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
throw new InvalidArgumentException(sprintf('The configure callable for class "%s" is not a callable.', \get_class($service))); throw new InvalidArgumentException(sprintf('The configure callable for class "%s" is not a callable.', \get_class($service)));
} }
\call_user_func($callable, $service); $callable($service);
} }
return $service; return $service;

View File

@ -28,6 +28,6 @@ class RealServiceInstantiator implements InstantiatorInterface
*/ */
public function instantiateProxy(ContainerInterface $container, Definition $definition, $id, $realInstantiator) public function instantiateProxy(ContainerInterface $container, Definition $definition, $id, $realInstantiator)
{ {
return \call_user_func($realInstantiator); return $realInstantiator();
} }
} }

View File

@ -35,7 +35,7 @@ class ClosureLoader extends Loader
*/ */
public function load($resource, $type = null) public function load($resource, $type = null)
{ {
\call_user_func($resource, $this->container); $resource($this->container);
} }
/** /**

View File

@ -15,6 +15,8 @@ use Psr\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
/** /**
* ContainerBagInterface is the interface implemented by objects that manage service container parameters.
*
* @author Nicolas Grekas <p@tchwork.com> * @author Nicolas Grekas <p@tchwork.com>
*/ */
interface ContainerBagInterface extends ContainerInterface interface ContainerBagInterface extends ContainerInterface

View File

@ -15,7 +15,7 @@ use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
/** /**
* ParameterBagInterface. * ParameterBagInterface is the interface implemented by objects that manage service container parameters.
* *
* @author Fabien Potencier <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
*/ */

View File

@ -86,39 +86,40 @@ class ServiceLocator implements PsrContainerInterface
$class = isset($class[3]['object']) ? \get_class($class[3]['object']) : null; $class = isset($class[3]['object']) ? \get_class($class[3]['object']) : null;
$externalId = $this->externalId ?: $class; $externalId = $this->externalId ?: $class;
$msg = sprintf('Service "%s" not found: ', $id); $msg = array();
$msg[] = sprintf('Service "%s" not found:', $id);
if (!$this->container) { if (!$this->container) {
$class = null; $class = null;
} elseif ($this->container->has($id) || isset($this->container->getRemovedIds()[$id])) { } elseif ($this->container->has($id) || isset($this->container->getRemovedIds()[$id])) {
$msg .= 'even though it exists in the app\'s container, '; $msg[] = 'even though it exists in the app\'s container,';
} else { } else {
try { try {
$this->container->get($id); $this->container->get($id);
$class = null; $class = null;
} catch (ServiceNotFoundException $e) { } catch (ServiceNotFoundException $e) {
if ($e->getAlternatives()) { if ($e->getAlternatives()) {
$msg .= sprintf(' did you mean %s? Anyway, ', $this->formatAlternatives($e->getAlternatives(), 'or')); $msg[] = sprintf('did you mean %s? Anyway,', $this->formatAlternatives($e->getAlternatives(), 'or'));
} else { } else {
$class = null; $class = null;
} }
} }
} }
if ($externalId) { if ($externalId) {
$msg .= sprintf('the container inside "%s" is a smaller service locator that %s', $externalId, $this->formatAlternatives()); $msg[] = sprintf('the container inside "%s" is a smaller service locator that %s', $externalId, $this->formatAlternatives());
} else { } else {
$msg .= sprintf('the current service locator %s', $this->formatAlternatives()); $msg[] = sprintf('the current service locator %s', $this->formatAlternatives());
} }
if (!$class) { if (!$class) {
// no-op // no-op
} elseif (is_subclass_of($class, ServiceSubscriberInterface::class)) { } elseif (is_subclass_of($class, ServiceSubscriberInterface::class)) {
$msg .= sprintf(' Unless you need extra laziness, try using dependency injection instead. Otherwise, you need to declare it using "%s::getSubscribedServices()".', preg_replace('/([^\\\\]++\\\\)++/', '', $class)); $msg[] = sprintf('Unless you need extra laziness, try using dependency injection instead. Otherwise, you need to declare it using "%s::getSubscribedServices()".', preg_replace('/([^\\\\]++\\\\)++/', '', $class));
} else { } else {
$msg .= 'Try using dependency injection instead.'; $msg[] = 'Try using dependency injection instead.';
} }
return new ServiceNotFoundException($id, end($this->loading) ?: null, null, array(), $msg); return new ServiceNotFoundException($id, end($this->loading) ?: null, null, array(), implode(' ', $msg));
} }
private function createCircularReferenceException(string $id, array $path): ContainerExceptionInterface private function createCircularReferenceException(string $id, array $path): ContainerExceptionInterface

View File

@ -17,6 +17,7 @@ use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
/** /**
* This class tests the integration of the different compiler passes. * This class tests the integration of the different compiler passes.
@ -120,6 +121,21 @@ class IntegrationTest extends TestCase
$this->assertFalse($container->hasDefinition('c'), 'Service C was not inlined.'); $this->assertFalse($container->hasDefinition('c'), 'Service C was not inlined.');
} }
public function testCanDecorateServiceSubscriber()
{
$container = new ContainerBuilder();
$container->register(ServiceSubscriberStub::class)
->addTag('container.service_subscriber')
->setPublic(true);
$container->register(DecoratedServiceSubscriber::class)
->setDecoratedService(ServiceSubscriberStub::class);
$container->compile();
$this->assertInstanceOf(DecoratedServiceSubscriber::class, $container->get(ServiceSubscriberStub::class));
}
/** /**
* @dataProvider getYamlCompileTests * @dataProvider getYamlCompileTests
*/ */
@ -220,6 +236,18 @@ class IntegrationTest extends TestCase
} }
} }
class ServiceSubscriberStub implements ServiceSubscriberInterface
{
public static function getSubscribedServices()
{
return array();
}
}
class DecoratedServiceSubscriber
{
}
class IntegrationTestStub extends IntegrationTestStubParent class IntegrationTestStub extends IntegrationTestStubParent
{ {
} }

View File

@ -61,6 +61,20 @@ class ServiceLocatorTest extends BaseServiceLocatorTest
$subscriber->getFoo(); $subscriber->getFoo();
} }
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException
* @expectedExceptionMessage Service "foo" not found: even though it exists in the app's container, the container inside "foo" is a smaller service locator that is empty... Try using dependency injection instead.
*/
public function testGetThrowsServiceNotFoundException()
{
$container = new Container();
$container->set('foo', new \stdClass());
$locator = new ServiceLocator(array());
$locator = $locator->withContext('foo', $container);
$locator->get('foo');
}
public function testInvoke() public function testInvoke()
{ {
$locator = $this->getServiceLocator(array( $locator = $this->getServiceLocator(array(

View File

@ -110,7 +110,7 @@ class WrappedListener
$e = $this->stopwatch->start($this->name, 'event_listener'); $e = $this->stopwatch->start($this->name, 'event_listener');
\call_user_func($this->optimizedListener, $event, $eventName, $this->dispatcher ?: $dispatcher); ($this->optimizedListener)($event, $eventName, $this->dispatcher ?: $dispatcher);
if ($e->isStarted()) { if ($e->isStarted()) {
$e->stop(); $e->stop();

View File

@ -218,7 +218,7 @@ class EventDispatcher implements EventDispatcherInterface
if ($event->isPropagationStopped()) { if ($event->isPropagationStopped()) {
break; break;
} }
\call_user_func($listener, $event, $eventName, $this); $listener($event, $eventName, $this);
} }
} }

View File

@ -51,7 +51,7 @@ class CustomFilterIterator extends \FilterIterator
$fileinfo = $this->current(); $fileinfo = $this->current();
foreach ($this->filters as $filter) { foreach ($this->filters as $filter) {
if (false === \call_user_func($filter, $fileinfo)) { if (false === $filter($fileinfo)) {
return false; return false;
} }
} }

View File

@ -73,7 +73,7 @@ class SortableIterator implements \IteratorAggregate
} elseif (self::SORT_BY_NONE === $sort) { } elseif (self::SORT_BY_NONE === $sort) {
$this->sort = $order; $this->sort = $order;
} elseif (\is_callable($sort)) { } elseif (\is_callable($sort)) {
$this->sort = $reverseOrder ? function ($a, $b) use ($sort) { return -\call_user_func($sort, $a, $b); } : $sort; $this->sort = $reverseOrder ? function ($a, $b) use ($sort) { return -$sort($a, $b); } : $sort;
} else { } else {
throw new \InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.'); throw new \InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.');
} }

View File

@ -41,7 +41,7 @@ class CallbackTransformer implements DataTransformerInterface
*/ */
public function transform($data) public function transform($data)
{ {
return \call_user_func($this->transform, $data); return ($this->transform)($data);
} }
/** /**
@ -57,6 +57,6 @@ class CallbackTransformer implements DataTransformerInterface
*/ */
public function reverseTransform($data) public function reverseTransform($data)
{ {
return \call_user_func($this->reverseTransform, $data); return ($this->reverseTransform)($data);
} }
} }

View File

@ -155,7 +155,7 @@ class ArrayChoiceList implements ChoiceListInterface
$givenValues = array(); $givenValues = array();
foreach ($choices as $i => $givenChoice) { foreach ($choices as $i => $givenChoice) {
$givenValues[$i] = (string) \call_user_func($this->valueCallback, $givenChoice); $givenValues[$i] = (string) ($this->valueCallback)($givenChoice);
} }
return array_intersect($givenValues, array_keys($this->choices)); return array_intersect($givenValues, array_keys($this->choices));
@ -202,7 +202,7 @@ class ArrayChoiceList implements ChoiceListInterface
continue; continue;
} }
$choiceValue = (string) \call_user_func($value, $choice); $choiceValue = (string) $value($choice);
$choicesByValues[$choiceValue] = $choice; $choicesByValues[$choiceValue] = $choice;
$keysByValues[$choiceValue] = $key; $keysByValues[$choiceValue] = $key;
$structuredValues[$key] = $choiceValue; $structuredValues[$key] = $choiceValue;

View File

@ -118,7 +118,7 @@ class DefaultChoiceListFactory implements ChoiceListFactoryInterface
// $value may be an integer or a string, since it's stored in the array // $value may be an integer or a string, since it's stored in the array
// keys. We want to guarantee it's a string though. // keys. We want to guarantee it's a string though.
$key = $keys[$value]; $key = $keys[$value];
$nextIndex = \is_int($index) ? $index++ : \call_user_func($index, $choice, $key, $value); $nextIndex = \is_int($index) ? $index++ : $index($choice, $key, $value);
// BC normalize label to accept a false value // BC normalize label to accept a false value
if (null === $label) { if (null === $label) {
@ -127,7 +127,7 @@ class DefaultChoiceListFactory implements ChoiceListFactoryInterface
} elseif (false !== $label) { } elseif (false !== $label) {
// If "choice_label" is set to false and "expanded" is true, the value false // If "choice_label" is set to false and "expanded" is true, the value false
// should be passed on to the "label" option of the checkboxes/radio buttons // should be passed on to the "label" option of the checkboxes/radio buttons
$dynamicLabel = \call_user_func($label, $choice, $key, $value); $dynamicLabel = $label($choice, $key, $value);
$label = false === $dynamicLabel ? false : (string) $dynamicLabel; $label = false === $dynamicLabel ? false : (string) $dynamicLabel;
} }
@ -137,11 +137,11 @@ class DefaultChoiceListFactory implements ChoiceListFactoryInterface
$label, $label,
// The attributes may be a callable or a mapping from choice indices // The attributes may be a callable or a mapping from choice indices
// to nested arrays // to nested arrays
\is_callable($attr) ? \call_user_func($attr, $choice, $key, $value) : (isset($attr[$key]) ? $attr[$key] : array()) \is_callable($attr) ? $attr($choice, $key, $value) : (isset($attr[$key]) ? $attr[$key] : array())
); );
// $isPreferred may be null if no choices are preferred // $isPreferred may be null if no choices are preferred
if ($isPreferred && \call_user_func($isPreferred, $choice, $key, $value)) { if ($isPreferred && $isPreferred($choice, $key, $value)) {
$preferredViews[$nextIndex] = $view; $preferredViews[$nextIndex] = $view;
} else { } else {
$otherViews[$nextIndex] = $view; $otherViews[$nextIndex] = $view;
@ -200,7 +200,7 @@ class DefaultChoiceListFactory implements ChoiceListFactoryInterface
private static function addChoiceViewGroupedBy($groupBy, $choice, $value, $label, $keys, &$index, $attr, $isPreferred, &$preferredViews, &$otherViews) private static function addChoiceViewGroupedBy($groupBy, $choice, $value, $label, $keys, &$index, $attr, $isPreferred, &$preferredViews, &$otherViews)
{ {
$groupLabel = \call_user_func($groupBy, $choice, $keys[$value], $value); $groupLabel = $groupBy($choice, $keys[$value], $value);
if (null === $groupLabel) { if (null === $groupLabel) {
// If the callable returns null, don't group the choice // If the callable returns null, don't group the choice

View File

@ -46,7 +46,7 @@ class CallbackChoiceLoader implements ChoiceLoaderInterface
return $this->choiceList; return $this->choiceList;
} }
return $this->choiceList = new ArrayChoiceList(\call_user_func($this->callback), $value); return $this->choiceList = new ArrayChoiceList(($this->callback)(), $value);
} }
/** /**

View File

@ -30,11 +30,6 @@ class IntlCallbackChoiceLoader extends CallbackChoiceLoader
return array(); return array();
} }
// If no callable is set, values are the same as choices
if (null === $value) {
return $values;
}
return $this->loadChoiceList($value)->getChoicesForValues($values); return $this->loadChoiceList($value)->getChoicesForValues($values);
} }

View File

@ -135,7 +135,7 @@ class ResizeFormListener implements EventSubscriberInterface
/** @var FormInterface $child */ /** @var FormInterface $child */
foreach ($form as $name => $child) { foreach ($form as $name => $child) {
$isNew = !isset($previousData[$name]); $isNew = !isset($previousData[$name]);
$isEmpty = \is_callable($this->deleteEmpty) ? \call_user_func($this->deleteEmpty, $child->getData()) : $child->isEmpty(); $isEmpty = \is_callable($this->deleteEmpty) ? ($this->deleteEmpty)($child->getData()) : $child->isEmpty();
// $isNew can only be true if allowAdd is true, so we don't // $isNew can only be true if allowAdd is true, so we don't
// need to check allowAdd again // need to check allowAdd again

View File

@ -106,11 +106,6 @@ class CountryType extends AbstractType implements ChoiceLoaderInterface
return array(); return array();
} }
// If no callable is set, values are the same as choices
if (null === $value) {
return $values;
}
return $this->loadChoiceList($value)->getChoicesForValues($values); return $this->loadChoiceList($value)->getChoicesForValues($values);
} }

View File

@ -106,11 +106,6 @@ class CurrencyType extends AbstractType implements ChoiceLoaderInterface
return array(); return array();
} }
// If no callable is set, values are the same as choices
if (null === $value) {
return $values;
}
return $this->loadChoiceList($value)->getChoicesForValues($values); return $this->loadChoiceList($value)->getChoicesForValues($values);
} }

View File

@ -106,11 +106,6 @@ class LanguageType extends AbstractType implements ChoiceLoaderInterface
return array(); return array();
} }
// If no callable is set, values are the same as choices
if (null === $value) {
return $values;
}
return $this->loadChoiceList($value)->getChoicesForValues($values); return $this->loadChoiceList($value)->getChoicesForValues($values);
} }

View File

@ -106,11 +106,6 @@ class LocaleType extends AbstractType implements ChoiceLoaderInterface
return array(); return array();
} }
// If no callable is set, values are the same as choices
if (null === $value) {
return $values;
}
return $this->loadChoiceList($value)->getChoicesForValues($values); return $this->loadChoiceList($value)->getChoicesForValues($values);
} }

View File

@ -73,7 +73,7 @@ class HttpFoundationRequestHandler implements RequestHandlerInterface
$form->submit(null, false); $form->submit(null, false);
$form->addError(new FormError( $form->addError(new FormError(
\call_user_func($form->getConfig()->getOption('upload_max_size_message')), $form->getConfig()->getOption('upload_max_size_message')(),
null, null,
array('{{ max }}' => $this->serverParams->getNormalizedIniPostMaxSize()) array('{{ max }}' => $this->serverParams->getNormalizedIniPostMaxSize())
)); ));

View File

@ -180,7 +180,7 @@ class FormValidator extends ConstraintValidator
private static function resolveValidationGroups($groups, FormInterface $form) private static function resolveValidationGroups($groups, FormInterface $form)
{ {
if (!\is_string($groups) && \is_callable($groups)) { if (!\is_string($groups) && \is_callable($groups)) {
$groups = \call_user_func($groups, $form); $groups = $groups($form);
} }
if ($groups instanceof GroupSequence) { if ($groups instanceof GroupSequence) {

View File

@ -51,7 +51,7 @@ class ValidationListener implements EventSubscriberInterface
$form = $event->getForm(); $form = $event->getForm();
if ($form->isRoot()) { if ($form->isRoot()) {
// Validate the form in group "Default" // Form groups are validated internally (FormValidator). Here we don't set groups as they are retrieved into the validator.
foreach ($this->validator->validate($form) as $violation) { foreach ($this->validator->validate($form) as $violation) {
// Allow the "invalid" constraint to be put onto // Allow the "invalid" constraint to be put onto
// non-synchronized forms // non-synchronized forms

View File

@ -48,7 +48,7 @@ class UploadValidatorExtension extends AbstractTypeExtension
$translationDomain = $this->translationDomain; $translationDomain = $this->translationDomain;
$resolver->setNormalizer('upload_max_size_message', function (Options $options, $message) use ($translator, $translationDomain) { $resolver->setNormalizer('upload_max_size_message', function (Options $options, $message) use ($translator, $translationDomain) {
return function () use ($translator, $translationDomain, $message) { return function () use ($translator, $translationDomain, $message) {
return $translator->trans(\call_user_func($message), array(), $translationDomain); return $translator->trans($message(), array(), $translationDomain);
}; };
}); });
} }

View File

@ -532,11 +532,12 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac
$submittedData = null; $submittedData = null;
} elseif (is_scalar($submittedData)) { } elseif (is_scalar($submittedData)) {
$submittedData = (string) $submittedData; $submittedData = (string) $submittedData;
} elseif ($this->config->getOption('allow_file_upload')) { } elseif (!$this->config->getOption('allow_file_upload') && $this->config->getRequestHandler()->isFileUpload($submittedData)) {
// no-op
} elseif ($this->config->getRequestHandler()->isFileUpload($submittedData)) {
$submittedData = null; $submittedData = null;
$this->transformationFailure = new TransformationFailedException('Submitted data was expected to be text or number, file upload given.'); $this->transformationFailure = new TransformationFailedException('Submitted data was expected to be text or number, file upload given.');
} elseif (\is_array($submittedData) && !$this->config->getCompound() && !$this->config->hasOption('multiple')) {
$submittedData = null;
$this->transformationFailure = new TransformationFailedException('Submitted data was expected to be text or number, array given.');
} }
$dispatcher = $this->config->getEventDispatcher(); $dispatcher = $this->config->getEventDispatcher();

View File

@ -12,6 +12,8 @@
namespace Symfony\Component\Form; namespace Symfony\Component\Form;
/** /**
* Allows creating a form based on a name, a class or a property.
*
* @author Bernhard Schussek <bschussek@gmail.com> * @author Bernhard Schussek <bschussek@gmail.com>
*/ */
interface FormFactoryInterface interface FormFactoryInterface

View File

@ -78,7 +78,7 @@ class NativeRequestHandler implements RequestHandlerInterface
$form->submit(null, false); $form->submit(null, false);
$form->addError(new FormError( $form->addError(new FormError(
\call_user_func($form->getConfig()->getOption('upload_max_size_message')), $form->getConfig()->getOption('upload_max_size_message')(),
null, null,
array('{{ max }}' => $this->serverParams->getNormalizedIniPostMaxSize()) array('{{ max }}' => $this->serverParams->getNormalizedIniPostMaxSize())
)); ));

View File

@ -84,6 +84,11 @@ class IntlCallbackChoiceLoaderTest extends TestCase
); );
} }
public function testLoadChoicesForValuesDropsNonExistentChoices()
{
$this->assertSame(array(), self::$loader->loadChoicesForValues(array('foo')));
}
public function testLoadValuesForChoicesLoadsChoiceListOnFirstCall() public function testLoadValuesForChoicesLoadsChoiceListOnFirstCall()
{ {
$this->assertSame( $this->assertSame(

View File

@ -1081,6 +1081,22 @@ class CompoundFormTest extends AbstractFormTest
$this->assertFalse($submit->isSubmitted()); $this->assertFalse($submit->isSubmitted());
} }
public function testArrayTransformationFailureOnSubmit()
{
$this->form->add($this->getBuilder('foo')->setCompound(false)->getForm());
$this->form->add($this->getBuilder('bar', null, null, array('multiple' => false))->setCompound(false)->getForm());
$this->form->submit(array(
'foo' => array('foo'),
'bar' => array('bar'),
));
$this->assertNull($this->form->get('foo')->getData());
$this->assertSame('Submitted data was expected to be text or number, array given.', $this->form->get('foo')->getTransformationFailure()->getMessage());
$this->assertSame(array('bar'), $this->form->get('bar')->getData());
}
public function testFileUpload() public function testFileUpload()
{ {
$reqHandler = new HttpFoundationRequestHandler(); $reqHandler = new HttpFoundationRequestHandler();

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type; namespace Symfony\Component\Form\Tests\Extension\Core\Type;
use Symfony\Component\Form\ChoiceList\View\ChoiceView; use Symfony\Component\Form\ChoiceList\View\ChoiceView;
use Symfony\Component\Form\Extension\Core\Type\CountryType;
use Symfony\Component\Intl\Util\IntlTestHelper; use Symfony\Component\Intl\Util\IntlTestHelper;
class CountryTypeTest extends BaseTypeTest class CountryTypeTest extends BaseTypeTest
@ -80,4 +81,14 @@ class CountryTypeTest extends BaseTypeTest
{ {
parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData); parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData);
} }
/**
* @group legacy
*/
public function testInvalidChoiceValuesAreDropped()
{
$type = new CountryType();
$this->assertSame(array(), $type->loadChoicesForValues(array('foo')));
}
} }

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type; namespace Symfony\Component\Form\Tests\Extension\Core\Type;
use Symfony\Component\Form\ChoiceList\View\ChoiceView; use Symfony\Component\Form\ChoiceList\View\ChoiceView;
use Symfony\Component\Form\Extension\Core\Type\CurrencyType;
use Symfony\Component\Intl\Util\IntlTestHelper; use Symfony\Component\Intl\Util\IntlTestHelper;
class CurrencyTypeTest extends BaseTypeTest class CurrencyTypeTest extends BaseTypeTest
@ -61,4 +62,14 @@ class CurrencyTypeTest extends BaseTypeTest
{ {
parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData); parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData);
} }
/**
* @group legacy
*/
public function testInvalidChoiceValuesAreDropped()
{
$type = new CurrencyType();
$this->assertSame(array(), $type->loadChoicesForValues(array('foo')));
}
} }

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type; namespace Symfony\Component\Form\Tests\Extension\Core\Type;
use Symfony\Component\Form\ChoiceList\View\ChoiceView; use Symfony\Component\Form\ChoiceList\View\ChoiceView;
use Symfony\Component\Form\Extension\Core\Type\LanguageType;
use Symfony\Component\Intl\Util\IntlTestHelper; use Symfony\Component\Intl\Util\IntlTestHelper;
class LanguageTypeTest extends BaseTypeTest class LanguageTypeTest extends BaseTypeTest
@ -73,4 +74,14 @@ class LanguageTypeTest extends BaseTypeTest
{ {
parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData); parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData);
} }
/**
* @group legacy
*/
public function testInvalidChoiceValuesAreDropped()
{
$type = new LanguageType();
$this->assertSame(array(), $type->loadChoicesForValues(array('foo')));
}
} }

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type; namespace Symfony\Component\Form\Tests\Extension\Core\Type;
use Symfony\Component\Form\ChoiceList\View\ChoiceView; use Symfony\Component\Form\ChoiceList\View\ChoiceView;
use Symfony\Component\Form\Extension\Core\Type\LocaleType;
use Symfony\Component\Intl\Util\IntlTestHelper; use Symfony\Component\Intl\Util\IntlTestHelper;
class LocaleTypeTest extends BaseTypeTest class LocaleTypeTest extends BaseTypeTest
@ -61,4 +62,14 @@ class LocaleTypeTest extends BaseTypeTest
{ {
parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData); parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData);
} }
/**
* @group legacy
*/
public function testInvalidChoiceValuesAreDropped()
{
$type = new LocaleType();
$this->assertSame(array(), $type->loadChoicesForValues(array('foo')));
}
} }

View File

@ -83,14 +83,6 @@ class UrlTypeTest extends TextTypeTest
)); ));
} }
public function testSubmitWithNonStringDataDoesNotBreakTheFixUrlProtocolListener()
{
$form = $this->factory->create(static::TESTED_TYPE);
$form->submit(array('domain.com', 'www.domain.com'));
$this->assertSame(array('domain.com', 'www.domain.com'), $form->getData());
}
public function testSubmitNullUsesDefaultEmptyData($emptyData = 'empty', $expectedData = 'http://empty') public function testSubmitNullUsesDefaultEmptyData($emptyData = 'empty', $expectedData = 'http://empty')
{ {
$form = $this->factory->create(static::TESTED_TYPE, null, array( $form = $this->factory->create(static::TESTED_TYPE, null, array(

View File

@ -220,7 +220,7 @@ class FormValidatorTest extends ConstraintValidatorTestCase
->getForm(); ->getForm();
// Launch transformer // Launch transformer
$form->submit(array()); $form->submit('foo');
$this->expectNoValidate(); $this->expectNoValidate();

View File

@ -41,6 +41,6 @@ class UploadValidatorExtensionTest extends TypeTestCase
$extension->configureOptions($resolver); $extension->configureOptions($resolver);
$options = $resolver->resolve(); $options = $resolver->resolve();
$this->assertEquals('translated max {{ max }}!', \call_user_func($options['upload_max_size_message'])); $this->assertEquals('translated max {{ max }}!', $options['upload_max_size_message']());
} }
} }

View File

@ -101,9 +101,9 @@ class HeaderBag implements \IteratorAggregate, \Countable
/** /**
* Returns a header value by name. * Returns a header value by name.
* *
* @param string $key The header name * @param string $key The header name
* @param string|string[]|null $default The default value * @param string|null $default The default value
* @param bool $first Whether to return the first value or all header values * @param bool $first Whether to return the first value or all header values
* *
* @return string|string[]|null The first header value or default value if $first is true, an array of values otherwise * @return string|string[]|null The first header value or default value if $first is true, an array of values otherwise
*/ */

View File

@ -1908,7 +1908,7 @@ class Request
private static function createRequestFromFactory(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) private static function createRequestFromFactory(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
{ {
if (self::$requestFactory) { if (self::$requestFactory) {
$request = \call_user_func(self::$requestFactory, $query, $request, $attributes, $cookies, $files, $server, $content); $request = (self::$requestFactory)($query, $request, $attributes, $cookies, $files, $server, $content);
if (!$request instanceof self) { if (!$request instanceof self) {
throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.'); throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.');

View File

@ -111,7 +111,7 @@ class StreamedResponse extends Response
throw new \LogicException('The Response callback must not be null.'); throw new \LogicException('The Response callback must not be null.');
} }
\call_user_func($this->callback); ($this->callback)();
return $this; return $this;
} }

View File

@ -390,7 +390,7 @@ class MockPdo extends \PDO
public function prepare($statement, $driverOptions = array()) public function prepare($statement, $driverOptions = array())
{ {
return \is_callable($this->prepareResult) return \is_callable($this->prepareResult)
? \call_user_func($this->prepareResult, $statement, $driverOptions) ? ($this->prepareResult)($statement, $driverOptions)
: $this->prepareResult; : $this->prepareResult;
} }

View File

@ -91,7 +91,7 @@ class FileLinkFormatter implements \Serializable
if ($this->requestStack && $this->baseDir && $this->urlFormat) { if ($this->requestStack && $this->baseDir && $this->urlFormat) {
$request = $this->requestStack->getMasterRequest(); $request = $this->requestStack->getMasterRequest();
if ($request instanceof Request) { if ($request instanceof Request) {
if ($this->urlFormat instanceof \Closure && !$this->urlFormat = \call_user_func($this->urlFormat)) { if ($this->urlFormat instanceof \Closure && !$this->urlFormat = ($this->urlFormat)()) {
return; return;
} }

View File

@ -12,7 +12,6 @@
namespace Symfony\Component\Messenger\DependencyInjection; namespace Symfony\Component\Messenger\DependencyInjection;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
@ -163,11 +162,7 @@ class MessengerPass implements CompilerPassInterface
foreach ($handlersByBusAndMessage as $bus => $handlersByMessage) { foreach ($handlersByBusAndMessage as $bus => $handlersByMessage) {
foreach ($handlersByMessage as $message => $handlerIds) { foreach ($handlersByMessage as $message => $handlerIds) {
$handlers = array_map(function (string $handlerId) { return new Reference($handlerId); }, $handlerIds); $handlers = array_map(function (string $handlerId) { return new Reference($handlerId); }, $handlerIds);
$handlersId = "messenger.handlers.$bus.$message"; $handlersLocatorMappingByBus[$bus][$message] = new IteratorArgument($handlers);
$definitions[$handlersId] = (new Definition(RewindableGenerator::class))
->setFactory('current')
->addArgument(array($handlers));
$handlersLocatorMappingByBus[$bus][$message] = new Reference($handlersId);
} }
} }
$container->addDefinitions($definitions); $container->addDefinitions($definitions);

View File

@ -12,7 +12,7 @@
namespace Symfony\Component\Messenger\Tests\DependencyInjection; namespace Symfony\Component\Messenger\Tests\DependencyInjection;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass; use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass;
use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass; use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
@ -67,8 +67,8 @@ class MessengerPassTest extends TestCase
$this->assertSame(HandlersLocator::class, $handlersLocatorDefinition->getClass()); $this->assertSame(HandlersLocator::class, $handlersLocatorDefinition->getClass());
$this->assertEquals( $this->assertEquals(
array( array(
DummyMessage::class => new Reference("messenger.handlers.$busId.".DummyMessage::class), DummyMessage::class => new IteratorArgument(array(new Reference(DummyHandler::class))),
SecondMessage::class => new Reference("messenger.handlers.$busId.".SecondMessage::class), SecondMessage::class => new IteratorArgument(array(new Reference(MissingArgumentTypeHandler::class))),
), ),
$handlersLocatorDefinition->getArgument(0) $handlersLocatorDefinition->getArgument(0)
); );
@ -107,8 +107,8 @@ class MessengerPassTest extends TestCase
$this->assertSame(HandlersLocator::class, $commandBusHandlersLocatorDefinition->getClass()); $this->assertSame(HandlersLocator::class, $commandBusHandlersLocatorDefinition->getClass());
$this->assertEquals( $this->assertEquals(
array( array(
MultipleBusesMessage::class => new Reference("messenger.handlers.$commandBusId.".MultipleBusesMessage::class), MultipleBusesMessage::class => new IteratorArgument(array(new Reference(MultipleBusesMessageHandler::class))),
DummyCommand::class => new Reference("messenger.handlers.$commandBusId.".DummyCommand::class), DummyCommand::class => new IteratorArgument(array(new Reference(DummyCommandHandler::class))),
), ),
$commandBusHandlersLocatorDefinition->getArgument(0) $commandBusHandlersLocatorDefinition->getArgument(0)
); );
@ -117,8 +117,8 @@ class MessengerPassTest extends TestCase
$this->assertSame(HandlersLocator::class, $queryBusHandlersLocatorDefinition->getClass()); $this->assertSame(HandlersLocator::class, $queryBusHandlersLocatorDefinition->getClass());
$this->assertEquals( $this->assertEquals(
array( array(
DummyQuery::class => new Reference("messenger.handlers.$queryBusId.".DummyQuery::class), DummyQuery::class => new IteratorArgument(array(new Reference(DummyQueryHandler::class))),
MultipleBusesMessage::class => new Reference("messenger.handlers.$queryBusId.".MultipleBusesMessage::class), MultipleBusesMessage::class => new IteratorArgument(array(new Reference(MultipleBusesMessageHandler::class))),
), ),
$queryBusHandlersLocatorDefinition->getArgument(0) $queryBusHandlersLocatorDefinition->getArgument(0)
); );
@ -155,13 +155,10 @@ class MessengerPassTest extends TestCase
$handlersMapping = $container->getDefinition($busId.'.messenger.handlers_locator')->getArgument(0); $handlersMapping = $container->getDefinition($busId.'.messenger.handlers_locator')->getArgument(0);
$this->assertArrayHasKey(DummyMessage::class, $handlersMapping); $this->assertArrayHasKey(DummyMessage::class, $handlersMapping);
$this->assertEquals(new Reference("messenger.handlers.$busId.".DummyMessage::class), $handlersMapping[DummyMessage::class]); $this->assertEquals(new IteratorArgument(array(new Reference(HandlerWithMultipleMessages::class))), $handlersMapping[DummyMessage::class]);
$this->assertArrayHasKey(SecondMessage::class, $handlersMapping); $this->assertArrayHasKey(SecondMessage::class, $handlersMapping);
$handlersDefinition = $container->getDefinition($handlersMapping[SecondMessage::class]); $this->assertEquals(new IteratorArgument(array(new Reference(PrioritizedHandler::class), new Reference(HandlerWithMultipleMessages::class))), $handlersMapping[SecondMessage::class]);
$this->assertSame(RewindableGenerator::class, $handlersDefinition->getClass());
$this->assertEquals(array(new Reference(PrioritizedHandler::class), new Reference(HandlerWithMultipleMessages::class)), $handlersDefinition->getArgument(0)[0]);
} }
public function testGetClassesAndMethodsAndPrioritiesFromTheSubscriber() public function testGetClassesAndMethodsAndPrioritiesFromTheSubscriber()
@ -183,13 +180,13 @@ class MessengerPassTest extends TestCase
$this->assertArrayHasKey(DummyMessage::class, $handlersMapping); $this->assertArrayHasKey(DummyMessage::class, $handlersMapping);
$this->assertArrayHasKey(SecondMessage::class, $handlersMapping); $this->assertArrayHasKey(SecondMessage::class, $handlersMapping);
$dummyHandlerReference = $container->getDefinition($handlersMapping[DummyMessage::class])->getArgument(0)[0][0]; $dummyHandlerReference = $handlersMapping[DummyMessage::class]->getValues()[0];
$dummyHandlerDefinition = $container->getDefinition($dummyHandlerReference); $dummyHandlerDefinition = $container->getDefinition($dummyHandlerReference);
$this->assertSame('callable', $dummyHandlerDefinition->getClass()); $this->assertSame('callable', $dummyHandlerDefinition->getClass());
$this->assertEquals(array(new Reference(HandlerMappingMethods::class), 'dummyMethod'), $dummyHandlerDefinition->getArgument(0)); $this->assertEquals(array(new Reference(HandlerMappingMethods::class), 'dummyMethod'), $dummyHandlerDefinition->getArgument(0));
$this->assertSame(array('Closure', 'fromCallable'), $dummyHandlerDefinition->getFactory()); $this->assertSame(array('Closure', 'fromCallable'), $dummyHandlerDefinition->getFactory());
$secondHandlerReference = $container->getDefinition($handlersMapping[SecondMessage::class])->getArgument(0)[0][1]; $secondHandlerReference = $handlersMapping[SecondMessage::class]->getValues()[1];
$secondHandlerDefinition = $container->getDefinition($secondHandlerReference); $secondHandlerDefinition = $container->getDefinition($secondHandlerReference);
$this->assertSame(PrioritizedHandler::class, $secondHandlerDefinition->getClass()); $this->assertSame(PrioritizedHandler::class, $secondHandlerDefinition->getClass());
} }
@ -281,11 +278,11 @@ class MessengerPassTest extends TestCase
$handlersMapping = $container->getDefinition($busId.'.messenger.handlers_locator')->getArgument(0); $handlersMapping = $container->getDefinition($busId.'.messenger.handlers_locator')->getArgument(0);
$this->assertArrayHasKey(DummyMessage::class, $handlersMapping); $this->assertArrayHasKey(DummyMessage::class, $handlersMapping);
$firstReference = $container->getDefinition($handlersMapping[DummyMessage::class])->getArgument(0)[0][0]; $firstReference = $handlersMapping[DummyMessage::class]->getValues()[0];
$this->assertEquals(array(new Reference(HandlerWithGenerators::class), 'dummyMethod'), $container->getDefinition($firstReference)->getArgument(0)); $this->assertEquals(array(new Reference(HandlerWithGenerators::class), 'dummyMethod'), $container->getDefinition($firstReference)->getArgument(0));
$this->assertArrayHasKey(SecondMessage::class, $handlersMapping); $this->assertArrayHasKey(SecondMessage::class, $handlersMapping);
$secondReference = $container->getDefinition($handlersMapping[SecondMessage::class])->getArgument(0)[0][0]; $secondReference = $handlersMapping[SecondMessage::class]->getValues()[0];
$this->assertEquals(array(new Reference(HandlerWithGenerators::class), 'secondMessage'), $container->getDefinition($secondReference)->getArgument(0)); $this->assertEquals(array(new Reference(HandlerWithGenerators::class), 'secondMessage'), $container->getDefinition($secondReference)->getArgument(0));
} }
@ -304,13 +301,13 @@ class MessengerPassTest extends TestCase
$eventsHandlerMapping = $container->getDefinition($eventsBusId.'.messenger.handlers_locator')->getArgument(0); $eventsHandlerMapping = $container->getDefinition($eventsBusId.'.messenger.handlers_locator')->getArgument(0);
$this->assertEquals(array(DummyMessage::class), array_keys($eventsHandlerMapping)); $this->assertEquals(array(DummyMessage::class), array_keys($eventsHandlerMapping));
$firstReference = $container->getDefinition($eventsHandlerMapping[DummyMessage::class])->getArgument(0)[0][0]; $firstReference = $eventsHandlerMapping[DummyMessage::class]->getValues()[0];
$this->assertEquals(array(new Reference(HandlerOnSpecificBuses::class), 'dummyMethodForEvents'), $container->getDefinition($firstReference)->getArgument(0)); $this->assertEquals(array(new Reference(HandlerOnSpecificBuses::class), 'dummyMethodForEvents'), $container->getDefinition($firstReference)->getArgument(0));
$commandsHandlerMapping = $container->getDefinition($commandsBusId.'.messenger.handlers_locator')->getArgument(0); $commandsHandlerMapping = $container->getDefinition($commandsBusId.'.messenger.handlers_locator')->getArgument(0);
$this->assertEquals(array(DummyMessage::class), array_keys($commandsHandlerMapping)); $this->assertEquals(array(DummyMessage::class), array_keys($commandsHandlerMapping));
$firstReference = $container->getDefinition($commandsHandlerMapping[DummyMessage::class])->getArgument(0)[0][0]; $firstReference = $commandsHandlerMapping[DummyMessage::class]->getValues()[0];
$this->assertEquals(array(new Reference(HandlerOnSpecificBuses::class), 'dummyMethodForCommands'), $container->getDefinition($firstReference)->getArgument(0)); $this->assertEquals(array(new Reference(HandlerOnSpecificBuses::class), 'dummyMethodForCommands'), $container->getDefinition($firstReference)->getArgument(0));
} }

View File

@ -47,7 +47,7 @@ class OptionsResolverIntrospector
*/ */
public function getDefault(string $option) public function getDefault(string $option)
{ {
return \call_user_func($this->get, 'defaults', $option, sprintf('No default value was set for the "%s" option.', $option)); return ($this->get)('defaults', $option, sprintf('No default value was set for the "%s" option.', $option));
} }
/** /**
@ -57,7 +57,7 @@ class OptionsResolverIntrospector
*/ */
public function getLazyClosures(string $option): array public function getLazyClosures(string $option): array
{ {
return \call_user_func($this->get, 'lazy', $option, sprintf('No lazy closures were set for the "%s" option.', $option)); return ($this->get)('lazy', $option, sprintf('No lazy closures were set for the "%s" option.', $option));
} }
/** /**
@ -67,7 +67,7 @@ class OptionsResolverIntrospector
*/ */
public function getAllowedTypes(string $option): array public function getAllowedTypes(string $option): array
{ {
return \call_user_func($this->get, 'allowedTypes', $option, sprintf('No allowed types were set for the "%s" option.', $option)); return ($this->get)('allowedTypes', $option, sprintf('No allowed types were set for the "%s" option.', $option));
} }
/** /**
@ -77,7 +77,7 @@ class OptionsResolverIntrospector
*/ */
public function getAllowedValues(string $option): array public function getAllowedValues(string $option): array
{ {
return \call_user_func($this->get, 'allowedValues', $option, sprintf('No allowed values were set for the "%s" option.', $option)); return ($this->get)('allowedValues', $option, sprintf('No allowed values were set for the "%s" option.', $option));
} }
/** /**
@ -85,7 +85,7 @@ class OptionsResolverIntrospector
*/ */
public function getNormalizer(string $option): \Closure public function getNormalizer(string $option): \Closure
{ {
return \call_user_func($this->get, 'normalizers', $option, sprintf('No normalizer was set for the "%s" option.', $option)); return ($this->get)('normalizers', $option, sprintf('No normalizer was set for the "%s" option.', $option));
} }
/** /**
@ -95,6 +95,6 @@ class OptionsResolverIntrospector
*/ */
public function getDeprecationMessage(string $option) public function getDeprecationMessage(string $option)
{ {
return \call_user_func($this->get, 'deprecated', $option, sprintf('No deprecation was set for the "%s" option.', $option)); return ($this->get)('deprecated', $option, sprintf('No deprecation was set for the "%s" option.', $option));
} }
} }

View File

@ -1309,7 +1309,7 @@ class Process implements \IteratorAggregate
if ($this->outputDisabled) { if ($this->outputDisabled) {
return function ($type, $data) use ($callback) { return function ($type, $data) use ($callback) {
if (null !== $callback) { if (null !== $callback) {
return \call_user_func($callback, $type, $data); return $callback($type, $data);
} }
}; };
} }
@ -1324,7 +1324,7 @@ class Process implements \IteratorAggregate
} }
if (null !== $callback) { if (null !== $callback) {
return \call_user_func($callback, $type, $data); return $callback($type, $data);
} }
}; };
} }

View File

@ -67,7 +67,7 @@ abstract class ObjectRouteLoader extends Loader
throw new \BadMethodCallException(sprintf('Method "%s" not found on "%s" when importing routing resource "%s"', $method, \get_class($loaderObject), $resource)); throw new \BadMethodCallException(sprintf('Method "%s" not found on "%s" when importing routing resource "%s"', $method, \get_class($loaderObject), $resource));
} }
$routeCollection = \call_user_func(array($loaderObject, $method), $this); $routeCollection = $loaderObject->$method($this);
if (!$routeCollection instanceof RouteCollection) { if (!$routeCollection instanceof RouteCollection) {
$type = \is_object($routeCollection) ? \get_class($routeCollection) : \gettype($routeCollection); $type = \is_object($routeCollection) ? \get_class($routeCollection) : \gettype($routeCollection);

View File

@ -96,14 +96,22 @@ class GuardAuthenticationListener implements ListenerInterface
$request = $event->getRequest(); $request = $event->getRequest();
try { try {
if (null !== $this->logger) { if (null !== $this->logger) {
$this->logger->debug('Calling getCredentials() on guard authenticator.', array('firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator))); $this->logger->debug('Checking support on guard authenticator.', array('firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)));
} }
// abort the execution of the authenticator if it doesn't support the request // abort the execution of the authenticator if it doesn't support the request
if (!$guardAuthenticator->supports($request)) { if (!$guardAuthenticator->supports($request)) {
if (null !== $this->logger) {
$this->logger->debug('Guard authenticator does not support the request.', array('firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)));
}
return; return;
} }
if (null !== $this->logger) {
$this->logger->debug('Calling getCredentials() on guard authenticator.', array('firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)));
}
// allow the authenticator to fetch authentication info from the request // allow the authenticator to fetch authentication info from the request
$credentials = $guardAuthenticator->getCredentials($request); $credentials = $guardAuthenticator->getCredentials($request);

View File

@ -38,7 +38,7 @@ trait TargetPathTrait
* @param SessionInterface $session * @param SessionInterface $session
* @param string $providerKey The name of your firewall * @param string $providerKey The name of your firewall
* *
* @return string * @return string|null
*/ */
private function getTargetPath(SessionInterface $session, $providerKey) private function getTargetPath(SessionInterface $session, $providerKey)
{ {

View File

@ -413,25 +413,9 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn
unset($data[$key]); unset($data[$key]);
continue; continue;
} }
try {
if (null !== $constructorParameter->getClass()) {
if (!$this->serializer instanceof DenormalizerInterface) {
throw new LogicException(sprintf('Cannot create an instance of %s from serialized data because the serializer inject in "%s" is not a denormalizer', $constructorParameter->getClass(), self::class));
}
$parameterClass = $constructorParameter->getClass()->getName();
$parameterData = $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createChildContext($context, $paramName));
}
} catch (\ReflectionException $e) {
throw new RuntimeException(sprintf('Could not determine the class of the parameter "%s".', $key), 0, $e);
} catch (MissingConstructorArgumentsException $e) {
if (!$constructorParameter->getType()->allowsNull()) {
throw $e;
}
$parameterData = null;
}
// Don't run set for a parameter passed to the constructor // Don't run set for a parameter passed to the constructor
$params[] = $parameterData; $params[] = $this->denormalizeParameter($reflectionClass, $constructorParameter, $paramName, $parameterData, $context, $format);
unset($data[$key]); unset($data[$key]);
} elseif (array_key_exists($key, $context[static::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class] ?? array())) { } elseif (array_key_exists($key, $context[static::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class] ?? array())) {
$params[] = $context[static::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class][$key]; $params[] = $context[static::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class][$key];
@ -454,6 +438,31 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn
return new $class(); return new $class();
} }
/**
* @internal
*/
protected function denormalizeParameter(\ReflectionClass $class, \ReflectionParameter $parameter, $parameterName, $parameterData, array $context, $format = null)
{
try {
if (null !== $parameter->getClass()) {
if (!$this->serializer instanceof DenormalizerInterface) {
throw new LogicException(sprintf('Cannot create an instance of %s from serialized data because the serializer inject in "%s" is not a denormalizer', $parameter->getClass(), self::class));
}
$parameterClass = $parameter->getClass()->getName();
$parameterData = $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createChildContext($context, $parameterName));
}
} catch (\ReflectionException $e) {
throw new RuntimeException(sprintf('Could not determine the class of the parameter "%s".', $parameterName), 0, $e);
} catch (MissingConstructorArgumentsException $e) {
if (!$parameter->getType()->allowsNull()) {
throw $e;
}
$parameterData = null;
}
return $parameterData;
}
/** /**
* @param array $parentContext * @param array $parentContext
* @param string $attribute * @param string $attribute

View File

@ -94,7 +94,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
$data = array(); $data = array();
$stack = array(); $stack = array();
$attributes = $this->getAttributes($object, $format, $context); $attributes = $this->getAttributes($object, $format, $context);
$class = $this->objectClassResolver ? \call_user_func($this->objectClassResolver, $object) : \get_class($object); $class = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object);
$attributesMetadata = $this->classMetadataFactory ? $this->classMetadataFactory->getMetadataFor($class)->getAttributesMetadata() : null; $attributesMetadata = $this->classMetadataFactory ? $this->classMetadataFactory->getMetadataFor($class)->getAttributesMetadata() : null;
$maxDepthHandler = $context[self::MAX_DEPTH_HANDLER] ?? $this->defaultContext[self::MAX_DEPTH_HANDLER] ?? $this->maxDepthHandler; $maxDepthHandler = $context[self::MAX_DEPTH_HANDLER] ?? $this->defaultContext[self::MAX_DEPTH_HANDLER] ?? $this->maxDepthHandler;
@ -106,7 +106,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
$attributeValue = $this->getAttributeValue($object, $attribute, $format, $context); $attributeValue = $this->getAttributeValue($object, $attribute, $format, $context);
if ($maxDepthReached) { if ($maxDepthReached) {
$attributeValue = \call_user_func($maxDepthHandler, $attributeValue, $object, $attribute, $format, $context); $attributeValue = $maxDepthHandler($attributeValue, $object, $attribute, $format, $context);
} }
/** /**
@ -168,7 +168,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
*/ */
protected function getAttributes($object, $format = null, array $context) protected function getAttributes($object, $format = null, array $context)
{ {
$class = $this->objectClassResolver ? \call_user_func($this->objectClassResolver, $object) : \get_class($object); $class = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object);
$key = $class.'-'.$context['cache_key']; $key = $class.'-'.$context['cache_key'];
if (isset($this->attributesCache[$key])) { if (isset($this->attributesCache[$key])) {
@ -363,7 +363,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
return (float) $data; return (float) $data;
} }
if (\call_user_func('is_'.$builtinType, $data)) { if (('is_'.$builtinType)($data)) {
return $data; return $data;
} }
} }
@ -375,6 +375,18 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
throw new NotNormalizableValueException(sprintf('The type of the "%s" attribute for class "%s" must be one of "%s" ("%s" given).', $attribute, $currentClass, implode('", "', array_keys($expectedTypes)), \gettype($data))); throw new NotNormalizableValueException(sprintf('The type of the "%s" attribute for class "%s" must be one of "%s" ("%s" given).', $attribute, $currentClass, implode('", "', array_keys($expectedTypes)), \gettype($data)));
} }
/**
* @internal
*/
protected function denormalizeParameter(\ReflectionClass $class, \ReflectionParameter $parameter, $parameterName, $parameterData, array $context, $format = null)
{
if (null === $this->propertyTypeExtractor || null === $types = $this->propertyTypeExtractor->getTypes($class->getName(), $parameterName)) {
return parent::denormalizeParameter($class, $parameter, $parameterName, $parameterData, $context, $format);
}
return $this->validateAndDenormalize($class->getName(), $parameterName, $parameterData, $format, $context);
}
/** /**
* @return Type[]|null * @return Type[]|null
*/ */

View File

@ -53,7 +53,7 @@ class ArrayDenormalizer implements ContextAwareDenormalizerInterface, Serializer
$builtinType = isset($context['key_type']) ? $context['key_type']->getBuiltinType() : null; $builtinType = isset($context['key_type']) ? $context['key_type']->getBuiltinType() : null;
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
if (null !== $builtinType && !\call_user_func('is_'.$builtinType, $key)) { if (null !== $builtinType && !('is_'.$builtinType)($key)) {
throw new NotNormalizableValueException(sprintf('The type of the key "%s" must be "%s" ("%s" given).', $key, $builtinType, \gettype($key))); throw new NotNormalizableValueException(sprintf('The type of the key "%s" must be "%s" ("%s" given).', $key, $builtinType, \gettype($key)));
} }

View File

@ -0,0 +1,128 @@
<?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\Component\Serializer\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
class DeserializeNestedArrayOfObjectsTest extends TestCase
{
public function provider()
{
return array(
//from property PhpDoc
array(Zoo::class),
//from argument constructor PhpDoc
array(ZooImmutable::class),
);
}
/**
* @dataProvider provider
*/
public function testPropertyPhpDoc($class)
{
//GIVEN
$json = <<<EOF
{
"animals": [
{"name": "Bug"}
]
}
EOF;
$serializer = new Serializer(array(
new ObjectNormalizer(null, null, null, new PhpDocExtractor()),
new ArrayDenormalizer(),
), array('json' => new JsonEncoder()));
//WHEN
/** @var Zoo $zoo */
$zoo = $serializer->deserialize($json, $class, 'json');
//THEN
self::assertCount(1, $zoo->getAnimals());
self::assertInstanceOf(Animal::class, $zoo->getAnimals()[0]);
}
}
class Zoo
{
/** @var Animal[] */
private $animals = array();
/**
* @return Animal[]
*/
public function getAnimals()
{
return $this->animals;
}
/**
* @param Animal[] $animals
*/
public function setAnimals(array $animals)
{
$this->animals = $animals;
}
}
class ZooImmutable
{
/** @var Animal[] */
private $animals = array();
/**
* @param Animal[] $animals
*/
public function __construct(array $animals = array())
{
$this->animals = $animals;
}
/**
* @return Animal[]
*/
public function getAnimals()
{
return $this->animals;
}
}
class Animal
{
/** @var string */
private $name;
public function __construct()
{
echo '';
}
/**
* @return string|null
*/
public function getName()
{
return $this->name;
}
/**
* @param string|null $name
*/
public function setName($name)
{
$this->name = $name;
}
}

View File

@ -313,13 +313,13 @@ class PhpEngine implements EngineInterface, \ArrayAccess
// the performance when the same value is escaped multiple times (e.g. loops) // the performance when the same value is escaped multiple times (e.g. loops)
if (is_scalar($value)) { if (is_scalar($value)) {
if (!isset(self::$escaperCache[$context][$value])) { if (!isset(self::$escaperCache[$context][$value])) {
self::$escaperCache[$context][$value] = \call_user_func($this->getEscaper($context), $value); self::$escaperCache[$context][$value] = $this->getEscaper($context)($value);
} }
return self::$escaperCache[$context][$value]; return self::$escaperCache[$context][$value];
} }
return \call_user_func($this->getEscaper($context), $value); return $this->getEscaper($context)($value);
} }
/** /**

View File

@ -242,7 +242,7 @@ EOF
}; };
if (null !== $this->directoryIteratorProvider) { if (null !== $this->directoryIteratorProvider) {
return \call_user_func($this->directoryIteratorProvider, $directory, $default); return ($this->directoryIteratorProvider)($directory, $default);
} }
return $default($directory); return $default($directory);
@ -255,7 +255,7 @@ EOF
}; };
if (null !== $this->isReadableProvider) { if (null !== $this->isReadableProvider) {
return \call_user_func($this->isReadableProvider, $fileOrDirectory, $default); return ($this->isReadableProvider)($fileOrDirectory, $default);
} }
return $default($fileOrDirectory); return $default($fileOrDirectory);

View File

@ -46,7 +46,7 @@ class PluralizationRules
} }
if (isset(self::$rules[$locale])) { if (isset(self::$rules[$locale])) {
$return = \call_user_func(self::$rules[$locale], $number); $return = self::$rules[$locale]($number);
if (!\is_int($return) || $return < 0) { if (!\is_int($return) || $return < 0) {
return 0; return 0;

View File

@ -43,7 +43,7 @@ class CallbackValidator extends ConstraintValidator
throw new ConstraintDefinitionException(sprintf('%s targeted by Callback constraint is not a valid callable', json_encode($method))); throw new ConstraintDefinitionException(sprintf('%s targeted by Callback constraint is not a valid callable', json_encode($method)));
} }
\call_user_func($method, $object, $this->context, $constraint->payload); $method($object, $this->context, $constraint->payload);
} elseif (null !== $object) { } elseif (null !== $object) {
if (!method_exists($object, $method)) { if (!method_exists($object, $method)) {
throw new ConstraintDefinitionException(sprintf('Method "%s" targeted by Callback constraint does not exist in class %s', $method, \get_class($object))); throw new ConstraintDefinitionException(sprintf('Method "%s" targeted by Callback constraint does not exist in class %s', $method, \get_class($object)));

View File

@ -54,7 +54,7 @@ class ChoiceValidator extends ConstraintValidator
) { ) {
throw new ConstraintDefinitionException('The Choice constraint expects a valid callback'); throw new ConstraintDefinitionException('The Choice constraint expects a valid callback');
} }
$choices = \call_user_func($choices); $choices = $choices();
} else { } else {
$choices = $constraint->choices; $choices = $constraint->choices;
} }

View File

@ -130,6 +130,7 @@ class IbanValidator extends ConstraintValidator
'TN' => 'TN59\d{2}\d{3}\d{13}\d{2}', // Tunisia 'TN' => 'TN59\d{2}\d{3}\d{13}\d{2}', // Tunisia
'TR' => 'TR\d{2}\d{5}[\dA-Z]{1}[\dA-Z]{16}', // Turkey 'TR' => 'TR\d{2}\d{5}[\dA-Z]{1}[\dA-Z]{16}', // Turkey
'UA' => 'UA\d{2}\d{6}[\dA-Z]{19}', // Ukraine 'UA' => 'UA\d{2}\d{6}[\dA-Z]{19}', // Ukraine
'VA' => 'VA\d{2}\d{3}\d{15}', // Vatican City State
'VG' => 'VG\d{2}[A-Z]{4}\d{16}', // Virgin Islands, British 'VG' => 'VG\d{2}[A-Z]{4}\d{16}', // Virgin Islands, British
'WF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Wallis and Futuna Islands 'WF' => 'FR\d{2}\d{5}\d{5}[\dA-Z]{11}\d{2}', // Wallis and Futuna Islands
'XK' => 'XK\d{2}\d{4}\d{10}\d{2}', // Republic of Kosovo 'XK' => 'XK\d{2}\d{4}\d{10}\d{2}', // Republic of Kosovo

View File

@ -156,6 +156,7 @@ class IbanValidatorTest extends ConstraintValidatorTestCase
array('TR330006100519786457841326'), //Turkey array('TR330006100519786457841326'), //Turkey
array('UA213223130000026007233566001'), //Ukraine array('UA213223130000026007233566001'), //Ukraine
array('AE260211000000230064016'), //United Arab Emirates array('AE260211000000230064016'), //United Arab Emirates
array('VA59001123000012345678'), //Vatican City State
); );
} }
@ -274,6 +275,7 @@ class IbanValidatorTest extends ConstraintValidatorTestCase
array('TR3300061005197864578413261'), //Turkey array('TR3300061005197864578413261'), //Turkey
array('UA21AAAA1300000260072335660012'), //Ukraine array('UA21AAAA1300000260072335660012'), //Ukraine
array('AE2602110000002300640161'), //United Arab Emirates array('AE2602110000002300640161'), //United Arab Emirates
array('VA590011230000123456781'), //Vatican City State
); );
} }
@ -385,6 +387,7 @@ class IbanValidatorTest extends ConstraintValidatorTestCase
array('TR330006100519786457841327'), //Turkey array('TR330006100519786457841327'), //Turkey
array('UA213223130000026007233566002'), //Ukraine array('UA213223130000026007233566002'), //Ukraine
array('AE260211000000230064017'), //United Arab Emirates array('AE260211000000230064017'), //United Arab Emirates
array('VA59001123000012345671'), //Vatican City State
); );
} }

View File

@ -12,6 +12,10 @@
namespace Symfony\Component\Validator\Tests\Validator; namespace Symfony\Component\Validator\Tests\Validator;
use Symfony\Component\Translation\IdentityTranslator; use Symfony\Component\Translation\IdentityTranslator;
use Symfony\Component\Validator\Constraints\All;
use Symfony\Component\Validator\Constraints\Collection;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\ConstraintValidatorFactory; use Symfony\Component\Validator\ConstraintValidatorFactory;
use Symfony\Component\Validator\Context\ExecutionContextFactory; use Symfony\Component\Validator\Context\ExecutionContextFactory;
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface; use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
@ -95,4 +99,38 @@ class RecursiveValidatorTest extends AbstractTest
$validator->validate($entity, null, array()); $validator->validate($entity, null, array());
} }
public function testCollectionConstraintValidateAllGroupsForNestedConstraints()
{
$this->metadata->addPropertyConstraint('data', new Collection(array('fields' => array(
'one' => array(new NotBlank(array('groups' => 'one')), new Length(array('min' => 2, 'groups' => 'two'))),
'two' => array(new NotBlank(array('groups' => 'two'))),
))));
$entity = new Entity();
$entity->data = array('one' => 't', 'two' => '');
$violations = $this->validator->validate($entity, null, array('one', 'two'));
$this->assertCount(2, $violations);
$this->assertInstanceOf(Length::class, $violations->get(0)->getConstraint());
$this->assertInstanceOf(NotBlank::class, $violations->get(1)->getConstraint());
}
public function testAllConstraintValidateAllGroupsForNestedConstraints()
{
$this->metadata->addPropertyConstraint('data', new All(array('constraints' => array(
new NotBlank(array('groups' => 'one')),
new Length(array('min' => 2, 'groups' => 'two')),
))));
$entity = new Entity();
$entity->data = array('one' => 't', 'two' => '');
$violations = $this->validator->validate($entity, null, array('one', 'two'));
$this->assertCount(2, $violations);
$this->assertInstanceOf(NotBlank::class, $violations->get(0)->getConstraint());
$this->assertInstanceOf(Length::class, $violations->get(1)->getConstraint());
}
} }

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Validator\Validator; namespace Symfony\Component\Validator\Validator;
use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\Composite;
use Symfony\Component\Validator\Constraints\GroupSequence; use Symfony\Component\Validator\Constraints\GroupSequence;
use Symfony\Component\Validator\ConstraintValidatorFactoryInterface; use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;
use Symfony\Component\Validator\Context\ExecutionContext; use Symfony\Component\Validator\Context\ExecutionContext;
@ -788,6 +789,10 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
if (null !== $cacheKey) { if (null !== $cacheKey) {
$constraintHash = spl_object_hash($constraint); $constraintHash = spl_object_hash($constraint);
if ($constraint instanceof Composite) {
$constraintHash .= $group;
}
if ($context->isConstraintValidated($cacheKey, $constraintHash)) { if ($context->isConstraintValidated($cacheKey, $constraintHash)) {
continue; continue;
} }

View File

@ -231,7 +231,7 @@ abstract class AbstractCloner implements ClonerInterface
} }
if ($this->prevErrorHandler) { if ($this->prevErrorHandler) {
return \call_user_func($this->prevErrorHandler, $type, $msg, $file, $line, $context); return ($this->prevErrorHandler)($type, $msg, $file, $line, $context);
} }
return false; return false;

View File

@ -164,7 +164,7 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface
*/ */
protected function dumpLine($depth) protected function dumpLine($depth)
{ {
\call_user_func($this->lineDumper, $this->line, $depth, $this->indentPad); ($this->lineDumper)($this->line, $depth, $this->indentPad);
$this->line = ''; $this->line = '';
} }

View File

@ -41,7 +41,7 @@ class VarDumper
}; };
} }
return \call_user_func(self::$handler, $var); return (self::$handler)($var);
} }
public static function setHandler(callable $callable = null) public static function setHandler(callable $callable = null)

View File

@ -93,15 +93,9 @@ class Registry
throw new NotInstantiableTypeException($class); throw new NotInstantiableTypeException($class);
} }
} }
if (null !== $proto && !$proto instanceof \Throwable) { if (null !== $proto && !$proto instanceof \Throwable && !$proto instanceof \Serializable && !\method_exists($class, '__sleep')) {
try { try {
if (!$proto instanceof \Serializable && !\method_exists($class, '__sleep')) { serialize($proto);
serialize($proto);
} elseif ($instantiableWithoutConstructor) {
serialize($reflector->newInstanceWithoutConstructor());
} else {
serialize(unserialize(($proto instanceof \Serializable ? 'C:' : 'O:').\strlen($class).':"'.$class.'":0:{}'));
}
} catch (\Exception $e) { } catch (\Exception $e) {
throw new NotInstantiableTypeException($class, $e); throw new NotInstantiableTypeException($class, $e);
} }

View File

@ -0,0 +1,11 @@
<?php
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
$o = \Symfony\Component\VarExporter\Internal\Registry::unserialize([], [
'C:51:"Symfony\\Component\\VarExporter\\Tests\\FooSerializable":20:{a:1:{i:0;s:3:"bar";}}',
]),
null,
[],
$o[0],
[]
);

View File

@ -194,6 +194,8 @@ class VarExporterTest extends TestCase
yield array('wakeup-refl', $value); yield array('wakeup-refl', $value);
yield array('abstract-parent', new ConcreteClass()); yield array('abstract-parent', new ConcreteClass());
yield array('foo-serializable', new FooSerializable('bar'));
} }
} }
@ -342,3 +344,28 @@ class ConcreteClass extends AbstractClass
$this->setBar(234); $this->setBar(234);
} }
} }
class FooSerializable implements \Serializable
{
private $foo;
public function __construct(string $foo)
{
$this->foo = $foo;
}
public function getFoo(): string
{
return $this->foo;
}
public function serialize(): string
{
return serialize(array($this->getFoo()));
}
public function unserialize($str)
{
list($this->foo) = unserialize($str);
}
}

View File

@ -11,7 +11,7 @@ It can also be used with extensions defined in the [HTML5 link type extensions w
Resources Resources
--------- ---------
* [Documentation](https://symfony.com/doc/current/components/weblink/introduction.html) * [Documentation](https://symfony.com/doc/current/components/web_link.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html) * [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and * [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls) [send Pull Requests](https://github.com/symfony/symfony/pulls)

Some files were not shown because too many files have changed in this diff Show More