作者 刘锟

Merge remote-tracking branch 'origin/master' into akun

正在显示 35 个修改的文件 包含 4468 行增加98 行删除

要显示太多修改。

为保证性能只显示 35 of 35+ 个文件。

@@ -33,7 +33,7 @@ class UpgradeProjectCount extends Command @@ -33,7 +33,7 @@ class UpgradeProjectCount extends Command
33 protected $description = '升级项目统计'; 33 protected $description = '升级项目统计';
34 34
35 public function handle(){ 35 public function handle(){
36 - $project_id = 627; 36 + $project_id = 632;
37 ProjectServer::useProject($project_id); 37 ProjectServer::useProject($project_id);
38 $this->count($project_id); 38 $this->count($project_id);
39 DB::disconnect('custom_mysql'); 39 DB::disconnect('custom_mysql');
@@ -36,7 +36,7 @@ class UpgradeProjectCount extends Command @@ -36,7 +36,7 @@ class UpgradeProjectCount extends Command
36 protected $description = '升级项目统计'; 36 protected $description = '升级项目统计';
37 37
38 public function handle(){ 38 public function handle(){
39 - $project_id = 627; 39 + $project_id = 632;
40 $oldModel = new UpdateOldInfo(); 40 $oldModel = new UpdateOldInfo();
41 $info = $oldModel->read(['project_id'=>$project_id]); 41 $info = $oldModel->read(['project_id'=>$project_id]);
42 $url = $info['old_domain_online']; 42 $url = $info['old_domain_online'];
  1 +<?php
  2 +
  3 +namespace App\Console\Commands;
  4 +
  5 +
  6 +use App\Exceptions\InquiryFilterException;
  7 +use App\Services\SyncSubmitTaskService;
  8 +use Illuminate\Console\Command;
  9 +use App\Models\SyncSubmitTask\SyncSubmitTask as SyncSubmitTaskModel;
  10 +use Illuminate\Support\Facades\DB;
  11 +use Illuminate\Support\Facades\Redis;
  12 +use Illuminate\Support\Facades\Schema;
  13 +use Illuminate\Support\Str;
  14 +
  15 +/**
  16 + *
  17 + * Class SyncSubmitTask
  18 + * @package App\Console\Commands
  19 + * @author zbj
  20 + * @date 2023/11/28
  21 + */
  22 +class SyncSubmitTask extends Command
  23 +{
  24 +
  25 + protected $signature = 'sync_submit_task';
  26 + protected $description = '询盘、访问异步任务';
  27 +
  28 + public function handle()
  29 + {
  30 + $backup = false;
  31 + while (true) {
  32 + $task_id = $this->getTaskId();
  33 + if ($task_id > 2000000) {
  34 + $backup = true;
  35 + }
  36 + if (empty($task_id)) {
  37 + if ($backup) {
  38 + $this->backup();
  39 + $backup = false;
  40 + }
  41 + sleep(5);
  42 + continue;
  43 + }
  44 + $this->output('任务' . $task_id . '开始');
  45 + $task_info = SyncSubmitTaskModel::find($task_id);
  46 + if (empty($task_info) || $task_info->status) {
  47 + $this->output('任务不存在或者已执行');
  48 + continue;
  49 + }
  50 + try {
  51 + SyncSubmitTaskService::handler($task_info);
  52 + $task_info->status = 1;
  53 + $task_info->save();
  54 +
  55 + $this->output('任务完成');
  56 + } catch (InquiryFilterException $e) {
  57 + $task_info->status = 1;
  58 + $task_info->is_filtered = 1;
  59 + $task_info->remark = $e->getMessage();
  60 + $task_info->save();
  61 +
  62 + $this->output('任务完成');
  63 + } catch (\Exception $e) {
  64 + $task_info->retry = $task_info->retry + 1;
  65 + if ($task_info->retry >= 3) {
  66 + $task_info->status = 2;
  67 + $task_info->remark = Str::substr($e->getMessage(), 0, 200);
  68 + } else {
  69 + Redis::lpush('sync_submit_task', $task_id);
  70 + }
  71 + $task_info->save();
  72 +
  73 + $this->output('任务失败:' . $e->getMessage());
  74 + }
  75 + }
  76 + }
  77 +
  78 + public function getTaskId()
  79 + {
  80 + $task_id = Redis::rpop('sync_submit_task');
  81 + if (empty($task_id)) {
  82 + $ids = SyncSubmitTaskModel::where('status', 0)->limit(100)->pluck('id');
  83 + foreach ($ids as $id) {
  84 + Redis::lpush('sync_submit_task', $id);
  85 + }
  86 + $task_id = Redis::rpop('sync_submit_task');
  87 + }
  88 + return $task_id;
  89 + }
  90 +
  91 + /**
  92 + * 输出处理日志
  93 + */
  94 + public function output($message): bool
  95 + {
  96 + echo date('Y-m-d H:i:s') . ' | ' . $message . PHP_EOL;
  97 + return true;
  98 + }
  99 +
  100 + /**
  101 + * 备份数据
  102 + * @author zbj
  103 + * @date 2024/1/23
  104 + */
  105 + public function backup()
  106 + {
  107 + DB::beginTransaction();
  108 + try {
  109 + $table = (new SyncSubmitTaskModel())->getTable();
  110 + $new_table = $table . '_backup_' . date('Ymd');
  111 +
  112 + //重命名当前表
  113 + Schema::rename($table, $new_table);
  114 + //克隆表数据
  115 + DB::statement('CREATE TABLE ' . $table . ' LIKE ' . $new_table);
  116 +
  117 + DB::commit();
  118 +
  119 + $this->output('数据备份成功');
  120 + } catch (\Exception $e) {
  121 + $this->output('数据备份失败' . $e->getMessage());
  122 + DB::rollBack();
  123 + }
  124 + return true;
  125 + }
  126 +}
@@ -55,7 +55,7 @@ class UpdateRoute extends Command @@ -55,7 +55,7 @@ class UpdateRoute extends Command
55 */ 55 */
56 public function handle(){ 56 public function handle(){
57 $projectModel = new Project(); 57 $projectModel = new Project();
58 - $list = $projectModel->list(['id'=>433]); 58 + $list = $projectModel->list(['id'=>627]);
59 $data = []; 59 $data = [];
60 foreach ($list as $v){ 60 foreach ($list as $v){
61 echo date('Y-m-d H:i:s') . 'project_id:'.$v['id'] . PHP_EOL; 61 echo date('Y-m-d H:i:s') . 'project_id:'.$v['id'] . PHP_EOL;
@@ -116,6 +116,7 @@ class UpdateRoute extends Command @@ -116,6 +116,7 @@ class UpdateRoute extends Command
116 if(!empty($v['route'])){ 116 if(!empty($v['route'])){
117 $tag = "-tag"; 117 $tag = "-tag";
118 if (!(substr($v['route'], -strlen($tag)) === $tag)) { 118 if (!(substr($v['route'], -strlen($tag)) === $tag)) {
  119 + echo date('Y-m-d H:i:s') . '拼接 :'.$v['id'] . PHP_EOL;
119 // $route = Translate::tran($v['route'], 'en').$tag; 120 // $route = Translate::tran($v['route'], 'en').$tag;
120 // 如果不是以 '-tag' 结尾,则拼接上 '-tag' 121 // 如果不是以 '-tag' 结尾,则拼接上 '-tag'
121 $route = $v['route'].$tag; 122 $route = $v['route'].$tag;
  1 +<?php
  2 +
  3 +namespace App\Exceptions;
  4 +
  5 +use Exception;
  6 +
  7 +/**
  8 + * @notes: 询盘过滤
  9 + * Class InquiryFilterException
  10 + * @package App\Exceptions
  11 + */
  12 +class InquiryFilterException extends Exception
  13 +{
  14 +
  15 +}
  1 +<?php
  2 +
  3 +
  4 +namespace App\Helper;
  5 +
  6 +
  7 +class Country
  8 +{
  9 + public static function getCountryList(){
  10 + $country = json_decode(self::$country_json, true);
  11 + return array_column($country['country'], 'name');
  12 + }
  13 +
  14 +
  15 +
  16 + public static $country_json = '{
  17 + "country":[
  18 + {
  19 + "name":"安哥拉",
  20 + "num":"-7",
  21 + "area_code":"244"
  22 + },
  23 + {
  24 + "name":"阿富汗",
  25 + "num":"0",
  26 + "area_code":"93"
  27 + },
  28 + {
  29 + "name":"阿尔巴尼亚",
  30 + "num":"-7",
  31 + "area_code":"355"
  32 + },
  33 + {
  34 + "name":"阿尔及利亚",
  35 + "num":"-8",
  36 + "area_code":"213"
  37 + },
  38 + {
  39 + "name":"安道尔共和国",
  40 + "num":"-8",
  41 + "area_code":"376"
  42 + },
  43 + {
  44 + "name":"安圭拉岛",
  45 + "num":"-12",
  46 + "area_code":"1264"
  47 + },
  48 + {
  49 + "name":"安提瓜和巴布达",
  50 + "num":"-12",
  51 + "area_code":"1268"
  52 + },
  53 + {
  54 + "name":"阿根廷",
  55 + "num":"-11",
  56 + "area_code":"54"
  57 + },
  58 + {
  59 + "name":"亚美尼亚",
  60 + "num":"-6",
  61 + "area_code":"374"
  62 + },
  63 + {
  64 + "name":"阿森松",
  65 + "num":"-8",
  66 + "area_code":"247"
  67 + },
  68 + {
  69 + "name":"澳大利亚",
  70 + "num":"2",
  71 + "area_code":"61"
  72 + },
  73 + {
  74 + "name":"奥地利",
  75 + "num":"-7",
  76 + "area_code":"43"
  77 + },
  78 + {
  79 + "name":"阿塞拜疆",
  80 + "num":"-5",
  81 + "area_code":"994"
  82 + },
  83 + {
  84 + "name":"巴哈马",
  85 + "num":"-13",
  86 + "area_code":"1242"
  87 + },
  88 + {
  89 + "name":"巴林",
  90 + "num":"-5",
  91 + "area_code":"973"
  92 + },
  93 + {
  94 + "name":"孟加拉国",
  95 + "num":"-2",
  96 + "area_code":"880"
  97 + },
  98 + {
  99 + "name":"巴巴多斯",
  100 + "num":"-12",
  101 + "area_code":"1246"
  102 + },
  103 + {
  104 + "name":"白俄罗斯",
  105 + "num":"-6",
  106 + "area_code":"375"
  107 + },
  108 + {
  109 + "name":"比利时",
  110 + "num":"-7",
  111 + "area_code":"32"
  112 + },
  113 + {
  114 + "name":"伯利兹",
  115 + "num":"-14",
  116 + "area_code":"501"
  117 + },
  118 + {
  119 + "name":"贝宁",
  120 + "num":"-7",
  121 + "area_code":"229"
  122 + },
  123 + {
  124 + "name":"百慕大群岛",
  125 + "num":"-12",
  126 + "area_code":"1441"
  127 + },
  128 + {
  129 + "name":"玻利维亚",
  130 + "num":"-12",
  131 + "area_code":"591"
  132 + },
  133 + {
  134 + "name":"博茨瓦纳",
  135 + "num":"-6",
  136 + "area_code":"267"
  137 + },
  138 + {
  139 + "name":"巴西",
  140 + "num":"-11",
  141 + "area_code":"55"
  142 + },
  143 + {
  144 + "name":"文莱",
  145 + "num":"0",
  146 + "area_code":"673"
  147 + },
  148 + {
  149 + "name":"保加利亚",
  150 + "num":"-6",
  151 + "area_code":"359"
  152 + },
  153 + {
  154 + "name":"布基纳法索",
  155 + "num":"-8",
  156 + "area_code":"226"
  157 + },
  158 + {
  159 + "name":"缅甸",
  160 + "num":"-1.3",
  161 + "area_code":"95"
  162 + },
  163 + {
  164 + "name":"布隆迪",
  165 + "num":"-6",
  166 + "area_code":"257"
  167 + },
  168 + {
  169 + "name":"喀麦隆",
  170 + "num":"-7",
  171 + "area_code":"237"
  172 + },
  173 + {
  174 + "name":"加拿大",
  175 + "num":"-13",
  176 + "area_code":"1"
  177 + },
  178 + {
  179 + "name":"开曼群岛",
  180 + "num":"-13",
  181 + "area_code":"1345"
  182 + },
  183 + {
  184 + "name":"中非共和国",
  185 + "num":"-7",
  186 + "area_code":"236"
  187 + },
  188 + {
  189 + "name":"乍得",
  190 + "num":"-7",
  191 + "area_code":"235"
  192 + },
  193 + {
  194 + "name":"智利",
  195 + "num":"-13",
  196 + "area_code":"56"
  197 + },
  198 + {
  199 + "name":"中国",
  200 + "num":"0",
  201 + "area_code":"86"
  202 + },
  203 + {
  204 + "name":"哥伦比亚",
  205 + "num":"-13",
  206 + "area_code":"57"
  207 + },
  208 + {
  209 + "name":"刚果",
  210 + "num":"-7",
  211 + "area_code":"242"
  212 + },
  213 + {
  214 + "name":"库克群岛",
  215 + "num":"-18.3",
  216 + "area_code":"682"
  217 + },
  218 + {
  219 + "name":"哥斯达黎加",
  220 + "num":"-14",
  221 + "area_code":"506"
  222 + },
  223 + {
  224 + "name":"古巴",
  225 + "num":"-13",
  226 + "area_code":"53"
  227 + },
  228 + {
  229 + "name":"塞浦路斯",
  230 + "num":"-6",
  231 + "area_code":"357"
  232 + },
  233 + {
  234 + "name":"捷克",
  235 + "num":"-7",
  236 + "area_code":"420"
  237 + },
  238 + {
  239 + "name":"丹麦",
  240 + "num":"-7",
  241 + "area_code":"45"
  242 + },
  243 + {
  244 + "name":"吉布提",
  245 + "num":"-5",
  246 + "area_code":"253"
  247 + },
  248 + {
  249 + "name":"多米尼加共和国",
  250 + "num":"-13",
  251 + "area_code":"1890"
  252 + },
  253 + {
  254 + "name":"厄瓜多尔",
  255 + "num":"-13",
  256 + "area_code":"593"
  257 + },
  258 + {
  259 + "name":"埃及",
  260 + "num":"-6",
  261 + "area_code":"20"
  262 + },
  263 + {
  264 + "name":"萨尔瓦多",
  265 + "num":"-14",
  266 + "area_code":"503"
  267 + },
  268 + {
  269 + "name":"爱沙尼亚",
  270 + "num":"-5",
  271 + "area_code":"372"
  272 + },
  273 + {
  274 + "name":"埃塞俄比亚",
  275 + "num":"-5",
  276 + "area_code":"251"
  277 + },
  278 + {
  279 + "name":"斐济",
  280 + "num":"4",
  281 + "area_code":"679"
  282 + },
  283 + {
  284 + "name":"芬兰",
  285 + "num":"-6",
  286 + "area_code":"358"
  287 + },
  288 + {
  289 + "name":"法国",
  290 + "num":"-8",
  291 + "area_code":"33"
  292 + },
  293 + {
  294 + "name":"法属圭亚那",
  295 + "num":"-12",
  296 + "area_code":"594"
  297 + },
  298 + {
  299 + "name":"加蓬",
  300 + "num":"-7",
  301 + "area_code":"241"
  302 + },
  303 + {
  304 + "name":"冈比亚",
  305 + "num":"-8",
  306 + "area_code":"220"
  307 + },
  308 + {
  309 + "name":"格鲁吉亚",
  310 + "num":"0",
  311 + "area_code":"995"
  312 + },
  313 + {
  314 + "name":"德国",
  315 + "num":"-7",
  316 + "area_code":"49"
  317 + },
  318 + {
  319 + "name":"加纳",
  320 + "num":"-8",
  321 + "area_code":"233"
  322 + },
  323 + {
  324 + "name":"直布罗陀",
  325 + "num":"-8",
  326 + "area_code":"350"
  327 + },
  328 + {
  329 + "name":"希腊",
  330 + "num":"-6",
  331 + "area_code":"30"
  332 + },
  333 + {
  334 + "name":"格林纳达",
  335 + "num":"-14",
  336 + "area_code":"1809"
  337 + },
  338 + {
  339 + "name":"关岛",
  340 + "num":"2",
  341 + "area_code":"1671"
  342 + },
  343 + {
  344 + "name":"危地马拉",
  345 + "num":"-14",
  346 + "area_code":"502"
  347 + },
  348 + {
  349 + "name":"几内亚",
  350 + "num":"-8",
  351 + "area_code":"224"
  352 + },
  353 + {
  354 + "name":"圭亚那",
  355 + "num":"-11",
  356 + "area_code":"592"
  357 + },
  358 + {
  359 + "name":"海地",
  360 + "num":"-13",
  361 + "area_code":"509"
  362 + },
  363 + {
  364 + "name":"洪都拉斯",
  365 + "num":"-14",
  366 + "area_code":"504"
  367 + },
  368 + {
  369 + "name":"中国香港",
  370 + "num":"0",
  371 + "area_code":"852"
  372 + },
  373 + {
  374 + "name":"匈牙利",
  375 + "num":"-7",
  376 + "area_code":"36"
  377 + },
  378 + {
  379 + "name":"冰岛",
  380 + "num":"-9",
  381 + "area_code":"354"
  382 + },
  383 + {
  384 + "name":"印度",
  385 + "num":"-2.3",
  386 + "area_code":"91"
  387 + },
  388 + {
  389 + "name":"印度尼西亚",
  390 + "num":"-0.3",
  391 + "area_code":"62"
  392 + },
  393 + {
  394 + "name":"伊朗",
  395 + "num":"-4.3",
  396 + "area_code":"98"
  397 + },
  398 + {
  399 + "name":"伊拉克",
  400 + "num":"-5",
  401 + "area_code":"964"
  402 + },
  403 + {
  404 + "name":"爱尔兰",
  405 + "num":"-4.3",
  406 + "area_code":"353"
  407 + },
  408 + {
  409 + "name":"以色列",
  410 + "num":"-6",
  411 + "area_code":"972"
  412 + },
  413 + {
  414 + "name":"意大利",
  415 + "num":"-7",
  416 + "area_code":"39"
  417 + },
  418 + {
  419 + "name":"科特迪瓦",
  420 + "num":"-6",
  421 + "area_code":"225"
  422 + },
  423 + {
  424 + "name":"牙买加",
  425 + "num":"-12",
  426 + "area_code":"1876"
  427 + },
  428 + {
  429 + "name":"日本",
  430 + "num":"1",
  431 + "area_code":"81"
  432 + },
  433 + {
  434 + "name":"约旦",
  435 + "num":"-6",
  436 + "area_code":"962"
  437 + },
  438 + {
  439 + "name":"柬埔寨",
  440 + "num":"-1",
  441 + "area_code":"855"
  442 + },
  443 + {
  444 + "name":"哈萨克斯坦",
  445 + "num":"-5",
  446 + "area_code":"327"
  447 + },
  448 + {
  449 + "name":"肯尼亚",
  450 + "num":"-5",
  451 + "area_code":"254"
  452 + },
  453 + {
  454 + "name":"韩国",
  455 + "num":"1",
  456 + "area_code":"82"
  457 + },
  458 + {
  459 + "name":"科威特",
  460 + "num":"-5",
  461 + "area_code":"965"
  462 + },
  463 + {
  464 + "name":"吉尔吉斯坦",
  465 + "num":"-5",
  466 + "area_code":"331"
  467 + },
  468 + {
  469 + "name":"老挝",
  470 + "num":"-1",
  471 + "area_code":"856"
  472 + },
  473 + {
  474 + "name":"拉脱维亚",
  475 + "num":"-5",
  476 + "area_code":"371"
  477 + },
  478 + {
  479 + "name":"黎巴嫩",
  480 + "num":"-6",
  481 + "area_code":"961"
  482 + },
  483 + {
  484 + "name":"莱索托",
  485 + "num":"-6",
  486 + "area_code":"266"
  487 + },
  488 + {
  489 + "name":"利比里亚",
  490 + "num":"-8",
  491 + "area_code":"231"
  492 + },
  493 + {
  494 + "name":"利比亚",
  495 + "num":"-6",
  496 + "area_code":"218"
  497 + },
  498 + {
  499 + "name":"列支敦士登",
  500 + "num":"-7",
  501 + "area_code":"423"
  502 + },
  503 + {
  504 + "name":"立陶宛",
  505 + "num":"-5",
  506 + "area_code":"370"
  507 + },
  508 + {
  509 + "name":"卢森堡",
  510 + "num":"-7",
  511 + "area_code":"352"
  512 + },
  513 + {
  514 + "name":"中国澳门",
  515 + "num":"0",
  516 + "area_code":"853"
  517 + },
  518 + {
  519 + "name":"马达加斯加",
  520 + "num":"-5",
  521 + "area_code":"261"
  522 + },
  523 + {
  524 + "name":"马拉维",
  525 + "num":"-6",
  526 + "area_code":"265"
  527 + },
  528 + {
  529 + "name":"马来西亚",
  530 + "num":"-0.5",
  531 + "area_code":"60"
  532 + },
  533 + {
  534 + "name":"马尔代夫",
  535 + "num":"-7",
  536 + "area_code":"960"
  537 + },
  538 + {
  539 + "name":"马里",
  540 + "num":"-8",
  541 + "area_code":"223"
  542 + },
  543 + {
  544 + "name":"马耳他",
  545 + "num":"-7",
  546 + "area_code":"356"
  547 + },
  548 + {
  549 + "name":"马里亚那群岛",
  550 + "num":"1",
  551 + "area_code":"1670"
  552 + },
  553 + {
  554 + "name":"马提尼克",
  555 + "num":"-12",
  556 + "area_code":"596"
  557 + },
  558 + {
  559 + "name":"毛里求斯",
  560 + "num":"-4",
  561 + "area_code":"230"
  562 + },
  563 + {
  564 + "name":"墨西哥",
  565 + "num":"-15",
  566 + "area_code":"52"
  567 + },
  568 + {
  569 + "name":"摩尔多瓦",
  570 + "num":"-5",
  571 + "area_code":"373"
  572 + },
  573 + {
  574 + "name":"摩纳哥",
  575 + "num":"-7",
  576 + "area_code":"377"
  577 + },
  578 + {
  579 + "name":"蒙古",
  580 + "num":"0",
  581 + "area_code":"976"
  582 + },
  583 + {
  584 + "name":"蒙特塞拉特岛",
  585 + "num":"-12",
  586 + "area_code":"1664"
  587 + },
  588 + {
  589 + "name":"摩洛哥",
  590 + "num":"-6",
  591 + "area_code":"212"
  592 + },
  593 + {
  594 + "name":"莫桑比克",
  595 + "num":"-6",
  596 + "area_code":"258"
  597 + },
  598 + {
  599 + "name":"纳米比亚",
  600 + "num":"-7",
  601 + "area_code":"264"
  602 + },
  603 + {
  604 + "name":"瑙鲁",
  605 + "num":"4",
  606 + "area_code":"674"
  607 + },
  608 + {
  609 + "name":"尼泊尔",
  610 + "num":"-2.3",
  611 + "area_code":"977"
  612 + },
  613 + {
  614 + "name":"荷属安的列斯",
  615 + "num":"-12",
  616 + "area_code":"599"
  617 + },
  618 + {
  619 + "name":"荷兰",
  620 + "num":"-7",
  621 + "area_code":"31"
  622 + },
  623 + {
  624 + "name":"新西兰",
  625 + "num":"4",
  626 + "area_code":"64"
  627 + },
  628 + {
  629 + "name":"尼加拉瓜",
  630 + "num":"-14",
  631 + "area_code":"505"
  632 + },
  633 + {
  634 + "name":"尼日尔",
  635 + "num":"-8",
  636 + "area_code":"227"
  637 + },
  638 + {
  639 + "name":"尼日利亚",
  640 + "num":"-7",
  641 + "area_code":"234"
  642 + },
  643 + {
  644 + "name":"朝鲜",
  645 + "num":"1",
  646 + "area_code":"850"
  647 + },
  648 + {
  649 + "name":"挪威",
  650 + "num":"-7",
  651 + "area_code":"47"
  652 + },
  653 + {
  654 + "name":"阿曼",
  655 + "num":"-4",
  656 + "area_code":"968"
  657 + },
  658 + {
  659 + "name":"巴基斯坦",
  660 + "num":"-2.3",
  661 + "area_code":"92"
  662 + },
  663 + {
  664 + "name":"巴拿马",
  665 + "num":"-13",
  666 + "area_code":"507"
  667 + },
  668 + {
  669 + "name":"巴布亚新几内亚",
  670 + "num":"2",
  671 + "area_code":"675"
  672 + },
  673 + {
  674 + "name":"巴拉圭",
  675 + "num":"-12",
  676 + "area_code":"595"
  677 + },
  678 + {
  679 + "name":"秘鲁",
  680 + "num":"-13",
  681 + "area_code":"51"
  682 + },
  683 + {
  684 + "name":"菲律宾",
  685 + "num":"0",
  686 + "area_code":"63"
  687 + },
  688 + {
  689 + "name":"波兰",
  690 + "num":"-7",
  691 + "area_code":"48"
  692 + },
  693 + {
  694 + "name":"法属玻利尼西亚",
  695 + "num":"3",
  696 + "area_code":"689"
  697 + },
  698 + {
  699 + "name":"葡萄牙",
  700 + "num":"-8",
  701 + "area_code":"351"
  702 + },
  703 + {
  704 + "name":"波多黎各",
  705 + "num":"-12",
  706 + "area_code":"1787"
  707 + },
  708 + {
  709 + "name":"卡塔尔",
  710 + "num":"-5",
  711 + "area_code":"974"
  712 + },
  713 + {
  714 + "name":"留尼旺",
  715 + "num":"-4",
  716 + "area_code":"262"
  717 + },
  718 + {
  719 + "name":"罗马尼亚",
  720 + "num":"-6",
  721 + "area_code":"40"
  722 + },
  723 + {
  724 + "name":"俄罗斯",
  725 + "num":"-5",
  726 + "area_code":"7"
  727 + },
  728 + {
  729 + "name":"圣文森特岛",
  730 + "num":"-12",
  731 + "area_code":"1784"
  732 + },
  733 + {
  734 + "name":"东萨摩亚(美)",
  735 + "num":"-19",
  736 + "area_code":"684"
  737 + },
  738 + {
  739 + "name":"西萨摩亚",
  740 + "num":"-19",
  741 + "area_code":"685"
  742 + },
  743 + {
  744 + "name":"圣马力诺",
  745 + "num":"-7",
  746 + "area_code":"378"
  747 + },
  748 + {
  749 + "name":"圣多美和普林西比",
  750 + "num":"-8",
  751 + "area_code":"239"
  752 + },
  753 + {
  754 + "name":"沙特阿拉伯",
  755 + "num":"-5",
  756 + "area_code":"966"
  757 + },
  758 + {
  759 + "name":"塞内加尔",
  760 + "num":"-8",
  761 + "area_code":"221"
  762 + },
  763 + {
  764 + "name":"塞舌尔",
  765 + "num":"-4",
  766 + "area_code":"248"
  767 + },
  768 + {
  769 + "name":"塞拉利昂",
  770 + "num":"-8",
  771 + "area_code":"232"
  772 + },
  773 + {
  774 + "name":"新加坡",
  775 + "num":"0.3",
  776 + "area_code":"65"
  777 + },
  778 + {
  779 + "name":"斯洛伐克",
  780 + "num":"-7",
  781 + "area_code":"421"
  782 + },
  783 + {
  784 + "name":"斯洛文尼亚",
  785 + "num":"-7",
  786 + "area_code":"386"
  787 + },
  788 + {
  789 + "name":"所罗门群岛",
  790 + "num":"3",
  791 + "area_code":"677"
  792 + },
  793 + {
  794 + "name":"索马里",
  795 + "num":"-5",
  796 + "area_code":"252"
  797 + },
  798 + {
  799 + "name":"南非",
  800 + "num":"-6",
  801 + "area_code":"27"
  802 + },
  803 + {
  804 + "name":"西班牙",
  805 + "num":"-8",
  806 + "area_code":"34"
  807 + },
  808 + {
  809 + "name":"斯里兰卡",
  810 + "num":"0",
  811 + "area_code":"94"
  812 + },
  813 + {
  814 + "name":"圣卢西亚",
  815 + "num":"-12",
  816 + "area_code":"1758"
  817 + },
  818 + {
  819 + "name":"圣文森特",
  820 + "num":"-12",
  821 + "area_code":"1784"
  822 + },
  823 + {
  824 + "name":"苏丹",
  825 + "num":"-6",
  826 + "area_code":"249"
  827 + },
  828 + {
  829 + "name":"苏里南",
  830 + "num":"-11.3",
  831 + "area_code":"597"
  832 + },
  833 + {
  834 + "name":"斯威士兰",
  835 + "num":"-6",
  836 + "area_code":"268"
  837 + },
  838 + {
  839 + "name":"瑞典",
  840 + "num":"-7",
  841 + "area_code":"46"
  842 + },
  843 + {
  844 + "name":"瑞士",
  845 + "num":"-7",
  846 + "area_code":"41"
  847 + },
  848 + {
  849 + "name":"叙利亚",
  850 + "num":"-6",
  851 + "area_code":"963"
  852 + },
  853 + {
  854 + "name":"台湾省",
  855 + "num":"0",
  856 + "area_code":"886"
  857 + },
  858 + {
  859 + "name":"塔吉克斯坦",
  860 + "num":"-5",
  861 + "area_code":"992"
  862 + },
  863 + {
  864 + "name":"坦桑尼亚",
  865 + "num":"-5",
  866 + "area_code":"255"
  867 + },
  868 + {
  869 + "name":"泰国",
  870 + "num":"-1",
  871 + "area_code":"66"
  872 + },
  873 + {
  874 + "name":"多哥",
  875 + "num":"-8",
  876 + "area_code":"228"
  877 + },
  878 + {
  879 + "name":"汤加",
  880 + "num":"4",
  881 + "area_code":"676"
  882 + },
  883 + {
  884 + "name":"特立尼达和多巴哥",
  885 + "num":"-12",
  886 + "area_code":"1809"
  887 + },
  888 + {
  889 + "name":"突尼斯",
  890 + "num":"-7",
  891 + "area_code":"216"
  892 + },
  893 + {
  894 + "name":"土耳其",
  895 + "num":"-6",
  896 + "area_code":"90"
  897 + },
  898 + {
  899 + "name":"土库曼斯坦",
  900 + "num":"-5",
  901 + "area_code":"993"
  902 + },
  903 + {
  904 + "name":"乌干达",
  905 + "num":"-5",
  906 + "area_code":"256"
  907 + },
  908 + {
  909 + "name":"乌克兰",
  910 + "num":"-5",
  911 + "area_code":"380"
  912 + },
  913 + {
  914 + "name":"阿拉伯联合酋长国",
  915 + "num":"-4",
  916 + "area_code":"971"
  917 + },
  918 + {
  919 + "name":"英国",
  920 + "num":"-8",
  921 + "area_code":"44"
  922 + },
  923 + {
  924 + "name":"美国",
  925 + "num":"-13",
  926 + "area_code":"1"
  927 + },
  928 + {
  929 + "name":"乌拉圭",
  930 + "num":"-10.3",
  931 + "area_code":"598"
  932 + },
  933 + {
  934 + "name":"乌兹别克斯坦",
  935 + "num":"-5",
  936 + "area_code":"233"
  937 + },
  938 + {
  939 + "name":"委内瑞拉",
  940 + "num":"-12.3",
  941 + "area_code":"58"
  942 + },
  943 + {
  944 + "name":"越南",
  945 + "num":"-1",
  946 + "area_code":"84"
  947 + },
  948 + {
  949 + "name":"也门",
  950 + "num":"-5",
  951 + "area_code":"967"
  952 + },
  953 + {
  954 + "name":"南斯拉夫",
  955 + "num":"-7",
  956 + "area_code":"381"
  957 + },
  958 + {
  959 + "name":"津巴布韦",
  960 + "num":"-6",
  961 + "area_code":"263"
  962 + },
  963 + {
  964 + "name":"扎伊尔",
  965 + "num":"-7",
  966 + "area_code":"243"
  967 + },
  968 + {
  969 + "name":"赞比亚",
  970 + "num":"-6",
  971 + "area_code":"260"
  972 + }
  973 + ]
  974 +}';
  975 +
  976 +
  977 +}
@@ -124,4 +124,36 @@ class FormGlobalsoApi @@ -124,4 +124,36 @@ class FormGlobalsoApi
124 } 124 }
125 return $res; 125 return $res;
126 } 126 }
  127 +
  128 + /**
  129 + * 提交询盘
  130 + * @param $ip
  131 + * @param $referer
  132 + * @param $submit_at
  133 + * @param $data
  134 + * @return array|false|mixed
  135 + * @author zbj
  136 + * @date 2024/1/20
  137 + */
  138 + public function submitInquiry($ip, $referer, $submit_at, $data)
  139 + {
  140 + $api_url = $this->url . '/api/external-interface/add/fa043f9cbec6b38f';
  141 +
  142 + $data['ip'] = $ip;
  143 + $data['token'] = md5($referer . $data['name'] . $ip . date("Y-m-d"));
  144 + $data['refer'] = $referer;
  145 + $data['submit_time'] = date('Y-m-d H:i:s', strtotime($submit_at));
  146 + $data['source'] = 1; //固定
  147 +
  148 + try {
  149 + $res = HttpUtils::post($api_url, $data);
  150 + $res = Arr::s2a($res);
  151 + } catch (\Exception | GuzzleException $e) {
  152 + errorLog('提交询盘信息失败', $data, $e);
  153 + return false;
  154 + }
  155 + return $res;
  156 + }
  157 +
  158 +
127 } 159 }
  1 +<?php
  2 +/**
  3 + * Website: http://sourceforge.net/projects/simplehtmldom/
  4 + * Additional projects: http://sourceforge.net/projects/debugobject/
  5 + * Acknowledge: Jose Solorzano (https://sourceforge.net/projects/php-html/)
  6 + *
  7 + * Licensed under The MIT License
  8 + * See the LICENSE file in the project root for more information.
  9 + *
  10 + * Authors:
  11 + * S.C. Chen
  12 + * John Schlick
  13 + * Rus Carroll
  14 + * logmanoriginal
  15 + *
  16 + * Contributors:
  17 + * Yousuke Kumakura
  18 + * Vadim Voituk
  19 + * Antcs
  20 + *
  21 + * Version Rev. 1.9.1 (291)
  22 + */
  23 +
  24 +define('HDOM_TYPE_ELEMENT', 1);
  25 +define('HDOM_TYPE_COMMENT', 2);
  26 +define('HDOM_TYPE_TEXT', 3);
  27 +define('HDOM_TYPE_ENDTAG', 4);
  28 +define('HDOM_TYPE_ROOT', 5);
  29 +define('HDOM_TYPE_UNKNOWN', 6);
  30 +define('HDOM_QUOTE_DOUBLE', 0);
  31 +define('HDOM_QUOTE_SINGLE', 1);
  32 +define('HDOM_QUOTE_NO', 3);
  33 +define('HDOM_INFO_BEGIN', 0);
  34 +define('HDOM_INFO_END', 1);
  35 +define('HDOM_INFO_QUOTE', 2);
  36 +define('HDOM_INFO_SPACE', 3);
  37 +define('HDOM_INFO_TEXT', 4);
  38 +define('HDOM_INFO_INNER', 5);
  39 +define('HDOM_INFO_OUTER', 6);
  40 +define('HDOM_INFO_ENDSPACE', 7);
  41 +
  42 +defined('DEFAULT_TARGET_CHARSET') || define('DEFAULT_TARGET_CHARSET', 'UTF-8');
  43 +defined('DEFAULT_BR_TEXT') || define('DEFAULT_BR_TEXT', "\r\n");
  44 +defined('DEFAULT_SPAN_TEXT') || define('DEFAULT_SPAN_TEXT', ' ');
  45 +defined('MAX_FILE_SIZE') || define('MAX_FILE_SIZE', 600000);
  46 +define('HDOM_SMARTY_AS_TEXT', 1);
  47 +
  48 +function file_get_html(
  49 + $url,
  50 + $use_include_path = false,
  51 + $context = null,
  52 + $offset = 0,
  53 + $maxLen = -1,
  54 + $lowercase = true,
  55 + $forceTagsClosed = true,
  56 + $target_charset = DEFAULT_TARGET_CHARSET,
  57 + $stripRN = true,
  58 + $defaultBRText = DEFAULT_BR_TEXT,
  59 + $defaultSpanText = DEFAULT_SPAN_TEXT)
  60 +{
  61 + if($maxLen <= 0) { $maxLen = MAX_FILE_SIZE; }
  62 +
  63 + $dom = new simple_html_dom(
  64 + null,
  65 + $lowercase,
  66 + $forceTagsClosed,
  67 + $target_charset,
  68 + $stripRN,
  69 + $defaultBRText,
  70 + $defaultSpanText
  71 + );
  72 +
  73 + /**
  74 + * For sourceforge users: uncomment the next line and comment the
  75 + * retrieve_url_contents line 2 lines down if it is not already done.
  76 + */
  77 + $arrContextOptions = [
  78 + 'ssl' => [
  79 + 'verify_peer' => false,
  80 + 'verify_peer_name' => false,
  81 + ]
  82 + ];
  83 +
  84 + $context = stream_context_create($arrContextOptions);
  85 + $contents = file_get_contents(
  86 + $url,
  87 + $use_include_path,
  88 + $context,
  89 + $offset,
  90 + $maxLen
  91 + );
  92 + // $contents = retrieve_url_contents($url);
  93 +
  94 + if (empty($contents) || strlen($contents) > $maxLen) {
  95 + $dom->clear();
  96 + return false;
  97 + }
  98 +
  99 + return $dom->load($contents, $lowercase, $stripRN);
  100 +}
  101 +
  102 +function str_get_html(
  103 + $str,
  104 + $lowercase = true,
  105 + $forceTagsClosed = true,
  106 + $target_charset = DEFAULT_TARGET_CHARSET,
  107 + $stripRN = true,
  108 + $defaultBRText = DEFAULT_BR_TEXT,
  109 + $defaultSpanText = DEFAULT_SPAN_TEXT)
  110 +{
  111 + $dom = new simple_html_dom(
  112 + null,
  113 + $lowercase,
  114 + $forceTagsClosed,
  115 + $target_charset,
  116 + $stripRN,
  117 + $defaultBRText,
  118 + $defaultSpanText
  119 + );
  120 +
  121 + if (empty($str) || strlen($str) > MAX_FILE_SIZE) {
  122 + $dom->clear();
  123 + return false;
  124 + }
  125 +
  126 + return $dom->load($str, $lowercase, $stripRN);
  127 +}
  128 +
  129 +function dump_html_tree($node, $show_attr = true, $deep = 0)
  130 +{
  131 + $node->dump($node);
  132 +}
  133 +
  134 +class simple_html_dom_node
  135 +{
  136 + public $nodetype = HDOM_TYPE_TEXT;
  137 + public $tag = 'text';
  138 + public $attr = array();
  139 + public $children = array();
  140 + public $nodes = array();
  141 + public $parent = null;
  142 + public $_ = array();
  143 + public $tag_start = 0;
  144 + private $dom = null;
  145 +
  146 + function __construct($dom)
  147 + {
  148 + $this->dom = $dom;
  149 + $dom->nodes[] = $this;
  150 + }
  151 +
  152 + function __destruct()
  153 + {
  154 + $this->clear();
  155 + }
  156 +
  157 + function __toString()
  158 + {
  159 + return $this->outertext();
  160 + }
  161 +
  162 + function clear()
  163 + {
  164 + $this->dom = null;
  165 + $this->nodes = null;
  166 + $this->parent = null;
  167 + $this->children = null;
  168 + }
  169 +
  170 + function dump($show_attr = true, $depth = 0)
  171 + {
  172 + echo str_repeat("\t", $depth) . $this->tag;
  173 +
  174 + if ($show_attr && count($this->attr) > 0) {
  175 + echo '(';
  176 + foreach ($this->attr as $k => $v) {
  177 + echo "[$k]=>\"$v\", ";
  178 + }
  179 + echo ')';
  180 + }
  181 +
  182 + echo "\n";
  183 +
  184 + if ($this->nodes) {
  185 + foreach ($this->nodes as $node) {
  186 + $node->dump($show_attr, $depth + 1);
  187 + }
  188 + }
  189 + }
  190 +
  191 + function dump_node($echo = true)
  192 + {
  193 + $string = $this->tag;
  194 +
  195 + if (count($this->attr) > 0) {
  196 + $string .= '(';
  197 + foreach ($this->attr as $k => $v) {
  198 + $string .= "[$k]=>\"$v\", ";
  199 + }
  200 + $string .= ')';
  201 + }
  202 +
  203 + if (count($this->_) > 0) {
  204 + $string .= ' $_ (';
  205 + foreach ($this->_ as $k => $v) {
  206 + if (is_array($v)) {
  207 + $string .= "[$k]=>(";
  208 + foreach ($v as $k2 => $v2) {
  209 + $string .= "[$k2]=>\"$v2\", ";
  210 + }
  211 + $string .= ')';
  212 + } else {
  213 + $string .= "[$k]=>\"$v\", ";
  214 + }
  215 + }
  216 + $string .= ')';
  217 + }
  218 +
  219 + if (isset($this->text)) {
  220 + $string .= " text: ({$this->text})";
  221 + }
  222 +
  223 + $string .= ' HDOM_INNER_INFO: ';
  224 +
  225 + if (isset($node->_[HDOM_INFO_INNER])) {
  226 + $string .= "'" . $node->_[HDOM_INFO_INNER] . "'";
  227 + } else {
  228 + $string .= ' NULL ';
  229 + }
  230 +
  231 + $string .= ' children: ' . count($this->children);
  232 + $string .= ' nodes: ' . count($this->nodes);
  233 + $string .= ' tag_start: ' . $this->tag_start;
  234 + $string .= "\n";
  235 +
  236 + if ($echo) {
  237 + echo $string;
  238 + return;
  239 + } else {
  240 + return $string;
  241 + }
  242 + }
  243 +
  244 + function parent($parent = null)
  245 + {
  246 + // I am SURE that this doesn't work properly.
  247 + // It fails to unset the current node from it's current parents nodes or
  248 + // children list first.
  249 + if ($parent !== null) {
  250 + $this->parent = $parent;
  251 + $this->parent->nodes[] = $this;
  252 + $this->parent->children[] = $this;
  253 + }
  254 +
  255 + return $this->parent;
  256 + }
  257 +
  258 + function has_child()
  259 + {
  260 + return !empty($this->children);
  261 + }
  262 +
  263 + function children($idx = -1)
  264 + {
  265 + if ($idx === -1) {
  266 + return $this->children;
  267 + }
  268 +
  269 + if (isset($this->children[$idx])) {
  270 + return $this->children[$idx];
  271 + }
  272 +
  273 + return null;
  274 + }
  275 +
  276 + function first_child()
  277 + {
  278 + if (count($this->children) > 0) {
  279 + return $this->children[0];
  280 + }
  281 + return null;
  282 + }
  283 +
  284 + function last_child()
  285 + {
  286 + if (count($this->children) > 0) {
  287 + return end($this->children);
  288 + }
  289 + return null;
  290 + }
  291 +
  292 + function next_sibling()
  293 + {
  294 + if ($this->parent === null) {
  295 + return null;
  296 + }
  297 +
  298 + $idx = array_search($this, $this->parent->children, true);
  299 +
  300 + if ($idx !== false && isset($this->parent->children[$idx + 1])) {
  301 + return $this->parent->children[$idx + 1];
  302 + }
  303 +
  304 + return null;
  305 + }
  306 +
  307 + function prev_sibling()
  308 + {
  309 + if ($this->parent === null) {
  310 + return null;
  311 + }
  312 +
  313 + $idx = array_search($this, $this->parent->children, true);
  314 +
  315 + if ($idx !== false && $idx > 0) {
  316 + return $this->parent->children[$idx - 1];
  317 + }
  318 +
  319 + return null;
  320 + }
  321 +
  322 + function find_ancestor_tag($tag)
  323 + {
  324 + global $debug_object;
  325 + if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
  326 +
  327 + if ($this->parent === null) {
  328 + return null;
  329 + }
  330 +
  331 + $ancestor = $this->parent;
  332 +
  333 + while (!is_null($ancestor)) {
  334 + if (is_object($debug_object)) {
  335 + $debug_object->debug_log(2, 'Current tag is: ' . $ancestor->tag);
  336 + }
  337 +
  338 + if ($ancestor->tag === $tag) {
  339 + break;
  340 + }
  341 +
  342 + $ancestor = $ancestor->parent;
  343 + }
  344 +
  345 + return $ancestor;
  346 + }
  347 +
  348 + function innertext()
  349 + {
  350 + if (isset($this->_[HDOM_INFO_INNER])) {
  351 + return $this->_[HDOM_INFO_INNER];
  352 + }
  353 +
  354 + if (isset($this->_[HDOM_INFO_TEXT])) {
  355 + return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
  356 + }
  357 +
  358 + $ret = '';
  359 +
  360 + foreach ($this->nodes as $n) {
  361 + $ret .= $n->outertext();
  362 + }
  363 +
  364 + return $ret;
  365 + }
  366 +
  367 + function outertext()
  368 + {
  369 + global $debug_object;
  370 +
  371 + if (is_object($debug_object)) {
  372 + $text = '';
  373 +
  374 + if ($this->tag === 'text') {
  375 + if (!empty($this->text)) {
  376 + $text = ' with text: ' . $this->text;
  377 + }
  378 + }
  379 +
  380 + $debug_object->debug_log(1, 'Innertext of tag: ' . $this->tag . $text);
  381 + }
  382 +
  383 + if ($this->tag === 'root') {
  384 + return $this->innertext();
  385 + }
  386 +
  387 + // todo: What is the use of this callback? Remove?
  388 + if ($this->dom && $this->dom->callback !== null) {
  389 + call_user_func_array($this->dom->callback, array($this));
  390 + }
  391 +
  392 + if (isset($this->_[HDOM_INFO_OUTER])) {
  393 + return $this->_[HDOM_INFO_OUTER];
  394 + }
  395 +
  396 + if (isset($this->_[HDOM_INFO_TEXT])) {
  397 + return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
  398 + }
  399 +
  400 + $ret = '';
  401 +
  402 + if ($this->dom && $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]) {
  403 + $ret = $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]->makeup();
  404 + }
  405 +
  406 + if (isset($this->_[HDOM_INFO_INNER])) {
  407 + // todo: <br> should either never have HDOM_INFO_INNER or always
  408 + if ($this->tag !== 'br') {
  409 + $ret .= $this->_[HDOM_INFO_INNER];
  410 + }
  411 + } elseif ($this->nodes) {
  412 + foreach ($this->nodes as $n) {
  413 + $ret .= $this->convert_text($n->outertext());
  414 + }
  415 + }
  416 +
  417 + if (isset($this->_[HDOM_INFO_END]) && $this->_[HDOM_INFO_END] != 0) {
  418 + $ret .= '</' . $this->tag . '>';
  419 + }
  420 +
  421 + return $ret;
  422 + }
  423 +
  424 + function text()
  425 + {
  426 + if (isset($this->_[HDOM_INFO_INNER])) {
  427 + return $this->_[HDOM_INFO_INNER];
  428 + }
  429 +
  430 + switch ($this->nodetype) {
  431 + case HDOM_TYPE_TEXT: return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
  432 + case HDOM_TYPE_COMMENT: return '';
  433 + case HDOM_TYPE_UNKNOWN: return '';
  434 + }
  435 +
  436 + if (strcasecmp($this->tag, 'script') === 0) { return ''; }
  437 + if (strcasecmp($this->tag, 'style') === 0) { return ''; }
  438 +
  439 + $ret = '';
  440 +
  441 + // In rare cases, (always node type 1 or HDOM_TYPE_ELEMENT - observed
  442 + // for some span tags, and some p tags) $this->nodes is set to NULL.
  443 + // NOTE: This indicates that there is a problem where it's set to NULL
  444 + // without a clear happening.
  445 + // WHY is this happening?
  446 + if (!is_null($this->nodes)) {
  447 + foreach ($this->nodes as $n) {
  448 + // Start paragraph after a blank line
  449 + if ($n->tag === 'p') {
  450 + $ret = trim($ret) . "\n\n";
  451 + }
  452 +
  453 + $ret .= $this->convert_text($n->text());
  454 +
  455 + // If this node is a span... add a space at the end of it so
  456 + // multiple spans don't run into each other. This is plaintext
  457 + // after all.
  458 + if ($n->tag === 'span') {
  459 + $ret .= $this->dom->default_span_text;
  460 + }
  461 + }
  462 + }
  463 + return $ret;
  464 + }
  465 +
  466 + function xmltext()
  467 + {
  468 + $ret = $this->innertext();
  469 + $ret = str_ireplace('<![CDATA[', '', $ret);
  470 + $ret = str_replace(']]>', '', $ret);
  471 + return $ret;
  472 + }
  473 +
  474 + function makeup()
  475 + {
  476 + // text, comment, unknown
  477 + if (isset($this->_[HDOM_INFO_TEXT])) {
  478 + return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
  479 + }
  480 +
  481 + $ret = '<' . $this->tag;
  482 + $i = -1;
  483 +
  484 + foreach ($this->attr as $key => $val) {
  485 + ++$i;
  486 +
  487 + // skip removed attribute
  488 + if ($val === null || $val === false) { continue; }
  489 +
  490 + $ret .= $this->_[HDOM_INFO_SPACE][$i][0];
  491 +
  492 + //no value attr: nowrap, checked selected...
  493 + if ($val === true) {
  494 + $ret .= $key;
  495 + } else {
  496 + switch ($this->_[HDOM_INFO_QUOTE][$i])
  497 + {
  498 + case HDOM_QUOTE_DOUBLE: $quote = '"'; break;
  499 + case HDOM_QUOTE_SINGLE: $quote = '\''; break;
  500 + default: $quote = '';
  501 + }
  502 +
  503 + $ret .= $key
  504 + . $this->_[HDOM_INFO_SPACE][$i][1]
  505 + . '='
  506 + . $this->_[HDOM_INFO_SPACE][$i][2]
  507 + . $quote
  508 + . $val
  509 + . $quote;
  510 + }
  511 + }
  512 +
  513 + $ret = $this->dom->restore_noise($ret);
  514 + return $ret . $this->_[HDOM_INFO_ENDSPACE] . '>';
  515 + }
  516 +
  517 + function find($selector, $idx = null, $lowercase = false)
  518 + {
  519 + $selectors = $this->parse_selector($selector);
  520 + if (($count = count($selectors)) === 0) { return array(); }
  521 + $found_keys = array();
  522 +
  523 + // find each selector
  524 + for ($c = 0; $c < $count; ++$c) {
  525 + // The change on the below line was documented on the sourceforge
  526 + // code tracker id 2788009
  527 + // used to be: if (($levle=count($selectors[0]))===0) return array();
  528 + if (($levle = count($selectors[$c])) === 0) { return array(); }
  529 + if (!isset($this->_[HDOM_INFO_BEGIN])) { return array(); }
  530 +
  531 + $head = array($this->_[HDOM_INFO_BEGIN] => 1);
  532 + $cmd = ' '; // Combinator
  533 +
  534 + // handle descendant selectors, no recursive!
  535 + for ($l = 0; $l < $levle; ++$l) {
  536 + $ret = array();
  537 +
  538 + foreach ($head as $k => $v) {
  539 + $n = ($k === -1) ? $this->dom->root : $this->dom->nodes[$k];
  540 + //PaperG - Pass this optional parameter on to the seek function.
  541 + $n->seek($selectors[$c][$l], $ret, $cmd, $lowercase);
  542 + }
  543 +
  544 + $head = $ret;
  545 + $cmd = $selectors[$c][$l][4]; // Next Combinator
  546 + }
  547 +
  548 + foreach ($head as $k => $v) {
  549 + if (!isset($found_keys[$k])) {
  550 + $found_keys[$k] = 1;
  551 + }
  552 + }
  553 + }
  554 +
  555 + // sort keys
  556 + ksort($found_keys);
  557 +
  558 + $found = array();
  559 + foreach ($found_keys as $k => $v) {
  560 + $found[] = $this->dom->nodes[$k];
  561 + }
  562 +
  563 + // return nth-element or array
  564 + if (is_null($idx)) { return $found; }
  565 + elseif ($idx < 0) { $idx = count($found) + $idx; }
  566 + return (isset($found[$idx])) ? $found[$idx] : null;
  567 + }
  568 +
  569 + protected function seek($selector, &$ret, $parent_cmd, $lowercase = false)
  570 + {
  571 + global $debug_object;
  572 + if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
  573 +
  574 + list($tag, $id, $class, $attributes, $cmb) = $selector;
  575 + $nodes = array();
  576 +
  577 + if ($parent_cmd === ' ') { // Descendant Combinator
  578 + // Find parent closing tag if the current element doesn't have a closing
  579 + // tag (i.e. void element)
  580 + $end = (!empty($this->_[HDOM_INFO_END])) ? $this->_[HDOM_INFO_END] : 0;
  581 + if ($end == 0) {
  582 + $parent = $this->parent;
  583 + while (!isset($parent->_[HDOM_INFO_END]) && $parent !== null) {
  584 + $end -= 1;
  585 + $parent = $parent->parent;
  586 + }
  587 + $end += $parent->_[HDOM_INFO_END];
  588 + }
  589 +
  590 + // Get list of target nodes
  591 + $nodes_start = $this->_[HDOM_INFO_BEGIN] + 1;
  592 + $nodes_count = $end - $nodes_start;
  593 + $nodes = array_slice($this->dom->nodes, $nodes_start, $nodes_count, true);
  594 + } elseif ($parent_cmd === '>') { // Child Combinator
  595 + $nodes = $this->children;
  596 + } elseif ($parent_cmd === '+'
  597 + && $this->parent
  598 + && in_array($this, $this->parent->children)) { // Next-Sibling Combinator
  599 + $index = array_search($this, $this->parent->children, true) + 1;
  600 + if ($index < count($this->parent->children))
  601 + $nodes[] = $this->parent->children[$index];
  602 + } elseif ($parent_cmd === '~'
  603 + && $this->parent
  604 + && in_array($this, $this->parent->children)) { // Subsequent Sibling Combinator
  605 + $index = array_search($this, $this->parent->children, true);
  606 + $nodes = array_slice($this->parent->children, $index);
  607 + }
  608 +
  609 + // Go throgh each element starting at this element until the end tag
  610 + // Note: If this element is a void tag, any previous void element is
  611 + // skipped.
  612 + foreach($nodes as $node) {
  613 + $pass = true;
  614 +
  615 + // Skip root nodes
  616 + if(!$node->parent) {
  617 + $pass = false;
  618 + }
  619 +
  620 + // Handle 'text' selector
  621 + if($pass && $tag === 'text' && $node->tag === 'text') {
  622 + $ret[array_search($node, $this->dom->nodes, true)] = 1;
  623 + unset($node);
  624 + continue;
  625 + }
  626 +
  627 + // Skip if node isn't a child node (i.e. text nodes)
  628 + if($pass && !in_array($node, $node->parent->children, true)) {
  629 + $pass = false;
  630 + }
  631 +
  632 + // Skip if tag doesn't match
  633 + if ($pass && $tag !== '' && $tag !== $node->tag && $tag !== '*') {
  634 + $pass = false;
  635 + }
  636 +
  637 + // Skip if ID doesn't exist
  638 + if ($pass && $id !== '' && !isset($node->attr['id'])) {
  639 + $pass = false;
  640 + }
  641 +
  642 + // Check if ID matches
  643 + if ($pass && $id !== '' && isset($node->attr['id'])) {
  644 + // Note: Only consider the first ID (as browsers do)
  645 + $node_id = explode(' ', trim($node->attr['id']))[0];
  646 +
  647 + if($id !== $node_id) { $pass = false; }
  648 + }
  649 +
  650 + // Check if all class(es) exist
  651 + if ($pass && $class !== '' && is_array($class) && !empty($class)) {
  652 + if (isset($node->attr['class'])) {
  653 + $node_classes = explode(' ', $node->attr['class']);
  654 +
  655 + if ($lowercase) {
  656 + $node_classes = array_map('strtolower', $node_classes);
  657 + }
  658 +
  659 + foreach($class as $c) {
  660 + if(!in_array($c, $node_classes)) {
  661 + $pass = false;
  662 + break;
  663 + }
  664 + }
  665 + } else {
  666 + $pass = false;
  667 + }
  668 + }
  669 +
  670 + // Check attributes
  671 + if ($pass
  672 + && $attributes !== ''
  673 + && is_array($attributes)
  674 + && !empty($attributes)) {
  675 + foreach($attributes as $a) {
  676 + list (
  677 + $att_name,
  678 + $att_expr,
  679 + $att_val,
  680 + $att_inv,
  681 + $att_case_sensitivity
  682 + ) = $a;
  683 +
  684 + // Handle indexing attributes (i.e. "[2]")
  685 + /**
  686 + * Note: This is not supported by the CSS Standard but adds
  687 + * the ability to select items compatible to XPath (i.e.
  688 + * the 3rd element within it's parent).
  689 + *
  690 + * Note: This doesn't conflict with the CSS Standard which
  691 + * doesn't work on numeric attributes anyway.
  692 + */
  693 + if (is_numeric($att_name)
  694 + && $att_expr === ''
  695 + && $att_val === '') {
  696 + $count = 0;
  697 +
  698 + // Find index of current element in parent
  699 + foreach ($node->parent->children as $c) {
  700 + if ($c->tag === $node->tag) ++$count;
  701 + if ($c === $node) break;
  702 + }
  703 +
  704 + // If this is the correct node, continue with next
  705 + // attribute
  706 + if ($count === (int)$att_name) continue;
  707 + }
  708 +
  709 + // Check attribute availability
  710 + if ($att_inv) { // Attribute should NOT be set
  711 + if (isset($node->attr[$att_name])) {
  712 + $pass = false;
  713 + break;
  714 + }
  715 + } else { // Attribute should be set
  716 + // todo: "plaintext" is not a valid CSS selector!
  717 + if ($att_name !== 'plaintext'
  718 + && !isset($node->attr[$att_name])) {
  719 + $pass = false;
  720 + break;
  721 + }
  722 + }
  723 +
  724 + // Continue with next attribute if expression isn't defined
  725 + if ($att_expr === '') continue;
  726 +
  727 + // If they have told us that this is a "plaintext"
  728 + // search then we want the plaintext of the node - right?
  729 + // todo "plaintext" is not a valid CSS selector!
  730 + if ($att_name === 'plaintext') {
  731 + $nodeKeyValue = $node->text();
  732 + } else {
  733 + $nodeKeyValue = $node->attr[$att_name];
  734 + }
  735 +
  736 + if (is_object($debug_object)) {
  737 + $debug_object->debug_log(2,
  738 + 'testing node: '
  739 + . $node->tag
  740 + . ' for attribute: '
  741 + . $att_name
  742 + . $att_expr
  743 + . $att_val
  744 + . ' where nodes value is: '
  745 + . $nodeKeyValue
  746 + );
  747 + }
  748 +
  749 + // If lowercase is set, do a case insensitive test of
  750 + // the value of the selector.
  751 + if ($lowercase) {
  752 + $check = $this->match(
  753 + $att_expr,
  754 + strtolower($att_val),
  755 + strtolower($nodeKeyValue),
  756 + $att_case_sensitivity
  757 + );
  758 + } else {
  759 + $check = $this->match(
  760 + $att_expr,
  761 + $att_val,
  762 + $nodeKeyValue,
  763 + $att_case_sensitivity
  764 + );
  765 + }
  766 +
  767 + if (is_object($debug_object)) {
  768 + $debug_object->debug_log(2,
  769 + 'after match: '
  770 + . ($check ? 'true' : 'false')
  771 + );
  772 + }
  773 +
  774 + if (!$check) {
  775 + $pass = false;
  776 + break;
  777 + }
  778 + }
  779 + }
  780 +
  781 + // Found a match. Add to list and clear node
  782 + if ($pass) $ret[$node->_[HDOM_INFO_BEGIN]] = 1;
  783 + unset($node);
  784 + }
  785 + // It's passed by reference so this is actually what this function returns.
  786 + if (is_object($debug_object)) {
  787 + $debug_object->debug_log(1, 'EXIT - ret: ', $ret);
  788 + }
  789 + }
  790 +
  791 + protected function match($exp, $pattern, $value, $case_sensitivity)
  792 + {
  793 + global $debug_object;
  794 + if (is_object($debug_object)) {$debug_object->debug_log_entry(1);}
  795 +
  796 + if ($case_sensitivity === 'i') {
  797 + $pattern = strtolower($pattern);
  798 + $value = strtolower($value);
  799 + }
  800 +
  801 + switch ($exp) {
  802 + case '=':
  803 + return ($value === $pattern);
  804 + case '!=':
  805 + return ($value !== $pattern);
  806 + case '^=':
  807 + return preg_match('/^' . preg_quote($pattern, '/') . '/', $value);
  808 + case '$=':
  809 + return preg_match('/' . preg_quote($pattern, '/') . '$/', $value);
  810 + case '*=':
  811 + return preg_match('/' . preg_quote($pattern, '/') . '/', $value);
  812 + case '|=':
  813 + /**
  814 + * [att|=val]
  815 + *
  816 + * Represents an element with the att attribute, its value
  817 + * either being exactly "val" or beginning with "val"
  818 + * immediately followed by "-" (U+002D).
  819 + */
  820 + return strpos($value, $pattern) === 0;
  821 + case '~=':
  822 + /**
  823 + * [att~=val]
  824 + *
  825 + * Represents an element with the att attribute whose value is a
  826 + * whitespace-separated list of words, one of which is exactly
  827 + * "val". If "val" contains whitespace, it will never represent
  828 + * anything (since the words are separated by spaces). Also if
  829 + * "val" is the empty string, it will never represent anything.
  830 + */
  831 + return in_array($pattern, explode(' ', trim($value)), true);
  832 + }
  833 + return false;
  834 + }
  835 +
  836 + protected function parse_selector($selector_string)
  837 + {
  838 + global $debug_object;
  839 + if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
  840 +
  841 + /**
  842 + * Pattern of CSS selectors, modified from mootools (https://mootools.net/)
  843 + *
  844 + * Paperg: Add the colon to the attribute, so that it properly finds
  845 + * <tag attr:ibute="something" > like google does.
  846 + *
  847 + * Note: if you try to look at this attribute, you MUST use getAttribute
  848 + * since $dom->x:y will fail the php syntax check.
  849 + *
  850 + * Notice the \[ starting the attribute? and the @? following? This
  851 + * implies that an attribute can begin with an @ sign that is not
  852 + * captured. This implies that an html attribute specifier may start
  853 + * with an @ sign that is NOT captured by the expression. Farther study
  854 + * is required to determine of this should be documented or removed.
  855 + *
  856 + * Matches selectors in this order:
  857 + *
  858 + * [0] - full match
  859 + *
  860 + * [1] - tag name
  861 + * ([\w:\*-]*)
  862 + * Matches the tag name consisting of zero or more words, colons,
  863 + * asterisks and hyphens.
  864 + *
  865 + * [2] - id name
  866 + * (?:\#([\w-]+))
  867 + * Optionally matches a id name, consisting of an "#" followed by
  868 + * the id name (one or more words and hyphens).
  869 + *
  870 + * [3] - class names (including dots)
  871 + * (?:\.([\w\.-]+))?
  872 + * Optionally matches a list of classs, consisting of an "."
  873 + * followed by the class name (one or more words and hyphens)
  874 + * where multiple classes can be chained (i.e. ".foo.bar.baz")
  875 + *
  876 + * [4] - attributes
  877 + * ((?:\[@?(?:!?[\w:-]+)(?:(?:[!*^$|~]?=)[\"']?(?:.*?)[\"']?)?(?:\s*?(?:[iIsS])?)?\])+)?
  878 + * Optionally matches the attributes list
  879 + *
  880 + * [5] - separator
  881 + * ([\/, >+~]+)
  882 + * Matches the selector list separator
  883 + */
  884 + // phpcs:ignore Generic.Files.LineLength
  885 + $pattern = "/([\w:\*-]*)(?:\#([\w-]+))?(?:|\.([\w\.-]+))?((?:\[@?(?:!?[\w:-]+)(?:(?:[!*^$|~]?=)[\"']?(?:.*?)[\"']?)?(?:\s*?(?:[iIsS])?)?\])+)?([\/, >+~]+)/is";
  886 +
  887 + preg_match_all(
  888 + $pattern,
  889 + trim($selector_string) . ' ', // Add final ' ' as pseudo separator
  890 + $matches,
  891 + PREG_SET_ORDER
  892 + );
  893 +
  894 + if (is_object($debug_object)) {
  895 + $debug_object->debug_log(2, 'Matches Array: ', $matches);
  896 + }
  897 +
  898 + $selectors = array();
  899 + $result = array();
  900 +
  901 + foreach ($matches as $m) {
  902 + $m[0] = trim($m[0]);
  903 +
  904 + // Skip NoOps
  905 + if ($m[0] === '' || $m[0] === '/' || $m[0] === '//') { continue; }
  906 +
  907 + // Convert to lowercase
  908 + if ($this->dom->lowercase) {
  909 + $m[1] = strtolower($m[1]);
  910 + }
  911 +
  912 + // Extract classes
  913 + if ($m[3] !== '') { $m[3] = explode('.', $m[3]); }
  914 +
  915 + /* Extract attributes (pattern based on the pattern above!)
  916 +
  917 + * [0] - full match
  918 + * [1] - attribute name
  919 + * [2] - attribute expression
  920 + * [3] - attribute value
  921 + * [4] - case sensitivity
  922 + *
  923 + * Note: Attributes can be negated with a "!" prefix to their name
  924 + */
  925 + if($m[4] !== '') {
  926 + preg_match_all(
  927 + "/\[@?(!?[\w:-]+)(?:([!*^$|~]?=)[\"']?(.*?)[\"']?)?(?:\s+?([iIsS])?)?\]/is",
  928 + trim($m[4]),
  929 + $attributes,
  930 + PREG_SET_ORDER
  931 + );
  932 +
  933 + // Replace element by array
  934 + $m[4] = array();
  935 +
  936 + foreach($attributes as $att) {
  937 + // Skip empty matches
  938 + if(trim($att[0]) === '') { continue; }
  939 +
  940 + $inverted = (isset($att[1][0]) && $att[1][0] === '!');
  941 + $m[4][] = array(
  942 + $inverted ? substr($att[1], 1) : $att[1], // Name
  943 + (isset($att[2])) ? $att[2] : '', // Expression
  944 + (isset($att[3])) ? $att[3] : '', // Value
  945 + $inverted, // Inverted Flag
  946 + (isset($att[4])) ? strtolower($att[4]) : '', // Case-Sensitivity
  947 + );
  948 + }
  949 + }
  950 +
  951 + // Sanitize Separator
  952 + if ($m[5] !== '' && trim($m[5]) === '') { // Descendant Separator
  953 + $m[5] = ' ';
  954 + } else { // Other Separator
  955 + $m[5] = trim($m[5]);
  956 + }
  957 +
  958 + // Clear Separator if it's a Selector List
  959 + if ($is_list = ($m[5] === ',')) { $m[5] = ''; }
  960 +
  961 + // Remove full match before adding to results
  962 + array_shift($m);
  963 + $result[] = $m;
  964 +
  965 + if ($is_list) { // Selector List
  966 + $selectors[] = $result;
  967 + $result = array();
  968 + }
  969 + }
  970 +
  971 + if (count($result) > 0) { $selectors[] = $result; }
  972 + return $selectors;
  973 + }
  974 +
  975 + function __get($name)
  976 + {
  977 + if (isset($this->attr[$name])) {
  978 + return $this->convert_text($this->attr[$name]);
  979 + }
  980 + switch ($name) {
  981 + case 'outertext': return $this->outertext();
  982 + case 'innertext': return $this->innertext();
  983 + case 'plaintext': return $this->text();
  984 + case 'xmltext': return $this->xmltext();
  985 + default: return array_key_exists($name, $this->attr);
  986 + }
  987 + }
  988 +
  989 + function __set($name, $value)
  990 + {
  991 + global $debug_object;
  992 + if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
  993 +
  994 + switch ($name) {
  995 + case 'outertext': return $this->_[HDOM_INFO_OUTER] = $value;
  996 + case 'innertext':
  997 + if (isset($this->_[HDOM_INFO_TEXT])) {
  998 + return $this->_[HDOM_INFO_TEXT] = $value;
  999 + }
  1000 + return $this->_[HDOM_INFO_INNER] = $value;
  1001 + }
  1002 +
  1003 + if (!isset($this->attr[$name])) {
  1004 + $this->_[HDOM_INFO_SPACE][] = array(' ', '', '');
  1005 + $this->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_DOUBLE;
  1006 + }
  1007 +
  1008 + $this->attr[$name] = $value;
  1009 + }
  1010 +
  1011 + function __isset($name)
  1012 + {
  1013 + switch ($name) {
  1014 + case 'outertext': return true;
  1015 + case 'innertext': return true;
  1016 + case 'plaintext': return true;
  1017 + }
  1018 + //no value attr: nowrap, checked selected...
  1019 + return (array_key_exists($name, $this->attr)) ? true : isset($this->attr[$name]);
  1020 + }
  1021 +
  1022 + function __unset($name)
  1023 + {
  1024 + if (isset($this->attr[$name])) { unset($this->attr[$name]); }
  1025 + }
  1026 +
  1027 + function convert_text($text)
  1028 + {
  1029 + global $debug_object;
  1030 + if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
  1031 +
  1032 + $converted_text = $text;
  1033 +
  1034 + $sourceCharset = '';
  1035 + $targetCharset = '';
  1036 +
  1037 + if ($this->dom) {
  1038 + $sourceCharset = strtoupper($this->dom->_charset);
  1039 + $targetCharset = strtoupper($this->dom->_target_charset);
  1040 + }
  1041 +
  1042 + if (is_object($debug_object)) {
  1043 + $debug_object->debug_log(3,
  1044 + 'source charset: '
  1045 + . $sourceCharset
  1046 + . ' target charaset: '
  1047 + . $targetCharset
  1048 + );
  1049 + }
  1050 +
  1051 + if (!empty($sourceCharset)
  1052 + && !empty($targetCharset)
  1053 + && (strcasecmp($sourceCharset, $targetCharset) != 0)) {
  1054 + // Check if the reported encoding could have been incorrect and the text is actually already UTF-8
  1055 + if ((strcasecmp($targetCharset, 'UTF-8') == 0)
  1056 + && ($this->is_utf8($text))) {
  1057 + $converted_text = $text;
  1058 + } else {
  1059 + $converted_text = iconv($sourceCharset, $targetCharset, $text);
  1060 + }
  1061 + }
  1062 +
  1063 + // Lets make sure that we don't have that silly BOM issue with any of the utf-8 text we output.
  1064 + if ($targetCharset === 'UTF-8') {
  1065 + if (substr($converted_text, 0, 3) === "\xef\xbb\xbf") {
  1066 + $converted_text = substr($converted_text, 3);
  1067 + }
  1068 +
  1069 + if (substr($converted_text, -3) === "\xef\xbb\xbf") {
  1070 + $converted_text = substr($converted_text, 0, -3);
  1071 + }
  1072 + }
  1073 +
  1074 + return $converted_text;
  1075 + }
  1076 +
  1077 + static function is_utf8($str)
  1078 + {
  1079 + $c = 0; $b = 0;
  1080 + $bits = 0;
  1081 + $len = strlen($str);
  1082 + for($i = 0; $i < $len; $i++) {
  1083 + $c = ord($str[$i]);
  1084 + if($c > 128) {
  1085 + if(($c >= 254)) { return false; }
  1086 + elseif($c >= 252) { $bits = 6; }
  1087 + elseif($c >= 248) { $bits = 5; }
  1088 + elseif($c >= 240) { $bits = 4; }
  1089 + elseif($c >= 224) { $bits = 3; }
  1090 + elseif($c >= 192) { $bits = 2; }
  1091 + else { return false; }
  1092 + if(($i + $bits) > $len) { return false; }
  1093 + while($bits > 1) {
  1094 + $i++;
  1095 + $b = ord($str[$i]);
  1096 + if($b < 128 || $b > 191) { return false; }
  1097 + $bits--;
  1098 + }
  1099 + }
  1100 + }
  1101 + return true;
  1102 + }
  1103 +
  1104 + function get_display_size()
  1105 + {
  1106 + global $debug_object;
  1107 +
  1108 + $width = -1;
  1109 + $height = -1;
  1110 +
  1111 + if ($this->tag !== 'img') {
  1112 + return false;
  1113 + }
  1114 +
  1115 + // See if there is aheight or width attribute in the tag itself.
  1116 + if (isset($this->attr['width'])) {
  1117 + $width = $this->attr['width'];
  1118 + }
  1119 +
  1120 + if (isset($this->attr['height'])) {
  1121 + $height = $this->attr['height'];
  1122 + }
  1123 +
  1124 + // Now look for an inline style.
  1125 + if (isset($this->attr['style'])) {
  1126 + // Thanks to user gnarf from stackoverflow for this regular expression.
  1127 + $attributes = array();
  1128 +
  1129 + preg_match_all(
  1130 + '/([\w-]+)\s*:\s*([^;]+)\s*;?/',
  1131 + $this->attr['style'],
  1132 + $matches,
  1133 + PREG_SET_ORDER
  1134 + );
  1135 +
  1136 + foreach ($matches as $match) {
  1137 + $attributes[$match[1]] = $match[2];
  1138 + }
  1139 +
  1140 + // If there is a width in the style attributes:
  1141 + if (isset($attributes['width']) && $width == -1) {
  1142 + // check that the last two characters are px (pixels)
  1143 + if (strtolower(substr($attributes['width'], -2)) === 'px') {
  1144 + $proposed_width = substr($attributes['width'], 0, -2);
  1145 + // Now make sure that it's an integer and not something stupid.
  1146 + if (filter_var($proposed_width, FILTER_VALIDATE_INT)) {
  1147 + $width = $proposed_width;
  1148 + }
  1149 + }
  1150 + }
  1151 +
  1152 + // If there is a width in the style attributes:
  1153 + if (isset($attributes['height']) && $height == -1) {
  1154 + // check that the last two characters are px (pixels)
  1155 + if (strtolower(substr($attributes['height'], -2)) == 'px') {
  1156 + $proposed_height = substr($attributes['height'], 0, -2);
  1157 + // Now make sure that it's an integer and not something stupid.
  1158 + if (filter_var($proposed_height, FILTER_VALIDATE_INT)) {
  1159 + $height = $proposed_height;
  1160 + }
  1161 + }
  1162 + }
  1163 +
  1164 + }
  1165 +
  1166 + // Future enhancement:
  1167 + // Look in the tag to see if there is a class or id specified that has
  1168 + // a height or width attribute to it.
  1169 +
  1170 + // Far future enhancement
  1171 + // Look at all the parent tags of this image to see if they specify a
  1172 + // class or id that has an img selector that specifies a height or width
  1173 + // Note that in this case, the class or id will have the img subselector
  1174 + // for it to apply to the image.
  1175 +
  1176 + // ridiculously far future development
  1177 + // If the class or id is specified in a SEPARATE css file thats not on
  1178 + // the page, go get it and do what we were just doing for the ones on
  1179 + // the page.
  1180 +
  1181 + $result = array(
  1182 + 'height' => $height,
  1183 + 'width' => $width
  1184 + );
  1185 +
  1186 + return $result;
  1187 + }
  1188 +
  1189 + function save($filepath = '')
  1190 + {
  1191 + $ret = $this->outertext();
  1192 +
  1193 + if ($filepath !== '') {
  1194 + file_put_contents($filepath, $ret, LOCK_EX);
  1195 + }
  1196 +
  1197 + return $ret;
  1198 + }
  1199 +
  1200 + function addClass($class)
  1201 + {
  1202 + if (is_string($class)) {
  1203 + $class = explode(' ', $class);
  1204 + }
  1205 +
  1206 + if (is_array($class)) {
  1207 + foreach($class as $c) {
  1208 + if (isset($this->class)) {
  1209 + if ($this->hasClass($c)) {
  1210 + continue;
  1211 + } else {
  1212 + $this->class .= ' ' . $c;
  1213 + }
  1214 + } else {
  1215 + $this->class = $c;
  1216 + }
  1217 + }
  1218 + } else {
  1219 + if (is_object($debug_object)) {
  1220 + $debug_object->debug_log(2, 'Invalid type: ', gettype($class));
  1221 + }
  1222 + }
  1223 + }
  1224 +
  1225 + function hasClass($class)
  1226 + {
  1227 + if (is_string($class)) {
  1228 + if (isset($this->class)) {
  1229 + return in_array($class, explode(' ', $this->class), true);
  1230 + }
  1231 + } else {
  1232 + if (is_object($debug_object)) {
  1233 + $debug_object->debug_log(2, 'Invalid type: ', gettype($class));
  1234 + }
  1235 + }
  1236 +
  1237 + return false;
  1238 + }
  1239 +
  1240 + function removeClass($class = null)
  1241 + {
  1242 + if (!isset($this->class)) {
  1243 + return;
  1244 + }
  1245 +
  1246 + if (is_null($class)) {
  1247 + $this->removeAttribute('class');
  1248 + return;
  1249 + }
  1250 +
  1251 + if (is_string($class)) {
  1252 + $class = explode(' ', $class);
  1253 + }
  1254 +
  1255 + if (is_array($class)) {
  1256 + $class = array_diff(explode(' ', $this->class), $class);
  1257 + if (empty($class)) {
  1258 + $this->removeAttribute('class');
  1259 + } else {
  1260 + $this->class = implode(' ', $class);
  1261 + }
  1262 + }
  1263 + }
  1264 +
  1265 + function getAllAttributes()
  1266 + {
  1267 + return $this->attr;
  1268 + }
  1269 +
  1270 + function getAttribute($name)
  1271 + {
  1272 + return $this->__get($name);
  1273 + }
  1274 +
  1275 + function setAttribute($name, $value)
  1276 + {
  1277 + $this->__set($name, $value);
  1278 + }
  1279 +
  1280 + function hasAttribute($name)
  1281 + {
  1282 + return $this->__isset($name);
  1283 + }
  1284 +
  1285 + function removeAttribute($name)
  1286 + {
  1287 + $this->__set($name, null);
  1288 + }
  1289 +
  1290 + function remove()
  1291 + {
  1292 + if ($this->parent) {
  1293 + $this->parent->removeChild($this);
  1294 + }
  1295 + }
  1296 +
  1297 + function removeChild($node)
  1298 + {
  1299 + $nidx = array_search($node, $this->nodes, true);
  1300 + $cidx = array_search($node, $this->children, true);
  1301 + $didx = array_search($node, $this->dom->nodes, true);
  1302 +
  1303 + if ($nidx !== false && $cidx !== false && $didx !== false) {
  1304 +
  1305 + foreach($node->children as $child) {
  1306 + $node->removeChild($child);
  1307 + }
  1308 +
  1309 + foreach($node->nodes as $entity) {
  1310 + $enidx = array_search($entity, $node->nodes, true);
  1311 + $edidx = array_search($entity, $node->dom->nodes, true);
  1312 +
  1313 + if ($enidx !== false && $edidx !== false) {
  1314 + unset($node->nodes[$enidx]);
  1315 + unset($node->dom->nodes[$edidx]);
  1316 + }
  1317 + }
  1318 +
  1319 + unset($this->nodes[$nidx]);
  1320 + unset($this->children[$cidx]);
  1321 + unset($this->dom->nodes[$didx]);
  1322 +
  1323 + $node->clear();
  1324 +
  1325 + }
  1326 + }
  1327 +
  1328 + function getElementById($id)
  1329 + {
  1330 + return $this->find("#$id", 0);
  1331 + }
  1332 +
  1333 + function getElementsById($id, $idx = null)
  1334 + {
  1335 + return $this->find("#$id", $idx);
  1336 + }
  1337 +
  1338 + function getElementByTagName($name)
  1339 + {
  1340 + return $this->find($name, 0);
  1341 + }
  1342 +
  1343 + function getElementsByTagName($name, $idx = null)
  1344 + {
  1345 + return $this->find($name, $idx);
  1346 + }
  1347 +
  1348 + function parentNode()
  1349 + {
  1350 + return $this->parent();
  1351 + }
  1352 +
  1353 + function childNodes($idx = -1)
  1354 + {
  1355 + return $this->children($idx);
  1356 + }
  1357 +
  1358 + function firstChild()
  1359 + {
  1360 + return $this->first_child();
  1361 + }
  1362 +
  1363 + function lastChild()
  1364 + {
  1365 + return $this->last_child();
  1366 + }
  1367 +
  1368 + function nextSibling()
  1369 + {
  1370 + return $this->next_sibling();
  1371 + }
  1372 +
  1373 + function previousSibling()
  1374 + {
  1375 + return $this->prev_sibling();
  1376 + }
  1377 +
  1378 + function hasChildNodes()
  1379 + {
  1380 + return $this->has_child();
  1381 + }
  1382 +
  1383 + function nodeName()
  1384 + {
  1385 + return $this->tag;
  1386 + }
  1387 +
  1388 + function appendChild($node)
  1389 + {
  1390 + $node->parent($this);
  1391 + return $node;
  1392 + }
  1393 +
  1394 +}
  1395 +
  1396 +class simple_html_dom
  1397 +{
  1398 + public $root = null;
  1399 + public $nodes = array();
  1400 + public $callback = null;
  1401 + public $lowercase = false;
  1402 + public $original_size;
  1403 + public $size;
  1404 +
  1405 + protected $pos;
  1406 + protected $doc;
  1407 + protected $char;
  1408 +
  1409 + protected $cursor;
  1410 + protected $parent;
  1411 + protected $noise = array();
  1412 + protected $token_blank = " \t\r\n";
  1413 + protected $token_equal = ' =/>';
  1414 + protected $token_slash = " />\r\n\t";
  1415 + protected $token_attr = ' >';
  1416 +
  1417 + public $_charset = '';
  1418 + public $_target_charset = '';
  1419 +
  1420 + protected $default_br_text = '';
  1421 +
  1422 + public $default_span_text = '';
  1423 +
  1424 + protected $self_closing_tags = array(
  1425 + 'area' => 1,
  1426 + 'base' => 1,
  1427 + 'br' => 1,
  1428 + 'col' => 1,
  1429 + 'embed' => 1,
  1430 + 'hr' => 1,
  1431 + 'img' => 1,
  1432 + 'input' => 1,
  1433 + 'link' => 1,
  1434 + 'meta' => 1,
  1435 + 'param' => 1,
  1436 + 'source' => 1,
  1437 + 'track' => 1,
  1438 + 'wbr' => 1
  1439 + );
  1440 + protected $block_tags = array(
  1441 + 'body' => 1,
  1442 + 'div' => 1,
  1443 + 'form' => 1,
  1444 + 'root' => 1,
  1445 + 'span' => 1,
  1446 + 'table' => 1
  1447 + );
  1448 + protected $optional_closing_tags = array(
  1449 + // Not optional, see
  1450 + // https://www.w3.org/TR/html/textlevel-semantics.html#the-b-element
  1451 + 'b' => array('b' => 1),
  1452 + 'dd' => array('dd' => 1, 'dt' => 1),
  1453 + // Not optional, see
  1454 + // https://www.w3.org/TR/html/grouping-content.html#the-dl-element
  1455 + 'dl' => array('dd' => 1, 'dt' => 1),
  1456 + 'dt' => array('dd' => 1, 'dt' => 1),
  1457 + 'li' => array('li' => 1),
  1458 + 'optgroup' => array('optgroup' => 1, 'option' => 1),
  1459 + 'option' => array('optgroup' => 1, 'option' => 1),
  1460 + 'p' => array('p' => 1),
  1461 + 'rp' => array('rp' => 1, 'rt' => 1),
  1462 + 'rt' => array('rp' => 1, 'rt' => 1),
  1463 + 'td' => array('td' => 1, 'th' => 1),
  1464 + 'th' => array('td' => 1, 'th' => 1),
  1465 + 'tr' => array('td' => 1, 'th' => 1, 'tr' => 1),
  1466 + );
  1467 +
  1468 + function __construct(
  1469 + $str = null,
  1470 + $lowercase = true,
  1471 + $forceTagsClosed = true,
  1472 + $target_charset = DEFAULT_TARGET_CHARSET,
  1473 + $stripRN = true,
  1474 + $defaultBRText = DEFAULT_BR_TEXT,
  1475 + $defaultSpanText = DEFAULT_SPAN_TEXT,
  1476 + $options = 0)
  1477 + {
  1478 + if ($str) {
  1479 + if (preg_match('/^http:\/\//i', $str) || is_file($str)) {
  1480 + $this->load_file($str);
  1481 + } else {
  1482 + $this->load(
  1483 + $str,
  1484 + $lowercase,
  1485 + $stripRN,
  1486 + $defaultBRText,
  1487 + $defaultSpanText,
  1488 + $options
  1489 + );
  1490 + }
  1491 + }
  1492 + // Forcing tags to be closed implies that we don't trust the html, but
  1493 + // it can lead to parsing errors if we SHOULD trust the html.
  1494 + if (!$forceTagsClosed) {
  1495 + $this->optional_closing_array = array();
  1496 + }
  1497 +
  1498 + $this->_target_charset = $target_charset;
  1499 + }
  1500 +
  1501 + function __destruct()
  1502 + {
  1503 + $this->clear();
  1504 + }
  1505 +
  1506 + function load(
  1507 + $str,
  1508 + $lowercase = true,
  1509 + $stripRN = true,
  1510 + $defaultBRText = DEFAULT_BR_TEXT,
  1511 + $defaultSpanText = DEFAULT_SPAN_TEXT,
  1512 + $options = 0)
  1513 + {
  1514 + global $debug_object;
  1515 +
  1516 + // prepare
  1517 + $this->prepare($str, $lowercase, $defaultBRText, $defaultSpanText);
  1518 +
  1519 + // Per sourceforge http://sourceforge.net/tracker/?func=detail&aid=2949097&group_id=218559&atid=1044037
  1520 + // Script tags removal now preceeds style tag removal.
  1521 + // strip out <script> tags
  1522 + $this->remove_noise("'<\s*script[^>]*[^/]>(.*?)<\s*/\s*script\s*>'is");
  1523 + $this->remove_noise("'<\s*script\s*>(.*?)<\s*/\s*script\s*>'is");
  1524 +
  1525 + // strip out the \r \n's if we are told to.
  1526 + if ($stripRN) {
  1527 + $this->doc = str_replace("\r", ' ', $this->doc);
  1528 + $this->doc = str_replace("\n", ' ', $this->doc);
  1529 +
  1530 + // set the length of content since we have changed it.
  1531 + $this->size = strlen($this->doc);
  1532 + }
  1533 +
  1534 + // strip out cdata
  1535 + $this->remove_noise("'<!\[CDATA\[(.*?)\]\]>'is", true);
  1536 + // strip out comments
  1537 + $this->remove_noise("'<!--(.*?)-->'is");
  1538 + // strip out <style> tags
  1539 + $this->remove_noise("'<\s*style[^>]*[^/]>(.*?)<\s*/\s*style\s*>'is");
  1540 + $this->remove_noise("'<\s*style\s*>(.*?)<\s*/\s*style\s*>'is");
  1541 + // strip out preformatted tags
  1542 + $this->remove_noise("'<\s*(?:code)[^>]*>(.*?)<\s*/\s*(?:code)\s*>'is");
  1543 + // strip out server side scripts
  1544 + $this->remove_noise("'(<\?)(.*?)(\?>)'s", true);
  1545 +
  1546 + if($options & HDOM_SMARTY_AS_TEXT) { // Strip Smarty scripts
  1547 + $this->remove_noise("'(\{\w)(.*?)(\})'s", true);
  1548 + }
  1549 +
  1550 + // parsing
  1551 + $this->parse();
  1552 + // end
  1553 + $this->root->_[HDOM_INFO_END] = $this->cursor;
  1554 + $this->parse_charset();
  1555 +
  1556 + // make load function chainable
  1557 + return $this;
  1558 + }
  1559 +
  1560 + function load_file()
  1561 + {
  1562 + $args = func_get_args();
  1563 +
  1564 + if(($doc = call_user_func_array('file_get_contents', $args)) !== false) {
  1565 + $this->load($doc, true);
  1566 + } else {
  1567 + return false;
  1568 + }
  1569 + }
  1570 +
  1571 + function set_callback($function_name)
  1572 + {
  1573 + $this->callback = $function_name;
  1574 + }
  1575 +
  1576 + function remove_callback()
  1577 + {
  1578 + $this->callback = null;
  1579 + }
  1580 +
  1581 + function save($filepath = '')
  1582 + {
  1583 + $ret = $this->root->innertext();
  1584 + if ($filepath !== '') { file_put_contents($filepath, $ret, LOCK_EX); }
  1585 + return $ret;
  1586 + }
  1587 +
  1588 + function find($selector, $idx = null, $lowercase = false)
  1589 + {
  1590 + return $this->root->find($selector, $idx, $lowercase);
  1591 + }
  1592 +
  1593 + function clear()
  1594 + {
  1595 + if (isset($this->nodes)) {
  1596 + foreach ($this->nodes as $n) {
  1597 + $n->clear();
  1598 + $n = null;
  1599 + }
  1600 + }
  1601 +
  1602 + // This add next line is documented in the sourceforge repository.
  1603 + // 2977248 as a fix for ongoing memory leaks that occur even with the
  1604 + // use of clear.
  1605 + if (isset($this->children)) {
  1606 + foreach ($this->children as $n) {
  1607 + $n->clear();
  1608 + $n = null;
  1609 + }
  1610 + }
  1611 +
  1612 + if (isset($this->parent)) {
  1613 + $this->parent->clear();
  1614 + unset($this->parent);
  1615 + }
  1616 +
  1617 + if (isset($this->root)) {
  1618 + $this->root->clear();
  1619 + unset($this->root);
  1620 + }
  1621 +
  1622 + unset($this->doc);
  1623 + unset($this->noise);
  1624 + }
  1625 +
  1626 + function dump($show_attr = true)
  1627 + {
  1628 + $this->root->dump($show_attr);
  1629 + }
  1630 +
  1631 + protected function prepare(
  1632 + $str, $lowercase = true,
  1633 + $defaultBRText = DEFAULT_BR_TEXT,
  1634 + $defaultSpanText = DEFAULT_SPAN_TEXT)
  1635 + {
  1636 + $this->clear();
  1637 +
  1638 + $this->doc = trim($str);
  1639 + $this->size = strlen($this->doc);
  1640 + $this->original_size = $this->size; // original size of the html
  1641 + $this->pos = 0;
  1642 + $this->cursor = 1;
  1643 + $this->noise = array();
  1644 + $this->nodes = array();
  1645 + $this->lowercase = $lowercase;
  1646 + $this->default_br_text = $defaultBRText;
  1647 + $this->default_span_text = $defaultSpanText;
  1648 + $this->root = new simple_html_dom_node($this);
  1649 + $this->root->tag = 'root';
  1650 + $this->root->_[HDOM_INFO_BEGIN] = -1;
  1651 + $this->root->nodetype = HDOM_TYPE_ROOT;
  1652 + $this->parent = $this->root;
  1653 + if ($this->size > 0) { $this->char = $this->doc[0]; }
  1654 + }
  1655 +
  1656 + protected function parse()
  1657 + {
  1658 + while (true) {
  1659 + // Read next tag if there is no text between current position and the
  1660 + // next opening tag.
  1661 + if (($s = $this->copy_until_char('<')) === '') {
  1662 + if($this->read_tag()) {
  1663 + continue;
  1664 + } else {
  1665 + return true;
  1666 + }
  1667 + }
  1668 +
  1669 + // Add a text node for text between tags
  1670 + $node = new simple_html_dom_node($this);
  1671 + ++$this->cursor;
  1672 + $node->_[HDOM_INFO_TEXT] = $s;
  1673 + $this->link_nodes($node, false);
  1674 + }
  1675 + }
  1676 +
  1677 + protected function parse_charset()
  1678 + {
  1679 + global $debug_object;
  1680 +
  1681 + $charset = null;
  1682 +
  1683 + if (function_exists('get_last_retrieve_url_contents_content_type')) {
  1684 + $contentTypeHeader = get_last_retrieve_url_contents_content_type();
  1685 + $success = preg_match('/charset=(.+)/', $contentTypeHeader, $matches);
  1686 + if ($success) {
  1687 + $charset = $matches[1];
  1688 + if (is_object($debug_object)) {
  1689 + $debug_object->debug_log(2,
  1690 + 'header content-type found charset of: '
  1691 + . $charset
  1692 + );
  1693 + }
  1694 + }
  1695 + }
  1696 +
  1697 + if (empty($charset)) {
  1698 + // https://www.w3.org/TR/html/document-metadata.html#statedef-http-equiv-content-type
  1699 + $el = $this->root->find('meta[http-equiv=Content-Type]', 0, true);
  1700 +
  1701 + if (!empty($el)) {
  1702 + $fullvalue = $el->content;
  1703 + if (is_object($debug_object)) {
  1704 + $debug_object->debug_log(2,
  1705 + 'meta content-type tag found'
  1706 + . $fullvalue
  1707 + );
  1708 + }
  1709 +
  1710 + if (!empty($fullvalue)) {
  1711 + $success = preg_match(
  1712 + '/charset=(.+)/i',
  1713 + $fullvalue,
  1714 + $matches
  1715 + );
  1716 +
  1717 + if ($success) {
  1718 + $charset = $matches[1];
  1719 + } else {
  1720 + // If there is a meta tag, and they don't specify the
  1721 + // character set, research says that it's typically
  1722 + // ISO-8859-1
  1723 + if (is_object($debug_object)) {
  1724 + $debug_object->debug_log(2,
  1725 + 'meta content-type tag couldn\'t be parsed. using iso-8859 default.'
  1726 + );
  1727 + }
  1728 +
  1729 + $charset = 'ISO-8859-1';
  1730 + }
  1731 + }
  1732 + }
  1733 + }
  1734 +
  1735 + if (empty($charset)) {
  1736 + // https://www.w3.org/TR/html/document-metadata.html#character-encoding-declaration
  1737 + if ($meta = $this->root->find('meta[charset]', 0)) {
  1738 + $charset = $meta->charset;
  1739 + if (is_object($debug_object)) {
  1740 + $debug_object->debug_log(2, 'meta charset: ' . $charset);
  1741 + }
  1742 + }
  1743 + }
  1744 +
  1745 + if (empty($charset)) {
  1746 + // Try to guess the charset based on the content
  1747 + // Requires Multibyte String (mbstring) support (optional)
  1748 + if (function_exists('mb_detect_encoding')) {
  1749 + /**
  1750 + * mb_detect_encoding() is not intended to distinguish between
  1751 + * charsets, especially single-byte charsets. Its primary
  1752 + * purpose is to detect which multibyte encoding is in use,
  1753 + * i.e. UTF-8, UTF-16, shift-JIS, etc.
  1754 + *
  1755 + * -- https://bugs.php.net/bug.php?id=38138
  1756 + *
  1757 + * Adding both CP1251/ISO-8859-5 and CP1252/ISO-8859-1 will
  1758 + * always result in CP1251/ISO-8859-5 and vice versa.
  1759 + *
  1760 + * Thus, only detect if it's either UTF-8 or CP1252/ISO-8859-1
  1761 + * to stay compatible.
  1762 + */
  1763 + $encoding = mb_detect_encoding(
  1764 + $this->doc,
  1765 + array( 'UTF-8', 'CP1252', 'ISO-8859-1' )
  1766 + );
  1767 +
  1768 + if ($encoding === 'CP1252' || $encoding === 'ISO-8859-1') {
  1769 + // Due to a limitation of mb_detect_encoding
  1770 + // 'CP1251'/'ISO-8859-5' will be detected as
  1771 + // 'CP1252'/'ISO-8859-1'. This will cause iconv to fail, in
  1772 + // which case we can simply assume it is the other charset.
  1773 + if (!@iconv('CP1252', 'UTF-8', $this->doc)) {
  1774 + $encoding = 'CP1251';
  1775 + }
  1776 + }
  1777 +
  1778 + if ($encoding !== false) {
  1779 + $charset = $encoding;
  1780 + if (is_object($debug_object)) {
  1781 + $debug_object->debug_log(2, 'mb_detect: ' . $charset);
  1782 + }
  1783 + }
  1784 + }
  1785 + }
  1786 +
  1787 + if (empty($charset)) {
  1788 + // Assume it's UTF-8 as it is the most likely charset to be used
  1789 + $charset = 'UTF-8';
  1790 + if (is_object($debug_object)) {
  1791 + $debug_object->debug_log(2, 'No match found, assume ' . $charset);
  1792 + }
  1793 + }
  1794 +
  1795 + // Since CP1252 is a superset, if we get one of it's subsets, we want
  1796 + // it instead.
  1797 + if ((strtolower($charset) == 'iso-8859-1')
  1798 + || (strtolower($charset) == 'latin1')
  1799 + || (strtolower($charset) == 'latin-1')) {
  1800 + $charset = 'CP1252';
  1801 + if (is_object($debug_object)) {
  1802 + $debug_object->debug_log(2,
  1803 + 'replacing ' . $charset . ' with CP1252 as its a superset'
  1804 + );
  1805 + }
  1806 + }
  1807 +
  1808 + if (is_object($debug_object)) {
  1809 + $debug_object->debug_log(1, 'EXIT - ' . $charset);
  1810 + }
  1811 +
  1812 + return $this->_charset = $charset;
  1813 + }
  1814 +
  1815 + protected function read_tag()
  1816 + {
  1817 + // Set end position if no further tags found
  1818 + if ($this->char !== '<') {
  1819 + $this->root->_[HDOM_INFO_END] = $this->cursor;
  1820 + return false;
  1821 + }
  1822 +
  1823 + $begin_tag_pos = $this->pos;
  1824 + $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
  1825 +
  1826 + // end tag
  1827 + if ($this->char === '/') {
  1828 + $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
  1829 +
  1830 + // Skip whitespace in end tags (i.e. in "</ html>")
  1831 + $this->skip($this->token_blank);
  1832 + $tag = $this->copy_until_char('>');
  1833 +
  1834 + // Skip attributes in end tags
  1835 + if (($pos = strpos($tag, ' ')) !== false) {
  1836 + $tag = substr($tag, 0, $pos);
  1837 + }
  1838 +
  1839 + $parent_lower = strtolower($this->parent->tag);
  1840 + $tag_lower = strtolower($tag);
  1841 +
  1842 + // The end tag is supposed to close the parent tag. Handle situations
  1843 + // when it doesn't
  1844 + if ($parent_lower !== $tag_lower) {
  1845 + // Parent tag does not have to be closed necessarily (optional closing tag)
  1846 + // Current tag is a block tag, so it may close an ancestor
  1847 + if (isset($this->optional_closing_tags[$parent_lower])
  1848 + && isset($this->block_tags[$tag_lower])) {
  1849 +
  1850 + $this->parent->_[HDOM_INFO_END] = 0;
  1851 + $org_parent = $this->parent;
  1852 +
  1853 + // Traverse ancestors to find a matching opening tag
  1854 + // Stop at root node
  1855 + while (($this->parent->parent)
  1856 + && strtolower($this->parent->tag) !== $tag_lower
  1857 + ){
  1858 + $this->parent = $this->parent->parent;
  1859 + }
  1860 +
  1861 + // If we don't have a match add current tag as text node
  1862 + if (strtolower($this->parent->tag) !== $tag_lower) {
  1863 + $this->parent = $org_parent; // restore origonal parent
  1864 +
  1865 + if ($this->parent->parent) {
  1866 + $this->parent = $this->parent->parent;
  1867 + }
  1868 +
  1869 + $this->parent->_[HDOM_INFO_END] = $this->cursor;
  1870 + return $this->as_text_node($tag);
  1871 + }
  1872 + } elseif (($this->parent->parent)
  1873 + && isset($this->block_tags[$tag_lower])
  1874 + ) {
  1875 + // Grandparent exists and current tag is a block tag, so our
  1876 + // parent doesn't have an end tag
  1877 + $this->parent->_[HDOM_INFO_END] = 0; // No end tag
  1878 + $org_parent = $this->parent;
  1879 +
  1880 + // Traverse ancestors to find a matching opening tag
  1881 + // Stop at root node
  1882 + while (($this->parent->parent)
  1883 + && strtolower($this->parent->tag) !== $tag_lower
  1884 + ) {
  1885 + $this->parent = $this->parent->parent;
  1886 + }
  1887 +
  1888 + // If we don't have a match add current tag as text node
  1889 + if (strtolower($this->parent->tag) !== $tag_lower) {
  1890 + $this->parent = $org_parent; // restore origonal parent
  1891 + $this->parent->_[HDOM_INFO_END] = $this->cursor;
  1892 + return $this->as_text_node($tag);
  1893 + }
  1894 + } elseif (($this->parent->parent)
  1895 + && strtolower($this->parent->parent->tag) === $tag_lower
  1896 + ) { // Grandparent exists and current tag closes it
  1897 + $this->parent->_[HDOM_INFO_END] = 0;
  1898 + $this->parent = $this->parent->parent;
  1899 + } else { // Random tag, add as text node
  1900 + return $this->as_text_node($tag);
  1901 + }
  1902 + }
  1903 +
  1904 + // Set end position of parent tag to current cursor position
  1905 + $this->parent->_[HDOM_INFO_END] = $this->cursor;
  1906 +
  1907 + if ($this->parent->parent) {
  1908 + $this->parent = $this->parent->parent;
  1909 + }
  1910 +
  1911 + $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
  1912 + return true;
  1913 + }
  1914 +
  1915 + // start tag
  1916 + $node = new simple_html_dom_node($this);
  1917 + $node->_[HDOM_INFO_BEGIN] = $this->cursor;
  1918 + ++$this->cursor;
  1919 + $tag = $this->copy_until($this->token_slash); // Get tag name
  1920 + $node->tag_start = $begin_tag_pos;
  1921 +
  1922 + // doctype, cdata & comments...
  1923 + // <!DOCTYPE html>
  1924 + // <![CDATA[ ... ]]>
  1925 + // <!-- Comment -->
  1926 + if (isset($tag[0]) && $tag[0] === '!') {
  1927 + $node->_[HDOM_INFO_TEXT] = '<' . $tag . $this->copy_until_char('>');
  1928 +
  1929 + if (isset($tag[2]) && $tag[1] === '-' && $tag[2] === '-') { // Comment ("<!--")
  1930 + $node->nodetype = HDOM_TYPE_COMMENT;
  1931 + $node->tag = 'comment';
  1932 + } else { // Could be doctype or CDATA but we don't care
  1933 + $node->nodetype = HDOM_TYPE_UNKNOWN;
  1934 + $node->tag = 'unknown';
  1935 + }
  1936 +
  1937 + if ($this->char === '>') { $node->_[HDOM_INFO_TEXT] .= '>'; }
  1938 +
  1939 + $this->link_nodes($node, true);
  1940 + $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
  1941 + return true;
  1942 + }
  1943 +
  1944 + // The start tag cannot contain another start tag, if so add as text
  1945 + // i.e. "<<html>"
  1946 + if ($pos = strpos($tag, '<') !== false) {
  1947 + $tag = '<' . substr($tag, 0, -1);
  1948 + $node->_[HDOM_INFO_TEXT] = $tag;
  1949 + $this->link_nodes($node, false);
  1950 + $this->char = $this->doc[--$this->pos]; // prev
  1951 + return true;
  1952 + }
  1953 +
  1954 + // Handle invalid tag names (i.e. "<html#doc>")
  1955 + if (!preg_match('/^\w[\w:-]*$/', $tag)) {
  1956 + $node->_[HDOM_INFO_TEXT] = '<' . $tag . $this->copy_until('<>');
  1957 +
  1958 + // Next char is the beginning of a new tag, don't touch it.
  1959 + if ($this->char === '<') {
  1960 + $this->link_nodes($node, false);
  1961 + return true;
  1962 + }
  1963 +
  1964 + // Next char closes current tag, add and be done with it.
  1965 + if ($this->char === '>') { $node->_[HDOM_INFO_TEXT] .= '>'; }
  1966 + $this->link_nodes($node, false);
  1967 + $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
  1968 + return true;
  1969 + }
  1970 +
  1971 + // begin tag, add new node
  1972 + $node->nodetype = HDOM_TYPE_ELEMENT;
  1973 + $tag_lower = strtolower($tag);
  1974 + $node->tag = ($this->lowercase) ? $tag_lower : $tag;
  1975 +
  1976 + // handle optional closing tags
  1977 + if (isset($this->optional_closing_tags[$tag_lower])) {
  1978 + // Traverse ancestors to close all optional closing tags
  1979 + while (isset($this->optional_closing_tags[$tag_lower][strtolower($this->parent->tag)])) {
  1980 + $this->parent->_[HDOM_INFO_END] = 0;
  1981 + $this->parent = $this->parent->parent;
  1982 + }
  1983 + $node->parent = $this->parent;
  1984 + }
  1985 +
  1986 + $guard = 0; // prevent infinity loop
  1987 +
  1988 + // [0] Space between tag and first attribute
  1989 + $space = array($this->copy_skip($this->token_blank), '', '');
  1990 +
  1991 + // attributes
  1992 + do {
  1993 + // Everything until the first equal sign should be the attribute name
  1994 + $name = $this->copy_until($this->token_equal);
  1995 +
  1996 + if ($name === '' && $this->char !== null && $space[0] === '') {
  1997 + break;
  1998 + }
  1999 +
  2000 + if ($guard === $this->pos) { // Escape infinite loop
  2001 + $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
  2002 + continue;
  2003 + }
  2004 +
  2005 + $guard = $this->pos;
  2006 +
  2007 + // handle endless '<'
  2008 + // Out of bounds before the tag ended
  2009 + if ($this->pos >= $this->size - 1 && $this->char !== '>') {
  2010 + $node->nodetype = HDOM_TYPE_TEXT;
  2011 + $node->_[HDOM_INFO_END] = 0;
  2012 + $node->_[HDOM_INFO_TEXT] = '<' . $tag . $space[0] . $name;
  2013 + $node->tag = 'text';
  2014 + $this->link_nodes($node, false);
  2015 + return true;
  2016 + }
  2017 +
  2018 + // handle mismatch '<'
  2019 + // Attributes cannot start after opening tag
  2020 + if ($this->doc[$this->pos - 1] == '<') {
  2021 + $node->nodetype = HDOM_TYPE_TEXT;
  2022 + $node->tag = 'text';
  2023 + $node->attr = array();
  2024 + $node->_[HDOM_INFO_END] = 0;
  2025 + $node->_[HDOM_INFO_TEXT] = substr(
  2026 + $this->doc,
  2027 + $begin_tag_pos,
  2028 + $this->pos - $begin_tag_pos - 1
  2029 + );
  2030 + $this->pos -= 2;
  2031 + $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
  2032 + $this->link_nodes($node, false);
  2033 + return true;
  2034 + }
  2035 +
  2036 + if ($name !== '/' && $name !== '') { // this is a attribute name
  2037 + // [1] Whitespace after attribute name
  2038 + $space[1] = $this->copy_skip($this->token_blank);
  2039 +
  2040 + $name = $this->restore_noise($name); // might be a noisy name
  2041 +
  2042 + if ($this->lowercase) { $name = strtolower($name); }
  2043 +
  2044 + if ($this->char === '=') { // attribute with value
  2045 + $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
  2046 + $this->parse_attr($node, $name, $space); // get attribute value
  2047 + } else {
  2048 + //no value attr: nowrap, checked selected...
  2049 + $node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_NO;
  2050 + $node->attr[$name] = true;
  2051 + if ($this->char != '>') { $this->char = $this->doc[--$this->pos]; } // prev
  2052 + }
  2053 +
  2054 + $node->_[HDOM_INFO_SPACE][] = $space;
  2055 +
  2056 + // prepare for next attribute
  2057 + $space = array(
  2058 + $this->copy_skip($this->token_blank),
  2059 + '',
  2060 + ''
  2061 + );
  2062 + } else { // no more attributes
  2063 + break;
  2064 + }
  2065 + } while ($this->char !== '>' && $this->char !== '/'); // go until the tag ended
  2066 +
  2067 + $this->link_nodes($node, true);
  2068 + $node->_[HDOM_INFO_ENDSPACE] = $space[0];
  2069 +
  2070 + // handle empty tags (i.e. "<div/>")
  2071 + if ($this->copy_until_char('>') === '/') {
  2072 + $node->_[HDOM_INFO_ENDSPACE] .= '/';
  2073 + $node->_[HDOM_INFO_END] = 0;
  2074 + } else {
  2075 + // reset parent
  2076 + if (!isset($this->self_closing_tags[strtolower($node->tag)])) {
  2077 + $this->parent = $node;
  2078 + }
  2079 + }
  2080 +
  2081 + $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
  2082 +
  2083 + // If it's a BR tag, we need to set it's text to the default text.
  2084 + // This way when we see it in plaintext, we can generate formatting that the user wants.
  2085 + // since a br tag never has sub nodes, this works well.
  2086 + if ($node->tag === 'br') {
  2087 + $node->_[HDOM_INFO_INNER] = $this->default_br_text;
  2088 + }
  2089 +
  2090 + return true;
  2091 + }
  2092 +
  2093 + protected function parse_attr($node, $name, &$space)
  2094 + {
  2095 + $is_duplicate = isset($node->attr[$name]);
  2096 +
  2097 + if (!$is_duplicate) // Copy whitespace between "=" and value
  2098 + $space[2] = $this->copy_skip($this->token_blank);
  2099 +
  2100 + switch ($this->char) {
  2101 + case '"':
  2102 + $quote_type = HDOM_QUOTE_DOUBLE;
  2103 + $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
  2104 + $value = $this->copy_until_char('"');
  2105 + $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
  2106 + break;
  2107 + case '\'':
  2108 + $quote_type = HDOM_QUOTE_SINGLE;
  2109 + $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
  2110 + $value = $this->copy_until_char('\'');
  2111 + $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
  2112 + break;
  2113 + default:
  2114 + $quote_type = HDOM_QUOTE_NO;
  2115 + $value = $this->copy_until($this->token_attr);
  2116 + }
  2117 +
  2118 + $value = $this->restore_noise($value);
  2119 +
  2120 + // PaperG: Attributes should not have \r or \n in them, that counts as
  2121 + // html whitespace.
  2122 + $value = str_replace("\r", '', $value);
  2123 + $value = str_replace("\n", '', $value);
  2124 +
  2125 + // PaperG: If this is a "class" selector, lets get rid of the preceeding
  2126 + // and trailing space since some people leave it in the multi class case.
  2127 + if ($name === 'class') {
  2128 + $value = trim($value);
  2129 + }
  2130 +
  2131 + if (!$is_duplicate) {
  2132 + $node->_[HDOM_INFO_QUOTE][] = $quote_type;
  2133 + $node->attr[$name] = $value;
  2134 + }
  2135 + }
  2136 +
  2137 + protected function link_nodes(&$node, $is_child)
  2138 + {
  2139 + $node->parent = $this->parent;
  2140 + $this->parent->nodes[] = $node;
  2141 + if ($is_child) {
  2142 + $this->parent->children[] = $node;
  2143 + }
  2144 + }
  2145 +
  2146 + protected function as_text_node($tag)
  2147 + {
  2148 + $node = new simple_html_dom_node($this);
  2149 + ++$this->cursor;
  2150 + $node->_[HDOM_INFO_TEXT] = '</' . $tag . '>';
  2151 + $this->link_nodes($node, false);
  2152 + $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
  2153 + return true;
  2154 + }
  2155 +
  2156 + protected function skip($chars)
  2157 + {
  2158 + $this->pos += strspn($this->doc, $chars, $this->pos);
  2159 + $this->char = ($this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
  2160 + }
  2161 +
  2162 + protected function copy_skip($chars)
  2163 + {
  2164 + $pos = $this->pos;
  2165 + $len = strspn($this->doc, $chars, $pos);
  2166 + $this->pos += $len;
  2167 + $this->char = ($this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
  2168 + if ($len === 0) { return ''; }
  2169 + return substr($this->doc, $pos, $len);
  2170 + }
  2171 +
  2172 + protected function copy_until($chars)
  2173 + {
  2174 + $pos = $this->pos;
  2175 + $len = strcspn($this->doc, $chars, $pos);
  2176 + $this->pos += $len;
  2177 + $this->char = ($this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
  2178 + return substr($this->doc, $pos, $len);
  2179 + }
  2180 +
  2181 + protected function copy_until_char($char)
  2182 + {
  2183 + if ($this->char === null) { return ''; }
  2184 +
  2185 + if (($pos = strpos($this->doc, $char, $this->pos)) === false) {
  2186 + $ret = substr($this->doc, $this->pos, $this->size - $this->pos);
  2187 + $this->char = null;
  2188 + $this->pos = $this->size;
  2189 + return $ret;
  2190 + }
  2191 +
  2192 + if ($pos === $this->pos) { return ''; }
  2193 +
  2194 + $pos_old = $this->pos;
  2195 + $this->char = $this->doc[$pos];
  2196 + $this->pos = $pos;
  2197 + return substr($this->doc, $pos_old, $pos - $pos_old);
  2198 + }
  2199 +
  2200 + protected function remove_noise($pattern, $remove_tag = false)
  2201 + {
  2202 + global $debug_object;
  2203 + if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
  2204 +
  2205 + $count = preg_match_all(
  2206 + $pattern,
  2207 + $this->doc,
  2208 + $matches,
  2209 + PREG_SET_ORDER | PREG_OFFSET_CAPTURE
  2210 + );
  2211 +
  2212 + for ($i = $count - 1; $i > -1; --$i) {
  2213 + $key = '___noise___' . sprintf('% 5d', count($this->noise) + 1000);
  2214 +
  2215 + if (is_object($debug_object)) {
  2216 + $debug_object->debug_log(2, 'key is: ' . $key);
  2217 + }
  2218 +
  2219 + $idx = ($remove_tag) ? 0 : 1; // 0 = entire match, 1 = submatch
  2220 + $this->noise[$key] = $matches[$i][$idx][0];
  2221 + $this->doc = substr_replace($this->doc, $key, $matches[$i][$idx][1], strlen($matches[$i][$idx][0]));
  2222 + }
  2223 +
  2224 + // reset the length of content
  2225 + $this->size = strlen($this->doc);
  2226 +
  2227 + if ($this->size > 0) {
  2228 + $this->char = $this->doc[0];
  2229 + }
  2230 + }
  2231 +
  2232 + function restore_noise($text)
  2233 + {
  2234 + global $debug_object;
  2235 + if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
  2236 +
  2237 + while (($pos = strpos($text, '___noise___')) !== false) {
  2238 + // Sometimes there is a broken piece of markup, and we don't GET the
  2239 + // pos+11 etc... token which indicates a problem outside of us...
  2240 +
  2241 + // todo: "___noise___1000" (or any number with four or more digits)
  2242 + // in the DOM causes an infinite loop which could be utilized by
  2243 + // malicious software
  2244 + if (strlen($text) > $pos + 15) {
  2245 + $key = '___noise___'
  2246 + . $text[$pos + 11]
  2247 + . $text[$pos + 12]
  2248 + . $text[$pos + 13]
  2249 + . $text[$pos + 14]
  2250 + . $text[$pos + 15];
  2251 +
  2252 + if (is_object($debug_object)) {
  2253 + $debug_object->debug_log(2, 'located key of: ' . $key);
  2254 + }
  2255 +
  2256 + if (isset($this->noise[$key])) {
  2257 + $text = substr($text, 0, $pos)
  2258 + . $this->noise[$key]
  2259 + . substr($text, $pos + 16);
  2260 + } else {
  2261 + // do this to prevent an infinite loop.
  2262 + $text = substr($text, 0, $pos)
  2263 + . 'UNDEFINED NOISE FOR KEY: '
  2264 + . $key
  2265 + . substr($text, $pos + 16);
  2266 + }
  2267 + } else {
  2268 + // There is no valid key being given back to us... We must get
  2269 + // rid of the ___noise___ or we will have a problem.
  2270 + $text = substr($text, 0, $pos)
  2271 + . 'NO NUMERIC NOISE KEY'
  2272 + . substr($text, $pos + 11);
  2273 + }
  2274 + }
  2275 + return $text;
  2276 + }
  2277 +
  2278 + function search_noise($text)
  2279 + {
  2280 + global $debug_object;
  2281 + if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
  2282 +
  2283 + foreach($this->noise as $noiseElement) {
  2284 + if (strpos($noiseElement, $text) !== false) {
  2285 + return $noiseElement;
  2286 + }
  2287 + }
  2288 + }
  2289 +
  2290 + function __toString()
  2291 + {
  2292 + return $this->root->innertext();
  2293 + }
  2294 +
  2295 + function __get($name)
  2296 + {
  2297 + switch ($name) {
  2298 + case 'outertext':
  2299 + return $this->root->innertext();
  2300 + case 'innertext':
  2301 + return $this->root->innertext();
  2302 + case 'plaintext':
  2303 + return $this->root->text();
  2304 + case 'charset':
  2305 + return $this->_charset;
  2306 + case 'target_charset':
  2307 + return $this->_target_charset;
  2308 + }
  2309 + }
  2310 +
  2311 + function childNodes($idx = -1)
  2312 + {
  2313 + return $this->root->childNodes($idx);
  2314 + }
  2315 +
  2316 + function firstChild()
  2317 + {
  2318 + return $this->root->first_child();
  2319 + }
  2320 +
  2321 + function lastChild()
  2322 + {
  2323 + return $this->root->last_child();
  2324 + }
  2325 +
  2326 + function createElement($name, $value = null)
  2327 + {
  2328 + return @str_get_html("<$name>$value</$name>")->firstChild();
  2329 + }
  2330 +
  2331 + function createTextNode($value)
  2332 + {
  2333 + return @end(str_get_html($value)->nodes);
  2334 + }
  2335 +
  2336 + function getElementById($id)
  2337 + {
  2338 + return $this->find("#$id", 0);
  2339 + }
  2340 +
  2341 + function getElementsById($id, $idx = null)
  2342 + {
  2343 + return $this->find("#$id", $idx);
  2344 + }
  2345 +
  2346 + function getElementByTagName($name)
  2347 + {
  2348 + return $this->find($name, 0);
  2349 + }
  2350 +
  2351 + function getElementsByTagName($name, $idx = -1)
  2352 + {
  2353 + return $this->find($name, $idx);
  2354 + }
  2355 +
  2356 + function loadFile()
  2357 + {
  2358 + $args = func_get_args();
  2359 + $this->load_file($args);
  2360 + }
  2361 +}
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Aside\Project; @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Aside\Project;
4 4
5 use App\Enums\Common\Code; 5 use App\Enums\Common\Code;
6 use App\Helper\Arr; 6 use App\Helper\Arr;
  7 +use App\Helper\Country;
7 use App\Helper\QuanqiusouApi; 8 use App\Helper\QuanqiusouApi;
8 use App\Http\Controllers\Aside\BaseController; 9 use App\Http\Controllers\Aside\BaseController;
9 use App\Http\Logic\Aside\Manage\ManageLogic; 10 use App\Http\Logic\Aside\Manage\ManageLogic;
@@ -898,4 +899,28 @@ class ProjectController extends BaseController @@ -898,4 +899,28 @@ class ProjectController extends BaseController
898 $lists = $webLanguageModel->list(); 899 $lists = $webLanguageModel->list();
899 $this->response('success',Code::SUCCESS,$lists); 900 $this->response('success',Code::SUCCESS,$lists);
900 } 901 }
  902 +
  903 + /**
  904 + * 获取国家地区列表
  905 + * @author zbj
  906 + * @date 2024/1/19
  907 + */
  908 + public function countryLists(){
  909 + $this->response('success',Code::SUCCESS, Country::getCountryList());
  910 + }
  911 +
  912 + /**
  913 + * 保存询盘过滤配置
  914 + * @author zbj
  915 + * @date 2024/1/19
  916 + */
  917 + public function saveInquiryFilterConfig(ProjectLogic $logic){
  918 + $this->request->validate([
  919 + 'project_id'=>'required',
  920 + ],[
  921 + 'project_id.required' => '项目id不能为空',
  922 + ]);
  923 + $logic->saveInquiryFilterConfig($this->param);
  924 + $this->response('success');
  925 + }
901 } 926 }
@@ -102,7 +102,7 @@ class LoginController extends BaseController @@ -102,7 +102,7 @@ class LoginController extends BaseController
102 * @method :post 102 * @method :post
103 * @time :2023/8/19 9:13 103 * @time :2023/8/19 9:13
104 */ 104 */
105 - public function sendLoginSms() 105 + public function sendLoginSms($type = SmsLog::TYPE_LOGIN)
106 { 106 {
107 $this->request->validate([ 107 $this->request->validate([
108 'mobile'=>['required', 'regex:/^1[3-9]\d{9}$/'], 108 'mobile'=>['required', 'regex:/^1[3-9]\d{9}$/'],
@@ -115,7 +115,7 @@ class LoginController extends BaseController @@ -115,7 +115,7 @@ class LoginController extends BaseController
115 if (empty($user)) { 115 if (empty($user)) {
116 $this->response('请输入正确的手机号码!', Code::SYSTEM_ERROR); 116 $this->response('请输入正确的手机号码!', Code::SYSTEM_ERROR);
117 } 117 }
118 - $last_sms = SmsLog::getLastLog($mobile, SmsLog::TYPE_LOGIN); 118 + $last_sms = SmsLog::getLastLog($mobile, $type);
119 if ($last_sms && $last_sms->use = SmsLog::USE_USABLE && time() - strtotime($last_sms->created_at) < 60) { 119 if ($last_sms && $last_sms->use = SmsLog::USE_USABLE && time() - strtotime($last_sms->created_at) < 60) {
120 $this->response('请不要重复发送短信!', Code::SYSTEM_ERROR); 120 $this->response('请不要重复发送短信!', Code::SYSTEM_ERROR);
121 } 121 }
@@ -126,7 +126,7 @@ class LoginController extends BaseController @@ -126,7 +126,7 @@ class LoginController extends BaseController
126 if (empty($send->Code) && $send->Code != 'OK') { 126 if (empty($send->Code) && $send->Code != 'OK') {
127 $this->response('发送失败, 请稍后重试!', Code::SYSTEM_ERROR); 127 $this->response('发送失败, 请稍后重试!', Code::SYSTEM_ERROR);
128 } 128 }
129 - SmsLog::createLog($mobile, $code['code']); 129 + SmsLog::createLog($mobile, $code['code'],$type);
130 $this->response('success'); 130 $this->response('success');
131 } 131 }
132 132
@@ -70,4 +70,21 @@ class ExtendController extends BaseController @@ -70,4 +70,21 @@ class ExtendController extends BaseController
70 $extendLogic->extendDel(); 70 $extendLogic->extendDel();
71 $this->response('success'); 71 $this->response('success');
72 } 72 }
  73 +
  74 +
  75 + /**
  76 + * 可搜索的字段列表
  77 + * @author zbj
  78 + * @date 2024/1/22
  79 + */
  80 + public function search_filed(){
  81 + $map = [
  82 + 'title' => '产品标题',
  83 + 'intro' => '短描述',
  84 + ];
  85 + //文本框类型扩展字段
  86 + $extends = Extend::where('type', 1)->pluck('title', 'key')->toArray();
  87 + $data = array_merge($map, $extends);
  88 + $this->response('success',Code::SUCCESS,$data);
  89 + }
73 } 90 }
@@ -246,7 +246,7 @@ class ProductController extends BaseController @@ -246,7 +246,7 @@ class ProductController extends BaseController
246 //获取当前用户选择的模版 246 //获取当前用户选择的模版
247 $v['video'] = json_decode($v['video'] ?? ''); 247 $v['video'] = json_decode($v['video'] ?? '');
248 $template_id = $this->getTemplateId(BTemplate::SOURCE_PRODUCT,BTemplate::IS_DETAIL); 248 $template_id = $this->getTemplateId(BTemplate::SOURCE_PRODUCT,BTemplate::IS_DETAIL);
249 - $v['is_renovation'] = $this->getIsRenovation(BTemplate::SOURCE_PRODUCT,BTemplate::IS_DETAIL,$template_id,$v['id']); 249 + $v['is_renovation'] = $this->getIsRenovation(BTemplate::SOURCE_PRODUCT,BTemplate::IS_DETAIL,$template_id,$v['id'] ?? 0);
250 $v['url'] = $this->user['domain'].$v['route']; 250 $v['url'] = $this->user['domain'].$v['route'];
251 //获取当前数据扩展字段及值 251 //获取当前数据扩展字段及值
252 $v['extend'] = $this->getExtendInfo($v['id']); 252 $v['extend'] = $this->getExtendInfo($v['id']);
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :RatingController.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2024/1/20 14:02
  8 + */
  9 +
  10 +namespace App\Http\Controllers\Bside\Scoring;
  11 +
  12 +use App\Enums\Common\Code;
  13 +use App\Http\Controllers\Bside\BaseController;
  14 +use App\Http\Logic\Bside\Scoring\RatingLogic;
  15 +use App\Models\Scoring\RatingQuestion;
  16 +use App\Models\Scoring\ScoringSystem;
  17 +use App\Models\Sms\SmsLog;
  18 +use App\Models\User\User;
  19 +use Mrgoon\AliSms\AliSms;
  20 +
  21 +/**
  22 + * @remark :评分系统问题管理
  23 + * @name :RatingController
  24 + * @author :lyh
  25 + * @method :post
  26 + * @time :2024/1/20 14:02
  27 + */
  28 +class RatingController extends BaseController
  29 +{
  30 + /**
  31 + * @remark :获取问卷调查记录(阶段记录)
  32 + * @name :getHistory
  33 + * @author :lyh
  34 + * @method :post
  35 + * @time :2024/1/20 15:03
  36 + */
  37 + public function getHistory(ScoringSystem $scoringSystem){
  38 + $this->request->validate([
  39 + 'type' => 'required',
  40 + ],[
  41 + 'type.required' => '问题类型不能为空',
  42 + ]);
  43 + $info = $scoringSystem->read($this->map);
  44 + $this->response('success',Code::SUCCESS,$info);
  45 + }
  46 +
  47 + /**
  48 + * @remark :问卷调查详情
  49 + * @name :getProjectRead
  50 + * @author :lyh
  51 + * @method :post
  52 + * @time :2024/1/20 14:11
  53 + */
  54 + public function getProjectRead(RatingLogic $ratingLogic){
  55 + $this->request->validate([
  56 + 'type' => 'required',
  57 + ],[
  58 + 'type.required' => '问题类型不能为空',
  59 + ]);
  60 + $info = $ratingLogic->getRatingRead();
  61 + $this->response('success',Code::SUCCESS,$info);
  62 + }
  63 +
  64 + /**
  65 + * @remark :提交评分
  66 + * @name :save
  67 + * @author :lyh
  68 + * @method :post
  69 + * @time :2024/1/20 14:43
  70 + */
  71 + public function save(RatingLogic $ratingLogic){
  72 + $this->request->validate([
  73 + 'data' => 'required',
  74 + 'mobile' => 'required',
  75 + ],[
  76 + 'data.required' => '请填写完整',
  77 + 'mobile.required' => '手机号码不能为空',
  78 + ]);
  79 + $ratingLogic->ratingSave();
  80 + $this->response('success');
  81 + }
  82 +}
@@ -129,7 +129,11 @@ class TranslateController extends BaseController @@ -129,7 +129,11 @@ class TranslateController extends BaseController
129 case RouteMap::SOURCE_PRODUCT_CATE: 129 case RouteMap::SOURCE_PRODUCT_CATE:
130 //获取当前产品分类关联多少产品 130 //获取当前产品分类关联多少产品
131 $productModel = new Product(); 131 $productModel = new Product();
132 - $count = $productModel->formatQuery(['category_id'=>['like','%,'.$v['source_id'].',%']])->count(); 132 + if($v['route'] == 'products'){
  133 + $count = $productModel->formatQuery(['status'=>1])->count();
  134 + }else{
  135 + $count = $productModel->formatQuery(['category_id'=>['like','%,'.$v['source_id'].',%'],'status'=>1])->count();
  136 + }
133 $this->pageList($data,$count,$v,1,15); 137 $this->pageList($data,$count,$v,1,15);
134 break; 138 break;
135 case RouteMap::SOURCE_BLOG: 139 case RouteMap::SOURCE_BLOG:
@@ -140,12 +144,20 @@ class TranslateController extends BaseController @@ -140,12 +144,20 @@ class TranslateController extends BaseController
140 break; 144 break;
141 case RouteMap::SOURCE_BLOG_CATE: 145 case RouteMap::SOURCE_BLOG_CATE:
142 $blogModel = new Blog(); 146 $blogModel = new Blog();
143 - $count = $blogModel->formatQuery(['category_id'=>['like','%,'.$v['source_id'].',%']])->count(); 147 + if($v['route'] == 'blog'){
  148 + $count = $blogModel->formatQuery(['status'=>1])->count();
  149 + }else{
  150 + $count = $blogModel->formatQuery(['category_id'=>['like','%,'.$v['source_id'].',%'],'status'=>1])->count();
  151 + }
144 $this->pageList($data,$count,$v,2,10); 152 $this->pageList($data,$count,$v,2,10);
145 break; 153 break;
146 case RouteMap::SOURCE_NEWS_CATE: 154 case RouteMap::SOURCE_NEWS_CATE:
147 $newsModel = new News(); 155 $newsModel = new News();
148 - $count = $newsModel->formatQuery(['category_id'=>['like','%,'.$v['source_id'].',%']])->count(); 156 + if($v['route'] == 'news'){
  157 + $count = $newsModel->formatQuery(['status'=>1])->count();
  158 + }else{
  159 + $count = $newsModel->formatQuery(['category_id'=>['like','%,'.$v['source_id'].',%'],'status'=>1])->count();
  160 + }
149 $this->pageList($data,$count,$v,3,10); 161 $this->pageList($data,$count,$v,3,10);
150 break; 162 break;
151 case RouteMap::SOURCE_MODULE: 163 case RouteMap::SOURCE_MODULE:
@@ -198,7 +210,11 @@ class TranslateController extends BaseController @@ -198,7 +210,11 @@ class TranslateController extends BaseController
198 case RouteMap::SOURCE_PRODUCT_CATE: 210 case RouteMap::SOURCE_PRODUCT_CATE:
199 //获取当前产品分类关联多少产品 211 //获取当前产品分类关联多少产品
200 $productModel = new Product(); 212 $productModel = new Product();
201 - $count = $productModel->formatQuery(['category_id'=>['like','%,'.$v['source_id'].',%']])->count(); 213 + if($v['route'] == 'products'){
  214 + $count = $productModel->formatQuery(['status'=>1])->count();
  215 + }else{
  216 + $count = $productModel->formatQuery(['category_id'=>['like','%,'.$v['source_id'].',%'],'status'=>1])->count();
  217 + }
202 $this->pageSixList($data,$count,$v,1,15); 218 $this->pageSixList($data,$count,$v,1,15);
203 break; 219 break;
204 case RouteMap::SOURCE_BLOG: 220 case RouteMap::SOURCE_BLOG:
@@ -209,12 +225,20 @@ class TranslateController extends BaseController @@ -209,12 +225,20 @@ class TranslateController extends BaseController
209 break; 225 break;
210 case RouteMap::SOURCE_BLOG_CATE: 226 case RouteMap::SOURCE_BLOG_CATE:
211 $blogModel = new Blog(); 227 $blogModel = new Blog();
212 - $count = $blogModel->formatQuery(['category_id'=>['like','%,'.$v['source_id'].',%']])->count(); 228 + if($v['route'] == 'blog'){
  229 + $count = $blogModel->formatQuery(['status'=>1])->count();
  230 + }else{
  231 + $count = $blogModel->formatQuery(['category_id'=>['like','%,'.$v['source_id'].',%'],'status'=>1])->count();
  232 + }
213 $this->pageSixList($data,$count,$v,2,10); 233 $this->pageSixList($data,$count,$v,2,10);
214 break; 234 break;
215 case RouteMap::SOURCE_NEWS_CATE: 235 case RouteMap::SOURCE_NEWS_CATE:
216 $newsModel = new News(); 236 $newsModel = new News();
217 - $count = $newsModel->formatQuery(['category_id'=>['like','%,'.$v['source_id'].',%']])->count(); 237 + if($v['route'] == 'news'){
  238 + $count = $newsModel->formatQuery(['status'=>1])->count();
  239 + }else{
  240 + $count = $newsModel->formatQuery(['category_id'=>['like','%,'.$v['source_id'].',%'],'status'=>1])->count();
  241 + }
218 $this->pageSixList($data,$count,$v,3,10); 242 $this->pageSixList($data,$count,$v,3,10);
219 break; 243 break;
220 case RouteMap::SOURCE_MODULE: 244 case RouteMap::SOURCE_MODULE:
@@ -8,6 +8,7 @@ use App\Exceptions\AsideGlobalException; @@ -8,6 +8,7 @@ use App\Exceptions\AsideGlobalException;
8 use App\Models\Com\NoticeLog; 8 use App\Models\Com\NoticeLog;
9 use App\Models\Com\UpdateLog; 9 use App\Models\Com\UpdateLog;
10 use App\Models\Devops\ServerConfig; 10 use App\Models\Devops\ServerConfig;
  11 +use App\Models\Project\InquiryFilterConfig;
11 use App\Models\Project\ProjectRenew; 12 use App\Models\Project\ProjectRenew;
12 use App\Models\Template\Setting; 13 use App\Models\Template\Setting;
13 use App\Models\User\ProjectMenu; 14 use App\Models\User\ProjectMenu;
@@ -35,6 +36,7 @@ use App\Models\Task\Task; @@ -35,6 +36,7 @@ use App\Models\Task\Task;
35 use App\Services\ProjectServer; 36 use App\Services\ProjectServer;
36 use Hashids\Hashids; 37 use Hashids\Hashids;
37 use App\Models\User\User as UserModel; 38 use App\Models\User\User as UserModel;
  39 +use Illuminate\Support\Facades\Cache;
38 use Illuminate\Support\Facades\DB; 40 use Illuminate\Support\Facades\DB;
39 use Illuminate\Support\Facades\Http; 41 use Illuminate\Support\Facades\Http;
40 use Illuminate\Support\Facades\Schema; 42 use Illuminate\Support\Facades\Schema;
@@ -63,8 +65,7 @@ class ProjectLogic extends BaseLogic @@ -63,8 +65,7 @@ class ProjectLogic extends BaseLogic
63 * @time :2023/7/28 17:11 65 * @time :2023/7/28 17:11
64 */ 66 */
65 public function getProjectInfo($id){ 67 public function getProjectInfo($id){
66 - $info = $this->model->with('payment')->with('deploy_build')->with('deploy_optimize')->with('online_check')  
67 - ->with('project_after')->where(['id'=>$id])->first()->toArray(); 68 + $info = $this->model->with(['payment', 'deploy_build', 'deploy_optimize', 'online_check', 'project_after','inquiry_filter_config'])->where(['id'=>$id])->first()->toArray();
68 $info['online_check']['name'] = (new Manage())->getName($info['online_check']['created_manage_id'] ?? 0); 69 $info['online_check']['name'] = (new Manage())->getName($info['online_check']['created_manage_id'] ?? 0);
69 $info['deploy_optimize']['minor_keywords'] = !empty($info['deploy_optimize']['minor_keywords']) ? json_decode($info['deploy_optimize']['minor_keywords']) : []; 70 $info['deploy_optimize']['minor_keywords'] = !empty($info['deploy_optimize']['minor_keywords']) ? json_decode($info['deploy_optimize']['minor_keywords']) : [];
70 $info['init_domain'] = $this->getInitDomain($info['serve_id'])['domain']; 71 $info['init_domain'] = $this->getInitDomain($info['serve_id'])['domain'];
@@ -145,6 +146,10 @@ class ProjectLogic extends BaseLogic @@ -145,6 +146,10 @@ class ProjectLogic extends BaseLogic
145 $this->saveProjectDeployOptimize($this->param['deploy_optimize']); 146 $this->saveProjectDeployOptimize($this->param['deploy_optimize']);
146 //保存售后信息 147 //保存售后信息
147 $this->saveProjectAfter($this->param['project_after']); 148 $this->saveProjectAfter($this->param['project_after']);
  149 + //保存询盘过滤配置
  150 +// $this->param['inquiry_filter_config']['project_id'] = $this->param['id'];
  151 +// $this->saveInquiryFilterConfig($this->param['inquiry_filter_config']);
  152 +
148 //创建站点 153 //创建站点
149 // $this->createSite($this->param); 154 // $this->createSite($this->param);
150 } 155 }
@@ -193,7 +198,7 @@ class ProjectLogic extends BaseLogic @@ -193,7 +198,7 @@ class ProjectLogic extends BaseLogic
193 } 198 }
194 $param['remain_day'] = $param['deploy_build']['service_duration'] - $param['finish_remain_day']; 199 $param['remain_day'] = $param['deploy_build']['service_duration'] - $param['finish_remain_day'];
195 $param['remain_day'] = ($param['remain_day'] > 0) ? $param['remain_day'] : 0; 200 $param['remain_day'] = ($param['remain_day'] > 0) ? $param['remain_day'] : 0;
196 - unset($param['payment'],$param['deploy_build'],$param['deploy_optimize'],$param['online_check'],$param['project_after']); 201 + unset($param['payment'],$param['deploy_build'],$param['deploy_optimize'],$param['online_check'],$param['project_after'],$param['inquiry_filter_config']);
197 //文件上传默认值 202 //文件上传默认值
198 if($param['is_upload_manage']){ 203 if($param['is_upload_manage']){
199 $param['upload_config'] = [ 204 $param['upload_config'] = [
@@ -310,6 +315,36 @@ class ProjectLogic extends BaseLogic @@ -310,6 +315,36 @@ class ProjectLogic extends BaseLogic
310 } 315 }
311 316
312 /** 317 /**
  318 + * 保存询盘过滤配置
  319 + * @param $config
  320 + * @return array
  321 + * @author zbj
  322 + * @date 2024/1/19
  323 + */
  324 + public function saveInquiryFilterConfig($config){
  325 +
  326 + $config['filter_countries'] = Arr::a2s(!empty($config['filter_countries']) ? $config['filter_countries'] : []);
  327 + $config['filter_contents'] = Arr::a2s(!empty($config['filter_contents']) ? $config['filter_contents'] : []);
  328 + $config['filter_referers'] = Arr::a2s(!empty($config['filter_referers']) ? $config['filter_referers'] : []);
  329 + $config['filter_emails'] = Arr::a2s(!empty($config['filter_emails']) ? $config['filter_emails'] : []);
  330 + $config['filter_mobiles'] = Arr::a2s(!empty($config['filter_mobiles']) ? $config['filter_mobiles'] : []);
  331 + $config['filter_names'] = Arr::a2s(!empty($config['filter_names']) ? $config['filter_names'] : []);
  332 + $config['black_ips'] = $config['black_ips'] ?? '';
  333 +
  334 + $model = InquiryFilterConfig::where('project_id', $config['project_id'])->first();
  335 + if(!$model){
  336 + $model = new InquiryFilterConfig();
  337 + $model->add($config);
  338 + }else{
  339 + $model->edit($config,['project_id'=>$config['project_id']]);
  340 + }
  341 +
  342 + Cache::forget(InquiryFilterConfig::cacheKey($config['project_id']));
  343 +
  344 + return $this->success();
  345 + }
  346 +
  347 + /**
313 * @remark :根据类型状态设置 348 * @remark :根据类型状态设置
314 * @name :setTypeStatusEdit 349 * @name :setTypeStatusEdit
315 * @author :lyh 350 * @author :lyh
@@ -40,6 +40,7 @@ class BTemplateLogLogic extends BaseLogic @@ -40,6 +40,7 @@ class BTemplateLogLogic extends BaseLogic
40 } 40 }
41 $bTemplateModel = new BTemplate(); 41 $bTemplateModel = new BTemplate();
42 if($info['template_id'] == 0){//定制项目 42 if($info['template_id'] == 0){//定制项目
  43 + //TODO::还原头部+底部
43 $bTemplateModel->edit(['html'=>$info['text']],['template_id'=>$info['template_id'],'source'=>$info['source'],'source_id'=>$info['source_id']]); 44 $bTemplateModel->edit(['html'=>$info['text']],['template_id'=>$info['template_id'],'source'=>$info['source'],'source_id'=>$info['source_id']]);
44 return $this->success(); 45 return $this->success();
45 } 46 }
@@ -121,23 +122,31 @@ class BTemplateLogLogic extends BaseLogic @@ -121,23 +122,31 @@ class BTemplateLogLogic extends BaseLogic
121 if($info === false){ 122 if($info === false){
122 $this->fail('当前数据不存在,或已被删除'); 123 $this->fail('当前数据不存在,或已被删除');
123 } 124 }
124 - if(empty($info['other'])){  
125 - $footer_other = str_replace('<header','',characterTruncation($info['text'],'/<style id="globalsojs-footer">(.*?)<header/s'));  
126 - $info['other'] = preg_replace('/<style id="globalsojs-footer">(.*?)<\/style>/s', '', $footer_other);  
127 - }  
128 - $html = $info['head_css'].$info['main_css'].$info['footer_css'].$info['other'].  
129 - $info['head_html'].$info['main_html'].$info['footer_html'];  
130 - $serviceSettingModel = new ServiceSettingModel();  
131 - $list = $serviceSettingModel->list(['type'=>2],'created_at');  
132 - //拼接html  
133 - foreach ($list as $v){  
134 - if($v['key'] == 'head'){  
135 - $html = $v['values'].$html; 125 + if($this->user['is_customized'] != BTemplate::IS_VISUALIZATION){
  126 + if(empty($info['other'])){
  127 + $footer_other = str_replace('<header','',characterTruncation($info['text'],'/<style id="globalsojs-footer">(.*?)<header/s'));
  128 + $info['other'] = preg_replace('/<style id="globalsojs-footer">(.*?)<\/style>/s', '', $footer_other);
136 } 129 }
137 - if($v['key'] == 'footer'){  
138 - $html = $html.$v['values']; 130 + $html = $info['head_css'].$info['main_css'].$info['footer_css'].$info['other'].
  131 + $info['head_html'].$info['main_html'].$info['footer_html'];
  132 + $serviceSettingModel = new ServiceSettingModel();
  133 + $list = $serviceSettingModel->list(['type'=>2],'created_at');
  134 + //拼接html
  135 + foreach ($list as $v){
  136 + if($v['key'] == 'head'){
  137 + $html = $v['values'].$html;
  138 + }
  139 + if($v['key'] == 'footer'){
  140 + $html = $html.$v['values'];
  141 + }
  142 + }
  143 + }else{
  144 + $page_array = (array)$this->user['is_visualization']->page_array;//获取所有定制界面
  145 + if (in_array(1, $page_array)) {//首页是定制界面
  146 + $html = $info['text'];
139 } 147 }
140 } 148 }
141 return $this->success(['html'=>$html]); 149 return $this->success(['html'=>$html]);
142 } 150 }
  151 +
143 } 152 }
@@ -62,7 +62,6 @@ class MonthCountLogic extends BaseLogic @@ -62,7 +62,6 @@ class MonthCountLogic extends BaseLogic
62 $startTime = Carbon::now()->startOfMonth()->toDateString(); 62 $startTime = Carbon::now()->startOfMonth()->toDateString();
63 $endTime = date('Y-m-d',time()); 63 $endTime = date('Y-m-d',time());
64 $arr = []; 64 $arr = [];
65 - ProjectServer::useProject($this->user['project_id']);  
66 $arr = $this->inquiryCount($arr,$startTime,$endTime,$this->user['domain']); 65 $arr = $this->inquiryCount($arr,$startTime,$endTime,$this->user['domain']);
67 $arr = $this->flowCount($arr,$startTime,$endTime,$this->user['project_id']); 66 $arr = $this->flowCount($arr,$startTime,$endTime,$this->user['project_id']);
68 $arr = $this->sourceCount($arr,$startTime,$endTime,$this->user['domain']); 67 $arr = $this->sourceCount($arr,$startTime,$endTime,$this->user['domain']);
@@ -91,11 +90,11 @@ class MonthCountLogic extends BaseLogic @@ -91,11 +90,11 @@ class MonthCountLogic extends BaseLogic
91 foreach ($data as $v){ 90 foreach ($data as $v){
92 if(($startTime.' 00:00:00' <= $v['submit_time']) && $v['submit_time'] <= $endTime.' 23:59:59'){ 91 if(($startTime.' 00:00:00' <= $v['submit_time']) && $v['submit_time'] <= $endTime.' 23:59:59'){
93 $arr['month_total']++; 92 $arr['month_total']++;
94 - }  
95 - if(isset($countryArr[$v['country']])){  
96 - $countryArr[$v['country']]++;  
97 - }else{  
98 - $countryArr[$v['country']] = 1; 93 + if(isset($countryArr[$v['country']])){
  94 + $countryArr[$v['country']]++;
  95 + }else{
  96 + $countryArr[$v['country']] = 1;
  97 + }
99 } 98 }
100 } 99 }
101 } 100 }
@@ -123,6 +123,7 @@ class NavLogic extends BaseLogic @@ -123,6 +123,7 @@ class NavLogic extends BaseLogic
123 'remark'=>$param['remark'] ?? '', 123 'remark'=>$param['remark'] ?? '',
124 'group_id'=>$param['group_id'], 124 'group_id'=>$param['group_id'],
125 'show'=>$param['show'] ?? 0, 125 'show'=>$param['show'] ?? 0,
  126 + 'class'=>$param['class'] ?? ''
126 ]; 127 ];
127 return $this->success($data); 128 return $this->success($data);
128 } 129 }
@@ -510,6 +510,8 @@ class ProductLogic extends BaseLogic @@ -510,6 +510,8 @@ class ProductLogic extends BaseLogic
510 'created_uid'=>$this->user['id'], 510 'created_uid'=>$this->user['id'],
511 'created_at'=>date('Y-m-d H:i:s'), 511 'created_at'=>date('Y-m-d H:i:s'),
512 'updated_at'=>date('Y-m-d H:i:s'), 512 'updated_at'=>date('Y-m-d H:i:s'),
  513 + 'six_read'=>$info['six_read'],
  514 + 'is_upgrade'=>$info['is_upgrade']
513 ]; 515 ];
514 if(isset($info['icon']) && !empty($info['icon'])){ 516 if(isset($info['icon']) && !empty($info['icon'])){
515 foreach ($info['icon'] as $k1 => $v1){ 517 foreach ($info['icon'] as $k1 => $v1){
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :RatingLogic.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2024/1/20 14:15
  8 + */
  9 +
  10 +namespace App\Http\Logic\Bside\Scoring;
  11 +
  12 +use App\Http\Logic\Bside\BaseLogic;
  13 +use App\Models\Scoring\RatingQuestion;
  14 +use App\Models\Scoring\ScoringSystem;
  15 +
  16 +class RatingLogic extends BaseLogic
  17 +{
  18 + public function __construct()
  19 + {
  20 + parent::__construct();
  21 + $this->model = new RatingQuestion();
  22 + $this->scoringModel = new ScoringSystem();
  23 + $this->param = $this->requestAll;
  24 + }
  25 +
  26 + /**
  27 + * @remark :获取详情
  28 + * @name :getRatingRead
  29 + * @author :lyh
  30 + * @method :post
  31 + * @time :2024/1/20 14:27
  32 + */
  33 + public function getRatingRead(){
  34 + $data = [
  35 + 'company'=>$this->project['company'],
  36 + 'mobile'=>$this->project['mobile'],
  37 + 'uptime'=>$this->project['uptime'],
  38 + 'domain'=>$this->user['domain'],
  39 + 'question'=>$this->model->list(['type'=>$this->param['type']]),
  40 + ];
  41 + return $this->success($data);
  42 + }
  43 +
  44 + /**
  45 + * @remark :提交统计
  46 + * @name :ratingSave
  47 + * @author :lyh
  48 + * @method :post
  49 + * @time :2024/1/20 14:46
  50 + */
  51 + public function ratingSave(){
  52 + $param = [
  53 + 'data'=>json_encode($this->param['data']),
  54 + 'mobile'=>$this->param['mobile'],
  55 + 'project_id'=>$this->user['project_id'],
  56 + ];
  57 + return $this->scoringModel->add($param);
  58 + }
  59 +}
@@ -49,6 +49,9 @@ class TranslateLogic extends BaseLogic @@ -49,6 +49,9 @@ class TranslateLogic extends BaseLogic
49 $new_key = $this->getUrlRead($url); 49 $new_key = $this->getUrlRead($url);
50 if($info === false){ 50 if($info === false){
51 $translate_list = Translate::tran($new_key, $languageInfo['short']); 51 $translate_list = Translate::tran($new_key, $languageInfo['short']);
  52 + if(empty($translate_list)){
  53 + $this->fail('翻译失败,请联系管理员');
  54 + }
52 foreach ($new_key as $k=>$v){ 55 foreach ($new_key as $k=>$v){
53 $data[] = [ 56 $data[] = [
54 trim($v)=>$translate_list[$k], 57 trim($v)=>$translate_list[$k],
@@ -70,10 +73,12 @@ class TranslateLogic extends BaseLogic @@ -70,10 +73,12 @@ class TranslateLogic extends BaseLogic
70 $arr2[0]=>$translate_list 73 $arr2[0]=>$translate_list
71 ]; 74 ];
72 }else{ 75 }else{
73 - foreach ($arr2 as $k => $v){  
74 - $data[] = [  
75 - trim($v)=>$translate_list[$k]  
76 - ]; 76 + if(!empty($translate_list)){
  77 + foreach ($arr2 as $k => $v){
  78 + $data[] = [
  79 + trim($v)=>$translate_list[$k]
  80 + ];
  81 + }
77 } 82 }
78 } 83 }
79 } 84 }
@@ -155,53 +160,33 @@ class TranslateLogic extends BaseLogic @@ -155,53 +160,33 @@ class TranslateLogic extends BaseLogic
155 * @time :2023/11/22 10:02 160 * @time :2023/11/22 10:02
156 */ 161 */
157 public function getUrlRead($url){ 162 public function getUrlRead($url){
158 - $contextOptions = [  
159 - 'ssl' => [  
160 - 'verify_peer' => false,  
161 - 'verify_peer_name' => false,  
162 - ],  
163 - ];  
164 - $context = stream_context_create($contextOptions);  
165 - $sourceCode = file_get_contents($url, false, $context);  
166 - if(!$sourceCode){  
167 - $this->fail('当前url不存在');  
168 - }  
169 - // 过滤掉具有 "change-language-cont" 类的元素  
170 - $pattern = '/<div\b[^>]*\sclass=[\'"]([^\'"]*change-language-cont[^\'"]*)[\'"][^>]*>(.*?)<\/div>/is';  
171 - $sourceCode = preg_replace($pattern, '', $sourceCode);  
172 - $pattern = '/<style\b[^>]*>(.*?)<\/style>/s'; // 定义匹配`<style>`标签及其内容的正则表达式  
173 - $strippedContent = preg_replace($pattern, '', $sourceCode); // 删除`<style>`标签及其内容  
174 - $pattern = '/<script\b[^>]*>(.*?)<\/script>/s'; // 定义匹配`<script>`标签及其内容的正则表达式  
175 - $strippedContent = preg_replace($pattern, '', $strippedContent); // 删除`<script>`标签及其内容  
176 - $pattern = '/<link\b[^>]*>/'; // 定义匹配 `<link>` 标签的正则表达式  
177 - $strippedContent = preg_replace($pattern, '', $strippedContent); // 删除 `<link>` 标签  
178 - $pattern = '/>([^<]+)</'; // 定义匹配中间内容不是标签的正则表达式  
179 - $matches = array();  
180 - preg_match_all($pattern, $strippedContent, $matches);  
181 - $textContentArray = array_filter($matches[1], function($item) {  
182 - return !empty(trim($item));  
183 - });  
184 - // 过滤掉包含逗号加换行的内容  
185 - $textContentArray = array_filter($textContentArray, function($item) {  
186 - return strpos($item, ',') === false && strpos($item, PHP_EOL) === false;  
187 - });  
188 - $contentData = [];  
189 - foreach ($textContentArray as $v){  
190 - $content = trim($v);  
191 - $trimmedString = preg_replace('/\s+/', ' ', $content);  
192 - $contentData[] = $trimmedString;  
193 - }  
194 - $contentData = array_values($textContentArray);  
195 - $pattern = '/<meta\s+[^>]*name=[\'"](keywords|description)[\'"][^>]*content=[\'"]([^\'"]+)[\'"]>/i'; // 匹配 name 为 "keywords" 或 "description" 的 meta 标签的正则表达式  
196 - $matches = array();  
197 - preg_match_all($pattern, $strippedContent, $matches);  
198 - $metaData = array();  
199 - foreach ($matches[2] as $index => $content) {  
200 - if(!empty(trim($content))){  
201 - $metaData[] = $content; 163 + $dom = file_get_html($url);
  164 + $texts = $dom->find("text");
  165 + $description = $dom->find("meta[name=description]",0);
  166 + $keywords = $dom->find("meta[name=keywords]",0);
  167 + // 组装需要翻译的内容 HTML内文案、meta description、meta keywords
  168 + $need_tran = [];
  169 + foreach ($texts as $k=>$text) {
  170 + $tag= $text->parent()->tag;
  171 + if (in_array($tag, ['script', 'style', 'root'])){
  172 + continue;
  173 + }
  174 + $string = trim($text->text());
  175 + if (empty($string)){
  176 + continue;
  177 + }
  178 + $country_class = '';
  179 + if (method_exists($text->parent()->parent(),"find") && $text->parent()->parent()->find("b")) {
  180 + $country_class = $text->parent()->parent()->find("b",0)->class;
  181 + }
  182 + if(FALSE !== strpos($country_class, 'country-flag')) {
  183 + continue;
202 } 184 }
  185 + $need_tran[] = htmlspecialchars_decode(html_entity_decode($string));
203 } 186 }
204 - $data = array_merge($metaData, $contentData); 187 + $need_tran[] = $description->attr['content'];
  188 + $need_tran[] = $keywords->attr['content'];
  189 + return $need_tran;
205 return $data; 190 return $data;
206 } 191 }
207 192
@@ -244,7 +229,16 @@ class TranslateLogic extends BaseLogic @@ -244,7 +229,16 @@ class TranslateLogic extends BaseLogic
244 * @time :2023/6/12 10:52 229 * @time :2023/6/12 10:52
245 */ 230 */
246 public function translateSave(){ 231 public function translateSave(){
247 -// try { 232 + $data = [];
  233 + //处理传递的data
  234 + foreach ($this->param['data'] as $k => $v){
  235 + if(!empty($v) && is_array($v)){
  236 + foreach ($v as $text => $translate){
  237 + $data[$text] = $translate;
  238 + }
  239 + }
  240 + }
  241 + try {
248 $info = $this->model->read(['language_id'=>$this->param['language_id'],'url'=>$this->param['url'],'type'=>$this->param['type']]); 242 $info = $this->model->read(['language_id'=>$this->param['language_id'],'url'=>$this->param['url'],'type'=>$this->param['type']]);
249 if($info === false){ 243 if($info === false){
250 $param = [ 244 $param = [
@@ -254,15 +248,67 @@ class TranslateLogic extends BaseLogic @@ -254,15 +248,67 @@ class TranslateLogic extends BaseLogic
254 'language_id'=>$this->param['language_id'], 248 'language_id'=>$this->param['language_id'],
255 'alias'=>$this->param['alias'], 249 'alias'=>$this->param['alias'],
256 ]; 250 ];
257 - $param['data'] = json_encode($this->param['data'],JSON_UNESCAPED_UNICODE); 251 + $param['data'] = json_encode($data,JSON_UNESCAPED_UNICODE);
258 $this->model->add($param); 252 $this->model->add($param);
259 }else{ 253 }else{
260 - $data = json_encode($this->param['data'],JSON_UNESCAPED_UNICODE); 254 + $data = json_encode($data,JSON_UNESCAPED_UNICODE);
261 $this->model->edit(['data'=>$data],['language_id'=>$this->param['language_id'],'url'=>$this->param['url'],'type'=>$this->param['type']]); 255 $this->model->edit(['data'=>$data],['language_id'=>$this->param['language_id'],'url'=>$this->param['url'],'type'=>$this->param['type']]);
262 } 256 }
263 -// }catch (\Exception $e){  
264 -// $this->fail('系统错误请联系管理员');  
265 -// } 257 + }catch (\Exception $e){
  258 + $this->fail('系统错误请联系管理员');
  259 + }
  260 + $this->handleRoute($this->param['url']);
266 return $this->success(); 261 return $this->success();
267 } 262 }
  263 +
  264 + /**
  265 + * @remark :处理路由
  266 + * @name :handleRoute
  267 + * @author :lyh
  268 + * @method :post
  269 + * @time :2024/1/18 17:25
  270 + */
  271 + public function handleRoute($url){
  272 + $lang = $this->getLanguage($this->param['language_id'])['short'];
  273 + $str = trim($url,'/');
  274 + $page = 0;
  275 + $route = 'index';
  276 + if(!empty($str)){
  277 + $arr = explode('/',$str);
  278 + $num = count($arr);
  279 + if($num == 1){
  280 + $route = $arr[0];
  281 + }elseif ($num == 2){
  282 + if(ctype_digit($arr[1])){//是数字的情况
  283 + $page = $arr[1];
  284 + $route = $arr[0];
  285 + }else{
  286 + $route = $arr[1];
  287 + }
  288 + }elseif($num == 3){
  289 + if(ctype_digit($arr[2])){//是数字的情况
  290 + $page = $arr[2];
  291 + $route = $arr[0];
  292 + }else{
  293 + if($arr[2] == 'page'){
  294 + $route = $arr[1];
  295 + }else{
  296 + $route = $arr[0];
  297 + }
  298 + }
  299 + }elseif ($num == 4){
  300 + if(ctype_digit($arr[3])){//是数字的情况
  301 + $page = $arr[3];
  302 + $route = $arr[1];
  303 + }
  304 + }
  305 + }
  306 + if($page != 0){
  307 + $data['page'] = $page;
  308 + }
  309 + $data['new_route'] = $route;
  310 + $data['lang'] = $lang;
  311 + $data['project_id']= $this->user['project_id'];
  312 + $this->curlDelRoute($data);
  313 + }
268 } 314 }
@@ -170,6 +170,7 @@ class UserLoginLogic @@ -170,6 +170,7 @@ class UserLoginLogic
170 $info['upload_config'] = $project['upload_config']; 170 $info['upload_config'] = $project['upload_config'];
171 $info['main_lang_id'] = $project['main_lang_id']; 171 $info['main_lang_id'] = $project['main_lang_id'];
172 $info['image_max'] = $project['image_max']; 172 $info['image_max'] = $project['image_max'];
  173 + $info['uptime'] = $project['uptime'];
