diff --git a/.travis.yml b/.travis.yml index 01cfd22..a3cc40a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ sudo: false language: php php: - - 7.0 - 7.1 - 7.2 - nightly @@ -13,14 +12,14 @@ matrix: - php: nightly before_script: - - if [[ "$TRAVIS_PHP_VERSION" == '7.0' ]]; then composer require satooshi/php-coveralls:^0.7 squizlabs/php_codesniffer:^2.5 -n ; fi - - if [[ "$TRAVIS_PHP_VERSION" != '7.0' ]]; then composer install -n ; fi + - composer require php-coveralls/php-coveralls:^2.1.0 + - composer install -n script: - - if [[ "$TRAVIS_PHP_VERSION" == '7.0' ]]; then mkdir -p build/logs && phpunit --coverage-clover build/logs/clover.xml ; fi - - if [[ "$TRAVIS_PHP_VERSION" != '7.0' ]]; then vendor/bin/phpunit ; fi - - if [[ "$TRAVIS_PHP_VERSION" == '7.0' ]]; then vendor/bin/phpcs ; fi - - vendor/bin/phpstan analyse -l 3 src/ + - mkdir -p build/logs && phpunit --coverage-clover build/logs/clover.xml + - vendor/bin/phpunit + - vendor/bin/phpcs + - vendor/bin/phpstan analyse src tests after_script: - - if [[ "$TRAVIS_PHP_VERSION" == '7.0' ]]; then php vendor/bin/coveralls -v ; fi + - php vendor/bin/coveralls -v diff --git a/README.md b/README.md index e477aa0..0ab7dcd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # Slim-Http -Strict PSR-7 implementation used by the Slim Framework, but you may use it -separately with any framework compatible with the PSR-7 standard. +Slim PSR-7 Object Decorators [![Build Status](https://travis-ci.org/slimphp/Slim-Http.svg?branch=master)](https://travis-ci.org/slimphp/Slim-Http) [![Coverage Status](https://coveralls.io/repos/slimphp/Slim-Http/badge.svg?branch=master&service=github)](https://coveralls.io/github/slimphp/Slim-Http?branch=master) @@ -18,7 +17,7 @@ $ composer require slim/http "^0.1" ``` This will install the `slim/http` component and all required dependencies. -PHP 7.0, or newer, is required. +PHP 7.1, or newer, is required. ## Usage diff --git a/composer.json b/composer.json index bd88413..0494d8c 100644 --- a/composer.json +++ b/composer.json @@ -1,8 +1,8 @@ { "name": "slim/http", "type": "library", - "description": "Slim's PSR-7 implementation", - "keywords": ["psr7","psr-7","http"], + "description": "Slim PSR-7 Object Decorators", + "keywords": ["psr7", "psr-7", "http"], "homepage": "http://slimframework.com", "license": "MIT", "authors": [ @@ -23,20 +23,24 @@ } ], "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0" + "ext-json": "*", + "ext-libxml": "*", + "ext-SimpleXML": "*", + "php": "^7.1", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "php-http/message-factory": "^1.0" }, "require-dev": { - "squizlabs/php_codesniffer": "^2.5", - "phpunit/phpunit": "^6.0|^7.0", - "php-http/psr7-integration-tests": "dev-master", - "phpstan/phpstan": "^0.9" + "nyholm/psr7": "^1.0", + "phpunit/phpunit": "^7.0", + "phpstan/phpstan": "^0.10.3", + "squizlabs/php_codesniffer": "^3.3.2", + "zendframework/zend-diactoros": "^2.0", + "php-http/psr7-integration-tests": "dev-master" }, "provide": { - "psr/http-message-implementation": "1.0" - }, - "conflict": { - "slim/slim": "^3.0" + "php-http/message-factory": "^1.0" }, "autoload": { "psr-4": { @@ -51,9 +55,11 @@ "scripts": { "test": [ "@phpunit", - "@phpcs" + "@phpcs", + "@phpstan" ], - "phpunit": "php vendor/bin/phpunit", - "phpcs": "php vendor/bin/phpcs" + "phpunit": "php vendor/bin/phpunit --process-isolation", + "phpcs": "php vendor/bin/phpcs", + "phpstan": "php -d memory_limit=-1 vendor/bin/phpstan analyse src tests" } } diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..4d884ad --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,7 @@ +parameters: + level: max + ignoreErrors: + - '#Access to an undefined property Slim\\Http\\Decorators\\ResponseDecorator::\$foo#' + - '#Access to an undefined property Slim\\Http\\Decorators\\ServerRequestDecorator::\$foo#' + - '#Access to an undefined property Slim\\Http\\Decorators\\UriDecorator::\$foo#' + - '#Parameter \#1 \$port of method Slim\\Http\\Decorators\\UriDecorator::withPort() expects int|null, string given#' diff --git a/src/Body.php b/src/Body.php deleted file mode 100644 index 93c6068..0000000 --- a/src/Body.php +++ /dev/null @@ -1,22 +0,0 @@ - $value) { - $this->set($key, $value); - } - } - - /******************************************************************************** - * Collection interface - *******************************************************************************/ - - /** - * Set collection item - * - * @param string $key The data key - * @param mixed $value The data value - */ - public function set($key, $value) - { - $this->data[$key] = $value; - } - - /** - * Get collection item for key - * - * @param string $key The data key - * @param mixed $default The default value to return if data key does not exist - * - * @return mixed The key's value, or the default value - */ - public function get($key, $default = null) - { - return $this->has($key) ? $this->data[$key] : $default; - } - - /** - * Add item to collection - * - * @param array $items Key-value array of data to append to this collection - */ - public function replace(array $items) - { - foreach ($items as $key => $value) { - $this->set($key, $value); - } - } - - /** - * Get all items in collection - * - * @return array The collection's source data - */ - public function all() - { - return $this->data; - } - - /** - * Get collection keys - * - * @return array The collection's source data keys - */ - public function keys() - { - return array_keys($this->data); - } - - /** - * Does this collection have a given key? - * - * @param string $key The data key - * - * @return bool - */ - public function has($key) - { - return array_key_exists($key, $this->data); - } - - /** - * Remove item from collection - * - * @param string $key The data key - */ - public function remove($key) - { - unset($this->data[$key]); - } - - /** - * Remove all items from collection - */ - public function clear() - { - $this->data = []; - } - - /******************************************************************************** - * ArrayAccess interface - *******************************************************************************/ - - /** - * Does this collection have a given key? - * - * @param string $key The data key - * - * @return bool - */ - public function offsetExists($key) - { - return $this->has($key); - } - - /** - * Get collection item for key - * - * @param string $key The data key - * - * @return mixed The key's value, or the default value - */ - public function offsetGet($key) - { - return $this->get($key); - } - - /** - * Set collection item - * - * @param string $key The data key - * @param mixed $value The data value - */ - public function offsetSet($key, $value) - { - $this->set($key, $value); - } - - /** - * Remove item from collection - * - * @param string $key The data key - */ - public function offsetUnset($key) - { - $this->remove($key); - } - - /** - * Get number of items in collection - * - * @return int - */ - public function count() - { - return count($this->data); - } - - /******************************************************************************** - * IteratorAggregate interface - *******************************************************************************/ - - /** - * Get collection iterator - * - * @return \ArrayIterator - */ - public function getIterator() - { - return new ArrayIterator($this->data); - } -} diff --git a/src/CollectionInterface.php b/src/CollectionInterface.php deleted file mode 100644 index 3c9c192..0000000 --- a/src/CollectionInterface.php +++ /dev/null @@ -1,32 +0,0 @@ - '', - 'domain' => null, - 'hostonly' => null, - 'path' => null, - 'expires' => null, - 'secure' => false, - 'httponly' => false - ]; - - /** - * Create new cookies helper - * - * @param array $cookies - */ - public function __construct(array $cookies = []) - { - $this->requestCookies = $cookies; - } - - /** - * Set default cookie properties - * - * @param array $settings - */ - public function setDefaults(array $settings) - { - $this->defaults = array_replace($this->defaults, $settings); - } - - /** - * Get request cookie - * - * @param string $name Cookie name - * @param mixed $default Cookie default value - * - * @return mixed Cookie value if present, else default - */ - public function get($name, $default = null) - { - return isset($this->requestCookies[$name]) ? $this->requestCookies[$name] : $default; - } - - /** - * Set response cookie - * - * @param string $name Cookie name - * @param string|array $value Cookie value, or cookie properties - */ - public function set($name, $value) - { - if (!is_array($value)) { - $value = ['value' => (string)$value]; - } - $this->responseCookies[$name] = array_replace($this->defaults, $value); - } - - /** - * Convert to `Set-Cookie` headers - * - * @return string[] - */ - public function toHeaders() - { - $headers = []; - foreach ($this->responseCookies as $name => $properties) { - $headers[] = $this->toHeader($name, $properties); - } - - return $headers; - } - - /** - * Convert to `Set-Cookie` header - * - * @param string $name Cookie name - * @param array $properties Cookie properties - * - * @return string - */ - protected function toHeader($name, array $properties) - { - $result = urlencode($name) . '=' . urlencode($properties['value']); - - if (isset($properties['domain'])) { - $result .= '; domain=' . $properties['domain']; - } - - if (isset($properties['path'])) { - $result .= '; path=' . $properties['path']; - } - - if (isset($properties['expires'])) { - if (is_string($properties['expires'])) { - $timestamp = strtotime($properties['expires']); - } else { - $timestamp = (int)$properties['expires']; - } - if ($timestamp !== 0) { - $result .= '; expires=' . gmdate('D, d-M-Y H:i:s e', $timestamp); - } - } - - if (isset($properties['secure']) && $properties['secure']) { - $result .= '; secure'; - } - - if (isset($properties['hostonly']) && $properties['hostonly']) { - $result .= '; HostOnly'; - } - - if (isset($properties['httponly']) && $properties['httponly']) { - $result .= '; HttpOnly'; - } - - return $result; - } - - /** - * Parse HTTP request `Cookie:` header and extract - * into a PHP associative array. - * - * @param string|string[] $header The raw HTTP request `Cookie:` header - * - * @return array Associative array of cookie names and values - * - * @throws InvalidArgumentException if the cookie data cannot be parsed - */ - public static function parseHeader($header) - { - if (is_array($header) === true) { - $header = isset($header[0]) ? $header[0] : ''; - } - - if (is_string($header) === false) { - throw new InvalidArgumentException('Cannot parse Cookie data. Header value must be a string.'); - } - - $header = rtrim($header, "\r\n"); - $pieces = preg_split('@[;]\s*@', $header); - $cookies = []; - - foreach ($pieces as $cookie) { - $cookie = explode('=', $cookie, 2); - - if (count($cookie) === 2) { - $key = urldecode($cookie[0]); - $value = urldecode($cookie[1]); - - if (!isset($cookies[$key])) { - $cookies[$key] = $value; - } - } - } - - return $cookies; - } -} diff --git a/src/CookiesInterface.php b/src/CookiesInterface.php deleted file mode 100644 index c0d9c98..0000000 --- a/src/CookiesInterface.php +++ /dev/null @@ -1,23 +0,0 @@ -response = $response; + $this->streamFactory = $streamFactory; + } + + /** + * Disable magic setter to ensure immutability + * @param mixed $name + * @param mixed $value + */ + public function __set($name, $value) + { + } + + /** + * Gets the body of the message. + * + * @return StreamInterface Returns the body as a stream. + */ + public function getBody() + { + return $this->response->getBody(); + } + + /** + * Retrieves a message header value by the given case-insensitive name. + * + * This method returns an array of all the header values of the given + * case-insensitive header name. + * + * If the header does not appear in the message, this method MUST return an + * empty array. + * + * @param string $name Case-insensitive header field name. + * @return string[] An array of string values as provided for the given + * header. If the header does not appear in the message, this method MUST + * return an empty array. + */ + public function getHeader($name) + { + return $this->response->getHeader($name); + } + + /** + * Retrieves a comma-separated string of the values for a single header. + * + * This method returns all of the header values of the given + * case-insensitive header name as a string concatenated together using + * a comma. + * + * NOTE: Not all header values may be appropriately represented using + * comma concatenation. For such headers, use getHeader() instead + * and supply your own delimiter when concatenating. + * + * If the header does not appear in the message, this method MUST return + * an empty string. + * + * @param string $name Case-insensitive header field name. + * @return string A string of values as provided for the given header + * concatenated together using a comma. If the header does not appear in + * the message, this method MUST return an empty string. + */ + public function getHeaderLine($name) + { + return $this->response->getHeaderLine($name); + } + + /** + * Retrieves all message header values. + * + * The keys represent the header name as it will be sent over the wire, and + * each value is an array of strings associated with the header. + * + * // Represent the headers as a string + * foreach ($message->getHeaders() as $name => $values) { + * echo $name . ": " . implode(", ", $values); + * } + * + * // Emit headers iteratively: + * foreach ($message->getHeaders() as $name => $values) { + * foreach ($values as $value) { + * header(sprintf('%s: %s', $name, $value), false); + * } + * } + * + * While header names are not case-sensitive, getHeaders() will preserve the + * exact case in which headers were originally specified. + * + * @return string[][] Returns an associative array of the message's headers. Each + * key MUST be a header name, and each value MUST be an array of strings + * for that header. + */ + public function getHeaders() + { + return $this->response->getHeaders(); + } + + /** + * Retrieves the HTTP protocol version as a string. + * + * The string MUST contain only the HTTP version number (e.g., "1.1", "1.0"). + * + * @return string HTTP protocol version. + */ + public function getProtocolVersion() + { + return $this->response->getProtocolVersion(); + } + + /** + * Gets the response reason phrase associated with the status code. + * + * Because a reason phrase is not a required element in a response + * status line, the reason phrase value MAY be null. Implementations MAY + * choose to return the default RFC 7231 recommended reason phrase (or those + * listed in the IANA HTTP Status Code Registry) for the response's + * status code. + * + * @link http://tools.ietf.org/html/rfc7231#section-6 + * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml + * @return string Reason phrase; must return an empty string if none present. + */ + public function getReasonPhrase() + { + return $this->response->getReasonPhrase(); + } + + /** + * Gets the response status code. + * + * The status code is a 3-digit integer result code of the server's attempt + * to understand and satisfy the request. + * + * @return int Status code. + */ + public function getStatusCode() + { + return $this->response->getStatusCode(); + } + + /** + * Checks if a header exists by the given case-insensitive name. + * + * @param string $name Case-insensitive header field name. + * @return bool Returns true if any header names match the given header + * name using a case-insensitive string comparison. Returns false if + * no matching header name is found in the message. + */ + public function hasHeader($name) + { + return $this->response->hasHeader($name); + } + + /** + * Return an instance with the specified header appended with the given value. + * + * Existing values for the specified header will be maintained. The new + * value(s) will be appended to the existing list. If the header did not + * exist previously, it will be added. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * new header and/or value. + * + * @param string $name Case-insensitive header field name to add. + * @param string|string[] $value Header value(s). + * @return ResponseDecorator + * @throws InvalidArgumentException for invalid header names or values. + */ + public function withAddedHeader($name, $value) + { + $response = $this->response->withAddedHeader($name, $value); + return new ResponseDecorator($response, $this->streamFactory); + } + + /** + * Return an instance with the specified message body. + * + * The body MUST be a StreamInterface object. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * new body stream. + * + * @param StreamInterface $body Body. + * @return ResponseDecorator + * @throws InvalidArgumentException When the body is not valid. + */ + public function withBody(StreamInterface $body) + { + $response = $this->response->withBody($body); + return new ResponseDecorator($response, $this->streamFactory); + } + + /** + * Return an instance with the provided value replacing the specified header. + * + * While header names are case-insensitive, the casing of the header will + * be preserved by this function, and returned from getHeaders(). + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * new and/or updated header and value. + * + * @param string $name Case-insensitive header field name. + * @param string|string[] $value Header value(s). + * @return ResponseDecorator + * @throws InvalidArgumentException for invalid header names or values. + */ + public function withHeader($name, $value) + { + $response = $this->response->withHeader($name, $value); + return new ResponseDecorator($response, $this->streamFactory); + } + + /** + * Return an instance without the specified header. + * + * Header resolution MUST be done without case-sensitivity. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that removes + * the named header. + * + * @param string $name Case-insensitive header field name to remove. + * @return ResponseDecorator + */ + public function withoutHeader($name) + { + $response = $this->response->withoutHeader($name); + return new ResponseDecorator($response, $this->streamFactory); + } + + /** + * Return an instance with the specified HTTP protocol version. + * + * The version string MUST contain only the HTTP version number (e.g., + * "1.1", "1.0"). + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * new protocol version. + * + * @param string $version HTTP protocol version + * @return ResponseDecorator + */ + public function withProtocolVersion($version) + { + $response = $this->response->withProtocolVersion($version); + return new ResponseDecorator($response, $this->streamFactory); + } + + /** + * Return an instance with the specified status code and, optionally, reason phrase. + * + * If no reason phrase is specified, implementations MAY choose to default + * to the RFC 7231 or IANA recommended reason phrase for the response's + * status code. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated status and reason phrase. + * + * @link http://tools.ietf.org/html/rfc7231#section-6 + * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml + * @param int $code The 3-digit integer result code to set. + * @param string $reasonPhrase The reason phrase to use with the + * provided status code; if none is provided, implementations MAY + * use the defaults as suggested in the HTTP specification. + * @return ResponseDecorator + * @throws InvalidArgumentException For invalid status code arguments. + */ + public function withStatus($code, $reasonPhrase = '') + { + $response = $this->response->withStatus($code, $reasonPhrase); + return new ResponseDecorator($response, $this->streamFactory); + } + + /** + * Json. + * + * Note: This method is not part of the PSR-7 standard. + * + * This method prepares the response object to return an HTTP Json + * response to the client. + * + * @param mixed $data The data + * @param int $status The HTTP status code. + * @param int $options Json encoding options + * @param int $depth Json encoding max depth + * @return ResponseDecorator + */ + public function withJson($data, int $status = null, int $options = 0, int $depth = 512): ResponseInterface + { + $json = (string) json_encode($data, $options, $depth); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw new RuntimeException(json_last_error_msg(), json_last_error()); + } + + $response = $this->response + ->withHeader('Content-Type', 'application/json;charset=utf-8') + ->withBody($this->streamFactory->createStream($json)); + + if ($status !== null) { + $response = $response->withStatus($status); + } + + return new ResponseDecorator($response, $this->streamFactory); + } + + /** + * Redirect. + * + * Note: This method is not part of the PSR-7 standard. + * + * This method prepares the response object to return an HTTP Redirect + * response to the client. + * + * @param string $url The redirect destination. + * @param int|null $status The redirect HTTP status code. + * @return ResponseInterface + */ + public function withRedirect(string $url, $status = null): ResponseInterface + { + $response = $this->response->withHeader('Location', $url); + + if (!is_null($status)) { + return $response->withStatus($status); + } + + return $response->withStatus(302); + } + + /** + * Write data to the response body. + * + * Note: This method is not part of the PSR-7 standard. + * + * Proxies to the underlying stream and writes the provided data to it. + * + * @param string $data + * @return self + */ + public function write($data) + { + $this->response->getBody()->write($data); + return $this; + } + + /** + * Is this response a client error? + * + * Note: This method is not part of the PSR-7 standard. + * + * @return bool + */ + public function isClientError(): bool + { + return $this->response->getStatusCode() >= 400 && $this->response->getStatusCode() < 500; + } + + /** + * Is this response empty? + * + * Note: This method is not part of the PSR-7 standard. + * + * @return bool + */ + public function isEmpty(): bool + { + return in_array($this->response->getStatusCode(), [204, 205, 304]); + } + + /** + * Is this response forbidden? + * + * Note: This method is not part of the PSR-7 standard. + * + * @return bool + * @api + */ + public function isForbidden(): bool + { + return $this->response->getStatusCode() === 403; + } + + /** + * Is this response informational? + * + * Note: This method is not part of the PSR-7 standard. + * + * @return bool + */ + public function isInformational(): bool + { + return $this->response->getStatusCode() >= 100 && $this->response->getStatusCode() < 200; + } + + /** + * Is this response OK? + * + * Note: This method is not part of the PSR-7 standard. + * + * @return bool + */ + public function isOk(): bool + { + return $this->response->getStatusCode() === 200; + } + + /** + * Is this response not Found? + * + * Note: This method is not part of the PSR-7 standard. + * + * @return bool + */ + public function isNotFound(): bool + { + return $this->response->getStatusCode() === 404; + } + + /** + * Is this response a redirect? + * + * Note: This method is not part of the PSR-7 standard. + * + * @return bool + */ + public function isRedirect(): bool + { + return in_array($this->response->getStatusCode(), [301, 302, 303, 307, 308]); + } + + /** + * Is this response a redirection? + * + * Note: This method is not part of the PSR-7 standard. + * + * @return bool + */ + public function isRedirection(): bool + { + return $this->response->getStatusCode() >= 300 && $this->response->getStatusCode() < 400; + } + + /** + * Is this response a server error? + * + * Note: This method is not part of the PSR-7 standard. + * + * @return bool + */ + public function isServerError(): bool + { + return $this->response->getStatusCode() >= 500 && $this->response->getStatusCode() < 600; + } + + /** + * Is this response successful? + * + * Note: This method is not part of the PSR-7 standard. + * + * @return bool + */ + public function isSuccessful(): bool + { + return $this->response->getStatusCode() >= 200 && $this->response->getStatusCode() < 300; + } + + /** + * Convert response to string. + * + * Note: This method is not part of the PSR-7 standard. + * + * @return string + */ + public function __toString(): string + { + $output = sprintf( + 'HTTP/%s %s %s%s', + $this->response->getProtocolVersion(), + $this->response->getStatusCode(), + $this->response->getReasonPhrase(), + self::EOL + ); + + foreach ($this->response->getHeaders() as $name => $values) { + $output .= sprintf('%s: %s', $name, $this->response->getHeaderLine($name)) . self::EOL; + } + + $output .= self::EOL; + $output .= (string) $this->response->getBody(); + + return $output; + } +} diff --git a/src/Request.php b/src/Decorators/ServerRequestDecorator.php similarity index 63% rename from src/Request.php rename to src/Decorators/ServerRequestDecorator.php index 9144fe3..92de46d 100644 --- a/src/Request.php +++ b/src/Decorators/ServerRequestDecorator.php @@ -3,176 +3,49 @@ * Slim Framework (https://slimframework.com) * * @link https://github.com/slimphp/Slim-Http - * @copyright Copyright (c) 2011-2017 Josh Lockhart + * @copyright Copyright (c) 2011-2018 Josh Lockhart * @license https://github.com/slimphp/Slim-Http/blob/master/LICENSE (MIT License) */ -namespace Slim\Http; +namespace Slim\Http\Decorators; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UriInterface; use Closure; use InvalidArgumentException; -use Psr\Http\Message\UploadedFileInterface; use RuntimeException; -use Psr\Http\Message\ServerRequestInterface; -use Psr\Http\Message\UriInterface; -use Psr\Http\Message\StreamInterface; /** - * Request - * - * This class represents an HTTP request. It manages - * the request method, URI, headers, cookies, and body - * according to the PSR-7 standard. - * - * @link https://github.com/php-fig/http-message/blob/master/src/MessageInterface.php - * @link https://github.com/php-fig/http-message/blob/master/src/RequestInterface.php - * @link https://github.com/php-fig/http-message/blob/master/src/ServerRequestInterface.php + * Class ServerRequestDecorator + * @package Slim\Http\Decorators */ -class Request extends Message implements ServerRequestInterface +class ServerRequestDecorator implements ServerRequestInterface { /** - * The request method - * - * @var string - */ - protected $method; - - /** - * The request URI object - * - * @var \Psr\Http\Message\UriInterface - */ - protected $uri; - - /** - * The request URI target (path + query string) - * - * @var string - */ - protected $requestTarget; - - /** - * The request query string params - * - * @var array - */ - protected $queryParams; - - /** - * The request cookies - * - * @var array + * @var ServerRequestInterface */ - protected $cookies; + private $serverRequest; /** - * The server environment variables at the time the request was created. - * * @var array */ - protected $serverParams; - - /** - * The request attributes (route segment names and values) - * - * @var Collection - */ - protected $attributes; + private $bodyParsers; /** - * The request body parsed (if possible) into a PHP array or object - * - * @var null|array|object|false - */ - protected $bodyParsed = false; - - /** - * List of request body parsers (e.g., url-encoded, JSON, XML, multipart) - * - * @var callable[] - */ - protected $bodyParsers = []; - - /** - * List of uploaded files - * - * @var UploadedFileInterface[] - */ - protected $uploadedFiles; - - /** - * Create new HTTP request with data extracted from the application - * Environment object - * - * @param array $globals The global server variables. - * - * @return static + * ServerRequestDecorator constructor. + * @param ServerRequestInterface $serverRequest */ - public static function createFromGlobals(array $globals) + public function __construct(ServerRequestInterface $serverRequest) { - $method = isset($globals['REQUEST_METHOD']) ? $globals['REQUEST_METHOD'] : null; - $uri = Uri::createFromGlobals($globals); - $headers = Headers::createFromGlobals($globals); - $cookies = Cookies::parseHeader($headers->get('Cookie', [])); - $serverParams = $globals; - $body = new RequestBody(); - $uploadedFiles = UploadedFile::createFromGlobals($globals); - - $request = new static($method, $uri, $headers, $cookies, $serverParams, $body, $uploadedFiles); - - if ($method === 'POST' && - in_array($request->getMediaType(), ['application/x-www-form-urlencoded', 'multipart/form-data']) - ) { - // parsed body must be $_POST - $request = $request->withParsedBody($_POST); - } - return $request; - } - - /** - * Create new HTTP request. - * - * Adds a host header when none was provided and a host is defined in uri. - * - * @param string $method The request method - * @param UriInterface $uri The request URI object - * @param HeadersInterface $headers The request headers collection - * @param array $cookies The request cookies collection - * @param array $serverParams The server environment variables - * @param StreamInterface $body The request body object - * @param array $uploadedFiles The request uploadedFiles collection - * @throws InvalidArgumentException on invalid HTTP method - */ - public function __construct( - $method, - UriInterface $uri, - HeadersInterface $headers, - array $cookies, - array $serverParams, - StreamInterface $body, - array $uploadedFiles = [] - ) { - $this->method = $this->filterMethod($method); - $this->uri = $uri; - $this->headers = $headers; - $this->cookies = $cookies; - $this->serverParams = $serverParams; - $this->attributes = new Collection(); - $this->body = $body; - $this->uploadedFiles = $uploadedFiles; - - if (isset($serverParams['SERVER_PROTOCOL'])) { - $this->protocolVersion = str_replace('HTTP/', '', $serverParams['SERVER_PROTOCOL']); - } - - if (!$this->headers->has('Host') || $this->uri->getHost() !== '') { - $this->headers->set('Host', $this->uri->getHost()); - } + $this->serverRequest = $serverRequest; $this->registerMediaTypeParser('application/json', function ($input) { $result = json_decode($input, true); + if (!is_array($result)) { return null; } + return $result; }); @@ -180,12 +53,15 @@ public function __construct( $backup = libxml_disable_entity_loader(true); $backup_errors = libxml_use_internal_errors(true); $result = simplexml_load_string($input); + libxml_disable_entity_loader($backup); libxml_clear_errors(); libxml_use_internal_errors($backup_errors); + if ($result === false) { return null; } + return $result; }); @@ -193,12 +69,15 @@ public function __construct( $backup = libxml_disable_entity_loader(true); $backup_errors = libxml_use_internal_errors(true); $result = simplexml_load_string($input); + libxml_disable_entity_loader($backup); libxml_clear_errors(); libxml_use_internal_errors($backup_errors); + if ($result === false) { return null; } + return $result; }); @@ -209,198 +88,245 @@ public function __construct( } /** - * This method is applied to the cloned object - * after PHP performs an initial shallow-copy. This - * method completes a deep-copy by creating new objects - * for the cloned object's internal reference pointers. + * Disable magic setter to ensure immutability + * @param mixed $name + * @param mixed $value */ - public function __clone() + public function __set($name, $value) { - $this->headers = clone $this->headers; - $this->attributes = clone $this->attributes; - $this->body = clone $this->body; } - /******************************************************************************* - * Method - ******************************************************************************/ - /** - * Retrieves the HTTP method of the request. + * Retrieve a single derived request attribute. * - * @return string Returns the request method. + * Retrieves a single derived request attribute as described in + * getAttributes(). If the attribute has not been previously set, returns + * the default value as provided. + * + * This method obviates the need for a hasAttribute() method, as it allows + * specifying a default value to return if the attribute is not found. + * + * @see getAttributes() + * @param string $name The attribute name. + * @param mixed $default Default value to return if the attribute does not exist. + * @return mixed */ - public function getMethod() + public function getAttribute($name, $default = null) { - return $this->method; + return $this->serverRequest->getAttribute($name, $default); } /** - * Return an instance with the provided HTTP method. - * - * While HTTP method names are typically all uppercase characters, HTTP - * method names are case-sensitive and thus implementations SHOULD NOT - * modify the given string. + * Retrieve attributes derived from the request. * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that has the - * changed request method. + * The request "attributes" may be used to allow injection of any + * parameters derived from the request: e.g., the results of path + * match operations; the results of decrypting cookies; the results of + * deserializing non-form-encoded message bodies; etc. Attributes + * will be application and request specific, and CAN be mutable. * - * @param string $method Case-sensitive method. - * @return static - * @throws \InvalidArgumentException for invalid HTTP methods. + * @return array Attributes derived from the request. */ - public function withMethod($method) + public function getAttributes() { - $method = $this->filterMethod($method); - $clone = clone $this; - $clone->method = $method; - - return $clone; + return $this->serverRequest->getAttributes(); } /** - * Validate the HTTP method + * Gets the body of the message. * - * @param null|string $method - * @return string - * @throws \InvalidArgumentException on invalid HTTP method. + * @return StreamInterface Returns the body as a stream. */ - protected function filterMethod($method) + public function getBody() { - if ($method === null) { - return ''; - } - - if (!is_string($method)) { - throw new InvalidArgumentException(sprintf( - 'Unsupported HTTP method; must be a string, received %s', - (is_object($method) ? get_class($method) : gettype($method)) - )); - } - - if (preg_match("/^[!#$%&'*+.^_`|~0-9a-z-]+$/i", $method) !== 1) { - throw new InvalidArgumentException(sprintf( - 'Unsupported HTTP method "%s" provided', - $method - )); - } - - return $method; + return $this->serverRequest->getBody(); } /** - * Does this request use a given method? + * Retrieve cookies. * - * Note: This method is not part of the PSR-7 standard. + * Retrieves cookies sent by the client to the server. * - * @param string $method HTTP method - * @return bool + * The data MUST be compatible with the structure of the $_COOKIE + * superglobal. + * + * @return array */ - public function isMethod($method) + public function getCookieParams() { - return $this->getMethod() === $method; + return $this->serverRequest->getCookieParams(); } /** - * Is this a GET request? + * Retrieves a message header value by the given case-insensitive name. * - * Note: This method is not part of the PSR-7 standard. + * This method returns an array of all the header values of the given + * case-insensitive header name. * - * @return bool + * If the header does not appear in the message, this method MUST return an + * empty array. + * + * @param string $name Case-insensitive header field name. + * @return string[] An array of string values as provided for the given + * header. If the header does not appear in the message, this method MUST + * return an empty array. */ - public function isGet() + public function getHeader($name) { - return $this->isMethod('GET'); + return $this->serverRequest->getHeader($name); } /** - * Is this a POST request? + * Retrieves a comma-separated string of the values for a single header. * - * Note: This method is not part of the PSR-7 standard. + * This method returns all of the header values of the given + * case-insensitive header name as a string concatenated together using + * a comma. * - * @return bool - */ - public function isPost() - { - return $this->isMethod('POST'); - } - - /** - * Is this a PUT request? + * NOTE: Not all header values may be appropriately represented using + * comma concatenation. For such headers, use getHeader() instead + * and supply your own delimiter when concatenating. * - * Note: This method is not part of the PSR-7 standard. + * If the header does not appear in the message, this method MUST return + * an empty string. * - * @return bool + * @param string $name Case-insensitive header field name. + * @return string A string of values as provided for the given header + * concatenated together using a comma. If the header does not appear in + * the message, this method MUST return an empty string. */ - public function isPut() + public function getHeaderLine($name) { - return $this->isMethod('PUT'); + return $this->serverRequest->getHeaderLine($name); } /** - * Is this a PATCH request? + * Retrieves all message header values. * - * Note: This method is not part of the PSR-7 standard. + * The keys represent the header name as it will be sent over the wire, and + * each value is an array of strings associated with the header. * - * @return bool + * // Represent the headers as a string + * foreach ($message->getHeaders() as $name => $values) { + * echo $name . ": " . implode(", ", $values); + * } + * + * // Emit headers iteratively: + * foreach ($message->getHeaders() as $name => $values) { + * foreach ($values as $value) { + * header(sprintf('%s: %s', $name, $value), false); + * } + * } + * + * While header names are not case-sensitive, getHeaders() will preserve the + * exact case in which headers were originally specified. + * + * @return string[][] Returns an associative array of the message's headers. Each + * key MUST be a header name, and each value MUST be an array of strings + * for that header. */ - public function isPatch() + public function getHeaders() { - return $this->isMethod('PATCH'); + return $this->serverRequest->getHeaders(); } /** - * Is this a DELETE request? - * - * Note: This method is not part of the PSR-7 standard. + * Retrieves the HTTP method of the request. * - * @return bool + * @return string Returns the request method. */ - public function isDelete() + public function getMethod() { - return $this->isMethod('DELETE'); + return $this->serverRequest->getMethod(); } /** - * Is this a HEAD request? + * Retrieve any parameters provided in the request body. * - * Note: This method is not part of the PSR-7 standard. + * If the request Content-Type is either application/x-www-form-urlencoded + * or multipart/form-data, and the request method is POST, this method MUST + * return the contents of $_POST. * - * @return bool + * Otherwise, this method may return any results of deserializing + * the request body content; as parsing returns structured content, the + * potential types MUST be arrays or objects only. A null value indicates + * the absence of body content. + * + * @return null|array|object The deserialized body parameters, if any. + * These will typically be an array or object. */ - public function isHead() + public function getParsedBody() { - return $this->isMethod('HEAD'); + $parsedBody = $this->serverRequest->getParsedBody(); + + if ($parsedBody !== null) { + return $parsedBody; + } + + $mediaType = $this->getMediaType(); + if ($mediaType === null) { + return null; + } + + // Look for a media type with a structured syntax suffix (RFC 6839) + $parts = explode('+', $mediaType); + if (count($parts) >= 2) { + $mediaType = 'application/' . $parts[count($parts)-1]; + } + + if (isset($this->bodyParsers[$mediaType]) === true) { + $body = (string) $this->getBody(); + $parsed = $this->bodyParsers[$mediaType]($body); + + if (!is_null($parsed) && !is_object($parsed) && !is_array($parsed)) { + throw new RuntimeException( + 'Request body media type parser return value must be an array, an object, or null' + ); + } + + return $parsed; + } + + return null; } /** - * Is this a OPTIONS request? + * Retrieves the HTTP protocol version as a string. * - * Note: This method is not part of the PSR-7 standard. + * The string MUST contain only the HTTP version number (e.g., "1.1", "1.0"). * - * @return bool + * @return string HTTP protocol version. */ - public function isOptions() + public function getProtocolVersion() { - return $this->isMethod('OPTIONS'); + return $this->serverRequest->getProtocolVersion(); } /** - * Is this an XHR request? + * Retrieve query string arguments. * - * Note: This method is not part of the PSR-7 standard. + * Retrieves the deserialized query string arguments, if any. * - * @return bool + * Note: the query params might not be in sync with the URI or server + * params. If you need to ensure you are only getting the original + * values, you may need to parse the query string from `getUri()->getQuery()` + * or from the `QUERY_STRING` server param. + * + * @return array */ - public function isXhr() + public function getQueryParams() { - return $this->getHeaderLine('X-Requested-With') === 'XMLHttpRequest'; - } + $queryParams = $this->serverRequest->getQueryParams(); + + if (is_array($queryParams) && !empty($queryParams)) { + return $queryParams; + } - /******************************************************************************* - * URI - ******************************************************************************/ + $parsedQueryParams = []; + parse_str($this->serverRequest->getUri()->getQuery(), $parsedQueryParams); + + return $parsedQueryParams; + } /** * Retrieves the message's request target. @@ -420,55 +346,38 @@ public function isXhr() */ public function getRequestTarget() { - if ($this->requestTarget) { - return $this->requestTarget; - } - - if ($this->uri === null) { - return '/'; - } - - $path = $this->uri->getPath(); - $path = '/' . ltrim($path, '/'); - - $query = $this->uri->getQuery(); - if ($query) { - $path .= '?' . $query; - } - $this->requestTarget = $path; + return $this->serverRequest->getRequestTarget(); + } - return $this->requestTarget; + /** + * Retrieve server parameters. + * + * Retrieves data related to the incoming request environment, + * typically derived from PHP's $_SERVER superglobal. The data IS NOT + * REQUIRED to originate from $_SERVER. + * + * @return array + */ + public function getServerParams() + { + return $this->serverRequest->getServerParams(); } /** - * Return an instance with the specific request-target. + * Retrieve normalized file upload data. * - * If the request needs a non-origin-form request-target — e.g., for - * specifying an absolute-form, authority-form, or asterisk-form — - * this method may be used to create an instance with the specified - * request-target, verbatim. + * This method returns upload metadata in a normalized tree, with each leaf + * an instance of Psr\Http\Message\UploadedFileInterface. * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that has the - * changed request target. + * These values MAY be prepared from $_FILES or the message body during + * instantiation, or MAY be injected via withUploadedFiles(). * - * @link http://tools.ietf.org/html/rfc7230#section-2.7 (for the various - * request-target forms allowed in request messages) - * @param mixed $requestTarget - * @return static - * @throws InvalidArgumentException if the request target is invalid + * @return array An array tree of UploadedFileInterface instances; an empty + * array MUST be returned if no data is present. */ - public function withRequestTarget($requestTarget) + public function getUploadedFiles() { - if (preg_match('#\s#', $requestTarget)) { - throw new InvalidArgumentException( - 'Invalid request target provided; must be a string and cannot contain whitespace' - ); - } - $clone = clone $this; - $clone->requestTarget = $requestTarget; - - return $clone; + return $this->serverRequest->getUploadedFiles(); } /** @@ -482,238 +391,264 @@ public function withRequestTarget($requestTarget) */ public function getUri() { - return $this->uri; + return $this->serverRequest->getUri(); } /** - * Returns an instance with the provided URI. - * - * This method MUST update the Host header of the returned request by - * default if the URI contains a host component. If the URI does not - * contain a host component, any pre-existing Host header MUST be carried - * over to the returned request. + * Checks if a header exists by the given case-insensitive name. * - * You can opt-in to preserving the original state of the Host header by - * setting `$preserveHost` to `true`. When `$preserveHost` is set to - * `true`, this method interacts with the Host header in the following ways: + * @param string $name Case-insensitive header field name. + * @return bool Returns true if any header names match the given header + * name using a case-insensitive string comparison. Returns false if + * no matching header name is found in the message. + */ + public function hasHeader($name) + { + return $this->serverRequest->hasHeader($name); + } + + /** + * Return an instance with the specified header appended with the given value. * - * - If the the Host header is missing or empty, and the new URI contains - * a host component, this method MUST update the Host header in the returned - * request. - * - If the Host header is missing or empty, and the new URI does not contain a - * host component, this method MUST NOT update the Host header in the returned - * request. - * - If a Host header is present and non-empty, this method MUST NOT update - * the Host header in the returned request. + * Existing values for the specified header will be maintained. The new + * value(s) will be appended to the existing list. If the header did not + * exist previously, it will be added. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the - * new UriInterface instance. + * new header and/or value. * - * @link http://tools.ietf.org/html/rfc3986#section-4.3 - * @param UriInterface $uri New request URI to use. - * @param bool $preserveHost Preserve the original state of the Host header. - * @return static + * @param string $name Case-insensitive header field name to add. + * @param string|string[] $value Header value(s). + * @return ServerRequestDecorator + * @throws InvalidArgumentException for invalid header names or values. */ - public function withUri(UriInterface $uri, $preserveHost = false) + public function withAddedHeader($name, $value) { - $clone = clone $this; - $clone->uri = $uri; - - if (!$preserveHost) { - if ($uri->getHost() !== '') { - $clone->headers->set('Host', $uri->getHost()); - } - } else { - if ($uri->getHost() !== '' && (!$this->hasHeader('Host') || $this->getHeaderLine('Host') === '')) { - $clone->headers->set('Host', $uri->getHost()); - } - } - - return $clone; + $serverRequest = $this->serverRequest->withAddedHeader($name, $value); + return new ServerRequestDecorator($serverRequest); } /** - * Get request content type. + * Return an instance with the specified derived request attribute. * - * Note: This method is not part of the PSR-7 standard. + * This method allows setting a single derived request attribute as + * described in getAttributes(). + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated attribute. * - * @return string|null The request content type, if known + * @see getAttributes() + * @param string $name The attribute name. + * @param mixed $value The value of the attribute. + * @return ServerRequestDecorator */ - public function getContentType() + public function withAttribute($name, $value) { - $result = $this->getHeader('Content-Type'); - - return $result ? $result[0] : null; + $serverRequest = $this->serverRequest->withAttribute($name, $value); + return new ServerRequestDecorator($serverRequest); } /** - * Get request media type, if known. + * Create a new instance with the specified derived request attributes. * * Note: This method is not part of the PSR-7 standard. * - * @return string|null The request media type, minus content-type params + * This method allows setting all new derived request attributes as + * described in getAttributes(). + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * updated attributes. + * + * @param array $attributes New attributes + * @return ServerRequestDecorator */ - public function getMediaType() + public function withAttributes(array $attributes) { - $contentType = $this->getContentType(); - if ($contentType) { - $contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType); + $serverRequest = $this->serverRequest; - return strtolower($contentTypeParts[0]); + foreach ($attributes as $attribute => $value) { + $serverRequest = $serverRequest->withAttribute($attribute, $value); } - return null; + return new ServerRequestDecorator($serverRequest); } /** - * Get request media type params, if known. + * Return an instance that removes the specified derived request attribute. * - * Note: This method is not part of the PSR-7 standard. + * This method allows removing a single derived request attribute as + * described in getAttributes(). * - * @return array + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that removes + * the attribute. + * + * @see getAttributes() + * @param string $name The attribute name. + * @return ServerRequestDecorator */ - public function getMediaTypeParams() + public function withoutAttribute($name) { - $contentType = $this->getContentType(); - $contentTypeParams = []; - if ($contentType) { - $contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType); - $contentTypePartsLength = count($contentTypeParts); - for ($i = 1; $i < $contentTypePartsLength; $i++) { - $paramParts = explode('=', $contentTypeParts[$i]); - $contentTypeParams[strtolower($paramParts[0])] = $paramParts[1]; - } - } - - return $contentTypeParams; + $serverRequest = $this->serverRequest->withoutAttribute($name); + return new ServerRequestDecorator($serverRequest); } /** - * Get request content character set, if known. + * Return an instance with the specified message body. * - * Note: This method is not part of the PSR-7 standard. + * The body MUST be a StreamInterface object. * - * @return string|null + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * new body stream. + * + * @param StreamInterface $body Body. + * @return ServerRequestDecorator + * @throws InvalidArgumentException When the body is not valid. */ - public function getContentCharset() + public function withBody(StreamInterface $body) { - $mediaTypeParams = $this->getMediaTypeParams(); - if (isset($mediaTypeParams['charset'])) { - return $mediaTypeParams['charset']; - } - - return null; + $serverRequest = $this->serverRequest->withBody($body); + return new ServerRequestDecorator($serverRequest); } /** - * Get request content length, if known. + * Return an instance with the specified cookies. * - * Note: This method is not part of the PSR-7 standard. + * The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST + * be compatible with the structure of $_COOKIE. Typically, this data will + * be injected at instantiation. * - * @return int|null + * This method MUST NOT update the related Cookie header of the request + * instance, nor related values in the server params. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated cookie values. + * + * @param array $cookies Array of key/value pairs representing cookies. + * @return ServerRequestDecorator */ - public function getContentLength() + public function withCookieParams(array $cookies) { - $result = $this->headers->get('Content-Length'); - - return $result ? (int)$result[0] : null; + $serverRequest = $this->serverRequest->withCookieParams($cookies); + return new ServerRequestDecorator($serverRequest); } - /******************************************************************************* - * Cookies - ******************************************************************************/ - /** - * Retrieve cookies. + * Return an instance with the provided value replacing the specified header. * - * Retrieves cookies sent by the client to the server. + * While header names are case-insensitive, the casing of the header will + * be preserved by this function, and returned from getHeaders(). * - * The data MUST be compatible with the structure of the $_COOKIE - * superglobal. + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * new and/or updated header and value. * - * @return array + * @param string $name Case-insensitive header field name. + * @param string|string[] $value Header value(s). + * @return ServerRequestDecorator + * @throws InvalidArgumentException for invalid header names or values. */ - public function getCookieParams() + public function withHeader($name, $value) { - return $this->cookies; + $serverRequest = $this->serverRequest->withHeader($name, $value); + return new ServerRequestDecorator($serverRequest); } /** - * Fetch cookie value from cookies sent by the client to the server. + * Return an instance without the specified header. * - * Note: This method is not part of the PSR-7 standard. + * Header resolution MUST be done without case-sensitivity. * - * @param string $key The attribute name. - * @param mixed $default Default value to return if the attribute does not exist. + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that removes + * the named header. * - * @return mixed + * @param string $name Case-insensitive header field name to remove. + * @return ServerRequestDecorator */ - public function getCookieParam($key, $default = null) + public function withoutHeader($name) { - $cookies = $this->getCookieParams(); - $result = $default; - if (isset($cookies[$key])) { - $result = $cookies[$key]; - } + $serverRequest = $this->serverRequest->withoutHeader($name); + return new ServerRequestDecorator($serverRequest); + } - return $result; + /** + * Return an instance with the provided HTTP method. + * + * While HTTP method names are typically all uppercase characters, HTTP + * method names are case-sensitive and thus implementations SHOULD NOT + * modify the given string. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * changed request method. + * + * @param string $method Case-sensitive method. + * @return ServerRequestDecorator + * @throws InvalidArgumentException for invalid HTTP methods. + */ + public function withMethod($method) + { + $serverRequest = $this->serverRequest->withMethod($method); + return new ServerRequestDecorator($serverRequest); } /** - * Return an instance with the specified cookies. + * Return an instance with the specified body parameters. * - * The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST - * be compatible with the structure of $_COOKIE. Typically, this data will - * be injected at instantiation. + * These MAY be injected during instantiation. * - * This method MUST NOT update the related Cookie header of the request - * instance, nor related values in the server params. + * If the request Content-Type is either application/x-www-form-urlencoded + * or multipart/form-data, and the request method is POST, use this method + * ONLY to inject the contents of $_POST. + * + * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of + * deserializing the request body content. Deserialization/parsing returns + * structured data, and, as such, this method ONLY accepts arrays or objects, + * or a null value if nothing was available to parse. + * + * As an example, if content negotiation determines that the request data + * is a JSON payload, this method could be used to create a request + * instance with the deserialized parameters. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the - * updated cookie values. + * updated body parameters. * - * @param array $cookies Array of key/value pairs representing cookies. - * @return static + * @param null|array|object $data The deserialized body data. This will + * typically be in an array or object. + * @return ServerRequestDecorator + * @throws InvalidArgumentException if an unsupported argument type is + * provided. */ - public function withCookieParams(array $cookies) + public function withParsedBody($data) { - $clone = clone $this; - $clone->cookies = $cookies; - - return $clone; + $serverRequest = $this->serverRequest->withParsedBody($data); + return new ServerRequestDecorator($serverRequest); } - /******************************************************************************* - * Query Params - ******************************************************************************/ - /** - * Retrieve query string arguments. + * Return an instance with the specified HTTP protocol version. * - * Retrieves the deserialized query string arguments, if any. + * The version string MUST contain only the HTTP version number (e.g., + * "1.1", "1.0"). * - * Note: the query params might not be in sync with the URI or server - * params. If you need to ensure you are only getting the original - * values, you may need to parse the query string from `getUri()->getQuery()` - * or from the `QUERY_STRING` server param. + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * new protocol version. * - * @return array + * @param string $version HTTP protocol version + * @return ServerRequestDecorator */ - public function getQueryParams() + public function withProtocolVersion($version) { - if (is_array($this->queryParams)) { - return $this->queryParams; - } - - if ($this->uri === null) { - return []; - } - - parse_str($this->uri->getQuery(), $this->queryParams); // <-- URL decodes data - - return $this->queryParams; + $serverRequest = $this->serverRequest->withProtocolVersion($version); + return new ServerRequestDecorator($serverRequest); } /** @@ -736,35 +671,35 @@ public function getQueryParams() * * @param array $query Array of query string arguments, typically from * $_GET. - * @return static + * @return ServerRequestDecorator */ public function withQueryParams(array $query) { - $clone = clone $this; - $clone->queryParams = $query; - - return $clone; + $serverRequest = $this->serverRequest->withQueryParams($query); + return new ServerRequestDecorator($serverRequest); } - /******************************************************************************* - * File Params - ******************************************************************************/ - /** - * Retrieve normalized file upload data. + * Return an instance with the specific request-target. * - * This method returns upload metadata in a normalized tree, with each leaf - * an instance of Psr\Http\Message\UploadedFileInterface. + * If the request needs a non-origin-form request-target — e.g., for + * specifying an absolute-form, authority-form, or asterisk-form — + * this method may be used to create an instance with the specified + * request-target, verbatim. * - * These values MAY be prepared from $_FILES or the message body during - * instantiation, or MAY be injected via withUploadedFiles(). + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * changed request target. * - * @return array An array tree of UploadedFileInterface instances; an empty - * array MUST be returned if no data is present. + * @link http://tools.ietf.org/html/rfc7230#section-5.3 (for the various + * request-target forms allowed in request messages) + * @param mixed $requestTarget + * @return ServerRequestDecorator */ - public function getUploadedFiles() + public function withRequestTarget($requestTarget) { - return $this->uploadedFiles; + $serverRequest = $this->serverRequest->withRequestTarget($requestTarget); + return new ServerRequestDecorator($serverRequest); } /** @@ -775,265 +710,270 @@ public function getUploadedFiles() * updated body parameters. * * @param array $uploadedFiles An array tree of UploadedFileInterface instances. - * @return static - * @throws \InvalidArgumentException if an invalid structure is provided. + * @return ServerRequestDecorator + * @throws InvalidArgumentException if an invalid structure is provided. */ public function withUploadedFiles(array $uploadedFiles) { - $clone = clone $this; - $clone->uploadedFiles = $uploadedFiles; - - return $clone; + $serverRequest = $this->serverRequest->withUploadedFiles($uploadedFiles); + return new ServerRequestDecorator($serverRequest); } - /******************************************************************************* - * Server Params - ******************************************************************************/ - /** - * Retrieve server parameters. + * Returns an instance with the provided URI. * - * Retrieves data related to the incoming request environment, - * typically derived from PHP's $_SERVER superglobal. The data IS NOT - * REQUIRED to originate from $_SERVER. + * This method MUST update the Host header of the returned request by + * default if the URI contains a host component. If the URI does not + * contain a host component, any pre-existing Host header MUST be carried + * over to the returned request. * - * @return array + * You can opt-in to preserving the original state of the Host header by + * setting `$preserveHost` to `true`. When `$preserveHost` is set to + * `true`, this method interacts with the Host header in the following ways: + * + * - If the Host header is missing or empty, and the new URI contains + * a host component, this method MUST update the Host header in the returned + * request. + * - If the Host header is missing or empty, and the new URI does not contain a + * host component, this method MUST NOT update the Host header in the returned + * request. + * - If a Host header is present and non-empty, this method MUST NOT update + * the Host header in the returned request. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * new UriInterface instance. + * + * @link http://tools.ietf.org/html/rfc3986#section-4.3 + * @param UriInterface $uri New request URI to use. + * @param bool $preserveHost Preserve the original state of the Host header. + * @return ServerRequestDecorator */ - public function getServerParams() + public function withUri(UriInterface $uri, $preserveHost = false) { - return $this->serverParams; + $serverRequest = $this->serverRequest->withUri($uri, $preserveHost); + return new ServerRequestDecorator($serverRequest); } /** - * Retrieve a server parameter. + * Get serverRequest content character set, if known. * * Note: This method is not part of the PSR-7 standard. * - * @param string $key - * @param mixed $default - * @return mixed + * @return string|null */ - public function getServerParam($key, $default = null) + public function getContentCharset() { - $serverParams = $this->getServerParams(); + $mediaTypeParams = $this->getMediaTypeParams(); - return isset($serverParams[$key]) ? $serverParams[$key] : $default; + if (isset($mediaTypeParams['charset'])) { + return $mediaTypeParams['charset']; + } + + return null; } - /******************************************************************************* - * Attributes - ******************************************************************************/ - /** - * Retrieve attributes derived from the request. + * Get serverRequest content type. * - * The request "attributes" may be used to allow injection of any - * parameters derived from the request: e.g., the results of path - * match operations; the results of decrypting cookies; the results of - * deserializing non-form-encoded message bodies; etc. Attributes - * will be application and request specific, and CAN be mutable. + * Note: This method is not part of the PSR-7 standard. * - * @return array Attributes derived from the request. + * @return string|null The serverRequest content type, if known */ - public function getAttributes() + public function getContentType() { - return $this->attributes->all(); + $result = $this->serverRequest->getHeader('Content-Type'); + return $result ? $result[0] : null; } /** - * Retrieve a single derived request attribute. - * - * Retrieves a single derived request attribute as described in - * getAttributes(). If the attribute has not been previously set, returns - * the default value as provided. + * Get serverRequest content length, if known. * - * This method obviates the need for a hasAttribute() method, as it allows - * specifying a default value to return if the attribute is not found. + * Note: This method is not part of the PSR-7 standard. * - * @see getAttributes() - * @param string $name The attribute name. - * @param mixed $default Default value to return if the attribute does not exist. - * @return mixed + * @return int|null */ - public function getAttribute($name, $default = null) + public function getContentLength() { - return $this->attributes->get($name, $default); + $result = $this->serverRequest->getHeader('Content-Length'); + return $result ? (int) $result[0] : null; } /** - * Return an instance with the specified derived request attribute. + * Fetch cookie value from cookies sent by the client to the server. * - * This method allows setting a single derived request attribute as - * described in getAttributes(). + * Note: This method is not part of the PSR-7 standard. * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that has the - * updated attribute. + * @param string $key The attribute name. + * @param mixed $default Default value to return if the attribute does not exist. * - * @see getAttributes() - * @param string $name The attribute name. - * @param mixed $value The value of the attribute. - * @return static + * @return mixed */ - public function withAttribute($name, $value) + public function getCookieParam($key, $default = null) { - $clone = clone $this; - $clone->attributes->set($name, $value); + $cookies = $this->serverRequest->getCookieParams(); + $result = $default; + + if (isset($cookies[$key])) { + $result = $cookies[$key]; + } - return $clone; + return $result; } /** - * Create a new instance with the specified derived request attributes. + * Get serverRequest media type, if known. * * Note: This method is not part of the PSR-7 standard. * - * This method allows setting all new derived request attributes as - * described in getAttributes(). - * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return a new instance that has the - * updated attributes. - * - * @param array $attributes New attributes - * @return static + * @return string|null The serverRequest media type, minus content-type params */ - public function withAttributes(array $attributes) + public function getMediaType() { - $clone = clone $this; - $clone->attributes = new Collection($attributes); + $contentType = $this->getContentType(); - return $clone; + if ($contentType) { + $contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType); + if ($contentTypeParts === false) { + return null; + } + return strtolower($contentTypeParts[0]); + } + + return null; } /** - * Return an instance that removes the specified derived request attribute. - * - * This method allows removing a single derived request attribute as - * described in getAttributes(). + * Get serverRequest media type params, if known. * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that removes - * the attribute. + * Note: This method is not part of the PSR-7 standard. * - * @see getAttributes() - * @param string $name The attribute name. - * @return static + * @return array */ - public function withoutAttribute($name) + public function getMediaTypeParams() { - $clone = clone $this; - $clone->attributes->remove($name); + $contentType = $this->getContentType(); + $contentTypeParams = []; - return $clone; - } + if ($contentType) { + $contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType); + if ($contentTypeParts !== false) { + $contentTypePartsLength = count($contentTypeParts); + for ($i = 1; $i < $contentTypePartsLength; $i++) { + $paramParts = explode('=', $contentTypeParts[$i]); + $contentTypeParams[strtolower($paramParts[0])] = $paramParts[1]; + } + } + } - /******************************************************************************* - * Body - ******************************************************************************/ + return $contentTypeParams; + } /** - * Retrieve any parameters provided in the request body. + * Fetch serverRequest parameter value from body or query string (in that order). * - * If the request Content-Type is either application/x-www-form-urlencoded - * or multipart/form-data, and the request method is POST, this method MUST - * return the contents of $_POST. + * Note: This method is not part of the PSR-7 standard. * - * Otherwise, this method may return any results of deserializing - * the request body content; as parsing returns structured content, the - * potential types MUST be arrays or objects only. A null value indicates - * the absence of body content. + * @param string $key The parameter key. + * @param string $default The default value. * - * @return null|array|object The deserialized body parameters, if any. - * These will typically be an array or object. - * @throws RuntimeException if the request body media type parser returns an invalid value + * @return mixed The parameter value. */ - public function getParsedBody() + public function getParam($key, $default = null) { - if ($this->bodyParsed !== false) { - return $this->bodyParsed; - } + $postParams = $this->getParsedBody(); + $getParams = $this->getQueryParams(); + $result = $default; - if (!$this->body) { - return null; + if (is_array($postParams) && isset($postParams[$key])) { + $result = $postParams[$key]; + } elseif (is_object($postParams) && property_exists($postParams, $key)) { + $result = $postParams->$key; + } elseif (isset($getParams[$key])) { + $result = $getParams[$key]; } - $mediaType = $this->getMediaType(); - - // look for a media type with a structured syntax suffix (RFC 6839) - $parts = explode('+', $mediaType); - if (count($parts) >= 2) { - $mediaType = 'application/' . $parts[count($parts)-1]; - } + return $result; + } - if (isset($this->bodyParsers[$mediaType]) === true) { - $body = (string)$this->getBody(); - $parsed = $this->bodyParsers[$mediaType]($body); + /** + * Fetch associative array of body and query string parameters. + * + * Note: This method is not part of the PSR-7 standard. + * + * @return array + */ + public function getParams() + { + $params = $this->getQueryParams(); + $postParams = $this->getParsedBody(); - if (!is_null($parsed) && !is_object($parsed) && !is_array($parsed)) { - throw new RuntimeException( - 'Request body media type parser return value must be an array, an object, or null' - ); - } - $this->bodyParsed = $parsed; - return $this->bodyParsed; + if ($postParams) { + $params = array_merge($params, (array)$postParams); } - return null; + return $params; } /** - * Return an instance with the specified body parameters. + * Fetch parameter value from serverRequest body. * - * These MAY be injected during instantiation. + * Note: This method is not part of the PSR-7 standard. * - * If the request Content-Type is either application/x-www-form-urlencoded - * or multipart/form-data, and the request method is POST, use this method - * ONLY to inject the contents of $_POST. + * @param string $key + * @param mixed $default * - * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of - * deserializing the request body content. Deserialization/parsing returns - * structured data, and, as such, this method ONLY accepts arrays or objects, - * or a null value if nothing was available to parse. + * @return mixed + */ + public function getParsedBodyParam($key, $default = null) + { + $postParams = $this->getParsedBody(); + $result = $default; + + if (is_array($postParams) && isset($postParams[$key])) { + $result = $postParams[$key]; + } elseif (is_object($postParams) && property_exists($postParams, $key)) { + $result = $postParams->$key; + } + + return $result; + } + + /** + * Fetch parameter value from query string. * - * As an example, if content negotiation determines that the request data - * is a JSON payload, this method could be used to create a request - * instance with the deserialized parameters. + * Note: This method is not part of the PSR-7 standard. * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that has the - * updated body parameters. + * @param string $key + * @param mixed $default * - * @param null|array|object $data The deserialized body data. This will - * typically be in an array or object. - * @return static - * @throws \InvalidArgumentException if an unsupported argument type is - * provided. + * @return mixed */ - public function withParsedBody($data) + public function getQueryParam($key, $default = null) { - if (!is_null($data) && !is_object($data) && !is_array($data)) { - throw new InvalidArgumentException('Parsed body value must be an array, an object, or null'); - } + $getParams = $this->getQueryParams(); + $result = $default; - $clone = clone $this; - $clone->bodyParsed = $data; + if (isset($getParams[$key])) { + $result = $getParams[$key]; + } - return $clone; + return $result; } /** - * Force Body to be parsed again. + * Retrieve a server parameter. * * Note: This method is not part of the PSR-7 standard. * - * @return $this + * @param string $key + * @param mixed $default + * @return mixed */ - public function reparseBody() + public function getServerParam($key, $default = null) { - $this->bodyParsed = false; - - return $this; + $serverParams = $this->serverRequest->getServerParams(); + return isset($serverParams[$key]) ? $serverParams[$key] : $default; } /** @@ -1041,108 +981,127 @@ public function reparseBody() * * Note: This method is not part of the PSR-7 standard. * - * @param string $mediaType A HTTP media type (excluding content-type - * params). - * @param callable $callable A callable that returns parsed contents for - * media type. + * @param string $mediaType A HTTP media type (excluding content-type params). + * @param callable $callable A callable that returns parsed contents for media type. + * @return self */ public function registerMediaTypeParser($mediaType, callable $callable) { if ($callable instanceof Closure) { $callable = $callable->bindTo($this); } + $this->bodyParsers[$mediaType] = $callable; - } - /******************************************************************************* - * Parameters (e.g., POST and GET data) - ******************************************************************************/ + return $this; + } /** - * Fetch request parameter value from body or query string (in that order). + * Is this a DELETE serverRequest? * * Note: This method is not part of the PSR-7 standard. * - * @param string $key The parameter key. - * @param string $default The default value. - * - * @return mixed The parameter value. + * @return bool */ - public function getParam($key, $default = null) + public function isDelete() { - $postParams = $this->getParsedBody(); - $getParams = $this->getQueryParams(); - $result = $default; - if (is_array($postParams) && isset($postParams[$key])) { - $result = $postParams[$key]; - } elseif (is_object($postParams) && property_exists($postParams, $key)) { - $result = $postParams->$key; - } elseif (isset($getParams[$key])) { - $result = $getParams[$key]; - } - - return $result; + return $this->isMethod('DELETE'); } /** - * Fetch parameter value from request body. + * Is this a GET serverRequest? * * Note: This method is not part of the PSR-7 standard. * - * @param string $key - * @param mixed $default + * @return bool + */ + public function isGet() + { + return $this->isMethod('GET'); + } + + /** + * Is this a HEAD serverRequest? * - * @return mixed + * Note: This method is not part of the PSR-7 standard. + * + * @return bool */ - public function getParsedBodyParam($key, $default = null) + public function isHead() { - $postParams = $this->getParsedBody(); - $result = $default; - if (is_array($postParams) && isset($postParams[$key])) { - $result = $postParams[$key]; - } elseif (is_object($postParams) && property_exists($postParams, $key)) { - $result = $postParams->$key; - } + return $this->isMethod('HEAD'); + } - return $result; + /** + * Does this serverRequest use a given method? + * + * Note: This method is not part of the PSR-7 standard. + * + * @param string $method HTTP method + * @return bool + */ + public function isMethod($method) + { + return $this->serverRequest->getMethod() === $method; } /** - * Fetch parameter value from query string. + * Is this a OPTIONS serverRequest? * * Note: This method is not part of the PSR-7 standard. * - * @param string $key - * @param mixed $default + * @return bool + */ + public function isOptions() + { + return $this->isMethod('OPTIONS'); + } + + /** + * Is this a PATCH serverRequest? * - * @return mixed + * Note: This method is not part of the PSR-7 standard. + * + * @return bool */ - public function getQueryParam($key, $default = null) + public function isPatch() { - $getParams = $this->getQueryParams(); - $result = $default; - if (isset($getParams[$key])) { - $result = $getParams[$key]; - } + return $this->isMethod('PATCH'); + } - return $result; + /** + * Is this a POST serverRequest? + * + * Note: This method is not part of the PSR-7 standard. + * + * @return bool + */ + public function isPost() + { + return $this->isMethod('POST'); } /** - * Fetch associative array of body and query string parameters. + * Is this a PUT serverRequest? * * Note: This method is not part of the PSR-7 standard. * - * @return array + * @return bool */ - public function getParams() + public function isPut() { - $params = $this->getQueryParams(); - $postParams = $this->getParsedBody(); - if ($postParams) { - $params = array_merge($params, (array)$postParams); - } + return $this->isMethod('PUT'); + } - return $params; + /** + * Is this an XHR serverRequest? + * + * Note: This method is not part of the PSR-7 standard. + * + * @return bool + */ + public function isXhr() + { + return $this->serverRequest->getHeaderLine('X-Requested-With') === 'XMLHttpRequest'; } } diff --git a/src/Decorators/UriDecorator.php b/src/Decorators/UriDecorator.php new file mode 100644 index 0000000..8c080ce --- /dev/null +++ b/src/Decorators/UriDecorator.php @@ -0,0 +1,411 @@ +uri = $uri; + } + + /** + * Disable magic setter to ensure immutability + * @param mixed $name + * @param mixed $value + */ + public function __set($name, $value) + { + } + + /** + * Retrieve the authority component of the URI. + * + * If no authority information is present, this method MUST return an empty + * string. + * + * The authority syntax of the URI is: + * + *
+     * [user-info@]host[:port]
+     * 
+ * + * If the port component is not set or is the standard port for the current + * scheme, it SHOULD NOT be included. + * + * @see https://tools.ietf.org/html/rfc3986#section-3.2 + * @return string The URI authority, in "[user-info@]host[:port]" format. + */ + public function getAuthority() + { + return $this->uri->getAuthority(); + } + + /** + * Retrieve the fragment component of the URI. + * + * If no fragment is present, this method MUST return an empty string. + * + * The leading "#" character is not part of the fragment and MUST NOT be + * added. + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.5. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.5 + * @return string The URI fragment. + */ + public function getFragment() + { + return $this->uri->getFragment(); + } + + /** + * Retrieve the host component of the URI. + * + * If no host is present, this method MUST return an empty string. + * + * The value returned MUST be normalized to lowercase, per RFC 3986 + * Section 3.2.2. + * + * @see http://tools.ietf.org/html/rfc3986#section-3.2.2 + * @return string The URI host. + */ + public function getHost() + { + return $this->uri->getHost(); + } + + /** + * Retrieve the path component of the URI. + * + * The path can either be empty or absolute (starting with a slash) or + * rootless (not starting with a slash). Implementations MUST support all + * three syntaxes. + * + * Normally, the empty path "" and absolute path "/" are considered equal as + * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically + * do this normalization because in contexts with a trimmed base path, e.g. + * the front controller, this difference becomes significant. It's the task + * of the user to handle both "" and "/". + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.3. + * + * As an example, if the value should include a slash ("/") not intended as + * delimiter between path segments, that value MUST be passed in encoded + * form (e.g., "%2F") to the instance. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.3 + * @return string The URI path. + */ + public function getPath() + { + return $this->uri->getPath(); + } + + /** + * Retrieve the port component of the URI. + * + * If a port is present, and it is non-standard for the current scheme, + * this method MUST return it as an integer. If the port is the standard port + * used with the current scheme, this method SHOULD return null. + * + * If no port is present, and no scheme is present, this method MUST return + * a null value. + * + * If no port is present, but a scheme is present, this method MAY return + * the standard port for that scheme, but SHOULD return null. + * + * @return null|int The URI port. + */ + public function getPort() + { + return $this->uri->getPort(); + } + + /** + * Retrieve the query string of the URI. + * + * If no query string is present, this method MUST return an empty string. + * + * The leading "?" character is not part of the query and MUST NOT be + * added. + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.4. + * + * As an example, if a value in a key/value pair of the query string should + * include an ampersand ("&") not intended as a delimiter between values, + * that value MUST be passed in encoded form (e.g., "%26") to the instance. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.4 + * @return string The URI query string. + */ + public function getQuery() + { + return $this->uri->getQuery(); + } + + /** + * Retrieve the scheme component of the URI. + * + * If no scheme is present, this method MUST return an empty string. + * + * The value returned MUST be normalized to lowercase, per RFC 3986 + * Section 3.1. + * + * The trailing ":" character is not part of the scheme and MUST NOT be + * added. + * + * @see https://tools.ietf.org/html/rfc3986#section-3.1 + * @return string The URI scheme. + */ + public function getScheme() + { + return $this->uri->getScheme(); + } + + /** + * Retrieve the user information component of the URI. + * + * If no user information is present, this method MUST return an empty + * string. + * + * If a user is present in the URI, this will return that value; + * additionally, if the password is also present, it will be appended to the + * user value, with a colon (":") separating the values. + * + * The trailing "@" character is not part of the user information and MUST + * NOT be added. + * + * @return string The URI user information, in "username[:password]" format. + */ + public function getUserInfo() + { + return $this->uri->getUserInfo(); + } + + /** + * Return an instance with the specified URI fragment. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified URI fragment. + * + * Users can provide both encoded and decoded fragment characters. + * Implementations ensure the correct encoding as outlined in getFragment(). + * + * An empty fragment value is equivalent to removing the fragment. + * + * @param string $fragment The fragment to use with the new instance. + * @return UriDecorator A new instance with the specified fragment. + */ + public function withFragment($fragment) + { + $uri = $this->uri->withFragment($fragment); + return new UriDecorator($uri); + } + + /** + * Return an instance with the specified host. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified host. + * + * An empty host value is equivalent to removing the host. + * + * @param string $host The hostname to use with the new instance. + * @return UriDecorator A new instance with the specified host. + * @throws InvalidArgumentException for invalid hostnames. + */ + public function withHost($host) + { + $uri = $this->uri->withHost($host); + return new UriDecorator($uri); + } + + /** + * Return an instance with the specified path. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified path. + * + * The path can either be empty or absolute (starting with a slash) or + * rootless (not starting with a slash). Implementations MUST support all + * three syntaxes. + * + * If the path is intended to be domain-relative rather than path relative then + * it must begin with a slash ("/"). Paths not starting with a slash ("/") + * are assumed to be relative to some base path known to the application or + * consumer. + * + * Users can provide both encoded and decoded path characters. + * Implementations ensure the correct encoding as outlined in getPath(). + * + * @param string $path The path to use with the new instance. + * @return UriDecorator A new instance with the specified path. + * @throws InvalidArgumentException for invalid paths. + */ + public function withPath($path) + { + $uri = $this->uri->withPath($path); + return new UriDecorator($uri); + } + + /** + * Return an instance with the specified port. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified port. + * + * Implementations MUST raise an exception for ports outside the + * established TCP and UDP port ranges. + * + * A null value provided for the port is equivalent to removing the port + * information. + * + * @param null|int $port The port to use with the new instance; a null value + * removes the port information. + * @return UriDecorator A new instance with the specified port. + * @throws InvalidArgumentException for invalid ports. + */ + public function withPort($port) + { + $uri = $this->uri->withPort($port); + return new UriDecorator($uri); + } + + /** + * Return an instance with the specified query string. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified query string. + * + * Users can provide both encoded and decoded query characters. + * Implementations ensure the correct encoding as outlined in getQuery(). + * + * An empty query string value is equivalent to removing the query string. + * + * @param string $query The query string to use with the new instance. + * @return UriDecorator A new instance with the specified query string. + * @throws InvalidArgumentException for invalid query strings. + */ + public function withQuery($query) + { + $uri = $this->uri->withQuery($query); + return new UriDecorator($uri); + } + + /** + * Return an instance with the specified scheme. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified scheme. + * + * Implementations MUST support the schemes "http" and "https" case + * insensitively, and MAY accommodate other schemes if required. + * + * An empty scheme is equivalent to removing the scheme. + * + * @param string $scheme The scheme to use with the new instance. + * @return UriDecorator A new instance with the specified scheme. + * @throws InvalidArgumentException for invalid or unsupported schemes. + */ + public function withScheme($scheme) + { + $uri = $this->uri->withScheme($scheme); + return new UriDecorator($uri); + } + + /** + * Return an instance with the specified user information. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified user information. + * + * Password is optional, but the user information MUST include the + * user; an empty string for the user is equivalent to removing user + * information. + * + * @param string $user The user name to use for authority. + * @param null|string $password The password associated with $user. + * @return UriDecorator A new instance with the specified user information. + */ + public function withUserInfo($user, $password = null) + { + $uri = $this->uri->withUserInfo($user, $password); + return new UriDecorator($uri); + } + + /** + * Return the string representation as a URI reference. + * + * Depending on which components of the URI are present, the resulting + * string is either a full URI or relative reference according to RFC 3986, + * Section 4.1. The method concatenates the various components of the URI, + * using the appropriate delimiters: + * + * - If a scheme is present, it MUST be suffixed by ":". + * - If an authority is present, it MUST be prefixed by "//". + * - The path can be concatenated without delimiters. But there are two + * cases where the path has to be adjusted to make the URI reference + * valid as PHP does not allow to throw an exception in __toString(): + * - If the path is rootless and an authority is present, the path MUST + * be prefixed by "/". + * - If the path is starting with more than one "/" and no authority is + * present, the starting slashes MUST be reduced to one. + * - If a query is present, it MUST be prefixed by "?". + * - If a fragment is present, it MUST be prefixed by "#". + * + * @see http://tools.ietf.org/html/rfc3986#section-4.1 + * @return string + */ + public function __toString() + { + return $this->uri->__toString(); + } + + /** + * Return the fully qualified base URL. + * + * Note that this method never includes a trailing / + * + * This method is not part of PSR-7. + * + * @return string + */ + public function getBaseUrl() + { + $scheme = $this->uri->getScheme(); + $authority = $this->uri->getAuthority(); + return ($scheme !== '' ? $scheme . ':' : '') . ($authority !== '' ? '//' . $authority : ''); + } +} diff --git a/src/Environment.php b/src/Environment.php deleted file mode 100644 index ff2d2ab..0000000 --- a/src/Environment.php +++ /dev/null @@ -1,57 +0,0 @@ - 'HTTP/1.1', - 'REQUEST_METHOD' => 'GET', - 'REQUEST_SCHEME' => $defscheme, - 'SCRIPT_NAME' => '', - 'REQUEST_URI' => '', - 'QUERY_STRING' => '', - 'SERVER_NAME' => 'localhost', - 'SERVER_PORT' => $defport, - 'HTTP_HOST' => 'localhost', - 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', - 'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.8', - 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.3', - 'HTTP_USER_AGENT' => 'Slim Framework', - 'REMOTE_ADDR' => '127.0.0.1', - 'REQUEST_TIME' => time(), - 'REQUEST_TIME_FLOAT' => microtime(true), - ], $userData); - } -} diff --git a/src/Factory/DecoratedResponseFactory.php b/src/Factory/DecoratedResponseFactory.php new file mode 100644 index 0000000..e199abb --- /dev/null +++ b/src/Factory/DecoratedResponseFactory.php @@ -0,0 +1,53 @@ +responseFactory = $responseFactory; + $this->streamFactory = $streamFactory; + } + + /** + * @param int $code + * @param string $reasonPhrase + * @return ResponseDecorator + */ + public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface + { + $response = $this->responseFactory->createResponse($code, $reasonPhrase); + return new ResponseDecorator($response, $this->streamFactory); + } +} diff --git a/src/Factory/DecoratedServerRequestFactory.php b/src/Factory/DecoratedServerRequestFactory.php new file mode 100644 index 0000000..c5a7b67 --- /dev/null +++ b/src/Factory/DecoratedServerRequestFactory.php @@ -0,0 +1,46 @@ +serverRequestFactory = $serverRequestFactory; + } + + /** + * @param string $method + * @param \Psr\Http\Message\UriInterface|string $uri + * @param array $serverParams + * @return ServerRequestDecorator + */ + public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface + { + $serverRequest = $this->serverRequestFactory->createServerRequest($method, $uri, $serverParams); + return new ServerRequestDecorator($serverRequest); + } +} diff --git a/src/Factory/DecoratedUriFactory.php b/src/Factory/DecoratedUriFactory.php new file mode 100644 index 0000000..f6bc7cc --- /dev/null +++ b/src/Factory/DecoratedUriFactory.php @@ -0,0 +1,44 @@ +uriFactory = $uriFactory; + } + + /** + * @param string $uri + * @return UriDecorator + */ + public function createUri(string $uri = ''): UriInterface + { + $uri = $this->uriFactory->createUri($uri); + return new UriDecorator($uri); + } +} diff --git a/src/Headers.php b/src/Headers.php deleted file mode 100644 index 9d8c299..0000000 --- a/src/Headers.php +++ /dev/null @@ -1,240 +0,0 @@ - 1, - 'CONTENT_LENGTH' => 1, - 'PHP_AUTH_USER' => 1, - 'PHP_AUTH_PW' => 1, - 'PHP_AUTH_DIGEST' => 1, - 'AUTH_TYPE' => 1, - ]; - - /** - * Create new headers collection with data extracted from - * the PHP global environment - * - * @param array $globals Global server variables - * - * @return self - */ - public static function createFromGlobals(array $globals) - { - $data = []; - $globals = self::determineAuthorization($globals); - foreach ($globals as $key => $value) { - $key = strtoupper($key); - if (isset(static::$special[$key]) || strpos($key, 'HTTP_') === 0) { - if ($key !== 'HTTP_CONTENT_LENGTH') { - $data[self::reconstructOriginalKey($key)] = $value; - } - } - } - - return new static($data); - } - - /** - * If HTTP_AUTHORIZATION does not exist tries to get it from - * getallheaders() when available. - * - * @param array $globals The Slim application Environment - * - * @return array - */ - - public static function determineAuthorization(array $globals) - { - $authorization = isset($globals['HTTP_AUTHORIZATION']) ? $globals['HTTP_AUTHORIZATION'] : null; - - if (empty($authorization) && is_callable('getallheaders')) { - $headers = getallheaders(); - $headers = array_change_key_case($headers, CASE_LOWER); - if (isset($headers['authorization'])) { - $globals['HTTP_AUTHORIZATION'] = $headers['authorization']; - } - } - - return $globals; - } - - /** - * Return array of HTTP header names and values. - * This method returns the _original_ header name - * as specified by the end user. - * - * @return array - */ - public function all() - { - $all = parent::all(); - $out = []; - foreach ($all as $key => $props) { - $out[$props['originalKey']] = $props['value']; - } - - return $out; - } - - /** - * Set HTTP header value - * - * This method sets a header value. It replaces - * any values that may already exist for the header name. - * - * @param string $key The case-insensitive header name - * @param string $value The header value - */ - public function set($key, $value) - { - if (!is_array($value)) { - $value = [$value]; - } - parent::set($this->normalizeKey($key), [ - 'value' => $value, - 'originalKey' => $key - ]); - } - - /** - * Get HTTP header value - * - * @param string $key The case-insensitive header name - * @param mixed $default The default value if key does not exist - * - * @return string[] - */ - public function get($key, $default = null) - { - if ($this->has($key)) { - return parent::get($this->normalizeKey($key))['value']; - } - - return $default; - } - - /** - * Get HTTP header key as originally specified - * - * @param string $key The case-insensitive header name - * @param mixed $default The default value if key does not exist - * - * @return string - */ - public function getOriginalKey($key, $default = null) - { - if ($this->has($key)) { - return parent::get($this->normalizeKey($key))['originalKey']; - } - - return $default; - } - - /** - * Add HTTP header value - * - * This method appends a header value. Unlike the set() method, - * this method _appends_ this new value to any values - * that already exist for this header name. - * - * @param string $key The case-insensitive header name - * @param array|string $value The new header value(s) - */ - public function add($key, $value) - { - $oldValues = $this->get($key, []); - $newValues = is_array($value) ? $value : [$value]; - $this->set($key, array_merge($oldValues, array_values($newValues))); - } - - /** - * Does this collection have a given header? - * - * @param string $key The case-insensitive header name - * - * @return bool - */ - public function has($key) - { - return parent::has($this->normalizeKey($key)); - } - - /** - * Remove header from collection - * - * @param string $key The case-insensitive header name - */ - public function remove($key) - { - parent::remove($this->normalizeKey($key)); - } - - /** - * Normalize header name - * - * This method transforms header names into a - * normalized form. This is how we enable case-insensitive - * header names in the other methods in this class. - * - * @param string $key The case-insensitive header name - * - * @return string Normalized header name - */ - public function normalizeKey($key) - { - $key = strtr(strtolower($key), '_', '-'); - if (strpos($key, 'http-') === 0) { - $key = substr($key, 5); - } - - return $key; - } - - /** - * Reconstruct original header name - * - * This method takes an HTTP header name from the Environment - * and returns it as it was probably formatted by the actual client. - * - * @param string $key An HTTP header key from the $_SERVER global variable - * - * @return string The reconstructed key - * - * @example CONTENT_TYPE => Content-Type - * @example HTTP_USER_AGENT => User-Agent - */ - private static function reconstructOriginalKey($key) - { - if (strpos($key, 'HTTP_') === 0) { - $key = substr($key, 5); - } - return strtr(ucwords(strtr(strtolower($key), '_', ' ')), ' ', '-'); - } -} diff --git a/src/HeadersInterface.php b/src/HeadersInterface.php deleted file mode 100644 index 05fb109..0000000 --- a/src/HeadersInterface.php +++ /dev/null @@ -1,22 +0,0 @@ - true, - '1.1' => true, - '2.0' => true, - '2' => true, - ]; - - /** - * Headers - * - * @var \Slim\Http\HeadersInterface - */ - protected $headers; - - /** - * Body object - * - * @var \Psr\Http\Message\StreamInterface - */ - protected $body; - - - /** - * Disable magic setter to ensure immutability - */ - public function __set($name, $value) - { - // Do nothing - } - - /******************************************************************************* - * Protocol - ******************************************************************************/ - - /** - * Retrieves the HTTP protocol version as a string. - * - * The string MUST contain only the HTTP version number (e.g., "1.1", "1.0"). - * - * @return string HTTP protocol version. - */ - public function getProtocolVersion() - { - return $this->protocolVersion; - } - - /** - * Return an instance with the specified HTTP protocol version. - * - * The version string MUST contain only the HTTP version number (e.g., - * "1.1", "1.0"). - * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that has the - * new protocol version. - * - * @param string $version HTTP protocol version - * @return static - * @throws InvalidArgumentException if the http version is an invalid number - */ - public function withProtocolVersion($version) - { - if (!isset(self::$validProtocolVersions[$version])) { - throw new InvalidArgumentException( - 'Invalid HTTP version. Must be one of: ' - . implode(', ', array_keys(self::$validProtocolVersions)) - ); - } - $clone = clone $this; - $clone->protocolVersion = $version; - - return $clone; - } - - /******************************************************************************* - * Headers - ******************************************************************************/ - - /** - * Retrieves all message header values. - * - * The keys represent the header name as it will be sent over the wire, and - * each value is an array of strings associated with the header. - * - * // Represent the headers as a string - * foreach ($message->getHeaders() as $name => $values) { - * echo $name . ": " . implode(", ", $values); - * } - * - * // Emit headers iteratively: - * foreach ($message->getHeaders() as $name => $values) { - * foreach ($values as $value) { - * header(sprintf('%s: %s', $name, $value), false); - * } - * } - * - * While header names are not case-sensitive, getHeaders() will preserve the - * exact case in which headers were originally specified. - * - * @return array Returns an associative array of the message's headers. Each - * key MUST be a header name, and each value MUST be an array of strings - * for that header. - */ - public function getHeaders() - { - return $this->headers->all(); - } - - /** - * Checks if a header exists by the given case-insensitive name. - * - * @param string $name Case-insensitive header field name. - * @return bool Returns true if any header names match the given header - * name using a case-insensitive string comparison. Returns false if - * no matching header name is found in the message. - */ - public function hasHeader($name) - { - return $this->headers->has($name); - } - - /** - * Retrieves a message header value by the given case-insensitive name. - * - * This method returns an array of all the header values of the given - * case-insensitive header name. - * - * If the header does not appear in the message, this method MUST return an - * empty array. - * - * @param string $name Case-insensitive header field name. - * @return string[] An array of string values as provided for the given - * header. If the header does not appear in the message, this method MUST - * return an empty array. - */ - public function getHeader($name) - { - return $this->headers->get($name, []); - } - - /** - * Retrieves a comma-separated string of the values for a single header. - * - * This method returns all of the header values of the given - * case-insensitive header name as a string concatenated together using - * a comma. - * - * NOTE: Not all header values may be appropriately represented using - * comma concatenation. For such headers, use getHeader() instead - * and supply your own delimiter when concatenating. - * - * If the header does not appear in the message, this method MUST return - * an empty string. - * - * @param string $name Case-insensitive header field name. - * @return string A string of values as provided for the given header - * concatenated together using a comma. If the header does not appear in - * the message, this method MUST return an empty string. - */ - public function getHeaderLine($name) - { - return implode(',', $this->headers->get($name, [])); - } - - /** - * Return an instance with the provided value replacing the specified header. - * - * While header names are case-insensitive, the casing of the header will - * be preserved by this function, and returned from getHeaders(). - * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that has the - * new and/or updated header and value. - * - * @param string $name Case-insensitive header field name. - * @param string|string[] $value Header value(s). - * @return static - * @throws \InvalidArgumentException for invalid header names or values. - */ - public function withHeader($name, $value) - { - $this->validateHeaderName($name); - $this->validateHeaderValue($value); - - $clone = clone $this; - $clone->headers->set($name, $value); - - return $clone; - } - - /** - * Return an instance with the specified header appended with the given value. - * - * Existing values for the specified header will be maintained. The new - * value(s) will be appended to the existing list. If the header did not - * exist previously, it will be added. - * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that has the - * new header and/or value. - * - * @param string $name Case-insensitive header field name to add. - * @param string|string[] $value Header value(s). - * @return static - * @throws \InvalidArgumentException for invalid header names or values. - */ - public function withAddedHeader($name, $value) - { - $this->validateHeaderName($name); - $this->validateHeaderValue($value); - - $clone = clone $this; - $clone->headers->add($name, $value); - - return $clone; - } - - /** - * Return an instance without the specified header. - * - * Header resolution MUST be done without case-sensitivity. - * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that removes - * the named header. - * - * @param string $name Case-insensitive header field name to remove. - * @return static - */ - public function withoutHeader($name) - { - $clone = clone $this; - $clone->headers->remove($name); - - return $clone; - } - - /** - * @param string $name - * @throws \InvalidArgumentException - */ - protected function validateHeaderName($name) - { - if (!is_string($name) || empty($name)) { - throw new InvalidArgumentException('Header names must be a non empty strings'); - } - - if (!preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $name)) { - throw new InvalidArgumentException("'$name' is not valid header name"); - } - } - - /** - * @param string|string[] $value - * @throws \InvalidArgumentException - */ - protected function validateHeaderValue($value) - { - if (!is_array($value)) { - $value = [$value]; - } elseif (empty($value)) { - throw new InvalidArgumentException('Header values must be non empty strings'); - } - - foreach ($value as $v) { - if (!is_string($v) && ! is_numeric($v)) { - throw new InvalidArgumentException('Header values must be strings or numeric'); - } - - $v = (string) $v; - if (preg_match("#(?:(?:(?body; - } - - /** - * Return an instance with the specified message body. - * - * The body MUST be a StreamInterface object. - * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return a new instance that has the - * new body stream. - * - * @param StreamInterface $body Body. - * @return static - * @throws \InvalidArgumentException When the body is not valid. - */ - public function withBody(StreamInterface $body) - { - // TODO: Test for invalid body? - $clone = clone $this; - $clone->body = $body; - - return $clone; - } -} diff --git a/src/RequestBody.php b/src/RequestBody.php deleted file mode 100644 index 744bbc7..0000000 --- a/src/RequestBody.php +++ /dev/null @@ -1,27 +0,0 @@ - 'Continue', - 101 => 'Switching Protocols', - 102 => 'Processing', - //Successful 2xx - 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', - 226 => 'IM Used', - //Redirection 3xx - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 306 => '(Unused)', - 307 => 'Temporary Redirect', - 308 => 'Permanent Redirect', - //Client Error 4xx - 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 Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed', - 418 => 'I\'m a teapot', - 421 => 'Misdirected Request', - 422 => 'Unprocessable Entity', - 423 => 'Locked', - 424 => 'Failed Dependency', - 426 => 'Upgrade Required', - 428 => 'Precondition Required', - 429 => 'Too Many Requests', - 431 => 'Request Header Fields Too Large', - 444 => 'Connection Closed Without Response', - 451 => 'Unavailable For Legal Reasons', - 499 => 'Client Closed Request', - //Server Error 5xx - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported', - 506 => 'Variant Also Negotiates', - 507 => 'Insufficient Storage', - 508 => 'Loop Detected', - 510 => 'Not Extended', - 511 => 'Network Authentication Required', - 599 => 'Network Connect Timeout Error', - ]; - - /** - * EOL characters used for HTTP response. - * - * @var string - */ - const EOL = "\r\n"; - - /** - * Create new HTTP response. - * - * @param int $status The response status code. - * @param HeadersInterface|null $headers The response headers. - * @param StreamInterface|null $body The response body. - */ - public function __construct($status = 200, HeadersInterface $headers = null, StreamInterface $body = null) - { - $this->status = $this->filterStatus($status); - $this->headers = $headers ? $headers : new Headers(); - $this->body = $body ? $body : new Body(fopen('php://temp', 'r+')); - } - - /** - * This method is applied to the cloned object - * after PHP performs an initial shallow-copy. This - * method completes a deep-copy by creating new objects - * for the cloned object's internal reference pointers. - */ - public function __clone() - { - $this->headers = clone $this->headers; - } - - /******************************************************************************* - * Status - ******************************************************************************/ - - /** - * Gets the response status code. - * - * The status code is a 3-digit integer result code of the server's attempt - * to understand and satisfy the request. - * - * @return int Status code. - */ - public function getStatusCode() - { - return $this->status; - } - - /** - * Return an instance with the specified status code and, optionally, reason phrase. - * - * If no reason phrase is specified, implementations MAY choose to default - * to the RFC 7231 or IANA recommended reason phrase for the response's - * status code. - * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that has the - * updated status and reason phrase. - * - * @link http://tools.ietf.org/html/rfc7231#section-6 - * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml - * @param int $code The 3-digit integer result code to set. - * @param string $reasonPhrase The reason phrase to use with the - * provided status code; if none is provided, implementations MAY - * use the defaults as suggested in the HTTP specification. - * @return static - * @throws \InvalidArgumentException For invalid status code arguments. - */ - public function withStatus($code, $reasonPhrase = '') - { - $code = $this->filterStatus($code); - - if (!is_string($reasonPhrase) && !method_exists($reasonPhrase, '__toString')) { - throw new InvalidArgumentException('ReasonPhrase must be a string'); - } - - $clone = clone $this; - $clone->status = $code; - if ($reasonPhrase === '' && isset(static::$messages[$code])) { - $reasonPhrase = static::$messages[$code]; - } - - if ($reasonPhrase === '') { - throw new InvalidArgumentException('ReasonPhrase must be supplied for this code'); - } - - $clone->reasonPhrase = $reasonPhrase; - - return $clone; - } - - /** - * Filter HTTP status code. - * - * @param int $status HTTP status code. - * @return int - * @throws \InvalidArgumentException If an invalid HTTP status code is provided. - */ - protected function filterStatus($status) - { - if (!is_integer($status) || $status<100 || $status>599) { - throw new InvalidArgumentException('Invalid HTTP status code'); - } - - return $status; - } - - /** - * Gets the response reason phrase associated with the status code. - * - * Because a reason phrase is not a required element in a response - * status line, the reason phrase value MAY be null. Implementations MAY - * choose to return the default RFC 7231 recommended reason phrase (or those - * listed in the IANA HTTP Status Code Registry) for the response's - * status code. - * - * @link http://tools.ietf.org/html/rfc7231#section-6 - * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml - * @return string Reason phrase; must return an empty string if none present. - */ - public function getReasonPhrase() - { - if ($this->reasonPhrase) { - return $this->reasonPhrase; - } - if (isset(static::$messages[$this->status])) { - return static::$messages[$this->status]; - } - return ''; - } - - /******************************************************************************* - * Body - ******************************************************************************/ - - /** - * Write data to the response body. - * - * Note: This method is not part of the PSR-7 standard. - * - * Proxies to the underlying stream and writes the provided data to it. - * - * @param string $data - * @return $this - */ - public function write($data) - { - $this->getBody()->write($data); - - return $this; - } - - /******************************************************************************* - * Response Helpers - ******************************************************************************/ - - /** - * Redirect. - * - * Note: This method is not part of the PSR-7 standard. - * - * This method prepares the response object to return an HTTP Redirect - * response to the client. - * - * @param string|UriInterface $url The redirect destination. - * @param int|null $status The redirect HTTP status code. - * @return static - */ - public function withRedirect($url, $status = null) - { - $responseWithRedirect = $this->withHeader('Location', (string)$url); - - if (is_null($status) && $this->getStatusCode() === 200) { - $status = 302; - } - - if (!is_null($status)) { - return $responseWithRedirect->withStatus($status); - } - - return $responseWithRedirect; - } - - /** - * Json. - * - * Note: This method is not part of the PSR-7 standard. - * - * This method prepares the response object to return an HTTP Json - * response to the client. - * - * @param mixed $data The data - * @param int $status The HTTP status code. - * @param int $encodingOptions Json encoding options - * @throws \RuntimeException - * @return static - */ - public function withJson($data, $status = null, $encodingOptions = 0) - { - $response = $this->withBody(new Body(fopen('php://temp', 'r+'))); - $response->body->write($json = json_encode($data, $encodingOptions)); - - // Ensure that the json encoding passed successfully - if ($json === false) { - throw new \RuntimeException(json_last_error_msg(), json_last_error()); - } - - $responseWithJson = $response->withHeader('Content-Type', 'application/json;charset=utf-8'); - if (isset($status)) { - return $responseWithJson->withStatus($status); - } - return $responseWithJson; - } - - /** - * Is this response empty? - * - * Note: This method is not part of the PSR-7 standard. - * - * @return bool - */ - public function isEmpty() - { - return in_array($this->getStatusCode(), [204, 205, 304]); - } - - /** - * Is this response informational? - * - * Note: This method is not part of the PSR-7 standard. - * - * @return bool - */ - public function isInformational() - { - return $this->getStatusCode() >= 100 && $this->getStatusCode() < 200; - } - - /** - * Is this response OK? - * - * Note: This method is not part of the PSR-7 standard. - * - * @return bool - */ - public function isOk() - { - return $this->getStatusCode() === 200; - } - - /** - * Is this response successful? - * - * Note: This method is not part of the PSR-7 standard. - * - * @return bool - */ - public function isSuccessful() - { - return $this->getStatusCode() >= 200 && $this->getStatusCode() < 300; - } - - /** - * Is this response a redirect? - * - * Note: This method is not part of the PSR-7 standard. - * - * @return bool - */ - public function isRedirect() - { - return in_array($this->getStatusCode(), [301, 302, 303, 307, 308]); - } - - /** - * Is this response a redirection? - * - * Note: This method is not part of the PSR-7 standard. - * - * @return bool - */ - public function isRedirection() - { - return $this->getStatusCode() >= 300 && $this->getStatusCode() < 400; - } - - /** - * Is this response forbidden? - * - * Note: This method is not part of the PSR-7 standard. - * - * @return bool - * @api - */ - public function isForbidden() - { - return $this->getStatusCode() === 403; - } - - /** - * Is this response not Found? - * - * Note: This method is not part of the PSR-7 standard. - * - * @return bool - */ - public function isNotFound() - { - return $this->getStatusCode() === 404; - } - - /** - * Is this response a client error? - * - * Note: This method is not part of the PSR-7 standard. - * - * @return bool - */ - public function isClientError() - { - return $this->getStatusCode() >= 400 && $this->getStatusCode() < 500; - } - - /** - * Is this response a server error? - * - * Note: This method is not part of the PSR-7 standard. - * - * @return bool - */ - public function isServerError() - { - return $this->getStatusCode() >= 500 && $this->getStatusCode() < 600; - } - - /** - * Convert response to string. - * - * Note: This method is not part of the PSR-7 standard. - * - * @return string - */ - public function __toString() - { - $output = sprintf( - 'HTTP/%s %s %s', - $this->getProtocolVersion(), - $this->getStatusCode(), - $this->getReasonPhrase() - ); - $output .= Response::EOL; - foreach ($this->getHeaders() as $name => $values) { - $output .= sprintf('%s: %s', $name, $this->getHeaderLine($name)) . Response::EOL; - } - $output .= Response::EOL; - $output .= (string)$this->getBody(); - - return $output; - } -} diff --git a/src/Stream.php b/src/Stream.php deleted file mode 100644 index f49d176..0000000 --- a/src/Stream.php +++ /dev/null @@ -1,450 +0,0 @@ - ['r', 'r+', 'w+', 'a+', 'x+', 'c+'], - 'writable' => ['r+', 'w', 'w+', 'a', 'a+', 'x', 'x+', 'c', 'c+'], - ]; - - /** - * The underlying stream resource - * - * @var resource|null - */ - protected $stream; - - /** - * Stream metadata - * - * @var array|null - */ - protected $meta; - - /** - * Is this stream readable? - * - * @var bool|null - */ - protected $readable; - - /** - * Is this stream writable? - * - * @var bool|null - */ - protected $writable; - - /** - * Is this stream seekable? - * - * @var bool|null - */ - protected $seekable; - - /** - * The size of the stream if known - * - * @var null|int - */ - protected $size; - - /** - * Is this stream a pipe? - * - * @var bool|null - */ - protected $isPipe; - - /** - * Create a new Stream. - * - * @param resource $stream A PHP resource handle. - * - * @throws InvalidArgumentException If argument is not a resource. - */ - public function __construct($stream) - { - $this->attach($stream); - } - - /** - * Get stream metadata as an associative array or retrieve a specific key. - * - * The keys returned are identical to the keys returned from PHP's - * stream_get_meta_data() function. - * - * @link http://php.net/manual/en/function.stream-get-meta-data.php - * - * @param string $key Specific metadata to retrieve. - * - * @return array|mixed|null Returns an associative array if no key is - * provided. Returns a specific key value if a key is provided and the - * value is found, or null if the key is not found. - */ - public function getMetadata($key = null) - { - $this->meta = stream_get_meta_data($this->stream); - if (is_null($key) === true) { - return $this->meta; - } - - return isset($this->meta[$key]) ? $this->meta[$key] : null; - } - - /** - * Is a resource attached to this stream? - * - * Note: This method is not part of the PSR-7 standard. - * - * @return bool - */ - protected function isAttached() - { - return is_resource($this->stream); - } - - /** - * Attach new resource to this object. - * - * Note: This method is not part of the PSR-7 standard. - * - * @param resource $newStream A PHP resource handle. - * - * @throws InvalidArgumentException If argument is not a valid PHP resource. - */ - protected function attach($newStream) - { - if (is_resource($newStream) === false) { - throw new InvalidArgumentException(__METHOD__ . ' argument must be a valid PHP resource'); - } - - if ($this->isAttached() === true) { - $this->detach(); - } - - $this->stream = $newStream; - } - - /** - * Separates any underlying resources from the stream. - * - * After the stream has been detached, the stream is in an unusable state. - * - * @return resource|null Underlying PHP stream, if any - */ - public function detach() - { - $oldResource = $this->stream; - $this->stream = null; - $this->meta = null; - $this->readable = null; - $this->writable = null; - $this->seekable = null; - $this->size = null; - $this->isPipe = null; - - return $oldResource; - } - - /** - * Reads all data from the stream into a string, from the beginning to end. - * - * This method MUST attempt to seek to the beginning of the stream before - * reading data and read the stream until the end is reached. - * - * Warning: This could attempt to load a large amount of data into memory. - * - * This method MUST NOT raise an exception in order to conform with PHP's - * string casting operations. - * - * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring - * @return string - */ - public function __toString() - { - if (!$this->isAttached()) { - return ''; - } - - try { - $this->rewind(); - return $this->getContents(); - } catch (RuntimeException $e) { - return ''; - } - } - - /** - * Closes the stream and any underlying resources. - */ - public function close() - { - if ($this->isAttached() === true) { - if ($this->isPipe()) { - pclose($this->stream); - } else { - fclose($this->stream); - } - } - - $this->detach(); - } - - /** - * Get the size of the stream if known. - * - * @return int|null Returns the size in bytes if known, or null if unknown. - */ - public function getSize() - { - if (!$this->size && $this->isAttached() === true) { - $stats = fstat($this->stream); - $this->size = isset($stats['size']) && !$this->isPipe() ? $stats['size'] : null; - } - - return $this->size; - } - - /** - * Returns the current position of the file read/write pointer - * - * @return int Position of the file pointer - * - * @throws RuntimeException on error. - */ - public function tell() - { - if (!$this->isAttached() || ($position = ftell($this->stream)) === false || $this->isPipe()) { - throw new RuntimeException('Could not get the position of the pointer in stream'); - } - - return $position; - } - - /** - * Returns true if the stream is at the end of the stream. - * - * @return bool - */ - public function eof() - { - return $this->isAttached() ? feof($this->stream) : true; - } - - /** - * Returns whether or not the stream is readable. - * - * @return bool - */ - public function isReadable() - { - if ($this->readable === null) { - if ($this->isPipe()) { - $this->readable = true; - } else { - $this->readable = false; - if ($this->isAttached()) { - $meta = $this->getMetadata(); - foreach (self::$modes['readable'] as $mode) { - if (strpos($meta['mode'], $mode) === 0) { - $this->readable = true; - break; - } - } - } - } - } - - return $this->readable; - } - - /** - * Returns whether or not the stream is writable. - * - * @return bool - */ - public function isWritable() - { - if ($this->writable === null) { - $this->writable = false; - if ($this->isAttached()) { - $meta = $this->getMetadata(); - foreach (self::$modes['writable'] as $mode) { - if (strpos($meta['mode'], $mode) === 0) { - $this->writable = true; - break; - } - } - } - } - - return $this->writable; - } - - /** - * Returns whether or not the stream is seekable. - * - * @return bool - */ - public function isSeekable() - { - if ($this->seekable === null) { - $this->seekable = false; - if ($this->isAttached()) { - $meta = $this->getMetadata(); - $this->seekable = !$this->isPipe() && $meta['seekable']; - } - } - - return $this->seekable; - } - - /** - * Seek to a position in the stream. - * - * @link http://www.php.net/manual/en/function.fseek.php - * - * @param int $offset Stream offset - * @param int $whence Specifies how the cursor position will be calculated - * based on the seek offset. Valid values are identical to the built-in - * PHP $whence values for `fseek()`. SEEK_SET: Set position equal to - * offset bytes SEEK_CUR: Set position to current location plus offset - * SEEK_END: Set position to end-of-stream plus offset. - * - * @throws RuntimeException on failure. - */ - public function seek($offset, $whence = SEEK_SET) - { - // Note that fseek returns 0 on success! - if (!$this->isSeekable() || fseek($this->stream, $offset, $whence) === -1) { - throw new RuntimeException('Could not seek in stream'); - } - } - - /** - * Seek to the beginning of the stream. - * - * If the stream is not seekable, this method will raise an exception; - * otherwise, it will perform a seek(0). - * - * @see seek() - * - * @link http://www.php.net/manual/en/function.fseek.php - * - * @throws RuntimeException on failure. - */ - public function rewind() - { - if (!$this->isSeekable() || rewind($this->stream) === false) { - throw new RuntimeException('Could not rewind stream'); - } - } - - /** - * Read data from the stream. - * - * @param int $length Read up to $length bytes from the object and return - * them. Fewer than $length bytes may be returned if underlying stream - * call returns fewer bytes. - * - * @return string Returns the data read from the stream, or an empty string - * if no bytes are available. - * - * @throws RuntimeException if an error occurs. - */ - public function read($length) - { - if (!$this->isReadable() || ($data = fread($this->stream, $length)) === false) { - throw new RuntimeException('Could not read from stream'); - } - - return $data; - } - - /** - * Write data to the stream. - * - * @param string $string The string that is to be written. - * - * @return int Returns the number of bytes written to the stream. - * - * @throws RuntimeException on failure. - */ - public function write($string) - { - if (!$this->isWritable() || ($written = fwrite($this->stream, $string)) === false) { - throw new RuntimeException('Could not write to stream'); - } - - // reset size so that it will be recalculated on next call to getSize() - $this->size = null; - - return $written; - } - - /** - * Returns the remaining contents in a string - * - * @return string - * - * @throws RuntimeException if unable to read or an error occurs while - * reading. - */ - public function getContents() - { - if (!$this->isReadable() || ($contents = stream_get_contents($this->stream)) === false) { - throw new RuntimeException('Could not get contents of stream'); - } - - return $contents; - } - - /** - * Returns whether or not the stream is a pipe. - * - * @return bool - */ - public function isPipe() - { - if ($this->isPipe === null) { - $this->isPipe = false; - if ($this->isAttached()) { - $mode = fstat($this->stream)['mode']; - $this->isPipe = ($mode & self::FSTAT_MODE_S_IFIFO) !== 0; - } - } - - return $this->isPipe; - } -} diff --git a/src/UploadedFile.php b/src/UploadedFile.php deleted file mode 100644 index d084d46..0000000 --- a/src/UploadedFile.php +++ /dev/null @@ -1,333 +0,0 @@ -has('slim.files')) { - return $env['slim.files']; - } elseif (isset($_FILES)) { - return static::parseUploadedFiles($_FILES); - } - - return []; - } - - /** - * Parse a non-normalized, i.e. $_FILES superglobal, tree of uploaded file data. - * - * @param array $uploadedFiles The non-normalized tree of uploaded file data. - * - * @return array A normalized tree of UploadedFile instances. - */ - private static function parseUploadedFiles(array $uploadedFiles) - { - $parsed = []; - foreach ($uploadedFiles as $field => $uploadedFile) { - if (!isset($uploadedFile['error'])) { - if (is_array($uploadedFile)) { - $parsed[$field] = static::parseUploadedFiles($uploadedFile); - } - continue; - } - - $parsed[$field] = []; - if (!is_array($uploadedFile['error'])) { - $parsed[$field] = new static( - $uploadedFile['tmp_name'], - isset($uploadedFile['name']) ? $uploadedFile['name'] : null, - isset($uploadedFile['type']) ? $uploadedFile['type'] : null, - isset($uploadedFile['size']) ? $uploadedFile['size'] : null, - $uploadedFile['error'], - true - ); - } else { - $subArray = []; - foreach ($uploadedFile['error'] as $fileIdx => $error) { - // normalise subarray and re-parse to move the input's keyname up a level - $subArray[$fileIdx]['name'] = $uploadedFile['name'][$fileIdx]; - $subArray[$fileIdx]['type'] = $uploadedFile['type'][$fileIdx]; - $subArray[$fileIdx]['tmp_name'] = $uploadedFile['tmp_name'][$fileIdx]; - $subArray[$fileIdx]['error'] = $uploadedFile['error'][$fileIdx]; - $subArray[$fileIdx]['size'] = $uploadedFile['size'][$fileIdx]; - - $parsed[$field] = static::parseUploadedFiles($subArray); - } - } - } - - return $parsed; - } - - /** - * Construct a new UploadedFile instance. - * - * @param string $file The full path to the uploaded file provided by the client. - * @param string|null $name The file name. - * @param string|null $type The file media type. - * @param int|null $size The file size in bytes. - * @param int $error The UPLOAD_ERR_XXX code representing the status of the upload. - * @param bool $sapi Indicates if the upload is in a SAPI environment. - */ - public function __construct($file, $name = null, $type = null, $size = null, $error = UPLOAD_ERR_OK, $sapi = false) - { - $this->file = $file; - $this->name = $name; - $this->type = $type; - $this->size = $size; - $this->error = $error; - $this->sapi = $sapi; - } - - /** - * Retrieve a stream representing the uploaded file. - * - * This method MUST return a StreamInterface instance, representing the - * uploaded file. The purpose of this method is to allow utilizing native PHP - * stream functionality to manipulate the file upload, such as - * stream_copy_to_stream() (though the result will need to be decorated in a - * native PHP stream wrapper to work with such functions). - * - * If the moveTo() method has been called previously, this method MUST raise - * an exception. - * - * @return StreamInterface Stream representation of the uploaded file. - * @throws \RuntimeException in cases when no stream is available or can be - * created. - */ - public function getStream() - { - if ($this->moved) { - throw new \RuntimeException(sprintf('Uploaded file %s has already been moved', $this->name)); - } - if ($this->stream === null) { - $this->stream = new Stream(fopen($this->file, 'r')); - } - - return $this->stream; - } - - /** - * Move the uploaded file to a new location. - * - * Use this method as an alternative to move_uploaded_file(). This method is - * guaranteed to work in both SAPI and non-SAPI environments. - * Implementations must determine which environment they are in, and use the - * appropriate method (move_uploaded_file(), rename(), or a stream - * operation) to perform the operation. - * - * $targetPath may be an absolute path, or a relative path. If it is a - * relative path, resolution should be the same as used by PHP's rename() - * function. - * - * The original file or stream MUST be removed on completion. - * - * If this method is called more than once, any subsequent calls MUST raise - * an exception. - * - * When used in an SAPI environment where $_FILES is populated, when writing - * files via moveTo(), is_uploaded_file() and move_uploaded_file() SHOULD be - * used to ensure permissions and upload status are verified correctly. - * - * If you wish to move to a stream, use getStream(), as SAPI operations - * cannot guarantee writing to stream destinations. - * - * @see http://php.net/is_uploaded_file - * @see http://php.net/move_uploaded_file - * - * @param string $targetPath Path to which to move the uploaded file. - * - * @throws InvalidArgumentException if the $path specified is invalid. - * @throws RuntimeException on any error during the move operation, or on - * the second or subsequent call to the method. - */ - public function moveTo($targetPath) - { - if ($this->moved) { - throw new RuntimeException('Uploaded file already moved'); - } - - $targetIsStream = strpos($targetPath, '://') > 0; - if (!$targetIsStream && !is_writable(dirname($targetPath))) { - throw new InvalidArgumentException('Upload target path is not writable'); - } - - if ($targetIsStream) { - if (!copy($this->file, $targetPath)) { - throw new RuntimeException(sprintf('Error moving uploaded file %s to %s', $this->name, $targetPath)); - } - if (!unlink($this->file)) { - throw new RuntimeException(sprintf('Error removing uploaded file %s', $this->name)); - } - } elseif ($this->sapi) { - if (!is_uploaded_file($this->file)) { - throw new RuntimeException(sprintf('%s is not a valid uploaded file', $this->file)); - } - - if (!move_uploaded_file($this->file, $targetPath)) { - throw new RuntimeException(sprintf('Error moving uploaded file %s to %s', $this->name, $targetPath)); - } - } else { - if (!rename($this->file, $targetPath)) { - throw new RuntimeException(sprintf('Error moving uploaded file %s to %s', $this->name, $targetPath)); - } - } - - $this->moved = true; - } - - /** - * Retrieve the error associated with the uploaded file. - * - * The return value MUST be one of PHP's UPLOAD_ERR_XXX constants. - * - * If the file was uploaded successfully, this method MUST return - * UPLOAD_ERR_OK. - * - * Implementations SHOULD return the value stored in the "error" key of - * the file in the $_FILES array. - * - * @see http://php.net/manual/en/features.file-upload.errors.php - * - * @return int One of PHP's UPLOAD_ERR_XXX constants. - */ - public function getError() - { - return $this->error; - } - - /** - * Retrieve the filename sent by the client. - * - * Do not trust the value returned by this method. A client could send - * a malicious filename with the intention to corrupt or hack your - * application. - * - * Implementations SHOULD return the value stored in the "name" key of - * the file in the $_FILES array. - * - * @return string|null The filename sent by the client or null if none - * was provided. - */ - public function getClientFilename() - { - return $this->name; - } - - /** - * Retrieve the media type sent by the client. - * - * Do not trust the value returned by this method. A client could send - * a malicious media type with the intention to corrupt or hack your - * application. - * - * Implementations SHOULD return the value stored in the "type" key of - * the file in the $_FILES array. - * - * @return string|null The media type sent by the client or null if none - * was provided. - */ - public function getClientMediaType() - { - return $this->type; - } - - /** - * Retrieve the file size. - * - * Implementations SHOULD return the value stored in the "size" key of - * the file in the $_FILES array if available, as PHP calculates this based - * on the actual size transmitted. - * - * @return int|null The file size in bytes or null if unknown. - */ - public function getSize() - { - return $this->size; - } -} diff --git a/src/Uri.php b/src/Uri.php deleted file mode 100644 index 40ee88c..0000000 --- a/src/Uri.php +++ /dev/null @@ -1,795 +0,0 @@ -scheme = $this->filterScheme($scheme); - $this->host = $this->filterHost($host); - $this->port = $this->filterPort($port); - $this->path = $this->filterPath($path); - $this->query = $this->filterQuery($query); - $this->fragment = $this->filterQuery($fragment); - $this->user = $user; - $this->password = $password; - } - - /** - * Create new Uri from string. - * - * @param string $uri Complete Uri string - * (i.e., https://user:pass@host:443/path?query). - * - * @return self - */ - public static function createFromString($uri) - { - if (!is_string($uri) && !method_exists($uri, '__toString')) { - throw new InvalidArgumentException('Uri must be a string'); - } - - $parts = parse_url($uri); - $scheme = isset($parts['scheme']) ? $parts['scheme'] : ''; - $user = isset($parts['user']) ? $parts['user'] : ''; - $pass = isset($parts['pass']) ? $parts['pass'] : ''; - $host = isset($parts['host']) ? $parts['host'] : ''; - $port = isset($parts['port']) ? $parts['port'] : null; - $path = isset($parts['path']) ? $parts['path'] : ''; - $query = isset($parts['query']) ? $parts['query'] : ''; - $fragment = isset($parts['fragment']) ? $parts['fragment'] : ''; - - return new static($scheme, $host, $port, $path, $query, $fragment, $user, $pass); - } - - /** - * Create new Uri from environment. - * - * @param array $globals The global server variables. - * - * @return self - */ - public static function createFromGlobals(array $globals) - { - $env = new Collection($globals); - - // Scheme - $isSecure = $env->get('HTTPS'); - $scheme = (empty($isSecure) || $isSecure === 'off') ? 'http' : 'https'; - - // Authority: Username and password - $username = $env->get('PHP_AUTH_USER', ''); - $password = $env->get('PHP_AUTH_PW', ''); - - // Authority: Host - if ($env->has('HTTP_HOST')) { - $host = $env->get('HTTP_HOST'); - } else { - $host = $env->get('SERVER_NAME'); - } - - // Authority: Port - $port = (int)$env->get('SERVER_PORT', 80); - if (preg_match('/^(\[[a-fA-F0-9:.]+\])(:\d+)?\z/', $host, $matches)) { - $host = $matches[1]; - - if (isset($matches[2])) { - $port = (int) substr($matches[2], 1); - } - } else { - $pos = strpos($host, ':'); - if ($pos !== false) { - $port = (int) substr($host, $pos + 1); - $host = strstr($host, ':', true); - } - } - - $requestUri = $env->get('REQUEST_URI'); - - // Query string - $queryString = $env->get('QUERY_STRING', ''); - if ($queryString === '') { - $queryString = parse_url('http://example.com' . $env->get('REQUEST_URI'), PHP_URL_QUERY); - } - - // Fragment - $fragment = ''; - - // Build Uri - $uri = new static($scheme, $host, $port, $requestUri, $queryString, $fragment, $username, $password); - - return $uri; - } - - /******************************************************************************** - * Scheme - *******************************************************************************/ - - /** - * Retrieve the scheme component of the URI. - * - * If no scheme is present, this method MUST return an empty string. - * - * The value returned MUST be normalized to lowercase, per RFC 3986 - * Section 3.1. - * - * The trailing ":" character is not part of the scheme and MUST NOT be - * added. - * - * @see https://tools.ietf.org/html/rfc3986#section-3.1 - * @return string The URI scheme. - */ - public function getScheme() - { - return $this->scheme; - } - - /** - * Return an instance with the specified scheme. - * - * This method MUST retain the state of the current instance, and return - * an instance that contains the specified scheme. - * - * Implementations MUST support the schemes "http" and "https" case - * insensitively, and MAY accommodate other schemes if required. - * - * An empty scheme is equivalent to removing the scheme. - * - * @param string $scheme The scheme to use with the new instance. - * @return self A new instance with the specified scheme. - * @throws \InvalidArgumentException for invalid or unsupported schemes. - */ - public function withScheme($scheme) - { - $scheme = $this->filterScheme($scheme); - $clone = clone $this; - $clone->scheme = $scheme; - - return $clone; - } - - /** - * Filter Uri scheme. - * - * @param string $scheme Raw Uri scheme. - * @return string - * - * @throws InvalidArgumentException If the Uri scheme is not a string. - * @throws InvalidArgumentException If Uri scheme is not "", "https", or "http". - */ - protected function filterScheme($scheme) - { - static $valid = [ - '' => true, - 'https' => true, - 'http' => true, - ]; - - if (!is_string($scheme) && !method_exists($scheme, '__toString')) { - throw new InvalidArgumentException('Uri scheme must be a string'); - } - - $scheme = str_replace('://', '', strtolower($scheme)); - if (!isset($valid[$scheme])) { - throw new InvalidArgumentException('Uri scheme must be one of: "", "https", "http"'); - } - - return $scheme; - } - - /******************************************************************************** - * Authority - *******************************************************************************/ - - /** - * Retrieve the authority component of the URI. - * - * If no authority information is present, this method MUST return an empty - * string. - * - * The authority syntax of the URI is: - * - *
-     * [user-info@]host[:port]
-     * 
- * - * If the port component is not set or is the standard port for the current - * scheme, it SHOULD NOT be included. - * - * @see https://tools.ietf.org/html/rfc3986#section-3.2 - * @return string The URI authority, in "[user-info@]host[:port]" format. - */ - public function getAuthority() - { - $userInfo = $this->getUserInfo(); - $host = $this->getHost(); - $port = $this->getPort(); - - return ($userInfo !== '' ? $userInfo . '@' : '') . $host . ($port !== null ? ':' . $port : ''); - } - - /** - * Retrieve the user information component of the URI. - * - * If no user information is present, this method MUST return an empty - * string. - * - * If a user is present in the URI, this will return that value; - * additionally, if the password is also present, it will be appended to the - * user value, with a colon (":") separating the values. - * - * The trailing "@" character is not part of the user information and MUST - * NOT be added. - * - * @return string The URI user information, in "username[:password]" format. - */ - public function getUserInfo() - { - $info = $this->user; - - if (isset($this->password) && $this->password !== '') { - $info .= ':' . $this->password; - } - - return $info; - } - - /** - * Return an instance with the specified user information. - * - * This method MUST retain the state of the current instance, and return - * an instance that contains the specified user information. - * - * Password is optional, but the user information MUST include the - * user; an empty string for the user is equivalent to removing user - * information. - * - * @param string $user The user name to use for authority. - * @param null|string $password The password associated with $user. - * @return self A new instance with the specified user information. - */ - public function withUserInfo($user, $password = null) - { - $clone = clone $this; - $clone->user = $this->filterUserInfo($user); - if ($clone->user !== '') { - $clone->password = isset($password) ? $this->filterUserInfo($password) : ''; - } else { - $clone->password = ''; - } - - return $clone; - } - - /** - * Filters the user info string. - * - * @param string $query The raw uri query string. - * @return string The percent-encoded query string. - */ - protected function filterUserInfo($query) - { - return preg_replace_callback( - '/(?:[^a-zA-Z0-9_\-\.~!\$&\'\(\)\*\+,;=]+|%(?![A-Fa-f0-9]{2}))/u', - function ($match) { - return rawurlencode($match[0]); - }, - $query - ); - } - - /** - * Retrieve the host component of the URI. - * - * If no host is present, this method MUST return an empty string. - * - * The value returned MUST be normalized to lowercase, per RFC 3986 - * Section 3.2.2. - * - * @see http://tools.ietf.org/html/rfc3986#section-3.2.2 - * @return string The URI host. - */ - public function getHost() - { - return $this->host; - } - - /** - * Return an instance with the specified host. - * - * This method MUST retain the state of the current instance, and return - * an instance that contains the specified host. - * - * An empty host value is equivalent to removing the host. - * - * @param string $host The hostname to use with the new instance. - * @return self A new instance with the specified host. - * @throws \InvalidArgumentException for invalid hostnames. - */ - public function withHost($host) - { - $clone = clone $this; - $clone->host = $this->filterHost($host); - - return $clone; - } - - /** - * Filter Uri host. - * - * If the supplied host is an IPv6 address, then it is converted to a reference - * as per RFC 2373. - * - * @param string $host The host to filter. - * @return string - * @throws \InvalidArgumentException for invalid host names. - */ - protected function filterHost($host) - { - if (!is_string($host) && !method_exists($host, '__toString')) { - throw new InvalidArgumentException('Uri host must be a string'); - } - - if (filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { - $host = '[' . $host . ']'; - } - - return strtolower($host); - } - - /** - * Retrieve the port component of the URI. - * - * If a port is present, and it is non-standard for the current scheme, - * this method MUST return it as an integer. If the port is the standard port - * used with the current scheme, this method SHOULD return null. - * - * If no port is present, and no scheme is present, this method MUST return - * a null value. - * - * If no port is present, but a scheme is present, this method MAY return - * the standard port for that scheme, but SHOULD return null. - * - * @return null|int The URI port. - */ - public function getPort() - { - return $this->port && !$this->hasStandardPort() ? $this->port : null; - } - - /** - * Return an instance with the specified port. - * - * This method MUST retain the state of the current instance, and return - * an instance that contains the specified port. - * - * Implementations MUST raise an exception for ports outside the - * established TCP and UDP port ranges. - * - * A null value provided for the port is equivalent to removing the port - * information. - * - * @param null|int $port The port to use with the new instance; a null value - * removes the port information. - * @return self A new instance with the specified port. - * @throws \InvalidArgumentException for invalid ports. - */ - public function withPort($port) - { - $port = $this->filterPort($port); - $clone = clone $this; - $clone->port = $port; - - return $clone; - } - - /** - * Does this Uri use a standard port? - * - * @return bool - */ - protected function hasStandardPort() - { - return ($this->scheme === 'http' && $this->port === 80) || ($this->scheme === 'https' && $this->port === 443); - } - - /** - * Filter Uri port. - * - * @param null|int $port The Uri port number. - * @return null|int - * - * @throws InvalidArgumentException If the port is invalid. - */ - protected function filterPort($port) - { - if (is_null($port) || (is_integer($port) && ($port >= 1 && $port <= 65535))) { - return $port; - } - - throw new InvalidArgumentException('Uri port must be null or an integer between 1 and 65535 (inclusive)'); - } - - /******************************************************************************** - * Path - *******************************************************************************/ - - /** - * Retrieve the path component of the URI. - * - * The path can either be empty or absolute (starting with a slash) or - * rootless (not starting with a slash). Implementations MUST support all - * three syntaxes. - * - * Normally, the empty path "" and absolute path "/" are considered equal as - * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically - * do this normalization because in contexts with a trimmed base path, e.g. - * the front controller, this difference becomes significant. It's the task - * of the user to handle both "" and "/". - * - * The value returned MUST be percent-encoded, but MUST NOT double-encode - * any characters. To determine what characters to encode, please refer to - * RFC 3986, Sections 2 and 3.3. - * - * As an example, if the value should include a slash ("/") not intended as - * delimiter between path segments, that value MUST be passed in encoded - * form (e.g., "%2F") to the instance. - * - * @see https://tools.ietf.org/html/rfc3986#section-2 - * @see https://tools.ietf.org/html/rfc3986#section-3.3 - * @return string The URI path. - */ - public function getPath() - { - return $this->path; - } - - /** - * Return an instance with the specified path. - * - * This method MUST retain the state of the current instance, and return - * an instance that contains the specified path. - * - * The path can either be empty or absolute (starting with a slash) or - * rootless (not starting with a slash). Implementations MUST support all - * three syntaxes. - * - * If the path is intended to be domain-relative rather than path relative then - * it must begin with a slash ("/"). Paths not starting with a slash ("/") - * are assumed to be relative to some base path known to the application or - * consumer. - * - * Users can provide both encoded and decoded path characters. - * Implementations ensure the correct encoding as outlined in getPath(). - * - * @param string $path The path to use with the new instance. - * @return self A new instance with the specified path. - * @throws \InvalidArgumentException for invalid paths. - */ - public function withPath($path) - { - if (!is_string($path)) { - throw new InvalidArgumentException('Uri path must be a string'); - } - - $clone = clone $this; - $clone->path = $this->filterPath($path); - - return $clone; - } - - /** - * Filter Uri path. - * - * This method percent-encodes all reserved - * characters in the provided path string. This method - * will NOT double-encode characters that are already - * percent-encoded. - * - * @param string $path The raw uri path. - * @return string The RFC 3986 percent-encoded uri path. - * @link http://www.faqs.org/rfcs/rfc3986.html - */ - protected function filterPath($path) - { - return preg_replace_callback( - '/(?:[^a-zA-Z0-9_\-\.~:@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/', - function ($match) { - return rawurlencode($match[0]); - }, - $path - ); - } - - /******************************************************************************** - * Query - *******************************************************************************/ - - /** - * Retrieve the query string of the URI. - * - * If no query string is present, this method MUST return an empty string. - * - * The leading "?" character is not part of the query and MUST NOT be - * added. - * - * The value returned MUST be percent-encoded, but MUST NOT double-encode - * any characters. To determine what characters to encode, please refer to - * RFC 3986, Sections 2 and 3.4. - * - * As an example, if a value in a key/value pair of the query string should - * include an ampersand ("&") not intended as a delimiter between values, - * that value MUST be passed in encoded form (e.g., "%26") to the instance. - * - * @see https://tools.ietf.org/html/rfc3986#section-2 - * @see https://tools.ietf.org/html/rfc3986#section-3.4 - * @return string The URI query string. - */ - public function getQuery() - { - return $this->query; - } - - /** - * Return an instance with the specified query string. - * - * This method MUST retain the state of the current instance, and return - * an instance that contains the specified query string. - * - * Users can provide both encoded and decoded query characters. - * Implementations ensure the correct encoding as outlined in getQuery(). - * - * An empty query string value is equivalent to removing the query string. - * - * @param string $query The query string to use with the new instance. - * @return self A new instance with the specified query string. - * @throws \InvalidArgumentException for invalid query strings. - */ - public function withQuery($query) - { - if (!is_string($query) && !method_exists($query, '__toString')) { - throw new InvalidArgumentException('Uri query must be a string'); - } - $query = ltrim($query, '?'); - $clone = clone $this; - $clone->query = $this->filterQuery($query); - - return $clone; - } - - /** - * Filters the query string or fragment of a URI. - * - * @param string $query The raw uri query string. - * @return string The percent-encoded query string. - */ - protected function filterQuery($query) - { - return preg_replace_callback( - '/(?:[^a-zA-Z0-9_\-\.~!\$&\'\(\)\*\+,;=%:@\/\?]+|%(?![A-Fa-f0-9]{2}))/', - function ($match) { - return rawurlencode($match[0]); - }, - $query - ); - } - - /******************************************************************************** - * Fragment - *******************************************************************************/ - - /** - * Retrieve the fragment component of the URI. - * - * If no fragment is present, this method MUST return an empty string. - * - * The leading "#" character is not part of the fragment and MUST NOT be - * added. - * - * The value returned MUST be percent-encoded, but MUST NOT double-encode - * any characters. To determine what characters to encode, please refer to - * RFC 3986, Sections 2 and 3.5. - * - * @see https://tools.ietf.org/html/rfc3986#section-2 - * @see https://tools.ietf.org/html/rfc3986#section-3.5 - * @return string The URI fragment. - */ - public function getFragment() - { - return $this->fragment; - } - - /** - * Return an instance with the specified URI fragment. - * - * This method MUST retain the state of the current instance, and return - * an instance that contains the specified URI fragment. - * - * Users can provide both encoded and decoded fragment characters. - * Implementations ensure the correct encoding as outlined in getFragment(). - * - * An empty fragment value is equivalent to removing the fragment. - * - * @param string $fragment The fragment to use with the new instance. - * @return self A new instance with the specified fragment. - */ - public function withFragment($fragment) - { - if (!is_string($fragment) && !method_exists($fragment, '__toString')) { - throw new InvalidArgumentException('Uri fragment must be a string'); - } - $fragment = ltrim($fragment, '#'); - $clone = clone $this; - $clone->fragment = $this->filterQuery($fragment); - - return $clone; - } - - /******************************************************************************** - * Helpers - *******************************************************************************/ - - /** - * Return the string representation as a URI reference. - * - * Depending on which components of the URI are present, the resulting - * string is either a full URI or relative reference according to RFC 3986, - * Section 4.1. The method concatenates the various components of the URI, - * using the appropriate delimiters: - * - * - If a scheme is present, it MUST be suffixed by ":". - * - If an authority is present, it MUST be prefixed by "//". - * - The path can be concatenated without delimiters. But there are two - * cases where the path has to be adjusted to make the URI reference - * valid as PHP does not allow to throw an exception in __toString(): - * - If the path is rootless and an authority is present, the path MUST - * be prefixed by "/". - * - If the path is starting with more than one "/" and no authority is - * present, the starting slashes MUST be reduced to one. - * - If a query is present, it MUST be prefixed by "?". - * - If a fragment is present, it MUST be prefixed by "#". - * - * @see http://tools.ietf.org/html/rfc3986#section-4.1 - * @return string - */ - public function __toString() - { - $scheme = $this->getScheme(); - $authority = $this->getAuthority(); - $path = $this->getPath(); - $query = $this->getQuery(); - $fragment = $this->getFragment(); - - $path = '/' . ltrim($path, '/'); - - return ($scheme !== '' ? $scheme . ':' : '') - . ($authority !== '' ? '//' . $authority : '') - . $path - . ($query !== '' ? '?' . $query : '') - . ($fragment !== '' ? '#' . $fragment : ''); - } - - /** - * Return the fully qualified base URL. - * - * Note that this method never includes a trailing / - * - * This method is not part of PSR-7. - * - * @return string - */ - public function getBaseUrl() - { - $scheme = $this->getScheme(); - $authority = $this->getAuthority(); - - return ($scheme !== '' ? $scheme . ':' : '') - . ($authority !== '' ? '//' . $authority : ''); - } -} diff --git a/tests/BodyTest.php b/tests/BodyTest.php deleted file mode 100644 index 3c9cba4..0000000 --- a/tests/BodyTest.php +++ /dev/null @@ -1,416 +0,0 @@ -stream) === true) { - fclose($this->stream); - } - } - - /** - * This method creates a new resource, and it seeds - * the resource with lorem ipsum text. The returned - * resource is readable, writable, and seekable. - * - * @param string $mode - * - * @return resource - */ - public function resourceFactory($mode = 'r+') - { - $stream = fopen('php://temp', $mode); - fwrite($stream, $this->text); - rewind($stream); - - return $stream; - } - - public function testConstructorAttachesStream() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - $bodyStream = new ReflectionProperty($body, 'stream'); - $bodyStream->setAccessible(true); - - $this->assertSame($this->stream, $bodyStream->getValue($body)); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testConstructorInvalidStream() - { - $this->stream = 'foo'; - $body = new Body($this->stream); - } - - public function testGetMetadata() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - - $this->assertTrue(is_array($body->getMetadata())); - } - - public function testGetMetadataKey() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - - $this->assertEquals('php://temp', $body->getMetadata('uri')); - } - - public function testGetMetadataKeyNotFound() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - - $this->assertNull($body->getMetadata('foo')); - } - - public function testDetach() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - - $bodyStream = new ReflectionProperty($body, 'stream'); - $bodyStream->setAccessible(true); - - $bodyMetadata = new ReflectionProperty($body, 'meta'); - $bodyMetadata->setAccessible(true); - - $bodyReadable = new ReflectionProperty($body, 'readable'); - $bodyReadable->setAccessible(true); - - $bodyWritable = new ReflectionProperty($body, 'writable'); - $bodyWritable->setAccessible(true); - - $bodySeekable = new ReflectionProperty($body, 'seekable'); - $bodySeekable->setAccessible(true); - - $result = $body->detach(); - - $this->assertSame($this->stream, $result); - $this->assertNull($bodyStream->getValue($body)); - $this->assertNull($bodyMetadata->getValue($body)); - $this->assertNull($bodyReadable->getValue($body)); - $this->assertNull($bodyWritable->getValue($body)); - $this->assertNull($bodySeekable->getValue($body)); - } - - public function testToStringAttached() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - - $this->assertEquals($this->text, (string)$body); - } - - public function testToStringAttachedRewindsFirst() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - - $this->assertEquals($this->text, (string)$body); - $this->assertEquals($this->text, (string)$body); - $this->assertEquals($this->text, (string)$body); - } - - public function testToStringDetached() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - $bodyStream = new ReflectionProperty($body, 'stream'); - $bodyStream->setAccessible(true); - $bodyStream->setValue($body, null); - - $this->assertEquals('', (string)$body); - } - - public function testClose() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - $body->close(); - - $this->assertAttributeEquals(null, 'stream', $body); - //$this->assertFalse($body->isAttached()); #1269 - } - - public function testGetSizeAttached() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - - $this->assertEquals(mb_strlen($this->text), $body->getSize()); - } - - public function testGetSizeDetached() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - $bodyStream = new ReflectionProperty($body, 'stream'); - $bodyStream->setAccessible(true); - $bodyStream->setValue($body, null); - - $this->assertNull($body->getSize()); - } - - public function testTellAttached() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - fseek($this->stream, 10); - - $this->assertEquals(10, $body->tell()); - } - - /** - * @expectedException \RuntimeException - */ - public function testTellDetachedThrowsRuntimeException() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - $bodyStream = new ReflectionProperty($body, 'stream'); - $bodyStream->setAccessible(true); - $bodyStream->setValue($body, null); - - $body->tell(); - } - - public function testEofAttachedFalse() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - fseek($this->stream, 10); - - $this->assertFalse($body->eof()); - } - - public function testEofAttachedTrue() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - while (feof($this->stream) === false) { - fread($this->stream, 1024); - } - - $this->assertTrue($body->eof()); - } - - public function testEofDetached() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - $bodyStream = new ReflectionProperty($body, 'stream'); - $bodyStream->setAccessible(true); - $bodyStream->setValue($body, null); - - $this->assertTrue($body->eof()); - } - - public function isReadableAttachedTrue() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - - $this->assertTrue($body->isReadable()); - } - - public function isReadableAttachedFalse() - { - $stream = fopen('php://temp', 'w'); - $body = new Body($this->stream); - - $this->assertFalse($body->isReadable()); - fclose($stream); - } - - public function testIsReadableDetached() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - $body->detach(); - - $this->assertFalse($body->isReadable()); - } - - public function isWritableAttachedTrue() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - - $this->assertTrue($body->isWritable()); - } - - public function isWritableAttachedFalse() - { - $stream = fopen('php://temp', 'r'); - $body = new Body($this->stream); - - $this->assertFalse($body->isWritable()); - fclose($stream); - } - - public function testIsWritableDetached() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - $body->detach(); - - $this->assertFalse($body->isWritable()); - } - - public function isSeekableAttachedTrue() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - - $this->assertTrue($body->isSeekable()); - } - - // TODO: Is seekable is false when attached... how? - - public function testIsSeekableDetached() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - $body->detach(); - - $this->assertFalse($body->isSeekable()); - } - - public function testSeekAttached() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - $body->seek(10); - - $this->assertEquals(10, ftell($this->stream)); - } - - /** - * @expectedException \RuntimeException - */ - public function testSeekDetachedThrowsRuntimeException() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - $body->detach(); - - $body->seek(10); - } - - public function testRewindAttached() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - fseek($this->stream, 10); - $body->rewind(); - - $this->assertEquals(0, ftell($this->stream)); - } - - /** - * @expectedException \RuntimeException - */ - public function testRewindDetachedThrowsRuntimeException() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - $body->detach(); - - $body->rewind(); - } - - public function testReadAttached() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - - $this->assertEquals(substr($this->text, 0, 10), $body->read(10)); - } - - /** - * @expectedException \RuntimeException - */ - public function testReadDetachedThrowsRuntimeException() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - $body->detach(); - - $body->read(10); - } - - public function testWriteAttached() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - while (feof($this->stream) === false) { - fread($this->stream, 1024); - } - $body->write('foo'); - - $this->assertEquals($this->text . 'foo', (string)$body); - } - - /** - * @expectedException \RuntimeException - */ - public function testWriteDetachedThrowsRuntimeException() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - $body->detach(); - - $body->write('foo'); - } - - public function testGetContentsAttached() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - fseek($this->stream, 10); - - $this->assertEquals(substr($this->text, 10), $body->getContents()); - } - - /** - * @expectedException \RuntimeException - */ - public function testGetContentsDetachedThrowsRuntimeException() - { - $this->stream = $this->resourceFactory(); - $body = new Body($this->stream); - $body->detach(); - - $body->getContents(); - } -} diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php deleted file mode 100644 index 9a96013..0000000 --- a/tests/CollectionTest.php +++ /dev/null @@ -1,156 +0,0 @@ -bag = new Collection(); - $this->property = new ReflectionProperty($this->bag, 'data'); - $this->property->setAccessible(true); - } - - public function testInitializeWithData() - { - $bag = new Collection(['foo' => 'bar']); - $bagProperty = new ReflectionProperty($bag, 'data'); - $bagProperty->setAccessible(true); - - $this->assertEquals(['foo' => 'bar'], $bagProperty->getValue($bag)); - } - - public function testSet() - { - $this->bag->set('foo', 'bar'); - $this->assertArrayHasKey('foo', $this->property->getValue($this->bag)); - $bag = $this->property->getValue($this->bag); - $this->assertEquals('bar', $bag['foo']); - } - - public function testOffsetSet() - { - $this->bag['foo'] = 'bar'; - $this->assertArrayHasKey('foo', $this->property->getValue($this->bag)); - $bag = $this->property->getValue($this->bag); - $this->assertEquals('bar', $bag['foo']); - } - - public function testGet() - { - $this->property->setValue($this->bag, ['foo' => 'bar']); - $this->assertEquals('bar', $this->bag->get('foo')); - } - - public function testGetWithDefault() - { - $this->property->setValue($this->bag, ['foo' => 'bar']); - $this->assertEquals('default', $this->bag->get('abc', 'default')); - } - - public function testReplace() - { - $this->bag->replace([ - 'abc' => '123', - 'foo' => 'bar', - ]); - $this->assertArrayHasKey('abc', $this->property->getValue($this->bag)); - $this->assertArrayHasKey('foo', $this->property->getValue($this->bag)); - $bag = $this->property->getValue($this->bag); - $this->assertEquals('123', $bag['abc']); - $this->assertEquals('bar', $bag['foo']); - } - - public function testAll() - { - $data = [ - 'abc' => '123', - 'foo' => 'bar', - ]; - $this->property->setValue($this->bag, $data); - $this->assertEquals($data, $this->bag->all()); - } - - public function testKeys() - { - $data = [ - 'abc' => '123', - 'foo' => 'bar', - ]; - $this->property->setValue($this->bag, $data); - $this->assertEquals(['abc', 'foo'], $this->bag->keys()); - } - - public function testHas() - { - $this->property->setValue($this->bag, ['foo' => 'bar']); - $this->assertTrue($this->bag->has('foo')); - $this->assertFalse($this->bag->has('abc')); - } - - public function testOffsetExists() - { - $this->property->setValue($this->bag, ['foo' => 'bar']); - $this->assertTrue(isset($this->bag['foo'])); - } - - public function testRemove() - { - $data = [ - 'abc' => '123', - 'foo' => 'bar', - ]; - $this->property->setValue($this->bag, $data); - $this->bag->remove('foo'); - $this->assertEquals(['abc' => '123'], $this->property->getValue($this->bag)); - } - - public function testOffsetUnset() - { - $data = [ - 'abc' => '123', - 'foo' => 'bar', - ]; - $this->property->setValue($this->bag, $data); - - unset($this->bag['foo']); - $this->assertEquals(['abc' => '123'], $this->property->getValue($this->bag)); - } - - public function testClear() - { - $data = [ - 'abc' => '123', - 'foo' => 'bar', - ]; - $this->property->setValue($this->bag, $data); - $this->bag->clear(); - $this->assertEquals([], $this->property->getValue($this->bag)); - } - - public function testCount() - { - $this->property->setValue($this->bag, ['foo' => 'bar', 'abc' => '123']); - $this->assertEquals(2, $this->bag->count()); - } -} diff --git a/tests/CookiesTest.php b/tests/CookiesTest.php deleted file mode 100644 index ea40917..0000000 --- a/tests/CookiesTest.php +++ /dev/null @@ -1,235 +0,0 @@ - 'Works', - ]); - $prop = new ReflectionProperty($cookies, 'requestCookies'); - $prop->setAccessible(true); - $this->assertNotEmpty($prop->getValue($cookies)['test']); - $this->assertEquals('Works', $prop->getValue($cookies)['test']); - } - - public function testSetDefaults() - { - $defaults = [ - 'value' => 'toast', - 'domain' => null, - 'hostonly' => null, - 'path' => null, - 'expires' => null, - 'secure' => true, - 'httponly' => true - ]; - - $cookies = new Cookies; - - $prop = new ReflectionProperty($cookies, 'defaults'); - $prop->setAccessible(true); - - $origDefaults = $prop->getValue($cookies); - - $cookies->setDefaults($defaults); - - $this->assertEquals($defaults, $prop->getValue($cookies)); - $this->assertNotEquals($origDefaults, $prop->getValue($cookies)); - } - - public function testSetCookieValues() - { - $cookies = new Cookies; - $cookies->set('foo', 'bar'); - - $prop = new ReflectionProperty($cookies, 'responseCookies'); - $prop->setAccessible(true); - - //we expect all of these values with null/false defaults - $expectedValue = [ - 'foo' => [ - 'value' => 'bar', - 'domain' => null, - 'hostonly' => null, - 'path' => null, - 'expires' => null, - 'secure' => false, - 'httponly' => false - ] - ]; - - $this->assertEquals($expectedValue, $prop->getValue($cookies)); - } - - public function testSetCookieValuesContainDefaults() - { - $cookies = new Cookies; - $defaults = [ - 'value' => 'toast', - 'domain' => null, - 'hostonly' => null, - 'path' => null, - 'expires' => null, - 'secure' => true, - 'httponly' => true - ]; - - $cookies->setDefaults($defaults); - $cookies->set('foo', 'bar'); - - $prop = new ReflectionProperty($cookies, 'responseCookies'); - $prop->setAccessible(true); - - //we expect to have secure and httponly from defaults - $expectedValue = [ - 'foo' => [ - 'value' => 'bar', - 'domain' => null, - 'hostonly' => null, - 'path' => null, - 'expires' => null, - 'secure' => true, - 'httponly' => true - ] - ]; - - $this->assertEquals($expectedValue, $prop->getValue($cookies)); - } - - public function testSetCookieValuesCanOverrideDefaults() - { - $cookies = new Cookies; - $defaults = [ - 'value' => 'toast', - 'domain' => null, - 'hostonly' => null, - 'path' => null, - 'expires' => null, - 'secure' => true, - 'httponly' => true - ]; - - $cookies->setDefaults($defaults); - - //default has secure true, lets override it to false - $cookies->set('foo', ['value' => 'bar', 'secure' => false]); - - $prop = new ReflectionProperty($cookies, 'responseCookies'); - $prop->setAccessible(true); - - $expectedValue = [ - 'foo' => [ - 'value' => 'bar', - 'domain' => null, - 'hostonly' => null, - 'path' => null, - 'expires' => null, - 'secure' => false, - 'httponly' => true - ] - ]; - - $this->assertEquals($expectedValue, $prop->getValue($cookies)); - } - - public function testGet() - { - $cookies = new Cookies(['foo' => 'bar']); - $this->assertEquals('bar', $cookies->get('foo')); - $this->assertNull($cookies->get('missing')); - $this->assertEquals('defaultValue', $cookies->get('missing', 'defaultValue')); - } - - public function testParseHeader() - { - $cookies = Cookies::parseHeader('foo=bar; name=Josh'); - $this->assertEquals('bar', $cookies['foo']); - $this->assertEquals('Josh', $cookies['name']); - } - - public function testParseHeaderWithJsonArray() - { - $cookies = Cookies::parseHeader('foo=bar; testarray=["someVar1","someVar2","someVar3"]'); - $this->assertEquals('bar', $cookies['foo']); - $this->assertContains('someVar3', json_decode($cookies['testarray'])); - } - - public function testToHeaders() - { - $cookies = new Cookies; - $cookies->set('test', 'Works'); - $cookies->set('test_array', ['value' => 'bar', 'domain' => 'example.com']); - $this->assertEquals('test=Works', $cookies->toHeaders()[0]); - $this->assertEquals('test_array=bar; domain=example.com', $cookies->toHeaders()[1]); - } - - public function testToHeader() - { - $cookies = new Cookies(); - $class = new ReflectionClass($cookies); - $method = $class->getMethod('toHeader'); - $method->setAccessible(true); - $properties = [ - 'name' => 'test', - 'properties' => [ - 'value' => 'Works' - ] - ]; - $time = time(); - $formattedDate = gmdate('D, d-M-Y H:i:s e', $time); - $propertiesComplex = [ - 'name' => 'test_complex', - 'properties' => [ - 'value' => 'Works', - 'domain' => 'example.com', - 'expires' => $time, - 'path' => '/', - 'secure' => true, - 'hostonly' => true, - 'httponly' => true - ] - ]; - $stringDate = '2016-01-01 12:00:00'; - $formattedStringDate = gmdate('D, d-M-Y H:i:s e', strtotime($stringDate)); - $propertiesStringDate = [ - 'name' => 'test_date', - 'properties' => [ - 'value' => 'Works', - 'expires' => $stringDate, - ] - ]; - $cookie = $method->invokeArgs($cookies, $properties); - $cookieComplex = $method->invokeArgs($cookies, $propertiesComplex); - $cookieStringDate = $method->invokeArgs($cookies, $propertiesStringDate); - $this->assertEquals('test=Works', $cookie); - $this->assertEquals( - 'test_complex=Works; domain=example.com; path=/; expires=' - . $formattedDate . '; secure; HostOnly; HttpOnly', - $cookieComplex - ); - $this->assertEquals('test_date=Works; expires=' . $formattedStringDate, $cookieStringDate); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testParseHeaderException() - { - Cookies::parseHeader(new \StdClass); - } -} diff --git a/tests/Decorators/ResponseDecoratorTest.php b/tests/Decorators/ResponseDecoratorTest.php new file mode 100644 index 0000000..81d129d --- /dev/null +++ b/tests/Decorators/ResponseDecoratorTest.php @@ -0,0 +1,449 @@ +factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(); + $response->foo = 'bar'; + + $this->assertFalse(property_exists($response, 'foo')); + } + } + + public function testGetHeader() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(); + $response = $response->withHeader('Content-Type', 'application/json'); + + $this->assertEquals(['application/json'], $response->getHeader('Content-Type')); + } + } + + public function testGetStatusCode() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(404); + $this->assertEquals(404, $response->getStatusCode()); + } + } + + public function testWithStatus() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(); + $clone = $response->withStatus(404); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals(404, $clone->getStatusCode()); + } + } + + public function testGetReasonPhrase() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(404, 'Not Found'); + $this->assertEquals('Not Found', $response->getReasonPhrase()); + } + } + + public function testGetCustomReasonPhrase() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(200, 'Custom Phrase'); + $this->assertEquals('Custom Phrase', $response->getReasonPhrase()); + } + } + + public function testWithAddedHeader() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(); + $response = $response->withHeader('Content-Type', 'application/json'); + $response = $response->withAddedHeader('Content-Type', 'application/pdf'); + + $this->assertEquals(['application/json', 'application/pdf'], $response->getHeader('Content-Type')); + } + } + + public function testWithoutHeader() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(); + + $response = $response->withHeader('Content-Type', 'application/json'); + $this->assertEquals(['application/json'], $response->getHeader('Content-Type')); + + $response = $response->withoutHeader('Content-Type'); + $this->assertEquals([], $response->getHeader('Content-Type')); + } + } + + public function testWithProtocolVersion() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(); + + $response = $response->withProtocolVersion('1.0'); + $this->assertEquals('1.0', $response->getProtocolVersion()); + } + } + + public function testWithRedirect() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(); + $clone = $response->withRedirect('/foo', 301); + $cloneWithDefaultStatus = $response->withRedirect('/foo'); + $cloneWithStatusMethod = $response->withStatus(301)->withRedirect('/foo'); + + $this->assertSame(200, $response->getStatusCode()); + $this->assertFalse($response->hasHeader('Location')); + + $this->assertSame(301, $clone->getStatusCode()); + $this->assertTrue($clone->hasHeader('Location')); + $this->assertEquals('/foo', $clone->getHeaderLine('Location')); + + $this->assertSame(302, $cloneWithDefaultStatus->getStatusCode()); + $this->assertTrue($cloneWithDefaultStatus->hasHeader('Location')); + $this->assertEquals('/foo', $cloneWithDefaultStatus->getHeaderLine('Location')); + + $this->assertSame(302, $cloneWithStatusMethod->getStatusCode()); + $this->assertTrue($cloneWithStatusMethod->hasHeader('Location')); + $this->assertEquals('/foo', $cloneWithStatusMethod->getHeaderLine('Location')); + } + } + + public function testIsEmpty() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(204); + + $this->assertTrue($response->isEmpty()); + } + } + + public function testIsInformational() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(100); + + $this->assertTrue($response->isInformational()); + } + } + + public function testIsOk() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(); + + $this->assertTrue($response->isOk()); + } + } + + public function testIsSuccessful() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(201); + $this->assertTrue($response->isSuccessful()); + } + } + + public function testIsRedirect() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(302); + $this->assertTrue($response->isRedirect()); + } + } + + public function testIsRedirection() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(308); + $this->assertTrue($response->isRedirection()); + } + } + + public function testIsForbidden() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(403); + $this->assertTrue($response->isForbidden()); + } + } + + public function testIsNotFound() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(404); + $this->assertTrue($response->isNotFound()); + } + } + + public function testIsClientError() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(400); + $this->assertTrue($response->isClientError()); + } + } + + public function testIsServerError() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(503); + $this->assertTrue($response->isServerError()); + } + } + + public function testToString() + { + $output = 'HTTP/1.1 404 Not Found' . ResponseDecorator::EOL . + 'X-Foo: Bar' . ResponseDecorator::EOL . ResponseDecorator::EOL . + 'Where am I?'; + + $expectedOutputString = ''; + $actualOutputString = ''; + + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(308); + $response = $response->withStatus(404)->withHeader('X-Foo', 'Bar')->write('Where am I?'); + + $expectedOutputString .= $output; + $actualOutputString .= (string) $response; + } + + $this->assertEquals($expectedOutputString, $actualOutputString); + } + + public function testWithJson() + { + $data = ['foo' => 'bar1&bar2']; + + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + /** @var ResponseDecorator $originalResponse */ + $originalResponse = $decoratedResponseFactory->createResponse(503); + $response = $originalResponse->withJson($data, 201); + + $this->assertNotEquals($response->getStatusCode(), $originalResponse->getStatusCode()); + $this->assertEquals(201, $response->getStatusCode()); + $this->assertEquals('application/json;charset=utf-8', $response->getHeaderLine('Content-Type')); + + $body = $response->getBody(); + $body->rewind(); + $dataJson = $body->getContents(); //json_decode($body->getContents(), true); + + $originalBody = $originalResponse->getBody(); + $originalBody->rewind(); + $originalContents = $originalBody->getContents(); + + // test the original body hasn't be replaced + $this->assertNotEquals($dataJson, $originalContents); + $this->assertEquals('{"foo":"bar1&bar2"}', $dataJson); + $this->assertEquals($data['foo'], json_decode($dataJson, true)['foo']); + + $response = $response->withJson($data, 200, JSON_HEX_AMP); + + $body = $response->getBody(); + $body->rewind(); + $dataJson = $body->getContents(); + + $this->assertEquals('{"foo":"bar1\u0026bar2"}', $dataJson); + $this->assertEquals($data['foo'], json_decode($dataJson, true)['foo']); + + $response = $response->withStatus(201)->withJson([]); + $this->assertEquals($response->getStatusCode(), 201); + } + } + + /** + * @expectedException \RuntimeException + */ + public function testWithInvalidJsonThrowsException() + { + $data = ['foo' => 'bar'.chr(233)]; + $this->assertEquals('bar'.chr(233), $data['foo']); + + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedResponseFactory = new DecoratedResponseFactory( + $provider->getResponseFactory(), + $provider->getStreamFactory() + ); + + $response = $decoratedResponseFactory->createResponse(400); + $response->withJson($data, 200); + } + } +} diff --git a/tests/Decorators/ServerRequestDecoratorTest.php b/tests/Decorators/ServerRequestDecoratorTest.php new file mode 100644 index 0000000..6b2de83 --- /dev/null +++ b/tests/Decorators/ServerRequestDecoratorTest.php @@ -0,0 +1,1162 @@ +factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', '/'); + $request->foo = 'bar'; + + $this->assertFalse(property_exists($request, 'foo')); + } + } + + public function testAddsHostHeaderFromUri() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'http://example.com'); + $this->assertEquals('example.com', $request->getHeaderLine('Host')); + } + } + + public function testGetMethod() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', '/'); + $this->assertEquals('GET', $request->getMethod()); + } + } + + public function testWithMethod() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', '/'); + $request = $request->withMethod('POST'); + $this->assertEquals('POST', $request->getMethod()); + } + } + + public function testWithMethodCaseSensitive() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', '/'); + $request = $request->withMethod('pOsT'); + $this->assertEquals('pOsT', $request->getMethod()); + } + } + + public function testWithAllAllowedCharactersMethod() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', '/'); + $request = $request->withMethod("!#$%&'*+.^_`|~09AZ-"); + $this->assertEquals("!#$%&'*+.^_`|~09AZ-", $request->getMethod()); + } + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testWithMethodInvalid() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', '/'); + $request->withMethod('B@R'); + } + } + + public function testIsGet() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', '/'); + $this->assertEquals(true, $request->isGet()); + } + } + + public function testIsPost() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('POST', '/'); + $this->assertEquals(true, $request->isPost()); + } + } + + public function testIsPut() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('PUT', '/'); + $this->assertEquals(true, $request->isPut()); + } + } + + public function testIsPatch() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('PATCH', '/'); + $this->assertEquals(true, $request->isPatch()); + } + } + + public function testIsDelete() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('DELETE', '/'); + $this->assertEquals(true, $request->isDelete()); + } + } + + public function testIsHead() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('HEAD', '/'); + $this->assertEquals(true, $request->isHead()); + } + } + + public function testIsOptions() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('OPTIONS', '/'); + $this->assertEquals(true, $request->isOptions()); + } + } + + public function testIsXhr() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', '/'); + $request = $request + ->withHeader('Content-Type', 'application/x-www-form-urlencoded') + ->withHeader('X-Requested-With', 'XMLHttpRequest'); + + $this->assertEquals(true, $request->isXhr()); + } + } + + public function testGetRequestTarget() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com/foo/bar?abc=123'); + $this->assertEquals('/foo/bar?abc=123', $request->getRequestTarget()); + } + } + + public function testWithRequestTarget() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com/foo/bar?abc=123'); + $request = $request->withRequestTarget('/foo/bar?abc=123'); + + $this->assertEquals('/foo/bar?abc=123', $request->getRequestTarget()); + } + } + + public function testGetUri() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $this->assertEquals('https://google.com', $request->getUri()); + } + } + + public function testWithUri() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $uriFactory = $provider->getUriFactory(); + $uri = $uriFactory->createUri('https://example.com'); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withUri($uri); + + $this->assertEquals('https://example.com', $request->getUri()); + } + } + + public function testGetContentType() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withHeader('Content-Type', 'application/json'); + + $this->assertEquals('application/json', $request->getContentType()); + } + } + + public function testGetContentTypeEmpty() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $this->assertNull($request->getContentType()); + } + } + + public function testGetMediaType() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withHeader('Content-Type', 'application/json'); + + $this->assertEquals('application/json', $request->getMediaType()); + } + } + + public function testGetMediaTypeEmpty() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $this->assertNull($request->getMediaType()); + } + } + + public function testGetMediaTypeParams() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withHeader('Content-Type', 'application/json;charset=utf8;foo=bar'); + + $this->assertEquals(['charset' => 'utf8', 'foo' => 'bar'], $request->getMediaTypeParams()); + } + } + + public function testGetMediaTypeParamsEmpty() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withHeader('Content-Type', 'application/json'); + + $this->assertEquals([], $request->getMediaTypeParams()); + } + } + + public function testGetMediaTypeParamsWithoutHeader() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $this->assertEquals([], $request->getMediaTypeParams()); + } + } + + public function testGetContentCharset() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withHeader('Content-Type', 'application/json;charset=utf8'); + + $this->assertEquals('utf8', $request->getContentCharset()); + } + } + + public function testGetContentCharsetEmpty() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withHeader('Content-Type', 'application/json'); + + $this->assertNull($request->getContentCharset()); + } + } + + public function testGetContentCharsetWithoutHeader() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $this->assertNull($request->getContentCharset()); + } + } + + public function testGetContentLength() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withHeader('Content-Length', '150'); + + $this->assertEquals(150, $request->getContentLength()); + } + } + + public function testGetContentLengthWithoutHeader() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + + $this->assertNull($request->getContentLength()); + } + } + + public function testGetCookieParam() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withCookieParams(['user' => 'john']); + + $this->assertEquals('john', $request->getCookieParam('user')); + } + } + + public function testGetCookieParamWithDefault() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $this->assertEquals('john', $request->getCookieParam('user', 'john')); + } + } + + public function testGetCookieParams() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withCookieParams(['user' => 'john', 'password' => '123']); + + $this->assertEquals(['user' => 'john', 'password' => '123'], $request->getCookieParams()); + } + } + + public function testWithCookieParams() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withCookieParams(['user' => 'john']); + + $this->assertEquals(['user' => 'john'], $request->getCookieParams()); + } + } + + public function testGetQueryParams() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withQueryParams(['bar' => '123']); + + $this->assertEquals(['bar' => '123'], $request->getQueryParams()); + } + } + + public function testWithQueryParams() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withQueryParams(['bar' => '123']); + + $this->assertEquals(['bar' => '123'], $request->getQueryParams()); + } + } + + public function testWithQueryParamsEmpty() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withQueryParams(['bar' => '123']); + $clone = $request->withQueryParams([]); + + $this->assertEquals(['bar' => '123'], $request->getQueryParams()); + $this->assertEquals([], $clone->getQueryParams()); + } + } + + public function testWithUploadedFiles() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $streamFactory = $provider->getStreamFactory(); + $stream = $streamFactory->createStream('test'); + + $uploadedFileFactory = $provider->getUploadedFileFactory(); + $files = [$uploadedFileFactory->createUploadedFile($stream, null, 0, 'foo.txt')]; + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $clone = $request->withUploadedFiles($files); + + $this->assertEquals([], $request->getUploadedFiles()); + $this->assertEquals($files, $clone->getUploadedFiles()); + } + } + + public function testGetServerParam() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $serverParams = ['HTTP_AUTHORIZATION' => 'test']; + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com', $serverParams); + + $this->assertEquals('test', $request->getServerParam('HTTP_AUTHORIZATION')); + } + } + + public function testGetServerParamWithDefault() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $this->assertEquals('test', $request->getServerParam('HTTP_AUTHORIZATION', 'test')); + } + } + + public function testGetServerParams() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $serverParams = ['HTTP_AUTHORIZATION' => 'test']; + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com', $serverParams); + + $this->assertEquals($serverParams, $request->getServerParams()); + } + } + + public function testGetAttribute() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withAttribute('foo', 'bar'); + + $this->assertEquals('bar', $request->getAttribute('foo')); + } + } + + public function testGetAttributes() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request + ->withAttribute('foo', 'bar') + ->withAttribute('bar', 'baz'); + + $this->assertEquals(['foo' => 'bar', 'bar' => 'baz'], $request->getAttributes()); + } + } + + public function testWithAttribute() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withAttribute('foo', 'bar'); + $clone = $request->withAttribute('foo', 'baz'); + + $this->assertEquals('baz', $clone->getAttribute('foo')); + } + } + + public function testWithAttributes() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withAttributes(['foo' => 'bar', 'bar' => 'baz']); + + $this->assertEquals(['foo' => 'bar', 'bar' => 'baz'], $request->getAttributes()); + } + } + + public function testWithoutAttribute() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withAttributes(['foo' => 'bar', 'bar' => 'baz']); + $request = $request->withoutAttribute('bar'); + + $this->assertEquals(['foo' => 'bar'], $request->getAttributes()); + } + } + + public function testGetParsedBody() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('POST', 'https://google.com'); + $request = $request->withParsedBody(['foo' => 'bar']); + + $this->assertEquals(['foo' => 'bar'], $request->getParsedBody()); + } + } + + public function testGetParsedBodyNull() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('POST', 'https://google.com'); + $request = $request->withParsedBody([]); + $clone = $request->withParsedBody(null); + + $this->assertNull($clone->getParsedBody()); + } + } + + public function testGetParsedBodyForm() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $streamFactory = $provider->getStreamFactory(); + $stream = $streamFactory->createStream('foo=bar'); + + $request = $decoratedServerRequestFactory->createServerRequest('POST', 'https://google.com'); + $request = $request + ->withHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf8') + ->withBody($stream); + + $this->assertEquals(['foo' => 'bar'], $request->getParsedBody()); + } + } + + public function testGetParsedBodyJson() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $streamFactory = $provider->getStreamFactory(); + $stream = $streamFactory->createStream('{"foo":"bar"}'); + + $request = $decoratedServerRequestFactory->createServerRequest('POST', 'https://google.com'); + $request = $request + ->withHeader('Content-Type', 'application/json;charset=utf8') + ->withBody($stream); + + $this->assertEquals(['foo' => 'bar'], $request->getParsedBody()); + } + } + + public function testGetParsedBodyInvalidJson() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $streamFactory = $provider->getStreamFactory(); + $stream = $streamFactory->createStream('{"foo"}/bar'); + + $request = $decoratedServerRequestFactory->createServerRequest('POST', 'https://google.com'); + $request = $request + ->withHeader('Content-Type', 'application/json;charset=utf8') + ->withBody($stream); + + $this->assertNull($request->getParsedBody()); + } + } + + public function testGetParsedBodySemiValidJson() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $streamFactory = $provider->getStreamFactory(); + $stream = $streamFactory->createStream('"foo bar"'); + + $request = $decoratedServerRequestFactory->createServerRequest('POST', 'https://google.com'); + $request = $request + ->withHeader('Content-Type', 'application/json;charset=utf8') + ->withBody($stream); + + $this->assertNull($request->getParsedBody()); + } + } + + public function testGetParsedBodyWithJsonStructuredSuffix() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $streamFactory = $provider->getStreamFactory(); + $stream = $streamFactory->createStream('{"foo":"bar"}'); + + $request = $decoratedServerRequestFactory->createServerRequest('POST', 'https://google.com'); + $request = $request + ->withHeader('Content-Type', 'application/vnd.api+json;charset=utf8') + ->withBody($stream); + + $this->assertEquals(['foo' => 'bar'], $request->getParsedBody()); + } + } + + public function testGetParsedBodyXml() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $streamFactory = $provider->getStreamFactory(); + $stream = $streamFactory->createStream('John'); + + $request = $decoratedServerRequestFactory->createServerRequest('POST', 'https://google.com'); + $request = $request + ->withHeader('Content-Type', 'application/hal+xml;charset=utf8') + ->withBody($stream); + + /** @var \stdClass $obj */ + $obj = $request->getParsedBody(); + $this->assertEquals('John', $obj->name); + } + } + + public function testGetParsedBodyStructuredSuffixXml() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $streamFactory = $provider->getStreamFactory(); + $stream = $streamFactory->createStream('John'); + + $request = $decoratedServerRequestFactory->createServerRequest('POST', 'https://google.com'); + $request = $request + ->withHeader('Content-Type', 'application/xml;charset=utf8') + ->withBody($stream); + + /** @var \stdClass $obj */ + $obj = $request->getParsedBody(); + $this->assertEquals('John', $obj->name); + } + } + + public function testGetParsedBodyXmlWithTextXMLMediaType() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $streamFactory = $provider->getStreamFactory(); + $stream = $streamFactory->createStream('John'); + + $request = $decoratedServerRequestFactory->createServerRequest('POST', 'https://google.com'); + $request = $request + ->withHeader('Content-Type', 'text/xml') + ->withBody($stream); + + /** @var \stdClass $obj */ + $obj = $request->getParsedBody(); + $this->assertEquals('John', $obj->name); + } + } + + /** + * Will fail if a simple_xml warning is created + */ + public function testInvalidXmlIsQuietForTextXml() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $streamFactory = $provider->getStreamFactory(); + $stream = $streamFactory->createStream('JohncreateServerRequest('POST', 'https://google.com'); + $request = $request + ->withHeader('Content-Type', 'text/xml') + ->withBody($stream); + + $this->assertNull($request->getParsedBody()); + } + } + + /** + * Will fail if a simple_xml warning is created + */ + public function testInvalidXmlIsQuietForApplicationXml() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $streamFactory = $provider->getStreamFactory(); + $stream = $streamFactory->createStream('JohncreateServerRequest('POST', 'https://google.com'); + $request = $request + ->withHeader('Content-Type', 'application/xml') + ->withBody($stream); + + $this->assertNull($request->getParsedBody()); + } + } + + public function testGetParameterFromBody() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('POST', 'https://google.com'); + $request = $request->withParsedBody(['foo' => 'bar']); + $clone = $request->withParsedBody((object) ['foo' => 'bar']); + + $this->assertEquals('bar', $request->getParam('foo')); + $this->assertEquals('bar', $clone->getParam('foo')); + } + } + + /** + * @expectedException \RuntimeException + */ + public function testGetParsedBodyThrowsRuntimeExceptionWhenInvalidTypeReturned() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $streamFactory = $provider->getStreamFactory(); + $stream = $streamFactory->createStream('{"foo":"bar"}'); + + $request = $decoratedServerRequestFactory->createServerRequest('POST', 'https://google.com'); + $request = $request + ->withHeader('Content-Type', 'application/json;charset=utf8') + ->withBody($stream); + + $request->registerMediaTypeParser('application/json', function () { + return 10; + }); + + $request->getParsedBody(); + } + } + + public function testWithParsedBody() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('POST', 'https://google.com'); + $request = $request->withParsedBody([]); + + $this->assertEquals([], $request->getParsedBody()); + } + } + + public function testWithParsedBodyNull() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('POST', 'https://google.com'); + $request = $request->withParsedBody(null); + $this->assertNull($request->getParsedBody()); + } + } + + public function testGetParameterFromBodyWithHelper() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('POST', 'https://google.com'); + $request = $request->withParsedBody(['foo' => 'bar']); + $clone = $request->withParsedBody((object) ['foo' => 'bar']); + + $this->assertEquals('bar', $request->getParsedBodyParam('foo')); + $this->assertEquals('bar', $clone->getParsedBodyParam('foo')); + } + } + + public function testGetQueryParam() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com?foo=bar'); + $this->assertEquals('bar', $request->getQueryParam('foo')); + } + } + + public function testGetQueryParamWithGetParam() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com?foo=bar'); + $this->assertEquals('bar', $request->getParam('foo')); + } + } + + public function testGetParams() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com?foo=bar&bar=baz'); + $this->assertEquals(['foo' => 'bar', 'bar' => 'baz'], $request->getParams()); + } + } + + public function testGetParamsWithBodyPriority() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $streamFactory = $provider->getStreamFactory(); + $stream = $streamFactory->createStream('foo=bar&bar=baz'); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com?foo=baz&bar=foo'); + $request = $request + ->withHeader('Content-Type', 'application/x-www-form-urlencoded') + ->withBody($stream); + + $this->assertEquals(['foo' => 'bar', 'bar' => 'baz'], $request->getParams()); + } + } + + public function testGetParamFromBodyOverQuery() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $streamFactory = $provider->getStreamFactory(); + $stream = $streamFactory->createStream('foo=bar'); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com?foo=baz'); + $request = $request + ->withHeader('Content-Type', 'application/x-www-form-urlencoded') + ->withBody($stream); + + $this->assertEquals('bar', $request->getParam('foo')); + } + } + + public function testGetParamWithDefaultFromBodyOverQuery() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $streamFactory = $provider->getStreamFactory(); + $stream = $streamFactory->createStream('foo=bar'); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com?foo=baz'); + $request = $request + ->withHeader('Content-Type', 'application/x-www-form-urlencoded') + ->withBody($stream); + + $this->assertEquals('baz', $request->getParam('bar', 'baz')); + } + } + + public function testGetProtocolVersion() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withProtocolVersion('1.0'); + + $this->assertEquals('1.0', $request->getProtocolVersion()); + } + } + + public function testWithProtocolVersion() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withProtocolVersion('1.0'); + $clone = $request->withProtocolVersion('1.1'); + + $this->assertEquals('1.0', $request->getProtocolVersion()); + $this->assertEquals('1.1', $clone->getProtocolVersion()); + } + } + + public function testGetHeaders() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withHeader('Content-Type', 'application/json'); + + $expectedHeaders = ['Content-Type' => ['application/json'], 'Host' => ['google.com']]; + $this->assertEquals($expectedHeaders, $request->getHeaders()); + } + } + + public function testHasHeaders() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request->withHeader('Content-Type', 'application/json'); + + $this->assertEquals(true, $request->hasHeader('Content-Type')); + } + } + + public function testWithAddedHeader() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request + ->withHeader('Content-Type', 'application/json') + ->withAddedHeader('Content-Type', 'application/xml'); + + $this->assertEquals(['application/json', 'application/xml'], $request->getHeader('Content-Type')); + } + } + + public function testWithoutHeader() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedServerRequestFactory = new DecoratedServerRequestFactory($provider->getServerRequestFactory()); + + $request = $decoratedServerRequestFactory->createServerRequest('GET', 'https://google.com'); + $request = $request + ->withHeader('Content-Length', '150') + ->withHeader('Content-Type', 'application/json'); + + $this->assertEquals(true, $request->hasHeader('Content-Length')); + + $request = $request->withoutHeader('Content-Length'); + $this->assertEquals(false, $request->hasHeader('Content-Length')); + } + } +} diff --git a/tests/Decorators/UriDecoratorTest.php b/tests/Decorators/UriDecoratorTest.php new file mode 100644 index 0000000..00fb59e --- /dev/null +++ b/tests/Decorators/UriDecoratorTest.php @@ -0,0 +1,448 @@ +factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com'); + $uri->foo = 'bar'; + + $this->assertFalse(property_exists($uri, 'foo')); + } + } + + public function testGetScheme() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com'); + $this->assertEquals('https', $uri->getScheme()); + } + } + + public function testWithScheme() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com'); + $uri = $uri->withScheme('http'); + + $this->assertEquals('http', $uri->getScheme()); + } + } + + public function testWithSchemeEmpty() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com'); + $uri = $uri->withScheme(''); + + $this->assertEquals('', $uri->getScheme()); + } + } + + public function testGetAuthorityWithUsernameAndPassword() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com'); + $uri = $uri->withUserInfo('user', 'password'); + + $this->assertEquals('user:password@google.com', $uri->getAuthority()); + } + } + + public function testGetAuthorityWithUsername() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://user@google.com'); + $uri = $uri->withUserInfo('user'); + + $this->assertEquals('user@google.com', $uri->getAuthority()); + } + } + + public function testGetAuthority() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://user@google.com'); + $this->assertEquals('user@google.com', $uri->getAuthority()); + } + } + + public function testGetAuthorityWithNonStandardPort() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com:400'); + $this->assertEquals('google.com:400', $uri->getAuthority()); + } + } + + public function testGetUserInfoWithUsernameAndPassword() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com'); + $uri = $uri->withUserInfo('user', 'password'); + + $this->assertEquals('user:password', $uri->getUserInfo()); + } + } + + public function testGetUserInfoWithUsernameAndPasswordEncodesCorrectly() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://user%40pass%3Aword@google.com'); + $this->assertEquals('user%40pass%3Aword', $uri->getUserInfo()); + } + } + + public function testGetUserInfoWithUsername() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://user@google.com'); + $this->assertEquals('user', $uri->getUserInfo()); + } + } + + public function testGetUserInfoNone() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com'); + $this->assertEquals('', $uri->getUserInfo()); + } + } + + public function testGetHost() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com'); + $this->assertEquals('google.com', $uri->getHost()); + } + } + + public function testWithHost() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com'); + $uri = $uri->withHost('microsoft.com'); + + $this->assertEquals('microsoft.com', $uri->getHost()); + } + } + + public function testWithPort() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com'); + $uri = $uri->withPort(400); + + $this->assertEquals(400, $uri->getPort()); + } + } + + public function testWithPortNull() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com'); + $uri = $uri->withPort(null); + + $this->assertEquals(null, $uri->getPort()); + } + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testWithPortInvalidInt() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com'); + $uri->withPort(70000); + } + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testWithPortInvalidString() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com'); + $uri->withPort('invalid'); + } + } + + public function testGetPath() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com/path'); + $this->assertEquals('/path', $uri->getPath()); + } + } + + public function testWithPath() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com/path'); + $uri = $uri->withPath('/newPath'); + + $this->assertEquals('/newPath', $uri->getPath()); + } + } + + public function testWithPathWithoutPrefix() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com/path'); + $uri = $uri->withPath('newPath'); + + $this->assertEquals('newPath', $uri->getPath()); + } + } + + public function testWithPathEmptyValue() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com/path'); + $uri = $uri->withPath(''); + + $this->assertEquals('', $uri->getPath()); + } + } + + public function testGetQuery() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com/path?foo=bar'); + $this->assertEquals('foo=bar', $uri->getQuery()); + } + } + + public function testWithQuery() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com/path?foo=bar'); + $uri = $uri->withQuery('bar=baz'); + + $this->assertEquals('bar=baz', $uri->getQuery()); + } + } + + public function testWithQueryEmpty() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com/path?foo=bar'); + $uri = $uri->withQuery(''); + + $this->assertEquals('', $uri->getQuery()); + } + } + + public function testGetFragment() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com/#fragment'); + $this->assertEquals('fragment', $uri->getFragment()); + } + } + + public function testWithFragment() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com/#fragment'); + $uri = $uri->withFragment('new-fragment'); + + $this->assertEquals('new-fragment', $uri->getFragment()); + } + } + + public function testWithFragmentEmpty() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com/#fragment'); + $uri = $uri->withFragment(''); + + $this->assertEquals('', $uri->getFragment()); + } + } + + public function testToString() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://user@google.com/path/otherpath?foo=bar#fragment'); + $this->assertEquals('https://user@google.com/path/otherpath?foo=bar#fragment', (string) $uri); + + $uri = $uri->withPath('new-path'); + $this->assertEquals('https://user@google.com/new-path?foo=bar#fragment', (string) $uri); + + $uri = $uri->withPath('/other-path'); + $this->assertEquals('https://user@google.com/other-path?foo=bar#fragment', (string) $uri); + } + } + + public function testGetBaseUrl() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com/path/otherpath?foo=bar#fragment'); + $this->assertEquals('https://google.com', $uri->getBaseUrl()); + } + } + + public function testGetBaseUrlWithNoBasePath() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://google.com/index.php'); + $this->assertEquals('https://google.com', $uri->getBaseUrl()); + } + } + + public function testGetBaseUrlWithAuthority() + { + foreach ($this->factoryProviders as $factoryProvider) { + /** @var Psr17FactoryProvider $provider */ + $provider = new $factoryProvider; + $decoratedUriFactory = new DecoratedUriFactory($provider->getUriFactory()); + + $uri = $decoratedUriFactory->createUri('https://user:password@google.com/path/otherpath?foo=bar#fragment'); + $this->assertEquals('https://user:password@google.com', $uri->getBaseUrl()); + } + } +} diff --git a/tests/EnvironmentTest.php b/tests/EnvironmentTest.php deleted file mode 100644 index 1d27886..0000000 --- a/tests/EnvironmentTest.php +++ /dev/null @@ -1,58 +0,0 @@ - '/foo/bar/index.php', - 'REQUEST_URI' => '/foo/bar?abc=123', - ]); - - $this->assertEquals('/foo/bar/index.php', $env['SCRIPT_NAME']); - $this->assertEquals('/foo/bar?abc=123', $env['REQUEST_URI']); - $this->assertEquals('localhost', $env['HTTP_HOST']); - } - - /** - * Test environment from mock data with HTTPS - */ - public function testMockHttps() - { - $env = Environment::mock([ - 'HTTPS' => 'on' - ]); - - $this->assertInternalType('array', $env); - $this->assertEquals('on', $env['HTTPS']); - $this->assertEquals(443, $env['SERVER_PORT']); - } - - /** - * Test environment from mock data with REQUEST_SCHEME - */ - public function testMockRequestScheme() - { - $env = Environment::mock([ - 'REQUEST_SCHEME' => 'https' - ]); - - $this->assertInternalType('array', $env); - $this->assertEquals('https', $env['REQUEST_SCHEME']); - $this->assertEquals(443, $env['SERVER_PORT']); - } -} diff --git a/tests/HeadersTest.php b/tests/HeadersTest.php deleted file mode 100644 index c21771d..0000000 --- a/tests/HeadersTest.php +++ /dev/null @@ -1,251 +0,0 @@ - 'application/json', - ]); - $h = Headers::createFromGlobals($e); - $prop = new ReflectionProperty($h, 'data'); - $prop->setAccessible(true); - - $this->assertTrue(is_array($prop->getValue($h)['accept'])); - $this->assertEquals('application/json', $prop->getValue($h)['accept']['value'][0]); - $this->assertEquals('Accept', $prop->getValue($h)['accept']['originalKey']); - } - - public function testCreateFromGlobalsWithSpecialHeaders() - { - $e = Environment::mock([ - 'CONTENT_TYPE' => 'application/json', - ]); - $h = Headers::createFromGlobals($e); - $prop = new ReflectionProperty($h, 'data'); - $prop->setAccessible(true); - - $this->assertTrue(is_array($prop->getValue($h)['content-type'])); - $this->assertEquals('application/json', $prop->getValue($h)['content-type']['value'][0]); - $this->assertEquals('Content-Type', $prop->getValue($h)['content-type']['originalKey']); - } - - public function testCreateFromGlobalsIgnoresHeaders() - { - $e = Environment::mock([ - 'CONTENT_TYPE' => 'text/csv', - 'HTTP_CONTENT_LENGTH' => 1230, // <-- Ignored - ]); - $h = Headers::createFromGlobals($e); - $prop = new ReflectionProperty($h, 'data'); - $prop->setAccessible(true); - - $this->assertNotContains('content-length', $prop->getValue($h)); - $this->assertEquals('Content-Type', $prop->getValue($h)['content-type']['originalKey']); - } - - public function testConstructor() - { - $h = new Headers([ - 'Content-Length' => 100, - ]); - $prop = new ReflectionProperty($h, 'data'); - $prop->setAccessible(true); - - $this->assertTrue(is_array($prop->getValue($h)['content-length'])); - $this->assertEquals(100, $prop->getValue($h)['content-length']['value'][0]); - } - - public function testSetSingleValue() - { - $h = new Headers(); - $h->set('Content-Length', 100); - $prop = new ReflectionProperty($h, 'data'); - $prop->setAccessible(true); - - $this->assertTrue(is_array($prop->getValue($h)['content-length'])); - $this->assertEquals(100, $prop->getValue($h)['content-length']['value'][0]); - } - - public function testSetArrayValue() - { - $h = new Headers(); - $h->set('Allow', ['GET', 'POST']); - $prop = new ReflectionProperty($h, 'data'); - $prop->setAccessible(true); - - $this->assertTrue(is_array($prop->getValue($h)['allow'])); - $this->assertEquals(['GET', 'POST'], $prop->getValue($h)['allow']['value']); - } - - public function testGet() - { - $h = new Headers(); - $prop = new ReflectionProperty($h, 'data'); - $prop->setAccessible(true); - $prop->setValue($h, [ - 'allow' => [ - 'value' => ['GET', 'POST'], - 'originalKey' => 'Allow' - ] - ]); - - $this->assertEquals(['GET', 'POST'], $h->get('Allow')); - } - - public function testGetOriginalKey() - { - $h = new Headers(); - $h->set('http-test_key', 'testValue'); - $h->get('test-key'); - - $value = $h->get('test-key'); - - $this->assertEquals('testValue', reset($value)); - $this->assertEquals('http-test_key', $h->getOriginalKey('test-key')); - $this->assertNull($h->getOriginalKey('test-non-existing')); - } - - public function testGetNotExists() - { - $h = new Headers(); - - $this->assertNull($h->get('Foo')); - } - - public function testAddNewValue() - { - $h = new Headers(); - $h->add('Foo', 'Bar'); - $prop = new ReflectionProperty($h, 'data'); - $prop->setAccessible(true); - - $this->assertTrue(is_array($prop->getValue($h)['foo'])); - $this->assertEquals(['Bar'], $prop->getValue($h)['foo']['value']); - } - - public function testAddAnotherValue() - { - $h = new Headers(); - $h->add('Foo', 'Bar'); - $h->add('Foo', 'Xyz'); - $prop = new ReflectionProperty($h, 'data'); - $prop->setAccessible(true); - - $this->assertTrue(is_array($prop->getValue($h)['foo'])); - $this->assertEquals(['Bar', 'Xyz'], $prop->getValue($h)['foo']['value']); - } - - public function testAddArrayValue() - { - $h = new Headers(); - $h->add('Foo', 'Bar'); - $h->add('Foo', ['Xyz', '123']); - $prop = new ReflectionProperty($h, 'data'); - $prop->setAccessible(true); - - $this->assertTrue(is_array($prop->getValue($h)['foo'])); - $this->assertEquals(['Bar', 'Xyz', '123'], $prop->getValue($h)['foo']['value']); - } - - public function testHas() - { - $h = new Headers(); - $prop = new ReflectionProperty($h, 'data'); - $prop->setAccessible(true); - $prop->setValue($h, [ - 'allow' => [ - 'value' => ['GET', 'POST'], - 'originalKey' => 'Allow' - ] - ]); - $this->assertTrue($h->has('allow')); - $this->assertFalse($h->has('foo')); - } - - public function testRemove() - { - $h = new Headers(); - $prop = new ReflectionProperty($h, 'data'); - $prop->setAccessible(true); - $prop->setValue($h, [ - 'Allow' => [ - 'value' => ['GET', 'POST'], - 'originalKey' => 'Allow' - ] - ]); - $h->remove('Allow'); - - $this->assertNotContains('Allow', $prop->getValue($h)); - } - - public function testOriginalKeys() - { - $h = new Headers(); - $prop = new ReflectionProperty($h, 'data'); - $prop->setAccessible(true); - $prop->setValue($h, [ - 'Allow' => [ - 'value' => ['GET', 'POST'], - 'originalKey' => 'ALLOW' - ] - ]); - $all = $h->all(); - - $this->assertArrayHasKey('ALLOW', $all); - } - - public function testNormalizeKey() - { - $h = new Headers(); - $this->assertEquals('foo-bar', $h->normalizeKey('HTTP_FOO_BAR')); - $this->assertEquals('foo-bar', $h->normalizeKey('HTTP-FOO-BAR')); - $this->assertEquals('foo-bar', $h->normalizeKey('Http-Foo-Bar')); - $this->assertEquals('foo-bar', $h->normalizeKey('Http_Foo_Bar')); - $this->assertEquals('foo-bar', $h->normalizeKey('http_foo_bar')); - $this->assertEquals('foo-bar', $h->normalizeKey('http-foo-bar')); - } - - public function testDetermineAuthorization() - { - $e = Environment::mock([]); - $en = Headers::determineAuthorization($e); - $h = Headers::createFromGlobals($e); - - $this->assertEquals('electrolytes', $en['HTTP_AUTHORIZATION']); - $this->assertEquals(['electrolytes'], $h['Authorization']); - } - - public function testDetermineAuthorizationHonoursHttpAuthorizationKey() - { - $e = Environment::mock(['HTTP_AUTHORIZATION' => 'foo']); - $en = Headers::determineAuthorization($e); - $h = Headers::createFromGlobals($e); - - $this->assertEquals('foo', $en['HTTP_AUTHORIZATION']); - $this->assertEquals(['foo'], $h['Authorization']); - } - - public function testDetermineAuthorizationWhenEmpty() - { - $e = Environment::mock(['HTTP_AUTHORIZATION' => '']); - $en = Headers::determineAuthorization($e); - $h = Headers::createFromGlobals($e); - - $this->assertEquals('electrolytes', $en['HTTP_AUTHORIZATION']); - $this->assertEquals(['electrolytes'], $h['Authorization']); - } -} diff --git a/tests/Integration/BaseTestFactories.php b/tests/Integration/BaseTestFactories.php deleted file mode 100644 index da6cb85..0000000 --- a/tests/Integration/BaseTestFactories.php +++ /dev/null @@ -1,51 +0,0 @@ -buildUri('/'), new Headers(), [], $_SERVER, $this->buildStream('')); - } -} diff --git a/tests/Integration/StreamTest.php b/tests/Integration/StreamTest.php deleted file mode 100644 index 0de6f4c..0000000 --- a/tests/Integration/StreamTest.php +++ /dev/null @@ -1,38 +0,0 @@ -protocolVersion = '1.0'; - - $this->assertEquals('1.0', $message->getProtocolVersion()); - } - - /** - * @covers Slim\Http\Message::withProtocolVersion - */ - public function testWithProtocolVersion() - { - $message = new MessageStub(); - $clone = $message->withProtocolVersion('1.0'); - - $this->assertEquals('1.0', $clone->protocolVersion); - } - - /** - * @covers Slim\Http\Message::withProtocolVersion - * @expectedException \InvalidArgumentException - */ - public function testWithProtocolVersionInvalidThrowsException() - { - $message = new MessageStub(); - $message->withProtocolVersion('3.0'); - } - - /******************************************************************************* - * Headers - ******************************************************************************/ - - /** - * @covers Slim\Http\Message::getHeaders - */ - public function testGetHeaders() - { - $headers = new Headers(); - $headers->add('X-Foo', 'one'); - $headers->add('X-Foo', 'two'); - $headers->add('X-Foo', 'three'); - - $message = new MessageStub(); - $message->headers = $headers; - - $shouldBe = [ - 'X-Foo' => [ - 'one', - 'two', - 'three', - ], - ]; - - $this->assertEquals($shouldBe, $message->getHeaders()); - } - - /** - * @covers Slim\Http\Message::hasHeader - */ - public function testHasHeader() - { - $headers = new Headers(); - $headers->add('X-Foo', 'one'); - - $message = new MessageStub(); - $message->headers = $headers; - - $this->assertTrue($message->hasHeader('X-Foo')); - $this->assertFalse($message->hasHeader('X-Bar')); - } - - /** - * @covers Slim\Http\Message::getHeaderLine - */ - public function testGetHeaderLine() - { - $headers = new Headers(); - $headers->add('X-Foo', 'one'); - $headers->add('X-Foo', 'two'); - $headers->add('X-Foo', 'three'); - - $message = new MessageStub(); - $message->headers = $headers; - - $this->assertEquals('one,two,three', $message->getHeaderLine('X-Foo')); - $this->assertEquals('', $message->getHeaderLine('X-Bar')); - } - - /** - * @covers Slim\Http\Message::getHeader - */ - public function testGetHeader() - { - $headers = new Headers(); - $headers->add('X-Foo', 'one'); - $headers->add('X-Foo', 'two'); - $headers->add('X-Foo', 'three'); - - $message = new MessageStub(); - $message->headers = $headers; - - $this->assertEquals(['one', 'two', 'three'], $message->getHeader('X-Foo')); - $this->assertEquals([], $message->getHeader('X-Bar')); - } - - /** - * @covers Slim\Http\Message::withHeader - */ - public function testWithHeader() - { - $headers = new Headers(); - $headers->add('X-Foo', 'one'); - $message = new MessageStub(); - $message->headers = $headers; - $clone = $message->withHeader('X-Foo', 'bar'); - - $this->assertEquals('bar', $clone->getHeaderLine('X-Foo')); - } - - /** - * @covers Slim\Http\Message::withAddedHeader - */ - public function testWithAddedHeader() - { - $headers = new Headers(); - $headers->add('X-Foo', 'one'); - $message = new MessageStub(); - $message->headers = $headers; - $clone = $message->withAddedHeader('X-Foo', 'two'); - - $this->assertEquals('one,two', $clone->getHeaderLine('X-Foo')); - } - - /** - * @covers Slim\Http\Message::withoutHeader - */ - public function testWithoutHeader() - { - $headers = new Headers(); - $headers->add('X-Foo', 'one'); - $headers->add('X-Bar', 'two'); - $response = new MessageStub(); - $response->headers = $headers; - $clone = $response->withoutHeader('X-Foo'); - $shouldBe = [ - 'X-Bar' => ['two'], - ]; - - $this->assertEquals($shouldBe, $clone->getHeaders()); - } - - /******************************************************************************* - * Body - ******************************************************************************/ - - /** - * @covers Slim\Http\Message::getBody - */ - public function testGetBody() - { - $body = $this->getBody(); - $message = new MessageStub(); - $message->body = $body; - - $this->assertSame($body, $message->getBody()); - } - - /** - * @covers Slim\Http\Message::withBody - */ - public function testWithBody() - { - $body = $this->getBody(); - $body2 = $this->getBody(); - $message = new MessageStub(); - $message->body = $body; - $clone = $message->withBody($body2); - - $this->assertSame($body, $message->body); - $this->assertSame($body2, $clone->body); - } - - /** - * @return \PHPUnit_Framework_MockObject_MockObject|\Slim\Http\Body - */ - protected function getBody() - { - return $this->getMockBuilder('Slim\Http\Body')->disableOriginalConstructor()->getMock(); - } -} diff --git a/tests/Mocks/MessageStub.php b/tests/Mocks/MessageStub.php deleted file mode 100644 index 61a8087..0000000 --- a/tests/Mocks/MessageStub.php +++ /dev/null @@ -1,38 +0,0 @@ -responseFactory = new Psr17Factory(); + $this->serverRequestFactory = new Psr17Factory(); + $this->streamFactory = new Psr17Factory(); + $this->uploadedFileFactory = new Psr17Factory(); + $this->uriFactory = new Psr17Factory(); + } +} diff --git a/tests/Providers/Psr17FactoryProvider.php b/tests/Providers/Psr17FactoryProvider.php new file mode 100644 index 0000000..fd47e84 --- /dev/null +++ b/tests/Providers/Psr17FactoryProvider.php @@ -0,0 +1,87 @@ +responseFactory; + } + + /** + * @return ServerRequestFactoryInterface + */ + public function getServerRequestFactory(): ServerRequestFactoryInterface + { + return $this->serverRequestFactory; + } + + /** + * @return StreamFactoryInterface + */ + public function getStreamFactory(): StreamFactoryInterface + { + return $this->streamFactory; + } + + /** + * @return UploadedFileFactoryInterface + */ + public function getUploadedFileFactory(): UploadedFileFactoryInterface + { + return $this->uploadedFileFactory; + } + + /** + * @return UriFactoryInterface + */ + public function getUriFactory(): UriFactoryInterface + { + return $this->uriFactory; + } +} diff --git a/tests/Providers/ZendDiactorosPsr17FactoryProvider.php b/tests/Providers/ZendDiactorosPsr17FactoryProvider.php new file mode 100644 index 0000000..93baec1 --- /dev/null +++ b/tests/Providers/ZendDiactorosPsr17FactoryProvider.php @@ -0,0 +1,34 @@ +responseFactory = new ResponseFactory(); + $this->serverRequestFactory = new ServerRequestFactory(); + $this->streamFactory = new StreamFactory(); + $this->uploadedFileFactory = new UploadedFileFactory(); + $this->uriFactory = new UriFactory(); + } +} diff --git a/tests/Psr7Integration/Nyholm/ResponseTest.php b/tests/Psr7Integration/Nyholm/ResponseTest.php new file mode 100644 index 0000000..6dc9665 --- /dev/null +++ b/tests/Psr7Integration/Nyholm/ResponseTest.php @@ -0,0 +1,34 @@ +getResponseFactory(), + $provider->getStreamFactory() + ); + + return $decoratedResponseFactory->createResponse(); + } +} diff --git a/tests/Psr7Integration/Nyholm/ServerRequestTest.php b/tests/Psr7Integration/Nyholm/ServerRequestTest.php new file mode 100644 index 0000000..9a2089d --- /dev/null +++ b/tests/Psr7Integration/Nyholm/ServerRequestTest.php @@ -0,0 +1,31 @@ +getServerRequestFactory()); + + return $decoratedServerRequestFactory->createServerRequest('GET', 'http://foo.com', $_SERVER); + } +} diff --git a/tests/Psr7Integration/Nyholm/UriTest.php b/tests/Psr7Integration/Nyholm/UriTest.php new file mode 100644 index 0000000..b0a4cc2 --- /dev/null +++ b/tests/Psr7Integration/Nyholm/UriTest.php @@ -0,0 +1,31 @@ +getUriFactory()); + + return $decoratedUriFactory->createUri($uri); + } +} diff --git a/tests/Psr7Integration/Zend/ResponseTest.php b/tests/Psr7Integration/Zend/ResponseTest.php new file mode 100644 index 0000000..265446d --- /dev/null +++ b/tests/Psr7Integration/Zend/ResponseTest.php @@ -0,0 +1,34 @@ +getResponseFactory(), + $provider->getStreamFactory() + ); + + return $decoratedResponseFactory->createResponse(); + } +} diff --git a/tests/Psr7Integration/Zend/ServerRequestTest.php b/tests/Psr7Integration/Zend/ServerRequestTest.php new file mode 100644 index 0000000..0027529 --- /dev/null +++ b/tests/Psr7Integration/Zend/ServerRequestTest.php @@ -0,0 +1,31 @@ +getServerRequestFactory()); + + return $decoratedServerRequestFactory->createServerRequest('GET', 'http://foo.com', $_SERVER); + } +} diff --git a/tests/Psr7Integration/Zend/UriTest.php b/tests/Psr7Integration/Zend/UriTest.php new file mode 100644 index 0000000..f5fa18b --- /dev/null +++ b/tests/Psr7Integration/Zend/UriTest.php @@ -0,0 +1,31 @@ +getUriFactory()); + + return $decoratedUriFactory->createUri($uri); + } +} diff --git a/tests/RequestBodyTest.php b/tests/RequestBodyTest.php deleted file mode 100644 index 83dc0ae..0000000 --- a/tests/RequestBodyTest.php +++ /dev/null @@ -1,334 +0,0 @@ -body = new RequestBody(); - $this->body->write($this->text); - $this->body->rewind(); - } - - protected function tearDown() - { - if (is_resource($this->stream) === true) { - fclose($this->stream); - } - $this->body = null; - } - - /** - * This method creates a new resource, and it seeds - * the resource with lorem ipsum text. The returned - * resource is readable, writable, and seekable. - * - * @param string $mode - * - * @return resource - */ - public function resourceFactory($mode = 'r+') - { - $stream = fopen('php://temp', $mode); - fwrite($stream, $this->text); - rewind($stream); - - return $stream; - } - - public function testConstructorAttachesStream() - { - $bodyStream = new ReflectionProperty($this->body, 'stream'); - $bodyStream->setAccessible(true); - - $this->assertInternalType('resource', $bodyStream->getValue($this->body)); - } - - public function testConstructorSetsMetadata() - { - $bodyMetadata = new ReflectionProperty($this->body, 'meta'); - $bodyMetadata->setAccessible(true); - - $this->assertTrue(is_array($bodyMetadata->getValue($this->body))); - } - - public function testGetMetadata() - { - $this->assertTrue(is_array($this->body->getMetadata())); - } - - public function testGetMetadataKey() - { - $this->assertEquals('php://temp', $this->body->getMetadata('uri')); - } - - public function testGetMetadataKeyNotFound() - { - $this->assertNull($this->body->getMetadata('foo')); - } - - public function testDetach() - { - $bodyStream = new ReflectionProperty($this->body, 'stream'); - $bodyStream->setAccessible(true); - - $bodyMetadata = new ReflectionProperty($this->body, 'meta'); - $bodyMetadata->setAccessible(true); - - $bodyReadable = new ReflectionProperty($this->body, 'readable'); - $bodyReadable->setAccessible(true); - - $bodyWritable = new ReflectionProperty($this->body, 'writable'); - $bodyWritable->setAccessible(true); - - $bodySeekable = new ReflectionProperty($this->body, 'seekable'); - $bodySeekable->setAccessible(true); - - $result = $this->body->detach(); - - $this->assertInternalType('resource', $result); - $this->assertNull($bodyStream->getValue($this->body)); - $this->assertNull($bodyMetadata->getValue($this->body)); - $this->assertNull($bodyReadable->getValue($this->body)); - $this->assertNull($bodyWritable->getValue($this->body)); - $this->assertNull($bodySeekable->getValue($this->body)); - } - - public function testToStringAttached() - { - $this->assertEquals($this->text, (string)$this->body); - } - - public function testToStringAttachedRewindsFirst() - { - $this->assertEquals($this->text, (string)$this->body); - $this->assertEquals($this->text, (string)$this->body); - $this->assertEquals($this->text, (string)$this->body); - } - - public function testToStringDetached() - { - $bodyStream = new ReflectionProperty($this->body, 'stream'); - $bodyStream->setAccessible(true); - $bodyStream->setValue($this->body, null); - - $this->assertEquals('', (string)$this->body); - } - - /** - * @expectedException \RuntimeException - */ - public function testClose() - { - $this->body->close(); - - $this->assertAttributeEquals(null, 'stream', $this->body); - $this->assertFalse($this->body->isReadable()); - $this->assertFalse($this->body->isWritable()); - $this->assertEquals('', (string)$this->body); - - $this->body->tell(); - } - - public function testGetSizeAttached() - { - $this->assertEquals(mb_strlen($this->text), $this->body->getSize()); - } - - public function testGetSizeDetached() - { - $bodyStream = new ReflectionProperty($this->body, 'stream'); - $bodyStream->setAccessible(true); - $bodyStream->setValue($this->body, null); - - $this->assertNull($this->body->getSize()); - } - - public function testTellAttached() - { - $this->body->seek(10); - - $this->assertEquals(10, $this->body->tell()); - } - - /** - * @expectedException \RuntimeException - */ - public function testTellDetachedThrowsRuntimeException() - { - $bodyStream = new ReflectionProperty($this->body, 'stream'); - $bodyStream->setAccessible(true); - $bodyStream->setValue($this->body, null); - - $this->body->tell(); - } - - public function testEofAttachedFalse() - { - $this->body->seek(10); - - $this->assertFalse($this->body->eof()); - } - - public function testEofAttachedTrue() - { - while ($this->body->eof() === false) { - $this->body->read(1024); - } - - $this->assertTrue($this->body->eof()); - } - - public function testEofDetached() - { - $bodyStream = new ReflectionProperty($this->body, 'stream'); - $bodyStream->setAccessible(true); - $bodyStream->setValue($this->body, null); - - $this->assertTrue($this->body->eof()); - } - - public function testIsReadableAttachedTrue() - { - $this->assertTrue($this->body->isReadable()); - } - - public function testIsReadableDetached() - { - $this->body->detach(); - - $this->assertFalse($this->body->isReadable()); - } - - public function testIsWritableAttachedTrue() - { - $this->assertTrue($this->body->isWritable()); - } - - public function testIsWritableDetached() - { - $this->body->detach(); - - $this->assertFalse($this->body->isWritable()); - } - - public function isSeekableAttachedTrue() - { - $this->assertTrue($this->body->isSeekable()); - } - - // TODO: Is seekable is false when attached... how? - - public function testIsSeekableDetached() - { - $this->body->detach(); - - $this->assertFalse($this->body->isSeekable()); - } - - public function testSeekAttached() - { - $this->body->seek(10); - - $this->assertEquals(10, $this->body->tell()); - } - - /** - * @expectedException \RuntimeException - */ - public function testSeekDetachedThrowsRuntimeException() - { - $this->body->detach(); - $this->body->seek(10); - } - - public function testRewindAttached() - { - $this->body->seek(10); - $this->body->rewind(); - - $this->assertEquals(0, $this->body->tell()); - } - - /** - * @expectedException \RuntimeException - */ - public function testRewindDetachedThrowsRuntimeException() - { - $this->body->detach(); - $this->body->rewind(); - } - - public function testReadAttached() - { - $this->assertEquals(substr($this->text, 0, 10), $this->body->read(10)); - } - - /** - * @expectedException \RuntimeException - */ - public function testReadDetachedThrowsRuntimeException() - { - $this->body->detach(); - $this->body->read(10); - } - - public function testWriteAttached() - { - while ($this->body->eof() === false) { - $this->body->read(1024); - } - $this->body->write('foo'); - - $this->assertEquals($this->text . 'foo', (string)$this->body); - } - - /** - * @expectedException \RuntimeException - */ - public function testWriteDetachedThrowsRuntimeException() - { - $this->body->detach(); - $this->body->write('foo'); - } - - public function testGetContentsAttached() - { - $this->body->seek(10); - - $this->assertEquals(substr($this->text, 10), $this->body->getContents()); - } - - /** - * @expectedException \RuntimeException - */ - public function testGetContentsDetachedThrowsRuntimeException() - { - $this->body->detach(); - $this->body->getContents(); - } -} diff --git a/tests/RequestTest.php b/tests/RequestTest.php deleted file mode 100644 index 75dcfcb..0000000 --- a/tests/RequestTest.php +++ /dev/null @@ -1,1094 +0,0 @@ - 'john', - 'id' => '123', - ]; - $serverParams = $env; - $body = new RequestBody(); - $uploadedFiles = UploadedFile::createFromGlobals($env); - $request = new Request('GET', $uri, $headers, $cookies, $serverParams, $body, $uploadedFiles); - - return $request; - } - - public function testDisableSetter() - { - $request = $this->requestFactory(); - $request->foo = 'bar'; - - $this->assertFalse(property_exists($request, 'foo')); - } - - public function testAddsHostHeaderFromUri() - { - $request = $this->requestFactory(); - $this->assertEquals('example.com', $request->getHeaderLine('Host')); - } - - /******************************************************************************* - * Method - ******************************************************************************/ - - public function testGetMethod() - { - $this->assertEquals('GET', $this->requestFactory()->getMethod()); - } - - public function testWithMethod() - { - $request = $this->requestFactory()->withMethod('PUT'); - - $this->assertAttributeEquals('PUT', 'method', $request); - } - - public function testWithMethodCaseSensitive() - { - $request = $this->requestFactory()->withMethod('pOsT'); - - $this->assertAttributeEquals('pOsT', 'method', $request); - } - - public function testWithAllAllowedCharactersMethod() - { - $request = $this->requestFactory()->withMethod("!#$%&'*+.^_`|~09AZ-"); - - $this->assertAttributeEquals("!#$%&'*+.^_`|~09AZ-", 'method', $request); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testWithMethodInvalid() - { - $this->requestFactory()->withMethod('B@R'); - } - - public function testWithMethodNull() - { - $request = $this->requestFactory()->withMethod(null); - - $this->assertAttributeEquals(null, 'method', $request); - } - - /** - * @covers Slim\Http\Request::createFromGlobals - */ - public function testCreateFromGlobals() - { - $env = Environment::mock([ - 'SCRIPT_NAME' => '/index.php', - 'REQUEST_URI' => '/foo', - 'REQUEST_METHOD' => 'POST', - ]); - - $request = Request::createFromGlobals($env); - $this->assertEquals('POST', $request->getMethod()); - $this->assertEquals($env, $request->getServerParams()); - } - - /** - * @covers Slim\Http\Request::createFromGlobals - */ - public function testCreateFromGlobalsWithMultipart() - { - $_POST['foo'] = 'bar'; - - $env = Environment::mock([ - 'SCRIPT_NAME' => '/index.php', - 'REQUEST_URI' => '/foo', - 'REQUEST_METHOD' => 'POST', - 'HTTP_CONTENT_TYPE' => 'multipart/form-data; boundary=---foo' - ]); - - $request = Request::createFromGlobals($env); - unset($_POST); - - $this->assertEquals(['foo' => 'bar'], $request->getParsedBody()); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testCreateRequestWithInvalidMethodString() - { - $uri = Uri::createFromString('https://example.com:443/foo/bar?abc=123'); - $headers = new Headers(); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $request = new Request('B@R', $uri, $headers, $cookies, $serverParams, $body); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testCreateRequestWithInvalidMethodOther() - { - $uri = Uri::createFromString('https://example.com:443/foo/bar?abc=123'); - $headers = new Headers(); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $request = new Request(10, $uri, $headers, $cookies, $serverParams, $body); - } - - public function testIsGet() - { - $request = $this->requestFactory(); - $prop = new ReflectionProperty($request, 'method'); - $prop->setAccessible(true); - $prop->setValue($request, 'GET'); - - $this->assertTrue($request->isGet()); - } - - public function testIsPost() - { - $request = $this->requestFactory(); - $prop = new ReflectionProperty($request, 'method'); - $prop->setAccessible(true); - $prop->setValue($request, 'POST'); - - $this->assertTrue($request->isPost()); - } - - public function testIsPut() - { - $request = $this->requestFactory(); - $prop = new ReflectionProperty($request, 'method'); - $prop->setAccessible(true); - $prop->setValue($request, 'PUT'); - - $this->assertTrue($request->isPut()); - } - - public function testIsPatch() - { - $request = $this->requestFactory(); - $prop = new ReflectionProperty($request, 'method'); - $prop->setAccessible(true); - $prop->setValue($request, 'PATCH'); - - $this->assertTrue($request->isPatch()); - } - - public function testIsDelete() - { - $request = $this->requestFactory(); - $prop = new ReflectionProperty($request, 'method'); - $prop->setAccessible(true); - $prop->setValue($request, 'DELETE'); - - $this->assertTrue($request->isDelete()); - } - - public function testIsHead() - { - $request = $this->requestFactory(); - $prop = new ReflectionProperty($request, 'method'); - $prop->setAccessible(true); - $prop->setValue($request, 'HEAD'); - - $this->assertTrue($request->isHead()); - } - - public function testIsOptions() - { - $request = $this->requestFactory(); - $prop = new ReflectionProperty($request, 'method'); - $prop->setAccessible(true); - $prop->setValue($request, 'OPTIONS'); - - $this->assertTrue($request->isOptions()); - } - - public function testIsXhr() - { - $uri = Uri::createFromString('https://example.com:443/foo/bar?abc=123'); - $headers = new Headers([ - 'Content-Type' => 'application/x-www-form-urlencoded', - 'X-Requested-With' => 'XMLHttpRequest', - ]); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $request = new Request('GET', $uri, $headers, $cookies, $serverParams, $body); - - $this->assertTrue($request->isXhr()); - } - - /******************************************************************************* - * URI - ******************************************************************************/ - - public function testGetRequestTarget() - { - $this->assertEquals('/foo/bar?abc=123', $this->requestFactory()->getRequestTarget()); - } - - public function testGetRequestTargetAlreadySet() - { - $request = $this->requestFactory(); - $prop = new ReflectionProperty($request, 'requestTarget'); - $prop->setAccessible(true); - $prop->setValue($request, '/foo/bar?abc=123'); - - $this->assertEquals('/foo/bar?abc=123', $request->getRequestTarget()); - } - - public function testGetRequestTargetIfNoUri() - { - $request = $this->requestFactory(); - $prop = new ReflectionProperty($request, 'uri'); - $prop->setAccessible(true); - $prop->setValue($request, null); - - $this->assertEquals('/', $request->getRequestTarget()); - } - - public function testWithRequestTarget() - { - $clone = $this->requestFactory()->withRequestTarget('/test?user=1'); - - $this->assertAttributeEquals('/test?user=1', 'requestTarget', $clone); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testWithRequestTargetThatHasSpaces() - { - $this->requestFactory()->withRequestTarget('/test/m ore/stuff?user=1'); - } - - public function testGetUri() - { - $uri = Uri::createFromString('https://example.com:443/foo/bar?abc=123'); - $headers = new Headers(); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $request = new Request('GET', $uri, $headers, $cookies, $serverParams, $body); - - $this->assertSame($uri, $request->getUri()); - } - - public function testWithUri() - { - // Uris - $uri1 = Uri::createFromString('https://example.com:443/foo/bar?abc=123'); - $uri2 = Uri::createFromString('https://example2.com:443/test?xyz=123'); - - // Request - $headers = new Headers(); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $request = new Request('GET', $uri1, $headers, $cookies, $serverParams, $body); - $clone = $request->withUri($uri2); - - $this->assertAttributeSame($uri2, 'uri', $clone); - } - - public function testWithUriPreservesHost() - { - // When `$preserveHost` is set to `true`, this method interacts with - // the Host header in the following ways: - - // - If the the Host header is missing or empty, and the new URI contains - // a host component, this method MUST update the Host header in the returned - // request. - $uri1 = Uri::createFromString(''); - $uri2 = Uri::createFromString('http://example2.com/test'); - - // Request - $headers = new Headers(); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $request = new Request('GET', $uri1, $headers, $cookies, $serverParams, $body); - - $clone = $request->withUri($uri2, true); - $this->assertSame('example2.com', $clone->getHeaderLine('Host')); - - // - If the Host header is missing or empty, and the new URI does not contain a - // host component, this method MUST NOT update the Host header in the returned - // request. - $uri3 = Uri::createFromString(''); - - $clone = $request->withUri($uri3, true); - $this->assertSame('', $clone->getHeaderLine('Host')); - - // - If a Host header is present and non-empty, this method MUST NOT update - // the Host header in the returned request. - $request = $request->withHeader('Host', 'example.com'); - $clone = $request->withUri($uri2, true); - $this->assertSame('example.com', $clone->getHeaderLine('Host')); - } - - public function testGetContentType() - { - $headers = new Headers([ - 'Content-Type' => ['application/json;charset=utf8'], - ]); - $request = $this->requestFactory(); - $headersProp = new ReflectionProperty($request, 'headers'); - $headersProp->setAccessible(true); - $headersProp->setValue($request, $headers); - - $this->assertEquals('application/json;charset=utf8', $request->getContentType()); - } - - public function testGetContentTypeEmpty() - { - $request = $this->requestFactory(); - - $this->assertNull($request->getContentType()); - } - - public function testGetMediaType() - { - $headers = new Headers([ - 'Content-Type' => ['application/json;charset=utf8'], - ]); - $request = $this->requestFactory(); - $headersProp = new ReflectionProperty($request, 'headers'); - $headersProp->setAccessible(true); - $headersProp->setValue($request, $headers); - - $this->assertEquals('application/json', $request->getMediaType()); - } - - public function testGetMediaTypeEmpty() - { - $request = $this->requestFactory(); - - $this->assertNull($request->getMediaType()); - } - - public function testGetMediaTypeParams() - { - $headers = new Headers([ - 'Content-Type' => ['application/json;charset=utf8;foo=bar'], - ]); - $request = $this->requestFactory(); - $headersProp = new ReflectionProperty($request, 'headers'); - $headersProp->setAccessible(true); - $headersProp->setValue($request, $headers); - - $this->assertEquals(['charset' => 'utf8', 'foo' => 'bar'], $request->getMediaTypeParams()); - } - - public function testGetMediaTypeParamsEmpty() - { - $headers = new Headers([ - 'Content-Type' => ['application/json'], - ]); - $request = $this->requestFactory(); - $headersProp = new ReflectionProperty($request, 'headers'); - $headersProp->setAccessible(true); - $headersProp->setValue($request, $headers); - - $this->assertEquals([], $request->getMediaTypeParams()); - } - - public function testGetMediaTypeParamsWithoutHeader() - { - $request = $this->requestFactory(); - - $this->assertEquals([], $request->getMediaTypeParams()); - } - - public function testGetContentCharset() - { - $headers = new Headers([ - 'Content-Type' => ['application/json;charset=utf8'], - ]); - $request = $this->requestFactory(); - $headersProp = new ReflectionProperty($request, 'headers'); - $headersProp->setAccessible(true); - $headersProp->setValue($request, $headers); - - $this->assertEquals('utf8', $request->getContentCharset()); - } - - public function testGetContentCharsetEmpty() - { - $headers = new Headers([ - 'Content-Type' => ['application/json'], - ]); - $request = $this->requestFactory(); - $headersProp = new ReflectionProperty($request, 'headers'); - $headersProp->setAccessible(true); - $headersProp->setValue($request, $headers); - - $this->assertNull($request->getContentCharset()); - } - - public function testGetContentCharsetWithoutHeader() - { - $request = $this->requestFactory(); - - $this->assertNull($request->getContentCharset()); - } - - public function testGetContentLength() - { - $headers = new Headers([ - 'Content-Length' => '150', // <-- Note we define as a string - ]); - $request = $this->requestFactory(); - $headersProp = new ReflectionProperty($request, 'headers'); - $headersProp->setAccessible(true); - $headersProp->setValue($request, $headers); - - $this->assertEquals(150, $request->getContentLength()); - } - - public function testGetContentLengthWithoutHeader() - { - $request = $this->requestFactory(); - - $this->assertNull($request->getContentLength()); - } - - /******************************************************************************* - * Cookies - ******************************************************************************/ - - public function testGetCookieParam() - { - $shouldBe = 'john'; - - $this->assertEquals($shouldBe, $this->requestFactory()->getCookieParam('user')); - } - - public function testGetCookieParamWithDefault() - { - $shouldBe = 'bar'; - - $this->assertEquals($shouldBe, $this->requestFactory()->getCookieParam('foo', 'bar')); - } - - public function testGetCookieParams() - { - $shouldBe = [ - 'user' => 'john', - 'id' => '123', - ]; - - $this->assertEquals($shouldBe, $this->requestFactory()->getCookieParams()); - } - - public function testWithCookieParams() - { - $request = $this->requestFactory(); - $clone = $request->withCookieParams(['type' => 'framework']); - - $this->assertEquals(['type' => 'framework'], $clone->getCookieParams()); - } - - /******************************************************************************* - * Query Params - ******************************************************************************/ - - public function testGetQueryParams() - { - $this->assertEquals(['abc' => '123'], $this->requestFactory()->getQueryParams()); - } - - public function testGetQueryParamsAlreadySet() - { - $request = $this->requestFactory(); - $prop = new ReflectionProperty($request, 'queryParams'); - $prop->setAccessible(true); - $prop->setValue($request, ['foo' => 'bar']); - - $this->assertEquals(['foo' => 'bar'], $request->getQueryParams()); - } - - public function testWithQueryParams() - { - $request = $this->requestFactory(); - $clone = $request->withQueryParams(['foo' => 'bar']); - $cloneUri = $clone->getUri(); - - $this->assertEquals('abc=123', $cloneUri->getQuery()); // <-- Unchanged - $this->assertEquals(['foo' => 'bar'], $clone->getQueryParams()); // <-- Changed - } - - public function testWithQueryParamsEmptyArray() - { - $request = $this->requestFactory(); - $clone = $request->withQueryParams([]); - $cloneUri = $clone->getUri(); - - $this->assertEquals('abc=123', $cloneUri->getQuery()); // <-- Unchanged - $this->assertEquals([], $clone->getQueryParams()); // <-- Changed - } - - public function testGetQueryParamsWithoutUri() - { - $request = $this->requestFactory(); - $prop = new ReflectionProperty($request, 'uri'); - $prop->setAccessible(true); - $prop->setValue($request, null); - - $this->assertEquals([], $request->getQueryParams()); - } - - /******************************************************************************* - * Uploaded files - ******************************************************************************/ - - /** - * @covers Slim\Http\Request::withUploadedFiles - * @covers Slim\Http\Request::getUploadedFiles - */ - public function testWithUploadedFiles() - { - $files = [new UploadedFile('foo.txt'), new UploadedFile('bar.txt')]; - - $request = $this->requestFactory(); - $clone = $request->withUploadedFiles($files); - - $this->assertEquals([], $request->getUploadedFiles()); - $this->assertEquals($files, $clone->getUploadedFiles()); - } - - /******************************************************************************* - * Server Params - ******************************************************************************/ - - public function testGetServerParams() - { - $mockEnv = Environment::mock(["HTTP_AUTHORIZATION" => "test"]); - $request = $this->requestFactory(["HTTP_AUTHORIZATION" => "test"]); - - $serverParams = $request->getServerParams(); - foreach ($serverParams as $key => $value) { - if ($key == 'REQUEST_TIME' || $key == 'REQUEST_TIME_FLOAT') { - $this->assertGreaterThanOrEqual( - $mockEnv[$key], - $value, - sprintf("%s value of %s was less than expected value of %s", $key, $value, $mockEnv[$key]) - ); - } else { - $this->assertEquals( - $mockEnv[$key], - $value, - sprintf("%s value of %s did not equal expected value of %s", $key, $value, $mockEnv[$key]) - ); - } - } - } - - public function testGetServerParam() - { - $shouldBe = 'HTTP/1.1'; - $request = $this->requestFactory(['SERVER_PROTOCOL' => 'HTTP/1.1']); - - $this->assertEquals($shouldBe, $this->requestFactory()->getServerParam('SERVER_PROTOCOL')); - } - - public function testGetServerParamWithDefault() - { - $shouldBe = 'bar'; - - $this->assertEquals($shouldBe, $this->requestFactory()->getServerParam('HTTP_NOT_EXIST', 'bar')); - } - - /******************************************************************************* - * File Params - ******************************************************************************/ - - /******************************************************************************* - * Attributes - ******************************************************************************/ - - public function testGetAttributes() - { - $request = $this->requestFactory(); - $attrProp = new ReflectionProperty($request, 'attributes'); - $attrProp->setAccessible(true); - $attrProp->setValue($request, new Collection(['foo' => 'bar'])); - - $this->assertEquals(['foo' => 'bar'], $request->getAttributes()); - } - - public function testGetAttribute() - { - $request = $this->requestFactory(); - $attrProp = new ReflectionProperty($request, 'attributes'); - $attrProp->setAccessible(true); - $attrProp->setValue($request, new Collection(['foo' => 'bar'])); - - $this->assertEquals('bar', $request->getAttribute('foo')); - $this->assertNull($request->getAttribute('bar')); - $this->assertEquals(2, $request->getAttribute('bar', 2)); - } - - public function testWithAttribute() - { - $request = $this->requestFactory(); - $attrProp = new ReflectionProperty($request, 'attributes'); - $attrProp->setAccessible(true); - $attrProp->setValue($request, new Collection(['foo' => 'bar'])); - $clone = $request->withAttribute('test', '123'); - - $this->assertEquals('123', $clone->getAttribute('test')); - } - - public function testWithAttributes() - { - $request = $this->requestFactory(); - $attrProp = new ReflectionProperty($request, 'attributes'); - $attrProp->setAccessible(true); - $attrProp->setValue($request, new Collection(['foo' => 'bar'])); - $clone = $request->withAttributes(['test' => '123']); - - $this->assertNull($clone->getAttribute('foo')); - $this->assertEquals('123', $clone->getAttribute('test')); - } - - public function testWithoutAttribute() - { - $request = $this->requestFactory(); - $attrProp = new ReflectionProperty($request, 'attributes'); - $attrProp->setAccessible(true); - $attrProp->setValue($request, new Collection(['foo' => 'bar'])); - $clone = $request->withoutAttribute('foo'); - - $this->assertNull($clone->getAttribute('foo')); - } - - /******************************************************************************* - * Body - ******************************************************************************/ - - public function testGetParsedBodyForm() - { - $method = 'GET'; - $uri = new Uri('https', 'example.com', 443, '/foo/bar', 'abc=123', '', ''); - $headers = new Headers(); - $headers->set('Content-Type', 'application/x-www-form-urlencoded;charset=utf8'); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $body->write('foo=bar'); - $request = new Request($method, $uri, $headers, $cookies, $serverParams, $body); - $this->assertEquals(['foo' => 'bar'], $request->getParsedBody()); - } - - public function testGetParsedBodyJson() - { - $method = 'GET'; - $uri = new Uri('https', 'example.com', 443, '/foo/bar', 'abc=123', '', ''); - $headers = new Headers(); - $headers->set('Content-Type', 'application/json;charset=utf8'); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $body->write('{"foo":"bar"}'); - $request = new Request($method, $uri, $headers, $cookies, $serverParams, $body); - - $this->assertEquals(['foo' => 'bar'], $request->getParsedBody()); - } - - public function testGetParsedBodyInvalidJson() - { - $method = 'GET'; - $uri = new Uri('https', 'example.com', 443, '/foo/bar', 'abc=123', '', ''); - $headers = new Headers(); - $headers->set('Content-Type', 'application/json;charset=utf8'); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $body->write('{foo}bar'); - $request = new Request($method, $uri, $headers, $cookies, $serverParams, $body); - - $this->assertNull($request->getParsedBody()); - } - - public function testGetParsedBodySemiValidJson() - { - $method = 'GET'; - $uri = new Uri('https', 'example.com', 443, '/foo/bar', 'abc=123', '', ''); - $headers = new Headers(); - $headers->set('Content-Type', 'application/json;charset=utf8'); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $body->write('"foo bar"'); - $request = new Request($method, $uri, $headers, $cookies, $serverParams, $body); - - $this->assertNull($request->getParsedBody()); - } - - public function testGetParsedBodyWithJsonStructuredSuffix() - { - $method = 'GET'; - $uri = new Uri('https', 'example.com', 443, '/foo/bar', 'abc=123', '', ''); - $headers = new Headers(); - $headers->set('Content-Type', 'application/vnd.api+json;charset=utf8'); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $body->write('{"foo":"bar"}'); - $request = new Request($method, $uri, $headers, $cookies, $serverParams, $body); - - $this->assertEquals(['foo' => 'bar'], $request->getParsedBody()); - } - - public function testGetParsedBodyXml() - { - $method = 'GET'; - $uri = new Uri('https', 'example.com', 443, '/foo/bar', 'abc=123', '', ''); - $headers = new Headers(); - $headers->set('Content-Type', 'application/xml;charset=utf8'); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $body->write('Josh'); - $request = new Request($method, $uri, $headers, $cookies, $serverParams, $body); - - $this->assertEquals('Josh', $request->getParsedBody()->name); - } - - public function testGetParsedBodyWithXmlStructuredSuffix() - { - $method = 'GET'; - $uri = new Uri('https', 'example.com', 443, '/foo/bar', 'abc=123', '', ''); - $headers = new Headers(); - $headers->set('Content-Type', 'application/hal+xml;charset=utf8'); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $body->write('Josh'); - $request = new Request($method, $uri, $headers, $cookies, $serverParams, $body); - - $this->assertEquals('Josh', $request->getParsedBody()->name); - } - - public function testGetParsedBodyXmlWithTextXMLMediaType() - { - $method = 'GET'; - $uri = new Uri('https', 'example.com', 443, '/foo/bar', 'abc=123', '', ''); - $headers = new Headers(); - $headers->set('Content-Type', 'text/xml'); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $body->write('Josh'); - $request = new Request($method, $uri, $headers, $cookies, $serverParams, $body); - - $this->assertEquals('Josh', $request->getParsedBody()->name); - } - - /** - * Will fail if a simple_xml warning is created - */ - public function testInvalidXmlIsQuietForTextXml() - { - $method = 'GET'; - $uri = new Uri('https', 'example.com', 443, '/foo/bar', 'abc=123', '', ''); - $headers = new Headers(); - $headers->set('Content-Type', 'text/xml'); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $body->write('Josh'); - $request = new Request($method, $uri, $headers, $cookies, $serverParams, $body); - - $this->assertEquals(null, $request->getParsedBody()); - } - - /** - * Will fail if a simple_xml warning is created - */ - public function testInvalidXmlIsQuietForApplicationXml() - { - $method = 'GET'; - $uri = new Uri('https', 'example.com', 443, '/foo/bar', 'abc=123', '', ''); - $headers = new Headers(); - $headers->set('Content-Type', 'application/xml'); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $body->write('Josh'); - $request = new Request($method, $uri, $headers, $cookies, $serverParams, $body); - - $this->assertEquals(null, $request->getParsedBody()); - } - - - public function testGetParsedBodyWhenAlreadyParsed() - { - $request = $this->requestFactory(); - $prop = new ReflectionProperty($request, 'bodyParsed'); - $prop->setAccessible(true); - $prop->setValue($request, ['foo' => 'bar']); - - $this->assertEquals(['foo' => 'bar'], $request->getParsedBody()); - } - - public function testGetParsedBodyWhenBodyDoesNotExist() - { - $request = $this->requestFactory(); - $prop = new ReflectionProperty($request, 'body'); - $prop->setAccessible(true); - $prop->setValue($request, null); - - $this->assertNull($request->getParsedBody()); - } - - public function testGetParsedBodyAfterCallReparseBody() - { - $uri = Uri::createFromString('https://example.com:443/?one=1'); - $headers = new Headers([ - 'Content-Type' => 'application/x-www-form-urlencoded;charset=utf8', - ]); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $body->write('foo=bar'); - $body->rewind(); - $request = new Request('POST', $uri, $headers, $cookies, $serverParams, $body); - - $this->assertEquals(['foo' => 'bar'], $request->getParsedBody()); - - $newBody = new RequestBody(); - $newBody->write('abc=123'); - $newBody->rewind(); - $request = $request->withBody($newBody); - $request->reparseBody(); - - $this->assertEquals(['abc' => '123'], $request->getParsedBody()); - } - - /** - * @expectedException \RuntimeException - */ - public function testGetParsedBodyAsArray() - { - $uri = Uri::createFromString('https://example.com:443/foo/bar?abc=123'); - $headers = new Headers([ - 'Content-Type' => 'application/json;charset=utf8', - ]); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $body->write('{"foo": "bar"}'); - $body->rewind(); - $request = new Request('POST', $uri, $headers, $cookies, $serverParams, $body); - $request->registerMediaTypeParser('application/json', function ($input) { - return 10; // <-- Return invalid body value - }); - $request->getParsedBody(); // <-- Triggers exception - } - - public function testWithParsedBody() - { - $clone = $this->requestFactory()->withParsedBody(['xyz' => '123']); - - $this->assertEquals(['xyz' => '123'], $clone->getParsedBody()); - } - - public function testWithParsedBodyEmptyArray() - { - $method = 'GET'; - $uri = new Uri('https', 'example.com', 443, '/foo/bar', 'abc=123', '', ''); - $headers = new Headers(); - $headers->set('Content-Type', 'application/x-www-form-urlencoded;charset=utf8'); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $body->write('foo=bar'); - $request = new Request($method, $uri, $headers, $cookies, $serverParams, $body); - - - $clone = $request->withParsedBody([]); - - $this->assertEquals([], $clone->getParsedBody()); - } - - public function testWithParsedBodyNull() - { - $method = 'GET'; - $uri = new Uri('https', 'example.com', 443, '/foo/bar', 'abc=123', '', ''); - $headers = new Headers(); - $headers->set('Content-Type', 'application/x-www-form-urlencoded;charset=utf8'); - $cookies = []; - $serverParams = []; - $body = new RequestBody(); - $body->write('foo=bar'); - $request = new Request($method, $uri, $headers, $cookies, $serverParams, $body); - - - $clone = $request->withParsedBody(null); - - $this->assertNull($clone->getParsedBody()); - } - - public function testGetParsedBodyReturnsNullWhenThereIsNoBodyData() - { - $request = $this->requestFactory(['REQUEST_METHOD' => 'POST']); - - $this->assertNull($request->getParsedBody()); - } - - public function testGetParsedBodyReturnsNullWhenThereIsNoMediaTypeParserRegistered() - { - $request = $this->requestFactory([ - 'REQUEST_METHOD' => 'POST', - 'CONTENT_TYPE' => 'text/csv', - ]); - $request->getBody()->write('foo,bar,baz'); - - $this->assertNull($request->getParsedBody()); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testWithParsedBodyInvalid() - { - $this->requestFactory()->withParsedBody(2); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testWithParsedBodyInvalidFalseValue() - { - $this->requestFactory()->withParsedBody(false); - } - - /******************************************************************************* - * Parameters - ******************************************************************************/ - - public function testGetParameterFromBody() - { - $body = new RequestBody(); - $body->write('foo=bar'); - $body->rewind(); - $request = $this->requestFactory() - ->withBody($body) - ->withHeader('Content-Type', 'application/x-www-form-urlencoded'); - - $this->assertEquals('bar', $request->getParam('foo')); - } - - public function testGetParameterFromBodyWithBodyParemeterHelper() - { - $body = new RequestBody(); - $body->write('foo=bar'); - $body->rewind(); - $request = $this->requestFactory() - ->withBody($body) - ->withHeader('Content-Type', 'application/x-www-form-urlencoded'); - - $this->assertEquals('bar', $request->getParsedBodyParam('foo')); - } - - public function testGetParameterFromQuery() - { - $request = $this->requestFactory()->withHeader('Content-Type', 'application/x-www-form-urlencoded'); - - $this->assertEquals('123', $request->getParam('abc')); - } - - public function testGetParameterFromQueryWithQueryParemeterHelper() - { - $request = $this->requestFactory()->withHeader('Content-Type', 'application/x-www-form-urlencoded'); - - $this->assertEquals('123', $request->getQueryParam('abc')); - } - - public function testGetParameterFromBodyOverQuery() - { - $body = new RequestBody(); - $body->write('abc=xyz'); - $body->rewind(); - $request = $this->requestFactory() - ->withBody($body) - ->withHeader('Content-Type', 'application/x-www-form-urlencoded'); - $this->assertEquals('xyz', $request->getParam('abc')); - } - - public function testGetParameterWithDefaultFromBodyOverQuery() - { - $body = new RequestBody(); - $body->write('abc=xyz'); - $body->rewind(); - $request = $this->requestFactory() - ->withBody($body) - ->withHeader('Content-Type', 'application/x-www-form-urlencoded'); - $this->assertEquals('xyz', $request->getParam('abc')); - $this->assertEquals('bar', $request->getParam('foo', 'bar')); - } - - public function testGetParameters() - { - $body = new RequestBody(); - $body->write('foo=bar'); - $body->rewind(); - $request = $this->requestFactory() - ->withBody($body) - ->withHeader('Content-Type', 'application/x-www-form-urlencoded'); - - $this->assertEquals(['abc' => '123', 'foo' => 'bar'], $request->getParams()); - } - - public function testGetParametersWithBodyPriority() - { - $body = new RequestBody(); - $body->write('foo=bar&abc=xyz'); - $body->rewind(); - $request = $this->requestFactory() - ->withBody($body) - ->withHeader('Content-Type', 'application/x-www-form-urlencoded'); - - $this->assertEquals(['abc' => 'xyz', 'foo' => 'bar'], $request->getParams()); - } - - /******************************************************************************* - * Protocol - ******************************************************************************/ - - public function testGetProtocolVersion() - { - $env = Environment::mock(['SERVER_PROTOCOL' => 'HTTP/1.0']); - $request = Request::createFromGlobals($env); - - $this->assertEquals('1.0', $request->getProtocolVersion()); - } -} diff --git a/tests/ResponseTest.php b/tests/ResponseTest.php deleted file mode 100755 index ce3b988..0000000 --- a/tests/ResponseTest.php +++ /dev/null @@ -1,341 +0,0 @@ -assertAttributeEquals(200, 'status', $response); - $this->assertAttributeInstanceOf('\Slim\Http\Headers', 'headers', $response); - $this->assertAttributeInstanceOf('\Psr\Http\Message\StreamInterface', 'body', $response); - } - - public function testConstructorWithCustomArgs() - { - $headers = new Headers(); - $body = new Body(fopen('php://temp', 'r+')); - $response = new Response(404, $headers, $body); - - $this->assertAttributeEquals(404, 'status', $response); - $this->assertAttributeSame($headers, 'headers', $response); - $this->assertAttributeSame($body, 'body', $response); - } - - public function testDeepCopyClone() - { - $headers = new Headers(); - $body = new Body(fopen('php://temp', 'r+')); - $response = new Response(404, $headers, $body); - $clone = clone $response; - - $this->assertAttributeEquals('1.1', 'protocolVersion', $clone); - $this->assertAttributeEquals(404, 'status', $clone); - $this->assertAttributeNotSame($headers, 'headers', $clone); - $this->assertAttributeSame($body, 'body', $clone); - } - - public function testDisableSetter() - { - $response = new Response(); - $response->foo = 'bar'; - - $this->assertFalse(property_exists($response, 'foo')); - } - - /******************************************************************************* - * Status - ******************************************************************************/ - - public function testGetStatusCode() - { - $response = new Response(); - $responseStatus = new ReflectionProperty($response, 'status'); - $responseStatus->setAccessible(true); - $responseStatus->setValue($response, '404'); - - $this->assertEquals(404, $response->getStatusCode()); - } - - public function testWithStatus() - { - $response = new Response(); - $clone = $response->withStatus(302); - - $this->assertAttributeEquals(302, 'status', $clone); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testWithStatusInvalidStatusCodeThrowsException() - { - $response = new Response(); - $response->withStatus(800); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage ReasonPhrase must be a string - */ - public function testWithStatusInvalidReasonPhraseThrowsException() - { - $response = new Response(); - $response->withStatus(200, null); - } - - public function testWithStatusEmptyReasonPhrase() - { - $responseWithNoMessage = new Response(310); - - $this->assertEquals('', $responseWithNoMessage->getReasonPhrase()); - } - - public function testGetReasonPhrase() - { - $response = new Response(404); - - $this->assertEquals('Not Found', $response->getReasonPhrase()); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage ReasonPhrase must be supplied for this code - */ - public function testMustSetReasonPhraseForUnrecognisedCode() - { - $response = new Response(); - $response = $response->withStatus(199); - } - - public function testSetReasonPhraseForUnrecognisedCode() - { - $response = new Response(); - $response = $response->withStatus(199, 'Random Message'); - - $this->assertEquals('Random Message', $response->getReasonPhrase()); - } - - public function testGetCustomReasonPhrase() - { - $response = new Response(); - $clone = $response->withStatus(200, 'Custom Phrase'); - - $this->assertEquals('Custom Phrase', $clone->getReasonPhrase()); - } - - /** - * @covers Slim\Http\Response::withRedirect - */ - public function testWithRedirect() - { - $response = new Response(200); - $clone = $response->withRedirect('/foo', 301); - $cloneWithDefaultStatus = $response->withRedirect('/foo'); - $cloneWithStatusMethod = $response->withStatus(301)->withRedirect('/foo'); - - $this->assertSame(200, $response->getStatusCode()); - $this->assertFalse($response->hasHeader('Location')); - - $this->assertSame(301, $clone->getStatusCode()); - $this->assertTrue($clone->hasHeader('Location')); - $this->assertEquals('/foo', $clone->getHeaderLine('Location')); - - $this->assertSame(302, $cloneWithDefaultStatus->getStatusCode()); - $this->assertTrue($cloneWithDefaultStatus->hasHeader('Location')); - $this->assertEquals('/foo', $cloneWithDefaultStatus->getHeaderLine('Location')); - - $this->assertSame(301, $cloneWithStatusMethod->getStatusCode()); - $this->assertTrue($cloneWithStatusMethod->hasHeader('Location')); - $this->assertEquals('/foo', $cloneWithStatusMethod->getHeaderLine('Location')); - } - - /******************************************************************************* - * Behaviors - ******************************************************************************/ - - public function testIsEmpty() - { - $response = new Response(); - $prop = new ReflectionProperty($response, 'status'); - $prop->setAccessible(true); - $prop->setValue($response, 204); - - $this->assertTrue($response->isEmpty()); - } - - public function testIsInformational() - { - $response = new Response(); - $prop = new ReflectionProperty($response, 'status'); - $prop->setAccessible(true); - $prop->setValue($response, 100); - - $this->assertTrue($response->isInformational()); - } - - public function testIsOk() - { - $response = new Response(); - $prop = new ReflectionProperty($response, 'status'); - $prop->setAccessible(true); - $prop->setValue($response, 200); - - $this->assertTrue($response->isOk()); - } - - public function testIsSuccessful() - { - $response = new Response(); - $prop = new ReflectionProperty($response, 'status'); - $prop->setAccessible(true); - $prop->setValue($response, 201); - - $this->assertTrue($response->isSuccessful()); - } - - public function testIsRedirect() - { - $response = new Response(); - $prop = new ReflectionProperty($response, 'status'); - $prop->setAccessible(true); - $prop->setValue($response, 302); - - $this->assertTrue($response->isRedirect()); - } - - public function testIsRedirection() - { - $response = new Response(); - $prop = new ReflectionProperty($response, 'status'); - $prop->setAccessible(true); - $prop->setValue($response, 308); - - $this->assertTrue($response->isRedirection()); - } - - public function testIsForbidden() - { - $response = new Response(); - $prop = new ReflectionProperty($response, 'status'); - $prop->setAccessible(true); - $prop->setValue($response, 403); - - $this->assertTrue($response->isForbidden()); - } - - public function testIsNotFound() - { - $response = new Response(); - $prop = new ReflectionProperty($response, 'status'); - $prop->setAccessible(true); - $prop->setValue($response, 404); - - $this->assertTrue($response->isNotFound()); - } - - public function testIsClientError() - { - $response = new Response(); - $prop = new ReflectionProperty($response, 'status'); - $prop->setAccessible(true); - $prop->setValue($response, 400); - - $this->assertTrue($response->isClientError()); - } - - public function testIsServerError() - { - $response = new Response(); - $prop = new ReflectionProperty($response, 'status'); - $prop->setAccessible(true); - $prop->setValue($response, 503); - - $this->assertTrue($response->isServerError()); - } - - public function testToString() - { - $output = 'HTTP/1.1 404 Not Found' . Response::EOL . - 'X-Foo: Bar' . Response::EOL . Response::EOL . - 'Where am I?'; - $this->expectOutputString($output); - $response = new Response(); - $response = $response->withStatus(404)->withHeader('X-Foo', 'Bar')->write('Where am I?'); - - echo $response; - } - - public function testWithJson() - { - $data = ['foo' => 'bar1&bar2']; - - $originalResponse = new Response(); - $response = $originalResponse->withJson($data, 201); - - $this->assertNotEquals($response->getStatusCode(), $originalResponse->getStatusCode()); - $this->assertEquals(201, $response->getStatusCode()); - $this->assertEquals('application/json;charset=utf-8', $response->getHeaderLine('Content-Type')); - - $body = $response->getBody(); - $body->rewind(); - $dataJson = $body->getContents(); //json_decode($body->getContents(), true); - - $originalBody = $originalResponse->getBody(); - $originalBody->rewind(); - $originalContents = $originalBody->getContents(); - - // test the original body hasn't be replaced - $this->assertNotEquals($dataJson, $originalContents); - - $this->assertEquals('{"foo":"bar1&bar2"}', $dataJson); - $this->assertEquals($data['foo'], json_decode($dataJson, true)['foo']); - - // Test encoding option - $response = $response->withJson($data, 200, JSON_HEX_AMP); - - $body = $response->getBody(); - $body->rewind(); - $dataJson = $body->getContents(); - - $this->assertEquals('{"foo":"bar1\u0026bar2"}', $dataJson); - $this->assertEquals($data['foo'], json_decode($dataJson, true)['foo']); - - $response = $response->withStatus(201)->withJson([]); - $this->assertEquals($response->getStatusCode(), 201); - } - - /** - * @expectedException \RuntimeException - */ - public function testWithInvalidJsonThrowsException() - { - $data = ['foo' => 'bar'.chr(233)]; - $this->assertEquals('bar'.chr(233), $data['foo']); - - $response = new Response(); - $response->withJson($data, 200); - - // Safety net: this assertion should not occur, since the RuntimeException - // must have been caught earlier by the test framework - $this->assertFalse(true); - } -} diff --git a/tests/StreamTest.php b/tests/StreamTest.php deleted file mode 100644 index 562027d..0000000 --- a/tests/StreamTest.php +++ /dev/null @@ -1,159 +0,0 @@ -pipeFh != null) { - stream_get_contents($this->pipeFh); // prevent broken pipe error message - } - } - - /** - * @covers Slim\Http\Stream::isPipe - */ - public function testIsPipe() - { - $this->openPipeStream(); - - $this->assertTrue($this->pipeStream->isPipe()); - - $this->pipeStream->detach(); - $this->assertFalse($this->pipeStream->isPipe()); - - $fhFile = fopen(__FILE__, 'r'); - $fileStream = new Stream($fhFile); - $this->assertFalse($fileStream->isPipe()); - } - - /** - * @covers Slim\Http\Stream::isReadable - */ - public function testIsPipeReadable() - { - $this->openPipeStream(); - - $this->assertTrue($this->pipeStream->isReadable()); - } - - /** - * @covers Slim\Http\Stream::isSeekable - */ - public function testPipeIsNotSeekable() - { - $this->openPipeStream(); - - $this->assertFalse($this->pipeStream->isSeekable()); - } - - /** - * @covers Slim\Http\Stream::seek - * @expectedException \RuntimeException - */ - public function testCannotSeekPipe() - { - $this->openPipeStream(); - - $this->pipeStream->seek(0); - } - - /** - * @covers Slim\Http\Stream::tell - * @expectedException \RuntimeException - */ - public function testCannotTellPipe() - { - $this->openPipeStream(); - - $this->pipeStream->tell(); - } - - /** - * @covers Slim\Http\Stream::rewind - * @expectedException \RuntimeException - */ - public function testCannotRewindPipe() - { - $this->openPipeStream(); - - $this->pipeStream->rewind(); - } - - /** - * @covers Slim\Http\Stream::getSize - */ - public function testPipeGetSizeYieldsNull() - { - $this->openPipeStream(); - - $this->assertNull($this->pipeStream->getSize()); - } - - /** - * @covers Slim\Http\Stream::close - */ - public function testClosePipe() - { - $this->openPipeStream(); - - stream_get_contents($this->pipeFh); // prevent broken pipe error message - $this->pipeStream->close(); - $this->pipeFh = null; - - $this->assertFalse($this->pipeStream->isPipe()); - } - - /** - * @covers Slim\Http\Stream::__toString - */ - public function testPipeToString() - { - $this->openPipeStream(); - - $this->assertSame('', (string) $this->pipeStream); - } - - /** - * @covers Slim\Http\Stream::getContents - */ - - public function testPipeGetContents() - { - $this->openPipeStream(); - - $contents = trim($this->pipeStream->getContents()); - $this->assertSame('12', $contents); - } - - /** - * Opens the pipe stream - * - * @see StreamTest::pipeStream - */ - private function openPipeStream() - { - $this->pipeFh = popen('echo 12', 'r'); - $this->pipeStream = new Stream($this->pipeFh); - } -} diff --git a/tests/Test.php b/tests/Test.php new file mode 100644 index 0000000..4c0f79f --- /dev/null +++ b/tests/Test.php @@ -0,0 +1,28 @@ +assertEquals($expected, $uploadedFile); - } - - /** - * @param array $input The input array to parse. - * - * @dataProvider providerCreateFromGlobals - */ - public function testCreateFromGlobalsFromUserData(array $input) - { - //If slim.files provided - it will return what was provided - $userData['slim.files'] = $input; - - $uploadedFile = UploadedFile::createFromGlobals(Environment::mock($userData)); - $this->assertEquals($input, $uploadedFile); - } - - public function testCreateFromGlobalsWithoutFile() - { - unset($_FILES); - - $uploadedFile = UploadedFile::createFromGlobals(Environment::mock()); - $this->assertEquals([], $uploadedFile); - } - - /** - * @return UploadedFile - */ - public function testConstructor() - { - $attr = [ - 'tmp_name' => self::$filename, - 'name' => 'my-avatar.txt', - 'size' => 8, - 'type' => 'text/plain', - 'error' => 0, - ]; - - $uploadedFile = new UploadedFile( - $attr['tmp_name'], - $attr['name'], - $attr['type'], - $attr['size'], - $attr['error'], - false - ); - - - $this->assertEquals($attr['name'], $uploadedFile->getClientFilename()); - $this->assertEquals($attr['type'], $uploadedFile->getClientMediaType()); - $this->assertEquals($attr['size'], $uploadedFile->getSize()); - $this->assertEquals($attr['error'], $uploadedFile->getError()); - - return $uploadedFile; - } - - /** - * @depends testConstructor - * @param UploadedFile $uploadedFile - * @return UploadedFile - */ - public function testGetStream(UploadedFile $uploadedFile) - { - $stream = $uploadedFile->getStream(); - $this->assertEquals(true, $uploadedFile->getStream() instanceof Stream); - $stream->close(); - - return $uploadedFile; - } - - /** - * @depends testConstructor - * @param UploadedFile $uploadedFile - * @expectedException \InvalidArgumentException - */ - public function testMoveToNotWritable(UploadedFile $uploadedFile) - { - $tempName = uniqid('file-'); - $path = 'some_random_dir' . DIRECTORY_SEPARATOR . $tempName; - $uploadedFile->moveTo($path); - } - - /** - * @depends testConstructor - * @param UploadedFile $uploadedFile - * @return UploadedFile - */ - public function testMoveTo(UploadedFile $uploadedFile) - { - $tempName = uniqid('file-'); - $path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $tempName; - $uploadedFile->moveTo($path); - - $this->assertFileExists($path); - - unlink($path); - - return $uploadedFile; - } - - /** - * @depends testMoveTo - * @param UploadedFile $uploadedFile - * @expectedException \RuntimeException - */ - public function testMoveToCannotBeDoneTwice(UploadedFile $uploadedFile) - { - $tempName = uniqid('file-'); - $path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $tempName; - $uploadedFile->moveTo($path); - $this->assertFileExists($path); - unlink($path); - - $uploadedFile->moveTo($path); - } - - /** - * This test must run after testMoveTo - * - * @depends testConstructor - * @param UploadedFile $uploadedFile - * @expectedException \RuntimeException - */ - public function testMoveToAgain(UploadedFile $uploadedFile) - { - $tempName = uniqid('file-'); - $path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $tempName; - $uploadedFile->moveTo($path); - } - - /** - * This test must run after testMoveTo - * - * @depends testConstructor - * @param UploadedFile $uploadedFile - * @expectedException \RuntimeException - */ - public function testMovedStream($uploadedFile) - { - $uploadedFile->getStream(); - } - - public function testMoveToStream() - { - $uploadedFile = $this->generateNewTmpFile(); - $uploadedFile->moveTo('php://temp'); - - $this->addToAssertionCount(1); // does not throw an exception - } - - public function providerCreateFromGlobals() - { - return [ - // no nest: - [ - // $_FILES array - [ - 'avatar' => [ - 'tmp_name' => 'phpUxcOty', - 'name' => 'my-avatar.png', - 'size' => 90996, - 'type' => 'image/png', - 'error' => 0, - ], - ], - // expected format of array - [ - 'avatar' => new UploadedFile('phpUxcOty', 'my-avatar.png', 'image/png', 90996, UPLOAD_ERR_OK, true) - ] - ], - // no nest, with error: - [ - // $_FILES array - [ - 'avatar' => [ - 'tmp_name' => 'phpUxcOty', - 'name' => 'my-avatar.png', - 'size' => 90996, - 'type' => 'image/png', - 'error' => 7, - ], - ], - // expected format of array - [ - 'avatar' => new UploadedFile( - 'phpUxcOty', - 'my-avatar.png', - 'image/png', - 90996, - UPLOAD_ERR_CANT_WRITE, - true - ) - ] - ], - - // array of files: - [ - // $_FILES array - [ - 'avatars' => [ - 'tmp_name' => [ - 0 => __DIR__ . DIRECTORY_SEPARATOR . 'file0.txt', - 1 => __DIR__ . DIRECTORY_SEPARATOR . 'file1.html', - ], - 'name' => [ - 0 => 'file0.txt', - 1 => 'file1.html', - ], - 'type' => [ - 0 => 'text/plain', - 1 => 'text/html', - ], - 'error' => [ - 0 => 0, - 1 => 0 - ], - 'size' => [ - 0 => 0, - 1 => 0 - ] - ], - ], - // expected format of array - [ - 'avatars' => [ - 0 => new UploadedFile( - __DIR__ . DIRECTORY_SEPARATOR . 'file0.txt', - 'file0.txt', - 'text/plain', - null, - UPLOAD_ERR_OK, - true - ), - 1 => new UploadedFile( - __DIR__ . DIRECTORY_SEPARATOR . 'file1.html', - 'file1.html', - 'text/html', - null, - UPLOAD_ERR_OK, - true - ), - ], - ] - ], - // array of files as multidimensional array: - [ - // $_FILES array - [ - [ - 'avatars' => [ - 'tmp_name' => [ - 0 => __DIR__ . DIRECTORY_SEPARATOR . 'file0.txt', - 1 => __DIR__ . DIRECTORY_SEPARATOR . 'file1.html', - ], - 'name' => [ - 0 => 'file0.txt', - 1 => 'file1.html', - ], - 'type' => [ - 0 => 'text/plain', - 1 => 'text/html', - ], - 'size' => [ - 0 => 0, - 1 => 0, - ], - ], - ], - ], - // expected format of array - [ - 0 => - [ - 'avatars' => - [ - 'tmp_name' => [], - 'name' => [], - 'type' => [], - 'size' => [], - ], - ], - ], - ], - // single nested file: - [ - // $_FILES array - [ - 'details' => [ - 'tmp_name' => [ - 'avatar' => __DIR__ . DIRECTORY_SEPARATOR . 'file0.txt', - ], - 'name' => [ - 'avatar' => 'file0.txt', - ], - 'type' => [ - 'avatar' => 'text/plain', - ], - 'error' => [ - 'avatar' => 0, - ], - 'size' => [ - 'avatar' => 0, - ], - ], - ], - // expected format of array - [ - 'details' => [ - 'avatar' => new UploadedFile( - __DIR__ . DIRECTORY_SEPARATOR . 'file0.txt', - 'file0.txt', - 'text/plain', - null, - UPLOAD_ERR_OK, - true - ), - ], - ] - ], - // nested array of files: - [ - [ - 'files' => [ - 'tmp_name' => [ - 'details' => [ - 'avatar' => [ - 0 => __DIR__ . DIRECTORY_SEPARATOR . 'file0.txt', - 1 => __DIR__ . DIRECTORY_SEPARATOR . 'file1.html', - ], - ], - ], - 'name' => [ - 'details' => [ - 'avatar' => [ - 0 => 'file0.txt', - 1 => 'file1.html', - ], - ], - ], - 'type' => [ - 'details' => [ - 'avatar' => [ - 0 => 'text/plain', - 1 => 'text/html', - ], - ], - ], - 'error' => [ - 'details' => [ - 'avatar' => [ - 0 => 0, - 1 => 0 - ], - ], - ], - 'size' => [ - 'details' => [ - 'avatar' => [ - 0 => 0, - 1 => 0 - ], - ], - ], - ], - ], - // expected format of array - [ - 'files' => [ - 'details' => [ - 'avatar' => [ - 0 => new UploadedFile( - __DIR__ . DIRECTORY_SEPARATOR . 'file0.txt', - 'file0.txt', - 'text/plain', - null, - UPLOAD_ERR_OK, - true - ), - 1 => new UploadedFile( - __DIR__ . DIRECTORY_SEPARATOR . 'file1.html', - 'file1.html', - 'text/html', - null, - UPLOAD_ERR_OK, - true - ), - ], - ], - ], - ] - ], - ]; - } - - /** - * @param array $mockEnv An array representing a mock environment. - * - * @return Request - */ - public function requestFactory(array $mockEnv) - { - $env = Environment::mock(); - - $uri = Uri::createFromString('https://example.com:443/foo/bar?abc=123'); - $headers = Headers::createFromGlobals($env); - $cookies = []; - $serverParams = $env->all(); - $body = new RequestBody(); - $uploadedFiles = UploadedFile::createFromGlobals($env); - $request = new Request('GET', $uri, $headers, $cookies, $serverParams, $body, $uploadedFiles); - - return $request; - } -} diff --git a/tests/UriTest.php b/tests/UriTest.php deleted file mode 100644 index 0609169..0000000 --- a/tests/UriTest.php +++ /dev/null @@ -1,612 +0,0 @@ -assertEquals('https', $this->uriFactory()->getScheme()); - } - - public function testWithScheme() - { - $uri = $this->uriFactory()->withScheme('http'); - - $this->assertAttributeEquals('http', 'scheme', $uri); - } - - public function testWithSchemeRemovesSuffix() - { - $uri = $this->uriFactory()->withScheme('http://'); - - $this->assertAttributeEquals('http', 'scheme', $uri); - } - - public function testWithSchemeEmpty() - { - $uri = $this->uriFactory()->withScheme(''); - - $this->assertAttributeEquals('', 'scheme', $uri); - } - - /** - * @expectedException InvalidArgumentException - * @expectedExceptionMessage Uri scheme must be one of: "", "https", "http" - */ - public function testWithSchemeInvalid() - { - $this->uriFactory()->withScheme('ftp'); - } - - /** - * @expectedException InvalidArgumentException - * @expectedExceptionMessage Uri scheme must be a string - */ - public function testWithSchemeInvalidType() - { - $this->uriFactory()->withScheme([]); - } - - /******************************************************************************** - * Authority - *******************************************************************************/ - - public function testGetAuthorityWithUsernameAndPassword() - { - $this->assertEquals('josh:sekrit@example.com', $this->uriFactory()->getAuthority()); - } - - public function testGetAuthorityWithUsername() - { - $uri = Uri::createFromString('https://josh@example.com/foo/bar?abc=123#section3'); - - $this->assertEquals('josh@example.com', $uri->getAuthority()); - } - - public function testGetAuthority() - { - $uri = Uri::createFromString('https://example.com/foo/bar?abc=123#section3'); - - $this->assertEquals('example.com', $uri->getAuthority()); - } - - public function testGetAuthorityWithNonStandardPort() - { - $uri = Uri::createFromString('https://example.com:400/foo/bar?abc=123#section3'); - - $this->assertEquals('example.com:400', $uri->getAuthority()); - } - - public function testGetUserInfoWithUsernameAndPassword() - { - $uri = Uri::createFromString('https://josh:sekrit@example.com:443/foo/bar?abc=123#section3'); - - $this->assertEquals('josh:sekrit', $uri->getUserInfo()); - } - - public function testGetUserInfoWithUsernameAndPasswordEncodesCorrectly() - { - $uri = Uri::createFromString('https://bob%40example.com:pass%3Aword@example.com:443/foo/bar?abc=123#section3'); - - $this->assertEquals('bob%40example.com:pass%3Aword', $uri->getUserInfo()); - } - - public function testGetUserInfoWithUsername() - { - $uri = Uri::createFromString('http://josh@example.com/foo/bar?abc=123#section3'); - - $this->assertEquals('josh', $uri->getUserInfo()); - } - - public function testGetUserInfoNone() - { - $uri = Uri::createFromString('https://example.com/foo/bar?abc=123#section3'); - - $this->assertEquals('', $uri->getUserInfo()); - } - - public function testWithUserInfo() - { - $uri = $this->uriFactory()->withUserInfo('bob', 'pass'); - - $this->assertAttributeEquals('bob', 'user', $uri); - $this->assertAttributeEquals('pass', 'password', $uri); - } - - public function testWithUserInfoEncodesCorrectly() - { - $uri = $this->uriFactory()->withUserInfo('bob@example.com', 'pass:word'); - - $this->assertAttributeEquals('bob%40example.com', 'user', $uri); - $this->assertAttributeEquals('pass%3Aword', 'password', $uri); - } - - public function testWithUserInfoRemovesPassword() - { - $uri = $this->uriFactory()->withUserInfo('bob'); - - $this->assertAttributeEquals('bob', 'user', $uri); - $this->assertAttributeEquals('', 'password', $uri); - } - - public function testWithUserInfoRemovesInfo() - { - $uri = $this->uriFactory()->withUserInfo('bob', 'password'); - - $uri = $uri->withUserInfo(''); - $this->assertAttributeEquals('', 'user', $uri); - $this->assertAttributeEquals('', 'password', $uri); - } - - public function testGetHost() - { - $this->assertEquals('example.com', $this->uriFactory()->getHost()); - } - - public function testWithHost() - { - $uri = $this->uriFactory()->withHost('slimframework.com'); - - $this->assertAttributeEquals('slimframework.com', 'host', $uri); - } - - public function testFilterHost() - { - $uri = new Uri('http', '2001:db8::1'); - - $this->assertEquals('[2001:db8::1]', $uri->getHost()); - } - - public function testGetPortWithSchemeAndNonDefaultPort() - { - $uri = new Uri('https', 'www.example.com', 4000); - - $this->assertEquals(4000, $uri->getPort()); - } - - public function testGetPortWithSchemeAndDefaultPort() - { - $uriHttp = new Uri('http', 'www.example.com', 80); - $uriHttps = new Uri('https', 'www.example.com', 443); - - $this->assertNull($uriHttp->getPort()); - $this->assertNull($uriHttps->getPort()); - } - - public function testGetPortWithoutSchemeAndPort() - { - $uri = new Uri('', 'www.example.com'); - - $this->assertNull($uri->getPort()); - } - - public function testGetPortWithSchemeWithoutPort() - { - $uri = new Uri('http', 'www.example.com'); - - $this->assertNull($uri->getPort()); - } - - public function testWithPort() - { - $uri = $this->uriFactory()->withPort(8000); - - $this->assertAttributeEquals(8000, 'port', $uri); - } - - public function testWithPortNull() - { - $uri = $this->uriFactory()->withPort(null); - - $this->assertAttributeEquals(null, 'port', $uri); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testWithPortInvalidInt() - { - $this->uriFactory()->withPort(70000); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testWithPortInvalidString() - { - $this->uriFactory()->withPort('Foo'); - } - - /******************************************************************************** - * Path - *******************************************************************************/ - - public function testGetPath() - { - $this->assertEquals('/foo/bar', $this->uriFactory()->getPath()); - } - - public function testWithPath() - { - $uri = $this->uriFactory()->withPath('/new'); - - $this->assertAttributeEquals('/new', 'path', $uri); - } - - public function testWithPathWithoutPrefix() - { - $uri = $this->uriFactory()->withPath('new'); - - $this->assertAttributeEquals('new', 'path', $uri); - } - - public function testWithPathEmptyValue() - { - $uri = $this->uriFactory()->withPath(''); - - $this->assertAttributeEquals('', 'path', $uri); - } - - public function testWithPathUrlEncodesInput() - { - $uri = $this->uriFactory()->withPath('/includes?/new'); - - $this->assertAttributeEquals('/includes%3F/new', 'path', $uri); - } - - public function testWithPathDoesNotDoubleEncodeInput() - { - $uri = $this->uriFactory()->withPath('/include%25s/new'); - - $this->assertAttributeEquals('/include%25s/new', 'path', $uri); - } - - /** - * @covers Slim\Http\Uri::withPath - * @expectedException InvalidArgumentException - * @expectedExceptionMessage Uri path must be a string - */ - public function testWithPathInvalidType() - { - $this->uriFactory()->withPath(['foo']); - } - - /******************************************************************************** - * Query - *******************************************************************************/ - - public function testGetQuery() - { - $this->assertEquals('abc=123', $this->uriFactory()->getQuery()); - } - - public function testWithQuery() - { - $uri = $this->uriFactory()->withQuery('xyz=123'); - - $this->assertAttributeEquals('xyz=123', 'query', $uri); - } - - public function testWithQueryRemovesPrefix() - { - $uri = $this->uriFactory()->withQuery('?xyz=123'); - - $this->assertAttributeEquals('xyz=123', 'query', $uri); - } - - public function testWithQueryEmpty() - { - $uri = $this->uriFactory()->withQuery(''); - - $this->assertAttributeEquals('', 'query', $uri); - } - - public function testFilterQuery() - { - $uri = $this->uriFactory()->withQuery('?foobar=%match'); - - $this->assertAttributeEquals('foobar=%25match', 'query', $uri); - } - - /** - * @covers Slim\Http\Uri::withQuery - * @expectedException InvalidArgumentException - * @expectedExceptionMessage Uri query must be a string - */ - public function testWithQueryInvalidType() - { - $this->uriFactory()->withQuery(['foo']); - } - - /******************************************************************************** - * Fragment - *******************************************************************************/ - - public function testGetFragment() - { - $this->assertEquals('section3', $this->uriFactory()->getFragment()); - } - - public function testWithFragment() - { - $uri = $this->uriFactory()->withFragment('other-fragment'); - - $this->assertAttributeEquals('other-fragment', 'fragment', $uri); - } - - public function testWithFragmentRemovesPrefix() - { - $uri = $this->uriFactory()->withFragment('#other-fragment'); - - $this->assertAttributeEquals('other-fragment', 'fragment', $uri); - } - - public function testWithFragmentEmpty() - { - $uri = $this->uriFactory()->withFragment(''); - - $this->assertAttributeEquals('', 'fragment', $uri); - } - - /** - * @covers Slim\Http\Uri::withFragment - * @expectedException InvalidArgumentException - * @expectedExceptionMessage Uri fragment must be a string - */ - public function testWithFragmentInvalidType() - { - $this->uriFactory()->withFragment(['foo']); - } - - /******************************************************************************** - * Helpers - *******************************************************************************/ - - public function testToString() - { - $uri = $this->uriFactory(); - - $this->assertEquals('https://josh:sekrit@example.com/foo/bar?abc=123#section3', (string) $uri); - - $uri = $uri->withPath('bar'); - $this->assertEquals('https://josh:sekrit@example.com/bar?abc=123#section3', (string) $uri); - - $uri = $uri->withPath('/bar'); - $this->assertEquals('https://josh:sekrit@example.com/bar?abc=123#section3', (string) $uri); - } - - /** - * @covers Slim\Http\Uri::createFromString - */ - public function testCreateFromString() - { - $uri = Uri::createFromString('https://example.com:8080/foo/bar?abc=123'); - - $this->assertEquals('https', $uri->getScheme()); - $this->assertEquals('example.com', $uri->getHost()); - $this->assertEquals('8080', $uri->getPort()); - $this->assertEquals('/foo/bar', $uri->getPath()); - $this->assertEquals('abc=123', $uri->getQuery()); - } - - /** - * @covers Slim\Http\Uri::createFromString - * @expectedException InvalidArgumentException - * @expectedExceptionMessage Uri must be a string - */ - public function testCreateFromStringWithInvalidType() - { - Uri::createFromString(['https://example.com:8080/foo/bar?abc=123']); - } - - public function testCreateFromGlobals() - { - $globals = Environment::mock([ - 'SCRIPT_NAME' => '/index.php', - 'REQUEST_URI' => '/foo/bar', - 'PHP_AUTH_USER' => 'josh', - 'PHP_AUTH_PW' => 'sekrit', - 'QUERY_STRING' => 'abc=123', - 'HTTP_HOST' => 'example.com:8080', - 'SERVER_PORT' => 8080, - ]); - - $uri = Uri::createFromGlobals($globals); - - $this->assertEquals('josh:sekrit', $uri->getUserInfo()); - $this->assertEquals('example.com', $uri->getHost()); - $this->assertEquals('8080', $uri->getPort()); - $this->assertEquals('/foo/bar', $uri->getPath()); - $this->assertEquals('abc=123', $uri->getQuery()); - $this->assertEquals('', $uri->getFragment()); - } - - - public function testCreateFromGlobalWithIPv6HostNoPort() - { - $environment = Environment::mock([ - 'SCRIPT_NAME' => '/index.php', - 'REQUEST_URI' => '/foo/bar', - 'PHP_AUTH_USER' => 'josh', - 'PHP_AUTH_PW' => 'sekrit', - 'QUERY_STRING' => 'abc=123', - 'HTTP_HOST' => '[2001:db8::1]', - 'REMOTE_ADDR' => '2001:db8::1', - 'SERVER_PORT' => 8080, - ]); - $uri = Uri::createFromGlobals($environment); - - $this->assertEquals('josh:sekrit', $uri->getUserInfo()); - $this->assertEquals('[2001:db8::1]', $uri->getHost()); - $this->assertEquals('8080', $uri->getPort()); - $this->assertEquals('/foo/bar', $uri->getPath()); - $this->assertEquals('abc=123', $uri->getQuery()); - $this->assertEquals('', $uri->getFragment()); - } - - public function testCreateFromGlobalsWithIPv6HostWithPort() - { - $globals = Environment::mock([ - 'SCRIPT_NAME' => '/index.php', - 'REQUEST_URI' => '/foo/bar', - 'PHP_AUTH_USER' => 'josh', - 'PHP_AUTH_PW' => 'sekrit', - 'QUERY_STRING' => 'abc=123', - 'HTTP_HOST' => '[2001:db8::1]:8080', - 'REMOTE_ADDR' => '2001:db8::1', - 'SERVER_PORT' => 8080, - ]); - - $uri = Uri::createFromGlobals($globals); - - $this->assertEquals('josh:sekrit', $uri->getUserInfo()); - $this->assertEquals('[2001:db8::1]', $uri->getHost()); - $this->assertEquals('8080', $uri->getPort()); - $this->assertEquals('/foo/bar', $uri->getPath()); - $this->assertEquals('abc=123', $uri->getQuery()); - $this->assertEquals('', $uri->getFragment()); - } - - public function testCreateFromGlobalsWithBasePathContainingSpace() - { - $globals = Environment::mock([ - 'SCRIPT_NAME' => "/f'oo bar/index.php", - 'REQUEST_URI' => "/f%27oo%20bar/baz", - ]); - $uri = Uri::createFromGlobals($globals); - - $this->assertEquals('/f%27oo%20bar/baz', $uri->getPath()); - } - - public function testGetBaseUrl() - { - $globals = Environment::mock([ - 'SCRIPT_NAME' => '/foo/index.php', - 'REQUEST_URI' => '/foo/bar', - 'QUERY_STRING' => 'abc=123', - 'HTTP_HOST' => 'example.com:80', - 'SERVER_PORT' => 80 - ]); - $uri = Uri::createFromGlobals($globals); - - $this->assertEquals('http://example.com', $uri->getBaseUrl()); - } - - public function testGetBaseUrlWithNoBasePath() - { - $globals = Environment::mock([ - 'SCRIPT_NAME' => '/index.php', - 'REQUEST_URI' => '/foo/bar', - 'QUERY_STRING' => 'abc=123', - 'HTTP_HOST' => 'example.com:80', - 'SERVER_PORT' => 80 - ]); - $uri = Uri::createFromGlobals($globals); - - $this->assertEquals('http://example.com', $uri->getBaseUrl()); - } - - public function testGetBaseUrlWithAuthority() - { - $globals = Environment::mock([ - 'SCRIPT_NAME' => '/foo/index.php', - 'REQUEST_URI' => '/foo/bar', - 'PHP_AUTH_USER' => 'josh', - 'PHP_AUTH_PW' => 'sekrit', - 'QUERY_STRING' => 'abc=123', - 'HTTP_HOST' => 'example.com:8080', - 'SERVER_PORT' => 8080 - ]); - $uri = Uri::createFromGlobals($globals); - - $this->assertEquals('http://josh:sekrit@example.com:8080', $uri->getBaseUrl()); - } - - /** - * @covers Slim\Http\Uri::createFromGlobals - * @ticket 1380 - */ - public function testWithPathWhenBaseRootIsEmpty() - { - $globals = \Slim\Http\Environment::mock([ - 'SCRIPT_NAME' => '/index.php', - 'REQUEST_URI' => '/bar', - ]); - $uri = \Slim\Http\Uri::createFromGlobals($globals); - - $this->assertEquals('http://localhost/test', (string) $uri->withPath('test')); - } - - /** - * When the URL is /foo/index.php/bar/baz, we need the baseURL to be - * /foo/index.php so that routing works correctly. - * - * @ticket 1639 as a fix to 1590 broke this. - */ - public function testRequestURIContainsIndexDotPhp() - { - $uri = Uri::createFromGlobals( - Environment::mock( - [ - 'SCRIPT_NAME' => '/foo/index.php', - 'REQUEST_URI' => '/foo/index.php/bar/baz', - ] - ) - ); - $this->assertSame('/foo/index.php/bar/baz', $uri->getPath()); - } - - public function testRequestURICanContainParams() - { - $uri = Uri::createFromGlobals( - Environment::mock( - [ - 'REQUEST_URI' => '/foo?abc=123', - ] - ) - ); - $this->assertEquals('abc=123', $uri->getQuery()); - } - - public function testUriDistinguishZeroFromEmptyString() - { - $expected = 'https://0:0@0:1/0?0#0'; - $this->assertSame($expected, (string) Uri::createFromString($expected)); - } - - public function testGetBaseUrlDistinguishZeroFromEmptyString() - { - $expected = 'https://0:0@0:1/0?0#0'; - $this->assertSame('https://0:0@0:1', (string) Uri::createFromString($expected)->getBaseUrl()); - } -} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 0f56b4a..26a796a 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,3 +1,2 @@ 'electrolytes']; - } -}