Completed
Push — master ( c6794e...0594fd )
by Nazar
03:59
created

Profile::disable_memory_cache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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