Passed
Push — master ( 4870c3...732c37 )
by Petr
07:47
created

Handler::buildAddress()   D

Complexity

Conditions 17
Paths 168

Size

Total Lines 49
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 67.753

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 24
c 1
b 0
f 0
dl 0
loc 49
ccs 11
cts 25
cp 0.44
rs 4.65
cc 17
nc 168
nop 1
crap 67.753

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace kalanis\kw_address_handler;
4
5
6
/**
7
 * Class Handler
8
 * @package kalanis\kw_address_handler
9
 * Class for basic work with address. It allows access for aboject manipulating the address. updating params when they come from string representation.
10
 * It has internal representation of address.
11
 */
12
class Handler
13
{
14
    /** @var Sources\Sources|null */
15
    protected $source = null;
16
    /** @var Params */
17
    protected $params = null;
18
19 6
    public function __construct(?Sources\Sources $address = null)
20
    {
21 6
        $this->params = new Params();
22 6
        $this->setSource($address);
23 6
    }
24
25 6
    public function setSource(?Sources\Sources $sources): self
26
    {
27 6
        if (empty($sources)) {
28 5
            return $this;
29
        }
30 6
        $this->source = $sources;
31 6
        $this->parse();
32 6
        return $this;
33
    }
34
35 6
    protected function parse(): void
36
    {
37 6
        $parts = parse_url($this->source->/** @scrutinizer ignore-call */getAddress());
38 6
        if ((false !== $parts) && isset($parts['path'])) {
39 6
            $this->source->/** @scrutinizer ignore-call */setPath($parts['path']);
40 6
            if (!isset($parts['query'])) {
41 1
                $parts['query'] = '';
42
            }
43 6
            $this->params->setParamsData(static::http_parse_query($parts['query']));
44
        }
45 6
    }
46
47
    /**
48
     * Returns an address inside the object.
49
     * @return Sources\Sources|null
50
     */
51 2
    public function getSource(): ?Sources\Sources
52
    {
53 2
        return $this->source;
54
    }
55
56
    /**
57
     * Returns object accessing parsed params inside the address
58
     * @return Params
59
     */
60 6
    public function getParams(): Params
61
    {
62 6
        return $this->params;
63
    }
64
65
    /**
66
     * Get address if there is anything to parse
67
     * @return string|null
68
     */
69 6
    public function getAddress(): ?string
70
    {
71 6
        return $this->source ? $this->rebuild()->source->/** @scrutinizer ignore-call */getAddress() : null;
72
    }
73
74 6
    protected function rebuild(): self
75
    {
76 6
        $parts = parse_url($this->source->/** @scrutinizer ignore-call */getAddress());
77 6
        if (false !== $parts) {
78 6
            if (!isset($parts['query'])) {
79 1
                $parts['query'] = '';
80
            }
81 6
            $queryArray = static::http_parse_query($parts['query']);
82 6
            foreach ($this->params->getParamsData() as $paramName => $paramValue) {
83 5
                $queryArray[$paramName] = $paramValue;
84
            }
85 6
            foreach ($queryArray as $paramName => $paramValue) {
86 5
                if (!$this->params->offsetExists($paramName)) {
87 1
                    unset($queryArray[$paramName]);
88
                }
89
            }
90 6
            $parts['query'] = http_build_query($queryArray);
91 6
            $this->source->/** @scrutinizer ignore-call */setAddress($this->buildAddress($parts));
92
        }
93 6
        return $this;
94
    }
95
96
    /**
97
     * Parses http query string into an array
98
     *
99
     * @author Alxcube <[email protected]>
100
     *
101
     * @param string $queryString String to parse
102
     * @param non-empty-string $argSeparator Query arguments separator
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
103
     * @param integer $decType Decoding type
104
     * @return array<int, string>
105
     * @codeCoverageIgnore for now - external source
106
     */
107 6
    public static function http_parse_query(string $queryString, string $argSeparator = '&', int $decType = PHP_QUERY_RFC1738): array
108
    {
109 6
        if (empty($queryString)) { return []; }
110 5
        $result = [];
111 5
        $parts  = explode($argSeparator, $queryString);
112
113 5
        foreach ($parts as $part) {
114 5
            list($paramName, $paramValue) = array_pad(explode('=', $part, 2), 2, '');
115
116
            switch ($decType) {
117 5
                case PHP_QUERY_RFC3986:
118
                    $paramName  = rawurldecode($paramName);
119
                    $paramValue = rawurldecode($paramValue);
120
                    break;
121
122 5
                case PHP_QUERY_RFC1738:
123
                default:
124 5
                    $paramName  = urldecode($paramName);
125 5
                    $paramValue = urldecode($paramValue);
126 5
                    break;
127
            }
128
129
130 5
            if (preg_match_all('/\[([^\]]*)\]/m', $paramName, $matches)) {
131
                $paramName = substr($paramName, 0, intval(strpos($paramName, '[')));
132
                $keys = array_merge([$paramName], $matches[1]);
133
            } else {
134 5
                $keys = [$paramName];
135
            }
136
137 5
            $target = &$result;
138
139 5
            foreach ($keys as $index) {
140 5
                if ('' === $index) {
141
                    if (isset($target)) {
142
                        if (is_array($target)) {
143
                            $intKeys = array_filter(array_keys($target), 'is_int');
144
                            $index   = count($intKeys) ? max($intKeys)+1 : 0;
145
                        } else {
146
                            $target = [$target];
147
                            $index  = 1;
148
                        }
149
                    } else {
150
                        $target = [];
151
                        $index  = 0;
152
                    }
153 5
                } elseif (isset($target[$index]) && !is_array($target[$index])) {
154
                    $target[$index] = [$target[$index]];
155
                }
156
157 5
                $target = &$target[$index];
158
            }
159
160 5
            if (is_array($target)) {
161
                $target[] = $paramValue;
162
            } else {
163 5
                $target = $paramValue;
164
            }
165
        }
166
167 5
        return $result;
168
    }
169
170
    /**
171
     * Build an address from parse_url parts. The generated address will be a relative address if a scheme or host are not provided.
172
     * @param array<string, int|string> $parts array of parse_url parts
173
     * @return string
174
     * @codeCoverageIgnore for now
175
     */
176 6
    protected function buildAddress(array $parts): string
177
    {
178 6
        $url = $scheme = '';
179
180 6
        if (isset($parts['scheme'])) {
181
            $scheme = $parts['scheme'];
182
            $url .= $scheme . ':';
183
        }
184
185 6
        if (isset($parts['host'])) {
186
            $url .= '//';
187
            if (isset($parts['user'])) {
188
                $url .= $parts['user'];
189
                if (isset($parts['pass'])) {
190
                    $url .= ':' . $parts['pass'];
191
                }
192
                $url .= '@';
193
            }
194
195
            $url .= $parts['host'];
196
197
            // Only include the port if it is not the default port of the scheme
198
            if (isset($parts['port'])
199
                && !(('http' == $scheme && 80 == $parts['port']) || ('https' == $scheme && 443 == $parts['port']))
200
            ) {
201
                $url .= ':' . $parts['port'];
202
            }
203
        }
204
205
        // Add the path component if present
206 6
        if (isset($parts['path']) && (0 !== strlen(strval($parts['path'])))) {
207
            // Always ensure that the path begins with '/' if set and something is before the path
208 5
            if ($url && strval($parts['path'])[0] != '/' && '/' != substr($url, -1)) {
209
                $url .= '/';
210
            }
211 5
            $url .= $parts['path'];
212
        }
213
214
        // Add the query string if present
215 6
        if (isset($parts['query'])) {
216 6
            $url .= '?' . $parts['query'];
217
        }
218
219
        // Ensure that # is only added to the url if fragment contains anything.
220 6
        if (isset($parts['fragment'])) {
221
            $url .= '#' . $parts['fragment'];
222
        }
223
224 6
        return $url;
225
    }
226
}
227