本文目录导读:

在PHP项目中实现设计模式检测,通常有几种不同的方法,从简单的代码审查到使用专门的静态分析工具,以下是几种常见的实现方式:
使用静态分析工具
PHPStan 自定义规则
// 检测单例模式
class SingletonRule implements \PHPStan\Rules\Rule
{
public function getNodeType(): string
{
return \PhpParser\Node\Stmt\ClassMethod::class;
}
public function processNode(
\PhpParser\Node $node,
\PHPStan\Analyser\Scope $scope
): array
{
$errors = [];
if ($node->name->name === '__construct' &&
$node->isPrivate()) {
// 检查是否有静态的getInstance方法
$class = $scope->getClassReflection();
if ($class && !$class->hasNativeMethod('getInstance')) {
$errors[] = \PHPStan\Rules\RuleErrorBuilder::message(
'私有构造函数但没有getInstance方法,可能不是单例模式'
)->build();
}
}
return $errors;
}
}
PHP_CodeSniffer 自定义嗅探器
class FactoryPatternSniff implements \PHP_CodeSniffer\Sniffs\Sniff
{
public function register()
{
return [T_CLASS];
}
public function process(\PHP_CodeSniffer\Files\File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$className = $phpcsFile->getDeclarationName($stackPtr);
// 检测工厂模式:查找返回类型为self或类的静态方法
for ($i = $stackPtr + 1; $i < $tokens[$stackPtr]['scope_closer']; $i++) {
if ($tokens[$i]['code'] === T_STATIC &&
$tokens[$i + 2]['code'] === T_FUNCTION) {
// 检查方法是否返回特定类型
// ...
}
}
}
}
使用反射实现运行时检测
class DesignPatternDetector
{
public function detectSingleton(string $className): bool
{
try {
$reflection = new \ReflectionClass($className);
// 检查私有构造函数
$constructor = $reflection->getConstructor();
if (!$constructor || !$constructor->isPrivate()) {
return false;
}
// 检查静态的getInstance方法
if (!$reflection->hasMethod('getInstance')) {
return false;
}
$method = $reflection->getMethod('getInstance');
return $method->isStatic() && $method->isPublic();
} catch (\ReflectionException $e) {
return false;
}
}
public function detectFactory(string $className): bool
{
$reflection = new \ReflectionClass($className);
$methods = $reflection->getMethods(\ReflectionMethod::IS_STATIC | \ReflectionMethod::IS_PUBLIC);
foreach ($methods as $method) {
// 检查是否有返回自身或派生类的静态工厂方法
$returnType = $method->getReturnType();
if ($returnType && !$returnType->isBuiltin()) {
$returnTypeName = $returnType->getName();
if (is_subclass_of($returnTypeName, $className) ||
$returnTypeName === $className) {
return true;
}
}
}
return false;
}
}
基于AST的检测
use PhpParser\{ParserFactory, NodeTraverser, NodeVisitorAbstract};
class PatternDetectorVisitor extends NodeVisitorAbstract
{
private $patterns = [];
public function enterNode(Node $node)
{
// 检测观察者模式
if ($node instanceof Node\Stmt\Class_) {
$this->detectObserverPattern($node);
$this->detectAdapterPattern($node);
$this->detectStrategyPattern($node);
}
// 检测策略模式(接口+多个实现)
if ($node instanceof Node\Stmt\Interface_) {
$this->checkForStrategyInterface($node);
}
}
private function detectObserverPattern(Node\Stmt\Class_ $class)
{
// 检查是否实现了SplSubject或SplObserver
foreach ($class->implements as $implement) {
$name = $implement->toString();
if (in_array($name, ['SplSubject', 'SplObserver'])) {
$this->patterns[] = [
'type' => 'Observer',
'class' => $class->name->toString(),
'line' => $class->getLine()
];
}
}
// 检查是否有attach/detach/notify方法
$hasAttach = $hasDetach = $hasNotify = false;
foreach ($class->stmts as $stmt) {
if ($stmt instanceof Node\Stmt\ClassMethod) {
switch ($stmt->name->name) {
case 'attach': $hasAttach = true; break;
case 'detach': $hasDetach = true; break;
case 'notify': $hasNotify = true; break;
}
}
}
if ($hasAttach && $hasDetach && $hasNotify) {
$this->patterns[] = [
'type' => 'Observer (Conventional)',
'class' => $class->name->toString(),
'line' => $class->getLine()
];
}
}
}
// 使用示例
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
$traverser = new NodeTraverser();
$visitor = new PatternDetectorVisitor();
$traverser->addVisitor($visitor);
$code = file_get_contents('your_file.php');
$ast = $parser->parse($code);
$traverser->traverse($ast);
print_r($visitor->getPatterns());
自动化测试脚本
<?php
require 'vendor/autoload.php';
class DesignPatternTest extends \PHPUnit\Framework\TestCase
{
private $detector;
protected function setUp(): void
{
$this->detector = new DesignPatternDetector();
}
/**
* @dataProvider singletonProvider
*/
public function testSingletonPattern(string $className)
{
$this->assertTrue(
$this->detector->detectSingleton($className),
"{$className} should be a singleton"
);
}
public function singletonProvider(): array
{
return [
[Database::class],
[Logger::class],
[Cache::class],
];
}
public function testAllClassesForPatterns()
{
$directory = new RecursiveDirectoryIterator('./src');
$iterator = new RecursiveIteratorIterator($directory);
$regex = new RegexIterator($iterator, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH);
foreach ($regex as $file) {
$className = $this->getClassNameFromFile($file[0]);
if ($className) {
$this->detectPatternsInClass($className);
}
}
}
private function detectPatternsInClass(string $className)
{
// 检测各种模式
if ($this->detector->detectSingleton($className)) {
echo "Singletion: {$className}\n";
}
// ... 其他模式检测
}
}
Git Hooks 集成
#!/bin/bash
# pre-commit hook 检测设计模式
echo "Running design pattern detection..."
# 使用自定义PHP脚本
php pattern-detector.php --check-modified-files
if [ $? -ne 0 ]; then
echo "Design pattern violations found!"
echo "Please fix before committing."
exit 1
fi
完整的检测类实现
class DesignPatternAnalyzer
{
private $patterns = [];
private $classes = [];
public function analyzeDirectory(string $path): array
{
$this->scanDirectory($path);
$this->analyzeClasses();
return $this->patterns;
}
private function scanDirectory(string $path): void
{
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path)
);
foreach ($files as $file) {
if ($file->getExtension() === 'php') {
$this->analyzeFile($file->getRealPath());
}
}
}
private function analyzeFile(string $filePath): void
{
$content = file_get_contents($filePath);
$tokens = token_get_all($content);
// 简单的模式识别
if (preg_match('/class\s+(\w+)/', $content, $matches)) {
$className = $matches[1];
// 检测单例模式
if (preg_match('/private\s+function\s+__construct/', $content) &&
preg_match('/public\s+static\s+function\s+getInstance/', $content)) {
$this->patterns[] = [
'pattern' => 'Singleton',
'class' => $className,
'file' => $filePath
];
}
// 检测工厂模式
if (preg_match('/interface\s+(\w+Factory)/', $content) ||
preg_match('/abstract\s+class\s+(\w+Factory)/', $content)) {
$this->patterns[] = [
'pattern' => 'Factory',
'class' => $className,
'file' => $filePath
];
}
}
}
}
使用建议
- 开发阶段:使用PHPStan或PHP_CodeSniffer自定义规则
- CI/CD流程:集成自动化检测脚本
- 代码审查:使用反射检测工具辅助人工审查
- 大型项目:结合AST分析工具进行全面检测
这些方法可以帮助你在PHP项目中实现自动化的设计模式检测,提高代码质量和一致性。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播