Merge branch '3.4' into 4.1

* 3.4:
  fix cs
  SCA: consolidate non empty array checks across codebase
  [cs] correct invalid @param types
  [Bridge/PhpUnit] Use composer to download phpunit
  [DI] fix taking lazy services into account when dumping the container
  [Form] Fixed empty data for compound date interval
  [Cache] fix optimizing Psr6Cache for AdapterInterface pools
  deal with explicitly enabled workflow nodes
This commit is contained in:
Nicolas Grekas 2018-11-20 17:14:00 +01:00
commit 713b7986fe
57 changed files with 329 additions and 139 deletions

View File

@ -11,7 +11,7 @@
*/
// Please update when phpunit needs to be reinstalled with fresh deps:
// Cache-Id-Version: 2017-11-22 09:30 UTC
// Cache-Id-Version: 2018-11-20 15:30 UTC
error_reporting(-1);
@ -89,29 +89,7 @@ if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__
@mkdir($PHPUNIT_DIR, 0777, true);
chdir($PHPUNIT_DIR);
if (file_exists("phpunit-$PHPUNIT_VERSION")) {
passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? '(del /S /F /Q %s & rmdir %1$s) >nul': 'rm -rf %s', "phpunit-$PHPUNIT_VERSION"));
}
if (extension_loaded('openssl') && filter_var(ini_get('allow_url_fopen'), FILTER_VALIDATE_BOOLEAN) && !isset($_SERVER['http_proxy']) && !isset($_SERVER['https_proxy'])) {
$remoteZip = "https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip";
$remoteZipStream = @fopen($remoteZip, 'rb');
if (!$remoteZipStream) {
throw new \RuntimeException("Could not find $remoteZip");
}
stream_copy_to_stream($remoteZipStream, fopen("$PHPUNIT_VERSION.zip", 'wb'));
} elseif ('\\' === DIRECTORY_SEPARATOR) {
passthru("certutil -urlcache -split -f \"https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip\" $PHPUNIT_VERSION.zip");
} else {
@unlink("$PHPUNIT_VERSION.zip");
passthru("wget -q https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip");
}
if (!class_exists('ZipArchive')) {
throw new \Exception('simple-phpunit requires the "zip" PHP extension to be installed and enabled in order to uncompress the downloaded PHPUnit packages.');
}
$zip = new ZipArchive();
$zip->open("$PHPUNIT_VERSION.zip");
$zip->extractTo(getcwd());
$zip->close();
passthru("$COMPOSER create-project --no-install --prefer-dist --no-scripts --no-plugins --no-progress --ansi phpunit/phpunit phpunit-$PHPUNIT_VERSION \"$PHPUNIT_VERSION.*\"");
chdir("phpunit-$PHPUNIT_VERSION");
if ($SYMFONY_PHPUNIT_REMOVE) {
passthru("$COMPOSER remove --no-update ".$SYMFONY_PHPUNIT_REMOVE);

View File

@ -18,6 +18,7 @@ use Symfony\Component\Asset\Package;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\Form\Form;
use Symfony\Component\Lock\Lock;
use Symfony\Component\Lock\Store\SemaphoreStore;
@ -220,10 +221,22 @@ class Configuration implements ConfigurationInterface
$workflows = $v;
unset($workflows['enabled']);
if (1 === \count($workflows) && isset($workflows[0]['enabled'])) {
if (1 === \count($workflows) && isset($workflows[0]['enabled']) && 1 === \count($workflows[0])) {
$workflows = array();
}
if (1 === \count($workflows) && isset($workflows['workflows']) && array_keys($workflows['workflows']) !== range(0, \count($workflows) - 1) && !empty(array_diff(array_keys($workflows['workflows']), array('audit_trail', 'type', 'marking_store', 'supports', 'support_strategy', 'initial_place', 'places', 'transitions')))) {
$workflows = $workflows['workflows'];
}
foreach ($workflows as $key => $workflow) {
if (isset($workflow['enabled']) && false === $workflow['enabled']) {
throw new LogicException(sprintf('Cannot disable a single workflow. Remove the configuration for the workflow "%s" instead.', $workflow['name']));
}
unset($workflows[$key]['enabled']);
}
$v = array(
'enabled' => true,
'workflows' => $workflows,

View File

@ -0,0 +1,19 @@
<?php
$container->loadFromExtension('framework', array(
'workflows' => array(
'enabled' => true,
'foo' => array(
'type' => 'workflow',
'supports' => array('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest'),
'initial_place' => 'bar',
'places' => array('bar', 'baz'),
'transitions' => array(
'bar_baz' => array(
'from' => array('foo'),
'to' => array('bar'),
),
),
),
),
));

View File

@ -0,0 +1,19 @@
<?php
$container->loadFromExtension('framework', array(
'workflows' => array(
'enabled' => true,
'workflows' => array(
'type' => 'workflow',
'supports' => array('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest'),
'initial_place' => 'bar',
'places' => array('bar', 'baz'),
'transitions' => array(
'bar_baz' => array(
'from' => array('foo'),
'to' => array('bar'),
),
),
),
),
));

View File

@ -0,0 +1,19 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:framework="http://symfony.com/schema/dic/symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
<framework:config>
<framework:workflow enabled="true" name="foo" type="workflow" initial-place="bar">
<framework:support>Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest</framework:support>
<framework:place>bar</framework:place>
<framework:place>baz</framework:place>
<framework:transition name="bar_baz">
<framework:from>bar</framework:from>
<framework:to>baz</framework:to>
</framework:transition>
</framework:workflow>
</framework:config>
</container>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:framework="http://symfony.com/schema/dic/symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
<framework:config>
<framework:workflow enabled="true" name="workflows" type="workflow" initial-place="bar">
<framework:support>Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest</framework:support>
<framework:place>bar</framework:place>
<framework:place>baz</framework:place>
<framework:transition name="bar_baz">
<framework:from>bar</framework:from>
<framework:to>baz</framework:to>
</framework:transition>
</framework:workflow>
</framework:config>
</container>

View File

@ -0,0 +1,16 @@
framework:
workflows:
enabled: true
workflows:
foo:
type: workflow
supports:
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
initial_place: bar
places:
- bar
- baz
transitions:
bar_baz:
from: [foo]
to: [bar]

View File

@ -0,0 +1,15 @@
framework:
workflows:
enabled: true
workflows:
type: workflow
supports:
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
initial_place: bar
places:
- bar
- baz
transitions:
bar_baz:
from: [foo]
to: [bar]

View File

@ -409,6 +409,20 @@ abstract class FrameworkExtensionTest extends TestCase
$this->assertTrue($container->hasDefinition('console.command.workflow_dump'));
}
public function testExplicitlyEnabledWorkflows()
{
$container = $this->createContainerFromFile('workflows_explicitly_enabled');
$this->assertTrue($container->hasDefinition('workflow.foo.definition'));
}
public function testExplicitlyEnabledWorkflowNamedWorkflows()
{
$container = $this->createContainerFromFile('workflows_explicitly_enabled_named_workflows');
$this->assertTrue($container->hasDefinition('workflow.workflows.definition'));
}
public function testEnabledPhpErrorsConfig()
{
$container = $this->createContainerFromFile('php_errors_enabled');

View File

@ -15,7 +15,7 @@ use Psr\Cache\CacheException as Psr6CacheException;
use Psr\Cache\CacheItemPoolInterface;
use Psr\SimpleCache\CacheException as SimpleCacheException;
use Psr\SimpleCache\CacheInterface;
use Symfony\Component\Cache\Adapter\AbstractAdapter;
use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\PruneableInterface;
@ -30,27 +30,36 @@ class Psr6Cache implements CacheInterface, PruneableInterface, ResettableInterfa
use ProxyTrait;
private $createCacheItem;
private $cacheItemPrototype;
public function __construct(CacheItemPoolInterface $pool)
{
$this->pool = $pool;
if ($pool instanceof AbstractAdapter) {
$this->createCacheItem = \Closure::bind(
function ($key, $value, $allowInt = false) {
if ($allowInt && \is_int($key)) {
$key = (string) $key;
} else {
CacheItem::validateKey($key);
}
$f = $this->createCacheItem;
return $f($key, $value, false);
},
$pool,
AbstractAdapter::class
);
if (!$pool instanceof AdapterInterface) {
return;
}
$cacheItemPrototype = &$this->cacheItemPrototype;
$createCacheItem = \Closure::bind(
function ($key, $value, $allowInt = false) use (&$cacheItemPrototype) {
$item = clone $cacheItemPrototype;
$item->key = $allowInt && \is_int($key) ? (string) $key : CacheItem::validateKey($key);
$item->value = $value;
$item->isHit = false;
return $item;
},
null,
CacheItem::class
);
$this->createCacheItem = function ($key, $value, $allowInt = false) use ($createCacheItem) {
if (null === $this->cacheItemPrototype) {
$this->get($allowInt && \is_int($key) ? (string) $key : $key);
}
$this->createCacheItem = $createCacheItem;
return $createCacheItem($key, $value, $allowInt);
};
}
/**
@ -65,6 +74,10 @@ class Psr6Cache implements CacheInterface, PruneableInterface, ResettableInterfa
} catch (Psr6CacheException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
if (null === $this->cacheItemPrototype) {
$this->cacheItemPrototype = clone $item;
$this->cacheItemPrototype->set(null);
}
return $item->isHit() ? $item->get() : $default;
}

View File

@ -54,7 +54,7 @@ trait AbstractTrait
/**
* Deletes all items in the pool.
*
* @param string The prefix used for all identifiers managed by this pool
* @param string $namespace The prefix used for all identifiers managed by this pool
*
* @return bool True if the pool was successfully cleared, false otherwise
*/

View File

@ -66,8 +66,8 @@ trait MemcachedTrait
* - 'memcached://user:pass@localhost?weight=33'
* - array(array('localhost', 11211, 33))
*
* @param array[]|string|string[] An array of servers, a DSN, or an array of DSNs
* @param array An array of options
* @param array[]|string|string[] $servers An array of servers, a DSN, or an array of DSNs
* @param array $options An array of options
*
* @return \Memcached
*

View File

@ -26,7 +26,7 @@ interface ConfigCacheFactoryInterface
* @param string $file The absolute cache file path
* @param callable $callable The callable to be executed when the cache needs to be filled (i. e. is not fresh). The cache will be passed as the only parameter to this callback
*
* @return ConfigCacheInterface $configCache The cache instance
* @return ConfigCacheInterface The cache instance
*/
public function cache($file, $callable);
}

View File

@ -92,7 +92,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
/**
* Gets the xml remappings that should be performed.
*
* @return array $remappings an array of the form array(array(string, string))
* @return array an array of the form array(array(string, string))
*/
public function getXmlRemappings()
{

View File

@ -368,7 +368,7 @@ abstract class BaseNode implements NodeInterface
*
* @param $value
*
* @return $value The normalized array value
* @return The normalized array value
*/
protected function preNormalize($value)
{

View File

@ -343,7 +343,7 @@ abstract class NodeDefinition implements NodeParentInterface
/**
* Instantiate and configure the node according to this definition.
*
* @return NodeInterface $node The node instance
* @return NodeInterface The node instance
*
* @throws InvalidDefinitionException When the definition is invalid
*/

View File

@ -276,7 +276,7 @@ class TableStyle
/**
* Gets crossing character.
*
* @return string $crossingChar
* @return string
*/
public function getCrossingChar()
{

View File

@ -21,8 +21,6 @@ interface InputAwareInterface
{
/**
* Sets the Console Input.
*
* @param InputInterface
*/
public function setInput(InputInterface $input);
}

View File

@ -37,6 +37,7 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements Repe
private $hasProxyDumper;
private $lazy;
private $expressionLanguage;
private $byConstructor;
/**
* @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls
@ -64,6 +65,7 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements Repe
$this->graph = $container->getCompiler()->getServiceReferenceGraph();
$this->graph->clear();
$this->lazy = false;
$this->byConstructor = false;
foreach ($container->getAliases() as $id => $alias) {
$targetId = $this->getDefinitionId((string) $alias);
@ -100,7 +102,8 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements Repe
$targetDefinition,
$value,
$this->lazy || ($this->hasProxyDumper && $targetDefinition && $targetDefinition->isLazy()),
ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior()
ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior(),
$this->byConstructor
);
return $value;
@ -118,8 +121,11 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements Repe
}
$this->lazy = false;
$byConstructor = $this->byConstructor;
$this->byConstructor = true;
$this->processValue($value->getFactory());
$this->processValue($value->getArguments());
$this->byConstructor = $byConstructor;
if (!$this->onlyConstructorArguments) {
$this->processValue($value->getProperties());

View File

@ -73,7 +73,7 @@ class ServiceReferenceGraph
/**
* Connects 2 nodes together in the Graph.
*/
public function connect(?string $sourceId, $sourceValue, ?string $destId, $destValue = null, $reference = null, bool $lazy = false, bool $weak = false)
public function connect(?string $sourceId, $sourceValue, ?string $destId, $destValue = null, $reference = null, bool $lazy = false, bool $weak = false, bool $byConstructor = false)
{
if (null === $sourceId || null === $destId) {
return;
@ -81,7 +81,7 @@ class ServiceReferenceGraph
$sourceNode = $this->createNode($sourceId, $sourceValue);
$destNode = $this->createNode($destId, $destValue);
$edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference, $lazy, $weak);
$edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference, $lazy, $weak, $byConstructor);
$sourceNode->addOutEdge($edge);
$destNode->addInEdge($edge);

View File

@ -25,14 +25,16 @@ class ServiceReferenceGraphEdge
private $value;
private $lazy;
private $weak;
private $byConstructor;
public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null, bool $lazy = false, bool $weak = false)
public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null, bool $lazy = false, bool $weak = false, bool $byConstructor = false)
{
$this->sourceNode = $sourceNode;
$this->destNode = $destNode;
$this->value = $value;
$this->lazy = $lazy;
$this->weak = $weak;
$this->byConstructor = $byConstructor;
}
/**
@ -84,4 +86,14 @@ class ServiceReferenceGraphEdge
{
return $this->weak;
}
/**
* Returns true if the edge links with a constructor argument.
*
* @return bool
*/
public function isReferencedByConstructor()
{
return $this->byConstructor;
}
}

View File

@ -406,7 +406,7 @@ class Definition
/**
* Sets the definition templates to conditionally apply on the current definition, keyed by parent interface/class.
*
* @param $instanceof ChildDefinition[]
* @param ChildDefinition[] $instanceof
*
* @return $this
*/

View File

@ -155,12 +155,16 @@ class PhpDumper extends Dumper
}
}
(new AnalyzeServiceReferencesPass(false))->process($this->container);
(new AnalyzeServiceReferencesPass(false, !$this->getProxyDumper() instanceof NullDumper))->process($this->container);
$this->circularReferences = array();
$checkedNodes = array();
foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) {
$currentPath = array($id => $id);
$this->analyzeCircularReferences($node->getOutEdges(), $checkedNodes, $currentPath);
foreach (array(true, false) as $byConstructor) {
foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) {
if (!$node->getValue() instanceof Definition) {
continue;
}
$currentPath = array($id => true);
$this->analyzeCircularReferences($node->getOutEdges(), $currentPath, $id, $byConstructor);
}
}
$this->container->getCompiler()->getServiceReferenceGraph()->clear();
@ -303,27 +307,31 @@ EOF;
return $this->proxyDumper;
}
private function analyzeCircularReferences(array $edges, &$checkedNodes, &$currentPath)
private function analyzeCircularReferences(array $edges, &$currentPath, $sourceId, $byConstructor)
{
foreach ($edges as $edge) {
if ($byConstructor && !$edge->isReferencedByConstructor()) {
continue;
}
$node = $edge->getDestNode();
$id = $node->getId();
if ($node->getValue() && ($edge->isLazy() || $edge->isWeak())) {
if (!$node->getValue() instanceof Definition || $sourceId === $id || $edge->isLazy() || $edge->isWeak()) {
// no-op
} elseif (isset($currentPath[$id])) {
$currentId = $id;
foreach (array_reverse($currentPath) as $parentId) {
$this->circularReferences[$parentId][$currentId] = $currentId;
if (!isset($this->circularReferences[$parentId][$currentId])) {
$this->circularReferences[$parentId][$currentId] = $byConstructor;
}
if ($parentId === $id) {
break;
}
$currentId = $parentId;
}
} elseif (!isset($checkedNodes[$id])) {
$checkedNodes[$id] = true;
} else {
$currentPath[$id] = $id;
$this->analyzeCircularReferences($node->getOutEdges(), $checkedNodes, $currentPath);
$this->analyzeCircularReferences($node->getOutEdges(), $currentPath, $id, $byConstructor);
unset($currentPath[$id]);
}
}
@ -667,8 +675,14 @@ EOF;
private function addInlineReference(string $id, Definition $definition, string $targetId, bool $forConstructor): string
{
list($callCount, $behavior) = $this->serviceCalls[$targetId];
while ($this->container->hasAlias($targetId)) {
$targetId = (string) $this->container->getAlias($targetId);
}
if ($id === $targetId) {
return $this->addInlineService($id, $definition, $definition, $forConstructor);
return $this->addInlineService($id, $definition, $definition);
}
if ('service_container' === $targetId || isset($this->referenceVariables[$targetId])) {
@ -677,9 +691,7 @@ EOF;
$hasSelfRef = isset($this->circularReferences[$id][$targetId]);
$forConstructor = $forConstructor && !isset($this->definitionVariables[$definition]);
list($callCount, $behavior) = $this->serviceCalls[$targetId];
$code = $hasSelfRef && !$forConstructor ? $this->addInlineService($id, $definition, $definition, $forConstructor) : '';
$code = $hasSelfRef && $this->circularReferences[$id][$targetId] && !$forConstructor ? $this->addInlineService($id, $definition, $definition) : '';
if (isset($this->referenceVariables[$targetId]) || (2 > $callCount && (!$hasSelfRef || !$forConstructor))) {
return $code;
@ -1222,7 +1234,7 @@ EOF;
/*{$this->docStar}
* Computes a dynamic parameter.
*
* @param string The name of the dynamic parameter to load
* @param string \$name The name of the dynamic parameter to load
*
* @return mixed The value of the dynamic parameter
*

View File

@ -115,7 +115,7 @@ class ProjectServiceContainer extends Container
/**
* Computes a dynamic parameter.
*
* @param string The name of the dynamic parameter to load
* @param string $name The name of the dynamic parameter to load
*
* @return mixed The value of the dynamic parameter
*

View File

@ -122,7 +122,7 @@ class ProjectServiceContainer extends Container
/**
* Computes a dynamic parameter.
*
* @param string The name of the dynamic parameter to load
* @param string $name The name of the dynamic parameter to load
*
* @return mixed The value of the dynamic parameter
*

View File

@ -132,7 +132,7 @@ class ProjectServiceContainer extends Container
/**
* Computes a dynamic parameter.
*
* @param string The name of the dynamic parameter to load
* @param string $name The name of the dynamic parameter to load
*
* @return mixed The value of the dynamic parameter
*

View File

@ -138,7 +138,7 @@ class Symfony_DI_PhpDumper_Test_EnvParameters extends Container
/**
* Computes a dynamic parameter.
*
* @param string The name of the dynamic parameter to load
* @param string $name The name of the dynamic parameter to load
*
* @return mixed The value of the dynamic parameter
*

View File

@ -102,7 +102,7 @@ class ProjectServiceContainer extends Container
/**
* Computes a dynamic parameter.
*
* @param string The name of the dynamic parameter to load
* @param string $name The name of the dynamic parameter to load
*
* @return mixed The value of the dynamic parameter
*

View File

@ -535,7 +535,7 @@ class ProjectServiceContainer extends Container
/**
* Computes a dynamic parameter.
*
* @param string The name of the dynamic parameter to load
* @param string $name The name of the dynamic parameter to load
*
* @return mixed The value of the dynamic parameter
*

View File

@ -475,7 +475,7 @@ class ProjectServiceContainer extends Container
/**
* Computes a dynamic parameter.
*
* @param string The name of the dynamic parameter to load
* @param string $name The name of the dynamic parameter to load
*
* @return mixed The value of the dynamic parameter
*

View File

@ -162,11 +162,16 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Public extends Container
*/
protected function getConnectionService()
{
$a = new \stdClass();
$a = ($this->services['dispatcher'] ?? $this->getDispatcherService());
$this->services['connection'] = $instance = new \stdClass(($this->services['dispatcher'] ?? $this->getDispatcherService()), $a);
if (isset($this->services['connection'])) {
return $this->services['connection'];
}
$b = new \stdClass();
$a->logger = ($this->services['logger'] ?? $this->getLoggerService());
$this->services['connection'] = $instance = new \stdClass($a, $b);
$b->logger = ($this->services['logger'] ?? $this->getLoggerService());
return $instance;
}
@ -178,14 +183,19 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Public extends Container
*/
protected function getConnection2Service()
{
$a = new \stdClass();
$a = ($this->services['dispatcher2'] ?? $this->getDispatcher2Service());
$this->services['connection2'] = $instance = new \stdClass(($this->services['dispatcher2'] ?? $this->getDispatcher2Service()), $a);
if (isset($this->services['connection2'])) {
return $this->services['connection2'];
}
$b = new \stdClass();
$b = new \stdClass($instance);
$b->handler2 = new \stdClass(($this->services['manager2'] ?? $this->getManager2Service()));
$this->services['connection2'] = $instance = new \stdClass($a, $b);
$a->logger2 = $b;
$c = new \stdClass($instance);
$c->handler2 = new \stdClass(($this->services['manager2'] ?? $this->getManager2Service()));
$b->logger2 = $c;
return $instance;
}
@ -425,7 +435,13 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Public extends Container
*/
protected function getSubscriberService()
{
return $this->services['subscriber'] = new \stdClass(($this->services['manager'] ?? $this->getManagerService()));
$a = ($this->services['manager'] ?? $this->getManagerService());
if (isset($this->services['subscriber'])) {
return $this->services['subscriber'];
}
return $this->services['subscriber'] = new \stdClass($a);
}
/**

View File

@ -125,7 +125,7 @@ class ProjectServiceContainer extends Container
/**
* Computes a dynamic parameter.
*
* @param string The name of the dynamic parameter to load
* @param string $name The name of the dynamic parameter to load
*
* @return mixed The value of the dynamic parameter
*

View File

@ -104,7 +104,7 @@ class Symfony_DI_PhpDumper_Test_Base64Parameters extends Container
/**
* Computes a dynamic parameter.
*
* @param string The name of the dynamic parameter to load
* @param string $name The name of the dynamic parameter to load
*
* @return mixed The value of the dynamic parameter
*

View File

@ -104,7 +104,7 @@ class Symfony_DI_PhpDumper_Test_CsvParameters extends Container
/**
* Computes a dynamic parameter.
*
* @param string The name of the dynamic parameter to load
* @param string $name The name of the dynamic parameter to load
*
* @return mixed The value of the dynamic parameter
*

View File

@ -128,7 +128,7 @@ class ProjectServiceContainer extends Container
/**
* Computes a dynamic parameter.
*
* @param string The name of the dynamic parameter to load
* @param string $name The name of the dynamic parameter to load
*
* @return mixed The value of the dynamic parameter
*

View File

@ -475,7 +475,7 @@ class Symfony_DI_PhpDumper_Errored_Definition extends Container
/**
* Computes a dynamic parameter.
*
* @param string The name of the dynamic parameter to load
* @param string $name The name of the dynamic parameter to load
*
* @return mixed The value of the dynamic parameter
*

View File

@ -152,7 +152,7 @@ class ProjectServiceContainer extends Container
/**
* Computes a dynamic parameter.
*
* @param string The name of the dynamic parameter to load
* @param string $name The name of the dynamic parameter to load
*
* @return mixed The value of the dynamic parameter
*

View File

@ -105,7 +105,7 @@ class Symfony_DI_PhpDumper_Test_JsonParameters extends Container
/**
* Computes a dynamic parameter.
*
* @param string The name of the dynamic parameter to load
* @param string $name The name of the dynamic parameter to load
*
* @return mixed The value of the dynamic parameter
*

View File

@ -130,7 +130,7 @@ class Symfony_DI_PhpDumper_Test_Rot13Parameters extends Container
/**
* Computes a dynamic parameter.
*
* @param string The name of the dynamic parameter to load
* @param string $name The name of the dynamic parameter to load
*
* @return mixed The value of the dynamic parameter
*

View File

@ -38,7 +38,7 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
/**
* Getter for subject property.
*
* @return mixed $subject The observer subject
* @return mixed The observer subject
*/
public function getSubject()
{

View File

@ -38,9 +38,9 @@ class DateIntervalType extends AbstractType
'seconds',
);
private static $widgets = array(
'text' => 'Symfony\Component\Form\Extension\Core\Type\TextType',
'integer' => 'Symfony\Component\Form\Extension\Core\Type\IntegerType',
'choice' => 'Symfony\Component\Form\Extension\Core\Type\ChoiceType',
'text' => TextType::class,
'integer' => IntegerType::class,
'choice' => ChoiceType::class,
);
/**
@ -96,31 +96,23 @@ class DateIntervalType extends AbstractType
if ('single_text' === $options['widget']) {
$builder->addViewTransformer(new DateIntervalToStringTransformer($format));
} else {
$childOptions = array();
foreach ($this->timeParts as $part) {
if ($options['with_'.$part]) {
$childOptions[$part] = array(
$childOptions = array(
'error_bubbling' => true,
'label' => $options['labels'][$part],
// Append generic carry-along options
'required' => $options['required'],
'translation_domain' => $options['translation_domain'],
// when compound the array entries are ignored, we need to cascade the configuration here
'empty_data' => isset($options['empty_data'][$part]) ? $options['empty_data'][$part] : null,
);
if ('choice' === $options['widget']) {
$childOptions[$part]['choice_translation_domain'] = false;
$childOptions[$part]['choices'] = $options[$part];
$childOptions[$part]['placeholder'] = $options['placeholder'][$part];
$childOptions['choice_translation_domain'] = false;
$childOptions['choices'] = $options[$part];
$childOptions['placeholder'] = $options['placeholder'][$part];
}
}
}
// Append generic carry-along options
foreach (array('required', 'translation_domain') as $passOpt) {
foreach ($this->timeParts as $part) {
if ($options['with_'.$part]) {
$childOptions[$part][$passOpt] = $options[$passOpt];
}
}
}
foreach ($this->timeParts as $part) {
if ($options['with_'.$part]) {
$childForm = $builder->create($part, self::$widgets[$options['widget']], $childOptions[$part]);
$childForm = $builder->create($part, self::$widgets[$options['widget']], $childOptions);
if ('integer' === $options['widget']) {
$childForm->addModelTransformer(
new ReversedTransformer(
@ -132,7 +124,7 @@ class DateIntervalType extends AbstractType
}
}
if ($options['with_invert']) {
$builder->add('invert', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', array(
$builder->add('invert', CheckboxType::class, array(
'label' => $options['labels']['invert'],
'error_bubbling' => true,
'required' => false,
@ -180,6 +172,9 @@ class DateIntervalType extends AbstractType
$compound = function (Options $options) {
return 'single_text' !== $options['widget'];
};
$emptyData = function (Options $options) {
return 'single_text' === $options['widget'] ? '' : array();
};
$placeholderDefault = function (Options $options) {
return $options['required'] ? null : '';
@ -238,6 +233,7 @@ class DateIntervalType extends AbstractType
// this option.
'data_class' => null,
'compound' => $compound,
'empty_data' => $emptyData,
'labels' => array(),
)
);

View File

@ -48,7 +48,7 @@ class FormValidator extends ConstraintValidator
// Validate the data against its own constraints
if ($form->isRoot() && (\is_object($data) || \is_array($data))) {
if (\is_array($groups) && \count($groups) > 0 || $groups instanceof GroupSequence && \count($groups->groups) > 0) {
if (($groups && \is_array($groups)) || ($groups instanceof GroupSequence && $groups->groups)) {
$validator->atPath('data')->validate($form->getData(), null, $groups);
}
}

View File

@ -423,4 +423,32 @@ class DateIntervalTypeTest extends BaseTypeTest
$this->assertSame($expectedData, $form->getNormData());
$this->assertSame($expectedData, $form->getData());
}
/**
* @dataProvider provideEmptyData
*/
public function testSubmitNullUsesDateEmptyData($widget, $emptyData, $expectedData)
{
$form = $this->factory->create(static::TESTED_TYPE, null, array(
'widget' => $widget,
'empty_data' => $emptyData,
));
$form->submit(null);
$this->assertSame($emptyData, $form->getViewData());
$this->assertEquals($expectedData, $form->getNormData());
$this->assertEquals($expectedData, $form->getData());
}
public function provideEmptyData()
{
$expectedData = \DateInterval::createFromDateString('6 years and 4 months');
return array(
'Simple field' => array('single_text', 'P6Y4M0D', $expectedData),
'Compound text field' => array('text', array('years' => '06', 'months' => '04', 'days' => '00'), $expectedData),
'Compound integer field' => array('integer', array('years' => '6', 'months' => '4', 'days' => '0'), $expectedData),
'Compound choice field' => array('choice', array('years' => '6', 'months' => '4', 'days' => '0'), $expectedData),
);
}
}

View File

@ -118,7 +118,7 @@ class ExceptionListener implements EventSubscriberInterface
* @param \Exception $exception The thrown exception
* @param Request $request The original request
*
* @return Request $request The cloned request
* @return Request The cloned request
*/
protected function duplicateRequest(\Exception $exception, Request $request)
{

View File

@ -93,7 +93,7 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
/**
* Gets the current store.
*
* @return StoreInterface $store A StoreInterface instance
* @return StoreInterface A StoreInterface instance
*/
public function getStore()
{

View File

@ -38,7 +38,7 @@ class Entry
/**
* Returns whether an attribute exists.
*
* @param $name string The name of the attribute
* @param string $name The name of the attribute
*
* @return bool
*/
@ -53,7 +53,7 @@ class Entry
* As LDAP can return multiple values for a single attribute,
* this value is returned as an array.
*
* @param $name string The name of the attribute
* @param string $name The name of the attribute
*
* @return array|null
*/

View File

@ -36,8 +36,8 @@ class InputStream implements \IteratorAggregate
/**
* Appends an input to the write buffer.
*
* @param resource|string|int|float|bool|\Traversable|null The input to append as scalar,
* stream resource or \Traversable
* @param resource|string|int|float|bool|\Traversable|null $input The input to append as scalar,
* stream resource or \Traversable
*/
public function write($input)
{

View File

@ -375,7 +375,7 @@ class Router implements RouterInterface, RequestMatcherInterface
* Provides the ConfigCache factory implementation, falling back to a
* default implementation if necessary.
*
* @return ConfigCacheFactoryInterface $configCacheFactory
* @return ConfigCacheFactoryInterface
*/
private function getConfigCacheFactory()
{

View File

@ -472,9 +472,6 @@ class PhpMatcherDumperTest extends TestCase
);
}
/**
* @param $dumper
*/
private function generateDumpedMatcher(RouteCollection $collection, $redirectableStub = false)
{
$options = array('class' => $this->matcherClass);

View File

@ -366,7 +366,7 @@ class PhpEngine implements EngineInterface, \ArrayAccess
*
* @param string $context The context name
*
* @return callable $escaper A PHP callable
* @return callable A PHP callable
*
* @throws \InvalidArgumentException
*/

View File

@ -90,7 +90,7 @@ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInter
/**
* Gets the fallback locales.
*
* @return array $locales The fallback locales
* @return array The fallback locales
*/
public function getFallbackLocales()
{

View File

@ -45,7 +45,7 @@ abstract class FileDumper implements DumperInterface
/**
* Sets backup flag.
*
* @param bool
* @param bool $backup
*
* @deprecated since Symfony 4.1
*/

View File

@ -89,7 +89,7 @@ class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface
/**
* Gets the fallback locales.
*
* @return array $locales The fallback locales
* @return array The fallback locales
*/
public function getFallbackLocales()
{

View File

@ -171,7 +171,7 @@ class Translator implements TranslatorInterface, TranslatorBagInterface
/**
* Gets the fallback locales.
*
* @return array $locales The fallback locales
* @return array The fallback locales
*/
public function getFallbackLocales()
{

View File

@ -69,7 +69,7 @@ class ArrayConverter
$elem = &$elem[$part];
}
if (\is_array($elem) && \count($elem) > 0 && $parentOfElem) {
if ($elem && \is_array($elem) && $parentOfElem) {
/* Process next case:
* 'foo.bar': 'test1'
* 'foo': 'test2'

View File

@ -120,7 +120,7 @@ abstract class Constraint
if (\is_array($options)) {
reset($options);
}
if (\is_array($options) && \count($options) > 0 && \is_string(key($options))) {
if ($options && \is_array($options) && \is_string(key($options))) {
foreach ($options as $option => $value) {
if (array_key_exists($option, $knownOptions)) {
$this->$option = $value;

View File

@ -19,8 +19,8 @@ namespace Symfony\Component\VarDumper\Caster;
class ClassStub extends ConstStub
{
/**
* @param string A PHP identifier, e.g. a class, method, interface, etc. name
* @param callable The callable targeted by the identifier when it is ambiguous or not a real PHP identifier
* @param string $identifier A PHP identifier, e.g. a class, method, interface, etc. name
* @param callable $callable The callable targeted by the identifier when it is ambiguous or not a real PHP identifier
*/
public function __construct(string $identifier, $callable = null)
{