/usr/share/php/SuperClosure/Analyzer/AstAnalyzer.php is in php-superclosure 2.2.0-1build1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | <?php namespace SuperClosure\Analyzer;
use SuperClosure\Analyzer\Visitor\ThisDetectorVisitor;
use SuperClosure\Exception\ClosureAnalysisException;
use SuperClosure\Analyzer\Visitor\ClosureLocatorVisitor;
use SuperClosure\Analyzer\Visitor\MagicConstantVisitor;
use PhpParser\NodeTraverser;
use PhpParser\PrettyPrinter\Standard as NodePrinter;
use PhpParser\Error as ParserError;
use PhpParser\NodeVisitor\NameResolver;
use PhpParser\Parser as CodeParser;
use PhpParser\ParserFactory;
use PhpParser\Lexer\Emulative as EmulativeLexer;
/**
* This is the AST based analyzer.
*
* We're using reflection and AST-based code parser to analyze a closure and
* determine its code and context using the nikic/php-parser library. The AST
* based analyzer and has more capabilities than the token analyzer, but is,
* unfortunately, about 25 times slower.
*/
class AstAnalyzer extends ClosureAnalyzer
{
protected function determineCode(array &$data)
{
// Find the closure by traversing through a AST of the code.
// Note: This also resolves class names to their FQCNs while traversing.
$this->locateClosure($data);
// Make a second pass through the AST, but only through the closure's
// nodes, to resolve any magic constants to literal values.
$traverser = new NodeTraverser;
$traverser->addVisitor(new MagicConstantVisitor($data['location']));
$traverser->addVisitor($thisDetector = new ThisDetectorVisitor);
$data['ast'] = $traverser->traverse([$data['ast']])[0];
$data['hasThis'] = $thisDetector->detected;
// Bounce the updated AST down to a string representation of the code.
$data['code'] = (new NodePrinter)->prettyPrint([$data['ast']]);
}
/**
* Parses the closure's code and produces an abstract syntax tree (AST).
*
* @param array $data
*
* @throws ClosureAnalysisException if there is an issue finding the closure
*/
private function locateClosure(array &$data)
{
try {
$locator = new ClosureLocatorVisitor($data['reflection']);
$fileAst = $this->getFileAst($data['reflection']);
$fileTraverser = new NodeTraverser;
$fileTraverser->addVisitor(new NameResolver);
$fileTraverser->addVisitor($locator);
$fileTraverser->traverse($fileAst);
} catch (ParserError $e) {
// @codeCoverageIgnoreStart
throw new ClosureAnalysisException(
'There was an error analyzing the closure code.', 0, $e
);
// @codeCoverageIgnoreEnd
}
$data['ast'] = $locator->closureNode;
if (!$data['ast']) {
// @codeCoverageIgnoreStart
throw new ClosureAnalysisException(
'The closure was not found within the abstract syntax tree.'
);
// @codeCoverageIgnoreEnd
}
$data['location'] = $locator->location;
}
/**
* Returns the variables that in the "use" clause of the closure definition.
* These are referred to as the "used variables", "static variables", or
* "closed upon variables", "context" of the closure.
*
* @param array $data
*/
protected function determineContext(array &$data)
{
// Get the variable names defined in the AST
$refs = 0;
$vars = array_map(function ($node) use (&$refs) {
if ($node->byRef) {
$refs++;
}
return $node->var;
}, $data['ast']->uses);
$data['hasRefs'] = ($refs > 0);
// Get the variable names and values using reflection
$values = $data['reflection']->getStaticVariables();
// Combine the names and values to create the canonical context.
foreach ($vars as $name) {
if (isset($values[$name])) {
$data['context'][$name] = $values[$name];
}
}
}
/**
* @param \ReflectionFunction $reflection
*
* @throws ClosureAnalysisException
*
* @return \PhpParser\Node[]
*/
private function getFileAst(\ReflectionFunction $reflection)
{
$fileName = $reflection->getFileName();
if (!file_exists($fileName)) {
throw new ClosureAnalysisException(
"The file containing the closure, \"{$fileName}\" did not exist."
);
}
return $this->getParser()->parse(file_get_contents($fileName));
}
/**
* @return CodeParser
*/
private function getParser()
{
if (class_exists('PhpParser\ParserFactory')) {
return (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
}
return new CodeParser(new EmulativeLexer);
}
}
|