Description
is_callable() on a string of the form '\::method' asks the autoloader for the empty class name. The same string without the leading backslash, '::method', does not autoload at all. A leading \ only marks the name as fully qualified, so this asymmetry looks like a bug.
The following code:
<?php
spl_autoload_register(function (string $class): void {
echo "autoload: '$class'\n";
});
foreach (['::a', '\::a', '\Foo::a', 'Foo::a'] as $callable) {
echo "is_callable(\"$callable\")\n";
is_callable($callable);
}
Resulted in this output:
is_callable("::a")
is_callable("\::a")
autoload: ''
is_callable("\Foo::a")
autoload: 'Foo'
is_callable("Foo::a")
autoload: 'Foo'
But I expected this output instead:
is_callable("::a")
is_callable("\::a")
is_callable("\Foo::a")
autoload: 'Foo'
is_callable("Foo::a")
autoload: 'Foo'
Reproduced unchanged on 8.3.32, 8.4.24 and 8.5.9 (official php:*-cli images).
An empty class name can never resolve, so autoloading it is pure overhead — and it is observable overhead. Composer's ClassLoader::findFileWithExtension() does $first = $class[0];, which on '' emits Warning: Uninitialized string offset 0. Any application that promotes warnings to exceptions (Laravel's HandleExceptions, for example) then fails.
We hit this in CI: Laravel's Factory::expandAttributes() calls is_callable() on every string attribute, and a randomly generated password starting with \:: (about 1 in a million with Faker's password()) made unrelated tests error out with ErrorException: Uninitialized string offset 0. Fixes are also filed with composer and laravel, but the autoload request for '' looks wrong regardless of who else guards against it.
I am happy to prepare a patch rejecting the empty class name before autoload if you agree this should change.
PHP Version
PHP 8.3.32, 8.4.24, 8.5.9
Operating System
Linux (official php:8.3-cli, php:8.4-cli, php:8.5-cli images)
Description
is_callable()on a string of the form'\::method'asks the autoloader for the empty class name. The same string without the leading backslash,'::method', does not autoload at all. A leading\only marks the name as fully qualified, so this asymmetry looks like a bug.The following code:
Resulted in this output:
But I expected this output instead:
Reproduced unchanged on 8.3.32, 8.4.24 and 8.5.9 (official
php:*-cliimages).An empty class name can never resolve, so autoloading it is pure overhead — and it is observable overhead. Composer's
ClassLoader::findFileWithExtension()does$first = $class[0];, which on''emitsWarning: Uninitialized string offset 0. Any application that promotes warnings to exceptions (Laravel'sHandleExceptions, for example) then fails.We hit this in CI: Laravel's
Factory::expandAttributes()callsis_callable()on every string attribute, and a randomly generated password starting with\::(about 1 in a million with Faker'spassword()) made unrelated tests error out withErrorException: Uninitialized string offset 0. Fixes are also filed with composer and laravel, but the autoload request for''looks wrong regardless of who else guards against it.I am happy to prepare a patch rejecting the empty class name before autoload if you agree this should change.
PHP Version
Operating System
Linux (official
php:8.3-cli,php:8.4-cli,php:8.5-cliimages)