173 $info['is_update_language'] = $project['is_update_language']; 174 $info['is_update_language'] = $project['is_update_language'];
174 $info['configuration'] = $project['deploy_build']['configuration']; 175 $info['configuration'] = $project['deploy_build']['configuration'];
175 $info['project_type'] = $project['type']; 176 $info['project_type'] = $project['type'];
@@ -208,6 +209,7 @@ class UserLoginLogic @@ -208,6 +209,7 @@ class UserLoginLogic
208 $info['upload_config'] = $project['upload_config']; 209 $info['upload_config'] = $project['upload_config'];
209 $info['main_lang_id'] = $project['main_lang_id']; 210 $info['main_lang_id'] = $project['main_lang_id'];
210 $info['image_max'] = $project['image_max']; 211 $info['image_max'] = $project['image_max'];
  212 + $info['uptime'] = $project['uptime'];
211 $info['is_update_language'] = $project['is_update_language']; 213 $info['is_update_language'] = $project['is_update_language'];
212 $info['configuration'] = $project['deploy_build']['configuration']; 214 $info['configuration'] = $project['deploy_build']['configuration'];
213 $info['project_type'] = $project['type']; 215 $info['project_type'] = $project['type'];
@@ -44,12 +44,6 @@ class InquiryForm extends Base @@ -44,12 +44,6 @@ class InquiryForm extends Base
44 return $map; 44 return $map;
45 } 45 }
46 46
47 -  
48 - public function getFieldAttribute($value)  
49 - {  
50 - return json_decode($value, true);  
51 - }  
52 -  
53 /** 47 /**
54 * @author zbj 48 * @author zbj
55 * @date 2023/12/5 49 * @date 2023/12/5
@@ -63,4 +57,51 @@ class InquiryForm extends Base @@ -63,4 +57,51 @@ class InquiryForm extends Base
63 } 57 }
64 return $field; 58 return $field;
65 } 59 }
  60 +
  61 + /**
  62 + * 根据提交数据的key获取表单id
  63 + * @param $data
  64 + * @return mixed
  65 + * @author zbj
  66 + * @date 2023/12/4
  67 + */
  68 + public static function getFromId($data){
  69 + unset($data['globalso-domain_host_url']);
  70 + unset($data['globalso-domain']);
  71 + unset($data['globalso-edition']);
  72 + unset($data['globalso-date']);
  73 + ksort($data);
  74 + $field = array_keys($data);
  75 + $sign = md5(json_encode($field));
  76 + $model = self::where('sign', $sign)->first();
  77 + if(!$model){
  78 + $model = new self();
  79 + $model->sign = $sign;
  80 + $model->field = $field;
  81 +
  82 + if(!empty($data['name']) && !empty($data['email']) && !empty($data['message'])){
  83 + $has_file = false;
  84 + foreach ($data as $v){
  85 + if (is_array($v)){
  86 + $has_file = true;
  87 + break;
  88 + }
  89 + }
  90 + !$has_file && $model->is_default = 1;
  91 + }
  92 +
  93 + $model->save();
  94 + }
  95 + return $model->id;
  96 + }
  97 +
  98 + public function setFieldAttribute($value)
  99 + {
  100 + $this->attributes['field'] = json_encode($value);
  101 + }
  102 +
  103 + public function getFieldAttribute($value)
  104 + {
  105 + return json_decode($value, true);
  106 + }
