Revert "My first commit of codes"

This reverts commit a6e5a69348.
This commit is contained in:
sujitprasad
2015-05-01 13:27:00 +05:30
parent 6f37d10de3
commit 16ea6e1984
8487 changed files with 0 additions and 1317246 deletions

View File

@@ -1,129 +0,0 @@
<?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\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."
);
}
$parser = new CodeParser(new EmulativeLexer);
return $parser->parse(file_get_contents($fileName));
}
}

View File

@@ -1,62 +0,0 @@
<?php namespace SuperClosure\Analyzer;
use SuperClosure\Exception\ClosureAnalysisException;
abstract class ClosureAnalyzer
{
/**
* Analyzer a given closure.
*
* @param \Closure $closure
*
* @throws ClosureAnalysisException
*
* @return array
*/
public function analyze(\Closure $closure)
{
$data = [
'reflection' => new \ReflectionFunction($closure),
'code' => null,
'hasThis' => false,
'context' => [],
'hasRefs' => false,
'binding' => null,
'scope' => null,
'isStatic' => $this->isClosureStatic($closure),
];
$this->determineCode($data);
$this->determineContext($data);
$this->determineBinding($data);
return $data;
}
abstract protected function determineCode(array &$data);
/**
* Returns the variables that are in the "use" clause of the closure.
*
* These variables are referred to as the "used variables", "static
* variables", "closed upon variables", or "context" of the closure.
*
* @param array $data
*/
abstract protected function determineContext(array &$data);
private function determineBinding(array &$data)
{
$data['binding'] = $data['reflection']->getClosureThis();
if ($scope = $data['reflection']->getClosureScopeClass()) {
$data['scope'] = $scope->getName();
}
}
private function isClosureStatic(\Closure $closure)
{
$rebound = new \ReflectionFunction(@$closure->bindTo(new \stdClass));
return $rebound->getClosureThis() === null;
}
}

View File

@@ -1,70 +0,0 @@
<?php namespace SuperClosure\Analyzer;
/**
* A Token object represents and individual token parsed from PHP code.
*
* Each Token object is a normalized token created from the result of the
* `get_token_all()`. function, which is part of PHP's tokenizer.
*
* @link http://us2.php.net/manual/en/tokens.php
*/
class Token
{
/**
* @var string The token name. Always null for literal tokens.
*/
public $name;
/**
* @var int|null The token's integer value. Always null for literal tokens.
*/
public $value;
/**
* @var string The PHP code of the token.
*/
public $code;
/**
* @var int|null The line number of the token in the original code.
*/
public $line;
/**
* Constructs a token object.
*
* @param string $code
* @param int|null $value
* @param int|null $line
*
* @throws \InvalidArgumentException
*/
public function __construct($code, $value = null, $line = null)
{
if (is_array($code)) {
list($value, $code, $line) = array_pad($code, 3, null);
}
$this->code = $code;
$this->value = $value;
$this->line = $line;
$this->name = $value ? token_name($value) : null;
}
/**
* Determines if the token's value/code is equal to the specified value.
*
* @param mixed $value The value to check.
*
* @return bool True if the token is equal to the value.
*/
public function is($value)
{
return ($this->code === $value || $this->value === $value);
}
public function __toString()
{
return $this->code;
}
}

View File

