/vendor/psy/psysh/src/Psy/Formatter/SignatureFormatter.php

https://gitlab.com/judielsm/Handora · PHP · 269 lines · 149 code · 30 blank · 90 comment · 16 complexity · faa15d0fc0d21ea406da8cc4e63111e9 MD5 · raw file

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