ai_upgrade_assistant-0.2.0-alpha2/src/Service/NodeVisitor/ClassUsageVisitor.php
src/Service/NodeVisitor/ClassUsageVisitor.php
<?php
namespace Drupal\ai_upgrade_assistant\Service\NodeVisitor;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
/**
* Node visitor that detects class usage and inheritance.
*/
class ClassUsageVisitor extends NodeVisitorAbstract {
/**
* List of findings.
*
* @var array
*/
protected $findings = [];
/**
* List of deprecated classes and their replacements.
*
* @var array
*/
protected $deprecatedClasses = [
// Core Entity Classes
'Drupal\Core\Entity\EntityNG' => [
'replacement' => 'Drupal\Core\Entity\ContentEntityBase',
'version' => '8.0.0',
'critical' => true,
],
'Drupal\Core\Entity\EntityInterface' => [
'replacement' => 'Drupal\Core\Entity\EntityInterface',
'version' => '8.0.0',
'critical' => true,
],
'Drupal\Core\Entity\Entity' => [
'replacement' => 'Drupal\Core\Entity\EntityBase',
'version' => '8.0.0',
'critical' => true,
],
// Field Classes
'Drupal\field\Plugin\Type\Widget\WidgetBase' => [
'replacement' => 'Drupal\Core\Field\WidgetBase',
'version' => '8.0.0',
'critical' => true,
],
'Drupal\field\Plugin\Type\Formatter\FormatterBase' => [
'replacement' => 'Drupal\Core\Field\FormatterBase',
'version' => '8.0.0',
'critical' => true,
],
// Plugin Classes
'Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator' => [
'replacement' => 'Drupal\Component\Plugin\Discovery\DerivativeInspectionDecorator',
'version' => '8.0.0',
'critical' => true,
],
// Config Classes
'Drupal\Core\Config\Entity\ConfigEntityStorage' => [
'replacement' => 'Drupal\Core\Config\Entity\ConfigEntityStorage',
'version' => '8.0.0',
'critical' => true,
],
// Database Classes
'Drupal\Core\Database\Driver\mysql\Connection' => [
'replacement' => 'Drupal\mysql\Driver\Database\mysql\Connection',
'version' => '10.0.0',
'critical' => false,
],
'Drupal\Core\Database\Driver\pgsql\Connection' => [
'replacement' => 'Drupal\pgsql\Driver\Database\pgsql\Connection',
'version' => '10.0.0',
'critical' => false,
],
// Form Classes
'Drupal\Core\Form\FormBase' => [
'replacement' => 'Drupal\Core\Form\FormBase with #lazy_builder',
'version' => '10.0.0',
'critical' => false,
'note' => 'Consider using #lazy_builder for better performance',
],
// Cache Classes
'Drupal\Core\Cache\CacheBackendInterface' => [
'replacement' => 'Drupal\Core\Cache\CacheBackendInterface with tags',
'version' => '10.0.0',
'critical' => false,
'note' => 'Ensure proper cache tags are used',
],
];
/**
* List of classes requiring special attention in Drupal 11.
*
* @var array
*/
protected $drupal11Classes = [
'Drupal\Core\Entity\EntityTypeInterface' => [
'changes' => [
'New methods added for revision support',
'Enhanced bundle handling',
],
'example' => 'implement getRevisionMetadata() method',
],
'Drupal\Core\Config\Entity\ConfigEntityInterface' => [
'changes' => [
'New validation constraints',
'Enhanced schema handling',
],
'example' => 'implement validateSchema() method',
],
];
/**
* {@inheritdoc}
*/
public function enterNode(Node $node) {
if ($node instanceof Node\Stmt\Class_) {
// Check class inheritance
if ($node->extends) {
$extends = $node->extends->toString();
$finding = [
'type' => 'class_extends',
'class' => $node->name->toString(),
'extends' => $extends,
'line' => $node->getLine(),
'file' => $node->getAttribute('file'),
];
if (isset($this->deprecatedClasses[$extends])) {
$finding['deprecated'] = true;
$finding['replacement'] = $this->deprecatedClasses[$extends]['replacement'];
$finding['version'] = $this->deprecatedClasses[$extends]['version'];
$finding['critical'] = $this->deprecatedClasses[$extends]['critical'];
if (isset($this->deprecatedClasses[$extends]['note'])) {
$finding['note'] = $this->deprecatedClasses[$extends]['note'];
}
}
if (isset($this->drupal11Classes[$extends])) {
$finding['drupal11_changes'] = $this->drupal11Classes[$extends];
}
$this->findings[] = $finding;
}
// Check interface implementations
foreach ($node->implements as $interface) {
$interfaceName = $interface->toString();
$finding = [
'type' => 'implements_interface',
'class' => $node->name->toString(),
'interface' => $interfaceName,
'line' => $node->getLine(),
'file' => $node->getAttribute('file'),
];
if (isset($this->deprecatedClasses[$interfaceName])) {
$finding['deprecated'] = true;
$finding['replacement'] = $this->deprecatedClasses[$interfaceName]['replacement'];
$finding['version'] = $this->deprecatedClasses[$interfaceName]['version'];
$finding['critical'] = $this->deprecatedClasses[$interfaceName]['critical'];
if (isset($this->deprecatedClasses[$interfaceName]['note'])) {
$finding['note'] = $this->deprecatedClasses[$interfaceName]['note'];
}
}
if (isset($this->drupal11Classes[$interfaceName])) {
$finding['drupal11_changes'] = $this->drupal11Classes[$interfaceName];
}
$this->findings[] = $finding;
}
// Check for trait usage
foreach ($node->traits as $trait) {
$traitName = $trait->toString();
$finding = [
'type' => 'uses_trait',
'class' => $node->name->toString(),
'trait' => $traitName,
'line' => $node->getLine(),
'file' => $node->getAttribute('file'),
];
if (isset($this->deprecatedClasses[$traitName])) {
$finding['deprecated'] = true;
$finding['replacement'] = $this->deprecatedClasses[$traitName]['replacement'];
$finding['version'] = $this->deprecatedClasses[$traitName]['version'];
$finding['critical'] = $this->deprecatedClasses[$traitName]['critical'];
}
$this->findings[] = $finding;
}
}
elseif ($node instanceof Node\Expr\New_) {
// Check class instantiation
if ($node->class instanceof Node\Name) {
$className = $node->class->toString();
$finding = [
'type' => 'class_instantiation',
'class' => $className,
'line' => $node->getLine(),
'file' => $node->getAttribute('file'),
'args_count' => count($node->args),
];
if (isset($this->deprecatedClasses[$className])) {
$finding['deprecated'] = true;
$finding['replacement'] = $this->deprecatedClasses[$className]['replacement'];
$finding['version'] = $this->deprecatedClasses[$className]['version'];
$finding['critical'] = $this->deprecatedClasses[$className]['critical'];
if (isset($this->deprecatedClasses[$className]['note'])) {
$finding['note'] = $this->deprecatedClasses[$className]['note'];
}
}
// Add constructor argument info if available
if ($node->args) {
$args = [];
foreach ($node->args as $arg) {
if ($arg->value instanceof Node\Scalar\String_) {
$args[] = $arg->value->value;
}
}
if ($args) {
$finding['constructor_args'] = $args;
}
}
$this->findings[] = $finding;
}
}
}
/**
* Gets the findings.
*
* @return array
* Array of findings.
*/
public function getFindings() {
return $this->findings;
}
/**
* Resets the findings.
*/
public function reset() {
$this->findings = [];
}
}
