Chris@13
|
1 <?php
|
Chris@13
|
2
|
Chris@13
|
3 /*
|
Chris@13
|
4 * This file is part of Psy Shell.
|
Chris@13
|
5 *
|
Chris@13
|
6 * (c) 2012-2018 Justin Hileman
|
Chris@13
|
7 *
|
Chris@13
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@13
|
9 * file that was distributed with this source code.
|
Chris@13
|
10 */
|
Chris@13
|
11
|
Chris@13
|
12 namespace Psy;
|
Chris@13
|
13
|
Chris@13
|
14 /**
|
Chris@13
|
15 * The Shell execution context.
|
Chris@13
|
16 *
|
Chris@13
|
17 * This class encapsulates the current variables, most recent return value and
|
Chris@13
|
18 * exception, and the current namespace.
|
Chris@13
|
19 */
|
Chris@13
|
20 class Context
|
Chris@13
|
21 {
|
Chris@13
|
22 private static $specialNames = ['_', '_e', '__out', '__psysh__', 'this'];
|
Chris@13
|
23
|
Chris@13
|
24 // Whitelist a very limited number of command-scope magic variable names.
|
Chris@13
|
25 // This might be a bad idea, but future me can sort it out.
|
Chris@13
|
26 private static $commandScopeNames = [
|
Chris@13
|
27 '__function', '__method', '__class', '__namespace', '__file', '__line', '__dir',
|
Chris@13
|
28 ];
|
Chris@13
|
29
|
Chris@13
|
30 private $scopeVariables = [];
|
Chris@13
|
31 private $commandScopeVariables = [];
|
Chris@13
|
32 private $returnValue;
|
Chris@13
|
33 private $lastException;
|
Chris@13
|
34 private $lastStdout;
|
Chris@13
|
35 private $boundObject;
|
Chris@16
|
36 private $boundClass;
|
Chris@13
|
37
|
Chris@13
|
38 /**
|
Chris@13
|
39 * Get a context variable.
|
Chris@13
|
40 *
|
Chris@13
|
41 * @throws InvalidArgumentException If the variable is not found in the current context
|
Chris@13
|
42 *
|
Chris@13
|
43 * @param string $name
|
Chris@13
|
44 *
|
Chris@13
|
45 * @return mixed
|
Chris@13
|
46 */
|
Chris@13
|
47 public function get($name)
|
Chris@13
|
48 {
|
Chris@13
|
49 switch ($name) {
|
Chris@13
|
50 case '_':
|
Chris@13
|
51 return $this->returnValue;
|
Chris@13
|
52
|
Chris@13
|
53 case '_e':
|
Chris@13
|
54 if (isset($this->lastException)) {
|
Chris@13
|
55 return $this->lastException;
|
Chris@13
|
56 }
|
Chris@13
|
57 break;
|
Chris@13
|
58
|
Chris@13
|
59 case '__out':
|
Chris@13
|
60 if (isset($this->lastStdout)) {
|
Chris@13
|
61 return $this->lastStdout;
|
Chris@13
|
62 }
|
Chris@13
|
63 break;
|
Chris@13
|
64
|
Chris@13
|
65 case 'this':
|
Chris@13
|
66 if (isset($this->boundObject)) {
|
Chris@13
|
67 return $this->boundObject;
|
Chris@13
|
68 }
|
Chris@13
|
69 break;
|
Chris@13
|
70
|
Chris@13
|
71 case '__function':
|
Chris@13
|
72 case '__method':
|
Chris@13
|
73 case '__class':
|
Chris@13
|
74 case '__namespace':
|
Chris@13
|
75 case '__file':
|
Chris@13
|
76 case '__line':
|
Chris@13
|
77 case '__dir':
|
Chris@17
|
78 if (\array_key_exists($name, $this->commandScopeVariables)) {
|
Chris@13
|
79 return $this->commandScopeVariables[$name];
|
Chris@13
|
80 }
|
Chris@13
|
81 break;
|
Chris@13
|
82
|
Chris@13
|
83 default:
|
Chris@17
|
84 if (\array_key_exists($name, $this->scopeVariables)) {
|
Chris@13
|
85 return $this->scopeVariables[$name];
|
Chris@13
|
86 }
|
Chris@13
|
87 break;
|
Chris@13
|
88 }
|
Chris@13
|
89
|
Chris@13
|
90 throw new \InvalidArgumentException('Unknown variable: $' . $name);
|
Chris@13
|
91 }
|
Chris@13
|
92
|
Chris@13
|
93 /**
|
Chris@13
|
94 * Get all defined variables.
|
Chris@13
|
95 *
|
Chris@13
|
96 * @return array
|
Chris@13
|
97 */
|
Chris@13
|
98 public function getAll()
|
Chris@13
|
99 {
|
Chris@17
|
100 return \array_merge($this->scopeVariables, $this->getSpecialVariables());
|
Chris@13
|
101 }
|
Chris@13
|
102
|
Chris@13
|
103 /**
|
Chris@13
|
104 * Get all defined magic variables: $_, $_e, $__out, $__class, $__file, etc.
|
Chris@13
|
105 *
|
Chris@13
|
106 * @return array
|
Chris@13
|
107 */
|
Chris@13
|
108 public function getSpecialVariables()
|
Chris@13
|
109 {
|
Chris@13
|
110 $vars = [
|
Chris@13
|
111 '_' => $this->returnValue,
|
Chris@13
|
112 ];
|
Chris@13
|
113
|
Chris@13
|
114 if (isset($this->lastException)) {
|
Chris@13
|
115 $vars['_e'] = $this->lastException;
|
Chris@13
|
116 }
|
Chris@13
|
117
|
Chris@13
|
118 if (isset($this->lastStdout)) {
|
Chris@13
|
119 $vars['__out'] = $this->lastStdout;
|
Chris@13
|
120 }
|
Chris@13
|
121
|
Chris@13
|
122 if (isset($this->boundObject)) {
|
Chris@13
|
123 $vars['this'] = $this->boundObject;
|
Chris@13
|
124 }
|
Chris@13
|
125
|
Chris@17
|
126 return \array_merge($vars, $this->commandScopeVariables);
|
Chris@13
|
127 }
|
Chris@13
|
128
|
Chris@13
|
129 /**
|
Chris@13
|
130 * Set all scope variables.
|
Chris@13
|
131 *
|
Chris@13
|
132 * This method does *not* set any of the magic variables: $_, $_e, $__out,
|
Chris@13
|
133 * $__class, $__file, etc.
|
Chris@13
|
134 *
|
Chris@13
|
135 * @param array $vars
|
Chris@13
|
136 */
|
Chris@13
|
137 public function setAll(array $vars)
|
Chris@13
|
138 {
|
Chris@13
|
139 foreach (self::$specialNames as $key) {
|
Chris@13
|
140 unset($vars[$key]);
|
Chris@13
|
141 }
|
Chris@13
|
142
|
Chris@13
|
143 foreach (self::$commandScopeNames as $key) {
|
Chris@13
|
144 unset($vars[$key]);
|
Chris@13
|
145 }
|
Chris@13
|
146
|
Chris@13
|
147 $this->scopeVariables = $vars;
|
Chris@13
|
148 }
|
Chris@13
|
149
|
Chris@13
|
150 /**
|
Chris@13
|
151 * Set the most recent return value.
|
Chris@13
|
152 *
|
Chris@13
|
153 * @param mixed $value
|
Chris@13
|
154 */
|
Chris@13
|
155 public function setReturnValue($value)
|
Chris@13
|
156 {
|
Chris@13
|
157 $this->returnValue = $value;
|
Chris@13
|
158 }
|
Chris@13
|
159
|
Chris@13
|
160 /**
|
Chris@13
|
161 * Get the most recent return value.
|
Chris@13
|
162 *
|
Chris@13
|
163 * @return mixed
|
Chris@13
|
164 */
|
Chris@13
|
165 public function getReturnValue()
|
Chris@13
|
166 {
|
Chris@13
|
167 return $this->returnValue;
|
Chris@13
|
168 }
|
Chris@13
|
169
|
Chris@13
|
170 /**
|
Chris@13
|
171 * Set the most recent Exception.
|
Chris@13
|
172 *
|
Chris@13
|
173 * @param \Exception $e
|
Chris@13
|
174 */
|
Chris@13
|
175 public function setLastException(\Exception $e)
|
Chris@13
|
176 {
|
Chris@13
|
177 $this->lastException = $e;
|
Chris@13
|
178 }
|
Chris@13
|
179
|
Chris@13
|
180 /**
|
Chris@13
|
181 * Get the most recent Exception.
|
Chris@13
|
182 *
|
Chris@13
|
183 * @throws \InvalidArgumentException If no Exception has been caught
|
Chris@13
|
184 *
|
Chris@13
|
185 * @return null|\Exception
|
Chris@13
|
186 */
|
Chris@13
|
187 public function getLastException()
|
Chris@13
|
188 {
|
Chris@13
|
189 if (!isset($this->lastException)) {
|
Chris@13
|
190 throw new \InvalidArgumentException('No most-recent exception');
|
Chris@13
|
191 }
|
Chris@13
|
192
|
Chris@13
|
193 return $this->lastException;
|
Chris@13
|
194 }
|
Chris@13
|
195
|
Chris@13
|
196 /**
|
Chris@13
|
197 * Set the most recent output from evaluated code.
|
Chris@13
|
198 *
|
Chris@13
|
199 * @param string $lastStdout
|
Chris@13
|
200 */
|
Chris@13
|
201 public function setLastStdout($lastStdout)
|
Chris@13
|
202 {
|
Chris@13
|
203 $this->lastStdout = $lastStdout;
|
Chris@13
|
204 }
|
Chris@13
|
205
|
Chris@13
|
206 /**
|
Chris@13
|
207 * Get the most recent output from evaluated code.
|
Chris@13
|
208 *
|
Chris@13
|
209 * @throws \InvalidArgumentException If no output has happened yet
|
Chris@13
|
210 *
|
Chris@13
|
211 * @return null|string
|
Chris@13
|
212 */
|
Chris@13
|
213 public function getLastStdout()
|
Chris@13
|
214 {
|
Chris@13
|
215 if (!isset($this->lastStdout)) {
|
Chris@13
|
216 throw new \InvalidArgumentException('No most-recent output');
|
Chris@13
|
217 }
|
Chris@13
|
218
|
Chris@13
|
219 return $this->lastStdout;
|
Chris@13
|
220 }
|
Chris@13
|
221
|
Chris@13
|
222 /**
|
Chris@13
|
223 * Set the bound object ($this variable) for the interactive shell.
|
Chris@13
|
224 *
|
Chris@16
|
225 * Note that this unsets the bound class, if any exists.
|
Chris@16
|
226 *
|
Chris@13
|
227 * @param object|null $boundObject
|
Chris@13
|
228 */
|
Chris@13
|
229 public function setBoundObject($boundObject)
|
Chris@13
|
230 {
|
Chris@17
|
231 $this->boundObject = \is_object($boundObject) ? $boundObject : null;
|
Chris@16
|
232 $this->boundClass = null;
|
Chris@13
|
233 }
|
Chris@13
|
234
|
Chris@13
|
235 /**
|
Chris@13
|
236 * Get the bound object ($this variable) for the interactive shell.
|
Chris@13
|
237 *
|
Chris@13
|
238 * @return object|null
|
Chris@13
|
239 */
|
Chris@13
|
240 public function getBoundObject()
|
Chris@13
|
241 {
|
Chris@13
|
242 return $this->boundObject;
|
Chris@13
|
243 }
|
Chris@13
|
244
|
Chris@13
|
245 /**
|
Chris@16
|
246 * Set the bound class (self) for the interactive shell.
|
Chris@16
|
247 *
|
Chris@16
|
248 * Note that this unsets the bound object, if any exists.
|
Chris@16
|
249 *
|
Chris@16
|
250 * @param string|null $boundClass
|
Chris@16
|
251 */
|
Chris@16
|
252 public function setBoundClass($boundClass)
|
Chris@16
|
253 {
|
Chris@17
|
254 $this->boundClass = (\is_string($boundClass) && $boundClass !== '') ? $boundClass : null;
|
Chris@16
|
255 $this->boundObject = null;
|
Chris@16
|
256 }
|
Chris@16
|
257
|
Chris@16
|
258 /**
|
Chris@16
|
259 * Get the bound class (self) for the interactive shell.
|
Chris@16
|
260 *
|
Chris@16
|
261 * @return string|null
|
Chris@16
|
262 */
|
Chris@16
|
263 public function getBoundClass()
|
Chris@16
|
264 {
|
Chris@16
|
265 return $this->boundClass;
|
Chris@16
|
266 }
|
Chris@16
|
267
|
Chris@16
|
268 /**
|
Chris@13
|
269 * Set command-scope magic variables: $__class, $__file, etc.
|
Chris@13
|
270 *
|
Chris@13
|
271 * @param array $commandScopeVariables
|
Chris@13
|
272 */
|
Chris@13
|
273 public function setCommandScopeVariables(array $commandScopeVariables)
|
Chris@13
|
274 {
|
Chris@13
|
275 $vars = [];
|
Chris@13
|
276 foreach ($commandScopeVariables as $key => $value) {
|
Chris@13
|
277 // kind of type check
|
Chris@17
|
278 if (\is_scalar($value) && \in_array($key, self::$commandScopeNames)) {
|
Chris@13
|
279 $vars[$key] = $value;
|
Chris@13
|
280 }
|
Chris@13
|
281 }
|
Chris@13
|
282
|
Chris@13
|
283 $this->commandScopeVariables = $vars;
|
Chris@13
|
284 }
|
Chris@13
|
285
|
Chris@13
|
286 /**
|
Chris@13
|
287 * Get command-scope magic variables: $__class, $__file, etc.
|
Chris@13
|
288 *
|
Chris@13
|
289 * @return array
|
Chris@13
|
290 */
|
Chris@13
|
291 public function getCommandScopeVariables()
|
Chris@13
|
292 {
|
Chris@13
|
293 return $this->commandScopeVariables;
|
Chris@13
|
294 }
|
Chris@13
|
295
|
Chris@13
|
296 /**
|
Chris@13
|
297 * Get unused command-scope magic variables names: __class, __file, etc.
|
Chris@13
|
298 *
|
Chris@13
|
299 * This is used by the shell to unset old command-scope variables after a
|
Chris@13
|
300 * new batch is set.
|
Chris@13
|
301 *
|
Chris@13
|
302 * @return array Array of unused variable names
|
Chris@13
|
303 */
|
Chris@13
|
304 public function getUnusedCommandScopeVariableNames()
|
Chris@13
|
305 {
|
Chris@17
|
306 return \array_diff(self::$commandScopeNames, \array_keys($this->commandScopeVariables));
|
Chris@13
|
307 }
|
Chris@13
|
308
|
Chris@13
|
309 /**
|
Chris@13
|
310 * Check whether a variable name is a magic variable.
|
Chris@13
|
311 *
|
Chris@13
|
312 * @param string $name
|
Chris@13
|
313 *
|
Chris@13
|
314 * @return bool
|
Chris@13
|
315 */
|
Chris@13
|
316 public static function isSpecialVariableName($name)
|
Chris@13
|
317 {
|
Chris@17
|
318 return \in_array($name, self::$specialNames) || \in_array($name, self::$commandScopeNames);
|
Chris@13
|
319 }
|
Chris@13
|
320 }
|