66 } 107 }
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 2
3 namespace App\Models\Inquiry; 3 namespace App\Models\Inquiry;
4 4
  5 +use App\Helper\FormGlobalsoApi;
5 use App\Models\Base; 6 use App\Models\Base;
6 use Illuminate\Database\Eloquent\SoftDeletes; 7 use Illuminate\Database\Eloquent\SoftDeletes;
7 use Illuminate\Support\Facades\DB; 8 use Illuminate\Support\Facades\DB;
@@ -23,6 +24,71 @@ class InquiryFormData extends Base @@ -23,6 +24,71 @@ class InquiryFormData extends Base
23 protected $connection = "custom_mysql"; 24 protected $connection = "custom_mysql";
24 protected $table = 'gl_inquiry_form_data'; 25 protected $table = 'gl_inquiry_form_data';
25 26
  27 +
  28 + /**
  29 + * 根据提交数据的key获取表单id
  30 + * @param $form_id
  31 + * @param $domain
  32 + * @param $ip
  33 + * @param $country
  34 + * @param $referer
  35 + * @param $user_agent
  36 + * @param $submit_at
  37 + * @param $data
  38 + * @return mixed
  39 + * @author zbj
  40 + * @date 2023/12/4
  41 + */
  42 + public static function saveData($form_id, $domain, $ip, $country, $referer, $user_agent, $submit_at, $data){
  43 + //数据标识
  44 + ksort($data);
  45 + $sign = md5(json_encode($data));
  46 + //5分钟内是否有重复数据
  47 + $is_exist = self::where('sign', $sign)->where('created_at', '>', date('Y-m-d H:i:s', strtotime('-5 minute')))->first();
  48 + if($is_exist){
  49 + return true;
  50 + }
  51 +
  52 + $model = new self();
  53 + $model->form_id = $form_id;
  54 + $model->domain = $domain;
  55 + $model->ip = $ip;
  56 + $model->country = $country;
  57 + $model->referer = $referer;
  58 + $model->user_agent = $user_agent;
  59 + $model->submit_at = $submit_at;
  60 + $model->data = $data;
  61 + $model->sign = $sign;
  62 + $model->save();
  63 +
  64 + if(!empty($data['name']) && !empty($data['email']) && !empty($data['message'])){
  65 + unset($data['globalso-domain_host_url']);
  66 + unset($data['globalso-domain']);
  67 + unset($data['globalso-edition']);
  68 + unset($data['globalso-date']);
  69 +
  70 + //推送邮件发送
  71 + $has_file = false;
  72 + foreach ($data as $k => $v){
  73 + if(is_array($v)){
  74 + $has_file = true;
  75 + break;
  76 + }
  77 + //其他字段补充到message里
  78 + if(!in_array($k, ['name', 'email', 'message', 'phone', 'ip', 'date', 'cname', 'domain', 'edition', 'domain_host_url'])){
  79 + $data['message'].= "<br/>" . $k .': ' . $v;
  80 + }
  81 + }
  82 + !$has_file && (new FormGlobalsoApi())->submitInquiry($ip, $referer, $submit_at, $data);
  83 + }
  84 + return true;
  85 + }
  86 +
  87 + public function setDataAttribute($value)
  88 + {
  89 + $this->attributes['data'] = json_encode($value);
  90 + }
  91 +
