Completed
Push — master ( 99686d...4efdc3 )
by Joschi
02:54
created

ObjectUrl::getRevision()   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 2
Bugs 0 Features 0
Metric Value
c 2
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\Domain
8
 * @author      Joschi Kuphal <[email protected]> / @jkphl
9
 * @copyright   Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
10
 * @license     http://opensource.org/licenses/MIT The MIT License (MIT)
11
 */
12
13
/***********************************************************************************
14
 *  The MIT License (MIT)
15
 *
16
 *  Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
17
 *
18
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
19
 *  this software and associated documentation files (the "Software"), to deal in
20
 *  the Software without restriction, including without limitation the rights to
21
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
22
 *  the Software, and to permit persons to whom the Software is furnished to do so,
23
 *  subject to the following conditions:
24
 *
25
 *  The above copyright notice and this permission notice shall be included in all
26
 *  copies or substantial portions of the Software.
27
 *
28
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
30
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
31
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
32
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
33
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34
 ***********************************************************************************/
35
36
namespace Apparat\Object\Domain\Model\Path;
37
38
use Apparat\Object\Domain\Model\Object\Id;
39
use Apparat\Object\Domain\Model\Object\Revision;
40
use Apparat\Object\Domain\Model\Object\Type;
41
42
/**
43
 * Object URL
44
 *
45
 * @package Apparat\Object\Domain\Model
46
 */
47
class ObjectUrl extends Url implements PathInterface
48
{
49
    /**
50
     * Object path
51
     *
52
     * @var LocalPath
53
     */
54
    protected $localPath = null;
55
56
    /*******************************************************************************
57
     * PUBLIC METHODS
58
     *******************************************************************************/
59
60
    /**
61
     * Object URL constructor
62
     *
63
     * @param string $url Object URL
64
     * @param boolean $remote Accept remote URL (less strict date component checking)
65
     * @throws InvalidArgumentException If remote URLs are not allowed and a remote URL is given
66
     */
67 31
    public function __construct($url, $remote = false)
68
    {
69 31
        parent::__construct($url);
70
71
        // If it's an invalid remote object URL
72 30
        if ($this->isAbsolute() && !$remote) {
73 1
            throw new InvalidArgumentException(
74 1
                sprintf('Unallowed remote object URL "%s"', $url),
75
                InvalidArgumentException::UNALLOWED_REMOTE_OBJECT_URL
76 1
            );
77
        }
78
79
        // Instantiate the local path component
80 29
        $this->localPath = new LocalPath(
81 29
            empty($this->urlParts['path']) ? '' : $this->urlParts['path'],
82 29
            $remote ? true : null, $this->urlParts['path']
0 ignored issues
show
Bug introduced by
It seems like $remote ? true : null can also be of type boolean; however, Apparat\Object\Domain\Mo...ocalPath::__construct() does only seem to accept null|object<Apparat\Obje...odel\Path\TRUE>|integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
83 29
        );
84
85
        // Normalize the path prefix
86 26
        if (!strlen($this->urlParts['path'])) {
87 12
            $this->urlParts['path'] = null;
88 12
        }
89 26
    }
90
91
    /**
92
     * Return the object's creation date
93
     *
94
     * @return \DateTimeImmutable Object creation date
95
     */
96 6
    public function getCreationDate()
97
    {
98 6
        return $this->localPath->getCreationDate();
99
    }
100
101
    /**
102
     * Set the object's creation date
103
     *
104
     * @param \DateTimeImmutable $creationDate
105
     * @return LocalPath New object path
106
     */
107 1
    public function setCreationDate(\DateTimeImmutable $creationDate)
108
    {
109 1
        $this->localPath = $this->localPath->setCreationDate($creationDate);
110 1
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Apparat\Object\Domain\Model\Path\ObjectUrl) is incompatible with the return type declared by the interface Apparat\Object\Domain\Mo...erface::setCreationDate of type Apparat\Object\Domain\Model\Path\LocalPath.

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...
111
    }
112
113
    /**
114
     * Return the object type
115
     *
116
     * @return Type Object type
117
     */
118 6
    public function getType()
119
    {
120 6
        return $this->localPath->getType();
121
    }
122
123
    /**
124
     * Set the object type
125
     *
126
     * @param Type $type Object type
127
     * @return ObjectUrl New object URL
128
     */
129 1
    public function setType(Type $type)
130
    {
131 1
        $this->localPath = $this->localPath->setType($type);
132 1
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Apparat\Object\Domain\Model\Path\ObjectUrl) is incompatible with the return type declared by the interface Apparat\Object\Domain\Mo...\PathInterface::setType of type Apparat\Object\Domain\Model\Path\LocalPath.

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...
133
    }
134
135
    /**
136
     * Return the object ID
137
     *
138
     * @return Id Object ID
139
     */
140 9
    public function getId()
