PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.8.9
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.8.9
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 / PhpParser / Lexer / Emulative.php

Emulative.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 3.8.9, at includes/Dependencies/PhpParser/Lexer/Emulative.php

175 lines 6.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPDeveloper\BetterDocs\Dependencies\PhpParser\Lexer;
4
5 use WPDeveloper\BetterDocs\Dependencies\PhpParser\ErrorHandler;
6 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Parser\Tokens;
7
8 class Emulative extends \WPDeveloper\BetterDocs\Dependencies\PhpParser\Lexer
9 {
10 protected $newKeywords;
11 protected $inObjectAccess;
12
13 const T_ELLIPSIS = 1001;
14 const T_POW = 1002;
15 const T_POW_EQUAL = 1003;
16 const T_COALESCE = 1004;
17 const T_SPACESHIP = 1005;
18 const T_YIELD_FROM = 1006;
19
20 const PHP_7_0 = '7.0.0dev';
21 const PHP_5_6 = '5.6.0rc1';
22
23 public function __construct(array $options = array()) {
24 parent::__construct($options);
25
26 $newKeywordsPerVersion = array(
27 // No new keywords since PHP 5.5
28 );
29
30 $this->newKeywords = array();
31 foreach ($newKeywordsPerVersion as $version => $newKeywords) {
32 if (version_compare(PHP_VERSION, $version, '>=')) {
33 break;
34 }
35
36 $this->newKeywords += $newKeywords;
37 }
38
39 if (version_compare(PHP_VERSION, self::PHP_7_0, '>=')) {
40 return;
41 }
42 $this->tokenMap[self::T_COALESCE] = Tokens::T_COALESCE;
43 $this->tokenMap[self::T_SPACESHIP] = Tokens::T_SPACESHIP;
44 $this->tokenMap[self::T_YIELD_FROM] = Tokens::T_YIELD_FROM;
45
46 if (version_compare(PHP_VERSION, self::PHP_5_6, '>=')) {
47 return;
48 }
49 $this->tokenMap[self::T_ELLIPSIS] = Tokens::T_ELLIPSIS;
50 $this->tokenMap[self::T_POW] = Tokens::T_POW;
51 $this->tokenMap[self::T_POW_EQUAL] = Tokens::T_POW_EQUAL;
52 }
53
54 public function startLexing($code, ErrorHandler $errorHandler = null) {
55 $this->inObjectAccess = false;
56
57 parent::startLexing($code, $errorHandler);
58 if ($this->requiresEmulation($code)) {
59 $this->emulateTokens();
60 }
61 }
62
63 /*
64 * Checks if the code is potentially using features that require emulation.
65 */
66 protected function requiresEmulation($code) {
67 if (version_compare(PHP_VERSION, self::PHP_7_0, '>=')) {
68 return false;
69 }
70
71 if (preg_match('(\?\?|<=>|yield[ \n\r\t]+from)', $code)) {
72 return true;
73 }
74
75 if (version_compare(PHP_VERSION, self::PHP_5_6, '>=')) {
76 return false;
77 }
78
79 return preg_match('(\.\.\.|(?<!/)\*\*(?!/))', $code);
80 }
81
82 /*
83 * Emulates tokens for newer PHP versions.
84 */
85 protected function emulateTokens() {
86 // We need to manually iterate and manage a count because we'll change
87 // the tokens array on the way
88 $line = 1;
89 for ($i = 0, $c = count($this->tokens); $i < $c; ++$i) {
90 $replace = null;
91 if (isset($this->tokens[$i + 1])) {
92 if ($this->tokens[$i] === '?' && $this->tokens[$i + 1] === '?') {
93 array_splice($this->tokens, $i, 2, array(
94 array(self::T_COALESCE, '??', $line)
95 ));
96 $c--;
97 continue;
98 }
99 if ($this->tokens[$i][0] === T_IS_SMALLER_OR_EQUAL
100 && $this->tokens[$i + 1] === '>'
101 ) {
102 array_splice($this->tokens, $i, 2, array(
103 array(self::T_SPACESHIP, '<=>', $line)
104 ));
105 $c--;
106 continue;
107 }
108 if ($this->tokens[$i] === '*' && $this->tokens[$i + 1] === '*') {
109 array_splice($this->tokens, $i, 2, array(
110 array(self::T_POW, '**', $line)
111 ));
112 $c--;
113 continue;
114 }
115 if ($this->tokens[$i] === '*' && $this->tokens[$i + 1][0] === T_MUL_EQUAL) {
116 array_splice($this->tokens, $i, 2, array(
117 array(self::T_POW_EQUAL, '**=', $line)
118 ));
119 $c--;
120 continue;
121 }
122 }
123
124 if (isset($this->tokens[$i + 2])) {
125 if ($this->tokens[$i][0] === T_YIELD && $this->tokens[$i + 1][0] === T_WHITESPACE
126 && $this->tokens[$i + 2][0] === T_STRING
127 && !strcasecmp($this->tokens[$i + 2][1], 'from')
128 ) {
129 array_splice($this->tokens, $i, 3, array(
130 array(
131 self::T_YIELD_FROM,
132 $this->tokens[$i][1] . $this->tokens[$i + 1][1] . $this->tokens[$i + 2][1],
133 $line
134 )
135 ));
136 $c -= 2;
137 $line += substr_count($this->tokens[$i][1], "\n");
138 continue;
139 }
140 if ($this->tokens[$i] === '.' && $this->tokens[$i + 1] === '.'
141 && $this->tokens[$i + 2] === '.'
142 ) {
143 array_splice($this->tokens, $i, 3, array(
144 array(self::T_ELLIPSIS, '...', $line)
145 ));
146 $c -= 2;
147 continue;
148 }
149 }
150
151 if (\is_array($this->tokens[$i])) {
152 $line += substr_count($this->tokens[$i][1], "\n");
153 }
154 }
155 }
156
157 public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) {
158 $token = parent::getNextToken($value, $startAttributes, $endAttributes);
159
160 // replace new keywords by their respective tokens. This is not done
161 // if we currently are in an object access (e.g. in $obj->namespace
162 // "namespace" stays a T_STRING tokens and isn't converted to T_NAMESPACE)
163 if (Tokens::T_STRING === $token && !$this->inObjectAccess) {
164 if (isset($this->newKeywords[strtolower($value)])) {
165 return $this->newKeywords[strtolower($value)];
166 }
167 } else {
168 // keep track of whether we currently are in an object access (after ->)
169 $this->inObjectAccess = Tokens::T_OBJECT_OPERATOR === $token;
170 }
171
172 return $token;
173 }
174 }
175