From 537a740a867aa75dea2390e575df0bbc6fbab669 Mon Sep 17 00:00:00 2001 From: Michael Bolli Date: Tue, 14 Mar 2023 18:41:50 +0100 Subject: [PATCH] prevent scanning interfaces and traits (#4) Thanks a lot! --- src/Driver/AbstractClassDriver.php | 46 +++++++++++++------ .../Driver/AbstractClassDriverTest.php | 37 +++++++++++++++ .../Classes/Invalid/Attribute/Interface.php | 19 ++++++++ .../Files/Classes/Invalid/Attribute/Trait.php | 27 +++++++++++ .../Mapping/Stubs/AbstractClassDriverStub.php | 35 ++++++++++++++ 5 files changed, 150 insertions(+), 14 deletions(-) create mode 100644 tests/Mapping/Driver/AbstractClassDriverTest.php create mode 100644 tests/Mapping/Files/Classes/Invalid/Attribute/Interface.php create mode 100644 tests/Mapping/Files/Classes/Invalid/Attribute/Trait.php create mode 100644 tests/Mapping/Stubs/AbstractClassDriverStub.php diff --git a/src/Driver/AbstractClassDriver.php b/src/Driver/AbstractClassDriver.php index 56ddc84..dec1d53 100644 --- a/src/Driver/AbstractClassDriver.php +++ b/src/Driver/AbstractClassDriver.php @@ -44,21 +44,18 @@ protected function getMappingClasses(): array /** * Load fully qualified class name from file. * - * @return class-string + * @return class-string|null */ - protected function loadClassFromFile(string $mappingFile): string + protected function loadClassFromFile(string $mappingFile): ?string { $content = file_get_contents($mappingFile); $tokens = token_get_all($content !== false ? $content : ''); $class = ''; - $next = $this->findNextToken($tokens, \T_NAMESPACE); + $next = $this->findNextToken($tokens, [\T_NAMESPACE]); if ($next !== null) { - $validTokenTypes = [\T_WHITESPACE, \T_NS_SEPARATOR, \T_STRING]; - if (\PHP_VERSION_ID >= 80_000) { - $validTokenTypes[] = T_NAME_QUALIFIED; - } + $validTokenTypes = $this->getValidTokenTypes(); while ( \array_key_exists($next + 1, $tokens) @@ -70,12 +67,19 @@ protected function loadClassFromFile(string $mappingFile): string ++$next; } - $next = $this->findNextToken($tokens, \T_CLASS, $next + 1, \T_DOUBLE_COLON); + // Exclude traits and interfaces + if ($this->findNextToken($tokens, [\T_TRAIT, \T_INTERFACE], $next + 1) !== null) { + return null; + } + + $next = $this->findNextToken($tokens, [\T_CLASS], $next + 1, \T_DOUBLE_COLON); + if ($next === null) { + return null; + } + + $next = $this->findNextToken($tokens, [\T_STRING], $next + 1); if ($next !== null) { - $next = $this->findNextToken($tokens, \T_STRING, $next + 1); - if ($next !== null) { - $class .= '\\' . $tokens[$next][1]; - } + $class .= '\\' . $tokens[$next][1]; } } @@ -87,8 +91,9 @@ protected function loadClassFromFile(string $mappingFile): string * Traverse token stack in search of next token. * * @param array $tokens + * @param array $types */ - private function findNextToken(array $tokens, int $type, int $start = 0, ?int $escapePreviousType = null): ?int + private function findNextToken(array $tokens, array $types, int $start = 0, ?int $escapePreviousType = null): ?int { $previousToken = false; @@ -99,7 +104,7 @@ private function findNextToken(array $tokens, int $type, int $start = 0, ?int $e } if ( - $token[0] === $type + \in_array($token[0], $types, true) && ( $escapePreviousType === null || !\is_array($previousToken) @@ -114,4 +119,17 @@ private function findNextToken(array $tokens, int $type, int $start = 0, ?int $e return null; } + + /** + * @return array + */ + private function getValidTokenTypes(): array + { + $validTokenTypes = [\T_WHITESPACE, \T_NS_SEPARATOR, \T_STRING]; + if (\PHP_VERSION_ID >= 80_000) { + $validTokenTypes[] = T_NAME_QUALIFIED; + } + + return $validTokenTypes; + } } diff --git a/tests/Mapping/Driver/AbstractClassDriverTest.php b/tests/Mapping/Driver/AbstractClassDriverTest.php new file mode 100644 index 0000000..f39162f --- /dev/null +++ b/tests/Mapping/Driver/AbstractClassDriverTest.php @@ -0,0 +1,37 @@ + + */ + +declare(strict_types=1); + +namespace Jgut\Mapping\Tests\Driver; + +use Jgut\Mapping\Tests\Files\Classes\Valid\Attribute\ClassA; +use Jgut\Mapping\Tests\Stubs\AbstractClassDriverStub; +use PHPUnit\Framework\TestCase; + +/** + * @internal + */ +class AbstractClassDriverTest extends TestCase +{ + public function testClassLoader(): void + { + $driver = new AbstractClassDriverStub([__DIR__ . '/../Files/Classes/Valid/Attribute']); + + $className = $driver->loadClassFromFile(__DIR__ . '/../Files/Classes/Valid/Attribute/ClassA.php'); + $traitName = $driver->loadClassFromFile(__DIR__ . '/../Files/Classes/Invalid/Attribute/Trait.php'); + $interfaceName = $driver->loadClassFromFile(__DIR__ . '/../Files/Classes/Invalid/Attribute/Interface.php'); + + static::assertSame(ClassA::class, $className); + static::assertNull($traitName); + static::assertNull($interfaceName); + } +} diff --git a/tests/Mapping/Files/Classes/Invalid/Attribute/Interface.php b/tests/Mapping/Files/Classes/Invalid/Attribute/Interface.php new file mode 100644 index 0000000..a1bca97 --- /dev/null +++ b/tests/Mapping/Files/Classes/Invalid/Attribute/Interface.php @@ -0,0 +1,19 @@ + + */ + +declare(strict_types=1); + +namespace Jgut\Mapping\Tests\Files\Classes\Invalid\Attribute; + +interface InterfaceA +{ + public function methodA(): void; +} diff --git a/tests/Mapping/Files/Classes/Invalid/Attribute/Trait.php b/tests/Mapping/Files/Classes/Invalid/Attribute/Trait.php new file mode 100644 index 0000000..52c65d0 --- /dev/null +++ b/tests/Mapping/Files/Classes/Invalid/Attribute/Trait.php @@ -0,0 +1,27 @@ + + */ + +declare(strict_types=1); + +namespace Jgut\Mapping\Tests\Files\Classes\Invalid\Attribute; + +trait TraitA +{ + public function methodA(): void + { + } + + public function methodB(): void + { + $var = new class() {}; + echo \get_class($var); + } +} diff --git a/tests/Mapping/Stubs/AbstractClassDriverStub.php b/tests/Mapping/Stubs/AbstractClassDriverStub.php new file mode 100644 index 0000000..7765ca6 --- /dev/null +++ b/tests/Mapping/Stubs/AbstractClassDriverStub.php @@ -0,0 +1,35 @@ + + */ + +declare(strict_types=1); + +namespace Jgut\Mapping\Tests\Stubs; + +use Jgut\Mapping\Driver\AbstractClassDriver; +use Jgut\Mapping\Metadata\MetadataInterface; + +class AbstractClassDriverStub extends AbstractClassDriver +{ + /** + * Get mapped metadata. + * + * @return array + */ + public function getMetadata(): array + { + return []; + } + + public function loadClassFromFile(string $mappingFile, bool $uselessVariable = true): ?string + { + return parent::loadClassFromFile($mappingFile); + } +}