Browse Source

first commit

liuhairui 6 months ago
parent
commit
90b3f1a384
100 changed files with 1992 additions and 932 deletions
  1. 129 23
      application/api/controller/WorkOrder.php
  2. 14 0
      application/config.php
  3. 26 1
      application/database.php
  4. 36 46
      application/service/AIGatewayService.php
  5. 1 0
      thinkphp/.gitignore
  6. 1 1
      thinkphp/README.md
  7. 1 1
      thinkphp/base.php
  8. 7 0
      thinkphp/library/think/Collection.php
  9. 19 7
      thinkphp/library/think/Cookie.php
  10. 1 1
      thinkphp/library/think/Lang.php
  11. 1 1
      thinkphp/library/think/Loader.php
  12. 28 3
      thinkphp/library/think/Model.php
  13. 8 1
      thinkphp/library/think/Paginator.php
  14. 12 7
      thinkphp/library/think/Request.php
  15. 2 2
      thinkphp/library/think/Template.php
  16. 25 3
      thinkphp/library/think/Validate.php
  17. 1 1
      thinkphp/library/think/console/output/Ask.php
  18. 2 2
      thinkphp/library/think/console/output/Descriptor.php
  19. 1 0
      thinkphp/library/think/db/Query.php
  20. 1 5
      thinkphp/library/think/db/builder/Mysql.php
  21. 1 2
      thinkphp/library/think/db/builder/Sqlsrv.php
  22. 1 1
      thinkphp/library/think/model/relation/HasMany.php
  23. 1 1
      thinkphp/library/think/model/relation/MorphOne.php
  24. 7 1
      thinkphp/library/think/session/driver/Redis.php
  25. 2 2
      thinkphp/library/traits/controller/Jump.php
  26. 2 2
      thinkphp/tpl/think_exception.tpl
  27. 1 0
      vendor/composer/autoload_psr4.php
  28. 5 0
      vendor/composer/autoload_static.php
  29. 197 152
      vendor/composer/installed.json
  30. 65 56
      vendor/composer/installed.php
  31. 2 2
      vendor/composer/platform_check.php
  32. BIN
      vendor/composer/tmp-46b11b947a3050d9f527b6bced75ee76~
  33. 0 0
      vendor/composer/tmp-4dddd166370a65ef32d5abc9c6924d21~
  34. 0 0
      vendor/composer/tmp-5f83881c4a8c80bca2bd03733828d4d6~
  35. BIN
      vendor/composer/tmp-701e0887138070223d492469d3ca8969~
  36. BIN
      vendor/composer/tmp-b6e3054c63db3413b85b6cb63e4bc72c~
  37. 0 0
      vendor/composer/tmp-bffa71a58ddb2af0aff1a32bb52cf051~
  38. 8 0
      vendor/guzzlehttp/guzzle/CHANGELOG.md
  39. 4 0
      vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php
  40. 6 6
      vendor/guzzlehttp/guzzle/src/Handler/Proxy.php
  41. 8 2
      vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php
  42. 1 1
      vendor/guzzlehttp/guzzle/src/Pool.php
  43. 1 1
      vendor/guzzlehttp/guzzle/src/Utils.php
  44. 1 1
      vendor/guzzlehttp/guzzle/src/functions.php
  45. 14 0
      vendor/guzzlehttp/promises/CHANGELOG.md
  46. 10 0
      vendor/guzzlehttp/psr7/CHANGELOG.md
  47. 11 11
      vendor/guzzlehttp/psr7/src/UploadedFile.php
  48. 1 1
      vendor/guzzlehttp/psr7/src/Uri.php
  49. 0 1
      vendor/maennchen/zipstream-php/.github/FUNDING.yml
  50. 0 12
      vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE.md
  51. 0 6
      vendor/maennchen/zipstream-php/.gitignore
  52. 4 0
      vendor/maennchen/zipstream-php/.phive/phars.xml
  53. 71 0
      vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php
  54. 15 0
      vendor/maennchen/zipstream-php/.phpdoc/template/base.html.twig
  55. 1 0
      vendor/maennchen/zipstream-php/.tool-versions
  56. 0 12
      vendor/maennchen/zipstream-php/.travis.yml
  57. 0 51
      vendor/maennchen/zipstream-php/CHANGELOG.md
  58. 0 25
      vendor/maennchen/zipstream-php/CONTRIBUTING.md
  59. 26 35
      vendor/maennchen/zipstream-php/README.md
  60. 39 4
      vendor/maennchen/zipstream-php/composer.json
  61. 79 0
      vendor/maennchen/zipstream-php/guides/ContentLength.rst
  62. 33 0
      vendor/maennchen/zipstream-php/guides/FlySystem.rst
  63. 16 0
      vendor/maennchen/zipstream-php/guides/Nginx.rst
  64. 61 0
      vendor/maennchen/zipstream-php/guides/Options.rst
  65. 18 0
      vendor/maennchen/zipstream-php/guides/PSR7Streams.rst
  66. 33 0
      vendor/maennchen/zipstream-php/guides/StreamOutput.rst
  67. 126 0
      vendor/maennchen/zipstream-php/guides/Symfony.rst
  68. 22 0
      vendor/maennchen/zipstream-php/guides/Varnish.rst
  69. 85 0
      vendor/maennchen/zipstream-php/guides/index.rst
  70. 39 0
      vendor/maennchen/zipstream-php/phpdoc.dist.xml
  71. 13 16
      vendor/maennchen/zipstream-php/phpunit.xml.dist
  72. 0 2
      vendor/maennchen/zipstream-php/psalm.xml
  73. 21 19
      vendor/maennchen/zipstream-php/src/Bigint.php
  74. 2 1
      vendor/maennchen/zipstream-php/src/DeflateStream.php
  75. 1 0
      vendor/maennchen/zipstream-php/src/Exception.php
  76. 1 0
      vendor/maennchen/zipstream-php/src/Exception/EncodingException.php
  77. 1 0
      vendor/maennchen/zipstream-php/src/Exception/FileNotFoundException.php
  78. 1 0
      vendor/maennchen/zipstream-php/src/Exception/FileNotReadableException.php
  79. 1 0
      vendor/maennchen/zipstream-php/src/Exception/IncompatibleOptionsException.php
  80. 1 0
      vendor/maennchen/zipstream-php/src/Exception/OverflowException.php
  81. 1 0
      vendor/maennchen/zipstream-php/src/Exception/StreamNotReadableException.php
  82. 119 113
      vendor/maennchen/zipstream-php/src/File.php
  83. 21 6
      vendor/maennchen/zipstream-php/src/Option/Archive.php
  84. 11 5
      vendor/maennchen/zipstream-php/src/Option/File.php
  85. 4 2
      vendor/maennchen/zipstream-php/src/Option/Method.php
  86. 6 3
      vendor/maennchen/zipstream-php/src/Option/Version.php
  87. 52 40
      vendor/maennchen/zipstream-php/src/Stream.php
  88. 82 73
      vendor/maennchen/zipstream-php/src/ZipStream.php
  89. 1 0
      vendor/maennchen/zipstream-php/test/BigintTest.php
  90. 203 139
      vendor/maennchen/zipstream-php/test/ZipStreamTest.php
  91. 1 0
      vendor/maennchen/zipstream-php/test/bootstrap.php
  92. 7 6
      vendor/maennchen/zipstream-php/test/bug/BugHonorFileTimeTest.php
  93. 8 0
      vendor/monolog/monolog/CHANGELOG.md
  94. 4 0
      vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php
  95. 1 0
      vendor/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php
  96. 9 4
      vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php
  97. 38 6
      vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php
  98. 4 3
      vendor/monolog/monolog/src/Monolog/Logger.php
  99. 1 1
      vendor/monolog/monolog/src/Monolog/Utils.php
  100. 44 0
      vendor/mpdf/mpdf/.github/workflows/static-analysis.yml

+ 129 - 23
application/api/controller/WorkOrder.php

@@ -9,11 +9,42 @@ use think\Db;
 use think\Log;
 use think\Queue;
 use think\queue\job\Redis;
