pax_global_header 0000666 0000000 0000000 00000000064 13535331707 0014521 g ustar 00root root 0000000 0000000 52 comment=06b78a0a2b2ab4557bf2d737eed6124c9b30fbcc
php-nyholm-psr7-1.2.1/ 0000775 0000000 0000000 00000000000 13535331707 0014506 5 ustar 00root root 0000000 0000000 php-nyholm-psr7-1.2.1/.editorconfig 0000664 0000000 0000000 00000000223 13535331707 0017160 0 ustar 00root root 0000000 0000000 root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
php-nyholm-psr7-1.2.1/.github/ 0000775 0000000 0000000 00000000000 13535331707 0016046 5 ustar 00root root 0000000 0000000 php-nyholm-psr7-1.2.1/.github/workflows/ 0000775 0000000 0000000 00000000000 13535331707 0020103 5 ustar 00root root 0000000 0000000 php-nyholm-psr7-1.2.1/.github/workflows/bc.yml 0000664 0000000 0000000 00000000333 13535331707 0021211 0 ustar 00root root 0000000 0000000 on: [push]
name: Roave
jobs:
roave_bc_check:
name: BC Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Roave BC Check
uses: docker://nyholm/roave-bc-check-ga
php-nyholm-psr7-1.2.1/.github/workflows/static.yml 0000664 0000000 0000000 00000001004 13535331707 0022110 0 ustar 00root root 0000000 0000000 on: [push]
name: Static analysis
jobs:
phpstan:
name: PHPStan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: PHPStan
uses: docker://oskarstark/phpstan-ga
with:
args: analyze --no-progress
php-cs-fixer:
name: PHP-CS-Fixer
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: PHP-CS-Fixer
uses: docker://oskarstark/php-cs-fixer-ga
with:
args: --dry-run --diff-format udiff
php-nyholm-psr7-1.2.1/.gitignore 0000664 0000000 0000000 00000000063 13535331707 0016475 0 ustar 00root root 0000000 0000000 /composer.lock
/phpstan.neon
/phpunit.xml
/vendor/
php-nyholm-psr7-1.2.1/.php_cs 0000664 0000000 0000000 00000001144 13535331707 0015763 0 ustar 00root root 0000000 0000000 setRiskyAllowed(true)
->setRules([
'@Symfony' => true,
'@Symfony:risky' => true,
'array_syntax' => array('syntax' => 'short'),
'native_function_invocation' => true,
'ordered_imports' => true,
'declare_strict_types' => true,
'single_import_per_statement' => false,
'concat_space' => ['spacing'=>'one'],
'phpdoc_align' => ['align'=>'left'],
])
->setFinder(
PhpCsFixer\Finder::create()
->in(__DIR__.'/src')
->name('*.php')
)
;
return $config;
php-nyholm-psr7-1.2.1/.scrutinizer.yml 0000664 0000000 0000000 00000000235 13535331707 0017670 0 ustar 00root root 0000000 0000000 filter:
excluded_paths: [vendor/*, tests/*]
checks:
php:
code_rating: true
duplication: true
tools:
external_code_coverage: true
php-nyholm-psr7-1.2.1/.travis.yml 0000664 0000000 0000000 00000002075 13535331707 0016623 0 ustar 00root root 0000000 0000000 language: php
sudo: false
cache:
directories:
- $HOME/.composer/cache
php:
- 7.1
- 7.2
- 7.3
env:
global:
- TEST_COMMAND="composer test"
branches:
except:
- /^patch-.*$/
matrix:
fast_finish: true
allow_failures:
- php: nightly
env: COMPOSER_FLAGS="--ignore-platform-reqs"
include:
- php: 7.2
name: Lowest version of dependencies
env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" COVERAGE=true TEST_COMMAND="composer test-ci"
- php: nightly
name: PHP 8.0
env: COMPOSER_FLAGS="--ignore-platform-reqs"
before_install:
- if ! [ -v "$DEPENDENCIES" ]; then composer require --no-update ${DEPENDENCIES}; fi;
install:
- composer update ${COMPOSER_FLAGS} --prefer-source --no-interaction
script:
- $TEST_COMMAND
after_success:
- if [[ "$COVERAGE" = true ]]; then wget https://scrutinizer-ci.com/ocular.phar; fi
- if [[ "$COVERAGE" = true ]]; then php ocular.phar code-coverage:upload --format=php-clover build/coverage.xml; fi
php-nyholm-psr7-1.2.1/CHANGELOG.md 0000664 0000000 0000000 00000004255 13535331707 0016325 0 ustar 00root root 0000000 0000000 # Changelog
All notable changes to this project will be documented in this file, in reverse chronological order by release.
## 1.2.1
### Changed
- Added `.github` and `phpstan.neon.dist` to `.gitattributes`.
## 1.2.0
### Changed
- Change minimal port number to 0 (unix socket)
- Updated `Psr17Factory::createResponse` to respect the specification. If second
argument is not used, a standard reason phrase. If an empty string is passed,
then the reason phrase will be empty.
### Fixed
- Check for seekable on the stream resource.
- Fixed the `Response::$reason` should never be null.
## 1.1.0
### Added
- Improved performance
- More tests for `UploadedFile` and `HttplugFactory`
### Removed
- Dead code
## 1.0.1
### Fixed
- Handle `fopen` failing in createStreamFromFile according to PSR-7.
- Reduce execution path to speed up performance.
- Fixed typos.
- Code style.
## 1.0.0
### Added
- Support for final PSR-17 (HTTP factories). (`Psr17Factory`)
- Support for numeric header values.
- Support for empty header values.
- All classes are final
- `HttplugFactory` that implements factory interfaces from HTTPlug.
### Changed
- `ServerRequest` does not extend `Request`.
### Removed
- The HTTPlug discovery strategy was removed since it is included in php-http/discovery 1.4.
- `UploadedFileFactory()` was removed in favor for `Psr17Factory`.
- `ServerRequestFactory()` was removed in favor for `Psr17Factory`.
- `StreamFactory`, `UriFactory`, abd `MessageFactory`. Use `HttplugFactory` instead.
- `ServerRequestFactory::createServerRequestFromArray`, `ServerRequestFactory::createServerRequestFromArrays` and
`ServerRequestFactory::createServerRequestFromGlobals`. Please use the new `nyholm/psr7-server` instead.
## 0.3.0
### Added
- Return types.
- Many `InvalidArgumentException`s are thrown when you use invalid arguments.
- Integration tests for `UploadedFile` and `ServerRequest`.
### Changed
- We dropped PHP7.0 support.
- PSR-17 factories have been marked as internal. They do not fall under our BC promise until PSR-17 is accepted.
- `UploadedFileFactory::createUploadedFile` does not accept a string file path.
## 0.2.3
No changelog before this release
php-nyholm-psr7-1.2.1/LICENSE 0000664 0000000 0000000 00000002056 13535331707 0015516 0 ustar 00root root 0000000 0000000 MIT License
Copyright (c) 2016 Tobias Nyholm
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
php-nyholm-psr7-1.2.1/README.md 0000664 0000000 0000000 00000007402 13535331707 0015770 0 ustar 00root root 0000000 0000000 # PSR-7 implementation
[](https://github.com/Nyholm/psr7/releases)
[](https://travis-ci.org/Nyholm/psr7)
[](https://scrutinizer-ci.com/g/Nyholm/psr7)
[](https://scrutinizer-ci.com/g/Nyholm/psr7)
[](https://packagist.org/packages/nyholm/psr7)
[](https://packagist.org/packages/nyholm/psr7)
[](LICENSE)
A super lightweight PSR-7 implementation. Very strict and very fast.
| Description | Guzzle | Zend | Slim | Nyholm |
| ---- | ------ | ---- | ---- | ------ |
| Lines of code | 3 000 | 3 000 | 1 700 | 1 000 |
| PHP7 | No | Yes | No | Yes |
| PSR-7* | 66% | 100% | 75% | 100% |
| PSR-17 | No | Yes | Yes | Yes |
| HTTPlug | No | No | No | Yes |
| Performance** | 1.34x | 1x | 1.16x | 1.75x |
\* Percent of completed tests in https://github.com/php-http/psr7-integration-tests
\** See benchmark at https://github.com/Nyholm/http-client-benchmark (higher is better)
## Installation
```bash
composer require nyholm/psr7
```
If you are using Symfony Flex then you get all message factories registered as services.
## Usage
The PSR-7 objects do not contain any other public methods than those defined in
the [PSR-7 specification](https://www.php-fig.org/psr/psr-7/).
### Create objects
Use the PSR-17 factory to create requests, streams, URIs etc.
```php
$psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();
$request = $psr17Factory->createRequest('GET', 'http://tnyholm.se');
$stream = $psr17Factory->createStream('foobar');
```
### Sending a request
With [HTTPlug](http://httplug.io/) or any other PSR-18 (HTTP client) you may send
requests like:
```bash
composer require kriswallsmith/buzz
```
```php
$psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();
$psr18Client = new \Buzz\Client\Curl($psr17Factory);
$request = $psr17Factory->createRequest('GET', 'http://tnyholm.se');
$response = $psr18Client->sendRequest($request);
```
### Create server requests
The [`nyholm/psr7-server`](https://github.com/Nyholm/psr7-server) package can be used
to create server requests from PHP superglobals.
```bash
composer require nyholm/psr7-server
```
```php
$psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();
$creator = new \Nyholm\Psr7Server\ServerRequestCreator(
$psr17Factory, // ServerRequestFactory
$psr17Factory, // UriFactory
$psr17Factory, // UploadedFileFactory
$psr17Factory // StreamFactory
);
$serverRequest = $creator->fromGlobals();
```
### Emitting a response
```bash
composer require zendframework/zend-httphandlerrunner
```
```php
$psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();
$responseBody = $psr17Factory->createStream('Hello world');
$response = $psr17Factory->createResponse(200)->withBody($responseBody);
(new \Zend\HttpHandlerRunner\Emitter\SapiEmitter())->emit($response);
```
## Our goal
This package is currently maintained by [Tobias Nyholm](http://nyholm.se) and
[Martijn van der Ven](https://vanderven.se/martijn/). They have decided that the
goal of this library should be to provide a super strict implementation of
[PSR-7](https://www.php-fig.org/psr/psr-7/) that is blazing fast.
The package will never include any extra features nor helper methods. All our classes
and functions exist because they are required to fulfill the PSR-7 specification.
php-nyholm-psr7-1.2.1/composer.json 0000664 0000000 0000000 00000002455 13535331707 0017236 0 ustar 00root root 0000000 0000000 {
"name": "nyholm/psr7",
"description": "A fast PHP7 implementation of PSR-7",
"license": "MIT",
"keywords": ["psr-7", "psr-17"],
"homepage": "http://tnyholm.se",
"authors": [
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com"
},
{
"name": "Martijn van der Ven",
"email": "martijn@vanderven.se"
}
],
"require": {
"php": "^7.1",
"psr/http-message": "^1.0",
"php-http/message-factory": "^1.0",
"psr/http-factory": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^7.5",
"php-http/psr7-integration-tests": "dev-master",
"http-interop/http-factory-tests": "dev-master"
},
"provide": {
"psr/http-message-implementation": "1.0",
"psr/http-factory-implementation": "1.0"
},
"autoload": {
"psr-4": {
"Nyholm\\Psr7\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\Nyholm\\Psr7\\": "tests/"
}
},
"scripts": {
"test": "vendor/bin/phpunit",
"test-ci": "vendor/bin/phpunit --coverage-text --coverage-clover=build/coverage.xml"
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
}
}
php-nyholm-psr7-1.2.1/phpstan.neon.dist 0000664 0000000 0000000 00000001745 13535331707 0020015 0 ustar 00root root 0000000 0000000 parameters:
level: 5
paths:
- src
ignoreErrors:
-
message: '#Strict comparison using === between false and true will always evaluate to false#'
path: %currentWorkingDirectory%/src/UploadedFile.php
-
message: '#Strict comparison using === between null and string will always evaluate to false#'
path: %currentWorkingDirectory%/src/Response.php
-
message: '#Result of && is always false.#'
path: %currentWorkingDirectory%/src/Response.php
-
message: '#Strict comparison using !== between null and null will always evaluate to false#'
path: %currentWorkingDirectory%/src/ServerRequest.php
-
message: '#Result of && is always false#'
path: %currentWorkingDirectory%/src/ServerRequest.php
-
message: '#Result of && is always false#'
path: %currentWorkingDirectory%/src/UploadedFile.php
php-nyholm-psr7-1.2.1/phpunit.xml.dist 0000664 0000000 0000000 00000002006 13535331707 0017657 0 ustar 00root root 0000000 0000000
tests/
./vendor/http-interop/http-factory-tests/test
src/
php-nyholm-psr7-1.2.1/src/ 0000775 0000000 0000000 00000000000 13535331707 0015275 5 ustar 00root root 0000000 0000000 php-nyholm-psr7-1.2.1/src/Factory/ 0000775 0000000 0000000 00000000000 13535331707 0016704 5 ustar 00root root 0000000 0000000 php-nyholm-psr7-1.2.1/src/Factory/HttplugFactory.php 0000664 0000000 0000000 00000002144 13535331707 0022375 0 ustar 00root root 0000000 0000000
* @author Martijn van der Ven
*/
final class HttplugFactory implements MessageFactory, StreamFactory, UriFactory
{
public function createRequest($method, $uri, array $headers = [], $body = null, $protocolVersion = '1.1')
{
return new Request($method, $uri, $headers, $body, $protocolVersion);
}
public function createResponse($statusCode = 200, $reasonPhrase = null, array $headers = [], $body = null, $version = '1.1')
{
return new Response((int) $statusCode, $headers, $body, $version, $reasonPhrase);
}
public function createStream($body = null)
{
return Stream::create($body ?? '');
}
public function createUri($uri = ''): UriInterface
{
if ($uri instanceof UriInterface) {
return $uri;
}
return new Uri($uri);
}
}
php-nyholm-psr7-1.2.1/src/Factory/Psr17Factory.php 0000664 0000000 0000000 00000005207 13535331707 0021665 0 ustar 00root root 0000000 0000000
* @author Martijn van der Ven
*/
final class Psr17Factory implements RequestFactoryInterface, ResponseFactoryInterface, ServerRequestFactoryInterface, StreamFactoryInterface, UploadedFileFactoryInterface, UriFactoryInterface
{
public function createRequest(string $method, $uri): RequestInterface
{
return new Request($method, $uri);
}
public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface
{
if (2 > \func_num_args()) {
// This will make the Response class to use a custom reasonPhrase
$reasonPhrase = null;
}
return new Response($code, [], null, '1.1', $reasonPhrase);
}
public function createStream(string $content = ''): StreamInterface
{
return Stream::create($content);
}
public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface
{
$resource = @\fopen($filename, $mode);
if (false === $resource) {
if ('' === $mode || false === \in_array($mode[0], ['r', 'w', 'a', 'x', 'c'])) {
throw new \InvalidArgumentException('The mode ' . $mode . ' is invalid.');
}
throw new \RuntimeException('The file ' . $filename . ' cannot be opened.');
}
return Stream::create($resource);
}
public function createStreamFromResource($resource): StreamInterface
{
return Stream::create($resource);
}
public function createUploadedFile(StreamInterface $stream, int $size = null, int $error = \UPLOAD_ERR_OK, string $clientFilename = null, string $clientMediaType = null): UploadedFileInterface
{
if (null === $size) {
$size = $stream->getSize();
}
return new UploadedFile($stream, $size, $error, $clientFilename, $clientMediaType);
}
public function createUri(string $uri = ''): UriInterface
{
return new Uri($uri);
}
public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface
{
return new ServerRequest($method, $uri, [], null, '1.1', $serverParams);
}
}
php-nyholm-psr7-1.2.1/src/MessageTrait.php 0000664 0000000 0000000 00000013776 13535331707 0020414 0 ustar 00root root 0000000 0000000
* @author Martijn van der Ven
*
* @internal should not be used outside of Nyholm/Psr7 as it does not fall under our BC promise
*/
trait MessageTrait
{
/** @var array Map of all registered headers, as original name => array of values */
private $headers = [];
/** @var array Map of lowercase header name => original name at registration */
private $headerNames = [];
/** @var string */
private $protocol = '1.1';
/** @var StreamInterface|null */
private $stream;
public function getProtocolVersion(): string
{
return $this->protocol;
}
public function withProtocolVersion($version): self
{
if ($this->protocol === $version) {
return $this;
}
$new = clone $this;
$new->protocol = $version;
return $new;
}
public function getHeaders(): array
{
return $this->headers;
}
public function hasHeader($header): bool
{
return isset($this->headerNames[\strtolower($header)]);
}
public function getHeader($header): array
{
$header = \strtolower($header);
if (!isset($this->headerNames[$header])) {
return [];
}
$header = $this->headerNames[$header];
return $this->headers[$header];
}
public function getHeaderLine($header): string
{
return \implode(', ', $this->getHeader($header));
}
public function withHeader($header, $value): self
{
$value = $this->validateAndTrimHeader($header, $value);
$normalized = \strtolower($header);
$new = clone $this;
if (isset($new->headerNames[$normalized])) {
unset($new->headers[$new->headerNames[$normalized]]);
}
$new->headerNames[$normalized] = $header;
$new->headers[$header] = $value;
return $new;
}
public function withAddedHeader($header, $value): self
{
if (!\is_string($header) || '' === $header) {
throw new \InvalidArgumentException('Header name must be an RFC 7230 compatible string.');
}
$new = clone $this;
$new->setHeaders([$header => $value]);
return $new;
}
public function withoutHeader($header): self
{
$normalized = \strtolower($header);
if (!isset($this->headerNames[$normalized])) {
return $this;
}
$header = $this->headerNames[$normalized];
$new = clone $this;
unset($new->headers[$header], $new->headerNames[$normalized]);
return $new;
}
public function getBody(): StreamInterface
{
if (null === $this->stream) {
$this->stream = Stream::create('');
}
return $this->stream;
}
public function withBody(StreamInterface $body): self
{
if ($body === $this->stream) {
return $this;
}
$new = clone $this;
$new->stream = $body;
return $new;
}
private function setHeaders(array $headers): void
{
foreach ($headers as $header => $value) {
$value = $this->validateAndTrimHeader($header, $value);
$normalized = \strtolower($header);
if (isset($this->headerNames[$normalized])) {
$header = $this->headerNames[$normalized];
$this->headers[$header] = \array_merge($this->headers[$header], $value);
} else {
$this->headerNames[$normalized] = $header;
$this->headers[$header] = $value;
}
}
}
/**
* Make sure the header complies with RFC 7230.
*
* Header names must be a non-empty string consisting of token characters.
*
* Header values must be strings consisting of visible characters with all optional
* leading and trailing whitespace stripped. This method will always strip such
* optional whitespace. Note that the method does not allow folding whitespace within
* the values as this was deprecated for almost all instances by the RFC.
*
* header-field = field-name ":" OWS field-value OWS
* field-name = 1*( "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / "^"
* / "_" / "`" / "|" / "~" / %x30-39 / ( %x41-5A / %x61-7A ) )
* OWS = *( SP / HTAB )
* field-value = *( ( %x21-7E / %x80-FF ) [ 1*( SP / HTAB ) ( %x21-7E / %x80-FF ) ] )
*
* @see https://tools.ietf.org/html/rfc7230#section-3.2.4
*/
private function validateAndTrimHeader($header, $values): array
{
if (!\is_string($header) || 1 !== \preg_match("@^[!#$%&'*+.^_`|~0-9A-Za-z-]+$@", $header)) {
throw new \InvalidArgumentException('Header name must be an RFC 7230 compatible string.');
}
if (!\is_array($values)) {
// This is simple, just one value.
if ((!\is_numeric($values) && !\is_string($values)) || 1 !== \preg_match("@^[ \t\x21-\x7E\x80-\xFF]*$@", (string) $values)) {
throw new \InvalidArgumentException('Header values must be RFC 7230 compatible strings.');
}
return [\trim((string) $values, " \t")];
}
if (empty($values)) {
throw new \InvalidArgumentException('Header values must be a string or an array of strings, empty array given.');
}
// Assert Non empty array
$returnValues = [];
foreach ($values as $v) {
if ((!\is_numeric($v) && !\is_string($v)) || 1 !== \preg_match("@^[ \t\x21-\x7E\x80-\xFF]*$@", (string) $v)) {
throw new \InvalidArgumentException('Header values must be RFC 7230 compatible strings.');
}
$returnValues[] = \trim((string) $v, " \t");
}
return $returnValues;
}
}
php-nyholm-psr7-1.2.1/src/Request.php 0000664 0000000 0000000 00000002333 13535331707 0017437 0 ustar 00root root 0000000 0000000
* @author Martijn van der Ven
*/
final class Request implements RequestInterface
{
use MessageTrait;
use RequestTrait;
/**
* @param string $method HTTP method
* @param string|UriInterface $uri URI
* @param array $headers Request headers
* @param string|resource|StreamInterface|null $body Request body
* @param string $version Protocol version
*/
public function __construct(string $method, $uri, array $headers = [], $body = null, string $version = '1.1')
{
if (!($uri instanceof UriInterface)) {
$uri = new Uri($uri);
}
$this->method = $method;
$this->uri = $uri;
$this->setHeaders($headers);
$this->protocol = $version;
if (!$this->hasHeader('Host')) {
$this->updateHostFromUri();
}
// If we got no body, defer initialization of the stream until Request::getBody()
if ('' !== $body && null !== $body) {
$this->stream = Stream::create($body);
}
}
}
php-nyholm-psr7-1.2.1/src/RequestTrait.php 0000664 0000000 0000000 00000005125 13535331707 0020445 0 ustar 00root root 0000000 0000000
* @author Martijn van der Ven
*
* @internal should not be used outside of Nyholm/Psr7 as it does not fall under our BC promise
*/
trait RequestTrait
{
/** @var string */
private $method;
/** @var string|null */
private $requestTarget;
/** @var UriInterface|null */
private $uri;
public function getRequestTarget(): string
{
if (null !== $this->requestTarget) {
return $this->requestTarget;
}
if ('' === $target = $this->uri->getPath()) {
$target = '/';
}
if ('' !== $this->uri->getQuery()) {
$target .= '?' . $this->uri->getQuery();
}
return $target;
}
public function withRequestTarget($requestTarget): self
{
if (\preg_match('#\s#', $requestTarget)) {
throw new \InvalidArgumentException('Invalid request target provided; cannot contain whitespace');
}
$new = clone $this;
$new->requestTarget = $requestTarget;
return $new;
}
public function getMethod(): string
{
return $this->method;
}
public function withMethod($method): self
{
if (!\is_string($method)) {
throw new \InvalidArgumentException('Method must be a string');
}
$new = clone $this;
$new->method = $method;
return $new;
}
public function getUri(): UriInterface
{
return $this->uri;
}
public function withUri(UriInterface $uri, $preserveHost = false): self
{
if ($uri === $this->uri) {
return $this;
}
$new = clone $this;
$new->uri = $uri;
if (!$preserveHost || !$this->hasHeader('Host')) {
$new->updateHostFromUri();
}
return $new;
}
private function updateHostFromUri(): void
{
if ('' === $host = $this->uri->getHost()) {
return;
}
if (null !== ($port = $this->uri->getPort())) {
$host .= ':' . $port;
}
if (isset($this->headerNames['host'])) {
$header = $this->headerNames['host'];
} else {
$this->headerNames['host'] = $header = 'Host';
}
// Ensure Host is the first header.
// See: http://tools.ietf.org/html/rfc7230#section-5.4
$this->headers = [$header => [$host]] + $this->headers;
}
}
php-nyholm-psr7-1.2.1/src/Response.php 0000664 0000000 0000000 00000007757 13535331707 0017624 0 ustar 00root root 0000000 0000000
* @author Martijn van der Ven
*/
final class Response implements ResponseInterface
{
use MessageTrait;
/** @var array Map of standard HTTP status code/reason phrases */
private const PHRASES = [
100 => 'Continue', 101 => 'Switching Protocols', 102 => 'Processing',
200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-status', 208 => 'Already Reported',
300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => 'Switch Proxy', 307 => 'Temporary Redirect',
400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', 418 => 'I\'m a teapot', 422 => 'Unprocessable Entity', 423 => 'Locked', 424 => 'Failed Dependency', 425 => 'Unordered Collection', 426 => 'Upgrade Required', 428 => 'Precondition Required', 429 => 'Too Many Requests', 431 => 'Request Header Fields Too Large', 451 => 'Unavailable For Legal Reasons',
500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported', 506 => 'Variant Also Negotiates', 507 => 'Insufficient Storage', 508 => 'Loop Detected', 511 => 'Network Authentication Required',
];
/** @var string */
private $reasonPhrase = '';
/** @var int */
private $statusCode;
/**
* @param int $status Status code
* @param array $headers Response headers
* @param string|resource|StreamInterface|null $body Response body
* @param string $version Protocol version
* @param string|null $reason Reason phrase (when empty a default will be used based on the status code)
*/
public function __construct(int $status = 200, array $headers = [], $body = null, string $version = '1.1', string $reason = null)
{
// If we got no body, defer initialization of the stream until Response::getBody()
if ('' !== $body && null !== $body) {
$this->stream = Stream::create($body);
}
$this->statusCode = $status;
$this->setHeaders($headers);
if (null === $reason && isset(self::PHRASES[$this->statusCode])) {
$this->reasonPhrase = self::PHRASES[$status];
} else {
$this->reasonPhrase = $reason ?? '';
}
$this->protocol = $version;
}
public function getStatusCode(): int
{
return $this->statusCode;
}
public function getReasonPhrase(): string
{
return $this->reasonPhrase;
}
public function withStatus($code, $reasonPhrase = ''): self
{
if (!\is_int($code) && !\is_string($code)) {
throw new \InvalidArgumentException('Status code has to be an integer');
}
$code = (int) $code;
if ($code < 100 || $code > 599) {
throw new \InvalidArgumentException('Status code has to be an integer between 100 and 599');
}
$new = clone $this;
$new->statusCode = $code;
if ((null === $reasonPhrase || '' === $reasonPhrase) && isset(self::PHRASES[$new->statusCode])) {
$reasonPhrase = self::PHRASES[$new->statusCode];
}
$new->reasonPhrase = $reasonPhrase;
return $new;
}
}
php-nyholm-psr7-1.2.1/src/ServerRequest.php 0000664 0000000 0000000 00000007450 13535331707 0020633 0 ustar 00root root 0000000 0000000
* @author Martijn van der Ven
*/
final class ServerRequest implements ServerRequestInterface
{
use MessageTrait;
use RequestTrait;
/** @var array */
private $attributes = [];
/** @var array */
private $cookieParams = [];
/** @var array|object|null */
private $parsedBody;
/** @var array */
private $queryParams = [];
/** @var array */
private $serverParams;
/** @var UploadedFileInterface[] */
private $uploadedFiles = [];
/**
* @param string $method HTTP method
* @param string|UriInterface $uri URI
* @param array $headers Request headers
* @param string|resource|StreamInterface|null $body Request body
* @param string $version Protocol version
* @param array $serverParams Typically the $_SERVER superglobal
*/
public function __construct(string $method, $uri, array $headers = [], $body = null, string $version = '1.1', array $serverParams = [])
{
$this->serverParams = $serverParams;
if (!($uri instanceof UriInterface)) {
$uri = new Uri($uri);
}
$this->method = $method;
$this->uri = $uri;
$this->setHeaders($headers);
$this->protocol = $version;
if (!$this->hasHeader('Host')) {
$this->updateHostFromUri();
}
// If we got no body, defer initialization of the stream until ServerRequest::getBody()
if ('' !== $body && null !== $body) {
$this->stream = Stream::create($body);
}
}
public function getServerParams(): array
{
return $this->serverParams;
}
public function getUploadedFiles(): array
{
return $this->uploadedFiles;
}
public function withUploadedFiles(array $uploadedFiles)
{
$new = clone $this;
$new->uploadedFiles = $uploadedFiles;
return $new;
}
public function getCookieParams(): array
{
return $this->cookieParams;
}
public function withCookieParams(array $cookies)
{
$new = clone $this;
$new->cookieParams = $cookies;
return $new;
}
public function getQueryParams(): array
{
return $this->queryParams;
}
public function withQueryParams(array $query)
{
$new = clone $this;
$new->queryParams = $query;
return $new;
}
public function getParsedBody()
{
return $this->parsedBody;
}
public function withParsedBody($data)
{
if (!\is_array($data) && !\is_object($data) && null !== $data) {
throw new \InvalidArgumentException('First parameter to withParsedBody MUST be object, array or null');
}
$new = clone $this;
$new->parsedBody = $data;
return $new;
}
public function getAttributes(): array
{
return $this->attributes;
}
public function getAttribute($attribute, $default = null)
{
if (false === \array_key_exists($attribute, $this->attributes)) {
return $default;
}
return $this->attributes[$attribute];
}
public function withAttribute($attribute, $value): self
{
$new = clone $this;
$new->attributes[$attribute] = $value;
return $new;
}
public function withoutAttribute($attribute): self
{
if (false === \array_key_exists($attribute, $this->attributes)) {
return $this;
}
$new = clone $this;
unset($new->attributes[$attribute]);
return $new;
}
}
php-nyholm-psr7-1.2.1/src/Stream.php 0000664 0000000 0000000 00000014370 13535331707 0017246 0 ustar 00root root 0000000 0000000
* @author Martijn van der Ven
*/
final class Stream implements StreamInterface
{
/** @var resource|null A resource reference */
private $stream;
/** @var bool */
private $seekable;
/** @var bool */
private $readable;
/** @var bool */
private $writable;
/** @var array|mixed|void|null */
private $uri;
/** @var int|null */
private $size;
/** @var array Hash of readable and writable stream types */
private const READ_WRITE_HASH = [
'read' => [
'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true,
'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true,
'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true,
'x+t' => true, 'c+t' => true, 'a+' => true,
],
'write' => [
'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true,
'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true,
'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true,
'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true,
],
];
private function __construct()
{
}
/**
* Creates a new PSR-7 stream.
*
* @param string|resource|StreamInterface $body
*
* @return StreamInterface
*
* @throws \InvalidArgumentException
*/
public static function create($body = ''): StreamInterface
{
if ($body instanceof StreamInterface) {
return $body;
}
if (\is_string($body)) {
$resource = \fopen('php://temp', 'rw+');
\fwrite($resource, $body);
$body = $resource;
}
if (\is_resource($body)) {
$new = new self();
$new->stream = $body;
$meta = \stream_get_meta_data($new->stream);
$new->seekable = $meta['seekable'] && 0 === \fseek($new->stream, 0, \SEEK_CUR);
$new->readable = isset(self::READ_WRITE_HASH['read'][$meta['mode']]);
$new->writable = isset(self::READ_WRITE_HASH['write'][$meta['mode']]);
$new->uri = $new->getMetadata('uri');
return $new;
}
throw new \InvalidArgumentException('First argument to Stream::create() must be a string, resource or StreamInterface.');
}
/**
* Closes the stream when the destructed.
*/
public function __destruct()
{
$this->close();
}
public function __toString(): string
{
try {
if ($this->isSeekable()) {
$this->seek(0);
}
return $this->getContents();
} catch (\Exception $e) {
return '';
}
}
public function close(): void
{
if (isset($this->stream)) {
if (\is_resource($this->stream)) {
\fclose($this->stream);
}
$this->detach();
}
}
public function detach()
{
if (!isset($this->stream)) {
return null;
}
$result = $this->stream;
unset($this->stream);
$this->size = $this->uri = null;
$this->readable = $this->writable = $this->seekable = false;
return $result;
}
public function getSize(): ?int
{
if (null !== $this->size) {
return $this->size;
}
if (!isset($this->stream)) {
return null;
}
// Clear the stat cache if the stream has a URI
if ($this->uri) {
\clearstatcache(true, $this->uri);
}
$stats = \fstat($this->stream);
if (isset($stats['size'])) {
$this->size = $stats['size'];
return $this->size;
}
return null;
}
public function tell(): int
{
if (false === $result = \ftell($this->stream)) {
throw new \RuntimeException('Unable to determine stream position');
}
return $result;
}
public function eof(): bool
{
return !$this->stream || \feof($this->stream);
}
public function isSeekable(): bool
{
return $this->seekable;
}
public function seek($offset, $whence = \SEEK_SET): void
{
if (!$this->seekable) {
throw new \RuntimeException('Stream is not seekable');
}
if (-1 === \fseek($this->stream, $offset, $whence)) {
throw new \RuntimeException('Unable to seek to stream position ' . $offset . ' with whence ' . \var_export($whence, true));
}
}
public function rewind(): void
{
$this->seek(0);
}
public function isWritable(): bool
{
return $this->writable;
}
public function write($string): int
{
if (!$this->writable) {
throw new \RuntimeException('Cannot write to a non-writable stream');
}
// We can't know the size after writing anything
$this->size = null;
if (false === $result = \fwrite($this->stream, $string)) {
throw new \RuntimeException('Unable to write to stream');
}
return $result;
}
public function isReadable(): bool
{
return $this->readable;
}
public function read($length): string
{
if (!$this->readable) {
throw new \RuntimeException('Cannot read from non-readable stream');
}
return \fread($this->stream, $length);
}
public function getContents(): string
{
if (!isset($this->stream)) {
throw new \RuntimeException('Unable to read stream contents');
}
if (false === $contents = \stream_get_contents($this->stream)) {
throw new \RuntimeException('Unable to read stream contents');
}
return $contents;
}
public function getMetadata($key = null)
{
if (!isset($this->stream)) {
return $key ? null : [];
}
$meta = \stream_get_meta_data($this->stream);
if (null === $key) {
return $meta;
}
return $meta[$key] ?? null;
}
}
php-nyholm-psr7-1.2.1/src/UploadedFile.php 0000664 0000000 0000000 00000011676 13535331707 0020356 0 ustar 00root root 0000000 0000000
* @author Martijn van der Ven
*/
final class UploadedFile implements UploadedFileInterface
{
/** @var array */
private const ERRORS = [
\UPLOAD_ERR_OK => 1,
\UPLOAD_ERR_INI_SIZE => 1,
\UPLOAD_ERR_FORM_SIZE => 1,
\UPLOAD_ERR_PARTIAL => 1,
\UPLOAD_ERR_NO_FILE => 1,
\UPLOAD_ERR_NO_TMP_DIR => 1,
\UPLOAD_ERR_CANT_WRITE => 1,
\UPLOAD_ERR_EXTENSION => 1,
];
/** @var string */
private $clientFilename;
/** @var string */
private $clientMediaType;
/** @var int */
private $error;
/** @var string|null */
private $file;
/** @var bool */
private $moved = false;
/** @var int */
private $size;
/** @var StreamInterface|null */
private $stream;
/**
* @param StreamInterface|string|resource $streamOrFile
* @param int $size
* @param int $errorStatus
* @param string|null $clientFilename
* @param string|null $clientMediaType
*/
public function __construct($streamOrFile, $size, $errorStatus, $clientFilename = null, $clientMediaType = null)
{
if (false === \is_int($errorStatus) || !isset(self::ERRORS[$errorStatus])) {
throw new \InvalidArgumentException('Upload file error status must be an integer value and one of the "UPLOAD_ERR_*" constants.');
}
if (false === \is_int($size)) {
throw new \InvalidArgumentException('Upload file size must be an integer');
}
if (null !== $clientFilename && !\is_string($clientFilename)) {
throw new \InvalidArgumentException('Upload file client filename must be a string or null');
}
if (null !== $clientMediaType && !\is_string($clientMediaType)) {
throw new \InvalidArgumentException('Upload file client media type must be a string or null');
}
$this->error = $errorStatus;
$this->size = $size;
$this->clientFilename = $clientFilename;
$this->clientMediaType = $clientMediaType;
if (\UPLOAD_ERR_OK === $this->error) {
// Depending on the value set file or stream variable.
if (\is_string($streamOrFile)) {
$this->file = $streamOrFile;
} elseif (\is_resource($streamOrFile)) {
$this->stream = Stream::create($streamOrFile);
} elseif ($streamOrFile instanceof StreamInterface) {
$this->stream = $streamOrFile;
} else {
throw new \InvalidArgumentException('Invalid stream or file provided for UploadedFile');
}
}
}
/**
* @throws \RuntimeException if is moved or not ok
*/
private function validateActive(): void
{
if (\UPLOAD_ERR_OK !== $this->error) {
throw new \RuntimeException('Cannot retrieve stream due to upload error');
}
if ($this->moved) {
throw new \RuntimeException('Cannot retrieve stream after it has already been moved');
}
}
public function getStream(): StreamInterface
{
$this->validateActive();
if ($this->stream instanceof StreamInterface) {
return $this->stream;
}
$resource = \fopen($this->file, 'r');
return Stream::create($resource);
}
public function moveTo($targetPath): void
{
$this->validateActive();
if (!\is_string($targetPath) || '' === $targetPath) {
throw new \InvalidArgumentException('Invalid path provided for move operation; must be a non-empty string');
}
if (null !== $this->file) {
$this->moved = 'cli' === \PHP_SAPI ? \rename($this->file, $targetPath) : \move_uploaded_file($this->file, $targetPath);
} else {
$stream = $this->getStream();
if ($stream->isSeekable()) {
$stream->rewind();
}
// Copy the contents of a stream into another stream until end-of-file.
$dest = Stream::create(\fopen($targetPath, 'w'));
while (!$stream->eof()) {
if (!$dest->write($stream->read(1048576))) {
break;
}
}
$this->moved = true;
}
if (false === $this->moved) {
throw new \RuntimeException(\sprintf('Uploaded file could not be moved to %s', $targetPath));
}
}
public function getSize(): int
{
return $this->size;
}
public function getError(): int
{
return $this->error;
}
public function getClientFilename(): ?string
{
return $this->clientFilename;
}
public function getClientMediaType(): ?string
{
return $this->clientMediaType;
}
}
php-nyholm-psr7-1.2.1/src/Uri.php 0000664 0000000 0000000 00000017522 13535331707 0016554 0 ustar 00root root 0000000 0000000
* @author Martijn van der Ven
*/
final class Uri implements UriInterface
{
private const SCHEMES = ['http' => 80, 'https' => 443];
private const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~';
private const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
/** @var string Uri scheme. */
private $scheme = '';
/** @var string Uri user info. */
private $userInfo = '';
/** @var string Uri host. */
private $host = '';
/** @var int|null Uri port. */
private $port;
/** @var string Uri path. */
private $path = '';
/** @var string Uri query string. */
private $query = '';
/** @var string Uri fragment. */
private $fragment = '';
public function __construct(string $uri = '')
{
if ('' !== $uri) {
if (false === $parts = \parse_url($uri)) {
throw new \InvalidArgumentException("Unable to parse URI: $uri");
}
// Apply parse_url parts to a URI.
$this->scheme = isset($parts['scheme']) ? \strtolower($parts['scheme']) : '';
$this->userInfo = $parts['user'] ?? '';
$this->host = isset($parts['host']) ? \strtolower($parts['host']) : '';
$this->port = isset($parts['port']) ? $this->filterPort($parts['port']) : null;
$this->path = isset($parts['path']) ? $this->filterPath($parts['path']) : '';
$this->query = isset($parts['query']) ? $this->filterQueryAndFragment($parts['query']) : '';
$this->fragment = isset($parts['fragment']) ? $this->filterQueryAndFragment($parts['fragment']) : '';
if (isset($parts['pass'])) {
$this->userInfo .= ':' . $parts['pass'];
}
}
}
public function __toString(): string
{
return self::createUriString($this->scheme, $this->getAuthority(), $this->path, $this->query, $this->fragment);
}
public function getScheme(): string
{
return $this->scheme;
}
public function getAuthority(): string
{
if ('' === $this->host) {
return '';
}
$authority = $this->host;
if ('' !== $this->userInfo) {
$authority = $this->userInfo . '@' . $authority;
}
if (null !== $this->port) {
$authority .= ':' . $this->port;
}
return $authority;
}
public function getUserInfo(): string
{
return $this->userInfo;
}
public function getHost(): string
{
return $this->host;
}
public function getPort(): ?int
{
return $this->port;
}
public function getPath(): string
{
return $this->path;
}
public function getQuery(): string
{
return $this->query;
}
public function getFragment(): string
{
return $this->fragment;
}
public function withScheme($scheme): self
{
if (!\is_string($scheme)) {
throw new \InvalidArgumentException('Scheme must be a string');
}
if ($this->scheme === $scheme = \strtolower($scheme)) {
return $this;
}
$new = clone $this;
$new->scheme = $scheme;
$new->port = $new->filterPort($new->port);
return $new;
}
public function withUserInfo($user, $password = null): self
{
$info = $user;
if (null !== $password && '' !== $password) {
$info .= ':' . $password;
}
if ($this->userInfo === $info) {
return $this;
}
$new = clone $this;
$new->userInfo = $info;
return $new;
}
public function withHost($host): self
{
if (!\is_string($host)) {
throw new \InvalidArgumentException('Host must be a string');
}
if ($this->host === $host = \strtolower($host)) {
return $this;
}
$new = clone $this;
$new->host = $host;
return $new;
}
public function withPort($port): self
{
if ($this->port === $port = $this->filterPort($port)) {
return $this;
}
$new = clone $this;
$new->port = $port;
return $new;
}
public function withPath($path): self
{
if ($this->path === $path = $this->filterPath($path)) {
return $this;
}
$new = clone $this;
$new->path = $path;
return $new;
}
public function withQuery($query): self
{
if ($this->query === $query = $this->filterQueryAndFragment($query)) {
return $this;
}
$new = clone $this;
$new->query = $query;
return $new;
}
public function withFragment($fragment): self
{
if ($this->fragment === $fragment = $this->filterQueryAndFragment($fragment)) {
return $this;
}
$new = clone $this;
$new->fragment = $fragment;
return $new;
}
/**
* Create a URI string from its various parts.
*/
private static function createUriString(string $scheme, string $authority, string $path, string $query, string $fragment): string
{
$uri = '';
if ('' !== $scheme) {
$uri .= $scheme . ':';
}
if ('' !== $authority) {
$uri .= '//' . $authority;
}
if ('' !== $path) {
if ('/' !== $path[0]) {
if ('' !== $authority) {
// If the path is rootless and an authority is present, the path MUST be prefixed by "/"
$path = '/' . $path;
}
} elseif (isset($path[1]) && '/' === $path[1]) {
if ('' === $authority) {
// If the path is starting with more than one "/" and no authority is present, the
// starting slashes MUST be reduced to one.
$path = '/' . \ltrim($path, '/');
}
}
$uri .= $path;
}
if ('' !== $query) {
$uri .= '?' . $query;
}
if ('' !== $fragment) {
$uri .= '#' . $fragment;
}
return $uri;
}
/**
* Is a given port non-standard for the current scheme?
*/
private static function isNonStandardPort(string $scheme, int $port): bool
{
return !isset(self::SCHEMES[$scheme]) || $port !== self::SCHEMES[$scheme];
}
private function filterPort($port): ?int
{
if (null === $port) {
return null;
}
$port = (int) $port;
if (0 > $port || 0xffff < $port) {
throw new \InvalidArgumentException(\sprintf('Invalid port: %d. Must be between 0 and 65535', $port));
}
return self::isNonStandardPort($this->scheme, $port) ? $port : null;
}
private function filterPath($path): string
{
if (!\is_string($path)) {
throw new \InvalidArgumentException('Path must be a string');
}
return \preg_replace_callback('/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/', [__CLASS__, 'rawurlencodeMatchZero'], $path);
}
private function filterQueryAndFragment($str): string
{
if (!\is_string($str)) {
throw new \InvalidArgumentException('Query and fragment must be a string');
}
return \preg_replace_callback('/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/', [__CLASS__, 'rawurlencodeMatchZero'], $str);
}
private static function rawurlencodeMatchZero(array $match): string
{
return \rawurlencode($match[0]);
}
}
php-nyholm-psr7-1.2.1/tests/ 0000775 0000000 0000000 00000000000 13535331707 0015650 5 ustar 00root root 0000000 0000000 php-nyholm-psr7-1.2.1/tests/Factory/ 0000775 0000000 0000000 00000000000 13535331707 0017257 5 ustar 00root root 0000000 0000000 php-nyholm-psr7-1.2.1/tests/Factory/HttplugFactoryTest.php 0000664 0000000 0000000 00000004217 13535331707 0023613 0 ustar 00root root 0000000 0000000 createRequest('POST', 'https://nyholm.tech', ['Content-Type' => 'text/html'], 'foobar', '2.0');
$this->assertEquals('POST', $r->getMethod());
$this->assertEquals('https://nyholm.tech', $r->getUri()->__toString());
$this->assertEquals('2.0', $r->getProtocolVersion());
$this->assertEquals('foobar', $r->getBody()->__toString());
$headers = $r->getHeaders();
$this->assertCount(2, $headers); // Including HOST
$this->assertArrayHasKey('Content-Type', $headers);
$this->assertEquals('text/html', $headers['Content-Type'][0]);
}
public function testCreateResponse()
{
$factory = new HttplugFactory();
$r = $factory->createResponse(217, 'Perfect', ['Content-Type' => 'text/html'], 'foobar', '2.0');
$this->assertEquals(217, $r->getStatusCode());
$this->assertEquals('Perfect', $r->getReasonPhrase());
$this->assertEquals('2.0', $r->getProtocolVersion());
$this->assertEquals('foobar', $r->getBody()->__toString());
$headers = $r->getHeaders();
$this->assertCount(1, $headers);
$this->assertArrayHasKey('Content-Type', $headers);
$this->assertEquals('text/html', $headers['Content-Type'][0]);
}
public function testCreateStream()
{
$factory = new HttplugFactory();
$stream = $factory->createStream('foobar');
$this->assertInstanceOf(StreamInterface::class, $stream);
$this->assertEquals('foobar', $stream->__toString());
}
public function testCreateUri()
{
$factory = new HttplugFactory();
$uri = $factory->createUri('https://nyholm.tech/foo');
$this->assertInstanceOf(UriInterface::class, $uri);
$this->assertEquals('https://nyholm.tech/foo', $uri->__toString());
}
}
php-nyholm-psr7-1.2.1/tests/Factory/Psr17FactoryTest.php 0000664 0000000 0000000 00000002106 13535331707 0023073 0 ustar 00root root 0000000 0000000 createResponse(200);
$this->assertEquals('OK', $r->getReasonPhrase());
$r = $factory->createResponse(200, '');
$this->assertEquals('', $r->getReasonPhrase());
$r = $factory->createResponse(200, 'Foo');
$this->assertEquals('Foo', $r->getReasonPhrase());
/*
* Test for non-standard response codes
*/
$r = $factory->createResponse(567);
$this->assertEquals('', $r->getReasonPhrase());
$r = $factory->createResponse(567, '');
$this->assertEquals(567, $r->getStatusCode());
$this->assertEquals('', $r->getReasonPhrase());
$r = $factory->createResponse(567, 'Foo');
$this->assertEquals(567, $r->getStatusCode());
$this->assertEquals('Foo', $r->getReasonPhrase());
}
}
php-nyholm-psr7-1.2.1/tests/Integration/ 0000775 0000000 0000000 00000000000 13535331707 0020133 5 ustar 00root root 0000000 0000000 php-nyholm-psr7-1.2.1/tests/Integration/RequestTest.php 0000664 0000000 0000000 00000000402 13535331707 0023130 0 ustar 00root root 0000000 0000000 createUploadedFile(Stream::create('writing to tempfile'));
}
}
php-nyholm-psr7-1.2.1/tests/Integration/UriTest.php 0000664 0000000 0000000 00000000350 13535331707 0022241 0 ustar 00root root 0000000 0000000 assertEquals('/', (string) $r->getUri());
}
public function testRequestUriMayBeUri()
{
$uri = new Uri('/');
$r = new Request('GET', $uri);
$this->assertSame($uri, $r->getUri());
}
public function testValidateRequestUri()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Unable to parse URI: ///');
new Request('GET', '///');
}
public function testCanConstructWithBody()
{
$r = new Request('GET', '/', [], 'baz');
$this->assertInstanceOf(StreamInterface::class, $r->getBody());
$this->assertEquals('baz', (string) $r->getBody());
}
public function testNullBody()
{
$r = new Request('GET', '/', [], null);
$this->assertInstanceOf(StreamInterface::class, $r->getBody());
$this->assertSame('', (string) $r->getBody());
}
public function testFalseyBody()
{
$r = new Request('GET', '/', [], '0');
$this->assertInstanceOf(StreamInterface::class, $r->getBody());
$this->assertSame('0', (string) $r->getBody());
}
public function testConstructorDoesNotReadStreamBody()
{
$body = $this->getMockBuilder(StreamInterface::class)->getMock();
$body->expects($this->never())
->method('__toString');
$r = new Request('GET', '/', [], $body);
$this->assertSame($body, $r->getBody());
}
public function testWithUri()
{
$r1 = new Request('GET', '/');
$u1 = $r1->getUri();
$u2 = new Uri('http://www.example.com');
$r2 = $r1->withUri($u2);
$this->assertNotSame($r1, $r2);
$this->assertSame($u2, $r2->getUri());
$this->assertSame($u1, $r1->getUri());
$r3 = new Request('GET', '/');
$u3 = $r3->getUri();
$r4 = $r3->withUri($u3);
$this->assertSame($r3, $r4, 'If the Request did not change, then there is no need to create a new request object');
$u4 = new Uri('/');
$r5 = $r3->withUri($u4);
$this->assertNotSame($r3, $r5);
}
public function testSameInstanceWhenSameUri()
{
$r1 = new Request('GET', 'http://foo.com');
$r2 = $r1->withUri($r1->getUri());
$this->assertSame($r1, $r2);
}
public function testWithRequestTarget()
{
$r1 = new Request('GET', '/');
$r2 = $r1->withRequestTarget('*');
$this->assertEquals('*', $r2->getRequestTarget());
$this->assertEquals('/', $r1->getRequestTarget());
}
public function testWithInvalidRequestTarget()
{
$r = new Request('GET', '/');
$this->expectException(\InvalidArgumentException::class);
$r->withRequestTarget('foo bar');
}
public function testGetRequestTarget()
{
$r = new Request('GET', 'https://nyholm.tech');
$this->assertEquals('/', $r->getRequestTarget());
$r = new Request('GET', 'https://nyholm.tech/foo?bar=baz');
$this->assertEquals('/foo?bar=baz', $r->getRequestTarget());
$r = new Request('GET', 'https://nyholm.tech?bar=baz');
$this->assertEquals('/?bar=baz', $r->getRequestTarget());
}
public function testRequestTargetDoesNotAllowSpaces()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Invalid request target provided; cannot contain whitespace');
$r1 = new Request('GET', '/');
$r1->withRequestTarget('/foo bar');
}
public function testRequestTargetDefaultsToSlash()
{
$r1 = new Request('GET', '');
$this->assertEquals('/', $r1->getRequestTarget());
$r2 = new Request('GET', '*');
$this->assertEquals('*', $r2->getRequestTarget());
$r3 = new Request('GET', 'http://foo.com/bar baz/');
$this->assertEquals('/bar%20baz/', $r3->getRequestTarget());
}
public function testBuildsRequestTarget()
{
$r1 = new Request('GET', 'http://foo.com/baz?bar=bam');
$this->assertEquals('/baz?bar=bam', $r1->getRequestTarget());
}
public function testBuildsRequestTargetWithFalseyQuery()
{
$r1 = new Request('GET', 'http://foo.com/baz?0');
$this->assertEquals('/baz?0', $r1->getRequestTarget());
}
public function testHostIsAddedFirst()
{
$r = new Request('GET', 'http://foo.com/baz?bar=bam', ['Foo' => 'Bar']);
$this->assertEquals([
'Host' => ['foo.com'],
'Foo' => ['Bar'],
], $r->getHeaders());
}
public function testCanGetHeaderAsCsv()
{
$r = new Request('GET', 'http://foo.com/baz?bar=bam', [
'Foo' => ['a', 'b', 'c'],
]);
$this->assertEquals('a, b, c', $r->getHeaderLine('Foo'));
$this->assertEquals('', $r->getHeaderLine('Bar'));
}
public function testHostIsNotOverwrittenWhenPreservingHost()
{
$r = new Request('GET', 'http://foo.com/baz?bar=bam', ['Host' => 'a.com']);
$this->assertEquals(['Host' => ['a.com']], $r->getHeaders());
$r2 = $r->withUri(new Uri('http://www.foo.com/bar'), true);
$this->assertEquals('a.com', $r2->getHeaderLine('Host'));
}
public function testOverridesHostWithUri()
{
$r = new Request('GET', 'http://foo.com/baz?bar=bam');
$this->assertEquals(['Host' => ['foo.com']], $r->getHeaders());
$r2 = $r->withUri(new Uri('http://www.baz.com/bar'));
$this->assertEquals('www.baz.com', $r2->getHeaderLine('Host'));
}
public function testAggregatesHeaders()
{
$r = new Request('GET', '', [
'ZOO' => 'zoobar',
'zoo' => ['foobar', 'zoobar'],
]);
$this->assertEquals(['ZOO' => ['zoobar', 'foobar', 'zoobar']], $r->getHeaders());
$this->assertEquals('zoobar, foobar, zoobar', $r->getHeaderLine('zoo'));
}
public function testSupportNumericHeaders()
{
$r = new Request('GET', '', [
'Content-Length' => 200,
]);
$this->assertSame(['Content-Length' => ['200']], $r->getHeaders());
$this->assertSame('200', $r->getHeaderLine('Content-Length'));
}
public function testAddsPortToHeader()
{
$r = new Request('GET', 'http://foo.com:8124/bar');
$this->assertEquals('foo.com:8124', $r->getHeaderLine('host'));
}
public function testAddsPortToHeaderAndReplacePreviousPort()
{
$r = new Request('GET', 'http://foo.com:8124/bar');
$r = $r->withUri(new Uri('http://foo.com:8125/bar'));
$this->assertEquals('foo.com:8125', $r->getHeaderLine('host'));
}
public function testCannotHaveHeaderWithEmptyName()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Header name must be an RFC 7230 compatible string.');
$r = new Request('GET', 'https://example.com/');
$r->withHeader('', 'Bar');
}
public function testCanHaveHeaderWithEmptyValue()
{
$r = new Request('GET', 'https://example.com/');
$r = $r->withHeader('Foo', '');
$this->assertEquals([''], $r->getHeader('Foo'));
}
public function testUpdateHostFromUri()
{
$request = new Request('GET', '/');
$request = $request->withUri(new Uri('https://nyholm.tech'));
$this->assertEquals('nyholm.tech', $request->getHeaderLine('Host'));
$request = new Request('GET', 'https://example.com/');
$this->assertEquals('example.com', $request->getHeaderLine('Host'));
$request = $request->withUri(new Uri('https://nyholm.tech'));
$this->assertEquals('nyholm.tech', $request->getHeaderLine('Host'));
$request = new Request('GET', '/');
$request = $request->withUri(new Uri('https://nyholm.tech:8080'));
$this->assertEquals('nyholm.tech:8080', $request->getHeaderLine('Host'));
$request = new Request('GET', '/');
$request = $request->withUri(new Uri('https://nyholm.tech:443'));
$this->assertEquals('nyholm.tech', $request->getHeaderLine('Host'));
}
}
php-nyholm-psr7-1.2.1/tests/Resources/ 0000775 0000000 0000000 00000000000 13535331707 0017622 5 ustar 00root root 0000000 0000000 php-nyholm-psr7-1.2.1/tests/Resources/foo.txt 0000664 0000000 0000000 00000000007 13535331707 0021143 0 ustar 00root root 0000000 0000000 Foobar
php-nyholm-psr7-1.2.1/tests/ResponseTest.php 0000664 0000000 0000000 00000022236 13535331707 0021024 0 ustar 00root root 0000000 0000000 assertSame(200, $r->getStatusCode());
$this->assertSame('1.1', $r->getProtocolVersion());
$this->assertSame('OK', $r->getReasonPhrase());
$this->assertSame([], $r->getHeaders());
$this->assertInstanceOf(StreamInterface::class, $r->getBody());
$this->assertSame('', (string) $r->getBody());
}
public function testCanConstructWithStatusCode()
{
$r = new Response(404);
$this->assertSame(404, $r->getStatusCode());
$this->assertSame('Not Found', $r->getReasonPhrase());
}
public function testCanConstructWithUndefinedStatusCode()
{
$r = new Response(999);
$this->assertSame(999, $r->getStatusCode());
$this->assertSame('', $r->getReasonPhrase());
}
public function testCanConstructWithStatusCodeAndEmptyReason()
{
$r = new Response(404, [], null, '1.1', '');
$this->assertSame(404, $r->getStatusCode());
$this->assertSame('', $r->getReasonPhrase());
}
public function testConstructorDoesNotReadStreamBody()
{
$body = $this->getMockBuilder(StreamInterface::class)->getMock();
$body->expects($this->never())
->method('__toString');
$r = new Response(200, [], $body);
$this->assertSame($body, $r->getBody());
}
public function testStatusCanBeNumericString()
{
$r = new Response('404');
$r2 = $r->withStatus('201');
$this->assertSame(404, $r->getStatusCode());
$this->assertSame('Not Found', $r->getReasonPhrase());
$this->assertSame(201, $r2->getStatusCode());
$this->assertSame('Created', $r2->getReasonPhrase());
}
public function testCanConstructWithHeaders()
{
$r = new Response(200, ['Foo' => 'Bar']);
$this->assertSame(['Foo' => ['Bar']], $r->getHeaders());
$this->assertSame('Bar', $r->getHeaderLine('Foo'));
$this->assertSame(['Bar'], $r->getHeader('Foo'));
}
public function testCanConstructWithHeadersAsArray()
{
$r = new Response(200, [
'Foo' => ['baz', 'bar'],
]);
$this->assertSame(['Foo' => ['baz', 'bar']], $r->getHeaders());
$this->assertSame('baz, bar', $r->getHeaderLine('Foo'));
$this->assertSame(['baz', 'bar'], $r->getHeader('Foo'));
}
public function testCanConstructWithBody()
{
$r = new Response(200, [], 'baz');
$this->assertInstanceOf(StreamInterface::class, $r->getBody());
$this->assertSame('baz', (string) $r->getBody());
}
public function testNullBody()
{
$r = new Response(200, [], null);
$this->assertInstanceOf(StreamInterface::class, $r->getBody());
$this->assertSame('', (string) $r->getBody());
}
public function testFalseyBody()
{
$r = new Response(200, [], '0');
$this->assertInstanceOf(StreamInterface::class, $r->getBody());
$this->assertSame('0', (string) $r->getBody());
}
public function testCanConstructWithReason()
{
$r = new Response(200, [], null, '1.1', 'bar');
$this->assertSame('bar', $r->getReasonPhrase());
$r = new Response(200, [], null, '1.1', '0');
$this->assertSame('0', $r->getReasonPhrase(), 'Falsey reason works');
}
public function testCanConstructWithProtocolVersion()
{
$r = new Response(200, [], null, '1000');
$this->assertSame('1000', $r->getProtocolVersion());
}
public function testWithStatusCodeAndNoReason()
{
$r = (new Response())->withStatus(201);
$this->assertSame(201, $r->getStatusCode());
$this->assertSame('Created', $r->getReasonPhrase());
}
public function testWithStatusCodeAndReason()
{
$r = (new Response())->withStatus(201, 'Foo');
$this->assertSame(201, $r->getStatusCode());
$this->assertSame('Foo', $r->getReasonPhrase());
$r = (new Response())->withStatus(201, '0');
$this->assertSame(201, $r->getStatusCode());
$this->assertSame('0', $r->getReasonPhrase(), 'Falsey reason works');
}
public function testWithProtocolVersion()
{
$r = (new Response())->withProtocolVersion('1000');
$this->assertSame('1000', $r->getProtocolVersion());
}
public function testSameInstanceWhenSameProtocol()
{
$r = new Response();
$this->assertSame($r, $r->withProtocolVersion('1.1'));
}
public function testWithBody()
{
$b = (new \Nyholm\Psr7\Factory\Psr17Factory())->createStream('0');
$r = (new Response())->withBody($b);
$this->assertInstanceOf(StreamInterface::class, $r->getBody());
$this->assertSame('0', (string) $r->getBody());
}
public function testSameInstanceWhenSameBody()
{
$r = new Response();
$b = $r->getBody();
$this->assertSame($r, $r->withBody($b));
}
public function testWithHeader()
{
$r = new Response(200, ['Foo' => 'Bar']);
$r2 = $r->withHeader('baZ', 'Bam');
$this->assertSame(['Foo' => ['Bar']], $r->getHeaders());
$this->assertSame(['Foo' => ['Bar'], 'baZ' => ['Bam']], $r2->getHeaders());
$this->assertSame('Bam', $r2->getHeaderLine('baz'));
$this->assertSame(['Bam'], $r2->getHeader('baz'));
}
public function testWithHeaderAsArray()
{
$r = new Response(200, ['Foo' => 'Bar']);
$r2 = $r->withHeader('baZ', ['Bam', 'Bar']);
$this->assertSame(['Foo' => ['Bar']], $r->getHeaders());
$this->assertSame(['Foo' => ['Bar'], 'baZ' => ['Bam', 'Bar']], $r2->getHeaders());
$this->assertSame('Bam, Bar', $r2->getHeaderLine('baz'));
$this->assertSame(['Bam', 'Bar'], $r2->getHeader('baz'));
}
public function testWithHeaderReplacesDifferentCase()
{
$r = new Response(200, ['Foo' => 'Bar']);
$r2 = $r->withHeader('foO', 'Bam');
$this->assertSame(['Foo' => ['Bar']], $r->getHeaders());
$this->assertSame(['foO' => ['Bam']], $r2->getHeaders());
$this->assertSame('Bam', $r2->getHeaderLine('foo'));
$this->assertSame(['Bam'], $r2->getHeader('foo'));
}
public function testWithAddedHeader()
{
$r = new Response(200, ['Foo' => 'Bar']);
$r2 = $r->withAddedHeader('foO', 'Baz');
$this->assertSame(['Foo' => ['Bar']], $r->getHeaders());
$this->assertSame(['Foo' => ['Bar', 'Baz']], $r2->getHeaders());
$this->assertSame('Bar, Baz', $r2->getHeaderLine('foo'));
$this->assertSame(['Bar', 'Baz'], $r2->getHeader('foo'));
}
public function testWithAddedHeaderAsArray()
{
$r = new Response(200, ['Foo' => 'Bar']);
$r2 = $r->withAddedHeader('foO', ['Baz', 'Bam']);
$this->assertSame(['Foo' => ['Bar']], $r->getHeaders());
$this->assertSame(['Foo' => ['Bar', 'Baz', 'Bam']], $r2->getHeaders());
$this->assertSame('Bar, Baz, Bam', $r2->getHeaderLine('foo'));
$this->assertSame(['Bar', 'Baz', 'Bam'], $r2->getHeader('foo'));
}
public function testWithAddedHeaderThatDoesNotExist()
{
$r = new Response(200, ['Foo' => 'Bar']);
$r2 = $r->withAddedHeader('nEw', 'Baz');
$this->assertSame(['Foo' => ['Bar']], $r->getHeaders());
$this->assertSame(['Foo' => ['Bar'], 'nEw' => ['Baz']], $r2->getHeaders());
$this->assertSame('Baz', $r2->getHeaderLine('new'));
$this->assertSame(['Baz'], $r2->getHeader('new'));
}
public function testWithoutHeaderThatExists()
{
$r = new Response(200, ['Foo' => 'Bar', 'Baz' => 'Bam']);
$r2 = $r->withoutHeader('foO');
$this->assertTrue($r->hasHeader('foo'));
$this->assertSame(['Foo' => ['Bar'], 'Baz' => ['Bam']], $r->getHeaders());
$this->assertFalse($r2->hasHeader('foo'));
$this->assertSame(['Baz' => ['Bam']], $r2->getHeaders());
}
public function testWithoutHeaderThatDoesNotExist()
{
$r = new Response(200, ['Baz' => 'Bam']);
$r2 = $r->withoutHeader('foO');
$this->assertSame($r, $r2);
$this->assertFalse($r2->hasHeader('foo'));
$this->assertSame(['Baz' => ['Bam']], $r2->getHeaders());
}
public function testSameInstanceWhenRemovingMissingHeader()
{
$r = new Response();
$this->assertSame($r, $r->withoutHeader('foo'));
}
public function trimmedHeaderValues()
{
return [
[new Response(200, ['OWS' => " \t \tFoo\t \t "])],
[(new Response())->withHeader('OWS', " \t \tFoo\t \t ")],
[(new Response())->withAddedHeader('OWS', " \t \tFoo\t \t ")],
];
}
/**
* @dataProvider trimmedHeaderValues
*/
public function testHeaderValuesAreTrimmed($r)
{
$this->assertSame(['OWS' => ['Foo']], $r->getHeaders());
$this->assertSame('Foo', $r->getHeaderLine('OWS'));
$this->assertSame(['Foo'], $r->getHeader('OWS'));
}
}
php-nyholm-psr7-1.2.1/tests/ServerRequestTest.php 0000664 0000000 0000000 00000007250 13535331707 0022044 0 ustar 00root root 0000000 0000000 new UploadedFile('test', 123, UPLOAD_ERR_OK),
];
$request2 = $request1->withUploadedFiles($files);
$this->assertNotSame($request2, $request1);
$this->assertSame([], $request1->getUploadedFiles());
$this->assertSame($files, $request2->getUploadedFiles());
}
public function testServerParams()
{
$params = ['name' => 'value'];
$request = new ServerRequest('GET', '/', [], null, '1.1', $params);
$this->assertSame($params, $request->getServerParams());
}
public function testCookieParams()
{
$request1 = new ServerRequest('GET', '/');
$params = ['name' => 'value'];
$request2 = $request1->withCookieParams($params);
$this->assertNotSame($request2, $request1);
$this->assertEmpty($request1->getCookieParams());
$this->assertSame($params, $request2->getCookieParams());
}
public function testQueryParams()
{
$request1 = new ServerRequest('GET', '/');
$params = ['name' => 'value'];
$request2 = $request1->withQueryParams($params);
$this->assertNotSame($request2, $request1);
$this->assertEmpty($request1->getQueryParams());
$this->assertSame($params, $request2->getQueryParams());
}
public function testParsedBody()
{
$request1 = new ServerRequest('GET', '/');
$params = ['name' => 'value'];
$request2 = $request1->withParsedBody($params);
$this->assertNotSame($request2, $request1);
$this->assertEmpty($request1->getParsedBody());
$this->assertSame($params, $request2->getParsedBody());
}
public function testAttributes()
{
$request1 = new ServerRequest('GET', '/');
$request2 = $request1->withAttribute('name', 'value');
$request3 = $request2->withAttribute('other', 'otherValue');
$request4 = $request3->withoutAttribute('other');
$request5 = $request3->withoutAttribute('unknown');
$this->assertNotSame($request2, $request1);
$this->assertNotSame($request3, $request2);
$this->assertNotSame($request4, $request3);
$this->assertNotSame($request5, $request4);
$this->assertEmpty($request1->getAttributes());
$this->assertEmpty($request1->getAttribute('name'));
$this->assertEquals(
'something',
$request1->getAttribute('name', 'something'),
'Should return the default value'
);
$this->assertEquals('value', $request2->getAttribute('name'));
$this->assertEquals(['name' => 'value'], $request2->getAttributes());
$this->assertEquals(['name' => 'value', 'other' => 'otherValue'], $request3->getAttributes());
$this->assertEquals(['name' => 'value'], $request4->getAttributes());
}
public function testNullAttribute()
{
$request = (new ServerRequest('GET', '/'))->withAttribute('name', null);
$this->assertSame(['name' => null], $request->getAttributes());
$this->assertNull($request->getAttribute('name', 'different-default'));
$requestWithoutAttribute = $request->withoutAttribute('name');
$this->assertSame([], $requestWithoutAttribute->getAttributes());
$this->assertSame('different-default', $requestWithoutAttribute->getAttribute('name', 'different-default'));
}
}
php-nyholm-psr7-1.2.1/tests/StreamTest.php 0000664 0000000 0000000 00000012711 13535331707 0020456 0 ustar 00root root 0000000 0000000 assertTrue($stream->isReadable());
$this->assertTrue($stream->isWritable());
$this->assertTrue($stream->isSeekable());
$this->assertEquals('php://temp', $stream->getMetadata('uri'));
$this->assertIsArray($stream->getMetadata());
$this->assertEquals(4, $stream->getSize());
$this->assertFalse($stream->eof());
$stream->close();
}
public function testStreamClosesHandleOnDestruct()
{
$handle = fopen('php://temp', 'r');
$stream = Stream::create($handle);
unset($stream);
$this->assertFalse(is_resource($handle));
}
public function testConvertsToString()
{
$handle = fopen('php://temp', 'w+');
fwrite($handle, 'data');
$stream = Stream::create($handle);
$this->assertEquals('data', (string) $stream);
$this->assertEquals('data', (string) $stream);
$stream->close();
}
public function testGetsContents()
{
$handle = fopen('php://temp', 'w+');
fwrite($handle, 'data');
$stream = Stream::create($handle);
$this->assertEquals('', $stream->getContents());
$stream->seek(0);
$this->assertEquals('data', $stream->getContents());
$this->assertEquals('', $stream->getContents());
}
public function testChecksEof()
{
$handle = fopen('php://temp', 'w+');
fwrite($handle, 'data');
$stream = Stream::create($handle);
$this->assertFalse($stream->eof());
$stream->read(4);
$this->assertTrue($stream->eof());
$stream->close();
}
public function testGetSize()
{
$size = filesize(__FILE__);
$handle = fopen(__FILE__, 'r');
$stream = Stream::create($handle);
$this->assertEquals($size, $stream->getSize());
// Load from cache
$this->assertEquals($size, $stream->getSize());
$stream->close();
}
public function testEnsuresSizeIsConsistent()
{
$h = fopen('php://temp', 'w+');
$this->assertEquals(3, fwrite($h, 'foo'));
$stream = Stream::create($h);
$this->assertEquals(3, $stream->getSize());
$this->assertEquals(4, $stream->write('test'));
$this->assertEquals(7, $stream->getSize());
$this->assertEquals(7, $stream->getSize());
$stream->close();
}
public function testProvidesStreamPosition()
{
$handle = fopen('php://temp', 'w+');
$stream = Stream::create($handle);
$this->assertEquals(0, $stream->tell());
$stream->write('foo');
$this->assertEquals(3, $stream->tell());
$stream->seek(1);
$this->assertEquals(1, $stream->tell());
$this->assertSame(ftell($handle), $stream->tell());
$stream->close();
}
public function testCanDetachStream()
{
$r = fopen('php://temp', 'w+');
$stream = Stream::create($r);
$stream->write('foo');
$this->assertTrue($stream->isReadable());
$this->assertSame($r, $stream->detach());
$stream->detach();
$this->assertFalse($stream->isReadable());
$this->assertFalse($stream->isWritable());
$this->assertFalse($stream->isSeekable());
$throws = function (callable $fn) use ($stream) {
try {
$fn($stream);
$this->fail();
} catch (\Exception $e) {
// Suppress the exception
}
};
$throws(function ($stream) {
$stream->read(10);
});
$throws(function ($stream) {
$stream->write('bar');
});
$throws(function ($stream) {
$stream->seek(10);
});
$throws(function ($stream) {
$stream->tell();
});
$throws(function ($stream) {
$stream->eof();
});
$throws(function ($stream) {
$stream->getSize();
});
$throws(function ($stream) {
$stream->getContents();
});
$this->assertSame('', (string) $stream);
$stream->close();
}
public function testCloseClearProperties()
{
$handle = fopen('php://temp', 'r+');
$stream = Stream::create($handle);
$stream->close();
$this->assertFalse($stream->isSeekable());
$this->assertFalse($stream->isReadable());
$this->assertFalse($stream->isWritable());
$this->assertNull($stream->getSize());
$this->assertEmpty($stream->getMetadata());
}
public function testUnseekableStreamWrapper()
{
stream_wrapper_register('nyholm-psr7-test', TestStreamWrapper::class);
$handle = fopen('nyholm-psr7-test://', 'r');
stream_wrapper_unregister('nyholm-psr7-test');
$stream = Stream::create($handle);
$this->assertFalse($stream->isSeekable());
}
}
class TestStreamWrapper
{
public $context;
public function stream_open()
{
return true;
}
public function stream_seek(int $offset, int $whence = SEEK_SET)
{
return false;
}
public function stream_eof()
{
return true;
}
}
php-nyholm-psr7-1.2.1/tests/UploadedFileTest.php 0000664 0000000 0000000 00000020016 13535331707 0021555 0 ustar 00root root 0000000 0000000 cleanup = [];
}
public function tearDown()
{
foreach ($this->cleanup as $file) {
if (is_scalar($file) && file_exists($file)) {
unlink($file);
}
}
}
public function invalidStreams()
{
return [
'null' => [null],
'true' => [true],
'false' => [false],
'int' => [1],
'float' => [1.1],
'array' => [['filename']],
'object' => [(object) ['filename']],
];
}
/**
* @dataProvider invalidStreams
*/
public function testRaisesExceptionOnInvalidStreamOrFile($streamOrFile)
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Invalid stream or file provided for UploadedFile');
new UploadedFile($streamOrFile, 0, UPLOAD_ERR_OK);
}
public function invalidErrorStatuses()
{
return [
'null' => [null],
'true' => [true],
'false' => [false],
'float' => [1.1],
'string' => ['1'],
'array' => [[1]],
'object' => [(object) [1]],
'negative' => [-1],
'too-big' => [9],
];
}
/**
* @dataProvider invalidErrorStatuses
*/
public function testRaisesExceptionOnInvalidErrorStatus($status)
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('status');
new UploadedFile(fopen('php://temp', 'wb+'), 0, $status);
}
public function invalidFilenamesAndMediaTypes()
{
return [
'true' => [true],
'false' => [false],
'int' => [1],
'float' => [1.1],
'array' => [['string']],
'object' => [(object) ['string']],
];
}
/**
* @dataProvider invalidFilenamesAndMediaTypes
*/
public function testRaisesExceptionOnInvalidClientFilename($filename)
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('filename');
new UploadedFile(fopen('php://temp', 'wb+'), 0, UPLOAD_ERR_OK, $filename);
}
/**
* @dataProvider invalidFilenamesAndMediaTypes
*/
public function testRaisesExceptionOnInvalidClientMediaType($mediaType)
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('media type');
new UploadedFile(fopen('php://temp', 'wb+'), 0, UPLOAD_ERR_OK, 'foobar.baz', $mediaType);
}
public function testGetStreamReturnsOriginalStreamObject()
{
$stream = Stream::create('');
$upload = new UploadedFile($stream, 0, UPLOAD_ERR_OK);
$this->assertSame($stream, $upload->getStream());
}
public function testGetStreamReturnsWrappedPhpStream()
{
$stream = fopen('php://temp', 'wb+');
$upload = new UploadedFile($stream, 0, UPLOAD_ERR_OK);
$uploadStream = $upload->getStream()->detach();
$this->assertSame($stream, $uploadStream);
}
public function testGetStream()
{
$upload = new UploadedFile(__DIR__.'/Resources/foo.txt', 0, UPLOAD_ERR_OK);
$stream = $upload->getStream();
$this->assertInstanceOf(StreamInterface::class, $stream);
$this->assertEquals("Foobar\n", $stream->__toString());
}
public function testSuccessful()
{
$stream = Stream::create('Foo bar!');
$upload = new UploadedFile($stream, $stream->getSize(), UPLOAD_ERR_OK, 'filename.txt', 'text/plain');
$this->assertEquals($stream->getSize(), $upload->getSize());
$this->assertEquals('filename.txt', $upload->getClientFilename());
$this->assertEquals('text/plain', $upload->getClientMediaType());
$this->cleanup[] = $to = tempnam(sys_get_temp_dir(), 'successful');
$upload->moveTo($to);
$this->assertFileExists($to);
$this->assertEquals($stream->__toString(), file_get_contents($to));
}
public function invalidMovePaths()
{
return [
'null' => [null],
'true' => [true],
'false' => [false],
'int' => [1],
'float' => [1.1],
'empty' => [''],
'array' => [['filename']],
'object' => [(object) ['filename']],
];
}
/**
* @dataProvider invalidMovePaths
*/
public function testMoveRaisesExceptionForInvalidPath($path)
{
$stream = (new \Nyholm\Psr7\Factory\Psr17Factory())->createStream('Foo bar!');
$upload = new UploadedFile($stream, 0, UPLOAD_ERR_OK);
$this->cleanup[] = $path;
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('path');
$upload->moveTo($path);
}
public function testMoveCannotBeCalledMoreThanOnce()
{
$stream = (new \Nyholm\Psr7\Factory\Psr17Factory())->createStream('Foo bar!');
$upload = new UploadedFile($stream, 0, UPLOAD_ERR_OK);
$this->cleanup[] = $to = tempnam(sys_get_temp_dir(), 'diac');
$upload->moveTo($to);
$this->assertTrue(file_exists($to));
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('moved');
$upload->moveTo($to);
}
public function testCannotRetrieveStreamAfterMove()
{
$stream = (new \Nyholm\Psr7\Factory\Psr17Factory())->createStream('Foo bar!');
$upload = new UploadedFile($stream, 0, UPLOAD_ERR_OK);
$this->cleanup[] = $to = tempnam(sys_get_temp_dir(), 'diac');
$upload->moveTo($to);
$this->assertFileExists($to);
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('moved');
$upload->getStream();
}
public function nonOkErrorStatus()
{
return [
'UPLOAD_ERR_INI_SIZE' => [UPLOAD_ERR_INI_SIZE],
'UPLOAD_ERR_FORM_SIZE' => [UPLOAD_ERR_FORM_SIZE],
'UPLOAD_ERR_PARTIAL' => [UPLOAD_ERR_PARTIAL],
'UPLOAD_ERR_NO_FILE' => [UPLOAD_ERR_NO_FILE],
'UPLOAD_ERR_NO_TMP_DIR' => [UPLOAD_ERR_NO_TMP_DIR],
'UPLOAD_ERR_CANT_WRITE' => [UPLOAD_ERR_CANT_WRITE],
'UPLOAD_ERR_EXTENSION' => [UPLOAD_ERR_EXTENSION],
];
}
/**
* @dataProvider nonOkErrorStatus
*/
public function testConstructorDoesNotRaiseExceptionForInvalidStreamWhenErrorStatusPresent($status)
{
$uploadedFile = new UploadedFile('not ok', 0, $status);
$this->assertSame($status, $uploadedFile->getError());
}
/**
* @dataProvider nonOkErrorStatus
*/
public function testMoveToRaisesExceptionWhenErrorStatusPresent($status)
{
$uploadedFile = new UploadedFile('not ok', 0, $status);
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('upload error');
$uploadedFile->moveTo(__DIR__.'/'.uniqid());
}
/**
* @dataProvider nonOkErrorStatus
*/
public function testGetStreamRaisesExceptionWhenErrorStatusPresent($status)
{
$uploadedFile = new UploadedFile('not ok', 0, $status);
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('upload error');
$uploadedFile->getStream();
}
public function testMoveToCreatesStreamIfOnlyAFilenameWasProvided()
{
$this->cleanup[] = $from = tempnam(sys_get_temp_dir(), 'copy_from');
$this->cleanup[] = $to = tempnam(sys_get_temp_dir(), 'copy_to');
copy(__FILE__, $from);
$uploadedFile = new UploadedFile($from, 100, UPLOAD_ERR_OK, basename($from), 'text/plain');
$uploadedFile->moveTo($to);
$this->assertFileEquals(__FILE__, $to);
}
}
php-nyholm-psr7-1.2.1/tests/UriTest.php 0000664 0000000 0000000 00000044475 13535331707 0017776 0 ustar 00root root 0000000 0000000 assertSame('https', $uri->getScheme());
$this->assertSame('user:pass@example.com:8080', $uri->getAuthority());
$this->assertSame('user:pass', $uri->getUserInfo());
$this->assertSame('example.com', $uri->getHost());
$this->assertSame(8080, $uri->getPort());
$this->assertSame('/path/123', $uri->getPath());
$this->assertSame('q=abc', $uri->getQuery());
$this->assertSame('test', $uri->getFragment());
$this->assertSame('https://user:pass@example.com:8080/path/123?q=abc#test', (string) $uri);
}
public function testCanTransformAndRetrievePartsIndividually()
{
$uri = (new Uri())
->withScheme('https')
->withUserInfo('user', 'pass')
->withHost('example.com')
->withPort(8080)
->withPath('/path/123')
->withQuery('q=abc')
->withFragment('test');
$this->assertSame('https', $uri->getScheme());
$this->assertSame('user:pass@example.com:8080', $uri->getAuthority());
$this->assertSame('user:pass', $uri->getUserInfo());
$this->assertSame('example.com', $uri->getHost());
$this->assertSame(8080, $uri->getPort());
$this->assertSame('/path/123', $uri->getPath());
$this->assertSame('q=abc', $uri->getQuery());
$this->assertSame('test', $uri->getFragment());
$this->assertSame('https://user:pass@example.com:8080/path/123?q=abc#test', (string) $uri);
}
/**
* @dataProvider getValidUris
*/
public function testValidUrisStayValid($input)
{
$uri = new Uri($input);
$this->assertSame($input, (string) $uri);
}
public function getValidUris()
{
return [
['urn:path-rootless'],
['urn:path:with:colon'],
['urn:/path-absolute'],
['urn:/'],
// only scheme with empty path
['urn:'],
// only path
['/'],
['relative/'],
['0'],
// same document reference
[''],
// network path without scheme
['//example.org'],
['//example.org/'],
['//example.org?q#h'],
// only query
['?q'],
['?q=abc&foo=bar'],
// only fragment
['#fragment'],
// dot segments are not removed automatically
['./foo/../bar'],
];
}
/**
* @dataProvider getInvalidUris
*/
public function testInvalidUrisThrowException($invalidUri)
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Unable to parse URI');
new Uri($invalidUri);
}
public function getInvalidUris()
{
return [
// parse_url() requires the host component which makes sense for http(s)
// but not when the scheme is not known or different. So '//' or '///' is
// currently invalid as well but should not according to RFC 3986.
['http://'],
['urn://host:with:colon'], // host cannot contain ":"
];
}
public function testPortMustBeValid()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Invalid port: 100000. Must be between 0 and 65535');
(new Uri())->withPort(100000);
}
public function testWithPortCannotBeNegative()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Invalid port: -1. Must be between 0 and 65535');
(new Uri())->withPort(-1);
}
public function testParseUriPortCannotBeZero()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Unable to parse URI');
new Uri('//example.com:0');
}
public function testSchemeMustHaveCorrectType()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Scheme must be a string');
(new Uri())->withScheme([]);
}
public function testHostMustHaveCorrectType()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Host must be a string');
(new Uri())->withHost([]);
}
public function testPathMustHaveCorrectType()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Path must be a string');
(new Uri())->withPath([]);
}
public function testQueryMustHaveCorrectType()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Query and fragment must be a string');
(new Uri())->withQuery([]);
}
public function testFragmentMustHaveCorrectType()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Query and fragment must be a string');
(new Uri())->withFragment([]);
}
public function testCanParseFalseyUriParts()
{
$uri = new Uri('0://0:0@0/0?0#0');
$this->assertSame('0', $uri->getScheme());
$this->assertSame('0:0@0', $uri->getAuthority());
$this->assertSame('0:0', $uri->getUserInfo());
$this->assertSame('0', $uri->getHost());
$this->assertSame('/0', $uri->getPath());
$this->assertSame('0', $uri->getQuery());
$this->assertSame('0', $uri->getFragment());
$this->assertSame('0://0:0@0/0?0#0', (string) $uri);
}
public function testCanConstructFalseyUriParts()
{
$uri = (new Uri())
->withScheme('0')
->withUserInfo('0', '0')
->withHost('0')
->withPath('/0')
->withQuery('0')
->withFragment('0');
$this->assertSame('0', $uri->getScheme());
$this->assertSame('0:0@0', $uri->getAuthority());
$this->assertSame('0:0', $uri->getUserInfo());
$this->assertSame('0', $uri->getHost());
$this->assertSame('/0', $uri->getPath());
$this->assertSame('0', $uri->getQuery());
$this->assertSame('0', $uri->getFragment());
$this->assertSame('0://0:0@0/0?0#0', (string) $uri);
}
public function getResolveTestCases()
{
return [
[self::RFC3986_BASE, 'g:h', 'g:h'],
[self::RFC3986_BASE, 'g', 'http://a/b/c/g'],
[self::RFC3986_BASE, './g', 'http://a/b/c/g'],
[self::RFC3986_BASE, 'g/', 'http://a/b/c/g/'],
[self::RFC3986_BASE, '/g', 'http://a/g'],
[self::RFC3986_BASE, '//g', 'http://g'],
[self::RFC3986_BASE, '?y', 'http://a/b/c/d;p?y'],
[self::RFC3986_BASE, 'g?y', 'http://a/b/c/g?y'],
[self::RFC3986_BASE, '#s', 'http://a/b/c/d;p?q#s'],
[self::RFC3986_BASE, 'g#s', 'http://a/b/c/g#s'],
[self::RFC3986_BASE, 'g?y#s', 'http://a/b/c/g?y#s'],
[self::RFC3986_BASE, ';x', 'http://a/b/c/;x'],
[self::RFC3986_BASE, 'g;x', 'http://a/b/c/g;x'],
[self::RFC3986_BASE, 'g;x?y#s', 'http://a/b/c/g;x?y#s'],
[self::RFC3986_BASE, '', self::RFC3986_BASE],
[self::RFC3986_BASE, '.', 'http://a/b/c/'],
[self::RFC3986_BASE, './', 'http://a/b/c/'],
[self::RFC3986_BASE, '..', 'http://a/b/'],
[self::RFC3986_BASE, '../', 'http://a/b/'],
[self::RFC3986_BASE, '../g', 'http://a/b/g'],
[self::RFC3986_BASE, '../..', 'http://a/'],
[self::RFC3986_BASE, '../../', 'http://a/'],
[self::RFC3986_BASE, '../../g', 'http://a/g'],
[self::RFC3986_BASE, '../../../g', 'http://a/g'],
[self::RFC3986_BASE, '../../../../g', 'http://a/g'],
[self::RFC3986_BASE, '/./g', 'http://a/g'],
[self::RFC3986_BASE, '/../g', 'http://a/g'],
[self::RFC3986_BASE, 'g.', 'http://a/b/c/g.'],
[self::RFC3986_BASE, '.g', 'http://a/b/c/.g'],
[self::RFC3986_BASE, 'g..', 'http://a/b/c/g..'],
[self::RFC3986_BASE, '..g', 'http://a/b/c/..g'],
[self::RFC3986_BASE, './../g', 'http://a/b/g'],
[self::RFC3986_BASE, 'foo////g', 'http://a/b/c/foo////g'],
[self::RFC3986_BASE, './g/.', 'http://a/b/c/g/'],
[self::RFC3986_BASE, 'g/./h', 'http://a/b/c/g/h'],
[self::RFC3986_BASE, 'g/../h', 'http://a/b/c/h'],
[self::RFC3986_BASE, 'g;x=1/./y', 'http://a/b/c/g;x=1/y'],
[self::RFC3986_BASE, 'g;x=1/../y', 'http://a/b/c/y'],
// dot-segments in the query or fragment
[self::RFC3986_BASE, 'g?y/./x', 'http://a/b/c/g?y/./x'],
[self::RFC3986_BASE, 'g?y/../x', 'http://a/b/c/g?y/../x'],
[self::RFC3986_BASE, 'g#s/./x', 'http://a/b/c/g#s/./x'],
[self::RFC3986_BASE, 'g#s/../x', 'http://a/b/c/g#s/../x'],
[self::RFC3986_BASE, 'g#s/../x', 'http://a/b/c/g#s/../x'],
[self::RFC3986_BASE, '?y#s', 'http://a/b/c/d;p?y#s'],
['http://a/b/c/d;p?q#s', '?y', 'http://a/b/c/d;p?y'],
['http://u@a/b/c/d;p?q', '.', 'http://u@a/b/c/'],
['http://u:p@a/b/c/d;p?q', '.', 'http://u:p@a/b/c/'],
['http://a/b/c/d/', 'e', 'http://a/b/c/d/e'],
['urn:no-slash', 'e', 'urn:e'],
// falsey relative parts
[self::RFC3986_BASE, '//0', 'http://0'],
[self::RFC3986_BASE, '0', 'http://a/b/c/0'],
[self::RFC3986_BASE, '?0', 'http://a/b/c/d;p?0'],
[self::RFC3986_BASE, '#0', 'http://a/b/c/d;p?q#0'],
];
}
public function testSchemeIsNormalizedToLowercase()
{
$uri = new Uri('HTTP://example.com');
$this->assertSame('http', $uri->getScheme());
$this->assertSame('http://example.com', (string) $uri);
$uri = (new Uri('//example.com'))->withScheme('HTTP');
$this->assertSame('http', $uri->getScheme());
$this->assertSame('http://example.com', (string) $uri);
}
public function testHostIsNormalizedToLowercase()
{
$uri = new Uri('//eXaMpLe.CoM');
$this->assertSame('example.com', $uri->getHost());
$this->assertSame('//example.com', (string) $uri);
$uri = (new Uri())->withHost('eXaMpLe.CoM');
$this->assertSame('example.com', $uri->getHost());
$this->assertSame('//example.com', (string) $uri);
}
public function testPortIsNullIfStandardPortForScheme()
{
// HTTPS standard port
$uri = new Uri('https://example.com:443');
$this->assertNull($uri->getPort());
$this->assertSame('example.com', $uri->getAuthority());
$uri = (new Uri('https://example.com'))->withPort(443);
$this->assertNull($uri->getPort());
$this->assertSame('example.com', $uri->getAuthority());
// HTTP standard port
$uri = new Uri('http://example.com:80');
$this->assertNull($uri->getPort());
$this->assertSame('example.com', $uri->getAuthority());
$uri = (new Uri('http://example.com'))->withPort(80);
$this->assertNull($uri->getPort());
$this->assertSame('example.com', $uri->getAuthority());
}
public function testPortIsReturnedIfSchemeUnknown()
{
$uri = (new Uri('//example.com'))->withPort(80);
$this->assertSame(80, $uri->getPort());
$this->assertSame('example.com:80', $uri->getAuthority());
}
public function testStandardPortIsNullIfSchemeChanges()
{
$uri = new Uri('http://example.com:443');
$this->assertSame('http', $uri->getScheme());
$this->assertSame(443, $uri->getPort());
$uri = $uri->withScheme('https');
$this->assertNull($uri->getPort());
}
public function testPortPassedAsStringIsCastedToInt()
{
$uri = (new Uri('//example.com'))->withPort('8080');
$this->assertSame(8080, $uri->getPort(), 'Port is returned as integer');
$this->assertSame('example.com:8080', $uri->getAuthority());
}
public function testPortCanBeRemoved()
{
$uri = (new Uri('http://example.com:8080'))->withPort(null);
$this->assertNull($uri->getPort());
$this->assertSame('http://example.com', (string) $uri);
}
public function testAuthorityWithUserInfoButWithoutHost()
{
$uri = (new Uri())->withUserInfo('user', 'pass');
$this->assertSame('user:pass', $uri->getUserInfo());
$this->assertSame('', $uri->getAuthority());
}
public function uriComponentsEncodingProvider()
{
$unreserved = 'a-zA-Z0-9.-_~!$&\'()*+,;=:@';
return [
// Percent encode spaces
['/pa th?q=va lue#frag ment', '/pa%20th', 'q=va%20lue', 'frag%20ment', '/pa%20th?q=va%20lue#frag%20ment'],
// Percent encode multibyte
['/€?€#€', '/%E2%82%AC', '%E2%82%AC', '%E2%82%AC', '/%E2%82%AC?%E2%82%AC#%E2%82%AC'],
// Don't encode something that's already encoded
['/pa%20th?q=va%20lue#frag%20ment', '/pa%20th', 'q=va%20lue', 'frag%20ment', '/pa%20th?q=va%20lue#frag%20ment'],
// Percent encode invalid percent encodings
['/pa%2-th?q=va%2-lue#frag%2-ment', '/pa%252-th', 'q=va%252-lue', 'frag%252-ment', '/pa%252-th?q=va%252-lue#frag%252-ment'],
// Don't encode path segments
['/pa/th//two?q=va/lue#frag/ment', '/pa/th//two', 'q=va/lue', 'frag/ment', '/pa/th//two?q=va/lue#frag/ment'],
// Don't encode unreserved chars or sub-delimiters
["/$unreserved?$unreserved#$unreserved", "/$unreserved", $unreserved, $unreserved, "/$unreserved?$unreserved#$unreserved"],
// Encoded unreserved chars are not decoded
['/p%61th?q=v%61lue#fr%61gment', '/p%61th', 'q=v%61lue', 'fr%61gment', '/p%61th?q=v%61lue#fr%61gment'],
];
}
/**
* @dataProvider uriComponentsEncodingProvider
*/
public function testUriComponentsGetEncodedProperly($input, $path, $query, $fragment, $output)
{
$uri = new Uri($input);
$this->assertSame($path, $uri->getPath());
$this->assertSame($query, $uri->getQuery());
$this->assertSame($fragment, $uri->getFragment());
$this->assertSame($output, (string) $uri);
}
public function testWithPathEncodesProperly()
{
$uri = (new Uri())->withPath('/baz?#€/b%61r');
// Query and fragment delimiters and multibyte chars are encoded.
$this->assertSame('/baz%3F%23%E2%82%AC/b%61r', $uri->getPath());
$this->assertSame('/baz%3F%23%E2%82%AC/b%61r', (string) $uri);
}
public function testWithQueryEncodesProperly()
{
$uri = (new Uri())->withQuery('?=#&€=/&b%61r');
// A query starting with a "?" is valid and must not be magically removed. Otherwise it would be impossible to
// construct such an URI. Also the "?" and "/" does not need to be encoded in the query.
$this->assertSame('?=%23&%E2%82%AC=/&b%61r', $uri->getQuery());
$this->assertSame('??=%23&%E2%82%AC=/&b%61r', (string) $uri);
}
public function testWithFragmentEncodesProperly()
{
$uri = (new Uri())->withFragment('#€?/b%61r');
// A fragment starting with a "#" is valid and must not be magically removed. Otherwise it would be impossible to
// construct such an URI. Also the "?" and "/" does not need to be encoded in the fragment.
$this->assertSame('%23%E2%82%AC?/b%61r', $uri->getFragment());
$this->assertSame('#%23%E2%82%AC?/b%61r', (string) $uri);
}
public function testAllowsForRelativeUri()
{
$uri = (new Uri())->withPath('foo');
$this->assertSame('foo', $uri->getPath());
$this->assertSame('foo', (string) $uri);
}
public function testAddsSlashForRelativeUriStringWithHost()
{
// If the path is rootless and an authority is present, the path MUST
// be prefixed by "/".
$uri = (new Uri())->withPath('foo')->withHost('example.com');
$this->assertSame('foo', $uri->getPath());
// concatenating a relative path with a host doesn't work: "//example.comfoo" would be wrong
$this->assertSame('//example.com/foo', (string) $uri);
}
public function testRemoveExtraSlashesWihoutHost()
{
// If the path is starting with more than one "/" and no authority is
// present, the starting slashes MUST be reduced to one.
$uri = (new Uri())->withPath('//foo');
$this->assertSame('//foo', $uri->getPath());
// URI "//foo" would be interpreted as network reference and thus change the original path to the host
$this->assertSame('/foo', (string) $uri);
}
public function testDefaultReturnValuesOfGetters()
{
$uri = new Uri();
$this->assertSame('', $uri->getScheme());
$this->assertSame('', $uri->getAuthority());
$this->assertSame('', $uri->getUserInfo());
$this->assertSame('', $uri->getHost());
$this->assertNull($uri->getPort());
$this->assertSame('', $uri->getPath());
$this->assertSame('', $uri->getQuery());
$this->assertSame('', $uri->getFragment());
}
public function testImmutability()
{
$uri = new Uri();
$this->assertNotSame($uri, $uri->withScheme('https'));
$this->assertNotSame($uri, $uri->withUserInfo('user', 'pass'));
$this->assertNotSame($uri, $uri->withHost('example.com'));
$this->assertNotSame($uri, $uri->withPort(8080));
$this->assertNotSame($uri, $uri->withPath('/path/123'));
$this->assertNotSame($uri, $uri->withQuery('q=abc'));
$this->assertNotSame($uri, $uri->withFragment('test'));
}
}