helper.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2025 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <liu21st@gmail.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types = 1);
  12. //------------------------
  13. // ThinkPHP 助手函数
  14. //-------------------------
  15. use think\App;
  16. use think\Container;
  17. use think\exception\HttpException;
  18. use think\exception\HttpResponseException;
  19. use think\facade\Cache;
  20. use think\facade\Config;
  21. use think\facade\Cookie;
  22. use think\facade\Env;
  23. use think\facade\Event;
  24. use think\facade\Lang;
  25. use think\facade\Log;
  26. use think\facade\Request;
  27. use think\facade\Route;
  28. use think\facade\Session;
  29. use think\Response;
  30. use think\response\File;
  31. use think\response\Json;
  32. use think\response\Jsonp;
  33. use think\response\Redirect;
  34. use think\response\View;
  35. use think\response\Xml;
  36. use think\route\Url as UrlBuild;
  37. use think\Validate;
  38. use think\validate\ValidateRuleSet;
  39. if (!function_exists('abort')) {
  40. /**
  41. * 抛出HTTP异常
  42. * @param integer|Response $code 状态码 或者 Response对象实例
  43. * @param string $message 错误信息
  44. * @param array $header 参数
  45. */
  46. function abort($code, string $message = '', array $header = [])
  47. {
  48. if ($code instanceof Response) {
  49. throw new HttpResponseException($code);
  50. } else {
  51. throw new HttpException($code, $message, null, $header);
  52. }
  53. }
  54. }
  55. if (!function_exists('app')) {
  56. /**
  57. * 快速获取容器中的实例 支持依赖注入
  58. * @template T
  59. * @param string|class-string<T> $name 类名或标识 默认获取当前应用实例
  60. * @param array $args 参数
  61. * @param bool $newInstance 是否每次创建新的实例
  62. * @return T|object|App
  63. */
  64. function app(string $name = '', array $args = [], bool $newInstance = false)
  65. {
  66. return Container::getInstance()->make($name ?: App::class, $args, $newInstance);
  67. }
  68. }
  69. if (!function_exists('bind')) {
  70. /**
  71. * 绑定一个类到容器
  72. * @param string|array $abstract 类标识、接口(支持批量绑定)
  73. * @param mixed $concrete 要绑定的类、闭包或者实例
  74. * @return Container
  75. */
  76. function bind($abstract, $concrete = null)
  77. {
  78. return Container::getInstance()->bind($abstract, $concrete);
  79. }
  80. }
  81. if (!function_exists('cache')) {
  82. /**
  83. * 缓存管理
  84. * @param string $name 缓存名称
  85. * @param mixed $value 缓存值
  86. * @param mixed $options 缓存参数
  87. * @param string $tag 缓存标签
  88. * @return mixed
  89. */
  90. function cache(?string $name = null, $value = '', $options = null, $tag = null)
  91. {
  92. if (is_null($name)) {
  93. return app('cache');
  94. }
  95. if ('' === $value) {
  96. // 获取缓存
  97. return str_starts_with($name, '?') ? Cache::has(substr($name, 1)) : Cache::get($name);
  98. } elseif (is_null($value)) {
  99. // 删除缓存
  100. return Cache::delete($name);
  101. }
  102. // 缓存数据
  103. if (is_array($options)) {
  104. $expire = $options['expire'] ?? null; //修复查询缓存无法设置过期时间
  105. } else {
  106. $expire = $options;
  107. }
  108. if (is_null($tag)) {
  109. return Cache::set($name, $value, $expire);
  110. } else {
  111. return Cache::tag($tag)->set($name, $value, $expire);
  112. }
  113. }
  114. }
  115. if (!function_exists('config')) {
  116. /**
  117. * 获取和设置配置参数
  118. * @param string|array $name 参数名
  119. * @param mixed $value 参数值
  120. * @return mixed
  121. */
  122. function config($name = '', $value = null)
  123. {
  124. if (is_array($name)) {
  125. return Config::set($name, $value);
  126. }
  127. return str_starts_with($name, '?') ? Config::has(substr($name, 1)) : Config::get($name, $value);
  128. }
  129. }
  130. if (!function_exists('cookie')) {
  131. /**
  132. * Cookie管理
  133. * @param string $name cookie名称
  134. * @param mixed $value cookie值
  135. * @param mixed $option 参数
  136. * @return mixed
  137. */
  138. function cookie(string $name, $value = '', $option = null)
  139. {
  140. if (is_null($value)) {
  141. // 删除
  142. Cookie::delete($name, $option ?: []);
  143. } elseif ('' === $value) {
  144. // 获取
  145. return str_starts_with($name, '?') ? Cookie::has(substr($name, 1)) : Cookie::get($name);
  146. } else {
  147. // 设置
  148. return Cookie::set($name, $value, $option);
  149. }
  150. }
  151. }
  152. if (!function_exists('download')) {
  153. /**
  154. * 获取\think\response\Download对象实例
  155. * @param string $filename 要下载的文件
  156. * @param string $name 显示文件名
  157. * @param bool $content 是否为内容
  158. * @param int $expire 有效期(秒)
  159. * @return \think\response\File
  160. */
  161. function download(string $filename, string $name = '', bool $content = false, int $expire = 180): File
  162. {
  163. return Response::create($filename, 'file')->name($name)->isContent($content)->expire($expire);
  164. }
  165. }
  166. if (!function_exists('dump')) {
  167. /**
  168. * 浏览器友好的变量输出
  169. * @param mixed $vars 要输出的变量
  170. * @return void
  171. */
  172. function dump(...$vars)
  173. {
  174. ob_start();
  175. var_dump(...$vars);
  176. $output = ob_get_clean();
  177. $output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output);
  178. if (PHP_SAPI == 'cli') {
  179. $output = PHP_EOL . $output . PHP_EOL;
  180. } else {
  181. if (!extension_loaded('xdebug')) {
  182. $output = htmlspecialchars($output, ENT_SUBSTITUTE);
  183. }
  184. $output = '<pre>' . $output . '</pre>';
  185. }
  186. echo $output;
  187. }
  188. }
  189. if (!function_exists('env')) {
  190. /**
  191. * 获取环境变量值
  192. * @access public
  193. * @param string $name 环境变量名(支持二级 .号分割)
  194. * @param string $default 默认值
  195. * @return mixed
  196. */
  197. function env(?string $name = null, $default = null)
  198. {
  199. return Env::get($name, $default);
  200. }
  201. }
  202. if (!function_exists('event')) {
  203. /**
  204. * 触发事件
  205. * @param mixed $event 事件名(或者类名)
  206. * @param mixed $args 参数
  207. * @return mixed
  208. */
  209. function event($event, $args = null)
  210. {
  211. return Event::trigger($event, $args);
  212. }
  213. }
  214. if (!function_exists('halt')) {
  215. /**
  216. * 调试变量并且中断输出
  217. * @param mixed $vars 调试变量或者信息
  218. */
  219. function halt(...$vars)
  220. {
  221. dump(...$vars);
  222. throw new HttpResponseException(Response::create());
  223. }
  224. }
  225. if (!function_exists('input')) {
  226. /**
  227. * 获取输入数据 支持默认值和过滤
  228. * @param string $key 获取的变量名
  229. * @param mixed $default 默认值
  230. * @param string|array|null $filter 过滤方法
  231. * @return mixed
  232. */
  233. function input(string $key = '', $default = null, $filter = '')
  234. {
  235. if (str_starts_with($key, '?')) {
  236. $key = substr($key, 1);
  237. $has = true;
  238. }
  239. if ($pos = strpos($key, '.')) {
  240. // 指定参数来源
  241. $method = substr($key, 0, $pos);
  242. if (in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'route', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) {
  243. $key = substr($key, $pos + 1);
  244. if ('server' == $method && is_null($default)) {
  245. $default = '';
  246. }
  247. } else {
  248. $method = 'param';
  249. }
  250. } else {
  251. // 默认为自动判断
  252. $method = 'param';
  253. }
  254. return isset($has) ?
  255. request()->has($key, $method) :
  256. request()->$method($key, $default, $filter);
  257. }
  258. }
  259. if (!function_exists('invoke')) {
  260. /**
  261. * 调用反射实例化对象或者执行方法 支持依赖注入
  262. * @param mixed $call 类名或者callable
  263. * @param array $args 参数
  264. * @return mixed
  265. */
  266. function invoke($call, array $args = [])
  267. {
  268. if (is_callable($call)) {
  269. return Container::getInstance()->invoke($call, $args);
  270. }
  271. return Container::getInstance()->invokeClass($call, $args);
  272. }
  273. }
  274. if (!function_exists('json')) {
  275. /**
  276. * 获取\think\response\Json对象实例
  277. * @param mixed $data 返回的数据
  278. * @param int $code 状态码
  279. * @param array $header 头部
  280. * @param array $options 参数
  281. * @return \think\response\Json
  282. */
  283. function json($data = [], $code = 200, $header = [], $options = []): Json
  284. {
  285. return Response::create($data, 'json', $code)->header($header)->options($options);
  286. }
  287. }
  288. if (!function_exists('jsonp')) {
  289. /**
  290. * 获取\think\response\Jsonp对象实例
  291. * @param mixed $data 返回的数据
  292. * @param int $code 状态码
  293. * @param array $header 头部
  294. * @param array $options 参数
  295. * @return \think\response\Jsonp
  296. */
  297. function jsonp($data = [], $code = 200, $header = [], $options = []): Jsonp
  298. {
  299. return Response::create($data, 'jsonp', $code)->header($header)->options($options);
  300. }
  301. }
  302. if (!function_exists('lang')) {
  303. /**
  304. * 获取语言变量值
  305. * @param string $name 语言变量名
  306. * @param array $vars 动态变量值
  307. * @param string $lang 语言
  308. * @return mixed
  309. */
  310. function lang(string $name, array $vars = [], string $lang = '')
  311. {
  312. return Lang::get($name, $vars, $lang);
  313. }
  314. }
  315. if (!function_exists('parse_name')) {
  316. /**
  317. * 字符串命名风格转换
  318. * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
  319. * @param string $name 字符串
  320. * @param int $type 转换类型
  321. * @param bool $ucfirst 首字母是否大写(驼峰规则)
  322. * @return string
  323. */
  324. function parse_name(string $name, int $type = 0, bool $ucfirst = true): string
  325. {
  326. if ($type) {
  327. $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
  328. return strtoupper($match[1]);
  329. }, $name);
  330. return $ucfirst ? ucfirst($name) : lcfirst($name);
  331. }
  332. return strtolower(trim(preg_replace('/[A-Z]/', '_\\0', $name), '_'));
  333. }
  334. }
  335. if (!function_exists('redirect')) {
  336. /**
  337. * 获取\think\response\Redirect对象实例
  338. * @param string $url 重定向地址
  339. * @param int $code 状态码
  340. * @return \think\response\Redirect
  341. */
  342. function redirect(string $url = '', int $code = 302): Redirect
  343. {
  344. return Response::create($url, 'redirect', $code);
  345. }
  346. }
  347. if (!function_exists('request')) {
  348. /**
  349. * 获取当前Request对象实例
  350. * @return Request
  351. */
  352. function request(): \think\Request
  353. {
  354. return app('request');
  355. }
  356. }
  357. if (!function_exists('response')) {
  358. /**
  359. * 创建普通 Response 对象实例
  360. * @param mixed $data 输出数据
  361. * @param int|string $code 状态码
  362. * @param array $header 头信息
  363. * @param string $type
  364. * @return Response
  365. */
  366. function response($data = '', $code = 200, $header = [], $type = 'html'): Response
  367. {
  368. return Response::create($data, $type, $code)->header($header);
  369. }
  370. }
  371. if (!function_exists('session')) {
  372. /**
  373. * Session管理
  374. * @param string $name session名称
  375. * @param mixed $value session值
  376. * @return mixed
  377. */
  378. function session($name = '', $value = '')
  379. {
  380. if (is_null($name)) {
  381. // 清除
  382. Session::clear();
  383. } elseif ('' === $name) {
  384. return Session::all();
  385. } elseif (is_null($value)) {
  386. // 删除
  387. Session::delete($name);
  388. } elseif ('' === $value) {
  389. // 判断或获取
  390. return str_starts_with($name, '?') ? Session::has(substr($name, 1)) : Session::get($name);
  391. } else {
  392. // 设置
  393. Session::set($name, $value);
  394. }
  395. }
  396. }
  397. if (!function_exists('token')) {
  398. /**
  399. * 获取Token令牌
  400. * @param string $name 令牌名称
  401. * @param mixed $type 令牌生成方法
  402. * @return string
  403. */
  404. function token(string $name = '__token__', string $type = 'md5'): string
  405. {
  406. return Request::buildToken($name, $type);
  407. }
  408. }
  409. if (!function_exists('token_field')) {
  410. /**
  411. * 生成令牌隐藏表单
  412. * @param string $name 令牌名称
  413. * @param mixed $type 令牌生成方法
  414. * @return string
  415. */
  416. function token_field(string $name = '__token__', string $type = 'md5'): string
  417. {
  418. $token = Request::buildToken($name, $type);
  419. return '<input type="hidden" name="' . $name . '" value="' . $token . '" />';
  420. }
  421. }
  422. if (!function_exists('token_meta')) {
  423. /**
  424. * 生成令牌meta
  425. * @param string $name 令牌名称
  426. * @param mixed $type 令牌生成方法
  427. * @return string
  428. */
  429. function token_meta(string $name = '__token__', string $type = 'md5'): string
  430. {
  431. $token = Request::buildToken($name, $type);
  432. return '<meta name="csrf-token" content="' . $token . '">';
  433. }
  434. }
  435. if (!function_exists('trace')) {
  436. /**
  437. * 记录日志信息
  438. * @param mixed $log log信息 支持字符串和数组
  439. * @param string $level 日志级别
  440. * @return array|void
  441. */
  442. function trace($log = '[think]', string $level = 'log')
  443. {
  444. if ('[think]' === $log) {
  445. return Log::getLog();
  446. }
  447. Log::record($log, $level);
  448. }
  449. }
  450. if (!function_exists('url')) {
  451. /**
  452. * Url生成
  453. * @param string $url 路由地址
  454. * @param array $vars 变量
  455. * @param bool|string $suffix 生成的URL后缀
  456. * @param bool|string $domain 域名
  457. * @return UrlBuild
  458. */
  459. function url(string $url = '', array $vars = [], $suffix = true, $domain = false): UrlBuild
  460. {
  461. return Route::buildUrl($url, $vars)->suffix($suffix)->domain($domain);
  462. }
  463. }
  464. if (!function_exists('validate')) {
  465. /**
  466. * 生成验证对象
  467. * @param string|array $validate 验证器类名或者验证规则数组
  468. * @param array $message 错误提示信息
  469. * @param bool $batch 是否批量验证
  470. * @param bool $failException 是否抛出异常
  471. * @return Validate
  472. */
  473. function validate($validate = '', array $message = [], bool $batch = false, bool $failException = true): Validate
  474. {
  475. if (is_array($validate) || '' === $validate) {
  476. $v = new Validate();
  477. if (is_array($validate)) {
  478. $v->rule($validate);
  479. }
  480. } else {
  481. if (str_contains($validate, '.')) {
  482. // 支持场景
  483. [$validate, $scene] = explode('.', $validate);
  484. }
  485. $class = str_contains($validate, '\\') ? $validate : app()->parseClass('validate', $validate);
  486. $v = new $class();
  487. if (!empty($scene)) {
  488. $v->scene($scene);
  489. }
  490. }
  491. return $v->message($message)->batch($batch)->failException($failException);
  492. }
  493. }
  494. if (!function_exists('rules')) {
  495. /**
  496. * 定义ValidateRuleSet规则集合
  497. * @param array $rules 验证因子集
  498. * @return ValidateRuleSet
  499. */
  500. function rules(array $rules): ValidateRuleSet
  501. {
  502. return ValidateRuleSet::rules($rules);
  503. }
  504. }
  505. if (!function_exists('view')) {
  506. /**
  507. * 渲染模板输出
  508. * @param string $template 模板文件
  509. * @param array $vars 模板变量
  510. * @param int $code 状态码
  511. * @param callable $filter 内容过滤
  512. * @return \think\response\View
  513. */
  514. function view(string $template = '', $vars = [], $code = 200, $filter = null): View
  515. {
  516. return Response::create($template, 'view', $code)->assign($vars)->filter($filter);
  517. }
  518. }
  519. if (!function_exists('display')) {
  520. /**
  521. * 渲染模板输出
  522. * @param string $content 渲染内容
  523. * @param array $vars 模板变量
  524. * @param int $code 状态码
  525. * @param callable $filter 内容过滤
  526. * @return \think\response\View
  527. */
  528. function display(string $content, $vars = [], $code = 200, $filter = null): View
  529. {
  530. return Response::create($content, 'view', $code)->isContent(true)->assign($vars)->filter($filter);
  531. }
  532. }
  533. if (!function_exists('xml')) {
  534. /**
  535. * 获取\think\response\Xml对象实例
  536. * @param mixed $data 返回的数据
  537. * @param int $code 状态码
  538. * @param array $header 头部
  539. * @param array $options 参数
  540. * @return \think\response\Xml
  541. */
  542. function xml($data = [], $code = 200, $header = [], $options = []): Xml
  543. {
  544. return Response::create($data, 'xml', $code)->header($header)->options($options);
  545. }
  546. }
  547. if (!function_exists('app_path')) {
  548. /**
  549. * 获取当前应用目录
  550. *
  551. * @param string $path
  552. * @return string
  553. */
  554. function app_path($path = '')
  555. {
  556. return app()->getAppPath() . ($path ? $path . DIRECTORY_SEPARATOR : $path);
  557. }
  558. }
  559. if (!function_exists('base_path')) {
  560. /**
  561. * 获取应用基础目录
  562. *
  563. * @param string $path
  564. * @return string
  565. */
  566. function base_path($path = '')
  567. {
  568. return app()->getBasePath() . ($path ? $path . DIRECTORY_SEPARATOR : $path);
  569. }
  570. }
  571. if (!function_exists('config_path')) {
  572. /**
  573. * 获取应用配置目录
  574. *
  575. * @param string $path
  576. * @return string
  577. */
  578. function config_path($path = '')
  579. {
  580. return app()->getConfigPath() . ($path ? $path . DIRECTORY_SEPARATOR : $path);
  581. }
  582. }
  583. if (!function_exists('public_path')) {
  584. /**
  585. * 获取web根目录
  586. *
  587. * @param string $path
  588. * @return string
  589. */
  590. function public_path($path = '')
  591. {
  592. return app()->getRootPath() . 'public' . DIRECTORY_SEPARATOR . ($path ? ltrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $path);
  593. }
  594. }
  595. if (!function_exists('runtime_path')) {
  596. /**
  597. * 获取应用运行时目录
  598. *
  599. * @param string $path
  600. * @return string
  601. */
  602. function runtime_path($path = '')
  603. {
  604. return app()->getRuntimePath() . ($path ? $path . DIRECTORY_SEPARATOR : $path);
  605. }
  606. }
  607. if (!function_exists('root_path')) {
  608. /**
  609. * 获取项目根目录
  610. *
  611. * @param string $path
  612. * @return string
  613. */
  614. function root_path($path = '')
  615. {
  616. return app()->getRootPath() . ($path ? $path . DIRECTORY_SEPARATOR : $path);
  617. }
  618. }