Test Failed
Push — master ( a1e735...534e7d )
by Bálint
13:42 queued 13s
created

Url::getSegmentCount()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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