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