Completed
Push — master ( 420c23...93f504 )
by ignace nyamagana
02:57
created

Relativize   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 166
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 10
Bugs 0 Features 0
Metric Value
c 10
b 0
f 0
dl 0
loc 166
ccs 59
cts 59
cp 1
rs 10
wmc 26
lcom 1
cbo 2

8 Methods

Rating   Name   Duplication   Size   Complexity  
A hostToAscii() 0 9 2
A __construct() 0 5 1
B __invoke() 0 24 5
A isRelativizable() 0 8 3
A relativizePath() 0 16 4
A getSegments() 0 8 3
B formatPath() 0 18 6
A formatPathWithEmptyBaseQuery() 0 7 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 2016 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\Modifiers;
13
14
use League\Uri\Interfaces\Uri;
15
use Psr\Http\Message\UriInterface;
16
17
/**
18
 * Resolve an URI according to a base URI using
19
 * RFC3986 rules
20
 *
21
 * @package League.uri
22
 * @author  Ignace Nyamagana Butera <[email protected]>
23
 * @since   4.2.0
24
 */
25
class Relativize extends AbstractUriModifier
26
{
27
    /**
28
     * Base URI
29
     *
30
     * @var Uri|UriInterface
31
     */
32
    protected $base_uri;
33
34
    /**
35
     * New instance
36
     *
37
     * @param Uri|UriInterface $base_uri
38
     */
39 147
    public function __construct($base_uri)
40
    {
41 147
        $this->assertUriObject($base_uri);
42 147
        $this->base_uri = $this->hostToAscii($base_uri);
43 147
    }
44
45
    /**
46
     * Convert the Uri host component to its ascii version
47
     *
48
     * @param Uri|UriInterface $uri
49
     *
50
     * @return Uri|UriInterface
51
     */
52 147
    protected function hostToAscii($uri)
53
    {
54 147
        static $modifier;
55 147
        if (null === $modifier) {
56 3
            $modifier = new HostToAscii();
57 2
        }
58
59 147
        return $modifier($uri);
60
    }
61
62
    /**
63
     * Return a Uri object modified according to the modifier
64
     *
65
     * @param Uri|UriInterface $target
66
     *
67
     * @return Uri|UriInterface
68
     */
69 147
    public function __invoke($target)
70
    {
71 147
        $this->assertUriObject($target);
72 147
        if (!$this->isRelativizable($target)) {
73 63
            return $target;
74
        }
75
76 84
        $target = $target->withScheme('')->withPort(null)->withUserInfo('')->withHost('');
77
78 84
        $target_path = $target->getPath();
79 84
        if ($target_path !== $this->base_uri->getPath()) {
80 60
            return $target->withPath($this->relativizePath($target_path));
81
        }
82
83 24
        if ($target->getQuery() === $this->base_uri->getQuery()) {
84 12
            return $target->withPath('')->withQuery('');
85
        }
86
87 12
        if ('' === $target->getQuery()) {
88 6
            return $target->withPath($this->formatPathWithEmptyBaseQuery($target_path));
89
        }
90
91 6
        return $target->withPath('');
92
    }
93
94
    /**
95
     * Tell whether the submitted URI object can be relativize
96
     *
97
     * @param Uri|UriInterface $target
98
     *
99
     * @return bool
100
     */
101 147
    protected function isRelativizable($target)
102
    {
103 147
        $target = $this->hostToAscii($target);
104
105 147
        return $this->base_uri->getScheme() === $target->getScheme()
106 147
            && $this->base_uri->getAuthority() === $target->getAuthority()
107 147
            && !uri_reference($target)['relative_path'];
108
    }
109
110
    /**
111
     * Relative the URI for a authority-less target URI
112
     *
113
     * @param string $path
114
     *
115
     * @return string
116
     */
117 60
    protected function relativizePath($path)
118
    {
119 60
        $base_segments = $this->getSegments($this->base_uri->getPath());
120 60
        $target_segments = $this->getSegments($path);
121 60
        $target_basename = array_pop($target_segments);
122 60
        array_pop($base_segments);
123 60
        foreach ($base_segments as $offset => $segment) {
124 48
            if (!isset($target_segments[$offset]) || $segment !== $target_segments[$offset]) {
125 24
                break;
126
            }
127 33
            unset($base_segments[$offset], $target_segments[$offset]);
128 40
        }
129 60
        $target_segments[] = $target_basename;
130
131 60
        return $this->formatPath(str_repeat('../', count($base_segments)).implode('/', $target_segments));
132
    }
133
134
    /**
135
     * returns the path segments
136
     *
137
     * @param string $path
138
     *
139
     * @return array
140
     */
141 66
    protected function getSegments($path)
142
    {
143 66
        if ('' !== $path && '/' === $path[0]) {
144 66
            $path = substr($path, 1);
145 44
        }
146
147 66
        return explode('/', $path);
148
    }
149
150
    /**
151
     * Formatting the path to keep a valid URI
152
     *
153
     * @param string $path
154
     *
155
     * @return string
156
     */
157 60
    protected function formatPath($path)
158
    {
159 60
        if ('' === $path) {
160 9
            $base_path = $this->base_uri->getPath();
161 9
            return in_array($base_path, ['', '/']) ? $base_path : './';
162
        }
163
164 51
        if (false === ($colon_pos = strpos($path, ':'))) {
165 42
            return $path;
166
        }
167
168 9
        $slash_pos = strpos($path, '/');
169 9
        if (false === $slash_pos || $colon_pos < $slash_pos) {
170 6
            return "./$path";
171
        }
172
173 3
        return $path;
174
    }
175
176
    /**
177
     * Formatting the path to keep a resolvable URI
178
     *
179
     * @param string $path
180
     *
181
     * @return string
182
     */
183 6
    protected function formatPathWithEmptyBaseQuery($path)
184
    {
185 6
        $target_segments = $this->getSegments($path);
186 6
        $basename = end($target_segments);
187
188 6
        return '' === $basename ? './' : $basename;
189
    }
190
}
191