-
 class WorkOrder extends Api
 {
     protected $noNeedLogin = ['*'];
     protected $noNeedRight = ['*'];
+
+    /**
+     * 通过店铺ID-查询对应店铺表数据
+     *
+     */
+    public function PatternApi()
+    {
+        $params = $this->request->param('pattern_id', '');
+        $tableName = 'pattern-' . $params;
+
+        // 连接 MongoDB
+        $mongo = Db::connect('mongodb');
+
+        // 查询指定 skc 的数据
+        $data = $mongo->table($tableName)
+            ->field('
+                name,
+                skc,
+                file
+            ')
+            ->where("skc", '0853004152036')
+            ->select();
+
+        $data = json_decode(json_encode($data), true);  // 数组
+
+        return json([
+            'code' => 0,
+            'msg' => '获取成功',
+            'data' => $data
+        ]);
+    }
+
     /**
      * 出图接口
      * 此方法处理图像转换为文本的请求,将图像信息存入队列以供后续处理。
@@ -34,11 +65,11 @@ class WorkOrder extends Api
     public function imgtowimg()
     {
         $prompt = $this->request->param('prompt', '');
-        $denoising = (float)$this->request->param('denoising_strength', 0.2); // 重绘幅度
-        $scale = (float)$this->request->param('scale', 2.0); // 放大倍数
+        $denoising = (float)$this->request->param('denoising_strength', 0.2);
+        $scale = (float)$this->request->param('scale', 2.0);
         $modelName = $this->request->param('model', 'realisticVisionV51_v51VAE-inpainting.safetensors [f0d4872d24]');
 
-        //原图路径
+        // 原图路径
         $imgRelPath = 'uploads/operate/ai/Preview/arr/0828004096727.png';
         $imgPath = ROOT_PATH . 'public/' . $imgRelPath;
 
@@ -46,16 +77,17 @@ class WorkOrder extends Api
             return json(['code' => 1, 'msg' => '原图不存在:' . $imgRelPath]);
         }
 
+        // 获取原图尺寸 × 倍数
         list($originW, $originH) = getimagesize($imgPath);
         $targetW = intval($originW * $scale);
         $targetH = intval($originH * $scale);
 
-        // 将原图转为 base64
+        // base64 编码
         $imgData = file_get_contents($imgPath);
         $base64Img = base64_encode($imgData);
         $initImage = 'data:image/png;base64,' . $base64Img;
 
-        // 构造请求体
+        // 请求体
         $postData = json_encode([
             'prompt' => $prompt,
             'steps' => 30,
@@ -63,7 +95,7 @@ class WorkOrder extends Api
             'denoising_strength' => $denoising,
             'width' => $targetW,
             'height' => $targetH,
-            'resize_mode' => 1, // 缩放模式
+            'resize_mode' => 1,
             'inpaint_full_res' => true,
             'inpainting_fill' => 1,
             'init_images' => [$initImage],
@@ -95,14 +127,15 @@ class WorkOrder extends Api
             return json(['code' => 1, 'msg' => '接口未返回图像数据']);
         }
 
-        // 保存生成图像
+        // 保存图像:原图名 + -1
         $resultImg = base64_decode($data['images'][0]);
-        $saveDir = ROOT_PATH . 'public/uploads/img/';
+        $saveDir = ROOT_PATH . 'public/uploads/img2img/';
         if (!is_dir($saveDir)) {
             mkdir($saveDir, 0755, true);
         }
 
-        $fileName = 'img2img_' . date('Ymd_His') . '_' . mt_rand(1000, 9999) . '.png';
+        $originalBaseName = pathinfo($imgRelPath, PATHINFO_FILENAME);
+        $fileName = $originalBaseName . '-1.png';
         $savePath = $saveDir . $fileName;
         file_put_contents($savePath, $resultImg);
 
@@ -110,23 +143,89 @@ class WorkOrder extends Api
             'code' => 0,
             'msg' => '图像生成成功',
             'data' => [
-                'origin_url' => '/uploads/img/' . $fileName
+                'origin_url' => '/uploads/img2img/' . $fileName
             ]
         ]);
     }
 
-
-    // 后期图片处理
     /**
-     * 后期图处理
+     * 后期图像处理
      * /sdapi/v1/extra-single-image
      */
     public function extra_image()
     {
+        $imgRelPath = 'uploads/img2img/0828004096727-1.png'; // 图生图结果
+        $imgPath = ROOT_PATH . 'public/' . $imgRelPath;
+
+        if (!file_exists($imgPath)) {
+            return json(['code' => 1, 'msg' => '原图不存在:' . $imgRelPath]);
+        }
+
+        $imgData = file_get_contents($imgPath);
+        $base64Img = base64_encode($imgData);
+
+        $postData = json_encode([
+            'resize_mode' => 0,
+            'show_extras_results' => true,
+            'gfpgan_visibility' => 0,
+            'codeformer_visibility' => 0,
+            'codeformer_weight' => 0,
+            'upscaling_resize' => 2.45,
+            'upscaling_crop' => true,
+            'upscaler_1' => 'R-ESRGAN 4x+ Anime6B',
+            'upscaler_2' => 'None',
+            'extras_upscaler_2_visibility' => 0,
+            'upscale_first' => false,
+            'image' => $base64Img
+        ]);
+
+        $apiUrl = "http://20.0.17.233:45001/sdapi/v1/extra-single-image";
+        $headers = ['Content-Type: application/json'];
+
+        $ch = curl_init();
+        curl_setopt($ch, CURLOPT_URL, $apiUrl);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($ch, CURLOPT_POST, true);
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
+        curl_setopt($ch, CURLOPT_TIMEOUT, 90);
+        $response = curl_exec($ch);
+        $error = curl_error($ch);
+        curl_close($ch);
+
+        if ($error) {
+            return json(['code' => 1, 'msg' => '请求失败:' . $error]);
+        }
+
+        $data = json_decode($response, true);
+        if (!isset($data['image'])) {
+            return json(['code' => 1, 'msg' => '接口未返回图像数据']);
+        }
+
+        // 保存:原图基础名 + -2
+        $resultImg = base64_decode($data['image']);
+        $saveDir = ROOT_PATH . 'public/uploads/extra_image/';
+        if (!is_dir($saveDir)) {
+            mkdir($saveDir, 0755, true);
+        }
+
+        $originalBaseName = pathinfo($imgRelPath, PATHINFO_FILENAME);
+        $fileName = $originalBaseName . '-2.png';
+        $savePath = $saveDir . $fileName;
+        file_put_contents($savePath, $resultImg);
 
+        return json([
+            'code' => 0,
+            'msg' => '图像后处理成功',
+            'data' => [
+                'url' => '/uploads/extra_image/' . $fileName
+            ]
+        ]);
     }
 
 
+
+
     /**
      * 查询队列列表
      * 统计文件对应的队列情况
@@ -138,19 +237,23 @@ class WorkOrder extends Api
             ->where('old_image_file', $params)
             ->order('id desc')
             ->select();
+
+        $result = [];  //初始化变量,避免未定义错误
+
         foreach ($queue_logs as &$log) {
             $taskId = $log['id'];
-            // 从 image_task_log 表统计状态
             $statusCount = Db::name('image_task_log')
                 ->field('status, COUNT(*) as count')
                 ->where('task_id', $taskId)
                 ->where('mod_rq', null)
                 ->group('status')
                 ->select();
+
             $log['已完成数量'] = 0;
             $log['处理中数量'] = 0;
             $log['排队中的数量'] = 0;
             $log['失败数量'] = 0;
+
             foreach ($statusCount as $item) {
                 switch ($item['status']) {
                     case 0:
@@ -167,18 +270,18 @@ class WorkOrder extends Api
                         break;
                 }
             }
-            // ✅ 只保留排队中的数量大于 0 的记录
-            if ($log['排队中的数量'] > 0) {
+
+            if ($log['处理中数量'] > 0) {
                 $result[] = $log;
             }
         }
+
         return json([
             'code' => 0,
-            'msg' => '查询成功',
+            'msg'  => '查询成功',
             'data' => $result,
             'count' => count($result)
         ]);
-
     }
 
     /**
@@ -261,6 +364,7 @@ class WorkOrder extends Api
      */
     public function stopQueueProcesses()
     {
+
         $redis = new \Redis();
         $redis->connect('127.0.0.1', 6379);
         $redis->auth('123456');
@@ -284,18 +388,20 @@ class WorkOrder extends Api
         $redis->del($key_txttotxt);
         $redis->del($key_imgtotxt);
 
-        // 删除数据库中 log = '队列中' 的记录
-        Db::name('image_task_log')
+
+        $counts = Db::name('image_task_log')
             ->where('log', '队列中')
+            ->whereOr('status', 1)
+            ->where('create_time', '>=', date('Y-m-d 00:00:00'))
             ->update([
-                'status' => "",
+                'status' => "-1",
                 'log' => '清空取消队列',
                 'mod_rq' => date('Y-m-d H:i:s')
             ]);
 
         return json([
             'code' => 0,
-            'msg'  => '成功停止队列任务并清除队列日志'
+            'msg'  => '成功停止队列任务'
         ]);
     }
 

+ 14 - 0
application/config.php

@@ -309,4 +309,18 @@ return [
         //API接口地址
         'api_url'               => 'https://api.fastadmin.net',
     ],
+    'mongodb' => [
+        'type'        => '\think\mongo\Connection',
+        'query'       => '\think\mongo\Query',
+        'hostname'    => '20.0.16.79',
+        'hostport'    => 27017,
+        'database'    => 'qiqi',
+        'username'    => '',
+        'password'    => '',
+        'params'      => [],
+        'charset'     => 'utf8mb4',
+        'prefix'      => '',
+        'pk_convert_id' => false,
+        'debug'       => false,
+    ]
 ];

+ 26 - 1
application/database.php

@@ -23,7 +23,7 @@ return [
     // 用户名
     'username'        => Env::get('database.username', 'ai_mesdb'),
     // 密码
-    'password'        => Env::get('database.password', 'a6BLYaEB54a5d22W'),
+    'password'        => Env::get('database.password', 'xAPLXSPXGWDRCtnG'),
 
     // 端口
     'hostport'        => Env::get('database.hostport', '3306'),
@@ -55,6 +55,31 @@ return [
     'datetime_format' => false,
     // 是否需要进行SQL性能分析
     'sql_explain'     => false,
+    // MongoDB数据库配置
+    'mongodb' => [
+        // type 和 query 必须写成这个类名(如果你用的是 think-mongo 1.1)
+        'type'        => '\think\mongo\Connection',
+        'query'       => '\think\mongo\Query',
+
+        // MongoDB 服务器信息
+        'hostname'    => '20.0.16.79',   // 改成你的MongoDB服务器IP
+        'hostport'    => 27017,          // 默认端口
+
+        // 数据库名(⚠️ 必须填写)
+        'database'    => 'qiqi',
+
+        // 账号密码(如果没有就留空)
+        'username'    => '',
+        'password'    => '',
+
+        // 认证源(如果没启用认证就可以留空或删除这行)
+        'params'      => [],
+
+        // 其他配置
+        'pk_convert_id' => false,  // 推荐false,防止ObjectId自动转字符串
+        'debug'          => true,  // 开启调试
+    ],
+
     /**
      * 配置中间表数据库
      **/

+ 36 - 46
application/service/AIGatewayService.php

@@ -7,46 +7,37 @@ class AIGatewayService{
     /**
      * 接口访问配置
      *
-     * 配置说明:
-     * - gpt:用于文本生成或图文识别模型接口(如 chat/completions)
-     * OpenAI GPT 接口配置:支持 chat、图文结合模型(如 gpt-4-vision-preview)
-     *
-     * - dalle:用于文生图(图像生成)模型接口
-     * OpenAI DALL·E 接口配置:用于文生图像(图像生成)
-     *
      * 每个模块包含:
      * - api_key:API 调用密钥(Bearer Token)
      * - api_url:对应功能的服务端地址
      */
     protected $config = [
-        'gpt' => [
+        //图生文
+        'imgtotxt' => [
+            'api_key' => 'sk-scyWT2YPrPOIzralcb6WzCeFdAZvl91rh1JcuuaNMrhEJNDZ',
+            'api_url' => 'https://niubi.zeabur.app/v1/chat/completions'
+        ],
+        //文生文
+        'txttotxt' => [
             'api_key' => 'sk-Bhos1lXTRpZiAAmN06624a219a874eCd91Dc068b902a3e73',
             'api_url' => 'https://one.opengptgod.com/v1/chat/completions'
         ],
-        'dalle' => [
+        //文生图
+        'txttoimg' => [
             'api_key' => 'sk-e0JuPjMntkbgi1BoMjrqyyzMKzAxILkQzyGMSy3xiMupuoWY',
             'api_url' => 'https://niubi.zeabur.app/v1/images/generations'
         ]
     ];
 
     /**
-     * 调用 GPT-4 图文识别模型接口(图生文)
-     *
+     * 图生文
      * @param string $imageUrl 图像 URL,支持公网可访问地址
      * @param string $prompt   对图像的提问内容或提示文本
-     *
-     * 功能说明:
-     * - 使用 OpenAI 的 gpt-4-vision-preview 模型对图片进行图文理解
-     * - 支持图像与文本混合输入,返回图像内容相关的文本输出
-     * - 限制最大 token 数为 1000
-     *
-     * 返回值:
-     * - 返回调用 GPT API 后的响应结果(通常为模型生成的文本)
      */
     public function callGptApi($imageUrl, $prompt)
     {
         $data = [
-            "model" => "gpt-4-vision-preview",
+            "model" => "gemini-2.5-pro-preview-05-06",
             "messages" => [[
                 "role" => "user",
                 "content" => [
@@ -59,21 +50,31 @@ class AIGatewayService{
             ]],
             "max_tokens" => 1000
         ];
-        return $this->callApi($this->config['gpt']['api_url'], $this->config['gpt']['api_key'], $data);
+
+        return $this->callApi($this->config['imgtotxt']['api_url'], $this->config['imgtotxt']['api_key'], $data);
     }
+//    public function callGptApi($imageUrl, $prompt)
+//    {
+//        $data = [
+//             "model" => "gpt-4-vision-preview",
+//            "messages" => [[
+//                "role" => "user",
+//                "content" => [
+//                    ["type" => "text", "text" => $prompt],
+//                    ["type" => "image_url", "image_url" => [
+//                        "url" => $imageUrl,
+//                        "detail" => "auto"
+//                    ]]
+//                ]
+//            ]],
+//            "max_tokens" => 1000
+//        ];
+//        return $this->callApi($this->config['imgtotxt']['api_url'], $this->config['imgtotxt']['api_key'], $data);
+//    }
 
     /**
-     * 调用 GPT 文生文模型接口(文本生成)
-     *
+     * 文生文
      * @param string $prompt 用户输入的文本提示内容
-     *
-     * 功能说明:
-     * - 使用 OpenAI 的 GPT-4 模型,根据用户提供的 prompt 文本生成响应内容
-     * - 支持上下文重置,确保每次调用为独立会话(无历史记忆)
-     * - session_id 设为 null 表示不使用会话追踪
-     *
-     * 返回值:
-     * - 返回调用 GPT 接口后的响应结果(通常为模型生成的文本)
      */
     public function txtGptApi($prompt)
     {
@@ -84,27 +85,16 @@ class AIGatewayService{
             'context_reset' => true
         ];
         return $this->callApi(
-            $this->config['gpt']['api_url'],
-            $this->config['gpt']['api_key'],
+            $this->config['txttotxt']['api_url'],
+            $this->config['txttotxt']['api_key'],
             $data
         );
     }
 
     /**
-     * 调用 DALL·E 图像生成接口(文生图)
-     *
+     * 文生图
      * @param string $prompt          提示文本,用于指导图像生成
      * @param string $selectedOption  模型名称,例如 'dall-e-3' 或其他兼容模型
-     *
-     * 功能说明:
-     * - 根据用户提供的文本提示,通过指定的模型生成图像
-     * - 若选择模型为 'dall-e-3',使用标准质量(standard);其他模型使用高清质量(hd)
-     * - 默认生成 1 张图像,尺寸为 1024x1024,风格为 vivid(生动)
-     * - 返回的图像链接格式为 URL
-     * - 每次调用为独立会话(session_id 为 null,context_reset 为 true)
-     *
-     * 返回值:
-     * - 返回调用 DALL·E 接口后的响应结果(图像 URL)
      */
     public function callDalleApi($prompt,$selectedOption)
     {
@@ -134,7 +124,7 @@ class AIGatewayService{
             ];
         }
 
-        return $this->callApi($this->config['dalle']['api_url'], $this->config['dalle']['api_key'], $data);
+        return $this->callApi($this->config['txttoimg']['api_url'], $this->config['txttoimg']['api_key'], $data);
     }
 
     /**

+ 1 - 0
thinkphp/.gitignore

@@ -2,3 +2,4 @@
 /vendor
 .idea
 .DS_Store
+/.vscode

+ 1 - 1
thinkphp/README.md

@@ -60,7 +60,7 @@ ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
 
 本项目包含的第三方源码和二进制文件之版权信息另行标注。
 
-版权所有Copyright © 2006-2022 by ThinkPHP (http://thinkphp.cn)
+版权所有Copyright © 2006-2024 by ThinkPHP (http://thinkphp.cn)
 
 All rights reserved。
 

+ 1 - 1
thinkphp/base.php

@@ -9,7 +9,7 @@
 // | Author: liu21st <liu21st@gmail.com>
 // +----------------------------------------------------------------------
 
-define('THINK_VERSION', '5.0.25');
+define('THINK_VERSION', '5.0.28');
 define('THINK_START_TIME', microtime(true));
 define('THINK_START_MEM', memory_get_usage());
 define('EXT', '.php');

+ 7 - 0
thinkphp/library/think/Collection.php

@@ -360,6 +360,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
      * @param  mixed $offset 键名
      * @return bool
      */
+    #[\ReturnTypeWillChange]
     public function offsetExists($offset)
     {
         return array_key_exists($offset, $this->items);
@@ -371,6 +372,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
      * @param  mixed $offset 键名
      * @return mixed
      */
+    #[\ReturnTypeWillChange]
     public function offsetGet($offset)
     {
         return $this->items[$offset];
@@ -383,6 +385,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
      * @param  mixed $value  值
      * @return void
      */
+    #[\ReturnTypeWillChange]
     public function offsetSet($offset, $value)
     {
         if (is_null($offset)) {
@@ -398,6 +401,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
      * @param  mixed $offset 键名
      * @return void
      */
+    #[\ReturnTypeWillChange]
     public function offsetUnset($offset)
     {
         unset($this->items[$offset]);
@@ -408,6 +412,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
      * @access public
      * @return int
      */
+    #[\ReturnTypeWillChange]
     public function count()
     {
         return count($this->items);
@@ -418,6 +423,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
      * @access public
      * @return ArrayIterator
      */
+    #[\ReturnTypeWillChange]
     public function getIterator()
     {
         return new ArrayIterator($this->items);
@@ -428,6 +434,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
      * @access public
      * @return array
      */
+    #[\ReturnTypeWillChange]
     public function jsonSerialize()
     {
         return $this->toArray();

+ 19 - 7
thinkphp/library/think/Cookie.php

@@ -45,7 +45,7 @@ class Cookie
 
         self::$config = array_merge(self::$config, array_change_key_case($config));
 
-        if (!empty(self::$config['httponly'])) {
+        if (!empty(self::$config['httponly']) && PHP_SESSION_ACTIVE != session_status()) {
             ini_set('session.cookie_httponly', 1);
         }
 
@@ -101,14 +101,26 @@ class Cookie
         }
 
         $expire = !empty($config['expire']) ?
-        $_SERVER['REQUEST_TIME'] + intval($config['expire']) :
-        0;
+            $_SERVER['REQUEST_TIME'] + intval($config['expire']) :
+            0;
 
         if ($config['setcookie']) {
-            setcookie(
-                $name, $value, $expire, $config['path'], $config['domain'],
-                $config['secure'], $config['httponly']
-            );
+            if (PHP_VERSION_ID >= 70300) {
+                // PHP 7.3+ 支持选项数组
+                setcookie($name, $value, array_merge([
+                    'expires'  => $expire,
+                    'path'     => $config['path'],
+                    'domain'   => $config['domain'],
+                    'secure'   => $config['secure'],
+                    'httponly' => $config['httponly']
+                ], isset($config['samesite']) ? ['samesite' => $config['samesite']] : []));
+            } else {
+                // 旧版本 PHP 使用传统参数方式
+                setcookie(
+                    $name, $value, $expire, $config['path'], $config['domain'],
+                    $config['secure'], $config['httponly']
+                );
+            }
         }
 
         $_COOKIE[$name] = $value;

+ 1 - 1
thinkphp/library/think/Lang.php

@@ -201,7 +201,7 @@ class Lang
         } elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
             // 自动侦测浏览器语言
             preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches);
-            $langSet     = strtolower($matches[1]);
+            $langSet     = strtolower($matches[1] ?? '');
             $acceptLangs = Config::get('header_accept_lang');
 
             if (isset($acceptLangs[$langSet])) {

+ 1 - 1
thinkphp/library/think/Loader.php

@@ -613,7 +613,7 @@ class Loader
         if ($type) {
             $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
                 return strtoupper($match[1]);
-            }, $name);
+            }, $name ?? '');
 
             return $ucfirst ? ucfirst($name) : lcfirst($name);
         }

+ 28 - 3
thinkphp/library/think/Model.php

@@ -537,6 +537,11 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
             list($type, $param) = explode(':', $type, 2);
         }
         switch ($type) {
+            case 'string':
+            case 'bigint':
+                $value = (string) $value;
+                break;
+            case 'int':
             case 'integer':
                 $value = (int) $value;
                 break;
@@ -547,6 +552,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
                     $value = (float) number_format($value, $param, '.', '');
                 }
                 break;
+            case 'bool':
             case 'boolean':
                 $value = (bool) $value;
                 break;
@@ -670,6 +676,11 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
             list($type, $param) = explode(':', $type, 2);
         }
         switch ($type) {
+            case 'string':
+            case 'bigint':
+                $value = (string) $value;
+                break;
+            case 'int':
             case 'integer':
                 $value = (int) $value;
                 break;
@@ -680,6 +691,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
                     $value = (float) number_format($value, $param, '.', '');
                 }
                 break;
+            case 'bool':
             case 'boolean':
                 $value = (bool) $value;
                 break;
@@ -1267,10 +1279,18 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
             $data = $this->data;
         } else {
             $data = array_udiff_assoc($this->data, $this->origin, function ($a, $b) {
+                if (is_numeric($a) && is_numeric($b)) {
+                    if (strcmp($a, $b) !== 0) {
+                        return 1;
+                    }
+                    if ($a == $b) {
+                        return 0;
+                    }
+                }
                 if ((empty($a) || empty($b)) && $a !== $b) {
                     return 1;
                 }
-                return is_object($a) || $a != $b ? 1 : 0;
+                return is_object($a) || $a !== $b ? 1 : 0;
             });
         }
 
@@ -1309,10 +1329,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
     }
 
     /**
-     * 字段值(延迟)增长
+     * 字段值(延迟)减少
      * @access public
      * @param string  $field    字段名
-     * @param integer $step     增长
+     * @param integer $step     减少
      * @param integer $lazyTime 延时时间(s)
      * @return integer|true
      * @throws Exception
@@ -2268,27 +2288,32 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
     }
 
     // JsonSerializable
+    #[\ReturnTypeWillChange]
     public function jsonSerialize()
     {
         return $this->toArray();
     }
 
     // ArrayAccess
+    #[\ReturnTypeWillChange]
     public function offsetSet($name, $value)
     {
         $this->setAttr($name, $value);
     }
 
+    #[\ReturnTypeWillChange]
     public function offsetExists($name)
     {
         return $this->__isset($name);
     }
 
+    #[\ReturnTypeWillChange]
     public function offsetUnset($name)
     {
         $this->__unset($name);
     }
 
+    #[\ReturnTypeWillChange]
     public function offsetGet($name)
     {
         return $this->getAttr($name);

+ 8 - 1
thinkphp/library/think/Paginator.php

@@ -128,7 +128,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
         }
         $url = $path;
         if (!empty($parameters)) {
-            $url .= '?' . http_build_query($parameters, null, '&');
+            $url .= '?' . http_build_query($parameters, '', '&');
         }
         return $url . $this->buildFragment();
     }
@@ -304,6 +304,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
      * @return Traversable An instance of an object implementing <b>Iterator</b> or
      * <b>Traversable</b>
      */
+    #[\ReturnTypeWillChange]
     public function getIterator()
     {
         return new ArrayIterator($this->items->all());
@@ -314,6 +315,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
      * @param mixed $offset
      * @return bool
      */
+    #[\ReturnTypeWillChange]
     public function offsetExists($offset)
     {
         return $this->items->offsetExists($offset);
@@ -324,6 +326,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
      * @param mixed $offset
      * @return mixed
      */
+    #[\ReturnTypeWillChange]
     public function offsetGet($offset)
     {
         return $this->items->offsetGet($offset);
@@ -334,6 +337,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
      * @param mixed $offset
      * @param mixed $value
      */
+    #[\ReturnTypeWillChange]
     public function offsetSet($offset, $value)
     {
         $this->items->offsetSet($offset, $value);
@@ -345,6 +349,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
      * @return void
      * @since 5.0.0
      */
+    #[\ReturnTypeWillChange]
     public function offsetUnset($offset)
     {
         $this->items->offsetUnset($offset);
@@ -353,6 +358,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
     /**
      * Count elements of an object
      */
+    #[\ReturnTypeWillChange]
     public function count()
     {
         return $this->items->count();
@@ -388,6 +394,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
     /**
      * Specify data which should be serialized to JSON
      */
+    #[\ReturnTypeWillChange]
     public function jsonSerialize()
     {
         return $this->toArray();

+ 12 - 7
thinkphp/library/think/Request.php

@@ -521,19 +521,24 @@ class Request
             // 获取原始请求类型
             return $this->server('REQUEST_METHOD') ?: 'GET';
         } elseif (!$this->method) {
-            if (isset($_POST[Config::get('var_method')])) {
-                $method = strtoupper($_POST[Config::get('var_method')]);
+            $varMethod = Config::get('var_method');
+            if ($varMethod && isset($_POST[$varMethod])) {
+                $method = strtoupper($_POST[$varMethod]);
                 if (in_array($method, ['GET', 'POST', 'DELETE', 'PUT', 'PATCH'])) {
                     $this->method = $method;
                     $this->{$this->method}($_POST);
                 } else {
                     $this->method = 'POST';
                 }
-                unset($_POST[Config::get('var_method')]);
-            } elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
-                $this->method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
+                unset($_POST[$varMethod]);
             } else {
-                $this->method = $this->server('REQUEST_METHOD') ?: 'GET';
+                $method = $this->server('REQUEST_METHOD') ?: 'GET';
+                $httpMethodOverride = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ?? '');
+                if ($method === 'POST' && in_array($httpMethodOverride, ['GET', 'POST', 'DELETE', 'PUT', 'PATCH'])) {
+                    $this->method = $httpMethodOverride;
+                } else {
+                    $this->method = $method;
+                }
             }
         }
         return $this->method;
@@ -1091,7 +1096,7 @@ class Request
         foreach ($filters as $filter) {
             if (is_callable($filter)) {
                 // 调用函数或者方法过滤
-                $value = call_user_func($filter, $value);
+                $value = call_user_func($filter, $value ?? '');
             } elseif (is_scalar($value)) {
                 if (false !== strpos($filter, '/')) {
                     // 正则过滤

+ 2 - 2
thinkphp/library/think/Template.php

@@ -925,11 +925,11 @@ class Template
                                     $args[1] = str_replace('###', $name, $args[1]);
                                     $name    = "$fun($args[1])";
                                 } else {
-                                    $name = "$fun($name,$args[1])";
+                                    $name = "$fun($name ?? '',$args[1])";
                                 }
                             } else {
                                 if (!empty($args[0])) {
-                                    $name = "$fun($name)";
+                                    $name = "$fun($name ?? '')";
                                 }
                             }
                         }

+ 25 - 3
thinkphp/library/think/Validate.php

@@ -37,11 +37,13 @@ class Validate
     // 验证规则默认提示信息
     protected static $typeMsg = [
         'require'     => ':attribute require',
+        'must'        => ':attribute must',
         'number'      => ':attribute must be numeric',
         'integer'     => ':attribute must be integer',
         'float'       => ':attribute must be float',
         'boolean'     => ':attribute must be bool',
         'email'       => ':attribute not a valid email address',
+        'mobile'      => ':attribute not a valid mobile',
         'array'       => ':attribute must be a array',
         'accepted'    => ':attribute must be yes,on or 1',
         'date'        => ':attribute not a valid datetime',
@@ -91,6 +93,23 @@ class Validate
     // 当前验证场景
     protected $currentScene = null;
 
+    /**
+     * 内置正则验证规则
+     * @var array
+     */
+    protected $defaultRegex = [
+        'alpha'       => '/^[A-Za-z]+$/',
+        'alphaNum'    => '/^[A-Za-z0-9]+$/',
+        'alphaDash'   => '/^[A-Za-z0-9\-\_]+$/',
+        'chs'         => '/^[\x{4e00}-\x{9fa5}\x{9fa6}-\x{9fef}\x{3400}-\x{4db5}\x{20000}-\x{2ebe0}]+$/u',
+        'chsAlpha'    => '/^[\x{4e00}-\x{9fa5}\x{9fa6}-\x{9fef}\x{3400}-\x{4db5}\x{20000}-\x{2ebe0}a-zA-Z]+$/u',
+        'chsAlphaNum' => '/^[\x{4e00}-\x{9fa5}\x{9fa6}-\x{9fef}\x{3400}-\x{4db5}\x{20000}-\x{2ebe0}a-zA-Z0-9]+$/u',
+        'chsDash'     => '/^[\x{4e00}-\x{9fa5}\x{9fa6}-\x{9fef}\x{3400}-\x{4db5}\x{20000}-\x{2ebe0}a-zA-Z0-9\_\-]+$/u',
+        'mobile'      => '/^1[3-9]\d{9}$/',
+        'idCard'      => '/(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)/',
+        'zip'         => '/\d{6}/',
+    ];
+
     // 正则表达式 regex = ['zip'=>'\d{6}',...]
     protected $regex = [];
 
@@ -931,10 +950,10 @@ class Validate
         if (is_string($rule) && strpos($rule, ',')) {
             list($rule, $param) = explode(',', $rule);
         } elseif (is_array($rule)) {
-            $param = isset($rule[1]) ? $rule[1] : null;
+            $param = $rule[1] ?? 0;
             $rule  = $rule[0];
         } else {
-            $param = null;
+            $param = 0;
         }
         return false !== filter_var($value, is_int($rule) ? $rule : filter_id($rule), $param);
     }
@@ -1226,8 +1245,11 @@ class Validate
     {
         if (isset($this->regex[$rule])) {
             $rule = $this->regex[$rule];
+        } elseif (isset($this->defaultRegex[$rule])) {
+            $rule = $this->defaultRegex[$rule];
         }
-        if (0 !== strpos($rule, '/') && !preg_match('/\/[imsU]{0,4}$/', $rule)) {
+
+        if (is_string($rule) && 0 !== strpos($rule, '/') && !preg_match('/\/[imsU]{0,4}$/', $rule)) {
             // 不是正则表达式则两端补上/
             $rule = '/^' . $rule . '$/';
         }

+ 1 - 1
thinkphp/library/think/console/output/Ask.php

@@ -299,7 +299,7 @@ class Ask
             $width = max(array_map('strlen', array_keys($this->question->getChoices())));
 
             foreach ($this->question->getChoices() as $key => $value) {
-                $this->output->writeln(sprintf("  [<comment>%-${width}s</comment>] %s", $key, $value));
+                $this->output->writeln(sprintf("  [<comment>%-{$width}s</comment>] %s", $key, $value));
             }
         }
 

+ 2 - 2
thinkphp/library/think/console/output/Descriptor.php

@@ -155,7 +155,7 @@ class Descriptor
 
             $this->writeText('<comment>Options:</comment>', $options);
             foreach ($definition->getOptions() as $option) {
-                if (strlen($option->getShortcut()) > 1) {
+                if ($option->getShortcut() && strlen($option->getShortcut()) > 1) {
                     $laterOptions[] = $option;
                     continue;
                 }
@@ -219,7 +219,7 @@ class Descriptor
             $width = $this->getColumnWidth($description->getCommands());
 
             foreach ($description->getCommands() as $command) {
-                $this->writeText(sprintf("%-${width}s %s", $command->getName(), $command->getDescription()), $options);
+                $this->writeText(sprintf("%-{$width}s %s", $command->getName(), $command->getDescription()), $options);
                 $this->writeText("\n");
             }
         } else {

+ 1 - 0
thinkphp/library/think/db/Query.php

@@ -480,6 +480,7 @@ class Query
             $result = Cache::get($guid);
         }
         if (false === $result) {
+            $result = [];
             if (isset($this->options['field'])) {
                 unset($this->options['field']);
             }

+ 1 - 5
thinkphp/library/think/db/builder/Mysql.php

@@ -109,12 +109,8 @@ class Mysql extends Builder
             }
         }
 
-//        if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) {
-//            throw new Exception('not support data:' . $key);
-//        }
-        if ($strict && !preg_match('/^[\w\.\*\x00-\xff]+$/', $key)) {
+        if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) {
             throw new Exception('not support data:' . $key);
-
         }
         if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)`.\s]/', $key))) {
             $key = '`' . $key . '`';

+ 1 - 2
thinkphp/library/think/db/builder/Sqlsrv.php

@@ -13,7 +13,6 @@ namespace think\db\builder;
 
 use think\db\Builder;
 use think\db\Expression;
-use think\Exception;
 
 /**
  * Sqlsrv数据库驱动
@@ -96,7 +95,7 @@ class Sqlsrv extends Builder
             }
         }
 
-        if ($strict && !preg_match('/^[\w\.\*\x7f-\xff]+$/', $key)) {
+        if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) {
             throw new Exception('not support data:' . $key);
         }
         if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)\[.\s]/', $key))) {

+ 1 - 1
thinkphp/library/think/model/relation/HasMany.php

@@ -164,7 +164,7 @@ class HasMany extends Relation
             }
         }
         $localKey = $this->localKey ?: $this->parent->getPk();
-        return $this->query->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $localKey)->fetchSql()->count();
+        return $this->query->alias( 'count_table')->whereExp( 'count_table.' . $this->foreignKey, '=' . $this->parent->getTable() . '.' . $localKey)->fetchSql()->count();
     }
 
     /**

+ 1 - 1
thinkphp/library/think/model/relation/MorphOne.php

@@ -211,7 +211,7 @@ class MorphOne extends Relation
 
         $model = new $this->model();
 
-        return $model->save() ? $model : false;
+        return $model->save($data) ? $model : false;
     }
 
     /**

+ 7 - 1
thinkphp/library/think/session/driver/Redis.php

@@ -42,6 +42,7 @@ class Redis extends SessionHandler
      * @return bool
      * @throws Exception
      */
+    #[\ReturnTypeWillChange]
     public function open($savePath, $sessName)
     {
         // 检测php环境
@@ -69,6 +70,7 @@ class Redis extends SessionHandler
      * 关闭Session
      * @access public
      */
+    #[\ReturnTypeWillChange]
     public function close()
     {
         $this->gc(ini_get('session.gc_maxlifetime'));
@@ -83,6 +85,7 @@ class Redis extends SessionHandler
      * @param string $sessID
      * @return string
      */
+    #[\ReturnTypeWillChange]
     public function read($sessID)
     {
         return (string) $this->handler->get($this->config['session_name'] . $sessID);
@@ -92,9 +95,10 @@ class Redis extends SessionHandler
      * 写入Session
      * @access public
      * @param string $sessID
-     * @param String $sessData
+     * @param string $sessData
      * @return bool
      */
+    #[\ReturnTypeWillChange]
     public function write($sessID, $sessData)
     {
         if ($this->config['expire'] > 0) {
@@ -110,6 +114,7 @@ class Redis extends SessionHandler
      * @param string $sessID
      * @return bool
      */
+    #[\ReturnTypeWillChange]
     public function destroy($sessID)
     {
         return $this->handler->del($this->config['session_name'] . $sessID) > 0;
@@ -121,6 +126,7 @@ class Redis extends SessionHandler
      * @param string $sessMaxLifeTime
      * @return bool
      */
+    #[\ReturnTypeWillChange]
     public function gc($sessMaxLifeTime)
     {
         return true;

+ 2 - 2
thinkphp/library/traits/controller/Jump.php

@@ -38,8 +38,8 @@ trait Jump
     {
         if (is_null($url) && !is_null(Request::instance()->server('HTTP_REFERER'))) {
             $url = Request::instance()->server('HTTP_REFERER');
-        } elseif ('' !== $url && !strpos($url, '://') && 0 !== strpos($url, '/')) {
-            $url = Url::build($url);
+        } elseif ('' !== $url && !strpos($url ?? '', '://') && 0 !== strpos($url ?? '', '/')) {
+            $url = Url::build($url ?? '');
         }
 
         $type = $this->getResponseType();

+ 2 - 2
thinkphp/tpl/think_exception.tpl

@@ -68,7 +68,7 @@
                         break;
                 }
 
-                $result[] = is_int($key) ? $value : "'{$key}' => {$value}";
+                $result[] = is_int($key) ? $value : sprintf('\'%s\' => %s', htmlentities($key), $value);
             }
 
             return implode(', ', $result);
@@ -519,7 +519,7 @@
             var err_line = $('.line-' + LINE, ol[0])[0];
             err_line.className = err_line.className + ' line-error';
 
-            $.getScript('//cdn.bootcss.com/prettify/r298/prettify.min.js', function(){
+            $.getScript('//lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/prettify/r298/prettify.min.js', function(){
                 prettyPrint();
 
                 // 解决Firefox浏览器一个很诡异的问题

+ 1 - 0
vendor/composer/autoload_psr4.php

@@ -6,6 +6,7 @@ $vendorDir = dirname(__DIR__);
 $baseDir = dirname($vendorDir);
 
 return array(
+    'think\\mongo\\' => array($vendorDir . '/topthink/think-mongo/src'),
     'think\\helper\\' => array($vendorDir . '/topthink/think-helper/src'),
     'think\\composer\\' => array($vendorDir . '/topthink/think-installer/src'),
     'think\\captcha\\' => array($vendorDir . '/topthink/think-captcha/src'),

+ 5 - 0
vendor/composer/autoload_static.php

@@ -27,6 +27,7 @@ class ComposerStaticInita171a4a837511915e525c40585d599c6
     public static $prefixLengthsPsr4 = array (
         't' => 
         array (
+            'think\\mongo\\' => 12,
             'think\\helper\\' => 13,
             'think\\composer\\' => 15,
             'think\\captcha\\' => 14,
@@ -108,6 +109,10 @@ class ComposerStaticInita171a4a837511915e525c40585d599c6
     );
 
     public static $prefixDirsPsr4 = array (
+        'think\\mongo\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/topthink/think-mongo/src',
+        ),
         'think\\helper\\' => 
         array (
             0 => __DIR__ . '/..' . '/topthink/think-helper/src',

+ 197 - 152
vendor/composer/installed.json

@@ -117,17 +117,17 @@
         },
         {
             "name": "guzzlehttp/guzzle",
-            "version": "7.9.2",
-            "version_normalized": "7.9.2.0",
+            "version": "7.9.3",
+            "version_normalized": "7.9.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "d281ed313b989f213357e3be1a179f02196ac99b"
+                "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b",
-                "reference": "d281ed313b989f213357e3be1a179f02196ac99b",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77",
+                "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77",
                 "shasum": ""
             },
             "require": {
@@ -154,7 +154,7 @@
                 "ext-intl": "Required for Internationalized Domain Name (IDN) support",
                 "psr/log": "Required for using the Log middleware"
             },
-            "time": "2024-07-24T11:22:20+00:00",
+            "time": "2025-03-27T13:37:11+00:00",
             "type": "library",
             "extra": {
                 "bamarni-bin": {
@@ -226,7 +226,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/guzzle/issues",
-                "source": "https://github.com/guzzle/guzzle/tree/7.9.2"
+                "source": "https://github.com/guzzle/guzzle/tree/7.9.3"
             },
             "funding": [
                 {
@@ -246,17 +246,17 @@
         },
         {
             "name": "guzzlehttp/promises",
-            "version": "2.0.4",
-            "version_normalized": "2.0.4.0",
+            "version": "2.2.0",
+            "version_normalized": "2.2.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/promises.git",
-                "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455"
+                "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455",
-                "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455",
+                "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c",
+                "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c",
                 "shasum": ""
             },
             "require": {
@@ -266,7 +266,7 @@
                 "bamarni/composer-bin-plugin": "^1.8.2",
                 "phpunit/phpunit": "^8.5.39 || ^9.6.20"
             },
-            "time": "2024-10-17T10:06:22+00:00",
+            "time": "2025-03-27T13:27:01+00:00",
             "type": "library",
             "extra": {
                 "bamarni-bin": {
@@ -312,7 +312,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/promises/issues",
-                "source": "https://github.com/guzzle/promises/tree/2.0.4"
+                "source": "https://github.com/guzzle/promises/tree/2.2.0"
             },
             "funding": [
                 {
@@ -332,17 +332,17 @@
         },
         {
             "name": "guzzlehttp/psr7",
-            "version": "2.7.0",
-            "version_normalized": "2.7.0.0",
+            "version": "2.7.1",
+            "version_normalized": "2.7.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/psr7.git",
-                "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201"
+                "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
-                "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16",
+                "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16",
                 "shasum": ""
             },
             "require": {
@@ -363,7 +363,7 @@
             "suggest": {
                 "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
             },
-            "time": "2024-07-18T11:15:46+00:00",
+            "time": "2025-03-27T12:30:47+00:00",
             "type": "library",
             "extra": {
                 "bamarni-bin": {
@@ -431,7 +431,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/psr7/issues",
-                "source": "https://github.com/guzzle/psr7/tree/2.7.0"
+                "source": "https://github.com/guzzle/psr7/tree/2.7.1"
             },
             "funding": [
                 {
@@ -509,32 +509,35 @@
         },
         {
             "name": "maennchen/zipstream-php",
-            "version": "2.1.0",
-            "version_normalized": "2.1.0.0",
+            "version": "2.2.6",
+            "version_normalized": "2.2.6.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/maennchen/ZipStream-PHP.git",
-                "reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58"
+                "reference": "30ad6f93cf3efe4192bc7a4c9cad11ff8f4f237f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/c4c5803cc1f93df3d2448478ef79394a5981cc58",
-                "reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58",
+                "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/30ad6f93cf3efe4192bc7a4c9cad11ff8f4f237f",
+                "reference": "30ad6f93cf3efe4192bc7a4c9cad11ff8f4f237f",
                 "shasum": ""
             },
             "require": {
                 "myclabs/php-enum": "^1.5",
-                "php": ">= 7.1",
+                "php": "^7.4 || ^8.0",
                 "psr/http-message": "^1.0",
                 "symfony/polyfill-mbstring": "^1.0"
             },
             "require-dev": {
                 "ext-zip": "*",
-                "guzzlehttp/guzzle": ">= 6.3",
+                "friendsofphp/php-cs-fixer": "^3.9",
+                "guzzlehttp/guzzle": "^6.5.3 || ^7.2.0",
                 "mikey179/vfsstream": "^1.6",
-                "phpunit/phpunit": ">= 7.5"
+                "php-coveralls/php-coveralls": "^2.4",
+                "phpunit/phpunit": "^8.5.8 || ^9.4.2",
+                "vimeo/psalm": "^4.1"
             },
-            "time": "2020-05-30T13:11:16+00:00",
+            "time": "2022-11-25T18:57:19+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -571,7 +574,7 @@
             ],
             "support": {
                 "issues": "https://github.com/maennchen/ZipStream-PHP/issues",
-                "source": "https://github.com/maennchen/ZipStream-PHP/tree/2.1.0"
+                "source": "https://github.com/maennchen/ZipStream-PHP/tree/2.2.6"
             },
             "funding": [
                 {
@@ -700,17 +703,17 @@
         },
         {
             "name": "monolog/monolog",
-            "version": "2.9.3",
-            "version_normalized": "2.9.3.0",
+            "version": "2.10.0",
+            "version_normalized": "2.10.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/Seldaek/monolog.git",
-                "reference": "a30bfe2e142720dfa990d0a7e573997f5d884215"
+                "reference": "5cf826f2991858b54d5c3809bee745560a1042a7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/a30bfe2e142720dfa990d0a7e573997f5d884215",
-                "reference": "a30bfe2e142720dfa990d0a7e573997f5d884215",
+                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/5cf826f2991858b54d5c3809bee745560a1042a7",
+                "reference": "5cf826f2991858b54d5c3809bee745560a1042a7",
                 "shasum": ""
             },
             "require": {
@@ -756,7 +759,7 @@
                 "rollbar/rollbar": "Allow sending log messages to Rollbar",
                 "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
             },
-            "time": "2024-04-12T20:52:51+00:00",
+            "time": "2024-11-12T12:43:37+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -789,7 +792,7 @@
             ],
             "support": {
                 "issues": "https://github.com/Seldaek/monolog/issues",
-                "source": "https://github.com/Seldaek/monolog/tree/2.9.3"
+                "source": "https://github.com/Seldaek/monolog/tree/2.10.0"
             },
             "funding": [
                 {
@@ -805,17 +808,17 @@
         },
         {
             "name": "mpdf/mpdf",
-            "version": "v8.2.4",
-            "version_normalized": "8.2.4.0",
+            "version": "v8.2.5",
+            "version_normalized": "8.2.5.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/mpdf/mpdf.git",
-                "reference": "9e3ff91606fed11cd58a130eabaaf60e56fdda88"
+                "reference": "e175b05e3e00977b85feb96a8cccb174ac63621f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/mpdf/mpdf/zipball/9e3ff91606fed11cd58a130eabaaf60e56fdda88",
-                "reference": "9e3ff91606fed11cd58a130eabaaf60e56fdda88",
+                "url": "https://api.github.com/repos/mpdf/mpdf/zipball/e175b05e3e00977b85feb96a8cccb174ac63621f",
+                "reference": "e175b05e3e00977b85feb96a8cccb174ac63621f",
                 "shasum": ""
             },
             "require": {
@@ -825,7 +828,7 @@
                 "mpdf/psr-log-aware-trait": "^2.0 || ^3.0",
                 "myclabs/deep-copy": "^1.7",
                 "paragonie/random_compat": "^1.4|^2.0|^9.99.99",
-                "php": "^5.6 || ^7.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0",
+                "php": "^5.6 || ^7.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
                 "psr/http-message": "^1.0 || ^2.0",
                 "psr/log": "^1.0 || ^2.0 || ^3.0",
                 "setasign/fpdi": "^2.1"
@@ -842,7 +845,7 @@
                 "ext-xml": "Needed mainly for SVG manipulation",
                 "ext-zlib": "Needed for compression of embedded resources, such as fonts"
             },
-            "time": "2024-06-14T16:06:41+00:00",
+            "time": "2024-11-18T15:30:42+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -875,7 +878,7 @@
                 "utf-8"
             ],
             "support": {
-                "docs": "http://mpdf.github.io",
+                "docs": "https://mpdf.github.io",
                 "issues": "https://github.com/mpdf/mpdf/issues",
                 "source": "https://github.com/mpdf/mpdf"
             },
@@ -987,17 +990,17 @@
         },
         {
             "name": "myclabs/deep-copy",
-            "version": "1.12.1",
-            "version_normalized": "1.12.1.0",
+            "version": "1.13.1",
+            "version_normalized": "1.13.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/myclabs/DeepCopy.git",
-                "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845"
+                "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845",
-                "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845",
+                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c",
+                "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c",
                 "shasum": ""
             },
             "require": {
@@ -1013,7 +1016,7 @@
                 "phpspec/prophecy": "^1.10",
                 "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
             },
-            "time": "2024-11-08T17:47:46+00:00",
+            "time": "2025-04-29T12:36:36+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -1038,7 +1041,7 @@
             ],
             "support": {
                 "issues": "https://github.com/myclabs/DeepCopy/issues",
-                "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1"
+                "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1"
             },
             "funding": [
                 {
@@ -1050,17 +1053,17 @@
         },
         {
             "name": "myclabs/php-enum",
-            "version": "1.8.4",
-            "version_normalized": "1.8.4.0",
+            "version": "1.8.5",
+            "version_normalized": "1.8.5.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/myclabs/php-enum.git",
-                "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483"
+                "reference": "e7be26966b7398204a234f8673fdad5ac6277802"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/myclabs/php-enum/zipball/a867478eae49c9f59ece437ae7f9506bfaa27483",
-                "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483",
+                "url": "https://api.github.com/repos/myclabs/php-enum/zipball/e7be26966b7398204a234f8673fdad5ac6277802",
+                "reference": "e7be26966b7398204a234f8673fdad5ac6277802",
                 "shasum": ""
             },
             "require": {
@@ -1070,9 +1073,9 @@
             "require-dev": {
                 "phpunit/phpunit": "^9.5",
                 "squizlabs/php_codesniffer": "1.*",
-                "vimeo/psalm": "^4.6.2"
+                "vimeo/psalm": "^4.6.2 || ^5.2"
             },
-            "time": "2022-08-04T09:53:51+00:00",
+            "time": "2025-01-14T11:49:03+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -1094,13 +1097,13 @@
                 }
             ],
             "description": "PHP Enum implementation",
-            "homepage": "http://github.com/myclabs/php-enum",
+            "homepage": "https://github.com/myclabs/php-enum",
             "keywords": [
                 "enum"
             ],
             "support": {
                 "issues": "https://github.com/myclabs/php-enum/issues",
-                "source": "https://github.com/myclabs/php-enum/tree/1.8.4"
+                "source": "https://github.com/myclabs/php-enum/tree/1.8.5"
             },
             "funding": [
                 {
@@ -1602,17 +1605,17 @@
         },
         {
             "name": "predis/predis",
-            "version": "v3.0.0",
-            "version_normalized": "3.0.0.0",
+            "version": "v3.0.1",
+            "version_normalized": "3.0.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/predis/predis.git",
-                "reference": "7d86f7afb37940bfc7aaa2909147616881377667"
+                "reference": "34fb0a7da0330df1bab4280fcac4afdeeccc3edf"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/predis/predis/zipball/7d86f7afb37940bfc7aaa2909147616881377667",
-                "reference": "7d86f7afb37940bfc7aaa2909147616881377667",
+                "url": "https://api.github.com/repos/predis/predis/zipball/34fb0a7da0330df1bab4280fcac4afdeeccc3edf",
+                "reference": "34fb0a7da0330df1bab4280fcac4afdeeccc3edf",
                 "shasum": ""
             },
             "require": {
@@ -1628,7 +1631,7 @@
             "suggest": {
                 "ext-relay": "Faster connection with in-memory caching (>=0.6.2)"
             },
-            "time": "2025-05-02T23:18:59+00:00",
+            "time": "2025-05-16T18:30:32+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -1656,7 +1659,7 @@
             ],
             "support": {
                 "issues": "https://github.com/predis/predis/issues",
-                "source": "https://github.com/predis/predis/tree/v3.0.0"
+                "source": "https://github.com/predis/predis/tree/v3.0.1"
             },
             "funding": [
                 {
@@ -1720,23 +1723,23 @@
         },
         {
             "name": "psr/container",
-            "version": "2.0.1",
-            "version_normalized": "2.0.1.0",
+            "version": "2.0.2",
+            "version_normalized": "2.0.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/container.git",
-                "reference": "2ae37329ee82f91efadc282cc2d527fd6065a5ef"
+                "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/container/zipball/2ae37329ee82f91efadc282cc2d527fd6065a5ef",
-                "reference": "2ae37329ee82f91efadc282cc2d527fd6065a5ef",
+                "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+                "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.2.0"
+                "php": ">=7.4.0"
             },
-            "time": "2021-03-24T13:40:57+00:00",
+            "time": "2021-11-05T16:47:00+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -1770,7 +1773,7 @@
             ],
             "support": {
                 "issues": "https://github.com/php-fig/container/issues",
-                "source": "https://github.com/php-fig/container/tree/2.0.1"
+                "source": "https://github.com/php-fig/container/tree/2.0.2"
             },
             "install-path": "../psr/container"
         },
@@ -2152,37 +2155,37 @@
         },
         {
             "name": "setasign/fpdi",
-            "version": "v2.6.1",
-            "version_normalized": "2.6.1.0",
+            "version": "v2.6.3",
+            "version_normalized": "2.6.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/Setasign/FPDI.git",
-                "reference": "09a816004fcee9ed3405bd164147e3fdbb79a56f"
+                "reference": "67c31f5e50c93c20579ca9e23035d8c540b51941"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Setasign/FPDI/zipball/09a816004fcee9ed3405bd164147e3fdbb79a56f",
-                "reference": "09a816004fcee9ed3405bd164147e3fdbb79a56f",
+                "url": "https://api.github.com/repos/Setasign/FPDI/zipball/67c31f5e50c93c20579ca9e23035d8c540b51941",
+                "reference": "67c31f5e50c93c20579ca9e23035d8c540b51941",
                 "shasum": ""
             },
             "require": {
                 "ext-zlib": "*",
-                "php": "^5.6 || ^7.0 || ^8.0"
+                "php": "^7.1 || ^8.0"
             },
             "conflict": {
                 "setasign/tfpdf": "<1.31"
             },
             "require-dev": {
-                "phpunit/phpunit": "~5.7",
+                "phpunit/phpunit": "^7",
                 "setasign/fpdf": "~1.8.6",
                 "setasign/tfpdf": "~1.33",
                 "squizlabs/php_codesniffer": "^3.5",
-                "tecnickcom/tcpdf": "~6.2"
+                "tecnickcom/tcpdf": "^6.2"
             },
             "suggest": {
                 "setasign/fpdf": "FPDI will extend this class but as it is also possible to use TCPDF or tFPDF as an alternative. There's no fixed dependency configured."
             },
-            "time": "2024-09-02T10:17:15+00:00",
+            "time": "2025-02-05T13:22:35+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -2215,7 +2218,7 @@
             ],
             "support": {
                 "issues": "https://github.com/Setasign/FPDI/issues",
-                "source": "https://github.com/Setasign/FPDI/tree/v2.6.1"
+                "source": "https://github.com/Setasign/FPDI/tree/v2.6.3"
             },
             "funding": [
                 {
@@ -2327,17 +2330,17 @@
         },
         {
             "name": "symfony/cache-contracts",
-            "version": "v2.5.3",
-            "version_normalized": "2.5.3.0",
+            "version": "v2.5.4",
+            "version_normalized": "2.5.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/cache-contracts.git",
-                "reference": "fee6db04d913094e2fb55ff8e7db5685a8134463"
+                "reference": "517c3a3619dadfa6952c4651767fcadffb4df65e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/fee6db04d913094e2fb55ff8e7db5685a8134463",
-                "reference": "fee6db04d913094e2fb55ff8e7db5685a8134463",
+                "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/517c3a3619dadfa6952c4651767fcadffb4df65e",
+                "reference": "517c3a3619dadfa6952c4651767fcadffb4df65e",
                 "shasum": ""
             },
             "require": {
@@ -2347,15 +2350,15 @@
             "suggest": {
                 "symfony/cache-implementation": ""
             },
-            "time": "2024-01-23T13:51:25+00:00",
+            "time": "2024-09-25T14:11:13+00:00",
             "type": "library",
             "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/contracts",
+                    "name": "symfony/contracts"
+                },
                 "branch-alias": {
                     "dev-main": "2.5-dev"
-                },
-                "thanks": {
-                    "name": "symfony/contracts",
-                    "url": "https://github.com/symfony/contracts"
                 }
             },
             "installation-source": "dist",
@@ -2389,7 +2392,7 @@
                 "standards"
             ],
             "support": {
-                "source": "https://github.com/symfony/cache-contracts/tree/v2.5.3"
+                "source": "https://github.com/symfony/cache-contracts/tree/v2.5.4"
             },
             "funding": [
                 {
@@ -2409,31 +2412,31 @@
         },
         {
             "name": "symfony/deprecation-contracts",
-            "version": "v2.5.3",
-            "version_normalized": "2.5.3.0",
+            "version": "v2.5.4",
+            "version_normalized": "2.5.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/deprecation-contracts.git",
-                "reference": "80d075412b557d41002320b96a096ca65aa2c98d"
+                "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/80d075412b557d41002320b96a096ca65aa2c98d",
-                "reference": "80d075412b557d41002320b96a096ca65aa2c98d",
+                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/605389f2a7e5625f273b53960dc46aeaf9c62918",
+                "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918",
                 "shasum": ""
             },
             "require": {
                 "php": ">=7.1"
             },
-            "time": "2023-01-24T14:02:46+00:00",
+            "time": "2024-09-25T14:11:13+00:00",
             "type": "library",
             "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/contracts",
+                    "name": "symfony/contracts"
+                },
                 "branch-alias": {
                     "dev-main": "2.5-dev"
-                },
-                "thanks": {
-                    "name": "symfony/contracts",
-                    "url": "https://github.com/symfony/contracts"
                 }
             },
             "installation-source": "dist",
@@ -2459,7 +2462,7 @@
             "description": "A generic function and convention to trigger deprecation notices",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.3"
+                "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.4"
             },
             "funding": [
                 {
@@ -2567,17 +2570,17 @@
         },
         {
             "name": "symfony/event-dispatcher-contracts",
-            "version": "v2.5.3",
-            "version_normalized": "2.5.3.0",
+            "version": "v2.5.4",
+            "version_normalized": "2.5.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher-contracts.git",
-                "reference": "540f4c73e87fd0c71ca44a6aa305d024ac68cb73"
+                "reference": "e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/540f4c73e87fd0c71ca44a6aa305d024ac68cb73",
-                "reference": "540f4c73e87fd0c71ca44a6aa305d024ac68cb73",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f",
+                "reference": "e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f",
                 "shasum": ""
             },
             "require": {
@@ -2587,15 +2590,15 @@
             "suggest": {
                 "symfony/event-dispatcher-implementation": ""
             },
-            "time": "2024-01-23T13:51:25+00:00",
+            "time": "2024-09-25T14:11:13+00:00",
             "type": "library",
             "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/contracts",
+                    "name": "symfony/contracts"
+                },
                 "branch-alias": {
                     "dev-main": "2.5-dev"
-                },
-                "thanks": {
-                    "name": "symfony/contracts",
-                    "url": "https://github.com/symfony/contracts"
                 }
             },
             "installation-source": "dist",
@@ -2629,7 +2632,7 @@
                 "standards"
             ],
             "support": {
-                "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.3"
+                "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.4"
             },
             "funding": [
                 {
@@ -2715,17 +2718,17 @@
         },
         {
             "name": "symfony/http-foundation",
-            "version": "v5.4.46",
-            "version_normalized": "5.4.46.0",
+            "version": "v5.4.48",
+            "version_normalized": "5.4.48.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-foundation.git",
-                "reference": "168b77c71e6f02d8fc479db78beaf742a37d3cab"
+                "reference": "3f38b8af283b830e1363acd79e5bc3412d055341"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/168b77c71e6f02d8fc479db78beaf742a37d3cab",
-                "reference": "168b77c71e6f02d8fc479db78beaf742a37d3cab",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3f38b8af283b830e1363acd79e5bc3412d055341",
+                "reference": "3f38b8af283b830e1363acd79e5bc3412d055341",
                 "shasum": ""
             },
             "require": {
@@ -2746,7 +2749,7 @@
             "suggest": {
                 "symfony/mime": "To use the file extension guesser"
             },
-            "time": "2024-11-05T15:52:21+00:00",
+            "time": "2024-11-13T18:58:02+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -2774,7 +2777,7 @@
             "description": "Defines an object-oriented layer for the HTTP specification",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/http-foundation/tree/v5.4.46"
+                "source": "https://github.com/symfony/http-foundation/tree/v5.4.48"
             },
             "funding": [
                 {
@@ -2794,20 +2797,21 @@
         },
         {
             "name": "symfony/polyfill-mbstring",
-            "version": "v1.31.0",
-            "version_normalized": "1.31.0.0",
+            "version": "v1.32.0",
+            "version_normalized": "1.32.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-mbstring.git",
-                "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
+                "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
-                "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
+                "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
                 "shasum": ""
             },
             "require": {
+                "ext-iconv": "*",
                 "php": ">=7.2"
             },
             "provide": {
@@ -2816,12 +2820,12 @@
             "suggest": {
                 "ext-mbstring": "For best performance"
             },
-            "time": "2024-09-09T11:45:10+00:00",
+            "time": "2024-12-23T08:48:59+00:00",
             "type": "library",
             "extra": {
                 "thanks": {
-                    "name": "symfony/polyfill",
-                    "url": "https://github.com/symfony/polyfill"
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
                 }
             },
             "installation-source": "dist",
@@ -2857,7 +2861,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
+                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0"
             },
             "funding": [
                 {
@@ -2877,8 +2881,8 @@
         },
         {
             "name": "symfony/polyfill-php73",
-            "version": "v1.31.0",
-            "version_normalized": "1.31.0.0",
+            "version": "v1.32.0",
+            "version_normalized": "1.32.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php73.git",
@@ -2897,8 +2901,8 @@
             "type": "library",
             "extra": {
                 "thanks": {
-                    "name": "symfony/polyfill",
-                    "url": "https://github.com/symfony/polyfill"
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
                 }
             },
             "installation-source": "dist",
@@ -2936,7 +2940,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-php73/tree/v1.31.0"
+                "source": "https://github.com/symfony/polyfill-php73/tree/v1.32.0"
             },
             "funding": [
                 {
@@ -2956,28 +2960,28 @@
         },
         {
             "name": "symfony/polyfill-php80",
-            "version": "v1.31.0",
-            "version_normalized": "1.31.0.0",
+            "version": "v1.32.0",
+            "version_normalized": "1.32.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php80.git",
-                "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8"
+                "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
-                "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+                "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
                 "shasum": ""
             },
             "require": {
                 "php": ">=7.2"
             },
-            "time": "2024-09-09T11:45:10+00:00",
+            "time": "2025-01-02T08:10:11+00:00",
             "type": "library",
             "extra": {
                 "thanks": {
-                    "name": "symfony/polyfill",
-                    "url": "https://github.com/symfony/polyfill"
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
                 }
             },
             "installation-source": "dist",
@@ -3019,7 +3023,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0"
+                "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0"
             },
             "funding": [
                 {
@@ -3276,7 +3280,7 @@
             "source": {
                 "type": "git",
                 "url": "https://gitee.com/fastadminnet/framework.git",
-                "reference": "c859e712f50362d8ee3a7cd2e495af5494847bef"
+                "reference": "4f0a5f5bfe43421f904eba071ad6a58140232507"
             },
             "require": {
                 "php": ">=7.1.0",
@@ -3290,7 +3294,7 @@
                 "phpunit/phpunit": "4.8.*",
                 "sebastian/phpcpd": "2.*"
             },
-            "time": "2024-06-25T09:03:56+00:00",
+            "time": "2025-06-09T10:13:46+00:00",
             "default-branch": true,
             "type": "think-framework",
             "installation-source": "source",
@@ -3451,6 +3455,47 @@
             },
             "install-path": "../topthink/think-installer"
         },
+        {
+            "name": "topthink/think-mongo",
+            "version": "v1.1",
+            "version_normalized": "1.1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/top-think/think-mongo.git",
+                "reference": "04548b89d283a15e4b3fc040cd0b3832efba15c5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/top-think/think-mongo/zipball/04548b89d283a15e4b3fc040cd0b3832efba15c5",
+                "reference": "04548b89d283a15e4b3fc040cd0b3832efba15c5",
+                "shasum": ""
+            },
+            "time": "2016-11-04T02:10:44+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "files": [],
+                "psr-4": {
+                    "think\\mongo\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "liu21st",
+                    "email": "liu21st@gmail.com"
+                }
+            ],
+            "description": "mongodb driver for thinkphp5",
+            "support": {
+                "issues": "https://github.com/top-think/think-mongo/issues",
+                "source": "https://github.com/top-think/think-mongo/tree/master"
+            },
+            "install-path": "../topthink/think-mongo"
+        },
         {
             "name": "topthink/think-queue",
             "version": "v1.1.6",

+ 65 - 56
vendor/composer/installed.php

@@ -3,7 +3,7 @@
         'name' => 'karsonzhang/fastadmin',
         'pretty_version' => 'dev-master',
         'version' => 'dev-master',
-        'reference' => 'b42c05256dd34a2d995f5dd3eb9d8bd1bd20ec4d',
+        'reference' => 'ef9d9750c2aa976f426ef909c62128f3ba38339b',
         'type' => 'project',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -29,27 +29,27 @@
             'dev_requirement' => false,
         ),
         'guzzlehttp/guzzle' => array(
-            'pretty_version' => '7.9.2',
-            'version' => '7.9.2.0',
-            'reference' => 'd281ed313b989f213357e3be1a179f02196ac99b',
+            'pretty_version' => '7.9.3',
+            'version' => '7.9.3.0',
+            'reference' => '7b2f29fe81dc4da0ca0ea7d42107a0845946ea77',
             'type' => 'library',
             'install_path' => __DIR__ . '/../guzzlehttp/guzzle',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'guzzlehttp/promises' => array(
-            'pretty_version' => '2.0.4',
-            'version' => '2.0.4.0',
-            'reference' => 'f9c436286ab2892c7db7be8c8da4ef61ccf7b455',
+            'pretty_version' => '2.2.0',
+            'version' => '2.2.0.0',
+            'reference' => '7c69f28996b0a6920945dd20b3857e499d9ca96c',
             'type' => 'library',
             'install_path' => __DIR__ . '/../guzzlehttp/promises',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'guzzlehttp/psr7' => array(
-            'pretty_version' => '2.7.0',
-            'version' => '2.7.0.0',
-            'reference' => 'a70f5c95fb43bc83f07c9c948baa0dc1829bf201',
+            'pretty_version' => '2.7.1',
+            'version' => '2.7.1.0',
+            'reference' => 'c2270caaabe631b3b44c85f99e5a04bbb8060d16',
             'type' => 'library',
             'install_path' => __DIR__ . '/../guzzlehttp/psr7',
             'aliases' => array(),
@@ -58,7 +58,7 @@
         'karsonzhang/fastadmin' => array(
             'pretty_version' => 'dev-master',
             'version' => 'dev-master',
-            'reference' => 'b42c05256dd34a2d995f5dd3eb9d8bd1bd20ec4d',
+            'reference' => 'ef9d9750c2aa976f426ef909c62128f3ba38339b',
             'type' => 'project',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),
@@ -74,9 +74,9 @@
             'dev_requirement' => false,
         ),
         'maennchen/zipstream-php' => array(
-            'pretty_version' => '2.1.0',
-            'version' => '2.1.0.0',
-            'reference' => 'c4c5803cc1f93df3d2448478ef79394a5981cc58',
+            'pretty_version' => '2.2.6',
+            'version' => '2.2.6.0',
+            'reference' => '30ad6f93cf3efe4192bc7a4c9cad11ff8f4f237f',
             'type' => 'library',
             'install_path' => __DIR__ . '/../maennchen/zipstream-php',
             'aliases' => array(),
@@ -101,18 +101,18 @@
             'dev_requirement' => false,
         ),
         'monolog/monolog' => array(
-            'pretty_version' => '2.9.3',
-            'version' => '2.9.3.0',
-            'reference' => 'a30bfe2e142720dfa990d0a7e573997f5d884215',
+            'pretty_version' => '2.10.0',
+            'version' => '2.10.0.0',
+            'reference' => '5cf826f2991858b54d5c3809bee745560a1042a7',
             'type' => 'library',
             'install_path' => __DIR__ . '/../monolog/monolog',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'mpdf/mpdf' => array(
-            'pretty_version' => 'v8.2.4',
-            'version' => '8.2.4.0',
-            'reference' => '9e3ff91606fed11cd58a130eabaaf60e56fdda88',
+            'pretty_version' => 'v8.2.5',
+            'version' => '8.2.5.0',
+            'reference' => 'e175b05e3e00977b85feb96a8cccb174ac63621f',
             'type' => 'library',
             'install_path' => __DIR__ . '/../mpdf/mpdf',
             'aliases' => array(),
@@ -137,18 +137,18 @@
             'dev_requirement' => false,
         ),
         'myclabs/deep-copy' => array(
-            'pretty_version' => '1.12.1',
-            'version' => '1.12.1.0',
-            'reference' => '123267b2c49fbf30d78a7b2d333f6be754b94845',
+            'pretty_version' => '1.13.1',
+            'version' => '1.13.1.0',
+            'reference' => '1720ddd719e16cf0db4eb1c6eca108031636d46c',
             'type' => 'library',
             'install_path' => __DIR__ . '/../myclabs/deep-copy',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'myclabs/php-enum' => array(
-            'pretty_version' => '1.8.4',
-            'version' => '1.8.4.0',
-            'reference' => 'a867478eae49c9f59ece437ae7f9506bfaa27483',
+            'pretty_version' => '1.8.5',
+            'version' => '1.8.5.0',
+            'reference' => 'e7be26966b7398204a234f8673fdad5ac6277802',
             'type' => 'library',
             'install_path' => __DIR__ . '/../myclabs/php-enum',
             'aliases' => array(),
@@ -218,9 +218,9 @@
             'dev_requirement' => false,
         ),
         'predis/predis' => array(
-            'pretty_version' => 'v3.0.0',
-            'version' => '3.0.0.0',
-            'reference' => '7d86f7afb37940bfc7aaa2909147616881377667',
+            'pretty_version' => 'v3.0.1',
+            'version' => '3.0.1.0',
+            'reference' => '34fb0a7da0330df1bab4280fcac4afdeeccc3edf',
             'type' => 'library',
             'install_path' => __DIR__ . '/../predis/predis',
             'aliases' => array(),
@@ -242,9 +242,9 @@
             ),
         ),
         'psr/container' => array(
-            'pretty_version' => '2.0.1',
-            'version' => '2.0.1.0',
-            'reference' => '2ae37329ee82f91efadc282cc2d527fd6065a5ef',
+            'pretty_version' => '2.0.2',
+            'version' => '2.0.2.0',
+            'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963',
             'type' => 'library',
             'install_path' => __DIR__ . '/../psr/container',
             'aliases' => array(),
@@ -350,9 +350,9 @@
             'dev_requirement' => false,
         ),
         'setasign/fpdi' => array(
-            'pretty_version' => 'v2.6.1',
-            'version' => '2.6.1.0',
-            'reference' => '09a816004fcee9ed3405bd164147e3fdbb79a56f',
+            'pretty_version' => 'v2.6.3',
+            'version' => '2.6.3.0',
+            'reference' => '67c31f5e50c93c20579ca9e23035d8c540b51941',
             'type' => 'library',
             'install_path' => __DIR__ . '/../setasign/fpdi',
             'aliases' => array(),
@@ -368,9 +368,9 @@
             'dev_requirement' => false,
         ),
         'symfony/cache-contracts' => array(
-            'pretty_version' => 'v2.5.3',
-            'version' => '2.5.3.0',
-            'reference' => 'fee6db04d913094e2fb55ff8e7db5685a8134463',
+            'pretty_version' => 'v2.5.4',
+            'version' => '2.5.4.0',
+            'reference' => '517c3a3619dadfa6952c4651767fcadffb4df65e',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/cache-contracts',
             'aliases' => array(),
@@ -383,9 +383,9 @@
             ),
         ),
         'symfony/deprecation-contracts' => array(
-            'pretty_version' => 'v2.5.3',
-            'version' => '2.5.3.0',
-            'reference' => '80d075412b557d41002320b96a096ca65aa2c98d',
+            'pretty_version' => 'v2.5.4',
+            'version' => '2.5.4.0',
+            'reference' => '605389f2a7e5625f273b53960dc46aeaf9c62918',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
             'aliases' => array(),
@@ -401,9 +401,9 @@
             'dev_requirement' => false,
         ),
         'symfony/event-dispatcher-contracts' => array(
-            'pretty_version' => 'v2.5.3',
-            'version' => '2.5.3.0',
-            'reference' => '540f4c73e87fd0c71ca44a6aa305d024ac68cb73',
+            'pretty_version' => 'v2.5.4',
+            'version' => '2.5.4.0',
+            'reference' => 'e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/event-dispatcher-contracts',
             'aliases' => array(),
@@ -425,26 +425,26 @@
             'dev_requirement' => false,
         ),
         'symfony/http-foundation' => array(
-            'pretty_version' => 'v5.4.46',
-            'version' => '5.4.46.0',
-            'reference' => '168b77c71e6f02d8fc479db78beaf742a37d3cab',
+            'pretty_version' => 'v5.4.48',
+            'version' => '5.4.48.0',
+            'reference' => '3f38b8af283b830e1363acd79e5bc3412d055341',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/http-foundation',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'symfony/polyfill-mbstring' => array(
-            'pretty_version' => 'v1.31.0',
-            'version' => '1.31.0.0',
-            'reference' => '85181ba99b2345b0ef10ce42ecac37612d9fd341',
+            'pretty_version' => 'v1.32.0',
+            'version' => '1.32.0.0',
+            'reference' => '6d857f4d76bd4b343eac26d6b539585d2bc56493',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'symfony/polyfill-php73' => array(
-            'pretty_version' => 'v1.31.0',
-            'version' => '1.31.0.0',
+            'pretty_version' => 'v1.32.0',
+            'version' => '1.32.0.0',
             'reference' => '0f68c03565dcaaf25a890667542e8bd75fe7e5bb',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/polyfill-php73',
@@ -452,9 +452,9 @@
             'dev_requirement' => false,
         ),
         'symfony/polyfill-php80' => array(
-            'pretty_version' => 'v1.31.0',
-            'version' => '1.31.0.0',
-            'reference' => '60328e362d4c2c802a54fcbf04f9d3fb892b4cf8',
+            'pretty_version' => 'v1.32.0',
+            'version' => '1.32.0.0',
+            'reference' => '0cc9dd0f17f61d8131e7df6b84bd344899fe2608',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/polyfill-php80',
             'aliases' => array(),
@@ -490,7 +490,7 @@
         'topthink/framework' => array(
             'pretty_version' => 'dev-master',
             'version' => 'dev-master',
-            'reference' => 'c859e712f50362d8ee3a7cd2e495af5494847bef',
+            'reference' => '4f0a5f5bfe43421f904eba071ad6a58140232507',
             'type' => 'think-framework',
             'install_path' => __DIR__ . '/../../thinkphp',
             'aliases' => array(
@@ -525,6 +525,15 @@
             'aliases' => array(),
             'dev_requirement' => false,
         ),
+        'topthink/think-mongo' => array(
+            'pretty_version' => 'v1.1',
+            'version' => '1.1.0.0',
+            'reference' => '04548b89d283a15e4b3fc040cd0b3832efba15c5',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../topthink/think-mongo',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
         'topthink/think-queue' => array(
             'pretty_version' => 'v1.1.6',
             'version' => '1.1.6.0',

+ 2 - 2
vendor/composer/platform_check.php

@@ -4,8 +4,8 @@
 
 $issues = array();
 
-if (!(PHP_VERSION_ID >= 70300)) {
-    $issues[] = 'Your Composer dependencies require a PHP version ">= 7.3.0". You are running ' . PHP_VERSION . '.';
+if (!(PHP_VERSION_ID >= 70400)) {
+    $issues[] = 'Your Composer dependencies require a PHP version ">= 7.4.0". You are running ' . PHP_VERSION . '.';
 }
 
 if ($issues) {

BIN
vendor/composer/tmp-46b11b947a3050d9f527b6bced75ee76~


+ 0 - 0
vendor/composer/tmp-4dddd166370a65ef32d5abc9c6924d21~


+ 0 - 0
vendor/composer/tmp-5f83881c4a8c80bca2bd03733828d4d6~


BIN
vendor/composer/tmp-701e0887138070223d492469d3ca8969~


BIN
vendor/composer/tmp-b6e3054c63db3413b85b6cb63e4bc72c~


+ 0 - 0
vendor/composer/tmp-bffa71a58ddb2af0aff1a32bb52cf051~


+ 8 - 0
vendor/guzzlehttp/guzzle/CHANGELOG.md

@@ -3,6 +3,14 @@
 Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version.
 
 
+## 7.9.3 - 2025-03-27
+
+### Changed
+
+- Remove explicit content-length header for GET requests
+- Improve compatibility with bad servers for boolean cookie values
+
+
 ## 7.9.2 - 2024-07-24
 
 ### Fixed

+ 4 - 0
vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php

@@ -62,6 +62,10 @@ class SetCookie
                             if (is_numeric($value)) {
                                 $data[$search] = (int) $value;
                             }
+                        } elseif ($search === 'Secure' || $search === 'Discard' || $search === 'HttpOnly') {
+                            if ($value) {
+                                $data[$search] = true;
+                            }
                         } else {
                             $data[$search] = $value;
                         }

+ 6 - 6
vendor/guzzlehttp/guzzle/src/Handler/Proxy.php

@@ -17,10 +17,10 @@ class Proxy
      * Sends synchronous requests to a specific handler while sending all other
      * requests to another handler.
      *
-     * @param callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface $default Handler used for normal responses
-     * @param callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface $sync    Handler used for synchronous responses.
+     * @param callable(RequestInterface, array): PromiseInterface $default Handler used for normal responses
+     * @param callable(RequestInterface, array): PromiseInterface $sync    Handler used for synchronous responses.
      *
-     * @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the composed handler.
+     * @return callable(RequestInterface, array): PromiseInterface Returns the composed handler.
      */
     public static function wrapSync(callable $default, callable $sync): callable
     {
@@ -37,10 +37,10 @@ class Proxy
      * performance benefits of curl while still supporting true streaming
      * through the StreamHandler.
      *
-     * @param callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface $default   Handler used for non-streaming responses
-     * @param callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface $streaming Handler used for streaming responses
+     * @param callable(RequestInterface, array): PromiseInterface $default   Handler used for non-streaming responses
+     * @param callable(RequestInterface, array): PromiseInterface $streaming Handler used for streaming responses
      *
-     * @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the composed handler.
+     * @return callable(RequestInterface, array): PromiseInterface Returns the composed handler.
      */
     public static function wrapStreaming(callable $default, callable $streaming): callable
     {

+ 8 - 2
vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php

@@ -53,8 +53,14 @@ class StreamHandler
             $request = $request->withoutHeader('Expect');
 
             // Append a content-length header if body size is zero to match
-            // cURL's behavior.
-            if (0 === $request->getBody()->getSize()) {
+            // the behavior of `CurlHandler`
+            if (
+                (
+                    0 === \strcasecmp('PUT', $request->getMethod())
+                    || 0 === \strcasecmp('POST', $request->getMethod())
+                )
+                && 0 === $request->getBody()->getSize()
+            ) {
                 $request = $request->withHeader('Content-Length', '0');
             }
 

+ 1 - 1
vendor/guzzlehttp/guzzle/src/Pool.php

@@ -86,7 +86,7 @@ class Pool implements PromisorInterface
      * @param ClientInterface $client   Client used to send the requests
      * @param array|\Iterator $requests Requests to send concurrently.
      * @param array           $options  Passes through the options available in
-     *                                  {@see \GuzzleHttp\Pool::__construct}
+     *                                  {@see Pool::__construct}
      *
      * @return array Returns an array containing the response or an exception
      *               in the same order that the requests were sent.

+ 1 - 1
vendor/guzzlehttp/guzzle/src/Utils.php

@@ -79,7 +79,7 @@ final class Utils
      *
      * The returned handler is not wrapped by any default middlewares.
      *
-     * @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the best handler for the given system.
+     * @return callable(\Psr\Http\Message\RequestInterface, array): Promise\PromiseInterface Returns the best handler for the given system.
      *
      * @throws \RuntimeException if no viable Handler is available.
      */

+ 1 - 1
vendor/guzzlehttp/guzzle/src/functions.php

@@ -50,7 +50,7 @@ function debug_resource($value = null)
  *
  * The returned handler is not wrapped by any default middlewares.
  *
- * @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the best handler for the given system.
+ * @return callable(\Psr\Http\Message\RequestInterface, array): Promise\PromiseInterface Returns the best handler for the given system.
  *
  * @throws \RuntimeException if no viable Handler is available.
  *

+ 14 - 0
vendor/guzzlehttp/promises/CHANGELOG.md

@@ -1,6 +1,20 @@
 # CHANGELOG
 
 
+## 2.2.0 - 2025-03-27
+
+### Fixed
+
+- Revert "Allow an empty EachPromise to be resolved by running the queue"
+
+
+## 2.1.0 - 2025-03-27
+
+### Added
+
+- Allow an empty EachPromise to be resolved by running the queue
+
+
 ## 2.0.4 - 2024-10-17
 
 ### Fixed

+ 10 - 0
vendor/guzzlehttp/psr7/CHANGELOG.md

@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 2.7.1 - 2025-03-27
+
+### Fixed
+
+- Fixed uppercase IPv6 addresses in URI
+
+### Changed
+
+- Improve uploaded file error message
+
 ## 2.7.0 - 2024-07-18
 
 ### Added

+ 11 - 11
vendor/guzzlehttp/psr7/src/UploadedFile.php

@@ -11,15 +11,15 @@ use RuntimeException;
 
 class UploadedFile implements UploadedFileInterface
 {
-    private const ERRORS = [
-        UPLOAD_ERR_OK,
-        UPLOAD_ERR_INI_SIZE,
-        UPLOAD_ERR_FORM_SIZE,
-        UPLOAD_ERR_PARTIAL,
-        UPLOAD_ERR_NO_FILE,
-        UPLOAD_ERR_NO_TMP_DIR,
-        UPLOAD_ERR_CANT_WRITE,
-        UPLOAD_ERR_EXTENSION,
+    private const ERROR_MAP = [
+        UPLOAD_ERR_OK => 'UPLOAD_ERR_OK',
+        UPLOAD_ERR_INI_SIZE => 'UPLOAD_ERR_INI_SIZE',
+        UPLOAD_ERR_FORM_SIZE => 'UPLOAD_ERR_FORM_SIZE',
+        UPLOAD_ERR_PARTIAL => 'UPLOAD_ERR_PARTIAL',
+        UPLOAD_ERR_NO_FILE => 'UPLOAD_ERR_NO_FILE',
+        UPLOAD_ERR_NO_TMP_DIR => 'UPLOAD_ERR_NO_TMP_DIR',
+        UPLOAD_ERR_CANT_WRITE => 'UPLOAD_ERR_CANT_WRITE',
+        UPLOAD_ERR_EXTENSION => 'UPLOAD_ERR_EXTENSION',
     ];
 
     /**
@@ -104,7 +104,7 @@ class UploadedFile implements UploadedFileInterface
      */
     private function setError(int $error): void
     {
-        if (false === in_array($error, UploadedFile::ERRORS, true)) {
+        if (!isset(UploadedFile::ERROR_MAP[$error])) {
             throw new InvalidArgumentException(
                 'Invalid error status for UploadedFile'
             );
@@ -137,7 +137,7 @@ class UploadedFile implements UploadedFileInterface
     private function validateActive(): void
     {
         if (false === $this->isOk()) {
-            throw new RuntimeException('Cannot retrieve stream due to upload error');
+            throw new RuntimeException(\sprintf('Cannot retrieve stream due to upload error (%s)', self::ERROR_MAP[$this->error]));
         }
 
         if ($this->isMoved()) {

+ 1 - 1
vendor/guzzlehttp/psr7/src/Uri.php

@@ -107,7 +107,7 @@ class Uri implements UriInterface, \JsonSerializable
     {
         // If IPv6
         $prefix = '';
-        if (preg_match('%^(.*://\[[0-9:a-f]+\])(.*?)$%', $url, $matches)) {
+        if (preg_match('%^(.*://\[[0-9:a-fA-F]+\])(.*?)$%', $url, $matches)) {
             /** @var array{0:string, 1:string, 2:string} $matches */
             $prefix = $matches[1];
             $url = $matches[2];

+ 0 - 1
vendor/maennchen/zipstream-php/.github/FUNDING.yml

@@ -1 +0,0 @@
-open_collective: zipstream

+ 0 - 12
vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE.md

@@ -1,12 +0,0 @@
-# Description of the problem
-
-Please be very descriptive and include as much details as possible.
-
-# Example code
-
-# Informations
-
-* ZipStream-PHP version:
-* PHP version:
-
-Please include any supplemental information you deem relevant to this issue.

+ 0 - 6
vendor/maennchen/zipstream-php/.gitignore

@@ -1,6 +0,0 @@
-clover.xml
-composer.lock
-coverage.clover
-.idea
-phpunit.xml
-vendor

+ 4 - 0
vendor/maennchen/zipstream-php/.phive/phars.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phive xmlns="https://phar.io/phive">
+  <phar name="phpdocumentor" version="^3.3.1" installed="3.3.1" location="./tools/phpdocumentor" copy="false"/>
+</phive>

+ 71 - 0
vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php

@@ -0,0 +1,71 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * PHP-CS-Fixer config for ZipStream-PHP
+ * @author Nicolas CARPi <nico-git@deltablot.email>
+ * @copyright 2022 Nicolas CARPi
+ * @see https://github.com/maennchen/ZipStream-PHP
+ * @license MIT
+ * @package maennchen/ZipStream-PHP
+ */
+
+use PhpCsFixer\Config;
+use PhpCsFixer\Finder;
+
+$finder = Finder::create()
+    ->exclude('.github')
+    ->exclude('.phpdoc')
+    ->exclude('docs')
+    ->exclude('tools')
+    ->exclude('vendor')
+    ->in(__DIR__);
+
+$config = new Config();
+return $config->setRules([
+        '@PER' => true,
+        '@PER:risky' => true,
+        '@PHP81Migration' => true,
+        '@PHPUnit84Migration:risky' => true,
+        'array_syntax' => ['syntax' => 'short'],
+        'class_attributes_separation' => true,
+        'declare_strict_types' => true,
+        'dir_constant' => true,
+        'is_null' => true,
+        'no_homoglyph_names' => true,
+        'no_null_property_initialization' => true,
+        'no_php4_constructor' => true,
+        'no_unused_imports' => true,
+        'no_useless_else' => true,
+        'non_printable_character' => true,
+        'ordered_imports' => true,
+        'ordered_class_elements' => true,
+        'php_unit_construct' => true,
+        'pow_to_exponentiation' => true,
+        'psr_autoloading' => true,
+        'random_api_migration' => true,
+        'return_assignment' => true,
+        'self_accessor' => true,
+        'semicolon_after_instruction' => true,
+        'short_scalar_cast' => true,
+        'simplified_null_return' => true,
+        'single_blank_line_before_namespace' => true,
+        'single_class_element_per_statement' => true,
+        'single_line_comment_style' => true,
+        'single_quote' => true,
+        'space_after_semicolon' => true,
+        'standardize_not_equals' => true,
+        'strict_param' => true,
+        'ternary_operator_spaces' => true,
+        'trailing_comma_in_multiline' => true,
+        'trim_array_spaces' => true,
+        'unary_operator_spaces' => true,
+        'global_namespace_import' => [
+            'import_classes' => true,
+            'import_functions' => true,
+            'import_constants' => true,
+        ],
+    ])
+    ->setFinder($finder)
+    ->setRiskyAllowed(true);

+ 15 - 0
vendor/maennchen/zipstream-php/.phpdoc/template/base.html.twig

@@ -0,0 +1,15 @@
+{% extends 'layout.html.twig' %}
+
+{% set topMenu = {
+    "menu": [
+        { "name": "Guides", "url": "https://maennchen.dev/ZipStream-PHP/guide/index.html"},
+        { "name": "API", "url": "https://maennchen.dev/ZipStream-PHP/classes/ZipStream-ZipStream.html"},
+        { "name": "Issues", "url": "https://github.com/maennchen/ZipStream-PHP/issues"},
+    ],
+    "social": [
+        { "iconClass": "fab fa-github", "url": "https://github.com/maennchen/ZipStream-PHP"},
+        { "iconClass": "fas fa-envelope-open-text", "url": "https://github.com/maennchen/ZipStream-PHP/discussions"},
+        { "iconClass": "fas fa-money-bill", "url": "https://opencollective.com/zipstream"},
+    ]
+}
+%}

+ 1 - 0
vendor/maennchen/zipstream-php/.tool-versions

@@ -0,0 +1 @@
+php 8.1.13

+ 0 - 12
vendor/maennchen/zipstream-php/.travis.yml

@@ -1,12 +0,0 @@
-language: php
-dist: trusty
-sudo: false
-php:
-  - 7.1
-  - 7.2
-  - 7.3
-install: composer install
-script: ./vendor/bin/phpunit --coverage-clover=coverage.clover
-after_script:
-  - wget https://scrutinizer-ci.com/ocular.phar
-  - php ocular.phar code-coverage:upload --format=php-clover coverage.clover

+ 0 - 51
vendor/maennchen/zipstream-php/CHANGELOG.md

@@ -1,51 +0,0 @@
-# CHANGELOG for ZipStream-PHP
-
-All notable changes to this project will be documented in this file.
-
-The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
-and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
-
-## [2.1.0] - 2020-06-01
-### Changed
-- Don't execute ob_flush() when output buffering is not enabled (#152)
-- Fix inconsistent return type on 32-bit systems (#149) Fix #144
-- Use mbstring polyfill (#151)
-- Promote 7zip usage over unzip to avoid UTF-8 issues (#147)
-
-## [2.0.0] - 2020-02-22
-### Breaking change
-- Only the self opened streams will be closed (#139)
-If you were relying on ZipStream to close streams that the library didn't open,
-you'll need to close them yourself now.
-
-### Changed
-- Minor change to data descriptor (#136)
-
-## [1.2.0] - 2019-07-11
-
-### Added
-- Option to flush output buffer after every write (#122)
-
-## [1.1.0] - 2019-04-30
-
-### Fixed
-- Honor last-modified timestamps set via `ZipStream\Option\File::setTime()` (#106)
-- Documentation regarding output of HTTP headers
-- Test warnings with PHPUnit (#109)
-
-### Added
-- Test for FileNotReadableException (#114)
-- Size attribute to File options (#113)
-- Tests on PHP 7.3 (#108)
-
-## [1.0.0] - 2019-04-17
-
-### Breaking changes
-- Mininum PHP version is now 7.1
-- Options are now passed to the ZipStream object via the Option\Archive object. See the wiki for available options and code examples
-
-### Added
-- Add large file support with Zip64 headers
-
-### Changed
-- Major refactoring and code cleanup

+ 0 - 25
vendor/maennchen/zipstream-php/CONTRIBUTING.md

@@ -1,25 +0,0 @@
-# ZipStream Readme for Contributors
-## Code styling
-### Indention
-For spaces are used to indent code. The convention is [K&R](http://en.wikipedia.org/wiki/Indent_style#K&R)
-
-### Comments
-Double Slashes are used for an one line comment.
-
-Classes, Variables, Methods etc:
-
-```php
-/**
- * My comment
- *
- * @myanotation like @param etc.
- */
-```
-
-## Pull requests
-Feel free to submit pull requests.
-
-## Testing
-For every new feature please write a new PHPUnit test.
-
-Before every commit execute `./vendor/bin/phpunit` to check if your changes wrecked something:

+ 26 - 35
vendor/maennchen/zipstream-php/README.md

@@ -1,12 +1,17 @@
 # ZipStream-PHP
 
-[![Build Status](https://travis-ci.org/maennchen/ZipStream-PHP.svg?branch=master)](https://travis-ci.org/maennchen/ZipStream-PHP)
-[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/maennchen/ZipStream-PHP/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/maennchen/ZipStream-PHP/)
-[![Code Coverage](https://scrutinizer-ci.com/g/maennchen/ZipStream-PHP/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/maennchen/ZipStream-PHP/)
+[![Main Branch](https://github.com/maennchen/ZipStream-PHP/actions/workflows/branch_main.yml/badge.svg)](https://github.com/maennchen/ZipStream-PHP/actions/workflows/branch_main.yml)
+[![Coverage Status](https://coveralls.io/repos/github/maennchen/ZipStream-PHP/badge.svg?branch=main)](https://coveralls.io/github/maennchen/ZipStream-PHP?branch=main)
 [![Latest Stable Version](https://poser.pugx.org/maennchen/zipstream-php/v/stable)](https://packagist.org/packages/maennchen/zipstream-php)
 [![Total Downloads](https://poser.pugx.org/maennchen/zipstream-php/downloads)](https://packagist.org/packages/maennchen/zipstream-php)
 [![Financial Contributors on Open Collective](https://opencollective.com/zipstream/all/badge.svg?label=financial+contributors)](https://opencollective.com/zipstream) [![License](https://img.shields.io/github/license/maennchen/zipstream-php.svg)](LICENSE)
 
+## Unstable Branch
+
+The `main` branch is not stable. Please see the
+[releases](https://github.com/maennchen/ZipStream-PHP/releases) for a stable
+version.
+
 ## Overview
 
 A fast and simple streaming zip file downloader for PHP. Using this library will save you from having to write the Zip to disk. You can directly send it to the user, which is much faster. It can work with S3 buckets or any PSR7 Stream.
@@ -21,7 +26,10 @@ Simply add a dependency on maennchen/zipstream-php to your project's composer.js
 composer require maennchen/zipstream-php
 ```
 
-## Usage and options
+## Usage
+
+For detailed instructions, please check the
+[Documentation](https://maennchen.dev/ZipStream-PHP/).
 
 Here's a simple example:
 
@@ -42,39 +50,20 @@ $zip->addFile('hello.txt', 'This is the contents of hello.txt');
 // add a file named 'some_image.jpg' from a local file 'path/to/image.jpg'
 $zip->addFileFromPath('some_image.jpg', 'path/to/image.jpg');
 
-// add a file named 'goodbye.txt' from an open stream resource
-$fp = tmpfile();
-fwrite($fp, 'The quick brown fox jumped over the lazy dog.');
-rewind($fp);
-$zip->addFileFromStream('goodbye.txt', $fp);
-fclose($fp);
-
 // finish the zip stream
 $zip->finish();
 ```
 
-You can also add comments, modify file timestamps, and customize (or
-disable) the HTTP headers. It is also possible to specify the storage method when adding files,
-the current default storage method is 'deflate' i.e files are stored with Compression mode 0x08.
-
-See the [Wiki](https://github.com/maennchen/ZipStream-PHP/wiki) for details.
-
-## Known issue
-
-The native Mac OS archive extraction tool might not open archives in some conditions. A workaround is to disable the Zip64 feature with the option `$opt->setEnableZip64(false)`. This limits the archive to 4 Gb and 64k files but will allow Mac OS users to open them without issue. See #116.
-
-The linux `unzip` utility might not handle properly unicode characters. It is recommended to extract with another tool like [7-zip](https://www.7-zip.org/). See #146.
-
 ## Upgrade to version 2.0.0
 
-* Only the self opened streams will be closed (#139)
-If you were relying on ZipStream to close streams that the library didn't open,
-you'll need to close them yourself now.
+- Only the self opened streams will be closed (#139)
+  If you were relying on ZipStream to close streams that the library didn't open,
+  you'll need to close them yourself now.
 
 ## Upgrade to version 1.0.0
 
-* All options parameters to all function have been moved from an `array` to structured option objects. See [the wiki](https://github.com/maennchen/ZipStream-PHP/wiki/Available-options) for examples.
-* The whole library has been refactored. The minimal PHP requirement has been raised to PHP 7.1.
+- All options parameters to all function have been moved from an `array` to structured option objects. See [the wiki](https://github.com/maennchen/ZipStream-PHP/wiki/Available-options) for examples.
+- The whole library has been refactored. The minimal PHP requirement has been raised to PHP 7.1.
 
 ## Usage with Symfony and S3
 
@@ -82,21 +71,23 @@ You can find example code on [the wiki](https://github.com/maennchen/ZipStream-P
 
 ## Contributing
 
-ZipStream-PHP is a collaborative project. Please take a look at the [CONTRIBUTING.md](CONTRIBUTING.md) file.
+ZipStream-PHP is a collaborative project. Please take a look at the
+[.github/CONTRIBUTING.md](.github/CONTRIBUTING.md) file.
 
 ## About the Authors
 
-* Paul Duncan <pabs@pablotron.org> - https://pablotron.org/
-* Jonatan Männchen <jonatan@maennchen.ch> - https://maennchen.dev
-* Jesse G. Donat <donatj@gmail.com> - https://donatstudios.com
-* Nicolas CARPi <nico-git@deltablot.email> - https://www.deltablot.com
-* Nik Barham <nik@brokencube.co.uk> - https://www.brokencube.co.uk
+- Paul Duncan <pabs@pablotron.org> - https://pablotron.org/
+- Jonatan Männchen <jonatan@maennchen.ch> - https://maennchen.dev
+- Jesse G. Donat <donatj@gmail.com> - https://donatstudios.com
+- Nicolas CARPi <nico-git@deltablot.email> - https://www.deltablot.com
+- Nik Barham <nik@brokencube.co.uk> - https://www.brokencube.co.uk
 
 ## Contributors
 
 ### Code Contributors
 
-This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
+This project exists thanks to all the people who contribute.
+[[Contribute](.github/CONTRIBUTING.md)].
 <a href="https://github.com/maennchen/ZipStream-PHP/graphs/contributors"><img src="https://opencollective.com/zipstream/contributors.svg?width=890&button=false" /></a>
 
 ### Financial Contributors

+ 39 - 4
vendor/maennchen/zipstream-php/composer.json

@@ -22,20 +22,55 @@
     }
   ],
   "require": {
-    "php": ">= 7.1",
+    "php": "^7.4 || ^8.0",
     "symfony/polyfill-mbstring": "^1.0",
     "psr/http-message": "^1.0",
     "myclabs/php-enum": "^1.5"
   },
   "require-dev": {
-    "phpunit/phpunit": ">= 7.5",
-    "guzzlehttp/guzzle": ">= 6.3",
+    "phpunit/phpunit": "^8.5.8 || ^9.4.2",
+    "guzzlehttp/guzzle": "^6.5.3 || ^7.2.0",
     "ext-zip": "*",
-    "mikey179/vfsstream": "^1.6"
+    "mikey179/vfsstream": "^1.6",
+    "vimeo/psalm": "^4.1",
+    "php-coveralls/php-coveralls": "^2.4",
+    "friendsofphp/php-cs-fixer": "^3.9"
+  },
+  "scripts": {
+    "format": "php-cs-fixer fix",
+    "test": "composer run test:unit && composer run test:formatted && composer run test:lint",
+    "test:unit": "phpunit --coverage-clover=coverage.clover.xml",
+    "test:formatted": "composer run format -- --dry-run --stop-on-violation --using-cache=no",
+    "test:lint": "psalm --stats --show-info --find-unused-psalm-suppress",
+    "coverage:report": "php-coveralls --coverage_clover=coverage.clover.xml --json_path=coveralls-upload.json -v",
+    "install:tools": "phive install --trust-gpg-keys 0x67F861C3D889C656",
+    "docs:generate": "tools/phpdocumentor --sourcecode"
   },
   "autoload": {
     "psr-4": {
       "ZipStream\\": "src/"
     }
+  },
+  "archive": {
+    "exclude": [
+      "/composer.lock",
+      "/docs",
+      "/.gitattributes",
+      "/.github",
+      "/.gitignore",
+      "/guides",
+      "/.phive",
+      "/.php-cs-fixer.cache",
+      "/.php-cs-fixer.dist.php",
+      "/.phpdoc",
+      "/phpdoc.dist.xml",
+      "/.phpunit.result.cache",
+      "/phpunit.xml.dist",
+      "/psalm.xml",
+      "/test",
+      "/tools",
+      "/.tool-versions",
+      "/vendor"
+    ]
   }
 }

+ 79 - 0
vendor/maennchen/zipstream-php/guides/ContentLength.rst

@@ -0,0 +1,79 @@
+Adding Content-Length header
+=============
+
+Adding a ``Content-Length`` header for ``ZipStream`` is not trivial since the
+size is not known beforehand.
+
+The following workaround adds an approximated header:
+
+.. code-block:: php
+
+    class Zip
+    {
+        /** @var string */
+        private $name;
+
+        private $files = [];
+
+        public function __construct($name)
+        {
+            $this->name = $name;
+        }
+
+        public function addFile($name, $data)
+        {
+            $this->files[] = ['type' => 'addFile', 'name' => $name, 'data' => $data];
+        }
+
+        public function addFileFromPath($name, $path)
+        {
+            $this->files[] = ['type' => 'addFileFromPath', 'name' => $name, 'path' => $path];
+        }
+
+        public function getEstimate()
+        {
+            $estimate = 22;
+            foreach ($this->files as $file) {
+            $estimate += 76 + 2 * strlen($file['name']);
+            if ($file['type'] === 'addFile') {
+                $estimate += strlen($file['data']);
+            }
+            if ($file['type'] === 'addFileFromPath') {
+                $estimate += filesize($file['path']);
+            }
+            }
+            return $estimate;
+        }
+
+        public function finish()
+        {
+            header('Content-Length: ' . $this->getEstimate());
+            $options = new \ZipStream\Option\Archive();
+            $options->setSendHttpHeaders(true);
+            $options->setEnableZip64(false);
+            $options->setDeflateLevel(-1);
+            $zip = new \ZipStream\ZipStream($this->name, $options);
+
+            $fileOptions = new \ZipStream\Option\File();
+            $fileOptions->setMethod(\ZipStream\Option\Method::STORE());
+            foreach ($this->files as $file) {
+            if ($file['type'] === 'addFile') {
+                $zip->addFile($file['name'], $file['data'], $fileOptions);
+            }
+            if ($file['type'] === 'addFileFromPath') {
+                $zip->addFileFromPath($file['name'], $file['path'], $fileOptions);
+            }
+            }
+            $zip->finish();
+            exit;
+        }
+    }
+
+It only works with the following constraints:
+
+- All file content is known beforehand.
+- Content Deflation is disabled
+
+Thanks to
+`partiellkorrekt <https://github.com/maennchen/ZipStream-PHP/issues/89#issuecomment-1047949274>`_
+for this workaround.

+ 33 - 0
vendor/maennchen/zipstream-php/guides/FlySystem.rst

@@ -0,0 +1,33 @@
+Usage with FlySystem
+===============
+
+For saving or uploading the generated zip, you can use the
+`Flysystem <https://flysystem.thephpleague.com>`_ package, and its many
+adapters.
+
+For that you will need to provide another stream than the ``php://output``
+default one, and pass it to Flysystem ``putStream`` method.
+
+.. code-block:: php
+
+    // Open Stream only once for read and write since it's a memory stream and
+    // the content is lost when closing the stream / opening another one
+    $tempStream = fopen('php://memory', 'w+');
+
+    // Init Options
+    $zipStreamOptions = new Archive();
+    $zipStreamOptions->setOutputStream($tempStream);
+
+    // Create Zip Archive
+    $zipStream = new ZipStream('test.zip', $zipStreamOptions);
+    $zipStream->addFile('test.txt', 'text');
+    $zipStream->finish();
+
+    // Store File (see Flysystem documentation, and all its framework integration)
+    $adapter = new Local(__DIR__.'/path/to/folder'); // Can be any adapter (AWS, Google, Ftp, etc.)
+    $filesystem = new Filesystem($adapter);
+
+    $filesystem->putStream('test.zip', $tempStream)
+
+    // Close Stream
+    fclose($tempStream);

+ 16 - 0
vendor/maennchen/zipstream-php/guides/Nginx.rst

@@ -0,0 +1,16 @@
+Usage with nginx
+=============
+
+If you are using nginx as a webserver, it will try to buffer the response.
+So you'll want to disable this with a custom header:
+
+.. code-block:: php
+    header('X-Accel-Buffering: no');
+    # or with the Response class from Symfony
+    $response->headers->set('X-Accel-Buffering', 'no');
+
+Alternatively, you can tweak the
+`fastcgi cache parameters <https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_buffers>`_
+within nginx config.
+
+See `original issue <https://github.com/maennchen/ZipStream-PHP/issues/77>`_.

+ 61 - 0
vendor/maennchen/zipstream-php/guides/Options.rst

@@ -0,0 +1,61 @@
+Available options
+===============
+
+Here is the full list of options available to you. You can also have a look at
+``src/Option/Archive.php`` file.
+
+First, an instance of ``ZipStream\Option\Archive`` needs to be created, and
+after that you use setters methods to modify the values.
+
+.. code-block:: php
+    use ZipStream\ZipStream;
+    use ZipStream\Option\Archive as ArchiveOptions;
+
+    require_once 'vendor/autoload.php';
+
+    $opt = new ArchiveOptions();
+
+    // Define output stream (argument is of type resource)
+    $opt->setOutputStream($fd);
+
+    // Set the deflate level (default is 6; use -1 to disable it)
+    $opt->setDeflateLevel(6);
+
+    // Add a comment to the zip file
+    $opt->setComment('This is a comment.');
+
+    // Size, in bytes, of the largest file to try and load into memory (used by addFileFromPath()).  Large files may also be compressed differently; see the 'largeFileMethod' option.
+    $opt->setLargeFileSize(30000000);
+
+    // How to handle large files.  Legal values are STORE (the default), or DEFLATE. Store sends the file raw and is significantly faster, while DEFLATE compresses the file and is much, much slower. Note that deflate must compress the file twice and is extremely slow.
+    $opt->setLargeFileMethod(ZipStream\Option\Method::STORE());
+    $opt->setLargeFileMethod(ZipStream\Option\Method::DEFLATE());
+
+    // Send http headers (default is false)
+    $opt->setSendHttpHeaders(false);
+
+    // HTTP Content-Disposition.  Defaults to 'attachment', where FILENAME is the specified filename. Note that this does nothing if you are not sending HTTP headers.
+    $opt->setContentDisposition('attachment');
+
+    // Set the content type (does nothing if you are not sending HTTP headers)
+    $opt->setContentType('application/x-zip');
+
+    // Set the function called for setting headers. Default is the `header()` of PHP
+    $opt->setHttpHeaderCallback('header');
+
+    // Enable streaming files with single read where general purpose bit 3 indicates local file header contain zero values in crc and size fields, these appear only after file contents in data descriptor block. Default is false. Set to true if your input stream is remote (used with addFileFromStream()).
+    $opt->setZeroHeader(false);
+
+    // Enable reading file stat for determining file size. When a 32-bit system reads file size that is over 2 GB, invalid value appears in file size due to integer overflow. Should be disabled on 32-bit systems with method addFileFromPath if any file may exceed 2 GB. In this case file will be read in blocks and correct size will be determined from content. Default is true.
+    $opt->setStatFiles(true);
+
+    // Enable zip64 extension, allowing very large archives (> 4Gb or file count > 64k)
+    // default is true
+    $opt->setEnableZip64(true);
+
+    // Flush output buffer after every write
+    // default is false
+    $opt->setFlushOutput(true);
+
+    // Now that everything is set you can pass the options to the ZipStream instance
+    $zip = new ZipStream('example.zip', $opt);

+ 18 - 0
vendor/maennchen/zipstream-php/guides/PSR7Streams.rst

@@ -0,0 +1,18 @@
+Usage with PSR 7 Streams
+===============
+
+PSR-7 streams are `standardized streams <https://www.php-fig.org/psr/psr-7/>`_.
+
+ZipStream-PHP supports working with these streams with the function
+``addFileFromPsr7Stream``. 
+
+For all parameters of the function see the API documentation.
+
+Example
+---------------
+
+.. code-block:: php
+    
+    $stream = $response->getBody();
+    // add a file named 'streamfile.txt' from the content of the stream
+    $zip->addFileFromPsr7Stream('streamfile.txt', $stream);

+ 33 - 0
vendor/maennchen/zipstream-php/guides/StreamOutput.rst

@@ -0,0 +1,33 @@
+Stream Output
+===============
+
+Stream to S3 Bucket
+---------------
+
+.. code-block:: php
+    use Aws\S3\S3Client;
+    use Aws\Credentials\CredentialProvider;
+    use ZipStream\Option\Archive;
+    use ZipStream\ZipStream;
+
+    $bucket = 'your bucket name';
+    $client = new S3Client([
+        'region' => 'your region',
+        'version' => 'latest',
+        'bucketName' => $bucket,
+        'credentials' => CredentialProvider::defaultProvider(),
+    ]);
+    $client->registerStreamWrapper();
+
+    $zipFile = fopen("s3://$bucket/example.zip", 'w');
+
+    $options = new Archive();
+    $options->setEnableZip64(false);
+    $options->setOutputStream($zipFile);
+
+    $zip = new ZipStream(null, $options);
+    $zip->addFile('file1.txt', 'File1 data');
+    $zip->addFile('file2.txt', 'File2 data');
+    $zip->finish();
+
+    fclose($zipFile);

+ 126 - 0
vendor/maennchen/zipstream-php/guides/Symfony.rst

@@ -0,0 +1,126 @@
+Usage with Symfony
+===============
+
+Overview for using ZipStream in Symfony
+--------
+
+Using ZipStream in Symfony requires use of Symfony's ``StreamedResponse`` when
+used in controller actions.
+
+Wrap your call to the relevant ``ZipStream`` stream method (i.e. ``addFile``,
+``addFileFromPath``, ``addFileFromStream``) in Symfony's ``StreamedResponse``
+function passing in any required arguments for your use case.
+
+Using Symfony's ``StreamedResponse`` will allow Symfony to stream output from
+ZipStream correctly to users' browsers and avoid a corrupted final zip landing
+on the users' end.
+
+Example for using ``ZipStream`` in a controller action to zip stream files
+stored in an AWS S3 bucket by key:
+
+.. code-block:: php
+
+    use Symfony\Component\HttpFoundation\StreamedResponse;
+    use Aws\S3\S3Client;
+    use ZipStream;
+
+    //...
+
+    /**
+    * @Route("/zipstream", name="zipstream")
+    */
+    public function zipStreamAction()
+    {
+        //sample test file on s3
+        $s3keys = array(
+        "ziptestfolder/file1.txt"
+        );
+
+        $s3Client = $this->get('app.amazon.s3'); //s3client service
+        $s3Client->registerStreamWrapper(); //required
+
+        //using StreamedResponse to wrap ZipStream functionality for files on AWS s3.
+        $response = new StreamedResponse(function() use($s3keys, $s3Client)
+        {
+            // Define suitable options for ZipStream Archive.
+            $options = new \ZipStream\Option\Archive();
+            $options->setContentType('application/octet-stream');
+            // this is needed to prevent issues with truncated zip files
+            $options->setZeroHeader(true);
+            $options->setComment('test zip file.');
+
+            //initialise zipstream with output zip filename and options.
+            $zip = new ZipStream\ZipStream('test.zip', $options);
+
+            //loop keys - useful for multiple files
+            foreach ($s3keys as $key) {
+                // Get the file name in S3 key so we can save it to the zip
+                //file using the same name.
+                $fileName = basename($key);
+
+                //concatenate s3path.
+                $bucket = 'bucketname'; //replace with your bucket name or get from parameters file.
+                $s3path = "s3://" . $bucket . "/" . $key;
+
+                //addFileFromStream
+                if ($streamRead = fopen($s3path, 'r')) {
+                $zip->addFileFromStream($fileName, $streamRead);
+                } else {
+                die('Could not open stream for reading');
+                }
+            }
+
+            $zip->finish();
+
+        });
+
+        return $response;
+    }
+
+In the above example, files on AWS S3 are being streamed from S3 to the Symfon
+application via ``fopen`` call when the s3Client has ``registerStreamWrapper``
+applied. This stream is then passed to ``ZipStream`` via the
+``addFileFromStream`` function, which ZipStream then streams as a zip to the
+client browser via Symfony's ``StreamedResponse``. No Zip is created server
+side, which makes this approach a more efficient solution for streaming zips to
+the client browser especially for larger files.
+
+For the above use case you will need to have installed
+`aws/aws-sdk-php-symfony <https://github.com/aws/aws-sdk-php-symfony>`_ to
+support accessing S3 objects in your Symfony web application. This is not
+required for locally stored files on you server you intend to stream via
+``ZipStream``.
+
+See official Symfony documentation for details on
+`Symfony's StreamedResponse <https://symfony.com/doc/current/components/http_foundation.html#streaming-a-response>`_ 
+``Symfony\Component\HttpFoundation\StreamedResponse``.
+
+Note from `S3 documentation <https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/s3-stream-wrapper.html>`_:
+
+    Streams opened in "r" mode only allow data to be read from the stream, and
+    are not seekable by default. This is so that data can be downloaded from
+    Amazon S3 in a truly streaming manner, where previously read bytes do not
+    need to be buffered into memory. If you need a stream to be seekable, you
+    can pass seekable into the stream context options of a function.
+
+Make sure to configure your S3 context correctly!
+
+Uploading a file
+--------
+
+You need to add correct permissions
+(see `#120 <https://github.com/maennchen/ZipStream-PHP/issues/120>`_)
+
+**example code**
+
+
+.. code-block:: php
+
+    $path = "s3://{$adapter->getBucket()}/{$this->getArchivePath()}";
+
+    // the important bit
+    $outputContext = stream_context_create([
+        's3' => ['ACL' => 'public-read'],
+    ]);
+
+    fopen($path, 'w', null, $outputContext);

+ 22 - 0
vendor/maennchen/zipstream-php/guides/Varnish.rst

@@ -0,0 +1,22 @@
+Usage with Varnish
+=============
+
+Serving a big zip with varnish in between can cause random stream close.
+This can be solved by adding attached code to the vcl file.
+
+To avoid the problem, add the following to your varnish config file:
+
+.. code-block::
+    sub vcl_recv {
+        # Varnish can’t intercept the discussion anymore
+        # helps for streaming big zips
+        if (req.url ~ "\.(tar|gz|zip|7z|exe)$") {
+            return (pipe);
+        }
+    }
+    # Varnish can’t intercept the discussion anymore
+    # helps for streaming big zips
+    sub vcl_pipe {
+        set bereq.http.connection = "close";
+        return (pipe);
+    }

+ 85 - 0
vendor/maennchen/zipstream-php/guides/index.rst

@@ -0,0 +1,85 @@
+ZipStream PHP
+=============
+
+A fast and simple streaming zip file downloader for PHP. Using this library will
+save you from having to write the Zip to disk. You can directly send it to the
+user, which is much faster. It can work with S3 buckets or any PSR7 Stream.
+
+.. toctree::
+
+   index
+   Symfony
+   Options
+   StreamOutput
+   FlySystem
+   PSR7Streams
+   Nginx
+   Varnish
+   ContentLength
+
+Installation
+---------------
+
+Simply add a dependency on ``maennchen/zipstream-php`` to your project's
+``composer.json`` file if you use Composer to manage the dependencies of your
+project. Use following command to add the package to your project's dependencies:
+
+.. code-block:: sh
+   composer require maennchen/zipstream-php
+
+Usage Intro
+---------------
+
+Here's a simple example:
+
+.. code-block:: php
+
+   // Autoload the dependencies
+   require 'vendor/autoload.php';
+
+   // enable output of HTTP headers
+   $options = new ZipStream\Option\Archive();
+   $options->setSendHttpHeaders(true);
+
+   // create a new zipstream object
+   $zip = new ZipStream\ZipStream('example.zip', $options);
+
+   // create a file named 'hello.txt'
+   $zip->addFile('hello.txt', 'This is the contents of hello.txt');
+
+   // add a file named 'some_image.jpg' from a local file 'path/to/image.jpg'
+   $zip->addFileFromPath('some_image.jpg', 'path/to/image.jpg');
+
+   // add a file named 'goodbye.txt' from an open stream resource
+   $fp = tmpfile();
+   fwrite($fp, 'The quick brown fox jumped over the lazy dog.');
+   rewind($fp);
+   $zip->addFileFromStream('goodbye.txt', $fp);
+   fclose($fp);
+
+   // finish the zip stream
+   $zip->finish();
+
+You can also add comments, modify file timestamps, and customize (or
+disable) the HTTP headers. It is also possible to specify the storage method
+when adding files, the current default storage method is ``DEFLATE``
+i.e files are stored with Compression mode 0x08.
+
+Known Issues
+---------------
+
+The native Mac OS archive extraction tool prior to macOS 10.15 might not open
+archives in some conditions. A workaround is to disable the Zip64 feature with
+the option ``enableZip64: false``. This limits the archive to 4 Gb and 64k files
+but will allow users on macOS 10.14 and below to open them without issue.
+See `#116 <https://github.com/maennchen/ZipStream-PHP/issues/146>`_.
+
+The linux ``unzip`` utility might not handle properly unicode characters.
+It is recommended to extract with another tool like
+`7-zip <https://www.7-zip.org/>`_.
+See `#146 <https://github.com/maennchen/ZipStream-PHP/issues/146>`_.
+
+It is the responsability of the client code to make sure that files are not
+saved with the same path, as it is not possible for the library to figure it out
+while streaming a zip.
+See `#154 <https://github.com/maennchen/ZipStream-PHP/issues/154>`_.

+ 39 - 0
vendor/maennchen/zipstream-php/phpdoc.dist.xml

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<phpdocumentor
+        configVersion="3"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xmlns="https://www.phpdoc.org"
+        xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/phpDocumentor/phpDocumentor/master/data/xsd/phpdoc.xsd"
+>
+    <title>ZipStream-PHP</title>
+    <paths>
+        <output>docs</output>
+    </paths>
+    <version number="3.0.0">
+        <folder>latest</folder>
+        <api>
+            <source dsn=".">
+                <path>src</path>
+            </source>
+            <output>api</output>
+            <ignore hidden="true" symlinks="true">
+                <path>tests/**/*</path>
+                <path>vendor/**/*</path>
+            </ignore>
+            <extensions>
+                <extension>php</extension>
+            </extensions>
+            <visibility>public</visibility>
+            <default-package-name>ZipStream</default-package-name>
+            <include-source>true</include-source>
+        </api>
+        <guide>
+            <source dsn=".">
+                <path>guides</path>
+            </source>
+            <output>guide</output>
+        </guide>
+    </version>
+    <setting name="guides.enabled" value="true"/>
+    <template name="default" />
+</phpdocumentor>

+ 13 - 16
vendor/maennchen/zipstream-php/phpunit.xml.dist

@@ -1,17 +1,14 @@
-<phpunit bootstrap="test/bootstrap.php">
-    <testsuites>
-        <testsuite name="Application">
-            <directory>test</directory>
-        </testsuite>
-    </testsuites>
-
-    <logging>
-        <log type="coverage-clover" target="clover.xml"/>
-    </logging>
-
-    <filter>
-        <whitelist processUncoveredFilesFromWhitelist="true">
-            <directory suffix=".php">src</directory>
-        </whitelist>
-    </filter>
+<?xml version="1.0"?>
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="test/bootstrap.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
+  <coverage processUncoveredFiles="true">
+    <include>
+      <directory suffix=".php">src</directory>
+    </include>
+  </coverage>
+  <testsuites>
+    <testsuite name="Application">
+      <directory>test</directory>
+    </testsuite>
+  </testsuites>
+  <logging/>
 </phpunit>

+ 0 - 2
vendor/maennchen/zipstream-php/psalm.xml

@@ -1,6 +1,5 @@
 <?xml version="1.0"?>
 <psalm
-    totallyTyped="false"
     resolveFromConfigFile="true"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns="https://getpsalm.org/schema/config"
@@ -34,7 +33,6 @@
         <MissingReturnType errorLevel="info" />
         <MissingPropertyType errorLevel="info" />
         <InvalidDocblock errorLevel="info" />
-        <MisplacedRequiredParam errorLevel="info" />
 
         <PropertyNotSetInConstructor errorLevel="info" />
         <MissingConstructor errorLevel="info" />

+ 21 - 19
vendor/maennchen/zipstream-php/src/Bigint.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace ZipStream;
@@ -22,22 +23,6 @@ class Bigint
         $this->fillBytes($value, 0, 8);
     }
 
-    /**
-     * Fill the bytes field with int
-     *
-     * @param int $value
-     * @param int $start
-     * @param int $count
-     * @return void
-     */
-    protected function fillBytes(int $value, int $start, int $count): void
-    {
-        for ($i = 0; $i < $count; $i++) {
-            $this->bytes[$start + $i] = $i >= PHP_INT_SIZE ? 0 : $value & 0xFF;
-            $value >>= 8;
-        }
-    }
-
     /**
      * Get an instance
      *
@@ -58,7 +43,7 @@ class Bigint
      */
     public static function fromLowHigh(int $low, int $high): self
     {
-        $bigint = new Bigint();
+        $bigint = new self();
         $bigint->fillBytes($low, 0, 4);
         $bigint->fillBytes($high, 4, 4);
         return $bigint;
@@ -108,6 +93,7 @@ class Bigint
     /**
      * Check if is over 32
      *
+     * @psalm-suppress ArgumentTypeCoercion
      * @param bool $force
      * @return bool
      */
@@ -149,7 +135,7 @@ class Bigint
      * @param Bigint $other
      * @return Bigint
      */
-    public function add(Bigint $other): Bigint
+    public function add(self $other): self
     {
         $result = clone $this;
         $overflow = false;
@@ -165,8 +151,24 @@ class Bigint
             }
         }
         if ($overflow) {
-            throw new OverflowException;
+            throw new OverflowException();
         }
         return $result;
     }
+
+    /**
+     * Fill the bytes field with int
+     *
+     * @param int $value
+     * @param int $start
+     * @param int $count
+     * @return void
+     */
+    protected function fillBytes(int $value, int $start, int $count): void
+    {
+        for ($i = 0; $i < $count; $i++) {
+            $this->bytes[$start + $i] = $i >= PHP_INT_SIZE ? 0 : $value & 0xFF;
+            $value >>= 8;
+        }
+    }
 }

+ 2 - 1
vendor/maennchen/zipstream-php/src/DeflateStream.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace ZipStream;
@@ -58,7 +59,7 @@ class DeflateStream extends Stream
             'comment' => $options->getComment(),
             'method' => $options->getMethod(),
             'deflateLevel' => $options->getDeflateLevel(),
-            'time' => $options->getTime()
+            'time' => $options->getTime(),
         ];
         $this->filter = stream_filter_append(
             $this->stream,

+ 1 - 0
vendor/maennchen/zipstream-php/src/Exception.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace ZipStream;

+ 1 - 0
vendor/maennchen/zipstream-php/src/Exception/EncodingException.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace ZipStream\Exception;

+ 1 - 0
vendor/maennchen/zipstream-php/src/Exception/FileNotFoundException.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace ZipStream\Exception;

+ 1 - 0
vendor/maennchen/zipstream-php/src/Exception/FileNotReadableException.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace ZipStream\Exception;

+ 1 - 0
vendor/maennchen/zipstream-php/src/Exception/IncompatibleOptionsException.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace ZipStream\Exception;

+ 1 - 0
vendor/maennchen/zipstream-php/src/Exception/OverflowException.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace ZipStream\Exception;

+ 1 - 0
vendor/maennchen/zipstream-php/src/Exception/StreamNotReadableException.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace ZipStream\Exception;

+ 119 - 113
vendor/maennchen/zipstream-php/src/File.php

@@ -1,10 +1,11 @@
 <?php
+
 declare(strict_types=1);
 
 namespace ZipStream;
 
+use HashContext;
 use Psr\Http\Message\StreamInterface;
-use ZipStream\Exception\EncodingException;
 use ZipStream\Exception\FileNotFoundException;
 use ZipStream\Exception\FileNotReadableException;
 use ZipStream\Exception\OverflowException;
@@ -14,13 +15,15 @@ use ZipStream\Option\Version;
 
 class File
 {
-    const HASH_ALGORITHM = 'crc32b';
+    public const HASH_ALGORITHM = 'crc32b';
+
+    public const BIT_ZERO_HEADER = 0x0008;
+
+    public const BIT_EFS_UTF8 = 0x0800;
 
-    const BIT_ZERO_HEADER = 0x0008;
-    const BIT_EFS_UTF8 = 0x0800;
+    public const COMPUTE = 1;
 
-    const COMPUTE = 1;
-    const SEND = 2;
+    public const SEND = 2;
 
     private const CHUNKED_READ_BLOCK_SIZE = 1048576;
 
@@ -38,6 +41,7 @@ class File
      * @var Bigint
      */
     public $len;
+
     /**
      * @var Bigint
      */
@@ -75,8 +79,9 @@ class File
      * @var resource
      */
     private $deflate;
+
     /**
-     * @var resource
+     * @var HashContext
      */
     private $hash;
 
@@ -160,21 +165,17 @@ class File
             // Sets Bit 11: Language encoding flag (EFS).  If this bit is set,
             // the filename and comment fields for this file
             // MUST be encoded using UTF-8. (see APPENDIX D)
-            if (!mb_check_encoding($name, 'UTF-8') ||
-                !mb_check_encoding($comment, 'UTF-8')) {
-                throw new EncodingException(
-                    'File name and comment should use UTF-8 ' .
-                    'if one of them does not fit into ASCII range.'
-                );
+            if (mb_check_encoding($name, 'UTF-8') &&
+                mb_check_encoding($comment, 'UTF-8')) {
+                $this->bits |= self::BIT_EFS_UTF8;
             }
-            $this->bits |= self::BIT_EFS_UTF8;
         }
 
         if ($this->method->equals(Method::DEFLATE())) {
             $this->version = Version::DEFLATE();
         }
 
-        $force = (boolean)($this->bits & self::BIT_ZERO_HEADER) &&
+        $force = (bool)($this->bits & self::BIT_ZERO_HEADER) &&
             $this->zip->opt->isEnableZip64();
 
         $footer = $this->buildZip64ExtraBlock($force);
@@ -226,6 +227,97 @@ class File
         return str_replace(['\\', ':', '*', '?', '"', '<', '>', '|'], '_', $filename);
     }
 
+    /**
+     * Create and send data descriptor footer for this file.
+     *
+     * @return void
+     */
+    public function addFileFooter(): void
+    {
+        if ($this->bits & self::BIT_ZERO_HEADER) {
+            // compressed and uncompressed size
+            $sizeFormat = 'V';
+            if ($this->zip->opt->isEnableZip64()) {
+                $sizeFormat = 'P';
+            }
+            $fields = [
+                ['V', ZipStream::DATA_DESCRIPTOR_SIGNATURE],
+                ['V', $this->crc],              // CRC32
+                [$sizeFormat, $this->zlen],     // Length of compressed data
+                [$sizeFormat, $this->len],      // Length of original data
+            ];
+
+            $footer = ZipStream::packFields($fields);
+            $this->zip->send($footer);
+        } else {
+            $footer = '';
+        }
+        $this->totalLength = $this->hlen->add($this->zlen)->add(Bigint::init(strlen($footer)));
+        $this->zip->addToCdr($this);
+    }
+
+    public function processStream(StreamInterface $stream): void
+    {
+        $this->zlen = new Bigint();
+        $this->len = new Bigint();
+
+        if ($this->zip->opt->isZeroHeader()) {
+            $this->processStreamWithZeroHeader($stream);
+        } else {
+            $this->processStreamWithComputedHeader($stream);
+        }
+    }
+
+    /**
+     * Send CDR record for specified file.
+     *
+     * @return string
+     */
+    public function getCdrFile(): string
+    {
+        $name = static::filterFilename($this->name);
+
+        // get attributes
+        $comment = $this->opt->getComment();
+
+        // get dos timestamp
+        $time = static::dosTime($this->opt->getTime()->getTimestamp());
+
+        $footer = $this->buildZip64ExtraBlock();
+
+        $fields = [
+            ['V', ZipStream::CDR_FILE_SIGNATURE],   // Central file header signature
+            ['v', ZipStream::ZIP_VERSION_MADE_BY],  // Made by version
+            ['v', $this->version->getValue()],      // Extract by version
+            ['v', $this->bits],                     // General purpose bit flags - data descriptor flag set
+            ['v', $this->method->getValue()],       // Compression method
+            ['V', $time],                           // Timestamp (DOS Format)
+            ['V', $this->crc],                      // CRC32
+            ['V', $this->zlen->getLowFF()],         // Compressed Data Length
+            ['V', $this->len->getLowFF()],          // Original Data Length
+            ['v', strlen($name)],                   // Length of filename
+            ['v', strlen($footer)],                 // Extra data len (see above)
+            ['v', strlen($comment)],                // Length of comment
+            ['v', 0],                               // Disk number
+            ['v', 0],                               // Internal File Attributes
+            ['V', 32],                              // External File Attributes
+            ['V', $this->ofs->getLowFF()],           // Relative offset of local header
+        ];
+
+        // pack fields, then append name and comment
+        $header = ZipStream::packFields($fields);
+
+        return $header . $name . $footer . $comment;
+    }
+
+    /**
+     * @return Bigint
+     */
+    public function getTotalLength(): Bigint
+    {
+        return $this->totalLength;
+    }
+
     /**
      * Convert a UNIX timestamp to a DOS timestamp.
      *
@@ -239,14 +331,14 @@ class File
 
         // set lower-bound on dates
         if ($d['year'] < 1980) {
-            $d = array(
+            $d = [
                 'year' => 1980,
                 'mon' => 1,
                 'mday' => 1,
                 'hours' => 0,
                 'minutes' => 0,
-                'seconds' => 0
-            );
+                'seconds' => 0,
+            ];
         }
 
         // remove extra years from 1980
@@ -264,7 +356,6 @@ class File
 
     protected function buildZip64ExtraBlock(bool $force = false): string
     {
-
         $fields = [];
         if ($this->len->isOver32($force)) {
             $fields[] = ['P', $this->len];          // Length of original data
@@ -291,50 +382,14 @@ class File
             $this->version = Version::ZIP64();
         }
 
-        return ZipStream::packFields($fields);
-    }
-
-    /**
-     * Create and send data descriptor footer for this file.
-     *
-     * @return void
-     */
-
-    public function addFileFooter(): void
-    {
-
-        if ($this->bits & self::BIT_ZERO_HEADER) {
-            // compressed and uncompressed size
-            $sizeFormat = 'V';
-            if ($this->zip->opt->isEnableZip64()) {
-                $sizeFormat = 'P';
-            }
-            $fields = [
-                ['V', ZipStream::DATA_DESCRIPTOR_SIGNATURE],
-                ['V', $this->crc],              // CRC32
-                [$sizeFormat, $this->zlen],     // Length of compressed data
-                [$sizeFormat, $this->len],      // Length of original data
-            ];
-
-            $footer = ZipStream::packFields($fields);
-            $this->zip->send($footer);
-        } else {
-            $footer = '';
+        if ($this->bits & self::BIT_EFS_UTF8) {
+            // Put the tricky entry to
+            // force Linux unzip to lookup EFS flag.
+            $fields[] = ['v', 0x5653];  // Choose 'ZS' for proprietary usage
+            $fields[] = ['v', 0x0000];  // zero length
         }
-        $this->totalLength = $this->hlen->add($this->zlen)->add(Bigint::init(strlen($footer)));
-        $this->zip->addToCdr($this);
-    }
-
-    public function processStream(StreamInterface $stream): void
-    {
-        $this->zlen = new Bigint();
-        $this->len = new Bigint();
 
-        if ($this->zip->opt->isZeroHeader()) {
-            $this->processStreamWithZeroHeader($stream);
-        } else {
-            $this->processStreamWithComputedHeader($stream);
-        }
+        return ZipStream::packFields($fields);
     }
 
     protected function processStreamWithZeroHeader(StreamInterface $stream): void
@@ -354,7 +409,7 @@ class File
             $data = $stream->read(self::CHUNKED_READ_BLOCK_SIZE);
             $total += strlen($data);
             if ($size > 0 && $total > $size) {
-                $data = substr($data, 0 , strlen($data)-($total - $size));
+                $data = substr($data, 0, strlen($data)-($total - $size));
             }
             $this->deflateData($stream, $data, $options);
             if ($options & self::SEND) {
@@ -366,7 +421,8 @@ class File
 
     protected function deflateInit(): void
     {
-        $this->hash = hash_init(self::HASH_ALGORITHM);
+        $hash = hash_init(self::HASH_ALGORITHM);
+        $this->hash = $hash;
         if ($this->method->equals(Method::DEFLATE())) {
             $this->deflate = deflate_init(
                 ZLIB_ENCODING_RAW,
@@ -424,54 +480,4 @@ class File
         $this->readStream($stream, self::SEND);
         $this->addFileFooter();
     }
-
-    /**
-     * Send CDR record for specified file.
-     *
-     * @return string
-     */
-    public function getCdrFile(): string
-    {
-        $name = static::filterFilename($this->name);
-
-        // get attributes
-        $comment = $this->opt->getComment();
-
-        // get dos timestamp
-        $time = static::dosTime($this->opt->getTime()->getTimestamp());
-
-        $footer = $this->buildZip64ExtraBlock();
-
-        $fields = [
-            ['V', ZipStream::CDR_FILE_SIGNATURE],   // Central file header signature
-            ['v', ZipStream::ZIP_VERSION_MADE_BY],  // Made by version
-            ['v', $this->version->getValue()],      // Extract by version
-            ['v', $this->bits],                     // General purpose bit flags - data descriptor flag set
-            ['v', $this->method->getValue()],       // Compression method
-            ['V', $time],                           // Timestamp (DOS Format)
-            ['V', $this->crc],                      // CRC32
-            ['V', $this->zlen->getLowFF()],         // Compressed Data Length
-            ['V', $this->len->getLowFF()],          // Original Data Length
-            ['v', strlen($name)],                   // Length of filename
-            ['v', strlen($footer)],                 // Extra data len (see above)
-            ['v', strlen($comment)],                // Length of comment
-            ['v', 0],                               // Disk number
-            ['v', 0],                               // Internal File Attributes
-            ['V', 32],                              // External File Attributes
-            ['V', $this->ofs->getLowFF()]           // Relative offset of local header
-        ];
-
-        // pack fields, then append name and comment
-        $header = ZipStream::packFields($fields);
-
-        return $header . $name . $footer . $comment;
-    }
-
-    /**
-     * @return Bigint
-     */
-    public function getTotalLength(): Bigint
-    {
-        return $this->totalLength;
-    }
 }

+ 21 - 6
vendor/maennchen/zipstream-php/src/Option/Archive.php

@@ -1,15 +1,20 @@
 <?php
+
 declare(strict_types=1);
 
 namespace ZipStream\Option;
 
+use Psr\Http\Message\StreamInterface;
+
 final class Archive
 {
-    const DEFAULT_DEFLATE_LEVEL = 6;
+    public const DEFAULT_DEFLATE_LEVEL = 6;
+
     /**
      * @var string
      */
     private $comment = '';
+
     /**
      * Size, in bytes, of the largest file to try
      * and load into memory (used by
@@ -20,6 +25,7 @@ final class Archive
      * @var int
      */
     private $largeFileSize = 20 * 1024 * 1024;
+
     /**
      * How to handle large files.  Legal values are
      * Method::STORE() (the default), or
@@ -32,6 +38,7 @@ final class Archive
      * @var Method
      */
     private $largeFileMethod;
+
     /**
      * Boolean indicating whether or not to send
      * the HTTP headers for this file.
@@ -39,12 +46,14 @@ final class Archive
      * @var bool
      */
     private $sendHttpHeaders = false;
+
     /**
      * The method called to send headers
      *
      * @var Callable
      */
     private $httpHeaderCallback = 'header';
+
     /**
      * Enable Zip64 extension, supporting very large
      * archives (any size > 4 GB or file count > 64k)
@@ -52,6 +61,7 @@ final class Archive
      * @var bool
      */
     private $enableZip64 = true;
+
     /**
      * Enable streaming files with single read where
      * general purpose bit 3 indicates local file header
@@ -62,6 +72,7 @@ final class Archive
      * @var bool
      */
     private $zeroHeader = false;
+
     /**
      * Enable reading file stat for determining file size.
      * When a 32-bit system reads file size that is
@@ -75,11 +86,13 @@ final class Archive
      * @var bool
      */
     private $statFiles = true;
+
     /**
      * Enable flush after every write to output stream.
      * @var bool
      */
     private $flushOutput = false;
+
     /**
      * HTTP Content-Disposition.  Defaults to
      * 'attachment', where
@@ -91,6 +104,7 @@ final class Archive
      * @var string
      */
     private $contentDisposition = 'attachment';
+
     /**
      * Note that this does nothing if you are
      * not sending HTTP headers.
@@ -98,13 +112,14 @@ final class Archive
      * @var string
      */
     private $contentType = 'application/x-zip';
+
     /**
      * @var int
      */
     private $deflateLevel = 6;
 
     /**
-     * @var resource
+     * @var StreamInterface|resource
      */
     private $outputStream;
 
@@ -157,12 +172,12 @@ final class Archive
         $this->sendHttpHeaders = $sendHttpHeaders;
     }
 
-    public function getHttpHeaderCallback(): Callable
+    public function getHttpHeaderCallback(): callable
     {
         return $this->httpHeaderCallback;
     }
 
-    public function setHttpHeaderCallback(Callable $httpHeaderCallback): void
+    public function setHttpHeaderCallback(callable $httpHeaderCallback): void
     {
         $this->httpHeaderCallback = $httpHeaderCallback;
     }
@@ -228,7 +243,7 @@ final class Archive
     }
 
     /**
-     * @return resource
+     * @return StreamInterface|resource
      */
     public function getOutputStream()
     {
@@ -236,7 +251,7 @@ final class Archive
     }
 
     /**
-     * @param resource $outputStream
+     * @param StreamInterface|resource $outputStream
      */
     public function setOutputStream($outputStream): void
     {

+ 11 - 5
vendor/maennchen/zipstream-php/src/Option/File.php

@@ -1,9 +1,11 @@
 <?php
+
 declare(strict_types=1);
 
 namespace ZipStream\Option;
 
 use DateTime;
+use DateTimeInterface;
 
 final class File
 {
@@ -11,18 +13,22 @@ final class File
      * @var string
      */
     private $comment = '';
+
     /**
      * @var Method
      */
     private $method;
+
     /**
      * @var int
      */
     private $deflateLevel;
+
     /**
-     * @var DateTime
+     * @var DateTimeInterface
      */
     private $time;
+
     /**
      * @var int
      */
@@ -83,17 +89,17 @@ final class File
     }
 
     /**
-     * @return DateTime
+     * @return DateTimeInterface
      */
-    public function getTime(): DateTime
+    public function getTime(): DateTimeInterface
     {
         return $this->time;
     }
 
     /**
-     * @param DateTime $time
+     * @param DateTimeInterface $time
      */
-    public function setTime(DateTime $time): void
+    public function setTime(DateTimeInterface $time): void
     {
         $this->time = $time;
     }

+ 4 - 2
vendor/maennchen/zipstream-php/src/Option/Method.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace ZipStream\Option;
@@ -14,6 +15,7 @@ use MyCLabs\Enum\Enum;
  */
 class Method extends Enum
 {
-    const STORE = 0x00;
-    const DEFLATE = 0x08;
+    public const STORE = 0x00;
+
+    public const DEFLATE = 0x08;
 }

+ 6 - 3
vendor/maennchen/zipstream-php/src/Option/Version.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace ZipStream\Option;
@@ -16,7 +17,9 @@ use MyCLabs\Enum\Enum;
  */
 class Version extends Enum
 {
-    const STORE = 0x000A; // 1.00
-    const DEFLATE = 0x0014; // 2.00
-    const ZIP64 = 0x002D; // 4.50
+    public const STORE = 0x000A; // 1.00
+
+    public const DEFLATE = 0x0014; // 2.00
+
+    public const ZIP64 = 0x002D; // 4.50
 }

+ 52 - 40
vendor/maennchen/zipstream-php/src/Stream.php

@@ -1,8 +1,11 @@
 <?php
+
 declare(strict_types=1);
 
 namespace ZipStream;
 
+use function mb_strlen;
+
 use Psr\Http\Message\StreamInterface;
 use RuntimeException;
 
@@ -22,6 +25,29 @@ class Stream implements StreamInterface
         $this->stream = $stream;
     }
 
+    /**
+     * Reads all data from the stream into a string, from the beginning to end.
+     *
+     * This method MUST attempt to seek to the beginning of the stream before
+     * reading data and read the stream until the end is reached.
+     *
+     * Warning: This could attempt to load a large amount of data into memory.
+     *
+     * This method MUST NOT raise an exception in order to conform with PHP's
+     * string casting operations.
+     *
+     * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
+     * @return string
+     */
+    public function __toString(): string
+    {
+        try {
+            $this->seek(0);
+        } catch (RuntimeException $e) {
+        }
+        return (string) stream_get_contents($this->stream);
+    }
+
     /**
      * Closes the stream and any underlying resources.
      *
@@ -49,28 +75,6 @@ class Stream implements StreamInterface
         return $result;
     }
 
-    /**
-     * Reads all data from the stream into a string, from the beginning to end.
-     *
-     * This method MUST attempt to seek to the beginning of the stream before
-     * reading data and read the stream until the end is reached.
-     *
-     * Warning: This could attempt to load a large amount of data into memory.
-     *
-     * This method MUST NOT raise an exception in order to conform with PHP's
-     * string casting operations.
-     *
-     * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
-     * @return string
-     */
-    public function __toString(): string
-    {
-        try {
-            $this->seek(0);
-        } catch (\RuntimeException $e) {}
-        return (string) stream_get_contents($this->stream);
-    }
-
     /**
      * Seek to a position in the stream.
      *
@@ -81,15 +85,15 @@ class Stream implements StreamInterface
      *     PHP $whence values for `fseek()`.  SEEK_SET: Set position equal to
      *     offset bytes SEEK_CUR: Set position to current location plus offset
      *     SEEK_END: Set position to end-of-stream plus offset.
-     * @throws \RuntimeException on failure.
+     * @throws RuntimeException on failure.
      */
     public function seek($offset, $whence = SEEK_SET): void
     {
         if (!$this->isSeekable()) {
-            throw new RuntimeException;
+            throw new RuntimeException();
         }
         if (fseek($this->stream, $offset, $whence) !== 0) {
-            throw new RuntimeException;
+            throw new RuntimeException();
         }
     }
 
@@ -136,13 +140,13 @@ class Stream implements StreamInterface
      * Returns the current position of the file read/write pointer
      *
      * @return int Position of the file pointer
-     * @throws \RuntimeException on error.
+     * @throws RuntimeException on error.
      */
     public function tell(): int
     {
         $position = ftell($this->stream);
         if ($position === false) {
-            throw new RuntimeException;
+            throw new RuntimeException();
         }
         return $position;
     }
@@ -165,7 +169,7 @@ class Stream implements StreamInterface
      *
      * @see seek()
      * @link http://www.php.net/manual/en/function.fseek.php
-     * @throws \RuntimeException on failure.
+     * @throws RuntimeException on failure.
      */
     public function rewind(): void
     {
@@ -177,17 +181,17 @@ class Stream implements StreamInterface
      *
      * @param string $string The string that is to be written.
      * @return int Returns the number of bytes written to the stream.
-     * @throws \RuntimeException on failure.
+     * @throws RuntimeException on failure.
      */
     public function write($string): int
     {
         if (!$this->isWritable()) {
-            throw new RuntimeException;
+            throw new RuntimeException();
         }
         if (fwrite($this->stream, $string) === false) {
-            throw new RuntimeException;
+            throw new RuntimeException();
         }
-        return \mb_strlen($string);
+        return mb_strlen($string);
     }
 
     /**
@@ -197,7 +201,11 @@ class Stream implements StreamInterface
      */
     public function isWritable(): bool
     {
-        return preg_match('/[waxc+]/', $this->getMetadata('mode')) === 1;
+        $mode = $this->getMetadata('mode');
+        if (!is_string($mode)) {
+            throw new RuntimeException('Could not get stream mode from metadata!');
+        }
+        return preg_match('/[waxc+]/', $mode) === 1;
     }
 
     /**
@@ -208,16 +216,16 @@ class Stream implements StreamInterface
      *     call returns fewer bytes.
      * @return string Returns the data read from the stream, or an empty string
      *     if no bytes are available.
-     * @throws \RuntimeException if an error occurs.
+     * @throws RuntimeException if an error occurs.
      */
     public function read($length): string
     {
         if (!$this->isReadable()) {
-            throw new RuntimeException;
+            throw new RuntimeException();
         }
         $result = fread($this->stream, $length);
         if ($result === false) {
-            throw new RuntimeException;
+            throw new RuntimeException();
         }
         return $result;
     }
@@ -229,24 +237,28 @@ class Stream implements StreamInterface
      */
     public function isReadable(): bool
     {
-        return preg_match('/[r+]/', $this->getMetadata('mode')) === 1;
+        $mode = $this->getMetadata('mode');
+        if (!is_string($mode)) {
+            throw new RuntimeException('Could not get stream mode from metadata!');
+        }
+        return preg_match('/[r+]/', $mode) === 1;
     }
 
     /**
      * Returns the remaining contents in a string
      *
      * @return string
-     * @throws \RuntimeException if unable to read or an error occurs while
+     * @throws RuntimeException if unable to read or an error occurs while
      *     reading.
      */
     public function getContents(): string
     {
         if (!$this->isReadable()) {
-            throw new RuntimeException;
+            throw new RuntimeException();
         }
         $result = stream_get_contents($this->stream);
         if ($result === false) {
-            throw new RuntimeException;
+            throw new RuntimeException();
         }
         return $result;
     }

+ 82 - 73
vendor/maennchen/zipstream-php/src/ZipStream.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace ZipStream;
@@ -80,19 +81,24 @@ class ZipStream
      * to prevent file permissions issues upon extract (see #84)
      * 0x603 is 00000110 00000011 in binary, so 6 and 3
      */
-    const ZIP_VERSION_MADE_BY = 0x603;
+    public const ZIP_VERSION_MADE_BY = 0x603;
 
     /**
-     * The following signatures end with 0x4b50, which in ASCII is PK,
+     * The following signatures end with 0x4b50, which in ASCII is PK,
      * the initials of the inventor Phil Katz.
      * See https://en.wikipedia.org/wiki/Zip_(file_format)#File_headers
      */
-    const FILE_HEADER_SIGNATURE = 0x04034b50;
-    const CDR_FILE_SIGNATURE = 0x02014b50;
-    const CDR_EOF_SIGNATURE = 0x06054b50;
-    const DATA_DESCRIPTOR_SIGNATURE = 0x08074b50;
-    const ZIP64_CDR_EOF_SIGNATURE = 0x06064b50;
-    const ZIP64_CDR_LOCATOR_SIGNATURE = 0x07064b50;
+    public const FILE_HEADER_SIGNATURE = 0x04034b50;
+
+    public const CDR_FILE_SIGNATURE = 0x02014b50;
+
+    public const CDR_EOF_SIGNATURE = 0x06054b50;
+
+    public const DATA_DESCRIPTOR_SIGNATURE = 0x08074b50;
+
+    public const ZIP64_CDR_EOF_SIGNATURE = 0x06064b50;
+
+    public const ZIP64_CDR_LOCATOR_SIGNATURE = 0x07064b50;
 
     /**
      * Global Options
@@ -312,12 +318,9 @@ class ZipStream
      *
      * Examples:
      *
-     *   // create a temporary file stream and write text to it
-     *   $fp = tmpfile();
-     *   fwrite($fp, 'The quick brown fox jumped over the lazy dog.');
-     *
+     *   $stream = $response->getBody();
      *   // add a file named 'streamfile.txt' from the content of the stream
-     *   $x->addFileFromPsr7Stream('streamfile.txt', $fp);
+     *   $x->addFileFromPsr7Stream('streamfile.txt', $stream);
      *
      * @return void
      */
@@ -378,34 +381,6 @@ class ZipStream
         $this->clear();
     }
 
-    /**
-     * Send ZIP64 CDR EOF (Central Directory Record End-of-File) record.
-     *
-     * @return void
-     */
-    protected function addCdr64Eof(): void
-    {
-        $num_files = count($this->files);
-        $cdr_length = $this->cdr_ofs;
-        $cdr_offset = $this->ofs;
-
-        $fields = [
-            ['V', static::ZIP64_CDR_EOF_SIGNATURE],     // ZIP64 end of central file header signature
-            ['P', 44],                                  // Length of data below this header (length of block - 12) = 44
-            ['v', static::ZIP_VERSION_MADE_BY],         // Made by version
-            ['v', Version::ZIP64],                      // Extract by version
-            ['V', 0x00],                                // disk number
-            ['V', 0x00],                                // no of disks
-            ['P', $num_files],                          // no of entries on disk
-            ['P', $num_files],                          // no of entries in cdr
-            ['P', $cdr_length],                         // CDR size
-            ['P', $cdr_offset],                         // CDR offset
-        ];
-
-        $ret = static::packFields($fields);
-        $this->send($ret);
-    }
-
     /**
      * Create a format string and argument list for pack(), then call
      * pack() and return the result.
@@ -459,7 +434,13 @@ class ZipStream
         }
         $this->need_headers = false;
 
-        fwrite($this->opt->getOutputStream(), $str);
+        $outputStream = $this->opt->getOutputStream();
+
+        if ($outputStream instanceof StreamInterface) {
+            $outputStream->write($str);
+        } else {
+            fwrite($outputStream, $str);
+        }
 
         if ($this->opt->isFlushOutput()) {
             // flush output buffer if it is on and flushable
@@ -473,6 +454,62 @@ class ZipStream
         }
     }
 
+    /**
+     * Is this file larger than large_file_size?
+     *
+     * @param string $path
+     * @return bool
+     */
+    public function isLargeFile(string $path): bool
+    {
+        if (!$this->opt->isStatFiles()) {
+            return false;
+        }
+        $stat = stat($path);
+        return $stat['size'] > $this->opt->getLargeFileSize();
+    }
+
+    /**
+     * Save file attributes for trailing CDR record.
+     *
+     * @param File $file
+     * @return void
+     */
+    public function addToCdr(File $file): void
+    {
+        $file->ofs = $this->ofs;
+        $this->ofs = $this->ofs->add($file->getTotalLength());
+        $this->files[] = $file->getCdrFile();
+    }
+
+    /**
+     * Send ZIP64 CDR EOF (Central Directory Record End-of-File) record.
+     *
+     * @return void
+     */
+    protected function addCdr64Eof(): void
+    {
+        $num_files = count($this->files);
+        $cdr_length = $this->cdr_ofs;
+        $cdr_offset = $this->ofs;
+
+        $fields = [
+            ['V', static::ZIP64_CDR_EOF_SIGNATURE],     // ZIP64 end of central file header signature
+            ['P', 44],                                  // Length of data below this header (length of block - 12) = 44
+            ['v', static::ZIP_VERSION_MADE_BY],         // Made by version
+            ['v', Version::ZIP64],                      // Extract by version
+            ['V', 0x00],                                // disk number
+            ['V', 0x00],                                // no of disks
+            ['P', $num_files],                          // no of entries on disk
+            ['P', $num_files],                          // no of entries in cdr
+            ['P', $cdr_length],                         // CDR size
+            ['P', $cdr_offset],                         // CDR offset
+        ];
+
+        $ret = static::packFields($fields);
+        $this->send($ret);
+    }
+
     /**
      * Send HTTP headers for this stream.
      *
@@ -492,13 +529,13 @@ class ZipStream
             $disposition .= "; filename*=UTF-8''{$urlencoded}";
         }
 
-        $headers = array(
+        $headers = [
             'Content-Type' => $this->opt->getContentType(),
             'Content-Disposition' => $disposition,
             'Pragma' => 'public',
             'Cache-Control' => 'public, must-revalidate',
-            'Content-Transfer-Encoding' => 'binary'
-        );
+            'Content-Transfer-Encoding' => 'binary',
+        ];
 
         $call = $this->opt->getHttpHeaderCallback();
         foreach ($headers as $key => $val) {
@@ -568,32 +605,4 @@ class ZipStream
         $this->cdr_ofs = new Bigint();
         $this->opt = new ArchiveOptions();
     }
-
-    /**
-     * Is this file larger than large_file_size?
-     *
-     * @param string $path
-     * @return bool
-     */
-    public function isLargeFile(string $path): bool
-    {
-        if (!$this->opt->isStatFiles()) {
-            return false;
-        }
-        $stat = stat($path);
-        return $stat['size'] > $this->opt->getLargeFileSize();
-    }
-
-    /**
-     * Save file attributes for trailing CDR record.
-     *
-     * @param File $file
-     * @return void
-     */
-    public function addToCdr(File $file): void
-    {
-        $file->ofs = $this->ofs;
-        $this->ofs = $this->ofs->add($file->getTotalLength());
-        $this->files[] = $file->getCdrFile();
-    }
 }

+ 1 - 0
vendor/maennchen/zipstream-php/test/BigintTest.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace BigintTest;

+ 203 - 139
vendor/maennchen/zipstream-php/test/ZipStreamTest.php

@@ -1,15 +1,21 @@
 <?php
+
 declare(strict_types=1);
 
 namespace ZipStreamTest;
 
-use org\bovigo\vfs\vfsStream;
 use GuzzleHttp\Psr7\Response;
+use org\bovigo\vfs\vfsStream;
 use PHPUnit\Framework\TestCase;
+use RecursiveDirectoryIterator;
+use RecursiveIteratorIterator;
+use ReflectionClass;
+use ZipArchive;
 use ZipStream\File;
 use ZipStream\Option\Archive as ArchiveOptions;
 use ZipStream\Option\File as FileOptions;
 use ZipStream\Option\Method;
+use ZipStream\Stream;
 use ZipStream\ZipStream;
 
 /**
@@ -17,7 +23,7 @@ use ZipStream\ZipStream;
  */
 class ZipStreamTest extends TestCase
 {
-    const OSX_ARCHIVE_UTILITY =
+    public const OSX_ARCHIVE_UTILITY =
         '/System/Library/CoreServices/Applications/Archive Utility.app/Contents/MacOS/Archive Utility';
 
     public function testFileNotFoundException(): void
@@ -35,7 +41,7 @@ class ZipStreamTest extends TestCase
         // create new virtual filesystem
         $root = vfsStream::setup('vfs');
         // create a virtual file with no permissions
-        $file = vfsStream::newFile('foo.txt', 0000)->at($root)->setContent('bar');
+        $file = vfsStream::newFile('foo.txt', 0)->at($root)->setContent('bar');
         $zip = new ZipStream();
         $this->expectException(\ZipStream\Exception\FileNotReadableException::class);
         $zip->addFileFromPath('foo.txt', $file->url());
@@ -44,7 +50,7 @@ class ZipStreamTest extends TestCase
     public function testDostime(): void
     {
         // Allows testing of protected method
-        $class = new \ReflectionClass(File::class);
+        $class = new ReflectionClass(File::class);
         $method = $class->getMethod('dostime');
         $method->setAccessible(true);
 
@@ -75,81 +81,12 @@ class ZipStreamTest extends TestCase
         $tmpDir = $this->validateAndExtractZip($tmp);
 
         $files = $this->getRecursiveFileList($tmpDir);
-        $this->assertEquals(['sample.txt', 'test/sample.txt'], $files);
+        $this->assertSame(['sample.txt', 'test' . DIRECTORY_SEPARATOR . 'sample.txt'], $files);
 
         $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
         $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
     }
 
-    /**
-     * @return array
-     */
-    protected function getTmpFileStream(): array
-    {
-        $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
-        $stream = fopen($tmp, 'wb+');
-
-        return array($tmp, $stream);
-    }
-
-    /**
-     * @param string $tmp
-     * @return string
-     */
-    protected function validateAndExtractZip($tmp): string
-    {
-        $tmpDir = $this->getTmpDir();
-
-        $zipArch = new \ZipArchive;
-        $res = $zipArch->open($tmp);
-
-        if ($res !== true) {
-            $this->fail("Failed to open {$tmp}. Code: $res");
-
-            return $tmpDir;
-        }
-
-        $this->assertEquals(0, $zipArch->status);
-        $this->assertEquals(0, $zipArch->statusSys);
-
-        $zipArch->extractTo($tmpDir);
-        $zipArch->close();
-
-        return $tmpDir;
-    }
-
-    protected function getTmpDir(): string
-    {
-        $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
-        unlink($tmp);
-        mkdir($tmp) or $this->fail('Failed to make directory');
-
-        return $tmp;
-    }
-
-    /**
-     * @param string $path
-     * @return string[]
-     */
-    protected function getRecursiveFileList(string $path): array
-    {
-        $data = array();
-        $path = (string)realpath($path);
-        $files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path));
-
-        $pathLen = strlen($path);
-        foreach ($files as $file) {
-            $filePath = $file->getRealPath();
-            if (!is_dir($filePath)) {
-                $data[] = substr($filePath, $pathLen + 1);
-            }
-        }
-
-        sort($data);
-
-        return $data;
-    }
-
     public function testAddFileUtf8NameComment(): void
     {
         [$tmp, $stream] = $this->getTmpFileStream();
@@ -176,19 +113,17 @@ class ZipStreamTest extends TestCase
         $tmpDir = $this->validateAndExtractZip($tmp);
 
         $files = $this->getRecursiveFileList($tmpDir);
-        $this->assertEquals(array($name), $files);
+        $this->assertSame([$name], $files);
         $this->assertStringEqualsFile($tmpDir . '/' . $name, $content);
 
-        $zipArch = new \ZipArchive();
+        $zipArch = new ZipArchive();
         $zipArch->open($tmp);
-        $this->assertEquals($comment, $zipArch->getCommentName($name));
+        $this->assertSame($comment, $zipArch->getCommentName($name));
     }
 
     public function testAddFileUtf8NameNonUtfComment(): void
     {
-        $this->expectException(\ZipStream\Exception\EncodingException::class);
-
-        $stream = $this->getTmpFileStream()[1];
+        [$tmp, $stream] = $this->getTmpFileStream();
 
         $options = new ArchiveOptions();
         $options->setOutputStream($stream);
@@ -197,33 +132,66 @@ class ZipStreamTest extends TestCase
 
         $name = 'á.txt';
         $content = 'any';
-        $comment = 'á';
+        $comment = mb_convert_encoding('á', 'ISO-8859-2', 'UTF-8');
+
+        // @see https://libzip.org/documentation/zip_file_get_comment.html
+        //
+        // mb_convert_encoding hasn't CP437.
+        // nearly CP850 (DOS-Latin-1)
+        $guessComment = mb_convert_encoding($comment, 'UTF-8', 'CP850');
 
         $fileOptions = new FileOptions();
-        $fileOptions->setComment(mb_convert_encoding($comment, 'ISO-8859-2', 'UTF-8'));
+        $fileOptions->setComment($comment);
 
         $zip->addFile($name, $content, $fileOptions);
+        $zip->finish();
+        fclose($stream);
+
+        $zipArch = new ZipArchive();
+        $zipArch->open($tmp);
+        $this->assertSame($guessComment, $zipArch->getCommentName($name));
+        $this->assertSame($comment, $zipArch->getCommentName($name, ZipArchive::FL_ENC_RAW));
     }
 
     public function testAddFileNonUtf8NameUtfComment(): void
     {
-        $this->expectException(\ZipStream\Exception\EncodingException::class);
-
-        $stream = $this->getTmpFileStream()[1];
+        [$tmp, $stream] = $this->getTmpFileStream();
 
         $options = new ArchiveOptions();
         $options->setOutputStream($stream);
 
         $zip = new ZipStream(null, $options);
 
-        $name = 'á.txt';
+        $name = mb_convert_encoding('á.txt', 'ISO-8859-2', 'UTF-8');
         $content = 'any';
         $comment = 'á';
 
+        // @see https://libzip.org/documentation/zip_get_name.html
+        //
+        // mb_convert_encoding hasn't CP437.
+        // nearly CP850 (DOS-Latin-1)
+        $guessName = mb_convert_encoding($name, 'UTF-8', 'CP850');
+
         $fileOptions = new FileOptions();
         $fileOptions->setComment($comment);
 
-        $zip->addFile(mb_convert_encoding($name, 'ISO-8859-2', 'UTF-8'), $content, $fileOptions);
+        $zip->addFile($name, $content, $fileOptions);
+        $zip->finish();
+        fclose($stream);
+
+        $tmpDir = $this->validateAndExtractZip($tmp);
+
+        $files = $this->getRecursiveFileList($tmpDir);
+
+        $this->assertNotSame([$name], $files);
+        $this->assertSame([$guessName], $files);
+        $this->assertStringEqualsFile($tmpDir . '/' . $guessName, $content);
+
+        $zipArch = new ZipArchive();
+        $zipArch->open($tmp);
+        $this->assertSame($guessName, $zipArch->getNameIndex(0));
+        $this->assertSame($name, $zipArch->getNameIndex(0, ZipArchive::FL_ENC_RAW));
+        $this->assertSame($comment, $zipArch->getCommentName($guessName));
     }
 
     public function testAddFileWithStorageMethod(): void
@@ -243,13 +211,13 @@ class ZipStreamTest extends TestCase
         $zip->finish();
         fclose($stream);
 
-        $zipArch = new \ZipArchive();
+        $zipArch = new ZipArchive();
         $zipArch->open($tmp);
 
         $sample1 = $zipArch->statName('sample.txt');
         $sample12 = $zipArch->statName('test/sample.txt');
-        $this->assertEquals($sample1['comp_method'], Method::STORE);
-        $this->assertEquals($sample12['comp_method'], Method::DEFLATE);
+        $this->assertSame($sample1['comp_method'], Method::STORE);
+        $this->assertSame($sample12['comp_method'], Method::DEFLATE);
 
         $zipArch->close();
     }
@@ -275,7 +243,7 @@ class ZipStreamTest extends TestCase
 
         exec(escapeshellarg(self::OSX_ARCHIVE_UTILITY) . ' ' . escapeshellarg($tmp), $output, $returnStatus);
 
-        $this->assertEquals(0, $returnStatus);
+        $this->assertSame(0, $returnStatus);
         $this->assertCount(0, $output);
 
         $this->assertFileExists(dirname($tmp) . '/' . $folder . '/sample.txt');
@@ -307,7 +275,7 @@ class ZipStreamTest extends TestCase
         $tmpDir = $this->validateAndExtractZip($tmp);
 
         $files = $this->getRecursiveFileList($tmpDir);
-        $this->assertEquals(array('sample.txt', 'test/sample.txt'), $files);
+        $this->assertSame(['sample.txt', 'test' . DIRECTORY_SEPARATOR . 'sample.txt'], $files);
 
         $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
         $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
@@ -338,14 +306,14 @@ class ZipStreamTest extends TestCase
         $zip->finish();
         fclose($stream);
 
-        $zipArch = new \ZipArchive();
+        $zipArch = new ZipArchive();
         $zipArch->open($tmp);
 
         $sample1 = $zipArch->statName('sample.txt');
-        $this->assertEquals(Method::STORE, $sample1['comp_method']);
+        $this->assertSame(Method::STORE, $sample1['comp_method']);
 
         $sample2 = $zipArch->statName('test/sample.txt');
-        $this->assertEquals(Method::DEFLATE, $sample2['comp_method']);
+        $this->assertSame(Method::DEFLATE, $sample2['comp_method']);
 
         $zipArch->close();
     }
@@ -366,42 +334,6 @@ class ZipStreamTest extends TestCase
         }
     }
 
-    protected function addLargeFileFileFromPath($method, $zeroHeader, $zip64): void
-    {
-        [$tmp, $stream] = $this->getTmpFileStream();
-
-        $options = new ArchiveOptions();
-        $options->setOutputStream($stream);
-        $options->setLargeFileMethod($method);
-        $options->setLargeFileSize(5);
-        $options->setZeroHeader($zeroHeader);
-        $options->setEnableZip64($zip64);
-
-        $zip = new ZipStream(null, $options);
-
-        [$tmpExample, $streamExample] = $this->getTmpFileStream();
-        for ($i = 0; $i <= 10000; $i++) {
-            fwrite($streamExample, sha1((string)$i));
-            if ($i % 100 === 0) {
-                fwrite($streamExample, "\n");
-            }
-        }
-        fclose($streamExample);
-        $shaExample = sha1_file($tmpExample);
-        $zip->addFileFromPath('sample.txt', $tmpExample);
-        unlink($tmpExample);
-
-        $zip->finish();
-        fclose($stream);
-
-        $tmpDir = $this->validateAndExtractZip($tmp);
-
-        $files = $this->getRecursiveFileList($tmpDir);
-        $this->assertEquals(array('sample.txt'), $files);
-
-        $this->assertEquals(sha1_file($tmpDir . '/sample.txt'), $shaExample, "SHA-1 Mismatch Method: {$method}");
-    }
-
     public function testAddFileFromStream(): void
     {
         [$tmp, $stream] = $this->getTmpFileStream();
@@ -433,7 +365,7 @@ class ZipStreamTest extends TestCase
         $tmpDir = $this->validateAndExtractZip($tmp);
 
         $files = $this->getRecursiveFileList($tmpDir);
-        $this->assertEquals(array('sample.txt', 'test/sample.txt'), $files);
+        $this->assertSame(['sample.txt', 'test' . DIRECTORY_SEPARATOR . 'sample.txt'], $files);
 
         $this->assertStringEqualsFile(__FILE__, file_get_contents($tmpDir . '/sample.txt'));
         $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
@@ -466,14 +398,14 @@ class ZipStreamTest extends TestCase
         $zip->finish();
         fclose($stream);
 
-        $zipArch = new \ZipArchive();
+        $zipArch = new ZipArchive();
         $zipArch->open($tmp);
 
         $sample1 = $zipArch->statName('sample.txt');
-        $this->assertEquals(Method::STORE, $sample1['comp_method']);
+        $this->assertSame(Method::STORE, $sample1['comp_method']);
 
         $sample2 = $zipArch->statName('test/sample.txt');
-        $this->assertEquals(Method::DEFLATE, $sample2['comp_method']);
+        $this->assertSame(Method::DEFLATE, $sample2['comp_method']);
 
         $zipArch->close();
     }
@@ -500,7 +432,34 @@ class ZipStreamTest extends TestCase
         $tmpDir = $this->validateAndExtractZip($tmp);
 
         $files = $this->getRecursiveFileList($tmpDir);
-        $this->assertEquals(array('sample.json'), $files);
+        $this->assertSame(['sample.json'], $files);
+        $this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
+    }
+
+    public function testAddFileFromPsr7StreamWithOutputToPsr7Stream(): void
+    {
+        [$tmp, $resource] = $this->getTmpFileStream();
+        $psr7OutputStream = new Stream($resource);
+
+        $options = new ArchiveOptions();
+        $options->setOutputStream($psr7OutputStream);
+
+        $zip = new ZipStream(null, $options);
+
+        $body = 'Sample String Data';
+        $response = new Response(200, [], $body);
+
+        $fileOptions = new FileOptions();
+        $fileOptions->setMethod(Method::STORE());
+
+        $zip->addFileFromPsr7Stream('sample.json', $response->getBody(), $fileOptions);
+        $zip->finish();
+        $psr7OutputStream->close();
+
+        $tmpDir = $this->validateAndExtractZip($tmp);
+        $files = $this->getRecursiveFileList($tmpDir);
+
+        $this->assertSame(['sample.json'], $files);
         $this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
     }
 
@@ -529,7 +488,7 @@ class ZipStreamTest extends TestCase
         $tmpDir = $this->validateAndExtractZip($tmp);
 
         $files = $this->getRecursiveFileList($tmpDir);
-        $this->assertEquals(array('sample.json'), $files);
+        $this->assertSame(['sample.json'], $files);
         $this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
     }
 
@@ -552,7 +511,7 @@ class ZipStreamTest extends TestCase
         $tmpDir = $this->validateAndExtractZip($tmp);
 
         $files = $this->getRecursiveFileList($tmpDir);
-        $this->assertEquals(['sample.txt', 'test/sample.txt'], $files);
+        $this->assertSame(['sample.txt', 'test' . DIRECTORY_SEPARATOR . 'sample.txt'], $files);
 
         $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
         $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
@@ -562,7 +521,7 @@ class ZipStreamTest extends TestCase
     {
         // WORKAROUND (1/2): remove phpunit's output buffer in order to run test without any buffering
         ob_end_flush();
-        $this->assertEquals(0, ob_get_level());
+        $this->assertSame(0, ob_get_level());
 
         [$tmp, $stream] = $this->getTmpFileStream();
 
@@ -583,4 +542,109 @@ class ZipStreamTest extends TestCase
         // WORKAROUND (2/2): add back output buffering so that PHPUnit doesn't complain that it is missing
         ob_start();
     }
+
+    /**
+     * @return array
+     */
+    protected function getTmpFileStream(): array
+    {
+        $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
+        $stream = fopen($tmp, 'wb+');
+
+        return [$tmp, $stream];
+    }
+
+    /**
+     * @param string $tmp
+     * @return string
+     */
+    protected function validateAndExtractZip($tmp): string
+    {
+        $tmpDir = $this->getTmpDir();
+
+        $zipArch = new ZipArchive();
+        $res = $zipArch->open($tmp);
+
+        if ($res !== true) {
+            $this->fail("Failed to open {$tmp}. Code: $res");
+
+            return $tmpDir;
+        }
+
+        $this->assertSame(0, $zipArch->status);
+        $this->assertSame(0, $zipArch->statusSys);
+
+        $zipArch->extractTo($tmpDir);
+        $zipArch->close();
+
+        return $tmpDir;
+    }
+
+    protected function getTmpDir(): string
+    {
+        $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
+        unlink($tmp);
+        mkdir($tmp) or $this->fail('Failed to make directory');
+
+        return $tmp;
+    }
+
+    /**
+     * @param string $path
+     * @return string[]
+     */
+    protected function getRecursiveFileList(string $path): array
+    {
+        $data = [];
+        $path = (string)realpath($path);
+        $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
+
+        $pathLen = strlen($path);
+        foreach ($files as $file) {
+            $filePath = $file->getRealPath();
+            if (!is_dir($filePath)) {
+                $data[] = substr($filePath, $pathLen + 1);
+            }
+        }
+
+        sort($data);
+
+        return $data;
+    }
+
+    protected function addLargeFileFileFromPath($method, $zeroHeader, $zip64): void
+    {
+        [$tmp, $stream] = $this->getTmpFileStream();
+
+        $options = new ArchiveOptions();
+        $options->setOutputStream($stream);
+        $options->setLargeFileMethod($method);
+        $options->setLargeFileSize(5);
+        $options->setZeroHeader($zeroHeader);
+        $options->setEnableZip64($zip64);
+
+        $zip = new ZipStream(null, $options);
+
+        [$tmpExample, $streamExample] = $this->getTmpFileStream();
+        for ($i = 0; $i <= 10000; $i++) {
+            fwrite($streamExample, sha1((string)$i));
+            if ($i % 100 === 0) {
+                fwrite($streamExample, "\n");
+            }
+        }
+        fclose($streamExample);
+        $shaExample = sha1_file($tmpExample);
+        $zip->addFileFromPath('sample.txt', $tmpExample);
+        unlink($tmpExample);
+
+        $zip->finish();
+        fclose($stream);
+
+        $tmpDir = $this->validateAndExtractZip($tmp);
+
+        $files = $this->getRecursiveFileList($tmpDir);
+        $this->assertSame(['sample.txt'], $files);
+
+        $this->assertSame(sha1_file($tmpDir . '/sample.txt'), $shaExample, "SHA-1 Mismatch Method: {$method}");
+    }
 }

+ 1 - 0
vendor/maennchen/zipstream-php/test/bootstrap.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 date_default_timezone_set('UTC');

+ 7 - 6
vendor/maennchen/zipstream-php/test/bug/BugHonorFileTimeTest.php

@@ -1,18 +1,19 @@
 <?php
+
 declare(strict_types=1);
 
 namespace BugHonorFileTimeTest;
 
 use DateTime;
-use PHPUnit\Framework\TestCase;
-use ZipStream\Option\{
-    Archive,
-    File
-};
-use ZipStream\ZipStream;
 
 use function fopen;
 
+use PHPUnit\Framework\TestCase;
+use ZipStream\Option\Archive;
+use ZipStream\Option\File;
+
+use ZipStream\ZipStream;
+
 /**
  * Asserts that specified last-modified timestamps are not overwritten when a
  * file is added

+ 8 - 0
vendor/monolog/monolog/CHANGELOG.md

@@ -1,3 +1,11 @@
+### 2.10.0 (2024-11-12)
+
+  * Added `$fileOpenMode` to `StreamHandler` to define a custom fopen mode to open the log file (#1913)
+  * Fixed `StreamHandler` handling of write failures so that it now closes/reopens the stream and retries the write once before failing (#1882)
+  * Fixed `StreamHandler` error handler causing issues if a stream handler triggers an error (#1866)
+  * Fixed `JsonFormatter` handling of incomplete classes (#1834)
+  * Fixed `RotatingFileHandler` bug where rotation could sometimes not happen correctly (#1905)
+
 ### 2.9.3 (2024-04-12)
 
   * Fixed PHP 8.4 deprecation warnings (#1874)

+ 4 - 0
vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php

@@ -192,6 +192,10 @@ class JsonFormatter extends NormalizerFormatter
                 return $data;
             }
 
+            if (\get_class($data) === '__PHP_Incomplete_Class') {
+                return new \ArrayObject($data);
+            }
+
             if (method_exists($data, '__toString')) {
                 return $data->__toString();
             }

+ 1 - 0
vendor/monolog/monolog/src/Monolog/Handler/ElasticsearchHandler.php

@@ -187,6 +187,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
      */
     protected function createExceptionFromResponses($responses): Throwable
     {
+        // @phpstan-ignore offsetAccess.nonOffsetAccessible
         foreach ($responses['items'] ?? [] as $item) {
             if (isset($item['index']['error'])) {
                 return $this->createExceptionFromError($item['index']['error']);

+ 9 - 4
vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php

@@ -112,17 +112,22 @@ class RotatingFileHandler extends StreamHandler
      */
     protected function write(array $record): void
     {
-        // on the first record written, if the log is new, we should rotate (once per day)
+        // on the first record written, if the log is new, we rotate (once per day) after the log has been written so that the new file exists
         if (null === $this->mustRotate) {
             $this->mustRotate = null === $this->url || !file_exists($this->url);
         }
 
+        // if the next rotation is expired, then we rotate immediately
         if ($this->nextRotation <= $record['datetime']) {
             $this->mustRotate = true;
-            $this->close();
+            $this->close(); // triggers rotation
         }
 
         parent::write($record);
+
+        if ($this->mustRotate) {
+            $this->close(); // triggers rotation
+        }
     }
 
     /**
@@ -134,6 +139,8 @@ class RotatingFileHandler extends StreamHandler
         $this->url = $this->getTimedFilename();
         $this->nextRotation = new \DateTimeImmutable('tomorrow');
 
+        $this->mustRotate = false;
+
         // skip GC of old logs if files are unlimited
         if (0 === $this->maxFiles) {
             return;
@@ -166,8 +173,6 @@ class RotatingFileHandler extends StreamHandler
                 restore_error_handler();
             }
         }
-
-        $this->mustRotate = false;
     }
 
     protected function getTimedFilename(): string

+ 38 - 6
vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php

@@ -41,17 +41,22 @@ class StreamHandler extends AbstractProcessingHandler
     protected $filePermission;
     /** @var bool */
     protected $useLocking;
+	/** @var string */
+    protected $fileOpenMode;
     /** @var true|null */
     private $dirCreated = null;
+    /** @var bool */
+    private $retrying = false;
 
     /**
      * @param resource|string $stream         If a missing path can't be created, an UnexpectedValueException will be thrown on first write
      * @param int|null        $filePermission Optional file permissions (default (0644) are only for owner read/write)
      * @param bool            $useLocking     Try to lock log file before doing any writes
+     * @param string          $fileOpenMode   The fopen() mode used when opening a file, if $stream is a file path
      *
      * @throws \InvalidArgumentException If stream is not a resource or string
      */
-    public function __construct($stream, $level = Logger::DEBUG, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false)
+    public function __construct($stream, $level = Logger::DEBUG, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false, $fileOpenMode = 'a')
     {
         parent::__construct($level, $bubble);
 
@@ -78,6 +83,7 @@ class StreamHandler extends AbstractProcessingHandler
             throw new \InvalidArgumentException('A stream must either be a resource or a string.');
         }
 
+        $this->fileOpenMode = $fileOpenMode;
         $this->filePermission = $filePermission;
         $this->useLocking = $useLocking;
     }
@@ -134,9 +140,11 @@ class StreamHandler extends AbstractProcessingHandler
             }
             $this->createDir($url);
             $this->errorMessage = null;
-            set_error_handler([$this, 'customErrorHandler']);
+            set_error_handler(function (...$args) {
+                return $this->customErrorHandler(...$args);
+            });
             try {
-                $stream = fopen($url, 'a');
+                $stream = fopen($url, $this->fileOpenMode);
                 if ($this->filePermission !== null) {
                     @chmod($url, $this->filePermission);
                 }
@@ -162,8 +170,30 @@ class StreamHandler extends AbstractProcessingHandler
             flock($stream, LOCK_EX);
         }
 
-        $this->streamWrite($stream, $record);
+        $this->errorMessage = null;
+        set_error_handler(function (...$args) {
+            return $this->customErrorHandler(...$args);
+        });
+        try {
+            $this->streamWrite($stream, $record);
+        } finally {
+            restore_error_handler();
+        }
+        if ($this->errorMessage !== null) {
+            $error = $this->errorMessage;
+            // close the resource if possible to reopen it, and retry the failed write
+            if (!$this->retrying && $this->url !== null && $this->url !== 'php://memory') {
+                $this->retrying = true;
+                $this->close();
+                $this->write($record);
+
+                return;
+            }
+
+            throw new \UnexpectedValueException('Writing to the log file failed: '.$error . Utils::getRecordMessageForException($record));
+        }
 
+        $this->retrying = false;
         if ($this->useLocking) {
             flock($stream, LOCK_UN);
         }
@@ -183,7 +213,7 @@ class StreamHandler extends AbstractProcessingHandler
 
     private function customErrorHandler(int $code, string $msg): bool
     {
-        $this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg);
+        $this->errorMessage = preg_replace('{^(fopen|mkdir|fwrite)\(.*?\): }', '', $msg);
 
         return true;
     }
@@ -212,7 +242,9 @@ class StreamHandler extends AbstractProcessingHandler
         $dir = $this->getDirFromStream($url);
         if (null !== $dir && !is_dir($dir)) {
             $this->errorMessage = null;
-            set_error_handler([$this, 'customErrorHandler']);
+            set_error_handler(function (...$args) {
+                return $this->customErrorHandler(...$args);
+            });
             $status = mkdir($dir, 0777, true);
             restore_error_handler();
             if (false === $status && !is_dir($dir) && strpos((string) $this->errorMessage, 'File exists') === false) {

+ 4 - 3
vendor/monolog/monolog/src/Monolog/Logger.php

@@ -169,7 +169,7 @@ class Logger implements LoggerInterface, ResettableInterface
     private $logDepth = 0;
 
     /**
-     * @var \WeakMap<\Fiber, int>|null Keeps track of depth inside fibers to prevent infinite logging loops
+     * @var \WeakMap<\Fiber<mixed, mixed, mixed, mixed>, int> Keeps track of depth inside fibers to prevent infinite logging loops
      */
     private $fiberLogDepth;
 
@@ -197,7 +197,7 @@ class Logger implements LoggerInterface, ResettableInterface
 
         if (\PHP_VERSION_ID >= 80100) {
             // Local variable for phpstan, see https://github.com/phpstan/phpstan/issues/6732#issuecomment-1111118412
-            /** @var \WeakMap<\Fiber, int> $fiberLogDepth */
+            /** @var \WeakMap<\Fiber<mixed, mixed, mixed, mixed>, int> $fiberLogDepth */
             $fiberLogDepth = new \WeakMap();
             $this->fiberLogDepth = $fiberLogDepth;
         }
@@ -345,6 +345,7 @@ class Logger implements LoggerInterface, ResettableInterface
 
         if ($this->detectCycles) {
             if (\PHP_VERSION_ID >= 80100 && $fiber = \Fiber::getCurrent()) {
+                // @phpstan-ignore offsetAssign.dimType
                 $this->fiberLogDepth[$fiber] = $this->fiberLogDepth[$fiber] ?? 0;
                 $logDepth = ++$this->fiberLogDepth[$fiber];
             } else {
@@ -753,7 +754,7 @@ class Logger implements LoggerInterface, ResettableInterface
 
         if (\PHP_VERSION_ID >= 80100) {
             // Local variable for phpstan, see https://github.com/phpstan/phpstan/issues/6732#issuecomment-1111118412
-            /** @var \WeakMap<\Fiber, int> $fiberLogDepth */
+            /** @var \WeakMap<\Fiber<mixed, mixed, mixed, mixed>, int> $fiberLogDepth */
             $fiberLogDepth = new \WeakMap();
             $this->fiberLogDepth = $fiberLogDepth;
         }

+ 1 - 1
vendor/monolog/monolog/src/Monolog/Utils.php

@@ -249,7 +249,7 @@ final class Utils
         }
 
         $val = (int) $match['val'];
-        switch (strtolower($match['unit'] ?? '')) {
+        switch (strtolower($match['unit'])) {
             case 'g':
                 $val *= 1024;
             case 'm':

+ 44 - 0
vendor/mpdf/mpdf/.github/workflows/static-analysis.yml

@@ -0,0 +1,44 @@
+# https://help.github.com/en/categories/automating-your-workflow-with-github-actions
+
+name: "Static Analysis check"
+
+on:
+  pull_request:
+  push:
+    branches:
+      - "master"
+      - "development"
+      - "test"
+
+jobs:
+
+  stan:
+
+    name: "Static Analysis check"
+
+    runs-on: ${{ matrix.operating-system }}
+
+    strategy:
+      matrix:
+        php-version:
+          - "8.2"
+
+        operating-system: [ubuntu-latest]
+
+    steps:
+      - name: "Checkout"
+        uses: "actions/checkout@v4"
+
+      - name: "Install PHP"
+        uses: "shivammathur/setup-php@v2"
+        with:
+          coverage: "none"
+          php-version: "${{ matrix.php-version }}"
+          extensions: "mbstring"
+          tools: composer:v2
+
+      - name: "Install dependencies"
+        run: "composer install --no-interaction --no-progress && composer require \"phpstan/phpstan:^2.0\""
+
+      - name: "Static Analysis check"
+        run: vendor/bin/phpstan --no-progress

Some files were not shown because too many files changed in this diff