1

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 ?

Thomas
  • 1,231
  • 14
  • 25
  • What is `googlelogger`? I don't know what I a missing, but I can't see the circular dependency. – Nima Aug 12 '20 at 17:26
  • Oups, corrected. Logger requires Slack, Slack Requires SecretManager, SecretManager requires Logger – Thomas Aug 12 '20 at 18:19
  • Is `SecretManager` a custom class you wrote? How and why does it use the logger? Does it specifically need `SlackService`? – Nima Aug 13 '20 at 04:03
  • Yes, it's a class I wrote to fetch secret from Google Secret Manager. Slack Service needs it to get its API TOKEN. The question here is not how to functionnally break the circular dependency, but how to do it technically. I've tried @Inject et inject Slack via a Setter instead of constructor, but the method never gets called. I'll update my post – Thomas Aug 13 '20 at 10:45
  • 1
    Have a look at these issues [191](https://github.com/PHP-DI/PHP-DI/issues/191) and [527](https://github.com/PHP-DI/PHP-DI/issues/527) on github and also, [this answer](https://stackoverflow.com/a/24078158/1788201) on SO. – Nima Aug 13 '20 at 16:33

1 Answers1

0

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();
Thomas
  • 1,231
  • 14
  • 25