26 public function getDataAttribute($value) 92 public function getDataAttribute($value)
27 { 93 {
28 return json_decode($value, true); 94 return json_decode($value, true);
  1 +<?php
  2 +
  3 +namespace App\Models\Project;
  4 +
  5 +use App\Models\Base;
  6 +use Illuminate\Support\Facades\Cache;
  7 +
  8 +/**
  9 + * 询盘过滤配置
  10 + * Class InquiryFilterConfig
  11 + * @package App\Models\Project
  12 + * @author zbj
  13 + * @date 2024/1/19
  14 + */
  15 +class InquiryFilterConfig extends Base
  16 +{
  17 + //设置关联表名
  18 + protected $table = 'gl_project_inquiry_filter_config';
  19 +
  20 + protected $casts = [
  21 + 'filter_countries' => 'array',
  22 + 'filter_contents' => 'array',
  23 + 'filter_referers' => 'array',
  24 + 'filter_emails' => 'array',
  25 + 'filter_mobiles' => 'array',
  26 + 'filter_names' => 'array',
  27 + ];
  28 +
  29 + /**
  30 + * @param $project_id
  31 + * @return string
  32 + * @author zbj
  33 + * @date 2024/1/20
  34 + */
  35 + public static function cacheKey($project_id): string
  36 + {
  37 + return 'project_inquiry_filter_config_info' . $project_id;
  38 + }
  39 +
  40 + /**
  41 + * @param $project_id
  42 + * @return mixed
  43 + * @author zbj
  44 + * @date 2024/1/20
  45 + */
  46 + public static function getCacheInfoByProjectId($project_id){
  47 + $info = Cache::get(self::cacheKey($project_id));
  48 + if (!$info) {
  49 + $info = self::where('project_id', $project_id)->first();
  50 + Cache::put(self::cacheKey($project_id), $info, 2 * 3600);
  51 + }
  52 + return $info;
  53 + }
  54 +}
@@ -207,6 +207,18 @@ class Project extends Base @@ -207,6 +207,18 @@ class Project extends Base
207 return self::hasOne(After::class, 'project_id', 'id'); 207 return self::hasOne(After::class, 'project_id', 'id');
208 } 208 }
209 209
  210 +
  211 + /**
  212 + * 询盘过滤配置
  213 + * @return \Illuminate\Database\Eloquent\Relations\HasOne
  214 + * @author zbj
  215 + * @date 2024/1/19
  216 + */
  217 + public function inquiry_filter_config()
  218 + {
  219 + return self::hasOne(InquiryFilterConfig::class, 'project_id', 'id');
  220 + }
  221 +
