├── .gitattributes ├── hhast-lint.json ├── .gitignore ├── hh_autoload.json ├── .hhconfig ├── CODE_OF_CONDUCT.md ├── composer.json ├── .github └── workflows │ └── build-and-test.yml ├── README.md ├── LICENSE ├── CONTRIBUTING.md └── src ├── ResponseInterface.hack ├── UploadedFileInterface.hack ├── RequestInterface.hack ├── ServerRequestInterface.hack ├── MessageInterface.hack └── UriInterface.hack /.gitattributes: -------------------------------------------------------------------------------- 1 | .hhvmconfig.hdf export-ignore 2 | -------------------------------------------------------------------------------- /hhast-lint.json: -------------------------------------------------------------------------------- 1 | { 2 | "roots": [ "src/" ] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | *.swp 3 | *~ 4 | *.hhast.parser-cache 5 | composer.lock 6 | .var/ 7 | -------------------------------------------------------------------------------- /hh_autoload.json: -------------------------------------------------------------------------------- 1 | { 2 | "roots": [ 3 | "src" 4 | ], 5 | "devRoots": [], 6 | "devFailureHandler": "Facebook\\AutoloadMap\\HHClientFallbackHandler", 7 | "useFactsIfAvailable": true 8 | } 9 | -------------------------------------------------------------------------------- /.hhconfig: -------------------------------------------------------------------------------- 1 | error_php_lambdas = true 2 | disable_xhp_children_declarations = false 3 | allowed_decl_fixme_codes=2053,4045,4047 4 | allowed_fixme_codes_strict=2011,2049,2050,2053,2083,3084,4027,4045,4047,4104,4106,4107,4108,4110,4128,4135,4188,4223,4240,4323,4390 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | Facebook has adopted a Code of Conduct that we expect project participants to 4 | adhere to. Please [read the full text](https://code.facebook.com/codeofconduct) 5 | so that you can understand what actions will and will not be tolerated. 6 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "facebook/hack-http-request-response-interfaces", 3 | "description": "Common interface for HTTP messages", 4 | "homepage": "https://github.com/facebookexperimental/hack-http-request-response-interfaces", 5 | "require-dev": { 6 | "hhvm/hhast": "^4.0", 7 | "hhvm/hhvm-autoload": "^3" 8 | }, 9 | "require": { 10 | "hhvm": "^4.128" 11 | }, 12 | "config": { 13 | "allow-plugins": { 14 | "hhvm/hhvm-autoload": true 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/build-and-test.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | on: 3 | push: 4 | pull_request: 5 | schedule: 6 | - cron: '42 15 * * *' 7 | jobs: 8 | build: 9 | name: HHVM ${{matrix.hhvm}} - ${{matrix.os}} 10 | strategy: 11 | # Run tests on all OS's and HHVM versions, even if one fails 12 | fail-fast: false 13 | matrix: 14 | os: [ ubuntu ] 15 | hhvm: 16 | - "4.128" 17 | - latest 18 | - nightly 19 | runs-on: ${{matrix.os}}-latest 20 | steps: 21 | - uses: actions/checkout@v2 22 | - uses: hhvm/actions/hack-lint-test@master 23 | with: 24 | hhvm: ${{matrix.hhvm}} 25 | skip_tests: true 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Hack HTTP Request and Response Interfaces 2 | ========================================= 3 | 4 | This project aims to create standard request and response interfaces for Hack, 5 | using [PSR-7](http://www.php-fig.org/psr/psr-7/) as a starting point. 6 | 7 | This project is nearing a stable v1.0 release. 8 | 9 | ## Motivation 10 | 11 | PSR-7 was designed for PHP, not Hack, and some descisions do not fit smoothly 12 | with Hack's type system. 13 | 14 | We would like agreement on a standardized interface before releasing v1.0 of 15 | several libraries that could and should use this. 16 | 17 | Additionally, with the planned end of PHP support in HHVM, it will stop being 18 | possible to use the canonical definitions or common implementations of PSR-7 in 19 | Hack code. 20 | 21 | ## Requirements 22 | 23 | HHVM 4.41 and above. 24 | 25 | ## License 26 | 27 | By contributing to Hack HTTP Request and Response Interfaces, you agree that your contributions will be licensed 28 | under the LICENSE file in the root directory of this source tree. 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-present, Facebook, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Hack HTTP Request and Response Interfaces 2 | We want to make contributing to this project as easy and transparent as 3 | possible. 4 | 5 | ## Our Development Process 6 | 7 | All development is on GitHub. We prefer starting discussions via illustrative 8 | pull requests, however we also welcome issues. 9 | 10 | ## Pull Requests 11 | 12 | We actively welcome your pull requests. 13 | 14 | 1. Fork the repo and create your branch from `main`. 15 | 2. If you've added code that should be tested, add tests. 16 | 3. If you've changed APIs, update the documentation. 17 | 4. Ensure the test suite passes. 18 | 5. Make sure your code lints. 19 | 6. If you haven't already, complete the Contributor License Agreement ("CLA"). 20 | 21 | ## Contributor License Agreement ("CLA") 22 | In order to accept your pull request, we need you to submit a CLA. You only need 23 | to do this once to work on any of Facebook's open source projects. 24 | 25 | Complete your CLA here: 26 | 27 | ## Issues 28 | We use GitHub issues to track public bugs. Please ensure your description is 29 | clear and has sufficient instructions to be able to reproduce the issue. 30 | 31 | Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe 32 | disclosure of security bugs. In those cases, please go through the process 33 | outlined on that page and do not file a public issue. 34 | 35 | ## Coding Style 36 | 37 | - 2 spaces for indentation 38 | - match `hackfmt` where possible 39 | - pass HHAST linters. 40 | 41 | ## License 42 | By contributing to Hack HTTP Request and Response Interfaces, you agree that your contributions will be licensed 43 | under the LICENSE file in the root directory of this source tree. 44 | -------------------------------------------------------------------------------- /src/ResponseInterface.hack: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 PHP Framework Interoperability Group 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | * 22 | * Portions Copyright (c) 2018-present, Facebook, Inc. 23 | * All rights reserved. 24 | * 25 | * This source code is licensed under the MIT license found in the 26 | * LICENSE file in the root directory of this source tree. 27 | * 28 | */ 29 | 30 | namespace Facebook\Experimental\Http\Message; 31 | 32 | use namespace HH\Lib\IO; 33 | 34 | /** 35 | * Representation of an outgoing, server-side response. 36 | * 37 | * Per the HTTP specification, this interface includes properties for 38 | * each of the following: 39 | * 40 | * - Protocol version 41 | * - Status code and reason phrase 42 | * - Headers 43 | * - Message body 44 | * 45 | * Responses are considered immutable; all methods that might change state MUST 46 | * be implemented such that they retain the internal state of the current 47 | * message and return an instance that contains the changed state. 48 | */ 49 | interface ResponseInterface extends MessageInterface { 50 | /** 51 | * Gets the response status code. 52 | * 53 | * The status code is a 3-digit integer result code of the server's attempt 54 | * to understand and satisfy the request. 55 | * 56 | * @return int Status code. 57 | */ 58 | public function getStatusCode(): int; 59 | 60 | /** 61 | * Return an instance with the specified status code and, optionally, reason phrase. 62 | * 63 | * If no reason phrase is specified, implementations MAY choose to default 64 | * to the RFC 7231 or IANA recommended reason phrase for the response's 65 | * status code. 66 | * 67 | * This method MUST be implemented in such a way as to retain the 68 | * immutability of the message, and MUST return an instance that has the 69 | * updated status and reason phrase. 70 | * 71 | * @link http://tools.ietf.org/html/rfc7231#section-6 72 | * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml 73 | * @param int $code The 3-digit integer result code to set. 74 | * @param string $reasonPhrase The reason phrase to use with the 75 | * provided status code; if none is provided, implementations MAY 76 | * use the defaults as suggested in the HTTP specification. 77 | * @return static 78 | * @throws \InvalidArgumentException For invalid status code arguments. 79 | */ 80 | public function withStatus(int $code, string $reasonPhrase = ''): this; 81 | 82 | /** 83 | * Gets the response reason phrase associated with the status code. 84 | * 85 | * Because a reason phrase is not a required element in a response 86 | * status line, the reason phrase value MAY be null. Implementations MAY 87 | * choose to return the default RFC 7231 recommended reason phrase (or those 88 | * listed in the IANA HTTP Status Code Registry) for the response's 89 | * status code. 90 | * 91 | * @link http://tools.ietf.org/html/rfc7231#section-6 92 | * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml 93 | * @return string Reason phrase; must return an empty string if none present. 94 | */ 95 | public function getReasonPhrase(): string; 96 | 97 | /** 98 | * Gets the body of the message. 99 | */ 100 | public function getBody(): IO\WriteHandle; 101 | 102 | /** 103 | * Return an instance with the specified message body. 104 | * 105 | * This method MUST be implemented in such a way as to retain the 106 | * immutability of the message, and MUST return a new instance that has the 107 | * new body stream. 108 | */ 109 | public function withBody(IO\WriteHandle $body): this; 110 | } 111 | -------------------------------------------------------------------------------- /src/UploadedFileInterface.hack: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 PHP Framework Interoperability Group 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | * 22 | * Portions Copyright (c) 2018-present, Facebook, Inc. 23 | * All rights reserved. 24 | * 25 | * This source code is licensed under the MIT license found in the 26 | * LICENSE file in the root directory of this source tree. 27 | * 28 | */ 29 | 30 | namespace Facebook\Experimental\Http\Message; 31 | use namespace HH\Lib\IO; 32 | 33 | enum UploadedFileError: int { 34 | ERROR_EXCEEDS_MAX_INI_SIZE = 1; 35 | ERROR_EXCEEDS_MAX_FORM_SIZE = 2; 36 | ERROR_INCOMPLETE = 3; 37 | ERROR_NO_FILE = 4; 38 | ERROR_TMP_DIR_NOT_SPECIFIED = 6; 39 | ERROR_TMP_DIR_NOT_WRITEABLE = 7; 40 | ERROR_CANCELED_BY_EXTENSION = 8; 41 | } 42 | 43 | /** 44 | * Value object representing a file uploaded through an HTTP request. 45 | * 46 | * Instances of this interface are considered immutable; all methods that 47 | * might change state MUST be implemented such that they retain the internal 48 | * state of the current instance and return an instance that contains the 49 | * changed state. 50 | */ 51 | interface UploadedFileInterface { 52 | /** 53 | * Retrieve a stream representing the uploaded file. 54 | * 55 | * This method MUST return a StreamInterface instance, representing the 56 | * uploaded file. The purpose of this method is to allow utilizing native PHP 57 | * stream functionality to manipulate the file upload, such as 58 | * stream_copy_to_stream() (though the result will need to be decorated in a 59 | * native PHP stream wrapper to work with such functions). 60 | * 61 | * If the moveTo() method has been called previously, this method MUST raise 62 | * an exception. 63 | * 64 | * @throws \RuntimeException in cases when no stream is available or can be 65 | * created. 66 | */ 67 | public function getStream(): IO\ReadHandle; 68 | 69 | /** 70 | * Move the uploaded file to a new location. 71 | * 72 | * Use this method as an alternative to move_uploaded_file(). This method is 73 | * guaranteed to work in both SAPI and non-SAPI environments. 74 | * Implementations may determine which environment they are in, and use the 75 | * appropriate method (move_uploaded_file(), rename(), or a stream 76 | * operation) to perform the operation. Implementations are not required to 77 | * use these functions however. Implementing in pure Hack is also allowed. 78 | * 79 | * $targetPath may be an absolute path, or a relative path. If it is a 80 | * relative path, resolution should be the same as used by PHP's rename() 81 | * function. 82 | * 83 | * The original file or stream MUST be removed on completion. 84 | * 85 | * If this method is called more than once, any subsequent calls MUST raise 86 | * an exception. 87 | * 88 | * When used in an SAPI environment where $_FILES is populated, when writing 89 | * files via moveTo(), is_uploaded_file() and move_uploaded_file() SHOULD be 90 | * used to ensure permissions and upload status are verified correctly. 91 | * 92 | * If you wish to move to a stream, use getStream(), as SAPI operations 93 | * cannot guarantee writing to stream destinations. 94 | * 95 | * @see http://php.net/is_uploaded_file 96 | * @see http://php.net/move_uploaded_file 97 | * @param string $targetPath Path to which to move the uploaded file. 98 | * @throws \InvalidArgumentException if the $targetPath specified is invalid. 99 | * @throws \RuntimeException on any error during the move operation, or on 100 | * the second or subsequent call to the method. 101 | */ 102 | public function moveToAsync(string $target_path): Awaitable; 103 | 104 | /** 105 | * Retrieve the file size. 106 | * 107 | * Implementations SHOULD return the value stored in the "size" key of 108 | * the file in the $_FILES array if available, as PHP calculates this based 109 | * on the actual size transmitted. 110 | * 111 | * @return int|null The file size in bytes or null if unknown. 112 | */ 113 | public function getSize(): ?int; 114 | 115 | /** 116 | * Retrieve the error associated with the uploaded file. 117 | * 118 | * If the file was uploaded successfully, this method MUST return 119 | * null. 120 | * 121 | * @return ?UploadedFileError 122 | */ 123 | public function getError(): ?UploadedFileError; 124 | 125 | /** 126 | * Retrieve the filename sent by the client. 127 | * 128 | * Do not trust the value returned by this method. A client could send 129 | * a malicious filename with the intention to corrupt or hack your 130 | * application. 131 | * 132 | * Implementations SHOULD return the value stored in the "name" key of 133 | * the file in the $_FILES array. 134 | */ 135 | public function getClientFilename(): string; 136 | 137 | /** 138 | * Retrieve the media type sent by the client. 139 | * 140 | * Do not trust the value returned by this method. A client could send 141 | * a malicious media type with the intention to corrupt or hack your 142 | * application. 143 | * 144 | * This may be the empty string. 145 | */ 146 | public function getClientMediaType(): string; 147 | } 148 | -------------------------------------------------------------------------------- /src/RequestInterface.hack: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 PHP Framework Interoperability Group 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | * 22 | * Portions Copyright (c) 2018-present, Facebook, Inc. 23 | * All rights reserved. 24 | * 25 | * This source code is licensed under the MIT license found in the 26 | * LICENSE file in the root directory of this source tree. 27 | * 28 | */ 29 | 30 | namespace Facebook\Experimental\Http\Message; 31 | 32 | use namespace HH\Lib\IO; 33 | 34 | enum HTTPMethod: string { 35 | PUT = 'PUT'; 36 | GET = 'GET'; 37 | POST = 'POST'; 38 | HEAD = 'HEAD'; 39 | PATCH = 'PATCH'; 40 | TRACE = 'TRACE'; 41 | DELETE = 'DELETE'; 42 | OPTIONS = 'OPTIONS'; 43 | CONNECT = 'CONNECT'; 44 | } 45 | 46 | type RequestURIOptions = shape('preserveHost' => bool); 47 | 48 | /** 49 | * Representation of an outgoing, client-side request. 50 | * 51 | * Per the HTTP specification, this interface includes properties for 52 | * each of the following: 53 | * 54 | * - Protocol version 55 | * - HTTP method 56 | * - URI 57 | * - Headers 58 | * - Message body 59 | * 60 | * During construction, implementations MUST attempt to set the Host header from 61 | * a provided URI if no Host header is provided. 62 | * 63 | * Requests are considered immutable; all methods that might change state MUST 64 | * be implemented such that they retain the internal state of the current 65 | * message and return an instance that contains the changed state. 66 | */ 67 | interface RequestInterface extends MessageInterface { 68 | /** 69 | * Retrieves the message's request target. 70 | * 71 | * Retrieves the message's request-target either as it will appear (for 72 | * clients), as it appeared at request (for servers), or as it was 73 | * specified for the instance (see withRequestTarget()). 74 | * 75 | * In most cases, this will be the origin-form of the composed URI, 76 | * unless a value was provided to the concrete implementation (see 77 | * withRequestTarget() below). 78 | * 79 | * If no URI is available, and no request-target has been specifically 80 | * provided, this method MUST return the string "/". 81 | * 82 | * @return string 83 | */ 84 | public function getRequestTarget(): string; 85 | 86 | /** 87 | * Return an instance with the specific request-target. 88 | * 89 | * If the request needs a non-origin-form request-target — e.g., for 90 | * specifying an absolute-form, authority-form, or asterisk-form — 91 | * this method may be used to create an instance with the specified 92 | * request-target, verbatim. 93 | * 94 | * This method MUST be implemented in such a way as to retain the 95 | * immutability of the message, and MUST return an instance that has the 96 | * changed request target. 97 | * 98 | * @link http://tools.ietf.org/html/rfc7230#section-5.3 (for the various 99 | * request-target forms allowed in request messages) 100 | * @return static 101 | */ 102 | public function withRequestTarget(string $requestTarget): this; 103 | 104 | /** 105 | * Retrieves the HTTP method of the request. 106 | */ 107 | public function getMethod(): HTTPMethod; 108 | 109 | /** 110 | * Return an instance with the provided HTTP method. 111 | * 112 | * This method MUST be implemented in such a way as to retain the 113 | * immutability of the message, and MUST return an instance that has the 114 | * changed request method. 115 | */ 116 | public function withMethod(HTTPMethod $method): this; 117 | 118 | /** 119 | * Retrieves the URI instance. 120 | * 121 | * This method MUST return a UriInterface instance. 122 | * 123 | * @link http://tools.ietf.org/html/rfc3986#section-4.3 124 | * @return UriInterface Returns a UriInterface instance 125 | * representing the URI of the request. 126 | */ 127 | public function getUri(): UriInterface; 128 | 129 | /** 130 | * Returns an instance with the provided URI. 131 | * 132 | * This method MUST update the Host header of the returned request by 133 | * default if the URI contains a host component. If the URI does not 134 | * contain a host component, any pre-existing Host header MUST be carried 135 | * over to the returned request. 136 | * 137 | * You can opt-in to preserving the original state of the Host header by 138 | * setting `preserveHost` option to `true` in $options shape. 139 | * When `preserveHost` option is set to 140 | * `true`, this method interacts with the Host header in the following ways: 141 | * 142 | * - If the Host header is missing or empty, and the new URI contains 143 | * a host component, this method MUST update the Host header in the returned 144 | * request. 145 | * - If the Host header is missing or empty, and the new URI does not contain a 146 | * host component, this method MUST NOT update the Host header in the returned 147 | * request. 148 | * - If a Host header is present and non-empty, this method MUST NOT update 149 | * the Host header in the returned request. 150 | * 151 | * This method MUST be implemented in such a way as to retain the 152 | * immutability of the message, and MUST return an instance that has the 153 | * new UriInterface instance. 154 | * 155 | * @link http://tools.ietf.org/html/rfc3986#section-4.3 156 | * @return static 157 | */ 158 | public function withUri(UriInterface $uri, RequestURIOptions $options = shape('preserveHost' => false)): this; 159 | 160 | /** 161 | * Gets the body of the message. 162 | */ 163 | public function getBody(): IO\ReadHandle; 164 | 165 | /** 166 | * Return an instance with the specified message body. 167 | * 168 | * This method MUST be implemented in such a way as to retain the 169 | * immutability of the message, and MUST return a new instance that has the 170 | * new body stream. 171 | */ 172 | public function withBody(IO\ReadHandle $body): this; 173 | } 174 | -------------------------------------------------------------------------------- /src/ServerRequestInterface.hack: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 PHP Framework Interoperability Group 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | * 22 | * Portions Copyright (c) 2018-present, Facebook, Inc. 23 | * All rights reserved. 24 | * 25 | * This source code is licensed under the MIT license found in the 26 | * LICENSE file in the root directory of this source tree. 27 | * 28 | */ 29 | 30 | namespace Facebook\Experimental\Http\Message; 31 | 32 | /** 33 | * Representation of an incoming, server-side HTTP request. 34 | * 35 | * Per the HTTP specification, this interface includes properties for 36 | * each of the following: 37 | * 38 | * - Protocol version 39 | * - HTTP method 40 | * - URI 41 | * - Headers 42 | * - Message body 43 | * 44 | * Additionally, it encapsulates all data as it has arrived to the 45 | * application from the CGI and/or PHP environment, including: 46 | * 47 | * - The values represented in $_SERVER. 48 | * - Any cookies provided (generally via $_COOKIE) 49 | * - Query string arguments (generally via $_GET, or as parsed via parse_str()) 50 | * - Upload files, if any (as represented by $_FILES) 51 | * - Deserialized body parameters (generally from $_POST) 52 | * 53 | * $_SERVER values MUST be treated as immutable, as they represent application 54 | * state at the time of request; as such, no methods are provided to allow 55 | * modification of those values. The other values provide such methods, as they 56 | * can be restored from $_SERVER or the request body, and may need treatment 57 | * during the application (e.g., body parameters may be deserialized based on 58 | * content type). 59 | * 60 | * Additionally, this interface recognizes the utility of introspecting a 61 | * request to derive and match additional parameters (e.g., via URI path 62 | * matching, decrypting cookie values, deserializing non-form-encoded body 63 | * content, matching authorization headers to users, etc). These parameters 64 | * are stored in an "attributes" property. 65 | * 66 | * Requests are considered immutable; all methods that might change state MUST 67 | * be implemented such that they retain the internal state of the current 68 | * message and return an instance that contains the changed state. 69 | */ 70 | interface ServerRequestInterface extends RequestInterface { 71 | /** 72 | * Retrieve server parameters. 73 | * 74 | * Retrieves data related to the incoming request environment, 75 | * typically derived from PHP's $_SERVER superglobal. The data IS NOT 76 | * REQUIRED to originate from $_SERVER. 77 | */ 78 | public function getServerParams(): dict; 79 | 80 | /** 81 | * Set server parameters. 82 | */ 83 | public function withServerParams(dict $values): this; 84 | 85 | /** 86 | * Retrieve cookies. 87 | * 88 | * Retrieves cookies sent by the client to the server. 89 | * 90 | * Cookie names must not contain `.` or ` `. 91 | */ 92 | public function getCookieParams(): dict; 93 | 94 | /** 95 | * Return an instance with the specified cookies. 96 | * 97 | * Cookie names must not contain `.` or ` `. 98 | * 99 | * This method MUST NOT update the related Cookie header of the request 100 | * instance, nor related values in the server params. 101 | * 102 | * This method MUST be implemented in such a way as to retain the 103 | * immutability of the message, and MUST return an instance that has the 104 | * updated cookie values. 105 | */ 106 | public function withCookieParams(dict $cookies): this; 107 | 108 | /** 109 | * Retrieve query string arguments. 110 | * 111 | * Retrieves the deserialized query string arguments, if any. 112 | * 113 | * Note: the query params might not be in sync with the URI or server 114 | * params. If you need to ensure you are only getting the original 115 | * values, you may need to parse the query string from `getUri()->getQuery()` 116 | * or from the `QUERY_STRING` server param. 117 | */ 118 | public function getQueryParams(): dict; 119 | 120 | /** 121 | * Return an instance with the specified query string arguments. 122 | * 123 | * These values SHOULD remain immutable over the course of the incoming 124 | * request. They MAY be injected during instantiation, such as from PHP's 125 | * $_GET superglobal, or MAY be derived from some other value such as the 126 | * URI. In cases where the arguments are parsed from the URI, the data 127 | * MUST be compatible with what PHP's parse_str() would return for 128 | * purposes of how duplicate query parameters are handled, and how nested 129 | * sets are handled. 130 | * 131 | * Setting query string arguments MUST NOT change the URI stored by the 132 | * request, nor the values in the server params. 133 | * 134 | * This method MUST be implemented in such a way as to retain the 135 | * immutability of the message, and MUST return an instance that has the 136 | * updated query string arguments. 137 | */ 138 | public function withQueryParams(dict $query): this; 139 | 140 | /** 141 | * Retrieve normalized file upload data. 142 | */ 143 | public function getUploadedFiles(): dict; 144 | 145 | /** 146 | * Create a new instance with the specified uploaded files. 147 | * 148 | * This method MUST be implemented in such a way as to retain the 149 | * immutability of the message, and MUST return an instance that has the 150 | * updated body parameters. 151 | */ 152 | public function withUploadedFiles( 153 | dict $uploadedFiles, 154 | ): this; 155 | 156 | /** 157 | * Retrieve any parameters provided in the request body. 158 | * 159 | * If the request Content-Type is either application/x-www-form-urlencoded 160 | * or multipart/form-data, and the request method is POST, this method MUST 161 | * return the key-value pairs. 162 | * 163 | * Otherwise, it will return the empty dict. 164 | */ 165 | public function getParsedBody(): dict; 166 | 167 | /** 168 | * Return an instance with the specified body parameters. 169 | */ 170 | public function withParsedBody(dict $data): this; 171 | } 172 | -------------------------------------------------------------------------------- /src/MessageInterface.hack: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 PHP Framework Interoperability Group 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | * 22 | * Portions Copyright (c) 2018-present, Facebook, Inc. 23 | * All rights reserved. 24 | * 25 | * This source code is licensed under the MIT license found in the 26 | * LICENSE file in the root directory of this source tree. 27 | * 28 | */ 29 | 30 | namespace Facebook\Experimental\Http\Message; 31 | 32 | /** 33 | * HTTP messages consist of requests from a client to a server and responses 34 | * from a server to a client. This interface defines the methods common to 35 | * each. 36 | * 37 | * Messages are considered immutable; all methods that might change state MUST 38 | * be implemented such that they retain the internal state of the current 39 | * message and return an instance that contains the changed state. 40 | * 41 | * @link http://www.ietf.org/rfc/rfc7230.txt 42 | * @link http://www.ietf.org/rfc/rfc7231.txt 43 | */ 44 | interface MessageInterface { 45 | /** 46 | * Retrieves the HTTP protocol version as a string. 47 | * 48 | * The string MUST contain only the HTTP version number (e.g., "1.1", "1.0"). 49 | */ 50 | public function getProtocolVersion(): string; 51 | 52 | /** 53 | * Return an instance with the specified HTTP protocol version. 54 | * 55 | * The version string MUST contain only the HTTP version number (e.g., 56 | * "1.1", "1.0"). 57 | * 58 | * This method MUST be implemented in such a way as to retain the 59 | * immutability of the message, and MUST return an instance that has the 60 | * new protocol version. 61 | */ 62 | public function withProtocolVersion(string $version): this; 63 | 64 | /** 65 | * Retrieves all message header values. 66 | * 67 | * The keys represent the header name as it will be sent over the wire, and 68 | * each value is a vec of strings associated with the header. 69 | * 70 | * While header names are not case-sensitive, getHeaders() will preserve the 71 | * exact case in which headers were originally specified. 72 | * 73 | * Implementations *MUST* return header names of the form `Foo-Bar`, but keys 74 | * should be considered case-insensitive. 75 | * 76 | * Implementations *MAY* choose to normalize some headers in different ways, 77 | * for example, `ETag` instead of `Etag`. 78 | */ 79 | public function getHeaders(): dict>; 80 | 81 | /** 82 | * Checks if a header exists by the given case-insensitive name. 83 | * 84 | * @param string $name Case-insensitive header field name, in the same form 85 | * as HTTP itself - e.g. `Content-Length`, not `CONTENT_LENGTH`. 86 | * @return Returns true if any header names match the given header 87 | * name using a case-insensitive string comparison. Returns false if 88 | * no matching header name is found in the message. 89 | */ 90 | public function hasHeader(string $name): bool; 91 | 92 | /** 93 | * Retrieves a message header value by the given case-insensitive name. 94 | * 95 | * This method returns an array of all the header values of the given 96 | * case-insensitive header name. 97 | * 98 | * If the header does not appear in the message, this method MUST return an 99 | * empty array. 100 | * 101 | * @param string $name Case-insensitive header field name, in the same form 102 | * as HTTP itself - e.g. `Content-Length`, not `CONTENT_LENGTH` 103 | */ 104 | public function getHeader(string $name): vec; 105 | 106 | /** 107 | * Retrieves a comma-separated string of the values for a single header. 108 | * 109 | * This method returns all of the header values of the given 110 | * case-insensitive header name as a string concatenated together using 111 | * a comma. 112 | * 113 | * NOTE: Not all header values may be appropriately represented using 114 | * comma concatenation. For such headers, use getHeader() instead 115 | * and supply your own delimiter when concatenating. 116 | * 117 | * If the header does not appear in the message, this method MUST return 118 | * an empty string. 119 | * 120 | * @param string $name Case-insensitive header field name, in the same form 121 | * as HTTP itself - for example, `Content-Length`, not `CONTENT_LENGTH` 122 | * @return string A string of values as provided for the given header 123 | * concatenated together using a comma. If the header does not appear in 124 | * the message, this method MUST return an empty string. 125 | */ 126 | public function getHeaderLine(string $name): string; 127 | 128 | /** 129 | * Return an instance with the provided values replacing the specified header. 130 | * 131 | * While header names are case-insensitive, the casing of the header will 132 | * be preserved by this function, and returned from getHeaders(). 133 | * 134 | * For example, if `foo` is already set as a header, calling 135 | * `withHeader('Foo')` will unset `foo`, and set `Foo` 136 | * 137 | * This method MUST be implemented in such a way as to retain the 138 | * immutability of the message, and MUST return an instance that has the 139 | * new and/or updated header and value. 140 | * 141 | * @param string $name Case-insensitive header field name. 142 | * @throws \InvalidArgumentException for invalid header names or values. 143 | */ 144 | public function withHeader(string $name, vec $values): this; 145 | 146 | /** 147 | * Return an instance with the provided value replacing the specified header. 148 | * 149 | * While header names are case-insensitive, the casing of the header will 150 | * be preserved by this function, and returned from getHeaders(). 151 | * 152 | * This method MUST be implemented in such a way as to retain the 153 | * immutability of the message, and MUST return an instance that has the 154 | * new and/or updated header and value. 155 | * 156 | * @param string $name Case-insensitive header field name. 157 | * @throws \InvalidArgumentException for invalid header names or values. 158 | */ 159 | public function withHeaderLine(string $name, string $value): this; 160 | 161 | /** 162 | * Return an instance with the specified header appended with the given values. 163 | * 164 | * Existing values for the specified header will be maintained. The new 165 | * values will be appended to the existing list. If the header did not 166 | * exist previously, it will be added. 167 | * 168 | * This method MUST be implemented in such a way as to retain the 169 | * immutability of the message, and MUST return an instance that has the 170 | * new header and/or value. 171 | * 172 | * @param string $name Case-insensitive header field name to add. 173 | * @return static 174 | * @throws \InvalidArgumentException for invalid header names or values. 175 | */ 176 | public function withAddedHeader(string $name, vec $values): this; 177 | 178 | /** 179 | * Return an instance with the specified header appended with the given value. 180 | * 181 | * Existing values for the specified header will be maintained. The new 182 | * value will be appended to the existing list. If the header did not 183 | * exist previously, it will be added. 184 | * 185 | * This method MUST be implemented in such a way as to retain the 186 | * immutability of the message, and MUST return an instance that has the 187 | * new header and/or value. 188 | * 189 | * @param string $name Case-insensitive header field name to add. 190 | * @return static 191 | * @throws \InvalidArgumentException for invalid header names or values. 192 | */ 193 | public function withAddedHeaderLine(string $name, string $value): this; 194 | 195 | /** 196 | * Return an instance without the specified header. 197 | * 198 | * Header resolution MUST be done without case-sensitivity. 199 | * 200 | * This method MUST be implemented in such a way as to retain the 201 | * immutability of the message, and MUST return an instance that removes 202 | * the named header. 203 | * 204 | * @param string $name Case-insensitive header field name to remove. 205 | * @return static 206 | */ 207 | public function withoutHeader(string $name): this; 208 | } 209 | -------------------------------------------------------------------------------- /src/UriInterface.hack: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 PHP Framework Interoperability Group 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | * 22 | * Portions Copyright (c) 2018-present, Facebook, Inc. 23 | * All rights reserved. 24 | * 25 | * This source code is licensed under the MIT license found in the 26 | * LICENSE file in the root directory of this source tree. 27 | * 28 | */ 29 | namespace Facebook\Experimental\Http\Message; 30 | 31 | /** 32 | * Value object representing a URI. 33 | * 34 | * This interface is meant to represent URIs according to RFC 3986 and to 35 | * provide methods for most common operations. Additional functionality for 36 | * working with URIs can be provided on top of the interface or externally. 37 | * Its primary use is for HTTP requests, but may also be used in other 38 | * contexts. 39 | * 40 | * Instances of this interface are considered immutable; all methods that 41 | * might change state MUST be implemented such that they retain the internal 42 | * state of the current instance and return an instance that contains the 43 | * changed state. 44 | * 45 | * Typically the Host header will be also be present in the request message. 46 | * For server-side requests, the scheme will typically be discoverable in the 47 | * server parameters. 48 | * 49 | * @link http://tools.ietf.org/html/rfc3986 (the URI specification) 50 | */ 51 | interface UriInterface { 52 | /** 53 | * Retrieve the scheme component of the URI. 54 | * 55 | * If no scheme is present, this method MUST return the empty string 56 | * 57 | * The value returned MUST be normalized to lowercase, per RFC 3986 58 | * Section 3.1. 59 | * 60 | * The trailing ":" character is not part of the scheme and MUST NOT be 61 | * added. 62 | * 63 | * @see https://tools.ietf.org/html/rfc3986#section-3.1 64 | * @return null|string The URI scheme. 65 | */ 66 | public function getScheme(): string; 67 | 68 | /** 69 | * Retrieve the authority component of the URI. 70 | * 71 | * If no authority information is present, this method MUST return null 72 | * 73 | * The authority syntax of the URI is: 74 | * 75 | *
 76 |    * [user-info@]host[:port]
 77 |    * 
78 | * 79 | * If the port component is not set or is the standard port for the current 80 | * scheme, it SHOULD NOT be included. 81 | * 82 | * @see https://tools.ietf.org/html/rfc3986#section-3.2 83 | * @return null|string The URI authority, in "[user-info@]host[:port]" format. 84 | */ 85 | public function getAuthority(): string; 86 | 87 | /** 88 | * Retrieve the user information component of the URI. 89 | * 90 | * If no credentials are present, this method MUST empty strings for username 91 | * and password. 92 | * 93 | * @return shape A shape containing username and password 94 | */ 95 | public function getUserInfo(): shape('user' => string, 'pass' => string); 96 | 97 | /** 98 | * Retrieve the host component of the URI. 99 | * 100 | * If no host is present, this method MUST return the empty string 101 | * 102 | * The value returned MUST be normalized to lowercase, per RFC 3986 103 | * Section 3.2.2. 104 | * 105 | * @see http://tools.ietf.org/html/rfc3986#section-3.2.2 106 | * @return null|string The URI host. 107 | */ 108 | public function getHost(): string; 109 | 110 | /** 111 | * Retrieve the port component of the URI. 112 | * 113 | * If a port is present, and it is non-standard for the current scheme, 114 | * this method MUST return it as an integer. If the port is the standard port 115 | * used with the current scheme, this method SHOULD return null. 116 | * 117 | * If no port is present, and no scheme is present, this method MUST return 118 | * a null value. 119 | * 120 | * If no port is present, but a scheme is present, this method MAY return 121 | * the standard port for that scheme, but SHOULD return null. 122 | * 123 | * @return null|int The URI port. 124 | */ 125 | public function getPort(): ?int; 126 | 127 | /** 128 | * Retrieve the path component of the URI. 129 | * 130 | * The path can either be empty or absolute (starting with a slash) or 131 | * rootless (not starting with a slash). Implementations MUST support all 132 | * three syntaxes. 133 | * 134 | * Normally, the empty path "" and absolute path "/" are considered equal as 135 | * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically 136 | * do this normalization because in contexts with a trimmed base path, e.g. 137 | * the front controller, this difference becomes significant. It's the task 138 | * of the user to handle both "" and "/". 139 | * 140 | * The value returned MUST be percent-encoded, but MUST NOT double-encode 141 | * any characters. To determine what characters to encode, please refer to 142 | * RFC 3986, Sections 2 and 3.3. 143 | * 144 | * As an example, if the value should include a slash ("/") not intended as 145 | * delimiter between path segments, that value MUST be passed in encoded 146 | * form (e.g., "%2F") to the instance. 147 | * 148 | * @see https://tools.ietf.org/html/rfc3986#section-2 149 | * @see https://tools.ietf.org/html/rfc3986#section-3.3 150 | */ 151 | public function getPath(): string; 152 | 153 | /** 154 | * Retrieve the encoded query string of the URI. 155 | * 156 | * If no query params or string is present, this method MUST return the empty 157 | * string. 158 | * 159 | * The leading "?" character is not part of the query and MUST NOT be 160 | * added. 161 | * 162 | * The value returned MUST be percent-encoded, but MUST NOT double-encode 163 | * any characters. To determine what characters to encode, please refer to 164 | * RFC 3986, Sections 2 and 3.4. 165 | * 166 | * As an example, if a value in a key/value pair of the query string should 167 | * include an ampersand ("&") not intended as a delimiter between values, 168 | * that value MUST be passed in encoded form (e.g., "%26") to the instance. 169 | * 170 | * @see https://tools.ietf.org/html/rfc3986#section-2 171 | * @see https://tools.ietf.org/html/rfc3986#section-3.4 172 | * @return null|string The percent-encoded query string 173 | */ 174 | public function getRawQuery(): string; 175 | 176 | /** 177 | * Retrieve the query params of the URI. 178 | * 179 | * If no query params are present, this method MUST return an empty dict 180 | * 181 | * All keys and values MUST not be encoded. 182 | */ 183 | public function getQuery(): dict; 184 | 185 | /** 186 | * Retrieve the fragment component of the URI. 187 | * 188 | * If no fragment is present, this method MUST return the empty string 189 | * 190 | * The leading "#" character is not part of the fragment and MUST NOT be 191 | * added. 192 | * 193 | * The value returned MUST be percent-encoded, but MUST NOT double-encode 194 | * any characters. To determine what characters to encode, please refer to 195 | * RFC 3986, Sections 2 and 3.5. 196 | * 197 | * @see https://tools.ietf.org/html/rfc3986#section-2 198 | * @see https://tools.ietf.org/html/rfc3986#section-3.5 199 | */ 200 | public function getFragment(): string; 201 | 202 | /** 203 | * Return an instance with the specified scheme. 204 | * 205 | * This method MUST retain the state of the current instance, and return 206 | * an instance that contains the specified scheme. 207 | * 208 | * Implementations MUST support the schemes "http" and "https" case 209 | * insensitively, and MAY accommodate other schemes if required. 210 | * 211 | * An empty string is equivalent to removing the scheme. 212 | * 213 | * @throws \InvalidArgumentException for invalid or unsupported schemes. 214 | */ 215 | public function withScheme(string $scheme): this; 216 | 217 | /** 218 | * Return an instance with the specified user information. 219 | * 220 | * This method MUST retain the state of the current instance, and return 221 | * an instance that contains the specified user information. 222 | * 223 | * An empty string for the user is equivalent to removing user 224 | * information. 225 | */ 226 | public function withUserInfo(string $user, string $password): this; 227 | 228 | /** 229 | * Return an instance with the specified host. 230 | * 231 | * This method MUST retain the state of the current instance, and return 232 | * an instance that contains the specified host. 233 | * 234 | * An empty string is equivalent to removing the host. 235 | * 236 | * @throws \InvalidArgumentException for invalid hostnames. 237 | */ 238 | public function withHost(string $host): this; 239 | 240 | /** 241 | * Return an instance with the specified port. 242 | * 243 | * This method MUST retain the state of the current instance, and return 244 | * an instance that contains the specified port. 245 | * 246 | * Implementations MUST raise an exception for ports outside the 247 | * established TCP and UDP port ranges. 248 | * 249 | * A null value provided for the port is equivalent to removing the port 250 | * information. 251 | * 252 | * @throws \InvalidArgumentException for invalid ports. 253 | */ 254 | public function withPort(?int $port = null): this; 255 | 256 | /** 257 | * Return an instance with the specified path. 258 | * 259 | * This method MUST retain the state of the current instance, and return 260 | * an instance that contains the specified path. 261 | * 262 | * The path can either be empty or absolute (starting with a slash) or 263 | * rootless (not starting with a slash). Implementations MUST support all 264 | * three syntaxes. 265 | * 266 | * If the path is intended to be domain-relative rather than path relative then 267 | * it must begin with a slash ("/"). Paths not starting with a slash ("/") 268 | * are assumed to be relative to some base path known to the application or 269 | * consumer. 270 | * 271 | * Users can provide both encoded and decoded path characters. 272 | * Implementations ensure the correct encoding as outlined in getPath(). 273 | * 274 | * @throws \InvalidArgumentException for invalid paths. 275 | */ 276 | public function withPath(string $path): this; 277 | 278 | /** 279 | * Return an instance with the specified query params. 280 | * 281 | * This method MUST retain the state of the current instance, and return 282 | * an instance that contains the specified query. 283 | * 284 | * Users must provide only unencoded (raw) query characters in order to avoid double 285 | * encoding. 286 | * Implementations ensure the correct encoding as outlined in getQuery(). 287 | * All query params which value is set to an empty string MUST be omitted. 288 | * The query params dict is assumed to be empty if there are no elements in 289 | * it or all params have been omitted. 290 | * 291 | * An empty query params dict is equivalent to removing the query. 292 | * 293 | * @return static A new instance with the specified query. 294 | * @throws \InvalidArgumentException for invalid query params. 295 | */ 296 | public function withQuery(dict $query): this; 297 | 298 | /** 299 | * Return an instance with the specified query string. 300 | * 301 | * This method MUST retain the state of the current instance, and return 302 | * an instance that contains the specified query. 303 | * 304 | * Users must provide only unencoded (raw) query characters in order to avoid double 305 | * encoding. 306 | * Implementations ensure the correct encoding as outlined in getQuery(). 307 | * 308 | * An empty query string is equivalent to removing the query. 309 | * 310 | * @return static A new instance with the specified query. 311 | * @throws \InvalidArgumentException for invalid query params. 312 | */ 313 | public function withRawQuery(string $query): this; 314 | 315 | /** 316 | * Return an instance with the specified URI fragment. 317 | * 318 | * This method MUST retain the state of the current instance, and return 319 | * an instance that contains the specified URI fragment. 320 | * 321 | * Users can provide both encoded and decoded fragment characters. 322 | * Implementations ensure the correct encoding as outlined in getFragment(). 323 | * 324 | * A null value provided for the fragment is equivalent to removing the 325 | * fragment. 326 | * 327 | * @param string $fragment The fragment to use with the new instance. 328 | * @return static A new instance with the specified fragment. 329 | */ 330 | public function withFragment(string $fragment): this; 331 | 332 | /** 333 | * Return the string representation as a URI reference. 334 | * 335 | * Depending on which components of the URI are present, the resulting 336 | * string is either a full URI or relative reference according to RFC 3986, 337 | * Section 4.1. The method concatenates the various components of the URI, 338 | * using the appropriate delimiters: 339 | * 340 | * - If a scheme is present, it MUST be suffixed by ":". 341 | * - If an authority is present, it MUST be prefixed by "//". 342 | * - The path can be concatenated without delimiters. But there are two 343 | * cases where the an exception should be thrown: 344 | * - If the path is rootless and an authority is present 345 | * - If the path is starting with more than one "/" and no authority is 346 | * present 347 | * - If a query is present, it MUST be prefixed by "?". 348 | * - If a fragment is present, it MUST be prefixed by "#". 349 | * 350 | * @see http://tools.ietf.org/html/rfc3986#section-4.1 351 | */ 352 | public function toString(): string; 353 | } 354 | --------------------------------------------------------------------------------