Inspector   F
last analyzed

Complexity

Total Complexity 64

Size/Duplication

Total Lines 431
Duplicated Lines 0 %

Test Coverage

Coverage 36.71%

Importance

Changes 0
Metric Value
eloc 199
dl 0
loc 431
ccs 76
cts 207
cp 0.3671
rs 3.28
c 0
b 0
f 0
wmc 64

14 Methods

Rating   Name   Duplication   Size   Complexity  
A getViewtypes() 0 2 1
A getPluginHooks() 0 2 1
A getEvents() 0 2 1
A getWidgets() 0 9 2
F getViews() 0 72 13
A getActions() 0 18 4
A getSimpleCache() 0 22 5
A buildHandlerTree() 0 22 5
A getWebServices() 0 21 5
A describeCallable() 0 2 1
A getServices() 0 14 3
A getViewsData() 0 6 2
C getMenus() 0 82 11
B getRoutes() 0 43 10

How to fix   Complexity   

Complex Class

Complex classes like Inspector often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Inspector, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Elgg\Debug;
3
4
use Elgg\Debug\Inspector\ViewComponent;
5
use Elgg\Includer;
6
use Elgg\Project\Paths;
7
8
/**
9
 * WARNING: API IN FLUX. DO NOT USE DIRECTLY.
10
 *
11
 * @access private
12
 *
13
 * @package Elgg.Core
14
 * @since   1.11
15
 */
