Completed
Pull Request — master (#32261)
by Matthew
32:20 queued 18:21
created

User   F

Complexity

Total Complexity 68

Size/Duplication

Total Lines 467
Duplicated Lines 1.93 %

Coupling/Cohesion

Components 1
Dependencies 26

Importance

Changes 0
Metric Value
wmc 68
lcom 1
cbo 26
dl 9
loc 467
rs 2.96
c 0
b 0
f 0

25 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 25 5
A getUID() 0 3 1
A getDisplayName() 0 7 2
A setDisplayName() 0 20 4
A setEMailAddress() 0 9 2
A getLastLogin() 0 3 1
A updateLastLoginTimestamp() 0 6 1
B delete() 0 45 6
B setPassword() 0 32 6
A getHome() 0 3 1
A getBackendClassName() 0 7 2
A canChangeAvatar() 0 10 3
A canChangePassword() 0 7 3
B canChangeDisplayName() 0 18 8
A isEnabled() 0 3 1
A setEnabled() 0 12 3
A getEMailAddress() 0 3 1
A getQuota() 0 7 2
A setQuota() 0 9 3
A getAvatarImage() 0 14 3
A getCloudId() 0 5 1
A removeProtocolFromUrl() 9 9 3
A triggerChange() 0 5 3
A getSearchTerms() 0 7 2
A setSearchTerms() 0 7 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like User often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use User, and based on these observations, apply Extract Interface, too.

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