public function isNotLocalClass() { $linksUp = Token::linksAsList(); $this->addMethod(<<<GREMLIN sideEffect{ inside = it.get().value("fullnspath"); } .where( __.repeat( __.in({$linksUp}) ).until( hasLabel("Class") ).filter{ it.get().value("fullnspath") == inside; }.count().is(eq(0)) ) GREMLIN ); return $this; }
public function run() { $linksIn = Token::linksAsList(); // processing '\parent' fullnspath $query = <<<GREMLIN g.V().hasLabel("Identifier").filter{ it.get().value("fullnspath").toLowerCase() == "\\\\parent"} .where( __.until( and( hasLabel("Class"), __.out("NAME").not(has("atom", "Void")) ) ).repeat(__.in({$linksIn})).out("EXTENDS") ) .property('fullnspath', __.until( and( hasLabel("Class"), __.out("NAME").not(has("atom", "Void")) ) ).repeat(__.in({$linksIn})).out("EXTENDS").values("fullnspath") ) .where( __.until( and( hasLabel("Class"), __.out("NAME").not(has("atom", "Void")) ) ).repeat(__.in({$linksIn})).out("EXTENDS").in("DEFINITION") ) .addE('DEFINITION').from( __.until( and( hasLabel("Class"), __.out("NAME").not(has("atom", "Void")) ) ).repeat(__.in({$linksIn})).out("EXTENDS").in("DEFINITION") ) GREMLIN; $this->gremlin->query($query); display("\\parent to fullnspath\n"); // processing '\self' fullnspath $query = <<<GREMLIN g.V().hasLabel("Identifier").filter{ it.get().value("fullnspath").toLowerCase() == "\\\\self"} .where( __.until( and( hasLabel("Class", "Interface", "Trait"), __.out("NAME").not(has("atom", "Void")) ) ).repeat(__.in({$linksIn})) ) .property('fullnspath', __.until( and( hasLabel("Class", "Interface", "Trait"), __.out("NAME").not(has("atom", "Void")) ) ).repeat(__.in({$linksIn})).out("NAME").values("fullnspath") ) .addE('DEFINITION').from( __.until( and( hasLabel("Class", "Interface", "Trait"), __.out("NAME").not(has("atom", "Void")) ) ).repeat(__.in({$linksIn})) ) GREMLIN; $this->gremlin->query($query); display('\\self to fullnspath'); // processing '\static' fullnspath $query = <<<GREMLIN g.V().hasLabel("Identifier").filter{ it.get().value("fullnspath").toLowerCase() == "\\\\static"} .where( __.until( and( hasLabel("Class", "Trait"), __.out("NAME").not(has("atom", "Void")) ) ).repeat(__.in({$linksIn})) ) .property('fullnspath', __.until( and( hasLabel("Class", "Trait"), __.out("NAME").not(has("atom", "Void")) ) ).repeat(__.in({$linksIn})).out("NAME").values("fullnspath") ) .addE('DEFINITION').from( __.until( and( hasLabel("Class", "Trait"), __.out("NAME").not(has("atom", "Void")) ) ).repeat(__.in({$linksIn})) ) GREMLIN; $this->gremlin->query($query); display('\\static to fullnspath'); // Create link between Class constant and definition $query = <<<'GREMLIN' g.V().hasLabel('Staticconstant').as('first') .out('CONSTANT').sideEffect{name = it.get().value("code");}.select('first') .out('CLASS').hasLabel("Identifier", "Nsname").sideEffect{classe = it.get().value("fullnspath");}.in('DEFINITION') .where( __.sideEffect{classes = [];} .emit(hasLabel("Class")).repeat( out("EXTENDS").in("DEFINITION") ).times(15) .out("BLOCK").out("ELEMENT").hasLabel("Const").out("CONST").as('const') .out("NAME").filter{ it.get().value("code") == name; }.select('const') .sideEffect{classes.add(it.get()); } .fold() ) .map{classes[0]}.as('theClass') .addE('DEFINITION').from( 'first' ) GREMLIN; $this->gremlin->query($query); display('Create link between Class constant and definition'); // Create propertyname for Property Definitions $query = <<<GREMLIN g.V().hasLabel("Ppp", "Var").out("PPP").as("ppp") .coalesce( out("LEFT"), __.filter{ true } ) .sideEffect{ propertyname = it.get().value('code').toString().substring(1, it.get().value('code').size()); } .select("ppp") .sideEffect{ it.get().property('propertyname', propertyname); } GREMLIN; $this->gremlin->query($query); display('set propertyname'); // update fullnspath with fallback for functions $query = <<<GREMLIN g.V().hasLabel("Functioncall").as("a") .has("fullnspath", without('')) .has('token', within('T_STRING', 'T_NS_SEPARATOR')) .where( __.in("NEW", "METHOD").count().is(eq(0))) .sideEffect{ fullnspath = it.get().value("fullnspath")} .in('DEFINITION') .filter{ it.get().value("fullnspath") != fullnspath} .sideEffect{ fullnspath = it.get().value("fullnspath")} .select("a") .sideEffect{ it.get().property("fullnspath", fullnspath ); } GREMLIN; $this->gremlin->query($query); display('fallback for global functioncall'); // update fullnspath with fallback for functions $query = <<<GREMLIN g.V().hasLabel("Functioncall").has("fullnspath", without('')) .has('token', within('T_STRING', 'T_NS_SEPARATOR')) .where( __.in("NEW", "METHOD", "DEFINITION").count().is(eq(0))) .sideEffect{ fullnspath = it.get().vertices(OUT, 'NAME').next().value("fullnspath").toString().toLowerCase(); it.get().property("fullnspath", fullnspath ); } GREMLIN; $this->gremlin->query($query); display('refine functioncall fullnspath'); // update fullnspath with fallback for functions $query = <<<GREMLIN g.V().hasLabel("Identifier", "Nsname").as("a") .has("fullnspath", without('')) .has('token', within('T_STRING', 'T_NS_SEPARATOR')) .where( __.in("NEW", "METHOD", "NAME", "SUBNAME").count().is(eq(0))) .sideEffect{ fullnspath = it.get().value("fullnspath")} .in('DEFINITION').out("NAME") .filter{ it.get().value("fullnspath") != fullnspath} .sideEffect{ fullnspath = it.get().value("fullnspath")} .select("a") .sideEffect{ it.get().property("fullnspath", fullnspath ); } GREMLIN; $this->gremlin->query($query); display('fallback for global constants'); // fallback for PHP and ext, class, function, constant // update fullnspath with fallback for functions $pathDocs = $this->config->dir_root . '/data/analyzers.sqlite'; $docs = new Docs($pathDocs); $exts = $docs->listAllAnalyzer('Extensions'); $exts[] = 'php_constants'; $c = array(); $f = array(); foreach ($exts as $ext) { $inifile = str_replace('Extensions\\Ext', '', $ext) . '.ini'; $fullpath = $this->config->dir_root . '/data/' . $inifile; $iniFile = parse_ini_file($fullpath); if (!empty($iniFile['constants'][0])) { $c[] = $iniFile['constants']; } if (!empty($iniFile['functions'][0])) { $f[] = $iniFile['functions']; } } $constants = call_user_func_array('array_merge', $c); $constants = array_filter($constants, function ($x) { return strpos($x, '\\') === false; }); $constants = array_map('strtolower', $constants); $query = <<<GREMLIN g.V().hasLabel("Identifier").where( __.in("DEFINITION", "NEW", "USE", "NAME", "EXTENDS", "IMPLEMENTS", "CLASS", "CONST", "CONSTANT", "TYPEHINT", "FUNCTION", "GROUPUSE", "SUBNAME").count().is(eq(0)) ) .filter{ it.get().value("code").toLowerCase() in arg1 } .sideEffect{ fullnspath = "\\\\" + it.get().value("code").toLowerCase(); it.get().property("fullnspath", fullnspath); } GREMLIN; $this->gremlin->query($query, array('arg1' => $constants)); display('spot PHP / ext constants'); $query = 'g.V().hasLabel("Const").out("CONST").out("NAME").filter{ (it.get().value("fullnspath") =~ "^\\\\\\\\[^\\\\\\\\]+\\$" ).getCount() > 0 }.values("code")'; $constants = $this->gremlin->query($query); $constantsGlobal = $constants->results; $query = 'g.V().hasLabel("Const").out("CONST").out("NAME").filter{ (it.get().value("fullnspath") =~ "^\\\\\\\\[^\\\\\\\\]+\\$" ).getCount() == 0 }.values("fullnspath")'; $constants = $this->gremlin->query($query); $constantsDefinitions = $constants->results; $query = <<<GREMLIN g.V().hasLabel("Identifier").where( __.in("DEFINITION", "NEW", "USE", "NAME", "EXTENDS", "IMPLEMENTS", "CLASS", "CONST", "CONSTANT", "TYPEHINT", "FUNCTION", "GROUPUSE", "SUBNAME").count().is(eq(0)) ) .filter{ it.get().value("code") in arg1 } .filter{ !(it.get().value("fullnspath").toLowerCase() in arg2) } .sideEffect{ name = it.get().value("code"); } .sideEffect{ fullnspath = "\\\\" + it.get().value("code").toLowerCase(); it.get().property("fullnspath", fullnspath); }.addE("DEFINITION").from( g.V().hasLabel("Const").out("CONST").out("NAME").filter{ it.get().value("code") == name} ) GREMLIN; $this->gremlin->query($query, array('arg1' => $constantsGlobal, 'arg2' => $constantsDefinitions)); display('spot constants that falls back on global constants'); $functions = call_user_func_array('array_merge', $f); $functions = array_filter($functions, function ($x) { return strpos($x, '\\') === false; }); $functions = array_map('strtolower', $functions); $query = <<<GREMLIN g.V().hasLabel("Functioncall").not(has("token", "T_OPEN_TAG_WITH_ECHO")) .filter{ it.get().value("code").toLowerCase() in arg1 } .where( __.in("DEFINITION").count().is(eq(0)) ) .sideEffect{ fullnspath = "\\\\" + it.get().value("code").toLowerCase(); it.get().property("fullnspath", fullnspath); } GREMLIN; $this->gremlin->query($query, array('arg1' => $functions)); display('mark PHP native functions call'); // Define-style constant definitions $query = <<<GREMLIN g.V().hasLabel("Functioncall").has("fullnspath", "\\\\define") .out("ARGUMENTS").out("ARGUMENT").has("rank", 0) .hasLabel("String").has("noDelimiter") .map{ s = it.get().value("noDelimiter").toString().toLowerCase(); if ( s.substring(0,1) != "\\\\") { s = "\\\\" + s; } it.get().property("fullnspath", s); s; }.unique(); GREMLIN; $constants = $this->gremlin->query($query); $constants = $constants->results; if (!empty($constants)) { // First round, with full ns path $query = <<<GREMLIN g.V().hasLabel("Identifier", "Nsname") .where( __.in("NAME", "SUBNAME").count().is(eq(0)) ) .filter{ it.get().value("fullnspath") in arg1 }.sideEffect{name = it.get().value("fullnspath"); } .addE('DEFINITION') .from( g.V().hasLabel("Functioncall").has("fullnspath", "\\\\define") .out("ARGUMENTS").as("a").out("ARGUMENT").has("rank", 0).hasLabel("String") .filter{ it.get().value("fullnspath") == name}.select('a') ) GREMLIN; $res = $this->gremlin->query($query, array('arg1' => $constants)); // Second round, with fallback to global constants $query = <<<GREMLIN g.V().hasLabel("Identifier", "Nsname") .where( __.in("NAME", "SUBNAME").count().is(eq(0)) ) .where( __.in("DEFINITION").count().is(eq(0)) ) .filter{ name = "\\\\" + it.get().value("fullcode").toString().toLowerCase(); name in arg1 } .addE('DEFINITION') .from( g.V().hasLabel("Functioncall").has("fullnspath", "\\\\define") .out("ARGUMENTS").as("a").out("ARGUMENT").has("rank", 0).hasLabel("String") .filter{ it.get().value("fullnspath") == name}.select('a') ) GREMLIN; $res = $this->gremlin->query($query, array('arg1' => $constants)); // TODO : handle case-insensitive display('Link constant definitions'); } else { display('Link constant definitions : skipping.'); } display('Mark literal expressions as constants'); $query = <<<GREMLIN g.V().hasLabel("Integer", "Boolean", "Real", "Null", "Void", "InlineHtml", "Magicconstant", "Staticconstant", "Void") .sideEffect{ it.get().property("constant", true); } GREMLIN; $this->gremlin->query($query); $query = <<<GREMLIN g.V().hasLabel("String").where( __.out("CONCAT").count().is(eq(0))) .sideEffect{ it.get().property("constant", true); } GREMLIN; $this->gremlin->query($query); $query = <<<GREMLIN g.V().hasLabel("Identifier", "Nsname").not(hasLabel("Functioncall")) .sideEffect{ it.get().property("constant", true); } GREMLIN; $this->gremlin->query($query); $data = new Methods(); $deterministFunctions = $data->getDeterministFunctions(); $deterministFunctions = array_map(function ($x) { return '\\' . $x; }, $deterministFunctions); for ($i = 0; $i < 3; ++$i) { // Cases for Structures (all sub element are constante => structure is constante) $structures = array('Addition' => array('LEFT', 'RIGHT'), 'Multiplication' => array('LEFT', 'RIGHT'), 'Bitshift' => array('LEFT', 'RIGHT'), 'Logical' => array('LEFT', 'RIGHT'), 'Power' => array('LEFT', 'RIGHT'), 'Keyvalue' => array('KEY', 'VALUE'), 'Arguments' => array('ARGUMENT'), 'Sequence' => array('ELEMENT'), 'Break' => array('BREAK'), 'Continue' => array('CONTINUE'), 'Return' => array('RETURN'), 'Ternary' => array('CONDITION', 'THEN', 'ELSE'), 'Comparison' => array('LEFT', 'RIGHT'), 'Noscream' => array('AT'), 'Not' => array('NOT'), 'Parenthesis' => array('CODE'), 'Concatenation' => array('CONCAT'), 'String' => array('CONCAT')); foreach ($structures as $atom => $links) { $linksList = "'" . implode("', '", $links) . "'"; $query = <<<GREMLIN g.V().hasLabel("{$atom}").where( __.out({$linksList}).not(has("constant", true)).count().is(eq(0)) ) .sideEffect{ it.get().property("constant", true);} GREMLIN; $this->gremlin->query($query); } $query = <<<GREMLIN g.V().hasLabel("Functioncall").filter{ it.get().value("fullnspath") in arg1} .where( __.out("ARGUMENTS").out("ARGUMENT").not(has("constant", true)).count().is(eq(0)) ) .sideEffect{ it.get().property("constant", true);} GREMLIN; $this->gremlin->query($query, array('arg1' => $deterministFunctions)); } display('Mark constants expressions'); $query = <<<GREMLIN g.V().hasLabel("Variable").has("code", "\\\$GLOBALS").in("VARIABLE").hasLabel("Array").as("var") .out("INDEX").hasLabel("String") .sideEffect{ varname = '\$' + it.get().value('noDelimiter'); it.get().property("globalvar", varname);} GREMLIN; $this->gremlin->query($query); display('Mark constants expressions'); }
public function run() { $analyzer = $this->config->program; if (empty($analyzer)) { die('Provide the analyzer with the option -P X/Y. Aborting' . "\n"); } $analyzerClass = Analyzer::getClass($analyzer); if ($analyzerClass === false) { $die = "'{$analyzer}' doesn't exist. Aborting\n"; $r = Analyzer::getSuggestionClass($analyzer); if (count($r) > 0) { $die .= 'Did you mean : ' . implode(', ', str_replace('_', '/', $r)) . "\n"; } die($die); } $analyzer = Analyzer::getName($analyzerClass); $query = <<<GREMLIN g.V().hasLabel("Analysis").has("analyzer", "{$analyzer}").out().count(); GREMLIN; $vertices = $this->gremlin->query($query)->results; if (isset($vertices[0]->notCompatibleWithPhpVersion)) { die($this->config->program . " is not compatible with the running version of PHP. No result available.\n"); } if (isset($vertices[0]->notCompatibleWithPhpConfiguration)) { die($this->config->program . " is not compatible with the compilation of the running version of PHP. No result available.\n"); } $return = array(); if ($this->config->style == 'BOOLEAN') { $queryTemplate = 'g.V().hasLabel("Analysis").has("analyzer", "' . $analyzer . '").out().count().is(gt(0))'; $vertices = $this->gremlin->query($queryTemplate); $return[] = $vertices[0]; } elseif ($this->config->style == 'COUNTED_ALL') { $queryTemplate = 'g.V().hasLabel("Analysis").has("analyzer", "' . $analyzer . '").out().count()'; $vertices = $this->gremlin->query($queryTemplate)->results; $return[] = $vertices[0]; } elseif ($this->config->style == 'ALL') { $linksDown = Token::linksAsList(); $query = <<<GREMLIN g.V().hasLabel("Analysis").has("analyzer", "{$analyzer}").out('ANALYZED') .sideEffect{ line = it.get().value('line'); fullcode = it.get().value('fullcode'); file='None'; theFunction = 'None'; theClass='None'; theNamespace='None'; } .sideEffect{ line = it.get().value('line'); } .until( hasLabel('Project') ).repeat( __.in({$linksDown}) .sideEffect{ if (it.get().label() == 'Function') { theFunction = it.get().value('code')} } .sideEffect{ if (it.get().label() == 'Class') { theClass = it.get().value('fullcode')} } .sideEffect{ if (it.get().label() == 'File') { file = it.get().value('fullcode')} } ) .map{ ['line':line, 'file':file, 'fullcode':fullcode, 'function':theFunction, 'class':theClass, 'namespace':theNamespace]; } GREMLIN; $vertices = $this->gremlin->query($query)->results; $return = array(); foreach ($vertices as $k => $v) { $row = array($v->fullcode, $v->file, $v->line, $v->namespace, $v->class, $v->function); $return[] = $row; } } elseif ($this->config->style == 'DISTINCT') { $queryTemplate = 'g.V().hasLabel("Analysis").has("analyzer", "' . $analyzer . '").out("ANALYZED").values("code").unique()'; $vertices = $this->gremlin->query($queryTemplate)->results; $return = array(); foreach ($vertices as $k => $v) { $return[] = array($v); } } elseif ($this->config->style == 'COUNTED') { $queryTemplate = 'g.V().hasLabel("Analysis").has("analyzer", "' . $analyzer . '").out("ANALYZED").groupCount("m")by("code").cap("m")'; $vertices = $this->gremlin->query($queryTemplate)->results; $return = array(); foreach ($vertices[0] as $k => $v) { $return[$k] = $v; } } if ($this->config->json === true) { $text = json_encode($return); } elseif ($this->config->csv === true) { $text = array(array('Code', 'File', 'Namespace', 'Class', 'Function')); foreach ($return as $k => $v) { if (is_array($v)) { $text[] = $v; } else { $text[] = array($k, $v); } } } elseif ($this->config->html === true || $this->config->odt === true) { $text = ''; foreach ($return as $k => $r) { if ($this->config->style == 'COUNTED') { $text .= "+ {$k} => {$r}\n"; } else { $text .= "+ {$k}\n"; if (is_array($r)) { $text .= ' + ' . implode("\n + ", $r) . "\n"; } else { $text .= "+ {$r}\n"; } } } } else { // count also for $this->config->text == 1 $text = ''; foreach ($return as $k => $v) { if ($this->config->style == 'COUNTED') { $text .= "{$k} => {$v}\n"; } else { $text .= implode(', ', $v) . "\n"; } } } if ($this->config->output) { echo $text; } switch (1) { case $this->config->json: $extension = 'json'; break 1; case $this->config->odt: $extension = 'odt'; break 1; case $this->config->html: $extension = 'html'; break 1; case $this->config->csv: $extension = 'csv'; break 1; case $this->config->text: default: $extension = 'txt'; break 1; } if ($this->config->file != 'stdout') { $name = $this->config->file . '.' . $extension; if (file_exists($name)) { die("{$name} already exists. Aborting\n"); } if ($this->config->format == 'ODT') { $name1 = FILE . '.html'; file_put_contents($name1, $text); $name = FILE . '.' . $extension; shell_exec('pandoc -o ' . $name . ' ' . $name1); unlink($name1); } elseif ($this->config->format == 'CSV') { $csvFile = fopen($name, 'w'); foreach ($text as $t) { fputcsv($csvFile, $t); } fclose($csvFile); } else { file_put_contents($name, $text); } } }