Completed
Push — master ( c57b95...1d3e11 )
by Maxence
30s queued 14s
created

Member::getMembers()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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