diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 2eb1441..5356c53 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -22,7 +22,6 @@ final class Configuration implements ConfigurationInterface $treeBuilder ->root('nelmio_api_doc') ->children() - ->scalarNode('source_folder')->defaultValue('%kernel.root_dir%/../src')->end() ->arrayNode('routes') ->info('Filter the routes that are documented') ->addDefaultsIfNotSet() diff --git a/DependencyInjection/NelmioApiDocExtension.php b/DependencyInjection/NelmioApiDocExtension.php index 458d8bd..829df1d 100644 --- a/DependencyInjection/NelmioApiDocExtension.php +++ b/DependencyInjection/NelmioApiDocExtension.php @@ -45,15 +45,10 @@ final class NelmioApiDocExtension extends Extension implements PrependExtensionI $routeCollectionBuilder->replaceArgument(0, $config['routes']['path_patterns']); // Import services needed for each library + $loader->load('swagger_php.xml'); if (class_exists(DocBlockFactory::class)) { $loader->load('php_doc.xml'); } - if (class_exists(Swagger::class)) { - $loader->load('swagger_php.xml'); - - $swaggerPHPDescriber = $container->getDefinition('nelmio_api_doc.describers.swagger_php'); - $swaggerPHPDescriber->replaceArgument(0, $config['source_folder']); - } if (interface_exists(ParamInterface::class)) { $loader->load('fos_rest.xml'); } diff --git a/Describer/SwaggerPhpDescriber.php b/Describer/SwaggerPhpDescriber.php index 5f26d1c..d016b94 100644 --- a/Describer/SwaggerPhpDescriber.php +++ b/Describer/SwaggerPhpDescriber.php @@ -14,37 +14,59 @@ namespace Nelmio\ApiDocBundle\Describer; use Nelmio\ApiDocBundle\SwaggerPhp\AddDefaults; use Nelmio\ApiDocBundle\SwaggerPhp\ModelRegister; use Nelmio\ApiDocBundle\SwaggerPhp\OperationResolver; +use Nelmio\ApiDocBundle\Util\ControllerReflector; use Swagger\Analyser; use Swagger\Analysis; +use Symfony\Component\Finder\Finder; +use Symfony\Component\Routing\RouteCollection; final class SwaggerPhpDescriber extends ExternalDocDescriber implements ModelRegistryAwareInterface { use ModelRegistryAwareTrait; - private $operationResolver; + private $routeCollection; + private $controllerReflector; - public function __construct(string $projectPath, bool $overwrite = false) + public function __construct(RouteCollection $routeCollection, ControllerReflector $controllerReflector, bool $overwrite = false) { - $nelmioNamespace = 'Nelmio\\ApiDocBundle\\'; - if (!in_array($nelmioNamespace, Analyser::$whitelist)) { - Analyser::$whitelist[] = $nelmioNamespace; - } + $this->routeCollection = $routeCollection; + $this->controllerReflector = $controllerReflector; - parent::__construct(function () use ($projectPath) { - $options = ['processors' => $this->getProcessors()]; - $annotation = \Swagger\scan($projectPath, $options); + parent::__construct(function () { + $whitelist = Analyser::$whitelist; + Analyser::$whitelist = false; + try { + $options = ['processors' => $this->getProcessors()]; + $annotation = \Swagger\scan($this->getFinder(), $options); - return json_decode(json_encode($annotation)); + return json_decode(json_encode($annotation)); + } finally { + Analyser::$whitelist = $whitelist; + } }, $overwrite); } - /** - * If set, the describer will try to complete paths and create - * implicit operations. - */ - public function setOperationResolver(OperationResolver $operationResolver) + private function getFinder() { - $this->operationResolver = $operationResolver; + $files = []; + foreach ($this->routeCollection->all() as $route) { + if (!$route->hasDefault('_controller')) { + continue; + } + + // if able to resolve the controller + $controller = $route->getDefault('_controller'); + if ($callable = $this->controllerReflector->getReflectionClassAndMethod($controller)) { + list($class, $method) = $callable; + + $files[$class->getFileName()] = true; + } + } + + $finder = new Finder(); + $finder->append(array_keys($files)); + + return $finder; } private function getProcessors(): array @@ -52,10 +74,8 @@ final class SwaggerPhpDescriber extends ExternalDocDescriber implements ModelReg $processors = [ new AddDefaults(), new ModelRegister($this->modelRegistry), + new OperationResolver($this->routeCollection, $this->controllerReflector), ]; - if (null !== $this->operationResolver) { - $processors[] = $this->operationResolver; - } return array_merge($processors, Analysis::processors()); } diff --git a/ModelDescriber/ScalarModelDescriber.php b/ModelDescriber/ScalarModelDescriber.php index 4581dcf..48a502f 100644 --- a/ModelDescriber/ScalarModelDescriber.php +++ b/ModelDescriber/ScalarModelDescriber.php @@ -21,7 +21,7 @@ class ScalarModelDescriber implements ModelDescriberInterface Type::BUILTIN_TYPE_INT => 'integer', Type::BUILTIN_TYPE_FLOAT => 'float', Type::BUILTIN_TYPE_STRING => 'string', - Type::BUILTIN_TYPE_BOOL => 'boolean' + Type::BUILTIN_TYPE_BOOL => 'boolean', ]; public function describe(Model $model, Schema $schema) diff --git a/Resources/config/swagger_php.xml b/Resources/config/swagger_php.xml index e582117..70bf245 100644 --- a/Resources/config/swagger_php.xml +++ b/Resources/config/swagger_php.xml @@ -5,31 +5,15 @@ - %kernel.root_dir% - - - + + + + + + - - - - - - - - - - - - - - - - - - diff --git a/Tests/Describer/SwaggerPhpDescriberTest.php b/Tests/Describer/SwaggerPhpDescriberTest.php deleted file mode 100644 index 0102f2e..0000000 --- a/Tests/Describer/SwaggerPhpDescriberTest.php +++ /dev/null @@ -1,34 +0,0 @@ -getSwaggerDoc(); - $info = $api->getInfo(); - - $this->assertEquals('My Awesome App', $info->getTitle()); - $this->assertEquals('1.3', $info->getVersion()); - } - - protected function setUp() - { - $this->describer = new SwaggerPhpDescriber(__DIR__.'/../Fixtures'); - $this->describer->setModelRegistry(new ModelRegistry([], new Swagger())); - } -} diff --git a/Tests/Fixtures/SwaggerPhp/Api.php b/Tests/Fixtures/SwaggerPhp/Api.php deleted file mode 100644 index b1b8efb..0000000 --- a/Tests/Fixtures/SwaggerPhp/Api.php +++ /dev/null @@ -1,24 +0,0 @@ -loadFromExtension('nelmio_api_doc', [ - 'source_folder' => '%kernel.root_dir%', 'routes' => [ 'path_patterns' => ['^/api(?!/admin)'], ], diff --git a/Util/ControllerReflector.php b/Util/ControllerReflector.php index a4b7d0f..6a6f9ff 100644 --- a/Util/ControllerReflector.php +++ b/Util/ControllerReflector.php @@ -22,6 +22,8 @@ final class ControllerReflector private $container; private $controllerNameParser; + private $controllers = []; + public function __construct(ContainerInterface $container, ControllerNameParser $controllerNameParser) { $this->container = $container; @@ -69,6 +71,10 @@ final class ControllerReflector private function getClassAndMethod(string $controller) { + if (isset($this->controllers[$controller])) { + return $this->controllers[$controller]; + } + if (false === strpos($controller, '::') && 2 === substr_count($controller, ':')) { $controller = $this->controllerNameParser->parse($controller); } @@ -98,9 +104,11 @@ final class ControllerReflector } if (!isset($class) || !isset($method)) { + $this->controllers[$controller] = null; + return; } - return [$class, $method]; + return $this->controllers[$controller] = [$class, $method]; } } diff --git a/composer.json b/composer.json index 431a599..7fc9452 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,8 @@ "php": "~7.0|~7.1", "symfony/framework-bundle": "^2.8|^3.0", "symfony/property-info": "^2.8|^3.0", - "exsyst/swagger": "~0.2.3" + "exsyst/swagger": "~0.2.3", + "zircote/swagger-php": "dev-master" }, "require-dev": { "symfony/twig-bundle": "^2.8|^3.0", @@ -34,13 +35,11 @@ "doctrine/annotations": "^1.2", "phpdocumentor/reflection-docblock": "^3.1", - "zircote/swagger-php": "^2.0", "api-platform/core": "^2.0", "friendsofsymfony/rest-bundle": "^2.0" }, "suggest": { "phpdocumentor/reflection-docblock": "For parsing php docs.", - "zircote/swagger-php": "For using swagger annotations.", "api-platform/core": "For using an API oriented framework.", "friendsofsymfony/rest-bundle": "For using the parameters annotations." },