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

Circle::getUniqueId()   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
35
use daita\MySmallPhpTools\Db\Nextcloud\nc22\INC22QueryRow;
36
use daita\MySmallPhpTools\Exceptions\InvalidItemException;
37
use daita\MySmallPhpTools\IDeserializable;
38
use daita\MySmallPhpTools\Traits\Nextcloud\nc22\TNC22Deserialize;
39
use daita\MySmallPhpTools\Traits\TArrayTools;
40
use DateTime;
41
use JsonSerializable;
42
use OCA\Circles\Exceptions\CircleNotFoundException;
43
use OCA\Circles\Exceptions\OwnerNotFoundException;
44
use OCA\Circles\IMemberships;
45
46
47
/**
48
 * Class Circle
49
 *
50
 * ** examples of use of bitwise flags for members management:
51
 *      CFG_OPEN, CFG_REQUEST, CFG_INVITE, CFG_FRIEND
52
 *
53
 * - CFG_OPEN                             => everyone can enter. moderator can add members.
54
 * - CFG_OPEN | CFG_REQUEST               => anyone can initiate a request to join the circle, moderator can
55
 *                                           add members
56
 * - CFG_OPEN | CFG_INVITE                => every one can enter, moderator must send invitation.
57
 * - CFG_OPEN | CFG_INVITE | CFG_REQUEST  => every one send a request, moderator must send invitation.
58
 * - CFG_OPEN | CFG_FRIEND                => useless
59
 * - CFG_OPEN | CFG_FRIEND | *            => useless
60
 *
61
 * - CFG_CIRCLE                           => no one can enter, moderator can add members.
62
 *                                           default config, this is only for code readability.
63
 * - CFG_INVITE                           => no one can enter, moderator must send invitation.
64
 * - CFG_FRIEND                           => no one can enter, but all members can add new member.
65
 * - CFG_REQUEST                          => useless (use CFG_OPEN | CFG_REQUEST)
66
 * - CFG_FRIEND | CFG_REQUEST             => no one can join the circle, but all members can request a
67
 *                                           moderator to accept new member
68
 * - CFG_FRIEND | CFG_INVITE              => no one can join the circle, but all members can add new member.
69
 *                                           An invitation will be generated
70
 * - CFG_FRIEND | CFG_INVITE | CFG_REQUEST  => no one can join the circle, but all members can request a
71
 *                                             moderator to accept new member. An invitation will be generated
72
 *
73
 * @package OCA\Circles\Model
74
 */
