Completed
Push — master ( be0cda...a5882f )
by Maxence
03:17
created

FederatedUser::getUserType()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
6
/**
7
 * Circles - Bring cloud-users closer together.
8
 *
9
 * This file is licensed under the Affero General Public License version 3 or
10
 * later. See the COPYING file.
11
 *
12
 * @author Maxence Lange <[email protected]>
13
 * @copyright 2021
14
 * @license GNU AGPL version 3 or any later version
15
 *
16
 * This program is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License as
18
 * published by the Free Software Foundation, either version 3 of the
19
 * License, or (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28
 *
29
 */
30
31
32
namespace OCA\Circles\Model;
33
34
use daita\MySmallPhpTools\Db\Nextcloud\nc22\INC22QueryRow;
35
use daita\MySmallPhpTools\Exceptions\InvalidItemException;
36
use daita\MySmallPhpTools\IDeserializable;
37
use daita\MySmallPhpTools\Traits\Nextcloud\nc22\TNC22Deserialize;
38
use daita\MySmallPhpTools\Traits\TArrayTools;
39
use JsonSerializable;
40
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
41
use OCA\Circles\Exceptions\OwnerNotFoundException;
42
use OCA\Circles\Exceptions\UnknownInterfaceException;
43
use OCA\Circles\IFederatedUser;
44
use OCA\Circles\IMemberships;
45
46
47
/**
48
 * Class FederatedUser
49
 *
50
 * @package OCA\Circles\Model
51
 */
52
class FederatedUser extends ManagedModel implements
53
	IFederatedUser,
54
	IMemberships,
55
	IDeserializable,
56
	INC22QueryRow,
