Merge branch '3.4'

* 3.4:
  [Bridge/PhpUnit] Sync the bridge version installed in vendor/ and in phpunit clone
  [DI] Analyze setter-circular deps more precisely
  fixing that PropertyNormalizer supports parent properties
  [SecurityBundle] Don't trigger auto-picking notice if provider is set per listener
  [TwigBundle][FrameworkBundle] Remove the internals from debug autowiring
  [DI] Skip hot_path tag for deprecated services as their class might also be
  [Cache] Memcached options should ignore "lazy"
  [FrameworkBundle] Dont create empty bundles directory
This commit is contained in:
Nicolas Grekas 2017-11-20 22:12:12 +01:00
commit 5b6c1b3132
23 changed files with 208 additions and 181 deletions

View File

@ -68,7 +68,17 @@ if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__
if (5.1 <= $PHPUNIT_VERSION && $PHPUNIT_VERSION < 5.4) {
passthru("$COMPOSER require --no-update phpunit/phpunit-mock-objects \"~3.1.0\"");
}
passthru("$COMPOSER require --no-update symfony/phpunit-bridge \"~3.4-beta5@dev|^4.0-beta5@dev\"");
passthru("$COMPOSER require --no-update symfony/phpunit-bridge \"*\"");
if (file_exists(($path = dirname(__DIR__)).'/composer.json')) {
if (file_exists($path.'/vendor/symfony/phpunit-bridge/composer.json')) {
$path .= '/vendor/symfony/phpunit-bridge';
}
} elseif (file_exists($path.'/symfony/phpunit-bridge/composer.json')) {
$path .= '/symfony/phpunit-bridge';
}
if (file_exists($path)) {
passthru("$COMPOSER config repositories.phpunit-bridge path ".escapeshellarg($path));
}
$prevRoot = getenv('COMPOSER_ROOT_VERSION');
putenv("COMPOSER_ROOT_VERSION=$PHPUNIT_VERSION.99");
$exit = proc_close(proc_open("$COMPOSER install --no-dev --prefer-dist --no-progress --ansi", array(), $p, getcwd(), null, array('bypass_shell' => true)));

View File

@ -98,9 +98,7 @@ EOT
}
}
// Create the bundles directory otherwise symlink will fail.
$bundlesDir = $targetArg.'/bundles/';
$this->filesystem->mkdir($bundlesDir, 0777);
$io = new SymfonyStyle($input, $output);
$io->newLine();
@ -164,10 +162,14 @@ EOT
}
}
// remove the assets of the bundles that no longer exist
$dirsToRemove = Finder::create()->depth(0)->directories()->exclude($validAssetDirs)->in($bundlesDir);
$this->filesystem->remove($dirsToRemove);
if (is_dir($bundlesDir)) {
$dirsToRemove = Finder::create()->depth(0)->directories()->exclude($validAssetDirs)->in($bundlesDir);
$this->filesystem->remove($dirsToRemove);
}
$io->table(array('', 'Bundle', 'Method / Error'), $rows);
if ($rows) {
$io->table(array('', 'Bundle', 'Method / Error'), $rows);
}
if (0 !== $exitCode) {
$io->error('Some errors occurred while installing assets.');
@ -175,7 +177,7 @@ EOT
if ($copyUsed) {
$io->note('Some assets were installed via copy. If you make changes to these assets you have to run this command again.');
}
$io->success('All assets were successfully installed.');
$io->success($rows ? 'All assets were successfully installed.' : 'No assets were provided by any bundle.');
}
return $exitCode;

View File

