Completed
Push — master ( bca2e7...1f8246 )
by Maxence
02:35 queued 11s
created

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