Completed
Pull Request — master (#586)
by Maxence
02:42
created

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