├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── composer.json └── src ├── AbstractUrl.php ├── Components ├── AbstractArray.php ├── AbstractComponent.php ├── AbstractSegment.php ├── ComponentArrayInterface.php ├── ComponentInterface.php ├── Fragment.php ├── Host.php ├── HostInterface.php ├── Pass.php ├── Path.php ├── PathInterface.php ├── Port.php ├── Query.php ├── QueryInterface.php ├── Scheme.php ├── SegmentInterface.php └── User.php ├── Url.php ├── UrlConstants.php ├── UrlImmutable.php └── UrlInterface.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | #Changelog 2 | 3 | All Notable changes to `League\Url` will be documented in this file 4 | 5 | ## 3.3.5 - 2015-07-15 6 | 7 | ### Fixed 8 | - Bug fix path get relative [pull request #81](https://github.com/thephpleague/url/pull/81) 9 | 10 | ## 3.3.4 - 2015-07-01 11 | 12 | ### Fixed 13 | - Bug fix Query parsing [pull request #79](https://github.com/thephpleague/url/pull/79) 14 | 15 | ## 3.3.3 - 2015-06-25 16 | 17 | ### Fixed 18 | - Update `True\Punycode` requirement to 2.0.0 to allow PHP7 support 19 | 20 | ## 3.3.2 - 2015-05-13 21 | 22 | ### Fixed 23 | 24 | - Bug fix URL parsing [issue #65](https://github.com/thephpleague/url/issues/58) 25 | 26 | ## 3.3.1 - 2015-03-26 27 | 28 | ### Fixed 29 | - `League\Url\Components\Query` bug fix [issue #58](https://github.com/thephpleague/url/issues/58), improved bug fix [issue #31](https://github.com/thephpleague/url/issues/31) 30 | 31 | ## 3.3.0 - 2015-03-20 32 | 33 | ### Added 34 | - adding the `toArray` method to `League\Url\AbstractUrl` to output the URL like PHP native `parse_url` [issue #56](https://github.com/thephpleague/url/issues/56) 35 | 36 | ### Fixed 37 | - `League\Url\Components\Query` bug fix remove parameter only if the value equals `null` [issue #58](https://github.com/thephpleague/url/issues/58) 38 | 39 | ## 3.2.1 - 2014-11-27 40 | 41 | ### Added 42 | - Nothing 43 | 44 | ### Deprecated 45 | - Nothing 46 | 47 | ### Fixed 48 | - `League\Url\AbstractUrl\createFromServer` bug fix handling of `$_SERVER['HTTP_HOST']` 49 | 50 | ### Remove 51 | - Nothing 52 | 53 | ### Security 54 | - Nothing 55 | 56 | ## 3.2.0 - 2014-11-12 57 | 58 | ### Added 59 | - adding the following methods to `League\Url\AbstractUrl` 60 | - `getUserInfo` 61 | - `getAuthority` 62 | - `sameValueAs` 63 | 64 | ### Deprecated 65 | - Nothing 66 | 67 | ### Fixed 68 | - `League\Url\Components\Fragment::__toString` encoding symbols according to [RFC3986](http://tools.ietf.org/html/rfc3986#section-3.5) 69 | 70 | ### Remove 71 | - Nothing 72 | 73 | ### Security 74 | - Nothing 75 | 76 | ## 3.1.1 - 2014-09-02 77 | 78 | ### Added 79 | - Nothing 80 | 81 | ### Deprecated 82 | - Nothing 83 | 84 | ### Fixed 85 | - `parse_str` does not preserve key params 86 | 87 | ### Remove 88 | - Nothing 89 | 90 | ### Security 91 | - Nothing 92 | 93 | ## 3.1.0 - 2014-07-10 94 | 95 | ### Added 96 | - Adding IDN support using `True\Punycode` package 97 | - The library now **requires** the `mbstring` extension to work. 98 | 99 | The following methods were added: 100 | 101 | - `League\Url\Components\Host::toAscii` 102 | - `League\Url\Components\Host::toUnicode` as an alias of `League\Url\Components\Host::__toString` 103 | 104 | ### Deprecated 105 | - Nothing 106 | 107 | ### Fixed 108 | - invalid URI parsing 109 | 110 | ### Remove 111 | - Nothing 112 | 113 | ### Security 114 | - Nothing 115 | 116 | ## 3.0.1 - 2014-06-31 117 | 118 | ### Added 119 | - Nothing 120 | 121 | ### Deprecated 122 | - Nothing 123 | 124 | ### Fixed 125 | - invalid URI parsing 126 | 127 | ### Remove 128 | - Nothing 129 | 130 | ### Security 131 | - Nothing 132 | 133 | ## 3.0 - 2014-06-25 134 | 135 | New Release, complete rewrite from `Bakame\Url` 136 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | We accept contributions via Pull Requests on [Github](https://github.com/nyamsprod/League.url). 6 | 7 | 8 | ## Pull Requests 9 | 10 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer). 11 | 12 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 13 | 14 | - **Document any change in behaviour** - Make sure the README and any other relevant documentation are kept up-to-date. 15 | 16 | - **Consider our release cycle** - We try to follow semver. Randomly breaking public APIs is not an option. 17 | 18 | - **Create topic branches** - Don't ask us to pull from your master branch. 19 | 20 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 21 | 22 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before submitting. 23 | 24 | 25 | ## Running Tests 26 | 27 | ``` bash 28 | $ phpunit 29 | ``` 30 | 31 | 32 | **Happy coding**! 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2014 ignace nyamagana butera 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Url version 3 2 | 3 | # League\Url 3 is EOL since 2015-09-23 4 | 5 | **This repository is for the legacy Url version 3, [the new version](https://github.com/thephpleague/uri) is available at https://github.com/thephpleague/uri.** 6 | 7 | 8 | [![Build Status](https://img.shields.io/travis/thephpleague/url/3.x.svg?style=flat-square)](https://travis-ci.org/thephpleague/url) 9 | [![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/thephpleague/url/3.x.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/url/?branch=3.x) 10 | [![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/url/3.x.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/url/?branch=3.x) 11 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) 12 | [![Latest Version](https://img.shields.io/github/release/thephpleague/url.svg?style=flat-square)](https://github.com/thephpleague/url/releases) 13 | [![Total Downloads](https://img.shields.io/packagist/dt/league/url.svg?style=flat-square)](https://packagist.org/packages/league/url) 14 | 15 | Url is a simple library to ease creating and managing Urls in PHP. 16 | 17 | This package is compliant with [PSR-2][], and [PSR-4][]. 18 | 19 | [PSR-4]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md 20 | [PSR-2]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md 21 | 22 | Requirements 23 | ------- 24 | 25 | You need **PHP >= 5.3.0** and the `mbstring` extension to use the library, but the latest stable version of PHP is recommended. 26 | 27 | Install 28 | ------- 29 | 30 | Install `Url` using Composer. 31 | 32 | ``` 33 | composer require league/url 34 | ``` 35 | 36 | This will edit (or create) your `composer.json` file and automatically choose the most recent version in the 3.x serie. 37 | 38 | #### Going Solo 39 | 40 | You can also use `League\Url` without using Composer by [downloading the library](https://github.com/thephpleague/url/releases) and using a [PSR-4](http://www.php-fig.org/psr/psr-4/) compatible autoloader. 41 | 42 | Documentation 43 | ------- 44 | 45 | `League\Url` is [fully documented](http://url.thephpleague.com). Contribute to this documentation in the [gh-pages branch](https://github.com/thephpleague/url/tree/gh-pages). 46 | 47 | Testing 48 | ------- 49 | 50 | ``` bash 51 | $ phpunit 52 | ``` 53 | 54 | Contributing 55 | ------- 56 | 57 | Please see [CONTRIBUTING](CONTRIBUTING.md) for details. 58 | 59 | Credits 60 | ------- 61 | 62 | - [ignace nyamagana butera](https://github.com/nyamsprod) 63 | - [All Contributors](graphs/contributors) 64 | 65 | License 66 | ------- 67 | 68 | The MIT License (MIT). Please see [License File](LICENSE) for more information. 69 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "league/url", 3 | "type": "library", 4 | "description" : "League/url is a lightweight PHP Url manipulating library", 5 | "keywords": ["php", "url", "parse_url"], 6 | "license": "MIT", 7 | "homepage" : "http://url.thephpleague.com", 8 | "authors": [ 9 | { 10 | "name" : "Ignace Nyamagana Butera", 11 | "email" : "nyamsprod@gmail.com", 12 | "homepage" : "https://github.com/nyamsprod/", 13 | "role" : "Developer" 14 | } 15 | ], 16 | "support": { 17 | "forum": "https://groups.google.com/forum/#!forum/thephpleague", 18 | "issues": "https://github.com/thephpleague/url/issues" 19 | }, 20 | "require": { 21 | "php" : ">=5.3.0", 22 | "ext-mbstring" : "*", 23 | "true/punycode": "^2.0" 24 | }, 25 | "require-dev": { 26 | "phpunit/phpunit" : "^4.0" 27 | }, 28 | "autoload": { 29 | "psr-4": { 30 | "League\\Url\\": "src" 31 | } 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "League\\Url\\Test\\": "tests" 36 | } 37 | }, 38 | "extra": { 39 | "branch-alias": { 40 | "dev-master": "3.3-dev" 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/AbstractUrl.php: -------------------------------------------------------------------------------- 1 | getBaseUrl().$this->getRelativeUrl(); 94 | if ('/' == $url) { 95 | return ''; 96 | } 97 | 98 | return $url; 99 | } 100 | 101 | /** 102 | * {@inheritdoc} 103 | */ 104 | public function getRelativeUrl(UrlInterface $ref_url = null) 105 | { 106 | if (is_null($ref_url)) { 107 | return $this->path->getUriComponent() 108 | .$this->query->getUriComponent() 109 | .$this->fragment->getUriComponent(); 110 | } 111 | 112 | if ($this->getBaseUrl() != $ref_url->getBaseUrl()) { 113 | return $this->__toString(); 114 | } 115 | 116 | return $this->path->getRelativePath($ref_url->getPath()) 117 | .$this->query->getUriComponent() 118 | .$this->fragment->getUriComponent(); 119 | } 120 | 121 | /** 122 | * {@inheritdoc} 123 | */ 124 | public function getUserInfo() 125 | { 126 | $user = $this->user->getUriComponent().$this->pass->getUriComponent(); 127 | if ('' != $user) { 128 | $user .= '@'; 129 | } 130 | 131 | return $user; 132 | } 133 | 134 | /** 135 | * {@inheritdoc} 136 | */ 137 | public function getAuthority() 138 | { 139 | $user = $this->getUserInfo(); 140 | $host = $this->host->getUriComponent(); 141 | $port = $this->port->getUriComponent(); 142 | 143 | return $user.$host.$port; 144 | } 145 | 146 | /** 147 | * {@inheritdoc} 148 | */ 149 | public function getBaseUrl() 150 | { 151 | $scheme = $this->scheme->getUriComponent(); 152 | $auth = $this->getAuthority(); 153 | if ('' != $auth && '' == $scheme) { 154 | $scheme = '//'; 155 | } 156 | 157 | return $scheme.$auth; 158 | } 159 | 160 | /** 161 | * {@inheritdoc} 162 | */ 163 | public function sameValueAs(UrlInterface $url) 164 | { 165 | return $this->__toString() == $url->__toString(); 166 | } 167 | 168 | /** 169 | * Retuns a array representation like parse_url 170 | * But includes all components 171 | * 172 | * @return array 173 | */ 174 | public function toArray() 175 | { 176 | return array( 177 | 'scheme' => $this->scheme->get(), 178 | 'user' => $this->user->get(), 179 | 'pass' => $this->pass->get(), 180 | 'host' => $this->host->get(), 181 | 'port' => $this->port->get(), 182 | 'path' => $this->path->get(), 183 | 'query' => $this->query->get(), 184 | 'fragment' => $this->fragment->get(), 185 | ); 186 | } 187 | 188 | /** 189 | * Return a instance of Url from a string 190 | * 191 | * @param string $url a string or an object that implement the __toString method 192 | * 193 | * @return static 194 | * 195 | * @throws RuntimeException If the URL can not be parse 196 | */ 197 | public static function createFromUrl($url) 198 | { 199 | $url = (string) $url; 200 | $url = trim($url); 201 | $original_url = $url; 202 | $url = self::sanitizeUrl($url); 203 | 204 | //if no valid scheme is found we add one 205 | if (is_null($url)) { 206 | throw new RuntimeException(sprintf('The given URL: `%s` could not be parse', $original_url)); 207 | } 208 | 209 | $components = array_merge(array( 210 | 'scheme' => null, 211 | 'user' => null, 212 | 'pass' => null, 213 | 'host' => null, 214 | 'port' => null, 215 | 'path' => null, 216 | 'query' => null, 217 | 'fragment' => null, 218 | ), self::parseUrl($url)); 219 | $components = self::formatAuthComponent($components); 220 | $components = self::formatPathComponent($components, $original_url); 221 | 222 | return new static( 223 | new Components\Scheme($components['scheme']), 224 | new Components\User($components['user']), 225 | new Components\Pass($components['pass']), 226 | new Components\Host($components['host']), 227 | new Components\Port($components['port']), 228 | new Components\Path($components['path']), 229 | new Components\Query($components['query']), 230 | new Components\Fragment($components['fragment']) 231 | ); 232 | } 233 | 234 | /** 235 | * Parse a string as an URL 236 | * 237 | * @param string $url The URL to parse 238 | * 239 | * @throws InvalidArgumentException if the URL can not be parsed 240 | * 241 | * @return array 242 | */ 243 | protected static function parseUrl($url) 244 | { 245 | $components = @parse_url($url); 246 | if (! empty($components)) { 247 | return $components; 248 | } 249 | 250 | if (is_null(static::$is_parse_url_bugged)) { 251 | static::$is_parse_url_bugged = ! is_array(@parse_url("//example.org:80")); 252 | } 253 | 254 | //bugfix for https://bugs.php.net/bug.php?id=68917 255 | if (static::$is_parse_url_bugged && 256 | strpos($url, '/') === 0 && 257 | is_array($components = @parse_url('http:'.$url)) 258 | ) { 259 | unset($components['scheme']); 260 | return $components; 261 | } 262 | throw new RuntimeException(sprintf("The given URL: `%s` could not be parse", $url)); 263 | } 264 | 265 | protected static function sanitizeUrl($url) 266 | { 267 | if ('' == $url || strpos($url, '//') === 0) { 268 | return $url; 269 | } 270 | 271 | if (! preg_match(',^((http|ftp|ws)s?:),i', $url, $matches)) { 272 | return '//'.$url; 273 | } 274 | 275 | $scheme_length = strlen($matches[0]); 276 | if (strpos(substr($url, $scheme_length), '//') === 0) { 277 | return $url; 278 | } 279 | 280 | return null; 281 | } 282 | 283 | /** 284 | * Return a instance of Url from a server array 285 | * 286 | * @param array $server the server array 287 | * 288 | * @return static 289 | * 290 | * @throws RuntimeException If the URL can not be parse 291 | */ 292 | public static function createFromServer(array $server) 293 | { 294 | $scheme = self::fetchServerScheme($server); 295 | $host = self::fetchServerHost($server); 296 | $port = self::fetchServerPort($server); 297 | $request = self::fetchServerRequestUri($server); 298 | 299 | return self::createFromUrl($scheme.$host.$port.$request); 300 | } 301 | 302 | /** 303 | * Return the Server URL scheme component 304 | * 305 | * @param array $server the server array 306 | * 307 | * @return string 308 | */ 309 | protected static function fetchServerScheme(array $server) 310 | { 311 | $scheme = ''; 312 | if (isset($server['SERVER_PROTOCOL'])) { 313 | $scheme = explode('/', $server['SERVER_PROTOCOL']); 314 | $scheme = strtolower($scheme[0]); 315 | if (isset($server['HTTPS']) && 'off' != $server['HTTPS']) { 316 | $scheme .= 's'; 317 | } 318 | $scheme .= ':'; 319 | } 320 | 321 | return $scheme.'//'; 322 | } 323 | 324 | /** 325 | * Return the Server URL host component 326 | * 327 | * @param array $server the server array 328 | * 329 | * @return string 330 | * 331 | * @throws \RuntimeException If no host is detected 332 | */ 333 | protected static function fetchServerHost(array $server) 334 | { 335 | if (isset($server['HTTP_HOST'])) { 336 | $header = $server['HTTP_HOST']; 337 | if (! preg_match('/(:\d+)$/', $header, $matches)) { 338 | return $header; 339 | } 340 | 341 | return substr($header, 0, -strlen($matches[1])); 342 | } 343 | 344 | if (isset($server['SERVER_ADDR'])) { 345 | return $server['SERVER_ADDR']; 346 | } 347 | 348 | throw new RuntimeException('Host could not be detected'); 349 | } 350 | 351 | /** 352 | * Return the Server URL port component 353 | * 354 | * @param array $server the server array 355 | * 356 | * @return string 357 | */ 358 | protected static function fetchServerPort(array $server) 359 | { 360 | $port = ''; 361 | if (isset($server['SERVER_PORT']) && '80' != $server['SERVER_PORT']) { 362 | $port = ':'. (int) $server['SERVER_PORT']; 363 | } 364 | 365 | return $port; 366 | } 367 | 368 | /** 369 | * Return the Server URL Request Uri component 370 | * 371 | * @param array $server the server array 372 | * 373 | * @return string 374 | */ 375 | protected static function fetchServerRequestUri(array $server) 376 | { 377 | if (isset($server['REQUEST_URI'])) { 378 | return $server['REQUEST_URI']; 379 | } 380 | 381 | if (isset($server['PHP_SELF'])) { 382 | return $server['PHP_SELF']; 383 | } 384 | 385 | return '/'; 386 | } 387 | 388 | /** 389 | * Reformat the component according to the auth content 390 | * 391 | * @param array $components the result from parse_url 392 | * 393 | * @return array 394 | */ 395 | protected static function formatAuthComponent(array $components) 396 | { 397 | if (!is_null($components['scheme']) 398 | && is_null($components['host']) 399 | && !empty($components['path']) 400 | && strpos($components['path'], '@') !== false 401 | ) { 402 | $tmp = explode('@', $components['path'], 2); 403 | $components['user'] = $components['scheme']; 404 | $components['pass'] = $tmp[0]; 405 | $components['path'] = $tmp[1]; 406 | $components['scheme'] = null; 407 | } 408 | 409 | return $components; 410 | } 411 | 412 | /** 413 | * Reformat the component according to the host content 414 | * 415 | * @param array $components the result from parse_url 416 | * 417 | * @return array 418 | */ 419 | protected static function formatHostComponent(array $components) 420 | { 421 | if (strpos($components['host'], '@')) { 422 | list($auth, $components['host']) = explode('@', $components['host']); 423 | $components['user'] = $auth; 424 | $components['pass'] = null; 425 | if (false !== strpos($auth, ':')) { 426 | list($components['user'], $components['pass']) = explode(':', $auth); 427 | } 428 | } 429 | 430 | return $components; 431 | } 432 | 433 | /** 434 | * Reformat the component according to the path content 435 | * 436 | * @param array $components the result from parse_url 437 | * @param string $url the original URL to be parse 438 | * 439 | * @return array 440 | */ 441 | protected static function formatPathComponent(array $components, $url) 442 | { 443 | if (is_null($components['scheme']) 444 | && is_null($components['host']) 445 | && !empty($components['path']) 446 | ) { 447 | if (0 === strpos($components['path'], '///')) { 448 | //even with the added scheme the URL is still broken 449 | throw new RuntimeException(sprintf('The given URL: `%s` could not be parse', $url)); 450 | } 451 | 452 | if (0 === strpos($components['path'], '//')) { 453 | $tmp = substr($components['path'], 2); 454 | $components['path'] = null; 455 | $res = explode('/', $tmp, 2); 456 | $components['host'] = $res[0]; 457 | if (isset($res[1])) { 458 | $components['path'] = $res[1]; 459 | } 460 | $components = self::formatHostComponent($components); 461 | } 462 | } 463 | 464 | return $components; 465 | } 466 | } 467 | -------------------------------------------------------------------------------- /src/Components/AbstractArray.php: -------------------------------------------------------------------------------- 1 | data; 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | */ 48 | public function keys() 49 | { 50 | $args = func_get_args(); 51 | if (! $args) { 52 | return array_keys($this->data); 53 | } 54 | 55 | return array_keys($this->data, $args[0], true); 56 | } 57 | 58 | /** 59 | * IteratorAggregate Interface method 60 | * 61 | * @return ArrayIterator 62 | */ 63 | public function getIterator() 64 | { 65 | return new ArrayIterator($this->data); 66 | } 67 | 68 | /** 69 | * Countable Interface method 70 | * 71 | * @param int $mode 72 | * 73 | * @return integer 74 | */ 75 | public function count($mode = COUNT_NORMAL) 76 | { 77 | return count($this->data, $mode); 78 | } 79 | 80 | /** 81 | * ArrayAccess Interface method 82 | * 83 | * @param int|string $offset 84 | * 85 | * @return bool 86 | */ 87 | public function offsetExists($offset) 88 | { 89 | return isset($this->data[$offset]); 90 | } 91 | 92 | /** 93 | * ArrayAccess Interface method 94 | * 95 | * @param int|string $offset 96 | */ 97 | public function offsetUnset($offset) 98 | { 99 | unset($this->data[$offset]); 100 | } 101 | 102 | /** 103 | * ArrayAccess Interface method 104 | * 105 | * @param int|string $offset 106 | * 107 | * @return null 108 | */ 109 | public function offsetGet($offset) 110 | { 111 | if (isset($this->data[$offset])) { 112 | return $this->data[$offset]; 113 | } 114 | 115 | return null; 116 | } 117 | 118 | public static function isStringable($data) 119 | { 120 | return is_string($data) || (is_object($data)) && (method_exists($data, '__toString')); 121 | } 122 | 123 | /** 124 | * convert a given data into an array 125 | * 126 | * @param mixed $data the data to insert 127 | * @param \Closure $callback a callable function to be called to parse 128 | * a given string into the corresponding component 129 | * 130 | * @return array 131 | * 132 | * @throws \RuntimeException if the data is not valid 133 | */ 134 | protected function convertToArray($data, Closure $callback) 135 | { 136 | if (is_null($data)) { 137 | return array(); 138 | } 139 | 140 | if ($data instanceof Traversable) { 141 | return iterator_to_array($data); 142 | } 143 | 144 | if (self::isStringable($data)) { 145 | $data = (string) $data; 146 | $data = trim($data); 147 | $data = $callback($data); 148 | } 149 | 150 | if (! is_array($data)) { 151 | throw new RuntimeException('Your submitted data could not be converted into a proper array'); 152 | } 153 | 154 | return $data; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/Components/AbstractComponent.php: -------------------------------------------------------------------------------- 1 | set($data); 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function set($data) 46 | { 47 | $this->data = $this->validate($data); 48 | } 49 | 50 | /** 51 | * {@inheritdoc} 52 | */ 53 | public function get() 54 | { 55 | if (is_null($this->data) || ! $this->data) { 56 | return null; 57 | } 58 | 59 | return $this->data; 60 | } 61 | 62 | /** 63 | * {@inheritdoc} 64 | */ 65 | public function __toString() 66 | { 67 | return str_replace(null, '', $this->get()); 68 | } 69 | 70 | /** 71 | * {@inheritdoc} 72 | */ 73 | public function getUriComponent() 74 | { 75 | return $this->__toString(); 76 | } 77 | 78 | /** 79 | * Validate a component 80 | * 81 | * @param mixed $data the component value to be validate 82 | * 83 | * @return string|null 84 | * 85 | * @throws InvalidArgumentException If The data is invalid 86 | */ 87 | protected function validate($data) 88 | { 89 | return $this->sanitizeComponent($data); 90 | } 91 | 92 | /** 93 | * Sanitize a string component 94 | * 95 | * @param mixed $str 96 | * 97 | * @return string|null 98 | */ 99 | protected function sanitizeComponent($str) 100 | { 101 | if (is_null($str)) { 102 | return $str; 103 | } 104 | $str = filter_var((string) $str, FILTER_UNSAFE_RAW, array('flags' => FILTER_FLAG_STRIP_LOW)); 105 | $str = trim($str); 106 | 107 | return $str; 108 | } 109 | 110 | /** 111 | * {@inheritdoc} 112 | */ 113 | public function sameValueAs(ComponentInterface $component) 114 | { 115 | return $this->__toString() == $component->__toString(); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/Components/AbstractSegment.php: -------------------------------------------------------------------------------- 1 | set($data); 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function set($data) 46 | { 47 | $this->data = array_filter($this->validate($data), function ($value) { 48 | return ! is_null($value); 49 | }); 50 | } 51 | 52 | /** 53 | * {@inheritdoc} 54 | */ 55 | public function __toString() 56 | { 57 | return (string) $this->get(); 58 | } 59 | 60 | /** 61 | * {@inheritdoc} 62 | */ 63 | public function getUriComponent() 64 | { 65 | return $this->__toString(); 66 | } 67 | 68 | /** 69 | * {@inheritdoc} 70 | * @param string|array|\Traversable $data the data 71 | */ 72 | public function remove($data) 73 | { 74 | $data = $this->fetchRemainingSegment($this->data, $data); 75 | if (! is_null($data)) { 76 | $this->set($data); 77 | } 78 | } 79 | 80 | /** 81 | * {@inheritdoc} 82 | */ 83 | public function sameValueAs(ComponentInterface $component) 84 | { 85 | return $this->__toString() == $component->__toString(); 86 | } 87 | 88 | /** 89 | * Sanitize a string component recursively 90 | * 91 | * @param mixed $str 92 | * 93 | * @return mixed 94 | */ 95 | protected function sanitizeValue($str) 96 | { 97 | if (is_array($str)) { 98 | foreach ($str as &$value) { 99 | $value = $this->sanitizeValue($value); 100 | } 101 | unset($value); 102 | 103 | return $str; 104 | } 105 | 106 | $str = filter_var((string) $str, FILTER_UNSAFE_RAW, array('flags' => FILTER_FLAG_STRIP_LOW)); 107 | $str = trim($str); 108 | 109 | return $str; 110 | } 111 | 112 | /** 113 | * ArrayAccess Interface method 114 | * 115 | * @param int|string $offset 116 | * @param mixed $value 117 | */ 118 | public function offsetSet($offset, $value) 119 | { 120 | $data = $this->data; 121 | if (is_null($offset)) { 122 | $data[] = $value; 123 | $this->set($data); 124 | 125 | return; 126 | } 127 | $offset = filter_var($offset, FILTER_VALIDATE_INT, array('min_range' => 0)); 128 | if (false === $offset) { 129 | throw new InvalidArgumentException('Offset must be an integer'); 130 | } 131 | $data[$offset] = $value; 132 | $this->set($data); 133 | } 134 | 135 | /** 136 | * Validate a component 137 | * 138 | * @param mixed $data the component value to be validate 139 | * 140 | * @return array 141 | * 142 | * @throws \InvalidArgumentException If The data is invalid 143 | */ 144 | abstract protected function validate($data); 145 | 146 | /** 147 | * {@inheritdoc} 148 | * @param string|array|\Traversable $data 149 | * @param string $whence 150 | * @param integer $whence_index 151 | */ 152 | public function append($data, $whence = null, $whence_index = null) 153 | { 154 | $this->data = $this->appendSegment( 155 | $this->data, 156 | $this->validate($data), 157 | $whence, 158 | $whence_index 159 | ); 160 | } 161 | 162 | /** 163 | * {@inheritdoc} 164 | * @param string|array|\Traversable $data 165 | * @param string $whence 166 | * @param integer $whence_index 167 | */ 168 | public function prepend($data, $whence = null, $whence_index = null) 169 | { 170 | $this->data = $this->prependSegment( 171 | $this->data, 172 | $this->validate($data), 173 | $whence, 174 | $whence_index 175 | ); 176 | } 177 | 178 | /** 179 | * Format removing component labels 180 | * 181 | * @param string|array|\Traversable $data the component value to be validate 182 | * 183 | * @return array 184 | */ 185 | protected function formatRemoveSegment($data) 186 | { 187 | return $this->sanitizeValue($this->validateSegment($data)); 188 | } 189 | 190 | /** 191 | * Validate data before insertion into a URL segment based component 192 | * 193 | * @param mixed $data the data to insert 194 | * 195 | * @return array 196 | * 197 | * @throws \RuntimeException if the data is not valid 198 | */ 199 | protected function validateSegment($data) 200 | { 201 | $delimiter = $this->delimiter; 202 | 203 | return $this->convertToArray($data, function ($str) use ($delimiter) { 204 | if ('' == $str) { 205 | return array(); 206 | } 207 | if ($delimiter == $str[0]) { 208 | $str = substr($str, 1); 209 | } 210 | 211 | return explode($delimiter, $str); 212 | }); 213 | } 214 | 215 | /** 216 | * Append some data to a given array 217 | * 218 | * @param array $left the original array 219 | * @param array $value the data to prepend 220 | * @param string $whence the value of the data to prepend before 221 | * @param integer $whence_index the occurrence index for $whence 222 | * 223 | * @return array 224 | */ 225 | protected function appendSegment(array $left, array $value, $whence = null, $whence_index = null) 226 | { 227 | $right = array(); 228 | if (null !== $whence && count($found = array_keys($left, $whence))) { 229 | array_reverse($found); 230 | $index = $found[0]; 231 | if (array_key_exists($whence_index, $found)) { 232 | $index = $found[$whence_index]; 233 | } 234 | $right = array_slice($left, $index+1); 235 | $left = array_slice($left, 0, $index+1); 236 | } 237 | 238 | return array_merge($left, $value, $right); 239 | } 240 | 241 | /** 242 | * Prepend some data to a given array 243 | * 244 | * @param array $right the original array 245 | * @param array $value the data to prepend 246 | * @param string $whence the value of the data to prepend before 247 | * @param integer $whence_index the occurrence index for $whence 248 | * 249 | * @return array 250 | */ 251 | protected function prependSegment(array $right, array $value, $whence = null, $whence_index = null) 252 | { 253 | $left = array(); 254 | if (null !== $whence && count($found = array_keys($right, $whence))) { 255 | $index = $found[0]; 256 | if (array_key_exists($whence_index, $found)) { 257 | $index = $found[$whence_index]; 258 | } 259 | $left = array_slice($right, 0, $index); 260 | $right = array_slice($right, $index); 261 | } 262 | 263 | return array_merge($left, $value, $right); 264 | } 265 | 266 | /** 267 | * Remove some data from a given array 268 | * 269 | * @param array $data the original array 270 | * @param mixed $value the data to be removed (can be an array or a single segment) 271 | * 272 | * @return string|null 273 | * 274 | * @throws \RuntimeException If $value is invalid 275 | */ 276 | protected function fetchRemainingSegment(array $data, $value) 277 | { 278 | $segment = implode($this->delimiter, $data); 279 | if ('' == $value) { 280 | if ($index = array_search('', $data, true)) { 281 | $left = array_slice($data, 0, $index); 282 | $right = array_slice($data, $index+1); 283 | 284 | return implode($this->delimiter, array_merge($left, $right)); 285 | } 286 | 287 | return $segment; 288 | } 289 | 290 | $part = implode($this->delimiter, $this->formatRemoveSegment($value)); 291 | 292 | $regexStart = '@(:?^|\\'.$this->delimiter.')'; 293 | 294 | if (! preg_match($regexStart.preg_quote($part, '@').'@', $segment, $matches, PREG_OFFSET_CAPTURE)) { 295 | return null; 296 | } 297 | 298 | $pos = $matches[0][1]; 299 | 300 | return substr($segment, 0, $pos).substr($segment, $pos + strlen($part) + 1); 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /src/Components/ComponentArrayInterface.php: -------------------------------------------------------------------------------- 1 | __toString(); 54 | if ('' != $value) { 55 | $value = '#'.$value; 56 | } 57 | 58 | return $value; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Components/Host.php: -------------------------------------------------------------------------------- 1 | encoding = mb_internal_encoding(); 51 | if (stripos($this->encoding, 'utf-8') === false) { 52 | mb_internal_encoding('UTF-8'); 53 | } 54 | } 55 | 56 | /** 57 | * Restore the Environment Internal Encoding 58 | * 59 | * @return void 60 | */ 61 | protected function restoreInternalEncoding() 62 | { 63 | mb_internal_encoding($this->encoding); 64 | } 65 | 66 | /** 67 | * {@inheritdoc} 68 | */ 69 | public function __construct($data = null) 70 | { 71 | $this->punycode = new Punycode('UTF-8'); 72 | parent::__construct($data); 73 | } 74 | 75 | /** 76 | * {@inheritdoc} 77 | */ 78 | public function get() 79 | { 80 | $res = array(); 81 | foreach (array_values($this->data) as $value) { 82 | $res[] = $this->punycode->decode($value); 83 | } 84 | if (! $res) { 85 | return null; 86 | } 87 | 88 | return implode($this->delimiter, $res); 89 | } 90 | 91 | /** 92 | * {@inheritdoc} 93 | */ 94 | public function toAscii() 95 | { 96 | $this->saveInternalEncoding(); 97 | $res = $this->punycode->encode($this->__toString()); 98 | $this->restoreInternalEncoding(); 99 | 100 | return $res; 101 | } 102 | 103 | /** 104 | * {@inheritdoc} 105 | */ 106 | public function toUnicode() 107 | { 108 | return $this->__toString(); 109 | } 110 | 111 | protected function isValidHostLength(array $data) 112 | { 113 | $res = array_filter($data, function ($label) { 114 | return mb_strlen($label) > 63; 115 | }); 116 | 117 | return 0 == count($res); 118 | } 119 | 120 | protected function isValidHostPattern(array $data) 121 | { 122 | $data = explode( 123 | $this->delimiter, 124 | $this->punycode->encode(implode($this->delimiter, $data)) 125 | ); 126 | 127 | $res = preg_grep('/^[0-9a-z]([0-9a-z-]{0,61}[0-9a-z])?$/i', $data, PREG_GREP_INVERT); 128 | 129 | return 0 == count($res); 130 | } 131 | 132 | protected function isValidHostLabels(array $data = array()) 133 | { 134 | $labels = array_merge($this->data, $data); 135 | $count_labels = count($labels); 136 | 137 | return $count_labels > 0 && $count_labels < 127 && 255 > strlen(implode($this->delimiter, $labels)); 138 | } 139 | 140 | /** 141 | * Validate Host data before insertion into a URL host component 142 | * 143 | * @param mixed $data the data to insert 144 | * 145 | * @return array 146 | * 147 | * @throws RuntimeException If the added is invalid 148 | */ 149 | protected function validate($data) 150 | { 151 | $data = $this->validateSegment($data); 152 | if (! $data) { 153 | return $data; 154 | } 155 | 156 | $this->saveInternalEncoding(); 157 | if (! $this->isValidHostLength($data)) { 158 | $this->restoreInternalEncoding(); 159 | throw new RuntimeException('Invalid hostname, check its length'); 160 | } 161 | 162 | if (! $this->isValidHostPattern($data)) { 163 | $this->restoreInternalEncoding(); 164 | throw new RuntimeException('Invalid host label, check its content'); 165 | } 166 | 167 | if (! $this->isValidHostLabels($data)) { 168 | $this->restoreInternalEncoding(); 169 | throw new RuntimeException('Invalid host label counts, check its count'); 170 | } 171 | 172 | $data = $this->sanitizeValue($data); 173 | $data = explode( 174 | $this->delimiter, 175 | $this->punycode->decode(implode($this->delimiter, $data)) 176 | ); 177 | $this->restoreInternalEncoding(); 178 | 179 | return $data; 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/Components/HostInterface.php: -------------------------------------------------------------------------------- 1 | __toString(); 29 | if ('' != $value) { 30 | $value = ':'.$value; 31 | } 32 | 33 | return $value; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Components/Path.php: -------------------------------------------------------------------------------- 1 | data) as $value) { 35 | $res[] = rawurlencode($value); 36 | } 37 | if (! $res) { 38 | return null; 39 | } 40 | 41 | return implode($this->delimiter, $res); 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | */ 47 | public function getUriComponent() 48 | { 49 | return '/'.$this->__toString(); 50 | } 51 | 52 | /** 53 | * {@inheritdoc} 54 | */ 55 | public function getRelativePath(PathInterface $reference) 56 | { 57 | if ($this->sameValueAs($reference)) { 58 | return ''; 59 | } 60 | 61 | $ref_path = array_values($reference->toArray()); 62 | $this_path = array_values($this->data); 63 | $filename = array_pop($this_path); 64 | 65 | //retrieve the final consecutive identical segment in the current path 66 | $index = 0; 67 | foreach ($ref_path as $offset => $value) { 68 | if (! isset($this_path[$offset]) || $value != $this_path[$offset]) { 69 | break; 70 | } 71 | $index++; 72 | } 73 | //deduce the number of similar segment according to the reference path 74 | $nb_common_segment = count($ref_path) - $index; 75 | $nb_segments = array(); 76 | if ($nb_common_segment) { 77 | $nb_segments = array_fill(0, $nb_common_segment, '..'); 78 | } 79 | 80 | //let's output the relative path using a new Path object 81 | $res = new Path(array_merge( 82 | $nb_segments, 83 | array_slice($this_path, $index), 84 | array($filename) 85 | )); 86 | 87 | return $res->__toString(); 88 | } 89 | 90 | /** 91 | * {@inheritdoc} 92 | */ 93 | protected function validate($data) 94 | { 95 | $data = $this->sanitizeValue($this->validateSegment($data)); 96 | 97 | return array_map('urldecode', $data); 98 | } 99 | 100 | /** 101 | * {@inheritdoc} 102 | */ 103 | protected function formatRemoveSegment($data) 104 | { 105 | return array_map('urldecode', parent::formatRemoveSegment($data)); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Components/PathInterface.php: -------------------------------------------------------------------------------- 1 | array('min_range' => 1), 37 | )); 38 | 39 | if (! $data) { 40 | throw new RuntimeException('A port must be a valid positive integer'); 41 | } 42 | 43 | return (int) $data; 44 | } 45 | 46 | /** 47 | * {@inheritdoc} 48 | */ 49 | public function getUriComponent() 50 | { 51 | $value = $this->__toString(); 52 | if ('' != $value) { 53 | $value = ':'.$value; 54 | } 55 | 56 | return $value; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Components/Query.php: -------------------------------------------------------------------------------- 1 | set($data); 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | public function set($data) 41 | { 42 | $this->data = array_filter($this->validate($data), function ($value) { 43 | if (is_string($value)) { 44 | $value = trim($value); 45 | } 46 | 47 | return null !== $value; 48 | }); 49 | } 50 | 51 | /** 52 | * {@inheritdoc} 53 | */ 54 | public function get() 55 | { 56 | if (!$this->data) { 57 | return null; 58 | } 59 | 60 | return str_replace( 61 | array('+'), 62 | array('%20'), 63 | http_build_query($this->data, '', '&') 64 | ); 65 | } 66 | 67 | /** 68 | * {@inheritdoc} 69 | */ 70 | public function __toString() 71 | { 72 | return (string) $this->get(); 73 | } 74 | 75 | /** 76 | * {@inheritdoc} 77 | */ 78 | public function sameValueAs(ComponentInterface $component) 79 | { 80 | return $this->__toString() == $component->__toString(); 81 | } 82 | 83 | /** 84 | * {@inheritdoc} 85 | */ 86 | public function getUriComponent() 87 | { 88 | $value = $this->__toString(); 89 | if ('' != $value) { 90 | $value = '?'.$value; 91 | } 92 | 93 | return $value; 94 | } 95 | 96 | /** 97 | * {@inheritdoc} 98 | */ 99 | public function modify($data) 100 | { 101 | $this->set(array_merge($this->data, $this->validate($data))); 102 | } 103 | 104 | /** 105 | * {@inheritdoc} 106 | */ 107 | protected function validate($data) 108 | { 109 | return $this->convertToArray($data, function ($str) { 110 | if ('' == $str) { 111 | return array(); 112 | } 113 | if ('?' == $str[0]) { 114 | $str = substr($str, 1); 115 | } 116 | 117 | //let's preserve the key params 118 | $str = preg_replace_callback('/(?:^|(?<=&))[^=|&[]+/', function ($match) { 119 | return bin2hex(urldecode($match[0])); 120 | }, $str); 121 | parse_str($str, $arr); 122 | 123 | //hexbin does not work in PHP 5.3 124 | $arr = array_combine(array_map(function ($value) { 125 | return pack('H*', $value); 126 | 127 | }, array_keys($arr)), $arr); 128 | 129 | return $arr; 130 | }); 131 | } 132 | 133 | /** 134 | * {@inheritdoc} 135 | */ 136 | public function offsetSet($offset, $value) 137 | { 138 | if (is_null($offset)) { 139 | throw new RuntimeException('offset can not be null'); 140 | } 141 | $this->modify(array($offset => $value)); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/Components/QueryInterface.php: -------------------------------------------------------------------------------- 1 | array('regexp' => '/^'.UrlConstants::SCHEME_REGEXP.'$/i'), 38 | )); 39 | 40 | if (! $data) { 41 | throw new RuntimeException('This class only deals with http URL'); 42 | } 43 | 44 | return strtolower($data); 45 | } 46 | 47 | /** 48 | * {@inheritdoc} 49 | */ 50 | public function getUriComponent() 51 | { 52 | $value = $this->__toString(); 53 | if ('' != $value) { 54 | $value .= '://'; 55 | } 56 | 57 | return $value; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Components/SegmentInterface.php: -------------------------------------------------------------------------------- 1 | scheme = $scheme; 45 | $this->user = $user; 46 | $this->pass = $pass; 47 | $this->host = $host; 48 | $this->port = $port; 49 | $this->path = $path; 50 | $this->query = $query; 51 | $this->fragment = $fragment; 52 | } 53 | 54 | /** 55 | * {@inheritdoc} 56 | */ 57 | public function setScheme($data) 58 | { 59 | $this->scheme->set($data); 60 | 61 | return $this; 62 | } 63 | 64 | /** 65 | * {@inheritdoc} 66 | */ 67 | public function getScheme() 68 | { 69 | return $this->scheme; 70 | } 71 | 72 | /** 73 | * {@inheritdoc} 74 | */ 75 | public function setUser($data) 76 | { 77 | $this->user->set($data); 78 | 79 | return $this; 80 | } 81 | 82 | /** 83 | * {@inheritdoc} 84 | */ 85 | public function getUser() 86 | { 87 | return $this->user; 88 | } 89 | 90 | /** 91 | * {@inheritdoc} 92 | */ 93 | public function setPass($data) 94 | { 95 | $this->pass->set($data); 96 | 97 | return $this; 98 | } 99 | 100 | /** 101 | * {@inheritdoc} 102 | */ 103 | public function getPass() 104 | { 105 | return $this->pass; 106 | } 107 | 108 | /** 109 | * {@inheritdoc} 110 | */ 111 | public function setHost($data) 112 | { 113 | $this->host->set($data); 114 | 115 | return $this; 116 | } 117 | 118 | /** 119 | * {@inheritdoc} 120 | */ 121 | public function getHost() 122 | { 123 | return $this->host; 124 | } 125 | 126 | /** 127 | * {@inheritdoc} 128 | */ 129 | public function setPort($data) 130 | { 131 | $this->port->set($data); 132 | 133 | return $this; 134 | } 135 | 136 | /** 137 | * {@inheritdoc} 138 | */ 139 | public function getPort() 140 | { 141 | return $this->port; 142 | } 143 | 144 | /** 145 | * {@inheritdoc} 146 | */ 147 | public function setPath($data) 148 | { 149 | $this->path->set($data); 150 | 151 | return $this; 152 | } 153 | 154 | /** 155 | * {@inheritdoc} 156 | */ 157 | public function getPath() 158 | { 159 | return $this->path; 160 | } 161 | 162 | /** 163 | * {@inheritdoc} 164 | */ 165 | public function setQuery($data) 166 | { 167 | $this->query->set($data); 168 | 169 | return $this; 170 | } 171 | 172 | /** 173 | * {@inheritdoc} 174 | */ 175 | public function getQuery() 176 | { 177 | return $this->query; 178 | } 179 | 180 | /** 181 | * {@inheritdoc} 182 | */ 183 | public function setFragment($data) 184 | { 185 | $this->fragment->set($data); 186 | 187 | return $this; 188 | } 189 | 190 | /** 191 | * {@inheritdoc} 192 | */ 193 | public function getFragment() 194 | { 195 | return $this->fragment; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/UrlConstants.php: -------------------------------------------------------------------------------- 1 | scheme = clone $scheme; 45 | $this->user = clone $user; 46 | $this->pass = clone $pass; 47 | $this->host = clone $host; 48 | $this->port = clone $port; 49 | $this->path = clone $path; 50 | $this->query = clone $query; 51 | $this->fragment = clone $fragment; 52 | } 53 | 54 | /** 55 | * To Enable cloning 56 | */ 57 | public function __clone() 58 | { 59 | $this->scheme = clone $this->scheme; 60 | $this->user = clone $this->user; 61 | $this->pass = clone $this->pass; 62 | $this->host = clone $this->host; 63 | $this->port = clone $this->port; 64 | $this->path = clone $this->path; 65 | $this->query = clone $this->query; 66 | $this->fragment = clone $this->fragment; 67 | } 68 | 69 | /** 70 | * {@inheritdoc} 71 | */ 72 | public function setScheme($data) 73 | { 74 | $clone = clone $this; 75 | $clone->scheme->set($data); 76 | 77 | return $clone; 78 | } 79 | 80 | /** 81 | * {@inheritdoc} 82 | */ 83 | public function getScheme() 84 | { 85 | return clone $this->scheme; 86 | } 87 | 88 | /** 89 | * {@inheritdoc} 90 | */ 91 | public function setUser($data) 92 | { 93 | $clone = clone $this; 94 | $clone->user->set($data); 95 | 96 | return $clone; 97 | } 98 | 99 | /** 100 | * {@inheritdoc} 101 | */ 102 | public function getUser() 103 | { 104 | return clone $this->user; 105 | } 106 | 107 | /** 108 | * {@inheritdoc} 109 | */ 110 | public function setPass($data) 111 | { 112 | $clone = clone $this; 113 | $clone->pass->set($data); 114 | 115 | return $clone; 116 | } 117 | 118 | /** 119 | * {@inheritdoc} 120 | */ 121 | public function getPass() 122 | { 123 | return clone $this->pass; 124 | } 125 | 126 | /** 127 | * {@inheritdoc} 128 | */ 129 | public function setHost($data) 130 | { 131 | $clone = clone $this; 132 | $clone->host->set($data); 133 | 134 | return $clone; 135 | } 136 | 137 | /** 138 | * {@inheritdoc} 139 | */ 140 | public function getHost() 141 | { 142 | return clone $this->host; 143 | } 144 | 145 | /** 146 | * {@inheritdoc} 147 | */ 148 | public function setPort($data) 149 | { 150 | $clone = clone $this; 151 | $clone->port->set($data); 152 | 153 | return $clone; 154 | } 155 | 156 | /** 157 | * {@inheritdoc} 158 | */ 159 | public function getPort() 160 | { 161 | return clone $this->port; 162 | } 163 | 164 | /** 165 | * {@inheritdoc} 166 | */ 167 | public function setPath($data) 168 | { 169 | $clone = clone $this; 170 | $clone->path->set($data); 171 | 172 | return $clone; 173 | } 174 | 175 | /** 176 | * {@inheritdoc} 177 | */ 178 | public function getPath() 179 | { 180 | return clone $this->path; 181 | } 182 | 183 | /** 184 | * {@inheritdoc} 185 | */ 186 | public function setQuery($data) 187 | { 188 | $clone = clone $this; 189 | $clone->query->set($data); 190 | 191 | return $clone; 192 | } 193 | 194 | /** 195 | * {@inheritdoc} 196 | */ 197 | public function getQuery() 198 | { 199 | return clone $this->query; 200 | } 201 | 202 | /** 203 | * {@inheritdoc} 204 | */ 205 | public function setFragment($data) 206 | { 207 | $clone = clone $this; 208 | $clone->fragment->set($data); 209 | 210 | return $clone; 211 | } 212 | 213 | /** 214 | * {@inheritdoc} 215 | */ 216 | public function getFragment() 217 | { 218 | return clone $this->fragment; 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/UrlInterface.php: -------------------------------------------------------------------------------- 1 |