ai_upgrade_assistant-0.2.0-alpha2/src/Service/NodeVisitor/NamespaceVisitor.php
src/Service/NodeVisitor/NamespaceVisitor.php
<?php
namespace Drupal\ai_upgrade_assistant\Service\NodeVisitor;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
/**
* Node visitor that analyzes namespace usage and PSR-4 compliance.
*/
class NamespaceVisitor extends NodeVisitorAbstract {
/**
* List of findings.
*
* @var array
*/
protected $findings = [];
/**
* The module name.
*
* @var string
*/
protected $moduleName;
/**
* The module path.
*
* @var string
*/
protected $modulePath;
/**
* Constructs a NamespaceVisitor.
*
* @param string $module_name
* The module name.
* @param string $module_path
* The module path.
*/
public function __construct($module_name, $module_path) {
$this->moduleName = $module_name;
$this->modulePath = $module_path;
}
/**
* {@inheritdoc}
*/
public function enterNode(Node $node) {
if ($node instanceof Node\Stmt\Namespace_) {
$namespace = $node->name->toString();
$finding = [
'type' => 'namespace',
'name' => $namespace,
'line' => $node->getLine(),
'file' => $node->getAttribute('file'),
];
// Check PSR-4 compliance
$expected_namespace = $this->getExpectedNamespace($node->getAttribute('file'));
if ($namespace !== $expected_namespace) {
$finding['psr4_violation'] = true;
$finding['expected_namespace'] = $expected_namespace;
}
// Check for use statements
$use_statements = [];
foreach ($node->stmts as $stmt) {
if ($stmt instanceof Node\Stmt\Use_) {
foreach ($stmt->uses as $use) {
$use_name = $use->name->toString();
$use_statements[] = [
'name' => $use_name,
'alias' => $use->alias ? $use->alias->name : null,
'line' => $use->getLine(),
];
// Check for deprecated namespaces
if ($this->isDeprecatedNamespace($use_name)) {
$finding['deprecated_uses'][] = [
'name' => $use_name,
'replacement' => $this->getNamespaceReplacement($use_name),
'line' => $use->getLine(),
];
}
}
}
}
$finding['use_statements'] = $use_statements;
// Check for common namespace issues
$this->checkNamespaceIssues($finding);
$this->findings[] = $finding;
}
}
/**
* Gets the expected namespace based on file path.
*
* @param string $file_path
* The file path.
*
* @return string
* The expected namespace.
*/
protected function getExpectedNamespace($file_path) {
$relative_path = str_replace($this->modulePath . '/src/', '', $file_path);
$relative_path = str_replace('.php', '', $relative_path);
$parts = explode('/', $relative_path);
return 'Drupal\\' . $this->moduleName . '\\' . implode('\\', $parts);
}
/**
* Checks if a namespace is deprecated.
*
* @param string $namespace
* The namespace to check.
*
* @return bool
* TRUE if the namespace is deprecated.
*/
protected function isDeprecatedNamespace($namespace) {
$deprecated_namespaces = [
'Drupal\Core\Entity\EntityNG',
'Drupal\field\Plugin\Type',
'Drupal\system\Plugin\views\field',
'Drupal\views\Plugin\views\field',
];
foreach ($deprecated_namespaces as $deprecated) {
if (strpos($namespace, $deprecated) === 0) {
return true;
}
}
return false;
}
/**
* Gets the replacement for a deprecated namespace.
*
* @param string $namespace
* The deprecated namespace.
*
* @return string
* The replacement namespace.
*/
protected function getNamespaceReplacement($namespace) {
$replacements = [
'Drupal\Core\Entity\EntityNG' => 'Drupal\Core\Entity',
'Drupal\field\Plugin\Type' => 'Drupal\Core\Field',
'Drupal\system\Plugin\views\field' => 'Drupal\views\Plugin\views\field',
'Drupal\views\Plugin\views\field' => 'Drupal\views\Plugin\views\field',
];
foreach ($replacements as $old => $new) {
if (strpos($namespace, $old) === 0) {
return str_replace($old, $new, $namespace);
}
}
return $namespace;
}
/**
* Checks for common namespace issues.
*
* @param array &$finding
* The finding array to add issues to.
*/
protected function checkNamespaceIssues(array &$finding) {
$namespace = $finding['name'];
// Check for common namespace mistakes
if (strpos($namespace, '_') !== false) {
$finding['issues'][] = 'Namespace contains underscores, should use CamelCase';
}
if (strpos($namespace, '\\\\') !== false) {
$finding['issues'][] = 'Namespace contains double backslashes';
}
// Check for proper Drupal namespace prefix
if (strpos($namespace, 'Drupal\\') !== 0) {
$finding['issues'][] = 'Namespace should start with "Drupal\\"';
}
// Check for proper module namespace
$expected_prefix = 'Drupal\\' . $this->moduleName;
if (strpos($namespace, $expected_prefix) !== 0) {
$finding['issues'][] = "Namespace should start with \"$expected_prefix\"";
}
// Check for test namespaces
if (strpos($namespace, 'Drupal\Tests') === 0) {
if (!preg_match('/^Drupal\\\\Tests\\\\' . $this->moduleName . '\\\\(Unit|Kernel|Functional|FunctionalJavascript)\\\\/', $namespace)) {
$finding['issues'][] = 'Test namespace should include proper test type (Unit/Kernel/Functional/FunctionalJavascript)';
}
}
}
/**
* Gets the findings.
*
* @return array
* Array of findings.
*/
public function getFindings() {
return $this->findings;
}
/**
* Resets the findings.
*/
public function reset() {
$this->findings = [];
}
}
