Core::boot()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 12
rs 9.4285
1
<?php
2
declare(strict_types=1);
3
4
namespace Spires\Core;
5
6
use Spires\Container\Container;
7
use Spires\Contracts\Core\Core as CoreContract;
8
use Spires\Contracts\Core\UndefinedConfigKeyException;
9
10
class Core extends Container implements CoreContract
11
{
12
    /**
13
     * @var string
14
     */
15
    const VERSION = '0.1.1';
16
17
    /**
18
     * @var string
19
     */
20
    protected $basePath;
21
22
    /**
23
     * @var bool
24
     */
25
    protected $hasBeenBootstrapped = false;
26
27
    /**
28
     * @var bool
29
     */
30
    protected $booted = false;
31
32
    /**
33
     * @var array
34
     */
35
    protected $serviceProviders = [];
36
37
    /**
38
     * @var array
39
     */
40
    protected $loadedProviders = [];
41
42
    /**
43
     * @var array
44
     */
45
    protected $plugins = [];
46
47
    /**
48
     * @param  string|null $basePath
49
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
50
     */
51
    public function __construct($basePath = null)
52
    {
53
        $this->registerBaseBindings();
54
55
        if ($basePath) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $basePath of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
56
            $this->setBasePath($basePath);
57
        }
58
    }
59
60
    /**
61
     * @return string
62
     */
63
    public function version()
64
    {
65
        return static::VERSION;
66
    }
67
68
    /**
69
     * @return string
70
     */
71
    public function basePath()
72
    {
73
        return $this->basePath;
74
    }
75
76
    /**
77
     * @param  \Spires\Core\ServiceProvider|string $provider
78
     * @param  array $config
79
     * @param  bool $force
80
     * @return ServiceProvider
81
     */
82
    public function register($provider, array $config = [], $force = false)
83
    {
84
        if (($registered = $this->getProvider($provider)) && !$force) {
85
            return $registered;
86
        }
87
88
        // If the given "provider" is a string, we will resolve it, passing in the
89
        // application instance automatically for the developer. This is simply
90
        // a more convenient way of specifying your service provider classes.
91
        if (is_string($provider)) {
92
            $provider = $this->resolveProviderClass($provider);
93
        }
94
95
        $this->registerConfig($provider, $config);
96
97
        $this->registerProvider($provider);
98
99
        $this->markAsRegistered($provider);
100
101
        $this->addPlugins($provider);
102
103
        // If the application has already booted, we will call this boot method on
104
        // the provider class so it has an opportunity to do its boot logic and
105
        // will be ready for any usage by the developer's application logics.
106
        if ($this->booted) {
107
            $this->bootProvider($provider);
108
        }
109
110
        return $provider;
111
    }
112
113
    /**
114
     * Register all of the base service providers.
115
     *
116
     * @return void
117
     */
118
    public function registerBaseServiceProviders()
119
    {
120
        $this->register(\Spires\Plugins\SystemMessage\ServiceProvider::class);
121
        $this->register(\Spires\Plugins\ChannelOperations\ServiceProvider::class);
122
        $this->register(\Spires\Plugins\PingPong\ServiceProvider::class);
123
        $this->register(\Spires\Plugins\Message\ServiceProvider::class);
124
        $this->register(\Spires\Plugins\BangMessage\ServiceProvider::class);
125
    }
126
127
    /**
128
     * Get the service providers that have been loaded.
129
     *
130
     * @return array
131
     */
132
    public function getLoadedProviders()
133
    {
134
        return $this->loadedProviders;
135
    }
136
137
    /**
138
     * Get all plugins.
139
     *
140
     * @return array
141
     */
142
    public function getPlugins()
143
    {
144
        return $this->plugins;
145
    }
146
147
    /**
148
     * Boot the application's service providers.
149
     *
150
     * @return void
151
     */
152
    public function boot()
153
    {
154
        if ($this->booted) {
155
            return;
156
        }
157
158
        array_walk($this->serviceProviders, function ($provider) {
159
            $this->bootProvider($provider);
160
        });
161
162
        $this->booted = true;
163
    }
164
165
    /**
166
     * Determine if the application has booted.
167
     *
168
     * @return bool
169
     */
170
    public function isBooted()
