From d3f104382d6eaa38caca03f6df34c2022dd02e80 Mon Sep 17 00:00:00 2001 From: Loz Calver Date: Tue, 19 Jul 2022 17:02:20 +0100 Subject: [PATCH] NEW: Add support for autoloading PHP 8.1 enums --- src/Core/Manifest/ClassContentRemover.php | 11 +++- src/Core/Manifest/ClassManifest.php | 65 ++++++++++++++++--- src/Core/Manifest/ClassManifestVisitor.php | 17 +++++ tests/php/Core/Manifest/ClassManifestTest.php | 38 ++++++++++- .../classmanifest/module/enums/EnumA.php | 5 ++ .../classmanifest/module/enums/EnumB.php | 5 ++ 6 files changed, 128 insertions(+), 13 deletions(-) create mode 100644 tests/php/Core/Manifest/fixtures/classmanifest/module/enums/EnumA.php create mode 100644 tests/php/Core/Manifest/fixtures/classmanifest/module/enums/EnumB.php diff --git a/src/Core/Manifest/ClassContentRemover.php b/src/Core/Manifest/ClassContentRemover.php index 06e0bc8a4..e606d1c4f 100644 --- a/src/Core/Manifest/ClassContentRemover.php +++ b/src/Core/Manifest/ClassContentRemover.php @@ -6,7 +6,7 @@ namespace SilverStripe\Core\Manifest; * Class ClassContentRemover * @package SilverStripe\Core\Manifest * - * A utility class to clean the contents of a PHP file containing classes. + * A utility class to clean the contents of a PHP file containing classes/interfaces/traits/enums. * * It removes any code with in `$cut_off_depth` number of curly braces. */ @@ -29,7 +29,7 @@ class ClassContentRemover return $contents; } - if (!preg_match('/\b(?:class|interface|trait)/i', $contents ?? '')) { + if (!preg_match('/\b(?:class|interface|trait|enum)/i', $contents ?? '')) { return ''; } @@ -50,7 +50,12 @@ class ClassContentRemover } // only count if we see a class/interface/trait keyword - if (!$startCounting && in_array($token[0], [T_CLASS, T_INTERFACE, T_TRAIT])) { + $targetTokens = [T_CLASS, T_INTERFACE, T_TRAIT]; + if (version_compare(phpversion(), '8.1.0', '>')) { + $targetTokens[] = T_ENUM; + } + + if (!$startCounting && in_array($token[0], $targetTokens)) { $startCounting = true; } diff --git a/src/Core/Manifest/ClassManifest.php b/src/Core/Manifest/ClassManifest.php index b871a3465..f476e76e9 100644 --- a/src/Core/Manifest/ClassManifest.php +++ b/src/Core/Manifest/ClassManifest.php @@ -67,6 +67,8 @@ class ClassManifest 'implementors', 'traits', 'traitNames', + 'enums', + 'enumNames', ]; /** @@ -150,6 +152,20 @@ class ClassManifest */ protected $traitNames = []; + /** + * Map of lowercase enum names to paths + * + * @var array + */ + protected $enums = []; + + /** + * Map of lowercase enum names to proper case + * + * @var array + */ + protected $enumNames = []; + /** * PHP Parser for parsing found files * @@ -334,10 +350,11 @@ class ClassManifest { $lowerName = strtolower($name ?? ''); foreach ([ - $this->classes, - $this->interfaces, - $this->traits, - ] as $source) { + $this->classes, + $this->interfaces, + $this->traits, + $this->enums, + ] as $source) { if (isset($source[$lowerName]) && file_exists($source[$lowerName] ?? '')) { return $source[$lowerName]; } @@ -355,10 +372,11 @@ class ClassManifest { $lowerName = strtolower($name ?? ''); foreach ([ - $this->classNames, - $this->interfaceNames, - $this->traitNames, - ] as $source) { + $this->classNames, + $this->interfaceNames, + $this->traitNames, + $this->enumNames, + ] as $source) { if (isset($source[$lowerName])) { return $source[$lowerName]; } @@ -406,6 +424,26 @@ class ClassManifest return $this->traitNames; } + /** + * Returns a map of lowercased enum names to file paths. + * + * @return array + */ + public function getEnums() + { + return $this->enums; + } + + /** + * Returns a map of lowercase enum names to proper enum names in the manifest + * + * @return array + */ + public function getEnumNames() + { + return $this->enumNames; + } + /** * Returns an array of all the descendant data. * @@ -562,6 +600,7 @@ class ClassManifest $classes = $data['classes']; $interfaces = $data['interfaces']; $traits = $data['traits']; + $enums = $data['enums']; } else { $changed = true; // Build from php file parser @@ -579,6 +618,7 @@ class ClassManifest $classes = $this->getVisitor()->getClasses(); $interfaces = $this->getVisitor()->getInterfaces(); $traits = $this->getVisitor()->getTraits(); + $enums = $this->getVisitor()->getEnums(); } // Merge raw class data into global list @@ -641,6 +681,13 @@ class ClassManifest $this->traitNames[$lowerTrait] = $traitName; } + // Merge all enums + foreach ($enums as $enumName => $enumInfo) { + $lowerEnum = strtolower($enumName ?? ''); + $this->enums[$lowerEnum] = $pathname; + $this->enumNames[$lowerEnum] = $enumName; + } + // Save back to cache if configured if ($changed && $this->cache) { $cache = [ @@ -725,7 +772,7 @@ class ClassManifest if (!$data || !is_array($data)) { return false; } - foreach (['classes', 'interfaces', 'traits'] as $key) { + foreach (['classes', 'interfaces', 'traits', 'enums'] as $key) { // Must be set if (!isset($data[$key])) { return false; diff --git a/src/Core/Manifest/ClassManifestVisitor.php b/src/Core/Manifest/ClassManifestVisitor.php index 9fa6a6f32..f4508ca6e 100644 --- a/src/Core/Manifest/ClassManifestVisitor.php +++ b/src/Core/Manifest/ClassManifestVisitor.php @@ -15,10 +15,20 @@ class ClassManifestVisitor extends NodeVisitorAbstract private $interfaces = []; + private bool $includeEnums; + + private $enums = []; + + public function __construct() + { + $this->includeEnums = version_compare(phpversion(), '8.1.0', '>'); + } + public function resetState() { $this->classes = []; $this->traits = []; + $this->enums = []; $this->interfaces = []; } @@ -49,6 +59,8 @@ class ClassManifestVisitor extends NodeVisitorAbstract ]; } elseif ($node instanceof Node\Stmt\Trait_) { $this->traits[(string)$node->namespacedName] = []; + } elseif ($this->includeEnums && $node instanceof Node\Stmt\Enum_) { + $this->enums[(string)$node->namespacedName] = []; } elseif ($node instanceof Node\Stmt\Interface_) { $extends = []; foreach ($node->extends as $ancestor) { @@ -74,6 +86,11 @@ class ClassManifestVisitor extends NodeVisitorAbstract return $this->traits; } + public function getEnums() + { + return $this->enums; + } + public function getInterfaces() { return $this->interfaces; diff --git a/tests/php/Core/Manifest/ClassManifestTest.php b/tests/php/Core/Manifest/ClassManifestTest.php index 736f76732..fa895316e 100644 --- a/tests/php/Core/Manifest/ClassManifestTest.php +++ b/tests/php/Core/Manifest/ClassManifestTest.php @@ -43,7 +43,7 @@ class ClassManifestTest extends SapphireTest */ public function providerTestGetItemPath() { - return [ + $paths = [ ['CLASSA', 'module/classes/ClassA.php'], ['ClassA', 'module/classes/ClassA.php'], ['classa', 'module/classes/ClassA.php'], @@ -55,6 +55,14 @@ class ClassManifestTest extends SapphireTest ['VendorClassA', 'vendor/silverstripe/modulec/code/VendorClassA.php'], ['VendorTraitA', 'vendor/silverstripe/modulec/code/VendorTraitA.php'], ]; + + if (version_compare(phpversion(), '8.1.0', '>')) { + $paths[] = ['ENUMA', 'module/enums/EnumA.php']; + $paths[] = ['EnumA', 'module/enums/EnumA.php']; + $paths[] = ['enuma', 'module/enums/EnumA.php']; + } + + return $paths; } /** @@ -169,6 +177,34 @@ class ClassManifestTest extends SapphireTest } } + public function testGetEnums() + { + if (!version_compare(phpversion(), '8.1.0', '>')) { + $this->markTestSkipped('Enums are only available on PHP 8.1+'); + } + + $expect = [ + 'enuma' => "{$this->base}/module/enums/EnumA.php", + 'enumb' => "{$this->base}/module/enums/EnumB.php", + ]; + $this->assertEquals($expect, $this->manifest->getEnums()); + } + + public function testGetEnumNames() + { + if (!version_compare(phpversion(), '8.1.0', '>')) { + $this->markTestSkipped('Enums are only available on PHP 8.1+'); + } + + $this->assertEquals( + [ + 'enuma' => 'EnumA', + 'enumb' => 'EnumB', + ], + $this->manifest->getEnumNames() + ); + } + public function testTestManifestIncludesTestClasses() { $this->assertArrayNotHasKey('testclassa', $this->manifest->getClasses()); diff --git a/tests/php/Core/Manifest/fixtures/classmanifest/module/enums/EnumA.php b/tests/php/Core/Manifest/fixtures/classmanifest/module/enums/EnumA.php new file mode 100644 index 000000000..a1ba13816 --- /dev/null +++ b/tests/php/Core/Manifest/fixtures/classmanifest/module/enums/EnumA.php @@ -0,0 +1,5 @@ +