Search code examples
phpdependency-injectionslimcircular-dependencyphp-di

Slim Framework : How to break circular dependency injection?


I'm using Slim 4.5.0 with PHP-DI 6 and I've a circular dependency issue.

I know how to solve this kind of issue using a setter, but in the context of SlimFramework, I can't get anything to work.

I want to send message to a Slack Channel when an error occurs.

LoggerInterface::class => function (ContainerInterface $c):Logger
{
  return new Logger($c->get(SlackService::class), $c->get("googleLogger"), (string)$c->get("RCQVersion"), $c->get('settings')['appSettings']['deploymentType'], $c->get('settings')['online']);
},

SecretManagerService::class => function (ContainerInterface $c):SecretManagerService
{
  return new SecretManagerService($c->get('settings'), $c->get(LoggerInterface::class));
},
SlackService::class =>function(ContainerInterface $c):SlackService
{
  $slackToken = $c->get(SecretManagerService::class)->getSecret(SecretManagerService::$SLACK_TOKEN);
  return new SlackService($slackToken, (string)$c->get("RCQVersion"), $c->get('settings')['appSettings']['deploymentType'], $c->get('settings')['online']);
},

What I need is to provide the SlackService to the my custom Logger.

I've tried to use @Inject keyword in my Logger class to set the Slack Service (and remove it from the constructor):

  /**
   * @Inject
   * @var SlackService $slackService
   */
  private $slackService;

Or use a setter function and @Inject (with and without the class Name)

 /**
   * @Inject("RedCrossQuest\Service\SlackService")
   * @param SlackService $slackService
   */
  public function setSlackService(SlackService $slackService)
  {
    $this->slackService = $slackService;
  }

But this doesn't work, while I feel it's the way to go.

I already use @Inject to set property on my class (string value), and it works well, but here, for some reasons it doesn't.

I didn't find here anything that could help to understand why it wouldn't work. https://php-di.org/doc/annotations.html

Each time an error occurs, I get an error saying slackService is null Uncaught Error: Call to a member function postMessage() on null

What am I missing to make the @Inject() work ?


Solution

  • As pointed by Nima with the tickets, Circular Dependencies can't be solved using setter, unless you use Lazy Loading. The catch is that it requires a proxy libs, that have 3 additional dependencies, which is a bit overkill for my simple use case. (also it seems that there's a missing step in the documentation of PHP-DI)

    • zendframework/zend-eventmanager (3.2.1)
    • zendframework/zend-code (3.4.1)
    • ocramius/proxy-manager (2.2.3)
    • ocramius/package-versions (1.5.1)

    To workaround this, I manually did the job of PHP-DI.

    • I set a setter on my Logger to set the SlackService, once the container is built, and I did not add the @Inject in the comments above the setter method.
    // Set up dependencies
    $dependencies = require __DIR__ . '/../../src/dependencies.php';
    $dependencies($containerBuilder);
    
    // Build PHP-DI Container instance
    $container = $containerBuilder->build();
    
    $loggerInterface = $container->get(LoggerInterface::class);
    $loggerInterface->setSlackService ($container->get(SlackService::class);
    
    
    // Instantiate the app
    AppFactory::setContainer($container);
    $app = AppFactory::create();