forked from extern/egroupware
472 lines
10 KiB
PHP
472 lines
10 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* A class for performing code analysis for php scripts
|
||
|
* It is designed to be the heart of a code limiting script
|
||
|
* to use with Savant {@link http://phpsavant.com}
|
||
|
*
|
||
|
* This code should be php4 compatiable but i've only run it in php5 and some of the Tokenizer constants have changed
|
||
|
*
|
||
|
* @author Joshua Eichorn <josh@bluga.net>
|
||
|
* @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:
|
||
|
* <code>
|
||
|
* $analyzer = new PHPCodeAnalyzer();
|
||
|
* $analyzer->source = file_get_contents(__FILE__);
|
||
|
* $analyzer->analyze();
|
||
|
* print_r($analyzer->calledMethods);
|
||
|
* </code>
|
||
|
*
|
||
|
* @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 <josh@bluga.net>
|
||
|
*/
|
||
|
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<br>\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;
|
||
|
}
|
||
|
}
|
||
|
?>
|