本文目录导读:

在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
然后在项目配置中包含循环依赖检测规则。
这样的实现可以:
- 检测类之间的循环依赖
- 支持反射分析和文件扫描
- 输出完整的循环依赖路径
- 轻量级,无需额外依赖