Subversion Repository Public Repository

Nextrek

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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
<?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];
    }
}

Commits for Nextrek/Aiba_backup/vendor/jeremeamia/SuperClosure/src/Jeremeamia/SuperClosure/ClosureParser.php

Diff revisions: vs.
Revision Author Commited Message
1464 MOliva picture MOliva Tue 13 Oct, 2020 11:16:56 +0000