210 /** 222 /**
211 * 域名 223 * 域名
212 * @return \Illuminate\Database\Eloquent\Relations\HasOne 224 * @return \Illuminate\Database\Eloquent\Relations\HasOne
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :RatingQuestion.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2024/1/20 11:14
  8 + */
  9 +
  10 +namespace App\Models\Scoring;
  11 +
  12 +use App\Models\Base;
  13 +
  14 +/**
  15 + * @remark :问题
  16 + * @name :RatingQuestion
  17 + * @author :lyh
  18 + * @method :post
  19 + * @time :2024/1/20 11:15
  20 + */
  21 +class RatingQuestion extends Base
  22 +{
  23 + protected $table = 'gl_rating_questions';
  24 +}
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :ScoringSystem.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2024/1/20 11:17
  8 + */
  9 +
  10 +namespace App\Models\Scoring;
  11 +
  12 +use App\Models\Base;
  13 +
  14 +class ScoringSystem extends Base
  15 +{
  16 + protected $table = 'gl_scoring_system';
  17 +}
@@ -16,6 +16,7 @@ class SmsLog extends Base @@ -16,6 +16,7 @@ class SmsLog extends Base
16 const TYPE_REGISTER = 'register'; 16 const TYPE_REGISTER = 'register';
17 const TYPE_LOGIN = 'login'; 17 const TYPE_LOGIN = 'login';
18 18
  19 + const TYPE_CORING = 'coring';//评分系统
