Test Failed
Push — release/3.x ( 447600...4272fa )
by
unknown
02:22 queued 10s
created

Rewriter::extractPath()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 6
nc 3
nop 1
dl 0
loc 13
rs 9.2
c 0
b 0
f 0
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
    public function __construct(Aliasing $aliasing)
24
    {
25
        $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
    }
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
    public function setLocalDomains($localDomains)
34
    {
35
        $this->localDomains = $localDomains;
36
    }
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
    public function rewrite(array $urls, $mode)
56
    {
57
        $mappings = $this->aliasing->getAliasingMap(
58
            array_map(
59
                [$this, 'extractPath'],
60
                $urls
61
            ),
62
            $mode
63
        );
64
65
        $ret = [];
66
        foreach ($urls as $url) {
67
            if (isset($mappings[$url])) {
68
                // early match, if the value can be mapping directly, used that.
69
                $ret[$url] = $mappings[$url];
70
                continue;
71
            }
72
73
            $parts = $this->parseUrl($url);
74
75
            if (!isset($parts['path'])) {
76
                // no path, nothing to map.
77
                $ret[$url] = $url;
78
                continue;
79
            }
80
81
            if (isset($parts['host'])) {
82
                if (!in_array($parts['host'], $this->localDomains)) {
83
                    // external url, don't do mapping.
84
                    $ret[$url] = $url;
85
                    continue;
86
                }
87
            }
88
89
            // don't rewrite this.
90
            if (isset($parts['user']) || isset($parts['password'])) {
91
                $ret[$url]=  $url;
92
                continue;
93
            }
94
95
            $rewritten = '';
96
            if (isset($parts['scheme'])) {
97
                $rewritten .= $parts['scheme'] . ':';
98
            }
99
            if (isset($parts['host'])) {
100
                $rewritten .= '//' . $parts['host'];
101
            }
102
            if (isset($parts['port'])) {
103
                $rewritten .= ':' . $parts['port'];
104
            }
105
            if (isset($parts['path'])) {
106
                if (isset($mappings[$parts['path']])) {
107
                    $rewritten .= $mappings[$parts['path']];
108
                } else {
109
                    // no match on path level, keep as-is
110
                    $ret[$url] = $url;
111
                    continue;
112
                }
113
            }
114
            if (isset($parts['params'])) {
115
                $rewritten .= '/' . $parts['params'];
116
            }
117
            if (isset($parts['query'])) {
118
                $rewritten .= '?' . $parts['query'];
119
            }
120
            if (isset($parts['fragment'])) {
121
                $rewritten .= '#' . $parts['fragment'];
122
            }
123
124
            $ret[$url] = $rewritten;
125
        }
126
        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
    public function extractPath($url)
137
    {
138
        $parts = $this->parseUrl($url);
139
140
        if (!isset($parts['path'])) {
141
            return null;
142
        }
143
        // ignore non-http schemes
144
        if (isset($parts['scheme']) && !in_array($parts['scheme'], ['http', 'https'])) {
145
            return null;
146
        }
147
148
        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
    public function parseUrl($url)
166
    {
167
        $ret = parse_url($url);
168
169
        if (isset($ret['path'])) {
170
            $parts = explode('/', $ret['path']);
171
            $params = [];
172
            while (false !== strpos(end($parts), '=')) {
173
                array_push($params, array_pop($parts));
174
            }
175
            if (count($params)) {
176
                $ret['path'] = join('/', $parts);
177
                $ret['params'] = join('/', $params);
178
            }
179
        }
180
181
        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
    public function rewriteMatches($content, $mode, $matchedGroups)
202
    {
203
        $replacements = [];
204
        foreach ($this->rewrite(array_keys($matchedGroups), $mode) as $from => $to) {
205
            if (isset($matchedGroups[$from]) && $from !== $to) {
206
                foreach ($matchedGroups[$from] as list($source, $prefix, $oldUrl, $suffix)) {
207
                    $replacements[$source] = $prefix . $to . $suffix;
208
                }
209
            }
210
        }
211
212
        return strtr($content, $replacements);
213
    }
214
}
215