Skip to content

Commit

Permalink
fix: load needed information only during interface inferring
Browse files Browse the repository at this point in the history
This commit changes the way interface inferring is done.

Previously, the whole set of information for all interfaces was loaded
everytime the library was used, which could lead to performance issue
for no reason.

Now, when an interface must be inferred, only information about this
interface will be loaded.
  • Loading branch information
romm committed Mar 30, 2024
1 parent b5c460c commit c8e204a
Show file tree
Hide file tree
Showing 5 changed files with 16 additions and 67 deletions.
2 changes: 1 addition & 1 deletion src/Mapper/Tree/Builder/InterfaceNodeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
try {
$classType = $this->implementations->implementation($className, $values);
} catch (ObjectImplementationCallbackError $exception) {
throw UserlandError::from($exception->original());
throw UserlandError::from($exception);
}

$class = $this->classDefinitionRepository->for($classType);
Expand Down
26 changes: 10 additions & 16 deletions src/Mapper/Tree/Builder/ObjectImplementations.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@

use CuyZ\Valinor\Definition\FunctionDefinition;
use CuyZ\Valinor\Definition\FunctionsContainer;
use CuyZ\Valinor\Mapper\Tree\Exception\InvalidAbstractObjectName;
use CuyZ\Valinor\Mapper\Tree\Exception\InvalidResolvedImplementationValue;
use CuyZ\Valinor\Mapper\Tree\Exception\MissingObjectImplementationRegistration;
use CuyZ\Valinor\Mapper\Tree\Exception\ObjectImplementationCallbackError;
use CuyZ\Valinor\Mapper\Tree\Exception\ObjectImplementationNotRegistered;
use CuyZ\Valinor\Mapper\Tree\Exception\ResolvedImplementationIsNotAccepted;
use CuyZ\Valinor\Type\ClassType;
use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
use CuyZ\Valinor\Type\Parser\TypeParser;
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\ClassStringType;
use CuyZ\Valinor\Type\Types\InterfaceType;
use CuyZ\Valinor\Type\Types\UnionType;
use Exception;

use function assert;

/** @internal */
final class ObjectImplementations
{
Expand All @@ -30,12 +30,7 @@ final class ObjectImplementations
public function __construct(
private FunctionsContainer $functions,
private TypeParser $typeParser
) {
foreach ($functions as $name => $function) {
/** @var string $name */
$this->implementations[$name] = $this->implementations($name);
}
}
) {}

