Arr.php 14 KB

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