Issue #2497691 by Crell, cilefen, marvin_B8, dawehner: Include Symfony PSR-7 bridge library
parent
de1930407d
commit
a9dd72b1bd
|
@ -33,7 +33,9 @@
|
|||
"behat/mink": "~1.6",
|
||||
"behat/mink-goutte-driver": "~1.1",
|
||||
"fabpot/goutte": "^2.0.3",
|
||||
"masterminds/html5": "~2.1"
|
||||
"masterminds/html5": "~2.1",
|
||||
"symfony/psr-http-message-bridge": "v0.2",
|
||||
"zendframework/zend-diactoros": "1.1.0"
|
||||
},
|
||||
"replace": {
|
||||
"drupal/action": "self.version",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "9e0763598519826fa345ed30454ca14b",
|
||||
"hash": "ed8b854299d501cf91b057146f2a5b2c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "behat/mink",
|
||||
|
@ -1535,6 +1535,55 @@
|
|||
],
|
||||
"time": "2015-04-02 05:36:41"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-message.git",
|
||||
"reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/85d63699f0dbedb190bbd4b0d2b9dc707ea4c298",
|
||||
"reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP messages",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"time": "2015-05-04 20:22:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "1.0.0",
|
||||
|
@ -2771,6 +2820,60 @@
|
|||
"homepage": "https://symfony.com",
|
||||
"time": "2015-05-15 13:33:16"
|
||||
},
|
||||
{
|
||||
"name": "symfony/psr-http-message-bridge",
|
||||
"version": "v0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/psr-http-message-bridge.git",
|
||||
"reference": "dc7e308e1dc2898a46776e2221a643cb08315453"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/dc7e308e1dc2898a46776e2221a643cb08315453",
|
||||
"reference": "dc7e308e1dc2898a46776e2221a643cb08315453",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3",
|
||||
"psr/http-message": "~1.0",
|
||||
"symfony/http-foundation": "~2.3|~3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "~2.7|~3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"zendframework/zend-diactoros": "To use the Zend Diactoros factory"
|
||||
},
|
||||
"type": "symfony-bridge",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Bridge\\PsrHttpMessage\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
},
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
}
|
||||
],
|
||||
"description": "PSR HTTP message bridge",
|
||||
"homepage": "http://symfony.com",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr-7"
|
||||
],
|
||||
"time": "2015-05-29 17:57:12"
|
||||
},
|
||||
{
|
||||
"name": "symfony/routing",
|
||||
"version": "v2.7.0",
|
||||
|
@ -3140,6 +3243,56 @@
|
|||
],
|
||||
"time": "2015-04-19 08:30:27"
|
||||
},
|
||||
{
|
||||
"name": "zendframework/zend-diactoros",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zendframework/zend-diactoros.git",
|
||||
"reference": "df65f70fc36f24d51a90ad706a09cd9b74fc4dd8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/df65f70fc36f24d51a90ad706a09cd9b74fc4dd8",
|
||||
"reference": "df65f70fc36f24d51a90ad706a09cd9b74fc4dd8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4",
|
||||
"psr/http-message": "~1.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-message-implementation": "~1.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.6",
|
||||
"squizlabs/php_codesniffer": "^2.3.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev",
|
||||
"dev-develop": "1.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Zend\\Diactoros\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-2-Clause"
|
||||
],
|
||||
"description": "PSR HTTP Message implementations",
|
||||
"homepage": "https://github.com/zendframework/zend-diactoros",
|
||||
"keywords": [
|
||||
"http",
|
||||
"psr",
|
||||
"psr-7"
|
||||
],
|
||||
"time": "2015-06-24 20:42:54"
|
||||
},
|
||||
{
|
||||
"name": "zendframework/zend-escaper",
|
||||
"version": "2.4.0",
|
||||
|
|
|
@ -596,6 +596,10 @@ services:
|
|||
- { name: http_middleware, priority: 50 }
|
||||
calls:
|
||||
- [setContainer, ['@service_container']]
|
||||
psr7.http_foundation_factory:
|
||||
class: Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory
|
||||
psr7.http_message_factory:
|
||||
class: Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory
|
||||
language_manager:
|
||||
class: Drupal\Core\Language\LanguageManager
|
||||
arguments: ['@language.default']
|
||||
|
|
|
@ -9,6 +9,7 @@ return array(
|
|||
'Zend\\Stdlib\\' => array($vendorDir . '/zendframework/zend-stdlib'),
|
||||
'Zend\\Feed\\' => array($vendorDir . '/zendframework/zend-feed'),
|
||||
'Zend\\Escaper\\' => array($vendorDir . '/zendframework/zend-escaper'),
|
||||
'Zend\\Diactoros\\' => array($vendorDir . '/zendframework/zend-diactoros/src'),
|
||||
'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'),
|
||||
'Symfony\\Component\\Validator\\' => array($vendorDir . '/symfony/validator'),
|
||||
'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
|
||||
|
@ -26,7 +27,9 @@ return array(
|
|||
'Symfony\\Component\\ClassLoader\\' => array($vendorDir . '/symfony/class-loader'),
|
||||
'Symfony\\Component\\BrowserKit\\' => array($vendorDir . '/symfony/browser-kit'),
|
||||
'Symfony\\Cmf\\Component\\Routing\\' => array($vendorDir . '/symfony-cmf/routing'),
|
||||
'Symfony\\Bridge\\PsrHttpMessage\\' => array($vendorDir . '/symfony/psr-http-message-bridge'),
|
||||
'React\\Promise\\' => array($vendorDir . '/react/promise/src'),
|
||||
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
|
||||
'Masterminds\\' => array($vendorDir . '/masterminds/html5/src'),
|
||||
'GuzzleHttp\\Stream\\' => array($vendorDir . '/guzzlehttp/streams/src'),
|
||||
'GuzzleHttp\\Ring\\' => array($vendorDir . '/guzzlehttp/ringphp/src'),
|
||||
|
|
|
@ -3399,5 +3399,164 @@
|
|||
"lexer",
|
||||
"parser"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "1.0",
|
||||
"version_normalized": "1.0.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-message.git",
|
||||
"reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/85d63699f0dbedb190bbd4b0d2b9dc707ea4c298",
|
||||
"reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"time": "2015-05-04 20:22:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP messages",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "symfony/psr-http-message-bridge",
|
||||
"version": "v0.2",
|
||||
"version_normalized": "0.2.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/psr-http-message-bridge.git",
|
||||
"reference": "dc7e308e1dc2898a46776e2221a643cb08315453"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/dc7e308e1dc2898a46776e2221a643cb08315453",
|
||||
"reference": "dc7e308e1dc2898a46776e2221a643cb08315453",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3",
|
||||
"psr/http-message": "~1.0",
|
||||
"symfony/http-foundation": "~2.3|~3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "~2.7|~3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"zendframework/zend-diactoros": "To use the Zend Diactoros factory"
|
||||
},
|
||||
"time": "2015-05-29 17:57:12",
|
||||
"type": "symfony-bridge",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Bridge\\PsrHttpMessage\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
},
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
}
|
||||
],
|
||||
"description": "PSR HTTP message bridge",
|
||||
"homepage": "http://symfony.com",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr-7"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "zendframework/zend-diactoros",
|
||||
"version": "1.1.0",
|
||||
"version_normalized": "1.1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zendframework/zend-diactoros.git",
|
||||
"reference": "df65f70fc36f24d51a90ad706a09cd9b74fc4dd8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/df65f70fc36f24d51a90ad706a09cd9b74fc4dd8",
|
||||
"reference": "df65f70fc36f24d51a90ad706a09cd9b74fc4dd8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4",
|
||||
"psr/http-message": "~1.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-message-implementation": "~1.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.6",
|
||||
"squizlabs/php_codesniffer": "^2.3.1"
|
||||
},
|
||||
"time": "2015-06-24 20:42:54",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev",
|
||||
"dev-develop": "1.1-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Zend\\Diactoros\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-2-Clause"
|
||||
],
|
||||
"description": "PSR HTTP Message implementations",
|
||||
"homepage": "https://github.com/zendframework/zend-diactoros",
|
||||
"keywords": [
|
||||
"http",
|
||||
"psr",
|
||||
"psr-7"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2014 PHP Framework Interoperability Group
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,13 @@
|
|||
PSR Http Message
|
||||
================
|
||||
|
||||
This repository holds all interfaces/classes/traits related to
|
||||
[PSR-7](http://www.php-fig.org/psr/psr-7/).
|
||||
|
||||
Note that this is not a HTTP message implementation of its own. It is merely an
|
||||
interface that describes a HTTP message. See the specification for more details.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
We'll certainly need some stuff in here.
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "psr/http-message",
|
||||
"description": "Common interface for HTTP messages",
|
||||
"keywords": ["psr", "psr-7", "http", "http-message", "request", "response"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
|
||||
namespace Psr\Http\Message;
|
||||
|
||||
/**
|
||||
* HTTP messages consist of requests from a client to a server and responses
|
||||
* from a server to a client. This interface defines the methods common to
|
||||
* each.
|
||||
*
|
||||
* Messages are considered immutable; all methods that might change state MUST
|
||||
* be implemented such that they retain the internal state of the current
|
||||
* message and return an instance that contains the changed state.
|
||||
*
|
||||
* @link http://www.ietf.org/rfc/rfc7230.txt
|
||||
* @link http://www.ietf.org/rfc/rfc7231.txt
|
||||
*/
|
||||
interface MessageInterface
|
||||
{
|
||||
/**
|
||||
* 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 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 self
|
||||
*/
|
||||
public function withProtocolVersion($version);
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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 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 self
|
||||
* @throws \InvalidArgumentException for invalid header names or values.
|
||||
*/
|
||||
public function withHeader($name, $value);
|
||||
|
||||
/**
|
||||
* 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 self
|
||||
* @throws \InvalidArgumentException for invalid header names or values.
|
||||
*/
|
||||
public function withAddedHeader($name, $value);
|
||||
|
||||
/**
|
||||
* 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 self
|
||||
*/
|
||||
public function withoutHeader($name);
|
||||
|
||||
/**
|
||||
* Gets the body of the message.
|
||||
*
|
||||
* @return StreamInterface Returns the body as a stream.
|
||||
*/
|
||||
public function getBody();
|
||||
|
||||
/**
|
||||
* 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 self
|
||||
* @throws \InvalidArgumentException When the body is not valid.
|
||||
*/
|
||||
public function withBody(StreamInterface $body);
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
namespace Psr\Http\Message;
|
||||
|
||||
/**
|
||||
* Representation of an outgoing, client-side request.
|
||||
*
|
||||
* Per the HTTP specification, this interface includes properties for
|
||||
* each of the following:
|
||||
*
|
||||
* - Protocol version
|
||||
* - HTTP method
|
||||
* - URI
|
||||
* - Headers
|
||||
* - Message body
|
||||
*
|
||||
* During construction, implementations MUST attempt to set the Host header from
|
||||
* a provided URI if no Host header is provided.
|
||||
*
|
||||
* Requests are considered immutable; all methods that might change state MUST
|
||||
* be implemented such that they retain the internal state of the current
|
||||
* message and return an instance that contains the changed state.
|
||||
*/
|
||||
interface RequestInterface extends MessageInterface
|
||||
{
|
||||
/**
|
||||
* Retrieves the message's request target.
|
||||
*
|
||||
* Retrieves the message's request-target either as it will appear (for
|
||||
* clients), as it appeared at request (for servers), or as it was
|
||||
* specified for the instance (see withRequestTarget()).
|
||||
*
|
||||
* In most cases, this will be the origin-form of the composed URI,
|
||||
* unless a value was provided to the concrete implementation (see
|
||||
* withRequestTarget() below).
|
||||
*
|
||||
* If no URI is available, and no request-target has been specifically
|
||||
* provided, this method MUST return the string "/".
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRequestTarget();
|
||||
|
||||
/**
|
||||
* Return an instance with the specific request-target.
|
||||
*
|
||||
* 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 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.
|
||||
*
|
||||
* @link http://tools.ietf.org/html/rfc7230#section-2.7 (for the various
|
||||
* request-target forms allowed in request messages)
|
||||
* @param mixed $requestTarget
|
||||
* @return self
|
||||
*/
|
||||
public function withRequestTarget($requestTarget);
|
||||
|
||||
/**
|
||||
* Retrieves the HTTP method of the request.
|
||||
*
|
||||
* @return string Returns the request method.
|
||||
*/
|
||||
public function getMethod();
|
||||
|
||||
/**
|
||||
* 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 self
|
||||
* @throws \InvalidArgumentException for invalid HTTP methods.
|
||||
*/
|
||||
public function withMethod($method);
|
||||
|
||||
/**
|
||||
* Retrieves the URI instance.
|
||||
*
|
||||
* This method MUST return a UriInterface instance.
|
||||
*
|
||||
* @link http://tools.ietf.org/html/rfc3986#section-4.3
|
||||
* @return UriInterface Returns a UriInterface instance
|
||||
* representing the URI of the request.
|
||||
*/
|
||||
public function 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.
|
||||
*
|
||||
* 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 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 self
|
||||
*/
|
||||
public function withUri(UriInterface $uri, $preserveHost = false);
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace Psr\Http\Message;
|
||||
|
||||
/**
|
||||
* Representation of an outgoing, server-side response.
|
||||
*
|
||||
* Per the HTTP specification, this interface includes properties for
|
||||
* each of the following:
|
||||
*
|
||||
* - Protocol version
|
||||
* - Status code and reason phrase
|
||||
* - Headers
|
||||
* - Message body
|
||||
*
|
||||
* Responses are considered immutable; all methods that might change state MUST
|
||||
* be implemented such that they retain the internal state of the current
|
||||
* message and return an instance that contains the changed state.
|
||||
*/
|
||||
interface ResponseInterface extends MessageInterface
|
||||
{
|
||||
/**
|
||||
* 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 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 self
|
||||
* @throws \InvalidArgumentException For invalid status code arguments.
|
||||
*/
|
||||
public function withStatus($code, $reasonPhrase = '');
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
<?php
|
||||
|
||||
namespace Psr\Http\Message;
|
||||
|
||||
/**
|
||||
* Representation of an incoming, server-side HTTP request.
|
||||
*
|
||||
* Per the HTTP specification, this interface includes properties for
|
||||
* each of the following:
|
||||
*
|
||||
* - Protocol version
|
||||
* - HTTP method
|
||||
* - URI
|
||||
* - Headers
|
||||
* - Message body
|
||||
*
|
||||
* Additionally, it encapsulates all data as it has arrived to the
|
||||
* application from the CGI and/or PHP environment, including:
|
||||
*
|
||||
* - The values represented in $_SERVER.
|
||||
* - Any cookies provided (generally via $_COOKIE)
|
||||
* - Query string arguments (generally via $_GET, or as parsed via parse_str())
|
||||
* - Upload files, if any (as represented by $_FILES)
|
||||
* - Deserialized body parameters (generally from $_POST)
|
||||
*
|
||||
* $_SERVER values MUST be treated as immutable, as they represent application
|
||||
* state at the time of request; as such, no methods are provided to allow
|
||||
* modification of those values. The other values provide such methods, as they
|
||||
* can be restored from $_SERVER or the request body, and may need treatment
|
||||
* during the application (e.g., body parameters may be deserialized based on
|
||||
* content type).
|
||||
*
|
||||
* Additionally, this interface recognizes the utility of introspecting a
|
||||
* request to derive and match additional parameters (e.g., via URI path
|
||||
* matching, decrypting cookie values, deserializing non-form-encoded body
|
||||
* content, matching authorization headers to users, etc). These parameters
|
||||
* are stored in an "attributes" property.
|
||||
*
|
||||
* Requests are considered immutable; all methods that might change state MUST
|
||||
* be implemented such that they retain the internal state of the current
|
||||
* message and return an instance that contains the changed state.
|
||||
*/
|
||||
interface ServerRequestInterface extends RequestInterface
|
||||
{
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* Retrieve cookies.
|
||||
*
|
||||
* Retrieves cookies sent by the client to the server.
|
||||
*
|
||||
* The data MUST be compatible with the structure of the $_COOKIE
|
||||
* superglobal.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCookieParams();
|
||||
|
||||
/**
|
||||
* Return an instance with the specified cookies.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 self
|
||||
*/
|
||||
public function withCookieParams(array $cookies);
|
||||
|
||||
/**
|
||||
* Retrieve query string arguments.
|
||||
*
|
||||
* Retrieves the deserialized query string arguments, if any.
|
||||
*
|
||||
* 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 getQueryParams();
|
||||
|
||||
/**
|
||||
* Return an instance with the specified query string arguments.
|
||||
*
|
||||
* These values SHOULD remain immutable over the course of the incoming
|
||||
* request. They MAY be injected during instantiation, such as from PHP's
|
||||
* $_GET superglobal, or MAY be derived from some other value such as the
|
||||
* URI. In cases where the arguments are parsed from the URI, the data
|
||||
* MUST be compatible with what PHP's parse_str() would return for
|
||||
* purposes of how duplicate query parameters are handled, and how nested
|
||||
* sets are handled.
|
||||
*
|
||||
* Setting query string arguments MUST NOT change the URI stored by the
|
||||
* request, nor the 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 query string arguments.
|
||||
*
|
||||
* @param array $query Array of query string arguments, typically from
|
||||
* $_GET.
|
||||
* @return self
|
||||
*/
|
||||
public function withQueryParams(array $query);
|
||||
|
||||
/**
|
||||
* Retrieve normalized file upload data.
|
||||
*
|
||||
* This method returns upload metadata in a normalized tree, with each leaf
|
||||
* an instance of Psr\Http\Message\UploadedFileInterface.
|
||||
*
|
||||
* These values MAY be prepared from $_FILES or the message body during
|
||||
* instantiation, or MAY be injected via withUploadedFiles().
|
||||
*
|
||||
* @return array An array tree of UploadedFileInterface instances; an empty
|
||||
* array MUST be returned if no data is present.
|
||||
*/
|
||||
public function getUploadedFiles();
|
||||
|
||||
/**
|
||||
* Create a new instance with the specified uploaded files.
|
||||
*
|
||||
* 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 array An array tree of UploadedFileInterface instances.
|
||||
* @return self
|
||||
* @throws \InvalidArgumentException if an invalid structure is provided.
|
||||
*/
|
||||
public function withUploadedFiles(array $uploadedFiles);
|
||||
|
||||
/**
|
||||
* Retrieve any parameters provided in the request body.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 getParsedBody();
|
||||
|
||||
/**
|
||||
* Return an instance with the specified body parameters.
|
||||
*
|
||||
* These MAY be injected during instantiation.
|
||||
*
|
||||
* 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 body parameters.
|
||||
*
|
||||
* @param null|array|object $data The deserialized body data. This will
|
||||
* typically be in an array or object.
|
||||
* @return self
|
||||
* @throws \InvalidArgumentException if an unsupported argument type is
|
||||
* provided.
|
||||
*/
|
||||
public function withParsedBody($data);
|
||||
|
||||
/**
|
||||
* Retrieve attributes derived from the request.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @return array Attributes derived from the request.
|
||||
*/
|
||||
public function getAttributes();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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 getAttribute($name, $default = null);
|
||||
|
||||
/**
|
||||
* Return an instance with the specified derived request attribute.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @see getAttributes()
|
||||
* @param string $name The attribute name.
|
||||
* @param mixed $value The value of the attribute.
|
||||
* @return self
|
||||
*/
|
||||
public function withAttribute($name, $value);
|
||||
|
||||
/**
|
||||
* Return an instance that removes the specified derived request attribute.
|
||||
*
|
||||
* This method allows removing 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 removes
|
||||
* the attribute.
|
||||
*
|
||||
* @see getAttributes()
|
||||
* @param string $name The attribute name.
|
||||
* @return self
|
||||
*/
|
||||
public function withoutAttribute($name);
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
namespace Psr\Http\Message;
|
||||
|
||||
/**
|
||||
* Describes a data stream.
|
||||
*
|
||||
* Typically, an instance will wrap a PHP stream; this interface provides
|
||||
* a wrapper around the most common operations, including serialization of
|
||||
* the entire stream to a string.
|
||||
*/
|
||||
interface StreamInterface
|
||||
{
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* Closes the stream and any underlying resources.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function close();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* Returns the current position of the file read/write pointer
|
||||
*
|
||||
* @return int Position of the file pointer
|
||||
* @throws \RuntimeException on error.
|
||||
*/
|
||||
public function tell();
|
||||
|
||||
/**
|
||||
* Returns true if the stream is at the end of the stream.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function eof();
|
||||
|
||||
/**
|
||||
* Returns whether or not the stream is seekable.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSeekable();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* Returns whether or not the stream is writable.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isWritable();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Returns whether or not the stream is readable.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isReadable();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Returns the remaining contents in a string
|
||||
*
|
||||
* @return string
|
||||
* @throws \RuntimeException if unable to read or an error occurs while
|
||||
* reading.
|
||||
*/
|
||||
public function getContents();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
|
||||
namespace Psr\Http\Message;
|
||||
|
||||
/**
|
||||
* Value object representing a file uploaded through an HTTP request.
|
||||
*
|
||||
* Instances of this interface are considered immutable; all methods that
|
||||
* might change state MUST be implemented such that they retain the internal
|
||||
* state of the current instance and return an instance that contains the
|
||||
* changed state.
|
||||
*/
|
||||
interface UploadedFileInterface
|
||||
{
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
|
@ -0,0 +1,323 @@
|
|||
<?php
|
||||
namespace Psr\Http\Message;
|
||||
|
||||
/**
|
||||
* Value object representing a URI.
|
||||
*
|
||||
* This interface is meant to represent URIs according to RFC 3986 and to
|
||||
* provide methods for most common operations. Additional functionality for
|
||||
* working with URIs can be provided on top of the interface or externally.
|
||||
* Its primary use is for HTTP requests, but may also be used in other
|
||||
* contexts.
|
||||
*
|
||||
* Instances of this interface are considered immutable; all methods that
|
||||
* might change state MUST be implemented such that they retain the internal
|
||||
* state of the current instance and return an instance that contains the
|
||||
* changed state.
|
||||
*
|
||||
* Typically the Host header will be also be present in the request message.
|
||||
* For server-side requests, the scheme will typically be discoverable in the
|
||||
* server parameters.
|
||||
*
|
||||
* @link http://tools.ietf.org/html/rfc3986 (the URI specification)
|
||||
*/
|
||||
interface UriInterface
|
||||
{
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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:
|
||||
*
|
||||
* <pre>
|
||||
* [user-info@]host[:port]
|
||||
* </pre>
|
||||
*
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
language: php
|
||||
|
||||
sudo: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.3
|
||||
- php: 5.4
|
||||
- php: 5.5
|
||||
- php: 5.6
|
||||
- php: 5.3
|
||||
env: deps=low
|
||||
- php: 5.6
|
||||
env: deps=high
|
||||
- php: nightly
|
||||
- php: hhvm
|
||||
allow_failures:
|
||||
- php: nightly
|
||||
- php: hhvm
|
||||
fast_finish: true
|
||||
|
||||
env:
|
||||
global:
|
||||
- deps=no
|
||||
- SYMFONY_DEPRECATIONS_HELPER=weak
|
||||
|
||||
before_install:
|
||||
- composer self-update
|
||||
- if [[ "$TRAVIS_PHP_VERSION" != "nightly" ]] && [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then phpenv config-rm xdebug.ini; fi;
|
||||
- if [[ "$TRAVIS_PHP_VERSION" != "nightly" ]] && [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]] && [ $(php -r "echo PHP_MINOR_VERSION;") -le 4 ]; then echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
|
||||
- if [[ "$TRAVIS_PHP_VERSION" != "nightly" ]] && [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then php -i; fi;
|
||||
# Set the COMPOSER_ROOT_VERSION to the right version according to the branch being built
|
||||
- if [ "$TRAVIS_BRANCH" = "master" ]; then export COMPOSER_ROOT_VERSION=dev-master; else export COMPOSER_ROOT_VERSION="$TRAVIS_BRANCH".x-dev; fi;
|
||||
|
||||
install:
|
||||
- if [[ "$TRAVIS_PHP_VERSION" != "5.3" ]] && [[ "$TRAVIS_PHP_VERSION" != "5.4" ]]; then composer require --no-update zendframework/zend-diactoros; fi;
|
||||
- if [ "$deps" = "no" ]; then export SYMFONY_DEPRECATIONS_HELPER=strict; fi;
|
||||
- if [ "$deps" = "no" ]; then composer --prefer-source install; fi;
|
||||
- if [ "$deps" = "high" ]; then composer --prefer-source update; fi;
|
||||
- if [ "$deps" = "low" ]; then composer --prefer-source --prefer-lowest --prefer-stable update; fi;
|
||||
|
||||
script:
|
||||
- phpunit
|
|
@ -0,0 +1,7 @@
|
|||
CHANGELOG
|
||||
=========
|
||||
|
||||
2.8.0
|
||||
-----
|
||||
|
||||
* added the component
|
|
@ -0,0 +1,164 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\PsrHttpMessage\Factory;
|
||||
|
||||
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Zend\Diactoros\Response as DiactorosResponse;
|
||||
use Zend\Diactoros\ServerRequest;
|
||||
use Zend\Diactoros\ServerRequestFactory as DiactorosRequestFactory;
|
||||
use Zend\Diactoros\Stream as DiactorosStream;
|
||||
use Zend\Diactoros\UploadedFile as DiactorosUploadedFile;
|
||||
|
||||
/**
|
||||
* Builds Psr\HttpMessage instances using the Zend Diactoros implementation.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class DiactorosFactory implements HttpMessageFactoryInterface
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
if (!class_exists('Zend\Diactoros\ServerRequestFactory')) {
|
||||
throw new \RuntimeException('Zend Diactoros must be installed to use the DiactorosFactory.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createRequest(Request $symfonyRequest)
|
||||
{
|
||||
$server = DiactorosRequestFactory::normalizeServer($symfonyRequest->server->all());
|
||||
$headers = $symfonyRequest->headers->all();
|
||||
|
||||
try {
|
||||
$body = new DiactorosStream($symfonyRequest->getContent(true));
|
||||
} catch (\LogicException $e) {
|
||||
$body = new DiactorosStream('php://temp', 'wb+');
|
||||
$body->write($symfonyRequest->getContent());
|
||||
}
|
||||
|
||||
$request = new ServerRequest(
|
||||
$server,
|
||||
DiactorosRequestFactory::normalizeFiles($this->getFiles($symfonyRequest->files->all())),
|
||||
$symfonyRequest->getUri(),
|
||||
$symfonyRequest->getMethod(),
|
||||
$body,
|
||||
$headers
|
||||
);
|
||||
|
||||
$request = $request
|
||||
->withCookieParams($symfonyRequest->cookies->all())
|
||||
->withQueryParams($symfonyRequest->query->all())
|
||||
->withParsedBody($symfonyRequest->request->all())
|
||||
;
|
||||
|
||||
foreach ($symfonyRequest->attributes->all() as $key => $value) {
|
||||
$request = $request->withAttribute($key, $value);
|
||||
}
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts Symfony uploaded files array to the PSR one.
|
||||
*
|
||||
* @param array $uploadedFiles
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getFiles(array $uploadedFiles)
|
||||
{
|
||||
$files = array();
|
||||
|
||||
foreach ($uploadedFiles as $key => $value) {
|
||||
if ($value instanceof UploadedFile) {
|
||||
$files[$key] = $this->createUploadedFile($value);
|
||||
} else {
|
||||
$files[$key] = $this->getFiles($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PSR-7 UploadedFile instance from a Symfony one.
|
||||
*
|
||||
* @param UploadedFile $symfonyUploadedFile
|
||||
*
|
||||
* @return UploadedFileInterface
|
||||
*/
|
||||
private function createUploadedFile(UploadedFile $symfonyUploadedFile)
|
||||
{
|
||||
return new DiactorosUploadedFile(
|
||||
$symfonyUploadedFile->getRealPath(),
|
||||
$symfonyUploadedFile->getSize(),
|
||||
$symfonyUploadedFile->getError(),
|
||||
$symfonyUploadedFile->getClientOriginalName(),
|
||||
$symfonyUploadedFile->getClientMimeType()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createResponse(Response $symfonyResponse)
|
||||
{
|
||||
if ($symfonyResponse instanceof BinaryFileResponse) {
|
||||
$stream = new DiactorosStream($symfonyResponse->getFile()->getPathname(), 'r');
|
||||
} else {
|
||||
$stream = new DiactorosStream('php://temp', 'wb+');
|
||||
if ($symfonyResponse instanceof StreamedResponse) {
|
||||
ob_start(function ($buffer) use ($stream) {
|
||||
$stream->write($buffer);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$symfonyResponse->sendContent();
|
||||
ob_end_clean();
|
||||
} else {
|
||||
$stream->write($symfonyResponse->getContent());
|
||||
}
|
||||
}
|
||||
|
||||
$headers = $symfonyResponse->headers->all();
|
||||
|
||||
$cookies = $symfonyResponse->headers->getCookies();
|
||||
if (!empty($cookies)) {
|
||||
$headers['Set-Cookie'] = array();
|
||||
|
||||
foreach ($cookies as $cookie) {
|
||||
$headers['Set-Cookie'][] = $cookie->__toString();
|
||||
}
|
||||
}
|
||||
|
||||
$response = new DiactorosResponse(
|
||||
$stream,
|
||||
$symfonyResponse->getStatusCode(),
|
||||
$headers
|
||||
);
|
||||
|
||||
$protocolVersion = $symfonyResponse->getProtocolVersion();
|
||||
if ('1.1' !== $protocolVersion) {
|
||||
$response = $response->withProtocolVersion($protocolVersion);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
199
core/vendor/symfony/psr-http-message-bridge/Factory/HttpFoundationFactory.php
vendored
Normal file
199
core/vendor/symfony/psr-http-message-bridge/Factory/HttpFoundationFactory.php
vendored
Normal file
|
@ -0,0 +1,199 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\PsrHttpMessage\Factory;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class HttpFoundationFactory implements HttpFoundationFactoryInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createRequest(ServerRequestInterface $psrRequest)
|
||||
{
|
||||
$parsedBody = $psrRequest->getParsedBody();
|
||||
$parsedBody = is_array($parsedBody) ? $parsedBody : array();
|
||||
|
||||
$request = new Request(
|
||||
$psrRequest->getQueryParams(),
|
||||
$parsedBody,
|
||||
$psrRequest->getAttributes(),
|
||||
$psrRequest->getCookieParams(),
|
||||
$this->getFiles($psrRequest->getUploadedFiles()),
|
||||
$psrRequest->getServerParams(),
|
||||
$psrRequest->getBody()->__toString()
|
||||
);
|
||||
$request->headers->replace($psrRequest->getHeaders());
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts to the input array to $_FILES structure.
|
||||
*
|
||||
* @param array $uploadedFiles
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getFiles(array $uploadedFiles)
|
||||
{
|
||||
$files = array();
|
||||
|
||||
foreach ($uploadedFiles as $key => $value) {
|
||||
if ($value instanceof UploadedFileInterface) {
|
||||
$files[$key] = $this->createUploadedFile($value);
|
||||
} else {
|
||||
$files[$key] = $this->getFiles($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Symfony UploadedFile instance from PSR-7 ones.
|
||||
*
|
||||
* @param UploadedFileInterface $psrUploadedFile
|
||||
*
|
||||
* @return UploadedFile
|
||||
*/
|
||||
private function createUploadedFile(UploadedFileInterface $psrUploadedFile)
|
||||
{
|
||||
$temporaryPath = $this->getTemporaryPath();
|
||||
$psrUploadedFile->moveTo($temporaryPath);
|
||||
|
||||
$clientFileName = $psrUploadedFile->getClientFilename();
|
||||
|
||||
return new UploadedFile(
|
||||
$temporaryPath,
|
||||
null === $clientFileName ? '' : $clientFileName,
|
||||
$psrUploadedFile->getClientMediaType(),
|
||||
$psrUploadedFile->getSize(),
|
||||
$psrUploadedFile->getError(),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a temporary file path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getTemporaryPath()
|
||||
{
|
||||
return tempnam(sys_get_temp_dir(), uniqid('symfony', true));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createResponse(ResponseInterface $psrResponse)
|
||||
{
|
||||
$response = new Response(
|
||||
$psrResponse->getBody()->__toString(),
|
||||
$psrResponse->getStatusCode(),
|
||||
$psrResponse->getHeaders()
|
||||
);
|
||||
$response->setProtocolVersion($psrResponse->getProtocolVersion());
|
||||
|
||||
foreach ($psrResponse->getHeader('Set-Cookie') as $cookie) {
|
||||
$response->headers->setCookie($this->createCookie($cookie));
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Cookie instance from a cookie string.
|
||||
*
|
||||
* Some snippets have been taken from the Guzzle project: https://github.com/guzzle/guzzle/blob/5.3/src/Cookie/SetCookie.php#L34
|
||||
*
|
||||
* @param string $cookie
|
||||
*
|
||||
* @return Cookie
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function createCookie($cookie)
|
||||
{
|
||||
foreach (explode(';', $cookie) as $part) {
|
||||
$part = trim($part);
|
||||
|
||||
$data = explode('=', $part, 2);
|
||||
$name = $data[0];
|
||||
$value = isset($data[1]) ? trim($data[1], " \n\r\t\0\x0B\"") : null;
|
||||
|
||||
if (!isset($cookieName)) {
|
||||
$cookieName = $name;
|
||||
$cookieValue = $value;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('expires' === strtolower($name) && null !== $value) {
|
||||
$cookieExpire = new \DateTime($value);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('path' === strtolower($name) && null !== $value) {
|
||||
$cookiePath = $value;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('domain' === strtolower($name) && null !== $value) {
|
||||
$cookieDomain = $value;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('secure' === strtolower($name)) {
|
||||
$cookieSecure = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('httponly' === strtolower($name)) {
|
||||
$cookieHttpOnly = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($cookieName)) {
|
||||
throw new \InvalidArgumentException('The value of the Set-Cookie header is malformed.');
|
||||
}
|
||||
|
||||
return new Cookie(
|
||||
$cookieName,
|
||||
$cookieValue,
|
||||
isset($cookieExpire) ? $cookieExpire : 0,
|
||||
isset($cookiePath) ? $cookiePath : '/',
|
||||
isset($cookieDomain) ? $cookieDomain : null,
|
||||
isset($cookieSecure),
|
||||
isset($cookieHttpOnly)
|
||||
);
|
||||
}
|
||||
}
|
43
core/vendor/symfony/psr-http-message-bridge/HttpFoundationFactoryInterface.php
vendored
Normal file
43
core/vendor/symfony/psr-http-message-bridge/HttpFoundationFactoryInterface.php
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\PsrHttpMessage;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Creates Symfony Request and Response instances from PSR-7 ones.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface HttpFoundationFactoryInterface
|
||||
{
|
||||
/**
|
||||
* Creates a Symfony Request instance from a PSR-7 one.
|
||||
*
|
||||
* @param ServerRequestInterface $psrRequest
|
||||
*
|
||||
* @return Request
|
||||
*/
|
||||
public function createRequest(ServerRequestInterface $psrRequest);
|
||||
|
||||
/**
|
||||
* Creates a Symfony Response instance from a PSR-7 one.
|
||||
*
|
||||
* @param ResponseInterface $psrResponse
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function createResponse(ResponseInterface $psrResponse);
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\PsrHttpMessage;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Creates PSR HTTP Request and Response instances from Symfony ones.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface HttpMessageFactoryInterface
|
||||
{
|
||||
/**
|
||||
* Creates a PSR-7 Request instance from a Symfony one.
|
||||
*
|
||||
* @param Request $symfonyRequest
|
||||
*
|
||||
* @return ServerRequestInterface
|
||||
*/
|
||||
public function createRequest(Request $symfonyRequest);
|
||||
|
||||
/**
|
||||
* Creates a PSR-7 Response instance from a Symfony one.
|
||||
*
|
||||
* @param Response $symfonyResponse
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function createResponse(Response $symfonyResponse);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2004-2015 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,14 @@
|
|||
PSR-7 Bridge
|
||||
============
|
||||
|
||||
Provides integration for PSR7.
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
If you want to run the unit tests, install dev dependencies before
|
||||
running PHPUnit:
|
||||
|
||||
$ cd path/to/Symfony/Bridge/PsrHttpMessage/
|
||||
$ composer.phar install
|
||||
$ phpunit
|
164
core/vendor/symfony/psr-http-message-bridge/Tests/Factory/DiactorosFactoryTest.php
vendored
Normal file
164
core/vendor/symfony/psr-http-message-bridge/Tests/Factory/DiactorosFactoryTest.php
vendored
Normal file
|
@ -0,0 +1,164 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\PsrHttpMessage\Tests\Factory;
|
||||
|
||||
use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class DiactorosFactoryTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $factory;
|
||||
private $tmpDir;
|
||||
|
||||
public function setup()
|
||||
{
|
||||
if (!class_exists('Zend\Diactoros\ServerRequestFactory')) {
|
||||
$this->markTestSkipped('Zend Diactoros is not installed.');
|
||||
}
|
||||
|
||||
$this->factory = new DiactorosFactory();
|
||||
$this->tmpDir = sys_get_temp_dir();
|
||||
}
|
||||
|
||||
public function testCreateRequest()
|
||||
{
|
||||
$stdClass = new \stdClass();
|
||||
$request = new Request(
|
||||
array(
|
||||
'foo' => '1',
|
||||
'bar' => array('baz' => '42'),
|
||||
),
|
||||
array(
|
||||
'twitter' => array(
|
||||
'@dunglas' => 'Kévin Dunglas',
|
||||
'@coopTilleuls' => 'Les-Tilleuls.coop',
|
||||
),
|
||||
'baz' => '2',
|
||||
),
|
||||
array(
|
||||
'a1' => $stdClass,
|
||||
'a2' => array('foo' => 'bar'),
|
||||
),
|
||||
array(
|
||||
'c1' => 'foo',
|
||||
'c2' => array('c3' => 'bar'),
|
||||
),
|
||||
array(
|
||||
'f1' => $this->createUploadedFile('F1', 'f1.txt', 'text/plain', UPLOAD_ERR_OK),
|
||||
'foo' => array('f2' => $this->createUploadedFile('F2', 'f2.txt', 'text/plain', UPLOAD_ERR_OK)),
|
||||
),
|
||||
array(
|
||||
'REQUEST_METHOD' => 'POST',
|
||||
'HTTP_HOST' => 'dunglas.fr',
|
||||
'HTTP_X_SYMFONY' => '2.8',
|
||||
),
|
||||
'Content'
|
||||
);
|
||||
|
||||
$psrRequest = $this->factory->createRequest($request);
|
||||
|
||||
$this->assertEquals('Content', $psrRequest->getBody()->__toString());
|
||||
|
||||
$queryParams = $psrRequest->getQueryParams();
|
||||
$this->assertEquals('1', $queryParams['foo']);
|
||||
$this->assertEquals('42', $queryParams['bar']['baz']);
|
||||
|
||||
$parsedBody = $psrRequest->getParsedBody();
|
||||
$this->assertEquals('Kévin Dunglas', $parsedBody['twitter']['@dunglas']);
|
||||
$this->assertEquals('Les-Tilleuls.coop', $parsedBody['twitter']['@coopTilleuls']);
|
||||
$this->assertEquals('2', $parsedBody['baz']);
|
||||
|
||||
$attributes = $psrRequest->getAttributes();
|
||||
$this->assertEquals($stdClass, $attributes['a1']);
|
||||
$this->assertEquals('bar', $attributes['a2']['foo']);
|
||||
|
||||
$cookies = $psrRequest->getCookieParams();
|
||||
$this->assertEquals('foo', $cookies['c1']);
|
||||
$this->assertEquals('bar', $cookies['c2']['c3']);
|
||||
|
||||
$uploadedFiles = $psrRequest->getUploadedFiles();
|
||||
$this->assertEquals('F1', $uploadedFiles['f1']->getStream()->__toString());
|
||||
$this->assertEquals('f1.txt', $uploadedFiles['f1']->getClientFilename());
|
||||
$this->assertEquals('text/plain', $uploadedFiles['f1']->getClientMediaType());
|
||||
$this->assertEquals(UPLOAD_ERR_OK, $uploadedFiles['f1']->getError());
|
||||
|
||||
$this->assertEquals('F2', $uploadedFiles['foo']['f2']->getStream()->__toString());
|
||||
$this->assertEquals('f2.txt', $uploadedFiles['foo']['f2']->getClientFilename());
|
||||
$this->assertEquals('text/plain', $uploadedFiles['foo']['f2']->getClientMediaType());
|
||||
$this->assertEquals(UPLOAD_ERR_OK, $uploadedFiles['foo']['f2']->getError());
|
||||
|
||||
$serverParams = $psrRequest->getServerParams();
|
||||
$this->assertEquals('POST', $serverParams['REQUEST_METHOD']);
|
||||
$this->assertEquals('2.8', $serverParams['HTTP_X_SYMFONY']);
|
||||
$this->assertEquals('POST', $psrRequest->getMethod());
|
||||
$this->assertEquals(array('2.8'), $psrRequest->getHeader('X-Symfony'));
|
||||
}
|
||||
|
||||
private function createUploadedFile($content, $originalName, $mimeType, $error)
|
||||
{
|
||||
$path = tempnam($this->tmpDir, uniqid());
|
||||
file_put_contents($path, $content);
|
||||
|
||||
return new UploadedFile($path, $originalName, $mimeType, filesize($path), $error, true);
|
||||
}
|
||||
|
||||
public function testCreateResponse()
|
||||
{
|
||||
$response = new Response(
|
||||
'Response content.',
|
||||
202,
|
||||
array('X-Symfony' => array('2.8'))
|
||||
);
|
||||
$response->headers->setCookie(new Cookie('city', 'Lille', new \DateTime('Wed, 13 Jan 2021 22:23:01 GMT')));
|
||||
|
||||
$psrResponse = $this->factory->createResponse($response);
|
||||
$this->assertEquals('Response content.', $psrResponse->getBody()->__toString());
|
||||
$this->assertEquals(202, $psrResponse->getStatusCode());
|
||||
$this->assertEquals(array('2.8'), $psrResponse->getHeader('X-Symfony'));
|
||||
$this->assertEquals(array('city=Lille; expires=Wed, 13-Jan-2021 22:23:01 GMT; path=/; httponly'), $psrResponse->getHeader('Set-Cookie'));
|
||||
}
|
||||
|
||||
public function testCreateResponseFromStreamed()
|
||||
{
|
||||
$response = new StreamedResponse(function () {
|
||||
echo "Line 1\n";
|
||||
flush();
|
||||
|
||||
echo "Line 2\n";
|
||||
flush();
|
||||
});
|
||||
|
||||
$psrResponse = $this->factory->createResponse($response);
|
||||
|
||||
$this->assertEquals("Line 1\nLine 2\n", $psrResponse->getBody()->__toString());
|
||||
}
|
||||
|
||||
public function testCreateResponseFromBinaryFile()
|
||||
{
|
||||
$path = tempnam($this->tmpDir, uniqid());
|
||||
file_put_contents($path, 'Binary');
|
||||
|
||||
$response = new BinaryFileResponse($path);
|
||||
|
||||
$psrResponse = $this->factory->createResponse($response);
|
||||
|
||||
$this->assertEquals('Binary', $psrResponse->getBody()->__toString());
|
||||
}
|
||||
}
|
211
core/vendor/symfony/psr-http-message-bridge/Tests/Factory/HttpFoundationFactoryTest.php
vendored
Normal file
211
core/vendor/symfony/psr-http-message-bridge/Tests/Factory/HttpFoundationFactoryTest.php
vendored
Normal file
|
@ -0,0 +1,211 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\PsrHttpMessage\Tests\Factory;
|
||||
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
|
||||
use Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\Response;
|
||||
use Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\ServerRequest;
|
||||
use Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\Stream;
|
||||
use Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\UploadedFile;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class HttpFoundationFactoryTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $factory;
|
||||
private $tmpDir;
|
||||
|
||||
public function setup()
|
||||
{
|
||||
$this->factory = new HttpFoundationFactory();
|
||||
$this->tmpDir = sys_get_temp_dir();
|
||||
}
|
||||
|
||||
public function testCreateRequest()
|
||||
{
|
||||
$stdClass = new \stdClass();
|
||||
$serverRequest = new ServerRequest(
|
||||
'1.1',
|
||||
array(
|
||||
'X-Dunglas-API-Platform' => '1.0',
|
||||
'X-data' => array('a', 'b'),
|
||||
),
|
||||
new Stream('The body'),
|
||||
'/about/kevin',
|
||||
'GET',
|
||||
'http://les-tilleuls.coop/about/kevin',
|
||||
array('country' => 'France'),
|
||||
array('city' => 'Lille'),
|
||||
array('url' => 'http://les-tilleuls.coop'),
|
||||
array(
|
||||
'doc1' => $this->createUploadedFile('Doc 1', UPLOAD_ERR_OK, 'doc1.txt', 'text/plain'),
|
||||
'nested' => array(
|
||||
'docs' => array(
|
||||
$this->createUploadedFile('Doc 2', UPLOAD_ERR_OK, 'doc2.txt', 'text/plain'),
|
||||
$this->createUploadedFile('Doc 3', UPLOAD_ERR_OK, 'doc3.txt', 'text/plain'),
|
||||
),
|
||||
),
|
||||
),
|
||||
array('url' => 'http://dunglas.fr'),
|
||||
array('custom' => $stdClass)
|
||||
);
|
||||
|
||||
$symfonyRequest = $this->factory->createRequest($serverRequest);
|
||||
|
||||
$this->assertEquals('http://les-tilleuls.coop', $symfonyRequest->query->get('url'));
|
||||
$this->assertEquals('doc1.txt', $symfonyRequest->files->get('doc1')->getClientOriginalName());
|
||||
$this->assertEquals('doc2.txt', $symfonyRequest->files->get('nested[docs][0]', null, true)->getClientOriginalName());
|
||||
$this->assertEquals('doc3.txt', $symfonyRequest->files->get('nested[docs][1]', null, true)->getClientOriginalName());
|
||||
$this->assertEquals('http://dunglas.fr', $symfonyRequest->request->get('url'));
|
||||
$this->assertEquals($stdClass, $symfonyRequest->attributes->get('custom'));
|
||||
$this->assertEquals('Lille', $symfonyRequest->cookies->get('city'));
|
||||
$this->assertEquals('France', $symfonyRequest->server->get('country'));
|
||||
$this->assertEquals('The body', $symfonyRequest->getContent());
|
||||
$this->assertEquals('1.0', $symfonyRequest->headers->get('X-Dunglas-API-Platform'));
|
||||
$this->assertEquals(array('a', 'b'), $symfonyRequest->headers->get('X-data', null, false));
|
||||
}
|
||||
|
||||
public function testCreateRequestWithNullParsedBody()
|
||||
{
|
||||
$serverRequest = new ServerRequest(
|
||||
'1.1',
|
||||
array(),
|
||||
new Stream(),
|
||||
'/',
|
||||
'GET',
|
||||
null,
|
||||
array(),
|
||||
array(),
|
||||
array(),
|
||||
array(),
|
||||
null,
|
||||
array()
|
||||
);
|
||||
|
||||
$this->assertCount(0, $this->factory->createRequest($serverRequest)->request);
|
||||
}
|
||||
|
||||
public function testCreateRequestWithObjectParsedBody()
|
||||
{
|
||||
$serverRequest = new ServerRequest(
|
||||
'1.1',
|
||||
array(),
|
||||
new Stream(),
|
||||
'/',
|
||||
'GET',
|
||||
null,
|
||||
array(),
|
||||
array(),
|
||||
array(),
|
||||
array(),
|
||||
new \stdClass(),
|
||||
array()
|
||||
);
|
||||
|
||||
$this->assertCount(0, $this->factory->createRequest($serverRequest)->request);
|
||||
}
|
||||
|
||||
public function testCreateUploadedFile()
|
||||
{
|
||||
$uploadedFile = $this->createUploadedFile('An uploaded file.', UPLOAD_ERR_OK, 'myfile.txt', 'text/plain');
|
||||
$symfonyUploadedFile = $this->callCreateUploadedFile($uploadedFile);
|
||||
|
||||
$uniqid = uniqid();
|
||||
$symfonyUploadedFile->move($this->tmpDir, $uniqid);
|
||||
|
||||
$this->assertEquals($uploadedFile->getSize(), $symfonyUploadedFile->getClientSize());
|
||||
$this->assertEquals(UPLOAD_ERR_OK, $symfonyUploadedFile->getError());
|
||||
$this->assertEquals('myfile.txt', $symfonyUploadedFile->getClientOriginalName());
|
||||
$this->assertEquals('txt', $symfonyUploadedFile->getClientOriginalExtension());
|
||||
$this->assertEquals('text/plain', $symfonyUploadedFile->getClientMimeType());
|
||||
$this->assertEquals('An uploaded file.', file_get_contents($this->tmpDir.'/'.$uniqid));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileException
|
||||
* @expectedExceptionMessage The file "e" could not be written on disk.
|
||||
*/
|
||||
public function testCreateUploadedFileWithError()
|
||||
{
|
||||
$uploadedFile = $this->createUploadedFile('Error.', UPLOAD_ERR_CANT_WRITE, 'e', 'text/plain');
|
||||
$symfonyUploadedFile = $this->callCreateUploadedFile($uploadedFile);
|
||||
|
||||
$this->assertEquals(UPLOAD_ERR_CANT_WRITE, $symfonyUploadedFile->getError());
|
||||
|
||||
$symfonyUploadedFile->move($this->tmpDir, 'shouldFail.txt');
|
||||
}
|
||||
|
||||
private function createUploadedFile($content, $error, $clientFileName, $clientMediaType)
|
||||
{
|
||||
$filePath = tempnam($this->tmpDir, uniqid());
|
||||
file_put_contents($filePath, $content);
|
||||
|
||||
return new UploadedFile($filePath, filesize($filePath), $error, $clientFileName, $clientMediaType);
|
||||
}
|
||||
|
||||
private function callCreateUploadedFile(UploadedFileInterface $uploadedFile)
|
||||
{
|
||||
$reflection = new \ReflectionClass($this->factory);
|
||||
$createUploadedFile = $reflection->getMethod('createUploadedFile');
|
||||
$createUploadedFile->setAccessible(true);
|
||||
|
||||
return $createUploadedFile->invokeArgs($this->factory, array($uploadedFile));
|
||||
}
|
||||
|
||||
public function testCreateResponse()
|
||||
{
|
||||
$response = new Response(
|
||||
'1.0',
|
||||
array(
|
||||
'X-Symfony' => array('2.8'),
|
||||
'Set-Cookie' => array(
|
||||
'theme=light',
|
||||
'test',
|
||||
'ABC=AeD; Domain=dunglas.fr; Path=/kevin; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly',
|
||||
|
||||
),
|
||||
),
|
||||
new Stream('The response body'),
|
||||
200
|
||||
);
|
||||
|
||||
$symfonyResponse = $this->factory->createResponse($response);
|
||||
|
||||
$this->assertEquals('1.0', $symfonyResponse->getProtocolVersion());
|
||||
$this->assertEquals('2.8', $symfonyResponse->headers->get('X-Symfony'));
|
||||
|
||||
$cookies = $symfonyResponse->headers->getCookies();
|
||||
$this->assertEquals('theme', $cookies[0]->getName());
|
||||
$this->assertEquals('light', $cookies[0]->getValue());
|
||||
$this->assertEquals(0, $cookies[0]->getExpiresTime());
|
||||
$this->assertNull($cookies[0]->getDomain());
|
||||
$this->assertEquals('/', $cookies[0]->getPath());
|
||||
$this->assertFalse($cookies[0]->isSecure());
|
||||
$this->assertFalse($cookies[0]->isHttpOnly());
|
||||
|
||||
$this->assertEquals('test', $cookies[1]->getName());
|
||||
$this->assertNull($cookies[1]->getValue());
|
||||
|
||||
$this->assertEquals('ABC', $cookies[2]->getName());
|
||||
$this->assertEquals('AeD', $cookies[2]->getValue());
|
||||
$this->assertEquals(strtotime('Wed, 13 Jan 2021 22:23:01 GMT'), $cookies[2]->getExpiresTime());
|
||||
$this->assertEquals('dunglas.fr', $cookies[2]->getDomain());
|
||||
$this->assertEquals('/kevin', $cookies[2]->getPath());
|
||||
$this->assertTrue($cookies[2]->isSecure());
|
||||
$this->assertTrue($cookies[2]->isHttpOnly());
|
||||
|
||||
$this->assertEquals('The response body', $symfonyResponse->getContent());
|
||||
$this->assertEquals(200, $symfonyResponse->getStatusCode());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\PsrHttpMessage\Tests\Fixtures;
|
||||
|
||||
use Psr\Http\Message\MessageInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Message.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class Message implements MessageInterface
|
||||
{
|
||||
private $version = '1.1';
|
||||
private $headers = array();
|
||||
private $body;
|
||||
|
||||
public function __construct($version = '1.1', array $headers = array(), StreamInterface $body = null)
|
||||
{
|
||||
$this->version = $version;
|
||||
$this->headers = $headers;
|
||||
$this->body = null === $body ? new Stream() : $body;
|
||||
}
|
||||
|
||||
public function getProtocolVersion()
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
public function withProtocolVersion($version)
|
||||
{
|
||||
throw new \BadMethodCallException('Not implemented.');
|
||||
}
|
||||
|
||||
public function getHeaders()
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
public function hasHeader($name)
|
||||
{
|
||||
return isset($this->headers[$name]);
|
||||
}
|
||||
|
||||
public function getHeader($name)
|
||||
{
|
||||
return $this->hasHeader($name) ? $this->headers[$name] : array();
|
||||
}
|
||||
|
||||
public function getHeaderLine($name)
|
||||
{
|
||||
return $this->hasHeader($name) ? implode(',', $this->headers[$name]) : '';
|
||||
}
|
||||
|
||||
public function withHeader($name, $value)
|
||||
{
|
||||
throw new \BadMethodCallException('Not implemented.');
|
||||
}
|
||||
|
||||
public function withAddedHeader($name, $value)
|
||||
{
|
||||
throw new \BadMethodCallException('Not implemented.');
|
||||
}
|
||||
|
||||
public function withoutHeader($name)
|
||||
{
|
||||
throw new \BadMethodCallException('Not implemented.');
|
||||
}
|
||||
|
||||
public function getBody()
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
public function withBody(StreamInterface $body)
|
||||
{
|
||||
throw new \BadMethodCallException('Not implemented.');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\PsrHttpMessage\Tests\Fixtures;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class Response extends Message implements ResponseInterface
|
||||
{
|
||||
private $statusCode;
|
||||
|
||||
public function __construct($version = '1.1', array $headers = array(), StreamInterface $body = null, $statusCode = 200)
|
||||
{
|
||||
parent::__construct($version, $headers, $body);
|
||||
|
||||
$this->statusCode = $statusCode;
|
||||
}
|
||||
|
||||
public function getStatusCode()
|
||||
{
|
||||
return $this->statusCode;
|
||||
}
|
||||
|
||||
public function withStatus($code, $reasonPhrase = '')
|
||||
{
|
||||
throw new \BadMethodCallException('Not implemented.');
|
||||
}
|
||||
|
||||
public function getReasonPhrase()
|
||||
{
|
||||
throw new \BadMethodCallException('Not implemented.');
|
||||
}
|
||||
}
|
141
core/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/ServerRequest.php
vendored
Normal file
141
core/vendor/symfony/psr-http-message-bridge/Tests/Fixtures/ServerRequest.php
vendored
Normal file
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\PsrHttpMessage\Tests\Fixtures;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class ServerRequest extends Message implements ServerRequestInterface
|
||||
{
|
||||
private $requestTarget;
|
||||
private $method;
|
||||
private $uri;
|
||||
private $server;
|
||||
private $cookies;
|
||||
private $query;
|
||||
private $uploadedFiles;
|
||||
private $data;
|
||||
private $attributes;
|
||||
|
||||
public function __construct($version = '1.1', array $headers = array(), StreamInterface $body = null, $requestTarget = '/', $method = 'GET', $uri = null, array $server = array(), array $cookies = array(), array $query = array(), array $uploadedFiles = array(), $data = null, array $attributes = array())
|
||||
{
|
||||
parent::__construct($version, $headers, $body);
|
||||
|
||||
$this->requestTarget = $requestTarget;
|
||||
$this->method = $method;
|
||||
$this->uri = $uri;
|
||||
$this->server = $server;
|
||||
$this->cookies = $cookies;
|
||||
$this->query = $query;
|
||||
$this->uploadedFiles = $uploadedFiles;
|
||||
$this->data = $data;
|
||||
$this->attributes = $attributes;
|
||||
}
|
||||
|
||||
public function getRequestTarget()
|
||||
{
|
||||
return $this->requestTarget;
|
||||
}
|
||||
|
||||
public function withRequestTarget($requestTarget)
|
||||
{
|
||||
throw new \BadMethodCallException('Not implemented.');
|
||||
}
|
||||
|
||||
public function getMethod()
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
public function withMethod($method)
|
||||
{
|
||||
}
|
||||
|
||||
public function getUri()
|
||||
{
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
public function withUri(UriInterface $uri, $preserveHost = false)
|
||||
{
|
||||
throw new \BadMethodCallException('Not implemented.');
|
||||
}
|
||||
|
||||
public function getServerParams()
|
||||
{
|
||||
return $this->server;
|
||||
}
|
||||
|
||||
public function getCookieParams()
|
||||
{
|
||||
return $this->cookies;
|
||||
}
|
||||
|
||||
public function withCookieParams(array $cookies)
|
||||
{
|
||||
throw new \BadMethodCallException('Not implemented.');
|
||||
}
|
||||
|
||||
public function getQueryParams()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
public function withQueryParams(array $query)
|
||||
{
|
||||
throw new \BadMethodCallException('Not implemented.');
|
||||
}
|
||||
|
||||
public function getUploadedFiles()
|
||||
{
|
||||
return $this->uploadedFiles;
|
||||
}
|
||||
|
||||
public function withUploadedFiles(array $uploadedFiles)
|
||||
{
|
||||
throw new \BadMethodCallException('Not implemented.');
|
||||
}
|
||||
|
||||
public function getParsedBody()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function withParsedBody($data)
|
||||
{
|
||||
throw new \BadMethodCallException('Not implemented.');
|
||||
}
|
||||
|
||||
public function getAttributes()
|
||||
{
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
public function getAttribute($name, $default = null)
|
||||
{
|
||||
return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
|
||||
}
|
||||
|
||||
public function withAttribute($name, $value)
|
||||
{
|
||||
throw new \BadMethodCallException('Not implemented.');
|
||||
}
|
||||
|
||||
public function withoutAttribute($name)
|
||||
{
|
||||
throw new \BadMethodCallException('Not implemented.');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\PsrHttpMessage\Tests\Fixtures;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class Stream implements StreamInterface
|
||||
{
|
||||
private $stringContent;
|
||||
|
||||
public function __construct($stringContent = '')
|
||||
{
|
||||
$this->stringContent = $stringContent;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->stringContent;
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
}
|
||||
|
||||
public function detach()
|
||||
{
|
||||
}
|
||||
|
||||
public function getSize()
|
||||
{
|
||||
}
|
||||
|
||||
public function tell()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function eof()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isSeekable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function seek($offset, $whence = SEEK_SET)
|
||||
{
|
||||
}
|
||||
|
||||
public function rewind()
|
||||
{
|
||||
}
|
||||
|
||||
public function isWritable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function write($string)
|
||||
{
|
||||
}
|
||||
|
||||
public function isReadable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function read($length)
|
||||
{
|
||||
return $this->stringContent;
|
||||
}
|
||||
|
||||
public function getContents()
|
||||
{
|
||||
return $this->stringContent;
|
||||
}
|
||||
|
||||
public function getMetadata($key = null)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\PsrHttpMessage\Tests\Fixtures;
|
||||
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class UploadedFile implements UploadedFileInterface
|
||||
{
|
||||
private $filePath;
|
||||
private $size;
|
||||
private $error;
|
||||
private $clientFileName;
|
||||
private $clientMediaType;
|
||||
|
||||
public function __construct($filePath, $size = null, $error = UPLOAD_ERR_OK, $clientFileName = null, $clientMediaType = null)
|
||||
{
|
||||
$this->filePath = $filePath;
|
||||
$this->size = $size;
|
||||
$this->error = $error;
|
||||
$this->clientFileName = $clientFileName;
|
||||
$this->clientMediaType = $clientMediaType;
|
||||
}
|
||||
|
||||
public function getStream()
|
||||
{
|
||||
throw new \RuntimeException('No stream is available.');
|
||||
}
|
||||
|
||||
public function moveTo($targetPath)
|
||||
{
|
||||
rename($this->filePath, $targetPath);
|
||||
}
|
||||
|
||||
public function getSize()
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
public function getError()
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
public function getClientFilename()
|
||||
{
|
||||
return $this->clientFileName;
|
||||
}
|
||||
|
||||
public function getClientMediaType()
|
||||
{
|
||||
return $this->clientMediaType;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "symfony/psr-http-message-bridge",
|
||||
"type": "symfony-bridge",
|
||||
"description": "PSR HTTP message bridge",
|
||||
"keywords": ["http", "psr-7", "http-message"],
|
||||
"homepage": "http://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.3",
|
||||
"psr/http-message": "~1.0",
|
||||
"symfony/http-foundation": "~2.3|~3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "~2.7|~3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"zendframework/zend-diactoros": "To use the Zend Diactoros factory"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Bridge\\PsrHttpMessage\\": "" }
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Symfony PSR-7 HTTP message Bridge Test Suite">
|
||||
<directory>./Tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./</directory>
|
||||
<exclude>
|
||||
<directory>./Resources</directory>
|
||||
<directory>./Tests</directory>
|
||||
<directory>./vendor</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
|
@ -0,0 +1,3 @@
|
|||
coverage_clover: clover.xml
|
||||
json_path: coveralls-upload.json
|
||||
src_dir: src
|
|
@ -0,0 +1,231 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file, in reverse chronological order by release.
|
||||
|
||||
## 1.1.0 - 2015-06-24
|
||||
|
||||
### Added
|
||||
|
||||
- [#52](https://github.com/zendframework/zend-diactoros/pull/52),
|
||||
[#58](https://github.com/zendframework/zend-diactoros/pull/58),
|
||||
[#59](https://github.com/zendframework/zend-diactoros/pull/59), and
|
||||
[#61](https://github.com/zendframework/zend-diactoros/pull/61) create several
|
||||
custom response types for simplifying response creation:
|
||||
|
||||
- `Zend\Diactoros\Response\HtmlResponse` accepts HTML content via its
|
||||
constructor, and sets the `Content-Type` to `text/html`.
|
||||
- `Zend\Diactoros\Response\JsonResponse` accepts data to serialize to JSON via
|
||||
its constructor, and sets the `Content-Type` to `application/json`.
|
||||
- `Zend\Diactoros\Response\EmptyResponse` allows creating empty, read-only
|
||||
responses, with a default status code of 204.
|
||||
- `Zend\Diactoros\Response\RedirectResponse` allows specifying a URI for the
|
||||
`Location` header in the constructor, with a default status code of 302.
|
||||
|
||||
Each also accepts an optional status code, and optional headers (which can
|
||||
also be used to provide an alternate `Content-Type` in the case of the HTML
|
||||
and JSON responses).
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- [#43](https://github.com/zendframework/zend-diactoros/pull/43) removed both
|
||||
`ServerRequestFactory::marshalUri()` and `ServerRequestFactory::marshalHostAndPort()`,
|
||||
which were deprecated prior to the 1.0 release.
|
||||
|
||||
### Fixed
|
||||
|
||||
- [#29](https://github.com/zendframework/zend-diactoros/pull/29) fixes request
|
||||
method validation to allow any valid token as defined by [RFC
|
||||
7230](http://tools.ietf.org/html/rfc7230#appendix-B). This allows usage of
|
||||
custom request methods, vs a static, hard-coded list.
|
||||
|
||||
## 1.0.5 - 2015-06-24
|
||||
|
||||
### Added
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Fixed
|
||||
|
||||
- [#60](https://github.com/zendframework/zend-diactoros/pull/60) fixes
|
||||
the behavior of `UploadedFile` when the `$errorStatus` provided at
|
||||
instantiation is not `UPLOAD_ERR_OK`. Prior to the fix, an
|
||||
`InvalidArgumentException` would occur at instantiation due to the fact that
|
||||
the upload file was missing or invalid. With the fix, no exception is raised
|
||||
until a call to `moveTo()` or `getStream()` is made.
|
||||
|
||||
## 1.0.4 - 2015-06-23
|
||||
|
||||
This is a security release.
|
||||
|
||||
A patch has been applied to `Zend\Diactoros\Uri::filterPath()` that ensures that
|
||||
paths can only begin with a single leading slash. This prevents the following
|
||||
potential security issues:
|
||||
|
||||
- XSS vectors. If the URI path is used for links or form targets, this prevents
|
||||
cases where the first segment of the path resembles a domain name, thus
|
||||
creating scheme-relative links such as `//example.com/foo`. With the patch,
|
||||
the leading double slash is reduced to a single slash, preventing the XSS
|
||||
vector.
|
||||
- Open redirects. If the URI path is used for `Location` or `Link` headers,
|
||||
without a scheme and authority, potential for open redirects exist if clients
|
||||
do not prepend the scheme and authority. Again, preventing a double slash
|
||||
corrects the vector.
|
||||
|
||||
If you are using `Zend\Diactoros\Uri` for creating links, form targets, or
|
||||
redirect paths, and only using the path segment, we recommend upgrading
|
||||
immediately.
|
||||
|
||||
### Added
|
||||
|
||||
- [#25](https://github.com/zendframework/zend-diactoros/pull/25) adds
|
||||
documentation. Documentation is written in markdown, and can be converted to
|
||||
HTML using [bookdown](http://bookdown.io). New features now MUST include
|
||||
documentation for acceptance.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Fixed
|
||||
|
||||
- [#51](https://github.com/zendframework/zend-diactoros/pull/51) fixes
|
||||
`MessageTrait::getHeaderLine()` to return an empty string instead of `null` if
|
||||
the header is undefined (which is the behavior specified in PSR-7).
|
||||
- [#57](https://github.com/zendframework/zend-diactoros/pull/57) fixes the
|
||||
behavior of how the `ServerRequestFactory` marshals upload files when they are
|
||||
represented as a nested associative array.
|
||||
- [#49](https://github.com/zendframework/zend-diactoros/pull/49) provides several
|
||||
fixes that ensure that Diactoros complies with the PSR-7 specification:
|
||||
- `MessageInterface::getHeaderLine()` MUST return a string (that string CAN be
|
||||
empty). Previously, Diactoros would return `null`.
|
||||
- If no `Host` header is set, the `$preserveHost` flag MUST be ignored when
|
||||
calling `withUri()` (previously, Diactoros would not set the `Host` header
|
||||
if `$preserveHost` was `true`, but no `Host` header was present).
|
||||
- The request method MUST be a string; it CAN be empty. Previously, Diactoros
|
||||
would return `null`.
|
||||
- The request MUST return a `UriInterface` instance from `getUri()`; that
|
||||
instance CAN be empty. Previously, Diactoros would return `null`; now it
|
||||
lazy-instantiates an empty `Uri` instance on initialization.
|
||||
- [ZF2015-05](http://framework.zend.com/security/advisory/ZF2015-05) was
|
||||
addressed by altering `Uri::filterPath()` to prevent emitting a path prepended
|
||||
with multiple slashes.
|
||||
|
||||
## 1.0.3 - 2015-06-04
|
||||
|
||||
### Added
|
||||
|
||||
- [#48](https://github.com/zendframework/zend-diactoros/pull/48) drops the
|
||||
minimum supported PHP version to 5.4, to allow an easier upgrade path for
|
||||
Symfony 2.7 users, and potential Drupal 8 usage.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Nothing.
|
||||
|
||||
## 1.0.2 - 2015-06-04
|
||||
|
||||
### Added
|
||||
|
||||
- [#27](https://github.com/zendframework/zend-diactoros/pull/27) adds phonetic
|
||||
pronunciation of "Diactoros" to the README file.
|
||||
- [#36](https://github.com/zendframework/zend-diactoros/pull/36) adds property
|
||||
annotations to the class-level docblock of `Zend\Diactoros\RequestTrait` to
|
||||
ensure properties inherited from the `MessageTrait` are inherited by
|
||||
implementations.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- Nothing.
|
||||
-
|
||||
### Fixed
|
||||
|
||||
- [#41](https://github.com/zendframework/zend-diactoros/pull/41) fixes the
|
||||
namespace for test files to begin with `ZendTest` instead of `Zend`.
|
||||
- [#46](https://github.com/zendframework/zend-diactoros/pull/46) ensures that
|
||||
the cookie and query params for the `ServerRequest` implementation are
|
||||
initialized as arrays.
|
||||
- [#47](https://github.com/zendframework/zend-diactoros/pull/47) modifies the
|
||||
internal logic in `HeaderSecurity::isValid()` to use a regular expression
|
||||
instead of character-by-character comparisons, improving performance.
|
||||
|
||||
## 1.0.1 - 2015-05-26
|
||||
|
||||
### Added
|
||||
|
||||
- [#10](https://github.com/zendframework/zend-diactoros/pull/10) adds
|
||||
`Zend\Diactoros\RelativeStream`, which will return stream contents relative to
|
||||
a given offset (i.e., a subset of the stream). `AbstractSerializer` was
|
||||
updated to create a `RelativeStream` when creating the body of a message,
|
||||
which will prevent duplication of the stream in-memory.
|
||||
- [#21](https://github.com/zendframework/zend-diactoros/pull/21) adds a
|
||||
`.gitattributes` file that excludes directories and files not needed for
|
||||
production; this will further minify the package for production use cases.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Fixed
|
||||
|
||||
- [#9](https://github.com/zendframework/zend-diactoros/pull/9) ensures that
|
||||
attributes are initialized to an empty array, ensuring that attempts to
|
||||
retrieve single attributes when none are defined will not produce errors.
|
||||
- [#14](https://github.com/zendframework/zend-diactoros/pull/14) updates
|
||||
`Zend\Diactoros\Request` to use a `php://temp` stream by default instead of
|
||||
`php://memory`, to ensure requests do not create an out-of-memory condition.
|
||||
- [#15](https://github.com/zendframework/zend-diactoros/pull/15) updates
|
||||
`Zend\Diactoros\Stream` to ensure that write operations trigger an exception
|
||||
if the stream is not writeable. Additionally, it adds more robust logic for
|
||||
determining if a stream is writeable.
|
||||
|
||||
## 1.0.0 - 2015-05-21
|
||||
|
||||
First stable release, and first release as `zend-diactoros`.
|
||||
|
||||
### Added
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Nothing.
|
|
@ -0,0 +1,223 @@
|
|||
# CONTRIBUTING
|
||||
|
||||
## RESOURCES
|
||||
|
||||
If you wish to contribute to Zend Framework, please be sure to
|
||||
read/subscribe to the following resources:
|
||||
|
||||
- [Coding Standards](https://github.com/zendframework/zf2/wiki/Coding-Standards)
|
||||
- [Contributor's Guide](http://framework.zend.com/participate/contributor-guide)
|
||||
- ZF Contributor's mailing list:
|
||||
Archives: http://zend-framework-community.634137.n4.nabble.com/ZF-Contributor-f680267.html
|
||||
Subscribe: zf-contributors-subscribe@lists.zend.com
|
||||
- ZF Contributor's IRC channel:
|
||||
#zftalk.dev on Freenode.net
|
||||
|
||||
If you are working on new features or refactoring [create a proposal](https://github.com/zendframework/zend-diactoros/issues/new).
|
||||
|
||||
## Reporting Potential Security Issues
|
||||
|
||||
If you have encountered a potential security vulnerability, please **DO NOT** report it on the public
|
||||
issue tracker: send it to us at [zf-security@zend.com](mailto:zf-security@zend.com) instead.
|
||||
We will work with you to verify the vulnerability and patch it as soon as possible.
|
||||
|
||||
When reporting issues, please provide the following information:
|
||||
|
||||
- Component(s) affected
|
||||
- A description indicating how to reproduce the issue
|
||||
- A summary of the security vulnerability and impact
|
||||
|
||||
We request that you contact us via the email address above and give the project
|
||||
contributors a chance to resolve the vulnerability and issue a new release prior
|
||||
to any public exposure; this helps protect users and provides them with a chance
|
||||
to upgrade and/or update in order to protect their applications.
|
||||
|
||||
For sensitive email communications, please use [our PGP key](http://framework.zend.com/zf-security-pgp-key.asc).
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is in [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown/),
|
||||
and rendered using [bookdown](http://bookdown.io). Please read and follow the [general documentation
|
||||
guidelines](https://github.com/zendframework/documentation/blob/master/CONTRIBUTING.md) when
|
||||
providing documentation.
|
||||
|
||||
All new features **must** include documentation before they may be accepted and merged.
|
||||
|
||||
## RUNNING TESTS
|
||||
|
||||
To run tests:
|
||||
|
||||
- Clone the repository:
|
||||
|
||||
```console
|
||||
$ git clone git@github.com:zendframework/zend-diactoros.git
|
||||
$ cd
|
||||
```
|
||||
|
||||
- Install dependencies via composer:
|
||||
|
||||
```console
|
||||
$ curl -sS https://getcomposer.org/installer | php --
|
||||
$ ./composer.phar install
|
||||
```
|
||||
|
||||
If you don't have `curl` installed, you can also download `composer.phar` from https://getcomposer.org/
|
||||
|
||||
- Run the tests via `phpunit` and the provided PHPUnit config, like in this example:
|
||||
|
||||
```console
|
||||
$ ./vendor/bin/phpunit
|
||||
```
|
||||
|
||||
## Running Coding Standards Checks
|
||||
|
||||
This component uses [phpcs](https://github.com/squizlabs/PHP_CodeSniffer) for coding
|
||||
standards checks, and provides configuration for our selected checks.
|
||||
`phpcs` is installed by default via Composer.
|
||||
|
||||
To run checks only:
|
||||
|
||||
```console
|
||||
$ ./vendor/bin/phpcs --standard=PSR2 src test
|
||||
```
|
||||
|
||||
`phpcs` also installs a tool named `phpcbf` which can attempt to fix problems
|
||||
for you:
|
||||
|
||||
```console
|
||||
$ ./vendor/bin/phpcbf --standard=PSR2 src test
|
||||
```
|
||||
|
||||
If you allow phpcbf to fix CS issues, please re-run the tests to ensure
|
||||
they pass, and make sure you add and commit the changes after verification.
|
||||
|
||||
## Recommended Workflow for Contributions
|
||||
|
||||
Your first step is to establish a public repository from which we can
|
||||
pull your work into the master repository. We recommend using
|
||||
[GitHub](https://github.com), as that is where the component is already hosted.
|
||||
|
||||
1. Setup a [GitHub account](http://github.com/), if you haven't yet
|
||||
2. Fork the repository (http://github.com/zendframework/zend-diactoros)
|
||||
3. Clone the canonical repository locally and enter it.
|
||||
|
||||
```console
|
||||
$ git clone git://github.com:zendframework/zend-diactoros.git
|
||||
$ cd zend-diactoros
|
||||
```
|
||||
|
||||
4. Add a remote to your fork; substitute your GitHub username in the command
|
||||
below.
|
||||
|
||||
```console
|
||||
$ git remote add {username} git@github.com:{username}/zend-diactoros.git
|
||||
$ git fetch {username}
|
||||
```
|
||||
|
||||
### Keeping Up-to-Date
|
||||
|
||||
Periodically, you should update your fork or personal repository to
|
||||
match the canonical repository. Assuming you have setup your local repository
|
||||
per the instructions above, you can do the following:
|
||||
|
||||
|
||||
```console
|
||||
$ git checkout master
|
||||
$ git fetch origin
|
||||
$ git rebase origin/master
|
||||
# OPTIONALLY, to keep your remote up-to-date -
|
||||
$ git push {username} master:master
|
||||
```
|
||||
|
||||
If you're tracking other branches -- for example, the "develop" branch, where
|
||||
new feature development occurs -- you'll want to do the same operations for that
|
||||
branch; simply substitute "develop" for "master".
|
||||
|
||||
### Working on a patch
|
||||
|
||||
We recommend you do each new feature or bugfix in a new branch. This simplifies
|
||||
the task of code review as well as the task of merging your changes into the
|
||||
canonical repository.
|
||||
|
||||
A typical workflow will then consist of the following:
|
||||
|
||||
1. Create a new local branch based off either your master or develop branch.
|
||||
2. Switch to your new local branch. (This step can be combined with the
|
||||
previous step with the use of `git checkout -b`.)
|
||||
3. Do some work, commit, repeat as necessary.
|
||||
4. Push the local branch to your remote repository.
|
||||
5. Send a pull request.
|
||||
|
||||
The mechanics of this process are actually quite trivial. Below, we will
|
||||
create a branch for fixing an issue in the tracker.
|
||||
|
||||
```console
|
||||
$ git checkout -b hotfix/9295
|
||||
Switched to a new branch 'hotfix/9295'
|
||||
```
|
||||
|
||||
... do some work ...
|
||||
|
||||
|
||||
```console
|
||||
$ git commit
|
||||
```
|
||||
|
||||
... write your log message ...
|
||||
|
||||
|
||||
```console
|
||||
$ git push {username} hotfix/9295:hotfix/9295
|
||||
Counting objects: 38, done.
|
||||
Delta compression using up to 2 threads.
|
||||
Compression objects: 100% (18/18), done.
|
||||
Writing objects: 100% (20/20), 8.19KiB, done.
|
||||
Total 20 (delta 12), reused 0 (delta 0)
|
||||
To ssh://git@github.com/{username}/zend-diactoros.git
|
||||
b5583aa..4f51698 HEAD -> master
|
||||
```
|
||||
|
||||
To send a pull request, you have two options.
|
||||
|
||||
If using GitHub, you can do the pull request from there. Navigate to
|
||||
your repository, select the branch you just created, and then select the
|
||||
"Pull Request" button in the upper right. Select the user/organization
|
||||
"zendframework" as the recipient.
|
||||
|
||||
If using your own repository - or even if using GitHub - you can use `git
|
||||
format-patch` to create a patchset for us to apply; in fact, this is
|
||||
**recommended** for security-related patches. If you use `format-patch`, please
|
||||
send the patches as attachments to:
|
||||
|
||||
- zf-devteam@zend.com for patches without security implications
|
||||
- zf-security@zend.com for security patches
|
||||
|
||||
#### What branch to issue the pull request against?
|
||||
|
||||
Which branch should you issue a pull request against?
|
||||
|
||||
- For fixes against the stable release, issue the pull request against the
|
||||
"master" branch.
|
||||
- For new features, or fixes that introduce new elements to the public API (such
|
||||
as new public methods or properties), issue the pull request against the
|
||||
"develop" branch.
|
||||
|
||||
### Branch Cleanup
|
||||
|
||||
As you might imagine, if you are a frequent contributor, you'll start to
|
||||
get a ton of branches both locally and on your remote.
|
||||
|
||||
Once you know that your changes have been accepted to the master
|
||||
repository, we suggest doing some cleanup of these branches.
|
||||
|
||||
- Local branch cleanup
|
||||
|
||||
```console
|
||||
$ git branch -d <branchname>
|
||||
```
|
||||
|
||||
- Remote branch removal
|
||||
|
||||
```console
|
||||
$ git push {username} :<branchname>
|
||||
```
|
|
@ -0,0 +1,12 @@
|
|||
Copyright (c) 2015, Zend Technologies USA, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of Zend Technologies USA, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,40 @@
|
|||
# zend-diactoros
|
||||
|
||||
Master:
|
||||
[![Build status][Master image]][Master]
|
||||
[![Coverage Status][Master coverage image]][Master coverage]
|
||||
Develop:
|
||||
[![Build status][Develop image]][Develop]
|
||||
[![Coverage Status][Develop coverage image]][Develop coverage]
|
||||
|
||||
> Diactoros (pronunciation: `/dɪʌktɒrɒs/`): an epithet for Hermes, meaning literally, "the messenger."
|
||||
|
||||
This package supercedes and replaces [phly/http](https://github.com/phly/http).
|
||||
|
||||
`zend-diactoros` is a PHP package containing implementations of the [accepted PSR-7 HTTP message interfaces](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message.md), as well as a "server" implementation similar to [node's http.Server](http://nodejs.org/api/http.html).
|
||||
|
||||
* File issues at https://github.com/zendframework/zend-diactoros/issues
|
||||
* Issue patches to https://github.com/zendframework/zend-diactoros/pulls
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is [in the doc tree](doc/), and can be compiled using [bookdown](http://bookdown.io):
|
||||
|
||||
```console
|
||||
$ bookdown doc/bookdown.json
|
||||
$ php -S 0.0.0.0:8080 -t doc/html/ # then browse to http://localhost:8080/
|
||||
```
|
||||
|
||||
> ### Bookdown
|
||||
>
|
||||
> You can install bookdown globally using `composer global require bookdown/bookdown`. If you do
|
||||
> this, make sure that `$HOME/.composer/vendor/bin` is on your `$PATH`.
|
||||
|
||||
[Master]: https://travis-ci.org/zendframework/zend-diactoros
|
||||
[Master image]: https://secure.travis-ci.org/zendframework/zend-diactoros.svg?branch=master
|
||||
[Master coverage image]: https://img.shields.io/coveralls/zendframework/zend-diactoros/master.svg
|
||||
[Master coverage]: https://coveralls.io/r/zendframework/zend-diactoros?branch=master
|
||||
[Develop]: https://github.com/zendframeowork/zend-diactoros/tree/develop
|
||||
[Develop image]: https://secure.travis-ci.org/zendframework/zend-diactoros.svg?branch=develop
|
||||
[Develop coverage image]: https://coveralls.io/repos/zendframework/zend-diactoros/badge.svg?branch=develop
|
||||
[Develop coverage]: https://coveralls.io/r/zendframework/zend-diactoros?branch=develop
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"name": "zendframework/zend-diactoros",
|
||||
"description": "PSR HTTP Message implementations",
|
||||
"type": "library",
|
||||
"license": "BSD-2-Clause",
|
||||
"keywords": [
|
||||
"http",
|
||||
"psr",
|
||||
"psr-7"
|
||||
],
|
||||
"homepage": "https://github.com/zendframework/zend-diactoros",
|
||||
"support": {
|
||||
"issues": "https://github.com/zendframework/zend-diactoros/issues",
|
||||
"source": "https://github.com/zendframework/zend-diactoros"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev",
|
||||
"dev-develop": "1.1-dev"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4",
|
||||
"psr/http-message": "~1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/PHPUnit": "~4.6",
|
||||
"squizlabs/php_codesniffer": "^2.3.1"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-message-implementation": "~1.0.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Zend\\Diactoros\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"ZendTest\\Diactoros\\": "test/"
|
||||
},
|
||||
"files": [
|
||||
"test/TestAsset/Functions.php",
|
||||
"test/TestAsset/SapiResponse.php"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0"?>
|
||||
<ruleset name="Zend Framework coding standard">
|
||||
<description>Zend Framework coding standard</description>
|
||||
|
||||
<!-- display progress -->
|
||||
<arg value="p"/>
|
||||
<arg name="colors"/>
|
||||
|
||||
<!-- inherit rules from: -->
|
||||
<rule ref="PSR2"/>
|
||||
<rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
|
||||
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace">
|
||||
<properties>
|
||||
<property name="ignoreBlankLines" value="false"/>
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
<!-- Paths to check -->
|
||||
<file>src</file>
|
||||
<file>test</file>
|
||||
</ruleset>
|
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Provides base functionality for request and response de/serialization
|
||||
* strategies, including functionality for retrieving a line at a time from
|
||||
* the message, splitting headers from the body, and serializing headers.
|
||||
*/
|
||||
abstract class AbstractSerializer
|
||||
{
|
||||
const CR = "\r";
|
||||
const EOL = "\r\n";
|
||||
const LF = "\n";
|
||||
|
||||
/**
|
||||
* Retrieve a single line from the stream.
|
||||
*
|
||||
* Retrieves a line from the stream; a line is defined as a sequence of
|
||||
* characters ending in a CRLF sequence.
|
||||
*
|
||||
* @param StreamInterface $stream
|
||||
* @return string
|
||||
* @throws UnexpectedValueException if the sequence contains a CR or LF in
|
||||
* isolation, or ends in a CR.
|
||||
*/
|
||||
protected static function getLine(StreamInterface $stream)
|
||||
{
|
||||
$line = '';
|
||||
$crFound = false;
|
||||
while (! $stream->eof()) {
|
||||
$char = $stream->read(1);
|
||||
|
||||
if ($crFound && $char === self::LF) {
|
||||
$crFound = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// CR NOT followed by LF
|
||||
if ($crFound && $char !== self::LF) {
|
||||
throw new UnexpectedValueException('Unexpected carriage return detected');
|
||||
}
|
||||
|
||||
// LF in isolation
|
||||
if (! $crFound && $char === self::LF) {
|
||||
throw new UnexpectedValueException('Unexpected line feed detected');
|
||||
}
|
||||
|
||||
// CR found; do not append
|
||||
if ($char === self::CR) {
|
||||
$crFound = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Any other character: append
|
||||
$line .= $char;
|
||||
}
|
||||
|
||||
// CR found at end of stream
|
||||
if ($crFound) {
|
||||
throw new UnexpectedValueException("Unexpected end of headers");
|
||||
}
|
||||
|
||||
return $line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the stream into headers and body content.
|
||||
*
|
||||
* Returns an array containing two elements
|
||||
*
|
||||
* - The first is an array of headers
|
||||
* - The second is a StreamInterface containing the body content
|
||||
*
|
||||
* @param StreamInterface $stream
|
||||
* @return array
|
||||
* @throws UnexpectedValueException For invalid headers.
|
||||
*/
|
||||
protected static function splitStream(StreamInterface $stream)
|
||||
{
|
||||
$headers = [];
|
||||
$currentHeader = false;
|
||||
|
||||
while ($line = self::getLine($stream)) {
|
||||
if (preg_match(';^(?P<name>[!#$%&\'*+.^_`\|~0-9a-zA-Z-]+):(?P<value>.*)$;', $line, $matches)) {
|
||||
$currentHeader = $matches['name'];
|
||||
if (! isset($headers[$currentHeader])) {
|
||||
$headers[$currentHeader] = [];
|
||||
}
|
||||
$headers[$currentHeader][] = ltrim($matches['value']);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $currentHeader) {
|
||||
throw new UnexpectedValueException('Invalid header detected');
|
||||
}
|
||||
|
||||
if (! preg_match('#^[ \t]#', $line)) {
|
||||
throw new UnexpectedValueException('Invalid header continuation');
|
||||
}
|
||||
|
||||
// Append continuation to last header value found
|
||||
$value = array_pop($headers[$currentHeader]);
|
||||
$headers[$currentHeader][] = $value . ltrim($line);
|
||||
}
|
||||
|
||||
// use RelativeStream to avoid copying initial stream into memory
|
||||
return [$headers, new RelativeStream($stream, $stream->tell())];
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize headers to string values.
|
||||
*
|
||||
* @param array $headers
|
||||
* @return string
|
||||
*/
|
||||
protected static function serializeHeaders(array $headers)
|
||||
{
|
||||
$lines = [];
|
||||
foreach ($headers as $header => $values) {
|
||||
$normalized = self::filterHeader($header);
|
||||
foreach ($values as $value) {
|
||||
$lines[] = sprintf('%s: %s', $normalized, $value);
|
||||
}
|
||||
}
|
||||
|
||||
return implode("\r\n", $lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a header name to wordcase
|
||||
*
|
||||
* @param string $header
|
||||
* @return string
|
||||
*/
|
||||
protected static function filterHeader($header)
|
||||
{
|
||||
$filtered = str_replace('-', ' ', $header);
|
||||
$filtered = ucwords($filtered);
|
||||
return str_replace(' ', '-', $filtered);
|
||||
}
|
||||
}
|
19
core/vendor/zendframework/zend-diactoros/src/Exception/DeprecatedMethodException.php
vendored
Normal file
19
core/vendor/zendframework/zend-diactoros/src/Exception/DeprecatedMethodException.php
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros\Exception;
|
||||
|
||||
use BadMethodCallException;
|
||||
|
||||
/**
|
||||
* Exception indicating a deprecated method.
|
||||
*/
|
||||
class DeprecatedMethodException extends BadMethodCallException implements ExceptionInterface
|
||||
{
|
||||
}
|
17
core/vendor/zendframework/zend-diactoros/src/Exception/ExceptionInterface.php
vendored
Normal file
17
core/vendor/zendframework/zend-diactoros/src/Exception/ExceptionInterface.php
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros\Exception;
|
||||
|
||||
/**
|
||||
* Marker interface for package-specific exceptions.
|
||||
*/
|
||||
interface ExceptionInterface
|
||||
{
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Provide security tools around HTTP headers to prevent common injection vectors.
|
||||
*
|
||||
* Code is largely lifted from the Zend\Http\Header\HeaderValue implementation in
|
||||
* Zend Framework, released with the copyright and license below.
|
||||
*
|
||||
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
final class HeaderSecurity
|
||||
{
|
||||
/**
|
||||
* Private constructor; non-instantiable.
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a header value
|
||||
*
|
||||
* Ensures CRLF header injection vectors are filtered.
|
||||
*
|
||||
* Per RFC 7230, only VISIBLE ASCII characters, spaces, and horizontal
|
||||
* tabs are allowed in values; header continuations MUST consist of
|
||||
* a single CRLF sequence followed by a space or horizontal tab.
|
||||
*
|
||||
* This method filters any values not allowed from the string, and is
|
||||
* lossy.
|
||||
*
|
||||
* @see http://en.wikipedia.org/wiki/HTTP_response_splitting
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public static function filter($value)
|
||||
{
|
||||
$value = (string) $value;
|
||||
$length = strlen($value);
|
||||
$string = '';
|
||||
for ($i = 0; $i < $length; $i += 1) {
|
||||
$ascii = ord($value[$i]);
|
||||
|
||||
// Detect continuation sequences
|
||||
if ($ascii === 13) {
|
||||
$lf = ord($value[$i + 1]);
|
||||
$ws = ord($value[$i + 2]);
|
||||
if ($lf === 10 && in_array($ws, [9, 32], true)) {
|
||||
$string .= $value[$i] . $value[$i + 1];
|
||||
$i += 1;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Non-visible, non-whitespace characters
|
||||
// 9 === horizontal tab
|
||||
// 32-126, 128-254 === visible
|
||||
// 127 === DEL
|
||||
// 255 === null byte
|
||||
if (($ascii < 32 && $ascii !== 9)
|
||||
|| $ascii === 127
|
||||
|| $ascii > 254
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$string .= $value[$i];
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a header value.
|
||||
*
|
||||
* Per RFC 7230, only VISIBLE ASCII characters, spaces, and horizontal
|
||||
* tabs are allowed in values; header continuations MUST consist of
|
||||
* a single CRLF sequence followed by a space or horizontal tab.
|
||||
*
|
||||
* @see http://en.wikipedia.org/wiki/HTTP_response_splitting
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValid($value)
|
||||
{
|
||||
$value = (string) $value;
|
||||
|
||||
// Look for:
|
||||
// \n not preceded by \r, OR
|
||||
// \r not followed by \n, OR
|
||||
// \r\n not followed by space or horizontal tab; these are all CRLF attacks
|
||||
if (preg_match("#(?:(?:(?<!\r)\n)|(?:\r(?!\n))|(?:\r\n(?![ \t])))#", $value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Non-visible, non-whitespace characters
|
||||
// 9 === horizontal tab
|
||||
// 10 === line feed
|
||||
// 13 === carriage return
|
||||
// 32-126, 128-254 === visible
|
||||
// 127 === DEL (disallowed)
|
||||
// 255 === null byte (disallowed)
|
||||
if (preg_match('/[^\x09\x0a\x0d\x20-\x7E\x80-\xFE]/', $value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert a header value is valid.
|
||||
*
|
||||
* @param string $value
|
||||
* @throws InvalidArgumentException for invalid values
|
||||
*/
|
||||
public static function assertValid($value)
|
||||
{
|
||||
if (! self::isValid($value)) {
|
||||
throw new InvalidArgumentException('Invalid header value');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert whether or not a header name is valid.
|
||||
*
|
||||
* @see http://tools.ietf.org/html/rfc7230#section-3.2
|
||||
* @param mixed $name
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function assertValidName($name)
|
||||
{
|
||||
if (! preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $name)) {
|
||||
throw new InvalidArgumentException('Invalid header name');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,383 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Trait implementing the various methods defined in MessageInterface.
|
||||
*
|
||||
* @see https://github.com/php-fig/http-message/tree/master/src/MessageInterface.php
|
||||
*/
|
||||
trait MessageTrait
|
||||
{
|
||||
/**
|
||||
* List of all registered headers, as key => array of values.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $headers = [];
|
||||
|
||||
/**
|
||||
* Map of normalized header name to original name used to register header.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $headerNames = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $protocol = '1.1';
|
||||
|
||||
/**
|
||||
* @var StreamInterface
|
||||
*/
|
||||
private $stream;
|
||||
|
||||
/**
|
||||
* 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->protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function withProtocolVersion($version)
|
||||
{
|
||||
$new = clone $this;
|
||||
$new->protocol = $version;
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all message headers.
|
||||
*
|
||||
* 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);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public function getHeaders()
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a header exists by the given case-insensitive name.
|
||||
*
|
||||
* @param string $header Case-insensitive header 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($header)
|
||||
{
|
||||
return array_key_exists(strtolower($header), $this->headerNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $header 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($header)
|
||||
{
|
||||
if (! $this->hasHeader($header)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$header = $this->headerNames[strtolower($header)];
|
||||
$value = $this->headers[$header];
|
||||
$value = is_array($value) ? $value : [$value];
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$value = $this->getHeader($name);
|
||||
if (empty($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return implode(',', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an instance with the provided header, replacing any existing
|
||||
* values of any headers with the same case-insensitive name.
|
||||
*
|
||||
* 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 $header 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($header, $value)
|
||||
{
|
||||
if (is_string($value)) {
|
||||
$value = [$value];
|
||||
}
|
||||
|
||||
if (! is_array($value) || ! $this->arrayContainsOnlyStrings($value)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid header value; must be a string or array of strings'
|
||||
);
|
||||
}
|
||||
|
||||
HeaderSecurity::assertValidName($header);
|
||||
self::assertValidHeaderValue($value);
|
||||
|
||||
$normalized = strtolower($header);
|
||||
|
||||
$new = clone $this;
|
||||
$new->headerNames[$normalized] = $header;
|
||||
$new->headers[$header] = $value;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $header 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($header, $value)
|
||||
{
|
||||
if (is_string($value)) {
|
||||
$value = [ $value ];
|
||||
}
|
||||
|
||||
if (! is_array($value) || ! $this->arrayContainsOnlyStrings($value)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid header value; must be a string or array of strings'
|
||||
);
|
||||
}
|
||||
|
||||
HeaderSecurity::assertValidName($header);
|
||||
self::assertValidHeaderValue($value);
|
||||
|
||||
if (! $this->hasHeader($header)) {
|
||||
return $this->withHeader($header, $value);
|
||||
}
|
||||
|
||||
$normalized = strtolower($header);
|
||||
$header = $this->headerNames[$normalized];
|
||||
|
||||
$new = clone $this;
|
||||
$new->headers[$header] = array_merge($this->headers[$header], $value);
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $header Case-insensitive header field name to remove.
|
||||
* @return static
|
||||
*/
|
||||
public function withoutHeader($header)
|
||||
{
|
||||
if (! $this->hasHeader($header)) {
|
||||
return clone $this;
|
||||
}
|
||||
|
||||
$normalized = strtolower($header);
|
||||
$original = $this->headerNames[$normalized];
|
||||
|
||||
$new = clone $this;
|
||||
unset($new->headers[$original], $new->headerNames[$normalized]);
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the body of the message.
|
||||
*
|
||||
* @return StreamInterface Returns the body as a stream.
|
||||
*/
|
||||
public function getBody()
|
||||
{
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$new = clone $this;
|
||||
$new->stream = $body;
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an array contains only strings
|
||||
*
|
||||
* @param array $array
|
||||
* @return bool
|
||||
*/
|
||||
private function arrayContainsOnlyStrings(array $array)
|
||||
{
|
||||
return array_reduce($array, [__CLASS__, 'filterStringValue'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a set of headers to ensure they are in the correct internal format.
|
||||
*
|
||||
* Used by message constructors to allow setting all initial headers at once.
|
||||
*
|
||||
* @param array $originalHeaders Headers to filter.
|
||||
* @return array Filtered headers and names.
|
||||
*/
|
||||
private function filterHeaders(array $originalHeaders)
|
||||
{
|
||||
$headerNames = $headers = [];
|
||||
foreach ($originalHeaders as $header => $value) {
|
||||
if (! is_string($header)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! is_array($value) && ! is_string($value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! is_array($value)) {
|
||||
$value = [ $value ];
|
||||
}
|
||||
|
||||
$headerNames[strtolower($header)] = $header;
|
||||
$headers[$header] = $value;
|
||||
}
|
||||
|
||||
return [$headerNames, $headers];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a value is a string
|
||||
*
|
||||
* Used with array_reduce.
|
||||
*
|
||||
* @param bool $carry
|
||||
* @param mixed $item
|
||||
* @return bool
|
||||
*/
|
||||
private static function filterStringValue($carry, $item)
|
||||
{
|
||||
if (! is_string($item)) {
|
||||
return false;
|
||||
}
|
||||
return $carry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the provided header values are valid.
|
||||
*
|
||||
* @see http://tools.ietf.org/html/rfc7230#section-3.2
|
||||
* @param string[] $values
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private static function assertValidHeaderValue(array $values)
|
||||
{
|
||||
array_walk($values, __NAMESPACE__ . '\HeaderSecurity::assertValid');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros;
|
||||
|
||||
/**
|
||||
* Caching version of php://input
|
||||
*/
|
||||
class PhpInputStream extends Stream
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $cache = '';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $reachedEof = false;
|
||||
|
||||
/**
|
||||
* @param string|resource $stream
|
||||
* @param string $mode
|
||||
*/
|
||||
public function __construct($stream = 'php://input', $mode = 'r')
|
||||
{
|
||||
$mode = 'r';
|
||||
parent::__construct($stream, $mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
if ($this->reachedEof) {
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
$this->getContents();
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isWritable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($length)
|
||||
{
|
||||
$content = parent::read($length);
|
||||
if ($content && ! $this->reachedEof) {
|
||||
$this->cache .= $content;
|
||||
}
|
||||
|
||||
if ($this->eof()) {
|
||||
$this->reachedEof = true;
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContents($maxLength = -1)
|
||||
{
|
||||
if ($this->reachedEof) {
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
$contents = stream_get_contents($this->resource, $maxLength);
|
||||
$this->cache .= $contents;
|
||||
|
||||
if ($maxLength === -1 || $this->eof()) {
|
||||
$this->reachedEof = true;
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Class RelativeStream
|
||||
*
|
||||
* Wrapper for default Stream class, representing subpart (starting from given offset) of initial stream.
|
||||
* It can be used to avoid copying full stream, conserving memory.
|
||||
* @example see Zend\Diactoros\AbstractSerializer::splitStream()
|
||||
*/
|
||||
final class RelativeStream implements StreamInterface
|
||||
{
|
||||
/**
|
||||
* @var StreamInterface
|
||||
*/
|
||||
private $decoratedStream;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $offset;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param StreamInterface $decoratedStream
|
||||
* @param int $offset
|
||||
*/
|
||||
public function __construct(StreamInterface $decoratedStream, $offset)
|
||||
{
|
||||
$this->decoratedStream = $decoratedStream;
|
||||
$this->offset = (int)$offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$this->seek(0);
|
||||
return $this->getContents();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->decoratedStream->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function detach()
|
||||
{
|
||||
return $this->decoratedStream->detach();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
return $this->decoratedStream->getSize() - $this->offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tell()
|
||||
{
|
||||
return $this->decoratedStream->tell() - $this->offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function eof()
|
||||
{
|
||||
return $this->decoratedStream->eof();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isSeekable()
|
||||
{
|
||||
return $this->decoratedStream->isSeekable();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function seek($offset, $whence = SEEK_SET)
|
||||
{
|
||||
if ($whence == SEEK_SET) {
|
||||
return $this->decoratedStream->seek($offset + $this->offset, $whence);
|
||||
}
|
||||
return $this->decoratedStream->seek($offset, $whence);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
return $this->seek(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isWritable()
|
||||
{
|
||||
return $this->decoratedStream->isWritable();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($string)
|
||||
{
|
||||
return $this->decoratedStream->write($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReadable()
|
||||
{
|
||||
return $this->decoratedStream->isReadable();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($length)
|
||||
{
|
||||
return $this->decoratedStream->read($length);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContents()
|
||||
{
|
||||
return $this->decoratedStream->getContents();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMetadata($key = null)
|
||||
{
|
||||
return $this->decoratedStream->getMetadata($key);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* HTTP Request encapsulation
|
||||
*
|
||||
* Requests are considered immutable; all methods that might change state are
|
||||
* implemented such that they retain the internal state of the current
|
||||
* message and return a new instance that contains the changed state.
|
||||
*/
|
||||
class Request implements RequestInterface
|
||||
{
|
||||
use MessageTrait, RequestTrait;
|
||||
|
||||
/**
|
||||
* @param null|string $uri URI for the request, if any.
|
||||
* @param null|string $method HTTP method for the request, if any.
|
||||
* @param string|resource|StreamInterface $body Message body, if any.
|
||||
* @param array $headers Headers for the message, if any.
|
||||
* @throws \InvalidArgumentException for any invalid value.
|
||||
*/
|
||||
public function __construct($uri = null, $method = null, $body = 'php://temp', array $headers = [])
|
||||
{
|
||||
$this->initialize($uri, $method, $body, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHeaders()
|
||||
{
|
||||
$headers = $this->headers;
|
||||
if (! $this->hasHeader('host')
|
||||
&& ($this->uri && $this->uri->getHost())
|
||||
) {
|
||||
$headers['Host'] = [$this->getHostFromUri()];
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHeader($header)
|
||||
{
|
||||
if (! $this->hasHeader($header)) {
|
||||
if (strtolower($header) === 'host'
|
||||
&& ($this->uri && $this->uri->getHost())
|
||||
) {
|
||||
return [$this->getHostFromUri()];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
$header = $this->headerNames[strtolower($header)];
|
||||
$value = $this->headers[$header];
|
||||
$value = is_array($value) ? $value : [$value];
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros\Request;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use UnexpectedValueException;
|
||||
use Zend\Diactoros\AbstractSerializer;
|
||||
use Zend\Diactoros\Request;
|
||||
use Zend\Diactoros\Stream;
|
||||
use Zend\Diactoros\Uri;
|
||||
|
||||
/**
|
||||
* Serialize (cast to string) or deserialize (cast string to Request) messages.
|
||||
*
|
||||
* This class provides functionality for serializing a RequestInterface instance
|
||||
* to a string, as well as the reverse operation of creating a Request instance
|
||||
* from a string/stream representing a message.
|
||||
*/
|
||||
final class Serializer extends AbstractSerializer
|
||||
{
|
||||
/**
|
||||
* Deserialize a request string to a request instance.
|
||||
*
|
||||
* Internally, casts the message to a stream and invokes fromStream().
|
||||
*
|
||||
* @param string $message
|
||||
* @return Request
|
||||
* @throws UnexpectedValueException when errors occur parsing the message.
|
||||
*/
|
||||
public static function fromString($message)
|
||||
{
|
||||
$stream = new Stream('php://temp', 'wb+');
|
||||
$stream->write($message);
|
||||
return self::fromStream($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a request stream to a request instance.
|
||||
*
|
||||
* @param StreamInterface $stream
|
||||
* @return Request
|
||||
* @throws UnexpectedValueException when errors occur parsing the message.
|
||||
*/
|
||||
public static function fromStream(StreamInterface $stream)
|
||||
{
|
||||
if (! $stream->isReadable() || ! $stream->isSeekable()) {
|
||||
throw new InvalidArgumentException('Message stream must be both readable and seekable');
|
||||
}
|
||||
|
||||
$stream->rewind();
|
||||
|
||||
list($method, $requestTarget, $version) = self::getRequestLine($stream);
|
||||
$uri = self::createUriFromRequestTarget($requestTarget);
|
||||
|
||||
list($headers, $body) = self::splitStream($stream);
|
||||
|
||||
return (new Request($uri, $method, $body, $headers))
|
||||
->withProtocolVersion($version)
|
||||
->withRequestTarget($requestTarget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a request message to a string.
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
* @return string
|
||||
*/
|
||||
public static function toString(RequestInterface $request)
|
||||
{
|
||||
$headers = self::serializeHeaders($request->getHeaders());
|
||||
$body = (string) $request->getBody();
|
||||
$format = '%s %s HTTP/%s%s%s';
|
||||
|
||||
if (! empty($headers)) {
|
||||
$headers = "\r\n" . $headers;
|
||||
}
|
||||
if (! empty($body)) {
|
||||
$headers .= "\r\n\r\n";
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
$format,
|
||||
$request->getMethod(),
|
||||
$request->getRequestTarget(),
|
||||
$request->getProtocolVersion(),
|
||||
$headers,
|
||||
$body
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the components of the request line.
|
||||
*
|
||||
* Retrieves the first line of the stream and parses it, raising an
|
||||
* exception if it does not follow specifications; if valid, returns a list
|
||||
* with the method, target, and version, in that order.
|
||||
*
|
||||
* @param StreamInterface $stream
|
||||
* @return array
|
||||
*/
|
||||
private static function getRequestLine(StreamInterface $stream)
|
||||
{
|
||||
$requestLine = self::getLine($stream);
|
||||
|
||||
if (! preg_match(
|
||||
'#^(?P<method>[!\#$%&\'*+.^_`|~a-zA-Z0-9-]+) (?P<target>[^\s]+) HTTP/(?P<version>[1-9]\d*\.\d+)$#',
|
||||
$requestLine,
|
||||
$matches
|
||||
)) {
|
||||
throw new UnexpectedValueException('Invalid request line detected');
|
||||
}
|
||||
|
||||
return [$matches['method'], $matches['target'], $matches['version']];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a Uri instance based on the provided request target.
|
||||
*
|
||||
* If the request target is of authority or asterisk form, an empty Uri
|
||||
* instance is returned; otherwise, the value is used to create and return
|
||||
* a new Uri instance.
|
||||
*
|
||||
* @param string $requestTarget
|
||||
* @return Uri
|
||||
*/
|
||||
private static function createUriFromRequestTarget($requestTarget)
|
||||
{
|
||||
if (preg_match('#^https?://#', $requestTarget)) {
|
||||
return new Uri($requestTarget);
|
||||
}
|
||||
|
||||
if (preg_match('#^(\*|[^/])#', $requestTarget)) {
|
||||
return new Uri();
|
||||
}
|
||||
|
||||
return new Uri($requestTarget);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,309 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
/**
|
||||
* Trait with common request behaviors.
|
||||
*
|
||||
* Server and client-side requests differ slightly in how the Host header is
|
||||
* handled; on client-side, it should be calculated on-the-fly from the
|
||||
* composed URI (if present), while on server-side, it will be calculated from
|
||||
* the environment. As such, this trait exists to provide the common code
|
||||
* between both client-side and server-side requests, and each can then
|
||||
* use the headers functionality required by their implementations.
|
||||
*
|
||||
* @property array $headers
|
||||
* @property array $headerNames
|
||||
* @property StreamInterface $stream
|
||||
* @method bool hasHeader(string $header)
|
||||
*/
|
||||
trait RequestTrait
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $method = '';
|
||||
|
||||
/**
|
||||
* The request-target, if it has been provided or calculated.
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
private $requestTarget;
|
||||
|
||||
/**
|
||||
* @var null|UriInterface
|
||||
*/
|
||||
private $uri;
|
||||
|
||||
/**
|
||||
* Initialize request state.
|
||||
*
|
||||
* Used by constructors.
|
||||
*
|
||||
* @param null|string $uri URI for the request, if any.
|
||||
* @param null|string $method HTTP method for the request, if any.
|
||||
* @param string|resource|StreamInterface $body Message body, if any.
|
||||
* @param array $headers Headers for the message, if any.
|
||||
* @throws InvalidArgumentException for any invalid value.
|
||||
*/
|
||||
private function initialize($uri = null, $method = null, $body = 'php://memory', array $headers = [])
|
||||
{
|
||||
if (! $uri instanceof UriInterface && ! is_string($uri) && null !== $uri) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid URI provided; must be null, a string, or a Psr\Http\Message\UriInterface instance'
|
||||
);
|
||||
}
|
||||
|
||||
$this->validateMethod($method);
|
||||
|
||||
if (! is_string($body) && ! is_resource($body) && ! $body instanceof StreamInterface) {
|
||||
throw new InvalidArgumentException(
|
||||
'Body must be a string stream resource identifier, '
|
||||
. 'an actual stream resource, '
|
||||
. 'or a Psr\Http\Message\StreamInterface implementation'
|
||||
);
|
||||
}
|
||||
|
||||
if (is_string($uri)) {
|
||||
$uri = new Uri($uri);
|
||||
}
|
||||
|
||||
$this->method = $method ?: '';
|
||||
$this->uri = $uri ?: new Uri();
|
||||
$this->stream = ($body instanceof StreamInterface) ? $body : new Stream($body, 'r');
|
||||
|
||||
list($this->headerNames, $headers) = $this->filterHeaders($headers);
|
||||
$this->assertHeaders($headers);
|
||||
$this->headers = $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the message's request target.
|
||||
*
|
||||
* Retrieves the message's request-target either as it will appear (for
|
||||
* clients), as it appeared at request (for servers), or as it was
|
||||
* specified for the instance (see withRequestTarget()).
|
||||
*
|
||||
* In most cases, this will be the origin-form of the composed URI,
|
||||
* unless a value was provided to the concrete implementation (see
|
||||
* withRequestTarget() below).
|
||||
*
|
||||
* If no URI is available, and no request-target has been specifically
|
||||
* provided, this method MUST return the string "/".
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRequestTarget()
|
||||
{
|
||||
if (null !== $this->requestTarget) {
|
||||
return $this->requestTarget;
|
||||
}
|
||||
|
||||
if (! $this->uri) {
|
||||
return '/';
|
||||
}
|
||||
|
||||
$target = $this->uri->getPath();
|
||||
if ($this->uri->getQuery()) {
|
||||
$target .= '?' . $this->uri->getQuery();
|
||||
}
|
||||
|
||||
if (empty($target)) {
|
||||
$target = '/';
|
||||
}
|
||||
|
||||
return $target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with a specific request-target.
|
||||
*
|
||||
* 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 MUST be implemented in such a way as to retain the
|
||||
* immutability of the message, and MUST return a new instance that has the
|
||||
* changed request target.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public function withRequestTarget($requestTarget)
|
||||
{
|
||||
if (preg_match('#\s#', $requestTarget)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid request target provided; cannot contain whitespace'
|
||||
);
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->requestTarget = $requestTarget;
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the HTTP method of the request.
|
||||
*
|
||||
* @return string Returns the request method.
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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-insensitive method.
|
||||
* @return static
|
||||
* @throws InvalidArgumentException for invalid HTTP methods.
|
||||
*/
|
||||
public function withMethod($method)
|
||||
{
|
||||
$this->validateMethod($method);
|
||||
$new = clone $this;
|
||||
$new->method = $method;
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the URI instance.
|
||||
*
|
||||
* This method MUST return a UriInterface instance.
|
||||
*
|
||||
* @link http://tools.ietf.org/html/rfc3986#section-4.3
|
||||
* @return UriInterface Returns a UriInterface instance
|
||||
* representing the URI of the request, if any.
|
||||
*/
|
||||
public function getUri()
|
||||
{
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance with the provided URI.
|
||||
*
|
||||
* This method will 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 will be carried
|
||||
* over to the returned request.
|
||||
*
|
||||
* You can opt-in to preserving the original state of the Host header by
|
||||
* setting `$preserveHost` to `true`. When `$preserveHost` is set to
|
||||
* `true`, the returned request will not update the Host header of the
|
||||
* returned message -- even if the message contains no Host header. This
|
||||
* means that a call to `getHeader('Host')` on the original request MUST
|
||||
* equal the return value of a call to `getHeader('Host')` on 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 static
|
||||
*/
|
||||
public function withUri(UriInterface $uri, $preserveHost = false)
|
||||
{
|
||||
$new = clone $this;
|
||||
$new->uri = $uri;
|
||||
|
||||
if ($preserveHost && $this->hasHeader('Host')) {
|
||||
return $new;
|
||||
}
|
||||
|
||||
if (! $uri->getHost()) {
|
||||
return $new;
|
||||
}
|
||||
|
||||
$host = $uri->getHost();
|
||||
if ($uri->getPort()) {
|
||||
$host .= ':' . $uri->getPort();
|
||||
}
|
||||
|
||||
$new->headerNames['host'] = 'Host';
|
||||
$new->headers['Host'] = [$host];
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the HTTP method
|
||||
*
|
||||
* @param null|string $method
|
||||
* @throws InvalidArgumentException on invalid HTTP method.
|
||||
*/
|
||||
private function validateMethod($method)
|
||||
{
|
||||
if (null === $method) {
|
||||
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)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Unsupported HTTP method "%s" provided',
|
||||
$method
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the host from the URI instance
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getHostFromUri()
|
||||
{
|
||||
$host = $this->uri->getHost();
|
||||
$host .= $this->uri->getPort() ? ':' . $this->uri->getPort() : '';
|
||||
return $host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure header names and values are valid.
|
||||
*
|
||||
* @param array $headers
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private function assertHeaders(array $headers)
|
||||
{
|
||||
foreach ($headers as $name => $headerValues) {
|
||||
HeaderSecurity::assertValidName($name);
|
||||
array_walk($headerValues, __NAMESPACE__ . '\HeaderSecurity::assertValid');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* HTTP response encapsulation.
|
||||
*
|
||||
* Responses are considered immutable; all methods that might change state are
|
||||
* implemented such that they retain the internal state of the current
|
||||
* message and return a new instance that contains the changed state.
|
||||
*/
|
||||
class Response implements ResponseInterface
|
||||
{
|
||||
use MessageTrait;
|
||||
|
||||
/**
|
||||
* Map of standard HTTP status code/reason phrases
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $phrases = [
|
||||
// INFORMATIONAL CODES
|
||||
100 => 'Continue',
|
||||
101 => 'Switching Protocols',
|
||||
102 => 'Processing',
|
||||
// SUCCESS CODES
|
||||
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',
|
||||
// REDIRECTION CODES
|
||||
300 => 'Multiple Choices',
|
||||
301 => 'Moved Permanently',
|
||||
302 => 'Found',
|
||||
303 => 'See Other',
|
||||
304 => 'Not Modified',
|
||||
305 => 'Use Proxy',
|
||||
306 => 'Switch Proxy', // Deprecated
|
||||
307 => 'Temporary Redirect',
|
||||
// CLIENT ERROR
|
||||
400 => 'Bad Request',
|
||||
401 => 'Unauthorized',
|
||||
402 => 'Payment Required',
|
||||
403 => 'Forbidden',
|
||||
404 => 'Not Found',
|
||||
405 => 'Method Not Allowed',
|
||||
406 => 'Not Acceptable',
|
||||
407 => 'Proxy Authentication Required',
|
||||
408 => 'Request Time-out',
|
||||
409 => 'Conflict',
|
||||
410 => 'Gone',
|
||||
411 => 'Length Required',
|
||||
412 => 'Precondition Failed',
|
||||
413 => 'Request Entity Too Large',
|
||||
414 => 'Request-URI Too Large',
|
||||
415 => 'Unsupported Media Type',
|
||||
416 => 'Requested range not satisfiable',
|
||||
417 => 'Expectation Failed',
|
||||
418 => 'I\'m a teapot',
|
||||
422 => 'Unprocessable Entity',
|
||||
423 => 'Locked',
|
||||
424 => 'Failed Dependency',
|
||||
425 => 'Unordered Collection',
|
||||
426 => 'Upgrade Required',
|
||||
428 => 'Precondition Required',
|
||||
429 => 'Too Many Requests',
|
||||
431 => 'Request Header Fields Too Large',
|
||||
// SERVER ERROR
|
||||
500 => 'Internal Server Error',
|
||||
501 => 'Not Implemented',
|
||||
502 => 'Bad Gateway',
|
||||
503 => 'Service Unavailable',
|
||||
504 => 'Gateway Time-out',
|
||||
505 => 'HTTP Version not supported',
|
||||
506 => 'Variant Also Negotiates',
|
||||
507 => 'Insufficient Storage',
|
||||
508 => 'Loop Detected',
|
||||
511 => 'Network Authentication Required',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $reasonPhrase = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $statusCode = 200;
|
||||
|
||||
/**
|
||||
* @param string|resource|StreamInterface $stream Stream identifier and/or actual stream resource
|
||||
* @param int $status Status code for the response, if any.
|
||||
* @param array $headers Headers for the response, if any.
|
||||
* @throws InvalidArgumentException on any invalid element.
|
||||
*/
|
||||
public function __construct($body = 'php://memory', $status = 200, array $headers = [])
|
||||
{
|
||||
if (! is_string($body) && ! is_resource($body) && ! $body instanceof StreamInterface) {
|
||||
throw new InvalidArgumentException(
|
||||
'Stream must be a string stream resource identifier, '
|
||||
. 'an actual stream resource, '
|
||||
. 'or a Psr\Http\Message\StreamInterface implementation'
|
||||
);
|
||||
}
|
||||
|
||||
if (null !== $status) {
|
||||
$this->validateStatus($status);
|
||||
}
|
||||
|
||||
$this->stream = ($body instanceof StreamInterface) ? $body : new Stream($body, 'wb+');
|
||||
$this->statusCode = $status ? (int) $status : 200;
|
||||
|
||||
list($this->headerNames, $headers) = $this->filterHeaders($headers);
|
||||
$this->assertHeaders($headers);
|
||||
$this->headers = $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getStatusCode()
|
||||
{
|
||||
return $this->statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getReasonPhrase()
|
||||
{
|
||||
if (! $this->reasonPhrase
|
||||
&& isset($this->phrases[$this->statusCode])
|
||||
) {
|
||||
$this->reasonPhrase = $this->phrases[$this->statusCode];
|
||||
}
|
||||
|
||||
return $this->reasonPhrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function withStatus($code, $reasonPhrase = '')
|
||||
{
|
||||
$this->validateStatus($code);
|
||||
$new = clone $this;
|
||||
$new->statusCode = (int) $code;
|
||||
$new->reasonPhrase = $reasonPhrase;
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a status code.
|
||||
*
|
||||
* @param int|string $code
|
||||
* @throws InvalidArgumentException on an invalid status code.
|
||||
*/
|
||||
private function validateStatus($code)
|
||||
{
|
||||
if (! is_numeric($code)
|
||||
|| is_float($code)
|
||||
|| $code < 100
|
||||
|| $code >= 600
|
||||
) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Invalid status code "%s"; must be an integer between 100 and 599, inclusive',
|
||||
(is_scalar($code) ? $code : gettype($code))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure header names and values are valid.
|
||||
*
|
||||
* @param array $headers
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private function assertHeaders(array $headers)
|
||||
{
|
||||
foreach ($headers as $name => $headerValues) {
|
||||
HeaderSecurity::assertValidName($name);
|
||||
array_walk($headerValues, __NAMESPACE__ . '\HeaderSecurity::assertValid');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros\Response;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
interface EmitterInterface
|
||||
{
|
||||
/**
|
||||
* Emit a response.
|
||||
*
|
||||
* Emits a response, including status line, headers, and the message body,
|
||||
* according to the environment.
|
||||
*
|
||||
* Implementations of this method may be written in such a way as to have
|
||||
* side effects, such as usage of header() or pushing output to the
|
||||
* output buffer.
|
||||
*
|
||||
* Implementations MAY raise exceptions if they are unable to emit the
|
||||
* response; e.g., if headers have already been sent.
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
*/
|
||||
public function emit(ResponseInterface $response);
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros\Response;
|
||||
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\Stream;
|
||||
|
||||
/**
|
||||
* A class representing empty HTTP responses.
|
||||
*/
|
||||
class EmptyResponse extends Response
|
||||
{
|
||||
/**
|
||||
* Create an empty response with the given status code.
|
||||
*
|
||||
* @param int $status Status code for the response, if any.
|
||||
* @param array $headers Headers for the response, if any.
|
||||
*/
|
||||
public function __construct($status = 204, array $headers = [])
|
||||
{
|
||||
$body = new Stream('php://temp', 'r');
|
||||
parent::__construct($body, $status, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty response with the given headers.
|
||||
*
|
||||
* @param array $headers Headers for the response.
|
||||
* @return EmptyResponse
|
||||
*/
|
||||
public static function withHeaders(array $headers)
|
||||
{
|
||||
return new static(204, $headers);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros\Response;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\Stream;
|
||||
|
||||
/**
|
||||
* HTML response.
|
||||
*
|
||||
* Allows creating a response by passing an HTML string to the constructor;
|
||||
* by default, sets a status code of 200 and sets the Content-Type header to
|
||||
* text/html.
|
||||
*/
|
||||
class HtmlResponse extends Response
|
||||
{
|
||||
use InjectContentTypeTrait;
|
||||
|
||||
/**
|
||||
* Create an HTML response.
|
||||
*
|
||||
* Produces an HTML response with a Content-Type of text/html and a default
|
||||
* status of 200.
|
||||
*
|
||||
* @param string|StreamInterface $html HTML or stream for the message body.
|
||||
* @param int $status Integer status code for the response; 200 by default.
|
||||
* @param array $headers Array of headers to use at initialization.
|
||||
* @throws InvalidArgumentException if $html is neither a string or stream.
|
||||
*/
|
||||
public function __construct($html, $status = 200, array $headers = [])
|
||||
{
|
||||
parent::__construct(
|
||||
$this->createBody($html),
|
||||
$status,
|
||||
$this->injectContentType('text/html', $headers)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the message body.
|
||||
*
|
||||
* @param string|StreamInterface $html
|
||||
* @return StreamInterface
|
||||
* @throws InvalidArgumentException if $html is neither a string or stream.
|
||||
*/
|
||||
private function createBody($html)
|
||||
{
|
||||
if ($html instanceof StreamInterface) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
if (! is_string($html)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Invalid content (%s) provided to %s',
|
||||
(is_object($html) ? get_class($html) : gettype($html)),
|
||||
__CLASS__
|
||||
));
|
||||
}
|
||||
|
||||
$body = new Stream('php://temp', 'wb+');
|
||||
$body->write($html);
|
||||
return $body;
|
||||
}
|
||||
}
|
33
core/vendor/zendframework/zend-diactoros/src/Response/InjectContentTypeTrait.php
vendored
Normal file
33
core/vendor/zendframework/zend-diactoros/src/Response/InjectContentTypeTrait.php
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros\Response;
|
||||
|
||||
trait InjectContentTypeTrait
|
||||
{
|
||||
/**
|
||||
* Inject the provided Content-Type, if none is already present.
|
||||
*
|
||||
* @param string $contentType
|
||||
* @param array $headers
|
||||
* @return array Headers with injected Content-Type
|
||||
*/
|
||||
private function injectContentType($contentType, array $headers)
|
||||
{
|
||||
$hasContentType = array_reduce(array_keys($headers), function ($carry, $item) {
|
||||
return $carry ?: (strtolower($item) === 'content-type');
|
||||
}, false);
|
||||
|
||||
if (! $hasContentType) {
|
||||
$headers['content-type'] = [$contentType];
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros\Response;
|
||||
|
||||
use ArrayObject;
|
||||
use InvalidArgumentException;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\Stream;
|
||||
|
||||
/**
|
||||
* HTML response.
|
||||
*
|
||||
* Allows creating a response by passing an HTML string to the constructor;
|
||||
* by default, sets a status code of 200 and sets the Content-Type header to
|
||||
* text/html.
|
||||
*/
|
||||
class JsonResponse extends Response
|
||||
{
|
||||
use InjectContentTypeTrait;
|
||||
|
||||
/**
|
||||
* Create a JSON response with the given array of data.
|
||||
*
|
||||
* If the data provided is null, an empty ArrayObject is used; if the data
|
||||
* is scalar, it is cast to an array prior to serialization.
|
||||
*
|
||||
* Default JSON encoding is performed with the following options, which
|
||||
* produces RFC4627-compliant JSON, capable of embedding into HTML.
|
||||
*
|
||||
* - JSON_HEX_TAG
|
||||
* - JSON_HEX_APOS
|
||||
* - JSON_HEX_AMP
|
||||
* - JSON_HEX_QUOT
|
||||
*
|
||||
* @param string $data Data to convert to JSON.
|
||||
* @param int $status Integer status code for the response; 200 by default.
|
||||
* @param array $headers Array of headers to use at initialization.
|
||||
* @param int $encodingOptions JSON encoding options to use.
|
||||
* @throws InvalidArgumentException if unable to encode the $data to JSON.
|
||||
*/
|
||||
public function __construct($data, $status = 200, array $headers = [], $encodingOptions = 15)
|
||||
{
|
||||
$body = new Stream('php://temp', 'wb+');
|
||||
$body->write($this->jsonEncode($data, $encodingOptions));
|
||||
|
||||
$headers = $this->injectContentType('application/json', $headers);
|
||||
|
||||
parent::__construct($body, $status, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the provided data to JSON.
|
||||
*
|
||||
* @param mixed $data
|
||||
* @param int $encodingOptions
|
||||
* @return string
|
||||
* @throws InvalidArgumentException if unable to encode the $data to JSON.
|
||||
*/
|
||||
private function jsonEncode($data, $encodingOptions)
|
||||
{
|
||||
if (is_resource($data)) {
|
||||
throw new InvalidArgumentException('Cannot JSON encode resources');
|
||||
}
|
||||
|
||||
if ($data === null) {
|
||||
// Use an ArrayObject to force an empty JSON object.
|
||||
$data = new ArrayObject();
|
||||
}
|
||||
|
||||
if (is_scalar($data)) {
|
||||
$data = (array) $data;
|
||||
}
|
||||
|
||||
// Clear json_last_error()
|
||||
json_encode(null);
|
||||
|
||||
$json = json_encode($data, $encodingOptions);
|
||||
|
||||
if (JSON_ERROR_NONE !== json_last_error()) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Unable to encode data to JSON in %s: %s',
|
||||
__CLASS__,
|
||||
json_last_error_msg()
|
||||
));
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros\Response;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\Stream;
|
||||
|
||||
/**
|
||||
* Produce a redirect response.
|
||||
*/
|
||||
class RedirectResponse extends Response
|
||||
{
|
||||
/**
|
||||
* Create a redirect response.
|
||||
*
|
||||
* Produces a redirect response with a Location header and the given status
|
||||
* (302 by default).
|
||||
*
|
||||
* Note: this method overwrites the `location` $headers value.
|
||||
*
|
||||
* @param string|UriInterface $uri URI for the Location header.
|
||||
* @param int $status Integer status code for the redirect; 302 by default.
|
||||
* @param array $headers Array of headers to use at initialization.
|
||||
*/
|
||||
public function __construct($uri, $status = 302, array $headers = [])
|
||||
{
|
||||
if (! is_string($uri) && ! $uri instanceof UriInterface) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Uri provided to %s MUST be a string or Psr\Http\Message\UriInterface instance; received "%s"',
|
||||
__CLASS__,
|
||||
(is_object($uri) ? get_class($uri) : gettype($uri))
|
||||
));
|
||||
}
|
||||
|
||||
$headers['location'] = [(string) $uri];
|
||||
parent::__construct('php://temp', $status, $headers);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros\Response;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use RuntimeException;
|
||||
|
||||
class SapiEmitter implements EmitterInterface
|
||||
{
|
||||
/**
|
||||
* Emits a response for a PHP SAPI environment.
|
||||
*
|
||||
* Emits the status line and headers via the header() function, and the
|
||||
* body content via the output buffer.
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
* @param null|int $maxBufferLevel Maximum output buffering level to unwrap.
|
||||
*/
|
||||
public function emit(ResponseInterface $response, $maxBufferLevel = null)
|
||||
{
|
||||
if (headers_sent()) {
|
||||
throw new RuntimeException('Unable to emit response; headers already sent');
|
||||
}
|
||||
|
||||
$this->emitStatusLine($response);
|
||||
$this->emitHeaders($response);
|
||||
$this->emitBody($response, $maxBufferLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit the status line.
|
||||
*
|
||||
* Emits the status line using the protocol version and status code from
|
||||
* the response; if a reason phrase is availble, it, too, is emitted.
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
*/
|
||||
private function emitStatusLine(ResponseInterface $response)
|
||||
{
|
||||
$reasonPhrase = $response->getReasonPhrase();
|
||||
header(sprintf(
|
||||
'HTTP/%s %d%s',
|
||||
$response->getProtocolVersion(),
|
||||
$response->getStatusCode(),
|
||||
($reasonPhrase ? ' ' . $reasonPhrase : '')
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit response headers.
|
||||
*
|
||||
* Loops through each header, emitting each; if the header value
|
||||
* is an array with multiple values, ensures that each is sent
|
||||
* in such a way as to create aggregate headers (instead of replace
|
||||
* the previous).
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
*/
|
||||
private function emitHeaders(ResponseInterface $response)
|
||||
{
|
||||
foreach ($response->getHeaders() as $header => $values) {
|
||||
$name = $this->filterHeader($header);
|
||||
$first = true;
|
||||
foreach ($values as $value) {
|
||||
header(sprintf(
|
||||
'%s: %s',
|
||||
$name,
|
||||
$value
|
||||
), $first);
|
||||
$first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit the message body.
|
||||
*
|
||||
* Loops through the output buffer, flushing each, before emitting
|
||||
* the response body using `echo()`.
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
* @param int $maxBufferLevel Flush up to this buffer level.
|
||||
*/
|
||||
private function emitBody(ResponseInterface $response, $maxBufferLevel)
|
||||
{
|
||||
if (null === $maxBufferLevel) {
|
||||
$maxBufferLevel = ob_get_level();
|
||||
}
|
||||
|
||||
while (ob_get_level() > $maxBufferLevel) {
|
||||
ob_end_flush();
|
||||
}
|
||||
|
||||
echo $response->getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a header name to wordcase
|
||||
*
|
||||
* @param string $header
|
||||
* @return string
|
||||
*/
|
||||
private function filterHeader($header)
|
||||
{
|
||||
$filtered = str_replace('-', ' ', $header);
|
||||
$filtered = ucwords($filtered);
|
||||
return str_replace(' ', '-', $filtered);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros\Response;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use UnexpectedValueException;
|
||||
use Zend\Diactoros\AbstractSerializer;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\Stream;
|
||||
|
||||
final class Serializer extends AbstractSerializer
|
||||
{
|
||||
/**
|
||||
* Deserialize a response string to a response instance.
|
||||
*
|
||||
* @param string $message
|
||||
* @return Response
|
||||
* @throws UnexpectedValueException when errors occur parsing the message.
|
||||
*/
|
||||
public static function fromString($message)
|
||||
{
|
||||
$stream = new Stream('php://temp', 'wb+');
|
||||
$stream->write($message);
|
||||
return static::fromStream($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a response from a stream.
|
||||
*
|
||||
* @param StreamInterface $stream
|
||||
* @return ResponseInterface
|
||||
* @throws InvalidArgumentException when the stream is not readable.
|
||||
* @throws UnexpectedValueException when errors occur parsing the message.
|
||||
*/
|
||||
public static function fromStream(StreamInterface $stream)
|
||||
{
|
||||
if (! $stream->isReadable() || ! $stream->isSeekable()) {
|
||||
throw new InvalidArgumentException('Message stream must be both readable and seekable');
|
||||
}
|
||||
|
||||
$stream->rewind();
|
||||
|
||||
list($version, $status, $reasonPhrase) = self::getStatusLine($stream);
|
||||
list($headers, $body) = self::splitStream($stream);
|
||||
|
||||
return (new Response($body, $status, $headers))
|
||||
->withProtocolVersion($version)
|
||||
->withStatus($status, $reasonPhrase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a string representation of a response.
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
* @return string
|
||||
*/
|
||||
public static function toString(ResponseInterface $response)
|
||||
{
|
||||
$reasonPhrase = $response->getReasonPhrase();
|
||||
$headers = self::serializeHeaders($response->getHeaders());
|
||||
$body = (string) $response->getBody();
|
||||
$format = 'HTTP/%s %d%s%s%s';
|
||||
|
||||
if (! empty($headers)) {
|
||||
$headers = "\r\n" . $headers;
|
||||
}
|
||||
if (! empty($body)) {
|
||||
$headers .= "\r\n\r\n";
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
$format,
|
||||
$response->getProtocolVersion(),
|
||||
$response->getStatusCode(),
|
||||
($reasonPhrase ? ' ' . $reasonPhrase : ''),
|
||||
$headers,
|
||||
$body
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the status line for the message.
|
||||
*
|
||||
* @param StreamInterface $stream
|
||||
* @return array Array with three elements: 0 => version, 1 => status, 2 => reason
|
||||
* @throws UnexpectedValueException if line is malformed
|
||||
*/
|
||||
private static function getStatusLine(StreamInterface $stream)
|
||||
{
|
||||
$line = self::getLine($stream);
|
||||
|
||||
if (! preg_match(
|
||||
'#^HTTP/(?P<version>[1-9]\d*\.\d) (?P<status>[1-5]\d{2})(\s+(?P<reason>.+))?$#',
|
||||
$line,
|
||||
$matches
|
||||
)) {
|
||||
throw new UnexpectedValueException('No status line detected');
|
||||
}
|
||||
|
||||
return [$matches['version'], $matches['status'], isset($matches['reason']) ? $matches['reason'] : ''];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros;
|
||||
|
||||
use OutOfBoundsException;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* "Serve" incoming HTTP requests
|
||||
*
|
||||
* Given a callback, takes an incoming request, dispatches it to the
|
||||
* callback, and then sends a response.
|
||||
*/
|
||||
class Server
|
||||
{
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $callback;
|
||||
|
||||
/**
|
||||
* Response emitter to use; by default, uses Response\SapiEmitter.
|
||||
*
|
||||
* @var Response\EmitterInterface
|
||||
*/
|
||||
private $emitter;
|
||||
|
||||
/**
|
||||
* @var ServerRequestInterface
|
||||
*/
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* @var ResponseInterface
|
||||
*/
|
||||
private $response;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Given a callback, a request, and a response, we can create a server.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
*/
|
||||
public function __construct(
|
||||
callable $callback,
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response
|
||||
) {
|
||||
$this->callback = $callback;
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow retrieving the request, response and callback as properties
|
||||
*
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
* @throws OutOfBoundsException for invalid properties
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if (! property_exists($this, $name)) {
|
||||
throw new OutOfBoundsException('Cannot retrieve arbitrary properties from server');
|
||||
}
|
||||
return $this->{$name};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set alternate response emitter to use.
|
||||
*
|
||||
* @param Response\EmitterInterface $emitter
|
||||
*/
|
||||
public function setEmitter(Response\EmitterInterface $emitter)
|
||||
{
|
||||
$this->emitter = $emitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Server instance
|
||||
*
|
||||
* Creates a server instance from the callback and the following
|
||||
* PHP environmental values:
|
||||
*
|
||||
* - server; typically this will be the $_SERVER superglobal
|
||||
* - query; typically this will be the $_GET superglobal
|
||||
* - body; typically this will be the $_POST superglobal
|
||||
* - cookies; typically this will be the $_COOKIE superglobal
|
||||
* - files; typically this will be the $_FILES superglobal
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param array $server
|
||||
* @param array $query
|
||||
* @param array $body
|
||||
* @param array $cookies
|
||||
* @param array $files
|
||||
* @return static
|
||||
*/
|
||||
public static function createServer(
|
||||
callable $callback,
|
||||
array $server,
|
||||
array $query,
|
||||
array $body,
|
||||
array $cookies,
|
||||
array $files
|
||||
) {
|
||||
$request = ServerRequestFactory::fromGlobals($server, $query, $body, $cookies, $files);
|
||||
$response = new Response();
|
||||
return new static($callback, $request, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Server instance from an existing request object
|
||||
*
|
||||
* Provided a callback, an existing request object, and optionally an
|
||||
* existing response object, create and return the Server instance.
|
||||
*
|
||||
* If no Response object is provided, one will be created.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param ServerRequestInterface $request
|
||||
* @param null|ResponseInterface $response
|
||||
* @return static
|
||||
*/
|
||||
public static function createServerFromRequest(
|
||||
callable $callback,
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response = null
|
||||
) {
|
||||
if (! $response) {
|
||||
$response = new Response();
|
||||
}
|
||||
return new static($callback, $request, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* "Listen" to an incoming request
|
||||
*
|
||||
* If provided a $finalHandler, that callable will be used for
|
||||
* incomplete requests.
|
||||
*
|
||||
* Output buffering is enabled prior to invoking the attached
|
||||
* callback; any output buffered will be sent prior to any
|
||||
* response body content.
|
||||
*
|
||||
* @param null|callable $finalHandler
|
||||
*/
|
||||
public function listen(callable $finalHandler = null)
|
||||
{
|
||||
$callback = $this->callback;
|
||||
|
||||
ob_start();
|
||||
$bufferLevel = ob_get_level();
|
||||
|
||||
$response = $callback($this->request, $this->response, $finalHandler);
|
||||
if (! $response instanceof ResponseInterface) {
|
||||
$response = $this->response;
|
||||
}
|
||||
$this->getEmitter()->emit($response, $bufferLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current response emitter.
|
||||
*
|
||||
* If none has been registered, lazy-loads a Response\SapiEmitter.
|
||||
*
|
||||
* @return Response\EmitterInterface
|
||||
*/
|
||||
private function getEmitter()
|
||||
{
|
||||
if (! $this->emitter) {
|
||||
$this->emitter = new Response\SapiEmitter();
|
||||
}
|
||||
|
||||
return $this->emitter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,297 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
|
||||
/**
|
||||
* Server-side HTTP request
|
||||
*
|
||||
* Extends the Request definition to add methods for accessing incoming data,
|
||||
* specifically server parameters, cookies, matched path parameters, query
|
||||
* string arguments, body parameters, and upload file information.
|
||||
*
|
||||
* "Attributes" are discovered via decomposing the request (and usually
|
||||
* specifically the URI path), and typically will be injected by the application.
|
||||
*
|
||||
* Requests are considered immutable; all methods that might change state are
|
||||
* implemented such that they retain the internal state of the current
|
||||
* message and return a new instance that contains the changed state.
|
||||
*/
|
||||
class ServerRequest implements ServerRequestInterface
|
||||
{
|
||||
use MessageTrait, RequestTrait;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $attributes = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $cookieParams = [];
|
||||
|
||||
/**
|
||||
* @var null|array|object
|
||||
*/
|
||||
private $parsedBody;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $queryParams = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $serverParams;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $uploadedFiles;
|
||||
|
||||
/**
|
||||
* @param array $serverParams Server parameters, typically from $_SERVER
|
||||
* @param array $uploadedFiles Upload file information, a tree of UploadedFiles
|
||||
* @param null|string $uri URI for the request, if any.
|
||||
* @param null|string $method HTTP method for the request, if any.
|
||||
* @param string|resource|StreamInterface $body Message body, if any.
|
||||
* @param array $headers Headers for the message, if any.
|
||||
* @throws InvalidArgumentException for any invalid value.
|
||||
*/
|
||||
public function __construct(
|
||||
array $serverParams = [],
|
||||
array $uploadedFiles = [],
|
||||
$uri = null,
|
||||
$method = null,
|
||||
$body = 'php://input',
|
||||
array $headers = []
|
||||
) {
|
||||
$this->validateUploadedFiles($uploadedFiles);
|
||||
|
||||
$body = $this->getStream($body);
|
||||
$this->initialize($uri, $method, $body, $headers);
|
||||
$this->serverParams = $serverParams;
|
||||
$this->uploadedFiles = $uploadedFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getServerParams()
|
||||
{
|
||||
return $this->serverParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUploadedFiles()
|
||||
{
|
||||
return $this->uploadedFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function withUploadedFiles(array $uploadedFiles)
|
||||
{
|
||||
$this->validateUploadedFiles($uploadedFiles);
|
||||
$new = clone $this;
|
||||
$new->uploadedFiles = $uploadedFiles;
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCookieParams()
|
||||
{
|
||||
return $this->cookieParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function withCookieParams(array $cookies)
|
||||
{
|
||||
$new = clone $this;
|
||||
$new->cookieParams = $cookies;
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQueryParams()
|
||||
{
|
||||
return $this->queryParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function withQueryParams(array $query)
|
||||
{
|
||||
$new = clone $this;
|
||||
$new->queryParams = $query;
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParsedBody()
|
||||
{
|
||||
return $this->parsedBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function withParsedBody($data)
|
||||
{
|
||||
$new = clone $this;
|
||||
$new->parsedBody = $data;
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAttributes()
|
||||
{
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAttribute($attribute, $default = null)
|
||||
{
|
||||
if (! array_key_exists($attribute, $this->attributes)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $this->attributes[$attribute];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function withAttribute($attribute, $value)
|
||||
{
|
||||
$new = clone $this;
|
||||
$new->attributes[$attribute] = $value;
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function withoutAttribute($attribute)
|
||||
{
|
||||
if (! isset($this->attributes[$attribute])) {
|
||||
return clone $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
unset($new->attributes[$attribute]);
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy to receive the request method.
|
||||
*
|
||||
* This overrides the parent functionality to ensure the method is never
|
||||
* empty; if no method is present, it returns 'GET'.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
if (empty($this->method)) {
|
||||
return 'GET';
|
||||
}
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the request method.
|
||||
*
|
||||
* Unlike the regular Request implementation, the server-side
|
||||
* normalizes the method to uppercase to ensure consistency
|
||||
* and make checking the method simpler.
|
||||
*
|
||||
* This methods returns a new instance.
|
||||
*
|
||||
* @param string $method
|
||||
* @return self
|
||||
*/
|
||||
public function withMethod($method)
|
||||
{
|
||||
$this->validateMethod($method);
|
||||
$new = clone $this;
|
||||
$new->method = $method;
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the body stream
|
||||
*
|
||||
* @param string|resource|StreamInterface $stream
|
||||
* @return StreamInterface
|
||||
*/
|
||||
private function getStream($stream)
|
||||
{
|
||||
if ($stream === 'php://input') {
|
||||
return new PhpInputStream();
|
||||
}
|
||||
|
||||
if (! is_string($stream) && ! is_resource($stream) && ! $stream instanceof StreamInterface) {
|
||||
throw new InvalidArgumentException(
|
||||
'Stream must be a string stream resource identifier, '
|
||||
. 'an actual stream resource, '
|
||||
. 'or a Psr\Http\Message\StreamInterface implementation'
|
||||
);
|
||||
}
|
||||
|
||||
if (! $stream instanceof StreamInterface) {
|
||||
return new Stream($stream, 'r');
|
||||
}
|
||||
|
||||
return $stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively validate the structure in an uploaded files array.
|
||||
*
|
||||
* @param array $uploadedFiles
|
||||
* @throws InvalidArgumentException if any leaf is not an UploadedFileInterface instance.
|
||||
*/
|
||||
private function validateUploadedFiles(array $uploadedFiles)
|
||||
{
|
||||
foreach ($uploadedFiles as $file) {
|
||||
if (is_array($file)) {
|
||||
$this->validateUploadedFiles($file);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $file instanceof UploadedFileInterface) {
|
||||
throw new InvalidArgumentException('Invalid leaf in uploaded files structure');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,458 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\MessageInterface;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class for marshaling a request object from the current PHP environment.
|
||||
*
|
||||
* Logic largely refactored from the ZF2 Zend\Http\PhpEnvironment\Request class.
|
||||
*
|
||||
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
abstract class ServerRequestFactory
|
||||
{
|
||||
/**
|
||||
* Function to use to get apache request headers; present only to simplify mocking.
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
private static $apacheRequestHeaders = 'apache_request_headers';
|
||||
|
||||
/**
|
||||
* Create a request from the supplied superglobal values.
|
||||
*
|
||||
* If any argument is not supplied, the corresponding superglobal value will
|
||||
* be used.
|
||||
*
|
||||
* The ServerRequest created is then passed to the fromServer() method in
|
||||
* order to marshal the request URI and headers.
|
||||
*
|
||||
* @see fromServer()
|
||||
* @param array $server $_SERVER superglobal
|
||||
* @param array $query $_GET superglobal
|
||||
* @param array $body $_POST superglobal
|
||||
* @param array $cookies $_COOKIE superglobal
|
||||
* @param array $files $_FILES superglobal
|
||||
* @return ServerRequest
|
||||
* @throws InvalidArgumentException for invalid file values
|
||||
*/
|
||||
public static function fromGlobals(
|
||||
array $server = null,
|
||||
array $query = null,
|
||||
array $body = null,
|
||||
array $cookies = null,
|
||||
array $files = null
|
||||
) {
|
||||
$server = static::normalizeServer($server ?: $_SERVER);
|
||||
$files = static::normalizeFiles($files ?: $_FILES);
|
||||
$headers = static::marshalHeaders($server);
|
||||
$request = new ServerRequest(
|
||||
$server,
|
||||
$files,
|
||||
static::marshalUriFromServer($server, $headers),
|
||||
static::get('REQUEST_METHOD', $server, 'GET'),
|
||||
'php://input',
|
||||
$headers
|
||||
);
|
||||
|
||||
return $request
|
||||
->withCookieParams($cookies ?: $_COOKIE)
|
||||
->withQueryParams($query ?: $_GET)
|
||||
->withParsedBody($body ?: $_POST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access a value in an array, returning a default value if not found
|
||||
*
|
||||
* Will also do a case-insensitive search if a case sensitive search fails.
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $values
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($key, array $values, $default = null)
|
||||
{
|
||||
if (array_key_exists($key, $values)) {
|
||||
return $values[$key];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a header value.
|
||||
*
|
||||
* Does a case-insensitive search for a matching header.
|
||||
*
|
||||
* If found, it is returned as a string, using comma concatenation.
|
||||
*
|
||||
* If not, the $default is returned.
|
||||
*
|
||||
* @param string $header
|
||||
* @param array $headers
|
||||
* @param mixed $default
|
||||
* @return string
|
||||
*/
|
||||
public static function getHeader($header, array $headers, $default = null)
|
||||
{
|
||||
$header = strtolower($header);
|
||||
$headers = array_change_key_case($headers, CASE_LOWER);
|
||||
if (array_key_exists($header, $headers)) {
|
||||
$value = is_array($headers[$header]) ? implode(', ', $headers[$header]) : $headers[$header];
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marshal the $_SERVER array
|
||||
*
|
||||
* Pre-processes and returns the $_SERVER superglobal.
|
||||
*
|
||||
* @param array $server
|
||||
* @return array
|
||||
*/
|
||||
public static function normalizeServer(array $server)
|
||||
{
|
||||
// This seems to be the only way to get the Authorization header on Apache
|
||||
$apacheRequestHeaders = self::$apacheRequestHeaders;
|
||||
if (isset($server['HTTP_AUTHORIZATION'])
|
||||
|| ! is_callable($apacheRequestHeaders)
|
||||
) {
|
||||
return $server;
|
||||
}
|
||||
|
||||
$apacheRequestHeaders = $apacheRequestHeaders();
|
||||
if (isset($apacheRequestHeaders['Authorization'])) {
|
||||
$server['HTTP_AUTHORIZATION'] = $apacheRequestHeaders['Authorization'];
|
||||
return $server;
|
||||
}
|
||||
|
||||
if (isset($apacheRequestHeaders['authorization'])) {
|
||||
$server['HTTP_AUTHORIZATION'] = $apacheRequestHeaders['authorization'];
|
||||
return $server;
|
||||
}
|
||||
|
||||
return $server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize uploaded files
|
||||
*
|
||||
* Transforms each value into an UploadedFileInterface instance, and ensures
|
||||
* that nested arrays are normalized.
|
||||
*
|
||||
* @param array $files
|
||||
* @return array
|
||||
* @throws InvalidArgumentException for unrecognized values
|
||||
*/
|
||||
public static function normalizeFiles(array $files)
|
||||
{
|
||||
$normalized = [];
|
||||
foreach ($files as $key => $value) {
|
||||
if ($value instanceof UploadedFileInterface) {
|
||||
$normalized[$key] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_array($value) && isset($value['tmp_name'])) {
|
||||
$normalized[$key] = self::createUploadedFileFromSpec($value);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
$normalized[$key] = self::normalizeFiles($value);
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('Invalid value in files specification');
|
||||
}
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marshal headers from $_SERVER
|
||||
*
|
||||
* @param array $server
|
||||
* @return array
|
||||
*/
|
||||
public static function marshalHeaders(array $server)
|
||||
{
|
||||
$headers = [];
|
||||
foreach ($server as $key => $value) {
|
||||
if (strpos($key, 'HTTP_COOKIE') === 0) {
|
||||
// Cookies are handled using the $_COOKIE superglobal
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($value && strpos($key, 'HTTP_') === 0) {
|
||||
$name = strtr(substr($key, 5), '_', ' ');
|
||||
$name = strtr(ucwords(strtolower($name)), ' ', '-');
|
||||
$name = strtolower($name);
|
||||
|
||||
$headers[$name] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($value && strpos($key, 'CONTENT_') === 0) {
|
||||
$name = substr($key, 8); // Content-
|
||||
$name = 'Content-' . (($name == 'MD5') ? $name : ucfirst(strtolower($name)));
|
||||
$name = strtolower($name);
|
||||
$headers[$name] = $value;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marshal the URI from the $_SERVER array and headers
|
||||
*
|
||||
* @param array $server
|
||||
* @param array $headers
|
||||
* @return Uri
|
||||
*/
|
||||
public static function marshalUriFromServer(array $server, array $headers)
|
||||
{
|
||||
$uri = new Uri('');
|
||||
|
||||
// URI scheme
|
||||
$scheme = 'http';
|
||||
$https = self::get('HTTPS', $server);
|
||||
if (($https && 'off' !== $https)
|
||||
|| self::getHeader('x-forwarded-proto', $headers, false) === 'https'
|
||||
) {
|
||||
$scheme = 'https';
|
||||
}
|
||||
if (! empty($scheme)) {
|
||||
$uri = $uri->withScheme($scheme);
|
||||
}
|
||||
|
||||
// Set the host
|
||||
$accumulator = (object) ['host' => '', 'port' => null];
|
||||
self::marshalHostAndPortFromHeaders($accumulator, $server, $headers);
|
||||
$host = $accumulator->host;
|
||||
$port = $accumulator->port;
|
||||
if (! empty($host)) {
|
||||
$uri = $uri->withHost($host);
|
||||
if (! empty($port)) {
|
||||
$uri = $uri->withPort($port);
|
||||
}
|
||||
}
|
||||
|
||||
// URI path
|
||||
$path = self::marshalRequestUri($server);
|
||||
$path = self::stripQueryString($path);
|
||||
|
||||
// URI query
|
||||
$query = '';
|
||||
if (isset($server['QUERY_STRING'])) {
|
||||
$query = ltrim($server['QUERY_STRING'], '?');
|
||||
}
|
||||
|
||||
return $uri
|
||||
->withPath($path)
|
||||
->withQuery($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marshal the host and port from HTTP headers and/or the PHP environment
|
||||
*
|
||||
* @param stdClass $accumulator
|
||||
* @param array $server
|
||||
* @param array $headers
|
||||
*/
|
||||
public static function marshalHostAndPortFromHeaders(stdClass $accumulator, array $server, array $headers)
|
||||
{
|
||||
if (self::getHeader('host', $headers, false)) {
|
||||
self::marshalHostAndPortFromHeader($accumulator, self::getHeader('host', $headers));
|
||||
return;
|
||||
}
|
||||
|
||||
if (! isset($server['SERVER_NAME'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$accumulator->host = $server['SERVER_NAME'];
|
||||
if (isset($server['SERVER_PORT'])) {
|
||||
$accumulator->port = (int) $server['SERVER_PORT'];
|
||||
}
|
||||
|
||||
if (! isset($server['SERVER_ADDR']) || ! preg_match('/^\[[0-9a-fA-F\:]+\]$/', $accumulator->host)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Misinterpreted IPv6-Address
|
||||
// Reported for Safari on Windows
|
||||
self::marshalIpv6HostAndPort($accumulator, $server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the base URI for the request
|
||||
*
|
||||
* Looks at a variety of criteria in order to attempt to autodetect a base
|
||||
* URI, including rewrite URIs, proxy URIs, etc.
|
||||
*
|
||||
* From ZF2's Zend\Http\PhpEnvironment\Request class
|
||||
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*
|
||||
* @param array $server
|
||||
* @return string
|
||||
*/
|
||||
public static function marshalRequestUri(array $server)
|
||||
{
|
||||
// IIS7 with URL Rewrite: make sure we get the unencoded url
|
||||
// (double slash problem).
|
||||
$iisUrlRewritten = self::get('IIS_WasUrlRewritten', $server);
|
||||
$unencodedUrl = self::get('UNENCODED_URL', $server, '');
|
||||
if ('1' == $iisUrlRewritten && ! empty($unencodedUrl)) {
|
||||
return $unencodedUrl;
|
||||
}
|
||||
|
||||
$requestUri = self::get('REQUEST_URI', $server);
|
||||
|
||||
// Check this first so IIS will catch.
|
||||
$httpXRewriteUrl = self::get('HTTP_X_REWRITE_URL', $server);
|
||||
if ($httpXRewriteUrl !== null) {
|
||||
$requestUri = $httpXRewriteUrl;
|
||||
}
|
||||
|
||||
// Check for IIS 7.0 or later with ISAPI_Rewrite
|
||||
$httpXOriginalUrl = self::get('HTTP_X_ORIGINAL_URL', $server);
|
||||
if ($httpXOriginalUrl !== null) {
|
||||
$requestUri = $httpXOriginalUrl;
|
||||
}
|
||||
|
||||
if ($requestUri !== null) {
|
||||
return preg_replace('#^[^/:]+://[^/]+#', '', $requestUri);
|
||||
}
|
||||
|
||||
$origPathInfo = self::get('ORIG_PATH_INFO', $server);
|
||||
if (empty($origPathInfo)) {
|
||||
return '/';
|
||||
}
|
||||
|
||||
return $origPathInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip the query string from a path
|
||||
*
|
||||
* @param mixed $path
|
||||
* @return string
|
||||
*/
|
||||
public static function stripQueryString($path)
|
||||
{
|
||||
if (($qpos = strpos($path, '?')) !== false) {
|
||||
return substr($path, 0, $qpos);
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marshal the host and port from the request header
|
||||
*
|
||||
* @param stdClass $accumulator
|
||||
* @param string|array $host
|
||||
* @return void
|
||||
*/
|
||||
private static function marshalHostAndPortFromHeader(stdClass $accumulator, $host)
|
||||
{
|
||||
if (is_array($host)) {
|
||||
$host = implode(', ', $host);
|
||||
}
|
||||
|
||||
$accumulator->host = $host;
|
||||
$accumulator->port = null;
|
||||
|
||||
// works for regname, IPv4 & IPv6
|
||||
if (preg_match('|\:(\d+)$|', $accumulator->host, $matches)) {
|
||||
$accumulator->host = substr($accumulator->host, 0, -1 * (strlen($matches[1]) + 1));
|
||||
$accumulator->port = (int) $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marshal host/port from misinterpreted IPv6 address
|
||||
*
|
||||
* @param stdClass $accumulator
|
||||
* @param array $server
|
||||
*/
|
||||
private static function marshalIpv6HostAndPort(stdClass $accumulator, array $server)
|
||||
{
|
||||
$accumulator->host = '[' . $server['SERVER_ADDR'] . ']';
|
||||
$accumulator->port = $accumulator->port ?: 80;
|
||||
if ($accumulator->port . ']' === substr($accumulator->host, strrpos($accumulator->host, ':') + 1)) {
|
||||
// The last digit of the IPv6-Address has been taken as port
|
||||
// Unset the port so the default port can be used
|
||||
$accumulator->port = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return an UploadedFile instance from a $_FILES specification.
|
||||
*
|
||||
* If the specification represents an array of values, this method will
|
||||
* delegate to normalizeNestedFileSpec() and return that return value.
|
||||
*
|
||||
* @param array $value $_FILES struct
|
||||
* @return array|UploadedFileInterface
|
||||
*/
|
||||
private static function createUploadedFileFromSpec(array $value)
|
||||
{
|
||||
if (is_array($value['tmp_name'])) {
|
||||
return self::normalizeNestedFileSpec($value);
|
||||
}
|
||||
|
||||
return new UploadedFile(
|
||||
$value['tmp_name'],
|
||||
$value['size'],
|
||||
$value['error'],
|
||||
$value['name'],
|
||||
$value['type']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize an array of file specifications.
|
||||
*
|
||||
* Loops through all nested files and returns a normalized array of
|
||||
* UploadedFileInterface instances.
|
||||
*
|
||||
* @param array $files
|
||||
* @return UploadedFileInterface[]
|
||||
*/
|
||||
private static function normalizeNestedFileSpec(array $files = [])
|
||||
{
|
||||
$normalizedFiles = [];
|
||||
foreach (array_keys($files['tmp_name']) as $key) {
|
||||
$spec = [
|
||||
'tmp_name' => $files['tmp_name'][$key],
|
||||
'size' => $files['size'][$key],
|
||||
'error' => $files['error'][$key],
|
||||
'name' => $files['name'][$key],
|
||||
'type' => $files['type'][$key],
|
||||
];
|
||||
$normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
|
||||
}
|
||||
return $normalizedFiles;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,326 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Implementation of PSR HTTP streams
|
||||
*/
|
||||
class Stream implements StreamInterface
|
||||
{
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
protected $resource;
|
||||
|
||||
/**
|
||||
* @var string|resource
|
||||
*/
|
||||
protected $stream;
|
||||
|
||||
/**
|
||||
* @param string|resource $stream
|
||||
* @param string $mode Mode with which to open stream
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct($stream, $mode = 'r')
|
||||
{
|
||||
$this->stream = $stream;
|
||||
|
||||
if (is_resource($stream)) {
|
||||
$this->resource = $stream;
|
||||
} elseif (is_string($stream)) {
|
||||
set_error_handler(function ($errno, $errstr) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid file provided for stream; must be a valid path with valid permissions'
|
||||
);
|
||||
}, E_WARNING);
|
||||
$this->resource = fopen($stream, $mode);
|
||||
restore_error_handler();
|
||||
} else {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid stream provided; must be a string stream identifier or resource'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
if (! $this->isReadable()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
try {
|
||||
$this->rewind();
|
||||
return $this->getContents();
|
||||
} catch (RuntimeException $e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if (! $this->resource) {
|
||||
return;
|
||||
}
|
||||
|
||||
$resource = $this->detach();
|
||||
fclose($resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function detach()
|
||||
{
|
||||
$resource = $this->resource;
|
||||
$this->resource = null;
|
||||
return $resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a new stream/resource to the instance.
|
||||
*
|
||||
* @param string|resource $resource
|
||||
* @param string $mode
|
||||
* @throws InvalidArgumentException for stream identifier that cannot be
|
||||
* cast to a resource
|
||||
* @throws InvalidArgumentException for non-resource stream
|
||||
*/
|
||||
public function attach($resource, $mode = 'r')
|
||||
{
|
||||
$error = null;
|
||||
if (! is_resource($resource) && is_string($resource)) {
|
||||
set_error_handler(function ($e) use (&$error) {
|
||||
$error = $e;
|
||||
}, E_WARNING);
|
||||
$resource = fopen($resource, $mode);
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
throw new InvalidArgumentException('Invalid stream reference provided');
|
||||
}
|
||||
|
||||
if (! is_resource($resource)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid stream provided; must be a string stream identifier or resource'
|
||||
);
|
||||
}
|
||||
|
||||
$this->resource = $resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
if (null === $this->resource) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$stats = fstat($this->resource);
|
||||
return $stats['size'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tell()
|
||||
{
|
||||
if (! $this->resource) {
|
||||
throw new RuntimeException('No resource available; cannot tell position');
|
||||
}
|
||||
|
||||
$result = ftell($this->resource);
|
||||
if (! is_int($result)) {
|
||||
throw new RuntimeException('Error occurred during tell operation');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function eof()
|
||||
{
|
||||
if (! $this->resource) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return feof($this->resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isSeekable()
|
||||
{
|
||||
if (! $this->resource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$meta = stream_get_meta_data($this->resource);
|
||||
return $meta['seekable'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function seek($offset, $whence = SEEK_SET)
|
||||
{
|
||||
if (! $this->resource) {
|
||||
throw new RuntimeException('No resource available; cannot seek position');
|
||||
}
|
||||
|
||||
if (! $this->isSeekable()) {
|
||||
throw new RuntimeException('Stream is not seekable');
|
||||
}
|
||||
|
||||
$result = fseek($this->resource, $offset, $whence);
|
||||
|
||||
if (0 !== $result) {
|
||||
throw new RuntimeException('Error seeking within stream');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
return $this->seek(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isWritable()
|
||||
{
|
||||
if (! $this->resource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$meta = stream_get_meta_data($this->resource);
|
||||
$mode = $meta['mode'];
|
||||
|
||||
return (
|
||||
strstr($mode, 'x')
|
||||
|| strstr($mode, 'w')
|
||||
|| strstr($mode, 'c')
|
||||
|| strstr($mode, 'a')
|
||||
|| strstr($mode, '+')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($string)
|
||||
{
|
||||
if (! $this->resource) {
|
||||
throw new RuntimeException('No resource available; cannot write');
|
||||
}
|
||||
|
||||
if (! $this->isWritable()) {
|
||||
throw new RuntimeException('Stream is not writable');
|
||||
}
|
||||
|
||||
$result = fwrite($this->resource, $string);
|
||||
|
||||
if (false === $result) {
|
||||
throw new RuntimeException('Error writing to stream');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReadable()
|
||||
{
|
||||
if (! $this->resource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$meta = stream_get_meta_data($this->resource);
|
||||
$mode = $meta['mode'];
|
||||
|
||||
return (strstr($mode, 'r') || strstr($mode, '+'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($length)
|
||||
{
|
||||
if (! $this->resource) {
|
||||
throw new RuntimeException('No resource available; cannot read');
|
||||
}
|
||||
|
||||
if (! $this->isReadable()) {
|
||||
throw new RuntimeException('Stream is not readable');
|
||||
}
|
||||
|
||||
$result = fread($this->resource, $length);
|
||||
|
||||
if (false === $result) {
|
||||
throw new RuntimeException('Error reading stream');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContents()
|
||||
{
|
||||
if (! $this->isReadable()) {
|
||||
throw new RuntimeException('Stream is not readable');
|
||||
}
|
||||
|
||||
$result = stream_get_contents($this->resource);
|
||||
if (false === $result) {
|
||||
throw new RuntimeException('Error reading from stream');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMetadata($key = null)
|
||||
{
|
||||
if (null === $key) {
|
||||
return stream_get_meta_data($this->resource);
|
||||
}
|
||||
|
||||
$metadata = stream_get_meta_data($this->resource);
|
||||
if (! array_key_exists($key, $metadata)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $metadata[$key];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,233 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use RuntimeException;
|
||||
|
||||
class UploadedFile implements UploadedFileInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $clientFilename;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $clientMediaType;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $error;
|
||||
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
private $file;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $moved = false;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $size;
|
||||
|
||||
/**
|
||||
* @var null|StreamInterface
|
||||
*/
|
||||
private $stream;
|
||||
|
||||
public function __construct($streamOrFile, $size, $errorStatus, $clientFilename = null, $clientMediaType = null)
|
||||
{
|
||||
if ($errorStatus === UPLOAD_ERR_OK) {
|
||||
if (is_string($streamOrFile)) {
|
||||
$this->file = $streamOrFile;
|
||||
}
|
||||
if (is_resource($streamOrFile)) {
|
||||
$this->stream = new Stream($streamOrFile);
|
||||
}
|
||||
|
||||
if (! $this->file && ! $this->stream) {
|
||||
if (! $streamOrFile instanceof StreamInterface) {
|
||||
throw new InvalidArgumentException('Invalid stream or file provided for UploadedFile');
|
||||
}
|
||||
$this->stream = $streamOrFile;
|
||||
}
|
||||
}
|
||||
|
||||
if (! is_int($size)) {
|
||||
throw new InvalidArgumentException('Invalid size provided for UploadedFile; must be an int');
|
||||
}
|
||||
$this->size = $size;
|
||||
|
||||
if (! is_int($errorStatus)
|
||||
|| 0 > $errorStatus
|
||||
|| 8 < $errorStatus
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid error status for UploadedFile; must be an UPLOAD_ERR_* constant'
|
||||
);
|
||||
}
|
||||
$this->error = $errorStatus;
|
||||
|
||||
if (null !== $clientFilename && ! is_string($clientFilename)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid client filename provided for UploadedFile; must be null or a string'
|
||||
);
|
||||
}
|
||||
$this->clientFilename = $clientFilename;
|
||||
|
||||
if (null !== $clientMediaType && ! is_string($clientMediaType)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid client media type provided for UploadedFile; must be null or a string'
|
||||
);
|
||||
}
|
||||
$this->clientMediaType = $clientMediaType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws \RuntimeException if the upload was not successful.
|
||||
*/
|
||||
public function getStream()
|
||||
{
|
||||
if ($this->error !== UPLOAD_ERR_OK) {
|
||||
throw new RuntimeException('Cannot retrieve stream due to upload error');
|
||||
}
|
||||
|
||||
if ($this->moved) {
|
||||
throw new RuntimeException('Cannot retrieve stream after it has already been moved');
|
||||
}
|
||||
|
||||
if ($this->stream instanceof StreamInterface) {
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
$this->stream = new Stream($this->file);
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @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 \RuntimeException if the upload was not successful.
|
||||
* @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->error !== UPLOAD_ERR_OK) {
|
||||
throw new RuntimeException('Cannot retrieve stream due to upload error');
|
||||
}
|
||||
|
||||
if (! is_string($targetPath)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid path provided for move operation; must be a string'
|
||||
);
|
||||
}
|
||||
|
||||
if (empty($targetPath)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid path provided for move operation; must be a non-empty string'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->moved) {
|
||||
throw new RuntimeException('Cannot move file; already moved!');
|
||||
}
|
||||
|
||||
$sapi = PHP_SAPI;
|
||||
switch (true) {
|
||||
case (empty($sapi) || 0 === strpos($sapi, 'cli') || ! $this->file):
|
||||
// Non-SAPI environment, or no filename present
|
||||
$this->writeFile($targetPath);
|
||||
break;
|
||||
default:
|
||||
// SAPI environment, with file present
|
||||
if (false === move_uploaded_file($this->file, $targetPath)) {
|
||||
throw new RuntimeException('Error occurred while moving uploaded file');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$this->moved = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return int|null The file size in bytes or null if unknown.
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return string|null The filename sent by the client or null if none
|
||||
* was provided.
|
||||
*/
|
||||
public function getClientFilename()
|
||||
{
|
||||
return $this->clientFilename;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getClientMediaType()
|
||||
{
|
||||
return $this->clientMediaType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write internal stream to given path
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
private function writeFile($path)
|
||||
{
|
||||
$handle = fopen($path, 'wb+');
|
||||
if (false === $handle) {
|
||||
throw new RuntimeException('Unable to write to designated path');
|
||||
}
|
||||
|
||||
$this->stream->rewind();
|
||||
while (! $this->stream->eof()) {
|
||||
fwrite($handle, $this->stream->read(4096));
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,624 @@
|
|||
<?php
|
||||
/**
|
||||
* Zend Framework (http://framework.zend.com/)
|
||||
*
|
||||
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
||||
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Zend\Diactoros;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
/**
|
||||
* Implementation of Psr\Http\UriInterface.
|
||||
*
|
||||
* Provides a value object representing a URI for HTTP requests.
|
||||
*
|
||||
* Instances of this class are considered immutable; all methods that
|
||||
* might change state are implemented such that they retain the internal
|
||||
* state of the current instance and return a new instance that contains the
|
||||
* changed state.
|
||||
*/
|
||||
class Uri implements UriInterface
|
||||
{
|
||||
/**
|
||||
* Sub-delimiters used in query strings and fragments.
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
|
||||
|
||||
/**
|
||||
* Unreserved characters used in paths, query strings, and fragments.
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~';
|
||||
|
||||
/**
|
||||
* @var int[] Array indexed by valid scheme names to their corresponding ports.
|
||||
*/
|
||||
protected $allowedSchemes = [
|
||||
'http' => 80,
|
||||
'https' => 443,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $scheme = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $userInfo = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $host = '';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $port;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $query = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $fragment = '';
|
||||
|
||||
/**
|
||||
* generated uri string cache
|
||||
* @var string|null
|
||||
*/
|
||||
private $uriString;
|
||||
|
||||
/**
|
||||
* @param string $uri
|
||||
* @throws InvalidArgumentException on non-string $uri argument
|
||||
*/
|
||||
public function __construct($uri = '')
|
||||
{
|
||||
if (! is_string($uri)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'URI passed to constructor must be a string; received "%s"',
|
||||
(is_object($uri) ? get_class($uri) : gettype($uri))
|
||||
));
|
||||
}
|
||||
|
||||
if (! empty($uri)) {
|
||||
$this->parseUri($uri);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Operations to perform on clone.
|
||||
*
|
||||
* Since cloning usually is for purposes of mutation, we reset the
|
||||
* $uriString property so it will be re-calculated.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$this->uriString = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
if (null !== $this->uriString) {
|
||||
return $this->uriString;
|
||||
}
|
||||
|
||||
$this->uriString = static::createUriString(
|
||||
$this->scheme,
|
||||
$this->getAuthority(),
|
||||
$this->getPath(), // Absolute URIs should use a "/" for an empty path
|
||||
$this->query,
|
||||
$this->fragment
|
||||
);
|
||||
|
||||
return $this->uriString;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getScheme()
|
||||
{
|
||||
return $this->scheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAuthority()
|
||||
{
|
||||
if (empty($this->host)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$authority = $this->host;
|
||||
if (! empty($this->userInfo)) {
|
||||
$authority = $this->userInfo . '@' . $authority;
|
||||
}
|
||||
|
||||
if ($this->isNonStandardPort($this->scheme, $this->host, $this->port)) {
|
||||
$authority .= ':' . $this->port;
|
||||
}
|
||||
|
||||
return $authority;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUserInfo()
|
||||
{
|
||||
return $this->userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHost()
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPort()
|
||||
{
|
||||
return $this->isNonStandardPort($this->scheme, $this->host, $this->port)
|
||||
? $this->port
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFragment()
|
||||
{
|
||||
return $this->fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function withScheme($scheme)
|
||||
{
|
||||
$scheme = $this->filterScheme($scheme);
|
||||
|
||||
if ($scheme === $this->scheme) {
|
||||
// Do nothing if no change was made.
|
||||
return clone $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->scheme = $scheme;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function withUserInfo($user, $password = null)
|
||||
{
|
||||
$info = $user;
|
||||
if ($password) {
|
||||
$info .= ':' . $password;
|
||||
}
|
||||
|
||||
if ($info === $this->userInfo) {
|
||||
// Do nothing if no change was made.
|
||||
return clone $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->userInfo = $info;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function withHost($host)
|
||||
{
|
||||
if ($host === $this->host) {
|
||||
// Do nothing if no change was made.
|
||||
return clone $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->host = $host;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function withPort($port)
|
||||
{
|
||||
if (! is_numeric($port)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Invalid port "%s" specified; must be an integer or integer string',
|
||||
(is_object($port) ? get_class($port) : gettype($port))
|
||||
));
|
||||
}
|
||||
|
||||
$port = (int) $port;
|
||||
|
||||
if ($port === $this->port) {
|
||||
// Do nothing if no change was made.
|
||||
return clone $this;
|
||||
}
|
||||
|
||||
if ($port < 1 || $port > 65535) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Invalid port "%d" specified; must be a valid TCP/UDP port',
|
||||
$port
|
||||
));
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->port = $port;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function withPath($path)
|
||||
{
|
||||
if (! is_string($path)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid path provided; must be a string'
|
||||
);
|
||||
}
|
||||
|
||||
if (strpos($path, '?') !== false) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid path provided; must not contain a query string'
|
||||
);
|
||||
}
|
||||
|
||||
if (strpos($path, '#') !== false) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid path provided; must not contain a URI fragment'
|
||||
);
|
||||
}
|
||||
|
||||
$path = $this->filterPath($path);
|
||||
|
||||
if ($path === $this->path) {
|
||||
// Do nothing if no change was made.
|
||||
return clone $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->path = $path;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function withQuery($query)
|
||||
{
|
||||
if (! is_string($query)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Query string must be a string'
|
||||
);
|
||||
}
|
||||
|
||||
if (strpos($query, '#') !== false) {
|
||||
throw new InvalidArgumentException(
|
||||
'Query string must not include a URI fragment'
|
||||
);
|
||||
}
|
||||
|
||||
$query = $this->filterQuery($query);
|
||||
|
||||
if ($query === $this->query) {
|
||||
// Do nothing if no change was made.
|
||||
return clone $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->query = $query;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function withFragment($fragment)
|
||||
{
|
||||
$fragment = $this->filterFragment($fragment);
|
||||
|
||||
if ($fragment === $this->fragment) {
|
||||
// Do nothing if no change was made.
|
||||
return clone $this;
|
||||
}
|
||||
|
||||
$new = clone $this;
|
||||
$new->fragment = $fragment;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a URI into its parts, and set the properties
|
||||
*
|
||||
* @param string $uri
|
||||
*/
|
||||
private function parseUri($uri)
|
||||
{
|
||||
$parts = parse_url($uri);
|
||||
|
||||
if (false === $parts) {
|
||||
throw new \InvalidArgumentException(
|
||||
'The source URI string appears to be malformed'
|
||||
);
|
||||
}
|
||||
|
||||
$this->scheme = isset($parts['scheme']) ? $this->filterScheme($parts['scheme']) : '';
|
||||
$this->userInfo = isset($parts['user']) ? $parts['user'] : '';
|
||||
$this->host = isset($parts['host']) ? $parts['host'] : '';
|
||||
$this->port = isset($parts['port']) ? $parts['port'] : null;
|
||||
$this->path = isset($parts['path']) ? $this->filterPath($parts['path']) : '';
|
||||
$this->query = isset($parts['query']) ? $this->filterQuery($parts['query']) : '';
|
||||
$this->fragment = isset($parts['fragment']) ? $this->filterFragment($parts['fragment']) : '';
|
||||
|
||||
if (isset($parts['pass'])) {
|
||||
$this->userInfo .= ':' . $parts['pass'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a URI string from its various parts
|
||||
*
|
||||
* @param string $scheme
|
||||
* @param string $authority
|
||||
* @param string $path
|
||||
* @param string $query
|
||||
* @param string $fragment
|
||||
* @return string
|
||||
*/
|
||||
private static function createUriString($scheme, $authority, $path, $query, $fragment)
|
||||
{
|
||||
$uri = '';
|
||||
|
||||
if (! empty($scheme)) {
|
||||
$uri .= sprintf('%s://', $scheme);
|
||||
}
|
||||
|
||||
if (! empty($authority)) {
|
||||
$uri .= $authority;
|
||||
}
|
||||
|
||||
if ($path) {
|
||||
if (empty($path) || '/' !== substr($path, 0, 1)) {
|
||||
$path = '/' . $path;
|
||||
}
|
||||
|
||||
$uri .= $path;
|
||||
}
|
||||
|
||||
if ($query) {
|
||||
$uri .= sprintf('?%s', $query);
|
||||
}
|
||||
|
||||
if ($fragment) {
|
||||
$uri .= sprintf('#%s', $fragment);
|
||||
}
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a given port non-standard for the current scheme?
|
||||
*
|
||||
* @param string $scheme
|
||||
* @param string $host
|
||||
* @param int $port
|
||||
* @return bool
|
||||
*/
|
||||
private function isNonStandardPort($scheme, $host, $port)
|
||||
{
|
||||
if (! $scheme) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! $host || ! $port) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ! isset($this->allowedSchemes[$scheme]) || $port !== $this->allowedSchemes[$scheme];
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the scheme to ensure it is a valid scheme.
|
||||
*
|
||||
* @param string $scheme Scheme name.
|
||||
*
|
||||
* @return string Filtered scheme.
|
||||
*/
|
||||
private function filterScheme($scheme)
|
||||
{
|
||||
$scheme = strtolower($scheme);
|
||||
$scheme = preg_replace('#:(//)?$#', '', $scheme);
|
||||
|
||||
if (empty($scheme)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (! array_key_exists($scheme, $this->allowedSchemes)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Unsupported scheme "%s"; must be any empty string or in the set (%s)',
|
||||
$scheme,
|
||||
implode(', ', array_keys($this->allowedSchemes))
|
||||
));
|
||||
}
|
||||
|
||||
return $scheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the path of a URI to ensure it is properly encoded.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
private function filterPath($path)
|
||||
{
|
||||
$path = preg_replace_callback(
|
||||
'/(?:[^' . self::CHAR_UNRESERVED . ':@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/',
|
||||
[$this, 'urlEncodeChar'],
|
||||
$path
|
||||
);
|
||||
|
||||
if (empty($path)) {
|
||||
// No path
|
||||
return $path;
|
||||
}
|
||||
|
||||
if ($path[0] !== '/') {
|
||||
// Relative path
|
||||
return $path;
|
||||
}
|
||||
|
||||
// Ensure only one leading slash, to prevent XSS attempts.
|
||||
return '/' . ltrim($path, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a query string to ensure it is propertly encoded.
|
||||
*
|
||||
* Ensures that the values in the query string are properly urlencoded.
|
||||
*
|
||||
* @param string $query
|
||||
* @return string
|
||||
*/
|
||||
private function filterQuery($query)
|
||||
{
|
||||
if (! empty($query) && strpos($query, '?') === 0) {
|
||||
$query = substr($query, 1);
|
||||
}
|
||||
|
||||
$parts = explode('&', $query);
|
||||
foreach ($parts as $index => $part) {
|
||||
list($key, $value) = $this->splitQueryValue($part);
|
||||
if ($value === null) {
|
||||
$parts[$index] = $this->filterQueryOrFragment($key);
|
||||
continue;
|
||||
}
|
||||
$parts[$index] = sprintf(
|
||||
'%s=%s',
|
||||
$this->filterQueryOrFragment($key),
|
||||
$this->filterQueryOrFragment($value)
|
||||
);
|
||||
}
|
||||
|
||||
return implode('&', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a query value into a key/value tuple.
|
||||
*
|
||||
* @param string $value
|
||||
* @return array A value with exactly two elements, key and value
|
||||
*/
|
||||
private function splitQueryValue($value)
|
||||
{
|
||||
$data = explode('=', $value, 2);
|
||||
if (1 === count($data)) {
|
||||
$data[] = null;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a fragment value to ensure it is properly encoded.
|
||||
*
|
||||
* @param null|string $fragment
|
||||
* @return string
|
||||
*/
|
||||
private function filterFragment($fragment)
|
||||
{
|
||||
if (null === $fragment) {
|
||||
$fragment = '';
|
||||
}
|
||||
|
||||
if (! empty($fragment) && strpos($fragment, '#') === 0) {
|
||||
$fragment = substr($fragment, 1);
|
||||
}
|
||||
|
||||
return $this->filterQueryOrFragment($fragment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a query string key or value, or a fragment.
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
private function filterQueryOrFragment($value)
|
||||
{
|
||||
return preg_replace_callback(
|
||||
'/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/\?]+|%(?![A-Fa-f0-9]{2}))/',
|
||||
[$this, 'urlEncodeChar'],
|
||||
$value
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* URL encode a character returned by a regex.
|
||||
*
|
||||
* @param array $matches
|
||||
* @return string
|
||||
*/
|
||||
private function urlEncodeChar(array $matches)
|
||||
{
|
||||
return rawurlencode($matches[0]);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue