Completed
Pull Request — master (#32261)
by Matthew
17:18 queued 06:04
created

User::setPassword()   B

Complexity

Conditions 6
Paths 2

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

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