Helper.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909
  1. <?php
  2. namespace Dcat\Admin\Support;
  3. use Dcat\Admin\Admin;
  4. use Dcat\Admin\Grid;
  5. use Dcat\Laravel\Database\WhereHasInServiceProvider;
  6. use Illuminate\Contracts\Support\Arrayable;
  7. use Illuminate\Contracts\Support\Htmlable;
  8. use Illuminate\Contracts\Support\Jsonable;
  9. use Illuminate\Contracts\Support\Renderable;
  10. use Illuminate\Http\Request;
  11. use Illuminate\Support\Arr;
  12. use Illuminate\Support\Collection;
  13. use Illuminate\Support\Facades\File;
  14. use Illuminate\Support\Str;
  15. use Symfony\Component\Process\Process;
  16. class Helper
  17. {
  18. /**
  19. * @var array
  20. */
  21. public static $fileTypes = [
  22. 'image' => 'png|jpg|jpeg|tmp|gif',
  23. 'word' => 'doc|docx',
  24. 'excel' => 'xls|xlsx|csv',
  25. 'powerpoint' => 'ppt|pptx',
  26. 'pdf' => 'pdf',
  27. 'code' => 'php|js|java|python|ruby|go|c|cpp|sql|m|h|json|html|aspx',
  28. 'archive' => 'zip|tar\.gz|rar|rpm',
  29. 'txt' => 'txt|pac|log|md',
  30. 'audio' => 'mp3|wav|flac|3pg|aa|aac|ape|au|m4a|mpc|ogg',
  31. 'video' => 'mkv|rmvb|flv|mp4|avi|wmv|rm|asf|mpeg',
  32. ];
  33. /**
  34. * 把给定的值转化为数组.
  35. *
  36. * @param $value
  37. * @param bool $filter
  38. *
  39. * @return array
  40. */
  41. public static function array($value, bool $filter = true): array
  42. {
  43. if (! $value) {
  44. return [];
  45. }
  46. if ($value instanceof \Closure) {
  47. $value = $value();
  48. }
  49. if (is_array($value)) {
  50. } elseif ($value instanceof Jsonable) {
  51. $value = json_decode($value->toJson(), true);
  52. } elseif ($value instanceof Arrayable) {
  53. $value = $value->toArray();
  54. } elseif (is_string($value)) {
  55. $array = null;
  56. try {
  57. $array = json_decode($value, true);
  58. } catch (\Throwable $e) {
  59. }
  60. $value = is_array($array) ? $array : explode(',', $value);
  61. } else {
  62. $value = (array) $value;
  63. }
  64. return $filter ? array_filter($value, function ($v) {
  65. return $v !== '' && $v !== null;
  66. }) : $value;
  67. }
  68. /**
  69. * 把给定的值转化为字符串.
  70. *
  71. * @param string|Grid|\Closure|Renderable|Htmlable $value
  72. * @param array $params
  73. * @param object $newThis
  74. *
  75. * @return string
  76. */
  77. public static function render($value, $params = [], $newThis = null): string
  78. {
  79. if (is_string($value)) {
  80. return $value;
  81. }
  82. if ($value instanceof \Closure) {
  83. $newThis && ($value = $value->bindTo($newThis));
  84. $value = $value(...(array) $params);
  85. }
  86. if ($value instanceof Grid) {
  87. return (string) $value->render();
  88. }
  89. if ($value instanceof Renderable) {
  90. return (string) $value->render();
  91. }
  92. if ($value instanceof Htmlable) {
  93. return (string) $value->toHtml();
  94. }
  95. return (string) $value;
  96. }
  97. /**
  98. * @param array $attributes
  99. *
  100. * @return string
  101. */
  102. public static function buildHtmlAttributes($attributes)
  103. {
  104. $html = '';
  105. foreach ((array) $attributes as $key => &$value) {
  106. if (is_array($value)) {
  107. $value = implode(' ', $value);
  108. }
  109. if (is_numeric($key)) {
  110. $key = $value;
  111. }
  112. $element = '';
  113. if ($value !== null) {
  114. $element = $key.'="'.htmlentities($value, ENT_QUOTES, 'UTF-8').'"';
  115. }
  116. $html .= $element;
  117. }
  118. return $html;
  119. }
  120. /**
  121. * @param string $url
  122. * @param array $query
  123. *
  124. * @return string
  125. */
  126. public static function urlWithQuery(?string $url, array $query = [])
  127. {
  128. if (! $url || ! $query) {
  129. return $url;
  130. }
  131. $array = explode('?', $url);
  132. $url = $array[0];
  133. parse_str($array[1] ?? '', $originalQuery);
  134. return $url.'?'.http_build_query(array_merge($originalQuery, $query));
  135. }
  136. /**
  137. * @param string $url
  138. * @param string|array|Arrayable $keys
  139. *
  140. * @return string
  141. */
  142. public static function urlWithoutQuery($url, $keys)
  143. {
  144. if (! Str::contains($url, '?') || ! $keys) {
  145. return $url;
  146. }
  147. if ($keys instanceof Arrayable) {
  148. $keys = $keys->toArray();
  149. }
  150. $keys = (array) $keys;
  151. $urlInfo = parse_url($url);
  152. parse_str($urlInfo['query'], $query);
  153. Arr::forget($query, $keys);
  154. $baseUrl = explode('?', $url)[0];
  155. return $query
  156. ? $baseUrl.'?'.http_build_query($query)
  157. : $baseUrl;
  158. }
  159. /**
  160. * @param Arrayable|array|string $keys
  161. *
  162. * @return string
  163. */
  164. public static function fullUrlWithoutQuery($keys)
  165. {
  166. return static::urlWithoutQuery(request()->fullUrl(), $keys);
  167. }
  168. /**
  169. * @param string $url
  170. * @param string|array $keys
  171. *
  172. * @return bool
  173. */
  174. public static function urlHasQuery(string $url, $keys)
  175. {
  176. $value = explode('?', $url);
  177. if (empty($value[1])) {
  178. return false;
  179. }
  180. parse_str($value[1], $query);
  181. foreach ((array) $keys as $key) {
  182. if (Arr::has($query, $key)) {
  183. return true;
  184. }
  185. }
  186. return false;
  187. }
  188. /**
  189. * 匹配请求路径.
  190. *
  191. * @example
  192. * Helper::matchRequestPath(admin_base_path('auth/user'))
  193. * Helper::matchRequestPath(admin_base_path('auth/user*'))
  194. * Helper::matchRequestPath(admin_base_path('auth/user/* /edit'))
  195. * Helper::matchRequestPath('GET,POST:auth/user')
  196. *
  197. * @param string $path
  198. * @param null|string $current
  199. *
  200. * @return bool
  201. */
  202. public static function matchRequestPath($path, ?string $current = null)
  203. {
  204. $request = request();
  205. $current = $current ?: $request->decodedPath();
  206. if (Str::contains($path, ':')) {
  207. [$methods, $path] = explode(':', $path);
  208. $methods = array_map('strtoupper', explode(',', $methods));
  209. if (! empty($methods) && ! in_array($request->method(), $methods)) {
  210. return false;
  211. }
  212. }
  213. // 判断路由名称
  214. if ($request->routeIs($path)) {
  215. return true;
  216. }
  217. if (! Str::contains($path, '*')) {
  218. return $path === $current;
  219. }
  220. $path = str_replace(['*', '/'], ['([0-9a-z-_,])*', "\/"], $path);
  221. return preg_match("/$path/i", $current);
  222. }
  223. /**
  224. * 生成层级数据.
  225. *
  226. * @param array $nodes
  227. * @param int $parentId
  228. * @param string|null $primaryKeyName
  229. * @param string|null $parentKeyName
  230. * @param string|null $childrenKeyName
  231. *
  232. * @return array
  233. */
  234. public static function buildNestedArray(
  235. $nodes = [],
  236. $parentId = 0,
  237. ?string $primaryKeyName = null,
  238. ?string $parentKeyName = null,
  239. ?string $childrenKeyName = null
  240. ) {
  241. $branch = [];
  242. $primaryKeyName = $primaryKeyName ?: 'id';
  243. $parentKeyName = $parentKeyName ?: 'parent_id';
  244. $childrenKeyName = $childrenKeyName ?: 'children';
  245. $parentId = is_numeric($parentId) ? (int) $parentId : $parentId;
  246. foreach ($nodes as $node) {
  247. $pk = $node[$parentKeyName];
  248. $pk = is_numeric($pk) ? (int) $pk : $pk;
  249. if ($pk === $parentId) {
  250. $children = static::buildNestedArray(
  251. $nodes,
  252. $node[$primaryKeyName],
  253. $primaryKeyName,
  254. $parentKeyName,
  255. $childrenKeyName
  256. );
  257. if ($children) {
  258. $node[$childrenKeyName] = $children;
  259. }
  260. $branch[] = $node;
  261. }
  262. }
  263. return $branch;
  264. }
  265. /**
  266. * @param string $name
  267. * @param string $symbol
  268. *
  269. * @return mixed
  270. */
  271. public static function slug(string $name, string $symbol = '-')
  272. {
  273. $text = preg_replace_callback('/([A-Z])/', function ($text) use ($symbol) {
  274. return $symbol.strtolower($text[1]);
  275. }, $name);
  276. return str_replace('_', $symbol, ltrim($text, $symbol));
  277. }
  278. /**
  279. * @param array $array
  280. * @param int $level
  281. *
  282. * @return string
  283. */
  284. public static function exportArray(array &$array, $level = 1)
  285. {
  286. $start = '[';
  287. $end = ']';
  288. $txt = "$start\n";
  289. foreach ($array as $k => &$v) {
  290. if (is_array($v)) {
  291. $pre = is_string($k) ? "'$k' => " : "$k => ";
  292. $txt .= str_repeat(' ', $level * 4).$pre.static::exportArray($v, $level + 1).",\n";
  293. continue;
  294. }
  295. $t = $v;
  296. if ($v === true) {
  297. $t = 'true';
  298. } elseif ($v === false) {
  299. $t = 'false';
  300. } elseif ($v === null) {
  301. $t = 'null';
  302. } elseif (is_string($v)) {
  303. $v = str_replace("'", "\\'", $v);
  304. $t = "'$v'";
  305. }
  306. $pre = is_string($k) ? "'$k' => " : "$k => ";
  307. $txt .= str_repeat(' ', $level * 4)."{$pre}{$t},\n";
  308. }
  309. return $txt.str_repeat(' ', ($level - 1) * 4).$end;
  310. }
  311. /**
  312. * @param array $array
  313. *
  314. * @return string
  315. */
  316. public static function exportArrayPhp(array $array)
  317. {
  318. return "<?php \nreturn ".static::exportArray($array).";\n";
  319. }
  320. /**
  321. * 删除数组中的元素.
  322. *
  323. * @param array $array
  324. * @param mixed $value
  325. */
  326. public static function deleteByValue(&$array, $value)
  327. {
  328. $value = (array) $value;
  329. foreach ($array as $index => $item) {
  330. if (in_array($item, $value)) {
  331. unset($array[$index]);
  332. }
  333. }
  334. }
  335. /**
  336. * 颜色转亮.
  337. *
  338. * @param string $color
  339. * @param int $amt
  340. *
  341. * @return string
  342. */
  343. public static function colorLighten(string $color, int $amt)
  344. {
  345. if (! $amt) {
  346. return $color;
  347. }
  348. $hasPrefix = false;
  349. if (mb_strpos($color, '#') === 0) {
  350. $color = mb_substr($color, 1);
  351. $hasPrefix = true;
  352. }
  353. [$red, $blue, $green] = static::colorToRBG($color, $amt);
  354. return ($hasPrefix ? '#' : '').dechex($green + ($blue << 8) + ($red << 16));
  355. }
  356. /**
  357. * 颜色转暗.
  358. *
  359. * @param string $color
  360. * @param int $amt
  361. *
  362. * @return string
  363. */
  364. public static function colorDarken(string $color, int $amt)
  365. {
  366. return static::colorLighten($color, -$amt);
  367. }
  368. /**
  369. * 颜色透明度.
  370. *
  371. * @param string $color
  372. * @param float|string $alpha
  373. *
  374. * @return string
  375. */
  376. public static function colorAlpha(string $color, $alpha)
  377. {
  378. if ($alpha >= 1) {
  379. return $color;
  380. }
  381. if (mb_strpos($color, '#') === 0) {
  382. $color = mb_substr($color, 1);
  383. }
  384. [$red, $blue, $green] = static::colorToRBG($color);
  385. return "rgba($red, $blue, $green, $alpha)";
  386. }
  387. /**
  388. * @param string $color
  389. * @param int $amt
  390. *
  391. * @return array
  392. */
  393. public static function colorToRBG(string $color, int $amt = 0)
  394. {
  395. $format = function ($value) {
  396. if ($value > 255) {
  397. return 255;
  398. }
  399. if ($value < 0) {
  400. return 0;
  401. }
  402. return $value;
  403. };
  404. $num = hexdec($color);
  405. $red = $format(($num >> 16) + $amt);
  406. $blue = $format((($num >> 8) & 0x00FF) + $amt);
  407. $green = $format(($num & 0x0000FF) + $amt);
  408. return [$red, $blue, $green];
  409. }
  410. /**
  411. * 验证扩展包名称.
  412. *
  413. * @param string $name
  414. *
  415. * @return int
  416. */
  417. public static function validateExtensionName($name)
  418. {
  419. return preg_match('/^[\w\-_]+\/[\w\-_]+$/', $name);
  420. }
  421. /**
  422. * Get file icon.
  423. *
  424. * @param string $file
  425. *
  426. * @return string
  427. */
  428. public static function getFileIcon($file = '')
  429. {
  430. $extension = File::extension($file);
  431. foreach (static::$fileTypes as $type => $regex) {
  432. if (preg_match("/^($regex)$/i", $extension) !== 0) {
  433. return "fa fa-file-{$type}-o";
  434. }
  435. }
  436. return 'fa fa-file-o';
  437. }
  438. /**
  439. * 判断是否是ajax请求.
  440. *
  441. * @param Request $request
  442. *
  443. * @return bool
  444. */
  445. public static function isAjaxRequest(?Request $request = null)
  446. {
  447. /* @var Request $request */
  448. $request = $request ?: request();
  449. return $request->ajax() && ! $request->pjax();
  450. }
  451. /**
  452. * 判断是否是IE浏览器.
  453. *
  454. * @return false|int
  455. */
  456. public static function isIEBrowser()
  457. {
  458. return (bool) preg_match('/Mozilla\/5\.0 \(Windows NT 10\.0; WOW64; Trident\/7\.0; rv:[0-9\.]*\) like Gecko/i', $_SERVER['HTTP_USER_AGENT'] ?? '');
  459. }
  460. /**
  461. * 判断是否QQ浏览器.
  462. *
  463. * @return bool
  464. */
  465. public static function isQQBrowser()
  466. {
  467. return mb_strpos(mb_strtolower($_SERVER['HTTP_USER_AGENT'] ?? ''), 'qqbrowser') !== false;
  468. }
  469. /**
  470. * @param string $url
  471. *
  472. * @return void
  473. */
  474. public static function setPreviousUrl($url)
  475. {
  476. session()->flash('admin.prev.url', static::urlWithoutQuery((string) $url, '_pjax'));
  477. }
  478. /**
  479. * @return string
  480. */
  481. public static function getPreviousUrl()
  482. {
  483. return (string) (session()->get('admin.prev.url') ? url(session()->get('admin.prev.url')) : url()->previous());
  484. }
  485. /**
  486. * @param mixed $command
  487. * @param int $timeout
  488. * @param null $input
  489. * @param null $cwd
  490. *
  491. * @return Process
  492. */
  493. public static function process($command, $timeout = 100, $input = null, $cwd = null)
  494. {
  495. $parameters = [
  496. $command,
  497. $cwd,
  498. [],
  499. $input,
  500. $timeout,
  501. ];
  502. return is_string($command)
  503. ? Process::fromShellCommandline(...$parameters)
  504. : new Process(...$parameters);
  505. }
  506. /**
  507. * 判断两个值是否相等.
  508. *
  509. * @param $value1
  510. * @param $value2
  511. *
  512. * @return bool
  513. */
  514. public static function equal($value1, $value2)
  515. {
  516. if ($value1 === null || $value2 === null) {
  517. return false;
  518. }
  519. if (! is_scalar($value1) || ! is_scalar($value2)) {
  520. return $value1 === $value2;
  521. }
  522. return (string) $value1 === (string) $value2;
  523. }
  524. /**
  525. * 判断给定的数组是是否包含给定元素.
  526. *
  527. * @param mixed $value
  528. * @param array $array
  529. *
  530. * @return bool
  531. */
  532. public static function inArray($value, array $array)
  533. {
  534. $array = array_map(function ($v) {
  535. if (is_scalar($v) || $v === null) {
  536. $v = (string) $v;
  537. }
  538. return $v;
  539. }, $array);
  540. return in_array((string) $value, $array, true);
  541. }
  542. /**
  543. * Limit the number of characters in a string.
  544. *
  545. * @param string $value
  546. * @param int $limit
  547. * @param string $end
  548. * @return string
  549. */
  550. public static function strLimit($value, $limit = 100, $end = '...')
  551. {
  552. if (mb_strlen($value, 'UTF-8') <= $limit) {
  553. return $value;
  554. }
  555. return rtrim(mb_substr($value, 0, $limit, 'UTF-8')).$end;
  556. }
  557. /**
  558. * 获取类名或对象的文件路径.
  559. *
  560. * @param string|object $class
  561. *
  562. * @return string
  563. *
  564. * @throws \ReflectionException
  565. */
  566. public static function guessClassFileName($class)
  567. {
  568. if (is_object($class)) {
  569. $class = get_class($class);
  570. }
  571. try {
  572. if (class_exists($class)) {
  573. return (new \ReflectionClass($class))->getFileName();
  574. }
  575. } catch (\Throwable $e) {
  576. }
  577. $class = trim($class, '\\');
  578. $composer = Composer::parse(base_path('composer.json'));
  579. $map = collect($composer->autoload['psr-4'] ?? [])->mapWithKeys(function ($path, $namespace) {
  580. $namespace = trim($namespace, '\\').'\\';
  581. return [$namespace => [$namespace, $path]];
  582. })->sortBy(function ($_, $namespace) {
  583. return strlen($namespace);
  584. }, SORT_REGULAR, true);
  585. $prefix = explode($class, '\\')[0];
  586. if ($map->isEmpty()) {
  587. if (Str::startsWith($class, 'App\\')) {
  588. $values = ['App\\', 'app/'];
  589. }
  590. } else {
  591. $values = $map->filter(function ($_, $k) use ($class) {
  592. return Str::startsWith($class, $k);
  593. })->first();
  594. }
  595. if (empty($values)) {
  596. $values = [$prefix.'\\', self::slug($prefix).'/'];
  597. }
  598. [$namespace, $path] = $values;
  599. return base_path(str_replace([$namespace, '\\'], [$path, '/'], $class)).'.php';
  600. }
  601. /**
  602. * Is input data is has-one relation.
  603. *
  604. * @param Collection $fields
  605. * @param array $input
  606. */
  607. public static function prepareHasOneRelation(Collection $fields, array &$input)
  608. {
  609. $relations = [];
  610. $fields->each(function ($field) use (&$relations) {
  611. $column = $field->column();
  612. if (is_array($column)) {
  613. foreach ($column as $v) {
  614. if (Str::contains($v, '.')) {
  615. $first = explode('.', $v)[0];
  616. $relations[$first] = null;
  617. }
  618. }
  619. return;
  620. }
  621. if (Str::contains($column, '.')) {
  622. $first = explode('.', $column)[0];
  623. $relations[$first] = null;
  624. }
  625. });
  626. foreach ($relations as $first => $v) {
  627. if (isset($input[$first])) {
  628. foreach ($input[$first] as $key => $value) {
  629. if (is_array($value)) {
  630. $input["$first.$key"] = $value;
  631. }
  632. }
  633. $input = array_merge($input, Arr::dot([$first => $input[$first]]));
  634. }
  635. }
  636. }
  637. /**
  638. * 设置查询条件.
  639. *
  640. * @param mixed $model
  641. * @param string $column
  642. * @param string $query
  643. * @param mixed array $params
  644. *
  645. * @return void
  646. */
  647. public static function withQueryCondition($model, ?string $column, string $query, array $params)
  648. {
  649. if (! Str::contains($column, '.')) {
  650. $model->$query($column, ...$params);
  651. return;
  652. }
  653. $method = $query === 'orWhere' ? 'orWhere' : 'where';
  654. $subQuery = $query === 'orWhere' ? 'where' : $query;
  655. $model->$method(function ($q) use ($column, $subQuery, $params) {
  656. static::withRelationQuery($q, $column, $subQuery, $params);
  657. });
  658. }
  659. /**
  660. * 设置关联关系查询条件.
  661. *
  662. * @param mixed $model
  663. * @param string $column
  664. * @param string $query
  665. * @param mixed ...$params
  666. *
  667. * @return void
  668. */
  669. public static function withRelationQuery($model, ?string $column, string $query, array $params)
  670. {
  671. $column = explode('.', $column);
  672. array_unshift($params, array_pop($column));
  673. // 增加对whereHasIn的支持
  674. $method = class_exists(WhereHasInServiceProvider::class) ? 'whereHasIn' : 'whereHas';
  675. $model->$method(implode('.', $column), function ($relation) use ($params, $query) {
  676. $relation->$query(...$params);
  677. });
  678. }
  679. /**
  680. * Html转义.
  681. *
  682. * @param array|string $item
  683. *
  684. * @return mixed
  685. */
  686. public static function htmlEntityEncode($item)
  687. {
  688. if (is_array($item)) {
  689. array_walk_recursive($item, function (&$value) {
  690. $value = htmlentities($value);
  691. });
  692. } else {
  693. $item = htmlentities($item);
  694. }
  695. return $item;
  696. }
  697. /**
  698. * 格式化表单元素 name 属性.
  699. *
  700. * @param string|array $name
  701. *
  702. * @return mixed|string
  703. */
  704. public static function formatElementName($name)
  705. {
  706. if (! $name) {
  707. return $name;
  708. }
  709. if (is_array($name)) {
  710. foreach ($name as &$v) {
  711. $v = static::formatElementName($v);
  712. }
  713. return $name;
  714. }
  715. $name = explode('.', $name);
  716. if (count($name) == 1) {
  717. return $name[0];
  718. }
  719. $html = array_shift($name);
  720. foreach ($name as $piece) {
  721. $html .= "[$piece]";
  722. }
  723. return $html;
  724. }
  725. /**
  726. * Set an array item to a given value using "dot" notation.
  727. *
  728. * If no key is given to the method, the entire array will be replaced.
  729. *
  730. * @param array|\ArrayAccess $array
  731. * @param string $key
  732. * @param mixed $value
  733. * @return array
  734. */
  735. public static function arraySet(&$array, $key, $value)
  736. {
  737. if (is_null($key)) {
  738. return $array = $value;
  739. }
  740. $keys = explode('.', $key);
  741. $default = null;
  742. while (count($keys) > 1) {
  743. $key = array_shift($keys);
  744. if (! isset($array[$key]) || (! is_array($array[$key]) && ! $array[$key] instanceof \ArrayAccess)) {
  745. $array[$key] = [];
  746. }
  747. if (is_array($array)) {
  748. $array = &$array[$key];
  749. } else {
  750. if (is_object($array[$key])) {
  751. $array[$key] = static::arraySet($array[$key], implode('.', $keys), $value);
  752. } else {
  753. $mid = $array[$key];
  754. $array[$key] = static::arraySet($mid, implode('.', $keys), $value);
  755. }
  756. }
  757. }
  758. $array[array_shift($keys)] = $value;
  759. return $array;
  760. }
  761. }