@ -13,13 +13,6 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection;
use Doctrine\Common\Annotations\Reader;
use Symfony\Bridge\Monolog\Processor\DebugProcessor;
use Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand;
use Symfony\Bundle\FrameworkBundle\Command\RouterMatchCommand;
use Symfony\Bundle\FrameworkBundle\Command\TranslationDebugCommand;
use Symfony\Bundle\FrameworkBundle\Command\TranslationUpdateCommand;
use Symfony\Bundle\FrameworkBundle\Command\WorkflowDumpCommand;
use Symfony\Bundle\FrameworkBundle\Command\XliffLintCommand;
use Symfony\Bundle\FrameworkBundle\Command\YamlLintCommand;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Bundle\FrameworkBundle\Routing\AnnotatedRouteControllerLoader;
@ -120,10 +113,10 @@ class FrameworkExtension extends Extension
$loader->load('console.xml');
if (!class_exists(BaseXliffLintCommand::class)) {
$container->removeDefinition(XliffLintCommand::class);
$container->removeDefinition('console.command.xliff_lint');
}
if (!class_exists(BaseYamlLintCommand::class)) {
$container->removeDefinition(YamlLintCommand::class);
$container->removeDefinition('console.command.yaml_lint');
}
}
@ -203,7 +196,7 @@ class FrameworkExtension extends Extension
$container->removeDefinition('form.type_guesser.validator');
}
} else {
$container->removeDefinition('Symfony\Component\Form\Command\DebugCommand');
$container->removeDefinition('console.command.form_debug');
}
$this->registerSecurityCsrfConfiguration($config['csrf_protection'], $container, $loader);
@ -431,7 +424,7 @@ class FrameworkExtension extends Extension
private function registerWorkflowConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
{
if (!$config['enabled']) {
$container->removeDefinition(WorkflowDumpCommand::class);
$container->removeDefinition('console.command.workflow_dump');
return;
}
@ -603,8 +596,8 @@ class FrameworkExtension extends Extension
private function registerRouterConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
{
if (!$this->isConfigEnabled($container, $config)) {
$container->removeDefinition(RouterDebugCommand::class);
$container->removeDefinition(RouterMatchCommand::class);
$container->removeDefinition('console.command.router_debug');
$container->removeDefinition('console.command.router_match');
return;
}
@ -857,8 +850,8 @@ class FrameworkExtension extends Extension
private function registerTranslatorConfiguration(array $config, ContainerBuilder $container, LoaderInterface $loader)
{
if (!$this->isConfigEnabled($container, $config)) {
$container->removeDefinition(TranslationDebugCommand::class);
$container->removeDefinition(TranslationUpdateCommand::class);
$container->removeDefinition('console.command.translation_debug');
$container->removeDefinition('console.command.translation_update');
return;
}

View File

@ -13,75 +13,75 @@
<tag name="monolog.logger" channel="console" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\AboutCommand">
<service id="console.command.about" class="Symfony\Bundle\FrameworkBundle\Command\AboutCommand">
<tag name="console.command" command="about" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\AssetsInstallCommand">
<service id="console.command.assets_install" class="Symfony\Bundle\FrameworkBundle\Command\AssetsInstallCommand">
<argument type="service" id="filesystem" />
<tag name="console.command" command="assets:install" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\CacheClearCommand">
<service id="console.command.cache_clear" class="Symfony\Bundle\FrameworkBundle\Command\CacheClearCommand">
<argument type="service" id="cache_clearer" />
<argument type="service" id="filesystem" />
<tag name="console.command" command="cache:clear" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\CachePoolClearCommand">
<service id="console.command.cache_pool_clear" class="Symfony\Bundle\FrameworkBundle\Command\CachePoolClearCommand">
<argument type="service" id="cache.global_clearer" />
<tag name="console.command" command="cache:pool:clear" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\CachePoolPruneCommand">
<service id="console.command.cache_pool_prune" class="Symfony\Bundle\FrameworkBundle\Command\CachePoolPruneCommand">
<argument type="iterator" />
<tag name="console.command" command="cache:pool:prune" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\CacheWarmupCommand">
<service id="console.command.cache_warmup" class="Symfony\Bundle\FrameworkBundle\Command\CacheWarmupCommand">
<argument type="service" id="cache_warmer" />
<tag name="console.command" command="cache:warmup" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\ConfigDebugCommand">
<service id="console.command.config_debug" class="Symfony\Bundle\FrameworkBundle\Command\ConfigDebugCommand">
<tag name="console.command" command="debug:config" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\ConfigDumpReferenceCommand">
<service id="console.command.config_dump_reference" class="Symfony\Bundle\FrameworkBundle\Command\ConfigDumpReferenceCommand">
<tag name="console.command" command="config:dump-reference" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\ContainerDebugCommand">
<service id="console.command.container_debug" class="Symfony\Bundle\FrameworkBundle\Command\ContainerDebugCommand">
<tag name="console.command" command="debug:container" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\DebugAutowiringCommand">
<service id="console.command.debug_autowiring" class="Symfony\Bundle\FrameworkBundle\Command\DebugAutowiringCommand">
<tag name="console.command" command="debug:autowiring" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\EventDispatcherDebugCommand">
<service id="console.command.event_dispatcher_debug" class="Symfony\Bundle\FrameworkBundle\Command\EventDispatcherDebugCommand">
<argument type="service" id="event_dispatcher" />
<tag name="console.command" command="debug:event-dispatcher" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand">
<service id="console.command.router_debug" class="Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand">
<argument type="service" id="router" />
<tag name="console.command" command="debug:router" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\RouterMatchCommand">
<service id="console.command.router_match" class="Symfony\Bundle\FrameworkBundle\Command\RouterMatchCommand">
<argument type="service" id="router" />
<tag name="console.command" command="router:match" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\TranslationDebugCommand">
<service id="console.command.translation_debug" class="Symfony\Bundle\FrameworkBundle\Command\TranslationDebugCommand">
<argument type="service" id="translator" />
<argument type="service" id="translation.reader" />
<argument type="service" id="translation.extractor" />
<tag name="console.command" command="debug:translation" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\TranslationUpdateCommand">
<service id="console.command.translation_update" class="Symfony\Bundle\FrameworkBundle\Command\TranslationUpdateCommand">
<argument type="service" id="translation.writer" />
<argument type="service" id="translation.reader" />
<argument type="service" id="translation.extractor" />
@ -89,19 +89,19 @@
<tag name="console.command" command="translation:update" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\WorkflowDumpCommand">
<service id="console.command.workflow_dump" class="Symfony\Bundle\FrameworkBundle\Command\WorkflowDumpCommand">
<tag name="console.command" command="workflow:dump" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\XliffLintCommand">
<service id="console.command.xliff_lint" class="Symfony\Bundle\FrameworkBundle\Command\XliffLintCommand">
<tag name="console.command" command="lint:xliff" />
</service>
<service id="Symfony\Bundle\FrameworkBundle\Command\YamlLintCommand">
<service id="console.command.yaml_lint" class="Symfony\Bundle\FrameworkBundle\Command\YamlLintCommand">
<tag name="console.command" command="lint:yaml" />
</service>
<service id="Symfony\Component\Form\Command\DebugCommand">
<service id="console.command.form_debug" class="Symfony\Component\Form\Command\DebugCommand">
<argument type="service" id="form.registry" />
<argument type="collection" /> <!-- All form types namespaces are stored here by FormPass -->
<argument type="collection" /> <!-- All services form types are stored here by FormPass -->

View File

@ -55,12 +55,12 @@
<argument type="tagged" tag="config_cache.resource_checker" />
</service>
<service id="Symfony\Component\DependencyInjection\Config\ContainerParametersResourceChecker">
<service id="dependency_injection.config.container_parameters_resource_checker" class="Symfony\Component\DependencyInjection\Config\ContainerParametersResourceChecker">
<argument type="service" id="service_container" />
<tag name="config_cache.resource_checker" priority="-980" />
</service>
<service id="Symfony\Component\Config\Resource\SelfCheckingResourceChecker">
<service id="config.resource.self_checking_resource_checker" class="Symfony\Component\Config\Resource\SelfCheckingResourceChecker">
<tag name="config_cache.resource_checker" priority="-990" />
</service>

View File

@ -12,7 +12,6 @@
namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection;
use Doctrine\Common\Annotations\Annotation;
use Symfony\Bundle\FrameworkBundle\Command\WorkflowDumpCommand;
use Symfony\Bundle\FullStack;
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddAnnotationsCachedReaderPass;
@ -307,7 +306,7 @@ abstract class FrameworkExtensionTest extends TestCase
$container = $this->createContainerFromFile('workflows_enabled');
$this->assertTrue($container->has(Registry::class));
$this->assertTrue($container->hasDefinition(WorkflowDumpCommand::class));
$this->assertTrue($container->hasDefinition('console.command.workflow_dump'));
}
public function testRouter()

View File

@ -11,7 +11,6 @@
namespace Symfony\Bundle\SecurityBundle\DependencyInjection;
use Symfony\Bundle\SecurityBundle\Command\UserPasswordEncoderCommand;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
@ -109,7 +108,7 @@ class SecurityExtension extends Extension
if (class_exists(Application::class)) {
$loader->load('console.xml');
$container->getDefinition(UserPasswordEncoderCommand::class)->replaceArgument(1, array_keys($config['encoders']));
$container->getDefinition('security.command.user_password_encoder')->replaceArgument(1, array_keys($config['encoders']));
}
$container->registerForAutoconfiguration(VoterInterface::class)
@ -245,17 +244,16 @@ class SecurityExtension extends Extension
$config->replaceArgument(4, $firewall['stateless']);
// Provider id (must be configured explicitly per firewall/authenticator if more than one provider is set)
$defaultProvider = null;
if (isset($firewall['provider'])) {
if (!isset($providerIds[$normalizedName = str_replace('-', '_', $firewall['provider'])])) {
throw new InvalidConfigurationException(sprintf('Invalid firewall "%s": user provider "%s" not found.', $id, $firewall['provider']));
}
$defaultProvider = $providerIds[$normalizedName];
} else {
if (count($providerIds) > 1) {
throw new InvalidConfigurationException(sprintf('Not configuring explicitly the provider on "%s" firewall is ambiguous as there is more than one registered provider.', $id));
}
} elseif (1 === count($providerIds)) {
$defaultProvider = reset($providerIds);
} elseif ($providerIds) {
throw new InvalidConfigurationException(sprintf('Not configuring explicitly the provider on "%s" firewall is ambiguous as there is more than one registered provider.', $id));
}
$config->replaceArgument(5, $defaultProvider);
@ -399,7 +397,7 @@ class SecurityExtension extends Extension
return $this->contextListeners[$contextKey] = $listenerId;
}
private function createAuthenticationListeners($container, $id, $firewall, &$authenticationProviders, $defaultProvider, array $providerIds, $defaultEntryPoint)
private function createAuthenticationListeners($container, $id, $firewall, &$authenticationProviders, $defaultProvider = null, array $providerIds, $defaultEntryPoint)
{
$listeners = array();
$hasListeners = false;
@ -415,7 +413,7 @@ class SecurityExtension extends Extension
}
$userProvider = $providerIds[$normalizedName];
} else {
$userProvider = $defaultProvider;
$userProvider = $defaultProvider ?: $this->getFirstProvider($id, $key, $providerIds);
}
list($provider, $listenerId, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint);
@ -598,7 +596,7 @@ class SecurityExtension extends Extension
private function createSwitchUserListener($container, $id, $config, $defaultProvider, $stateless)
{
$userProvider = isset($config['provider']) ? $this->getUserProviderId($config['provider']) : $defaultProvider;
$userProvider = isset($config['provider']) ? $this->getUserProviderId($config['provider']) : ($defaultProvider ?: $this->getFirstProvider($id, 'switch_user', $providerIds));
$switchUserListenerId = 'security.authentication.switchuser_listener.'.$id;
$listener = $container->setDefinition($switchUserListenerId, new ChildDefinition('security.authentication.switchuser_listener'));
@ -697,4 +695,14 @@ class SecurityExtension extends Extension
return $this->expressionLanguage;
}
/**
* @deprecated since version 3.4, to be removed in 4.0
*/
private function getFirstProvider($firewallName, $listenerName, array $providerIds)
{
@trigger_error(sprintf('Listener "%s" on firewall "%s" has no "provider" set but multiple providers exist. Using the first configured provider (%s) is deprecated since 3.4 and will throw an exception in 4.0, set the "provider" key on the firewall instead.', $listenerName, $firewallName, $first = array_keys($providerIds)[0]), E_USER_DEPRECATED);
return $providerIds[$first];
}
}

