Skip to content

Enhance Hook package and boot pipeline to support bootstrap-time stage lifecycle events for optional integrations #536

Description

@armanist

Summary

Enhance the current Hook package and boot pipeline so optional integrations can subscribe to stage lifecycle events during bootstrap.

The goal is to support stage-owned BEFORE / AFTER hook points, allowing optional integrations such as debugbar to initialize at precise boot boundaries without being hardcoded into the core pipeline.

Why

The current Hook package is close to usable for this direction, but it still needs framework-level support for bootstrap-time stage events.

Current needs:

  • optional integrations should be able to initialize during boot without hardcoded stages in core
  • boot lifecycle points should be attached to meaningful stage boundaries
  • the framework should not need special-purpose stages such as InitDebuggerStage for optional tooling
  • the current Hook mechanism should be strengthened rather than replaced

A better model is:

  • each boot stage can define BEFORE and AFTER hook names
  • BootPipeline dispatches those hook points automatically around stage execution
  • optional integrations subscribe to the stage boundary they care about

For example, debugbar should be able to initialize at:

  • LoadModulesStage::BEFORE

instead of requiring a dedicated hardcoded debugger stage in core.

Goals

  • keep the current Hook-style mechanism and evolve it
  • make hook registration available during bootstrap
  • allow boot stages to define BEFORE / AFTER hook names
  • make BootPipeline dispatch those hook points automatically
  • support optional integrations without hardcoded core dependencies
  • keep the mechanism usable for user-defined application and module events, not only framework lifecycle hooks

Proposed Changes

  • enhance the Hook package so listener registration is safe early in bootstrap
  • introduce clearer API naming:
    • listen() instead of on()
    • dispatch() instead of fire()
  • keep on() / fire() as compatibility aliases if needed
  • avoid positional payload arrays for framework-defined hook payloads and use stable named payloads
  • allow boot stages to define lifecycle hook names such as BEFORE and AFTER
  • update BootPipeline to dispatch those hooks automatically around stage execution when present

Architectural Constraint

The mechanism introduced here must remain extensible for future event sources beyond boot stages, including:

  • route-level events
  • request/response events
  • console lifecycle events
  • user-defined business/domain events

Stage lifecycle hooks are the first concrete use case for this mechanism, but they must not lock the design into a stage-only event model.

Example Direction

Stage definition:

final class LoadModulesStage implements BootStageInterface
{
    public const BEFORE = 'boot.modules.before';
    public const AFTER = 'boot.modules.after';

    public function process(AppContext $context): void
    {
        // existing stage logic
    }
}

Pipeline behavior:

foreach ($this->stages as $stage) {
    if (defined(get_class($stage) . '::BEFORE')) {
        event()->dispatch($stage::BEFORE, [
            'context' => $context,
        ]);
    }

    $stage->process($context);

    if (defined(get_class($stage) . '::AFTER')) {
        event()->dispatch($stage::AFTER, [
            'context' => $context,
        ]);
    }
}

Optional integration subscription:

event()->listen(LoadModulesStage::BEFORE, function (array $payload): void {
    $context = $payload['context'];
    debugbar()->boot($context);
});

Acceptance Criteria

  • hook/event listener registration is safe during bootstrap
  • boot stages can define BEFORE / AFTER lifecycle hook names
  • BootPipeline dispatches those lifecycle hooks automatically when defined
  • framework-defined hook payloads use stable named payloads rather than positional arrays
  • clearer method names such as listen() and dispatch() are introduced
  • existing on() / fire() usage remains compatible if aliasing is chosen
  • optional integrations can subscribe to boot stage boundaries without hardcoded core stages
  • the design remains extensible for non-stage framework events and user-defined business events
  • tests cover bootstrap-time registration and stage lifecycle hook dispatch
  • docs are updated as needed

Notes

Relevant code:

  • src/Hook/HookManager.php
  • src/Hook/Helpers/hook.php
  • src/App/BootPipeline.php
  • src/App/Stages/InitHttpStage.php
  • src/App/Stages/LoadModulesStage.php
  • tests/Unit/Hook/HookManagerTest.php
  • tests/Unit/Hook/Helpers/HookHelperTest.php

This ticket should be treated as the foundation for:

  • lifecycle hooks
  • debugbar via lifecycle hooks

Metadata

Metadata

Assignees

No one assigned

    Labels

    hooksHook and event system
    No fields configured for Feature.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions