Fix GH-23232: lone namespace separator asks the autoloader for an empty class name - #23233
Conversation
Girgias
left a comment
There was a problem hiding this comment.
This seems okay as a fix, might be worse to see if this can be prevent on the call sites for the future.
|
Thanks! I put the guard in Validating at the call sites would mean duplicating "is this even a syntactically possible class name?" in each of them. |
b0d5d4d to
3916faf
Compare
|
For the record, the two userland-side fixes were both declined, which leaves this the only remaining place to fix it — and arguably the right level anyway.
That reasoning points here: the caller handing Composer an empty class name is the engine itself, via |
|
Indeed, autoloading |
|
I'm realising that this is a long standing behavioural change (even if it is dumb) so I'd prefer to only target master for this. |
|
I am struggling to construct a valid use case that would work now and break with this fix applied, I would categorize this as a bugfix. Unless the policy of PHP is to remain bug-for-bug compatible once a minor version is released, it should be fine for a bugfix release? Open to retargeting to master if that is what it takes to get this merged though. |
|
Just to make sure, this also fixes class_exists()? https://3v4l.org/Qe3u4#v8.5.3 because that's the main one I've had reports about regarding the Composer ClassLoader |
|
Yes. The test in this PR asserts it: |
…ty class name
A class name consisting solely of the namespace separator passed the
length check in zend_lookup_class_ex(), lost its leading backslash and
was then looked up and autoloaded as an empty string. This affected all
entry points using that lookup, e.g. is_callable('\::method') and
class_exists('\').
3916faf to
61874eb
Compare
|
Thank you @Girgias for your consideration and merge! |
Fixes GH-23232.
zend_lookup_class_ex()rejects an empty class name, but not a name consisting solely of the namespace separator:"\"passes the length check, gets its leading\stripped, and is then looked up — and autoloaded — as"".That is reachable from userland through any entry point using that lookup:
is_callable('\::method')splits at::, takes"\"as the class part and hands it tozend_lookup_class().is_callable('::method')is rejected as an invalid function name earlier, which is where the asymmetry in the issue comes from.class_exists('\')passes the name straight through, see https://3v4l.org/Qe3u4.A lone
\names no class, so returnNULLbefore consulting the class table or the autoloader.This is observable, not just wasted work: Composer's
ClassLoader::findFileWithExtension()does$first = $class[0];and warnsUninitialized string offset 0when handed'', which in applications that promote warnings to exceptions aborts the request. We hit it in CI through Laravel'sFactory::expandAttributes(), which callsis_callable()on every string attribute — a randomly generated password starting with\::was enough.Targeting
PHP-8.4as the lowest branch still receiving bug fixes.make testpasses onZend/testsandext/standard/tests/general_functions(5085 passed, 0 failed) in a debug build; the new.phptfails without the patch.