Completed
Push — master ( 43b569...fd5663 )
by
unknown
41:16 queued 28:28
created

User::__construct()   A

Complexity

Conditions 5
Paths 16

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 16
nop 8
dl 0
loc 25
rs 9.2088
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * @author Arthur Schiwon <[email protected]>
4
 * @author Bart Visscher <[email protected]>
5
 * @author Björn Schießle <[email protected]>
6
 * @author Jörn Friedrich Dreyer <[email protected]>
7
 * @author Lukas Reschke <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Robin Appelman <[email protected]>
10
 * @author Thomas Müller <[email protected]>
11
 * @author Tom Needham <[email protected]>
12
 * @author Victor Dubiniuk <[email protected]>
13
 * @author Vincent Petry <[email protected]>
14
 *
15
 * @copyright Copyright (c) 2018, ownCloud GmbH
16
 * @license AGPL-3.0
17
 *
18
 * This code is free software: you can redistribute it and/or modify
19
 * it under the terms of the GNU Affero General Public License, version 3,
20
 * as published by the Free Software Foundation.
21
 *
22
 * This program is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
 * GNU Affero General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU Affero General Public License, version 3,
28
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
29
 *
30
 */
31
32
namespace OC\User;
33
34
use OC\Files\Cache\Storage;
35
use OC\Hooks\Emitter;
36
use OC_Helper;
37
use OCP\Events\EventEmitterTrait;
38
use OCP\IAvatarManager;
39
use OCP\IImage;
40
use OCP\IURLGenerator;
41
use OCP\IUser;
42
use OCP\IConfig;
43
use OCP\IUserBackend;
44
use OCP\IUserSession;
45
use OCP\PreConditionNotMetException;
46
use OCP\User\IChangePasswordBackend;
47
use Symfony\Component\EventDispatcher\EventDispatcher;
48
use Symfony\Component\EventDispatcher\GenericEvent;
49
50
class User implements IUser {
51
	use EventEmitterTrait;
52
53
	/** @var Account */
54
	private $account;
55
56
	/** @var Emitter|Manager $emitter */
57
	private $emitter;
58
59
	/** @var \OCP\IConfig $config */
60
	private $config;
61
62
	/** @var IAvatarManager */
63
	private $avatarManager;
64
65
	/** @var IURLGenerator */
66
	private $urlGenerator;
67
68
	/** @var EventDispatcher */
69
	private $eventDispatcher;
70
71
	/** @var AccountMapper */
72
	private $mapper;
73
74
	/** @var \OC\Group\Manager  */
75
	private $groupManager;
76
77
	/** @var Session  */
78
	private $userSession;
79
80
	/**
81
	 * User constructor.
82
	 *
83
	 * @param Account $account
84
	 * @param AccountMapper $mapper
85
	 * @param null $emitter
86
	 * @param IConfig|null $config
87
	 * @param null $urlGenerator
88
	 * @param EventDispatcher|null $eventDispatcher
89
	 * @param \OC\Group\Manager|null $groupManager
90
	 * @param Session|null $userSession
91
	 */
92
	public function __construct(Account $account, AccountMapper $mapper, $emitter = null, IConfig $config = null,
93
								$urlGenerator = null, EventDispatcher $eventDispatcher = null,
94
								\OC\Group\Manager $groupManager = null, Session $userSession = null
95
	) {
96
		$this->account = $account;
97
		$this->mapper = $mapper;
98
		$this->emitter = $emitter;
99
		$this->eventDispatcher = $eventDispatcher;
100
		if ($config === null) {
101
			$config = \OC::$server->getConfig();
102
		}
103
		$this->config = $config;
104
		$this->urlGenerator = $urlGenerator;
105
		if ($this->urlGenerator === null) {
106
			$this->urlGenerator = \OC::$server->getURLGenerator();
107
		}
108
		$this->groupManager = $groupManager;
109
		if ($this->groupManager === null) {
110
			$this->groupManager = \OC::$server->getGroupManager();
111
		}
112
		$this->userSession = $userSession;
113
		if ($this->userSession === null) {
114
			$this->userSession = \OC::$server->getUserSession();
115
		}
116
	}
117
118
	/**
119
	 * get the user id
120
	 *
121
	 * @return string
122
	 */
123
	public function getUID() {
124
		return $this->account->getUserId();
125
	}
126
127
	/**
128
	 * get the user name
129
	 * TODO move username to account table
130
	 *
131
	 * @return string
132
	 */
133
	public function getUserName() {
134
		$uid = $this->getUID();
135
		return $this->config->getUserValue($uid, 'core', 'username', $uid);
136
	}
137
138
	/**
139
	 * set the user name
140
	 * TODO move username to account table
141
	 *
142
	 * @param string $userName
143
	 */
144
	public function setUserName($userName) {
145
		$currentUserName = $this->getUserName();
146
		if ($userName !== $currentUserName) {
147
			$uid = $this->getUID();
148
			try {
149
				$this->config->setUserValue($uid, 'core', 'username', $userName);
150
			} catch (PreConditionNotMetException $e) {
151
				// ignore, because precondition is empty
152
			}
153
		}
154
	}
155
156
	/**
157
	 * get the display name for the user, if no specific display name is set it will fallback to the user id
158
	 *
159
	 * @return string
160
	 */
161
	public function getDisplayName() {
162
		$displayName = $this->account->getDisplayName();
163
		if (empty($displayName)) {
164
			$displayName = $this->getUID();
165
		}
166
		return $displayName;
167
	}
168
169
	/**
170
	 * set the displayname for the user
171
	 *
172
	 * @param string $displayName
173
	 * @return bool
174
	 */
175
	public function setDisplayName($displayName) {
176
		if (!$this->canChangeDisplayName()) {
177
			return false;
178
		}
179
		$displayName = \trim($displayName);
180
		if ($displayName === $this->account->getDisplayName()) {
181
			return false;
182
		}
183
		$this->account->setDisplayName($displayName);
184
		$this->mapper->update($this->account);
185
186
		$backend = $this->account->getBackendInstance();
187
		if ($backend->implementsActions(Backend::SET_DISPLAYNAME)) {
188
			$backend->setDisplayName($this->account->getUserId(), $displayName);
189
		}
190
191
		$this->triggerChange('displayName', $displayName);
192
193
		return true;
194
	}
195
196
	/**
197
	 * set the email address of the user
198
	 *
199
	 * @param string|null $mailAddress
200
	 * @return void
201
	 * @since 9.0.0
202
	 */
203
	public function setEMailAddress($mailAddress) {
204
		$mailAddress = \trim($mailAddress);
205
		if ($mailAddress === $this->account->getEmail()) {
206
			return;
207
		}
208
		$this->account->setEmail($mailAddress);
209
		$this->mapper->update($this->account);
210
		$this->triggerChange('eMailAddress', $mailAddress);
211
	}
212
213
	/**
214
	 * returns the timestamp of the user's last login or 0 if the user did never
215
	 * login
216
	 *
217
	 * @return int
218
	 */
219
	public function getLastLogin() {
220
		return (int)$this->account->getLastLogin();
221
	}
222
223
	/**
224
	 * updates the timestamp of the most recent login of this user
225
	 */
226
	public function updateLastLoginTimestamp() {
227
		$firstTimeLogin = ($this->getLastLogin() === 0);
228
		$this->account->setLastLogin(\time());
229
		$this->mapper->update($this->account);
230
		return $firstTimeLogin;
231
	}
232
233
	/**
234
	 * Delete the user
235
	 *
236
	 * @return bool
237
	 */
238
	public function delete() {
239
		if ($this->emitter) {
240
			$this->emitter->emit('\OC\User', 'preDelete', [$this]);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface OC\Hooks\Emitter as the method emit() does only exist in the following implementations of said interface: OCA\Files_Sharing\Scanner, OC\App\CodeChecker\CodeChecker, OC\App\CodeChecker\InfoChecker, OC\Files\Cache\Scanner, OC\Files\Config\MountProviderCollection, OC\Files\Node\LazyRoot, OC\Files\Node\Root, OC\Files\ObjectStore\NoopScanner, OC\Files\Utils\Scanner, OC\Group\Manager, OC\Hooks\BasicEmitter, OC\Hooks\ForwardingEmitter, OC\Hooks\LegacyEmitter, OC\Hooks\PublicEmitter, OC\SubAdmin, OC\Updater, OC\User\Manager.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
241
		}
242
		// get the home now because it won't return it after user deletion
243
		$homePath = $this->getHome();
244
		$this->mapper->delete($this->account);
245
		$bi = $this->account->getBackendInstance();
246
		if ($bi !== null) {
247
			$bi->deleteUser($this->account->getUserId());
248
		}
249
250
		// FIXME: Feels like an hack - suggestions?
251
252
		// We have to delete the user from all groups
253
		foreach (\OC::$server->getGroupManager()->getUserGroups($this) as $group) {
254
			$group->removeUser($this);
255
		}
256
		// Delete the user's keys in preferences
257
		\OC::$server->getConfig()->deleteAllUserValues($this->getUID());
258
259
		// Delete all mount points for user
260
		\OC::$server->getUserStoragesService()->deleteAllMountsForUser($this);
261
		//Delete external storage or remove user from applicableUsers list
262
		\OC::$server->getGlobalStoragesService()->deleteAllForUser($this);
263
264
		// Delete user files in /data/
265
		if ($homePath !== false) {
266
			// FIXME: this operates directly on FS, should use View instead...
267
			// also this is not testable/mockable...
268
			\OC_Helper::rmdirr($homePath);
269
		}
270
271
		// Delete the users entry in the storage table
272
		Storage::remove('home::' . $this->getUID());
273
		Storage::remove('object::user:' . $this->getUID());
274
275
		\OC::$server->getCommentsManager()->deleteReferencesOfActor('users', $this->getUID());
276
		\OC::$server->getCommentsManager()->deleteReadMarksFromUser($this);
277
278
		if ($this->emitter) {
279
			$this->emitter->emit('\OC\User', 'postDelete', [$this]);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface OC\Hooks\Emitter as the method emit() does only exist in the following implementations of said interface: OCA\Files_Sharing\Scanner, OC\App\CodeChecker\CodeChecker, OC\App\CodeChecker\InfoChecker, OC\Files\Cache\Scanner, OC\Files\Config\MountProviderCollection, OC\Files\Node\LazyRoot, OC\Files\Node\Root, OC\Files\ObjectStore\NoopScanner, OC\Files\Utils\Scanner, OC\Group\Manager, OC\Hooks\BasicEmitter, OC\Hooks\ForwardingEmitter, OC\Hooks\LegacyEmitter, OC\Hooks\PublicEmitter, OC\SubAdmin, OC\Updater, OC\User\Manager.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
280
		}
281
		return true;
282
	}
283
284
	/**
285
	 * Set the user's password
286
	 *
287
	 * @param string $password
288
	 * @param string $recoveryPassword for the encryption app to reset encryption keys
289
	 * @return bool
290
	 * @throws \InvalidArgumentException
291
	 */
292
	public function setPassword($password, $recoveryPassword = null) {
293
		if (\OCP\Util::isEmptyString($password)) {
294
			throw new \InvalidArgumentException('Password cannot be empty');
295
		}
296
297
		return $this->emittingCall(function () use (&$password, &$recoveryPassword) {
298
			if ($this->emitter) {
299
				$this->emitter->emit('\OC\User', 'preSetPassword', [$this, $password, $recoveryPassword]);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface OC\Hooks\Emitter as the method emit() does only exist in the following implementations of said interface: OCA\Files_Sharing\Scanner, OC\App\CodeChecker\CodeChecker, OC\App\CodeChecker\InfoChecker, OC\Files\Cache\Scanner, OC\Files\Config\MountProviderCollection, OC\Files\Node\LazyRoot, OC\Files\Node\Root, OC\Files\ObjectStore\NoopScanner, OC\Files\Utils\Scanner, OC\Group\Manager, OC\Hooks\BasicEmitter, OC\Hooks\ForwardingEmitter, OC\Hooks\LegacyEmitter, OC\Hooks\PublicEmitter, OC\SubAdmin, OC\Updater, OC\User\Manager.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
300
				\OC::$server->getEventDispatcher()->dispatch(
301
					'OCP\User::validatePassword',
302
					new GenericEvent(null, ['uid'=> $this->getUID(), 'password' => $password])
303
				);
304
			}
305
			if ($this->canChangePassword()) {
306
				/** @var IChangePasswordBackend $backend */
307
				$backend = $this->account->getBackendInstance();
308
				$result = $backend->setPassword($this->getUID(), $password);
309
				if ($result) {
310
					if ($this->emitter) {
311
						$this->emitter->emit('\OC\User', 'postSetPassword', [$this, $password, $recoveryPassword]);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface OC\Hooks\Emitter as the method emit() does only exist in the following implementations of said interface: OCA\Files_Sharing\Scanner, OC\App\CodeChecker\CodeChecker, OC\App\CodeChecker\InfoChecker, OC\Files\Cache\Scanner, OC\Files\Config\MountProviderCollection, OC\Files\Node\LazyRoot, OC\Files\Node\Root, OC\Files\ObjectStore\NoopScanner, OC\Files\Utils\Scanner, OC\Group\Manager, OC\Hooks\BasicEmitter, OC\Hooks\ForwardingEmitter, OC\Hooks\LegacyEmitter, OC\Hooks\PublicEmitter, OC\SubAdmin, OC\Updater, OC\User\Manager.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
312
					}
313
					$this->config->deleteUserValue($this->getUID(), 'owncloud', 'lostpassword');
314
				}
315
				return !($result === false);
316
			} else {
317
				return false;
318
			}
319
		}, [
320
			'before' => ['user' => $this, 'password' => $password, 'recoveryPassword' => $recoveryPassword],
321
			'after' => ['user' => $this, 'password' => $password, 'recoveryPassword' => $recoveryPassword]
322
		], 'user', 'setpassword');
323
	}
324
325
	/**
326
	 * get the users home folder to mount
327
	 *
328
	 * @return string
329
	 */
330
	public function getHome() {
331
		return $this->account->getHome();
332
	}
333
334
	/**
335
	 * Get the name of the backend class the user is connected with
336
	 *
337
	 * @return string
338
	 */
339
	public function getBackendClassName() {
340
		$b = $this->account->getBackendInstance();
341
		if ($b instanceof IUserBackend) {
342
			return $b->getBackendName();
343
		}
344
		return $this->account->getBackend();
345
	}
346
347
	/**
348
	 * check if the backend allows the user to change his avatar on Personal page
349
	 *
350
	 * @return bool
351
	 */
352
	public function canChangeAvatar() {
353
		$backend = $this->account->getBackendInstance();
354
		if ($backend === null) {
355
			return false;
356
		}
357
		if ($backend->implementsActions(Backend::PROVIDE_AVATAR)) {
358
			return $backend->canChangeAvatar($this->getUID());
359
		}
360
		return true;
361
	}
362
363
	/**
364
	 * check if the backend supports changing passwords
365
	 *
366
	 * @return bool
367
	 */
368
	public function canChangePassword() {
369
		$backend = $this->account->getBackendInstance();
370
		if ($backend === null) {
371
			return false;
372
		}
373
		return $backend instanceof IChangePasswordBackend || $backend->implementsActions(Backend::SET_PASSWORD);
374
	}
375
376
	/**
377
	 * check if the backend supports changing display names
378
	 *
379
	 * @return bool
380
	 */
381
	public function canChangeDisplayName() {
382
		// Only Admin and SubAdmins are allowed to change display name
383
		if ($this->userSession instanceof IUserSession) {
384
			$user = $this->userSession->getUser();
385
			if (
386
				($this->config->getSystemValue('allow_user_to_change_display_name') === false) &&
387
				(($user !== null) && (!$this->groupManager->isAdmin($user->getUID()))) &&
388
				(($user !== null) && (!$this->groupManager->getSubAdmin()->isSubAdmin($user)))
389
			) {
390
				return false;
391
			}
392
		}
393
		$backend = $this->account->getBackendInstance();
394
		if ($backend === null) {
395
			return false;
396
		}
397
		return $backend->implementsActions(Backend::SET_DISPLAYNAME);
398
	}
399
400
	/**
401
	 * check if the user is enabled
402
	 *
403
	 * @return bool
404
	 */
405
	public function isEnabled() {
406
		return $this->account->getState() === Account::STATE_ENABLED;
407
	}
408
409
	/**
410
	 * set the enabled status for the user
411
	 *
412
	 * @param bool $enabled
413
	 */
414
	public function setEnabled($enabled) {
415
		if ($enabled === true) {
416
			$this->account->setState(Account::STATE_ENABLED);
417
		} else {
418
			$this->account->setState(Account::STATE_DISABLED);
419
		}
420
		$this->mapper->update($this->account);
421
422
		if ($this->eventDispatcher) {
423
			$this->eventDispatcher->dispatch(self::class . '::postSetEnabled', new GenericEvent($this));
424
		}
425
	}
426
427
	/**
428
	 * get the users email address
429
	 *
430
	 * @return string|null
431
	 * @since 9.0.0
432
	 */
433
	public function getEMailAddress() {
434
		return $this->account->getEmail();
435
	}
436
437
	/**
438
	 * get the users' quota
439
	 *
440
	 * @return string
441
	 * @since 9.0.0
442
	 */
443
	public function getQuota() {
444
		$quota = $this->account->getQuota();
445
		if ($quota === null) {
446
			return 'default';
447
		}
448
		return $quota;
449
	}
450
451
	/**
452
	 * set the users' quota
453
	 *
454
	 * @param string $quota
455
	 * @return void
456
	 * @since 9.0.0
457
	 */
458
	public function setQuota($quota) {
459
		if ($quota !== 'none' and $quota !== 'default') {
460
			$quota = OC_Helper::computerFileSize($quota);
461
			$quota = OC_Helper::humanFileSize($quota);
0 ignored issues
show
Documentation introduced by
$quota is of type false|double, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
462
		}
463
		$this->account->setQuota($quota);
464
		$this->mapper->update($this->account);
465
		$this->triggerChange('quota', $quota);
466
	}
467
468
	/**
469
	 * get the avatar image if it exists
470
	 *
471
	 * @param int $size
472
	 * @return IImage|null
473
	 * @since 9.0.0
474
	 */
475
	public function getAvatarImage($size) {
476
		// delay the initialization
477
		if ($this->avatarManager === null) {
478
			$this->avatarManager = \OC::$server->getAvatarManager();
479
		}
480
481
		$avatar = $this->avatarManager->getAvatar($this->getUID());
482
		$image = $avatar->get($size);
0 ignored issues
show
Bug Compatibility introduced by
The expression $avatar->get($size); of type boolean|OCP\IImage adds the type boolean to the return on line 484 which is incompatible with the return type declared by the interface OCP\IUser::getAvatarImage of type OCP\IImage|null.
Loading history...
483
		if ($image) {
484
			return $image;
485
		}
486
487
		return null;
488
	}
489
490
	/**
491
	 * get the federation cloud id
492
	 *
493
	 * @return string
494
	 * @since 9.0.0
495
	 */
496
	public function getCloudId() {
497
		$uid = $this->getUID();
498
		$server = $this->urlGenerator->getAbsoluteURL('/');
499
		return $uid . '@' . \rtrim($this->removeProtocolFromUrl($server), '/');
500
	}
501
502
	/**
503
	 * @param string $url
504
	 * @return string
505
	 */
506 View Code Duplication
	private function removeProtocolFromUrl($url) {
507
		if (\strpos($url, 'https://') === 0) {
508
			return \substr($url, \strlen('https://'));
509
		} elseif (\strpos($url, 'http://') === 0) {
510
			return \substr($url, \strlen('http://'));
511
		}
512
513
		return $url;
514
	}
515
516
	public function triggerChange($feature, $value = null) {
517
		if ($this->emitter && \in_array($feature, $this->account->getUpdatedFields())) {
518
			$this->emitter->emit('\OC\User', 'changeUser', [$this, $feature, $value]);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface OC\Hooks\Emitter as the method emit() does only exist in the following implementations of said interface: OCA\Files_Sharing\Scanner, OC\App\CodeChecker\CodeChecker, OC\App\CodeChecker\InfoChecker, OC\Files\Cache\Scanner, OC\Files\Config\MountProviderCollection, OC\Files\Node\LazyRoot, OC\Files\Node\Root, OC\Files\ObjectStore\NoopScanner, OC\Files\Utils\Scanner, OC\Group\Manager, OC\Hooks\BasicEmitter, OC\Hooks\ForwardingEmitter, OC\Hooks\LegacyEmitter, OC\Hooks\PublicEmitter, OC\SubAdmin, OC\Updater, OC\User\Manager.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
519
		}
520
	}
521
522
	/**
523
	 * @return string[]
524
	 * @since 10.0.1
525
	 */
526
	public function getSearchTerms() {
527
		$terms = [];
528
		foreach ($this->mapper->findByAccountId($this->account->getId()) as $term) {
529
			$terms[] = $term->getTerm();
530
		}
531
		return $terms;
532
	}
533
534
	/**
535
	 * @param string[] $terms
536
	 * @since 10.0.1
537
	 */
538
	public function setSearchTerms(array $terms) {
539
		// Check length of terms, cut if too long
540
		$terms = \array_map(function ($term) {
541
			return \substr($term, 0, 191);
542
		}, $terms);
543
		$this->mapper->setTermsForAccount($this->account->getId(), $terms);
544
	}
545
546
	/**
547
	 * @return integer
548
	 * @since 11.0.0
549
	 */
550
	public function getAccountId() {
551
		return $this->account->getId();
552
	}
553
}
554