root/events/phpframework/akelos/trunk/lib/AkReflection.php @ 21114

Revision 21114, 14.1 kB (checked in by gen, 5 years ago)

admin plugin test

Line 
1<?php
2
3
4class AkReflection
5{
6     
7    var $definitions = array();
8    var $requires = array();
9    var $tokens;
10     
11    var $symbols;
12   
13   
14
15    function _parse($source)
16    {
17        if (!function_exists('token_get_all')) {
18            trigger_error('Function "token_get_all" is not defined');
19            return false;
20        }
21        $source = @preg_match('/<\?php.*'.$source.'.*\?>/', $source)?$source:"<?php ".$source." ?>";
22        $this->tokens = token_get_all($source);
23        $this->definitions = array();
24        reset($this->tokens);
25        $previous = array();
26        $visibility = false;
27        $static = false;
28        $byReference = false;
29        $functionIndent = '';
30        $docBlock='';
31        while ($t = current($this->tokens)) {
32           
33            if (is_array($t)) {
34                if ($t[0] == T_CLASS || (defined('T_INTERFACE')? $t[0] == T_INTERFACE:false) || $t[0] == T_FUNCTION) {
35                    $previous = array_reverse($previous);
36                    foreach($previous as $prev) {
37                        if ($prev[0] == T_STATIC) {
38                            $static = true;
39                        } else if ($prev[0] == T_STRING && in_array($prev[1],array('private','public','protected'))) {
40                            $visibility = $prev[1];
41                        } else if ($prev[0] == T_PAAMAYIM_NEKUDOTAYIM) {
42                            $byReference = true;
43                        } else if (((defined('T_DOC_COMMENT')?$prev[0] == T_DOC_COMMENT:false) || T_COMMENT) && !@preg_match('/<\?php.*/',$prev[1]) && @preg_match('/\/\*/',$prev[1])) {
44                            $docBlock = isset($prev[1])?$prev[1]:null;
45                            break;
46                        } else if (isset($prev[1]) && in_array($prev[1],array('private','public','protected'))){
47                            $visibility = $prev[1];
48                        }
49                    }
50                    $indent='';
51                    if(!empty($docBlock)) {
52                        $doclines = split("\n",$docBlock);
53                        $lastLine = $doclines[count($doclines)-1];
54                        if (preg_match('/(\s*)?\*/',$lastLine,$matches)) {
55                           
56                            $indent = substr($matches[1],0,strlen($matches[1])-1);
57
58                            $doclines[0]=$indent.$doclines[0];
59                            foreach($doclines as $idx=>$line) {
60                                $pre = '';
61                                if ($idx>0) {
62                                    $pre = ' ';
63                                }
64                                $doclines[$idx] = $pre.trim($line);
65                            }
66                            $docBlock=implode("\n",$doclines);
67                        }
68                    } else {
69                        $indent = $functionIndent;
70                    }
71                    $docBlock = str_replace('<?php','',$docBlock);
72                    $string = (!empty($docBlock)?$docBlock."\n":'').($visibility?$visibility.' ':'').($static?' static ':'');
73                    $this->readDefinition($static, $visibility, $byReference, $docBlock,$string, $indent);
74                    $previous = array();
75                    $docBlock = '';
76                    $static = false;
77                    $visibility = false;
78                    $byReference = false;
79                    $functionIndent = '';
80                    $indent = '';
81                    continue;
82                } else if ($t[0] == T_REQUIRE || $t[0] == T_REQUIRE_ONCE || $t[0] == T_INCLUDE || $t[0] == T_INCLUDE_ONCE) {
83                    if (!isset($this->requires[$t[1]])) {
84                        $this->requires[$t[1]] = array();
85                    }
86                    $org = $t;
87                    $type= $t[1];
88                    $val='';
89                    next($this->tokens);
90                    $t = current($this->tokens);
91                    while ($t != '(') {
92                        next($this->tokens);
93                        $t = current($this->tokens);
94                    }
95                    next($this->tokens);
96                    $t = current($this->tokens);
97                    while ($t != ')') {
98                        next($this->tokens);
99                        if (is_array($t)) {
100                            $val.=$t[1];
101                        } else {
102                            $val.=$t;
103                        }
104                        $t = current($this->tokens);
105                    }
106                    $this->requires[$type][]=$val;
107                    $t = $org;
108                }
109            }
110            if ($t[0] != T_WHITESPACE) {
111                $previous[] = $t;
112            } else if ($t[0] == T_WHITESPACE){
113                $functionIndent.=$t[1];
114            }
115             
116            next($this->tokens);
117        }
118        $this->definitions = array_merge($this->definitions,$this->requires);
119    }
120    function _parseTag(&$tags, $tempTag)
121    {
122        switch($tempTag[0]) {
123            case 'param':
124                if (preg_match('/\$([a-zA-Z0-9_]+)\s+(.*)/s',$tempTag[1],$pmatches)) {
125                    if (!isset($tags['params'])) {
126                        $tags['params'] = array();
127                    } else if (!is_array($tags['params'])) {
128                        $currentValue = $tags['params'];
129                        $tags['params'] = array($currentValue);
130                    }
131                    $tags['params'][$pmatches[1]] = trim($pmatches[2]);
132                } else {
133                   
134                    $tags['_unmatched_'][] = array($tempTag[0],$tempTag[1]);
135                }
136                break;
137            default:
138                if(!empty($tags[$tempTag[0]])) {
139                    if(!is_array($tags[$tempTag[0]])) {
140                       
141                        $currentValue = $tags[$tempTag[0]];
142                        $tags[$tempTag[0]] = array($currentValue);
143                    }
144                    $tags[$tempTag[0]][]=trim($tempTag[1]);
145                } else {
146                    $tags[$tempTag[0]]=trim($tempTag[1]);
147                }
148               
149        }
150    }
151    function _parseDocBlock($string)
152    {
153        preg_match_all('/\/\*\*\n(\s*\*([^\n]+?\n)+)+.*?\*\//',$string,$matches);
154        $docBlockStructure = array('comment'=>null);
155        if (isset($matches[1][0])) {
156            $docPart = $matches[1][0];
157            $docPart = preg_replace('/\s*\*\s*/',"\n",$docPart);
158            $docPart = trim($docPart);
159            $commentLines = array();
160            $tags = array('_unmatched_'=>array());
161            $docLines = split("\n",$docPart);
162            $inComment = true;
163            $tempTag=array();
164            foreach ($docLines as $line) {
165                 if (preg_match('/^@([a-zA-Z0-9_]+)\s+(.+)$/',$line, $matches)) {
166                    if (!empty($tempTag)) {
167                        $this->_parseTag(&$tags, $tempTag);
168                    }
169                    $inComment = false;
170                    $tempTag = array($matches[1],$matches[2]);
171                } else if ($inComment) {
172                    $commentLines[] = $line;
173                } else {
174                    $tempTag[1].="\n".$line;
175                }
176            }
177            if (!empty($tempTag)) {
178                $this->_parseTag(&$tags, $tempTag);
179            }
180            $docBlockStructure['comment'] = trim(implode("\n",$commentLines));
181            $docBlockStructure['tags'] = $tags;
182        }
183        return $docBlockStructure;
184    }
185   
186    function readDefinition($static = false, $visibility = 'public', $byReference = false, $docBlock = '', $string = '', $indent)
187    {
188        $t = current($this->tokens);
189        $definitionType = $t[1];
190
191        // move past the class/interface/function token
192         
193        next($this->tokens);
194        $string.=$definitionType;
195        $string.=$this->skipWhiteAndComments();
196         
197        $t = current($this->tokens);
198        if (!isset($t[1])) {
199            while(!isset($t[1])) {
200                if ($t=='&') {
201                   
202                    $string.=$t;
203                    $byReference = true;
204                    next($this->tokens);
205                    $t = current($this->tokens);
206                   
207                }
208            }
209            //$definitionType = $t[1];
210            $string.=$t[1];
211           
212        } else {
213            $string.=$t[1];
214        }
215
216        $definitionName = $t[1];
217         
218        $this->definitions[] = array(
219          'type' => $definitionType,
220          'name' => $definitionName,
221          'visibility'=>$visibility==false?(substr($definitionName,0,2)=='__')?'private':(substr($definitionName,0,1)=='_'?'protected':false):$visibility,
222          'static'=>$static,
223          'returnByReference'=>$byReference,
224          'docBlock' => $docBlock,
225          'toString' => $string
226        );
227       
228        // move past the name identifier
229        next($this->tokens);
230       
231        list($params,$block,$pre,$post) = $this->getCodeBlock();
232        $default_options = false;
233        $available_options = false;
234        if (preg_match('/\$default_options.*?=.*?(array\(.*?\)).*?;/s',$block,$default_option_matches)) {
235            $default_options_string=$default_option_matches[1];
236            $default_options_string = preg_replace_callback('/\$([A-Za-z0-9_\->])+/',array(&$this,'_replaceVariablesInsideOptions'),$default_options_string);
237           @eval('$default_options = '.$default_options_string.';');
238        }
239        if (preg_match('/\$available_options.*?=.*?(array\(.*?\)).*?;/s',$block,$available_option_matches)) {
240            $available_options_string=$available_option_matches[1];
241            $available_options_string = preg_replace_callback('/\$([A-Za-z0-9_\->])+/',array(&$this,'_replaceVariablesInsideOptions'),$available_options_string);
242           @eval('$available_options = '.$available_options_string.';');
243        }
244        $string.=$pre.$block.$post;
245        $this->definitions[count($this->definitions)-1]['code'] = $block;
246        $this->definitions[count($this->definitions)-1]['params'] = $params;
247        $this->definitions[count($this->definitions)-1]['toString'] = $string;
248        $this->definitions[count($this->definitions)-1]['default_options'] = $default_options;
249        $this->definitions[count($this->definitions)-1]['available_options'] = $available_options;
250        $strlines = split("\n",$string);
251        foreach ($strlines as $idx=>$line) {
252            $first = substr($line,0,strlen($indent));
253            if ($first == $indent) {
254                $line = substr($line,strlen($first));
255                $strlines[$idx] = $line;
256            }
257        }
258        $doclines = split("\n",$docBlock);
259        foreach ($doclines as $idx=>$line) {
260            $first = substr($line,0,strlen($indent));
261            if ($first == $indent) {
262                $line = substr($line,strlen($first));
263                $doclines[$idx] = $line;
264            }
265        }
266        $this->definitions[count($this->definitions)-1]['toString'] = implode("\n",$strlines);
267        $this->definitions[count($this->definitions)-1]['docBlock'] = implode("\n",$doclines);
268    }
269    function _replaceVariablesInsideOptions($matches)
270    {
271        $name = $matches[0];
272        return '"'.str_replace('$','\$',$name).'"';
273    }
274    function skipWhiteAndComments()
275    {
276        $string = '';
277        while ($t = current($this->tokens)) {
278            if (is_array($t) && ($t[0] == T_WHITESPACE || (defined('T_DOC_COMMENT')?$t[0] == T_DOC_COMMENT:false) || $t[0] == T_COMMENT)) {
279                next($this->tokens);
280                $string.=$t[1];
281            } else {
282                return $string;
283            }
284        }
285    }
286
287    function skipCodeBlock()
288    {
289         
290        // we go forward until we find the first "{" token
291         
292        while(($t = current($this->tokens)) && $t != '{') {
293            next($this->tokens);
294        }
295        // we're about to enter the top level block
296        // which is our class/interface/function definition body
297        $nestingLevel = 0;
298
299        // we go forward keeping the $nestingLevel up-to-date
300        // until we get out of the definition body block
301        while($t = current($this->tokens)) {
302            if ($t == '{') {
303                $nestingLevel++;
304            }
305             
306            if ($t == '}') {
307                $nestingLevel--;
308            }
309             
310            next($this->tokens);
311             
312            if ($nestingLevel == 0) return;
313        }
314    }
315    function getCodeBlock()
316    {
317        $prestring = '';
318        $poststring = '';
319        $codeblock = '';
320        // we go forward until we find the first "{" token
321        $params = array();
322        $preParam = '';
323        while(($t = current($this->tokens)) && $t != '{') {
324            if (is_array($t)) {
325                switch ($t[0]) {
326                    case T_VARIABLE:
327                        $params[]=$preParam.$t[1];
328                        $preParam = '';
329                        break;
330                }
331                $prestring.=$t[1];
332            } else if (!in_array($t,array(',','(',')'))) {
333                $preParam.=$t;
334                $prestring.=$t;
335            } else {
336                $prestring.=$t;
337            }
338            next($this->tokens);
339             
340        }
341       
342        // we're about to enter the top level block
343         
344        // which is our class/interface/function definition body
345        $nestingLevel = 0;
346         
347        // we go forward keeping the $nestingLevel up-to-date
348        // until we get out of the definition body block
349        while($t = current($this->tokens)) {
350            if ($t == '{') {
351                $nestingLevel++;
352            }
353           
354            if ($t == '}') {
355                $nestingLevel--;
356            }
357           
358            next($this->tokens);
359           
360           
361            if ($nestingLevel == 0) {
362                $poststring.=$t;
363                return array($params,$codeblock,$prestring,$poststring);
364            } else {
365                if ($t == '{' && $nestingLevel==1) {
366                    $prestring.=$t;
367                    continue;
368                }
369                $codeblock.=is_array($t)?$t[1]:$t;
370            }
371        }
372    }
373   
374    function getDefinitions()
375    {
376        return $this->definitions;
377    }
378}
379?>
Note: See TracBrowser for help on using the browser.