PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/src/applications/config/option/PhabricatorApplicationConfigOptions.php

https://github.com/navyuginfo/phabricator
PHP | 238 lines | 202 code | 21 blank | 15 comment | 26 complexity | a01a50769f44d15455b1a7b614e48091 MD5 | raw file
Possible License(s): Apache-2.0, LGPL-2.0, LGPL-3.0, MIT, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  1. <?php
  2. abstract class PhabricatorApplicationConfigOptions extends Phobject {
  3. abstract public function getName();
  4. abstract public function getDescription();
  5. abstract public function getOptions();
  6. public function validateOption(PhabricatorConfigOption $option, $value) {
  7. if ($value === $option->getDefault()) {
  8. return;
  9. }
  10. if ($value === null) {
  11. return;
  12. }
  13. if ($option->isCustomType()) {
  14. return $option->getCustomObject()->validateOption($option, $value);
  15. }
  16. switch ($option->getType()) {
  17. case 'bool':
  18. if ($value !== true &&
  19. $value !== false) {
  20. throw new PhabricatorConfigValidationException(
  21. pht(
  22. "Option '%s' is of type bool, but value is not true or false.",
  23. $option->getKey()));
  24. }
  25. break;
  26. case 'int':
  27. if (!is_int($value)) {
  28. throw new PhabricatorConfigValidationException(
  29. pht(
  30. "Option '%s' is of type int, but value is not an integer.",
  31. $option->getKey()));
  32. }
  33. break;
  34. case 'string':
  35. if (!is_string($value)) {
  36. throw new PhabricatorConfigValidationException(
  37. pht(
  38. "Option '%s' is of type string, but value is not a string.",
  39. $option->getKey()));
  40. }
  41. break;
  42. case 'class':
  43. $symbols = id(new PhutilSymbolLoader())
  44. ->setType('class')
  45. ->setAncestorClass($option->getBaseClass())
  46. ->setConcreteOnly(true)
  47. ->selectSymbolsWithoutLoading();
  48. $names = ipull($symbols, 'name', 'name');
  49. if (empty($names[$value])) {
  50. throw new PhabricatorConfigValidationException(
  51. pht(
  52. "Option '%s' value must name a class extending '%s'.",
  53. $option->getKey(),
  54. $option->getBaseClass()));
  55. }
  56. break;
  57. case 'set':
  58. $valid = true;
  59. if (!is_array($value)) {
  60. throw new PhabricatorConfigValidationException(
  61. pht(
  62. "Option '%s' must be a set, but value is not an array.",
  63. $option->getKey()));
  64. }
  65. foreach ($value as $v) {
  66. if ($v !== true) {
  67. throw new PhabricatorConfigValidationException(
  68. pht(
  69. "Option '%s' must be a set, but array contains values other ".
  70. "than 'true'.",
  71. $option->getKey()));
  72. }
  73. }
  74. break;
  75. case 'list<regex>':
  76. $valid = true;
  77. if (!is_array($value)) {
  78. throw new PhabricatorConfigValidationException(
  79. pht(
  80. "Option '%s' must be a list of regular expressions, but value ".
  81. "is not an array.",
  82. $option->getKey()));
  83. }
  84. if ($value && array_keys($value) != range(0, count($value) - 1)) {
  85. throw new PhabricatorConfigValidationException(
  86. pht(
  87. "Option '%s' must be a list of regular expressions, but the ".
  88. "value is a map with unnatural keys.",
  89. $option->getKey()));
  90. }
  91. foreach ($value as $v) {
  92. $ok = @preg_match($v, '');
  93. if ($ok === false) {
  94. throw new PhabricatorConfigValidationException(
  95. pht(
  96. "Option '%s' must be a list of regular expressions, but the ".
  97. "value '%s' is not a valid regular expression.",
  98. $option->getKey(),
  99. $v));
  100. }
  101. }
  102. break;
  103. case 'list<string>':
  104. $valid = true;
  105. if (!is_array($value)) {
  106. throw new PhabricatorConfigValidationException(
  107. pht(
  108. "Option '%s' must be a list of strings, but value is not ".
  109. "an array.",
  110. $option->getKey()));
  111. }
  112. if ($value && array_keys($value) != range(0, count($value) - 1)) {
  113. throw new PhabricatorConfigValidationException(
  114. pht(
  115. "Option '%s' must be a list of strings, but the value is a ".
  116. "map with unnatural keys.",
  117. $option->getKey()));
  118. }
  119. foreach ($value as $v) {
  120. if (!is_string($v)) {
  121. throw new PhabricatorConfigValidationException(
  122. pht(
  123. "Option '%s' must be a list of strings, but it contains one ".
  124. "or more non-strings.",
  125. $option->getKey()));
  126. }
  127. }
  128. break;
  129. case 'wild':
  130. default:
  131. break;
  132. }
  133. $this->didValidateOption($option, $value);
  134. }
  135. protected function didValidateOption(
  136. PhabricatorConfigOption $option,
  137. $value) {
  138. // Hook for subclasses to do complex validation.
  139. return;
  140. }
  141. /**
  142. * Hook to render additional hints based on, e.g., the viewing user, request,
  143. * or other context. For example, this is used to show workspace IDs when
  144. * configuring `asana.workspace-id`.
  145. *
  146. * @param PhabricatorConfigOption Option being rendered.
  147. * @param AphrontRequest Active request.
  148. * @return wild Additional contextual description
  149. * information.
  150. */
  151. public function renderContextualDescription(
  152. PhabricatorConfigOption $option,
  153. AphrontRequest $request) {
  154. return null;
  155. }
  156. public function getKey() {
  157. $class = get_class($this);
  158. $matches = null;
  159. if (preg_match('/^Phabricator(.*)ConfigOptions$/', $class, $matches)) {
  160. return strtolower($matches[1]);
  161. }
  162. return strtolower(get_class($this));
  163. }
  164. final protected function newOption($key, $type, $default) {
  165. return id(new PhabricatorConfigOption())
  166. ->setKey($key)
  167. ->setType($type)
  168. ->setDefault($default)
  169. ->setGroup($this);
  170. }
  171. final public static function loadAll($external_only = false) {
  172. $symbols = id(new PhutilSymbolLoader())
  173. ->setAncestorClass('PhabricatorApplicationConfigOptions')
  174. ->setConcreteOnly(true)
  175. ->selectAndLoadSymbols();
  176. $groups = array();
  177. foreach ($symbols as $symbol) {
  178. if ($external_only && $symbol['library'] == 'phabricator') {
  179. continue;
  180. }
  181. $obj = newv($symbol['name'], array());
  182. $key = $obj->getKey();
  183. if (isset($groups[$key])) {
  184. $pclass = get_class($groups[$key]);
  185. $nclass = $symbol['name'];
  186. throw new Exception(
  187. "Multiple PhabricatorApplicationConfigOptions subclasses have the ".
  188. "same key ('{$key}'): {$pclass}, {$nclass}.");
  189. }
  190. $groups[$key] = $obj;
  191. }
  192. return $groups;
  193. }
  194. final public static function loadAllOptions($external_only = false) {
  195. $groups = self::loadAll($external_only);
  196. $options = array();
  197. foreach ($groups as $group) {
  198. foreach ($group->getOptions() as $option) {
  199. $key = $option->getKey();
  200. if (isset($options[$key])) {
  201. throw new Exception(
  202. "Mulitple PhabricatorApplicationConfigOptions subclasses contain ".
  203. "an option named '{$key}'!");
  204. }
  205. $options[$key] = $option;
  206. }
  207. }
  208. return $options;
  209. }
  210. /**
  211. * Deformat a HEREDOC for use in remarkup by converting line breaks to
  212. * spaces.
  213. */
  214. final protected function deformat($string) {
  215. return preg_replace('/(?<=\S)\n(?=\S)/', ' ', $string);
  216. }
  217. }