16
class Inspector {
17
18
	/**
19
	 * Get Elgg event information
20
	 *
21
	 * @return array [event,type] => array(handlers)
22
	 */
23 3
	public function getEvents() {
24 3
		return $this->buildHandlerTree(_elgg_services()->events->getAllHandlers());
25
	}
26
27
	/**
28
	 * Get Elgg plugin hooks information
29
	 *
30
	 * @return array [hook,type] => array(handlers)
31
	 */
32 3
	public function getPluginHooks() {
33 3
		return $this->buildHandlerTree(_elgg_services()->hooks->getAllHandlers());
34
	}
35
36
	/**
37
	 * Get all view types for known views
38
	 *
39
	 * @return string[]
40
	 */
41
	public function getViewtypes() {
42
		return array_keys($this->getViewsData()['locations']);
43
	}
44
45
	/**
46
	 * Get Elgg view information
47
	 *
48
	 * @param string $viewtype The Viewtype we wish to inspect
49
	 *
50
	 * @return array [view] => map of priority to ViewComponent[]
51
	 */
52 3
	public function getViews($viewtype = 'default') {
53 3
		$view_data = $this->getViewsData();
54
55
		// maps view name to array of ViewComponent[] with priority as keys
56 3
		$views = [];
57
58
		// add plugins and handle overrides
59 3
		foreach ($view_data['locations'][$viewtype] as $view => $location) {
60 3
			$component = new ViewComponent();
61 3
			$component->view = $view;
62 3
			$component->file = $location;
63
64 3
			$views[$view] = [500 => $component];
65
		}
66
67
		// now extensions
68 3
		foreach ($view_data['extensions'] as $view => $extensions) {
69 3
			$view_list = [];
70 3
			foreach ($extensions as $priority => $ext_view) {
71 3
				if (isset($views[$ext_view])) {
72 3
					$view_list[$priority] = $views[$ext_view][500];
73
				}
74
			}
75 3
			if (count($view_list) > 0) {
76 3
				$views[$view] = $view_list;
77
			}
78
		}
79
80 3
		ksort($views);
81
82
		// now overrides
83 3
		foreach ($views as $view => $view_list) {
84 3
			if (!empty($view_data['overrides'][$viewtype][$view])) {
85 3
				$overrides_list = [];
86 3
				foreach ($view_data['overrides'][$viewtype][$view] as $i => $location) {
87 3
					$component = new ViewComponent();
88 3
					$component->overridden = true;
89 3
					$component->view = $view;
90 3
					$component->file = $location;
91
92 3
					$overrides_list["o:$i"] = $component;
93
				}
94 3
				$views[$view] = $overrides_list + $view_list;
95
			}
96
		}
97
98
		// view handlers
99 3
		$handlers = _elgg_services()->hooks->getAllHandlers();
100
101 3
		$input_filtered_views = [];
102 3
		if (!empty($handlers['view_vars'])) {
103 3
			$input_filtered_views = array_keys($handlers['view_vars']);
104
		}
105
106 3
		$filtered_views = [];
107 3
		if (!empty($handlers['view'])) {
108
			$filtered_views = array_keys($handlers['view']);
109
		}
110
111 3
		$global_hooks = [];
112 3
		if (!empty($handlers['view_vars']['all'])) {
113
			$global_hooks[] = 'view_vars, all';
114
		}
115 3
		if (!empty($handlers['view']['all'])) {
116
			$global_hooks[] = 'view, all';
117
		}
118
119
		return [
120 3
			'views' => $views,
121 3
			'global_hooks' => $global_hooks,
122 3
			'input_filtered_views' => $input_filtered_views,
123 3
			'filtered_views' => $filtered_views,
124
		];
125
	}
126
127
	/**
128
	 * Get Elgg widget information
129
	 *
130
	 * @return array [widget] => array(name, contexts)
131
	 */
132
	public function getWidgets() {
133
		$tree = [];
134
		foreach (_elgg_services()->widgets->getAllTypes() as $handler => $handler_obj) {
135
			$tree[$handler] = [$handler_obj->name, implode(',', array_values($handler_obj->context))];
136
		}
137
138
		ksort($tree);
139
140
		return $tree;
141
	}
142
143
144
	/**
145
	 * Get Elgg actions information
146
	 *
147
	 * returns [action] => array(file, access)
148
	 *
149
	 * @return array
150
	 */
151 3
	public function getActions() {
152 3
		$tree = [];
153
		$access = [
154 3
			'public' => 'public',
155
			'logged_in' => 'logged in only',
156
			'admin' => 'admin only',
157
		];
158 3
		$start = strlen(elgg_get_root_path());
159 3
		foreach (_elgg_services()->actions->getAllActions() as $action => $info) {
160 3
			if (isset($info['file'])) {
161 3
				$info['file'] = substr($info['file'], $start);
162 3
			} else if ($info['controller']) {
163 3
				$info['file'] = $this->describeCallable($info['controller']);
164
			}
165 3
			$tree[$action] = [$info['file'], $access[$info['access']]];
166
		}
167 3
		ksort($tree);
168 3
		return $tree;
169
	}
170
171
	/**
172
	 * Get simplecache information
173
	 *
174
	 * @return array [views]
175
	 */
176
	public function getSimpleCache() {
177
		
178
		$simplecache = elgg_extract('simplecache', $this->getViewsData(), []);
179
		$locations = elgg_extract('locations', $this->getViewsData(), []);
180
		
181
		$tree = [];
182
		foreach ($simplecache as $view => $foo) {
183
			$tree[$view] = '';
184
		}
185
		
186
		// add all static views
187
		foreach ($locations as $viewtype) {
188
			foreach ($viewtype as $view => $location) {
189
				if (pathinfo($location, PATHINFO_EXTENSION) !== 'php') {
190
					$tree[$view] = '';
191
				}
192
			}
193
		}
194
195
		ksort($tree);
196
197
		return $tree;
198
	}
199
200
	/**
201
	 * Get Elgg route information
202
	 *
203
	 * returns [route] => array(path, resource)
204
	 *
205
	 * @return array
206
	 */
207
	public function getRoutes() {
208
		$tree = [];
209
		foreach (_elgg_services()->routeCollection->all() as $name => $route) {
210
			$handler = $route->getDefault('_handler') ? : '';
211
			if ($handler) {
212
				$handler = $this->describeCallable($handler);
213
			}
214
215
			$controller = $route->getDefault('_controller') ? : '';
216
			if ($controller) {
217
				$controller = $this->describeCallable($controller);
218
			}
219
220
			$resource = $route->getDefault('_resource') ? : '';
221
222
			$file = $route->getDefault('_file') ? : '';
223
224
			$middleware = $route->getDefault('_middleware');
225
			if (!is_array($middleware)) {
226
				if (!empty($middleware)) {
227
					$middleware = [$middleware];
228
				} else {
229
					$middleware = [];
230
				}
231
			}
232
			$middleware = array_map(function($e) {
233
				return $this->describeCallable($e);
234
			}, $middleware);
235
236
			$tree[$name] = [
237
				$route->getPath(),
238
				$resource,
239
				$handler,
240
				$controller,
241
				$file,
242
				$middleware,
243
			];
244
		}
245
		uasort($tree, function($e1, $e2) {
246
			return strcmp($e1[0], $e2[0]);
247
		});
248
249
		return $tree;
250
	}
251
252
	/**
253
	 * Get Elgg web services API methods
254
	 *
255
	 * @return array [method] => array(function, parameters, call_method, api auth, user auth)
256
	 */
257
	public function getWebServices() {
258
		global $API_METHODS;
259
260
		$tree = [];
261
		foreach ($API_METHODS as $method => $info) {
262
			$params = implode(', ', array_keys(elgg_extract('parameters', $info, [])));
263
			if (!$params) {
264
				$params = 'none';
265
			}
266
			$tree[$method] = [
267
				$info['function'],
268
				"params: $params",
269
				$info['call_method'],
270
				($info['require_api_auth']) ? 'API authentication required' : 'No API authentication required',
271
				($info['require_user_auth']) ? 'User authentication required' : 'No user authentication required',
272
			];
273
		}
274
275
		ksort($tree);
276
277
		return $tree;
278
	}
279
280
	/**
281
	 * Get information about registered menus
282
	 *
283
	 * @return array [menu name] => array(item name => array(text, href, section, parent))
284
	 */
285
	public function getMenus() {
286
287
		$menus = _elgg_config()->menus;
288
289
		// get JIT menu items
290
		// note that 'river' is absent from this list - hooks attempt to get object/subject entities cause problems
291
		$jit_menus = ['annotation', 'entity', 'login', 'longtext', 'owner_block', 'user_hover', 'widget'];
292
293
		// create generic ElggEntity, ElggAnnotation, ElggUser, ElggWidget
294
		$annotation = new \ElggAnnotation();
295
		$annotation->id = 999;
296
		$annotation->name = 'generic_comment';
297
		$annotation->value = 'testvalue';
298
299
		$entity = new \ElggObject();
300
		$entity->guid = 999;
301
		$entity->subtype = 'blog';
302
		$entity->title = 'test entity';
303
		$entity->access_id = ACCESS_PUBLIC;
304
305
		$user = new \ElggUser();
306
		$user->guid = 999;
307
		$user->name = "Test User";
308
		$user->username = 'test_user';
309
310
		$widget = new \ElggWidget();
311
		$widget->guid = 999;
312
		$widget->title = 'test widget';
313
314
		// call plugin hooks
315
		foreach ($jit_menus as $type) {
316
			$params = ['entity' => $entity, 'annotation' => $annotation, 'user' => $user];
317
			switch ($type) {
318
				case 'owner_block':
319
				case 'user_hover':
320
					$params['entity'] = $user;
321
					break;
322
				case 'widget':
323
					// this does not work because you cannot set a guid on an entity
324
					$params['entity'] = $widget;
325
					break;
326
				case 'longtext':
327
					$params['id'] = rand();
328
					break;
329
				default:
330
					break;
331
			}
332
			$menus[$type] = _elgg_services()->hooks->trigger('register', "menu:$type", $params, []);
333
		}
334
335
		// put the menus in tree form for inspection
336
		$tree = [];
337
338
		foreach ($menus as $menu_name => $attributes) {
339
			foreach ($attributes as $item) {
340
				/* @var \ElggMenuItem $item */
341
				$name = $item->getName();
342
				$text = htmlspecialchars($item->getText(), ENT_QUOTES, 'UTF-8', false);
343
				$href = $item->getHref();
344
				if ($href === false) {
345
					$href = 'not a link';
346
				} elseif ($href === "") {
347
					$href = 'not a direct link - possibly ajax';
348
				}
349
				$section = $item->getSection();
350
				$parent = $item->getParentName();
351
				if (!$parent) {
352
					$parent = 'none';
353
				}
354
355
				$tree[$menu_name][$name] = [
356
					"text: $text",
357
					"href: $href",
358
					"section: $section",
359
					"parent: $parent",
360
				];
361
			}
362
		}
363
364
		ksort($tree);
365
366
		return $tree;
367
	}
368
369
	/**
370
	 * Get a string description of a callback
371
	 *
372
	 * E.g. "function_name", "Static::method", "(ClassName)->method", "(Closure path/to/file.php:23)"
373
	 *
374
	 * @param mixed  $callable  Callable
375
	 * @param string $file_root If provided, it will be removed from the beginning of file names
376
	 * @return string
377
	 */
378 3
	public function describeCallable($callable, $file_root = '') {
379 3
		return _elgg_services()->handlers->describeCallable($callable, $file_root);
380
	}
381
382
	/**
383
	 * Build a tree of event handlers
384
	 *
385
	 * @param array $all_handlers Set of handlers from a HooksRegistrationService
386
	 *
387
	 * @return array
388
	 */
389 3
	protected function buildHandlerTree($all_handlers) {
390 3
		$tree = [];
391 3
		$root = elgg_get_root_path();
392 3
		$handlers_svc = _elgg_services()->handlers;
393
394 3
		foreach ($all_handlers as $hook => $types) {
395 3
			foreach ($types as $type => $priorities) {
396 3
				ksort($priorities);
397
398 3
				foreach ($priorities as $priority => $handlers) {
399 3
					foreach ($handlers as $callable) {
400 3
						$description = $handlers_svc->describeCallable($callable, $root);
401 3
						$callable = "$priority: $description";
402 3
						$tree["$hook, $type"][] = $callable;
403
					}
404
				}
405
			}
406
		}
407
408 3
		ksort($tree);
409
410 3
		return $tree;
411
	}
412
413
	/**
414
	 * Get data from the Views service
415
	 *
416
	 * @return array
417
	 */
418 3
	private function getViewsData() {
419 3
		static $data;
420 3
		if ($data === null) {
421 1
			$data = _elgg_services()->views->getInspectorData();
422
		}
423 3
		return $data;
424
	}
425
426
	/**
427
	 * Returns public DI services
428
	 *
429
	 * returns [service_name => [class, path]]
430
	 *
431
	 * @return array
432
	 */
433
	public function getServices() {
434
		$tree = [];
435
436
		foreach (_elgg_services()->dic_loader->getDefinitions() as $definition) {
437
			$services = Includer::includeFile($definition);
438
439
			foreach ($services as $name => $service) {
440
				$tree[$name] = [get_class(elgg()->$name), Paths::sanitize($definition, false)];
441
			}
442
		}
443
444
		ksort($tree);
445
446
		return $tree;
447
	}
448
}
449