19 const TYPE_MANAGER_LOGIN = 'manager_login';//管理员登录 20 const TYPE_MANAGER_LOGIN = 'manager_login';//管理员登录
20 const TYPE_NOTICE = 'notice'; 21 const TYPE_NOTICE = 'notice';
21 22
  1 +<?php
  2 +
  3 +namespace App\Models\SyncSubmitTask;
  4 +
  5 +use Illuminate\Database\Eloquent\Model;
  6 +use Illuminate\Support\Facades\Log;
  7 +use Illuminate\Support\Facades\Redis;
  8 +
  9 +/**
  10 + * @method static where(string $string, mixed $ip)
  11 + * @method static create(array $data)
  12 + */
  13 +class SyncSubmitTask extends Model
  14 +{
  15 +
  16 + const TYPE_INQUIRY = 'inquiry';
  17 + const TYPE_VISIT = 'visit';
  18 +
  19 + const TRAFFIC_DEFAULT = 0;
  20 + const TRAFFIC_TRUE = 1;
  21 +
  22 + //设置关联表名
  23 + /**
  24 + * @var mixed
  25 + */
  26 + protected $table = 'gl_sync_submit_task';
  27 +
  28 + protected $casts = [
  29 + 'data' => 'array',
  30 + ];
  31 +}
