Helper.php 21 KB

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