/**
  * @param mixed                 $value     The unexpected value found while traversing property path
  * @param PropertyPathInterface $path      The property path
  * @param int                   $pathIndex The property path index when the unexpected value was found
  */
 public function __construct($value, $path, $pathIndex = null)
 {
     if (func_num_args() === 3 && $path instanceof PropertyPathInterface) {
         $message = sprintf('PropertyAccessor requires a graph of objects or arrays to operate on, ' . 'but it found type "%s" while trying to traverse path "%s" at property "%s".', gettype($value), (string) $path, $path->getElement($pathIndex));
     } else {
         trigger_error('The ' . __CLASS__ . ' constructor now expects 3 arguments: the invalid property value, the ' . __NAMESPACE__ . '\\PropertyPathInterface object and the current index of the property path.', E_USER_DEPRECATED);
         $message = sprintf('Expected argument of type "%s", "%s" given', $path, is_object($value) ? get_class($value) : gettype($value));
     }
     parent::__construct($message);
 }
Example #2
0
 /**
  * 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 int                   $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)
 {
     if (!is_object($objectOrArray) && !is_array($objectOrArray)) {
         throw new UnexpectedTypeException($objectOrArray, 'object or array');
     }
     $propertyValues = array();
     for ($i = 0; $i < $lastIndex; ++$i) {
         $property = $propertyPath->getElement($i);
         $isIndex = $propertyPath->isIndex($i);
         // Create missing nested arrays on demand
         if ($isIndex && ($objectOrArray instanceof \ArrayAccess && !isset($objectOrArray[$property]) || is_array($objectOrArray) && !array_key_exists($property, $objectOrArray))) {
             $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];
         // the final value of the path must not be validated
         if ($i + 1 < $propertyPath->getLength() && !is_object($objectOrArray) && !is_array($objectOrArray)) {
             throw new UnexpectedTypeException($objectOrArray, 'object or array');
         }
         $propertyValues[] =& $propertyValue;
     }
     return $propertyValues;
 }
Example #3
0
 /**
  * Reads a value of the given property from an object or array.
  *
  * @param array|object          $object   The object or array to read from
  * @param mixed                 $property The property or index to read
  * @param boolean               $strict
  * @param PropertyPathInterface $propertyPath
  * @param int                   $propertyPathIndex
  *
  * @return mixed
  *
  * @throws Exception\NoSuchPropertyException if the property does not exist or is not public
  *
  * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
  */
 protected function &readValue(&$object, $property, $strict, PropertyPathInterface $propertyPath = null, $propertyPathIndex = null)
 {
     // Use an array instead of an object since performance is very crucial here
     $result = [self::VALUE => null, self::IS_REF => false];
     if (is_array($object)) {
         if (isset($object[$property])) {
             $result[self::VALUE] =& $object[$property];
             $result[self::IS_REF] = true;
         } elseif ($strict && !array_key_exists($property, $object)) {
             throw new Exception\NoSuchPropertyException(sprintf('The key "%s" does exist in an array.', $property));
         }
     } elseif ($object instanceof \ArrayAccess) {
         if (isset($object[$property])) {
             $result[self::VALUE] = $object[$property];
         } elseif ($strict) {
             $reflClass = new \ReflectionClass($object);
             throw new Exception\NoSuchPropertyException(sprintf('The key "%s" does exist in class "%s".', $property, $reflClass->name));
         }
     } elseif (is_object($object)) {
         $reflClass = new \ReflectionClass($object);
         $camel = $this->camelize($property);
         if ($reflClass->hasMethod($getter = 'get' . $camel) && $reflClass->getMethod($getter)->isPublic()) {
             $result[self::VALUE] = $object->{$getter}();
         } elseif ($reflClass->hasMethod($isser = 'is' . $camel) && $reflClass->getMethod($isser)->isPublic()) {
             $result[self::VALUE] = $object->{$isser}();
         } elseif ($reflClass->hasMethod($hasser = 'has' . $camel) && $reflClass->getMethod($hasser)->isPublic()) {
             $result[self::VALUE] = $object->{$hasser}();
         } elseif ($reflClass->hasMethod('__get') && $reflClass->getMethod('__get')->isPublic()) {
             if ($reflClass->hasMethod('__isset') && $reflClass->getMethod('__isset')->isPublic()) {
                 if (!isset($object->{$property})) {
                     throw new Exception\NoSuchPropertyException(sprintf('The property "%s" cannot be got by "__get" method ' . 'because "__isset" method returns false. Class "%s".', $property, $reflClass->name));
                 }
             }
             $result[self::VALUE] = $object->{$property};
         } else {
             $hasProp = $reflClass->hasProperty($property);
             if ($hasProp && $reflClass->getProperty($property)->isPublic()) {
                 $result[self::VALUE] =& $object->{$property};
                 $result[self::IS_REF] = true;
             } elseif (!$hasProp && property_exists($object, $property)) {
                 // Needed to support \stdClass instances. We need to explicitly
                 // exclude $hasProp, 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 {
                 $methods = [$getter, $isser, $hasser, '__get'];
                 if ($this->magicCall) {
                     $methods[] = '__call';
                 }
                 throw new Exception\NoSuchPropertyException(sprintf('Neither the property "%s" nor one of the methods "%s()" ' . 'exist and have public access in class "%s".', $property, implode('()", "', $methods), $reflClass->name));
             }
         }
     } else {
         if ($propertyPath !== null && $propertyPathIndex !== null) {
             throw new Exception\NoSuchPropertyException(sprintf('PropertyAccessor requires a graph of objects or arrays to operate on, ' . 'but it found type "%s" while trying to traverse path "%s" at property "%s".', gettype($object), (string) $propertyPath, $propertyPath->getElements()[$propertyPathIndex]));
         } else {
             throw new Exception\NoSuchPropertyException(sprintf('Unexpected object type. Expected "array or object", "%s" given.', is_object($object) ? get_class($object) : gettype($object)));
         }
     }
     // Objects are always passed around by reference
     if (!$result[self::IS_REF] && is_object($result[self::VALUE])) {
         $result[self::IS_REF] = true;
     }
     return $result;
 }
 /**
  * Reads the path from an object up to a given path index.
  *
  * @param array                 $zval                 The array containing the object or array to read from
  * @param PropertyPathInterface $propertyPath         The property path to read
  * @param int                   $lastIndex            The index up to which should be read
  * @param bool                  $ignoreInvalidIndices Whether to ignore invalid indices or throw an exception
  *
  * @return array The values read in the path.
  *
  * @throws UnexpectedTypeException If a value within the path is neither object nor array.
  * @throws NoSuchIndexException    If a non-existing index is accessed
  */
 private function readPropertiesUntil($zval, PropertyPathInterface $propertyPath, $lastIndex, $ignoreInvalidIndices = true)
 {
     if (!is_object($zval[self::VALUE]) && !is_array($zval[self::VALUE])) {
         throw new UnexpectedTypeException($zval[self::VALUE], $propertyPath, 0);
     }
     // Add the root object to the list
     $propertyValues = array($zval);
     for ($i = 0; $i < $lastIndex; ++$i) {
         $property = $propertyPath->getElement($i);
         $isIndex = $propertyPath->isIndex($i);
         if ($isIndex) {
             // Create missing nested arrays on demand
             if ($zval[self::VALUE] instanceof \ArrayAccess && !$zval[self::VALUE]->offsetExists($property) || is_array($zval[self::VALUE]) && !isset($zval[self::VALUE][$property]) && !array_key_exists($property, $zval[self::VALUE])) {
                 if (!$ignoreInvalidIndices) {
                     if (!is_array($zval[self::VALUE])) {
                         if (!$zval[self::VALUE] instanceof \Traversable) {
                             throw new NoSuchIndexException(sprintf('Cannot read index "%s" while trying to traverse path "%s".', $property, (string) $propertyPath));
                         }
                         $zval[self::VALUE] = iterator_to_array($zval[self::VALUE]);
                     }
                     throw new NoSuchIndexException(sprintf('Cannot read index "%s" while trying to traverse path "%s". Available indices are "%s".', $property, (string) $propertyPath, print_r(array_keys($zval[self::VALUE]), true)));
                 }
                 if ($i + 1 < $propertyPath->getLength()) {
                     if (isset($zval[self::REF])) {
                         $zval[self::VALUE][$property] = array();
                         $zval[self::REF] = $zval[self::VALUE];
                     } else {
                         $zval[self::VALUE] = array($property => array());
                     }
                 }
             }
             $zval = $this->readIndex($zval, $property);
         } else {
             $zval = $this->readProperty($zval, $property);
         }
         // the final value of the path must not be validated
         if ($i + 1 < $propertyPath->getLength() && !is_object($zval[self::VALUE]) && !is_array($zval[self::VALUE])) {
             throw new UnexpectedTypeException($zval[self::VALUE], $propertyPath, $i + 1);
         }
         if (isset($zval[self::REF]) && (0 === $i || isset($propertyValues[$i - 1][self::IS_REF_CHAINED]))) {
             // Set the IS_REF_CHAINED flag to true if:
             // current property is passed by reference and
             // it is the first element in the property path or
             // the IS_REF_CHAINED flag of its parent element is true
             // Basically, this flag is true only when the reference chain from the top element to current element is not broken
             $zval[self::IS_REF_CHAINED] = true;
         }
         $propertyValues[] = $zval;
     }
     return $propertyValues;
 }
 /**
  * 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 int                   $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, $ignoreInvalidIndices = true)
 {
     $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])) {
             if (!$ignoreInvalidIndices) {
                 if (!is_array($objectOrArray)) {
                     if (!$objectOrArray instanceof \Traversable) {
                         throw new NoSuchIndexException(sprintf('Cannot read property "%s".', $property));
                     }
                     $objectOrArray = iterator_to_array($objectOrArray);
                 }
                 throw new NoSuchIndexException(sprintf('Cannot read property "%s". Available properties are "%s"', $property, print_r(array_keys($objectOrArray), true)));
             }
             $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;
 }
Example #6
0
 /**
  * @param PropertyPathInterface $propertyPath
  *
  * @return string
  */
 protected function compilePathIndexes(PropertyPathInterface $propertyPath)
 {
     $indexes = [];
     $count = count($propertyPath->getElements());
     for ($i = 0; $i < $count; $i++) {
         $indexes[] = $propertyPath->isIndex($i);
     }
     return implode(', ', array_map(function ($val) {
         return $val ? 'true' : 'false';
     }, $indexes));
 }