171
    {
172
        return $this->booted;
173
    }
174
175
    /**
176
     * Register the basic bindings into the container.
177
     *
178
     * @return void
179
     */
180
    protected function registerBaseBindings()
181
    {
182
        static::setInstance($this);
183
        $this->instance(\Spires\Contracts\Core\Core::class, $this);
184
        $this->instance('core', $this);
185
        $this->instance(Container::class, $this);
186
    }
187
188
    /**
189
     * Set the base path for the application.
190
     *
191
     * @param  string $basePath
192
     * @return $this
193
     */
194
    protected function setBasePath($basePath)
195
    {
196
        $this->basePath = rtrim($basePath, '\/');
197
198
        $this->bindPathsInContainer();
199
200
        return $this;
201
    }
202
203
    /**
204
     * Bind all of the application paths in the container.
205
     *
206
     * @return void
207
     */
208
    protected function bindPathsInContainer()
209
    {
210
        $this->instance('path.base', $this->basePath());
211
    }
212
213
    /**
214
     * Get the registered service provider instance if it exists.
215
     *
216
     * @param  \Spires\Core\ServiceProvider|string $provider
217
     * @return \Spires\Core\ServiceProvider|null
218
     */
219
    protected function getProvider($provider)
220
    {
221
        $name = is_string($provider) ? $provider : get_class($provider);
222
223
        foreach ($this->serviceProviders as $key => $value) {
224
            if ($value instanceof $name) {
225
                return $value;
226
            }
227
        }
228
229
        return null;
230
    }
231
232
    /**
233
     * Resolve a service provider instance from the class name.
234
     *
235
     * @param  string $provider
236
     * @return \Spires\Core\ServiceProvider
237
     */
238
    protected function resolveProviderClass($provider)
239
    {
240
        return new $provider($this);
241
    }
242
243
    /**
244
     * Mark the given provider as registered.
245
     *
246
     * @param  \Spires\Core\ServiceProvider $provider
247
     * @return void
248
     */
249
    protected function markAsRegistered($provider)
250
    {
251
        $this->serviceProviders[] = $provider;
252
        $this->loadedProviders[get_class($provider)] = true;
253
    }
254
255
    /**
256
     * Add plugins from the service provider.
257
     *
258
     * @param  \Spires\Core\ServiceProvider $provider
259
     * @return void
260
     */
261
    protected function addPlugins($provider)
262
    {
263
        foreach ($provider->plugins() as $plugin) {
264
            $this->plugins[$plugin] = $this->make($plugin);
265
        }
266
    }
267
268
    /**
269
     * Register the given service provider.
270
     *
271
     * @param  \Spires\Core\ServiceProvider $provider
272
     * @param  array $config
273
     * @return mixed
274
     *
275
     * @throws \Spires\Contracts\Core\UndefinedConfigKeyException
276
     */
277
    protected function registerConfig(ServiceProvider $provider, array $config)
278
    {
279
        $default = $provider->config();
280
281
        if ($undefined = array_keys(array_diff_key($config, $default))) {
282
            throw new UndefinedConfigKeyException(
283
                'Undefined config keys passed to provider: [' . implode(', ', $undefined) . ']'
284
            );
285
        }
286
287
        foreach (array_merge($default, $config) as $key => $value) {
288
            $this->bind($key, function () use ($value) {
289
                return $value;
290
            });
291
        }
292
    }
293
294
    /**
295
     * Register the given service provider.
296
     *
297
     * @param  \Spires\Core\ServiceProvider $provider
298
     * @return mixed
299
     */
300
    protected function registerProvider(ServiceProvider $provider)
301
    {
302
        if (method_exists($provider, 'register')) {
303
            return $provider->register();
0 ignored issues
show
Documentation Bug introduced by
The method register does not exist on object<Spires\Core\ServiceProvider>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
304
        }
305
    }
306
307
    /**
308
     * Boot the given service provider.
309
     *
310
     * @param  \Spires\Core\ServiceProvider $provider
311
     * @return mixed
312
     */
313
    protected function bootProvider(ServiceProvider $provider)
314
    {
315
        if (method_exists($provider, 'boot')) {
316
            return $this->call([$provider, 'boot']);
317
        }
318
    }
319
}
320