{"id":18010859,"url":"https://github.com/guanguans/soar-php","last_synced_at":"2025-04-08T08:16:12.053Z","repository":{"id":34999617,"uuid":"178793017","full_name":"guanguans/soar-php","owner":"guanguans","description":"SQL optimizer and rewriter(assisted SQL tuning). - SQL 优化器和重写器(辅助 SQL 调优)。","archived":false,"fork":false,"pushed_at":"2025-04-04T12:28:32.000Z","size":155751,"stargazers_count":171,"open_issues_count":0,"forks_count":24,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-05T00:32:46.635Z","etag":null,"topics":["debugbar","mysql","optimizer","rewriter","soar","sql","sql-alchemy","sql-injection","sql-optimizer","sql-query","sql-rewriter","sql-tuning","sqlalchemy","tuning"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/guanguans.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"patreon":"guanguans","custom":"https://www.guanguans.cn/images/wechat.jpeg"}},"created_at":"2019-04-01T05:40:52.000Z","updated_at":"2025-04-04T12:28:36.000Z","dependencies_parsed_at":"2024-11-12T22:01:28.089Z","dependency_job_id":"5587dcc0-8c3e-4f2b-876b-a0b89d8c84e8","html_url":"https://github.com/guanguans/soar-php","commit_stats":{"total_commits":512,"total_committers":5,"mean_commits":102.4,"dds":0.109375,"last_synced_commit":"afb4f624dc206142a3636339391b25fb0b011af7"},"previous_names":[],"tags_count":96,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guanguans%2Fsoar-php","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guanguans%2Fsoar-php/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guanguans%2Fsoar-php/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guanguans%2Fsoar-php/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/guanguans","download_url":"https://codeload.github.com/guanguans/soar-php/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247801175,"owners_count":20998339,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["debugbar","mysql","optimizer","rewriter","soar","sql","sql-alchemy","sql-injection","sql-optimizer","sql-query","sql-rewriter","sql-tuning","sqlalchemy","tuning"],"created_at":"2024-10-30T02:15:34.281Z","updated_at":"2025-04-08T08:16:12.029Z","avatar_url":"https://github.com/guanguans.png","language":"PHP","readme":"# soar-php\n\n\u003e [!NOTE]\n\u003e 基于小米的 [soar](https://github.com/XiaoMi/soar) 开发的 SQL 优化器、重写器(辅助 SQL 调优)。\n\n[简体中文](README-zh_CN.md) | [ENGLISH](README.md)\n\n[![tests](https://github.com/guanguans/soar-php/actions/workflows/tests.yml/badge.svg)](https://github.com/guanguans/soar-php/actions/workflows/tests.yml)\n[![check \u0026 fix styling](https://github.com/guanguans/soar-php/actions/workflows/php-cs-fixer.yml/badge.svg)](https://github.com/guanguans/soar-php/actions/workflows/php-cs-fixer.yml)\n[![codecov](https://codecov.io/gh/guanguans/soar-php/graph/badge.svg?token=0RtgSGom4K)](https://codecov.io/gh/guanguans/soar-php)\n[![Latest Stable Version](https://poser.pugx.org/guanguans/soar-php/v)](https://packagist.org/packages/guanguans/soar-php)\n[![GitHub release (with filter)](https://img.shields.io/github/v/release/guanguans/soar-php)](https://github.com/guanguans/soar-php/releases)\n[![Total Downloads](https://poser.pugx.org/guanguans/soar-php/downloads)](https://packagist.org/packages/guanguans/soar-php)\n[![License](https://poser.pugx.org/guanguans/soar-php/license)](https://packagist.org/packages/guanguans/soar-php)\n\n## 环境要求\n\n* PHP \u003e= 8.0\n\n## 框架中使用\n\n- [x] Laravel - [laravel-soar](https://github.com/guanguans/laravel-soar), [laravel-web-soar](https://github.com/huangdijia/laravel-web-soar)\n- [x] ThinkPHP - [think-soar](https://github.com/guanguans/think-soar)\n- [x] Hyperf - [hyperf-soar](https://github.com/wilbur-oo/hyperf-soar)\n- [x] Webman - [webman-soar](https://github.com/Tinywan/webman-soar)\n- [ ] Yii2\n- [ ] Symfony\n- [ ] Slim\n\n## 安装\n\n```shell\ncomposer require guanguans/soar-php -v\n```\n\n## 使用\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e快速开始\u003c/b\u003e\u003c/summary\u003e\n\n```php\n\u003c?php\n\nrequire __DIR__.'/vendor/autoload.php';\n\nuse Guanguans\\SoarPHP\\Soar;\n\n// 快速创建 soar 实例\n$soar = Soar::create();\n\n/**\n * 创建自定义 soar 实例\n * 选项 @see examples/soar-options-example.php\n */\n$soar = Soar::create(\n    [\n        // 测试环境数据库配置.\n        '-test-dsn'    =\u003e [\n            'host'     =\u003e '127.0.0.1',\n            'port'     =\u003e '3306',\n            'dbname'   =\u003e 'laravel',\n            'username' =\u003e 'root',\n            'password' =\u003e 'root',\n            'disable'  =\u003e false,\n        ],\n        // 日志输出位置 (default \"soar.log\").\n        '-log-output'  =\u003e __DIR__.'/logs/soar.log',\n        // 优化建议输出格式，目前支持: json, text, markdown, html等 (default \"markdown\").\n        '-report-type' =\u003e 'json',\n    ],\n    '自定义的 soar 路径'\n);\n\n// 最终运行: /Users/yaozm/Documents/develop/soar-php/bin/soar.darwin-amd64 '-version=true'\n$soar-\u003eclone() // 克隆 soar，避免操作原始 soar 的选项。\n    -\u003eaddVersion(true) // 添加 -version 选项的值为 `true`\n    -\u003eaddVerbose(true) // 添加 -verbose 选项的值为 `true`\n    -\u003eremoveVersion()  // 移除 -version 选项\n    -\u003esetVersion(true) // 设置 -version 选项的值为 `true`\n    -\u003emergeVersion(true) // 合并 -version 选项的值为 `true`\n    -\u003eonlyVersion() // 仅保留 -version 选项\n    -\u003edump() // 打印调试信息\n    -\u003erun(); // 运行\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e:warning: 在 unix 操作系统非 cli 环境中运行时，可能会抛出 `Fatal error: ...Exit Code: 2(Misuse of shell builtins)...`\u003c/b\u003e\u003c/summary\u003e\n\n```php\n// Fatal error: Uncaught Guanguans\\SoarPHP\\Exceptions\\ProcessFailedException: The command \"'/Users/yaozm/Documents/develop/soar-php/bin/soar.darwin-amd64' '-report-type=json' '-query=select * from users;'\" failed. Exit Code: 2(Misuse of shell builtins) Working directory: /Users/yaozm/Documents/develop/soar-php Output: ================ Error Output: ================ panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1938665] goroutine 1 [running]: github.com/pingcap/tidb/util/memory.MemTotalNormal() pkg/mod/github.com/pingcap/tidb@v1.1.0-beta.0.20210601085537-5d7c852770eb/util/memory/meminfo.go:41 +0x65 github.com/pingcap/tidb/util/memory.init.0() pkg/mod/github.com/pingcap/tidb@v1.1.0-beta.0.20210601085537-5d7c852770eb/util/memory/meminfo.go:134 +0x175 in /Users/yaozm/Documents/develop/soar-php/src/Concerns/WithRunable.php:36 Stack trace: #0 /Users/yaozm/Documents/develop/soar-php/test.php(163): Guanguans\\SoarPHP\\Soar-\u003erun() #1 /User in /Users/yaozm/Documents/develop/soar-php/src/Concerns/WithRunable.php on line 36\n$soar-\u003esetSudoPassword('your sudo password'); // 设置 sudo 密码，以 sudo 运行 soar 命令，避免出现上述错误。\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eSQL 评分(SQL 指纹、分数、Explain 解读、启发式规则建议、索引规则建议)\u003c/b\u003e\u003c/summary\u003e\n\n```php\n$sqls = \u003c\u003c\u003c'sql'\nSELECT * FROM users;\nSELECT DATE_FORMAT (t.last_update,'%Y-%m-%d'),COUNT (DISTINCT (t.city)) FROM city t WHERE t.last_update\u003e '2018-10-22 00:00:00' AND t.city LIKE '%Chrome%' AND t.city='eip' GROUP BY DATE_FORMAT(t.last_update,'%Y-%m-%d') ORDER BY DATE_FORMAT(t.last_update,'%Y-%m-%d');\nDELETE city FROM city LEFT JOIN country ON city.country_id=country.country_id WHERE country.country IS NULL;\nUPDATE city INNER JOIN country ON city.country_id=country.country_id INNER JOIN address ON city.city_id=address.city_id SET city.city='Abha',city.last_update='2006-02-15 04:45:25',country.country='Afghanistan' WHERE city.city_id=10;\nINSERT INTO city (country_id) SELECT country_id FROM country;\nREPLACE INTO city (country_id) SELECT country_id FROM country;\nALTER TABLE inventory ADD INDEX `idx_store_film` (`store_id`,`film_id`),ADD INDEX `idx_store_film` (`store_id`,`film_id`),ADD INDEX `idx_store_film` (`store_id`,`film_id`);\nDROP TABLE `users`;\nCREATE TABLE `users` (\n  `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n  `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n  `email_verified_at` timestamp NULL DEFAULT NULL,\n  `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n  `remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n  `created_at` timestamp NULL DEFAULT NULL,\n  `updated_at` timestamp NULL DEFAULT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `users_email_unique` (`email`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;\nsql;\n\n$soar-\u003escores($sqls);\n$soar-\u003ehtmlScores($sqls);\n$soar-\u003emarkdownScores($sqls);\n$soar-\u003earrayScores($sqls);\n$soar-\u003ejsonScores($sqls);\n```\n\n```php\narray:9 [\n  0 =\u003e array:8 [\n    \"ID\" =\u003e \"30AFCB1E1344BEBD\"\n    \"Fingerprint\" =\u003e \"select * from users\"\n    \"Score\" =\u003e 80\n    \"Sample\" =\u003e \"SELECT * FROM users\"\n    \"Explain\" =\u003e array:1 [\n      0 =\u003e array:6 [\n        \"Item\" =\u003e \"EXP.000\"\n        \"Severity\" =\u003e \"L0\"\n        \"Summary\" =\u003e \"Explain信息\"\n        \"Content\" =\u003e \"\"\"\n          | id | select\\_type | table | partitions | type | possible_keys | key | key\\_len | ref | rows | filtered | scalability | Extra |\\n\n          |---|---|---|---|---|---|---|---|---|---|---|---|---|\\n\n          | 1  | SIMPLE | *users* | NULL | ALL | NULL | NULL | NULL | NULL | 1 | ☠️ **100.00%** | ☠️ **O(n)** | NULL |\\n\n          \\n\n          \"\"\"\n        \"Case\" =\u003e \"\"\"\n          ### Explain信息解读\\n\n          \\n\n          #### SelectType信息解读\\n\n          \\n\n          * **SIMPLE**: 简单SELECT(不使用UNION或子查询等).\\n\n          \\n\n          #### Type信息解读\\n\n          \\n\n          * ☠️ **ALL**: 最坏的情况, 从头到尾全表扫描.\\n\n          \"\"\"\n        \"Position\" =\u003e 0\n      ]\n    ]\n    \"HeuristicRules\" =\u003e array:1 [\n      0 =\u003e array:6 [\n        \"Item\" =\u003e \"CLA.001\"\n        \"Severity\" =\u003e \"L4\"\n        \"Summary\" =\u003e \"最外层 SELECT 未指定 WHERE 条件\"\n        \"Content\" =\u003e \"SELECT 语句没有 WHERE 子句，可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(*) 类型的请求如果不要求精度，建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。\"\n        \"Case\" =\u003e \"select id from tbl\"\n        \"Position\" =\u003e 0\n      ]\n    ]\n    \"IndexRules\" =\u003e null\n    \"Tables\" =\u003e array:1 [\n      0 =\u003e \"`laravel`.`users`\"\n    ]\n  ]\n  1 =\u003e array:8 [\n    \"ID\" =\u003e \"23D3498A40F9900D\"\n    \"Fingerprint\" =\u003e \"select date_format (t.last_update,?),count (distinct (t.city)) from city t where t.last_update\u003e ? and t.city like ? and t.city=? group by date_format(t.last_update,?) order by date_format(t.last_update,?)\"\n    \"Score\" =\u003e 0\n    \"Sample\" =\u003e \"SELECT DATE_FORMAT (t.last_update,'%Y-%m-%d'),COUNT (DISTINCT (t.city)) FROM city t WHERE t.last_update\u003e '2018-10-22 00:00:00' AND t.city LIKE '%Chrome%' AND t.city='eip' GROUP BY DATE_FORMAT(t.last_update,'%Y-%m-%d') ORDER BY DATE_FORMAT(t.last_update,'%Y-%m-%d')\"\n    \"Explain\" =\u003e null\n    \"HeuristicRules\" =\u003e array:7 [\n      0 =\u003e array:6 [\n        \"Item\" =\u003e \"ALI.001\"\n        \"Severity\" =\u003e \"L0\"\n        \"Summary\" =\u003e \"建议使用 AS 关键字显示声明一个别名\"\n        \"Content\" =\u003e \"在列或表别名(如\"tbl AS alias\")中, 明确使用 AS 关键字比隐含别名(如\"tbl alias\")更易懂。\"\n        \"Case\" =\u003e \"select name from tbl t1 where id \u003c 1000\"\n        \"Position\" =\u003e 0\n      ]\n      1 =\u003e array:6 [\n        \"Item\" =\u003e \"ARG.001\"\n        \"Severity\" =\u003e \"L4\"\n        \"Summary\" =\u003e \"不建议使用前项通配符查找\"\n        \"Content\" =\u003e \"例如 \"％foo\"，查询参数有一个前项通配符的情况无法使用已有索引。\"\n        \"Case\" =\u003e \"select c1,c2,c3 from tbl where name like '%foo'\"\n        \"Position\" =\u003e 0\n      ]\n      2 =\u003e array:6 [\n        \"Item\" =\u003e \"CLA.009\"\n        \"Severity\" =\u003e \"L2\"\n        \"Summary\" =\u003e \"ORDER BY 的条件为表达式\"\n        \"Content\" =\u003e \"当 ORDER BY 条件为表达式或函数时会使用到临时表，如果在未指定 WHERE 或 WHERE 条件返回的结果集较大时性能会很差。\"\n        \"Case\" =\u003e \"select description from film where title ='ACADEMY DINOSAUR' order by length-language_id;\"\n        \"Position\" =\u003e 0\n      ]\n      3 =\u003e array:6 [\n        \"Item\" =\u003e \"CLA.010\"\n        \"Severity\" =\u003e \"L2\"\n        \"Summary\" =\u003e \"GROUP BY 的条件为表达式\"\n        \"Content\" =\u003e \"当 GROUP BY 条件为表达式或函数时会使用到临时表，如果在未指定 WHERE 或 WHERE 条件返回的结果集较大时性能会很差。\"\n        \"Case\" =\u003e \"select description from film where title ='ACADEMY DINOSAUR' GROUP BY length-language_id;\"\n        \"Position\" =\u003e 0\n      ]\n      4 =\u003e array:6 [\n        \"Item\" =\u003e \"ERR.000\"\n        \"Severity\" =\u003e \"L8\"\n        \"Summary\" =\u003e \"No available MySQL environment, build-in sql parse failed: line 1 column 61 near \"DISTINCT (t.city)) FROM city t WHERE t.last_update\u003e '2018-10-22 00:00:00' AND t.city LIKE '%Chrome%' AND t.city='eip' GROUP BY DATE_FORMAT(t.last_update,'%Y-%m-%d') ORDER BY DATE_FORMAT(t.last_update,'%Y-%m-%d')\" \"\n        \"Content\" =\u003e \"line 1 column 61 near \"DISTINCT (t.city)) FROM city t WHERE t.last_update\u003e '2018-10-22 00:00:00' AND t.city LIKE '%Chrome%' AND t.city='eip' GROUP BY DATE_FORMAT(t.last_update,'%Y-%m-%d') ORDER BY DATE_FORMAT(t.last_update,'%Y-%m-%d')\" \"\n        \"Case\" =\u003e \"\"\n        \"Position\" =\u003e 0\n      ]\n      5 =\u003e array:6 [\n        \"Item\" =\u003e \"ERR.002\"\n        \"Severity\" =\u003e \"L8\"\n        \"Summary\" =\u003e \"MySQL execute failed\"\n        \"Content\" =\u003e \"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DISTINCT (t.city)) FROM city t WHERE t.last_update\u003e '2018-10-22 00:00:00' AND t.' at line 1\"\n        \"Case\" =\u003e \"\"\n        \"Position\" =\u003e 0\n      ]\n      6 =\u003e array:6 [\n        \"Item\" =\u003e \"KEY.008\"\n        \"Severity\" =\u003e \"L4\"\n        \"Summary\" =\u003e \"ORDER BY 多个列但排序方向不同时可能无法使用索引\"\n        \"Content\" =\u003e \"在 MySQL 8.0 之前当 ORDER BY 多个列指定的排序方向不同时将无法使用已经建立的索引。\"\n        \"Case\" =\u003e \"SELECT * FROM tbl ORDER BY a DESC, b ASC;\"\n        \"Position\" =\u003e 0\n      ]\n    ]\n    \"IndexRules\" =\u003e null\n    \"Tables\" =\u003e null\n  ]\n  2 =\u003e array:8 [\n    \"ID\" =\u003e \"E759EFCE5B432198\"\n    \"Fingerprint\" =\u003e \"delete city from city left join country on city.country_id=country.country_id where country.country is null\"\n    \"Score\" =\u003e 80\n    \"Sample\" =\u003e \"DELETE city FROM city LEFT JOIN country ON city.country_id=country.country_id WHERE country.country IS NULL\"\n    \"Explain\" =\u003e null\n    \"HeuristicRules\" =\u003e array:2 [\n      0 =\u003e array:6 [\n        \"Item\" =\u003e \"JOI.007\"\n        \"Severity\" =\u003e \"L4\"\n        \"Summary\" =\u003e \"不建议使用联表删除或更新\"\n        \"Content\" =\u003e \"当需要同时删除或更新多张表时建议使用简单语句，一条 SQL 只删除或更新一张表，尽量不要将多张表的操作在同一条语句。\"\n        \"Case\" =\u003e \"UPDATE users u LEFT JOIN hobby h ON u.id = h.uid SET u.name = 'pianoboy' WHERE h.hobby = 'piano';\"\n        \"Position\" =\u003e 0\n      ]\n      1 =\u003e array:6 [\n        \"Item\" =\u003e \"SEC.003\"\n        \"Severity\" =\u003e \"L0\"\n        \"Summary\" =\u003e \"使用DELETE/DROP/TRUNCATE等操作时注意备份\"\n        \"Content\" =\u003e \"在执行高危操作之前对数据进行备份是十分有必要的。\"\n        \"Case\" =\u003e \"delete from table where col = 'condition'\"\n        \"Position\" =\u003e 0\n      ]\n    ]\n    \"IndexRules\" =\u003e null\n    \"Tables\" =\u003e array:2 [\n      0 =\u003e \"`laravel`.`city`\"\n      1 =\u003e \"`laravel`.`country`\"\n    ]\n  ]\n  3 =\u003e array:8 [\n    \"ID\" =\u003e \"67B0C3CE9FA26F37\"\n    \"Fingerprint\" =\u003e \"update city inner join country on city.country_id=country.country_id inner join address on city.city_id=address.city_id set city.city=?,city.last_update=?,country.country=? where city.city_id=?\"\n    \"Score\" =\u003e 80\n    \"Sample\" =\u003e \"UPDATE city INNER JOIN country ON city.country_id=country.country_id INNER JOIN address ON city.city_id=address.city_id SET city.city='Abha',city.last_update='2006-02-15 04:45:25',country.country='Afghanistan' WHERE city.city_id=10\"\n    \"Explain\" =\u003e null\n    \"HeuristicRules\" =\u003e array:1 [\n      0 =\u003e array:6 [\n        \"Item\" =\u003e \"JOI.007\"\n        \"Severity\" =\u003e \"L4\"\n        \"Summary\" =\u003e \"不建议使用联表删除或更新\"\n        \"Content\" =\u003e \"当需要同时删除或更新多张表时建议使用简单语句，一条 SQL 只删除或更新一张表，尽量不要将多张表的操作在同一条语句。\"\n        \"Case\" =\u003e \"UPDATE users u LEFT JOIN hobby h ON u.id = h.uid SET u.name = 'pianoboy' WHERE h.hobby = 'piano';\"\n        \"Position\" =\u003e 0\n      ]\n    ]\n    \"IndexRules\" =\u003e null\n    \"Tables\" =\u003e array:3 [\n      0 =\u003e \"`laravel`.`address`\"\n      1 =\u003e \"`laravel`.`city`\"\n      2 =\u003e \"`laravel`.`country`\"\n    ]\n  ]\n  4 =\u003e array:8 [\n    \"ID\" =\u003e \"3656B13CC4F888E2\"\n    \"Fingerprint\" =\u003e \"insert into city (country_id) select country_id from country\"\n    \"Score\" =\u003e 65\n    \"Sample\" =\u003e \"INSERT INTO city (country_id) SELECT country_id FROM country\"\n    \"Explain\" =\u003e null\n    \"HeuristicRules\" =\u003e array:2 [\n      0 =\u003e array:6 [\n        \"Item\" =\u003e \"CLA.001\"\n        \"Severity\" =\u003e \"L4\"\n        \"Summary\" =\u003e \"最外层 SELECT 未指定 WHERE 条件\"\n        \"Content\" =\u003e \"SELECT 语句没有 WHERE 子句，可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(*) 类型的请求如果不要求精度，建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。\"\n        \"Case\" =\u003e \"select id from tbl\"\n        \"Position\" =\u003e 0\n      ]\n      1 =\u003e array:6 [\n        \"Item\" =\u003e \"LCK.001\"\n        \"Severity\" =\u003e \"L3\"\n        \"Summary\" =\u003e \"INSERT INTO xx SELECT 加锁粒度较大请谨慎\"\n        \"Content\" =\u003e \"INSERT INTO xx SELECT 加锁粒度较大请谨慎\"\n        \"Case\" =\u003e \"INSERT INTO tbl SELECT * FROM tbl2;\"\n        \"Position\" =\u003e 0\n      ]\n    ]\n    \"IndexRules\" =\u003e null\n    \"Tables\" =\u003e array:2 [\n      0 =\u003e \"`laravel`.`city`\"\n      1 =\u003e \"`laravel`.`country`\"\n    ]\n  ]\n  5 =\u003e array:8 [\n    \"ID\" =\u003e \"E3DDA1A929236E72\"\n    \"Fingerprint\" =\u003e \"replace into city (country_id) select country_id from country\"\n    \"Score\" =\u003e 65\n    \"Sample\" =\u003e \"REPLACE INTO city (country_id) SELECT country_id FROM country\"\n    \"Explain\" =\u003e null\n    \"HeuristicRules\" =\u003e array:2 [\n      0 =\u003e array:6 [\n        \"Item\" =\u003e \"CLA.001\"\n        \"Severity\" =\u003e \"L4\"\n        \"Summary\" =\u003e \"最外层 SELECT 未指定 WHERE 条件\"\n        \"Content\" =\u003e \"SELECT 语句没有 WHERE 子句，可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(*) 类型的请求如果不要求精度，建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。\"\n        \"Case\" =\u003e \"select id from tbl\"\n        \"Position\" =\u003e 0\n      ]\n      1 =\u003e array:6 [\n        \"Item\" =\u003e \"LCK.001\"\n        \"Severity\" =\u003e \"L3\"\n        \"Summary\" =\u003e \"INSERT INTO xx SELECT 加锁粒度较大请谨慎\"\n        \"Content\" =\u003e \"INSERT INTO xx SELECT 加锁粒度较大请谨慎\"\n        \"Case\" =\u003e \"INSERT INTO tbl SELECT * FROM tbl2;\"\n        \"Position\" =\u003e 0\n      ]\n    ]\n    \"IndexRules\" =\u003e null\n    \"Tables\" =\u003e array:2 [\n      0 =\u003e \"`laravel`.`city`\"\n      1 =\u003e \"`laravel`.`country`\"\n    ]\n  ]\n  6 =\u003e array:8 [\n    \"ID\" =\u003e \"9BB74D074BA0727C\"\n    \"Fingerprint\" =\u003e \"alter table inventory add index `idx_store_film` (`store_id`,`film_id`),add index `idx_store_film` (`store_id`,`film_id`),add index `idx_store_film` (`store_id`,`film_id`)\"\n    \"Score\" =\u003e 100\n    \"Sample\" =\u003e \"ALTER TABLE inventory ADD INDEX `idx_store_film` (`store_id`,`film_id`),ADD INDEX `idx_store_film` (`store_id`,`film_id`),ADD INDEX `idx_store_film` (`store_id`,`film_id`)\"\n    \"Explain\" =\u003e null\n    \"HeuristicRules\" =\u003e array:1 [\n      0 =\u003e array:6 [\n        \"Item\" =\u003e \"KEY.004\"\n        \"Severity\" =\u003e \"L0\"\n        \"Summary\" =\u003e \"提醒：请将索引属性顺序与查询对齐\"\n        \"Content\" =\u003e \"如果为列创建复合索引，请确保查询属性与索引属性的顺序相同，以便DBMS在处理查询时使用索引。如果查询和索引属性订单没有对齐，那么DBMS可能无法在查询处理期间使用索引。\"\n        \"Case\" =\u003e \"create index idx1 on tbl (last_name,first_name)\"\n        \"Position\" =\u003e 0\n      ]\n    ]\n    \"IndexRules\" =\u003e null\n    \"Tables\" =\u003e array:1 [\n      0 =\u003e \"`laravel`.`inventory`\"\n    ]\n  ]\n  7 =\u003e array:8 [\n    \"ID\" =\u003e \"C77607894B4EFCC6\"\n    \"Fingerprint\" =\u003e \"drop table `users`\"\n    \"Score\" =\u003e 100\n    \"Sample\" =\u003e \"DROP TABLE `users`\"\n    \"Explain\" =\u003e null\n    \"HeuristicRules\" =\u003e array:1 [\n      0 =\u003e array:6 [\n        \"Item\" =\u003e \"SEC.003\"\n        \"Severity\" =\u003e \"L0\"\n        \"Summary\" =\u003e \"使用DELETE/DROP/TRUNCATE等操作时注意备份\"\n        \"Content\" =\u003e \"在执行高危操作之前对数据进行备份是十分有必要的。\"\n        \"Case\" =\u003e \"delete from table where col = 'condition'\"\n        \"Position\" =\u003e 0\n      ]\n    ]\n    \"IndexRules\" =\u003e null\n    \"Tables\" =\u003e array:1 [\n      0 =\u003e \"`laravel`.`users`\"\n    ]\n  ]\n  8 =\u003e array:8 [\n    \"ID\" =\u003e \"D0870E395F2CA834\"\n    \"Fingerprint\" =\u003e \"create table `users` ( `id` bigint unsigned not null auto_increment, `name` varchar(?) collate utf8mb4_unicode_ci not ?, `email` varchar(?) collate utf8mb4_unicode_ci not ?, `email_verified_at` timestamp ? default ?, `password` varchar(?) collate utf8mb4_unicode_ci not ?, `remember_token` varchar(?) collate utf8mb4_unicode_ci default ?, `created_at` timestamp ? default ?, `updated_at` timestamp ? default ?, primary key (`id`), unique key `users_email_unique` (`email`) ) engine=innodb default charset=utf8mb4 collate=utf8mb4_unicode_ci\"\n    \"Score\" =\u003e 75\n    \"Sample\" =\u003e \"\"\"\n      CREATE TABLE `users` (\\n\n        `id` bigint unsigned NOT NULL AUTO_INCREMENT,\\n\n        `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\\n\n        `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\\n\n        `email_verified_at` timestamp NULL DEFAULT NULL,\\n\n        `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\\n\n        `remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\\n\n        `created_at` timestamp NULL DEFAULT NULL,\\n\n        `updated_at` timestamp NULL DEFAULT NULL,\\n\n        PRIMARY KEY (`id`),\\n\n        UNIQUE KEY `users_email_unique` (`email`)\\n\n      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci\n      \"\"\"\n    \"Explain\" =\u003e null\n    \"HeuristicRules\" =\u003e array:7 [\n      0 =\u003e array:6 [\n        \"Item\" =\u003e \"CLA.011\"\n        \"Severity\" =\u003e \"L1\"\n        \"Summary\" =\u003e \"建议为表添加注释\"\n        \"Content\" =\u003e \"为表添加注释能够使得表的意义更明确，从而为日后的维护带来极大的便利。\"\n        \"Case\" =\u003e \"CREATE TABLE `test1` (`ID` bigint(20) NOT NULL AUTO_INCREMENT,`c1` varchar(128) DEFAULT NULL,PRIMARY KEY (`ID`)) ENGINE=InnoDB DEFAULT CHARSET=utf8\"\n        \"Position\" =\u003e 0\n      ]\n      1 =\u003e array:6 [\n        \"Item\" =\u003e \"COL.004\"\n        \"Severity\" =\u003e \"L1\"\n        \"Summary\" =\u003e \"请为列添加默认值\"\n        \"Content\" =\u003e \"请为列添加默认值，如果是 ALTER 操作，请不要忘记将原字段的默认值写上。字段无默认值，当表较大时无法在线变更表结构。\"\n        \"Case\" =\u003e \"CREATE TABLE tbl (col int) ENGINE=InnoDB;\"\n        \"Position\" =\u003e 0\n      ]\n      2 =\u003e array:6 [\n        \"Item\" =\u003e \"COL.005\"\n        \"Severity\" =\u003e \"L1\"\n        \"Summary\" =\u003e \"列未添加注释\"\n        \"Content\" =\u003e \"建议对表中每个列添加注释，来明确每个列在表中的含义及作用。\"\n        \"Case\" =\u003e \"CREATE TABLE tbl (col int) ENGINE=InnoDB;\"\n        \"Position\" =\u003e 0\n      ]\n      3 =\u003e array:6 [\n        \"Item\" =\u003e \"COL.011\"\n        \"Severity\" =\u003e \"L0\"\n        \"Summary\" =\u003e \"当需要唯一约束时才使用 NULL，仅当列不能有缺失值时才使用 NOT NULL\"\n        \"Content\" =\u003e \"NULL 和0是不同的，10乘以 NULL 还是 NULL。NULL 和空字符串是不一样的。将一个字符串和标准 SQL 中的 NULL 联合起来的结果还是 NULL。NULL 和 FALSE 也是不同的。AND、OR 和 NOT 这三个布尔操作如果涉及 NULL，其结果也让很多人感到困惑。当您将一列声明为 NOT NULL 时，也就是说这列中的每一个值都必须存在且是有意义的。使用 NULL 来表示任意类型不存在的空值。 当您将一列声明为 NOT NULL 时，也就是说这列中的每一个值都必须存在且是有意义的。\"\n        \"Case\" =\u003e \"select c1,c2,c3 from tbl where c4 is null or c4 \u003c\u003e 1\"\n        \"Position\" =\u003e 49\n      ]\n      4 =\u003e array:6 [\n        \"Item\" =\u003e \"KWR.003\"\n        \"Severity\" =\u003e \"L1\"\n        \"Summary\" =\u003e \"不建议使用复数做列名或表名\"\n        \"Content\" =\u003e \"表名应该仅仅表示表里面的实体内容，不应该表示实体数量，对应于 DO 类名也是单数形式，符合表达习惯。\"\n        \"Case\" =\u003e \"CREATE TABLE tbl ( `books` int )\"\n        \"Position\" =\u003e 0\n      ]\n      5 =\u003e array:6 [\n        \"Item\" =\u003e \"SEC.002\"\n        \"Severity\" =\u003e \"L0\"\n        \"Summary\" =\u003e \"不使用明文存储密码\"\n        \"Content\" =\u003e \"使用明文存储密码或者使用明文在网络上传递密码都是不安全的。如果攻击者能够截获您用来插入密码的SQL语句，他们就能直接读到密码。另外，将用户输入的字符串以明文的形式插入到纯SQL语句中，也会让攻击者发现它。如果您能够读取密码，黑客也可以。解决方案是使用单向哈希函数对原始密码进行加密编码。哈希是指将输入字符串转化成另一个新的、不可识别的字符串的函数。对密码加密表达式加点随机串来防御“字典攻击”。不要将明文密码输入到SQL查询语句中。在应用程序代码中计算哈希串，只在SQL查询中使用哈希串。\"\n        \"Case\" =\u003e \"create table test(id int,name varchar(20) not null,password varchar(200)not null)\"\n        \"Position\" =\u003e 0\n      ]\n      6 =\u003e array:6 [\n        \"Item\" =\u003e \"STA.003\"\n        \"Severity\" =\u003e \"L1\"\n        \"Summary\" =\u003e \"索引起名不规范\"\n        \"Content\" =\u003e \"建议普通二级索引以idx_为前缀，唯一索引以uk_为前缀。\"\n        \"Case\" =\u003e \"select col from now where type!=0\"\n        \"Position\" =\u003e 0\n      ]\n    ]\n    \"IndexRules\" =\u003e null\n    \"Tables\" =\u003e array:1 [\n      0 =\u003e \"`laravel`.`users`\"\n    ]\n  ]\n]\n```\n\n![](docs/scores.png)\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003esoar 帮助\u003c/b\u003e\u003c/summary\u003e\n\n```php\n$soar-\u003ehelp()\n```\n\n```plain\nUsage of /Users/yaozm/Documents/develop/soar-php/bin/soar.darwin-amd64:\n  -allow-charsets string\n    \tAllowCharsets (default \"utf8,utf8mb4\")\n  -allow-collates string\n    \tAllowCollates\n  -allow-drop-index\n    \tAllowDropIndex, 允许输出删除重复索引的建议\n  -allow-engines string\n    \tAllowEngines (default \"innodb\")\n  -allow-online-as-test\n    \tAllowOnlineAsTest, 允许线上环境也可以当作测试环境\n  -blacklist string\n    \t指定 blacklist 配置文件的位置，文件中的 SQL 不会被评审。一行一条SQL，可以是指纹，也可以是正则\n  -check-config\n    \tCheck configs\n  -cleanup-test-database\n    \t单次运行清理历史1小时前残余的测试库。\n  -column-not-allow-type string\n    \tColumnNotAllowType (default \"boolean\")\n  -config string\n    \tConfig file path\n  -delimiter string\n    \tDelimiter, SQL分隔符 (default \";\")\n  -drop-test-temporary\n    \tDropTestTemporary, 是否清理测试环境产生的临时库表 (default true)\n  -dry-run\n    \t是否在预演环境执行 (default true)\n  -explain\n    \tExplain, 是否开启Explain执行计划分析 (default true)\n  -explain-format string\n    \tExplainFormat [json, traditional] (default \"traditional\")\n  -explain-max-filtered float\n    \tExplainMaxFiltered, filtered大于该配置给出警告 (default 100)\n  -explain-max-keys int\n    \tExplainMaxKeyLength, 最大key_len (default 3)\n  -explain-max-rows int\n    \tExplainMaxRows, 最大扫描行数警告 (default 10000)\n  -explain-min-keys int\n    \tExplainMinPossibleKeys, 最小possible_keys警告\n  -explain-sql-report-type string\n    \tExplainSQLReportType [pretty, sample, fingerprint] (default \"pretty\")\n  -explain-type string\n    \tExplainType [extended, partitions, traditional] (default \"extended\")\n  -explain-warn-access-type string\n    \tExplainWarnAccessType, 哪些access type不建议使用 (default \"ALL\")\n  -explain-warn-extra string\n    \tExplainWarnExtra, 哪些extra信息会给警告 (default \"Using temporary,Using filesort\")\n  -explain-warn-scalability string\n    \tExplainWarnScalability, 复杂度警告名单, 支持O(n),O(log n),O(1),O(?) (default \"O(n)\")\n  -explain-warn-select-type string\n    \tExplainWarnSelectType, 哪些select_type不建议使用\n  -ignore-rules string\n    \tIgnoreRules, 忽略的优化建议规则 (default \"COL.011\")\n  -index-prefix string\n    \tIdxPrefix (default \"idx_\")\n  -list-heuristic-rules\n    \tListHeuristicRules, 打印支持的评审规则列表\n  -list-report-types\n    \tListReportTypes, 打印支持的报告输出类型\n  -list-rewrite-rules\n    \tListRewriteRules, 打印支持的重写规则列表\n  -list-test-sqls\n    \tListTestSqls, 打印测试case用于测试\n  -log-level int\n    \tLogLevel, 日志级别, [0:Emergency, 1:Alert, 2:Critical, 3:Error, 4:Warning, 5:Notice, 6:Informational, 7:Debug] (default 3)\n  -log-output string\n    \tLogOutput, 日志输出位置 (default \"soar.log\")\n  -log_err_stacks\n    \tlog stack traces for errors\n  -log_rotate_max_size uint\n    \tsize in bytes at which logs are rotated (glog.MaxSize) (default 1887436800)\n  -markdown-extensions int\n    \tMarkdownExtensions, markdown 转 html支持的扩展包, 参考blackfriday (default 94)\n  -markdown-html-flags int\n    \tMarkdownHTMLFlags, markdown 转 html 支持的 flag, 参考blackfriday\n  -max-column-count int\n    \tMaxColCount, 单表允许的最大列数 (default 40)\n  -max-distinct-count int\n    \tMaxDistinctCount, 单条 SQL 中 Distinct 的最大数量 (default 5)\n  -max-group-by-cols-count int\n    \tMaxGroupByColsCount, 单条 SQL 中 GroupBy 包含列的最大数量 (default 5)\n  -max-in-count int\n    \tMaxInCount, IN()最大数量 (default 10)\n  -max-index-bytes int\n    \tMaxIdxBytes, 索引总长度限制 (default 3072)\n  -max-index-bytes-percolumn int\n    \tMaxIdxBytesPerColumn, 索引中单列最大字节数 (default 767)\n  -max-index-cols-count int\n    \tMaxIdxColsCount, 复合索引中包含列的最大数量 (default 5)\n  -max-index-count int\n    \tMaxIdxCount, 单表最大索引个数 (default 10)\n  -max-join-table-count int\n    \tMaxJoinTableCount, 单条 SQL 中 JOIN 表的最大数量 (default 5)\n  -max-pretty-sql-length int\n    \tMaxPrettySQLLength, 超出该长度的SQL会转换成指纹输出 (default 1024)\n  -max-query-cost int\n    \tMaxQueryCost, last_query_cost 超过该值时将给予警告 (default 9999)\n  -max-subquery-depth int\n    \tMaxSubqueryDepth (default 5)\n  -max-text-cols-count int\n    \tMaxTextColsCount, 表中含有的 text/blob 列的最大数量 (default 2)\n  -max-total-rows uint\n    \tMaxTotalRows, 计算散粒度时，当数据行数大于MaxTotalRows即开启数据库保护模式，不计算散粒度 (default 9999999)\n  -max-value-count int\n    \tMaxValueCount, INSERT/REPLACE 单次批量写入允许的行数 (default 100)\n  -max-varchar-length int\n    \tMaxVarcharLength (default 1024)\n  -min-cardinality float\n    \tMinCardinality，索引列散粒度最低阈值，散粒度低于该值的列不添加索引，建议范围0.0 ~ 100.0\n  -online-dsn string\n    \tOnlineDSN, 线上环境数据库配置, username:********@tcp(ip:port)/schema (default \"tcp/information_schema?timeout=3s\u0026charset=utf8\")\n  -only-syntax-check\n    \tOnlySyntaxCheck, 只做语法检查不输出优化建议\n  -print-config\n    \tPrint configs\n  -profiling\n    \tProfiling, 开启数据采样的情况下在测试环境执行Profile\n  -query string\n    \t待评审的 SQL 或 SQL 文件，如 SQL 中包含特殊字符建议使用文件名。\n  -report-css string\n    \tReportCSS, 当 ReportType 为 html 格式时使用的 css 风格，如不指定会提供一个默认风格。CSS可以是本地文件，也可以是一个URL\n  -report-javascript string\n    \tReportJavascript, 当 ReportType 为 html 格式时使用的javascript脚本，如不指定默认会加载SQL pretty 使用的 javascript。像CSS一样可以是本地文件，也可以是一个URL\n  -report-title string\n    \tReportTitle, 当 ReportType 为 html 格式时，HTML 的 title (default \"SQL优化分析报告\")\n  -report-type string\n    \tReportType, 优化建议输出格式，目前支持: json, text, markdown, html等 (default \"markdown\")\n  -rewrite-rules string\n    \tRewriteRules, 生效的重写规则 (default \"delimiter,orderbynull,groupbyconst,dmlorderby,having,star2columns,insertcolumns,distinctstar\")\n  -sampling\n    \tSampling, 数据采样开关\n  -sampling-condition string\n    \tSamplingCondition, 数据采样条件，如： WHERE xxx LIMIT xxx\n  -sampling-statistic-target int\n    \tSamplingStatisticTarget, 数据采样因子，对应 PostgreSQL 的 default_statistics_target (default 100)\n  -show-last-query-cost\n    \tShowLastQueryCost\n  -show-warnings\n    \tShowWarnings\n  -spaghetti-query-length int\n    \tSpaghettiQueryLength, SQL最大长度警告，超过该长度会给警告 (default 2048)\n  -test-dsn string\n    \tTestDSN, 测试环境数据库配置, username:********@tcp(ip:port)/schema (default \"tcp/information_schema?timeout=3s\u0026charset=utf8\")\n  -trace\n    \tTrace, 开启数据采样的情况下在测试环境执行Trace\n  -unique-key-prefix string\n    \tUkPrefix (default \"uk_\")\n  -verbose\n    \tVerbose\n  -version\n    \tPrint version info\n```\n\u003c/details\u003e\n\n## 测试\n\n```bash\ncomposer test\n```\n\n## 变更日志\n\n请参阅 [CHANGELOG](CHANGELOG.md) 获取最近有关更改的更多信息。\n\n## 贡献指南\n\n请参阅 [CONTRIBUTING](.github/CONTRIBUTING.md) 有关详细信息。\n\n## 安全漏洞\n\n请查看[我们的安全政策](../../security/policy)了解如何报告安全漏洞。\n\n## Contributors ✨\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"http://blog.charmingkamly.cn\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/15706085?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ekamly\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/guanguans/soar-php/issues?q=author%3Akamly\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"http://leslieeilsel.com/\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/25165449?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eLeslie Lau\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/guanguans/soar-php/issues?q=author%3Aleslieeilsel\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/huangdijia\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/8337659?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eD.J.Hwang\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#ideas-huangdijia\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/zhonghaibin\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/22255693?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003e海彬\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/guanguans/soar-php/issues?q=author%3Azhonghaibin\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/Aexus\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/3403478?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eimcm\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#ideas-Aexus\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-restore --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n\n## 协议\n\nMIT 许可证（MIT）。有关更多信息，请参见[协议文件](LICENSE)。\n","funding_links":["https://patreon.com/guanguans","https://www.guanguans.cn/images/wechat.jpeg"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguanguans%2Fsoar-php","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fguanguans%2Fsoar-php","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguanguans%2Fsoar-php/lists"}