common.php 72 KB

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