samedi 16 janvier 2016

Implementation of Dependency Injection and Service Locator

I came up to the following two simple implementations of DI and SL in my application:

Variant 1

/**
 * Get instance of service by name, or register new service.
 * 
 * @param string $name service name
 * @param callable $producer callable that returns an instance of service
 * @return mixed if producer is not set:
 * the instance of service, or NULL, if no such service was registered;
 * if producer is set: TRUE
 * @throws \LogicException if there is recursion in dependencies
 */
function service( $name, callable $producer = null ) {
    static $services = [];
    if( $producer ) {
        $services[ $name ] = function () use ( $producer ) {
            static $service = null;
            if( !$service ) {
                static $called = false;
                if( $called ) {
                    throw new \LogicException(
                            'Recursion in dependencies detected');
                }
                $called = true;
                $service = call_user_func( $producer );
                $called = false;
            }
            return $service;
        };
        return true;
    }
    return @call_user_func( $services[ $name ] );
}

Variant 2

/**
 * Get instance of service by name, or register new service.
 * 
 * @param string $name service name
 * @param callable $producer callable that returns an instance of service
 * @return mixed if producer is not set:
 * the instance of service, or NULL, if no such service was registered;
 * if producer is set: TRUE
 * @throws \LogicException if there is recursion in dependencies
 */
function service( $name, callable $producer = null ) {
    static $producers = [];
    static $services = [];
    if( $producer ) {
        $producers[ $name ] = $producer;
        $services[ $name ] = null;
        return true;
    }
    if( !@$services[ $name ] ) {
        static $called = [];
        if( @$called[ $name ] ) {
            throw new \LogicException('Recursion in dependencies detected');
        }
        $called[ $name ] = true;
        $services[ $name ] = @call_user_func( $producers[ $name ] );
        $called[ $name ] = false;
    }
    return $services[ $name ];
}

Example of use

/* services */

class ServiceFoo {}

class ServiceBar {}

class ServiceBaz {

    private $foo;
    private $bar;

    public function __construct( ServiceFoo $foo, ServiceBar $bar ) {
        $this->foo = $foo;
        $this->bar = $bar;
    }
}

/* register services */

service('foo', function () {
    return new ServiceFoo();
});

service('bar', function () {
    return new ServiceBar();
});

service('baz', function () {
    return new ServiceBaz(
        service('foo'),
        service('bar')
    );
});

/* get instance of ServiceBaz */

$baz = service('baz');

Which one of the variants do you suggest to use and why? Are there any pitfalls in these implementations that I had missed?

Aucun commentaire:

Enregistrer un commentaire