Failed Conditions
Branch refactor/kernels (fbf61a)
by Atanas
01:47
created

Application::loadServiceProviders()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 10
nc 1
nop 1
dl 0
loc 16
ccs 0
cts 0
cp 0
c 0
b 0
f 0
cc 2
crap 6
rs 9.9332
1
<?php
2
/**
3
 * @package   WPEmerge
4
 * @author    Atanas Angelov <[email protected]>
5
 * @copyright 2018 Atanas Angelov
6
 * @license   https://www.gnu.org/licenses/gpl-2.0.html GPL-2.0
7
 * @link      https://wpemerge.com/
8
 */
9
10
namespace WPEmerge\Application;
11
12
use Pimple\Container;
13
use Psr\Http\Message\ResponseInterface;
14
use WPEmerge\Controllers\ControllersServiceProvider;
15
use WPEmerge\Csrf\CsrfServiceProvider;
16
use WPEmerge\Exceptions\ConfigurationException;
17
use WPEmerge\Exceptions\ExceptionsServiceProvider;
18
use WPEmerge\Facades\Response;
19
use WPEmerge\Facades\Route;
20
use WPEmerge\Flash\FlashServiceProvider;
21
use WPEmerge\Input\OldInputServiceProvider;
22
use WPEmerge\Kernels\KernelsServiceProvider;
23
use WPEmerge\Requests\RequestsServiceProvider;
24
use WPEmerge\Responses\ResponsesServiceProvider;
25
use WPEmerge\Routing\RoutingServiceProvider;
26
use WPEmerge\ServiceProviders\ServiceProviderInterface;
27
use WPEmerge\Support\AliasLoader;
28
use WPEmerge\View\ViewServiceProvider;
29
30
/**
31
 * Main communication channel with the application.
32
 */
