Changing our coding standards slightly:
[isso.git] / docs / tools / function_finder.php
1 <?php
2 /*=====================================================================*\
3 || ###################################################################
4 || # Blue Static ISSO Framework
5 || # Copyright (c)2005-2008 Blue Static
6 || #
7 || # This program is free software; you can redistribute it and/or modify
8 || # it under the terms of the GNU General Public License as published by
9 || # the Free Software Foundation; version 2 of the License.
10 || #
11 || # This program is distributed in the hope that it will be useful, but
12 || # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 || # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 || # more details.
15 || #
16 || # You should have received a copy of the GNU General Public License along
17 || # with this program; if not, write to the Free Software Foundation, Inc.,
18 || # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19 || ###################################################################
20 \*=====================================================================*/
21
22 $path = '/Server/htdocs/sportal/';
23 $filelist = fetch_flat_listing($path);
24
25 foreach ($filelist as $filename)
26 {
27 $fpath = $path . $filename;
28 $file = file_get_contents($fpath);
29 $templist = fetch_methods($file);
30
31 $elements['methods']['classes'] = array_merge($elements['methods']['classes'], $templist['methods']['classes']);
32 $elements['methods']['functions'] = array_merge($elements['methods']['functions'], $templist['methods']['functions']);
33 $elements['limiters']["$fpath"] = $templist['limiters'];
34
35 $perfile["$fpath"] = $templist['methods'];
36 }
37
38 foreach ($elements['methods']['functions'] as $name)
39 {
40 $count['functions']["$name"] = 0;
41 }
42
43 foreach ($elements['methods']['classes'] as $name => $methods)
44 {
45 foreach ($methods as $method)
46 {
47 $count['classes']["$name"]["$method"] = 0;
48 }
49 }
50
51 foreach ($filelist as $filename)
52 {
53 $fpath = $path . $filename;
54
55 $file = file_get_contents($fpath);
56 $count = fetch_calls($file, $elements['methods'], $elements['limiters']["$fpath"], $count);
57 }
58
59 print_r($count);
60
61 // ###################################################################
62 // ###################################################################
63 // ###################################################################
64
65 function fetch_listing($path, $basepath = '', $unset = 1)
66 {
67 static $filelist;
68
69 if ($unset)
70 {
71 $filelist = array();
72 }
73
74 if (substr($path, (strlen($path) - 1), 1) != '/')
75 {
76 $path .= '/';
77 }
78
79 if ($handle = opendir($path))
80 {
81 while (($file = readdir($handle)) !== false)
82 {
83 if (substr($file, 0, 1) != '.' && $file != 'CVS')
84 {
85 if (is_dir($path . $file))
86 {
87 $filelist["$basepath"][] = "$file/";
88 fetch_listing("$path$file", "$basepath$file/", 0);
89 }
90 else
91 {
92 $filelist["$basepath"][] = $file;
93 }
94 }
95 }
96 closedir($handle);
97 }
98 return $filelist;
99 }
100
101 // ###################################################################
102
103 function fetch_flat_listing($path)
104 {
105 $filelist = fetch_listing($path);
106
107 foreach ($filelist as $basepath => $files)
108 {
109 foreach ($files as $file)
110 {
111 if (substr($file, (strlen($file) - 1), 1) != '/' && preg_match('#\.php$#', $file) && !strpos($basepath, '3rdparty') && !strpos($basepath, 'phpdoc') && !strpos($basepath, 'jpgraph'))
112 {
113 $flatlist[] = "./$basepath$file";
114 }
115 }
116 }
117 return $flatlist;
118 }
119
120 // ###################################################################
121
122 function fetch_calls($file, $methodlist, $limiters, $count)
123 {
124 static $objects, $extends;
125
126 // strip comments
127 $file = preg_replace('#/\*(.*?)\*/#s', '', $file);
128
129 // tokens
130 $tokens = token_get_all($file);
131
132 // valid tokens
133 static $validtokens, $othertokens;
134 if (!is_array($validtokens))
135 {
136 $validtokens = array('T_EXTENDS', 'T_FUNCTION', 'T_VARIABLE', 'T_STRING', 'T_NEW', 'T_DOUBLE_COLON', 'T_PAAMAYIM_NEKUDOTAYIM', 'T_OBJECT_OPERATOR');
137 $othertokens = array('(', ')', ';');
138 }
139
140 // good tokens
141 $gt = array();
142
143 // actual function calls
144 $elements = array();
145
146 // remove invalid tokens
147 $i = 0;
148 foreach ($tokens as $id => $bit)
149 {
150 unset($tokens["$id"]);
151 if (is_array($bit))
152 {
153 if (in_array($token_name = token_name($bit[0]), $validtokens))
154 {
155 $gt["$i"] = $bit;
156 $gt["$i"][2] = $token_name;
157 $gt["$i"][3] = $id;
158 $i++;
159 }
160 }
161 else
162 {
163 if (in_array($bit, $othertokens))
164 {
165 $gt["$i"][0] = '-1';
166 $gt["$i"][1] = $bit;
167 if ($bit == '(' || $bit == ')')
168 {
169 $gt["$i"][2] = 'P_' . ($bit == '(' ? 'OPEN' : 'CLOSE');
170 }
171 else
172 {
173 $gt["$i"][2] = 'SC';
174 }
175 $gt["$i"][3] = $id;
176 $i++;
177 }
178 }
179 }
180
181 // get what we're looking for
182 $triggers = array('new' => array(), 'popen' => array());
183 foreach ($methodlist['classes'] as $name => $methods)
184 {
185 $triggers['new'][] = $name;
186 $triggers['popen'] = array_merge($triggers['popen'], $methods);
187 }
188 $triggers['popen'] = array_merge($triggers['popen'], $methodlist['functions']);
189
190 // find a list of all the defined objects
191 foreach ($gt as $id => $bit)
192 {
193 $prevbit = $gt[ $id - 1 ];
194 $nextbit = $gt[ $id + 1 ];
195
196 if ($bit[2] == 'T_NEW')
197 {
198 if ($prevbit[2] == 'T_VARIABLE' && $nextbit[2] == 'T_STRING' && in_array($nextbit[1], $triggers['new']))
199 {
200 $objects["$prevbit[1]"] = $nextbit[1];
201 }
202 }
203 if ($bit[2] == 'T_EXTENDS')
204 {
205 if ($prevbit[2] == 'T_STRING' && $nextbit[2] == 'T_STRING' && in_array($prevbit[1], $triggers['new']) && in_array($prevbit[1], $triggers['new']))
206 {
207 $extends["$prevbit[1]"] = $nextbit[1];
208 }
209 }
210 }
211
212 // process tokens
213 $incall = false;
214 $root = 0;
215 $stack = array();
216 $functlist = array();
217 foreach ($gt as $id => $bit)
218 {
219 $token = $bit[2];
220 $prevbit = $gt[ $id - 1 ];
221 $nextbit = $gt[ $id + 1 ];
222
223 // handle object calls
224 if ($token == 'T_DOUBLE_COLON' || $token == 'T_PAAMAYIM_NEKUDOTAYIM' || $token == 'T_OBJECT_OPERATOR')
225 {
226 $classname = null;
227
228 // find classname
229 if (isset($objects["$prevbit[1]"]))
230 {
231 $classname = $objects["$prevbit[1]"];
232 }
233 else if (in_array($prevbit[1], $triggers['new']))
234 {
235 $classname = $prevbit[1];
236 }
237 // we've got an extension!
238 else if ($prevbit[1] == 'parent')
239 {
240 // find a class that the call could be in
241 foreach ($limiters as $class => $limits)
242 {
243 if ($bit[3] > $limits[0] && $bit[3] < $limits[1])
244 {
245 $refclass = $class;
246 }
247 }
248
249 // get the parent of that object
250 if (isset($methodlist['classes']["$refclass"]))
251 {
252 $tempclassname = $extends["$refclass"];
253 if (isset($methodlist['classes']["$tempclassname"]))
254 {
255 $classname = $tempclassname;
256 }
257 }
258 }
259 // we've got an inner-object call
260 else if ($prevbit[1] == '$this')
261 {
262 // find a class that the call could be in
263 foreach ($limiters as $class => $limits)
264 {
265 if ($bit[3] > $limits[0] && $bit[3] < $limits[1])
266 {
267 $refclass = $class;
268 }
269 }
270
271 if (isset($methodlist['classes']["$refclass"]))
272 {
273 $classname = $refclass;
274 }
275 }
276
277 // object name validation
278 if ($classname)
279 {
280 // method call validation
281 if (in_array($nextbit[1], $methodlist['classes']["$classname"]))
282 {
283 $count['classes']["$classname"]["$nextbit[1]"]++;
284 }
285 }
286 }
287 // initial
288 else if ($token == 'T_STRING' && $nextbit[2] == 'P_OPEN' && $prevbit[2] != 'T_FUNCTION' && $prevbit[2] != 'T_DOUBLE_COLON' && $prevbit[2] != 'T_PAAMAYIM_NEKUDOTAYIM' && $prevbit[2] != 'T_OBJECT_OPERATOR' && !in_array($bit[1], $triggers['new']))
289 {
290 $incall = true;
291 $root = $id;
292 $functlist["$id"] = $bit[1];
293 }
294 // parens in call
295 else if ($token == 'P_OPEN' && $incall)
296 {
297 array_push($stack, $id);
298 }
299 // close parens in call
300 else if ($token == 'P_CLOSE' && $incall)
301 {
302 // find the most recent function
303 $end = array_pop($stack) - 1;
304
305 // see if we can increment a function
306 if (in_array($functlist["$end"], $methodlist['functions']))
307 {
308 $count['functions'][ $functlist["$end"] ]++;
309 }
310
311 // we're done with all functions and the semi colon is next
312 if (count($stack) == 0 && $nextbit[2] == 'SC')
313 {
314 $incall = false;
315 }
316 }
317 }
318
319 return $count;
320 }
321
322 // ###################################################################
323
324 function fetch_methods($file)
325 {
326 // strip comments
327 $file = preg_replace('#/\*(.*?)\*/#s', '', $file);
328
329 // tokens
330 $tokens = token_get_all($file);
331
332 // valid tokens
333 static $validtokens, $othertokensopen, $othertokensclose, $othertokens;
334 if (!is_array($validtokens))
335 {
336 $validtokens = array('T_CLASS', 'T_FUNCTION', 'T_STRING');
337 $othertokensopen = array('{', '${', 'T_CURLY_OPEN', 'T_DOLLAR_OPEN_CURLY_BRACES');
338 $othertokensclose = array('}');
339 $othertokens = array_merge($othertokensopen, $othertokensclose);
340 }
341
342 // good tokens
343 $gt = array();
344
345 // actual named items
346 $elements = array();
347
348 // remove invalid tokens
349 $i = 0;
350 foreach ($tokens as $id => $bit)
351 {
352 unset($tokens["$id"]);
353 if (is_array($bit))
354 {
355 // if we have a T_CLASS, T_FUNCTION, or T_STRING
356 if (in_array($token_name = token_name($bit[0]), $validtokens))
357 {
358 $gt["$i"] = $bit;
359 $gt["$i"][2] = $token_name;
360 $gt["$i"][3] = $id;
361 $i++;
362 }
363 // special open {
364 else if (in_array($token_name = token_name($bit[0]), $othertokens))
365 {
366 $gt["$i"][0] = '-1';
367 $gt["$i"][1] = $bit[1];
368 $gt["$i"][2] = 'B_' . (in_array($token_name, $othertokensopen) ? 'OPEN' : 'CLOSE');
369 $gt["$i"][3] = $id;
370 $i++;
371 }
372 }
373 // if we have an opening or closing brace
374 else if (in_array($bit, $othertokens))
375 {
376 // make up some token information
377 $gt["$i"][0] = '-1';
378 $gt["$i"][1] = $bit;
379 $gt["$i"][2] = 'B_' . (in_array($bit, $othertokensopen) ? 'OPEN' : 'CLOSE');
380 $gt["$i"][3] = $id;
381 $i++;
382 }
383 }
384
385 // process tokens
386 $elements = array('classes' => array(), 'functions' => array());
387
388 $inclass = false;
389 $infunction = false;
390 $class = '';
391 $stack = array();
392 foreach ($gt as $id => $bit)
393 {
394 $token = $bit[2];
395 $nextbit = $gt[ $id + 1 ];
396
397 if ($token == 'T_CLASS')
398 {
399 $inclass = true;
400 $class = $nextbit[1];
401
402 $elements['classes']["$class"] = array();
403
404 $limiters["$class"][0] = $bit[3];
405 }
406 else if ($token == 'T_FUNCTION')
407 {
408 $infunction = true;
409
410 if ($inclass)
411 {
412 $elements['classes']["$class"][] = $nextbit[1];
413 }
414 else
415 {
416 $elements['functions'][] = $nextbit[1];
417 }
418 }
419 else if ($token == 'B_OPEN')
420 {
421 array_push($stack, $id);
422 }
423 else if ($token == 'B_CLOSE')
424 {
425 array_pop($stack);
426
427 // breaking out of a method
428 if ($inclass && $infunction)
429 {
430 if (count($stack) < 2)
431 {
432 $infunction = false;
433 }
434 }
435 // breaking out of a class
436 else if ($inclass && !$infunction)
437 {
438 if (count($stack) < 1)
439 {
440 $inclass = false;
441 $limiters["$class"][1] = $bit[3];
442 }
443 }
444 // breaking out of a function
445 else if (!$inclass && $infunction)
446 {
447 if (count($stack) == 0)
448 {
449 $infunction = false;
450 }
451 }
452 }
453 $lastbit = $bit;
454 }
455
456 return array('methods' => $elements, 'limiters' => $limiters);
457 }
458
459 ?>