Failed Conditions
Branch refactor/kernels (3a00e4)
by Atanas
01:41
created

Application   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 312
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 96
dl 0
loc 312
ccs 59
cts 59
cp 1
rs 9.76
c 0
b 0
f 0
wmc 33

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
A resolve() 0 8 2
A facade() 0 2 1
A loadConfig() 0 4 1
A verifyBootstrap() 0 3 2
A instantiate() 0 14 3
A debugging() 0 4 2
A isBootstrapped() 0 2 1
A loadServiceProviders() 0 16 2
A routes() 0 21 4
A loadRoutes() 0 7 2
A getContainer() 0 2 1
A bootstrapServiceProviders() 0 3 2
A registerServiceProviders() 0 3 2
A bootstrap() 0 15 3
A renderConfigurationExceptions() 0 14 3
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 Closure;
13
use Pimple\Container;
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\Request;
24
use WPEmerge\Requests\RequestsServiceProvider;
25
use WPEmerge\Responses\ResponsesServiceProvider;
26
use WPEmerge\Routing\RoutingServiceProvider;
27
use WPEmerge\ServiceProviders\ServiceProviderInterface;
28
use WPEmerge\Support\AliasLoader;
29
use WPEmerge\View\ViewServiceProvider;
30
31
/**
32
 * Main communication channel with the application.
33
 */
