Completed
Push — master ( 44cf85...f9cb43 )
by Maxence
03:20 queued 49s
created

Circle::hasInitiator()   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
	];
152
153
154
	public static $DEF_CFG_CORE_FILTER = [
155
		1,
156
		2,
157
		4
158
	];
159
160
	public static $DEF_CFG_SYSTEM_FILTER = [
161
		512,
162
		1024,
163
		2048
164
	];
165
166
167
	/** @var string */
168
	private $singleId = '';
169
170
	/** @var int */
171
	private $config = 0;
172
173
	/** @var string */
174
	private $name = '';
175
176
	/** @var string */
177
	private $displayName = '';
178
179
	/** @var int */
180
	private $source = 0;
181
182
	/** @var Member */
183
	private $owner;
184
185
	/** @var Member */
186
	private $initiator;
187
188
	/** @var array */
189
	private $settings = [];
190
191
	/** @var string */
192
	private $description = '';
193
194
	/** @var int */
195
	private $contactAddressBook = 0;
196
197
	/** @var string */
198
	private $contactGroupName = '';
199
200
	/** @var string */
201
	private $instance = '';
202
203
//	/** @var bool */
204
//	private $hidden = false;
205
206
	/** @var int */
207
	private $creation = 0;
208
209
210
	/** @var Member[] */
211
	private $members = null;
212
213
	/** @var Member[] */
214
	private $inheritedMembers = null;
215
216
	/** @var bool */
217
	private $detailedInheritedMember = false;
218
219
	/** @var Membership[] */
220
	private $memberships = null;
221
222
223
	/**
224
	 * Circle constructor.
225
	 */
226
	public function __construct() {
227
	}
228
229
	/**
230
	 * @param string $singleId
231
	 *
232
	 * @return self
233
	 */
234
	public function setSingleId(string $singleId): self {
235
		$this->singleId = $singleId;
236
237
		return $this;
238
	}
239
240
	/**
241
	 * @return string
242
	 */
243
	public function getSingleId(): string {
244
		return $this->singleId;
245
	}
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
	/**
258
	 * @param int $config
259
	 *
260
	 * @return self
261
	 */
262
	public function setConfig(int $config): self {
263
		$this->config = $config;
264
265
		return $this;
266
	}
267
268
	/**
269
	 * @return int
270
	 */
271
	public function getConfig(): int {
272
		return $this->config;
273
	}
274
275
	/**
276
	 * @param int $flag
277
	 * @param int $test
278
	 *
279
	 * @return bool
280
	 */
281
	public function isConfig(int $flag, int $test = 0): bool {
282
		if ($test === 0) {
283
			$test = $this->getConfig();
284
		}
285
286
		return (($test & $flag) !== 0);
287
	}
288
289
	/**
290
	 * @param int $flag
291
	 */
292
	public function addConfig(int $flag): void {
293
		if (!$this->isConfig($flag)) {
294
			$this->config += $flag;
295
		}
296
	}
297
298
	/**
299
	 * @param int $flag
300
	 */
301
	public function remConfig(int $flag): void {
302
		if ($this->isConfig($flag)) {
303
			$this->config -= $flag;
304
		}
305
	}
306
307
308
	/**
309
	 * @param string $name
310
	 *
311
	 * @return self
312
	 */
313
	public function setName(string $name): self {
314
		$this->name = $name;
315
		if ($this->displayName === '') {
316
			$this->displayName = $name;
317
		}
318
319
		return $this;
320
	}
321
322
	/**
323
	 * @return string
324
	 */
325
	public function getName(): string {
326
		return $this->name;
327
	}
328
329
330
	/**
331
	 * @param string $displayName
332
	 *
333
	 * @return self
334
	 */
335
	public function setDisplayName(string $displayName): self {
336
		if ($displayName !== '') {
337
			$this->displayName = $displayName;
338
		}
339
340
		return $this;
341
	}
342
343
	/**
344
	 * @return string
345
	 */
346
	public function getDisplayName(): string {
347
		return $this->displayName;
348
	}
