66use App \Model \Repository \AssignmentSolutions ;
77use App \Model \Entity \Group ;
88use App \Model \Entity \User ;
9+ use App \V1Module \Router \MethodRoute ;
10+ use Nette \Routing \RouteList ;
911use Symfony \Component \Console \Command \Command ;
1012use Symfony \Component \Console \Input \InputArgument ;
1113use Symfony \Component \Console \Input \InputInterface ;
@@ -26,12 +28,85 @@ protected function configure(): void
2628
2729 protected function execute (InputInterface $ input , OutputInterface $ output ): int
2830 {
29- $ r = new AnnotationHelper ('App\V1Module\Presenters\UsersPresenter ' );
30- $ data = $ r ->extractMethodData ('actionUpdateUiData ' );
31- var_dump ($ data );
31+ $ namespacePrefix = 'App\V1Module\Presenters \\' ;
32+
33+ $ routes = $ this ->getRoutes ();
34+ foreach ($ routes as $ route ) {
35+ $ metadata = $ this ->extractMetadata ($ route );
36+ $ route = $ this ->extractRoute ($ route );
37+
38+ $ className = $ namespacePrefix . $ metadata ['class ' ];
39+ $ annotationData = AnnotationHelper::extractAnnotationData ($ className , $ metadata ['method ' ]);
40+ }
3241
3342 return Command::SUCCESS ;
3443 }
44+
45+ function getRoutes (): array {
46+ $ router = \App \V1Module \RouterFactory::createRouter ();
47+
48+ # find all route object using a queue
49+ $ queue = [$ router ];
50+ $ routes = [];
51+ while (count ($ queue ) != 0 ) {
52+ $ cursor = array_shift ($ queue );
53+
54+ if ($ cursor instanceof RouteList) {
55+ foreach ($ cursor ->getRouters () as $ item ) {
56+ # lists contain routes or nested lists
57+ if ($ item instanceof RouteList) {
58+ array_push ($ queue , $ item );
59+ }
60+ else {
61+ # the first route is special and holds no useful information for annotation
62+ if (get_parent_class ($ item ) !== MethodRoute::class)
63+ continue ;
64+
65+ $ routes [] = $ this ->getPropertyValue ($ item , "route " );
66+ }
67+ }
68+ }
69+ }
70+
71+ return $ routes ;
72+ }
73+
74+ private function extractRoute ($ routeObj ) {
75+ $ mask = self ::getPropertyValue ($ routeObj , "mask " );
76+ return $ mask ;
77+ }
78+
79+ private function extractMetadata ($ routeObj ) {
80+ $ metadata = self ::getPropertyValue ($ routeObj , "metadata " );
81+ $ presenter = $ metadata ["presenter " ]["value " ];
82+ $ action = $ metadata ["action " ]["value " ];
83+
84+ # if the name is empty, the method will be called 'actionDefault'
85+ if ($ action === null )
86+ $ action = "default " ;
87+
88+ return [
89+ "class " => $ presenter . "Presenter " ,
90+ "method " => "action " . ucfirst ($ action ),
91+ ];
92+ }
93+
94+ private static function getPropertyValue ($ object , string $ propertyName ): mixed
95+ {
96+ $ class = new \ReflectionClass ($ object );
97+
98+ do {
99+ try {
100+ $ property = $ class ->getProperty ($ propertyName );
101+ } catch (\ReflectionException $ exception ) {
102+ $ class = $ class ->getParentClass ();
103+ $ property = null ;
104+ }
105+ } while ($ property === null && $ class !== null );
106+
107+ $ property ->setAccessible (true );
108+ return $ property ->getValue ($ object );
109+ }
35110}
36111
37112enum HttpMethods: string {
@@ -58,14 +133,14 @@ public function __construct(
58133}
59134
60135class AnnotationParameterData {
61- public string $ dataType ;
136+ public string | null $ dataType ;
62137 public string $ name ;
63- public string $ description ;
138+ public string | null $ description ;
64139
65140 public function __construct (
66- string $ dataType ,
141+ string | null $ dataType ,
67142 string $ name ,
68- string $ description
143+ string | null $ description
69144 ) {
70145 $ this ->dataType = $ dataType ;
71146 $ this ->name = $ name ;
@@ -74,25 +149,12 @@ public function __construct(
74149}
75150
76151class AnnotationHelper {
77- private string $ className ;
78- private \ReflectionClass $ class ;
79-
80- /**
81- * Constructor
82- * @param string $className Name of the class.
83- */
84- public function __construct (
85- string $ className
86- ) {
87- $ this ->className = $ className ;
88- $ this ->class = new \ReflectionClass ($ this ->className );
152+ private static function getMethod (string $ className , string $ methodName ): \ReflectionMethod {
153+ $ class = new \ReflectionClass ($ className );
154+ return $ class ->getMethod ($ methodName );
89155 }
90156
91- public function getMethod (string $ methodName ): \ReflectionMethod {
92- return $ this ->class ->getMethod ($ methodName );
93- }
94-
95- function extractAnnotationHttpMethod (array $ annotations ): HttpMethods |null {
157+ private static function extractAnnotationHttpMethod (array $ annotations ): HttpMethods |null {
96158 # get string values of backed enumeration
97159 $ cases = HttpMethods::cases ();
98160 $ methods = [];
@@ -110,7 +172,7 @@ function extractAnnotationHttpMethod(array $annotations): HttpMethods|null {
110172 return null ;
111173 }
112174
113- function extractAnnotationQueryParams (array $ annotations ): array {
175+ private static function extractAnnotationQueryParams (array $ annotations ): array {
114176 $ queryParams = [];
115177 foreach ($ annotations as $ annotation ) {
116178 # assumed that all query parameters have a @param annotation
@@ -128,7 +190,7 @@ function extractAnnotationQueryParams(array $annotations): array {
128190 return $ queryParams ;
129191 }
130192
131- function extractBodyParams (array $ expressions ): array {
193+ private static function extractBodyParams (array $ expressions ): array {
132194 $ dict = [];
133195 #sample: [ name="uiData", validation="array|null" ]
134196 foreach ($ expressions as $ expression ) {
@@ -141,7 +203,7 @@ function extractBodyParams(array $expressions): array {
141203 return $ dict ;
142204 }
143205
144- function extractAnnotationBodyParams (array $ annotations ): array {
206+ private static function extractAnnotationBodyParams (array $ annotations ): array {
145207 $ bodyParams = [];
146208 $ prefix = "@Param " ;
147209 foreach ($ annotations as $ annotation ) {
@@ -151,7 +213,7 @@ function extractAnnotationBodyParams(array $annotations): array {
151213 # remove '@Param(' from the start and ')' from the end
152214 $ body = substr ($ annotation , strlen ($ prefix ) + 1 , -1 );
153215 $ tokens = explode (", " , $ body );
154- $ values = $ this -> extractBodyParams ($ tokens );
216+ $ values = self :: extractBodyParams ($ tokens );
155217 $ descriptor = new AnnotationParameterData ($ values ["validation " ],
156218 $ values ["name " ], $ values ["description " ]);
157219 $ bodyParams [] = $ descriptor ;
@@ -160,8 +222,8 @@ function extractAnnotationBodyParams(array $annotations): array {
160222 return $ bodyParams ;
161223 }
162224
163- function getMethodAnnotations (string $ methodName ): array {
164- $ annotations = $ this -> getMethod ($ methodName )->getDocComment ();
225+ private static function getMethodAnnotations (string $ className , string $ methodName ): array {
226+ $ annotations = self :: getMethod ($ className , $ methodName )->getDocComment ();
165227 $ lines = preg_split ("/ \r\n| \n| \r/ " , $ annotations );
166228
167229 # trims whitespace and asterisks
@@ -196,11 +258,12 @@ function getMethodAnnotations(string $methodName): array {
196258 return $ merged ;
197259 }
198260
199- public function extractMethodData ($ methodName ): AnnotationData {
200- $ methodAnnotations = $ this ->getMethodAnnotations ($ methodName );
201- $ httpMethod = $ this ->extractAnnotationHttpMethod ($ methodAnnotations );
202- $ queryParams = $ this ->extractAnnotationQueryParams ($ methodAnnotations );
203- $ bodyParams = $ this ->extractAnnotationBodyParams ($ methodAnnotations );
261+ public static function extractAnnotationData (string $ className , string $ methodName ): AnnotationData {
262+ $ methodAnnotations = self ::getMethodAnnotations ($ className , $ methodName );
263+
264+ $ httpMethod = self ::extractAnnotationHttpMethod ($ methodAnnotations );
265+ $ queryParams = self ::extractAnnotationQueryParams ($ methodAnnotations );
266+ $ bodyParams = self ::extractAnnotationBodyParams ($ methodAnnotations );
204267 $ data = new AnnotationData ($ httpMethod , $ queryParams , $ bodyParams );
205268 return $ data ;
206269 }
0 commit comments