Rewriter   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 199
Duplicated Lines 0 %

Test Coverage

Coverage 95.89%

Importance

Changes 4
Bugs 2 Features 0
Metric Value
eloc 68
c 4
b 2
f 0
dl 0
loc 199
ccs 70
cts 73
cp 0.9589
rs 9.92
wmc 31

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A setLocalDomains() 0 3 1
A extractPath() 0 13 4
A parseUrl() 0 17 4
F rewrite() 0 72 16
A rewriteMatches() 0 12 5
1
<?php
2
/**
3
 * @author Gerard van Helden <[email protected]>
4
 * @copyright Zicht Online <http://zicht.nl>
5
 */
6
7
namespace Zicht\Bundle\UrlBundle\Url;
8
9
use Zicht\Bundle\UrlBundle\Aliasing\Aliasing;
10
11
/**
12
 * Class Rewriter
13
 */
14
class Rewriter
15
{
16
    private $localDomains = [];
17
18
    /**
19
     * Constructor
20
     *
21
     * @param Aliasing $aliasing
22
     */
23 26
    public function __construct(Aliasing $aliasing)
24
    {
25 26
        $this->aliasing = $aliasing;
0 ignored issues
show
Bug Best Practice introduced by
The property aliasing does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
26 26
    }
27
28
    /**
29
     * Set a list of domains in urls to consider 'local', i.e. process the 'path' while keeping the domain in tact.
30
     *
31
     * @param string[] $localDomains
32
     */
33 9
    public function setLocalDomains($localDomains)
34
    {
35 9
        $this->localDomains = $localDomains;
36 9
    }
37
38
    /**
39
     * Rewrite all $urls, given the $mappings map of mappings (from => to), within the context of
40
     * host names $localDomains.
41
     *
42
     * Example:
43
     *
44
     *     rewrite(['http://example.org/foo?x=y'], ['/foo' => '/bar'], ['example.org']);
45
     *
46
     *
47
     * Would return the following mapping:
48
     *
49
     * ['http://example.org/foo?x=y' => 'http://example.org/bar?x=y']
50
     *
51
     * @param string[] $urls
52
     * @param string $mode
53
     * @return array
54
     */
55 23
    public function rewrite(array $urls, $mode)
56
    {
57 23
        $mappings = $this->aliasing->getAliasingMap(
58 23
            array_map(
59 23
                [$this, 'extractPath'],
60 23
                $urls
61
            ),
62 23
            $mode
63
        );
64
65 23
        $ret = [];
66 23
        foreach ($urls as $url) {
67 22
            if (isset($mappings[$url])) {
68
                // early match, if the value can be mapping directly, used that.
69 11
                $ret[$url] = $mappings[$url];
70 11
                continue;
71
            }
72
73 11
            $parts = $this->parseUrl($url);
74
75 11
            if (!isset($parts['path'])) {
76
                // no path, nothing to map.
77 1
                $ret[$url] = $url;
78 1
                continue;
79
            }
80
81 11
            if (isset($parts['host'])) {
82 8
                if (!in_array($parts['host'], $this->localDomains)) {
83
                    // external url, don't do mapping.
84 3
                    $ret[$url] = $url;
85 3
                    continue;
86
                }
87
            }
88
89
            // don't rewrite this.
90 8
            if (isset($parts['user']) || isset($parts['password'])) {
91
                $ret[$url]=  $url;
92
                continue;
93
            }
94
95 8
            $rewritten = '';
96 8
            if (isset($parts['scheme'])) {
97 5
                $rewritten .= $parts['scheme'] . ':';
98
            }
99 8
            if (isset($parts['host'])) {
100 5
                $rewritten .= '//' . $parts['host'];
101
            }
102 8
            if (isset($parts['port'])) {
103
                $rewritten .= ':' . $parts['port'];
104
            }
105 8
            if (isset($parts['path'])) {
106 8
                if (isset($mappings[$parts['path']])) {
107 6
                    $rewritten .= $mappings[$parts['path']];
108
                } else {
109
                    // no match on path level, keep as-is
110 3
                    $ret[$url] = $url;
111 3
                    continue;
112
                }
113
            }
114 6
            if (isset($parts['params'])) {
115 3
                $rewritten .= '/' . $parts['params'];
116
            }
117 6
            if (isset($parts['query'])) {
118 3
                $rewritten .= '?' . $parts['query'];
119
            }
120 6
            if (isset($parts['fragment'])) {
121 1
                $rewritten .= '#' . $parts['fragment'];
122
            }
123
124 6
            $ret[$url] = $rewritten;
125
        }
126 23
        return $ret;
127
    }
128
129
130
    /**
131
     * Extract the path of the URL which is considered for aliasing.
132
     *
133
     * @param string $url
134
     * @return string|null
135
     */
136 25
    public function extractPath($url)
137
    {
138 25
        $parts = $this->parseUrl($url);
139
140 25
        if (!isset($parts['path'])) {
141 1
            return null;
142
        }
143
        // ignore non-http schemes
144 25
        if (isset($parts['scheme']) && !in_array($parts['scheme'], ['http', 'https'])) {
145 1
            return null;
146
        }
147
148 24
        return $parts['path'];
149
    }
150
151
152
    /**
153
     * Parse the url, and additionally add a 'parameters' part which is defined as follows:
154
     *
155
     * The parameters may occur after the path, separated by slashes, where each of the key value pairs are
156
     * separated by '='.
157
     *
158
     * e.g.:
159
     *      /this/is/the/path/while=these/are=paremeters
160
     *      ^----- path ----^ ^------- params ---------^
161
     *
162
     * @param string $url
163
     * @return mixed
164
     */
165 25
    public function parseUrl($url)
166
    {
167 25
        $ret = parse_url($url);
168
169 25
        if (isset($ret['path'])) {
170 25
            $parts = explode('/', $ret['path']);
171 25
            $params = [];
172 25
            while (false !== strpos(end($parts), '=')) {
173 5
                array_push($params, array_pop($parts));
174
            }
175 25
            if (count($params)) {
176 5
                $ret['path'] = join('/', $parts);
177 5
                $ret['params'] = join('/', $params);
178
            }
179
        }
180
181 25
        return $ret;
182
    }
183
184
185
    /**
186
     * This convenenience method processes the urls in the content with a structure matching the following:
187
     *
188
     * ['the url' => ['total string that should be replaced', 'the prefix', 'the url', 'the suffix']]
189
     *
190
     * e.g.:
191
     * 'http://example.org/' => ['<a href="http://example.org/"', '<a href="', 'http://example.org/', '"']
192
     *
193
     * It replaces contents by replacing all occurrences of the 0 index with the 1 index, concatenated with the
194
     * rewritten url, and the suffix.
195
     *
196
     * @param string $content
197
     * @param string $mode
198
     * @param mixed $matchedGroups
199
     * @return string
200
     */
201 14
    public function rewriteMatches($content, $mode, $matchedGroups)
202
    {
203 14
        $replacements = [];
204 14
        foreach ($this->rewrite(array_keys($matchedGroups), $mode) as $from => $to) {
205 13
            if (isset($matchedGroups[$from]) && $from !== $to) {
206 10
                foreach ($matchedGroups[$from] as list($source, $prefix, $oldUrl, $suffix)) {
207 13
                    $replacements[$source] = $prefix . $to . $suffix;
208
                }
209
            }
210
        }
211
212 14
        return strtr($content, $replacements);
213
    }
214
}
215