2013-01-07 08:19:31 +00:00
< ? 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\PropertyAccess ;
2015-01-08 17:24:48 +00:00
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException ;
2013-01-07 08:19:31 +00:00
use Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException ;
use Symfony\Component\PropertyAccess\Exception\OutOfBoundsException ;
/**
* Default implementation of { @ link PropertyPathInterface } .
*
* @ author Bernhard Schussek < bschussek @ gmail . com >
*/
class PropertyPath implements \IteratorAggregate , PropertyPathInterface
{
/**
* Character used for separating between plural and singular of an element .
*/
const SINGULAR_SEPARATOR = '|' ;
/**
2014-12-21 17:00:50 +00:00
* The elements of the property path .
*
2013-01-07 08:19:31 +00:00
* @ var array
*/
2019-01-16 09:39:14 +00:00
private $elements = [];
2013-01-07 08:19:31 +00:00
/**
2014-12-21 17:00:50 +00:00
* The number of elements in the property path .
*
2014-04-16 11:30:19 +01:00
* @ var int
2013-01-07 08:19:31 +00:00
*/
private $length ;
/**
* Contains a Boolean for each property in $elements denoting whether this
* element is an index . It is a property otherwise .
2014-12-21 17:00:50 +00:00
*
2013-01-07 08:19:31 +00:00
* @ var array
*/
2019-01-16 09:39:14 +00:00
private $isIndex = [];
2013-01-07 08:19:31 +00:00
/**
2014-12-21 17:00:50 +00:00
* String representation of the path .
*
2013-01-07 08:19:31 +00:00
* @ var string
*/
private $pathAsString ;
/**
* Constructs a property path from a string .
*
2013-03-27 22:40:36 +00:00
* @ param PropertyPath | string $propertyPath The property path as string or instance
2013-01-07 08:19:31 +00:00
*
2015-01-08 17:24:48 +00:00
* @ throws InvalidArgumentException If the given path is not a string
2013-03-27 22:40:36 +00:00
* @ throws InvalidPropertyPathException If the syntax of the property path is not valid
2013-01-07 08:19:31 +00:00
*/
public function __construct ( $propertyPath )
{
// Can be used as copy constructor
2015-03-31 00:38:19 +01:00
if ( $propertyPath instanceof self ) {
2013-01-07 08:19:31 +00:00
/* @var PropertyPath $propertyPath */
$this -> elements = $propertyPath -> elements ;
$this -> length = $propertyPath -> length ;
$this -> isIndex = $propertyPath -> isIndex ;
$this -> pathAsString = $propertyPath -> pathAsString ;
return ;
}
2018-07-05 12:24:53 +01:00
if ( ! \is_string ( $propertyPath )) {
2018-09-08 13:44:02 +01:00
throw new InvalidArgumentException ( sprintf ( 'The property path constructor needs a string or an instance of "Symfony\Component\PropertyAccess\PropertyPath". Got: "%s"' , \is_object ( $propertyPath ) ? \get_class ( $propertyPath ) : \gettype ( $propertyPath )));
2013-01-07 08:19:31 +00:00
}
if ( '' === $propertyPath ) {
throw new InvalidPropertyPathException ( 'The property path should not be empty.' );
}
$this -> pathAsString = $propertyPath ;
$position = 0 ;
$remaining = $propertyPath ;
// first element is evaluated differently - no leading dot for properties
2016-03-17 14:00:21 +00:00
$pattern = '/^(([^\.\[]++)|\[([^\]]++)\])(.*)/' ;
2013-01-07 08:19:31 +00:00
while ( preg_match ( $pattern , $remaining , $matches )) {
if ( '' !== $matches [ 2 ]) {
$element = $matches [ 2 ];
$this -> isIndex [] = false ;
} else {
$element = $matches [ 3 ];
$this -> isIndex [] = true ;
}
2015-01-21 19:48:04 +00:00
2013-01-07 08:19:31 +00:00
$this -> elements [] = $element ;
2018-07-05 12:24:53 +01:00
$position += \strlen ( $matches [ 1 ]);
2013-01-07 08:19:31 +00:00
$remaining = $matches [ 4 ];
2016-03-22 08:55:46 +00:00
$pattern = '/^(\.([^\.|\[]++)|\[([^\]]++)\])(.*)/' ;
2013-01-07 08:19:31 +00:00
}
if ( '' !== $remaining ) {
2018-09-08 13:44:02 +01:00
throw new InvalidPropertyPathException ( sprintf ( 'Could not parse property path "%s". Unexpected token "%s" at position %d' , $propertyPath , $remaining [ 0 ], $position ));
2013-01-07 08:19:31 +00:00
}
2018-07-05 12:24:53 +01:00
$this -> length = \count ( $this -> elements );
2013-01-07 08:19:31 +00:00
}
/**
* { @ inheritdoc }
*/
public function __toString ()
{
return $this -> pathAsString ;
}
/**
* { @ inheritdoc }
*/
public function getLength ()
{
return $this -> length ;
}
/**
* { @ inheritdoc }
*/
public function getParent ()
{
if ( $this -> length <= 1 ) {
2014-04-16 08:15:58 +01:00
return ;
2013-01-07 08:19:31 +00:00
}
$parent = clone $this ;
-- $parent -> length ;
$parent -> pathAsString = substr ( $parent -> pathAsString , 0 , max ( strrpos ( $parent -> pathAsString , '.' ), strrpos ( $parent -> pathAsString , '[' )));
array_pop ( $parent -> elements );
array_pop ( $parent -> isIndex );
return $parent ;
}
/**
2014-12-21 17:00:50 +00:00
* Returns a new iterator for this path .
2013-01-07 08:19:31 +00:00
*
* @ return PropertyPathIteratorInterface
*/
public function getIterator ()
{
return new PropertyPathIterator ( $this );
}
/**
* { @ inheritdoc }
*/
public function getElements ()
{
return $this -> elements ;
}
/**
* { @ inheritdoc }
*/
public function getElement ( $index )
{
if ( ! isset ( $this -> elements [ $index ])) {
2013-04-10 11:24:37 +01:00
throw new OutOfBoundsException ( sprintf ( 'The index %s is not within the property path' , $index ));
2013-01-07 08:19:31 +00:00
}
return $this -> elements [ $index ];
}
/**
* { @ inheritdoc }
*/
public function isProperty ( $index )
{
if ( ! isset ( $this -> isIndex [ $index ])) {
2013-04-10 11:24:37 +01:00
throw new OutOfBoundsException ( sprintf ( 'The index %s is not within the property path' , $index ));
2013-01-07 08:19:31 +00:00
}
return ! $this -> isIndex [ $index ];
}
/**
* { @ inheritdoc }
*/
public function isIndex ( $index )
{
if ( ! isset ( $this -> isIndex [ $index ])) {
2013-04-10 11:24:37 +01:00
throw new OutOfBoundsException ( sprintf ( 'The index %s is not within the property path' , $index ));
2013-01-07 08:19:31 +00:00
}
return $this -> isIndex [ $index ];
}
}