浏览代码

公告群聊

LiuJ 3 月之前
父节点
当前提交
cf5b8b7e57
共有 100 个文件被更改,包括 12471 次插入15 次删除
  1. 58 2
      app/JsonRpc/NewsService.php
  2. 27 0
      app/Model/ChatGroups.php
  3. 27 0
      app/Model/ChatGroupsMember.php
  4. 54 0
      app/Tools/PublicData.php
  5. 4 1
      composer.json
  6. 362 1
      composer.lock
  7. 20 0
      vendor/brick/math/LICENSE
  8. 39 0
      vendor/brick/math/composer.json
  9. 70 0
      vendor/brick/math/psalm-baseline.xml
  10. 792 0
      vendor/brick/math/src/BigDecimal.php
  11. 1062 0
      vendor/brick/math/src/BigInteger.php
  12. 515 0
      vendor/brick/math/src/BigNumber.php
  13. 424 0
      vendor/brick/math/src/BigRational.php
  14. 35 0
      vendor/brick/math/src/Exception/DivisionByZeroException.php
  15. 23 0
      vendor/brick/math/src/Exception/IntegerOverflowException.php
  16. 12 0
      vendor/brick/math/src/Exception/MathException.php
  17. 12 0
      vendor/brick/math/src/Exception/NegativeNumberException.php
  18. 41 0
      vendor/brick/math/src/Exception/NumberFormatException.php
  19. 19 0
      vendor/brick/math/src/Exception/RoundingNecessaryException.php
  20. 668 0
      vendor/brick/math/src/Internal/Calculator.php
  21. 75 0
      vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php
  22. 125 0
      vendor/brick/math/src/Internal/Calculator/GmpCalculator.php
  23. 598 0
      vendor/brick/math/src/Internal/Calculator/NativeCalculator.php
  24. 98 0
      vendor/brick/math/src/RoundingMode.php
  25. 23 4
      vendor/composer/InstalledVersions.php
  26. 193 0
      vendor/composer/autoload_classmap.php
  27. 1 0
      vendor/composer/autoload_files.php
  28. 6 1
      vendor/composer/autoload_psr4.php
  29. 224 2
      vendor/composer/autoload_static.php
  30. 376 0
      vendor/composer/installed.json
  31. 55 4
      vendor/composer/installed.php
  32. 2 0
      vendor/hyperf/paginator/.gitattributes
  33. 22 0
      vendor/hyperf/paginator/LICENSE
  34. 49 0
      vendor/hyperf/paginator/composer.json
  35. 545 0
      vendor/hyperf/paginator/src/AbstractCursorPaginator.php
  36. 488 0
      vendor/hyperf/paginator/src/AbstractPaginator.php
  37. 33 0
      vendor/hyperf/paginator/src/ConfigProvider.php
  38. 93 0
      vendor/hyperf/paginator/src/Contract/CursorPaginator.php
  39. 111 0
      vendor/hyperf/paginator/src/Cursor.php
  40. 138 0
      vendor/hyperf/paginator/src/CursorPaginator.php
  41. 179 0
      vendor/hyperf/paginator/src/LengthAwarePaginator.php
  42. 72 0
      vendor/hyperf/paginator/src/Listener/PageResolverListener.php
  43. 152 0
      vendor/hyperf/paginator/src/Paginator.php
  44. 185 0
      vendor/hyperf/paginator/src/UrlWindow.php
  45. 2 0
      vendor/hyperf/snowflake/.gitattributes
  46. 21 0
      vendor/hyperf/snowflake/LICENSE
  47. 46 0
      vendor/hyperf/snowflake/composer.json
  48. 24 0
      vendor/hyperf/snowflake/publish/snowflake.php
  49. 38 0
      vendor/hyperf/snowflake/src/Concern/Snowflake.php
  50. 37 0
      vendor/hyperf/snowflake/src/ConfigProvider.php
  51. 74 0
      vendor/hyperf/snowflake/src/Configuration.php
  52. 66 0
      vendor/hyperf/snowflake/src/ConfigurationInterface.php
  53. 19 0
      vendor/hyperf/snowflake/src/Exception/SnowflakeException.php
  54. 63 0
      vendor/hyperf/snowflake/src/IdGenerator.php
  55. 19 0
      vendor/hyperf/snowflake/src/IdGenerator/SnowflakeIdGenerator.php
  56. 26 0
      vendor/hyperf/snowflake/src/IdGeneratorInterface.php
  57. 98 0
      vendor/hyperf/snowflake/src/Meta.php
  58. 77 0
      vendor/hyperf/snowflake/src/MetaGenerator.php
  59. 49 0
      vendor/hyperf/snowflake/src/MetaGenerator/RandomMilliSecondMetaGenerator.php
  60. 81 0
      vendor/hyperf/snowflake/src/MetaGenerator/RedisMetaGenerator.php
  61. 39 0
      vendor/hyperf/snowflake/src/MetaGenerator/RedisMilliSecondMetaGenerator.php
  62. 39 0
      vendor/hyperf/snowflake/src/MetaGenerator/RedisSecondMetaGenerator.php
  63. 34 0
      vendor/hyperf/snowflake/src/MetaGeneratorFactory.php
  64. 24 0
      vendor/hyperf/snowflake/src/MetaGeneratorInterface.php
  65. 19 0
      vendor/ramsey/collection/LICENSE
  66. 59 0
      vendor/ramsey/collection/README.md
  67. 169 0
      vendor/ramsey/collection/SECURITY.md
  68. 108 0
      vendor/ramsey/collection/composer.json
  69. 171 0
      vendor/ramsey/collection/src/AbstractArray.php
  70. 365 0
      vendor/ramsey/collection/src/AbstractCollection.php
  71. 51 0
      vendor/ramsey/collection/src/AbstractSet.php
  72. 49 0
      vendor/ramsey/collection/src/ArrayInterface.php
  73. 95 0
      vendor/ramsey/collection/src/Collection.php
  74. 253 0
      vendor/ramsey/collection/src/CollectionInterface.php
  75. 166 0
      vendor/ramsey/collection/src/DoubleEndedQueue.php
  76. 313 0
      vendor/ramsey/collection/src/DoubleEndedQueueInterface.php
  77. 21 0
      vendor/ramsey/collection/src/Exception/CollectionException.php
  78. 24 0
      vendor/ramsey/collection/src/Exception/CollectionMismatchException.php
  79. 24 0
      vendor/ramsey/collection/src/Exception/InvalidArgumentException.php
  80. 26 0
      vendor/ramsey/collection/src/Exception/InvalidPropertyOrMethod.php
  81. 24 0
      vendor/ramsey/collection/src/Exception/NoSuchElementException.php
  82. 24 0
      vendor/ramsey/collection/src/Exception/OutOfBoundsException.php
  83. 24 0
      vendor/ramsey/collection/src/Exception/UnsupportedOperationException.php
  84. 24 0
      vendor/ramsey/collection/src/GenericArray.php
  85. 205 0
      vendor/ramsey/collection/src/Map/AbstractMap.php
  86. 59 0
      vendor/ramsey/collection/src/Map/AbstractTypedMap.php
  87. 24 0
      vendor/ramsey/collection/src/Map/AssociativeArrayMap.php
  88. 142 0
      vendor/ramsey/collection/src/Map/MapInterface.php
  89. 110 0
      vendor/ramsey/collection/src/Map/NamedParameterMap.php
  90. 112 0
      vendor/ramsey/collection/src/Map/TypedMap.php
  91. 36 0
      vendor/ramsey/collection/src/Map/TypedMapInterface.php
  92. 148 0
      vendor/ramsey/collection/src/Queue.php
  93. 202 0
      vendor/ramsey/collection/src/QueueInterface.php
  94. 59 0
      vendor/ramsey/collection/src/Set.php
  95. 31 0
      vendor/ramsey/collection/src/Sort.php
  96. 57 0
      vendor/ramsey/collection/src/Tool/TypeTrait.php
  97. 100 0
      vendor/ramsey/collection/src/Tool/ValueExtractorTrait.php
  98. 92 0
      vendor/ramsey/collection/src/Tool/ValueToStringTrait.php
  99. 19 0
      vendor/ramsey/uuid/LICENSE
  100. 83 0
      vendor/ramsey/uuid/README.md

+ 58 - 2
app/JsonRpc/NewsService.php

@@ -33,10 +33,14 @@ use Ramsey\Uuid\Uuid;
 use Hyperf\Utils\Random;
 use Fukuball\Jieba\Jieba;
 use Fukuball\Jieba\Finalseg;
-use App\Model\ChatRecords;
+
 use Hyperf\Utils\Collection;
 
 use function Hyperf\Support\retry;
+use App\Tools\PublicData;
+use App\Model\ChatGroups;
+use App\Model\ChatGroupsMember;
+use App\Model\ChatRecords;
 
 #[RpcService(name: "NewsService", protocol: "jsonrpc-http", server: "jsonrpc-http")]
 class NewsService implements NewsServiceInterface
@@ -1968,9 +1972,48 @@ class NewsService implements NewsServiceInterface
             })
             ->pluck('user_id')->toArray();
         $user_id = array_unique($user_id);
+        $chat_ids = $user_id;
         $user_id = json_encode($user_id);
         $data['re_user_ids'] = $user_id;
-        $result = Notice::create($data);
+        $result = Notice::insertGetId($data);
+        if ($result && $data['is_group'] == 1) {
+            //chat_group
+            $group_id = PublicData::uuid();
+            $groupData = [
+                'id' => $group_id,
+                'creator_id' => $data['user_id'],
+                'group_name' => $data['group_name'] ?? '',
+                'profile' => $data['profile'] ?? 0,
+            ];
+
+            $groupResult = ChatGroups::insertGetId($groupData);
+            var_dump($groupResult, '-----------------groupid-------');
+            $groupMemberData = [
+                'id' => PublicData::uuid(),
+                'user_id' => $data['user_id'],
+                'group_id' => $group_id,
+                'leader' => 2,
+            ];
+            $groupMemberResult = ChatGroupsMember::insertGetId($groupMemberData);
+
+            //$chat_ids 去除掉$data['user_id']
+            $chat_ids = array_diff($chat_ids, [$data['user_id']]);
+            //插入群成员表
+            $groupMemberDataUser = [];
+            foreach ($chat_ids as $key => $value) {
+                $groupMemberDataUser[] = [
+                    'id' => PublicData::uuid(),
+                    'user_id' => $value,
+                    'group_id' => $group_id,
+                    'leader' => 1,
+                ];
+            }
+            ChatGroupsMember::insert($groupMemberDataUser);
+            //更新result的 group_id
+            $data['group_id'] = $group_id;
+            Notice::where(['id' => $result])->update($data);
+        }
+
         if (empty($result)) {
             return Result::error("添加失败", 0);
         }
@@ -2053,6 +2096,19 @@ class NewsService implements NewsServiceInterface
     }
     public function deleteNotice(array $data): array
     {
+        $notice = Notice::where('id', $data['id'])->first();
+        if (empty($notice)) {
+            return Result::error("删除失败", 0);
+        }
+        $chat_group_id = $notice->group_id;
+        if ($chat_group_id) {
+            //删除群聊
+            ChatGroups::where('id', $chat_group_id)->delete();
+            //删除群成员
+            ChatGroupsMember::where('group_id', $chat_group_id)->delete();
+            //删除群聊记录
+            ChatRecords::where('receiver_id', $chat_group_id)->delete();
+        }
         $result = Notice::where('id', $data['id'])->delete();
         return Result::success($result);
     }

+ 27 - 0
app/Model/ChatGroups.php

@@ -0,0 +1,27 @@
+<?php
+
+declare (strict_types = 1);
+
+namespace App\Model;
+
+use Hyperf\DbConnection\Model\Model;
+
+/**
+ */
+class ChatGroups extends Model
+{
+    /**
+     * The table associated with the model.
+     */
+    protected ?string $table = 'chat_groups';
+
+    /**
+     * The attributes that are mass assignable.
+     */
+    protected array $fillable = [];
+
+    /**
+     * The attributes that should be cast to native types.
+     */
+    protected array $casts = [];
+}

+ 27 - 0
app/Model/ChatGroupsMember.php

@@ -0,0 +1,27 @@
+<?php
+
+declare (strict_types = 1);
+
+namespace App\Model;
+
+use Hyperf\DbConnection\Model\Model;
+
+/**
+ */
+class ChatGroupsMember extends Model
+{
+    /**
+     * The table associated with the model.
+     */
+    protected ?string $table = 'chat_groups_members';
+
+    /**
+     * The attributes that are mass assignable.
+     */
+    protected array $fillable = [];
+
+    /**
+     * The attributes that should be cast to native types.
+     */
+    protected array $casts = [];
+}

+ 54 - 0
app/Tools/PublicData.php

@@ -0,0 +1,54 @@
+<?php
+namespace App\Tools;
+use App\Constants\ErrorCode;
+use Hyperf\Snowflake\IdGeneratorInterface;
+use Hyperf\Context\ApplicationContext;
+class PublicData
+{
+    /**
+     * 递归查询
+     * @param $menuItems
+     * @param $parentId
+     * @return array
+     */
+    public static  function buildMenuTree($menuItems, $parentId = 0) {
+        $tree = [];
+        foreach ($menuItems as $item) {
+            if ($item['pid'] == $parentId) {
+                // 找到子菜单
+                $children = self::buildMenuTree($menuItems, $item['id']);
+                // 如果子菜单存在,则添加到当前菜单的children中
+                if ($children) {
+                    $item['children'] = $children;
+                }
+                // 将当前菜单添加到树中
+                $tree[] = $item;
+            }
+        }
+        return $tree;
+    }
+
+    /**
+     * 计算两个时间差的天数
+     * @param $sTime
+     * @param $eTime
+     * @return float
+     */
+    public static function residueDay($sTime,$eTime)
+    {
+        $startTime = strtotime($sTime);
+        $endTime = strtotime($eTime);
+        $diffDays = floor(($endTime - $startTime) / (60 * 60 * 24));
+        return $diffDays;
+    }
+
+    public static function uuid()
+    {
+        var_dump("uuid==========");
+        $container = ApplicationContext::getContainer();
+        $generator = $container->get(IdGeneratorInterface::class);
+        var_dump("uuid==+++++++++++",$generator->generate());
+        return $generator->generate();
+    }
+
+}

+ 4 - 1
composer.json

@@ -31,13 +31,16 @@
         "hyperf/logger": "~3.1.0",
         "hyperf/memory": "~3.1.0",
         "hyperf/nacos": "^3.1",
+        "hyperf/paginator": "^3.1",
         "hyperf/process": "~3.1.0",
         "hyperf/redis": "~3.1.0",
         "hyperf/rpc-server": "*",
         "hyperf/service-governance": "^3.1",
         "hyperf/service-governance-consul": "^3.1",
         "hyperf/service-governance-nacos": "^3.1",
-        "hyperf/utils": "^3.1"
+        "hyperf/snowflake": "^3.1",
+        "hyperf/utils": "^3.1",
+        "ramsey/uuid": "^4.7"
     },
     "require-dev": {
         "friendsofphp/php-cs-fixer": "^3.0",

+ 362 - 1
composer.lock

@@ -4,8 +4,68 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "04eeed2b1b6a04f0539eaee477e8e0af",
+    "content-hash": "1f3e4f9eb6cb2b59a5b4e2acc6bc716e",
     "packages": [
+        {
+            "name": "brick/math",
+            "version": "0.12.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/brick/math.git",
+                "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba",
+                "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.1"
+            },
+            "require-dev": {
+                "php-coveralls/php-coveralls": "^2.2",
+                "phpunit/phpunit": "^10.1",
+                "vimeo/psalm": "6.8.8"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Brick\\Math\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Arbitrary-precision arithmetic library",
+            "keywords": [
+                "Arbitrary-precision",
+                "BigInteger",
+                "BigRational",
+                "arithmetic",
+                "bigdecimal",
+                "bignum",
+                "bignumber",
+                "brick",
+                "decimal",
+                "integer",
+                "math",
+                "mathematics",
+                "rational"
+            ],
+            "support": {
+                "issues": "https://github.com/brick/math/issues",
+                "source": "https://github.com/brick/math/tree/0.12.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/BenMorel",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-02-28T13:11:00+00:00"
+        },
         {
             "name": "carbonphp/carbon-doctrine-types",
             "version": "3.2.0",
@@ -3198,6 +3258,74 @@
             ],
             "time": "2024-03-23T11:28:51+00:00"
         },
+        {
+            "name": "hyperf/paginator",
+            "version": "v3.1.49",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/hyperf/paginator.git",
+                "reference": "dd9d59f5601fbdaa69f488348c7debd1931feb7b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/hyperf/paginator/zipball/dd9d59f5601fbdaa69f488348c7debd1931feb7b",
+                "reference": "dd9d59f5601fbdaa69f488348c7debd1931feb7b",
+                "shasum": ""
+            },
+            "require": {
+                "hyperf/contract": "~3.1.0",
+                "hyperf/support": "~3.1.0",
+                "hyperf/utils": "~3.1.0",
+                "php": ">=8.1"
+            },
+            "suggest": {
+                "hyperf/event": "Reqiured to use PageResolverListener.",
+                "hyperf/framework": "Reqiured to use PageResolverListener.",
+                "hyperf/http-server": "Reqiured to use PageResolverListener."
+            },
+            "type": "library",
+            "extra": {
+                "hyperf": {
+                    "config": "Hyperf\\Paginator\\ConfigProvider"
+                },
+                "branch-alias": {
+                    "dev-master": "3.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Hyperf\\Paginator\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "A paginator component for hyperf.",
+            "homepage": "https://hyperf.io",
+            "keywords": [
+                "hyperf",
+                "paginator",
+                "php"
+            ],
+            "support": {
+                "docs": "https://hyperf.wiki",
+                "issues": "https://github.com/hyperf/hyperf/issues",
+                "pull-request": "https://github.com/hyperf/hyperf/pulls",
+                "source": "https://github.com/hyperf/hyperf"
+            },
+            "funding": [
+                {
+                    "url": "https://hyperf.wiki/#/zh-cn/donate",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://opencollective.com/hyperf",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2024-12-17T09:42:13+00:00"
+        },
         {
             "name": "hyperf/pipeline",
             "version": "v3.1.15",
@@ -3943,6 +4071,71 @@
             ],
             "time": "2024-03-23T11:28:51+00:00"
         },
+        {
+            "name": "hyperf/snowflake",
+            "version": "v3.1.42",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/hyperf/snowflake.git",
+                "reference": "0271dfd9f0030e4f4cdd90f91428d1dbb35ecd56"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/hyperf/snowflake/zipball/0271dfd9f0030e4f4cdd90f91428d1dbb35ecd56",
+                "reference": "0271dfd9f0030e4f4cdd90f91428d1dbb35ecd56",
+                "shasum": ""
+            },
+            "require": {
+                "hyperf/contract": "~3.1.0",
+                "php": ">=8.1"
+            },
+            "suggest": {
+                "hyperf/config": "Required to read snowflake config.",
+                "hyperf/redis": "Required to use RedisMilliSecondMetaGenerator or RedisSecondMetaGenerator.",
+                "psr/container": "Required to use MetaGeneratorFactory."
+            },
+            "type": "library",
+            "extra": {
+                "hyperf": {
+                    "config": "Hyperf\\Snowflake\\ConfigProvider"
+                },
+                "branch-alias": {
+                    "dev-master": "3.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Hyperf\\Snowflake\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "A snowflake library",
+            "homepage": "https://hyperf.io",
+            "keywords": [
+                "php",
+                "snowflake"
+            ],
+            "support": {
+                "docs": "https://hyperf.wiki",
+                "issues": "https://github.com/hyperf/hyperf/issues",
+                "pull-request": "https://github.com/hyperf/hyperf/pulls",
+                "source": "https://github.com/hyperf/hyperf"
+            },
+            "funding": [
+                {
+                    "url": "https://hyperf.wiki/#/zh-cn/donate",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://opencollective.com/hyperf",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2024-09-25T02:54:12+00:00"
+        },
         {
             "name": "hyperf/stdlib",
             "version": "v3.1.15",
@@ -5555,6 +5748,174 @@
             },
             "time": "2019-03-08T08:55:37+00:00"
         },
+        {
+            "name": "ramsey/collection",
+            "version": "2.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ramsey/collection.git",
+                "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2",
+                "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.1"
+            },
+            "require-dev": {
+                "captainhook/plugin-composer": "^5.3",
+                "ergebnis/composer-normalize": "^2.45",
+                "fakerphp/faker": "^1.24",
+                "hamcrest/hamcrest-php": "^2.0",
+                "jangregor/phpstan-prophecy": "^2.1",
+                "mockery/mockery": "^1.6",
+                "php-parallel-lint/php-console-highlighter": "^1.0",
+                "php-parallel-lint/php-parallel-lint": "^1.4",
+                "phpspec/prophecy-phpunit": "^2.3",
+                "phpstan/extension-installer": "^1.4",
+                "phpstan/phpstan": "^2.1",
+                "phpstan/phpstan-mockery": "^2.0",
+                "phpstan/phpstan-phpunit": "^2.0",
+                "phpunit/phpunit": "^10.5",
+                "ramsey/coding-standard": "^2.3",
+                "ramsey/conventional-commits": "^1.6",
+                "roave/security-advisories": "dev-latest"
+            },
+            "type": "library",
+            "extra": {
+                "captainhook": {
+                    "force-install": true
+                },
+                "ramsey/conventional-commits": {
+                    "configFile": "conventional-commits.json"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Ramsey\\Collection\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ben Ramsey",
+                    "email": "ben@benramsey.com",
+                    "homepage": "https://benramsey.com"
+                }
+            ],
+            "description": "A PHP library for representing and manipulating collections.",
+            "keywords": [
+                "array",
+                "collection",
+                "hash",
+                "map",
+                "queue",
+                "set"
+            ],
+            "support": {
+                "issues": "https://github.com/ramsey/collection/issues",
+                "source": "https://github.com/ramsey/collection/tree/2.1.1"
+            },
+            "time": "2025-03-22T05:38:12+00:00"
+        },
+        {
+            "name": "ramsey/uuid",
+            "version": "4.7.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ramsey/uuid.git",
+                "reference": "91039bc1faa45ba123c4328958e620d382ec7088"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088",
+                "reference": "91039bc1faa45ba123c4328958e620d382ec7088",
+                "shasum": ""
+            },
+            "require": {
+                "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12",
+                "ext-json": "*",
+                "php": "^8.0",
+                "ramsey/collection": "^1.2 || ^2.0"
+            },
+            "replace": {
+                "rhumsaa/uuid": "self.version"
+            },
+            "require-dev": {
+                "captainhook/captainhook": "^5.10",
+                "captainhook/plugin-composer": "^5.3",
+                "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
+                "doctrine/annotations": "^1.8",
+                "ergebnis/composer-normalize": "^2.15",
+                "mockery/mockery": "^1.3",
+                "paragonie/random-lib": "^2",
+                "php-mock/php-mock": "^2.2",
+                "php-mock/php-mock-mockery": "^1.3",
+                "php-parallel-lint/php-parallel-lint": "^1.1",
+                "phpbench/phpbench": "^1.0",
+                "phpstan/extension-installer": "^1.1",
+                "phpstan/phpstan": "^1.8",
+                "phpstan/phpstan-mockery": "^1.1",
+                "phpstan/phpstan-phpunit": "^1.1",
+                "phpunit/phpunit": "^8.5 || ^9",
+                "ramsey/composer-repl": "^1.4",
+                "slevomat/coding-standard": "^8.4",
+                "squizlabs/php_codesniffer": "^3.5",
+                "vimeo/psalm": "^4.9"
+            },
+            "suggest": {
+                "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
+                "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
+                "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
+                "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
+                "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
+            },
+            "type": "library",
+            "extra": {
+                "captainhook": {
+                    "force-install": true
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/functions.php"
+                ],
+                "psr-4": {
+                    "Ramsey\\Uuid\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
+            "keywords": [
+                "guid",
+                "identifier",
+                "uuid"
+            ],
+            "support": {
+                "issues": "https://github.com/ramsey/uuid/issues",
+                "source": "https://github.com/ramsey/uuid/tree/4.7.6"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/ramsey",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-04-27T21:32:50+00:00"
+        },
         {
             "name": "swow/psr7-plus",
             "version": "v1.1.2",

+ 20 - 0
vendor/brick/math/LICENSE

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-present Benjamin Morel
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 39 - 0
vendor/brick/math/composer.json

@@ -0,0 +1,39 @@
+{
+    "name": "brick/math",
+    "description": "Arbitrary-precision arithmetic library",
+    "type": "library",
+    "keywords": [
+        "Brick",
+        "Math",
+        "Mathematics",
+        "Arbitrary-precision",
+        "Arithmetic",
+        "BigInteger",
+        "BigDecimal",
+        "BigRational",
+        "BigNumber",
+        "Bignum",
+        "Decimal",
+        "Rational",
+        "Integer"
+    ],
+    "license": "MIT",
+    "require": {
+        "php": "^8.1"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "^10.1",
+        "php-coveralls/php-coveralls": "^2.2",
+        "vimeo/psalm": "6.8.8"
+    },
+    "autoload": {
+        "psr-4": {
+            "Brick\\Math\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Brick\\Math\\Tests\\": "tests/"
+        }
+    }
+}

+ 70 - 0
vendor/brick/math/psalm-baseline.xml

@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<files psalm-version="6.8.8@1361cd33008feb3ae2b4a93f1860e14e538ec8c2">
+  <file src="src/BigInteger.php">
+    <FalsableReturnStatement>
+      <code><![CDATA[\hex2bin($hex)]]></code>
+    </FalsableReturnStatement>
+    <InvalidFalsableReturnType>
+      <code><![CDATA[string]]></code>
+    </InvalidFalsableReturnType>
+  </file>
+  <file src="src/Exception/DivisionByZeroException.php">
+    <ClassMustBeFinal>
+      <code><![CDATA[DivisionByZeroException]]></code>
+    </ClassMustBeFinal>
+  </file>
+  <file src="src/Exception/IntegerOverflowException.php">
+    <ClassMustBeFinal>
+      <code><![CDATA[IntegerOverflowException]]></code>
+    </ClassMustBeFinal>
+  </file>
+  <file src="src/Exception/NegativeNumberException.php">
+    <ClassMustBeFinal>
+      <code><![CDATA[NegativeNumberException]]></code>
+    </ClassMustBeFinal>
+  </file>
+  <file src="src/Exception/NumberFormatException.php">
+    <ClassMustBeFinal>
+      <code><![CDATA[NumberFormatException]]></code>
+    </ClassMustBeFinal>
+  </file>
+  <file src="src/Exception/RoundingNecessaryException.php">
+    <ClassMustBeFinal>
+      <code><![CDATA[RoundingNecessaryException]]></code>
+    </ClassMustBeFinal>
+  </file>
+  <file src="src/Internal/Calculator/BcMathCalculator.php">
+    <ClassMustBeFinal>
+      <code><![CDATA[BcMathCalculator]]></code>
+    </ClassMustBeFinal>
+  </file>
+  <file src="src/Internal/Calculator/GmpCalculator.php">
+    <ClassMustBeFinal>
+      <code><![CDATA[GmpCalculator]]></code>
+    </ClassMustBeFinal>
+  </file>
+  <file src="src/Internal/Calculator/NativeCalculator.php">
+    <ClassMustBeFinal>
+      <code><![CDATA[NativeCalculator]]></code>
+    </ClassMustBeFinal>
+    <InvalidOperand>
+      <code><![CDATA[$a * $b]]></code>
+      <code><![CDATA[$a * 1]]></code>
+      <code><![CDATA[$a + $b]]></code>
+      <code><![CDATA[$b * 1]]></code>
+      <code><![CDATA[$b * 1]]></code>
+      <code><![CDATA[$blockA * $blockB + $carry]]></code>
+      <code><![CDATA[$blockA + $blockB]]></code>
+      <code><![CDATA[$blockA + $blockB + $carry]]></code>
+      <code><![CDATA[$blockA - $blockB]]></code>
+      <code><![CDATA[$blockA - $blockB - $carry]]></code>
+      <code><![CDATA[$carry]]></code>
+      <code><![CDATA[$mul % $complement]]></code>
+      <code><![CDATA[$mul - $value]]></code>
+      <code><![CDATA[$nb - 1]]></code>
+      <code><![CDATA[$sum += $complement]]></code>
+      <code><![CDATA[($mul - $value) / $complement]]></code>
+      <code><![CDATA[($nb - 1) * 10]]></code>
+    </InvalidOperand>
+  </file>
+</files>

+ 792 - 0
vendor/brick/math/src/BigDecimal.php

@@ -0,0 +1,792 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math;
+
+use Brick\Math\Exception\DivisionByZeroException;
+use Brick\Math\Exception\MathException;
+use Brick\Math\Exception\NegativeNumberException;
+use Brick\Math\Internal\Calculator;
+use Override;
+
+/**
+ * Immutable, arbitrary-precision signed decimal numbers.
+ *
+ * @psalm-immutable
+ */
+final class BigDecimal extends BigNumber
+{
+    /**
+     * The unscaled value of this decimal number.
+     *
+     * This is a string of digits with an optional leading minus sign.
+     * No leading zero must be present.
+     * No leading minus sign must be present if the value is 0.
+     */
+    private readonly string $value;
+
+    /**
+     * The scale (number of digits after the decimal point) of this decimal number.
+     *
+     * This must be zero or more.
+     */
+    private readonly int $scale;
+
+    /**
+     * Protected constructor. Use a factory method to obtain an instance.
+     *
+     * @param string $value The unscaled value, validated.
+     * @param int    $scale The scale, validated.
+     */
+    protected function __construct(string $value, int $scale = 0)
+    {
+        $this->value = $value;
+        $this->scale = $scale;
+    }
+
+    /**
+     * @psalm-pure
+     */
+    #[Override]
+    protected static function from(BigNumber $number): static
+    {
+        return $number->toBigDecimal();
+    }
+
+    /**
+     * Creates a BigDecimal from an unscaled value and a scale.
+     *
+     * Example: `(12345, 3)` will result in the BigDecimal `12.345`.
+     *
+     * @param BigNumber|int|float|string $value The unscaled value. Must be convertible to a BigInteger.
+     * @param int                        $scale The scale of the number, positive or zero.
+     *
+     * @throws \InvalidArgumentException If the scale is negative.
+     *
+     * @psalm-pure
+     */
+    public static function ofUnscaledValue(BigNumber|int|float|string $value, int $scale = 0) : BigDecimal
+    {
+        if ($scale < 0) {
+            throw new \InvalidArgumentException('The scale cannot be negative.');
+        }
+
+        return new BigDecimal((string) BigInteger::of($value), $scale);
+    }
+
+    /**
+     * Returns a BigDecimal representing zero, with a scale of zero.
+     *
+     * @psalm-pure
+     */
+    public static function zero() : BigDecimal
+    {
+        /**
+         * @psalm-suppress ImpureStaticVariable
+         * @var BigDecimal|null $zero
+         */
+        static $zero;
+
+        if ($zero === null) {
+            $zero = new BigDecimal('0');
+        }
+
+        return $zero;
+    }
+
+    /**
+     * Returns a BigDecimal representing one, with a scale of zero.
+     *
+     * @psalm-pure
+     */
+    public static function one() : BigDecimal
+    {
+        /**
+         * @psalm-suppress ImpureStaticVariable
+         * @var BigDecimal|null $one
+         */
+        static $one;
+
+        if ($one === null) {
+            $one = new BigDecimal('1');
+        }
+
+        return $one;
+    }
+
+    /**
+     * Returns a BigDecimal representing ten, with a scale of zero.
+     *
+     * @psalm-pure
+     */
+    public static function ten() : BigDecimal
+    {
+        /**
+         * @psalm-suppress ImpureStaticVariable
+         * @var BigDecimal|null $ten
+         */
+        static $ten;
+
+        if ($ten === null) {
+            $ten = new BigDecimal('10');
+        }
+
+        return $ten;
+    }
+
+    /**
+     * Returns the sum of this number and the given one.
+     *
+     * The result has a scale of `max($this->scale, $that->scale)`.
+     *
+     * @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigDecimal.
+     *
+     * @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
+     */
+    public function plus(BigNumber|int|float|string $that) : BigDecimal
+    {
+        $that = BigDecimal::of($that);
+
+        if ($that->value === '0' && $that->scale <= $this->scale) {
+            return $this;
+        }
+
+        if ($this->value === '0' && $this->scale <= $that->scale) {
+            return $that;
+        }
+
+        [$a, $b] = $this->scaleValues($this, $that);
+
+        $value = Calculator::get()->add($a, $b);
+        $scale = $this->scale > $that->scale ? $this->scale : $that->scale;
+
+        return new BigDecimal($value, $scale);
+    }
+
+    /**
+     * Returns the difference of this number and the given one.
+     *
+     * The result has a scale of `max($this->scale, $that->scale)`.
+     *
+     * @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigDecimal.
+     *
+     * @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
+     */
+    public function minus(BigNumber|int|float|string $that) : BigDecimal
+    {
+        $that = BigDecimal::of($that);
+
+        if ($that->value === '0' && $that->scale <= $this->scale) {
+            return $this;
+        }
+
+        [$a, $b] = $this->scaleValues($this, $that);
+
+        $value = Calculator::get()->sub($a, $b);
+        $scale = $this->scale > $that->scale ? $this->scale : $that->scale;
+
+        return new BigDecimal($value, $scale);
+    }
+
+    /**
+     * Returns the product of this number and the given one.
+     *
+     * The result has a scale of `$this->scale + $that->scale`.
+     *
+     * @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigDecimal.
+     *
+     * @throws MathException If the multiplier is not a valid number, or is not convertible to a BigDecimal.
+     */
+    public function multipliedBy(BigNumber|int|float|string $that) : BigDecimal
+    {
+        $that = BigDecimal::of($that);
+
+        if ($that->value === '1' && $that->scale === 0) {
+            return $this;
+        }
+
+        if ($this->value === '1' && $this->scale === 0) {
+            return $that;
+        }
+
+        $value = Calculator::get()->mul($this->value, $that->value);
+        $scale = $this->scale + $that->scale;
+
+        return new BigDecimal($value, $scale);
+    }
+
+    /**
+     * Returns the result of the division of this number by the given one, at the given scale.
+     *
+     * @param BigNumber|int|float|string $that         The divisor.
+     * @param int|null                   $scale        The desired scale, or null to use the scale of this number.
+     * @param RoundingMode               $roundingMode An optional rounding mode, defaults to UNNECESSARY.
+     *
+     * @throws \InvalidArgumentException If the scale or rounding mode is invalid.
+     * @throws MathException             If the number is invalid, is zero, or rounding was necessary.
+     */
+    public function dividedBy(BigNumber|int|float|string $that, ?int $scale = null, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
+    {
+        $that = BigDecimal::of($that);
+
+        if ($that->isZero()) {
+            throw DivisionByZeroException::divisionByZero();
+        }
+
+        if ($scale === null) {
+            $scale = $this->scale;
+        } elseif ($scale < 0) {
+            throw new \InvalidArgumentException('Scale cannot be negative.');
+        }
+
+        if ($that->value === '1' && $that->scale === 0 && $scale === $this->scale) {
+            return $this;
+        }
+
+        $p = $this->valueWithMinScale($that->scale + $scale);
+        $q = $that->valueWithMinScale($this->scale - $scale);
+
+        $result = Calculator::get()->divRound($p, $q, $roundingMode);
+
+        return new BigDecimal($result, $scale);
+    }
+
+    /**
+     * Returns the exact result of the division of this number by the given one.
+     *
+     * The scale of the result is automatically calculated to fit all the fraction digits.
+     *
+     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
+     *
+     * @throws MathException If the divisor is not a valid number, is not convertible to a BigDecimal, is zero,
+     *                       or the result yields an infinite number of digits.
+     */
+    public function exactlyDividedBy(BigNumber|int|float|string $that) : BigDecimal
+    {
+        $that = BigDecimal::of($that);
+
+        if ($that->value === '0') {
+            throw DivisionByZeroException::divisionByZero();
+        }
+
+        [, $b] = $this->scaleValues($this, $that);
+
+        $d = \rtrim($b, '0');
+        $scale = \strlen($b) - \strlen($d);
+
+        $calculator = Calculator::get();
+
+        foreach ([5, 2] as $prime) {
+            for (;;) {
+                $lastDigit = (int) $d[-1];
+
+                if ($lastDigit % $prime !== 0) {
+                    break;
+                }
+
+                $d = $calculator->divQ($d, (string) $prime);
+                $scale++;
+            }
+        }
+
+        return $this->dividedBy($that, $scale)->stripTrailingZeros();
+    }
+
+    /**
+     * Returns this number exponentiated to the given value.
+     *
+     * The result has a scale of `$this->scale * $exponent`.
+     *
+     * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
+     */
+    public function power(int $exponent) : BigDecimal
+    {
+        if ($exponent === 0) {
+            return BigDecimal::one();
+        }
+
+        if ($exponent === 1) {
+            return $this;
+        }
+
+        if ($exponent < 0 || $exponent > Calculator::MAX_POWER) {
+            throw new \InvalidArgumentException(\sprintf(
+                'The exponent %d is not in the range 0 to %d.',
+                $exponent,
+                Calculator::MAX_POWER
+            ));
+        }
+
+        return new BigDecimal(Calculator::get()->pow($this->value, $exponent), $this->scale * $exponent);
+    }
+
+    /**
+     * Returns the quotient of the division of this number by the given one.
+     *
+     * The quotient has a scale of `0`.
+     *
+     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
+     *
+     * @throws MathException If the divisor is not a valid decimal number, or is zero.
+     */
+    public function quotient(BigNumber|int|float|string $that) : BigDecimal
+    {
+        $that = BigDecimal::of($that);
+
+        if ($that->isZero()) {
+            throw DivisionByZeroException::divisionByZero();
+        }
+
+        $p = $this->valueWithMinScale($that->scale);
+        $q = $that->valueWithMinScale($this->scale);
+
+        $quotient = Calculator::get()->divQ($p, $q);
+
+        return new BigDecimal($quotient, 0);
+    }
+
+    /**
+     * Returns the remainder of the division of this number by the given one.
+     *
+     * The remainder has a scale of `max($this->scale, $that->scale)`.
+     *
+     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
+     *
+     * @throws MathException If the divisor is not a valid decimal number, or is zero.
+     */
+    public function remainder(BigNumber|int|float|string $that) : BigDecimal
+    {
+        $that = BigDecimal::of($that);
+
+        if ($that->isZero()) {
+            throw DivisionByZeroException::divisionByZero();
+        }
+
+        $p = $this->valueWithMinScale($that->scale);
+        $q = $that->valueWithMinScale($this->scale);
+
+        $remainder = Calculator::get()->divR($p, $q);
+
+        $scale = $this->scale > $that->scale ? $this->scale : $that->scale;
+
+        return new BigDecimal($remainder, $scale);
+    }
+
+    /**
+     * Returns the quotient and remainder of the division of this number by the given one.
+     *
+     * The quotient has a scale of `0`, and the remainder has a scale of `max($this->scale, $that->scale)`.
+     *
+     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
+     *
+     * @return BigDecimal[] An array containing the quotient and the remainder.
+     *
+     * @psalm-return array{BigDecimal, BigDecimal}
+     *
+     * @throws MathException If the divisor is not a valid decimal number, or is zero.
+     */
+    public function quotientAndRemainder(BigNumber|int|float|string $that) : array
+    {
+        $that = BigDecimal::of($that);
+
+        if ($that->isZero()) {
+            throw DivisionByZeroException::divisionByZero();
+        }
+
+        $p = $this->valueWithMinScale($that->scale);
+        $q = $that->valueWithMinScale($this->scale);
+
+        [$quotient, $remainder] = Calculator::get()->divQR($p, $q);
+
+        $scale = $this->scale > $that->scale ? $this->scale : $that->scale;
+
+        $quotient = new BigDecimal($quotient, 0);
+        $remainder = new BigDecimal($remainder, $scale);
+
+        return [$quotient, $remainder];
+    }
+
+    /**
+     * Returns the square root of this number, rounded down to the given number of decimals.
+     *
+     * @throws \InvalidArgumentException If the scale is negative.
+     * @throws NegativeNumberException If this number is negative.
+     */
+    public function sqrt(int $scale) : BigDecimal
+    {
+        if ($scale < 0) {
+            throw new \InvalidArgumentException('Scale cannot be negative.');
+        }
+
+        if ($this->value === '0') {
+            return new BigDecimal('0', $scale);
+        }
+
+        if ($this->value[0] === '-') {
+            throw new NegativeNumberException('Cannot calculate the square root of a negative number.');
+        }
+
+        $value = $this->value;
+        $addDigits = 2 * $scale - $this->scale;
+
+        if ($addDigits > 0) {
+            // add zeros
+            $value .= \str_repeat('0', $addDigits);
+        } elseif ($addDigits < 0) {
+            // trim digits
+            if (-$addDigits >= \strlen($this->value)) {
+                // requesting a scale too low, will always yield a zero result
+                return new BigDecimal('0', $scale);
+            }
+
+            $value = \substr($value, 0, $addDigits);
+        }
+
+        $value = Calculator::get()->sqrt($value);
+
+        return new BigDecimal($value, $scale);
+    }
+
+    /**
+     * Returns a copy of this BigDecimal with the decimal point moved $n places to the left.
+     */
+    public function withPointMovedLeft(int $n) : BigDecimal
+    {
+        if ($n === 0) {
+            return $this;
+        }
+
+        if ($n < 0) {
+            return $this->withPointMovedRight(-$n);
+        }
+
+        return new BigDecimal($this->value, $this->scale + $n);
+    }
+
+    /**
+     * Returns a copy of this BigDecimal with the decimal point moved $n places to the right.
+     */
+    public function withPointMovedRight(int $n) : BigDecimal
+    {
+        if ($n === 0) {
+            return $this;
+        }
+
+        if ($n < 0) {
+            return $this->withPointMovedLeft(-$n);
+        }
+
+        $value = $this->value;
+        $scale = $this->scale - $n;
+
+        if ($scale < 0) {
+            if ($value !== '0') {
+                $value .= \str_repeat('0', -$scale);
+            }
+            $scale = 0;
+        }
+
+        return new BigDecimal($value, $scale);
+    }
+
+    /**
+     * Returns a copy of this BigDecimal with any trailing zeros removed from the fractional part.
+     */
+    public function stripTrailingZeros() : BigDecimal
+    {
+        if ($this->scale === 0) {
+            return $this;
+        }
+
+        $trimmedValue = \rtrim($this->value, '0');
+
+        if ($trimmedValue === '') {
+            return BigDecimal::zero();
+        }
+
+        $trimmableZeros = \strlen($this->value) - \strlen($trimmedValue);
+
+        if ($trimmableZeros === 0) {
+            return $this;
+        }
+
+        if ($trimmableZeros > $this->scale) {
+            $trimmableZeros = $this->scale;
+        }
+
+        $value = \substr($this->value, 0, -$trimmableZeros);
+        $scale = $this->scale - $trimmableZeros;
+
+        return new BigDecimal($value, $scale);
+    }
+
+    /**
+     * Returns the absolute value of this number.
+     */
+    public function abs() : BigDecimal
+    {
+        return $this->isNegative() ? $this->negated() : $this;
+    }
+
+    /**
+     * Returns the negated value of this number.
+     */
+    public function negated() : BigDecimal
+    {
+        return new BigDecimal(Calculator::get()->neg($this->value), $this->scale);
+    }
+
+    #[Override]
+    public function compareTo(BigNumber|int|float|string $that) : int
+    {
+        $that = BigNumber::of($that);
+
+        if ($that instanceof BigInteger) {
+            $that = $that->toBigDecimal();
+        }
+
+        if ($that instanceof BigDecimal) {
+            [$a, $b] = $this->scaleValues($this, $that);
+
+            return Calculator::get()->cmp($a, $b);
+        }
+
+        return - $that->compareTo($this);
+    }
+
+    #[Override]
+    public function getSign() : int
+    {
+        return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1);
+    }
+
+    public function getUnscaledValue() : BigInteger
+    {
+        return self::newBigInteger($this->value);
+    }
+
+    public function getScale() : int
+    {
+        return $this->scale;
+    }
+
+    /**
+     * Returns the number of significant digits in the number.
+     *
+     * This is the number of digits to both sides of the decimal point, stripped of leading zeros.
+     * The sign has no impact on the result.
+     *
+     * Examples:
+     *   0 => 0
+     *   0.0 => 0
+     *   123 => 3
+     *   123.456 => 6
+     *   0.00123 => 3
+     *   0.0012300 => 5
+     */
+    public function getPrecision(): int
+    {
+        $value = $this->value;
+
+        if ($value === '0') {
+            return 0;
+        }
+
+        $length = \strlen($value);
+
+        return ($value[0] === '-') ? $length - 1 : $length;
+    }
+
+    /**
+     * Returns a string representing the integral part of this decimal number.
+     *
+     * Example: `-123.456` => `-123`.
+     */
+    public function getIntegralPart() : string
+    {
+        if ($this->scale === 0) {
+            return $this->value;
+        }
+
+        $value = $this->getUnscaledValueWithLeadingZeros();
+
+        return \substr($value, 0, -$this->scale);
+    }
+
+    /**
+     * Returns a string representing the fractional part of this decimal number.
+     *
+     * If the scale is zero, an empty string is returned.
+     *
+     * Examples: `-123.456` => '456', `123` => ''.
+     */
+    public function getFractionalPart() : string
+    {
+        if ($this->scale === 0) {
+            return '';
+        }
+
+        $value = $this->getUnscaledValueWithLeadingZeros();
+
+        return \substr($value, -$this->scale);
+    }
+
+    /**
+     * Returns whether this decimal number has a non-zero fractional part.
+     */
+    public function hasNonZeroFractionalPart() : bool
+    {
+        return $this->getFractionalPart() !== \str_repeat('0', $this->scale);
+    }
+
+    #[Override]
+    public function toBigInteger() : BigInteger
+    {
+        $zeroScaleDecimal = $this->scale === 0 ? $this : $this->dividedBy(1, 0);
+
+        return self::newBigInteger($zeroScaleDecimal->value);
+    }
+
+    #[Override]
+    public function toBigDecimal() : BigDecimal
+    {
+        return $this;
+    }
+
+    #[Override]
+    public function toBigRational() : BigRational
+    {
+        $numerator = self::newBigInteger($this->value);
+        $denominator = self::newBigInteger('1' . \str_repeat('0', $this->scale));
+
+        return self::newBigRational($numerator, $denominator, false);
+    }
+
+    #[Override]
+    public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
+    {
+        if ($scale === $this->scale) {
+            return $this;
+        }
+
+        return $this->dividedBy(BigDecimal::one(), $scale, $roundingMode);
+    }
+
+    #[Override]
+    public function toInt() : int
+    {
+        return $this->toBigInteger()->toInt();
+    }
+
+    #[Override]
+    public function toFloat() : float
+    {
+        return (float) (string) $this;
+    }
+
+    #[Override]
+    public function __toString() : string
+    {
+        if ($this->scale === 0) {
+            return $this->value;
+        }
+
+        $value = $this->getUnscaledValueWithLeadingZeros();
+
+        return \substr($value, 0, -$this->scale) . '.' . \substr($value, -$this->scale);
+    }
+
+    /**
+     * This method is required for serializing the object and SHOULD NOT be accessed directly.
+     *
+     * @internal
+     *
+     * @return array{value: string, scale: int}
+     */
+    public function __serialize(): array
+    {
+        return ['value' => $this->value, 'scale' => $this->scale];
+    }
+
+    /**
+     * This method is only here to allow unserializing the object and cannot be accessed directly.
+     *
+     * @internal
+     * @psalm-suppress RedundantPropertyInitializationCheck
+     *
+     * @param array{value: string, scale: int} $data
+     *
+     * @throws \LogicException
+     */
+    public function __unserialize(array $data): void
+    {
+        if (isset($this->value)) {
+            throw new \LogicException('__unserialize() is an internal function, it must not be called directly.');
+        }
+
+        $this->value = $data['value'];
+        $this->scale = $data['scale'];
+    }
+
+    /**
+     * Puts the internal values of the given decimal numbers on the same scale.
+     *
+     * @return array{string, string} The scaled integer values of $x and $y.
+     */
+    private function scaleValues(BigDecimal $x, BigDecimal $y) : array
+    {
+        $a = $x->value;
+        $b = $y->value;
+
+        if ($b !== '0' && $x->scale > $y->scale) {
+            $b .= \str_repeat('0', $x->scale - $y->scale);
+        } elseif ($a !== '0' && $x->scale < $y->scale) {
+            $a .= \str_repeat('0', $y->scale - $x->scale);
+        }
+
+        return [$a, $b];
+    }
+
+    private function valueWithMinScale(int $scale) : string
+    {
+        $value = $this->value;
+
+        if ($this->value !== '0' && $scale > $this->scale) {
+            $value .= \str_repeat('0', $scale - $this->scale);
+        }
+
+        return $value;
+    }
+
+    /**
+     * Adds leading zeros if necessary to the unscaled value to represent the full decimal number.
+     */
+    private function getUnscaledValueWithLeadingZeros() : string
+    {
+        $value = $this->value;
+        $targetLength = $this->scale + 1;
+        $negative = ($value[0] === '-');
+        $length = \strlen($value);
+
+        if ($negative) {
+            $length--;
+        }
+
+        if ($length >= $targetLength) {
+            return $this->value;
+        }
+
+        if ($negative) {
+            $value = \substr($value, 1);
+        }
+
+        $value = \str_pad($value, $targetLength, '0', STR_PAD_LEFT);
+
+        if ($negative) {
+            $value = '-' . $value;
+        }
+
+        return $value;
+    }
+}

+ 1062 - 0
vendor/brick/math/src/BigInteger.php

@@ -0,0 +1,1062 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math;
+
+use Brick\Math\Exception\DivisionByZeroException;
+use Brick\Math\Exception\IntegerOverflowException;
+use Brick\Math\Exception\MathException;
+use Brick\Math\Exception\NegativeNumberException;
+use Brick\Math\Exception\NumberFormatException;
+use Brick\Math\Internal\Calculator;
+use Override;
+
+/**
+ * An arbitrary-size integer.
+ *
+ * All methods accepting a number as a parameter accept either a BigInteger instance,
+ * an integer, or a string representing an arbitrary size integer.
+ *
+ * @psalm-immutable
+ */
+final class BigInteger extends BigNumber
+{
+    /**
+     * The value, as a string of digits with optional leading minus sign.
+     *
+     * No leading zeros must be present.
+     * No leading minus sign must be present if the number is zero.
+     */
+    private readonly string $value;
+
+    /**
+     * Protected constructor. Use a factory method to obtain an instance.
+     *
+     * @param string $value A string of digits, with optional leading minus sign.
+     */
+    protected function __construct(string $value)
+    {
+        $this->value = $value;
+    }
+
+    /**
+     * @psalm-pure
+     */
+    #[Override]
+    protected static function from(BigNumber $number): static
+    {
+        return $number->toBigInteger();
+    }
+
+    /**
+     * Creates a number from a string in a given base.
+     *
+     * The string can optionally be prefixed with the `+` or `-` sign.
+     *
+     * Bases greater than 36 are not supported by this method, as there is no clear consensus on which of the lowercase
+     * or uppercase characters should come first. Instead, this method accepts any base up to 36, and does not
+     * differentiate lowercase and uppercase characters, which are considered equal.
+     *
+     * For bases greater than 36, and/or custom alphabets, use the fromArbitraryBase() method.
+     *
+     * @param string $number The number to convert, in the given base.
+     * @param int    $base   The base of the number, between 2 and 36.
+     *
+     * @throws NumberFormatException     If the number is empty, or contains invalid chars for the given base.
+     * @throws \InvalidArgumentException If the base is out of range.
+     *
+     * @psalm-pure
+     */
+    public static function fromBase(string $number, int $base) : BigInteger
+    {
+        if ($number === '') {
+            throw new NumberFormatException('The number cannot be empty.');
+        }
+
+        if ($base < 2 || $base > 36) {
+            throw new \InvalidArgumentException(\sprintf('Base %d is not in range 2 to 36.', $base));
+        }
+
+        if ($number[0] === '-') {
+            $sign = '-';
+            $number = \substr($number, 1);
+        } elseif ($number[0] === '+') {
+            $sign = '';
+            $number = \substr($number, 1);
+        } else {
+            $sign = '';
+        }
+
+        if ($number === '') {
+            throw new NumberFormatException('The number cannot be empty.');
+        }
+
+        $number = \ltrim($number, '0');
+
+        if ($number === '') {
+            // The result will be the same in any base, avoid further calculation.
+            return BigInteger::zero();
+        }
+
+        if ($number === '1') {
+            // The result will be the same in any base, avoid further calculation.
+            return new BigInteger($sign . '1');
+        }
+
+        $pattern = '/[^' . \substr(Calculator::ALPHABET, 0, $base) . ']/';
+
+        if (\preg_match($pattern, \strtolower($number), $matches) === 1) {
+            throw new NumberFormatException(\sprintf('"%s" is not a valid character in base %d.', $matches[0], $base));
+        }
+
+        if ($base === 10) {
+            // The number is usable as is, avoid further calculation.
+            return new BigInteger($sign . $number);
+        }
+
+        $result = Calculator::get()->fromBase($number, $base);
+
+        return new BigInteger($sign . $result);
+    }
+
+    /**
+     * Parses a string containing an integer in an arbitrary base, using a custom alphabet.
+     *
+     * Because this method accepts an alphabet with any character, including dash, it does not handle negative numbers.
+     *
+     * @param string $number   The number to parse.
+     * @param string $alphabet The alphabet, for example '01' for base 2, or '01234567' for base 8.
+     *
+     * @throws NumberFormatException     If the given number is empty or contains invalid chars for the given alphabet.
+     * @throws \InvalidArgumentException If the alphabet does not contain at least 2 chars.
+     *
+     * @psalm-pure
+     */
+    public static function fromArbitraryBase(string $number, string $alphabet) : BigInteger
+    {
+        if ($number === '') {
+            throw new NumberFormatException('The number cannot be empty.');
+        }
+
+        $base = \strlen($alphabet);
+
+        if ($base < 2) {
+            throw new \InvalidArgumentException('The alphabet must contain at least 2 chars.');
+        }
+
+        $pattern = '/[^' . \preg_quote($alphabet, '/') . ']/';
+
+        if (\preg_match($pattern, $number, $matches) === 1) {
+            throw NumberFormatException::charNotInAlphabet($matches[0]);
+        }
+
+        $number = Calculator::get()->fromArbitraryBase($number, $alphabet, $base);
+
+        return new BigInteger($number);
+    }
+
+    /**
+     * Translates a string of bytes containing the binary representation of a BigInteger into a BigInteger.
+     *
+     * The input string is assumed to be in big-endian byte-order: the most significant byte is in the zeroth element.
+     *
+     * If `$signed` is true, the input is assumed to be in two's-complement representation, and the leading bit is
+     * interpreted as a sign bit. If `$signed` is false, the input is interpreted as an unsigned number, and the
+     * resulting BigInteger will always be positive or zero.
+     *
+     * This method can be used to retrieve a number exported by `toBytes()`, as long as the `$signed` flags match.
+     *
+     * @param string $value  The byte string.
+     * @param bool   $signed Whether to interpret as a signed number in two's-complement representation with a leading
+     *                       sign bit.
+     *
+     * @throws NumberFormatException If the string is empty.
+     */
+    public static function fromBytes(string $value, bool $signed = true) : BigInteger
+    {
+        if ($value === '') {
+            throw new NumberFormatException('The byte string must not be empty.');
+        }
+
+        $twosComplement = false;
+
+        if ($signed) {
+            $x = \ord($value[0]);
+
+            if (($twosComplement = ($x >= 0x80))) {
+                $value = ~$value;
+            }
+        }
+
+        $number = self::fromBase(\bin2hex($value), 16);
+
+        if ($twosComplement) {
+            return $number->plus(1)->negated();
+        }
+
+        return $number;
+    }
+
+    /**
+     * Generates a pseudo-random number in the range 0 to 2^numBits - 1.
+     *
+     * Using the default random bytes generator, this method is suitable for cryptographic use.
+     *
+     * @psalm-param (callable(int): string)|null $randomBytesGenerator
+     *
+     * @param int           $numBits              The number of bits.
+     * @param callable|null $randomBytesGenerator A function that accepts a number of bytes as an integer, and returns a
+     *                                            string of random bytes of the given length. Defaults to the
+     *                                            `random_bytes()` function.
+     *
+     * @throws \InvalidArgumentException If $numBits is negative.
+     */
+    public static function randomBits(int $numBits, ?callable $randomBytesGenerator = null) : BigInteger
+    {
+        if ($numBits < 0) {
+            throw new \InvalidArgumentException('The number of bits cannot be negative.');
+        }
+
+        if ($numBits === 0) {
+            return BigInteger::zero();
+        }
+
+        if ($randomBytesGenerator === null) {
+            $randomBytesGenerator = random_bytes(...);
+        }
+
+        /** @var int<1, max> $byteLength */
+        $byteLength = \intdiv($numBits - 1, 8) + 1;
+
+        $extraBits = ($byteLength * 8 - $numBits);
+        $bitmask   = \chr(0xFF >> $extraBits);
+
+        $randomBytes    = $randomBytesGenerator($byteLength);
+        $randomBytes[0] = $randomBytes[0] & $bitmask;
+
+        return self::fromBytes($randomBytes, false);
+    }
+
+    /**
+     * Generates a pseudo-random number between `$min` and `$max`.
+     *
+     * Using the default random bytes generator, this method is suitable for cryptographic use.
+     *
+     * @psalm-param (callable(int): string)|null $randomBytesGenerator
+     *
+     * @param BigNumber|int|float|string $min                  The lower bound. Must be convertible to a BigInteger.
+     * @param BigNumber|int|float|string $max                  The upper bound. Must be convertible to a BigInteger.
+     * @param callable|null              $randomBytesGenerator A function that accepts a number of bytes as an integer,
+     *                                                         and returns a string of random bytes of the given length.
+     *                                                         Defaults to the `random_bytes()` function.
+     *
+     * @throws MathException If one of the parameters cannot be converted to a BigInteger,
+     *                       or `$min` is greater than `$max`.
+     */
+    public static function randomRange(
+        BigNumber|int|float|string $min,
+        BigNumber|int|float|string $max,
+        ?callable $randomBytesGenerator = null
+    ) : BigInteger {
+        $min = BigInteger::of($min);
+        $max = BigInteger::of($max);
+
+        if ($min->isGreaterThan($max)) {
+            throw new MathException('$min cannot be greater than $max.');
+        }
+
+        if ($min->isEqualTo($max)) {
+            return $min;
+        }
+
+        $diff      = $max->minus($min);
+        $bitLength = $diff->getBitLength();
+
+        // try until the number is in range (50% to 100% chance of success)
+        do {
+            $randomNumber = self::randomBits($bitLength, $randomBytesGenerator);
+        } while ($randomNumber->isGreaterThan($diff));
+
+        return $randomNumber->plus($min);
+    }
+
+    /**
+     * Returns a BigInteger representing zero.
+     *
+     * @psalm-pure
+     */
+    public static function zero() : BigInteger
+    {
+        /**
+         * @psalm-suppress ImpureStaticVariable
+         * @var BigInteger|null $zero
+         */
+        static $zero;
+
+        if ($zero === null) {
+            $zero = new BigInteger('0');
+        }
+
+        return $zero;
+    }
+
+    /**
+     * Returns a BigInteger representing one.
+     *
+     * @psalm-pure
+     */
+    public static function one() : BigInteger
+    {
+        /**
+         * @psalm-suppress ImpureStaticVariable
+         * @var BigInteger|null $one
+         */
+        static $one;
+
+        if ($one === null) {
+            $one = new BigInteger('1');
+        }
+
+        return $one;
+    }
+
+    /**
+     * Returns a BigInteger representing ten.
+     *
+     * @psalm-pure
+     */
+    public static function ten() : BigInteger
+    {
+        /**
+         * @psalm-suppress ImpureStaticVariable
+         * @var BigInteger|null $ten
+         */
+        static $ten;
+
+        if ($ten === null) {
+            $ten = new BigInteger('10');
+        }
+
+        return $ten;
+    }
+
+    public static function gcdMultiple(BigInteger $a, BigInteger ...$n): BigInteger
+    {
+        $result = $a;
+
+        foreach ($n as $next) {
+            $result = $result->gcd($next);
+
+            if ($result->isEqualTo(1)) {
+                return $result;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns the sum of this number and the given one.
+     *
+     * @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigInteger.
+     *
+     * @throws MathException If the number is not valid, or is not convertible to a BigInteger.
+     */
+    public function plus(BigNumber|int|float|string $that) : BigInteger
+    {
+        $that = BigInteger::of($that);
+
+        if ($that->value === '0') {
+            return $this;
+        }
+
+        if ($this->value === '0') {
+            return $that;
+        }
+
+        $value = Calculator::get()->add($this->value, $that->value);
+
+        return new BigInteger($value);
+    }
+
+    /**
+     * Returns the difference of this number and the given one.
+     *
+     * @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigInteger.
+     *
+     * @throws MathException If the number is not valid, or is not convertible to a BigInteger.
+     */
+    public function minus(BigNumber|int|float|string $that) : BigInteger
+    {
+        $that = BigInteger::of($that);
+
+        if ($that->value === '0') {
+            return $this;
+        }
+
+        $value = Calculator::get()->sub($this->value, $that->value);
+
+        return new BigInteger($value);
+    }
+
+    /**
+     * Returns the product of this number and the given one.
+     *
+     * @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigInteger.
+     *
+     * @throws MathException If the multiplier is not a valid number, or is not convertible to a BigInteger.
+     */
+    public function multipliedBy(BigNumber|int|float|string $that) : BigInteger
+    {
+        $that = BigInteger::of($that);
+
+        if ($that->value === '1') {
+            return $this;
+        }
+
+        if ($this->value === '1') {
+            return $that;
+        }
+
+        $value = Calculator::get()->mul($this->value, $that->value);
+
+        return new BigInteger($value);
+    }
+
+    /**
+     * Returns the result of the division of this number by the given one.
+     *
+     * @param BigNumber|int|float|string $that         The divisor. Must be convertible to a BigInteger.
+     * @param RoundingMode               $roundingMode An optional rounding mode, defaults to UNNECESSARY.
+     *
+     * @throws MathException If the divisor is not a valid number, is not convertible to a BigInteger, is zero,
+     *                       or RoundingMode::UNNECESSARY is used and the remainder is not zero.
+     */
+    public function dividedBy(BigNumber|int|float|string $that, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigInteger
+    {
+        $that = BigInteger::of($that);
+
+        if ($that->value === '1') {
+            return $this;
+        }
+
+        if ($that->value === '0') {
+            throw DivisionByZeroException::divisionByZero();
+        }
+
+        $result = Calculator::get()->divRound($this->value, $that->value, $roundingMode);
+
+        return new BigInteger($result);
+    }
+
+    /**
+     * Returns this number exponentiated to the given value.
+     *
+     * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
+     */
+    public function power(int $exponent) : BigInteger
+    {
+        if ($exponent === 0) {
+            return BigInteger::one();
+        }
+
+        if ($exponent === 1) {
+            return $this;
+        }
+
+        if ($exponent < 0 || $exponent > Calculator::MAX_POWER) {
+            throw new \InvalidArgumentException(\sprintf(
+                'The exponent %d is not in the range 0 to %d.',
+                $exponent,
+                Calculator::MAX_POWER
+            ));
+        }
+
+        return new BigInteger(Calculator::get()->pow($this->value, $exponent));
+    }
+
+    /**
+     * Returns the quotient of the division of this number by the given one.
+     *
+     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger.
+     *
+     * @throws DivisionByZeroException If the divisor is zero.
+     */
+    public function quotient(BigNumber|int|float|string $that) : BigInteger
+    {
+        $that = BigInteger::of($that);
+
+        if ($that->value === '1') {
+            return $this;
+        }
+
+        if ($that->value === '0') {
+            throw DivisionByZeroException::divisionByZero();
+        }
+
+        $quotient = Calculator::get()->divQ($this->value, $that->value);
+
+        return new BigInteger($quotient);
+    }
+
+    /**
+     * Returns the remainder of the division of this number by the given one.
+     *
+     * The remainder, when non-zero, has the same sign as the dividend.
+     *
+     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger.
+     *
+     * @throws DivisionByZeroException If the divisor is zero.
+     */
+    public function remainder(BigNumber|int|float|string $that) : BigInteger
+    {
+        $that = BigInteger::of($that);
+
+        if ($that->value === '1') {
+            return BigInteger::zero();
+        }
+
+        if ($that->value === '0') {
+            throw DivisionByZeroException::divisionByZero();
+        }
+
+        $remainder = Calculator::get()->divR($this->value, $that->value);
+
+        return new BigInteger($remainder);
+    }
+
+    /**
+     * Returns the quotient and remainder of the division of this number by the given one.
+     *
+     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger.
+     *
+     * @return BigInteger[] An array containing the quotient and the remainder.
+     *
+     * @psalm-return array{BigInteger, BigInteger}
+     *
+     * @throws DivisionByZeroException If the divisor is zero.
+     */
+    public function quotientAndRemainder(BigNumber|int|float|string $that) : array
+    {
+        $that = BigInteger::of($that);
+
+        if ($that->value === '0') {
+            throw DivisionByZeroException::divisionByZero();
+        }
+
+        [$quotient, $remainder] = Calculator::get()->divQR($this->value, $that->value);
+
+        return [
+            new BigInteger($quotient),
+            new BigInteger($remainder)
+        ];
+    }
+
+    /**
+     * Returns the modulo of this number and the given one.
+     *
+     * The modulo operation yields the same result as the remainder operation when both operands are of the same sign,
+     * and may differ when signs are different.
+     *
+     * The result of the modulo operation, when non-zero, has the same sign as the divisor.
+     *
+     * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger.
+     *
+     * @throws DivisionByZeroException If the divisor is zero.
+     */
+    public function mod(BigNumber|int|float|string $that) : BigInteger
+    {
+        $that = BigInteger::of($that);
+
+        if ($that->value === '0') {
+            throw DivisionByZeroException::modulusMustNotBeZero();
+        }
+
+        $value = Calculator::get()->mod($this->value, $that->value);
+
+        return new BigInteger($value);
+    }
+
+    /**
+     * Returns the modular multiplicative inverse of this BigInteger modulo $m.
+     *
+     * @throws DivisionByZeroException If $m is zero.
+     * @throws NegativeNumberException If $m is negative.
+     * @throws MathException           If this BigInteger has no multiplicative inverse mod m (that is, this BigInteger
+     *                                 is not relatively prime to m).
+     */
+    public function modInverse(BigInteger $m) : BigInteger
+    {
+        if ($m->value === '0') {
+            throw DivisionByZeroException::modulusMustNotBeZero();
+        }
+
+        if ($m->isNegative()) {
+            throw new NegativeNumberException('Modulus must not be negative.');
+        }
+
+        if ($m->value === '1') {
+            return BigInteger::zero();
+        }
+
+        $value = Calculator::get()->modInverse($this->value, $m->value);
+
+        if ($value === null) {
+            throw new MathException('Unable to compute the modInverse for the given modulus.');
+        }
+
+        return new BigInteger($value);
+    }
+
+    /**
+     * Returns this number raised into power with modulo.
+     *
+     * This operation only works on positive numbers.
+     *
+     * @param BigNumber|int|float|string $exp The exponent. Must be positive or zero.
+     * @param BigNumber|int|float|string $mod The modulus. Must be strictly positive.
+     *
+     * @throws NegativeNumberException If any of the operands is negative.
+     * @throws DivisionByZeroException If the modulus is zero.
+     */
+    public function modPow(BigNumber|int|float|string $exp, BigNumber|int|float|string $mod) : BigInteger
+    {
+        $exp = BigInteger::of($exp);
+        $mod = BigInteger::of($mod);
+
+        if ($this->isNegative() || $exp->isNegative() || $mod->isNegative()) {
+            throw new NegativeNumberException('The operands cannot be negative.');
+        }
+
+        if ($mod->isZero()) {
+            throw DivisionByZeroException::modulusMustNotBeZero();
+        }
+
+        $result = Calculator::get()->modPow($this->value, $exp->value, $mod->value);
+
+        return new BigInteger($result);
+    }
+
+    /**
+     * Returns the greatest common divisor of this number and the given one.
+     *
+     * The GCD is always positive, unless both operands are zero, in which case it is zero.
+     *
+     * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number.
+     */
+    public function gcd(BigNumber|int|float|string $that) : BigInteger
+    {
+        $that = BigInteger::of($that);
+
+        if ($that->value === '0' && $this->value[0] !== '-') {
+            return $this;
+        }
+
+        if ($this->value === '0' && $that->value[0] !== '-') {
+            return $that;
+        }
+
+        $value = Calculator::get()->gcd($this->value, $that->value);
+
+        return new BigInteger($value);
+    }
+
+    /**
+     * Returns the integer square root number of this number, rounded down.
+     *
+     * The result is the largest x such that x² ≤ n.
+     *
+     * @throws NegativeNumberException If this number is negative.
+     */
+    public function sqrt() : BigInteger
+    {
+        if ($this->value[0] === '-') {
+            throw new NegativeNumberException('Cannot calculate the square root of a negative number.');
+        }
+
+        $value = Calculator::get()->sqrt($this->value);
+
+        return new BigInteger($value);
+    }
+
+    /**
+     * Returns the absolute value of this number.
+     */
+    public function abs() : BigInteger
+    {
+        return $this->isNegative() ? $this->negated() : $this;
+    }
+
+    /**
+     * Returns the inverse of this number.
+     */
+    public function negated() : BigInteger
+    {
+        return new BigInteger(Calculator::get()->neg($this->value));
+    }
+
+    /**
+     * Returns the integer bitwise-and combined with another integer.
+     *
+     * This method returns a negative BigInteger if and only if both operands are negative.
+     *
+     * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number.
+     */
+    public function and(BigNumber|int|float|string $that) : BigInteger
+    {
+        $that = BigInteger::of($that);
+
+        return new BigInteger(Calculator::get()->and($this->value, $that->value));
+    }
+
+    /**
+     * Returns the integer bitwise-or combined with another integer.
+     *
+     * This method returns a negative BigInteger if and only if either of the operands is negative.
+     *
+     * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number.
+     */
+    public function or(BigNumber|int|float|string $that) : BigInteger
+    {
+        $that = BigInteger::of($that);
+
+        return new BigInteger(Calculator::get()->or($this->value, $that->value));
+    }
+
+    /**
+     * Returns the integer bitwise-xor combined with another integer.
+     *
+     * This method returns a negative BigInteger if and only if exactly one of the operands is negative.
+     *
+     * @param BigNumber|int|float|string $that The operand. Must be convertible to an integer number.
+     */
+    public function xor(BigNumber|int|float|string $that) : BigInteger
+    {
+        $that = BigInteger::of($that);
+
+        return new BigInteger(Calculator::get()->xor($this->value, $that->value));
+    }
+
+    /**
+     * Returns the bitwise-not of this BigInteger.
+     */
+    public function not() : BigInteger
+    {
+        return $this->negated()->minus(1);
+    }
+
+    /**
+     * Returns the integer left shifted by a given number of bits.
+     */
+    public function shiftedLeft(int $distance) : BigInteger
+    {
+        if ($distance === 0) {
+            return $this;
+        }
+
+        if ($distance < 0) {
+            return $this->shiftedRight(- $distance);
+        }
+
+        return $this->multipliedBy(BigInteger::of(2)->power($distance));
+    }
+
+    /**
+     * Returns the integer right shifted by a given number of bits.
+     */
+    public function shiftedRight(int $distance) : BigInteger
+    {
+        if ($distance === 0) {
+            return $this;
+        }
+
+        if ($distance < 0) {
+            return $this->shiftedLeft(- $distance);
+        }
+
+        $operand = BigInteger::of(2)->power($distance);
+
+        if ($this->isPositiveOrZero()) {
+            return $this->quotient($operand);
+        }
+
+        return $this->dividedBy($operand, RoundingMode::UP);
+    }
+
+    /**
+     * Returns the number of bits in the minimal two's-complement representation of this BigInteger, excluding a sign bit.
+     *
+     * For positive BigIntegers, this is equivalent to the number of bits in the ordinary binary representation.
+     * Computes (ceil(log2(this < 0 ? -this : this+1))).
+     */
+    public function getBitLength() : int
+    {
+        if ($this->value === '0') {
+            return 0;
+        }
+
+        if ($this->isNegative()) {
+            return $this->abs()->minus(1)->getBitLength();
+        }
+
+        return \strlen($this->toBase(2));
+    }
+
+    /**
+     * Returns the index of the rightmost (lowest-order) one bit in this BigInteger.
+     *
+     * Returns -1 if this BigInteger contains no one bits.
+     */
+    public function getLowestSetBit() : int
+    {
+        $n = $this;
+        $bitLength = $this->getBitLength();
+
+        for ($i = 0; $i <= $bitLength; $i++) {
+            if ($n->isOdd()) {
+                return $i;
+            }
+
+            $n = $n->shiftedRight(1);
+        }
+
+        return -1;
+    }
+
+    /**
+     * Returns whether this number is even.
+     */
+    public function isEven() : bool
+    {
+        return \in_array($this->value[-1], ['0', '2', '4', '6', '8'], true);
+    }
+
+    /**
+     * Returns whether this number is odd.
+     */
+    public function isOdd() : bool
+    {
+        return \in_array($this->value[-1], ['1', '3', '5', '7', '9'], true);
+    }
+
+    /**
+     * Returns true if and only if the designated bit is set.
+     *
+     * Computes ((this & (1<<n)) != 0).
+     *
+     * @param int $n The bit to test, 0-based.
+     *
+     * @throws \InvalidArgumentException If the bit to test is negative.
+     */
+    public function testBit(int $n) : bool
+    {
+        if ($n < 0) {
+            throw new \InvalidArgumentException('The bit to test cannot be negative.');
+        }
+
+        return $this->shiftedRight($n)->isOdd();
+    }
+
+    #[Override]
+    public function compareTo(BigNumber|int|float|string $that) : int
+    {
+        $that = BigNumber::of($that);
+
+        if ($that instanceof BigInteger) {
+            return Calculator::get()->cmp($this->value, $that->value);
+        }
+
+        return - $that->compareTo($this);
+    }
+
+    #[Override]
+    public function getSign() : int
+    {
+        return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1);
+    }
+
+    #[Override]
+    public function toBigInteger() : BigInteger
+    {
+        return $this;
+    }
+
+    #[Override]
+    public function toBigDecimal() : BigDecimal
+    {
+        return self::newBigDecimal($this->value);
+    }
+
+    #[Override]
+    public function toBigRational() : BigRational
+    {
+        return self::newBigRational($this, BigInteger::one(), false);
+    }
+
+    #[Override]
+    public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
+    {
+        return $this->toBigDecimal()->toScale($scale, $roundingMode);
+    }
+
+    #[Override]
+    public function toInt() : int
+    {
+        $intValue = (int) $this->value;
+
+        if ($this->value !== (string) $intValue) {
+            throw IntegerOverflowException::toIntOverflow($this);
+        }
+
+        return $intValue;
+    }
+
+    #[Override]
+    public function toFloat() : float
+    {
+        return (float) $this->value;
+    }
+
+    /**
+     * Returns a string representation of this number in the given base.
+     *
+     * The output will always be lowercase for bases greater than 10.
+     *
+     * @throws \InvalidArgumentException If the base is out of range.
+     */
+    public function toBase(int $base) : string
+    {
+        if ($base === 10) {
+            return $this->value;
+        }
+
+        if ($base < 2 || $base > 36) {
+            throw new \InvalidArgumentException(\sprintf('Base %d is out of range [2, 36]', $base));
+        }
+
+        return Calculator::get()->toBase($this->value, $base);
+    }
+
+    /**
+     * Returns a string representation of this number in an arbitrary base with a custom alphabet.
+     *
+     * Because this method accepts an alphabet with any character, including dash, it does not handle negative numbers;
+     * a NegativeNumberException will be thrown when attempting to call this method on a negative number.
+     *
+     * @param string $alphabet The alphabet, for example '01' for base 2, or '01234567' for base 8.
+     *
+     * @throws NegativeNumberException   If this number is negative.
+     * @throws \InvalidArgumentException If the given alphabet does not contain at least 2 chars.
+     */
+    public function toArbitraryBase(string $alphabet) : string
+    {
+        $base = \strlen($alphabet);
+
+        if ($base < 2) {
+            throw new \InvalidArgumentException('The alphabet must contain at least 2 chars.');
+        }
+
+        if ($this->value[0] === '-') {
+            throw new NegativeNumberException(__FUNCTION__ . '() does not support negative numbers.');
+        }
+
+        return Calculator::get()->toArbitraryBase($this->value, $alphabet, $base);
+    }
+
+    /**
+     * Returns a string of bytes containing the binary representation of this BigInteger.
+     *
+     * The string is in big-endian byte-order: the most significant byte is in the zeroth element.
+     *
+     * If `$signed` is true, the output will be in two's-complement representation, and a sign bit will be prepended to
+     * the output. If `$signed` is false, no sign bit will be prepended, and this method will throw an exception if the
+     * number is negative.
+     *
+     * The string will contain the minimum number of bytes required to represent this BigInteger, including a sign bit
+     * if `$signed` is true.
+     *
+     * This representation is compatible with the `fromBytes()` factory method, as long as the `$signed` flags match.
+     *
+     * @param bool $signed Whether to output a signed number in two's-complement representation with a leading sign bit.
+     *
+     * @throws NegativeNumberException If $signed is false, and the number is negative.
+     */
+    public function toBytes(bool $signed = true) : string
+    {
+        if (! $signed && $this->isNegative()) {
+            throw new NegativeNumberException('Cannot convert a negative number to a byte string when $signed is false.');
+        }
+
+        $hex = $this->abs()->toBase(16);
+
+        if (\strlen($hex) % 2 !== 0) {
+            $hex = '0' . $hex;
+        }
+
+        $baseHexLength = \strlen($hex);
+
+        if ($signed) {
+            if ($this->isNegative()) {
+                $bin = \hex2bin($hex);
+                assert($bin !== false);
+
+                $hex = \bin2hex(~$bin);
+                $hex = self::fromBase($hex, 16)->plus(1)->toBase(16);
+
+                $hexLength = \strlen($hex);
+
+                if ($hexLength < $baseHexLength) {
+                    $hex = \str_repeat('0', $baseHexLength - $hexLength) . $hex;
+                }
+
+                if ($hex[0] < '8') {
+                    $hex = 'FF' . $hex;
+                }
+            } else {
+                if ($hex[0] >= '8') {
+                    $hex = '00' . $hex;
+                }
+            }
+        }
+
+        return \hex2bin($hex);
+    }
+
+    #[Override]
+    public function __toString() : string
+    {
+        return $this->value;
+    }
+
+    /**
+     * This method is required for serializing the object and SHOULD NOT be accessed directly.
+     *
+     * @internal
+     *
+     * @return array{value: string}
+     */
+    public function __serialize(): array
+    {
+        return ['value' => $this->value];
+    }
+
+    /**
+     * This method is only here to allow unserializing the object and cannot be accessed directly.
+     *
+     * @internal
+     * @psalm-suppress RedundantPropertyInitializationCheck
+     *
+     * @param array{value: string} $data
+     *
+     * @throws \LogicException
+     */
+    public function __unserialize(array $data): void
+    {
+        if (isset($this->value)) {
+            throw new \LogicException('__unserialize() is an internal function, it must not be called directly.');
+        }
+
+        $this->value = $data['value'];
+    }
+}

+ 515 - 0
vendor/brick/math/src/BigNumber.php

@@ -0,0 +1,515 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math;
+
+use Brick\Math\Exception\DivisionByZeroException;
+use Brick\Math\Exception\MathException;
+use Brick\Math\Exception\NumberFormatException;
+use Brick\Math\Exception\RoundingNecessaryException;
+use Override;
+
+/**
+ * Common interface for arbitrary-precision rational numbers.
+ *
+ * @psalm-immutable
+ */
+abstract class BigNumber implements \JsonSerializable
+{
+    /**
+     * The regular expression used to parse integer or decimal numbers.
+     */
+    private const PARSE_REGEXP_NUMERICAL =
+        '/^' .
+            '(?<sign>[\-\+])?' .
+            '(?<integral>[0-9]+)?' .
+            '(?<point>\.)?' .
+            '(?<fractional>[0-9]+)?' .
+            '(?:[eE](?<exponent>[\-\+]?[0-9]+))?' .
+        '$/';
+
+    /**
+     * The regular expression used to parse rational numbers.
+     */
+    private const PARSE_REGEXP_RATIONAL =
+        '/^' .
+            '(?<sign>[\-\+])?' .
+            '(?<numerator>[0-9]+)' .
+            '\/?' .
+            '(?<denominator>[0-9]+)' .
+        '$/';
+
+    /**
+     * Creates a BigNumber of the given value.
+     *
+     * The concrete return type is dependent on the given value, with the following rules:
+     *
+     * - BigNumber instances are returned as is
+     * - integer numbers are returned as BigInteger
+     * - floating point numbers are converted to a string then parsed as such
+     * - strings containing a `/` character are returned as BigRational
+     * - strings containing a `.` character or using an exponential notation are returned as BigDecimal
+     * - strings containing only digits with an optional leading `+` or `-` sign are returned as BigInteger
+     *
+     * @throws NumberFormatException If the format of the number is not valid.
+     * @throws DivisionByZeroException If the value represents a rational number with a denominator of zero.
+     * @throws RoundingNecessaryException If the value cannot be converted to an instance of the subclass without rounding.
+     *
+     * @psalm-pure
+     */
+    final public static function of(BigNumber|int|float|string $value) : static
+    {
+        $value = self::_of($value);
+
+        if (static::class === BigNumber::class) {
+            // https://github.com/vimeo/psalm/issues/10309
+            assert($value instanceof static);
+
+            return $value;
+        }
+
+        return static::from($value);
+    }
+
+    /**
+     * @throws NumberFormatException If the format of the number is not valid.
+     * @throws DivisionByZeroException If the value represents a rational number with a denominator of zero.
+     *
+     * @psalm-pure
+     */
+    private static function _of(BigNumber|int|float|string $value) : BigNumber
+    {
+        if ($value instanceof BigNumber) {
+            return $value;
+        }
+
+        if (\is_int($value)) {
+            return new BigInteger((string) $value);
+        }
+
+        if (is_float($value)) {
+            $value = (string) $value;
+        }
+
+        if (str_contains($value, '/')) {
+            // Rational number
+            if (\preg_match(self::PARSE_REGEXP_RATIONAL, $value, $matches, PREG_UNMATCHED_AS_NULL) !== 1) {
+                throw NumberFormatException::invalidFormat($value);
+            }
+
+            $sign        = $matches['sign'];
+            $numerator   = $matches['numerator'];
+            $denominator = $matches['denominator'];
+
+            assert($numerator !== null);
+            assert($denominator !== null);
+
+            $numerator   = self::cleanUp($sign, $numerator);
+            $denominator = self::cleanUp(null, $denominator);
+
+            if ($denominator === '0') {
+                throw DivisionByZeroException::denominatorMustNotBeZero();
+            }
+
+            return new BigRational(
+                new BigInteger($numerator),
+                new BigInteger($denominator),
+                false
+            );
+        } else {
+            // Integer or decimal number
+            if (\preg_match(self::PARSE_REGEXP_NUMERICAL, $value, $matches, PREG_UNMATCHED_AS_NULL) !== 1) {
+                throw NumberFormatException::invalidFormat($value);
+            }
+
+            $sign = $matches['sign'];
+            $point = $matches['point'];
+            $integral = $matches['integral'];
+            $fractional = $matches['fractional'];
+            $exponent = $matches['exponent'];
+
+            if ($integral === null && $fractional === null) {
+                throw NumberFormatException::invalidFormat($value);
+            }
+
+            if ($integral === null) {
+                $integral = '0';
+            }
+
+            if ($point !== null || $exponent !== null) {
+                $fractional = ($fractional ?? '');
+                $exponent = ($exponent !== null) ? (int)$exponent : 0;
+
+                if ($exponent === PHP_INT_MIN || $exponent === PHP_INT_MAX) {
+                    throw new NumberFormatException('Exponent too large.');
+                }
+
+                $unscaledValue = self::cleanUp($sign, $integral . $fractional);
+
+                $scale = \strlen($fractional) - $exponent;
+
+                if ($scale < 0) {
+                    if ($unscaledValue !== '0') {
+                        $unscaledValue .= \str_repeat('0', -$scale);
+                    }
+                    $scale = 0;
+                }
+
+                return new BigDecimal($unscaledValue, $scale);
+            }
+
+            $integral = self::cleanUp($sign, $integral);
+
+            return new BigInteger($integral);
+        }
+    }
+
+    /**
+     * Overridden by subclasses to convert a BigNumber to an instance of the subclass.
+     *
+     * @throws RoundingNecessaryException If the value cannot be converted.
+     *
+     * @psalm-pure
+     */
+    abstract protected static function from(BigNumber $number): static;
+
+    /**
+     * Proxy method to access BigInteger's protected constructor from sibling classes.
+     *
+     * @internal
+     * @psalm-pure
+     */
+    final protected function newBigInteger(string $value) : BigInteger
+    {
+        return new BigInteger($value);
+    }
+
+    /**
+     * Proxy method to access BigDecimal's protected constructor from sibling classes.
+     *
+     * @internal
+     * @psalm-pure
+     */
+    final protected function newBigDecimal(string $value, int $scale = 0) : BigDecimal
+    {
+        return new BigDecimal($value, $scale);
+    }
+
+    /**
+     * Proxy method to access BigRational's protected constructor from sibling classes.
+     *
+     * @internal
+     * @psalm-pure
+     */
+    final protected function newBigRational(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator) : BigRational
+    {
+        return new BigRational($numerator, $denominator, $checkDenominator);
+    }
+
+    /**
+     * Returns the minimum of the given values.
+     *
+     * @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
+     *                                              to an instance of the class this method is called on.
+     *
+     * @throws \InvalidArgumentException If no values are given.
+     * @throws MathException             If an argument is not valid.
+     *
+     * @psalm-pure
+     */
+    final public static function min(BigNumber|int|float|string ...$values) : static
+    {
+        $min = null;
+
+        foreach ($values as $value) {
+            $value = static::of($value);
+
+            if ($min === null || $value->isLessThan($min)) {
+                $min = $value;
+            }
+        }
+
+        if ($min === null) {
+            throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
+        }
+
+        return $min;
+    }
+
+    /**
+     * Returns the maximum of the given values.
+     *
+     * @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
+     *                                              to an instance of the class this method is called on.
+     *
+     * @throws \InvalidArgumentException If no values are given.
+     * @throws MathException             If an argument is not valid.
+     *
+     * @psalm-pure
+     */
+    final public static function max(BigNumber|int|float|string ...$values) : static
+    {
+        $max = null;
+
+        foreach ($values as $value) {
+            $value = static::of($value);
+
+            if ($max === null || $value->isGreaterThan($max)) {
+                $max = $value;
+            }
+        }
+
+        if ($max === null) {
+            throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
+        }
+
+        return $max;
+    }
+
+    /**
+     * Returns the sum of the given values.
+     *
+     * @param BigNumber|int|float|string ...$values The numbers to add. All the numbers need to be convertible
+     *                                              to an instance of the class this method is called on.
+     *
+     * @throws \InvalidArgumentException If no values are given.
+     * @throws MathException             If an argument is not valid.
+     *
+     * @psalm-pure
+     */
+    final public static function sum(BigNumber|int|float|string ...$values) : static
+    {
+        /** @var static|null $sum */
+        $sum = null;
+
+        foreach ($values as $value) {
+            $value = static::of($value);
+
+            $sum = $sum === null ? $value : self::add($sum, $value);
+        }
+
+        if ($sum === null) {
+            throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
+        }
+
+        return $sum;
+    }
+
+    /**
+     * Adds two BigNumber instances in the correct order to avoid a RoundingNecessaryException.
+     *
+     * @todo This could be better resolved by creating an abstract protected method in BigNumber, and leaving to
+     *       concrete classes the responsibility to perform the addition themselves or delegate it to the given number,
+     *       depending on their ability to perform the operation. This will also require a version bump because we're
+     *       potentially breaking custom BigNumber implementations (if any...)
+     *
+     * @psalm-pure
+     */
+    private static function add(BigNumber $a, BigNumber $b) : BigNumber
+    {
+        if ($a instanceof BigRational) {
+            return $a->plus($b);
+        }
+
+        if ($b instanceof BigRational) {
+            return $b->plus($a);
+        }
+
+        if ($a instanceof BigDecimal) {
+            return $a->plus($b);
+        }
+
+        if ($b instanceof BigDecimal) {
+            return $b->plus($a);
+        }
+
+        /** @var BigInteger $a */
+
+        return $a->plus($b);
+    }
+
+    /**
+     * Removes optional leading zeros and applies sign.
+     *
+     * @param string|null $sign   The sign, '+' or '-', optional. Null is allowed for convenience and treated as '+'.
+     * @param string      $number The number, validated as a non-empty string of digits.
+     *
+     * @psalm-pure
+     */
+    private static function cleanUp(string|null $sign, string $number) : string
+    {
+        $number = \ltrim($number, '0');
+
+        if ($number === '') {
+            return '0';
+        }
+
+        return $sign === '-' ? '-' . $number : $number;
+    }
+
+    /**
+     * Checks if this number is equal to the given one.
+     */
+    final public function isEqualTo(BigNumber|int|float|string $that) : bool
+    {
+        return $this->compareTo($that) === 0;
+    }
+
+    /**
+     * Checks if this number is strictly lower than the given one.
+     */
+    final public function isLessThan(BigNumber|int|float|string $that) : bool
+    {
+        return $this->compareTo($that) < 0;
+    }
+
+    /**
+     * Checks if this number is lower than or equal to the given one.
+     */
+    final public function isLessThanOrEqualTo(BigNumber|int|float|string $that) : bool
+    {
+        return $this->compareTo($that) <= 0;
+    }
+
+    /**
+     * Checks if this number is strictly greater than the given one.
+     */
+    final public function isGreaterThan(BigNumber|int|float|string $that) : bool
+    {
+        return $this->compareTo($that) > 0;
+    }
+
+    /**
+     * Checks if this number is greater than or equal to the given one.
+     */
+    final public function isGreaterThanOrEqualTo(BigNumber|int|float|string $that) : bool
+    {
+        return $this->compareTo($that) >= 0;
+    }
+
+    /**
+     * Checks if this number equals zero.
+     */
+    final public function isZero() : bool
+    {
+        return $this->getSign() === 0;
+    }
+
+    /**
+     * Checks if this number is strictly negative.
+     */
+    final public function isNegative() : bool
+    {
+        return $this->getSign() < 0;
+    }
+
+    /**
+     * Checks if this number is negative or zero.
+     */
+    final public function isNegativeOrZero() : bool
+    {
+        return $this->getSign() <= 0;
+    }
+
+    /**
+     * Checks if this number is strictly positive.
+     */
+    final public function isPositive() : bool
+    {
+        return $this->getSign() > 0;
+    }
+
+    /**
+     * Checks if this number is positive or zero.
+     */
+    final public function isPositiveOrZero() : bool
+    {
+        return $this->getSign() >= 0;
+    }
+
+    /**
+     * Returns the sign of this number.
+     *
+     * @psalm-return -1|0|1
+     *
+     * @return int -1 if the number is negative, 0 if zero, 1 if positive.
+     */
+    abstract public function getSign() : int;
+
+    /**
+     * Compares this number to the given one.
+     *
+     * @psalm-return -1|0|1
+     *
+     * @return int -1 if `$this` is lower than, 0 if equal to, 1 if greater than `$that`.
+     *
+     * @throws MathException If the number is not valid.
+     */
+    abstract public function compareTo(BigNumber|int|float|string $that) : int;
+
+    /**
+     * Converts this number to a BigInteger.
+     *
+     * @throws RoundingNecessaryException If this number cannot be converted to a BigInteger without rounding.
+     */
+    abstract public function toBigInteger() : BigInteger;
+
+    /**
+     * Converts this number to a BigDecimal.
+     *
+     * @throws RoundingNecessaryException If this number cannot be converted to a BigDecimal without rounding.
+     */
+    abstract public function toBigDecimal() : BigDecimal;
+
+    /**
+     * Converts this number to a BigRational.
+     */
+    abstract public function toBigRational() : BigRational;
+
+    /**
+     * Converts this number to a BigDecimal with the given scale, using rounding if necessary.
+     *
+     * @param int          $scale        The scale of the resulting `BigDecimal`.
+     * @param RoundingMode $roundingMode An optional rounding mode, defaults to UNNECESSARY.
+     *
+     * @throws RoundingNecessaryException If this number cannot be converted to the given scale without rounding.
+     *                                    This only applies when RoundingMode::UNNECESSARY is used.
+     */
+    abstract public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal;
+
+    /**
+     * Returns the exact value of this number as a native integer.
+     *
+     * If this number cannot be converted to a native integer without losing precision, an exception is thrown.
+     * Note that the acceptable range for an integer depends on the platform and differs for 32-bit and 64-bit.
+     *
+     * @throws MathException If this number cannot be exactly converted to a native integer.
+     */
+    abstract public function toInt() : int;
+
+    /**
+     * Returns an approximation of this number as a floating-point value.
+     *
+     * Note that this method can discard information as the precision of a floating-point value
+     * is inherently limited.
+     *
+     * If the number is greater than the largest representable floating point number, positive infinity is returned.
+     * If the number is less than the smallest representable floating point number, negative infinity is returned.
+     */
+    abstract public function toFloat() : float;
+
+    /**
+     * Returns a string representation of this number.
+     *
+     * The output of this method can be parsed by the `of()` factory method;
+     * this will yield an object equal to this one, without any information loss.
+     */
+    abstract public function __toString() : string;
+
+    #[Override]
+    final public function jsonSerialize() : string
+    {
+        return $this->__toString();
+    }
+}

+ 424 - 0
vendor/brick/math/src/BigRational.php

@@ -0,0 +1,424 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math;
+
+use Brick\Math\Exception\DivisionByZeroException;
+use Brick\Math\Exception\MathException;
+use Brick\Math\Exception\NumberFormatException;
+use Brick\Math\Exception\RoundingNecessaryException;
+use Override;
+
+/**
+ * An arbitrarily large rational number.
+ *
+ * This class is immutable.
+ *
+ * @psalm-immutable
+ */
+final class BigRational extends BigNumber
+{
+    /**
+     * The numerator.
+     */
+    private readonly BigInteger $numerator;
+
+    /**
+     * The denominator. Always strictly positive.
+     */
+    private readonly BigInteger $denominator;
+
+    /**
+     * Protected constructor. Use a factory method to obtain an instance.
+     *
+     * @param BigInteger $numerator        The numerator.
+     * @param BigInteger $denominator      The denominator.
+     * @param bool       $checkDenominator Whether to check the denominator for negative and zero.
+     *
+     * @throws DivisionByZeroException If the denominator is zero.
+     */
+    protected function __construct(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator)
+    {
+        if ($checkDenominator) {
+            if ($denominator->isZero()) {
+                throw DivisionByZeroException::denominatorMustNotBeZero();
+            }
+
+            if ($denominator->isNegative()) {
+                $numerator   = $numerator->negated();
+                $denominator = $denominator->negated();
+            }
+        }
+
+        $this->numerator   = $numerator;
+        $this->denominator = $denominator;
+    }
+
+    /**
+     * @psalm-pure
+     */
+    #[Override]
+    protected static function from(BigNumber $number): static
+    {
+        return $number->toBigRational();
+    }
+
+    /**
+     * Creates a BigRational out of a numerator and a denominator.
+     *
+     * If the denominator is negative, the signs of both the numerator and the denominator
+     * will be inverted to ensure that the denominator is always positive.
+     *
+     * @param BigNumber|int|float|string $numerator   The numerator. Must be convertible to a BigInteger.
+     * @param BigNumber|int|float|string $denominator The denominator. Must be convertible to a BigInteger.
+     *
+     * @throws NumberFormatException      If an argument does not represent a valid number.
+     * @throws RoundingNecessaryException If an argument represents a non-integer number.
+     * @throws DivisionByZeroException    If the denominator is zero.
+     *
+     * @psalm-pure
+     */
+    public static function nd(
+        BigNumber|int|float|string $numerator,
+        BigNumber|int|float|string $denominator,
+    ) : BigRational {
+        $numerator   = BigInteger::of($numerator);
+        $denominator = BigInteger::of($denominator);
+
+        return new BigRational($numerator, $denominator, true);
+    }
+
+    /**
+     * Returns a BigRational representing zero.
+     *
+     * @psalm-pure
+     */
+    public static function zero() : BigRational
+    {
+        /**
+         * @psalm-suppress ImpureStaticVariable
+         * @var BigRational|null $zero
+         */
+        static $zero;
+
+        if ($zero === null) {
+            $zero = new BigRational(BigInteger::zero(), BigInteger::one(), false);
+        }
+
+        return $zero;
+    }
+
+    /**
+     * Returns a BigRational representing one.
+     *
+     * @psalm-pure
+     */
+    public static function one() : BigRational
+    {
+        /**
+         * @psalm-suppress ImpureStaticVariable
+         * @var BigRational|null $one
+         */
+        static $one;
+
+        if ($one === null) {
+            $one = new BigRational(BigInteger::one(), BigInteger::one(), false);
+        }
+
+        return $one;
+    }
+
+    /**
+     * Returns a BigRational representing ten.
+     *
+     * @psalm-pure
+     */
+    public static function ten() : BigRational
+    {
+        /**
+         * @psalm-suppress ImpureStaticVariable
+         * @var BigRational|null $ten
+         */
+        static $ten;
+
+        if ($ten === null) {
+            $ten = new BigRational(BigInteger::ten(), BigInteger::one(), false);
+        }
+
+        return $ten;
+    }
+
+    public function getNumerator() : BigInteger
+    {
+        return $this->numerator;
+    }
+
+    public function getDenominator() : BigInteger
+    {
+        return $this->denominator;
+    }
+
+    /**
+     * Returns the quotient of the division of the numerator by the denominator.
+     */
+    public function quotient() : BigInteger
+    {
+        return $this->numerator->quotient($this->denominator);
+    }
+
+    /**
+     * Returns the remainder of the division of the numerator by the denominator.
+     */
+    public function remainder() : BigInteger
+    {
+        return $this->numerator->remainder($this->denominator);
+    }
+
+    /**
+     * Returns the quotient and remainder of the division of the numerator by the denominator.
+     *
+     * @return BigInteger[]
+     *
+     * @psalm-return array{BigInteger, BigInteger}
+     */
+    public function quotientAndRemainder() : array
+    {
+        return $this->numerator->quotientAndRemainder($this->denominator);
+    }
+
+    /**
+     * Returns the sum of this number and the given one.
+     *
+     * @param BigNumber|int|float|string $that The number to add.
+     *
+     * @throws MathException If the number is not valid.
+     */
+    public function plus(BigNumber|int|float|string $that) : BigRational
+    {
+        $that = BigRational::of($that);
+
+        $numerator   = $this->numerator->multipliedBy($that->denominator);
+        $numerator   = $numerator->plus($that->numerator->multipliedBy($this->denominator));
+        $denominator = $this->denominator->multipliedBy($that->denominator);
+
+        return new BigRational($numerator, $denominator, false);
+    }
+
+    /**
+     * Returns the difference of this number and the given one.
+     *
+     * @param BigNumber|int|float|string $that The number to subtract.
+     *
+     * @throws MathException If the number is not valid.
+     */
+    public function minus(BigNumber|int|float|string $that) : BigRational
+    {
+        $that = BigRational::of($that);
+
+        $numerator   = $this->numerator->multipliedBy($that->denominator);
+        $numerator   = $numerator->minus($that->numerator->multipliedBy($this->denominator));
+        $denominator = $this->denominator->multipliedBy($that->denominator);
+
+        return new BigRational($numerator, $denominator, false);
+    }
+
+    /**
+     * Returns the product of this number and the given one.
+     *
+     * @param BigNumber|int|float|string $that The multiplier.
+     *
+     * @throws MathException If the multiplier is not a valid number.
+     */
+    public function multipliedBy(BigNumber|int|float|string $that) : BigRational
+    {
+        $that = BigRational::of($that);
+
+        $numerator   = $this->numerator->multipliedBy($that->numerator);
+        $denominator = $this->denominator->multipliedBy($that->denominator);
+
+        return new BigRational($numerator, $denominator, false);
+    }
+
+    /**
+     * Returns the result of the division of this number by the given one.
+     *
+     * @param BigNumber|int|float|string $that The divisor.
+     *
+     * @throws MathException If the divisor is not a valid number, or is zero.
+     */
+    public function dividedBy(BigNumber|int|float|string $that) : BigRational
+    {
+        $that = BigRational::of($that);
+
+        $numerator   = $this->numerator->multipliedBy($that->denominator);
+        $denominator = $this->denominator->multipliedBy($that->numerator);
+
+        return new BigRational($numerator, $denominator, true);
+    }
+
+    /**
+     * Returns this number exponentiated to the given value.
+     *
+     * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
+     */
+    public function power(int $exponent) : BigRational
+    {
+        if ($exponent === 0) {
+            $one = BigInteger::one();
+
+            return new BigRational($one, $one, false);
+        }
+
+        if ($exponent === 1) {
+            return $this;
+        }
+
+        return new BigRational(
+            $this->numerator->power($exponent),
+            $this->denominator->power($exponent),
+            false
+        );
+    }
+
+    /**
+     * Returns the reciprocal of this BigRational.
+     *
+     * The reciprocal has the numerator and denominator swapped.
+     *
+     * @throws DivisionByZeroException If the numerator is zero.
+     */
+    public function reciprocal() : BigRational
+    {
+        return new BigRational($this->denominator, $this->numerator, true);
+    }
+
+    /**
+     * Returns the absolute value of this BigRational.
+     */
+    public function abs() : BigRational
+    {
+        return new BigRational($this->numerator->abs(), $this->denominator, false);
+    }
+
+    /**
+     * Returns the negated value of this BigRational.
+     */
+    public function negated() : BigRational
+    {
+        return new BigRational($this->numerator->negated(), $this->denominator, false);
+    }
+
+    /**
+     * Returns the simplified value of this BigRational.
+     */
+    public function simplified() : BigRational
+    {
+        $gcd = $this->numerator->gcd($this->denominator);
+
+        $numerator = $this->numerator->quotient($gcd);
+        $denominator = $this->denominator->quotient($gcd);
+
+        return new BigRational($numerator, $denominator, false);
+    }
+
+    #[Override]
+    public function compareTo(BigNumber|int|float|string $that) : int
+    {
+        return $this->minus($that)->getSign();
+    }
+
+    #[Override]
+    public function getSign() : int
+    {
+        return $this->numerator->getSign();
+    }
+
+    #[Override]
+    public function toBigInteger() : BigInteger
+    {
+        $simplified = $this->simplified();
+
+        if (! $simplified->denominator->isEqualTo(1)) {
+            throw new RoundingNecessaryException('This rational number cannot be represented as an integer value without rounding.');
+        }
+
+        return $simplified->numerator;
+    }
+
+    #[Override]
+    public function toBigDecimal() : BigDecimal
+    {
+        return $this->numerator->toBigDecimal()->exactlyDividedBy($this->denominator);
+    }
+
+    #[Override]
+    public function toBigRational() : BigRational
+    {
+        return $this;
+    }
+
+    #[Override]
+    public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
+    {
+        return $this->numerator->toBigDecimal()->dividedBy($this->denominator, $scale, $roundingMode);
+    }
+
+    #[Override]
+    public function toInt() : int
+    {
+        return $this->toBigInteger()->toInt();
+    }
+
+    #[Override]
+    public function toFloat() : float
+    {
+        $simplified = $this->simplified();
+        return $simplified->numerator->toFloat() / $simplified->denominator->toFloat();
+    }
+
+    #[Override]
+    public function __toString() : string
+    {
+        $numerator   = (string) $this->numerator;
+        $denominator = (string) $this->denominator;
+
+        if ($denominator === '1') {
+            return $numerator;
+        }
+
+        return $numerator . '/' . $denominator;
+    }
+
+    /**
+     * This method is required for serializing the object and SHOULD NOT be accessed directly.
+     *
+     * @internal
+     *
+     * @return array{numerator: BigInteger, denominator: BigInteger}
+     */
+    public function __serialize(): array
+    {
+        return ['numerator' => $this->numerator, 'denominator' => $this->denominator];
+    }
+
+    /**
+     * This method is only here to allow unserializing the object and cannot be accessed directly.
+     *
+     * @internal
+     * @psalm-suppress RedundantPropertyInitializationCheck
+     *
+     * @param array{numerator: BigInteger, denominator: BigInteger} $data
+     *
+     * @throws \LogicException
+     */
+    public function __unserialize(array $data): void
+    {
+        if (isset($this->numerator)) {
+            throw new \LogicException('__unserialize() is an internal function, it must not be called directly.');
+        }
+
+        $this->numerator = $data['numerator'];
+        $this->denominator = $data['denominator'];
+    }
+}

+ 35 - 0
vendor/brick/math/src/Exception/DivisionByZeroException.php

@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Exception;
+
+/**
+ * Exception thrown when a division by zero occurs.
+ */
+class DivisionByZeroException extends MathException
+{
+    /**
+     * @psalm-pure
+     */
+    public static function divisionByZero() : DivisionByZeroException
+    {
+        return new self('Division by zero.');
+    }
+
+    /**
+     * @psalm-pure
+     */
+    public static function modulusMustNotBeZero() : DivisionByZeroException
+    {
+        return new self('The modulus must not be zero.');
+    }
+
+    /**
+     * @psalm-pure
+     */
+    public static function denominatorMustNotBeZero() : DivisionByZeroException
+    {
+        return new self('The denominator of a rational number cannot be zero.');
+    }
+}

+ 23 - 0
vendor/brick/math/src/Exception/IntegerOverflowException.php

@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Exception;
+
+use Brick\Math\BigInteger;
+
+/**
+ * Exception thrown when an integer overflow occurs.
+ */
+class IntegerOverflowException extends MathException
+{
+    /**
+     * @psalm-pure
+     */
+    public static function toIntOverflow(BigInteger $value) : IntegerOverflowException
+    {
+        $message = '%s is out of range %d to %d and cannot be represented as an integer.';
+
+        return new self(\sprintf($message, (string) $value, PHP_INT_MIN, PHP_INT_MAX));
+    }
+}

+ 12 - 0
vendor/brick/math/src/Exception/MathException.php

@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Exception;
+
+/**
+ * Base class for all math exceptions.
+ */
+class MathException extends \Exception
+{
+}

+ 12 - 0
vendor/brick/math/src/Exception/NegativeNumberException.php

@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Exception;
+
+/**
+ * Exception thrown when attempting to perform an unsupported operation, such as a square root, on a negative number.
+ */
+class NegativeNumberException extends MathException
+{
+}

+ 41 - 0
vendor/brick/math/src/Exception/NumberFormatException.php

@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Exception;
+
+/**
+ * Exception thrown when attempting to create a number from a string with an invalid format.
+ */
+class NumberFormatException extends MathException
+{
+    public static function invalidFormat(string $value) : self
+    {
+        return new self(\sprintf(
+            'The given value "%s" does not represent a valid number.',
+            $value,
+        ));
+    }
+
+    /**
+     * @param string $char The failing character.
+     *
+     * @psalm-pure
+     */
+    public static function charNotInAlphabet(string $char) : self
+    {
+        $ord = \ord($char);
+
+        if ($ord < 32 || $ord > 126) {
+            $char = \strtoupper(\dechex($ord));
+
+            if ($ord < 10) {
+                $char = '0' . $char;
+            }
+        } else {
+            $char = '"' . $char . '"';
+        }
+
+        return new self(\sprintf('Char %s is not a valid character in the given alphabet.', $char));
+    }
+}

+ 19 - 0
vendor/brick/math/src/Exception/RoundingNecessaryException.php

@@ -0,0 +1,19 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Exception;
+
+/**
+ * Exception thrown when a number cannot be represented at the requested scale without rounding.
+ */
+class RoundingNecessaryException extends MathException
+{
+    /**
+     * @psalm-pure
+     */
+    public static function roundingNecessary() : RoundingNecessaryException
+    {
+        return new self('Rounding is necessary to represent the result of the operation at this scale.');
+    }
+}

+ 668 - 0
vendor/brick/math/src/Internal/Calculator.php

@@ -0,0 +1,668 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Internal;
+
+use Brick\Math\Exception\RoundingNecessaryException;
+use Brick\Math\RoundingMode;
+
+/**
+ * Performs basic operations on arbitrary size integers.
+ *
+ * Unless otherwise specified, all parameters must be validated as non-empty strings of digits,
+ * without leading zero, and with an optional leading minus sign if the number is not zero.
+ *
+ * Any other parameter format will lead to undefined behaviour.
+ * All methods must return strings respecting this format, unless specified otherwise.
+ *
+ * @internal
+ *
+ * @psalm-immutable
+ */
+abstract class Calculator
+{
+    /**
+     * The maximum exponent value allowed for the pow() method.
+     */
+    public const MAX_POWER = 1_000_000;
+
+    /**
+     * The alphabet for converting from and to base 2 to 36, lowercase.
+     */
+    public const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz';
+
+    /**
+     * The Calculator instance in use.
+     */
+    private static ?Calculator $instance = null;
+
+    /**
+     * Sets the Calculator instance to use.
+     *
+     * An instance is typically set only in unit tests: the autodetect is usually the best option.
+     *
+     * @param Calculator|null $calculator The calculator instance, or NULL to revert to autodetect.
+     */
+    final public static function set(?Calculator $calculator) : void
+    {
+        self::$instance = $calculator;
+    }
+
+    /**
+     * Returns the Calculator instance to use.
+     *
+     * If none has been explicitly set, the fastest available implementation will be returned.
+     *
+     * @psalm-pure
+     * @psalm-suppress ImpureStaticProperty
+     */
+    final public static function get() : Calculator
+    {
+        if (self::$instance === null) {
+            /** @psalm-suppress ImpureMethodCall */
+            self::$instance = self::detect();
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * Returns the fastest available Calculator implementation.
+     *
+     * @codeCoverageIgnore
+     */
+    private static function detect() : Calculator
+    {
+        if (\extension_loaded('gmp')) {
+            return new Calculator\GmpCalculator();
+        }
+
+        if (\extension_loaded('bcmath')) {
+            return new Calculator\BcMathCalculator();
+        }
+
+        return new Calculator\NativeCalculator();
+    }
+
+    /**
+     * Extracts the sign & digits of the operands.
+     *
+     * @return array{bool, bool, string, string} Whether $a and $b are negative, followed by their digits.
+     */
+    final protected function init(string $a, string $b) : array
+    {
+        return [
+            $aNeg = ($a[0] === '-'),
+            $bNeg = ($b[0] === '-'),
+
+            $aNeg ? \substr($a, 1) : $a,
+            $bNeg ? \substr($b, 1) : $b,
+        ];
+    }
+
+    /**
+     * Returns the absolute value of a number.
+     */
+    final public function abs(string $n) : string
+    {
+        return ($n[0] === '-') ? \substr($n, 1) : $n;
+    }
+
+    /**
+     * Negates a number.
+     */
+    final public function neg(string $n) : string
+    {
+        if ($n === '0') {
+            return '0';
+        }
+
+        if ($n[0] === '-') {
+            return \substr($n, 1);
+        }
+
+        return '-' . $n;
+    }
+
+    /**
+     * Compares two numbers.
+     *
+     * @psalm-return -1|0|1
+     *
+     * @return int -1 if the first number is less than, 0 if equal to, 1 if greater than the second number.
+     */
+    final public function cmp(string $a, string $b) : int
+    {
+        [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
+
+        if ($aNeg && ! $bNeg) {
+            return -1;
+        }
+
+        if ($bNeg && ! $aNeg) {
+            return 1;
+        }
+
+        $aLen = \strlen($aDig);
+        $bLen = \strlen($bDig);
+
+        if ($aLen < $bLen) {
+            $result = -1;
+        } elseif ($aLen > $bLen) {
+            $result = 1;
+        } else {
+            $result = $aDig <=> $bDig;
+        }
+
+        return $aNeg ? -$result : $result;
+    }
+
+    /**
+     * Adds two numbers.
+     */
+    abstract public function add(string $a, string $b) : string;
+
+    /**
+     * Subtracts two numbers.
+     */
+    abstract public function sub(string $a, string $b) : string;
+
+    /**
+     * Multiplies two numbers.
+     */
+    abstract public function mul(string $a, string $b) : string;
+
+    /**
+     * Returns the quotient of the division of two numbers.
+     *
+     * @param string $a The dividend.
+     * @param string $b The divisor, must not be zero.
+     *
+     * @return string The quotient.
+     */
+    abstract public function divQ(string $a, string $b) : string;
+
+    /**
+     * Returns the remainder of the division of two numbers.
+     *
+     * @param string $a The dividend.
+     * @param string $b The divisor, must not be zero.
+     *
+     * @return string The remainder.
+     */
+    abstract public function divR(string $a, string $b) : string;
+
+    /**
+     * Returns the quotient and remainder of the division of two numbers.
+     *
+     * @param string $a The dividend.
+     * @param string $b The divisor, must not be zero.
+     *
+     * @return array{string, string} An array containing the quotient and remainder.
+     */
+    abstract public function divQR(string $a, string $b) : array;
+
+    /**
+     * Exponentiates a number.
+     *
+     * @param string $a The base number.
+     * @param int    $e The exponent, validated as an integer between 0 and MAX_POWER.
+     *
+     * @return string The power.
+     */
+    abstract public function pow(string $a, int $e) : string;
+
+    /**
+     * @param string $b The modulus; must not be zero.
+     */
+    public function mod(string $a, string $b) : string
+    {
+        return $this->divR($this->add($this->divR($a, $b), $b), $b);
+    }
+
+    /**
+     * Returns the modular multiplicative inverse of $x modulo $m.
+     *
+     * If $x has no multiplicative inverse mod m, this method must return null.
+     *
+     * This method can be overridden by the concrete implementation if the underlying library has built-in support.
+     *
+     * @param string $m The modulus; must not be negative or zero.
+     */
+    public function modInverse(string $x, string $m) : ?string
+    {
+        if ($m === '1') {
+            return '0';
+        }
+
+        $modVal = $x;
+
+        if ($x[0] === '-' || ($this->cmp($this->abs($x), $m) >= 0)) {
+            $modVal = $this->mod($x, $m);
+        }
+
+        [$g, $x] = $this->gcdExtended($modVal, $m);
+
+        if ($g !== '1') {
+            return null;
+        }
+
+        return $this->mod($this->add($this->mod($x, $m), $m), $m);
+    }
+
+    /**
+     * Raises a number into power with modulo.
+     *
+     * @param string $base The base number; must be positive or zero.
+     * @param string $exp  The exponent; must be positive or zero.
+     * @param string $mod  The modulus; must be strictly positive.
+     */
+    abstract public function modPow(string $base, string $exp, string $mod) : string;
+
+    /**
+     * Returns the greatest common divisor of the two numbers.
+     *
+     * This method can be overridden by the concrete implementation if the underlying library
+     * has built-in support for GCD calculations.
+     *
+     * @return string The GCD, always positive, or zero if both arguments are zero.
+     */
+    public function gcd(string $a, string $b) : string
+    {
+        if ($a === '0') {
+            return $this->abs($b);
+        }
+
+        if ($b === '0') {
+            return $this->abs($a);
+        }
+
+        return $this->gcd($b, $this->divR($a, $b));
+    }
+
+    /**
+     * @return array{string, string, string} GCD, X, Y
+     */
+    private function gcdExtended(string $a, string $b) : array
+    {
+        if ($a === '0') {
+            return [$b, '0', '1'];
+        }
+
+        [$gcd, $x1, $y1] = $this->gcdExtended($this->mod($b, $a), $a);
+
+        $x = $this->sub($y1, $this->mul($this->divQ($b, $a), $x1));
+        $y = $x1;
+
+        return [$gcd, $x, $y];
+    }
+
+    /**
+     * Returns the square root of the given number, rounded down.
+     *
+     * The result is the largest x such that x² ≤ n.
+     * The input MUST NOT be negative.
+     */
+    abstract public function sqrt(string $n) : string;
+
+    /**
+     * Converts a number from an arbitrary base.
+     *
+     * This method can be overridden by the concrete implementation if the underlying library
+     * has built-in support for base conversion.
+     *
+     * @param string $number The number, positive or zero, non-empty, case-insensitively validated for the given base.
+     * @param int    $base   The base of the number, validated from 2 to 36.
+     *
+     * @return string The converted number, following the Calculator conventions.
+     */
+    public function fromBase(string $number, int $base) : string
+    {
+        return $this->fromArbitraryBase(\strtolower($number), self::ALPHABET, $base);
+    }
+
+    /**
+     * Converts a number to an arbitrary base.
+     *
+     * This method can be overridden by the concrete implementation if the underlying library
+     * has built-in support for base conversion.
+     *
+     * @param string $number The number to convert, following the Calculator conventions.
+     * @param int    $base   The base to convert to, validated from 2 to 36.
+     *
+     * @return string The converted number, lowercase.
+     */
+    public function toBase(string $number, int $base) : string
+    {
+        $negative = ($number[0] === '-');
+
+        if ($negative) {
+            $number = \substr($number, 1);
+        }
+
+        $number = $this->toArbitraryBase($number, self::ALPHABET, $base);
+
+        if ($negative) {
+            return '-' . $number;
+        }
+
+        return $number;
+    }
+
+    /**
+     * Converts a non-negative number in an arbitrary base using a custom alphabet, to base 10.
+     *
+     * @param string $number   The number to convert, validated as a non-empty string,
+     *                         containing only chars in the given alphabet/base.
+     * @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
+     * @param int    $base     The base of the number, validated from 2 to alphabet length.
+     *
+     * @return string The number in base 10, following the Calculator conventions.
+     */
+    final public function fromArbitraryBase(string $number, string $alphabet, int $base) : string
+    {
+        // remove leading "zeros"
+        $number = \ltrim($number, $alphabet[0]);
+
+        if ($number === '') {
+            return '0';
+        }
+
+        // optimize for "one"
+        if ($number === $alphabet[1]) {
+            return '1';
+        }
+
+        $result = '0';
+        $power = '1';
+
+        $base = (string) $base;
+
+        for ($i = \strlen($number) - 1; $i >= 0; $i--) {
+            $index = \strpos($alphabet, $number[$i]);
+
+            if ($index !== 0) {
+                $result = $this->add($result, ($index === 1)
+                    ? $power
+                    : $this->mul($power, (string) $index)
+                );
+            }
+
+            if ($i !== 0) {
+                $power = $this->mul($power, $base);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Converts a non-negative number to an arbitrary base using a custom alphabet.
+     *
+     * @param string $number   The number to convert, positive or zero, following the Calculator conventions.
+     * @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
+     * @param int    $base     The base to convert to, validated from 2 to alphabet length.
+     *
+     * @return string The converted number in the given alphabet.
+     */
+    final public function toArbitraryBase(string $number, string $alphabet, int $base) : string
+    {
+        if ($number === '0') {
+            return $alphabet[0];
+        }
+
+        $base = (string) $base;
+        $result = '';
+
+        while ($number !== '0') {
+            [$number, $remainder] = $this->divQR($number, $base);
+            $remainder = (int) $remainder;
+
+            $result .= $alphabet[$remainder];
+        }
+
+        return \strrev($result);
+    }
+
+    /**
+     * Performs a rounded division.
+     *
+     * Rounding is performed when the remainder of the division is not zero.
+     *
+     * @param string       $a            The dividend.
+     * @param string       $b            The divisor, must not be zero.
+     * @param RoundingMode $roundingMode The rounding mode.
+     *
+     * @throws \InvalidArgumentException  If the rounding mode is invalid.
+     * @throws RoundingNecessaryException If RoundingMode::UNNECESSARY is provided but rounding is necessary.
+     *
+     * @psalm-suppress ImpureFunctionCall
+     */
+    final public function divRound(string $a, string $b, RoundingMode $roundingMode) : string
+    {
+        [$quotient, $remainder] = $this->divQR($a, $b);
+
+        $hasDiscardedFraction = ($remainder !== '0');
+        $isPositiveOrZero = ($a[0] === '-') === ($b[0] === '-');
+
+        $discardedFractionSign = function() use ($remainder, $b) : int {
+            $r = $this->abs($this->mul($remainder, '2'));
+            $b = $this->abs($b);
+
+            return $this->cmp($r, $b);
+        };
+
+        $increment = false;
+
+        switch ($roundingMode) {
+            case RoundingMode::UNNECESSARY:
+                if ($hasDiscardedFraction) {
+                    throw RoundingNecessaryException::roundingNecessary();
+                }
+                break;
+
+            case RoundingMode::UP:
+                $increment = $hasDiscardedFraction;
+                break;
+
+            case RoundingMode::DOWN:
+                break;
+
+            case RoundingMode::CEILING:
+                $increment = $hasDiscardedFraction && $isPositiveOrZero;
+                break;
+
+            case RoundingMode::FLOOR:
+                $increment = $hasDiscardedFraction && ! $isPositiveOrZero;
+                break;
+
+            case RoundingMode::HALF_UP:
+                $increment = $discardedFractionSign() >= 0;
+                break;
+
+            case RoundingMode::HALF_DOWN:
+                $increment = $discardedFractionSign() > 0;
+                break;
+
+            case RoundingMode::HALF_CEILING:
+                $increment = $isPositiveOrZero ? $discardedFractionSign() >= 0 : $discardedFractionSign() > 0;
+                break;
+
+            case RoundingMode::HALF_FLOOR:
+                $increment = $isPositiveOrZero ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
+                break;
+
+            case RoundingMode::HALF_EVEN:
+                $lastDigit = (int) $quotient[-1];
+                $lastDigitIsEven = ($lastDigit % 2 === 0);
+                $increment = $lastDigitIsEven ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
+                break;
+
+            default:
+                throw new \InvalidArgumentException('Invalid rounding mode.');
+        }
+
+        if ($increment) {
+            return $this->add($quotient, $isPositiveOrZero ? '1' : '-1');
+        }
+
+        return $quotient;
+    }
+
+    /**
+     * Calculates bitwise AND of two numbers.
+     *
+     * This method can be overridden by the concrete implementation if the underlying library
+     * has built-in support for bitwise operations.
+     */
+    public function and(string $a, string $b) : string
+    {
+        return $this->bitwise('and', $a, $b);
+    }
+
+    /**
+     * Calculates bitwise OR of two numbers.
+     *
+     * This method can be overridden by the concrete implementation if the underlying library
+     * has built-in support for bitwise operations.
+     */
+    public function or(string $a, string $b) : string
+    {
+        return $this->bitwise('or', $a, $b);
+    }
+
+    /**
+     * Calculates bitwise XOR of two numbers.
+     *
+     * This method can be overridden by the concrete implementation if the underlying library
+     * has built-in support for bitwise operations.
+     */
+    public function xor(string $a, string $b) : string
+    {
+        return $this->bitwise('xor', $a, $b);
+    }
+
+    /**
+     * Performs a bitwise operation on a decimal number.
+     *
+     * @param 'and'|'or'|'xor' $operator The operator to use.
+     * @param string           $a        The left operand.
+     * @param string           $b        The right operand.
+     */
+    private function bitwise(string $operator, string $a, string $b) : string
+    {
+        [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
+
+        $aBin = $this->toBinary($aDig);
+        $bBin = $this->toBinary($bDig);
+
+        $aLen = \strlen($aBin);
+        $bLen = \strlen($bBin);
+
+        if ($aLen > $bLen) {
+            $bBin = \str_repeat("\x00", $aLen - $bLen) . $bBin;
+        } elseif ($bLen > $aLen) {
+            $aBin = \str_repeat("\x00", $bLen - $aLen) . $aBin;
+        }
+
+        if ($aNeg) {
+            $aBin = $this->twosComplement($aBin);
+        }
+        if ($bNeg) {
+            $bBin = $this->twosComplement($bBin);
+        }
+
+        $value = match ($operator) {
+            'and' => $aBin & $bBin,
+            'or' => $aBin | $bBin,
+            'xor' => $aBin ^ $bBin,
+        };
+
+        $negative = match ($operator) {
+            'and' => $aNeg and $bNeg,
+            'or' => $aNeg or $bNeg,
+            'xor' => $aNeg xor $bNeg,
+        };
+
+        if ($negative) {
+            $value = $this->twosComplement($value);
+        }
+
+        $result = $this->toDecimal($value);
+
+        return $negative ? $this->neg($result) : $result;
+    }
+
+    /**
+     * @param string $number A positive, binary number.
+     */
+    private function twosComplement(string $number) : string
+    {
+        $xor = \str_repeat("\xff", \strlen($number));
+
+        $number ^= $xor;
+
+        for ($i = \strlen($number) - 1; $i >= 0; $i--) {
+            $byte = \ord($number[$i]);
+
+            if (++$byte !== 256) {
+                $number[$i] = \chr($byte);
+                break;
+            }
+
+            $number[$i] = "\x00";
+
+            if ($i === 0) {
+                $number = "\x01" . $number;
+            }
+        }
+
+        return $number;
+    }
+
+    /**
+     * Converts a decimal number to a binary string.
+     *
+     * @param string $number The number to convert, positive or zero, only digits.
+     */
+    private function toBinary(string $number) : string
+    {
+        $result = '';
+
+        while ($number !== '0') {
+            [$number, $remainder] = $this->divQR($number, '256');
+            $result .= \chr((int) $remainder);
+        }
+
+        return \strrev($result);
+    }
+
+    /**
+     * Returns the positive decimal representation of a binary number.
+     *
+     * @param string $bytes The bytes representing the number.
+     */
+    private function toDecimal(string $bytes) : string
+    {
+        $result = '0';
+        $power = '1';
+
+        for ($i = \strlen($bytes) - 1; $i >= 0; $i--) {
+            $index = \ord($bytes[$i]);
+
+            if ($index !== 0) {
+                $result = $this->add($result, ($index === 1)
+                    ? $power
+                    : $this->mul($power, (string) $index)
+                );
+            }
+
+            if ($i !== 0) {
+                $power = $this->mul($power, '256');
+            }
+        }
+
+        return $result;
+    }
+}

+ 75 - 0
vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php

@@ -0,0 +1,75 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Internal\Calculator;
+
+use Brick\Math\Internal\Calculator;
+use Override;
+
+/**
+ * Calculator implementation built around the bcmath library.
+ *
+ * @internal
+ *
+ * @psalm-immutable
+ */
+class BcMathCalculator extends Calculator
+{
+    #[Override]
+    public function add(string $a, string $b) : string
+    {
+        return \bcadd($a, $b, 0);
+    }
+
+    #[Override]
+    public function sub(string $a, string $b) : string
+    {
+        return \bcsub($a, $b, 0);
+    }
+
+    #[Override]
+    public function mul(string $a, string $b) : string
+    {
+        return \bcmul($a, $b, 0);
+    }
+
+    #[Override]
+    public function divQ(string $a, string $b) : string
+    {
+        return \bcdiv($a, $b, 0);
+    }
+
+    #[Override]
+    public function divR(string $a, string $b) : string
+    {
+        return \bcmod($a, $b, 0);
+    }
+
+    #[Override]
+    public function divQR(string $a, string $b) : array
+    {
+        $q = \bcdiv($a, $b, 0);
+        $r = \bcmod($a, $b, 0);
+
+        return [$q, $r];
+    }
+
+    #[Override]
+    public function pow(string $a, int $e) : string
+    {
+        return \bcpow($a, (string) $e, 0);
+    }
+
+    #[Override]
+    public function modPow(string $base, string $exp, string $mod) : string
+    {
+        return \bcpowmod($base, $exp, $mod, 0);
+    }
+
+    #[Override]
+    public function sqrt(string $n) : string
+    {
+        return \bcsqrt($n, 0);
+    }
+}

+ 125 - 0
vendor/brick/math/src/Internal/Calculator/GmpCalculator.php

@@ -0,0 +1,125 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Internal\Calculator;
+
+use Brick\Math\Internal\Calculator;
+use Override;
+
+/**
+ * Calculator implementation built around the GMP library.
+ *
+ * @internal
+ *
+ * @psalm-immutable
+ */
+class GmpCalculator extends Calculator
+{
+    #[Override]
+    public function add(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_add($a, $b));
+    }
+
+    #[Override]
+    public function sub(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_sub($a, $b));
+    }
+
+    #[Override]
+    public function mul(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_mul($a, $b));
+    }
+
+    #[Override]
+    public function divQ(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_div_q($a, $b));
+    }
+
+    #[Override]
+    public function divR(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_div_r($a, $b));
+    }
+
+    #[Override]
+    public function divQR(string $a, string $b) : array
+    {
+        [$q, $r] = \gmp_div_qr($a, $b);
+
+        return [
+            \gmp_strval($q),
+            \gmp_strval($r)
+        ];
+    }
+
+    #[Override]
+    public function pow(string $a, int $e) : string
+    {
+        return \gmp_strval(\gmp_pow($a, $e));
+    }
+
+    #[Override]
+    public function modInverse(string $x, string $m) : ?string
+    {
+        $result = \gmp_invert($x, $m);
+
+        if ($result === false) {
+            return null;
+        }
+
+        return \gmp_strval($result);
+    }
+
+    #[Override]
+    public function modPow(string $base, string $exp, string $mod) : string
+    {
+        return \gmp_strval(\gmp_powm($base, $exp, $mod));
+    }
+
+    #[Override]
+    public function gcd(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_gcd($a, $b));
+    }
+
+    #[Override]
+    public function fromBase(string $number, int $base) : string
+    {
+        return \gmp_strval(\gmp_init($number, $base));
+    }
+
+    #[Override]
+    public function toBase(string $number, int $base) : string
+    {
+        return \gmp_strval($number, $base);
+    }
+
+    #[Override]
+    public function and(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_and($a, $b));
+    }
+
+    #[Override]
+    public function or(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_or($a, $b));
+    }
+
+    #[Override]
+    public function xor(string $a, string $b) : string
+    {
+        return \gmp_strval(\gmp_xor($a, $b));
+    }
+
+    #[Override]
+    public function sqrt(string $n) : string
+    {
+        return \gmp_strval(\gmp_sqrt($n));
+    }
+}

+ 598 - 0
vendor/brick/math/src/Internal/Calculator/NativeCalculator.php

@@ -0,0 +1,598 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math\Internal\Calculator;
+
+use Brick\Math\Internal\Calculator;
+use Override;
+
+/**
+ * Calculator implementation using only native PHP code.
+ *
+ * @internal
+ *
+ * @psalm-immutable
+ */
+class NativeCalculator extends Calculator
+{
+    /**
+     * The max number of digits the platform can natively add, subtract, multiply or divide without overflow.
+     * For multiplication, this represents the max sum of the lengths of both operands.
+     *
+     * In addition, it is assumed that an extra digit can hold a carry (1) without overflowing.
+     * Example: 32-bit: max number 1,999,999,999 (9 digits + carry)
+     *          64-bit: max number 1,999,999,999,999,999,999 (18 digits + carry)
+     */
+    private readonly int $maxDigits;
+
+    /**
+     * @codeCoverageIgnore
+     */
+    public function __construct()
+    {
+        $this->maxDigits = match (PHP_INT_SIZE) {
+            4 => 9,
+            8 => 18,
+            default => throw new \RuntimeException('The platform is not 32-bit or 64-bit as expected.')
+        };
+    }
+
+    #[Override]
+    public function add(string $a, string $b) : string
+    {
+        /**
+         * @psalm-var numeric-string $a
+         * @psalm-var numeric-string $b
+         */
+        $result = $a + $b;
+
+        if (is_int($result)) {
+            return (string) $result;
+        }
+
+        if ($a === '0') {
+            return $b;
+        }
+
+        if ($b === '0') {
+            return $a;
+        }
+
+        [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
+
+        $result = $aNeg === $bNeg ? $this->doAdd($aDig, $bDig) : $this->doSub($aDig, $bDig);
+
+        if ($aNeg) {
+            $result = $this->neg($result);
+        }
+
+        return $result;
+    }
+
+    #[Override]
+    public function sub(string $a, string $b) : string
+    {
+        return $this->add($a, $this->neg($b));
+    }
+
+    #[Override]
+    public function mul(string $a, string $b) : string
+    {
+        /**
+         * @psalm-var numeric-string $a
+         * @psalm-var numeric-string $b
+         */
+        $result = $a * $b;
+
+        if (is_int($result)) {
+            return (string) $result;
+        }
+
+        if ($a === '0' || $b === '0') {
+            return '0';
+        }
+
+        if ($a === '1') {
+            return $b;
+        }
+
+        if ($b === '1') {
+            return $a;
+        }
+
+        if ($a === '-1') {
+            return $this->neg($b);
+        }
+
+        if ($b === '-1') {
+            return $this->neg($a);
+        }
+
+        [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
+
+        $result = $this->doMul($aDig, $bDig);
+
+        if ($aNeg !== $bNeg) {
+            $result = $this->neg($result);
+        }
+
+        return $result;
+    }
+
+    #[Override]
+    public function divQ(string $a, string $b) : string
+    {
+        return $this->divQR($a, $b)[0];
+    }
+
+    #[Override]
+    public function divR(string $a, string $b): string
+    {
+        return $this->divQR($a, $b)[1];
+    }
+
+    #[Override]
+    public function divQR(string $a, string $b) : array
+    {
+        if ($a === '0') {
+            return ['0', '0'];
+        }
+
+        if ($a === $b) {
+            return ['1', '0'];
+        }
+
+        if ($b === '1') {
+            return [$a, '0'];
+        }
+
+        if ($b === '-1') {
+            return [$this->neg($a), '0'];
+        }
+
+        /** @psalm-var numeric-string $a */
+        $na = $a * 1; // cast to number
+
+        if (is_int($na)) {
+            /** @psalm-var numeric-string $b */
+            $nb = $b * 1;
+
+            if (is_int($nb)) {
+                // the only division that may overflow is PHP_INT_MIN / -1,
+                // which cannot happen here as we've already handled a divisor of -1 above.
+                $q = intdiv($na, $nb);
+                $r = $na % $nb;
+
+                return [
+                    (string) $q,
+                    (string) $r
+                ];
+            }
+        }
+
+        [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
+
+        [$q, $r] = $this->doDiv($aDig, $bDig);
+
+        if ($aNeg !== $bNeg) {
+            $q = $this->neg($q);
+        }
+
+        if ($aNeg) {
+            $r = $this->neg($r);
+        }
+
+        return [$q, $r];
+    }
+
+    #[Override]
+    public function pow(string $a, int $e) : string
+    {
+        if ($e === 0) {
+            return '1';
+        }
+
+        if ($e === 1) {
+            return $a;
+        }
+
+        $odd = $e % 2;
+        $e -= $odd;
+
+        $aa = $this->mul($a, $a);
+
+        /** @psalm-suppress PossiblyInvalidArgument We're sure that $e / 2 is an int now */
+        $result = $this->pow($aa, $e / 2);
+
+        if ($odd === 1) {
+            $result = $this->mul($result, $a);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Algorithm from: https://www.geeksforgeeks.org/modular-exponentiation-power-in-modular-arithmetic/
+     */
+    #[Override]
+    public function modPow(string $base, string $exp, string $mod) : string
+    {
+        // special case: the algorithm below fails with 0 power 0 mod 1 (returns 1 instead of 0)
+        if ($base === '0' && $exp === '0' && $mod === '1') {
+            return '0';
+        }
+
+        // special case: the algorithm below fails with power 0 mod 1 (returns 1 instead of 0)
+        if ($exp === '0' && $mod === '1') {
+            return '0';
+        }
+
+        $x = $base;
+
+        $res = '1';
+
+        // numbers are positive, so we can use remainder instead of modulo
+        $x = $this->divR($x, $mod);
+
+        while ($exp !== '0') {
+            if (in_array($exp[-1], ['1', '3', '5', '7', '9'])) { // odd
+                $res = $this->divR($this->mul($res, $x), $mod);
+            }
+
+            $exp = $this->divQ($exp, '2');
+            $x = $this->divR($this->mul($x, $x), $mod);
+        }
+
+        return $res;
+    }
+
+    /**
+     * Adapted from https://cp-algorithms.com/num_methods/roots_newton.html
+     */
+    #[Override]
+    public function sqrt(string $n) : string
+    {
+        if ($n === '0') {
+            return '0';
+        }
+
+        // initial approximation
+        $x = \str_repeat('9', \intdiv(\strlen($n), 2) ?: 1);
+
+        $decreased = false;
+
+        for (;;) {
+            $nx = $this->divQ($this->add($x, $this->divQ($n, $x)), '2');
+
+            if ($x === $nx || $this->cmp($nx, $x) > 0 && $decreased) {
+                break;
+            }
+
+            $decreased = $this->cmp($nx, $x) < 0;
+            $x = $nx;
+        }
+
+        return $x;
+    }
+
+    /**
+     * Performs the addition of two non-signed large integers.
+     */
+    private function doAdd(string $a, string $b) : string
+    {
+        [$a, $b, $length] = $this->pad($a, $b);
+
+        $carry = 0;
+        $result = '';
+
+        for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) {
+            $blockLength = $this->maxDigits;
+
+            if ($i < 0) {
+                $blockLength += $i;
+                /** @psalm-suppress LoopInvalidation */
+                $i = 0;
+            }
+
+            /** @psalm-var numeric-string $blockA */
+            $blockA = \substr($a, $i, $blockLength);
+
+            /** @psalm-var numeric-string $blockB */
+            $blockB = \substr($b, $i, $blockLength);
+
+            $sum = (string) ($blockA + $blockB + $carry);
+            $sumLength = \strlen($sum);
+
+            if ($sumLength > $blockLength) {
+                $sum = \substr($sum, 1);
+                $carry = 1;
+            } else {
+                if ($sumLength < $blockLength) {
+                    $sum = \str_repeat('0', $blockLength - $sumLength) . $sum;
+                }
+                $carry = 0;
+            }
+
+            $result = $sum . $result;
+
+            if ($i === 0) {
+                break;
+            }
+        }
+
+        if ($carry === 1) {
+            $result = '1' . $result;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Performs the subtraction of two non-signed large integers.
+     */
+    private function doSub(string $a, string $b) : string
+    {
+        if ($a === $b) {
+            return '0';
+        }
+
+        // Ensure that we always subtract to a positive result: biggest minus smallest.
+        $cmp = $this->doCmp($a, $b);
+
+        $invert = ($cmp === -1);
+
+        if ($invert) {
+            $c = $a;
+            $a = $b;
+            $b = $c;
+        }
+
+        [$a, $b, $length] = $this->pad($a, $b);
+
+        $carry = 0;
+        $result = '';
+
+        $complement = 10 ** $this->maxDigits;
+
+        for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) {
+            $blockLength = $this->maxDigits;
+
+            if ($i < 0) {
+                $blockLength += $i;
+                /** @psalm-suppress LoopInvalidation */
+                $i = 0;
+            }
+
+            /** @psalm-var numeric-string $blockA */
+            $blockA = \substr($a, $i, $blockLength);
+
+            /** @psalm-var numeric-string $blockB */
+            $blockB = \substr($b, $i, $blockLength);
+
+            $sum = $blockA - $blockB - $carry;
+
+            if ($sum < 0) {
+                $sum += $complement;
+                $carry = 1;
+            } else {
+                $carry = 0;
+            }
+
+            $sum = (string) $sum;
+            $sumLength = \strlen($sum);
+
+            if ($sumLength < $blockLength) {
+                $sum = \str_repeat('0', $blockLength - $sumLength) . $sum;
+            }
+
+            $result = $sum . $result;
+
+            if ($i === 0) {
+                break;
+            }
+        }
+
+        // Carry cannot be 1 when the loop ends, as a > b
+        assert($carry === 0);
+
+        $result = \ltrim($result, '0');
+
+        if ($invert) {
+            $result = $this->neg($result);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Performs the multiplication of two non-signed large integers.
+     */
+    private function doMul(string $a, string $b) : string
+    {
+        $x = \strlen($a);
+        $y = \strlen($b);
+
+        $maxDigits = \intdiv($this->maxDigits, 2);
+        $complement = 10 ** $maxDigits;
+
+        $result = '0';
+
+        for ($i = $x - $maxDigits;; $i -= $maxDigits) {
+            $blockALength = $maxDigits;
+
+            if ($i < 0) {
+                $blockALength += $i;
+                /** @psalm-suppress LoopInvalidation */
+                $i = 0;
+            }
+
+            $blockA = (int) \substr($a, $i, $blockALength);
+
+            $line = '';
+            $carry = 0;
+
+            for ($j = $y - $maxDigits;; $j -= $maxDigits) {
+                $blockBLength = $maxDigits;
+
+                if ($j < 0) {
+                    $blockBLength += $j;
+                    /** @psalm-suppress LoopInvalidation */
+                    $j = 0;
+                }
+
+                $blockB = (int) \substr($b, $j, $blockBLength);
+
+                $mul = $blockA * $blockB + $carry;
+                $value = $mul % $complement;
+                $carry = ($mul - $value) / $complement;
+
+                $value = (string) $value;
+                $value = \str_pad($value, $maxDigits, '0', STR_PAD_LEFT);
+
+                $line = $value . $line;
+
+                if ($j === 0) {
+                    break;
+                }
+            }
+
+            if ($carry !== 0) {
+                $line = $carry . $line;
+            }
+
+            $line = \ltrim($line, '0');
+
+            if ($line !== '') {
+                $line .= \str_repeat('0', $x - $blockALength - $i);
+                $result = $this->add($result, $line);
+            }
+
+            if ($i === 0) {
+                break;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Performs the division of two non-signed large integers.
+     *
+     * @return string[] The quotient and remainder.
+     */
+    private function doDiv(string $a, string $b) : array
+    {
+        $cmp = $this->doCmp($a, $b);
+
+        if ($cmp === -1) {
+            return ['0', $a];
+        }
+
+        $x = \strlen($a);
+        $y = \strlen($b);
+
+        // we now know that a >= b && x >= y
+
+        $q = '0'; // quotient
+        $r = $a; // remainder
+        $z = $y; // focus length, always $y or $y+1
+
+        /** @psalm-var numeric-string $b */
+        $nb = $b * 1; // cast to number
+        // performance optimization in cases where the remainder will never cause int overflow
+        if (is_int(($nb - 1) * 10 + 9)) {
+            $r = (int) \substr($a, 0, $z - 1);
+
+            for ($i = $z - 1; $i < $x; $i++) {
+                $n = $r * 10 + (int) $a[$i];
+                /** @psalm-var int $nb */
+                $q .= \intdiv($n, $nb);
+                $r = $n % $nb;
+            }
+
+            return [\ltrim($q, '0') ?: '0', (string) $r];
+        }
+
+        for (;;) {
+            $focus = \substr($a, 0, $z);
+
+            $cmp = $this->doCmp($focus, $b);
+
+            if ($cmp === -1) {
+                if ($z === $x) { // remainder < dividend
+                    break;
+                }
+
+                $z++;
+            }
+
+            $zeros = \str_repeat('0', $x - $z);
+
+            $q = $this->add($q, '1' . $zeros);
+            $a = $this->sub($a, $b . $zeros);
+
+            $r = $a;
+
+            if ($r === '0') { // remainder == 0
+                break;
+            }
+
+            $x = \strlen($a);
+
+            if ($x < $y) { // remainder < dividend
+                break;
+            }
+
+            $z = $y;
+        }
+
+        return [$q, $r];
+    }
+
+    /**
+     * Compares two non-signed large numbers.
+     *
+     * @psalm-return -1|0|1
+     */
+    private function doCmp(string $a, string $b) : int
+    {
+        $x = \strlen($a);
+        $y = \strlen($b);
+
+        $cmp = $x <=> $y;
+
+        if ($cmp !== 0) {
+            return $cmp;
+        }
+
+        return \strcmp($a, $b) <=> 0; // enforce -1|0|1
+    }
+
+    /**
+     * Pads the left of one of the given numbers with zeros if necessary to make both numbers the same length.
+     *
+     * The numbers must only consist of digits, without leading minus sign.
+     *
+     * @return array{string, string, int}
+     */
+    private function pad(string $a, string $b) : array
+    {
+        $x = \strlen($a);
+        $y = \strlen($b);
+
+        if ($x > $y) {
+            $b = \str_repeat('0', $x - $y) . $b;
+
+            return [$a, $b, $x];
+        }
+
+        if ($x < $y) {
+            $a = \str_repeat('0', $y - $x) . $a;
+
+            return [$a, $b, $y];
+        }
+
+        return [$a, $b, $x];
+    }
+}

+ 98 - 0
vendor/brick/math/src/RoundingMode.php

@@ -0,0 +1,98 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Brick\Math;
+
+/**
+ * Specifies a rounding behavior for numerical operations capable of discarding precision.
+ *
+ * Each rounding mode indicates how the least significant returned digit of a rounded result
+ * is to be calculated. If fewer digits are returned than the digits needed to represent the
+ * exact numerical result, the discarded digits will be referred to as the discarded fraction
+ * regardless the digits' contribution to the value of the number. In other words, considered
+ * as a numerical value, the discarded fraction could have an absolute value greater than one.
+ */
+enum RoundingMode
+{
+    /**
+     * Asserts that the requested operation has an exact result, hence no rounding is necessary.
+     *
+     * If this rounding mode is specified on an operation that yields a result that
+     * cannot be represented at the requested scale, a RoundingNecessaryException is thrown.
+     */
+    case UNNECESSARY;
+
+    /**
+     * Rounds away from zero.
+     *
+     * Always increments the digit prior to a nonzero discarded fraction.
+     * Note that this rounding mode never decreases the magnitude of the calculated value.
+     */
+    case UP;
+
+    /**
+     * Rounds towards zero.
+     *
+     * Never increments the digit prior to a discarded fraction (i.e., truncates).
+     * Note that this rounding mode never increases the magnitude of the calculated value.
+     */
+    case DOWN;
+
+    /**
+     * Rounds towards positive infinity.
+     *
+     * If the result is positive, behaves as for UP; if negative, behaves as for DOWN.
+     * Note that this rounding mode never decreases the calculated value.
+     */
+    case CEILING;
+
+    /**
+     * Rounds towards negative infinity.
+     *
+     * If the result is positive, behave as for DOWN; if negative, behave as for UP.
+     * Note that this rounding mode never increases the calculated value.
+     */
+    case FLOOR;
+
+    /**
+     * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.
+     *
+     * Behaves as for UP if the discarded fraction is >= 0.5; otherwise, behaves as for DOWN.
+     * Note that this is the rounding mode commonly taught at school.
+     */
+    case HALF_UP;
+
+    /**
+     * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round down.
+     *
+     * Behaves as for UP if the discarded fraction is > 0.5; otherwise, behaves as for DOWN.
+     */
+    case HALF_DOWN;
+
+    /**
+     * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards positive infinity.
+     *
+     * If the result is positive, behaves as for HALF_UP; if negative, behaves as for HALF_DOWN.
+     */
+    case HALF_CEILING;
+
+    /**
+     * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards negative infinity.
+     *
+     * If the result is positive, behaves as for HALF_DOWN; if negative, behaves as for HALF_UP.
+     */
+    case HALF_FLOOR;
+
+    /**
+     * Rounds towards the "nearest neighbor" unless both neighbors are equidistant, in which case rounds towards the even neighbor.
+     *
+     * Behaves as for HALF_UP if the digit to the left of the discarded fraction is odd;
+     * behaves as for HALF_DOWN if it's even.
+     *
+     * Note that this is the rounding mode that statistically minimizes
+     * cumulative error when applied repeatedly over a sequence of calculations.
+     * It is sometimes known as "Banker's rounding", and is chiefly used in the USA.
+     */
+    case HALF_EVEN;
+}

+ 23 - 4
vendor/composer/InstalledVersions.php

@@ -32,6 +32,11 @@ class InstalledVersions
      */
     private static $installed;
 
+    /**
+     * @var bool
+     */
+    private static $installedIsLocalDir;
+
     /**
      * @var bool|null
      */
@@ -309,6 +314,12 @@ class InstalledVersions
     {
         self::$installed = $data;
         self::$installedByVendor = array();
+
+        // when using reload, we disable the duplicate protection to ensure that self::$installed data is
+        // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
+        // so we have to assume it does not, and that may result in duplicate data being returned when listing
+        // all installed packages for example
+        self::$installedIsLocalDir = false;
     }
 
     /**
@@ -322,19 +333,27 @@ class InstalledVersions
         }
 
         $installed = array();
+        $copiedLocalDir = false;
 
         if (self::$canGetVendors) {
+            $selfDir = strtr(__DIR__, '\\', '/');
             foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+                $vendorDir = strtr($vendorDir, '\\', '/');
                 if (isset(self::$installedByVendor[$vendorDir])) {
                     $installed[] = self::$installedByVendor[$vendorDir];
                 } elseif (is_file($vendorDir.'/composer/installed.php')) {
                     /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
                     $required = require $vendorDir.'/composer/installed.php';
-                    $installed[] = self::$installedByVendor[$vendorDir] = $required;
-                    if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
-                        self::$installed = $installed[count($installed) - 1];
+                    self::$installedByVendor[$vendorDir] = $required;
+                    $installed[] = $required;
+                    if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
+                        self::$installed = $required;
+                        self::$installedIsLocalDir = true;
                     }
                 }
+                if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
+                    $copiedLocalDir = true;
+                }
             }
         }
 
@@ -350,7 +369,7 @@ class InstalledVersions
             }
         }
 
-        if (self::$installed !== array()) {
+        if (self::$installed !== array() && !$copiedLocalDir) {
             $installed[] = self::$installed;
         }
 

+ 193 - 0
vendor/composer/autoload_classmap.php

@@ -19,7 +19,12 @@ return array(
     'App\\Model\\ArticleData' => $baseDir . '/app/Model/ArticleData.php',
     'App\\Model\\ArticleSurvey' => $baseDir . '/app/Model/ArticleSurvey.php',
     'App\\Model\\Category' => $baseDir . '/app/Model/Category.php',
+    'App\\Model\\ChatGroups' => $baseDir . '/app/Model/ChatGroups.php',
+    'App\\Model\\ChatGroupsMember' => $baseDir . '/app/Model/ChatGroupsMember.php',
+    'App\\Model\\ChatRecords' => $baseDir . '/app/Model/ChatRecords.php',
+    'App\\Model\\District' => $baseDir . '/app/Model/District.php',
     'App\\Model\\Good' => $baseDir . '/app/Model/Good.php',
+    'App\\Model\\JobCompany' => $baseDir . '/app/Model/JobCompany.php',
     'App\\Model\\JobEnum' => $baseDir . '/app/Model/JobEnum.php',
     'App\\Model\\JobIndustry' => $baseDir . '/app/Model/JobIndustry.php',
     'App\\Model\\JobNature' => $baseDir . '/app/Model/JobNature.php',
@@ -27,15 +32,35 @@ return array(
     'App\\Model\\JobRecruiting' => $baseDir . '/app/Model/JobRecruiting.php',
     'App\\Model\\Link' => $baseDir . '/app/Model/Link.php',
     'App\\Model\\Model' => $baseDir . '/app/Model/Model.php',
+    'App\\Model\\Notice' => $baseDir . '/app/Model/Notice.php',
+    'App\\Model\\User' => $baseDir . '/app/Model/User.php',
+    'App\\Model\\UserInfo' => $baseDir . '/app/Model/UserInfo.php',
     'App\\Model\\Web' => $baseDir . '/app/Model/Web.php',
     'App\\Model\\Website' => $baseDir . '/app/Model/Website.php',
     'App\\Model\\WebsiteCategory' => $baseDir . '/app/Model/WebsiteCategory.php',
     'App\\Model\\WebsiteColumn' => $baseDir . '/app/Model/WebsiteColumn.php',
+    'App\\Model\\WebsiteGroup' => $baseDir . '/app/Model/WebsiteGroup.php',
     'App\\Model\\WebsiteRole' => $baseDir . '/app/Model/WebsiteRole.php',
     'App\\Model\\WebsiteRoleUser' => $baseDir . '/app/Model/WebsiteRoleUser.php',
     'App\\Model\\jobHunting' => $baseDir . '/app/Model/jobHunting.php',
+    'App\\Tools\\PublicData' => $baseDir . '/app/Tools/PublicData.php',
     'App\\Tools\\Result' => $baseDir . '/app/Tools/Result.php',
     'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
+    'Brick\\Math\\BigDecimal' => $vendorDir . '/brick/math/src/BigDecimal.php',
+    'Brick\\Math\\BigInteger' => $vendorDir . '/brick/math/src/BigInteger.php',
+    'Brick\\Math\\BigNumber' => $vendorDir . '/brick/math/src/BigNumber.php',
+    'Brick\\Math\\BigRational' => $vendorDir . '/brick/math/src/BigRational.php',
+    'Brick\\Math\\Exception\\DivisionByZeroException' => $vendorDir . '/brick/math/src/Exception/DivisionByZeroException.php',
+    'Brick\\Math\\Exception\\IntegerOverflowException' => $vendorDir . '/brick/math/src/Exception/IntegerOverflowException.php',
+    'Brick\\Math\\Exception\\MathException' => $vendorDir . '/brick/math/src/Exception/MathException.php',
+    'Brick\\Math\\Exception\\NegativeNumberException' => $vendorDir . '/brick/math/src/Exception/NegativeNumberException.php',
+    'Brick\\Math\\Exception\\NumberFormatException' => $vendorDir . '/brick/math/src/Exception/NumberFormatException.php',
+    'Brick\\Math\\Exception\\RoundingNecessaryException' => $vendorDir . '/brick/math/src/Exception/RoundingNecessaryException.php',
+    'Brick\\Math\\Internal\\Calculator' => $vendorDir . '/brick/math/src/Internal/Calculator.php',
+    'Brick\\Math\\Internal\\Calculator\\BcMathCalculator' => $vendorDir . '/brick/math/src/Internal/Calculator/BcMathCalculator.php',
+    'Brick\\Math\\Internal\\Calculator\\GmpCalculator' => $vendorDir . '/brick/math/src/Internal/Calculator/GmpCalculator.php',
+    'Brick\\Math\\Internal\\Calculator\\NativeCalculator' => $vendorDir . '/brick/math/src/Internal/Calculator/NativeCalculator.php',
+    'Brick\\Math\\RoundingMode' => $vendorDir . '/brick/math/src/RoundingMode.php',
     'CURLStringFile' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php',
     'Carbon\\AbstractTranslator' => $vendorDir . '/nesbot/carbon/src/Carbon/AbstractTranslator.php',
     'Carbon\\Carbon' => $vendorDir . '/nesbot/carbon/src/Carbon/Carbon.php',
@@ -1305,6 +1330,16 @@ return array(
     'Hyperf\\Nacos\\Provider\\InstanceProvider' => $vendorDir . '/hyperf/nacos/src/Provider/InstanceProvider.php',
     'Hyperf\\Nacos\\Provider\\OperatorProvider' => $vendorDir . '/hyperf/nacos/src/Provider/OperatorProvider.php',
     'Hyperf\\Nacos\\Provider\\ServiceProvider' => $vendorDir . '/hyperf/nacos/src/Provider/ServiceProvider.php',
+    'Hyperf\\Paginator\\AbstractCursorPaginator' => $vendorDir . '/hyperf/paginator/src/AbstractCursorPaginator.php',
+    'Hyperf\\Paginator\\AbstractPaginator' => $vendorDir . '/hyperf/paginator/src/AbstractPaginator.php',
+    'Hyperf\\Paginator\\ConfigProvider' => $vendorDir . '/hyperf/paginator/src/ConfigProvider.php',
+    'Hyperf\\Paginator\\Contract\\CursorPaginator' => $vendorDir . '/hyperf/paginator/src/Contract/CursorPaginator.php',
+    'Hyperf\\Paginator\\Cursor' => $vendorDir . '/hyperf/paginator/src/Cursor.php',
+    'Hyperf\\Paginator\\CursorPaginator' => $vendorDir . '/hyperf/paginator/src/CursorPaginator.php',
+    'Hyperf\\Paginator\\LengthAwarePaginator' => $vendorDir . '/hyperf/paginator/src/LengthAwarePaginator.php',
+    'Hyperf\\Paginator\\Listener\\PageResolverListener' => $vendorDir . '/hyperf/paginator/src/Listener/PageResolverListener.php',
+    'Hyperf\\Paginator\\Paginator' => $vendorDir . '/hyperf/paginator/src/Paginator.php',
+    'Hyperf\\Paginator\\UrlWindow' => $vendorDir . '/hyperf/paginator/src/UrlWindow.php',
     'Hyperf\\Pipeline\\Pipeline' => $vendorDir . '/hyperf/pipeline/src/Pipeline.php',
     'Hyperf\\Pool\\Channel' => $vendorDir . '/hyperf/pool/src/Channel.php',
     'Hyperf\\Pool\\ConfigProvider' => $vendorDir . '/hyperf/pool/src/ConfigProvider.php',
@@ -1449,6 +1484,22 @@ return array(
     'Hyperf\\ServiceGovernance\\Exception\\RegisterInstanceException' => $vendorDir . '/hyperf/service-governance/src/Exception/RegisterInstanceException.php',
     'Hyperf\\ServiceGovernance\\Listener\\RegisterServiceListener' => $vendorDir . '/hyperf/service-governance/src/Listener/RegisterServiceListener.php',
     'Hyperf\\ServiceGovernance\\ServiceManager' => $vendorDir . '/hyperf/service-governance/src/ServiceManager.php',
+    'Hyperf\\Snowflake\\Concern\\Snowflake' => $vendorDir . '/hyperf/snowflake/src/Concern/Snowflake.php',
+    'Hyperf\\Snowflake\\ConfigProvider' => $vendorDir . '/hyperf/snowflake/src/ConfigProvider.php',
+    'Hyperf\\Snowflake\\Configuration' => $vendorDir . '/hyperf/snowflake/src/Configuration.php',
+    'Hyperf\\Snowflake\\ConfigurationInterface' => $vendorDir . '/hyperf/snowflake/src/ConfigurationInterface.php',
+    'Hyperf\\Snowflake\\Exception\\SnowflakeException' => $vendorDir . '/hyperf/snowflake/src/Exception/SnowflakeException.php',
+    'Hyperf\\Snowflake\\IdGenerator' => $vendorDir . '/hyperf/snowflake/src/IdGenerator.php',
+    'Hyperf\\Snowflake\\IdGeneratorInterface' => $vendorDir . '/hyperf/snowflake/src/IdGeneratorInterface.php',
+    'Hyperf\\Snowflake\\IdGenerator\\SnowflakeIdGenerator' => $vendorDir . '/hyperf/snowflake/src/IdGenerator/SnowflakeIdGenerator.php',
+    'Hyperf\\Snowflake\\Meta' => $vendorDir . '/hyperf/snowflake/src/Meta.php',
+    'Hyperf\\Snowflake\\MetaGenerator' => $vendorDir . '/hyperf/snowflake/src/MetaGenerator.php',
+    'Hyperf\\Snowflake\\MetaGeneratorFactory' => $vendorDir . '/hyperf/snowflake/src/MetaGeneratorFactory.php',
+    'Hyperf\\Snowflake\\MetaGeneratorInterface' => $vendorDir . '/hyperf/snowflake/src/MetaGeneratorInterface.php',
+    'Hyperf\\Snowflake\\MetaGenerator\\RandomMilliSecondMetaGenerator' => $vendorDir . '/hyperf/snowflake/src/MetaGenerator/RandomMilliSecondMetaGenerator.php',
+    'Hyperf\\Snowflake\\MetaGenerator\\RedisMetaGenerator' => $vendorDir . '/hyperf/snowflake/src/MetaGenerator/RedisMetaGenerator.php',
+    'Hyperf\\Snowflake\\MetaGenerator\\RedisMilliSecondMetaGenerator' => $vendorDir . '/hyperf/snowflake/src/MetaGenerator/RedisMilliSecondMetaGenerator.php',
+    'Hyperf\\Snowflake\\MetaGenerator\\RedisSecondMetaGenerator' => $vendorDir . '/hyperf/snowflake/src/MetaGenerator/RedisSecondMetaGenerator.php',
     'Hyperf\\Stdlib\\SplPriorityQueue' => $vendorDir . '/hyperf/stdlib/src/SplPriorityQueue.php',
     'Hyperf\\Stringable\\Pluralizer' => $vendorDir . '/hyperf/stringable/src/Pluralizer.php',
     'Hyperf\\Stringable\\Str' => $vendorDir . '/hyperf/stringable/src/Str.php',
@@ -3773,6 +3824,148 @@ return array(
     'Psr\\SimpleCache\\CacheException' => $vendorDir . '/psr/simple-cache/src/CacheException.php',
     'Psr\\SimpleCache\\CacheInterface' => $vendorDir . '/psr/simple-cache/src/CacheInterface.php',
     'Psr\\SimpleCache\\InvalidArgumentException' => $vendorDir . '/psr/simple-cache/src/InvalidArgumentException.php',
+    'Ramsey\\Collection\\AbstractArray' => $vendorDir . '/ramsey/collection/src/AbstractArray.php',
+    'Ramsey\\Collection\\AbstractCollection' => $vendorDir . '/ramsey/collection/src/AbstractCollection.php',
+    'Ramsey\\Collection\\AbstractSet' => $vendorDir . '/ramsey/collection/src/AbstractSet.php',
+    'Ramsey\\Collection\\ArrayInterface' => $vendorDir . '/ramsey/collection/src/ArrayInterface.php',
+    'Ramsey\\Collection\\Collection' => $vendorDir . '/ramsey/collection/src/Collection.php',
+    'Ramsey\\Collection\\CollectionInterface' => $vendorDir . '/ramsey/collection/src/CollectionInterface.php',
+    'Ramsey\\Collection\\DoubleEndedQueue' => $vendorDir . '/ramsey/collection/src/DoubleEndedQueue.php',
+    'Ramsey\\Collection\\DoubleEndedQueueInterface' => $vendorDir . '/ramsey/collection/src/DoubleEndedQueueInterface.php',
+    'Ramsey\\Collection\\Exception\\CollectionException' => $vendorDir . '/ramsey/collection/src/Exception/CollectionException.php',
+    'Ramsey\\Collection\\Exception\\CollectionMismatchException' => $vendorDir . '/ramsey/collection/src/Exception/CollectionMismatchException.php',
+    'Ramsey\\Collection\\Exception\\InvalidArgumentException' => $vendorDir . '/ramsey/collection/src/Exception/InvalidArgumentException.php',
+    'Ramsey\\Collection\\Exception\\InvalidPropertyOrMethod' => $vendorDir . '/ramsey/collection/src/Exception/InvalidPropertyOrMethod.php',
+    'Ramsey\\Collection\\Exception\\NoSuchElementException' => $vendorDir . '/ramsey/collection/src/Exception/NoSuchElementException.php',
+    'Ramsey\\Collection\\Exception\\OutOfBoundsException' => $vendorDir . '/ramsey/collection/src/Exception/OutOfBoundsException.php',
+    'Ramsey\\Collection\\Exception\\UnsupportedOperationException' => $vendorDir . '/ramsey/collection/src/Exception/UnsupportedOperationException.php',
+    'Ramsey\\Collection\\GenericArray' => $vendorDir . '/ramsey/collection/src/GenericArray.php',
+    'Ramsey\\Collection\\Map\\AbstractMap' => $vendorDir . '/ramsey/collection/src/Map/AbstractMap.php',
+    'Ramsey\\Collection\\Map\\AbstractTypedMap' => $vendorDir . '/ramsey/collection/src/Map/AbstractTypedMap.php',
+    'Ramsey\\Collection\\Map\\AssociativeArrayMap' => $vendorDir . '/ramsey/collection/src/Map/AssociativeArrayMap.php',
+    'Ramsey\\Collection\\Map\\MapInterface' => $vendorDir . '/ramsey/collection/src/Map/MapInterface.php',
+    'Ramsey\\Collection\\Map\\NamedParameterMap' => $vendorDir . '/ramsey/collection/src/Map/NamedParameterMap.php',
+    'Ramsey\\Collection\\Map\\TypedMap' => $vendorDir . '/ramsey/collection/src/Map/TypedMap.php',
+    'Ramsey\\Collection\\Map\\TypedMapInterface' => $vendorDir . '/ramsey/collection/src/Map/TypedMapInterface.php',
+    'Ramsey\\Collection\\Queue' => $vendorDir . '/ramsey/collection/src/Queue.php',
+    'Ramsey\\Collection\\QueueInterface' => $vendorDir . '/ramsey/collection/src/QueueInterface.php',
+    'Ramsey\\Collection\\Set' => $vendorDir . '/ramsey/collection/src/Set.php',
+    'Ramsey\\Collection\\Sort' => $vendorDir . '/ramsey/collection/src/Sort.php',
+    'Ramsey\\Collection\\Tool\\TypeTrait' => $vendorDir . '/ramsey/collection/src/Tool/TypeTrait.php',
+    'Ramsey\\Collection\\Tool\\ValueExtractorTrait' => $vendorDir . '/ramsey/collection/src/Tool/ValueExtractorTrait.php',
+    'Ramsey\\Collection\\Tool\\ValueToStringTrait' => $vendorDir . '/ramsey/collection/src/Tool/ValueToStringTrait.php',
+    'Ramsey\\Uuid\\BinaryUtils' => $vendorDir . '/ramsey/uuid/src/BinaryUtils.php',
+    'Ramsey\\Uuid\\Builder\\BuilderCollection' => $vendorDir . '/ramsey/uuid/src/Builder/BuilderCollection.php',
+    'Ramsey\\Uuid\\Builder\\DefaultUuidBuilder' => $vendorDir . '/ramsey/uuid/src/Builder/DefaultUuidBuilder.php',
+    'Ramsey\\Uuid\\Builder\\DegradedUuidBuilder' => $vendorDir . '/ramsey/uuid/src/Builder/DegradedUuidBuilder.php',
+    'Ramsey\\Uuid\\Builder\\FallbackBuilder' => $vendorDir . '/ramsey/uuid/src/Builder/FallbackBuilder.php',
+    'Ramsey\\Uuid\\Builder\\UuidBuilderInterface' => $vendorDir . '/ramsey/uuid/src/Builder/UuidBuilderInterface.php',
+    'Ramsey\\Uuid\\Codec\\CodecInterface' => $vendorDir . '/ramsey/uuid/src/Codec/CodecInterface.php',
+    'Ramsey\\Uuid\\Codec\\GuidStringCodec' => $vendorDir . '/ramsey/uuid/src/Codec/GuidStringCodec.php',
+    'Ramsey\\Uuid\\Codec\\OrderedTimeCodec' => $vendorDir . '/ramsey/uuid/src/Codec/OrderedTimeCodec.php',
+    'Ramsey\\Uuid\\Codec\\StringCodec' => $vendorDir . '/ramsey/uuid/src/Codec/StringCodec.php',
+    'Ramsey\\Uuid\\Codec\\TimestampFirstCombCodec' => $vendorDir . '/ramsey/uuid/src/Codec/TimestampFirstCombCodec.php',
+    'Ramsey\\Uuid\\Codec\\TimestampLastCombCodec' => $vendorDir . '/ramsey/uuid/src/Codec/TimestampLastCombCodec.php',
+    'Ramsey\\Uuid\\Converter\\NumberConverterInterface' => $vendorDir . '/ramsey/uuid/src/Converter/NumberConverterInterface.php',
+    'Ramsey\\Uuid\\Converter\\Number\\BigNumberConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Number/BigNumberConverter.php',
+    'Ramsey\\Uuid\\Converter\\Number\\DegradedNumberConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Number/DegradedNumberConverter.php',
+    'Ramsey\\Uuid\\Converter\\Number\\GenericNumberConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Number/GenericNumberConverter.php',
+    'Ramsey\\Uuid\\Converter\\TimeConverterInterface' => $vendorDir . '/ramsey/uuid/src/Converter/TimeConverterInterface.php',
+    'Ramsey\\Uuid\\Converter\\Time\\BigNumberTimeConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Time/BigNumberTimeConverter.php',
+    'Ramsey\\Uuid\\Converter\\Time\\DegradedTimeConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Time/DegradedTimeConverter.php',
+    'Ramsey\\Uuid\\Converter\\Time\\GenericTimeConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Time/GenericTimeConverter.php',
+    'Ramsey\\Uuid\\Converter\\Time\\PhpTimeConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Time/PhpTimeConverter.php',
+    'Ramsey\\Uuid\\Converter\\Time\\UnixTimeConverter' => $vendorDir . '/ramsey/uuid/src/Converter/Time/UnixTimeConverter.php',
+    'Ramsey\\Uuid\\DegradedUuid' => $vendorDir . '/ramsey/uuid/src/DegradedUuid.php',
+    'Ramsey\\Uuid\\DeprecatedUuidInterface' => $vendorDir . '/ramsey/uuid/src/DeprecatedUuidInterface.php',
+    'Ramsey\\Uuid\\DeprecatedUuidMethodsTrait' => $vendorDir . '/ramsey/uuid/src/DeprecatedUuidMethodsTrait.php',
+    'Ramsey\\Uuid\\Exception\\BuilderNotFoundException' => $vendorDir . '/ramsey/uuid/src/Exception/BuilderNotFoundException.php',
+    'Ramsey\\Uuid\\Exception\\DateTimeException' => $vendorDir . '/ramsey/uuid/src/Exception/DateTimeException.php',
+    'Ramsey\\Uuid\\Exception\\DceSecurityException' => $vendorDir . '/ramsey/uuid/src/Exception/DceSecurityException.php',
+    'Ramsey\\Uuid\\Exception\\InvalidArgumentException' => $vendorDir . '/ramsey/uuid/src/Exception/InvalidArgumentException.php',
+    'Ramsey\\Uuid\\Exception\\InvalidBytesException' => $vendorDir . '/ramsey/uuid/src/Exception/InvalidBytesException.php',
+    'Ramsey\\Uuid\\Exception\\InvalidUuidStringException' => $vendorDir . '/ramsey/uuid/src/Exception/InvalidUuidStringException.php',
+    'Ramsey\\Uuid\\Exception\\NameException' => $vendorDir . '/ramsey/uuid/src/Exception/NameException.php',
+    'Ramsey\\Uuid\\Exception\\NodeException' => $vendorDir . '/ramsey/uuid/src/Exception/NodeException.php',
+    'Ramsey\\Uuid\\Exception\\RandomSourceException' => $vendorDir . '/ramsey/uuid/src/Exception/RandomSourceException.php',
+    'Ramsey\\Uuid\\Exception\\TimeSourceException' => $vendorDir . '/ramsey/uuid/src/Exception/TimeSourceException.php',
+    'Ramsey\\Uuid\\Exception\\UnableToBuildUuidException' => $vendorDir . '/ramsey/uuid/src/Exception/UnableToBuildUuidException.php',
+    'Ramsey\\Uuid\\Exception\\UnsupportedOperationException' => $vendorDir . '/ramsey/uuid/src/Exception/UnsupportedOperationException.php',
+    'Ramsey\\Uuid\\Exception\\UuidExceptionInterface' => $vendorDir . '/ramsey/uuid/src/Exception/UuidExceptionInterface.php',
+    'Ramsey\\Uuid\\FeatureSet' => $vendorDir . '/ramsey/uuid/src/FeatureSet.php',
+    'Ramsey\\Uuid\\Fields\\FieldsInterface' => $vendorDir . '/ramsey/uuid/src/Fields/FieldsInterface.php',
+    'Ramsey\\Uuid\\Fields\\SerializableFieldsTrait' => $vendorDir . '/ramsey/uuid/src/Fields/SerializableFieldsTrait.php',
+    'Ramsey\\Uuid\\Generator\\CombGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/CombGenerator.php',
+    'Ramsey\\Uuid\\Generator\\DceSecurityGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/DceSecurityGenerator.php',
+    'Ramsey\\Uuid\\Generator\\DceSecurityGeneratorInterface' => $vendorDir . '/ramsey/uuid/src/Generator/DceSecurityGeneratorInterface.php',
+    'Ramsey\\Uuid\\Generator\\DefaultNameGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/DefaultNameGenerator.php',
+    'Ramsey\\Uuid\\Generator\\DefaultTimeGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/DefaultTimeGenerator.php',
+    'Ramsey\\Uuid\\Generator\\NameGeneratorFactory' => $vendorDir . '/ramsey/uuid/src/Generator/NameGeneratorFactory.php',
+    'Ramsey\\Uuid\\Generator\\NameGeneratorInterface' => $vendorDir . '/ramsey/uuid/src/Generator/NameGeneratorInterface.php',
+    'Ramsey\\Uuid\\Generator\\PeclUuidNameGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/PeclUuidNameGenerator.php',
+    'Ramsey\\Uuid\\Generator\\PeclUuidRandomGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/PeclUuidRandomGenerator.php',
+    'Ramsey\\Uuid\\Generator\\PeclUuidTimeGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/PeclUuidTimeGenerator.php',
+    'Ramsey\\Uuid\\Generator\\RandomBytesGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/RandomBytesGenerator.php',
+    'Ramsey\\Uuid\\Generator\\RandomGeneratorFactory' => $vendorDir . '/ramsey/uuid/src/Generator/RandomGeneratorFactory.php',
+    'Ramsey\\Uuid\\Generator\\RandomGeneratorInterface' => $vendorDir . '/ramsey/uuid/src/Generator/RandomGeneratorInterface.php',
+    'Ramsey\\Uuid\\Generator\\RandomLibAdapter' => $vendorDir . '/ramsey/uuid/src/Generator/RandomLibAdapter.php',
+    'Ramsey\\Uuid\\Generator\\TimeGeneratorFactory' => $vendorDir . '/ramsey/uuid/src/Generator/TimeGeneratorFactory.php',
+    'Ramsey\\Uuid\\Generator\\TimeGeneratorInterface' => $vendorDir . '/ramsey/uuid/src/Generator/TimeGeneratorInterface.php',
+    'Ramsey\\Uuid\\Generator\\UnixTimeGenerator' => $vendorDir . '/ramsey/uuid/src/Generator/UnixTimeGenerator.php',
+    'Ramsey\\Uuid\\Guid\\Fields' => $vendorDir . '/ramsey/uuid/src/Guid/Fields.php',
+    'Ramsey\\Uuid\\Guid\\Guid' => $vendorDir . '/ramsey/uuid/src/Guid/Guid.php',
+    'Ramsey\\Uuid\\Guid\\GuidBuilder' => $vendorDir . '/ramsey/uuid/src/Guid/GuidBuilder.php',
+    'Ramsey\\Uuid\\Lazy\\LazyUuidFromString' => $vendorDir . '/ramsey/uuid/src/Lazy/LazyUuidFromString.php',
+    'Ramsey\\Uuid\\Math\\BrickMathCalculator' => $vendorDir . '/ramsey/uuid/src/Math/BrickMathCalculator.php',
+    'Ramsey\\Uuid\\Math\\CalculatorInterface' => $vendorDir . '/ramsey/uuid/src/Math/CalculatorInterface.php',
+    'Ramsey\\Uuid\\Math\\RoundingMode' => $vendorDir . '/ramsey/uuid/src/Math/RoundingMode.php',
+    'Ramsey\\Uuid\\Nonstandard\\Fields' => $vendorDir . '/ramsey/uuid/src/Nonstandard/Fields.php',
+    'Ramsey\\Uuid\\Nonstandard\\Uuid' => $vendorDir . '/ramsey/uuid/src/Nonstandard/Uuid.php',
+    'Ramsey\\Uuid\\Nonstandard\\UuidBuilder' => $vendorDir . '/ramsey/uuid/src/Nonstandard/UuidBuilder.php',
+    'Ramsey\\Uuid\\Nonstandard\\UuidV6' => $vendorDir . '/ramsey/uuid/src/Nonstandard/UuidV6.php',
+    'Ramsey\\Uuid\\Provider\\DceSecurityProviderInterface' => $vendorDir . '/ramsey/uuid/src/Provider/DceSecurityProviderInterface.php',
+    'Ramsey\\Uuid\\Provider\\Dce\\SystemDceSecurityProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Dce/SystemDceSecurityProvider.php',
+    'Ramsey\\Uuid\\Provider\\NodeProviderInterface' => $vendorDir . '/ramsey/uuid/src/Provider/NodeProviderInterface.php',
+    'Ramsey\\Uuid\\Provider\\Node\\FallbackNodeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Node/FallbackNodeProvider.php',
+    'Ramsey\\Uuid\\Provider\\Node\\NodeProviderCollection' => $vendorDir . '/ramsey/uuid/src/Provider/Node/NodeProviderCollection.php',
+    'Ramsey\\Uuid\\Provider\\Node\\RandomNodeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Node/RandomNodeProvider.php',
+    'Ramsey\\Uuid\\Provider\\Node\\StaticNodeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Node/StaticNodeProvider.php',
+    'Ramsey\\Uuid\\Provider\\Node\\SystemNodeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Node/SystemNodeProvider.php',
+    'Ramsey\\Uuid\\Provider\\TimeProviderInterface' => $vendorDir . '/ramsey/uuid/src/Provider/TimeProviderInterface.php',
+    'Ramsey\\Uuid\\Provider\\Time\\FixedTimeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Time/FixedTimeProvider.php',
+    'Ramsey\\Uuid\\Provider\\Time\\SystemTimeProvider' => $vendorDir . '/ramsey/uuid/src/Provider/Time/SystemTimeProvider.php',
+    'Ramsey\\Uuid\\Rfc4122\\Fields' => $vendorDir . '/ramsey/uuid/src/Rfc4122/Fields.php',
+    'Ramsey\\Uuid\\Rfc4122\\FieldsInterface' => $vendorDir . '/ramsey/uuid/src/Rfc4122/FieldsInterface.php',
+    'Ramsey\\Uuid\\Rfc4122\\MaxTrait' => $vendorDir . '/ramsey/uuid/src/Rfc4122/MaxTrait.php',
+    'Ramsey\\Uuid\\Rfc4122\\MaxUuid' => $vendorDir . '/ramsey/uuid/src/Rfc4122/MaxUuid.php',
+    'Ramsey\\Uuid\\Rfc4122\\NilTrait' => $vendorDir . '/ramsey/uuid/src/Rfc4122/NilTrait.php',
+    'Ramsey\\Uuid\\Rfc4122\\NilUuid' => $vendorDir . '/ramsey/uuid/src/Rfc4122/NilUuid.php',
+    'Ramsey\\Uuid\\Rfc4122\\TimeTrait' => $vendorDir . '/ramsey/uuid/src/Rfc4122/TimeTrait.php',
+    'Ramsey\\Uuid\\Rfc4122\\UuidBuilder' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidBuilder.php',
+    'Ramsey\\Uuid\\Rfc4122\\UuidInterface' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidInterface.php',
+    'Ramsey\\Uuid\\Rfc4122\\UuidV1' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV1.php',
+    'Ramsey\\Uuid\\Rfc4122\\UuidV2' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV2.php',
+    'Ramsey\\Uuid\\Rfc4122\\UuidV3' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV3.php',
+    'Ramsey\\Uuid\\Rfc4122\\UuidV4' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV4.php',
+    'Ramsey\\Uuid\\Rfc4122\\UuidV5' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV5.php',
+    'Ramsey\\Uuid\\Rfc4122\\UuidV6' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV6.php',
+    'Ramsey\\Uuid\\Rfc4122\\UuidV7' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV7.php',
+    'Ramsey\\Uuid\\Rfc4122\\UuidV8' => $vendorDir . '/ramsey/uuid/src/Rfc4122/UuidV8.php',
+    'Ramsey\\Uuid\\Rfc4122\\Validator' => $vendorDir . '/ramsey/uuid/src/Rfc4122/Validator.php',
+    'Ramsey\\Uuid\\Rfc4122\\VariantTrait' => $vendorDir . '/ramsey/uuid/src/Rfc4122/VariantTrait.php',
+    'Ramsey\\Uuid\\Rfc4122\\VersionTrait' => $vendorDir . '/ramsey/uuid/src/Rfc4122/VersionTrait.php',
+    'Ramsey\\Uuid\\Type\\Decimal' => $vendorDir . '/ramsey/uuid/src/Type/Decimal.php',
+    'Ramsey\\Uuid\\Type\\Hexadecimal' => $vendorDir . '/ramsey/uuid/src/Type/Hexadecimal.php',
+    'Ramsey\\Uuid\\Type\\Integer' => $vendorDir . '/ramsey/uuid/src/Type/Integer.php',
+    'Ramsey\\Uuid\\Type\\NumberInterface' => $vendorDir . '/ramsey/uuid/src/Type/NumberInterface.php',
+    'Ramsey\\Uuid\\Type\\Time' => $vendorDir . '/ramsey/uuid/src/Type/Time.php',
+    'Ramsey\\Uuid\\Type\\TypeInterface' => $vendorDir . '/ramsey/uuid/src/Type/TypeInterface.php',
+    'Ramsey\\Uuid\\Uuid' => $vendorDir . '/ramsey/uuid/src/Uuid.php',
+    'Ramsey\\Uuid\\UuidFactory' => $vendorDir . '/ramsey/uuid/src/UuidFactory.php',
+    'Ramsey\\Uuid\\UuidFactoryInterface' => $vendorDir . '/ramsey/uuid/src/UuidFactoryInterface.php',
+    'Ramsey\\Uuid\\UuidInterface' => $vendorDir . '/ramsey/uuid/src/UuidInterface.php',
+    'Ramsey\\Uuid\\Validator\\GenericValidator' => $vendorDir . '/ramsey/uuid/src/Validator/GenericValidator.php',
+    'Ramsey\\Uuid\\Validator\\ValidatorInterface' => $vendorDir . '/ramsey/uuid/src/Validator/ValidatorInterface.php',
     'React\\Cache\\ArrayCache' => $vendorDir . '/react/cache/src/ArrayCache.php',
     'React\\Cache\\CacheInterface' => $vendorDir . '/react/cache/src/CacheInterface.php',
     'React\\ChildProcess\\Process' => $vendorDir . '/react/child-process/src/Process.php',

+ 1 - 0
vendor/composer/autoload_files.php

@@ -36,4 +36,5 @@ return array(
     'c72349b1fe8d0deeedd3a52e8aa814d8' => $vendorDir . '/mockery/mockery/library/helpers.php',
     'ce9671a430e4846b44e1c68c7611f9f5' => $vendorDir . '/mockery/mockery/library/Mockery.php',
     '9b38cf48e83f5d8f60375221cd213eee' => $vendorDir . '/phpstan/phpstan/bootstrap.php',
+    'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php',
 );

+ 6 - 1
vendor/composer/autoload_psr4.php

@@ -34,9 +34,11 @@ return array(
     'React\\Dns\\' => array($vendorDir . '/react/dns/src'),
     'React\\ChildProcess\\' => array($vendorDir . '/react/child-process/src'),
     'React\\Cache\\' => array($vendorDir . '/react/cache/src'),
+    'Ramsey\\Uuid\\' => array($vendorDir . '/ramsey/uuid/src'),
+    'Ramsey\\Collection\\' => array($vendorDir . '/ramsey/collection/src'),
     'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
     'Psr\\Log\\' => array($vendorDir . '/psr/log/src'),
-    'Psr\\Http\\Server\\' => array($vendorDir . '/psr/http-server-handler/src', $vendorDir . '/psr/http-server-middleware/src'),
+    'Psr\\Http\\Server\\' => array($vendorDir . '/psr/http-server-middleware/src', $vendorDir . '/psr/http-server-handler/src'),
     'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
     'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
     'Psr\\EventDispatcher\\' => array($vendorDir . '/psr/event-dispatcher/src'),
@@ -59,6 +61,7 @@ return array(
     'Hyperf\\Support\\' => array($vendorDir . '/hyperf/support/src'),
     'Hyperf\\Stringable\\' => array($vendorDir . '/hyperf/stringable/src'),
     'Hyperf\\Stdlib\\' => array($vendorDir . '/hyperf/stdlib/src'),
+    'Hyperf\\Snowflake\\' => array($vendorDir . '/hyperf/snowflake/src'),
     'Hyperf\\ServiceGovernance\\' => array($vendorDir . '/hyperf/service-governance/src'),
     'Hyperf\\ServiceGovernanceNacos\\' => array($vendorDir . '/hyperf/service-governance-nacos/src'),
     'Hyperf\\ServiceGovernanceConsul\\' => array($vendorDir . '/hyperf/service-governance-consul/src'),
@@ -70,6 +73,7 @@ return array(
     'Hyperf\\Process\\' => array($vendorDir . '/hyperf/process/src'),
     'Hyperf\\Pool\\' => array($vendorDir . '/hyperf/pool/src'),
     'Hyperf\\Pipeline\\' => array($vendorDir . '/hyperf/pipeline/src'),
+    'Hyperf\\Paginator\\' => array($vendorDir . '/hyperf/paginator/src'),
     'Hyperf\\Nacos\\' => array($vendorDir . '/hyperf/nacos/src'),
     'Hyperf\\ModelListener\\' => array($vendorDir . '/hyperf/model-listener/src'),
     'Hyperf\\Memory\\' => array($vendorDir . '/hyperf/memory/src'),
@@ -127,5 +131,6 @@ return array(
     'Clue\\React\\NDJson\\' => array($vendorDir . '/clue/ndjson-react/src'),
     'Carbon\\Doctrine\\' => array($vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine'),
     'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'),
+    'Brick\\Math\\' => array($vendorDir . '/brick/math/src'),
     'App\\' => array($baseDir . '/app'),
 );

+ 224 - 2
vendor/composer/autoload_static.php

@@ -37,6 +37,7 @@ class ComposerStaticInit88f2a4d4a4e81dc7d415bcdf39930654
         'c72349b1fe8d0deeedd3a52e8aa814d8' => __DIR__ . '/..' . '/mockery/mockery/library/helpers.php',
         'ce9671a430e4846b44e1c68c7611f9f5' => __DIR__ . '/..' . '/mockery/mockery/library/Mockery.php',
         '9b38cf48e83f5d8f60375221cd213eee' => __DIR__ . '/..' . '/phpstan/phpstan/bootstrap.php',
+        'e39a8b23c42d4e1452234d762b03835a' => __DIR__ . '/..' . '/ramsey/uuid/src/functions.php',
     );
 
     public static $prefixLengthsPsr4 = array (
@@ -73,6 +74,8 @@ class ComposerStaticInit88f2a4d4a4e81dc7d415bcdf39930654
             'React\\Dns\\' => 10,
             'React\\ChildProcess\\' => 19,
             'React\\Cache\\' => 12,
+            'Ramsey\\Uuid\\' => 12,
+            'Ramsey\\Collection\\' => 18,
         ),
         'P' => 
         array (
@@ -113,6 +116,7 @@ class ComposerStaticInit88f2a4d4a4e81dc7d415bcdf39930654
             'Hyperf\\Support\\' => 15,
             'Hyperf\\Stringable\\' => 18,
             'Hyperf\\Stdlib\\' => 14,
+            'Hyperf\\Snowflake\\' => 17,
             'Hyperf\\ServiceGovernance\\' => 25,
             'Hyperf\\ServiceGovernanceNacos\\' => 30,
             'Hyperf\\ServiceGovernanceConsul\\' => 31,
@@ -124,6 +128,7 @@ class ComposerStaticInit88f2a4d4a4e81dc7d415bcdf39930654
             'Hyperf\\Process\\' => 15,
             'Hyperf\\Pool\\' => 12,
             'Hyperf\\Pipeline\\' => 16,
+            'Hyperf\\Paginator\\' => 17,
             'Hyperf\\Nacos\\' => 13,
             'Hyperf\\ModelListener\\' => 21,
             'Hyperf\\Memory\\' => 14,
@@ -197,6 +202,10 @@ class ComposerStaticInit88f2a4d4a4e81dc7d415bcdf39930654
             'Carbon\\Doctrine\\' => 16,
             'Carbon\\' => 7,
         ),
+        'B' => 
+        array (
+            'Brick\\Math\\' => 11,
+        ),
         'A' => 
         array (
             'App\\' => 4,
@@ -316,6 +325,14 @@ class ComposerStaticInit88f2a4d4a4e81dc7d415bcdf39930654
         array (
             0 => __DIR__ . '/..' . '/react/cache/src',
         ),
+        'Ramsey\\Uuid\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/ramsey/uuid/src',
+        ),
+        'Ramsey\\Collection\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/ramsey/collection/src',
+        ),
         'Psr\\SimpleCache\\' => 
         array (
             0 => __DIR__ . '/..' . '/psr/simple-cache/src',
@@ -326,8 +343,8 @@ class ComposerStaticInit88f2a4d4a4e81dc7d415bcdf39930654
         ),
         'Psr\\Http\\Server\\' => 
         array (
-            0 => __DIR__ . '/..' . '/psr/http-server-handler/src',
-            1 => __DIR__ . '/..' . '/psr/http-server-middleware/src',
+            0 => __DIR__ . '/..' . '/psr/http-server-middleware/src',
+            1 => __DIR__ . '/..' . '/psr/http-server-handler/src',
         ),
         'Psr\\Http\\Message\\' => 
         array (
@@ -418,6 +435,10 @@ class ComposerStaticInit88f2a4d4a4e81dc7d415bcdf39930654
         array (
             0 => __DIR__ . '/..' . '/hyperf/stdlib/src',
         ),
+        'Hyperf\\Snowflake\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/hyperf/snowflake/src',
+        ),
         'Hyperf\\ServiceGovernance\\' => 
         array (
             0 => __DIR__ . '/..' . '/hyperf/service-governance/src',
@@ -462,6 +483,10 @@ class ComposerStaticInit88f2a4d4a4e81dc7d415bcdf39930654
         array (
             0 => __DIR__ . '/..' . '/hyperf/pipeline/src',
         ),
+        'Hyperf\\Paginator\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/hyperf/paginator/src',
+        ),
         'Hyperf\\Nacos\\' => 
         array (
             0 => __DIR__ . '/..' . '/hyperf/nacos/src',
@@ -690,6 +715,10 @@ class ComposerStaticInit88f2a4d4a4e81dc7d415bcdf39930654
         array (
             0 => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon',
         ),
+        'Brick\\Math\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/brick/math/src',
+        ),
         'App\\' => 
         array (
             0 => __DIR__ . '/../..' . '/app',
@@ -710,7 +739,12 @@ class ComposerStaticInit88f2a4d4a4e81dc7d415bcdf39930654
         'App\\Model\\ArticleData' => __DIR__ . '/../..' . '/app/Model/ArticleData.php',
         'App\\Model\\ArticleSurvey' => __DIR__ . '/../..' . '/app/Model/ArticleSurvey.php',
         'App\\Model\\Category' => __DIR__ . '/../..' . '/app/Model/Category.php',
+        'App\\Model\\ChatGroups' => __DIR__ . '/../..' . '/app/Model/ChatGroups.php',
+        'App\\Model\\ChatGroupsMember' => __DIR__ . '/../..' . '/app/Model/ChatGroupsMember.php',
+        'App\\Model\\ChatRecords' => __DIR__ . '/../..' . '/app/Model/ChatRecords.php',
+        'App\\Model\\District' => __DIR__ . '/../..' . '/app/Model/District.php',
         'App\\Model\\Good' => __DIR__ . '/../..' . '/app/Model/Good.php',
+        'App\\Model\\JobCompany' => __DIR__ . '/../..' . '/app/Model/JobCompany.php',
         'App\\Model\\JobEnum' => __DIR__ . '/../..' . '/app/Model/JobEnum.php',
         'App\\Model\\JobIndustry' => __DIR__ . '/../..' . '/app/Model/JobIndustry.php',
         'App\\Model\\JobNature' => __DIR__ . '/../..' . '/app/Model/JobNature.php',
@@ -718,15 +752,35 @@ class ComposerStaticInit88f2a4d4a4e81dc7d415bcdf39930654
         'App\\Model\\JobRecruiting' => __DIR__ . '/../..' . '/app/Model/JobRecruiting.php',
         'App\\Model\\Link' => __DIR__ . '/../..' . '/app/Model/Link.php',
         'App\\Model\\Model' => __DIR__ . '/../..' . '/app/Model/Model.php',
+        'App\\Model\\Notice' => __DIR__ . '/../..' . '/app/Model/Notice.php',
+        'App\\Model\\User' => __DIR__ . '/../..' . '/app/Model/User.php',
+        'App\\Model\\UserInfo' => __DIR__ . '/../..' . '/app/Model/UserInfo.php',
         'App\\Model\\Web' => __DIR__ . '/../..' . '/app/Model/Web.php',
         'App\\Model\\Website' => __DIR__ . '/../..' . '/app/Model/Website.php',
         'App\\Model\\WebsiteCategory' => __DIR__ . '/../..' . '/app/Model/WebsiteCategory.php',
         'App\\Model\\WebsiteColumn' => __DIR__ . '/../..' . '/app/Model/WebsiteColumn.php',
+        'App\\Model\\WebsiteGroup' => __DIR__ . '/../..' . '/app/Model/WebsiteGroup.php',
         'App\\Model\\WebsiteRole' => __DIR__ . '/../..' . '/app/Model/WebsiteRole.php',
         'App\\Model\\WebsiteRoleUser' => __DIR__ . '/../..' . '/app/Model/WebsiteRoleUser.php',
         'App\\Model\\jobHunting' => __DIR__ . '/../..' . '/app/Model/jobHunting.php',
+        'App\\Tools\\PublicData' => __DIR__ . '/../..' . '/app/Tools/PublicData.php',
         'App\\Tools\\Result' => __DIR__ . '/../..' . '/app/Tools/Result.php',
         'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
+        'Brick\\Math\\BigDecimal' => __DIR__ . '/..' . '/brick/math/src/BigDecimal.php',
+        'Brick\\Math\\BigInteger' => __DIR__ . '/..' . '/brick/math/src/BigInteger.php',
+        'Brick\\Math\\BigNumber' => __DIR__ . '/..' . '/brick/math/src/BigNumber.php',
+        'Brick\\Math\\BigRational' => __DIR__ . '/..' . '/brick/math/src/BigRational.php',
+        'Brick\\Math\\Exception\\DivisionByZeroException' => __DIR__ . '/..' . '/brick/math/src/Exception/DivisionByZeroException.php',
+        'Brick\\Math\\Exception\\IntegerOverflowException' => __DIR__ . '/..' . '/brick/math/src/Exception/IntegerOverflowException.php',
+        'Brick\\Math\\Exception\\MathException' => __DIR__ . '/..' . '/brick/math/src/Exception/MathException.php',
+        'Brick\\Math\\Exception\\NegativeNumberException' => __DIR__ . '/..' . '/brick/math/src/Exception/NegativeNumberException.php',
+        'Brick\\Math\\Exception\\NumberFormatException' => __DIR__ . '/..' . '/brick/math/src/Exception/NumberFormatException.php',
+        'Brick\\Math\\Exception\\RoundingNecessaryException' => __DIR__ . '/..' . '/brick/math/src/Exception/RoundingNecessaryException.php',
+        'Brick\\Math\\Internal\\Calculator' => __DIR__ . '/..' . '/brick/math/src/Internal/Calculator.php',
+        'Brick\\Math\\Internal\\Calculator\\BcMathCalculator' => __DIR__ . '/..' . '/brick/math/src/Internal/Calculator/BcMathCalculator.php',
+        'Brick\\Math\\Internal\\Calculator\\GmpCalculator' => __DIR__ . '/..' . '/brick/math/src/Internal/Calculator/GmpCalculator.php',
+        'Brick\\Math\\Internal\\Calculator\\NativeCalculator' => __DIR__ . '/..' . '/brick/math/src/Internal/Calculator/NativeCalculator.php',
+        'Brick\\Math\\RoundingMode' => __DIR__ . '/..' . '/brick/math/src/RoundingMode.php',
         'CURLStringFile' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php',
         'Carbon\\AbstractTranslator' => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon/AbstractTranslator.php',
         'Carbon\\Carbon' => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon/Carbon.php',
@@ -1996,6 +2050,16 @@ class ComposerStaticInit88f2a4d4a4e81dc7d415bcdf39930654
         'Hyperf\\Nacos\\Provider\\InstanceProvider' => __DIR__ . '/..' . '/hyperf/nacos/src/Provider/InstanceProvider.php',
         'Hyperf\\Nacos\\Provider\\OperatorProvider' => __DIR__ . '/..' . '/hyperf/nacos/src/Provider/OperatorProvider.php',
         'Hyperf\\Nacos\\Provider\\ServiceProvider' => __DIR__ . '/..' . '/hyperf/nacos/src/Provider/ServiceProvider.php',
+        'Hyperf\\Paginator\\AbstractCursorPaginator' => __DIR__ . '/..' . '/hyperf/paginator/src/AbstractCursorPaginator.php',
+        'Hyperf\\Paginator\\AbstractPaginator' => __DIR__ . '/..' . '/hyperf/paginator/src/AbstractPaginator.php',
+        'Hyperf\\Paginator\\ConfigProvider' => __DIR__ . '/..' . '/hyperf/paginator/src/ConfigProvider.php',
+        'Hyperf\\Paginator\\Contract\\CursorPaginator' => __DIR__ . '/..' . '/hyperf/paginator/src/Contract/CursorPaginator.php',
+        'Hyperf\\Paginator\\Cursor' => __DIR__ . '/..' . '/hyperf/paginator/src/Cursor.php',
+        'Hyperf\\Paginator\\CursorPaginator' => __DIR__ . '/..' . '/hyperf/paginator/src/CursorPaginator.php',
+        'Hyperf\\Paginator\\LengthAwarePaginator' => __DIR__ . '/..' . '/hyperf/paginator/src/LengthAwarePaginator.php',
+        'Hyperf\\Paginator\\Listener\\PageResolverListener' => __DIR__ . '/..' . '/hyperf/paginator/src/Listener/PageResolverListener.php',
+        'Hyperf\\Paginator\\Paginator' => __DIR__ . '/..' . '/hyperf/paginator/src/Paginator.php',
+        'Hyperf\\Paginator\\UrlWindow' => __DIR__ . '/..' . '/hyperf/paginator/src/UrlWindow.php',
         'Hyperf\\Pipeline\\Pipeline' => __DIR__ . '/..' . '/hyperf/pipeline/src/Pipeline.php',
         'Hyperf\\Pool\\Channel' => __DIR__ . '/..' . '/hyperf/pool/src/Channel.php',
         'Hyperf\\Pool\\ConfigProvider' => __DIR__ . '/..' . '/hyperf/pool/src/ConfigProvider.php',
@@ -2140,6 +2204,22 @@ class ComposerStaticInit88f2a4d4a4e81dc7d415bcdf39930654
         'Hyperf\\ServiceGovernance\\Exception\\RegisterInstanceException' => __DIR__ . '/..' . '/hyperf/service-governance/src/Exception/RegisterInstanceException.php',
         'Hyperf\\ServiceGovernance\\Listener\\RegisterServiceListener' => __DIR__ . '/..' . '/hyperf/service-governance/src/Listener/RegisterServiceListener.php',
         'Hyperf\\ServiceGovernance\\ServiceManager' => __DIR__ . '/..' . '/hyperf/service-governance/src/ServiceManager.php',
+        'Hyperf\\Snowflake\\Concern\\Snowflake' => __DIR__ . '/..' . '/hyperf/snowflake/src/Concern/Snowflake.php',
+        'Hyperf\\Snowflake\\ConfigProvider' => __DIR__ . '/..' . '/hyperf/snowflake/src/ConfigProvider.php',
+        'Hyperf\\Snowflake\\Configuration' => __DIR__ . '/..' . '/hyperf/snowflake/src/Configuration.php',
+        'Hyperf\\Snowflake\\ConfigurationInterface' => __DIR__ . '/..' . '/hyperf/snowflake/src/ConfigurationInterface.php',
+        'Hyperf\\Snowflake\\Exception\\SnowflakeException' => __DIR__ . '/..' . '/hyperf/snowflake/src/Exception/SnowflakeException.php',
+        'Hyperf\\Snowflake\\IdGenerator' => __DIR__ . '/..' . '/hyperf/snowflake/src/IdGenerator.php',
+        'Hyperf\\Snowflake\\IdGeneratorInterface' => __DIR__ . '/..' . '/hyperf/snowflake/src/IdGeneratorInterface.php',
+        'Hyperf\\Snowflake\\IdGenerator\\SnowflakeIdGenerator' => __DIR__ . '/..' . '/hyperf/snowflake/src/IdGenerator/SnowflakeIdGenerator.php',
+        'Hyperf\\Snowflake\\Meta' => __DIR__ . '/..' . '/hyperf/snowflake/src/Meta.php',
+        'Hyperf\\Snowflake\\MetaGenerator' => __DIR__ . '/..' . '/hyperf/snowflake/src/MetaGenerator.php',
+        'Hyperf\\Snowflake\\MetaGeneratorFactory' => __DIR__ . '/..' . '/hyperf/snowflake/src/MetaGeneratorFactory.php',
+        'Hyperf\\Snowflake\\MetaGeneratorInterface' => __DIR__ . '/..' . '/hyperf/snowflake/src/MetaGeneratorInterface.php',
+        'Hyperf\\Snowflake\\MetaGenerator\\RandomMilliSecondMetaGenerator' => __DIR__ . '/..' . '/hyperf/snowflake/src/MetaGenerator/RandomMilliSecondMetaGenerator.php',
+        'Hyperf\\Snowflake\\MetaGenerator\\RedisMetaGenerator' => __DIR__ . '/..' . '/hyperf/snowflake/src/MetaGenerator/RedisMetaGenerator.php',
+        'Hyperf\\Snowflake\\MetaGenerator\\RedisMilliSecondMetaGenerator' => __DIR__ . '/..' . '/hyperf/snowflake/src/MetaGenerator/RedisMilliSecondMetaGenerator.php',
+        'Hyperf\\Snowflake\\MetaGenerator\\RedisSecondMetaGenerator' => __DIR__ . '/..' . '/hyperf/snowflake/src/MetaGenerator/RedisSecondMetaGenerator.php',
         'Hyperf\\Stdlib\\SplPriorityQueue' => __DIR__ . '/..' . '/hyperf/stdlib/src/SplPriorityQueue.php',
         'Hyperf\\Stringable\\Pluralizer' => __DIR__ . '/..' . '/hyperf/stringable/src/Pluralizer.php',
         'Hyperf\\Stringable\\Str' => __DIR__ . '/..' . '/hyperf/stringable/src/Str.php',
@@ -4464,6 +4544,148 @@ class ComposerStaticInit88f2a4d4a4e81dc7d415bcdf39930654
         'Psr\\SimpleCache\\CacheException' => __DIR__ . '/..' . '/psr/simple-cache/src/CacheException.php',
         'Psr\\SimpleCache\\CacheInterface' => __DIR__ . '/..' . '/psr/simple-cache/src/CacheInterface.php',
         'Psr\\SimpleCache\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/simple-cache/src/InvalidArgumentException.php',
+        'Ramsey\\Collection\\AbstractArray' => __DIR__ . '/..' . '/ramsey/collection/src/AbstractArray.php',
+        'Ramsey\\Collection\\AbstractCollection' => __DIR__ . '/..' . '/ramsey/collection/src/AbstractCollection.php',
+        'Ramsey\\Collection\\AbstractSet' => __DIR__ . '/..' . '/ramsey/collection/src/AbstractSet.php',
+        'Ramsey\\Collection\\ArrayInterface' => __DIR__ . '/..' . '/ramsey/collection/src/ArrayInterface.php',
+        'Ramsey\\Collection\\Collection' => __DIR__ . '/..' . '/ramsey/collection/src/Collection.php',
+        'Ramsey\\Collection\\CollectionInterface' => __DIR__ . '/..' . '/ramsey/collection/src/CollectionInterface.php',
+        'Ramsey\\Collection\\DoubleEndedQueue' => __DIR__ . '/..' . '/ramsey/collection/src/DoubleEndedQueue.php',
+        'Ramsey\\Collection\\DoubleEndedQueueInterface' => __DIR__ . '/..' . '/ramsey/collection/src/DoubleEndedQueueInterface.php',
+        'Ramsey\\Collection\\Exception\\CollectionException' => __DIR__ . '/..' . '/ramsey/collection/src/Exception/CollectionException.php',
+        'Ramsey\\Collection\\Exception\\CollectionMismatchException' => __DIR__ . '/..' . '/ramsey/collection/src/Exception/CollectionMismatchException.php',
+        'Ramsey\\Collection\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/ramsey/collection/src/Exception/InvalidArgumentException.php',
+        'Ramsey\\Collection\\Exception\\InvalidPropertyOrMethod' => __DIR__ . '/..' . '/ramsey/collection/src/Exception/InvalidPropertyOrMethod.php',
+        'Ramsey\\Collection\\Exception\\NoSuchElementException' => __DIR__ . '/..' . '/ramsey/collection/src/Exception/NoSuchElementException.php',
+        'Ramsey\\Collection\\Exception\\OutOfBoundsException' => __DIR__ . '/..' . '/ramsey/collection/src/Exception/OutOfBoundsException.php',
+        'Ramsey\\Collection\\Exception\\UnsupportedOperationException' => __DIR__ . '/..' . '/ramsey/collection/src/Exception/UnsupportedOperationException.php',
+        'Ramsey\\Collection\\GenericArray' => __DIR__ . '/..' . '/ramsey/collection/src/GenericArray.php',
+        'Ramsey\\Collection\\Map\\AbstractMap' => __DIR__ . '/..' . '/ramsey/collection/src/Map/AbstractMap.php',
+        'Ramsey\\Collection\\Map\\AbstractTypedMap' => __DIR__ . '/..' . '/ramsey/collection/src/Map/AbstractTypedMap.php',
+        'Ramsey\\Collection\\Map\\AssociativeArrayMap' => __DIR__ . '/..' . '/ramsey/collection/src/Map/AssociativeArrayMap.php',
+        'Ramsey\\Collection\\Map\\MapInterface' => __DIR__ . '/..' . '/ramsey/collection/src/Map/MapInterface.php',
+        'Ramsey\\Collection\\Map\\NamedParameterMap' => __DIR__ . '/..' . '/ramsey/collection/src/Map/NamedParameterMap.php',
+        'Ramsey\\Collection\\Map\\TypedMap' => __DIR__ . '/..' . '/ramsey/collection/src/Map/TypedMap.php',
+        'Ramsey\\Collection\\Map\\TypedMapInterface' => __DIR__ . '/..' . '/ramsey/collection/src/Map/TypedMapInterface.php',
+        'Ramsey\\Collection\\Queue' => __DIR__ . '/..' . '/ramsey/collection/src/Queue.php',
+        'Ramsey\\Collection\\QueueInterface' => __DIR__ . '/..' . '/ramsey/collection/src/QueueInterface.php',
+        'Ramsey\\Collection\\Set' => __DIR__ . '/..' . '/ramsey/collection/src/Set.php',
+        'Ramsey\\Collection\\Sort' => __DIR__ . '/..' . '/ramsey/collection/src/Sort.php',
+        'Ramsey\\Collection\\Tool\\TypeTrait' => __DIR__ . '/..' . '/ramsey/collection/src/Tool/TypeTrait.php',
+        'Ramsey\\Collection\\Tool\\ValueExtractorTrait' => __DIR__ . '/..' . '/ramsey/collection/src/Tool/ValueExtractorTrait.php',
+        'Ramsey\\Collection\\Tool\\ValueToStringTrait' => __DIR__ . '/..' . '/ramsey/collection/src/Tool/ValueToStringTrait.php',
+        'Ramsey\\Uuid\\BinaryUtils' => __DIR__ . '/..' . '/ramsey/uuid/src/BinaryUtils.php',
+        'Ramsey\\Uuid\\Builder\\BuilderCollection' => __DIR__ . '/..' . '/ramsey/uuid/src/Builder/BuilderCollection.php',
+        'Ramsey\\Uuid\\Builder\\DefaultUuidBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Builder/DefaultUuidBuilder.php',
+        'Ramsey\\Uuid\\Builder\\DegradedUuidBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Builder/DegradedUuidBuilder.php',
+        'Ramsey\\Uuid\\Builder\\FallbackBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Builder/FallbackBuilder.php',
+        'Ramsey\\Uuid\\Builder\\UuidBuilderInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Builder/UuidBuilderInterface.php',
+        'Ramsey\\Uuid\\Codec\\CodecInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Codec/CodecInterface.php',
+        'Ramsey\\Uuid\\Codec\\GuidStringCodec' => __DIR__ . '/..' . '/ramsey/uuid/src/Codec/GuidStringCodec.php',
+        'Ramsey\\Uuid\\Codec\\OrderedTimeCodec' => __DIR__ . '/..' . '/ramsey/uuid/src/Codec/OrderedTimeCodec.php',
+        'Ramsey\\Uuid\\Codec\\StringCodec' => __DIR__ . '/..' . '/ramsey/uuid/src/Codec/StringCodec.php',
+        'Ramsey\\Uuid\\Codec\\TimestampFirstCombCodec' => __DIR__ . '/..' . '/ramsey/uuid/src/Codec/TimestampFirstCombCodec.php',
+        'Ramsey\\Uuid\\Codec\\TimestampLastCombCodec' => __DIR__ . '/..' . '/ramsey/uuid/src/Codec/TimestampLastCombCodec.php',
+        'Ramsey\\Uuid\\Converter\\NumberConverterInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/NumberConverterInterface.php',
+        'Ramsey\\Uuid\\Converter\\Number\\BigNumberConverter' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/Number/BigNumberConverter.php',
+        'Ramsey\\Uuid\\Converter\\Number\\DegradedNumberConverter' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/Number/DegradedNumberConverter.php',
+        'Ramsey\\Uuid\\Converter\\Number\\GenericNumberConverter' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/Number/GenericNumberConverter.php',
+        'Ramsey\\Uuid\\Converter\\TimeConverterInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/TimeConverterInterface.php',
+        'Ramsey\\Uuid\\Converter\\Time\\BigNumberTimeConverter' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/Time/BigNumberTimeConverter.php',
+        'Ramsey\\Uuid\\Converter\\Time\\DegradedTimeConverter' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/Time/DegradedTimeConverter.php',
+        'Ramsey\\Uuid\\Converter\\Time\\GenericTimeConverter' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/Time/GenericTimeConverter.php',
+        'Ramsey\\Uuid\\Converter\\Time\\PhpTimeConverter' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/Time/PhpTimeConverter.php',
+        'Ramsey\\Uuid\\Converter\\Time\\UnixTimeConverter' => __DIR__ . '/..' . '/ramsey/uuid/src/Converter/Time/UnixTimeConverter.php',
+        'Ramsey\\Uuid\\DegradedUuid' => __DIR__ . '/..' . '/ramsey/uuid/src/DegradedUuid.php',
+        'Ramsey\\Uuid\\DeprecatedUuidInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/DeprecatedUuidInterface.php',
+        'Ramsey\\Uuid\\DeprecatedUuidMethodsTrait' => __DIR__ . '/..' . '/ramsey/uuid/src/DeprecatedUuidMethodsTrait.php',
+        'Ramsey\\Uuid\\Exception\\BuilderNotFoundException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/BuilderNotFoundException.php',
+        'Ramsey\\Uuid\\Exception\\DateTimeException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/DateTimeException.php',
+        'Ramsey\\Uuid\\Exception\\DceSecurityException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/DceSecurityException.php',
+        'Ramsey\\Uuid\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/InvalidArgumentException.php',
+        'Ramsey\\Uuid\\Exception\\InvalidBytesException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/InvalidBytesException.php',
+        'Ramsey\\Uuid\\Exception\\InvalidUuidStringException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/InvalidUuidStringException.php',
+        'Ramsey\\Uuid\\Exception\\NameException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/NameException.php',
+        'Ramsey\\Uuid\\Exception\\NodeException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/NodeException.php',
+        'Ramsey\\Uuid\\Exception\\RandomSourceException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/RandomSourceException.php',
+        'Ramsey\\Uuid\\Exception\\TimeSourceException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/TimeSourceException.php',
+        'Ramsey\\Uuid\\Exception\\UnableToBuildUuidException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/UnableToBuildUuidException.php',
+        'Ramsey\\Uuid\\Exception\\UnsupportedOperationException' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/UnsupportedOperationException.php',
+        'Ramsey\\Uuid\\Exception\\UuidExceptionInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Exception/UuidExceptionInterface.php',
+        'Ramsey\\Uuid\\FeatureSet' => __DIR__ . '/..' . '/ramsey/uuid/src/FeatureSet.php',
+        'Ramsey\\Uuid\\Fields\\FieldsInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Fields/FieldsInterface.php',
+        'Ramsey\\Uuid\\Fields\\SerializableFieldsTrait' => __DIR__ . '/..' . '/ramsey/uuid/src/Fields/SerializableFieldsTrait.php',
+        'Ramsey\\Uuid\\Generator\\CombGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/CombGenerator.php',
+        'Ramsey\\Uuid\\Generator\\DceSecurityGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/DceSecurityGenerator.php',
+        'Ramsey\\Uuid\\Generator\\DceSecurityGeneratorInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/DceSecurityGeneratorInterface.php',
+        'Ramsey\\Uuid\\Generator\\DefaultNameGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/DefaultNameGenerator.php',
+        'Ramsey\\Uuid\\Generator\\DefaultTimeGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/DefaultTimeGenerator.php',
+        'Ramsey\\Uuid\\Generator\\NameGeneratorFactory' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/NameGeneratorFactory.php',
+        'Ramsey\\Uuid\\Generator\\NameGeneratorInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/NameGeneratorInterface.php',
+        'Ramsey\\Uuid\\Generator\\PeclUuidNameGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/PeclUuidNameGenerator.php',
+        'Ramsey\\Uuid\\Generator\\PeclUuidRandomGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/PeclUuidRandomGenerator.php',
+        'Ramsey\\Uuid\\Generator\\PeclUuidTimeGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/PeclUuidTimeGenerator.php',
+        'Ramsey\\Uuid\\Generator\\RandomBytesGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/RandomBytesGenerator.php',
+        'Ramsey\\Uuid\\Generator\\RandomGeneratorFactory' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/RandomGeneratorFactory.php',
+        'Ramsey\\Uuid\\Generator\\RandomGeneratorInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/RandomGeneratorInterface.php',
+        'Ramsey\\Uuid\\Generator\\RandomLibAdapter' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/RandomLibAdapter.php',
+        'Ramsey\\Uuid\\Generator\\TimeGeneratorFactory' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/TimeGeneratorFactory.php',
+        'Ramsey\\Uuid\\Generator\\TimeGeneratorInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/TimeGeneratorInterface.php',
+        'Ramsey\\Uuid\\Generator\\UnixTimeGenerator' => __DIR__ . '/..' . '/ramsey/uuid/src/Generator/UnixTimeGenerator.php',
+        'Ramsey\\Uuid\\Guid\\Fields' => __DIR__ . '/..' . '/ramsey/uuid/src/Guid/Fields.php',
+        'Ramsey\\Uuid\\Guid\\Guid' => __DIR__ . '/..' . '/ramsey/uuid/src/Guid/Guid.php',
+        'Ramsey\\Uuid\\Guid\\GuidBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Guid/GuidBuilder.php',
+        'Ramsey\\Uuid\\Lazy\\LazyUuidFromString' => __DIR__ . '/..' . '/ramsey/uuid/src/Lazy/LazyUuidFromString.php',
+        'Ramsey\\Uuid\\Math\\BrickMathCalculator' => __DIR__ . '/..' . '/ramsey/uuid/src/Math/BrickMathCalculator.php',
+        'Ramsey\\Uuid\\Math\\CalculatorInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Math/CalculatorInterface.php',
+        'Ramsey\\Uuid\\Math\\RoundingMode' => __DIR__ . '/..' . '/ramsey/uuid/src/Math/RoundingMode.php',
+        'Ramsey\\Uuid\\Nonstandard\\Fields' => __DIR__ . '/..' . '/ramsey/uuid/src/Nonstandard/Fields.php',
+        'Ramsey\\Uuid\\Nonstandard\\Uuid' => __DIR__ . '/..' . '/ramsey/uuid/src/Nonstandard/Uuid.php',
+        'Ramsey\\Uuid\\Nonstandard\\UuidBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Nonstandard/UuidBuilder.php',
+        'Ramsey\\Uuid\\Nonstandard\\UuidV6' => __DIR__ . '/..' . '/ramsey/uuid/src/Nonstandard/UuidV6.php',
+        'Ramsey\\Uuid\\Provider\\DceSecurityProviderInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/DceSecurityProviderInterface.php',
+        'Ramsey\\Uuid\\Provider\\Dce\\SystemDceSecurityProvider' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/Dce/SystemDceSecurityProvider.php',
+        'Ramsey\\Uuid\\Provider\\NodeProviderInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/NodeProviderInterface.php',
+        'Ramsey\\Uuid\\Provider\\Node\\FallbackNodeProvider' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/Node/FallbackNodeProvider.php',
+        'Ramsey\\Uuid\\Provider\\Node\\NodeProviderCollection' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/Node/NodeProviderCollection.php',
+        'Ramsey\\Uuid\\Provider\\Node\\RandomNodeProvider' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/Node/RandomNodeProvider.php',
+        'Ramsey\\Uuid\\Provider\\Node\\StaticNodeProvider' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/Node/StaticNodeProvider.php',
+        'Ramsey\\Uuid\\Provider\\Node\\SystemNodeProvider' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/Node/SystemNodeProvider.php',
+        'Ramsey\\Uuid\\Provider\\TimeProviderInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/TimeProviderInterface.php',
+        'Ramsey\\Uuid\\Provider\\Time\\FixedTimeProvider' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/Time/FixedTimeProvider.php',
+        'Ramsey\\Uuid\\Provider\\Time\\SystemTimeProvider' => __DIR__ . '/..' . '/ramsey/uuid/src/Provider/Time/SystemTimeProvider.php',
+        'Ramsey\\Uuid\\Rfc4122\\Fields' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/Fields.php',
+        'Ramsey\\Uuid\\Rfc4122\\FieldsInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/FieldsInterface.php',
+        'Ramsey\\Uuid\\Rfc4122\\MaxTrait' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/MaxTrait.php',
+        'Ramsey\\Uuid\\Rfc4122\\MaxUuid' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/MaxUuid.php',
+        'Ramsey\\Uuid\\Rfc4122\\NilTrait' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/NilTrait.php',
+        'Ramsey\\Uuid\\Rfc4122\\NilUuid' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/NilUuid.php',
+        'Ramsey\\Uuid\\Rfc4122\\TimeTrait' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/TimeTrait.php',
+        'Ramsey\\Uuid\\Rfc4122\\UuidBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidBuilder.php',
+        'Ramsey\\Uuid\\Rfc4122\\UuidInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidInterface.php',
+        'Ramsey\\Uuid\\Rfc4122\\UuidV1' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidV1.php',
+        'Ramsey\\Uuid\\Rfc4122\\UuidV2' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidV2.php',
+        'Ramsey\\Uuid\\Rfc4122\\UuidV3' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidV3.php',
+        'Ramsey\\Uuid\\Rfc4122\\UuidV4' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidV4.php',
+        'Ramsey\\Uuid\\Rfc4122\\UuidV5' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidV5.php',
+        'Ramsey\\Uuid\\Rfc4122\\UuidV6' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidV6.php',
+        'Ramsey\\Uuid\\Rfc4122\\UuidV7' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidV7.php',
+        'Ramsey\\Uuid\\Rfc4122\\UuidV8' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/UuidV8.php',
+        'Ramsey\\Uuid\\Rfc4122\\Validator' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/Validator.php',
+        'Ramsey\\Uuid\\Rfc4122\\VariantTrait' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/VariantTrait.php',
+        'Ramsey\\Uuid\\Rfc4122\\VersionTrait' => __DIR__ . '/..' . '/ramsey/uuid/src/Rfc4122/VersionTrait.php',
+        'Ramsey\\Uuid\\Type\\Decimal' => __DIR__ . '/..' . '/ramsey/uuid/src/Type/Decimal.php',
+        'Ramsey\\Uuid\\Type\\Hexadecimal' => __DIR__ . '/..' . '/ramsey/uuid/src/Type/Hexadecimal.php',
+        'Ramsey\\Uuid\\Type\\Integer' => __DIR__ . '/..' . '/ramsey/uuid/src/Type/Integer.php',
+        'Ramsey\\Uuid\\Type\\NumberInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Type/NumberInterface.php',
+        'Ramsey\\Uuid\\Type\\Time' => __DIR__ . '/..' . '/ramsey/uuid/src/Type/Time.php',
+        'Ramsey\\Uuid\\Type\\TypeInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Type/TypeInterface.php',
+        'Ramsey\\Uuid\\Uuid' => __DIR__ . '/..' . '/ramsey/uuid/src/Uuid.php',
+        'Ramsey\\Uuid\\UuidFactory' => __DIR__ . '/..' . '/ramsey/uuid/src/UuidFactory.php',
+        'Ramsey\\Uuid\\UuidFactoryInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/UuidFactoryInterface.php',
+        'Ramsey\\Uuid\\UuidInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/UuidInterface.php',
+        'Ramsey\\Uuid\\Validator\\GenericValidator' => __DIR__ . '/..' . '/ramsey/uuid/src/Validator/GenericValidator.php',
+        'Ramsey\\Uuid\\Validator\\ValidatorInterface' => __DIR__ . '/..' . '/ramsey/uuid/src/Validator/ValidatorInterface.php',
         'React\\Cache\\ArrayCache' => __DIR__ . '/..' . '/react/cache/src/ArrayCache.php',
         'React\\Cache\\CacheInterface' => __DIR__ . '/..' . '/react/cache/src/CacheInterface.php',
         'React\\ChildProcess\\Process' => __DIR__ . '/..' . '/react/child-process/src/Process.php',

+ 376 - 0
vendor/composer/installed.json

@@ -1,5 +1,68 @@
 {
     "packages": [
+        {
+            "name": "brick/math",
+            "version": "0.12.3",
+            "version_normalized": "0.12.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/brick/math.git",
+                "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba",
+                "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.1"
+            },
+            "require-dev": {
+                "php-coveralls/php-coveralls": "^2.2",
+                "phpunit/phpunit": "^10.1",
+                "vimeo/psalm": "6.8.8"
+            },
+            "time": "2025-02-28T13:11:00+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Brick\\Math\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Arbitrary-precision arithmetic library",
+            "keywords": [
+                "Arbitrary-precision",
+                "BigInteger",
+                "BigRational",
+                "arithmetic",
+                "bigdecimal",
+                "bignum",
+                "bignumber",
+                "brick",
+                "decimal",
+                "integer",
+                "math",
+                "mathematics",
+                "rational"
+            ],
+            "support": {
+                "issues": "https://github.com/brick/math/issues",
+                "source": "https://github.com/brick/math/tree/0.12.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/BenMorel",
+                    "type": "github"
+                }
+            ],
+            "install-path": "../brick/math"
+        },
         {
             "name": "carbonphp/carbon-doctrine-types",
             "version": "3.2.0",
@@ -3963,6 +4026,77 @@
             ],
             "install-path": "../hyperf/nacos"
         },
+        {
+            "name": "hyperf/paginator",
+            "version": "v3.1.49",
+            "version_normalized": "3.1.49.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/hyperf/paginator.git",
+                "reference": "dd9d59f5601fbdaa69f488348c7debd1931feb7b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/hyperf/paginator/zipball/dd9d59f5601fbdaa69f488348c7debd1931feb7b",
+                "reference": "dd9d59f5601fbdaa69f488348c7debd1931feb7b",
+                "shasum": ""
+            },
+            "require": {
+                "hyperf/contract": "~3.1.0",
+                "hyperf/support": "~3.1.0",
+                "hyperf/utils": "~3.1.0",
+                "php": ">=8.1"
+            },
+            "suggest": {
+                "hyperf/event": "Reqiured to use PageResolverListener.",
+                "hyperf/framework": "Reqiured to use PageResolverListener.",
+                "hyperf/http-server": "Reqiured to use PageResolverListener."
+            },
+            "time": "2024-12-17T09:42:13+00:00",
+            "type": "library",
+            "extra": {
+                "hyperf": {
+                    "config": "Hyperf\\Paginator\\ConfigProvider"
+                },
+                "branch-alias": {
+                    "dev-master": "3.1-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Hyperf\\Paginator\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "A paginator component for hyperf.",
+            "homepage": "https://hyperf.io",
+            "keywords": [
+                "hyperf",
+                "paginator",
+                "php"
+            ],
+            "support": {
+                "docs": "https://hyperf.wiki",
+                "issues": "https://github.com/hyperf/hyperf/issues",
+                "pull-request": "https://github.com/hyperf/hyperf/pulls",
+                "source": "https://github.com/hyperf/hyperf"
+            },
+            "funding": [
+                {
+                    "url": "https://hyperf.wiki/#/zh-cn/donate",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://opencollective.com/hyperf",
+                    "type": "open_collective"
+                }
+            ],
+            "install-path": "../hyperf/paginator"
+        },
         {
             "name": "hyperf/pipeline",
             "version": "v3.1.15",
@@ -4741,6 +4875,74 @@
             ],
             "install-path": "../hyperf/service-governance-nacos"
         },
+        {
+            "name": "hyperf/snowflake",
+            "version": "v3.1.42",
+            "version_normalized": "3.1.42.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/hyperf/snowflake.git",
+                "reference": "0271dfd9f0030e4f4cdd90f91428d1dbb35ecd56"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/hyperf/snowflake/zipball/0271dfd9f0030e4f4cdd90f91428d1dbb35ecd56",
+                "reference": "0271dfd9f0030e4f4cdd90f91428d1dbb35ecd56",
+                "shasum": ""
+            },
+            "require": {
+                "hyperf/contract": "~3.1.0",
+                "php": ">=8.1"
+            },
+            "suggest": {
+                "hyperf/config": "Required to read snowflake config.",
+                "hyperf/redis": "Required to use RedisMilliSecondMetaGenerator or RedisSecondMetaGenerator.",
+                "psr/container": "Required to use MetaGeneratorFactory."
+            },
+            "time": "2024-09-25T02:54:12+00:00",
+            "type": "library",
+            "extra": {
+                "hyperf": {
+                    "config": "Hyperf\\Snowflake\\ConfigProvider"
+                },
+                "branch-alias": {
+                    "dev-master": "3.1-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Hyperf\\Snowflake\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "A snowflake library",
+            "homepage": "https://hyperf.io",
+            "keywords": [
+                "php",
+                "snowflake"
+            ],
+            "support": {
+                "docs": "https://hyperf.wiki",
+                "issues": "https://github.com/hyperf/hyperf/issues",
+                "pull-request": "https://github.com/hyperf/hyperf/pulls",
+                "source": "https://github.com/hyperf/hyperf"
+            },
+            "funding": [
+                {
+                    "url": "https://hyperf.wiki/#/zh-cn/donate",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://opencollective.com/hyperf",
+                    "type": "open_collective"
+                }
+            ],
+            "install-path": "../hyperf/snowflake"
+        },
         {
             "name": "hyperf/stdlib",
             "version": "v3.1.15",
@@ -7350,6 +7552,180 @@
             },
             "install-path": "../ralouphie/getallheaders"
         },
+        {
+            "name": "ramsey/collection",
+            "version": "2.1.1",
+            "version_normalized": "2.1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ramsey/collection.git",
+                "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2",
+                "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.1"
+            },
+            "require-dev": {
+                "captainhook/plugin-composer": "^5.3",
+                "ergebnis/composer-normalize": "^2.45",
+                "fakerphp/faker": "^1.24",
+                "hamcrest/hamcrest-php": "^2.0",
+                "jangregor/phpstan-prophecy": "^2.1",
+                "mockery/mockery": "^1.6",
+                "php-parallel-lint/php-console-highlighter": "^1.0",
+                "php-parallel-lint/php-parallel-lint": "^1.4",
+                "phpspec/prophecy-phpunit": "^2.3",
+                "phpstan/extension-installer": "^1.4",
+                "phpstan/phpstan": "^2.1",
+                "phpstan/phpstan-mockery": "^2.0",
+                "phpstan/phpstan-phpunit": "^2.0",
+                "phpunit/phpunit": "^10.5",
+                "ramsey/coding-standard": "^2.3",
+                "ramsey/conventional-commits": "^1.6",
+                "roave/security-advisories": "dev-latest"
+            },
+            "time": "2025-03-22T05:38:12+00:00",
+            "type": "library",
+            "extra": {
+                "captainhook": {
+                    "force-install": true
+                },
+                "ramsey/conventional-commits": {
+                    "configFile": "conventional-commits.json"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Ramsey\\Collection\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ben Ramsey",
+                    "email": "ben@benramsey.com",
+                    "homepage": "https://benramsey.com"
+                }
+            ],
+            "description": "A PHP library for representing and manipulating collections.",
+            "keywords": [
+                "array",
+                "collection",
+                "hash",
+                "map",
+                "queue",
+                "set"
+            ],
+            "support": {
+                "issues": "https://github.com/ramsey/collection/issues",
+                "source": "https://github.com/ramsey/collection/tree/2.1.1"
+            },
+            "install-path": "../ramsey/collection"
+        },
+        {
+            "name": "ramsey/uuid",
+            "version": "4.7.6",
+            "version_normalized": "4.7.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ramsey/uuid.git",
+                "reference": "91039bc1faa45ba123c4328958e620d382ec7088"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088",
+                "reference": "91039bc1faa45ba123c4328958e620d382ec7088",
+                "shasum": ""
+            },
+            "require": {
+                "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12",
+                "ext-json": "*",
+                "php": "^8.0",
+                "ramsey/collection": "^1.2 || ^2.0"
+            },
+            "replace": {
+                "rhumsaa/uuid": "self.version"
+            },
+            "require-dev": {
+                "captainhook/captainhook": "^5.10",
+                "captainhook/plugin-composer": "^5.3",
+                "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
+                "doctrine/annotations": "^1.8",
+                "ergebnis/composer-normalize": "^2.15",
+                "mockery/mockery": "^1.3",
+                "paragonie/random-lib": "^2",
+                "php-mock/php-mock": "^2.2",
+                "php-mock/php-mock-mockery": "^1.3",
+                "php-parallel-lint/php-parallel-lint": "^1.1",
+                "phpbench/phpbench": "^1.0",
+                "phpstan/extension-installer": "^1.1",
+                "phpstan/phpstan": "^1.8",
+                "phpstan/phpstan-mockery": "^1.1",
+                "phpstan/phpstan-phpunit": "^1.1",
+                "phpunit/phpunit": "^8.5 || ^9",
+                "ramsey/composer-repl": "^1.4",
+                "slevomat/coding-standard": "^8.4",
+                "squizlabs/php_codesniffer": "^3.5",
+                "vimeo/psalm": "^4.9"
+            },
+            "suggest": {
+                "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
+                "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
+                "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
+                "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
+                "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
+            },
+            "time": "2024-04-27T21:32:50+00:00",
+            "type": "library",
+            "extra": {
+                "captainhook": {
+                    "force-install": true
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "src/functions.php"
+                ],
+                "psr-4": {
+                    "Ramsey\\Uuid\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
+            "keywords": [
+                "guid",
+                "identifier",
+                "uuid"
+            ],
+            "support": {
+                "issues": "https://github.com/ramsey/uuid/issues",
+                "source": "https://github.com/ramsey/uuid/tree/4.7.6"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/ramsey",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../ramsey/uuid"
+        },
         {
             "name": "react/cache",
             "version": "v1.2.0",

+ 55 - 4
vendor/composer/installed.php

@@ -3,13 +3,22 @@
         'name' => 'hyperf/hyperf-skeleton',
         'pretty_version' => 'dev-master',
         'version' => 'dev-master',
-        'reference' => 'b47d14db61b94620b24c47d4a24d5041ac70eea0',
+        'reference' => '3aaacbc1f89ea4966bdb5b9ed46890ce7735093b',
         'type' => 'project',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
         'dev' => true,
     ),
     'versions' => array(
+        'brick/math' => array(
+            'pretty_version' => '0.12.3',
+            'version' => '0.12.3.0',
+            'reference' => '866551da34e9a618e64a819ee1e01c20d8a588ba',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../brick/math',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
         'carbonphp/carbon-doctrine-types' => array(
             'pretty_version' => '3.2.0',
             'version' => '3.2.0.0',
@@ -448,7 +457,7 @@
         'hyperf/hyperf-skeleton' => array(
             'pretty_version' => 'dev-master',
             'version' => 'dev-master',
-            'reference' => 'b47d14db61b94620b24c47d4a24d5041ac70eea0',
+            'reference' => '3aaacbc1f89ea4966bdb5b9ed46890ce7735093b',
             'type' => 'project',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),
@@ -517,6 +526,15 @@
             'aliases' => array(),
             'dev_requirement' => false,
         ),
+        'hyperf/paginator' => array(
+            'pretty_version' => 'v3.1.49',
+            'version' => '3.1.49.0',
+            'reference' => 'dd9d59f5601fbdaa69f488348c7debd1931feb7b',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../hyperf/paginator',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
         'hyperf/pipeline' => array(
             'pretty_version' => 'v3.1.15',
             'version' => '3.1.15.0',
@@ -616,6 +634,15 @@
             'aliases' => array(),
             'dev_requirement' => false,
         ),
+        'hyperf/snowflake' => array(
+            'pretty_version' => 'v3.1.42',
+            'version' => '3.1.42.0',
+            'reference' => '0271dfd9f0030e4f4cdd90f91428d1dbb35ecd56',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../hyperf/snowflake',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
         'hyperf/stdlib' => array(
             'pretty_version' => 'v3.1.15',
             'version' => '3.1.15.0',
@@ -997,8 +1024,8 @@
         'psr/log-implementation' => array(
             'dev_requirement' => false,
             'provided' => array(
-                0 => '3.0.0',
-                1 => '1.0|2.0|3.0',
+                0 => '1.0|2.0|3.0',
+                1 => '3.0.0',
             ),
         ),
         'psr/simple-cache' => array(
@@ -1019,6 +1046,24 @@
             'aliases' => array(),
             'dev_requirement' => false,
         ),
+        'ramsey/collection' => array(
+            'pretty_version' => '2.1.1',
+            'version' => '2.1.1.0',
+            'reference' => '344572933ad0181accbf4ba763e85a0306a8c5e2',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../ramsey/collection',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'ramsey/uuid' => array(
+            'pretty_version' => '4.7.6',
+            'version' => '4.7.6.0',
+            'reference' => '91039bc1faa45ba123c4328958e620d382ec7088',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../ramsey/uuid',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
         'react/cache' => array(
             'pretty_version' => 'v1.2.0',
             'version' => '1.2.0.0',
@@ -1082,6 +1127,12 @@
             'aliases' => array(),
             'dev_requirement' => true,
         ),
+        'rhumsaa/uuid' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => '4.7.6',
+            ),
+        ),
         'sebastian/cli-parser' => array(
             'pretty_version' => '2.0.1',
             'version' => '2.0.1.0',

+ 2 - 0
vendor/hyperf/paginator/.gitattributes

@@ -0,0 +1,2 @@
+/tests export-ignore
+/.github export-ignore

+ 22 - 0
vendor/hyperf/paginator/LICENSE

@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+Copyright (c) Hyperf
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 49 - 0
vendor/hyperf/paginator/composer.json

@@ -0,0 +1,49 @@
+{
+    "name": "hyperf/paginator",
+    "description": "A paginator component for hyperf.",
+    "license": "MIT",
+    "keywords": [
+        "php",
+        "hyperf",
+        "paginator"
+    ],
+    "homepage": "https://hyperf.io",
+    "support": {
+        "issues": "https://github.com/hyperf/hyperf/issues",
+        "source": "https://github.com/hyperf/hyperf",
+        "docs": "https://hyperf.wiki",
+        "pull-request": "https://github.com/hyperf/hyperf/pulls"
+    },
+    "require": {
+        "php": ">=8.1",
+        "hyperf/contract": "~3.1.0",
+        "hyperf/support": "~3.1.0",
+        "hyperf/utils": "~3.1.0"
+    },
+    "suggest": {
+        "hyperf/event": "Reqiured to use PageResolverListener.",
+        "hyperf/framework": "Reqiured to use PageResolverListener.",
+        "hyperf/http-server": "Reqiured to use PageResolverListener."
+    },
+    "autoload": {
+        "psr-4": {
+            "Hyperf\\Paginator\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "HyperfTest\\Paginator\\": "tests/"
+        }
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "3.1-dev"
+        },
+        "hyperf": {
+            "config": "Hyperf\\Paginator\\ConfigProvider"
+        }
+    }
+}

+ 545 - 0
vendor/hyperf/paginator/src/AbstractCursorPaginator.php

@@ -0,0 +1,545 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Paginator;
+
+use ArrayAccess;
+use Closure;
+use Exception;
+use Hyperf\Collection\Arr;
+use Hyperf\Collection\Collection;
+use Hyperf\Database\Model\Collection as ModelCollection;
+use Hyperf\Database\Model\Model;
+use Hyperf\Database\Model\Relations\Pivot;
+use Hyperf\Resource\Json\JsonResource;
+use Hyperf\Stringable\Str;
+use Hyperf\Support\Traits\ForwardsCalls;
+use Hyperf\Tappable\Tappable;
+use stdClass;
+use Stringable;
+use Traversable;
+
+use function Hyperf\Collection\collect;
+
+abstract class AbstractCursorPaginator implements Stringable
+{
+    use ForwardsCalls;
+    use Tappable;
+
+    /**
+     * All of the items being paginated.
+     */
+    protected Collection|ModelCollection $items;
+
+    /**
+     * The number of items to be shown per page.
+     */
+    protected int $perPage;
+
+    /**
+     * The base path to assign to all URLs.
+     */
+    protected string $path = '/';
+
+    /**
+     * The query parameters to add to all URLs.
+     */
+    protected array $query = [];
+
+    /**
+     * The URL fragment to add to all URLs.
+     */
+    protected ?string $fragment = null;
+
+    /**
+     * The cursor string variable used to store the page.
+     */
+    protected string $cursorName = 'cursor';
+
+    /**
+     * The current cursor.
+     */
+    protected ?Cursor $cursor = null;
+
+    /**
+     * The paginator parameters for the cursor.
+     */
+    protected array $parameters = [];
+
+    /**
+     * The paginator options.
+     */
+    protected array $options = [];
+
+    /**
+     * Indicates whether there are more items in the data source.
+     *
+     * @return bool
+     */
+    protected bool $hasMore;
+
+    /**
+     * The current cursor resolver callback.
+     */
+    protected static ?Closure $currentCursorResolver = null;
+
+    /**
+     * Make dynamic calls into the collection.
+     * @param mixed $method
+     * @param mixed $parameters
+     */
+    public function __call($method, $parameters)
+    {
+        return $this->forwardCallTo($this->getCollection(), $method, $parameters);
+    }
+
+    /**
+     * Get the URL for a given cursor.
+     */
+    public function url(?Cursor $cursor): string
+    {
+        // If we have any extra query string key / value pairs that need to be added
+        // onto the URL, we will put them in query string form and then attach it
+        // to the URL. This allows for extra information like sortings storage.
+        $parameters = is_null($cursor) ? [] : [$this->cursorName => $cursor->encode()];
+
+        if (count($this->query) > 0) {
+            $parameters = array_merge($this->query, $parameters);
+        }
+
+        return $this->path()
+            . (str_contains($this->path(), '?') ? '&' : '?')
+            . Arr::query($parameters)
+            . $this->buildFragment();
+    }
+
+    /**
+     * Get the URL for the previous page.
+     */
+    public function previousPageUrl(): ?string
+    {
+        if (is_null($previousCursor = $this->previousCursor())) {
+            return null;
+        }
+
+        return $this->url($previousCursor);
+    }
+
+    /**
+     * The URL for the next page, or null.
+     */
+    public function nextPageUrl(): ?string
+    {
+        if (is_null($nextCursor = $this->nextCursor())) {
+            return null;
+        }
+
+        return $this->url($nextCursor);
+    }
+
+    /**
+     * Get the "cursor" that points to the previous set of items.
+     */
+    public function previousCursor(): ?Cursor
+    {
+        if (is_null($this->cursor)
+            || ($this->cursor->pointsToPreviousItems() && ! $this->hasMore)) {
+            return null;
+        }
+
+        if ($this->items->isEmpty()) {
+            return null;
+        }
+
+        return $this->getCursorForItem($this->items->first(), false);
+    }
+
+    /**
+     * Get the "cursor" that points to the next set of items.
+     */
+    public function nextCursor(): ?Cursor
+    {
+        if ((is_null($this->cursor) && ! $this->hasMore)
+            || (! is_null($this->cursor) && $this->cursor->pointsToNextItems() && ! $this->hasMore)) {
+            return null;
+        }
+
+        if ($this->items->isEmpty()) {
+            return null;
+        }
+
+        return $this->getCursorForItem($this->items->last());
+    }
+
+    /**
+     * Get a cursor instance for the given item.
+     */
+    public function getCursorForItem(array|ArrayAccess|stdClass $item, bool $isNext = true): Cursor
+    {
+        return new Cursor($this->getParametersForItem($item), $isNext);
+    }
+
+    /**
+     * Get the cursor parameters for a given object.
+     *
+     * @param ArrayAccess|stdClass $item
+     * @return array
+     *
+     * @throws Exception
+     */
+    public function getParametersForItem($item)
+    {
+        return collect($this->parameters)
+            ->filter()
+            ->flip()
+            ->map(function ($_, $parameterName) use ($item) {
+                if ($item instanceof JsonResource) {
+                    $item = $item->resource;
+                }
+
+                if ($item instanceof Model
+                    && ! is_null($parameter = $this->getPivotParameterForItem($item, $parameterName))) {
+                    return $parameter;
+                }
+                if ($item instanceof ArrayAccess || is_array($item)) {
+                    return $this->ensureParameterIsPrimitive(
+                        $item[$parameterName] ?? $item[Str::afterLast($parameterName, '.')]
+                    );
+                }
+                if (is_object($item)) {
+                    return $this->ensureParameterIsPrimitive(
+                        $item->{$parameterName} ?? $item->{Str::afterLast($parameterName, '.')}
+                    );
+                }
+
+                throw new Exception('Only arrays and objects are supported when cursor paginating items.');
+            })->toArray();
+    }
+
+    /**
+     * Get / set the URL fragment to be appended to URLs.
+     */
+    public function fragment(?string $fragment = null): null|static|string
+    {
+        if (is_null($fragment)) {
+            return $this->fragment;
+        }
+
+        $this->fragment = $fragment;
+
+        return $this;
+    }
+
+    /**
+     * Add a set of query string values to the paginator.
+     */
+    public function appends(null|array|string $key, ?string $value = null): static
+    {
+        if (is_null($key)) {
+            return $this;
+        }
+
+        if (is_array($key)) {
+            return $this->appendArray($key);
+        }
+
+        return $this->addQuery($key, $value);
+    }
+
+    /**
+     * Add all current query string values to the paginator.
+     */
+    public function withQueryString(): static
+    {
+        if ($query = Paginator::resolveQueryString()) {
+            return $this->appends($query);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Load a set of relationships onto the mixed relationship collection.
+     */
+    public function loadMorph(string $relation, array $relations): static
+    {
+        $collection = $this->getCollection();
+        if ($collection instanceof ModelCollection) {
+            $collection->loadMorph($relation, $relations);
+        }
+        return $this;
+    }
+
+    /**
+     * Load a set of relationship counts onto the mixed relationship collection.
+     */
+    public function loadMorphCount(string $relation, array $relations): static
+    {
+        $collection = $this->getCollection();
+        if ($collection instanceof ModelCollection) {
+            $collection->loadMorphCount($relation, $relations);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get the slice of items being paginated.
+     */
+    public function items(): array
+    {
+        return $this->items->all();
+    }
+
+    /**
+     * Transform each item in the slice of items using a callback.
+     */
+    public function through(callable $callback): static
+    {
+        $this->items->transform($callback);
+
+        return $this;
+    }
+
+    /**
+     * Get the number of items shown per page.
+     */
+    public function perPage(): int
+    {
+        return $this->perPage;
+    }
+
+    /**
+     * Get the current cursor being paginated.
+     */
+    public function cursor(): ?Cursor
+    {
+        return $this->cursor;
+    }
+
+    /**
+     * Get the query string variable used to store the cursor.
+     */
+    public function getCursorName(): string
+    {
+        return $this->cursorName;
+    }
+
+    /**
+     * Set the query string variable used to store the cursor.
+     */
+    public function setCursorName(string $name): static
+    {
+        $this->cursorName = $name;
+
+        return $this;
+    }
+
+    /**
+     * Set the base path to assign to all URLs.
+     */
+    public function withPath(string $path): static
+    {
+        return $this->setPath($path);
+    }
+
+    /**
+     * Set the base path to assign to all URLs.
+     */
+    public function setPath(string $path): static
+    {
+        $this->path = $path;
+
+        return $this;
+    }
+
+    /**
+     * Get the base path for paginator generated URLs.
+     */
+    public function path(): ?string
+    {
+        return $this->path;
+    }
+
+    /**
+     * Resolve the current cursor or return the default value.
+     * @param null|mixed $default
+     */
+    public static function resolveCurrentCursor(string $cursorName = 'cursor', $default = null): ?Cursor
+    {
+        if (isset(static::$currentCursorResolver)) {
+            return call_user_func(static::$currentCursorResolver, $cursorName);
+        }
+
+        return $default;
+    }
+
+    /**
+     * Set the current cursor resolver callback.
+     */
+    public static function currentCursorResolver(Closure $resolver): void
+    {
+        static::$currentCursorResolver = $resolver;
+    }
+
+    /**
+     * Get an iterator for the items.
+     */
+    public function getIterator(): Traversable
+    {
+        return $this->items->getIterator();
+    }
+
+    /**
+     * Determine if the list of items is empty.
+     */
+    public function isEmpty(): bool
+    {
+        return $this->items->isEmpty();
+    }
+
+    /**
+     * Determine if the list of items is not empty.
+     */
+    public function isNotEmpty(): bool
+    {
+        return $this->items->isNotEmpty();
+    }
+
+    /**
+     * Get the number of items for the current page.
+     */
+    public function count(): int
+    {
+        return $this->items->count();
+    }
+
+    /**
+     * Get the paginator's underlying collection.
+     */
+    public function getCollection(): Collection|ModelCollection
+    {
+        return $this->items;
+    }
+
+    /**
+     * Set the paginator's underlying collection.
+     */
+    public function setCollection(Collection $collection): static
+    {
+        $this->items = $collection;
+
+        return $this;
+    }
+
+    /**
+     * Get the paginator options.
+     */
+    public function getOptions(): array
+    {
+        return $this->options;
+    }
+
+    /**
+     * Determine if the given item exists.
+     */
+    public function offsetExists(mixed $key): bool
+    {
+        return $this->items->has($key);
+    }
+
+    /**
+     * Get the item at the given offset.
+     */
+    public function offsetGet(mixed $key): mixed
+    {
+        return $this->items->get($key);
+    }
+
+    /**
+     * Set the item at the given offset.
+     */
+    public function offsetSet(mixed $key, mixed $value): void
+    {
+        $this->items->put($key, $value);
+    }
+
+    /**
+     * Unset the item at the given key.
+     */
+    public function offsetUnset(mixed $key): void
+    {
+        $this->items->forget($key);
+    }
+
+    /**
+     * Get the cursor parameter value from a pivot model if applicable.
+     */
+    protected function getPivotParameterForItem(ArrayAccess|stdClass $item, string $parameterName): ?string
+    {
+        $table = Str::beforeLast($parameterName, '.');
+        if (is_object($item) && method_exists($item, 'getRelations')) {
+            foreach ($item->getRelations() as $relation) {
+                if ($relation instanceof Pivot && $relation->getTable() === $table) {
+                    return $this->ensureParameterIsPrimitive(
+                        $relation->getAttribute(Str::afterLast($parameterName, '.'))
+                    );
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Ensure the parameter is a primitive type.
+     *
+     * This can resolve issues that arise the developer uses a value object for an attribute.
+     */
+    protected function ensureParameterIsPrimitive(mixed $parameter): mixed
+    {
+        return is_object($parameter) && method_exists($parameter, '__toString')
+            ? (string) $parameter
+            : $parameter;
+    }
+
+    /**
+     * Add an array of query string values.
+     */
+    protected function appendArray(array $keys): static
+    {
+        foreach ($keys as $key => $value) {
+            $this->addQuery($key, $value);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add a query string value to the paginator.
+     */
+    protected function addQuery(string $key, string $value): static
+    {
+        if ($key !== $this->cursorName) {
+            $this->query[$key] = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Build the full fragment portion of a URL.
+     */
+    protected function buildFragment(): string
+    {
+        return $this->fragment ? '#' . $this->fragment : '';
+    }
+}

+ 488 - 0
vendor/hyperf/paginator/src/AbstractPaginator.php

@@ -0,0 +1,488 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Paginator;
+
+use ArrayAccess;
+use ArrayIterator;
+use Closure;
+use Hyperf\Collection\Arr;
+use Hyperf\Collection\Collection;
+use Hyperf\Contract\PaginatorInterface;
+use Hyperf\Stringable\Str;
+use Hyperf\Support\Traits\ForwardsCalls;
+use Stringable;
+
+abstract class AbstractPaginator implements PaginatorInterface, ArrayAccess, Stringable
+{
+    use ForwardsCalls;
+
+    /**
+     * The number of links to display on each side of current page link.
+     */
+    public int $onEachSide = 3;
+
+    /**
+     * The paginator options.
+     */
+    protected array $options = [];
+
+    /**
+     * All the items being paginated.
+     */
+    protected Collection $items;
+
+    /**
+     * The number of items to be shown per page.
+     */
+    protected int $perPage;
+
+    /**
+     * The current page being "viewed".
+     */
+    protected int $currentPage;
+
+    /**
+     * The base path to assign to all URLs.
+     */
+    protected string $path = '/';
+
+    /**
+     * The query parameters to add to all URLs.
+     */
+    protected array $query = [];
+
+    /**
+     * The URL fragment to add to all URLs.
+     */
+    protected ?string $fragment = null;
+
+    /**
+     * The query string variable used to store the page.
+     */
+    protected string $pageName = 'page';
+
+    /**
+     * The current path resolver callback.
+     */
+    protected static ?Closure $currentPathResolver = null;
+
+    /**
+     * The current page resolver callback.
+     */
+    protected static ?Closure $currentPageResolver = null;
+
+    /**
+     * The query string resolver callback.
+     */
+    protected static ?Closure $queryStringResolver = null;
+
+    /**
+     * Make dynamic calls into the collection.
+     */
+    public function __call(string $method, array $parameters)
+    {
+        return $this->forwardCallTo($this->getCollection(), $method, $parameters);
+    }
+
+    /**
+     * Render the contents of the paginator when casting to string.
+     */
+    public function __toString(): string
+    {
+        return $this->render();
+    }
+
+    /**
+     * Get the URL for the previous page.
+     */
+    public function previousPageUrl(): ?string
+    {
+        if ($this->currentPage() > 1) {
+            return $this->url($this->currentPage() - 1);
+        }
+        return null;
+    }
+
+    /**
+     * Create a range of pagination URLs.
+     */
+    public function getUrlRange(int $start, int $end): array
+    {
+        return Collection::range($start, $end)
+            ->mapWithKeys(fn ($page) => [$page => $this->url($page)])
+            ->all();
+    }
+
+    /**
+     * Get the URL for a given page number.
+     */
+    public function url(int $page): string
+    {
+        if ($page <= 0) {
+            $page = 1;
+        }
+
+        // If we have any extra query string key / value pairs that need to be added
+        // onto the URL, we will put them in query string form and then attach it
+        // to the URL. This allows for extra information like sortings storage.
+        $parameters = [$this->pageName => $page];
+
+        if (count($this->query) > 0) {
+            $parameters = array_merge($this->query, $parameters);
+        }
+
+        return $this->path . (Str::contains($this->path, '?') ? '&' : '?') . Arr::query($parameters) . $this->buildFragment();
+    }
+
+    /**
+     * Get / set the URL fragment to be appended to URLs.
+     *
+     * @return null|$this|string
+     */
+    public function fragment(?string $fragment = null)
+    {
+        if (is_null($fragment)) {
+            return $this->fragment;
+        }
+
+        $this->fragment = $fragment;
+
+        return $this;
+    }
+
+    /**
+     * Add a set of query string values to the paginator.
+     *
+     * @param null|array|string $key
+     */
+    public function appends($key, null|array|string $value = null): static
+    {
+        if (is_null($key)) {
+            return $this;
+        }
+
+        if (is_array($key)) {
+            return $this->appendArray($key);
+        }
+
+        return $this->addQuery($key, $value);
+    }
+
+    /**
+     * Load a set of relationships onto the mixed relationship collection.
+     */
+    public function loadMorph(string $relation, array $relations): static
+    {
+        $collection = $this->getCollection();
+        if (method_exists($collection, 'loadMorph')) {
+            $collection->loadMorph($relation, $relations);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Get the slice of items being paginated.
+     */
+    public function items(): array
+    {
+        return $this->items->all();
+    }
+
+    /**
+     * Get the number of the first item in the slice.
+     */
+    public function firstItem(): ?int
+    {
+        return count($this->items) > 0 ? ($this->currentPage - 1) * $this->perPage + 1 : null;
+    }
+
+    /**
+     * Get the number of the last item in the slice.
+     */
+    public function lastItem(): ?int
+    {
+        return count($this->items) > 0 ? $this->firstItem() + $this->count() - 1 : null;
+    }
+
+    /**
+     * Get the number of items shown per page.
+     */
+    public function perPage(): int
+    {
+        return $this->perPage;
+    }
+
+    /**
+     * Determine if there are enough items to split into multiple pages.
+     */
+    public function hasPages(): bool
+    {
+        return $this->currentPage() != 1 || $this->hasMorePages();
+    }
+
+    /**
+     * Determine if the paginator is on the first page.
+     */
+    public function onFirstPage(): bool
+    {
+        return $this->currentPage() <= 1;
+    }
+
+    /**
+     * Get the current page.
+     */
+    public function currentPage(): int
+    {
+        return $this->currentPage;
+    }
+
+    /**
+     * Get the query string variable used to store the page.
+     */
+    public function getPageName(): string
+    {
+        return $this->pageName;
+    }
+
+    /**
+     * Set the query string variable used to store the page.
+     */
+    public function setPageName(string $name): static
+    {
+        $this->pageName = $name;
+
+        return $this;
+    }
+
+    /**
+     * Set the base path to assign to all URLs.
+     */
+    public function withPath(string $path): static
+    {
+        return $this->setPath($path);
+    }
+
+    /**
+     * Set the base path to assign to all URLs.
+     */
+    public function setPath(string $path): static
+    {
+        $this->path = $path;
+
+        return $this;
+    }
+
+    /**
+     * Set the number of links to display on each side of current page link.
+     */
+    public function onEachSide(int $count): static
+    {
+        $this->onEachSide = $count;
+
+        return $this;
+    }
+
+    /**
+     * Resolve the current request path or return the default value.
+     */
+    public static function resolveCurrentPath(string $default = '/'): string
+    {
+        if (isset(static::$currentPathResolver)) {
+            return call_user_func(static::$currentPathResolver);
+        }
+
+        return $default;
+    }
+
+    /**
+     * Set the current request path resolver callback.
+     */
+    public static function currentPathResolver(Closure $resolver): void
+    {
+        static::$currentPathResolver = $resolver;
+    }
+
+    /**
+     * Resolve the current page or return the default value.
+     */
+    public static function resolveCurrentPage(string $pageName = 'page', int $default = 1): int
+    {
+        if (isset(static::$currentPageResolver)) {
+            return call_user_func(static::$currentPageResolver, $pageName);
+        }
+
+        return $default;
+    }
+
+    /**
+     * Set the current page resolver callback.
+     */
+    public static function currentPageResolver(Closure $resolver): void
+    {
+        static::$currentPageResolver = $resolver;
+    }
+
+    /**
+     * Get an iterator for the items.
+     */
+    public function getIterator(): ArrayIterator
+    {
+        return $this->items->getIterator();
+    }
+
+    /**
+     * Determine if the list of items is empty.
+     */
+    public function isEmpty(): bool
+    {
+        return $this->items->isEmpty();
+    }
+
+    /**
+     * Determine if the list of items is not empty.
+     */
+    public function isNotEmpty(): bool
+    {
+        return $this->items->isNotEmpty();
+    }
+
+    /**
+     * Get the number of items for the current page.
+     */
+    public function count(): int
+    {
+        return $this->items->count();
+    }
+
+    /**
+     * Get the paginator's underlying collection.
+     */
+    public function getCollection(): Collection
+    {
+        return $this->items;
+    }
+
+    /**
+     * Set the paginator's underlying collection.
+     */
+    public function setCollection(Collection $collection): static
+    {
+        $this->items = $collection;
+
+        return $this;
+    }
+
+    /**
+     * Get the paginator options.
+     */
+    public function getOptions(): array
+    {
+        return $this->options;
+    }
+
+    public function offsetExists(mixed $offset): bool
+    {
+        return $this->items->has($offset);
+    }
+
+    public function offsetGet(mixed $offset): mixed
+    {
+        return $this->items->get($offset);
+    }
+
+    public function offsetSet(mixed $offset, mixed $value): void
+    {
+        $this->items->put($offset, $value);
+    }
+
+    /**
+     * Unset the item at the given key.
+     */
+    public function offsetUnset(mixed $offset): void
+    {
+        $this->items->forget($offset);
+    }
+
+    /**
+     * Add all current query string values to the paginator.
+     */
+    public function withQueryString(): static
+    {
+        if (isset(static::$queryStringResolver)) {
+            return $this->appends(call_user_func(static::$queryStringResolver));
+        }
+
+        return $this;
+    }
+
+    /**
+     * Resolve the query string or return the default value.
+     */
+    public static function resolveQueryString(null|array|string $default = null): string
+    {
+        if (isset(static::$queryStringResolver)) {
+            return (static::$queryStringResolver)();
+        }
+
+        return $default;
+    }
+
+    /**
+     * Set with query string resolver callback.
+     */
+    public static function queryStringResolver(Closure $resolver): void
+    {
+        static::$queryStringResolver = $resolver;
+    }
+
+    /**
+     * Determine if the given value is a valid page number.
+     */
+    protected function isValidPageNumber(int $page): bool
+    {
+        return $page >= 1 && filter_var($page, FILTER_VALIDATE_INT) !== false;
+    }
+
+    /**
+     * Add an array of query string values.
+     */
+    protected function appendArray(array $keys): static
+    {
+        foreach ($keys as $key => $value) {
+            $this->addQuery($key, $value);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add a query string value to the paginator.
+     */
+    protected function addQuery(string $key, array|string $value): static
+    {
+        if ($key !== $this->pageName) {
+            $this->query[$key] = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Build the full fragment portion of a URL.
+     */
+    protected function buildFragment(): string
+    {
+        return $this->fragment ? '#' . $this->fragment : '';
+    }
+}

+ 33 - 0
vendor/hyperf/paginator/src/ConfigProvider.php

@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Paginator;
+
+use Hyperf\Contract\LengthAwarePaginatorInterface;
+use Hyperf\Contract\PaginatorInterface;
+use Hyperf\Paginator\Listener\PageResolverListener;
+
+class ConfigProvider
+{
+    public function __invoke(): array
+    {
+        return [
+            'dependencies' => [
+                PaginatorInterface::class => Paginator::class,
+                LengthAwarePaginatorInterface::class => LengthAwarePaginator::class,
+            ],
+            'listeners' => [
+                PageResolverListener::class,
+            ],
+        ];
+    }
+}

+ 93 - 0
vendor/hyperf/paginator/src/Contract/CursorPaginator.php

@@ -0,0 +1,93 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Paginator\Contract;
+
+use Hyperf\Paginator\Cursor;
+
+interface CursorPaginator
+{
+    /**
+     * Get the URL for a given cursor.
+     */
+    public function url(?Cursor $cursor): string;
+
+    /**
+     * Add a set of query string values to the paginator.
+     */
+    public function appends(null|array|string $key, ?string $value = null): static;
+
+    /**
+     * Get / set the URL fragment to be appended to URLs.
+     */
+    public function fragment(?string $fragment = null): null|static|string;
+
+    /**
+     * Add all current query string values to the paginator.
+     */
+    public function withQueryString(): static;
+
+    /**
+     * Get the URL for the previous page, or null.
+     */
+    public function previousPageUrl(): ?string;
+
+    /**
+     * The URL for the next page, or null.
+     */
+    public function nextPageUrl(): ?string;
+
+    /**
+     * Get all of the items being paginated.
+     */
+    public function items(): array;
+
+    /**
+     * Get the "cursor" of the previous set of items.
+     */
+    public function previousCursor(): ?Cursor;
+
+    /**
+     * Get the "cursor" of the next set of items.
+     */
+    public function nextCursor(): ?Cursor;
+
+    /**
+     * Determine how many items are being shown per page.
+     */
+    public function perPage(): int;
+
+    /**
+     * Get the current cursor being paginated.
+     */
+    public function cursor(): ?Cursor;
+
+    /**
+     * Determine if there are enough items to split into multiple pages.
+     */
+    public function hasPages(): bool;
+
+    /**
+     * Get the base path for paginator generated URLs.
+     */
+    public function path(): ?string;
+
+    /**
+     * Determine if the list of items is empty or not.
+     */
+    public function isEmpty(): bool;
+
+    /**
+     * Determine if the list of items is not empty.
+     */
+    public function isNotEmpty(): bool;
+}

+ 111 - 0
vendor/hyperf/paginator/src/Cursor.php

@@ -0,0 +1,111 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Paginator;
+
+use Hyperf\Contract\Arrayable;
+use JsonException;
+use UnexpectedValueException;
+
+use function Hyperf\Collection\collect;
+
+class Cursor implements Arrayable
+{
+    /**
+     * Create a new cursor instance.
+     * @param array $parameters the parameters associated with the cursor
+     * @param bool $pointsToNextItems determine whether the cursor points to the next or previous set of items
+     */
+    public function __construct(
+        protected array $parameters,
+        protected bool $pointsToNextItems = true
+    ) {
+    }
+
+    /**
+     * Get the given parameter from the cursor.
+     */
+    public function parameter(string $parameterName): ?string
+    {
+        if (! array_key_exists($parameterName, $this->parameters)) {
+            throw new UnexpectedValueException("Unable to find parameter [{$parameterName}] in pagination item.");
+        }
+
+        return (string) $this->parameters[$parameterName];
+    }
+
+    /**
+     * Get the given parameters from the cursor.
+     */
+    public function parameters(array $parameterNames): array
+    {
+        return collect($parameterNames)->map(function ($parameterName) {
+            return $this->parameter($parameterName);
+        })->toArray();
+    }
+
+    /**
+     * Determine whether the cursor points to the next set of items.
+     */
+    public function pointsToNextItems(): bool
+    {
+        return $this->pointsToNextItems;
+    }
+
+    /**
+     * Determine whether the cursor points to the previous set of items.
+     */
+    public function pointsToPreviousItems(): bool
+    {
+        return ! $this->pointsToNextItems;
+    }
+
+    /**
+     * Get the array representation of the cursor.
+     */
+    public function toArray(): array
+    {
+        return array_merge($this->parameters, [
+            '_pointsToNextItems' => $this->pointsToNextItems,
+        ]);
+    }
+
+    /**
+     * Get the encoded string representation of the cursor to construct a URL.
+     */
+    public function encode(): string
+    {
+        return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode(json_encode($this->toArray(), JSON_THROW_ON_ERROR)));
+    }
+
+    /**
+     * Get a cursor instance from the encoded string representation.
+     */
+    public static function fromEncoded(?string $encodedString): ?static
+    {
+        if (! is_string($encodedString)) {
+            return null;
+        }
+
+        try {
+            $parameters = json_decode(base64_decode(str_replace(['-', '_'], ['+', '/'], $encodedString)), true, 512, JSON_THROW_ON_ERROR);
+        } catch (JsonException $e) {
+            return null;
+        }
+
+        $pointsToNextItems = $parameters['_pointsToNextItems'];
+
+        unset($parameters['_pointsToNextItems']);
+
+        return new static($parameters, $pointsToNextItems);
+    }
+}

+ 138 - 0
vendor/hyperf/paginator/src/CursorPaginator.php

@@ -0,0 +1,138 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Paginator;
+
+use ArrayAccess;
+use Countable;
+use Hyperf\Collection\Collection;
+use Hyperf\Contract\Arrayable;
+use Hyperf\Contract\Jsonable;
+use Hyperf\Paginator\Contract\CursorPaginator as CursorPaginatorContract;
+use IteratorAggregate;
+use JsonSerializable;
+
+class CursorPaginator extends AbstractCursorPaginator implements ArrayAccess, Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable, CursorPaginatorContract
+{
+    /**
+     * Indicates whether there are more items in the data source.
+     *
+     * @return bool
+     */
+    protected bool $hasMore;
+
+    /**
+     * Create a new paginator instance.
+     */
+    public function __construct(mixed $items, int $perPage, ?Cursor $cursor = null, array $options = [])
+    {
+        $this->options = $options;
+
+        foreach ($options as $key => $value) {
+            $this->{$key} = $value;
+        }
+
+        $this->perPage = (int) $perPage;
+        $this->cursor = $cursor;
+        $this->path = $this->path !== '/' ? rtrim($this->path, '/') : $this->path;
+
+        $this->setItems($items);
+    }
+
+    public function __toString(): string
+    {
+        return $this->toJson();
+    }
+
+    /**
+     * Determine if there are more items in the data source.
+     */
+    public function hasMorePages(): bool
+    {
+        return (is_null($this->cursor) && $this->hasMore)
+            || (! is_null($this->cursor) && $this->cursor->pointsToNextItems() && $this->hasMore)
+            || (! is_null($this->cursor) && $this->cursor->pointsToPreviousItems());
+    }
+
+    /**
+     * Determine if there are enough items to split into multiple pages.
+     */
+    public function hasPages(): bool
+    {
+        return ! $this->onFirstPage() || $this->hasMorePages();
+    }
+
+    /**
+     * Determine if the paginator is on the first page.
+     */
+    public function onFirstPage(): bool
+    {
+        return is_null($this->cursor) || ($this->cursor->pointsToPreviousItems() && ! $this->hasMore);
+    }
+
+    /**
+     * Determine if the paginator is on the last page.
+     */
+    public function onLastPage(): bool
+    {
+        return ! $this->hasMorePages();
+    }
+
+    /**
+     * Get the instance as an array.
+     */
+    public function toArray(): array
+    {
+        return [
+            'data' => $this->items->toArray(),
+            'path' => $this->path(),
+            'per_page' => $this->perPage(),
+            'next_cursor' => $this->nextCursor()?->encode(),
+            'next_page_url' => $this->nextPageUrl(),
+            'prev_cursor' => $this->previousCursor()?->encode(),
+            'prev_page_url' => $this->previousPageUrl(),
+        ];
+    }
+
+    /**
+     * Convert the object into something JSON serializable.
+     */
+    public function jsonSerialize(): array
+    {
+        return $this->toArray();
+    }
+
+    /**
+     * Convert the object to its JSON representation.
+     */
+    public function toJson(int $options = 0): string
+    {
+        return json_encode($this->jsonSerialize(), JSON_THROW_ON_ERROR | $options);
+    }
+
+    /**
+     * Set the items for the paginator.
+     * @param mixed $items
+     */
+    protected function setItems($items)
+    {
+        $this->items = $items instanceof Collection ? $items : Collection::make($items);
+
+        $this->hasMore = $this->items->count() > $this->perPage;
+
+        $this->items = $this->items->slice(0, $this->perPage);
+
+        if (! is_null($this->cursor) && $this->cursor->pointsToPreviousItems()) {
+            $this->items = $this->items->reverse()->values();
+        }
+    }
+}

+ 179 - 0
vendor/hyperf/paginator/src/LengthAwarePaginator.php

@@ -0,0 +1,179 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Paginator;
+
+use Countable;
+use Hyperf\Collection\Collection;
+use Hyperf\Contract\Arrayable;
+use Hyperf\Contract\Jsonable;
+use Hyperf\Contract\LengthAwarePaginatorInterface;
+use IteratorAggregate;
+use JsonSerializable;
+use RuntimeException;
+
+class LengthAwarePaginator extends AbstractPaginator implements Arrayable, Countable, IteratorAggregate, JsonSerializable, Jsonable, LengthAwarePaginatorInterface
+{
+    /**
+     * The total number of items before slicing.
+     */
+    protected int $total;
+
+    /**
+     * The last available page.
+     */
+    protected int $lastPage;
+
+    /**
+     * Create a new paginator instance.
+     *
+     * @param array $options (path, query, fragment, pageName)
+     */
+    public function __construct(mixed $items, int $total, int $perPage, ?int $currentPage = 1, array $options = [])
+    {
+        $this->options = $options;
+
+        foreach ($options as $key => $value) {
+            $this->{$key} = $value;
+        }
+
+        $this->total = $total;
+        $this->perPage = $perPage;
+        $this->lastPage = max((int) ceil($total / $perPage), 1);
+        $this->path = $this->path !== '/' ? rtrim($this->path, '/') : $this->path;
+        $this->currentPage = $this->setCurrentPage($currentPage, $this->pageName);
+        $this->items = $items instanceof Collection ? $items : Collection::make($items);
+    }
+
+    public function __toString(): string
+    {
+        return $this->toJson();
+    }
+
+    /**
+     * Render the paginator using the given view.
+     */
+    public function links(?string $view = null, array $data = [])
+    {
+        return $this->render($view, $data);
+    }
+
+    /**
+     * Render the paginator using the given view.
+     */
+    public function render(?string $view = null, array $data = []): string
+    {
+        if ($view) {
+            throw new RuntimeException('WIP.');
+        }
+        return json_encode(array_merge($data, $this->items()), 0);
+    }
+
+    /**
+     * Get the total number of items being paginated.
+     */
+    public function total(): int
+    {
+        return $this->total;
+    }
+
+    /**
+     * Determine if there are more items in the data source.
+     */
+    public function hasMorePages(): bool
+    {
+        return $this->currentPage() < $this->lastPage();
+    }
+
+    /**
+     * Get the URL for the next page.
+     */
+    public function nextPageUrl(): ?string
+    {
+        if ($this->lastPage() > $this->currentPage()) {
+            return $this->url($this->currentPage() + 1);
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the last page.
+     */
+    public function lastPage(): int
+    {
+        return $this->lastPage;
+    }
+
+    /**
+     * Get the instance as an array.
+     */
+    public function toArray(): array
+    {
+        return [
+            'current_page' => $this->currentPage(),
+            'data' => $this->items->toArray(),
+            'first_page_url' => $this->url(1),
+            'from' => $this->firstItem(),
+            'last_page' => $this->lastPage(),
+            'last_page_url' => $this->url($this->lastPage()),
+            'next_page_url' => $this->nextPageUrl(),
+            'path' => $this->path,
+            'per_page' => $this->perPage(),
+            'prev_page_url' => $this->previousPageUrl(),
+            'to' => $this->lastItem(),
+            'total' => $this->total(),
+        ];
+    }
+
+    /**
+     * Convert the object into something JSON serializable.
+     */
+    public function jsonSerialize(): mixed
+    {
+        return $this->toArray();
+    }
+
+    /**
+     * Convert the object to its JSON representation.
+     */
+    public function toJson(int $options = 0): string
+    {
+        return json_encode($this->jsonSerialize(), $options);
+    }
+
+    /**
+     * Get the current page for the request.
+     */
+    protected function setCurrentPage(?int $currentPage, string $pageName): int
+    {
+        $currentPage = $currentPage ?: static::resolveCurrentPage($pageName);
+
+        return $this->isValidPageNumber($currentPage) ? $currentPage : 1;
+    }
+
+    /**
+     * Get the array of elements to pass to the view.
+     */
+    protected function elements(): array
+    {
+        $window = UrlWindow::make($this);
+
+        return array_filter([
+            $window['first'],
+            is_array($window['slider']) ? '...' : null,
+            $window['slider'],
+            is_array($window['last']) ? '...' : null,
+            $window['last'],
+        ]);
+    }
+}

+ 72 - 0
vendor/hyperf/paginator/src/Listener/PageResolverListener.php

@@ -0,0 +1,72 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Paginator\Listener;
+
+use Hyperf\Context\ApplicationContext;
+use Hyperf\Context\Context;
+use Hyperf\Event\Contract\ListenerInterface;
+use Hyperf\Framework\Event\BootApplication;
+use Hyperf\HttpServer\Contract\RequestInterface;
+use Hyperf\Paginator\Paginator;
+use Psr\Http\Message\ServerRequestInterface;
+
+class PageResolverListener implements ListenerInterface
+{
+    /**
+     * @return string[] returns the events that you want to listen
+     */
+    public function listen(): array
+    {
+        return [
+            BootApplication::class,
+        ];
+    }
+
+    /**
+     * Handle the Event when the event is triggered, all listeners will
+     * complete before the event is returned to the EventDispatcher.
+     */
+    public function process(object $event): void
+    {
+        Paginator::currentPageResolver(function ($pageName = 'page') {
+            if (! ApplicationContext::hasContainer()
+                || ! interface_exists(RequestInterface::class)
+                || ! Context::has(ServerRequestInterface::class)
+            ) {
+                return 1;
+            }
+
+            $container = ApplicationContext::getContainer();
+            $page = $container->get(RequestInterface::class)->input($pageName);
+
+            if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) {
+                return (int) $page;
+            }
+
+            return 1;
+        });
+
+        Paginator::currentPathResolver(function () {
+            $default = '/';
+            if (! ApplicationContext::hasContainer()
+                || ! interface_exists(RequestInterface::class)
+                || ! Context::has(ServerRequestInterface::class)
+            ) {
+                return $default;
+            }
+
+            $container = ApplicationContext::getContainer();
+            return $container->get(RequestInterface::class)->url();
+        });
+    }
+}

+ 152 - 0
vendor/hyperf/paginator/src/Paginator.php

@@ -0,0 +1,152 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Paginator;
+
+use Countable;
+use Hyperf\Collection\Collection;
+use Hyperf\Contract\Arrayable;
+use Hyperf\Contract\Jsonable;
+use IteratorAggregate;
+use JsonSerializable;
+use RuntimeException;
+
+class Paginator extends AbstractPaginator implements Arrayable, Countable, IteratorAggregate, JsonSerializable, Jsonable
+{
+    /**
+     * Determine if there are more items in the data source.
+     */
+    protected bool $hasMore;
+
+    /**
+     * Create a new paginator instance.
+     *
+     * @param array $options (path, query, fragment, pageName)
+     * @param mixed $items
+     */
+    public function __construct($items, int $perPage, ?int $currentPage = null, array $options = [])
+    {
+        $this->options = $options;
+
+        foreach ($options as $key => $value) {
+            $this->{$key} = $value;
+        }
+
+        $this->perPage = $perPage;
+        $this->currentPage = $this->setCurrentPage($currentPage);
+        $this->path = $this->path !== '/' ? rtrim($this->path, '/') : $this->path;
+
+        $this->setItems($items);
+    }
+
+    public function __toString(): string
+    {
+        return $this->toJson();
+    }
+
+    /**
+     * Get the URL for the next page.
+     */
+    public function nextPageUrl(): ?string
+    {
+        if ($this->hasMorePages()) {
+            return $this->url($this->currentPage() + 1);
+        }
+        return null;
+    }
+
+    /**
+     * Render the paginator using the given view.
+     */
+    public function render(?string $view = null, array $data = []): string
+    {
+        if ($view) {
+            throw new RuntimeException('WIP.');
+        }
+        return json_encode($data, 0);
+    }
+
+    /**
+     * Manually indicate that the paginator does have more pages.
+     */
+    public function hasMorePagesWhen(bool $hasMore = true): self
+    {
+        $this->hasMore = $hasMore;
+
+        return $this;
+    }
+
+    /**
+     * Determine if there are more items in the data source.
+     */
+    public function hasMorePages(): bool
+    {
+        return $this->hasMore;
+    }
+
+    /**
+     * Get the instance as an array.
+     */
+    public function toArray(): array
+    {
+        return [
+            'current_page' => $this->currentPage(),
+            'data' => $this->items->toArray(),
+            'first_page_url' => $this->url(1),
+            'from' => $this->firstItem(),
+            'next_page_url' => $this->nextPageUrl(),
+            'path' => $this->path,
+            'per_page' => $this->perPage(),
+            'prev_page_url' => $this->previousPageUrl(),
+            'to' => $this->lastItem(),
+        ];
+    }
+
+    /**
+     * Convert the object into something JSON serializable.
+     */
+    public function jsonSerialize(): mixed
+    {
+        return $this->toArray();
+    }
+
+    /**
+     * Convert the object to its JSON representation.
+     */
+    public function toJson(int $options = 0): string
+    {
+        return json_encode($this->jsonSerialize(), $options);
+    }
+
+    /**
+     * Get the current page for the request.
+     */
+    protected function setCurrentPage(?int $currentPage): int
+    {
+        $currentPage = $currentPage ?: static::resolveCurrentPage();
+
+        return $this->isValidPageNumber($currentPage) ? $currentPage : 1;
+    }
+
+    /**
+     * Set the items for the paginator.
+     * @param mixed $items
+     */
+    protected function setItems($items): void
+    {
+        $this->items = $items instanceof Collection ? $items : Collection::make($items);
+
+        $this->hasMore = $this->items->count() > $this->perPage;
+
+        $this->items = $this->items->slice(0, $this->perPage);
+    }
+}

+ 185 - 0
vendor/hyperf/paginator/src/UrlWindow.php

@@ -0,0 +1,185 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Paginator;
+
+use Hyperf\Contract\LengthAwarePaginatorInterface;
+
+class UrlWindow
+{
+    /**
+     * @param AbstractPaginator&LengthAwarePaginatorInterface $paginator
+     */
+    public function __construct(protected LengthAwarePaginatorInterface $paginator)
+    {
+    }
+
+    /**
+     * Create a new URL window instance.
+     */
+    public static function make(LengthAwarePaginatorInterface $paginator): array
+    {
+        return (new static($paginator))->get();
+    }
+
+    /**
+     * Get the window of URLs to be shown.
+     */
+    public function get(): array
+    {
+        $onEachSide = $this->paginator->onEachSide;
+
+        if ($this->paginator->lastPage() < ($onEachSide * 2) + 6) {
+            return $this->getSmallSlider();
+        }
+
+        return $this->getUrlSlider($onEachSide);
+    }
+
+    /**
+     * Get the page range for the current page window.
+     */
+    public function getAdjacentUrlRange(int $onEachSide): array
+    {
+        return $this->paginator->getUrlRange(
+            $this->currentPage() - $onEachSide,
+            $this->currentPage() + $onEachSide
+        );
+    }
+
+    /**
+     * Get the starting URLs of a pagination slider.
+     */
+    public function getStart(): array
+    {
+        return $this->paginator->getUrlRange(1, 2);
+    }
+
+    /**
+     * Get the ending URLs of a pagination slider.
+     */
+    public function getFinish(): array
+    {
+        return $this->paginator->getUrlRange(
+            $this->lastPage() - 1,
+            $this->lastPage()
+        );
+    }
+
+    /**
+     * Determine if the underlying paginator being presented has pages to show.
+     */
+    public function hasPages(): bool
+    {
+        return $this->paginator->lastPage() > 1;
+    }
+
+    /**
+     * Get the slider of URLs there are not enough pages to slide.
+     */
+    protected function getSmallSlider(): array
+    {
+        return [
+            'first' => $this->paginator->getUrlRange(1, $this->lastPage()),
+            'slider' => null,
+            'last' => null,
+        ];
+    }
+
+    /**
+     * Create a URL slider links.
+     */
+    protected function getUrlSlider(int $onEachSide): array
+    {
+        $window = $onEachSide * 2;
+
+        if (! $this->hasPages()) {
+            return ['first' => null, 'slider' => null, 'last' => null];
+        }
+
+        // If the current page is very close to the beginning of the page range, we will
+        // just render the beginning of the page range, followed by the last 2 of the
+        // links in this list, since we will not have room to create a full slider.
+        if ($this->currentPage() <= $window) {
+            return $this->getSliderTooCloseToBeginning($window);
+        }
+
+        // If the current page is close to the ending of the page range we will just get
+        // this first couple pages, followed by a larger window of these ending pages
+        // since we're too close to the end of the list to create a full on slider.
+        if ($this->currentPage() > ($this->lastPage() - $window)) {
+            return $this->getSliderTooCloseToEnding($window);
+        }
+
+        // If we have enough room on both sides of the current page to build a slider we
+        // will surround it with both the beginning and ending caps, with this window
+        // of pages in the middle providing a Google style sliding paginator setup.
+        return $this->getFullSlider($onEachSide);
+    }
+
+    /**
+     * Get the slider of URLs when too close to beginning of window.
+     */
+    protected function getSliderTooCloseToBeginning(int $window): array
+    {
+        return [
+            'first' => $this->paginator->getUrlRange(1, $window + 2),
+            'slider' => null,
+            'last' => $this->getFinish(),
+        ];
+    }
+
+    /**
+     * Get the slider of URLs when too close to ending of window.
+     */
+    protected function getSliderTooCloseToEnding(int $window): array
+    {
+        $last = $this->paginator->getUrlRange(
+            $this->lastPage() - ($window + 2),
+            $this->lastPage()
+        );
+
+        return [
+            'first' => $this->getStart(),
+            'slider' => null,
+            'last' => $last,
+        ];
+    }
+
+    /**
+     * Get the slider of URLs when a full slider can be made.
+     */
+    protected function getFullSlider(int $onEachSide): array
+    {
+        return [
+            'first' => $this->getStart(),
+            'slider' => $this->getAdjacentUrlRange($onEachSide),
+            'last' => $this->getFinish(),
+        ];
+    }
+
+    /**
+     * Get the current page from the paginator.
+     */
+    protected function currentPage(): int
+    {
+        return $this->paginator->currentPage();
+    }
+
+    /**
+     * Get the last page from the paginator.
+     */
+    protected function lastPage(): int
+    {
+        return $this->paginator->lastPage();
+    }
+}

+ 2 - 0
vendor/hyperf/snowflake/.gitattributes

@@ -0,0 +1,2 @@
+/tests export-ignore
+/.github export-ignore

+ 21 - 0
vendor/hyperf/snowflake/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Hyperf
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 46 - 0
vendor/hyperf/snowflake/composer.json

@@ -0,0 +1,46 @@
+{
+    "name": "hyperf/snowflake",
+    "description": "A snowflake library",
+    "license": "MIT",
+    "keywords": [
+        "php",
+        "snowflake"
+    ],
+    "homepage": "https://hyperf.io",
+    "support": {
+        "issues": "https://github.com/hyperf/hyperf/issues",
+        "source": "https://github.com/hyperf/hyperf",
+        "docs": "https://hyperf.wiki",
+        "pull-request": "https://github.com/hyperf/hyperf/pulls"
+    },
+    "require": {
+        "php": ">=8.1",
+        "hyperf/contract": "~3.1.0"
+    },
+    "suggest": {
+        "hyperf/config": "Required to read snowflake config.",
+        "hyperf/redis": "Required to use RedisMilliSecondMetaGenerator or RedisSecondMetaGenerator.",
+        "psr/container": "Required to use MetaGeneratorFactory."
+    },
+    "autoload": {
+        "psr-4": {
+            "Hyperf\\Snowflake\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "HyperfTest\\Snowflake\\": "tests/"
+        }
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "3.1-dev"
+        },
+        "hyperf": {
+            "config": "Hyperf\\Snowflake\\ConfigProvider"
+        }
+    }
+}

+ 24 - 0
vendor/hyperf/snowflake/publish/snowflake.php

@@ -0,0 +1,24 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+use Hyperf\Snowflake\MetaGenerator\RedisMilliSecondMetaGenerator;
+use Hyperf\Snowflake\MetaGenerator\RedisSecondMetaGenerator;
+use Hyperf\Snowflake\MetaGeneratorInterface;
+
+return [
+    'begin_second' => MetaGeneratorInterface::DEFAULT_BEGIN_SECOND,
+    RedisMilliSecondMetaGenerator::class => [
+        'pool' => 'default',
+    ],
+    RedisSecondMetaGenerator::class => [
+        'pool' => 'default',
+    ],
+];

+ 38 - 0
vendor/hyperf/snowflake/src/Concern/Snowflake.php

@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Snowflake\Concern;
+
+use Hyperf\Context\ApplicationContext;
+use Hyperf\Snowflake\IdGeneratorInterface;
+
+trait Snowflake
+{
+    public function creating()
+    {
+        if (! $this->getKey()) {
+            $container = ApplicationContext::getContainer();
+            $generator = $container->get(IdGeneratorInterface::class);
+            $this->{$this->getKeyName()} = $generator->generate();
+        }
+    }
+
+    public function getIncrementing()
+    {
+        return false;
+    }
+
+    public function getKeyType()
+    {
+        return 'int';
+    }
+}

+ 37 - 0
vendor/hyperf/snowflake/src/ConfigProvider.php

@@ -0,0 +1,37 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Snowflake;
+
+use Hyperf\Snowflake\IdGenerator\SnowflakeIdGenerator;
+
+class ConfigProvider
+{
+    public function __invoke(): array
+    {
+        return [
+            'dependencies' => [
+                IdGeneratorInterface::class => SnowflakeIdGenerator::class,
+                MetaGeneratorInterface::class => MetaGeneratorFactory::class,
+                ConfigurationInterface::class => Configuration::class,
+            ],
+            'publish' => [
+                [
+                    'id' => 'config',
+                    'description' => 'The config of snowflake.',
+                    'source' => __DIR__ . '/../publish/snowflake.php',
+                    'destination' => BASE_PATH . '/config/autoload/snowflake.php',
+                ],
+            ],
+        ];
+    }
+}

+ 74 - 0
vendor/hyperf/snowflake/src/Configuration.php

@@ -0,0 +1,74 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Snowflake;
+
+class Configuration implements ConfigurationInterface
+{
+    protected int $millisecondBits = 41;
+
+    protected int $dataCenterIdBits = 5;
+
+    protected int $workerIdBits = 5;
+
+    protected int $sequenceBits = 12;
+
+    public function maxWorkerId(): int
+    {
+        return -1 ^ (-1 << $this->workerIdBits);
+    }
+
+    public function maxDataCenterId(): int
+    {
+        return -1 ^ (-1 << $this->dataCenterIdBits);
+    }
+
+    public function maxSequence(): int
+    {
+        return -1 ^ (-1 << $this->sequenceBits);
+    }
+
+    public function getTimestampLeftShift(): int
+    {
+        return $this->sequenceBits + $this->workerIdBits + $this->dataCenterIdBits;
+    }
+
+    public function getDataCenterIdShift(): int
+    {
+        return $this->sequenceBits + $this->workerIdBits;
+    }
+
+    public function getWorkerIdShift(): int
+    {
+        return $this->sequenceBits;
+    }
+
+    public function getTimestampBits(): int
+    {
+        return $this->millisecondBits;
+    }
+
+    public function getDataCenterIdBits(): int
+    {
+        return $this->dataCenterIdBits;
+    }
+
+    public function getWorkerIdBits(): int
+    {
+        return $this->workerIdBits;
+    }
+
+    public function getSequenceBits(): int
+    {
+        return $this->sequenceBits;
+    }
+}

+ 66 - 0
vendor/hyperf/snowflake/src/ConfigurationInterface.php

@@ -0,0 +1,66 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Snowflake;
+
+interface ConfigurationInterface
+{
+    /**
+     * Get the maximum worker id bits.
+     */
+    public function maxWorkerId(): int;
+
+    /**
+     * Get the maximum data center id bits.
+     */
+    public function maxDataCenterId(): int;
+
+    /**
+     * Get the maximum sequence bits.
+     */
+    public function maxSequence(): int;
+
+    /**
+     * Get the timestamp left shift.
+     */
+    public function getTimestampLeftShift(): int;
+
+    /**
+     * Get the data center id shift.
+     */
+    public function getDataCenterIdShift(): int;
+
+    /**
+     * Get the worker id shift.
+     */
+    public function getWorkerIdShift(): int;
+
+    /**
+     * Get the timestamp bits.
+     */
+    public function getTimestampBits(): int;
+
+    /**
+     * Get the data center id bits.
+     */
+    public function getDataCenterIdBits(): int;
+
+    /**
+     * Get the worker id bits.
+     */
+    public function getWorkerIdBits(): int;
+
+    /**
+     * Get the sequence bits.
+     */
+    public function getSequenceBits(): int;
+}

+ 19 - 0
vendor/hyperf/snowflake/src/Exception/SnowflakeException.php

@@ -0,0 +1,19 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Snowflake\Exception;
+
+use RuntimeException;
+
+class SnowflakeException extends RuntimeException
+{
+}

+ 63 - 0
vendor/hyperf/snowflake/src/IdGenerator.php

@@ -0,0 +1,63 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Snowflake;
+
+abstract class IdGenerator implements IdGeneratorInterface
+{
+    protected ConfigurationInterface $config;
+
+    public function __construct(protected MetaGeneratorInterface $metaGenerator)
+    {
+        $this->config = $metaGenerator->getConfiguration();
+    }
+
+    public function generate(?Meta $meta = null): int
+    {
+        $meta = $this->meta($meta);
+
+        $interval = $meta->getTimeInterval() << $this->config->getTimestampLeftShift();
+        $dataCenterId = $meta->getDataCenterId() << $this->config->getDataCenterIdShift();
+        $workerId = $meta->getWorkerId() << $this->config->getWorkerIdShift();
+
+        return $interval | $dataCenterId | $workerId | $meta->getSequence();
+    }
+
+    public function degenerate(int $id): Meta
+    {
+        $interval = $id >> $this->config->getTimestampLeftShift();
+        $dataCenterId = $id >> $this->config->getDataCenterIdShift();
+        $workerId = $id >> $this->config->getWorkerIdShift();
+
+        return new Meta(
+            $interval << $this->config->getDataCenterIdBits() ^ $dataCenterId,
+            $dataCenterId << $this->config->getWorkerIdBits() ^ $workerId,
+            $workerId << $this->config->getSequenceBits() ^ $id,
+            $interval + $this->metaGenerator->getBeginTimestamp(),
+            $this->metaGenerator->getBeginTimestamp()
+        );
+    }
+
+    public function getMetaGenerator(): MetaGeneratorInterface
+    {
+        return $this->metaGenerator;
+    }
+
+    protected function meta(?Meta $meta = null): Meta
+    {
+        if (is_null($meta)) {
+            return $this->metaGenerator->generate();
+        }
+
+        return $meta;
+    }
+}

+ 19 - 0
vendor/hyperf/snowflake/src/IdGenerator/SnowflakeIdGenerator.php

@@ -0,0 +1,19 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Snowflake\IdGenerator;
+
+use Hyperf\Snowflake\IdGenerator;
+
+class SnowflakeIdGenerator extends IdGenerator
+{
+}

+ 26 - 0
vendor/hyperf/snowflake/src/IdGeneratorInterface.php

@@ -0,0 +1,26 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Snowflake;
+
+interface IdGeneratorInterface extends \Hyperf\Contract\IdGeneratorInterface
+{
+    /**
+     * Generate an ID by meta, if meta is null, then use the default meta.
+     */
+    public function generate(?Meta $meta = null): int;
+
+    /**
+     * Degenerate the meta by ID.
+     */
+    public function degenerate(int $id): Meta;
+}

+ 98 - 0
vendor/hyperf/snowflake/src/Meta.php

@@ -0,0 +1,98 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Snowflake;
+
+class Meta
+{
+    /**
+     * @var int [0, 31]
+     */
+    protected int $dataCenterId;
+
+    /**
+     * @var int [0, 31]
+     */
+    protected int $workerId;
+
+    /**
+     * @var int [0, 4095]
+     */
+    protected int $sequence;
+
+    /**
+     * @var int seconds or milliseconds
+     */
+    protected int $timestamp = 0;
+
+    /**
+     * @var int seconds or milliseconds
+     */
+    protected int $beginTimestamp = 0;
+
+    public function __construct(int $dataCenterId, int $workerId, int $sequence, int $timestamp, int $beginTimestamp = 1560960000)
+    {
+        $this->dataCenterId = $dataCenterId;
+        $this->workerId = $workerId;
+        $this->sequence = $sequence;
+        $this->timestamp = $timestamp;
+        $this->beginTimestamp = $beginTimestamp;
+    }
+
+    public function getTimeInterval(): int
+    {
+        return $this->timestamp - $this->beginTimestamp;
+    }
+
+    public function getDataCenterId(): int
+    {
+        return $this->dataCenterId;
+    }
+
+    public function getWorkerId(): int
+    {
+        return $this->workerId;
+    }
+
+    public function getSequence(): int
+    {
+        return $this->sequence;
+    }
+
+    public function setDataCenterId(int $dataCenterId): self
+    {
+        $this->dataCenterId = $dataCenterId;
+        return $this;
+    }
+
+    public function setWorkerId(int $workerId): self
+    {
+        $this->workerId = $workerId;
+        return $this;
+    }
+
+    public function setSequence(int $sequence): self
+    {
+        $this->sequence = $sequence;
+        return $this;
+    }
+
+    public function getTimestamp(): int
+    {
+        return $this->timestamp;
+    }
+
+    public function getBeginTimestamp(): int
+    {
+        return $this->beginTimestamp;
+    }
+}

+ 77 - 0
vendor/hyperf/snowflake/src/MetaGenerator.php

@@ -0,0 +1,77 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Snowflake;
+
+use Hyperf\Snowflake\Exception\SnowflakeException;
+
+abstract class MetaGenerator implements MetaGeneratorInterface
+{
+    protected int $sequence = 0;
+
+    protected int $lastTimestamp = 0;
+
+    public function __construct(protected ConfigurationInterface $configuration, protected int $beginTimestamp)
+    {
+        $this->lastTimestamp = $this->getTimestamp();
+    }
+
+    public function generate(): Meta
+    {
+        $timestamp = $this->getTimestamp();
+
+        if ($timestamp == $this->lastTimestamp) {
+            $this->sequence = ($this->sequence + 1) % $this->configuration->maxSequence();
+            if ($this->sequence == 0) {
+                $timestamp = $this->getNextTimestamp();
+            }
+        } elseif ($timestamp < $this->lastTimestamp) {
+            $this->clockMovedBackwards($timestamp, $this->lastTimestamp);
+            $this->sequence = ($this->sequence + 1) % $this->configuration->maxSequence();
+            $timestamp = $this->lastTimestamp;
+        } else {
+            $this->sequence = 0;
+        }
+
+        if ($timestamp < $this->beginTimestamp) {
+            throw new SnowflakeException(sprintf('The beginTimestamp %d is invalid, because it smaller than timestamp %d.', $this->beginTimestamp, $timestamp));
+        }
+
+        $this->lastTimestamp = $timestamp;
+        $sequence = $this->sequence;
+
+        return new Meta($this->getDataCenterId(), $this->getWorkerId(), $sequence, $timestamp, $this->beginTimestamp);
+    }
+
+    public function getBeginTimestamp(): int
+    {
+        return $this->beginTimestamp;
+    }
+
+    public function getConfiguration(): ConfigurationInterface
+    {
+        return $this->configuration;
+    }
+
+    abstract public function getDataCenterId(): int;
+
+    abstract public function getWorkerId(): int;
+
+    abstract public function getTimestamp(): int;
+
+    abstract public function getNextTimestamp(): int;
+
+    protected function clockMovedBackwards($timestamp, $lastTimestamp)
+    {
+        throw new SnowflakeException(sprintf('Clock moved backwards. Refusing to generate id for %d milliseconds.', $lastTimestamp - $timestamp));
+    }
+}

+ 49 - 0
vendor/hyperf/snowflake/src/MetaGenerator/RandomMilliSecondMetaGenerator.php

@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Snowflake\MetaGenerator;
+
+use Hyperf\Snowflake\ConfigurationInterface;
+use Hyperf\Snowflake\MetaGenerator;
+
+class RandomMilliSecondMetaGenerator extends MetaGenerator
+{
+    public function __construct(ConfigurationInterface $configuration, int $beginTimestamp)
+    {
+        parent::__construct($configuration, $beginTimestamp * 1000);
+    }
+
+    public function getDataCenterId(): int
+    {
+        return rand(0, 31);
+    }
+
+    public function getWorkerId(): int
+    {
+        return rand(0, 31);
+    }
+
+    public function getTimestamp(): int
+    {
+        return intval(microtime(true) * 1000);
+    }
+
+    public function getNextTimestamp(): int
+    {
+        $timestamp = $this->getTimestamp();
+        while ($timestamp <= $this->lastTimestamp) {
+            $timestamp = $this->getTimestamp();
+        }
+
+        return $timestamp;
+    }
+}

+ 81 - 0
vendor/hyperf/snowflake/src/MetaGenerator/RedisMetaGenerator.php

@@ -0,0 +1,81 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Snowflake\MetaGenerator;
+
+use Hyperf\Contract\ConfigInterface;
+use Hyperf\Coroutine\Locker;
+use Hyperf\Redis\RedisProxy;
+use Hyperf\Snowflake\ConfigurationInterface;
+use Hyperf\Snowflake\MetaGenerator;
+use Redis;
+
+use function Hyperf\Support\make;
+
+abstract class RedisMetaGenerator extends MetaGenerator
+{
+    public const DEFAULT_REDIS_KEY = 'hyperf:snowflake:workerId';
+
+    protected ?int $workerId = null;
+
+    protected ?int $dataCenterId = null;
+
+    public function __construct(ConfigurationInterface $configuration, int $beginTimestamp, protected ConfigInterface $config)
+    {
+        parent::__construct($configuration, $beginTimestamp);
+    }
+
+    public function init()
+    {
+        if (is_null($this->workerId) || is_null($this->dataCenterId)) {
+            if (Locker::lock(static::class)) {
+                try {
+                    $this->initDataCenterIdAndWorkerId();
+                } finally {
+                    Locker::unlock(static::class);
+                }
+            }
+        }
+    }
+
+    public function getDataCenterId(): int
+    {
+        $this->init();
+
+        return $this->dataCenterId;
+    }
+
+    public function getWorkerId(): int
+    {
+        $this->init();
+
+        return $this->workerId;
+    }
+
+    private function initDataCenterIdAndWorkerId(): void
+    {
+        if (is_null($this->workerId) || is_null($this->dataCenterId)) {
+            $pool = $this->config->get(sprintf('snowflake.%s.pool', static::class), 'default');
+
+            /** @var Redis $redis */
+            $redis = make(RedisProxy::class, [
+                'pool' => $pool,
+            ]);
+
+            $key = $this->config->get(sprintf('snowflake.%s.key', static::class), static::DEFAULT_REDIS_KEY);
+            $id = $redis->incr($key);
+
+            $this->workerId = $id % $this->configuration->maxWorkerId();
+            $this->dataCenterId = intval($id / $this->configuration->maxWorkerId()) % $this->configuration->maxDataCenterId();
+        }
+    }
+}

+ 39 - 0
vendor/hyperf/snowflake/src/MetaGenerator/RedisMilliSecondMetaGenerator.php

@@ -0,0 +1,39 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Snowflake\MetaGenerator;
+
+use Hyperf\Contract\ConfigInterface;
+use Hyperf\Snowflake\ConfigurationInterface;
+
+class RedisMilliSecondMetaGenerator extends RedisMetaGenerator
+{
+    public function __construct(ConfigurationInterface $configuration, int $beginTimestamp, ConfigInterface $config)
+    {
+        parent::__construct($configuration, $beginTimestamp * 1000, $config);
+    }
+
+    public function getTimestamp(): int
+    {
+        return intval(microtime(true) * 1000);
+    }
+
+    public function getNextTimestamp(): int
+    {
+        $timestamp = $this->getTimestamp();
+        while ($timestamp <= $this->lastTimestamp) {
+            $timestamp = $this->getTimestamp();
+        }
+
+        return $timestamp;
+    }
+}

+ 39 - 0
vendor/hyperf/snowflake/src/MetaGenerator/RedisSecondMetaGenerator.php

@@ -0,0 +1,39 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Snowflake\MetaGenerator;
+
+use Hyperf\Contract\ConfigInterface;
+use Hyperf\Snowflake\ConfigurationInterface;
+
+class RedisSecondMetaGenerator extends RedisMetaGenerator
+{
+    public function __construct(ConfigurationInterface $configuration, int $beginTimestamp, ConfigInterface $config)
+    {
+        parent::__construct($configuration, $beginTimestamp, $config);
+    }
+
+    public function getTimestamp(): int
+    {
+        return time();
+    }
+
+    public function getNextTimestamp(): int
+    {
+        return $this->lastTimestamp + 1;
+    }
+
+    protected function clockMovedBackwards($timestamp, $lastTimestamp)
+    {
+        // Don't throw exception
+    }
+}

+ 34 - 0
vendor/hyperf/snowflake/src/MetaGeneratorFactory.php

@@ -0,0 +1,34 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Snowflake;
+
+use Hyperf\Contract\ConfigInterface;
+use Hyperf\Snowflake\MetaGenerator\RedisMilliSecondMetaGenerator;
+use Psr\Container\ContainerInterface;
+
+use function Hyperf\Support\make;
+
+class MetaGeneratorFactory
+{
+    public function __invoke(ContainerInterface $container)
+    {
+        $config = $container->get(ConfigInterface::class);
+        $beginSecond = $config->get('snowflake.begin_second', MetaGeneratorInterface::DEFAULT_BEGIN_SECOND);
+
+        return make(RedisMilliSecondMetaGenerator::class, [
+            $container->get(ConfigurationInterface::class),
+            $beginSecond,
+            $config,
+        ]);
+    }
+}

+ 24 - 0
vendor/hyperf/snowflake/src/MetaGeneratorInterface.php

@@ -0,0 +1,24 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * This file is part of Hyperf.
+ *
+ * @link     https://www.hyperf.io
+ * @document https://hyperf.wiki
+ * @contact  group@hyperf.io
+ * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
+ */
+
+namespace Hyperf\Snowflake;
+
+interface MetaGeneratorInterface
+{
+    public const DEFAULT_BEGIN_SECOND = 1560960000;
+
+    public function generate(): Meta;
+
+    public function getBeginTimestamp(): int;
+
+    public function getConfiguration(): ConfigurationInterface;
+}

+ 19 - 0
vendor/ramsey/collection/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2015-2022 Ben Ramsey <ben@benramsey.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 59 - 0
vendor/ramsey/collection/README.md

@@ -0,0 +1,59 @@
+<h1 align="center">ramsey/collection</h1>
+
+<p align="center">
+    <strong>A PHP library for representing and manipulating collections.</strong>
+</p>
+
+<p align="center">
+    <a href="https://github.com/ramsey/collection"><img src="http://img.shields.io/badge/source-ramsey/collection-blue.svg?style=flat-square" alt="Source Code"></a>
+    <a href="https://packagist.org/packages/ramsey/collection"><img src="https://img.shields.io/packagist/v/ramsey/collection.svg?style=flat-square&label=release" alt="Download Package"></a>
+    <a href="https://php.net"><img src="https://img.shields.io/packagist/php-v/ramsey/collection.svg?style=flat-square&colorB=%238892BF" alt="PHP Programming Language"></a>
+    <a href="https://github.com/ramsey/collection/blob/master/LICENSE"><img src="https://img.shields.io/packagist/l/ramsey/collection.svg?style=flat-square&colorB=darkcyan" alt="Read License"></a>
+    <a href="https://github.com/ramsey/collection/actions/workflows/continuous-integration.yml"><img src="https://img.shields.io/github/actions/workflow/status/ramsey/collection/continuous-integration.yml?branch=main&logo=github&style=flat-square" alt="Build Status"></a>
+    <a href="https://codecov.io/gh/ramsey/collection"><img src="https://img.shields.io/codecov/c/gh/ramsey/collection?label=codecov&logo=codecov&style=flat-square" alt="Codecov Code Coverage"></a>
+</p>
+
+## About
+
+ramsey/collection is a PHP library for representing and manipulating collections.
+
+Much inspiration for this library came from the [Java Collections Framework][java].
+
+This project adheres to a [code of conduct](CODE_OF_CONDUCT.md).
+By participating in this project and its community, you are expected to
+uphold this code.
+
+## Installation
+
+Install this package as a dependency using [Composer](https://getcomposer.org).
+
+``` bash
+composer require ramsey/collection
+```
+
+## Usage
+
+Examples of how to use this library may be found in the
+[Wiki pages](https://github.com/ramsey/collection/wiki/Examples).
+
+## Contributing
+
+Contributions are welcome! To contribute, please familiarize yourself with
+[CONTRIBUTING.md](CONTRIBUTING.md).
+
+## Coordinated Disclosure
+
+Keeping user information safe and secure is a top priority, and we welcome the
+contribution of external security researchers. If you believe you've found a
+security issue in software that is maintained in this repository, please read
+[SECURITY.md][] for instructions on submitting a vulnerability report.
+
+## Copyright and License
+
+The ramsey/collection library is copyright © [Ben Ramsey](https://benramsey.com)
+and licensed for use under the terms of the
+MIT License (MIT). Please see [LICENSE](LICENSE) for more information.
+
+
+[java]: http://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html
+[security.md]: https://github.com/ramsey/collection/blob/main/SECURITY.md

+ 169 - 0
vendor/ramsey/collection/SECURITY.md

@@ -0,0 +1,169 @@
+<!--
+    This policy template was created using the HackerOne Policy Builder [1],
+    with guidance from the National Telecommunications and Information
+    Administration Coordinated Vulnerability Disclosure Template [2].
+ -->
+
+# Vulnerability Disclosure Policy (VDP)
+
+## Brand Promise
+
+<!--
+    This is your brand promise. Its objective is to "demonstrate a clear, good
+    faith commitment to customers and other stakeholders potentially impacted by
+    security vulnerabilities" [2].
+-->
+
+Keeping user information safe and secure is a top priority, and we welcome the
+contribution of external security researchers.
+
+## Scope
+
+<!--
+    This is your initial scope. It tells vulnerability finders and reporters
+    "which systems and capabilities are 'fair game' versus 'off limits'" [2].
+    For software packages, this is often a list of currently maintained versions
+    of the package.
+-->
+
+If you believe you've found a security issue in software that is maintained in
+this repository, we encourage you to notify us.
+
+| Version | In scope | Source code |
+| ------- | :------: | ----------- |
+| latest  | ✅        | https://github.com/ramsey/collection |
+
+## How to Submit a Report
+
+<!--
+    This is your communication process. It tells security researchers how to
+    contact you to report a vulnerability. It may be a link to a web form that
+    uses HTTPS for secure communication, or it may be an email address.
+    Optionally, you may choose to include a PGP public key, so that researchers
+    may send you encrypted messages.
+-->
+
+To submit a vulnerability report, please contact us at security@ramsey.dev.
+Your submission will be reviewed and validated by a member of our team.
+
+## Safe Harbor
+
+<!--
+    This section assures vulnerability finders and reporters that they will
+    receive good faith responses to their good faith acts. In other words,
+    "we will not take legal action if..." [2].
+-->
+
+We support safe harbor for security researchers who:
+
+* Make a good faith effort to avoid privacy violations, destruction of data, and
+  interruption or degradation of our services.
+* Only interact with accounts you own or with explicit permission of the account
+  holder. If you do encounter Personally Identifiable Information (PII) contact
+  us immediately, do not proceed with access, and immediately purge any local
+  information.
+* Provide us with a reasonable amount of time to resolve vulnerabilities prior
+  to any disclosure to the public or a third party.
+
+We will consider activities conducted consistent with this policy to constitute
+"authorized" conduct and will not pursue civil action or initiate a complaint to
+law enforcement. We will help to the extent we can if legal action is initiated
+by a third party against you.
+
+Please submit a report to us before engaging in conduct that may be inconsistent
+with or unaddressed by this policy.
+
+## Preferences
+
+<!--
+    The preferences section sets expectations based on priority and submission
+    volume, rather than legal objection or restriction [2].
+
+    According to the NTIA [2]:
+
+        This section is a living document that sets expectations for preferences
+        and priorities, typically maintained by the support and engineering
+        team. This can outline classes of vulnerabilities, reporting style
+        (crash dumps, CVSS scoring, proof-of-concept, etc.), tools, etc. Too
+        many preferences can set the wrong tone or make reporting findings
+        difficult to navigate. This section also sets expectations to the
+        researcher community for what types of issues are considered important
+        or not.
+-->
+
+* Please provide detailed reports with reproducible steps and a clearly defined
+  impact.
+* Include the version number of the vulnerable package in your report
+* Social engineering (e.g. phishing, vishing, smishing) is prohibited.
+
+<!--
+    References
+
+    [1] HackerOne. Policy builder. Retrieved from https://hackerone.com/policy-builder/
+
+    [2] NTIA Safety Working Group. 2016. "Early stage" coordinated vulnerability
+    disclosure template: Version 1.1. (15 December 2016). Retrieved from
+    https://www.ntia.doc.gov/files/ntia/publications/ntia_vuln_disclosure_early_stage_template.pdf
+-->
+
+## Encryption Key for security@ramsey.dev
+
+For increased privacy when reporting sensitive issues, you may encrypt your
+message using the following public key:
+
+```
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBF+Z9gEBEACbT/pIx8RR0K18t8Z2rDnmEV44YdT7HNsMdq+D6SAlx8UUb6AU
+jGIbV9dgBgGNtOLU1pxloaJwL9bWIRbj+X/Qb2WNIP//Vz1Y40ox1dSpfCUrizXx
+kb4p58Xml0PsB8dg3b4RDUgKwGC37ne5xmDnigyJPbiB2XJ6Xc46oPCjh86XROTK
+wEBB2lY67ClBlSlvC2V9KmbTboRQkLdQDhOaUosMb99zRb0EWqDLaFkZVjY5HI7i
+0pTveE6dI12NfHhTwKjZ5pUiAZQGlKA6J1dMjY2unxHZkQj5MlMfrLSyJHZxccdJ
+xD94T6OTcTHt/XmMpI2AObpewZDdChDQmcYDZXGfAhFoJmbvXsmLMGXKgzKoZ/ls
+RmLsQhh7+/r8E+Pn5r+A6Hh4uAc14ApyEP0ckKeIXw1C6pepHM4E8TEXVr/IA6K/
+z6jlHORixIFX7iNOnfHh+qwOgZw40D6JnBfEzjFi+T2Cy+JzN2uy7I8UnecTMGo3
+5t6astPy6xcH6kZYzFTV7XERR6LIIVyLAiMFd8kF5MbJ8N5ElRFsFHPW+82N2HDX
+c60iSaTB85k6R6xd8JIKDiaKE4sSuw2wHFCKq33d/GamYezp1wO+bVUQg88efljC
+2JNFyD+vl30josqhw1HcmbE1TP3DlYeIL5jQOlxCMsgai6JtTfHFM/5MYwARAQAB
+tBNzZWN1cml0eUByYW1zZXkuZGV2iQJUBBMBCAA+FiEE4drPD+/ofZ570fAYq0bv
+vXQCywIFAl+Z9gECGwMFCQeGH4AFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQ
+q0bvvXQCywIkEA//Qcwv8MtTCy01LHZd9c7VslwhNdXQDYymcTyjcYw8x7O22m4B
+3hXE6vqAplFhVxxkqXB2ef0tQuzxhPHNJgkCE4Wq4i+V6qGpaSVHQT2W6DN/NIhL
+vS8OdScc6zddmIbIkSrzVVAtjwehFNEIrX3DnbbbK+Iku7vsKT5EclOluIsjlYoX
+goW8IeReyDBqOe2H3hoCGw6EA0D/NYV2bJnfy53rXVIyarsXXeOLp7eNEH6Td7aW
+PVSrMZJe1t+knrEGnEdrXWzlg4lCJJCtemGv+pKBUomnyISXSdqyoRCCzvQjqyig
+2kRebUX8BXPW33p4OXPj9sIboUOjZwormWwqqbFMO+J4TiVCUoEoheI7emPFRcNN
+QtPJrjbY1++OznBc0GRpfeUkGoU1cbRl1bnepnFIZMTDLkrVW6I1Y4q8ZVwX3BkE
+N81ctFrRpHBlU36EdHvjPQmGtuiL77Qq3fWmMv7yTvK1wHJAXfEb0ZJWHZCbck3w
+l0CVq0Z+UUAOM8Rp1N0N8m92xtapav0qCFU9qzf2J5qX6GRmWv+d29wPgFHzDWBm
+nnrYYIA4wJLx00U6SMcVBSnNe91B+RfGY5XQhbWPjQQecOGCSDsxaFAq2MeOVJyZ
+bIjLYfG9GxoLKr5R7oLRJvZI4nKKBc1Kci/crZbdiSdQhSQGlDz88F1OHeCIdQQQ
+EQgAHRYhBOhdAxHd+lus86YQ57Atl5icjAcbBQJfmfdIAAoJELAtl5icjAcbFVcA
+/1LqB3ZjsnXDAvvAXZVjSPqofSlpMLeRQP6IM/A9Odq0AQCZrtZc1knOMGEcjppK
+Rk+sy/R0Mshy8TDuaZIRgh2Ux7kCDQRfmfYBARAAmchKzzVz7IaEq7PnZDb3szQs
+T/+E9F3m39yOpV4fEB1YzObonFakXNT7Gw2tZEx0eitUMqQ/13jjfu3UdzlKl2bR
+qA8LrSQRhB+PTC9A1XvwxCUYhhjGiLzJ9CZL6hBQB43qHOmE9XJPme90geLsF+gK
+u39Waj1SNWzwGg+Gy1Gl5f2AJoDTxznreCuFGj+Vfaczt/hlfgqpOdb9jsmdoE7t
+3DSWppA9dRHWwQSgE6J28rR4QySBcqyXS6IMykqaJn7Z26yNIaITLnHCZOSY8zhP
+ha7GFsN549EOCgECbrnPt9dmI2+hQE0RO0e7SOBNsIf5sz/i7urhwuj0CbOqhjc2
+X1AEVNFCVcb6HPi/AWefdFCRu0gaWQxn5g+9nkq5slEgvzCCiKYzaBIcr8qR6Hb4
+FaOPVPxO8vndRouq57Ws8XpAwbPttioFuCqF4u9K+tK/8e2/R8QgRYJsE3Cz/Fu8
++pZFpMnqbDEbK3DL3ss+1ed1sky+mDV8qXXeI33XW5hMFnk1JWshUjHNlQmE6ftC
+U0xSTMVUtwJhzH2zDp8lEdu7qi3EsNULOl68ozDr6soWAvCbHPeTdTOnFySGCleG
+/3TonsoZJs/sSPPJnxFQ1DtgQL6EbhIwa0ZwU4eKYVHZ9tjxuMX3teFzRvOrJjgs
++ywGlsIURtEckT5Y6nMAEQEAAYkCPAQYAQgAJhYhBOHazw/v6H2ee9HwGKtG7710
+AssCBQJfmfYBAhsMBQkHhh+AAAoJEKtG7710AssC8NcP/iDAcy1aZFvkA0EbZ85p
+i7/+ywtE/1wF4U4/9OuLcoskqGGnl1pJNPooMOSBCfreoTB8HimT0Fln0CoaOm4Q
+pScNq39JXmf4VxauqUJVARByP6zUfgYarqoaZNeuFF0S4AZJ2HhGzaQPjDz1uKVM
+PE6tQSgQkFzdZ9AtRA4vElTH6yRAgmepUsOihk0b0gUtVnwtRYZ8e0Qt3ie97a73
+DxLgAgedFRUbLRYiT0vNaYbainBsLWKpN/T8odwIg/smP0Khjp/ckV60cZTdBiPR
+szBTPJESMUTu0VPntc4gWwGsmhZJg/Tt/qP08XYo3VxNYBegyuWwNR66zDWvwvGH
+muMv5UchuDxp6Rt3JkIO4voMT1JSjWy9p8krkPEE4V6PxAagLjdZSkt92wVLiK5x
+y5gNrtPhU45YdRAKHr36OvJBJQ42CDaZ6nzrzghcIp9CZ7ANHrI+QLRM/csz+AGA
+szSp6S4mc1lnxxfbOhPPpebZPn0nIAXoZnnoVKdrxBVedPQHT59ZFvKTQ9Fs7gd3
+sYNuc7tJGFGC2CxBH4ANDpOQkc5q9JJ1HSGrXU3juxIiRgfA26Q22S9c71dXjElw
+Ri584QH+bL6kkYmm8xpKF6TVwhwu5xx/jBPrbWqFrtbvLNrnfPoapTihBfdIhkT6
+nmgawbBHA02D5xEqB5SU3WJu
+=eJNx
+-----END PGP PUBLIC KEY BLOCK-----
+```

+ 108 - 0
vendor/ramsey/collection/composer.json

@@ -0,0 +1,108 @@
+{
+    "name": "ramsey/collection",
+    "description": "A PHP library for representing and manipulating collections.",
+    "license": "MIT",
+    "type": "library",
+    "keywords": [
+        "array",
+        "collection",
+        "hash",
+        "map",
+        "queue",
+        "set"
+    ],
+    "authors": [
+        {
+            "name": "Ben Ramsey",
+            "email": "ben@benramsey.com",
+            "homepage": "https://benramsey.com"
+        }
+    ],
+    "require": {
+        "php": "^8.1"
+    },
+    "require-dev": {
+        "captainhook/plugin-composer": "^5.3",
+        "ergebnis/composer-normalize": "^2.45",
+        "fakerphp/faker": "^1.24",
+        "hamcrest/hamcrest-php": "^2.0",
+        "jangregor/phpstan-prophecy": "^2.1",
+        "mockery/mockery": "^1.6",
+        "php-parallel-lint/php-console-highlighter": "^1.0",
+        "php-parallel-lint/php-parallel-lint": "^1.4",
+        "phpspec/prophecy-phpunit": "^2.3",
+        "phpstan/extension-installer": "^1.4",
+        "phpstan/phpstan": "^2.1",
+        "phpstan/phpstan-mockery": "^2.0",
+        "phpstan/phpstan-phpunit": "^2.0",
+        "phpunit/phpunit": "^10.5",
+        "ramsey/coding-standard": "^2.3",
+        "ramsey/conventional-commits": "^1.6",
+        "roave/security-advisories": "dev-latest"
+    },
+    "prefer-stable": true,
+    "autoload": {
+        "psr-4": {
+            "Ramsey\\Collection\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Ramsey\\Collection\\Test\\": "tests/"
+        }
+    },
+    "config": {
+        "allow-plugins": {
+            "captainhook/plugin-composer": true,
+            "dealerdirect/phpcodesniffer-composer-installer": true,
+            "ergebnis/composer-normalize": true,
+            "phpstan/extension-installer": true
+        },
+        "sort-packages": true
+    },
+    "extra": {
+        "captainhook": {
+            "force-install": true
+        },
+        "ramsey/conventional-commits": {
+            "configFile": "conventional-commits.json"
+        }
+    },
+    "scripts": {
+        "dev:analyze": [
+            "@dev:analyze:phpstan"
+        ],
+        "dev:analyze:phpstan": "phpstan analyse --ansi --memory-limit=1G",
+        "dev:build:clean": "git clean -fX build/",
+        "dev:lint": [
+            "@dev:lint:syntax",
+            "@dev:lint:style"
+        ],
+        "dev:lint:fix": "phpcbf",
+        "dev:lint:style": "phpcs --colors",
+        "dev:lint:syntax": "parallel-lint --colors src/ tests/",
+        "dev:test": [
+            "@dev:lint",
+            "@dev:analyze",
+            "@dev:test:unit"
+        ],
+        "dev:test:coverage:ci": "phpunit --colors=always --coverage-text --coverage-clover build/coverage/clover.xml --coverage-cobertura build/coverage/cobertura.xml --coverage-crap4j build/coverage/crap4j.xml --coverage-xml build/coverage/coverage-xml --log-junit build/junit.xml",
+        "dev:test:coverage:html": "phpunit --colors=always --coverage-html build/coverage/coverage-html/",
+        "dev:test:unit": "phpunit --colors=always",
+        "test": "@dev:test"
+    },
+    "scripts-descriptions": {
+        "dev:analyze": "Runs all static analysis checks.",
+        "dev:analyze:phpstan": "Runs the PHPStan static analyzer.",
+        "dev:build:clean": "Cleans the build/ directory.",
+        "dev:lint": "Runs all linting checks.",
+        "dev:lint:fix": "Auto-fixes coding standards issues, if possible.",
+        "dev:lint:style": "Checks for coding standards issues.",
+        "dev:lint:syntax": "Checks for syntax errors.",
+        "dev:test": "Runs linting, static analysis, and unit tests.",
+        "dev:test:coverage:ci": "Runs unit tests and generates CI coverage reports.",
+        "dev:test:coverage:html": "Runs unit tests and generates HTML coverage report.",
+        "dev:test:unit": "Runs unit tests.",
+        "test": "Runs linting, static analysis, and unit tests."
+    }
+}

+ 171 - 0
vendor/ramsey/collection/src/AbstractArray.php

@@ -0,0 +1,171 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection;
+
+use ArrayIterator;
+use Traversable;
+
+use function count;
+
+/**
+ * This class provides a basic implementation of `ArrayInterface`, to minimize
+ * the effort required to implement this interface.
+ *
+ * @template T
+ * @implements ArrayInterface<T>
+ */
+abstract class AbstractArray implements ArrayInterface
+{
+    /**
+     * The items of this array.
+     *
+     * @var array<array-key, T>
+     */
+    protected array $data = [];
+
+    /**
+     * Constructs a new array object.
+     *
+     * @param array<array-key, T> $data The initial items to add to this array.
+     */
+    public function __construct(array $data = [])
+    {
+        // Invoke offsetSet() for each value added; in this way, subclasses
+        // may provide additional logic about values added to the array object.
+        foreach ($data as $key => $value) {
+            $this[$key] = $value;
+        }
+    }
+
+    /**
+     * Returns an iterator for this array.
+     *
+     * @link http://php.net/manual/en/iteratoraggregate.getiterator.php IteratorAggregate::getIterator()
+     *
+     * @return Traversable<array-key, T>
+     */
+    public function getIterator(): Traversable
+    {
+        return new ArrayIterator($this->data);
+    }
+
+    /**
+     * Returns `true` if the given offset exists in this array.
+     *
+     * @link http://php.net/manual/en/arrayaccess.offsetexists.php ArrayAccess::offsetExists()
+     *
+     * @param array-key $offset The offset to check.
+     */
+    public function offsetExists(mixed $offset): bool
+    {
+        return isset($this->data[$offset]);
+    }
+
+    /**
+     * Returns the value at the specified offset.
+     *
+     * @link http://php.net/manual/en/arrayaccess.offsetget.php ArrayAccess::offsetGet()
+     *
+     * @param array-key $offset The offset for which a value should be returned.
+     *
+     * @return T the value stored at the offset, or null if the offset
+     *     does not exist.
+     */
+    public function offsetGet(mixed $offset): mixed
+    {
+        return $this->data[$offset];
+    }
+
+    /**
+     * Sets the given value to the given offset in the array.
+     *
+     * @link http://php.net/manual/en/arrayaccess.offsetset.php ArrayAccess::offsetSet()
+     *
+     * @param array-key | null $offset The offset to set. If `null`, the value
+     *     may be set at a numerically-indexed offset.
+     * @param T $value The value to set at the given offset.
+     */
+    public function offsetSet(mixed $offset, mixed $value): void
+    {
+        if ($offset === null) {
+            $this->data[] = $value;
+        } else {
+            $this->data[$offset] = $value;
+        }
+    }
+
+    /**
+     * Removes the given offset and its value from the array.
+     *
+     * @link http://php.net/manual/en/arrayaccess.offsetunset.php ArrayAccess::offsetUnset()
+     *
+     * @param array-key $offset The offset to remove from the array.
+     */
+    public function offsetUnset(mixed $offset): void
+    {
+        unset($this->data[$offset]);
+    }
+
+    /**
+     * Returns data suitable for PHP serialization.
+     *
+     * @link https://www.php.net/manual/en/language.oop5.magic.php#language.oop5.magic.serialize
+     * @link https://www.php.net/serialize
+     *
+     * @return array<array-key, T>
+     */
+    public function __serialize(): array
+    {
+        return $this->data;
+    }
+
+    /**
+     * Adds unserialized data to the object.
+     *
+     * @param array<array-key, T> $data
+     */
+    public function __unserialize(array $data): void
+    {
+        $this->data = $data;
+    }
+
+    /**
+     * Returns the number of items in this array.
+     *
+     * @link http://php.net/manual/en/countable.count.php Countable::count()
+     */
+    public function count(): int
+    {
+        return count($this->data);
+    }
+
+    public function clear(): void
+    {
+        $this->data = [];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function toArray(): array
+    {
+        return $this->data;
+    }
+
+    public function isEmpty(): bool
+    {
+        return $this->data === [];
+    }
+}

+ 365 - 0
vendor/ramsey/collection/src/AbstractCollection.php

@@ -0,0 +1,365 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection;
+
+use Closure;
+use Ramsey\Collection\Exception\CollectionMismatchException;
+use Ramsey\Collection\Exception\InvalidArgumentException;
+use Ramsey\Collection\Exception\InvalidPropertyOrMethod;
+use Ramsey\Collection\Exception\NoSuchElementException;
+use Ramsey\Collection\Exception\UnsupportedOperationException;
+use Ramsey\Collection\Tool\TypeTrait;
+use Ramsey\Collection\Tool\ValueExtractorTrait;
+use Ramsey\Collection\Tool\ValueToStringTrait;
+
+use function array_filter;
+use function array_key_first;
+use function array_key_last;
+use function array_map;
+use function array_merge;
+use function array_reduce;
+use function array_search;
+use function array_udiff;
+use function array_uintersect;
+use function in_array;
+use function is_int;
+use function is_object;
+use function spl_object_id;
+use function sprintf;
+use function usort;
+
+/**
+ * This class provides a basic implementation of `CollectionInterface`, to
+ * minimize the effort required to implement this interface
+ *
+ * @template T
+ * @extends AbstractArray<T>
+ * @implements CollectionInterface<T>
+ */
+abstract class AbstractCollection extends AbstractArray implements CollectionInterface
+{
+    use TypeTrait;
+    use ValueToStringTrait;
+    use ValueExtractorTrait;
+
+    /**
+     * @throws InvalidArgumentException if $element is of the wrong type.
+     */
+    public function add(mixed $element): bool
+    {
+        $this[] = $element;
+
+        return true;
+    }
+
+    public function contains(mixed $element, bool $strict = true): bool
+    {
+        return in_array($element, $this->data, $strict);
+    }
+
+    /**
+     * @throws InvalidArgumentException if $element is of the wrong type.
+     */
+    public function offsetSet(mixed $offset, mixed $value): void
+    {
+        if ($this->checkType($this->getType(), $value) === false) {
+            throw new InvalidArgumentException(
+                'Value must be of type ' . $this->getType() . '; value is '
+                . $this->toolValueToString($value),
+            );
+        }
+
+        if ($offset === null) {
+            $this->data[] = $value;
+        } else {
+            $this->data[$offset] = $value;
+        }
+    }
+
+    public function remove(mixed $element): bool
+    {
+        if (($position = array_search($element, $this->data, true)) !== false) {
+            unset($this[$position]);
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
+     *     on the elements in this collection.
+     * @throws UnsupportedOperationException if unable to call column() on this
+     *     collection.
+     *
+     * @inheritDoc
+     */
+    public function column(string $propertyOrMethod): array
+    {
+        $temp = [];
+
+        foreach ($this->data as $item) {
+            $temp[] = $this->extractValue($item, $propertyOrMethod);
+        }
+
+        return $temp;
+    }
+
+    /**
+     * @return T
+     *
+     * @throws NoSuchElementException if this collection is empty.
+     */
+    public function first(): mixed
+    {
+        $firstIndex = array_key_first($this->data);
+
+        if ($firstIndex === null) {
+            throw new NoSuchElementException('Can\'t determine first item. Collection is empty');
+        }
+
+        return $this->data[$firstIndex];
+    }
+
+    /**
+     * @return T
+     *
+     * @throws NoSuchElementException if this collection is empty.
+     */
+    public function last(): mixed
+    {
+        $lastIndex = array_key_last($this->data);
+
+        if ($lastIndex === null) {
+            throw new NoSuchElementException('Can\'t determine last item. Collection is empty');
+        }
+
+        return $this->data[$lastIndex];
+    }
+
+    /**
+     * @return CollectionInterface<T>
+     *
+     * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
+     *     on the elements in this collection.
+     * @throws UnsupportedOperationException if unable to call sort() on this
+     *     collection.
+     */
+    public function sort(?string $propertyOrMethod = null, Sort $order = Sort::Ascending): CollectionInterface
+    {
+        $collection = clone $this;
+
+        usort(
+            $collection->data,
+            function (mixed $a, mixed $b) use ($propertyOrMethod, $order): int {
+                $aValue = $this->extractValue($a, $propertyOrMethod);
+                $bValue = $this->extractValue($b, $propertyOrMethod);
+
+                return ($aValue <=> $bValue) * ($order === Sort::Descending ? -1 : 1);
+            },
+        );
+
+        return $collection;
+    }
+
+    /**
+     * @param callable(T): bool $callback A callable to use for filtering elements.
+     *
+     * @return CollectionInterface<T>
+     */
+    public function filter(callable $callback): CollectionInterface
+    {
+        $collection = clone $this;
+        $collection->data = array_merge([], array_filter($collection->data, $callback));
+
+        return $collection;
+    }
+
+    /**
+     * @return CollectionInterface<T>
+     *
+     * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
+     *     on the elements in this collection.
+     * @throws UnsupportedOperationException if unable to call where() on this
+     *     collection.
+     */
+    public function where(?string $propertyOrMethod, mixed $value): CollectionInterface
+    {
+        return $this->filter(
+            fn (mixed $item): bool => $this->extractValue($item, $propertyOrMethod) === $value,
+        );
+    }
+
+    /**
+     * @param callable(T): TCallbackReturn $callback A callable to apply to each
+     *     item of the collection.
+     *
+     * @return CollectionInterface<TCallbackReturn>
+     *
+     * @template TCallbackReturn
+     */
+    public function map(callable $callback): CollectionInterface
+    {
+        return new Collection('mixed', array_map($callback, $this->data));
+    }
+
+    /**
+     * @param callable(TCarry, T): TCarry $callback A callable to apply to each
+     *     item of the collection to reduce it to a single value.
+     * @param TCarry $initial This is the initial value provided to the callback.
+     *
+     * @return TCarry
+     *
+     * @template TCarry
+     */
+    public function reduce(callable $callback, mixed $initial): mixed
+    {
+        return array_reduce($this->data, $callback, $initial);
+    }
+
+    /**
+     * @param CollectionInterface<T> $other The collection to check for divergent
+     *     items.
+     *
+     * @return CollectionInterface<T>
+     *
+     * @throws CollectionMismatchException if the compared collections are of
+     *     differing types.
+     */
+    public function diff(CollectionInterface $other): CollectionInterface
+    {
+        $this->compareCollectionTypes($other);
+
+        $diffAtoB = array_udiff($this->data, $other->toArray(), $this->getComparator());
+        $diffBtoA = array_udiff($other->toArray(), $this->data, $this->getComparator());
+
+        $collection = clone $this;
+        $collection->data = array_merge($diffAtoB, $diffBtoA);
+
+        return $collection;
+    }
+
+    /**
+     * @param CollectionInterface<T> $other The collection to check for
+     *     intersecting items.
+     *
+     * @return CollectionInterface<T>
+     *
+     * @throws CollectionMismatchException if the compared collections are of
+     *     differing types.
+     */
+    public function intersect(CollectionInterface $other): CollectionInterface
+    {
+        $this->compareCollectionTypes($other);
+
+        $collection = clone $this;
+        $collection->data = array_uintersect($this->data, $other->toArray(), $this->getComparator());
+
+        return $collection;
+    }
+
+    /**
+     * @param CollectionInterface<T> ...$collections The collections to merge.
+     *
+     * @return CollectionInterface<T>
+     *
+     * @throws CollectionMismatchException if unable to merge any of the given
+     *     collections or items within the given collections due to type
+     *     mismatch errors.
+     */
+    public function merge(CollectionInterface ...$collections): CollectionInterface
+    {
+        $mergedCollection = clone $this;
+
+        foreach ($collections as $index => $collection) {
+            if (!$collection instanceof static) {
+                throw new CollectionMismatchException(
+                    sprintf('Collection with index %d must be of type %s', $index, static::class),
+                );
+            }
+
+            // When using generics (Collection.php, Set.php, etc),
+            // we also need to make sure that the internal types match each other
+            if ($this->getUniformType($collection) !== $this->getUniformType($this)) {
+                throw new CollectionMismatchException(
+                    sprintf(
+                        'Collection items in collection with index %d must be of type %s',
+                        $index,
+                        $this->getType(),
+                    ),
+                );
+            }
+
+            foreach ($collection as $key => $value) {
+                if (is_int($key)) {
+                    $mergedCollection[] = $value;
+                } else {
+                    $mergedCollection[$key] = $value;
+                }
+            }
+        }
+
+        return $mergedCollection;
+    }
+
+    /**
+     * @param CollectionInterface<T> $other
+     *
+     * @throws CollectionMismatchException
+     */
+    private function compareCollectionTypes(CollectionInterface $other): void
+    {
+        if (!$other instanceof static) {
+            throw new CollectionMismatchException('Collection must be of type ' . static::class);
+        }
+
+        // When using generics (Collection.php, Set.php, etc),
+        // we also need to make sure that the internal types match each other
+        if ($this->getUniformType($other) !== $this->getUniformType($this)) {
+            throw new CollectionMismatchException('Collection items must be of type ' . $this->getType());
+        }
+    }
+
+    private function getComparator(): Closure
+    {
+        return function (mixed $a, mixed $b): int {
+            // If the two values are object, we convert them to unique scalars.
+            // If the collection contains mixed values (unlikely) where some are objects
+            // and some are not, we leave them as they are.
+            // The comparator should still work and the result of $a < $b should
+            // be consistent but unpredictable since not documented.
+            if (is_object($a) && is_object($b)) {
+                $a = spl_object_id($a);
+                $b = spl_object_id($b);
+            }
+
+            return $a === $b ? 0 : ($a < $b ? 1 : -1);
+        };
+    }
+
+    /**
+     * @param CollectionInterface<mixed> $collection
+     */
+    private function getUniformType(CollectionInterface $collection): string
+    {
+        return match ($collection->getType()) {
+            'integer' => 'int',
+            'boolean' => 'bool',
+            'double' => 'float',
+            default => $collection->getType(),
+        };
+    }
+}

+ 51 - 0
vendor/ramsey/collection/src/AbstractSet.php

@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection;
+
+/**
+ * This class contains the basic implementation of a collection that does not
+ * allow duplicated values (a set), to minimize the effort required to implement
+ * this specific type of collection.
+ *
+ * @template T
+ * @extends AbstractCollection<T>
+ */
+abstract class AbstractSet extends AbstractCollection
+{
+    public function add(mixed $element): bool
+    {
+        if ($this->contains($element)) {
+            return false;
+        }
+
+        // Call offsetSet() on the parent instead of add(), since calling
+        // parent::add() will invoke $this->offsetSet(), which will call
+        // $this->contains() a second time. This can cause performance issues
+        // with extremely large collections. For more information, see
+        // https://github.com/ramsey/collection/issues/68.
+        parent::offsetSet(null, $element);
+
+        return true;
+    }
+
+    public function offsetSet(mixed $offset, mixed $value): void
+    {
+        if ($this->contains($value)) {
+            return;
+        }
+
+        parent::offsetSet($offset, $value);
+    }
+}

+ 49 - 0
vendor/ramsey/collection/src/ArrayInterface.php

@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection;
+
+use ArrayAccess;
+use Countable;
+use IteratorAggregate;
+
+/**
+ * `ArrayInterface` provides traversable array functionality to data types.
+ *
+ * @template T
+ * @extends ArrayAccess<array-key, T>
+ * @extends IteratorAggregate<array-key, T>
+ */
+interface ArrayInterface extends
+    ArrayAccess,
+    Countable,
+    IteratorAggregate
+{
+    /**
+     * Removes all items from this array.
+     */
+    public function clear(): void;
+
+    /**
+     * Returns a native PHP array representation of this array object.
+     *
+     * @return array<array-key, T>
+     */
+    public function toArray(): array;
+
+    /**
+     * Returns `true` if this array is empty.
+     */
+    public function isEmpty(): bool;
+}

+ 95 - 0
vendor/ramsey/collection/src/Collection.php

@@ -0,0 +1,95 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection;
+
+/**
+ * A collection represents a group of objects.
+ *
+ * Each object in the collection is of a specific, defined type.
+ *
+ * This is a direct implementation of `CollectionInterface`, provided for
+ * the sake of convenience.
+ *
+ * Example usage:
+ *
+ * ```
+ * $collection = new \Ramsey\Collection\Collection('My\\Foo');
+ * $collection->add(new \My\Foo());
+ * $collection->add(new \My\Foo());
+ *
+ * foreach ($collection as $foo) {
+ *     // Do something with $foo
+ * }
+ * ```
+ *
+ * It is preferable to subclass `AbstractCollection` to create your own typed
+ * collections. For example:
+ *
+ * ```
+ * namespace My\Foo;
+ *
+ * class FooCollection extends \Ramsey\Collection\AbstractCollection
+ * {
+ *     public function getType()
+ *     {
+ *         return 'My\\Foo';
+ *     }
+ * }
+ * ```
+ *
+ * And then use it similarly to the earlier example:
+ *
+ * ```
+ * $fooCollection = new \My\Foo\FooCollection();
+ * $fooCollection->add(new \My\Foo());
+ * $fooCollection->add(new \My\Foo());
+ *
+ * foreach ($fooCollection as $foo) {
+ *     // Do something with $foo
+ * }
+ * ```
+ *
+ * The benefit with this approach is that you may do type-checking on the
+ * collection object:
+ *
+ * ```
+ * if ($collection instanceof \My\Foo\FooCollection) {
+ *     // the collection is a collection of My\Foo objects
+ * }
+ * ```
+ *
+ * @template T
+ * @extends AbstractCollection<T>
+ */
+class Collection extends AbstractCollection
+{
+    /**
+     * Constructs a collection object of the specified type, optionally with the
+     * specified data.
+     *
+     * @param string $collectionType The type or class name associated with this
+     *     collection.
+     * @param array<array-key, T> $data The initial items to store in the collection.
+     */
+    public function __construct(private readonly string $collectionType, array $data = [])
+    {
+        parent::__construct($data);
+    }
+
+    public function getType(): string
+    {
+        return $this->collectionType;
+    }
+}

+ 253 - 0
vendor/ramsey/collection/src/CollectionInterface.php

@@ -0,0 +1,253 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection;
+
+use Ramsey\Collection\Exception\CollectionMismatchException;
+use Ramsey\Collection\Exception\InvalidArgumentException;
+use Ramsey\Collection\Exception\InvalidPropertyOrMethod;
+use Ramsey\Collection\Exception\NoSuchElementException;
+use Ramsey\Collection\Exception\UnsupportedOperationException;
+
+/**
+ * A collection represents a group of values, known as its elements.
+ *
+ * Some collections allow duplicate elements and others do not. Some are ordered
+ * and others unordered.
+ *
+ * @template T
+ * @extends ArrayInterface<T>
+ */
+interface CollectionInterface extends ArrayInterface
+{
+    /**
+     * Ensures that this collection contains the specified element (optional
+     * operation).
+     *
+     * Returns `true` if this collection changed as a result of the call.
+     * (Returns `false` if this collection does not permit duplicates and
+     * already contains the specified element.)
+     *
+     * Collections that support this operation may place limitations on what
+     * elements may be added to this collection. In particular, some
+     * collections will refuse to add `null` elements, and others will impose
+     * restrictions on the type of elements that may be added. Collection
+     * classes should clearly specify in their documentation any restrictions
+     * on what elements may be added.
+     *
+     * If a collection refuses to add a particular element for any reason other
+     * than that it already contains the element, it must throw an exception
+     * (rather than returning `false`). This preserves the invariant that a
+     * collection always contains the specified element after this call returns.
+     *
+     * @param T $element The element to add to the collection.
+     *
+     * @return bool `true` if this collection changed as a result of the call.
+     *
+     * @throws InvalidArgumentException if the collection refuses to add the
+     *     $element for any reason other than that it already contains the element.
+     */
+    public function add(mixed $element): bool;
+
+    /**
+     * Returns `true` if this collection contains the specified element.
+     *
+     * @param T $element The element to check whether the collection contains.
+     * @param bool $strict Whether to perform a strict type check on the value.
+     */
+    public function contains(mixed $element, bool $strict = true): bool;
+
+    /**
+     * Returns the type associated with this collection.
+     */
+    public function getType(): string;
+
+    /**
+     * Removes a single instance of the specified element from this collection,
+     * if it is present.
+     *
+     * @param T $element The element to remove from the collection.
+     *
+     * @return bool `true` if an element was removed as a result of this call.
+     */
+    public function remove(mixed $element): bool;
+
+    /**
+     * Returns the values from the given property, method, or array key.
+     *
+     * @param string $propertyOrMethod The name of the property, method, or
+     *     array key to evaluate and return.
+     *
+     * @return list<mixed>
+     *
+     * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
+     *     on the elements in this collection.
+     * @throws UnsupportedOperationException if unable to call column() on this
+     *     collection.
+     */
+    public function column(string $propertyOrMethod): array;
+
+    /**
+     * Returns the first item of the collection.
+     *
+     * @return T
+     *
+     * @throws NoSuchElementException if this collection is empty.
+     */
+    public function first(): mixed;
+
+    /**
+     * Returns the last item of the collection.
+     *
+     * @return T
+     *
+     * @throws NoSuchElementException if this collection is empty.
+     */
+    public function last(): mixed;
+
+    /**
+     * Sort the collection by a property, method, or array key with the given
+     * sort order.
+     *
+     * If $propertyOrMethod is `null`, this will sort by comparing each element.
+     *
+     * This will always leave the original collection untouched and will return
+     * a new one.
+     *
+     * @param string | null $propertyOrMethod The property, method, or array key
+     *     to sort by.
+     * @param Sort $order The sort order for the resulting collection.
+     *
+     * @return CollectionInterface<T>
+     *
+     * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
+     *     on the elements in this collection.
+     * @throws UnsupportedOperationException if unable to call sort() on this
+     *     collection.
+     */
+    public function sort(?string $propertyOrMethod = null, Sort $order = Sort::Ascending): self;
+
+    /**
+     * Filter out items of the collection which don't match the criteria of
+     * given callback.
+     *
+     * This will always leave the original collection untouched and will return
+     * a new one.
+     *
+     * See the {@link http://php.net/manual/en/function.array-filter.php PHP array_filter() documentation}
+     * for examples of how the `$callback` parameter works.
+     *
+     * @param callable(T): bool $callback A callable to use for filtering elements.
+     *
+     * @return CollectionInterface<T>
+     */
+    public function filter(callable $callback): self;
+
+    /**
+     * Create a new collection where the result of the given property, method,
+     * or array key of each item in the collection equals the given value.
+     *
+     * This will always leave the original collection untouched and will return
+     * a new one.
+     *
+     * @param string | null $propertyOrMethod The property, method, or array key
+     *     to evaluate. If `null`, the element itself is compared to $value.
+     * @param mixed $value The value to match.
+     *
+     * @return CollectionInterface<T>
+     *
+     * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
+     *     on the elements in this collection.
+     * @throws UnsupportedOperationException if unable to call where() on this
+     *     collection.
+     */
+    public function where(?string $propertyOrMethod, mixed $value): self;
+
+    /**
+     * Apply a given callback method on each item of the collection.
+     *
+     * This will always leave the original collection untouched. The new
+     * collection is created by mapping the callback to each item of the
+     * original collection.
+     *
+     * See the {@link http://php.net/manual/en/function.array-map.php PHP array_map() documentation}
+     * for examples of how the `$callback` parameter works.
+     *
+     * @param callable(T): TCallbackReturn $callback A callable to apply to each
+     *     item of the collection.
+     *
+     * @return CollectionInterface<TCallbackReturn>
+     *
+     * @template TCallbackReturn
+     */
+    public function map(callable $callback): self;
+
+    /**
+     * Apply a given callback method on each item of the collection
+     * to reduce it to a single value.
+     *
+     * See the {@link http://php.net/manual/en/function.array-reduce.php PHP array_reduce() documentation}
+     * for examples of how the `$callback` and `$initial` parameters work.
+     *
+     * @param callable(TCarry, T): TCarry $callback A callable to apply to each
+     *     item of the collection to reduce it to a single value.
+     * @param TCarry $initial This is the initial value provided to the callback.
+     *
+     * @return TCarry
+     *
+     * @template TCarry
+     */
+    public function reduce(callable $callback, mixed $initial): mixed;
+
+    /**
+     * Create a new collection with divergent items between current and given
+     * collection.
+     *
+     * @param CollectionInterface<T> $other The collection to check for divergent
+     *     items.
+     *
+     * @return CollectionInterface<T>
+     *
+     * @throws CollectionMismatchException if the compared collections are of
+     *     differing types.
+     */
+    public function diff(CollectionInterface $other): self;
+
+    /**
+     * Create a new collection with intersecting item between current and given
+     * collection.
+     *
+     * @param CollectionInterface<T> $other The collection to check for
+     *     intersecting items.
+     *
+     * @return CollectionInterface<T>
+     *
+     * @throws CollectionMismatchException if the compared collections are of
+     *     differing types.
+     */
+    public function intersect(CollectionInterface $other): self;
+
+    /**
+     * Merge current items and items of given collections into a new one.
+     *
+     * @param CollectionInterface<T> ...$collections The collections to merge.
+     *
+     * @return CollectionInterface<T>
+     *
+     * @throws CollectionMismatchException if unable to merge any of the given
+     *     collections or items within the given collections due to type
+     *     mismatch errors.
+     */
+    public function merge(CollectionInterface ...$collections): self;
+}

+ 166 - 0
vendor/ramsey/collection/src/DoubleEndedQueue.php

@@ -0,0 +1,166 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection;
+
+use Ramsey\Collection\Exception\InvalidArgumentException;
+use Ramsey\Collection\Exception\NoSuchElementException;
+
+use function array_key_last;
+use function array_pop;
+use function array_unshift;
+
+/**
+ * This class provides a basic implementation of `DoubleEndedQueueInterface`, to
+ * minimize the effort required to implement this interface.
+ *
+ * @template T
+ * @extends Queue<T>
+ * @implements DoubleEndedQueueInterface<T>
+ */
+class DoubleEndedQueue extends Queue implements DoubleEndedQueueInterface
+{
+    /**
+     * Constructs a double-ended queue (dequeue) object of the specified type,
+     * optionally with the specified data.
+     *
+     * @param string $queueType The type or class name associated with this dequeue.
+     * @param array<array-key, T> $data The initial items to store in the dequeue.
+     */
+    public function __construct(private readonly string $queueType, array $data = [])
+    {
+        parent::__construct($this->queueType, $data);
+    }
+
+    /**
+     * @throws InvalidArgumentException if $element is of the wrong type
+     */
+    public function addFirst(mixed $element): bool
+    {
+        if ($this->checkType($this->getType(), $element) === false) {
+            throw new InvalidArgumentException(
+                'Value must be of type ' . $this->getType() . '; value is '
+                . $this->toolValueToString($element),
+            );
+        }
+
+        array_unshift($this->data, $element);
+
+        return true;
+    }
+
+    /**
+     * @throws InvalidArgumentException if $element is of the wrong type
+     */
+    public function addLast(mixed $element): bool
+    {
+        return $this->add($element);
+    }
+
+    public function offerFirst(mixed $element): bool
+    {
+        try {
+            return $this->addFirst($element);
+        } catch (InvalidArgumentException) {
+            return false;
+        }
+    }
+
+    public function offerLast(mixed $element): bool
+    {
+        return $this->offer($element);
+    }
+
+    /**
+     * @return T the first element in this queue.
+     *
+     * @throws NoSuchElementException if the queue is empty
+     */
+    public function removeFirst(): mixed
+    {
+        return $this->remove();
+    }
+
+    /**
+     * @return T the last element in this queue.
+     *
+     * @throws NoSuchElementException if this queue is empty.
+     */
+    public function removeLast(): mixed
+    {
+        return $this->pollLast() ?? throw new NoSuchElementException(
+            'Can\'t return element from Queue. Queue is empty.',
+        );
+    }
+
+    /**
+     * @return T | null the head of this queue, or `null` if this queue is empty.
+     */
+    public function pollFirst(): mixed
+    {
+        return $this->poll();
+    }
+
+    /**
+     * @return T | null the tail of this queue, or `null` if this queue is empty.
+     */
+    public function pollLast(): mixed
+    {
+        return array_pop($this->data);
+    }
+
+    /**
+     * @return T the head of this queue.
+     *
+     * @throws NoSuchElementException if this queue is empty.
+     */
+    public function firstElement(): mixed
+    {
+        return $this->element();
+    }
+
+    /**
+     * @return T the tail of this queue.
+     *
+     * @throws NoSuchElementException if this queue is empty.
+     */
+    public function lastElement(): mixed
+    {
+        return $this->peekLast() ?? throw new NoSuchElementException(
+            'Can\'t return element from Queue. Queue is empty.',
+        );
+    }
+
+    /**
+     * @return T | null the head of this queue, or `null` if this queue is empty.
+     */
+    public function peekFirst(): mixed
+    {
+        return $this->peek();
+    }
+
+    /**
+     * @return T | null the tail of this queue, or `null` if this queue is empty.
+     */
+    public function peekLast(): mixed
+    {
+        $lastIndex = array_key_last($this->data);
+
+        if ($lastIndex === null) {
+            return null;
+        }
+
+        return $this->data[$lastIndex];
+    }
+}

+ 313 - 0
vendor/ramsey/collection/src/DoubleEndedQueueInterface.php

@@ -0,0 +1,313 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection;
+
+use Ramsey\Collection\Exception\NoSuchElementException;
+use RuntimeException;
+
+/**
+ * A linear collection that supports element insertion and removal at both ends.
+ *
+ * Most `DoubleEndedQueueInterface` implementations place no fixed limits on the
+ * number of elements they may contain, but this interface supports
+ * capacity-restricted double-ended queues as well as those with no fixed size
+ * limit.
+ *
+ * This interface defines methods to access the elements at both ends of the
+ * double-ended queue. Methods are provided to insert, remove, and examine the
+ * element. Each of these methods exists in two forms: one throws an exception
+ * if the operation fails, the other returns a special value (either `null` or
+ * `false`, depending on the operation). The latter form of the insert operation
+ * is designed specifically for use with capacity-restricted implementations; in
+ * most implementations, insert operations cannot fail.
+ *
+ * The twelve methods described above are summarized in the following table:
+ *
+ * <table>
+ * <caption>Summary of DoubleEndedQueueInterface methods</caption>
+ * <thead>
+ * <tr>
+ * <th></th>
+ * <th colspan=2>First Element (Head)</th>
+ * <th colspan=2>Last Element (Tail)</th>
+ * </tr>
+ * <tr>
+ * <td></td>
+ * <td><em>Throws exception</em></td>
+ * <td><em>Special value</em></td>
+ * <td><em>Throws exception</em></td>
+ * <td><em>Special value</em></td>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <th>Insert</th>
+ * <td><code>addFirst()</code></td>
+ * <td><code>offerFirst()</code></td>
+ * <td><code>addLast()</code></td>
+ * <td><code>offerLast()</code></td>
+ * </tr>
+ * <tr>
+ * <th>Remove</th>
+ * <td><code>removeFirst()</code></td>
+ * <td><code>pollFirst()</code></td>
+ * <td><code>removeLast()</code></td>
+ * <td><code>pollLast()</code></td>
+ * </tr>
+ * <tr>
+ * <th>Examine</th>
+ * <td><code>firstElement()</code></td>
+ * <td><code>peekFirst()</code></td>
+ * <td><code>lastElement()</code></td>
+ * <td><code>peekLast()</code></td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * This interface extends the `QueueInterface`. When a double-ended queue is
+ * used as a queue, FIFO (first-in-first-out) behavior results. Elements are
+ * added at the end of the double-ended queue and removed from the beginning.
+ * The methods inherited from the `QueueInterface` are precisely equivalent to
+ * `DoubleEndedQueueInterface` methods as indicated in the following table:
+ *
+ * <table>
+ * <caption>Comparison of QueueInterface and DoubleEndedQueueInterface methods</caption>
+ * <thead>
+ * <tr>
+ * <th>QueueInterface Method</th>
+ * <th>DoubleEndedQueueInterface Method</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td><code>add()</code></td>
+ * <td><code>addLast()</code></td>
+ * </tr>
+ * <tr>
+ * <td><code>offer()</code></td>
+ * <td><code>offerLast()</code></td>
+ * </tr>
+ * <tr>
+ * <td><code>remove()</code></td>
+ * <td><code>removeFirst()</code></td>
+ * </tr>
+ * <tr>
+ * <td><code>poll()</code></td>
+ * <td><code>pollFirst()</code></td>
+ * </tr>
+ * <tr>
+ * <td><code>element()</code></td>
+ * <td><code>firstElement()</code></td>
+ * </tr>
+ * <tr>
+ * <td><code>peek()</code></td>
+ * <td><code>peekFirst()</code></td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * Double-ended queues can also be used as LIFO (last-in-first-out) stacks. When
+ * a double-ended queue is used as a stack, elements are pushed and popped from
+ * the beginning of the double-ended queue. Stack concepts are precisely
+ * equivalent to `DoubleEndedQueueInterface` methods as indicated in the table
+ * below:
+ *
+ * <table>
+ * <caption>Comparison of stack concepts and DoubleEndedQueueInterface methods</caption>
+ * <thead>
+ * <tr>
+ * <th>Stack concept</th>
+ * <th>DoubleEndedQueueInterface Method</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td><em>push</em></td>
+ * <td><code>addFirst()</code></td>
+ * </tr>
+ * <tr>
+ * <td><em>pop</em></td>
+ * <td><code>removeFirst()</code></td>
+ * </tr>
+ * <tr>
+ * <td><em>peek</em></td>
+ * <td><code>peekFirst()</code></td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * Note that the `peek()` method works equally well when a double-ended queue is
+ * used as a queue or a stack; in either case, elements are drawn from the
+ * beginning of the double-ended queue.
+ *
+ * While `DoubleEndedQueueInterface` implementations are not strictly required
+ * to prohibit the insertion of `null` elements, they are strongly encouraged to
+ * do so. Users of any `DoubleEndedQueueInterface` implementations that do allow
+ * `null` elements are strongly encouraged *not* to take advantage of the
+ * ability to insert nulls. This is so because `null` is used as a special
+ * return value by various methods to indicated that the double-ended queue is
+ * empty.
+ *
+ * @template T
+ * @extends QueueInterface<T>
+ */
+interface DoubleEndedQueueInterface extends QueueInterface
+{
+    /**
+     * Inserts the specified element at the front of this queue if it is
+     * possible to do so immediately without violating capacity restrictions.
+     *
+     * When using a capacity-restricted double-ended queue, it is generally
+     * preferable to use the `offerFirst()` method.
+     *
+     * @param T $element The element to add to the front of this queue.
+     *
+     * @return bool `true` if this queue changed as a result of the call.
+     *
+     * @throws RuntimeException if a queue refuses to add a particular element
+     *     for any reason other than that it already contains the element.
+     *     Implementations should use a more-specific exception that extends
+     *     `\RuntimeException`.
+     */
+    public function addFirst(mixed $element): bool;
+
+    /**
+     * Inserts the specified element at the end of this queue if it is possible
+     * to do so immediately without violating capacity restrictions.
+     *
+     * When using a capacity-restricted double-ended queue, it is generally
+     * preferable to use the `offerLast()` method.
+     *
+     * This method is equivalent to `add()`.
+     *
+     * @param T $element The element to add to the end of this queue.
+     *
+     * @return bool `true` if this queue changed as a result of the call.
+     *
+     * @throws RuntimeException if a queue refuses to add a particular element
+     *     for any reason other than that it already contains the element.
+     *     Implementations should use a more-specific exception that extends
+     *     `\RuntimeException`.
+     */
+    public function addLast(mixed $element): bool;
+
+    /**
+     * Inserts the specified element at the front of this queue if it is
+     * possible to do so immediately without violating capacity restrictions.
+     *
+     * When using a capacity-restricted queue, this method is generally
+     * preferable to `addFirst()`, which can fail to insert an element only by
+     * throwing an exception.
+     *
+     * @param T $element The element to add to the front of this queue.
+     *
+     * @return bool `true` if the element was added to this queue, else `false`.
+     */
+    public function offerFirst(mixed $element): bool;
+
+    /**
+     * Inserts the specified element at the end of this queue if it is possible
+     * to do so immediately without violating capacity restrictions.
+     *
+     * When using a capacity-restricted queue, this method is generally
+     * preferable to `addLast()` which can fail to insert an element only by
+     * throwing an exception.
+     *
+     * @param T $element The element to add to the end of this queue.
+     *
+     * @return bool `true` if the element was added to this queue, else `false`.
+     */
+    public function offerLast(mixed $element): bool;
+
+    /**
+     * Retrieves and removes the head of this queue.
+     *
+     * This method differs from `pollFirst()` only in that it throws an
+     * exception if this queue is empty.
+     *
+     * @return T the first element in this queue.
+     *
+     * @throws NoSuchElementException if this queue is empty.
+     */
+    public function removeFirst(): mixed;
+
+    /**
+     * Retrieves and removes the tail of this queue.
+     *
+     * This method differs from `pollLast()` only in that it throws an exception
+     * if this queue is empty.
+     *
+     * @return T the last element in this queue.
+     *
+     * @throws NoSuchElementException if this queue is empty.
+     */
+    public function removeLast(): mixed;
+
+    /**
+     * Retrieves and removes the head of this queue, or returns `null` if this
+     * queue is empty.
+     *
+     * @return T | null the head of this queue, or `null` if this queue is empty.
+     */
+    public function pollFirst(): mixed;
+
+    /**
+     * Retrieves and removes the tail of this queue, or returns `null` if this
+     * queue is empty.
+     *
+     * @return T | null the tail of this queue, or `null` if this queue is empty.
+     */
+    public function pollLast(): mixed;
+
+    /**
+     * Retrieves, but does not remove, the head of this queue.
+     *
+     * This method differs from `peekFirst()` only in that it throws an
+     * exception if this queue is empty.
+     *
+     * @return T the head of this queue.
+     *
+     * @throws NoSuchElementException if this queue is empty.
+     */
+    public function firstElement(): mixed;
+
+    /**
+     * Retrieves, but does not remove, the tail of this queue.
+     *
+     * This method differs from `peekLast()` only in that it throws an exception
+     * if this queue is empty.
+     *
+     * @return T the tail of this queue.
+     *
+     * @throws NoSuchElementException if this queue is empty.
+     */
+    public function lastElement(): mixed;
+
+    /**
+     * Retrieves, but does not remove, the head of this queue, or returns `null`
+     * if this queue is empty.
+     *
+     * @return T | null the head of this queue, or `null` if this queue is empty.
+     */
+    public function peekFirst(): mixed;
+
+    /**
+     * Retrieves, but does not remove, the tail of this queue, or returns `null`
+     * if this queue is empty.
+     *
+     * @return T | null the tail of this queue, or `null` if this queue is empty.
+     */
+    public function peekLast(): mixed;
+}

+ 21 - 0
vendor/ramsey/collection/src/Exception/CollectionException.php

@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection\Exception;
+
+use Throwable;
+
+interface CollectionException extends Throwable
+{
+}

+ 24 - 0
vendor/ramsey/collection/src/Exception/CollectionMismatchException.php

@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection\Exception;
+
+use RuntimeException;
+
+/**
+ * Thrown when attempting to operate on collections of differing types.
+ */
+class CollectionMismatchException extends RuntimeException implements CollectionException
+{
+}

+ 24 - 0
vendor/ramsey/collection/src/Exception/InvalidArgumentException.php

@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection\Exception;
+
+use InvalidArgumentException as PhpInvalidArgumentException;
+
+/**
+ * Thrown to indicate an argument is not of the expected type.
+ */
+class InvalidArgumentException extends PhpInvalidArgumentException implements CollectionException
+{
+}

+ 26 - 0
vendor/ramsey/collection/src/Exception/InvalidPropertyOrMethod.php

@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection\Exception;
+
+use RuntimeException;
+
+/**
+ * Thrown when attempting to evaluate a property, method, or array key
+ * that doesn't exist on an element or cannot otherwise be evaluated in the
+ * current context.
+ */
+class InvalidPropertyOrMethod extends RuntimeException implements CollectionException
+{
+}

+ 24 - 0
vendor/ramsey/collection/src/Exception/NoSuchElementException.php

@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection\Exception;
+
+use RuntimeException;
+
+/**
+ * Thrown when attempting to access an element that does not exist.
+ */
+class NoSuchElementException extends RuntimeException implements CollectionException
+{
+}

+ 24 - 0
vendor/ramsey/collection/src/Exception/OutOfBoundsException.php

@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection\Exception;
+
+use OutOfBoundsException as PhpOutOfBoundsException;
+
+/**
+ * Thrown when attempting to access an element out of the range of the collection.
+ */
+class OutOfBoundsException extends PhpOutOfBoundsException implements CollectionException
+{
+}

+ 24 - 0
vendor/ramsey/collection/src/Exception/UnsupportedOperationException.php

@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection\Exception;
+
+use RuntimeException;
+
+/**
+ * Thrown to indicate that the requested operation is not supported.
+ */
+class UnsupportedOperationException extends RuntimeException implements CollectionException
+{
+}

+ 24 - 0
vendor/ramsey/collection/src/GenericArray.php

@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection;
+
+/**
+ * `GenericArray` represents a standard array object.
+ *
+ * @extends AbstractArray<mixed>
+ */
+class GenericArray extends AbstractArray
+{
+}

+ 205 - 0
vendor/ramsey/collection/src/Map/AbstractMap.php

@@ -0,0 +1,205 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection\Map;
+
+use Ramsey\Collection\AbstractArray;
+use Ramsey\Collection\Exception\InvalidArgumentException;
+use Traversable;
+
+use function array_key_exists;
+use function array_keys;
+use function in_array;
+use function var_export;
+
+/**
+ * This class provides a basic implementation of `MapInterface`, to minimize the
+ * effort required to implement this interface.
+ *
+ * @template K of array-key
+ * @template T
+ * @extends AbstractArray<T>
+ * @implements MapInterface<K, T>
+ */
+abstract class AbstractMap extends AbstractArray implements MapInterface
+{
+    /**
+     * @param array<K, T> $data The initial items to add to this map.
+     */
+    public function __construct(array $data = [])
+    {
+        parent::__construct($data);
+    }
+
+    /**
+     * @return Traversable<K, T>
+     */
+    public function getIterator(): Traversable
+    {
+        return parent::getIterator();
+    }
+
+    /**
+     * @param K $offset The offset to set
+     * @param T $value The value to set at the given offset.
+     *
+     * @inheritDoc
+     */
+    public function offsetSet(mixed $offset, mixed $value): void
+    {
+        if ($offset === null) {
+            throw new InvalidArgumentException(
+                'Map elements are key/value pairs; a key must be provided for '
+                . 'value ' . var_export($value, true),
+            );
+        }
+
+        $this->data[$offset] = $value;
+    }
+
+    public function containsKey(int | string $key): bool
+    {
+        return array_key_exists($key, $this->data);
+    }
+
+    public function containsValue(mixed $value): bool
+    {
+        return in_array($value, $this->data, true);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function keys(): array
+    {
+        /** @var list<K> */
+        return array_keys($this->data);
+    }
+
+    /**
+     * @param K $key The key to return from the map.
+     * @param T | null $defaultValue The default value to use if `$key` is not found.
+     *
+     * @return T | null the value or `null` if the key could not be found.
+     */
+    public function get(int | string $key, mixed $defaultValue = null): mixed
+    {
+        return $this[$key] ?? $defaultValue;
+    }
+
+    /**
+     * @param K $key The key to put or replace in the map.
+     * @param T $value The value to store at `$key`.
+     *
+     * @return T | null the previous value associated with key, or `null` if
+     *     there was no mapping for `$key`.
+     */
+    public function put(int | string $key, mixed $value): mixed
+    {
+        $previousValue = $this->get($key);
+        $this[$key] = $value;
+
+        return $previousValue;
+    }
+
+    /**
+     * @param K $key The key to put in the map.
+     * @param T $value The value to store at `$key`.
+     *
+     * @return T | null the previous value associated with key, or `null` if
+     *     there was no mapping for `$key`.
+     */
+    public function putIfAbsent(int | string $key, mixed $value): mixed
+    {
+        $currentValue = $this->get($key);
+
+        if ($currentValue === null) {
+            $this[$key] = $value;
+        }
+
+        return $currentValue;
+    }
+
+    /**
+     * @param K $key The key to remove from the map.
+     *
+     * @return T | null the previous value associated with key, or `null` if
+     *     there was no mapping for `$key`.
+     */
+    public function remove(int | string $key): mixed
+    {
+        $previousValue = $this->get($key);
+        unset($this[$key]);
+
+        return $previousValue;
+    }
+
+    public function removeIf(int | string $key, mixed $value): bool
+    {
+        if ($this->get($key) === $value) {
+            unset($this[$key]);
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * @param K $key The key to replace.
+     * @param T $value The value to set at `$key`.
+     *
+     * @return T | null the previous value associated with key, or `null` if
+     *     there was no mapping for `$key`.
+     */
+    public function replace(int | string $key, mixed $value): mixed
+    {
+        $currentValue = $this->get($key);
+
+        if ($this->containsKey($key)) {
+            $this[$key] = $value;
+        }
+
+        return $currentValue;
+    }
+
+    public function replaceIf(int | string $key, mixed $oldValue, mixed $newValue): bool
+    {
+        if ($this->get($key) === $oldValue) {
+            $this[$key] = $newValue;
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * @return array<K, T>
+     */
+    public function __serialize(): array
+    {
+        /** @var array<K, T> */
+        return parent::__serialize();
+    }
+
+    /**
+     * @return array<K, T>
+     */
+    public function toArray(): array
+    {
+        /** @var array<K, T> */
+        return parent::toArray();
+    }
+}

+ 59 - 0
vendor/ramsey/collection/src/Map/AbstractTypedMap.php

@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection\Map;
+
+use Ramsey\Collection\Exception\InvalidArgumentException;
+use Ramsey\Collection\Tool\TypeTrait;
+use Ramsey\Collection\Tool\ValueToStringTrait;
+
+/**
+ * This class provides a basic implementation of `TypedMapInterface`, to
+ * minimize the effort required to implement this interface.
+ *
+ * @template K of array-key
+ * @template T
+ * @extends AbstractMap<K, T>
+ * @implements TypedMapInterface<K, T>
+ */
+abstract class AbstractTypedMap extends AbstractMap implements TypedMapInterface
+{
+    use TypeTrait;
+    use ValueToStringTrait;
+
+    /**
+     * @param K $offset
+     * @param T $value
+     *
+     * @inheritDoc
+     */
+    public function offsetSet(mixed $offset, mixed $value): void
+    {
+        if ($this->checkType($this->getKeyType(), $offset) === false) {
+            throw new InvalidArgumentException(
+                'Key must be of type ' . $this->getKeyType() . '; key is '
+                . $this->toolValueToString($offset),
+            );
+        }
+
+        if ($this->checkType($this->getValueType(), $value) === false) {
+            throw new InvalidArgumentException(
+                'Value must be of type ' . $this->getValueType() . '; value is '
+                . $this->toolValueToString($value),
+            );
+        }
+
+        parent::offsetSet($offset, $value);
+    }
+}

+ 24 - 0
vendor/ramsey/collection/src/Map/AssociativeArrayMap.php

@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection\Map;
+
+/**
+ * `AssociativeArrayMap` represents a standard associative array object.
+ *
+ * @extends AbstractMap<string, mixed>
+ */
+class AssociativeArrayMap extends AbstractMap
+{
+}

+ 142 - 0
vendor/ramsey/collection/src/Map/MapInterface.php

@@ -0,0 +1,142 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection\Map;
+
+use Ramsey\Collection\ArrayInterface;
+
+/**
+ * An object that maps keys to values.
+ *
+ * A map cannot contain duplicate keys; each key can map to at most one value.
+ *
+ * @template K of array-key
+ * @template T
+ * @extends ArrayInterface<T>
+ */
+interface MapInterface extends ArrayInterface
+{
+    /**
+     * Returns `true` if this map contains a mapping for the specified key.
+     *
+     * @param K $key The key to check in the map.
+     */
+    public function containsKey(int | string $key): bool;
+
+    /**
+     * Returns `true` if this map maps one or more keys to the specified value.
+     *
+     * This performs a strict type check on the value.
+     *
+     * @param T $value The value to check in the map.
+     */
+    public function containsValue(mixed $value): bool;
+
+    /**
+     * Return an array of the keys contained in this map.
+     *
+     * @return list<K>
+     */
+    public function keys(): array;
+
+    /**
+     * Returns the value to which the specified key is mapped, `null` if this
+     * map contains no mapping for the key, or (optionally) `$defaultValue` if
+     * this map contains no mapping for the key.
+     *
+     * @param K $key The key to return from the map.
+     * @param T | null $defaultValue The default value to use if `$key` is not found.
+     *
+     * @return T | null the value or `null` if the key could not be found.
+     */
+    public function get(int | string $key, mixed $defaultValue = null): mixed;
+
+    /**
+     * Associates the specified value with the specified key in this map.
+     *
+     * If the map previously contained a mapping for the key, the old value is
+     * replaced by the specified value.
+     *
+     * @param K $key The key to put or replace in the map.
+     * @param T $value The value to store at `$key`.
+     *
+     * @return T | null the previous value associated with key, or `null` if
+     *     there was no mapping for `$key`.
+     */
+    public function put(int | string $key, mixed $value): mixed;
+
+    /**
+     * Associates the specified value with the specified key in this map only if
+     * it is not already set.
+     *
+     * If there is already a value associated with `$key`, this returns that
+     * value without replacing it.
+     *
+     * @param K $key The key to put in the map.
+     * @param T $value The value to store at `$key`.
+     *
+     * @return T | null the previous value associated with key, or `null` if
+     *     there was no mapping for `$key`.
+     */
+    public function putIfAbsent(int | string $key, mixed $value): mixed;
+
+    /**
+     * Removes the mapping for a key from this map if it is present.
+     *
+     * @param K $key The key to remove from the map.
+     *
+     * @return T | null the previous value associated with key, or `null` if
+     *     there was no mapping for `$key`.
+     */
+    public function remove(int | string $key): mixed;
+
+    /**
+     * Removes the entry for the specified key only if it is currently mapped to
+     * the specified value.
+     *
+     * This performs a strict type check on the value.
+     *
+     * @param K $key The key to remove from the map.
+     * @param T $value The value to match.
+     *
+     * @return bool true if the value was removed.
+     */
+    public function removeIf(int | string $key, mixed $value): bool;
+
+    /**
+     * Replaces the entry for the specified key only if it is currently mapped
+     * to some value.
+     *
+     * @param K $key The key to replace.
+     * @param T $value The value to set at `$key`.
+     *
+     * @return T | null the previous value associated with key, or `null` if
+     *     there was no mapping for `$key`.
+     */
+    public function replace(int | string $key, mixed $value): mixed;
+
+    /**
+     * Replaces the entry for the specified key only if currently mapped to the
+     * specified value.
+     *
+     * This performs a strict type check on the value.
+     *
+     * @param K $key The key to remove from the map.
+     * @param T $oldValue The value to match.
+     * @param T $newValue The value to use as a replacement.
+     *
+     * @return bool true if the value was replaced.
+     */
+    public function replaceIf(int | string $key, mixed $oldValue, mixed $newValue): bool;
+}

+ 110 - 0
vendor/ramsey/collection/src/Map/NamedParameterMap.php

@@ -0,0 +1,110 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection\Map;
+
+use Ramsey\Collection\Exception\InvalidArgumentException;
+use Ramsey\Collection\Tool\TypeTrait;
+use Ramsey\Collection\Tool\ValueToStringTrait;
+
+use function array_combine;
+use function array_key_exists;
+use function is_int;
+
+/**
+ * `NamedParameterMap` represents a mapping of values to a set of named keys
+ * that may optionally be typed
+ *
+ * @extends AbstractMap<string, mixed>
+ */
+class NamedParameterMap extends AbstractMap
+{
+    use TypeTrait;
+    use ValueToStringTrait;
+
+    /**
+     * Named parameters defined for this map.
+     *
+     * @var array<string, string>
+     */
+    private readonly array $namedParameters;
+
+    /**
+     * Constructs a new `NamedParameterMap`.
+     *
+     * @param array<array-key, string> $namedParameters The named parameters defined for this map.
+     * @param array<string, mixed> $data An initial set of data to set on this map.
+     */
+    public function __construct(array $namedParameters, array $data = [])
+    {
+        $this->namedParameters = $this->filterNamedParameters($namedParameters);
+        parent::__construct($data);
+    }
+
+    /**
+     * Returns named parameters set for this `NamedParameterMap`.
+     *
+     * @return array<string, string>
+     */
+    public function getNamedParameters(): array
+    {
+        return $this->namedParameters;
+    }
+
+    public function offsetSet(mixed $offset, mixed $value): void
+    {
+        if (!array_key_exists($offset, $this->namedParameters)) {
+            throw new InvalidArgumentException(
+                'Attempting to set value for unconfigured parameter \''
+                . $this->toolValueToString($offset) . '\'',
+            );
+        }
+
+        if ($this->checkType($this->namedParameters[$offset], $value) === false) {
+            throw new InvalidArgumentException(
+                'Value for \'' . $offset . '\' must be of type '
+                . $this->namedParameters[$offset] . '; value is '
+                . $this->toolValueToString($value),
+            );
+        }
+
+        $this->data[$offset] = $value;
+    }
+
+    /**
+     * Given an array of named parameters, constructs a proper mapping of
+     * named parameters to types.
+     *
+     * @param array<array-key, string> $namedParameters The named parameters to filter.
+     *
+     * @return array<string, string>
+     */
+    protected function filterNamedParameters(array $namedParameters): array
+    {
+        $names = [];
+        $types = [];
+
+        foreach ($namedParameters as $key => $value) {
+            if (is_int($key)) {
+                $names[] = $value;
+                $types[] = 'mixed';
+            } else {
+                $names[] = $key;
+                $types[] = $value;
+            }
+        }
+
+        return array_combine($names, $types) ?: [];
+    }
+}

+ 112 - 0
vendor/ramsey/collection/src/Map/TypedMap.php

@@ -0,0 +1,112 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection\Map;
+
+/**
+ * A `TypedMap` represents a map of elements where key and value are typed.
+ *
+ * Each element is identified by a key with defined type and a value of defined
+ * type. The keys of the map must be unique. The values on the map can be
+ * repeated but each with its own different key.
+ *
+ * The most common case is to use a string type key, but it's not limited to
+ * this type of keys.
+ *
+ * This is a direct implementation of `TypedMapInterface`, provided for the sake
+ * of convenience.
+ *
+ * Example usage:
+ *
+ * ```
+ * $map = new TypedMap('string', Foo::class);
+ * $map['x'] = new Foo();
+ * foreach ($map as $key => $value) {
+ *     // do something with $key, it will be a Foo::class
+ * }
+ *
+ * // this will throw an exception since key must be string
+ * $map[10] = new Foo();
+ *
+ * // this will throw an exception since value must be a Foo
+ * $map['bar'] = 'bar';
+ *
+ * // initialize map with contents
+ * $map = new TypedMap('string', Foo::class, [
+ *     new Foo(), new Foo(), new Foo()
+ * ]);
+ * ```
+ *
+ * It is preferable to subclass `AbstractTypedMap` to create your own typed map
+ * implementation:
+ *
+ * ```
+ * class FooTypedMap extends AbstractTypedMap
+ * {
+ *     public function getKeyType()
+ *     {
+ *         return 'int';
+ *     }
+ *
+ *     public function getValueType()
+ *     {
+ *          return Foo::class;
+ *     }
+ * }
+ * ```
+ *
+ * … but you also may use the `TypedMap` class:
+ *
+ * ```
+ * class FooTypedMap extends TypedMap
+ * {
+ *     public function __constructor(array $data = [])
+ *     {
+ *         parent::__construct('int', Foo::class, $data);
+ *     }
+ * }
+ * ```
+ *
+ * @template K of array-key
+ * @template T
+ * @extends AbstractTypedMap<K, T>
+ */
+class TypedMap extends AbstractTypedMap
+{
+    /**
+     * Constructs a map object of the specified key and value types,
+     * optionally with the specified data.
+     *
+     * @param string $keyType The data type of the map's keys.
+     * @param string $valueType The data type of the map's values.
+     * @param array<K, T> $data The initial data to set for this map.
+     */
+    public function __construct(
+        private readonly string $keyType,
+        private readonly string $valueType,
+        array $data = [],
+    ) {
+        parent::__construct($data);
+    }
+
+    public function getKeyType(): string
+    {
+        return $this->keyType;
+    }
+
+    public function getValueType(): string
+    {
+        return $this->valueType;
+    }
+}

+ 36 - 0
vendor/ramsey/collection/src/Map/TypedMapInterface.php

@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection\Map;
+
+/**
+ * A `TypedMapInterface` represents a map of elements where key and value are
+ * typed.
+ *
+ * @template K of array-key
+ * @template T
+ * @extends MapInterface<K, T>
+ */
+interface TypedMapInterface extends MapInterface
+{
+    /**
+     * Return the type used on the key.
+     */
+    public function getKeyType(): string;
+
+    /**
+     * Return the type forced on the values.
+     */
+    public function getValueType(): string;
+}

+ 148 - 0
vendor/ramsey/collection/src/Queue.php

@@ -0,0 +1,148 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection;
+
+use Ramsey\Collection\Exception\InvalidArgumentException;
+use Ramsey\Collection\Exception\NoSuchElementException;
+use Ramsey\Collection\Tool\TypeTrait;
+use Ramsey\Collection\Tool\ValueToStringTrait;
+
+use function array_key_first;
+
+/**
+ * This class provides a basic implementation of `QueueInterface`, to minimize
+ * the effort required to implement this interface.
+ *
+ * @template T
+ * @extends AbstractArray<T>
+ * @implements QueueInterface<T>
+ */
+class Queue extends AbstractArray implements QueueInterface
+{
+    use TypeTrait;
+    use ValueToStringTrait;
+
+    /**
+     * Constructs a queue object of the specified type, optionally with the
+     * specified data.
+     *
+     * @param string $queueType The type or class name associated with this queue.
+     * @param array<array-key, T> $data The initial items to store in the queue.
+     */
+    public function __construct(private readonly string $queueType, array $data = [])
+    {
+        parent::__construct($data);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Since arbitrary offsets may not be manipulated in a queue, this method
+     * serves only to fulfill the `ArrayAccess` interface requirements. It is
+     * invoked by other operations when adding values to the queue.
+     *
+     * @throws InvalidArgumentException if $value is of the wrong type.
+     */
+    public function offsetSet(mixed $offset, mixed $value): void
+    {
+        if ($this->checkType($this->getType(), $value) === false) {
+            throw new InvalidArgumentException(
+                'Value must be of type ' . $this->getType() . '; value is '
+                . $this->toolValueToString($value),
+            );
+        }
+
+        $this->data[] = $value;
+    }
+
+    /**
+     * @throws InvalidArgumentException if $value is of the wrong type.
+     */
+    public function add(mixed $element): bool
+    {
+        $this[] = $element;
+
+        return true;
+    }
+
+    /**
+     * @return T
+     *
+     * @throws NoSuchElementException if this queue is empty.
+     */
+    public function element(): mixed
+    {
+        return $this->peek() ?? throw new NoSuchElementException(
+            'Can\'t return element from Queue. Queue is empty.',
+        );
+    }
+
+    public function offer(mixed $element): bool
+    {
+        try {
+            return $this->add($element);
+        } catch (InvalidArgumentException) {
+            return false;
+        }
+    }
+
+    /**
+     * @return T | null
+     */
+    public function peek(): mixed
+    {
+        $index = array_key_first($this->data);
+
+        if ($index === null) {
+            return null;
+        }
+
+        return $this[$index];
+    }
+
+    /**
+     * @return T | null
+     */
+    public function poll(): mixed
+    {
+        $index = array_key_first($this->data);
+
+        if ($index === null) {
+            return null;
+        }
+
+        $head = $this[$index];
+        unset($this[$index]);
+
+        return $head;
+    }
+
+    /**
+     * @return T
+     *
+     * @throws NoSuchElementException if this queue is empty.
+     */
+    public function remove(): mixed
+    {
+        return $this->poll() ?? throw new NoSuchElementException(
+            'Can\'t return element from Queue. Queue is empty.',
+        );
+    }
+
+    public function getType(): string
+    {
+        return $this->queueType;
+    }
+}

+ 202 - 0
vendor/ramsey/collection/src/QueueInterface.php

@@ -0,0 +1,202 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection;
+
+use Ramsey\Collection\Exception\NoSuchElementException;
+use RuntimeException;
+
+/**
+ * A queue is a collection in which the entities in the collection are kept in
+ * order.
+ *
+ * The principal operations on the queue are the addition of entities to the end
+ * (tail), also known as *enqueue*, and removal of entities from the front
+ * (head), also known as *dequeue*. This makes the queue a first-in-first-out
+ * (FIFO) data structure.
+ *
+ * Besides basic array operations, queues provide additional insertion,
+ * extraction, and inspection operations. Each of these methods exists in two
+ * forms: one throws an exception if the operation fails, the other returns a
+ * special value (either `null` or `false`, depending on the operation). The
+ * latter form of the insert operation is designed specifically for use with
+ * capacity-restricted `QueueInterface` implementations; in most
+ * implementations, insert operations cannot fail.
+ *
+ * <table>
+ * <caption>Summary of QueueInterface methods</caption>
+ * <thead>
+ * <tr>
+ * <td></td>
+ * <td><em>Throws exception</em></td>
+ * <td><em>Returns special value</em></td>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <th>Insert</th>
+ * <td><code>add()</code></td>
+ * <td><code>offer()</code></td>
+ * </tr>
+ * <tr>
+ * <th>Remove</th>
+ * <td><code>remove()</code></td>
+ * <td><code>poll()</code></td>
+ * </tr>
+ * <tr>
+ * <th>Examine</th>
+ * <td><code>element()</code></td>
+ * <td><code>peek()</code></td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * Queues typically, but do not necessarily, order elements in a FIFO
+ * (first-in-first-out) manner. Among the exceptions are priority queues, which
+ * order elements according to a supplied comparator, or the elements' natural
+ * ordering, and LIFO queues (or stacks) which order the elements LIFO
+ * (last-in-first-out). Whatever the ordering used, the head of the queue is
+ * that element which would be removed by a call to remove() or poll(). In a
+ * FIFO queue, all new elements are inserted at the tail of the queue. Other
+ * kinds of queues may use different placement rules. Every `QueueInterface`
+ * implementation must specify its ordering properties.
+ *
+ * The `offer()` method inserts an element if possible, otherwise returning
+ * `false`. This differs from the `add()` method, which can fail to add an
+ * element only by throwing an unchecked exception. The `offer()` method is
+ * designed for use when failure is a normal, rather than exceptional
+ * occurrence, for example, in fixed-capacity (or "bounded") queues.
+ *
+ * The `remove()` and `poll()` methods remove and return the head of the queue.
+ * Exactly which element is removed from the queue is a function of the queue's
+ * ordering policy, which differs from implementation to implementation. The
+ * `remove()` and `poll()` methods differ only in their behavior when the queue
+ * is empty: the `remove()` method throws an exception, while the `poll()`
+ * method returns `null`.
+ *
+ * The `element()` and `peek()` methods return, but do not remove, the head of
+ * the queue.
+ *
+ * `QueueInterface` implementations generally do not allow insertion of `null`
+ * elements, although some implementations do not prohibit insertion of `null`.
+ * Even in the implementations that permit it, `null` should not be inserted
+ * into a queue, as `null` is also used as a special return value by the
+ * `poll()` method to indicate that the queue contains no elements.
+ *
+ * @template T
+ * @extends ArrayInterface<T>
+ */
+interface QueueInterface extends ArrayInterface
+{
+    /**
+     * Ensures that this queue contains the specified element (optional
+     * operation).
+     *
+     * Returns `true` if this queue changed as a result of the call. (Returns
+     * `false` if this queue does not permit duplicates and already contains the
+     * specified element.)
+     *
+     * Queues that support this operation may place limitations on what elements
+     * may be added to this queue. In particular, some queues will refuse to add
+     * `null` elements, and others will impose restrictions on the type of
+     * elements that may be added. Queue classes should clearly specify in their
+     * documentation any restrictions on what elements may be added.
+     *
+     * If a queue refuses to add a particular element for any reason other than
+     * that it already contains the element, it must throw an exception (rather
+     * than returning `false`). This preserves the invariant that a queue always
+     * contains the specified element after this call returns.
+     *
+     * @see self::offer()
+     *
+     * @param T $element The element to add to this queue.
+     *
+     * @return bool `true` if this queue changed as a result of the call.
+     *
+     * @throws RuntimeException if a queue refuses to add a particular element
+     *     for any reason other than that it already contains the element.
+     *     Implementations should use a more-specific exception that extends
+     *     `\RuntimeException`.
+     */
+    public function add(mixed $element): bool;
+
+    /**
+     * Retrieves, but does not remove, the head of this queue.
+     *
+     * This method differs from `peek()` only in that it throws an exception if
+     * this queue is empty.
+     *
+     * @see self::peek()
+     *
+     * @return T the head of this queue.
+     *
+     * @throws NoSuchElementException if this queue is empty.
+     */
+    public function element(): mixed;
+
+    /**
+     * Inserts the specified element into this queue if it is possible to do so
+     * immediately without violating capacity restrictions.
+     *
+     * When using a capacity-restricted queue, this method is generally
+     * preferable to `add()`, which can fail to insert an element only by
+     * throwing an exception.
+     *
+     * @see self::add()
+     *
+     * @param T $element The element to add to this queue.
+     *
+     * @return bool `true` if the element was added to this queue, else `false`.
+     */
+    public function offer(mixed $element): bool;
+
+    /**
+     * Retrieves, but does not remove, the head of this queue, or returns `null`
+     * if this queue is empty.
+     *
+     * @see self::element()
+     *
+     * @return T | null the head of this queue, or `null` if this queue is empty.
+     */
+    public function peek(): mixed;
+
+    /**
+     * Retrieves and removes the head of this queue, or returns `null`
+     * if this queue is empty.
+     *
+     * @see self::remove()
+     *
+     * @return T | null the head of this queue, or `null` if this queue is empty.
+     */
+    public function poll(): mixed;
+
+    /**
+     * Retrieves and removes the head of this queue.
+     *
+     * This method differs from `poll()` only in that it throws an exception if
+     * this queue is empty.
+     *
+     * @see self::poll()
+     *
+     * @return T the head of this queue.
+     *
+     * @throws NoSuchElementException if this queue is empty.
+     */
+    public function remove(): mixed;
+
+    /**
+     * Returns the type associated with this queue.
+     */
+    public function getType(): string;
+}

+ 59 - 0
vendor/ramsey/collection/src/Set.php

@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection;
+
+/**
+ * A set is a collection that contains no duplicate elements.
+ *
+ * Great care must be exercised if mutable objects are used as set elements.
+ * The behavior of a set is not specified if the value of an object is changed
+ * in a manner that affects equals comparisons while the object is an element in
+ * the set.
+ *
+ * Example usage:
+ *
+ * ```
+ * $foo = new \My\Foo();
+ * $set = new Set(\My\Foo::class);
+ *
+ * $set->add($foo); // returns TRUE, the element doesn't exist
+ * $set->add($foo); // returns FALSE, the element already exists
+ *
+ * $bar = new \My\Foo();
+ * $set->add($bar); // returns TRUE, $bar !== $foo
+ * ```
+ *
+ * @template T
+ * @extends AbstractSet<T>
+ */
+class Set extends AbstractSet
+{
+    /**
+     * Constructs a set object of the specified type, optionally with the
+     * specified data.
+     *
+     * @param string $setType The type or class name associated with this set.
+     * @param array<array-key, T> $data The initial items to store in the set.
+     */
+    public function __construct(private readonly string $setType, array $data = [])
+    {
+        parent::__construct($data);
+    }
+
+    public function getType(): string
+    {
+        return $this->setType;
+    }
+}

+ 31 - 0
vendor/ramsey/collection/src/Sort.php

@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection;
+
+/**
+ * Collection sorting
+ */
+enum Sort: string
+{
+    /**
+     * Sort items in a collection in ascending order.
+     */
+    case Ascending = 'asc';
+
+    /**
+     * Sort items in a collection in descending order.
+     */
+    case Descending = 'desc';
+}

+ 57 - 0
vendor/ramsey/collection/src/Tool/TypeTrait.php

@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection\Tool;
+
+use function is_array;
+use function is_bool;
+use function is_callable;
+use function is_float;
+use function is_int;
+use function is_numeric;
+use function is_object;
+use function is_resource;
+use function is_scalar;
+use function is_string;
+
+/**
+ * Provides functionality to check values for specific types.
+ */
+trait TypeTrait
+{
+    /**
+     * Returns `true` if value is of the specified type.
+     *
+     * @param string $type The type to check the value against.
+     * @param mixed $value The value to check.
+     */
+    protected function checkType(string $type, mixed $value): bool
+    {
+        return match ($type) {
+            'array' => is_array($value),
+            'bool', 'boolean' => is_bool($value),
+            'callable' => is_callable($value),
+            'float', 'double' => is_float($value),
+            'int', 'integer' => is_int($value),
+            'null' => $value === null,
+            'numeric' => is_numeric($value),
+            'object' => is_object($value),
+            'resource' => is_resource($value),
+            'scalar' => is_scalar($value),
+            'string' => is_string($value),
+            'mixed' => true,
+            default => $value instanceof $type,
+        };
+    }
+}

+ 100 - 0
vendor/ramsey/collection/src/Tool/ValueExtractorTrait.php

@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection\Tool;
+
+use Ramsey\Collection\Exception\InvalidPropertyOrMethod;
+use Ramsey\Collection\Exception\UnsupportedOperationException;
+use ReflectionProperty;
+
+use function is_array;
+use function is_object;
+use function method_exists;
+use function property_exists;
+use function sprintf;
+
+/**
+ * Provides functionality to extract the value of a property or method from an object.
+ */
+trait ValueExtractorTrait
+{
+    /**
+     * Returns the type associated with this collection.
+     */
+    abstract public function getType(): string;
+
+    /**
+     * Extracts the value of the given property, method, or array key from the
+     * element.
+     *
+     * If `$propertyOrMethod` is `null`, we return the element as-is.
+     *
+     * @param mixed $element The element to extract the value from.
+     * @param string | null $propertyOrMethod The property or method for which the
+     *     value should be extracted.
+     *
+     * @return mixed the value extracted from the specified property, method,
+     *     or array key, or the element itself.
+     *
+     * @throws InvalidPropertyOrMethod
+     * @throws UnsupportedOperationException
+     */
+    protected function extractValue(mixed $element, ?string $propertyOrMethod): mixed
+    {
+        if ($propertyOrMethod === null) {
+            return $element;
+        }
+
+        if (!is_object($element) && !is_array($element)) {
+            throw new UnsupportedOperationException(sprintf(
+                'The collection type "%s" does not support the $propertyOrMethod parameter',
+                $this->getType(),
+            ));
+        }
+
+        if (is_array($element)) {
+            return $element[$propertyOrMethod] ?? throw new InvalidPropertyOrMethod(sprintf(
+                'Key or index "%s" not found in collection elements',
+                $propertyOrMethod,
+            ));
+        }
+
+        if (property_exists($element, $propertyOrMethod) && method_exists($element, $propertyOrMethod)) {
+            $reflectionProperty = new ReflectionProperty($element, $propertyOrMethod);
+            if ($reflectionProperty->isPublic()) {
+                return $element->$propertyOrMethod;
+            }
+
+            return $element->{$propertyOrMethod}();
+        }
+
+        if (property_exists($element, $propertyOrMethod)) {
+            return $element->$propertyOrMethod;
+        }
+
+        if (method_exists($element, $propertyOrMethod)) {
+            return $element->{$propertyOrMethod}();
+        }
+
+        if (isset($element->$propertyOrMethod)) {
+            return $element->$propertyOrMethod;
+        }
+
+        throw new InvalidPropertyOrMethod(sprintf(
+            'Method or property "%s" not defined in %s',
+            $propertyOrMethod,
+            $element::class,
+        ));
+    }
+}

+ 92 - 0
vendor/ramsey/collection/src/Tool/ValueToStringTrait.php

@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * This file is part of the ramsey/collection library
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
+ * @license http://opensource.org/licenses/MIT MIT
+ */
+
+declare(strict_types=1);
+
+namespace Ramsey\Collection\Tool;
+
+use DateTimeInterface;
+
+use function assert;
+use function get_resource_type;
+use function is_array;
+use function is_bool;
+use function is_callable;
+use function is_object;
+use function is_resource;
+use function is_scalar;
+
+/**
+ * Provides functionality to express a value as string
+ */
+trait ValueToStringTrait
+{
+    /**
+     * Returns a string representation of the value.
+     *
+     * - null value: `'NULL'`
+     * - boolean: `'TRUE'`, `'FALSE'`
+     * - array: `'Array'`
+     * - scalar: converted-value
+     * - resource: `'(type resource #number)'`
+     * - object with `__toString()`: result of `__toString()`
+     * - object DateTime: ISO 8601 date
+     * - object: `'(className Object)'`
+     * - anonymous function: same as object
+     *
+     * @param mixed $value the value to return as a string.
+     */
+    protected function toolValueToString(mixed $value): string
+    {
+        // null
+        if ($value === null) {
+            return 'NULL';
+        }
+
+        // boolean constants
+        if (is_bool($value)) {
+            return $value ? 'TRUE' : 'FALSE';
+        }
+
+        // array
+        if (is_array($value)) {
+            return 'Array';
+        }
+
+        // scalar types (integer, float, string)
+        if (is_scalar($value)) {
+            return (string) $value;
+        }
+
+        // resource
+        if (is_resource($value)) {
+            return '(' . get_resource_type($value) . ' resource #' . (int) $value . ')';
+        }
+
+        // From here, $value should be an object.
+        assert(is_object($value));
+
+        // __toString() is implemented
+        if (is_callable([$value, '__toString'])) {
+            /** @var string */
+            return $value->__toString();
+        }
+
+        // object of type \DateTime
+        if ($value instanceof DateTimeInterface) {
+            return $value->format('c');
+        }
+
+        // unknown type
+        return '(' . $value::class . ' Object)';
+    }
+}

+ 19 - 0
vendor/ramsey/uuid/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2012-2023 Ben Ramsey <ben@benramsey.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 83 - 0
vendor/ramsey/uuid/README.md

@@ -0,0 +1,83 @@
+<h1 align="center">ramsey/uuid</h1>
+
+<p align="center">
+    <strong>A PHP library for generating and working with UUIDs.</strong>
+</p>
+
+<p align="center">
+    <a href="https://github.com/ramsey/uuid"><img src="http://img.shields.io/badge/source-ramsey/uuid-blue.svg?style=flat-square" alt="Source Code"></a>
+    <a href="https://packagist.org/packages/ramsey/uuid"><img src="https://img.shields.io/packagist/v/ramsey/uuid.svg?style=flat-square&label=release" alt="Download Package"></a>
+    <a href="https://php.net"><img src="https://img.shields.io/packagist/php-v/ramsey/uuid.svg?style=flat-square&colorB=%238892BF" alt="PHP Programming Language"></a>
+    <a href="https://github.com/ramsey/uuid/blob/4.x/LICENSE"><img src="https://img.shields.io/packagist/l/ramsey/uuid.svg?style=flat-square&colorB=darkcyan" alt="Read License"></a>
+    <a href="https://github.com/ramsey/uuid/actions/workflows/continuous-integration.yml"><img src="https://img.shields.io/github/actions/workflow/status/ramsey/uuid/continuous-integration.yml?branch=4.x&logo=github&style=flat-square" alt="Build Status"></a>
+    <a href="https://app.codecov.io/gh/ramsey/uuid/branch/4.x"><img src="https://img.shields.io/codecov/c/github/ramsey/uuid/4.x?label=codecov&logo=codecov&style=flat-square" alt="Codecov Code Coverage"></a>
+    <a href="https://shepherd.dev/github/ramsey/uuid"><img src="https://img.shields.io/endpoint?style=flat-square&url=https%3A%2F%2Fshepherd.dev%2Fgithub%2Framsey%2Fuuid%2Fcoverage" alt="Psalm Type Coverage"></a>
+</p>
+
+ramsey/uuid is a PHP library for generating and working with universally unique
+identifiers (UUIDs).
+
+This project adheres to a [code of conduct](CODE_OF_CONDUCT.md).
+By participating in this project and its community, you are expected to
+uphold this code.
+
+Much inspiration for this library came from the [Java][javauuid] and
+[Python][pyuuid] UUID libraries.
+
+## Installation
+
+The preferred method of installation is via [Composer][]. Run the following
+command to install the package and add it as a requirement to your project's
+`composer.json`:
+
+```bash
+composer require ramsey/uuid
+```
+
+## Upgrading to Version 4
+
+See the documentation for a thorough upgrade guide:
+
+* [Upgrading ramsey/uuid Version 3 to 4](https://uuid.ramsey.dev/en/stable/upgrading/3-to-4.html)
+
+## Documentation
+
+Please see <https://uuid.ramsey.dev> for documentation, tips, examples, and
+frequently asked questions.
+
+## Contributing
+
+Contributions are welcome! To contribute, please familiarize yourself with
+[CONTRIBUTING.md](CONTRIBUTING.md).
+
+## Coordinated Disclosure
+
+Keeping user information safe and secure is a top priority, and we welcome the
+contribution of external security researchers. If you believe you've found a
+security issue in software that is maintained in this repository, please read
+[SECURITY.md][] for instructions on submitting a vulnerability report.
+
+## ramsey/uuid for Enterprise
+
+Available as part of the Tidelift Subscription.
+
+The maintainers of ramsey/uuid and thousands of other packages are working with
+Tidelift to deliver commercial support and maintenance for the open source
+packages you use to build your applications. Save time, reduce risk, and improve
+code health, while paying the maintainers of the exact packages you use.
+[Learn more.](https://tidelift.com/subscription/pkg/packagist-ramsey-uuid?utm_source=undefined&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
+
+## Copyright and License
+
+The ramsey/uuid library is copyright © [Ben Ramsey](https://benramsey.com/) and
+licensed for use under the MIT License (MIT). Please see [LICENSE][] for more
+information.
+
+[rfc4122]: http://tools.ietf.org/html/rfc4122
+[conduct]: https://github.com/ramsey/uuid/blob/4.x/CODE_OF_CONDUCT.md
+[javauuid]: http://docs.oracle.com/javase/6/docs/api/java/util/UUID.html
+[pyuuid]: http://docs.python.org/3/library/uuid.html
+[composer]: http://getcomposer.org/
+[contributing.md]: https://github.com/ramsey/uuid/blob/4.x/CONTRIBUTING.md
+[security.md]: https://github.com/ramsey/uuid/blob/4.x/SECURITY.md
+[license]: https://github.com/ramsey/uuid/blob/4.x/LICENSE

部分文件因为文件数量过多而无法显示