[ Index ]

PHP Cross Reference of Limb3

title

Body

[close]

/tests_runner/lib/spikephpcoverage/src/parser/ -> PHPParser.php (source)

   1  <?php
   2      /*
   3      *  $Id: PHPParser.php 14665 2005-03-23 19:37:50Z npac $
   4      *  
   5      *  Copyright(c) 2004-2005, SpikeSource Inc. All Rights Reserved.
   6      *  Licensed under the Open Source License version 2.1
   7      *  (See http://www.spikesource.com/license.html)
   8      */
   9  ?>
  10  <?php
  11  
  12      if(!defined("__PHPCOVERAGE_HOME")) {
  13          define("__PHPCOVERAGE_HOME", dirname(dirname(__FILE__)));
  14      }
  15      require_once __PHPCOVERAGE_HOME . "/parser/Parser.php";
  16  
  17      /** 
  18      * Parser for PHP files 
  19      * 
  20      * @author Nimish Pachapurkar (npac@spikesource.com)
  21      * @version $Revision: 14665 $
  22      * @package tests_runner
  23      */
  24      class PHPParser extends Parser {
  25          /*{{{ Members */
  26  
  27          private $inPHP = false;
  28          private $phpStarters = array('<?php', '<?', '<?=');
  29          private $phpFinisher = '?>';
  30          private $inComment = false;
  31          private $lastLineEndTokenType = "";
  32          // If one of these tokens occur as the last token of a line
  33          // then the next line can be treated as a continuation line
  34          // depending on how it starts.
  35          public static $contTypes = array(
  36              "(",
  37              ",",
  38              ".",
  39              "=",
  40              T_LOGICAL_XOR,
  41              T_LOGICAL_AND,
  42              T_LOGICAL_OR,
  43              T_PLUS_EQUAL,
  44              T_MINUS_EQUAL,
  45              T_MUL_EQUAL,
  46              T_DIV_EQUAL,
  47              T_CONCAT_EQUAL,
  48              T_MOD_EQUAL,
  49              T_AND_EQUAL,
  50              T_OR_EQUAL,
  51              T_XOR_EQUAL,
  52              T_BOOLEAN_AND,
  53              T_BOOLEAN_OR,
  54              T_OBJECT_OPERATOR, 
  55              T_DOUBLE_ARROW, 
  56              "[", 
  57              "]",
  58              T_LOGICAL_OR, 
  59              T_LOGICAL_XOR, 
  60              T_LOGICAL_AND
  61          );
  62  
  63          /*}}}*/
  64          /*{{{ protected function processLine() */
  65  
  66          /** 
  67          * Process a line read from the file and determine if it is an
  68          * executable line or not. 
  69          *
  70          * This is the work horse function that does most of the parsing.
  71          * To parse PHP, get_all_tokens() tokenizer function is used.
  72          * 
  73          * @param $line  Line to be parsed.
  74          * @access protected
  75          */
  76          protected function processLine($line) {
  77  
  78              // Default values
  79              $this->lineType = LINE_TYPE_NOEXEC;
  80              $line = trim($line);
  81              $parseLine = $line;
  82              $artificialStart = false;
  83              $artificialEnd = false;
  84  
  85              // If we are not inside PHP opening tag
  86              if(!$this->inPHP) {
  87                  $pos = -1;
  88  
  89                  // Confirm that the line does not have T_OPEN_TAG_WITH_ECHO (< ? =)
  90                  if(strpos($line, $this->phpStarters[2]) === false) {
  91                      // If the line has PHP start tag of the first kind
  92                      if(($pos = strpos($line, $this->phpStarters[0])) !== false) {
  93                          $pos = $pos + strlen($this->phpStarters[0]);
  94                      }
  95                      // if the line has PHP start tag of the second kind.
  96                      else if(($pos = strpos($line, $this->phpStarters[1])) !== false) {
  97                          $pos = $pos + strlen($this->phpStarters[1]);
  98                      }
  99                      // $pos now points to the character after opening tag
 100                      if($pos > 0) {
 101                          $this->inPHP = true;
 102                          //echo "Going in PHP\n";
 103                          // Remove the part of the line till the PHP opening 
 104                          // tag and recurse
 105                          return $this->processLine(trim(substr($line, $pos))); 
 106                      }
 107                  }
 108              }
 109              // If we are already in PHP 
 110              else if($this->inPHP) {
 111                  // If we are inside a multi-line comment, that is not ending 
 112                  // on the same line
 113                  if((strpos($line, "/*") !== false && 
 114                  strpos($line, "*/") === false) || 
 115                  (strpos($line, "/*") > strpos($line, "*/"))) {
 116                      $this->inComment = true;
 117                  }
 118                  if($this->inComment) {
 119                      // Do we need to append an artificial comment start?
 120                      // (otherwise the tokenizer might throw error.
 121                      if(strpos($line, "/*") === false) {
 122                          $line = "/*" . $line;
 123                          $artificialStart = true;
 124                      }
 125                      // Do we need to append an artificial comment end?
 126                      if(strpos($line, "*/") === false) {
 127                          $line = $line . "*/";
 128                          $artificialEnd = true;
 129                      }
 130                  }
 131                  // Since we are inside php, append php opening and closing tags
 132                  // to prevent tokenizer from mis-interpreting the line
 133                  $parseLine = "<?php " . $line . " ?>";
 134              }
 135  
 136              // Tokenize
 137              $tokens = @token_get_all($parseLine);
 138              $this->logger->debug("inPHP? " . $this->inPHP . "\nLine:" . $parseLine,
 139                  __FILE__, __LINE__);
 140              $this->logger->debug(print_r($tokens, true), __FILE__, __LINE__);
 141              $seenEnough = false;
 142              $seeMore = false;
 143              $tokenCnt = 0; //tokens in this line
 144              if($this->isContinuation($this->lastLineEndTokenType)) {
 145                  $this->lineType = LINE_TYPE_CONT;
 146                  $this->logger->debug("Continuation !", __FILE__, __LINE__);
 147              }
 148              foreach($tokens as $token) {
 149                  $tokenCnt ++;
 150                  if($this->inPHP) {
 151                      if($tokenCnt == 2) {
 152                          if($this->isContinuation($token)) {
 153                              $this->lineType = LINE_TYPE_CONT;
 154                              $this->logger->debug("Continuation! Token: $token",
 155                                  __FILE__, __LINE__);
 156                              break;
 157                          }
 158                      }
 159                  }
 160  
 161                  if(is_string($token)) {
 162                      // FIXME: Add more cases, if needed
 163                      switch($token) {
 164                          // Any of these things, are non-executable.
 165                      case '{':
 166                      case '}':
 167                      case '(':
 168                      case ')':
 169                      case ';':
 170                          if($this->lineType != LINE_TYPE_EXEC) {
 171                              $this->lineType = LINE_TYPE_NOEXEC;
 172                          }
 173                          break; 
 174  
 175                      // Everything else by default is executable.
 176                      default:
 177                          $this->lineType = LINE_TYPE_EXEC;
 178                          break;
 179                      }
 180                      $this->logger->debug("Status: " . $this->getLineTypeStr($this->lineType) . "\t\tToken: $token",
 181                          __FILE__, __LINE__);
 182                  }
 183                  else {
 184                      // The token is an array
 185                      list($tokenType, $text) = $token;
 186                      switch($tokenType) {
 187  
 188                          // If it is a comment end or start, set the correct flag
 189                          // If we have put the start or end artificially, ignore!
 190                      case T_COMMENT:
 191                      case T_DOC_COMMENT:
 192                          if(strpos($text, "/*") !== false && !$artificialStart) {
 193                              $this->inComment = true;
 194                          }
 195                          if(strpos($text, "*/") !== false && !$artificialEnd) {
 196                              $this->inComment = false;
 197                          }
 198  
 199                      case T_WHITESPACE:              // white space
 200                      case T_CLOSE_TAG:               // ? >
 201                      case T_OPEN_TAG:                // < ?
 202                      case T_OPEN_TAG_WITH_ECHO:      // < ? =
 203                      case T_CURLY_OPEN:              // 
 204                      case T_INLINE_HTML:             // <br/><b>jhsk</b>
 205                          //case T_STRING:                  // 
 206                      case T_EXTENDS:                 // extends
 207                      case T_STATIC:                  // static
 208                      case T_STRING_VARNAME:          // string varname?
 209                      case T_CHARACTER:               // character
 210                      case T_ELSE:                    // else
 211                      case T_CONSTANT_ENCAPSED_STRING:   // "some str"
 212                          // Only if decision is not already made
 213                          // mark this non-executable.
 214                          if($this->lineType != LINE_TYPE_EXEC) {
 215                              $this->lineType = LINE_TYPE_NOEXEC;
 216                          }
 217                          break;
 218  
 219                      case T_PRIVATE:                 // private
 220                      case T_PUBLIC:                  // public
 221                      case T_PROTECTED:               // protected
 222                      case T_VAR:                     // var
 223                      case T_FUNCTION:                // function
 224                      case T_CLASS:                   // class
 225                      case T_REQUIRE:                 // require
 226                      case T_REQUIRE_ONCE:            // require_once
 227                      case T_INCLUDE:                 // include
 228                      case T_INCLUDE_ONCE:            // include_once
 229                      case T_ARRAY:                   // array
 230                          $this->lineType = LINE_TYPE_NOEXEC;
 231                          // No need to see any further
 232                          $seenEnough = true;
 233                          break; 
 234  
 235                      case T_VARIABLE:                // $foo
 236                          $seeMore = true;
 237                          $this->lineType = LINE_TYPE_EXEC;
 238                          break;
 239  
 240                      default:
 241                          $seeMore = false;
 242                          $this->lineType = LINE_TYPE_EXEC;
 243                          break;
 244                      }
 245                      $this->logger->debug("Status: " . $this->getLineTypeStr($this->lineType) . "\t\tToken type: $tokenType \tText: $text",
 246                          __FILE__, __LINE__);
 247                  }
 248                  if(($this->lineType == LINE_TYPE_EXEC && !$seeMore) 
 249                      || $seenEnough) {
 250                          $this->logger->debug("Made a decision! Exiting. Token Type: $tokenType & Text: $text",
 251                              __FILE__, __LINE__);
 252                      if($seenEnough) {
 253                          $this->logger->debug("Seen enough at Token Type: $tokenType & Text: $text",
 254                              __FILE__, __LINE__);
 255                      }
 256                      break;
 257                  }
 258              } // end foreach
 259              $this->logger->debug("Line Type: " . $this->getLineTypeStr($this->lineType),
 260                  __FILE__, __LINE__);
 261              if($this->inPHP) {
 262                  $this->lastLineEndTokenType = $this->getLastTokenType($tokens);
 263              }
 264              $this->logger->debug("Last End Token: " . $this->lastLineEndTokenType,
 265                  __FILE__, __LINE__);
 266  
 267              if($this->inPHP) {
 268                  // Check if PHP block ends on this line
 269                  if(($pos = strpos($line, $this->phpFinisher)) !== false) {
 270                      $this->inPHP = false;
 271                      // If line is not executable so far, check for the 
 272                      // remaining part
 273                      if($this->lineType != LINE_TYPE_EXEC) {
 274                          return $this->processLine(trim(substr($line, $pos+2)));
 275                      }
 276                  }
 277              }
 278          }
 279  
 280          /*}}}*/
 281          /*{{{ public function getLineType() */
 282  
 283          /** 
 284          * Returns the type of line just read 
 285          * 
 286          * @return Line type
 287          * @access public
 288          */
 289          public function getLineType() {
 290              return $this->lineType;
 291          }
 292          /*}}}*/
 293          /*{{{ protected function isContinuation() */
 294  
 295          /** 
 296          * Check if a line is a continuation of the previous line 
 297          * 
 298          * @param &$token Second token in a line (after PHP start)
 299          * @return Boolean True if the line is a continuation; false otherwise
 300          * @access protected
 301          */
 302          protected function isContinuation(&$token) {
 303              if(is_string($token)) {
 304                  switch($token) {
 305                  case ".":
 306                  case ",";
 307                  case "]":
 308                  case "[":
 309                  case "(":
 310                  case ")":
 311                  case "=":
 312                      return true;
 313                  }
 314              }
 315              else {
 316                  list($tokenType, $text) = $token;               
 317                  switch($tokenType) {
 318                  case T_CONSTANT_ENCAPSED_STRING:
 319                  case T_ARRAY:
 320                  case T_DOUBLE_ARROW:
 321                  case T_OBJECT_OPERATOR:
 322                  case T_LOGICAL_XOR:
 323                  case T_LOGICAL_AND:
 324                  case T_LOGICAL_OR:
 325                  case T_PLUS_EQUAL:
 326                  case T_MINUS_EQUAL:
 327                  case T_MUL_EQUAL:
 328                  case T_DIV_EQUAL:
 329                  case T_CONCAT_EQUAL:
 330                  case T_MOD_EQUAL:
 331                  case T_AND_EQUAL:
 332                  case T_OR_EQUAL:
 333                  case T_XOR_EQUAL:
 334                  case T_BOOLEAN_AND:
 335                  case T_BOOLEAN_OR:
 336                  case T_LNUMBER:
 337                  case T_DNUMBER:
 338                      return true;
 339  
 340                  case T_STRING:
 341                  case T_VARIABLE:
 342                      return in_array($this->lastLineEndTokenType, PHPParser::$contTypes);
 343                  }
 344              }
 345  
 346              return false;
 347          }
 348          /*}}}*/
 349          /*{{{ protected function getTokenType() */
 350  
 351          /** 
 352          * Get the token type of a token (if exists) or
 353          * the token itself.
 354          * 
 355          * @param $token Token
 356          * @return Token type or token itself
 357          * @access protected
 358          */
 359          protected function getTokenType($token) {
 360              if(is_string($token)) {
 361                  return $token;
 362              }
 363              else {
 364                  list($tokenType, $text) = $token;
 365                  return $tokenType;
 366              }
 367          }
 368          /*}}}*/
 369          /*{{{*/
 370  
 371          /** 
 372          * Return the type of last non-empty token in a line 
 373          * 
 374          * @param &$tokens Array of tokens for a line
 375          * @return mixed Last non-empty token type (or token) if exists; false otherwise
 376          * @access protected
 377          */
 378          protected function getLastTokenType(&$tokens) {
 379              for($i = count($tokens)-2; $i > 0; $i--) {
 380                  if(empty($tokens[$i])) {
 381                      continue;
 382                  }
 383                  if(is_string($tokens[$i])) {
 384                      return $tokens[$i];
 385                  }
 386                  else {
 387                      list($tokenType, $text) = $tokens[$i];
 388                      if($tokenType != T_WHITESPACE && $tokenType != T_EMPTY) {
 389                          return $tokenType;
 390                      }
 391                  }
 392              }
 393              return false;
 394          }
 395          /*}}}*/
 396  
 397          /*
 398          // Main
 399          $obj = new PHPParser();
 400          $obj->parse("test.php");
 401          while(($line = $obj->getLine()) !== false) {
 402              echo "#########################\n";
 403              echo "[" . $line . "] Type: [" . $obj->getLineTypeStr($obj->getLineType()) . "]\n";
 404              echo "#########################\n";
 405      }
 406      */
 407  
 408      }
 409  ?>


Generated: Sat Nov 22 03:48:54 2008 Cross-referenced by PHPXref 0.7