common.php 77 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2013-2019 http://www.thinkcmf.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +---------------------------------------------------------------------
  9. // | Author: Dean <zxxjjforever@163.com>
  10. // +----------------------------------------------------------------------
  11. use think\Db;
  12. use think\facade\Env;
  13. use think\facade\Url;
  14. use dir\Dir;
  15. use think\facade\Route;
  16. use think\Loader;
  17. use cmf\lib\Storage;
  18. use think\facade\Hook;
  19. // 应用公共文件
  20. if (PHP_SAPI == 'cli') {
  21. $apps = cmf_scan_dir(APP_PATH . '*', GLOB_ONLYDIR);
  22. foreach ($apps as $app) {
  23. $commandFile = APP_PATH . $app . '/command.php';
  24. if (file_exists($commandFile)) {
  25. $commands = include $commandFile;
  26. // 注册命令行指令
  27. \think\Console::addDefaultCommands($commands);
  28. }
  29. }
  30. }
  31. /**
  32. * 获取当前登录的管理员ID
  33. * @return int
  34. */
  35. function cmf_get_current_admin_id()
  36. {
  37. return session('ADMIN_ID');
  38. }
  39. /**
  40. * 获取当前登录的商家ID
  41. * @return int
  42. */
  43. function cmf_get_current_shop_id()
  44. {
  45. return session('SHOP_ID');
  46. }
  47. /**
  48. * 判断前台用户是否登录
  49. * @return boolean
  50. */
  51. function cmf_is_user_login()
  52. {
  53. $sessionUser = session('user');
  54. return !empty($sessionUser);
  55. }
  56. /**
  57. * 获取当前登录的前台用户的信息,未登录时,返回false
  58. * @return array|boolean
  59. */
  60. function cmf_get_current_user()
  61. {
  62. $sessionUser = session('user');
  63. if (!empty($sessionUser)) {
  64. unset($sessionUser['user_pass']); // 销毁敏感数据
  65. return $sessionUser;
  66. } else {
  67. return false;
  68. }
  69. }
  70. /**
  71. * 更新当前登录前台用户的信息
  72. * @param array $user 前台用户的信息
  73. */
  74. function cmf_update_current_user($user)
  75. {
  76. session('user', $user);
  77. }
  78. /**
  79. * 获取当前登录前台用户id
  80. * @return int
  81. */
  82. function cmf_get_current_user_id()
  83. {
  84. $sessionUserId = session('user.id');
  85. if (empty($sessionUserId)) {
  86. return 0;
  87. }
  88. return $sessionUserId;
  89. }
  90. /**
  91. * 返回带协议的域名
  92. */
  93. function cmf_get_domain()
  94. {
  95. return request()->domain();
  96. }
  97. /**
  98. * 获取网站根目录
  99. * @return string 网站根目录
  100. */
  101. function cmf_get_root()
  102. {
  103. $root = request()->root();
  104. $root = str_replace("//", '/', $root);
  105. $root = str_replace('/index.php', '', $root);
  106. if (defined('APP_NAMESPACE') && APP_NAMESPACE == 'api') {
  107. $root = preg_replace('/\/api(.php)$/', '', $root);
  108. }
  109. $root = rtrim($root, '/');
  110. return $root;
  111. }
  112. /**
  113. * 获取当前主题名
  114. * @return string
  115. */
  116. function cmf_get_current_theme()
  117. {
  118. if (PHP_SAPI != 'cli') {
  119. static $_currentTheme;
  120. if (!empty($_currentTheme)) {
  121. return $_currentTheme;
  122. }
  123. }
  124. $t = 't';
  125. $theme = config('template.cmf_default_theme');
  126. $cmfDetectTheme = config('template.cmf_detect_theme');
  127. if ($cmfDetectTheme) {
  128. if (isset($_GET[$t])) {
  129. $theme = $_GET[$t];
  130. cookie('cmf_template', $theme, 864000);
  131. } elseif (cookie('cmf_template')) {
  132. $theme = cookie('cmf_template');
  133. }
  134. }
  135. $hookTheme = hook_one('switch_theme');
  136. if ($hookTheme) {
  137. $theme = $hookTheme;
  138. }
  139. $designT = '_design_theme';
  140. if (isset($_GET[$designT])) {
  141. $theme = $_GET[$designT];
  142. cookie('cmf_design_theme', $theme, 4);
  143. } elseif (cookie('cmf_design_theme')) {
  144. $theme = cookie('cmf_design_theme');
  145. }
  146. $_currentTheme = $theme;
  147. return $theme;
  148. }
  149. /**
  150. * 获取当前后台主题名
  151. * @return string
  152. */
  153. function cmf_get_current_admin_theme()
  154. {
  155. if (PHP_SAPI != 'cli') {
  156. static $_currentAdminTheme;
  157. if (!empty($_currentAdminTheme)) {
  158. return $_currentAdminTheme;
  159. }
  160. }
  161. $t = '_at';
  162. $theme = config('template.cmf_admin_default_theme');
  163. $cmfDetectTheme = true;
  164. if ($cmfDetectTheme) {
  165. if (isset($_GET[$t])) {
  166. $theme = $_GET[$t];
  167. cookie('cmf_admin_theme', $theme, 864000);
  168. } elseif (cookie('cmf_admin_theme')) {
  169. $theme = cookie('cmf_admin_theme');
  170. }
  171. }
  172. $hookTheme = hook_one('switch_admin_theme');
  173. if ($hookTheme) {
  174. $theme = $hookTheme;
  175. }
  176. $_currentAdminTheme = $theme;
  177. return $theme;
  178. }
  179. /**
  180. * 获取前台模板根目录
  181. * @param string $theme
  182. * @return string 前台模板根目录
  183. */
  184. function cmf_get_theme_path($theme = null)
  185. {
  186. $themePath = config('template.cmf_theme_path');
  187. if ($theme === null) {
  188. // 获取当前主题名称
  189. $theme = cmf_get_current_theme();
  190. }
  191. return WEB_ROOT . $themePath . $theme;
  192. }
  193. /**
  194. * 获取用户头像地址
  195. * @param $avatar 用户头像文件路径,相对于 upload 目录
  196. * @return string
  197. */
  198. function cmf_get_user_avatar_url($avatar)
  199. {
  200. if (!empty($avatar)) {
  201. if (strpos($avatar, "http") === 0) {
  202. return $avatar;
  203. } else {
  204. return cmf_get_image_url($avatar, 'avatar');
  205. }
  206. } else {
  207. return $avatar;
  208. }
  209. }
  210. /**
  211. * CMF密码加密方法
  212. * @param string $pw 要加密的原始密码
  213. * @param string $authCode 加密字符串
  214. * @return string
  215. */
  216. function cmf_password($pw, $authCode = '')
  217. {
  218. if (empty($authCode)) {
  219. $authCode = config('database.authcode');
  220. }
  221. $result = "###" . md5(md5($authCode . $pw));
  222. return $result;
  223. }
  224. /**
  225. * CMF密码加密方法 (X2.0.0以前的方法)
  226. * @param string $pw 要加密的原始密码
  227. * @return string
  228. */
  229. function cmf_password_old($pw)
  230. {
  231. $decor = md5(config('database.prefix'));
  232. $mi = md5($pw);
  233. return substr($decor, 0, 12) . $mi . substr($decor, -4, 4);
  234. }
  235. /**
  236. * CMF密码比较方法,所有涉及密码比较的地方都用这个方法
  237. * @param string $password 要比较的密码
  238. * @param string $passwordInDb 数据库保存的已经加密过的密码
  239. * @return boolean 密码相同,返回true
  240. */
  241. function cmf_compare_password($password, $passwordInDb)
  242. {
  243. if (strpos($passwordInDb, "###") === 0) {
  244. return cmf_password($password) == $passwordInDb;
  245. } else {
  246. return cmf_password_old($password) == $passwordInDb;
  247. }
  248. }
  249. /**
  250. * 文件日志
  251. * @param $content 要写入的内容
  252. * @param string $file 日志文件,在web 入口目录
  253. */
  254. function cmf_log($content, $file = "log.txt")
  255. {
  256. file_put_contents($file, $content, FILE_APPEND);
  257. }
  258. /**
  259. * 随机字符串生成
  260. * @param int $len 生成的字符串长度
  261. * @return string
  262. */
  263. function cmf_random_string($len = 6)
  264. {
  265. $chars = [
  266. "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
  267. "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
  268. "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G",
  269. "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
  270. "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2",
  271. "3", "4", "5", "6", "7", "8", "9"
  272. ];
  273. $charsLen = count($chars) - 1;
  274. shuffle($chars); // 将数组打乱
  275. $output = "";
  276. for ($i = 0; $i < $len; $i++) {
  277. $output .= $chars[mt_rand(0, $charsLen)];
  278. }
  279. return $output;
  280. }
  281. /**
  282. * 清空系统缓存
  283. */
  284. function cmf_clear_cache()
  285. {
  286. // 清除 opcache缓存
  287. if (function_exists("opcache_reset")) {
  288. opcache_reset();
  289. }
  290. $dirs = [];
  291. \app\weixin\service\PRedis::delByKeys('cache:*');
  292. $rootDirs = cmf_scan_dir(Env::get('runtime_path') . "*");
  293. //$noNeedClear=array(".","..","Data");
  294. $noNeedClear = ['.', '..', 'log'];
  295. $rootDirs = array_diff($rootDirs, $noNeedClear);
  296. foreach ($rootDirs as $dir) {
  297. if ($dir != "." && $dir != "..") {
  298. $dir = Env::get('runtime_path') . $dir;
  299. if (is_dir($dir)) {
  300. //array_push ( $dirs, $dir );
  301. $tmpRootDirs = cmf_scan_dir($dir . "/*");
  302. foreach ($tmpRootDirs as $tDir) {
  303. if ($tDir != "." && $tDir != "..") {
  304. $tDir = $dir . '/' . $tDir;
  305. if (is_dir($tDir)) {
  306. array_push($dirs, $tDir);
  307. } else {
  308. // @unlink($tDir);
  309. }
  310. }
  311. }
  312. } else {
  313. // @unlink($dir);
  314. }
  315. }
  316. }
  317. $dirTool = new Dir("");
  318. foreach ($dirs as $dir) {
  319. $dirTool->delDir($dir);
  320. }
  321. }
  322. /**
  323. * 保存数组变量到php文件
  324. * @param string $path 保存路径
  325. * @param mixed $var 要保存的变量
  326. * @return boolean 保存成功返回true,否则false
  327. */
  328. function cmf_save_var($path, $var)
  329. {
  330. $result = file_put_contents($path, "<?php\treturn " . var_export($var, true) . ";?>");
  331. return $result;
  332. }
  333. /**
  334. * 设置动态配置
  335. * @param array $data <br>如:['template' => ['cmf_default_theme' => 'default']];
  336. * @return boolean
  337. */
  338. function cmf_set_dynamic_config($data)
  339. {
  340. if (!is_array($data)) {
  341. return false;
  342. }
  343. foreach ($data as $key => $value) {
  344. if (is_array($value)) {
  345. $configFile = CMF_DATA . "config/{$key}.php";
  346. if (file_exists($configFile)) {
  347. $configs = include $configFile;
  348. } else {
  349. $configs = [];
  350. }
  351. $configs = array_merge($configs, $value);
  352. try {
  353. file_put_contents($configFile, "<?php\treturn " . var_export($configs, true) . ";");
  354. } catch (\Exception $e) {
  355. return false;
  356. }
  357. }
  358. }
  359. cmf_clear_cache();
  360. return true;
  361. }
  362. /**
  363. * 转化格式化的字符串为数组
  364. * @param string $tag 要转化的字符串,格式如:"id:2;cid:1;order:post_date desc;"
  365. * @return array 转化后字符串<pre>
  366. * array(
  367. * 'id'=>'2',
  368. * 'cid'=>'1',
  369. * 'order'=>'post_date desc'
  370. * )
  371. */
  372. function cmf_param_lable($tag = '')
  373. {
  374. $param = [];
  375. $array = explode(';', $tag);
  376. foreach ($array as $v) {
  377. $v = trim($v);
  378. if (!empty($v)) {
  379. list($key, $val) = explode(':', $v);
  380. $param[trim($key)] = trim($val);
  381. }
  382. }
  383. return $param;
  384. }
  385. /**
  386. * 获取后台管理设置的网站信息,此类信息一般用于前台
  387. * @return array
  388. */
  389. function cmf_get_site_info()
  390. {
  391. $siteInfo = cmf_get_option('site_info');
  392. if (isset($siteInfo['site_analytics'])) {
  393. $siteInfo['site_analytics'] = htmlspecialchars_decode($siteInfo['site_analytics']);
  394. }
  395. return $siteInfo;
  396. }
  397. /**
  398. * 获取CMF系统的设置,此类设置用于全局
  399. * @return array
  400. */
  401. function cmf_get_cmf_setting()
  402. {
  403. return cmf_get_option('cmf_setting');
  404. }
  405. /**
  406. * 更新CMF系统的设置,此类设置用于全局
  407. * @param $data
  408. * @return bool
  409. * @throws \think\Exception
  410. * @throws \think\db\exception\DataNotFoundException
  411. * @throws \think\db\exception\ModelNotFoundException
  412. * @throws \think\exception\DbException
  413. * @throws \think\exception\PDOException
  414. */
  415. function cmf_set_cmf_setting($data)
  416. {
  417. if (!is_array($data) || empty($data)) {
  418. return false;
  419. }
  420. return cmf_set_option('cmf_setting', $data);
  421. }
  422. /**
  423. * 设置系统配置,通用
  424. * @param string $key 配置键值,都小写
  425. * @param array $data 配置值,数组
  426. * @param bool $replace 是否完全替换
  427. * @return bool 是否成功
  428. * @throws \think\Exception
  429. * @throws \think\db\exception\DataNotFoundException
  430. * @throws \think\db\exception\ModelNotFoundException
  431. * @throws \think\exception\DbException
  432. * @throws \think\exception\PDOException
  433. */
  434. function cmf_set_option($key, $data, $replace = false)
  435. {
  436. if (!is_array($data) || empty($data) || !is_string($key) || empty($key)) {
  437. return false;
  438. }
  439. $key = strtolower($key);
  440. $option = [];
  441. $findOption = Db::name('option')->where('option_name', $key)->find();
  442. if ($findOption) {
  443. if (!$replace) {
  444. $oldOptionValue = json_decode($findOption['option_value'], true);
  445. if (!empty($oldOptionValue)) {
  446. $data = array_merge($oldOptionValue, $data);
  447. }
  448. }
  449. $option['option_value'] = json_encode($data);
  450. Db::name('option')->where('option_name', $key)->update($option);
  451. // echo Db::name('option')->getLastSql() . "\n";
  452. } else {
  453. $option['option_name'] = $key;
  454. $option['option_value'] = json_encode($data);
  455. Db::name('option')->insert($option);
  456. }
  457. cache('cmf_options_' . $key, null);//删除缓存
  458. return true;
  459. }
  460. /**
  461. * 获取系统配置,通用
  462. * @param string $key 配置键值,都小写
  463. * @return array
  464. */
  465. function cmf_get_option($key)
  466. {
  467. if (!is_string($key) || empty($key)) {
  468. return [];
  469. }
  470. if (PHP_SAPI != 'cli') {
  471. static $cmfGetOption;
  472. if (empty($cmfGetOption)) {
  473. $cmfGetOption = [];
  474. } else {
  475. if (!empty($cmfGetOption[$key])) {
  476. return $cmfGetOption[$key];
  477. }
  478. }
  479. }
  480. $optionValue = cache('cmf_options_' . $key);
  481. if (empty($optionValue)) {
  482. $optionValue = Db::name('option')->where('option_name', $key)->value('option_value');
  483. if (!empty($optionValue)) {
  484. $optionValue = json_decode($optionValue, true);
  485. cache('cmf_options_' . $key, $optionValue);
  486. }
  487. }
  488. $cmfGetOption[$key] = $optionValue;
  489. return $optionValue;
  490. }
  491. /**
  492. * 获取CMF上传配置
  493. */
  494. function cmf_get_upload_setting()
  495. {
  496. $uploadSetting = cmf_get_option('upload_setting');
  497. if (empty($uploadSetting) || empty($uploadSetting['file_types'])) {
  498. $uploadSetting = [
  499. 'file_types' => [
  500. 'image' => [
  501. 'upload_max_filesize' => '10240',//单位KB
  502. 'extensions' => 'jpg,jpeg,png,gif,bmp4'
  503. ],
  504. 'video' => [
  505. 'upload_max_filesize' => '10240',
  506. 'extensions' => 'mp4,avi,wmv,rm,rmvb,mkv'
  507. ],
  508. 'audio' => [
  509. 'upload_max_filesize' => '10240',
  510. 'extensions' => 'mp3,wma,wav'
  511. ],
  512. 'file' => [
  513. 'upload_max_filesize' => '10240',
  514. 'extensions' => 'txt,pdf,doc,docx,xls,xlsx,ppt,pptx,zip,rar'
  515. ]
  516. ],
  517. 'chunk_size' => 512,//单位KB
  518. 'max_files' => 20 //最大同时上传文件数
  519. ];
  520. }
  521. if (empty($uploadSetting['upload_max_filesize'])) {
  522. $uploadMaxFileSizeSetting = [];
  523. foreach ($uploadSetting['file_types'] as $setting) {
  524. $extensions = explode(',', trim($setting['extensions']));
  525. if (!empty($extensions)) {
  526. $uploadMaxFileSize = intval($setting['upload_max_filesize']) * 1024;//转化成B
  527. foreach ($extensions as $ext) {
  528. if (!isset($uploadMaxFileSizeSetting[$ext]) || $uploadMaxFileSize > $uploadMaxFileSizeSetting[$ext]) {
  529. $uploadMaxFileSizeSetting[$ext] = $uploadMaxFileSize;
  530. }
  531. }
  532. }
  533. }
  534. $uploadSetting['upload_max_filesize'] = $uploadMaxFileSizeSetting;
  535. }
  536. return $uploadSetting;
  537. }
  538. /**
  539. * 获取html文本里的img
  540. * @param string $content html 内容
  541. * @return array 图片列表 数组item格式<pre>
  542. * [
  543. * "src"=>'图片链接',
  544. * "title"=>'图片标签的 title 属性',
  545. * "alt"=>'图片标签的 alt 属性'
  546. * ]
  547. * </pre>
  548. */
  549. function cmf_get_content_images($content)
  550. {
  551. //import('phpQuery.phpQuery', EXTEND_PATH);
  552. \phpQuery::newDocumentHTML($content);
  553. $pq = pq(null);
  554. $images = $pq->find("img");
  555. $imagesData = [];
  556. if ($images->length) {
  557. foreach ($images as $img) {
  558. $img = pq($img);
  559. $image = [];
  560. $image['src'] = $img->attr("src");
  561. $image['title'] = $img->attr("title");
  562. $image['alt'] = $img->attr("alt");
  563. array_push($imagesData, $image);
  564. }
  565. }
  566. \phpQuery::$documents = null;
  567. return $imagesData;
  568. }
  569. /**
  570. * 去除字符串中的指定字符
  571. * @param string $str 待处理字符串
  572. * @param string $chars 需去掉的特殊字符
  573. * @return string
  574. */
  575. function cmf_strip_chars($str, $chars = '?<*.>\'\"')
  576. {
  577. return preg_replace('/[' . $chars . ']/is', '', $str);
  578. }
  579. /**
  580. * 发送邮件
  581. * @param string $address 收件人邮箱
  582. * @param string $subject 邮件标题
  583. * @param string $message 邮件内容
  584. * @return array<br>
  585. * 返回格式:<br>
  586. * array(<br>
  587. * "error"=>0|1,//0代表出错<br>
  588. * "message"=> "出错信息"<br>
  589. * );
  590. * @throws phpmailerException
  591. */
  592. function cmf_send_email($address, $subject, $message)
  593. {
  594. $smtpSetting = cmf_get_option('smtp_setting');
  595. $mail = new \PHPMailer\PHPMailer\PHPMailer();
  596. // 设置PHPMailer使用SMTP服务器发送Email
  597. $mail->IsSMTP();
  598. $mail->IsHTML(true);
  599. //$mail->SMTPDebug = 3;
  600. // 设置邮件的字符编码,若不指定,则为'UTF-8'
  601. $mail->CharSet = 'UTF-8';
  602. // 添加收件人地址,可以多次使用来添加多个收件人
  603. $mail->AddAddress($address);
  604. // 设置邮件正文
  605. $mail->Body = $message;
  606. // 设置邮件头的From字段。
  607. $mail->From = $smtpSetting['from'];
  608. // 设置发件人名字
  609. $mail->FromName = $smtpSetting['from_name'];
  610. // 设置邮件标题
  611. $mail->Subject = $subject;
  612. // 设置SMTP服务器。
  613. $mail->Host = $smtpSetting['host'];
  614. //by Rainfer
  615. // 设置SMTPSecure。
  616. $Secure = $smtpSetting['smtp_secure'];
  617. $mail->SMTPSecure = empty($Secure) ? '' : $Secure;
  618. // 设置SMTP服务器端口。
  619. $port = $smtpSetting['port'];
  620. $mail->Port = empty($port) ? "25" : $port;
  621. // 设置为"需要验证"
  622. $mail->SMTPAuth = true;
  623. $mail->SMTPAutoTLS = false;
  624. $mail->Timeout = 10;
  625. // 设置用户名和密码。
  626. $mail->Username = $smtpSetting['username'];
  627. $mail->Password = $smtpSetting['password'];
  628. // 发送邮件。
  629. if (!$mail->Send()) {
  630. $mailError = $mail->ErrorInfo;
  631. return ["error" => 1, "message" => $mailError];
  632. } else {
  633. return ["error" => 0, "message" => "success"];
  634. }
  635. }
  636. /**
  637. * 转化数据库保存的文件路径,为可以访问的url
  638. * @param string $file
  639. * @param mixed $style 图片样式,支持各大云存储
  640. * @return string
  641. */
  642. function cmf_get_asset_url($file, $style = '')
  643. {
  644. if (strpos($file, "http") === 0) {
  645. return $file;
  646. } else if (strpos($file, "/") === 0) {
  647. return $file;
  648. } else {
  649. // $storage = cmf_get_option('storage');
  650. // if (empty($storage['type'])) {
  651. // $storage['type'] = 'Local';
  652. // }
  653. // if ($storage['type'] != 'Local') {
  654. // $watermark = cmf_get_plugin_config($storage['type']);
  655. // $style = empty($style) ? $watermark['styles_watermark'] : $style;
  656. // }
  657. $storage = Storage::instance();
  658. return $storage->getUrl($file, $style);
  659. }
  660. }
  661. /**
  662. * 转化数据库保存图片的文件路径,为可以访问的url
  663. * @param string $file 文件路径,数据存储的文件相对路径
  664. * @param string $style 图片样式,支持各大云存储
  665. * @return string 图片链接
  666. */
  667. function cmf_get_image_url($file, $style = 'watermark')
  668. {
  669. if (empty($file)) {
  670. return '';
  671. }
  672. if (strpos($file, "http") === 0) {
  673. return $file;
  674. } else if (strpos($file, "/") === 0) {
  675. return cmf_get_domain() . $file;
  676. } else {
  677. // $storage = cmf_get_option('storage');
  678. // if (empty($storage['type'])) {
  679. // $storage['type'] = 'Local';
  680. // }
  681. // if ($storage['type'] != 'Local') {
  682. // $watermark = cmf_get_plugin_config($storage['type']);
  683. // $style = empty($style) ? $watermark['styles_watermark'] : $style;
  684. // }
  685. $storage = Storage::instance();
  686. return $storage->getImageUrl($file, $style);
  687. }
  688. }
  689. /**
  690. * 获取图片预览链接
  691. * @param string $file 文件路径,相对于upload
  692. * @param string $style 图片样式,支持各大云存储
  693. * @return string
  694. */
  695. function cmf_get_image_preview_url($file, $style = 'watermark')
  696. {
  697. if (empty($file)) {
  698. return '';
  699. }
  700. if (strpos($file, "http") === 0) {
  701. return $file;
  702. } else if (strpos($file, "/") === 0) {
  703. return $file;
  704. } else {
  705. // $storage = cmf_get_option('storage');
  706. // if (empty($storage['type'])) {
  707. // $storage['type'] = 'Local';
  708. // }
  709. // if ($storage['type'] != 'Local') {
  710. // $watermark = cmf_get_plugin_config($storage['type']);
  711. // $style = empty($style) ? $watermark['styles_watermark'] : $style;
  712. // }
  713. $storage = Storage::instance();
  714. return $storage->getPreviewUrl($file, $style);
  715. }
  716. }
  717. /**
  718. * 获取文件下载链接
  719. * @param string $file 文件路径,数据库里保存的相对路径
  720. * @param int $expires 过期时间,单位 s
  721. * @return string 文件链接
  722. */
  723. function cmf_get_file_download_url($file, $expires = 3600)
  724. {
  725. if (empty($file)) {
  726. return '';
  727. }
  728. if (strpos($file, "http") === 0) {
  729. return $file;
  730. } else if (strpos($file, "/") === 0) {
  731. return $file;
  732. } else {
  733. $storage = Storage::instance();
  734. return $storage->getFileDownloadUrl($file, $expires);
  735. }
  736. }
  737. /**
  738. * 解密用cmf_str_encode加密的字符串
  739. * @param $string 要解密的字符串
  740. * @param string $key 加密时salt
  741. * @param int $expiry 多少秒后过期
  742. * @param string $operation 操作,默认为DECODE
  743. * @return bool|string
  744. */
  745. function cmf_str_decode($string, $key = '', $expiry = 0, $operation = 'DECODE')
  746. {
  747. $ckey_length = 4;
  748. $key = md5($key ? $key : config("database.authcode"));
  749. $keya = md5(substr($key, 0, 16));
  750. $keyb = md5(substr($key, 16, 16));
  751. $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';
  752. $cryptkey = $keya . md5($keya . $keyc);
  753. $key_length = strlen($cryptkey);
  754. $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
  755. $string_length = strlen($string);
  756. $result = '';
  757. $box = range(0, 255);
  758. $rndkey = [];
  759. for ($i = 0; $i <= 255; $i++) {
  760. $rndkey[$i] = ord($cryptkey[$i % $key_length]);
  761. }
  762. for ($j = $i = 0; $i < 256; $i++) {
  763. $j = ($j + $box[$i] + $rndkey[$i]) % 256;
  764. $tmp = $box[$i];
  765. $box[$i] = $box[$j];
  766. $box[$j] = $tmp;
  767. }
  768. for ($a = $j = $i = 0; $i < $string_length; $i++) {
  769. $a = ($a + 1) % 256;
  770. $j = ($j + $box[$a]) % 256;
  771. $tmp = $box[$a];
  772. $box[$a] = $box[$j];
  773. $box[$j] = $tmp;
  774. $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
  775. }
  776. if ($operation == 'DECODE') {
  777. if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
  778. return substr($result, 26);
  779. } else {
  780. return '';
  781. }
  782. } else {
  783. return $keyc . str_replace('=', '', base64_encode($result));
  784. }
  785. }
  786. /**
  787. * 加密字符串
  788. * @param $string 要加密的字符串
  789. * @param string $key salt
  790. * @param int $expiry 多少秒后过期
  791. * @return bool|string
  792. */
  793. function cmf_str_encode($string, $key = '', $expiry = 0)
  794. {
  795. return cmf_str_decode($string, $key, $expiry, "ENCODE");
  796. }
  797. /**
  798. * 获取文件相对路径
  799. * @param string $assetUrl 文件的URL
  800. * @return string
  801. */
  802. function cmf_asset_relative_url($assetUrl)
  803. {
  804. if (strpos($assetUrl, "http") === 0) {
  805. return $assetUrl;
  806. } else {
  807. return str_replace('/upload/', '', $assetUrl);
  808. }
  809. }
  810. /**
  811. * 检查用户对某个url内容的可访问性,用于记录如是否赞过,是否访问过等等;开发者可以自由控制,对于没有必要做的检查可以不做,以减少服务器压力
  812. * @param string $object 访问对象的id,格式:不带前缀的表名+id;如post1表示xx_post表里id为1的记录;如果object为空,表示只检查对某个url访问的合法性
  813. * @param int $countLimit 访问次数限制,如1,表示只能访问一次
  814. * @param boolean $ipLimit ip限制,false为不限制,true为限制
  815. * @param int $expire 距离上次访问的最小时间单位s,0表示不限制,大于0表示最后访问$expire秒后才可以访问
  816. * @return true 可访问,false不可访问
  817. * @throws \think\Exception
  818. * @throws \think\db\exception\DataNotFoundException
  819. * @throws \think\db\exception\ModelNotFoundException
  820. * @throws \think\exception\DbException
  821. * @throws \think\exception\PDOException
  822. */
  823. function cmf_check_user_action($object = "", $countLimit = 1, $ipLimit = false, $expire = 0)
  824. {
  825. $request = request();
  826. $action = $request->module() . "/" . $request->controller() . "/" . $request->action();
  827. if (is_array($object)) {
  828. $userId = $object['user_id'];
  829. $object = $object['object'];
  830. } else {
  831. $userId = cmf_get_current_user_id();
  832. }
  833. $ip = get_client_ip(0, true);//修复ip获取
  834. $where = ["user_id" => $userId, "action" => $action, "object" => $object];
  835. if ($ipLimit) {
  836. $where['ip'] = $ip;
  837. }
  838. $findLog = Db::name('user_action_log')->where($where)->find();
  839. $time = time();
  840. if ($findLog) {
  841. Db::name('user_action_log')->where($where)->update([
  842. "count" => Db::raw("count+1"),
  843. "last_visit_time" => $time,
  844. "ip" => $ip
  845. ]);
  846. if ($findLog['count'] >= $countLimit) {
  847. return false;
  848. }
  849. if ($expire > 0 && ($time - $findLog['last_visit_time']) < $expire) {
  850. return false;
  851. }
  852. } else {
  853. Db::name('user_action_log')->insert([
  854. "user_id" => $userId,
  855. "action" => $action,
  856. "object" => $object,
  857. "count" => Db::raw("count+1"),
  858. "last_visit_time" => $time, "ip" => $ip
  859. ]);
  860. }
  861. return true;
  862. }
  863. /**
  864. * 判断是否为手机访问
  865. * @return boolean
  866. */
  867. function cmf_is_mobile()
  868. {
  869. if (PHP_SAPI != 'cli') {
  870. static $cmf_is_mobile;
  871. if (isset($cmf_is_mobile))
  872. return $cmf_is_mobile;
  873. }
  874. $cmf_is_mobile = request()->isMobile();
  875. return $cmf_is_mobile;
  876. }
  877. /**
  878. * 判断是否为微信访问
  879. * @return boolean
  880. */
  881. function cmf_is_wechat()
  882. {
  883. if (strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger') !== false) {
  884. return true;
  885. }
  886. return false;
  887. }
  888. /**
  889. * 判断是否为Android访问
  890. * @return boolean
  891. */
  892. function cmf_is_android()
  893. {
  894. if (strpos($_SERVER['HTTP_USER_AGENT'], 'Android') !== false) {
  895. return true;
  896. }
  897. return false;
  898. }
  899. /**
  900. * 判断是否为ios访问
  901. * @return boolean
  902. */
  903. function cmf_is_ios()
  904. {
  905. if (strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone') || strpos($_SERVER['HTTP_USER_AGENT'], 'iPad')) {
  906. {
  907. return true;
  908. }
  909. return false;
  910. }
  911. }
  912. /**
  913. * 判断是否为iPhone访问
  914. * @return boolean
  915. */
  916. function cmf_is_iphone()
  917. {
  918. if (strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone')) {
  919. {
  920. return true;
  921. }
  922. return false;
  923. }
  924. }
  925. /**
  926. * 判断是否为iPad访问
  927. * @return boolean
  928. */
  929. function cmf_is_ipad()
  930. {
  931. if (strpos($_SERVER['HTTP_USER_AGENT'], 'iPad')) {
  932. {
  933. return true;
  934. }
  935. return false;
  936. }
  937. }
  938. /**
  939. * 添加钩子
  940. * @param string $hook 钩子名称
  941. * @param mixed $params 传入参数
  942. * @return void
  943. */
  944. function hook($hook, $params = null)
  945. {
  946. return Hook::listen($hook, $params);
  947. }
  948. /**
  949. * 添加钩子,只执行一个
  950. * @param string $hook 钩子名称
  951. * @param mixed $params 传入参数
  952. * @return mixed
  953. */
  954. function hook_one($hook, $params = null)
  955. {
  956. return Hook::listen($hook, $params, true);
  957. }
  958. /**
  959. * 获取插件类名
  960. * @param string $name 插件名
  961. * @return string
  962. */
  963. function cmf_get_plugin_class($name)
  964. {
  965. $name = ucwords($name);
  966. $pluginDir = cmf_parse_name($name);
  967. $class = "plugins\\{$pluginDir}\\{$name}Plugin";
  968. return $class;
  969. }
  970. /**
  971. * 获取插件配置
  972. * @param string $name 插件名,大驼峰格式
  973. * @return array
  974. */
  975. function cmf_get_plugin_config($name)
  976. {
  977. $class = cmf_get_plugin_class($name);
  978. if (class_exists($class)) {
  979. $plugin = new $class();
  980. return $plugin->getConfig();
  981. } else {
  982. return [];
  983. }
  984. }
  985. /**
  986. * 替代scan_dir的方法
  987. * @param string $pattern 检索模式 搜索模式 *.txt,*.doc; (同glog方法)
  988. * @param int $flags
  989. * @param $pattern
  990. * @return array
  991. */
  992. function cmf_scan_dir($pattern, $flags = null)
  993. {
  994. $files = glob($pattern, $flags);
  995. if (empty($files)) {
  996. $files = [];
  997. } else {
  998. $files = array_map('basename', $files);
  999. }
  1000. return $files;
  1001. }
  1002. /**
  1003. * 获取某个目录下所有子目录
  1004. * @param $dir
  1005. * @return array
  1006. */
  1007. function cmf_sub_dirs($dir)
  1008. {
  1009. $dir = ltrim($dir, "/");
  1010. $dirs = [];
  1011. $subDirs = cmf_scan_dir("$dir/*", GLOB_ONLYDIR);
  1012. if (!empty($subDirs)) {
  1013. foreach ($subDirs as $subDir) {
  1014. $subDir = "$dir/$subDir";
  1015. array_push($dirs, $subDir);
  1016. $subDirSubDirs = cmf_sub_dirs($subDir);
  1017. if (!empty($subDirSubDirs)) {
  1018. $dirs = array_merge($dirs, $subDirSubDirs);
  1019. }
  1020. }
  1021. }
  1022. return $dirs;
  1023. }
  1024. /**
  1025. * 生成访问插件的url
  1026. * @param string $url url格式:插件名://控制器名/方法
  1027. * @param array $vars 参数
  1028. * @param bool $domain 是否显示域名 或者直接传入域名
  1029. * @return string
  1030. */
  1031. function cmf_plugin_url($url, $vars = [], $domain = false)
  1032. {
  1033. global $CMF_GV_routes;
  1034. if (empty($CMF_GV_routes)) {
  1035. $routeModel = new \app\admin\model\RouteModel();
  1036. $CMF_GV_routes = $routeModel->getRoutes();
  1037. }
  1038. $url = parse_url($url);
  1039. $case_insensitive = true;
  1040. $plugin = $case_insensitive ? Loader::parseName($url['scheme']) : $url['scheme'];
  1041. $controller = $case_insensitive ? Loader::parseName($url['host']) : $url['host'];
  1042. $action = trim($case_insensitive ? strtolower($url['path']) : $url['path'], '/');
  1043. /* 解析URL带的参数 */
  1044. if (isset($url['query'])) {
  1045. parse_str($url['query'], $query);
  1046. $vars = array_merge($query, $vars);
  1047. }
  1048. /* 基础参数 */
  1049. $params = [
  1050. '_plugin' => $plugin,
  1051. '_controller' => $controller,
  1052. '_action' => $action,
  1053. ];
  1054. $pluginUrl = '\\cmf\\controller\\PluginController@index?' . http_build_query($params);
  1055. if (!empty($vars) && !empty($CMF_GV_routes[$pluginUrl])) {
  1056. foreach ($CMF_GV_routes[$pluginUrl] as $actionRoute) {
  1057. $sameVars = array_intersect_assoc($vars, $actionRoute['vars']);
  1058. if (count($sameVars) == count($actionRoute['vars'])) {
  1059. ksort($sameVars);
  1060. $pluginUrl = $pluginUrl . '&' . http_build_query($sameVars);
  1061. $vars = array_diff_assoc($vars, $sameVars);
  1062. break;
  1063. }
  1064. }
  1065. }
  1066. return url($pluginUrl, $vars, true, $domain);
  1067. }
  1068. /**
  1069. * 检查权限
  1070. * @param $userId int 要检查权限的用户 ID
  1071. * @param $name string|array 需要验证的规则列表,支持逗号分隔的权限规则或索引数组
  1072. * @param $relation string 如果为 'or' 表示满足任一条规则即通过验证;如果为 'and'则表示需满足所有规则才能通过验证
  1073. * @return boolean 通过验证返回true;失败返回false
  1074. */
  1075. function cmf_auth_check($userId, $name = null, $relation = 'or', $checkStatus=true)
  1076. {
  1077. if (empty($userId)) {
  1078. return false;
  1079. }
  1080. if ($userId == 1) {
  1081. return true;
  1082. }
  1083. $authObj = new \cmf\lib\Auth();
  1084. if (empty($name)) {
  1085. $request = request();
  1086. $module = $request->module();
  1087. $controller = $request->controller();
  1088. $action = $request->action();
  1089. $name = strtolower($module . "/" . $controller . "/" . $action);
  1090. }
  1091. return $authObj->check($userId, $name, $relation,$checkStatus);
  1092. }
  1093. function cmf_alpha_id($in, $to_num = false, $pad_up = 4, $passKey = null)
  1094. {
  1095. $index = "aBcDeFgHiJkLmNoPqRsTuVwXyZAbCdEfGhIjKlMnOpQrStUvWxYz0123456789";
  1096. if ($passKey !== null) {
  1097. // Although this function's purpose is to just make the
  1098. // ID short - and not so much secure,
  1099. // with this patch by Simon Franz (http://blog.snaky.org/)
  1100. // you can optionally supply a password to make it harder
  1101. // to calculate the corresponding numeric ID
  1102. for ($n = 0; $n < strlen($index); $n++) $i[] = substr($index, $n, 1);
  1103. $passhash = hash('sha256', $passKey);
  1104. $passhash = (strlen($passhash) < strlen($index)) ? hash('sha512', $passKey) : $passhash;
  1105. for ($n = 0; $n < strlen($index); $n++) $p[] = substr($passhash, $n, 1);
  1106. array_multisort($p, SORT_DESC, $i);
  1107. $index = implode($i);
  1108. }
  1109. $base = strlen($index);
  1110. if ($to_num) {
  1111. // Digital number <<-- alphabet letter code
  1112. $in = strrev($in);
  1113. $out = 0;
  1114. $len = strlen($in) - 1;
  1115. for ($t = 0; $t <= $len; $t++) {
  1116. $bcpow = pow($base, $len - $t);
  1117. $out = $out + strpos($index, substr($in, $t, 1)) * $bcpow;
  1118. }
  1119. if (is_numeric($pad_up)) {
  1120. $pad_up--;
  1121. if ($pad_up > 0) $out -= pow($base, $pad_up);
  1122. }
  1123. $out = sprintf('%F', $out);
  1124. $out = substr($out, 0, strpos($out, '.'));
  1125. } else {
  1126. // Digital number -->> alphabet letter code
  1127. if (is_numeric($pad_up)) {
  1128. $pad_up--;
  1129. if ($pad_up > 0) $in += pow($base, $pad_up);
  1130. }
  1131. $out = "";
  1132. for ($t = floor(log($in, $base)); $t >= 0; $t--) {
  1133. $bcp = pow($base, $t);
  1134. $a = floor($in / $bcp) % $base;
  1135. $out = $out . substr($index, $a, 1);
  1136. $in = $in - ($a * $bcp);
  1137. }
  1138. $out = strrev($out); // reverse
  1139. }
  1140. return $out;
  1141. }
  1142. /**
  1143. * 金钱格式化小数点
  1144. * @param $money
  1145. * @param int $decimal 小数点位数,默认系统配置2位
  1146. * @return float
  1147. */
  1148. function moneyFormat($money, $decimal=null){
  1149. $formatConfig = [];
  1150. $charset = !empty($formatConfig['charset'])? trim($formatConfig['charset']) : 'utf-8';
  1151. if($decimal === null){
  1152. $decimal = isset($formatConfig['moneyDecimal'])? intval($formatConfig['moneyDecimal']): 2;
  1153. }
  1154. $money = round($money, $decimal+1);
  1155. $data = explode('.', $money);
  1156. $money = isset($data[0])? $data[0] : 0;
  1157. $float = isset($data[1])? $data[1] : '';
  1158. $len = $float? mb_strlen($float, $charset) : 0;
  1159. $decimal = $decimal>=0? intval($decimal) : 2;
  1160. $num1 = isset($data[1])? $data[1] : '';
  1161. $float = $len>=$decimal? mb_substr($num1, 0, $decimal, $charset) : $float.str_repeat('0', $decimal-$len);
  1162. $money = $money.'.'.$float;
  1163. return number_format($money, $decimal, '.', '');
  1164. }
  1165. /**
  1166. * 返回JSON数据
  1167. * @param $params 状态码:success-成功,error-失败或返回所有参数数组如:['code'=>'success','messgae'=>'message','data'=>[]]
  1168. * @param string $message 错误消息
  1169. * @param array $data 返回数据
  1170. */
  1171. function showJson($params, $message='', $data=null, $end=''){
  1172. if($params && is_array($params)){
  1173. $message = isset($params['message'])? trim($params['message']) : 'message';
  1174. $data = isset($params['data'])? $params['data'] : null;
  1175. $code = isset($params['code'])? $params['code'] : 'error';
  1176. }else{
  1177. $code = $params;
  1178. }
  1179. $code = is_numeric($code)? lang($code) : $code;
  1180. $message = is_numeric($message)? lang($message) : $message;
  1181. $jsonData = [
  1182. 'code' => in_array($code, ['error', 'success','login','exception']) ? $code : 'error',
  1183. 'message' => $message ? $message : 'message',
  1184. ];
  1185. if ($data !== null) {
  1186. $jsonData['data'] = $data;
  1187. }
  1188. header("Content:application/json;chartset=uft-8");
  1189. echo json_encode($jsonData, 256).$end;
  1190. exit;
  1191. }
  1192. /**
  1193. * 生成用户名
  1194. * @author wesmiler
  1195. * @date 2018年9月25日
  1196. * @param string $userCode ID,可为空
  1197. * @return string
  1198. */
  1199. function makeUserName($userCode='',$prefix='U'){
  1200. $charset = 'utf-8';
  1201. $codeLength = 8;
  1202. $length = 10;
  1203. $userCode = $userCode? $userCode : db('user')->max('id')+1;
  1204. $length = $length>10? $length : 10;
  1205. $userCodeLength = mb_strlen($userCode, $charset);
  1206. $prefixLength = mb_strlen($prefix, $charset);
  1207. if($userCodeLength>$codeLength){
  1208. $code = md5(date('is').uniqid($prefix).rand(10, 999).$userCode);
  1209. $code = $prefix.'00'.mb_substr($code, -8, 6, $charset);
  1210. }else{
  1211. $time = microtime(true);
  1212. $code = $prefix.str_repeat('0', $length-$userCodeLength-$prefixLength-1).($time%9).$userCode;
  1213. }
  1214. $code = strtoupper($code);
  1215. return $code;
  1216. }
  1217. /**
  1218. * @param $str
  1219. * @return string
  1220. */
  1221. function formatName($str){
  1222. return mb_substr($str, 0, 3, 'utf-8').'*****'.mb_substr($str, -3, 3, 'utf-8');
  1223. }
  1224. /**
  1225. * 格式化昵称,若有手机号码则隐藏
  1226. * @param $nickname
  1227. * @return string
  1228. */
  1229. function formatNickname($nickname){
  1230. if(preg_match("/^1[3-9][0-9]{9}$/", $nickname)){
  1231. $nickname = mb_substr($nickname, 0, 3, 'utf-8').'*****'.mb_substr($nickname, -3, 3, 'utf-8');
  1232. }
  1233. return $nickname;
  1234. }
  1235. /**
  1236. * HTTP请求
  1237. * @param $url 链接
  1238. * @param $data 提交数据
  1239. * @param string $type 请求类型:get post
  1240. * @param string $dataType 返回数据类型 array json
  1241. * @param int $timeout
  1242. * @return mixed
  1243. */
  1244. function httpRequest($url, $data=[], $type='post', $dataType='array', $timeout=60){
  1245. $data = $data && is_array($data)? http_build_query($data) : $data;
  1246. $url = strtolower($type) == 'get'? $url.(strpos($url, '?') === false ? '?' : ''). $data : $url;
  1247. $ch = curl_init($url);
  1248. curl_setopt($ch, CURLOPT_POST, 1);
  1249. curl_setopt($ch, CURLOPT_HEADER,0 );
  1250. curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1);
  1251. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  1252. curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
  1253. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查,0-规避ssl的证书检查
  1254. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
  1255. curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
  1256. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  1257. $ret = curl_exec($ch);
  1258. curl_close($ch);
  1259. if(strtolower($dataType) == 'array' && !is_array($ret)){
  1260. $ret = json_decode($ret, true);
  1261. }
  1262. return $ret;
  1263. }
  1264. /**
  1265. * HTTP请求
  1266. * @param $url 链接
  1267. * @param $data 提交数据
  1268. * @param string $type 请求类型:get post
  1269. * @param string $dataType 返回数据类型 array json
  1270. * @param int $timeout
  1271. * @return mixed
  1272. */
  1273. function httpHeaderRequest($url, $data=[], $type='post', $headers=[], $dataType='array', $timeout=60){
  1274. $data = $data && is_array($data)? http_build_query($data) : $data;
  1275. $url = strtolower($type) == 'get'? $url.(strpos($url, '?') === false ? '?' : ''). $data : $url;
  1276. $ch = curl_init($url);
  1277. if($headers){
  1278. curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  1279. }
  1280. curl_setopt($ch, CURLOPT_POST, 1);
  1281. curl_setopt($ch, CURLOPT_HEADER,0 );
  1282. curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1);
  1283. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  1284. curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
  1285. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查,0-规避ssl的证书检查
  1286. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
  1287. curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
  1288. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  1289. $ret = curl_exec($ch);
  1290. curl_close($ch);
  1291. if(strtolower($dataType) == 'array' && !is_array($ret)){
  1292. $ret = json_decode($ret, true);
  1293. }
  1294. return $ret;
  1295. }
  1296. /**
  1297. * HTTP请求(远程文件)
  1298. * @param $url 链接
  1299. * @param $data 提交数据
  1300. * @param string $type 请求类型:get post
  1301. * @param string $dataType 返回数据类型 array json
  1302. * @param int $timeout
  1303. * @return mixed
  1304. */
  1305. function httpFilePost($url, $data=[], $dataType='array', $timeout=120){
  1306. $ch = curl_init($url);
  1307. curl_setopt($ch, CURLOPT_POST, 1);
  1308. curl_setopt($ch, CURLOPT_HEADER,0 );
  1309. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  1310. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查,0-规避ssl的证书检查
  1311. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
  1312. curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
  1313. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  1314. $ret = curl_exec($ch);
  1315. curl_close($ch);
  1316. if(strtolower($dataType) == 'array' && !is_array($ret)){
  1317. $ret = json_decode($ret, true);
  1318. }
  1319. return $ret;
  1320. }
  1321. /**
  1322. * 异步请求
  1323. * @param $url 链接
  1324. * @param $data 提交数据
  1325. * @param string $type 请求类型:get post
  1326. * @param string $dataType 返回数据类型 array json
  1327. * @param int $timeout
  1328. * @return mixed
  1329. */
  1330. function httpAnsycRequest($url, $data=[], $type='post', $dataType='array'){
  1331. try {
  1332. $data = $data && is_array($data)? http_build_query($data) : $data;
  1333. $url = strtolower($type) == 'get'? $url.(strpos($url, '?') === false ? '?' : ''). $data : $url;
  1334. $ch = curl_init($url);
  1335. curl_setopt($ch, CURLOPT_POST, 1);
  1336. curl_setopt($ch, CURLOPT_HEADER,0 );
  1337. curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1);
  1338. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  1339. curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
  1340. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查,0-规避ssl的证书检查
  1341. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
  1342. curl_setopt($ch, CURLOPT_TIMEOUT, 1);
  1343. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  1344. $ret = curl_exec($ch);
  1345. curl_close($ch);
  1346. if(strtolower($dataType) == 'array' && !is_array($ret)){
  1347. $ret = $ret? json_decode($ret, true) : 'no data';
  1348. }
  1349. }catch (\Exception $exception){
  1350. $ret = $exception->getMessage();
  1351. }
  1352. return $ret;
  1353. }
  1354. /**
  1355. * 验证码检查,验证完后销毁验证码
  1356. * @param string $value 要验证的字符串
  1357. * @param string $id 验证码的ID
  1358. * @param bool $reset 验证成功后是否重置
  1359. * @return bool
  1360. */
  1361. function cmf_captcha_check($value, $id = "", $reset = true)
  1362. {
  1363. $captcha = new \think\captcha\Captcha();
  1364. $captcha->reset = $reset;
  1365. return $captcha->check($value, $id);
  1366. }
  1367. /**
  1368. * 切分SQL文件成多个可以单独执行的sql语句
  1369. * @param $file string sql文件路径
  1370. * @param $tablePre string 表前缀
  1371. * @param string $charset 字符集
  1372. * @param string $defaultTablePre 默认表前缀
  1373. * @param string $defaultCharset 默认字符集
  1374. * @return array
  1375. */
  1376. function cmf_split_sql($file, $tablePre, $charset = 'utf8mb4', $defaultTablePre = 'cmf_', $defaultCharset = 'utf8mb4')
  1377. {
  1378. if (file_exists($file)) {
  1379. //读取SQL文件
  1380. $sql = file_get_contents($file);
  1381. $sql = str_replace("\r", "\n", $sql);
  1382. $sql = str_replace("BEGIN;\n", '', $sql);//兼容 navicat 导出的 insert 语句
  1383. $sql = str_replace("COMMIT;\n", '', $sql);//兼容 navicat 导出的 insert 语句
  1384. $sql = str_replace($defaultCharset, $charset, $sql);
  1385. $sql = trim($sql);
  1386. //替换表前缀
  1387. $sql = str_replace(" `{$defaultTablePre}", " `{$tablePre}", $sql);
  1388. $sqls = explode(";\n", $sql);
  1389. return $sqls;
  1390. }
  1391. return [];
  1392. }
  1393. /**
  1394. * 判断当前的语言包,并返回语言包名
  1395. * @return string 语言包名
  1396. */
  1397. function cmf_current_lang()
  1398. {
  1399. return request()->langset();
  1400. }
  1401. /**
  1402. * 获取惟一订单号
  1403. * @return string
  1404. */
  1405. function cmf_get_order_sn()
  1406. {
  1407. return date('Ymd') . substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
  1408. }
  1409. /**
  1410. * 获取文件扩展名
  1411. * @param string $filename 文件名
  1412. * @return string 文件扩展名
  1413. */
  1414. function cmf_get_file_extension($filename)
  1415. {
  1416. $pathinfo = pathinfo($filename);
  1417. return strtolower($pathinfo['extension']);
  1418. }
  1419. /**
  1420. * 检查手机或邮箱是否还可以发送验证码,并返回生成的验证码
  1421. * @param string $account 手机或邮箱
  1422. * @param integer $length 验证码位数,支持4,6,8
  1423. * @return string 数字验证码
  1424. * @throws \think\db\exception\DataNotFoundException
  1425. * @throws \think\db\exception\ModelNotFoundException
  1426. * @throws \think\exception\DbException
  1427. */
  1428. function cmf_get_verification_code($account, $length = 6)
  1429. {
  1430. if (empty($account)) return false;
  1431. $verificationCodeQuery = Db::name('verification_code');
  1432. $currentTime = time();
  1433. $maxCount = 5;
  1434. $findVerificationCode = $verificationCodeQuery->where('account', $account)->find();
  1435. $result = false;
  1436. if (empty($findVerificationCode)) {
  1437. $result = true;
  1438. } else {
  1439. $sendTime = $findVerificationCode['send_time'];
  1440. $todayStartTime = strtotime(date('Y-m-d', $currentTime));
  1441. if ($sendTime < $todayStartTime) {
  1442. $result = true;
  1443. } else if ($findVerificationCode['count'] < $maxCount) {
  1444. $result = true;
  1445. }
  1446. }
  1447. if ($result) {
  1448. switch ($length) {
  1449. case 4:
  1450. $result = rand(1000, 9999);
  1451. break;
  1452. case 6:
  1453. $result = rand(100000, 999999);
  1454. break;
  1455. case 8:
  1456. $result = rand(10000000, 99999999);
  1457. break;
  1458. default:
  1459. $result = rand(100000, 999999);
  1460. }
  1461. }
  1462. return $result;
  1463. }
  1464. /**
  1465. * 更新手机或邮箱验证码发送日志
  1466. * @param string $account 手机或邮箱
  1467. * @param string $code 验证码
  1468. * @param int $expireTime 过期时间
  1469. * @return int|string
  1470. * @throws \think\Exception
  1471. * @throws \think\db\exception\DataNotFoundException
  1472. * @throws \think\db\exception\ModelNotFoundException
  1473. * @throws \think\exception\DbException
  1474. * @throws \think\exception\PDOException
  1475. */
  1476. function cmf_verification_code_log($account, $code, $expireTime = 0)
  1477. {
  1478. $currentTime = time();
  1479. $expireTime = $expireTime > $currentTime ? $expireTime : $currentTime + 30 * 60;
  1480. $findVerificationCode = Db::name('verification_code')->where('account', $account)->find();
  1481. if ($findVerificationCode) {
  1482. $todayStartTime = strtotime(date("Y-m-d"));//当天0点
  1483. if ($findVerificationCode['send_time'] <= $todayStartTime) {
  1484. $count = 1;
  1485. } else {
  1486. $count = Db::raw('count+1');
  1487. }
  1488. $result = Db::name('verification_code')
  1489. ->where('account', $account)
  1490. ->update([
  1491. 'send_time' => $currentTime,
  1492. 'expire_time' => $expireTime,
  1493. 'code' => $code,
  1494. 'count' => $count
  1495. ]);
  1496. } else {
  1497. $result = Db::name('verification_code')
  1498. ->insert([
  1499. 'account' => $account,
  1500. 'send_time' => $currentTime,
  1501. 'code' => $code,
  1502. 'count' => 1,
  1503. 'expire_time' => $expireTime
  1504. ]);
  1505. }
  1506. return $result;
  1507. }
  1508. /**
  1509. * 手机或邮箱验证码检查,验证完后销毁验证码增加安全性,返回true验证码正确,false验证码错误
  1510. * @param string $account 手机或邮箱
  1511. * @param string $code 验证码
  1512. * @param boolean $clear 是否验证后销毁验证码
  1513. * @return string 错误消息,空字符串代码验证码正确
  1514. * @return string
  1515. * @throws \think\Exception
  1516. * @throws \think\db\exception\DataNotFoundException
  1517. * @throws \think\db\exception\ModelNotFoundException
  1518. * @throws \think\exception\DbException
  1519. * @throws \think\exception\PDOException
  1520. */
  1521. function cmf_check_verification_code($account, $code, $clear = false)
  1522. {
  1523. $findVerificationCode = Db::name('verification_code')->where('account', $account)->find();
  1524. if ($findVerificationCode) {
  1525. if ($findVerificationCode['expire_time'] > time()) {
  1526. if ($code == $findVerificationCode['code']) {
  1527. if ($clear) {
  1528. Db::name('verification_code')->where('account', $account)->update(['code' => '']);
  1529. }
  1530. } else {
  1531. return "验证码不正确!";
  1532. }
  1533. } else {
  1534. return "验证码已经过期,请先获取验证码!";
  1535. }
  1536. } else {
  1537. return "请先获取验证码!";
  1538. }
  1539. return "";
  1540. }
  1541. /**
  1542. * 清除某个手机或邮箱的数字验证码,一般在验证码验证正确完成后
  1543. * @param string $account 手机或邮箱
  1544. * @return boolean true:手机验证码正确,false:手机验证码错误
  1545. * @throws \think\Exception
  1546. * @throws \think\exception\PDOException
  1547. */
  1548. function cmf_clear_verification_code($account)
  1549. {
  1550. $verificationCodeQuery = Db::name('verification_code');
  1551. $result = $verificationCodeQuery->where('account', $account)->update(['code' => '']);
  1552. return $result;
  1553. }
  1554. /**
  1555. * 区分大小写的文件存在判断
  1556. * @param string $filename 文件地址
  1557. * @return boolean
  1558. */
  1559. function file_exists_case($filename)
  1560. {
  1561. if (is_file($filename)) {
  1562. if (APP_DEBUG) {
  1563. if (basename(realpath($filename)) != basename($filename))
  1564. return false;
  1565. }
  1566. return true;
  1567. }
  1568. return false;
  1569. }
  1570. /**
  1571. * 生成用户 token
  1572. * @param $userId
  1573. * @param $deviceType
  1574. * @return string 用户 token
  1575. * @throws \think\Exception
  1576. * @throws \think\db\exception\DataNotFoundException
  1577. * @throws \think\db\exception\ModelNotFoundException
  1578. * @throws \think\exception\DbException
  1579. * @throws \think\exception\PDOException
  1580. */
  1581. function cmf_generate_user_token($userId, $deviceType)
  1582. {
  1583. $userTokenQuery = Db::name("user_token")
  1584. ->where('user_id', $userId)
  1585. ->where('device_type', $deviceType);
  1586. $findUserToken = $userTokenQuery->find();
  1587. $currentTime = time();
  1588. $expireTime = $currentTime + 24 * 3600 * 180;
  1589. $token = md5(uniqid()) . md5(uniqid());
  1590. if (empty($findUserToken)) {
  1591. Db::name("user_token")->insert([
  1592. 'token' => $token,
  1593. 'user_id' => $userId,
  1594. 'expire_time' => $expireTime,
  1595. 'create_time' => $currentTime,
  1596. 'device_type' => $deviceType
  1597. ]);
  1598. } else {
  1599. if ($findUserToken['expire_time'] > time() && !empty($findUserToken['token'])) {
  1600. $token = $findUserToken['token'];
  1601. } else {
  1602. Db::name("user_token")
  1603. ->where('user_id', $userId)
  1604. ->where('device_type', $deviceType)
  1605. ->update([
  1606. 'token' => $token,
  1607. 'expire_time' => $expireTime,
  1608. 'create_time' => $currentTime
  1609. ]);
  1610. }
  1611. }
  1612. return $token;
  1613. }
  1614. /**
  1615. * 字符串命名风格转换
  1616. * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
  1617. * @param string $name 字符串
  1618. * @param integer $type 转换类型
  1619. * @param bool $ucfirst 首字母是否大写(驼峰规则)
  1620. * @return string
  1621. */
  1622. function cmf_parse_name($name, $type = 0, $ucfirst = true)
  1623. {
  1624. return Loader::parseName($name, $type, $ucfirst);
  1625. }
  1626. /**
  1627. * 判断字符串是否为已经序列化过
  1628. * @param $str
  1629. * @return bool
  1630. */
  1631. function cmf_is_serialized($str)
  1632. {
  1633. return ($str == serialize(false) || @unserialize($str) !== false);
  1634. }
  1635. /**
  1636. * 判断是否SSL协议
  1637. * @return boolean
  1638. */
  1639. function cmf_is_ssl()
  1640. {
  1641. if (isset($_SERVER['HTTPS']) && ('1' == $_SERVER['HTTPS'] || 'on' == strtolower($_SERVER['HTTPS']))) {
  1642. return true;
  1643. } elseif (isset($_SERVER['SERVER_PORT']) && ('443' == $_SERVER['SERVER_PORT'])) {
  1644. return true;
  1645. }
  1646. return false;
  1647. }
  1648. /**
  1649. * 获取CMF系统的设置,此类设置用于全局
  1650. * @param string $key 设置key,为空时返回所有配置信息
  1651. * @return array|bool|mixed
  1652. * @throws \think\db\exception\DataNotFoundException
  1653. * @throws \think\db\exception\ModelNotFoundException
  1654. * @throws \think\exception\DbException
  1655. */
  1656. function cmf_get_cmf_settings($key = "")
  1657. {
  1658. $cmfSettings = cache("cmf_settings");
  1659. if (empty($cmfSettings)) {
  1660. $objOptions = new \app\admin\model\OptionModel();
  1661. $objResult = $objOptions->where("option_name", 'cmf_settings')->find();
  1662. $arrOption = $objResult ? $objResult->toArray() : [];
  1663. if ($arrOption) {
  1664. $cmfSettings = json_decode($arrOption['option_value'], true);
  1665. } else {
  1666. $cmfSettings = [];
  1667. }
  1668. cache("cmf_settings", $cmfSettings);
  1669. }
  1670. if (!empty($key)) {
  1671. if (isset($cmfSettings[$key])) {
  1672. return $cmfSettings[$key];
  1673. } else {
  1674. return false;
  1675. }
  1676. }
  1677. return $cmfSettings;
  1678. }
  1679. /**
  1680. * @deprecated
  1681. * 判读是否sae环境
  1682. * @return bool
  1683. */
  1684. function cmf_is_sae()
  1685. {
  1686. if (function_exists('saeAutoLoader')) {
  1687. return true;
  1688. } else {
  1689. return false;
  1690. }
  1691. }
  1692. /**
  1693. * 获取客户端IP地址
  1694. * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
  1695. * @param boolean $adv 是否进行高级模式获取(有可能被伪装)
  1696. * @return string
  1697. */
  1698. function get_client_ip($type = 0, $adv = true)
  1699. {
  1700. return request()->ip($type, $adv);
  1701. }
  1702. /**
  1703. * 生成base64的url,用于数据库存放 url
  1704. * @param $url 路由地址,如 控制器/方法名,应用/控制器/方法名
  1705. * @param $params url参数
  1706. * @return string
  1707. */
  1708. function cmf_url_encode($url, $params)
  1709. {
  1710. // 解析参数
  1711. if (is_string($params)) {
  1712. // aaa=1&bbb=2 转换成数组
  1713. parse_str($params, $params);
  1714. }
  1715. return base64_encode(json_encode(['action' => $url, 'param' => $params]));
  1716. }
  1717. /**
  1718. * CMF Url生成
  1719. * @param string $url 路由地址
  1720. * @param string|array $vars 变量
  1721. * @param bool|string $suffix 生成的URL后缀
  1722. * @param bool|string $domain 域名
  1723. * @return string
  1724. * @throws \think\db\exception\DataNotFoundException
  1725. * @throws \think\db\exception\ModelNotFoundException
  1726. * @throws \think\exception\DbException
  1727. */
  1728. function cmf_url($url = '', $vars = '', $suffix = true, $domain = false)
  1729. {
  1730. global $CMF_GV_routes;
  1731. if (empty($CMF_GV_routes)) {
  1732. $routeModel = new \app\admin\model\RouteModel();
  1733. $CMF_GV_routes = $routeModel->getRoutes();
  1734. }
  1735. if (false === strpos($url, '://') && 0 !== strpos($url, '/')) {
  1736. $info = parse_url($url);
  1737. $url = !empty($info['path']) ? $info['path'] : '';
  1738. if (isset($info['fragment'])) {
  1739. // 解析锚点
  1740. $anchor = $info['fragment'];
  1741. if (false !== strpos($anchor, '?')) {
  1742. // 解析参数
  1743. list($anchor, $info['query']) = explode('?', $anchor, 2);
  1744. }
  1745. if (false !== strpos($anchor, '@')) {
  1746. // 解析域名
  1747. list($anchor, $domain) = explode('@', $anchor, 2);
  1748. }
  1749. } elseif (strpos($url, '@') && false === strpos($url, '\\')) {
  1750. // 解析域名
  1751. list($url, $domain) = explode('@', $url, 2);
  1752. }
  1753. }
  1754. // 解析参数
  1755. if (is_string($vars)) {
  1756. // aaa=1&bbb=2 转换成数组
  1757. parse_str($vars, $vars);
  1758. }
  1759. if (isset($info['query'])) {
  1760. // 解析地址里面参数 合并到vars
  1761. parse_str($info['query'], $params);
  1762. $vars = array_merge($params, $vars);
  1763. }
  1764. if (!empty($vars) && !empty($CMF_GV_routes[$url])) {
  1765. foreach ($CMF_GV_routes[$url] as $actionRoute) {
  1766. $sameVars = array_intersect_assoc($vars, $actionRoute['vars']);
  1767. if (count($sameVars) == count($actionRoute['vars'])) {
  1768. ksort($sameVars);
  1769. $url = $url . '?' . http_build_query($sameVars);
  1770. $vars = array_diff_assoc($vars, $sameVars);
  1771. break;
  1772. }
  1773. }
  1774. }
  1775. if (!empty($anchor)) {
  1776. $url = $url . '#' . $anchor;
  1777. }
  1778. // if (!empty($domain)) {
  1779. // $url = $url . '@' . $domain;
  1780. // }
  1781. return Url::build($url, $vars, $suffix, $domain);
  1782. }
  1783. /**
  1784. * 判断 cmf 核心是否安装
  1785. * @return bool
  1786. */
  1787. function cmf_is_installed()
  1788. {
  1789. static $cmfIsInstalled;
  1790. if (empty($cmfIsInstalled)) {
  1791. $cmfIsInstalled = file_exists(CMF_DATA . 'install.lock');
  1792. }
  1793. return $cmfIsInstalled;
  1794. }
  1795. /**
  1796. * 替换编辑器内容中的文件地址
  1797. * @param string $content 编辑器内容
  1798. * @param boolean $isForDbSave true:表示把绝对地址换成相对地址,用于数据库保存,false:表示把相对地址换成绝对地址用于界面显示
  1799. * @return string
  1800. */
  1801. function cmf_replace_content_file_url($content, $isForDbSave = false)
  1802. {
  1803. //import('phpQuery.phpQuery', EXTEND_PATH);
  1804. \phpQuery::newDocumentHTML($content);
  1805. $pq = pq(null);
  1806. $storage = Storage::instance();
  1807. $localStorage = new cmf\lib\storage\Local([]);
  1808. $storageDomain = $storage->getDomain();
  1809. $domain = request()->host();
  1810. $images = $pq->find("img");
  1811. if ($images->length) {
  1812. foreach ($images as $img) {
  1813. $img = pq($img);
  1814. $imgSrc = $img->attr("src");
  1815. if ($isForDbSave) {
  1816. if (preg_match("/^\/upload\//", $imgSrc)) {
  1817. $img->attr("src", preg_replace("/^\/upload\//", '', $imgSrc));
  1818. } elseif (preg_match("/^http(s)?:\/\/$domain\/upload\//", $imgSrc)) {
  1819. $img->attr("src", $localStorage->getFilePath($imgSrc));
  1820. } elseif (preg_match("/^http(s)?:\/\/$storageDomain\//", $imgSrc)) {
  1821. $img->attr("src", $storage->getFilePath($imgSrc));
  1822. }
  1823. } else {
  1824. $img->attr("src", cmf_get_image_url($imgSrc));
  1825. }
  1826. }
  1827. }
  1828. $links = $pq->find("a");
  1829. if ($links->length) {
  1830. foreach ($links as $link) {
  1831. $link = pq($link);
  1832. $href = $link->attr("href");
  1833. if ($isForDbSave) {
  1834. if (preg_match("/^\/upload\//", $href)) {
  1835. $link->attr("href", preg_replace("/^\/upload\//", '', $href));
  1836. } elseif (preg_match("/^http(s)?:\/\/$domain\/upload\//", $href)) {
  1837. $link->attr("href", $localStorage->getFilePath($href));
  1838. } elseif (preg_match("/^http(s)?:\/\/$storageDomain\//", $href)) {
  1839. $link->attr("href", $storage->getFilePath($href));
  1840. }
  1841. } else {
  1842. if (!(preg_match("/^\//", $href) || preg_match("/^http/", $href))) {
  1843. $link->attr("href", cmf_get_file_download_url($href));
  1844. }
  1845. }
  1846. }
  1847. }
  1848. $content = $pq->html();
  1849. \phpQuery::$documents = null;
  1850. return $content;
  1851. }
  1852. /**
  1853. * 获取后台风格名称
  1854. * @return string
  1855. */
  1856. function cmf_get_admin_style()
  1857. {
  1858. $adminSettings = cmf_get_option('admin_settings');
  1859. return empty($adminSettings['admin_style']) ? 'simpleadmin' : $adminSettings['admin_style'];
  1860. }
  1861. /**
  1862. * curl get 请求
  1863. * @param $url
  1864. * @return mixed
  1865. */
  1866. function cmf_curl_get($url)
  1867. {
  1868. $ch = curl_init();
  1869. curl_setopt($ch, CURLOPT_URL, $url);
  1870. curl_setopt($ch, CURLOPT_FAILONERROR, true);
  1871. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
  1872. curl_setopt($ch, CURLOPT_AUTOREFERER, true);
  1873. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  1874. curl_setopt($ch, CURLOPT_TIMEOUT, 5);
  1875. $SSL = substr($url, 0, 8) == "https://" ? true : false;
  1876. // if ($SSL) {
  1877. // curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 信任任何证书
  1878. // curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); // 检查证书中是否设置域名
  1879. // }
  1880. $content = curl_exec($ch);
  1881. curl_close($ch);
  1882. return $content;
  1883. }
  1884. /**
  1885. * 用户操作记录
  1886. * @param string $action 用户操作
  1887. * @throws \think\Exception
  1888. * @throws \think\db\exception\DataNotFoundException
  1889. * @throws \think\db\exception\ModelNotFoundException
  1890. * @throws \think\exception\DbException
  1891. * @throws \think\exception\PDOException
  1892. */
  1893. function cmf_user_action($action)
  1894. {
  1895. $userId = cmf_get_current_user_id();
  1896. if (empty($userId)) {
  1897. return;
  1898. }
  1899. $findUserAction = Db::name('user_action')->where('action', $action)->find();
  1900. if (empty($findUserAction)) {
  1901. return;
  1902. }
  1903. $changeScore = false;
  1904. if ($findUserAction['cycle_type'] == 0) {
  1905. $changeScore = true;
  1906. } elseif ($findUserAction['reward_number'] > 0) {
  1907. $findUserScoreLog = Db::name('user_score_log')->order('create_time DESC')->find();
  1908. if (!empty($findUserScoreLog)) {
  1909. $cycleType = intval($findUserAction['cycle_type']);
  1910. $cycleTime = intval($findUserAction['cycle_time']);
  1911. switch ($cycleType) {//1:按天;2:按小时;3:永久
  1912. case 1:
  1913. $firstDayStartTime = strtotime(date('Y-m-d', $findUserScoreLog['create_time']));
  1914. $endDayEndTime = strtotime(date('Y-m-d', strtotime("+{$cycleTime} day", $firstDayStartTime)));
  1915. // $todayStartTime = strtotime(date('Y-m-d'));
  1916. // $todayEndTime = strtotime(date('Y-m-d', strtotime('+1 day')));
  1917. $findUserScoreLogCount = Db::name('user_score_log')->where([
  1918. 'user_id' => $userId,
  1919. 'create_time' => [['gt', $firstDayStartTime], ['lt', $endDayEndTime]]
  1920. ])->count();
  1921. if ($findUserScoreLogCount < $findUserAction['reward_number']) {
  1922. $changeScore = true;
  1923. }
  1924. break;
  1925. case 2:
  1926. if (($findUserScoreLog['create_time'] + $cycleTime * 3600) < time()) {
  1927. $changeScore = true;
  1928. }
  1929. break;
  1930. case 3:
  1931. break;
  1932. }
  1933. } else {
  1934. $changeScore = true;
  1935. }
  1936. }
  1937. if ($changeScore) {
  1938. Db::name('user_score_log')->insert([
  1939. 'user_id' => $userId,
  1940. 'create_time' => time(),
  1941. 'action' => $action,
  1942. 'score' => $findUserAction['score'],
  1943. 'coin' => $findUserAction['coin'],
  1944. ]);
  1945. $data = [];
  1946. if ($findUserAction['score'] > 0) {
  1947. $data['score'] = Db::raw('score+' . $findUserAction['score']);
  1948. }
  1949. if ($findUserAction['score'] < 0) {
  1950. $data['score'] = Db::raw('score-' . abs($findUserAction['score']));
  1951. }
  1952. if ($findUserAction['coin'] > 0) {
  1953. $data['coin'] = Db::raw('coin+' . $findUserAction['coin']);
  1954. }
  1955. if ($findUserAction['coin'] < 0) {
  1956. $data['coin'] = Db::raw('coin-' . abs($findUserAction['coin']));
  1957. }
  1958. Db::name('user')->where('id', $userId)->update($data);
  1959. }
  1960. }
  1961. function cmf_api_request($url, $params = [])
  1962. {
  1963. //初始化
  1964. $curl = curl_init();
  1965. //设置抓取的url
  1966. curl_setopt($curl, CURLOPT_URL, 'http://127.0.0.1:1314/api/' . $url);
  1967. //设置头文件的信息作为数据流输出
  1968. curl_setopt($curl, CURLOPT_HEADER, 0);
  1969. //设置获取的信息以文件流的形式返回,而不是直接输出。
  1970. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  1971. //设置post方式提交
  1972. curl_setopt($curl, CURLOPT_POST, 1);
  1973. $token = session('token');
  1974. curl_setopt($curl, CURLOPT_HTTPHEADER, ["XX-Token: $token"]);
  1975. //设置post数据
  1976. curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
  1977. //执行命令
  1978. $data = curl_exec($curl);
  1979. //关闭URL请求
  1980. curl_close($curl);
  1981. //显示获得的数据
  1982. return json_decode($data, true);
  1983. }
  1984. /**
  1985. * 判断是否允许开放注册
  1986. */
  1987. function cmf_is_open_registration()
  1988. {
  1989. $cmfSettings = cmf_get_option('cmf_settings');
  1990. return empty($cmfSettings['open_registration']) ? false : true;
  1991. }
  1992. /**
  1993. * XML编码
  1994. * @param mixed $data 数据
  1995. * @param string $root 根节点名
  1996. * @param string $item 数字索引的子节点名
  1997. * @param string $attr 根节点属性
  1998. * @param string $id 数字索引子节点key转换的属性名
  1999. * @param string $encoding 数据编码
  2000. * @return string
  2001. */
  2002. function cmf_xml_encode($data, $root = 'think', $item = 'item', $attr = '', $id = 'id', $encoding = 'utf-8')
  2003. {
  2004. if (is_array($attr)) {
  2005. $_attr = [];
  2006. foreach ($attr as $key => $value) {
  2007. $_attr[] = "{$key}=\"{$value}\"";
  2008. }
  2009. $attr = implode(' ', $_attr);
  2010. }
  2011. $attr = trim($attr);
  2012. $attr = empty($attr) ? '' : " {$attr}";
  2013. $xml = "<?xml version=\"1.0\" encoding=\"{$encoding}\"?>";
  2014. $xml .= "<{$root}{$attr}>";
  2015. $xml .= cmf_data_to_xml($data, $item, $id);
  2016. $xml .= "</{$root}>";
  2017. return $xml;
  2018. }
  2019. /**
  2020. * 数据XML编码
  2021. * @param mixed $data 数据
  2022. * @param string $item 数字索引时的节点名称
  2023. * @param string $id 数字索引key转换为的属性名
  2024. * @return string
  2025. */
  2026. function cmf_data_to_xml($data, $item = 'item', $id = 'id')
  2027. {
  2028. $xml = $attr = '';
  2029. foreach ($data as $key => $val) {
  2030. if (is_numeric($key)) {
  2031. $id && $attr = " {$id}=\"{$key}\"";
  2032. $key = $item;
  2033. }
  2034. $xml .= "<{$key}{$attr}>";
  2035. $xml .= (is_array($val) || is_object($val)) ? cmf_data_to_xml($val, $item, $id) : $val;
  2036. $xml .= "</{$key}>";
  2037. }
  2038. return $xml;
  2039. }
  2040. /**
  2041. * 检查手机格式,中国手机不带国家代码,国际手机号格式为:国家代码-手机号
  2042. * @param $mobile
  2043. * @return bool
  2044. */
  2045. function cmf_check_mobile($mobile)
  2046. {
  2047. if (preg_match('/(^(13\d|14\d|15\d|16\d|17\d|18\d|19\d)\d{8})$/', $mobile)) {
  2048. return true;
  2049. } else {
  2050. if (preg_match('/^\d{1,4}-\d{5,11}$/', $mobile)) {
  2051. if (preg_match('/^\d{1,4}-0+/', $mobile)) {
  2052. //不能以0开头
  2053. return false;
  2054. }
  2055. return true;
  2056. }
  2057. return false;
  2058. }
  2059. }
  2060. /**
  2061. * 文件大小格式化
  2062. * @param $bytes 文件大小(字节 Byte)
  2063. * @return string
  2064. */
  2065. function cmf_file_size_format($bytes)
  2066. {
  2067. $type = ['B', 'KB', 'MB', 'GB', 'TB'];
  2068. for ($i = 0; $bytes >= 1024; $i++)//单位每增大1024,则单位数组向后移动一位表示相应的单位
  2069. {
  2070. $bytes /= 1024;
  2071. }
  2072. return (floor($bytes * 100) / 100) . $type[$i];//floor是取整函数,为了防止出现一串的小数,这里取了两位小数
  2073. }
  2074. /**
  2075. * 计数器增加
  2076. * @param $name 计数器英文标识
  2077. * @param int $min 计数器最小值
  2078. * @param int $step 增加步长
  2079. * @return mixed
  2080. */
  2081. function cmf_counter_inc($name, $min = 1, $step = 1)
  2082. {
  2083. $id = cache('core_counter_' . $name);
  2084. if (empty($id)) {
  2085. $id = Db::name('core_counter')->where('name', $name)->value('id');
  2086. if (empty($id)) {
  2087. $id = Db::name('core_counter')->insertGetId([
  2088. 'name' => $name,
  2089. 'value' => 0
  2090. ]);
  2091. }
  2092. cache('core_counter_' . $name, $id);
  2093. }
  2094. Db::startTrans();
  2095. try {
  2096. $value = Db::name('core_counter')->where('id', $id)->lock(true)->value('value');
  2097. if ($min > $value) {
  2098. $value = $min;
  2099. } else {
  2100. $value += $step;
  2101. }
  2102. Db::name('core_counter')->where('id', $id)->update(['value' => $value]);
  2103. Db::commit();
  2104. } catch (\Exception $e) {
  2105. Db::rollback();
  2106. $value = false;
  2107. }
  2108. return $value;
  2109. }
  2110. /**
  2111. * 获取ThinkPHP版本
  2112. * @return string
  2113. */
  2114. function cmf_thinkphp_version()
  2115. {
  2116. return \think\facade\App::version();
  2117. }
  2118. /**
  2119. * 获取ThinkCMF版本
  2120. * @return string
  2121. */
  2122. function cmf_version()
  2123. {
  2124. try {
  2125. $version = trim(file_get_contents(CMF_ROOT . 'version'));
  2126. } catch (\Exception $e) {
  2127. $version = '0.0.0';
  2128. }
  2129. return $version;
  2130. }
  2131. /**
  2132. * 获取ThinkCMF核心包目录
  2133. */
  2134. function cmf_core_path()
  2135. {
  2136. return __DIR__ . DIRECTORY_SEPARATOR;
  2137. }
  2138. /**
  2139. * 获取模块配置文件路径
  2140. * @param $app 应用
  2141. * @param $file 文件名不带后缀
  2142. */
  2143. function cmf_get_app_config_file($app, $file)
  2144. {
  2145. switch ($app) {
  2146. case 'cmf':
  2147. $configFile = cmf_core_path() . "{$file}.php";
  2148. break;
  2149. case 'swoole':
  2150. $configFile = Env::get('root_path') . "vendor/thinkcmf/cmf-swoole/src/{$file}.php";
  2151. break;
  2152. default:
  2153. $configFile = APP_PATH . $app . "/{$file}.php";
  2154. if (!file_exists($configFile)) {
  2155. $configFile = Env::get('root_path') . "vendor/thinkcmf/cmf-app/src/{$app}/{$file}.php";
  2156. }
  2157. }
  2158. return $configFile;
  2159. }
  2160. /**
  2161. * 转换+-为desc和asc
  2162. * @deprecated
  2163. * @param $order array 转换对象
  2164. * @return array
  2165. */
  2166. function order_shift($order)
  2167. {
  2168. $orderArr = [];
  2169. foreach ($order as $key => $value) {
  2170. $upDwn = substr($value, 0, 1);
  2171. $orderType = $upDwn == '-' ? 'desc' : 'asc';
  2172. $orderField = substr($value, 1);
  2173. if (!empty($whiteParams)) {
  2174. if (in_array($orderField, $whiteParams)) {
  2175. $orderArr[$orderField] = $orderType;
  2176. }
  2177. } else {
  2178. $orderArr[$orderField] = $orderType;
  2179. }
  2180. }
  2181. return $orderArr;
  2182. }
  2183. /**
  2184. * 模型检查
  2185. * @deprecated
  2186. * @param $relationFilter array 检查的字段
  2187. * @param $relations string 被检查的字段
  2188. * @return array|bool
  2189. */
  2190. function allowed_relations($relationFilter, $relations)
  2191. {
  2192. if (is_string($relations)) {
  2193. $relations = explode(',', $relations);
  2194. }
  2195. if (!is_array($relations)) {
  2196. return false;
  2197. }
  2198. return array_intersect($relationFilter, $relations);
  2199. }
  2200. /**
  2201. * 字符串转数组
  2202. * @deprecated
  2203. * @param string $string 字符串
  2204. * @return array
  2205. */
  2206. function str_to_arr($string)
  2207. {
  2208. $result = is_string($string) ? explode(',', $string) : $string;
  2209. return $result;
  2210. }
  2211. /**
  2212. * 获取格式化参数
  2213. * @param $ptdate
  2214. * @return false|string
  2215. */
  2216. function getFormatTime($ptdate) {
  2217. $ptime = strtotime($ptdate);
  2218. $etime = time() - $ptime;
  2219. if( $etime <= 60) {
  2220. $msg = '刚刚';
  2221. } else if($etime > 60 && $etime <= 60 * 60){
  2222. $msg = floor($etime / 60) . ' 分钟前';
  2223. } else if($etime > 60 * 60 && $etime <= 24 * 60 * 60){
  2224. $msg = date('Ymd',$ptime)==date('Ymd',time()) ? '今天 '.date('H点',$ptime) : '昨天 '.date('H点',$ptime);
  2225. } else if($etime > 24 * 60 * 60 && $etime <= 2 * 24 * 60 * 60){
  2226. $msg = date('Ymd',$ptime)+1==date('Ymd',time()) ? '昨天 '.date('H点',$ptime) : '前天 '.date('H点',$ptime);
  2227. } else if($etime > 2 * 24 * 60 * 60 && $etime <= 12 * 30 * 24 * 60 * 60){
  2228. $msg = date('Y',$ptime)==date('Y',time()) ? date('m月d日',$ptime) : date('Y-m-d H:i',$ptime);
  2229. } else {
  2230. $msg = date('Y-m-d',$ptime);
  2231. }
  2232. return $msg;
  2233. }
  2234. /**
  2235. * 获取文件大小
  2236. * @param $size
  2237. * @return string
  2238. */
  2239. function getFileSize($size){
  2240. if($size/1024/1024>1){
  2241. return moneyFormat($size/1024/1024, 2).'M';
  2242. }else{
  2243. return moneyFormat($size/1024, 2).'KB';
  2244. }
  2245. }
  2246. /**
  2247. * 强制删除目录
  2248. * @param $dir
  2249. */
  2250. function deleteDir($dirName)
  2251. {
  2252. $d = scandir($dirName);
  2253. foreach ($d as $v){
  2254. if($v != '.' && $v != '..'){
  2255. if(is_dir($dirName.'/'.$v)){
  2256. deleteDir($dirName.'/'.$v);
  2257. }else if(is_file($dirName.'/'.$v)){
  2258. unlink($dirName.'/'.$v);
  2259. }
  2260. }
  2261. }
  2262. if(file_exists($dirName) && is_dir($dirName)){
  2263. rmdir($dirName);
  2264. }
  2265. }
  2266. /**
  2267. * 获取文件后缀名
  2268. * @param $file
  2269. * @return mixed
  2270. */
  2271. function getFileType($file){
  2272. $data = explode('.', $file);
  2273. $fileType = end($data);
  2274. return $fileType;
  2275. }
  2276. /**
  2277. * 生成交易单号
  2278. * @param string $prefix
  2279. * @param string $ext
  2280. * @return string
  2281. */
  2282. function makeTradeNo($prefix='', $ext=''){
  2283. $orderConfig = [];
  2284. $prefix = $prefix? $prefix : 'SG';
  2285. $length = isset($orderConfig['length'])? intval($orderConfig['length']-9) : 10;
  2286. $length = max(8, $length);
  2287. $uid = $ext? date('is').substr($ext, -3,1) : intval(time()/rand(1,9));
  2288. $len = mb_strlen($uid,'utf-8');
  2289. if($len < $length){
  2290. $uid = str_repeat('0', $length-$len-1).rand(0,9).$uid;
  2291. }else{
  2292. $uid = date('h').rand(0,$length).$uid;
  2293. }
  2294. return strtoupper($prefix).date('ymdH').$uid;
  2295. }
  2296. /**
  2297. * 日志
  2298. * @param $key 键名
  2299. * @param $val 值
  2300. * @return bool|int
  2301. */
  2302. function saveLogCache($key, $val){
  2303. $debug = config('weixin.debug');
  2304. if(!$debug){
  2305. return false;
  2306. }
  2307. // $path = CMF_ROOT.'/';
  2308. $path = './logs/';
  2309. if(!is_dir($path)){
  2310. mkdir('./logs', 0755, true);
  2311. }
  2312. $fileSize = 0;
  2313. file_put_contents('test.log', var_export($path, true));
  2314. $file = 'cache_'.str_replace(':','_', $key).'.log';
  2315. if(file_exists($path.$file)){
  2316. $fileSize = filesize($path.$file);
  2317. }
  2318. $val = is_array($val)? json_encode($val, 256) : $val;
  2319. if($fileSize/1024/1024 > 5){
  2320. return file_put_contents($path.$file, date('Y-m-d H:i:s').': '. $val."\n\n");
  2321. }else{
  2322. return file_put_contents($path.$file, date('Y-m-d H:i:s').': '. $val."\n\n", 8);
  2323. }
  2324. // cache($key, $val);
  2325. }
  2326. /**
  2327. * 格式化
  2328. * @param $str
  2329. * @param string $mark
  2330. * @return string
  2331. */
  2332. function formatStr($str, $mark='*'){
  2333. if(strlen($str) == 11){
  2334. return substr($str, 0, 5).str_repeat($mark, 4).substr($str, -2, 2);
  2335. }
  2336. return $str;
  2337. }
  2338. /**
  2339. * 生成编号
  2340. * @param $num 数字
  2341. * @param string $prefix 前缀
  2342. * @param int $length 长度,最小4
  2343. * @return mixed
  2344. */
  2345. function makeNo($num, $prefix='', $length=4, $hasZero=false){
  2346. $charset = 'utf-8';
  2347. $prefix = $prefix? $prefix : ' N';
  2348. $length = $length>3? $length-1 : 3;
  2349. $len = mb_strlen($num, $charset);
  2350. $zeroLen = $length>$len? $length - $len : 0;
  2351. if($zeroLen && $hasZero){
  2352. return strtoupper($prefix.str_repeat('0', $zeroLen).$num);
  2353. }else{
  2354. return strtoupper($prefix.$num);
  2355. }
  2356. }
  2357. /**
  2358. * 超出2038年的32位系统时间转换
  2359. * @param $unixtime 时间戳
  2360. * @param string $timezone 时间格式
  2361. * @param string $dateFormat 转换格式
  2362. * @return string
  2363. */
  2364. function unixtimeToDate($dateFormat='', $unixtime=0, $timezone = 'PRC') {
  2365. $unixtime = $unixtime? $unixtime : time();
  2366. $datetime = new \DateTime("@$unixtime"); //DateTime类的bug,加入@可以将Unix时间戳作为参数传入
  2367. $datetime->setTimezone(new \DateTimeZone($timezone));
  2368. $dateFormat = $dateFormat? $dateFormat : 'Y-m-d H:i:s';
  2369. return $datetime->format($dateFormat);
  2370. }
  2371. /**
  2372. * 超出2038年的32位系统时间转换时间戳
  2373. * @param $date 时间
  2374. * @param string $timezone
  2375. * @return string
  2376. */
  2377. function dateToUnixtime($date, $timezone = 'PRC') {
  2378. $datetime= new \DateTime($date, new \DateTimeZone($timezone));
  2379. return $datetime->format('U');
  2380. }
  2381. /**
  2382. * 计算两点之间的距离
  2383. * @param $lng1 经度1
  2384. * @param $lat1 纬度1
  2385. * @param $lng2 经度2
  2386. * @param $lat2 纬度2
  2387. * @param int $unit m,km
  2388. * @param int $decimal 位数
  2389. * @return float
  2390. */
  2391. function getDistance($lng1, $lat1, $lng2, $lat2, $unit = 2, $decimal = 2)
  2392. {
  2393. $EARTH_RADIUS = 6370.996; // 地球半径系数
  2394. $PI = 3.1415926535898;
  2395. $radLat1 = $lat1 * $PI / 180.0;
  2396. $radLat2 = $lat2 * $PI / 180.0;
  2397. $radLng1 = $lng1 * $PI / 180.0;
  2398. $radLng2 = $lng2 * $PI / 180.0;
  2399. $a = $radLat1 - $radLat2;
  2400. $b = $radLng1 - $radLng2;
  2401. $distance = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2)));
  2402. $distance = $distance * $EARTH_RADIUS * 1000;
  2403. if ($unit === 2) {
  2404. $distance /= 1000;
  2405. }
  2406. return round($distance, $decimal);
  2407. }
  2408. /**
  2409. * 获取星座
  2410. * @param $date 如 01-01
  2411. * @return string
  2412. */
  2413. function getStart($date)
  2414. {
  2415. if ($date >= '03-21' && $date <= '04-19') {
  2416. return '白羊座';
  2417. } else if ($date >= '04-20' && $date <= '05-20') {
  2418. return '金牛座';
  2419. } else if ($date >= '05-21' && $date <= '06-21') {
  2420. return '双子座';
  2421. } else if ($date >= '06-22' && $date <= '07-22') {
  2422. return '巨蟹座';
  2423. } else if ($date >= '07-23' && $date <= '08-22') {
  2424. return '狮子座';
  2425. } else if ($date >= '08-23' && $date <= '09-22') {
  2426. return '处女座';
  2427. } else if ($date >= '09-23' && $date <= '10-23') {
  2428. return '天秤座';
  2429. } else if ($date >= '10-24' && $date <= '11-22') {
  2430. return '天蝎座';
  2431. } else if ($date >= '11-23' && $date <= '12-21') {
  2432. return '射手座';
  2433. } else if (($date >= '12-22' && $date<='12-31') || ($date >= '01-01' && $date <= '01-19')) {
  2434. return '摩羯座';
  2435. } else if ($date >= '01-20' && $date <= '02-18') {
  2436. return '水瓶座';
  2437. } else if ($date >= '02-19' && $date <= '03-20') {
  2438. return '双鱼座';
  2439. }
  2440. }
  2441. /**
  2442. * 格式化时间段
  2443. * @param int $time 时间戳
  2444. * @return string 输出格式化时间
  2445. * @author wesmiler
  2446. * @date 2019/5/23
  2447. */
  2448. function format_time($time)
  2449. {
  2450. $interval = time() - $time;
  2451. $format = array(
  2452. '31536000' => '年',
  2453. '2592000' => '个月',
  2454. '604800' => '星期',
  2455. '86400' => '天',
  2456. '3600' => '小时',
  2457. '60' => '分钟',
  2458. '1' => '秒',
  2459. );
  2460. foreach ($format as $key => $val) {
  2461. $match = floor($interval / (int)$key);
  2462. if (0 != $match) {
  2463. return $match . $val . '前';
  2464. }
  2465. }
  2466. return date('Y-m-d', $time);
  2467. }