Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
54.24% covered (warning)
54.24%
32 / 59
36.84% covered (danger)
36.84%
7 / 19
CRAP
0.00% covered (danger)
0.00%
0 / 1
ServiceManager
54.24% covered (warning)
54.24%
32 / 59
36.84% covered (danger)
36.84%
7 / 19
176.39
0.00% covered (danger)
0.00%
0 / 1
 getServiceManager
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setServiceManager
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getServiceId
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
4
 load
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
5.05
 tryAutowiring
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 has
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 register
75.00% covered (warning)
75.00%
6 / 8
0.00% covered (danger)
0.00%
0 / 1
3.14
 unregister
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getConfig
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 propagate
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 build
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 __sleep
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 overload
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getContainer
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 rebuildContainer
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getContainerBuilder
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getContainerStarter
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; under version 2
7 * of the License (non-upgradable).
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 * Copyright (c) 2015-2021 (original work) Open Assessment Technologies SA;
19 *
20 */
21
22namespace oat\oatbox\service;
23
24use oat\generis\model\DependencyInjection\ContainerBuilder;
25use oat\generis\model\DependencyInjection\ContainerStarter;
26use oat\oatbox\Configurable;
27use Zend\ServiceManager\ServiceLocatorInterface;
28use Zend\ServiceManager\ServiceLocatorAwareInterface;
29use Psr\Container\ContainerInterface;
30
31/**
32 * The simple placeholder ServiceManager
33 * @author Joel Bout <joel@taotesting.com>
34 */
35class ServiceManager implements ServiceLocatorInterface, ContainerInterface
36{
37    private static $instance;
38
39    /** @var ContainerStarter */
40    private $containerStarter;
41
42    /**
43     * @return \oat\oatbox\service\ServiceManager
44     * @deprecated Pass service locator instead of relying on static function
45     */
46    public static function getServiceManager()
47    {
48        if (is_null(self::$instance)) {
49            self::$instance = new ServiceManager(\common_ext_ConfigDriver::singleton());
50        }
51        return self::$instance;
52    }
53
54    public static function setServiceManager(ServiceManager $serviceManager)
55    {
56        self::$instance = $serviceManager;
57    }
58
59    private $services = [];
60
61    /**
62     * @var \common_persistence_KeyValuePersistence
63     */
64    private $configService;
65
66    public function __construct($configService)
67    {
68        $this->configService = $configService;
69    }
70
71    /**
72     * Returns the service configured for the serviceKey
73     * or throws a ServiceNotFoundException
74     *
75     * @param string $serviceKey
76     * @return ConfigurableService
77     * @throws ServiceNotFoundException
78     * @see ContainerInterface::get()
79     * @deprecated Use $this->getContainer()->get()
80     */
81    public function get($serviceKey)
82    {
83        $serviceId = $this->getServiceId($serviceKey);
84        if (!isset($this->services[$serviceId])) {
85            $this->services[$serviceId] = $this->load($serviceId, $serviceKey);
86        }
87        return $this->services[$serviceId];
88    }
89
90    /**
91     * Extract the service id from the provided parameter
92     * @param string $serviceKey
93     * @return string
94     */
95    private function getServiceId($serviceKey)
96    {
97        return ((interface_exists($serviceKey) || class_exists($serviceKey)) && defined($serviceKey . '::SERVICE_ID'))
98            ? $serviceKey::SERVICE_ID
99            : (string)$serviceKey
100        ;
101    }
102
103    /**
104     * Loads the service referenced by id
105     *
106     * @param string $serviceId
107     * @param string $serviceKey
108     * @throws ServiceNotFoundException
109     * @return ConfigurableService
110     */
111    private function load($serviceId, $serviceKey)
112    {
113        $service = $this->getConfig()->get($serviceId);
114        if ($service === false) {
115            $service = $this->tryAutowiring($serviceId, $serviceKey);
116        }
117
118        if ($service instanceof ServiceFactoryInterface) {
119            $service = $service($this);
120        }
121
122        if (is_object($service) && ($service instanceof ServiceLocatorAwareInterface)) {
123            $service->setServiceLocator($this);
124        }
125        return $service;
126    }
127
128    /**
129     * Try to initialize the class without parameters
130     *
131     * @param string $serviceId
132     * @param string $serviceKey
133     * @throws ServiceNotFoundException
134     * @return ConfigurableService
135     */
136    private function tryAutowiring($serviceId, $serviceKey)
137    {
138        if (!class_exists($serviceKey) || !is_subclass_of($serviceKey, ConfigurableService::class)) {
139            throw new ServiceNotFoundException($serviceId);
140        }
141        return new $serviceKey();
142    }
143
144    /**
145     * (non-PHPdoc)
146     * @see ContainerInterface::has()
147     * @deprecated Use $this->getContainer()->has()
148     */
149    public function has($serviceKey)
150    {
151        if (isset($this->services[$serviceKey])) {
152            return true;
153        }
154        $parts = explode('/', $serviceKey, 2);
155        if (count($parts) < 2) {
156            return false;
157        }
158
159        return $this->getConfig()->exists($serviceKey);
160    }
161
162    /**
163     * Registers a service, overwritting a potentially already
164     * existing service.
165     *3
166     * @param string $serviceKey
167     * @param ConfigurableService $service
168     * @throws \common_Exception
169     * @deprecated New services must be registered using Dependency Injection Container
170     */
171    public function register($serviceKey, ConfigurableService $service)
172    {
173        $parts = explode('/', $serviceKey, 2);
174        if (count($parts) < 2) {
175            throw new \common_Exception('Invalid servicekey ' . $serviceKey);
176        }
177        $this->propagate($service);
178        $this->services[$serviceKey] = $service;
179        $success = $this->getConfig()->set($serviceKey, $service);
180        if (!$success) {
181            throw new \common_exception_Error('Unable to write ' . $serviceKey);
182        }
183    }
184
185    /**
186     * @deprecated New services must be registered using Dependency Injection Container
187     */
188    public function unregister($serviceKey)
189    {
190        unset($this->services[$serviceKey]);
191        return $this->getConfig()->del($serviceKey);
192    }
193
194    /**
195     * @return \common_persistence_KeyValuePersistence
196     */
197    protected function getConfig()
198    {
199        return $this->configService;
200    }
201
202    /**
203     * Propagate service dependencies
204     *
205     * @param  $service
206     * @return mixed
207     *
208     * @deprecated - If class uses ServiceManagerAwareTrait use $this->propagate($service)
209     * @deprecated New services must be registered using Dependency Injection Container
210     */
211    public function propagate($service)
212    {
213        if (is_object($service) &&  ($service instanceof ServiceLocatorAwareInterface)) {
214            $service->setServiceLocator($this);
215        }
216        return $service;
217    }
218
219
220    /**
221     * Service or sub-service factory
222     *
223     * @param $className
224     * @param array $options
225     * @return mixed
226     * @deprecated New services must be registered using Dependency Injection Container
227     */
228    public function build($className, array $options = [])
229    {
230        if (is_a($className, Configurable::class, true)) {
231            $service = new $className($options);
232            $this->propagate($service);
233            return $service;
234        }
235
236        throw new ServiceNotFoundException($className);
237    }
238
239    /**
240     * Prevents accidental serialisation of the services
241     * @return array
242     */
243    public function __sleep()
244    {
245        return [];
246    }
247
248    /**
249     * Dynamically overload a service without persisting it
250     *
251     * @param $serviceKey
252     * @param ConfigurableService $service
253     * @deprecated New services must be registered using Dependency Injection Container
254     */
255    public function overload($serviceKey, ConfigurableService $service)
256    {
257        $this->services[$serviceKey] = $service;
258    }
259
260    /**
261     * @TODO Container will be removed from here as soon as we do not need ServiceManager anymore.
262     */
263    public function getContainer(): ContainerInterface
264    {
265        return $this->getContainerStarter()->getContainer();
266    }
267
268    public function rebuildContainer(): void
269    {
270        // if container was already built on same request, it has to be restarted before rebuild
271        // to avoid fatal error of compiled container modification
272        $this->containerStarter = null;
273        $this->getContainerStarter()->getContainerBuilder()->forceBuild();
274        // after container rebuild this cached starter needs a reset, to avoid using container already cached in memory
275        $this->containerStarter = null;
276    }
277
278    /**
279     * @TODO ContainerBuilder will be removed from here as soon as we do not need ServiceManager anymore.
280     */
281    public function getContainerBuilder(): ContainerBuilder
282    {
283        return $this->getContainerStarter()->getContainerBuilder();
284    }
285
286    /**
287     * @TODO ContainerStarter will be removed from here as soon as we do not need ServiceManager anymore.
288     */
289    private function getContainerStarter(): ContainerStarter
290    {
291        if (!$this->containerStarter) {
292            $this->containerStarter = new ContainerStarter($this);
293        }
294
295        return $this->containerStarter;
296    }
297}