Passed
Push — master ( 562d32...d311da )
by Gabriel
01:39
created

ProviderRepository::add()   A

Complexity

Conditions 6
Paths 12

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6.1666

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 12
c 2
b 0
f 0
dl 0
loc 21
ccs 10
cts 12
cp 0.8333
rs 9.2222
cc 6
nc 12
nop 1
crap 6.1666
1
<?php
2
3
namespace Nip\Container\ServiceProviders;
4
5
use Nip\Container\ContainerAwareInterface;
6
use Nip\Container\ContainerAwareTrait;
7
use Nip\Container\ServiceProviders\Providers\AbstractServiceProvider;
8
use Nip\Container\ServiceProviders\Providers\BootableServiceProviderInterface;
9
use Nip\Container\ServiceProviders\Providers\ServiceProviderInterface;
10
use Nip\Utility\Arr;
11
12
/**
13
 * Class ServiceProviderAggregate
14
 * @package Nip\Container\ServiceProvider
15
 */
16
class ProviderRepository implements ProviderRepositoryInterface
17
{
18
    use ContainerAwareTrait;
19
20
    /**
21
     * @var array
22
     */
23
    protected $services = [];
24
25
    /**
26
     * @var AbstractServiceProvider[]
27
     */
28
    protected $providers = [];
29
30
    /**
31
     * @var array Array of registered providers class names
32
     */
33
    protected $registeredProviders = [];
34
35
    /**
36
     * The deferred services and their providers.
37
     *
38
     * @var array
39
     */
40
    protected $deferredServices = [];
41
42
    /**
43
     * Indicates if the application has "booted".
44
     *
45
     * @var bool
46
     */
47
    protected $booted = false;
48
49
    /**
50
     * Adds a new Service Provider
51
     * {@inheritdoc}
52
     */
53 2
    public function add($provider)
54
    {
55 2
        if (is_string($provider) && class_exists($provider)) {
56 1
            $provider = $this->resolveProvider($provider);
57
        }
58
59 2
        if ($provider instanceof ContainerAwareInterface) {
60 2
            $provider->setContainer($this->getContainer());
61
        }
62
63 2
        if ($provider instanceof ServiceProviderInterface) {
64 2
            foreach ($provider->provides() as $service) {
65 2
                $this->services[$service] = get_class($provider);
66
            }
67 2
            $this->providers[] = $provider;
68 2
            return $this;
69
        }
70
71
        throw new \InvalidArgumentException(
72
            'A service provider must be a fully qualified class name or instance ' .
73
            'of (\Nip\Container\ServiceProvider\ServiceProviderInterface)'
74
        );
75
    }
76
77
    /**
78
     * Resolve a service provider instance from the class name.
79
     *
80
     * @param  string $provider
81
     * @return AbstractServiceProvider
82
     */
83 1
    public function resolveProvider($provider)
84
    {
85 1
        return new $provider($this);
86
    }
87
88
    public function register()
89
    {
90
        foreach ($this->providers as $provider) {
91
            $this->registerProvider($provider);
92
        }
93
    }
94
95
    /**
96
     * Register a service provider with the application.
97
     *
98
     * @param  AbstractServiceProvider|string $provider
99
     * @return AbstractServiceProvider
100
     */
101
    public function registerProvider($provider)
102
    {
103
        if (($provider = $this->getProvider($provider))) {
104
        } elseif (is_string($provider)) {
0 ignored issues
show
introduced by
The condition is_string($provider) is always false.
Loading history...
105
            $provider = $this->resolveProvider($provider);
106
        }
107
108
        if ($this->registeredProvider($provider)) {
109
            return $provider;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $provider also could return the type Nip\Container\ServicePro...AbstractServiceProvider which is incompatible with the return type mandated by Nip\Container\ServicePro...ace::registerProvider() of void.
Loading history...
110
        }
111
112
        if (method_exists($provider, 'register')) {
113
            $provider->register();
114
        }
115
116
        $this->markAsRegistered($provider);
117
118
        // If the application has already booted, we will call this boot method on
119
        // the provider class so it has an opportunity to do its boot logic and
120
        // will be ready for any usage by this developer's application logic.
121
        if ($this->booted) {
122
            $this->bootProvider($provider);
0 ignored issues
show
Bug introduced by
It seems like $provider can also be of type null; however, parameter $provider of Nip\Container\ServicePro...ository::bootProvider() does only seem to accept Nip\Container\ServicePro...AbstractServiceProvider, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

122
            $this->bootProvider(/** @scrutinizer ignore-type */ $provider);
Loading history...
123
        }
124
        return $provider;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $provider also could return the type Nip\Container\ServicePro...eProvider&object|object which is incompatible with the return type mandated by Nip\Container\ServicePro...ace::registerProvider() of void.
Loading history...
125
    }
126
127
    /**
128
     * Get the registered service provider instance if it exists.
129
     *
130
     * @param  AbstractServiceProvider|string $provider
131
     * @return AbstractServiceProvider|null
132
     */
133 3
    public function getProvider($provider)
134
    {
135 3
        $name = is_string($provider) ? $provider : get_class($provider);
136
        return Arr::first($this->providers, function ($value) use ($name) {
137 2
            return $value instanceof $name;
138 3
        });
139
    }
140
141
    /**
142
     * @param AbstractServiceProvider $provider
143
     * @return bool
144
     */
145
    public function registeredProvider($provider)
146
    {
147
        $providerClass = get_class($provider);
148
        if (isset($this->registeredProviders[$providerClass]) && $this->registeredProviders[$providerClass] === true) {
149
            return true;
150
        }
151
        return false;
152
    }
153
154
    /**
155
     * Mark the given provider as registered.
156
     *
157
     * @param  AbstractServiceProvider $provider
158
     * @return void
159
     */
160
    protected function markAsRegistered($provider)
161
    {
162
        $this->registeredProviders[get_class($provider)] = true;
163
    }
164
165
    /**
166
     * Boot the given service provider.
167
     *
168
     * @param  AbstractServiceProvider $provider
169
     * @return mixed
170
     */
171
    protected function bootProvider(AbstractServiceProvider $provider)
172
    {
173
        if ($provider instanceof BootableServiceProviderInterface) {
174
            return $provider->boot();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $provider->boot() targeting Nip\Container\ServicePro...oviderInterface::boot() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
175
        }
176
        return null;
177
    }
178
    
179
    /**
180
     * @return array
181
     */
182
    public function getProviders(): array
183
    {
184
        return $this->providers;
185
    }
186
187
    /**
188
     * Check to see if the services is registered
189
     * {@inheritdoc}
190
     */
191
    public function provides($service)
192
    {
193
        return array_key_exists($service, $this->providers);
194
    }
195
196
    public function boot()
197
    {
198
        foreach ($this->providers as $provider) {
199
            $this->bootProvider($provider);
200
        }
201
202
        $this->setBooted(true);
203
    }
204
205
    /**
206
     * @param bool $booted
207
     */
208
    public function setBooted($booted)
209
    {
210
        $this->booted = $booted;
211
    }
212
213
    /**
214
     * Load and boot all of the remaining deferred providers.
215
     *
216
     * @return void
217
     */
218
    public function loadDeferredProviders()
219
    {
220
        // We will simply spin through each of the deferred providers and register each
221
        // one and boot them if the application has booted. This should make each of
222
        // the remaining services available to this application for immediate use.
223
        foreach ($this->deferredServices as $service => $provider) {
224
            $this->loadDeferredProvider($service);
225
        }
226
        $this->deferredServices = [];
227
    }
228
229
    /**
230
     * Load the provider for a deferred service.
231
     *
232
     * @param  string $service
233
     * @return void
234
     */
235
    public function loadDeferredProvider($service)
236
    {
237
        if (!isset($this->deferredServices[$service])) {
238
            return;
239
        }
240
        $provider = $this->deferredServices[$service];
241
242
        // If the service provider has not already been loaded and registered we can
243
        // register it with the application and remove the service from this list
244
        // of deferred services, since it will already be loaded on subsequent.
245
        if (!isset($this->registeredProviders[$provider])) {
246
            $this->registerDeferredProvider($provider, $service);
247
        }
248
    }
249
250
    /**
251
     * Register a deferred provider and service.
252
     *
253
     * @param  string $provider
254
     * @param  string $service
255
     * @return void
256
     */
257
    public function registerDeferredProvider($provider, $service = null)
258
    {
259
        // Once the provider that provides the deferred service has been registered we
260
        // will remove it from our local list of the deferred services with related
261
        // providers so that this container does not try to resolve it out again.
262
        if ($service) {
263
            unset($this->deferredServices[$service]);
264
        }
265
        $this->registerProvider($instance = new $provider($this));
266
    }
267
}
268