349
350
351
	/**
352
	 * @param int $source
353
	 *
354
	 * @return Circle
355
	 */
356
	public function setSource(int $source): self {
357
		$this->source = $source;
358
359
		return $this;
360
	}
361
362
	/**
363
	 * @return int
364
	 */
365
	public function getSource(): int {
366
		return $this->source;
367
	}
368
369
370
	/**
371
	 * @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...
372
	 *
373
	 * @return self
374
	 */
375
	public function setOwner(?Member $owner): self {
376
		$this->owner = $owner;
377
378
		return $this;
379
	}
380
381
	/**
382
	 * @return Member
383
	 */
384
	public function getOwner(): Member {
385
		return $this->owner;
386
	}
387
388
	/**
389
	 * @return bool
390
	 */
391
	public function hasOwner(): bool {
392
		return ($this->owner !== null);
393
	}
394
395
396
	/**
397
	 * @return bool
398
	 */
399
	public function hasMembers(): bool {
400
		return !is_null($this->members);
401
	}
402
403
	/**
404
	 * @param array $members
405
	 *
406
	 * @return self
407
	 */
408
	public function setMembers(array $members): IMemberships {
409
		$this->members = $members;
410
411
		return $this;
412
	}
413
414
	/**
415
	 * @return array
416
	 */
417
	public function getMembers(): array {
418
		if (!$this->hasMembers()) {
419
			$this->getManager()->getMembers($this);
420
		}
421
422
		return $this->members;
423
	}
424
425
426
	/**
427
	 * @param array $members
428
	 * @param bool $detailed
429
	 *
430
	 * @return self
431
	 */
432
	public function setInheritedMembers(array $members, bool $detailed): IMemberships {
433
		$this->inheritedMembers = $members;
434
		$this->detailedInheritedMember = $detailed;
435
436
		return $this;
437
	}
438
439
	/**
440
	 * @param bool $detailed
441
	 *
442
	 * @return array
443
	 */
444
	public function getInheritedMembers(bool $detailed = false): array {
445
		if (is_null($this->inheritedMembers)
446
			|| ($detailed && !$this->detailedInheritedMember)) {
447
			$this->getManager()->getInheritedMembers($this, $detailed);
448
		}
449
450
		return $this->inheritedMembers;
451
	}
452
453
454
	/**
455
	 * @return bool
456
	 */
457
	public function hasMemberships(): bool {
458
		return !is_null($this->memberships);
459
	}
460
461
	/**
462
	 * @param array $memberships
463
	 *
464
	 * @return self
465
	 */
466
	public function setMemberships(array $memberships): IMemberships {
467
		$this->memberships = $memberships;
468
469
		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...
470
	}
471
472
	/**
473
	 * @return Membership[]
474
	 */
475
	public function getMemberships(): array {
476
		if (!$this->hasMemberships()) {
477
			$this->getManager()->getMemberships($this);
478
		}
479
480
		return $this->memberships;
481
	}
482
483
484
	/**
485
	 * @param Member|null $initiator
486
	 *
487
	 * @return Circle
488
	 */
489
	public function setInitiator(?Member $initiator): self {
490
		$this->initiator = $initiator;
491
492
		return $this;
493
	}
494
495
	/**
496
	 * @return Member
497
	 */
498
	public function getInitiator(): Member {
499
		return $this->initiator;
500
	}
501
502
	/**
503
	 * @return bool
504
	 */
505
	public function hasInitiator(): bool {
506
		return ($this->initiator !== null);
507
	}
508
509
	/**
510
	 * @param string $instance
511
	 *
512
	 * @return Circle
513
	 */
514
	public function setInstance(string $instance): self {
515
		if ($this->isConfig(self::CFG_NO_OWNER)) {
516
			$this->instance = $instance;
517
		}
518
519
		return $this;
520
	}
521
522
	/**
523
	 * @return string
524
	 * @throws OwnerNotFoundException
525
	 */
