Completed
Pull Request — master (#31951)
by Jörn Friedrich
15:52
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
	/** @var Account */
53
	private $account;
54
55
	/** @var Emitter|Manager $emitter */
56
	private $emitter;
57
58
	/** @var \OCP\IConfig $config */
59
	private $config;
60
61
	/** @var IAvatarManager */
62
	private $avatarManager;
63
64
	/** @var IURLGenerator */
65
	private $urlGenerator;
66
67
	/** @var EventDispatcher */
68
	private $eventDispatcher;
69
70
	/** @var AccountMapper */
71
	private $mapper;
72
73
	/** @var \OC\Group\Manager  */
74
	private $groupManager;
75
76
	/** @var Session  */
77
	private $userSession;
78
79
	/**
80
	 * User constructor.
81
	 *
82
	 * @param Account $account
83
	 * @param AccountMapper $mapper
84
	 * @param null $emitter
85
	 * @param IConfig|null $config
86
	 * @param null $urlGenerator
87
	 * @param EventDispatcher|null $eventDispatcher
88
	 * @param \OC\Group\Manager|null $groupManager
89
	 * @param Session|null $userSession
90
	 */
91
	public function __construct(Account $account, AccountMapper $mapper, $emitter = null, IConfig $config = null,
92
								$urlGenerator = null, EventDispatcher $eventDispatcher = null,
93
								\OC\Group\Manager $groupManager = null, Session $userSession = null
94
	) {
95
		$this->account = $account;
96
		$this->mapper = $mapper;
97
		$this->emitter = $emitter;
98
		$this->eventDispatcher = $eventDispatcher;
99
		if ($config === null) {
100
			$config = \OC::$server->getConfig();
101
		}
102
		$this->config = $config;
103
		$this->urlGenerator = $urlGenerator;
104
		if ($this->urlGenerator === null) {
105
			$this->urlGenerator = \OC::$server->getURLGenerator();
106
		}
107
		$this->groupManager = $groupManager;
108
		if ($this->groupManager === null) {
109
			$this->groupManager = \OC::$server->getGroupManager();
110
		}
111
		$this->userSession = $userSession;
112
		if ($this->userSession === null) {
113
			$this->userSession = \OC::$server->getUserSession();
114
		}
115
	}
116
117
	/**
118
	 * get the user id
119
	 *
120
	 * @return string
121
	 */
122
	public function getUID() {
123
		return $this->account->getUserId();
124
	}
125
126
	/**
127
	 * get the user name
128
	 * TODO move username to account table
129
	 *
130
	 * @return string
131
	 */
132
	public function getUserName() {
133
		$uid = $this->getUID();
134
		return $this->config->getUserValue($uid, 'core', 'username', $uid);
135
	}
136
137
	/**
138
	 * set the user name
139
	 * TODO move username to account table
140
	 *
141
	 * @param string $userName
142
	 */
143
	public function setUserName($userName) {
144
		$currentUserName = $this->getUserName();
145
		if ($userName !== $currentUserName) {
146
			$uid = $this->getUID();
147
			try {
148
				$this->config->setUserValue($uid, 'core', 'username', $userName);
149
			} catch (PreConditionNotMetException $e) {
150
				// ignore, because precondition is empty
151
			}
152
		}
153
	}
154
155
	/**
156
	 * get the display name for the user, if no specific display name is set it will fallback to the user id
157
	 *
158
	 * @return string
159
	 */
160
	public function getDisplayName() {
161
		$displayName = $this->account->getDisplayName();
162
		if (empty($displayName)) {
163
			$displayName = $this->getUID();
164
		}
165
		return $displayName;
166
	}
167
168
	/**
169
	 * set the displayname for the user
170
	 *
171
	 * @param string $displayName
172
	 * @return bool
173
	 */
174
	public function setDisplayName($displayName) {
175
		if (!$this->canChangeDisplayName()) {
176
			return false;
177
		}
178
		$displayName = \trim($displayName);
179
		if ($displayName === $this->account->getDisplayName()) {
180
			return false;
181
		}
182
		$this->account->setDisplayName($displayName);
183
		$this->mapper->update($this->account);
184
185
		$backend = $this->account->getBackendInstance();
186
		if ($backend->implementsActions(Backend::SET_DISPLAYNAME)) {
187
			$backend->setDisplayName($this->account->getUserId(), $displayName);
188
		}
189
190
		$this->triggerChange('displayName', $displayName);
191
192
		return true;
193
	}
194
195
	/**
196
	 * set the email address of the user
197
	 *
198
	 * @param string|null $mailAddress
199
	 * @return void
200
	 * @since 9.0.0
201
	 */
202
	public function setEMailAddress($mailAddress) {
203
		$mailAddress = \trim($mailAddress);
204
		if ($mailAddress === $this->account->getEmail()) {
205
			return;
206
		}
207
		$this->account->setEmail($mailAddress);
208
		$this->mapper->update($this->account);
209
		$this->triggerChange('eMailAddress', $mailAddress);
210
	}
211
212
	/**
213
	 * returns the timestamp of the user's last login or 0 if the user did never
214
	 * login
215
	 *
216
	 * @return int
217
	 */
218
	public function getLastLogin() {
219
		return (int)$this->account->getLastLogin();
220
	}
221
222
	/**
223
	 * updates the timestamp of the most recent login of this user
224
	 */
225
	public function updateLastLoginTimestamp() {
226
		$firstTimeLogin = ($this->getLastLogin() === 0);
227
		$this->account->setLastLogin(\time());
228
		$this->mapper->update($this->account);
229
		return $firstTimeLogin;
230
	}
231
232
	/**
233
	 * Delete the user
234
	 *
235
	 * @return bool
236
	 */
237
	public function delete() {
238
		if ($this->emitter) {
239
			$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...
240
		}
241
		// get the home now because it won't return it after user deletion
242
		$homePath = $this->getHome();
243
		$this->mapper->delete($this->account);
244
		$bi = $this->account->getBackendInstance();
245
		if ($bi !== null) {
246
			$bi->deleteUser($this->account->getUserId());
247
		}
248
249
		// FIXME: Feels like an hack - suggestions?
250
251
		// We have to delete the user from all groups
252
		foreach (\OC::$server->getGroupManager()->getUserGroups($this) as $group) {
253
			$group->removeUser($this);
254
		}
255
		// Delete the user's keys in preferences
256
		\OC::$server->getConfig()->deleteAllUserValues($this->getUID());
257
258
		// Delete all mount points for user
259
		\OC::$server->getUserStoragesService()->deleteAllMountsForUser($this);
260
		//Delete external storage or remove user from applicableUsers list
261
		\OC::$server->getGlobalStoragesService()->deleteAllForUser($this);
262
263
		// Delete user files in /data/
264
		if ($homePath !== false) {
265
			// FIXME: this operates directly on FS, should use View instead...
266
			// also this is not testable/mockable...
267
			\OC_Helper::rmdirr($homePath);
268
		}
269
270
		// Delete the users entry in the storage table
271
		Storage::remove('home::' . $this->getUID());
272
		Storage::remove('object::user:' . $this->getUID());
273
274
		\OC::$server->getCommentsManager()->deleteReferencesOfActor('users', $this->getUID());
275
		\OC::$server->getCommentsManager()->deleteReadMarksFromUser($this);
276
277
		if ($this->emitter) {
278
			$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...
279
		}
280
		return true;
281
	}
282
283
	/**
284
	 * Set the password of the user
285
	 *
286
	 * @param string $password
287
	 * @param string $recoveryPassword for the encryption app to reset encryption keys
288
	 * @return bool
289
	 */
290
	public function setPassword($password, $recoveryPassword = null) {
291
		return $this->emittingCall(function () use (&$password, &$recoveryPassword) {
292
			if ($this->emitter) {
293
				$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...
294
				\OC::$server->getEventDispatcher()->dispatch(
295
					'OCP\User::validatePassword',
296
					new GenericEvent(null, ['uid'=> $this->getUID(), 'password' => $password])
297
				);
298
			}
299
			if ($this->canChangePassword()) {
300
				/** @var IChangePasswordBackend $backend */
301
				$backend = $this->account->getBackendInstance();
302
				$result = $backend->setPassword($this->getUID(), $password);
303
				if ($result) {
304
					if ($this->emitter) {
305
						$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...
306
					}
307
					$this->config->deleteUserValue($this->getUID(), 'owncloud', 'lostpassword');
308
				}
309
				return !($result === false);
310
			} else {
311
				return false;
312
			}
313
		}, [
314
			'before' => ['user' => $this, 'password' => $password, 'recoveryPassword' => $recoveryPassword],
315
			'after' => ['user' => $this, 'password' => $password, 'recoveryPassword' => $recoveryPassword]
316
		], 'user', 'setpassword');
317
	}
318
319
	/**
320
	 * get the users home folder to mount
321
	 *
322
	 * @return string
323
	 */
324
	public function getHome() {
325
		return $this->account->getHome();
326
	}
327
328
	/**
329
	 * Get the name of the backend class the user is connected with
330
	 *
331
	 * @return string
332
	 */
333
	public function getBackendClassName() {
334
		$b = $this->account->getBackendInstance();
335
		if ($b instanceof IUserBackend) {
336
			return $b->getBackendName();
337
		}
338
		return $this->account->getBackend();
339
	}
340
341
	/**
342
	 * check if the backend allows the user to change his avatar on Personal page
343
	 *
344
	 * @return bool
345
	 */
346
	public function canChangeAvatar() {
347
		$backend = $this->account->getBackendInstance();
348
		if ($backend === null) {
349
			return false;
350
		}
351
		if ($backend->implementsActions(Backend::PROVIDE_AVATAR)) {
352
			return $backend->canChangeAvatar($this->getUID());
353
		}
354
		return true;
355
	}
356
357
	/**
358
	 * check if the backend supports changing passwords
359
	 *
360
	 * @return bool
361
	 */
362
	public function canChangePassword() {
363
		$backend = $this->account->getBackendInstance();
364
		if ($backend === null) {
365
			return false;
366
		}
367
		return $backend instanceof IChangePasswordBackend || $backend->implementsActions(Backend::SET_PASSWORD);
368
	}
369
370
	/**
371
	 * check if the backend supports changing display names
372
	 *
373
	 * @return bool
374
	 */
375
	public function canChangeDisplayName() {
376
		// Only Admin and SubAdmins are allowed to change display name
377
		if ($this->userSession instanceof IUserSession) {
378
			$user = $this->userSession->getUser();
379
			if (
380
				($this->config->getSystemValue('allow_user_to_change_display_name') === false) &&
381
				(($user !== null) && (!$this->groupManager->isAdmin($user->getUID()))) &&
382
				(($user !== null) && (!$this->groupManager->getSubAdmin()->isSubAdmin($user)))
383
			) {
384
				return false;
385
			}
386
		}
387
		$backend = $this->account->getBackendInstance();
388
		if ($backend === null) {
389
			return false;
390
		}
391
		return $backend->implementsActions(Backend::SET_DISPLAYNAME);
392
	}
393
394
	/**
395
	 * check if the user is enabled
396
	 *
397
	 * @return bool
398
	 */
399
	public function isEnabled() {
400
		return $this->account->getState() === Account::STATE_ENABLED;
401
	}
402
403
	/**
404
	 * set the enabled status for the user
405
	 *
406
	 * @param bool $enabled
407
	 */
408
	public function setEnabled($enabled) {
409
		if ($enabled === true) {
410
			$this->account->setState(Account::STATE_ENABLED);
411
		} else {
412
			$this->account->setState(Account::STATE_DISABLED);
413
		}
414
		$this->mapper->update($this->account);
415
416
		if ($this->eventDispatcher) {
417
			$this->eventDispatcher->dispatch(self::class . '::postSetEnabled', new GenericEvent($this));
418
		}
419
	}
420
421
	/**
422
	 * get the users email address
423
	 *
424
	 * @return string|null
425
	 * @since 9.0.0
426
	 */
427
	public function getEMailAddress() {
428
		return $this->account->getEmail();
429
	}
430
431
	/**
432
	 * get the users' quota
433
	 *
434
	 * @return string
435
	 * @since 9.0.0
436
	 */
437
	public function getQuota() {
438
		$quota = $this->account->getQuota();
439
		if ($quota === null) {
440
			return 'default';
441
		}
442
		return $quota;
443
	}
444
445
	/**
446
	 * set the users' quota
447
	 *
448
	 * @param string $quota
449
	 * @return void
450
	 * @since 9.0.0
451
	 */
452
	public function setQuota($quota) {
453
		if ($quota !== 'none' and $quota !== 'default') {
454
			$quota = OC_Helper::computerFileSize($quota);
455
			$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...
456
		}
457
		$this->account->setQuota($quota);
458
		$this->mapper->update($this->account);
459
		$this->triggerChange('quota', $quota);
460
	}
461
462
	/**
463
	 * get the avatar image if it exists
464
	 *
465
	 * @param int $size
466
	 * @return IImage|null
467
	 * @since 9.0.0
468
	 */
469
	public function getAvatarImage($size) {
470
		// delay the initialization
471
		if ($this->avatarManager === null) {
472
			$this->avatarManager = \OC::$server->getAvatarManager();
473
		}
474
475
		$avatar = $this->avatarManager->getAvatar($this->getUID());
476
		$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 478 which is incompatible with the return type declared by the interface OCP\IUser::getAvatarImage of type OCP\IImage|null.
Loading history...
477
		if ($image) {
478
			return $image;
479
		}
480
481
		return null;
482
	}
483
484
	/**
485
	 * get the federation cloud id
486
	 *
487
	 * @return string
488
	 * @since 9.0.0
489
	 */
490
	public function getCloudId() {
491
		$uid = $this->getUID();
492
		$server = $this->urlGenerator->getAbsoluteURL('/');
493
		return $uid . '@' . \rtrim($this->removeProtocolFromUrl($server), '/');
494
	}
495
496
	/**
497
	 * @param string $url
498
	 * @return string
499
	 */
500 View Code Duplication
	private function removeProtocolFromUrl($url) {
501
		if (\strpos($url, 'https://') === 0) {
502
			return \substr($url, \strlen('https://'));
503
		} elseif (\strpos($url, 'http://') === 0) {
504
			return \substr($url, \strlen('http://'));
505
		}
506
507
		return $url;
508
	}
509
510
	public function triggerChange($feature, $value = null) {
511
		if ($this->emitter && \in_array($feature, $this->account->getUpdatedFields())) {
512
			$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...
513
		}
514
	}
515
516
	/**
517
	 * @return string[]
518
	 * @since 10.0.1
519
	 */
520
	public function getSearchTerms() {
521
		$terms = [];
522
		foreach ($this->mapper->findByAccountId($this->account->getId()) as $term) {
523
			$terms[] = $term->getTerm();
524
		}
525
		return $terms;
526
	}
527
528
	/**
529
	 * @param string[] $terms
530
	 * @since 10.0.1
531
	 */
532
	public function setSearchTerms(array $terms) {
533
		// Check length of terms, cut if too long
534
		$terms = \array_map(function ($term) {
535
			return \substr($term, 0, 191);
536
		}, $terms);
537
		$this->mapper->setTermsForAccount($this->account->getId(), $terms);
538
	}
539
}
540