33
class Application {
34
	/**
35
	 * Flag whether the application has been bootstrapped.
36
	 *
37
	 * @var boolean
38
	 */
39
	protected $bootstrapped = false;
40
41
	/**
42
	 * IoC container.
43
	 *
44
	 * @var Container
45
	 */
46
	protected $container = null;
47
48
	/**
49
	 * Array of application service providers.
50
	 *
51
	 * @var string[]
52
	 */
53
	protected $service_providers = [
54
		KernelsServiceProvider::class,
55
		ExceptionsServiceProvider::class,
56
		RequestsServiceProvider::class,
57
		ResponsesServiceProvider::class,
58
		RoutingServiceProvider::class,
59
		ViewServiceProvider::class,
60
		ControllersServiceProvider::class,
61
		CsrfServiceProvider::class,
62
		FlashServiceProvider::class,
63
		OldInputServiceProvider::class,
64
	];
65
66
	/**
67
	 * Constructor.
68
	 *
69
	 * @param Container $container
70
	 */
71 1
	public function __construct( Container $container ) {
72 1
		$this->container = $container;
73
74 1
		$config = isset( $container[ WPEMERGE_CONFIG_KEY ] ) ? $container[ WPEMERGE_CONFIG_KEY ] : [];
75 1
		$config = array_merge( [
76 1
			'providers' => [],
77 1
		], $config );
78 1
		$container[ WPEMERGE_CONFIG_KEY ] = $config;
79 1
	}
80
81
	/**
82
	 * Get whether WordPress is in debug mode.
83
	 *
84
	 * @return boolean
85
	 */
86 1
	public function debugging() {
87 1
		$debugging = ( defined( 'WP_DEBUG' ) && WP_DEBUG );
88 1
		$debugging = apply_filters( 'wpemerge.debug', $debugging );
89 1
		return $debugging;
90
	}
91
92
	/**
93
	 * Get whether the application has been bootstrapped.
94
	 *
95
	 * @return boolean
96
	 */
97 1
	public function isBootstrapped() {
98 1
		return $this->bootstrapped;
99
	}
100
101
	/**
102
	 * Throw an exception if the application has not been bootstrapped.
103
	 *
104
	 * @throws ConfigurationException
105
	 * @return void
106
	 */
107 5
	protected function verifyBootstrap() {
108 5
		if ( ! $this->isBootstrapped() ) {
109 1
			throw new ConfigurationException( static::class . ' must be bootstrapped first.' );
110
		}
111 4
	}
112
113
	/**
114
	 * Get the IoC container instance.
115
	 *
116
	 * @return Container
117
	 */
118 1
	public function getContainer() {
119 1
		return $this->container;
120
	}
121
122
	/**
123
	 * Bootstrap the application.
124
	 * WordPress' 'after_setup_theme' action is a good place to call this.
125
	 *
126
	 * @throws ConfigurationException
127
	 * @param  array   $config
128
	 * @param  boolean $run
129
	 * @return void
130
	 */
131 3
	public function bootstrap( $config = [], $run = true ) {
132 3
		if ( $this->isBootstrapped() ) {
133 1
			throw new ConfigurationException( static::class . ' already bootstrapped.' );
134
		}
135
136 3
		$container = $this->getContainer();
137 3
		$this->loadConfig( $container, $config );
138 3
		$this->loadServiceProviders( $container );
139
140 3
		$this->bootstrapped = true;
141
142 3
		if ( $run ) {
143
			$kernel = $this->resolve( WPEMERGE_WORDPRESS_HTTP_KERNEL_KEY );
144
			$kernel->bootstrap();
145
		}
146 3
	}
147
148
	/**
149
	 * Load config into the service container.
150
	 *
151
	 * @codeCoverageIgnore
152
	 * @param  Container $container
153
	 * @param  array     $config
154
	 * @return void
155
	 */
156
	protected function loadConfig( Container $container, $config ) {
157
		$container[ WPEMERGE_CONFIG_KEY ] = array_merge(
158
			$container[ WPEMERGE_CONFIG_KEY ],
159
			$config
160
		);
161
	}
162
163
	/**
164
	 * Register and bootstrap all service providers.
165
	 *
166
	 * @codeCoverageIgnore
167
	 * @param  Container $container
168
	 * @return void
169
	 */
170
	protected function loadServiceProviders( Container $container ) {
171
		$container[ WPEMERGE_SERVICE_PROVIDERS_KEY ] = array_merge(
172
			$this->service_providers,
173
			$container[ WPEMERGE_CONFIG_KEY ]['providers']
174
		);
175
176
		$service_providers = array_map( function ( $service_provider ) {
177
			if ( ! is_subclass_of( $service_provider, ServiceProviderInterface::class ) ) {
178
				throw new ConfigurationException( 'The following class does not implement ServiceProviderInterface: ' . $service_provider );
179
			}
180
181
			return new $service_provider();
182
		}, $container[ WPEMERGE_SERVICE_PROVIDERS_KEY ] );
183
184
		$this->registerServiceProviders( $service_providers, $container );
185
		$this->bootstrapServiceProviders( $service_providers, $container );
186
	}
187
188
	/**
189
	 * Register all service providers.
190
	 *
191
	 * @param  array<\WPEmerge\ServiceProviders\ServiceProviderInterface> $service_providers
192
	 * @param  Container                                                  $container
193
	 * @return void
194
	 */
195 1
	protected function registerServiceProviders( $service_providers, Container $container ) {
196 1
		foreach ( $service_providers as $provider ) {
197 1
			$provider->register( $container );
198 1
		}
199 1
	}
200
201
	/**
202
	 * Bootstrap all service providers.
203
	 *
204
	 * @param  array<\WPEmerge\ServiceProviders\ServiceProviderInterface> $service_providers
205
	 * @param  Container                                                  $container
206
	 * @return void
207
	 */
208 1
	protected function bootstrapServiceProviders( $service_providers, Container $container ) {
209 1
		foreach ( $service_providers as $provider ) {
210 1
			$provider->bootstrap( $container );
211 1
		}
212 1
	}
213
214
	/**
215
	 * Register a facade class.
216
	 *
217
	 * @param  string $alias
218
	 * @param  string $facade_class
219
	 * @return void
220
	 */
221 1
	public function facade( $alias, $facade_class ) {
222 1
		AliasLoader::getInstance()->alias( $alias, $facade_class );
223 1
	}
224
225
	/**
226
	 * Resolve a dependency from the IoC container.
227
	 *
228
	 * @param  string     $key
229
	 * @return mixed|null
230
	 */
231 2
	public function resolve( $key ) {
232 2
		$this->verifyBootstrap();
233
234 2
		if ( ! isset( $this->getContainer()[ $key ] ) ) {
235 1
			return null;
236
		}
237
238 1
		return $this->getContainer()[ $key ];
239
	}
240
241
	/**
242
	 * Create and return a class instance.
243
	 *
244
	 * @throws ClassNotFoundException
245
	 * @param  string $class
246
	 * @return object
247
	 */
248 3
	public function instantiate( $class ) {
249 3
		$this->verifyBootstrap();
250
251 3
		$instance = $this->resolve( $class );
252
253 3
		if ( $instance === null ) {
254 2
			if ( ! class_exists( $class ) ) {
255 1
				throw new ClassNotFoundException( 'Class not found: ' . $class );
256
			}
257
258 1
			$instance = new $class();
259 1
		}
260
261 2
		return $instance;
262
	}
263
264
	/**
265
	 * Load a route definition file, applying middleware to all routes defined within.
266
	 *
267
	 * @codeCoverageIgnore
268
	 * @param  string        $file
269
	 * @param  array<string> $middleware
270
	 * @return void
271
	 */
272
	protected function loadRoutes( $file, $middleware = [] ) {
273
		if ( empty( $file ) ) {
274
			return;
275
		}
276
277
		Route::middleware( $middleware )->group( $file );
278
	}
279
280
	/**
281
	 * Load route definition files according to the current request.
282
	 *
283
	 * @codeCoverageIgnore
284
	 * @param  string $web
285
	 * @param  string $admin
286
	 * @param  string $ajax
287
	 * @return void
288
	 */
289
	public function routes( $web = '', $admin = '', $ajax = '' ) {
290
		if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
291
			$this->loadRoutes( $ajax, ['ajax'] );
292
			return;
293
		}
294
295
		if ( is_admin() ) {
296
			$this->loadRoutes( $admin, ['admin'] );
297
			return;
298
		}
299
300
		$this->loadRoutes( $web, ['web'] );
301
	}
302
}
303