Вход Регистрация
Файл: symfony-2.7/src/Symfony/Component/PropertyAccess/PropertyPathBuilder.php
Строк: 436
<?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 SymfonyComponentPropertyAccess;

use 
SymfonyComponentPropertyAccessExceptionOutOfBoundsException;

/**
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class PropertyPathBuilder
{
    
/**
     * @var array
     */
    
private $elements = array();

    
/**
     * @var array
     */
    
private $isIndex = array();

    
/**
     * Creates a new property path builder.
     *
     * @param null|PropertyPathInterface|string $path The path to initially store
     *                                                in the builder. Optional.
     */
    
public function __construct($path = null)
    {
        if (
null !== $path) {
            
$this->append($path);
        }
    }

    
/**
     * Appends a (sub-) path to the current path.
     *
     * @param PropertyPathInterface|string $path   The path to append.
     * @param int                          $offset The offset where the appended
     *                                             piece starts in $path.
     * @param int                          $length The length of the appended piece.
     *                                             If 0, the full path is appended.
     */
    
public function append($path, $offset = 0, $length = 0)
    {
        if (
is_string($path)) {
            
$path = new PropertyPath($path);
        }

        if (
0 === $length) {
            
$end = $path->getLength();
        } else {
            
$end = $offset + $length;
        }

        for (; 
$offset < $end; ++$offset) {
            
$this->elements[] = $path->getElement($offset);
            
$this->isIndex[] = $path->isIndex($offset);
        }
    }

    
/**
     * Appends an index element to the current path.
     *
     * @param string $name The name of the appended index
     */
    
public function appendIndex($name)
    {
        
$this->elements[] = $name;
        
$this->isIndex[] = true;
    }

    
/**
     * Appends a property element to the current path.
     *
     * @param string $name The name of the appended property
     */
    
public function appendProperty($name)
    {
        
$this->elements[] = $name;
        
$this->isIndex[] = false;
    }

    
/**
     * Removes elements from the current path.
     *
     * @param int $offset The offset at which to remove
     * @param int $length The length of the removed piece
     *
     * @throws OutOfBoundsException if offset is invalid
     */
    
public function remove($offset, $length = 1)
    {
        if (!isset(
$this->elements[$offset])) {
            throw new 
OutOfBoundsException(sprintf('The offset %s is not within the property path', $offset));
        }

        
$this->resize($offset, $length, 0);
    }

    
/**
     * Replaces a sub-path by a different (sub-) path.
     *
     * @param int                          $offset     The offset at which to replace.
     * @param int                          $length     The length of the piece to replace.
     * @param PropertyPathInterface|string $path       The path to insert.
     * @param int                          $pathOffset The offset where the inserted piece
     *                                                 starts in $path.
     * @param int                          $pathLength The length of the inserted piece.
     *                                                 If 0, the full path is inserted.
     *
     * @throws OutOfBoundsException If the offset is invalid
     */
    
public function replace($offset, $length, $path, $pathOffset = 0, $pathLength = 0)
    {
        if (
is_string($path)) {
            
$path = new PropertyPath($path);
        }

        if (
$offset < 0 && abs($offset) <= $this->getLength()) {
            
$offset = $this->getLength() + $offset;
        } elseif (!isset(
$this->elements[$offset])) {
            throw new 
OutOfBoundsException('The offset '.$offset.' is not within the property path');
        }

        if (
0 === $pathLength) {
            
$pathLength = $path->getLength() - $pathOffset;
        }

        
$this->resize($offset, $length, $pathLength);

        for (
$i = 0; $i < $pathLength; ++$i) {
            
$this->elements[$offset + $i] = $path->getElement($pathOffset + $i);
            
$this->isIndex[$offset + $i] = $path->isIndex($pathOffset + $i);
        }
    }

    
/**
     * Replaces a property element by an index element.
     *
     * @param int    $offset The offset at which to replace
     * @param string $name   The new name of the element. Optional.
     *
     * @throws OutOfBoundsException If the offset is invalid
     */
    
public function replaceByIndex($offset, $name = null)
    {
        if (!isset(
$this->elements[$offset])) {
            throw new 
OutOfBoundsException(sprintf('The offset %s is not within the property path', $offset));
        }

        if (
null !== $name) {
            
$this->elements[$offset] = $name;
        }

        
$this->isIndex[$offset] = true;
    }

    
