PageRenderTime 22ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/sites/all/modules/service_container/lib/Drupal/Component/Utility/NestedArray.php

https://gitlab.com/leoplanxxi/dr7-web-buap-2016
PHP | 352 lines | 65 code | 10 blank | 277 comment | 15 complexity | badb059fe543f16778cd204f5c48d21c MD5 | raw file
  1. <?php
  2. /**
  3. * @file
  4. * Contains Drupal\Component\Utility\NestedArray.
  5. */
  6. namespace Drupal\Component\Utility;
  7. /**
  8. * Provides helpers to perform operations on nested arrays and array keys of variable depth.
  9. *
  10. * @ingroup utility
  11. */
  12. class NestedArray {
  13. /**
  14. * Retrieves a value from a nested array with variable depth.
  15. *
  16. * This helper function should be used when the depth of the array element
  17. * being retrieved may vary (that is, the number of parent keys is variable).
  18. * It is primarily used for form structures and renderable arrays.
  19. *
  20. * Without this helper function the only way to get a nested array value with
  21. * variable depth in one line would be using eval(), which should be avoided:
  22. * @code
  23. * // Do not do this! Avoid eval().
  24. * // May also throw a PHP notice, if the variable array keys do not exist.
  25. * eval('$value = $array[\'' . implode("']['", $parents) . "'];");
  26. * @endcode
  27. *
  28. * Instead, use this helper function:
  29. * @code
  30. * $value = NestedArray::getValue($form, $parents);
  31. * @endcode
  32. *
  33. * A return value of NULL is ambiguous, and can mean either that the requested
  34. * key does not exist, or that the actual value is NULL. If it is required to
  35. * know whether the nested array key actually exists, pass a third argument
  36. * that is altered by reference:
  37. * @code
  38. * $key_exists = NULL;
  39. * $value = NestedArray::getValue($form, $parents, $key_exists);
  40. * if ($key_exists) {
  41. * // Do something with $value.
  42. * }
  43. * @endcode
  44. *
  45. * However if the number of array parent keys is static, the value should
  46. * always be retrieved directly rather than calling this function.
  47. * For instance:
  48. * @code
  49. * $value = $form['signature_settings']['signature'];
  50. * @endcode
  51. *
  52. * @param array $array
  53. * The array from which to get the value.
  54. * @param array $parents
  55. * An array of parent keys of the value, starting with the outermost key.
  56. * @param bool $key_exists
  57. * (optional) If given, an already defined variable that is altered by
  58. * reference.
  59. *
  60. * @return mixed
  61. * The requested nested value. Possibly NULL if the value is NULL or not all
  62. * nested parent keys exist. $key_exists is altered by reference and is a
  63. * Boolean that indicates whether all nested parent keys exist (TRUE) or not
  64. * (FALSE). This allows to distinguish between the two possibilities when
  65. * NULL is returned.
  66. *
  67. * @see NestedArray::setValue()
  68. * @see NestedArray::unsetValue()
  69. */
  70. public static function &getValue(array &$array, array $parents, &$key_exists = NULL) {
  71. $ref = &$array;
  72. foreach ($parents as $parent) {
  73. if (is_array($ref) && array_key_exists($parent, $ref)) {
  74. $ref = &$ref[$parent];
  75. }
  76. else {
  77. $key_exists = FALSE;
  78. $null = NULL;
  79. return $null;
  80. }
  81. }
  82. $key_exists = TRUE;
  83. return $ref;
  84. }
  85. /**
  86. * Sets a value in a nested array with variable depth.
  87. *
  88. * This helper function should be used when the depth of the array element you
  89. * are changing may vary (that is, the number of parent keys is variable). It
  90. * is primarily used for form structures and renderable arrays.
  91. *
  92. * Example:
  93. * @code
  94. * // Assume you have a 'signature' element somewhere in a form. It might be:
  95. * $form['signature_settings']['signature'] = array(
  96. * '#type' => 'text_format',
  97. * '#title' => t('Signature'),
  98. * );
  99. * // Or, it might be further nested:
  100. * $form['signature_settings']['user']['signature'] = array(
  101. * '#type' => 'text_format',
  102. * '#title' => t('Signature'),
  103. * );
  104. * @endcode
  105. *
  106. * To deal with the situation, the code needs to figure out the route to the
  107. * element, given an array of parents that is either
  108. * @code array('signature_settings', 'signature') @endcode
  109. * in the first case or
  110. * @code array('signature_settings', 'user', 'signature') @endcode
  111. * in the second case.
  112. *
  113. * Without this helper function the only way to set the signature element in
  114. * one line would be using eval(), which should be avoided:
  115. * @code
  116. * // Do not do this! Avoid eval().
  117. * eval('$form[\'' . implode("']['", $parents) . '\'] = $element;');
  118. * @endcode
  119. *
  120. * Instead, use this helper function:
  121. * @code
  122. * NestedArray::setValue($form, $parents, $element);
  123. * @endcode
  124. *
  125. * However if the number of array parent keys is static, the value should
  126. * always be set directly rather than calling this function. For instance,
  127. * for the first example we could just do:
  128. * @code
  129. * $form['signature_settings']['signature'] = $element;
  130. * @endcode
  131. *
  132. * @param array $array
  133. * A reference to the array to modify.
  134. * @param array $parents
  135. * An array of parent keys, starting with the outermost key.
  136. * @param mixed $value
  137. * The value to set.
  138. * @param bool $force
  139. * (optional) If TRUE, the value is forced into the structure even if it
  140. * requires the deletion of an already existing non-array parent value. If
  141. * FALSE, PHP throws an error if trying to add into a value that is not an
  142. * array. Defaults to FALSE.
  143. *
  144. * @see NestedArray::unsetValue()
  145. * @see NestedArray::getValue()
  146. */
  147. public static function setValue(array &$array, array $parents, $value, $force = FALSE) {
  148. $ref = &$array;
  149. foreach ($parents as $parent) {
  150. // PHP auto-creates container arrays and NULL entries without error if $ref
  151. // is NULL, but throws an error if $ref is set, but not an array.
  152. if ($force && isset($ref) && !is_array($ref)) {
  153. $ref = array();
  154. }
  155. $ref = &$ref[$parent];
  156. }
  157. $ref = $value;
  158. }
  159. /**
  160. * Unsets a value in a nested array with variable depth.
  161. *
  162. * This helper function should be used when the depth of the array element you
  163. * are changing may vary (that is, the number of parent keys is variable). It
  164. * is primarily used for form structures and renderable arrays.
  165. *
  166. * Example:
  167. * @code
  168. * // Assume you have a 'signature' element somewhere in a form. It might be:
  169. * $form['signature_settings']['signature'] = array(
  170. * '#type' => 'text_format',
  171. * '#title' => t('Signature'),
  172. * );
  173. * // Or, it might be further nested:
  174. * $form['signature_settings']['user']['signature'] = array(
  175. * '#type' => 'text_format',
  176. * '#title' => t('Signature'),
  177. * );
  178. * @endcode
  179. *
  180. * To deal with the situation, the code needs to figure out the route to the
  181. * element, given an array of parents that is either
  182. * @code array('signature_settings', 'signature') @endcode
  183. * in the first case or
  184. * @code array('signature_settings', 'user', 'signature') @endcode
  185. * in the second case.
  186. *
  187. * Without this helper function the only way to unset the signature element in
  188. * one line would be using eval(), which should be avoided:
  189. * @code
  190. * // Do not do this! Avoid eval().
  191. * eval('unset($form[\'' . implode("']['", $parents) . '\']);');
  192. * @endcode
  193. *
  194. * Instead, use this helper function:
  195. * @code
  196. * NestedArray::unset_nested_value($form, $parents, $element);
  197. * @endcode
  198. *
  199. * However if the number of array parent keys is static, the value should
  200. * always be set directly rather than calling this function. For instance, for
  201. * the first example we could just do:
  202. * @code
  203. * unset($form['signature_settings']['signature']);
  204. * @endcode
  205. *
  206. * @param array $array
  207. * A reference to the array to modify.
  208. * @param array $parents
  209. * An array of parent keys, starting with the outermost key and including
  210. * the key to be unset.
  211. * @param bool $key_existed
  212. * (optional) If given, an already defined variable that is altered by
  213. * reference.
  214. *
  215. * @see NestedArray::setValue()
  216. * @see NestedArray::getValue()
  217. */
  218. public static function unsetValue(array &$array, array $parents, &$key_existed = NULL) {
  219. $unset_key = array_pop($parents);
  220. $ref = &self::getValue($array, $parents, $key_existed);
  221. if ($key_existed && is_array($ref) && array_key_exists($unset_key, $ref)) {
  222. $key_existed = TRUE;
  223. unset($ref[$unset_key]);
  224. }
  225. else {
  226. $key_existed = FALSE;
  227. }
  228. }
  229. /**
  230. * Determines whether a nested array contains the requested keys.
  231. *
  232. * This helper function should be used when the depth of the array element to
  233. * be checked may vary (that is, the number of parent keys is variable). See
  234. * NestedArray::setValue() for details. It is primarily used for form
  235. * structures and renderable arrays.
  236. *
  237. * If it is required to also get the value of the checked nested key, use
  238. * NestedArray::getValue() instead.
  239. *
  240. * If the number of array parent keys is static, this helper function is
  241. * unnecessary and the following code can be used instead:
  242. * @code
  243. * $value_exists = isset($form['signature_settings']['signature']);
  244. * $key_exists = array_key_exists('signature', $form['signature_settings']);
  245. * @endcode
  246. *
  247. * @param array $array
  248. * The array with the value to check for.
  249. * @param array $parents
  250. * An array of parent keys of the value, starting with the outermost key.
  251. *
  252. * @return bool
  253. * TRUE if all the parent keys exist, FALSE otherwise.
  254. *
  255. * @see NestedArray::getValue()
  256. */
  257. public static function keyExists(array $array, array $parents) {
  258. // Although this function is similar to PHP's array_key_exists(), its
  259. // arguments should be consistent with getValue().
  260. $key_exists = NULL;
  261. self::getValue($array, $parents, $key_exists);
  262. return $key_exists;
  263. }
  264. /**
  265. * Merges multiple arrays, recursively, and returns the merged array.
  266. *
  267. * This function is similar to PHP's array_merge_recursive() function, but it
  268. * handles non-array values differently. When merging values that are not both
  269. * arrays, the latter value replaces the former rather than merging with it.
  270. *
  271. * Example:
  272. * @code
  273. * $link_options_1 = array('fragment' => 'x', 'attributes' => array('title' => t('X'), 'class' => array('a', 'b')));
  274. * $link_options_2 = array('fragment' => 'y', 'attributes' => array('title' => t('Y'), 'class' => array('c', 'd')));
  275. *
  276. * // This results in array('fragment' => array('x', 'y'), 'attributes' => array('title' => array(t('X'), t('Y')), 'class' => array('a', 'b', 'c', 'd'))).
  277. * $incorrect = array_merge_recursive($link_options_1, $link_options_2);
  278. *
  279. * // This results in array('fragment' => 'y', 'attributes' => array('title' => t('Y'), 'class' => array('a', 'b', 'c', 'd'))).
  280. * $correct = NestedArray::mergeDeep($link_options_1, $link_options_2);
  281. * @endcode
  282. *
  283. * @param array ...
  284. * Arrays to merge.
  285. *
  286. * @return array
  287. * The merged array.
  288. *
  289. * @see NestedArray::mergeDeepArray()
  290. */
  291. public static function mergeDeep() {
  292. return self::mergeDeepArray(func_get_args());
  293. }
  294. /**
  295. * Merges multiple arrays, recursively, and returns the merged array.
  296. *
  297. * This function is equivalent to NestedArray::mergeDeep(), except the
  298. * input arrays are passed as a single array parameter rather than a variable
  299. * parameter list.
  300. *
  301. * The following are equivalent:
  302. * - NestedArray::mergeDeep($a, $b);
  303. * - NestedArray::mergeDeepArray(array($a, $b));
  304. *
  305. * The following are also equivalent:
  306. * - call_user_func_array('NestedArray::mergeDeep', $arrays_to_merge);
  307. * - NestedArray::mergeDeepArray($arrays_to_merge);
  308. *
  309. * @param array $arrays
  310. * An arrays of arrays to merge.
  311. * @param bool $preserve_integer_keys
  312. * (optional) If given, integer keys will be preserved and merged instead of
  313. * appended. Defaults to FALSE.
  314. *
  315. * @return array
  316. * The merged array.
  317. *
  318. * @see NestedArray::mergeDeep()
  319. */
  320. public static function mergeDeepArray(array $arrays, $preserve_integer_keys = FALSE) {
  321. $result = array();
  322. foreach ($arrays as $array) {
  323. foreach ($array as $key => $value) {
  324. // Renumber integer keys as array_merge_recursive() does unless
  325. // $preserve_integer_keys is set to TRUE. Note that PHP automatically
  326. // converts array keys that are integer strings (e.g., '1') to integers.
  327. if (is_integer($key) && !$preserve_integer_keys) {
  328. $result[] = $value;
  329. }
  330. // Recurse when both values are arrays.
  331. elseif (isset($result[$key]) && is_array($result[$key]) && is_array($value)) {
  332. $result[$key] = self::mergeDeepArray(array($result[$key], $value), $preserve_integer_keys);
  333. }
  334. // Otherwise, use the latter value, overriding any previous value.
  335. else {
  336. $result[$key] = $value;
  337. }
  338. }
  339. }
  340. return $result;
  341. }
  342. }