Fixed 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: 17
@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:
Thank you for reporting this issue, it has now been resolved. We are aiming to include any changes that have been made in a future XF release (2.2.13).

Change log:
Improve performance of \XF\Extension::resolveExtendedClassToRoot using an inverse lookup table
There may be a delay before changes are rolled out to the XenForo Community.
I know it's not the intended purpose, but it seems it's also useful for finding a namespace typo (DigitaLPoint vs DigitalPoint). Oops... 😂

LogicException: Tried to call XF\Extension::resolveExtendedClassToRoot on DigitaLPoint\Marketplace\Entity\ItemWatch, but extendClass was never called on the base class in src/XF/Extension.php at line 302
Top Bottom