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

engine/classes/Elgg/Database/UsersTable.php (1 issue)

Checks if the types of returned expressions are compatible with the documented types.

Best Practice Bug Major
1
<?php
2
3
namespace Elgg\Database;
4
5
use Elgg\Config as Conf;
6
use Elgg\Database;
7
use ElggUser;
8
use RegistrationException;
9
10
/**
11
 * WARNING: API IN FLUX. DO NOT USE DIRECTLY.
12
 *
13
 * @access private
14
 *
15
 * @package    Elgg.Core
16
 * @subpackage Database
17
 * @since      1.10.0
18
 */
19
class UsersTable {
20
21
	use \Elgg\TimeUsing;
22
23
	/**
24
	 * @var Conf
25
	 */
26
	protected $config;
27
28
	/**
29
	 * @var Database
30
	 */
31
	protected $db;
32
33
	/**
34
	 * @var MetadataTable
35
	 */
36
	protected $metadata;
37
38
	/**
39
	 * Constructor
40
	 *
41
	 * @param Conf          $config   Config
42
	 * @param Database      $db       Database
43
	 * @param MetadataTable $metadata Metadata table
44
	 */
45 4419
	public function __construct(Conf $config, Database $db, MetadataTable $metadata) {
46 4419
		$this->config = $config;
47 4419
		$this->db = $db;
48 4419
		$this->metadata = $metadata;
49 4419
	}
50
51
	/**
52
	 * Get user by username
53
	 *
54
	 * @param string $username The user's username
55
	 *
56
	 * @return ElggUser|false Depending on success
57
	 */
58 459
	public function getByUsername($username) {
59
60
		// Fixes #6052. Username is frequently sniffed from the path info, which,
61
		// unlike $_GET, is not URL decoded. If the username was not URL encoded,
62
		// this is harmless.
63 459
		$username = rawurldecode($username);
64
65 459
		if (!$username) {
66
			return false;
67
		}
68
69 459
		$entity =_elgg_services()->dataCache->usernames->load($username);
70 459
		if ($entity instanceof ElggUser) {
71 8
			return $entity;
72
		}
73
74 459
		$users = elgg_get_entities([
75 459
			'types' => 'user',
76
			'metadata_name_value_pairs' => [
77
				[
78 459
					'name' => 'username',
79 459
					'value' => $username,
80
				],
81
			],
82 459
			'limit' => 1,
83
		]);
84
		
85 459
		return $users ? $users[0] : false;
86
	}
87
88
	/**
89
	 * Get an array of users from an email address
90
	 *
91
	 * @param string $email Email address
92
	 * @return array
93
	 */
94 162
	public function getByEmail($email) {
95 162
		if (!$email) {
96
			return [];
97
		}
98
		
99 162
		$users = elgg_get_entities([
100 162
			'types' => 'user',
101
			'metadata_name_value_pairs' => [
102
				[
103 162
					'name' => 'email',
104 162
					'value' => $email,
105
				],
106
			],
107 162
			'limit' => 1,
108
		]);
109
110 162
		return $users ? : [];
0 ignored issues
show
Bug Best Practice introduced by Ismayil Khayredinov
The expression return $users ?: array() also could return the type integer which is incompatible with the documented return type array.
Loading history...
111
	}
112
113
	/**
114
	 * Return users (or the number of them) who have been active within a recent period.
115
	 *
116
	 * @param array $options Array of options with keys:
117
	 *
118
	 *   seconds (int)  => Length of period (default 600 = 10min)
119
	 *   limit   (int)  => Limit (default 10)
120
	 *   offset  (int)  => Offset (default 0)
121
	 *   count   (bool) => Return a count instead of users? (default false)
122
	 *
123
	 * @return \ElggUser[]|int
124
	 */
125 1
	public function findActive(array $options = []) {
126
	
127 1
		$options = array_merge([
128 1
			'seconds' => 600,
129 1
			'limit' => $this->config->default_limit,
130 1
		], $options);
131
132
		// cast options we're sending to hook
133 1
		foreach (['seconds', 'limit', 'offset'] as $key) {
134 1
			$options[$key] = (int) $options[$key];
135
		}
136 1
		$options['count'] = (bool) $options['count'];
137
138
		// allow plugins to override
139
		$params = [
140 1
			'seconds' => $options['seconds'],
141 1
			'limit' => $options['limit'],
142 1
			'offset' => $options['offset'],
143 1
			'count' => $options['count'],
144 1
			'options' => $options,
145
		];
146 1
		$data = _elgg_services()->hooks->trigger('find_active_users', 'system', $params, null);
147
		// check null because the handler could legitimately return falsey values.
148 1
		if ($data !== null) {
149
			return $data;
150
		}
151
152 1
		$time = $this->getCurrentTime()->getTimestamp() - $options['seconds'];
153 1
		return elgg_get_entities([
154 1
			'type' => 'user',
155 1
			'limit' => $options['limit'],
156 1
			'offset' => $options['offset'],
157 1
			'count' => $options['count'],
158 1
			'wheres' => ["e.last_action >= {$time}"],
159 1
			'order_by' => "e.last_action desc",
160
		]);
161
	}
162
163
	/**
164
	 * Registers a user, returning false if the username already exists
165
	 *
166
	 * @param string $username              The username of the new user
167
	 * @param string $password              The password
168
	 * @param string $name                  The user's display name
169
	 * @param string $email                 The user's email address
170
	 * @param bool   $allow_multiple_emails Allow the same email address to be
171
	 *                                      registered multiple times?
172
	 * @param string $subtype               Subtype of the user entity
173
	 *
174
	 * @return int|false The new user's GUID; false on failure
175
	 * @throws RegistrationException
176
	 */
177 162
	public function register($username, $password, $name, $email, $allow_multiple_emails = false, $subtype = null) {
178
179
		// no need to trim password
180 162
		$username = trim($username);
181 162
		$name = trim(strip_tags($name));
182 162
		$email = trim($email);
183
184
		// A little sanity checking
185 162
		if (empty($username) || empty($password) || empty($name) || empty($email)) {
186
			return false;
187
		}
188
189
		// Make sure a user with conflicting details hasn't registered and been disabled
190 162
		$access_status = access_get_show_hidden_status();
191 162
		access_show_hidden_entities(true);
192
193 162
		if (!validate_email_address($email)) {
194
			throw new RegistrationException(_elgg_services()->translator->translate('registration:emailnotvalid'));
195
		}
196
197 162
		if (!validate_password($password)) {
198
			throw new RegistrationException(_elgg_services()->translator->translate('registration:passwordnotvalid'));
199
		}
200
201 162
		if (!validate_username($username)) {
202
			throw new RegistrationException(_elgg_services()->translator->translate('registration:usernamenotvalid'));
203
		}
204
205 162
		if (get_user_by_username($username)) {
206
			throw new RegistrationException(_elgg_services()->translator->translate('registration:userexists'));
207
		}
208
209 162
		if ((!$allow_multiple_emails) && (get_user_by_email($email))) {
210
			throw new RegistrationException(_elgg_services()->translator->translate('registration:dupeemail'));
211
		}
212
213 162
		access_show_hidden_entities($access_status);
214
215
		// Create user
216 162
		$constructor = ElggUser::class;
217 162
		if (isset($subtype)) {
218 160
			$class = elgg_get_entity_class('user', $subtype);
219 160
			if ($class && class_exists($class) && is_subclass_of($class, ElggUser::class)) {
220
				$constructor = $class;
221
			}
222
		}
223
224 162
		$user = new $constructor();
225 162
		if (isset($subtype)) {
226 160
			$user->subtype = $subtype;
227
		}
228 162
		$user->username = $username;
229 162
		$user->email = $email;
230 162
		$user->name = $name;
231 162
		$user->access_id = ACCESS_PUBLIC;
232 162
		$user->owner_guid = 0; // Users aren't owned by anyone, even if they are admin created.
233 162
		$user->container_guid = 0; // Users aren't contained by anyone, even if they are admin created.
234 162
		$user->language = _elgg_services()->translator->getCurrentLanguage();
235 162
		if ($user->save() === false) {
236
			return false;
237
		}
238
		
239
		// doing this after save to prevent metadata save notices on unwritable metadata password_hash
240 162
		$user->setPassword($password);
241
242
		// Turn on email notifications by default
243 162
		$user->setNotificationSetting('email', true);
244
	
245 162
		return $user->getGUID();
246
	}
247
248
	/**
249
	 * Generates a unique invite code for a user
250
	 *
251
	 * @param string $username The username of the user sending the invitation
252
	 *
253
	 * @return string Invite code
254
	 * @see validateInviteCode
255
	 */
256
	public function generateInviteCode($username) {
257
		$time = $this->getCurrentTime()->getTimestamp();
258
		return "$time." . _elgg_services()->hmac->getHmac([(int) $time, $username])->getToken();
259
	}
260
261
	/**
262
	 * Validate a user's invite code
263
	 *
264
	 * @param string $username The username
265
	 * @param string $code     The invite code
266
	 *
267
	 * @return bool
268
	 * @see generateInviteCode
269
	 */
270
	public function validateInviteCode($username, $code) {
271
		// validate the format of the token created by ->generateInviteCode()
272
		if (!preg_match('~^(\d+)\.([a-zA-Z0-9\-_]+)$~', $code, $m)) {
273
			return false;
274
		}
275
		$time = $m[1];
276
		$mac = $m[2];
277
278
		return _elgg_services()->hmac->getHmac([(int) $time, $username])->matchesToken($mac);
279
	}
280
}
281