Module_Manager::create_module()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 5
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 11
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Module loader
7
 *
8
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
9
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
10
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
11
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
12
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
13
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
14
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
15
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
16
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
17
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
18
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19
 *
20
 * @author Glynn Quelch <[email protected]>
21
 * @license http://www.opensource.org/licenses/mit-license.html  MIT License
22
 * @package PinkCrab\Perique\Module
23
 * @since 2.0.0
24
 */
25
26
namespace PinkCrab\Perique\Services\Registration;
27
28
use Exception;
29
use PinkCrab\Loader\Hook_Loader;
30
use PinkCrab\Perique\Application\Hooks;
31
use PinkCrab\Perique\Interfaces\Module;
32
use PinkCrab\Perique\Interfaces\DI_Container;
33
use PinkCrab\Perique\Interfaces\Inject_Hook_Loader;
34
use PinkCrab\Perique\Interfaces\Registration_Middleware;
35
use PinkCrab\Perique\Exceptions\Module_Manager_Exception;
36
use PinkCrab\Perique\Services\Registration\Registration_Service;
37
38
final class Module_Manager {
39
40
	/**
41
	 * Modules
42
	 *
43
	 * @since 2.0.0
44
	 * @var array{
45
	 *  0:class-string<Module>,
46
	 *  1:?callable(Module, ?Registration_Middleware):Module
47
	 * }[]
48
	 */
49
	protected array $modules = array();
50
51
	/**
52
	 * Access to the DI Container
53
	 *
54
	 * @var DI_Container
55
	 */
56
	private DI_Container $di_container;
57
58
	/**
59
	 * Manages all the Registration Middleware.
60
	 *
61
	 * @var Registration_Service
62
	 */
63
	private Registration_Service $registration_service;
64
65
	/**
66
	 * Creates a new instance of the registration service.
67
	 *
68
	 * @param DI_Container $di_container
69
	 */
70
	public function __construct( DI_Container $di_container, Registration_Service $registration_service ) {
71
		$this->di_container = $di_container;
72
73
		// Create the registration service.
74
		$this->registration_service = $registration_service;
75
	}
76
77
	/**
78
	 * Adds a module to the manager.
79
	 *
80
	 * @template Module_Instance of Module
81
	 * @param class-string<Module_Instance>                     $module_name
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...
82
	 * @param callable(Module, ?Registration_Middleware):Module $config
83
	 * @return void
84
	 */
85
	public function push_module( string $module_name, ?callable $config = null ): void {
86
		$this->modules[] = array( $module_name, $config );
87
	}
88
89
	/**
90
	 * Adds a class to the Registration Service.
91
	 *
92
	 * @param class-string $class_string The class to register.
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
93
	 */
94
	public function register_class( string $class_string ): void {
95
		$this->registration_service->push_class( $class_string );
96
	}
97
98
	/**
99
	 * Creates and registers all modules.
100
	 *
101
	 * @throws Module_Manager_Exception If invalid module class name provided (Code 20)
102
	 * @throws Module_Manager_Exception If module does not implement Module (Code 21)
103
	 * @throws Module_Manager_Exception If module does not implement Registration_Middleware (Code 22)
104
	 */
105
	public function register_modules(): void {
106
		// Allow for additional apps to hook into the Module Manager.
107
		do_action( Hooks::MODULE_MANAGER, $this );
108
109
		foreach ( $this->modules as list($module_name, $config) ) {
110
			// Create the instance.
111
			$module = $this->create_module( $module_name );
112
113
			// Create the middleware.
114
			$middleware = $this->create_middleware( $module );
115
116
			// If a config is provided, call it.
117
			if ( ! is_null( $config ) ) {
118
				$module = $config( $module, $middleware );
119
			}
120
121
			// Add to the modules and register all hooks.
122
			$this->register_hooks( $module );
123
124
			// Add to the middleware, if provided.
125
			if ( ! is_null( $middleware ) ) {
126
				$this->registration_service->push_middleware( $middleware );
127
			}
128
		}
129
	}
130
131
	/**
132
	 * Creates the module from its class name.
133
	 *
134
	 * @param class-string<Module> $module
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<Module> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<Module>.
Loading history...
135
	 * @return Module
136
	 */
137
	private function create_module( string $module ): Module {
138
		$instance = $this->di_container->create( $module );
139
140
		// If not an object or not an instance of the module interface, throw.
141
		if ( ! is_object( $instance )
142
		|| ! is_a( $instance, Module::class, true )
143
		) {
144
			throw Module_Manager_Exception::invalid_module_class_name( esc_html( $module ) );
145
		}
146
147
		return $instance;
148
	}
149
150
	/**
151
	 * Create the middleware from a module.
152
	 *
153
	 * @param Module $module
154
	 * @return Registration_Middleware|null
155
	 */
156
	private function create_middleware( Module $module ): ?Registration_Middleware {
157
		$middleware = $module->get_middleware();
158
159
		// If no middleware is provided, return null.
160
		if ( is_null( $middleware ) ) {
161
			return null;
162
		}
163
164
		// If not an object or not an instance of the module interface, throw.
165
		if ( ! is_a( $middleware, Registration_Middleware::class, true ) ) {
166
			throw Module_Manager_Exception::invalid_registration_middleware( $middleware ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped, escaped in exception.
167
		}
168
169
		// Create the middleware.
170
		$middleware = $this->di_container->create( $middleware );
171
172
		// If the middleware is not an object, throw.
173
		if ( ! is_object( $middleware )
174
		|| ! is_a( $middleware, Registration_Middleware::class, true )
175
		) {
176
			throw Module_Manager_Exception::failed_to_create_registration_middleware( $middleware ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped, escaped in exception.
177
		}
178
179
		return $middleware;
180
	}
181
182
	/**
183
	 * Register all hooks for modules.
184
	 *
185
	 * @param Module $module
186
	 * @return void
187
	 */
188
	private function register_hooks( Module $module ): void {
189
		add_action( Hooks::APP_INIT_PRE_BOOT, array( $module, 'pre_boot' ), 10, 3 );
190
		add_action( Hooks::APP_INIT_PRE_REGISTRATION, array( $module, 'pre_register' ), 10, 3 );
191
		add_action( Hooks::APP_INIT_POST_REGISTRATION, array( $module, 'post_register' ), 10, 3 );
192
	}
193
194
	/**
195
	 * Process all the middleware using Registration Service.
196
	 *
197
	 * @return void
198
	 */
199
	public function process_middleware(): void {
200
201
		// Process all middleware.
202
		$this->registration_service->process();
203
	}
204
}
205