Просмотр файла libarea-0.9/vendor/utopia-php/framework/src/Request.php

Размер файла: 16.42Kb
<?php

namespace Utopia;

class Request
{
    /**
     * HTTP methods
     */
    public const METHOD_OPTIONS = 'OPTIONS';

    public const METHOD_GET = 'GET';

    public const METHOD_HEAD = 'HEAD';

    public const METHOD_POST = 'POST';

    public const METHOD_PATCH = 'PATCH';

    public const METHOD_PUT = 'PUT';

    public const METHOD_DELETE = 'DELETE';

    public const METHOD_TRACE = 'TRACE';

    public const METHOD_CONNECT = 'CONNECT';

    /**
     * Container for raw php://input parsed stream
     *
     * @var string
     */
    private $rawPayload = '';

    /**
     * Container for php://input parsed stream as an associative array
     *
     * @var array|null
     */
    protected $payload = null;

    /**
     * Container for parsed query string params
     *
     * @var array|null
     */
    protected $queryString = null;

    /**
     * Container for parsed headers
     *
     * @var array|null
     */
    protected $headers = null;

    /**
     * Get Param
     *
     * Get param by current method name
     *
     * @param  string  $key
     * @param  mixed  $default
     * @return mixed
     */
    public function getParam(string $key, mixed $default = null): mixed
    {
        $params = $this->getParams();

        return (isset($params[$key])) ? $params[$key] : $default;
    }

    /**
     * Get Params
     *
     * Get all params of current method
     *
     * @return array
     */
    public function getParams(): array
    {
        return $this->generateInput();
    }

    /**
     * Get Query
     *
     * Method for querying HTTP GET request parameters. If $key is not found $default value will be returned.
     *
     * @param  string  $key
     * @param  mixed  $default
     * @return mixed
     */
    public function getQuery(string $key, mixed $default = null): mixed
    {
        $this->generateInput();

        return $this->queryString[$key] ?? $default;
    }

    /**
     * Get payload
     *
     * Method for querying HTTP request payload parameters. If $key is not found $default value will be returned.
     *
     * @param  string  $key
     * @param  mixed  $default
     * @return mixed
     */
    public function getPayload(string $key, mixed $default = null): mixed
    {
        $this->generateInput();

        return $this->payload[$key] ?? $default;
    }

    /**
     * Get raw payload
     *
     * Method for getting the HTTP request payload as a raw string.
     *
     * @return string
     */
    public function getRawPayload(): string
    {
        $this->generateInput();

        return $this->rawPayload;
    }

    /**
     * Get server
     *
     * Method for querying server parameters. If $key is not found $default value will be returned.
     *
     * @param  string  $key
     * @param  string|null  $default
     * @return string|null
     */
    public function getServer(string $key, string $default = null): ?string
    {
        return $_SERVER[$key] ?? $default;
    }

    /**
     * Set server
     *
     * Method for setting server parameters.
     *
     * @param  string  $key
     * @param  string  $value
     * @return static
     */
    public function setServer(string $key, string $value): static
    {
        $_SERVER[$key] = $value;

        return $this;
    }

    /**
     * Get IP
     *
     * Returns users IP address.
     * Support HTTP_X_FORWARDED_FOR header usually return
     *  from different proxy servers or PHP default REMOTE_ADDR
     *
     * @return string
     */
    public function getIP(): string
    {
        $ips = explode(',', $this->getHeader('HTTP_X_FORWARDED_FOR', $this->getServer('REMOTE_ADDR') ?? '0.0.0.0'));

        return trim($ips[0] ?? '');
    }

    /**
     * Get Protocol
     *
     * Returns request protocol.
     * Support HTTP_X_FORWARDED_PROTO header usually return
     *  from different proxy servers or PHP default REQUEST_SCHEME
     *
     * @return string
     */
    public function getProtocol(): string
    {
        return $this->getServer('HTTP_X_FORWARDED_PROTO', $this->getServer('REQUEST_SCHEME')) ?? 'https';
    }

    /**
     * Get Port
     *
     * Returns request port.
     *
     * @return string
     */
    public function getPort(): string
    {
        return (string) \parse_url($this->getProtocol().'://'.$this->getServer('HTTP_HOST', ''), PHP_URL_PORT);
    }

    /**
     * Get Hostname
     *
     * Returns request hostname.
     *
     * @return string
     */
    public function getHostname(): string
    {
        return (string) \parse_url($this->getProtocol().'://'.$this->getServer('HTTP_HOST', ''), PHP_URL_HOST);
    }

    /**
     * Get Method
     *
     * Return HTTP request method
     *
     * @return string
     */
    public function getMethod(): string
    {
        return $this->getServer('REQUEST_METHOD') ?? 'UNKNOWN';
    }

    /**
     * Set Method
     *
     * Set HTTP request method
     *
     * @param  string  $method
     * @return static
     */
    public function setMethod(string $method): static
    {
        $this->setServer('REQUEST_METHOD', $method);

        return $this;
    }

