ai_upgrade_assistant-0.2.0-alpha2/src/Service/MachineLearning/Visitor/ContextPatternVisitor.php
src/Service/MachineLearning/Visitor/ContextPatternVisitor.php
<?php
namespace Drupal\ai_upgrade_assistant\Service\MachineLearning\Visitor;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
/**
* Visitor for extracting contextual patterns from code.
*/
class ContextPatternVisitor extends NodeVisitorAbstract {
/**
* Collected patterns.
*
* @var array
*/
protected $patterns = [];
/**
* Context stack.
*
* @var array
*/
protected $contextStack = [];
/**
* Current namespace.
*
* @var string
*/
protected $currentNamespace = '';
/**
* Use statements.
*
* @var array
*/
protected $useStatements = [];
/**
* {@inheritdoc}
*/
public function beforeTraverse(array $nodes) {
$this->patterns = [
'module_context' => [],
'class_context' => [],
'method_context' => [],
'dependency_context' => [],
'usage_context' => [],
];
$this->contextStack = [];
return null;
}
/**
* {@inheritdoc}
*/
public function enterNode(Node $node) {
if ($node instanceof Node\Stmt\Namespace_) {
$this->enterNamespace($node);
}
elseif ($node instanceof Node\Stmt\Use_) {
$this->collectUseStatement($node);
}
elseif ($node instanceof Node\Stmt\Class_) {
$this->enterClass($node);
}
elseif ($node instanceof Node\Stmt\ClassMethod) {
$this->enterMethod($node);
}
elseif ($node instanceof Node\Expr\MethodCall || $node instanceof Node\Expr\StaticCall) {
$this->analyzeMethodUsage($node);
}
}
/**
* {@inheritdoc}
*/
public function leaveNode(Node $node) {
if ($node instanceof Node\Stmt\Class_ || $node instanceof Node\Stmt\ClassMethod) {
array_pop($this->contextStack);
}
}
/**
* Processes namespace node.
*/
protected function enterNamespace(Node\Stmt\Namespace_ $node) {
$this->currentNamespace = $node->name ? $node->name->toString() : '';
// Extract module context from namespace
if (strpos($this->currentNamespace, 'Drupal\\') === 0) {
$parts = explode('\\', $this->currentNamespace);
if (isset($parts[1])) {
$this->patterns['module_context'][] = [
'type' => 'module_namespace',
'module' => $parts[1],
'namespace' => $this->currentNamespace,
];
}
}
}
/**
* Collects use statements.
*/
protected function collectUseStatement(Node\Stmt\Use_ $node) {
foreach ($node->uses as $use) {
$this->useStatements[$use->getAlias()->name] = [
'name' => $use->name->toString(),
'alias' => $use->alias ? $use->alias->toString() : null,
];
}
}
/**
* Processes class node.
*/
protected function enterClass(Node\Stmt\Class_ $node) {
$context = [
'type' => 'class',
'name' => $node->name->toString(),
'namespace' => $this->currentNamespace,
'dependencies' => $this->collectClassDependencies($node),
];
$this->contextStack[] = $context;
$this->patterns['class_context'][] = $context;
}
/**
* Processes method node.
*/
protected function enterMethod(Node\Stmt\ClassMethod $node) {
$context = [
'type' => 'method',
'name' => $node->name->toString(),
'class_context' => end($this->contextStack),
'dependencies' => $this->collectMethodDependencies($node),
];
$this->contextStack[] = $context;
$this->patterns['method_context'][] = $context;
}
/**
* Analyzes method usage context.
*/
protected function analyzeMethodUsage(Node $node) {
$currentContext = end($this->contextStack);
if (!$currentContext) {
return;
}
$usage = [
'type' => $node instanceof Node\Expr\StaticCall ? 'static_call' : 'method_call',
'context' => $currentContext,
];
if ($node instanceof Node\Expr\StaticCall) {
if ($node->class instanceof Node\Name) {
$usage['class'] = $this->resolveClassName($node->class);
}
}
elseif ($node instanceof Node\Expr\MethodCall) {
if ($node->var instanceof Node\Expr\Variable) {
$usage['variable'] = $node->var->name;
}
}
if ($node->name instanceof Node\Identifier) {
$usage['method'] = $node->name->toString();
}
$this->patterns['usage_context'][] = $usage;
}
/**
* Collects class dependencies.
*/
protected function collectClassDependencies(Node\Stmt\Class_ $node) {
$dependencies = [];
// Collect extends
if ($node->extends) {
$dependencies['extends'] = $this->resolveClassName($node->extends);
}
// Collect implements
if ($node->implements) {
$dependencies['implements'] = array_map(
function ($interface) {
return $this->resolveClassName($interface);
},
$node->implements
);
}
// Collect traits
foreach ($node->stmts as $stmt) {
if ($stmt instanceof Node\Stmt\TraitUse) {
$dependencies['traits'] = array_map(
function ($trait) {
return $this->resolveClassName($trait);
},
$stmt->traits
);
}
}
$this->patterns['dependency_context'][] = [
'type' => 'class_dependencies',
'class' => $node->name->toString(),
'dependencies' => $dependencies,
];
return $dependencies;
}
/**
* Collects method dependencies.
*/
protected function collectMethodDependencies(Node\Stmt\ClassMethod $node) {
$dependencies = [];
// Collect parameter types
foreach ($node->params as $param) {
if ($param->type instanceof Node\Name) {
$dependencies['parameters'][] = [
'name' => $param->var->name,
'type' => $this->resolveClassName($param->type),
];
}
}
// Collect return type
if ($node->returnType instanceof Node\Name) {
$dependencies['return_type'] = $this->resolveClassName($node->returnType);
}
$this->patterns['dependency_context'][] = [
'type' => 'method_dependencies',
'method' => $node->name->toString(),
'class_context' => end($this->contextStack),
'dependencies' => $dependencies,
];
return $dependencies;
}
/**
* Resolves class name using use statements and current namespace.
*/
protected function resolveClassName(Node\Name $name) {
if ($name->isFullyQualified()) {
return $name->toString();
}
$firstPart = $name->getFirst();
if (isset($this->useStatements[$firstPart])) {
$nameParts = $name->getParts();
array_shift($nameParts);
return $this->useStatements[$firstPart]['name'] . ($nameParts ? '\\' . implode('\\', $nameParts) : '');
}
return $this->currentNamespace . '\\' . $name->toString();
}
/**
* Gets collected patterns.
*
* @return array
* The collected patterns.
*/
public function getPatterns() {
return $this->patterns;
}
}
