ai_upgrade_assistant-0.2.0-alpha2/src/Service/NodeVisitor/PluginVisitor.php
src/Service/NodeVisitor/PluginVisitor.php
<?php
namespace Drupal\ai_upgrade_assistant\Service\NodeVisitor;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
/**
* Node visitor that analyzes plugin usage and deprecated plugin types.
*/
class PluginVisitor extends NodeVisitorAbstract {
/**
* List of findings.
*
* @var array
*/
protected $findings = [];
/**
* List of deprecated plugin types and their replacements.
*
* @var array
*/
protected $deprecatedPluginTypes = [
'field.widget' => [
'base_class' => 'Drupal\field\Plugin\Type\Widget\WidgetBase',
'replacement' => 'Drupal\Core\Field\WidgetBase',
'version' => '8.0.0',
'critical' => true,
],
'field.formatter' => [
'base_class' => 'Drupal\field\Plugin\Type\Formatter\FormatterBase',
'replacement' => 'Drupal\Core\Field\FormatterBase',
'version' => '8.0.0',
'critical' => true,
],
'image.effect' => [
'base_class' => 'Drupal\image\Plugin\ImageEffect\ImageEffectBase',
'replacement' => 'Drupal\image\ImageEffectBase',
'version' => '8.7.0',
'critical' => false,
],
];
/**
* List of plugin types requiring special attention in Drupal 11.
*
* @var array
*/
protected $drupal11PluginTypes = [
'views.field' => [
'changes' => [
'New field value handling',
'Enhanced render array support',
],
'example' => 'Use structured render arrays for field output',
],
'block' => [
'changes' => [
'Cache metadata improvements',
'Block context handling',
],
'example' => 'Use block context for dynamic content',
],
];
/**
* {@inheritdoc}
*/
public function enterNode(Node $node) {
if ($node instanceof Node\Stmt\Class_) {
// Check for plugin annotation
$doc_comment = $node->getDocComment();
if ($doc_comment) {
$finding = [
'type' => 'plugin',
'class' => $node->name->toString(),
'line' => $node->getLine(),
'file' => $node->getAttribute('file'),
];
// Extract plugin type from annotation
preg_match('/@(\w+)\(/', $doc_comment->getText(), $matches);
if (!empty($matches[1])) {
$finding['plugin_type'] = $matches[1];
}
// Check base classes
if ($node->extends) {
$base_class = $node->extends->toString();
$finding['base_class'] = $base_class;
// Check for deprecated base classes
foreach ($this->deprecatedPluginTypes as $type => $info) {
if ($base_class === $info['base_class']) {
$finding['deprecated'] = true;
$finding['replacement'] = $info['replacement'];
$finding['version'] = $info['version'];
$finding['critical'] = $info['critical'];
break;
}
}
}
// Check for D11 plugin type changes
if (isset($finding['plugin_type']) && isset($this->drupal11PluginTypes[$finding['plugin_type']])) {
$finding['drupal11_changes'] = $this->drupal11PluginTypes[$finding['plugin_type']];
}
// Check plugin interfaces
$interfaces = [];
foreach ($node->implements as $interface) {
$interfaces[] = $interface->toString();
}
if (!empty($interfaces)) {
$finding['interfaces'] = $interfaces;
}
// Check plugin configuration
foreach ($node->stmts as $stmt) {
if ($stmt instanceof Node\Stmt\ClassMethod) {
if ($stmt->name->toString() === 'defaultConfiguration') {
$finding['has_configuration'] = true;
}
elseif ($stmt->name->toString() === 'buildConfigurationForm') {
$finding['has_config_form'] = true;
}
}
}
$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 = [];
}
}
