NEW: Add support for autoloading PHP 8.1 enums

This commit is contained in:
Loz Calver 2022-07-19 17:02:20 +01:00 committed by Loz Calver
parent c0e8a21acf
commit d3f104382d
6 changed files with 128 additions and 13 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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());

View File

@ -0,0 +1,5 @@
<?php
/**
* @ignore
*/
enum EnumA { }

View File

@ -0,0 +1,5 @@
<?php
/**
* @ignore
*/
enum EnumB { }