如何用PHP项目实现循环依赖检测?

wen java案例 2

本文目录导读:

如何用PHP项目实现循环依赖检测?

  1. 基础依赖检测类
  2. 反射方式检测类依赖
  3. 文件扫描方式实现
  4. 使用示例
  5. Composer包集成方式

在PHP项目中实现循环依赖检测,通常用于分析类之间的依赖关系,以下是一个完整的实现方案:

基础依赖检测类

<?php
class CircularDependencyDetector
{
    private array $dependencies = [];
    private array $visited = [];
    private array $inStack = [];
    private array $circularPaths = [];
    /**
     * 添加依赖关系
     */
    public function addDependency(string $from, string $to): void
    {
        if (!isset($this->dependencies[$from])) {
            $this->dependencies[$from] = [];
        }
        $this->dependencies[$from][] = $to;
    }
    /**
     * 检测循环依赖
     */
    public function detect(): array
    {
        $this->visited = [];
        $this->inStack = [];
        $this->circularPaths = [];
        foreach (array_keys($this->dependencies) as $node) {
            if (!isset($this->visited[$node])) {
                $this->dfs($node, []);
            }
        }
        return $this->circularPaths;
    }
    /**
     * 深度优先搜索检测环
     */
    private function dfs(string $node, array $path): void
    {
        $this->visited[$node] = true;
        $this->inStack[$node] = true;
        $path[] = $node;
        if (isset($this->dependencies[$node])) {
            foreach ($this->dependencies[$node] as $neighbor) {
                if (!isset($this->visited[$neighbor])) {
                    $this->dfs($neighbor, $path);
                } elseif (isset($this->inStack[$neighbor])) {
                    // 发现循环依赖
                    $circlePath = array_slice($path, array_search($neighbor, $path));
                    $circlePath[] = $neighbor;
                    $this->circularPaths[] = $circlePath;
                }
            }
        }
        array_pop($path);
        $this->inStack[$node] = false;
    }
}

反射方式检测类依赖

<?php
class ReflectionDependencyDetector
{
    private array $dependencies = [];
    private array $discoveredClasses = [];
    /**
     * 分析类的依赖关系
     */
    public function analyzeClass(string $className): void
    {
        if (in_array($className, $this->discoveredClasses)) {
            return;
        }
        $this->discoveredClasses[] = $className;
        if (!class_exists($className)) {
            return;
        }
        $reflection = new ReflectionClass($className);
        // 分析构造函数参数
        $constructor = $reflection->getConstructor();
        if ($constructor) {
            foreach ($constructor->getParameters() as $parameter) {
                $type = $parameter->getType();
                if ($type && !$type->isBuiltin()) {
                    $dependencyClass = $type->getName();
                    $this->addDependency($className, $dependencyClass);
                    $this->analyzeClass($dependencyClass);
                }
            }
        }
        // 分析属性类型
        foreach ($reflection->getProperties() as $property) {
            $type = $property->getType();
            if ($type && !$type->isBuiltin()) {
                $dependencyClass = $type->getName();
                $this->addDependency($className, $dependencyClass);
                $this->analyzeClass($dependencyClass);
            }
        }
    }
    public function addDependency(string $from, string $to): void
    {
        if (!isset($this->dependencies[$from])) {
            $this->dependencies[$from] = [];
        }
        $this->dependencies[$from][] = $to;
    }
    public function getDependencies(): array
    {
        return $this->dependencies;
    }
    /**
     * 检测所有依赖中的循环
     */
    public function detectCircular(): array
    {
        $detector = new CircularDependencyDetector();
        foreach ($this->dependencies as $from => $tos) {
            foreach ($tos as $to) {
                $detector->addDependency($from, $to);
            }
        }
        return $detector->detect();
    }
}

文件扫描方式实现

