Seditio Source
Root |
./othercms/phpBB3/vendor/symfony/routing/Matcher/Dumper/StaticPrefixCollection.php
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Routing\Matcher\Dumper;

/**
 * Prefix tree of routes preserving routes order.
 *
 * @author Frank de Jonge <info@frankdejonge.nl>
 *
 * @internal
 */
class StaticPrefixCollection
{
   
/**
     * @var string
     */
   
private $prefix;

   
/**
     * @var array[]|StaticPrefixCollection[]
     */
   
private $items = [];

   
/**
     * @var int
     */
   
private $matchStart = 0;

    public function
__construct($prefix = '')
    {
       
$this->prefix = $prefix;
    }

    public function
getPrefix()
    {
        return
$this->prefix;
    }

   
/**
     * @return mixed[]|StaticPrefixCollection[]
     */
   
public function getItems()
    {
        return
$this->items;
    }

   
/**
     * Adds a route to a group.
     *
     * @param string $prefix
     * @param mixed  $route
     */
   
public function addRoute($prefix, $route)
    {
       
$prefix = '/' === $prefix ? $prefix : rtrim($prefix, '/');
       
$this->guardAgainstAddingNotAcceptedRoutes($prefix);

        if (
$this->prefix === $prefix) {
           
// When a prefix is exactly the same as the base we move up the match start position.
            // This is needed because otherwise routes that come afterwards have higher precedence
            // than a possible regular expression, which goes against the input order sorting.
           
$this->items[] = [$prefix, $route];
           
$this->matchStart = \count($this->items);

            return;
        }

        foreach (
$this->items as $i => $item) {
            if (
$i < $this->matchStart) {
                continue;
            }

            if (
$item instanceof self && $item->accepts($prefix)) {
               
$item->addRoute($prefix, $route);

                return;
            }

           
$group = $this->groupWithItem($item, $prefix, $route);

            if (
$group instanceof self) {
               
$this->items[$i] = $group;

                return;
            }
        }

       
// No optimised case was found, in this case we simple add the route for possible
        // grouping when new routes are added.
       
$this->items[] = [$prefix, $route];
    }

   
/**
     * Tries to combine a route with another route or group.
     *
     * @param StaticPrefixCollection|array $item
     * @param string                       $prefix
     * @param mixed                        $route
     *
     * @return StaticPrefixCollection|null
     */
   
private function groupWithItem($item, $prefix, $route)
    {
       
$itemPrefix = $item instanceof self ? $item->prefix : $item[0];
       
$commonPrefix = $this->detectCommonPrefix($prefix, $itemPrefix);

        if (!
$commonPrefix) {
            return
null;
        }

       
$child = new self($commonPrefix);

        if (
$item instanceof self) {
           
$child->items = [$item];
        } else {
           
$child->addRoute($item[0], $item[1]);
        }

       
$child->addRoute($prefix, $route);

        return
$child;
    }

   
/**
     * Checks whether a prefix can be contained within the group.
     *
     * @param string $prefix
     *
     * @return bool Whether a prefix could belong in a given group
     */
   
private function accepts($prefix)
    {
        return
'' === $this->prefix || 0 === strpos($prefix, $this->prefix);
    }

   
/**
     * Detects whether there's a common prefix relative to the group prefix and returns it.
     *
     * @param string $prefix
     * @param string $anotherPrefix
     *
     * @return false|string A common prefix, longer than the base/group prefix, or false when none available
     */
   
private function detectCommonPrefix($prefix, $anotherPrefix)
    {
       
$baseLength = \strlen($this->prefix);
       
$commonLength = $baseLength;
       
$end = min(\strlen($prefix), \strlen($anotherPrefix));

        for (
$i = $baseLength; $i <= $end; ++$i) {
            if (
substr($prefix, 0, $i) !== substr($anotherPrefix, 0, $i)) {
                break;
            }

           
$commonLength = $i;
        }

       
$commonPrefix = rtrim(substr($prefix, 0, $commonLength), '/');

        if (\
strlen($commonPrefix) > $baseLength) {
            return
$commonPrefix;
        }

        return
false;
    }

   
/**
     * Optimizes the tree by inlining items from groups with less than 3 items.
     */
   
public function optimizeGroups()
    {
       
$index = -1;

        while (isset(
$this->items[++$index])) {
           
$item = $this->items[$index];

            if (
$item instanceof self) {
               
$item->optimizeGroups();

               
// When a group contains only two items there's no reason to optimize because at minimum
                // the amount of prefix check is 2. In this case inline the group.
               
if ($item->shouldBeInlined()) {
                   
array_splice($this->items, $index, 1, $item->items);

                   
// Lower index to pass through the same index again after optimizing.
                    // The first item of the replacements might be a group needing optimization.
                   
--$index;
                }
            }
        }
    }

    private function
shouldBeInlined()
    {
        if (\
count($this->items) >= 3) {
            return
false;
        }

        foreach (
$this->items as $item) {
            if (
$item instanceof self) {
                return
true;
            }
        }

        foreach (
$this->items as $item) {
            if (\
is_array($item) && $item[0] === $this->prefix) {
                return
false;
            }
        }

        return
true;
    }

   
/**
     * Guards against adding incompatible prefixes in a group.
     *
     * @param string $prefix
     *
     * @throws \LogicException when a prefix does not belong in a group
     */
   
private function guardAgainstAddingNotAcceptedRoutes($prefix)
    {
        if (!
$this->accepts($prefix)) {
           
$message = sprintf('Could not add route with prefix %s to collection with prefix %s', $prefix, $this->prefix);

            throw new \
LogicException($message);
        }
    }
}