Completed
Push — master ( e5d943...acf07d )
by Joschi
02:33
created

AbstractObject::setDomainProperties()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 16
ccs 10
cts 10
cp 1
rs 9.2
cc 4
eloc 8
nc 2
nop 2
crap 4
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\Author\AuthorInterface;
41
use Apparat\Object\Domain\Model\Path\RepositoryPath;
42
use Apparat\Object\Domain\Model\Path\RepositoryPathInterface;
43
use Apparat\Object\Domain\Model\Properties\AbstractDomainProperties;
44
use Apparat\Object\Domain\Model\Properties\GenericPropertiesInterface;
45
use Apparat\Object\Domain\Model\Properties\InvalidArgumentException as PropertyInvalidArgumentException;
46
use Apparat\Object\Domain\Model\Properties\MetaProperties;
47
use Apparat\Object\Domain\Model\Properties\ProcessingInstructions;
48
use Apparat\Object\Domain\Model\Properties\Relations;
49
use Apparat\Object\Domain\Model\Properties\SystemProperties;
50
use Apparat\Object\Domain\Repository\Service;
51
52
/**
53
 * Abstract object
54
 *
55
 * @package Apparat\Object
56
 * @subpackage Apparat\Object\Domain
57
 */
58
abstract class AbstractObject implements ObjectInterface
59
{
60
    /**
61
     * Clean state
62
     *
63
     * @var int
64
     */
65
    const STATE_CLEAN = 0;
66
    /**
67
     * Dirty state
68
     *
69
     * @var int
70
     */
71
    const STATE_DIRTY = 1;
72
    /**
73
     * Mutated state
74
     *
75
     * @var int
76
     */
77
    const STATE_MUTATED = 2;
78
    /**
79
     * System properties
80
     *
81
     * @var SystemProperties
82
     */
83
    protected $systemProperties;
84
    /**
85
     * Meta properties
86
     *
87
     * @var MetaProperties
88
     */
89
    protected $metaProperties;
90
    /**
91
     * Domain properties
92
     *
93
     * @var AbstractDomainProperties
94
     */
95
    protected $domainProperties;
96
    /**
97
     * Object payload
98
     *
99
     * @var string
100
     */
101
    protected $payload;
102
    /**
103
     * Repository path
104
     *
105
     * @var RepositoryPathInterface
106
     */
107
    protected $path;
108
    /**
109
     * Domain property collection class
110
     *
111
     * @var string
112
     */
113
    protected $domainPropertyCClass = AbstractDomainProperties::class;
114
    /**
115
     * Object relations
116
     *
117
     * @var Relations
118
     */
119
    protected $relations;
120
    /**
121
     * Processing instructions
122
     *
123
     * @var ProcessingInstructions
124
     */
125
    protected $processingInstructions;
126
    /**
127
     * Latest revision index
128
     *
129
     * @var Revision
130
     */
131
    protected $latestRevision;
132
    /**
133
     * Object state
134
     *
135
     * @var int
136
     */
137
    protected $state = self::STATE_CLEAN;
138
    /**
139
     * Property collection states
140
     *
141
     * @var array
142
     */
143
    protected $collectionStates = [];
144
145
    /**
146
     * Object constructor
147
     *
148
     * @param string $payload Object payload
149
     * @param array $propertyData Property data
150
     * @param RepositoryPathInterface $path Object repository path
151
     */
152 21
    public function __construct($payload = '', array $propertyData = [], RepositoryPathInterface $path = null)
153
    {
154
        // If the domain property collection class is invalid
155 21
        if (!$this->domainPropertyCClass
156 21
            || !class_exists($this->domainPropertyCClass)
157 21
            || !(new \ReflectionClass($this->domainPropertyCClass))->isSubclassOf(AbstractDomainProperties::class)
158 21
        ) {
159 1
            throw new PropertyInvalidArgumentException(
160 1
                sprintf(
161 1
                    'Invalid domain property collection class "%s"',
162 1
                    $this->domainPropertyCClass
163 1
                ),
164
                PropertyInvalidArgumentException::INVALID_DOMAIN_PROPERTY_COLLECTION_CLASS
165 1
            );
166
        }
167
168 20
        $this->path = $path;
169
170
        // Load the current revision data
171 20
        $this->loadRevisionData($payload, $propertyData);
172
173
        // Save the latest revision index
174 18
        $this->latestRevision = $this->getRevision();
175 18
    }
176
177
    /**
178
     * Load object revision data
179
     *
180
     * @param string $payload Object payload
181
     * @param array $propertyData Property data
182
     */
183 20
    protected function loadRevisionData($payload = '', array $propertyData = [])
184
    {
185 20
        $this->payload = $payload;
186
187
        // Instantiate the system properties
188 20
        $systemPropertyData = (empty($propertyData[SystemProperties::COLLECTION]) ||
189 20
            !is_array(
190 20
                $propertyData[SystemProperties::COLLECTION]
191 20
            )) ? [] : $propertyData[SystemProperties::COLLECTION];
192 20
        $this->systemProperties = Kernel::create(SystemProperties::class, [$systemPropertyData, $this]);
193
194
        // Instantiate the meta properties
195 19
        $metaPropertyData = (empty($propertyData[MetaProperties::COLLECTION]) ||
196 18
            !is_array(
197 18
                $propertyData[MetaProperties::COLLECTION]
198 19
            )) ? [] : $propertyData[MetaProperties::COLLECTION];
199
        /** @var MetaProperties $metaPropertyCollection */
200 19
        $metaPropertyCollection = Kernel::create(MetaProperties::class, [$metaPropertyData, $this]);
201 18
        $this->setMetaProperties($metaPropertyCollection, true);
202
203
        // Instantiate the domain properties
204 18
        $domainPropertyData = (empty($propertyData[AbstractDomainProperties::COLLECTION]) ||
205 17
            !is_array(
206 17
                $propertyData[AbstractDomainProperties::COLLECTION]
207 18
            )) ? [] : $propertyData[AbstractDomainProperties::COLLECTION];
208
        /** @var AbstractDomainProperties $domainPropertyCollection */
209 18
        $domainPropertyCollection = Kernel::create($this->domainPropertyCClass, [$domainPropertyData, $this]);
210 18
        $this->setDomainProperties($domainPropertyCollection, true);
211
212
        // Instantiate the processing instructions
213 18
        $procInstData = (empty($propertyData[ProcessingInstructions::COLLECTION]) ||
214 14
            !is_array(
215 14
                $propertyData[ProcessingInstructions::COLLECTION]
216 18
            )) ? [] : $propertyData[ProcessingInstructions::COLLECTION];
217
        /** @var ProcessingInstructions $procInstCollection */
218 18
        $procInstCollection = Kernel::create(ProcessingInstructions::class, [$procInstData, $this]);
219 18
        $this->setProcessingInstructions($procInstCollection, true);
220
221
        // Instantiate the object relations
222 18
        $relationData = (empty($propertyData[Relations::COLLECTION]) ||
223 14
            !is_array(
224 14
                $propertyData[Relations::COLLECTION]
225 18
            )) ? [] : $propertyData[Relations::COLLECTION];
226
        /** @var Relations $relationCollection */
227 18
        $relationCollection = Kernel::create(Relations::class, [$relationData, $this]);
228 18
        $this->setRelations($relationCollection, true);
229
230
        // Reset the object state to clean
231 18
        $this->state = self::STATE_CLEAN;
232 18
    }
233
234
    /**
235
     * Set the meta properties collection
236
     *
237
     * @param MetaProperties $metaProperties Meta property collection
238
     * @param bool $overwrite Overwrite the existing collection (if present)
239
     */
240 18
    protected function setMetaProperties(MetaProperties $metaProperties, $overwrite = false)
241
    {
242 18
        $this->metaProperties = $metaProperties;
243 18
        $metaPropertiesState = spl_object_hash($this->metaProperties);
244
245
        // If the meta property collection state has changed
246
        if (!$overwrite
247 18
            && !empty($this->collectionStates[MetaProperties::COLLECTION])
248 18
            && ($metaPropertiesState !== $this->collectionStates[MetaProperties::COLLECTION])
249 18
        ) {
250
            // Flag this object as mutated
251 1
            $this->setMutatedState();
252 1
        }
253
254 18
        $this->collectionStates[MetaProperties::COLLECTION] = $metaPropertiesState;
255 18
    }
256
257
    /**
258
     * Set the object state to mutated
259
     */
260 2
    protected function setMutatedState()
261
    {
262
        // If this object is not in mutated state yet
263 2
        if (!($this->state & self::STATE_MUTATED) && !$this->isDraft()) {
264
            // TODO: Send signal
265
266
            // Increment the latest revision number
267 2
            $this->latestRevision = $this->latestRevision->increment();
268
269
            // Create draft system properties
270 2
            $this->systemProperties = $this->systemProperties->createDraft($this->latestRevision);
271
272
            // Set the draft flag on the repository path
273 2
            $this->path = $this->path->setDraft(true);
274
275
            // If this is not already a draft ...
276
            // Recreate the system properties
277
            // Copy the object ID
278
            // Copy the object type
279
            // Set the revision number to latest revision + 1
280
            // Set the creation date to now
281
            // Set no publication date
282
            // Set the draft flag on the repository path
283
            // Increase the latest revision by 1
284
285
            // Else if this is a draft
286
            // No action needed
287 2
        }
288
289
        // Enable the mutated (and dirty) state
290 2
        $this->state |= (self::STATE_DIRTY | self::STATE_MUTATED);
291 2
    }
292
293
    /**
294
     * Return the object draft mode
295
     *
296
     * @return boolean Object draft mode
297
     */
298 3
    public function isDraft()
299
    {
300 3
        return $this->systemProperties->isDraft();
301
    }
302
303
    /**
304
     * Set the domain properties collection
305
     *
306
     * @param GenericPropertiesInterface $domainProperties Domain property collection
307
     * @param bool $overwrite Overwrite the existing collection (if present)
308
     */
309 18
    protected function setDomainProperties(GenericPropertiesInterface $domainProperties, $overwrite = false)
310
    {
311 18
        $this->domainProperties = $domainProperties;
0 ignored issues
show
Documentation Bug introduced by
$domainProperties is of type object<Apparat\Object\Do...ricPropertiesInterface>, but the property $domainProperties was declared to be of type object<Apparat\Object\Do...stractDomainProperties>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
312 18
        $domainPropertiesState = spl_object_hash($this->domainProperties);
313
314
        // If the domain property collection state has changed
315
        if (!$overwrite
316 18
            && !empty($this->collectionStates[AbstractDomainProperties::COLLECTION])
317 18
            && ($domainPropertiesState !== $this->collectionStates[AbstractDomainProperties::COLLECTION])
318 18
        ) {
319
            // Flag this object as mutated
320 1
            $this->setMutatedState();
321 1
        }
322
323 18
        $this->collectionStates[AbstractDomainProperties::COLLECTION] = $domainPropertiesState;
324 18
    }
325
326
    /**
327
     * Set the processing instruction collection
328
     *
329
     * @param GenericPropertiesInterface $processingInstructions Processing instruction collection
330
     * @param bool $overwrite Overwrite the existing collection (if present)
331
     */
332 18
    protected function setProcessingInstructions(GenericPropertiesInterface $processingInstructions, $overwrite = false)
333
    {
334 18
        $this->processingInstructions = $processingInstructions;
0 ignored issues
show
Documentation Bug introduced by
$processingInstructions is of type object<Apparat\Object\Do...ricPropertiesInterface>, but the property $processingInstructions was declared to be of type object<Apparat\Object\Do...ProcessingInstructions>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
335 18
        $processingInstructionsState = spl_object_hash($this->processingInstructions);
336
337
        // If the domain property collection state has changed
338
        if (!$overwrite
339 18
            && !empty($this->collectionStates[ProcessingInstructions::COLLECTION])
340 18
            && ($processingInstructionsState !== $this->collectionStates[ProcessingInstructions::COLLECTION])
341 18
        ) {
342
            // Flag this object as dirty
343 1
            $this->setDirtyState();
344 1
        }
345
346 18
        $this->collectionStates[ProcessingInstructions::COLLECTION] = $processingInstructionsState;
347 18
    }
348
349
    /**
350
     * Set the object state to dirty
351
     */
352 1
    protected function setDirtyState()
353
    {
354
        // If this object is not in dirty state yet
355 1
        if (!($this->state & self::STATE_DIRTY)) {
356
            // TODO: Send signal
357 1
        }
358
359
        // Enable the dirty state
360 1
        $this->state |= self::STATE_DIRTY;
361 1
    }
362
363
    /**
364
     * Set the relations collection
365
     *
366
     * @param Relations $relations Relations collection
367
     * @param bool $overwrite Overwrite the existing collection (if present)
368
     */
369 18
    protected function setRelations(Relations $relations, $overwrite = false)
370
    {
371 18
        $this->relations = $relations;
372 18
        $relationsState = spl_object_hash($this->relations);
373
374
        // If the domain property collection state has changed
375
        if (!$overwrite
376 18
            && !empty($this->collectionStates[Relations::COLLECTION])
377 18
            && ($relationsState !== $this->collectionStates[Relations::COLLECTION])
378 18
        ) {
379
            // Flag this object as dirty
380
            $this->setDirtyState();
381
        }
382
383 18
        $this->collectionStates[Relations::COLLECTION] = $relationsState;
384 18
    }
385
386
    /**
387
     * Return the object revision
388
     *
389
     * @return Revision Object revision
390
     */
391 18
    public function getRevision()
392
    {
393 18
        return $this->systemProperties->getRevision();
394
    }
395
396
    /**
397
     * Return whether the object is in mutated state
398
     *
399
     * @return boolean Mutated state
400
     */
401 3
    public function isMutated()
402
    {
403 3
        return !!($this->state & self::STATE_MUTATED);
404
    }
405
406
    /**
407
     * Use a specific object revision
408
     *
409
     * @param Revision $revision Revision to be used
410
     * @return ObjectInterface Object
411
     * @throws OutOfBoundsException If the requested revision is invalid
412
     */
413 17
    public function useRevision(Revision $revision)
414
    {
415 17
        $isCurrentRevision = false;
416
417
        // If the requested revision is invalid
418 17
        if (!$revision->isCurrent() &&
419
            (($revision->getRevision() < 1) || ($revision->getRevision() > $this->latestRevision->getRevision()))
420 17
        ) {
421
            throw new OutOfBoundsException(sprintf('Invalid object revision "%s"', $revision->getRevision()),
422
                OutOfBoundsException::INVALID_OBJECT_REVISION);
423
        }
424
425
        // If the current revision got requested
426 17
        if ($revision->isCurrent()) {
427 17
            $isCurrentRevision = true;
428 17
            $revision = $this->latestRevision;
429 17
        }
430
431
        // If the requested revision is not already used
432 17
        if ($revision != $this->getRevision()) {
433
            /** @var ManagerInterface $objectManager */
434
            $objectManager = Kernel::create(Service::class)->getObjectManager();
435
436
            // Load the requested object revision resource
437
            /** @var Revision $newRevision */
438
            $newRevision = $isCurrentRevision ? Kernel::create(Revision::class, [Revision::CURRENT]) :
439
                $revision;
440
            /** @var RepositoryPath $newRevisionPath */
441
            $newRevisionPath = $this->path->setRevision($newRevision);
442
            $revisionResource = $objectManager->loadObject($newRevisionPath);
443
444
            // Load the revision resource data
445
            $this->loadRevisionData($revisionResource->getPayload(), $revisionResource->getPropertyData());
446
447
            // Set the current revision path
448
            $this->path = $newRevisionPath;
449
        }
450
451 17
        return $this;
452
    }
453
454
    /**
455
     * Return the object ID
456
     *
457
     * @return Id Object ID
458
     */
459 5
    public function getId()
460
    {
461 5
        return $this->systemProperties->getId();
462
    }
463
464
    /**
465
     * Return the object type
466
     *
467
     * @return Type Object type
468
     */
469 1
    public function getType()
470
    {
471 1
        return $this->systemProperties->getType();
472
    }
473
474
    /**
475
     * Return the creation date & time
476
     *
477
     * @return \DateTimeImmutable Creation date & time
478
     */
479 1
    public function getCreated()
480
    {
481 1
        return $this->systemProperties->getCreated();
482
    }
483
484
    /**
485
     * Return the publication date & time
486
     *
487
     * @return \DateTimeImmutable Publication date & time
488
     */
489 1
    public function getPublished()
490
    {
491 1
        return $this->systemProperties->getPublished();
492
    }
493
494
    /**
495
     * Return the object hash
496
     *
497
     * @return string Object hash
498
     */
499 1
    public function getHash()
500
    {
501 1
        return $this->systemProperties->getHash();
502
    }
503
504
    /**
505
     * Return the object description
506
     *
507
     * @return string Object description
508
     */
509 2
    public function getDescription()
510
    {
511 2
        return $this->metaProperties->getDescription();
512
    }
513
514
    /**
515
     * Set the description
516
     *
517
     * @param string $description Description
518
     * @return ObjectInterface Self reference
519
     */
520 1
    public function setDescription($description)
521
    {
522 1
        $this->setMetaProperties($this->metaProperties->setDescription($description));
523 1
        return $this;
524
    }
525
526
    /**
527
     * Return the object abstract
528
     *
529
     * @return string Object abstract
530
     */
531 2
    public function getAbstract()
532
    {
533 2
        return $this->metaProperties->getAbstract();
534
    }
535
536
    /**
537
     * Set the abstract
538
     *
539
     * @param string $abstract Abstract
540
     * @return ObjectInterface Self reference
541
     */
542 1
    public function setAbstract($abstract)
543
    {
544 1
        $this->setMetaProperties($this->metaProperties->setAbstract($abstract));
545 1
        return $this;
546
    }
547
548
    /**
549
     * Return all object keywords
550
     *
551
     * @return array Object keywords
552
     */
553 2
    public function getKeywords()
554
    {
555 2
        return $this->metaProperties->getKeywords();
556
    }
557
558
    /**
559
     * Set the keywords
560
     *
561
     * @param array $keywords Keywords
562
     * @return ObjectInterface Self reference
563
     */
564 1
    public function setKeywords(array $keywords)
565
    {
566 1
        $this->setMetaProperties($this->metaProperties->setKeywords($keywords));
567 1
        return $this;
568
    }
569
570
    /**
571
     * Return all object categories
572
     *
573
     * @return array Object categories
574
     */
575 2
    public function getCategories()
576
    {
577 2
        return $this->metaProperties->getCategories();
578
    }
579
580
    /**
581
     * Set the categories
582
     *
583
     * @param array $categories Categories
584
     * @return ObjectInterface Self reference
585
     */
586 1
    public function setCategories(array $categories)
587
    {
588 1
        $this->setMetaProperties($this->metaProperties->setCategories($categories));
589 1
        return $this;
590
    }
591
592
    /**
593
     * Return all object authors
594
     *
595
     * @return AuthorInterface[] Authors
596
     */
597 2
    public function getAuthors()
598
    {
599 2
        return $this->metaProperties->getAuthors();
600
    }
601
602
    /**
603
     * Add an object author
604
     *
605
     * @param AuthorInterface $author Author
606
     * @return ObjectInterface Self reference
607
     */
608 1
    public function addAuthor(AuthorInterface $author)
609
    {
610 1
        $authors = $this->metaProperties->getAuthors();
611 1
        $authors[] = $author;
612 1
        $this->metaProperties->setAuthors($authors);
613 1
        return $this;
614
    }
615
616
    /**
617
     * Return the object repository path
618
     *
619
     * @return RepositoryPathInterface Object repository path
620
     */
621 18
    public function getRepositoryPath()
622
    {
623 18
        return $this->path;
624
    }
625
626
    /**
627
     * Return the object property data
628
     *
629
     * @return array Object property data
630
     */
631 5
    public function getPropertyData()
632
    {
633 5
        $propertyData = array_filter([
634 5
            SystemProperties::COLLECTION => $this->systemProperties->toArray(),
635 5
            MetaProperties::COLLECTION => $this->metaProperties->toArray(),
636 5
            AbstractDomainProperties::COLLECTION => $this->domainProperties->toArray(),
637 5
            ProcessingInstructions::COLLECTION => $this->processingInstructions->toArray(),
638 5
            Relations::COLLECTION => $this->relations->toArray(),
639 5
        ], function (array $collection) {
640 5
            return (boolean)count($collection);
641 5
        });
642
643 5
        return $propertyData;
644
    }
645
646
    /**
647
     * Return the object payload
648
     *
649
     * @return string Object payload
650
     */
651 2
    public function getPayload()
652
    {
653 2
        return $this->payload;
654
    }
655
656
    /**
657
     * Set the payload
658
     *
659
     * @param string $payload Payload
660
     * @return ObjectInterface Self reference
661
     */
662
    public function setPayload($payload)
663
    {
664
        // If the payload is changed
665
        if ($payload !== $this->payload) {
666
            $this->setMutatedState();
667
        }
668
669
        $this->payload = $payload;
670
        return $this;
671
    }
672
673
    /**
674
     * Return the absolute object URL
675
     *
676
     * @return string
677
     */
678 4
    public function getAbsoluteUrl()
679
    {
680 4
        return getenv('APPARAT_BASE_URL').ltrim($this->path->getRepository()->getUrl(), '/').strval($this->path);
681
    }
682
683
    /**
684
     * Get a domain property value
685
     *
686
     * Multi-level properties might be traversed by property name paths separated with colons (":").
687
     *
688
     * @param string $property Property name
689
     * @return mixed Property value
690
     */
691 2
    public function getDomainProperty($property)
692
    {
693 2
        return $this->domainProperties->getProperty($property);
694
    }
695
696
    /**
697
     * Set a domain property value
698
     *
699
     * @param string $property Property name
700
     * @param mixed $value Property value
701
     * @return ObjectInterface Self reference
702
     */
703 1
    public function setDomainProperty($property, $value)
704
    {
705 1
        $this->setDomainProperties($this->domainProperties->setProperty($property, $value));
706 1
        return $this;
707
    }
708
709
    /**
710
     * Get a processing instruction
711
     *
712
     * @param string $procInst Processing instruction name
713
     * @return mixed Processing instruction
714
     */
715
    public function getProcessingInstruction($procInst)
716
    {
717
        return $this->processingInstructions->getProperty($procInst);
718
    }
719
720
    /**
721
     * Set a processing instruction
722
     *
723
     * @param string $procInst Processing instruction name
724
     * @param mixed $value Processing instruction
725
     * @return ObjectInterface Self reference
726
     */
727 1
    public function setProcessingInstruction($procInst, $value)
728
    {
729 1
        $this->setProcessingInstructions($this->processingInstructions->setProperty($procInst, $value));
730 1
        return $this;
731
    }
732
733
    /**
734
     * Persist the current object revision
735
     *
736
     * @return ObjectInterface Object
737
     */
738
    public function persist()
739
    {
740
        // If this is not the latest revision
741
        if ($this->getRevision() != $this->latestRevision) {
742
            throw new RuntimeException(
743
                sprintf(
744
                    'Cannot persist revision %s/%s',
745
                    $this->getRevision()->getRevision(),
746
                    $this->latestRevision->getRevision()
747
                ),
748
                RuntimeException::CANNOT_PERSIST_EARLIER_REVISION
749
            );
750
        }
751
752
        // If this object is in dirty state
753
        if ($this->isDirty()) {
754
            $this->path->getRepository()->updateObject($this);
755
            $this->state = self::STATE_CLEAN;
756
        }
757
758
        return $this;
759
    }
760
761
    /**
762
     * Return whether the object is in dirty state
763
     *
764
     * @return boolean Dirty state
765
     */
766 3
    public function isDirty()
767
    {
768 3
        return !!($this->state & self::STATE_DIRTY);
769
    }
770
}
771