Просмотр исходного кода

chore: add missing type hint & improve style

M1Screw 2 лет назад
Родитель
Сommit
9bbeef6d93

+ 5 - 5
README.md

@@ -30,11 +30,11 @@ SSPanel UIM 是一款专为 Shadowsocks / V2Ray / Trojan 协议设计的多用
 - 集成 支付宝当面付,Stripe 银行卡,彩虹易支付 等多种支付系统
 - 支持多种邮件服务,内置队列功能,无需第三方组件即可使用
 - 内置基于 Bootstrap 5 的 tabler 主题,模板引擎支持
-- 支持 Shadowsocks 2022,Shadowsocks AEAD,Trojan-Go 等最新代理协议
-- 通用订阅接口,一键 json/clash/sip008 格式订阅下发
+- 支持 Shadowsocks 2022,TUIC 等最新代理协议
+- 通用订阅接口,一键 json/clash/sip008/sing-box 格式订阅下发
 - 自定义节点配置,模块化订阅系统,支持多种传统订阅模式
 - 重构的商店系统,支持多种计费模式,包括但不限于包年包月,按量计费,接入类型计费等
-- 重构的定时任务系统,再也不需要麻烦地配置 crontab,一个命令即可完成所有定时任务
+- 重构的定时任务系统,再也不需要麻烦地配置 crontab,一个命令即可完成所有定时任务
 
 ## 安装
 
@@ -42,8 +42,8 @@ SSPanel UIM 的需要以下程序才能正常的安装和运行:
 
 - Git
 - Nginx(必须使用 HTTPS/HTTPS is REQUIRED)
-- PHP 8.1+ (推荐开启 OPcache & JIT /OPcache & JIT is highly recommended)
-- MariaDB 10.6+(关闭严格模式,不兼容 MySQL/Disable strict mode, DO NOT USE MYSQL)
+- PHP 8.1+ (强烈推荐开启 OPcache /OPcache is highly recommended)
+- MariaDB 10.6+(关闭严格模式,不兼容 MySQL/Disable strict mode, DO NOT USE MySQL)
 - Redis 7.0+
 
 我们推荐用户在开始使用之前有一定程度的 PHP 和 Linux 使用知识,能够至少正确识别使用中所出现的问题并在 issue 中提供所需的信息。

+ 1 - 1
app/routes.php

@@ -286,7 +286,7 @@ return static function (Slim\App $app): void {
         $group->post('/users/aliveip', App\Controllers\WebAPI\UserController::class . ':addAliveIp');
         $group->post('/users/detectlog', App\Controllers\WebAPI\UserController::class . ':addDetectLog');
         // 审计 & 杂七杂八的功能
-        $group->get('/func/detect_rules', App\Controllers\WebAPI\FuncController::class . ':getDetectLogs');
+        $group->get('/func/detect_rules', App\Controllers\WebAPI\FuncController::class . ':getDetectRules');
         $group->get('/func/ping', App\Controllers\WebAPI\FuncController::class . ':ping');
     })->add(new NodeToken());
 

+ 1 - 0
config/appprofile.example.php

@@ -53,6 +53,7 @@ $_ENV['SingBox_Config'] = [
                 'server' => 'dnspod',
             ],
         ],
