App_Factory::default_config_paths()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 16
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 20
rs 9.7333
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
	 * Gets the defined base path.
80
	 *
81
	 * @return string
82
	 * @since 1.4.0
83
	 */
84
	public function get_base_path(): string {
85
		return $this->base_path;
86
	}
87
88
	/**
89
	 * Sets the base view path.
90
	 *
91
	 * @since 1.4.0
92
	 * @param string $base_view_path
93
	 * @return self
94
	 */
95
	public function set_base_view_path( string $base_view_path ): self {
96
		$this->base_view_path = \trailingslashit( $base_view_path );
97
98
		// Set the view base path on the app.
99
		$this->app->set_view_path( $this->base_view_path );
100
101
		return $this;
102
	}
103
104
	/**
105
	 * Get the base view path.
106
	 *
107
	 * @since 1.4.0
108
	 * @return string
109
	 */
110
	public function get_base_view_path(): string {
111
		return null !== $this->base_view_path
112
			? $this->base_view_path
113
			: \trailingslashit( $this->default_config_paths()['path']['view'] );
114
	}
115
116
	/**
117
	 * Pre populates a standard instance of the App
118
	 *
119
	 * THIS WAS REPLACED IN 1.4.0
120
	 * ASSUMES THE VIEW BASE PATH IS THE SAME AS THE BASE PATH
121
	 * THIS IS KEPT FOR BACKWARDS COMPATIBILITY
122
	 * @infection-ignore-all
123
	 * @return self
124
	 */
125
	public function with_wp_dice( bool $include_default_rules = false ): self {
126
		// If the view path is not set, set it to the same as base path.
127
		if ( null === $this->base_view_path ) {
128
			$this->base_view_path = $this->base_path;
129
		}
130
		return $this->default_setup( $include_default_rules );
131
	}
132
133
	/**
134
	 * Pre populates a standard instance of the App
135
	 * Uses the PinkCrab_Dice container
136
	 * Sets up registration and loader instances.
137
	 * Adds Hookable Middleware
138
	 *
139
	 * Just requires Class List, Config and DI Rules.
140
	 * @since 1.4.0
141
	 *
142
	 * @param bool $include_default_rules
143
	 * @return self
144
	 */
145
	public function default_setup( bool $include_default_rules = true ): self {
146
		$loader = new Hook_Loader();
147
148
		// Setup DI Container
149
		$container = PinkCrab_Dice::withDice( new Dice() );
150
151
		if ( $include_default_rules === true ) {
152
			$container->addRules( $this->default_di_rules() );
153
		}
154
155
		$this->app->set_container( $container );
156
		$this->app->set_loader( $loader );
157
158
		// Set registration middleware
159
		$module_manager = new Module_Manager( $container, new Registration_Service( $container ) );
160
		$module_manager->push_module( Hookable_Module::class );
161
162
		// Push any modules that have been added before the module manager was set.
163
		foreach ( $this->modules as $module ) {
164
			$module_manager->push_module( $module[0], $module[1] );
165
		}
166
167
		$this->app->set_module_manager( $module_manager );
168
169
		return $this;
170
	}
171
172
	/**
173
	 * Add a module to the application.
174
	 *
175
	 * @template Module_Instance of Module
176
	 * @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...
177
	 * @param ?callable(Module, ?Registration_Middleware):Module $callback
178
	 * @return self
179
	 */
180
	public function module( string $module, ?callable $callback = null ): self {
181
182
		// If the Module_Manager has been set in app, add the module to app.
183
		if ( $this->app->has_module_manager() ) {
184
			$this->app->module( $module, $callback );
185
			return $this;
186
		}
187
188
		$this->modules[] = array( $module, $callback );
189
		return $this;
190
	}
191
192
	/**
193
	 * Returns the basic DI rules which are used to set.
194
	 * Renderable with PHP_Engine implementation
195
	 *
196
	 * @return array<mixed>
197
	 */
