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