PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.3
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.3
4.9.3 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 All 201 releases
betterdocs / includes / Dependencies / SuperClosure / Analyzer / Visitor / ClosureLocatorVisitor.php

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

122 lines 4.6 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\Visitor;
2 // phpcs:ignoreFile -- Bundled third-party (Mozart) dependency; exempt from plugin coding standards.
3
4 use WPDeveloper\BetterDocs\Dependencies\SuperClosure\Exception\ClosureAnalysisException;
5 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Stmt\Namespace_ as NamespaceNode;
6 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Stmt\Trait_ as TraitNode;
7 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Stmt\Class_ as ClassNode;
8 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Expr\Closure as ClosureNode;
9 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node as AstNode;
10 use WPDeveloper\BetterDocs\Dependencies\PhpParser\NodeVisitorAbstract as NodeVisitor;
11
12 /**
13 * This is a visitor that extends the nikic/php-parser library and looks for a
14 * closure node and its location.
15 *
16 * @internal
17 */
18 final class ClosureLocatorVisitor extends NodeVisitor
19 {
20 /**
21 * @var \ReflectionFunction
22 */
23 private $reflection;
24
25 /**
26 * @var ClosureNode
27 */
28 public $closureNode;
29
30 /**
31 * @var array
32 */
33 public $location;
34
35 /**
36 * @param \ReflectionFunction $reflection
37 */
38 public function __construct($reflection)
39 {
40 $this->reflection = $reflection;
41 $this->location = [
42 'class' => null,
43 'directory' => dirname($this->reflection->getFileName()),
44 'file' => $this->reflection->getFileName(),
45 'function' => $this->reflection->getName(),
46 'line' => $this->reflection->getStartLine(),
47 'method' => null,
48 'namespace' => null,
49 'trait' => null,
50 ];
51 }
52
53 public function enterNode(AstNode $node)
54 {
55 // Determine information about the closure's location
56 if (!$this->closureNode) {
57 if ($node instanceof NamespaceNode) {
58 $namespace = $node->name !== null
59 ? $node->name->toString()
60 : null;
61 $this->location['namespace'] = $namespace;
62 }
63 if ($node instanceof TraitNode) {
64 $this->location['trait'] = (string) $node->name;
65 $this->location['class'] = null;
66 } elseif ($node instanceof ClassNode) {
67 $this->location['class'] = (string) $node->name;
68 $this->location['trait'] = null;
69 }
70 }
71
72 // Locate the node of the closure
73 if ($node instanceof ClosureNode) {
74 if ($node->getAttribute('startLine') == $this->location['line']) {
75 if ($this->closureNode) {
76 $line = $this->location['file'] . ':' . $node->getAttribute('startLine');
77 throw new ClosureAnalysisException("Two closures were "
78 . "declared on the same line ({$line}) of code. Cannot "
79 . "determine which closure was the intended target.");
80 } else {
81 $this->closureNode = $node;
82 }
83 }
84 }
85 }
86
87 public function leaveNode(AstNode $node)
88 {
89 // Determine information about the closure's location
90 if (!$this->closureNode) {
91 if ($node instanceof NamespaceNode) {
92 $this->location['namespace'] = null;
93 }
94 if ($node instanceof TraitNode) {
95 $this->location['trait'] = null;
96 } elseif ($node instanceof ClassNode) {
97 $this->location['class'] = null;
98 }
99 }
100 }
101
102 public function afterTraverse(array $nodes)
103 {
104 if ($this->location['class']) {
105 $this->location['class'] = $this->location['namespace'] . '\\' . $this->location['class'];
106 $this->location['method'] = "{$this->location['class']}::{$this->location['function']}";
107 } elseif ($this->location['trait']) {
108 $this->location['trait'] = $this->location['namespace'] . '\\' . $this->location['trait'];
109 $this->location['method'] = "{$this->location['trait']}::{$this->location['function']}";
110
111 // If the closure was declared in a trait, then we will do a best
112 // effort guess on the name of the class that used the trait. It's
113 // actually impossible at this point to know for sure what it is.
114 if ($closureScope = $this->reflection->getClosureScopeClass()) {
115 $this->location['class'] = $closureScope ? $closureScope->getName() : null;
116 } elseif ($closureThis = $this->reflection->getClosureThis()) {
117 $this->location['class'] = get_class($closureThis);
118 }
119 }
120 }
121 }
122