mirror of
https://github.com/OpenXE-org/OpenXE.git
synced 2024-12-27 23:20:28 +01:00
544 lines
15 KiB
PHP
544 lines
15 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace Xentral\Components\Http;
|
||
|
|
||
|
use Xentral\Components\Http\Collection\FilesCollection;
|
||
|
use Xentral\Components\Http\Collection\ParameterCollection;
|
||
|
use Xentral\Components\Http\Collection\ReadonlyParameterCollection;
|
||
|
use Xentral\Components\Http\Collection\ServerParameter;
|
||
|
use Xentral\Components\Http\Exception\HttpException;
|
||
|
use Xentral\Components\Http\Exception\InvalidArgumentException;
|
||
|
use Xentral\Components\Http\Exception\MethodNotAllowedException;
|
||
|
use Xentral\Components\Http\File\FileUpload;
|
||
|
|
||
|
class Request
|
||
|
{
|
||
|
/** @var array $supportedMethods */
|
||
|
protected static $supportedMethods = [
|
||
|
'GET',
|
||
|
'POST',
|
||
|
'PUT',
|
||
|
'DELETE',
|
||
|
];
|
||
|
|
||
|
/** @var ReadonlyParameterCollection $get $_GET-Parameter */
|
||
|
public $get;
|
||
|
|
||
|
/** @var ReadonlyParameterCollection $post $_POST-Parameter */
|
||
|
public $post;
|
||
|
|
||
|
/** @var ReadonlyParameterCollection $cookie $_COOKIE-Parameter */
|
||
|
public $cookie;
|
||
|
|
||
|
/** @var FilesCollection $files $_FILES-Parameter */
|
||
|
public $files;
|
||
|
|
||
|
/** @var ServerParameter $server $_SERVER-Parameter */
|
||
|
public $server;
|
||
|
|
||
|
/** @var ReadonlyParameterCollection $header */
|
||
|
public $header;
|
||
|
|
||
|
/** @var ParameterCollection $attributes Custom request attributes (e.g. Router arguments) */
|
||
|
public $attributes;
|
||
|
|
||
|
/** @var string $method */
|
||
|
protected $method;
|
||
|
|
||
|
/** @var string $pathInfo */
|
||
|
protected $pathInfo;
|
||
|
|
||
|
/** @var string $requestUri */
|
||
|
protected $requestUri;
|
||
|
|
||
|
/** @var string|resource|null $content */
|
||
|
protected $content;
|
||
|
|
||
|
/** @var array $acceptableContentTypes */
|
||
|
protected $acceptableContentTypes;
|
||
|
|
||
|
/**
|
||
|
* @param array $get
|
||
|
* @param array $post
|
||
|
* @param array $files
|
||
|
* @param array $server
|
||
|
* @param array $cookie
|
||
|
* @param string|resource|null $content
|
||
|
*/
|
||
|
public function __construct(
|
||
|
array $get = [],
|
||
|
array $post = [],
|
||
|
array $files = [],
|
||
|
array $server = [],
|
||
|
array $cookie = [],
|
||
|
$content = null
|
||
|
) {
|
||
|
$this->get = new ReadonlyParameterCollection(!empty($get) ? $get : (array)$_GET);
|
||
|
$this->post = new ReadonlyParameterCollection(!empty($post) ? $post : (array)$_POST);
|
||
|
$this->files = new FilesCollection(!empty($files) ? $files : (array)$_FILES);
|
||
|
$this->server = new ServerParameter(!empty($server) ? $server : (array)$_SERVER);
|
||
|
$this->cookie = new ReadonlyParameterCollection(!empty($cookie) ? $cookie : (array)$_COOKIE);
|
||
|
$this->header = new ReadonlyParameterCollection($this->server->getHeaders());
|
||
|
$this->attributes = new ParameterCollection([]);
|
||
|
$this->method = $this->getMethod();
|
||
|
$this->requestUri = $this->getRequestUri();
|
||
|
$this->pathInfo = $this->getPathInfo();
|
||
|
$this->content = $content;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns an instance of Request created with php's superglobals.
|
||
|
*
|
||
|
* @param string|null $content
|
||
|
*
|
||
|
* @return Request
|
||
|
*/
|
||
|
public static function createFromGlobals($content = null)
|
||
|
{
|
||
|
$request = new static((array)$_GET, (array)$_POST, (array)$_FILES, (array)$_SERVER, (array)$_COOKIE, $content);
|
||
|
|
||
|
if (
|
||
|
$request->server->get('CONTENT_TYPE') === 'application/x-www-form-urlencoded' &&
|
||
|
strtoupper($request->server->get('REQUEST_METHOD')) === 'PUT'
|
||
|
) {
|
||
|
parse_str($request->getContent(), $postParams);
|
||
|
$request->post = new ReadonlyParameterCollection($postParams);
|
||
|
}
|
||
|
|
||
|
return $request;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets a $_GET parameter value
|
||
|
*
|
||
|
* @param string $name
|
||
|
* @param mixed|null $default
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public function getGet($name, $default = null)
|
||
|
{
|
||
|
return $this->get->get($name, $default);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets a $_POST parameter value
|
||
|
*
|
||
|
* @param string $name
|
||
|
* @param mixed|null $default
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public function getPost($name, $default = null)
|
||
|
{
|
||
|
return $this->post->get($name, $default);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets a $_GET or $_POST parameter value
|
||
|
*
|
||
|
* Looks at $_GET parameters first.
|
||
|
*
|
||
|
* @param string $name
|
||
|
* @param mixed|null $default
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public function getParam($name, $default = null)
|
||
|
{
|
||
|
if ($this->get->has($name)) {
|
||
|
return $this->get->get($name);
|
||
|
}
|
||
|
|
||
|
if ($this->post->has($name)) {
|
||
|
return $this->post->get($name);
|
||
|
}
|
||
|
|
||
|
return $default;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets a $_FILES parameter value
|
||
|
*
|
||
|
* @param string $name
|
||
|
* @param mixed|null $default
|
||
|
*
|
||
|
* @return FileUpload|mixed
|
||
|
*/
|
||
|
public function getFile($name, $default = null)
|
||
|
{
|
||
|
return $this->files->get($name, $default);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets a $_SERVER parameter value
|
||
|
*
|
||
|
* @param string $name
|
||
|
* @param mixed|null $default
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public function getServer($name, $default = null)
|
||
|
{
|
||
|
return $this->server->get($name, $default);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets a HTTP header value
|
||
|
*
|
||
|
* Use the HTTP header name as used in the Browser.
|
||
|
*
|
||
|
* @example getHeader('Content-Type') returns 'text'
|
||
|
* @example getHeader('CONTENT_TYPE') returns null
|
||
|
*
|
||
|
* @param string $name
|
||
|
* @param mixed|null $default
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public function getHeader($name, $default = null)
|
||
|
{
|
||
|
return $this->header->get($name, $default);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets a $_COOKIE parameter value
|
||
|
*
|
||
|
* @param string $name
|
||
|
* @param mixed|null $default
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public function getCookie($name, $default = null)
|
||
|
{
|
||
|
return $this->cookie->get($name, $default);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if a secure protocol was used.
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function isSecure()
|
||
|
{
|
||
|
if ($this->server->get('HTTPS') === 'on') {
|
||
|
return true;
|
||
|
}
|
||
|
if ($this->server->get('HTTP_X_FORWARDED_SSL') === 'on') {
|
||
|
return true;
|
||
|
}
|
||
|
if ($this->server->get('HTTP_X_FORWARDED_PROTO') === 'https') {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if the request is an ajax request.
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function isAjax()
|
||
|
{
|
||
|
return strtolower($this->server->get('HTTP_X_REQUESTED_WITH')) === 'xmlhttprequest';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if the request was issued via command line.
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function isCli()
|
||
|
{
|
||
|
return php_sapi_name() === 'cli';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the HTTP method.
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function getMethod()
|
||
|
{
|
||
|
if ($this->method === null) {
|
||
|
$method = strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
|
||
|
if (!in_array($method, self::$supportedMethods, true)) {
|
||
|
throw new MethodNotAllowedException(self::$supportedMethods);
|
||
|
}
|
||
|
$this->method = $method;
|
||
|
}
|
||
|
|
||
|
return $this->method;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the request URI.
|
||
|
*
|
||
|
* Same as $_SERVER['REQUEST_URI']
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function getRequestUri()
|
||
|
{
|
||
|
if (null === $this->requestUri) {
|
||
|
$this->requestUri = $this->server->get('REQUEST_URI');
|
||
|
}
|
||
|
|
||
|
return $this->requestUri;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get path info from the request.
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function getPathInfo()
|
||
|
{
|
||
|
if (null === $this->pathInfo) {
|
||
|
$this->pathInfo = !empty($this->server->get('PATH_INFO')) ? $this->server->get('PATH_INFO') : '';
|
||
|
}
|
||
|
|
||
|
return $this->pathInfo;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns protocol and hostname.
|
||
|
*
|
||
|
* @example 'http://www.xentral.com'
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function getSchemeAndHttpHost()
|
||
|
{
|
||
|
$protocol = 'http';
|
||
|
if ($this->isSecure() || strtolower($this->server->get('REQUEST_SCHEME')) === 'https') {
|
||
|
$protocol = 'https';
|
||
|
}
|
||
|
return sprintf(
|
||
|
'%s://%s',
|
||
|
$protocol,
|
||
|
$this->server->get('HTTP_HOST')
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the full URL with GET parameters.
|
||
|
*
|
||
|
* @example ->'http://www.xentral.com/path/index.php?param=1¶m2=2'
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function getFullUrl()
|
||
|
{
|
||
|
return $this->getSchemeAndHttpHost() . $this->getRequestUri();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the URL without GET parameters.
|
||
|
*
|
||
|
* @example 'http://www.xentral.com/path/index.php?var=1' -> 'http://www.xentral.com/path'
|
||
|
* @example 'http://www.xentral.com/path/?var=1' -> 'http://www.xentral.com/path'
|
||
|
* @example 'http://www.xentral.com/path?var=1' -> 'http://www.xentral.com'
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function getBaseUrl()
|
||
|
{
|
||
|
$baseUrl = '';
|
||
|
$uri = $this->getRequestUri();
|
||
|
$uri = preg_replace('/([^?]+)[?]?.*/', '\1', $uri);
|
||
|
|
||
|
$uriParts = explode('/', $uri);
|
||
|
for ($i=0; $i < count($uriParts)-1; $i++) {
|
||
|
if( $uriParts[$i] !== '') {
|
||
|
$baseUrl .= '/' . $uriParts[$i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$baseUrl = $this->getSchemeAndHttpHost() . $baseUrl;
|
||
|
|
||
|
//Failsafe?
|
||
|
// $queryString = $this->server->get('QUERY_STRING');
|
||
|
// parse_str($queryString, $queryParts);
|
||
|
//
|
||
|
// /** @see /www/api/docs.html#failsafe */
|
||
|
// if (isset($queryParts['path'])) {
|
||
|
// return sprintf('%s?path=%s', $baseUrl, $queryParts['path']);
|
||
|
// }
|
||
|
|
||
|
return $baseUrl;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Appends specified path to the URL.
|
||
|
*
|
||
|
* @example getUrlForPath('/path') -> 'http://www.xentral.com/path'
|
||
|
*
|
||
|
* @param string $path must starts with a slash '/'
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function getUrlForPath($path)
|
||
|
{
|
||
|
if (!preg_match('/^\/.*$/', $path)) {
|
||
|
throw new InvalidArgumentException('The first argument must start with a slash "/"');
|
||
|
}
|
||
|
|
||
|
$schemeAndHost = $this->getSchemeAndHttpHost();
|
||
|
$urlPath = preg_replace('/^(.*)\/[^\/]*$/', '\1', $this->server->get('SCRIPT_NAME'));
|
||
|
|
||
|
return sprintf('%s%s%s', $schemeAndHost, $urlPath, $path);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*Returns the path between the URI and the current SCRIPT_NAME
|
||
|
*
|
||
|
* @example 'http://www.xentral.com/www/api/v1/dateien/50' -> '/v1/dateien/50'
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function getBasePath()
|
||
|
{
|
||
|
$basePath = '';
|
||
|
$scriptNameParts = explode('/', $this->server->get('SCRIPT_NAME'));
|
||
|
$uri = preg_replace('/([^?]+)[?]?.*/', '\1', $this->server->get('REQUEST_URI'));
|
||
|
$uriParts = explode('/', $uri);
|
||
|
|
||
|
for($i=0; $i < count($uriParts)-0; $i++) {
|
||
|
if (!isset($scriptNameParts[$i]) || $scriptNameParts[$i] !== $uriParts[$i]) {
|
||
|
$basePath .= '/'. $uriParts[$i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if($basePath === '') {
|
||
|
$basePath = '/';
|
||
|
}
|
||
|
|
||
|
return $basePath;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @deprecated Use getFullUrl or getBaseUrl instead!
|
||
|
*
|
||
|
* @param bool $withQueryParams GET-Parameter mitliefern?
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function getFullUri($withQueryParams = true)
|
||
|
{
|
||
|
$scheme = $this->server->get('REQUEST_SCHEME');
|
||
|
$hostAndPort = $this->server->get('HTTP_HOST');
|
||
|
$requestUri = $this->server->get('REQUEST_URI');
|
||
|
|
||
|
$fullUriWithQueryParams = sprintf('%s://%s%s', $scheme, $hostAndPort, $requestUri);
|
||
|
if ($withQueryParams === true) {
|
||
|
return $fullUriWithQueryParams;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Nachfolgend werden die GET-Parameter aus der Uri entfernt
|
||
|
*/
|
||
|
|
||
|
$offset = strpos($fullUriWithQueryParams, '?');
|
||
|
$fullUriWithoutQueryParams = $offset !== false
|
||
|
? substr_replace($fullUriWithQueryParams, '', $offset)
|
||
|
: $fullUriWithQueryParams;
|
||
|
|
||
|
// Query-String zerlegen
|
||
|
$queryString = $this->server->get('QUERY_STRING');
|
||
|
parse_str($queryString, $queryParts);
|
||
|
|
||
|
/** @see /www/api/docs.html#failsafe */
|
||
|
if (isset($queryParts['path'])) {
|
||
|
return $fullUriWithoutQueryParams . '?path=' . $queryParts['path'];
|
||
|
}
|
||
|
|
||
|
return $fullUriWithoutQueryParams;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if the failsafe URL was used for the request
|
||
|
*
|
||
|
* @example Failsafe-Uri: /api/index.php?path=/v1/adressen
|
||
|
*
|
||
|
* @see /www/api/docs.html#failsafe
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function isFailsafeUri()
|
||
|
{
|
||
|
$queryString = $this->server->get('QUERY_STRING');
|
||
|
parse_str($queryString, $queryParts);
|
||
|
|
||
|
return isset($queryParts['path']);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the request body.
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function getContent()
|
||
|
{
|
||
|
if (null === $this->content) {
|
||
|
$this->content = file_get_contents('php://input');
|
||
|
}
|
||
|
|
||
|
return !empty($this->content) ? $this->content : '';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the content type of the request.
|
||
|
*
|
||
|
* @return string|null [json|xml|html|...] null if not set
|
||
|
*/
|
||
|
public function getContentType()
|
||
|
{
|
||
|
$contentTypeRaw = $this->header->get('Content-Type');
|
||
|
if ($contentTypeRaw === null || $contentTypeRaw === '') {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
// Boundary bei Multipart Content-Type entfernen
|
||
|
// @example "Content-Type: multipart/form-data; boundary=gc0p4Jq0M2Yt08jU534c0p"
|
||
|
$posSemicolon = strpos($contentTypeRaw, ';');
|
||
|
if ($posSemicolon !== false) {
|
||
|
$contentTypeRaw = trim(substr($contentTypeRaw, 0, $posSemicolon));
|
||
|
}
|
||
|
|
||
|
$typeParts = explode('/', strtolower($contentTypeRaw));
|
||
|
if (count($typeParts) < 2) {
|
||
|
throw new HttpException(400, sprintf('Invalid content type "%s".', $contentTypeRaw));
|
||
|
}
|
||
|
|
||
|
return $typeParts[1];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the data types from the HTTP Accept header.
|
||
|
*
|
||
|
* @return array [] if not set
|
||
|
*/
|
||
|
public function getAcceptableContentTypes()
|
||
|
{
|
||
|
if (null === $this->acceptableContentTypes) {
|
||
|
$acceptHeaderRaw = $this->header->get('Accept');
|
||
|
$acceptParts = explode(',', $acceptHeaderRaw);
|
||
|
|
||
|
$acceptable = [];
|
||
|
foreach ($acceptParts as $acceptPart) {
|
||
|
if ($pos = strpos($acceptPart, ';')) {
|
||
|
// Priorität abschneiden
|
||
|
$acceptPart = substr($acceptPart, 0, $pos);
|
||
|
}
|
||
|
$acceptable[] = trim($acceptPart);
|
||
|
}
|
||
|
|
||
|
$this->acceptableContentTypes = $acceptable;
|
||
|
}
|
||
|
|
||
|
return $this->acceptableContentTypes;
|
||
|
}
|
||
|
}
|