/**
     * Replaces an index element by a property element.
     *
     * @param int    $offset The offset at which to replace
     * @param string $name   The new name of the element. Optional.
     *
     * @throws OutOfBoundsException If the offset is invalid
     */
    
public function replaceByProperty($offset, $name = null)
    {
        if (!isset(
$this->elements[$offset])) {
            throw new 
OutOfBoundsException(sprintf('The offset %s is not within the property path', $offset));
        }

        if (
null !== $name) {
            
$this->elements[$offset] = $name;
        }

        
$this->isIndex[$offset] = false;
    }

    
/**
     * Returns the length of the current path.
     *
     * @return int The path length
     */
    
public function getLength()
    {
        return 
count($this->elements);
    }

    
/**
     * Returns the current property path.
     *
     * @return PropertyPathInterface The constructed property path
     */
    
public function getPropertyPath()
    {
        
$pathAsString = $this->__toString();

        return 
'' !== $pathAsString ? new PropertyPath($pathAsString) : null;
    }

    
/**
     * Returns the current property path as string.
     *
     * @return string The property path as string
     */
    
public function __toString()
    {
        
$string = '';

        foreach (
$this->elements as $offset => $element) {
            if (
$this->isIndex[$offset]) {
                
$element = '['.$element.']';
            } elseif (
'' !== $string) {
                
$string .= '.';
            }

            
$string .= $element;
        }

        return 
$string;
    }

    
/**
     * Resizes the path so that a chunk of length $cutLength is
     * removed at $offset and another chunk of length $insertionLength
     * can be inserted.
     *
     * @param int $offset          The offset where the removed chunk starts
     * @param int $cutLength       The length of the removed chunk
     * @param int $insertionLength The length of the inserted chunk
     */
    
private function resize($offset, $cutLength, $insertionLength)
    {
        
// Nothing else to do in this case
        
if ($insertionLength === $cutLength) {
            return;
        }

        
$length = count($this->elements);

        if (
$cutLength > $insertionLength) {
            
// More elements should be removed than inserted
            
$diff = $cutLength - $insertionLength;
            
$newLength = $length - $diff;

            
// Shift elements to the left (left-to-right until the new end)
            // Max allowed offset to be shifted is such that
            // $offset + $diff < $length (otherwise invalid index access)
            // i.e. $offset < $length - $diff = $newLength
            
for ($i = $offset; $i < $newLength; ++$i) {
                
$this->elements[$i] = $this->elements[$i + $diff];
                
$this->isIndex[$i] = $this->isIndex[$i + $diff];
            }

            
// All remaining elements should be removed
            
for (; $i < $length; ++$i) {
                unset(
$this->elements[$i]);
                unset(
$this->isIndex[$i]);
            }
        } else {
            
$diff = $insertionLength - $cutLength;

            
$newLength = $length + $diff;
            
$indexAfterInsertion = $offset + $insertionLength;

            
// $diff <= $insertionLength
            // $indexAfterInsertion >= $insertionLength
            // => $diff <= $indexAfterInsertion

            // In each of the following loops, $i >= $diff must hold,
            // otherwise ($i - $diff) becomes negative.

            // Shift old elements to the right to make up space for the
            // inserted elements. This needs to be done left-to-right in
            // order to preserve an ascending array index order
            // Since $i = max($length, $indexAfterInsertion) and $indexAfterInsertion >= $diff,
            // $i >= $diff is guaranteed.
            
for ($i = max($length, $indexAfterInsertion); $i < $newLength; ++$i) {
                
$this->elements[$i] = $this->elements[$i - $diff];
                
$this->isIndex[$i] = $this->isIndex[$i - $diff];
            }

            
// Shift remaining elements to the right. Do this right-to-left
            // so we don't overwrite elements before copying them
            // The last written index is the immediate index after the inserted
            // string, because the indices before that will be overwritten
            // anyway.
            // Since $i >= $indexAfterInsertion and $indexAfterInsertion >= $diff,
            // $i >= $diff is guaranteed.
            
for ($i = $length - 1; $i >= $indexAfterInsertion; --$i) {
                
$this->elements[$i] = $this->elements[$i - $diff];
                
$this->isIndex[$i] = $this->isIndex[$i - $diff];
            }
        }
    }
}
Онлайн: 0
Реклама