34
class Application {
35
	/**
36
	 * IoC container.
37
	 *
38
	 * @var Container
39
	 */
40
	protected $container = null;
41
42
	/**
43
	 * Flag whether to intercept and render configuration exceptions.
44
	 *
45
	 * @var boolean
46
	 */
47
	protected $render_configuration_exceptions = true;
48
49
	/**
50
	 * Flag whether the application has been bootstrapped.
51
	 *
52
	 * @var boolean
53
	 */
54
	protected $bootstrapped = false;
55
56
	/**
57
	 * Array of application service providers.
58
	 *
59
	 * @var string[]
60
	 */
61
	protected $service_providers = [
62
		KernelsServiceProvider::class,
63
		ExceptionsServiceProvider::class,
64
		RequestsServiceProvider::class,
65
		ResponsesServiceProvider::class,
66
		RoutingServiceProvider::class,
67
		ViewServiceProvider::class,
68
		ControllersServiceProvider::class,
69 1
		CsrfServiceProvider::class,
70 1
		FlashServiceProvider::class,
71
		OldInputServiceProvider::class,
72 1
	];
73 1
74 1
	/**
75 1
	 * Constructor.
76 1
	 *
77 1
	 * @param Container $container
78
	 * @param boolean   $render_configuration_exceptions
79
	 */
80
	public function __construct( Container $container, $render_configuration_exceptions = true ) {
81
		$this->container = $container;
82
		$this->render_configuration_exceptions = $render_configuration_exceptions;
83
84 1
		$config = isset( $container[ WPEMERGE_CONFIG_KEY ] ) ? $container[ WPEMERGE_CONFIG_KEY ] : [];
85 1
		$config = array_merge( [
86 1
			'providers' => [],
87 1
		], $config );
88
		$container[ WPEMERGE_CONFIG_KEY ] = $config;
89
	}
90
91
	/**
92
	 * Get whether WordPress is in debug mode.
93
	 *
94
	 * @return boolean
95 1
	 */
96 1
	public function debugging() {
97
		$debugging = ( defined( 'WP_DEBUG' ) && WP_DEBUG );
98
		$debugging = apply_filters( 'wpemerge.debug', $debugging );
99
		return $debugging;
100
	}
101
102
	/**
103
	 * Get whether the application has been bootstrapped.
104
	 *
105 5
	 * @return boolean
106 5
	 */
107 2
	public function isBootstrapped() {
108
		return $this->bootstrapped;
109 4
	}
110
111
	/**
112
	 * Throw an exception if the application has not been bootstrapped.
113
	 *
114
	 * @return void
115
	 */
116 1
	protected function verifyBootstrap() {
117 1
		if ( ! $this->isBootstrapped() ) {
118
			throw new ConfigurationException( static::class . ' must be bootstrapped first.' );
119
		}
120
	}
121
122
	/**
123
	 * Get the IoC container instance.
124
	 *
125
	 * @return Container
126
	 */
127
	public function getContainer() {
128
		return $this->container;
129 4
	}
130 4
131 1
	/**
132
	 * Bootstrap the application.
133
	 * WordPress' 'after_setup_theme' action is a good place to call this.
134 4
	 *
135 4
	 * @param  array   $config
136 4
	 * @param  boolean $run
137
	 * @return void
138 4
	 */
139
	public function bootstrap( $config = [], $run = true ) {
140 4
		$this->renderConfigurationExceptions( function () use ( $config, $run ) {
141 1
			if ( $this->isBootstrapped() ) {
142 1
				throw new ConfigurationException( static::class . ' already bootstrapped.' );
143 1
			}
144 4
145
			$container = $this->getContainer();
146
			$this->loadConfig( $container, $config );
147
			$this->loadServiceProviders( $container );
148
149
			$this->bootstrapped = true;
150
151
			if ( $run ) {
152
				$kernel = $this->resolve( WPEMERGE_WORDPRESS_HTTP_KERNEL_KEY );
153
				$kernel->bootstrap();
154
			}
155
		} );
156
	}
157
158
	/**
159
	 * Load config into the service container.
160
	 *
161
	 * @codeCoverageIgnore
162
	 * @param  Container $container
163
	 * @param  array     $config
164
	 * @return void
165
	 */
166
	protected function loadConfig( Container $container, $config ) {
167
		$container[ WPEMERGE_CONFIG_KEY ] = array_merge(
168
			$container[ WPEMERGE_CONFIG_KEY ],
169
			$config
170
		);
171
	}
172
173
	/**
174
	 * Register and bootstrap all service providers.
175
	 *
176
	 * @codeCoverageIgnore
177
	 * @param  Container $container
178
	 * @return void
179
	 */
180
	protected function loadServiceProviders( Container $container ) {
181
		$container[ WPEMERGE_SERVICE_PROVIDERS_KEY ] = array_merge(
182
			$this->service_providers,
183
			$container[ WPEMERGE_CONFIG_KEY ]['providers']
184
		);
185
186
		$service_providers = array_map( function ( $service_provider ) {
187
			if ( ! is_subclass_of( $service_provider, ServiceProviderInterface::class ) ) {
188
				throw new ConfigurationException( 'The following class does not implement ServiceProviderInterface: ' . $service_provider );
189
			}
190
191
			return new $service_provider();
192
		}, $container[ WPEMERGE_SERVICE_PROVIDERS_KEY ] );
193 1
194 1
		$this->registerServiceProviders( $service_providers, $container );
195 1
		$this->bootstrapServiceProviders( $service_providers, $container );
196 1
	}
197 1
198
	/**
199
	 * Register all service providers.
200
	 *
201
	 * @param  array<\WPEmerge\ServiceProviders\ServiceProviderInterface> $service_providers
202
	 * @param  Container                                                  $container
203
	 * @return void
204
	 */
205
	protected function registerServiceProviders( $service_providers, Container $container ) {
206 1
		foreach ( $service_providers as $provider ) {
207 1
			$provider->register( $container );
208 1
		}
209 1
	}
210 1
211
	/**
212
	 * Bootstrap all service providers.
213
	 *
214
	 * @param  array<\WPEmerge\ServiceProviders\ServiceProviderInterface> $service_providers
215
	 * @param  Container                                                  $container
216
	 * @return void
217
	 */
218
	protected function bootstrapServiceProviders( $service_providers, Container $container ) {
219 1
		foreach ( $service_providers as $provider ) {
220 1
			$provider->bootstrap( $container );
221 1
		}
222
	}
223
224
	/**
225
	 * Register a facade class.
226
	 *
227
	 * @param  string $alias
228
	 * @param  string $facade_class
229 2
	 * @return void
230 2
	 */
231
	public function facade( $alias, $facade_class ) {
232 2
		AliasLoader::getInstance()->alias( $alias, $facade_class );
233 1
	}
234
235
	/**
236 1
	 * Resolve a dependency from the IoC container.
237
	 *
238
	 * @param  string     $key
239
	 * @return mixed|null
240
	 */
241
	public function resolve( $key ) {
242
		$this->verifyBootstrap();
243
244
		if ( ! isset( $this->getContainer()[ $key ] ) ) {
245
			return null;
246 3
		}
247 3
248
		return $this->getContainer()[ $key ];
249 3
	}
250
251 3
	/**
252 2
	 * Create and return a class instance.
253 1
	 *
254
	 * @throws ClassNotFoundException
255
	 * @param  string $class
256 1
	 * @return object
257 1
	 */
258
	public function instantiate( $class ) {
259 2
		$this->verifyBootstrap();
260
261
		$instance = $this->resolve( $class );
262
263
		if ( $instance === null ) {
264
			if ( ! class_exists( $class ) ) {
265
				throw new ClassNotFoundException( 'Class not found: ' . $class );
266
			}
267
268
			$instance = new $class();
269
		}
270
271
		return $instance;
272
	}
273
274
	/**
275
	 * Load a route definition file, applying middleware to all routes defined within.
276
	 *
277
	 * @codeCoverageIgnore
278
	 * @param  string               $file
279
	 * @param  array<string, mixed> $default_attributes
280
	 * @param  array<string, mixed> $attributes
281
	 * @return void
282
	 */
283
	protected function loadRoutes( $file, $attributes ) {
284
		if ( empty( $file ) ) {
285
			return;
286
		}
287
288
		$this->renderConfigurationExceptions( function () use ( $attributes, $file ) {
289
			Route::attributes( $attributes )->group( $file );
290
		} );
291
	}
292
293
	/**
294
	 * Load route definition files according to the current request.
295
	 *
296
	 * @codeCoverageIgnore
297
	 * @param  string $web
298
	 * @param  string $admin
299
	 * @param  string $ajax
300
	 * @return void
301
	 */
302
	public function routes( $web = '', $admin = '', $ajax = '' ) {
303
		if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
304
			$this->loadRoutes( $ajax, [
305
				'namespace' => '\\App\\Controllers\\Ajax\\',
306
				'middleware' => ['ajax'],
307
			] );
308
			return;
309
		}
310
311
		if ( is_admin() ) {
312
			$this->loadRoutes( $admin, [
313
				'namespace' => '\\App\\Controllers\\Admin\\',
314
				'middleware' => ['admin'],
315
			] );
316
			return;
317
		}
318
319
		$this->loadRoutes( $web, [
320
			'namespace' => '\\App\\Controllers\\Web\\',
321
			'handler' => '\\WPEmerge\\Controllers\\WordPressController@handle',
322
			'middleware' => ['web'],
323
		] );
324
	}
325
326
	/**
327
	 * Catch any configuration exceptions and short-circuit to an error page.
328
	 *
329
	 * @param  Closure $action
330
	 * @return void
331
	 */
332
	protected function renderConfigurationExceptions( Closure $action ) {
333
		try {
334
			$action();
335
		} catch ( ConfigurationException $exception ) {
336
			if ( ! $this->render_configuration_exceptions ) {
337
				throw $exception;
338
			}
339
340
			$request = Request::fromGlobals();
341
			$handler = $this->resolve( WPEMERGE_EXCEPTIONS_CONFIGURATION_ERROR_HANDLER_KEY );
342
343
			add_filter( 'wpemerge.pretty_errors.apply_admin_styles', '__return_false' );
344
			Response::respond( $handler->getResponse( $request, $exception ) );
345
			wp_die();
346
		}
347
	}
348
}
349