4a0bfa8e9294156987f82de3567c49dd1466b319
[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         return implode(' ', array_map(function ($modifier) {
82             return sprintf('<keyword>%s</keyword>', $modifier);
83         }, \Reflection::getModifierNames($reflector->getModifiers())));
84     }
85
86     /**
87      * Format a class signature.
88      *
89      * @param \ReflectionClass $reflector
90      *
91      * @return string Formatted signature
92      */
93     private static function formatClass(\ReflectionClass $reflector)
94     {
95         $chunks = [];
96
97         if ($modifiers = self::formatModifiers($reflector)) {
98             $chunks[] = $modifiers;
99         }
100
101         if ($reflector->isTrait()) {
102             $chunks[] = 'trait';
103         } else {
104             $chunks[] = $reflector->isInterface() ? 'interface' : 'class';
105         }
106
107         $chunks[] = sprintf('<class>%s</class>', self::formatName($reflector));
108
109         if ($parent = $reflector->getParentClass()) {
110             $chunks[] = 'extends';
111             $chunks[] = sprintf('<class>%s</class>', $parent->getName());
112         }
113
114         $interfaces = $reflector->getInterfaceNames();
115         if (!empty($interfaces)) {
116             sort($interfaces);
117
118             $chunks[] = 'implements';
119             $chunks[] = implode(', ', array_map(function ($name) {
120                 return sprintf('<class>%s</class>', $name);
121             }, $interfaces));
122         }
123
124         return implode(' ', $chunks);
125     }
126
127     /**
128      * Format a constant signature.
129      *
130      * @param ReflectionConstant $reflector
131      *
132      * @return string Formatted signature
133      */
134     private static function formatConstant(ReflectionConstant $reflector)
135     {
136         $value = $reflector->getValue();
137         $style = self::getTypeStyle($value);
138
139         return sprintf(
140             '<keyword>const</keyword> <const>%s</const> = <%s>%s</%s>',
141             self::formatName($reflector),
142             $style,
143             OutputFormatter::escape(Json::encode($value)),
144             $style
145         );
146     }
147
148     /**
149      * Helper for getting output style for a given value's type.
150      *
151      * @param mixed $value
152      *
153      * @return string
154      */
155     private static function getTypeStyle($value)
156     {
157         if (is_int($value) || is_float($value)) {
158             return 'number';
159         } elseif (is_string($value)) {
160             return 'string';
161         } elseif (is_bool($value) || is_null($value)) {
162             return 'bool';
163         } else {
164             return 'strong';
165         }
166     }
167
168     /**
169      * Format a property signature.
170      *
171      * @param \ReflectionProperty $reflector
172      *
173      * @return string Formatted signature
174      */
175     private static function formatProperty(\ReflectionProperty $reflector)
176     {
177         return sprintf(
178             '%s <strong>$%s</strong>',
179             self::formatModifiers($reflector),
180             $reflector->getName()
181         );
182     }
183
184     /**
185      * Format a function signature.
186      *
187      * @param \ReflectionFunction $reflector
188      *
189      * @return string Formatted signature
190      */
191     private static function formatFunction(\ReflectionFunctionAbstract $reflector)
192     {
193         return sprintf(
194             '<keyword>function</keyword> %s<function>%s</function>(%s)',
195             $reflector->returnsReference() ? '&' : '',
196             self::formatName($reflector),
197             implode(', ', self::formatFunctionParams($reflector))
198         );
199     }
200
201     /**
202      * Format a method signature.
203      *
204      * @param \ReflectionMethod $reflector
205      *
206      * @return string Formatted signature
207      */
208     private static function formatMethod(\ReflectionMethod $reflector)
209     {
210         return sprintf(
211             '%s %s',
212             self::formatModifiers($reflector),
213             self::formatFunction($reflector)
214         );
215     }
216
217     /**
218      * Print the function params.
219      *
220      * @param \ReflectionFunctionAbstract $reflector
221      *
222      * @return array
223      */
224     private static function formatFunctionParams(\ReflectionFunctionAbstract $reflector)
225     {
226         $params = [];
227         foreach ($reflector->getParameters() as $param) {
228             $hint = '';
229             try {
230                 if ($param->isArray()) {
231                     $hint = '<keyword>array</keyword> ';
232                 } elseif ($class = $param->getClass()) {
233                     $hint = sprintf('<class>%s</class> ', $class->getName());
234                 }
235             } catch (\Exception $e) {
236                 // sometimes we just don't know...
237                 // bad class names, or autoloaded classes that haven't been loaded yet, or whathaveyou.
238                 // come to think of it, the only time I've seen this is with the intl extension.
239
240                 // Hax: we'll try to extract it :P
241                 $chunks = explode('$' . $param->getName(), (string) $param);
242                 $chunks = explode(' ', trim($chunks[0]));
243                 $guess  = end($chunks);
244
245                 $hint = sprintf('<urgent>%s</urgent> ', $guess);
246             }
247
248             if ($param->isOptional()) {
249                 if (!$param->isDefaultValueAvailable()) {
250                     $value     = 'unknown';
251                     $typeStyle = 'urgent';
252                 } else {
253                     $value     = $param->getDefaultValue();
254                     $typeStyle = self::getTypeStyle($value);
255                     $value     = is_array($value) ? 'array()' : is_null($value) ? 'null' : var_export($value, true);
256                 }
257                 $default = sprintf(' = <%s>%s</%s>', $typeStyle, OutputFormatter::escape($value), $typeStyle);
258             } else {
259                 $default = '';
260             }
261
262             $params[] = sprintf(
263                 '%s%s<strong>$%s</strong>%s',
264                 $param->isPassedByReference() ? '&' : '',
265                 $hint,
266                 $param->getName(),
267                 $default
268             );
269         }
270
271         return $params;
272     }
273 }