Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[DI] wither calls break services when used in certain configurations #48814

Copy link
Copy link
Closed
@cs278

Description

@cs278
Issue body actions

Symfony version(s) affected

4.4.49, 5.4.17 (those I've reproduced the issue on)

Description

Configuring a service that uses the wither pattern, added in #30212, can break the dumped PHP container with more complex configurations. The inlined service that uses the wither pattern for some reason is written into the container with the service ID of the dependant service, most of the time it is subsequently replaced by the correct implementation but in some configurations this does not happen.

As an example this YAML generates the following method:

services:
    _defaults:
        autoconfigure: true

    App\TestServiceOptions:
        public: false
        calls:
            - withOption: !returns_clone ['foo', 'bar']

    App\TestService:
        public: false
        calls:
            - setOptions: ['@App\TestServiceOptions']

    App\TestCommand:
        arguments:
            - ~
            - '@App\TestService'
/**
 * Gets the private 'App\TestCommand' shared service.
 *
 * @return \App\TestCommand
 */
public static function do($container, $lazyLoad = true)
{
   // include_once ...

    $a = new \App\TestService();

    $b = new \App\TestServiceOptions()
    // Here: inlined wither service is written as App\TestCommand
    $container->privates['App\TestCommand'] = $b = $b->withOption('foo', 'bar');

    $a->setOptions($b);

    // Invalid service is replaced here - fixing the problem.
    $container->privates['App\\TestCommand'] = $instance = new \App\TestCommand(NULL, $a);

    $instance->setName('test');

    return $instance;
}

As I said above in some situations, the correct initialisation of $container->privates['App\\TestCommand'] is done before instead of after which then breaks other services as they get completely the wrong class.

How to reproduce

I've not managed to create a simple reproducer, but I do have one based on the Sentry Symfony bundle which is how I found this problem in the first place. https://github.com/cs278/symfony-issue-48814

git clone https://github.com/cs278/symfony-issue-48814.git
composer install --no-scripts
rm -rf var/cache/dev/
bin/console cache:clear -vvv
grep -h -R -B 10 -A 20 'SentryBeforeSend(' var/cache/dev/Container*/*.php

In this case the following service method is created:

/**
 * Gets the private 'Sentry\State\HubInterface' shared service.
 *
 * @return \Sentry\State\HubInterface
 */
protected function getHubInterfaceService()
{
    $this->privates['Sentry\\State\\HubInterface'] = $instance = \Sentry\State\HubAdapter::getInstance();

    $a = new \App\SentryBeforeSend();
    $this->privates['Sentry\State\HubInterface'] = $a = $a->withTag('foo', 'bar'); // Broken!

    $b = new \Sentry\Options(['before_send' => $a, 'integrations' => [0 => new \Sentry\Integration\IgnoreErrorsIntegration(['ignore_exceptions' => [0 => 'Symfony\\Component\\ErrorHandler\\Error\\FatalError', 1 => 'Symfony\\Component\\Debug\\Exception\\FatalErrorException']]), 1 => new \Sentry\Integration\RequestIntegration(new \Sentry\SentryBundle\Integration\RequestFetcher(($this->services['request_stack'] ?? ($this->services['request_stack'] = new \Symfony\Component\HttpFoundation\RequestStack())), NULL))], 'prefixes' => [0 => \dirname(__DIR__, 4), 1 => '.', 2 => '/usr/share/php'], 'trace_propagation_targets' => [], 'environment' => 'dev', 'release' => 'dev-master@425c01f', 'tags' => [], 'in_app_exclude' => [0 => $this->targetDir.'', 1 => $this->targetDir.'', 2 => (\dirname(__DIR__, 4).'/vendor')], 'in_app_include' => [], 'class_serializers' => [], 'dsn' => $this->getEnv('SENTRY_DSN')]);

    $c = new \Sentry\ClientBuilder($b);
    $c->setSdkIdentifier('sentry.php.symfony');
    $c->setSdkVersion('4.5.0');
    $c->setTransportFactory(new \Sentry\SentryBundle\Transport\TransportFactory(NULL, NULL, NULL, NULL, NULL, NULL));
    $c->setSerializer(new \Sentry\Serializer\Serializer($b));
    $c->setRepresentationSerializer(new \Sentry\Serializer\RepresentationSerializer($b));
    if ($this->has('Psr\\Log\\NullLogger')) {
        $c->setLogger(($this->services['Psr\\Log\\NullLogger'] ?? $this->get('Psr\\Log\\NullLogger', /* ContainerInterface::NULL_ON_INVALID_REFERENCE */ 2)));
    }

    $instance->bindClient($c->getClient());

    return $instance;
}

Possible Solution

I've got a PR incoming with a fix.

Additional Context

Workaround until a fix is released is to make the service that uses the returns_clone tag public.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.