View File

@ -7,7 +7,7 @@
<services>
<defaults public="false" />
<service id="Symfony\Bundle\SecurityBundle\Command\UserPasswordEncoderCommand">
<service id="security.command.user_password_encoder" class="Symfony\Bundle\SecurityBundle\Command\UserPasswordEncoderCommand">
<argument type="service" id="security.encoder_factory"/>
<argument type="collection" /> <!-- encoders' user classes -->
<tag name="console.command" command="security:encode-password" />

View File

@ -12,7 +12,6 @@
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\SecurityBundle\Command\UserPasswordEncoderCommand;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Bundle\SecurityBundle\SecurityBundle;
@ -355,7 +354,7 @@ abstract class CompleteConfigurationTest extends TestCase
public function testUserPasswordEncoderCommandIsRegistered()
{
$this->assertTrue($this->getContainer('remember_me_options')->has(UserPasswordEncoderCommand::class));
$this->assertTrue($this->getContainer('remember_me_options')->has('security.command.user_password_encoder'));
}
public function testDefaultAccessDecisionManagerStrategyIsAffirmative()

View File

@ -147,6 +147,27 @@ class SecurityExtensionTest extends TestCase
$this->assertTrue($container->getDefinition('security.authentication.switchuser_listener.some_firewall')->getArgument(9));
}
public function testPerListenerProvider()
{
$container = $this->getRawContainer();
$container->loadFromExtension('security', array(
'providers' => array(
'first' => array('id' => 'foo'),
'second' => array('id' => 'bar'),
),
'firewalls' => array(
'default' => array(
'http_basic' => array('provider' => 'second'),
'logout_on_user_change' => true,
),
),
));
$container->compile();
$this->addToAssertionCount(1);
}
protected function getRawContainer()
{
$container = new ContainerBuilder();

View File

@ -7,13 +7,13 @@
<services>
<defaults public="false" />
<service id="Symfony\Bridge\Twig\Command\DebugCommand">
<service id="twig.command.debug" class="Symfony\Bridge\Twig\Command\DebugCommand">
<argument type="service" id="twig" />
<argument>%kernel.project_dir%</argument>
<tag name="console.command" command="debug:twig" />
</service>
<service id="Symfony\Bundle\TwigBundle\Command\LintCommand">
<service id="twig.command.lint" class="Symfony\Bundle\TwigBundle\Command\LintCommand">
<argument type="service" id="twig" />
<tag name="console.command" command="lint:twig" />
</service>

View File

@ -130,7 +130,7 @@ trait MemcachedTrait
}
// set client's options
unset($options['persistent_id'], $options['username'], $options['password'], $options['weight']);
unset($options['persistent_id'], $options['username'], $options['password'], $options['weight'], $options['lazy']);
$options = array_change_key_case($options, CASE_UPPER);
$client->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
$client->setOption(\Memcached::OPT_NO_BLOCK, true);

