WritingUtils.php 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <?php
  2. namespace Knuckles\Scribe\Tools;
  3. use Knuckles\Scribe\Scribe;
  4. use Symfony\Component\VarExporter\VarExporter;
  5. class WritingUtils
  6. {
  7. public static array $httpMethodToCssColour = [
  8. 'GET' => 'green',
  9. 'HEAD' => 'darkgreen',
  10. 'POST' => 'black',
  11. 'PUT' => 'darkblue',
  12. 'PATCH' => 'purple',
  13. 'DELETE' => 'red',
  14. 'OPTIONS' => 'grey',
  15. ];
  16. /**
  17. * Print a value as valid PHP, handling arrays and proper indentation.
  18. *
  19. * @param mixed $value
  20. * @param int $indentationLevel
  21. *
  22. * @return string
  23. * @throws \Symfony\Component\VarExporter\Exception\ExceptionInterface
  24. *
  25. */
  26. public static function printPhpValue($value, int $indentationLevel = 0): string
  27. {
  28. $output = VarExporter::export($value);
  29. // Padding with x spaces so they align
  30. $split = explode("\n", $output);
  31. $result = '';
  32. $padWith = str_repeat(' ', $indentationLevel);
  33. foreach ($split as $index => $line) {
  34. $result .= ($index == 0 ? '' : "\n$padWith") . $line;
  35. }
  36. return $result;
  37. }
  38. public static function printQueryParamsAsString(array $cleanQueryParams): string
  39. {
  40. $qs = '';
  41. foreach ($cleanQueryParams as $paramName => $value) {
  42. $qs .= static::printSingleQueryParamsAsString('', $paramName, $value, true);
  43. }
  44. return rtrim($qs, '&');
  45. }
  46. public static function printSingleQueryParamsAsString(string $prefix, string|int $key, mixed $parameters, bool $firstLevel): string
  47. {
  48. if (!is_array($parameters)) {
  49. if ($firstLevel) {
  50. return sprintf("%s=%s&", urlencode($key), urlencode($parameters));
  51. } else {
  52. if (is_string($key)) {
  53. return sprintf("%s[%s]=%s&", $prefix, urlencode($key), urlencode($parameters));
  54. } else {
  55. return sprintf("%s[]=%s&", $prefix, urlencode($parameters));
  56. }
  57. }
  58. } else {
  59. if ($firstLevel) {
  60. $newPrefix = urlencode($key);
  61. } else {
  62. $newPrefix = sprintf("%s[%s]", $prefix, urlencode($key));
  63. }
  64. $query = '';
  65. foreach ($parameters as $item => $itemValue) {
  66. $query .= static::printSingleQueryParamsAsString($newPrefix, $item, $itemValue, false);
  67. }
  68. }
  69. return $query;
  70. }
  71. /**
  72. * Print key-value query params as a hash { "key1": "value1", "key2": "value2" }
  73. * Supports custom delimiters (eg "=>", default: ":"),
  74. * custom braces (eg "[]", default: "{}"),
  75. * custom quotes (eg ', default: "),
  76. * custom indentation, line endings etc.
  77. * Expands/simplifies arrays {key: [1, 2,]} becomes {"key[0]": "1","key[1]": "2"}
  78. * Expands hashes {key: {a: 1, b: 2, c: {e: 3}}} becomes {"key[a]": "1", "key[b]": "2", "key[c][e]": "3"}
  79. *
  80. * @param array $cleanQueryParams
  81. * @param string $quote
  82. * @param string $delimiter
  83. * @param int $spacesIndentation
  84. * @param string $braces
  85. * @param int $closingBraceIndentation
  86. * @param string $startLinesWith
  87. * @param string $endLinesWith
  88. *
  89. * @return string
  90. */
  91. public static function printQueryParamsAsKeyValue(
  92. array $cleanQueryParams,
  93. string $quote = '"',
  94. string $delimiter = ":",
  95. int $spacesIndentation = 4,
  96. string $braces = "{}",
  97. int $closingBraceIndentation = 0,
  98. string $startLinesWith = '',
  99. string $endLinesWith = ','
  100. ): string {
  101. $output = isset($braces[0]) ? "{$braces[0]}\n" : '';
  102. foreach ($cleanQueryParams as $parameter => $value) {
  103. $output .= self::printSingleQueryParamAsKeyValue($value, $spacesIndentation, $startLinesWith, $quote,
  104. $parameter, $delimiter, $endLinesWith);
  105. }
  106. $closing = isset($braces[1]) ? str_repeat(" ", $closingBraceIndentation) . "{$braces[1]}" : '';
  107. return $output . $closing;
  108. }
  109. /**
  110. * @param mixed $value
  111. * @param int $spacesIndentation
  112. * @param string $startLinesWith
  113. * @param string $quote
  114. * @param string $parameter
  115. * @param string $delimiter
  116. * @param string $endLinesWith
  117. * @return string
  118. */
  119. protected static function printSingleQueryParamAsKeyValue(
  120. mixed $value, int $spacesIndentation, string $startLinesWith, string $quote,
  121. string $parameter, string $delimiter, string $endLinesWith
  122. ): string {
  123. if (!is_array($value)) {
  124. $output = str_repeat(" ", $spacesIndentation);
  125. // Example: -----"param_name": "value"----
  126. $formattedValue = is_bool($value) ? ($value ? 1 : 0) : $value;
  127. $output .= "$startLinesWith$quote$parameter$quote$delimiter $quote$formattedValue$quote$endLinesWith\n";
  128. } else {
  129. $output = '';
  130. if (count($value) == 0) {
  131. return $output;
  132. }
  133. // List query param (eg filter[]=haha should become "filter[0]": "haha")
  134. // Hash query param (eg filter[name]=john should become "filter[name]": "john")
  135. // Hash query param (eg filter[info][name]=john should become "filter[info][name]": "john")
  136. foreach ($value as $item => $itemValue) {
  137. $parameterString = sprintf('%s[%s]', $parameter, $item);
  138. if (is_array($itemValue)) {
  139. $output .= static::printSingleQueryParamAsKeyValue($itemValue, $spacesIndentation, $startLinesWith,
  140. $quote, $parameterString, $delimiter, $endLinesWith);
  141. } else {
  142. $output .= str_repeat(" ", $spacesIndentation);
  143. $output .= sprintf("%s%s%s%s%s %s%s%s%s\n", $startLinesWith, $quote, $parameterString, $quote,
  144. $delimiter, $quote, $itemValue, $quote, $endLinesWith);
  145. }
  146. }
  147. }
  148. return $output;
  149. }
  150. /**
  151. * Expand a request parameter into one or more parameters to be used when sending as form-data.
  152. * A primitive value like ("name", "John") is returned as ["name" => "John"]
  153. * Lists like ("filter", ["haha"]) becomes ["filter[]" => "haha"]
  154. * Maps like ("filter", ["name" => "john", "age" => "12"]) become ["filter[name]" => "john", "filter[age]" => 12]
  155. *
  156. * @param string $parameter The name of the parameter
  157. * @param mixed $value Value of the parameter
  158. *
  159. * @return array
  160. */
  161. public static function getParameterNamesAndValuesForFormData(string $parameter, $value): array
  162. {
  163. if (!is_array($value)) {
  164. return [$parameter => $value];
  165. }
  166. // We assume it's a list if its first key is 0
  167. $keys = array_keys($value);
  168. if (count($keys) && $keys[0] === 0) {
  169. if (is_array($value[0])) {
  170. // handle nested arrays/objects
  171. $params = [];
  172. $expanded = self::getParameterNamesAndValuesForFormData('', $value[0]);
  173. foreach ($expanded as $fieldName => $itemValue) {
  174. $paramName = $parameter . '[]' . $fieldName;
  175. $params[$paramName] = $itemValue;
  176. }
  177. return $params;
  178. }
  179. return [$parameter . '[]' => $value[0]];
  180. }
  181. // Transform hashes
  182. $params = [];
  183. foreach ($value as $item => $itemValue) {
  184. if (is_array($itemValue)) {
  185. $expanded = self::getParameterNamesAndValuesForFormData('', $itemValue);
  186. foreach ($expanded as $fieldName => $subItemValue) {
  187. $paramName = $parameter . "[$item]" . $fieldName;
  188. $params[$paramName] = $subItemValue;
  189. }
  190. } else {
  191. $params[$parameter . "[$item]"] = $itemValue;
  192. }
  193. }
  194. return $params;
  195. }
  196. public static function getSampleBody(array $nestedBodyParameters)
  197. {
  198. if (!empty($nestedBodyParameters['[]'])) {
  199. return [self::getSampleBody($nestedBodyParameters['[]']['__fields'])];
  200. }
  201. return array_map(function ($param) {
  202. if (!empty($param['__fields'])) {
  203. return self::getSampleBody($param['__fields']);
  204. }
  205. return $param['example'];
  206. }, $nestedBodyParameters);
  207. }
  208. /**
  209. * Convert a list of possible values to a friendly string:
  210. * [1, 2, 3] -> "1, 2, or 3"
  211. * [1, 2] -> "1 or 2"
  212. * [1] -> "1"
  213. * Each value is wrapped in HTML <code> tags, so you actually get "<code>1</code>, <code>2</code>, or
  214. * <code>3</code>"
  215. *
  216. * @param array $list
  217. *
  218. * @return string
  219. */
  220. public static function getListOfValuesAsFriendlyHtmlString(array $list = [], string $conjunction = "or"): string
  221. {
  222. return match (count($list)) {
  223. 1 => "<code>{$list[0]}</code>",
  224. 2 => "<code>{$list[0]}</code> $conjunction <code>{$list[1]}</code>",
  225. default => "<code>"
  226. . implode('</code>, <code>', array_slice($list, 0, -1))
  227. . "</code>, $conjunction <code>" . end($list) . "</code>",
  228. };
  229. }
  230. /**
  231. * Convert a path like 'js/tryitout.js' to include the current Scribe version ('js/tryitout-3.0.1.js')
  232. */
  233. public static function getVersionedAsset(string $assetPath): string
  234. {
  235. $index = strrpos($assetPath, ".");
  236. return substr_replace($assetPath, '-' . Scribe::VERSION, $index, 0);
  237. }
  238. }