@@ -4,6 +4,8 @@ namespace App\Models\Visit; @@ -4,6 +4,8 @@ namespace App\Models\Visit;
4 4
5 5
6 use App\Models\Base; 6 use App\Models\Base;
  7 +use Carbon\Carbon;
  8 +use Illuminate\Support\Facades\DB;
7 9
8 /** 10 /**
9 * Class Visit 11 * Class Visit
@@ -19,6 +21,7 @@ class Visit extends Base @@ -19,6 +21,7 @@ class Visit extends Base
19 //连接数据库 21 //连接数据库
20 protected $connection = 'custom_mysql'; 22 protected $connection = 'custom_mysql';
21 protected $appends = ['device_text']; 23 protected $appends = ['device_text'];
  24 + protected $fillable = ['ip','device_port','country','city','url','referrer_url','depth','domain','updated_date', 'created_at'];
22 25
23 const DEVICE_PC = 1; 26 const DEVICE_PC = 1;
24 const DEVICE_MOBILE = 2; 27 const DEVICE_MOBILE = 2;
@@ -47,4 +50,52 @@ class Visit extends Base @@ -47,4 +50,52 @@ class Visit extends Base
47 return self::deviceMap()[$this->device_port] ?? ''; 50 return self::deviceMap()[$this->device_port] ?? '';
48 } 51 }
49 52
  53 + /**
  54 + * 访问写入
  55 + */
  56 + public static function saveData($data)
  57 + {
  58 + //判断IP当天是否有一条数据
  59 + $visit = Visit::where("ip",$data['ip'])->where("created_at",">=",Carbon::now()->today()->startOfDay())
  60 + ->where("created_at","<=",Carbon::now()->today()->endOfDay())
  61 + ->first();
  62 + DB::connection('custom_mysql')->beginTransaction();
  63 + if (!empty($visit) && $visit->count() >= 1){
  64 + //当天已存在IP访问记录
  65 + try{
  66 + $data["customer_visit_id"] = $visit->id;
  67 + VisitItem::create($data);
  68 + Visit::where("id",$visit->id)->update(['depth' => $visit->depth + 1]);
  69 + DB::connection('custom_mysql')->commit();
  70 + }catch (\Exception $e){
  71 + DB::connection('custom_mysql')->rollBack();
  72 + throw new \Exception($e->getMessage());
  73 + }
  74 + }else{
  75 + //第一次访问
  76 + try{
  77 + $id = Visit::create($data)->id;
  78 + $data["customer_visit_id"] = $id;
  79 + VisitItem::create($data);
  80 + DB::connection('custom_mysql')->commit();
  81 + }catch (\Exception $e){
  82 + DB::connection('custom_mysql')->rollBack();
  83 + throw new \Exception($e->getMessage());
  84 + }
  85 + }
  86 + return true;
  87 + }
  88 +
  89 +
  90 + public static function isInquiry($ip){
  91 + $visit = Visit::where("ip",$ip)->where("created_at",">=",Carbon::now()->today()->startOfDay())
  92 + ->where("created_at","<=",Carbon::now()->today()->endOfDay())
  93 + ->first();
  94 + if($visit){
  95 + $visit->is_inquiry = 1;
  96 + $visit->save();
  97 + }
  98 +
  99 + return true;
  100 + }
