Completed
Pull Request — master (#31951)
by Jörn Friedrich
10:58
created

User::setUserName()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
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
	/**
541
	 * @return integer
542
	 * @since 11.0.0
543
	 */
544
	public function getAccountId() {
545
		return $this->account->getId();
546
	}
547
}
548