| [ Index ] |
PHP Cross Reference of Limb3 |
[Summary view] [Print] [Text view]
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 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Sat Nov 22 03:48:54 2008 | Cross-referenced by PHPXref 0.7 |