75
class Circle extends ManagedModel implements IMemberships, IDeserializable, INC22QueryRow, JsonSerializable {
76
77
78
	use TArrayTools;
79
	use TNC22Deserialize;
80
81
82
	const FLAGS_SHORT = 1;
83
	const FLAGS_LONG = 2;
84
85
86
	// specific value
87
	const CFG_CIRCLE = 0;        // only for code readability. Circle is locked by default.
88
	const CFG_SINGLE = 1;        // Circle with only one single member.
89
	const CFG_PERSONAL = 2;      // Personal circle, only the owner can see it.
90
91
	// bitwise
92
	const CFG_SYSTEM = 4;            // System Circle (not managed by the official front-end). Meaning some config are limited
93
	const CFG_VISIBLE = 8;           // Visible to everyone, if not visible, people have to know its name to be able to find it
94
	const CFG_OPEN = 16;             // Circle is open, people can join
95
	const CFG_INVITE = 32;           // Adding a member generate an invitation that needs to be accepted
96
	const CFG_REQUEST = 64;          // Request to join Circles needs to be confirmed by a moderator
97
	const CFG_FRIEND = 128;          // Members of the circle can invite their friends
98
	const CFG_PROTECTED = 256;       // Password protected to join/request
99
	const CFG_NO_OWNER = 512;        // no owner, only members
100
	const CFG_HIDDEN = 1024;         // hidden from listing, but available as a share entity
101
	const CFG_BACKEND = 2048;            // Fully hidden, only backend Circles
102
	const CFG_LOCAL = 4096;              // Local even on GlobalScale
103
	const CFG_ROOT = 8192;               // Circle cannot be inside another Circle
104
	const CFG_CIRCLE_INVITE = 16384;     // Circle must confirm when invited in another circle
105
	const CFG_FEDERATED = 32768;         // Federated
106
	const CFG_MOUNTPOINT = 65536;        // Generate a Files folder for this Circle
107
108
	public static $DEF_CFG_MAX = 131071;
109
110
111
	/**
112
	 * Note: When editing those values, update lib/Application/Capabilities.php
113
	 *
114
	 * @see Capabilities::getCapabilitiesCircleConstants()
115
	 * @var array
116
	 */
117
	public static $DEF_CFG = [
118
		1     => 'S|Single',
119
		2     => 'P|Personal',
120
		4     => 'Y|System',
121
		8     => 'V|Visible',
122
		16    => 'O|Open',
123
		32    => 'I|Invite',
124
		64    => 'JR|Join Request',
125
		128   => 'F|Friends',
126
		256   => 'PP|Password Protected',
127
		512   => 'NO|No Owner',
128
		1024  => 'H|Hidden',
129
		2048  => 'T|Backend',
130
		4096  => 'L|Local',
131
		8192  => 'T|Root',
132
		16384 => 'CI|Circle Invite',
133
		32768 => 'F|Federated',
134
		65536 => 'M|Nountpoint'
135
	];
136
137
138
	/**
139
	 * Note: When editing those values, update lib/Application/Capabilities.php
140
	 *
141
	 * @see Capabilities::getCapabilitiesCircleConstants()
142
	 * @var array
143
	 */
144
	public static $DEF_SOURCE = [
145
		1     => 'Nextcloud User',
146
		2     => 'Nextcloud Group',
147
		4     => 'Mail Address',
148
		8     => 'Contact',
149
		16    => 'Circle',
150
		10001 => 'Circles App',
151
		10002 => 'occ Command Line'
152
	];
153
154
155
	public static $DEF_CFG_CORE_FILTER = [
156
		1,
157
		2,
158
		4
159
	];
160
161
	public static $DEF_CFG_SYSTEM_FILTER = [
162
		512,
163
		1024,
164
		2048
165
	];
166
167
168
	/** @var string */
169
	private $singleId = '';
170
171
	/** @var int */
172
	private $config = 0;
173
174
	/** @var string */
175
	private $name = '';
176
177
	/** @var string */
178
	private $displayName = '';
179
180
	/** @var int */
181
	private $source = 0;
182
183
	/** @var Member */
184
	private $owner;
185
186
	/** @var Member */
187
	private $initiator;
188
189
	/** @var array */
190
	private $settings = [];
191
192
	/** @var string */
193
	private $description = '';
194
195
	/** @var int */
196
	private $contactAddressBook = 0;
197
198
	/** @var string */
199
	private $contactGroupName = '';
200
201
	/** @var string */
202
	private $instance = '';
203
204
//	/** @var bool */
205
//	private $hidden = false;
206
207
	/** @var int */
208
	private $creation = 0;
209
210
211
	/** @var Member[] */
212
	private $members = null;
213
214
	/** @var Member[] */
215
	private $inheritedMembers = null;
216
217
	/** @var bool */
218
	private $detailedInheritedMember = false;
219
220
	/** @var Membership[] */
221
	private $memberships = null;
222
223
224
	/**
225
	 * Circle constructor.
226
	 */
227
	public function __construct() {
228
	}
229
230
	/**
231
	 * @param string $singleId
232
	 *
233
	 * @return self
234
	 */
235
	public function setSingleId(string $singleId): self {
236
		$this->singleId = $singleId;
237
238
		return $this;
239
	}
240
241
	/**
242
	 * @return string
243
	 */
244
	public function getSingleId(): string {
245
		return $this->singleId;
246
	}
247
248
	/**
249
	 * @return string
250
	 * @deprecated - removed in NC23
251
	 */
252
	public function getUniqueId(): string {
253
		return $this->getSingleId();
254
	}
255
256
	/**
257
	 * @param int $config
258
	 *
259
	 * @return self
260
	 */
261
	public function setConfig(int $config): self {
262
		$this->config = $config;
263
264
		return $this;
265
	}
266
267
	/**
268
	 * @return int
269
	 */
270
	public function getConfig(): int {
271
		return $this->config;
272
	}
273
274
	/**
275
	 * @param int $flag
276
	 * @param int $test
277
	 *
278
	 * @return bool
279
	 */
280
	public function isConfig(int $flag, int $test = 0): bool {
281
		if ($test === 0) {
282
			$test = $this->getConfig();
283
		}
284
285
		return (($test & $flag) !== 0);
286
	}
287
288
	/**
289
	 * @param int $flag
290
	 */
291
	public function addConfig(int $flag): void {
292
		if (!$this->isConfig($flag)) {
293
			$this->config += $flag;
294
		}
295
	}
296
297
	/**
298
	 * @param int $flag
299
	 */
300
	public function remConfig(int $flag): void {
301
		if ($this->isConfig($flag)) {
302
			$this->config -= $flag;
303
		}
304
	}
305
306
307
	/**
308
	 * @param string $name
309
	 *
310
	 * @return self
311
	 */
312
	public function setName(string $name): self {
313
		$this->name = $name;
314
		if ($this->displayName === '') {
315
			$this->displayName = $name;
316
		}
317
318
		return $this;
319
	}
320
321
	/**
322
	 * @return string
323
	 */
324
	public function getName(): string {
325
		return $this->name;
326
	}
327
328
329
	/**
330
	 * @param string $displayName
331
	 *
332
	 * @return self
333
	 */
334
	public function setDisplayName(string $displayName): self {
335
		if ($displayName !== '') {
336
			$this->displayName = $displayName;
337
		}
338
339
		return $this;
340
	}
341
342
	/**
343
	 * @return string
344
	 */
345
	public function getDisplayName(): string {
346
		return $this->displayName;
347
	}
348
349
350
	/**
351
	 * @param int $source
352
	 *
353
	 * @return Circle
354
	 */
355
	public function setSource(int $source): self {
356
		$this->source = $source;
357
358
		return $this;
359
	}
360
361
	/**
362
	 * @return int
363
	 */
364
	public function getSource(): int {
365
		return $this->source;
366
	}
367
368
369
	/**
370
	 * @param ?Member $owner
0 ignored issues
show
Documentation introduced by
The doc-type ?Member could not be parsed: Unknown type name "?Member" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
371
	 *
372
	 * @return self
373
	 */
374
	public function setOwner(?Member $owner): self {
375
		$this->owner = $owner;
376
377
		return $this;
378
	}
379
380
	/**
381
	 * @return Member
382
	 */
383
	public function getOwner(): Member {
384
		return $this->owner;
385
	}
386
387
	/**
388
	 * @return bool
389
	 */
390
	public function hasOwner(): bool {
391
		return ($this->owner !== null);
392
	}
393
394
395
	/**
396
	 * @return bool
397
	 */
398
	public function hasMembers(): bool {
399
		return !is_null($this->members);
400
	}
401
402
	/**
403
	 * @param array $members
404
	 *
405
	 * @return self
406
	 */
407
	public function setMembers(array $members): IMemberships {
408
		$this->members = $members;
409
410
		return $this;
411
	}
412
413
	/**
414
	 * @return array
415
	 */
416
	public function getMembers(): array {
417
		if (!$this->hasMembers()) {
418
			$this->getManager()->getMembers($this);
419
		}
420
421
		return $this->members;
422
	}
423
424
425
	/**
426
	 * @param array $members
427
	 * @param bool $detailed
428
	 *
429
	 * @return self
430
	 */
431
	public function setInheritedMembers(array $members, bool $detailed): IMemberships {
432
		$this->inheritedMembers = $members;
433
		$this->detailedInheritedMember = $detailed;
434
435
		return $this;
436
	}
437
438
	/**
439
	 * @param bool $detailed
440
	 *
441
	 * @return Member[]
442
	 */
443
	public function getInheritedMembers(bool $detailed = false): array {
444
		if (is_null($this->inheritedMembers)
445
			|| ($detailed && !$this->detailedInheritedMember)) {
446
			$this->getManager()->getInheritedMembers($this, $detailed);
447
		}
448
449
		return $this->inheritedMembers;
450
	}
451
452
453
	/**
454
	 * @return bool
455
	 */
456
	public function hasMemberships(): bool {
457
		return !is_null($this->memberships);
458
	}
459
460
	/**
461
	 * @param array $memberships
462
	 *
463
	 * @return self
464
	 */
465
	public function setMemberships(array $memberships): IMemberships {
466
		$this->memberships = $memberships;
467
468
		return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (OCA\Circles\Model\Circle) 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...
469
	}
470
471
	/**
472
	 * @return Membership[]
473
	 */
474
	public function getMemberships(): array {
475
		if (!$this->hasMemberships()) {
476
			$this->getManager()->getMemberships($this);
477
		}
478
479
		return $this->memberships;
480
	}
481
482
483
	/**
484
	 * @param Member|null $initiator
485
	 *
486
	 * @return Circle
487
	 */
488
	public function setInitiator(?Member $initiator): self {
489
		$this->initiator = $initiator;
490
491
		return $this;
492
	}
493
494
	/**
495
	 * @return Member
496
	 */
497
	public function getInitiator(): Member {
498
		return $this->initiator;
499
	}
500
501
	/**
502
	 * @return bool
503
	 */
504
	public function hasInitiator(): bool {
505
		return ($this->initiator !== null);
506
	}
507
508
	/**
509
	 * @param string $instance
510
	 *
511
	 * @return Circle
512
	 */
513
	public function setInstance(string $instance): self {
514
		if ($this->isConfig(self::CFG_NO_OWNER)) {
515
			$this->instance = $instance;
516
		}
517
518
		return $this;
519
	}
520
521
	/**
522
	 * @return string
523
	 * @throws OwnerNotFoundException
524
	 */
525
	public function getInstance(): string {
526
		if (!$this->hasOwner()) {
527
			throw new OwnerNotFoundException('circle has no owner');
528
		}
529
530
		return $this->getOwner()->getInstance();
531
	}
532
533
534
	/**
535
	 * @param array $settings
536
	 *
537
	 * @return self
538
	 */
539
	public function setSettings(array $settings): self {
540
		$this->settings = $settings;
541
542
		return $this;
543
	}
544
545
	/**
546
	 * @return array
547
	 */
548
	public function getSettings(): array {
549
		return $this->settings;
550
	}
551
552
553
	/**
554
	 * @param string $description
555
	 *
556
	 * @return self
557
	 */
558
	public function setDescription(string $description): self {
559
		$this->description = $description;
560
561
		return $this;
562
	}
563
564
	/**
565
	 * @return string
566
	 */
567
	public function getDescription(): string {
568
		return $this->description;
569
	}
570
571
572
	/**
573
	 * @return string
574
	 */
575
	public function getUrl(): string {
576
		return $this->getManager()->generateLinkToCircle($this->getSingleId());
577
	}
578
579
580
	/**
581
	 * @param int $contactAddressBook
582
	 *
583
	 * @return self
584
	 */
585
	public function setContactAddressBook(int $contactAddressBook): self {
586
		$this->contactAddressBook = $contactAddressBook;
587
588
		return $this;
589
	}
590
591
	/**
592
	 * @return int
593
	 */
594
	public function getContactAddressBook(): int {
595
		return $this->contactAddressBook;
596
	}
597
598
599
	/**
600
	 * @param string $contactGroupName
601
	 *
602
	 * @return self
603
	 */
604
	public function setContactGroupName(string $contactGroupName): self {
605
		$this->contactGroupName = $contactGroupName;
606
607
		return $this;
608
	}
609
610
	/**
611
	 * @return string
612
	 */
613
	public function getContactGroupName(): string {
614
		return $this->contactGroupName;
615
	}
616
617
618
	/**
619
	 * @param int $creation
620
	 *
621
	 * @return self
622
	 */
623
	public function setCreation(int $creation): self {
624
		$this->creation = $creation;
625
626
		return $this;
627
	}
628
629
	/**
630
	 * @return int
631
	 */
632
	public function getCreation(): int {
633
		return $this->creation;
634
	}
635
636
637
	/**
638
	 * @param array $data
639
	 *
640
	 * @return $this
641
	 * @throws InvalidItemException
642
	 */
643
	public function import(array $data): IDeserializable {
644
		if ($this->get('id', $data) === '') {
645
			throw new InvalidItemException();
646
		}
647
648
		$this->setSingleId($this->get('id', $data))
649
			 ->setName($this->get('name', $data))
650
			 ->setDisplayName($this->get('displayName', $data))
651
			 ->setSource($this->getInt('source', $data))
652
			 ->setConfig($this->getInt('config', $data))
653
			 ->setSettings($this->getArray('settings', $data))
654
//			 ->setContactAddressBook($this->get('contact_addressbook', $data))
655
//			 ->setContactGroupName($this->get('contact_groupname', $data))
656
			 ->setDescription($this->get('description', $data))
657
			 ->setCreation($this->getInt('creation', $data));
658
659
		try {
660
			/** @var Member $owner */
661
			$owner = $this->deserialize($this->getArray('owner', $data), Member::class);
662
			$this->setOwner($owner);
663
		} catch (InvalidItemException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
664
		}
665
666
		try {
667
			/** @var Member $initiator */
668
			$initiator = $this->deserialize($this->getArray('initiator', $data), Member::class);
669
			$this->setInitiator($initiator);
670
		} catch (InvalidItemException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
671
		}
672
673
		return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (OCA\Circles\Model\Circle) 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...
674
	}
675
676
677
	/**
678
	 * @return array
679
	 */
680
	public function jsonSerialize(): array {
681
		$arr = [
682
			'id'          => $this->getSingleId(),
683
			'name'        => $this->getName(),
684
			'displayName' => $this->getDisplayName(),
685
			'source'      => $this->getSource(),
686
			'config'      => $this->getConfig(),
687
			'description' => $this->getDescription(),
688
			'settings'    => $this->getSettings(),
689
			'url'         => $this->getUrl(),
690
			'creation'    => $this->getCreation(),
691
			'initiator'   => ($this->hasInitiator()) ? $this->getInitiator() : null
692
		];
693
694
		if ($this->hasOwner()) {
695
			$arr['owner'] = $this->getOwner();
696
		}
697
698
		if ($this->hasMembers()) {
699
			$arr['members'] = $this->getMembers();
700
		}
701
702
		if (!is_null($this->inheritedMembers)) {
703
			$arr['inheritedMembers'] = $this->getInheritedMembers();
704
		}
705
706
		if ($this->hasMemberships()) {
707
			$arr['memberships'] = $this->getMemberships();
708
		}
709
710
		return $arr;
711
	}
712
713
714
	/**
715
	 * @param array $data
716
	 * @param string $prefix
717
	 *
718
	 * @return INC22QueryRow
719
	 * @throws CircleNotFoundException
720
	 */
721
	public function importFromDatabase(array $data, string $prefix = ''): INC22QueryRow {
722
		if ($this->get($prefix . 'unique_id', $data) === '') {
723
			throw new CircleNotFoundException();
724
		}
725
726
		$this->setSingleId($this->get($prefix . 'unique_id', $data))
727
			 ->setName($this->get($prefix . 'name', $data))
728
			 ->setDisplayName($this->get($prefix . 'display_name', $data))
729
			 ->setConfig($this->getInt($prefix . 'config', $data))
730
			 ->setSource($this->getInt($prefix . 'source', $data))
731
			 ->setInstance($this->get($prefix . 'instance', $data))
732
			 ->setSettings($this->getArray($prefix . 'settings', $data))
733
			 ->setContactAddressBook($this->getInt($prefix . 'contact_addressbook', $data))
734
			 ->setContactGroupName($this->get($prefix . 'contact_groupname', $data))
735
			 ->setDescription($this->get($prefix . 'description', $data));
736
737
		$creation = $this->get($prefix . 'creation', $data);
738
		$this->setCreation(DateTime::createFromFormat('Y-m-d H:i:s', $creation)->getTimestamp());
739
740
		$this->getManager()->manageImportFromDatabase($this, $data, $prefix);
741
742
		return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (OCA\Circles\Model\Circle) 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...
743
	}
744
745
746
	/**
747
	 * @param Circle $circle
748
	 *
749
	 * @return bool
750
	 * @throws OwnerNotFoundException
751
	 */
752
	public function compareWith(Circle $circle): bool {
753
		if ($this->getSingleId() !== $circle->getSingleId()
754
			|| $this->getInstance() !== $circle->getInstance()
755
			|| $this->getConfig() !== $circle->getConfig()) {
756
			return false;
757
		}
758
759
		if ($this->hasOwner()
760
			&& (!$circle->hasOwner()
761
				|| !$this->getOwner()->compareWith($circle->getOwner()))) {
762
			return false;
763
		}
764
765
		if ($this->hasInitiator()
766
			&& (!$circle->hasInitiator()
767
				|| !$this->getInitiator()->compareWith($circle->getInitiator()))) {
768
			return false;
769
		}
770
771
		return true;
772
	}
773
774
775
	/**
776
	 * @param Circle $circle
777
	 * @param int $display
778
	 *
779
	 * @return array
780
	 */
781
	public static function getCircleFlags(Circle $circle, int $display = self::FLAGS_LONG): array {
782
		$config = [];
783
		foreach (array_keys(Circle::$DEF_CFG) as $def) {
784
			if ($circle->isConfig($def)) {
785
				list($short, $long) = explode('|', Circle::$DEF_CFG[$def]);
786
				switch ($display) {
787
788
					case self::FLAGS_SHORT:
789
						$config[] = $short;
790
						break;
791
792
					case self::FLAGS_LONG:
793
						$config[] = $long;
794
						break;
795
				}
796
			}
797
		}
798
799
		return $config;
800
	}
801
802
}
803
804