dadajuice/zephyrus

View on GitHub
src/Zephyrus/Network/Request.php

Summary

Maintainability
A
3 hrs
Test Coverage
A
100%
<?php namespace Zephyrus\Network;

use Zephyrus\Application\Configuration;
use Zephyrus\Exceptions\RouteMethodUnsupportedException;
use Zephyrus\Exceptions\Security\IntrusionDetectionException;
use Zephyrus\Exceptions\Security\InvalidCsrfException;
use Zephyrus\Exceptions\Security\MissingCsrfException;
use Zephyrus\Exceptions\Security\UnauthorizedAccessException;
use Zephyrus\Network\Request\CookieJar;
use Zephyrus\Network\Request\QueryString;
use Zephyrus\Network\Request\RequestAccept;
use Zephyrus\Network\Request\RequestBody;
use Zephyrus\Network\Request\RequestHistory;
use Zephyrus\Network\Router\RouteDefinition;
use Zephyrus\Security\AuthorizationGuard;
use Zephyrus\Security\CsrfGuard;
use Zephyrus\Security\IntrusionDetection;

class Request
{
    private ServerEnvironnement $environnement;
    private Url $url;
    private RequestBody $body;
    private RequestAccept $accept;
    private RequestHistory $history;
    private QueryString $queryString;
    private ?RouteDefinition $routeDefinition = null; // Exists only when the route is found in the repository ...
    private CookieJar $cookieJar;
    private IntrusionDetection $intrusionDetection;
    private CsrfGuard $csrfGuard;
    private AuthorizationGuard $authorizationGuard;

    public function __construct(ServerEnvironnement $environnement)
    {
        $this->environnement = $environnement;
        $this->url = new Url($environnement->getRequestedUrl());
        $this->queryString = $this->url->buildQueryString();
        $this->body = new RequestBody($environnement->getRawData(), $environnement->getContentType());
        $this->accept = new RequestAccept($environnement->getAccept());
        $this->cookieJar = new CookieJar($environnement->getCookies());
        $this->intrusionDetection = new IntrusionDetection($this, Configuration::getSecurity('ids'));
        $this->csrfGuard = new CsrfGuard($this, Configuration::getSecurity('csrf'));
        $this->authorizationGuard = new AuthorizationGuard($this);
        $this->history = new RequestHistory();
    }

    /**
     * Guards the request before processing by verifying if the intrusion detection system detected an activity over the
     * configured threshold or if the CSRF was not submitted. If the IDS is not enabled, it is ignored. Same with the
     * CSRF guard.
     *
     * @throws IntrusionDetectionException
     * @throws InvalidCsrfException
     * @throws MissingCsrfException
     * @throws UnauthorizedAccessException
     */
    public function guard(): void
    {
        if ($this->intrusionDetection->isEnabled()) {
            $this->intrusionDetection->run();
        }
        if ($this->csrfGuard->isEnabled()) {
            $this->csrfGuard->run();
        }
        $this->authorizationGuard->run();
    }

    /**
     * Retrieves the HTTP method of the request (either GET, POST, PUT, PATCH or DELETE).
     *
     * @throws RouteMethodUnsupportedException
     * @return HttpMethod
     */
    public function getMethod(): HttpMethod
    {
        return $this->body->getHttpMethodOverride() ?? $this->environnement->getMethod();
    }

    /**
     * Retrieves the requested route as it should be processed by the router (e.g. /users/4).
     *
     * @return string
     */
    public function getRoute(): string
    {
        return $this->url->getPath();
    }

    /**
     * Retrieves one specific parameter from the body data or query string. If the specified parameter doesn't exist,
     * the method returns the given default value (defaults to null).
     *
     * @param string $name
     * @param mixed $default
     * @return mixed
     */
    public function getParameter(string $name, mixed $default = null): mixed
    {
        return $this->queryString->getArgument($name) ?? $this->body->getParameter($name, $default);
    }

    /**
     * Retrieves the entire request parameters either given in query string or body.
     *
     * @return array
     */
    public function getParameters(): array
    {
        return array_merge($this->queryString->getArguments(), $this->body->getParameters());
    }

    public function getRequestedUrl(): string
    {
        return $this->environnement->getRequestedUrl();
    }

    /**
     * Retrieves the complete request body with parsed parameter based on the content type.
     *
     * @return RequestBody
     */
    public function getBody(): RequestBody
    {
        return $this->body;
    }

    /**
     * Retrieves the accepted representation the client asks with the request.
     *
     * @return RequestAccept
     */
    public function getAccept(): RequestAccept
    {
        return $this->accept;
    }

    public function getCookieJar(): CookieJar
    {
        return $this->cookieJar;
    }

    /**
     * Retrieves the complete Uri instance for the current request. Meaning, the complete details of the url the client
     * requested.
     *
     * @return Url
     */
    public function getUrl(): Url
    {
        return $this->url;
    }

    public function getClientIp(): string
    {
        return $this->environnement->getClientIp();
    }

    public function getUserAgent(): string
    {
        return $this->environnement->getUserAgent();
    }

    /**
     * Returns only the last recorded visited GET route the client did within his active session. Should be considered
     * for returning to the previous visited URL.
     *
     * @return string
     */
    public function getReferer(): string
    {
        return $this->history->getReferer();
    }

    /**
     * Retrieves the entire route definition instance the client requested which contains the url, the arguments and
     * callbacks associated. Can be null if the router was not used.
     *
     * @return RouteDefinition | null
     */
    public function getRouteDefinition(): ?RouteDefinition
    {
        return $this->routeDefinition;
    }

    /**
     * Applies the matching route definition for the client request if any. The route definition contains essentially
     * what is needed for the URL arguments and the callback to execute.
     *
     * @param RouteDefinition $routeDefinition
     */
    public function setRouteDefinition(RouteDefinition $routeDefinition): void
    {
        $this->routeDefinition = $routeDefinition;
    }

    /**
     * Retrieves all the request headers the client sent.
     *
     * @return array
     */
    public function getHeaders(): array
    {
        return $this->environnement->getHeaders();
    }

    /**
     * Retrieves a specific HTTP header from the request (case-insensitive).
     *
     * @param string $name
     * @param string|null $defaultValue
     * @return string|null
     */
    public function getHeader(string $name, ?string $defaultValue = null): ?string
    {
        return $this->environnement->getHeader($name) ?? $defaultValue;
    }

    public function getArgument(string $name, mixed $defaultValue = null): mixed
    {
        return $this->routeDefinition?->getArgument($name, $defaultValue) ?? null;
    }

    public function getArguments(): array
    {
        return $this->routeDefinition?->getArguments() ?? [];
    }

    public function getServerEnvironnement(): ServerEnvironnement
    {
        return $this->environnement;
    }

    public function getIntrusionDetection(): IntrusionDetection
    {
        return $this->intrusionDetection;
    }

    public function getCsrfGuard(): CsrfGuard
    {
        return $this->csrfGuard;
    }

    public function getHistory(): RequestHistory
    {
        return $this->history;
    }

    /**
     * Records the request into the client session history.
     */
    public function addToHistory(): void
    {
        $this->history->add($this);
    }

    public function getFiles(): array
    {
        return $_FILES;
    }
}