Form.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Form handling code.
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. declare(strict_types=1);
  9. namespace PhpMyAdmin\Config;
  10. use PhpMyAdmin\Config\ConfigFile;
  11. /**
  12. * Base class for forms, loads default configuration options, checks allowed
  13. * values etc.
  14. *
  15. * @package PhpMyAdmin
  16. */
  17. class Form
  18. {
  19. /**
  20. * Form name
  21. * @var string
  22. */
  23. public $name;
  24. /**
  25. * Arbitrary index, doesn't affect class' behavior
  26. * @var int
  27. */
  28. public $index;
  29. /**
  30. * Form fields (paths), filled by {@link readFormPaths()}, indexed by field name
  31. * @var array
  32. */
  33. public $fields;
  34. /**
  35. * Stores default values for some fields (eg. pmadb tables)
  36. * @var array
  37. */
  38. public $default;
  39. /**
  40. * Caches field types, indexed by field names
  41. * @var array
  42. */
  43. private $_fieldsTypes;
  44. /**
  45. * ConfigFile instance
  46. * @var ConfigFile
  47. */
  48. private $_configFile;
  49. /**
  50. * Constructor, reads default config values
  51. *
  52. * @param string $formName Form name
  53. * @param array $form Form data
  54. * @param ConfigFile $cf Config file instance
  55. * @param int $index arbitrary index, stored in Form::$index
  56. */
  57. public function __construct(
  58. $formName,
  59. array $form,
  60. ConfigFile $cf,
  61. $index = null
  62. ) {
  63. $this->index = $index;
  64. $this->_configFile = $cf;
  65. $this->loadForm($formName, $form);
  66. }
  67. /**
  68. * Returns type of given option
  69. *
  70. * @param string $optionName path or field name
  71. *
  72. * @return string|null one of: boolean, integer, double, string, select, array
  73. */
  74. public function getOptionType($optionName)
  75. {
  76. $key = ltrim(
  77. mb_substr(
  78. $optionName,
  79. (int) mb_strrpos($optionName, '/')
  80. ),
  81. '/'
  82. );
  83. return isset($this->_fieldsTypes[$key])
  84. ? $this->_fieldsTypes[$key]
  85. : null;
  86. }
  87. /**
  88. * Returns allowed values for select fields
  89. *
  90. * @param string $optionPath Option path
  91. *
  92. * @return array
  93. */
  94. public function getOptionValueList($optionPath)
  95. {
  96. $value = $this->_configFile->getDbEntry($optionPath);
  97. if ($value === null) {
  98. trigger_error("$optionPath - select options not defined", E_USER_ERROR);
  99. return [];
  100. }
  101. if (! is_array($value)) {
  102. trigger_error("$optionPath - not a static value list", E_USER_ERROR);
  103. return [];
  104. }
  105. // convert array('#', 'a', 'b') to array('a', 'b')
  106. if (isset($value[0]) && $value[0] === '#') {
  107. // remove first element ('#')
  108. array_shift($value);
  109. // $value has keys and value names, return it
  110. return $value;
  111. }
  112. // convert value list array('a', 'b') to array('a' => 'a', 'b' => 'b')
  113. $hasStringKeys = false;
  114. $keys = [];
  115. for ($i = 0, $nb = count($value); $i < $nb; $i++) {
  116. if (! isset($value[$i])) {
  117. $hasStringKeys = true;
  118. break;
  119. }
  120. $keys[] = is_bool($value[$i]) ? (int) $value[$i] : $value[$i];
  121. }
  122. if (! $hasStringKeys) {
  123. $value = array_combine($keys, $value);
  124. }
  125. // $value has keys and value names, return it
  126. return $value;
  127. }
  128. /**
  129. * array_walk callback function, reads path of form fields from
  130. * array (see docs for \PhpMyAdmin\Config\Forms\BaseForm::getForms)
  131. *
  132. * @param mixed $value Value
  133. * @param mixed $key Key
  134. * @param mixed $prefix Prefix
  135. *
  136. * @return void
  137. */
  138. private function _readFormPathsCallback($value, $key, $prefix)
  139. {
  140. static $groupCounter = 0;
  141. if (is_array($value)) {
  142. $prefix .= $key . '/';
  143. array_walk($value, [$this, '_readFormPathsCallback'], $prefix);
  144. return;
  145. }
  146. if (! is_int($key)) {
  147. $this->default[$prefix . $key] = $value;
  148. $value = $key;
  149. }
  150. // add unique id to group ends
  151. if ($value == ':group:end') {
  152. $value .= ':' . $groupCounter++;
  153. }
  154. $this->fields[] = $prefix . $value;
  155. }
  156. /**
  157. * Reads form paths to {@link $fields}
  158. *
  159. * @param array $form Form
  160. *
  161. * @return void
  162. */
  163. protected function readFormPaths(array $form)
  164. {
  165. // flatten form fields' paths and save them to $fields
  166. $this->fields = [];
  167. array_walk($form, [$this, '_readFormPathsCallback'], '');
  168. // $this->fields is an array of the form: [0..n] => 'field path'
  169. // change numeric indexes to contain field names (last part of the path)
  170. $paths = $this->fields;
  171. $this->fields = [];
  172. foreach ($paths as $path) {
  173. $key = ltrim(
  174. mb_substr($path, (int) mb_strrpos($path, '/')),
  175. '/'
  176. );
  177. $this->fields[$key] = $path;
  178. }
  179. // now $this->fields is an array of the form: 'field name' => 'field path'
  180. }
  181. /**
  182. * Reads fields' types to $this->_fieldsTypes
  183. *
  184. * @return void
  185. */
  186. protected function readTypes()
  187. {
  188. $cf = $this->_configFile;
  189. foreach ($this->fields as $name => $path) {
  190. if (mb_strpos((string) $name, ':group:') === 0) {
  191. $this->_fieldsTypes[$name] = 'group';
  192. continue;
  193. }
  194. $v = $cf->getDbEntry($path);
  195. if ($v !== null) {
  196. $type = is_array($v) ? 'select' : $v;
  197. } else {
  198. $type = gettype($cf->getDefault($path));
  199. }
  200. $this->_fieldsTypes[$name] = $type;
  201. }
  202. }
  203. /**
  204. * Remove slashes from group names
  205. * @see issue #15836
  206. *
  207. * @param array $form The form data
  208. *
  209. * @return array
  210. */
  211. protected function cleanGroupPaths(array $form): array
  212. {
  213. foreach ($form as &$name) {
  214. if (is_string($name)) {
  215. if (mb_strpos($name, ':group:') === 0) {
  216. $name = str_replace('/', '-', $name);
  217. }
  218. }
  219. }
  220. return $form;
  221. }
  222. /**
  223. * Reads form settings and prepares class to work with given subset of
  224. * config file
  225. *
  226. * @param string $formName Form name
  227. * @param array $form Form
  228. *
  229. * @return void
  230. */
  231. public function loadForm($formName, array $form)
  232. {
  233. $this->name = $formName;
  234. $form = $this->cleanGroupPaths($form);
  235. $this->readFormPaths($form);
  236. $this->readTypes();
  237. }
  238. }