public function has(string $name): bool
{
Expand All @@ -52,6 +47,9 @@ public function function(string $name): FunctionDefinition
*/
public function implementation(string $name, array $arguments): ClassType
{
/** @infection-ignore-all / We cannot test the assignment */
$this->implementations[$name] ??= $this->implementations($name);

$class = $this->call($name, $arguments);

return $this->implementations[$name][$class]
Expand Down Expand Up @@ -83,18 +81,14 @@ private function implementations(string $name): array
{
$function = $this->functions->get($name)->definition;

try {
$type = $this->typeParser->parse($name);
} catch (InvalidType) {
}
$type = $this->typeParser->parse($name);

if (! isset($type) || (! $type instanceof InterfaceType && ! $type instanceof ClassType)) {
throw new InvalidAbstractObjectName($name);
}
/** @infection-ignore-all */
assert($type instanceof InterfaceType || $type instanceof ClassType);

$classes = $this->implementationsByReturnSignature($name, $function);

if (empty($classes)) {
if ($classes === []) {
throw new MissingObjectImplementationRegistration($name, $function);
}

Expand Down
2 changes: 1 addition & 1 deletion tests/Integration/Cache/CacheInjectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function test_cache_entries_are_written_once_during_mapping(): void

$files = $this->recursivelyFindPhpFiles($cacheDirectory);

self::assertCount(6, $files);
self::assertCount(4, $files);

foreach ($files as $file) {
$file->setContent($file->getContent() . "\n// generated value 1661895014");
Expand Down
4 changes: 2 additions & 2 deletions tests/Integration/Cache/CacheWarmupTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ public function test_will_warmup_type_parser_cache_for_object_with_constructor()
$mapper->warmup(ObjectToWarmupWithConstructors::class);
$mapper->warmup(ObjectToWarmupWithConstructors::class, SomeObjectC::class);

self::assertSame(7, $this->cache->countEntries());
self::assertSame(7, $this->cache->timeSetWasCalled());
self::assertSame(6, $this->cache->countEntries());
self::assertSame(6, $this->cache->timeSetWasCalled());
}

public function test_will_warmup_type_parser_cache_for_interface(): void
Expand Down
49 changes: 2 additions & 47 deletions tests/Integration/Mapping/InterfaceInferringMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@

use CuyZ\Valinor\Mapper\MappingError;
use CuyZ\Valinor\Mapper\Tree\Exception\CannotResolveObjectType;
use CuyZ\Valinor\Mapper\Tree\Exception\InvalidAbstractObjectName;
use CuyZ\Valinor\Mapper\Tree\Exception\InvalidResolvedImplementationValue;
use CuyZ\Valinor\Mapper\Tree\Exception\MissingObjectImplementationRegistration;
use CuyZ\Valinor\Mapper\Tree\Exception\ObjectImplementationCallbackError;
use CuyZ\Valinor\Mapper\Tree\Exception\ObjectImplementationNotRegistered;
use CuyZ\Valinor\Mapper\Tree\Exception\ResolvedImplementationIsNotAccepted;
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\FakeErrorMessage;
use CuyZ\Valinor\Tests\Fixture\Object\InterfaceWithDifferentNamespaces\A\ClassThatInheritsInterfaceA;
use CuyZ\Valinor\Tests\Fixture\Object\InterfaceWithDifferentNamespaces\B\ClassThatInheritsInterfaceB;
use CuyZ\Valinor\Tests\Fixture\Object\InterfaceWithDifferentNamespaces\ClassWithBothInterfaces;
Expand Down Expand Up @@ -279,30 +277,6 @@ public function test_object_implementation_callback_error_throws_exception(): vo
->map(DateTimeInterface::class, []);
}

public function test_invalid_abstract_object_name_throws_exception(): void
{
$this->expectException(InvalidAbstractObjectName::class);
$this->expectExceptionCode(1653990369);
$this->expectExceptionMessage('Invalid interface or class name `invalid type`.');

$this->mapperBuilder()
->infer('invalid type', fn () => stdClass::class) // @phpstan-ignore-line
->mapper()
->map(stdClass::class, []);
}

public function test_invalid_abstract_object_type_throws_exception(): void
{
$this->expectException(InvalidAbstractObjectName::class);
$this->expectExceptionCode(1653990369);
$this->expectExceptionMessage('Invalid interface or class name `string`.');

$this->mapperBuilder()
->infer('string', fn () => stdClass::class) // @phpstan-ignore-line
->mapper()
->map(stdClass::class, []);
}

public function test_missing_object_implementation_registration_throws_exception(): void
{
$this->expectException(MissingObjectImplementationRegistration::class);
Expand All @@ -315,7 +289,7 @@ public function test_missing_object_implementation_registration_throws_exception
fn (string $type) => SomeClassThatInheritsInterfaceA::class
)
->mapper()
->map(SomeInterface::class, []);
->map(SomeInterface::class, 'foo');
}

public function test_invalid_union_object_implementation_registration_throws_exception(): void
Expand All @@ -330,7 +304,7 @@ public function test_invalid_union_object_implementation_registration_throws_exc
fn (string $value): string|int => $value === 'foo' ? 'foo' : 42
)
->mapper()
->map(SomeInterface::class, []);
->map(SomeInterface::class, 'foo');
}

public function test_invalid_class_string_object_implementation_registration_throws_exception(): void
Expand Down Expand Up @@ -402,25 +376,6 @@ public function test_invalid_source_value_throws_exception(): void
}
}

public function test_exception_thrown_is_caught_and_throws_message_exception(): void
{
try {
$this->mapperBuilder()
->infer(
DateTimeInterface::class,
/** @return class-string<DateTime> */
fn (string $value) => throw new FakeErrorMessage('some error message', 1645303304)
)
->mapper()
->map(DateTimeInterface::class, 'foo');
} catch (MappingError $exception) {
$error = $exception->node()->messages()[0];

self::assertSame('1645303304', $error->code());
self::assertSame('some error message', (string)$error);
}
}

public function test_superfluous_values_throws_exception(): void
{
try {
Expand Down

0 comments on commit c8e204a

Please sign in to comment.