2012-04-12 12:50:20 +02:00
|
|
|
|
<?php
|
|
|
|
|
|
2012-04-13 11:03:05 +02:00
|
|
|
|
/*
|
|
|
|
|
* This file is part of the NelmioApiDocBundle.
|
|
|
|
|
*
|
|
|
|
|
* (c) Nelmio <hello@nelm.io>
|
|
|
|
|
*
|
|
|
|
|
* For the full copyright and license information, please view the LICENSE
|
|
|
|
|
* file that was distributed with this source code.
|
|
|
|
|
*/
|
|
|
|
|
|
2012-04-12 18:37:42 +02:00
|
|
|
|
namespace Nelmio\ApiDocBundle\Extractor;
|
2012-04-12 12:50:20 +02:00
|
|
|
|
|
|
|
|
|
use Doctrine\Common\Annotations\Reader;
|
2012-04-19 16:23:26 +02:00
|
|
|
|
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
2012-07-19 17:56:49 -04:00
|
|
|
|
use Nelmio\ApiDocBundle\Parser\ParserInterface;
|
2013-07-02 21:57:09 -07:00
|
|
|
|
use Nelmio\ApiDocBundle\Parser\PostParserInterface;
|
2012-04-19 16:23:26 +02:00
|
|
|
|
use Symfony\Component\Routing\Route;
|
2012-04-12 12:50:20 +02:00
|
|
|
|
use Symfony\Component\Routing\RouterInterface;
|
2012-04-13 16:33:24 +02:00
|
|
|
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
|
|
|
|
use Symfony\Component\HttpFoundation\Request;
|
2012-08-31 14:57:42 -04:00
|
|
|
|
use Nelmio\ApiDocBundle\Util\DocCommentExtractor;
|
2012-04-12 12:50:20 +02:00
|
|
|
|
|
|
|
|
|
class ApiDocExtractor
|
|
|
|
|
{
|
2013-03-26 11:49:12 +01:00
|
|
|
|
const ANNOTATION_CLASS = 'Nelmio\\ApiDocBundle\\Annotation\\ApiDoc';
|
2012-07-18 12:23:57 +02:00
|
|
|
|
|
2012-04-13 16:33:24 +02:00
|
|
|
|
/**
|
2012-11-19 18:21:12 +01:00
|
|
|
|
* @var ContainerInterface
|
2012-04-13 16:33:24 +02:00
|
|
|
|
*/
|
2012-09-06 18:45:12 +03:00
|
|
|
|
protected $container;
|
2012-04-13 16:33:24 +02:00
|
|
|
|
|
2012-04-12 12:50:20 +02:00
|
|
|
|
/**
|
2012-11-19 18:21:12 +01:00
|
|
|
|
* @var RouterInterface
|
2012-04-12 12:50:20 +02:00
|
|
|
|
*/
|
2012-09-06 18:45:12 +03:00
|
|
|
|
protected $router;
|
2012-04-12 12:50:20 +02:00
|
|
|
|
|
|
|
|
|
/**
|
2012-11-19 18:21:12 +01:00
|
|
|
|
* @var Reader
|
2012-04-12 12:50:20 +02:00
|
|
|
|
*/
|
2012-09-06 18:45:12 +03:00
|
|
|
|
protected $reader;
|
2012-04-12 12:50:20 +02:00
|
|
|
|
|
2012-08-31 14:57:42 -04:00
|
|
|
|
/**
|
2012-11-19 18:21:12 +01:00
|
|
|
|
* @var DocCommentExtractor
|
2012-08-31 14:57:42 -04:00
|
|
|
|
*/
|
|
|
|
|
private $commentExtractor;
|
|
|
|
|
|
2012-07-20 00:58:58 +02:00
|
|
|
|
/**
|
2012-11-19 18:21:12 +01:00
|
|
|
|
* @var array ParserInterface
|
2012-07-20 00:58:58 +02:00
|
|
|
|
*/
|
2012-09-06 18:45:12 +03:00
|
|
|
|
protected $parsers = array();
|
2012-07-20 00:58:58 +02:00
|
|
|
|
|
2013-04-12 17:05:13 +02:00
|
|
|
|
/**
|
|
|
|
|
* @var array HandlerInterface
|
|
|
|
|
*/
|
|
|
|
|
protected $handlers;
|
|
|
|
|
|
2013-04-16 13:46:15 +02:00
|
|
|
|
public function __construct(ContainerInterface $container, RouterInterface $router, Reader $reader, DocCommentExtractor $commentExtractor, array $handlers)
|
2012-04-12 12:50:20 +02:00
|
|
|
|
{
|
2012-04-13 16:33:24 +02:00
|
|
|
|
$this->container = $container;
|
2012-07-20 00:58:58 +02:00
|
|
|
|
$this->router = $router;
|
|
|
|
|
$this->reader = $reader;
|
2012-08-31 14:57:42 -04:00
|
|
|
|
$this->commentExtractor = $commentExtractor;
|
2013-04-12 17:05:13 +02:00
|
|
|
|
$this->handlers = $handlers;
|
2012-04-12 12:50:20 +02:00
|
|
|
|
}
|
|
|
|
|
|
2012-09-06 18:45:12 +03:00
|
|
|
|
/**
|
|
|
|
|
* Return a list of route to inspect for ApiDoc annotation
|
|
|
|
|
* You can extend this method if you don't want all the routes
|
|
|
|
|
* to be included.
|
|
|
|
|
*
|
2013-03-18 08:40:03 +01:00
|
|
|
|
* @return Route[] An array of routes
|
2012-09-06 18:45:12 +03:00
|
|
|
|
*/
|
|
|
|
|
public function getRoutes()
|
|
|
|
|
{
|
2013-03-27 19:36:42 +01:00
|
|
|
|
return $this->router->getRouteCollection()->all();
|
2013-03-22 14:05:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Extracts annotations from all known routes
|
|
|
|
|
*
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public function all()
|
|
|
|
|
{
|
|
|
|
|
return $this->extractAnnotations($this->getRoutes());
|
2012-09-06 18:45:12 +03:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-12 17:48:21 +02:00
|
|
|
|
/**
|
|
|
|
|
* Returns an array of data where each data is an array with the following keys:
|
|
|
|
|
* - annotation
|
|
|
|
|
* - resource
|
|
|
|
|
*
|
2013-03-27 23:19:16 +01:00
|
|
|
|
* @param array $routes array of Route-objects for which the annotations should be extracted
|
2013-03-27 19:36:42 +01:00
|
|
|
|
*
|
2012-04-12 17:48:21 +02:00
|
|
|
|
* @return array
|
|
|
|
|
*/
|
2013-03-27 23:19:16 +01:00
|
|
|
|
public function extractAnnotations(array $routes)
|
2012-04-12 12:50:20 +02:00
|
|
|
|
{
|
2013-03-18 08:40:03 +01:00
|
|
|
|
$array = array();
|
2012-04-12 17:24:38 +02:00
|
|
|
|
$resources = array();
|
2012-04-12 12:50:20 +02:00
|
|
|
|
|
2013-03-22 14:05:14 +01:00
|
|
|
|
foreach ($routes as $route) {
|
2013-03-27 23:21:04 +01:00
|
|
|
|
if (!$route instanceof Route) {
|
2013-03-27 22:28:26 +01:00
|
|
|
|
throw new \InvalidArgumentException(sprintf('All elements of $routes must be instances of Route. "%s" given', gettype($route)));
|
2013-03-27 19:36:42 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($method = $this->getReflectionMethod($route->getDefault('_controller'))) {
|
2012-07-20 00:58:58 +02:00
|
|
|
|
if ($annotation = $this->reader->getMethodAnnotation($method, self::ANNOTATION_CLASS)) {
|
|
|
|
|
if ($annotation->isResource()) {
|
2013-10-11 15:44:31 +02:00
|
|
|
|
if (!($resource = $annotation->getResource())) {
|
|
|
|
|
// remove format from routes used for resource grouping
|
|
|
|
|
$resources[] = str_replace('.{_format}', '', $route->getPattern());
|
|
|
|
|
} else {
|
|
|
|
|
$resources[] = $resource;
|
|
|
|
|
}
|
2012-04-13 16:33:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-20 00:58:58 +02:00
|
|
|
|
$array[] = array('annotation' => $this->extractData($annotation, $route, $method));
|
2012-04-13 16:33:24 +02:00
|
|
|
|
}
|
2012-04-12 12:50:20 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-12 17:24:38 +02:00
|
|
|
|
rsort($resources);
|
|
|
|
|
foreach ($array as $index => $element) {
|
|
|
|
|
$hasResource = false;
|
2012-07-20 00:58:58 +02:00
|
|
|
|
$pattern = $element['annotation']->getRoute()->getPattern();
|
2012-04-12 17:24:38 +02:00
|
|
|
|
|
|
|
|
|
foreach ($resources as $resource) {
|
2013-10-11 15:44:31 +02:00
|
|
|
|
if (0 === strpos($pattern, $resource) || $resource === $element['annotation']->getResource()) {
|
2012-04-12 17:24:38 +02:00
|
|
|
|
$array[$index]['resource'] = $resource;
|
|
|
|
|
|
|
|
|
|
$hasResource = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (false === $hasResource) {
|
|
|
|
|
$array[$index]['resource'] = 'others';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-13 10:48:25 +02:00
|
|
|
|
$methodOrder = array('GET', 'POST', 'PUT', 'DELETE');
|
|
|
|
|
usort($array, function($a, $b) use ($methodOrder) {
|
2012-04-12 17:24:38 +02:00
|
|
|
|
if ($a['resource'] === $b['resource']) {
|
2012-07-20 00:58:58 +02:00
|
|
|
|
if ($a['annotation']->getRoute()->getPattern() === $b['annotation']->getRoute()->getPattern()) {
|
|
|
|
|
$methodA = array_search($a['annotation']->getRoute()->getRequirement('_method'), $methodOrder);
|
|
|
|
|
$methodB = array_search($b['annotation']->getRoute()->getRequirement('_method'), $methodOrder);
|
2012-04-13 10:48:25 +02:00
|
|
|
|
|
|
|
|
|
if ($methodA === $methodB) {
|
2012-07-20 00:58:58 +02:00
|
|
|
|
return strcmp(
|
|
|
|
|
$a['annotation']->getRoute()->getRequirement('_method'),
|
|
|
|
|
$b['annotation']->getRoute()->getRequirement('_method')
|
|
|
|
|
);
|
2012-04-13 10:48:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $methodA > $methodB ? 1 : -1;
|
2012-04-12 17:24:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-20 00:58:58 +02:00
|
|
|
|
return strcmp(
|
|
|
|
|
$a['annotation']->getRoute()->getPattern(),
|
|
|
|
|
$b['annotation']->getRoute()->getPattern()
|
|
|
|
|
);
|
2012-04-12 17:24:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return strcmp($a['resource'], $b['resource']);
|
|
|
|
|
});
|
|
|
|
|
|
2012-04-12 12:50:20 +02:00
|
|
|
|
return $array;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-13 17:56:09 +02:00
|
|
|
|
/**
|
2012-07-20 00:58:58 +02:00
|
|
|
|
* Returns the ReflectionMethod for the given controller string.
|
2012-04-13 17:56:09 +02:00
|
|
|
|
*
|
|
|
|
|
* @param string $controller
|
2012-05-23 00:20:50 +02:00
|
|
|
|
* @return \ReflectionMethod|null
|
2012-04-13 17:56:09 +02:00
|
|
|
|
*/
|
|
|
|
|
public function getReflectionMethod($controller)
|
|
|
|
|
{
|
|
|
|
|
if (preg_match('#(.+)::([\w]+)#', $controller, $matches)) {
|
|
|
|
|
$class = $matches[1];
|
|
|
|
|
$method = $matches[2];
|
|
|
|
|
} elseif (preg_match('#(.+):([\w]+)#', $controller, $matches)) {
|
|
|
|
|
$controller = $matches[1];
|
|
|
|
|
$method = $matches[2];
|
|
|
|
|
if ($this->container->has($controller)) {
|
|
|
|
|
$this->container->enterScope('request');
|
2013-05-03 16:26:16 +02:00
|
|
|
|
$this->container->set('request', new Request(), 'request');
|
2012-04-13 17:56:09 +02:00
|
|
|
|
$class = get_class($this->container->get($controller));
|
|
|
|
|
$this->container->leaveScope('request');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isset($class) && isset($method)) {
|
|
|
|
|
try {
|
|
|
|
|
return new \ReflectionMethod($class, $method);
|
|
|
|
|
} catch (\ReflectionException $e) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-12 17:48:21 +02:00
|
|
|
|
/**
|
2012-07-20 00:58:58 +02:00
|
|
|
|
* Returns an ApiDoc annotation.
|
2012-04-12 17:48:21 +02:00
|
|
|
|
*
|
|
|
|
|
* @param string $controller
|
2012-05-23 00:33:01 +02:00
|
|
|
|
* @param Route $route
|
2012-07-20 00:58:58 +02:00
|
|
|
|
* @return ApiDoc|null
|
2012-04-12 17:48:21 +02:00
|
|
|
|
*/
|
2012-04-12 12:50:20 +02:00
|
|
|
|
public function get($controller, $route)
|
|
|
|
|
{
|
2012-04-19 16:23:26 +02:00
|
|
|
|
if ($method = $this->getReflectionMethod($controller)) {
|
2012-07-20 00:58:58 +02:00
|
|
|
|
if ($annotation = $this->reader->getMethodAnnotation($method, self::ANNOTATION_CLASS)) {
|
2012-04-19 16:23:26 +02:00
|
|
|
|
if ($route = $this->router->getRouteCollection()->get($route)) {
|
2012-07-20 00:58:58 +02:00
|
|
|
|
return $this->extractData($annotation, $route, $method);
|
2012-04-19 16:23:26 +02:00
|
|
|
|
}
|
2012-04-12 12:50:20 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2012-04-13 14:42:28 +02:00
|
|
|
|
|
|
|
|
|
return null;
|
2012-04-12 12:50:20 +02:00
|
|
|
|
}
|
2012-07-23 15:44:37 -04:00
|
|
|
|
|
2012-07-19 17:56:49 -04:00
|
|
|
|
/**
|
|
|
|
|
* Registers a class parser to use for parsing input class metadata
|
|
|
|
|
*
|
2012-07-23 15:44:37 -04:00
|
|
|
|
* @param ParserInterface $parser
|
2012-07-19 17:56:49 -04:00
|
|
|
|
*/
|
2012-07-23 15:44:37 -04:00
|
|
|
|
public function addParser(ParserInterface $parser)
|
2012-07-19 17:56:49 -04:00
|
|
|
|
{
|
|
|
|
|
$this->parsers[] = $parser;
|
|
|
|
|
}
|
2012-04-19 16:23:26 +02:00
|
|
|
|
|
|
|
|
|
/**
|
2012-07-20 00:58:58 +02:00
|
|
|
|
* Returns a new ApiDoc instance with more data.
|
2012-04-19 16:23:26 +02:00
|
|
|
|
*
|
2012-05-23 00:33:01 +02:00
|
|
|
|
* @param ApiDoc $annotation
|
|
|
|
|
* @param Route $route
|
|
|
|
|
* @param \ReflectionMethod $method
|
2012-07-20 00:58:58 +02:00
|
|
|
|
* @return ApiDoc
|
2012-04-19 16:23:26 +02:00
|
|
|
|
*/
|
2012-07-20 00:58:58 +02:00
|
|
|
|
protected function extractData(ApiDoc $annotation, Route $route, \ReflectionMethod $method)
|
2012-04-19 16:23:26 +02:00
|
|
|
|
{
|
2012-07-20 00:58:58 +02:00
|
|
|
|
// create a new annotation
|
|
|
|
|
$annotation = clone $annotation;
|
2012-04-19 20:27:27 +02:00
|
|
|
|
|
2013-07-25 13:47:54 -07:00
|
|
|
|
// doc
|
|
|
|
|
$annotation->setDocumentation($this->commentExtractor->getDocCommentText($method));
|
|
|
|
|
|
2012-07-20 00:58:58 +02:00
|
|
|
|
// parse annotations
|
|
|
|
|
$this->parseAnnotations($annotation, $route, $method);
|
|
|
|
|
|
|
|
|
|
// route
|
|
|
|
|
$annotation->setRoute($route);
|
|
|
|
|
|
|
|
|
|
// description
|
2012-04-19 16:23:26 +02:00
|
|
|
|
if (null === $annotation->getDescription()) {
|
2013-07-25 13:47:54 -07:00
|
|
|
|
$comments = explode("\n", $annotation->getDocumentation());
|
2012-04-19 16:23:26 +02:00
|
|
|
|
// just set the first line
|
2012-04-19 17:17:36 +02:00
|
|
|
|
$comment = trim($comments[0]);
|
2012-07-13 15:53:24 +02:00
|
|
|
|
$comment = preg_replace("#\n+#", ' ', $comment);
|
|
|
|
|
$comment = preg_replace('#\s+#', ' ', $comment);
|
|
|
|
|
$comment = preg_replace('#[_`*]+#', '', $comment);
|
2012-04-19 17:17:36 +02:00
|
|
|
|
|
2012-04-19 20:27:27 +02:00
|
|
|
|
if ('@' !== substr($comment, 0, 1)) {
|
|
|
|
|
$annotation->setDescription($comment);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-27 12:56:19 -04:00
|
|
|
|
// input (populates 'parameters' for the formatters)
|
2012-07-23 15:44:37 -04:00
|
|
|
|
if (null !== $input = $annotation->getInput()) {
|
2012-07-19 17:56:49 -04:00
|
|
|
|
$parameters = array();
|
|
|
|
|
|
2013-03-25 12:28:00 +01:00
|
|
|
|
$normalizedInput = $this->normalizeClassParameter($input);
|
|
|
|
|
|
2013-07-02 21:57:09 -07:00
|
|
|
|
$supportedParsers = array();
|
2013-06-30 18:46:00 -07:00
|
|
|
|
$parameters = array();
|
2012-07-19 17:56:49 -04:00
|
|
|
|
foreach ($this->parsers as $parser) {
|
2013-03-25 12:28:00 +01:00
|
|
|
|
if ($parser->supports($normalizedInput)) {
|
2013-07-02 21:57:09 -07:00
|
|
|
|
$supportedParsers[] = $parser;
|
2013-06-30 18:46:00 -07:00
|
|
|
|
$parameters = $this->mergeParameters($parameters, $parser->parse($normalizedInput));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-02 21:57:09 -07:00
|
|
|
|
foreach($supportedParsers as $parser) {
|
|
|
|
|
if($parser instanceof PostParserInterface) {
|
2013-06-30 18:46:00 -07:00
|
|
|
|
$mp = $parser->postParse($normalizedInput, $parameters);
|
|
|
|
|
$parameters = $this->mergeParameters($parameters, $mp);
|
2012-07-19 17:56:49 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
2012-07-20 00:58:58 +02:00
|
|
|
|
|
2013-06-30 21:28:40 -07:00
|
|
|
|
$parameters = $this->clearClasses($parameters);
|
|
|
|
|
|
2012-07-20 00:58:58 +02:00
|
|
|
|
if ('PUT' === $method) {
|
|
|
|
|
// All parameters are optional with PUT (update)
|
|
|
|
|
array_walk($parameters, function($val, $key) use (&$data) {
|
|
|
|
|
$parameters[$key]['required'] = false;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$annotation->setParameters($parameters);
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-19 18:21:12 +01:00
|
|
|
|
// output (populates 'response' for the formatters)
|
|
|
|
|
if (null !== $output = $annotation->getOutput()) {
|
2012-08-27 12:56:19 -04:00
|
|
|
|
$response = array();
|
2012-08-27 13:25:03 -04:00
|
|
|
|
|
2013-03-25 12:28:00 +01:00
|
|
|
|
$normalizedOutput = $this->normalizeClassParameter($output);
|
|
|
|
|
|
2012-08-27 12:56:19 -04:00
|
|
|
|
foreach ($this->parsers as $parser) {
|
2013-03-25 12:28:00 +01:00
|
|
|
|
if ($parser->supports($normalizedOutput)) {
|
|
|
|
|
$response = $parser->parse($normalizedOutput);
|
2012-08-27 12:56:19 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
2013-06-30 21:28:40 -07:00
|
|
|
|
$response = $this->clearClasses($response);
|
2012-08-27 13:25:03 -04:00
|
|
|
|
|
2012-08-27 12:56:19 -04:00
|
|
|
|
$annotation->setResponse($response);
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-20 00:58:58 +02:00
|
|
|
|
// requirements
|
|
|
|
|
$requirements = array();
|
2012-08-24 18:42:54 +02:00
|
|
|
|
foreach ($route->getRequirements() as $name => $value) {
|
2012-07-20 00:58:58 +02:00
|
|
|
|
if ('_method' !== $name) {
|
|
|
|
|
$requirements[$name] = array(
|
|
|
|
|
'requirement' => $value,
|
2012-11-17 17:39:54 +01:00
|
|
|
|
'dataType' => '',
|
2012-07-20 00:58:58 +02:00
|
|
|
|
'description' => '',
|
|
|
|
|
);
|
|
|
|
|
}
|
2012-12-10 10:21:04 -08:00
|
|
|
|
if ('_scheme' == $name) {
|
|
|
|
|
$https = ('https' == $value);
|
|
|
|
|
$annotation->setHttps($https);
|
|
|
|
|
}
|
2012-07-20 00:58:58 +02:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-19 20:27:27 +02:00
|
|
|
|
$paramDocs = array();
|
2012-08-31 14:57:42 -04:00
|
|
|
|
foreach (explode("\n", $this->commentExtractor->getDocComment($method)) as $line) {
|
2012-04-19 20:27:27 +02:00
|
|
|
|
if (preg_match('{^@param (.+)}', trim($line), $matches)) {
|
|
|
|
|
$paramDocs[] = $matches[1];
|
|
|
|
|
}
|
2013-04-16 16:19:19 +01:00
|
|
|
|
if (preg_match('{^@deprecated\b(.*)}', trim($line), $matches)) {
|
|
|
|
|
$annotation->setDeprecated(true);
|
|
|
|
|
}
|
2013-08-16 19:18:05 +02:00
|
|
|
|
if (preg_match('{^@link\b(.*)}', trim($line), $matches)) {
|
|
|
|
|
$annotation->setLink($matches[1]);
|
|
|
|
|
}
|
2012-04-19 16:23:26 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-04-16 16:16:30 +01:00
|
|
|
|
$regexp = '{(\w*) *\$%s\b *(.*)}i';
|
2012-07-20 00:58:58 +02:00
|
|
|
|
foreach ($route->compile()->getVariables() as $var) {
|
|
|
|
|
$found = false;
|
|
|
|
|
foreach ($paramDocs as $paramDoc) {
|
|
|
|
|
if (preg_match(sprintf($regexp, preg_quote($var)), $paramDoc, $matches)) {
|
2012-11-17 17:39:54 +01:00
|
|
|
|
$requirements[$var]['dataType'] = isset($matches[1]) ? $matches[1] : '';
|
2012-07-20 00:58:58 +02:00
|
|
|
|
$requirements[$var]['description'] = $matches[2];
|
|
|
|
|
|
|
|
|
|
if (!isset($requirements[$var]['requirement'])) {
|
|
|
|
|
$requirements[$var]['requirement'] = '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!isset($requirements[$var]) && false === $found) {
|
2012-11-17 17:39:54 +01:00
|
|
|
|
$requirements[$var] = array('requirement' => '', 'dataType' => '', 'description' => '');
|
2012-07-20 00:58:58 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$annotation->setRequirements($requirements);
|
|
|
|
|
|
|
|
|
|
return $annotation;
|
2012-04-19 16:23:26 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-03-25 12:28:00 +01:00
|
|
|
|
protected function normalizeClassParameter($input)
|
|
|
|
|
{
|
|
|
|
|
$defaults = array(
|
|
|
|
|
'class' => '',
|
|
|
|
|
'groups' => array(),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// normalize strings
|
|
|
|
|
if (is_string($input)) {
|
|
|
|
|
$input = array('class' => $input);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// normalize groups
|
|
|
|
|
if (isset($input['groups']) && is_string($input['groups'])) {
|
|
|
|
|
$input['groups'] = array_map('trim', explode(',', $input['groups']));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return array_merge($defaults, $input);
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-02 21:57:09 -07:00
|
|
|
|
/**
|
|
|
|
|
* Merges two parameter arrays together. This logic allows documentation to be built
|
|
|
|
|
* from multiple parser passes, with later passes extending previous passes:
|
|
|
|
|
* - Boolean values are true if any parser returns true.
|
|
|
|
|
* - Requirement parameters are concatenated.
|
|
|
|
|
* - Other string values are overridden by later parsers when present.
|
|
|
|
|
* - Array parameters are recursively merged.
|
|
|
|
|
*
|
|
|
|
|
* @param array $p1 The pre-existing parameters array.
|
|
|
|
|
* @param array $p2 The newly-returned parameters array.
|
|
|
|
|
* @return array The resulting, merged array.
|
|
|
|
|
*/
|
2013-06-30 18:46:00 -07:00
|
|
|
|
protected function mergeParameters($p1, $p2)
|
|
|
|
|
{
|
2013-06-30 21:28:40 -07:00
|
|
|
|
$params = $p1;
|
2013-06-30 18:46:00 -07:00
|
|
|
|
|
|
|
|
|
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;
|
2013-07-02 21:57:09 -07:00
|
|
|
|
} elseif(in_array($name, array('requirement'))) {
|
2013-06-30 18:46:00 -07:00
|
|
|
|
if(isset($v1[$name])) {
|
|
|
|
|
$v1[$name] .= ', ' . $value;
|
|
|
|
|
} else {
|
|
|
|
|
$v1[$name] = $value;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$v1[$name] = $value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$params[$propname] = $v1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $params;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-20 00:58:58 +02:00
|
|
|
|
/**
|
|
|
|
|
* Parses annotations for a given method, and adds new information to the given ApiDoc
|
|
|
|
|
* annotation. Useful to extract information from the FOSRestBundle annotations.
|
|
|
|
|
*
|
|
|
|
|
* @param ApiDoc $annotation
|
|
|
|
|
* @param Route $route
|
|
|
|
|
* @param ReflectionMethod $method
|
|
|
|
|
*/
|
|
|
|
|
protected function parseAnnotations(ApiDoc $annotation, Route $route, \ReflectionMethod $method)
|
|
|
|
|
{
|
2013-04-12 17:05:13 +02:00
|
|
|
|
$annots = $this->reader->getMethodAnnotations($method);
|
|
|
|
|
foreach ($this->handlers as $handler) {
|
2013-04-16 13:46:15 +02:00
|
|
|
|
$handler->handle($annotation, $annots, $route, $method);
|
2012-07-20 00:58:58 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2013-06-30 21:28:40 -07:00
|
|
|
|
|
2013-07-02 21:57:09 -07:00
|
|
|
|
/**
|
|
|
|
|
* Clears the temporary 'class' parameter from the parameters array before it is returned.
|
|
|
|
|
*
|
|
|
|
|
* @param array $array The source array.
|
|
|
|
|
* @return array The cleared array.
|
|
|
|
|
*/
|
2013-06-30 21:28:40 -07:00
|
|
|
|
protected function clearClasses($array)
|
|
|
|
|
{
|
|
|
|
|
if(is_array($array)) {
|
|
|
|
|
unset($array['class']);
|
|
|
|
|
foreach($array as $name => $item) {
|
|
|
|
|
$array[$name] = $this->clearClasses($item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return $array;
|
|
|
|
|
}
|
2012-04-12 12:50:20 +02:00
|
|
|
|
}
|