@@ -1,118 +0,0 @@
<?php namespace SuperClosure\Analyzer;
use SuperClosure\Exception\ClosureAnalysisException;
/**
* This is the token based analyzer.
*
* We're using Uses reflection and tokenization to analyze a closure and
* determine its code and context. This is much faster than the AST based
* implementation.
*/
class TokenAnalyzer extends ClosureAnalyzer
{
public function determineCode(array &$data)
{
$this->determineTokens($data);
$data['code'] = implode('', $data['tokens']);
$data['hasThis'] = (strpos($data['code'], '$this') !== false);
}
private function determineTokens(array &$data)
{
$potential = $this->determinePotentialTokens($data['reflection']);
$braceLevel = $index = $step = $insideUse = 0;
$data['tokens'] = $data['context'] = [];
foreach ($potential as $token) {
$token = new Token($token);
switch ($step) {
// Handle tokens before the function declaration.
case 0:
if ($token->is(T_FUNCTION)) {
$data['tokens'][] = $token;
$step++;
}
break;
// Handle tokens inside the function signature.
case 1:
$data['tokens'][] = $token;
if ($insideUse) {
if ($token->is(T_VARIABLE)) {
$varName = trim($token, '$ ');
$data['context'][$varName] = null;
} elseif ($token->is('&')) {
$data['hasRefs'] = true;
}
} elseif ($token->is(T_USE)) {
$insideUse++;
}
if ($token->is('{')) {
$step++;
$braceLevel++;
}
break;
// Handle tokens inside the function body.
case 2:
$data['tokens'][] = $token;
if ($token->is('{')) {
$braceLevel++;
} elseif ($token->is('}')) {
$braceLevel--;
if ($braceLevel === 0) {
$step++;
}
}
break;
// Handle tokens after the function declaration.
case 3:
if ($token->is(T_FUNCTION)) {
throw new ClosureAnalysisException('Multiple closures '
. 'were declared on the same line of code. Could '
. 'determine which closure was the intended target.'
);
}
break;
}
}
}
private function determinePotentialTokens(\ReflectionFunction $reflection)
{
// Load the file containing the code for the function.
$fileName = $reflection->getFileName();
if (!is_readable($fileName)) {
throw new ClosureAnalysisException(
"Cannot read the file containing the closure: \"{$fileName}\"."
);
}
$code = '';
$file = new \SplFileObject($fileName);
$file->seek($reflection->getStartLine() - 1);
while ($file->key() < $reflection->getEndLine()) {
$code .= $file->current();
$file->next();
}
$code = trim($code);
if (strpos($code, '<?php') !== 0) {
$code = "<?php\n" . $code;
}
return token_get_all($code);
}
protected function determineContext(array &$data)
{
// Get the values of the variables that are closed upon in "use".
$values = $data['reflection']->getStaticVariables();
// Construct the context by combining the variable names and values.
foreach ($data['context'] as $name => &$value) {
if (isset($values[$name])) {
$value = $values[$name];
}
}
}
}

View File

