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

Circle::importFromDatabase()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 9.552
c 0
b 0
f 0
cc 2
nc 2
nop 2
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 Circle[] */
210
	private $memberOf = null;
211
212
	/** @var Member[] */
213
	private $members = null;
214
215
	/** @var Member[] */
216
	private $inheritedMembers = null;
217
218
	/** @var bool */
219
	private $detailedInheritedMember = false;
220
221
	/** @var Membership[] */
222
	private $memberships = null;
223
224
225
	/**
226
	 * Circle constructor.
227
	 */
228
	public function __construct() {
229
	}
230
231
	/**
232
	 * @param string $singleId
233
	 *
234
	 * @return self
235
	 */
236
	public function setSingleId(string $singleId): self {
237
		$this->singleId = $singleId;
238
239
		return $this;
240
	}
241
242
	/**
243
	 * @return string
244
	 */
245
	public function getSingleId(): string {
246
		return $this->singleId;
247
	}
248
249
250
	/**
251
	 * @param int $config
252
	 *
253
	 * @return self
254
	 */
255
	public function setConfig(int $config): self {
256
		$this->config = $config;
257
258
		return $this;
259
	}
260
261
	/**
262
	 * @return int
263
	 */
264
	public function getConfig(): int {
265
		return $this->config;
266
	}
267
268
	/**
269
	 * @param int $flag
270
	 * @param int $test
271
	 *
272
	 * @return bool
273
	 */
274
	public function isConfig(int $flag, int $test = 0): bool {
275
		if ($test === 0) {
276
			$test = $this->getConfig();
277
		}
278
279
		return (($test & $flag) !== 0);
280
	}
281
282
	/**
283
	 * @param int $flag
284
	 */
285
	public function addConfig(int $flag): void {
286
		if (!$this->isConfig($flag)) {
287
			$this->config += $flag;
288
		}
289
	}
290
291
	/**
292
	 * @param int $flag
293
	 */
294
	public function remConfig(int $flag): void {
295
		if ($this->isConfig($flag)) {
296
			$this->config -= $flag;
297
		}
298
	}
299
300
301
	/**
302
	 * @param string $name
303
	 *
304
	 * @return self
305
	 */
306
	public function setName(string $name): self {
307
		$this->name = $name;
308
		if ($this->displayName === '') {
309
			$this->displayName = $name;
310
		}
311
312
		return $this;
313
	}
314
315
	/**
316
	 * @return string
317
	 */
318
	public function getName(): string {
319
		return $this->name;
320
	}
321
322
323
	/**
324
	 * @param string $displayName
325
	 *
326
	 * @return self
327
	 */
328
	public function setDisplayName(string $displayName): self {
329
		if ($displayName !== '') {
330
			$this->displayName = $displayName;
331
		}
332
333
		return $this;
334
	}
335
336
	/**
337
	 * @return string
338
	 */
339
	public function getDisplayName(): string {
340
		return $this->displayName;
341
	}
342
343
344
	/**
345
	 * @param int $source
346
	 *
347
	 * @return Circle
348
	 */
349
	public function setSource(int $source): self {
350
		$this->source = $source;
351
352
		return $this;
353
	}
354
355
	/**
356
	 * @return int
357
	 */
358
	public function getSource(): int {
359
		return $this->source;
360
	}
361
362
363
	/**
364
	 * @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...
365
	 *
366
	 * @return self
367
	 */
368
	public function setOwner(?Member $owner): self {
369
		$this->owner = $owner;
370
371
		return $this;
372
	}
373
374
	/**
375
	 * @return Member
376
	 */
377
	public function getOwner(): Member {
378
		return $this->owner;
379
	}
380
381
	/**
382
	 * @return bool
383
	 */
384
	public function hasOwner(): bool {
385
		return ($this->owner !== null);
386
	}
387
388
389
	/**
390
	 * @param array $members
391
	 *
392
	 * @return self
393
	 */
394
	public function setMembers(array $members): IMemberships {
395
		$this->members = $members;
396
397
		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::setMembers of type self.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
398
	}
399
400
	/**
401
	 * @return array
402
	 */
403
	public function getMembers(): array {
404
		if (is_null($this->members)) {
405
			$this->getManager()->getMembers($this);
406
		}
407
408
		return $this->members;
409
	}
410
411
412
	/**
413
	 * @param array $members
414
	 * @param bool $detailed
415
	 *
416
	 * @return self
417
	 */
418
	public function setInheritedMembers(array $members, bool $detailed): IMemberships {
419
		$this->inheritedMembers = $members;
420
		$this->detailedInheritedMember = $detailed;
421
422
		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::setInheritedMembers of type self.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

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