Completed
Push — master ( 792697...afdc23 )
by ignace nyamagana
04:24
created

AbstractHierarchicalComponent::__construct()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 1
ccs 0
cts 0
cp 0
nc 1
1
<?php
2
/**
3
 * League.Uri (http://uri.thephpleague.com)
4
 *
5
 * @package   League.uri
6
 * @author    Ignace Nyamagana Butera <[email protected]>
7
 * @copyright 2013-2015 Ignace Nyamagana Butera
8
 * @license   https://github.com/thephpleague/uri/blob/master/LICENSE (MIT License)
9
 * @version   4.2.0
10
 * @link      https://github.com/thephpleague/uri/
11
 */
12
namespace League\Uri\Components;
13
14
use InvalidArgumentException;
15
use League\Uri\Interfaces\HierarchicalComponent;
16
use League\Uri\Types\ImmutableCollectionTrait;
17
use League\Uri\Types\ImmutableComponentTrait;
18
19
/**
20
 * An abstract class to ease collection like Component object manipulation
21
 *
22
 * @package League.uri
23
 * @author  Ignace Nyamagana Butera <[email protected]>
24
 * @since   4.0.0
25
 */
26
abstract class AbstractHierarchicalComponent
27
{
28
    use ImmutableCollectionTrait;
29
30
    use ImmutableComponentTrait;
31
32
    const IS_ABSOLUTE = 1;
33
34
    const IS_RELATIVE = 0;
35
36
    /**
37
     * Hierarchical component separator
38
     *
39
     * @var string
40
     */
41
    protected static $separator;
42
43
    /**
44
     * Is the object considered absolute
45
     *
46
     * @var int
47
     */
48
    protected $isAbsolute = self::IS_RELATIVE;
49
50
    /**
51
     * new instance
52
     *
53
     * @param null|string $str the component value
54
     */
55
    abstract public function __construct($str);
56
57
    /**
58
     * Returns whether or not the component is absolute or not
59
     *
60
     * @return bool
61
     */
62 27
    public function isAbsolute()
63
    {
64 27
        return $this->isAbsolute === self::IS_ABSOLUTE;
65
    }
66
67
    /**
68
     * Returns an instance with the specified string
69
     *
70
     * This method MUST retain the state of the current instance, and return
71
     * an instance that contains the modified data
72
     *
73
     * @param string $value
74
     *
75
     * @return static
76
     */
77 561
    public function modify($value)
78
    {
79 561
        if ($value === $this->getContent()) {
80 177
            return $this;
81
        }
82
83 492
        return new static($value);
84
    }
85
86
    /**
87
     * Returns the component literal value
88
     *
89
     * @return string|null
90
     */
91
    abstract public function getContent();
92
93
    /**
94
     * Returns the instance string representation; If the
95
     * instance is not defined an empty string is returned
96
     *
97
     * @return string
98
     */
99 1069
    public function __toString()
100
    {
101 1069
        return (string) $this->getContent();
102
    }
103
104
    /**
105
     * Returns the instance string representation
106
     * with its optional URI delimiters
107
     *
108
     * @return string
109
     */
110 776
    public function getUriComponent()
111
    {
112 776
        return $this->__toString();
113
    }
114
115
    /**
116
     * Returns an instance with the modified segment
117
     *
118
     * This method MUST retain the state of the current instance, and return
119
     * an instance that contains the modified component with the replaced data
120
     *
121
     * @param int                          $offset    the label offset to remove and replace by
122
     *                                                the given component
123
     * @param HierarchicalComponent|string $component the component added
124
     *
125
     * @return static
126
     */
127 72
    public function replace($offset, $component)
128
    {
129 72
        if (!empty($this->data) && !$this->hasKey($offset)) {
130 15
            return $this;
131
        }
132
133 57
        $source = $this->toArray();
134 57
        $dest   = $this->validateComponent($component)->toArray();
135 57
        if ('' === $dest[count($dest) - 1]) {
136 12
            array_pop($dest);
137 8
        }
138
139 57
        $data = array_merge(array_slice($source, 0, $offset), $dest, array_slice($source, $offset + 1));
140 57
        if ($data === $this->data) {
141 3
            return $this;
142
        }
143
144 54
        return $this->newCollectionInstance($data);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->newCollectionInstance($data); (League\Uri\Interfaces\Collection) is incompatible with the return type documented by League\Uri\Components\Ab...hicalComponent::replace of type League\Uri\Components\Ab...ctHierarchicalComponent.

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...
145
    }
146
147
    /**
148
     * Validate a component as a HierarchicalComponent object
149
     *
150
     * @param HierarchicalComponent|string $component
151
     *
152
     * @return static
153
     */
154 189
    protected function validateComponent($component)
155
    {
156 189
        if (!$component instanceof HierarchicalComponent) {
157 138
            return $this->modify($component);
158
        }
159
160 75
        return $component;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $component; (League\Uri\Interfaces\HierarchicalComponent) is incompatible with the return type documented by League\Uri\Components\Ab...nent::validateComponent of type League\Uri\Components\Ab...ctHierarchicalComponent.

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...
161
    }
162
163
    /**
164
     * DEPRECATION WARNING! This method will be removed in the next major point release
165
     *
166
     * @deprecated deprecated since version 4.2
167
     *
168
     * return a new instance from an array or a traversable object
169
     *
170
     * @param \Traversable|string[] $data The segments list
171
     * @param int                   $type one of the constant IS_ABSOLUTE or IS_RELATIVE
172
     *
173
     * @throws InvalidArgumentException If $type is not a recognized constant
174
     *
175
     * @return static
176
     */
177
    public static function createFromArray($data, $type = self::IS_RELATIVE)
178
    {
179
        static $type_list = [self::IS_ABSOLUTE => 1, self::IS_RELATIVE => 1];
180
        if (!isset($type_list[$type])) {
181
            throw new InvalidArgumentException('Please verify the submitted constant');
182
        }
183
184
        return new static(static::formatComponentString($data, $type));
0 ignored issues
show
Deprecated Code introduced by
The method League\Uri\Components\Ab...formatComponentString() has been deprecated with message: deprecated since version 4.2 Return a formatted component string according to its type

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
185
    }
186
187
    /**
188
     * DEPRECATION WARNING! This method will be removed in the next major point release
189
     *
190
     * @deprecated deprecated since version 4.2
191
     *
192
     * Return a formatted component string according to its type
193
     *
194
     * @param \Traversable|string[] $data The segments list
195
     * @param int                   $type
196
     *
197
     * @throws InvalidArgumentException If $data is invalid
198
     *
199
     * @return string
200
     */
201
    protected static function formatComponentString($data, $type)
202
    {
203
        $path = implode(static::$separator, static::validateIterator($data));
204
        if (self::IS_ABSOLUTE == $type) {
205
            return static::$separator.$path;
206
        }
207
208
        return $path;
209
    }
210
}
211