Issues (30)

src/ServiceProviders/ProviderRepository.php (5 issues)

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
11
/**
12
 * Class ServiceProviderAggregate
13
 * @package Nip\Container\ServiceProvider
14
 */
15
class ProviderRepository implements ProviderRepositoryInterface
16
{
17
    use ContainerAwareTrait;
18
19
    /**
20
     * @var array
21
     */
22
    protected $services = [];
23
24
    /**
25
     * @var AbstractServiceProvider[]
26
     */
27
    protected $providers = [];
28
29
    /**
30
     * @var array Array of registered providers class names
31
     */
32
    protected $registeredProviders = [];
33
34
    /**
35
     * The deferred services and their providers.
36
     *
37
     * @var array
38
     */
39
    protected $deferredServices = [];
40
41
    /**
42
     * Indicates if the application has "booted".
43
     *
44
     * @var bool
45
     */
46
    protected $booted = false;
47
48
    /**
49
     * Adds a new Service Provider
50
     * {@inheritdoc}
51
     */
52
    public function add($provider)
53 2
    {
54
        if (is_string($provider) && class_exists($provider)) {
55 2
            $provider = $this->resolveProvider($provider);
56 1
        }
57
58
        if ($provider instanceof ContainerAwareInterface) {
59 2
            $provider->setContainer($this->getContainer());
60 2
        }
61
62
        if ($provider instanceof ServiceProviderInterface) {
63 2
            foreach ($provider->provides() as $service) {
64 2
                $this->services[$service] = get_class($provider);
65 2
            }
66
            $this->providers[] = $provider;
67 2
            return $this;
68 2
        }
69
70
        throw new \InvalidArgumentException(
71
            'A service provider must be a fully qualified class name or instance ' .
72
            'of (\Nip\Container\ServiceProvider\ServiceProviderInterface)'
73
        );
74
    }
75
76
    /**
77
     * Resolve a service provider instance from the class name.
78
     *
79
     * @param  string $provider
80
     * @return AbstractServiceProvider
81
     */
82
    public function resolveProvider($provider)
83 1
    {
84
        return new $provider($this);
85 1
    }
86
87
    public function register()
88
    {
89
        foreach ($this->providers as $provider) {
90
            $this->registerProvider($provider);
91
        }
92
    }
93
94
    /**
95
     * Register a service provider with the application.
96
     *
97
     * @param  AbstractServiceProvider|string $provider
98
     * @return AbstractServiceProvider
99
     */
100
    public function registerProvider($provider)
101
    {
102
        if (($provider = $this->getProvider($provider))) {
103
        } elseif (is_string($provider)) {
0 ignored issues
show
The condition is_string($provider) is always false.
Loading history...
104
            $provider = $this->resolveProvider($provider);
105
        }
106
107
        if ($this->registeredProvider($provider)) {
108
            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...
109
        }
110
111
        if (method_exists($provider, 'register')) {
112
            $provider->register();
113
        }
114
115
        $this->markAsRegistered($provider);
116
117
        // If the application has already booted, we will call this boot method on
118
        // the provider class so it has an opportunity to do its boot logic and
119
        // will be ready for any usage by this developer's application logic.
120
        if ($this->booted) {
121
            $this->bootProvider($provider);
0 ignored issues
show
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

121
            $this->bootProvider(/** @scrutinizer ignore-type */ $provider);
Loading history...
122
        }
123
        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...
124
    }
125
126
    /**
127
     * Get the registered service provider instance if it exists.
128
     *
129
     * @param  AbstractServiceProvider|string $provider
130
     * @return AbstractServiceProvider|null
131
     */
132
    public function getProvider($provider)
133 3
    {
134
        $name = is_string($provider) ? $provider : get_class($provider);
135 3
136
        foreach ($this->providers as $key => $value) {
137 2
            if ($value instanceof $name) {
138 3
                return $value;
139
            }
140
        }
141
        return null;
142
    }
143
144
    /**
145
     * @param AbstractServiceProvider $provider
146
     * @return bool
147
     */
148
    public function registeredProvider($provider)
149
    {
150
        $providerClass = get_class($provider);
151
        if (isset($this->registeredProviders[$providerClass]) && $this->registeredProviders[$providerClass] === true) {
152
            return true;
153
        }
154
        return false;
155
    }
156
157
    /**
158
     * Mark the given provider as registered.
159
     *
160
     * @param  AbstractServiceProvider $provider
161
     * @return void
162
     */
163
    protected function markAsRegistered($provider)
164
    {
165
        $this->registeredProviders[get_class($provider)] = true;
166
    }
167
168
    /**
169
     * Boot the given service provider.
170
     *
171
     * @param  AbstractServiceProvider $provider
172
     * @return mixed
173
     */
174
    protected function bootProvider(AbstractServiceProvider $provider)
175
    {
176
        if ($provider instanceof BootableServiceProviderInterface) {
177
            return $provider->boot();
0 ignored issues
show
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...
178
        }
179
        return null;
180
    }
181
    
182
    /**
183
     * @return array
184
     */
185
    public function getProviders(): array
186
    {
187
        return $this->providers;
188
    }
189
190
    /**
191
     * Check to see if the services is registered
192
     * {@inheritdoc}
193
     */
194
    public function provides($service)
195
    {
196
        return array_key_exists($service, $this->providers);
197
    }
198
199
    public function boot()
200
    {
201
        foreach ($this->providers as $provider) {
202
            $this->bootProvider($provider);
203
        }
204
205
        $this->setBooted(true);
206
    }
207
208
    /**
209
     * @param bool $booted
210
     */
211
    public function setBooted($booted)
212
    {
213
        $this->booted = $booted;
214
    }
215
216
    /**
217
     * Load and boot all of the remaining deferred providers.
218
     *
219
     * @return void
220
     */
221
    public function loadDeferredProviders()
222
    {
223
        // We will simply spin through each of the deferred providers and register each
224
        // one and boot them if the application has booted. This should make each of
225
        // the remaining services available to this application for immediate use.
226
        foreach ($this->deferredServices as $service => $provider) {
227
            $this->loadDeferredProvider($service);
228
        }
229
        $this->deferredServices = [];
230
    }
231
232
    /**
233
     * Load the provider for a deferred service.
234
     *
235
     * @param  string $service
236
     * @return void
237
     */
238
    public function loadDeferredProvider($service)
239
    {
240
        if (!isset($this->deferredServices[$service])) {
241
            return;
242
        }
243
        $provider = $this->deferredServices[$service];
244
245
        // If the service provider has not already been loaded and registered we can
246
        // register it with the application and remove the service from this list
247
        // of deferred services, since it will already be loaded on subsequent.
248
        if (!isset($this->registeredProviders[$provider])) {
249
            $this->registerDeferredProvider($provider, $service);
250
        }
251
    }
252
253
    /**
254
     * Register a deferred provider and service.
255
     *
256
     * @param  string $provider
257
     * @param  string $service
258
     * @return void
259
     */
260
    public function registerDeferredProvider($provider, $service = null)
261
    {
262
        // Once the provider that provides the deferred service has been registered we
263
        // will remove it from our local list of the deferred services with related
264
        // providers so that this container does not try to resolve it out again.
265
        if ($service) {
266
            unset($this->deferredServices[$service]);
267
        }
268
        $this->registerProvider($instance = new $provider($this));
269
    }
270
}
271