Skip to content
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

Feature/service locator #665

Open
wants to merge 9 commits into
base: master
from

Conversation

Projects
None yet
3 participants
@falkenhawk
Copy link

commented May 31, 2019

ServiceLocator

  • Serving "lazy" dependencies for classes using ServiceSubscriberInterface.
  • A ServiceSubscriber exposes its dependencies via static getSubscribedServices() method.
  • A ServiceLocator instance could then be injected into a class via constructor or a property - the instance would be already configured with dependences read from getSubscribedServices(), but the dependences won't be instantiated until first get - that's how "laziness" is introduced
  • DI\Definition\Reference checks if it's a ServiceLocator entry by comparing its name with DI\Definition\Reference::$serviceLocatorClass
  • Reference definitions are passed with additional parameter - $requestingName which generally points to name of the class which implements ServiceSubscriberInterface - to resolve ServiceLocator for that class
  • Suggested as a lightweight alternative for heavyweight proxies from ocramius/proxy-manager

note:

  • it's based on changes from #662 (so commit 59a2216 is included here as well) - since there is second argument passed to Reference constructor, to also cover AnnotationBasedAutowiring::readConstructor() method, which was introduced in that PR

Usage:

class MyClass implements ServiceSubscriberInterface
{
    /**
     * @var ServiceLocator
     */
    protected $serviceLocator;

    /**
     * @param ServiceLocator $serviceLocator
     */
    public function __construct(ServiceLocator $serviceLocator)
    {
        $this->serviceLocator = $serviceLocator;
    }

    /**
     * Lazy instantiate heavy dependencies on-demand
     */
    public static function getSubscribedServices(): array
    {
        return [
            SomeClass::class,
            'heavyDependency' => HeavyService::class,
        ];
    }

    public function doOperation()
    {
        $someClass = $this->serviceLocator->get(SomeClass::class);
        return $someClass->doSomething();
    }

    public function getSomethingFromThatHeavyDependency()
    {
        // this method may be rarely used, and it might be good idea to skip resolving the dependency every time during instantiation for performance reasons
        return $this->serviceLocator->get('heavyDependency')->getSomething();
    }
}

[from ovos#7]

falkenhawk added some commits Apr 26, 2019

AnnotationBasedAutowiring with additional options for performance tweaks
- adds possibility to disable features which are not used
- useAnnotations(boolean) toggle on autowire() helper to enable/disable reading annotations on specific definitions
ServiceLocator
- Serving "lazy" dependencies for classes using ServiceSubscriberInterface.
- A ServiceSubscriber exposes its dependencies via static getSubscribedServices() method.
- A ServiceLocator instance could then be injected into a class via constructor or a property - the instance would be already configured with dependences read from getSubscribedServices(), but the dependences won't be instantiated until first get - that's how "laziness" is introduced
- DI\Definition\Reference checks if it's a ServiceLocator entry by comparing its name with DI\Definition\Reference::$serviceLocatorClass
- Reference definitions are passed with additional parameter - $requestingName which generally points to name of the class which implements ServiceSubscriberInterface - to resolve ServiceLocator for that class
- Suggested as a lightweight alternative for heavyweight proxies from ocramius/proxy-manager
ServiceLocatorRepository - replace setService with override method
- the method may be used only before a locator instance is created - to avoid ambiguous situations
- create method refactored - a locator instance will be returned if already created, only if passed services match, otherwise an exception will be thrown
Reference and ServiceLocatorDefinition cleanup
- resolve and isResolvable will check if getSubscribedServices method exists on given class
compiler - extract resolveServiceLocator method to CompiledContainer
+ added the same method_exists check as in ServiceLocatorDefinition
@SvenRtbg

This comment has been minimized.

Copy link

commented May 31, 2019

A service locator? Really? That is SO the anti pattern.

If creating a "heavy" dependency that is rarely used is bothering you, there are a couple of options without using the service locator antipattern:

  • Separate that part from the main class and instantiate only when needed
  • Make that dependency light, i.e. dont do work in the constructor

If you really feel you need a service locator, you could always implement a simple class that gets the DI container instance passed as a parameter and then only resolves specific calls when really needed. This would also not tie your own code to interface implementations of PHP-DI.

Remember that there is no PSR definition for a service locator interface (I guess there are reasons), so by adding such interfaces, you effectively make it impossible to change the dependency container.

@mnapoli

This comment has been minimized.

Copy link
Member

commented Jun 1, 2019

@SvenRtbg please respect others contributions.

You may be confused but ContainerInterface is a service locator interface. PHP-DI can be used as a service locator (and it has to be used like this at some point in an application, nothing is magic). Service locator is a pattern that is defined by the usage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.