* @copyright Joshua Eichorn 2004 * @package PHPCodeAnalyzer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * @license http://www.gnu.org/copyleft/lesser.html LGPL */ /**#@+ * compat tokeniezer defines */ if (! defined('T_OLD_FUNCTION')) { define('T_OLD_FUNCTION', T_FUNCTION); } if (!defined('T_ML_COMMENT')) { define('T_ML_COMMENT', T_COMMENT); } else { define('T_DOC_COMMENT', T_ML_COMMENT); } /**#@-*/ /** * Code Analysis class * * Example Usage: * * $analyzer = new PHPCodeAnalyzer(); * $analyzer->source = file_get_contents(__FILE__); * $analyzer->analyze(); * print_r($analyzer->calledMethods); * * * @todo is it important to grab the details from creating new functions defines classes? * @todo support php5 only stuff like interface * * @version 0.4 * @license http://www.gnu.org/copyleft/lesser.html LGPL * @copyright Joshua Eichorn 2004 * @package PHPCodeAnalyzer * @author Joshua Eichorn */ class PHPCodeAnalyzer { /** * Source code to analyze */ var $source = ""; /** * functions called */ var $calledFunctions = array(); /** * Called constructs */ var $calledConstructs = array(); /** * methods called */ var $calledMethods = array(); /** * static methods called */ var $calledStaticMethods = array(); /** * new classes instantiated */ var $classesInstantiated = array(); /** * variables used */ var $usedVariables = array(); /** * member variables used */ var $usedMemberVariables = array(); /** * classes created */ var $createdClasses = array(); /** * functions created */ var $createdFunctions = array(); /** * Files includes or requried */ var $filesIncluded = array(); // private variables /**#@+ * @access private */ var $currentString = null; var $currentStrings = null; var $currentVar = false; var $staticClass = false; var $inNew = false; var $inInclude = false; var $lineNumber = 1; /**#@-*/ /** * parse source filling informational arrays */ function analyze() { $tokens = token_get_all($this->source); // mapping of token to method to call $handleMap = array( T_STRING => 'handleString', T_CONSTANT_ENCAPSED_STRING => 'handleString', T_ENCAPSED_AND_WHITESPACE => 'handleString', T_CHARACTER => 'handleString', T_NUM_STRING => 'handleString', T_DNUMBER => 'handleString', T_FUNC_C => 'handleString', T_CLASS_C => 'handleString', T_FILE => 'handleString', T_LINE => 'handleString', T_DOUBLE_ARROW => 'handleString', T_DOUBLE_COLON => 'handleDoubleColon', T_NEW => 'handleNew', T_OBJECT_OPERATOR => 'handleObjectOperator', T_VARIABLE => 'handleVariable', T_FUNCTION => 'handleFunction', T_OLD_FUNCTION => 'handleFunction', T_CLASS => 'handleClass', T_WHITESPACE => 'handleWhitespace', T_INLINE_HTML => 'handleWhitespace', T_OPEN_TAG => 'handleWhitespace', T_CLOSE_TAG => 'handleWhitespace', T_AS => 'handleAs', T_ECHO => 'handleConstruct', T_EVAL => 'handleConstruct', T_UNSET => 'handleConstruct', T_ISSET => 'handleConstruct', T_PRINT => 'handleConstruct', T_FOR => 'handleConstruct', T_FOREACH=> 'handleConstruct', T_EMPTY => 'handleConstruct', T_EXIT => 'handleConstruct', T_CASE => 'handleConstruct', T_GLOBAL=> 'handleConstruct', T_UNSET => 'handleConstruct', T_WHILE => 'handleConstruct', T_DO => 'handleConstruct', T_IF => 'handleConstruct', T_LIST => 'handleConstruct', T_RETURN=> 'handleConstruct', T_STATIC=> 'handleConstruct', T_ENDFOR=> 'handleConstruct', T_ENDFOREACH=> 'handleConstruct', T_ENDIF=> 'handleConstruct', T_ENDSWITCH=> 'handleConstruct', T_ENDWHILE=> 'handleConstruct', T_INCLUDE => 'handleInclude', T_INCLUDE_ONCE => 'handleInclude', T_REQUIRE => 'handleInclude', T_REQUIRE_ONCE => 'handleInclude', ); foreach($tokens as $token) { if (is_string($token)) { // we have a simple 1-character token $this->handleSimpleToken($token); } else { list($id, $text) = $token; if (isseT($handleMap[$id])) { $call = $handleMap[$id]; $this->$call($id,$text); } /*else { echo token_name($id).": $text
\n"; }*/ } } } /** * Handle a 1 char token * @access private */ function handleSimpleToken($token) { if ($token !== ";") { $this->currentStrings .= $token; } switch($token) { case "(": // method is called if ($this->staticClass !== false) { if (!isset($this->calledStaticMethods[$this->staticClass][$this->currentString])) { $this->calledStaticMethods[$this->staticClass][$this->currentString] = array(); } $this->calledStaticMethods[$this->staticClass][$this->currentString][] = $this->lineNumber; $this->staticClass = false; } else if ($this->currentVar !== false) { if (!isset($this->calledMethods[$this->currentVar][$this->currentString])) { $this->calledMethods[$this->currentVar][$this->currentString] = array(); } $this->calledMethods[$this->currentVar][$this->currentString][] = $this->lineNumber; $this->currentVar = false; } else if ($this->inNew !== false) { $this->classInstantiated(); } else if ($this->currentString !== null) { $this->functionCalled(); } //$this->currentString = null; break; case "=": case ";": if ($this->inNew !== false) { $this->classInstantiated(); } else if ($this->inInclude !== false) { $this->fileIncluded(); } else if ($this->currentVar !== false) { $this->useMemberVar(); } $this->currentString = null; $this->currentStrings = null; break; } } /** * handle includes and requires * @access private */ function handleInclude($id,$text) { $this->inInclude = true; $this->handleConstruct($id,$text); } /** * handle String tokens * @access private */ function handleString($id,$text) { $this->currentString = $text; $this->currentStrings .= $text; } /** * handle variables * @access private */ function handleVariable($id,$text) { $this->currentString = $text; $this->currentStrings .= $text; $this->useVariable(); } /** * handle Double Colon tokens * @access private */ function handleDoubleColon($id,$text) { $this->staticClass = $this->currentString; $this->currentString = null; } /** * handle new keyword * @access private */ function handleNew($id,$text) { $this->inNew = true; } /** * handle function * @access private */ function handleFunction($id,$text) { $this->createdFunctions[] = $this->lineNumber; } /** * handle class * @access private */ function handleClass($id,$text) { $this->createdClasses[] = $this->lineNumber; } /** * Handle -> * @access private */ function handleObjectOperator($id,$text) { $this->currentVar = $this->currentString; $this->currentString = null; $this->currentStrings .= $text; } /** * handle whitespace to figure out line counts * @access private */ function handleWhitespace($id,$text) { $this->lineNumber+=substr_count($text,"\n"); if ($id == T_CLOSE_TAG) { $this->handleSimpleToken(";"); } } /** * as has been used we must have a var before it * * @access private */ function handleAs($id,$text) { $this->handleSimpleToken(";"); } /** * a language construct has been called record it * @access private */ function handleConstruct($id,$construct) { if (!isset($this->calledConstructs[$construct])) { $this->calledConstructs[$construct] = array(); } $this->calledConstructs[$construct][] = $this->lineNumber; $this->currentString = null; } /** * a class was Instantiated record it * @access private */ function classInstantiated() { if (!isset($this->classesInstantiated[$this->currentString])) { $this->classesInstantiated[$this->currentString] = array(); } $this->classesInstantiated[$this->currentString][] = $this->lineNumber; $this->inNew = false; } /** * a file was included record it * @access private */ function fileIncluded() { if (!isset($this->filesIncluded[$this->currentStrings])) { $this->filesIncluded[$this->currentStrings] = array(); } $this->filesIncluded[$this->currentStrings][] = $this->lineNumber; $this->inInclude = false; $this->currentString = null; $this->currentStrings = ""; } /** * a function was called record it * @access private */ function functionCalled($id = false) { if (!isset($this->calledFunctions[$this->currentString])) { $this->calledFunctions[$this->currentString] = array(); } $this->calledFunctions[$this->currentString][] = $this->lineNumber; $this->currentString = null; } /** * we used a member variable record it * @access private */ function useMemberVar() { if (!isset($this->usedMemberVariables[$this->currentVar][$this->currentString])) { $this->usedMemberVariables[$this->currentVar][$this->currentString] = array(); } $this->usedMemberVariables[$this->currentVar][$this->currentString][] = $this->lineNumber; $this->currentVar = false; $this->currentString = null; } /** * we used a variable record it * @access private */ function useVariable() { if (!isset($this->usedVariables[$this->currentString])) { $this->usedVariables[$this->currentString] = array(); } $this->usedVariables[$this->currentString][] = $this->lineNumber; } } ?>