From 5e1549a29db6b45c78c7e2315f20700ba881e29c Mon Sep 17 00:00:00 2001 From: Josh Hall-Bachner Date: Sun, 30 Jun 2013 18:46:00 -0700 Subject: [PATCH] Built parse-merging into the ApiDocExtractor. Wired up a "post-parse" pass to allow recursive parsing across multiple parsers. --- Extractor/ApiDocExtractor.php | 51 +++++++++++++++++-- Formatter/AbstractFormatter.php | 2 +- Parser/FormTypeParser.php | 3 +- Parser/JmsMetadataParser.php | 4 ++ ...idationParser.php => ValidationParser.php} | 41 +++++++++++---- Resources/config/formatters.xml | 4 +- 6 files changed, 89 insertions(+), 16 deletions(-) rename Parser/{SymfonyValidationParser.php => ValidationParser.php} (72%) diff --git a/Extractor/ApiDocExtractor.php b/Extractor/ApiDocExtractor.php index 720a593..4ba5a37 100644 --- a/Extractor/ApiDocExtractor.php +++ b/Extractor/ApiDocExtractor.php @@ -268,10 +268,17 @@ class ApiDocExtractor $normalizedInput = $this->normalizeClassParameter($input); + $parameters = array(); foreach ($this->parsers as $parser) { if ($parser->supports($normalizedInput)) { - $parameters = $parser->parse($normalizedInput); - break; + $parameters = $this->mergeParameters($parameters, $parser->parse($normalizedInput)); + } + } + + foreach($this->parsers as $parser) { + if($parser->supports($normalizedInput) && method_exists($parser, 'postParse')) { + $mp = $parser->postParse($normalizedInput, $parameters); + $parameters = $this->mergeParameters($parameters, $mp); } } @@ -294,7 +301,6 @@ class ApiDocExtractor foreach ($this->parsers as $parser) { if ($parser->supports($normalizedOutput)) { $response = $parser->parse($normalizedOutput); - break; } } @@ -374,6 +380,45 @@ class ApiDocExtractor return array_merge($defaults, $input); } + protected function mergeParameters($p1, $p2) + { + $params = array(); + + foreach($p2 as $propname => $propvalue) { + if(!isset($p1[$propname])) { + $params[$propname] = $propvalue; + } else { + $v1 = $p1[$propname]; + + foreach($propvalue as $name => $value) { + if(is_array($value)) { + if(isset($v1[$name]) && is_array($v1[$name])) { + $v1[$name] = $this->mergeParameters($v1[$name], $value); + } else { + $v1[$name] = $value; + } + } elseif(!is_null($value)) { + if(in_array($name, array('required', 'readonly'))) { + $v1[$name] = $v1[$name] || $value; + } elseif($name == 'requirement') { + if(isset($v1[$name])) { + $v1[$name] .= ', ' . $value; + } else { + $v1[$name] = $value; + } + } else { + $v1[$name] = $value; + } + } + } + + $params[$propname] = $v1; + } + } + + return $params; + } + /** * Parses annotations for a given method, and adds new information to the given ApiDoc * annotation. Useful to extract information from the FOSRestBundle annotations. diff --git a/Formatter/AbstractFormatter.php b/Formatter/AbstractFormatter.php index f521a4a..ca52fdd 100644 --- a/Formatter/AbstractFormatter.php +++ b/Formatter/AbstractFormatter.php @@ -69,10 +69,10 @@ abstract class AbstractFormatter implements FormatterInterface $newName = $this->getNewName($name, $info, $parentName); $newParams[$newName] = array( - 'description' => $info['description'], 'dataType' => $info['dataType'], 'readonly' => $info['readonly'], 'required' => $info['required'], + 'description' => array_key_exists('description', $info) ? $info['description'] : null, 'format' => array_key_exists('format', $info) ? $info['format'] : null, 'sinceVersion' => array_key_exists('sinceVersion', $info) ? $info['sinceVersion'] : null, 'untilVersion' => array_key_exists('untilVersion', $info) ? $info['untilVersion'] : null, diff --git a/Parser/FormTypeParser.php b/Parser/FormTypeParser.php index f55f913..bf4d4ca 100644 --- a/Parser/FormTypeParser.php +++ b/Parser/FormTypeParser.php @@ -87,7 +87,6 @@ class FormTypeParser implements ParserInterface private function parseForm($form, $prefix = null) { - $className = get_class($form); $parameters = array(); foreach ($form as $name => $child) { $config = $child->getConfig(); @@ -133,6 +132,8 @@ class FormTypeParser implements ParserInterface 'description' => $config->getAttribute('description'), 'readonly' => $config->getDisabled(), ); + } else { + $parameters[$name]['class'] = $type; } continue; diff --git a/Parser/JmsMetadataParser.php b/Parser/JmsMetadataParser.php index 8286e20..1b3d149 100644 --- a/Parser/JmsMetadataParser.php +++ b/Parser/JmsMetadataParser.php @@ -126,6 +126,10 @@ class JmsMetadataParser implements ParserInterface 'untilVersion' => $item->untilVersion, ); + if(!is_null($dataType['class'])) { + $params[$name]['class'] = $dataType['class']; + } + // if class already parsed, continue, to avoid infinite recursion if (in_array($dataType['class'], $visited)) { continue; diff --git a/Parser/SymfonyValidationParser.php b/Parser/ValidationParser.php similarity index 72% rename from Parser/SymfonyValidationParser.php rename to Parser/ValidationParser.php index f1294cf..1385cd4 100644 --- a/Parser/SymfonyValidationParser.php +++ b/Parser/ValidationParser.php @@ -6,7 +6,7 @@ use Nelmio\ApiDocBundle\Parser\ParserInterface; use Symfony\Component\Validator\MetadataFactoryInterface; use Symfony\Component\Validator\Constraint; -class SymfonyValidationParser implements ParserInterface +class ValidationParser implements ParserInterface { /** * @var \Symfony\Component\Validator\MetadataFactoryInterface @@ -33,26 +33,49 @@ class SymfonyValidationParser implements ParserInterface */ public function parse(array $input) { - $vparams = array(); + $params = array(); $className = $input['class']; $classdata = $this->factory->getMetadataFor($className); + $properties = $classdata->getConstrainedProperties(); - if($classdata->hasPropertyMetadata($name)) { - $propdata = $classdata->getPropertyMetadata($name); - $propdata = reset($propdata); - $constraints = $propdata->getConstraints(); + foreach($properties as $property) { + $vparams = array(); + $pds = $classdata->getPropertyMetadata($property); + foreach($pds as $propdata) { + $constraints = $propdata->getConstraints(); - foreach($constraints as $constraint) { - $vparams = $this->parseConstraint($constraint, $vparams); + foreach($constraints as $constraint) { + $vparams = $this->parseConstraint($constraint, $vparams); + } } if(isset($vparams['format'])) { $vparams['format'] = join(', ', $vparams['format']); } + + foreach(array('dataType', 'readonly', 'required') as $reqprop) { + if(!isset($vparams[$reqprop])) { + $vparams[$reqprop] = null; + } + } + + $params[$property] = $vparams; } - return $vparams; + return $params; + } + + public function postParse(array $input, $parameters) + { + foreach($parameters as $param => $data) { + if(isset($data['class']) && isset($data['children'])) { + $input = array('class' => $data['class']); + $parameters[$param]['children'] = $this->parse($input, $parameters[$param]['children']); + } + } + + return $parameters; } protected function parseConstraint(Constraint $constraint, $vparams) diff --git a/Resources/config/formatters.xml b/Resources/config/formatters.xml index 6e23df1..b9a9610 100644 --- a/Resources/config/formatters.xml +++ b/Resources/config/formatters.xml @@ -5,7 +5,7 @@ Nelmio\ApiDocBundle\Parser\FormTypeParser - Nelmio\ApiDocBundle\Parser\SymfonyValidationParser + Nelmio\ApiDocBundle\Parser\ValidationParser Nelmio\ApiDocBundle\Formatter\AbstractFormatter Nelmio\ApiDocBundle\Formatter\MarkdownFormatter Nelmio\ApiDocBundle\Formatter\SimpleFormatter @@ -19,7 +19,7 @@ - +