Passed
Branch refactor/kernels (f01954)
by Atanas
01:48
created

Application::loadRoutes()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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