CuyZ / Valinor

PHP library that helps to map any input into a strongly-typed value object structure.

Home Page:https://valinor.cuyz.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unneeded calls to the autoloader

TimWolla opened this issue · comments

Please consider the following code:

<?php

namespace NS;

use CuyZ\Valinor\MapperBuilder;

require('vendor/autoload.php');

\spl_autoload_register(function (string $class) {
    echo "L: ", $class, PHP_EOL;
});

final class Target
{
    /**
     * @param   array{
     *              foo?: string,
     *              bar?: int,
     *              baz?: array<string, int>,
     *          } $foo
     * @param   array<string,string> $bar 
     */
    public function __construct(
        public readonly array $foo,
        public readonly array $bar,
    )
    {
    }
}

try {
    $payload = (new MapperBuilder())
        ->mapper()
        ->map(
            Target::class,
            [
                "bar" => [],
                "foo" => [
                    "foo" => "foo",
                ]
            ]
        );

    var_dump($payload);
} catch (\CuyZ\Valinor\Mapper\MappingError $error) {
    $messages = \CuyZ\Valinor\Mapper\Tree\Message\Messages::flattenFromNode(
        $error->node()
    );

    foreach ($messages as $message) {
        echo $message, PHP_EOL;
    }
}

When running this code, the following output is emitted:

L: NS\array
L: NS\array
L: NS\foo
L: NS\foo
L: foo
L: foo
L: foo
L: NS\string
L: NS\string
L: NS\bar
L: NS\bar
L: bar
L: bar
L: bar
L: NS\int
L: NS\int
L: NS\baz
L: NS\baz
L: baz
L: baz
L: baz
L: NS\array
L: NS\array
L: NS\string
L: NS\string
L: NS\int
L: NS\int
L: NS\array
L: NS\array
L: NS\string
L: NS\string
L: NS\string
L: NS\string
L: NS\array
L: NS\array
L: NS\foo
L: NS\foo
L: foo
L: foo
L: foo
L: NS\string
L: NS\string
L: NS\bar
L: NS\bar
L: bar
L: bar
L: bar
L: NS\int
L: NS\int
L: NS\baz
L: NS\baz
L: baz
L: baz
L: baz
L: NS\array
L: NS\array
L: NS\string
L: NS\string
L: NS\int
L: NS\int
L: NS\array
L: NS\array
L: NS\string
L: NS\string
L: NS\string
L: NS\string
/pwd/test.php:44:
class NS\Target#146 (2) {
  public readonly array $foo =>
  array(1) {
    'foo' =>
    string(3) "foo"
  }
  public readonly array $bar =>
  array(0) {
  }
}

It becomes clear that the autoloader is uselessly called for classnames that are never going to result in a hit. This can get expensive for an autoloader that checks the file system for a class to include. Especially since the calls are repeated multiple times:

  • NS\int, NS\string and NS\array can never successfully load a class, because int, string and array are reserved class names.
  • foo, bar and baz as keys of the array shape and thus should not be considered class names in the first place.

As a result I did not expect any calls to my autoloader happening at all.

Thanks for the investigation! I took some time to submit #499 which will reduce the number of calls to the autoloader by a lot. Preventing class existence checks for shaped array keys would require a lot more work, which I may have time to do later (so I'm keeping this issue opened for now).

Good news, I found some time to submit #515 which fixes this issue.

Thanks again for reporting it!