22
33namespace App \Console ;
44
5- use App \Helpers \Swagger \FileBuilder ;
5+ use App \Helpers \Swagger \TempAnnotationFileBuilder ;
66use App \Helpers \Swagger \AnnotationHelper ;
77use App \V1Module \Router \MethodRoute ;
88use Nette \Routing \RouteList ;
99use Symfony \Component \Console \Command \Command ;
1010use Symfony \Component \Console \Input \InputInterface ;
1111use Symfony \Component \Console \Output \OutputInterface ;
12+ use Exception ;
13+ use ReflectionException ;
14+ use ReflectionClass ;
1215
1316class SwaggerAnnotator extends Command
1417{
@@ -18,59 +21,68 @@ class SwaggerAnnotator extends Command
1821
1922 protected function configure (): void
2023 {
24+ $ filePath = self ::$ autogeneratedAnnotationFilePath ;
2125 $ this ->setName (self ::$ defaultName )->setDescription (
22- 'Annotate all methods with Swagger PHP annotations. '
26+ "Extracts endpoint method annotations and puts them into a temporary file that can be used to generate "
27+ . " an OpenAPI documentation. The file is located at {$ filePath }"
2328 );
2429 }
2530
2631 protected function execute (InputInterface $ input , OutputInterface $ output ): int
2732 {
28- # create a temporary file containing transpiled annotations usable by the external library (Swagger-PHP)
29- $ fileBuilder = new FileBuilder (self ::$ autogeneratedAnnotationFilePath );
30- $ fileBuilder ->startClass ('__Autogenerated_Annotation_Controller__ ' , '1.0 ' , 'ReCodEx API ' );
31-
32- # get all routes of the api
33- $ routes = $ this ->getRoutes ();
34- foreach ($ routes as $ routeObj ) {
35- # extract class and method names of the endpoint
36- $ metadata = $ this ->extractMetadata ($ routeObj );
37- $ route = $ this ->extractRoute ($ routeObj );
38- $ className = self ::$ presenterNamespace . $ metadata ['class ' ];
39-
40- # extract data from the existing annotations
41- $ annotationData = AnnotationHelper::extractAnnotationData ($ className , $ metadata ['method ' ], $ route );
42-
43- # add an empty method to the file with the transpiled annotations
44- $ fileBuilder ->addAnnotatedMethod ($ metadata ['method ' ], $ annotationData ->toSwaggerAnnotations ($ route ));
45- }
46- $ fileBuilder ->endClass ();
33+ try {
34+ // create a temporary file containing transpiled annotations usable by the external library (Swagger-PHP)
35+ $ fileBuilder = new TempAnnotationFileBuilder (self ::$ autogeneratedAnnotationFilePath );
36+ $ fileBuilder ->startClass ('__Autogenerated_Annotation_Controller__ ' , '1.0 ' , 'ReCodEx API ' );
37+
38+ // get all routes of the api
39+ $ routes = $ this ->getRoutes ();
40+ foreach ($ routes as $ routeObj ) {
41+ // extract class and method names of the endpoint
42+ $ metadata = $ this ->extractMetadata ($ routeObj );
43+ $ route = $ this ->extractRoute ($ routeObj );
44+ $ className = self ::$ presenterNamespace . $ metadata ['class ' ];
45+
46+ // extract data from the existing annotations
47+ $ annotationData = AnnotationHelper::extractAnnotationData ($ className , $ metadata ['method ' ], $ route );
48+
49+ // add an empty method to the file with the transpiled annotations
50+ $ fileBuilder ->addAnnotatedMethod ($ metadata ['method ' ], $ annotationData ->toSwaggerAnnotations ($ route ));
51+ }
52+ $ fileBuilder ->endClass ();
53+
54+ return Command::SUCCESS ;
55+ } catch (Exception $ e ) {
56+ $ output ->writeln ("Error in SwaggerAnnotator: {$ e ->getMessage ()}" );
4757
48- return Command::SUCCESS ;
58+ return Command::FAILURE ;
59+ }
4960 }
5061
5162 /**
5263 * Finds all route objects of the API
5364 * @return array Returns an array of all found route objects.
5465 */
55- function getRoutes (): array {
66+ private function getRoutes (): array
67+ {
5668 $ router = \App \V1Module \RouterFactory::createRouter ();
5769
58- # find all route object using a queue
70+ // find all route object using a queue
5971 $ queue = [$ router ];
6072 $ routes = [];
6173 while (count ($ queue ) != 0 ) {
6274 $ cursor = array_shift ($ queue );
6375
6476 if ($ cursor instanceof RouteList) {
6577 foreach ($ cursor ->getRouters () as $ item ) {
66- # lists contain routes or nested lists
78+ // lists contain routes or nested lists
6779 if ($ item instanceof RouteList) {
6880 array_push ($ queue , $ item );
69- }
70- else {
71- # the first route is special and holds no useful information for annotation
72- if (get_parent_class ($ item ) !== MethodRoute::class)
81+ } else {
82+ // the first route is special and holds no useful information for annotation
83+ if (get_parent_class ($ item ) !== MethodRoute::class) {
7384 continue ;
85+ }
7486
7587 $ routes [] = $ this ->getPropertyValue ($ item , "route " );
7688 }
@@ -85,10 +97,11 @@ function getRoutes(): array {
8597 * Extracts the route string from a route object. Replaces '<..>' in the route with '{...}'.
8698 * @param mixed $routeObj
8799 */
88- private function extractRoute ($ routeObj ): string {
100+ private function extractRoute ($ routeObj ): string
101+ {
89102 $ mask = self ::getPropertyValue ($ routeObj , "mask " );
90103
91- # sample: replaces '/users/<id>' with '/users/{id}'
104+ // sample: replaces '/users/<id>' with '/users/{id}'
92105 $ mask = str_replace (["< " , "> " ], ["{ " , "} " ], $ mask );
93106 return "/ " . $ mask ;
94107 }
@@ -98,14 +111,16 @@ private function extractRoute($routeObj): string {
98111 * @param mixed $routeObj The route object representing the endpoint.
99112 * @return string[] Returns a dictionary [ "class" => ..., "method" => ...]
100113 */
101- private function extractMetadata ($ routeObj ) {
114+ private function extractMetadata ($ routeObj )
115+ {
102116 $ metadata = self ::getPropertyValue ($ routeObj , "metadata " );
103117 $ presenter = $ metadata ["presenter " ]["value " ];
104118 $ action = $ metadata ["action " ]["value " ];
105119
106- # if the name is empty, the method will be called 'actionDefault'
107- if ($ action === null )
120+ // if the name is empty, the method will be called 'actionDefault'
121+ if ($ action === null ) {
108122 $ action = "default " ;
123+ }
109124
110125 return [
111126 "class " => $ presenter . "Presenter " ,
@@ -122,12 +137,12 @@ private function extractMetadata($routeObj) {
122137 */
123138 private static function getPropertyValue ($ object , string $ propertyName ): mixed
124139 {
125- $ class = new \ ReflectionClass ($ object );
140+ $ class = new ReflectionClass ($ object );
126141
127142 do {
128143 try {
129144 $ property = $ class ->getProperty ($ propertyName );
130- } catch (\ ReflectionException $ exception ) {
145+ } catch (ReflectionException $ exception ) {
131146 $ class = $ class ->getParentClass ();
132147 $ property = null ;
133148 }
0 commit comments