526
	public function getInstance(): string {
527
		if (!$this->hasOwner()) {
528
			throw new OwnerNotFoundException('circle has no owner');
529
		}
530
531
		return $this->getOwner()->getInstance();
532
	}
533
534
535
	/**
536
	 * @param array $settings
537
	 *
538
	 * @return self
539
	 */
540
	public function setSettings(array $settings): self {
541
		$this->settings = $settings;
542
543
		return $this;
544
	}
545
546
	/**
547
	 * @return array
548
	 */
549
	public function getSettings(): array {
550
		return $this->settings;
551
	}
552
553
554
	/**
555
	 * @param string $description
556
	 *
557
	 * @return self
558
	 */
559
	public function setDescription(string $description): self {
560
		$this->description = $description;
561
562
		return $this;
563
	}
564
565
	/**
566
	 * @return string
567
	 */
568
	public function getDescription(): string {
569
		return $this->description;
570
	}
571
572
573
	/**
574
	 * @return string
575
	 */
576
	public function getUrl(): string {
577
		return $this->getManager()->generateLinkToCircle($this->getSingleId());
578
	}
579
580
581
	/**
582
	 * @param int $contactAddressBook
583
	 *
584
	 * @return self
585
	 */
586
	public function setContactAddressBook(int $contactAddressBook): self {
587
		$this->contactAddressBook = $contactAddressBook;
588
589
		return $this;
590
	}
591
592
	/**
593
	 * @return int
594
	 */
595
	public function getContactAddressBook(): int {
596
		return $this->contactAddressBook;
597
	}
598
599
600
	/**
601
	 * @param string $contactGroupName
602
	 *
603
	 * @return self
604
	 */
605
	public function setContactGroupName(string $contactGroupName): self {
606
		$this->contactGroupName = $contactGroupName;
607
608
		return $this;
609
	}
610
611
	/**
612
	 * @return string
613
	 */
614
	public function getContactGroupName(): string {
615
		return $this->contactGroupName;
616
	}
617
618
619
	/**
620
	 * @param int $creation
621
	 *
622
	 * @return self
623
	 */
624
	public function setCreation(int $creation): self {
625
		$this->creation = $creation;
626
627
		return $this;
628
	}
629
630
	/**
631
	 * @return int
632
	 */
633
	public function getCreation(): int {
634
		return $this->creation;
635
	}
636
637
638
	/**
639
	 * @param array $data
640
	 *
641
	 * @return $this
642
	 * @throws InvalidItemException
643
	 */
644
	public function import(array $data): IDeserializable {
645
		if ($this->get('id', $data) === '') {
646
			throw new InvalidItemException();
647
		}
648
649
		$this->setSingleId($this->get('id', $data))
650
			 ->setName($this->get('name', $data))
651
			 ->setDisplayName($this->get('displayName', $data))
652
			 ->setSource($this->getInt('source', $data))
653
			 ->setConfig($this->getInt('config', $data))
654
			 ->setSettings($this->getArray('settings', $data))
655
//			 ->setContactAddressBook($this->get('contact_addressbook', $data))
656
//			 ->setContactGroupName($this->get('contact_groupname', $data))
657
			 ->setDescription($this->get('description', $data))
658
			 ->setCreation($this->getInt('creation', $data));
659
660
		try {
661
			/** @var Member $owner */
662
			$owner = $this->deserialize($this->getArray('owner', $data), Member::class);
663
			$this->setOwner($owner);
664
		} catch (InvalidItemException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
665
		}
666
667
		try {
668
			/** @var Member $initiator */
669
			$initiator = $this->deserialize($this->getArray('initiator', $data), Member::class);
670
			$this->setInitiator($initiator);
671
		} catch (InvalidItemException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
672
		}
673
674
		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...
675
	}
676
677
678
	/**
679
	 * @return array
680
	 */
