Helper.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. <?php
  2. namespace Dcat\Admin\Support;
  3. use Dcat\Admin\Grid;
  4. use Illuminate\Contracts\Support\Arrayable;
  5. use Illuminate\Contracts\Support\Htmlable;
  6. use Illuminate\Contracts\Support\Jsonable;
  7. use Illuminate\Contracts\Support\Renderable;
  8. use Illuminate\Http\Request;
  9. use Illuminate\Support\Arr;
  10. use Illuminate\Support\Facades\Artisan;
  11. use Illuminate\Support\Facades\File;
  12. use Illuminate\Support\Str;
  13. use Symfony\Component\Process\Process;
  14. class Helper
  15. {
  16. /**
  17. * @var array
  18. */
  19. public static $fileTypes = [
  20. 'image' => 'png|jpg|jpeg|tmp|gif',
  21. 'word' => 'doc|docx',
  22. 'excel' => 'xls|xlsx|csv',
  23. 'powerpoint' => 'ppt|pptx',
  24. 'pdf' => 'pdf',
  25. 'code' => 'php|js|java|python|ruby|go|c|cpp|sql|m|h|json|html|aspx',
  26. 'archive' => 'zip|tar\.gz|rar|rpm',
  27. 'txt' => 'txt|pac|log|md',
  28. 'audio' => 'mp3|wav|flac|3pg|aa|aac|ape|au|m4a|mpc|ogg',
  29. 'video' => 'mkv|rmvb|flv|mp4|avi|wmv|rm|asf|mpeg',
  30. ];
  31. /**
  32. * 更新扩展配置.
  33. *
  34. * @param array $config
  35. *
  36. * @return bool
  37. */
  38. public static function updateExtensionConfig(array $config)
  39. {
  40. $files = app('files');
  41. $result = (bool) $files->put(config_path('admin-extensions.php'), self::exportArrayPhp($config));
  42. if ($result && is_file(base_path('bootstrap/cache/config.php'))) {
  43. Artisan::call('config:cache');
  44. }
  45. config(['admin-extensions' => $config]);
  46. return $result;
  47. }
  48. /**
  49. * 把给定的值转化为数组.
  50. *
  51. * @param $value
  52. * @param bool $filter
  53. *
  54. * @return array
  55. */
  56. public static function array($value, bool $filter = true): array
  57. {
  58. if (! $value) {
  59. return [];
  60. }
  61. if ($value instanceof \Closure) {
  62. $value = $value();
  63. }
  64. if (is_array($value)) {
  65. } elseif ($value instanceof Jsonable) {
  66. $value = json_decode($value->toJson(), true);
  67. } elseif ($value instanceof Arrayable) {
  68. $value = $value->toArray();
  69. } elseif (is_string($value)) {
  70. $array = null;
  71. try {
  72. $array = json_decode($value, true);
  73. } catch (\Throwable $e) {
  74. }
  75. $value = is_array($array) ? $array : explode(',', $value);
  76. } else {
  77. $value = (array) $value;
  78. }
  79. return $filter ? array_filter($value, function ($v) {
  80. return $v !== '' && $v !== null;
  81. }) : $value;
  82. }
  83. /**
  84. * 把给定的值转化为字符串.
  85. *
  86. * @param string|Grid|\Closure|Renderable|Htmlable $value
  87. * @param array $params
  88. * @param object $newThis
  89. *
  90. * @return string
  91. */
  92. public static function render($value, $params = [], $newThis = null): string
  93. {
  94. if (is_string($value)) {
  95. return $value;
  96. }
  97. if ($value instanceof Grid) {
  98. return (string) $value->render();
  99. }
  100. if ($value instanceof \Closure) {
  101. $newThis && $value = $value->bindTo($newThis);
  102. $value = $value(...(array) $params);
  103. }
  104. if ($value instanceof Renderable) {
  105. return (string) $value->render();
  106. }
  107. if ($value instanceof Htmlable) {
  108. return (string) $value->toHtml();
  109. }
  110. return (string) $value;
  111. }
  112. /**
  113. * @param array $attributes
  114. *
  115. * @return string
  116. */
  117. public static function buildHtmlAttributes($attributes)
  118. {
  119. $html = '';
  120. foreach ((array) $attributes as $key => &$value) {
  121. if (is_array($value)) {
  122. $value = implode(' ', $value);
  123. }
  124. if (is_numeric($key)) {
  125. $key = $value;
  126. }
  127. $element = '';
  128. if ($value !== null) {
  129. $element = $key.'="'.htmlentities($value, ENT_QUOTES, 'UTF-8').'"';
  130. }
  131. $html .= $element;
  132. }
  133. return $html;
  134. }
  135. /**
  136. * @param string $url
  137. * @param array $query
  138. *
  139. * @return string
  140. */
  141. public static function urlWithQuery(?string $url, array $query = [])
  142. {
  143. if (! $url || ! $query) {
  144. return $url;
  145. }
  146. $array = explode('?', $url);
  147. $url = $array[0];
  148. parse_str($array[1] ?? '', $originalQuery);
  149. return $url.'?'.http_build_query(array_merge($originalQuery, $query));
  150. }
  151. /**
  152. * @param string $url
  153. * @param string|array|Arrayable $keys
  154. *
  155. * @return string
  156. */
  157. public static function urlWithoutQuery($url, $keys)
  158. {
  159. if (! Str::contains($url, '?') || ! $keys) {
  160. return $url;
  161. }
  162. if ($keys instanceof Arrayable) {
  163. $keys = $keys->toArray();
  164. }
  165. $keys = (array) $keys;
  166. $urlInfo = parse_url($url);
  167. parse_str($urlInfo['query'], $query);
  168. Arr::forget($query, $keys);
  169. $baseUrl = explode('?', $url)[0];
  170. return $query
  171. ? $baseUrl.'?'.http_build_query($query)
  172. : $baseUrl;
  173. }
  174. /**
  175. * @param Arrayable|array|string $keys
  176. *
  177. * @return string
  178. */
  179. public static function fullUrlWithoutQuery($keys)
  180. {
  181. return static::urlWithoutQuery(request()->fullUrl(), $keys);
  182. }
  183. /**
  184. * @param string $url
  185. * @param string|array $keys
  186. *
  187. * @return bool
  188. */
  189. public static function urlHasQuery(string $url, $keys)
  190. {
  191. $value = explode('?', $url);
  192. if (empty($value[1])) {
  193. return false;
  194. }
  195. parse_str($value[1], $query);
  196. foreach ((array) $keys as $key) {
  197. if (Arr::has($query, $key)) {
  198. return true;
  199. }
  200. }
  201. return false;
  202. }
  203. /**
  204. * 匹配请求路径.
  205. *
  206. * @example
  207. * Helper::matchRequestPath(admin_base_path('auth/user'))
  208. * Helper::matchRequestPath(admin_base_path('auth/user*'))
  209. * Helper::matchRequestPath(admin_base_path('auth/user/* /edit'))
  210. * Helper::matchRequestPath('GET,POST:auth/user')
  211. *
  212. * @param string $path
  213. * @param null|string $current
  214. *
  215. * @return bool
  216. */
  217. public static function matchRequestPath($path, ?string $current = null)
  218. {
  219. $request = request();
  220. $current = $current ?: $request->decodedPath();
  221. if (Str::contains($path, ':')) {
  222. [$methods, $path] = explode(':', $path);
  223. $methods = array_map('strtoupper', explode(',', $methods));
  224. if (! empty($methods) && ! in_array($request->method(), $methods)) {
  225. return false;
  226. }
  227. }
  228. // 判断路由名称
  229. if ($request->routeIs($path)) {
  230. return true;
  231. }
  232. if (! Str::contains($path, '*')) {
  233. return $path === $current;
  234. }
  235. $path = str_replace(['*', '/'], ['([0-9a-z-_,])*', "\/"], $path);
  236. return preg_match("/$path/i", $current);
  237. }
  238. /**
  239. * 生成层级数据.
  240. *
  241. * @param array $nodes
  242. * @param int $parentId
  243. * @param string|null $primaryKeyName
  244. * @param string|null $parentKeyName
  245. * @param string|null $childrenKeyName
  246. *
  247. * @return array
  248. */
  249. public static function buildNestedArray(
  250. $nodes = [],
  251. $parentId = 0,
  252. ?string $primaryKeyName = null,
  253. ?string $parentKeyName = null,
  254. ?string $childrenKeyName = null
  255. ) {
  256. $branch = [];
  257. $primaryKeyName = $primaryKeyName ?: 'id';
  258. $parentKeyName = $parentKeyName ?: 'parent_id';
  259. $childrenKeyName = $childrenKeyName ?: 'children';
  260. $parentId = is_numeric($parentId) ? (int) $parentId : $parentId;
  261. foreach ($nodes as $node) {
  262. $pk = Arr::get($node, $parentKeyName);
  263. $pk = is_numeric($pk) ? (int) $pk : $pk;
  264. if ($pk === $parentId) {
  265. $children = static::buildNestedArray(
  266. $nodes,
  267. Arr::get($node, $primaryKeyName),
  268. $primaryKeyName,
  269. $parentKeyName,
  270. $childrenKeyName
  271. );
  272. if ($children) {
  273. $node[$childrenKeyName] = $children;
  274. }
  275. $branch[] = $node;
  276. }
  277. }
  278. return $branch;
  279. }
  280. /**
  281. * @param string $name
  282. * @param string $symbol
  283. *
  284. * @return mixed
  285. */
  286. public static function slug(string $name, string $symbol = '-')
  287. {
  288. $text = preg_replace_callback('/([A-Z])/', function (&$text) use ($symbol) {
  289. return $symbol.strtolower($text[1]);
  290. }, $name);
  291. return str_replace('_', $symbol, ltrim($text, $symbol));
  292. }
  293. /**
  294. * @param array $array
  295. * @param int $level
  296. *
  297. * @return string
  298. */
  299. public static function exportArray(array &$array, $level = 1)
  300. {
  301. $start = '[';
  302. $end = ']';
  303. $txt = "$start\n";
  304. foreach ($array as $k => &$v) {
  305. if (is_array($v)) {
  306. $pre = is_string($k) ? "'$k' => " : "$k => ";
  307. $txt .= str_repeat(' ', $level * 4).$pre.static::exportArray($v, $level + 1).",\n";
  308. continue;
  309. }
  310. $t = $v;
  311. if ($v === true) {
  312. $t = 'true';
  313. } elseif ($v === false) {
  314. $t = 'false';
  315. } elseif ($v === null) {
  316. $t = 'null';
  317. } elseif (is_string($v)) {
  318. $v = str_replace("'", "\\'", $v);
  319. $t = "'$v'";
  320. }
  321. $pre = is_string($k) ? "'$k' => " : "$k => ";
  322. $txt .= str_repeat(' ', $level * 4)."{$pre}{$t},\n";
  323. }
  324. return $txt.str_repeat(' ', ($level - 1) * 4).$end;
  325. }
  326. /**
  327. * @param array $array
  328. *
  329. * @return string
  330. */
  331. public static function exportArrayPhp(array $array)
  332. {
  333. return "<?php \nreturn ".static::exportArray($array).";\n";
  334. }
  335. /**
  336. * 删除数组中的元素.
  337. *
  338. * @param array $array
  339. * @param mixed $value
  340. */
  341. public static function deleteByValue(&$array, $value)
  342. {
  343. $value = (array) $value;
  344. foreach ($array as $index => $item) {
  345. if (in_array($item, $value)) {
  346. unset($array[$index]);
  347. }
  348. }
  349. }
  350. /**
  351. * 颜色转亮.
  352. *
  353. * @param string $color
  354. * @param int $amt
  355. *
  356. * @return string
  357. */
  358. public static function colorLighten(string $color, int $amt)
  359. {
  360. if (! $amt) {
  361. return $color;
  362. }
  363. $hasPrefix = false;
  364. if (mb_strpos($color, '#') === 0) {
  365. $color = mb_substr($color, 1);
  366. $hasPrefix = true;
  367. }
  368. [$red, $blue, $green] = static::colorToRBG($color, $amt);
  369. return ($hasPrefix ? '#' : '').dechex($green + ($blue << 8) + ($red << 16));
  370. }
  371. /**
  372. * 颜色转暗.
  373. *
  374. * @param string $color
  375. * @param int $amt
  376. *
  377. * @return string
  378. */
  379. public static function colorDarken(string $color, int $amt)
  380. {
  381. return static::colorLighten($color, -$amt);
  382. }
  383. /**
  384. * 颜色透明度.
  385. *
  386. * @param string $color
  387. * @param float|string $alpha
  388. *
  389. * @return string
  390. */
  391. public static function colorAlpha(string $color, $alpha)
  392. {
  393. if ($alpha >= 1) {
  394. return $color;
  395. }
  396. if (mb_strpos($color, '#') === 0) {
  397. $color = mb_substr($color, 1);
  398. }
  399. [$red, $blue, $green] = static::colorToRBG($color);
  400. return "rgba($red, $blue, $green, $alpha)";
  401. }
  402. /**
  403. * @param string $color
  404. * @param int $amt
  405. *
  406. * @return array
  407. */
  408. public static function colorToRBG(string $color, int $amt = 0)
  409. {
  410. $format = function ($value) {
  411. if ($value > 255) {
  412. return 255;
  413. }
  414. if ($value < 0) {
  415. return 0;
  416. }
  417. return $value;
  418. };
  419. $num = hexdec($color);
  420. $red = $format(($num >> 16) + $amt);
  421. $blue = $format((($num >> 8) & 0x00FF) + $amt);
  422. $green = $format(($num & 0x0000FF) + $amt);
  423. return [$red, $blue, $green];
  424. }
  425. /**
  426. * 验证扩展包名称.
  427. *
  428. * @param string $name
  429. *
  430. * @return int
  431. */
  432. public static function validateExtensionName($name)
  433. {
  434. return preg_match('/^[\w\-_]+\/[\w\-_]+$/', $name);
  435. }
  436. /**
  437. * Get file icon.
  438. *
  439. * @param string $file
  440. *
  441. * @return string
  442. */
  443. public static function getFileIcon($file = '')
  444. {
  445. $extension = File::extension($file);
  446. foreach (static::$fileTypes as $type => $regex) {
  447. if (preg_match("/^($regex)$/i", $extension) !== 0) {
  448. return "fa fa-file-{$type}-o";
  449. }
  450. }
  451. return 'fa fa-file-o';
  452. }
  453. /**
  454. * 判断是否是ajax请求.
  455. *
  456. * @param Request $request
  457. *
  458. * @return bool
  459. */
  460. public static function isAjaxRequest(?Request $request = null)
  461. {
  462. /* @var Request $request */
  463. $request = $request ?: request();
  464. return $request->ajax() && ! $request->pjax();
  465. }
  466. /**
  467. * 判断是否是IE浏览器.
  468. *
  469. * @return false|int
  470. */
  471. public static function isIEBrowser()
  472. {
  473. 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'] ?? '');
  474. }
  475. /**
  476. * 判断是否QQ浏览器.
  477. *
  478. * @return bool
  479. */
  480. public static function isQQBrowser()
  481. {
  482. return mb_strpos(mb_strtolower($_SERVER['HTTP_USER_AGENT'] ?? ''), 'qqbrowser') !== false;
  483. }
  484. /**
  485. * @param string $url
  486. *
  487. * @return void
  488. */
  489. public static function setPreviousUrl($url)
  490. {
  491. session()->flash('admin.prev.url', static::urlWithoutQuery((string) $url, '_pjax'));
  492. }
  493. /**
  494. * @return string
  495. */
  496. public static function getPreviousUrl()
  497. {
  498. return (string) (session()->get('admin.prev.url') ? url(session()->get('admin.prev.url')) : url()->previous());
  499. }
  500. /**
  501. * @param mixed $command
  502. * @param int $timeout
  503. * @param null $input
  504. * @param null $cwd
  505. *
  506. * @return Process
  507. */
  508. public static function process($command, $timeout = 100, $input = null, $cwd = null)
  509. {
  510. $parameters = [
  511. $command,
  512. $cwd,
  513. [],
  514. $input,
  515. $timeout,
  516. ];
  517. return is_string($command)
  518. ? Process::fromShellCommandline(...$parameters)
  519. : new Process(...$parameters);
  520. }
  521. /**
  522. * 判断两个值是否相等.
  523. *
  524. * @param $value1
  525. * @param $value2
  526. *
  527. * @return bool
  528. */
  529. public static function equal($value1, $value2)
  530. {
  531. if ($value1 === null || $value2 === null) {
  532. return false;
  533. }
  534. if (! is_scalar($value1) || ! is_scalar($value2)) {
  535. return $value1 === $value2;
  536. }
  537. return (string) $value1 === (string) $value2;
  538. }
  539. }