Completed
Push — master ( fb692e...c26f50 )
by ignace nyamagana
03:48
created

AbstractHierarchicalComponent::createFromArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
ccs 5
cts 5
cp 1
rs 9.4285
cc 2
eloc 5
nc 2
nop 2
crap 2
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 League\Uri\Interfaces\HierarchicalComponent;
15
use League\Uri\Types\ImmutableCollectionTrait;
16
use League\Uri\Types\ImmutableComponentTrait;
17
18
/**
19
 * An abstract class to ease collection like Component object manipulation
20
 *
21
 * @package League.uri
22
 * @author  Ignace Nyamagana Butera <[email protected]>
23
 * @since   4.0.0
24
 */
25
abstract class AbstractHierarchicalComponent
26
{
27
    use ImmutableCollectionTrait;
28
29
    use ImmutableComponentTrait;
30
31
    const IS_ABSOLUTE = 1;
32
33
    const IS_RELATIVE = 0;
34
35
    /**
36
     * Hierarchical component separator
37
     *
38
     * @var string
39
     */
40
    protected static $separator;
41
42
    /**
43
     * Is the object considered absolute
44
     *
45
     * @var int
46
     */
47
    protected $isAbsolute = self::IS_RELATIVE;
48
49
    /**
50
     * new instance
51
     *
52
     * @param null|string $str the component value
53
     */
54
    abstract public function __construct($str);
55
56
    /**
57
     * Returns an instance with the specified string
58
     *
59
     * This method MUST retain the state of the current instance, and return
60
     * an instance that contains the modified data
61
     *
62
     * @param string $value
63
     *
64
     * @return static
65
     */
66 501
    public function modify($value)
67
    {
68 501
        if ($value == $this->__toString()) {
69 66
            return $this;
70
        }
71
72 462
        return new static($value);
73
    }
74
75
    /**
76
     * Returns whether or not the component is absolute or not
77
     *
78
     * @return bool
79
     */
80 27
    public function isAbsolute()
81
    {
82 27
        return $this->isAbsolute == self::IS_ABSOLUTE;
83
    }
84
85
    /**
86
     * Return a new instance when needed
87
     *
88
     * @param array $data
89
     *
90
     * @return static
91
     */
92
    abstract protected function newCollectionInstance(array $data);
93
94
    /**
95
     * Returns the component literal value
96
     *
97
     * @return string|null
98
     */
99
    abstract public function getContent();
100
101
    /**
102
     * Returns the instance string representation; If the
103
     * instance is not defined an empty string is returned
104
     *
105
     * @return string
106
     */
107 1051
    public function __toString()
108
    {
109 1051
        return (string) $this->getContent();
110
    }
111
112
    /**
113
     * Returns the instance string representation
114
     * with its optional URI delimiters
115
     *
116
     * @return string
117
     */
118 761
    public function getUriComponent()
119
    {
120 761
        return $this->__toString();
121
    }
122
123
    /**
124
     * Validate a component as a HierarchicalComponent object
125
     *
126
     * @param HierarchicalComponent|string $component
127
     *
128
     * @return static
129
     */
130 186
    protected function validateComponent($component)
131
    {
132 186
        if (!$component instanceof HierarchicalComponent) {
133 75
            return $this->modify($component);
134
        }
135
136 135
        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...
137
    }
138
139
    /**
140
     * Returns an instance with the modified segment
141
     *
142
     * This method MUST retain the state of the current instance, and return
143
     * an instance that contains the modified component with the replaced data
144
     *
145
     * @param int                          $offset    the label offset to remove and replace by
146
     *                                                the given component
147
     * @param HierarchicalComponent|string $component the component added
148
     *
149
     * @return static
150
     */
151 69
    public function replace($offset, $component)
152
    {
153 69
        if (!empty($this->data) && !$this->hasKey($offset)) {
154 15
            return $this;
155
        }
156
157 54
        $source = $this->toArray();
158 54
        $dest   = $this->validateComponent($component)->toArray();
159 54
        if ('' == $dest[count($dest) - 1]) {
160 12
            array_pop($dest);
161 8
        }
162
163 54
        return $this->newCollectionInstance(
164 54
            array_merge(array_slice($source, 0, $offset), $dest, array_slice($source, $offset + 1))
165 36
        );
166
    }
167
}
168