tapestry-cloud/tapestry

View on GitHub
src/Entities/Url.php

Summary

Maintainability
B
6 hrs
Test Coverage
A
98%
<?php

namespace Tapestry\Entities;

class Url
{
    /**
     * @var Configuration
     */
    private $configuration;

    /**
     * @var string
     */
    private $siteUrl;

    /**
     * Site constructor.
     *
     * @param Configuration $configuration
     */
    public function __construct(Configuration $configuration)
    {
        $this->configuration = $configuration;
    }

    public function parse($uri = '')
    {
        $this->loadSiteUrl();
        $uri = $this->cleanUri($uri);

        return $this->siteUrl.'/'.$this->encode($uri);
    }

    private function loadSiteUrl()
    {
        if (! is_null($this->siteUrl)) {
            return $this->siteUrl;
        }
        if (! $this->siteUrl = $this->configuration->get('site.url')) {
            throw new \Exception('The site url is not set in your site configuration.');
        }

        $this->siteUrl = $this->cleanUri($this->siteUrl);

        return $this->siteUrl;
    }

    private function cleanUri($text)
    {
        $text = trim($text);

        if (substr($text, 0, 1) === '/') {
            $text = substr($text, 1);
        }

        return ($text === false) ? '' : $text;
    }

    /**
     * @param string $uri
     * @return array
     */
    private function encode($uri = '')
    {
        $parts = explode('/', $uri);
        foreach ($parts as &$part) {
            if (strpos($part, 'index') !== false) {
                $part = null;
                continue;
            }
            $part = $this->encodePart($part);
        }
        unset($part);

        $parts = array_filter($parts, function ($value) {
            return ! is_null($value);
        });

        // If the last uri part doesn't contain a file name e.g. index.html or readme.txt

        $e = end($parts);
        if (
            strlen($e) > 0 &&
            strpos($e, '?') === false &&
            strpos($e, '=') === false &&
            strpos($e, '&') === false &&
            strpos($e, '#') === false &&
            strpos($e, '.') === false
        ) {
            array_push($parts, '');
        }

        return trim(implode('/', $parts));
    }

    /**
     * Ensures that uri parts containing ? or # get encoded correctly.
     *
     * @param string $part
     * @return string
     */
    private function encodePart($part)
    {
        if (strpos($part, '?') !== false) {
            if (strpos($part, '?') === 0) {
                $output = '?';
                $part = substr($part, 1);
            } else {
                $pe = explode('?', $part);
                $output = $pe[0].'/?';
                $part = $pe[1];
                unset($pe);
            }

            if (strpos($part, '&')) {
                $parts = explode('&', $part);
            } else {
                $parts = [$part];
            }
            foreach ($parts as &$p) {
                if (strpos($p, '=') !== false) {
                    $pe = explode('=', $p);
                    $p = $pe[0].'='.urlencode($pe[1]);
                }
            }
            unset($p, $pe);

            $output .= implode('&', $parts);

            return $output;
        }

        if (strpos($part, '#') !== false) {
            if (strpos($part, '#') === 0) {
                if (substr($part, 1, 1) === '!') {
                    return '#!'.rawurlencode(substr($part, 2));
                }

                return '#'.rawurlencode(substr($part, 1));
            }

            $pe = explode('#', $part);
            if (substr($pe[1], 0, 1) === '!') {
                return $pe[0].'/#!'.rawurlencode(substr($pe[1], 1));
            }

            return $pe[0].'/#'.rawurlencode($pe[1]);
        }

        return rawurlencode($part);
    }
}