Completed
Push — master ( b0bce0...c1c6ff )
by ignace nyamagana
04:32
created

src/Components/AbstractHierarchicalComponent.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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 whether or not the component is absolute or not
58
     *
59
     * @return bool
60
     */
61 27
    public function isAbsolute()
62
    {
63 27
        return $this->isAbsolute === self::IS_ABSOLUTE;
64
    }
65
66
    /**
67
     * Returns an instance with the specified string
68
     *
69
     * This method MUST retain the state of the current instance, and return
70
     * an instance that contains the modified data
71
     *
72
     * @param string $value
73
     *
74
     * @return static
75
     */
76 549
    public function modify($value)
77
    {
78 549
        if ($value === $this->getContent()) {
79 165
            return $this;
80
        }
81
82 489
        return new static($value);
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 1054
    public function __toString()
99
    {
100 1054
        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 72
    public function replace($offset, $component)
127
    {
128 72
        if (!empty($this->data) && !$this->hasKey($offset)) {
129 15
            return $this;
130
        }
131
132 57
        $source = $this->toArray();
133 57
        $dest   = $this->validateComponent($component)->toArray();
134 57
        if ('' === $dest[count($dest) - 1]) {
135 12
            array_pop($dest);
136 8
        }
137
138 57
        $data = array_merge(array_slice($source, 0, $offset), $dest, array_slice($source, $offset + 1));
139 57
        if ($data === $this->data) {
140 3
            return $this;
141
        }
142
143 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...
144
    }
145
146
    /**
147
     * Validate a component as a HierarchicalComponent object
148
     *
149
     * @param HierarchicalComponent|string $component
150
     *
151
     * @return static
152
     */
153 189
    protected function validateComponent($component)
154
    {
155 189
        if (!$component instanceof HierarchicalComponent) {
156 138
            return $this->modify($component);
157
        }
158
159 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...
160
    }
161
}
162