ai_upgrade_assistant-0.2.0-alpha2/src/Service/NodeVisitor/HookVisitor.php
src/Service/NodeVisitor/HookVisitor.php
<?php
namespace Drupal\ai_upgrade_assistant\Service\NodeVisitor;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
/**
* Node visitor that detects hook implementations.
*/
class HookVisitor extends NodeVisitorAbstract {
/**
* List of findings.
*
* @var array
*/
protected $findings = [];
/**
* The module name.
*
* @var string
*/
protected $moduleName;
/**
* List of deprecated hooks and their replacements.
*
* @var array
*/
protected $deprecatedHooks = [
// Entity hooks
'hook_entity_load' => [
'replacement' => 'hook_ENTITY_TYPE_load',
'version' => '8.7.0',
'critical' => true,
],
'hook_entity_presave' => [
'replacement' => 'hook_ENTITY_TYPE_presave',
'version' => '8.7.0',
'critical' => true,
],
'hook_entity_insert' => [
'replacement' => 'hook_ENTITY_TYPE_insert',
'version' => '8.7.0',
'critical' => true,
],
'hook_entity_update' => [
'replacement' => 'hook_ENTITY_TYPE_update',
'version' => '8.7.0',
'critical' => true,
],
'hook_entity_delete' => [
'replacement' => 'hook_ENTITY_TYPE_delete',
'version' => '8.7.0',
'critical' => true,
],
// File hooks
'hook_file_download' => [
'replacement' => 'hook_file_download_access',
'version' => '10.0.0',
'critical' => false,
],
// Theme hooks
'hook_theme_registry_alter' => [
'replacement' => 'hook_theme_registry_info_alter',
'version' => '10.0.0',
'critical' => false,
],
// Token hooks
'hook_tokens' => [
'replacement' => 'hook_token_info and hook_tokens_alter',
'version' => '10.0.0',
'critical' => false,
],
// Field hooks
'hook_field_widget_form_alter' => [
'replacement' => 'hook_field_widget_complete_form_alter',
'version' => '10.0.0',
'critical' => false,
],
// Views hooks
'hook_views_pre_render' => [
'replacement' => 'hook_views_pre_build',
'version' => '10.0.0',
'critical' => false,
],
// Form hooks
'hook_form_system_theme_settings_alter' => [
'replacement' => 'hook_form_system_theme_settings_alter',
'version' => '10.0.0',
'critical' => false,
],
// Block hooks
'hook_block_view_alter' => [
'replacement' => 'hook_block_build_alter',
'version' => '10.0.0',
'critical' => false,
],
];
/**
* List of hooks that require special attention in Drupal 11.
*
* @var array
*/
protected $drupal11Hooks = [
'hook_update_N' => [
'attention' => 'Ensure numeric suffix follows Drupal 11 version numbering',
'example' => 'mymodule_update_110001()',
],
'hook_install' => [
'attention' => 'Check for deprecated install functions',
'example' => 'Use \Drupal::configFactory() instead of update_variable_set()',
],
'hook_uninstall' => [
'attention' => 'Check for deprecated uninstall functions',
'example' => 'Use State API instead of variable_del()',
],
'hook_requirements' => [
'attention' => 'Update version requirements for Drupal 11',
'example' => "return ['php' => ['value' => PHP_VERSION, 'minimum' => '8.1.0']]",
],
];
/**
* Constructs a new HookVisitor.
*
* @param string $module_name
* The module name.
*/
public function __construct($module_name) {
$this->moduleName = $module_name;
}
/**
* {@inheritdoc}
*/
public function enterNode(Node $node) {
if ($node instanceof Node\Stmt\Function_) {
$name = $node->name->toString();
// Check if this is a hook implementation
if (strpos($name, $this->moduleName . '_') === 0) {
$hook_name = str_replace($this->moduleName . '_', 'hook_', $name);
$finding = [
'type' => 'hook',
'name' => $name,
'hook' => $hook_name,
'line' => $node->getLine(),
'file' => $node->getAttribute('file'),
'params' => array_map(function($param) {
return $param->var->name;
}, $node->params),
];
// Check if this is a deprecated hook
if (isset($this->deprecatedHooks[$hook_name])) {
$finding['deprecated'] = true;
$finding['replacement'] = $this->deprecatedHooks[$hook_name]['replacement'];
$finding['version'] = $this->deprecatedHooks[$hook_name]['version'];
$finding['critical'] = $this->deprecatedHooks[$hook_name]['critical'];
}
// Check if this hook needs special attention in Drupal 11
if (isset($this->drupal11Hooks[$hook_name])) {
$finding['drupal11_attention'] = true;
$finding['attention_note'] = $this->drupal11Hooks[$hook_name]['attention'];
$finding['example'] = $this->drupal11Hooks[$hook_name]['example'];
}
// Check for update hooks specifically
if (preg_match('/^' . preg_quote($this->moduleName) . '_update_\d+$/', $name)) {
$finding['is_update_hook'] = true;
$number = substr($name, strlen($this->moduleName) + 7);
if (strlen($number) !== 6 || substr($number, 0, 2) !== '11') {
$finding['update_hook_warning'] = 'Update hook number should be 6 digits starting with 11 for Drupal 11 (e.g., 110001)';
}
}
$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 = [];
}
}
