import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Example {
public static void main(String[] args) {
final String regex = "(\\s+)(?:abstract\\s+|final\\s+|private\\s+|protected\\s+|public\\s+)?(?:static\\s+)?(function)\\s+(\\w+)\\s*(\\((?>[^()]+|(?R))*\\))|(\\{(?>[^\\{\\}]+|(?R))*\\})";
final String string = "<?php declare(strict_types=1);\n\n"
+ "namespace PHPAstVisualizer;\n\n"
+ "use phpDocumentor\\GraphViz\\Edge as GraphEdge;\n"
+ "use phpDocumentor\\GraphViz\\Graph;\n"
+ "use phpDocumentor\\GraphViz\\Node as GraphNode;\n\n"
+ "class Options {\n"
+ " private $options = [\n"
+ " 'graph' => [],\n"
+ " 'node' => ['shape' => 'rect'],\n"
+ " 'edge' => [],\n"
+ " 'childEdge' => ['style' => 'dashed','arrowhead' => 'empty'],\n"
+ " ];\n"
+ " private $name;\n"
+ "}\n\n"
+ " public function __construct(string $name, array $options = []) {\n"
+ " $this->name = $name;\n"
+ " $this->graphOptions = array_merge($this->options, $options);\n"
+ " }\n\n"
+ " public function getName(): string {\n"
+ " return $this->name;\n"
+ " }\n\n"
+ " public function graph(Graph $graph) {\n"
+ " foreach ($this->options['graph'] as $name => $value) {\n"
+ " $graph->{'set' . $name}($value);\n"
+ " }\n"
+ " }\n\n"
+ " public function node(GraphNode $node) {\n"
+ " foreach ($this->options['node'] as $name => $value) {\n"
+ " $node->{'set' . $name}($value);\n"
+ " }\n"
+ " }\n\n"
+ " public function childEdge(GraphEdge $edge) {\n"
+ " $this->edge($edge);\n"
+ " foreach ($this->options['childEdge'] as $name => $value) {\n"
+ " $edge->{'set' . $name}($value);\n"
+ " }\n"
+ " }\n\n"
+ " public function edge(GraphEdge $edge) {\n"
+ " foreach ($this->options['edge'] as $name => $value) {\n"
+ " $edge->{'set' . $name}($value);\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ " /**\n"
+ " * PHP file with an autoload function which will be included into the\n"
+ " * sandbox of the InstantSVC CodeAnalyzer\n"
+ " * @var string\n"
+ " */\n"
+ " protected $autoloadFile = '';\n\n"
+ " /**\n"
+ " * Sets the output driver and initializes\n"
+ " * @param CallgraphDriver $driver output driver to use\n"
+ " * @return PHPCallGraph\n"
+ " */\n"
+ " public function __construct(CallgraphDriver $driver = null) {\n"
+ " if ($driver != null) {\n"
+ " $this->driver = $driver;\n"
+ " } else {\n"
+ " $this->driver = new TextDriver();\n"
+ " }\n"
+ " $functions = get_defined_functions();\n"
+ " $this->internalFunctions = $functions['internal'];\n"
+ " // List of PHP keywords which could be followed by an opening parenthis\n"
+ " // taken from PHP Manual (http://www.php.net/reserved_keywords)\n"
+ " $this->internalKeywords = array(\n"
+ " 'array', 'declare', 'die', 'echo', 'elseif', 'empty', 'eval',\n"
+ " 'exit', 'for', 'foreach', 'global', 'if', 'include', 'include_once',\n"
+ " 'isset', 'list', 'print', 'require', 'require_once', 'return',\n"
+ " 'switch', 'unset', 'while', 'catch', 'or', 'and', 'xor', 'new Exception');\n"
+ " $this->constants = array_keys(get_defined_constants());\n\n"
+ " //TODO: provide setter for this\n"
+ " //$this->ignoreList = array('PHPCallGraph::setDriver', 'PHPCallGraph::__construct', 'PHPCallGraph::setShowExternalCalls', 'PHPCallGraph::setShowInternalFunctions', 'PHPCallGraph::save', 'PHPCallGraph::__toString');\n"
+ " }\n\n"
+ " public function setDriver(CallgraphDriver $driver = null) {\n"
+ " if ($driver != null) {\n"
+ " $this->driver = $driver;\n"
+ " }\n"
+ " }\n\n"
+ " /**\n"
+ " * Enable or disable printing of debug information\n"
+ " * @param boolean $enabled\n"
+ " */\n"
+ " public function setDebug($enabled = true) {\n"
+ " $this->debug = $enabled;\n"
+ " }\n\n"
+ " protected function debug($string) {\n"
+ " if ($this->debug) {\n"
+ " print \"||PHPCallGraph| \".$string.\"\\n\";\n"
+ " }\n"
+ " }\n\n"
+ " protected function info($string) {\n"
+ " if ($this->showInfo) {\n"
+ " print \"||PHPCallGraph| \".$string.\"\\n\";\n"
+ " }\n"
+ " }\n\n"
+ " protected function warning($string) {\n"
+ " if ($this->showWarnings) {\n"
+ " print \"||PHPCallGraph* *WARNING* \".$string.\"\\n\";\n"
+ " }\n"
+ " }\n\n\n"
+ " /**\n"
+ " * Sets a PHP file with an autoload function which will be included into\n"
+ " * the sandbox of the InstantSVC CodeAnalyzer\n"
+ " * @param string $filename Name of a PHP file with an autoload function\n"
+ " * @return boolean success\n"
+ " */\n"
+ " public function setAutoloadFile($filename) {\n"
+ " $returnValue = false;\n"
+ " if (!empty($filename) and is_file($filename) and is_readable($filename)) {\n"
+ " $this->autoloadFile = $filename;\n"
+ " $returnValue = true;\n"
+ " } else {\n"
+ " //TODO: throw exception\n"
+ " }\n"
+ " return $returnValue;\n"
+ " }\n\n"
+ " public function setShowExternalCalls($boolean = true) {\n"
+ " $this->showExternalCalls = $boolean;\n"
+ " }\n\n"
+ " public function setShowInternalFunctions($boolean = true) {\n"
+ " $this->showInternalFunctions = $boolean;\n"
+ " }\n\n"
+ " public function collectFileNames(array $filesOrDirs, $recursive = false) {\n"
+ " $files = array();\n"
+ " foreach ($filesOrDirs as $fileOrDir) {\n"
+ " if (is_file($fileOrDir)) {\n"
+ " $files[] = $fileOrDir;\n"
+ " } elseif (is_dir($fileOrDir)) {\n"
+ " $globbed = glob(\"$fileOrDir/*\");\n"
+ " if ($recursive) {\n"
+ " $files = array_merge($files, $this->collectFileNames($globbed, true));\n"
+ " } else {\n"
+ " foreach($globbed as $path) {\n"
+ " if (is_file($path)) {\n"
+ " $files[] = $path;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " return $files;\n"
+ " }\n\n"
+ " public function parse(array $filesOrDirs, $recursive = false) {\n"
+ " $files = $this->collectFileNames($filesOrDirs, $recursive);\n"
+ " if ($this->debug) {\n"
+ " var_dump($files);\n"
+ " }\n\n"
+ " $ca = new iscCodeAnalyzer(null);\n"
+ " $ca->setDebug($this->debug);\n"
+ " $ca->setAutoloadFile($this->autoloadFile);\n"
+ " $ca->inspectFiles($files);\n"
+ " $this->codeSummary = $ca->getCodeSummary();\n"
+ " $this->analyseCodeSummary();\n"
+ " }\n\n"
+ " public function parseFile($file) {\n"
+ " $ca = new iscCodeAnalyzer(null);\n"
+ " $ca->setDebug($this->debug);\n"
+ " $ca->setAutoloadFile($this->autoloadFile);\n"
+ " $ca->inspectFiles(array($file));\n"
+ " $this->codeSummary = $ca->getCodeSummary();\n"
+ " $this->analyseCodeSummary();\n"
+ " }\n\n"
+ " public function parseDir($dir = '.') {\n"
+ " $ca = new iscCodeAnalyzer($dir);\n"
+ " $ca->setDebug($this->debug);\n"
+ " $ca->setAutoloadFile($this->autoloadFile);\n"
+ " $ca->collect();\n"
+ " $this->codeSummary = $ca->getCodeSummary();\n"
+ " $this->analyseCodeSummary();\n"
+ " }\n\n"
+ " public function analyseCodeSummary() {\n"
+ " $this->buildLookupTables();\n\n"
+ " //TODO: analyze code in the global scope\n"
+ " // currently a workarround is to manually wrap such code\n"
+ " // in a dummy function called dummyFunctionForFile_filename_php()\n\n"
+ " // analyze functions\n"
+ " if (!empty($this->codeSummary['functions'])) {\n"
+ " foreach ($this->codeSummary['functions'] as $functionName => $function) {\n"
+ " $this->parseMethodBody(\n"
+ " '-',\n"
+ " $functionName,\n"
+ " array(),\n"
+ " array(),\n"
+ " $function['file'],\n"
+ " $function['startLine'],\n"
+ " $function['endLine']\n"
+ " );\n"
+ " }\n"
+ " }\n\n"
+ " // analyze classes\n"
+ " if (!empty($this->codeSummary['classes'])) {\n"
+ " foreach ($this->codeSummary['classes'] as $className => $class) {\n"
+ " /*\n"
+ " echo $className, \"\\n\";\n"
+ " var_export($class);\n"
+ " echo \"\\n\\n\";\n"
+ " //*/\n"
+ " if (!empty($class['methods'])) {\n"
+ " $propertyNames = array_keys($class['properties']);\n"
+ " $methodNames = array_keys($class['methods']);\n"
+ " //var_export($propertyNames);\n"
+ " //var_export($methodNames);\n"
+ " foreach ($class['methods'] as $methodName => $method) {\n"
+ " $this->parseMethodBody(\n"
+ " $className,\n"
+ " $methodName,\n"
+ " $propertyNames,\n"
+ " $methodNames,\n"
+ " $class['file'],\n"
+ " $method['startLine'],\n"
+ " $method['endLine']\n"
+ " );\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n\n"
+ " protected function buildLookupTables() {\n"
+ " if (!empty($this->codeSummary['classes'])) {\n"
+ " foreach ($this->codeSummary['classes'] as $className => $class) {\n"
+ " //currently unused\n"
+ " /*\n"
+ " if (!empty($class['properties'])) {\n"
+ " foreach ($class['properties'] as $propertyName => $property) {\n"
+ " $this->propertyLookupTable[$propertyName][] = $className;\n"
+ " }\n"
+ " }\n"
+ " //*/\n"
+ " if (!empty($class['methods'])) {\n"
+ " foreach ($class['methods'] as $methodName => $method) {\n"
+ " $this->methodLookupTable[$methodName][] = $className;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n\n"
+ " /**\n"
+ " * @param string $className\n"
+ " * @param string $methodName\n"
+ " * @param array $propertyNames\n"
+ " * @param array $methodNames\n"
+ " * @param string $file\n"
+ " * @param integer $startLine\n"
+ " * @param integer $endLine\n"
+ " */\n"
+ " public function parseMethodBody(\n"
+ " $className,\n"
+ " $methodName,\n"
+ " Array $propertyNames,\n"
+ " Array $methodNames,\n"
+ " $file,\n"
+ " $startLine,\n"
+ " $endLine\n"
+ " ) {\n"
+ " if ($className == '-') { // we are analyzing a function not a method\n"
+ " if (substr($methodName, 0, strlen('dummyFunctionForFile_')) == 'dummyFunctionForFile_') {\n"
+ " // the function has been introduced manually to encapsulate code in the global scope\n"
+ " $callerName = str_replace('_', '.', substr($methodName, strlen('dummyFunctionForFile_'))) . '(File)';\n"
+ " } else {\n"
+ " $callerName = $methodName . $this->generateParametersForSignature($this->codeSummary['functions'][$methodName]['params']);\n"
+ " }\n"
+ " } else {\n"
+ " //TODO: visibilities\n"
+ " $callerName = $className . '::' . $methodName . $this->generateParametersForSignature($this->codeSummary['classes'][$className]['methods'][$methodName]['params']);\n"
+ " }\n\n"
+ " $callerNameWithoutParameterList = substr($callerName, 0, strpos($callerName, '('));\n"
+ " \n"
+ " if (!in_array($callerNameWithoutParameterList, $this->ignoreList)) {\n"
+ " $this->debug('phpCallGraph: analyzing ' . $callerName);\n"
+ " $offset = $startLine - 1;\n"
+ " $length = $endLine - $startLine + 1;\n\n"
+ " // obtain source code\n"
+ " $memberCode = implode('', array_slice(file($file), $offset, $length));\n"
+ " $memberCode = \"<?php\\nclass $className {\\n\" . $memberCode . \"}\\n?>\\n\";\n"
+ " //echo $memberCode;\n\n"
+ " $this->debug(\"= Analyzing $callerName =\");\n"
+ " $this->info(\" defined in $file on line $offset\");\n"
+ " $this->driver->startFunction($offset, $file, $callerName, $memberCode);\n\n"
+ " $insideDoubleQuotedString = false;\n"
+ " $lineNumber = $offset - 1;\n"
+ " $blocksStarted = 0;\n"
+ " // parse source code\n"
+ " $tokens = token_get_all($memberCode);\n"
+ " /*\n"
+ " if ($methodName == '__construct') {\n"
+ " print_r($tokens);\n"
+ " }\n"
+ " //*/\n\n"
+ " //TODO: implement a higher level API for working with PHP parser tokens (e.g. TokenIterator)\n"
+ " foreach ($tokens as $i => $token) {\n"
+ " //TODO: obtain method signature directly from the source file\n"
+ " if (is_array($token)) {\n"
+ " $lineNumber+= substr_count($token[1], \"\\n\");\n"
+ " }\n\n"
+ " /*\n"
+ " if (count($token) == 3) {\n"
+ " echo \"\\t\", token_name($token[0]), \"\\n\";\n"
+ " echo \"\\t\\t\", $token[1], \"\\n\";\n"
+ " } else {\n"
+ " echo \"\\t\", $token[0], \"\\n\";\n"
+ " }\n"
+ " //*/\n\n"
+ " // skip call analysis for the method signature\n"
+ " if ($blocksStarted < 2) {\n"
+ " // method body not yet started\n"
+ " if ($token[0] == '{') {\n"
+ " ++$blocksStarted;\n"
+ " }\n"
+ " continue;\n"
+ " }\n\n"
+ " if (!$insideDoubleQuotedString and $token == '\"') {\n"
+ " $insideDoubleQuotedString = true;\n"
+ " } elseif ($insideDoubleQuotedString and $token == '\"') {\n"
+ " $insideDoubleQuotedString = false;\n"
+ " } elseif (!$insideDoubleQuotedString and $token != '\"') {\n"
+ " if ($token[0] == T_STRING\n"
+ " //and ($token[1] != $className or $tokens[$i - 2][0] == T_NEW )\n"
+ " //and $token[1] != $methodName\n"
+ " ) {\n"
+ " if (\n"
+ " !in_array($token[1], $propertyNames) //TODO: property name equals name of a function or method\n"
+ " and !in_array($token[1], $this->constants) //TODO: constant name equals name of a function or method\n"
+ " and $token[1] != 'true'\n"
+ " and $token[1] != 'false'\n"
+ " and $token[1] != 'null'\n"
+ " ) {\n"
+ " $previousPreviousPreviousToken = $tokens[ $i - 3 ];\n"
+ " $previousPreviousToken = $tokens[ $i - 2 ];\n"
+ " $previousToken = $tokens[ $i - 1 ];\n"
+ " $nextToken = $tokens[ $i + 1 ];\n"
+ " $tokenAfterNext = $tokens[ $i + 2 ];\n\n"
+ " $this->info($this->getTokenValues($tokens, $i));\n\n\n"
+ " if ($nextToken[0] == T_DOUBLE_COLON) {\n"
+ " // beginning of a call to a static method\n"
+ " //nop\n"
+ " continue;\n"
+ " } elseif (\n"
+ " (\n"
+ " $tokens[ $i - 4][0] == T_CATCH\n"
+ " and $previousPreviousPreviousToken[0] == T_WHITESPACE\n"
+ " and $previousPreviousToken == '('\n"
+ " and $previousToken[0] == T_WHITESPACE\n"
+ " )\n"
+ " or\n"
+ " (\n"
+ " $previousPreviousPreviousToken[0] == T_CATCH\n"
+ " and $previousPreviousToken[0] == T_WHITESPACE\n"
+ " and $previousToken == '('\n"
+ " )\n"
+ " or\n"
+ " (\n"
+ " $previousPreviousToken[0] == T_CATCH\n"
+ " and $previousToken == '('\n"
+ " )\n"
+ " ){\n"
+ " // catch block\n"
+ " continue;\n"
+ " } elseif ($previousPreviousToken[0] == T_NEW){\n"
+ " $this->debug('Found object creation with new operator');\n"
+ " if (!$this->showExternalCalls) {\n"
+ " continue;\n"
+ " }\n"
+ " $calleeClass = $token[1];\n"
+ " if (isset($this->codeSummary['classes'][$calleeClass])) {\n"
+ " // find constructor method\n"
+ " if (isset($this->codeSummary['classes'][$calleeClass]['methods']['__construct'])) {\n"
+ " $calleeName = \"$calleeClass::__construct\"\n"
+ " . $this->generateParametersForSignature($this->codeSummary['classes'][$calleeClass]['methods']['__construct']['params']);\n"
+ " } elseif (isset($this->codeSummary['classes'][$calleeClass]['methods'][$calleeClass])) {\n"
+ " $calleeName = \"$calleeClass::$calleeClass\"\n"
+ " . $this->generateParametersForSignature($this->codeSummary['classes'][$calleeClass]['methods'][$calleeClass]['params']);\n"
+ " } else {\n"
+ " $calleeName = \"$calleeClass::__construct()\";\n"
+ " }\n"
+ " $calleeFile = $this->codeSummary['classes'][$calleeClass]['file'];\n"
+ " } else {\n"
+ " // TODO: decide how this case should be handled (could be a PEAR class or a class of a PHP extension, e.g. GTK)\n"
+ " //if ($this->showInternalFunctions)\n"
+ " $calleeName = \"$calleeClass::__construct()\"; // TODO: it could also be $calleeClass::$calleeClass() implemented in PHP4-style, however if we don't have the code, we can't now\n"
+ " $calleeFile = '';\n"
+ " }\n\n"
+ " $this->info($this->getTokenValues($tokens, $i));\n"
+ " $this->recordVariableAsType($calleeClass, $tokens[$i-6][1]);\n"
+ " } elseif (\n"
+ " (\n"
+ " isset($previousPreviousToken[1]) and $previousPreviousToken[1] == '$this'\n"
+ " and $previousToken[0] == T_OBJECT_OPERATOR\n"
+ " and in_array($token[1], $methodNames)\n"
+ " )\n"
+ " or\n"
+ " (\n"
+ " isset($previousPreviousToken[1]) and $previousPreviousToken[1] == 'self'\n"
+ " and $previousToken[0] == T_DOUBLE_COLON\n"
+ " and in_array($token[1], $methodNames)\n"
+ " )\n"
+ " ){\n"
+ " $this->info('internal method call ($this-> and self:: and $className::)');\n"
+ " $calleeName = \"$className::{$token[1]}\" . $this->generateParametersForSignature($this->codeSummary['classes'][$className]['methods'][$token[1]]['params']);\n"
+ " $calleeFile = $file;\n"
+ " } elseif ($previousToken[0] == T_OBJECT_OPERATOR) {\n"
+ " $this->debug('External method call or property access'); \n"
+ " //TODO: what if a object holds another instance of its class\n"
+ " if (!$this->showExternalCalls) {\n"
+ " continue;\n"
+ " }\n"
+ " if ($nextToken == '(' or ($nextToken[0] == T_WHITESPACE and $tokenAfterNext == '(')) {\n"
+ " $calleeName = $token[1];\n"
+ " $this->debug(\"Calling for $calleeName\");\n\n"
+ " $variable = $tokens[$i-2][1];\n"
+ " $this->debug(\"Variable = $variable\");\n"
+ " $calleeClass = $this->lookupTypeForVariable($variable);\n"
+ " $this->debug('found as ' . $calleeClass);\n"
+ " if ($calleeClass) {\n"
+ " $calleeParams = $this->generateParametersForSignature(\n"
+ " $this->codeSummary['classes'][$calleeClass]['methods'][$calleeName]['params']\n"
+ " );\n"
+ " $calleeFile = $this->codeSummary['classes'][$calleeClass]['file'];\n"
+ " } else {\n"
+ " if (\n"
+ " isset($this->methodLookupTable[$calleeName])\n"
+ " and count($this->methodLookupTable[$calleeName]) == 1\n"
+ " ) {\n"
+ " // there is only one class having a method with this name\n"
+ " // SMELL: but if the user only registers part of a system the only one hit \n"
+ " // is not necessarily valid.\n"
+ " $calleeClass = $this->methodLookupTable[$calleeName][0];\n"
+ " if (isset($this->codeSummary['classes'][$calleeClass])) {\n"
+ " $calleeParams = $this->generateParametersForSignature(\n"
+ " $this->codeSummary['classes'][$calleeClass]['methods'][$calleeName]['params']\n"
+ " );\n"
+ " $calleeFile = $this->codeSummary['classes'][$calleeClass]['file'];\n\n"
+ " $this->info(\"RECORDING CLASS OF $previousPreviousToken[1] VARIABLE to be $calleeClass\\n\");\n"
+ " $this->info($this->getTokenValues($tokens, $i));\n\n"
+ " } else {\n"
+ " $this-warning(\"calleeClass is unset\");\n"
+ " $calleeParams = null;\n"
+ " $calleeFile = null;\n"
+ " }\n"
+ " } else {\n"
+ " $numEntries = count($this->methodLookupTable[$calleeName]);\n"
+ " if ($numEntries == 0) {\n"
+ " $this->warning(\"method $calleeName was called, but I have no record for that\");\n"
+ " } else {\n"
+ " $this->warning(\"I have $numEntries for $calleeName!\");\n"
+ " }\n\n"
+ " $this->info($this->getTokenValues($tokens, $i));\n\n"
+ " $calleeClass = '';\n"
+ " $calleeParams = '()';\n"
+ " $calleeFile = '';\n"
+ " }\n"
+ " }\n"
+ " $calleeName = \"$calleeClass::$calleeName$calleeParams\";\n"
+ " } else {\n"
+ " $this->info(\"Property access\");\n"
+ " continue;\n"
+ " }\n"
+ " } elseif ($previousToken[0] == T_DOUBLE_COLON){\n"
+ " $this->debug(\"static external method call\");\n"
+ " if (!$this->showExternalCalls) {\n"
+ " continue;\n"
+ " }\n"
+ " if ($nextToken != '(' and !($nextToken[0] == T_WHITESPACE and $tokenAfterNext == '(')) {\n"
+ " // constant access\n"
+ " continue;\n"
+ " }\n"
+ " $calleeClass = $previousPreviousToken[1];\n"
+ " $calleeMethod = $token[1];\n"
+ " $calleeFile = '';\n"
+ " $calleeParams = '()';\n"
+ " // parent::\n"
+ " if ($calleeClass == 'parent' and !empty($this->codeSummary['classes'][$className]['parentClass'])) {\n"
+ " $calleeClass = $this->codeSummary['classes'][$className]['parentClass'];\n"
+ " }\n"
+ " if (isset($this->codeSummary['classes'][$calleeClass])) {\n"
+ " $calleeFile = $this->codeSummary['classes'][$calleeClass]['file'];\n"
+ " if (isset($this->codeSummary['classes'][$calleeClass]['methods'][$calleeMethod]['params'])) {\n"
+ " $calleeParams = $this->generateParametersForSignature($this->codeSummary['classes'][$calleeClass]['methods'][$calleeMethod]['params']);\n"
+ " }\n"
+ " }\n"
+ " $calleeName = \"$calleeClass::$calleeMethod$calleeParams\";\n"
+ " //TODO: handle self::myMethod(); $className::myMethod(); here => abolish internal method call case\n"
+ " } else {\n\n"
+ " $calledFunction = $token[1];\n"
+ " $calleeFile = '';\n"
+ " $calleeParams = '()';\n"
+ " \n"
+ " $this->info(\"Function call: \".$calledFunction);\n\n"
+ " if (in_array($calledFunction, $this->internalFunctions)) {\n"
+ " if (!$this->showInternalFunctions) {\n"
+ " continue;\n"
+ " }\n"
+ " } else {\n"
+ " if (!$this->showExternalCalls) {\n"
+ " continue;\n"
+ " }\n"
+ " if (isset($this->codeSummary['functions'][$calledFunction])) {\n"
+ " $calleeFile = $this->codeSummary['functions'][$calledFunction]['file'];\n"
+ " if (isset($this->codeSummary['functions'][$calledFunction]['params'])) {\n"
+ " $calleeParams = $this->generateParametersForSignature($this->codeSummary['functions'][$calledFunction]['params']);\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " $calleeName = $calledFunction . $calleeParams;\n"
+ " }\n"
+ " $this->debug(\"---> $calleeName called from line $lineNumber and defined in $calleeFile\");\n"
+ " $this->driver->addCall($lineNumber, $calleeFile, $calleeName);\n"
+ " }\n"
+ " }\n"
+ " } else {\n"
+ " //TODO: parse calls inside double quoted strings\n"
+ " $this->info(' ignoring code inside \"\"');\n"
+ " }\n"
+ " }\n"
+ " $this->debug('== endFunction ==');\n"
+ " $this->driver->endFunction();\n"
+ " }\n"
+ " }\n"
+ "}\n\n"
+ " public function generateParametersForSignature($parameters) {\n"
+ " $result = '(';\n"
+ " if (!empty($parameters)) {\n"
+ " foreach($parameters as $parameterName => $parameter) {\n"
+ " if ($parameter['byReference']) {\n"
+ " $result.= '&';\n"
+ " }\n"
+ " $result.= '$' . $parameterName . ', ';\n"
+ " }\n"
+ " $result = substr($result, 0, -2);\n"
+ " }\n"
+ " $result.= ')';\n"
+ " return $result;\n"
+ " }\n"
+ "}\n"
+ " public function __toString() {\n"
+ " return $this->driver->__toString();\n"
+ " }\n\n"
+ " public function save($file) {\n"
+ " return file_put_contents($file, $this->__toString());\n"
+ " }\n\n"
+ " \n"
+ " protected function getTokenValues($tokens, $i) {\n"
+ " $width = 10;\n"
+ " $pad = ' ';\n"
+ " $out = \"\\n\";\n"
+ " $start = -5;\n"
+ " $end = 4;\n"
+ " $headerLine = '';\n"
+ " $rowLine = '';\n"
+ " $tokenLine = '';\n"
+ " for ($j = $start; $j <= $end; $j++) {\n"
+ " $n = ($i + $j);\n"
+ " $currentToken = $tokens[$n];\n"
+ " $tokenType = '';\n"
+ " $cell = '';\n"
+ " $header = '';\n"
+ " if (is_array($currentToken)) {\n"
+ " $mainValue = $currentToken[1];\n"
+ " $mainValue = str_replace(\"\\n\", '\\n', $mainValue);\n"
+ " $mainValue = str_replace(\"\\t\", '\\t', $mainValue);\n"
+ " if ($mainVlaue = '') {\n"
+ " $mainValue = 'nil';\n"
+ " } \n"
+ " $cell = $mainValue;\n"
+ " $tokenType = token_name($currentToken[0]);\n"
+ " } else {\n"
+ " $cell = '\"'.$currentToken.'\"';\n"
+ " }\n"
+ " $cell = $cell;\n"
+ " $cell = str_pad($cell, $width, $pad);\n"
+ " $tokenType = str_pad(substr($tokenType,0,$width), $width, $pad);\n"
+ " $header = '['.$j.']';\n"
+ " $header = str_pad($header, $width, $pad);\n\n"
+ " $headerLine .= $header.' ';\n"
+ " $rowLine .= $cell.' ';\n"
+ " $tokenLine .= $tokenType.' ';\n"
+ " }\n"
+ " $out .= $headerLine.\"\\n\";\n"
+ " $out .= $rowLine.\"\\n\";\n"
+ " $out .= $tokenLine.\"\\n\";\n"
+ " return $out;\n"
+ " }\n\n"
+ " protected $variableTypes = array();\n"
+ " protected function recordVariableAsType($class, $variable) {\n"
+ " $this->debug(\"RECORDING $variable AS TYPE $class\");\n"
+ " $this->variableTypes[$variable] = $class;\n"
+ " }\n\n\n"
+ " /**\n"
+ " * Returns a recorded type for a given variable, unless the variable is named '$self'\n"
+ " * in which case returns the class name.\n"
+ " * If no match is found returns null.\n"
+ " */\n"
+ " protected function lookupTypeForVariable($variable) {\n"
+ " $recordedType = $this->variableTypes[$variable];\n"
+ " if ($recordedType) {\n"
+ " $type = $recordedType;\n"
+ " } else {\n"
+ " $this->warning(\"No recorded type for $variable\");\n"
+ " $type = null;\n"
+ " }\n"
+ " $this->info('LOOKUP FOR '.$variable.' returns '.$type);\n"
+ " return $type;\n"
+ " }\n"
+ "}\n"
+ "?>\n"
+ "<?php\n"
+ "/**\n"
+ " * Implementation of a call graph generation strategy wich renders a graph with\n"
+ " * dot.\n"
+ " *\n"
+ " * PHP version 5\n"
+ " *\n"
+ " * This file is part of phpCallGraph.\n"
+ " *\n"
+ " * PHPCallGraph is free software; you can redistribute it and/or modify\n"
+ " * it under the terms of the GNU General Public License as published by\n"
+ " * the Free Software Foundation; either version 3 of the License, or\n"
+ " * (at your option) any later version.\n"
+ " *\n"
+ " * PHPCallGraph is distributed in the hope that it will be useful,\n"
+ " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+ " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
+ " * GNU General Public License for more details.\n"
+ " *\n"
+ " * You should have received a copy of the GNU General Public License\n"
+ " * along with this program. If not, see <http://www.gnu.org/licenses/>.\n"
+ " *\n"
+ " * @package PHPCallGraph\n"
+ " * @author Falko Menge <fakko at users dot sourceforge dot net>\n"
+ " * @copyright 2007-2009 Falko Menge\n"
+ " * @license http://www.gnu.org/licenses/gpl.txt GNU General Public License\n"
+ " */\n\n"
+ "// reduce warnings because of PEAR dependency\n"
+ "error_reporting(E_ALL ^ E_NOTICE);\n\n"
+ "require_once 'CallgraphDriver.php';\n"
+ "require_once 'Image/GraphViz.php';\n\n"
+ "/**\n"
+ " * Implementation of a call graph generation strategy wich renders a graph with\n"
+ " * dot.\n"
+ " */\n"
+ "class GraphVizDriver implements CallgraphDriver {\n\n"
+ " protected $outputFormat;\n"
+ " protected $dotCommand;\n"
+ " protected $useColor = true;\n"
+ " protected $graph;\n"
+ " protected $currentCaller = '';\n"
+ " protected $internalFunctions;\n"
+ "}\n"
+ " /**\n"
+ " * @return CallgraphDriver\n"
+ " */\n"
+ " public function __construct($outputFormat = 'png', $dotCommand = 'dot') {\n"
+ " $this->initializeNewGraph();\n"
+ " $this->setDotCommand($dotCommand);\n"
+ " $this->setOutputFormat($outputFormat);\n"
+ " $functions = get_defined_functions();\n"
+ " $this->internalFunctions = $functions['internal'];\n"
+ " \n"
+ " }\n\n"
+ " /**\n"
+ " * @return void\n"
+ " */\n"
+ " public function reset() {\n"
+ " $this->initializeNewGraph();\n"
+ " }\n\n"
+ " /**\n"
+ " * @return void\n"
+ " */\n"
+ " protected function initializeNewGraph() {\n"
+ " $this->graph = new Image_GraphViz(\n"
+ " true,\n"
+ " array(\n"
+ " 'fontname' => 'Verdana',\n"
+ " 'fontsize' => 12.0,\n"
+ " //'fontcolor' => 'gray5',\n"
+ " 'rankdir' => 'LR', // left-to-right\n"
+ " )\n"
+ " );\n"
+ " $this->graph->dotCommand = $this->dotCommand;\n"
+ " }\n\n"
+ " /**\n"
+ " * Sets path to GraphViz/dot command\n"
+ " * @param string $dotCommand Path to GraphViz/dot command\n"
+ " * @return void\n"
+ " */\n"
+ " public function setDotCommand($dotCommand = 'dot') {\n"
+ " $this->dotCommand = $dotCommand;\n"
+ " $this->graph->dotCommand = $dotCommand;\n"
+ " }\n\n"
+ " /**\n"
+ " * Sets output format\n"
+ " * @param string $outputFormat One of the output formats supported by GraphViz/dot\n"
+ " * @return void\n"
+ " */\n"
+ " public function setOutputFormat($outputFormat = 'png') {\n"
+ " $this->outputFormat = $outputFormat;\n"
+ " }\n\n"
+ " /**\n"
+ " * Enables or disables the use of color\n"
+ " * @param boolean $boolean True if color should be used\n"
+ " * @return void\n"
+ " */\n"
+ " public function setUseColor($boolean = true) {\n"
+ " $this->useColor = $boolean;\n"
+ " }\n\n"
+ " /**\n"
+ " * @param integer $line\n"
+ " * @param string $file\n"
+ " * @param string $name\n"
+ " * @return void\n"
+ " */\n"
+ " public function startFunction($line, $file, $name, $memberCode) {\n"
+ " $this->addNode($name);\n"
+ " $this->currentCaller = $name;\n"
+ " }\n\n"
+ " /**\n"
+ " * @param integer $line\n"
+ " * @param string $file\n"
+ " * @param string $name\n"
+ " * @return void\n"
+ " */\n"
+ " public function addCall($line, $file, $name) {\n"
+ " $this->addNode($name);\n"
+ " $this->graph->addEdge(array($this->currentCaller => $name));\n"
+ " }\n\n"
+ " /**\n"
+ " * @return void\n"
+ " */\n"
+ " protected function addNode($name) {\n"
+ " $nameParts = explode('::', $name);\n"
+ " $cluster = 'default';\n"
+ " $label = $name;\n"
+ " $color = 'lavender'; //lightblue2, lightsteelblue2, azure2, slategray2\n"
+ " if (count($nameParts) == 2) { // method call\n"
+ " if (empty($nameParts[0])) {\n"
+ " $cluster = 'class is unknown';\n"
+ " } else {\n"
+ " $cluster = $nameParts[0];\n"
+ " }\n"
+ " // obtain method name\n"
+ " $label = $nameParts[1];\n"
+ " }\n"
+ " // remove parameter list\n"
+ " $label = substr($label, 0, strpos($label, '('));\n\n"
+ " if (count($nameParts) == 1) { // function call\n"
+ " if (in_array($label, $this->internalFunctions)) { // call to internal function\n"
+ " $cluster = 'internal PHP functions';\n"
+ " }\n"
+ " }\n"
+ " $this->graph->addNode(\n"
+ " $name,\n"
+ " array(\n"
+ " 'fontname' => 'Verdana',\n"
+ " 'fontsize' => 12.0,\n"
+ " //'fontcolor' => 'gray5',\n"
+ " 'label' => $label,\n"
+ " //'style' => 'rounded' . ($this->useColor ? ',filled' : ''), // produces errors in rendering\n"
+ " 'style' => ($this->useColor ? 'filled' : 'rounded'),\n"
+ " 'color' => ($this->useColor ? $color : 'black'),\n"
+ " 'shape' => ($this->useColor ? 'ellipse' : 'rectangle'),\n"
+ " ),\n"
+ " $cluster\n"
+ " );\n"
+ " //*\n"
+ " $this->graph->addCluster(\n"
+ " $cluster,\n"
+ " $cluster,\n"
+ " array(\n"
+ "// 'style' => ($this->useColor ? 'filled' : ''),\n"
+ " 'color' => 'gray20',\n"
+ "// 'bgcolor' => '',\n"
+ " )\n"
+ " );\n"
+ " //*/\n"
+ " }\n\n"
+ " /**\n"
+ " * @return void\n"
+ " */\n"
+ " public function endFunction() {\n"
+ " }\n\n"
+ " /**\n"
+ " * @return string\n"
+ " */\n"
+ " public function __toString() {\n"
+ " return $this->graph->fetch($this->outputFormat);\n"
+ " }\n"
+ "}\n"
+ "?>\n";
final Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.COMMENTS | Pattern.MULTILINE | Pattern.DOTALL);
final Matcher matcher = pattern.matcher(string);
while (matcher.find()) {
System.out.println("Full match: " + matcher.group(0));
for (int i = 1; i <= matcher.groupCount(); i++) {
System.out.println("Group " + i + ": " + matcher.group(i));
}
}
}
}
Please keep in mind that these code samples are automatically generated and are not guaranteed to work. If you find any syntax errors, feel free to submit a bug report. For a full regex reference for Java, please visit: https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html