phpffcms/ffcms-core

View on GitHub
src/Network/Request.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

namespace Ffcms\Core\Network;

use Ffcms\Core\App;
use Ffcms\Core\Helper\Type\Str;
use Ffcms\Templex\Url\UrlRepository;
use Symfony\Component\HttpFoundation\Request as FoundationRequest;
use Ffcms\Core\Network\Extends\InputBag;
use Ffcms\Core\Network\Extends\InputBag\InputBag as InputBagInputBag;

/**
 * Class Request. Classic implementation of httpfoundation.request with smooth additions and changes which allow
 * working as well as in ffcms.
 * @package Ffcms\Core\Network
 */
class Request extends FoundationRequest
{
    use Request\MvcFeatures, Request\MultiLanguageFeatures, Request\RouteMapFeatures;

    /**
     * Request constructor.
     * @inheritdoc
     * @param array $query
     * @param array $request
     * @param array $attributes
     * @param array $cookies
     * @param array $files
     * @param array $server
     * @param null $content
     */
    public function __construct(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
    {
        parent::__construct($query, $request, $attributes, $cookies, $files, $server, $content);
        $this->searchRedirect();
        $this->runMultiLanguage();
        $this->processBalancerProxies();
        $this->loadTrustedProxies();
        $this->runRouteBinding();
        $this->setTemplexFeatures();
    }

    /**
     * Sets the parameters for this request.
     *
     * This method also re-initializes all properties.
     *
     * @param array $query The GET parameters
     * @param array $request The POST parameters
     * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
     * @param array $cookies The COOKIE parameters
     * @param array $files The FILES parameters
     * @param array $server The SERVER parameters
     * @param string $content The raw body data
     *
     * @api
     */
    public function initialize(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
    {
        parent::initialize([], [], $attributes, [], $files, $server, $content);

        // override InputBag with allowed array-driven access
        $this->request = new InputBag($request);
        $this->query = new InputBag($query);
        $this->cookies = new InputBag($cookies);

        $basePath = trim(App::$Properties->get('basePath'), '/');
        if ($basePath !== null && Str::length($basePath) > 0) {
            $basePath = '/' . $basePath;
        }

        if (!defined('env_no_uri') || env_no_uri === false) {
            $basePath .= '/' . strtolower(env_name);
        }

        // we never try to use path's without friendly url's
        $this->basePath = $this->baseUrl = $basePath;
    }

    private function processBalancerProxies()
    {
        // fix cloudflare forwarding
        $protos = $this->headers->get('cf-visitor', false);
        if ($protos) {
            $data = json_decode($protos);
            if ($data && $data->scheme && (string)$data->scheme === 'https') {
                $this->server->set('HTTPS', 'on');
            }
        }
    }

    /**
     * Set trusted proxies from configs
     */
    private function loadTrustedProxies()
    {
        $proxies = App::$Properties->get('trustedProxy');
        if (!$proxies || Str::likeEmpty($proxies)) {
            return;
        }

        $pList = explode(',', $proxies);
        $resultList = [];
        foreach ($pList as $proxy) {
            $resultList[] = trim($proxy);
        }
        self::setTrustedProxies($resultList, self::HEADER_X_FORWARDED_ALL);
    }

    /**
     * Get pathway as string
     * @return string
     */
    public function getPathInfo(): string
    {
        $route = $this->languageInPath ? Str::sub(parent::getPathInfo(), Str::length($this->language) + 1) : parent::getPathInfo();
        if (!Str::startsWith('/', $route)) {
            $route = '/' . $route;
        }
        return $route;
    }

    /**
     * Get pathway without current controller/action path
     * @return string
     */
    public function getPathWithoutControllerAction(): ?string
    {
        $path = trim($this->getPathInfo(), '/');
        if ($this->aliasPathTarget !== null) {
            $path = trim($this->aliasPathTarget, '/');
        }

        $pathArray = explode('/', $path);
        if ($pathArray[0] === Str::lowerCase($this->getController())) {
            // unset controller
            array_shift($pathArray);
            if ($pathArray[0] === Str::lowerCase($this->getAction())) {
                // unset action
                array_shift($pathArray);
            }
        }
        return trim(implode('/', $pathArray), '/');
    }

    /**
     * Get current full request URI
     * @return string
     */
    public function getFullUrl(): string
    {
        return $this->getSchemeAndHttpHost() . $this->getRequestUri();
    }

    /**
     * Get base path from current environment without basePath of subdirectories
     * @return string
     */
    public function getInterfaceSlug(): string
    {
        $path = $this->getBasePath();
        $subDir = App::$Properties->get('basePath');
        if ($subDir !== '/') {
            $offset = (int)Str::length($subDir);
            $path = Str::sub($path, --$offset);
        }
        return $path;
    }

    /**
     * Set templex template engine URL features
     * @return void
     */
    private function setTemplexFeatures(): void
    {
        $sub = null;
        if ($this->getInterfaceSlug() && Str::length($this->getInterfaceSlug()) > 0) {
            $sub = $this->getInterfaceSlug() . '/';
        }

        if ($this->languageInPath()) {
            $sub .= $this->getLanguage();
        }

        UrlRepository::factory()->setUrlAndSubdir($this->getFullUrl(), $sub);
    }
}