Example #7
0
 /**
  * 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 int                   $lastIndex            The index up to which should be read
  * @param bool                  $ignoreInvalidIndices Whether to ignore invalid indices
  *                                                    or throw an exception
  *
  * @return array The values read in the path.
  *
  * @throws UnexpectedTypeException If a value within the path is neither object nor array.
  * @throws NoSuchIndexException    If a non-existing index is accessed
  */
 private function &readPropertiesUntil(&$objectOrArray, PropertyPathInterface $propertyPath, $lastIndex, $ignoreInvalidIndices = true)
 {
     if (!is_object($objectOrArray) && !is_array($objectOrArray)) {
         throw new UnexpectedTypeException($objectOrArray, $propertyPath, 0);
     }
     $propertyValues = array();
     for ($i = 0; $i < $lastIndex; ++$i) {
         $property = $propertyPath->getElement($i);
         $isIndex = $propertyPath->isIndex($i);
         // Create missing nested arrays on demand
         if ($isIndex && ($objectOrArray instanceof \ArrayAccess && !isset($objectOrArray[$property]) || is_array($objectOrArray) && !array_key_exists($property, $objectOrArray))) {
             if (!$ignoreInvalidIndices) {
                 if (!is_array($objectOrArray)) {
                     if (!$objectOrArray instanceof \Traversable) {
                         throw new NoSuchIndexException(sprintf('Cannot read index "%s" while trying to traverse path "%s".', $property, (string) $propertyPath));
                     }
                     $objectOrArray = iterator_to_array($objectOrArray);
                 }
                 throw new NoSuchIndexException(sprintf('Cannot read index "%s" while trying to traverse path "%s". Available indices are "%s".', $property, (string) $propertyPath, print_r(array_keys($objectOrArray), true)));
             }
             $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];
         // the final value of the path must not be validated
         if ($i + 1 < $propertyPath->getLength() && !is_object($objectOrArray) && !is_array($objectOrArray)) {
             throw new UnexpectedTypeException($objectOrArray, $propertyPath, $i + 1);
         }
         $propertyValues[] =& $propertyValue;
     }
     return $propertyValues;
 }
    /**
     * 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 $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, PropertyPathInterface $path, $pathOffset = 0, $pathLength = 0)
    {
        if (!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);
        }
    }
 /**
  * @param \Symfony\Component\PropertyAccess\PropertyPathInterface $propertyPath
  * @return string
  */
 private function getMostNestedProperty(PropertyPathInterface $propertyPath)
 {
     return $propertyPath->getElement($propertyPath->getLength() - 1);
 }
 /**
  * @param mixed                 $value     The unexpected value found while traversing property path
  * @param PropertyPathInterface $path      The property path
  * @param int                   $pathIndex The property path index when the unexpected value was found
  */
 public function __construct($value, PropertyPathInterface $path, $pathIndex)
 {
     $message = sprintf('PropertyAccessor requires a graph of objects or arrays to operate on, ' . 'but it found type "%s" while trying to traverse path "%s" at property "%s".', gettype($value), (string) $path, $path->getElement($pathIndex));
     parent::__construct($message);
 }