PHP 项目统计长短传比例分布方案
概念定义
先明确"长传"和"短传"在 PHP 项目中的定义(根据不同场景有不同含义):

| 场景 | 长传 | 短传 |
|---|---|---|
| 函数参数 | 参数数量多(>3-4个) | 参数数量少(≤3个) |
| 请求参数 | 数据传输量大(>10KB) | 数据传输量小 |
| HTTP 请求体 | 大 body | 小 body |
| 代码行数 | 函数/方法行数长 | 函数/方法行数短 |
下面以函数参数传递(最常见)为例给出统计方案。
统计方法
方法1:静态代码分析(PHP-Parser)
使用 nikic/php-parser 解析 AST,统计所有函数的参数数量。
<?php
require 'vendor/autoload.php';
use PhpParser\ParserFactory;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
use PhpParser\Node;
class ParamCountVisitor extends NodeVisitorAbstract
{
public array $stats = [
'short' => 0, // 短传 <=3
'med' => 0, // 中传 4-6
'long' => 0, // 长传 >6
'dist' => [], // 分布明细
];
public function enterNode(Node $node)
{
if ($node instanceof Node\FunctionLike) {
$count = count($node->getParams());
$this->stats['dist'][$count] = ($this->stats['dist'][$count] ?? 0) + 1;
if ($count <= 3) {
$this->stats['short']++;
} elseif ($count <= 6) {
$this->stats['med']++;
} else {
$this->stats['long']++;
}
}
return null;
}
}
function analyze($dir): array
{
$parser = (new ParserFactory())->createForNewestSupportedVersion();
$visitor = new ParamCountVisitor();
$traverser = new NodeTraverser();
$traverser->addVisitor($visitor);
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir)
);
foreach ($it as $file) {
if ($file->getExtension() !== 'php') continue;
try {
$ast = $parser->parse(file_get_contents($file->getPathname()));
if ($ast) $traverser->traverse($ast);
} catch (\Throwable $e) {
// 忽略语法错误文件
}
}
return $visitor->stats;
}
$stats = analyze(__DIR__ . '/src');
$total = $stats['short'] + $stats['med'] + $stats['long'];
echo "短传(≤3): {$stats['short']} (" . round($stats['short']/$total*100,1) . "%)\n";
echo "中传(4-6): {$stats['med']} (" . round($stats['med']/$total*100,1) . "%)\n";
echo "长传(>6): {$stats['long']} (" . round($stats['long']/$total*100,1) . "%)\n";
// 输出分布直方图
ksort($stats['dist']);
foreach ($stats['dist'] as $n => $c) {
printf("参数%d个: %s (%d)\n", $n, str_repeat('█', $c), $c);
}
示例输出:
短传(≤3): 1240 (62.5%)
中传(4-6): 580 (29.3%)
长传(>6): 162 (8.2%)
参数0个: ████████ (8)
参数1个: ██████████████████ (18)
参数2个: ████████████████████ (20)
参数3个: ██████████████████████████ (26)
参数4个: ████████████████████ (20)
参数5个: ████████████████ (16)
参数6个: ████████████ (12)
参数7个: ██████ (6)
参数8个: ████ (4)
方法2:PHPUnit / 运行时埋点
如果运行期统计实际调用的长短传比例,可以:
// 在基类或中间件里
function recordParams(array $params, string $func): void
{
$n = count($params);
// 写入 Redis / 日志
Redis::hincrby('param_stats', "count_$n", 1);
Redis::hincrby('param_stats', 'total', 1);
}
配合 debug_backtrace() 或 AOP 扩展(如 Swoole 的 hook)实现无侵入统计。
方法3:命令行快速统计(grep + awk)
适用于快速估算:
# 统计所有 function 定义的参数个数(简易版)
grep -rEho 'function\s+\w+\s*\(([^)]*)\)' src/ \
| sed -E 's/.*\((.*)\).*/\1/' \
| awk -F',' '{
if ($0=="") n=0; else n=NF;
if (n<=3) short++; else if (n<=6) med++; else long++;
total++;
if (n>0) print n
}
END {
print "短传:", short, short/total*100 "%"
print "中传:", med, med/total*100 "%"
print "长传:", long, long/total*100 "%"
}'
典型分布参考
根据业界大量 PHP 项目(Laravel、Symfony 等)统计经验:
| 类型 | 参数个数 | 占比大致范围 | 说明 |
|---|---|---|---|
| 短传 | 0-3 | 60% ~ 75% | 最常见,符合直觉 |
| 中传 | 4-6 | 20% ~ 30% | 多为构造函数/配置类 |
| 长传 | 7+ | 3% ~ 10% | 需重构,建议用对象/数组/DTO |
幂律分布特征:参数个数越多,出现频率越低,近似指数衰减。
如何可视化分布
方案 A:Mermaid / 文本直方图
0个 ████████ 8
1个 ██████████████████ 18
2个 ████████████████████ 20 ← 峰值
3个 ██████████████████████████26
4个 ████████████████████ 20
5个 ████████████████ 16
6个 ████████████ 12
7个 ██████ 6
8个 ████ 4
9+ ██ 2
方案 B:HTML + Chart.js
把 $stats['dist'] 直接 json_encode 后喂给 Chart.js 画柱状图。
方案 C:导出 CSV 到 Excel / Grafana
params,count,percent
0,8,0.4
1,18,0.9
...
扩展:其他维度的"长短传"统计
| 维度 | 统计方式 | 常用阈值 |
|---|---|---|
| HTTP body 大小 | Nginx log $request_length |
>10KB 长,<1KB 短 |
| 响应大小 | $body_bytes_sent |
同上 |
| 函数代码行数 | PHP-Parser 统计 startLine-endLine |
>50 行长传 |
| SQL 语句长度 | 慢查询日志 | >1KB 长 |
| 方法入参字节数 | serialize($args) 长度 |
自定义 |
建议
- 优先用 PHP-Parser:准确、可扩展,能区分方法/闭包/箭头函数。
- 结合运行时埋点:静态统计的是"定义",运行期才是"实际调用分布"。
- 长传比例 >15% 时建议重构:
- 参数对象模式(Parameter Object)
- DTO / Value Object
- Builder 模式
- 展示时用对数坐标:因为分布是幂律,长尾很长。
需要我针对某个具体维度(HTTP 请求大小、SQL 长度、函数行数)给出完整可运行的统计脚本吗?告诉我你的场景即可。