View File

@ -59,7 +59,7 @@ class CheckCircularReferencesPass implements CompilerPassInterface
if (empty($this->checkedNodes[$id])) {
// Don't check circular references for lazy edges
if (!$node->getValue() || !$edge->isLazy()) {
if (!$node->getValue() || (!$edge->isLazy() && !$edge->isWeak())) {
$searchKey = array_search($id, $this->currentPath);
$this->currentPath[] = $id;

View File

@ -52,12 +52,12 @@ class ResolveHotPathPass extends AbstractRecursivePass
if ($value instanceof ArgumentInterface) {
return $value;
}
if ($value instanceof Definition && $isRoot && (isset($this->resolvedIds[$this->currentId]) || !$value->hasTag($this->tagName))) {
return $value;
if ($value instanceof Definition && $isRoot && (isset($this->resolvedIds[$this->currentId]) || !$value->hasTag($this->tagName) || $value->isDeprecated())) {
return $value->isDeprecated() ? $value->clearTag($this->tagName) : $value;
}
if ($value instanceof Reference && ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE !== $value->getInvalidBehavior() && $this->container->has($id = (string) $value)) {
$definition = $this->container->findDefinition($id);
if (!$definition->hasTag($this->tagName)) {
if (!$definition->hasTag($this->tagName) && !$definition->isDeprecated()) {
$this->resolvedIds[$id] = true;
$definition->addTag($this->tagName);
parent::processValue($definition, false);

View File

@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Variable;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerInterface;
@ -67,6 +68,7 @@ class PhpDumper extends Dumper
private $hotPathTag;
private $inlineRequires;
private $inlinedRequires = array();
private $circularReferences = array();
/**
* @var ProxyDumper
@ -134,6 +136,14 @@ class PhpDumper extends Dumper
$this->initializeMethodNamesMap('Container' === $baseClass ? Container::class : $baseClass);
(new AnalyzeServiceReferencesPass())->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);
}
$this->docStar = $options['debug'] ? '*' : '';
if (!empty($options['file']) && is_dir($dir = dirname($options['file']))) {
@ -235,6 +245,7 @@ EOF;
$this->targetDirRegex = null;
$this->inlinedRequires = array();
$this->circularReferences = array();
$unusedEnvs = array();
foreach ($this->container->getEnvCounters() as $env => $use) {
@ -267,19 +278,19 @@ EOF;
array_unshift($inlinedDefinitions, $definition);
$collectLineage = $this->inlineRequires && !($this->hotPathTag && $definition->hasTag($this->hotPathTag));
$isNonLazyShared = !$this->getProxyDumper()->isProxyCandidate($definition) && $definition->isShared();
$collectLineage = $this->inlineRequires && !$this->isHotPath($definition);
$isNonLazyShared = isset($this->circularReferences[$cId]) && !$this->getProxyDumper()->isProxyCandidate($definition) && $definition->isShared();
$lineage = $calls = $behavior = array();
foreach ($inlinedDefinitions as $iDefinition) {
if ($collectLineage && $class = is_array($factory = $iDefinition->getFactory()) && is_string($factory[0]) ? $factory[0] : $iDefinition->getClass()) {
if ($collectLineage && !$iDefinition->isDeprecated() && $class = is_array($factory = $iDefinition->getFactory()) && is_string($factory[0]) ? $factory[0] : $iDefinition->getClass()) {
$this->collectLineage($class, $lineage);
}
$this->getServiceCallsFromArguments($iDefinition->getArguments(), $calls, $behavior, $isNonLazyShared);
$this->getServiceCallsFromArguments($iDefinition->getArguments(), $calls, $behavior, $isNonLazyShared, $cId);
$isPreInstantiation = $isNonLazyShared && $iDefinition !== $definition && !$this->hasReference($cId, $iDefinition->getMethodCalls(), true) && !$this->hasReference($cId, $iDefinition->getProperties(), true);
$this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior, $isPreInstantiation);
$this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior, $isPreInstantiation);
$this->getServiceCallsFromArguments(array($iDefinition->getConfigurator()), $calls, $behavior, $isPreInstantiation);
$this->getServiceCallsFromArguments(array($iDefinition->getFactory()), $calls, $behavior, $isNonLazyShared);
$this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior, $isPreInstantiation, $cId);
$this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior, $isPreInstantiation, $cId);
$this->getServiceCallsFromArguments(array($iDefinition->getConfigurator()), $calls, $behavior, $isPreInstantiation, $cId);
$this->getServiceCallsFromArguments(array($iDefinition->getFactory()), $calls, $behavior, $isNonLazyShared, $cId);
}
$code = '';
@ -333,6 +344,33 @@ EOTXT
return $code;
}
private function analyzeCircularReferences(array $edges, &$checkedNodes, &$currentPath)
{
foreach ($edges as $edge) {
$node = $edge->getDestNode();
$id = $node->getId();
if (isset($checkedNodes[$id])) {
continue;
}
if ($node->getValue() && ($edge->isLazy() || $edge->isWeak())) {
// no-op
} elseif (isset($currentPath[$id])) {
foreach (array_reverse($currentPath) as $parentId) {
$this->circularReferences[$parentId][$id] = $id;
$id = $parentId;
}
} else {
$currentPath[$id] = $id;
$this->analyzeCircularReferences($node->getOutEdges(), $checkedNodes, $currentPath);
}
$checkedNodes[$id] = true;
array_pop($currentPath);
}
}
private function collectLineage($class, array &$lineage)
{
if (isset($lineage[$class])) {
@ -526,7 +564,7 @@ EOTXT
}
foreach ($definition->getArguments() as $arg) {
if (!$arg || ($arg instanceof Reference && 'service_container' === (string) $arg)) {
if (!$arg) {
continue;
}
if (is_array($arg) && 3 >= count($arg)) {
@ -534,7 +572,7 @@ EOTXT
if ($this->dumpValue($k) !== $this->dumpValue($k, false)) {
return false;
}
if (!$v || ($v instanceof Reference && 'service_container' === (string) $v)) {
if (!$v) {
continue;
}
if ($v instanceof Reference && $this->container->has($id = (string) $v) && $this->container->findDefinition($id)->isSynthetic()) {
@ -694,7 +732,7 @@ EOTXT
$lazyInitialization = '';
}
$asFile = $this->asFiles && $definition->isShared() && !($this->hotPathTag && $definition->hasTag($this->hotPathTag));
$asFile = $this->asFiles && $definition->isShared() && !$this->isHotPath($definition);
$methodName = $this->generateMethodName($id);
if ($asFile) {
$file = $methodName.'.php';
@ -755,7 +793,7 @@ EOF;
$definitions = $this->container->getDefinitions();
ksort($definitions);
foreach ($definitions as $id => $definition) {
if ($definition->isSynthetic() || ($this->asFiles && $definition->isShared() && !($this->hotPathTag && $definition->hasTag($this->hotPathTag)))) {
if ($definition->isSynthetic() || ($this->asFiles && $definition->isShared() && !$this->isHotPath($definition))) {
continue;
}
if ($definition->isPublic()) {
@ -773,7 +811,7 @@ EOF;
$definitions = $this->container->getDefinitions();
ksort($definitions);
foreach ($definitions as $id => $definition) {
if (!$definition->isSynthetic() && $definition->isShared() && !($this->hotPathTag && $definition->hasTag($this->hotPathTag))) {
if (!$definition->isSynthetic() && $definition->isShared() && !$this->isHotPath($definition)) {
$code = $this->addService($id, $definition, $file);
yield $file => $code;
}
@ -996,7 +1034,7 @@ EOF;
$definitions = $this->container->getDefinitions();
ksort($definitions);
foreach ($definitions as $id => $definition) {
if (!$definition->isSynthetic() && $definition->isPublic() && (!$this->asFiles || !$definition->isShared() || ($this->hotPathTag && $definition->hasTag($this->hotPathTag)))) {
if (!$definition->isSynthetic() && $definition->isPublic() && (!$this->asFiles || !$definition->isShared() || $this->isHotPath($definition))) {
$code .= ' '.$this->export($id).' => '.$this->export($this->generateMethodName($id)).",\n";
}
}
@ -1010,7 +1048,7 @@ EOF;
$definitions = $this->container->getDefinitions();
ksort($definitions);
foreach ($definitions as $id => $definition) {
if (!$definition->isSynthetic() && $definition->isPublic() && $definition->isShared() && !($this->hotPathTag && $definition->hasTag($this->hotPathTag))) {
if (!$definition->isSynthetic() && $definition->isPublic() && $definition->isShared() && !$this->isHotPath($definition)) {
$code .= sprintf(" %s => __DIR__.'/%s.php',\n", $this->export($id), $this->generateMethodName($id));
}
}
@ -1260,16 +1298,16 @@ EOF;
return implode(' && ', $conditions);
}
private function getServiceCallsFromArguments(array $arguments, array &$calls, array &$behavior, bool $isPreInstantiation)
private function getServiceCallsFromArguments(array $arguments, array &$calls, array &$behavior, bool $isPreInstantiation, string $callerId)
{
foreach ($arguments as $argument) {
if (is_array($argument)) {
$this->getServiceCallsFromArguments($argument, $calls, $behavior, $isPreInstantiation);
$this->getServiceCallsFromArguments($argument, $calls, $behavior, $isPreInstantiation, $callerId);
} elseif ($argument instanceof Reference) {
$id = (string) $argument;
if (!isset($calls[$id])) {
$calls[$id] = (int) ($isPreInstantiation && $this->container->has($id) && !$this->container->findDefinition($id)->isSynthetic());
$calls[$id] = (int) ($isPreInstantiation && isset($this->circularReferences[$callerId][$id]));
}
if (!isset($behavior[$id])) {
$behavior[$id] = $argument->getInvalidBehavior();
@ -1321,6 +1359,10 @@ EOF;
private function hasReference(string $id, array $arguments, bool $deep = false, array &$visited = array()): bool
{
if (!isset($this->circularReferences[$id])) {
return false;
}
foreach ($arguments as $argument) {
if (is_array($argument)) {
if ($this->hasReference($id, $argument, $deep, $visited)) {
@ -1334,7 +1376,7 @@ EOF;
return true;
}
if (!$deep || isset($visited[$argumentId]) || 'service_container' === $argumentId) {
if (!$deep || isset($visited[$argumentId]) || !isset($this->circularReferences[$id][$argumentId])) {
continue;
}
@ -1570,7 +1612,7 @@ EOF;
if ($definition->isShared()) {
$code = sprintf('$this->%s[\'%s\'] = %s', $definition->isPublic() ? 'services' : 'privates', $id, $code);
}
} elseif ($this->asFiles && $definition->isShared() && !($this->hotPathTag && $definition->hasTag($this->hotPathTag))) {
} elseif ($this->asFiles && $definition->isShared() && !$this->isHotPath($definition)) {
$code = sprintf("\$this->load(__DIR__.'/%s.php')", $this->generateMethodName($id));
} else {
$code = sprintf('$this->%s()', $this->generateMethodName($id));
@ -1694,6 +1736,11 @@ EOF;
return $this->expressionLanguage;
}
private function isHotPath(Definition $definition)
{
return $this->hotPathTag && $definition->hasTag($this->hotPathTag) && !$definition->isDeprecated();
}
private function export($value)
{
if (null !== $this->targetDirRegex && is_string($value) && preg_match($this->targetDirRegex, $value, $matches, PREG_OFFSET_CAPTURE)) {

View File

@ -34,11 +34,15 @@ class ResolveHotPathPassTest extends TestCase
;
$container->register('lazy');
$container->register('bar');
$container->register('bar')->addArgument(new Reference('buz'));
$container->register('baz')->addArgument(new Reference('lazy'));
$container->register('baz')->addArgument(new Reference('lazy'));
$container->register('bar')
->addArgument(new Reference('buz'))
->addArgument(new Reference('deprec_ref_notag'));
$container->register('baz')
->addArgument(new Reference('lazy'))
->addArgument(new Reference('lazy'));
$container->register('buz');
$container->register('deprec_with_tag')->setDeprecated()->addTag('container.hot_path');
$container->register('deprec_ref_notag')->setDeprecated();
(new ResolveHotPathPass())->process($container);
@ -47,5 +51,7 @@ class ResolveHotPathPassTest extends TestCase
$this->assertTrue($container->getDefinition('buz')->hasTag('container.hot_path'));
$this->assertFalse($container->getDefinition('baz')->hasTag('container.hot_path'));
$this->assertFalse($container->getDefinition('service_container')->hasTag('container.hot_path'));
$this->assertFalse($container->getDefinition('deprec_with_tag')->hasTag('container.hot_path'));
$this->assertFalse($container->getDefinition('deprec_ref_notag')->hasTag('container.hot_path'));
}
}

View File

@ -176,7 +176,7 @@ class PhpDumperTest extends TestCase
if ('\\' === DIRECTORY_SEPARATOR) {
$dump = str_replace('\\\\Fixtures\\\\includes\\\\foo.php', '/Fixtures/includes/foo.php', $dump);
}
$this->assertStringMatchesFormatFile(self::$fixturesPath.'/php/services9_as_files.txt', $dump);
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services9_as_files.txt', $dump);
}
public function testServicesWithAnonymousFactories()

View File

@ -66,18 +66,12 @@ use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
// Returns the public 'configured_service' shared service.
$a = ($this->services['baz'] ?? $this->load(__DIR__.'/getBazService.php'));
if (isset($this->services['configured_service'])) {
return $this->services['configured_service'];
}
$b = new \ConfClass();
$b->setFoo($a);
$a = new \ConfClass();
$a->setFoo(($this->services['baz'] ?? $this->load(__DIR__.'/getBazService.php')));
$this->services['configured_service'] = $instance = new \stdClass();
$b->configureStdClass($instance);
$a->configureStdClass($instance);
return $instance;
@ -130,13 +124,7 @@ use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
// Returns the public 'factory_service' shared service.
$a = ($this->services['foo.baz'] ?? $this->load(__DIR__.'/getFoo_BazService.php'));
if (isset($this->services['factory_service'])) {
return $this->services['factory_service'];
}
return $this->services['factory_service'] = $a->getInstance();
return $this->services['factory_service'] = ($this->services['foo.baz'] ?? $this->load(__DIR__.'/getFoo_BazService.php'))->getInstance();
[Container%s/getFactoryServiceSimpleService.php] => <?php
@ -145,13 +133,7 @@ use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
// Returns the public 'factory_service_simple' shared service.
$a = ($this->privates['factory_simple'] ?? $this->load(__DIR__.'/getFactorySimpleService.php'));
if (isset($this->services['factory_service_simple'])) {
return $this->services['factory_service_simple'];
}
return $this->services['factory_service_simple'] = $a->getInstance();
return $this->services['factory_service_simple'] = ($this->privates['factory_simple'] ?? $this->load(__DIR__.'/getFactorySimpleService.php'))->getInstance();
[Container%s/getFactorySimpleService.php] => <?php
@ -173,10 +155,6 @@ use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
$a = ($this->services['foo.baz'] ?? $this->load(__DIR__.'/getFoo_BazService.php'));
if (isset($this->services['foo'])) {
return $this->services['foo'];
}
$this->services['foo'] = $instance = \Bar\FooClass::getInstance('foo', $a, array('bar' => 'foo is bar', 'foobar' => 'bar'), true, $this);
$instance->foo = 'bar';
@ -413,10 +391,6 @@ class ProjectServiceContainer extends Container
{
$a = ($this->services['foo.baz'] ?? $this->load(__DIR__.'/getFoo_BazService.php'));
if (isset($this->services['bar'])) {
return $this->services['bar'];
}
$this->services['bar'] = $instance = new \Bar\FooClass('foo', $a, $this->getParameter('foo_bar'));
$a->configure($instance);

View File

@ -124,10 +124,6 @@ class ProjectServiceContainer extends Container
{
$a = ($this->services['foo.baz'] ?? $this->getFoo_BazService());
if (isset($this->services['bar'])) {
return $this->services['bar'];
}
$this->services['bar'] = $instance = new \Bar\FooClass('foo', $a, $this->getParameter('foo_bar'));
$a->configure($instance);
@ -166,18 +162,12 @@ class ProjectServiceContainer extends Container
*/
protected function getConfiguredServiceService()
{
$a = ($this->services['baz'] ?? $this->getBazService());
if (isset($this->services['configured_service'])) {
return $this->services['configured_service'];
}
$b = new \ConfClass();
$b->setFoo($a);
$a = new \ConfClass();
$a->setFoo(($this->services['baz'] ?? $this->getBazService()));
$this->services['configured_service'] = $instance = new \stdClass();
$b->configureStdClass($instance);
$a->configureStdClass($instance);
return $instance;
}
@ -237,13 +227,7 @@ class ProjectServiceContainer extends Container
*/
protected function getFactoryServiceService()
{
$a = ($this->services['foo.baz'] ?? $this->getFoo_BazService());
if (isset($this->services['factory_service'])) {
return $this->services['factory_service'];
}
return $this->services['factory_service'] = $a->getInstance();
return $this->services['factory_service'] = ($this->services['foo.baz'] ?? $this->getFoo_BazService())->getInstance();
}
/**
@ -253,13 +237,7 @@ class ProjectServiceContainer extends Container
*/
protected function getFactoryServiceSimpleService()
{
$a = ($this->privates['factory_simple'] ?? $this->getFactorySimpleService());
if (isset($this->services['factory_service_simple'])) {
return $this->services['factory_service_simple'];
}
return $this->services['factory_service_simple'] = $a->getInstance();
return $this->services['factory_service_simple'] = ($this->privates['factory_simple'] ?? $this->getFactorySimpleService())->getInstance();
}
/**
@ -271,10 +249,6 @@ class ProjectServiceContainer extends Container
{
$a = ($this->services['foo.baz'] ?? $this->getFoo_BazService());
if (isset($this->services['foo'])) {
return $this->services['foo'];
}
$this->services['foo'] = $instance = \Bar\FooClass::getInstance('foo', $a, array('bar' => 'foo is bar', 'foobar' => 'bar'), true, $this);
$instance->foo = 'bar';

View File

@ -72,13 +72,7 @@ class ProjectServiceContainer extends Container
*/
protected function getBarServiceService()
{
$a = ($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass());
if (isset($this->services['bar_service'])) {
return $this->services['bar_service'];
}
return $this->services['bar_service'] = new \stdClass($a);
return $this->services['bar_service'] = new \stdClass(($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass()));
}
/**
@ -164,10 +158,6 @@ class ProjectServiceContainer extends Container
{
$a = ($this->services['translator.loader_3'] ?? $this->services['translator.loader_3'] = new \stdClass());
if (isset($this->services['translator_3'])) {
return $this->services['translator_3'];
}
$this->services['translator_3'] = $instance = new \Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator(new \Symfony\Component\DependencyInjection\ServiceLocator(array('translator.loader_3' => function () {
return ($this->services['translator.loader_3'] ?? $this->services['translator.loader_3'] = new \stdClass());
})));

View File

@ -63,13 +63,7 @@ class ProjectServiceContainer extends Container
*/
protected function getBarServiceService()
{
$a = ($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass());
if (isset($this->services['bar_service'])) {
return $this->services['bar_service'];
}
return $this->services['bar_service'] = new \stdClass($a);
return $this->services['bar_service'] = new \stdClass(($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass()));
}
/**
@ -79,12 +73,6 @@ class ProjectServiceContainer extends Container
*/
protected function getFooServiceService()
{
$a = ($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass());
if (isset($this->services['foo_service'])) {
return $this->services['foo_service'];
}
return $this->services['foo_service'] = new \stdClass($a);
return $this->services['foo_service'] = new \stdClass(($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass()));
}
}

View File

@ -56,11 +56,13 @@ class PropertyNormalizer extends AbstractObjectNormalizer
$class = new \ReflectionClass($class);
// We look for at least one non-static property
foreach ($class->getProperties() as $property) {
if (!$property->isStatic()) {
return true;
do {
foreach ($class->getProperties() as $property) {
if (!$property->isStatic()) {
return true;
}
}
}
} while ($class = $class->getParentClass());
return false;
}

View File

@ -441,6 +441,11 @@ class PropertyNormalizerTest extends TestCase
$this->assertEquals($expected, $result);
}
public function testInheritedPropertiesSupport()
{
$this->assertTrue($this->normalizer->supportsNormalization(new PropertyChildDummy()));
}
}
class PropertyDummy
@ -509,3 +514,12 @@ class StaticPropertyDummy
{
private static $property = 'value';
}
class PropertyParentDummy
{
private $foo = 'bar';
}
class PropertyChildDummy extends PropertyParentDummy
{
}