* @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;
}
}
?>