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

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

admin plugin test

Line 
1<?php
2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
4// +----------------------------------------------------------------------+
5// | Akelos Framework - http://www.akelos.org                             |
6// +----------------------------------------------------------------------+
7// | Copyright (c) 2002-2006, Akelos Media, S.L.  & Bermi Ferrer Martinez |
8// | Released under the GNU Lesser General Public License, see LICENSE.txt|
9// +----------------------------------------------------------------------+
10
11require_once(AK_VENDOR_DIR.DS.'pear'.DS.'PHP'.DS.'Compat'.DS.'Constant'.DS.'T.php');
12
13/**
14* @todo Avoid the ussage of globals in the PHP parser
15*/
16
17/**
18 * This is a modified version of the pear/PHP_Shell package by Jan Kneschke
19 * and is used for validating PHP in the interactive shell
20 * before terminating execution with fatal errors.
21 *
22 * @package ActiveSupport
23 * @subpackage Console
24 * @author Jan Kneschke <jan@kneschke.de>
25 * @author Bermi Ferrer <bermi a.t akelos c.om>
26 * @copyright Copyright (c) 2002-2006, Akelos Media, S.L. http://www.akelos.org
27 * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html>
28 */
29class AkPhpParser
30{
31    var $errors = array();
32    var $code = '';
33
34    function AkPhpParser($code)
35    {
36        $this->code = trim($code);
37    }
38    /**
39    * parse the PHP code
40    *
41    * we parse before we eval() the code to
42    * - fetch fatal errors before they come up
43    * - know about where we have to wait for closing braces
44    *
45    * @return int 0 if a executable statement is in the code-buffer, non-zero otherwise
46    */
47    function parse()
48    {
49
50        $this->code = trim($this->code);
51        if (empty($this->code)){
52            return 1;
53        }
54
55        $t = token_get_all('<?php '.$this->code.' ?>');
56
57        $need_semicolon = 1; /* do we need a semicolon to complete the statement ? */
58        $need_return = 1;    /* can we prepend a return to the eval-string ? */
59        $eval = '';          /* code to be eval()'ed later */
60        $braces = array();   /* to track if we need more closing braces */
61
62        $methods = array();  /* to track duplicate methods in a class declaration */
63        $ts = array();       /* tokens without whitespaces */
64
65        foreach ($t as $ndx => $token) {
66            if (is_array($token)) {
67                $ignore = 0;
68
69                switch($token[0]) {
70                    case T_WHITESPACE:
71                    case T_OPEN_TAG:
72                    case T_CLOSE_TAG:
73                    $ignore = 1;
74                    break;
75                    case T_FOREACH:
76                    case T_DO:
77                    case T_WHILE:
78                    case T_FOR:
79
80                    case T_IF:
81                    case T_RETURN:
82
83                    case T_CLASS:
84                    case T_FUNCTION:
85                    case T_INTERFACE:
86
87                    case T_PRINT:
88                    case T_ECHO:
89
90                    case T_COMMENT:
91                    case T_UNSET:
92
93                    case T_INCLUDE:
94                    case T_REQUIRE:
95                    case T_INCLUDE_ONCE:
96                    case T_REQUIRE_ONCE:
97                    case T_TRY:
98                    $need_return = 0;
99                    break;
100                    case T_VARIABLE:
101                    case T_STRING:
102                    case T_NEW:
103                    case T_EXTENDS:
104                    case T_IMPLEMENTS:
105                    case T_OBJECT_OPERATOR:
106                    case T_DOUBLE_COLON:
107                    case T_INSTANCEOF:
108
109                    case T_CATCH:
110
111                    case T_ELSE:
112                    case T_AS:
113                    case T_LNUMBER:
114                    case T_DNUMBER:
115                    case T_CONSTANT_ENCAPSED_STRING:
116                    case T_ENCAPSED_AND_WHITESPACE:
117                    case T_CHARACTER:
118                    case T_ARRAY:
119                    case T_DOUBLE_ARROW:
120
121                    case T_CONST:
122                    case T_PUBLIC:
123                    case T_PROTECTED:
124                    case T_PRIVATE:
125                    case T_ABSTRACT:
126                    case T_STATIC:
127                    case T_VAR:
128
129                    case T_INC:
130                    case T_DEC:
131                    case T_SL:
132                    case T_SL_EQUAL:
133                    case T_SR:
134                    case T_SR_EQUAL:
135
136                    case T_IS_EQUAL:
137                    case T_IS_IDENTICAL:
138                    case T_IS_GREATER_OR_EQUAL:
139                    case T_IS_SMALLER_OR_EQUAL:
140
141                    case T_BOOLEAN_OR:
142                    case T_LOGICAL_OR:
143                    case T_BOOLEAN_AND:
144                    case T_LOGICAL_AND:
145                    case T_LOGICAL_XOR:
146                    case T_MINUS_EQUAL:
147                    case T_PLUS_EQUAL:
148                    case T_MUL_EQUAL:
149                    case T_DIV_EQUAL:
150                    case T_MOD_EQUAL:
151                    case T_XOR_EQUAL:
152                    case T_AND_EQUAL:
153                    case T_OR_EQUAL:
154
155                    case T_FUNC_C:
156                    case T_CLASS_C:
157                    case T_LINE:
158                    case T_FILE:
159
160                    /* just go on */
161                    break;
162                    default:
163                    /* debug unknown tags*/
164                    error_log(sprintf("unknown tag: %d (%s): %s".PHP_EOL, $token[0], token_name($token[0]), $token[1]));
165
166                    break;
167                }
168                if (!$ignore) {
169                    $eval .= $token[1]." ";
170                    $ts[] = array("token" => $token[0], "value" => $token[1]);
171                }
172            } else {
173                $ts[] = array("token" => $token, "value" => '');
174
175                $last = count($ts) - 1;
176
177                switch ($token) {
178                    case '(':
179                    /* walk backwards through the tokens */
180
181                    if ($last >= 3 &&
182                    $ts[$last - 1]['token'] == T_STRING &&
183                    $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
184                    $ts[$last - 3]['token'] == T_VARIABLE ) {
185
186                        /* $object->method( */
187
188                        /* $object has to exist and has to be a object */
189                        $objname = $ts[$last - 3]['value'];
190
191                        if (!isset($GLOBALS[ltrim($objname, '$')])) {
192                            $this->addError(sprintf('Variable \'%s\' is not set', $objname));
193                        }
194
195                        $k = ltrim($objname, '$');
196
197                        if(isset($GLOBALS[$k])){
198                            $object = $GLOBALS[$k];
199
200                            if (!is_object($object)) {
201                                $this->addError(sprintf('Variable \'%s\' is not a class', $objname));
202                            }
203
204                            $method = $ts[$last - 1]['value'];
205
206                            /* obj */
207
208                            if (!method_exists($object, $method)) {
209                                $this->addError(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'",
210                                $objname, get_class($object), $method));
211                            }
212                        }
213                    } else if ($last >= 3 &&
214                    $ts[$last - 1]['token'] == T_VARIABLE &&
215                    $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
216                    $ts[$last - 3]['token'] == T_VARIABLE ) {
217
218                        /* $object->$method( */
219
220                        /* $object has to exist and has to be a object */
221                        $objname = $ts[$last - 3]['value'];
222
223                        if (!isset($GLOBALS[ltrim($objname, '$')])) {
224                            $this->addError(sprintf('Variable \'%s\' is not set', $objname));
225                        }
226                        $object = $GLOBALS[ltrim($objname, '$')];
227
228                        if (!is_object($object)) {
229                            $this->addError(sprintf('Variable \'%s\' is not a class', $objname));
230                        }
231
232                        $methodname = $ts[$last - 1]['value'];
233
234                        if (!isset($GLOBALS[ltrim($methodname, '$')])) {
235                            $this->addError(sprintf('Variable \'%s\' is not set', $methodname));
236                        }
237                        $method = $GLOBALS[ltrim($methodname, '$')];
238
239                        /* obj */
240
241                        if (!method_exists($object, $method)) {
242                            $this->addError(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'",
243                            $objname, get_class($object), $method));
244                        }
245
246                    } else if ($last >= 6 &&
247                    $ts[$last - 1]['token'] == T_STRING &&
248                    $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
249                    $ts[$last - 3]['token'] == ']' &&
250                    /* might be anything as index */
251                    $ts[$last - 5]['token'] == '[' &&
252                    $ts[$last - 6]['token'] == T_VARIABLE ) {
253
254                        /* $object[...]->method( */
255
256                        /* $object has to exist and has to be a object */
257                        $objname = $ts[$last - 6]['value'];
258
259                        if (!isset($GLOBALS[ltrim($objname, '$')])) {
260                            $this->addError(sprintf('Variable \'%s\' is not set', $objname));
261                        }
262                        $array = $GLOBALS[ltrim($objname, '$')];
263
264                        if (!is_array($array)) {
265                            $this->addError(sprintf('Variable \'%s\' is not a array', $objname));
266                        }
267
268                        $andx = $ts[$last - 4]['value'];
269
270                        if (!isset($array[$andx])) {
271                            $this->addError(sprintf('%s[\'%s\'] is not set', $objname, $andx));
272                        }
273
274                        $object = $array[$andx];
275
276                        if (!is_object($object)) {
277                            $this->addError(sprintf('Variable \'%s\' is not a class', $objname));
278                        }
279
280                        $method = $ts[$last - 1]['value'];
281
282                        /* obj */
283
284                        if (!method_exists($object, $method)) {
285                            $this->addError(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'",
286                            $objname, get_class($object), $method));
287                        }
288
289                    } else if ($last >= 3 &&
290                    $ts[$last - 1]['token'] == T_STRING &&
291                    $ts[$last - 2]['token'] == T_DOUBLE_COLON &&
292                    $ts[$last - 3]['token'] == T_STRING ) {
293
294                        /* Class::method() */
295
296                        /* $object has to exist and has to be a object */
297                        $classname = $ts[$last - 3]['value'];
298
299                        if (!class_exists($classname)) {
300                            $this->addError(sprintf('Class \'%s\' doesn\'t exist', $classname));
301                        }
302
303                        $method = $ts[$last - 1]['value'];
304
305                        if (!empty($method) && !in_array($method, (array)get_class_methods($classname))) {
306                            $this->addError(sprintf("Class '%s' doesn't have a method named '%s'",
307                            $classname, $method));
308                        }
309                    } else if ($last >= 3 &&
310                    $ts[$last - 1]['token'] == T_VARIABLE &&
311                    $ts[$last - 2]['token'] == T_DOUBLE_COLON &&
312                    $ts[$last - 3]['token'] == T_STRING ) {
313
314                        /* Class::method() */
315
316                        /* $object has to exist and has to be a object */
317                        $classname = $ts[$last - 3]['value'];
318
319                        if (!class_exists($classname)) {
320                            $this->addError(sprintf('Class \'%s\' doesn\'t exist', $classname));
321                        }
322
323                        $methodname = $ts[$last - 1]['value'];
324
325                        if (!isset($GLOBALS[ltrim($methodname, '$')])) {
326                            $this->addError(sprintf('Variable \'%s\' is not set', $methodname));
327                        }
328                        $method = $GLOBALS[ltrim($methodname, '$')];
329
330                        if (!in_array($method, get_class_methods($classname))) {
331                            $this->addError(sprintf("Class '%s' doesn't have a method named '%s'",
332                            $classname, $method));
333                        }
334
335                    } else if ($last >= 2 &&
336                    $ts[$last - 1]['token'] == T_STRING &&
337                    $ts[$last - 2]['token'] == T_NEW ) {
338
339                        /* new Class() */
340
341                        $classname = $ts[$last - 1]['value'];
342
343                        if (!class_exists($classname)) {
344                            $this->addError(sprintf('Class \'%s\' doesn\'t exist', $classname));
345                        }
346
347                        if(AK_PHP5){
348                            $r = new ReflectionClass($classname);
349
350                            if ($r->isAbstract()) {
351                                $this->addError(sprintf("Can't instantiate abstract Class '%s'", $classname));
352                            }
353
354                            if (!$r->isInstantiable()) {
355                                $this->addError(sprintf('Class \'%s\' can\'t be instantiated. Is the class abstract ?', $classname));
356                            }
357                        }
358
359                    } else if ($last >= 2 &&
360                    $ts[0]['token'] != T_CLASS &&
361                    $ts[$last - 1]['token'] == T_STRING &&
362                    $ts[$last - 2]['token'] == T_FUNCTION ) {
363
364                        /* make sure we are not a in class definition */
365
366                        /* function a() */
367
368                        $func = $ts[$last - 1]['value'];
369
370                        if (function_exists($func)) {
371                            $this->addError(sprintf('Function \'%s\' is already defined', $func));
372                        }
373                    } else if ($last >= 4 &&
374                    $ts[0]['token'] == T_CLASS &&
375                    $ts[1]['token'] == T_STRING &&
376                    $ts[$last - 1]['token'] == T_STRING &&
377                    $ts[$last - 2]['token'] == T_FUNCTION ) {
378
379                        /* make sure we are not a in class definition */
380
381                        /* class a { .. function a() ... } */
382
383                        $func = $ts[$last - 1]['value'];
384                        $classname = $ts[1]['value'];
385
386                        if (isset($methods[$func])) {
387                            $this->addError(sprintf("Can't redeclare method '%s' in Class '%s'", $func, $classname));
388                        }
389
390                        $methods[$func] = 1;
391
392                    } else if ($last >= 1 &&
393                    $ts[$last - 1]['token'] == T_STRING ) {
394                        /* func() */
395                        $funcname = $ts[$last - 1]['value'];
396
397                        if (!function_exists($funcname)) {
398                            $this->addError(sprintf("Function %s() doesn't exist", $funcname));
399                        }
400                    } else if ($last >= 1 &&
401                    $ts[$last - 1]['token'] == T_VARIABLE ) {
402
403                        /* $object has to exist and has to be a object */
404                        $funcname = $ts[$last - 1]['value'];
405
406                        if (!isset($GLOBALS[ltrim($funcname, '$')])) {
407                            $this->addError(sprintf('Variable \'%s\' is not set', $funcname));
408                        }
409                        $k = ltrim($funcname, '$');
410
411                        if(isset($GLOBALS[$k])){
412                            $func = $GLOBALS[$k];
413
414                            if (!function_exists($func)) {
415                                $this->addError(sprintf("Function %s() doesn't exist", $func));
416                            }
417                        }
418
419                    }
420
421                    array_push($braces, $token);
422                    break;
423                    case '{':
424                    $need_return = 0;
425
426                    if ($last >= 2 &&
427                    $ts[$last - 1]['token'] == T_STRING &&
428                    $ts[$last - 2]['token'] == T_CLASS ) {
429
430                        /* class name { */
431
432                        $classname = $ts[$last - 1]['value'];
433
434                        if (class_exists($classname)) {
435                            $this->addError(sprintf("Class '%s' can't be redeclared", $classname));
436                        }
437                    } else if ($last >= 4 &&
438                    $ts[$last - 1]['token'] == T_STRING &&
439                    $ts[$last - 2]['token'] == T_EXTENDS &&
440                    $ts[$last - 3]['token'] == T_STRING &&
441                    $ts[$last - 4]['token'] == T_CLASS ) {
442
443                        /* class classname extends classname { */
444
445                        $classname = $ts[$last - 3]['value'];
446                        $extendsname = $ts[$last - 1]['value'];
447
448                        if (class_exists($classname, false)) {
449                            $this->addError(sprintf("Class '%s' can't be redeclared",
450                            $classname));
451                        }
452                        if (!class_exists($extendsname, false)) {
453                            $this->addError(sprintf("Can't extend '%s' from not existing Class '%s'",
454                            $classname, $extendsname));
455                        }
456                    } else if ($last >= 4 &&
457                    $ts[$last - 1]['token'] == T_STRING &&
458                    $ts[$last - 2]['token'] == T_IMPLEMENTS &&
459                    $ts[$last - 3]['token'] == T_STRING &&
460                    $ts[$last - 4]['token'] == T_CLASS ) {
461
462                        /* class name implements interface { */
463
464                        $classname = $ts[$last - 3]['value'];
465                        $implements = $ts[$last - 1]['value'];
466
467                        if (class_exists($classname, false)) {
468                            $this->addError(sprintf("Class '%s' can't be redeclared",
469                            $classname));
470                        }
471                        if (!interface_exists($implements, false)) {
472                            $this->addError(sprintf("Can't implement not existing Interface '%s' for Class '%s'",
473                            $implements, $classname));
474                        }
475                    }
476
477                    array_push($braces, $token);
478                    break;
479                    case '}':
480                    $need_return = 0;
481                    case ')':
482                    array_pop($braces);
483                    break;
484                }
485
486                $eval .= $token;
487            }
488        }
489
490        $last = count($ts) - 1;
491        if ($last >= 2 &&
492        $ts[$last - 0]['token'] == T_STRING &&
493        $ts[$last - 1]['token'] == T_DOUBLE_COLON &&
494        $ts[$last - 2]['token'] == T_STRING ) {
495
496            /* Class::constant */
497
498            /* $object has to exist and has to be a object */
499            $classname = $ts[$last - 2]['value'];
500
501            if (!class_exists($classname)) {
502                $this->addError(sprintf('Class \'%s\' doesn\'t exist', $classname));
503            }
504
505            $constname = $ts[$last - 0]['value'];
506
507            if(AK_PHP5){
508                $c = new ReflectionClass($classname);
509                if (!$c->hasConstant($constname)) {
510                    $this->addError(sprintf("Class '%s' doesn't have a constant named '%s'",
511                    $classname, $constname));
512                }
513            }
514        } else if ($last == 0 &&
515        $ts[$last - 0]['token'] == T_VARIABLE ) {
516
517            /* $var */
518
519            $varname = $ts[$last - 0]['value'];
520
521            if (!isset($GLOBALS[ltrim($varname, '$')])) {
522                $this->addError(sprintf('Variable \'%s\' is not set', $varname));
523            }
524        }
525
526
527        $need_more = count($braces);
528
529        if ($need_more || ';' === $token) {
530            $need_semicolon = 0;
531        }
532
533        if ($need_return) {
534            $eval = "return ".$eval;
535        }
536
537        /* add a traling ; if necessary */
538        if ($need_semicolon){
539            $eval .= ';';
540        }
541
542        if (!$need_more) {
543            $this->code = $eval;
544        }
545
546        return $need_more;
547    }
548
549    function addError($error)
550    {
551        $this->errors[$error] = '';
552    }
553
554    function hasErrors()
555    {
556        return !empty($this->errors);
557    }
558
559    function getErrors()
560    {
561        return array_keys($this->errors);
562    }
563
564}
565
566?>
Note: See TracBrowser for help on using the browser.