-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: introduce enum extractor (#42)
- Loading branch information
Showing
5 changed files
with
237 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Cdn77\EntityFqnExtractor; | ||
|
||
use Cdn77\EntityFqnExtractor\Exception\EnumDefinitionInFileIsInvalid; | ||
|
||
use function count; | ||
use function ltrim; | ||
use function Safe\file_get_contents; | ||
use function token_get_all; | ||
|
||
use const T_ENUM; | ||
use const T_NAME_QUALIFIED; | ||
use const T_NAMESPACE; | ||
use const T_STRING; | ||
use const T_WHITESPACE; | ||
|
||
final class EnumExtractor | ||
{ | ||
/** @return list<class-string> */ | ||
public static function all(string $filePathName): array | ||
{ | ||
$code = file_get_contents($filePathName); | ||
|
||
/** @var list<class-string> $enums */ | ||
$enums = []; | ||
$namespace = ''; | ||
$tokens = token_get_all($code); | ||
$count = count($tokens); | ||
|
||
foreach ($tokens as $i => $token) { | ||
if ($i < 2) { | ||
continue; | ||
} | ||
|
||
if ($token[0] === T_NAMESPACE) { | ||
for ($j = $i + 1; $j < $count; ++$j) { | ||
if ($tokens[$j][0] === T_NAME_QUALIFIED) { | ||
$namespace = $tokens[$j][1]; | ||
|
||
break; | ||
} | ||
|
||
if ($tokens[$j][0] === T_STRING) { | ||
$namespace .= '\\' . $tokens[$j][1]; | ||
|
||
continue; | ||
} | ||
|
||
if ($tokens[$j] === '{' || $tokens[$j] === ';') { | ||
$namespace = ltrim($namespace, '\\'); | ||
|
||
break; | ||
} | ||
} | ||
|
||
continue; | ||
} | ||
|
||
if ( | ||
$tokens[$i - 2][0] !== T_ENUM | ||
|| $tokens[$i - 1][0] !== T_WHITESPACE | ||
|| $token[0] !== T_STRING | ||
) { | ||
continue; | ||
} | ||
|
||
$enumName = $tokens[$i][1]; | ||
/** @psalm-var class-string $fqn */ | ||
$fqn = $namespace . '\\' . $enumName; | ||
$enums[] = $fqn; | ||
} | ||
|
||
if ($enums === []) { | ||
throw EnumDefinitionInFileIsInvalid::noEnum($filePathName); | ||
} | ||
|
||
return $enums; | ||
} | ||
|
||
/** @return class-string */ | ||
public static function get(string $filePathName): string | ||
{ | ||
$enums = self::all($filePathName); | ||
|
||
if (count($enums) > 1) { | ||
throw EnumDefinitionInFileIsInvalid::multipleEnums($filePathName); | ||
} | ||
|
||
return $enums[0]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Cdn77\EntityFqnExtractor\Exception; | ||
|
||
use InvalidArgumentException; | ||
use PhpParser\Error; | ||
|
||
use function Safe\sprintf; | ||
|
||
final class EnumDefinitionInFileIsInvalid extends InvalidArgumentException | ||
{ | ||
public static function cannotParse(string $filePathName, Error $error): self | ||
{ | ||
return new self( | ||
sprintf('Cannot parse file %s', $filePathName), | ||
previous: $error | ||
); | ||
} | ||
|
||
public static function noEnum(string $filePathName): self | ||
{ | ||
return new self( | ||
sprintf('There is no enum in a file %s', $filePathName) | ||
); | ||
} | ||
|
||
public static function multipleEnums(string $filePathName): self | ||
{ | ||
return new self( | ||
sprintf('There are multiple enums in a file %s', $filePathName) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Cdn77\EntityFqnExtractor\Tests; | ||
|
||
use Cdn77\EntityFqnExtractor\EnumExtractor; | ||
use Cdn77\EntityFqnExtractor\Exception\EnumDefinitionInFileIsInvalid; | ||
use Cdn77\EntityFqnExtractor\Fixtures\SomeDirectory\EnumFixture; | ||
use Generator; | ||
|
||
final class EnumExtractorTest extends TestCaseBase | ||
{ | ||
public function testGet(): void | ||
{ | ||
self::assertSame( | ||
EnumFixture::class, | ||
EnumExtractor::get( | ||
__DIR__ . '/Fixtures/SomeDirectory/EnumFixture.php' | ||
) | ||
); | ||
} | ||
|
||
/** | ||
* @param list<class-string> $expectedClasses | ||
* | ||
* @dataProvider dataProviderAll | ||
*/ | ||
public function testAll(array $expectedClasses, string $fixture): void | ||
{ | ||
self::assertSame( | ||
$expectedClasses, | ||
EnumExtractor::all(__DIR__ . $fixture) | ||
); | ||
} | ||
|
||
/** @return Generator<string, array{list<string>, string}> */ | ||
public function dataProviderAll(): Generator | ||
{ | ||
yield 'two enums' => [ | ||
[ | ||
'Cdn77\EntityFqnExtractor\Fixtures\SomeDirectory\Enum1', | ||
'Cdn77\EntityFqnExtractor\Fixtures\SomeDirectory\Enum2', | ||
], | ||
'/Fixtures/SomeDirectory/EnumsFixture.php', | ||
]; | ||
|
||
yield 'class and interface' => [ | ||
[ | ||
'Cdn77\EntityFqnExtractor\Fixtures\SomeDirectory\A', | ||
'Cdn77\EntityFqnExtractor\Fixtures\SomeDirectory\B', | ||
], | ||
'/Fixtures/SomeDirectory/EnumInterfaceFixture.php', | ||
]; | ||
} | ||
|
||
/** @dataProvider dataProviderGetThrows */ | ||
public function testGetThrows(string $expectedMessage, string $path): void | ||
{ | ||
$this->expectException(EnumDefinitionInFileIsInvalid::class); | ||
$this->expectExceptionMessage($expectedMessage); | ||
EnumExtractor::get(__DIR__ . $path); | ||
} | ||
|
||
/** @return Generator<string, array{string, string}> */ | ||
public function dataProviderGetThrows(): Generator | ||
{ | ||
yield 'class' => ['There is no enum in a file', '/Fixtures/SomeDirectory/ClassFixture.php']; | ||
yield 'interface' => ['There is no enum in a file', '/Fixtures/SomeDirectory/InterfaceFixture.php']; | ||
yield 'trait' => ['There is no enum in a file', '/Fixtures/SomeDirectory/TraitFixture.php']; | ||
yield 'multiple enums' => [ | ||
'There are multiple enums in a file', | ||
'/Fixtures/SomeDirectory/EnumsFixture.php', | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Cdn77\EntityFqnExtractor\Fixtures\SomeDirectory; | ||
|
||
// @codingStandardsIgnoreFile | ||
enum A | ||
{ | ||
} | ||
|
||
interface InterfaceFixture | ||
{ | ||
} | ||
|
||
enum B { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Cdn77\EntityFqnExtractor\Fixtures\SomeDirectory; | ||
|
||
// @codingStandardsIgnoreFile | ||
|
||
enum Enum1 | ||
{ | ||
} | ||
|
||
enum Enum2 | ||
{ | ||
} |