Completed
Push — master ( 2d80b2...8a71ed )
by Joschi
03:21
created

ObjectProxy::getCanonicalUrl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 4
ccs 0
cts 4
cp 0
crap 2
rs 10
1
<?php
2
3
/**
4
 * apparat-object
5
 *
6
 * @category    Apparat
7
 * @package     Apparat\Object
8
 * @subpackage  Apparat\Object\Domain
9
 * @author      Joschi Kuphal <[email protected]> / @jkphl
10
 * @copyright   Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
11
 * @license     http://opensource.org/licenses/MIT The MIT License (MIT)
12
 */
13
14
/***********************************************************************************
15
 *  The MIT License (MIT)
16
 *
17
 *  Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
18
 *
19
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
20
 *  this software and associated documentation files (the "Software"), to deal in
21
 *  the Software without restriction, including without limitation the rights to
22
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
23
 *  the Software, and to permit persons to whom the Software is furnished to do so,
24
 *  subject to the following conditions:
25
 *
26
 *  The above copyright notice and this permission notice shall be included in all
27
 *  copies or substantial portions of the Software.
28
 *
29
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
31
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
32
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
33
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
 ***********************************************************************************/
36
37
namespace Apparat\Object\Domain\Model\Object;
38
39
use Apparat\Kernel\Ports\Kernel;
40
use Apparat\Object\Domain\Model\Uri\ApparatUrl;
41
use Apparat\Object\Domain\Model\Uri\RepositoryLocatorInterface;
42
use Apparat\Object\Domain\Model\Relation\RelationInterface;
43
use Apparat\Object\Domain\Repository\Service;
44
45
/**
46
 * Object proxy (lazy loading)
47
 *
48
 * @package Apparat\Object
49
 * @subpackage Apparat\Object\Domain
50
 */