681
	public function jsonSerialize(): array {
682
		$arr = [
683
			'id'          => $this->getSingleId(),
684
			'name'        => $this->getName(),
685
			'displayName' => $this->getDisplayName(),
686
			'source'      => $this->getSource(),
687
			'config'      => $this->getConfig(),
688
			'description' => $this->getDescription(),
689
			'settings'    => $this->getSettings(),
690
			'url'        => $this->getUrl(),
691
			'creation'    => $this->getCreation(),
692
			'initiator'   => ($this->hasInitiator()) ? $this->getInitiator() : null
693
		];
694
695
		if ($this->hasOwner()) {
696
			$arr['owner'] = $this->getOwner();
697
		}
698
699
		if ($this->hasMembers()) {
700
			$arr['members'] = $this->getMembers();
701
		}
702
703
		if (!is_null($this->inheritedMembers)) {
704
			$arr['inheritedMembers'] = $this->getInheritedMembers();
705
		}
706
707
		if ($this->hasMemberships()) {
708
			$arr['memberships'] = $this->getMemberships();
709
		}
710
711
		return $arr;
712
	}
713
714
715
	/**
716
	 * @param array $data
717
	 * @param string $prefix
718
	 *
719
	 * @return INC22QueryRow
720
	 * @throws CircleNotFoundException
721
	 */
722
	public function importFromDatabase(array $data, string $prefix = ''): INC22QueryRow {
723
		if ($this->get($prefix . 'unique_id', $data) === '') {
724
			throw new CircleNotFoundException();
725
		}
726
727
		$this->setSingleId($this->get($prefix . 'unique_id', $data))
728
			 ->setName($this->get($prefix . 'name', $data))
729
			 ->setDisplayName($this->get($prefix . 'display_name', $data))
730
			 ->setConfig($this->getInt($prefix . 'config', $data))
731
			 ->setSource($this->getInt($prefix . 'source', $data))
732
			 ->setInstance($this->get($prefix . 'instance', $data))
733
			 ->setSettings($this->getArray($prefix . 'settings', $data))
734
			 ->setContactAddressBook($this->getInt($prefix . 'contact_addressbook', $data))
735
			 ->setContactGroupName($this->get($prefix . 'contact_groupname', $data))
736
			 ->setDescription($this->get($prefix . 'description', $data));
737
738
		$creation = $this->get($prefix . 'creation', $data);
739
		$this->setCreation(DateTime::createFromFormat('Y-m-d H:i:s', $creation)->getTimestamp());
740
741
		$this->getManager()->manageImportFromDatabase($this, $data, $prefix);
742
743
		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...
744
	}
745
746
747
	/**
748
	 * @param Circle $circle
749
	 *
750
	 * @return bool
751
	 * @throws OwnerNotFoundException
752
	 */
753
	public function compareWith(Circle $circle): bool {
754
		if ($this->getSingleId() !== $circle->getSingleId()
755
			|| $this->getInstance() !== $circle->getInstance()
756
			|| $this->getConfig() !== $circle->getConfig()) {
757
			return false;
758
		}
759
760
		if ($this->hasOwner()
761
			&& (!$circle->hasOwner()
762
				|| !$this->getOwner()->compareWith($circle->getOwner()))) {
763
			return false;
764
		}
765
766
		if ($this->hasInitiator()
767
			&& (!$circle->hasInitiator()
768
				|| !$this->getInitiator()->compareWith($circle->getInitiator()))) {
769
			return false;
770
		}
771
772
		return true;
773
	}
774
775
776
	/**
777
	 * @param Circle $circle
778
	 * @param int $display
779
	 *
780
	 * @return array
781
	 */
782
	public static function getCircleFlags(Circle $circle, int $display = self::FLAGS_LONG): array {
783
		$config = [];
784
		foreach (array_keys(Circle::$DEF_CFG) as $def) {
785
			if ($circle->isConfig($def)) {
786
				list($short, $long) = explode('|', Circle::$DEF_CFG[$def]);
787
				switch ($display) {
788
789
					case self::FLAGS_SHORT:
790
						$config[] = $short;
791
						break;
792
793
					case self::FLAGS_LONG:
794
						$config[] = $long;
795
						break;
796
				}
797
			}
798
		}
799
800
		return $config;
801
	}
802
803
}
804
805