ServiceLocator   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 138
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 2

Importance

Changes 0
Metric Value
wmc 18
lcom 2
cbo 2
dl 0
loc 138
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 1 1
A instance() 0 8 2
A get() 0 25 5
A _getService() 0 8 2
A _getFactory() 0 20 5
A set() 0 8 2
A destroy() 0 4 1
1
<?php
2
3
namespace Faulancer\ServiceLocator;
4
5
use Faulancer\Exception\FactoryMayIncompatibleException;
6
use Faulancer\Exception\ServiceNotFoundException;
7
8
/**
9
 * Class ServiceLocator
10
 *
11
 * @package Faulancer\ServiceLocator
12
 * @author Florian Knapp <[email protected]>
13
 */
14
class ServiceLocator implements ServiceLocatorInterface {
15
16
    /**
17
     * Holds the service locator instance
18
     * @var ServiceLocator
19
     */
20
    private static $instance = null;
21
22
    /**
23
     * Holds the requested services
24
     * @var array
25
     */
26
    private static $services = [];
27
28
    /** @var array */
29
    private static $backup = [];
30
31
    /**
32
     * ServiceLocator private constructor.
33
     */
34
    private function __construct() {}
35
36
    /**
37
     * Return the instance of the service locator
38
     * @return ServiceLocator
39
     */
40
    public static function instance()
41
    {
42
        if (self::$instance === null) {
43
            self::$instance = new self;
44
        }
45
46
        return self::$instance;
47
    }
48
49
    /**
50
     * Try to get the service.
51
     * Returns per default always the same instance of the service/factory.
52
     *
53
     * @param string  $service
54
     * @param bool    $shared
55
     * @return ServiceInterface|FactoryInterface
56
     */
57
    public function get(string $service = '', bool $shared = true)
58
    {
59
        if (in_array($service, array_keys(self::$backup))) {
60
61
            $overriddenService = self::$services[$service];
62
            self::$services[$service] = self::$backup[$service];
63
            unset(self::$backup[$service]);
64
            return $overriddenService;
65
66
        }
67
68
        if ($shared && !empty(self::$services[$service])) {
69
            return self::$services[$service];
70
        }
71
72
        try {
73
            $class = $this->_getFactory($service)->createService($this);
74
        } catch (FactoryMayIncompatibleException $e) {
75
            $class = $this->_getService($service);
76
        }
77
78
        self::$services[$service] = $class;
79
80
        return $class;
81
    }
82
83
    /**
84
     * Get specific service by class name
85
     *
86
     * @param  string $service
87
     * @return mixed
88
     * @throws ServiceNotFoundException
89
     */
90
    private function _getService(string $service)
91
    {
92
        if (!class_exists($service)) {
93
            throw new ServiceNotFoundException($service . ' not found');
94
        }
95
96
        return new $service();
97
    }
98
99
    /**
100
     * Check if we have a factory for this service
101
     *
102
     * @param string $service
103
     * @return FactoryInterface|null
104
     * @throws FactoryMayIncompatibleException
105
     */
106
    private function _getFactory(string $service)
107
    {
108
        $parts     = explode('\\', $service);
109
        $className = array_splice($parts, count($parts) - 1, 1);
110
        $class     = implode('\\', $parts) . '\\Factory\\' . $className[0] . 'Factory';
111
112
        $isAutoDetected = class_exists($class) && in_array(FactoryInterface::class, class_implements($class));
113
        $isDirectAccess = class_exists($service) && in_array(FactoryInterface::class, class_implements($service));
114
115
        if ($isAutoDetected) {
116
            return new $class();
117
        }
118
119
        // This is a direct factory access
120
        if ($isDirectAccess) {
121
            return new $service();
122
        }
123
124
        throw new FactoryMayIncompatibleException();
125
    }
126
127
    /**
128
     * @param string           $name
129
     * @param ServiceInterface $service
130
     * @internal
131
     */
132
    public function set($name, $service)
133
    {
134
        if (isset(self::$services[$name])) {
135
            self::$backup[$name] = self::$services[$name];
136
        }
137
138
        self::$services[$name] = $service;
139
    }
140
141
    /**
142
     * Reset the service locators instance
143
     *
144
     * @internal
145
     */
146
    public static function destroy()
147
    {
148
        self::$instance = null;
149
    }
150
151
}