57
	JsonSerializable {
58
59
60
	use TArrayTools;
61
	use TNC22Deserialize;
62
63
64
	/** @var string */
65
	private $singleId = '';
66
67
	/** @var string */
68
	private $userId;
69
70
	/** @var int */
71
	private $userType;
72
73
	/** @var string */
74
	private $displayName = '';
75
76
	/** @var Circle */
77
	private $basedOn;
78
79
	/** @var int */
80
	private $config = 0;
81
82
	/** @var string */
83
	private $instance;
84
85
	/** @var Membership */
86
	private $link;
87
88
	/** @var Membership[] */
89
	private $memberships = null;
90
91
92
	/**
93
	 * FederatedUser constructor.
94
	 */
95
	public function __construct() {
96
	}
97
98
99
	/**
100
	 * @param string $userId
101
	 * @param string $instance
102
	 * @param int $type
103
	 * @param string $displayName
104
	 * @param Circle|null $basedOn
105
	 *
106
	 * @return $this
107
	 */
108
	public function set(
109
		string $userId,
110
		string $instance = '',
111
		int $type = Member::TYPE_USER,
112
		string $displayName = '',
113
		?Circle $basedOn = null
114
	): self {
115
116
		$this->userId = $userId;
117
		$this->displayName = ($displayName === '') ? $userId : $displayName;
118
		$this->setInstance($instance);
119
		$this->userType = $type;
120
		$this->basedOn = $basedOn;
121
122
		return $this;
123
	}
124
125
126
	/**
127
	 * @param string $singleId
128
	 *
129
	 * @return self
130
	 */
131
	public function setSingleId(string $singleId): self {
132
		$this->singleId = $singleId;
133
134
		return $this;
135
	}
136
137
	/**
138
	 * @return string
139
	 */
140
	public function getSingleId(): string {
141
		return $this->singleId;
142
	}
143
144
145
	/**
146
	 * @param string $userId
147
	 *
148
	 * @return self
149
	 */
150
	public function setUserId(string $userId): self {
151
		$this->userId = $userId;
152
153
		return $this;
154
	}
155
156
	/**
157
	 * @return string
158
	 */
159
	public function getUserId(): string {
160
		return $this->userId;
161
	}
162
163
164
	/**
165
	 * @param int $userType
166
	 *
167
	 * @return self
168
	 */
169
	public function setUserType(int $userType): self {
170
		$this->userType = $userType;
171
172
		return $this;
173
	}
174
175
	/**
176
	 * @return int
177
	 */
178
	public function getUserType(): int {
179
		return $this->userType;
180
	}
181
182
	/**
183
	 * @param string $displayName
184
	 *
185
	 * @return FederatedUser
186
	 */
187
	public function setDisplayName(string $displayName): self {
188
		$this->displayName = $displayName;
189
190
		return $this;
191
	}
192
193
	/**
194
	 * @return string
195
	 */
196
	public function getDisplayName(): string {
197
		return $this->displayName;
198
	}
199
200
201
	/**
202
	 * @return bool
203
	 */
204
	public function hasBasedOn(): bool {
205
		return !is_null($this->basedOn);
206
	}
207
208
	/**
209
	 * @param Circle|null $basedOn
210
	 *
211
	 * @return $this
212
	 */
213
	public function setBasedOn(?Circle $basedOn): self {
214
		$this->basedOn = $basedOn;
215
216
		return $this;
217
	}
218
219
	/**
220
	 * @return Circle
221
	 */
222
	public function getBasedOn(): Circle {
223
		return $this->basedOn;
224
	}
225
226
227
	/**
228
	 * @param int $config
229
	 *
230
	 * @return self
231
	 */
232
	public function setConfig(int $config): self {
233
		$this->config = $config;
234
235
		return $this;
236
	}
237
238
	/**
239
	 * @return int
240
	 */
241
	public function getConfig(): int {
242
		return $this->config;
243
	}
244
245
246
	/**
247
	 * @param string $instance
248
	 *
249
	 * @return self
250
	 */
251
	public function setInstance(string $instance): self {
252
		if ($instance === '') {
253
			// TODO: is it needed ?
254
			$instance = $this->getManager()->getLocalInstance();
255
		}
256
257
		$this->instance = $instance;
258
259
		return $this;
260
	}
261
262
	/**
263
	 * @return string
264
	 */
265
	public function getInstance(): string {
266
		return $this->instance;
267
	}
268
269
270
	/**
271
	 * @return bool
272
	 */
273
	public function isLocal(): bool {
274
		return $this->getManager()->isLocalInstance($this->getInstance());
275
	}
276
277
278
	/**
279
	 * @return bool
280
	 */
281
	public function hasMemberships(): bool {
282
		return !is_null($this->memberships);
283
	}
284
285
	/**
286
	 * @param array $memberships
287
	 *
288
	 * @return self
289
	 */
290
	public function setMemberships(array $memberships): IMemberships {
291
		$this->memberships = $memberships;
292
293
		return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (OCA\Circles\Model\FederatedUser) is incompatible with the return type declared by the interface OCA\Circles\IMemberships::setMemberships of type self.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
294
	}
295
296
	/**
297
	 * @return Membership[]
298
	 */
299
	public function getMemberships(): array {
300
		if (!$this->hasMemberships()) {
301
			$this->getManager()->getMemberships($this);
302
		}
303
304
		return $this->memberships;
305
	}
306
307
308
	/**
309
	 * @return bool
310
	 */
311
	public function hasLink(): bool {
312
		return !is_null($this->link);
313
	}
314
315
	/**
316
	 * @param Membership $link
317
	 *
318
	 * @return $this
319
	 */
320
	public function setLink(Membership $link): self {
321
		$this->link = $link;
322
323
		return $this;
324
	}
325
326
	/**
327
	 * @return Membership
328
	 */
329
	public function getLink(): Membership {
330
		return $this->link;
331
	}
332
333
334
	/**
335
	 * @param array $data
336
	 *
337
	 * @return $this
338
	 * @throws InvalidItemException
339
	 */
340
	public function import(array $data): IDeserializable {
341
		if ($this->get('user_id', $data) === '') {
342
			throw new InvalidItemException();
343
		}
344
345
		$this->setSingleId($this->get('id', $data));
346
		$this->setUserId($this->get('userId', $data));
347
		$this->setUserType($this->getInt('userType', $data));
348
		$this->setDisplayName($this->get('displayName', $data));
349
		$this->setInstance($this->get('instance', $data));
350
		//$this->setMemberships($this->getArray('memberships'));
351
352
		try {
353
			/** @var Circle $circle */
354
			$circle = $this->deserialize($this->getArray('basedOn', $data), Circle::class);
355
			$this->setBasedOn($circle);
356
		} catch (InvalidItemException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
357
		}
358
359
		return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (OCA\Circles\Model\FederatedUser) is incompatible with the return type declared by the interface daita\MySmallPhpTools\IDeserializable::import of type self.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
360
	}
361
362
363
	/**
364
	 * @param Circle $circle
365
	 *
366
	 * @return FederatedUser
367
	 * @throws OwnerNotFoundException
368
	 */
369
	public function importFromCircle(Circle $circle): self {
370
		$this->setSingleId($circle->getSingleId());
371
372
		if ($circle->isConfig(Circle::CFG_SINGLE)) {
373
			$owner = $circle->getOwner();
374
			$this->set(
375
				$owner->getUserId(),
376
				$owner->getInstance(),
377
				$owner->getUserType(),
378
				$owner->getDisplayName(),
379
				$circle
380
			);
381
		} else {
382
			$this->set(
383
				$circle->getDisplayName(),
384
				$circle->getInstance(),
385
				Member::TYPE_CIRCLE,
386
				$circle->getDisplayName(),
387
				$circle
388
			);
389
		}
390
391
		return $this;
392
	}
393
394
395
	/**
396
	 * @param array $data
397
	 * @param string $prefix
398
	 *
399
	 * @return INC22QueryRow
400
	 * @throws FederatedUserNotFoundException
401
	 */
402
	public function importFromDatabase(array $data, string $prefix = ''): INC22QueryRow {
403
		if ($this->get($prefix . 'single_id', $data) === '') {
404
			throw new FederatedUserNotFoundException();
405
		}
406
407
		$this->setSingleId($this->get($prefix . 'single_id', $data));
408
		$this->setUserId($this->get($prefix . 'user_id', $data));
409
		$this->setUserType($this->getInt($prefix . 'user_type', $data));
410
		$this->setDisplayName($this->get($prefix . 'cached_name', $data));
411
		$this->setInstance($this->get($prefix . 'instance', $data));
412
413
		$this->getManager()->manageImportFromDatabase($this, $data, $prefix);
414
415
		return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (OCA\Circles\Model\FederatedUser) is incompatible with the return type declared by the interface daita\MySmallPhpTools\Db...Row::importFromDatabase of type self.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
416
	}
417
418
419
	/**
420
	 * @return string[]
421
	 * @throws UnknownInterfaceException
422
	 */
423
	public function jsonSerialize(): array {
424
		$arr = [
425
			'id'          => $this->getSingleId(),
426
			'userId'      => $this->getUserId(),
427
			'userType'    => $this->getUserType(),
428
			'displayName' => $this->getDisplayName(),
429
			'instance'    => $this->getManager()->fixInstance($this->getInstance())
430
		];
431
432
		if ($this->hasBasedOn()) {
433
			$arr['basedOn'] = $this->getBasedOn();
434
		}
435
436
		if ($this->hasLink()) {
437
			$arr['link'] = $this->getLink();
438
		}
439
440
		if (!is_null($this->memberships)) {
441
			$arr['memberships'] = $this->getMemberships();
442
		}
443
444
		return $arr;
445
	}
446
447
448
	/**
449
	 * @param IFederatedUser $member
450
	 *
451
	 * @return bool
452
	 */
453
	public function compareWith(IFederatedUser $member): bool {
454
		$local = ($this->getManager()->isLocalInstance($this->getInstance())
455
				  && $this->getManager()->isLocalInstance($member->getInstance()));
456
457
		return !($this->getSingleId() !== $member->getSingleId()
458
				 || $this->getUserId() !== $member->getUserId()
459
				 || $this->getUserType() <> $member->getUserType()
460
				 || (!$local && $this->getInstance() !== $member->getInstance()));
461
	}
462
463
}
464
465