misc: import namespace token parser inside library

The `TokenParser` is imported from the `doctrine/annotations` package in
order to reduce the coupling to this library, which will lead to its
removal from the dependencies in an upcoming version.
This commit is contained in:
Nathan Boiron 2022-10-06 13:40:11 +02:00 committed by GitHub
parent 833193e025
commit 0b8ca98a2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 666 additions and 77 deletions

View File

@ -4,7 +4,10 @@ $finder = PhpCsFixer\Finder::create()->in([
'./src',
'./tests',
'./qa',
]);
])
->notPath('Fixtures/FunctionWithGroupedImportStatements.php')
->notPath('Fixtures/FunctionWithSeveralImportStatementsInSameUseStatement.php')
->notPath('Fixtures/TwoClassesInDifferentNamespaces.php');
if (PHP_VERSION_ID < 8_00_00) {
$finder = $finder
@ -33,6 +36,6 @@ return (new PhpCsFixer\Config())
'no_empty_phpdoc' => true,
'no_superfluous_phpdoc_tags' => [
'allow_mixed' => true,
'remove_inheritdoc' => true
'remove_inheritdoc' => true,
],
]);

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace CuyZ\Valinor\Type\Parser\Lexer;
use CuyZ\Valinor\Type\Parser\Lexer\Token\Token;
use CuyZ\Valinor\Utility\Reflection\ClassAliasParser;
use CuyZ\Valinor\Utility\Reflection\PhpParser;
use CuyZ\Valinor\Utility\Reflection\Reflection;
use ReflectionClass;
use ReflectionFunction;
@ -39,13 +39,13 @@ final class AliasLexer implements TypeLexer
private function resolve(string $symbol): string
{
$alias = ClassAliasParser::get()->resolveAlias($symbol, $this->reflection);
$alias = $this->resolveAlias($symbol);
if (strtolower($alias) !== strtolower($symbol)) {
return $alias;
}
$namespaced = $this->resolveNamespaced($symbol, $this->reflection);
$namespaced = $this->resolveNamespaced($symbol);
if ($namespaced !== $symbol) {
return $namespaced;
@ -54,11 +54,36 @@ final class AliasLexer implements TypeLexer
return $symbol;
}
/**
* @param ReflectionClass<object>|ReflectionFunction $reflection
*/
private function resolveNamespaced(string $symbol, Reflector $reflection): string
private function resolveAlias(string $symbol): string
{
$alias = $symbol;
$namespaceParts = explode('\\', $symbol);
$lastPart = array_shift($namespaceParts);
if ($lastPart) {
$alias = strtolower($lastPart);
}
$aliases = PhpParser::parseUseStatements($this->reflection);
if (! isset($aliases[$alias])) {
return $symbol;
}
$full = $aliases[$alias];
if (! empty($namespaceParts)) {
$full .= '\\' . implode('\\', $namespaceParts);
}
return $full;
}
private function resolveNamespaced(string $symbol): string
{
$reflection = $this->reflection;
if ($reflection instanceof ReflectionFunction) {
$reflection = $reflection->getClosureScopeClass();
}

View File

@ -1,67 +0,0 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Utility\Reflection;
use CuyZ\Valinor\Utility\IsSingleton;
use CuyZ\Valinor\Utility\Singleton;
use ReflectionClass;
use ReflectionFunction;
use Reflector;
use function array_shift;
use function explode;
use function implode;
use function strtolower;
/** @internal */
final class ClassAliasParser
{
use IsSingleton;
/** @var array<string, array<string, string>> */
private array $aliases = [];
/**
* If the given symbol was imported as an alias in the given class, the
* original value is returned.
*
* @param ReflectionClass<object>|ReflectionFunction $reflection
*/
public function resolveAlias(string $symbol, Reflector $reflection): string
{
$alias = $symbol;
$namespaceParts = explode('\\', $symbol);
$lastPart = array_shift($namespaceParts);
if ($lastPart) {
$alias = strtolower($lastPart);
}
$aliases = $this->aliases($reflection);
if (! isset($aliases[$alias])) {
return $symbol;
}
$full = $aliases[$alias];
if (! empty($namespaceParts)) {
$full .= '\\' . implode('\\', $namespaceParts);
}
return $full;
}
/**
* @param ReflectionClass<object>|ReflectionFunction $reflection
* @return array<string, string>
*/
private function aliases(Reflector $reflection): array
{
/** @infection-ignore-all */
return $this->aliases[Reflection::signature($reflection)] ??= Singleton::phpParser()->parseUseStatements($reflection);
}
}

View File

@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Utility\Reflection;
use ReflectionClass;
use ReflectionFunction;
use ReflectionMethod;
use Reflector;
use SplFileObject;
/**
* @internal
*
* Imported from `doctrine/annotations`:
* @link https://github.com/doctrine/annotations/blob/4858ab786a6cb568149209a9112dad3808c8a4de/lib/Doctrine/Common/Annotations/PhpParser.php
*/
final class PhpParser
{
/** @var array<string, array<string, string>> */
private static array $statements = [];
/**
* @param ReflectionClass<object>|ReflectionFunction|ReflectionMethod $reflection
* @return array<string, string>
*/
public static function parseUseStatements(Reflector $reflection): array
{
// @infection-ignore-all
return self::$statements[Reflection::signature($reflection)] ??= self::fetchUseStatements($reflection);
}
/**
* @param ReflectionClass<object>|ReflectionFunction|ReflectionMethod $reflection
* @return array<string, string>
*/
private static function fetchUseStatements(Reflector $reflection): array
{
$filename = $reflection->getFileName();
$startLine = $reflection->getStartLine();
if ($reflection instanceof ReflectionMethod) {
$namespaceName = $reflection->getDeclaringClass()->getNamespaceName();
} elseif ($reflection instanceof ReflectionFunction && $reflection->getClosureScopeClass()) {
$namespaceName = $reflection->getClosureScopeClass()->getNamespaceName();
} else {
$namespaceName = $reflection->getNamespaceName();
}
// @infection-ignore-all these values will never be `true`
if ($filename === false || $startLine === false) {
return [];
}
if (! is_file($filename)) {
return [];
}
$content = self::getFileContent($filename, $startLine);
return (new TokenParser($content))->parseUseStatements($namespaceName);
}
private static function getFileContent(string $filename, int $lineNumber): string
{
// @infection-ignore-all no need to test with `-1`
$lineCnt = 0;
$content = '';
$file = new SplFileObject($filename);
while (! $file->eof()) {
if ($lineCnt++ === $lineNumber) {
break;
}
$content .= $file->fgets();
}
return $content;
}
}

View File

@ -0,0 +1,157 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Utility\Reflection;
/**
* @internal
*
* Imported from `doctrine/annotations`:
* @link https://github.com/doctrine/annotations/blob/de990c9a69782a8b15fa8c9248de0ef4d82ed701/lib/Doctrine/Common/Annotations/TokenParser.php
*/
final class TokenParser
{
/** @var array<int, array{0: int, 1: string}|string> */
private array $tokens;
private int $numTokens;
private int $pointer = 0;
public function __construct(string $content)
{
$this->tokens = token_get_all($content);
/** @see https://github.com/doctrine/annotations/blob/4858ab786a6cb568149209a9112dad3808c8a4de/lib/Doctrine/Common/Annotations/TokenParser.php#L55-L61 */
// @infection-ignore-all
token_get_all("<?php\n/**\n *\n */"); // @phpstan-ignore-line
$this->numTokens = count($this->tokens);
}
/**
* @return array<string, string>
*/
public function parseUseStatements(string $namespaceName): array
{
$currentNamespace = '';
$statements = [];
while ($token = $this->next()) {
if ($currentNamespace === $namespaceName && $token[0] === T_USE) {
$statements = array_merge($statements, $this->parseUseStatement());
continue;
}
if ($token[0] !== T_NAMESPACE) {
continue;
}
$currentNamespace = $this->parseNamespace();
// Get fresh array for new namespace. This is to prevent the parser
// to collect the use statements for a previous namespace with the
// same name (this is the case if a namespace is defined twice).
$statements = [];
}
return $statements;
}
/**
* @return array<string, string>
*/
private function parseUseStatement(): array
{
$groupRoot = '';
$class = '';
$alias = '';
$statements = [];
$explicitAlias = false;
while ($token = $this->next()) {
if (! $explicitAlias && $token[0] === T_STRING) {
$class .= $token[1]; // @PHP8.0 remove concatenation
$alias = $token[1];
} elseif ($explicitAlias && $token[0] === T_STRING) {
$alias = $token[1];
} elseif (PHP_VERSION_ID >= 80000 // @PHP8.0 remove condition
&& ($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED)
) {
$class .= $token[1]; // @PHP8.0 remove concatenation
$classSplit = explode('\\', $token[1]);
$alias = $classSplit[count($classSplit) - 1];
} elseif ($token[0] === T_NS_SEPARATOR) {
$class .= '\\';
$alias = '';
} elseif ($token[0] === T_AS) {
$explicitAlias = true;
$alias = '';
} elseif ($token === ',') {
$statements[strtolower($alias)] = $groupRoot . $class;
$class = '';
$alias = '';
$explicitAlias = false;
} elseif ($token === ';') {
if ($alias !== '') {
$statements[strtolower($alias)] = $groupRoot . $class;
}
break;
} elseif ($token === '{') {
$groupRoot = $class;
$class = '';
}
}
return $statements;
}
/**
* Gets the next non whitespace and non comment token.
*
* @return array{0: int, 1: string}|string|null
*/
private function next()
{
for ($i = $this->pointer; $i < $this->numTokens; $i++) {
$this->pointer++;
if ($this->tokens[$i][0] === T_WHITESPACE
|| $this->tokens[$i][0] === T_COMMENT
|| $this->tokens[$i][0] === T_DOC_COMMENT
) {
continue;
}
return $this->tokens[$i];
}
return null;
}
private function parseNamespace(): string
{
$name = '';
// @PHP8.0 remove `infection-ignore-all`
// @infection-ignore-all
while (($token = $this->next())
// @PHP8.0 remove conditions
&& (
(
PHP_VERSION_ID < 80000
&& ($token[0] === T_NS_SEPARATOR || $token[0] === T_STRING)
)
|| (
PHP_VERSION_ID >= 80000
&& ($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED)
)
)
) {
$name .= $token[1]; // @PHP8.0 `return $token[1];` and `throw Error()` at the end of the method
}
return $name;
}
}

View File

@ -4,9 +4,9 @@ declare(strict_types=1);
namespace CuyZ\Valinor\Utility;
use CuyZ\Valinor\Utility\Reflection\PhpParser;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\Common\Annotations\PhpParser;
use Doctrine\Common\Annotations\Reader;
/** @internal */

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures;
use CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir\Bar as BarAlias;
use CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir\Foo;
use DateTimeImmutable;
use stdClass as stdClassAlias;
final class ClassInSingleNamespace
{
// @PHP8.0 promoted properties
public Foo $classInNamespaceWithoutAlias;
public BarAlias $classInNamespaceWithAlias;
public DateTimeImmutable $classInRootNamespaceWithoutAlias;
public stdClassAlias $classInRootNamespaceWithAlias;
public function __construct(
Foo $classInNamespaceWithoutAlias,
BarAlias $classInNamespaceWithAlias,
DateTimeImmutable $classInRootNamespaceWithoutAlias,
stdClassAlias $classInRootNamespaceWithAlias
) {
$this->classInNamespaceWithoutAlias = $classInNamespaceWithoutAlias;
$this->classInNamespaceWithAlias = $classInNamespaceWithAlias;
$this->classInRootNamespaceWithoutAlias = $classInRootNamespaceWithoutAlias;
$this->classInRootNamespaceWithAlias = $classInRootNamespaceWithAlias;
}
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
use CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir\Foo as FooAlias;
use CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir\Bar as BarAlias;
function function_in_root_namespace(
FooAlias $foo,
BarAlias $bar
): void {
}

View File

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures;
// Only one use statement with two grouped import statements, no trailing comma.
use CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir\ {
Foo as FooAlias,
Bar as BarAlias // no trailing comma
};
// Only one use statement with two grouped import statements, trailing comma.
use CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir\{
Foo as AnotherFooAlias,
Bar as AnotherBarAlias, // trailing comma
};
function function_with_grouped_import_statements(
FooAlias $foo,
BarAlias $bar,
AnotherFooAlias $anotherFoo,
AnotherBarAlias $anotherBar
): void {
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures;
// Only one use statement with two import statements.
use CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir\Foo as FooAlias,
CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir\Bar as BarAlias;
use CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir\Foo as AnotherFooAlias,
CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir\Bar as AnotherBarAlias;
function function_with_several_import_statements_in_same_use_statement(
FooAlias $foo,
BarAlias $bar,
AnotherFooAlias $anotherFoo,
AnotherBarAlias $anotherBar
): void {
}

View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir;
final class Bar
{
}

View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir;
final class Foo
{
}

View File

@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
// A commented namespace should not be parsed
//
// namespace CuyZ\Valinor\Tests\Fixtures\WithAliasA {
// use DateTime as SomeDateTimeAlias;
// }
namespace CuyZ\Valinor\Tests\Fixtures\WithAliasA {
use CuyZ\Valinor\Tests\Fixtures\WithAliasB\ClassB;
use CuyZ\Valinor\Tests\Fixtures\WithAliasB\ClassB as classBAlias;
use CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir\Bar as BarAlias;
use CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir\Foo;
use DateTimeImmutable;
use stdClass as stdClassAlias;
function functionA(
Foo $classInOtherFileWithoutAlias,
BarAlias $classInOtherFileWithAlias,
ClassB $classInSameFileWithoutAlias,
classBAlias $classInSameFileWithAlias,
DateTimeImmutable $classInRootNamespaceWithoutAlias,
stdClassAlias $classInRootNamespaceWithAlias
): void {
}
class ClassA
{
// @PHP8.0 promoted properties
public Foo $classInOtherFileWithoutAlias;
public BarAlias $classInOtherFileWithAlias;
public classB $classInSameFileWithoutAlias;
public classBAlias $classInSameFileWithAlias;
public DateTimeImmutable $classInRootNamespaceWithoutAlias;
public stdClassAlias $classInRootNamespaceWithAlias;
public function __construct(
Foo $classInOtherFileWithoutAlias,
BarAlias $classInOtherFileWithAlias,
ClassB $classInSameFileWithoutAlias,
classBAlias $classInSameFileWithAlias,
DateTimeImmutable $classInRootNamespaceWithoutAlias,
stdClassAlias $classInRootNamespaceWithAlias
) {
$this->classInOtherFileWithoutAlias = $classInOtherFileWithoutAlias;
$this->classInOtherFileWithAlias = $classInOtherFileWithAlias;
$this->classInSameFileWithoutAlias = $classInSameFileWithoutAlias;
$this->classInSameFileWithAlias = $classInSameFileWithAlias;
$this->classInRootNamespaceWithoutAlias = $classInRootNamespaceWithoutAlias;
$this->classInRootNamespaceWithAlias = $classInRootNamespaceWithAlias;
}
}
}
// First case of a duplicated namespace: the alias `AnotherDateTimeAlias` is not
// accessible in the second case below and should not be fetched by the parser.
namespace CuyZ\Valinor\Tests\Fixtures\WithAliasB {
use DateTime as AnotherDateTimeAlias;
}
namespace CuyZ\Valinor\Tests\Fixtures\WithAliasB {
use CuyZ\Valinor\Tests\Fixtures\WithAliasA\ClassA;
use CuyZ\Valinor\Tests\Fixtures\WithAliasA\ClassA as classAAlias;
use CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir\Bar as BarAlias;
use CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir\Foo;
use DateTimeImmutable;
use stdClass as stdClassAlias;
function functionB(
Foo $classInOtherFileWithoutAlias,
BarAlias $classInOtherFileWithAlias,
ClassA $classInSameFileWithoutAlias,
classAAlias $classInSameFileWithAlias,
DateTimeImmutable $classInRootNamespaceWithoutAlias,
stdClassAlias $classInRootNamespaceWithAlias
): void {
}
class ClassB
{
// @PHP8.0 promoted properties
public Foo $classInOtherFileWithoutAlias;
public BarAlias $classInOtherFileWithAlias;
public classA $classInSameFileWithoutAlias;
public classAAlias $classInSameFileWithAlias;
public DateTimeImmutable $classInRootNamespaceWithoutAlias;
public stdClassAlias $classInRootNamespaceWithAlias;
public function __construct(
Foo $classInOtherFileWithoutAlias,
BarAlias $classInOtherFileWithAlias,
ClassA $classInSameFileWithoutAlias,
classAAlias $classInSameFileWithAlias,
DateTimeImmutable $classInRootNamespaceWithoutAlias,
stdClassAlias $classInRootNamespaceWithAlias
) {
$this->classInOtherFileWithoutAlias = $classInOtherFileWithoutAlias;
$this->classInOtherFileWithAlias = $classInOtherFileWithAlias;
$this->classInSameFileWithoutAlias = $classInSameFileWithoutAlias;
$this->classInSameFileWithAlias = $classInSameFileWithAlias;
$this->classInRootNamespaceWithoutAlias = $classInRootNamespaceWithoutAlias;
$this->classInRootNamespaceWithAlias = $classInRootNamespaceWithAlias;
}
}
}
// Third case of a duplicated namespace: the alias `YetAnotherDateTimeAlias` is
// not accessible in the second case above and should not be fetched by the
// parser.
namespace CuyZ\Valinor\Tests\Fixtures\WithAliasB {
use DateTimeImmutable as YetAnotherDateTimeAlias;
}

View File

@ -0,0 +1,167 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Tests\Unit\Utility\Reflection;
use CuyZ\Valinor\Tests\Fixtures\WithAliasA\ClassA;
use CuyZ\Valinor\Tests\Fixtures\WithAliasB\ClassB;
use CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\ClassInSingleNamespace;
use CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir\Bar;
use CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\SubDir\Foo;
use CuyZ\Valinor\Utility\Reflection\PhpParser;
use Generator;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use ReflectionFunction;
use ReflectionMethod;
require_once __DIR__ . '/Fixtures/TwoClassesInDifferentNamespaces.php';
require_once __DIR__ . '/Fixtures/FunctionInRootNamespace.php';
require_once __DIR__ . '/Fixtures/FunctionWithSeveralImportStatementsInSameUseStatement.php';
require_once __DIR__ . '/Fixtures/FunctionWithGroupedImportStatements.php';
final class PhpParserTest extends TestCase
{
/**
* @dataProvider useStatementsDataProvider
* @template T of object
*
* @param ReflectionClass<T>|ReflectionFunction|ReflectionMethod $reflection
* @param array<string, string> $expectedMap
*/
public function test_parse_use_statements($reflection, array $expectedMap): void
{
$actualMap = PhpParser::parseUseStatements($reflection);
self::assertSame($expectedMap, $actualMap);
}
public function useStatementsDataProvider(): Generator
{
yield 'no use statements' => [
new ReflectionClass(\stdClass::class),
[]
];
yield 'one namespace' => [
new ReflectionClass(ClassInSingleNamespace::class),
[
'baralias' => Bar::class,
'foo' => Foo::class,
'datetimeimmutable' => \DateTimeImmutable::class,
'stdclassalias' => \stdClass::class,
]
];
yield 'multiple namespaces, class A' => [
new ReflectionClass(ClassA::class),
[
'classb' => ClassB::class,
'classbalias' => ClassB::class,
'baralias' => Bar::class,
'foo' => Foo::class,
'datetimeimmutable' => \DateTimeImmutable::class,
'stdclassalias' => \stdClass::class,
]
];
yield 'multiple namespaces, class B' => [
new ReflectionClass(ClassB::class),
[
'classa' => ClassA::class,
'classaalias' => ClassA::class,
'baralias' => Bar::class,
'foo' => Foo::class,
'datetimeimmutable' => \DateTimeImmutable::class,
'stdclassalias' => \stdClass::class,
]
];
yield 'multiple namespaces, function A' => [
new ReflectionFunction('\CuyZ\Valinor\Tests\Fixtures\WithAliasA\functionA'),
[
'classb' => ClassB::class,
'classbalias' => ClassB::class,
'baralias' => Bar::class,
'foo' => Foo::class,
'datetimeimmutable' => \DateTimeImmutable::class,
'stdclassalias' => \stdClass::class,
]
];
yield 'multiple namespaces, function B' => [
new ReflectionFunction('\CuyZ\Valinor\Tests\Fixtures\WithAliasB\functionB'),
[
'classa' => ClassA::class,
'classaalias' => ClassA::class,
'baralias' => Bar::class,
'foo' => Foo::class,
'datetimeimmutable' => \DateTimeImmutable::class,
'stdclassalias' => \stdClass::class,
]
];
yield 'one namespace, method' => [
new ReflectionMethod(ClassInSingleNamespace::class, '__construct'),
[
'baralias' => Bar::class,
'foo' => Foo::class,
'datetimeimmutable' => \DateTimeImmutable::class,
'stdclassalias' => \stdClass::class,
]
];
yield 'multiple namespaces, class A method' => [
new ReflectionMethod(ClassA::class, '__construct'),
[
'classb' => ClassB::class,
'classbalias' => ClassB::class,
'baralias' => Bar::class,
'foo' => Foo::class,
'datetimeimmutable' => \DateTimeImmutable::class,
'stdclassalias' => \stdClass::class,
]
];
yield 'multiple namespaces, class B method' => [
new ReflectionMethod(ClassB::class, '__construct'),
[
'classa' => ClassA::class,
'classaalias' => ClassA::class,
'baralias' => Bar::class,
'foo' => Foo::class,
'datetimeimmutable' => \DateTimeImmutable::class,
'stdclassalias' => \stdClass::class,
]
];
yield 'function in root namespace' => [
new ReflectionFunction('function_in_root_namespace'),
[
'fooalias' => Foo::class,
'baralias' => Bar::class,
]
];
yield 'one namespace, one use statement, two import statements' => [
new ReflectionFunction('CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\function_with_several_import_statements_in_same_use_statement'),
[
'fooalias' => Foo::class,
'baralias' => Bar::class,
'anotherfooalias' => Foo::class,
'anotherbaralias' => Bar::class,
]
];
yield 'one namespace, one use statement, two grouped import statements' => [
new ReflectionFunction('\CuyZ\Valinor\Tests\Unit\Utility\Reflection\Fixtures\function_with_grouped_import_statements'),
[
'fooalias' => Foo::class,
'baralias' => Bar::class,
'anotherfooalias' => Foo::class,
'anotherbaralias' => Bar::class,
],
];
}
}