Completed
Branch development (176841)
by Elk
06:59
created

AddonSettings.subs.php ➔ list_integration_hooks_data()   F

Complexity

Conditions 39
Paths > 20000

Size

Total Lines 188

Duplication

Lines 5
Ratio 2.66 %

Code Coverage

Tests 0
CRAP Score 1560

Importance

Changes 0
Metric Value
cc 39
nc 30392
nop 3
dl 5
loc 188
rs 0
c 0
b 0
f 0
ccs 0
cts 147
cp 0
crap 1560

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Functions to support addon settings controller
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * This file contains code covered by:
11
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
12
 *
13
 * @version 2.0 dev
14
 *
15
 */
16
17
/**
18
 * Gets all of the files in a directory and its children directories
19
 *
20
 * @package AddonSettings
21
 * @param string $dir_path
22
 * @return array
23
 */
24
function get_files_recursive($dir_path)
25
{
26
	$files = array();
27
28
	try
29
	{
30
		$iterator = new \RecursiveIteratorIterator(
31
			new \RecursiveDirectoryIterator($dir_path, \RecursiveDirectoryIterator::SKIP_DOTS),
32
			\RecursiveIteratorIterator::SELF_FIRST,
33
			\RecursiveIteratorIterator::CATCH_GET_CHILD
34
		);
35
36
		foreach ($iterator as $file)
37
		{
38
			if ($file->isFile())
39
				$files[] = array('dir' => $file->getPath(), 'name' => $file->getFilename());
40
		}
41
	}
42
	catch (UnexpectedValueException $e)
43
	{
44
		// @todo, give them a prize
45
	}
46
47
	return $files;
48
}
49
50
/**
51
 * Callback function for the integration hooks list (list_integration_hooks)
52
 *
53
 * What it does:
54
 *
55
 * - Gets all of the hooks in the system and their status
56
 * - Would be better documented if Ema was not lazy
57
 *
58
 * @package AddonSettings
59
 * @param int $start The item to start with (for pagination purposes)
60
 * @param int $items_per_page  The number of items to show per page
61
 * @param string $sort A string indicating how to sort the results
62
 * @return array
63
 */
