123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670 |
- <?php
- /**
- * 助手函数
- *
- * @author mybsdc <[email protected]>
- * @date 2019/3/3
- * @time 16:34
- */
- use GuzzleHttp\Client;
- use GuzzleHttp\Cookie\CookieJar;
- use GuzzleHttp\Cookie\SetCookie;
- use Luolongfei\App\Console\GlobalValue;
- use Luolongfei\App\Console\MigrateEnvFile;
- use Luolongfei\App\Console\Upgrade;
- use Luolongfei\App\Constants\CommonConst;
- use Luolongfei\App\Exceptions\LlfException;
- use Luolongfei\Libs\Argv;
- use Luolongfei\Libs\Config;
- use Luolongfei\Libs\Env;
- use Luolongfei\Libs\Lang;
- use Luolongfei\Libs\Log;
- use Luolongfei\Libs\PhpColor;
- if (!function_exists('config')) {
- /**
- * 获取配置
- *
- * @param string $key 键,支持点式访问
- * @param string $default 默认值
- *
- * @return array|mixed
- */
- function config($key = '', $default = null)
- {
- return Config::getInstance()->get($key, $default);
- }
- }
- if (!function_exists('lang')) {
- /**
- * 读取语言包
- *
- * @param string $key 键,支持点式访问
- *
- * @return array|mixed|null
- */
- function lang($key = '')
- {
- return Lang::getInstance()->get($key);
- }
- }
- if (!function_exists('system_log')) {
- /**
- * 写日志
- *
- * @param $content
- * @param array $response
- * @param string $fileName
- * @description 受支持的着色标签
- * 'reset', 'bold', 'dark', 'italic', 'underline', 'blink', 'reverse', 'concealed', 'default', 'black', 'red',
- * 'green', 'yellow', 'blue', 'magenta', 'cyan', 'light_gray', 'dark_gray', 'light_red', 'light_green',
- * 'light_yellow', 'light_blue', 'light_magenta', 'light_cyan', 'white', 'bg_default', 'bg_black', 'bg_red',
- * 'bg_green', 'bg_yellow', 'bg_blue', 'bg_magenta', 'bg_cyan', 'bg_light_gray', 'bg_dark_gray', 'bg_light_red',
- * 'bg_light_green','bg_light_yellow', 'bg_light_blue', 'bg_light_magenta', 'bg_light_cyan', 'bg_white'
- *
- * system_log('<light_magenta>颜色 light_magenta</light_magenta>');
- */
- function system_log($content, array $response = [], $fileName = '')
- {
- try {
- $msg = sprintf(
- "[%s] %s %s\n",
- date('Y-m-d H:i:s'),
- is_string($content) ? $content : json_encode($content),
- $response ? json_encode($response, JSON_UNESCAPED_UNICODE) : '');
- // 过滤敏感信息
- if ((int)env('MOSAIC_SENSITIVE_INFO') === 1) {
- // 在 php 7.3 之前,连字符“-”在中括号中随便放,但在之后,只能放在开头或结尾或者转义后才能随便放
- $msg = preg_replace_callback('/(?P<secret>[\w.-]{1,3}?)(?=@[\w.-]+)/ui', function ($m) {
- return str_ireplace($m['secret'], str_repeat('*', strlen($m['secret'])), $m['secret']);
- }, $msg);
- }
- // 尝试为消息着色
- $c = PhpColor::getInstance()->getColorInstance();
- echo $c($msg)->colorize();
- // 干掉着色标签
- $msg = strip_tags($msg); // 不完整或者破损标签将导致更多的数据被删除
- // 写入日志文件
- if (is_writable(ROOT_PATH)) {
- $path = sprintf('%s/logs/%s/', ROOT_PATH, date('Y-m'));
- $file = $path . ($fileName ?: date('d')) . '.log';
- if (!is_dir($path)) {
- mkdir($path, 0766, true); // 0766 赋予所有用户读写
- chmod($path, 0766); // 显式 chmod 确保权限设置正确
- }
- $handle = fopen($file, 'a'); // 追加而非覆盖
- if ($handle !== false) {
- if (!filesize($file)) {
- chmod($file, 0666);
- }
- fwrite($handle, $msg);
- fclose($handle);
- }
- }
- flush();
- } catch (\Exception $e) {
- // do nothing
- }
- }
- }
- if (!function_exists('is_locked')) {
- /**
- * 检查任务是否已被锁定
- *
- * @param string $taskName
- * @param bool $always 是否被永久锁定
- *
- * @return bool
- * @throws Exception
- */
- function is_locked($taskName = '', $always = false)
- {
- try {
- $lock = sprintf(
- '%s/num_limit/%s/%s.lock',
- APP_PATH,
- $always ? 'always' : date('Y-m-d'),
- $taskName
- );
- return file_exists($lock);
- } catch (\Exception $e) {
- system_log(sprintf('检查任务%s是否锁定时出错,错误原因:%s', $taskName, $e->getMessage()));
- }
- return false;
- }
- }
- if (!function_exists('lock_task')) {
- /**
- * 锁定任务
- *
- * 防止重复执行
- *
- * @param string $taskName
- * @param bool $always 是否永久锁定
- *
- * @return bool
- */
- function lock_task($taskName = '', $always = false)
- {
- try {
- $lock = sprintf(
- '%s/num_limit/%s/%s.lock',
- APP_PATH,
- $always ? 'always' : date('Y-m-d'),
- $taskName
- );
- $path = dirname($lock);
- if (!is_dir($path)) {
- mkdir($path, 0777, true);
- chmod($path, 0777);
- }
- if (file_exists($lock)) {
- return true;
- }
- $handle = fopen($lock, 'a'); // 追加而非覆盖
- if (!filesize($lock)) {
- chmod($lock, 0666);
- }
- fwrite($handle, sprintf(
- "Locked at %s.\n",
- date('Y-m-d H:i:s')
- )
- );
- fclose($handle);
- Log::info(sprintf('%s已被锁定,此任务%s已不会再执行,请知悉', $taskName, $always ? '' : '今天内'));
- } catch (\Exception $e) {
- system_log(sprintf('创建锁定任务文件%s时出错,错误原因:%s', $lock, $e->getMessage()));
- return false;
- }
- return true;
- }
- }
- if (!function_exists('env')) {
- /**
- * 获取环境变量值
- *
- * @param string $key
- * @param string $default 默认值
- *
- * @return array | bool | false | null | string
- */
- function env($key = '', $default = null)
- {
- return Env::getInstance()->get($key, $default);
- }
- }
- if (!function_exists('get_argv')) {
- /**
- * 获取命令行传参
- *
- * @param string $name
- * @param string $default 默认值
- *
- * @return mixed|string
- */
- function get_argv(string $name, string $default = '')
- {
- return Argv::getInstance()->get($name, $default);
- }
- }
- if (!function_exists('system_check')) {
- /**
- * 检查环境是否满足要求
- *
- * @throws LlfException
- */
- function system_check()
- {
- // 由于各种云函数目前支持的最大的 PHP 版本为 7.2,故此处暂时不强制要求升级 PHP 7.3 以获得更好的兼容性
- if (version_compare(PHP_VERSION, '7.2.0') < 0) {
- throw new LlfException(34520006, ['7.3', PHP_VERSION]);
- }
- // 特殊环境无需检查这几项
- if (IS_SCF || !is_writable(ROOT_PATH) || (int)env('IS_KOYEB') === 1 || (int)env('IS_HEROKU') === 1) {
- system_log(lang('100009'));
- } else {
- if (!function_exists('putenv')) {
- throw new LlfException(34520005);
- }
- $envFile = ROOT_PATH . '/.env';
- if (!file_exists($envFile)) {
- throw new LlfException(copy(ROOT_PATH . '/.env.example', $envFile) ? 34520007 : 34520008);
- }
- // 检查当前 .env 文件版本是否过低,过低自动升级
- MigrateEnvFile::getInstance()->handle();
- }
- // 是否有新版可用
- if (config('new_version_detection')) {
- Upgrade::getInstance()->handle();
- } else {
- system_log(lang('100012'));
- }
- if (!extension_loaded('curl')) {
- throw new LlfException(34520010);
- }
- }
- }
- if (!function_exists('get_local_num')) {
- /**
- * 获取当地数字
- *
- * @param string|int $num
- *
- * @return string
- */
- function get_local_num($num)
- {
- $num = (string)$num;
- if (is_chinese()) {
- return $num;
- }
- // 英文数字规则
- $lastDigit = substr($num, -1);
- switch ($lastDigit) {
- case '1':
- return $num . 'st';
- case '2':
- return $num . 'nd';
- case '3':
- return $num . 'rd';
- default:
- return $num . 'th';
- }
- }
- }
- if (!function_exists('is_chinese')) {
- /**
- * 判断当前语言环境
- *
- * @return bool
- */
- function is_chinese()
- {
- return config('custom_language', 'zh') === 'zh';
- }
- }
- if (!function_exists('get_ip_info')) {
- /**
- * 获取 ip 信息
- *
- * @return string
- */
- function get_ip_info()
- {
- return \Luolongfei\Libs\IP::getInstance()->get();
- }
- }
- if (!function_exists('get_random_user_agent')) {
- /**
- * 获取随机 user-agent
- *
- * @return string
- */
- function get_random_user_agent()
- {
- $chromeVersions = [
- '121.0.0.0',
- ];
- return $chromeVersions[array_rand($chromeVersions)];
- }
- }
- if (!function_exists('autoRetry')) {
- /**
- * 自动重试
- *
- * @param $func
- * @param int $maxRetryCount
- * @param array $params
- *
- * @return mixed|void
- * @throws Exception
- */
- function autoRetry($func, $maxRetryCount = 3, $params = [])
- {
- $retryCount = 0;
- while (true) {
- try {
- return call_user_func_array($func, $params);
- } catch (\Exception $e) {
- $retryCount++;
- if ($retryCount > $maxRetryCount) {
- throw $e;
- }
- $sleepTime = getSleepTime($retryCount, 2, 10);
- if (stripos($e->getMessage(), '405') !== false) {
- system_log(\lang('100141'));
- sleep(9);
- // aws waf token 失效,将重新获取新的 token
- $handleInvalidToken = false;
- foreach ($params as &$param) {
- if ($param instanceof CookieJar) {
- $handleInvalidToken = true;
- $sleepTime = 1;
- delGlobalValue(CommonConst::AWS_WAF_TOKEN);
- $param->setCookie(buildAwsWafCookie(getAwsWafToken()));
- break;
- }
- }
- system_log($handleInvalidToken ? \lang('exception_msg.34520019') : sprintf(lang('exception_msg.34520015'), $sleepTime, $maxRetryCount, $retryCount, $maxRetryCount));
- continue;
- } else {
- system_log(sprintf(lang('exception_msg.34520016'), $e->getMessage(), $sleepTime, $maxRetryCount, $retryCount, $maxRetryCount));
- }
- sleep($sleepTime);
- }
- }
- }
- }
- if (!function_exists('buildAwsWafCookie')) {
- /**
- * 构建 aws waf cookie
- *
- * @param string $awsWafToken
- *
- * @return SetCookie
- */
- function buildAwsWafCookie(string $awsWafToken)
- {
- $cookie = new SetCookie();
- $cookie->setName('aws-waf-token');
- $cookie->setValue($awsWafToken);
- $cookie->setDomain('.my.freenom.com');
- return $cookie;
- }
- }
- if (!function_exists('getSleepTime')) {
- /**
- * 获取睡眠秒数
- *
- * @param int $i
- * @param int $magRatio
- * @param int $minSleepTime
- *
- * @return int
- */
- function getSleepTime($i, $magRatio = 4, $minSleepTime = 20)
- {
- $sleepTime = $i * $magRatio;
- if ($sleepTime < $minSleepTime) { // 最小休眠 $minSleepTime 秒
- return $minSleepTime;
- }
- return $sleepTime;
- }
- }
- if (!function_exists('getAwsWafToken')) {
- /**
- * 获取 aws waf token
- *
- * @return string
- * @throws LlfException
- * @throws \GuzzleHttp\Exception\GuzzleException
- */
- function getAwsWafToken()
- {
- // 优先从全局变量中获取
- $AWS_WAF_TOKEN = getGlobalValue(CommonConst::AWS_WAF_TOKEN);
- if ($AWS_WAF_TOKEN !== null) {
- return $AWS_WAF_TOKEN;
- }
- $client = new Client([
- 'headers' => [
- 'Accept' => 'application/json',
- 'Authorization' => \env('FF_SECRET_KEY', '')
- ],
- 'timeout' => 32,
- ]);
- // 调用开源的接口获取
- $USE_OPEN_SOURCE_WAF_SOLVER_API = (int)\env('USE_OPEN_SOURCE_WAF_SOLVER_API', 1);
- if ($USE_OPEN_SOURCE_WAF_SOLVER_API === 1) {
- $OPEN_SOURCE_WAF_SOLVER_URL = \env('OPEN_SOURCE_WAF_SOLVER_URL');
- if (!$OPEN_SOURCE_WAF_SOLVER_URL) {
- throw new LlfException(34520020);
- }
- $OPEN_SOURCE_WAF_SOLVER_URL = rtrim($OPEN_SOURCE_WAF_SOLVER_URL, '/');
- $startTime = time();
- $maxWaitSeconds = 300;
- $n = 0;
- while (true) {
- try {
- if (time() - $startTime >= $maxWaitSeconds) {
- break;
- }
- $r = $client->get($OPEN_SOURCE_WAF_SOLVER_URL);
- $body = json_decode($r->getBody()->getContents(), true);
- if (!isset($body['status']) || $body['status'] !== 'OK') {
- throw new \Exception(isset($body['msg']) ? $body['msg'] : json_encode($body));
- }
- $awsWafToken = $body['data']['token'];
- setGlobalValue(CommonConst::AWS_WAF_TOKEN, $awsWafToken);
- system_log(sprintf(lang('100139'), $awsWafToken));
- return $awsWafToken;
- } catch (\Exception $e) {
- system_log('<red>getAwsWafToken error:</red> ' . $e->getMessage());
- }
- $n++;
- sleep($n > 5 ? 60 : 10); // 前 5 次每次休眠 10 秒,之后每次休眠 60 秒
- }
- throw new LlfException(34520021, $maxWaitSeconds);
- }
- // 使用自建接口获取
- $AWS_WAF_SOLVER_URL = \env('AWS_WAF_SOLVER_URL');
- if (!$AWS_WAF_SOLVER_URL) {
- throw new LlfException(34520017);
- }
- $AWS_WAF_SOLVER_URL = rtrim($AWS_WAF_SOLVER_URL, '/');
- $i = 0;
- do {
- try {
- // 获取任务 ID
- $r = $client->get($AWS_WAF_SOLVER_URL);
- $body = json_decode($r->getBody()->getContents(), true);
- if (!isset($body['status']) || $body['status'] !== 'OK') {
- // 一般情况下走不到这个分支
- if (isset($body['msg']) && $body['msg'] === 'A task is already running') {
- sleep(180);
- }
- throw new \Exception(isset($body['msg']) ? $body['msg'] : json_encode($body));
- }
- // 已获取任务 ID,等待任务完成
- $taskId = $body['data']['task_id'];
- $startTime = time();
- while (true) {
- // 最多等 10 分钟
- if (time() - $startTime >= 600) {
- break;
- }
- $r = $client->get(sprintf('%s/%s', $AWS_WAF_SOLVER_URL, $taskId));
- $body = json_decode($r->getBody()->getContents(), true);
- if (!isset($body['status']) || $body['status'] !== 'OK') {
- throw new \Exception(isset($body['msg']) ? $body['msg'] : json_encode($body));
- }
- $taskStatus = $body['data']['task_status'];
- if ($taskStatus !== 'done') { // 任务进行中,继续等待
- sleep(2);
- continue;
- }
- if (!isset($body['data']['result']) || $body['data']['result'] === '') {
- throw new \Exception('no result');
- }
- $awsWafToken = $body['data']['result'] ?? '';
- setGlobalValue(CommonConst::AWS_WAF_TOKEN, $awsWafToken);
- system_log(sprintf(lang('100139'), $awsWafToken));
- return $awsWafToken;
- }
- } catch (\Exception $e) {
- system_log('<red>getAwsWafToken error:</red> ' . $e->getMessage());
- }
- sleep(1);
- $i++;
- } while ($i <= 10);
- throw new LlfException('34520018');
- }
- }
- if (!function_exists('getGlobalValue')) {
- /**
- * 获取全局变量
- *
- * @param string $name
- *
- * @return string|null
- */
- function getGlobalValue(string $name, ?string $default = null)
- {
- return GlobalValue::getInstance()->get($name, $default);
- }
- }
- if (!function_exists('setGlobalValue')) {
- /**
- * 设置全局变量
- *
- * @param string $name
- * @param string $value
- *
- * @return void
- */
- function setGlobalValue(string $name, string $value)
- {
- GlobalValue::getInstance()->set($name, $value);
- }
- }
- if (!function_exists('hasGlobalValue')) {
- /**
- * 是否存在全局变量
- *
- * @param string $name
- *
- * @return bool
- */
- function hasGlobalValue(string $name)
- {
- return GlobalValue::getInstance()->has($name);
- }
- }
- if (!function_exists('delGlobalValue')) {
- /**
- * 删除全局变量
- *
- * @param string $name
- *
- * @return void
- */
- function delGlobalValue(string $name)
- {
- GlobalValue::getInstance()->del($name);
- }
- }
- if (!function_exists('needAwsWafToken')) {
- /**
- * @return bool
- */
- function needAwsWafToken()
- {
- try {
- $client = new Client([
- 'headers' => [
- 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
- 'Accept-Encoding' => 'gzip, deflate, br',
- 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
- ],
- 'timeout' => 4.2011,
- CURLOPT_FOLLOWLOCATION => true,
- CURLOPT_AUTOREFERER => true,
- 'verify' => config('verify_ssl'),
- 'proxy' => config('freenom_proxy'),
- ]);
- $res = $client->get('https://my.freenom.com/clientarea.php');
- return $res->getStatusCode() != 200;
- } catch (\Exception $e) {
- return stripos($e->getMessage(), '405') !== false;
- }
- }
- }
|