php项目统计长短传比例如何分布?

wen PHP项目 3

PHP 项目统计长短传比例分布方案

概念定义

先明确"长传"和"短传"在 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) 长度 自定义

建议

  1. 优先用 PHP-Parser:准确、可扩展,能区分方法/闭包/箭头函数。
  2. 结合运行时埋点:静态统计的是"定义",运行期才是"实际调用分布"。
  3. 长传比例 >15% 时建议重构:
    • 参数对象模式(Parameter Object)
    • DTO / Value Object
    • Builder 模式
  4. 展示时用对数坐标:因为分布是幂律,长尾很长。

需要我针对某个具体维度(HTTP 请求大小、SQL 长度、函数行数)给出完整可运行的统计脚本吗?告诉我你的场景即可。

抱歉,评论功能暂时关闭!