Passed
Branch refactor/kernels (78e722)
by Atanas
01:44
created

Application::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

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