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