Skip to content

[FrameworkBundle] [DI] Automatically add "setLogger" method call. #24973

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from

Conversation

GaryPEGEOT
Copy link
Contributor

@GaryPEGEOT GaryPEGEOT commented Nov 15, 2017

Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets
License MIT
Doc PR

Automatically add setLogger method call to any service that implements Psr\Log\LoggerAwareInterface.

Not sure if this belong to FrameworkBundle or MonologBundle.

@nicolas-grekas nicolas-grekas added this to the 4.1 milestone Nov 15, 2017
@ro0NL
Copy link
Contributor

ro0NL commented Nov 15, 2017

enable by autoconfigure: true?

@chalasr
Copy link
Member

chalasr commented Nov 15, 2017

Belongs to monolog-bundle to me.

@ogizanagi
Copy link
Contributor

ogizanagi commented Nov 15, 2017

Looks good to me in the FrameworkBundle, as we now have the default logger from HttpKernel registered as logger service if monolog (or any other one registering a logger service) isn't installed.

}

$refl = new \ReflectionClass($definition->getClass());
if ($refl->implementsInterface(LoggerAwareInterface::class) && !$definition->hasMethodCall('setLogger')) {
Copy link
Contributor

@stloyd stloyd Nov 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe:

if ($definition->hasMethodCall('setLogger')) {
    continue;
}

if (\in_array(LoggerAwareInterface::class, class_implements($definition->getClass()), true)) {
}

$reference = new Reference('logger');

foreach ($container->getDefinitions() as $definition) {
if (!class_exists($definition->getClass()) || !$definition->isAutoconfigured()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

|| $definition->hasMethodCall('setLogger') here?

continue;
}

$refl = new \ReflectionClass($definition->getClass());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$container->getReflectionClass() i guess =/

class LoggerAwarePassTest extends TestCase
{
/**
* @covers \Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\LoggerAwarePass::process()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dont think this is needed really.

$reference = new Reference('logger');

foreach ($container->getDefinitions() as $definition) {
if (!class_exists($definition->getClass()) || !$definition->isAutoconfigured()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no reason to skip autoconfigured definitions. If we want autoconfigure to leverage this, the compiler pass should only process definitions with a given tag, and that tag+LoggerAwareInterface should be registered for autoconfiguration (see https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php#L269).

continue;
}

$refl = new \ReflectionClass($definition->getClass());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should use $container->getReflectionClass() and be done earlier, replacing the class_exists check by !$refl and probably throwing in case the check passes. See e.g. https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Console/DependencyInjection/AddConsoleCommandPass.php#L55


foreach ($container->getDefinitions() as $definition) {
$class = $definition->getClass();
if (!class_exists($class) || !$definition->isAutoconfigured() || $definition->hasMethodCall('setLogger')) {
Copy link
Member

@chalasr chalasr Nov 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want autoconfigure to leverage this, the compiler pass should only process definitions with a given tag, and that tag+LoggerAwareInterface should be registered for autoconfiguration (see https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php#L269).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!class_exists should be replaced by !$refl

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About autoconfiguration, should I add tag in Framework extension (like logger.aware?) or skip this checking and add the method call anyway ?

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, we don't need that compiler pass at all.
If we want to provide the behavior when autoconfiguration is on, we should just call $container->registerForAutoconfiguration(LoggerAwareInterface::class)->addMethodCall('setLogger', array(new Reference(LoggerInterface::class))) in some DI extension.

@GaryPEGEOT
Copy link
Contributor Author

Shouldn't we check that method call ins't already called explicitly ?

@chalasr
Copy link
Member

chalasr commented Nov 15, 2017

@nicolas-grekas Actually I think adding method calls to autoconfigure instanceofs is forbidden https://github.com/symfony/symfony/blob/master/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php#L37, isn't it? Should we remove that restriction?

@nicolas-grekas
Copy link
Member

Calling a setter twice is not an issue to me so not really a pb.
And yes, we should remove the restriction in place.

@chalasr
Copy link
Member

chalasr commented Nov 15, 2017

@GaryPEGEOT Can you replace the removed test by a new one ensuring that method calls are handled?

@@ -315,6 +317,9 @@ public function load(array $configs, ContainerBuilder $container)
$container->registerForAutoconfiguration(ObjectInitializerInterface::class)
->addTag('validator.initializer');

$container->registerForAutoconfiguration(LoggerAwareInterface::class)
->addMethodCall('setLogger', array(new Reference(LoggerInterface::class)));
Copy link
Contributor

@ro0NL ro0NL Nov 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling a setter twice is not an issue to me so not really a pb.

It would be a WTF no? (who's winning here?) Requires to move your definition outside the current file/context at least... not sure that justifies our technical implem, which is correct; we add a method call. But being a setter that messes things up.

Also not sure it justifies a addMethodCallOnce feat., perhaps as 2nd arg?

@GaryPEGEOT
Copy link
Contributor Author

@nicolas-grekas Actually autoconfigured calls seems not to be working out of the box (Or I'm missing something.):

    /**
     * Test that autoconfigured calls are handled gracefully.
     */
    public function testProcessForAutoconfiguredCalls()
    {
        $container = new ContainerBuilder(); $container->registerForAutoconfiguration(LoggerAwareInterface::class)->addMethodCall('setLogger')->addTag('foo');

        $def = $container->register(\get_class($this->createMock(LoggerAwareInterface::class)));
        $this->assertFalse($def->hasTag('foo'), 'Definition shouldn\'t have tags yet.'); // Passes
        $this->assertFalse($def->hasMethodCall('setLogger'), 'Definition shouldn\'t have method call yet.'); // Passes

        (new ResolveInstanceofConditionalsPass())->process($container);
        $this->assertTrue($def->hasTag('foo'), 'Definition should have "foo" tag.'); // Passes
        $this->assertTrue($def->hasMethodCall('setLogger'), 'Definition should have "setLogger" method call.'); // Fail
    }

And I see potential problems for calling set twice, if by example, method is overrided for any reason:

    /**
     * Sets a logger instance on the object.
     *
     * @param LoggerInterface $logger
     * @param string          $channel Which channel to use
     *
     * @return void
     */
    public function setLogger(LoggerInterface $logger, string $channel = null)
    {
        // TODO: Implement setLogger() method.
    }

Here second parameter will be called with default param, so not necessary the expected behavior.

Compiler pass with tagged services seems to be the best option to me.

@GaryPEGEOT GaryPEGEOT force-pushed the feature/logger-aware-di branch from 06502f1 to 35404a9 Compare November 16, 2017 10:13
@nicolas-grekas
Copy link
Member

Actually autoconfigured calls seems not to be working out of the box (Or I'm missing something.):
And I see potential problems for calling set twice, if by example, method is overrided for any reason

I'm OK to agree with that. That makes me wonder we should make that work in fact. The current exception in place that prevents using setters was put in place exactly because we didn't bother fixing those topic at the time.

But I would definitely prefer merging a PR that fixes that, which would provide high generality to the feature, vs merging a hardcoded adhoc alternative. The compiler pass as is could really well be part of a third party bundle meanwhile if you really want it.

@weaverryan
Copy link
Member

This makes sense. But this still requires the user to add the setLogger method. A better DX would be to add a new LoggerTrait with ‘@required’ above the setLogger method.

@ogizanagi
Copy link
Contributor

ogizanagi commented Nov 16, 2017

But this still requires the user to add the setLogger method

There is already a LoggerAwareTrait part of the psr/log package.

@GaryPEGEOT
Copy link
Contributor Author

I made a separate MR #24996 to allow auto-configured method calls

@nicolas-grekas
Copy link
Member

Closing in favor of #24996, thanks @GaryPEGEOT for taking over.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants