Skip to content

bootMetable called for each child class, staking the same hooks numerous times #252

Open
@Levivb

Description

@Levivb

Hey,

Thanks for these great packages.

I found something to what might be seen as a bug when registering the hooks in eloquence-metable.

when working with the following structure:

abstract class Base {
    use Metable;
}

class ChildA {
}

class ChildB {
}

class ChildC {
}

while instantiating the child classes, each of 'm runs bootMetable. Which ofcourse makes sense given Laravel's bootTraits nature.

The problem arises in sofa/hookable/src/Hookable.php:27

    public static function hook($method, Closure $hook)
    {
        static::$hooks[$method][] = $hook;
    }

since the hooks are stored in a static $hooks property on the Base class, each child class shares the same property.
When instantiating 3 childclasses, all 3 childclasses will register the hooks in it's base class. Effectively duplicating the code which needs to run.

In our case, we had 200 child classes, so 200 hooks were registered in the base class. When retrieving a property, and thus running through the registered hooks of getAttribute, this caused quite a bit of performance loss and also a stack trace too deep.

I've fixed in on our side via:

use Sofa\Eloquence\Metable as MetableBase;

class Base {
    use MetableBase {
        MetableBase::bootMetable as metableBootMetable;
    }

    /** @var bool */
    private static $isMetableBooted = false;

    public static function bootMetable(): void
    {
        if (self::$isMetableBooted) {
            return;
        }

        self::$isMetableBooted = true;

        static::metableBootMetable();
    }

but it might be worthwhile to backport that fix it here

Regards,

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions