common.php 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2013-2017 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 cmf\lib\Storage;
  12. use dir\Dir;
  13. use huo\controller\agent\AgentCache;
  14. use huo\controller\wap\Option;
  15. use huolib\constant\CommonConst;
  16. use huolib\constant\OptionConst;
  17. use think\Config;
  18. use think\Db;
  19. use think\Loader;
  20. use think\Request;
  21. use think\Route;
  22. use think\Url;
  23. // 应用公共文件
  24. //设置插件入口路由
  25. Route::any('plugin/[:_plugin]/[:_controller]/[:_action]', "\\cmf\\controller\\PluginController@index");
  26. Route::get('captcha/new', "\\cmf\\controller\\CaptchaController@index");
  27. /**
  28. * 获取当前登录的管理员ID
  29. *
  30. * @return int
  31. */
  32. function cmf_get_current_admin_id() {
  33. return session('ADMIN_ID');
  34. }
  35. /**
  36. * 判断前台用户是否登录
  37. *
  38. * @return boolean
  39. */
  40. function cmf_is_user_login() {
  41. $sessionUser = session('user');
  42. return !empty($sessionUser);
  43. }
  44. /**
  45. * 获取当前登录的前台用户的信息,未登录时,返回false
  46. *
  47. * @return array|boolean
  48. */
  49. function cmf_get_current_user() {
  50. $sessionUser = session('user');
  51. if (!empty($sessionUser)) {
  52. return $sessionUser;
  53. } else {
  54. return false;
  55. }
  56. }
  57. /**
  58. * 更新当前登录前台用户的信息
  59. *
  60. * @param array $user 前台用户的信息
  61. */
  62. function cmf_update_current_user($user) {
  63. session('user', $user);
  64. }
  65. /**
  66. * 获取当前登录前台用户id
  67. *
  68. * @return int
  69. */
  70. function cmf_get_current_user_id() {
  71. $sessionUserId = session('user.id');
  72. if (empty($sessionUserId)) {
  73. return 0;
  74. }
  75. return $sessionUserId;
  76. }
  77. /**
  78. * 返回带协议的域名
  79. */
  80. function cmf_get_domain() {
  81. $request = Request::instance();
  82. return $request->domain();
  83. }
  84. /**
  85. * 获取网站根目录
  86. *
  87. * @return string 网站根目录
  88. */
  89. function cmf_get_root() {
  90. $request = Request::instance();
  91. $root = $request->root();
  92. $root = preg_replace('/\/.*\.php/', '', $root);
  93. if (defined('APP_NAMESPACE') && APP_NAMESPACE == 'api') {
  94. $root = preg_replace('/\/api$/', '', $root);
  95. $root = rtrim($root, '/');
  96. }
  97. return $root;
  98. }
  99. /**
  100. * 获取当前主题名
  101. *
  102. * @return string
  103. */
  104. function cmf_get_current_theme() {
  105. static $_currentTheme;
  106. if (!empty($_currentTheme)) {
  107. return $_currentTheme;
  108. }
  109. $t = 't';
  110. $theme = config('cmf_default_theme');
  111. $cmfDetectTheme = config('cmf_detect_theme');
  112. if ($cmfDetectTheme) {
  113. if (isset($_GET[$t])) {
  114. $theme = $_GET[$t];
  115. cookie('cmf_template', $theme, CommonConst::CONST_DAY_SECONDS);
  116. } elseif (cookie('cmf_template')) {
  117. $theme = cookie('cmf_template');
  118. }
  119. }
  120. $hookTheme = hook_one('switch_theme');
  121. if ($hookTheme) {
  122. $theme = $hookTheme;
  123. }
  124. $_currentTheme = $theme;
  125. return $theme;
  126. }
  127. /**
  128. * 获取前台模板根目录
  129. *
  130. * @param string $theme
  131. *
  132. * @return string 前台模板根目录
  133. */
  134. function cmf_get_theme_path($theme = null) {
  135. $themePath = config('cmf_theme_path');
  136. if ($theme === null) {
  137. // 获取当前主题名称
  138. $theme = cmf_get_current_theme();
  139. }
  140. return './'.$themePath.$theme;
  141. }
  142. /**
  143. * 获取用户头像地址
  144. *
  145. * @param $avatar 用户头像文件路径,相对于 upload 目录
  146. *
  147. * @return string
  148. */
  149. function cmf_get_user_avatar_url($avatar) {
  150. if (!empty($avatar)) {
  151. if (strpos($avatar, "http") === 0) {
  152. return $avatar;
  153. } else {
  154. if (strpos($avatar, 'avatar/') === false) {
  155. $avatar = 'avatar/'.$avatar;
  156. }
  157. return cmf_get_image_url($avatar, 'avatar');
  158. }
  159. } else {
  160. return $avatar;
  161. }
  162. }
  163. /**
  164. * CMF密码加密方法
  165. *
  166. * @param string $pw 要加密的原始密码
  167. * @param string $authCode 加密字符串
  168. *
  169. * @return string
  170. */
  171. function cmf_password($pw, $authCode = '') {
  172. if (empty($authCode)) {
  173. $authCode = Config::get('database.authcode');
  174. }
  175. $result = "###".md5(md5($authCode.$pw));
  176. return $result;
  177. }
  178. /**
  179. * 判断是否加密密码
  180. *
  181. * @param $pwd
  182. *
  183. * @return bool
  184. */
  185. function cmf_pwd_is_encrypt($pwd) {
  186. if (0 === strpos($pwd, '###') && strlen($pwd) > 32) {
  187. return true;
  188. }
  189. return false;
  190. }
  191. /**
  192. * CMF密码加密方法 (X2.0.0以前的方法)
  193. *
  194. * @param string $pw 要加密的原始密码
  195. *
  196. * @return string
  197. */
  198. function cmf_password_old($pw) {
  199. $decor = md5(Config::get('database.prefix'));
  200. $mi = md5($pw);
  201. return substr($decor, 0, 12).$mi.substr($decor, -4, 4);
  202. }
  203. /**
  204. * CMF密码比较方法,所有涉及密码比较的地方都用这个方法
  205. *
  206. * @param string $password 要比较的密码
  207. * @param string $passwordInDb 数据库保存的已经加密过的密码
  208. *
  209. * @return boolean 密码相同,返回true
  210. */
  211. function cmf_compare_password($password, $passwordInDb) {
  212. if (strpos($passwordInDb, "###") === 0) {
  213. return cmf_password($password) == $passwordInDb;
  214. } else {
  215. return cmf_password_old($password) == $passwordInDb;
  216. }
  217. }
  218. /**
  219. * 文件日志
  220. *
  221. * @param $content 要写入的内容
  222. * @param string $file 日志文件,在web 入口目录
  223. */
  224. function cmf_log($content, $file = "log.txt") {
  225. file_put_contents($file, $content, FILE_APPEND);
  226. }
  227. /**
  228. * 随机字符串生成
  229. *
  230. * @param int $len 生成的字符串长度
  231. *
  232. * @return string
  233. */
  234. function cmf_random_string($len = 6) {
  235. $chars = [
  236. "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
  237. "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
  238. "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G",
  239. "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
  240. "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2",
  241. "3", "4", "5", "6", "7", "8", "9"
  242. ];
  243. $charsLen = count($chars) - 1;
  244. shuffle($chars); // 将数组打乱
  245. $output = "";
  246. for ($i = 0; $i < $len; $i++) {
  247. $output .= $chars[mt_rand(0, $charsLen)];
  248. }
  249. return $output;
  250. }
  251. /**
  252. * 清空系统缓存
  253. */
  254. function cmf_clear_cache() {
  255. $dirs = [];
  256. $rootDirs = cmf_scan_dir(RUNTIME_PATH."*");
  257. //$noNeedClear=array(".","..","Data");
  258. $noNeedClear = [".", ".."];
  259. $rootDirs = array_diff($rootDirs, $noNeedClear);
  260. foreach ($rootDirs as $dir) {
  261. if ($dir != "." && $dir != "..") {
  262. $dir = RUNTIME_PATH.$dir;
  263. if (is_dir($dir)) {
  264. //array_push ( $dirs, $dir );
  265. $tmpRootDirs = cmf_scan_dir($dir."/*");
  266. foreach ($tmpRootDirs as $tDir) {
  267. if ($tDir != "." && $tDir != "..") {
  268. $tDir = $dir.'/'.$tDir;
  269. if (is_dir($tDir)) {
  270. array_push($dirs, $tDir);
  271. } else {
  272. @unlink($tDir);
  273. }
  274. }
  275. }
  276. } else {
  277. @unlink($dir);
  278. }
  279. }
  280. }
  281. $dirTool = new Dir("");
  282. foreach ($dirs as $dir) {
  283. $dirTool->delDir($dir);
  284. }
  285. }
  286. /**
  287. * 保存数组变量到php文件
  288. *
  289. * @param string $path 保存路径
  290. * @param mixed $var 要保存的变量
  291. *
  292. * @return boolean 保存成功返回true,否则false
  293. */
  294. function cmf_save_var($path, $var) {
  295. $result = file_put_contents($path, "<?php\treturn ".var_export($var, true).";?>");
  296. return $result;
  297. }
  298. /**
  299. * 设置动态配置
  300. *
  301. * @param array $data <br>如:["cmf_default_theme"=>'simpleboot3'];
  302. *
  303. * @return boolean
  304. */
  305. function cmf_set_dynamic_config($data) {
  306. if (!is_array($data)) {
  307. return false;
  308. }
  309. $configFile = CMF_ROOT."data/conf/config.php";
  310. if (file_exists($configFile)) {
  311. $configs = include $configFile;
  312. } else {
  313. $configs = [];
  314. }
  315. $configs = array_merge($configs, $data);
  316. $result = file_put_contents($configFile, "<?php\treturn ".var_export($configs, true).";");
  317. cmf_clear_cache();
  318. return $result;
  319. }
  320. /**
  321. * 转化格式化的字符串为数组
  322. *
  323. * @param string $tag 要转化的字符串,格式如:"id:2;cid:1;order:post_date desc;"
  324. *
  325. * @return array 转化后字符串<pre>
  326. * array(
  327. * 'id'=>'2',
  328. * 'cid'=>'1',
  329. * 'order'=>'post_date desc'
  330. * )
  331. */
  332. function cmf_param_lable($tag = '') {
  333. $param = [];
  334. $array = explode(';', $tag);
  335. foreach ($array as $v) {
  336. $v = trim($v);
  337. if (!empty($v)) {
  338. list($key, $val) = explode(':', $v);
  339. $param[trim($key)] = trim($val);
  340. }
  341. }
  342. return $param;
  343. }
  344. /**
  345. * 获取后台管理设置的网站信息,此类信息一般用于前台
  346. *
  347. * @return array
  348. */
  349. function cmf_get_site_info($agent_id = 0) {
  350. $_base_site_Info = cmf_get_option(OptionConst::WAP_BASIC);
  351. $_base_site_icon = cmf_get_option(OptionConst::WAP_BASIC_ICON);
  352. $_siteInfo = array_merge($_base_site_Info, $_base_site_icon);
  353. if (!empty($agent_id)) {
  354. $_agent_data = AgentCache::ins()->getInfoByAgentId($agent_id);
  355. $_siteInfo[OptionConst::WAP_BASIC_SITE_NAME] = $_agent_data['site_name'];
  356. $_siteInfo[OptionConst::WAP_BASIC_ICON_SITE_LOGO] = $_agent_data['logo'];
  357. $_siteInfo[OptionConst::WAP_BASIC_ICON_SITE_QR_IMG] = $_agent_data['wx_qr'];
  358. $_siteInfo[OptionConst::WAP_BASIC_SDK_FLOAT_LOGO] = $_agent_data['float_icon'];
  359. }
  360. if (!empty($_siteInfo[OptionConst::WAP_BASIC_ICON_SITE_LOGO])) {
  361. $_siteInfo[OptionConst::WAP_BASIC_ICON_SITE_LOGO] = cmf_get_image_url(
  362. $_siteInfo[OptionConst::WAP_BASIC_ICON_SITE_LOGO]
  363. );
  364. }
  365. if (!empty($_siteInfo[OptionConst::WAP_BASIC_ICON_SITE_QR_IMG])) {
  366. $_siteInfo[OptionConst::WAP_BASIC_ICON_SITE_QR_IMG] = cmf_get_image_url(
  367. $_siteInfo[OptionConst::WAP_BASIC_ICON_SITE_QR_IMG]
  368. );
  369. }
  370. if (!empty($_siteInfo[OptionConst::WAP_BASIC_SDK_FLOAT_LOGO])) {
  371. $_siteInfo[OptionConst::WAP_BASIC_SDK_FLOAT_LOGO] = cmf_get_image_url(
  372. $_siteInfo[OptionConst::WAP_BASIC_SDK_FLOAT_LOGO]
  373. );
  374. }
  375. return $_siteInfo;
  376. }
  377. /**
  378. * 获取CMF系统的设置,此类设置用于全局
  379. *
  380. * @return array
  381. */
  382. function cmf_get_cmf_setting() {
  383. return cmf_get_option('cmf_setting');
  384. }
  385. /**
  386. * 更新CMF系统的设置,此类设置用于全局
  387. *
  388. * @param array $data
  389. *
  390. * @return boolean
  391. */
  392. function cmf_set_cmf_setting($data) {
  393. if (!is_array($data) || empty($data)) {
  394. return false;
  395. }
  396. return cmf_set_option('cmf_setting', $data);
  397. }
  398. /**
  399. * 设置系统配置,通用
  400. *
  401. * @param string $key 配置键值,都小写
  402. * @param array $data 配置值,数组
  403. * @param bool $replace 是否完全替换
  404. *
  405. * @return bool 是否成功
  406. */
  407. function cmf_set_option($key, $data, $replace = false) {
  408. if (!is_array($data) || empty($data) || !is_string($key) || empty($key)) {
  409. return false;
  410. }
  411. $key = strtolower($key);
  412. $option = [];
  413. $findOption = Db::name('option')->where('option_name', $key)->find();
  414. if ($findOption) {
  415. if (!$replace) {
  416. $oldOptionValue = json_decode($findOption['option_value'], true);
  417. if (!empty($oldOptionValue)) {
  418. $data = array_merge($oldOptionValue, $data);
  419. }
  420. }
  421. $option['option_value'] = json_encode($data);
  422. Db::name('option')->where('option_name', $key)->update($option);
  423. Db::name('option')->getLastSql();
  424. } else {
  425. $option['option_name'] = $key;
  426. $option['option_value'] = json_encode($data);
  427. Db::name('option')->insert($option);
  428. }
  429. cache('cmf_options_'.$key, null);//删除缓存
  430. return true;
  431. }
  432. /**
  433. * 获取系统配置,通用
  434. *
  435. * @param string $key 配置键值,都小写
  436. *
  437. * @return array
  438. */
  439. function cmf_get_option($key) {
  440. if (!is_string($key) || empty($key)) {
  441. return [];
  442. }
  443. static $cmfGetOption;
  444. if (empty($cmfGetOption)) {
  445. $cmfGetOption = [];
  446. } else {
  447. if (!empty($cmfGetOption[$key])) {
  448. return $cmfGetOption[$key];
  449. }
  450. }
  451. $optionValue = Option::ins()->getValue($key);
  452. // $optionValue = cache('option_name_key_'.$key);
  453. // if (empty($optionValue)) {
  454. // $optionValue = Db::name('option')->where('option_name', $key)->value('option_value');
  455. // if (!empty($optionValue)) {
  456. // $optionValue = json_decode($optionValue, true);
  457. // cache('option_name_key_'.$key, $optionValue);
  458. // }
  459. // }
  460. $cmfGetOption[$key] = $optionValue;
  461. return $optionValue;
  462. }
  463. /**
  464. * 获取CMF上传配置
  465. */
  466. function cmf_get_upload_setting() {
  467. $uploadSetting = cmf_get_option('upload_setting');
  468. if (empty($uploadSetting) || empty($uploadSetting['file_types'])) {
  469. $uploadSetting = [
  470. 'file_types' => [
  471. 'image' => [
  472. 'upload_max_filesize' => '10240',//单位KB
  473. 'extensions' => 'jpg,jpeg,png,gif,bmp4,ico'
  474. ],
  475. 'video' => [
  476. 'upload_max_filesize' => '10240',
  477. 'extensions' => 'mp4,avi,wmv,rm,rmvb,mkv'
  478. ],
  479. 'audio' => [
  480. 'upload_max_filesize' => '10240',
  481. 'extensions' => 'mp3,wma,wav'
  482. ],
  483. 'file' => [
  484. 'upload_max_filesize' => '10240',
  485. 'extensions' => 'txt,pdf,doc,docx,xls,xlsx,ppt,pptx,zip,rar,apk,pem'
  486. ]
  487. ],
  488. 'chunk_size' => 512,//单位KB
  489. 'max_files' => 20 //最大同时上传文件数
  490. ];
  491. }
  492. if (empty($uploadSetting['upload_max_filesize'])) {
  493. $uploadMaxFileSizeSetting = [];
  494. foreach ($uploadSetting['file_types'] as $setting) {
  495. $extensions = explode(',', trim($setting['extensions']));
  496. if (!empty($extensions)) {
  497. $uploadMaxFileSize = intval($setting['upload_max_filesize']) * 1024;//转化成B
  498. foreach ($extensions as $ext) {
  499. if (!isset($uploadMaxFileSizeSetting[$ext])
  500. || $uploadMaxFileSize > $uploadMaxFileSizeSetting[$ext] * 1024) {
  501. $uploadMaxFileSizeSetting[$ext] = $uploadMaxFileSize;
  502. }
  503. }
  504. }
  505. }
  506. $uploadSetting['upload_max_filesize'] = $uploadMaxFileSizeSetting;
  507. }
  508. return $uploadSetting;
  509. }
  510. /**
  511. * 获取html文本里的img
  512. *
  513. * @param string $content html 内容
  514. *
  515. * @return array 图片列表 数组item格式<pre>
  516. * [
  517. * "src"=>'图片链接',
  518. * "title"=>'图片标签的 title 属性',
  519. * "alt"=>'图片标签的 alt 属性'
  520. * ]
  521. * </pre>
  522. */
  523. function cmf_get_content_images($content) {
  524. import('phpQuery.phpQuery', EXTEND_PATH);
  525. \phpQuery::newDocumentHTML($content);
  526. $pq = pq(null);
  527. $images = $pq->find("img");
  528. $imagesData = [];
  529. if ($images->length) {
  530. foreach ($images as $img) {
  531. $img = pq($img);
  532. $image = [];
  533. $image['src'] = $img->attr("src");
  534. $image['title'] = $img->attr("title");
  535. $image['alt'] = $img->attr("alt");
  536. array_push($imagesData, $image);
  537. }
  538. }
  539. \phpQuery::$documents = null;
  540. return $imagesData;
  541. }
  542. /**
  543. * 去除字符串中的指定字符
  544. *
  545. * @param string $str 待处理字符串
  546. * @param string $chars 需去掉的特殊字符
  547. *
  548. * @return string
  549. */
  550. function cmf_strip_chars($str, $chars = '?<*.>\'\"') {
  551. return preg_replace('/['.$chars.']/is', '', $str);
  552. }
  553. /**
  554. * 发送邮件
  555. *
  556. * @param string $address 收件人邮箱
  557. * @param string $subject 邮件标题
  558. * @param string $message 邮件内容
  559. *
  560. * @return array<br>
  561. * 返回格式:<br>
  562. * array(<br>
  563. * "error"=>0|1,//0代表出错<br>
  564. * "message"=> "出错信息"<br>
  565. * );
  566. */
  567. function cmf_send_email($address, $subject, $message) {
  568. $smtpSetting = cmf_get_option('smtp_setting');
  569. $mail = new \PHPMailer();
  570. // 设置PHPMailer使用SMTP服务器发送Email
  571. $mail->IsSMTP();
  572. $mail->IsHTML(true);
  573. //$mail->SMTPDebug = 3;
  574. // 设置邮件的字符编码,若不指定,则为'UTF-8'
  575. $mail->CharSet = 'UTF-8';
  576. // 添加收件人地址,可以多次使用来添加多个收件人
  577. $mail->AddAddress($address);
  578. // 设置邮件正文
  579. $mail->Body = $message;
  580. // 设置邮件头的From字段。
  581. $mail->From = $smtpSetting['from'];
  582. // 设置发件人名字
  583. $mail->FromName = $smtpSetting['from_name'];
  584. // 设置邮件标题
  585. $mail->Subject = $subject;
  586. // 设置SMTP服务器。
  587. $mail->Host = $smtpSetting['host'];
  588. //by Rainfer
  589. // 设置SMTPSecure。
  590. $Secure = $smtpSetting['smtp_secure'];
  591. $mail->SMTPSecure = empty($Secure) ? '' : $Secure;
  592. // 设置SMTP服务器端口。
  593. $port = $smtpSetting['port'];
  594. $mail->Port = empty($port) ? "25" : $port;
  595. // 设置为"需要验证"
  596. $mail->SMTPAuth = true;
  597. $mail->SMTPAutoTLS = false;
  598. $mail->Timeout = 10;
  599. // 设置用户名和密码。
  600. $mail->Username = $smtpSetting['username'];
  601. $mail->Password = $smtpSetting['password'];
  602. // 发送邮件。
  603. if (!$mail->Send()) {
  604. $mailError = $mail->ErrorInfo;
  605. return ["error" => 1, "message" => $mailError];
  606. } else {
  607. return ["error" => 0, "message" => "success"];
  608. }
  609. }
  610. /**
  611. * 转化数据库保存的文件路径,为可以访问的url
  612. *
  613. * @param string $file
  614. * @param mixed $style 图片样式,支持各大云存储
  615. *
  616. * @return string
  617. */
  618. function cmf_get_asset_url($file, $style = '') {
  619. if (strpos($file, "http") === 0) {
  620. return $file;
  621. } else if (strpos($file, "/") === 0) {
  622. return $file;
  623. } else {
  624. $storage = Storage::instance();
  625. return $storage->getUrl($file, $style);
  626. }
  627. }
  628. /**
  629. * 转化数据库保存图片的文件路径,为可以访问的url
  630. *
  631. * @param string $file 文件路径,数据存储的文件相对路径
  632. * @param string $style 图片样式,支持各大云存储
  633. *
  634. * @return string 图片链接
  635. */
  636. function cmf_get_image_url($file, $style = '') {
  637. if (empty($file)) {
  638. return '';
  639. }
  640. if (strpos($file, "http") === 0 || strpos($file, "https") === 0 || strpos($file, "//") === 0) {
  641. return $file;
  642. } else if (strpos($file, "/") === 0) {
  643. return STATICSITE.$file;
  644. } else {
  645. $storage = Storage::instance();
  646. return $storage->getImageUrl($file, $style);
  647. }
  648. }
  649. /**
  650. * 获取图片预览链接
  651. *
  652. * @param string $file 文件路径,相对于upload
  653. * @param string $style 图片样式,支持各大云存储
  654. *
  655. * @return string
  656. */
  657. function cmf_get_image_preview_url($file, $style = 'watermark') {
  658. if (empty($file)) {
  659. return '';
  660. }
  661. if (strpos($file, "http") === 0 || strpos($file, "https") === 0 || strpos($file, "//") === 0) {
  662. return $file;
  663. } else if (strpos($file, "/") === 0) {
  664. return STATICSITE.$file;
  665. } else {
  666. $storage = Storage::instance();
  667. return $storage->getPreviewUrl($file, $style);
  668. }
  669. }
  670. /**
  671. * 获取文件下载链接
  672. *
  673. * @param string $file 文件路径,数据库里保存的相对路径
  674. * @param int $expires 过期时间,单位 s
  675. *
  676. * @return string 文件链接
  677. */
  678. function cmf_get_file_download_url($file, $expires = 3600) {
  679. if (strpos($file, "http") === 0) {
  680. return $file;
  681. } else if (strpos($file, "/") === 0) {
  682. return $file;
  683. } else {
  684. $storage = Storage::instance();
  685. return $storage->getFileDownloadUrl($file, $expires);
  686. }
  687. }
  688. /**
  689. * 解密用cmf_str_encode加密的字符串
  690. *
  691. * @param $string 要解密的字符串
  692. * @param string $key 加密时salt
  693. * @param int $expiry 多少秒后过期
  694. * @param string $operation 操作,默认为DECODE
  695. *
  696. * @return bool|string
  697. */
  698. function cmf_str_decode($string, $key = '', $expiry = 0, $operation = 'DECODE') {
  699. $ckey_length = 4;
  700. $key = md5($key ? $key : config("authcode"));
  701. $keya = md5(substr($key, 0, 16));
  702. $keyb = md5(substr($key, 16, 16));
  703. $keyc = $ckey_length ? ($operation == 'DECODE'
  704. ? substr($string, 0, $ckey_length)
  705. : substr(
  706. md5(microtime()), -$ckey_length
  707. )) : '';
  708. $cryptkey = $keya.md5($keya.$keyc);
  709. $key_length = strlen($cryptkey);
  710. $string = $operation == 'DECODE'
  711. ? base64_decode(substr($string, $ckey_length))
  712. : sprintf(
  713. '%010d', $expiry ? $expiry + time() : 0
  714. ).substr(
  715. md5($string.$keyb), 0, 16
  716. ).$string;
  717. $string_length = strlen($string);
  718. $result = '';
  719. $box = range(0, 255);
  720. $rndkey = [];
  721. for ($i = 0; $i <= 255; $i++) {
  722. $rndkey[$i] = ord($cryptkey[$i % $key_length]);
  723. }
  724. for ($j = $i = 0; $i < 256; $i++) {
  725. $j = ($j + $box[$i] + $rndkey[$i]) % 256;
  726. $tmp = $box[$i];
  727. $box[$i] = $box[$j];
  728. $box[$j] = $tmp;
  729. }
  730. for ($a = $j = $i = 0; $i < $string_length; $i++) {
  731. $a = ($a + 1) % 256;
  732. $j = ($j + $box[$a]) % 256;
  733. $tmp = $box[$a];
  734. $box[$a] = $box[$j];
  735. $box[$j] = $tmp;
  736. $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
  737. }
  738. if ($operation == 'DECODE') {
  739. if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0)
  740. && substr($result, 10, 16) == substr(
  741. md5(substr($result, 26).$keyb), 0, 16
  742. )) {
  743. return substr($result, 26);
  744. } else {
  745. return '';
  746. }
  747. } else {
  748. return $keyc.str_replace('=', '', base64_encode($result));
  749. }
  750. }
  751. /**
  752. * 加密字符串
  753. *
  754. * @param $string 要加密的字符串
  755. * @param string $key salt
  756. * @param int $expiry 多少秒后过期
  757. *
  758. * @return bool|string
  759. */
  760. function cmf_str_encode($string, $key = '', $expiry = 0) {
  761. return cmf_str_decode($string, $key, $expiry, "ENCODE");
  762. }
  763. /**
  764. * 获取文件相对路径
  765. *
  766. * @param string $assetUrl 文件的URL
  767. *
  768. * @return string
  769. */
  770. function cmf_asset_relative_url($assetUrl) {
  771. if (strpos($assetUrl, "http") === 0) {
  772. return $assetUrl;
  773. } else {
  774. return str_replace('/upload/', '', $assetUrl);
  775. }
  776. }
  777. /**
  778. * 检查用户对某个url内容的可访问性,用于记录如是否赞过,是否访问过等等;开发者可以自由控制,对于没有必要做的检查可以不做,以减少服务器压力
  779. *
  780. * @param string $object 访问对象的id,格式:不带前缀的表名+id;如post1表示xx_post表里id为1的记录;如果object为空,表示只检查对某个url访问的合法性
  781. * @param int $countLimit 访问次数限制,如1,表示只能访问一次
  782. * @param boolean $ipLimit ip限制,false为不限制,true为限制
  783. * @param int $expire 距离上次访问的最小时间单位s,0表示不限制,大于0表示最后访问$expire秒后才可以访问
  784. *
  785. * @return true 可访问,false不可访问
  786. */
  787. function cmf_check_user_action($object = "", $countLimit = 1, $ipLimit = false, $expire = 0) {
  788. $request = request();
  789. $action = $request->module()."/".$request->controller()."/".$request->action();
  790. $userId = cmf_get_current_user_id();
  791. $ip = get_client_ip(0, true);//修复ip获取
  792. $where = ["user_id" => $userId, "action" => $action, "object" => $object];
  793. if ($ipLimit) {
  794. $where['ip'] = $ip;
  795. }
  796. $findLog = Db::name('user_action_log')->where($where)->find();
  797. $time = time();
  798. if ($findLog) {
  799. Db::name('user_action_log')->where($where)->update(
  800. [
  801. "count" => ["exp", "count+1"],
  802. "last_visit_time" => $time,
  803. "ip" => $ip
  804. ]
  805. );
  806. if ($findLog['count'] >= $countLimit) {
  807. return false;
  808. }
  809. if ($expire > 0 && ($time - $findLog['last_visit_time']) < $expire) {
  810. return false;
  811. }
  812. } else {
  813. Db::name('user_action_log')->insert(
  814. [
  815. "user_id" => $userId,
  816. "action" => $action,
  817. "object" => $object,
  818. "count" => ["exp", "count+1"],
  819. "last_visit_time" => $time, "ip" => $ip
  820. ]
  821. );
  822. }
  823. return true;
  824. }
  825. /**
  826. * 判断是否为手机访问
  827. *
  828. * @return boolean
  829. */
  830. function cmf_is_mobile() {
  831. static $cmf_is_mobile;
  832. if (isset($cmf_is_mobile)) {
  833. return $cmf_is_mobile;
  834. }
  835. $cmf_is_mobile = Request::instance()->isMobile();
  836. return $cmf_is_mobile;
  837. }
  838. /**
  839. * 判断是否为微信访问
  840. *
  841. * @return boolean
  842. */
  843. function cmf_is_wechat() {
  844. if (strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger') !== false) {
  845. return true;
  846. }
  847. return false;
  848. }
  849. /**
  850. * 添加钩子
  851. *
  852. * @param string $hook 钩子名称
  853. * @param mixed $params 传入参数
  854. * @param mixed $extra 额外参数
  855. *
  856. * @return void
  857. */
  858. function hook($hook, &$params = null, $extra = null) {
  859. return \think\Hook::listen($hook, $params, $extra);
  860. }
  861. /**
  862. * 添加钩子,只执行一个
  863. *
  864. * @param string $hook 钩子名称
  865. * @param mixed $params 传入参数
  866. * @param mixed $extra 额外参数
  867. *
  868. * @return mixed
  869. */
  870. function hook_one($hook, &$params = null, $extra = null) {
  871. return \think\Hook::listen($hook, $params, $extra, true);
  872. }
  873. /**
  874. * 获取插件类的类名
  875. *
  876. * @param string $name 插件名
  877. *
  878. * @return string
  879. */
  880. function cmf_get_plugin_class($name) {
  881. $name = ucwords($name);
  882. $pluginDir = cmf_parse_name($name);
  883. $class = "plugins\\{$pluginDir}\\{$name}Plugin";
  884. return $class;
  885. }
  886. /**
  887. * 获取插件类的配置
  888. *
  889. * @param string $name 插件名
  890. *
  891. * @return array
  892. */
  893. function cmf_get_plugin_config($name) {
  894. $class = cmf_get_plugin_class($name);
  895. if (class_exists($class)) {
  896. $plugin = new $class();
  897. return $plugin->getConfig();
  898. } else {
  899. return [];
  900. }
  901. }
  902. /**
  903. * 替代scan_dir的方法
  904. *
  905. * @param string $pattern 检索模式 搜索模式 *.txt,*.doc; (同glog方法)
  906. * @param int $flags
  907. * @param $pattern
  908. *
  909. * @return array
  910. */
  911. function cmf_scan_dir($pattern, $flags = null) {
  912. $files = glob($pattern, $flags);
  913. if (empty($files)) {
  914. $files = [];
  915. } else {
  916. $files = array_map('basename', $files);
  917. }
  918. return $files;
  919. }
  920. /**
  921. * 获取某个目录下所有子目录
  922. *
  923. * @param $dir
  924. *
  925. * @return array
  926. */
  927. function cmf_sub_dirs($dir) {
  928. $dir = ltrim($dir, "/");
  929. $dirs = [];
  930. $subDirs = cmf_scan_dir("$dir/*", GLOB_ONLYDIR);
  931. if (!empty($subDirs)) {
  932. foreach ($subDirs as $subDir) {
  933. $subDir = "$dir/$subDir";
  934. array_push($dirs, $subDir);
  935. $subDirSubDirs = cmf_sub_dirs($subDir);
  936. if (!empty($subDirSubDirs)) {
  937. $dirs = array_merge($dirs, $subDirSubDirs);
  938. }
  939. }
  940. }
  941. return $dirs;
  942. }
  943. /**
  944. * 生成访问插件的url
  945. *
  946. * @param string $url url格式:插件名://控制器名/方法
  947. * @param array $param 参数
  948. * @param bool $domain 是否显示域名 或者直接传入域名
  949. *
  950. * @return string
  951. */
  952. function cmf_plugin_url($url, $param = [], $domain = false) {
  953. $url = parse_url($url);
  954. $case_insensitive = true;
  955. $plugin = $case_insensitive ? Loader::parseName($url['scheme']) : $url['scheme'];
  956. $controller = $case_insensitive ? Loader::parseName($url['host']) : $url['host'];
  957. $action = trim($case_insensitive ? strtolower($url['path']) : $url['path'], '/');
  958. /* 解析URL带的参数 */
  959. if (isset($url['query'])) {
  960. parse_str($url['query'], $query);
  961. $param = array_merge($query, $param);
  962. }
  963. /* 基础参数 */
  964. $params = [
  965. '_plugin' => $plugin,
  966. '_controller' => $controller,
  967. '_action' => $action,
  968. ];
  969. $params = array_merge($params, $param); //添加额外参数
  970. return url('\\cmf\\controller\\PluginController@index', $params, true, $domain);
  971. }
  972. /**
  973. * 检查权限
  974. *
  975. * @param $userId int 要检查权限的用户 ID
  976. * @param $name string|array 需要验证的规则列表,支持逗号分隔的权限规则或索引数组
  977. * @param $relation string 如果为 'or' 表示满足任一条规则即通过验证;如果为 'and'则表示需满足所有规则才能通过验证
  978. *
  979. * @return boolean 通过验证返回true;失败返回false
  980. */
  981. function cmf_auth_check($userId, $name = null, $relation = 'or') {
  982. if (empty($userId)) {
  983. return false;
  984. }
  985. if ($userId == 1) {
  986. return true;
  987. }
  988. $authObj = new \cmf\lib\Auth();
  989. if (empty($name)) {
  990. $request = request();
  991. $module = $request->module();
  992. $controller = $request->controller();
  993. $action = $request->action();
  994. $name = strtolower($module."/".$controller."/".$action);
  995. }
  996. return $authObj->check($userId, $name, $relation);
  997. }
  998. function cmf_alpha_id($in, $to_num = false, $pad_up = 4, $passKey = null) {
  999. $index = "aBcDeFgHiJkLmNoPqRsTuVwXyZAbCdEfGhIjKlMnOpQrStUvWxYz0123456789";
  1000. if ($passKey !== null) {
  1001. // Although this function's purpose is to just make the
  1002. // ID short - and not so much secure,
  1003. // with this patch by Simon Franz (http://blog.snaky.org/)
  1004. // you can optionally supply a password to make it harder
  1005. // to calculate the corresponding numeric ID
  1006. for ($n = 0; $n < strlen($index); $n++) {
  1007. $i[] = substr($index, $n, 1);
  1008. }
  1009. $passhash = hash('sha256', $passKey);
  1010. $passhash = (strlen($passhash) < strlen($index)) ? hash('sha512', $passKey) : $passhash;
  1011. for ($n = 0; $n < strlen($index); $n++) {
  1012. $p[] = substr($passhash, $n, 1);
  1013. }
  1014. array_multisort($p, SORT_DESC, $i);
  1015. $index = implode($i);
  1016. }
  1017. $base = strlen($index);
  1018. if ($to_num) {
  1019. // Digital number <<-- alphabet letter code
  1020. $in = strrev($in);
  1021. $out = 0;
  1022. $len = strlen($in) - 1;
  1023. for ($t = 0; $t <= $len; $t++) {
  1024. $bcpow = pow($base, $len - $t);
  1025. $out = $out + strpos($index, substr($in, $t, 1)) * $bcpow;
  1026. }
  1027. if (is_numeric($pad_up)) {
  1028. $pad_up--;
  1029. if ($pad_up > 0) {
  1030. $out -= pow($base, $pad_up);
  1031. }
  1032. }
  1033. $out = sprintf('%F', $out);
  1034. $out = substr($out, 0, strpos($out, '.'));
  1035. } else {
  1036. // Digital number -->> alphabet letter code
  1037. if (is_numeric($pad_up)) {
  1038. $pad_up--;
  1039. if ($pad_up > 0) {
  1040. $in += pow($base, $pad_up);
  1041. }
  1042. }
  1043. $out = "";
  1044. for ($t = floor(log($in, $base)); $t >= 0; $t--) {
  1045. $bcp = pow($base, $t);
  1046. $a = floor($in / $bcp) % $base;
  1047. $out = $out.substr($index, $a, 1);
  1048. $in = $in - ($a * $bcp);
  1049. }
  1050. $out = strrev($out); // reverse
  1051. }
  1052. return $out;
  1053. }
  1054. /**
  1055. * 验证码检查,验证完后销毁验证码
  1056. *
  1057. * @param string $value
  1058. * @param string $id
  1059. *
  1060. * @return bool
  1061. */
  1062. function cmf_captcha_check($value, $id = "") {
  1063. $captcha = new \think\captcha\Captcha();
  1064. return $captcha->check($value, $id);
  1065. }
  1066. /**
  1067. * 切分SQL文件成多个可以单独执行的sql语句
  1068. *
  1069. * @param $file sql文件路径
  1070. * @param $tablePre 表前缀
  1071. * @param string $charset 字符集
  1072. * @param string $defaultTablePre 默认表前缀
  1073. * @param string $defaultCharset 默认字符集
  1074. *
  1075. * @return array
  1076. */
  1077. function cmf_split_sql($file, $tablePre, $charset = 'utf8mb4', $defaultTablePre = 'cmf_', $defaultCharset = 'utf8mb4') {
  1078. if (file_exists($file)) {
  1079. //读取SQL文件
  1080. $sql = file_get_contents($file);
  1081. $sql = str_replace("\r", "\n", $sql);
  1082. $sql = str_replace("BEGIN;\n", '', $sql);//兼容 navicat 导出的 insert 语句
  1083. $sql = str_replace("COMMIT;\n", '', $sql);//兼容 navicat 导出的 insert 语句
  1084. $sql = str_replace($defaultCharset, $charset, $sql);
  1085. $sql = trim($sql);
  1086. //替换表前缀
  1087. $sql = str_replace(" `{$defaultTablePre}", " `{$tablePre}", $sql);
  1088. $sqls = explode(";\n", $sql);
  1089. return $sqls;
  1090. }
  1091. return [];
  1092. }
  1093. /**
  1094. * 判断当前的语言包,并返回语言包名
  1095. *
  1096. * @return string 语言包名
  1097. */
  1098. function cmf_current_lang() {
  1099. return request()->langset();
  1100. }
  1101. /**
  1102. * 获取惟一订单号
  1103. *
  1104. * @return string
  1105. */
  1106. function cmf_get_order_sn() {
  1107. return date('Ymd').substr(implode(null, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
  1108. }
  1109. /**
  1110. * 获取文件扩展名
  1111. *
  1112. * @param string $filename 文件名
  1113. *
  1114. * @return string 文件扩展名
  1115. */
  1116. function cmf_get_file_extension($filename) {
  1117. $pathinfo = pathinfo($filename);
  1118. return strtolower($pathinfo['extension']);
  1119. }
  1120. /**
  1121. * 检查手机或邮箱是否还可以发送验证码,并返回生成的验证码
  1122. *
  1123. * @param string $account 手机或邮箱
  1124. * @param integer $length 验证码位数,支持4,6,8
  1125. *
  1126. * @return string 数字验证码
  1127. */
  1128. function cmf_get_verification_code($account, $length = 6) {
  1129. if (empty($account)) {
  1130. return false;
  1131. }
  1132. $verificationCodeQuery = Db::name('verification_code');
  1133. $currentTime = time();
  1134. $maxCount = 5;
  1135. $findVerificationCode = $verificationCodeQuery->where('account', $account)->find();
  1136. $result = false;
  1137. if (empty($findVerificationCode)) {
  1138. $result = true;
  1139. } else {
  1140. $sendTime = $findVerificationCode['send_time'];
  1141. $todayStartTime = strtotime(date('Y-m-d', $currentTime));
  1142. if ($sendTime < $todayStartTime) {
  1143. $result = true;
  1144. } else if ($findVerificationCode['count'] < $maxCount) {
  1145. $result = true;
  1146. }
  1147. }
  1148. if ($result) {
  1149. switch ($length) {
  1150. case 4:
  1151. $result = rand(1000, 9999);
  1152. break;
  1153. case 6:
  1154. $result = rand(100000, 999999);
  1155. break;
  1156. case 8:
  1157. $result = rand(10000000, 99999999);
  1158. break;
  1159. default:
  1160. $result = rand(100000, 999999);
  1161. }
  1162. }
  1163. return $result;
  1164. }
  1165. /**
  1166. * 更新手机或邮箱验证码发送日志
  1167. *
  1168. * @param string $account 手机或邮箱
  1169. * @param string $code 验证码
  1170. * @param int $expireTime 过期时间
  1171. *
  1172. * @return boolean
  1173. */
  1174. function cmf_verification_code_log($account, $code, $expireTime = 0) {
  1175. $currentTime = time();
  1176. $expireTime = $expireTime > $currentTime ? $expireTime : $currentTime + 30 * 60;
  1177. $verificationCodeQuery = Db::name('verification_code');
  1178. $findVerificationCode = $verificationCodeQuery->where('account', $account)->find();
  1179. if ($findVerificationCode) {
  1180. $todayStartTime = strtotime(date("Y-m-d"));//当天0点
  1181. if ($findVerificationCode['send_time'] <= $todayStartTime) {
  1182. $count = 1;
  1183. } else {
  1184. $count = ['exp', 'count+1'];
  1185. }
  1186. $result = $verificationCodeQuery
  1187. ->where('account', $account)
  1188. ->update(
  1189. [
  1190. 'send_time' => $currentTime,
  1191. 'expire_time' => $expireTime,
  1192. 'code' => $code,
  1193. 'count' => $count
  1194. ]
  1195. );
  1196. } else {
  1197. $result = $verificationCodeQuery
  1198. ->insert(
  1199. [
  1200. 'account' => $account,
  1201. 'send_time' => $currentTime,
  1202. 'code' => $code,
  1203. 'count' => 1,
  1204. 'expire_time' => $expireTime
  1205. ]
  1206. );
  1207. }
  1208. return $result;
  1209. }
  1210. /**
  1211. * 手机或邮箱验证码检查,验证完后销毁验证码增加安全性,返回true验证码正确,false验证码错误
  1212. *
  1213. * @param string $account 手机或邮箱
  1214. * @param string $code 验证码
  1215. * @param boolean $clear 是否验证后销毁验证码
  1216. *
  1217. * @return string 错误消息,空字符串代码验证码正确
  1218. */
  1219. function cmf_check_verification_code($account, $code, $clear = false) {
  1220. $verificationCodeQuery = Db::name('verification_code');
  1221. $findVerificationCode = $verificationCodeQuery->where('account', $account)->find();
  1222. if ($findVerificationCode) {
  1223. if ($findVerificationCode['expire_time'] > time()) {
  1224. if ($code == $findVerificationCode['code']) {
  1225. if ($clear) {
  1226. $verificationCodeQuery->where('account', $account)->update(['code' => '']);
  1227. }
  1228. } else {
  1229. return "验证码不正确!";
  1230. }
  1231. } else {
  1232. return "验证码已经过期,请先获取验证码!";
  1233. }
  1234. } else {
  1235. return "请先获取验证码!";
  1236. }
  1237. return "";
  1238. }
  1239. /**
  1240. * 清除某个手机或邮箱的数字验证码,一般在验证码验证正确完成后
  1241. *
  1242. * @param string $account 手机或邮箱
  1243. *
  1244. * @return boolean true:手机验证码正确,false:手机验证码错误
  1245. */
  1246. function cmf_clear_verification_code($account) {
  1247. $verificationCodeQuery = Db::name('verification_code');
  1248. $verificationCodeQuery->where('account', $account)->update(['code' => '']);
  1249. }
  1250. /**
  1251. * 区分大小写的文件存在判断
  1252. *
  1253. * @param string $filename 文件地址
  1254. *
  1255. * @return boolean
  1256. */
  1257. function file_exists_case($filename) {
  1258. if (is_file($filename)) {
  1259. if (IS_WIN && APP_DEBUG) {
  1260. if (basename(realpath($filename)) != basename($filename)) {
  1261. return false;
  1262. }
  1263. }
  1264. return true;
  1265. }
  1266. return false;
  1267. }
  1268. /**
  1269. * 生成用户 token
  1270. *
  1271. * @param $userId
  1272. * @param $deviceType
  1273. *
  1274. * @return string 用户 token
  1275. */
  1276. function cmf_generate_user_token($userId, $deviceType) {
  1277. $userTokenQuery = Db::name("user_token")
  1278. ->where('user_id', $userId)
  1279. ->where('device_type', $deviceType);
  1280. $findUserToken = $userTokenQuery->find();
  1281. $currentTime = time();
  1282. $expireTime = $currentTime + 24 * 3600 * 180;
  1283. $token = md5(uniqid()).md5(uniqid());
  1284. if (empty($findUserToken)) {
  1285. Db::name("user_token")->insert(
  1286. [
  1287. 'token' => $token,
  1288. 'user_id' => $userId,
  1289. 'expire_time' => $expireTime,
  1290. 'create_time' => $currentTime,
  1291. 'device_type' => $deviceType
  1292. ]
  1293. );
  1294. } else {
  1295. Db::name("user_token")
  1296. ->where('user_id', $userId)
  1297. ->where('device_type', $deviceType)
  1298. ->update(
  1299. [
  1300. 'token' => $token,
  1301. 'expire_time' => $expireTime,
  1302. 'create_time' => $currentTime
  1303. ]
  1304. );
  1305. }
  1306. return $token;
  1307. }
  1308. /**
  1309. * 字符串命名风格转换
  1310. * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
  1311. *
  1312. * @param string $name 字符串
  1313. * @param integer $type 转换类型
  1314. * @param bool $ucfirst 首字母是否大写(驼峰规则)
  1315. *
  1316. * @return string
  1317. */
  1318. function cmf_parse_name($name, $type = 0, $ucfirst = true) {
  1319. return Loader::parseName($name, $type, $ucfirst);
  1320. }
  1321. /**
  1322. * 判断字符串是否为已经序列化过
  1323. *
  1324. * @param $str
  1325. *
  1326. * @return bool
  1327. */
  1328. function cmf_is_serialized($str) {
  1329. return ($str == serialize(false) || @unserialize($str) !== false);
  1330. }
  1331. /**
  1332. * 判断是否SSL协议
  1333. *
  1334. * @return boolean
  1335. */
  1336. function cmf_is_ssl() {
  1337. if (isset($_SERVER['HTTPS']) && ('1' == $_SERVER['HTTPS'] || 'on' == strtolower($_SERVER['HTTPS']))) {
  1338. return true;
  1339. } elseif (isset($_SERVER['SERVER_PORT']) && ('443' == $_SERVER['SERVER_PORT'])) {
  1340. return true;
  1341. }
  1342. return false;
  1343. }
  1344. /**
  1345. * 获取CMF系统的设置,此类设置用于全局
  1346. *
  1347. * @param string $key 设置key,为空时返回所有配置信息
  1348. *
  1349. * @return mixed
  1350. */
  1351. function cmf_get_cmf_settings($key = "") {
  1352. $cmfSettings = cache("cmf_settings");
  1353. if (empty($cmfSettings)) {
  1354. $objOptions = new \huo\model\option\OptionModel();
  1355. $objResult = $objOptions->where("option_name", 'cmf_settings')->find();
  1356. $arrOption = $objResult ? $objResult->toArray() : [];
  1357. if ($arrOption) {
  1358. $cmfSettings = json_decode($arrOption['option_value'], true);
  1359. } else {
  1360. $cmfSettings = [];
  1361. }
  1362. cache("cmf_settings", $cmfSettings);
  1363. }
  1364. if (!empty($key)) {
  1365. if (isset($cmfSettings[$key])) {
  1366. return $cmfSettings[$key];
  1367. } else {
  1368. return false;
  1369. }
  1370. }
  1371. return $cmfSettings;
  1372. }
  1373. /**
  1374. * 判读是否sae环境
  1375. *
  1376. * @return bool
  1377. */
  1378. function cmf_is_sae() {
  1379. if (function_exists('saeAutoLoader')) {
  1380. return true;
  1381. } else {
  1382. return false;
  1383. }
  1384. }
  1385. /**
  1386. * 获取客户端IP地址
  1387. *
  1388. * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
  1389. * @param boolean $adv 是否进行高级模式获取(有可能被伪装)
  1390. *
  1391. * @return string
  1392. */
  1393. function get_client_ip($type = 0, $adv = false) {
  1394. return request()->ip($type, $adv);
  1395. }
  1396. /**
  1397. * 生成base64的url,用于数据库存放 url
  1398. *
  1399. * @param $url 路由地址,如 控制器/方法名,应用/控制器/方法名
  1400. * @param $params url参数
  1401. *
  1402. * @return string
  1403. */
  1404. function cmf_url_encode($url, $params) {
  1405. // 解析参数
  1406. if (is_string($params)) {
  1407. // aaa=1&bbb=2 转换成数组
  1408. parse_str($params, $params);
  1409. }
  1410. return base64_encode(json_encode(['action' => $url, 'param' => $params]));
  1411. }
  1412. /**
  1413. * CMF Url生成
  1414. *
  1415. * @param string $url 路由地址
  1416. * @param string|array $vars 变量
  1417. * @param bool|string $suffix 生成的URL后缀
  1418. * @param bool|string $domain 域名
  1419. *
  1420. * @return string
  1421. */
  1422. function cmf_url($url = '', $vars = '', $suffix = true, $domain = false) {
  1423. static $routes;
  1424. if (empty($routes)) {
  1425. $routeModel = new \huo\model\RouteModel();
  1426. $routes = $routeModel->getRoutes();
  1427. }
  1428. if (false === strpos($url, '://') && 0 !== strpos($url, '/')) {
  1429. $info = parse_url($url);
  1430. $url = !empty($info['path']) ? $info['path'] : '';
  1431. if (isset($info['fragment'])) {
  1432. // 解析锚点
  1433. $anchor = $info['fragment'];
  1434. if (false !== strpos($anchor, '?')) {
  1435. // 解析参数
  1436. list($anchor, $info['query']) = explode('?', $anchor, 2);
  1437. }
  1438. if (false !== strpos($anchor, '@')) {
  1439. // 解析域名
  1440. list($anchor, $domain) = explode('@', $anchor, 2);
  1441. }
  1442. } elseif (strpos($url, '@') && false === strpos($url, '\\')) {
  1443. // 解析域名
  1444. list($url, $domain) = explode('@', $url, 2);
  1445. }
  1446. }
  1447. // 解析参数
  1448. if (is_string($vars)) {
  1449. // aaa=1&bbb=2 转换成数组
  1450. parse_str($vars, $vars);
  1451. }
  1452. if (isset($info['query'])) {
  1453. // 解析地址里面参数 合并到vars
  1454. parse_str($info['query'], $params);
  1455. $vars = array_merge($params, $vars);
  1456. }
  1457. if (!empty($vars) && !empty($routes[$url])) {
  1458. foreach ($routes[$url] as $actionRoute) {
  1459. $sameVars = array_intersect_assoc($vars, $actionRoute['vars']);
  1460. if (count($sameVars) == count($actionRoute['vars'])) {
  1461. ksort($sameVars);
  1462. $url = $url.'?'.http_build_query($sameVars);
  1463. $vars = array_diff_assoc($vars, $sameVars);
  1464. break;
  1465. }
  1466. }
  1467. }
  1468. if (!empty($anchor)) {
  1469. $url = $url.'#'.$anchor;
  1470. }
  1471. if (!empty($domain)) {
  1472. $url = $url.'@'.$domain;
  1473. }
  1474. return Url::build($url, $vars, $suffix, $domain);
  1475. }
  1476. /**
  1477. * 判断 cmf 核心是否安装
  1478. *
  1479. * @return bool
  1480. */
  1481. function cmf_is_installed() {
  1482. static $cmfIsInstalled;
  1483. if (empty($cmfIsInstalled)) {
  1484. $cmfIsInstalled = file_exists(CMF_ROOT.'data/install.lock');
  1485. }
  1486. return $cmfIsInstalled;
  1487. }
  1488. /**
  1489. * 替换编辑器内容中的文件地址
  1490. *
  1491. * @param string $content 编辑器内容
  1492. * @param boolean $isForDbSave true:表示把绝对地址换成相对地址,用于数据库保存,false:表示把相对地址换成绝对地址用于界面显示
  1493. *
  1494. * @return string
  1495. */
  1496. function cmf_replace_content_file_url($content, $isForDbSave = false) {
  1497. import('phpQuery.phpQuery', EXTEND_PATH);
  1498. \phpQuery::newDocumentHTML($content);
  1499. $pq = pq(null);
  1500. $storage = Storage::instance();
  1501. $localStorage = new cmf\lib\storage\Local([]);
  1502. $storageDomain = $storage->getDomain();
  1503. $domain = request()->host();
  1504. $images = $pq->find("img");
  1505. if ($images->length) {
  1506. foreach ($images as $img) {
  1507. $img = pq($img);
  1508. $imgSrc = $img->attr("src");
  1509. if ($isForDbSave) {
  1510. if (preg_match("/^\/upload\//", $imgSrc)) {
  1511. $img->attr("src", preg_replace("/^\/upload\//", '', $imgSrc));
  1512. } elseif (preg_match("/^http(s)?:\/\/$domain\/upload\//", $imgSrc)) {
  1513. $img->attr("src", $localStorage->getFilePath($imgSrc));
  1514. } elseif (preg_match("/^http(s)?:\/\/$storageDomain\//", $imgSrc)) {
  1515. $img->attr("src", $storage->getFilePath($imgSrc));
  1516. }
  1517. } else {
  1518. $img->attr("src", cmf_get_image_url($imgSrc));
  1519. }
  1520. }
  1521. }
  1522. $links = $pq->find("a");
  1523. if ($links->length) {
  1524. foreach ($links as $link) {
  1525. $link = pq($link);
  1526. $href = $link->attr("href");
  1527. if ($isForDbSave) {
  1528. if (preg_match("/^\/upload\//", $href)) {
  1529. $link->attr("href", preg_replace("/^\/upload\//", '', $href));
  1530. } elseif (preg_match("/^http(s)?:\/\/$domain\/upload\//", $href)) {
  1531. $link->attr("href", $localStorage->getFilePath($href));
  1532. } elseif (preg_match("/^http(s)?:\/\/$storageDomain\//", $href)) {
  1533. $link->attr("href", $storage->getFilePath($href));
  1534. }
  1535. } else {
  1536. if (!(preg_match("/^\//", $href) || preg_match("/^http/", $href))) {
  1537. $link->attr("href", cmf_get_file_download_url($href));
  1538. }
  1539. }
  1540. }
  1541. }
  1542. $content = $pq->html();
  1543. \phpQuery::$documents = null;
  1544. return $content;
  1545. }
  1546. /**
  1547. * 获取后台风格名称
  1548. *
  1549. * @return string
  1550. */
  1551. function cmf_get_admin_style() {
  1552. $adminSettings = cmf_get_option('admin_settings');
  1553. return empty($adminSettings['admin_style']) ? 'flatadmin' : $adminSettings['admin_style'];
  1554. }
  1555. /**
  1556. * curl get 请求
  1557. *
  1558. * @param $url
  1559. *
  1560. * @return mixed
  1561. */
  1562. function cmf_curl_get($url) {
  1563. $ch = curl_init();
  1564. curl_setopt($ch, CURLOPT_URL, $url);
  1565. curl_setopt($ch, CURLOPT_FAILONERROR, true);
  1566. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
  1567. curl_setopt($ch, CURLOPT_AUTOREFERER, true);
  1568. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  1569. curl_setopt($ch, CURLOPT_TIMEOUT, 5);
  1570. $SSL = substr($url, 0, 8) == "https://" ? true : false;
  1571. if ($SSL) {
  1572. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 信任任何证书
  1573. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); // 检查证书中是否设置域名
  1574. }
  1575. $content = curl_exec($ch);
  1576. curl_close($ch);
  1577. return $content;
  1578. }
  1579. /**
  1580. * 用户操作记录
  1581. *
  1582. * @param string $action 用户操作
  1583. */
  1584. function cmf_user_action($action) {
  1585. $userId = cmf_get_current_user_id();
  1586. if (empty($userId)) {
  1587. return;
  1588. }
  1589. $findUserAction = Db::name('user_action')->where('action', $action)->find();
  1590. if (empty($findUserAction)) {
  1591. return;
  1592. }
  1593. $changeScore = false;
  1594. if ($findUserAction['cycle_type'] == 0) {
  1595. $changeScore = true;
  1596. } elseif ($findUserAction['reward_number'] > 0) {
  1597. $findUserScoreLog = Db::name('user_score_log')->order('create_time DESC')->find();
  1598. if (!empty($findUserScoreLog)) {
  1599. $cycleType = intval($findUserAction['cycle_type']);
  1600. switch ($cycleType) {//1:按天;2:按小时;3:永久
  1601. case 1:
  1602. $todayStartTime = strtotime(date('Y-m-d'));
  1603. $todayEndTime = strtotime(date('Y-m-d', strtotime('+1 day')));
  1604. $findUserScoreLogCount = Db::name('user_score_log')->where(
  1605. [
  1606. 'user_id' => $userId,
  1607. 'create_time' => [['gt', $todayStartTime], ['lt', $todayEndTime]]
  1608. ]
  1609. )->count();
  1610. if ($findUserScoreLogCount < $findUserAction['reward_number']) {
  1611. $changeScore = true;
  1612. }
  1613. break;
  1614. case 2:
  1615. if (($findUserScoreLog['create_time'] + 3600) < time()) {
  1616. $changeScore = true;
  1617. }
  1618. break;
  1619. case 3:
  1620. break;
  1621. }
  1622. } else {
  1623. $changeScore = true;
  1624. }
  1625. }
  1626. if ($changeScore) {
  1627. Db::name('user_score_log')->insert(
  1628. [
  1629. 'user_id' => $userId,
  1630. 'create_time' => time(),
  1631. 'action' => $action,
  1632. 'score' => $findUserAction['score'],
  1633. 'coin' => $findUserAction['coin'],
  1634. ]
  1635. );
  1636. $data = [];
  1637. if ($findUserAction['score'] > 0) {
  1638. $data['score'] = ['exp', 'score+'.$findUserAction['score']];
  1639. }
  1640. if ($findUserAction['score'] < 0) {
  1641. $data['score'] = ['exp', 'score-'.abs($findUserAction['score'])];
  1642. }
  1643. if ($findUserAction['coin'] > 0) {
  1644. $data['coin'] = ['exp', 'coin+'.$findUserAction['coin']];
  1645. }
  1646. if ($findUserAction['coin'] < 0) {
  1647. $data['coin'] = ['exp', 'coin-'.abs($findUserAction['coin'])];
  1648. }
  1649. Db::name('user')->where('id', $userId)->update($data);
  1650. }
  1651. }
  1652. function cmf_api_request($url, $params = []) {
  1653. //初始化
  1654. $curl = curl_init();
  1655. //设置抓取的url
  1656. curl_setopt($curl, CURLOPT_URL, 'http://127.0.0.1:1314/api/'.$url);
  1657. //设置头文件的信息作为数据流输出
  1658. curl_setopt($curl, CURLOPT_HEADER, 0);
  1659. //设置获取的信息以文件流的形式返回,而不是直接输出。
  1660. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  1661. //设置post方式提交
  1662. curl_setopt($curl, CURLOPT_POST, 1);
  1663. $token = session('token');
  1664. curl_setopt($curl, CURLOPT_HTTPHEADER, ["XX-Token: $token"]);
  1665. //设置post数据
  1666. curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
  1667. //执行命令
  1668. $data = curl_exec($curl);
  1669. //关闭URL请求
  1670. curl_close($curl);
  1671. //显示获得的数据
  1672. return json_decode($data, true);
  1673. }
  1674. /**
  1675. * 判断是否允许开放注册
  1676. */
  1677. function cmf_is_open_registration() {
  1678. $cmfSettings = cmf_get_option('cmf_settings');
  1679. return empty($cmfSettings['open_registration']) ? false : true;
  1680. }