#!/usr/bin/env php 0, 'files_updated' => 0, 'annotations_converted' => 0 ]; // Annotation mapping to attributes $annotationMap = [ '@test' => '#[Test]', '@group\s+(\w+)' => '#[Group("$1")]', '@depends\s+(\w+)' => '#[Depends("$1")]', '@dataProvider\s+(\w+)' => '#[DataProvider("$1")]', '@covers\s+(.+)' => '#[CoversClass($1)]', '@coversDefaultClass\s+(.+)' => '#[CoversClass($1)]' ]; // Required imports for attributes $requiredImports = [ 'use PHPUnit\\Framework\\Attributes\\Test;', 'use PHPUnit\\Framework\\Attributes\\Group;', 'use PHPUnit\\Framework\\Attributes\\Depends;', 'use PHPUnit\\Framework\\Attributes\\DataProvider;', 'use PHPUnit\\Framework\\Attributes\\CoversClass;' ]; function processTestDirectory($dir, &$stats, $annotationMap, $requiredImports) { if (!is_dir($dir)) { echo "Directory not found: $dir\n"; return; } echo "Processing directory: $dir\n"; $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($dir) ); foreach ($iterator as $file) { if ($file->getExtension() === 'php' && strpos($file->getFilename(), 'Test.php') !== false) { processTestFile($file->getPathname(), $stats, $annotationMap, $requiredImports); } } } function processTestFile($filePath, &$stats, $annotationMap, $requiredImports) { $stats['files_processed']++; echo "Processing: " . basename($filePath) . " "; $content = file_get_contents($filePath); if (!$content) { echo "[SKIP]\n"; return; } $originalContent = $content; $annotationsFound = false; // Convert annotations to attributes foreach ($annotationMap as $pattern => $replacement) { if ($pattern === '@test') { // Handle simple @test annotation $newContent = preg_replace('/^\s*\*\s*@test\s*$/m', '', $content); if ($newContent !== $content) { $content = $newContent; // Add attribute before method $content = preg_replace('/(\s+)(public function \w+\(\))/', '$1#[Test]' . "\n" . '$1$2', $content); $annotationsFound = true; $stats['annotations_converted']++; } } else { // Handle other annotations $count = 0; $content = preg_replace('/^\s*\*\s*' . $pattern . '\s*$/m', '', $content, -1, $count); if ($count > 0) { $annotationsFound = true; $stats['annotations_converted'] += $count; } } } // Add required imports if annotations were found if ($annotationsFound) { // Check if imports already exist foreach ($requiredImports as $import) { if (strpos($content, $import) === false) { // Find the namespace declaration and add imports after it $content = preg_replace( '/(namespace\s+[^;]+;)/', '$1' . "\n\n" . $import, $content, 1 ); } } file_put_contents($filePath, $content); $stats['files_updated']++; echo "[UPDATED]\n"; } else { echo "[NO CHANGES]\n"; } } // Process all test directories foreach ($testDirs as $dir) { if (is_dir($dir)) { processTestDirectory($dir, $stats, $annotationMap, $requiredImports); } } // Report results echo "\n🎯 MIGRATION RESULTS:\n"; echo "Files processed: {$stats['files_processed']}\n"; echo "Files updated: {$stats['files_updated']}\n"; echo "Annotations converted: {$stats['annotations_converted']}\n"; if ($stats['files_updated'] > 0) { echo "\n✅ SUCCESS: PHPUnit annotations migrated to PHP 8.4 attributes\n"; echo "🔒 SECURITY: Ready for PHP 8.4 migration (EOL vulnerability fix)\n"; } else { echo "\n⚠️ No annotations found to convert\n"; } echo "\nNext steps:\n"; echo "1. Test with: composer install (PHP 8.4 + PHPUnit 12)\n"; echo "2. Run tests: ./vendor/bin/phpunit\n"; echo "3. Deploy to staging environment\n"; exit(0);