Mercurial > hg > cmmr2012-drupal-site
comparison vendor/psy/psysh/src/Command/ReflectingCommand.php @ 0:c75dbcec494b
Initial commit from drush-created site
author | Chris Cannam |
---|---|
date | Thu, 05 Jul 2018 14:24:15 +0000 |
parents | |
children | a9cd425dd02b |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c75dbcec494b |
---|---|
1 <?php | |
2 | |
3 /* | |
4 * This file is part of Psy Shell. | |
5 * | |
6 * (c) 2012-2018 Justin Hileman | |
7 * | |
8 * For the full copyright and license information, please view the LICENSE | |
9 * file that was distributed with this source code. | |
10 */ | |
11 | |
12 namespace Psy\Command; | |
13 | |
14 use Psy\CodeCleaner\NoReturnValue; | |
15 use Psy\Context; | |
16 use Psy\ContextAware; | |
17 use Psy\Exception\ErrorException; | |
18 use Psy\Exception\RuntimeException; | |
19 use Psy\Util\Mirror; | |
20 | |
21 /** | |
22 * An abstract command with helpers for inspecting the current context. | |
23 */ | |
24 abstract class ReflectingCommand extends Command implements ContextAware | |
25 { | |
26 const CLASS_OR_FUNC = '/^[\\\\\w]+$/'; | |
27 const CLASS_MEMBER = '/^([\\\\\w]+)::(\w+)$/'; | |
28 const CLASS_STATIC = '/^([\\\\\w]+)::\$(\w+)$/'; | |
29 const INSTANCE_MEMBER = '/^(\$\w+)(::|->)(\w+)$/'; | |
30 | |
31 /** | |
32 * Context instance (for ContextAware interface). | |
33 * | |
34 * @var Context | |
35 */ | |
36 protected $context; | |
37 | |
38 /** | |
39 * ContextAware interface. | |
40 * | |
41 * @param Context $context | |
42 */ | |
43 public function setContext(Context $context) | |
44 { | |
45 $this->context = $context; | |
46 } | |
47 | |
48 /** | |
49 * Get the target for a value. | |
50 * | |
51 * @throws \InvalidArgumentException when the value specified can't be resolved | |
52 * | |
53 * @param string $valueName Function, class, variable, constant, method or property name | |
54 * | |
55 * @return array (class or instance name, member name, kind) | |
56 */ | |
57 protected function getTarget($valueName) | |
58 { | |
59 $valueName = trim($valueName); | |
60 $matches = []; | |
61 switch (true) { | |
62 case preg_match(self::CLASS_OR_FUNC, $valueName, $matches): | |
63 return [$this->resolveName($matches[0], true), null, 0]; | |
64 | |
65 case preg_match(self::CLASS_MEMBER, $valueName, $matches): | |
66 return [$this->resolveName($matches[1]), $matches[2], Mirror::CONSTANT | Mirror::METHOD]; | |
67 | |
68 case preg_match(self::CLASS_STATIC, $valueName, $matches): | |
69 return [$this->resolveName($matches[1]), $matches[2], Mirror::STATIC_PROPERTY | Mirror::PROPERTY]; | |
70 | |
71 case preg_match(self::INSTANCE_MEMBER, $valueName, $matches): | |
72 if ($matches[2] === '->') { | |
73 $kind = Mirror::METHOD | Mirror::PROPERTY; | |
74 } else { | |
75 $kind = Mirror::CONSTANT | Mirror::METHOD; | |
76 } | |
77 | |
78 return [$this->resolveObject($matches[1]), $matches[3], $kind]; | |
79 | |
80 default: | |
81 return [$this->resolveObject($valueName), null, 0]; | |
82 } | |
83 } | |
84 | |
85 /** | |
86 * Resolve a class or function name (with the current shell namespace). | |
87 * | |
88 * @throws ErrorException when `self` or `static` is used in a non-class scope | |
89 * | |
90 * @param string $name | |
91 * @param bool $includeFunctions (default: false) | |
92 * | |
93 * @return string | |
94 */ | |
95 protected function resolveName($name, $includeFunctions = false) | |
96 { | |
97 $shell = $this->getApplication(); | |
98 | |
99 // While not *technically* 100% accurate, let's treat `self` and `static` as equivalent. | |
100 if (in_array(strtolower($name), ['self', 'static'])) { | |
101 if ($boundClass = $shell->getBoundClass()) { | |
102 return $boundClass; | |
103 } | |
104 | |
105 if ($boundObject = $shell->getBoundObject()) { | |
106 return get_class($boundObject); | |
107 } | |
108 | |
109 $msg = sprintf('Cannot use "%s" when no class scope is active', strtolower($name)); | |
110 throw new ErrorException($msg, 0, E_USER_ERROR, "eval()'d code", 1); | |
111 } | |
112 | |
113 if (substr($name, 0, 1) === '\\') { | |
114 return $name; | |
115 } | |
116 | |
117 if ($namespace = $shell->getNamespace()) { | |
118 $fullName = $namespace . '\\' . $name; | |
119 | |
120 if (class_exists($fullName) || interface_exists($fullName) || ($includeFunctions && function_exists($fullName))) { | |
121 return $fullName; | |
122 } | |
123 } | |
124 | |
125 return $name; | |
126 } | |
127 | |
128 /** | |
129 * Get a Reflector and documentation for a function, class or instance, constant, method or property. | |
130 * | |
131 * @param string $valueName Function, class, variable, constant, method or property name | |
132 * | |
133 * @return array (value, Reflector) | |
134 */ | |
135 protected function getTargetAndReflector($valueName) | |
136 { | |
137 list($value, $member, $kind) = $this->getTarget($valueName); | |
138 | |
139 return [$value, Mirror::get($value, $member, $kind)]; | |
140 } | |
141 | |
142 /** | |
143 * Resolve code to a value in the current scope. | |
144 * | |
145 * @throws RuntimeException when the code does not return a value in the current scope | |
146 * | |
147 * @param string $code | |
148 * | |
149 * @return mixed Variable value | |
150 */ | |
151 protected function resolveCode($code) | |
152 { | |
153 try { | |
154 $value = $this->getApplication()->execute($code, true); | |
155 } catch (\Exception $e) { | |
156 // Swallow all exceptions? | |
157 } | |
158 | |
159 if (!isset($value) || $value instanceof NoReturnValue) { | |
160 throw new RuntimeException('Unknown target: ' . $code); | |
161 } | |
162 | |
163 return $value; | |
164 } | |
165 | |
166 /** | |
167 * Resolve code to an object in the current scope. | |
168 * | |
169 * @throws RuntimeException when the code resolves to a non-object value | |
170 * | |
171 * @param string $code | |
172 * | |
173 * @return object Variable instance | |
174 */ | |
175 private function resolveObject($code) | |
176 { | |
177 $value = $this->resolveCode($code); | |
178 | |
179 if (!is_object($value)) { | |
180 throw new RuntimeException('Unable to inspect a non-object'); | |
181 } | |
182 | |
183 return $value; | |
184 } | |
185 | |
186 /** | |
187 * @deprecated Use `resolveCode` instead | |
188 * | |
189 * @param string $name | |
190 * | |
191 * @return mixed Variable instance | |
192 */ | |
193 protected function resolveInstance($name) | |
194 { | |
195 @trigger_error('`resolveInstance` is deprecated; use `resolveCode` instead.', E_USER_DEPRECATED); | |
196 | |
197 return $this->resolveCode($name); | |
198 } | |
199 | |
200 /** | |
201 * Get a variable from the current shell scope. | |
202 * | |
203 * @param string $name | |
204 * | |
205 * @return mixed | |
206 */ | |
207 protected function getScopeVariable($name) | |
208 { | |
209 return $this->context->get($name); | |
210 } | |
211 | |
212 /** | |
213 * Get all scope variables from the current shell scope. | |
214 * | |
215 * @return array | |
216 */ | |
217 protected function getScopeVariables() | |
218 { | |
219 return $this->context->getAll(); | |
220 } | |
221 | |
222 /** | |
223 * Given a Reflector instance, set command-scope variables in the shell | |
224 * execution context. This is used to inject magic $__class, $__method and | |
225 * $__file variables (as well as a handful of others). | |
226 * | |
227 * @param \Reflector $reflector | |
228 */ | |
229 protected function setCommandScopeVariables(\Reflector $reflector) | |
230 { | |
231 $vars = []; | |
232 | |
233 switch (get_class($reflector)) { | |
234 case 'ReflectionClass': | |
235 case 'ReflectionObject': | |
236 $vars['__class'] = $reflector->name; | |
237 if ($reflector->inNamespace()) { | |
238 $vars['__namespace'] = $reflector->getNamespaceName(); | |
239 } | |
240 break; | |
241 | |
242 case 'ReflectionMethod': | |
243 $vars['__method'] = sprintf('%s::%s', $reflector->class, $reflector->name); | |
244 $vars['__class'] = $reflector->class; | |
245 $classReflector = $reflector->getDeclaringClass(); | |
246 if ($classReflector->inNamespace()) { | |
247 $vars['__namespace'] = $classReflector->getNamespaceName(); | |
248 } | |
249 break; | |
250 | |
251 case 'ReflectionFunction': | |
252 $vars['__function'] = $reflector->name; | |
253 if ($reflector->inNamespace()) { | |
254 $vars['__namespace'] = $reflector->getNamespaceName(); | |
255 } | |
256 break; | |
257 | |
258 case 'ReflectionGenerator': | |
259 $funcReflector = $reflector->getFunction(); | |
260 $vars['__function'] = $funcReflector->name; | |
261 if ($funcReflector->inNamespace()) { | |
262 $vars['__namespace'] = $funcReflector->getNamespaceName(); | |
263 } | |
264 if ($fileName = $reflector->getExecutingFile()) { | |
265 $vars['__file'] = $fileName; | |
266 $vars['__line'] = $reflector->getExecutingLine(); | |
267 $vars['__dir'] = dirname($fileName); | |
268 } | |
269 break; | |
270 | |
271 case 'ReflectionProperty': | |
272 case 'ReflectionClassConstant': | |
273 case 'Psy\Reflection\ReflectionClassConstant': | |
274 $classReflector = $reflector->getDeclaringClass(); | |
275 $vars['__class'] = $classReflector->name; | |
276 if ($classReflector->inNamespace()) { | |
277 $vars['__namespace'] = $classReflector->getNamespaceName(); | |
278 } | |
279 // no line for these, but this'll do | |
280 if ($fileName = $reflector->getDeclaringClass()->getFileName()) { | |
281 $vars['__file'] = $fileName; | |
282 $vars['__dir'] = dirname($fileName); | |
283 } | |
284 break; | |
285 | |
286 case 'Psy\Reflection\ReflectionConstant_': | |
287 if ($reflector->inNamespace()) { | |
288 $vars['__namespace'] = $reflector->getNamespaceName(); | |
289 } | |
290 break; | |
291 } | |
292 | |
293 if ($reflector instanceof \ReflectionClass || $reflector instanceof \ReflectionFunctionAbstract) { | |
294 if ($fileName = $reflector->getFileName()) { | |
295 $vars['__file'] = $fileName; | |
296 $vars['__line'] = $reflector->getStartLine(); | |
297 $vars['__dir'] = dirname($fileName); | |
298 } | |
299 } | |
300 | |
301 $this->context->setCommandScopeVariables($vars); | |
302 } | |
303 } |