These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Elgg\Database; |
||
4 | |||
5 | use Elgg\Cache\EntityCache; |
||
6 | use Elgg\Config as Conf; |
||
7 | use Elgg\Database; |
||
8 | use Elgg\Database\EntityTable; |
||
9 | use Elgg\EventsService; |
||
10 | use ElggUser; |
||
11 | use RegistrationException; |
||
12 | |||
13 | /** |
||
14 | * WARNING: API IN FLUX. DO NOT USE DIRECTLY. |
||
15 | * |
||
16 | * @access private |
||
17 | * |
||
18 | * @package Elgg.Core |
||
19 | * @subpackage Database |
||
20 | * @since 1.10.0 |
||
21 | */ |
||
22 | class UsersTable { |
||
23 | |||
24 | use \Elgg\TimeUsing; |
||
25 | |||
26 | /** |
||
27 | * @var Conf |
||
28 | */ |
||
29 | protected $config; |
||
30 | |||
31 | /** |
||
32 | * @var Database |
||
33 | */ |
||
34 | protected $db; |
||
35 | |||
36 | /** |
||
37 | * @var EntityTable |
||
38 | */ |
||
39 | protected $entities; |
||
40 | |||
41 | /** |
||
42 | * @var EntityCache |
||
43 | */ |
||
44 | protected $entity_cache; |
||
45 | |||
46 | /** |
||
47 | * @var EventsService |
||
48 | */ |
||
49 | protected $events; |
||
50 | |||
51 | /** |
||
52 | * @var string |
||
53 | */ |
||
54 | protected $table; |
||
55 | |||
56 | /** |
||
57 | * Constructor |
||
58 | * |
||
59 | * @param Conf $config Config |
||
60 | * @param Database $db Database |
||
61 | * @param EntityTable $entities Entity table |
||
62 | * @param EntityCache $cache Entity cache |
||
63 | * @param EventsService $events Event service |
||
64 | */ |
||
65 | 197 | View Code Duplication | public function __construct( |
66 | Conf $config, Database $db, EntityTable $entities, EntityCache $cache, EventsService $events |
||
67 | ) { |
||
68 | 197 | $this->config = $config; |
|
69 | 197 | $this->db = $db; |
|
70 | 197 | $this->table = $this->db->prefix . "users_entity"; |
|
71 | 197 | $this->entities = $entities; |
|
72 | 197 | $this->entity_cache = $cache; |
|
73 | 197 | $this->events = $events; |
|
74 | 197 | } |
|
75 | |||
76 | /** |
||
77 | * Return the user specific details of a user by a row. |
||
78 | * |
||
79 | * @param int $guid The \ElggUser guid |
||
80 | * |
||
81 | * @return mixed |
||
82 | * @access private |
||
83 | */ |
||
84 | public function getRow($guid) { |
||
85 | $sql = " |
||
86 | SELECT * FROM {$this->table} |
||
87 | WHERE guid = :guid |
||
88 | "; |
||
89 | $params = [ |
||
90 | ':guid' => $guid, |
||
91 | ]; |
||
92 | return $this->db->getDataRow($sql, null, $params); |
||
93 | } |
||
94 | |||
95 | /** |
||
96 | * Disables all of a user's entities |
||
97 | * |
||
98 | * @param int $owner_guid The owner GUID |
||
99 | * @return bool Depending on success |
||
100 | * @deprecated 2.3 |
||
101 | */ |
||
102 | public function disableEntities($owner_guid) { |
||
103 | return $this->entities->disableEntities($owner_guid); |
||
104 | } |
||
105 | |||
106 | /** |
||
107 | * Ban a user (calls events, stores the reason) |
||
108 | * |
||
109 | * @param int $user_guid The user guid |
||
110 | * @param string $reason A reason |
||
111 | * @return bool |
||
112 | */ |
||
113 | public function ban($user_guid, $reason = "") { |
||
114 | |||
115 | $user = get_entity($user_guid); |
||
116 | |||
117 | if (!$user instanceof ElggUser || !$user->canEdit()) { |
||
118 | return false; |
||
119 | } |
||
120 | |||
121 | if (!$this->events->trigger('ban', 'user', $user)) { |
||
122 | return false; |
||
123 | } |
||
124 | |||
125 | $user->ban_reason = $reason; |
||
126 | |||
127 | _elgg_invalidate_cache_for_entity($user_guid); |
||
128 | _elgg_invalidate_memcache_for_entity($user_guid); |
||
129 | |||
130 | if ($this->markBanned($user_guid, true)) { |
||
131 | return true; |
||
132 | } |
||
133 | |||
134 | return false; |
||
135 | } |
||
136 | |||
137 | /** |
||
138 | * Mark a user entity banned or unbanned. |
||
139 | * |
||
140 | * @note Use ban() or unban() |
||
141 | * |
||
142 | * @param int $guid User GUID |
||
143 | * @param bool $banned Mark the user banned? |
||
144 | * @return int Num rows affected |
||
145 | */ |
||
146 | public function markBanned($guid, $banned) { |
||
147 | |||
148 | $query = " |
||
149 | UPDATE {$this->table} |
||
150 | SET banned = :banned |
||
151 | WHERE guid = :guid |
||
152 | "; |
||
153 | |||
154 | $params = [ |
||
155 | ':banned' => $banned ? 'yes' : 'no', |
||
156 | ':guid' => (int) $guid, |
||
157 | ]; |
||
158 | |||
159 | return $this->db->updateData($query, true, $params); |
||
160 | } |
||
161 | |||
162 | /** |
||
163 | * Unban a user (calls events, removes the reason) |
||
164 | * |
||
165 | * @param int $user_guid Unban a user |
||
166 | * @return bool |
||
167 | */ |
||
168 | public function unban($user_guid) { |
||
169 | |||
170 | $user = get_entity($user_guid); |
||
171 | |||
172 | if (!$user instanceof ElggUser || !$user->canEdit()) { |
||
173 | return false; |
||
174 | } |
||
175 | |||
176 | if (!$this->events->trigger('unban', 'user', $user)) { |
||
177 | return false; |
||
178 | } |
||
179 | |||
180 | $user->deleteMetadata('ban_reason'); |
||
181 | |||
182 | _elgg_invalidate_cache_for_entity($user_guid); |
||
183 | _elgg_invalidate_memcache_for_entity($user_guid); |
||
184 | |||
185 | return $this->markBanned($user_guid, false); |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Makes user $guid an admin. |
||
190 | * |
||
191 | * @param int $user_guid User guid |
||
192 | * @return bool |
||
193 | */ |
||
194 | View Code Duplication | public function makeAdmin($user_guid) { |
|
195 | $user = get_entity($user_guid); |
||
196 | |||
197 | if (!$user instanceof ElggUser || !$user->canEdit()) { |
||
198 | return false; |
||
199 | } |
||
200 | |||
201 | if (!$this->events->trigger('make_admin', 'user', $user)) { |
||
202 | return false; |
||
203 | } |
||
204 | |||
205 | $query = " |
||
206 | UPDATE {$this->table} |
||
207 | SET admin = 'yes' |
||
208 | WHERE guid = :guid |
||
209 | "; |
||
210 | |||
211 | $params = [ |
||
212 | ':guid' => (int) $user_guid, |
||
213 | ]; |
||
214 | |||
215 | _elgg_invalidate_cache_for_entity($user_guid); |
||
216 | _elgg_invalidate_memcache_for_entity($user_guid); |
||
217 | |||
218 | if ($this->db->updateData($query, true, $params)) { |
||
219 | return true; |
||
220 | } |
||
221 | |||
222 | return false; |
||
223 | } |
||
224 | |||
225 | /** |
||
226 | * Removes user $guid's admin flag. |
||
227 | * |
||
228 | * @param int $user_guid User GUID |
||
229 | * @return bool |
||
230 | */ |
||
231 | View Code Duplication | public function removeAdmin($user_guid) { |
|
232 | |||
233 | $user = get_entity($user_guid); |
||
234 | |||
235 | if (!$user instanceof ElggUser || !$user->canEdit()) { |
||
236 | return false; |
||
237 | } |
||
238 | |||
239 | if (!$this->events->trigger('remove_admin', 'user', $user)) { |
||
240 | return false; |
||
241 | } |
||
242 | |||
243 | $query = " |
||
244 | UPDATE {$this->table} |
||
245 | SET admin = 'no' |
||
246 | WHERE guid = :guid |
||
247 | "; |
||
248 | |||
249 | $params = [ |
||
250 | ':guid' => (int) $user_guid, |
||
251 | ]; |
||
252 | |||
253 | _elgg_invalidate_cache_for_entity($user_guid); |
||
254 | _elgg_invalidate_memcache_for_entity($user_guid); |
||
255 | |||
256 | if ($this->db->updateData($query, true, $params)) { |
||
257 | return true; |
||
258 | } |
||
259 | |||
260 | return false; |
||
261 | } |
||
262 | |||
263 | /** |
||
264 | * Get user by username |
||
265 | * |
||
266 | * @param string $username The user's username |
||
267 | * |
||
268 | * @return ElggUser|false Depending on success |
||
269 | */ |
||
270 | public function getByUsername($username) { |
||
271 | |||
272 | // Fixes #6052. Username is frequently sniffed from the path info, which, |
||
273 | // unlike $_GET, is not URL decoded. If the username was not URL encoded, |
||
274 | // this is harmless. |
||
275 | $username = rawurldecode($username); |
||
276 | |||
277 | if (!$username) { |
||
278 | return false; |
||
279 | } |
||
280 | |||
281 | $entity = $this->entity_cache->getByUsername($username); |
||
282 | if ($entity) { |
||
283 | return $entity; |
||
284 | } |
||
285 | |||
286 | $users = $this->entities->getEntitiesFromAttributes([ |
||
287 | 'types' => 'user', |
||
288 | 'attribute_name_value_pairs' => [ |
||
289 | 'name' => 'username', |
||
290 | 'value' => $username, |
||
291 | ], |
||
292 | 'limit' => 1, |
||
293 | ]); |
||
294 | return $users ? $users[0] : false; |
||
295 | } |
||
296 | |||
297 | /** |
||
298 | * Get an array of users from an email address |
||
299 | * |
||
300 | * @param string $email Email address |
||
301 | * @return array |
||
302 | */ |
||
303 | public function getByEmail($email) { |
||
304 | if (!$email) { |
||
305 | return []; |
||
306 | } |
||
307 | |||
308 | $users = $this->entities->getEntitiesFromAttributes([ |
||
309 | 'types' => 'user', |
||
310 | 'attribute_name_value_pairs' => [ |
||
311 | 'name' => 'email', |
||
312 | 'value' => $email, |
||
313 | ], |
||
314 | 'limit' => 1, |
||
315 | ]); |
||
316 | |||
317 | return $users ? : []; |
||
318 | } |
||
319 | |||
320 | /** |
||
321 | * Return users (or the number of them) who have been active within a recent period. |
||
322 | * |
||
323 | * @param array $options Array of options with keys: |
||
324 | * |
||
325 | * seconds (int) => Length of period (default 600 = 10min) |
||
326 | * limit (int) => Limit (default 10) |
||
327 | * offset (int) => Offset (default 0) |
||
328 | * count (bool) => Return a count instead of users? (default false) |
||
329 | * |
||
330 | * @return \ElggUser[]|int |
||
331 | */ |
||
332 | public function findActive(array $options = []) { |
||
333 | |||
334 | $options = array_merge([ |
||
335 | 'seconds' => 600, |
||
336 | 'limit' => $this->config->default_limit, |
||
337 | ], $options); |
||
338 | |||
339 | // cast options we're sending to hook |
||
340 | foreach (['seconds', 'limit', 'offset'] as $key) { |
||
341 | $options[$key] = (int) $options[$key]; |
||
342 | } |
||
343 | $options['count'] = (bool) $options['count']; |
||
344 | |||
345 | // allow plugins to override |
||
346 | $params = [ |
||
347 | 'seconds' => $options['seconds'], |
||
348 | 'limit' => $options['limit'], |
||
349 | 'offset' => $options['offset'], |
||
350 | 'count' => $options['count'], |
||
351 | 'options' => $options, |
||
352 | ]; |
||
353 | $data = _elgg_services()->hooks->trigger('find_active_users', 'system', $params, null); |
||
354 | // check null because the handler could legitimately return falsey values. |
||
355 | if ($data !== null) { |
||
356 | return $data; |
||
357 | } |
||
358 | |||
359 | $dbprefix = $this->config->dbprefix; |
||
360 | $time = $this->getCurrentTime()->getTimestamp() - $options['seconds']; |
||
361 | return elgg_get_entities([ |
||
0 ignored issues
–
show
|
|||
362 | 'type' => 'user', |
||
363 | 'limit' => $options['limit'], |
||
364 | 'offset' => $options['offset'], |
||
365 | 'count' => $options['count'], |
||
366 | 'joins' => ["join {$dbprefix}users_entity u on e.guid = u.guid"], |
||
367 | 'wheres' => ["u.last_action >= {$time}"], |
||
368 | 'order_by' => "u.last_action desc", |
||
369 | ]); |
||
370 | } |
||
371 | |||
372 | /** |
||
373 | * Registers a user, returning false if the username already exists |
||
374 | * |
||
375 | * @param string $username The username of the new user |
||
376 | * @param string $password The password |
||
377 | * @param string $name The user's display name |
||
378 | * @param string $email The user's email address |
||
379 | * @param bool $allow_multiple_emails Allow the same email address to be |
||
380 | * registered multiple times? |
||
381 | * |
||
382 | * @return int|false The new user's GUID; false on failure |
||
383 | * @throws RegistrationException |
||
384 | */ |
||
385 | public function register($username, $password, $name, $email, $allow_multiple_emails = false) { |
||
386 | |||
387 | // no need to trim password |
||
388 | $username = trim($username); |
||
389 | $name = trim(strip_tags($name)); |
||
390 | $email = trim($email); |
||
391 | |||
392 | // A little sanity checking |
||
393 | if (empty($username) || empty($password) || empty($name) || empty($email)) { |
||
394 | return false; |
||
395 | } |
||
396 | |||
397 | // Make sure a user with conflicting details hasn't registered and been disabled |
||
398 | $access_status = access_get_show_hidden_status(); |
||
399 | access_show_hidden_entities(true); |
||
400 | |||
401 | if (!validate_email_address($email)) { |
||
402 | throw new RegistrationException(_elgg_services()->translator->translate('registration:emailnotvalid')); |
||
403 | } |
||
404 | |||
405 | if (!validate_password($password)) { |
||
406 | throw new RegistrationException(_elgg_services()->translator->translate('registration:passwordnotvalid')); |
||
407 | } |
||
408 | |||
409 | if (!validate_username($username)) { |
||
410 | throw new RegistrationException(_elgg_services()->translator->translate('registration:usernamenotvalid')); |
||
411 | } |
||
412 | |||
413 | if ($user = get_user_by_username($username)) { |
||
0 ignored issues
–
show
$user is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
414 | throw new RegistrationException(_elgg_services()->translator->translate('registration:userexists')); |
||
415 | } |
||
416 | |||
417 | if ((!$allow_multiple_emails) && (get_user_by_email($email))) { |
||
418 | throw new RegistrationException(_elgg_services()->translator->translate('registration:dupeemail')); |
||
419 | } |
||
420 | |||
421 | access_show_hidden_entities($access_status); |
||
422 | |||
423 | // Create user |
||
424 | $user = new ElggUser(); |
||
425 | $user->username = $username; |
||
426 | $user->email = $email; |
||
427 | $user->name = $name; |
||
428 | $user->access_id = ACCESS_PUBLIC; |
||
429 | $user->setPassword($password); |
||
430 | $user->owner_guid = 0; // Users aren't owned by anyone, even if they are admin created. |
||
431 | $user->container_guid = 0; // Users aren't contained by anyone, even if they are admin created. |
||
432 | $user->language = _elgg_services()->translator->getCurrentLanguage(); |
||
433 | if ($user->save() === false) { |
||
434 | return false; |
||
435 | } |
||
436 | |||
437 | // Turn on email notifications by default |
||
438 | $user->setNotificationSetting('email', true); |
||
439 | |||
440 | return $user->getGUID(); |
||
441 | } |
||
442 | |||
443 | /** |
||
444 | * Generates a unique invite code for a user |
||
445 | * |
||
446 | * @param string $username The username of the user sending the invitation |
||
447 | * |
||
448 | * @return string Invite code |
||
449 | * @see validateInviteCode |
||
450 | */ |
||
451 | public function generateInviteCode($username) { |
||
452 | $time = $this->getCurrentTime()->getTimestamp(); |
||
453 | return "$time." . _elgg_services()->hmac->getHmac([(int) $time, $username])->getToken(); |
||
454 | } |
||
455 | |||
456 | /** |
||
457 | * Validate a user's invite code |
||
458 | * |
||
459 | * @param string $username The username |
||
460 | * @param string $code The invite code |
||
461 | * |
||
462 | * @return bool |
||
463 | * @see generateInviteCode |
||
464 | */ |
||
465 | public function validateInviteCode($username, $code) { |
||
466 | // validate the format of the token created by ->generateInviteCode() |
||
467 | if (!preg_match('~^(\d+)\.([a-zA-Z0-9\-_]+)$~', $code, $m)) { |
||
468 | return false; |
||
469 | } |
||
470 | $time = $m[1]; |
||
471 | $mac = $m[2]; |
||
472 | |||
473 | return _elgg_services()->hmac->getHmac([(int) $time, $username])->matchesToken($mac); |
||
474 | } |
||
475 | |||
476 | /** |
||
477 | * Set the validation status for a user. |
||
478 | * |
||
479 | * @param int $user_guid The user's GUID |
||
480 | * @param bool $status Validated (true) or unvalidated (false) |
||
481 | * @param string $method Optional method to say how a user was validated |
||
482 | * @return bool |
||
483 | */ |
||
484 | public function setValidationStatus($user_guid, $status, $method = '') { |
||
485 | $user = get_user($user_guid); |
||
486 | if (!$user) { |
||
487 | return false; |
||
488 | } |
||
489 | |||
490 | $result1 = create_metadata($user->guid, 'validated', (int) $status); |
||
491 | $result2 = create_metadata($user->guid, 'validated_method', $method); |
||
492 | if ($result1 && $result2) { |
||
0 ignored issues
–
show
The expression
$result1 of type false|integer is loosely compared to true ; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
![]() The expression
$result2 of type false|integer is loosely compared to true ; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
![]() |
|||
493 | if ((bool) $status) { |
||
494 | elgg_trigger_after_event('validate', 'user', $user); |
||
495 | } else { |
||
496 | elgg_trigger_after_event('invalidate', 'user', $user); |
||
497 | } |
||
498 | return true; |
||
499 | } else { |
||
500 | return false; |
||
501 | } |
||
502 | } |
||
503 | |||
504 | /** |
||
505 | * Gets the validation status of a user. |
||
506 | * |
||
507 | * @param int $user_guid The user's GUID |
||
508 | * @return bool|null Null means status was not set for this user. |
||
509 | */ |
||
510 | public function getValidationStatus($user_guid) { |
||
511 | $user = get_entity($user_guid); |
||
512 | if (!$user || !isset($user->validated)) { |
||
513 | return null; |
||
514 | } |
||
515 | return (bool) $user->validated; |
||
516 | } |
||
517 | |||
518 | /** |
||
519 | * Sets the last action time of the given user to right now. |
||
520 | * |
||
521 | * @see _elgg_session_boot The session boot calls this at the beginning of every request |
||
522 | * |
||
523 | * @param ElggUser $user User entity |
||
524 | * @return void |
||
525 | */ |
||
526 | public function setLastAction(ElggUser $user) { |
||
527 | |||
528 | $time = $this->getCurrentTime()->getTimestamp(); |
||
529 | |||
530 | if ($user->last_action == $time) { |
||
531 | // no change required |
||
532 | return; |
||
533 | } |
||
534 | |||
535 | $query = " |
||
536 | UPDATE {$this->table} |
||
537 | SET |
||
538 | prev_last_action = last_action, |
||
539 | last_action = :last_action |
||
540 | WHERE guid = :guid |
||
541 | "; |
||
542 | |||
543 | $params = [ |
||
544 | ':last_action' => $time, |
||
545 | ':guid' => (int) $user->guid, |
||
546 | ]; |
||
547 | |||
548 | // these writes actually work, we just type hint read-only. |
||
549 | $user->prev_last_action = $user->last_action; |
||
550 | $user->last_action = $time; |
||
551 | |||
552 | execute_delayed_write_query($query, null, $params); |
||
553 | |||
554 | $this->entity_cache->set($user); |
||
555 | |||
556 | // If we save the user to memcache during this request, then we'll end up with the |
||
557 | // old (incorrect) attributes cached (notice the above query is delayed). So it's |
||
558 | // simplest to just resave the user after all plugin code runs. |
||
559 | register_shutdown_function(function () use ($user, $time) { |
||
560 | $this->entities->updateLastAction($user, $time); // keep entity table in sync |
||
561 | $user->storeInPersistedCache(_elgg_get_memcache('new_entity_cache'), $time); |
||
562 | }); |
||
563 | } |
||
564 | |||
565 | /** |
||
566 | * Sets the last logon time of the given user to right now. |
||
567 | * |
||
568 | * @param ElggUser $user User entity |
||
569 | * @return void |
||
570 | */ |
||
571 | public function setLastLogin(ElggUser $user) { |
||
572 | |||
573 | $time = $this->getCurrentTime()->getTimestamp(); |
||
574 | |||
575 | if ($user->last_login == $time) { |
||
576 | // no change required |
||
577 | return; |
||
578 | } |
||
579 | |||
580 | $query = " |
||
581 | UPDATE {$this->table} |
||
582 | SET |
||
583 | prev_last_login = last_login, |
||
584 | last_login = :last_login |
||
585 | WHERE guid = :guid |
||
586 | "; |
||
587 | |||
588 | $params = [ |
||
589 | ':last_login' => $time, |
||
590 | ':guid' => (int) $user->guid, |
||
591 | ]; |
||
592 | |||
593 | // these writes actually work, we just type hint read-only. |
||
594 | $user->prev_last_login = $user->last_login; |
||
595 | $user->last_login = $time; |
||
596 | |||
597 | execute_delayed_write_query($query, null, $params); |
||
598 | |||
599 | $this->entity_cache->set($user); |
||
600 | |||
601 | // If we save the user to memcache during this request, then we'll end up with the |
||
602 | // old (incorrect) attributes cached. Hence we want to invalidate as late as possible. |
||
603 | // the user object gets saved |
||
604 | register_shutdown_function(function () use ($user) { |
||
605 | $user->storeInPersistedCache(_elgg_get_memcache('new_entity_cache')); |
||
606 | }); |
||
607 | } |
||
608 | } |
||
609 |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.