    /**
     * Get URI
     *
     * Return HTTP request URI
     *
     * @return string
     */
    public function getURI(): string
    {
        return $this->getServer('REQUEST_URI') ?? '';
    }

    /**
     * Get Path
     *
     * Return HTTP request path
     *
     * @param  string  $uri
     * @return static
     */
    public function setURI(string $uri): static
    {
        $this->setServer('REQUEST_URI', $uri);

        return $this;
    }

    /**
     * Get files
     *
     * Method for querying upload files data. If $key is not found empty array will be returned.
     *
     * @param  string  $key
     * @return array
     */
    public function getFiles(string $key): array
    {
        return (isset($_FILES[$key])) ? $_FILES[$key] : [];
    }

    /**
     * Get Referer
     *
     * Return HTTP referer header
     *
     * @param  string  $default
     * @return string
     */
    public function getReferer(string $default = ''): string
    {
        return (string) $this->getServer('HTTP_REFERER', $default);
    }

    /**
     * Get Origin
     *
     * Return HTTP origin header
     *
     * @param  string  $default
     * @return string
     */
    public function getOrigin(string $default = ''): string
    {
        return (string) $this->getServer('HTTP_ORIGIN', $default);
    }

    /**
     * Get User Agent
     *
     * Return HTTP user agent header
     *
     * @param  string  $default
     * @return string
     */
    public function getUserAgent(string $default = ''): string
    {
        return (string) $this->getServer('HTTP_USER_AGENT', $default);
    }

    /**
     * Get Accept
     *
     * Return HTTP accept header
     *
     * @param  string  $default
     * @return string
     */
    public function getAccept(string $default = ''): string
    {
        return (string) $this->getServer('HTTP_ACCEPT', $default);
    }

    /**
     * Get cookie
     *
     * Method for querying HTTP cookie parameters. If $key is not found $default value will be returned.
     *
     * @param  string  $key
     * @param  string  $default
     * @return string
     */
    public function getCookie(string $key, string $default = ''): string
    {
        return (isset($_COOKIE[$key])) ? $_COOKIE[$key] : $default;
    }

    /**
     * Get header
     *
     * Method for querying HTTP header parameters. If $key is not found $default value will be returned.
     *
     * @param  string  $key
     * @param  string  $default
     * @return string
     */
    public function getHeader(string $key, string $default = ''): string
    {
        $headers = $this->generateHeaders();

        return (isset($headers[$key])) ? $headers[$key] : $default;
    }

    /**
     * Get headers
     *
     * Method for getting all HTTP header parameters.
     *
     * @return array<string,mixed>
     */
    public function getHeaders(): array
    {
        return $this->generateHeaders();
    }

    /**
     * Set header
     *
     * Method for adding HTTP header parameters.
     *
     * @param  string  $key
     * @param  string  $value
     * @return static
     */
    public function addHeader(string $key, string $value): static
    {
        $this->headers[$key] = $value;

        return $this;
    }

    /**
     * Remvoe header
     *
     * Method for removing HTTP header parameters.
     *
     * @param  string  $key
     * @return static
     */
    public function removeHeader(string $key): static
    {
        if (isset($this->headers[$key])) {
            unset($this->headers[$key]);
        }

        return $this;
    }

    /**
     * Get Request Size
     *
     * Returns request size in bytes
     *
     * @return int
     */
    public function getSize(): int
    {
        return \mb_strlen(\implode("\n", $this->generateHeaders()), '8bit') + \mb_strlen(\file_get_contents('php://input'), '8bit');
    }

    /**
     * Get Content Range Start
     *
     * Returns the start of content range
     *
     * @return int|null
     */
    public function getContentRangeStart(): ?int
    {
        $data = $this->parseContentRange();
        if (!empty($data)) {
            return $data['start'];
        } else {
            return null;
        }
    }

    /**
     * Get Content Range End
     *
     * Returns the end of content range
     *
     * @return int|null
     */
    public function getContentRangeEnd(): ?int
    {
        $data = $this->parseContentRange();
        if (!empty($data)) {
            return $data['end'];
        } else {
            return null;
        }
    }

    /**
     * Get Content Range Size
     *
     * Returns the size of content range
     *
     * @return int|null
     */
    public function getContentRangeSize(): ?int
    {
        $data = $this->parseContentRange();
        if (!empty($data)) {
            return $data['size'];
        } else {
            return null;
        }
    }

    /**
     * Get Content Range Unit
     *
     * Returns the unit of content range
     *
     * @return string|null
     */
    public function getContentRangeUnit(): ?string
    {
        $data = $this->parseContentRange();
        if (!empty($data)) {
            return $data['unit'];
        } else {
            return null;
        }
    }

    /**
     * Get Range Start
     *
     * Returns the start of range header
     *
     * @return int|null
     */
    public function getRangeStart(): ?int
    {
        $data = $this->parseRange();
        if (!empty($data)) {
            return $data['start'];
        }

        return null;
    }

