Passed
Push — master ( bf6fa9...817f88 )
by Nikolaos
06:58
created

UriTrait::filterScheme()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 15
nc 3
nop 1
dl 0
loc 25
rs 9.7666
c 1
b 0
f 0
ccs 0
cts 21
cp 0
crap 12
1
<?php
2
3
/**
4
 * This file is part of the Phalcon Framework.
5
 *
6
 * (c) Phalcon Team <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE.txt
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Phalcon\Http\Message\Traits;
15
16
use Phalcon\Helper\Str;
17
use Phalcon\Http\Message\Exception\InvalidArgumentException;
18
19
use function array_keys;
20
use function explode;
21
use function implode;
22
use function ltrim;
23
use function preg_replace;
24
use function rawurlencode;
25
26
/**
27
 * PSR-7 Uri
28
 */
29
trait UriTrait
30
{
31
    /**
32
     * If the value passed is empty it returns it prefixed and suffixed with
33
     * the passed parameters
34
     *
35
     * @param string $value
36
     * @param string $prefix
37
     * @param string $suffix
38
     *
39
     * @return string
40
     */
41
    private function checkValue(string $value, string $prefix = '', string $suffix = ''): string
42
    {
43
        if ('' !== $value) {
44
            $value = $prefix . $value . $suffix;
45
        }
46
47
        return $value;
48
    }
49
50
    /**
51
     * If no fragment is present, this method MUST return an empty string.
52
     *
53
     * The leading "#" character is not part of the fragment and MUST NOT be
54
     * added.
55
     *
56
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
57
     * any characters. To determine what characters to encode, please refer to
58
     * RFC 3986, Sections 2 and 3.5.
59
     *
60
     * @see https://tools.ietf.org/html/rfc3986#section-2
61
     * @see https://tools.ietf.org/html/rfc3986#section-3.5
62
     *
63
     * @param string $fragment
64
     *
65
     * @return string
66
     */
67
    private function filterFragment(string $fragment): string
68
    {
69
        return rawurlencode($fragment);
70
    }
71
72
    /**
73
     *
74
     * The path can either be empty or absolute (starting with a slash) or
75
     * rootless (not starting with a slash). Implementations MUST support all
76
     * three syntaxes.
77
     *
78
     * Normally, the empty path "" and absolute path "/" are considered equal as
79
     * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically
80
     * do this normalization because in contexts with a trimmed base path, e.g.
81
     * the front controller, this difference becomes significant. It's the task
82
     * of the user to handle both "" and "/".
83
     *
84
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
85
     * any characters. To determine what characters to encode, please refer to
86
     * RFC 3986, Sections 2 and 3.3.
87
     *
88
     * As an example, if the value should include a slash ("/") not intended as
89
     * delimiter between path segments, that value MUST be passed in encoded
90
     * form (e.g., "%2F") to the instance.
91
     *
92
     * @see https://tools.ietf.org/html/rfc3986#section-2
93
     * @see https://tools.ietf.org/html/rfc3986#section-3.3
94
     *
95
     * @param string $path
96
     *
97
     * @return string The URI path.
98
     */
99
    private function filterPath(string $path): string
100
    {
101
        if ('' === $path || true !== Str::startsWith($path, '/')) {
102
            return $path;
103
        }
104
105
        $parts = explode('/', $path);
106
        foreach ($parts as $key => $element) {
107
            $parts[$key] = rawurlencode($element);
108
        }
109
110
        $path = implode('/', $parts);
111
112
        return '/' . ltrim($path, '/');
113
    }
114
115
    /**
116
     * Checks the port. If it is a standard one (80,443) then it returns null
117
     *
118
     * @param mixed $port
119
     *
120
     * @return int|null
121
     */
122
    private function filterPort($port): ?int
123
    {
124
        $ports = [
125
            80  => 1,
126
            443 => 1,
127
        ];
128
129
        if (null !== $port) {
130
            $port = (int) $port;
131
            if (isset($ports[$port])) {
132
                $port = null;
133
            }
134
        }
135
136
        return $port;
137
    }
138
139
    /**
140
     * If no query string is present, this method MUST return an empty string.
141
     *
142
     * The leading "?" character is not part of the query and MUST NOT be
143
     * added.
144
     *
145
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
146
     * any characters. To determine what characters to encode, please refer to
147
     * RFC 3986, Sections 2 and 3.4.
148
     *
149
     * As an example, if a value in a key/value pair of the query string should
150
     * include an ampersand ("&") not intended as a delimiter between values,
151
     * that value MUST be passed in encoded form (e.g., "%26") to the instance.
152
     *
153
     * @see https://tools.ietf.org/html/rfc3986#section-2
154
     * @see https://tools.ietf.org/html/rfc3986#section-3.4
155
     *
156
     * @param string $query
157
     *
158
     * @return string The URI query string.
159
     */
160
    private function filterQuery(string $query): string
161
    {
162
        if ('' === $query) {
163
            return '';
164
        }
165
166
        $query = ltrim($query, '?');
167
        $parts = explode("&", $query);
168
        foreach ($parts as $index => $part) {
169
            [$key, $value] = $this->splitQueryValue($part);
170
            if (null === $value) {
171
                $parts[$index] = rawurlencode($key);
172
173
                continue;
174
            }
175
176
            $parts[$index] = rawurlencode($key) . '=' . rawurlencode($value);
177
        }
178
179
        return implode('&', $parts);
180
    }
181
182
    /**
183
     * Filters the passed scheme - only allowed schemes
184
     *
185
     * @param string $scheme
186
     *
187
     * @return string
188
     */
189
    private function filterScheme(string $scheme): string
190
    {
191
        $filtered = preg_replace(
192
            '#:(//)?$#',
193
            '',
194
            mb_strtolower($scheme)
195
        );
196
        $schemes  = [
197
            'http'  => 1,
198
            'https' => 1,
199
        ];
200
201
        if ('' === $filtered) {
202
            return '';
203
        }
204
205
        if (!isset($schemes[$filtered])) {
206
            throw new InvalidArgumentException(
207
                "Unsupported scheme [" . $filtered . "]. " .
208
                "Scheme must be one of [" .
209
                implode(", ", array_keys($schemes)) . "]"
210
            );
211
        }
212
213
        return $scheme;
214
    }
215
216
    /**
217
     * @param string $element
218
     *
219
     * @return array
220
     */
221
    private function splitQueryValue(string $element): array
222
    {
223
        $data = explode('=', $element, 2);
224
        if (!isset($data[1])) {
225
            $data[] = null;
226
        }
227
228
        return $data;
229
    }
230
}
231