64
function list_integration_hooks_data($start, $items_per_page, $sort)
65
{
66
	global $txt, $context, $scripturl;
67
68
	require_once(SUBSDIR . '/Package.subs.php');
69
70
	$hooks = $temp_hooks = get_integration_hooks();
71
	$hooks_data = $temp_data = $hook_status = array();
72
73
	$files = get_files_recursive(SOURCEDIR);
74
	if (!empty($files))
75
	{
76
		foreach ($files as $file)
77
		{
78
			if (is_file($file['dir'] . '/' . $file['name']) && substr($file['name'], -4) === '.php')
79
			{
80
				$fp = fopen($file['dir'] . '/' . $file['name'], 'rb');
81
				$fc = strtr(fread($fp, max(filesize($file['dir'] . '/' . $file['name']), 1)), array("\r" => '', "\n" => ''));
82
				fclose($fp);
83
84
				foreach ($temp_hooks as $hook => $functions)
0 ignored issues
show
Bug introduced by
The expression $temp_hooks of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
85
				{
86
					foreach ($functions as $function_o)
87
					{
88
						$hook_name = str_replace(']', '', $function_o);
89
90
						if (strpos($hook_name, '::') !== false)
91
						{
92
							$function = explode('::', $hook_name);
93
							$class = $function[0];
94
							$function = $function[1];
95
						}
96
						else
97
						{
98
							$class = '';
99
							$function = $hook_name;
100
						}
101
102
						$function = explode('|', $function);
103
						$function = $function[0];
104
105
						if (substr($hook, -8) === '_include')
106
						{
107
							$real_path = parse_path(trim($hook_name));
108
109
							if ($real_path == $hook_name)
110
								$hook_status[$hook][$hook_name]['exists'] = false;
111
							else
112
								$hook_status[$hook][$hook_name]['exists'] = file_exists(parse_path(ltrim($real_path, '|')));
113
114
							// I need to know if there is at least one function called in this file.
115
							$temp_data['include'][basename($function)] = array('hook' => $hook, 'function' => $function);
116
							unset($temp_hooks[$hook][$function_o]);
117
						}
118
						// Procedural functions as easy
119
						elseif (empty($class) && strpos(str_replace(' (', '(', $fc), 'function ' . trim($function) . '(') !== false)
120
						{
121
							$hook_status[$hook][$hook_name]['exists'] = true;
122
							$hook_status[$hook][$hook_name]['in_file'] = $file['name'];
123
124
							// I want to remember all the functions called within this file (to check later if they are
125
							// enabled or disabled and decide if the integrate_*_include of that file can be disabled too)
126
							$temp_data['function'][$file['name']][] = $function_o;
127
							unset($temp_hooks[$hook][$function_o]);
128
						}
129
						// OOP a bit more difficult
130
						elseif (!empty($class) && preg_match('~class\s*' . preg_quote(trim($class)) . '.*function\s*' . preg_quote(trim($function), '~') . '\s*\(~i', $fc) != 0)
131
						{
132
							$hook_status[$hook][$hook_name]['exists'] = true;
133
							$hook_status[$hook][$hook_name]['in_file'] = $file['name'];
134
135
							// I want to remember all the functions called within this file (to check later if they are
136
							// enabled or disabled and decide if the integrate_*_include of that file can be disabled too)
137
							$temp_data['function'][$file['name']][] = $function_o;
138
							unset($temp_hooks[$hook][$function_o]);
139
						}
140
					}
141
				}
142
			}
143
		}
144
	}
145
146
	$sort_types = array(
147
		'hook_name' => array('hook_name', SORT_ASC),
148
		'hook_name DESC' => array('hook_name', SORT_DESC),
149
		'function_name' => array('function_name', SORT_ASC),
150
		'function_name DESC' => array('function_name', SORT_DESC),
151
		'file_name' => array('file_name', SORT_ASC),
152
		'file_name DESC' => array('file_name', SORT_DESC),
153
		'status' => array('status', SORT_ASC),
154
		'status DESC' => array('status', SORT_DESC),
155
	);
156
157
	$sort_options = $sort_types[$sort];
158
	$sort = array();
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $sort. This often makes code more readable.
Loading history...
159
	$hooks_filters = array();
160
161
	foreach ($hooks as $hook => $functions)
0 ignored issues
show
Bug introduced by
The expression $hooks of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
162
	{
163
		$hooks_filters[] = '<option ' . ($context['current_filter'] == $hook ? 'selected="selected" ' : '') . ' value="' . $hook . '">' . $hook . '</option>';
164
		foreach ($functions as $function)
165
		{
166
			$function = str_replace(']', '', $function);
167
168
			// This is a not an include and the function is included in a certain file (if not it doesn't exists so don't care)
169
			if (substr($hook, -8) !== '_include' && isset($hook_status[$hook][$function]['in_file']))
170
			{
171
				$current_hook = isset($temp_data['include'][$hook_status[$hook][$function]['in_file']]) ? $temp_data['include'][$hook_status[$hook][$function]['in_file']] : '';
172
				$enabled = false;
173
174
				// Checking all the functions within this particular file
175
				// if any of them is enable then the file *must* be included and the integrate_*_include hook cannot be disabled
176
				foreach ($temp_data['function'][$hook_status[$hook][$function]['in_file']] as $func)
177
					$enabled = $enabled || strstr($func, ']') !== false;
178
179
				if (!$enabled && !empty($current_hook))
180
					$hook_status[$current_hook['hook']][$current_hook['function']]['enabled'] = true;
181
			}
182
		}
183
	}
184
185
		theme()->addInlineJavascript('
186
			var hook_name_header = document.getElementById(\'header_list_integration_hooks_hook_name\');
187
			hook_name_header.innerHTML += ' . JavaScriptEscape('
188
				<select onchange="window.location = \'' . $scripturl . '?action=admin;area=maintain;sa=hooks\' + (this.value ? \';filter=\' + this.value : \'\');">
189
					<option>---</option>
190
					<option value="">' . $txt['hooks_reset_filter'] . '</option>' . implode('', $hooks_filters) . '</select>' . '
191
				</select>') . ';', true);
192
193
	$temp_data = array();
194
	$id = 0;
195
196
	foreach ($hooks as $hook => $functions)
0 ignored issues
show
Bug introduced by
The expression $hooks of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
197
	{
198
		if (empty($context['filter']) || (!empty($context['filter']) && $context['filter'] == $hook))
199
		{
200
			foreach ($functions as $function)
201
			{
202
				$enabled = strstr($function, ']') === false;
203
				$function = str_replace(']', '', $function);
204
				$hook_exists = !empty($hook_status[$hook][$function]['exists']);
205
206 View Code Duplication
				if (strpos($function, '::') !== false)
207
				{
208
					$function = explode('::', $function);
209
					$function = $function[1];
210
				}
211
212
				$exploded = explode('|', $function);
213
214
				$temp_data[] = array(
215
					'id' => 'hookid_' . ($id++),
216
					'hook_name' => $hook,
217
					'function_name' => $function,
218
					'real_function' => $exploded[0],
219
					'included_file' => isset($exploded[1]) ? parse_path(trim($exploded[1])) : '',
220
					'file_name' => (isset($hook_status[$hook][$function]['in_file']) ? $hook_status[$hook][$function]['in_file'] : ''),
221
					'hook_exists' => $hook_exists,
222
					'status' => $hook_exists ? ($enabled ? 'allow' : 'moderate') : 'deny',
223
					'img_text' => $txt['hooks_' . ($hook_exists ? ($enabled ? 'active' : 'disabled') : 'missing')],
224
					'enabled' => $enabled,
225
					'can_be_disabled' => false,
226
				);
227
228
				// Build the array of sort to values
229
				$sort_end = end($temp_data);
230
				$sort[] = $sort_end[$sort_options[0]];
231
			}
232
		}
233
	}
234
235
	array_multisort($sort, $sort_options[1], $temp_data);
236
237
	$counter = 0;
238
	$start++;
239
240
	foreach ($temp_data as $data)
241
	{
242
		if (++$counter < $start)
243
			continue;
244
		elseif ($counter == $start + $items_per_page)
245
			break;
246
247
		$hooks_data[] = $data;
248
	}
249
250
	return $hooks_data;
251
}
252
253
/**
254
 * Simply returns the total count of integration hooks
255
 *
256
 * What it does:
257
 *
258
 * - used by createList() as a callback to determine the number of hooks in
259
 * use in the system
260
 *
261
 * @package AddonSettings
262
 *
263
 * @param boolean $filter
264
 *
265
 * @return int
266
 */
267
function integration_hooks_count($filter = false)
268
{
269
	$hooks = get_integration_hooks();
270
	$hooks_count = 0;
271
272
	foreach ($hooks as $hook => $functions)
0 ignored issues
show
Bug introduced by
The expression $hooks of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
273
	{
274
		if (empty($filter) || ($filter == $hook))
275
			$hooks_count += count($functions);
276
	}
277
278
	return $hooks_count;
279
}
280
281
/**
282
 * Parses modSettings to create integration hook array
283
 *
284
 * What it does:
285
 *
286
 * - used by createList() callbacks
287
 *
288
 * @package AddonSettings
289
 * @staticvar type $integration_hooks
290
 * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
291
 */
292
function get_integration_hooks()
293
{
294
	global $modSettings;
295
	static $integration_hooks = null;
296
297
	if ($integration_hooks === null)
298
	{
299
		$integration_hooks = array();
300
		foreach ($modSettings as $key => $value)
301
		{
302
			if (!empty($value) && substr($key, 0, 10) === 'integrate_')
303
				$integration_hooks[$key] = explode(',', $value);
304
		}
305
	}
306
307
	return $integration_hooks;
308
}
309