Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 * This file is part of Psy Shell.
|
Chris@0
|
5 *
|
Chris@0
|
6 * (c) 2012-2017 Justin Hileman
|
Chris@0
|
7 *
|
Chris@0
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@0
|
9 * file that was distributed with this source code.
|
Chris@0
|
10 */
|
Chris@0
|
11
|
Chris@0
|
12 namespace Psy\Formatter;
|
Chris@0
|
13
|
Chris@0
|
14 use Psy\Reflection\ReflectionConstant;
|
Chris@0
|
15 use Psy\Reflection\ReflectionLanguageConstruct;
|
Chris@0
|
16 use Psy\Util\Json;
|
Chris@0
|
17 use Symfony\Component\Console\Formatter\OutputFormatter;
|
Chris@0
|
18
|
Chris@0
|
19 /**
|
Chris@0
|
20 * An abstract representation of a function, class or property signature.
|
Chris@0
|
21 */
|
Chris@0
|
22 class SignatureFormatter implements Formatter
|
Chris@0
|
23 {
|
Chris@0
|
24 /**
|
Chris@0
|
25 * Format a signature for the given reflector.
|
Chris@0
|
26 *
|
Chris@0
|
27 * Defers to subclasses to do the actual formatting.
|
Chris@0
|
28 *
|
Chris@0
|
29 * @param \Reflector $reflector
|
Chris@0
|
30 *
|
Chris@0
|
31 * @return string Formatted signature
|
Chris@0
|
32 */
|
Chris@0
|
33 public static function format(\Reflector $reflector)
|
Chris@0
|
34 {
|
Chris@0
|
35 switch (true) {
|
Chris@0
|
36 case $reflector instanceof \ReflectionFunction:
|
Chris@0
|
37 case $reflector instanceof ReflectionLanguageConstruct:
|
Chris@0
|
38 return self::formatFunction($reflector);
|
Chris@0
|
39
|
Chris@0
|
40 // this case also covers \ReflectionObject:
|
Chris@0
|
41 case $reflector instanceof \ReflectionClass:
|
Chris@0
|
42 return self::formatClass($reflector);
|
Chris@0
|
43
|
Chris@0
|
44 case $reflector instanceof ReflectionConstant:
|
Chris@0
|
45 return self::formatConstant($reflector);
|
Chris@0
|
46
|
Chris@0
|
47 case $reflector instanceof \ReflectionMethod:
|
Chris@0
|
48 return self::formatMethod($reflector);
|
Chris@0
|
49
|
Chris@0
|
50 case $reflector instanceof \ReflectionProperty:
|
Chris@0
|
51 return self::formatProperty($reflector);
|
Chris@0
|
52
|
Chris@0
|
53 default:
|
Chris@0
|
54 throw new \InvalidArgumentException('Unexpected Reflector class: ' . get_class($reflector));
|
Chris@0
|
55 }
|
Chris@0
|
56 }
|
Chris@0
|
57
|
Chris@0
|
58 /**
|
Chris@0
|
59 * Print the signature name.
|
Chris@0
|
60 *
|
Chris@0
|
61 * @param \Reflector $reflector
|
Chris@0
|
62 *
|
Chris@0
|
63 * @return string Formatted name
|
Chris@0
|
64 */
|
Chris@0
|
65 public static function formatName(\Reflector $reflector)
|
Chris@0
|
66 {
|
Chris@0
|
67 return $reflector->getName();
|
Chris@0
|
68 }
|
Chris@0
|
69
|
Chris@0
|
70 /**
|
Chris@0
|
71 * Print the method, property or class modifiers.
|
Chris@0
|
72 *
|
Chris@0
|
73 * Technically this should be a trait. Can't wait for 5.4 :)
|
Chris@0
|
74 *
|
Chris@0
|
75 * @param \Reflector $reflector
|
Chris@0
|
76 *
|
Chris@0
|
77 * @return string Formatted modifiers
|
Chris@0
|
78 */
|
Chris@0
|
79 private static function formatModifiers(\Reflector $reflector)
|
Chris@0
|
80 {
|
Chris@0
|
81 return implode(' ', array_map(function ($modifier) {
|
Chris@0
|
82 return sprintf('<keyword>%s</keyword>', $modifier);
|
Chris@0
|
83 }, \Reflection::getModifierNames($reflector->getModifiers())));
|
Chris@0
|
84 }
|
Chris@0
|
85
|
Chris@0
|
86 /**
|
Chris@0
|
87 * Format a class signature.
|
Chris@0
|
88 *
|
Chris@0
|
89 * @param \ReflectionClass $reflector
|
Chris@0
|
90 *
|
Chris@0
|
91 * @return string Formatted signature
|
Chris@0
|
92 */
|
Chris@0
|
93 private static function formatClass(\ReflectionClass $reflector)
|
Chris@0
|
94 {
|
Chris@0
|
95 $chunks = array();
|
Chris@0
|
96
|
Chris@0
|
97 if ($modifiers = self::formatModifiers($reflector)) {
|
Chris@0
|
98 $chunks[] = $modifiers;
|
Chris@0
|
99 }
|
Chris@0
|
100
|
Chris@0
|
101 if (version_compare(PHP_VERSION, '5.4', '>=') && $reflector->isTrait()) {
|
Chris@0
|
102 $chunks[] = 'trait';
|
Chris@0
|
103 } else {
|
Chris@0
|
104 $chunks[] = $reflector->isInterface() ? 'interface' : 'class';
|
Chris@0
|
105 }
|
Chris@0
|
106
|
Chris@0
|
107 $chunks[] = sprintf('<class>%s</class>', self::formatName($reflector));
|
Chris@0
|
108
|
Chris@0
|
109 if ($parent = $reflector->getParentClass()) {
|
Chris@0
|
110 $chunks[] = 'extends';
|
Chris@0
|
111 $chunks[] = sprintf('<class>%s</class>', $parent->getName());
|
Chris@0
|
112 }
|
Chris@0
|
113
|
Chris@0
|
114 $interfaces = $reflector->getInterfaceNames();
|
Chris@0
|
115 if (!empty($interfaces)) {
|
Chris@0
|
116 sort($interfaces);
|
Chris@0
|
117
|
Chris@0
|
118 $chunks[] = 'implements';
|
Chris@0
|
119 $chunks[] = implode(', ', array_map(function ($name) {
|
Chris@0
|
120 return sprintf('<class>%s</class>', $name);
|
Chris@0
|
121 }, $interfaces));
|
Chris@0
|
122 }
|
Chris@0
|
123
|
Chris@0
|
124 return implode(' ', $chunks);
|
Chris@0
|
125 }
|
Chris@0
|
126
|
Chris@0
|
127 /**
|
Chris@0
|
128 * Format a constant signature.
|
Chris@0
|
129 *
|
Chris@0
|
130 * @param ReflectionConstant $reflector
|
Chris@0
|
131 *
|
Chris@0
|
132 * @return string Formatted signature
|
Chris@0
|
133 */
|
Chris@0
|
134 private static function formatConstant(ReflectionConstant $reflector)
|
Chris@0
|
135 {
|
Chris@0
|
136 $value = $reflector->getValue();
|
Chris@0
|
137 $style = self::getTypeStyle($value);
|
Chris@0
|
138
|
Chris@0
|
139 return sprintf(
|
Chris@0
|
140 '<keyword>const</keyword> <const>%s</const> = <%s>%s</%s>',
|
Chris@0
|
141 self::formatName($reflector),
|
Chris@0
|
142 $style,
|
Chris@0
|
143 OutputFormatter::escape(Json::encode($value)),
|
Chris@0
|
144 $style
|
Chris@0
|
145 );
|
Chris@0
|
146 }
|
Chris@0
|
147
|
Chris@0
|
148 /**
|
Chris@0
|
149 * Helper for getting output style for a given value's type.
|
Chris@0
|
150 *
|
Chris@0
|
151 * @param mixed $value
|
Chris@0
|
152 *
|
Chris@0
|
153 * @return string
|
Chris@0
|
154 */
|
Chris@0
|
155 private static function getTypeStyle($value)
|
Chris@0
|
156 {
|
Chris@0
|
157 if (is_int($value) || is_float($value)) {
|
Chris@0
|
158 return 'number';
|
Chris@0
|
159 } elseif (is_string($value)) {
|
Chris@0
|
160 return 'string';
|
Chris@0
|
161 } elseif (is_bool($value) || is_null($value)) {
|
Chris@0
|
162 return 'bool';
|
Chris@0
|
163 } else {
|
Chris@0
|
164 return 'strong';
|
Chris@0
|
165 }
|
Chris@0
|
166 }
|
Chris@0
|
167
|
Chris@0
|
168 /**
|
Chris@0
|
169 * Format a property signature.
|
Chris@0
|
170 *
|
Chris@0
|
171 * @param \ReflectionProperty $reflector
|
Chris@0
|
172 *
|
Chris@0
|
173 * @return string Formatted signature
|
Chris@0
|
174 */
|
Chris@0
|
175 private static function formatProperty(\ReflectionProperty $reflector)
|
Chris@0
|
176 {
|
Chris@0
|
177 return sprintf(
|
Chris@0
|
178 '%s <strong>$%s</strong>',
|
Chris@0
|
179 self::formatModifiers($reflector),
|
Chris@0
|
180 $reflector->getName()
|
Chris@0
|
181 );
|
Chris@0
|
182 }
|
Chris@0
|
183
|
Chris@0
|
184 /**
|
Chris@0
|
185 * Format a function signature.
|
Chris@0
|
186 *
|
Chris@0
|
187 * @param \ReflectionFunction $reflector
|
Chris@0
|
188 *
|
Chris@0
|
189 * @return string Formatted signature
|
Chris@0
|
190 */
|
Chris@0
|
191 private static function formatFunction(\ReflectionFunctionAbstract $reflector)
|
Chris@0
|
192 {
|
Chris@0
|
193 return sprintf(
|
Chris@0
|
194 '<keyword>function</keyword> %s<function>%s</function>(%s)',
|
Chris@0
|
195 $reflector->returnsReference() ? '&' : '',
|
Chris@0
|
196 self::formatName($reflector),
|
Chris@0
|
197 implode(', ', self::formatFunctionParams($reflector))
|
Chris@0
|
198 );
|
Chris@0
|
199 }
|
Chris@0
|
200
|
Chris@0
|
201 /**
|
Chris@0
|
202 * Format a method signature.
|
Chris@0
|
203 *
|
Chris@0
|
204 * @param \ReflectionMethod $reflector
|
Chris@0
|
205 *
|
Chris@0
|
206 * @return string Formatted signature
|
Chris@0
|
207 */
|
Chris@0
|
208 private static function formatMethod(\ReflectionMethod $reflector)
|
Chris@0
|
209 {
|
Chris@0
|
210 return sprintf(
|
Chris@0
|
211 '%s %s',
|
Chris@0
|
212 self::formatModifiers($reflector),
|
Chris@0
|
213 self::formatFunction($reflector)
|
Chris@0
|
214 );
|
Chris@0
|
215 }
|
Chris@0
|
216
|
Chris@0
|
217 /**
|
Chris@0
|
218 * Print the function params.
|
Chris@0
|
219 *
|
Chris@0
|
220 * @param \ReflectionFunctionAbstract $reflector
|
Chris@0
|
221 *
|
Chris@0
|
222 * @return string
|
Chris@0
|
223 */
|
Chris@0
|
224 private static function formatFunctionParams(\ReflectionFunctionAbstract $reflector)
|
Chris@0
|
225 {
|
Chris@0
|
226 $params = array();
|
Chris@0
|
227 foreach ($reflector->getParameters() as $param) {
|
Chris@0
|
228 $hint = '';
|
Chris@0
|
229 try {
|
Chris@0
|
230 if ($param->isArray()) {
|
Chris@0
|
231 $hint = '<keyword>array</keyword> ';
|
Chris@0
|
232 } elseif ($class = $param->getClass()) {
|
Chris@0
|
233 $hint = sprintf('<class>%s</class> ', $class->getName());
|
Chris@0
|
234 }
|
Chris@0
|
235 } catch (\Exception $e) {
|
Chris@0
|
236 // sometimes we just don't know...
|
Chris@0
|
237 // bad class names, or autoloaded classes that haven't been loaded yet, or whathaveyou.
|
Chris@0
|
238 // come to think of it, the only time I've seen this is with the intl extension.
|
Chris@0
|
239
|
Chris@0
|
240 // Hax: we'll try to extract it :P
|
Chris@0
|
241 $chunks = explode('$' . $param->getName(), (string) $param);
|
Chris@0
|
242 $chunks = explode(' ', trim($chunks[0]));
|
Chris@0
|
243 $guess = end($chunks);
|
Chris@0
|
244
|
Chris@0
|
245 $hint = sprintf('<urgent>%s</urgent> ', $guess);
|
Chris@0
|
246 }
|
Chris@0
|
247
|
Chris@0
|
248 if ($param->isOptional()) {
|
Chris@0
|
249 if (!$param->isDefaultValueAvailable()) {
|
Chris@0
|
250 $value = 'unknown';
|
Chris@0
|
251 $typeStyle = 'urgent';
|
Chris@0
|
252 } else {
|
Chris@0
|
253 $value = $param->getDefaultValue();
|
Chris@0
|
254 $typeStyle = self::getTypeStyle($value);
|
Chris@0
|
255 $value = is_array($value) ? 'array()' : is_null($value) ? 'null' : var_export($value, true);
|
Chris@0
|
256 }
|
Chris@0
|
257 $default = sprintf(' = <%s>%s</%s>', $typeStyle, OutputFormatter::escape($value), $typeStyle);
|
Chris@0
|
258 } else {
|
Chris@0
|
259 $default = '';
|
Chris@0
|
260 }
|
Chris@0
|
261
|
Chris@0
|
262 $params[] = sprintf(
|
Chris@0
|
263 '%s%s<strong>$%s</strong>%s',
|
Chris@0
|
264 $param->isPassedByReference() ? '&' : '',
|
Chris@0
|
265 $hint,
|
Chris@0
|
266 $param->getName(),
|
Chris@0
|
267 $default
|
Chris@0
|
268 );
|
Chris@0
|
269 }
|
Chris@0
|
270
|
Chris@0
|
271 return $params;
|
Chris@0
|
272 }
|
Chris@0
|
273 }
|