Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

Inspector   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 352
Duplicated Lines 0 %

Test Coverage

Coverage 42.77%

Importance

Changes 0
Metric Value
dl 0
loc 352
ccs 71
cts 166
cp 0.4277
rs 8.5454
c 0
b 0
f 0
wmc 49

12 Methods

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

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
6
/**
7
 * WARNING: API IN FLUX. DO NOT USE DIRECTLY.
8
 *
9
 * @access private
10
 *
11
 * @package Elgg.Core
12
 * @since   1.11
13
 */
14
class Inspector {
15
16
	/**
17
	 * Get Elgg event information
18
	 *
19
	 * @return array [event,type] => array(handlers)
20
	 */
21 1
	public function getEvents() {
22 1
		return $this->buildHandlerTree(_elgg_services()->hooks->getEvents()->getAllHandlers());
23
	}
24
25
	/**
26
	 * Get Elgg plugin hooks information
27
	 *
28
	 * @return array [hook,type] => array(handlers)
29
	 */
30 1
	public function getPluginHooks() {
31 1
		return $this->buildHandlerTree(_elgg_services()->hooks->getAllHandlers());
32
	}
33
34
	/**
35
	 * Get all view types for known views
36
	 *
37
	 * @return string[]
38
	 */
39
	public function getViewtypes() {
40
		return array_keys($this->getViewsData()['locations']);
41
	}
42
43
	/**
44
	 * Get Elgg view information
45
	 *
46
	 * @param string $viewtype The Viewtype we wish to inspect
47
	 *
48
	 * @return array [view] => map of priority to ViewComponent[]
49
	 */
50 1
	public function getViews($viewtype = 'default') {
51 1
		$view_data = $this->getViewsData();
52
53
		// maps view name to array of ViewComponent[] with priority as keys
54 1
		$views = [];
55
56
		// add plugins and handle overrides
57 1
		foreach ($view_data['locations'][$viewtype] as $view => $location) {
58 1
			$component = new ViewComponent();
59 1
			$component->view = $view;
60 1
			$component->file = $location;
61
62 1
			$views[$view] = [500 => $component];
63
		}
64
65
		// now extensions
66 1
		foreach ($view_data['extensions'] as $view => $extensions) {
67 1
			$view_list = [];
68 1
			foreach ($extensions as $priority => $ext_view) {
69 1
				if (isset($views[$ext_view])) {
70 1
					$view_list[$priority] = $views[$ext_view][500];
71
				}
72
			}
73 1
			if (count($view_list) > 0) {
74 1
				$views[$view] = $view_list;
75
			}
76
		}
77
78 1
		ksort($views);
79
80
		// now overrides
81 1
		foreach ($views as $view => $view_list) {
82 1
			if (!empty($view_data['overrides'][$viewtype][$view])) {
83 1
				$overrides_list = [];
84 1
				foreach ($view_data['overrides'][$viewtype][$view] as $i => $location) {
85 1
					$component = new ViewComponent();
86 1
					$component->overridden = true;
87 1
					$component->view = $view;
88 1
					$component->file = $location;
89
90 1
					$overrides_list["o:$i"] = $component;
91
				}
92 1
				$views[$view] = $overrides_list + $view_list;
93
			}
94
		}
95
96
		// view handlers
97 1
		$handlers = _elgg_services()->hooks->getAllHandlers();
98
99 1
		$input_filtered_views = [];
100 1
		if (!empty($handlers['view_vars'])) {
101 1
			$input_filtered_views = array_keys($handlers['view_vars']);
102
		}
103
104 1
		$filtered_views = [];
105 1
		if (!empty($handlers['view'])) {
106
			$filtered_views = array_keys($handlers['view']);
107
		}
108
109 1
		$global_hooks = [];
110 1
		if (!empty($handlers['view_vars']['all'])) {
111
			$global_hooks[] = 'view_vars, all';
112
		}
113 1
		if (!empty($handlers['view']['all'])) {
114
			$global_hooks[] = 'view, all';
115
		}
116
117
		return [
118 1
			'views' => $views,
119 1
			'global_hooks' => $global_hooks,
120 1
			'input_filtered_views' => $input_filtered_views,
121 1
			'filtered_views' => $filtered_views,
122
		];
123
	}
124
125
	/**
126
	 * Get Elgg widget information
127
	 *
128
	 * @return array [widget] => array(name, contexts)
129
	 */
130
	public function getWidgets() {
131
		$tree = [];
132
		foreach (_elgg_services()->widgets->getAllTypes() as $handler => $handler_obj) {
133
			$tree[$handler] = [$handler_obj->name, implode(',', array_values($handler_obj->context))];
134
		}
135
136
		ksort($tree);
137
138
		return $tree;
139
	}
140
141
142
	/**
143
	 * Get Elgg actions information
144
	 *
145
	 * returns [action] => array(file, access)
146
	 *
147
	 * @return array
148
	 */
149 1
	public function getActions() {
150 1
		$tree = [];
151
		$access = [
152 1
			'public' => 'public',
153
			'logged_in' => 'logged in only',
154
			'admin' => 'admin only',
155
		];
156 1
		$start = strlen(elgg_get_root_path());
157 1
		foreach (_elgg_services()->actions->getAllActions() as $action => $info) {
158 1
			$info['file'] = substr($info['file'], $start);
159 1
			$tree[$action] = [$info['file'], $access[$info['access']]];
160
		}
161 1
		ksort($tree);
162 1
		return $tree;
163
	}
164
165
	/**
166
	 * Get simplecache information
167
	 *
168
	 * @return array [views]
169
	 */
170
	public function getSimpleCache() {
171
		
172
		$simplecache = elgg_extract('simplecache', $this->getViewsData(), []);
173
		$locations = elgg_extract('locations', $this->getViewsData(), []);
174
		
175
		$tree = [];
176
		foreach ($simplecache as $view => $foo) {
177
			$tree[$view] = '';
178
		}
179
		
180
		// add all static views
181
		foreach ($locations as $viewtype) {
182
			foreach ($viewtype as $view => $location) {
183
				if (pathinfo($location, PATHINFO_EXTENSION) !== 'php') {
184
					$tree[$view] = '';
185
				}
186
			}
187
		}
188
189
		ksort($tree);
190
191
		return $tree;
192
	}
193
194
	/**
195
	 * Get Elgg web services API methods
196
	 *
197
	 * @return array [method] => array(function, parameters, call_method, api auth, user auth)
198
	 */
199
	public function getWebServices() {
200
		global $API_METHODS;
201
202
		$tree = [];
203
		foreach ($API_METHODS as $method => $info) {
204
			$params = implode(', ', array_keys(elgg_extract('parameters', $info, [])));
205
			if (!$params) {
206
				$params = 'none';
207
			}
208
			$tree[$method] = [
209
				$info['function'],
210
				"params: $params",
211
				$info['call_method'],
212
				($info['require_api_auth']) ? 'API authentication required' : 'No API authentication required',
213
				($info['require_user_auth']) ? 'User authentication required' : 'No user authentication required',
214
			];
215
		}
216
217
		ksort($tree);
218
219
		return $tree;
220
	}
221
222
	/**
223
	 * Get information about registered menus
224
	 *
225
	 * @return array [menu name] => array(item name => array(text, href, section, parent))
226
	 */
227
	public function getMenus() {
228
229
		$menus = _elgg_config()->menus;
230
231
		// get JIT menu items
232
		// note that 'river' is absent from this list - hooks attempt to get object/subject entities cause problems
233
		$jit_menus = ['annotation', 'entity', 'login', 'longtext', 'owner_block', 'user_hover', 'widget'];
234
235
		// create generic ElggEntity, ElggAnnotation, ElggUser, ElggWidget
236
		$annotation = new \ElggAnnotation();
237
		$annotation->id = 999;
238
		$annotation->name = 'generic_comment';
239
		$annotation->value = 'testvalue';
240
241
		$entity = new \ElggObject();
242
		$entity->guid = 999;
1 ignored issue
show
Bug introduced by Steve Clay
The property guid is declared read-only in ElggEntity.
Loading history...
243
		$entity->subtype = 'blog';
244
		$entity->title = 'test entity';
245
		$entity->access_id = ACCESS_PUBLIC;
246
247
		$user = new \ElggUser();
248
		$user->guid = 999;
249
		$user->name = "Test User";
250
		$user->username = 'test_user';
251
252
		$widget = new \ElggWidget();
253
		$widget->guid = 999;
254
		$widget->title = 'test widget';
255
256
		// call plugin hooks
257
		foreach ($jit_menus as $type) {
258
			$params = ['entity' => $entity, 'annotation' => $annotation, 'user' => $user];
259
			switch ($type) {
260
				case 'owner_block':
261
				case 'user_hover':
262
					$params['entity'] = $user;
263
					break;
264
				case 'widget':
265
					// this does not work because you cannot set a guid on an entity
266
					$params['entity'] = $widget;
267
					break;
268
				case 'longtext':
269
					$params['id'] = rand();
0 ignored issues
show
Bug introduced by Jeroen Dalsem
The call to rand() has too few arguments starting with min. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

269
					$params['id'] = /** @scrutinizer ignore-call */ rand();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
270
					break;
271
				default:
272
					break;
273
			}
274
			$menus[$type] = elgg_trigger_plugin_hook('register', "menu:$type", $params, []);
275
		}
276
277
		// put the menus in tree form for inspection
278
		$tree = [];
279
280
		foreach ($menus as $menu_name => $attributes) {
281
			foreach ($attributes as $item) {
282
				/* @var \ElggMenuItem $item */
283
				$name = $item->getName();
284
				$text = htmlspecialchars($item->getText(), ENT_QUOTES, 'UTF-8', false);
285
				$href = $item->getHref();
286
				if ($href === false) {
287
					$href = 'not a link';
288
				} elseif ($href === "") {
289
					$href = 'not a direct link - possibly ajax';
290
				}
291
				$section = $item->getSection();
292
				$parent = $item->getParentName();
293
				if (!$parent) {
294
					$parent = 'none';
295
				}
296
297
				$tree[$menu_name][$name] = [
298
					"text: $text",
299
					"href: $href",
300
					"section: $section",
301
					"parent: $parent",
302
				];
303
			}
304
		}
305
306
		ksort($tree);
307
308
		return $tree;
309
	}
310
311
	/**
312
	 * Get a string description of a callback
313
	 *
314
	 * E.g. "function_name", "Static::method", "(ClassName)->method", "(Closure path/to/file.php:23)"
315
	 *
316
	 * @param mixed  $callable  Callable
317
	 * @param string $file_root If provided, it will be removed from the beginning of file names
318
	 * @return string
319
	 */
320
	public function describeCallable($callable, $file_root = '') {
321
		return _elgg_services()->handlers->describeCallable($callable, $file_root);
322
	}
323
324
	/**
325
	 * Build a tree of event handlers
326
	 *
327
	 * @param array $all_handlers Set of handlers from a HooksRegistrationService
328
	 *
329
	 * @return array
330
	 */
331 1
	protected function buildHandlerTree($all_handlers) {
332 1
		$tree = [];
333 1
		$root = elgg_get_root_path();
334 1
		$handlers_svc = _elgg_services()->handlers;
335
336 1
		foreach ($all_handlers as $hook => $types) {
337 1
			foreach ($types as $type => $priorities) {
338 1
				ksort($priorities);
339
340 1
				foreach ($priorities as $priority => $handlers) {
341 1
					foreach ($handlers as $callable) {
342 1
						$description = $handlers_svc->describeCallable($callable, $root);
343 1
						$callable = "$priority: $description";
344 1
						$tree["$hook, $type"][] = $callable;
345
					}
346
				}
347
			}
348
		}
349
350 1
		ksort($tree);
351
352 1
		return $tree;
353
	}
354
355
	/**
356
	 * Get data from the Views service
357
	 *
358
	 * @return array
359
	 */
360 1
	private function getViewsData() {
361 1
		static $data;
362 1
		if ($data === null) {
363 1
			$data = _elgg_services()->views->getInspectorData();
364
		}
365 1
		return $data;
366
	}
367
}
368