Skip to content

Commit

Permalink
fix: properly detect namespaced class in docblock
Browse files Browse the repository at this point in the history
  • Loading branch information
romm committed Jan 8, 2024
1 parent c51c856 commit 6f7c77c
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 1 deletion.
12 changes: 11 additions & 1 deletion src/Type/Parser/Lexer/AliasLexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,17 @@ private function resolveAlias(string $symbol): string

private function resolveNamespaced(string $symbol): string
{
$namespace = $this->reflection->getNamespaceName();
$reflection = $this->reflection;

if ($reflection instanceof ReflectionFunction) {
$classReflection = $reflection->getClosureScopeClass();

if ($classReflection && $classReflection->getFileName() === $reflection->getFileName()) {
$reflection = $classReflection;
}
}

$namespace = $reflection->getNamespaceName();

if (! $namespace) {
return $symbol;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace CuyZ\Valinor\Tests\Integration\Mapping\Namespace;

use CuyZ\Valinor\Tests\Integration\IntegrationTest;
use SimpleNamespace\ImplementationOne;

final class NamespacedInterfaceInferringTest extends IntegrationTest
{
// @see https://github.com/CuyZ/Valinor/issues/394#issuecomment-1746722996
public function test_interface_inferred_from_same_namespace_as_file_runs_correctly(): void
{
// This file is excluded from the test so that it doesn't get autoloaded
// by the test suite; this allows testing a specific scenario where the
// mapper infers an interface from the same namespace as the file it is
// called from.
$result = require_once 'mapper-inferring-interface-from-non-class.php';

self::assertInstanceOf(ImplementationOne::class, $result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

namespace CuyZ\Valinor\Tests\Integration\Mapping\Namespace;

use CuyZ\Valinor\MapperBuilder;
use CuyZ\Valinor\Tests\Integration\IntegrationTest;

final class RegisteredStaticConstructorWithReturnTypeInDocBlockTest extends IntegrationTest
{
// @see https://github.com/CuyZ/Valinor/issues/461
public function test_registered_static_constructor_with_return_type_in_doc_block_works_properly(): void
{
$result = (new MapperBuilder())
->registerConstructor(
// PHP8.1 first-class callable syntax
[SomeClassWithStaticConstructor::class, 'staticConstructorWithReturnTypeInDocBlock'],
)
->mapper()
->map(SomeClassWithStaticConstructor::class, 'foo');

self::assertSame('foo', $result->value);
}
}

interface SomeSimpleInterface {}

final class SomeClassWithStaticConstructor implements SomeSimpleInterface
{
public function __construct(public string $value) {}

/**
* @return SomeSimpleInterface
*/
public static function staticConstructorWithReturnTypeInDocBlock(string $value)
{
return new self($value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace SimpleNamespace;

use CuyZ\Valinor\MapperBuilder;

require_once __DIR__ . '/../../../../vendor/autoload.php';

interface SomeInterface {}

final class ImplementationOne implements SomeInterface {}

final class ImplementationTwo implements SomeInterface {}

return (new MapperBuilder())
->infer(
SomeInterface::class,
/** @return class-string<ImplementationOne|ImplementationTwo> */
static fn (string $type): string => match ($type) {
'one' => ImplementationOne::class,
'two' => ImplementationTwo::class,
default => throw new \RuntimeException(),
},
)
->mapper()
->map(
SomeInterface::class,
['type' => 'one'],
);

0 comments on commit 6f7c77c

Please sign in to comment.