macocci7 / php-combination
一个简单的PHP库,用于生成组合
1.1.0
2024-05-15 08:01 UTC
Requires
- php: >=8.1
Requires (Dev)
- monolog/monolog: ^3.5
- php-parallel-lint/php-parallel-lint: ^1.3
- phpmd/phpmd: ^2.15
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.5
- squizlabs/php_codesniffer: ^3.7
README
1. 特点
PHP-Combination是一个简单的PHP库,用于从数组元素生成组合。
PHP-Combination可以
- 创建所有组合
- 排序组合
- 创建配对
- 创建N个元素的组合
- 创建A到B个元素的组合
- 创建多个数组的所有组合
- 在PHPUnit的数据提供者中使用
2. 内容
3. 要求
- PHP 8.1或更高版本
- Composer
4. 安装
composer require macocci7/php-combination
5. 类
对于相同的方法,存在两种类型的类。
5.1. Combination
Macocci7\PhpCombination\Combination
此类以array类型返回结果。
5.2. CombinationGenerator
Macocci7\PhpCombination\CombinationGenerator
此类以Generator对象类型返回结果。
5.3. 如何选择要使用的类
可能有三个因素。
-
all()方法和参数及结果中的元素数量
如果参数数组有$n个元素,
返回数组的数组元素数量将是
2 ** $n - 1
-
您的环境中的内存限制
内存使用取决于
- 要返回的数组元素数量
- 数据类型
Combination比CombinationGenerator使用更多的内存。 -
执行时间
循环生成器比循环数组花费更多的时间。
在某些情况下,CombinationGenerator比Combination慢几倍。
但是,将具有许多元素的数组用作参数可能导致Combination超过内存限制。
在这种情况下,请使用CombinationGenerator。
它永远不会超过内存限制,并肯定能完成任务。
6. 方法
6.1. Macocci7\PhpCombination\Combination
all(): 返回参数的所有组合pairs(): 返回参数的所有配对ofN(): 返回参数的N个元素的所有组合ofA2B(): 返回参数的A到B个元素的所有组合fromArrays(): 返回多个数组的所有组合,作为Iterator实例。
6.2. Macocci7\PhpCombination\CombinationGenenrator
all(): 返回参数的所有组合pairs(): 返回参数的所有配对ofN(): 返回参数的N个元素的所有组合ofA2B(): 返回参数的A到B个元素的所有组合fromArrays(): 返回多个数组的所有组合,作为Iterator实例。
7. 数组元素数量的限制
参数数组的元素数量
- 32位系统:30个元素
- 64位系统:62个元素
此限制设置为确保返回数组中的元素数量不超过PHP对数组索引数上限的限制。
PHP中数组最大索引数为PHP_INT_MAX。
PHP_INT_MAX:
- 32位系统:2147483647 === 2 ** 31 - 1
- 64位系统:9223372036854775807 === 2 ** 63 - 1
8. 用法
8.1. 基本用法
8.1.1 Combination
-
PHP
<?php require_once __DIR__ . '/../vendor/autoload.php'; use Macocci7\PhpCombination\Combination; $c = new Combination(); $items = [ 'A', 'B', 'C', ]; foreach ($c->all($items) as $index => $item) { echo sprintf( "%d: (%s)\n", $index, implode(', ', $item) ); }
-
结果
0: (A, B, C) 1: (A, B) 2: (A, C) 3: (A) 4: (B, C) 5: (B) 6: (C)
8.1.2. CombinationGenerator
-
PHP
<?php require_once __DIR__ . '/../vendor/autoload.php'; use Macocci7\PhpCombination\CombinationGenerator; $c = new CombinationGenerator(); $items = [ 'A', 'B', 'C', ]; foreach ($c->all($items) as $index => $item) { echo sprintf( "%d: (%s)\n", $index, implode(', ', $item) ); }
-
结果
0: (A, B, C) 1: (A, B) 2: (A, C) 3: (A) 4: (B, C) 5: (B) 6: (C)
8.2. 使用Combination
-
PHP
<?php require_once __DIR__ . '/../vendor/autoload.php'; use Macocci7\PhpCombination\Combination; // Create an Instance $c = new Combination(); // All Items $items = ['A', 'B', 'C', 'D', 'E', ]; echo sprintf("All Items:\n\t(%s)\n\n", implode(", ", $items)); // Call back $f = function (array $array): string { return sprintf("(%s)", implode(', ', $array)); }; // All Combinations echo sprintf( "All Combinations:\n\t%s\n\n", implode("\n\t", array_map($f, $c->all($items))) ); // All Pairs echo sprintf( "All Pairs:\n\t%s\n\n", implode("\n\t", array_map($f, $c->pairs($items))) ); // All Combinations of $n elements $n = 3; echo sprintf( "All Combinations of %d elements:\n\t%s\n\n", $n, implode("\n\t", array_map($f, $c->ofN($items, $n))) ); // All Combinations of $a to $b elements $a = 3; $b = 4; echo sprintf( "All Combinations of %d to %d elements:\n\t%s\n\n", $a, $b, implode("\n\t", array_map($f, $c->ofA2B($items, $a, $b))) ); // All Combinations of $a1, $a2 and $a3 $a1 = ['A1', 'A2', ]; $a2 = ['B1', 'B2', 'B3', ]; $a3 = ['C1', 'C2', 'C3', 'C4', ]; echo "All Combinations of multiple arrays:\n"; echo sprintf("\tArray1: (%s)\n", implode(', ', $a1)); echo sprintf("\tArray2: (%s)\n", implode(', ', $a2)); echo sprintf("\tArray3: (%s)\n", implode(', ', $a3)); $r = $c->fromArrays([$a1, $a2, $a3, ]); $count = count($a1) * count($a2) * count($a3); $n = strlen((string) $count); echo sprintf("\tThere're %d patterns:\n", $count); foreach ($r as $i => $e) { echo sprintf("\t%" . $n . "d: (%s)\n", $i + 1, implode(', ', $e)); }
-
结果
All Items: (A, B, C, D, E) All Combinations: (A, B, C, D, E) (A, B, C, D) (A, B, C, E) (A, B, C) (A, B, D, E) (A, B, D) (A, B, E) (A, B) (A, C, D, E) (A, C, D) (A, C, E) (A, C) (A, D, E) (A, D) (A, E) (A) (B, C, D, E) (B, C, D) (B, C, E) (B, C) (B, D, E) (B, D) (B, E) (B) (C, D, E) (C, D) (C, E) (C) (D, E) (D) (E) All Pairs: (A, B) (A, C) (A, D) (A, E) (B, C) (B, D) (B, E) (C, D) (C, E) (D, E) All Combinations of 3 elements: (A, B, C) (A, B, D) (A, B, E) (A, C, D) (A, C, E) (A, D, E) (B, C, D) (B, C, E) (B, D, E) (C, D, E) All Combinations of 3 to 4 elements: (A, B, C, D) (A, B, C, E) (A, B, C) (A, B, D, E) (A, B, D) (A, B, E) (A, C, D, E) (A, C, D) (A, C, E) (A, D, E) (B, C, D, E) (B, C, D) (B, C, E) (B, D, E) (C, D, E) All Combinations of multiple arrays: Array1: (A1, A2) Array2: (B1, B2, B3) Array3: (C1, C2, C3, C4) There're 24 patterns: 1: (A1, B1, C1) 2: (A1, B1, C2) 3: (A1, B1, C3) 4: (A1, B1, C4) 5: (A1, B2, C1) 6: (A1, B2, C2) 7: (A1, B2, C3) 8: (A1, B2, C4) 9: (A1, B3, C1) 10: (A1, B3, C2) 11: (A1, B3, C3) 12: (A1, B3, C4) 13: (A2, B1, C1) 14: (A2, B1, C2) 15: (A2, B1, C3) 16: (A2, B1, C4) 17: (A2, B2, C1) 18: (A2, B2, C2) 19: (A2, B2, C3) 20: (A2, B2, C4) 21: (A2, B3, C1) 22: (A2, B3, C2) 23: (A2, B3, C3) 24: (A2, B3, C4)
8.3. 带排序使用Combination
-
PHP
<?php require_once __DIR__ . '/../vendor/autoload.php'; use Macocci7\PhpCombination\Combination; // Create an Instance $c = new Combination(); // All Items $items = ['A', 'B', 'C', 'D', 'E', ]; echo sprintf("All Items:\n\t(%s)\n\n", implode(", ", $items)); // Set a flag to sort $sort = true; // Call back $f = function (array $array): string { return sprintf("(%s)", implode(', ', $array)); }; // All Combinations echo sprintf( "All Combinations:\n\t%s\n\n", implode("\n\t", array_map($f, $c->all($items, $sort))) ); // All Pairs: does not support sorting echo sprintf( "All Pairs:\n\t%s\n\n", implode("\n\t", array_map($f, $c->pairs($items))) ); // All Combinations of $n elements $n = 3; echo sprintf( "All Combinations of %d elements:\n\t%s\n\n", $n, implode("\n\t", array_map($f, $c->ofN($items, $n, $sort))) ); // All Combinations of $a to $b elements $a = 3; $b = 4; echo sprintf( "All Combinations of %d to %d elements:\n\t%s\n\n", $a, $b, implode("\n\t", array_map($f, $c->ofA2B($items, $a, $b, $sort))) ); // All Combinations of $a1, $a2 and $a3: does not support sorting $a1 = ['A1', 'A2', ]; $a2 = ['B1', 'B2', 'B3', ]; $a3 = ['C1', 'C2', 'C3', 'C4', ]; echo "All Combinations of multiple arrays:\n"; echo sprintf("\tArray1: (%s)\n", implode(', ', $a1)); echo sprintf("\tArray2: (%s)\n", implode(', ', $a2)); echo sprintf("\tArray3: (%s)\n", implode(', ', $a3)); $r = $c->fromArrays([$a1, $a2, $a3, ]); $count = count($a1) * count($a2) * count($a3); $n = strlen((string) $count); echo sprintf("\tThere're %d patterns:\n", $count); foreach ($r as $i => $e) { echo sprintf("\t%" . $n . "d: (%s)\n", $i + 1, implode(', ', $e)); }
-
结果
All Items: (A, B, C, D, E) All Combinations: (A) (A, B) (A, B, C) (A, B, C, D) (A, B, C, D, E) (A, B, C, E) (A, B, D) (A, B, D, E) (A, B, E) (A, C) (A, C, D) (A, C, D, E) (A, C, E) (A, D) (A, D, E) (A, E) (B) (B, C) (B, C, D) (B, C, D, E) (B, C, E) (B, D) (B, D, E) (B, E) (C) (C, D) (C, D, E) (C, E) (D) (D, E) (E) All Pairs: (A, B) (A, C) (A, D) (A, E) (B, C) (B, D) (B, E) (C, D) (C, E) (D, E) All Combinations of 3 elements: (A, B, C) (A, B, D) (A, B, E) (A, C, D) (A, C, E) (A, D, E) (B, C, D) (B, C, E) (B, D, E) (C, D, E) All Combinations of 3 to 4 elements: (A, B, C) (A, B, C, D) (A, B, C, E) (A, B, D) (A, B, D, E) (A, B, E) (A, C, D) (A, C, D, E) (A, C, E) (A, D, E) (B, C, D) (B, C, D, E) (B, C, E) (B, D, E) (C, D, E) All Combinations of multiple arrays: Array1: (A1, A2) Array2: (B1, B2, B3) Array3: (C1, C2, C3, C4) There're 24 patterns: 1: (A1, B1, C1) 2: (A1, B1, C2) 3: (A1, B1, C3) 4: (A1, B1, C4) 5: (A1, B2, C1) 6: (A1, B2, C2) 7: (A1, B2, C3) 8: (A1, B2, C4) 9: (A1, B3, C1) 10: (A1, B3, C2) 11: (A1, B3, C3) 12: (A1, B3, C4) 13: (A2, B1, C1) 14: (A2, B1, C2) 15: (A2, B1, C3) 16: (A2, B1, C4) 17: (A2, B2, C1) 18: (A2, B2, C2) 19: (A2, B2, C3) 20: (A2, B2, C4) 21: (A2, B3, C1) 22: (A2, B3, C2) 23: (A2, B3, C3) 24: (A2, B3, C4)
8.4. 使用CombinationGenerator
-
PHP
<?php require_once __DIR__ . '/../vendor/autoload.php'; use Macocci7\PhpCombination\CombinationGenerator; // Create an Instance $c = new CombinationGenerator(); // All Items $items = ['A', 'B', 'C', 'D', 'E', ]; echo sprintf("All Items:\n\t(%s)\n\n", implode(", ", $items)); // Common Format $fmt = "\t(%s)\n"; // All Combinations echo "All Combinations:\n"; foreach ($c->all($items) as $e) { echo sprintf($fmt, implode(', ', $e)); } echo "\n"; // All Pairs echo "All Pairs:\n"; foreach ($c->pairs($items) as $e) { echo sprintf($fmt, implode(', ', $e)); } echo "\n"; // All Combinations of $n elements $n = 3; echo sprintf("All Combinations of %d elements:\n", $n); foreach ($c->ofN($items, $n) as $e) { echo sprintf($fmt, implode(', ', $e)); } echo "\n"; // All Combinations of $a to $b elements $a = 3; $b = 4; echo sprintf( "All Combinations of %d to %d elements:\n", $a, $b, ); foreach ($c->ofA2B($items, $a, $b) as $e) { echo sprintf($fmt, implode(', ', $e)); } echo "\n"; // All Combinations of $a1, $a2 and $a3 $a1 = [ 'A1', 'A2', ]; $a2 = [ 'B1', 'B2', 'B3', ]; $a3 = [ 'C1', 'C2', 'C3', 'C4', ]; echo "All Combinations of multiple arrays:\n"; echo sprintf("\tArray1: (%s)\n", implode(', ', $a1)); echo sprintf("\tArray2: (%s)\n", implode(', ', $a2)); echo sprintf("\tArray3: (%s)\n", implode(', ', $a3)); $r = $c->fromArrays([$a1, $a2, $a3, ]); $count = count($a1) * count($a2) * count($a3); $n = strlen((string) $count); echo sprintf("\tThere're %d patterns:\n", $count); foreach ($r as $i => $e) { echo sprintf("\t%" . $n . "d: (%s)\n", $i + 1, implode(', ', $e)); }
-
结果
All Items: (A, B, C, D, E) All Combinations: (A, B, C, D, E) (A, B, C, D) (A, B, C, E) (A, B, C) (A, B, D, E) (A, B, D) (A, B, E) (A, B) (A, C, D, E) (A, C, D) (A, C, E) (A, C) (A, D, E) (A, D) (A, E) (A) (B, C, D, E) (B, C, D) (B, C, E) (B, C) (B, D, E) (B, D) (B, E) (B) (C, D, E) (C, D) (C, E) (C) (D, E) (D) (E) All Pairs: (A, B) (A, C) (A, D) (A, E) (B, C) (B, D) (B, E) (C, D) (C, E) (D, E) All Combinations of 3 elements: (A, B, C) (A, B, D) (A, B, E) (A, C, D) (A, C, E) (A, D, E) (B, C, D) (B, C, E) (B, D, E) (C, D, E) All Combinations of 3 to 4 elements: (A, B, C, D) (A, B, C, E) (A, B, C) (A, B, D, E) (A, B, D) (A, B, E) (A, C, D, E) (A, C, D) (A, C, E) (A, D, E) (B, C, D, E) (B, C, D) (B, C, E) (B, D, E) (C, D, E) All Combinations of multiple arrays: Array1: (A1, A2) Array2: (B1, B2, B3) Array3: (C1, C2, C3, C4) There're 24 patterns: 1: (A1, B1, C1) 2: (A1, B1, C2) 3: (A1, B1, C3) 4: (A1, B1, C4) 5: (A1, B2, C1) 6: (A1, B2, C2) 7: (A1, B2, C3) 8: (A1, B2, C4) 9: (A1, B3, C1) 10: (A1, B3, C2) 11: (A1, B3, C3) 12: (A1, B3, C4) 13: (A2, B1, C1) 14: (A2, B1, C2) 15: (A2, B1, C3) 16: (A2, B1, C4) 17: (A2, B2, C1) 18: (A2, B2, C2) 19: (A2, B2, C3) 20: (A2, B2, C4) 21: (A2, B3, C1) 22: (A2, B3, C2) 23: (A2, B3, C3) 24: (A2, B3, C4)
8.5. 在PHPUnit中使用
例如,fromArray()方法可以非常适用于与PHPUnit的数据提供者进行测试。
以下是一个用于测试排序产品类的示例,
具有size、color和amount模式。
-
测试前请安装MONOLOG:(仅此示例)
composer require --dev monolog/monolog
-
PHP: 待测试的类
<?php namespace Macocci7\PhpCombination\Examples; use Monolog\Level; use Monolog\Logger; use Monolog\Handler\StreamHandler; class UseInPhpUnit { private Logger $log; public function __construct() { $this->log = new Logger('UseInPhpUnit'); $this->log->pushHandler( new StreamHandler(__DIR__ . '/UseInPhpUnit.log', Level::Debug) ); } public function order( int $productId, string $size, string $color, int $amount ) { $this->log->info('Adding a new order', [ 'productId' => $productId, 'size' => $size, 'color' => $color, 'amount' => $amount, ]); return true; } }
-
PHP: 测试类
<?php declare(strict_types=1); namespace Macocci7\PhpCombination; require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/UseInPhpUnit.class.php'; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Macocci7\PhpCombination\Examples\UseInPhpUnit; use Macocci7\PhpCombination\Combination; final class UseInPhpUnitTest extends TestCase { public static function provide_order_can_order_correctly(): array { $products = [ 1101, 1102, ]; $sizes = [ 'S', 'M', 'L', ]; $colors = [ 'White', 'Black', ]; $amount = [ 1, 2, ]; $c = new Combination(); $data = []; foreach ( $c->fromArrays([$products, $sizes, $colors, $amount]) as $e ) { $data[implode(', ', $e)] = $e; } return $data; } /** * PHPDoc for PHPUnit 9.x * @dataProvider provide_order_can_order_correctly */ // Attribute for PHPUnit 10.x or later #[DataProvider('provide_order_can_order_correctly')] public function test_order_can_order_correctly( int $productId, string $size, string $color, int $amount ): void { $u = new UseInPhpUnit(); $this->assertTrue($u->order( $productId, $size, $color, $amount )); } }
-
结果: 标准输出
$ vendor/bin/phpunit ./examples/UseInPhpUnitTest.php --color=auto --testdox PHPUnit 10.5.19 by Sebastian Bergmann and contributors. Runtime: PHP 8.1.26 ........................ 24 / 24 (100%) Time: 00:00.053, Memory: 8.00 MB Use In Php Unit (Macocci7\PhpCombination\UseInPhpUnit) ✔ Order can order correctly with 1101,·S,·White,·1 ✔ Order can order correctly with 1101,·S,·White,·2 ✔ Order can order correctly with 1101,·S,·Black,·1 ✔ Order can order correctly with 1101,·S,·Black,·2 ✔ Order can order correctly with 1101,·M,·White,·1 ✔ Order can order correctly with 1101,·M,·White,·2 ✔ Order can order correctly with 1101,·M,·Black,·1 ✔ Order can order correctly with 1101,·M,·Black,·2 ✔ Order can order correctly with 1101,·L,·White,·1 ✔ Order can order correctly with 1101,·L,·White,·2 ✔ Order can order correctly with 1101,·L,·Black,·1 ✔ Order can order correctly with 1101,·L,·Black,·2 ✔ Order can order correctly with 1102,·S,·White,·1 ✔ Order can order correctly with 1102,·S,·White,·2 ✔ Order can order correctly with 1102,·S,·Black,·1 ✔ Order can order correctly with 1102,·S,·Black,·2 ✔ Order can order correctly with 1102,·M,·White,·1 ✔ Order can order correctly with 1102,·M,·White,·2 ✔ Order can order correctly with 1102,·M,·Black,·1 ✔ Order can order correctly with 1102,·M,·Black,·2 ✔ Order can order correctly with 1102,·L,·White,·1 ✔ Order can order correctly with 1102,·L,·White,·2 ✔ Order can order correctly with 1102,·L,·Black,·1 ✔ Order can order correctly with 1102,·L,·Black,·2 OK (24 tests, 24 assertions)
-
结果: 日志
[2024-04-18T00:46:47.065595+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1101,"size":"S","color":"White","amount":1} [] [2024-04-18T00:46:47.069266+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1101,"size":"S","color":"White","amount":2} [] [2024-04-18T00:46:47.070687+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1101,"size":"S","color":"Black","amount":1} [] [2024-04-18T00:46:47.072942+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1101,"size":"S","color":"Black","amount":2} [] [2024-04-18T00:46:47.074656+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1101,"size":"M","color":"White","amount":1} [] [2024-04-18T00:46:47.077154+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1101,"size":"M","color":"White","amount":2} [] [2024-04-18T00:46:47.078569+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1101,"size":"M","color":"Black","amount":1} [] [2024-04-18T00:46:47.079828+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1101,"size":"M","color":"Black","amount":2} [] [2024-04-18T00:46:47.080988+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1101,"size":"L","color":"White","amount":1} [] [2024-04-18T00:46:47.082439+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1101,"size":"L","color":"White","amount":2} [] [2024-04-18T00:46:47.083968+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1101,"size":"L","color":"Black","amount":1} [] [2024-04-18T00:46:47.084862+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1101,"size":"L","color":"Black","amount":2} [] [2024-04-18T00:46:47.085751+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1102,"size":"S","color":"White","amount":1} [] [2024-04-18T00:46:47.086692+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1102,"size":"S","color":"White","amount":2} [] [2024-04-18T00:46:47.090893+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1102,"size":"S","color":"Black","amount":1} [] [2024-04-18T00:46:47.092489+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1102,"size":"S","color":"Black","amount":2} [] [2024-04-18T00:46:47.094058+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1102,"size":"M","color":"White","amount":1} [] [2024-04-18T00:46:47.095176+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1102,"size":"M","color":"White","amount":2} [] [2024-04-18T00:46:47.096440+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1102,"size":"M","color":"Black","amount":1} [] [2024-04-18T00:46:47.099292+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1102,"size":"M","color":"Black","amount":2} [] [2024-04-18T00:46:47.100918+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1102,"size":"L","color":"White","amount":1} [] [2024-04-18T00:46:47.102176+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1102,"size":"L","color":"White","amount":2} [] [2024-04-18T00:46:47.103248+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1102,"size":"L","color":"Black","amount":1} [] [2024-04-18T00:46:47.106206+00:00] UseInPhpUnit.INFO: Adding a new order {"productId":1102,"size":"L","color":"Black","amount":2} []
9. 示例
- BasicUsage.php >> 结果在 BasicUsage.txt
- BasicUsageGenerator.php >> 结果在 BasicUsageGenerator.txt
- UseCombination.php >> 结果在 UseCombination.txt
- UseCombinationSort.php >> 结果在 UseCombinationSort.txt
- UseCombinationGenerator.php >> 结果在 UseCombinationGenerator.txt
- UseInPhpUnit.class.php & UseInPhpUnitTest.php >> 结果在 UseInPhpUnit.log
10. LICENSE
文档创建日期:2023/11/11
文档更新日期:2024/05/15
版权所有 2023 - 2024 macocci7