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;
|
|
|
|
|
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-04-12 12:50:20 +02:00
|
|
|
|
|
|
|
|
|
class ApiDocExtractor
|
|
|
|
|
{
|
2012-04-12 18:37:42 +02:00
|
|
|
|
const ANNOTATION_CLASS = 'Nelmio\\ApiDocBundle\\Annotation\\ApiDoc';
|
2012-04-12 12:50:20 +02:00
|
|
|
|
|
2012-04-13 16:33:24 +02:00
|
|
|
|
/**
|
|
|
|
|
* @var \Symfony\Component\DependencyInjection\ContainerInterface
|
|
|
|
|
*/
|
|
|
|
|
private $container;
|
|
|
|
|
|
2012-04-12 12:50:20 +02:00
|
|
|
|
/**
|
2012-04-12 17:48:21 +02:00
|
|
|
|
* @var \ymfony\Component\Routing\RouterInterface
|
2012-04-12 12:50:20 +02:00
|
|
|
|
*/
|
|
|
|
|
private $router;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @var \Doctrine\Common\Annotations\Reader
|
|
|
|
|
*/
|
|
|
|
|
private $reader;
|
|
|
|
|
|
2012-04-13 16:33:24 +02:00
|
|
|
|
public function __construct(ContainerInterface $container, RouterInterface $router, Reader $reader)
|
2012-04-12 12:50:20 +02:00
|
|
|
|
{
|
2012-04-13 16:33:24 +02:00
|
|
|
|
$this->container = $container;
|
2012-04-12 12:50:20 +02:00
|
|
|
|
$this->router = $router;
|
|
|
|
|
$this->reader = $reader;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-12 17:48:21 +02:00
|
|
|
|
/**
|
|
|
|
|
* Returns an array of data where each data is an array with the following keys:
|
|
|
|
|
* - annotation
|
|
|
|
|
* - route
|
|
|
|
|
* - resource
|
|
|
|
|
*
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
2012-04-12 12:50:20 +02:00
|
|
|
|
public function all()
|
|
|
|
|
{
|
|
|
|
|
$array = array();
|
2012-04-12 17:24:38 +02:00
|
|
|
|
$resources = array();
|
2012-04-12 12:50:20 +02:00
|
|
|
|
|
2012-04-13 16:33:24 +02:00
|
|
|
|
foreach ($this->router->getRouteCollection()->all() as $route) {
|
2012-04-19 16:23:26 +02:00
|
|
|
|
if ($method = $this->getReflectionMethod($route->getDefault('_controller'))) {
|
|
|
|
|
if ($annot = $this->reader->getMethodAnnotation($method, self::ANNOTATION_CLASS)) {
|
2012-04-13 16:33:24 +02:00
|
|
|
|
if ($annot->isResource()) {
|
|
|
|
|
$resources[] = $route->getPattern();
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-19 16:23:26 +02:00
|
|
|
|
$array[] = $this->getData($annot, $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;
|
|
|
|
|
$pattern = $element['route']->getPattern();
|
|
|
|
|
|
|
|
|
|
foreach ($resources as $resource) {
|
|
|
|
|
if (0 === strpos($pattern, $resource)) {
|
|
|
|
|
$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']) {
|
|
|
|
|
if ($a['route']->getPattern() === $b['route']->getPattern()) {
|
2012-04-13 10:48:25 +02:00
|
|
|
|
$methodA = array_search($a['route']->getRequirement('_method'), $methodOrder);
|
|
|
|
|
$methodB = array_search($b['route']->getRequirement('_method'), $methodOrder);
|
|
|
|
|
|
|
|
|
|
if ($methodA === $methodB) {
|
|
|
|
|
return strcmp($a['route']->getRequirement('_method'), $b['route']->getRequirement('_method'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $methodA > $methodB ? 1 : -1;
|
2012-04-12 17:24:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return strcmp($a['route']->getPattern(), $b['route']->getPattern());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return strcmp($a['resource'], $b['resource']);
|
|
|
|
|
});
|
|
|
|
|
|
2012-04-12 12:50:20 +02:00
|
|
|
|
return $array;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-13 17:56:09 +02:00
|
|
|
|
/**
|
|
|
|
|
* Returns the ReflectionMethod for the given controller string
|
|
|
|
|
*
|
|
|
|
|
* @param string $controller
|
|
|
|
|
* @return ReflectionMethod|null
|
|
|
|
|
*/
|
|
|
|
|
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');
|
|
|
|
|
$this->container->set('request', new Request);
|
|
|
|
|
$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
|
|
|
|
/**
|
|
|
|
|
* Returns an array containing two values with the following keys:
|
|
|
|
|
* - annotation
|
|
|
|
|
* - route
|
|
|
|
|
*
|
|
|
|
|
* @param string $controller
|
|
|
|
|
* @param Route $route
|
|
|
|
|
* @return array|null
|
|
|
|
|
*/
|
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)) {
|
|
|
|
|
if ($annot = $this->reader->getMethodAnnotation($method, self::ANNOTATION_CLASS)) {
|
|
|
|
|
if ($route = $this->router->getRouteCollection()->get($route)) {
|
|
|
|
|
return $this->getData($annot, $route, $method);
|
|
|
|
|
}
|
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-04-19 16:23:26 +02:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Allows to add more data to the ApiDoc object, and
|
|
|
|
|
* returns an array containing the following keys:
|
|
|
|
|
* - annotation
|
|
|
|
|
* - route
|
|
|
|
|
*
|
|
|
|
|
* @param ApiDoc $annotation
|
|
|
|
|
* @param Route $route
|
|
|
|
|
* @param ReflectionMethod $method
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
protected function getData(ApiDoc $annotation, Route $route, \ReflectionMethod $method)
|
|
|
|
|
{
|
|
|
|
|
if (null === $annotation->getDescription()) {
|
|
|
|
|
$comments = explode('\n', $this->getDocComment($method));
|
|
|
|
|
// just set the first line
|
|
|
|
|
$annotation->setDescription($comments[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return array('annotation' => $annotation, 'route' => $route);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function getDocComment(\Reflector $reflected)
|
|
|
|
|
{
|
|
|
|
|
$comment = $reflected->getDocComment();
|
|
|
|
|
|
|
|
|
|
// let's clean the doc block
|
|
|
|
|
$comment = str_replace('/**', '', $comment);
|
|
|
|
|
$comment = str_replace('*/', '', $comment);
|
|
|
|
|
$comment = str_replace('*', '', $comment);
|
|
|
|
|
$comment = str_replace("\r", '', trim($comment));
|
|
|
|
|
$comment = preg_replace("#\n[ \t]+#i", "\n", trim($comment));
|
|
|
|
|
$comment = str_replace("\n", "\\n", trim($comment));
|
|
|
|
|
$comment = preg_replace("#[\t ]+#i", ' ', trim($comment));
|
|
|
|
|
$comment = str_replace("\"", "\\\"", $comment);
|
|
|
|
|
|
|
|
|
|
return $comment;
|
|
|
|
|
}
|
2012-04-12 12:50:20 +02:00
|
|
|
|
}
|