Completed
Push — master ( 2d5356...e3df84 )
by Joschi
03:05
created

LocalPath::isDraft()   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 1
Metric Value
c 1
b 0
f 1
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\Path;
38
39
use Apparat\Kernel\Ports\Kernel;
40
use Apparat\Object\Domain\Model\Object\Id;
41
use Apparat\Object\Domain\Model\Object\Revision;
42
use Apparat\Object\Domain\Model\Object\Type;
43
44
/**
45
 * Object path
46
 *
47
 * @package Apparat\Object
48
 * @subpackage Apparat\Object\Domain
49
 */
50
class LocalPath implements PathInterface
51
{
52
    /**
53
     * Date PCRE pattern
54
     *
55
     * @var array
56
     */
57
    protected static $datePattern = [
58
        'Y' => '(?P<year>\d{4})',
59
        'm' => '(?P<month>\d{2})',
60
        'd' => '(?P<day>\d{2})',
61
        'H' => '(?P<hour>\d{2})',
62
        'i' => '(?P<minute>\d{2})',
63
        's' => '(?P<second>\d{2})',
64
    ];
65
    /**
66
     * Creation date
67
     *
68
     * @var \DateTimeImmutable
69
     */
70
    protected $creationDate = null;
71
    /**
72
     * Object ID
73
     *
74
     * @var Id
75
     */
76
    protected $uid = null;
77
    /**
78
     * Object type
79
     *
80
     * @var Type
81
     */
82
    protected $type = null;
83
    /**
84
     * Object revision
85
     *
86
     * @var Revision
87
     */
88
    protected $revision = null;
89
90
    /*******************************************************************************
91
     * PUBLIC METHODS
92
     *******************************************************************************/
93
94
    /**
95
     * Object URL constructor
96
     *
97
     * @param null|string $path Object path
98
     * @param null|boolean|int $datePrecision Date precision [NULL = local default, TRUE = any precision (remote object
99
     *     URLs)]
100
     * @param string $leader Leading base path
101
     * @throws InvalidArgumentException If the date precision is invalid
102
     * @throws InvalidArgumentException If the object URL path is invalid
103
     */
104 52
    public function __construct($path = null, $datePrecision = null, &$leader = '')
105
    {
106 52
        if (!empty($path)) {
107
            // If the local default date precision should be used
108 51
            if ($datePrecision === null) {
109 26
                $datePrecision = intval(getenv('OBJECT_DATE_PRECISION'));
110 26
            }
111
112 51
            $pathPattern = null;
113
114
            // If a valid integer date precision is given
115 51
            if (is_int($datePrecision) && ($datePrecision >= 0) && ($datePrecision < 7)) {
116
                $pathPattern = '%^(?P<leader>(/[^/]+)*)?/'.
117 26
                    implode(
118 26
                        '/',
119 26
                        array_slice(self::$datePattern, 0, $datePrecision)
120 26
                    ).($datePrecision ? '/' : '');
121
122
                // Else if the date precision may be arbitrary
123 51
            } elseif ($datePrecision === true) {
124 33
                $pathPattern = '%(?:/'.implode('(?:/', self::$datePattern);
125 33
                $pathPattern .= str_repeat(')?', count(self::$datePattern));
126 33
                $pathPattern .= '/';
127 33
            }
128
129
            // If the date precision is invalid
130 51
            if ($pathPattern === null) {
131 1
                throw new InvalidArgumentException(
132 1
                    sprintf(
133 1
                        'Invalid date precision "%s" (%s)',
134 1
                        strval($datePrecision),
135 1
                        gettype($datePrecision)
136 1
                    ),
137
                    InvalidArgumentException::INVALID_DATE_PRECISION
138 1
                );
139
            }
140
141 50
            $pathPattern .= '(?P<id>\d+)\.(?P<type>[a-z]+)(?:/(.*\.)?\\k';
142 50
            $pathPattern .= '<id>(?:(?P<draft>\+)|(?:-(?P<revision>\d+)))?(?P<extension>\.[a-z0-9]+)?)?$%';
143
144 50
            if (!preg_match($pathPattern, $path, $pathParts)) {
145 23
                throw new InvalidArgumentException(
146 23
                    sprintf('Invalid object URL path "%s"', $path),
147
                    InvalidArgumentException::INVALID_OBJECT_URL_PATH
148 23
                );
149
            }
150
151
            // If date components are used
152 50
            if ($datePrecision) {
153 50
                $year = $pathParts['year'];
154 50
                $month = isset($pathParts['month']) ? $pathParts['month'] ?: '01' : '01';
155 50
                $day = isset($pathParts['day']) ? $pathParts['day'] ?: '01' : '01';
156 50
                $hour = isset($pathParts['hour']) ? $pathParts['hour'] ?: '00' : '00';
157 50
                $minute = isset($pathParts['minute']) ? $pathParts['minute'] ?: '00' : '00';
158 50
                $second = isset($pathParts['second']) ? $pathParts['second'] ?: '00' : '00';
159 50
                $this->creationDate = new \DateTimeImmutable("$year-$month-$day".'T'."$hour:$minute:$second+00:00");
160 50
            }
161
162
            // Determine the leader
163 50
            $leader = ($datePrecision === true) ? substr(
164 32
                $path,
165 32
                0,
166 32
                strlen($path) - strlen($pathParts[0])
167 50
            ) : $pathParts['leader'];
168
169
            // Set the ID
170
171 50
            $this->uid = Kernel::create(Id::class, [intval($pathParts['id'])]);
172
173
            // Set the type
174 50
            $this->type = Kernel::create(Type::class, [$pathParts['type']]);
175
176
            // Set the revision
177 50
            $this->revision = Kernel::create(
178 50
                Revision::class,
179 50
                [empty($pathParts['revision']) ? Revision::CURRENT : intval($pathParts['revision']), !empty($pathParts['draft'])]
180 50
            );
181 50
        }
182 51
    }
183
184
    /**
185
     * Create and return the object URL path
186
     *
187
     * @return string Object path
188
     */
189 27
    public function __toString()
190
    {
191 27
        $path = [];
192 27
        $datePrecision = intval(getenv('OBJECT_DATE_PRECISION'));
193
194
        // Add the creation date
195 27
        foreach (array_slice(array_keys(self::$datePattern), 0, $datePrecision) as $dateFormat) {
196 27
            $path[] = $this->creationDate->format($dateFormat);
197 27
        }
198
199
        // Add the object ID and type
200 27
        $path[] = $this->uid->getId().'.'.$this->type->getType();
201
202
        // Add the ID, draft mode and revision
203 27
        $uid = $this->uid->getId();
204 27
        $path[] = $this->revision->isDraft() ? $uid.'+' : rtrim($uid.'-'.$this->revision->getRevision(), '-');
205
206 27
        return '/'.implode('/', $path);
207
    }
208
209
    /**
210
     * Return the object's creation date
211
     *
212
     * @return \DateTimeImmutable Object creation date
213
     */
214 28
    public function getCreationDate()
215
    {
216 28
        return $this->creationDate;
217
    }
218
219
    /**
220
     * Set the object's creation date
221
     *
222
     * @param \DateTimeImmutable $creationDate
223
     * @return PathInterface|LocalPath New object path
224
     */
225 2
    public function setCreationDate(\DateTimeImmutable $creationDate)
226
    {
227 2
        $path = clone $this;
228 2
        $path->creationDate = $creationDate;
229 2
        return $path;
230
    }
231
232
    /**
233
     * Return the object type
234
     *
235
     * @return Type Object type
236
     */
237 30
    public function getType()
238
    {
239 30
        return $this->type;
240
    }
241
242
    /**
243
     * Set the object type
244
     *
245
     * @param Type $type Object type
246
     * @return PathInterface|LocalPath New object path
247
     */
248 2
    public function setType(Type $type)
249
    {
250 2
        $path = clone $this;
251 2
        $path->type = $type;
252 2
        return $path;
253
    }
254
255
    /**
256
     * Return the object ID
257
     *
258
     * @return Id Object ID
259
     */
260 31
    public function getId()
261
    {
262 31
        return $this->uid;
263
    }
264
265
    /**
266
     * Set the object ID
267
     *
268
     * @param Id $uid Object ID
269
     * @return PathInterface|LocalPath New object path
270
     */
271 2
    public function setId(Id $uid)
272
    {
273 2
        $path = clone $this;
274 2
        $path->uid = $uid;
275 2
        return $path;
276
    }
277
278
    /**
279
     * Return the object revision
280
     *
281
     * @return Revision Object revision
282
     */
283 31
    public function getRevision()
284
    {
285 31
        return $this->revision;
286
    }
287
288
    /**
289
     * Set the object revision
290
     *
291
     * @param Revision $revision Object revision
292
     * @return PathInterface|LocalPath New object path
293
     */
294 28
    public function setRevision(Revision $revision)
295
    {
296 28
        $path = clone $this;
297 28
        $path->revision = $revision;
298 28
        return $path;
299
    }
300
}
301