home
/
zktecojo
/
public_html
/
vendor
/
jeremeamia
/
SuperClosure
/
src
/
Jeremeamia
/
SuperClosure
➕ New
📤 Upload
✎ Editing:
ClosureParser.php
← Back
<?php namespace Jeremeamia\SuperClosure; use Jeremeamia\SuperClosure\Visitor\ClosureFinderVisitor; use Jeremeamia\SuperClosure\Visitor\MagicConstantVisitor; /** * Parses a closure from its reflection such that the code and used (closed upon) variables are accessible. The * ClosureParser uses the fabulous nikic/php-parser library which creates abstract syntax trees (AST) of the code. * * @copyright Jeremy Lindblom 2010-2013 */ class ClosureParser { /** * @var array */ protected static $cache = array(); /** * @var \ReflectionFunction The reflection of the closure being parsed */ protected $reflection; /** * @var \PHPParser_Node An abstract syntax tree defining the code of the closure */ protected $abstractSyntaxTree; /** * @var array The variables used (closed upon) by the closure and their values */ protected $usedVariables; /** * @var string The closure's code */ protected $code; /** * Creates a ClosureParser for the provided closure * * @param \Closure $closure * * @return ClosureParser */ public static function fromClosure(\Closure $closure) { return new self(new \ReflectionFunction($closure)); } /** * Clears the internal cache of file ASTs. * * ASTs are stored for any file that is parsed to speed up multiple * parsings of the same file. If you are worried about the memory consumption of files the ClosureParser has already * parsed, you can call this function to clear the cache. The cache is not persistent and stores ASTs from the * current process */ public static function clearCache() { self::$cache = array(); } /** * @param \ReflectionFunction $reflection * * @throws \InvalidArgumentException */ public function __construct(\ReflectionFunction $reflection) { if (!$reflection->isClosure()) { throw new \InvalidArgumentException('You must provide the reflection of a closure.'); } $this->reflection = $reflection; } /** * Returns the reflection of the closure * * @return \ReflectionFunction */ public function getReflection() { return $this->reflection; } /** * Returns the abstract syntax tree (AST) of the closure's code. Class names are resolved to their fully-qualified * class names (FQCN) and magic constants are resolved to their values as they would be in the context of the * closure. * * @return \PHPParser_Node_Expr_Closure * @throws \InvalidArgumentException */ public function getClosureAbstractSyntaxTree() { if (!$this->abstractSyntaxTree) { try { // Parse the code from the file containing the closure and create an AST with FQCN resolved $fileAst = $this->getFileAbstractSyntaxTree(); $closureFinder = new ClosureFinderVisitor($this->reflection); $fileTraverser = new \PHPParser_NodeTraverser(); $fileTraverser->addVisitor(new \PHPParser_NodeVisitor_NameResolver); $fileTraverser->addVisitor($closureFinder); $fileTraverser->traverse($fileAst); } catch (\PHPParser_Error $e) { // @codeCoverageIgnoreStart throw new \InvalidArgumentException('There was an error parsing the file containing the closure.'); // @codeCoverageIgnoreEnd } // Find the first closure defined in the AST that is on the line where the closure is located $closureAst = $closureFinder->getClosureNode(); if (!$closureAst) { // @codeCoverageIgnoreStart throw new \InvalidArgumentException('The closure was not found within the abstract syntax tree.'); // @codeCoverageIgnoreEnd } // Resolve additional nodes by making a second pass through just the closure's nodes $closureTraverser = new \PHPParser_NodeTraverser(); $closureTraverser->addVisitor(new MagicConstantVisitor($closureFinder->getLocation())); $closureAst = $closureTraverser->traverse(array($closureAst)); $this->abstractSyntaxTree = $closureAst[0]; } return $this->abstractSyntaxTree; } /** * 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. * * @return array */ public function getUsedVariables() { if (!$this->usedVariables) { // Get the variable names defined in the AST $usedVarNames = array_map(function ($usedVar) { return $usedVar->var; }, $this->getClosureAbstractSyntaxTree()->uses); // Get the variable names and values using reflection $usedVarValues = $this->reflection->getStaticVariables(); // Combine the two arrays to create a canonical hash of variable names and values $this->usedVariables = array(); foreach ($usedVarNames as $name) { if (isset($usedVarValues[$name])) { $this->usedVariables[$name] = $usedVarValues[$name]; } } } return $this->usedVariables; } /** * Returns the formatted code of the closure * * @return string */ public function getCode() { if (!$this->code) { // Use the pretty printer to print the closure code from the AST $printer = new \PHPParser_PrettyPrinter_Default(); $this->code = $printer->prettyPrint(array($this->getClosureAbstractSyntaxTree())); } return $this->code; } /** * Loads the PHP file and produces an abstract syntax tree (AST) of the code. This is stored in an internal cache by * the filename for memoization within the same process * * @return array */ protected function getFileAbstractSyntaxTree() { $filename = $this->reflection->getFileName(); if (!isset(self::$cache[$filename])) { $parser = new \PHPParser_Parser(new \PHPParser_Lexer_Emulative); self::$cache[$filename] = $parser->parse(file_get_contents($filename)); } return self::$cache[$filename]; } }
💾 Save Changes
Cancel
📤 Upload File
×
Select File
Upload
Cancel
➕ Create New
×
Type
📄 File
📁 Folder
Name
Create
Cancel
✎ Rename Item
×
Current Name
New Name
Rename
Cancel
🔐 Change Permissions
×
Target File
Permission (e.g., 0755, 0644)
0755
0644
0777
Apply
Cancel