Completed
Push — master ( c26f50...f56ee5 )
by ignace nyamagana
04:19
created

AbstractHierarchicalComponent::replace()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5.0144

Importance

Changes 4
Bugs 0 Features 0
Metric Value
c 4
b 0
f 0
dl 0
loc 19
ccs 11
cts 12
cp 0.9167
rs 8.8571
nc 5
cc 5
eloc 11
nop 2
crap 5.0144
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
     * Returns the component literal value
87
     *
88
     * @return string|null
89
     */
90
    abstract public function getContent();
91
92
    /**
93
     * Returns the instance string representation; If the
94
     * instance is not defined an empty string is returned
95
     *
96
     * @return string
97
     */
98 1051
    public function __toString()
99
    {
100 1051
        return (string) $this->getContent();
101
    }
102
103
    /**
104
     * Returns the instance string representation
105
     * with its optional URI delimiters
106
     *
107
     * @return string
108
     */
109 761
    public function getUriComponent()
110
    {
111 761
        return $this->__toString();
112
    }
113
114
    /**
115
     * Returns an instance with the modified segment
116
     *
117
     * This method MUST retain the state of the current instance, and return
118
     * an instance that contains the modified component with the replaced data
119
     *
120
     * @param int                          $offset    the label offset to remove and replace by
121
     *                                                the given component
122
     * @param HierarchicalComponent|string $component the component added
123
     *
124
     * @return static
125
     */
126 69
    public function replace($offset, $component)
127
    {
128 69
        if (!empty($this->data) && !$this->hasKey($offset)) {
129 15
            return $this;
130
        }
131
132 54
        $source = $this->toArray();
133 54
        $dest   = $this->validateComponent($component)->toArray();
134 54
        if ('' == $dest[count($dest) - 1]) {
135 12
            array_pop($dest);
136 8
        }
137
138 54
        $data = array_merge(array_slice($source, 0, $offset), $dest, array_slice($source, $offset + 1));
139 54
        if ($data === $this->data) {
140
            return $this;
141
        }
142
143 54
        return $this->newCollectionInstance($data);
144
    }
145
146
    /**
147
     * Validate a component as a HierarchicalComponent object
148
     *
149
     * @param HierarchicalComponent|string $component
150
     *
151
     * @return static
152
     */
153 186
    protected function validateComponent($component)
154
    {
155 186
        if (!$component instanceof HierarchicalComponent) {
156 75
            return $this->modify($component);
157
        }
158
159 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...
160
    }
161
}
162