diff --git a/.editorconfig b/.editorconfig index 2da7375..27e2667 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,8 +14,5 @@ insert_final_newline = true [*.md] trim_trailing_whitespace = false -[*.php] -indent_style = tab - [*.yml] indent_size = 2 diff --git a/.gitignore b/.gitignore index c331141..41cf726 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +.php_cs.cache +.phpunit.result.cache composer.lock coverage.xml +phpcs.xml +phpunit.xml vendor/ diff --git a/.travis.yml b/.travis.yml index 7b697c1..b0e5e8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,8 @@ matrix: - php: 7.1 - php: 7.2 - php: 7.3 + - php: 7.4 + - php: nightly fast_finish: true before_install: @@ -13,4 +15,4 @@ before_install: install: - travis_retry composer install --no-interaction --prefer-source --no-suggest -script: vendor/bin/phpunit --colors=always --coverage-text +script: php vendor/bin/phpunit --colors=always --coverage-text diff --git a/LICENSE b/LICENSE index 4c67e86..19aa2e3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 Anatoly Fenric +Copyright (c) 2018 Sunrise // PHP Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 0923ce7..776c7f3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -## HTTP Server Request wrapper for PHP 7.1+ based on PSR-7 & PSR-17 +## HTTP server request wrapper for PHP 7.1+ (incl. PHP 8) based on PSR-7 & PSR-17 [![Gitter](https://badges.gitter.im/sunrise-php/support.png)](https://gitter.im/sunrise-php/support) -[![Build Status](https://api.travis-ci.com/sunrise-php/http-server-request.svg?branch=master)](https://travis-ci.com/sunrise-php/http-server-request) +[![Build Status](https://scrutinizer-ci.com/g/sunrise-php/http-server-request/badges/build.png?b=master)](https://scrutinizer-ci.com/g/sunrise-php/http-server-request/build-status/master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/sunrise-php/http-server-request/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/sunrise-php/http-server-request/?branch=master) [![Code Coverage](https://scrutinizer-ci.com/g/sunrise-php/http-server-request/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/sunrise-php/http-server-request/?branch=master) [![Latest Stable Version](https://poser.pugx.org/sunrise/http-server-request/v/stable)](https://packagist.org/packages/sunrise/http-server-request) @@ -30,10 +30,6 @@ $request = ServerRequestFactory::fromGlobals(); php vendor/bin/phpunit ``` -## Api documentation - -https://phpdoc.fenric.ru/ - ## Useful links * https://www.php-fig.org/psr/psr-7/ diff --git a/composer.json b/composer.json index 601b8c8..958757d 100644 --- a/composer.json +++ b/composer.json @@ -1,9 +1,18 @@ { "name": "sunrise/http-server-request", - "description": "Sunrise HTTP Server Request wrapper for PHP 7.1+ based on PSR-7 & PSR-17", - "keywords": ["fenric", "sunrise", "http-server-request", "psr-7", "psr-17"], "homepage": "https://github.com/sunrise-php/http-server-request", + "description": "Sunrise HTTP server request wrapper for PHP 7.1+ based on PSR-7 & PSR-17", "license": "MIT", + "keywords": [ + "fenric", + "sunrise", + "http", + "http-server-request", + "psr-7", + "psr-17", + "php7", + "php8" + ], "authors": [ { "name": "Anatoly Fenric", @@ -17,15 +26,16 @@ } ], "require": { - "php": "^7.1", + "php": "^7.1|^8.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.0", - "sunrise/http-message": "^1.0", - "sunrise/stream": "^1.0", - "sunrise/uri": "^1.0" + "sunrise/http-message": "^1.4", + "sunrise/stream": "^1.2", + "sunrise/uri": "^1.2" }, "require-dev": { - "phpunit/phpunit": "7.5.6" + "phpunit/phpunit": "7.5.20|9.5.0", + "sunrise/coding-standard": "1.0.0" }, "provide": { "psr/http-message-implementation": "1.0" @@ -45,7 +55,8 @@ }, "scripts": { "test": [ - "phpunit --colors=always --coverage-text" + "phpunit --colors=always --coverage-text", + "phpcs" ] } } diff --git a/functions/request_body.php b/functions/request_body.php index dedcb47..9a10e44 100644 --- a/functions/request_body.php +++ b/functions/request_body.php @@ -28,11 +28,11 @@ */ function request_body() : StreamInterface { - $resource = \fopen('php://temp', 'r+b'); + $resource = \fopen('php://temp', 'r+b'); - \stream_copy_to_stream(\fopen('php://input', 'rb'), $resource); + \stream_copy_to_stream(\fopen('php://input', 'rb'), $resource); - \rewind($resource); + \rewind($resource); - return (new StreamFactory)->createStreamFromResource($resource); + return (new StreamFactory)->createStreamFromResource($resource); } diff --git a/functions/request_files.php b/functions/request_files.php index 73bdb57..2d0ef4c 100644 --- a/functions/request_files.php +++ b/functions/request_files.php @@ -34,35 +34,29 @@ */ function request_files(array $files) : array { - $walker = function($path, $size, $error, $name, $type) use(& $walker) - { - if (! \is_array($path)) - { - $stream = (new StreamFactory)->createStreamFromFile($path, 'rb'); + $walker = function ($path, $size, $error, $name, $type) use (&$walker) { + if (! \is_array($path)) { + $stream = (new StreamFactory)->createStreamFromFile($path, 'rb'); - return (new UploadedFileFactory)->createUploadedFile($stream, $size, $error, $name, $type); - } + return (new UploadedFileFactory)->createUploadedFile($stream, $size, $error, $name, $type); + } - $result = []; + $result = []; + foreach ($path as $key => $value) { + $result[$key] = $walker($path[$key], $size[$key], $error[$key], $name[$key], $type[$key]); + } - foreach ($path as $key => $value) - { - $result[$key] = $walker($path[$key], $size[$key], $error[$key], $name[$key], $type[$key]); - } + return $result; + }; - return $result; - }; + $result = []; + foreach ($files as $key => $file) { + if (UPLOAD_ERR_NO_FILE === $file['error']) { + continue; + } - $result = []; + $result[$key] = $walker($file['tmp_name'], $file['size'], $file['error'], $file['name'], $file['type']); + } - foreach ($files as $key => $file) - { - if (UPLOAD_ERR_NO_FILE === $file['error']) { - continue; - } - - $result[$key] = $walker($file['tmp_name'], $file['size'], $file['error'], $file['name'], $file['type']); - } - - return $result; + return $result; } diff --git a/functions/request_headers.php b/functions/request_headers.php index c207897..444b233 100644 --- a/functions/request_headers.php +++ b/functions/request_headers.php @@ -24,23 +24,20 @@ */ function request_headers(array $server) : array { - $result = []; + $result = []; + foreach ($server as $key => $value) { + if (! (0 === \strncmp('HTTP_', $key, 5))) { + continue; + } - foreach ($server as $key => $value) - { - if (! (0 === \strncmp('HTTP_', $key, 5))) - { - continue; - } + $name = \substr($key, 5); + $name = \strtolower($name); + $name = \strtr($name, '_', ' '); + $name = \ucwords($name); + $name = \strtr($name, ' ', '-'); - $name = \substr($key, 5); - $name = \strtolower($name); - $name = \strtr($name, '_', ' '); - $name = \ucwords($name); - $name = \strtr($name, ' ', '-'); + $result[$name] = $value; + } - $result[$name] = $value; - } - - return $result; + return $result; } diff --git a/functions/request_http_version.php b/functions/request_http_version.php index 2aa28db..7611b6b 100644 --- a/functions/request_http_version.php +++ b/functions/request_http_version.php @@ -24,15 +24,13 @@ */ function request_http_version(array $server) : string { - $regex = '/^HTTP\/(\d(?:\.\d)?)$/'; + $regex = '/^HTTP\/(\d(?:\.\d)?)$/'; - if (isset($server['SERVER_PROTOCOL'])) - { - if (\preg_match($regex, $server['SERVER_PROTOCOL'], $matches)) - { - return $matches[1]; - } - } + if (isset($server['SERVER_PROTOCOL'])) { + if (\preg_match($regex, $server['SERVER_PROTOCOL'], $matches)) { + return $matches[1]; + } + } - return '1.1'; + return '1.1'; } diff --git a/functions/request_method.php b/functions/request_method.php index c99b90c..1d2b085 100644 --- a/functions/request_method.php +++ b/functions/request_method.php @@ -24,5 +24,5 @@ */ function request_method(array $server) : string { - return $server['REQUEST_METHOD'] ?? 'GET'; + return $server['REQUEST_METHOD'] ?? 'GET'; } diff --git a/functions/request_uri.php b/functions/request_uri.php index 0f98c4d..8d5f273 100644 --- a/functions/request_uri.php +++ b/functions/request_uri.php @@ -30,45 +30,33 @@ */ function request_uri(array $server) : UriInterface { - if (\array_key_exists('HTTPS', $server)) - { - if (! ('off' === $server['HTTPS'])) - { - $scheme = 'https://'; - } - } - - if (\array_key_exists('HTTP_HOST', $server)) - { - $domain = $server['HTTP_HOST']; - } - else if (\array_key_exists('SERVER_NAME', $server)) - { - $domain = $server['SERVER_NAME']; - - if (\array_key_exists('SERVER_PORT', $server)) - { - $domain .= ':' . $server['SERVER_PORT']; - } - } - - if (\array_key_exists('REQUEST_URI', $server)) - { - $target = $server['REQUEST_URI']; - } - else if (\array_key_exists('PHP_SELF', $server)) - { - $target = $server['PHP_SELF']; - - if (\array_key_exists('QUERY_STRING', $server)) - { - $target .= '?' . $server['QUERY_STRING']; - } - } - - return (new UriFactory)->createUri( - ($scheme ?? 'http://') . - ($domain ?? 'localhost') . - ($target ?? '/') - ); + if (\array_key_exists('HTTPS', $server)) { + if (! ('off' === $server['HTTPS'])) { + $scheme = 'https://'; + } + } + + if (\array_key_exists('HTTP_HOST', $server)) { + $domain = $server['HTTP_HOST']; + } elseif (\array_key_exists('SERVER_NAME', $server)) { + $domain = $server['SERVER_NAME']; + if (\array_key_exists('SERVER_PORT', $server)) { + $domain .= ':' . $server['SERVER_PORT']; + } + } + + if (\array_key_exists('REQUEST_URI', $server)) { + $target = $server['REQUEST_URI']; + } elseif (\array_key_exists('PHP_SELF', $server)) { + $target = $server['PHP_SELF']; + if (\array_key_exists('QUERY_STRING', $server)) { + $target .= '?' . $server['QUERY_STRING']; + } + } + + return (new UriFactory)->createUri( + ($scheme ?? 'http://') . + ($domain ?? 'localhost') . + ($target ?? '/') + ); } diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..8b51380 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,8 @@ + + + + + functions + src + tests + diff --git a/src/ServerRequest.php b/src/ServerRequest.php index c630dfd..27ba5e1 100644 --- a/src/ServerRequest.php +++ b/src/ServerRequest.php @@ -26,205 +26,196 @@ class ServerRequest extends Request implements ServerRequestInterface { - /** - * The server parameters - * - * @var array - */ - protected $serverParams = []; - - /** - * The request cookie parameters - * - * @var array - */ - protected $cookieParams = []; - - /** - * The request query parameters - * - * @var array - */ - protected $queryParams = []; - - /** - * The request uploaded files - * - * @var array - */ - protected $uploadedFiles = []; - - /** - * The request parsed body - * - * @var mixed - */ - protected $parsedBody; - - /** - * The request attributes - * - * @var array - */ - protected $attributes = []; - - /** - * {@inheritDoc} - */ - public function getServerParams() : array - { - return $this->serverParams; - } - - /** - * Gets a new instance of the message with the given server parameters - * - * MUST NOT be used outside of this package. - * - * @param array $serverParams - * - * @return ServerRequestInterface - */ - public function withServerParams(array $serverParams) : ServerRequestInterface - { - $clone = clone $this; - - $clone->serverParams = $serverParams; - - return $clone; - } - - /** - * {@inheritDoc} - */ - public function getCookieParams() : array - { - return $this->cookieParams; - } - - /** - * {@inheritDoc} - */ - public function withCookieParams(array $cookieParams) : ServerRequestInterface - { - $clone = clone $this; - - $clone->cookieParams = $cookieParams; - - return $clone; - } - - /** - * {@inheritDoc} - */ - public function getQueryParams() : array - { - return $this->queryParams; - } - - /** - * {@inheritDoc} - */ - public function withQueryParams(array $queryParams) : ServerRequestInterface - { - $clone = clone $this; - - $clone->queryParams = $queryParams; - - return $clone; - } - - /** - * {@inheritDoc} - */ - public function getUploadedFiles() : array - { - return $this->uploadedFiles; - } - - /** - * {@inheritDoc} - */ - public function withUploadedFiles(array $uploadedFiles) : ServerRequestInterface - { - // Validates the given uploaded files structure - \array_walk_recursive($uploadedFiles, function($uploadedFile) - { - if (! ($uploadedFile instanceof UploadedFileInterface)) - { - throw new \InvalidArgumentException('Invalid uploaded files structure'); - } - }); - - $clone = clone $this; - - $clone->uploadedFiles = $uploadedFiles; - - return $clone; - } - - /** - * {@inheritDoc} - */ - public function getParsedBody() - { - return $this->parsedBody; - } - - /** - * {@inheritDoc} - */ - public function withParsedBody($parsedBody) : ServerRequestInterface - { - $clone = clone $this; - - $clone->parsedBody = $parsedBody; - - return $clone; - } - - /** - * {@inheritDoc} - */ - public function getAttributes() : array - { - return $this->attributes; - } - - /** - * {@inheritDoc} - */ - public function getAttribute($name, $default = null) - { - if (\array_key_exists($name, $this->attributes)) - { - return $this->attributes[$name]; - } - - return $default; - } - - /** - * {@inheritDoc} - */ - public function withAttribute($name, $value) : ServerRequestInterface - { - $clone = clone $this; - - $clone->attributes[$name] = $value; - - return $clone; - } - - /** - * {@inheritDoc} - */ - public function withoutAttribute($name) : ServerRequestInterface - { - $clone = clone $this; - - unset($clone->attributes[$name]); - - return $clone; - } + /** + * The server parameters + * + * @var array + */ + protected $serverParams = []; + + /** + * The request cookie parameters + * + * @var array + */ + protected $cookieParams = []; + + /** + * The request query parameters + * + * @var array + */ + protected $queryParams = []; + + /** + * The request uploaded files + * + * @var array + */ + protected $uploadedFiles = []; + + /** + * The request parsed body + * + * @var mixed + */ + protected $parsedBody; + + /** + * The request attributes + * + * @var array + */ + protected $attributes = []; + + /** + * {@inheritDoc} + */ + public function getServerParams() : array + { + return $this->serverParams; + } + + /** + * Gets a new instance of the message with the given server parameters + * + * MUST NOT be used outside of this package. + * + * @param array $serverParams + * + * @return ServerRequestInterface + */ + public function withServerParams(array $serverParams) : ServerRequestInterface + { + $clone = clone $this; + $clone->serverParams = $serverParams; + + return $clone; + } + + /** + * {@inheritDoc} + */ + public function getCookieParams() : array + { + return $this->cookieParams; + } + + /** + * {@inheritDoc} + */ + public function withCookieParams(array $cookieParams) : ServerRequestInterface + { + $clone = clone $this; + $clone->cookieParams = $cookieParams; + + return $clone; + } + + /** + * {@inheritDoc} + */ + public function getQueryParams() : array + { + return $this->queryParams; + } + + /** + * {@inheritDoc} + */ + public function withQueryParams(array $queryParams) : ServerRequestInterface + { + $clone = clone $this; + $clone->queryParams = $queryParams; + + return $clone; + } + + /** + * {@inheritDoc} + */ + public function getUploadedFiles() : array + { + return $this->uploadedFiles; + } + + /** + * {@inheritDoc} + */ + public function withUploadedFiles(array $uploadedFiles) : ServerRequestInterface + { + // Validates the given uploaded files structure + \array_walk_recursive($uploadedFiles, function ($uploadedFile) { + if (! ($uploadedFile instanceof UploadedFileInterface)) { + throw new \InvalidArgumentException('Invalid uploaded files structure'); + } + }); + + $clone = clone $this; + $clone->uploadedFiles = $uploadedFiles; + + return $clone; + } + + /** + * {@inheritDoc} + */ + public function getParsedBody() + { + return $this->parsedBody; + } + + /** + * {@inheritDoc} + */ + public function withParsedBody($parsedBody) : ServerRequestInterface + { + $clone = clone $this; + $clone->parsedBody = $parsedBody; + + return $clone; + } + + /** + * {@inheritDoc} + */ + public function getAttributes() : array + { + return $this->attributes; + } + + /** + * {@inheritDoc} + */ + public function getAttribute($name, $default = null) + { + if (\array_key_exists($name, $this->attributes)) { + return $this->attributes[$name]; + } + + return $default; + } + + /** + * {@inheritDoc} + */ + public function withAttribute($name, $value) : ServerRequestInterface + { + $clone = clone $this; + $clone->attributes[$name] = $value; + + return $clone; + } + + /** + * {@inheritDoc} + */ + public function withoutAttribute($name) : ServerRequestInterface + { + $clone = clone $this; + + unset($clone->attributes[$name]); + + return $clone; + } } diff --git a/src/ServerRequestFactory.php b/src/ServerRequestFactory.php index 983a787..77c5ab0 100644 --- a/src/ServerRequestFactory.php +++ b/src/ServerRequestFactory.php @@ -28,65 +28,66 @@ class ServerRequestFactory implements ServerRequestFactoryInterface { - /** - * Creates the server request instance from superglobals variables - * - * @param null|array $server - * @param null|array $query - * @param null|array $body - * @param null|array $cookies - * @param null|array $files - * - * @return ServerRequestInterface - * - * @link http://php.net/manual/en/language.variables.superglobals.php - * @link https://www.php-fig.org/psr/psr-15/meta/ - */ - public static function fromGlobals(array $server = null, array $query = null, array $body = null, array $cookies = null, array $files = null) : ServerRequestInterface - { - $server = $server ?? $_SERVER ?? []; - $query = $query ?? $_GET ?? []; - $body = $body ?? $_POST ?? []; - $cookies = $cookies ?? $_COOKIE ?? []; - $files = $files ?? $_FILES ?? []; + /** + * Creates the server request instance from superglobals variables + * + * @param null|array $server + * @param null|array $query + * @param null|array $body + * @param null|array $cookies + * @param null|array $files + * + * @return ServerRequestInterface + * + * @link http://php.net/manual/en/language.variables.superglobals.php + * @link https://www.php-fig.org/psr/psr-15/meta/ + */ + public static function fromGlobals( + array $server = null, + array $query = null, + array $body = null, + array $cookies = null, + array $files = null + ) : ServerRequestInterface { + $server = $server ?? $_SERVER ?? []; + $query = $query ?? $_GET ?? []; + $body = $body ?? $_POST ?? []; + $cookies = $cookies ?? $_COOKIE ?? []; + $files = $files ?? $_FILES ?? []; - $request = (new ServerRequest) - ->withProtocolVersion(request_http_version($server)) - ->withBody(request_body()) - ->withMethod(request_method($server)) - ->withUri(request_uri($server)) - ->withServerParams($server) - ->withCookieParams($cookies) - ->withQueryParams($query) - ->withUploadedFiles(request_files($files)) - ->withParsedBody($body); + $request = (new ServerRequest) + ->withProtocolVersion(request_http_version($server)) + ->withBody(request_body()) + ->withMethod(request_method($server)) + ->withUri(request_uri($server)) + ->withServerParams($server) + ->withCookieParams($cookies) + ->withQueryParams($query) + ->withUploadedFiles(request_files($files)) + ->withParsedBody($body); - foreach (request_headers($server) as $name => $value) - { - $request = $request->withHeader($name, $value); - } + foreach (request_headers($server) as $name => $value) { + $request = $request->withHeader($name, $value); + } - return $request; - } + return $request; + } - /** - * {@inheritDoc} - */ - public function createServerRequest(string $method, $uri, array $serverParams = []) : ServerRequestInterface - { - if (! ($uri instanceof UriInterface)) - { - $uri = (new UriFactory) - ->createUri($uri); - } + /** + * {@inheritDoc} + */ + public function createServerRequest(string $method, $uri, array $serverParams = []) : ServerRequestInterface + { + if (! ($uri instanceof UriInterface)) { + $uri = (new UriFactory)->createUri($uri); + } - $body = (new StreamFactory) - ->createStream(); + $body = (new StreamFactory)->createStream(); - return (new ServerRequest) - ->withMethod($method) - ->withUri($uri) - ->withServerParams($serverParams) - ->withBody($body); - } + return (new ServerRequest) + ->withMethod($method) + ->withUri($uri) + ->withServerParams($serverParams) + ->withBody($body); + } } diff --git a/src/UploadedFile.php b/src/UploadedFile.php index f70dc61..08f210f 100644 --- a/src/UploadedFile.php +++ b/src/UploadedFile.php @@ -26,147 +26,144 @@ class UploadedFile implements UploadedFileInterface { - /** - * The file stream - * - * @var null|StreamInterface - */ - protected $stream; - - /** - * The file size - * - * @var null|int - */ - protected $size; - - /** - * The file error - * - * @var int - */ - protected $error; - - /** - * The file name - * - * @var null|string - */ - protected $clientFilename; - - /** - * The file type - * - * @var null|string - */ - protected $clientMediaType; - - /** - * Constructor of the class - * - * @param StreamInterface $stream - * @param null|int $size - * @param int $error - * @param null|string $clientFilename - * @param null|string $clientMediaType - */ - public function __construct(StreamInterface $stream, int $size = null, int $error = \UPLOAD_ERR_OK, string $clientFilename = null, string $clientMediaType = null) - { - $this->stream = $stream; - - $this->size = $size ?? $stream->getSize(); - - $this->error = $error; - - $this->clientFilename = $clientFilename; - - $this->clientMediaType = $clientMediaType; - } - - /** - * {@inheritDoc} - */ - public function getStream() : StreamInterface - { - if (! ($this->stream instanceof StreamInterface)) - { - throw new \RuntimeException('The uploaded file already moved'); - } - - return $this->stream; - } - - /** - * {@inheritDoc} - */ - public function moveTo($targetPath) : void - { - if (! ($this->stream instanceof StreamInterface)) - { - throw new \RuntimeException('The uploaded file already moved'); - } - - if (! (\UPLOAD_ERR_OK === $this->error)) - { - throw new \RuntimeException('The uploaded file cannot be moved due to an error'); - } - - $folder = \dirname($targetPath); - - if (! \is_dir($folder)) - { - throw new \RuntimeException(\sprintf('The uploaded file cannot be moved. The directory "%s" does not exist', $folder)); - } - - if (! \is_writeable($folder)) - { - throw new \RuntimeException(\sprintf('The uploaded file cannot be moved. The directory "%s" is not writeable', $folder)); - } - - $target = (new StreamFactory)->createStreamFromFile($targetPath, 'wb'); - - $this->stream->rewind(); - - while (! $this->stream->eof()) - { - $target->write($this->stream->read(4096)); - } - - $this->stream->close(); - $this->stream = null; - - $target->close(); - } - - /** - * {@inheritDoc} - */ - public function getSize() : ?int - { - return $this->size; - } - - /** - * {@inheritDoc} - */ - public function getError() : int - { - return $this->error; - } - - /** - * {@inheritDoc} - */ - public function getClientFilename() : ?string - { - return $this->clientFilename; - } - - /** - * {@inheritDoc} - */ - public function getClientMediaType() : ?string - { - return $this->clientMediaType; - } + /** + * The file stream + * + * @var null|StreamInterface + */ + protected $stream; + + /** + * The file size + * + * @var null|int + */ + protected $size; + + /** + * The file error + * + * @var int + */ + protected $error; + + /** + * The file name + * + * @var null|string + */ + protected $clientFilename; + + /** + * The file type + * + * @var null|string + */ + protected $clientMediaType; + + /** + * Constructor of the class + * + * @param StreamInterface $stream + * @param null|int $size + * @param int $error + * @param null|string $clientFilename + * @param null|string $clientMediaType + */ + public function __construct( + StreamInterface $stream, + int $size = null, + int $error = \UPLOAD_ERR_OK, + string $clientFilename = null, + string $clientMediaType = null + ) { + $this->stream = $stream; + $this->size = $size ?? $stream->getSize(); + $this->error = $error; + $this->clientFilename = $clientFilename; + $this->clientMediaType = $clientMediaType; + } + + /** + * {@inheritDoc} + */ + public function getStream() : StreamInterface + { + if (! ($this->stream instanceof StreamInterface)) { + throw new \RuntimeException('The uploaded file already moved'); + } + + return $this->stream; + } + + /** + * {@inheritDoc} + */ + public function moveTo($targetPath) : void + { + if (! ($this->stream instanceof StreamInterface)) { + throw new \RuntimeException('The uploaded file already moved'); + } + + if (! (\UPLOAD_ERR_OK === $this->error)) { + throw new \RuntimeException('The uploaded file cannot be moved due to an error'); + } + + $folder = \dirname($targetPath); + if (! \is_dir($folder)) { + throw new \RuntimeException( + \sprintf('The uploaded file cannot be moved. The directory "%s" does not exist', $folder) + ); + } + + if (! \is_writeable($folder)) { + throw new \RuntimeException( + \sprintf('The uploaded file cannot be moved. The directory "%s" is not writeable', $folder) + ); + } + + $target = (new StreamFactory)->createStreamFromFile($targetPath, 'wb'); + + $this->stream->rewind(); + while (! $this->stream->eof()) { + $target->write($this->stream->read(4096)); + } + + $this->stream->close(); + $this->stream = null; + + $target->close(); + } + + /** + * {@inheritDoc} + */ + public function getSize() : ?int + { + return $this->size; + } + + /** + * {@inheritDoc} + */ + public function getError() : int + { + return $this->error; + } + + /** + * {@inheritDoc} + */ + public function getClientFilename() : ?string + { + return $this->clientFilename; + } + + /** + * {@inheritDoc} + */ + public function getClientMediaType() : ?string + { + return $this->clientMediaType; + } } diff --git a/src/UploadedFileFactory.php b/src/UploadedFileFactory.php index 56bd471..4462b5b 100644 --- a/src/UploadedFileFactory.php +++ b/src/UploadedFileFactory.php @@ -26,11 +26,16 @@ class UploadedFileFactory implements UploadedFileFactoryInterface { - /** - * {@inheritDoc} - */ - public function createUploadedFile(StreamInterface $stream, int $size = null, int $error = \UPLOAD_ERR_OK, string $clientFilename = null, string $clientMediaType = null) : UploadedFileInterface - { - return new UploadedFile($stream, $size, $error, $clientFilename, $clientMediaType); - } + /** + * {@inheritDoc} + */ + public function createUploadedFile( + StreamInterface $stream, + int $size = null, + int $error = \UPLOAD_ERR_OK, + string $clientFilename = null, + string $clientMediaType = null + ) : UploadedFileInterface { + return new UploadedFile($stream, $size, $error, $clientFilename, $clientMediaType); + } } diff --git a/tests/ServerRequestFactoryTest.php b/tests/ServerRequestFactoryTest.php index b65f9fd..00adf99 100644 --- a/tests/ServerRequestFactoryTest.php +++ b/tests/ServerRequestFactoryTest.php @@ -1,299 +1,364 @@ -assertInstanceOf(ServerRequestFactoryInterface::class, $factory); - } - - public function testCreateServerRequest() - { - $method = 'GET'; - $uri = 'http://localhost:3000/'; - $server = $_SERVER; - - $request = (new ServerRequestFactory)->createServerRequest($method, $uri, $server); - - $this->assertInstanceOf(ServerRequestInterface::class, $request); - $this->assertEquals($method, $request->getMethod()); - $this->assertEquals($uri, (string) $request->getUri()); - $this->assertEquals($server, $request->getServerParams()); - - // default body of the request... - $this->assertInstanceOf(StreamInterface::class, $request->getBody()); - $this->assertTrue($request->getBody()->isSeekable()); - $this->assertTrue($request->getBody()->isWritable()); - $this->assertTrue($request->getBody()->isReadable()); - $this->assertEquals('php://temp', $request->getBody()->getMetadata('uri')); - } - - /** - * @runInSeparateProcess - */ - public function testCreateServerRequestFromGlobals() - { - $file = ['tmp_name' => $this->tmpfile(), 'size' => 0, 'error' => \UPLOAD_ERR_OK, 'name' => '', 'type' => '']; - - $_SERVER = ['foo' => 'bar']; - $_GET = ['bar' => 'baz']; - $_POST = ['baz' => 'qux']; - $_COOKIE = ['qux' => 'quux']; - $_FILES = ['quux' => $file]; - - $request = ServerRequestFactory::fromGlobals(); - $this->assertInstanceOf(ServerRequestInterface::class, $request); - - $this->assertEquals($_SERVER, $request->getServerParams()); - $this->assertEquals($_GET, $request->getQueryParams()); - $this->assertEquals($_POST, $request->getParsedBody()); - $this->assertEquals($_COOKIE, $request->getCookieParams()); - $this->assertEquals($_FILES['quux']['tmp_name'], $request->getUploadedFiles()['quux']->getStream()->getMetadata('uri')); - } - - public function testCreateServerRequestFromGlobalsWithServer() - { - $server = ['foo' => 'bar']; - $request = ServerRequestFactory::fromGlobals($server, [], [], [], []); - $this->assertEquals($server, $request->getServerParams()); - } - - public function testCreateServerRequestFromGlobalsWithQuery() - { - $query = ['foo' => 'bar']; - $request = ServerRequestFactory::fromGlobals([], $query, [], [], []); - $this->assertEquals($query, $request->getQueryParams()); - } - - public function testCreateServerRequestFromGlobalsWithBody() - { - $body = ['foo' => 'bar']; - $request = ServerRequestFactory::fromGlobals([], [], $body, [], []); - $this->assertEquals($body, $request->getParsedBody()); - } - - public function testCreateServerRequestFromGlobalsWithCookies() - { - $cookies = ['foo' => 'bar']; - $request = ServerRequestFactory::fromGlobals([], [], [], $cookies, []); - $this->assertEquals($cookies, $request->getCookieParams()); - } - - public function testCreateServerRequestFromGlobalsWithFiles() - { - $files['foo']['tmp_name'] = $this->tmpfile(); - $files['foo']['size'] = 0; - $files['foo']['error'] = \UPLOAD_ERR_OK; - $files['foo']['name'] = 'foo.txt'; - $files['foo']['type'] = 'text/plain'; - - $files['bar']['tmp_name'][0] = $this->tmpfile(); - $files['bar']['size'][0] = 0; - $files['bar']['error'][0] = \UPLOAD_ERR_OK; - $files['bar']['name'][0] = 'bar.txt'; - $files['bar']['type'][0] = 'text/plain'; - - $request = ServerRequestFactory::fromGlobals([], [], [], [], $files); - $uploadedFiles = $request->getUploadedFiles(); - - $this->assertEquals($files['foo']['tmp_name'], $uploadedFiles['foo']->getStream()->getMetadata('uri')); - $this->assertEquals($files['foo']['size'], $uploadedFiles['foo']->getSize()); - $this->assertEquals($files['foo']['error'], $uploadedFiles['foo']->getError()); - $this->assertEquals($files['foo']['name'], $uploadedFiles['foo']->getClientFilename()); - $this->assertEquals($files['foo']['type'], $uploadedFiles['foo']->getClientMediaType()); - - $this->assertEquals($files['bar']['tmp_name'][0], $uploadedFiles['bar'][0]->getStream()->getMetadata('uri')); - $this->assertEquals($files['bar']['size'][0], $uploadedFiles['bar'][0]->getSize()); - $this->assertEquals($files['bar']['error'][0], $uploadedFiles['bar'][0]->getError()); - $this->assertEquals($files['bar']['name'][0], $uploadedFiles['bar'][0]->getClientFilename()); - $this->assertEquals($files['bar']['type'][0], $uploadedFiles['bar'][0]->getClientMediaType()); - } - - public function testCreateServerRequestFromGlobalsWithUploadErrorNoFile() - { - $files = [ - 'foo' => [ - 'error' => \UPLOAD_ERR_NO_FILE, - 'size' => 0, - 'tmp_name' => '', - 'name' => '', - 'type' => '', - ], - ]; - - $request = ServerRequestFactory::fromGlobals([], [], [], [], $files); - - $this->assertCount(0, $request->getUploadedFiles()); - } - - public function headersFromGlobalsProvider() - { - return [ - [ - ['FOO' => 'bar'], - [], - ], - [ - ['HTTP_FOO' => 'bar'], - ['bar'], - ], - ]; - } - - /** - * @dataProvider headersFromGlobalsProvider - */ - public function testHeadersFromGlobals($header, $expectedValue) - { - $request = ServerRequestFactory::fromGlobals($header); - $this->assertEquals($expectedValue, $request->getHeader('foo')); - } - - public function protocolVersionFromGlobalsProvider() - { - return [ - [ - ['SERVER_PROTOCOL' => 'HTTP/2.0'], - '2.0', - ], - [ - ['SERVER_PROTOCOL' => 'HTTP/3'], - '3', - ], - ]; - } - - /** - * @dataProvider protocolVersionFromGlobalsProvider - */ - public function testProtocolVersionFromGlobals($protocolVersion, $expectedValue) - { - $request = ServerRequestFactory::fromGlobals($protocolVersion); - $this->assertEquals($expectedValue, $request->getProtocolVersion()); - } - - public function methodFromGlobalsProvider() - { - return [ - [ - ['REQUEST_METHOD' => 'POST'], - 'POST', - ], - [ - ['REQUEST_METHOD' => 'UNKNOWN'], - 'UNKNOWN', - ], - ]; - } - - /** - * @dataProvider methodFromGlobalsProvider - */ - public function testMethodFromGlobals($requestMethod, $expectedValue) - { - $request = ServerRequestFactory::fromGlobals($requestMethod); - $this->assertEquals($expectedValue, $request->getMethod()); - } - - public function uriFromGlobalsProvider() - { - return [ - [ - [], - 'http://localhost/', - ], - [ - ['HTTPS' => 'off'], - 'http://localhost/', - ], - [ - ['HTTPS' => 'on'], - 'https://localhost/', - ], - [ - ['HTTP_HOST' => 'example.com'], - 'http://example.com/', - ], - [ - ['HTTP_HOST' => 'example.com:3000'], - 'http://example.com:3000/', - ], - [ - ['SERVER_NAME' => 'example.com'], - 'http://example.com/', - ], - [ - ['SERVER_NAME' => 'example.com', 'SERVER_PORT' => 3000], - 'http://example.com:3000/', - ], - [ - ['SERVER_PORT' => 3000], - 'http://localhost/', - ], - [ - ['REQUEST_URI' => '/path'], - 'http://localhost/path', - ], - [ - ['REQUEST_URI' => '/path?query'], - 'http://localhost/path?query', - ], - [ - ['PHP_SELF' => '/path'], - 'http://localhost/path', - ], - [ - ['PHP_SELF' => '/path', 'QUERY_STRING' => 'query'], - 'http://localhost/path?query', - ], - [ - ['QUERY_STRING' => 'query'], - 'http://localhost/', - ], - ]; - } - - /** - * @dataProvider uriFromGlobalsProvider - */ - public function testUriFromGlobals($uri, $expectedValue) - { - $request = ServerRequestFactory::fromGlobals($uri); - $this->assertEquals($expectedValue, (string) $request->getUri()); - } - - protected function tearDown() - { - $tmpfiles = $this->tmpfiles; - - $this->tmpfiles = []; - - foreach ($tmpfiles as $tmpfile) - { - @ \unlink($tmpfile); - } - } - - private function tmpfile() : string - { - $folder = \sys_get_temp_dir(); - - $tmpfile = \tempnam($folder, 'sunrise'); - - $this->tmpfiles[] = $tmpfile; - - return $tmpfile; - } + + /** + * @var array + */ + private $tmpfiles = []; + + /** + * @return void + */ + public function testConstructor() : void + { + $factory = new ServerRequestFactory(); + + $this->assertInstanceOf(ServerRequestFactoryInterface::class, $factory); + } + + /** + * @return void + */ + public function testCreateServerRequest() : void + { + $method = 'GET'; + $uri = 'http://localhost:3000/'; + $server = $_SERVER; + + $request = (new ServerRequestFactory)->createServerRequest($method, $uri, $server); + + $this->assertInstanceOf(ServerRequestInterface::class, $request); + $this->assertEquals($method, $request->getMethod()); + $this->assertEquals($uri, (string) $request->getUri()); + $this->assertEquals($server, $request->getServerParams()); + + // default body of the request... + $this->assertInstanceOf(StreamInterface::class, $request->getBody()); + $this->assertTrue($request->getBody()->isSeekable()); + $this->assertTrue($request->getBody()->isWritable()); + $this->assertTrue($request->getBody()->isReadable()); + $this->assertEquals('php://temp', $request->getBody()->getMetadata('uri')); + } + + /** + * @runInSeparateProcess + * + * @return void + */ + public function testCreateServerRequestFromGlobals() : void + { + $file = ['tmp_name' => $this->tmpfile(), 'size' => 0, 'error' => \UPLOAD_ERR_OK, 'name' => '', 'type' => '']; + + $_SERVER = ['foo' => 'bar']; + $_GET = ['bar' => 'baz']; + $_POST = ['baz' => 'qux']; + $_COOKIE = ['qux' => 'quux']; + $_FILES = ['quux' => $file]; + + $request = ServerRequestFactory::fromGlobals(); + $this->assertInstanceOf(ServerRequestInterface::class, $request); + + $this->assertEquals($_SERVER, $request->getServerParams()); + $this->assertEquals($_GET, $request->getQueryParams()); + $this->assertEquals($_POST, $request->getParsedBody()); + $this->assertEquals($_COOKIE, $request->getCookieParams()); + + $this->assertEquals( + $_FILES['quux']['tmp_name'], + $request->getUploadedFiles()['quux']->getStream()->getMetadata('uri') + ); + } + + /** + * @return void + */ + public function testCreateServerRequestFromGlobalsWithServer() : void + { + $server = ['foo' => 'bar']; + $request = ServerRequestFactory::fromGlobals($server, [], [], [], []); + $this->assertEquals($server, $request->getServerParams()); + } + + /** + * @return void + */ + public function testCreateServerRequestFromGlobalsWithQuery() : void + { + $query = ['foo' => 'bar']; + $request = ServerRequestFactory::fromGlobals([], $query, [], [], []); + $this->assertEquals($query, $request->getQueryParams()); + } + + /** + * @return void + */ + public function testCreateServerRequestFromGlobalsWithBody() : void + { + $body = ['foo' => 'bar']; + $request = ServerRequestFactory::fromGlobals([], [], $body, [], []); + $this->assertEquals($body, $request->getParsedBody()); + } + + /** + * @return void + */ + public function testCreateServerRequestFromGlobalsWithCookies() : void + { + $cookies = ['foo' => 'bar']; + $request = ServerRequestFactory::fromGlobals([], [], [], $cookies, []); + $this->assertEquals($cookies, $request->getCookieParams()); + } + + /** + * @return void + */ + public function testCreateServerRequestFromGlobalsWithFiles() : void + { + $files['foo']['tmp_name'] = $this->tmpfile(); + $files['foo']['size'] = 0; + $files['foo']['error'] = \UPLOAD_ERR_OK; + $files['foo']['name'] = 'foo.txt'; + $files['foo']['type'] = 'text/plain'; + + $files['bar']['tmp_name'][0] = $this->tmpfile(); + $files['bar']['size'][0] = 0; + $files['bar']['error'][0] = \UPLOAD_ERR_OK; + $files['bar']['name'][0] = 'bar.txt'; + $files['bar']['type'][0] = 'text/plain'; + + $request = ServerRequestFactory::fromGlobals([], [], [], [], $files); + $uploadedFiles = $request->getUploadedFiles(); + + $this->assertEquals($files['foo']['tmp_name'], $uploadedFiles['foo']->getStream()->getMetadata('uri')); + $this->assertEquals($files['foo']['size'], $uploadedFiles['foo']->getSize()); + $this->assertEquals($files['foo']['error'], $uploadedFiles['foo']->getError()); + $this->assertEquals($files['foo']['name'], $uploadedFiles['foo']->getClientFilename()); + $this->assertEquals($files['foo']['type'], $uploadedFiles['foo']->getClientMediaType()); + + $this->assertEquals($files['bar']['tmp_name'][0], $uploadedFiles['bar'][0]->getStream()->getMetadata('uri')); + $this->assertEquals($files['bar']['size'][0], $uploadedFiles['bar'][0]->getSize()); + $this->assertEquals($files['bar']['error'][0], $uploadedFiles['bar'][0]->getError()); + $this->assertEquals($files['bar']['name'][0], $uploadedFiles['bar'][0]->getClientFilename()); + $this->assertEquals($files['bar']['type'][0], $uploadedFiles['bar'][0]->getClientMediaType()); + } + + /** + * @return void + */ + public function testCreateServerRequestFromGlobalsWithUploadErrorNoFile() : void + { + $files = [ + 'foo' => [ + 'error' => \UPLOAD_ERR_NO_FILE, + 'size' => 0, + 'tmp_name' => '', + 'name' => '', + 'type' => '', + ], + ]; + + $request = ServerRequestFactory::fromGlobals([], [], [], [], $files); + + $this->assertCount(0, $request->getUploadedFiles()); + } + + /** + * @dataProvider headersFromGlobalsProvider + * + * @return void + */ + public function testHeadersFromGlobals($header, $expectedValue) : void + { + $request = ServerRequestFactory::fromGlobals($header); + $this->assertEquals($expectedValue, $request->getHeader('foo')); + } + + /** + * @dataProvider protocolVersionFromGlobalsProvider + * + * @return void + */ + public function testProtocolVersionFromGlobals($protocolVersion, $expectedValue) : void + { + $request = ServerRequestFactory::fromGlobals($protocolVersion); + $this->assertEquals($expectedValue, $request->getProtocolVersion()); + } + + /** + * @dataProvider methodFromGlobalsProvider + * + * @return void + */ + public function testMethodFromGlobals($requestMethod, $expectedValue) : void + { + $request = ServerRequestFactory::fromGlobals($requestMethod); + $this->assertEquals($expectedValue, $request->getMethod()); + } + + /** + * @dataProvider uriFromGlobalsProvider + * + * @return void + */ + public function testUriFromGlobals($uri, $expectedValue) : void + { + $request = ServerRequestFactory::fromGlobals($uri); + $this->assertEquals($expectedValue, (string) $request->getUri()); + } + + /** + * @return void + */ + protected function tearDown() : void + { + $tmpfiles = $this->tmpfiles; + $this->tmpfiles = []; + + foreach ($tmpfiles as $tmpfile) { + @\unlink($tmpfile); + } + } + + /** + * @return string + */ + private function tmpfile() : string + { + $folder = \sys_get_temp_dir(); + $tmpfile = \tempnam($folder, 'sunrise'); + + $this->tmpfiles[] = $tmpfile; + + return $tmpfile; + } + + // providers... + + /** + * @return array + */ + public function headersFromGlobalsProvider() : array + { + return [ + [ + ['FOO' => 'bar'], + [], + ], + [ + ['HTTP_FOO' => 'bar'], + ['bar'], + ], + ]; + } + + /** + * @return array + */ + public function protocolVersionFromGlobalsProvider() : array + { + return [ + [ + ['SERVER_PROTOCOL' => 'HTTP/2.0'], + '2.0', + ], + [ + ['SERVER_PROTOCOL' => 'HTTP/3'], + '3', + ], + ]; + } + + /** + * @return array + */ + public function methodFromGlobalsProvider() : array + { + return [ + [ + ['REQUEST_METHOD' => 'POST'], + 'POST', + ], + [ + ['REQUEST_METHOD' => 'UNKNOWN'], + 'UNKNOWN', + ], + ]; + } + + /** + * @return array + */ + public function uriFromGlobalsProvider() : array + { + return [ + [ + [], + 'http://localhost/', + ], + [ + ['HTTPS' => 'off'], + 'http://localhost/', + ], + [ + ['HTTPS' => 'on'], + 'https://localhost/', + ], + [ + ['HTTP_HOST' => 'example.com'], + 'http://example.com/', + ], + [ + ['HTTP_HOST' => 'example.com:3000'], + 'http://example.com:3000/', + ], + [ + ['SERVER_NAME' => 'example.com'], + 'http://example.com/', + ], + [ + ['SERVER_NAME' => 'example.com', 'SERVER_PORT' => 3000], + 'http://example.com:3000/', + ], + [ + ['SERVER_PORT' => 3000], + 'http://localhost/', + ], + [ + ['REQUEST_URI' => '/path'], + 'http://localhost/path', + ], + [ + ['REQUEST_URI' => '/path?query'], + 'http://localhost/path?query', + ], + [ + ['PHP_SELF' => '/path'], + 'http://localhost/path', + ], + [ + ['PHP_SELF' => '/path', 'QUERY_STRING' => 'query'], + 'http://localhost/path?query', + ], + [ + ['QUERY_STRING' => 'query'], + 'http://localhost/', + ], + ]; + } } diff --git a/tests/ServerRequestTest.php b/tests/ServerRequestTest.php index 4427c1b..e7abd3e 100644 --- a/tests/ServerRequestTest.php +++ b/tests/ServerRequestTest.php @@ -1,167 +1,208 @@ -assertInstanceOf(ServerRequestInterface::class, $req); - } - - public function testServerParams() - { - $params = ['foo' => 'bar']; - - $req = new ServerRequest(); - $this->assertEquals([], $req->getServerParams()); - - $clone = $req->withServerParams($params); - $this->assertInstanceOf(ServerRequestInterface::class, $clone); - $this->assertEquals([], $req->getServerParams()); - $this->assertEquals($params, $clone->getServerParams()); - } - - public function testCookieParams() - { - $params = ['foo' => 'bar']; - - $req = new ServerRequest(); - $this->assertEquals([], $req->getCookieParams()); - - $clone = $req->withCookieParams($params); - $this->assertInstanceOf(ServerRequestInterface::class, $clone); - $this->assertEquals([], $req->getCookieParams()); - $this->assertEquals($params, $clone->getCookieParams()); - } - - public function testQueryParams() - { - $params = ['foo' => 'bar']; - - $req = new ServerRequest(); - $this->assertEquals([], $req->getQueryParams()); - - $clone = $req->withQueryParams($params); - $this->assertInstanceOf(ServerRequestInterface::class, $clone); - $this->assertEquals([], $req->getQueryParams()); - $this->assertEquals($params, $clone->getQueryParams()); - } - - public function testUploadedFiles() - { - $stream = (new StreamFactory)->createStreamFromFile('php://memory', 'rb'); - - $uploadedFiles = [ - 'foo' => new UploadedFile($stream), - 'bar' => [ - 'baz' => new UploadedFile($stream), - ], - ]; - - $req = new ServerRequest(); - $this->assertEquals([], $req->getUploadedFiles()); - - $clone = $req->withUploadedFiles($uploadedFiles); - $this->assertInstanceOf(ServerRequestInterface::class, $clone); - $this->assertEquals([], $req->getUploadedFiles()); - $this->assertEquals($uploadedFiles, $clone->getUploadedFiles()); - - $stream->close(); - } - - public function testInvalidUploadedFilesStructure() - { - $req = new ServerRequest(); - - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('Invalid uploaded files structure'); - - $req->withUploadedFiles(['foo' => 'bar']); - } - - /** - * @dataProvider parsedBodyProvider - */ - public function testParsedBody($parsedBody) - { - $req = new ServerRequest(); - $this->assertNull($req->getParsedBody()); - - $clone = $req->withParsedBody($parsedBody); - $this->assertInstanceOf(ServerRequestInterface::class, $clone); - $this->assertNull($req->getParsedBody()); - $this->assertEquals($parsedBody, $clone->getParsedBody()); - } - - /** - * @dataProvider attributesProvider - */ - public function testSetAttribute($key, $value) - { - $req = new ServerRequest(); - $this->assertEquals([], $req->getAttributes()); - - $clone = $req->withAttribute($key, $value); - $this->assertInstanceOf(ServerRequestInterface::class, $clone); - $this->assertEquals([], $req->getAttributes()); - $this->assertEquals([$key => $value], $clone->getAttributes()); - } - - public function testGetAttribute() - { - $req = (new ServerRequest) - ->withAttribute('foo', 'bar') - ->withAttribute('bar', 'baz'); - - $this->assertEquals('bar', $req->getAttribute('foo')); - $this->assertEquals('baz', $req->getAttribute('bar')); - $this->assertNull($req->getAttribute('baz')); - $this->assertFalse($req->getAttribute('baz', false)); - } - - public function testDeleteAttribute() - { - $req = (new ServerRequest) - ->withAttribute('foo', 'bar') - ->withAttribute('bar', 'baz'); - - $clone1 = $req->withoutAttribute('foo'); - $this->assertInstanceOf(ServerRequestInterface::class, $clone1); - $this->assertEquals(['bar' => 'baz'], $clone1->getAttributes()); - $this->assertNull($clone1->getAttribute('foo')); - - $clone2 = $clone1->withoutAttribute('bar'); - $this->assertInstanceOf(ServerRequestInterface::class, $clone2); - $this->assertEquals([], $clone2->getAttributes()); - $this->assertNull($clone2->getAttribute('bar')); - } - - // Providers... - - public function parsedBodyProvider() - { - return [ - [null], - ['foo bar'], - [['foo' => 'bar']], - ]; - } - - public function attributesProvider() - { - return [ - ['foo', null], - ['foo', 'bar'], - ['foo', ['bar']], - ]; - } + + /** + * @return void + */ + public function testConstructor() : void + { + $req = new ServerRequest(); + + $this->assertInstanceOf(ServerRequestInterface::class, $req); + } + + /** + * @return void + */ + public function testServerParams() : void + { + $params = ['foo' => 'bar']; + + $req = new ServerRequest(); + $this->assertEquals([], $req->getServerParams()); + + $clone = $req->withServerParams($params); + $this->assertInstanceOf(ServerRequestInterface::class, $clone); + $this->assertEquals([], $req->getServerParams()); + $this->assertEquals($params, $clone->getServerParams()); + } + + /** + * @return void + */ + public function testCookieParams() : void + { + $params = ['foo' => 'bar']; + + $req = new ServerRequest(); + $this->assertEquals([], $req->getCookieParams()); + + $clone = $req->withCookieParams($params); + $this->assertInstanceOf(ServerRequestInterface::class, $clone); + $this->assertEquals([], $req->getCookieParams()); + $this->assertEquals($params, $clone->getCookieParams()); + } + + /** + * @return void + */ + public function testQueryParams() : void + { + $params = ['foo' => 'bar']; + + $req = new ServerRequest(); + $this->assertEquals([], $req->getQueryParams()); + + $clone = $req->withQueryParams($params); + $this->assertInstanceOf(ServerRequestInterface::class, $clone); + $this->assertEquals([], $req->getQueryParams()); + $this->assertEquals($params, $clone->getQueryParams()); + } + + /** + * @return void + */ + public function testUploadedFiles() : void + { + $stream = (new StreamFactory)->createStreamFromFile('php://memory', 'rb'); + + $uploadedFiles = [ + 'foo' => new UploadedFile($stream), + 'bar' => [ + 'baz' => new UploadedFile($stream), + ], + ]; + + $req = new ServerRequest(); + $this->assertEquals([], $req->getUploadedFiles()); + + $clone = $req->withUploadedFiles($uploadedFiles); + $this->assertInstanceOf(ServerRequestInterface::class, $clone); + $this->assertEquals([], $req->getUploadedFiles()); + $this->assertEquals($uploadedFiles, $clone->getUploadedFiles()); + + $stream->close(); + } + + /** + * @return void + */ + public function testInvalidUploadedFilesStructure() : void + { + $req = new ServerRequest(); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid uploaded files structure'); + + $req->withUploadedFiles(['foo' => 'bar']); + } + + /** + * @dataProvider parsedBodyProvider + * + * @return void + */ + public function testParsedBody($parsedBody) : void + { + $req = new ServerRequest(); + $this->assertNull($req->getParsedBody()); + + $clone = $req->withParsedBody($parsedBody); + $this->assertInstanceOf(ServerRequestInterface::class, $clone); + $this->assertNull($req->getParsedBody()); + $this->assertEquals($parsedBody, $clone->getParsedBody()); + } + + /** + * @dataProvider attributesProvider + * + * @return void + */ + public function testSetAttribute($key, $value) : void + { + $req = new ServerRequest(); + $this->assertEquals([], $req->getAttributes()); + + $clone = $req->withAttribute($key, $value); + $this->assertInstanceOf(ServerRequestInterface::class, $clone); + $this->assertEquals([], $req->getAttributes()); + $this->assertEquals([$key => $value], $clone->getAttributes()); + } + + /** + * @return void + */ + public function testGetAttribute() : void + { + $req = (new ServerRequest) + ->withAttribute('foo', 'bar') + ->withAttribute('bar', 'baz'); + + $this->assertEquals('bar', $req->getAttribute('foo')); + $this->assertEquals('baz', $req->getAttribute('bar')); + $this->assertNull($req->getAttribute('baz')); + $this->assertFalse($req->getAttribute('baz', false)); + } + + /** + * @return void + */ + public function testDeleteAttribute() : void + { + $req = (new ServerRequest) + ->withAttribute('foo', 'bar') + ->withAttribute('bar', 'baz'); + + $clone1 = $req->withoutAttribute('foo'); + $this->assertInstanceOf(ServerRequestInterface::class, $clone1); + $this->assertEquals(['bar' => 'baz'], $clone1->getAttributes()); + $this->assertNull($clone1->getAttribute('foo')); + + $clone2 = $clone1->withoutAttribute('bar'); + $this->assertInstanceOf(ServerRequestInterface::class, $clone2); + $this->assertEquals([], $clone2->getAttributes()); + $this->assertNull($clone2->getAttribute('bar')); + } + + // Providers... + + /** + * @return array + */ + public function parsedBodyProvider() : array + { + return [ + [null], + ['foo bar'], + [['foo' => 'bar']], + ]; + } + + /** + * @return array + */ + public function attributesProvider() : array + { + return [ + ['foo', null], + ['foo', 'bar'], + ['foo', ['bar']], + ]; + } } diff --git a/tests/UploadedFileFactoryTest.php b/tests/UploadedFileFactoryTest.php index 8af08ac..e230fc7 100644 --- a/tests/UploadedFileFactoryTest.php +++ b/tests/UploadedFileFactoryTest.php @@ -1,7 +1,10 @@ -stream = (new StreamFactory)->createStreamFromFile('php://memory', 'r+b'); + /** + * @var null|StreamInterface + */ + private $stream; - $this->stream->write('foo'); - } + /** + * @return void + */ + protected function setUp() : void + { + $this->stream = (new StreamFactory)->createStreamFromFile('php://memory', 'r+b'); - protected function tearDown() - { - if ($this->stream instanceof StreamInterface) - { - $this->stream->close(); - } - } + $this->stream->write('foo'); + } - public function testConstructor() - { - $factory = new UploadedFileFactory(); + /** + * @return void + */ + protected function tearDown() : void + { + if ($this->stream instanceof StreamInterface) { + $this->stream->close(); + } + } - $this->assertInstanceOf(UploadedFileFactoryInterface::class, $factory); - } + /** + * @return void + */ + public function testConstructor() : void + { + $factory = new UploadedFileFactory(); - public function testCreateUploadedFile() - { - $uploadedFile = (new UploadedFileFactory)->createUploadedFile($this->stream); + $this->assertInstanceOf(UploadedFileFactoryInterface::class, $factory); + } - $this->assertInstanceOf(UploadedFileInterface::class, $uploadedFile); - $this->assertEquals($this->stream, $uploadedFile->getStream()); - $this->assertEquals($this->stream->getSize(), $uploadedFile->getSize()); - $this->assertEquals(\UPLOAD_ERR_OK, $uploadedFile->getError()); - $this->assertNull($uploadedFile->getClientFilename()); - $this->assertNull($uploadedFile->getClientMediaType()); - } + /** + * @return void + */ + public function testCreateUploadedFile() : void + { + $uploadedFile = (new UploadedFileFactory)->createUploadedFile($this->stream); - public function testCreateUploadedFileWithParameters() - { - $size = \random_int(\PHP_INT_MIN, \PHP_INT_MAX); - $error = \UPLOAD_ERR_NO_FILE; - $filename = 'photo.jpeg'; - $mediatype = 'image/jpeg'; + $this->assertInstanceOf(UploadedFileInterface::class, $uploadedFile); + $this->assertEquals($this->stream, $uploadedFile->getStream()); + $this->assertEquals($this->stream->getSize(), $uploadedFile->getSize()); + $this->assertEquals(\UPLOAD_ERR_OK, $uploadedFile->getError()); + $this->assertNull($uploadedFile->getClientFilename()); + $this->assertNull($uploadedFile->getClientMediaType()); + } - $uploadedFile = (new UploadedFileFactory)->createUploadedFile($this->stream, $size, $error, $filename, $mediatype); + /** + * @return void + */ + public function testCreateUploadedFileWithParameters() : void + { + $size = \random_int(\PHP_INT_MIN, \PHP_INT_MAX); + $error = \UPLOAD_ERR_NO_FILE; + $filename = 'photo.jpeg'; + $mediatype = 'image/jpeg'; - $this->assertEquals($this->stream, $uploadedFile->getStream()); - $this->assertEquals($size, $uploadedFile->getSize()); - $this->assertEquals($error, $uploadedFile->getError()); - $this->assertEquals($filename, $uploadedFile->getClientFilename()); - $this->assertEquals($mediatype, $uploadedFile->getClientMediaType()); - } + $uploadedFile = (new UploadedFileFactory)->createUploadedFile( + $this->stream, + $size, + $error, + $filename, + $mediatype + ); + + $this->assertEquals($this->stream, $uploadedFile->getStream()); + $this->assertEquals($size, $uploadedFile->getSize()); + $this->assertEquals($error, $uploadedFile->getError()); + $this->assertEquals($filename, $uploadedFile->getClientFilename()); + $this->assertEquals($mediatype, $uploadedFile->getClientMediaType()); + } } diff --git a/tests/UploadedFileTest.php b/tests/UploadedFileTest.php index c5dd2ad..83fa072 100644 --- a/tests/UploadedFileTest.php +++ b/tests/UploadedFileTest.php @@ -1,198 +1,270 @@ -stream = (new StreamFactory)->createStreamFromFile('php://memory', 'r+b'); - - $this->targetPath = \sys_get_temp_dir() . '/' . \bin2hex(\random_bytes(16)); - } - - protected function tearDown() - { - if ($this->stream instanceof StreamInterface) - { - $this->stream->close(); - } - - if (\file_exists($this->targetPath)) - { - @ \unlink($this->targetPath); - } - } - - public function testConstructor() - { - $uploadedFile = new UploadedFile($this->stream); - - $this->assertInstanceOf(UploadedFileInterface::class, $uploadedFile); - } - - public function testGetStream() - { - $uploadedFile = new UploadedFile($this->stream); - - $this->assertEquals($this->stream, $uploadedFile->getStream()); - } - - public function testGetSize() - { - $size = \random_int(\PHP_INT_MIN, \PHP_INT_MAX); - - $uploadedFile = new UploadedFile($this->stream, $size); - - $this->assertEquals($size, $uploadedFile->getSize()); - } - - public function testGetError() - { - $error = \UPLOAD_ERR_NO_FILE; - - $uploadedFile = new UploadedFile($this->stream, null, $error); - - $this->assertEquals($error, $uploadedFile->getError()); - } - - public function testGetClientFilename() - { - $filename = 'photo.jpeg'; - - $uploadedFile = new UploadedFile($this->stream, null, \UPLOAD_ERR_OK, $filename); - - $this->assertEquals($filename, $uploadedFile->getClientFilename()); - } - - public function testGetClientMediaType() - { - $mediatype = 'image/jpeg'; - - $uploadedFile = new UploadedFile($this->stream, null, \UPLOAD_ERR_OK, null, $mediatype); - - $this->assertEquals($mediatype, $uploadedFile->getClientMediaType()); - } - - public function testGetDefaultSize() - { - $uploadedFile = new UploadedFile($this->stream); - - $this->assertEquals($this->stream->getSize(), $uploadedFile->getSize()); - } - - public function testGetDefaultError() - { - $uploadedFile = new UploadedFile($this->stream); - - $this->assertEquals(\UPLOAD_ERR_OK, $uploadedFile->getError()); - } - - public function testGetDefaultClientFilename() - { - $uploadedFile = new UploadedFile($this->stream); - - $this->assertNull($uploadedFile->getClientFilename()); - } - - public function testGetDefaultClientMediaType() - { - $uploadedFile = new UploadedFile($this->stream); - - $this->assertNull($uploadedFile->getClientMediaType()); - } - - public function testMoveTo() - { - $content = 'foo'; - $this->stream->write($content); - - $uploadedFile = new UploadedFile($this->stream); - $uploadedFile->moveTo($this->targetPath); - - $this->assertFileExists($this->targetPath); - $this->assertEquals($content, \file_get_contents($this->targetPath)); - } - - public function testReWrite() - { - $content = 'qux'; - $this->stream->write($content); - - \file_put_contents($this->targetPath, "foo\nbar\nbaz"); - - $uploadedFile = new UploadedFile($this->stream); - $uploadedFile->moveTo($this->targetPath); - - $this->assertEquals($content, \file_get_contents($this->targetPath)); - } - - public function testGetStreamAfterMoveTo() - { - $uploadedFile = new UploadedFile($this->stream); - $uploadedFile->moveTo($this->targetPath); - - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('The uploaded file already moved'); - - $uploadedFile->getStream(); - } - - public function testReMove() - { - $uploadedFile = new UploadedFile($this->stream); - $uploadedFile->moveTo($this->targetPath); - - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('The uploaded file already moved'); - - $uploadedFile->moveTo($this->targetPath); - } - - /** - * @dataProvider errorProvider - */ - public function testMoveToWithError($error) - { - $uploadedFile = new UploadedFile($this->stream, null, $error); - - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('The uploaded file cannot be moved due to an error'); - - $uploadedFile->moveTo($this->targetPath); - } - - public function errorProvider() - { - return [ - [\UPLOAD_ERR_INI_SIZE], - [\UPLOAD_ERR_FORM_SIZE], - [\UPLOAD_ERR_PARTIAL], - [\UPLOAD_ERR_NO_FILE], - [\UPLOAD_ERR_NO_TMP_DIR], - [\UPLOAD_ERR_CANT_WRITE], - [\UPLOAD_ERR_EXTENSION], - ]; - } - - public function testMoveToNonExistentDirectory() - { - $targetPath = $this->targetPath . '/d'; - $uploadedFile = new UploadedFile($this->stream); - - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage(\sprintf('The uploaded file cannot be moved. The directory "%s" does not exist', $this->targetPath)); - - $uploadedFile->moveTo($targetPath); - } + /** + * @var null|StreamInterface + */ + private $stream; + + /** + * @var string + */ + private $targetPath = ''; + + /** + * @return void + */ + protected function setUp() : void + { + $this->stream = (new StreamFactory)->createStreamFromFile('php://memory', 'r+b'); + + $this->targetPath = \sys_get_temp_dir() . '/' . \bin2hex(\random_bytes(16)); + } + + /** + * @return void + */ + protected function tearDown() : void + { + if ($this->stream instanceof StreamInterface) { + $this->stream->close(); + } + + if (\file_exists($this->targetPath)) { + @\unlink($this->targetPath); + } + } + + /** + * @return void + */ + public function testConstructor() : void + { + $uploadedFile = new UploadedFile($this->stream); + + $this->assertInstanceOf(UploadedFileInterface::class, $uploadedFile); + } + + /** + * @return void + */ + public function testGetStream() : void + { + $uploadedFile = new UploadedFile($this->stream); + + $this->assertEquals($this->stream, $uploadedFile->getStream()); + } + + /** + * @return void + */ + public function testGetSize() : void + { + $size = \random_int(\PHP_INT_MIN, \PHP_INT_MAX); + + $uploadedFile = new UploadedFile($this->stream, $size); + + $this->assertEquals($size, $uploadedFile->getSize()); + } + + /** + * @return void + */ + public function testGetError() : void + { + $error = \UPLOAD_ERR_NO_FILE; + + $uploadedFile = new UploadedFile($this->stream, null, $error); + + $this->assertEquals($error, $uploadedFile->getError()); + } + + /** + * @return void + */ + public function testGetClientFilename() : void + { + $filename = 'photo.jpeg'; + + $uploadedFile = new UploadedFile($this->stream, null, \UPLOAD_ERR_OK, $filename); + + $this->assertEquals($filename, $uploadedFile->getClientFilename()); + } + + /** + * @return void + */ + public function testGetClientMediaType() : void + { + $mediatype = 'image/jpeg'; + + $uploadedFile = new UploadedFile($this->stream, null, \UPLOAD_ERR_OK, null, $mediatype); + + $this->assertEquals($mediatype, $uploadedFile->getClientMediaType()); + } + + /** + * @return void + */ + public function testGetDefaultSize() : void + { + $uploadedFile = new UploadedFile($this->stream); + + $this->assertEquals($this->stream->getSize(), $uploadedFile->getSize()); + } + + /** + * @return void + */ + public function testGetDefaultError() : void + { + $uploadedFile = new UploadedFile($this->stream); + + $this->assertEquals(\UPLOAD_ERR_OK, $uploadedFile->getError()); + } + + /** + * @return void + */ + public function testGetDefaultClientFilename() : void + { + $uploadedFile = new UploadedFile($this->stream); + + $this->assertNull($uploadedFile->getClientFilename()); + } + + /** + * @return void + */ + public function testGetDefaultClientMediaType() : void + { + $uploadedFile = new UploadedFile($this->stream); + + $this->assertNull($uploadedFile->getClientMediaType()); + } + + /** + * @return void + */ + public function testMoveTo() : void + { + $content = 'foo'; + $this->stream->write($content); + + $uploadedFile = new UploadedFile($this->stream); + $uploadedFile->moveTo($this->targetPath); + + $this->assertFileExists($this->targetPath); + $this->assertEquals($content, \file_get_contents($this->targetPath)); + } + + /** + * @return void + */ + public function testReWrite() : void + { + $content = 'qux'; + $this->stream->write($content); + + \file_put_contents($this->targetPath, "foo\nbar\nbaz"); + + $uploadedFile = new UploadedFile($this->stream); + $uploadedFile->moveTo($this->targetPath); + + $this->assertEquals($content, \file_get_contents($this->targetPath)); + } + + /** + * @return void + */ + public function testGetStreamAfterMoveTo() : void + { + $uploadedFile = new UploadedFile($this->stream); + $uploadedFile->moveTo($this->targetPath); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('The uploaded file already moved'); + + $uploadedFile->getStream(); + } + + /** + * @return void + */ + public function testReMove() : void + { + $uploadedFile = new UploadedFile($this->stream); + $uploadedFile->moveTo($this->targetPath); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('The uploaded file already moved'); + + $uploadedFile->moveTo($this->targetPath); + } + + /** + * @dataProvider errorProvider + * + * @return void + */ + public function testMoveToWithError($error) : void + { + $uploadedFile = new UploadedFile($this->stream, null, $error); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('The uploaded file cannot be moved due to an error'); + + $uploadedFile->moveTo($this->targetPath); + } + + /** + * @return void + */ + public function testMoveToNonExistentDirectory() : void + { + $targetPath = $this->targetPath . '/d'; + $uploadedFile = new UploadedFile($this->stream); + + $this->expectException(\RuntimeException::class); + + $this->expectExceptionMessage( + \sprintf('The uploaded file cannot be moved. The directory "%s" does not exist', $this->targetPath) + ); + + $uploadedFile->moveTo($targetPath); + } + + // providers... + + /** + * @return array + */ + public function errorProvider() : array + { + return [ + [\UPLOAD_ERR_INI_SIZE], + [\UPLOAD_ERR_FORM_SIZE], + [\UPLOAD_ERR_PARTIAL], + [\UPLOAD_ERR_NO_FILE], + [\UPLOAD_ERR_NO_TMP_DIR], + [\UPLOAD_ERR_CANT_WRITE], + [\UPLOAD_ERR_EXTENSION], + ]; + } }