141
    {
142 9
        return $this->localPath->getId();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->localPath->getId(); (integer) is incompatible with the return type declared by the interface Apparat\Object\Domain\Mo...th\PathInterface::getId of type Apparat\Object\Domain\Model\Object\Id.

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...
143
    }
144
145
    /**
146
     * Set the object ID
147
     *
148
     * @param Id $uid Object ID
149
     * @return ObjectUrl New object URL
150
     */
151 1
    public function setId(Id $uid)
152
    {
153 1
        $this->localPath = $this->localPath->setId($uid);
154 1
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Apparat\Object\Domain\Model\Path\ObjectUrl) is incompatible with the return type declared by the interface Apparat\Object\Domain\Mo...th\PathInterface::setId of type Apparat\Object\Domain\Model\Path\LocalPath.

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...
155
    }
156
157
158
    /**
159
     * Return the object revision
160
     *
161
     * @return Revision Object revision
162
     */
163 6
    public function getRevision()
164
    {
165 6
        return $this->localPath->getRevision();
166
    }
167
168
    /**
169
     * Set the object revision
170
     *
171
     * @param Revision $revision Object revision
172
     * @return ObjectUrl New object URL
173
     */
174 1
    public function setRevision(Revision $revision)
175
    {
176 1
        $this->localPath = $this->localPath->setRevision($revision);
177 1
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Apparat\Object\Domain\Model\Path\ObjectUrl) is incompatible with the return type declared by the interface Apparat\Object\Domain\Mo...hInterface::setRevision of type Apparat\Object\Domain\Model\Path\LocalPath.

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...
178
    }
179
180
    /**
181
     * Test if this URL matches all available parts of a given URL
182
     *
183
     * @param Url $url Comparison URL
184
     * @return bool This URL matches all available parts of the given URL
185
     */
186 7
    public function matches(Url $url)
187
    {
188
        // If the standard URL components don't match
189 7
        if (!parent::matches($url)) {
190 5
            return false;
191
        }
192
193
        // Extended tests if it's an object URL
194 4
        if ($url instanceof self) {
195
            // Test the object creation date
196 1
            if ($this->getCreationDate() != $url->getCreationDate()) {
197 1
                return false;
198
            }
199
200
            // Test the object ID
201 1
            if ($this->getId()->serialize() !== $url->getId()->serialize()) {
0 ignored issues
show
Bug introduced by
The method serialize cannot be called on $this->getId() (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
Bug introduced by
The method serialize cannot be called on $url->getId() (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
202 1
                return false;
203
            }
204
205
            // Test the object type
206 1
            if ($this->getType()->serialize() !== $url->getType()->serialize()) {
207 1
                return false;
208
            }
209
210
            // Test the object revision
211 1
            if ($this->getRevision()->serialize() !== $url->getRevision()->serialize()) {
212 1
                return false;
213
            }
214 1
        }
215
216 4
        return true;
217
    }
218
219
    /**
220
     * Return the local object path
221
     *
222
     * @return LocalPath Local object path
223
     */
224 1
    public function getLocalPath()
225
    {
226 1
        return $this->localPath;
227
    }
228
229
    /**
230
     * Return the repository URL part of this object URL
231
     *
232
     * @return string Repository URL
233
     * @see https://github.com/apparat/apparat/blob/master/doc/URL-DESIGN.md#repository-url
234
     */
235 10
    public function getRepositoryUrl()
236
    {
237
        // If the object URL is absolute and local: Extract the repository URL
238 10
        if ($this->isAbsoluteLocal()) {
239 1
            $repositoryUrl = substr($this->getPath(), strlen((new Url(getenv('APPARAT_BASE_URL')))->getPath()));
240
241
            // Else: If it's a relative URL: Extract the repository URL
242 10
        } elseif (!$this->isAbsolute()) {
243 6
            $repositoryUrl = $this->getPath();
244
245
            // Else: It must be a remote repository
246 6
        } else {
247
            $override = [
248 3
                'object' => '',
249 3
                'query' => '',
250 3
                'fragment' => '',
251 3
            ];
252 3
            $repositoryUrl = $this->getUrlInternal($override);
253
        }
254
255 10
        return $repositoryUrl;
256
    }
257
258
    /*******************************************************************************
259
     * PRIVATE METHODS
260
     *******************************************************************************/
261
262
    /**
263
     * Return the a complete serialized object URL
264
     *
265
     * @param array $override Override components
266
     * @return string Serialized URL
267
     */
268 8
    protected function getUrlInternal(array &$override = [])
269
    {
270 8
        parent::getUrlInternal($override);
271
272
        // Prepare the local object path
273 8
        if (isset($override['object'])) {
274 4
            $object = $override['object'];
275 4
        } else {
276 5
            $object = strval($this->localPath);
277
        }
278 8
        $override['object'] = $object;
279
280 8
        return "{$override['scheme']}{$override['user']}{$override['pass']}{$override['host']}{$override['port']}{$override['path']}{$override['object']}{$override['query']}{$override['fragment']}";
281
    }
282
}
283