50 } 101 }
@@ -16,4 +16,7 @@ class VisitItem extends Base @@ -16,4 +16,7 @@ class VisitItem extends Base
16 protected $table = 'gl_customer_visit_item'; 16 protected $table = 'gl_customer_visit_item';
17 //连接数据库 17 //连接数据库
18 protected $connection = 'custom_mysql'; 18 protected $connection = 'custom_mysql';
  19 +
  20 + protected $fillable = ['ip','customer_visit_id','device_port','country','city','url','referrer_url','domain','updated_date','created_at'];
  21 +
19 } 22 }
  1 +<?php
  2 +
  3 +
  4 +namespace App\Services;
  5 +
  6 +use App\Exceptions\InquiryFilterException;
  7 +use App\Models\Inquiry\InquiryForm;
  8 +use App\Models\Inquiry\InquiryFormData;
  9 +use App\Models\Project\InquiryFilterConfig;
  10 +use App\Models\Project\Project;
  11 +use App\Models\SyncSubmitTask\SyncSubmitTask;
  12 +use App\Models\Visit\Visit;
  13 +use Illuminate\Support\Facades\Http;
  14 +use Illuminate\Support\Facades\URL;
  15 +use Illuminate\Support\Str;
  16 +use function Symfony\Component\String\s;
  17 +
  18 +
  19 +/**
  20 + * Class SyncSubmitTaskService
  21 + * @package App\Services
  22 + * @author zbj
  23 + * @date 2023/12/4
  24 + */
  25 +class SyncSubmitTaskService
  26 +{
  27 + /**
  28 + * @param $task
  29 + * @return mixed
  30 + * @throws InquiryFilterException
  31 + * @author zbj
  32 + * @date 2023/11/28
  33 + */
  34 + public static function handler($task)
  35 + {
  36 + $data = $task['data'];
  37 + $checkIpCountry = self::checkIpCountry($data['domain'], $data['ip'], $task['type']);
  38 +
  39 + $data['ip'] = $checkIpCountry['ip'];
  40 + $data['country'] = $checkIpCountry['country'];
  41 + $data['submit_at'] = $task['created_at'];
  42 + $project = $checkIpCountry['project'];
  43 + $data['project_id'] = $project['id'];
  44 +
  45 + //特殊处理
  46 + if($project['id'] == 455 && !empty($data['email']) && $data['email'] == 'alb@marketingtu.org'){
  47 + return false;
  48 + }
  49 +
  50 + if(!ProjectServer::useProject($project['id'])){
  51 + return false;
  52 + }
  53 +
  54 + $action = $task['type'];
  55 + $handler = new self();
  56 + return $handler->$action($data);
  57 + }
  58 +
  59 +
  60 + /**
  61 + * 询盘
  62 + * @param $data
  63 + * @return bool
  64 + * @throws InquiryFilterException
  65 + * @author zbj
  66 + * @date 2023/12/4
  67 + */
  68 + public function inquiry($data)
  69 + {
  70 +
  71 + $this->inquiryFilter($data['project_id'], $data);
  72 +
  73 + $form_id = InquiryForm::getFromId($data['data']);
  74 +
  75 + InquiryFormData::saveData($form_id, $data['domain'], $data['ip'], $data['country'], $data['referer'], $data['user_agent'], $data['submit_at'], $data['data']);
  76 +
  77 + //转化询盘
  78 + Visit::isInquiry($data['ip']);
  79 +
  80 + return true;
  81 + }
  82 +
  83 + /**
  84 + * 访问
  85 + * @param $data
  86 + * @return bool
  87 + * @throws \Exception
  88 + * @author zbj
  89 + * @date 2023/12/4
  90 + */
  91 + public function visit($data)
  92 + {
  93 +
  94 + $visit_data = $data['data'];
  95 + $visit_data['referrer_url'] = $data['data']['referrer_url']??'';
  96 + $visit_data['device_port'] = $data['data']['device_port']??'';
  97 + $visit_data['url'] = $data['data']['url']??'';
  98 + $visit_data['domain'] = $data['domain']??'';
  99 + $visit_data['ip'] = $data['ip'];
  100 + $visit_data['country'] = $data['country'];
  101 + $visit_data['updated_date'] = $data['submit_at']->toDateString();
  102 + $visit_data['created_at'] = $data['submit_at'];
  103 + Visit::saveData($visit_data);
  104 +
  105 + return true;
  106 + }
  107 +
  108 + /**
  109 + * 根据ip查地区
  110 + * @param $ip
  111 + * @return string
  112 + * @author zbj
  113 + * @date 2023/11/28
  114 + */
  115 + public static function getCountryByIp($ip){
  116 + if(!$ip){
  117 + return '';
  118 + }
  119 + $res = Http::withoutVerifying()->get('http://ip.globalso.com', ['ip' => $ip]);
  120 + if($res->status() == 200){
  121 + return $res->body();
  122 + }else{
  123 + return '';
  124 + }
  125 + }
  126 +
  127 + /**
  128 + * 是否开始测试站或中国内地询盘和访问记录
  129 + * @param $domain
  130 + * @param $ip
  131 + * @param $type
  132 + * @return array
  133 + * @throws InquiryFilterException
  134 + * @author zbj
  135 + * @date 2023/11/30
  136 + */
  137 + public static function checkIpCountry($domain, $ip, $type){
  138 + $project = Project::getProjectByDomain($domain);
  139 + if(empty($project)){
  140 + throw new InquiryFilterException('项目不存在');
  141 + }
  142 +
  143 + // 测试环境返回信息
  144 + if (FALSE !== strpos($domain, 'globalso.site') && !$project->is_record_test_visit) {
  145 + throw new InquiryFilterException('测试环境过滤');
  146 + }
  147 +
  148 + if($ip == "127.0.0.1"){
  149 + throw new InquiryFilterException('127.0.0.1过滤');
  150 + }
  151 + $country = self::getCountryByIp($ip);
  152 + //访问记录才过滤是否国内
  153 + if ($country == "中国" && !$project->is_record_china_visit && $type == SyncSubmitTask::TYPE_VISIT){
  154 + throw new InquiryFilterException('中国内地过滤');
  155 + }
  156 + return [
  157 + 'project' => $project,
  158 + 'ip' => $ip,
  159 + 'country' => $country
  160 + ];
  161 + }
  162 +
  163 +
  164 + /**
  165 + * 询盘配置过滤
  166 + * @author zbj
  167 + * @date 2024/1/20
  168 + */
  169 + public static function inquiryFilter($project_id, $data){
  170 + $config = InquiryFilterConfig::getCacheInfoByProjectId($project_id);
  171 + //是否开启过滤
  172 + if($config && $config['status']){
  173 + //是否包含全局规则(就是project_id=1的配置)
  174 + if($project_id != Project::DEMO_PROJECT_ID && $config['is_global_rule']){
  175 + self::inquiryFilter(Project::DEMO_PROJECT_ID, $data);
  176 + }
  177 + //过滤国家
  178 + if($config['filter_countries'] && in_array($data['country'], $config['filter_countries'])){
  179 + throw new InquiryFilterException( '过滤国家:' . $data['country']);
  180 + }
  181 + //过滤ip
  182 + if($config['black_ips']){
  183 + $black_ips = explode("\r\n", $config['black_ips']);
  184 + //后端获取的ip
  185 + if(in_array($data['ip'], $black_ips)){
  186 + throw new InquiryFilterException( '过滤黑名单IP:' . $data['ip']);
  187 + }
  188 + //前端获取的ip
  189 + if(!empty($data['data']['ip']) && in_array($data['data']['ip'], $black_ips)){
  190 + throw new InquiryFilterException( '过滤黑名单IP:' . $data['data']['ip']);
  191 + }
  192 + }
  193 + //过滤内容
  194 + if(!empty($data['data']['message'])) {
  195 + //过滤内容关键字
  196 + if ($config['filter_contents']){
  197 + foreach ($config['filter_contents'] as $filter_content) {
  198 + if (Str::contains($data['data']['message'], $filter_content)) {
  199 + throw new InquiryFilterException('过滤内容:' . $filter_content);
  200 + }
  201 + }
  202 + }
  203 + //是否允许包含链接
  204 + if(!$config['is_allow_link']){
  205 + if (Str::contains($data['data']['message'], ['http://', 'https://', 'www.'])) {
  206 + throw new InquiryFilterException('不允许包含链接');
  207 + }
  208 + }
  209 + }
  210 + //过滤来源
  211 + if($config['filter_referers']){
  212 + //只比较path路径
  213 + $paths = array_map(function ($v){
  214 + return trim(parse_url(Url::to($v), PHP_URL_PATH), '/');
  215 + },$config['filter_referers']);
  216 +
  217 + //后端获取的referer
  218 + if(in_array(trim(parse_url($data['referer'], PHP_URL_PATH), '/'), $paths)){
  219 + throw new InquiryFilterException( '过滤来源链接:' . $data['referer']);
  220 + }
  221 + //前端获取的referer
  222 + if(!empty($data['data']['globalso-domain_host_url']) && in_array(parse_url($data['data']['globalso-domain_host_url'], PHP_URL_PATH), $paths)){
  223 + throw new InquiryFilterException( '过滤来源链接:' . $data['data']['globalso-domain_host_url']);
  224 + }
  225 + }
  226 + //过滤邮箱
  227 + if($config['filter_emails'] && !empty($data['data']['email'])){
  228 + foreach ($config['filter_emails'] as $filter_email){
  229 + if($data['data']['email'] == $filter_email){
  230 + throw new InquiryFilterException( '过滤邮箱:' . $filter_email);
  231 + }
  232 + }
  233 + }
  234 + //过滤电话
  235 + if($config['filter_mobiles'] && !empty($data['data']['phone'])){
  236 + foreach ($config['filter_mobiles'] as $filter_mobile){
  237 + if($data['data']['phone'] == $filter_mobile){
  238 + throw new InquiryFilterException( '过滤电话:' . $filter_mobile);
  239 + }
  240 + }
  241 + }
  242 + //过滤姓名
  243 + if($config['filter_names'] && !empty($data['data']['name'])){
  244 + foreach ($config['filter_names'] as $filter_name){
  245 + if($data['data']['name'] == $filter_name){
  246 + throw new InquiryFilterException( '过滤姓名:' . $filter_name);
  247 + }
  248 + }
  249 + }
  250 + }
  251 + return true;
  252 + }
  253 +
  254 +}
@@ -38,7 +38,8 @@ @@ -38,7 +38,8 @@
38 "Database\\Seeders\\": "database/seeders/" 38 "Database\\Seeders\\": "database/seeders/"
39 }, 39 },
40 "files": [ 40 "files": [
41 - "app/Helper/helper.php" 41 + "app/Helper/helper.php",
  42 + "app/Helper/simple_html_dom.php"
42 ] 43 ]
43 }, 44 },
44 "autoload-dev": { 45 "autoload-dev": {
@@ -182,6 +182,8 @@ Route::middleware(['aloginauth'])->group(function () { @@ -182,6 +182,8 @@ Route::middleware(['aloginauth'])->group(function () {
182 Route::any('/getOtherProject', [Aside\Project\ProjectController::class, 'getOtherProject'])->name('admin.project_getOtherProject');//获取其他项目设置 182 Route::any('/getOtherProject', [Aside\Project\ProjectController::class, 'getOtherProject'])->name('admin.project_getOtherProject');//获取其他项目设置
183 Route::any('/getChannel', [Aside\Project\ProjectController::class, 'getChannel'])->name('admin.project_getChannel');//其他项目设置 183 Route::any('/getChannel', [Aside\Project\ProjectController::class, 'getChannel'])->name('admin.project_getChannel');//其他项目设置
184 Route::any('/languageLists', [Aside\Project\ProjectController::class, 'languageLists'])->name('admin.project_languageLists');//其他项目设置 184 Route::any('/languageLists', [Aside\Project\ProjectController::class, 'languageLists'])->name('admin.project_languageLists');//其他项目设置
  185 + Route::any('/countryLists', [Aside\Project\ProjectController::class, 'countryLists'])->name('admin.project_countryLists');//国家地区列表
  186 + Route::any('/saveInquiryFilterConfig', [Aside\Project\ProjectController::class, 'saveInquiryFilterConfig'])->name('admin.project_inquiry_filter_config_save');//保存询盘过滤配置
185 //获取关键词前缀和后缀 187 //获取关键词前缀和后缀
186 Route::prefix('keyword')->group(function () { 188 Route::prefix('keyword')->group(function () {
187 Route::any('/getKeywordPrefix', [Aside\Project\KeywordPrefixController::class, 'getKeywordPrefix'])->name('admin.keyword_getKeywordPrefix'); 189 Route::any('/getKeywordPrefix', [Aside\Project\KeywordPrefixController::class, 'getKeywordPrefix'])->name('admin.keyword_getKeywordPrefix');