@@ -1,117 +0,0 @@
<?php namespace SuperClosure\Analyzer\Visitor;
use SuperClosure\Exception\ClosureAnalysisException;
use PhpParser\Node\Stmt\Namespace_ as NamespaceNode;
use PhpParser\Node\Stmt\Trait_ as TraitNode;
use PhpParser\Node\Stmt\Class_ as ClassNode;
use PhpParser\Node\Expr\Closure as ClosureNode;
use PhpParser\Node as AstNode;
use PhpParser\NodeVisitorAbstract as NodeVisitor;
/**
* This is a visitor that extends the nikic/php-parser library and looks for a
* closure node and its location.
*
* @internal
*/
final class ClosureLocatorVisitor extends NodeVisitor
{
/**
* @var \ReflectionFunction
*/
private $reflection;
/**
* @var ClosureNode
*/
public $closureNode;
/**
* @var array
*/
public $location;
/**
* @param \ReflectionFunction $reflection
*/
public function __construct($reflection)
{
$this->reflection = $reflection;
$this->location = [
'class' => null,
'directory' => dirname($this->reflection->getFileName()),
'file' => $this->reflection->getFileName(),
'function' => $this->reflection->getName(),
'line' => $this->reflection->getStartLine(),
'method' => null,
'namespace' => null,
'trait' => null,
];
}
public function enterNode(AstNode $node)
{
// Determine information about the closure's location
if (!$this->closureNode) {
if ($node instanceof NamespaceNode) {
$namespace = ($node->name && is_array($node->name->parts))
? implode('\\', $node->name->parts)
: null;
$this->location['namespace'] = $namespace;
}
if ($node instanceof TraitNode) {
$this->location['trait'] = $node->name;
$this->location['class'] = null;
} elseif ($node instanceof ClassNode) {
$this->location['class'] = $node->name;
$this->location['trait'] = null;
}
}
// Locate the node of the closure
if ($node instanceof ClosureNode) {
if ($node->getAttribute('startLine') == $this->location['line']) {
if ($this->closureNode) {
$line = $this->location['file'] . ':' . $node->getAttribute('startLine');
throw new ClosureAnalysisException("Two closures were "
. "declared on the same line ({$line}) of code. Cannot "
. "determine which closure was the intended target.");
} else {
$this->closureNode = $node;
}
}
}
}
public function leaveNode(AstNode $node)
{
// Determine information about the closure's location
if (!$this->closureNode) {
if ($node instanceof NamespaceNode) {
$this->location['namespace'] = null;
}
if ($node instanceof TraitNode) {
$this->location['trait'] = null;
} elseif ($node instanceof ClassNode) {
$this->location['class'] = null;
}
}
}
public function afterTraverse(array $nodes)
{
if ($this->location['class']) {
$this->location['class'] = $this->location['namespace'] . '\\' . $this->location['class'];
$this->location['method'] = "{$this->location['class']}::{$this->location['function']}";
} elseif ($this->location['trait']) {
$this->location['trait'] = $this->location['namespace'] . '\\' . $this->location['trait'];
$this->location['method'] = "{$this->location['trait']}::{$this->location['function']}";
}
if (!$this->location['class']) {
/** @var \ReflectionClass $closureScopeClass */
$closureScopeClass = $this->reflection->getClosureScopeClass();
$this->location['class'] = $closureScopeClass ? $closureScopeClass->getName() : null;
}
}
}

View File

@@ -1,50 +0,0 @@
<?php namespace SuperClosure\Analyzer\Visitor;
use PhpParser\Node\Scalar\LNumber as NumberNode;
use PhpParser\Node\Scalar\String as StringNode;
use PhpParser\Node as AstNode;
use PhpParser\NodeVisitorAbstract as NodeVisitor;
/**
* This is a visitor that resolves magic constants (e.g., __FILE__) to their
* intended values within a closure's AST.
*
* @internal
*/
final class MagicConstantVisitor extends NodeVisitor
{
/**
* @var array
*/
private $location;
/**
* @param array $location
*/
public function __construct(array $location)
{
$this->location = $location;
}
public function leaveNode(AstNode $node)
{
switch ($node->getType()) {
case 'Scalar_MagicConst_Class' :
return new StringNode($this->location['class']);
case 'Scalar_MagicConst_Dir' :
return new StringNode($this->location['directory']);
case 'Scalar_MagicConst_File' :
return new StringNode($this->location['file']);
case 'Scalar_MagicConst_Function' :
return new StringNode($this->location['function']);
case 'Scalar_MagicConst_Line' :
return new NumberNode($node->getAttribute('startLine'));
case 'Scalar_MagicConst_Method' :
return new StringNode($this->location['method']);
case 'Scalar_MagicConst_Namespace' :
return new StringNode($this->location['namespace']);
case 'Scalar_MagicConst_Trait' :
return new StringNode($this->location['trait']);
}
}
}

View File

@@ -1,27 +0,0 @@
<?php namespace SuperClosure\Analyzer\Visitor;
use PhpParser\Node as AstNode;
use PhpParser\Node\Expr\Variable as VariableNode;
use PhpParser\NodeVisitorAbstract as NodeVisitor;
/**
* Detects if the closure's AST contains a $this variable.
*
* @internal
*/
final class ThisDetectorVisitor extends NodeVisitor
{
/**
* @var bool
*/
public $detected = false;
public function leaveNode(AstNode $node)
{
if ($node instanceof VariableNode) {
if ($node->name === 'this') {
$this->detected = true;
}
}
}
}