Skip to content

Commit 55c9fc5

Browse files
authored
Merge pull request #109 from agustingomes/gh-100/add-enum-generation
Add initial use cases for Enum generation
2 parents 7fcc8ea + 5e6e3d0 commit 55c9fc5

File tree

10 files changed

+612
-96
lines changed

10 files changed

+612
-96
lines changed

composer.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@
3636
"autoload": {
3737
"psr-4": {
3838
"Laminas\\Code\\": "src/"
39-
}
39+
},
40+
"files": [
41+
"polyfill/ReflectionEnumPolyfill.php"
42+
]
4043
},
4144
"autoload-dev": {
4245
"psr-4": {

polyfill/ReflectionEnumPolyfill.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
if (\PHP_VERSION_ID >= 80100) {
4+
return;
5+
}
6+
7+
/** @internal */
8+
final class ReflectionEnum
9+
{
10+
public function __construct($enum)
11+
{
12+
}
13+
}
14+

psalm-baseline.xml

Lines changed: 42 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,9 @@
7777
<MixedArgumentTypeCoercion occurrences="1">
7878
<code>$name</code>
7979
</MixedArgumentTypeCoercion>
80-
<MixedAssignment occurrences="3">
80+
<MixedAssignment occurrences="2">
8181
<code>$use</code>
8282
<code>$value</code>
83-
<code>$value</code>
8483
</MixedAssignment>
8584
<MixedOperand occurrences="3">
8685
<code>$use</code>
@@ -258,6 +257,47 @@
258257
<code>new static()</code>
259258
</UnsafeInstantiation>
260259
</file>
260+
<file src="src/Generator/EnumGenerator/Cases/CaseFactory.php">
261+
<ArgumentTypeCoercion occurrences="1">
262+
<code>$pureCases</code>
263+
</ArgumentTypeCoercion>
264+
<MixedArgument occurrences="2">
265+
<code>$backingType-&gt;getName()</code>
266+
<code>$enum-&gt;getCases()</code>
267+
</MixedArgument>
268+
<MixedArgumentTypeCoercion occurrences="1">
269+
<code>$backedCases</code>
270+
</MixedArgumentTypeCoercion>
271+
<MixedArrayOffset occurrences="1">
272+
<code>$backedCases[$singleCase-&gt;getName()]</code>
273+
</MixedArrayOffset>
274+
<MixedAssignment occurrences="3">
275+
<code>$backedCases[$singleCase-&gt;getName()]</code>
276+
<code>$backingType</code>
277+
<code>$singleCase</code>
278+
</MixedAssignment>
279+
<MixedMethodCall occurrences="3">
280+
<code>getBackingValue</code>
281+
<code>getName</code>
282+
<code>getName</code>
283+
</MixedMethodCall>
284+
<UndefinedClass occurrences="1">
285+
<code>ReflectionEnumUnitCase</code>
286+
</UndefinedClass>
287+
<UndefinedMethod occurrences="3">
288+
<code>getBackingType</code>
289+
<code>getCases</code>
290+
<code>getCases</code>
291+
</UndefinedMethod>
292+
</file>
293+
<file src="src/Generator/EnumGenerator/EnumGenerator.php">
294+
<MixedArgument occurrences="1">
295+
<code>$enum-&gt;getName()</code>
296+
</MixedArgument>
297+
<UndefinedMethod occurrences="1">
298+
<code>getName</code>
299+
</UndefinedMethod>
300+
</file>
261301
<file src="src/Generator/FileGenerator.php">
262302
<DocblockTypeContradiction occurrences="2">
263303
<code>$class instanceof ClassGenerator</code>
@@ -1133,99 +1173,6 @@
11331173
<code>FooClass</code>
11341174
<code>FooClass</code>
11351175
</MissingDependency>
1136-
<MissingReturnType occurrences="91">
1137-
<code>testAbstractAccessors</code>
1138-
<code>testAddConstantAcceptsMixedScalars</code>
1139-
<code>testAddConstantRejectsArrayWithInvalidNestedValue</code>
1140-
<code>testAddConstantRejectsObjectConstantValue</code>
1141-
<code>testAddConstantRejectsResourceConstantValue</code>
1142-
<code>testAddConstantThrowsExceptionOnDuplicate</code>
1143-
<code>testAddConstantThrowsExceptionWithEmptyConstantName</code>
1144-
<code>testAddConstantThrowsExceptionWithInvalidName</code>
1145-
<code>testAddOneUseTwiceOnlyAddsOne</code>
1146-
<code>testAddOneUseWithAliasTwiceOnlyAddsOne</code>
1147-
<code>testAddPropertiesIsBackwardsCompatibleWithConstants</code>
1148-
<code>testAddPropertyIsBackwardsCompatibleWithConstants</code>
1149-
<code>testAddTraitAliasExceptionInvalidAliasArgument</code>
1150-
<code>testAddTraitAliasExceptionInvalidMethodFormat</code>
1151-
<code>testAddTraitAliasExceptionInvalidMethodTraitDoesNotExist</code>
1152-
<code>testAddTraitAliasExceptionInvalidVisibilityValue</code>
1153-
<code>testAddTraitAliasExceptionMethodAlreadyExists</code>
1154-
<code>testAddTraitOverrideExceptionInvalidMethodArgInArray</code>
1155-
<code>testAddTraitOverrideExceptionInvalidMethodFormat</code>
1156-
<code>testAddTraitOverrideExceptionInvalidMethodTraitDoesNotExist</code>
1157-
<code>testAddTraitOverrideExceptionInvalidTraitName</code>
1158-
<code>testAddTraitOverrideExceptionInvalidTraitToReplaceArgument</code>
1159-
<code>testAddUses</code>
1160-
<code>testCanAddConstant</code>
1161-
<code>testCanAddConstantsWithArrayOfGenerators</code>
1162-
<code>testCanAddConstantsWithArrayOfKeyValues</code>
1163-
<code>testCanAddMultipleTraitOverrides</code>
1164-
<code>testCanAddTraitAliasWithArray</code>
1165-
<code>testCanAddTraitAliasWithString</code>
1166-
<code>testCanAddTraitOverride</code>
1167-
<code>testCanAddTraitWithArray</code>
1168-
<code>testCanAddTraitWithString</code>
1169-
<code>testCanGetTraitsMethod</code>
1170-
<code>testCanRemoveAllTraitOverrides</code>
1171-
<code>testCanRemoveTrait</code>
1172-
<code>testCanRemoveTraitOverride</code>
1173-
<code>testClassCanBeGeneratedWithConstantAndPropertyWithSameName</code>
1174-
<code>testClassDocBlockAccessors</code>
1175-
<code>testClassFromReflectionDiscardParentImplementedInterfaces</code>
1176-
<code>testClassFromReflectionThatImplementsInterfaces</code>
1177-
<code>testCodeGenerationShouldTakeIntoAccountNamespacesFromReflection</code>
1178-
<code>testConstantsAddedFromReflection</code>
1179-
<code>testConstruction</code>
1180-
<code>testCorrectExtendNames</code>
1181-
<code>testCorrectExtendNamesFromGlobalNamespace</code>
1182-
<code>testCorrectImplementNames</code>
1183-
<code>testCorrectlyExtendsAliasOfProvidedFQCNIfUseAliasExists</code>
1184-
<code>testCorrectlyExtendsFullyQualifiedParentClass</code>
1185-
<code>testCorrectlyExtendsProvidedAliasIfUseAliasExists</code>
1186-
<code>testCorrectlyExtendsProvidedNamespaceAliasIfUseAliasExistsForNamespace</code>
1187-
<code>testCorrectlyExtendsRelativeParentClass</code>
1188-
<code>testCorrectlyExtendsWithNamespaceAliasOfProvidedFQCNIfUseAliasExistsForNamespace</code>
1189-
<code>testCreateFromArrayWithDocBlockFromArray</code>
1190-
<code>testCreateFromArrayWithDocBlockInstance</code>
1191-
<code>testExtendedClassAccessors</code>
1192-
<code>testExtendedClassProperies</code>
1193-
<code>testGenerateClassAndAddMethod</code>
1194-
<code>testGenerateWithFinalFlag</code>
1195-
<code>testHasExtendedClass</code>
1196-
<code>testHasImplementedInterface</code>
1197-
<code>testHasMethod</code>
1198-
<code>testHasMethodInsensitive</code>
1199-
<code>testHasProperty</code>
1200-
<code>testHasUse</code>
1201-
<code>testHasUseAlias</code>
1202-
<code>testHereDoc</code>
1203-
<code>testImplementedInterfacesAccessors</code>
1204-
<code>testMethodAccessors</code>
1205-
<code>testNameAccessors</code>
1206-
<code>testNonNamespaceClassReturnsAllMethods</code>
1207-
<code>testPassingANamespacedClassnameShouldGenerateAClassnameWithoutItsNamespace</code>
1208-
<code>testPassingANamespacedClassnameShouldGenerateANamespaceDeclaration</code>
1209-
<code>testPropertyAccessors</code>
1210-
<code>testRemoveConstant</code>
1211-
<code>testRemoveExtendedClass</code>
1212-
<code>testRemoveImplementedInterface</code>
1213-
<code>testRemoveMethod</code>
1214-
<code>testRemoveMethodInsensitive</code>
1215-
<code>testRemoveProperty</code>
1216-
<code>testRemoveUse</code>
1217-
<code>testRemoveUseAlias</code>
1218-
<code>testSetMethodNameAlreadyExistsThrowsException</code>
1219-
<code>testSetMethodNoMethodOrArrayThrowsException</code>
1220-
<code>testSetNameShouldDetermineIfNamespaceSegmentIsPresent</code>
1221-
<code>testSetPropertyAlreadyExistsThrowsException</code>
1222-
<code>testSetPropertyNoArrayOrPropertyThrowsException</code>
1223-
<code>testSetextendedclassShouldIgnoreEmptyClassnameOnGenerate</code>
1224-
<code>testSetextendedclassShouldNotIgnoreNonEmptyClassnameOnGenerate</code>
1225-
<code>testToString</code>
1226-
<code>testTraitGenerationWithAliasesAndOverrides</code>
1227-
<code>testUseTraitGeneration</code>
1228-
</MissingReturnType>
12291176
<MixedArgument occurrences="6">
12301177
<code>$overrides['myTrait::foo']</code>
12311178
<code>$overrides['myTrait::foo']</code>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
namespace Laminas\Code\Generator\EnumGenerator\Cases;
4+
5+
use InvalidArgumentException;
6+
7+
use function in_array;
8+
use function sprintf;
9+
10+
/**
11+
* @internal
12+
*
13+
* @psalm-immutable
14+
*/
15+
final class BackedCases
16+
{
17+
private string $type;
18+
19+
/** @var list<non-empty-string> */
20+
private array $cases;
21+
22+
/**
23+
* @param list<non-empty-string> $cases
24+
*/
25+
private function __construct(string $type, array $cases)
26+
{
27+
$this->type = $type;
28+
$this->cases = $cases;
29+
}
30+
31+
public function getBackedType(): string
32+
{
33+
return $this->type;
34+
}
35+
36+
/**
37+
* @return list<string>
38+
*/
39+
public function getCases(): array
40+
{
41+
return $this->cases;
42+
}
43+
44+
/**
45+
* @param array<non-empty-string, int>|array<non-empty-string, non-empty-string> $backedCases
46+
*/
47+
public static function fromCasesWithType(array $backedCases, string $type): self
48+
{
49+
if (in_array($type, ['int', 'string']) === false) {
50+
throw new InvalidArgumentException(sprintf(
51+
'"%s" is not a valid type for Enums, only "int" and "string" types are allowed.',
52+
$type
53+
));
54+
}
55+
56+
$cases = [];
57+
foreach ($backedCases as $case => $value) {
58+
if ($type === 'string') {
59+
$value = sprintf("'%s'", $value);
60+
}
61+
62+
$cases[] = $case . ' = ' . $value;
63+
}
64+
65+
return new self($type, $cases);
66+
}
67+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
namespace Laminas\Code\Generator\EnumGenerator\Cases;
4+
5+
use InvalidArgumentException;
6+
use ReflectionEnum;
7+
use ReflectionEnumBackedCase;
8+
use ReflectionEnumUnitCase;
9+
10+
use function array_key_exists;
11+
use function array_map;
12+
use function assert;
13+
14+
use const PHP_VERSION_ID;
15+
16+
/** @internal */
17+
final class CaseFactory
18+
{
19+
/**
20+
* @psalm-param array{
21+
* name: non-empty-string,
22+
* pureCases: list<non-empty-string>,
23+
* }|array{
24+
* name: non-empty-string,
25+
* backedCases: array{
26+
* type: 'int',
27+
* cases: array<non-empty-string, int>,
28+
* }|array{
29+
* type: 'string',
30+
* cases: array<non-empty-string, non-empty-string>,
31+
* },
32+
* } $options
33+
* @return BackedCases|PureCases
34+
*/
35+
public static function fromOptions(array $options)
36+
{
37+
if (array_key_exists('pureCases', $options) && ! array_key_exists('backedCases', $options)) {
38+
return PureCases::fromCases($options['pureCases']);
39+
}
40+
41+
assert(! array_key_exists('pureCases', $options) && array_key_exists('backedCases', $options));
42+
return BackedCases::fromCasesWithType($options['backedCases']['cases'], $options['backedCases']['type']);
43+
}
44+
45+
/**
46+
* @return BackedCases|PureCases
47+
*/
48+
public static function fromReflectionCases(ReflectionEnum $enum)
49+
{
50+
if (PHP_VERSION_ID < 80100) {
51+
throw new InvalidArgumentException('This feature only works from PHP 8.1 onwards.');
52+
}
53+
54+
$backingType = $enum->getBackingType();
55+
56+
if ($backingType === null) {
57+
$callback = static fn(ReflectionEnumUnitCase $singleCase): string => $singleCase->getName();
58+
$pureCases = array_map($callback, $enum->getCases());
59+
60+
return PureCases::fromCases($pureCases);
61+
}
62+
63+
$backedCases = [];
64+
foreach ($enum->getCases() as $singleCase) {
65+
$backedCases[$singleCase->getName()] = $singleCase->getBackingValue();
66+
}
67+
68+
return BackedCases::fromCasesWithType($backedCases, $backingType->getName());
69+
}
70+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace Laminas\Code\Generator\EnumGenerator\Cases;
4+
5+
/**
6+
* @internal
7+
*
8+
* @psalm-immutable
9+
*/
10+
final class PureCases
11+
{
12+
/** @var list<non-empty-string> */
13+
private array $cases;
14+
15+
/**
16+
* @param list<non-empty-string> $cases
17+
*/
18+
private function __construct(array $cases)
19+
{
20+
$this->cases = $cases;
21+
}
22+
23+
/**
24+
* @return list<string>
25+
*/
26+
public function getCases(): array
27+
{
28+
return $this->cases;
29+
}
30+
31+
/**
32+
* @param list<non-empty-string> $pureCases
33+
*/
34+
public static function fromCases(array $pureCases): self
35+
{
36+
return new self($pureCases);
37+
}
38+
}

0 commit comments

Comments
 (0)