<?php
namespace Poirot\Psr7\Response
{
// TODO Code clone from Poirot\Http\Response
/**
* Map of standard HTTP status code/reason phrases
*
* @var array
*/
$phrases = array(
// 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',
);
/**
* Get Status Code Reason
*
* @param int $statusCode
*
* @return null|string
*/
function getStatReasonFromCode($statusCode)
{
global $phrases;
return isset($phrases[$statusCode]) ? $phrases[$statusCode] : null;
}
}
namespace Poirot\Psr7
{
use Poirot\Ioc\instance;
use Poirot\Std\Type\StdTravers;
use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;
const URI_APPEND = 'uri.path.append';
const URI_PREPEND = 'uri.path.prepend';
/**
* String representation of an HTTP message.
*
* @param MessageInterface $httpMessage
*
* @return string
*/
function renderHttpMessage(MessageInterface $httpMessage)
{
$msg = renderHeaderHttpMessage($httpMessage);
return "{$msg}\r\n" . $httpMessage->getBody();
}
/**
* String representation of Headers of an HTTP message.
*
* @param MessageInterface $httpMessage
*
* @return string
*/
function renderHeaderHttpMessage(MessageInterface $httpMessage)
{
if ($httpMessage instanceof RequestInterface) {
$msg = trim($httpMessage->getMethod() . ' '
. $httpMessage->getRequestTarget())
. ' HTTP/' . $httpMessage->getProtocolVersion();
if (!$httpMessage->hasHeader('host'))
$msg .= "\r\nHost: " . $httpMessage->getUri()->getHost();
} elseif ($httpMessage instanceof ResponseInterface) {
$msg = 'HTTP/' . $httpMessage->getProtocolVersion() . ' '
. $httpMessage->getStatusCode() . ' '
. $httpMessage->getReasonPhrase();
} else
throw new \InvalidArgumentException('Unknown message type');
foreach ($httpMessage->getHeaders() as $name => $values) {
$msg .= "\r\n{$name}: ".$httpMessage->getHeaderLine($name);
}
return "{$msg}\r\n";
}
/**
* Clone and modify a uri with the given changes.
*
* - current values Must merge with given changes
*
* @param UriInterface $uri
* @param array|\Traversable $changes
* @param string $uriPath
*
* @return UriInterface
*/
function modifyUri(UriInterface $uri, $changes, $uriPath = URI_PREPEND)
{
if ($changes instanceof \Traversable)
$changes = \Poirot\Std\cast($changes)->toArray();
if (!is_array($changes))
throw new \InvalidArgumentException(sprintf(
'Changes must be an array or instance of Traversable; given: (%s).'
, \Poirot\Std\flatten($changes)
));
$uri = clone $uri;
if (isset($changes['scheme']))
$uri = $uri->withScheme($changes['scheme']);
if (isset($changes['host']))
$uri = $uri->withHost($changes['host']);
if (isset($changes['port']))
$uri = $uri->withPort($changes['port']);
if (isset($changes['user_info'])) {
if (empty($changes['user_info']))
// remove user info
$uri = $uri->withUserInfo('');
else {
list ($username, $password) = explode(':', $changes['user_info']);
$uri = $uri->withUserInfo($username, $password);
}
}
if (isset($changes['fragment']))
$uri = $uri->withFragment($changes['fragment']);
if (isset($changes['path'])) {
$curPath = $uri->getPath();
if ($uriPath === URI_PREPEND)
$uri = $uri->withPath(rtrim($changes['path'], '/').'/'.trim($curPath,'/'));
else
$uri = $uri->withPath(rtrim($curPath, '/').'/'.trim($changes['path'], '/'));
}
if (isset($changes['query'])) {
$query = $changes['query'];
if (is_string($query))
$mrgQuery = parseQuery($changes['query']);
elseif ($query instanceof \Traversable)
$mrgQuery = StdTravers::of($query)->toArray();
else
$mrgQuery = $query;
if (!is_array($mrgQuery))
throw new \InvalidArgumentException(sprintf(
'Query params must be Traversable or array or query string. given: (%s).'
, \Poirot\Std\flatten($mrgQuery)
));
$curQuery = parseQuery($uri->getQuery());
if ($uriPath === URI_PREPEND)
$query = array_merge($curQuery, $mrgQuery);
else
$query = array_merge($mrgQuery, $curQuery);
foreach ($query as $key => $val) {
// Cleanup null values from query params
if ($val === null)
unset($query[$key]);
}
$uri = $uri->withQuery(buildQuery($query));
}
return $uri;
}
/**
* Parse Psr Uri To It's Parts
*
* @param UriInterface $uri
*
* @return array
*/
function parseUriPsr(UriInterface $uri)
{
$parsed = array(
'scheme' => $uri->getScheme(),
'user_info' => $uri->getUserInfo(),
'host' => $uri->getHost(),
'port' => $uri->getPort(),
'path' => $uri->getPath(),
'query' => $uri->getQuery(),
'fragment' => $uri->getFragment(),
);
return $parsed;
}
/**
* Build a query string from an array of key value pairs.
*
* This function can use the return value of parseQuery() to build a query
* string. This function does not modify the provided keys when an array is
* encountered (like http_build_query would).
*
* @param array $params Query string parameters.
* @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
* to encode using RFC3986, or PHP_QUERY_RFC1738
* to encode using RFC1738.
* @return string
*/
function buildQuery(array $params, $encoding = PHP_QUERY_RFC3986)
{
if (!$params)
return '';
if ($encoding === false) {
$encoder = function ($str) { return $str; };
} elseif ($encoding == PHP_QUERY_RFC3986) {
$encoder = 'rawurlencode';
} elseif ($encoding == PHP_QUERY_RFC1738) {
$encoder = 'urlencode';
} else {
throw new \InvalidArgumentException('Invalid type');
}
$qs = '';
foreach ($params as $k => $v) {
if ($v === null)
// null meaning not has value; so delete it
continue;
$k = $encoder($k);
if (!is_array($v)) {
$qs .= $k;
if ($v !== null) {
$qs .= '=' . $encoder($v);
}
$qs .= '&';
} else {
foreach ($v as $vv) {
$qs .= $k;
if ($vv !== null) {
$qs .= '=' . $encoder($vv);
}
$qs .= '&';
}
}
}
return $qs ? (string) substr($qs, 0, -1) : '';
}
/**
* Parse a query string into an associative array.
*
* If multiple values are found for the same key, the value of that key
* value pair will become an array. This function does not parse nested
* PHP style arrays into an associative array (e.g., foo[a]=1&foo[b]=2 will
* be parsed into ['foo[a]' => '1', 'foo[b]' => '2']).
*
* @param string $str Query string to parse
* @param bool|string $urlEncoding How the query string is encoded
*
* @return array
*/
function parseQuery($str, $urlEncoding = true)
{
$result = array();
$str = (string) $str;
if ($str === '') {
return $result;
}
if ($urlEncoding === true) {
$decoder = function ($value) {
return rawurldecode(str_replace('+', ' ', $value));
};
} elseif ($urlEncoding == PHP_QUERY_RFC3986) {
$decoder = 'rawurldecode';
} elseif ($urlEncoding == PHP_QUERY_RFC1738) {
$decoder = 'urldecode';
} else {
$decoder = function ($str) { return $str; };
}
foreach (explode('&', $str) as $kvp) {
$parts = explode('=', $kvp, 2);
$key = $decoder($parts[0]);
$value = isset($parts[1]) ? $decoder($parts[1]) : null;
if (!isset($result[$key])) {
$result[$key] = $value;
} else {
if (!is_array($result[$key])) {
$result[$key] = array($result[$key]);
}
$result[$key][] = $value;
}
}
return $result;
}
function getUploadErrorMessageFromCode($code)
{
switch ($code) {
case UPLOAD_ERR_INI_SIZE:
$message = 'The uploaded file exceeds the Server filesize Setting.';
break;
case UPLOAD_ERR_FORM_SIZE:
$message = 'The uploaded file exceeds the Form filesize Setting.';
break;
case UPLOAD_ERR_NO_FILE:
$message = 'No file was uploaded.';
break;
case UPLOAD_ERR_NO_TMP_DIR:
$message = 'Missing a temporary folder.';
break;
case UPLOAD_ERR_CANT_WRITE:
$message = 'Failed to write file to disk.';
break;
default: $message = $code;
}
return $message;
}
}
|