Completed
Pull Request — master (#609)
by Maxence
02:38
created

Member::importFromDatabase()   B

Complexity

Conditions 8
Paths 41

Size

Total Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 48
rs 7.8901
c 0
b 0
f 0
cc 8
nc 41
nop 2
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 DateTime;
40
use JsonSerializable;
41
use OCA\Circles\AppInfo\Capabilities;
42
use OCA\Circles\Exceptions\MemberNotFoundException;
43
use OCA\Circles\Exceptions\ParseMemberLevelException;
44
use OCA\Circles\Exceptions\UnknownInterfaceException;
45
use OCA\Circles\Exceptions\UserTypeNotFoundException;
46
use OCA\Circles\IFederatedUser;
47
use OCA\Circles\IMemberships;
48
use OCA\Circles\Model\Federated\RemoteInstance;
49
50
51
/**
52
 * Class Member
53
 *
54
 * @package OCA\Circles\Model
55
 */
56
class Member extends ManagedModel implements
57
	IMemberships,
58
	IFederatedUser,
59
	IDeserializable,
60
	INC22QueryRow,
61
	JsonSerializable {
62
63
64
	use TArrayTools;
65
	use TNC22Deserialize;
66
67
68
	const LEVEL_NONE = 0;
69
	const LEVEL_MEMBER = 1;
70
	const LEVEL_MODERATOR = 4;
71
	const LEVEL_ADMIN = 8;
72
	const LEVEL_OWNER = 9;
73
74
	const TYPE_SINGLE = 0;
75
	const TYPE_USER = 1;
76
	const TYPE_GROUP = 2;
77
	const TYPE_MAIL = 4;
78
	const TYPE_CONTACT = 8;
79
	const TYPE_CIRCLE = 16;
80
	const TYPE_APP = 10000;
81
82
	const APP_CIRCLES = 10001;
83
	const APP_OCC = 10002;
84
85
86
	public static $TYPE = [
87
		0     => 'single',
88
		1     => 'user',
89
		2     => 'group',
90
		4     => 'mail',
91
		8     => 'contact',
92
		16    => 'circle',
93
		10000 => 'app'
94
	];
95
96
	/**
97
	 * Note: When editing those values, update lib/Application/Capabilities.php
98
	 *
99
	 * @see Capabilities::generateConstantsMember()
100
	 */
101
	const STATUS_INVITED = 'Invited';
102
	const STATUS_REQUEST = 'Requesting';
103
	const STATUS_MEMBER = 'Member';
104
	const STATUS_BLOCKED = 'Blocked';
105
106
107
	/**
108
	 * Note: When editing those values, update lib/Application/Capabilities.php
109
	 *
110
	 * @see Capabilities::generateConstantsMember()
111
	 * @var array
112
	 */
113
	public static $DEF_LEVEL = [
114
		1 => 'Member',
115
		4 => 'Moderator',
116
		8 => 'Admin',
117
		9 => 'Owner'
118
	];
119
120
121
	public static $DEF_TYPE_MAX = 31;
122
123
124
	/** @var string */
125
	private $id = '';
126
127
	/** @var string */
128
	private $circleId = '';
129
130
	/** @var string */
131
	private $singleId = '';
132
133
	/** @var string */
134
	private $userId = '';
135
136
	/** @var int */
137
	private $userType = 0;
138
139
	/** @var Circle */
140
	private $basedOn;
141
142
	/** @var Member */
143
	private $inheritanceFrom;
144
145
	/** @var FederatedUser */
146
	private $inheritedBy;
147
148
	/** @var string */
149
	private $instance = '';
150
151
	/** @var FederatedUser */
152
	private $invitedBy;
153
154
	/** @var RemoteInstance */
155
	private $remoteInstance;
156
157
	/** @var bool */
158
	private $local = false;
159
160
	/** @var int */
161
	private $level = 0;
162
163
	/** @var string */
164
	private $status = 'Unknown';
165
166
	/** @var array */
167
	private $notes = [];
168
169
	/** @var string */
170
	private $displayName = '';
171
172
	/** @var int */
173
	private $displayUpdate = 0;
174
175
	/** @var string */
176
	private $contactId = '';
177
178
	/** @var string */
179
	private $contactMeta = '';
180
181
	/** @var Circle */
182
	private $circle;
183
184
	/** @var int */
185
	private $joined = 0;
186
187
	/** @var Membership[] */
188
	private $memberships = null;
189
190
191
	/**
192
	 * Member constructor.
193
	 */
194
	public function __construct() {
195
	}
196
197
198
	/**
199
	 * @param string $id
200
	 *
201
	 * @return $this
202
	 */
203
	public function setId(string $id): self {
204
		$this->id = $id;
205
206
		return $this;
207
	}
208
209
	/**
210
	 * @return string
211
	 */
212
	public function getId(): string {
213
		return $this->id;
214
	}
215
216
217
	/**
218
	 * @param string $circleId
219
	 *
220
	 * @return Member
221
	 */
222
	public function setCircleId(string $circleId): self {
223
		$this->circleId = $circleId;
224
225
		return $this;
226
	}
227
228
	/**
229
	 * @return string
230
	 */
231
	public function getCircleId(): string {
232
		return $this->circleId;
233
	}
234
235
236
	/**
237
	 * This should replace user_id, user_type and instance; and will use the data from Circle with
238
	 * Config=CFG_SINGLE
239
	 *
240
	 * @param string $singleId
241
	 *
242
	 * @return $this
243
	 */
244
	public function setSingleId(string $singleId): self {
245
		$this->singleId = $singleId;
246
247
		return $this;
248
	}
249
250
	/**
251
	 * @return string
252
	 */
253
	public function getSingleId(): string {
254
		return $this->singleId;
255
	}
256
257
258
	/**
259
	 * @param string $userId
260
	 *
261
	 * @return Member
262
	 */
263
	public function setUserId(string $userId): self {
264
		$this->userId = $userId;
265
		if ($this->displayName === '') {
266
			$this->displayName = $userId;
267
		}
268
269
		return $this;
270
	}
271
272
	/**
273
	 * @return string
274
	 */
275
	public function getUserId(): string {
276
		return $this->userId;
277
	}
278
279
280
	/**
281
	 * @param int $userType
282
	 *
283
	 * @return Member
284
	 */
285
	public function setUserType(int $userType): self {
286
		$this->userType = $userType;
287
288
		return $this;
289
	}
290
291
	/**
292
	 * @return int
293
	 */
294
	public function getUserType(): int {
295
		return $this->userType;
296
	}
297
298
299
	/**
300
	 * @param string $instance
301
	 *
302
	 * @return Member
303
	 */
304
	public function setInstance(string $instance): self {
305
		$this->instance = $instance;
306
307
		return $this;
308
	}
309
310
	/**
311
	 * @return string
312
	 */
313
	public function getInstance(): string {
314
		return $this->instance;
315
	}
316
317
318
	/**
319
	 * @return bool
320
	 */
321
	public function isLocal(): bool {
322
		return $this->getManager()->isLocalInstance($this->getInstance());
323
	}
324
325
326
	/**
327
	 * @param FederatedUser $invitedBy
328
	 *
329
	 * @return Member
330
	 */
331
	public function setInvitedBy(FederatedUser $invitedBy): Member {
332
		$this->invitedBy = $invitedBy;
333
334
		return $this;
335
	}
336
337
	/**
338
	 * @return FederatedUser
339
	 */
340
	public function getInvitedBy(): FederatedUser {
341
		return $this->invitedBy;
342
	}
343
344
	/**
345
	 * @return bool
346
	 */
347
	public function hasInvitedBy(): bool {
348
		return !is_null($this->invitedBy);
349
	}
350
351
352
	/**
353
	 * @return bool
354
	 */
355
	public function hasRemoteInstance(): bool {
356
		return !is_null($this->remoteInstance);
357
	}
358
359
	/**
360
	 * @param RemoteInstance $remoteInstance
361
	 *
362
	 * @return Member
363
	 */
364
	public function setRemoteInstance(RemoteInstance $remoteInstance): self {
365
		$this->remoteInstance = $remoteInstance;
366
367
		return $this;
368
	}
369
370
	/**
371
	 * @return RemoteInstance
372
	 */
373
	public function getRemoteInstance(): RemoteInstance {
374
		return $this->remoteInstance;
375
	}
376
377
378
	/**
379
	 * @return bool
380
	 */
381
	public function hasBasedOn(): bool {
382
		return !is_null($this->basedOn);
383
	}
384
385
	/**
386
	 * @param Circle $basedOn
387
	 *
388
	 * @return $this
389
	 */
390
	public function setBasedOn(Circle $basedOn): self {
391
		$this->basedOn = $basedOn;
392
393
		return $this;
394
	}
395
396
	/**
397
	 * @return Circle
398
	 */
399
	public function getBasedOn(): Circle {
400
		return $this->basedOn;
401
	}
402
403
404
	/**
405
	 * @return bool
406
	 */
407
	public function hasInheritedBy(): bool {
408
		return !is_null($this->inheritedBy);
409
	}
410
411
	/**
412
	 * @param FederatedUser $inheritedBy
413
	 *
414
	 * @return $this
415
	 */
416
	public function setInheritedBy(FederatedUser $inheritedBy): self {
417
		$this->inheritedBy = $inheritedBy;
418
419
		return $this;
420
	}
421
422
	/**
423
	 * @return FederatedUser
424
	 */
425
	public function getInheritedBy(): FederatedUser {
426
		return $this->inheritedBy;
427
	}
428
429
430
	/**
431
	 * @return bool
432
	 */
433
	public function hasInheritanceFrom(): bool {
434
		return !is_null($this->inheritanceFrom);
435
	}
436
437
	/**
438
	 * @param Member $inheritanceFrom
439
	 *
440
	 * @return $this
441
	 */
442
	public function setInheritanceFrom(Member $inheritanceFrom): self {
443
		$this->inheritanceFrom = $inheritanceFrom;
444
445
		return $this;
446
	}
447
448
	/**
449
	 * @return Member|null
450
	 */
451
	public function getInheritanceFrom(): ?Member {
452
		return $this->inheritanceFrom;
453
	}
454
455
456
	/**
457
	 * @param int $level
458
	 *
459
	 * @return Member
460
	 */
461
	public function setLevel(int $level): self {
462
		$this->level = $level;
463
464
		return $this;
465
	}
466
467
	/**
468
	 * @return int
469
	 */
470
	public function getLevel(): int {
471
		return $this->level;
472
	}
473
474
475
	/**
476
	 * @param string $status
477
	 *
478
	 * @return Member
479
	 */
480
	public function setStatus(string $status): self {
481
		$this->status = $status;
482
483
		return $this;
484
	}
485
486
	/**
487
	 * @return string
488
	 */
489
	public function getStatus(): string {
490
		return $this->status;
491
	}
492
493
494
	/**
495
	 * @param array $notes
496
	 *
497
	 * @return Member
498
	 */
499
	public function setNotes(array $notes): self {
500
		$this->notes = $notes;
501
502
		return $this;
503
	}
504
505
	/**
506
	 * @return array
507
	 */
508
	public function getNotes(): array {
509
		return $this->notes;
510
	}
511
512
513
	/**
514
	 * @param string $key
515
	 *
516
	 * @return string
517
	 */
518
	public function getNote(string $key): string {
519
		return $this->get($key, $this->notes);
520
	}
521
522
	/**
523
	 * @param string $key
524
	 *
525
	 * @return array
526
	 */
527
	public function getNoteArray(string $key): array {
528
		return $this->getArray($key, $this->notes);
529
	}
530
531
	/**
532
	 * @param string $key
533
	 * @param string $note
534
	 *
535
	 * @return $this
536
	 */
537
	public function setNote(string $key, string $note): self {
538
		$this->notes[$key] = $note;
539
540
		return $this;
541
	}
542
543
	/**
544
	 * @param string $key
545
	 * @param array $note
546
	 *
547
	 * @return $this
548
	 */
549
	public function setNoteArray(string $key, array $note): self {
550
		$this->notes[$key] = $note;
551
552
		return $this;
553
	}
554
555
	/**
556
	 * @param string $key
557
	 * @param JsonSerializable $obj
558
	 *
559
	 * @return $this
560
	 */
561
	public function setNoteObj(string $key, JsonSerializable $obj): self {
562
		$this->notes[$key] = $obj;
563
564
		return $this;
565
	}
566
567
568
	/**
569
	 * @param string $displayName
570
	 *
571
	 * @return Member
572
	 */
573
	public function setDisplayName(string $displayName): self {
574
		if ($displayName !== '') {
575
			$this->displayName = $displayName;
576
		}
577
578
		return $this;
579
	}
580
581
582
	/**
583
	 * @param int $displayUpdate
584
	 *
585
	 * @return Member
586
	 */
587
	public function setDisplayUpdate(int $displayUpdate): self {
588
		$this->displayUpdate = $displayUpdate;
589
590
		return $this;
591
	}
592
593
	/**
594
	 * @return int
595
	 */
596
	public function getDisplayUpdate(): int {
597
		return $this->displayUpdate;
598
	}
599
600
601
	/**
602
	 * @return string
603
	 */
604
	public function getDisplayName(): string {
605
		return $this->displayName;
606
	}
607
608
609
	/**
610
	 * @param string $contactId
611
	 *
612
	 * @return Member
613
	 */
614
	public function setContactId(string $contactId): self {
615
		$this->contactId = $contactId;
616
617
		return $this;
618
	}
619
620
	/**
621
	 * @return string
622
	 */
623
	public function getContactId(): string {
624
		return $this->contactId;
625
	}
626
627
628
	/**
629
	 * @param string $contactMeta
630
	 *
631
	 * @return Member
632
	 */
633
	public function setContactMeta(string $contactMeta): self {
634
		$this->contactMeta = $contactMeta;
635
636
		return $this;
637
	}
638
639
	/**
640
	 * @return string
641
	 */
642
	public function getContactMeta(): string {
643
		return $this->contactMeta;
644
	}
645
646
647
	/**
648
	 * @param Circle $circle
649
	 *
650
	 * @return self
651
	 */
652
	public function setCircle(Circle $circle): self {
653
		$this->circle = $circle;
654
655
		return $this;
656
	}
657
658
	/**
659
	 * @return Circle
660
	 */
661
	public function getCircle(): Circle {
662
		return $this->circle;
663
	}
664
665
	/**
666
	 * @return bool
667
	 */
668
	public function hasCircle(): bool {
669
		return (!is_null($this->circle));
670
	}
671
672
673
	/**
674
	 * @param int $joined
675
	 *
676
	 * @return Member
677
	 */
678
	public function setJoined(int $joined): self {
679
		$this->joined = $joined;
680
681
		return $this;
682
	}
683
684
	/**
685
	 * @return int
686
	 */
687
	public function getJoined(): int {
688
		return $this->joined;
689
	}
690
691
692
	/**
693
	 * @return bool
694
	 */
695
	public function hasMemberships(): bool {
696
		return !is_null($this->memberships);
697
	}
698
699
	/**
700
	 * @param array $memberships
701
	 *
702
	 * @return self
703
	 */
704
	public function setMemberships(array $memberships): IMemberships {
705
		$this->memberships = $memberships;
706
707
		return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (OCA\Circles\Model\Member) 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...
708
	}
709
710
	/**
711
	 * @return Membership[]
712
	 */
713
	public function getMemberships(): array {
714
		if (is_null($this->memberships)) {
715
			$this->getManager()->getMemberships($this);
716
		}
717
718
		return $this->memberships;
719
	}
720
721
722
	/**
723
	 * @param Member $member
724
	 * @param bool $full
725
	 *
726
	 * @return bool
727
	 */
728
	public function compareWith(Member $member, bool $full = true): bool {
729
		if ($this->getId() !== $member->getId()
730
			|| $this->getCircleId() !== $member->getCircleId()
731
			|| $this->getSingleId() !== $member->getSingleId()
732
			|| $this->getUserId() !== $member->getUserId()
733
			|| $this->getUserType() <> $member->getUserType()
734
			|| $this->getInstance() !== $member->getInstance()) {
735
			return false;
736
		}
737
738
		if ($full
739
			&& ($this->getLevel() <> $member->getLevel()
740
				|| $this->getStatus() !== $member->getStatus())) {
741
			return false;
742
		}
743
744
		return true;
745
	}
746
747
748
	/**
749
	 * @param array $data
750
	 *
751
	 * @return $this
752
	 * @throws InvalidItemException
753
	 */
754
	public function import(array $data): IDeserializable {
755
		if ($this->get('userId', $data) === '') {
756
			throw new InvalidItemException();
757
		}
758
759
		$this->setId($this->get('id', $data));
760
		$this->setCircleId($this->get('circleId', $data));
761
		$this->setSingleId($this->get('singleId', $data));
762
		$this->setUserId($this->get('userId', $data));
763
		$this->setUserType($this->getInt('userType', $data));
764
		$this->setInstance($this->get('instance', $data));
765
		$this->setLevel($this->getInt('level', $data));
766
		$this->setStatus($this->get('status', $data));
767
		$this->setDisplayName($this->get('displayName', $data));
768
		$this->setDisplayUpdate($this->getInt('displayUpdate', $data));
769
		$this->setNotes($this->getArray('notes', $data));
770
		$this->setContactId($this->get('contactId', $data));
771
		$this->setContactMeta($this->get('contactMeta', $data));
772
		$this->setJoined($this->getInt('joined', $data));
773
774
		try {
775
			/** @var Circle $circle */
776
			$circle = $this->deserialize($this->getArray('circle', $data), Circle::class);
777
			$this->setCircle($circle);
778
		} catch (InvalidItemException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
779
		}
780
781
		try {
782
			/** @var Circle $circle */
783
			$circle = $this->deserialize($this->getArray('basedOn', $data), Circle::class);
784
			$this->setBasedOn($circle);
785
		} catch (InvalidItemException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
786
		}
787
788
		try {
789
			/** @var FederatedUser $invitedBy */
790
			$invitedBy = $this->deserialize($this->getArray('invitedBy', $data), FederatedUser::class);
791
			$this->setInvitedBy($invitedBy);
792
		} catch (InvalidItemException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
793
		}
794
795
		try {
796
			/** @var FederatedUSer $inheritedBy */
797
			$inheritedBy = $this->deserialize($this->getArray('inheritedBy', $data), Membership::class);
798
			$this->setInheritedBy($inheritedBy);
799
		} catch (InvalidItemException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
800
		}
801
802
		return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (OCA\Circles\Model\Member) 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...
803
	}
804
805
806
	/**
807
	 * @param array $data
808
	 * @param string $prefix
809
	 *
810
	 * @return INC22QueryRow
811
	 * @throws MemberNotFoundException
812
	 */
813
	public function importFromDatabase(array $data, string $prefix = ''): INC22QueryRow {
814
		if ($this->get($prefix . 'single_id', $data) === '') {
815
			throw new MemberNotFoundException();
816
		}
817
818
		$this->setId($this->get($prefix . 'member_id', $data));
819
		$this->setCircleId($this->get($prefix . 'circle_id', $data));
820
		$this->setSingleId($this->get($prefix . 'single_id', $data));
821
		$this->setUserId($this->get($prefix . 'user_id', $data));
822
		$this->setUserType($this->getInt($prefix . 'user_type', $data));
823
		$this->setInstance($this->get($prefix . 'instance', $data));
824
		$this->setLevel($this->getInt($prefix . 'level', $data));
825
		$this->setStatus($this->get($prefix . 'status', $data));
826
		$this->setDisplayName($this->get($prefix . 'cached_name', $data));
827
		$this->setNotes($this->getArray($prefix . 'note', $data));
828
		$this->setContactId($this->get($prefix . 'contact_id', $data));
829
		$this->setContactMeta($this->get($prefix . 'contact_meta', $data));
830
831
		$cachedUpdate = $this->get($prefix . 'cached_update', $data);
832
		if ($cachedUpdate !== '') {
833
			$this->setDisplayUpdate(DateTime::createFromFormat('Y-m-d H:i:s', $cachedUpdate)->getTimestamp());
834
		}
835
836
		$joined = $this->get($prefix . 'joined', $data);
837
		if ($joined !== '') {
838
			$this->setJoined(DateTime::createFromFormat('Y-m-d H:i:s', $joined)->getTimestamp());
839
		}
840
841
		if ($this->getInstance() === '') {
842
			$this->setInstance($this->getManager()->getLocalInstance());
843
		}
844
845
		$this->getManager()->manageImportFromDatabase($this, $data, $prefix);
846
847
		// in case invitedBy is not obtainable from 'invited_by', we reach data from 'note'
848
		if (!$this->hasInvitedBy()) {
849
			$invitedByArray = $this->getNoteArray('invitedBy');
850
			if (!empty($invitedByArray)) {
851
				try {
852
					$invitedBy = new FederatedUser();
853
					$this->setInvitedBy($invitedBy->import($invitedByArray));
854
				} catch (InvalidItemException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
855
				}
856
			}
857
		}
858
859
		return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (OCA\Circles\Model\Member) 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...
860
	}
861
862
863
	/**
864
	 * @return string[]
865
	 * @throws UnknownInterfaceException
866
	 */
867
	public function jsonSerialize(): array {
868
		$arr = [
869
			'id'            => $this->getId(),
870
			'circleId'      => $this->getCircleId(),
871
			'singleId'      => $this->getSingleId(),
872
			'userId'        => $this->getUserId(),
873
			'userType'      => $this->getUserType(),
874
			'instance'      => $this->getManager()->fixInstance($this->getInstance()),
875
			'local'         => $this->isLocal(),
876
			'level'         => $this->getLevel(),
877
			'status'        => $this->getStatus(),
878
			'displayName'   => $this->getDisplayName(),
879
			'displayUpdate' => $this->getDisplayUpdate(),
880
			'notes'         => $this->getNotes(),
881
			'contactId'     => $this->getContactId(),
882
			'contactMeta'   => $this->getContactMeta(),
883
			'joined'        => $this->getJoined()
884
		];
885
886
		if ($this->hasInvitedBy()) {
887
			$arr['invitedBy'] = $this->getInvitedBy();
888
		}
889
890
		if ($this->hasBasedOn()) {
891
			$arr['basedOn'] = $this->getBasedOn();
892
		}
893
894
		if ($this->hasInheritedBy()) {
895
			$arr['inheritedBy'] = $this->getInheritedBy();
896
		}
897
898
		if ($this->hasInheritanceFrom()) {
899
			$arr['inheritanceFrom'] = $this->getInheritanceFrom();
900
		}
901
902
		if ($this->hasCircle()) {
903
			$arr['circle'] = $this->getCircle();
904
		}
905
906
		if ($this->hasMemberships()) {
907
			$arr['memberships'] = $this->getMemberships();
908
		}
909
910
		if ($this->hasRemoteInstance()) {
911
			$arr['remoteInstance'] = $this->getRemoteInstance();
912
		}
913
914
		return $arr;
915
	}
916
917
918
	/**
919
	 * @param int $level
920
	 *
921
	 * @return int
922
	 * @throws ParseMemberLevelException
923
	 */
924
	public static function parseLevelInt(int $level): int {
925
		if (!array_key_exists($level, self::$DEF_LEVEL)) {
926
			$all = implode(', ', array_keys(self::$DEF_LEVEL));
927
			throw new ParseMemberLevelException('Available levels: ' . $all, 121);
928
		}
929
930
		return $level;
931
	}
932
933
934
	/**
935
	 * @param string $levelString
936
	 *
937
	 * @return int
938
	 * @throws ParseMemberLevelException
939
	 */
940
	public static function parseLevelString(string $levelString): int {
941
		$levelString = ucfirst(strtolower($levelString));
942
		$level = array_search($levelString, Member::$DEF_LEVEL);
943
944
		if (!$level) {
945
			$all = implode(', ', array_values(self::$DEF_LEVEL));
946
			throw new ParseMemberLevelException('Available levels: ' . $all, 121);
947
		}
948
949
		return (int)$level;
950
	}
951
952
	/**
953
	 * @param string $typeString
954
	 *
955
	 * @return int
956
	 * @throws UserTypeNotFoundException
957
	 */
958
	public static function parseTypeString(string $typeString): int {
959
		$typeString = strtolower($typeString);
960
		if (array_key_exists($typeString, Member::$TYPE)) {
961
			return (int)$typeString;
962
		}
963
964
		$type = array_search($typeString, Member::$TYPE);
965
		if ($type === false) {
966
			$all = implode(', ', array_values(self::$TYPE));
967
			throw new UserTypeNotFoundException('Available types: ' . $all);
968
		}
969
970
		return (int)$type;
971
	}
972
973
}
974
975