Skip to content
Advertisement

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("RedCrossQuestServiceSlackService")
   * @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 ?

Advertisement

Answer

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();
User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement