Passed
Push — master ( 599585...a7cf01 )
by Béla
03:50
created

Url::getQueryOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 0
cts 2
cp 0
rs 10
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
namespace POData\Common;
4
5
use POData\Common\ODataConstants;
6
7
8
/**
9
 * Class Url
10
 * @package POData\Common
11
 */
12
class Url
13
{
14
    private $_urlAsString = null;
15
    private $_parts = array();
16
    private $_segments = array();
17
    const ABS_URL_REGEXP = '/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/';
18
    const REL_URL_REGEXP = '/^(\/|\/([\w#!:.?+=&%@!\-\/]))?/';
19
20
    /**
21
     * array of query-string parameters
22
     *
23
     * @var array(string, string)
24
     */
25
    private $_queryOptions;
26
27
    /**
28
     * Creates new instance of Url
29
     *
30
     * @param string  $url        The url as string
31
     * @param boolean $isAbsolute Whether the given url is absolute or not
32
     *
33
     * @throws UrlFormatException Exception if url is malformed
34
     */
35 138
    public function __construct($url, $isAbsolute = true)
36
    {
37 138
        if ($isAbsolute) {
38 137
            if (!preg_match(self::ABS_URL_REGEXP, $url)) {
39 137
                throw new UrlFormatException(Messages::urlMalformedUrl($url));
40
            }
41
        } else {
42 1
            if (!preg_match(self::REL_URL_REGEXP, $url)) { //TODO: this matches EVERYTHING!!! what's the intent here? see #77
43
                throw new UrlFormatException(Messages::urlMalformedUrl($url));
44
            }
45
        }
46
47 137
        $this->_parts = parse_url(preg_replace('(\t|\n)', '', $url));
48 137
        if ($this->_parts === false) {
49 1
            throw new UrlFormatException(Messages::urlMalformedUrl($url));
50
        }
51
52 136
        $path = urldecode($this->getPath());
53 136
        if ($path != null) {
54 136
            $this->_segments = explode('/', trim($path, '/'));
55 136
            foreach ($this->_segments as $segment) {
56 136
                $segment = trim($segment);
57 136
                if (empty($segment)) {
58 1
                    throw new UrlFormatException(Messages::urlMalformedUrl($url));
59
                }
60
            }
61
        }
62
63 135
        $this->_urlAsString = urldecode($url);
64
65 135
        $this->_queryOptions = [];
66 135
        if (!empty($this->_parts['query'])) {
67 8
            parse_str($this->_parts['query'], $this->_queryOptions);
68
        }
69
    }
70
71
	 /**
72
     * Gets query options
73
     *
74
     * @return array
75
     */
76
    public function getQueryOptions()
77
    {
78
        return $this->_queryOptions;
79
    }
80
81
	 /**
82
     * Sets query options
83
     */
84 84
    public function setQueryOptions($_queryOptions)
85
    {
86 84
        $this->_queryOptions = $_queryOptions;
87
    }
88
89
    /**
90
     * Gets the url represented by this instance as string
91
     *
92
     * @return string
93
     */
94 87
    public function getUrlAsString()
95
    {
96 87
        return $this->_urlAsString;
97
    }
98
99
    /**
100
     * Get the scheme part of the Url
101
     *
102
     * @return string|null Returns the scheme part of the url,
103
     * if scheme is missing returns NULL
104
     */
105 96
    public function getScheme()
106
    {
107 96
        return isset ($this->_parts['scheme']) ? $this->_parts['scheme'] : null;
108
    }
109
110
    /**
111
     * Get the host part of the Url
112
     *
113
     * @return string|null Returns the host part of the url,
114
     * if host is missing returns NULL
115
     */
116 96
    public function getHost()
117
    {
118 96
        return isset ($this->_parts['host']) ? $this->_parts['host'] : null;
119
    }
120
121
    /**
122
     * Get the port number present in the url
123
     *
124
     * @return int
125
     */
126 96
    public function getPort()
127
    {
128 96
        $port = isset ($this->_parts['port']) ? $this->_parts['port'] : null;
129 96
        if ($port != null) {
130 85
            return $port;
131
        }
132
133 12
        $host = $this->getScheme();
134 12
        if ($host == 'https') {
135 1
            $port = 443;
136 12
        } else if ($host == 'http') {
137 12
            $port = 80;
138
        }
139
140 12
        return $port;
141
    }
142
143
    /**
144
     * To get the path segment
145
     *
146
     * @return string Returns the host part of the url,
147
     * if host is missing returns NULL
148
     */
149 136
    public function getPath()
150
    {
151 136
        return isset ($this->_parts['path']) ? $this->_parts['path'] : null;
152
    }
153
154
    /**
155
     * Get the query part
156
     *
157
     * @return string|null Returns the query part of the url,
158
     * if query is missing returns NULL
159
     */
160 85
    public function getQuery()
161
    {
162 85
        return isset ($this->_parts['query']) ? $this->_parts['query'] : null;
163
    }
164
165
    /**
166
     * Get the fragment part
167
     *
168
     * @return string|null Returns the fragment part of the url,
169
     * if fragment is missing returns NULL
170
     */
171 84
    public function getFragment()
172
    {
173 84
        return isset ($this->_parts['fragment']) ? $this->_parts['fragment'] : null;
174
    }
175
176
    /**
177
     * Get the segments
178
     *
179
     * @return array Returns array of segments,
180
     * if no segments then returns empty array.
181
     */
182 97
    public function getSegments()
183
    {
184 97
        return $this->_segments;
185
    }
186
187
    /**
188
     * Gets number of segments, if no segment then returns zero.
189
     *
190
     * @return int
191
     */
192 94
    public function getSegmentCount()
193
    {
194 94
        return count($this->_segments);
195
    }
196
197
    /**
198
     * Checks the url is absolute or not
199
     *
200
     * @return boolean Returns true if absolute url otherwise false
201
     */
202 1
    public function isAbsolute()
203
    {
204 1
        return isset ($this->_parts['scheme']);
205
    }
206
207
    /**
208
     * Checks the url is relative or not
209
     *
210
     * @return boolean
211
     */
212 1
    public function isRelative()
213
    {
214 1
        return !$this->isAbsolute();
215
    }
216
217
    /**
218
     * Checks this url is base uri for the given url.
219
     *
220
     * @param Url $targetUri The url to inspect the base part.
221
     *
222
     * @return boolean
223
     */
224 95
    public function isBaseOf(Url $targetUri)
225
    {
226 95
        if ($this->_parts['scheme'] !== $targetUri->getScheme()
227 95
            || $this->_parts['host'] !== $targetUri->getHost()
228 95
            || $this->getPort() !== $targetUri->getPort()
229
        ) {
230 1
                return false;
231
        }
232
233 95
        $srcSegmentCount = count($this->_segments);
234 95
        $targetSegments = $targetUri->getSegments();
235 95
        $targetSegmentCount = count($targetSegments);
236 95
        if ($srcSegmentCount > $targetSegmentCount) {
237 1
            return false;
238
        }
239
240 95
        for ($i = 0; $i < $srcSegmentCount; $i++) {
241 95
            if ($this->_segments[$i] !== $targetSegments[$i]) {
242 1
                return false;
243
            }
244
        }
245
246 95
        return true;
247
    }
248
249
    /**
250
     * This method verfies the client provided url query parameters and check whether
251
     * any of the odata query option specified more than once or check any of the
252
     * non-odata query parameter start will $ symbol or check any of the odata query
253
     * option specified with out value. If any of the above check fails throws
254
     * ODataException, else set _queryOptions member variable
255
     *
256
     * @return void
257
     *
258
     * @throws ODataException
259
     */
260 84
    public function validateQueryParameters()
261
    {
262 84
        $namesFound = array();
263 84
        foreach ($this->_queryOptions as $optionName => $optionValue) {
264 73
            if (empty($optionName)) {
265
                if (!empty($optionValue)) {
266
                    if (isset($optionValue[0]) && $optionValue[0] == '$') {
267
                        if ($this->_isODataQueryOption($optionValue)) {
268
                            throw ODataException::createBadRequestError(
269
                                Messages::hostODataQueryOptionFoundWithoutValue(
270
                                    $optionValue
271
                                )
272
                            );
273
                        } else {
274
                            throw ODataException::createBadRequestError(
275
                                Messages::hostNonODataOptionBeginsWithSystemCharacter(
276
                                    $optionValue
277
                                )
278
                            );
279
                        }
280
                    }
281
                }
282
            } else {
283 73
                if ($optionName[0] == '$') {
284 73
                    if (!$this->_isODataQueryOption($optionName)) {
285
                        throw ODataException::createBadRequestError(
286
                            Messages::hostNonODataOptionBeginsWithSystemCharacter(
287
                                $optionName
288
                            )
289
                        );
290
                    }
291
292 73
                    if (array_search($optionName, $namesFound) !== false) {
293
                        throw ODataException::createBadRequestError(
294
                            Messages::hostODataQueryOptionCannotBeSpecifiedMoreThanOnce(
295
                                $optionName
296
                            )
297
                        );
298
                    }
299
300 73
                    if (empty($optionValue) && $optionValue !== '0') {
301
                        throw ODataException::createBadRequestError(
302
                            Messages::hostODataQueryOptionFoundWithoutValue(
303
                                $optionName
304
                            )
305
                        );
306
                    }
307
308 73
                    $namesFound[] = $optionName;
309
                }
310
            }
311
        }
312
    }
313
314
    /**
315
     * Gets the value for the specified item in the request query string
316
     * Remark: This method assumes 'validateQueryParameters' has already been
317
     * called.
318
     *
319
     * @param string $item The query item to get the value of.
320
     *
321
     * @return string|null The value for the specified item in the request
322
     *                     query string NULL if the query option is absent.
323
     */
324 88
    public function getQueryStringItem($item)
325
    {
326 88
        if (array_key_exists($item, $this->_queryOptions)) {
327 77
            return $this->_queryOptions[$item];
328
        }
329
330 88
        return null;
331
    }
332
333
334
    /**
335
     * Verifies the given url option is a valid odata query option.
336
     *
337
     * @param string $optionName option to validate
338
     *
339
     * @return boolean True if the given option is a valid odata option False otherwise.
340
     *
341
     */
342 73
    private function _isODataQueryOption($optionName)
343
    {
344 73
        return ($optionName === ODataConstants::HTTPQUERY_STRING_FILTER ||
345 73
                $optionName === ODataConstants::HTTPQUERY_STRING_EXPAND ||
346 73
                $optionName === ODataConstants::HTTPQUERY_STRING_INLINECOUNT ||
347 73
                $optionName === ODataConstants::HTTPQUERY_STRING_ORDERBY ||
348 73
                $optionName === ODataConstants::HTTPQUERY_STRING_SELECT ||
349 73
                $optionName === ODataConstants::HTTPQUERY_STRING_SKIP ||
350 73
                $optionName === ODataConstants::HTTPQUERY_STRING_SKIPTOKEN ||
351 73
                $optionName === ODataConstants::HTTPQUERY_STRING_TOP ||
352 73
                $optionName === ODataConstants::HTTPQUERY_STRING_FORMAT);
353
    }
354
}
355