Completed
Push — master ( 81c425...a2bf82 )
by Joschi
02:41
created

AbstractObject::getHash()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
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\RepositoryPathInterface;
42
use Apparat\Object\Domain\Model\Properties\AbstractDomainProperties;
43
use Apparat\Object\Domain\Model\Properties\InvalidArgumentException as PropertyInvalidArgumentException;
44
use Apparat\Object\Domain\Model\Properties\MetaProperties;
45
use Apparat\Object\Domain\Model\Properties\ProcessingInstructions;
46
use Apparat\Object\Domain\Model\Properties\Relations;
47
use Apparat\Object\Domain\Model\Properties\SystemProperties;
48
use Apparat\Object\Domain\Repository\Service;
49
50
/**
51
 * Abstract object
52
 *
53
 * @package Apparat\Object
54
 * @subpackage Apparat\Object\Domain
55
 */
56
abstract class AbstractObject implements ObjectInterface
57
{
58
    /**
59
     * System properties
60
     *
61
     * @var SystemProperties
62
     */
63
    protected $systemProperties;
64
    /**
65
     * Meta properties
66
     *
67
     * @var MetaProperties
68
     */
69
    protected $metaProperties;
70
    /**
71
     * Domain properties
72
     *
73
     * @var AbstractDomainProperties
74
     */
75
    protected $domainProperties;
76
    /**
77
     * Object payload
78
     *
79
     * @var string
80
     */
81
    protected $payload;
82
    /**
83
     * Repository path
84
     *
85
     * @var RepositoryPathInterface
86
     */
87
    protected $path;
88
    /**
89
     * Domain property collection class
90
     *
91
     * @var string
92
     */
93
    protected $domainPropertyCClass = AbstractDomainProperties::class;
94
    /**
95
     * Object relations
96
     *
97
     * @var Relations
98
     */
99
    protected $relations;
100
    /**
101
     * Processing instructions
102
     *
103
     * @var ProcessingInstructions
104
     */
105
    protected $processingInstructions;
106
    /**
107
     * Latest revision index
108
     *
109
     * @var Revision
110
     */
111
    protected $latestRevision;
112
113
    /**
114
     * Object constructor
115
     *
116
     * @param string $payload Object payload
117
     * @param array $propertyData Property data
118
     * @param RepositoryPathInterface $path Object repository path
119
     */
120 18
    public function __construct($payload = '', array $propertyData = [], RepositoryPathInterface $path = null)
121
    {
122
        // If the domain property collection class is invalid
123 18
        if (!$this->domainPropertyCClass
124 18
            || !class_exists($this->domainPropertyCClass)
125 18
            || !(new \ReflectionClass($this->domainPropertyCClass))->isSubclassOf(AbstractDomainProperties::class)
126 18
        ) {
127 1
            throw new PropertyInvalidArgumentException(
128 1
                sprintf(
129 1
                    'Invalid domain property collection class "%s"',
130 1
                    $this->domainPropertyCClass
131 1
                ),
132
                PropertyInvalidArgumentException::INVALID_DOMAIN_PROPERTY_COLLECTION_CLASS
133 1
            );
134
        }
135
136 17
        $this->path = $path;
137
138
        // Load the current revision data
139 17
        $this->loadRevisionData($payload, $propertyData);
140
141
        // Save the latest revision index
142 15
        $this->latestRevision = $this->getRevision();
143 15
        $this->path = $path->setRevision($this->latestRevision);
0 ignored issues
show
Bug introduced by
It seems like $path is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
144 15
    }
145
146
    /**
147
     * Load object revision data
148
     *
149
     * @param string $payload Object payload
150
     * @param array $propertyData Property data
151
     */
152 17
    protected function loadRevisionData($payload = '', array $propertyData = [])
153
    {
154 17
        $this->payload = $payload;
155
156
        // Instantiate the system properties
157 17
        $systemPropertyData = (empty($propertyData[SystemProperties::COLLECTION]) ||
158 17
            !is_array(
159 17
                $propertyData[SystemProperties::COLLECTION]
160 17
            )) ? [] : $propertyData[SystemProperties::COLLECTION];
161 17
        $this->systemProperties = Kernel::create(SystemProperties::class, [$systemPropertyData, $this]);
162
163
        // Instantiate the meta properties
164 16
        $metaPropertyData = (empty($propertyData[MetaProperties::COLLECTION]) ||
165 15
            !is_array(
166 15
                $propertyData[MetaProperties::COLLECTION]
167 16
            )) ? [] : $propertyData[MetaProperties::COLLECTION];
168 16
        $this->metaProperties = Kernel::create(MetaProperties::class, [$metaPropertyData, $this]);
169
170
        // Instantiate the domain properties
171 15
        $domainPropertyData = (empty($propertyData[AbstractDomainProperties::COLLECTION]) ||
172 14
            !is_array(
173 14
                $propertyData[AbstractDomainProperties::COLLECTION]
174 15
            )) ? [] : $propertyData[AbstractDomainProperties::COLLECTION];
175 15
        $this->domainProperties = Kernel::create($this->domainPropertyCClass, [$domainPropertyData, $this]);
176
177
        // Instantiate the processing instructions
178 15
        $procInstData = (empty($propertyData[ProcessingInstructions::COLLECTION]) ||
179 11
            !is_array(
180 11
                $propertyData[ProcessingInstructions::COLLECTION]
181 15
            )) ? [] : $propertyData[ProcessingInstructions::COLLECTION];
182 15
        $this->processingInstructions = Kernel::create(ProcessingInstructions::class, [$procInstData, $this]);
183
184
        // Instantiate the object relations
185 15
        $relationData = (empty($propertyData[Relations::COLLECTION]) ||
186 11
            !is_array(
187 11
                $propertyData[Relations::COLLECTION]
188 15
            )) ? [] : $propertyData[Relations::COLLECTION];
189 15
        $this->relations = Kernel::create(Relations::class, [$relationData, $this]);
190 15
    }
191
192
    /**
193
     * Return the object revision
194
     *
195
     * @return Revision Object revision
196
     */
197 15
    public function getRevision()
198
    {
199 15
        return $this->systemProperties->getRevision();
200
    }
201
202
    /**
203
     * Use a specific object revision
204
     *
205
     * @param Revision $revision Revision to be used
206
     * @return ObjectInterface Object
207
     * @throws OutOfBoundsException If the requested revision is invalid
208
     */
209
    public function useRevision(Revision $revision)
210
    {
211
        $isCurrentRevision = false;
212
213
            // If the requested revision is invalid
214
        if (!$revision->isCurrent() && (($revision->getRevision() < 1) || ($revision->getRevision() > $this->latestRevision->getRevision()))) {
215
            throw new OutOfBoundsException(sprintf('Invalid object revision "%s"', $revision->getRevision()),
216
                OutOfBoundsException::INVALID_OBJECT_REVISION);
217
        }
218
219
        // If the requested revision is not already used
220
        if ($revision->getRevision() !== $this->path->getRevision()->getRevision()) {
221
            /** @var ManagerInterface $objectManager */
222
            $objectManager = Kernel::create(Service::class)->getObjectManager();
223
224
            // Load the requested object revision resource
225
            $newRevisionPath = $this->path->setRevision($isCurrentRevision ? Kernel::create(Revision::class, [Revision::CURRENT]) : $revision);
226
            $revisionResource = $objectManager->loadObject($newRevisionPath);
0 ignored issues
show
Compatibility introduced by
$newRevisionPath of type object<Apparat\Object\Do...del\Path\PathInterface> is not a sub-type of object<Apparat\Object\Do...epositoryPathInterface>. It seems like you assume a child interface of the interface Apparat\Object\Domain\Model\Path\PathInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
227
228
            // Load the revision resource data
229
            $this->loadRevisionData($revisionResource->getPayload(), $revisionResource->getPropertyData());
230
231
            // Set the current revision path
232
            $this->path = $newRevisionPath;
233
        }
234
235
        return $this;
236
    }
237
238
    /**
239
     * Return the object ID
240
     *
241
     * @return Id Object ID
242
     */
243 5
    public function getId()
244
    {
245 5
        return $this->systemProperties->getId();
246
    }
247
248
    /**
249
     * Return the object type
250
     *
251
     * @return Type Object type
252
     */
253 1
    public function getType()
254
    {
255 1
        return $this->systemProperties->getType();
256
    }
257
258
    /**
259
     * Return the creation date & time
260
     *
261
     * @return \DateTimeImmutable Creation date & time
262
     */
263 1
    public function getCreated()
264
    {
265 1
        return $this->systemProperties->getCreated();
266
    }
267
268
    /**
269
     * Return the publication date & time
270
     *
271
     * @return \DateTimeImmutable Publication date & time
272
     */
273 1
    public function getPublished()
274
    {
275 1
        return $this->systemProperties->getPublished();
276
    }
277
278
    /**
279
     * Return the object hash
280
     *
281
     * @return string Object hash
282
     */
283 1
    public function getHash()
284
    {
285 1
        return $this->systemProperties->getHash();
286
    }
287
288
    /**
289
     * Return the object description
290
     *
291
     * @return string Object description
292
     */
293 1
    public function getDescription()
294
    {
295 1
        return $this->metaProperties->getDescription();
296
    }
297
298
    /**
299
     * Return the object abstract
300
     *
301
     * @return string Object abstract
302
     */
303 1
    public function getAbstract()
304
    {
305 1
        return $this->metaProperties->getAbstract();
306
    }
307
308
    /**
309
     * Return all object keywords
310
     *
311
     * @return array Object keywords
312
     */
313 1
    public function getKeywords()
314
    {
315 1
        return $this->metaProperties->getKeywords();
316
    }
317
318
    /**
319
     * Return all object categories
320
     *
321
     * @return array Object categories
322
     */
323 1
    public function getCategories()
324
    {
325 1
        return $this->metaProperties->getCategories();
326
    }
327
328
    /**
329
     * Return all object authors
330
     *
331
     * @return AuthorInterface[] Authors
332
     */
333 2
    public function getAuthors()
334
    {
335 2
        return $this->metaProperties->getAuthors();
336
    }
337
338
    /**
339
     * Add an object author
340
     *
341
     * @param AuthorInterface $author Author
342
     * @return ObjectInterface Self reference
343
     */
344 1
    public function addAuthor(AuthorInterface $author)
345
    {
346 1
        $authors = $this->metaProperties->getAuthors();
347 1
        $authors[] = $author;
348 1
        $this->metaProperties->setAuthors($authors);
349 1
        return $this;
350
    }
351
352
    /**
353
     * Return the object repository path
354
     *
355
     * @return RepositoryPathInterface Object repository path
356
     */
357 15
    public function getRepositoryPath()
358
    {
359 15
        return $this->path;
360
    }
361
362
    /**
363
     * Return the object property data
364
     *
365
     * @return array Object property data
366
     */
367 2
    public function getPropertyData()
368
    {
369 2
        $propertyData = array_filter([
370 2
            SystemProperties::COLLECTION => $this->systemProperties->toArray(),
371 2
            MetaProperties::COLLECTION => $this->metaProperties->toArray(),
372 2
            AbstractDomainProperties::COLLECTION => $this->domainProperties->toArray(),
373 2
            ProcessingInstructions::COLLECTION => $this->processingInstructions->toArray(),
374 2
            Relations::COLLECTION => $this->relations->toArray(),
375 2
        ], function (array $collection) {
376 2
            return (boolean)count($collection);
377 2
        });
378
379 2
        return $propertyData;
380
    }
381
382
    /**
383
     * Return the object payload
384
     *
385
     * @return string Object payload
386
     */
387 2
    public function getPayload()
388
    {
389 2
        return $this->payload;
390
    }
391
392
    /**
393
     * Return the absolute object URL
394
     *
395
     * @return string
396
     */
397 1
    public function getAbsoluteUrl()
398
    {
399 1
        return getenv('APPARAT_BASE_URL').ltrim($this->path->getRepository()->getUrl(), '/').strval($this->path);
400
    }
401
402
    /**
403
     * Get a particular property value
404
     *
405
     * Multi-level properties might be traversed by property name paths separated with colons (":").
406
     *
407
     * @param string $property Property name
408
     * @return mixed Property value
409
     */
410 2
    public function getDomainProperty($property)
411
    {
412 2
        return $this->domainProperties->getProperty($property);
413
    }
414
}
415