Completed
Push — master ( 5aa43e...28467c )
by Joschi
02:48
created

ObjectProxy::addAuthor()   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
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 2
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)
0 ignored issues
show
Coding Style introduced by
Spaces must be used for alignment; tabs are not allowed
Loading history...
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\ApparatUrl;
42
use Apparat\Object\Domain\Model\Path\PathInterface;
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
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 2
    public function __construct(ApparatUrl $url)
76
    {
77 2
        $this->_url = $url;
78 2
    }
79
80
    /**
81
     * Return the object repository path
82
     *
83
     * @return PathInterface Object repository path
84
     */
85
    public function getRepositoryPath()
86
    {
87
        // If the object has already been instatiated
88
        if ($this->_object instanceof ObjectInterface) {
89
            return $this->_object->getRepositoryPath();
90
91
            // Else
92
        } else {
93
            return $this->_url->getLocalPath();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->_url->getLocalPath(); (Apparat\Object\Domain\Model\Path\LocalPath) is incompatible with the return type declared by the interface Apparat\Object\Domain\Mo...face::getRepositoryPath of type Apparat\Object\Domain\Mo...RepositoryPathInterface.

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...
94
        }
95
    }
96
97
    /**
98
     * Return the object ID
99
     *
100
     * @return Id Object ID
101
     */
102
    public function getId()
103
    {
104
        return $this->_object()->getId();
105
    }
106
107
    /**
108
     * Return the enclosed remote object
109
     *
110
     * @return ObjectInterface Remote object
111
     */
112
    protected function _object()
113
    {
114
        // Lazy-load the remote object if necessary
115
        if (!$this->_object instanceof ObjectInterface) {
116
117
            // Instantiate the local object repository, load and return the object
118
            $this->_object = Kernel::create(Service::class)->get($this->_url)->loadObject($this->_url->getLocalPath());
119
        }
120
121
        return $this->_object;
122
    }
123
124
    /**
125
     * Return the object type
126
     *
127
     * @return Type Object type
128
     */
129
    public function getType()
130
    {
131
        return $this->_object()->getType();
132
    }
133
134
    /**
135
     * Return the object revision
136
     *
137
     * @return Revision Object revision
138
     */
139
    public function getRevision()
140
    {
141
        return $this->_object()->getRevision();
142
    }
143
144
    /**
145
     * Return the creation date & time
146
     *
147
     * @return \DateTimeImmutable Creation date & time
148
     */
149
    public function getCreated()
150
    {
151
        return $this->_object()->getCreated();
152
    }
153
154
    /**
155
     * Return the publication date & time
156
     *
157
     * @return \DateTimeImmutable Publication date & time
158
     */
159
    public function getPublished()
160
    {
161
        return $this->_object()->getPublished();
162
    }
163
164
    /**
165
     * Return the object description
166
     *
167
     * @return string Object description
168
     */
169
    public function getDescription()
170
    {
171
        return $this->_object()->getDescription();
172
    }
173
174
    /**
175
     * Return the object abstract
176
     *
177
     * @return string Object abstract
178
     */
179
    public function getAbstract()
180
    {
181
        return $this->_object()->getAbstract();
182
    }
183
184
    /**
185
     * Return all object keywords
186
     *
187
     * @return array Object keywords
188
     */
189
    public function getKeywords()
190
    {
191
        return $this->_object()->getKeywords();
192
    }
193
194
    /**
195
     * Return all object categories
196
     *
197
     * @return array Object categories
198
     */
199
    public function getCategories()
200
    {
201
        return $this->_object()->getCategories();
202
    }
203
204
    /**
205
     * Return all object authors
206
     *
207
     * @return AuthorInterface[] Authors
208
     */
209
    public function getAuthors()
210
    {
211
        return $this->_object()->getAuthors();
212
    }
213
214
    /**
215
     * Add an object author
216
     *
217
     * @param AuthorInterface $author Author
218
     * @return ObjectInterface Self reference
219
     */
220
    public function addAuthor(AuthorInterface $author)
221
    {
222
        return $this->_object()->addAuthor($author);
223
    }
224
225
    /*******************************************************************************
226
     * MAGIG METHODS
227
     *******************************************************************************/
228
229
    /**
230
     * Return the absolute object URL
231
     *
232
     * @return string
233
     */
234 2
    public function getAbsoluteUrl()
235
    {
236 2
        return strval($this->_url);
237
    }
238
239
    /*******************************************************************************
240
     * PRIVATE METHODS
241
     *******************************************************************************/
242
243
    /**
244
     * Generic caller
245
     *
246
     * @param string $name Method name
247
     * @param array $arguments Method arguments
248
     */
249
    public function __call($name, $arguments)
250
    {
251
        $object = $this->_object();
252
        if (is_callable(array($object, $name))) {
253
            return $object->$name(...$arguments);
254
        }
255
256
        throw new InvalidArgumentException(
257
            sprintf('Invalid object proxy method "%s"', $name),
258
            InvalidArgumentException::INVALID_OBJECT_PROXY_METHOD
259
        );
260
    }
261
}
262