/**
  * Recursively parse all metadata for a class
  *
  * @param  string                    $className Class to get all metadata for
  * @param  array                     $visited   Classes we've already visited to prevent infinite recursion.
  * @param  array                     $groups    Serialization groups to include.
  * @return array                     metadata for given class
  * @throws \InvalidArgumentException
  */
 protected function doParse($className, $visited = array(), array $groups = array())
 {
     $meta = $this->factory->getMetadataForClass($className);
     if (null === $meta) {
         throw new \InvalidArgumentException(sprintf("No metadata found for class %s", $className));
     }
     $exclusionStrategies = array();
     $exclusionStrategies[] = new GroupsExclusionStrategy($groups);
     $params = array();
     // iterate over property metadata
     foreach ($meta->propertyMetadata as $item) {
         if (!is_null($item->type)) {
             $name = $this->namingStrategy->translateName($item);
             $dataType = $this->processDataType($item);
             // apply exclusion strategies
             foreach ($exclusionStrategies as $strategy) {
                 if (true === $strategy->shouldSkipProperty($item, SerializationContext::create())) {
                     continue 2;
                 }
             }
             $params[$name] = array('dataType' => $dataType['normalized'], 'required' => false, 'description' => $this->getDescription($className, $item), 'readonly' => $item->readOnly, 'sinceVersion' => $item->sinceVersion, 'untilVersion' => $item->untilVersion);
             // if class already parsed, continue, to avoid infinite recursion
             if (in_array($dataType['class'], $visited)) {
                 continue;
             }
             // check for nested classes with JMS metadata
             if ($dataType['class'] && null !== $this->factory->getMetadataForClass($dataType['class'])) {
                 $visited[] = $dataType['class'];
                 $params[$name]['children'] = $this->doParse($dataType['class'], $visited, $groups);
             }
         }
     }
     return $params;
 }
 /**
  * Recursively parse all metadata for a class
  *
  * @param  string                    $className Class to get all metadata for
  * @param  array                     $visited   Classes we've already visited to prevent infinite recursion.
  * @param  array                     $groups    Serialization groups to include.
  * @return array                     metadata for given class
  * @throws \InvalidArgumentException
  */
 protected function doParse($className, $visited = array(), array $groups = array())
 {
     $meta = $this->factory->getMetadataForClass($className);
     if (null === $meta) {
         throw new \InvalidArgumentException(sprintf("No metadata found for class %s", $className));
     }
     $exclusionStrategies = array();
     if ($groups) {
         $exclusionStrategies[] = new GroupsExclusionStrategy($groups);
     }
     $params = array();
     $reflection = new \ReflectionClass($className);
     $defaultProperties = array_map(function ($default) {
         if (is_array($default) && count($default) === 0) {
             return null;
         }
         return $default;
     }, $reflection->getDefaultProperties());
     // iterate over property metadata
     foreach ($meta->propertyMetadata as $item) {
         if (!is_null($item->type)) {
             $name = $this->namingStrategy->translateName($item);
             $dataType = $this->processDataType($item);
             // apply exclusion strategies
             foreach ($exclusionStrategies as $strategy) {
                 if (true === $strategy->shouldSkipProperty($item, SerializationContext::create())) {
                     $params[$name] = null;
                     continue 2;
                 }
             }
             if (!$dataType['inline']) {
                 $params[$name] = array('dataType' => $dataType['normalized'], 'actualType' => $dataType['actualType'], 'subType' => $dataType['class'], 'required' => false, 'default' => isset($defaultProperties[$item->name]) ? $defaultProperties[$item->name] : null, 'description' => $this->getDescription($item), 'readonly' => $item->readOnly, 'sinceVersion' => $item->sinceVersion, 'untilVersion' => $item->untilVersion);
                 if (!is_null($dataType['class']) && false === $dataType['primitive']) {
                     $params[$name]['class'] = $dataType['class'];
                 }
             }
             // we can use type property also for custom handlers, then we don't have here real class name
             if (!class_exists($dataType['class'])) {
                 continue;
             }
             // if class already parsed, continue, to avoid infinite recursion
             if (in_array($dataType['class'], $visited)) {
                 continue;
             }
             // check for nested classes with JMS metadata
             if ($dataType['class'] && false === $dataType['primitive'] && null !== $this->factory->getMetadataForClass($dataType['class'])) {
                 $visited[] = $dataType['class'];
                 $children = $this->doParse($dataType['class'], $visited, $groups);
                 if ($dataType['inline']) {
                     $params = array_merge($params, $children);
                 } else {
                     $params[$name]['children'] = $children;
                 }
             }
         }
     }
     return $params;
 }
 /**
  * rename extracted parameters with JMS metadata naming strategy
  *
  * @param array $parameters
  * @param array $input
  * @return array
  */
 protected function renameJmsParameters($parameters, $input)
 {
     $result = [];
     foreach ($parameters as $name => $value) {
         $className = $input['class'];
         $meta = null;
         try {
             $meta = $this->factory->getMetadataForClass($className);
         } catch (\ReflectionException $e) {
         }
         if (isset($meta->propertyMetadata[$name])) {
             $name = $this->namingStrategy->translateName($meta->propertyMetadata[$name]);
         }
         $result[$name] = $value;
     }
     return $result;
 }
 public function onPostSerialize(ObjectEvent $event)
 {
     $visitor = $event->getVisitor();
     $object = $event->getObject();
     $context = $event->getContext();
     /** @var ClassMetadata $metadata */
     $metadata = $this->hateoasMetadataFactory->getMetadataForClass(get_class($object));
     // if it has no json api metadata, skip it
     if (null === $metadata) {
         return;
     }
     /** @var \JMS\Serializer\Metadata\ClassMetadata $jmsMetadata */
     $jmsMetadata = $this->jmsMetadataFactory->getMetadataForClass(get_class($object));
     $propertyAccessor = PropertyAccess::createPropertyAccessor();
     if ($visitor instanceof JsonApiSerializationVisitor) {
         $visitor->addData(self::EXTRA_DATA_KEY, $this->getRelationshipDataArray($metadata, $this->getId($metadata, $object)));
         $relationships = array();
         foreach ($metadata->getRelationships() as $relationship) {
             $relationshipPropertyName = $relationship->getName();
             $relationshipObject = $propertyAccessor->getValue($object, $relationshipPropertyName);
             // JMS Serializer support
             if (!isset($jmsMetadata->propertyMetadata[$relationshipPropertyName])) {
                 continue;
             }
             $jmsPropertyMetadata = $jmsMetadata->propertyMetadata[$relationshipPropertyName];
             $relationshipPayloadKey = $this->namingStrategy->translateName($jmsPropertyMetadata);
             $relationshipData =& $relationships[$relationshipPayloadKey];
             $relationshipData = array();
             // add `links`
             $links = $this->processRelationshipLinks($object, $relationship, $relationshipPayloadKey);
             if ($links) {
                 $relationshipData['links'] = $links;
             }
             $include = [];
             if ($request = $this->requestStack->getCurrentRequest()) {
                 $include = $request->query->get('include');
                 $include = $this->parseInclude($include);
             }
             // FIXME: $includePath always is relative to the primary resource, so we can build our way with
             // class metadata to find out if we can include this relationship.
             foreach ($include as $includePath) {
                 $last = end($includePath);
                 if ($last === $relationship->getName()) {
                     // keep track of the path we are currently following (e.x. comments -> author)
                     $this->currentPath = $includePath;
                     $relationship->setIncludedByDefault(true);
                     // we are done here, since we have found out we can include this relationship :)
                     break;
                 }
             }
             // We show the relationships data if it is included or if there are no links. We do this
             // because there MUST be links or data (see: http://jsonapi.org/format/#document-resource-object-relationships).
             if ($relationship->isIncludedByDefault() || !$links || $relationship->getShowData()) {
                 // hasMany relationship
                 if ($this->isIteratable($relationshipObject)) {
                     $relationshipData['data'] = array();
                     foreach ($relationshipObject as $item) {
                         $relationshipData['data'][] = $this->processRelationship($item, $relationship, $context);
                     }
                 } else {
                     $relationshipData['data'] = $this->processRelationship($relationshipObject, $relationship, $context);
                 }
             }
         }
         if ($relationships) {
             $visitor->addData('relationships', $relationships);
         }
         // TODO: Improve link handling
         if (true === $metadata->getResource()->getShowLinkSelf()) {
             $visitor->addData('links', array('self' => $this->baseUrl . '/' . $metadata->getResource()->getType() . '/' . $this->getId($metadata, $object)));
         }
         $root = (array) $visitor->getRoot();
         $root['included'] = array_values($this->includedRelationships);
         $visitor->setRoot($root);
     }
 }
 public function setUp()
 {
     $this->propertyNamingStrategy = $this->prophesize(PropertyNamingStrategyInterface::class);
     $this->arraySerializationVisitor = new ArraySerializationVisitor($this->propertyNamingStrategy->reveal());
 }