Page_Dispatcher   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 214
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 26
eloc 81
c 2
b 0
f 0
dl 0
loc 214
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A register_group() 0 24 5
A register_single_page() 0 10 3
A register_primary_page() 0 4 1
A register_subpage() 0 15 4
A set_primary_page_details() 0 16 3
A admin_exception_notice() 0 22 2
A get_primary_page() 0 16 4
A get_pages() 0 13 3
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Registers page groups.
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_Admin_Menu
23
 */
24
25
namespace PinkCrab\Perique_Admin_Menu\Registrar;
26
27
use Throwable;
28
use PinkCrab\Perique\Services\View\View;
29
use PinkCrab\Perique_Admin_Menu\Page\Page;
30
use PinkCrab\Perique\Interfaces\DI_Container;
31
use PinkCrab\Perique_Admin_Menu\Registrar\Registrar;
32
use PinkCrab\Perique_Admin_Menu\Group\Abstract_Group;
33
use PinkCrab\Perique_Admin_Menu\Exception\Page_Exception;
34
use PinkCrab\Perique_Admin_Menu\Exception\Group_Exception;
35
use PinkCrab\Perique_Admin_Menu\Validator\Group_Validator;
36
37
class Page_Dispatcher {
38
39
	protected DI_Container $di_container;
40
	protected View $view;
41
	protected Registrar $registrar;
42
43
	public function __construct( DI_Container $di_container, View $view, Registrar $registrar ) {
44
		$this->di_container = $di_container;
45
		$this->view         = $view;
46
		$this->registrar    = $registrar;
47
	}
48
49
	/**
50
	 * Registers the group and all of its pages.
51
	 *
52
	 * @param \PinkCrab\Perique_Admin_Menu\Group\Abstract_Group $group
53
	 * @return void
54
	 */
55
	public function register_group( Abstract_Group $group ): void {
56
57
		// If current user can not access the group, bail without attempting to register.
58
		if ( ! current_user_can( $group->get_capability() ) ) {
59
			return;
60
		}
61
62
		try {
63
64
			// Validate the group.
65
			$validator = new Group_Validator();
66
			if ( ! $validator->validate( $group ) ) {
67
				throw Group_Exception::failed_validation( $validator, $group );
68
			}
69
70
			$this->register_primary_page( $group );
71
72
			// Register all pages and attempt to set primary page name in menu.
73
			foreach ( $this->get_pages( $group ) as $page ) {
74
				$this->register_subpage( $page, $this->get_primary_page( $group )->slug(), $group );
75
			}
76
			$this->set_primary_page_details( $group );
77
		} catch ( \Throwable $th ) {
78
			$this->admin_exception_notice( $group, $th );
79
		}
80
	}
81
82
	/**
83
	 * Handles the display of errors thrown registering admin menu pages in wp_admin.
84
	 *
85
	 * @param Abstract_Group|Page $object_instance
86
	 * @param \Throwable          $exception
87
	 * @return void
88
	 */
89
	public function admin_exception_notice( $object_instance, Throwable $exception ): void {
90
		add_action(
91
			'admin_notices',
92
			function () use ( $object_instance, $exception ) {
93
				$class   = 'notice notice-error';
94
				$message = sprintf(
95
					'%s <i>%s</i> generated errors while being registered. This might result in admin pages being missing or broken. <br><b>%s(%s: %s)</b>',
96
					get_class( $object_instance ) === Page::class ? 'Page' : 'Menu Group',
97
					get_class( $object_instance ),
98
					$exception->getMessage(),
99
					$exception->getFile(),
100
					$exception->getLine()
101
				);
102
				printf(
103
					'<div class="%1$s"><p>%2$s</p></div>',
104
					esc_attr( $class ),
105
					wp_kses(
106
						$message,
107
						array(
108
							'br' => array(),
109
							'b'  => array(),
110
							'i'  => array(),
111
						)
112
					)
113
				);
114
			}
115
		);
116
	}
117
118
	/**
119
	 * Registers the primary page and group.
120
	 *
121
	 * @param \PinkCrab\Perique_Admin_Menu\Group\Abstract_Group $group
122
	 * @return void
123
	 */
124
	protected function register_primary_page( Abstract_Group $group ): void {
125
		$this->registrar->register_primary(
126
			$this->get_primary_page( $group ),
127
			$group
128
		);
129
	}
130
131
	/**
132
	 * Gets the constructed primary page.
133
	 *
134
	 * @param \PinkCrab\Perique_Admin_Menu\Group\Abstract_Group $group
135
	 * @return \PinkCrab\Perique_Admin_Menu\Page\Page
136
	 */
137
	protected function get_primary_page( Abstract_Group $group ): Page {
138
		/**
139
 * @var Page
140
*/
141
		$page = $this->di_container->create( $group->get_primary_page() );
142
143
		if ( ! is_object( $page ) || ! is_a( $page, Page::class ) ) {
144
			throw Page_Exception::invalid_page_type( $page ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped, escaped in exception.
145
		}
146
147
		// Register view if requied.
148
		if ( \method_exists( $page, 'set_view' ) ) {
149
			$page->set_view( $this->view );
150
		}
151
152
		return $page;
153
	}
154
155
	/**
156
	 * Returns an array of all pages, constructed.
157
	 * Excludes the primary page.
158
	 *
159
	 * @param \PinkCrab\Perique_Admin_Menu\Group\Abstract_Group $group
160
	 * @return array<Page>
161
	 * @throws Page_Exception (Code 202)
162
	 */
163
	protected function get_pages( Abstract_Group $group ): array {
164
		return array_map(
165
			function ( string $page ): Page {
166
				$constructed_page = $this->di_container->create( $page );
167
				if ( ! is_object( $constructed_page ) || ! is_a( $constructed_page, Page::class ) ) {
168
					throw Page_Exception::invalid_page_type( $constructed_page ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped, escaped in exception.
169
				}
170
				return $constructed_page;
171
			},
172
			array_filter(
173
				$group->get_pages(),
174
				function ( string $page ) use ( $group ) {
175
					return $page !== $group->get_primary_page();
176
				}
177
			)
178
		);
179
	}
180
181
182
	/**
183
	 * Sets the menu title the primary page value.
184
	 *
185
	 * @param \PinkCrab\Perique_Admin_Menu\Group\Abstract_Group $group
186
	 * @return void
187
	 * @phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited
188
	 * @throws Group_Exception (code 253)
189
	 */
190
	protected function set_primary_page_details( Abstract_Group $group ) {
191
		global $submenu;
192
193
		$primary = $this->get_primary_page( $group );
194
195
		if ( ! array_key_exists( $primary->slug(), $submenu ) ) {
196
			return;
197
		}
198
199
		$primary_page_key = array_search(
200
			$primary->slug(),
201
			\array_column( $submenu[ $primary->slug() ], 2 ),
202
			true
203
		) ?: 0; // phpcs:ignore Universal.Operators.DisallowShortTernary.Found
204
205
		$submenu[ $primary->slug() ][ $primary_page_key ][0] = $primary->menu_title();
206
	}
207
208
209
	/**
210
	 * Registers a subpage.
211
	 *
212
	 * @param \PinkCrab\Perique_Admin_Menu\Page\Page                 $page
213
	 * @param string                                                 $parent_slug
214
	 * @param \PinkCrab\Perique_Admin_Menu\Group\Abstract_Group|null $group
215
	 * @return void
216
	 */
217
	public function register_subpage( Page $page, string $parent_slug, ?Abstract_Group $group = null ): void {
218
		// If user cant access the page, bail before attempting to register.
219
		if ( ! current_user_can( $page->capability() ) ) {
220
			return;
221
		}
222
223
		// Register view if required.
224
		if ( \method_exists( $page, 'set_view' ) ) {
225
			$page->set_view( $this->view );
226
		}
227
228
		try {
229
			$this->registrar->register_subpage( $page, $parent_slug, $group );
230
		} catch ( \Throwable $th ) {
231
			$this->admin_exception_notice( $page, $th );
232
		}
233
	}
234
235
	/**
236
	 * Registers a single page.
237
	 *
238
	 * @param \PinkCrab\Perique_Admin_Menu\Page\Page $page
239
	 * @return void
240
	 */
241
	public function register_single_page( Page $page ): void {
242
		// Register view if required.
243
		if ( \method_exists( $page, 'set_view' ) ) {
244
			$page->set_view( $this->view );
245
		}
246
247
		try {
248
			$this->registrar->register_primary( $page, null );
249
		} catch ( \Throwable $th ) {
250
			$this->admin_exception_notice( $page, $th );
251
		}
252
	}
253
}
254