[ Index ]

PHP Cross Reference of Akelos Framework

title

Body

[close]

/ -> AkPhpParser.php (source)

   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  
  11  require_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   */
  29  class 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  ?>


Generated: Mon Oct 27 12:43:49 2008 Cross-referenced by PHPXref 0.6