<?php
class FileDependencyAnalyzer
{
    private string $basePath;
    private array $classMap = [];
    public function __construct(string $basePath)
    {
        $this->basePath = rtrim($basePath, '/');
    }
    /**
     * 扫描目录下的PHP文件
     */
    public function scanDirectory(string $dir = ''): void
    {
        $targetDir = $dir ? $this->basePath . '/' . $dir : $this->basePath;
        $iterator = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($targetDir)
        );
        foreach ($iterator as $file) {
            if ($file->getExtension() === 'php') {
                $this->analyzeFile($file->getPathname());
            }
        }
    }
    /**
     * 分析单个文件
     */
    private function analyzeFile(string $filePath): void
    {
        $content = file_get_contents($filePath);
        // 提取类名
        preg_match('/class\s+(\w+)/', $content, $matches);
        if (!isset($matches[1])) {
            return;
        }
        $className = $matches[1];
        // 提取use语句
        preg_match_all('/use\s+([^;]+);/', $content, $useMatches);
        // 提取方法参数类型
        preg_match_all('/function\s+\w+\s*\([^)]*\)/', $content, $methodMatches);
        $dependencies = [];
        // 从use语句中提取依赖
        foreach ($useMatches[1] ?? [] as $use) {
            $parts = explode(' as ', $use);
            $fullClass = trim($parts[0]);
            $dependencies[] = $this->extractClassName($fullClass);
        }
        // 从方法参数中提取依赖
        foreach ($methodMatches[0] ?? [] as $method) {
            preg_match_all('/(\w+)\s+\$\w+/', $method, $paramMatches);
            foreach ($paramMatches[1] ?? [] as $typeHint) {
                if (!in_array($typeHint, ['string', 'int', 'float', 'bool', 'array', 'callable', 'iterable', 'object', 'mixed', 'void', 'null', 'self', 'parent'])) {
                    $dependencies[] = $typeHint;
                }
            }
        }
        $this->classMap[$className] = array_unique($dependencies);
    }
    /**
     * 提取类名(不包括命名空间)
     */
    private function extractClassName(string $fullClassName): string
    {
        $parts = explode('\\', $fullClassName);
        return end($parts);
    }
    /**
     * 检测循环依赖
     */
    public function detectCircularDependencies(): array
    {
        $detector = new CircularDependencyDetector();
        foreach ($this->classMap as $className => $dependencies) {
            foreach ($dependencies as $dependency) {
                if (isset($this->classMap[$dependency])) {
                    $detector->addDependency($className, $dependency);
                }
            }
        }
        return $detector->detect();
    }
}

使用示例

<?php
// 示例1:手动添加依赖检测
$detector = new CircularDependencyDetector();
$detector->addDependency('ClassA', 'ClassB');
$detector->addDependency('ClassB', 'ClassC');
$detector->addDependency('ClassC', 'ClassA'); // 创建循环依赖
$results = $detector->detect();
echo "循环依赖检测结果:" . PHP_EOL;
foreach ($results as $path) {
    echo implode(' -> ', $path) . PHP_EOL;
}
// 示例2:使用反射检测
$reflector = new ReflectionDependencyDetector();
$reflector->analyzeClass('SomeClass');
$circular = $reflector->detectCircular();
// 示例3:文件扫描检测
$fileAnalyzer = new FileDependencyAnalyzer('/path/to/your/project');
$fileAnalyzer->scanDirectory('src');
$circularDeps = $fileAnalyzer->detectCircularDependencies();
// 输出结果
if (!empty($circularDeps)) {
    echo "发现循环依赖:" . PHP_EOL;
    foreach ($circularDeps as $index => $path) {
        echo "循环 #" . ($index + 1) . ": " . implode(' -> ', $path) . PHP_EOL;
    }
} else {
    echo "未发现循环依赖" . PHP_EOL;
}

Composer包集成方式

如果需要更强大的循环依赖检测,可以使用Composer依赖:

composer require --dev phpmd/phpmd

然后在项目配置中包含循环依赖检测规则。

这样的实现可以:

  • 检测类之间的循环依赖
  • 支持反射分析和文件扫描
  • 输出完整的循环依赖路径
  • 轻量级,无需额外依赖

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