|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 /* 00003 * $Id: PHPParser.php,v 1.3 2010/12/14 17:36:00 moodlerobot Exp $ 00004 * 00005 * Copyright(c) 2004-2006, SpikeSource Inc. All Rights Reserved. 00006 * Licensed under the Open Software License version 2.1 00007 * (See http://www.spikesource.com/license.html) 00008 */ 00009 ?> 00010 <?php 00011 00012 if(!defined("__PHPCOVERAGE_HOME")) { 00013 define("__PHPCOVERAGE_HOME", dirname(dirname(__FILE__))); 00014 } 00015 require_once __PHPCOVERAGE_HOME . "/parser/Parser.php"; 00016 00024 class PHPParser extends Parser { 00025 /*{{{ Members */ 00026 00027 private $inHereDoc = false; 00028 private $inFunction = false; 00029 private $phpStarters = array('<?php', '<?', '<?='); 00030 private $phpFinisher = '?>'; 00031 private $inComment = false; 00032 private $lastLineEndTokenType = ""; 00033 private $fileToken = null; 00034 private $currFileToken = 0; 00035 private $lineNb = 0; 00036 private $multiLineTok = null; 00037 // If one of these tokens occur as the last token of a line 00038 // then the next line can be treated as a continuation line 00039 // depending on how it starts. 00040 public static $contTypes = array( 00041 "(", 00042 ",", 00043 ".", 00044 "=", 00045 T_LOGICAL_XOR, 00046 T_LOGICAL_AND, 00047 T_LOGICAL_OR, 00048 T_PLUS_EQUAL, 00049 T_MINUS_EQUAL, 00050 T_MUL_EQUAL, 00051 T_DIV_EQUAL, 00052 T_CONCAT_EQUAL, 00053 T_MOD_EQUAL, 00054 T_AND_EQUAL, 00055 T_OR_EQUAL, 00056 T_XOR_EQUAL, 00057 T_BOOLEAN_AND, 00058 T_BOOLEAN_OR, 00059 T_OBJECT_OPERATOR, 00060 T_DOUBLE_ARROW, 00061 "[", 00062 "]", 00063 T_LOGICAL_OR, 00064 T_LOGICAL_XOR, 00065 T_LOGICAL_AND, 00066 T_STRING 00067 ); 00068 /*}}}*/ 00069 00070 /*{{{ protected function openFileReadOnly() */ 00071 00079 protected function openFileReadOnly() { 00080 $this->fileToken = @token_get_all(file_get_contents($this->filename)); 00081 return parent::openFileReadOnly(); 00082 } 00083 00084 /*}}}*/ 00085 /*{{{ protected function getNextToken() */ 00086 00098 protected function getNextToken($line, &$pos) { 00099 $lineLen = strlen($line); 00100 if($pos >= $lineLen) { 00101 return null; 00102 } 00103 if($this->multiLineTok != null) { 00104 list($tok, $lnb, $posnl) = $this->multiLineTok; 00105 if(is_string($tok)) { 00106 $str = $tok; 00107 } else { 00108 $str = $tok[1]; 00109 } 00110 if($posnl >= strlen($str)) { 00111 $this->multiLineTok = null; 00112 } else { 00113 if(substr($str, $posnl + 1, $lineLen) == $line) { 00114 $pos += $lineLen; 00115 $newPosnl = $posnl + $lineLen; 00116 } else { 00117 $newPosnl = strpos($str, "\n", $posnl + 1); 00118 if($newPosnl === false) { 00119 $newPosnl = strlen($str); 00120 } 00121 $len = $newPosnl - $posnl - 1; 00122 //if(substr($str, $posnl + 1, $len) != substr($line, $pos, $len)) { 00123 //} 00124 $pos += $len; 00125 } 00126 $this->multiLineTok[1]++; 00127 $this->multiLineTok[2] = $newPosnl; 00128 return $tok; 00129 } 00130 } 00131 if(!isset($this->fileToken[$this->currFileToken])) { 00132 return null; 00133 } 00134 $tok = &$this->fileToken[$this->currFileToken]; 00135 if(is_string($tok)) { 00136 $str = $tok; 00137 } else { 00138 $str = $tok[1]; 00139 } 00140 $nbnl = substr_count($str, "\n"); 00141 if($nbnl > 0 && ($posnl = strpos($str, "\n")) != strlen($str) - 1) { 00142 $this->multiLineTok = array($tok, 1, $posnl); 00143 $str = substr($str, 0, $posnl + 1); 00144 } 00145 if(substr($line, $pos, strlen($str)) == $str) { 00146 $this->currFileToken++; 00147 $pos += strlen($str); 00148 return $tok; 00149 } else { 00150 return null; 00151 } 00152 } 00153 00154 /*}}}*/ 00155 /*{{{ protected function isMultiLineCont() */ 00156 00166 protected function isMultiLineCont() { 00167 if($this->multiLineTok == null 00168 || $this->multiLineTok[1] <= 1) { 00169 return false; 00170 } 00171 switch ($this->getTokenType($this->multiLineTok[0])) { 00172 case T_COMMENT: 00173 case T_INLINE_HTML: // <br/><b>jhsk</b> 00174 return false; 00175 } 00176 return true; 00177 } 00178 00179 /*}}}*/ 00180 /*{{{ protected function processLine() */ 00181 00192 protected function processLine($line) { 00193 00194 // Default values 00195 $prevLineType = $this->lineType; 00196 $this->lineType = LINE_TYPE_NOEXEC; 00197 $tokenCnt = 0; 00198 $this->lineNb++; 00199 $pos = 0; 00200 $seeMore = false; 00201 $seenEnough = false; 00202 $lastToken = null; 00203 00204 while (($token = $this->getNextToken($line, $pos))) { 00205 if (!is_string($token)) { 00206 $stoken = token_name($token[0]) . ' "' 00207 . str_replace(array("\\", "\"", "\n", "\r") 00208 , array("\\\\", "\\\"", "\\n", "\\r") 00209 , $token[1]) . '"'; 00210 if (isset($token[2])) { 00211 $stoken .= '[' . $token[2] . ']'; 00212 if ($token[2] != $this->lineNb) { 00213 $stoken .= ' != [' . $this->lineNb . ']'; 00214 } 00215 } 00216 } else { 00217 $stoken = $token; 00218 } 00219 $this->logger->debug("Token $stoken", __FILE__, __LINE__); 00220 if (!is_string($token) && $token[0] == T_WHITESPACE) { 00221 continue; 00222 } 00223 $lastToken = $token; 00224 $tokenCnt ++; 00225 if ($this->inHereDoc) { 00226 $this->lineType = LINE_TYPE_CONT; 00227 $this->logger->debug("Here doc Continuation! Token: $token", 00228 __FILE__, __LINE__); 00229 00230 if ($this->getTokenType($token) == T_END_HEREDOC) { 00231 $this->inHereDoc = false; 00232 } 00233 continue; 00234 } 00235 if ($this->inFunction) { 00236 $this->lineType = LINE_TYPE_NOEXEC; 00237 $this->logger->debug("Function! Token: $token", 00238 __FILE__, __LINE__); 00239 if ($this->getTokenType($token) == '{') { 00240 $this->inFunction = false; 00241 } 00242 continue; 00243 } 00244 00245 if($tokenCnt == 1 && $prevLineType != LINE_TYPE_NOEXEC 00246 && ($this->isMultiLineCont() 00247 || $this->isContinuation($token))) { 00248 $this->lineType = LINE_TYPE_CONT; 00249 $this->logger->debug("Continuation! Token: ".print_r($token, true), 00250 __FILE__, __LINE__); 00251 $seenEnough = true; 00252 continue; 00253 } 00254 if ($seenEnough) { 00255 continue; 00256 } 00257 if(is_string($token)) { 00258 // FIXME: Add more cases, if needed 00259 switch($token) { 00260 // Any of these things, are non-executable. 00261 // And so do not change the status of the line 00262 case '{': 00263 case '}': 00264 case '(': 00265 case ')': 00266 case ';': 00267 break; 00268 00269 // Everything else by default is executable. 00270 default: 00271 $this->logger->debug("Other string: $token", 00272 __FILE__, __LINE__); 00273 if($this->lineType == LINE_TYPE_NOEXEC) { 00274 $this->lineType = LINE_TYPE_EXEC; 00275 } 00276 break; 00277 } 00278 $this->logger->debug("Status: " . $this->getLineTypeStr($this->lineType) . "\t\tToken: $token", 00279 __FILE__, __LINE__); 00280 } 00281 else { 00282 // The token is an array 00283 list($tokenType, $text) = $token; 00284 switch($tokenType) { 00285 case T_COMMENT: 00286 case T_DOC_COMMENT: 00287 case T_WHITESPACE: // white space 00288 case T_OPEN_TAG: // < ? 00289 case T_OPEN_TAG_WITH_ECHO: // < ? = 00290 case T_CURLY_OPEN: // 00291 case T_INLINE_HTML: // <br/><b>jhsk</b> 00292 //case T_STRING: // 00293 case T_EXTENDS: // extends 00294 case T_STATIC: // static 00295 case T_STRING_VARNAME: // string varname? 00296 case T_CHARACTER: // character 00297 case T_ELSE: // else 00298 case T_CONSTANT_ENCAPSED_STRING: // "some str" 00299 case T_ARRAY: // array 00300 // Any of these things, are non-executable. 00301 // And so do not change the status of the line 00302 // as the line starts as non-executable. 00303 break; 00304 case T_START_HEREDOC: 00305 $this->inHereDoc = true; 00306 break; 00307 case T_PRIVATE: // private 00308 case T_PUBLIC: // public 00309 case T_PROTECTED: // protected 00310 case T_VAR: // var 00311 case T_GLOBAL: // global 00312 case T_ABSTRACT: // abstract (Moodle added) 00313 case T_CLASS: // class 00314 case T_INTERFACE: // interface 00315 case T_REQUIRE: // require 00316 case T_REQUIRE_ONCE: // require_once 00317 case T_INCLUDE: // include 00318 case T_INCLUDE_ONCE: // include_once 00319 case T_SWITCH: // switch 00320 case T_CONST: // const 00321 case T_TRY: // try 00322 $this->lineType = LINE_TYPE_NOEXEC; 00323 // No need to see any further 00324 $seenEnough = true; 00325 break; 00326 case T_FUNCTION: // function 00327 $this->lineType = LINE_TYPE_NOEXEC; 00328 $this->inFunction = true; 00329 // No need to see any further 00330 $seenEnough = true; 00331 break; 00332 case T_VARIABLE: // $foo 00333 $seeMore = true; 00334 if($this->lineType == LINE_TYPE_NOEXEC) { 00335 $this->lineType = LINE_TYPE_EXEC; 00336 } 00337 break; 00338 case T_CLOSE_TAG: 00339 if($this->lineType != LINE_TYPE_EXEC) { 00340 $this->lineType = LINE_TYPE_NOEXEC; 00341 } 00342 break; 00343 default: 00344 $this->logger->debug("Other token: " . token_name($tokenType), 00345 __FILE__, __LINE__); 00346 $seeMore = false; 00347 if($this->lineType == LINE_TYPE_NOEXEC) { 00348 $this->lineType = LINE_TYPE_EXEC; 00349 } 00350 break; 00351 } 00352 $this->logger->debug("Status: " . $this->getLineTypeStr($this->lineType) . "\t\tToken type: $tokenType \tText: $text", 00353 __FILE__, __LINE__); 00354 } 00355 if(($this->lineType == LINE_TYPE_EXEC && !$seeMore) 00356 || $seenEnough) { 00357 // start moodle modification: comment debug line causing notices 00358 //$this->logger->debug("Made a decision! Exiting. Token Type: $tokenType & Text: $text", 00359 // __FILE__, __LINE__); 00360 // end moodle modification 00361 if($seenEnough) { 00362 $this->logger->debug("Seen enough at Token Type: $tokenType & Text: $text", 00363 __FILE__, __LINE__); 00364 } else { 00365 $seenEnough = true; 00366 } 00367 } 00368 } // end while 00369 $this->logger->debug("Line Type: " . $this->getLineTypeStr($this->lineType), 00370 __FILE__, __LINE__); 00371 $this->lastLineEndTokenType = $this->getTokenType($lastToken); 00372 $this->logger->debug("Last End Token: " . $this->lastLineEndTokenType, 00373 __FILE__, __LINE__); 00374 } 00375 00376 /*}}}*/ 00377 /*{{{ public function getLineType() */ 00378 00385 public function getLineType() { 00386 return $this->lineType; 00387 } 00388 /*}}}*/ 00389 /*{{{ protected function isContinuation() */ 00390 00398 protected function isContinuation(&$token) { 00399 if(is_string($token)) { 00400 switch($token) { 00401 case ".": 00402 case ","; 00403 case "]": 00404 case "[": 00405 case "(": 00406 case ")": 00407 case "=": 00408 return true; 00409 } 00410 } 00411 else { 00412 list($tokenType, $text) = $token; 00413 switch($tokenType) { 00414 case T_CONSTANT_ENCAPSED_STRING: 00415 case T_ARRAY: 00416 case T_DOUBLE_ARROW: 00417 case T_OBJECT_OPERATOR: 00418 case T_LOGICAL_XOR: 00419 case T_LOGICAL_AND: 00420 case T_LOGICAL_OR: 00421 case T_PLUS_EQUAL: 00422 case T_MINUS_EQUAL: 00423 case T_MUL_EQUAL: 00424 case T_DIV_EQUAL: 00425 case T_CONCAT_EQUAL: 00426 case T_MOD_EQUAL: 00427 case T_AND_EQUAL: 00428 case T_OR_EQUAL: 00429 case T_XOR_EQUAL: 00430 case T_BOOLEAN_AND: 00431 case T_BOOLEAN_OR: 00432 case T_LNUMBER: 00433 case T_DNUMBER: 00434 return true; 00435 00436 case T_STRING: 00437 case T_VARIABLE: 00438 return in_array($this->lastLineEndTokenType, PHPParser::$contTypes); 00439 } 00440 } 00441 00442 return false; 00443 } 00444 /*}}}*/ 00445 /*{{{ protected function getTokenType() */ 00446 00455 protected function getTokenType($token) { 00456 if(is_string($token)) { 00457 return $token; 00458 } 00459 else { 00460 list($tokenType, $text) = $token; 00461 return $tokenType; 00462 } 00463 } 00464 /*}}}*/ 00465 /* 00466 // Main 00467 $obj = new PHPParser(); 00468 $obj->parse("test.php"); 00469 while(($line = $obj->getLine()) !== false) { 00470 echo "#########################\n"; 00471 echo "[" . $line . "] Type: [" . $obj->getLineTypeStr($obj->getLineType()) . "]\n"; 00472 echo "#########################\n"; 00473 } 00474 */ 00475 00476 } 00477 ?>