Arr.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: yunwuxin <448901948@qq.com>
  10. // +----------------------------------------------------------------------
  11. namespace think\helper;
  12. use ArrayAccess;
  13. use InvalidArgumentException;
  14. use think\Collection;
  15. class Arr
  16. {
  17. /**
  18. * Determine whether the given value is array accessible.
  19. *
  20. * @param mixed $value
  21. * @return bool
  22. */
  23. public static function accessible($value)
  24. {
  25. return is_array($value) || $value instanceof ArrayAccess;
  26. }
  27. /**
  28. * Add an element to an array using "dot" notation if it doesn't exist.
  29. *
  30. * @param array $array
  31. * @param string $key
  32. * @param mixed $value
  33. * @return array
  34. */
  35. public static function add($array, $key, $value)
  36. {
  37. if (is_null(static::get($array, $key))) {
  38. static::set($array, $key, $value);
  39. }
  40. return $array;
  41. }
  42. /**
  43. * Collapse an array of arrays into a single array.
  44. *
  45. * @param array $array
  46. * @return array
  47. */
  48. public static function collapse($array)
  49. {
  50. $results = [];
  51. foreach ($array as $values) {
  52. if ($values instanceof Collection) {
  53. $values = $values->all();
  54. } elseif (!is_array($values)) {
  55. continue;
  56. }
  57. $results = array_merge($results, $values);
  58. }
  59. return $results;
  60. }
  61. /**
  62. * Cross join the given arrays, returning all possible permutations.
  63. *
  64. * @param array ...$arrays
  65. * @return array
  66. */
  67. public static function crossJoin(...$arrays)
  68. {
  69. $results = [[]];
  70. foreach ($arrays as $index => $array) {
  71. $append = [];
  72. foreach ($results as $product) {
  73. foreach ($array as $item) {
  74. $product[$index] = $item;
  75. $append[] = $product;
  76. }
  77. }
  78. $results = $append;
  79. }
  80. return $results;
  81. }
  82. /**
  83. * Divide an array into two arrays. One with keys and the other with values.
  84. *
  85. * @param array $array
  86. * @return array
  87. */
  88. public static function divide($array)
  89. {
  90. return [array_keys($array), array_values($array)];
  91. }
  92. /**
  93. * Flatten a multi-dimensional associative array with dots.
  94. *
  95. * @param array $array
  96. * @param string $prepend
  97. * @return array
  98. */
  99. public static function dot($array, $prepend = '')
  100. {
  101. $results = [];
  102. foreach ($array as $key => $value) {
  103. if (is_array($value) && !empty($value)) {
  104. $results = array_merge($results, static::dot($value, $prepend . $key . '.'));
  105. } else {
  106. $results[$prepend . $key] = $value;
  107. }
  108. }
  109. return $results;
  110. }
  111. /**
  112. * Get all of the given array except for a specified array of keys.
  113. *
  114. * @param array $array
  115. * @param array|string $keys
  116. * @return array
  117. */
  118. public static function except($array, $keys)
  119. {
  120. static::forget($array, $keys);
  121. return $array;
  122. }
  123. /**
  124. * Determine if the given key exists in the provided array.
  125. *
  126. * @param \ArrayAccess|array $array
  127. * @param string|int $key
  128. * @return bool
  129. */
  130. public static function exists($array, $key)
  131. {
  132. if ($array instanceof ArrayAccess) {
  133. return $array->offsetExists($key);
  134. }
  135. return array_key_exists($key, $array);
  136. }
  137. /**
  138. * Return the first element in an array passing a given truth test.
  139. *
  140. * @param array $array
  141. * @param callable|null $callback
  142. * @param mixed $default
  143. * @return mixed
  144. */
  145. public static function first($array, callable $callback = null, $default = null)
  146. {
  147. if (is_null($callback)) {
  148. if (empty($array)) {
  149. return value($default);
  150. }
  151. foreach ($array as $item) {
  152. return $item;
  153. }
  154. }
  155. foreach ($array as $key => $value) {
  156. if (call_user_func($callback, $value, $key)) {
  157. return $value;
  158. }
  159. }
  160. return value($default);
  161. }
  162. /**
  163. * Return the last element in an array passing a given truth test.
  164. *
  165. * @param array $array
  166. * @param callable|null $callback
  167. * @param mixed $default
  168. * @return mixed
  169. */
  170. public static function last($array, callable $callback = null, $default = null)
  171. {
  172. if (is_null($callback)) {
  173. return empty($array) ? value($default) : end($array);
  174. }
  175. return static::first(array_reverse($array, true), $callback, $default);
  176. }
  177. /**
  178. * Flatten a multi-dimensional array into a single level.
  179. *
  180. * @param array $array
  181. * @param int $depth
  182. * @return array
  183. */
  184. public static function flatten($array, $depth = INF)
  185. {
  186. $result = [];
  187. foreach ($array as $item) {
  188. $item = $item instanceof Collection ? $item->all() : $item;
  189. if (!is_array($item)) {
  190. $result[] = $item;
  191. } elseif ($depth === 1) {
  192. $result = array_merge($result, array_values($item));
  193. } else {
  194. $result = array_merge($result, static::flatten($item, $depth - 1));
  195. }
  196. }
  197. return $result;
  198. }
  199. /**
  200. * Remove one or many array items from a given array using "dot" notation.
  201. *
  202. * @param array $array
  203. * @param array|string $keys
  204. * @return void
  205. */
  206. public static function forget(&$array, $keys)
  207. {
  208. $original = &$array;
  209. $keys = (array) $keys;
  210. if (count($keys) === 0) {
  211. return;
  212. }
  213. foreach ($keys as $key) {
  214. // if the exact key exists in the top-level, remove it
  215. if (static::exists($array, $key)) {
  216. unset($array[$key]);
  217. continue;
  218. }
  219. $parts = explode('.', $key);
  220. // clean up before each pass
  221. $array = &$original;
  222. while (count($parts) > 1) {
  223. $part = array_shift($parts);
  224. if (isset($array[$part]) && is_array($array[$part])) {
  225. $array = &$array[$part];
  226. } else {
  227. continue 2;
  228. }
  229. }
  230. unset($array[array_shift($parts)]);
  231. }
  232. }
  233. /**
  234. * Get an item from an array using "dot" notation.
  235. *
  236. * @param \ArrayAccess|array $array
  237. * @param string $key
  238. * @param mixed $default
  239. * @return mixed
  240. */
  241. public static function get($array, $key, $default = null)
  242. {
  243. if (!static::accessible($array)) {
  244. return value($default);
  245. }
  246. if (is_null($key)) {
  247. return $array;
  248. }
  249. if (static::exists($array, $key)) {
  250. return $array[$key];
  251. }
  252. if (strpos($key, '.') === false) {
  253. return $array[$key] ?? value($default);
  254. }
  255. foreach (explode('.', $key) as $segment) {
  256. if (static::accessible($array) && static::exists($array, $segment)) {
  257. $array = $array[$segment];
  258. } else {
  259. return value($default);
  260. }
  261. }
  262. return $array;
  263. }
  264. /**
  265. * Check if an item or items exist in an array using "dot" notation.
  266. *
  267. * @param \ArrayAccess|array $array
  268. * @param string|array $keys
  269. * @return bool
  270. */
  271. public static function has($array, $keys)
  272. {
  273. $keys = (array) $keys;
  274. if (!$array || $keys === []) {
  275. return false;
  276. }
  277. foreach ($keys as $key) {
  278. $subKeyArray = $array;
  279. if (static::exists($array, $key)) {
  280. continue;
  281. }
  282. foreach (explode('.', $key) as $segment) {
  283. if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
  284. $subKeyArray = $subKeyArray[$segment];
  285. } else {
  286. return false;
  287. }
  288. }
  289. }
  290. return true;
  291. }
  292. /**
  293. * Determines if an array is associative.
  294. *
  295. * An array is "associative" if it doesn't have sequential numerical keys beginning with zero.
  296. *
  297. * @param array $array
  298. * @return bool
  299. */
  300. public static function isAssoc(array $array)
  301. {
  302. $keys = array_keys($array);
  303. return array_keys($keys) !== $keys;
  304. }
  305. /**
  306. * Get a subset of the items from the given array.
  307. *
  308. * @param array $array
  309. * @param array|string $keys
  310. * @return array
  311. */
  312. public static function only($array, $keys)
  313. {
  314. return array_intersect_key($array, array_flip((array) $keys));
  315. }
  316. /**
  317. * Pluck an array of values from an array.
  318. *
  319. * @param array $array
  320. * @param string|array $value
  321. * @param string|array|null $key
  322. * @return array
  323. */
  324. public static function pluck($array, $value, $key = null)
  325. {
  326. $results = [];
  327. [$value, $key] = static::explodePluckParameters($value, $key);
  328. foreach ($array as $item) {
  329. $itemValue = data_get($item, $value);
  330. // If the key is "null", we will just append the value to the array and keep
  331. // looping. Otherwise we will key the array using the value of the key we
  332. // received from the developer. Then we'll return the final array form.
  333. if (is_null($key)) {
  334. $results[] = $itemValue;
  335. } else {
  336. $itemKey = data_get($item, $key);
  337. if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
  338. $itemKey = (string) $itemKey;
  339. }
  340. $results[$itemKey] = $itemValue;
  341. }
  342. }
  343. return $results;
  344. }
  345. /**
  346. * Explode the "value" and "key" arguments passed to "pluck".
  347. *
  348. * @param string|array $value
  349. * @param string|array|null $key
  350. * @return array
  351. */
  352. protected static function explodePluckParameters($value, $key)
  353. {
  354. $value = is_string($value) ? explode('.', $value) : $value;
  355. $key = is_null($key) || is_array($key) ? $key : explode('.', $key);
  356. return [$value, $key];
  357. }
  358. /**
  359. * Push an item onto the beginning of an array.
  360. *
  361. * @param array $array
  362. * @param mixed $value
  363. * @param mixed $key
  364. * @return array
  365. */
  366. public static function prepend($array, $value, $key = null)
  367. {
  368. if (is_null($key)) {
  369. array_unshift($array, $value);
  370. } else {
  371. $array = [$key => $value] + $array;
  372. }
  373. return $array;
  374. }
  375. /**
  376. * Get a value from the array, and remove it.
  377. *
  378. * @param array $array
  379. * @param string $key
  380. * @param mixed $default
  381. * @return mixed
  382. */
  383. public static function pull(&$array, $key, $default = null)
  384. {
  385. $value = static::get($array, $key, $default);
  386. static::forget($array, $key);
  387. return $value;
  388. }
  389. /**
  390. * Get one or a specified number of random values from an array.
  391. *
  392. * @param array $array
  393. * @param int|null $number
  394. * @return mixed
  395. *
  396. * @throws \InvalidArgumentException
  397. */
  398. public static function random($array, $number = null)
  399. {
  400. $requested = is_null($number) ? 1 : $number;
  401. $count = count($array);
  402. if ($requested > $count) {
  403. throw new InvalidArgumentException(
  404. "You requested {$requested} items, but there are only {$count} items available."
  405. );
  406. }
  407. if (is_null($number)) {
  408. return $array[array_rand($array)];
  409. }
  410. if ((int) $number === 0) {
  411. return [];
  412. }
  413. $keys = array_rand($array, $number);
  414. $results = [];
  415. foreach ((array) $keys as $key) {
  416. $results[] = $array[$key];
  417. }
  418. return $results;
  419. }
  420. /**
  421. * Set an array item to a given value using "dot" notation.
  422. *
  423. * If no key is given to the method, the entire array will be replaced.
  424. *
  425. * @param array $array
  426. * @param string $key
  427. * @param mixed $value
  428. * @return array
  429. */
  430. public static function set(&$array, $key, $value)
  431. {
  432. if (is_null($key)) {
  433. return $array = $value;
  434. }
  435. $keys = explode('.', $key);
  436. while (count($keys) > 1) {
  437. $key = array_shift($keys);
  438. // If the key doesn't exist at this depth, we will just create an empty array
  439. // to hold the next value, allowing us to create the arrays to hold final
  440. // values at the correct depth. Then we'll keep digging into the array.
  441. if (!isset($array[$key]) || !is_array($array[$key])) {
  442. $array[$key] = [];
  443. }
  444. $array = &$array[$key];
  445. }
  446. $array[array_shift($keys)] = $value;
  447. return $array;
  448. }
  449. /**
  450. * Shuffle the given array and return the result.
  451. *
  452. * @param array $array
  453. * @param int|null $seed
  454. * @return array
  455. */
  456. public static function shuffle($array, $seed = null)
  457. {
  458. if (is_null($seed)) {
  459. shuffle($array);
  460. } else {
  461. srand($seed);
  462. usort($array, function () {
  463. return rand(-1, 1);
  464. });
  465. }
  466. return $array;
  467. }
  468. /**
  469. * Sort the array using the given callback or "dot" notation.
  470. *
  471. * @param array $array
  472. * @param callable|string|null $callback
  473. * @return array
  474. */
  475. public static function sort($array, $callback = null)
  476. {
  477. return Collection::make($array)->sort($callback)->all();
  478. }
  479. /**
  480. * Recursively sort an array by keys and values.
  481. *
  482. * @param array $array
  483. * @return array
  484. */
  485. public static function sortRecursive($array)
  486. {
  487. foreach ($array as &$value) {
  488. if (is_array($value)) {
  489. $value = static::sortRecursive($value);
  490. }
  491. }
  492. if (static::isAssoc($array)) {
  493. ksort($array);
  494. } else {
  495. sort($array);
  496. }
  497. return $array;
  498. }
  499. /**
  500. * Convert the array into a query string.
  501. *
  502. * @param array $array
  503. * @return string
  504. */
  505. public static function query($array)
  506. {
  507. return http_build_query($array, null, '&', PHP_QUERY_RFC3986);
  508. }
  509. /**
  510. * Filter the array using the given callback.
  511. *
  512. * @param array $array
  513. * @param callable $callback
  514. * @return array
  515. */
  516. public static function where($array, callable $callback)
  517. {
  518. return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
  519. }
  520. /**
  521. * If the given value is not an array and not null, wrap it in one.
  522. *
  523. * @param mixed $value
  524. * @return array
  525. */
  526. public static function wrap($value)
  527. {
  528. if (is_null($value)) {
  529. return [];
  530. }
  531. return is_array($value) ? $value : [$value];
  532. }
  533. }