Completed
Push — master ( a14c1c...b0672a )
by Nazar
04:30
created

Profile::get_users_columns()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package   CleverStyle Framework
4
 * @author    Nazar Mokrynskyi <[email protected]>
5
 * @copyright Copyright (c) 2011-2016, Nazar Mokrynskyi
6
 * @license   MIT License, see license.txt
7
 */
8
namespace cs\User;
9
use
10
	cs\Config,
11
	cs\Event,
12
	cs\Language,
13
	cs\Session,
14
	cs\User,
15
	h;
16
17
/**
18
 * Trait that contains all methods for `cs\User` for working with user's profile
19
 *
20
 * @property \cs\Cache\Prefix $cache
21
 * @property int              $id
22
 *
23
 * @method \cs\DB\_Abstract db()
24
 * @method \cs\DB\_Abstract db_prime()
25
 * @method false|int[]      get_groups(false|int $user)
26
 */
27
trait Profile {
28
	/**
29
	 * Copy of columns list of users table for internal needs without Cache usage
30
	 * @var array
31
	 */
32
	protected $users_columns = [];
33
	/**
34
	 * Local cache of users data
35
	 * @var array
36
	 */
37
	protected $data = [];
38
	/**
39
	 * Whether to use memory cache (locally, inside object, may require a lot of memory if working with many users together)
40
	 * @var bool
41
	 */
42
	protected $memory_cache = true;
43 10
	protected function initialize_data () {
44 10
		$this->users_columns = $this->cache->get(
45 10
			'columns',
46
			function () {
47 2
				return $this->db()->columns('[prefix]users');
48 10
			}
49
		);
50 10
	}
51
	/**
52
	 * Get data item of specified user
53
	 *
54
	 * @param string|string[] $item
55
	 * @param false|int       $user If not specified - current user assumed
56
	 *
57
	 * @return false|int|mixed[]|string|Properties If <i>$item</i> is integer - cs\User\Properties object will be returned
58
	 */
59 8
	function get ($item, $user = false) {
60 8
		if (is_scalar($item) && ctype_digit((string)$item)) {
61
			return new Properties($item);
62
		}
63 8
		return $this->get_internal($item, $user);
64
	}
65
	/**
66
	 * Get data item of specified user
67
	 *
68
	 * @param string|string[] $item
69
	 * @param false|int       $user If not specified - current user assumed
70
	 *
71
	 * @return false|int|string|mixed[]
72
	 */
73 8
	protected function get_internal ($item, $user = false) {
74 8
		$user = (int)$user ?: $this->id;
75 8
		if (isset($this->data[$user])) {
76 6
			$data = $this->data[$user];
77
		} else {
78 8
			$data = $this->cache->get(
79
				$user,
80
				function () use ($user) {
81 8
					return $this->db()->qf(
82
						"SELECT *
83
						FROM `[prefix]users`
84 8
						WHERE `id` = $user
85 8
						LIMIT 1"
86 8
					) ?: false;
87 8
				}
88
			);
89 8
			if (!$data) {
90 4
				return false;
91 8
			} elseif ($this->memory_cache || $user = User::GUEST_ID) {
92 8
				$this->data[$user] = $data;
93
			}
94
		}
95
		/**
96
		 * If get an array of values
97
		 */
98 8
		if (is_array($item)) {
99 6
			$result = [];
100
			/**
101
			 * Trying to get value from the local cache, or make up an array of missing values
102
			 */
103 6
			foreach ($item as $i) {
104 6
				if (in_array($i, $this->users_columns)) {
105 6
					$result[$i] = $data[$i];
106
				}
107
			}
108 6
			return $result;
109
		}
110 8
		return in_array($item, $this->users_columns) ? $data[$item] : false;
111
	}
112
	/**
113
	 * Set data item of specified user
114
	 *
115
	 * @param array|string    $item Item-value array may be specified for setting several items at once
116
	 * @param int|null|string $value
117
	 * @param false|int       $user If not specified - current user assumed
118
	 *
119
	 * @return bool
120
	 */
121 4
	function set ($item, $value = null, $user = false) {
122 4
		$user     = (int)$user ?: $this->id;
123 4
		$data_set = [];
124 4
		if (!$this->set_internal($item, $value, $user, $data_set)) {
125
			return false;
126
		}
127 4
		if (!$data_set) {
128
			return true;
129
		}
130 4
		$update = [];
131 4
		foreach (array_keys($data_set) as $column) {
132 4
			$update[] = "`$column` = '%s'";
133
		}
134 4
		$update = implode(', ', $update);
135 4
		$result = $this->db_prime()->q(
136
			"UPDATE `[prefix]users`
137 4
			SET $update
138 4
			WHERE `id` = '$user'",
139 4
			xap($data_set, false)
140
		);
141 4
		if ($result) {
142 4
			unset($this->data[$user], $this->cache->$user);
143
		}
144 4
		return (bool)$result;
145
	}
146
	/**
147
	 * Set data item of specified user
148
	 *
149
	 * @param array|string    $item Item-value array may be specified for setting several items at once
150
	 * @param int|null|string $value
151
	 * @param int             $user If not specified - current user assumed
152
	 * @param array           $data_set
153
	 *
154
	 * @return bool
155
	 */
156 4
	protected function set_internal ($item, $value, $user, &$data_set) {
157 4
		if (is_array($item)) {
158
			$result = true;
159
			foreach ($item as $i => $v) {
160
				$result = $result && $this->set_internal($i, $v, $user, $data_set);
161
			}
162
			return $result;
163
		}
164 4
		if (!$this->set_internal_allowed($user, $item, $value)) {
165
			return false;
166
		}
167 4
		$old_value = $this->get($item, $user);
168 4
		if ($value == $old_value) {
169
			return true;
170
		}
171 4
		if ($item == 'language') {
172
			$value = $value ? Language::instance()->get('clanguage', $value) : '';
173 4
		} elseif ($item == 'timezone') {
174
			$value = in_array($value, get_timezones_list(), true) ? $value : '';
175 4
		} elseif ($item == 'avatar') {
176
			if (
177
				strpos($value, 'http://') !== 0 &&
178
				strpos($value, 'https://') !== 0
179
			) {
180
				$value = '';
181
			} else {
182
				$Event = Event::instance();
183
				$Event->fire(
184
					'System/upload_files/del_tag',
185
					[
186
						'url' => $old_value,
187
						'tag' => "users/$user/avatar"
188
					]
189
				);
190
				$Event->fire(
191
					'System/upload_files/add_tag',
192
					[
193
						'url' => $value,
194
						'tag' => "users/$user/avatar"
195
					]
196
				);
197
			}
198
		}
199
		/**
200
		 * @var string $item
201
		 */
202 4
		$data_set[$item] = $value;
203 4
		if (in_array($item, ['login', 'email'], true)) {
204
			$old_value               = $this->get($item.'_hash', $user);
205
			$data_set[$item.'_hash'] = hash('sha224', $value);
206
			unset($this->cache->$old_value);
207 4
		} elseif ($item == 'password_hash' || ($item == 'status' && $value == 0)) {
208 4
			Session::instance()->del_all($user);
209
		}
210 4
		return true;
211
	}
212
	/**
213
	 * Check whether setting specified item to specified value for specified user is allowed
214
	 *
215
	 * @param int    $user
216
	 * @param string $item
217
	 * @param string $value
218
	 *
219
	 * @return bool
220
	 */
221 4
	protected function set_internal_allowed ($user, $item, $value) {
222
		if (
223 4
			$user == User::GUEST_ID ||
224 4
			$item == 'id' ||
225 4
			!in_array($item, $this->users_columns, true)
226
		) {
227
			return false;
228
		}
229 4
		if (in_array($item, ['login', 'email'], true)) {
230
			$value = mb_strtolower($value);
231
			if (
232
				$item == 'email' &&
233
				!filter_var($value, FILTER_VALIDATE_EMAIL)
234
			) {
235
				return false;
236
			}
237
			if ($value == $this->get($item, $user)) {
238
				return true;
239
			}
240
			return !$this->get_id(hash('sha224', $value));
241
		}
242 4
		return true;
243
	}
244
	/**
245
	 * Get user id by login or email hash (sha224) (hash from lowercase string)
246
	 *
247
	 * @param  string $login_hash Login or email hash
248
	 *
249
	 * @return false|int User id if found and not guest, otherwise - boolean <i>false</i>
250
	 */
251 6
	function get_id ($login_hash) {
252 6
		if (!preg_match('/^[0-9a-z]{56}$/', $login_hash)) {
253
			return false;
254
		}
255 6
		$id = $this->cache->get(
256
			$login_hash,
257 6
			function () use ($login_hash) {
258 6
				return $this->db()->qfs(
259
					"SELECT `id`
260
					FROM `[prefix]users`
261
					WHERE
262
						`login_hash`	= '%s' OR
263
						`email_hash`	= '%s'
264 6
					LIMIT 1",
265
					$login_hash,
266
					$login_hash
267 6
				) ?: false;
268 6
			}
269
		);
270 6
		return $id && $id != User::GUEST_ID ? $id : false;
271
	}
272
	/**
273
	 * Get user avatar, if no one present - uses Gravatar
274
	 *
275
	 * @param int|null  $size Avatar size, if not specified or resizing is not possible - original image is used
276
	 * @param false|int $user If not specified - current user assumed
277
	 *
278
	 * @return string
279
	 */
280 2
	function avatar ($size = null, $user = false) {
281 2
		$user         = (int)$user ?: $this->id;
282 2
		$avatar       = $this->get('avatar', $user);
283 2
		$Config       = Config::instance();
284 2
		$guest_avatar = $Config->core_url().'/includes/img/guest.svg';
285 2
		if (!$avatar && $this->id != User::GUEST_ID && $Config->core['gravatar_support']) {
286
			$email_hash = md5($this->get('email', $user));
287
			$avatar     = "https://www.gravatar.com/avatar/$email_hash?d=mm&s=$size&d=".urlencode($guest_avatar);
288
		}
289 2
		if (!$avatar) {
290 2
			$avatar = $guest_avatar;
291
		}
292 2
		return h::prepare_url($avatar, true);
293
	}
294
	/**
295
	 * Get user name or login or email, depending on existing information
296
	 *
297
	 * @param false|int $user If not specified - current user assumed
298
	 *
299
	 * @return string
300
	 */
301 2
	function username ($user = false) {
302 2
		$user = (int)$user ?: $this->id;
303 2
		if ($user == User::GUEST_ID) {
304 2
			return Language::instance()->system_profile_guest;
305
		}
306
		$username = $this->get('username', $user);
307
		if (!$username) {
308
			$username = $this->get('login', $user);
309
		}
310
		if (!$username) {
311
			$username = $this->get('email', $user);
312
		}
313
		return $username;
314
	}
315
	/**
316
	 * Disable memory cache
317
	 *
318
	 * Memory cache stores users data inside User class in order to get data faster next time.
319
	 * But in case of working with large amount of users this cache can be too large. Disabling will cause some performance drop, but save a lot of RAM.
320
	 */
321 4
	function disable_memory_cache () {
322 4
		$this->memory_cache = false;
323 4
		$this->data         = [];
324 4
	}
325
	/**
326
	 * Returns array of users columns, available for getting of data
327
	 *
328
	 * @return array
329
	 */
330
	function get_users_columns () {
331
		return $this->users_columns;
332
	}
333
}
334