common.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. <?php
  2. // 应用公共文件
  3. use think\App;
  4. use ba\Filesystem;
  5. use think\Response;
  6. use think\facade\Db;
  7. use think\facade\Lang;
  8. use think\facade\Event;
  9. use think\facade\Config;
  10. use voku\helper\AntiXSS;
  11. use app\admin\model\Config as configModel;
  12. use think\exception\HttpResponseException;
  13. use Symfony\Component\HttpFoundation\IpUtils;
  14. if (!function_exists('__')) {
  15. /**
  16. * 语言翻译
  17. * @param string $name 被翻译字符
  18. * @param array $vars 替换字符数组
  19. * @param string $lang 翻译语言
  20. * @return mixed
  21. */
  22. function __(string $name, array $vars = [], string $lang = ''): mixed
  23. {
  24. if (is_numeric($name) || !$name) {
  25. return $name;
  26. }
  27. return Lang::get($name, $vars, $lang);
  28. }
  29. }
  30. if (!function_exists('filter')) {
  31. /**
  32. * 输入过滤
  33. * 富文本反XSS请使用 clean_xss,也就不需要及不能再 filter 了
  34. * @param string $string 要过滤的字符串
  35. * @return string
  36. */
  37. function filter(string $string): string
  38. {
  39. // 去除字符串两端空格(对防代码注入有一定作用)
  40. $string = trim($string);
  41. // 过滤html和php标签
  42. $string = strip_tags($string);
  43. // 特殊字符转实体
  44. return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, 'UTF-8');
  45. }
  46. }
  47. if (!function_exists('clean_xss')) {
  48. /**
  49. * 清理XSS
  50. * 通常只用于富文本,比 filter 慢
  51. * @param string $string
  52. * @return string
  53. */
  54. function clean_xss(string $string): string
  55. {
  56. return (new AntiXSS())->xss_clean($string);
  57. }
  58. }
  59. if (!function_exists('htmlspecialchars_decode_improve')) {
  60. /**
  61. * html解码增强
  62. * 被 clean_xss函数 和 filter函数 内的 htmlspecialchars 编码的字符串,需要用此函数才能完全解码
  63. * @param string $string
  64. * @param int $flags
  65. * @return string
  66. */
  67. function htmlspecialchars_decode_improve(string $string, int $flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401): string
  68. {
  69. return htmlspecialchars_decode($string, $flags);
  70. }
  71. }
  72. if (!function_exists('get_sys_config')) {
  73. /**
  74. * 获取站点的系统配置,不传递参数则获取所有配置项
  75. * @param string $name 变量名
  76. * @param string $group 变量分组,传递此参数来获取某个分组的所有配置项
  77. * @param bool $concise 是否开启简洁模式,简洁模式下,获取多项配置时只返回配置的键值对
  78. * @return mixed
  79. * @throws Throwable
  80. */
  81. function get_sys_config(string $name = '', string $group = '', bool $concise = true): mixed
  82. {
  83. if ($name) {
  84. // 直接使用->value('value')不能使用到模型的类型格式化
  85. $config = configModel::cache($name, null, configModel::$cacheTag)->where('name', $name)->find();
  86. if ($config) $config = $config['value'];
  87. } else {
  88. if ($group) {
  89. $temp = configModel::cache('group' . $group, null, configModel::$cacheTag)->where('group', $group)->select()->toArray();
  90. } else {
  91. $temp = configModel::cache('sys_config_all', null, configModel::$cacheTag)->order('weigh desc')->select()->toArray();
  92. }
  93. if ($concise) {
  94. $config = [];
  95. foreach ($temp as $item) {
  96. $config[$item['name']] = $item['value'];
  97. }
  98. } else {
  99. $config = $temp;
  100. }
  101. }
  102. return $config;
  103. }
  104. }
  105. if (!function_exists('get_route_remark')) {
  106. /**
  107. * 获取当前路由后台菜单规则的备注信息
  108. * @return string
  109. */
  110. function get_route_remark(): string
  111. {
  112. $controllerName = request()->controller(true);
  113. $actionName = request()->action(true);
  114. $path = str_replace('.', '/', $controllerName);
  115. $remark = Db::name('admin_rule')
  116. ->where('name', $path)
  117. ->whereOr('name', $path . '/' . $actionName)
  118. ->value('remark');
  119. return __((string)$remark);
  120. }
  121. }
  122. if (!function_exists('full_url')) {
  123. /**
  124. * 获取资源完整url地址;若安装了云存储或 config/buildadmin.php 配置了CdnUrl,则自动使用对应的CdnUrl
  125. * @param string $relativeUrl 资源相对地址 不传入则获取域名
  126. * @param boolean $domain 是否携带域名 或者直接传入域名
  127. * @param string $default 默认值
  128. * @return string
  129. */
  130. function full_url(string $relativeUrl = '', bool $domain = true, string $default = ''): string
  131. {
  132. // 存储/上传资料配置
  133. Event::trigger('uploadConfigInit', App::getInstance());
  134. $cdnUrl = Config::get('buildadmin.cdn_url');
  135. if (!$cdnUrl) $cdnUrl = request()->upload['cdn'] ?? request()->domain();
  136. if ($domain === true) {
  137. $domain = $cdnUrl;
  138. } elseif ($domain === false) {
  139. $domain = '';
  140. }
  141. $relativeUrl = $relativeUrl ?: $default;
  142. if (!$relativeUrl) return $domain;
  143. $regex = "/^((?:[a-z]+:)?\/\/|data:image\/)(.*)/i";
  144. if (preg_match('/^http(s)?:\/\//', $relativeUrl) || preg_match($regex, $relativeUrl) || $domain === false) {
  145. return $relativeUrl;
  146. }
  147. return $domain . $relativeUrl;
  148. }
  149. }
  150. if (!function_exists('encrypt_password')) {
  151. /**
  152. * 加密密码
  153. */
  154. function encrypt_password($password, $salt = '', $encrypt = 'md5')
  155. {
  156. return $encrypt($encrypt($password) . $salt);
  157. }
  158. }
  159. if (!function_exists('str_attr_to_array')) {
  160. /**
  161. * 将字符串属性列表转为数组
  162. * @param string $attr 属性,一行一个,无需引号,比如:class=input-class
  163. * @return array
  164. */
  165. function str_attr_to_array(string $attr): array
  166. {
  167. if (!$attr) return [];
  168. $attr = explode("\n", trim(str_replace("\r\n", "\n", $attr)));
  169. $attrTemp = [];
  170. foreach ($attr as $item) {
  171. $item = explode('=', $item);
  172. if (isset($item[0]) && isset($item[1])) {
  173. $attrVal = $item[1];
  174. if ($item[1] === 'false' || $item[1] === 'true') {
  175. $attrVal = !($item[1] === 'false');
  176. } elseif (is_numeric($item[1])) {
  177. $attrVal = (float)$item[1];
  178. }
  179. if (strpos($item[0], '.')) {
  180. $attrKey = explode('.', $item[0]);
  181. if (isset($attrKey[0]) && isset($attrKey[1])) {
  182. $attrTemp[$attrKey[0]][$attrKey[1]] = $attrVal;
  183. continue;
  184. }
  185. }
  186. $attrTemp[$item[0]] = $attrVal;
  187. }
  188. }
  189. return $attrTemp;
  190. }
  191. }
  192. if (!function_exists('action_in_arr')) {
  193. /**
  194. * 检测一个方法是否在传递的数组内
  195. * @param array $arr
  196. * @return bool
  197. */
  198. function action_in_arr(array $arr = []): bool
  199. {
  200. $arr = is_array($arr) ? $arr : explode(',', $arr);
  201. if (!$arr) {
  202. return false;
  203. }
  204. $arr = array_map('strtolower', $arr);
  205. if (in_array(strtolower(request()->action()), $arr) || in_array('*', $arr)) {
  206. return true;
  207. }
  208. return false;
  209. }
  210. }
  211. if (!function_exists('build_suffix_svg')) {
  212. /**
  213. * 构建文件后缀的svg图片
  214. * @param string $suffix 文件后缀
  215. * @param ?string $background 背景颜色,如:rgb(255,255,255)
  216. * @return string
  217. */
  218. function build_suffix_svg(string $suffix = 'file', string $background = null): string
  219. {
  220. $suffix = mb_substr(strtoupper($suffix), 0, 4);
  221. $total = unpack('L', hash('adler32', $suffix, true))[1];
  222. $hue = $total % 360;
  223. [$r, $g, $b] = hsv2rgb($hue / 360, 0.3, 0.9);
  224. $background = $background ?: "rgb($r,$g,$b)";
  225. return '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
  226. <path style="fill:#E2E5E7;" d="M128,0c-17.6,0-32,14.4-32,32v448c0,17.6,14.4,32,32,32h320c17.6,0,32-14.4,32-32V128L352,0H128z"/>
  227. <path style="fill:#B0B7BD;" d="M384,128h96L352,0v96C352,113.6,366.4,128,384,128z"/>
  228. <polygon style="fill:#CAD1D8;" points="480,224 384,128 480,128 "/>
  229. <path style="fill:' . $background . ';" d="M416,416c0,8.8-7.2,16-16,16H48c-8.8,0-16-7.2-16-16V256c0-8.8,7.2-16,16-16h352c8.8,0,16,7.2,16,16 V416z"/>
  230. <path style="fill:#CAD1D8;" d="M400,432H96v16h304c8.8,0,16-7.2,16-16v-16C416,424.8,408.8,432,400,432z"/>
  231. <g><text><tspan x="220" y="380" font-size="124" font-family="Verdana, Helvetica, Arial, sans-serif" fill="white" text-anchor="middle">' . $suffix . '</tspan></text></g>
  232. </svg>';
  233. }
  234. }
  235. if (!function_exists('get_area')) {
  236. /**
  237. * 获取省份地区数据
  238. * @throws Throwable
  239. */
  240. function get_area(): array
  241. {
  242. $province = request()->get('province', '');
  243. $city = request()->get('city', '');
  244. $where = ['pid' => 0, 'level' => 1];
  245. if ($province !== '') {
  246. $where['pid'] = $province;
  247. $where['level'] = 2;
  248. if ($city !== '') {
  249. $where['pid'] = $city;
  250. $where['level'] = 3;
  251. }
  252. }
  253. return Db::name('area')
  254. ->where($where)
  255. ->field('id as value,name as label')
  256. ->select()
  257. ->toArray();
  258. }
  259. }
  260. if (!function_exists('hsv2rgb')) {
  261. function hsv2rgb($h, $s, $v): array
  262. {
  263. $r = $g = $b = 0;
  264. $i = floor($h * 6);
  265. $f = $h * 6 - $i;
  266. $p = $v * (1 - $s);
  267. $q = $v * (1 - $f * $s);
  268. $t = $v * (1 - (1 - $f) * $s);
  269. switch ($i % 6) {
  270. case 0:
  271. $r = $v;
  272. $g = $t;
  273. $b = $p;
  274. break;
  275. case 1:
  276. $r = $q;
  277. $g = $v;
  278. $b = $p;
  279. break;
  280. case 2:
  281. $r = $p;
  282. $g = $v;
  283. $b = $t;
  284. break;
  285. case 3:
  286. $r = $p;
  287. $g = $q;
  288. $b = $v;
  289. break;
  290. case 4:
  291. $r = $t;
  292. $g = $p;
  293. $b = $v;
  294. break;
  295. case 5:
  296. $r = $v;
  297. $g = $p;
  298. $b = $q;
  299. break;
  300. }
  301. return [
  302. floor($r * 255),
  303. floor($g * 255),
  304. floor($b * 255)
  305. ];
  306. }
  307. }
  308. if (!function_exists('ip_check')) {
  309. /**
  310. * IP检查
  311. * @throws Throwable
  312. */
  313. function ip_check($ip = null): void
  314. {
  315. $ip = is_null($ip) ? request()->ip() : $ip;
  316. $noAccess = get_sys_config('no_access_ip');
  317. $noAccess = !$noAccess ? [] : array_filter(explode("\n", str_replace("\r\n", "\n", $noAccess)));
  318. if ($noAccess && IpUtils::checkIp($ip, $noAccess)) {
  319. $response = Response::create(['msg' => 'No permission request'], 'json', 403);
  320. throw new HttpResponseException($response);
  321. }
  322. }
  323. }
  324. if (!function_exists('set_timezone')) {
  325. /**
  326. * 设置时区
  327. * @throws Throwable
  328. */
  329. function set_timezone($timezone = null): void
  330. {
  331. $defaultTimezone = Config::get('app.default_timezone');
  332. $timezone = is_null($timezone) ? get_sys_config('time_zone') : $timezone;
  333. if ($timezone && $defaultTimezone != $timezone) {
  334. Config::set([
  335. 'app.default_timezone' => $timezone
  336. ]);
  337. date_default_timezone_set($timezone);
  338. }
  339. }
  340. }
  341. if (!function_exists('get_upload_config')) {
  342. /**
  343. * 获取上传配置
  344. * @return array
  345. */
  346. function get_upload_config(): array
  347. {
  348. // 存储/上传资料配置
  349. Event::trigger('uploadConfigInit', App::getInstance());
  350. $uploadConfig = Config::get('upload');
  351. $uploadConfig['maxsize'] = Filesystem::fileUnitToByte($uploadConfig['maxsize']);
  352. $upload = request()->upload;
  353. if (!$upload) {
  354. $uploadConfig['mode'] = 'local';
  355. return $uploadConfig;
  356. }
  357. unset($upload['cdn']);
  358. return array_merge($upload, $uploadConfig);
  359. }
  360. }