From 4f5b44bd3bd490309eb2ba7b44df4769816ba729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Sat, 3 Aug 2013 19:26:54 +0200 Subject: twig implementation --- .../Component/PropertyAccess/.gitattributes | 2 + .../Symfony/Component/PropertyAccess/.gitignore | 4 + .../Symfony/Component/PropertyAccess/CHANGELOG.md | 14 + .../Exception/ExceptionInterface.php | 21 + .../Exception/InvalidPropertyPathException.php | 21 + .../Exception/NoSuchPropertyException.php | 21 + .../Exception/OutOfBoundsException.php | 21 + .../PropertyAccess/Exception/RuntimeException.php | 21 + .../Exception/UnexpectedTypeException.php | 25 ++ .../Symfony/Component/PropertyAccess/LICENSE | 19 + .../Component/PropertyAccess/PropertyAccess.php | 60 +++ .../Component/PropertyAccess/PropertyAccessor.php | 442 +++++++++++++++++++++ .../PropertyAccess/PropertyAccessorBuilder.php | 67 ++++ .../PropertyAccess/PropertyAccessorInterface.php | 81 ++++ .../Component/PropertyAccess/PropertyPath.php | 225 +++++++++++ .../PropertyAccess/PropertyPathBuilder.php | 306 ++++++++++++++ .../PropertyAccess/PropertyPathInterface.php | 86 ++++ .../PropertyAccess/PropertyPathIterator.php | 55 +++ .../PropertyPathIteratorInterface.php | 34 ++ .../Symfony/Component/PropertyAccess/README.md | 14 + .../Component/PropertyAccess/StringUtil.php | 195 +++++++++ .../Symfony/Component/PropertyAccess/composer.json | 31 ++ 22 files changed, 1765 insertions(+) create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitattributes create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitignore create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/CHANGELOG.md create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/InvalidPropertyPathException.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/NoSuchPropertyException.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/OutOfBoundsException.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/RuntimeException.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/LICENSE create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccess.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessor.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPath.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathBuilder.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathInterface.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIterator.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIteratorInterface.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/README.md create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/StringUtil.php create mode 100644 vendor/symfony/property-access/Symfony/Component/PropertyAccess/composer.json (limited to 'vendor/symfony/property-access') diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitattributes b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitattributes new file mode 100644 index 00000000..80481513 --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitattributes @@ -0,0 +1,2 @@ +/Tests export-ignore +phpunit.xml.dist export-ignore diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitignore b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitignore new file mode 100644 index 00000000..44de97a3 --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/.gitignore @@ -0,0 +1,4 @@ +vendor/ +composer.lock +phpunit.xml + diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/CHANGELOG.md b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/CHANGELOG.md new file mode 100644 index 00000000..071ef3b5 --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/CHANGELOG.md @@ -0,0 +1,14 @@ +CHANGELOG +========= + +2.3.0 +------ + + * added PropertyAccessorBuilder, to enable or disable the support of "__call" + * added support for "__call" in the PropertyAccessor (disabled by default) + * [BC BREAK] changed PropertyAccessor to continue its search for a property or + method even if a non-public match was found. Before, a PropertyAccessDeniedException + was thrown in this case. Class PropertyAccessDeniedException was removed + now. + * deprecated PropertyAccess::getPropertyAccessor + * added PropertyAccess::createPropertyAccessor and PropertyAccess::createPropertyAccessorBuilder diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.php new file mode 100644 index 00000000..d1fcdac9 --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Exception; + +/** + * Marker interface for the PropertyAccess component. + * + * @author Bernhard Schussek + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/InvalidPropertyPathException.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/InvalidPropertyPathException.php new file mode 100644 index 00000000..69de31ce --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/InvalidPropertyPathException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Exception; + +/** + * Thrown when a property path is malformed. + * + * @author Bernhard Schussek + */ +class InvalidPropertyPathException extends RuntimeException +{ +} diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/NoSuchPropertyException.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/NoSuchPropertyException.php new file mode 100644 index 00000000..ebaa5a30 --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/NoSuchPropertyException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Exception; + +/** + * Thrown when a property cannot be found. + * + * @author Bernhard Schussek + */ +class NoSuchPropertyException extends RuntimeException +{ +} diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/OutOfBoundsException.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/OutOfBoundsException.php new file mode 100644 index 00000000..a3c45597 --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/OutOfBoundsException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Exception; + +/** + * Base OutOfBoundsException for the PropertyAccess component. + * + * @author Bernhard Schussek + */ +class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/RuntimeException.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/RuntimeException.php new file mode 100644 index 00000000..9fe843e3 --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/RuntimeException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Exception; + +/** + * Base RuntimeException for the PropertyAccess component. + * + * @author Bernhard Schussek + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php new file mode 100644 index 00000000..029d48c2 --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Exception; + +/** + * Thrown when a value does not match an expected type. + * + * @author Bernhard Schussek + */ +class UnexpectedTypeException extends RuntimeException +{ + public function __construct($value, $expectedType) + { + parent::__construct(sprintf('Expected argument of type "%s", "%s" given', $expectedType, is_object($value) ? get_class($value) : gettype($value))); + } +} diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/LICENSE b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/LICENSE new file mode 100644 index 00000000..88a57f8d --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2013 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccess.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccess.php new file mode 100644 index 00000000..3b234df9 --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccess.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess; + +/** + * Entry point of the PropertyAccess component. + * + * @author Bernhard Schussek + */ +final class PropertyAccess +{ + /** + * Creates a property accessor with the default configuration. + * + * @return PropertyAccessor The new property accessor + */ + public static function createPropertyAccessor() + { + return self::createPropertyAccessorBuilder()->getPropertyAccessor(); + } + + /** + * Creates a property accessor builder. + * + * @return PropertyAccessorBuilder The new property accessor builder + */ + public static function createPropertyAccessorBuilder() + { + return new PropertyAccessorBuilder(); + } + + /** + * Alias of {@link getPropertyAccessor}. + * + * @return PropertyAccessor The new property accessor + * + * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use + * {@link createPropertyAccessor()} instead. + */ + public static function getPropertyAccessor() + { + return self::createPropertyAccessor(); + } + + /** + * This class cannot be instantiated. + */ + private function __construct() + { + } +} diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessor.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessor.php new file mode 100644 index 00000000..c65d919e --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -0,0 +1,442 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess; + +use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; +use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException; + +/** + * Default implementation of {@link PropertyAccessorInterface}. + * + * @author Bernhard Schussek + */ +class PropertyAccessor implements PropertyAccessorInterface +{ + const VALUE = 0; + const IS_REF = 1; + + private $magicCall; + + /** + * Should not be used by application code. Use + * {@link PropertyAccess::getPropertyAccessor()} instead. + */ + public function __construct($magicCall = false) + { + $this->magicCall = $magicCall; + } + + /** + * {@inheritdoc} + */ + public function getValue($objectOrArray, $propertyPath) + { + if (is_string($propertyPath)) { + $propertyPath = new PropertyPath($propertyPath); + } elseif (!$propertyPath instanceof PropertyPathInterface) { + throw new UnexpectedTypeException($propertyPath, 'string or Symfony\Component\PropertyAccess\PropertyPathInterface'); + } + + $propertyValues =& $this->readPropertiesUntil($objectOrArray, $propertyPath, $propertyPath->getLength()); + + return $propertyValues[count($propertyValues) - 1][self::VALUE]; + } + + /** + * {@inheritdoc} + */ + public function setValue(&$objectOrArray, $propertyPath, $value) + { + if (is_string($propertyPath)) { + $propertyPath = new PropertyPath($propertyPath); + } elseif (!$propertyPath instanceof PropertyPathInterface) { + throw new UnexpectedTypeException($propertyPath, 'string or Symfony\Component\PropertyAccess\PropertyPathInterface'); + } + + $propertyValues =& $this->readPropertiesUntil($objectOrArray, $propertyPath, $propertyPath->getLength() - 1); + $overwrite = true; + + // Add the root object to the list + array_unshift($propertyValues, array( + self::VALUE => &$objectOrArray, + self::IS_REF => true, + )); + + for ($i = count($propertyValues) - 1; $i >= 0; --$i) { + $objectOrArray =& $propertyValues[$i][self::VALUE]; + + if ($overwrite) { + if (!is_object($objectOrArray) && !is_array($objectOrArray)) { + throw new UnexpectedTypeException($objectOrArray, 'object or array'); + } + + $property = $propertyPath->getElement($i); + //$singular = $propertyPath->singulars[$i]; + $singular = null; + + if ($propertyPath->isIndex($i)) { + $this->writeIndex($objectOrArray, $property, $value); + } else { + $this->writeProperty($objectOrArray, $property, $singular, $value); + } + } + + $value =& $objectOrArray; + $overwrite = !$propertyValues[$i][self::IS_REF]; + } + } + + /** + * Reads the path from an object up to a given path index. + * + * @param object|array $objectOrArray The object or array to read from + * @param PropertyPathInterface $propertyPath The property path to read + * @param integer $lastIndex The index up to which should be read + * + * @return array The values read in the path. + * + * @throws UnexpectedTypeException If a value within the path is neither object nor array. + */ + private function &readPropertiesUntil(&$objectOrArray, PropertyPathInterface $propertyPath, $lastIndex) + { + $propertyValues = array(); + + for ($i = 0; $i < $lastIndex; ++$i) { + if (!is_object($objectOrArray) && !is_array($objectOrArray)) { + throw new UnexpectedTypeException($objectOrArray, 'object or array'); + } + + $property = $propertyPath->getElement($i); + $isIndex = $propertyPath->isIndex($i); + $isArrayAccess = is_array($objectOrArray) || $objectOrArray instanceof \ArrayAccess; + + // Create missing nested arrays on demand + if ($isIndex && $isArrayAccess && !isset($objectOrArray[$property])) { + $objectOrArray[$property] = $i + 1 < $propertyPath->getLength() ? array() : null; + } + + if ($isIndex) { + $propertyValue =& $this->readIndex($objectOrArray, $property); + } else { + $propertyValue =& $this->readProperty($objectOrArray, $property); + } + + $objectOrArray =& $propertyValue[self::VALUE]; + + $propertyValues[] =& $propertyValue; + } + + return $propertyValues; + } + + /** + * Reads a key from an array-like structure. + * + * @param \ArrayAccess|array $array The array or \ArrayAccess object to read from + * @param string|integer $index The key to read + * + * @return mixed The value of the key + * + * @throws NoSuchPropertyException If the array does not implement \ArrayAccess or it is not an array + */ + private function &readIndex(&$array, $index) + { + if (!$array instanceof \ArrayAccess && !is_array($array)) { + throw new NoSuchPropertyException(sprintf('Index "%s" cannot be read from object of type "%s" because it doesn\'t implement \ArrayAccess', $index, get_class($array))); + } + + // Use an array instead of an object since performance is very crucial here + $result = array( + self::VALUE => null, + self::IS_REF => false + ); + + if (isset($array[$index])) { + if (is_array($array)) { + $result[self::VALUE] =& $array[$index]; + $result[self::IS_REF] = true; + } else { + $result[self::VALUE] = $array[$index]; + // Objects are always passed around by reference + $result[self::IS_REF] = is_object($array[$index]) ? true : false; + } + } + + return $result; + } + + /** + * Reads the a property from an object or array. + * + * @param object $object The object to read from. + * @param string $property The property to read. + * + * @return mixed The value of the read property + * + * @throws NoSuchPropertyException If the property does not exist or is not + * public. + */ + private function &readProperty(&$object, $property) + { + // Use an array instead of an object since performance is + // very crucial here + $result = array( + self::VALUE => null, + self::IS_REF => false + ); + + if (!is_object($object)) { + throw new NoSuchPropertyException(sprintf('Cannot read property "%s" from an array. Maybe you should write the property path as "[%s]" instead?', $property, $property)); + } + + $camelProp = $this->camelize($property); + $reflClass = new \ReflectionClass($object); + $getter = 'get'.$camelProp; + $isser = 'is'.$camelProp; + $hasser = 'has'.$camelProp; + $classHasProperty = $reflClass->hasProperty($property); + + if ($reflClass->hasMethod($getter) && $reflClass->getMethod($getter)->isPublic()) { + $result[self::VALUE] = $object->$getter(); + } elseif ($reflClass->hasMethod($isser) && $reflClass->getMethod($isser)->isPublic()) { + $result[self::VALUE] = $object->$isser(); + } elseif ($reflClass->hasMethod($hasser) && $reflClass->getMethod($hasser)->isPublic()) { + $result[self::VALUE] = $object->$hasser(); + } elseif ($reflClass->hasMethod('__get') && $reflClass->getMethod('__get')->isPublic()) { + $result[self::VALUE] = $object->$property; + } elseif ($classHasProperty && $reflClass->getProperty($property)->isPublic()) { + $result[self::VALUE] =& $object->$property; + $result[self::IS_REF] = true; + } elseif (!$classHasProperty && property_exists($object, $property)) { + // Needed to support \stdClass instances. We need to explicitly + // exclude $classHasProperty, otherwise if in the previous clause + // a *protected* property was found on the class, property_exists() + // returns true, consequently the following line will result in a + // fatal error. + $result[self::VALUE] =& $object->$property; + $result[self::IS_REF] = true; + } elseif ($this->magicCall && $reflClass->hasMethod('__call') && $reflClass->getMethod('__call')->isPublic()) { + // we call the getter and hope the __call do the job + $result[self::VALUE] = $object->$getter(); + } else { + throw new NoSuchPropertyException(sprintf( + 'Neither the property "%s" nor one of the methods "%s()", '. + '"%s()", "%s()", "__get()" or "__call()" exist and have public access in '. + 'class "%s".', + $property, + $getter, + $isser, + $hasser, + $reflClass->name + )); + } + + // Objects are always passed around by reference + if (is_object($result[self::VALUE])) { + $result[self::IS_REF] = true; + } + + return $result; + } + + /** + * Sets the value of the property at the given index in the path + * + * @param \ArrayAccess|array $array An array or \ArrayAccess object to write to + * @param string|integer $index The index to write at + * @param mixed $value The value to write + * + * @throws NoSuchPropertyException If the array does not implement \ArrayAccess or it is not an array + */ + private function writeIndex(&$array, $index, $value) + { + if (!$array instanceof \ArrayAccess && !is_array($array)) { + throw new NoSuchPropertyException(sprintf('Index "%s" cannot be modified in object of type "%s" because it doesn\'t implement \ArrayAccess', $index, get_class($array))); + } + + $array[$index] = $value; + } + + /** + * Sets the value of the property at the given index in the path + * + * @param object|array $object The object or array to write to + * @param string $property The property to write + * @param string|null $singular The singular form of the property name or null + * @param mixed $value The value to write + * + * @throws NoSuchPropertyException If the property does not exist or is not + * public. + */ + private function writeProperty(&$object, $property, $singular, $value) + { + $guessedAdders = ''; + + if (!is_object($object)) { + throw new NoSuchPropertyException(sprintf('Cannot write property "%s" to an array. Maybe you should write the property path as "[%s]" instead?', $property, $property)); + } + + $reflClass = new \ReflectionClass($object); + $plural = $this->camelize($property); + + // Any of the two methods is required, but not yet known + $singulars = null !== $singular ? array($singular) : (array) StringUtil::singularify($plural); + + if (is_array($value) || $value instanceof \Traversable) { + $methods = $this->findAdderAndRemover($reflClass, $singulars); + + if (null !== $methods) { + // At this point the add and remove methods have been found + // Use iterator_to_array() instead of clone in order to prevent side effects + // see https://github.com/symfony/symfony/issues/4670 + $itemsToAdd = is_object($value) ? iterator_to_array($value) : $value; + $itemToRemove = array(); + $propertyValue = $this->readProperty($object, $property); + $previousValue = $propertyValue[self::VALUE]; + + if (is_array($previousValue) || $previousValue instanceof \Traversable) { + foreach ($previousValue as $previousItem) { + foreach ($value as $key => $item) { + if ($item === $previousItem) { + // Item found, don't add + unset($itemsToAdd[$key]); + + // Next $previousItem + continue 2; + } + } + + // Item not found, add to remove list + $itemToRemove[] = $previousItem; + } + } + + foreach ($itemToRemove as $item) { + call_user_func(array($object, $methods[1]), $item); + } + + foreach ($itemsToAdd as $item) { + call_user_func(array($object, $methods[0]), $item); + } + + return; + } else { + // It is sufficient to include only the adders in the error + // message. If the user implements the adder but not the remover, + // an exception will be thrown in findAdderAndRemover() that + // the remover has to be implemented as well. + $guessedAdders = '"add'.implode('()", "add', $singulars).'()", '; + } + } + + $setter = 'set'.$this->camelize($property); + $classHasProperty = $reflClass->hasProperty($property); + + if ($reflClass->hasMethod($setter) && $reflClass->getMethod($setter)->isPublic()) { + $object->$setter($value); + } elseif ($reflClass->hasMethod('__set') && $reflClass->getMethod('__set')->isPublic()) { + $object->$property = $value; + } elseif ($classHasProperty && $reflClass->getProperty($property)->isPublic()) { + $object->$property = $value; + } elseif (!$classHasProperty && property_exists($object, $property)) { + // Needed to support \stdClass instances. We need to explicitly + // exclude $classHasProperty, otherwise if in the previous clause + // a *protected* property was found on the class, property_exists() + // returns true, consequently the following line will result in a + // fatal error. + $object->$property = $value; + } elseif ($this->magicCall && $reflClass->hasMethod('__call') && $reflClass->getMethod('__call')->isPublic()) { + // we call the getter and hope the __call do the job + $object->$setter($value); + } else { + throw new NoSuchPropertyException(sprintf( + 'Neither the property "%s" nor one of the methods %s"%s()", '. + '"__set()" or "__call()" exist and have public access in class "%s".', + $property, + $guessedAdders, + $setter, + $reflClass->name + )); + } + } + + /** + * Camelizes a given string. + * + * @param string $string Some string + * + * @return string The camelized version of the string + */ + private function camelize($string) + { + return preg_replace_callback('/(^|_|\.)+(.)/', function ($match) { return ('.' === $match[1] ? '_' : '').strtoupper($match[2]); }, $string); + } + + /** + * Searches for add and remove methods. + * + * @param \ReflectionClass $reflClass The reflection class for the given object + * @param array $singulars The singular form of the property name or null + * + * @return array|null An array containing the adder and remover when found, null otherwise + * + * @throws NoSuchPropertyException If the property does not exist + */ + private function findAdderAndRemover(\ReflectionClass $reflClass, array $singulars) + { + foreach ($singulars as $singular) { + $addMethod = 'add'.$singular; + $removeMethod = 'remove'.$singular; + + $addMethodFound = $this->isAccessible($reflClass, $addMethod, 1); + $removeMethodFound = $this->isAccessible($reflClass, $removeMethod, 1); + + if ($addMethodFound && $removeMethodFound) { + return array($addMethod, $removeMethod); + } + + if ($addMethodFound xor $removeMethodFound) { + throw new NoSuchPropertyException(sprintf( + 'Found the public method "%s()", but did not find a public "%s()" on class %s', + $addMethodFound ? $addMethod : $removeMethod, + $addMethodFound ? $removeMethod : $addMethod, + $reflClass->name + )); + } + } + + return null; + } + + /** + * Returns whether a method is public and has a specific number of required parameters. + * + * @param \ReflectionClass $class The class of the method + * @param string $methodName The method name + * @param integer $parameters The number of parameters + * + * @return Boolean Whether the method is public and has $parameters + * required parameters + */ + private function isAccessible(\ReflectionClass $class, $methodName, $parameters) + { + if ($class->hasMethod($methodName)) { + $method = $class->getMethod($methodName); + + if ($method->isPublic() && $method->getNumberOfRequiredParameters() === $parameters) { + return true; + } + } + + return false; + } +} diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php new file mode 100644 index 00000000..25686e95 --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess; + +/** + * A configurable builder for PropertyAccessorInterface objects. + * + * @author Jérémie Augustin + */ +class PropertyAccessorBuilder +{ + /** + * @var Boolean + */ + private $magicCall = false; + + /** + * Enables the use of "__call" by the ProperyAccessor. + * + * @return PropertyAccessorBuilder The builder object + */ + public function enableMagicCall() + { + $this->magicCall = true; + + return $this; + } + + /** + * Disables the use of "__call" by the ProperyAccessor. + * + * @return PropertyAccessorBuilder The builder object + */ + public function disableMagicCall() + { + $this->magicCall = false; + + return $this; + } + + /** + * @return Boolean true if the use of "__call" by the ProperyAccessor is enabled + */ + public function isMagicCallEnabled() + { + return $this->magicCall; + } + + /** + * Builds and returns a new propertyAccessor object. + * + * @return PropertyAccessorInterface The built propertyAccessor + */ + public function getPropertyAccessor() + { + return new PropertyAccessor($this->magicCall); + } +} diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php new file mode 100644 index 00000000..1eed7c7b --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess; + +/** + * Writes and reads values to/from an object/array graph. + * + * @author Bernhard Schussek + */ +interface PropertyAccessorInterface +{ + /** + * Sets the value at the end of the property path of the object + * + * Example: + * + * use Symfony\Component\PropertyAccess\PropertyAccess; + * + * $propertyAccessor = PropertyAccess::getPropertyAccessor(); + * + * echo $propertyAccessor->setValue($object, 'child.name', 'Fabien'); + * // equals echo $object->getChild()->setName('Fabien'); + * + * This method first tries to find a public setter for each property in the + * path. The name of the setter must be the camel-cased property name + * prefixed with "set". + * + * If the setter does not exist, this method tries to find a public + * property. The value of the property is then changed. + * + * If neither is found, an exception is thrown. + * + * @param object|array $objectOrArray The object or array to modify + * @param string|PropertyPathInterface $propertyPath The property path to modify + * @param mixed $value The value to set at the end of the property path + * + * @throws Exception\NoSuchPropertyException If a property does not exist or is not public. + * @throws Exception\UnexpectedTypeException If a value within the path is neither object + * nor array + */ + public function setValue(&$objectOrArray, $propertyPath, $value); + + /** + * Returns the value at the end of the property path of the object + * + * Example: + * + * use Symfony\Component\PropertyAccess\PropertyAccess; + * + * $propertyAccessor = PropertyAccess::getPropertyAccessor(); + * + * echo $propertyAccessor->getValue($object, 'child.name); + * // equals echo $object->getChild()->getName(); + * + * This method first tries to find a public getter for each property in the + * path. The name of the getter must be the camel-cased property name + * prefixed with "get", "is", or "has". + * + * If the getter does not exist, this method tries to find a public + * property. The value of the property is then returned. + * + * If none of them are found, an exception is thrown. + * + * @param object|array $objectOrArray The object or array to traverse + * @param string|PropertyPathInterface $propertyPath The property path to read + * + * @return mixed The value at the end of the property path + * + * @throws Exception\NoSuchPropertyException If a property does not exist or is not public. + */ + public function getValue($objectOrArray, $propertyPath); +} diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPath.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPath.php new file mode 100644 index 00000000..840fc715 --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPath.php @@ -0,0 +1,225 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess; + +use Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException; +use Symfony\Component\PropertyAccess\Exception\OutOfBoundsException; +use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException; + +/** + * Default implementation of {@link PropertyPathInterface}. + * + * @author Bernhard Schussek + */ +class PropertyPath implements \IteratorAggregate, PropertyPathInterface +{ + /** + * Character used for separating between plural and singular of an element. + * @var string + */ + const SINGULAR_SEPARATOR = '|'; + + /** + * The elements of the property path + * @var array + */ + private $elements = array(); + + /** + * The singular forms of the elements in the property path. + * @var array + */ + private $singulars = array(); + + /** + * The number of elements in the property path + * @var integer + */ + private $length; + + /** + * Contains a Boolean for each property in $elements denoting whether this + * element is an index. It is a property otherwise. + * @var array + */ + private $isIndex = array(); + + /** + * String representation of the path + * @var string + */ + private $pathAsString; + + /** + * Constructs a property path from a string. + * + * @param PropertyPath|string $propertyPath The property path as string or instance + * + * @throws UnexpectedTypeException If the given path is not a string + * @throws InvalidPropertyPathException If the syntax of the property path is not valid + */ + public function __construct($propertyPath) + { + // Can be used as copy constructor + if ($propertyPath instanceof PropertyPath) { + /* @var PropertyPath $propertyPath */ + $this->elements = $propertyPath->elements; + $this->singulars = $propertyPath->singulars; + $this->length = $propertyPath->length; + $this->isIndex = $propertyPath->isIndex; + $this->pathAsString = $propertyPath->pathAsString; + + return; + } + if (!is_string($propertyPath)) { + throw new UnexpectedTypeException($propertyPath, 'string or Symfony\Component\PropertyAccess\PropertyPath'); + } + + 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 + $pattern = '/^(([^\.\[]+)|\[([^\]]+)\])(.*)/'; + + while (preg_match($pattern, $remaining, $matches)) { + if ('' !== $matches[2]) { + $element = $matches[2]; + $this->isIndex[] = false; + } else { + $element = $matches[3]; + $this->isIndex[] = true; + } + // Disabled this behaviour as the syntax is not yet final + //$pos = strpos($element, self::SINGULAR_SEPARATOR); + $pos = false; + $singular = null; + + if (false !== $pos) { + $singular = substr($element, $pos + 1); + $element = substr($element, 0, $pos); + } + + $this->elements[] = $element; + $this->singulars[] = $singular; + + $position += strlen($matches[1]); + $remaining = $matches[4]; + $pattern = '/^(\.(\w+)|\[([^\]]+)\])(.*)/'; + } + + if ('' !== $remaining) { + throw new InvalidPropertyPathException(sprintf( + 'Could not parse property path "%s". Unexpected token "%s" at position %d', + $propertyPath, + $remaining{0}, + $position + )); + } + + $this->length = count($this->elements); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->pathAsString; + } + + /** + * {@inheritdoc} + */ + public function getLength() + { + return $this->length; + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + if ($this->length <= 1) { + return null; + } + + $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->singulars); + array_pop($parent->isIndex); + + return $parent; + } + + /** + * Returns a new iterator for this path + * + * @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])) { + throw new OutOfBoundsException(sprintf('The index %s is not within the property path', $index)); + } + + return $this->elements[$index]; + } + + /** + * {@inheritdoc} + */ + public function isProperty($index) + { + if (!isset($this->isIndex[$index])) { + throw new OutOfBoundsException(sprintf('The index %s is not within the property path', $index)); + } + + return !$this->isIndex[$index]; + } + + /** + * {@inheritdoc} + */ + public function isIndex($index) + { + if (!isset($this->isIndex[$index])) { + throw new OutOfBoundsException(sprintf('The index %s is not within the property path', $index)); + } + + return $this->isIndex[$index]; + } +} diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathBuilder.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathBuilder.php new file mode 100644 index 00000000..f4eb0fb9 --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathBuilder.php @@ -0,0 +1,306 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess; + +use Symfony\Component\PropertyAccess\Exception\OutOfBoundsException; + +/** + * @author Bernhard Schussek + */ +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 integer $offset The offset where the appended + * piece starts in $path. + * @param integer $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 integer $offset The offset at which to remove + * @param integer $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 integer $offset The offset at which to replace. + * @param integer $length The length of the piece to replace. + * @param PropertyPathInterface|string $path The path to insert. + * @param integer $pathOffset The offset where the inserted piece + * starts in $path. + * @param integer $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 integer $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 integer $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 integer 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 integer $offset The offset where the removed chunk starts + * @param integer $cutLength The length of the removed chunk + * @param integer $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]; + } + } + } +} diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathInterface.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathInterface.php new file mode 100644 index 00000000..95f34ffa --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathInterface.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess; + +/** + * A sequence of property names or array indices. + * + * @author Bernhard Schussek + */ +interface PropertyPathInterface extends \Traversable +{ + /** + * Returns the string representation of the property path + * + * @return string The path as string + */ + public function __toString(); + + /** + * Returns the length of the property path, i.e. the number of elements. + * + * @return integer The path length + */ + public function getLength(); + + /** + * Returns the parent property path. + * + * The parent property path is the one that contains the same items as + * this one except for the last one. + * + * If this property path only contains one item, null is returned. + * + * @return PropertyPath The parent path or null + */ + public function getParent(); + + /** + * Returns the elements of the property path as array + * + * @return array An array of property/index names + */ + public function getElements(); + + /** + * Returns the element at the given index in the property path + * + * @param integer $index The index key + * + * @return string A property or index name + * + * @throws Exception\OutOfBoundsException If the offset is invalid + */ + public function getElement($index); + + /** + * Returns whether the element at the given index is a property + * + * @param integer $index The index in the property path + * + * @return Boolean Whether the element at this index is a property + * + * @throws Exception\OutOfBoundsException If the offset is invalid + */ + public function isProperty($index); + + /** + * Returns whether the element at the given index is an array index + * + * @param integer $index The index in the property path + * + * @return Boolean Whether the element at this index is an array index + * + * @throws Exception\OutOfBoundsException If the offset is invalid + */ + public function isIndex($index); +} diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIterator.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIterator.php new file mode 100644 index 00000000..d6cd49ca --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIterator.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess; + +/** + * Traverses a property path and provides additional methods to find out + * information about the current element + * + * @author Bernhard Schussek + */ +class PropertyPathIterator extends \ArrayIterator implements PropertyPathIteratorInterface +{ + /** + * The traversed property path + * @var PropertyPathInterface + */ + protected $path; + + /** + * Constructor. + * + * @param PropertyPathInterface $path The property path to traverse + */ + public function __construct(PropertyPathInterface $path) + { + parent::__construct($path->getElements()); + + $this->path = $path; + } + + /** + * {@inheritdoc} + */ + public function isIndex() + { + return $this->path->isIndex($this->key()); + } + + /** + * {@inheritdoc} + */ + public function isProperty() + { + return $this->path->isProperty($this->key()); + } +} diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIteratorInterface.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIteratorInterface.php new file mode 100644 index 00000000..cb43f8d7 --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/PropertyPathIteratorInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess; + +/** + * @author Bernhard Schussek + */ +interface PropertyPathIteratorInterface extends \Iterator, \SeekableIterator +{ + /** + * Returns whether the current element in the property path is an array + * index. + * + * @return Boolean + */ + public function isIndex(); + + /** + * Returns whether the current element in the property path is a property + * name. + * + * @return Boolean + */ + public function isProperty(); +} diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/README.md b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/README.md new file mode 100644 index 00000000..0ae94e08 --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/README.md @@ -0,0 +1,14 @@ +PropertyAccess Component +======================== + +PropertyAccess reads/writes values from/to object/array graphs using a simple +string notation. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/PropertyAccess/ + $ composer.phar install --dev + $ phpunit diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/StringUtil.php b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/StringUtil.php new file mode 100644 index 00000000..e782cc10 --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/StringUtil.php @@ -0,0 +1,195 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess; + +/** + * Creates singulars from plurals. + * + * @author Bernhard Schussek + */ +class StringUtil +{ + /** + * Map english plural to singular suffixes + * + * @var array + * + * @see http://english-zone.com/spelling/plurals.html + * @see http://www.scribd.com/doc/3271143/List-of-100-Irregular-Plural-Nouns-in-English + */ + private static $pluralMap = array( + // First entry: plural suffix, reversed + // Second entry: length of plural suffix + // Third entry: Whether the suffix may succeed a vocal + // Fourth entry: Whether the suffix may succeed a consonant + // Fifth entry: singular suffix, normal + + // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) + array('a', 1, true, true, array('on', 'um')), + + // nebulae (nebula) + array('ea', 2, true, true, 'a'), + + // mice (mouse), lice (louse) + array('eci', 3, false, true, 'ouse'), + + // geese (goose) + array('esee', 4, false, true, 'oose'), + + // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius) + array('i', 1, true, true, 'us'), + + // men (man), women (woman) + array('nem', 3, true, true, 'man'), + + // children (child) + array('nerdlihc', 8, true, true, 'child'), + + // oxen (ox) + array('nexo', 4, false, false, 'ox'), + + // indices (index), appendices (appendix), prices (price) + array('seci', 4, false, true, array('ex', 'ix', 'ice')), + + // babies (baby) + array('sei', 3, false, true, 'y'), + + // analyses (analysis), ellipses (ellipsis), funguses (fungus), + // neuroses (neurosis), theses (thesis), emphases (emphasis), + // oases (oasis), crises (crisis), houses (house), bases (base), + // atlases (atlas), kisses (kiss) + array('ses', 3, true, true, array('s', 'se', 'sis')), + + // objectives (objective), alternative (alternatives) + array('sevit', 5, true, true, 'tive'), + + // lives (life), wives (wife) + array('sevi', 4, false, true, 'ife'), + + // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf) + array('sev', 3, true, true, 'f'), + + // axes (axis), axes (ax), axes (axe) + array('sexa', 4, false, false, array('ax', 'axe', 'axis')), + + // indexes (index), matrixes (matrix) + array('sex', 3, true, false, 'x'), + + // quizzes (quiz) + array('sezz', 4, true, false, 'z'), + + // bureaus (bureau) + array('suae', 4, false, true, 'eau'), + + // roses (rose), garages (garage), cassettes (cassette), + // waltzes (waltz), heroes (hero), bushes (bush), arches (arch), + // shoes (shoe) + array('se', 2, true, true, array('', 'e')), + + // tags (tag) + array('s', 1, true, true, ''), + + // chateaux (chateau) + array('xuae', 4, false, true, 'eau'), + ); + + /** + * This class should not be instantiated + */ + private function __construct() {} + + /** + * Returns the singular form of a word + * + * If the method can't determine the form with certainty, an array of the + * possible singulars is returned. + * + * @param string $plural A word in plural form + * @return string|array The singular form or an array of possible singular + * forms + */ + public static function singularify($plural) + { + $pluralRev = strrev($plural); + $lowerPluralRev = strtolower($pluralRev); + $pluralLength = strlen($lowerPluralRev); + + // The outer loop iterates over the entries of the plural table + // The inner loop $j iterates over the characters of the plural suffix + // in the plural table to compare them with the characters of the actual + // given plural suffix + foreach (self::$pluralMap as $map) { + $suffix = $map[0]; + $suffixLength = $map[1]; + $j = 0; + + // Compare characters in the plural table and of the suffix of the + // given plural one by one + while ($suffix[$j] === $lowerPluralRev[$j]) { + // Let $j point to the next character + ++$j; + + // Successfully compared the last character + // Add an entry with the singular suffix to the singular array + if ($j === $suffixLength) { + // Is there any character preceding the suffix in the plural string? + if ($j < $pluralLength) { + $nextIsVocal = false !== strpos('aeiou', $lowerPluralRev[$j]); + + if (!$map[2] && $nextIsVocal) { + // suffix may not succeed a vocal but next char is one + break; + } + + if (!$map[3] && !$nextIsVocal) { + // suffix may not succeed a consonant but next char is one + break; + } + } + + $newBase = substr($plural, 0, $pluralLength - $suffixLength); + $newSuffix = $map[4]; + + // Check whether the first character in the plural suffix + // is uppercased. If yes, uppercase the first character in + // the singular suffix too + $firstUpper = ctype_upper($pluralRev[$j - 1]); + + if (is_array($newSuffix)) { + $singulars = array(); + + foreach ($newSuffix as $newSuffixEntry) { + $singulars[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry); + } + + return $singulars; + } + + return $newBase.($firstUpper ? ucFirst($newSuffix) : $newSuffix); + } + + // Suffix is longer than word + if ($j === $pluralLength) { + break; + } + } + } + + // Convert teeth to tooth, feet to foot + if (false !== ($pos = strpos($plural, 'ee'))) { + return substr_replace($plural, 'oo', $pos, 2); + } + + // Assume that plural and singular is identical + return $plural; + } +} diff --git a/vendor/symfony/property-access/Symfony/Component/PropertyAccess/composer.json b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/composer.json new file mode 100644 index 00000000..31860427 --- /dev/null +++ b/vendor/symfony/property-access/Symfony/Component/PropertyAccess/composer.json @@ -0,0 +1,31 @@ +{ + "name": "symfony/property-access", + "type": "library", + "description": "Symfony PropertyAccess Component", + "keywords": ["property", "index", "access", "object", "array", "extraction", "injection", "reflection", "property path"], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\PropertyAccess\\": "" } + }, + "target-dir": "Symfony/Component/PropertyAccess", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + } +} -- cgit v1.2.3