PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.2
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.2
4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 All 200 releases
betterdocs / includes / Dependencies / SuperClosure / Analyzer / TokenAnalyzer.php

TokenAnalyzer.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.9.2, at includes/Dependencies/SuperClosure/Analyzer/TokenAnalyzer.php

120 lines 4.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php namespace WPDeveloper\BetterDocs\Dependencies\SuperClosure\Analyzer;
2 // phpcs:ignoreFile -- Bundled third-party (Mozart) dependency; exempt from plugin coding standards.
3
4 use WPDeveloper\BetterDocs\Dependencies\SuperClosure\Exception\ClosureAnalysisException;
5
6 /**
7 * This is the token based analyzer.
8 *
9 * We're using Uses reflection and tokenization to analyze a closure and
10 * determine its code and context. This is much faster than the AST based
11 * implementation.
12 */
13 class TokenAnalyzer extends ClosureAnalyzer
14 {
15 public function determineCode(array &$data)
16 {
17 $this->determineTokens($data);
18 $data['code'] = implode('', $data['tokens']);
19 $data['hasThis'] = (strpos($data['code'], '$this') !== false);
20 }
21
22 private function determineTokens(array &$data)
23 {
24 $potential = $this->determinePotentialTokens($data['reflection']);
25 $braceLevel = $index = $step = $insideUse = 0;
26 $data['tokens'] = $data['context'] = [];
27
28 foreach ($potential as $token) {
29 $token = new Token($token);
30 switch ($step) {
31 // Handle tokens before the function declaration.
32 case 0:
33 if ($token->is(T_FUNCTION)) {
34 $data['tokens'][] = $token;
35 $step++;
36 }
37 break;
38 // Handle tokens inside the function signature.
39 case 1:
40 $data['tokens'][] = $token;
41 if ($insideUse) {
42 if ($token->is(T_VARIABLE)) {
43 $varName = trim($token, '$ ');
44 $data['context'][$varName] = null;
45 } elseif ($token->is('&')) {
46 $data['hasRefs'] = true;
47 }
48 } elseif ($token->is(T_USE)) {
49 $insideUse++;
50 }
51 if ($token->is('{')) {
52 $step++;
53 $braceLevel++;
54 }
55 break;
56 // Handle tokens inside the function body.
57 case 2:
58 $data['tokens'][] = $token;
59 if ($token->is('{')) {
60 $braceLevel++;
61 } elseif ($token->is('}')) {
62 $braceLevel--;
63 if ($braceLevel === 0) {
64 $step++;
65 }
66 }
67 break;
68 // Handle tokens after the function declaration.
69 case 3:
70 if ($token->is(T_FUNCTION)) {
71 throw new ClosureAnalysisException('Multiple closures '
72 . 'were declared on the same line of code. Could not '
73 . 'determine which closure was the intended target.'
74 );
75 }
76 break;
77 }
78 }
79 }
80
81 private function determinePotentialTokens(\ReflectionFunction $reflection)
82 {
83 // Load the file containing the code for the function.
84 $fileName = $reflection->getFileName();
85 if (!is_readable($fileName)) {
86 throw new ClosureAnalysisException(
87 "Cannot read the file containing the closure: \"{$fileName}\"."
88 );
89 }
90
91 $code = '';
92 $file = new \SplFileObject($fileName);
93 $file->seek($reflection->getStartLine() - 1);
94 while ($file->key() < $reflection->getEndLine()) {
95 $code .= $file->current();
96 $file->next();
97 }
98
99 $code = trim($code);
100 if (strpos($code, '<?php') !== 0) {
101 $code = "<?php\n" . $code;
102 }
103
104 return token_get_all($code);
105 }
106
107 protected function determineContext(array &$data)
108 {
109 // Get the values of the variables that are closed upon in "use".
110 $values = $data['reflection']->getStaticVariables();
111
112 // Construct the context by combining the variable names and values.
113 foreach ($data['context'] as $name => &$value) {
114 if (isset($values[$name])) {
115 $value = $values[$name];
116 }
117 }
118 }
119 }
120