1
+ import os
2
+ import re
3
+
4
+ DEFAULT_BASE_METHODS = {
5
+ "ApiMutableModelControllerBase" : [{
6
+ "command" : "set" ,
7
+ "parameters" : "" ,
8
+ "method" : "POST"
9
+ }, {
10
+ "command" : "get" ,
11
+ "parameters" : "" ,
12
+ "method" : "GET"
13
+ }],
14
+ "ApiMutableServiceControllerBase" : [{
15
+ "command" : "status" ,
16
+ "parameters" : "" ,
17
+ "method" : "GET"
18
+ }, {
19
+ "command" : "start" ,
20
+ "parameters" : "" ,
21
+ "method" : "POST"
22
+ }, {
23
+ "command" : "stop" ,
24
+ "parameters" : "" ,
25
+ "method" : "POST"
26
+ }, {
27
+ "command" : "restart" ,
28
+ "parameters" : "" ,
29
+ "method" : "POST"
30
+ }, {
31
+ "command" : "reconfigure" ,
32
+ "parameters" : "" ,
33
+ "method" : "POST"
34
+ }]
35
+ }
36
+
37
+
38
+ class ApiParser :
39
+ def __init__ (self , filename ):
40
+ self ._filename = filename
41
+ self .base_filename = os .path .basename (filename )
42
+ self .controller = re .sub ('(?<!^)(?=[A-Z])' , '_' , self .base_filename .split ('Controller.php' )[0 ]).lower ()
43
+ self .module_name = filename .replace ('\\ ' , '/' ).split ('/' )[- 3 ].lower ()
44
+ self ._data = open (filename ).read ()
45
+
46
+ def _get_model_filename (self ):
47
+ m = re .findall (r"\sprotected\sstatic\s\$internalModelClass\s=\s['|\"]([\w|\\]*)['|\"];" , self ._data )
48
+ if len (m ) == 0 :
49
+ m = re .findall (r"\sprotected\sstatic\s\$internalServiceClass\s=\s['|\"]([\w|\\]*)['|\"];" , self ._data )
50
+
51
+ if len (m ) > 0 :
52
+ app_location = "/" .join (self ._filename .split ('/' )[:- 5 ])
53
+ model_xml = "%s/models/%s.xml" % (app_location , m [0 ].replace ("\\ " , "/" ))
54
+ if os .path .isfile (model_xml ):
55
+ return model_xml .replace ('//' , '/' )
56
+
57
+ def _parse_action_content (self , code_block ):
58
+ record = {}
59
+ boilerplates = {
60
+ 'get' : {'pattern' : '$this->getBase' , 'method' : 'GET' },
61
+ 'del' : {'pattern' : '$this->delBase' , 'method' : 'POST' },
62
+ 'add' : {'pattern' : '$this->addBase' , 'method' : 'POST' },
63
+ 'set' : {'pattern' : '$this->setBase' , 'method' : 'POST' },
64
+ 'toggle' : {'pattern' : '$this->toggleBase' , 'method' : 'POST' },
65
+ 'search' : {'pattern' : '$this->searchBase' , 'method' : 'GET,POST' },
66
+ }
67
+ for action , boilerplate in boilerplates .items ():
68
+ pos = code_block .find (boilerplate ['pattern' ])
69
+ if pos > - 1 :
70
+ record ['method' ] = boilerplate ['method' ]
71
+
72
+ if 'method' not in record :
73
+ methods = []
74
+ if code_block .find ('request->isPost(' ) > - 1 :
75
+ methods .append ('POST' )
76
+ if code_block .find ('request->isGet(' ) > - 1 :
77
+ methods .append ('GET' )
78
+ if len (methods ) > 1 :
79
+ record ['method' ] = ',' .join (methods )
80
+
81
+ return record
82
+
83
+
84
+ def parse_api_php (self ):
85
+ data = self ._data
86
+ m = re .findall (r"\n([\w]*).*class.*Controller.*extends\s([\w|\\]*)" , data )
87
+ base_class = m [0 ][1 ].split ('\\ ' )[- 1 ] if len (m ) > 0 else None
88
+ is_abstract = len (m ) > 0 and m [0 ][0 ] == 'abstract'
89
+
90
+ model_filename = self ._get_model_filename ()
91
+
92
+ function_callouts = re .findall (r"(\n\s+(private|public|protected)\s+function\s+(\w+)\((.*)\))" , data )
93
+ result = list ()
94
+ this_commands = []
95
+ for idx , function in enumerate (function_callouts ):
96
+ begin_marker = data .find (function_callouts [idx ][0 ])
97
+ if idx + 1 < len (function_callouts ):
98
+ end_marker = data .find (function_callouts [idx + 1 ][0 ])
99
+ else :
100
+ end_marker = - 1
101
+ code_block = data [begin_marker + len (function [0 ]):end_marker ]
102
+ if function [2 ].endswith ('Action' ):
103
+ cmd = "" .join ("_" + c .lower () if c .isupper () else c for c in function [2 ][:- 6 ])
104
+ this_commands .append (cmd )
105
+ record = {
106
+ 'method' : 'GET' ,
107
+ 'module' : self .module_name ,
108
+ 'controller' : self .controller ,
109
+ 'is_abstract' : is_abstract ,
110
+ 'base_class' : base_class ,
111
+ 'command' : cmd ,
112
+ 'parameters' : function [3 ].replace (' ' , '' ).replace ('"' , '""' ),
113
+ 'filename' : self .base_filename ,
114
+ 'model_filename' : model_filename
115
+ }
116
+ if is_abstract :
117
+ record ['type' ] = 'Abstract [non-callable]'
118
+ elif self .controller .find ('service' ) > - 1 :
119
+ record ['type' ] = 'Service'
120
+ else :
121
+ record ['type' ] = 'Resources'
122
+ record .update (self ._parse_action_content (code_block ))
123
+ # find most likely method (default => GET)
124
+ result .append (record )
125
+ if base_class in DEFAULT_BASE_METHODS :
126
+ for item in DEFAULT_BASE_METHODS [base_class ]:
127
+ if item not in this_commands :
128
+ result .append ({
129
+ 'type' : 'Service' ,
130
+ 'method' : item ['method' ],
131
+ 'module' : self .module_name ,
132
+ 'controller' : self .controller ,
133
+ 'is_abstract' : False ,
134
+ 'base_class' : base_class ,
135
+ 'command' : item ['command' ],
136
+ 'parameters' : item ['parameters' ],
137
+ 'filename' : self .base_filename ,
138
+ 'model_filename' : model_filename
139
+ })
140
+
141
+ return sorted (result , key = lambda i : i ['command' ])
0 commit comments