Subversion Repositories php-qbpwcf

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 liveuser 1
<?php
2
 
3
namespace Guzzle\Http;
4
 
5
use Guzzle\Common\Exception\InvalidArgumentException;
6
 
7
/**
8
 * Parses and generates URLs based on URL parts. In favor of performance, URL parts are not validated.
9
 */
10
class Url
11
{
12
    protected $scheme;
13
    protected $host;
14
    protected $port;
15
    protected $username;
16
    protected $password;
17
    protected $path = '';
18
    protected $fragment;
19
 
20
    /** @var QueryString Query part of the URL */
21
    protected $query;
22
 
23
    /**
24
     * Factory method to create a new URL from a URL string
25
     *
26
     * @param string $url Full URL used to create a Url object
27
     *
28
     * @return Url
29
     * @throws InvalidArgumentException
30
     */
31
    public static function factory($url)
32
    {
33
        static $defaults = array('scheme' => null, 'host' => null, 'path' => null, 'port' => null, 'query' => null,
34
            'user' => null, 'pass' => null, 'fragment' => null);
35
 
36
        if (false === ($parts = parse_url($url))) {
37
            throw new InvalidArgumentException('Was unable to parse malformed url: ' . $url);
38
        }
39
 
40
        $parts += $defaults;
41
 
42
        // Convert the query string into a QueryString object
43
        if ($parts['query'] || 0 !== strlen($parts['query'])) {
44
            $parts['query'] = QueryString::fromString($parts['query']);
45
        }
46
 
47
        return new static($parts['scheme'], $parts['host'], $parts['user'],
48
            $parts['pass'], $parts['port'], $parts['path'], $parts['query'],
49
            $parts['fragment']);
50
    }
51
 
52
    /**
53
     * Build a URL from parse_url parts. The generated URL will be a relative URL if a scheme or host are not provided.
54
     *
55
     * @param array $parts Array of parse_url parts
56
     *
57
     * @return string
58
     */
59
    public static function buildUrl(array $parts)
60
    {
61
        $url = $scheme = '';
62
 
63
        if (isset($parts['scheme'])) {
64
            $scheme = $parts['scheme'];
65
            $url .= $scheme . ':';
66
        }
67
 
68
        if (isset($parts['host'])) {
69
            $url .= '//';
70
            if (isset($parts['user'])) {
71
                $url .= $parts['user'];
72
                if (isset($parts['pass'])) {
73
                    $url .= ':' . $parts['pass'];
74
                }
75
                $url .=  '@';
76
            }
77
 
78
            $url .= $parts['host'];
79
 
80
            // Only include the port if it is not the default port of the scheme
81
            if (isset($parts['port'])
82
                && !(($scheme == 'http' && $parts['port'] == 80) || ($scheme == 'https' && $parts['port'] == 443))
83
            ) {
84
                $url .= ':' . $parts['port'];
85
            }
86
        }
87
 
88
        // Add the path component if present
89
        if (isset($parts['path']) && 0 !== strlen($parts['path'])) {
90
            // Always ensure that the path begins with '/' if set and something is before the path
91
            if ($url && $parts['path'][0] != '/' && substr($url, -1)  != '/') {
92
                $url .= '/';
93
            }
94
            $url .= $parts['path'];
95
        }
96
 
97
        // Add the query string if present
98
        if (isset($parts['query'])) {
99
            $url .= '?' . $parts['query'];
100
        }
101
 
102
        // Ensure that # is only added to the url if fragment contains anything.
103
        if (isset($parts['fragment'])) {
104
            $url .= '#' . $parts['fragment'];
105
        }
106
 
107
        return $url;
108
    }
109
 
110
    /**
111
     * Create a new URL from URL parts
112
     *
113
     * @param string                   $scheme   Scheme of the URL
114
     * @param string                   $host     Host of the URL
115
     * @param string                   $username Username of the URL
116
     * @param string                   $password Password of the URL
117
     * @param int                      $port     Port of the URL
118
     * @param string                   $path     Path of the URL
119
     * @param QueryString|array|string $query    Query string of the URL
120
     * @param string                   $fragment Fragment of the URL
121
     */
122
    public function __construct($scheme, $host, $username = null, $password = null, $port = null, $path = null, QueryString $query = null, $fragment = null)
123
    {
124
        $this->scheme = $scheme;
125
        $this->host = $host;
126
        $this->port = $port;
127
        $this->username = $username;
128
        $this->password = $password;
129
        $this->fragment = $fragment;
130
        if (!$query) {
131
            $this->query = new QueryString();
132
        } else {
133
            $this->setQuery($query);
134
        }
135
        $this->setPath($path);
136
    }
137
 
138
    /**
139
     * Clone the URL
140
     */
141
    public function __clone()
142
    {
143
        $this->query = clone $this->query;
144
    }
145
 
146
    /**
147
     * Returns the URL as a URL string
148
     *
149
     * @return string
150
     */
151
    public function __toString()
152
    {
153
        return self::buildUrl($this->getParts());
154
    }
155
 
156
    /**
157
     * Get the parts of the URL as an array
158
     *
159
     * @return array
160
     */
161
    public function getParts()
162
    {
163
        $query = (string) $this->query;
164
 
165
        return array(
166
            'scheme' => $this->scheme,
167
            'user' => $this->username,
168
            'pass' => $this->password,
169
            'host' => $this->host,
170
            'port' => $this->port,
171
            'path' => $this->getPath(),
172
            'query' => $query !== '' ? $query : null,
173
            'fragment' => $this->fragment,
174
        );
175
    }
176
 
177
    /**
178
     * Set the host of the request.
179
     *
180
     * @param string $host Host to set (e.g. www.yahoo.com, yahoo.com)
181
     *
182
     * @return Url
183
     */
184
    public function setHost($host)
185
    {
186
        if (strpos($host, ':') === false) {
187
            $this->host = $host;
188
        } else {
189
            list($host, $port) = explode(':', $host);
190
            $this->host = $host;
191
            $this->setPort($port);
192
        }
193
 
194
        return $this;
195
    }
196
 
197
    /**
198
     * Get the host part of the URL
199
     *
200
     * @return string
201
     */
202
    public function getHost()
203
    {
204
        return $this->host;
205
    }
206
 
207
    /**
208
     * Set the scheme part of the URL (http, https, ftp, etc)
209
     *
210
     * @param string $scheme Scheme to set
211
     *
212
     * @return Url
213
     */
214
    public function setScheme($scheme)
215
    {
216
        if ($this->scheme == 'http' && $this->port == 80) {
217
            $this->port = null;
218
        } elseif ($this->scheme == 'https' && $this->port == 443) {
219
            $this->port = null;
220
        }
221
 
222
        $this->scheme = $scheme;
223
 
224
        return $this;
225
    }
226
 
227
    /**
228
     * Get the scheme part of the URL
229
     *
230
     * @return string
231
     */
232
    public function getScheme()
233
    {
234
        return $this->scheme;
235
    }
236
 
237
    /**
238
     * Set the port part of the URL
239
     *
240
     * @param int $port Port to set
241
     *
242
     * @return Url
243
     */
244
    public function setPort($port)
245
    {
246
        $this->port = $port;
247
 
248
        return $this;
249
    }
250
 
251
    /**
252
     * Get the port part of the URl. Will return the default port for a given scheme if no port has been set.
253
     *
254
     * @return int|null
255
     */
256
    public function getPort()
257
    {
258
        if ($this->port) {
259
            return $this->port;
260
        } elseif ($this->scheme == 'http') {
261
            return 80;
262
        } elseif ($this->scheme == 'https') {
263
            return 443;
264
        }
265
 
266
        return null;
267
    }
268
 
269
    /**
270
     * Set the path part of the URL
271
     *
272
     * @param array|string $path Path string or array of path segments
273
     *
274
     * @return Url
275
     */
276
    public function setPath($path)
277
    {
278
        static $pathReplace = array(' ' => '%20', '?' => '%3F');
279
        if (is_array($path)) {
280
            $path = '/' . implode('/', $path);
281
        }
282
 
283
        $this->path = strtr($path, $pathReplace);
284
 
285
        return $this;
286
    }
287
 
288
    /**
289
     * Normalize the URL so that double slashes and relative paths are removed
290
     *
291
     * @return Url
292
     */
293
    public function normalizePath()
294
    {
295
        if (!$this->path || $this->path == '/' || $this->path == '*') {
296
            return $this;
297
        }
298
 
299
        $results = array();
300
        $segments = $this->getPathSegments();
301
        foreach ($segments as $segment) {
302
            if ($segment == '..') {
303
                array_pop($results);
304
            } elseif ($segment != '.' && $segment != '') {
305
                $results[] = $segment;
306
            }
307
        }
308
 
309
        // Combine the normalized parts and add the leading slash if needed
310
        $this->path = ($this->path[0] == '/' ? '/' : '') . implode('/', $results);
311
 
312
        // Add the trailing slash if necessary
313
        if ($this->path != '/' && end($segments) == '') {
314
            $this->path .= '/';
315
        }
316
 
317
        return $this;
318
    }
319
 
320
    /**
321
     * Add a relative path to the currently set path.
322
     *
323
     * @param string $relativePath Relative path to add
324
     *
325
     * @return Url
326
     */
327
    public function addPath($relativePath)
328
    {
329
        if ($relativePath != '/' && is_string($relativePath) && strlen($relativePath) > 0) {
330
            // Add a leading slash if needed
331
            if ($relativePath[0] != '/') {
332
                $relativePath = '/' . $relativePath;
333
            }
334
            $this->setPath(str_replace('//', '/', $this->path . $relativePath));
335
        }
336
 
337
        return $this;
338
    }
339
 
340
    /**
341
     * Get the path part of the URL
342
     *
343
     * @return string
344
     */
345
    public function getPath()
346
    {
347
        return $this->path;
348
    }
349
 
350
    /**
351
     * Get the path segments of the URL as an array
352
     *
353
     * @return array
354
     */
355
    public function getPathSegments()
356
    {
357
        return array_slice(explode('/', $this->getPath()), 1);
358
    }
359
 
360
    /**
361
     * Set the password part of the URL
362
     *
363
     * @param string $password Password to set
364
     *
365
     * @return Url
366
     */
367
    public function setPassword($password)
368
    {
369
        $this->password = $password;
370
 
371
        return $this;
372
    }
373
 
374
    /**
375
     * Get the password part of the URL
376
     *
377
     * @return null|string
378
     */
379
    public function getPassword()
380
    {
381
        return $this->password;
382
    }
383
 
384
    /**
385
     * Set the username part of the URL
386
     *
387
     * @param string $username Username to set
388
     *
389
     * @return Url
390
     */
391
    public function setUsername($username)
392
    {
393
        $this->username = $username;
394
 
395
        return $this;
396
    }
397
 
398
    /**
399
     * Get the username part of the URl
400
     *
401
     * @return null|string
402
     */
403
    public function getUsername()
404
    {
405
        return $this->username;
406
    }
407
 
408
    /**
409
     * Get the query part of the URL as a QueryString object
410
     *
411
     * @return QueryString
412
     */
413
    public function getQuery()
414
    {
415
        return $this->query;
416
    }
417
 
418
    /**
419
     * Set the query part of the URL
420
     *
421
     * @param QueryString|string|array $query Query to set
422
     *
423
     * @return Url
424
     */
425
    public function setQuery($query)
426
    {
427
        if (is_string($query)) {
428
            $output = null;
429
            parse_str($query, $output);
430
            $this->query = new QueryString($output);
431
        } elseif (is_array($query)) {
432
            $this->query = new QueryString($query);
433
        } elseif ($query instanceof QueryString) {
434
            $this->query = $query;
435
        }
436
 
437
        return $this;
438
    }
439
 
440
    /**
441
     * Get the fragment part of the URL
442
     *
443
     * @return null|string
444
     */
445
    public function getFragment()
446
    {
447
        return $this->fragment;
448
    }
449
 
450
    /**
451
     * Set the fragment part of the URL
452
     *
453
     * @param string $fragment Fragment to set
454
     *
455
     * @return Url
456
     */
457
    public function setFragment($fragment)
458
    {
459
        $this->fragment = $fragment;
460
 
461
        return $this;
462
    }
463
 
464
    /**
465
     * Check if this is an absolute URL
466
     *
467
     * @return bool
468
     */
469
    public function isAbsolute()
470
    {
471
        return $this->scheme && $this->host;
472
    }
473
 
474
    /**
475
     * Combine the URL with another URL. Follows the rules specific in RFC 3986 section 5.4.
476
     *
477
     * @param string $url           Relative URL to combine with
478
     * @param bool   $strictRfc3986 Set to true to use strict RFC 3986 compliance when merging paths. When first
479
     *                              released, Guzzle used an incorrect algorithm for combining relative URL paths. In
480
     *                              order to not break users, we introduced this flag to allow the merging of URLs based
481
     *                              on strict RFC 3986 section 5.4.1. This means that "http://a.com/foo/baz" merged with
482
     *                              "bar" would become "http://a.com/foo/bar". When this value is set to false, it would
483
     *                              become "http://a.com/foo/baz/bar".
484
     * @return Url
485
     * @throws InvalidArgumentException
486
     * @link http://tools.ietf.org/html/rfc3986#section-5.4
487
     */
488
    public function combine($url, $strictRfc3986 = false)
489
    {
490
        $url = self::factory($url);
491
 
492
        // Use the more absolute URL as the base URL
493
        if (!$this->isAbsolute() && $url->isAbsolute()) {
494
            $url = $url->combine($this);
495
        }
496
 
497
        // Passing a URL with a scheme overrides everything
498
        if ($buffer = $url->getScheme()) {
499
            $this->scheme = $buffer;
500
            $this->host = $url->getHost();
501
            $this->port = $url->getPort();
502
            $this->username = $url->getUsername();
503
            $this->password = $url->getPassword();
504
            $this->path = $url->getPath();
505
            $this->query = $url->getQuery();
506
            $this->fragment = $url->getFragment();
507
            return $this;
508
        }
509
 
510
        // Setting a host overrides the entire rest of the URL
511
        if ($buffer = $url->getHost()) {
512
            $this->host = $buffer;
513
            $this->port = $url->getPort();
514
            $this->username = $url->getUsername();
515
            $this->password = $url->getPassword();
516
            $this->path = $url->getPath();
517
            $this->query = $url->getQuery();
518
            $this->fragment = $url->getFragment();
519
            return $this;
520
        }
521
 
522
        $path = $url->getPath();
523
        $query = $url->getQuery();
524
 
525
        if (!$path) {
526
            if (count($query)) {
527
                $this->addQuery($query, $strictRfc3986);
528
            }
529
        } else {
530
            if ($path[0] == '/') {
531
                $this->path = $path;
532
            } elseif ($strictRfc3986) {
533
                $this->path .= '/../' . $path;
534
            } else {
535
                $this->path .= '/' . $path;
536
            }
537
            $this->normalizePath();
538
            $this->addQuery($query, $strictRfc3986);
539
        }
540
 
541
        $this->fragment = $url->getFragment();
542
 
543
        return $this;
544
    }
545
 
546
    private function addQuery(QueryString $new, $strictRfc386)
547
    {
548
        if (!$strictRfc386) {
549
            $new->merge($this->query);
550
        }
551
 
552
        $this->query = $new;
553
    }
554
}