ai_upgrade_assistant-0.2.0-alpha2/src/Service/UpgradePatternManager.php
src/Service/UpgradePatternManager.php
<?php
namespace Drupal\ai_upgrade_assistant\Service;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\State\StateInterface;
use Drupal\ai_upgrade_assistant\Service\PhpParserService;
/**
* Service for managing and analyzing Drupal upgrade patterns.
*/
class UpgradePatternManager {
/**
* The PHP parser service.
*
* @var \Drupal\ai_upgrade_assistant\Service\PhpParserService
*/
protected $phpParser;
/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The cache backend.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* The logger factory.
*
* @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
*/
protected $loggerFactory;
/**
* Pattern analysis cache.
*
* @var array
*/
protected $patternCache = [];
/**
* Constructs a new UpgradePatternManager.
*
* @param \Drupal\ai_upgrade_assistant\Service\PhpParserService $php_parser
* The PHP parser service.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* The cache backend.
* @param \Drupal\Core\State\StateInterface $state
* The state service.
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
* The logger factory service.
*/
public function __construct(
PhpParserService $php_parser,
ConfigFactoryInterface $config_factory,
ModuleHandlerInterface $module_handler,
CacheBackendInterface $cache,
StateInterface $state,
LoggerChannelFactoryInterface $logger_factory
) {
$this->phpParser = $php_parser;
$this->configFactory = $config_factory;
$this->moduleHandler = $module_handler;
$this->cache = $cache;
$this->state = $state;
$this->loggerFactory = $logger_factory;
}
/**
* Analyzes a module for upgrade patterns.
*
* @param string $module_name
* The name of the module to analyze.
*
* @return array
* Analysis results containing all detected patterns.
*/
public function analyzeModule($module_name) {
$cid = "ai_upgrade_assistant:pattern_analysis:{$module_name}";
if ($cached = $this->cache->get($cid)) {
return $cached->data;
}
$module_path = $this->moduleHandler->getModule($module_name)->getPath();
$files = $this->findPhpFiles($module_path);
$analysis = [
'module' => $module_name,
'files_analyzed' => count($files),
'patterns' => [
'version_specific' => [],
'database' => [],
'config' => [],
'entity' => [],
'form' => [],
'routing' => [],
'service' => [],
'theme' => [],
'cache' => [],
'access' => [],
'batch' => [],
'migration' => [],
],
'statistics' => [
'deprecated_items' => 0,
'upgrade_required' => false,
'complexity_score' => 0,
'risk_level' => 'low',
],
];
foreach ($files as $file) {
$file_analysis = $this->phpParser->analyzeFile($file, $module_name);
$this->mergeFileAnalysis($analysis, $file_analysis);
}
// Calculate complexity and risk metrics
$this->calculateMetrics($analysis);
// Cache the results
$this->cache->set($cid, $analysis, time() + 86400);
return $analysis;
}
/**
* Finds all PHP files in a directory recursively.
*
* @param string $directory
* Directory to search in.
*
* @return array
* Array of PHP file paths.
*/
protected function findPhpFiles($directory) {
$files = [];
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::SKIP_DOTS)
);
foreach ($iterator as $file) {
if ($file->getExtension() === 'php') {
$files[] = $file->getPathname();
}
}
return $files;
}
/**
* Merges file analysis results into module analysis.
*
* @param array &$module_analysis
* Module analysis array to merge into.
* @param array $file_analysis
* File analysis to merge.
*/
protected function mergeFileAnalysis(array &$module_analysis, array $file_analysis) {
if ($file_analysis['status'] === 'error') {
return;
}
// Merge pattern findings
foreach ($file_analysis as $pattern_type => $findings) {
if (isset($module_analysis['patterns'][$pattern_type])) {
$module_analysis['patterns'][$pattern_type] = array_merge(
$module_analysis['patterns'][$pattern_type],
$findings
);
}
}
// Update statistics
if (!empty($file_analysis['deprecated_functions'])) {
$module_analysis['statistics']['deprecated_items'] += count($file_analysis['deprecated_functions']);
}
}
/**
* Calculates complexity and risk metrics for the analysis.
*
* @param array &$analysis
* Analysis array to update with metrics.
*/
protected function calculateMetrics(array &$analysis) {
$complexity_factors = [
'deprecated_items' => 2,
'database_patterns' => 1.5,
'entity_patterns' => 1.2,
'service_patterns' => 1,
'routing_patterns' => 0.8,
'config_patterns' => 0.7,
];
$complexity_score = 0;
foreach ($complexity_factors as $pattern => $weight) {
if (isset($analysis['patterns'][$pattern])) {
$complexity_score += count($analysis['patterns'][$pattern]) * $weight;
}
}
$analysis['statistics']['complexity_score'] = round($complexity_score, 2);
// Determine risk level
if ($complexity_score > 50) {
$analysis['statistics']['risk_level'] = 'high';
}
elseif ($complexity_score > 20) {
$analysis['statistics']['risk_level'] = 'medium';
}
// Determine if upgrade is required
$analysis['statistics']['upgrade_required'] =
$analysis['statistics']['deprecated_items'] > 0 ||
!empty($analysis['patterns']['version_specific']);
}
/**
* Gets upgrade recommendations based on pattern analysis.
*
* @param array $analysis
* Pattern analysis results.
*
* @return array
* Upgrade recommendations.
*/
public function getUpgradeRecommendations(array $analysis) {
$recommendations = [];
// Version-specific recommendations
if (!empty($analysis['patterns']['version_specific'])) {
foreach ($analysis['patterns']['version_specific'] as $pattern) {
$recommendations[] = [
'priority' => 'high',
'type' => 'version_compatibility',
'description' => sprintf(
'Replace %s with %s (required for version %s)',
$pattern['name'],
$pattern['replacement'],
$pattern['version']
),
'line' => $pattern['line'],
];
}
}
// Database API recommendations
if (!empty($analysis['patterns']['database'])) {
$recommendations[] = [
'priority' => 'medium',
'type' => 'database_api',
'description' => 'Review database queries for compatibility with latest Database API',
'count' => count($analysis['patterns']['database']),
];
}
// Entity API recommendations
if (!empty($analysis['patterns']['entity'])) {
$recommendations[] = [
'priority' => 'medium',
'type' => 'entity_api',
'description' => 'Update Entity API usage to latest practices',
'count' => count($analysis['patterns']['entity']),
];
}
// Service container recommendations
if (!empty($analysis['patterns']['service'])) {
$recommendations[] = [
'priority' => 'medium',
'type' => 'dependency_injection',
'description' => 'Convert static service calls to dependency injection',
'count' => count($analysis['patterns']['service']),
];
}
return $recommendations;
}
/**
* Gets detailed pattern information for a specific pattern type.
*
* @param string $module_name
* The module name.
* @param string $pattern_type
* The pattern type to get details for.
*
* @return array
* Detailed pattern information.
*/
public function getPatternDetails($module_name, $pattern_type) {
$analysis = $this->analyzeModule($module_name);
if (!isset($analysis['patterns'][$pattern_type])) {
return [];
}
return [
'type' => $pattern_type,
'count' => count($analysis['patterns'][$pattern_type]),
'patterns' => $analysis['patterns'][$pattern_type],
'impact' => $this->calculatePatternImpact($pattern_type, $analysis),
];
}
/**
* Calculates the impact score for a specific pattern type.
*
* @param string $pattern_type
* The pattern type.
* @param array $analysis
* The full analysis results.
*
* @return array
* Impact assessment.
*/
protected function calculatePatternImpact($pattern_type, array $analysis) {
$impact_weights = [
'version_specific' => 1.0,
'database' => 0.8,
'entity' => 0.7,
'service' => 0.6,
'config' => 0.5,
'routing' => 0.4,
'theme' => 0.3,
'cache' => 0.3,
'access' => 0.3,
'batch' => 0.2,
'migration' => 0.4,
];
$weight = $impact_weights[$pattern_type] ?? 0.5;
$count = count($analysis['patterns'][$pattern_type]);
$impact_score = $count * $weight;
return [
'score' => round($impact_score, 2),
'level' => $impact_score > 5 ? 'high' : ($impact_score > 2 ? 'medium' : 'low'),
'weight' => $weight,
];
}
}