+        'final' => 'cloudflare',
         'strategy' => 'prefer_ipv6',
     ],
     'inbounds' => [

+ 81 - 53
src/Command/ClientDownload.php

@@ -5,8 +5,9 @@ declare(strict_types=1);
 namespace App\Command;
 
 use App\Services\Cloudflare;
-use Exception;
+use App\Utils\Tools;
 use GuzzleHttp\Client;
+use GuzzleHttp\Exception\GuzzleException;
 use function file_exists;
 use function file_get_contents;
 use function file_put_contents;
@@ -26,19 +27,15 @@ use const PHP_EOL;
 final class ClientDownload extends Command
 {
     public string $description = '├─=: php xcat ClientDownload - 定时更新客户端' . PHP_EOL;
-
-    private $client;
-
-    /**
-     * 保存基本路径
-     */
+    private Client $client;
     private string $basePath = BASE_PATH . '/';
+    private array $version;
 
     /**
-     * 下载配置
+     * @return void
+     *
+     * @throws GuzzleException
      */
-    private $version;
-
     public function boot(): void
     {
         $this->client = new Client();
@@ -67,6 +64,12 @@ final class ClientDownload extends Command
 
     /**
      * 下载远程文件
+     *
+     * @param string $fileName
+     * @param string $savePath
+     * @param string $url
+     *
+     * @return bool
      */
     private function getSourceFile(string $fileName, string $savePath, string $url): bool
     {
@@ -81,14 +84,14 @@ final class ClientDownload extends Command
             echo '- 下载 ' . $fileName . ' 成功,正在保存...' . PHP_EOL;
             $result = file_put_contents($savePath . $fileName, $request->getBody()->getContents());
 
-            if ($result === false) {
+            if (! $result) {
                 echo '- 保存 ' . $fileName . ' 至 ' . $savePath . ' 失败。' . PHP_EOL;
             } else {
                 echo '- 保存 ' . $fileName . ' 至 ' . $savePath . ' 成功。' . PHP_EOL;
             }
 
             return true;
-        } catch (Exception $e) {
+        } catch (GuzzleException $e) {
             echo '- 下载 ' . $fileName . ' 失败...' . PHP_EOL;
             echo $e->getMessage() . PHP_EOL;
 
@@ -98,6 +101,12 @@ final class ClientDownload extends Command
 
     /**
      * 获取 GitHub 常规 Release
+     *
+     * @param string $repo
+     *
+     * @return string
+     *
+     * @throws GuzzleException
      */
     private function getLatestReleaseTagName(string $repo): string
     {
@@ -105,7 +114,7 @@ final class ClientDownload extends Command
             ($_ENV['github_access_token'] !== '' ? '?access_token=' . $_ENV['github_access_token'] : '');
         $request = $this->client->get($url);
 
-        return (string) json_decode(
+        return json_decode(
             $request->getBody()->getContents(),
             true
         )['tag_name'];
@@ -113,6 +122,12 @@ final class ClientDownload extends Command
 
     /**
      * 获取 GitHub Pre-Release
+     *
+     * @param string $repo
+     *
+     * @return string
+     *
+     * @throws GuzzleException
      */
     private function getLatestPreReleaseTagName(string $repo): string
     {
@@ -124,15 +139,7 @@ final class ClientDownload extends Command
             true
         )[0];
 
-        return (string) $latest['tag_name'];
-    }
-
-    /**
-     * 判断是否 JSON
-     */
-    private function isJson(string $string): bool
-    {
-        return json_decode($string, true) !== false;
+        return $latest['tag_name'];
     }
 
     /**
@@ -147,6 +154,7 @@ final class ClientDownload extends Command
 
         if (! is_file($filePath)) {
             echo '本地软体版本库 LocalClientVersion.json 不存在,创建文件中...' . PHP_EOL;
+
             $result = file_put_contents(
                 $filePath,
                 json_encode(
@@ -155,7 +163,8 @@ final class ClientDownload extends Command
                     ]
                 )
             );
-            if ($result === false) {
+
+            if (! $result) {
                 echo 'LocalClientVersion.json 创建失败,脚本中止。' . PHP_EOL;
                 exit(0);
             }
@@ -163,7 +172,7 @@ final class ClientDownload extends Command
 
         $fileContent = file_get_contents($filePath);
 
-        if (! $this->isJson($fileContent)) {
+        if (! Tools::isJson($fileContent)) {
             echo 'LocalClientVersion.json 文件格式异常,脚本中止。' . PHP_EOL;
             exit(0);
         }
@@ -173,12 +182,17 @@ final class ClientDownload extends Command
 
     /**
      * 储存本地软体版本库
+     *
+     * @param array $versions
+     *
+     * @return void
      */
     private function setLocalVersions(array $versions): void
     {
         $fileName = 'LocalClientVersion.json';
         $filePath = BASE_PATH . '/storage/' . $fileName;
-        (bool) file_put_contents(
+
+        file_put_contents(
             $filePath,
             json_encode(
                 $versions
@@ -186,18 +200,47 @@ final class ClientDownload extends Command
         );
     }
 
+    /**
+     * @param $name
+     * @param $taskName
+     * @param $tagName
+     *
+     * @return array|string
+     */
+    private static function getNames($name, $taskName, $tagName): array|string
+    {
+        return str_replace(
+            [
+                '%taskName%',
+                '%tagName%',
+                '%tagName1%',
+            ],
+            [
+                $taskName,
+                $tagName,
+                substr($tagName, 1),
+            ],
+            $name
+        );
+    }
+
+    /**
+     * @param array $task
+     *
+     * @return void
+     *
+     * @throws GuzzleException
+     */
     private function getSoft(array $task): void
     {
         $savePath = $this->basePath . $task['savePath'];
         echo '====== ' . $task['name'] . ' 开始 ======' . PHP_EOL;
 
-        $tagMethod = match ($task['tagMethod']) {
-            'github_pre_release' => 'getLatestPreReleaseTagName',
-            default => 'getLatestReleaseTagName',
+        $tagName = match ($task['tagMethod']) {
+            'github_pre_release' => self::getLatestPreReleaseTagName($task['gitRepo']),
+            default => self::getLatestReleaseTagName($task['gitRepo']),
         };
 
-        $tagName = $this->$tagMethod($task['gitRepo']);
-
         if (! isset($this->version[$task['name']])) {
             echo '- 本地不存在 ' . $task['name'] . ',检测到当前最新版本为 ' . $tagName . PHP_EOL;
         } else {
@@ -211,33 +254,18 @@ final class ClientDownload extends Command
         }
 
         $this->version[$task['name']] = $tagName;
-        $nameFunction = static function ($name) use ($task, $tagName) {
-            return str_replace(
-                [
-                    '%taskName%',
-                    '%tagName%',
-                    '%tagName1%',
-                ],
-                [
-                    $task['name'],
-                    $tagName,
-                    substr($tagName, 1),
-                ],
-                $name
-            );
-        };
 
         foreach ($task['downloads'] as $download) {
-            $fileName = $nameFunction(($download['saveName'] !== '' ? $download['saveName'] : $download['sourceName']));
-            $sourceName = $nameFunction($download['sourceName']);
+            $fileName = $download['saveName'] !== '' ? $download['saveName'] : $download['sourceName'];
+            $fileName = self::getNames($fileName, $task['name'], $tagName);
+            $sourceName = self::getNames($download['sourceName'], $task['name'], $tagName);
             $filePath = $savePath . $fileName;
 
-            if (is_file($filePath)) {
-                echo '- 正在删除旧版本文件...' . PHP_EOL;
-                if (! unlink($filePath)) {
-                    echo '- 删除旧版本文件失败,此任务跳过,请检查权限' . PHP_EOL;
-                    continue;
-                }
+            echo '- 正在删除旧版本文件...' . PHP_EOL;
+
+            if (! unlink($filePath)) {
+                echo '- 删除旧版本文件失败,此任务跳过,请检查权限' . PHP_EOL;
+                continue;
             }
 
             $downloadUrl = 'https://github.com/' . $task['gitRepo'] .

+ 1 - 1
src/Command/Command.php

@@ -11,7 +11,7 @@ namespace App\Command;
  */
 abstract class Command
 {
-    public $argv;
+    public array|null $argv;
 
     public function __construct($argv)
     {

+ 22 - 13
src/Command/Migration.php

@@ -13,6 +13,7 @@ use function is_numeric;
 use function krsort;
 use function ksort;
 use function scandir;
+use const PHP_EOL;
 use const PHP_INT_MAX;
 use const SCANDIR_SORT_NONE;
 
@@ -28,10 +29,8 @@ END;
     public function boot(): void
     {
         $reverse = false;
-        // (min_version, max_version]
         $min_version = 0;
         $max_version = 0;
-
         $target = $this->argv[2] ?? 0;
 
         if ($target === 'new') {
@@ -45,14 +44,16 @@ END;
             $max_version = PHP_INT_MAX;
         } elseif ($target === 'new') {
             $tables = DB::select('SHOW TABLES');
+
             if ($tables === []) {
                 $max_version = PHP_INT_MAX;
             } else {
-                echo "Database is not empty, do not use \"new\" as version.\n";
+                echo 'Database is not empty, do not use "new" as version.' . PHP_EOL;
                 return;
             }
         } elseif (is_numeric($target)) {
             $target = (int) $target;
+
             if ($target < $current) {
                 $reverse = true;
                 $min_version = $target;
@@ -62,44 +63,50 @@ END;
                 $max_version = $target;
             }
         } else {
-            echo "Illegal version argument.\n";
+            echo 'Illegal version argument.' . PHP_EOL;
             return;
         }
 
-        echo "Current database version {$current}.\n\n";
+        echo 'Current database version ' . $current . PHP_EOL . PHP_EOL;
 
         $queue = [];
         $files = scandir(BASE_PATH . '/db/migrations/', SCANDIR_SORT_NONE);
+
         if ($files) {
             foreach ($files as $file) {
                 if ($file === '.' || $file === '..' || ! str_ends_with($file, '.php')) {
                     continue;
                 }
+
                 $version = (int) explode('-', $file, 1)[0];
-                echo "Found migration version {$version}.";
+                echo 'Found migration version ' . $version;
+
                 if ($version <= $min_version || $version > $max_version) {
-                    echo "...skip\n";
+                    echo '...skip' . PHP_EOL;
                     continue;
                 }
-                echo "\n";
+
+                echo PHP_EOL;
                 $object = require_once BASE_PATH . '/db/migrations/' . $file;
+
                 if ($object instanceof MigrationInterface) {
                     $queue[$version] = $object;
                 }
             }
         }
-        echo "\n";
+
+        echo PHP_EOL;
 
         if ($reverse) {
             krsort($queue);
             foreach ($queue as $version => $object) {
-                echo "Reverse on {$version}\n";
+                echo 'Reverse on ' . $version . PHP_EOL;
                 $current = $object->down();
             }
         } else {
             ksort($queue);
             foreach ($queue as $version => $object) {
-                echo "Forward to {$version}\n";
+                echo 'Forward to ' . $version . PHP_EOL;
                 $current = $object->up();
             }
         }
@@ -109,10 +116,12 @@ END;
                         VALUES("db_version", ?, "int", "2023020100")',
             default => 'UPDATE `config` SET `value` = ? WHERE `item` = "db_version"'
         };
+
         $stat = DB::getPdo()->prepare($sql);
         $stat->execute([$current]);
-
         $count = count($queue);
-        echo "\nMigration completed. {$count} file(s) processed.\nCurrent version: {$current}\n";
+
+        echo 'Migration completed. ' . $count . ' file(s) processed.' . PHP_EOL
+            . 'Current version: ' . $current . PHP_EOL;
     }
 }

+ 5 - 3
src/Command/Tool.php

@@ -139,12 +139,13 @@ EOL;
                 $new_item->mark = $item['mark'];
                 $new_item->save();
 
-                echo "添加新数据库设置:{$item_name}" . PHP_EOL;
+                echo '添加新数据库设置:' . $item_name . PHP_EOL;
                 $add_counter += 1;
             }
         }
         // 检查移除
         $db_settings = Setting::all();
+
         foreach ($db_settings as $db_setting) {
             if (! in_array($db_setting->item, $config)) {
                 $db_setting->delete();
@@ -153,12 +154,12 @@ EOL;
         }
 
         if ($add_counter !== 0) {
-            echo "总计添加了 {$add_counter} 项新数据库设置" . PHP_EOL;
+            echo '总计添加了 ' . $add_counter . ' 项新数据库设置' . PHP_EOL;
         } else {
             echo '没有任何新数据库设置项需要添加' . PHP_EOL;
         }
         if ($del_counter !== 0) {
-            echo "总计移除了 {$del_counter} 项数据库设置" . PHP_EOL;
+            echo '总计移除了 ' . $del_counter . ' 项数据库设置' . PHP_EOL;
         }
     }
 
@@ -360,6 +361,7 @@ EOL;
         fwrite(STDOUT, '请输入要设置的主题名称: ');
         $theme = trim(fgets(STDIN));
         $users = ModelsUser::all();
+
         foreach ($users as $user) {
             $user->theme = $theme;
             $user->save();

+ 20 - 10
src/Command/Update.php

@@ -4,6 +4,7 @@ declare(strict_types=1);
 
 namespace App\Command;
 
+use Exception;
 use tronovav\GeoIP2Update\Client;
 use function array_diff;
 use function copy;
@@ -15,6 +16,7 @@ use function str_replace;
 use function strpos;
 use function substr;
 use const BASE_PATH;
+use const PHP_EOL;
 
 final class Update extends Command
 {
@@ -29,9 +31,9 @@ END;
         $copy_result = copy(BASE_PATH . '/config/.config.php', BASE_PATH . '/config/.config.php.bak');
 
         if ($copy_result) {
-            echo ".config.php 文件备份成功。\n";
+            echo '.config.php 文件备份成功。' . PHP_EOL;
         } else {
-            echo ".config.php 文件备份失败,迁移终止。\n";
+            echo '.config.php 文件备份失败,迁移终止。' . PHP_EOL;
         }
 
         $config_old = file_get_contents(BASE_PATH . '/config/.config.php');
@@ -43,8 +45,9 @@ END;
             $regex = '/_ENV\[\'' . $key . '\'\].*?;/s';
             $matches_new = [];
             preg_match($regex, $config_new, $matches_new);
-            if (isset($matches_new[0]) === false) {
-                echo "未找到配置项:" . $key . " 未能在新版本 .config.php 文件中找到,可能已被更名或废弃。\n";
+
+            if (! isset($matches_new[0])) {
+                echo '未找到配置项:' . $key . ' 未能在新版本 .config.php 文件中找到,可能已被更名或废弃。' . PHP_EOL;
                 continue;
             }
 
@@ -83,23 +86,30 @@ END;
             $difference = substr($difference, 15);
             $difference = substr($difference, 0, -2);
 
-            echo "新增 .config.php 配置项:" . $difference . ":" . $comment . " \n";
+            echo '新增 .config.php 配置项:' . $difference . ':' . $comment . PHP_EOL;
         }
 
-        echo "没有任何新 .config.php 配置项需要添加。\n";
+        echo '没有任何新 .config.php 配置项需要添加。' . PHP_EOL;
         file_put_contents(BASE_PATH . '/config/.config.php', $config_new);
-        echo "迁移完成。\n";
+        echo '迁移完成。' . PHP_EOL;
 
         // 更新 GeoLite2 数据库
         if ($_ENV['maxmind_license_key'] !== '') {
-            echo "正在更新 GeoLite2 数据库...\n";
+            echo '正在更新 GeoLite2 数据库...' . PHP_EOL;
+
             $client = new Client([
                 'license_key' => $_ENV['maxmind_license_key'],
                 'dir' => BASE_PATH . '/storage/',
                 'editions' => ['GeoLite2-City', "GeoLite2-Country"],
             ]);
-            $client->run();
-            echo "成功更新 GeoLite2 数据库。\n";
+
+            try {
+                $client->run();
+                echo '成功更新 GeoLite2 数据库。' . PHP_EOL;
+            } catch (Exception $e) {
+                echo '更新 GeoLite2 数据库失败。' . PHP_EOL;
+                echo $e->getMessage() . PHP_EOL;
+            }
         }
     }
 }

+ 1 - 1
src/Controllers/WebAPI/FuncController.php

@@ -21,7 +21,7 @@ final class FuncController extends BaseController
         ]);
     }
 
-    public function getDetectLogs(ServerRequest $request, Response $response, array $args): ResponseInterface
+    public function getDetectRules(ServerRequest $request, Response $response, array $args): ResponseInterface
     {
         $rules = DetectRule::all();
 

+ 1 - 1
src/Services/Auth.php

@@ -8,7 +8,7 @@ use App\Models\User;
 
 final class Auth
 {
-    private $user;
+    private User $user;
 
     public static function login($uid, $time): void
     {

+ 14 - 27
src/Services/Bot/Telegram/Message.php

@@ -24,11 +24,6 @@ final class Message
      */
     private Api $bot;
 
-    /**
-     * 触发用户TG信息
-     */
-    private array $trigger_user;
-
     /**
      * 消息会话 ID
      */
@@ -43,7 +38,6 @@ final class Message
      * 触发源信息 ID
      */
     private $message_id;
-    private $user;
 
     /**
      * @throws TelegramSDKException|GuzzleException
@@ -51,14 +45,6 @@ final class Message
     public function __construct(Api $bot, Collection $message)
     {
         $this->bot = $bot;
-
-        $this->trigger_user = [
-            'id' => $message->getFrom()->getId(),
-            'name' => $message->getFrom()->getFirstName() . ' Message.php' . $message->getFrom()->getLastName(),
-            'username' => $message->getFrom()->getUsername(),
-        ];
-
-        $this->user = self::getUser($this->trigger_user['id']);
         $this->chat_id = $message->getChat()->getId();
         $this->message = $message;
         $this->message_id = $message->getMessageId();
@@ -96,14 +82,14 @@ final class Message
      */
     public function newChatParticipant(): void
     {
-        $NewChatMember = $this->message->getNewChatParticipant();
+        $new_chat_member = $this->message->getNewChatParticipant();
 
-        $Member = [
-            'id' => $NewChatMember->getId(),
-            'name' => $NewChatMember->getFirstName() . ' Message.php' . $NewChatMember->getLastName(),
+        $member = [
+            'id' => $new_chat_member->getId(),
+            'name' => $new_chat_member->getFirstName() . ' Message.php' . $new_chat_member->getLastName(),
         ];
 
-        if ($NewChatMember->getUsername() === Setting::obtain('telegram_bot')) {
+        if ($new_chat_member->getUsername() === Setting::obtain('telegram_bot')) {
             // 机器人加入新群组
             if (! Setting::obtain('allow_to_join_new_groups')
                 &&
@@ -120,7 +106,7 @@ final class Message
                     'kickChatMember',
                     [
                         'chat_id' => $this->chat_id,
-                        'user_id' => $Member['id'],
+                        'user_id' => $member['id'],
                     ]
                 );
             } else {
@@ -132,19 +118,19 @@ final class Message
             }
         } else {
             // 新成员加入群组
-            $NewUser = self::getUser($Member['id']);
+            $new_user = self::getUser($member['id']);
 
             if (Setting::obtain('telegram_group_bound_user')
                 &&
                 $this->chat_id === Setting::obtain('telegram_chatid')
                 &&
-                $NewUser === null
+                $new_user === null
                 &&
-                ! $NewChatMember->isBot()
+                ! $new_chat_member->isBot()
             ) {
                 $this->replyWithMessage(
                     [
-                        'text' => '由于 ' . $Member['name'] . ' 未绑定账户,将被移除。',
+                        'text' => '由于 ' . $member['name'] . ' 未绑定账户,将被移除。',
                     ]
                 );
 
@@ -152,15 +138,16 @@ final class Message
                     'kickChatMember',
                     [
                         'chat_id' => $this->chat_id,
-                        'user_id' => $Member['id'],
+                        'user_id' => $member['id'],
                     ]
                 );
+
                 return;
             }
 
             if (Setting::obtain('enable_welcome_message')) {
-                $text = ($NewUser->class > 0 ? '欢迎 VIP' . $NewUser->class .
-                    ' 用户 ' . $Member['name'] . '加入群组。' : '欢迎 ' . $Member['name']);
+                $text = ($new_user->class > 0 ? '欢迎 VIP' . $new_user->class .
+                    ' 用户 ' . $member['name'] . '加入群组。' : '欢迎 ' . $member['name']);
 
                 $this->replyWithMessage(
                     [

+ 2 - 1
src/Services/Gateway/Epay.php

@@ -93,7 +93,6 @@ final class Epay extends AbstractPayment
             'notify_url' => $_ENV['baseUrl'] . '/payment/notify/epay',
             'return_url' => $_ENV['baseUrl'] . '/user/payment/return/epay',
             'name' => $pl->tradeno,
-            #"name" =>  $user->mobile . "" . $price . "",
             'money' => $price,
             'sitename' => $_ENV['appName'],
         ];
@@ -112,12 +111,14 @@ final class Epay extends AbstractPayment
         if ($verify_result) {
             $out_trade_no = $_GET['out_trade_no'];
             $type = $_GET['type'];
+
             $type = match ($type) {
                 'qqpay' => 'QQ',
                 'wxpay' => 'WeChat',
                 'epusdt' => 'USDT',
                 default => 'Alipay',
             };
+
             $trade_status = $_GET['trade_status'];
 
             if ($trade_status === 'TRADE_SUCCESS') {

+ 3 - 4
src/Services/Gateway/Epay/EpayNotify.php

@@ -8,8 +8,8 @@ use function is_null;
 
 final class EpayNotify
 {
-    private $alipay_config;
-    private $http_verify_url;
+    private array $alipay_config;
+    private string $http_verify_url;
 
     public function __construct($alipay_config)
     {
@@ -26,14 +26,13 @@ final class EpayNotify
         $isSign = $this->getSignVeryfy($_GET, $_GET['sign']);
         //获取支付宝远程服务器ATN结果(验证是否是支付宝发来的消息)
         $responseTxt = 'true';
-        //if (! empty($_POST["notify_id"])) {$responseTxt = $this->getResponse($_POST["notify_id"]);}
-
         //验证
         //$responsetTxt的结果不是true,与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
         //isSign的结果不是true,与安全校验码、请求时的参数格式(如:带自定义参数等)、编码格式有关
         if (preg_match('/true$/i', $responseTxt) && $isSign) {
             return true;
         }
+
         return false;
     }
 

+ 1 - 1
src/Services/Gateway/Epay/EpaySubmit.php

@@ -6,7 +6,7 @@ namespace App\Services\Gateway\Epay;
 
 final class EpaySubmit
 {
-    private $alipay_config;
+    private array $alipay_config;
     private string $alipay_gateway_new;
 
     public function __construct($alipay_config)

+ 2 - 1
src/Services/Payment.php

@@ -5,6 +5,7 @@ declare(strict_types=1);
 namespace App\Services;
 
 use App\Utils\ClassHelper;
+use Psr\Http\Message\ResponseInterface;
 
 final class Payment
 {
@@ -61,7 +62,7 @@ final class Payment
         return $response->withStatus(404);
     }
 
-    public static function returnHTML($request, $response, $args)
+    public static function returnHTML($request, $response, $args): string|ResponseInterface
     {
         $payment = self::getPaymentByName($args['type']);
 

+ 3 - 2
src/Services/View.php

@@ -5,12 +5,13 @@ declare(strict_types=1);
 namespace App\Services;
 
 use App\Models\Setting;
+use Illuminate\Database\DatabaseManager;
 use Smarty;
 
 final class View
 {
-    public static $connection;
-    public static $beginTime;
+    public static DatabaseManager $connection;
+    public static float $beginTime;
 
     public static function getSmarty(): Smarty
     {

+ 2 - 2
src/Utils/ClassHelper.php

@@ -18,7 +18,7 @@ final class ClassHelper
 
         self::$composer = require __DIR__ . '/../../vendor/autoload.php';
 
-        if (is_null(self::$composer) === false) {
+        if (! is_null(self::$composer)) {
             self::$classes = array_keys(self::$composer->getClassMap());
         }
     }
@@ -27,7 +27,7 @@ final class ClassHelper
     {
         $allClasses = [];
 
-        if (is_null(self::$classes) === false) {
+        if (! is_null(self::$classes)) {
             foreach (self::$classes as $class) {
                 $allClasses[] = '\\' . $class;
             }

+ 107 - 6
src/Utils/Tools.php

@@ -24,6 +24,7 @@ use function floor;
 use function in_array;
 use function is_null;
 use function is_numeric;
+use function json_decode;
 use function log;
 use function opendir;
 use function openssl_random_pseudo_bytes;
@@ -48,9 +49,13 @@ final class Tools
     /**
      * 查询IP归属
      *
+     * @param string $ip
+     *
+     * @return string
+     *
      * @throws InvalidDatabaseException
      */
-    public static function getIpLocation($ip): string
+    public static function getIpLocation(string $ip): string
     {
         $err_msg = '';
         $city = null;
@@ -87,8 +92,13 @@ final class Tools
 
     /**
      * 根据流量值自动转换单位输出
+     *
+     * @param $size
+     * @param int $precision
+     *
+     * @return string
      */
-    public static function autoBytes($size, $precision = 2): string
+    public static function autoBytes($size, int $precision = 2): string
     {
         if ($size <= 0) {
             return '0B';
@@ -106,6 +116,10 @@ final class Tools
 
     /**
      * 根据含单位的流量值转换 B 输出
+     *
+     * @param $size
+     *
+     * @return int|null
      */
     public static function autoBytesR($size): ?int
     {
@@ -126,8 +140,13 @@ final class Tools
 
     /**
      * 根据速率值自动转换单位输出
+     *
+     * @param $size
+     * @param int $precision
+     *
+     * @return string
      */
-    public static function autoMbps($size, $precision = 2): string
+    public static function autoMbps($size, int $precision = 2): string
     {
         if ($size <= 0) {
             return '0Bps';
@@ -144,28 +163,48 @@ final class Tools
     }
 
     //虽然名字是toMB,但是实际上功能是from MB to B
+    /**
+     * @param $traffic
+     *
+     * @return float|int
+     */
     public static function toMB($traffic): float|int
     {
         return $traffic * 1048576;
     }
 
     //虽然名字是toGB,但是实际上功能是from GB to B
+    /**
+     * @param $traffic
+     *
+     * @return float|int
+     */
     public static function toGB($traffic): float|int
     {
         return $traffic * 1073741824;
     }
 
+    /**
+     * @param $traffic
+     *
+     * @return float
+     */
     public static function flowToGB($traffic): float
     {
         return $traffic / 1073741824;
     }
 
+    /**
+     * @param $traffic
+     *
+     * @return float
+     */
     public static function flowToMB($traffic): float
     {
         return $traffic / 1048576;
     }
 
-    public static function genRandomChar($length = 8): string
+    public static function genRandomChar(int $length = 8): string
     {
         return bin2hex(openssl_random_pseudo_bytes($length / 2));
     }
@@ -175,7 +214,7 @@ final class Tools
         return date('Y-m-d H:i:s', $time);
     }
 
-    public static function getAvPort()
+    public static function getAvPort(): mixed
     {
         if (Setting::obtain('min_port') > 65535
             || Setting::obtain('min_port') <= 0
@@ -192,6 +231,11 @@ final class Tools
         return $port[0];
     }
 
+    /**
+     * @param $dir
+     *
+     * @return array
+     */
     public static function getDir($dir): array
     {
         $dirArray = [];
@@ -211,6 +255,12 @@ final class Tools
         return $dirArray;
     }
 
+    /**
+     * @param $type
+     * @param $str
+     *
+     * @return bool
+     */
     public static function isParamValidate($type, $str): bool
     {
         $list = Config::getSupportParam($type);
@@ -222,15 +272,25 @@ final class Tools
         return false;
     }
 
+    /**
+     * @param $input
+     *
+     * @return bool
+     */
     public static function isEmail($input): bool
     {
-        if (filter_var($input, FILTER_VALIDATE_EMAIL) === false) {
+        if (! filter_var($input, FILTER_VALIDATE_EMAIL)) {
             return false;
         }
 
         return true;
     }
 
+    /**
+     * @param $email
+     *
+     * @return array
+     */
     public static function isEmailLegal($email): array
     {
         $res = [];
@@ -269,6 +329,11 @@ final class Tools
         }
     }
 
+    /**
+     * @param $input
+     *
+     * @return bool
+     */
     public static function isIPv4($input): bool
     {
         if (! filter_var($input, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
@@ -278,6 +343,11 @@ final class Tools
         return true;
     }
 
+    /**
+     * @param $input
+     *
+     * @return bool
+     */
     public static function isIPv6($input): bool
     {
         if (! filter_var($input, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
@@ -287,6 +357,11 @@ final class Tools
         return true;
     }
 
+    /**
+     * @param $input
+     *
+     * @return bool
+     */
     public static function isInt($input): bool
     {
         if (! filter_var($input, FILTER_VALIDATE_INT)) {
@@ -296,6 +371,22 @@ final class Tools
         return true;
     }
 
+    /**
+     * 判断是否 JSON
+     *
+     * @param string $string
+     *
+     * @return bool
+     */
+    public static function isJson(string $string): bool
+    {
+        if (! json_decode($string)) {
+            return false;
+        }
+
+        return true;
+    }
+
     public static function genSubToken(): string
     {
         $token = self::genRandomChar($_ENV['sub_token_len']);
@@ -310,6 +401,10 @@ final class Tools
 
     /**
      * 获取累计收入
+     *
+     * @param string $req
+     *
+     * @return float
      */
     public static function getIncome(string $req): float
     {
@@ -345,6 +440,12 @@ final class Tools
             ->get();
     }
 
+    /**
+     * @param $user
+     * @param $len
+     *
+     * @return string
+     */
     public static function getSs2022UserPk($user, $len): string
     {
         $passwd_hash = hash('sha256', $user->passwd);