1

I have two Services in my plugin. MyPluginService is the "main" service, and MyPlugin_OtherService accomplishes some other tasks (when it is occasionally needed).

There is an init method in MyPluginService which starts a chain of events, and under certain circumstances will call a method in MyPlugin_OtherService.

MyPluginService

public function init() {
    parent::init();
    if ("conditions are met") {
        craft()->myPlugin_otherService->methodInOtherService();
    }
}

public function methodInMainService() {
    // ...
}

MyPlugin_OtherService

public function methodInOtherService() {
    craft()->myPlugin->methodInMainService();
}

While the method in MyPlugin_OtherService is being run, it will refer back to a method in MyPluginService. This is where the error is triggered:

Fatal error: Call to undefined method stdClass::methodInMainService()

I don't understand why it's not recognizing that call to the main service method. Hasn't the main service already been initialized before this point? There is a lot about "lazy loading" that I don't understand, maybe this falls in that knowledge gap.

2

Your service’s init() function gets called immediately after it is loaded – and before Yii has had a chance to remember that craft()->myPlugin should return the service instance.

Take a look at these two lines from CModule::getComponent() (the method that the magic getter invokes when you call craft()->somePluginName):

$component->init();
return $this->_components[$id]=$component;

So when MyPlugin_OtherService::methodInOtherService() tries to refer back to craft()->myPlugin, craft()->myPlugin has not yet been assigned to the service instance, which results in the PHP fatal error you’re getting.

I can propose a change that would fix this:

$this->_components[$id] = $component;
$component->init();
return $component;

…but I’m not sure what the implications would be for that.

You might just need to question whether it’s so important for this code to be running from init() as opposed to another function that you’d have to invoke manually. Having it in init() could lead to other unexpected consequences as well.

|improve this answer|||||
  • To be clear, I'm talking about two services within the same plugin. – Lindsey D Jul 8 '14 at 22:51
  • 1
    @LindseyD Sorry, totally missed that. Updated my answer. – Brandon Kelly Jul 8 '14 at 23:07
  • +1 for moving it out of init() and explicitly calling it. – Brad Bell Jul 8 '14 at 23:10
  • Thanks Brandon. I've got it in the init because it's a pretty big piece of the puzzle, and many other facets of the plugin depend on the data generated there. But, if I'm understanding you correctly, you are suggesting that I remove that method call from the init, and instead simply call it manually before any possible scenario where the data might be needed? Then I would probably have to set an alreadyRun property in my main service class so I can easily determine if the process has been run. Does that sound like a better solution to you? – Lindsey D Jul 8 '14 at 23:35
  • 1
    @LindseyD yep, it will get called at the beginning of each request, and all your services will be available at that point. – Brandon Kelly Jul 9 '14 at 0:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.