    /**
     * Get Range End
     *
     * Returns the end of range header
     *
     * @return int|null
     */
    public function getRangeEnd(): ?int
    {
        $data = $this->parseRange();
        if (!empty($data)) {
            return $data['end'];
        }

        return null;
    }

    /**
     * Get Range Unit
     *
     * Returns the unit of range header
     *
     * @return string|null
     */
    public function getRangeUnit(): ?string
    {
        $data = $this->parseRange();
        if (!empty($data)) {
            return $data['unit'];
        }

        return null;
    }

    /**
     * Set query string parameters
     *
     * @param  array  $params
     * @return static
     */
    public function setQueryString(array $params): static
    {
        $this->queryString = $params;

        return $this;
    }

    /**
     * Set payload parameters
     *
     * @param  array  $params
     * @return static
     */
    public function setPayload(array $params): static
    {
        $this->payload = $params;

        return $this;
    }

    /**
     * Generate input
     *
     * Generate PHP input stream and parse it as an array in order to handle different content type of requests
     *
     * @return array
     */
    protected function generateInput(): array
    {
        if (null === $this->queryString) {
            $this->queryString = $_GET;
        }
        if (null === $this->payload) {
            $contentType = $this->getHeader('content-type');

            // Get content-type without the charset
            $length = \strpos($contentType, ';');
            $length = (empty($length)) ? \strlen($contentType) : $length;
            $contentType = \substr($contentType, 0, $length);

            $this->rawPayload = \file_get_contents('php://input');

            switch ($contentType) {
                case 'application/json':
                    $this->payload = \json_decode($this->rawPayload, true);
                    break;
                default:
                    $this->payload = $_POST;
                    break;
            }

            if (empty($this->payload)) { // Make sure we return same data type even if json payload is empty or failed
                $this->payload = [];
            }
        }

        return match ($this->getServer('REQUEST_METHOD', '')) {
            self::METHOD_POST,
            self::METHOD_PUT,
            self::METHOD_PATCH,
            self::METHOD_DELETE => $this->payload,
            default => $this->queryString
        };
    }

    /**
     * Generate headers
     *
     * Parse request headers as an array for easy querying using the getHeader method
     *
     * @return array
     */
    protected function generateHeaders(): array
    {
        if (null === $this->headers) {
            /**
             * Fallback for older PHP versions
             * that do not support generateHeaders
             */
            if (!\function_exists('getallheaders')) {
                $headers = [];

                foreach ($_SERVER as $name => $value) {
                    if (\substr($name, 0, 5) == 'HTTP_') {
                        $headers[\str_replace(' ', '-', \strtolower(\str_replace('_', ' ', \substr($name, 5))))] = $value;
                    }
                }

                $this->headers = $headers;

                return $this->headers;
            }

            $this->headers = array_change_key_case(getallheaders());
        }

        return $this->headers;
    }

    /**
     * Content Range Parser
     *
     * Parse content-range request header for easy access
     *
     * @return array|null
     */
    protected function parseContentRange(): ?array
    {
        $contentRange = $this->getHeader('content-range', '');
        $data = [];
        if (!empty($contentRange)) {
            $contentRange = explode(' ', $contentRange);
            if (count($contentRange) !== 2) {
                return null;
            }

            $data['unit'] = trim($contentRange[0]);

            if (empty($data['unit'])) {
                return null;
            }

            $rangeData = explode('/', $contentRange[1]);
            if (count($rangeData) !== 2) {
                return null;
            }

            if (!ctype_digit($rangeData[1])) {
                return null;
            }

            $data['size'] = (int) $rangeData[1];
            $parts = explode('-', $rangeData[0]);
            if (count($parts) != 2) {
                return null;
            }

            if (!ctype_digit($parts[0]) || !ctype_digit($parts[1])) {
                return null;
            }

            $data['start'] = (int) $parts[0];
            $data['end'] = (int) $parts[1];
            if ($data['start'] > $data['end'] || $data['end'] > $data['size']) {
                return null;
            }

            return $data;
        }

        return null;
    }

    /**
     * Range Parser
     *
     * Parse range request header for easy access
     *
     * @return array|null
     */
    protected function parseRange(): ?array
    {
        $rangeHeader = $this->getHeader('range', '');
        if (empty($rangeHeader)) {
            return null;
        }

        $data = [];
        $ranges = explode('=', $rangeHeader);
        if (count($ranges) !== 2 || empty($ranges[0]) || empty($ranges[1])) {
            return null;
        }
        $data['unit'] = $ranges[0];

        $ranges = explode('-', $ranges[1]);
        if (count($ranges) !== 2 || strlen($ranges[0]) === 0) {
            return null;
        }

        if (!ctype_digit($ranges[0])) {
            return null;
        }

        $data['start'] = (int) $ranges[0];

        if (strlen($ranges[1]) === 0) {
            $data['end'] = null;
        } else {
            if (!ctype_digit($ranges[1])) {
                return null;
            }
            $data['end'] = (int) $ranges[1];
        }

        if ($data['end'] !== null && $data['start'] >= $data['end']) {
            return null;
        }

        return $data;
    }
}