resolveExtendedClassToRoot has odd behavior that performs poorly


Well-known member
Affected version
resolveExtendedClassToRoot gets called at least once per entity instance, as it's called from Entity's constructor. However, it takes a rather complicated approach to obtaining its value, and that approach will has unexpected behavior if called before extendClass.

Currently, it walks up the class hierarchy, relying on PHP's quirky class alias handling to detect XFCP classes. It calls is_subclass_of in a loop, but that's a relatively slow function, and it could be called quite a bit if there are a lot of add-ons or entities.

There's a low-hanging optimization here that eliminates quite a bit of code. The current resolveExtendedClassToRoot implementation assumes that extendClass has been called first; otherwise, class aliases won't have been created. That means we can store an inverse lookup table alongside the forward lookup table (extensionMap). This also gives us an opportunity to detect likely logic errors where a call to resolveExtendedClassToRoot is being made too early--this currently fails by silently returning $class, but that's almost certainly not the result the caller was expected. The code also ends up being much simpler and no longer relies on class_alias quirks.

Patch attached. The patch also includes updated PHPDoc and removal of an unnecessary backslash in a regex. (Newer versions of PHP will provide the option to error on unrecognized escape sequences in regexes that could eventually become valid, thereby changing the meaning.)


  • 2022-12-07-resolveExtendedClassToRoot-fastpath-2.diff
    5.1 KB · Views: 9


Well-known member
@PaulB This is actually called twice per entity created. As instantiateEntity calls getEntityStructure which also calls resolveExtendedClassToRoot, then it calls the constructor which calls resolveExtendedClassToRoot again.

Benchmarking this patch, and it shaves a good 40-55ms off the page-load. Running it through xdebug profiler, resolveExtendedClassToRoot was using 15% of the wall-time! Other pages do more work and load less entities, but still noticeable amount of time (1-5% or so) without any design changes.
Last edited: