OpenXE/classes/Components/Http/Request.php

559 lines
15 KiB
PHP
Raw Normal View History

2021-05-21 08:49:41 +02:00
<?php
2023-03-30 10:54:18 +02:00
/*
* SPDX-FileCopyrightText: 2023 Andreas Palm
* SPDX-FileCopyrightText: 2019 Xentral ERP Software GmbH, Fuggerstrasse 11, D-86150 Augsburg
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
*/
2021-05-21 08:49:41 +02:00
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&param2=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 : '';
}
2023-03-30 10:54:18 +02:00
/**
* Return a JSON request body as php object
*
* @return object
*/
public function getJson() : object
{
return json_decode($this->getContent());
}
2021-05-21 08:49:41 +02:00
/**
* 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;
}
}