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