51
abstract class ObjectProxy implements ObjectInterface
52
{
53
    /**
54
     * Apparat object URL
55
     *
56
     * @var ApparatUrl
57
     */
58
    protected $url = null;
59
    /**
60
     * Object
61
     *
62
     * @var ObjectInterface
63
     */
64
    protected $object = null;
65
66
    /*******************************************************************************
67
     * PUBLIC METHODS
68
     *******************************************************************************/
69
70
    /**
71
     * Object proxy constructor
72
     *
73
     * @param ApparatUrl $url Apparat object URL
74
     */
75
    public function __construct(ApparatUrl $url)
76
    {
77
        $this->url = $url;
78
    }
79
80
    /**
81
     * Return the object repository locator
82
     *
83
     * @return RepositoryLocatorInterface Object repository locator
84
     */
85
    public function getRepositoryLocator()
86
    {
87
        // If the object has already been instantiated
88
        if ($this->object instanceof ObjectInterface) {
89
            return $this->object->getRepositoryLocator();
90
        }
91
92
        return $this->url->getLocator();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->url->getLocator(); (Apparat\Object\Domain\Model\Uri\Locator) is incompatible with the return type declared by the interface Apparat\Object\Domain\Mo...e::getRepositoryLocator of type Apparat\Object\Domain\Mo...ositoryLocatorInterface.

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...
93
    }
94
95
    /**
96
     * Return the object property data
97
     *
98
     * @param bool $serialize Serialize property objects
99
     * @return array Object property data
100
     */
101
    public function getPropertyData($serialize = true)
102
    {
103
        return $this->object()->getPropertyData($serialize);
104
    }
105
106
    /**
107
     * Return the enclosed remote object
108
     *
109
     * @return ObjectInterface Remote object
110
     */
111
    protected function object()
112
    {
113
        // Lazy-load the remote object if necessary
114
        if (!$this->object instanceof ObjectInterface) {
115
            // Instantiate the local object repository, load and return the object
116
            $this->object = Kernel::create(Service::class)->get($this->url)->loadObject($this->url->getLocator());
117
        }
118
119
        return $this->object;
120
    }
121
122
    /**
123
     * Return the object payload
124
     *
125
     * @return string Object payload
126
     */
127
    public function getPayload()
128
    {
129
        return $this->object()->getPayload();
130
    }
131
132
    /**
133
     * Set the payload
134
     *
135
     * @param string $payload Payload
136
     * @return ObjectInterface Self reference
137
     */
138
    public function setPayload($payload)
139
    {
140
        return $this->object()->setPayload($payload);
141
    }
142
143
    /**
144
     * Return the object ID
145
     *
146
     * @return Id Object ID
147
     */
148
    public function getId()
149
    {
150
        return $this->object()->getId();
151
    }
152
153
    /**
154
     * Return the object type
155
     *
156
     * @return Type Object type
157
     */
158
    public function getType()
159
    {
160
        return $this->object()->getType();
161
    }
162
163
    /**
164
     * Return the object revision
165
     *
166
     * @return Revision Object revision
167
     */
168
    public function getRevision()
169
    {
170
        return $this->object()->getRevision();
171
    }
172
173
    /**
174
     * Return the latitude
175
     *
176
     * @return float Latitude
177
     */
178
    public function getLatitude()
179
    {
180
        return $this->object()->getLatitude();
181
    }
182
183
    /**
184
     * Set the latitude
185
     *
186
     * @param float $latitude Latitude
187
     * @return ObjectInterface Self reference
188
     */
189
    public function setLatitude($latitude)
190
    {
191
        return $this->object()->setLatitude($latitude);
192
    }
193
194
    /**
195
     * Return the longitude
196
     *
197
     * @return float Longitude
198
     */
199
    public function getLongitude()
200
    {
201
        return $this->object()->getLongitude();
202
    }
203
204
    /**
205
     * Set the longitude
206
     *
207
     * @param float $longitude Longitude
208
     * @return ObjectInterface Self reference
209
     */
210
    public function setLongitude($longitude)
211
    {
212
        return $this->object()->setLongitude($longitude);
213
    }
214
215
    /**
216
     * Return the elevation
217
     *
218
     * @return float Elevation
219
     */
220
    public function getElevation()
221
    {
222
        return $this->object()->getElevation();
223
    }
224
225
    /**
226
     * Set the elevation
227
     *
228
     * @param float $elevation
229
     * @return ObjectInterface Self reference
230
     */
231
    public function setElevation($elevation)
232
    {
233
        return $this->object()->setElevation($elevation);
234
    }
235
236
237
    /**
238
     * Return the object draft mode
239
     *
240
     * @return boolean Object draft mode
241
     */
242
    public function isDraft()
243
    {
244
        return $this->object()->isDraft();
245
    }
246
247
    /**
248
     * Return whether the object is in modified state
249
     *
250
     * @return boolean Modified state
251
     */
252
    public function hasBeenModified()
253
    {
254
        return $this->object()->hasBeenModified();
255
    }
256
257
    /**
258
     * Return whether the object is in mutated state
259
     *
260
     * @return boolean Mutated state
261
     */
262
    public function hasBeenMutated()
263
    {
264
        return $this->object()->hasBeenMutated();
265
    }
266
267
    /**
268
     * Return the creation date & time
269
     *
270
     * @return \DateTimeInterface Creation date & time
271
     */
272
    public function getCreated()
273
    {
274
        return $this->object()->getCreated();
275
    }
276
277
    /**
278
     * Return the deletion date & time
279
     *
280
     * @return \DateTimeInterface Deletion date & time
281
     */
282
    public function getDeleted()
283
    {
284
        return $this->object()->getDeleted();
285
    }
286
287
    /**
288
     * Return the modification date & time
289
     *
290
     * @return \DateTimeInterface Modification date & time
291
     */
292
    public function getModified()
293
    {
294
        return $this->object()->getModified();
295
    }
296
297
    /**
298
     * Return the publication date & time
299
     *
300
     * @return \DateTimeInterface Publication date & time
301
     */
302
    public function getPublished()
303
    {
304
        return $this->object()->getPublished();
305
    }
306
307
    /**
308
     * Return the object title
309
     *
310
     * @return string Object title
311
     */
312
    public function getTitle()
313
    {
314
        return $this->object()->getTitle();
315
    }
316
317
    /**
318
     * Set the title
319
     *
320
     * @param string $title Title
321
     * @return ObjectInterface Self reference
322
     */
323
    public function setTitle($title)
324
    {
325
        return $this->object()->setTitle($title);
326
    }
327
328
    /**
329
     * Return the object slug
330
     *
331
     * @return string Object slug
332
     */
333
    public function getSlug()
334
    {
335
        return $this->object()->getSlug();
336
    }
337
338
    /**
339
     * Set the slug
340
     *
341
     * @param string $slug Slug
342
     * @return ObjectInterface Self reference
343
     */
344
    public function setSlug($slug)
345
    {
346
        return $this->object()->setSlug($slug);
347
    }
348
349
350
    /**
351
     * Return the object description
352
     *
353
     * @return string Object description
354
     */
355
    public function getDescription()
356
    {
357
        return $this->object()->getDescription();
358
    }
359
360
    /**
361
     * Set the description
362
     *
363
     * @param string $description Description
364
     * @return ObjectInterface Self reference
365
     */
366
    public function setDescription($description)
367
    {
368
        return $this->object()->setDescription($description);
369
    }
370
371
    /**
372
     * Return the object abstract
373
     *
374
     * @return string Object abstract
375
     */
376
    public function getAbstract()
377
    {
378
        return $this->object()->getAbstract();
379
    }
380
381
    /**
382
     * Set the abstract
383
     *
384
     * @param string $abstract Abstract
385
     * @return ObjectInterface Self reference
386
     */
387
    public function setAbstract($abstract)
388
    {
389
        return $this->object()->setAbstract($abstract);
390
    }
391
392
    /**
393
     * Return all object keywords
394
     *
395
     * @return array Object keywords
396
     */
397
    public function getKeywords()
398
    {
399
        return $this->object()->getKeywords();
400
    }
401
402
    /**
403
     * Set the keywords
404
     *
405
     * @param array $keywords Keywords
406
     * @return ObjectInterface Self reference
407
     */
408
    public function setKeywords(array $keywords)
409
    {
410
        return $this->object()->setKeywords($keywords);
411
    }
412
413
    /**
414
     * Return the license
415
     *
416
     * @return string License
417
     */
418
    public function getLicense()
419
    {
420
        return $this->object()->getLicense();
421
    }
422
423
    /**
424
     * Set the license
425
     *
426
     * @param string $license License
427
     * @return ObjectInterface Self reference
428
     */
429
    public function setLicense($license)
430
    {
431
        return $this->object()->setLicense($license);
432
    }
433
434
    /**
435
     * Return the language
436
     *
437
     * @return string Language
438
     */
439
    public function getLanguage()
440
    {
441
        return $this->object()->getLanguage();
442
    }
443
444
    /**
445
     * Return the privacy
446
     *
447
     * @return string Privacy
448
     */
449
    public function getPrivacy()
450
    {
451
        return $this->object()->getPrivacy();
452
    }
453
454
    /**
455
     * Set the privacy
456
     *
457
     * @param string $privacy Privacy
458
     * @return ObjectInterface Self reference
459
     */
460
    public function setPrivacy($privacy)
461
    {
462
        return $this->object()->setPrivacy($privacy);
463
    }
464
465
    /**
466
     * Return all object categories
467
     *
468
     * @return array Object categories
469
     */
470
    public function getCategories()
471
    {
472
        return $this->object()->getCategories();
473
    }
474
475
    /**
476
     * Set the categories
477
     *
478
     * @param array $categories Categories
479
     * @return ObjectInterface Self reference
480
     */
481
    public function setCategories(array $categories)
482
    {
483
        return $this->object()->setCategories($categories);
484
    }
485
486
    /**
487
     * Get a domain property value
488
     *
489
     * Multi-level properties might be traversed by property name locators separated with colons (":").
490
     *
491
     * @param string $property Property name
492
     * @return mixed Property value
493
     */
494
    public function getDomain($property)
495
    {
496
        return $this->object()->getDomain($property);
497
    }
498
499
    /**
500
     * Set a domain property value
501
     *
502
     * @param string $property Property name
503
     * @param mixed $value Property value
504
     * @return ObjectInterface Self reference
505
     */
506
    public function setDomain($property, $value)
507
    {
508
        return $this->object()->setDomain($property, $value);
509
    }
510
511
    /**
512
     * Get a processing instruction
513
     *
514
     * @param string $procInst Processing instruction name
515
     * @return mixed Processing instruction
516
     */
517
    public function getProcessingInstruction($procInst)
518
    {
519
        return $this->object()->getProcessingInstruction($procInst);
520
    }
521
522
    /**
523
     * Set a processing instruction
524
     *
525
     * @param string $procInst Processing instruction name
526
     * @param mixed $value Processing instruction
527
     * @return ObjectInterface Self reference
528
     */
529
    public function setProcessingInstruction($procInst, $value)
530
    {
531
        return $this->object()->setProcessingInstruction($procInst, $value);
532
    }
533
534
    /**
535
     * Return the absolute object URL
536
     *
537
     * @return string
538
     */
539
    public function getAbsoluteUrl()
540
    {
541
        return strval($this->url);
542
    }
543
544
    /**
545
     * Return the canonical object URL
546
     *
547
     * @return string
548
     */
549
    public function getCanonicalUrl()
550
    {
551
        return $this->getAbsoluteUrl();
552
    }
553
554
    /**
555
     * Generic caller
556
     *
557
     * @param string $name Method name
558
     * @param array $arguments Method arguments
559
     */
560
    public function __call($name, $arguments)
561
    {
562
        $object = $this->object();
563
        if (is_callable(array($object, $name))) {
564
            return $object->$name(...$arguments);
565
        }
566
567
        throw new InvalidArgumentException(
568
            sprintf('Invalid object proxy method "%s"', $name),
569
            InvalidArgumentException::INVALID_OBJECT_PROXY_METHOD
570
        );
571
    }
572
573
    /**
574
     * Use a specific object revision
575
     *
576
     * @param Revision $revision Revision to be used
577
     * @return ObjectInterface Object
578
     */
579
    public function useRevision(Revision $revision)
580
    {
581
        return $this->object()->useRevision($revision);
582
    }
583
584
    /**
585
     * Persist the current object revision
586
     *
587
     * @return ObjectInterface Object
588
     */
589
    public function persist()
590
    {
591
        return $this->object()->persist();
592
    }
593
594
    /**
595
     * Return whether the object is in published state
596
     *
597
     * @return boolean Published state
598
     */
599
    public function isPublished()
600
    {
601
        return $this->object()->isPublished();
602
    }
603
604
    /**
605
     * Return whether the object has just been published
606
     *
607
     * @return boolean Object has just been published
608
     */
609
    public function hasBeenPublished()
610
    {
611
        return $this->object()->hasBeenPublished();
612
    }
613
614
    /**
615
     * Return whether the object has been deleted
616
     *
617
     * @return boolean Object is deleted
618
     */
619
    public function isDeleted()
620
    {
621
        return $this->object()->isDeleted();
622
    }
623
624
    /**
625
     * Return whether the object has just been deleted
626
     *
627
     * @return boolean Object has just been deleted
628
     */
629
    public function hasBeenDeleted()
630
    {
631
        return $this->object()->hasBeenDeleted();
632
    }
633
634
    /**
635
     * Return whether the object has just been undeleted
636
     *
637
     * @return boolean Object has just been undeleted
638
     */
639
    public function hasBeenUndeleted()
640
    {
641
        return $this->object()->hasBeenUndeleted();
642
    }
643
644
645
    /**
646
     * Publish the current object revision
647
     *
648
     * @return ObjectInterface Object
649
     */
650
    public function publish()
651
    {
652
        return $this->object()->publish();
653
    }
654
655
    /**
656
     * Delete the object and all its revisions
657
     *
658
     * @return ObjectInterface Object
659
     */
660
    public function delete()
661
    {
662
        return $this->object()->delete();
663
    }
664
665
    /**
666
     * Undelete the object and all its revisions
667
     *
668
     * @return ObjectInterface Object
669
     */
670
    public function undelete()
671
    {
672
        return $this->object()->undelete();
673
    }
674
675
    /**
676
     * Add an object relation
677
     *
678
     * @param string|RelationInterface $relation Serialized or instantiated object relation
679
     * @param string|null $relationType Relation type
680
     * @return ObjectInterface
681
     */
682
    public function addRelation($relation, $relationType = null)
683
    {
684
        return $this->object()->addRelation($relation, $relationType);
685
    }
686
687
    /**
688
     * Delete an object relation
689
     *
690
     * @param RelationInterface $relation Object relation
691
     * @return ObjectInterface
692
     */
693
    public function deleteRelation(RelationInterface $relation)
694
    {
695
        return $this->object()->deleteRelation($relation);
696
    }
697
698
    /**
699
     * Get all relations (optional: Of a particular type)
700
     *
701
     * @param string|null $relationType Optional: Relation type
702
     * @return array Object relations
703
     */
704
    public function getRelations($relationType = null)
705
    {
706
        return $this->object()->getRelations($relationType);
707
    }
708
709
    /**
710
     * Find and return particular relations
711
     *
712
     * @param array $criteria Relation criteria
713
     * @return RelationInterface[] Relations
714
     */
715
    public function findRelations(array $criteria)
716
    {
717
        return $this->object()->findRelations($criteria);
718
    }
719
}
720