users::admin_users___search_prepare_where()   C
last analyzed

Complexity

Conditions 15
Paths 14

Size

Total Lines 38
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 240

Importance

Changes 0
Metric Value
cc 15
eloc 30
nc 14
nop 4
dl 0
loc 38
ccs 0
cts 31
cp 0
crap 240
rs 5.0504
c 0
b 0
f 0

How to fix   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
 * @package    CleverStyle Framework
4
 * @subpackage System module
5
 * @category   modules
6
 * @author     Nazar Mokrynskyi <[email protected]>
7
 * @license    0BSD
8
 */
9
namespace cs\modules\System\api\Controller\admin;
10
use
11
	cs\ExitException,
12
	cs\Language,
13
	cs\Response,
14
	cs\User;
15
16
trait users {
17
	/**
18
	 * Get user's data or data of several specified groups if specified in ids query parameter or allows to search for users by login or email (users id will
19
	 * be returned)
20
	 *
21
	 * Data will be pre-processed with `reg_date_formatted` and `reg_ip_formatted` keys added
22
	 *
23
	 * @param \cs\Request $Request
24
	 *
25
	 * @return array
26
	 *
27
	 * @throws ExitException
28
	 */
29
	public static function admin_users___get ($Request) {
30
		$User    = User::instance();
31
		$columns = static::admin_users___search_options()['columns'];
32
		$id      = $Request->route_ids(0);
33
		$ids     = $Request->query('ids');
0 ignored issues
show
Bug introduced by
'ids' of type string is incompatible with the type array<mixed,string[]>|string[] expected by parameter $name of cs\Request::query(). ( Ignorable by Annotation )

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

33
		$ids     = $Request->query(/** @scrutinizer ignore-type */ 'ids');
Loading history...
34
		$search  = $Request->query('search');
35
		if ($id) {
36
			$result = static::admin_users___get_post_process(
37
				$User->get($columns, $id)
38
			);
39
		} elseif ($ids) {
40
			$ids    = _int(explode(',', $ids));
0 ignored issues
show
Bug introduced by
It seems like $ids can also be of type array and array; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

40
			$ids    = _int(explode(',', /** @scrutinizer ignore-type */ $ids));
Loading history...
41
			$result = [];
42
			foreach ($ids as $id) {
43
				$result[] = static::admin_users___get_post_process(
44
					$User->get($columns, $id)
45
				);
46
			}
47
		} elseif ($search) {
48
			$result = _int($User->search_users($search));
0 ignored issues
show
Bug introduced by
It seems like $search can also be of type array and array; however, parameter $search_phrase of cs\User::search_users() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

48
			$result = _int($User->search_users(/** @scrutinizer ignore-type */ $search));
Loading history...
49
		} else {
50
			throw new ExitException(400);
51
		}
52
		if (!$result) {
53
			throw new ExitException(404);
54
		}
55
		return $result;
56
	}
57
	protected static function admin_users___get_post_process ($data) {
58
		$L                          = Language::prefix('system_admin_users_');
59
		$data['reg_date_formatted'] = $data['reg_date'] ? date($L->_date, $data['reg_date']) : $L->undefined;
0 ignored issues
show
Bug Best Practice introduced by
The property undefined does not exist on cs\Language\Prefix. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property _date does not exist on cs\Language\Prefix. Since you implemented __get, consider adding a @property annotation.
Loading history...
60
		$data['reg_ip_formatted']   = hex2ip($data['reg_ip'], 10);
61
		return $data;
62
	}
63
	/**
64
	 * Update user's data
65
	 *
66
	 * @param \cs\Request $Request
67
	 *
68
	 * @throws ExitException
69
	 */
70
	public static function admin_users___patch ($Request) {
71
		$user_id = (int)$Request->route_ids(0);
72
		$user    = $Request->data('user');
0 ignored issues
show
Bug introduced by
'user' of type string is incompatible with the type array<mixed,string[]>|string[] expected by parameter $name of cs\Request::data(). ( Ignorable by Annotation )

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

72
		$user    = $Request->data(/** @scrutinizer ignore-type */ 'user');
Loading history...
73
		if (!$user_id || !$user) {
74
			throw new ExitException(400);
75
		}
76
		$User      = User::instance();
77
		$user_data = array_filter(
78
			$user,
79
			function ($item) {
80
				return in_array($item, ['login', 'username', 'email', 'language', 'timezone', 'status', 'avatar'], true);
81
			},
82
			ARRAY_FILTER_USE_KEY
83
		);
84
		foreach ($user_data as &$d) {
85
			$d = xap($d, false);
86
		}
87
		unset($d);
88
		if (!$user_data && !isset($user['password'])) {
89
			throw new ExitException(400);
90
		}
91
		$L = Language::prefix('system_admin_users_');
92
		if (
93
			isset($user_data['login']) &&
94
			$user_data['login'] !== $User->get('login', $user_id) &&
95
			$User->get_id(hash('sha224', $user_data['login']))
96
		) {
97
			throw new ExitException($L->login_occupied, 400);
0 ignored issues
show
Bug Best Practice introduced by
The property login_occupied does not exist on cs\Language\Prefix. Since you implemented __get, consider adding a @property annotation.
Loading history...
98
		}
99
		if (
100
			isset($user_data['email']) &&
101
			$user_data['email'] !== $User->get('email', $user_id) &&
102
			$User->get_id(hash('sha224', $user_data['email']))
103
		) {
104
			throw new ExitException($L->email_occupied, 400);
0 ignored issues
show
Bug Best Practice introduced by
The property email_occupied does not exist on cs\Language\Prefix. Since you implemented __get, consider adding a @property annotation.
Loading history...
105
		}
106
		if (!$User->set($user_data, null, $user_id)) {
107
			throw new ExitException(500);
108
		}
109
		if (isset($user['password']) && !$User->set_password($user['password'], $user_id)) {
110
			throw new ExitException(500);
111
		}
112
	}
113
	/**
114
	 * Add new user
115
	 *
116
	 * @param \cs\Request $Request
117
	 *
118
	 * @return array
119
	 *
120
	 * @throws ExitException
121
	 */
122
	public static function admin_users___post ($Request) {
123
		$User  = User::instance();
124
		$email = $Request->data('email');
0 ignored issues
show
Bug introduced by
'email' of type string is incompatible with the type array<mixed,string[]>|string[] expected by parameter $name of cs\Request::data(). ( Ignorable by Annotation )

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

124
		$email = $Request->data(/** @scrutinizer ignore-type */ 'email');
Loading history...
125
		if (!$email) {
126
			throw new ExitException(400);
127
		}
128
		$result = $User->registration($email, false, false);
0 ignored issues
show
Bug introduced by
It seems like $email can also be of type array and array; however, parameter $email of cs\User::registration() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

128
		$result = $User->registration(/** @scrutinizer ignore-type */ $email, false, false);
Loading history...
129
		if (!$result) {
130
			throw new ExitException(500);
131
		}
132
		if ($result === 'exists') {
133
			$L = Language::prefix('system_admin_users_');
134
			throw new ExitException($L->user_already_exists, 400);
0 ignored issues
show
Bug Best Practice introduced by
The property user_already_exists does not exist on cs\Language\Prefix. Since you implemented __get, consider adding a @property annotation.
Loading history...
135
		}
136
		Response::instance()->code = 201;
0 ignored issues
show
Bug introduced by
The property code does not seem to exist on cs\False_class.
Loading history...
137
		return [
138
			'login'    => $User->get('login', $result['id']),
139
			'password' => $result['password']
140
		];
141
	}
142
	/**
143
	 * Advanced search for users (users data will be returned similar to GET method)
144
	 *
145
	 * @param \cs\Request $Request
146
	 *
147
	 * @return array
148
	 *
149
	 * @throws ExitException
150
	 */
151
	public static function admin_users___search ($Request) {
152
		$options = $Request->data('mode', 'column', 'text', 'page', 'limit');
0 ignored issues
show
Bug introduced by
'mode' of type string is incompatible with the type array<mixed,string[]>|string[] expected by parameter $name of cs\Request::data(). ( Ignorable by Annotation )

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

152
		$options = $Request->data(/** @scrutinizer ignore-type */ 'mode', 'column', 'text', 'page', 'limit');
Loading history...
153
		if (!$options) {
154
			throw new ExitException(400);
155
		}
156
		$mode           = $options['mode'];
157
		$column         = $options['column'];
158
		$text           = $options['text'];
159
		$page           = (int)$options['page'];
160
		$limit          = (int)$options['limit'];
161
		$search_options = static::admin_users___search_options();
162
		if (
163
			!in_array($mode, $search_options['modes']) ||
164
			(
165
				$column !== '' &&
166
				!in_array($column, $search_options['columns'])
167
			)
168
		) {
169
			throw new ExitException(400);
170
		}
171
		$cdb   = User::instance()->db();
172
		$where = static::admin_users___search_prepare_where($mode, $text, $column ?: $search_options['columns'], $cdb);
173
		$count = $cdb->qfs(
174
			"SELECT COUNT(`id`)
0 ignored issues
show
Bug introduced by
'SELECT COUNT(`id`) F...sers` WHERE '.$where of type string is incompatible with the type string[] expected by parameter $query of cs\DB\_Abstract::qfs(). ( Ignorable by Annotation )

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

174
			/** @scrutinizer ignore-type */ "SELECT COUNT(`id`)
Loading history...
175
			FROM `[prefix]users`
176
			WHERE $where"
177
		);
178
		if (!$count) {
179
			throw new ExitException(404);
180
		}
181
		$where = str_replace('%', '%%', $where);
182
		$ids   = $cdb->qfas(
183
			"SELECT `id`
0 ignored issues
show
Bug introduced by
'SELECT `id` FROM `[p... LIMIT %d OFFSET %d' of type string is incompatible with the type string[] expected by parameter $query of cs\DB\_Abstract::qfas(). ( Ignorable by Annotation )

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

183
			/** @scrutinizer ignore-type */ "SELECT `id`
Loading history...
184
			FROM `[prefix]users`
185
			WHERE $where
186
			ORDER BY `id`
187
			LIMIT %d OFFSET %d",
188
			$limit,
0 ignored issues
show
Bug introduced by
$limit of type integer is incompatible with the type string[] expected by parameter $query of cs\DB\_Abstract::qfas(). ( Ignorable by Annotation )

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

188
			/** @scrutinizer ignore-type */ $limit,
Loading history...
189
			($page - 1) * $limit
190
		);
191
		return [
192
			'count' => $count,
193
			'users' => static::admin_users___search_get($ids, $search_options['columns'])
194
		];
195
	}
196
	/**
197
	 * @param string           $mode
198
	 * @param string           $text
199
	 * @param string|string[]  $column
200
	 * @param \cs\DB\_Abstract $cdb
201
	 *
202
	 * @return string
203
	 */
204
	protected static function admin_users___search_prepare_where ($mode, $text, $column, $cdb) {
205
		$where = '1';
206
		if ($text && $mode) {
207
			switch ($mode) {
208
				case '=':
209
				case '!=':
210
				case '>':
211
				case '<':
212
				case '>=':
213
				case '<=':
214
				case 'LIKE':
215
				case 'NOT LIKE':
216
				case 'REGEXP':
217
				case 'NOT REGEXP':
218
					$where = static::admin_users___search_prepare_where_compose(
219
						"`%s` $mode %s",
220
						$column,
221
						$cdb->s($text)
222
					);
223
					break;
224
				case 'IN':
225
				case 'NOT IN':
226
					$where = static::admin_users___search_prepare_where_compose(
227
						"`%s` $mode (%s)",
228
						$column,
229
						implode(
230
							', ',
231
							$cdb->s(
0 ignored issues
show
Bug introduced by
$cdb->s(_trim(_trim(explode(',', $text), '''))) of type string is incompatible with the type array expected by parameter $pieces of implode(). ( Ignorable by Annotation )

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

231
							/** @scrutinizer ignore-type */ $cdb->s(
Loading history...
232
								_trim(
233
									_trim(explode(',', $text), "'")
234
								)
235
							)
236
						)
237
					);
238
					break;
239
			}
240
		}
241
		return $where;
242
	}
243
	/**
244
	 * @param string          $where
245
	 * @param string|string[] $column
246
	 * @param string          $text
247
	 *
248
	 * @return string
249
	 */
250
	protected static function admin_users___search_prepare_where_compose ($where, $column, $text) {
251
		if (is_array($column)) {
252
			$return = [];
253
			foreach ($column as $c) {
254
				$return[] = sprintf($where, $c, $text);
255
			}
256
			return '('.implode(' OR ', $return).')';
257
		}
258
		return sprintf($where, $column, $text);
259
	}
260
	/**
261
	 * @param int[]    $users
262
	 * @param string[] $columns
263
	 *
264
	 * @return array[]
265
	 */
266
	protected static function admin_users___search_get ($users, $columns) {
267
		$User = User::instance();
268
		foreach ($users as &$user) {
269
			$groups         = (array)$User->get_groups($user);
270
			$user           =
271
				$User->get($columns, $user) +
272
				[
273
					'is_user'  => in_array(User::USER_GROUP_ID, $groups),
274
					'is_admin' => in_array(User::ADMIN_GROUP_ID, $groups),
275
					'username' => $User->username($user)
276
				];
277
			$user['reg_ip'] = hex2ip($user['reg_ip'], 10);
278
		}
279
		return $users;
280
	}
281
	/**
282
	 * Get available search options
283
	 *
284
	 * @return string[][]
285
	 */
286
	public static function admin_users___search_options () {
287
		return [
288
			'modes'   => [
289
				'=',
290
				'!=',
291
				'>',
292
				'<',
293
				'>=',
294
				'<=',
295
				'LIKE',
296
				'NOT LIKE',
297
				'IN',
298
				'NOT IN',
299
				'IS NULL',
300
				'IS NOT NULL',
301
				'REGEXP',
302
				'NOT REGEXP'
303
			],
304
			'columns' => array_values(
305
				array_filter(
306
					User::instance()->get_users_columns(),
307
					function ($column) {
308
						return $column !== 'password_hash';
309
					}
310
				)
311
			)
312
		];
313
	}
314
}
315