198
	protected function default_di_rules(): array {
199
		return array(
200
			PHP_Engine::class         => array(
201
				'constructParams' => array(
202
					$this->get_base_view_path(),
203
				),
204
			),
205
			Renderable::class         => array(
206
				'instanceOf' => PHP_Engine::class,
207
				'shared'     => true,
208
			),
209
			Component_Compiler::class => array(
210
				'constructParams' => array( 'components' ),
211
			),
212
		);
213
	}
214
215
	/**
216
	 * Set the DI rules
217
	 *
218
	 * @param array<string,array<string,string|object|callable|null|false|\Closure>> $rules
219
	 * @return self
220
	 */
221
	public function di_rules( array $rules ): self {
222
		$this->app->container_config(
223
			function ( DI_Container $container ) use ( $rules ): void {
224
				$container->addRules( $rules );
225
			}
226
		);
227
		return $this;
228
	}
229
230
	/**
231
	 * Sets the registration class list.
232
	 *
233
	 * @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...
234
	 * @return self
235
	 */
236
	public function registration_classes( array $class_list ): self {
237
		$this->app->registration_classes( $class_list );
238
		return $this;
239
	}
240
241
	/**
242
	 * Sets the apps internal config
243
	 *
244
	 * @param array<string, mixed> $app_config
245
	 * @return self
246
	 */
247
	public function app_config( array $app_config ): self {
248
		$this->app->set_app_config( \array_replace_recursive( $this->default_config_paths(), $app_config ) );
249
		return $this;
250
	}
251
252
	/**
253
	 * Returns the populated app.
254
	 *
255
	 * @return \PinkCrab\Perique\Application\App
256
	 */
257
	public function app(): App {
258
		return $this->app;
259
	}
260
261
	/**
262
	 * Returns a booted version of the app.
263
	 *
264
	 * @return \PinkCrab\Perique\Application\App
265
	 */
266
	public function boot(): App {
267
		// Sets default settings if not already set.
268
		if ( ! $this->app->has_app_config() ) {
269
			$this->app_config( $this->default_config_paths() );
270
		}
271
272
		return $this->app->boot();
273
	}
274
275
	/**
276
	 * Generates some default paths for the app_config based on base path.
277
	 *
278
	 * @return array{
279
	 *  url:array{
280
	 *    plugin:string,
281
	 *    view:string,
282
	 *    assets:string,
283
	 *    upload_root:string,
284
	 *    upload_current:string,
285
	 *  },
286
	 *  path:array{
287
	 *    plugin:string,
288
	 *    view:string,
289
	 *    assets:string,
290
	 *    upload_root:string,
291
	 *    upload_current:string,
292
	 *  }
293
	 * }
294
	 */
295
	private function default_config_paths(): array {
296
		$wp_uploads = \wp_upload_dir();
297
298
		$base_path = App_Config_Path_Helper::normalise_path( $this->base_path );
299
		$view_path = $this->base_view_path ?? App_Config_Path_Helper::assume_view_path( $base_path );
300
301
		return array(
302
			'path' => array(
303
				'plugin'         => $base_path,
304
				'view'           => $view_path,
305
				'assets'         => $base_path . \DIRECTORY_SEPARATOR . 'assets',
306
				'upload_root'    => $wp_uploads['basedir'],
307
				'upload_current' => $wp_uploads['path'],
308
			),
309
			'url'  => array(
310
				'plugin'         => App_Config_Path_Helper::assume_base_url( $base_path ),
311
				'view'           => App_Config_Path_Helper::assume_view_url( $base_path, $view_path ),
312
				'assets'         => App_Config_Path_Helper::assume_base_url( $base_path ) . '/assets',
313
				'upload_root'    => $wp_uploads['baseurl'],
314
				'upload_current' => $wp_uploads['url'],
315
			),
316
		);
317
	}
318
}
319