Completed
Push — master ( 8e30a2...9d7a2c )
by Glynn
20s queued 12s
created

App_Factory::construct_registration_middleware()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
/**
5
 * Factory for creating standard instances of the App.
6
 *
7
 * @author Glynn Quelch <[email protected]>
8
 * @license http://www.opensource.org/licenses/mit-license.html  MIT License
9
 * @package PinkCrab\Perique
10
 * @since 0.4.0
11
 */
12
13
namespace PinkCrab\Perique\Application;
14
15
use Dice\Dice;
16
use PinkCrab\Loader\Hook_Loader;
17
use PinkCrab\Perique\Application\App;
18
use PinkCrab\Perique\Interfaces\Module;
19
use PinkCrab\Perique\Interfaces\Renderable;
20
use PinkCrab\Perique\Interfaces\DI_Container;
21
use PinkCrab\Perique\Services\View\PHP_Engine;
22
use PinkCrab\Perique\Services\Dice\PinkCrab_Dice;
23
use PinkCrab\Perique\Utils\App_Config_Path_Helper;
24
use PinkCrab\Perique\Interfaces\Registration_Middleware;
25
use PinkCrab\Perique\Services\Registration\Module_Manager;
26
use PinkCrab\Perique\Services\Registration\Registration_Service;
27
use PinkCrab\Perique\Services\View\Component\Component_Compiler;
28
use PinkCrab\Perique\Services\Registration\Modules\Hookable_Module;
29
30
class App_Factory {
31
32
	/**
33
	 * The app instance.
34
	 *
35
	 * @var App
36
	 */
37
	protected App $app;
38
39
	/**
40
	 * The base path of the app.
41
	 *
42
	 * @var string
43
	 */
44
	protected string $base_path;
45
46
	/**
47
	 * The base view path
48
	 *
49
	 * @var string|null
50
	 * @since 1.4.0
51
	 */
52
	protected ?string $base_view_path = null;
53
54
	/**
55
	 * Modules
56
	 *
57
	 * @since 2.0.0
58
	 * @var array{
59
	 *  0:class-string<Module>,
60
	 *  1:?callable(Module, ?Registration_Middleware):Module
61
	 * }[]
62
	 */
63
	protected array $modules = array();
64
65
	public function __construct( ?string $base_path = null ) {
66
67
		if ( null === $base_path ) {
68
			$file_index      = 0;
69
			$trace           = debug_backtrace(); //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
70
			$this->base_path = isset( $trace[ $file_index ]['file'] ) ? \trailingslashit( dirname( $trace[ $file_index ]['file'] ) ) : __DIR__;
71
		} else {
72
			$this->base_path = \trailingslashit( $base_path );
73
		}
74
75
		$this->app = new App( $this->base_path );
76
77
	}
78
79
		/**
80
	 * Gets the defined base path.
81
	 *
82
	 * @return string
83
	 * @since 1.4.0
84
	 */
85
	public function get_base_path(): string {
86
		return $this->base_path;
87
	}
88
89
	/**
90
	 * Sets the base view path.
91
	 *
92
	 * @since 1.4.0
93
	 * @param string $base_view_path
94
	 * @return self
95
	 */
96
	public function set_base_view_path( string $base_view_path ): self {
97
		$this->base_view_path = \trailingslashit( $base_view_path );
98
99
		// Set the view base path on the app.
100
		$this->app->set_view_path( $this->base_view_path );
101
102
		return $this;
103
	}
104
105
	/**
106
	 * Get the base view path.
107
	 *
108
	 * @since 1.4.0
109
	 * @return string
110
	 */
111
	public function get_base_view_path(): string {
112
		return null !== $this->base_view_path
113
			? $this->base_view_path
114
			: \trailingslashit( $this->default_config_paths()['path']['view'] );
115
	}
116
117
	/**
118
	 * Pre populates a standard instance of the App
119
	 *
120
	 * THIS WAS REPLACED IN 1.4.0
121
	 * ASSUMES THE VIEW BASE PATH IS THE SAME AS THE BASE PATH
122
	 * THIS IS KEPT FOR BACKWARDS COMPATIBILITY
123
	 * @infection-ignore-all
124
	 * @return self
125
	 */
126
	public function with_wp_dice( bool $include_default_rules = false ): self {
127
		// If the view path is not set, set it to the same as base path.
128
		if ( null === $this->base_view_path ) {
129
			$this->base_view_path = $this->base_path;
130
		}
131
		return $this->default_setup( $include_default_rules );
132
	}
133
134
	/**
135
	 * Pre populates a standard instance of the App
136
	 * Uses the PinkCrab_Dice container
137
	 * Sets up registration and loader instances.
138
	 * Adds Hookable Middleware
139
	 *
140
	 * Just requires Class List, Config and DI Rules.
141
	 * @since 1.4.0
142
	 *
143
	 * @param bool $include_default_rules
144
	 * @return self
145
	 */
146
	public function default_setup( bool $include_default_rules = true ): self {
147
		$loader = new Hook_Loader();
148
149
		// Setup DI Container
150
		$container = PinkCrab_Dice::withDice( new Dice() );
151
152
		if ( $include_default_rules === true ) {
153
			$container->addRules( $this->default_di_rules() );
154
		}
155
156
		$this->app->set_container( $container );
157
		$this->app->set_loader( $loader );
158
159
		// Set registration middleware
160
		$module_manager = new Module_Manager( $container, new Registration_Service( $container ) );
161
		$module_manager->push_module( Hookable_Module::class );
162
163
		// Push any modules that have been added before the module manager was set.
164
		foreach ( $this->modules as $module ) {
165
			$module_manager->push_module( $module[0], $module[1] );
166
		}
167
168
		$this->app->set_module_manager( $module_manager );
169
170
		return $this;
171
	}
172
173
	/**
174
	 * Add a module to the application.
175
	 *
176
	 * @template Module_Instance of Module
177
	 * @param class-string<Module_Instance> $module
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<Module_Instance> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<Module_Instance>.
Loading history...
178
	 * @param ?callable(Module, ?Registration_Middleware):Module $callback
179
	 * @return self
180
	 */
181
	public function module( string $module, ?callable $callback = null ): self {
182
183
		// If the Module_Manager has been set in app, add the module to app.
184
		if ( $this->app->has_module_manager() ) {
185
			$this->app->module( $module, $callback );
186
			return $this;
187
		}
188
189
		$this->modules[] = array( $module, $callback );
190
		return $this;
191
	}
192
193
	/**
194
	 * Returns the basic DI rules which are used to set.
195
	 * Renderable with PHP_Engine implementation
196
	 *
197
	 * @return array<mixed>
198
	 */
199
	protected function default_di_rules(): array {
200
		return array(
201
			PHP_Engine::class         => array(
202
				'constructParams' => array(
203
					$this->get_base_view_path(),
204
				),
205
			),
206
			Renderable::class         => array(
207
				'instanceOf' => PHP_Engine::class,
208
				'shared'     => true,
209
			),
210
			Component_Compiler::class => array(
211
				'constructParams' => array( 'components' ),
212
			),
213
		);
214
	}
215
216
	/**
217
	 * Set the DI rules
218
	 *
219
	 * @param array<string,array<string,string|object|callable|null|false|\Closure>> $rules
220
	 * @return self
221
	 */
222
	public function di_rules( array $rules ): self {
223
		$this->app->container_config(
224
			function( DI_Container $container ) use ( $rules ): void {
225
				$container->addRules( $rules );
226
			}
227
		);
228
		return $this;
229
	}
230
231
	/**
232
	 * Sets the registration class list.
233
	 *
234
	 * @param array<class-string> $class_list Array of fully namespaced class names.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string>.
Loading history...
235
	 * @return self
236
	 */
237
	public function registration_classes( array $class_list ): self {
238
		$this->app->registration_classes( $class_list );
239
		return $this;
240
	}
241
242
	/**
243
	 * Sets the apps internal config
244
	 *
245
	 * @param array<string, mixed> $app_config
246
	 * @return self
247
	 */
248
	public function app_config( array $app_config ): self {
249
		$this->app->set_app_config( \array_replace_recursive( $this->default_config_paths(), $app_config ) );
250
		return $this;
251
	}
252
253
	/**
254
	 * Returns the populated app.
255
	 *
256
	 * @return \PinkCrab\Perique\Application\App
257
	 */
258
	public function app(): App {
259
		return $this->app;
260
	}
261
262
	/**
263
	 * Returns a booted version of the app.
264
	 *
265
	 * @return \PinkCrab\Perique\Application\App
266
	 */
267
	public function boot(): App {
268
		// Sets default settings if not already set.
269
		if ( ! $this->app->has_app_config() ) {
270
			$this->app_config( $this->default_config_paths() );
271
		}
272
273
		return $this->app->boot();
274
	}
275
276
	/**
277
	 * Generates some default paths for the app_config based on base path.
278
	 *
279
	 * @return array{
280
	 *  url:array{
281
	 *    plugin:string,
282
	 *    view:string,
283
	 *    assets:string,
284
	 *    upload_root:string,
285
	 *    upload_current:string,
286
	 *  },
287
	 *  path:array{
288
	 *    plugin:string,
289
	 *    view:string,
290
	 *    assets:string,
291
	 *    upload_root:string,
292
	 *    upload_current:string,
293
	 *  }
294
	 * }
295
	 */
296
	private function default_config_paths(): array {
297
		$wp_uploads = \wp_upload_dir();
298
299
		$base_path = App_Config_Path_Helper::normalise_path( $this->base_path );
300
		$view_path = $this->base_view_path ?? App_Config_Path_Helper::assume_view_path( $base_path );
301
302
		return array(
303
			'path' => array(
304
				'plugin'         => $base_path,
305
				'view'           => $view_path,
306
				'assets'         => $base_path . \DIRECTORY_SEPARATOR . 'assets',
307
				'upload_root'    => $wp_uploads['basedir'],
308
				'upload_current' => $wp_uploads['path'],
309
			),
310
			'url'  => array(
311
				'plugin'         => App_Config_Path_Helper::assume_base_url( $base_path ),
312
				'view'           => App_Config_Path_Helper::assume_view_url( $base_path, $view_path ),
313
				'assets'         => App_Config_Path_Helper::assume_base_url( $base_path ) . '/assets',
314
				'upload_root'    => $wp_uploads['baseurl'],
315
				'upload_current' => $wp_uploads['url'],
316
			),
317
		);
318
	}
319
320
}
321