From ca4dacb3269c10b1fa1a7fd46b7b42f2d48d5477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= Date: Fri, 16 Oct 2026 08:54:00 +0200 Subject: [PATCH 01/18] Update README to reflect the `develop` branch structure and guidelines Revised README with the following changes: - Clarified that the `develop` branch is for active integration and ongoing development. - Removed outdated content related to FireHub's general overview and repository purpose, replacing it with development-specific information. - Highlighted stability guarantees (or lack thereof) for contributors and advanced testers. - Added badges for PHPUnit, PHPStan, and Codecov to provide visibility into CI/CD pipelines. - Updated links and references to point to the `develop` branch instead of `master`. These updates align the README with the branch's purpose and contribution workflows. --- README.md | 134 +++++++++++++++++------------------------------------- 1 file changed, 41 insertions(+), 93 deletions(-) diff --git a/README.md b/README.md index 5cd4227..7555139 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ version + /> +[![PHPStan](https://github.com/The-FireHub-Project/Application/actions/workflows/PHPStan.yml/badge.svg?branch=develop)](https://github.com/The-FireHub-Project/Application/actions/workflows/PHPStan.yml) +[![PHPUnit](https://github.com/The-FireHub-Project/Application/actions/workflows/PHPUnit.yml/badge.svg?branch=develop)](https://github.com/The-FireHub-Project/Application/actions/workflows/PHPUnit.yml) +[![Codecov](https://codecov.io/gh/The-FireHub-Project/Application/branch/develop/graph/badge.svg?token=XW2YEONF51)](https://app.codecov.io/gh/The-FireHub-Project/Application/tree/develop) +

- - GitHub created at - + GitHub last commit (branch) - + GitHub activity (branch) - - GitHub contributors - - GitHub language count - - GitHub top language - + GitHub repo size - - GitHub code size

@@ -71,78 +50,47 @@ Designed as a shared, edition-agnostic runtime component for Standard, Professio --- -## FireHub Icon About FireHub - -**FireHub** is a modular PHP framework built around: - -- Clean Architecture -- Domain-Driven Design (DDD) -- Explicit boundaries between Core, Adapters, and Modules -- Long-term maintainability and scalability +## FireHub Icon Development Branch -FireHub is designed for building **enterprise-grade PHP applications** without sacrificing clarity or developer experience. +⚠️ **This is the `develop` branch** -## FireHub Icon Repository Purpose +- Unstable +- APIs may change without notice +- Not intended for production use -FireHub Application provides application-level lifecycle management and orchestration for the FireHub Framework. It coordinates application initialization, configuration, readiness, and termination while delegating fundamental runtime lifecycle operations to the FireHub Kernel. +## FireHub Icon Branch Purpose -Its responsibilities include: +The `develop` branch is the **primary integration branch** for all ongoing development. -* **Application lifecycle** — managing application configuration, initialization, readiness, failure, and shutdown independently of the underlying Kernel lifecycle. -* **Bootstrap orchestration** — executing application-specific initialization components in a controlled order. -* **Termination orchestration** — coordinating application-specific cleanup before the underlying Kernel shuts down. -* **Application context** — providing application identity, environment, and execution metadata. -* **Service integration** — coordinating application service registration and initialization through dedicated runtime components. -* **Configuration integration** — preparing and exposing application configuration during initialization. -* **Module integration** — coordinating optional application modules and capabilities through explicit contracts. +It serves as the staging area where: +- Feature branches are merged +- Bug fixes are integrated +- Experimental work is stabilized +- Code is prepared for upcoming releases -FireHub Application uses the FireHub Kernel for fundamental runtime initialization and termination. The Kernel remains independent of application-specific behavior, allowing other runtime components, such as workers and schedulers, to reuse the same infrastructure. +All **release branches** are created **from `develop`**. -FireHub Application does not implement HTTP routing, console commands, background job processing, dependency resolution, or business logic. These responsibilities belong to dedicated adapters, runtime components, and application modules. +## Stability Guarantee -The Application runtime is edition-agnostic and supports Standard, Professional, and Enterprise applications through the contracts and extensions provided by their respective Core editions. +❌ No backward compatibility guarantee +❌ APIs may change without notice +❌ Behavior may be incomplete or inconsistent +❌ Breaking changes are expected -## FireHub Icon Security +This branch is intended **only for contributors and advanced testers**. -For security reporting and guidelines, please refer to our [SECURITY.md](https://github.com/The-FireHub-Project/.github/blob/master/.github/SECURITY.md) file. - -## FireHub Icon Community - -![GitHub watchers](https://img.shields.io/github/watchers/The-FireHub-Project/Application?style=social) -![GitHub Repo stars](https://img.shields.io/github/stars/The-FireHub-Project/Application?style=social) -![GitHub forks](https://img.shields.io/github/forks/The-FireHub-Project/Application?style=social) - -- Be sure to review the [Code of Conduct](https://github.com/The-FireHub-Project/.github/blob/master/.github/CODE_OF_CONDUCT.md). -- Read a [Contributing](https://github.com/The-FireHub-Project/.github/blob/master/.github/CONTRIBUTING.md) file to see how you can contribute to this project. -- Read our [Security policy](https://github.com/The-FireHub-Project/.github/blob/master/.github/SECURITY.md) file to see how you can report a vulnerability. -- Review the project [Governance model](https://github.com/The-FireHub-Project/.github/blob/master/.github/GOVERNANCE.md), including roles, decision-making, RFC process, and releases. - -## FireHub Icon Support - -

- - GitHub in-progress issues - - GitHub total issues - - GitHub opened pull requests -

+## FireHub Icon Composer Usage (Not Recommended) -For help with FireHub products, please refer to the following resources: +For internal testing only: -- **General questions & discussions**: [GitHub Discussions](https://github.com/orgs/The-FireHub-Project/discussions) -- **Bug reports & issues**: [Support](https://github.com/The-FireHub-Project/Application/issues) -- **Documentation & guides**: [FireHub Docs](https://the-firehub-project.github.io) -- **Commercial support** (Pro/Enterprise): contact your account manager +```json +{ + "require": { + "the-firehub-project/application": "dev-develop" + } +} +``` +⚠️ Never use dev-develop in production. ## FireHub Icon Authors and Contributors From 0d3ff1011f282fbbc17ad5135c43280a43cb3e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= Date: Fri, 16 Oct 2026 12:15:58 +0200 Subject: [PATCH 02/18] Add foundational application context and kernel integration. Introduced the `Application` class as the primary runtime entry point for FireHub projects, encapsulating application lifecycle management through a dedicated `Kernel`. Introduced the `Context` class to define immutable application-wide metadata with an execution environment (`Environment` enum) and a strongly typed `Name`. Updated `composer.json` to include dependencies for the FireHub Core Standard, Foundation, and other essential components. These changes establish the foundational structure for building and managing a FireHub application. --- composer.json | 3 ++ src/Application.php | 81 +++++++++++++++++++++++++++++++++ src/Context.php | 89 +++++++++++++++++++++++++++++++++++++ src/Context/Environment.php | 59 ++++++++++++++++++++++++ src/Context/Name.php | 71 +++++++++++++++++++++++++++++ 5 files changed, 303 insertions(+) create mode 100644 src/Application.php create mode 100644 src/Context.php create mode 100644 src/Context/Environment.php create mode 100644 src/Context/Name.php diff --git a/composer.json b/composer.json index 56c8051..ece3d2a 100644 --- a/composer.json +++ b/composer.json @@ -8,6 +8,9 @@ "require": { "php": "^8.5", + "the-firehub-project/core-standard": "dev-develop", + "the-firehub-project/runtime": ">=0.1.0 <1.0.0", + "the-firehub-project/foundation": "dev-develop", "the-firehub-project/kernel": "dev-develop" }, "require-dev": { diff --git a/src/Application.php b/src/Application.php new file mode 100644 index 0000000..2b97493 --- /dev/null +++ b/src/Application.php @@ -0,0 +1,81 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.2 + * @package Application + */ + +namespace FireHub\Application; + +use FireHub\Kernel\Kernel; + +/** + * ### Represents a FireHub application + * + * Provides the primary entry point for an application within the FireHub runtime. + * + * Maintains a dedicated kernel responsible for managing the application lifecycle. + * + * Application-specific services, configuration, and execution capabilities can be integrated independently + * without extending or duplicating the kernel API. + * @since 1.0.0 + */ +final readonly class Application { + + /** + * ### Application kernel + * @since 1.0.0 + */ + private Kernel $kernel; + + /** + * ### Constructor + * @since 1.0.0 + * + * @param \FireHub\Application\Context $context

+ * Application context. + *

+ * + * @return void + */ + public function __construct ( + private Context $context + ) { + + $this->kernel = new Kernel; + + } + + /** + * ### Gets the application kernel + * + * Returns the kernel responsible for managing the application lifecycle. + * @since 1.0.0 + * + * @return \FireHub\Kernel\Kernel Application kernel. + */ + public function kernel ():Kernel { + + return $this->kernel; + + } + + /** + * ### Gets the application execution context + * @since 1.0.0 + * + * @return \FireHub\Application\Context Application context. + */ + public function context ():Context { + + return $this->context; + + } + +} \ No newline at end of file diff --git a/src/Context.php b/src/Context.php new file mode 100644 index 0000000..1e69f23 --- /dev/null +++ b/src/Context.php @@ -0,0 +1,89 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.2 + * @package Application + */ + +namespace FireHub\Application; + +use FireHub\Core\Type\ValueObject; +use FireHub\Application\Context\ { + Environment, Name +}; + +/** + * ### Represents the application execution context + * + * Defines the immutable identity and execution environment of an application. + * + * The context contains application-wide metadata and remains unchanged throughout the application lifecycle. + * @since 1.0.0 + * + * @extends \FireHub\Core\Type\ValueObject, + * environment: \FireHub\Application\Context\Environment + * }> + */ +final readonly class Context extends ValueObject { + + /** + * ### Constructor + * @since 1.0.0 + * + * @param \FireHub\Application\Context\Name $name

+ * Application name. + *

+ * @param \FireHub\Application\Context\Environment $environment

+ * Application environment. + *

+ * + * @return void + */ + public function __construct ( + public Name $name, + public Environment $environment + ) {} + + /** + * @inheritDoc + * + * @since 1.0.0 + */ + public function value ():array { + + return [ + 'name' => $this->name, + 'environment' => $this->environment + ]; + + } + + /** + * @inheritDoc + * + * @since 1.0.0 + * + * @uses \FireHub\Application\Context\Name::value() To get the name value. + * + * @return array{ + * name: non-empty-string, + * environment: \FireHub\Application\Context\Environment + * } Comparison value. + */ + protected function comparisonValue ():array { + + return [ + 'name' => $this->name->value(), + 'environment' => $this->environment + ]; + + } + +} \ No newline at end of file diff --git a/src/Context/Environment.php b/src/Context/Environment.php new file mode 100644 index 0000000..82859aa --- /dev/null +++ b/src/Context/Environment.php @@ -0,0 +1,59 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.1 + * @package Application + */ + +namespace FireHub\Application\Context; + +/** + * ### Defines the application execution environment + * + * Represents the environment in which a FireHub application is executed. + * + * Application environments allow the framework and its components to distinguish between production, staging, + * development, and testing configurations without relying on arbitrary string values. + * @since 1.0.0 + */ +enum Environment { + + /** + * ### Production environment + * + * Represents a live application environment intended for end users. + * @since 1.0.0 + */ + case PRODUCTION; + + /** + * ### Staging environment + * + * Represents a pre-production environment used for deployment verification and acceptance testing. + * @since 1.0.0 + */ + case STAGING ; + + /** + * ### Development environment + * + * Represents an environment used for application development, debugging, and local execution. + * @since 1.0.0 + */ + case DEVELOPMENT; + + /** + * ### Testing environment + * + * Represents an environment used for automated tests and application behavior verification. + * @since 1.0.0 + */ + case TESTING; + +} \ No newline at end of file diff --git a/src/Context/Name.php b/src/Context/Name.php new file mode 100644 index 0000000..734e5ad --- /dev/null +++ b/src/Context/Name.php @@ -0,0 +1,71 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.2 + * @package Application + */ + +namespace FireHub\Application\Context; + +use FireHub\Core\Type\Str\Encoding; +use FireHub\Foundation\Identifier\Name as IdentifierName; +use FireHub\Foundation\Identifier\Exception\InvalidIdentifierValueException; +use FireHub\Runtime; + +/** + * ### Represents an application name + * + * Defines an immutable, strongly typed name used to identify an application within the FireHub ecosystem. + * + * The application name inherits its fundamental validation rules and value semantics from the Foundation identifier + * name implementation. + * + * Application names are compared by their concrete type and underlying value, ensuring that they remain distinct + * from other identifier types. + * @since 1.0.0 + * + * @template TValue of non-empty-string + * + * @extends \FireHub\Foundation\Identifier\Name + */ +final readonly class Name extends IdentifierName { + + /** + * ### Constructor + * @since 1.0.0 + * + * @uses \FireHub\Core\Type\Identifier::guard() To enforce the invariant that an identifier name cannot be empty. + * + * @param TValue $value

+ * The identifier name. + *

+ * + * @throws \FireHub\Foundation\Identifier\Exception\InvalidIdentifierValueException If the identifier name is + * empty or does not meet the length requirements. + * @throws \FireHub\Core\Exception\FireHubException If the condition is not met. + * @throws \FireHub\Core\Type\Exception\ValueObjectException If the exception is not a FireHubException. + * + * @return void + */ + public function __construct (string $value) { + + parent::__construct($value); + + $length = Runtime\Str\MB\Inspection::length($value, Encoding::UTF_8); + + $this->guard( + fn() => $length >= 3 && $length <= 64, + fn() => new InvalidIdentifierValueException( + 'Application name must contain between 3 and 64 characters.' + ) + ); + + } + +} \ No newline at end of file From a1d25b1684ea7355cbee1c2fd0946e10dc9698c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= Date: Mon, 19 Oct 2026 09:10:59 +0200 Subject: [PATCH 03/18] Expand application lifecycle management with initialization and disposal support. Introduced key components to manage the application lifecycle, including initialization and disposal mechanisms: - Added `Disposer` and `Initializer` interfaces to define contracts for application-specific resource management. - Implemented the `State` enum to represent various application lifecycle states. - Enhanced the `Application` class to support initializer/disposer registration, state management, and lifecycle transitions (`initialize` and `dispose` methods). - Added custom exceptions (`ApplicationInitializationException`, `ApplicationDisposalException`, `InvalidApplicationStateException`) to handle lifecycle errors. These changes establish a structured framework for controlling application startup and shutdown processes. --- src/Application.php | 251 +++++++++++++++++- src/Disposer.php | 49 ++++ .../ApplicationDisposalException.php | 32 +++ .../ApplicationInitializationException.php | 32 +++ .../InvalidApplicationStateException.php | 32 +++ src/Initializer.php | 47 ++++ src/State.php | 84 ++++++ 7 files changed, 521 insertions(+), 6 deletions(-) create mode 100644 src/Disposer.php create mode 100644 src/Exception/ApplicationDisposalException.php create mode 100644 src/Exception/ApplicationInitializationException.php create mode 100644 src/Exception/InvalidApplicationStateException.php create mode 100644 src/Initializer.php create mode 100644 src/State.php diff --git a/src/Application.php b/src/Application.php index 2b97493..a714e45 100644 --- a/src/Application.php +++ b/src/Application.php @@ -7,23 +7,37 @@ * @copyright 2026-present The FireHub Project - All rights reserved * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 * - * @php-version >=8.2 + * @php-version >=8.5 * @package Application */ namespace FireHub\Application; +use FireHub\Core\Meta\Enum\Kernel\State as KernelState; use FireHub\Kernel\Kernel; +use FireHub\Kernel\Lifecycle\ { + Engine, States +}; +use FireHub\Kernel\Lifecycle\Exception\ { + InvalidLifecycleStateException, LifecycleExecutionException +}; +use FireHub\Application\Exception\ { + ApplicationDisposalException, ApplicationInitializationException, InvalidApplicationStateException +}; /** - * ### Represents a FireHub application + * ### Manages the application lifecycle * - * Provides the primary entry point for an application within the FireHub runtime. + * Represents a FireHub application and coordinates its initialization, execution readiness, and resource disposal. * - * Maintains a dedicated kernel responsible for managing the application lifecycle. + * Each application owns an independent lifecycle engine configured with application-specific states. * - * Application-specific services, configuration, and execution capabilities can be integrated independently - * without extending or duplicating the kernel API. + * Application initializers and disposers are registered independently of kernel bootstrappers and terminators. + * + * The kernel must be booted before application initialization begins. Kernel initialization and termination remain + * explicit operations and are not automatically performed by the application. + * + * Lifecycle failures are exposed through application-specific exceptions. * @since 1.0.0 */ final readonly class Application { @@ -34,6 +48,14 @@ */ private Kernel $kernel; + /** + * ### Application lifecycle engine + * @since 1.0.0 + * + * @var Engine + */ + private Engine $lifecycle; + /** * ### Constructor * @since 1.0.0 @@ -50,6 +72,223 @@ public function __construct ( $this->kernel = new Kernel; + $this->lifecycle = new Engine( + new States( + State::CREATED, + State::INITIALIZING, + State::INITIALIZED, + State::DISPOSING, + State::DISPOSED, + State::INITIALIZATION_FAILED, + State::DISPOSAL_FAILED + ) + ); + + } + + /** + * ### Registers an application initializer + * + * Registers an initializer for execution during application initialization. + * + * Initializers are executed according to their configured execution order. Registration is permitted only before + * initialization begins. + * @since 1.0.0 + * + * @uses \FireHub\Kernel\Lifecycle\Engine::registerStartup() To register an initializer. + * @uses \FireHub\Application\Initializer::initialize() To execute the initializer. + * + * @param \FireHub\Application\Initializer $initializer

+ * Initializer to register. + *

+ * @param int $order [optional]

+ * Execution order. + *

+ * + * @throws \FireHub\Application\Exception\InvalidApplicationStateException If registration is not allowed. + * + * @return void + */ + public function registerInitializer (Initializer $initializer, int $order = 0):void { + + try { + + $this->lifecycle->registerStartup( + function () use ($initializer):void { + + $initializer->initialize($this); + + }, + $order + ); + + } catch (InvalidLifecycleStateException $exception) { + + throw new InvalidApplicationStateException( + 'Cannot register an initializer in the current application state.', + previous: $exception + ); + + } + + } + + /** + * ### Registers an application disposer + * + * Registers a disposer for execution during application disposal. + * + * Disposers are executed according to their configured execution order. Registration is permitted only before + * initialization begins. + * @since 1.0.0 + * + * @uses \FireHub\Kernel\Lifecycle\Engine::registerShutdown() To register a disposer. + * @uses \FireHub\Application\Disposer::dispose() To execute the disposer. + * + * @param \FireHub\Application\Disposer $disposer

+ * Disposer to register. + *

+ * @param int $order

+ * [optional] Execution order. + *

+ * + * @throws \FireHub\Application\Exception\InvalidApplicationStateException If registration is not allowed. + * + * @return void + */ + public function registerDisposer (Disposer $disposer, int $order = 0):void { + + try { + + $this->lifecycle->registerShutdown( + function () use ($disposer):void { + + $disposer->dispose($this); + + }, + $order + ); + + } catch (InvalidLifecycleStateException $exception) { + + throw new InvalidApplicationStateException( + 'Cannot register a disposer in the current application state.', + previous: $exception + ); + + } + + } + + /** + * ### Initializes the application + * + * Executes registered initializers and prepares the application for execution. + * + * The kernel must be fully booted before application initialization can begin. + * + * Initialization stops immediately when an initializer fails. + * + * Calling this method after successful initialization has no effect. + * @since 1.0.0 + * + * @uses \FireHub\Application\Application::state() To get the current state. + * @uses \FireHub\Kernel\Lifecycle\Engine::start() To execute registered initializers. + * + * @throws \FireHub\Application\Exception\InvalidApplicationStateException If initialization is not allowed. + * @throws \FireHub\Application\Exception\ApplicationInitializationException If an initializer fails. + * + * @return void + */ + public function initialize ():void { + + if ($this->state() === State::INITIALIZED) return; + + if ($this->kernel->state() !== KernelState::BOOTED) + throw new InvalidApplicationStateException( + 'The application cannot initialize before the kernel is booted.' + ); + + try { + + $this->lifecycle->start(); + + } catch (InvalidLifecycleStateException $exception) { + + throw new InvalidApplicationStateException( + 'Cannot initialize the application in the current state.', + previous: $exception + ); + + } catch (LifecycleExecutionException $exception) { + + throw new ApplicationInitializationException( + previous: $exception->getPrevious() ?? $exception + ); + + } + + } + + /** + * ### Disposes the application + * + * Executes registered disposers and releases resources managed by the application. + * + * Disposal may be performed after successful initialization, after initialization failure, or before + * initialization begins. + * + * All registered disposers are attempted even when an individual disposer fails. + * + * Calling this method after successful disposal has no effect. + * @since 1.0.0 + * + * @uses \FireHub\Kernel\Lifecycle\Engine::stop() To execute registered disposers. + * + * @throws \FireHub\Application\Exception\InvalidApplicationStateException If disposal is not allowed. + * @throws \FireHub\Application\Exception\ApplicationDisposalException If a disposer fails. + * + * @return void + */ + public function dispose ():void { + + try { + + $this->lifecycle->stop(); + + } catch (InvalidLifecycleStateException $exception) { + + throw new InvalidApplicationStateException( + 'Cannot dispose the application in the current state.', + previous: $exception + ); + + } catch (LifecycleExecutionException $exception) { + + throw new ApplicationDisposalException( + previous: $exception->getPrevious() ?? $exception + ); + + } + + } + + /** + * ### Gets the current application lifecycle state + * + * Returns the application's own lifecycle state directly from its lifecycle engine. + * + * No conversion from kernel states is performed. + * @since 1.0.0 + * + * @uses \FireHub\Kernel\Lifecycle\Engine::state() To get the current state. + * + * @return \FireHub\Application\State Current application lifecycle state. + */ + public function state ():State { + + return $this->lifecycle->state(); + } /** diff --git a/src/Disposer.php b/src/Disposer.php new file mode 100644 index 0000000..854d994 --- /dev/null +++ b/src/Disposer.php @@ -0,0 +1,49 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=7.4 + * @package Application + */ + +namespace FireHub\Application; + +/** + * ### Defines an application disposer + * + * Establishes the contract for releasing application-specific resources and performing cleanup during + * the application shutdown lifecycle. + * + * Disposers are executed during the application shutdown in their configured execution order. Each disposer + * receives the application instance, allowing access to its context and application-managed components. + * + * Disposal operations should release resources safely and should not depend on the application remaining + * operational after shutdown completes. + * + * This contract is independent of the kernel terminator contract and does not participate directly in + * the kernel lifecycle. + * @since 1.0.0 + */ +interface Disposer { + + /** + * ### Disposes of an application component + * + * Performs application-specific cleanup, releases allocated resources, and completes any required + * finalization operations. + * @since 1.0.0 + * + * @param \FireHub\Application\Application $application

+ * Application being shut down. + *

+ * + * @return void + */ + public function dispose (Application $application):void; + +} \ No newline at end of file diff --git a/src/Exception/ApplicationDisposalException.php b/src/Exception/ApplicationDisposalException.php new file mode 100644 index 0000000..e9184ed --- /dev/null +++ b/src/Exception/ApplicationDisposalException.php @@ -0,0 +1,32 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.1 + * @package Application + */ + +namespace FireHub\Application\Exception; + +use FireHub\Core\Exception\RuntimeException; + +/** + * ### Represents an application disposal failure + * + * Indicates that one or more application disposers failed while releasing managed resources. + * + * Remaining disposers are attempted even when an individual disposer fails. + * + * The original failure is preserved as the previous exception. + * @since 1.0.0 + */ +final class ApplicationDisposalException extends RuntimeException { + + protected const string DEFAULT_MESSAGE = 'The application failed to dispose'; + +} \ No newline at end of file diff --git a/src/Exception/ApplicationInitializationException.php b/src/Exception/ApplicationInitializationException.php new file mode 100644 index 0000000..324cb94 --- /dev/null +++ b/src/Exception/ApplicationInitializationException.php @@ -0,0 +1,32 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.1 + * @package Application + */ + +namespace FireHub\Application\Exception; + +use FireHub\Core\Exception\RuntimeException; + +/** + * ### Represents an application disposal failure + * + * Indicates that one or more application disposers failed while releasing managed resources. + * + * Remaining disposers are attempted even when an individual disposer fails. + * + * The original failure is preserved as the previous exception. + * @since 1.0.0 + */ +final class ApplicationInitializationException extends RuntimeException { + + protected const string DEFAULT_MESSAGE = 'The application failed to initialize'; + +} \ No newline at end of file diff --git a/src/Exception/InvalidApplicationStateException.php b/src/Exception/InvalidApplicationStateException.php new file mode 100644 index 0000000..e4f6c7b --- /dev/null +++ b/src/Exception/InvalidApplicationStateException.php @@ -0,0 +1,32 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.1 + * @package Application + */ + +namespace FireHub\Application\Exception; + +use FireHub\Core\Exception\Runtime\KernelException; + +/** + * ### Represents an application initialization failure + * + * Indicates that one or more application initializers could not complete successfully. + * + * The original failure is preserved as the previous exception, allowing callers to inspect the underlying cause. + * + * An initialization failure prevents the application from entering its initialized state. + * @since 1.0.0 + */ +final class InvalidApplicationStateException extends KernelException { + + protected const string DEFAULT_MESSAGE = 'The application is in an invalid state'; + +} \ No newline at end of file diff --git a/src/Initializer.php b/src/Initializer.php new file mode 100644 index 0000000..b00ed71 --- /dev/null +++ b/src/Initializer.php @@ -0,0 +1,47 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=7.4 + * @package Application + */ + +namespace FireHub\Application; + +/** + * ### Defines an application initializer + * + * Establishes the contract for initializing application-specific components during the application startup + * lifecycle. + * + * Initializers are executed during application boot in their configured execution order. Each initializer + * receives the application instance, allowing access to its context and application-managed components. + * + * Initialization must complete successfully before later initializers are executed. + * + * This contract is independent of the kernel bootstrapper contract and does not participate directly in + * the kernel lifecycle. + * @since 1.0.0 + */ +interface Initializer { + + /** + * ### Initializes an application component + * + * Performs application-specific initialization and prepares the component for application execution. + * @since 1.0.0 + * + * @param \FireHub\Application\Application $application

+ * Application being initialized. + *

+ * + * @return void + */ + public function initialize (Application $application):void; + +} \ No newline at end of file diff --git a/src/State.php b/src/State.php new file mode 100644 index 0000000..471404f --- /dev/null +++ b/src/State.php @@ -0,0 +1,84 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.1 + * @package Application + */ + +namespace FireHub\Application; + +/** + * ### Defines application lifecycle states + * + * Represents the possible states of an application throughout its initialization, execution readiness, + * and resource disposal lifecycle. + * + * Application lifecycle states are independent of kernel lifecycle states and reflect the execution + * of application initializers and disposers. + * @since 1.0.0 + */ +enum State { + + /** + * ### Created + * + * The application has been instantiated but has not started initialization. + * @since 1.0.0 + */ + case CREATED; + + /** + * ### Initializing + * + * The application is currently executing its initializers and preparing for execution. + * @since 1.0.0 + */ + case INITIALIZING; + + /** + * ### Initialized + * + * The application has successfully completed initialization and is ready for execution. + * @since 1.0.0 + */ + case INITIALIZED; + + /** + * ### Disposing + * + * The application is currently executing its disposers and releasing managed resources. + * @since 1.0.0 + */ + case DISPOSING; + + /** + * ### Disposed + * + * The application has completed its disposal process and is no longer operational. + * @since 1.0.0 + */ + case DISPOSED; + + /** + * ### Initialization Failed + * + * The application failed to execute its initializers and cannot continue its normal lifecycle. + * @since 1.0.0 + */ + case INITIALIZATION_FAILED; + + /** + * ### Disposal Failed + * + * The application failed to execute its disposers and cannot complete its normal lifecycle. + * @since 1.0.0 + */ + case DISPOSAL_FAILED; + +} \ No newline at end of file From 798875db0bf28a97e08475197bb7f958139f5984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= Date: Mon, 19 Oct 2026 10:09:04 +0200 Subject: [PATCH 04/18] Refactor `Application` lifecycle and enhance kernel integration. Streamlined `Application` lifecycle management by removing redundant state checks and unused imports. Improved documentation with additional annotations for `Kernel::dependencyBoundary()` utilization. Updated kernel initialization to leverage `dependencyBoundary` for enhanced dependency handling. These changes simplify lifecycle operations and improve integration with the kernel's architecture. --- src/Application.php | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Application.php b/src/Application.php index a714e45..d36f1e7 100644 --- a/src/Application.php +++ b/src/Application.php @@ -13,7 +13,6 @@ namespace FireHub\Application; -use FireHub\Core\Meta\Enum\Kernel\State as KernelState; use FireHub\Kernel\Kernel; use FireHub\Kernel\Lifecycle\ { Engine, States @@ -60,6 +59,8 @@ * ### Constructor * @since 1.0.0 * + * @uses \FireHub\Kernel\Kernel::dependencyBoundary() To create a kernel dependency boundary. + * * @param \FireHub\Application\Context $context

* Application context. *

@@ -81,7 +82,8 @@ public function __construct ( State::DISPOSED, State::INITIALIZATION_FAILED, State::DISPOSAL_FAILED - ) + ), + $this->kernel->dependencyBoundary() ); } @@ -192,7 +194,6 @@ function () use ($disposer):void { * Calling this method after successful initialization has no effect. * @since 1.0.0 * - * @uses \FireHub\Application\Application::state() To get the current state. * @uses \FireHub\Kernel\Lifecycle\Engine::start() To execute registered initializers. * * @throws \FireHub\Application\Exception\InvalidApplicationStateException If initialization is not allowed. @@ -202,13 +203,6 @@ function () use ($disposer):void { */ public function initialize ():void { - if ($this->state() === State::INITIALIZED) return; - - if ($this->kernel->state() !== KernelState::BOOTED) - throw new InvalidApplicationStateException( - 'The application cannot initialize before the kernel is booted.' - ); - try { $this->lifecycle->start(); From 0547d5017665105a0051bf8ade3fad3a6bc30b00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= <56673130+dieselxxx@users.noreply.github.com> Date: Mon, 19 Oct 2026 10:16:19 +0200 Subject: [PATCH 05/18] feat(application): support shared kernel lifecycle ownership --- src/Application.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Application.php b/src/Application.php index d36f1e7..69eec4e 100644 --- a/src/Application.php +++ b/src/Application.php @@ -33,7 +33,7 @@ * * Application initializers and disposers are registered independently of kernel bootstrappers and terminators. * - * The kernel must be booted before application initialization begins. Kernel initialization and termination remain + * The parent kernel must be booted before application initialization begins. Kernel initialization and termination remain * explicit operations and are not automatically performed by the application. * * Lifecycle failures are exposed through application-specific exceptions. @@ -64,14 +64,18 @@ * @param \FireHub\Application\Context $context

* Application context. *

+ * @param null|\FireHub\Kernel\Kernel $kernel [optional]

+ * Kernel instance to share with other runtime components. + *

* * @return void */ public function __construct ( - private Context $context + private Context $context, + ?Kernel $kernel = null ) { - $this->kernel = new Kernel; + $this->kernel = $kernel ?? new Kernel; $this->lifecycle = new Engine( new States( @@ -288,7 +292,7 @@ public function state ():State { /** * ### Gets the application kernel * - * Returns the kernel responsible for managing the application lifecycle. + * Returns the parent kernel on which the application lifecycle depends. * @since 1.0.0 * * @return \FireHub\Kernel\Kernel Application kernel. From d36b026076d8c88b397edefcf07fde0961392e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= Date: Mon, 19 Oct 2026 10:55:36 +0200 Subject: [PATCH 06/18] Enhance lifecycle state management and refactor `Application` initialization and disposal. Streamlined `Application` lifecycle processes by updating `Engine` to manage state transitions with the new `execute()` method. Replaced explicit state definitions with `State` constants directly, simplifying lifecycle construction. Key improvements: - Updated `initialize()` and `dispose()` methods to leverage `Engine::execute()` for handling callbacks and state changes. - Added state checks to prevent redundant initialization and disposal attempts. - Enhanced annotations for clearer documentation and better insight into lifecycle interactions. These changes improve the readability, robustness, and maintainability of lifecycle management within the `Application` class. --- src/Application.php | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/src/Application.php b/src/Application.php index 69eec4e..b1fc733 100644 --- a/src/Application.php +++ b/src/Application.php @@ -78,15 +78,7 @@ public function __construct ( $this->kernel = $kernel ?? new Kernel; $this->lifecycle = new Engine( - new States( - State::CREATED, - State::INITIALIZING, - State::INITIALIZED, - State::DISPOSING, - State::DISPOSED, - State::INITIALIZATION_FAILED, - State::DISPOSAL_FAILED - ), + State::CREATED, $this->kernel->dependencyBoundary() ); @@ -198,7 +190,9 @@ function () use ($disposer):void { * Calling this method after successful initialization has no effect. * @since 1.0.0 * + * @uses \FireHub\Kernel\Lifecycle\Engine::state() To get the current state. * @uses \FireHub\Kernel\Lifecycle\Engine::start() To execute registered initializers. + * @uses \FireHub\Kernel\Lifecycle\Engine::execute() To execute a callback. * * @throws \FireHub\Application\Exception\InvalidApplicationStateException If initialization is not allowed. * @throws \FireHub\Application\Exception\ApplicationInitializationException If an initializer fails. @@ -207,9 +201,18 @@ function () use ($disposer):void { */ public function initialize ():void { + if ($this->state() === State::INITIALIZED) return; + try { - $this->lifecycle->start(); + $this->lifecycle->execute( + callback: fn() => $this->lifecycle->start(), + from: [State::CREATED], + success: State::INITIALIZED, + failure: State::INITIALIZATION_FAILED, + entering: State::INITIALIZING, + acquire_parent: true + ); } catch (InvalidLifecycleStateException $exception) { @@ -241,6 +244,8 @@ public function initialize ():void { * Calling this method after successful disposal has no effect. * @since 1.0.0 * + * @uses \FireHub\Kernel\Lifecycle\Engine::state() To get the current state. + * @uses \FireHub\Kernel\Lifecycle\Engine::execute() To execute a callback. * @uses \FireHub\Kernel\Lifecycle\Engine::stop() To execute registered disposers. * * @throws \FireHub\Application\Exception\InvalidApplicationStateException If disposal is not allowed. @@ -250,9 +255,24 @@ public function initialize ():void { */ public function dispose ():void { + if ($this->state() === State::DISPOSED) return; + try { - $this->lifecycle->stop(); + $this->lifecycle->execute( + callback: fn() => $this->lifecycle->stop(), + from: [ + State::CREATED, + State::INITIALIZED, + State::INITIALIZATION_FAILED + ], + success: State::DISPOSED, + failure: State::DISPOSAL_FAILED, + entering: State::DISPOSING, + acquire_parent: true, + release_parent: true, + require_no_dependents: true + ); } catch (InvalidLifecycleStateException $exception) { From 47180c9377412f544913d3e5ad9336eeef3343f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= Date: Mon, 19 Oct 2026 13:33:05 +0200 Subject: [PATCH 07/18] Add usage examples for `Application` lifecycle methods and state management. Expanded inline documentation within the `Application` class by adding detailed usage examples for key lifecycle methods (`initialize`, `dispose`, etc.) and state management. These examples clarify usage patterns and demonstrate how to interact with `Context`, `State`, and the application runtime. Additionally: - Removed unused import `States` from `Lifecycle`. - Improved code readability and developer experience through enhanced documentation. These updates improve developer onboarding and provide better insight into the lifecycle operations of the `Application` class. --- src/Application.php | 143 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 3 deletions(-) diff --git a/src/Application.php b/src/Application.php index b1fc733..89b036b 100644 --- a/src/Application.php +++ b/src/Application.php @@ -14,9 +14,7 @@ namespace FireHub\Application; use FireHub\Kernel\Kernel; -use FireHub\Kernel\Lifecycle\ { - Engine, States -}; +use FireHub\Kernel\Lifecycle\Engine; use FireHub\Kernel\Lifecycle\Exception\ { InvalidLifecycleStateException, LifecycleExecutionException }; @@ -37,6 +35,31 @@ * explicit operations and are not automatically performed by the application. * * Lifecycle failures are exposed through application-specific exceptions. + * + * + * use FireHub\Application\Application; + * use FireHub\Application\Context; + * use FireHub\Application\Context\Name; + * use FireHub\Application\Context\Environment; + * + * $app = new Application( + * new Context( + * new Name('FireHub'), + * Environment::PRODUCTION + * )); + * + * $app->registerInitializer(new ClassThatImplementsInitializer); + * $app->registerDisposer(new ClassThatImplementsDisposer); + * + * $app->kernel()->boot(); + * $app->initialize(); + * + * // write code here + * + * $app->dispose(); + * $app->kernel()->shutdown(); + * + * * @since 1.0.0 */ final readonly class Application { @@ -91,6 +114,22 @@ public function __construct ( * * Initializers are executed according to their configured execution order. Registration is permitted only before * initialization begins. + * + * + * use FireHub\Application\Application; + * use FireHub\Application\Context; + * use FireHub\Application\Context\Name; + * use FireHub\Application\Context\Environment; + * + * $app = new Application( + * new Context( + * new Name('FireHub'), + * Environment::PRODUCTION + * )); + * + * $app->registerInitializer(new ClassThatImplementsInitializer); + * + * * @since 1.0.0 * * @uses \FireHub\Kernel\Lifecycle\Engine::registerStartup() To register an initializer. @@ -138,6 +177,22 @@ function () use ($initializer):void { * * Disposers are executed according to their configured execution order. Registration is permitted only before * initialization begins. + * + * + * use FireHub\Application\Application; + * use FireHub\Application\Context; + * use FireHub\Application\Context\Name; + * use FireHub\Application\Context\Environment; + * + * $app = new Application( + * new Context( + * new Name('FireHub'), + * Environment::PRODUCTION + * )); + * + * $app->registerDisposer(new ClassThatImplementsDisposer); + * + * * @since 1.0.0 * * @uses \FireHub\Kernel\Lifecycle\Engine::registerShutdown() To register a disposer. @@ -188,6 +243,22 @@ function () use ($disposer):void { * Initialization stops immediately when an initializer fails. * * Calling this method after successful initialization has no effect. + * + * + * use FireHub\Application\Application; + * use FireHub\Application\Context; + * use FireHub\Application\Context\Name; + * use FireHub\Application\Context\Environment; + * + * $app = new Application( + * new Context( + * new Name('FireHub'), + * Environment::PRODUCTION + * )); + * + * $app->initialize(); + * + * * @since 1.0.0 * * @uses \FireHub\Kernel\Lifecycle\Engine::state() To get the current state. @@ -242,6 +313,22 @@ public function initialize ():void { * All registered disposers are attempted even when an individual disposer fails. * * Calling this method after successful disposal has no effect. + * + * + * use FireHub\Application\Application; + * use FireHub\Application\Context; + * use FireHub\Application\Context\Name; + * use FireHub\Application\Context\Environment; + * + * $app = new Application( + * new Context( + * new Name('FireHub'), + * Environment::PRODUCTION + * )); + * + * $app->dispose(); + * + * * @since 1.0.0 * * @uses \FireHub\Kernel\Lifecycle\Engine::state() To get the current state. @@ -297,6 +384,34 @@ public function dispose ():void { * Returns the application's own lifecycle state directly from its lifecycle engine. * * No conversion from kernel states is performed. + * + * + * use FireHub\Application\Application; + * use FireHub\Application\Context; + * use FireHub\Application\Context\Name; + * use FireHub\Application\Context\Environment; + * + * $app = new Application( + * new Context( + * new Name('FireHub'), + * Environment::PRODUCTION + * )); + * + * $app->state(); + * + * // FireHub\Application\State::CREATED + * + * $app->initialize(); + * $app->state(); + * + * // FireHub\Application\State::INITIALIZED + * + * $app->dispose(); + * $app->state(); + * + * // FireHub\Application\State::DISPOSED + * + * * @since 1.0.0 * * @uses \FireHub\Kernel\Lifecycle\Engine::state() To get the current state. @@ -325,6 +440,28 @@ public function kernel ():Kernel { /** * ### Gets the application execution context + * + * + * use FireHub\Application\Application; + * use FireHub\Application\Context; + * use FireHub\Application\Context\Name; + * use FireHub\Application\Context\Environment; + * + * $app = new Application( + * new Context( + * new Name('FireHub'), + * Environment::PRODUCTION + * )); + * + * $kernel->context()->name->value(); + * + * // FireHub + * + * $kernel->context()->environment; + * + * // FireHub\Application\Context\Environment::PRODUCTION + * + * * @since 1.0.0 * * @return \FireHub\Application\Context Application context. From 794f372e1f9c0726003793f274f082b60d7f3fcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= Date: Mon, 19 Oct 2026 15:07:50 +0200 Subject: [PATCH 08/18] Integrate dependency injection container into `Application`. Enhanced `Application` functionality by introducing a new `Container` for managing dependencies. Updated the constructor to accept a `ContainerBoundary` instance with a default `Container` implementation, simplifying dependency handling. Added a `container()` method to retrieve the application's DI container. Additional changes: - Updated `composer.json` to include the `Container` package as a dependency. - Refactored the `Kernel` property for clarity by directly initializing it in the constructor. - Removed unnecessary inline `Kernel` assignment for improved readability. These changes improve dependency management and prepare the `Application` for more flexible and modular runtime configurations. --- composer.json | 3 ++- src/Application.php | 32 +++++++++++++++++++++----------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/composer.json b/composer.json index ece3d2a..4104601 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,8 @@ "the-firehub-project/core-standard": "dev-develop", "the-firehub-project/runtime": ">=0.1.0 <1.0.0", "the-firehub-project/foundation": "dev-develop", - "the-firehub-project/kernel": "dev-develop" + "the-firehub-project/kernel": "dev-develop", + "the-firehub-project/container": "dev-develop" }, "require-dev": { "phpstan/phpstan": "^2.0", diff --git a/src/Application.php b/src/Application.php index 89b036b..44a483e 100644 --- a/src/Application.php +++ b/src/Application.php @@ -13,8 +13,10 @@ namespace FireHub\Application; +use FireHub\Core\Boundary\Container as ContainerBoundary; use FireHub\Kernel\Kernel; use FireHub\Kernel\Lifecycle\Engine; +use FireHub\Container\Container; use FireHub\Kernel\Lifecycle\Exception\ { InvalidLifecycleStateException, LifecycleExecutionException }; @@ -64,12 +66,6 @@ */ final readonly class Application { - /** - * ### Application kernel - * @since 1.0.0 - */ - private Kernel $kernel; - /** * ### Application lifecycle engine * @since 1.0.0 @@ -87,19 +83,21 @@ * @param \FireHub\Application\Context $context

* Application context. *

- * @param null|\FireHub\Kernel\Kernel $kernel [optional]

- * Kernel instance to share with other runtime components. + * @param \FireHub\Kernel\Kernel $kernel [optional]

+ * Parent kernel. A new kernel is created by default. + *

+ * @param \FireHub\Core\Boundary\Container $container [optional]

+ * Application dependency injection container. A new container is created by default. *

* * @return void */ public function __construct ( private Context $context, - ?Kernel $kernel = null + private Kernel $kernel = new Kernel, + private ContainerBoundary $container = new Container ) { - $this->kernel = $kernel ?? new Kernel; - $this->lifecycle = new Engine( State::CREATED, $this->kernel->dependencyBoundary() @@ -472,4 +470,16 @@ public function context ():Context { } + /** + * ### Gets the application dependency injection container + * @since 1.0.0 + * + * @return \FireHub\Core\Boundary\Container Application container. + */ + public function container ():ContainerBoundary { + + return $this->container; + + } + } \ No newline at end of file From 6514b8aac57a1a0846f13296d5497d22aab3d316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= Date: Mon, 19 Oct 2026 15:40:16 +0200 Subject: [PATCH 09/18] Add dependency boundary and isolated operation execution to Application Introduced two new functionalities in `Application`: 1. `dependencyBoundary()`: Provides lifecycle dependency boundary access, enabling dependent runtimes during the application's initialized state. 2. `execute(Closure $callback)`: Allows executing operations within an isolated dependency injection scope while maintaining lifecycle dependency to prevent disposal during execution. Enhanced lifecycle management and isolation ensures safer runtime operations and promotes better dependency control. Removed internal marker from `Boundary` class to make it accessible. --- src/Application.php | 80 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/src/Application.php b/src/Application.php index 44a483e..efbafeb 100644 --- a/src/Application.php +++ b/src/Application.php @@ -13,9 +13,12 @@ namespace FireHub\Application; -use FireHub\Core\Boundary\Container as ContainerBoundary; +use FireHub\Core\Boundary\ { + Container\Scope as ScopeBoundary, Container as ContainerBoundary +}; use FireHub\Kernel\Kernel; use FireHub\Kernel\Lifecycle\Engine; +use FireHub\Kernel\Lifecycle\Dependency\Boundary; use FireHub\Container\Container; use FireHub\Kernel\Lifecycle\Exception\ { InvalidLifecycleStateException, LifecycleExecutionException @@ -23,6 +26,7 @@ use FireHub\Application\Exception\ { ApplicationDisposalException, ApplicationInitializationException, InvalidApplicationStateException }; +use Closure; /** * ### Manages the application lifecycle @@ -105,6 +109,27 @@ public function __construct ( } + /** + * ### Gets the application lifecycle dependency boundary + * + * Exposes the application as a parent lifecycle for dependent runtime executions. + * + * Dependencies can only be acquired while the application is initialized. + * + * @since 1.0.0 + * + * @uses \FireHub\Kernel\Lifecycle\Engine::dependencyBoundary() To create the dependency boundary. + * + * @return \FireHub\Kernel\Lifecycle\Dependency\Boundary Application lifecycle dependency boundary. + */ + public function dependencyBoundary ():Boundary { + + return $this->lifecycle->dependencyBoundary( + fn (State $state):bool => $state === State::INITIALIZED + ); + + } + /** * ### Registers an application initializer * @@ -376,6 +401,59 @@ public function dispose ():void { } + /** + * ### Executes an application operation + * + * Executes a callback within an isolated dependency injection scope. + * + * The application must be initialized before execution begins. + * + * Acquires an application lifecycle dependency to prevent disposal while the operation is active. + * + * The dependency injection scope is closed, and the lifecycle dependency is released after execution, including + * when the callback fails. + * + * + * $result = $app->execute( + * function () use (Container $app):void { + * + * $test = $app->container()->get(Test::class); + * + * return ''; + * + * } + * ); + * + * + * @since 1.0.0 + * + * @uses \FireHub\Application\Application::dependencyBoundary() To acquire a lifecycle dependency. + * @uses \FireHub\Kernel\Lifecycle\Dependency\Lease::release() To release the lifecycle dependency. + * + * @param Closure(\FireHub\Core\Boundary\Container):mixed $callback

+ * Operation to execute. + *

+ * + * @throws \FireHub\Kernel\Lifecycle\Exception\InvalidLifecycleStateException If the application is not available. + * + * @return mixed Operation result. + */ + public function execute (Closure $callback):mixed { + + $lease = $this->dependencyBoundary()->acquire(); + + try { + + return $callback($this->container); + + } finally { + + $lease->release(); + + } + + } + /** * ### Gets the current application lifecycle state * From 28fb5e7d19abba55f347f1e1072a697d541d6892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= Date: Tue, 20 Oct 2026 09:13:37 +0200 Subject: [PATCH 10/18] Introduce `Builder` and `Manager` for modular application lifecycle management. Added `Builder` and `Manager` components to enhance modularity and lifecycle orchestration for the `Application` class: 1. **Builder**: - Provides a fluent API for creating and configuring `Application` instances. - Introduced support for custom kernels, containers, initializers, and disposers during application construction. 2. **Manager**: - Manages multiple `Application` instances linked to a shared `Kernel`. - Includes functionality for registering, initializing, disposing, and retrieving applications by identifier. - Ensures lifecycle ordering and consistency across registered applications. 3. **Exceptions**: - Added `ApplicationAlreadyRegisteredException`, `ApplicationKernelMismatchException`, `ApplicationNotFoundException`, and `InvalidApplicationIdentifierException` for robust error handling in application registration and management. Other updates: - Refactored `Application` to align with the `Builder` and `Manager` architecture. - Updated inline documentation with detailed usage examples and improved annotations. - Simplified lambda execution in `Application::execute()` by providing direct `Application` context. These changes establish a modular and scalable foundation for managing the creation and lifecycle of FireHub applications. --- src/Application.php | 10 +- src/Builder.php | 292 ++++++++++++++ .../ApplicationAlreadyRegisteredException.php | 29 ++ .../ApplicationKernelMismatchException.php | 29 ++ .../ApplicationNotFoundException.php | 28 ++ .../InvalidApplicationIdentifierException.php | 28 ++ src/Manager.php | 373 ++++++++++++++++++ 7 files changed, 783 insertions(+), 6 deletions(-) create mode 100644 src/Builder.php create mode 100644 src/Exception/ApplicationAlreadyRegisteredException.php create mode 100644 src/Exception/ApplicationKernelMismatchException.php create mode 100644 src/Exception/ApplicationNotFoundException.php create mode 100644 src/Exception/InvalidApplicationIdentifierException.php create mode 100644 src/Manager.php diff --git a/src/Application.php b/src/Application.php index efbafeb..d349ed0 100644 --- a/src/Application.php +++ b/src/Application.php @@ -13,9 +13,7 @@ namespace FireHub\Application; -use FireHub\Core\Boundary\ { - Container\Scope as ScopeBoundary, Container as ContainerBoundary -}; +use FireHub\Core\Boundary\Container as ContainerBoundary; use FireHub\Kernel\Kernel; use FireHub\Kernel\Lifecycle\Engine; use FireHub\Kernel\Lifecycle\Dependency\Boundary; @@ -415,7 +413,7 @@ public function dispose ():void { * * * $result = $app->execute( - * function () use (Container $app):void { + * function () use (Application $app):void { * * $test = $app->container()->get(Test::class); * @@ -430,7 +428,7 @@ public function dispose ():void { * @uses \FireHub\Application\Application::dependencyBoundary() To acquire a lifecycle dependency. * @uses \FireHub\Kernel\Lifecycle\Dependency\Lease::release() To release the lifecycle dependency. * - * @param Closure(\FireHub\Core\Boundary\Container):mixed $callback

+ * @param Closure(\FireHub\Application\Application):mixed $callback

* Operation to execute. *

* @@ -444,7 +442,7 @@ public function execute (Closure $callback):mixed { try { - return $callback($this->container); + return $callback($this); } finally { diff --git a/src/Builder.php b/src/Builder.php new file mode 100644 index 0000000..d841171 --- /dev/null +++ b/src/Builder.php @@ -0,0 +1,292 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.5 + * @package Application + */ + +namespace FireHub\Application; + +use FireHub\Core\Boundary\Container as ContainerBoundary; +use FireHub\Container\Container; +use FireHub\Kernel\Kernel; + +/** + * ### Builds and configures FireHub applications + * + * Provides a fluent API for assembling an application from its context, kernel, dependency injection container, and + * lifecycle components. + * + * The builder separates application construction from runtime lifecycle management. Building an application does not + * boot its kernel, initialize the application, or execute any operations. + * + * A kernel and container are created automatically when no explicit instances are provided. + * + * Each build operation creates a new application instance. Explicitly configured kernel and container instances are + * shared between builds, while default instances are created independently for each application. + * + * + * use FireHub\Application\Builder; + * use FireHub\Application\Context; + * use FireHub\Application\Context\Name; + * use FireHub\Application\Context\Environment; + * use FireHub\Container\Container; + * use FireHub\Kernel\Kernel; + * + * $kernel = new Kernel; + * $container = new Container; + * + * $container->singleton(MyService::class); + * $container->freeze(); + * + * $app = Builder::create( + * new Context( + * new Name('FireHub'), + * Environment::PRODUCTION + * ))->withKernel($kernel) + * ->withContainer($container) + * ->withInitializer(new ClassThatImplementsInitializer) + * ->withDisposer(new ClassThatImplementsDisposer) + * ->build(); + * + * $kernel->boot(); + * $app->initialize(); + * + * try { + * + * $app->execute(function (Application $app):void { + * + * $service = $app->container()->get(MyService::class); + * + * $service->run(); + * + * }); + * + * } finally { + * + * try { + * + * $app->dispose(); + * + * } finally { + * + * $kernel->shutdown(); + * + * } + * + * } + * + * + * @since 1.0.0 + */ +final class Builder { + + /** + * ### Application kernel + * @since 1.0.0 + */ + private ?Kernel $kernel = null; + + /** + * ### Application dependency injection container + * @since 1.0.0 + */ + private ?ContainerBoundary $container = null; + + /** + * ### Application initializer + * @since 1.0.0 + * + * @var list + */ + private array $initializers = []; + + /** + * ### Application disposers + * @since 1.0.0 + * + * @var list + */ + private array $disposers = []; + + /** + * ### Constructor + * @since 1.0.0 + * + * @param \FireHub\Application\Context $context

+ * Application context. + *

+ * + * @return void + */ + private function __construct ( + private readonly Context $context + ) {} + + /** + * ### Creates an application builder + * + * Creates a new builder configured with the specified application context. + * @since 1.0.0 + * + * @param \FireHub\Application\Context $context

+ * Application context. + *

+ * + * @return self New application builder. + */ + public static function create (Context $context):self { + + return new self($context); + + } + + /** + * ### Configures the application kernel + * + * Sets the kernel associated with applications created by this builder. + * + * The specified kernel may be shared between multiple applications. + * @since 1.0.0 + * + * @param \FireHub\Kernel\Kernel $kernel

+ * Application kernel. + *

+ * + * @return $this Current builder. + */ + public function withKernel (Kernel $kernel):self { + + $this->kernel = $kernel; + + return $this; + + } + + /** + * ### Configures the dependency injection container + * + * Sets the container associated with applications created by this builder. + * + * The container is expected to be configured before application initialization. The builder does not register + * services, freeze registrations, or assume ownership of the container lifecycle. + * @since 1.0.0 + * + * @param \FireHub\Core\Boundary\Container $container

+ * Application dependency injection container. + *

+ * + * @return $this Current builder. + */ + public function withContainer (ContainerBoundary $container):self { + + $this->container = $container; + + return $this; + + } + + /** + * ### Registers an application initializer + * + * Adds an initializer to the application startup lifecycle. + * + * Initializers are registered during application construction and executed when the application is initialized. + * @since 1.0.0 + * + * @param \FireHub\Application\Initializer $initializer

+ * Initializer to register. + *

+ * @param int $order [optional]

+ * Initializer execution order. + *

+ * + * @return $this Current builder. + */ + public function withInitializer (Initializer $initializer, int $order = 0):self { + + $this->initializers[] = [ + 'initializer' => $initializer, + 'order' => $order + ]; + + return $this; + + } + + /** + * ### Registers an application disposer + * + * Adds a disposer to the application shutdown lifecycle. + * + * Disposers are registered during application construction and executed when the application has been disposed. + * @since 1.0.0 + * + * @param Disposer $disposer

+ * Disposer to register. + *

+ * @param int $order [optional]

+ * Disposer execution order. + *

+ * + * @return $this Current builder. + */ + public function withDisposer (Disposer $disposer, int $order = 0):self { + + $this->disposers[] = [ + 'disposer' => $disposer, + 'order' => $order + ]; + + return $this; + + } + + /** + * ### Builds an application + * + * Creates a new application using the configured context, kernel, container, initializers, and disposers. + * + * When no kernel or container has been explicitly configured, a new instance is created for the application. + * + * The resulting application remains in its created state. Kernel bootstrapping and application initialization + * must be performed separately. + * @since 1.0.0 + * + * @uses + * + * @throws \FireHub\Application\Exception\InvalidApplicationStateException If registration is not allowed. + * + * @return \FireHub\Application\Application Configured application. + */ + public function build ():Application { + + $application = new Application( + $this->context, + $this->kernel ?? new Kernel, + $this->container ?? new Container + ); + + foreach ($this->initializers as $entry) + $application->registerInitializer( + $entry['initializer'], + $entry['order'] + ); + + foreach ($this->disposers as $entry) + $application->registerDisposer( + $entry['disposer'], + $entry['order'] + ); + + return $application; + + } + +} \ No newline at end of file diff --git a/src/Exception/ApplicationAlreadyRegisteredException.php b/src/Exception/ApplicationAlreadyRegisteredException.php new file mode 100644 index 0000000..f9d9a4e --- /dev/null +++ b/src/Exception/ApplicationAlreadyRegisteredException.php @@ -0,0 +1,29 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.1 + * @package Application + */ + +namespace FireHub\Application\Exception; + +use FireHub\Core\Exception\RuntimeException; + +/** + * ### Application already registered exception + * + * Thrown when attempting to register an application using an identifier that is already associated with another + * application. + * @since 1.0.0 + */ +final class ApplicationAlreadyRegisteredException extends RuntimeException { + + protected const string DEFAULT_MESSAGE = 'The application identifier is already registered'; + +} \ No newline at end of file diff --git a/src/Exception/ApplicationKernelMismatchException.php b/src/Exception/ApplicationKernelMismatchException.php new file mode 100644 index 0000000..b92c6e8 --- /dev/null +++ b/src/Exception/ApplicationKernelMismatchException.php @@ -0,0 +1,29 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.1 + * @package Application + */ + +namespace FireHub\Application\Exception; + +use FireHub\Core\Exception\Runtime\InvalidArgumentException; + +/** + * ### Application kernel mismatch exception + * + * Thrown when attempting to register an application whose kernel differs from the kernel managed by the application + * manager. + * @since 1.0.0 + */ +final class ApplicationKernelMismatchException extends InvalidArgumentException { + + protected const string DEFAULT_MESSAGE = 'The application kernel does not match the application manager kernel'; + +} \ No newline at end of file diff --git a/src/Exception/ApplicationNotFoundException.php b/src/Exception/ApplicationNotFoundException.php new file mode 100644 index 0000000..1a630bf --- /dev/null +++ b/src/Exception/ApplicationNotFoundException.php @@ -0,0 +1,28 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.1 + * @package Application + */ + +namespace FireHub\Application\Exception; + +use FireHub\Core\Exception\Runtime\OutOfBoundsException; + +/** + * ### Application was not found exception + * + * Thrown when attempting to retrieve an application not registered with the application manager. + * @since 1.0.0 + */ +final class ApplicationNotFoundException extends OutOfBoundsException { + + protected const string DEFAULT_MESSAGE = 'The application was not found'; + +} \ No newline at end of file diff --git a/src/Exception/InvalidApplicationIdentifierException.php b/src/Exception/InvalidApplicationIdentifierException.php new file mode 100644 index 0000000..b15af2f --- /dev/null +++ b/src/Exception/InvalidApplicationIdentifierException.php @@ -0,0 +1,28 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.1 + * @package Application + */ + +namespace FireHub\Application\Exception; + +use FireHub\Core\Exception\Runtime\InvalidArgumentException; + +/** + * ### Invalid application identifier exception + * + * Thrown when an application identifier does not satisfy the manager's registration requirements. + * @since 1.0.0 + */ +final class InvalidApplicationIdentifierException extends InvalidArgumentException { + + protected const string DEFAULT_MESSAGE = 'The application identifier is invalid'; + +} \ No newline at end of file diff --git a/src/Manager.php b/src/Manager.php new file mode 100644 index 0000000..42ef3fc --- /dev/null +++ b/src/Manager.php @@ -0,0 +1,373 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.5 + * @package Application + */ + +namespace FireHub\Application; + +use FireHub\Kernel\Kernel; +use FireHub\Application\Exception\ { + ApplicationAlreadyRegisteredException, ApplicationKernelMismatchException, ApplicationNotFoundException, + InvalidApplicationIdentifierException +}; +use FireHub\Runtime; + +/** + * ### Manages multiple FireHub applications + * + * Coordinates the registration, initialization, and disposal of multiple applications associated with a shared kernel. + * + * Applications retain independent lifecycle states and dependency injection containers. The manager controls + * application lifecycle ordering without managing individual application executions or dependency injection scopes. + * + * The manager owns the kernel lifecycle only when explicitly instructed to start or stop it. + * + * + * use FireHub\Application\Manager; + * use FireHub\Application\Context; + * use FireHub\Application\Context\Name; + * use FireHub\Application\Context\Environment; + * use FireHub\Kernel\Kernel; + * + * $manager = new Manager; + * + * $frontend = $manager->create( + * new Context( + * new Name('Frontend'), + * Environment::PRODUCTION + * ))->withContainer($container) + * ->build(); + * + * $backend = $manager->create( + * new Context( + * new Name('Backend'), + * Environment::PRODUCTION + * ))->withContainer($container) + * ->build(); + * + * $manager + * ->register('frontend', $frontend) + * ->register('backend', $backend); + * + * $manager->initializeAll(); + * + * try { + * + * $result = $manager->get('frontend')->execute( + * function (Application $app):string { + * + * return 'Welcome to The FireHub Project!'; + * + * } + * ); + * + * } finally { + * + * try { + * + * $manager->disposeAll(); + * + * } finally { + * + * $manager->shutdown(); + * + * } + * + * } + * + * + * @since 1.0.0 + */ +final class Manager { + + /** + * ### Registered applications + * @since 1.0.0 + * + * @var array + */ + private array $applications = []; + + /** + * ### Constructor + * @since 1.0.0 + * + * @param \FireHub\Kernel\Kernel $kernel [optional]

+ * Shared application kernel. + *

+ * + * @return void + */ + public function __construct ( + private readonly Kernel $kernel = new Kernel + ) {} + + /** + * ### Creates an application builder + * + * Creates a new application builder configured with the manager's shared kernel. + * + * The builder allows additional application configuration before construction. + * + * Building the application does not automatically register it with the manager. + * @since 1.0.0 + * + * @uses \FireHub\Application\Builder::create() To create the application builder. + * @uses \FireHub\Application\Builder::withKernel() To configure the shared kernel. + * + * @param \FireHub\Application\Context $context

+ * Application context. + *

+ * + * @return \FireHub\Application\Builder Configured application builder. + */ + public function create (Context $context):Builder { + + return Builder::create($context) + ->withKernel($this->kernel); + + } + + /** + * ### Registers an application + * + * Registers an application under the specified identifier. + * + * The application must use the same kernel as this manager. + * @since 1.0.0 + * + * @uses \FireHub\Application\Application::kernel() To get the application kernel. + * + * @param non-empty-string $id

+ * Unique application identifier. + *

+ * @param \FireHub\Application\Application $application

+ * Application to register. + *

+ * + * @throws \FireHub\Application\Exception\InvalidApplicationIdentifierException If the identifier is empty. + * @throws \FireHub\Application\Exception\ApplicationAlreadyRegisteredException If the application is already + * registered. + * @throws \FireHub\Application\Exception\ApplicationKernelMismatchException If the application kernel does not + * match the manager kernel. + * + * @return $this Current manager. + */ + public function register (string $id, Application $application):self { + + if ($id === '') + throw new InvalidApplicationIdentifierException( + 'Application identifier cannot be empty.' + ); + + if (isset($this->applications[$id])) + throw new ApplicationAlreadyRegisteredException; + + if ($application->kernel() !== $this->kernel) + throw new ApplicationKernelMismatchException( + context: [ + 'application_kernel' => $application->kernel(), + 'kernel' => $this->kernel + ] + ); + + $this->applications[$id] = $application; + + return $this; + + } + + /** + * ### Gets a registered application + * @since 1.0.0 + * + * @param non-empty-string $id

+ * Application identifier. + *

+ * + * @throws \FireHub\Application\Exception\ApplicationNotFoundException If the application is not registered. + * + * @return \FireHub\Application\Application Registered application. + */ + public function get (string $id):Application { + + return $this->applications[$id] + ?? throw new ApplicationNotFoundException; + + } + + /** + * ### Checks whether an application is registered + * @since 1.0.0 + * + * @param non-empty-string $id

+ * Application identifier. + *

+ * + * @return bool True if the application is registered. + */ + public function has (string $id):bool { + + return isset($this->applications[$id]); + + } + + /** + * ### Gets all registered applications + * @since 1.0.0 + * + * @return array Registered applications. + */ + public function all ():array { + + return $this->applications; + + } + + /** + * ### Gets the shared kernel + * @since 1.0.0 + * + * @return \FireHub\Kernel\Kernel Shared application kernel. + */ + public function kernel ():Kernel { + + return $this->kernel; + + } + + /** + * ### Initializes a registered application + * + * Boots the shared kernel if necessary and initializes the specified application. + * @since 1.0.0 + * + * @uses \FireHub\Application\Manager::get() To get the application. + * @uses \FireHub\Kernel\Kernel::boot() To boot the kernel. + * @uses \FireHub\Application\Application::initialize() To initialize the application. + * + * @param non-empty-string $id

+ * Application identifier. + *

+ * + * @throws \FireHub\Application\Exception\ApplicationNotFoundException If the application is not registered. + * @throws \FireHub\Kernel\Exception\InvalidKernelStateException If booting is not allowed. + * @throws \FireHub\Kernel\Exception\KernelBootException If initialization fails. + * @throws \FireHub\Application\Exception\InvalidApplicationStateException If initialization is not allowed. + * @throws \FireHub\Application\Exception\ApplicationInitializationException If an initializer fails. + * + * @return void + */ + public function initialize (string $id):void { + + $application = $this->get($id); + + $this->kernel->boot(); + + $application->initialize(); + + } + + /** + * ### Disposes a registered application + * + * Disposes the specified application without shutting down the shared kernel. + * @since 1.0.0 + * + * @uses \FireHub\Application\Manager::get() To get the application. + * @uses \FireHub\Application\Application::dispose() To dispose the application. + * + * @param non-empty-string $id

+ * Application identifier. + *

+ * + * @throws \FireHub\Application\Exception\ApplicationNotFoundException If the application is not registered. + * @throws \FireHub\Application\Exception\InvalidApplicationStateException If disposal is not allowed. + * @throws \FireHub\Application\Exception\ApplicationDisposalException If a disposer fails. + * + * @return void + */ + public function dispose (string $id):void { + + $this->get($id)->dispose(); + + } + + /** + * ### Initializes all registered applications + * + * Boots the shared kernel and initializes applications in registration order. + * @since 1.0.0 + * + * @uses \FireHub\Kernel\Kernel::boot() To boot the kernel. + * @uses \FireHub\Application\Application::initialize() To initialize the application. + * + * @throws \FireHub\Kernel\Exception\InvalidKernelStateException If booting is not allowed. + * @throws \FireHub\Kernel\Exception\KernelBootException If initialization fails. + * @throws \FireHub\Application\Exception\InvalidApplicationStateException If initialization is not allowed. + * @throws \FireHub\Application\Exception\ApplicationInitializationException If an initializer fails. + * + * @return void + */ + public function initializeAll ():void { + + $this->kernel->boot(); + + foreach ($this->applications as $application) + $application->initialize(); + + } + + /** + * ### Disposes all registered applications + * + * Disposes applications in reverse registration order. + * + * The shared kernel remains active until explicitly shut down. + * @since 1.0.0 + * + * @uses \FireHub\Application\Application::dispose() To dispose the application. + * @uses \FireHub\Runtime\Arr\Transform::reverse() To reverse the application registration order. + * + * @throws \FireHub\Application\Exception\InvalidApplicationStateException If disposal is not allowed. + * @throws \FireHub\Application\Exception\ApplicationDisposalException If a disposer fails. + * + * @return void + */ + public function disposeAll ():void { + + foreach (Runtime\Arr\Transform::reverse($this->applications) as $application) + $application->dispose(); + + } + + /** + * ### Shuts down the shared kernel + * + * Shuts down the kernel after all applications have been disposed. + * + * The kernel lifecycle prevents shutdown while active application dependencies remain. + * @since 1.0.0 + * + * @uses \FireHub\Kernel\Kernel::shutdown() To shutdown the kernel. + * + * @throws \FireHub\Kernel\Exception\InvalidKernelStateException If shutdown is not allowed. + * @throws \FireHub\Kernel\Exception\KernelShutdownException If termination fails. + * + * @return void + */ + public function shutdown ():void { + + $this->kernel->shutdown(); + + } + +} \ No newline at end of file From 48913e9be4cd3901a0464a2cae8be8b6dc5fbe5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= Date: Tue, 20 Oct 2026 14:55:16 +0200 Subject: [PATCH 11/18] Add module management to `Builder` and enhance dependency injection support. Enhanced `Builder` functionality with support for application modules and dependency order resolution: - **Module registration**: Added `withModule()` method to register application modules with dependency resolution through `ModuleManager`. - **Service registration**: Updated `build()` to register services via `RegistersServices` and enforce compatibility with `Registrar` containers. - Introduced `ContainerRegistrationNotSupportedException` to handle unsupported containers gracefully. Additional updates: - Added `Module` as a composer dependency in `composer.json`. - Improved inline documentation to reflect module-related functionality. These changes extend modularity support, improve service registration handling, and enhance application configurability. --- composer.json | 3 +- src/Builder.php | 92 +++++++++++++++++-- ...ainerRegistrationNotSupportedException.php | 29 ++++++ 3 files changed, 116 insertions(+), 8 deletions(-) create mode 100644 src/Exception/ContainerRegistrationNotSupportedException.php diff --git a/composer.json b/composer.json index 4104601..f870d47 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,8 @@ "the-firehub-project/runtime": ">=0.1.0 <1.0.0", "the-firehub-project/foundation": "dev-develop", "the-firehub-project/kernel": "dev-develop", - "the-firehub-project/container": "dev-develop" + "the-firehub-project/container": "dev-develop", + "the-firehub-project/module": "dev-develop" }, "require-dev": { "phpstan/phpstan": "^2.0", diff --git a/src/Builder.php b/src/Builder.php index d841171..3b28ff4 100644 --- a/src/Builder.php +++ b/src/Builder.php @@ -13,9 +13,16 @@ namespace FireHub\Application; -use FireHub\Core\Boundary\Container as ContainerBoundary; +use FireHub\Core\Boundary\ { + Container\Registrar, Container as ContainerBoundary +}; +use FireHub\Core\Boundary\ { + Module\RegistersServices, Module +}; use FireHub\Container\Container; use FireHub\Kernel\Kernel; +use FireHub\Module\Manager as ModuleManager; +use FireHub\Application\Exception\ContainerRegistrationNotSupportedException; /** * ### Builds and configures FireHub applications @@ -99,6 +106,12 @@ final class Builder { */ private ?ContainerBoundary $container = null; + /** + * ### Application module manager + * @since 1.0.0 + */ + private ?ModuleManager $modules = null; + /** * ### Application initializer * @since 1.0.0 @@ -192,6 +205,38 @@ public function withContainer (ContainerBoundary $container):self { } + /** + * ### Registers an application module + * + * Adds a module to the application composition. + * + * Modules are identified by their fully qualified class names. Module dependencies are resolved during + * application construction. + * + * Service registrations contributed by modules are applied during the build process, before application + * initialization. + * @since 1.0.0 + * + * @uses \FireHub\Module\Manager::register() To register the module. + * + * @param \FireHub\Core\Boundary\Module $module

+ * Application module. + *

+ * + * @throws \FireHub\Module\Exception\ModuleAlreadyRegisteredException If the module is already registered. + * + * @return $this Current builder. + */ + public function withModule (Module $module):self { + + $this->modules ??= new ModuleManager; + + $this->modules->register($module); + + return $this; + + } + /** * ### Registers an application initializer * @@ -251,26 +296,59 @@ public function withDisposer (Disposer $disposer, int $order = 0):self { /** * ### Builds an application * - * Creates a new application using the configured context, kernel, container, initializers, and disposers. + * Creates an application using the configured context, kernel, dependency injection container, modules, + * initializers, and disposers. + * + * Module services are registered in dependency order before the application is constructed. * - * When no kernel or container has been explicitly configured, a new instance is created for the application. + * When the builder creates its own container, the container is automatically frozen after service registration. * - * The resulting application remains in its created state. Kernel bootstrapping and application initialization - * must be performed separately. + * Externally supplied containers remain under the caller's control and are never frozen by the builder. + * + * Building an application does not initialize its lifecycle. * @since 1.0.0 * - * @uses + * @uses \FireHub\Module\Manager::ordered() To get the ordered module list. + * @uses \FireHub\Core\Boundary\Module\RegistersServices::register() To register services. + * @uses \FireHub\Container\Container::freeze() To freeze the container. * * @throws \FireHub\Application\Exception\InvalidApplicationStateException If registration is not allowed. + * @throws \FireHub\Module\Exception\ModuleDependencyNotFoundException If a required module is not registered. + * @throws \FireHub\Module\Exception\CircularModuleDependencyException If circular dependencies are detected. + * @throws \FireHub\Application\Exception\ContainerRegistrationNotSupportedException If the container does not + * support service registration. * * @return \FireHub\Application\Application Configured application. */ public function build ():Application { + $container = $this->container ?? new Container; + + if ($this->modules !== null) { + + foreach ($this->modules->ordered() as $module) { + + if (!$module instanceof RegistersServices) + continue; + + if (!$container instanceof Registrar) + throw new ContainerRegistrationNotSupportedException( + 'The configured container does not support service registration.' + ); + + $module->register($container); + + } + + } + + if ($this->container === null && $container instanceof Container) + $container->freeze(); + $application = new Application( $this->context, $this->kernel ?? new Kernel, - $this->container ?? new Container + $container ); foreach ($this->initializers as $entry) diff --git a/src/Exception/ContainerRegistrationNotSupportedException.php b/src/Exception/ContainerRegistrationNotSupportedException.php new file mode 100644 index 0000000..38339b4 --- /dev/null +++ b/src/Exception/ContainerRegistrationNotSupportedException.php @@ -0,0 +1,29 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.1 + * @package Application + */ + +namespace FireHub\Application\Exception; + +use FireHub\Core\Exception\RuntimeException; + +/** + * ### Container registration not a supported exception + * + * Thrown when application composition requires service registration, but the configured dependency injection + * container does not implement the required registration boundary. + * @since 1.0.0 + */ +final class ContainerRegistrationNotSupportedException extends RuntimeException { + + protected const string DEFAULT_MESSAGE = 'The configured dependency injection container does not support service registration'; + +} \ No newline at end of file From 73f406a4f37df26169fca6c422499b48ec07fdce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= Date: Tue, 20 Oct 2026 15:12:44 +0200 Subject: [PATCH 12/18] Enhance `Builder` module management and freeze support. Updated `Builder` to improve module management and enforce immutability: - Cloned `ModuleManager` during application build to isolate changes and ensure thread safety. - Added freezing of `ModuleManager` after successful service registration to prevent further modifications. - Updated inline documentation to reflect the module freezing process and application lifecycle integration. Additional improvements: - Enhanced `Application` constructor to support storing and accessing the module manager. - Added `modules()` method to retrieve the `ModuleManager` instance from the `Application`. These changes reinforce modularity and immutability while streamlining module access in the application lifecycle. --- src/Application.php | 23 ++++++++++++++++++++++- src/Builder.php | 37 ++++++++++++++++++++++--------------- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/Application.php b/src/Application.php index d349ed0..a76c6f0 100644 --- a/src/Application.php +++ b/src/Application.php @@ -18,6 +18,7 @@ use FireHub\Kernel\Lifecycle\Engine; use FireHub\Kernel\Lifecycle\Dependency\Boundary; use FireHub\Container\Container; +use FireHub\Module\Manager as ModuleManager; use FireHub\Kernel\Lifecycle\Exception\ { InvalidLifecycleStateException, LifecycleExecutionException }; @@ -91,13 +92,17 @@ * @param \FireHub\Core\Boundary\Container $container [optional]

* Application dependency injection container. A new container is created by default. *

+ * @param \FireHub\Module\Manager $modules [optional]

+ * Application module manager. + *

* * @return void */ public function __construct ( private Context $context, private Kernel $kernel = new Kernel, - private ContainerBoundary $container = new Container + private ContainerBoundary $container = new Container, + private ModuleManager $modules = new ModuleManager ) { $this->lifecycle = new Engine( @@ -558,4 +563,20 @@ public function container ():ContainerBoundary { } + /** + * ### Gets the application module manager + * + * Returns the module manager associated with this application. + * + * The manager provides access to registered modules and their dependency ordering. + * @since 1.0.0 + * + * @return \FireHub\Module\Manager Application module manager. + */ + public function modules ():ModuleManager { + + return $this->modules; + + } + } \ No newline at end of file diff --git a/src/Builder.php b/src/Builder.php index 3b28ff4..e0aeb75 100644 --- a/src/Builder.php +++ b/src/Builder.php @@ -299,18 +299,24 @@ public function withDisposer (Disposer $disposer, int $order = 0):self { * Creates an application using the configured context, kernel, dependency injection container, modules, * initializers, and disposers. * - * Module services are registered in dependency order before the application is constructed. + * Modules are resolved in dependency order, and their service registrations are applied before the application + * is constructed. * - * When the builder creates its own container, the container is automatically frozen after service registration. + * The module manager is cloned and frozen after successful service registration, preventing further changes + * to the application's module composition. * - * Externally supplied containers remain under the caller's control and are never frozen by the builder. + * Containers created by the builder are automatically frozen after service registration. Externally supplied + * containers remain under the caller's control and are never frozen by the builder. * * Building an application does not initialize its lifecycle. * @since 1.0.0 * * @uses \FireHub\Module\Manager::ordered() To get the ordered module list. + * @uses \FireHub\Module\Manager::freeze() To freeze the module registry. * @uses \FireHub\Core\Boundary\Module\RegistersServices::register() To register services. * @uses \FireHub\Container\Container::freeze() To freeze the container. + * @uses \FireHub\Application\Application::registerInitializer() To register application initializers. + * @uses \FireHub\Application\Application::registerDisposer() To register application disposers. * * @throws \FireHub\Application\Exception\InvalidApplicationStateException If registration is not allowed. * @throws \FireHub\Module\Exception\ModuleDependencyNotFoundException If a required module is not registered. @@ -324,31 +330,32 @@ public function build ():Application { $container = $this->container ?? new Container; - if ($this->modules !== null) { + $modules = clone ($this->modules ?? new ModuleManager); - foreach ($this->modules->ordered() as $module) { + foreach ($modules->ordered() as $module) { - if (!$module instanceof RegistersServices) - continue; + if (!$module instanceof RegistersServices) + continue; - if (!$container instanceof Registrar) - throw new ContainerRegistrationNotSupportedException( - 'The configured container does not support service registration.' - ); + if (!$container instanceof Registrar) + throw new ContainerRegistrationNotSupportedException( + 'The configured container does not support service registration.' + ); - $module->register($container); - - } + $module->register($container); } + $modules->freeze(); + if ($this->container === null && $container instanceof Container) $container->freeze(); $application = new Application( $this->context, $this->kernel ?? new Kernel, - $container + $container, + $modules ); foreach ($this->initializers as $entry) From c5d08ced558f818646ccde6c13a9377d842fde17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= Date: Tue, 20 Oct 2026 15:32:21 +0200 Subject: [PATCH 13/18] Introduce `ExecutionContext` for isolated application execution and scope management. Added the `ExecutionContext` class to encapsulate execution-specific state and provide context for application operations. Key features include: - **Scoped Execution**: Introduced `ExecutionContext` with an isolated dependency injection scope, ensuring scoped services are shared within executions and isolated across executions. - **Integration with `Application::execute()`**: Updated `Application::execute()` to create a new `ExecutionContext` per operation, maintaining lifecycle dependency and automatic scope cleanup. - **API Enhancements**: Define accessor methods (`application()` and `scope()`) to retrieve the associated application and execution scope. These changes improve execution isolation, dependency management, and application safety during runtime operations. --- src/Application.php | 29 ++++++++++++--- src/ExecutionContext.php | 80 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 src/ExecutionContext.php diff --git a/src/Application.php b/src/Application.php index a76c6f0..5634773 100644 --- a/src/Application.php +++ b/src/Application.php @@ -411,10 +411,16 @@ public function dispose ():void { * * The application must be initialized before execution begins. * - * Acquires an application lifecycle dependency to prevent disposal while the operation is active. + * Each execution creates a new dependency injection scope and execution context. * - * The dependency injection scope is closed, and the lifecycle dependency is released after execution, including - * when the callback fails. + * Scoped services are reused within the execution but remain isolated from other executions. Singleton services + * remain shared through the originating application container. + * + * Acquires an application lifecycle dependency to prevent disposal while execution is active. + * + * The execution scope is automatically closed after the callback completes, including when an exception is thrown. + * + * The lifecycle dependency is released after scope cleanup. * * * $result = $app->execute( @@ -433,7 +439,7 @@ public function dispose ():void { * @uses \FireHub\Application\Application::dependencyBoundary() To acquire a lifecycle dependency. * @uses \FireHub\Kernel\Lifecycle\Dependency\Lease::release() To release the lifecycle dependency. * - * @param Closure(\FireHub\Application\Application):mixed $callback

+ * @param Closure(\FireHub\Application\ExecutionContext):mixed $callback

* Operation to execute. *

* @@ -447,7 +453,20 @@ public function execute (Closure $callback):mixed { try { - return $callback($this); + $scope = $this->container->createScope(); + + try { + + $context = new ExecutionContext( + $this, $scope + ); + + return $callback($context); + + } finally { + $scope->close(); + + } } finally { diff --git a/src/ExecutionContext.php b/src/ExecutionContext.php new file mode 100644 index 0000000..4d189a9 --- /dev/null +++ b/src/ExecutionContext.php @@ -0,0 +1,80 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.5 + * @package Application + */ + +namespace FireHub\Application; + +use FireHub\Core\Boundary\Container\Scope; + +/** + * ### Represents an application execution context + * + * Provides access to the application and its isolated dependency injection scope during a single execution. + * + * Each execution context is associated with exactly one scope. + * + * Scoped services are shared within the same execution context but remain isolated from other execution contexts. + * + * The application owns the scope lifecycle. The scope is automatically closed when execution completes. + * @since 1.0.0 + */ +final readonly class ExecutionContext { + + /** + * ### Constructor + * @since 1.0.0 + * + * @param \FireHub\Application\Application $application

+ * Application associated with this execution. + *

+ * @param \FireHub\Core\Boundary\Container\Scope $scope

+ * Execution dependency injection scope. + *

+ * + * @return void + */ + public function __construct ( + private Application $application, + private Scope $scope + ) {} + + /** + * ### Gets the application + * + * Returns the application associated with this execution context. + * @since 1.0.0 + * + * @return \FireHub\Application\Application Application instance. + */ + public function application ():Application { + + return $this->application; + + } + + /** + * ### Gets the execution dependency injection scope + * + * Returns the isolated scope associated with this execution. + * + * The scope is managed by the application and must not be manually closed by consumers. + * @since 1.0.0 + * + * @return \FireHub\Core\Boundary\Container\Scope Execution scope. + */ + public function scope ():Scope { + + return $this->scope; + + } + +} \ No newline at end of file From e488ba3ff7d642d2412c7e92ac89c7bbd7173f6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= Date: Tue, 20 Oct 2026 15:43:51 +0200 Subject: [PATCH 14/18] Refactor execute method to improve error handling and update context usage --- src/Application.php | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Application.php b/src/Application.php index 5634773..e226aa3 100644 --- a/src/Application.php +++ b/src/Application.php @@ -424,9 +424,11 @@ public function dispose ():void { * * * $result = $app->execute( - * function () use (Application $app):void { + * function () use (ExecutionContext $execution):void { * - * $test = $app->container()->get(Test::class); + * $scope = $execution->scope(); + * + * $service = $scope->get(Test::class); * * return ''; * @@ -444,12 +446,24 @@ public function dispose ():void { *

* * @throws \FireHub\Kernel\Lifecycle\Exception\InvalidLifecycleStateException If the application is not available. + * @throws \FireHub\Application\Exception\InvalidApplicationStateException If the application is not initialized. * * @return mixed Operation result. */ public function execute (Closure $callback):mixed { - $lease = $this->dependencyBoundary()->acquire(); + try { + + $lease = $this->dependencyBoundary()->acquire(); + + } catch (InvalidLifecycleStateException $exception) { + + throw new InvalidApplicationStateException( + 'Cannot execute an operation in the current application state.', + previous: $exception + ); + + } try { @@ -457,13 +471,12 @@ public function execute (Closure $callback):mixed { try { - $context = new ExecutionContext( - $this, $scope + return $callback( + new ExecutionContext($this, $scope) ); - return $callback($context); - } finally { + $scope->close(); } From 4411808fc3387c42ea77e3318c758d0681c112dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= Date: Wed, 21 Oct 2026 08:56:24 +0200 Subject: [PATCH 15/18] Add UnmanagedApplicationBuilderException and enhance Builder for managed applications --- src/Builder.php | 152 ++++++++++++++++-- .../UnmanagedApplicationBuilderException.php | 31 ++++ src/Manager.php | 149 ++++++++++------- 3 files changed, 259 insertions(+), 73 deletions(-) create mode 100644 src/Exception/UnmanagedApplicationBuilderException.php diff --git a/src/Builder.php b/src/Builder.php index e0aeb75..ef21645 100644 --- a/src/Builder.php +++ b/src/Builder.php @@ -22,27 +22,36 @@ use FireHub\Container\Container; use FireHub\Kernel\Kernel; use FireHub\Module\Manager as ModuleManager; -use FireHub\Application\Exception\ContainerRegistrationNotSupportedException; +use FireHub\Application\Exception\{ + ApplicationAlreadyRegisteredException, ApplicationKernelMismatchException, + ContainerRegistrationNotSupportedException, InvalidApplicationIdentifierException, UnmanagedApplicationBuilderException +}; /** * ### Builds and configures FireHub applications * - * Provides a fluent API for assembling an application from its context, kernel, dependency injection container, and - * lifecycle components. + * Provides a fluent API for assembling applications from their context, kernel, dependency injection container, + * modules, and lifecycle components. + * + * The builder supports both standalone and managed application construction. Standalone builders are created using + * create(), while managed builders are associated with an application manager and registration identifier. * - * The builder separates application construction from runtime lifecycle management. Building an application does not - * boot its kernel, initialize the application, or execute any operations. + * Building an application does not boot its kernel, initialize the application, or execute any operations. + * Managed builders additionally support registering constructed applications with their associated manager. * - * A kernel and container are created automatically when no explicit instances are provided. + * A kernel and container are created automatically when no explicit instances are provided. Managed applications + * inherit the shared kernel of their application manager. * - * Each build operation creates a new application instance. Explicitly configured kernel and container instances are - * shared between builds, while default instances are created independently for each application. + * Each build operation creates a new application instance. Explicitly configured kernel and container instances + * are shared between builds, while default instances are created independently for standalone applications. * * + * use FireHub\Application\Application; * use FireHub\Application\Builder; * use FireHub\Application\Context; * use FireHub\Application\Context\Name; * use FireHub\Application\Context\Environment; + * use FireHub\Application\ExecutionContext; * use FireHub\Container\Container; * use FireHub\Kernel\Kernel; * @@ -58,8 +67,9 @@ * Environment::PRODUCTION * ))->withKernel($kernel) * ->withContainer($container) - * ->withInitializer(new ClassThatImplementsInitializer) - * ->withDisposer(new ClassThatImplementsDisposer) + * ->withModule(new AppModule) + * ->withInitializer(new AppInitializer) + * ->withDisposer(new AppDisposer) * ->build(); * * $kernel->boot(); @@ -67,9 +77,9 @@ * * try { * - * $app->execute(function (Application $app):void { + * $app->execute(function (ExecutionContext $execution):void { * - * $service = $app->container()->get(MyService::class); + * $service = $execution->scope()->get(MyService::class); * * $service->run(); * @@ -94,6 +104,20 @@ */ final class Builder { + /** + * ### Application manager + * @since 1.0.0 + */ + private ?Manager $manager = null; + + /** + * ### Application registration identifier + * @since 1.0.0 + * + * @var null|non-empty-string + */ + private ?string $registration_id = null; + /** * ### Application kernel * @since 1.0.0 @@ -160,6 +184,47 @@ public static function create (Context $context):self { } + /** + * ### Creates a managed application builder + * + * Creates an application builder associated with the specified manager and registration identifier. + * + * Managed applications inherit the manager's shared kernel. + * @since 1.0.0 + * + * @uses \FireHub\Application\Manager::kernel() To get the application kernel. + * + * @param \FireHub\Application\Context $context

+ * Application context. + *

+ * @param \FireHub\Application\Manager $manager

+ * Application manager. + *

+ * @param non-empty-string $id

+ * Application registration identifier. + *

+ * + * @throws \FireHub\Application\Exception\InvalidApplicationIdentifierException If the identifier is empty. + * + * @return self Managed application builder. + */ + public static function managed (Context $context, Manager $manager, string $id):self { + + if ($id === '') + throw new InvalidApplicationIdentifierException( + 'Application identifier cannot be empty.' + ); + + $builder = new self($context); + + $builder->manager = $manager; + $builder->registration_id = $id; + $builder->kernel = $manager->kernel(); + + return $builder; + + } + /** * ### Configures the application kernel * @@ -168,14 +233,22 @@ public static function create (Context $context):self { * The specified kernel may be shared between multiple applications. * @since 1.0.0 * + * @uses \FireHub\Application\Manager::kernel() To get the application kernel. + * * @param \FireHub\Kernel\Kernel $kernel

* Application kernel. *

* + * @throws \FireHub\Application\Exception\ApplicationKernelMismatchException If the kernel is not shared with the + * manager. + * * @return $this Current builder. */ public function withKernel (Kernel $kernel):self { + if ($this->manager !== null && $kernel !== $this->manager->kernel()) + throw new ApplicationKernelMismatchException; + $this->kernel = $kernel; return $this; @@ -224,6 +297,7 @@ public function withContainer (ContainerBoundary $container):self { *

* * @throws \FireHub\Module\Exception\ModuleAlreadyRegisteredException If the module is already registered. + * @throws \FireHub\Module\Exception\ModuleManagerFrozenException If the manager is frozen. * * @return $this Current builder. */ @@ -374,4 +448,58 @@ public function build ():Application { } + /** + * ### Builds and registers an application + * + * Constructs the application and registers it with the associated application manager. + * + * Registration is available only for builders created through Builder::managed(). + * @since 1.0.0 + * + * @uses \FireHub\Application\Builder::build() To construct the application. + * @uses \FireHub\Application\Manager::register() To register the application. + * @uses \FireHub\Application\Manager::has() To check if the application is already registered. + * + * @throws \FireHub\Application\Exception\UnmanagedApplicationBuilderException If the builder is not associated + * with a manager. + * @throws \FireHub\Application\Exception\ApplicationAlreadyRegisteredException If the identifier is already + * registered. + * @throws \FireHub\Application\Exception\InvalidApplicationStateException If registration is not allowed. + * @throws \FireHub\Module\Exception\ModuleDependencyNotFoundException If a required module is not registered. + * @throws \FireHub\Module\Exception\CircularModuleDependencyException If circular dependencies are detected. + * @throws \FireHub\Application\Exception\ContainerRegistrationNotSupportedException If the container does not + * support service registration. + * @throws \FireHub\Application\Exception\InvalidApplicationIdentifierException If the identifier is empty. + * @throws \FireHub\Application\Exception\ApplicationAlreadyRegisteredException If the application is already + * registered. + * @throws \FireHub\Application\Exception\ApplicationKernelMismatchException If the application kernel does not + * match the manager kernel. + * + * @return \FireHub\Application\Application Registered application. + */ + public function register ():Application { + + if ($this->manager === null || $this->registration_id === null) + throw new UnmanagedApplicationBuilderException( + 'Cannot register an application using a standalone builder.', + context: [ + 'manager' => $this->manager, + 'registration_id' => $this->registration_id, + ] + ); + + if ($this->manager->has($this->registration_id)) + throw new ApplicationAlreadyRegisteredException; + + $application = $this->build(); + + $this->manager->register( + $this->registration_id, + $application + ); + + return $application; + + } + } \ No newline at end of file diff --git a/src/Exception/UnmanagedApplicationBuilderException.php b/src/Exception/UnmanagedApplicationBuilderException.php new file mode 100644 index 0000000..0e7ac82 --- /dev/null +++ b/src/Exception/UnmanagedApplicationBuilderException.php @@ -0,0 +1,31 @@ + + * @copyright 2026-present The FireHub Project - All rights reserved + * @license https://opensource.org/license/Apache-2-0 Apache License, Version 2.0 + * + * @php-version >=8.1 + * @package Application + */ + +namespace FireHub\Application\Exception; + +use FireHub\Core\Exception\Runtime\KernelException; + +/** + * ### Indicates that an application builder is not managed + * + * Thrown when an operation requiring an associated application manager is attempted to use a standalone application + * builder. + * + * Standalone builders can construct applications but cannot register them without an associated application manager. + * @since 1.0.0 + */ +final class UnmanagedApplicationBuilderException extends KernelException { + + protected const string DEFAULT_MESSAGE = 'The application builder is not managed'; + +} \ No newline at end of file diff --git a/src/Manager.php b/src/Manager.php index 42ef3fc..f2c1460 100644 --- a/src/Manager.php +++ b/src/Manager.php @@ -23,63 +23,66 @@ /** * ### Manages multiple FireHub applications * - * Coordinates the registration, initialization, and disposal of multiple applications associated with a shared kernel. + * Coordinates the registration, initialization, execution lifecycle, and disposal of multiple applications + * associated with a shared kernel. * - * Applications retain independent lifecycle states and dependency injection containers. The manager controls - * application lifecycle ordering without managing individual application executions or dependency injection scopes. + * Applications retain independent lifecycle states and dependency injection containers while sharing the same + * kernel instance. The manager coordinates application lifecycle ordering without managing individual executions + * or dependency injection scopes. * - * The manager owns the kernel lifecycle only when explicitly instructed to start or stop it. + * Applications can be registered explicitly using register() or configured and registered through managed builders + * created using application(). + * + * The bootstrap() operation boots the shared kernel and initializes all registered applications in registration + * order. Applications are disposed in reverse registration order using disposeAll(). + * + * The shared kernel remains active until shutdown() is explicitly invoked. * * - * use FireHub\Application\Manager; + * use FireHub\Application\Application; * use FireHub\Application\Context; * use FireHub\Application\Context\Name; * use FireHub\Application\Context\Environment; - * use FireHub\Kernel\Kernel; + * use FireHub\Application\ExecutionContext; + * use FireHub\Application\Manager; * * $manager = new Manager; * - * $frontend = $manager->create( + * $manager->application( + * 'frontend', * new Context( * new Name('Frontend'), * Environment::PRODUCTION - * ))->withContainer($container) - * ->build(); + * ))->withModule(new FrontendModule) + * ->withInitializer(new FrontendInitializer) + * ->withDisposer(new FrontendDisposer) + * ->register(); * - * $backend = $manager->create( + * $manager->application( + * 'backend', * new Context( * new Name('Backend'), * Environment::PRODUCTION - * ))->withContainer($container) - * ->build(); - * - * $manager - * ->register('frontend', $frontend) - * ->register('backend', $backend); - * - * $manager->initializeAll(); + * ))->withModule(new BackendModule) + * ->register(); * * try { * - * $result = $manager->get('frontend')->execute( - * function (Application $app):string { + * $manager->bootstrap(); * - * return 'Welcome to The FireHub Project!'; + * $manager->get('frontend')->execute( + * function (ExecutionContext $execution):void { * - * } - * ); + * $service = $execution->scope()->get(MyService::class); * - * } finally { + * $service->run(); * - * try { + * } + * ); * - * $manager->disposeAll(); - * - * } finally { - * - * $manager->shutdown(); + * } finally { * - * } + * $manager->shutdown(); * * } * @@ -127,12 +130,41 @@ public function __construct ( * Application context. *

* + * @throws \FireHub\Application\Exception\ApplicationKernelMismatchException If the kernel is not shared with the + * manager. + * * @return \FireHub\Application\Builder Configured application builder. */ public function create (Context $context):Builder { - return Builder::create($context) - ->withKernel($this->kernel); + return Builder::create($context)->withKernel($this->kernel); + + } + + /** + * ### Creates a managed application builder + * + * Creates a builder associated with this manager and assigns the specified registration identifier. + * + * The resulting builder can register the constructed application directly with this manager. + * @since 1.0.0 + * + * @uses \FireHub\Application\Builder::managed() To create the application builder. + * + * @param non-empty-string $id

+ * Application registration identifier. + *

+ * @param \FireHub\Application\Context $context

+ * Application context. + *

+ * + * @throws \FireHub\Application\Exception\InvalidApplicationIdentifierException If the identifier is empty. + * + * @return \FireHub\Application\Builder Managed application builder. + */ + public function application (string $id, Context $context):Builder { + + return Builder::managed($context, $this, $id); } @@ -302,9 +334,11 @@ public function dispose (string $id):void { } /** - * ### Initializes all registered applications + * ### Bootstraps all registered applications * - * Boots the shared kernel and initializes applications in registration order. + * Boots the shared kernel and initializes all registered applications in registration order. + * + * Each application retains its independent lifecycle state while sharing the same kernel. * @since 1.0.0 * * @uses \FireHub\Kernel\Kernel::boot() To boot the kernel. @@ -317,7 +351,7 @@ public function dispose (string $id):void { * * @return void */ - public function initializeAll ():void { + public function bootstrap ():void { $this->kernel->boot(); @@ -327,47 +361,40 @@ public function initializeAll ():void { } /** - * ### Disposes all registered applications + * ### Shuts down all managed applications and the shared kernel + * + * Disposes all registered applications in reverse registration order and subsequently shuts down the shared kernel. * - * Disposes applications in reverse registration order. + * The kernel shutdown is attempted even if application disposal fails. * - * The shared kernel remains active until explicitly shut down. + * Application disposal must be complete before the kernel is shut down because applications may depend on kernel + * services during their disposal lifecycle. * @since 1.0.0 * * @uses \FireHub\Application\Application::dispose() To dispose the application. * @uses \FireHub\Runtime\Arr\Transform::reverse() To reverse the application registration order. + * @uses \FireHub\Kernel\Kernel::shutdown() To shutdown the kernel. * + * @throws \FireHub\Kernel\Exception\InvalidKernelStateException If shutdown is not allowed. + * @throws \FireHub\Kernel\Exception\KernelShutdownException If termination fails. * @throws \FireHub\Application\Exception\InvalidApplicationStateException If disposal is not allowed. * @throws \FireHub\Application\Exception\ApplicationDisposalException If a disposer fails. * * @return void */ - public function disposeAll ():void { + public function shutdown ():void { - foreach (Runtime\Arr\Transform::reverse($this->applications) as $application) - $application->dispose(); + try { - } + foreach (Runtime\Arr\Transform::reverse($this->applications) as $application) + $application->dispose(); - /** - * ### Shuts down the shared kernel - * - * Shuts down the kernel after all applications have been disposed. - * - * The kernel lifecycle prevents shutdown while active application dependencies remain. - * @since 1.0.0 - * - * @uses \FireHub\Kernel\Kernel::shutdown() To shutdown the kernel. - * - * @throws \FireHub\Kernel\Exception\InvalidKernelStateException If shutdown is not allowed. - * @throws \FireHub\Kernel\Exception\KernelShutdownException If termination fails. - * - * @return void - */ - public function shutdown ():void { + } finally { - $this->kernel->shutdown(); + $this->kernel->shutdown(); - } + } + + } } \ No newline at end of file From dedf09dc64cae8dc9a3f35eb6ab96eebbd13377b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= Date: Wed, 21 Oct 2026 09:04:07 +0200 Subject: [PATCH 16/18] Add container freezing logic in application bootstrap process --- src/Manager.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Manager.php b/src/Manager.php index f2c1460..5b18fdf 100644 --- a/src/Manager.php +++ b/src/Manager.php @@ -14,6 +14,7 @@ namespace FireHub\Application; use FireHub\Kernel\Kernel; +use FireHub\Container\Container; use FireHub\Application\Exception\ { ApplicationAlreadyRegisteredException, ApplicationKernelMismatchException, ApplicationNotFoundException, InvalidApplicationIdentifierException @@ -343,6 +344,8 @@ public function dispose (string $id):void { * * @uses \FireHub\Kernel\Kernel::boot() To boot the kernel. * @uses \FireHub\Application\Application::initialize() To initialize the application. + * @uses \FireHub\Application\Application::container() To get the application container. + * @uses \FireHub\Container\Container::freeze() To freeze the container. * * @throws \FireHub\Kernel\Exception\InvalidKernelStateException If booting is not allowed. * @throws \FireHub\Kernel\Exception\KernelBootException If initialization fails. @@ -353,6 +356,15 @@ public function dispose (string $id):void { */ public function bootstrap ():void { + foreach ($this->applications as $application) { + + $container = $application->container(); + + if ($container instanceof Container) + $container->freeze(); + + } + $this->kernel->boot(); foreach ($this->applications as $application) From e31e6c87354eca2509c77dcf17d2a49c9482b05e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= Date: Wed, 21 Oct 2026 11:59:41 +0200 Subject: [PATCH 17/18] Update version to 0.1.0 in README and composer.json; add initial functionality entry in CHANGELOG --- CHANGELOG.md | 7 ++++++- README.md | 2 +- composer.json | 8 ++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 751d2d4..40de0ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,4 +3,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). \ No newline at end of file +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [v0.1.0] – 2026-10-21 + +### Added +* Implement initial functionality by @dieselxxx in https://github.com/The-FireHub-Project/Application/pull/1 \ No newline at end of file diff --git a/README.md b/README.md index 7555139..a5dba2b 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ /> version diff --git a/composer.json b/composer.json index f870d47..2ea906e 100644 --- a/composer.json +++ b/composer.json @@ -10,10 +10,10 @@ "the-firehub-project/core-standard": "dev-develop", "the-firehub-project/runtime": ">=0.1.0 <1.0.0", - "the-firehub-project/foundation": "dev-develop", - "the-firehub-project/kernel": "dev-develop", - "the-firehub-project/container": "dev-develop", - "the-firehub-project/module": "dev-develop" + "the-firehub-project/foundation": ">=0.1.0 <1.0.0", + "the-firehub-project/kernel": ">=0.1.0 <1.0.0", + "the-firehub-project/container": ">=0.1.0 <1.0.0", + "the-firehub-project/module": ">=0.1.0 <1.0.0" }, "require-dev": { "phpstan/phpstan": "^2.0", From 7b7cdc589e137d7e40295fe3442ac0d6c4ed59b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danijel=20Gali=C4=87?= <56673130+dieselxxx@users.noreply.github.com> Date: Wed, 21 Oct 2026 12:00:52 +0200 Subject: [PATCH 18/18] Update README.md --- README.md | 136 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 94 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index a5dba2b..cba5864 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ version + /> -[![PHPStan](https://github.com/The-FireHub-Project/Application/actions/workflows/PHPStan.yml/badge.svg?branch=develop)](https://github.com/The-FireHub-Project/Application/actions/workflows/PHPStan.yml) -[![PHPUnit](https://github.com/The-FireHub-Project/Application/actions/workflows/PHPUnit.yml/badge.svg?branch=develop)](https://github.com/The-FireHub-Project/Application/actions/workflows/PHPUnit.yml) -[![Codecov](https://codecov.io/gh/The-FireHub-Project/Application/branch/develop/graph/badge.svg?token=XW2YEONF51)](https://app.codecov.io/gh/The-FireHub-Project/Application/tree/develop) -

- + + GitHub created at + GitHub last commit (branch) - + GitHub activity (branch) - + + GitHub contributors + + GitHub language count + + GitHub top language + GitHub commit difference between two branches + + GitHub code size

@@ -50,47 +71,78 @@ Designed as a shared, edition-agnostic runtime component for Standard, Professio --- -## FireHub Icon Development Branch +## FireHub Icon About FireHub + +**FireHub** is a modular PHP framework built around: + +- Clean Architecture +- Domain-Driven Design (DDD) +- Explicit boundaries between Core, Adapters, and Modules +- Long-term maintainability and scalability -⚠️ **This is the `develop` branch** +FireHub is designed for building **enterprise-grade PHP applications** without sacrificing clarity or developer experience. -- Unstable -- APIs may change without notice -- Not intended for production use +## FireHub Icon Repository Purpose -## FireHub Icon Branch Purpose +FireHub Application provides application-level lifecycle management and orchestration for the FireHub Framework. It coordinates application initialization, configuration, readiness, and termination while delegating fundamental runtime lifecycle operations to the FireHub Kernel. -The `develop` branch is the **primary integration branch** for all ongoing development. +Its responsibilities include: -It serves as the staging area where: -- Feature branches are merged -- Bug fixes are integrated -- Experimental work is stabilized -- Code is prepared for upcoming releases +* **Application lifecycle** — managing application configuration, initialization, readiness, failure, and shutdown independently of the underlying Kernel lifecycle. +* **Bootstrap orchestration** — executing application-specific initialization components in a controlled order. +* **Termination orchestration** — coordinating application-specific cleanup before the underlying Kernel shuts down. +* **Application context** — providing application identity, environment, and execution metadata. +* **Service integration** — coordinating application service registration and initialization through dedicated runtime components. +* **Configuration integration** — preparing and exposing application configuration during initialization. +* **Module integration** — coordinating optional application modules and capabilities through explicit contracts. -All **release branches** are created **from `develop`**. +FireHub Application uses the FireHub Kernel for fundamental runtime initialization and termination. The Kernel remains independent of application-specific behavior, allowing other runtime components, such as workers and schedulers, to reuse the same infrastructure. -## Stability Guarantee +FireHub Application does not implement HTTP routing, console commands, background job processing, dependency resolution, or business logic. These responsibilities belong to dedicated adapters, runtime components, and application modules. -❌ No backward compatibility guarantee -❌ APIs may change without notice -❌ Behavior may be incomplete or inconsistent -❌ Breaking changes are expected +The Application runtime is edition-agnostic and supports Standard, Professional, and Enterprise applications through the contracts and extensions provided by their respective Core editions. -This branch is intended **only for contributors and advanced testers**. +## FireHub Icon Security -## FireHub Icon Composer Usage (Not Recommended) +For security reporting and guidelines, please refer to our [SECURITY.md](https://github.com/The-FireHub-Project/.github/blob/master/.github/SECURITY.md) file. + +## FireHub Icon Community + +![GitHub watchers](https://img.shields.io/github/watchers/The-FireHub-Project/Application?style=social) +![GitHub Repo stars](https://img.shields.io/github/stars/The-FireHub-Project/Application?style=social) +![GitHub forks](https://img.shields.io/github/forks/The-FireHub-Project/Application?style=social) + +- Be sure to review the [Code of Conduct](https://github.com/The-FireHub-Project/.github/blob/master/.github/CODE_OF_CONDUCT.md). +- Read a [Contributing](https://github.com/The-FireHub-Project/.github/blob/master/.github/CONTRIBUTING.md) file to see how you can contribute to this project. +- Read our [Security policy](https://github.com/The-FireHub-Project/.github/blob/master/.github/SECURITY.md) file to see how you can report a vulnerability. +- Review the project [Governance model](https://github.com/The-FireHub-Project/.github/blob/master/.github/GOVERNANCE.md), including roles, decision-making, RFC process, and releases. + +## FireHub Icon Support + +

+ + GitHub in-progress issues + + GitHub total issues + + GitHub opened pull requests +

-For internal testing only: +For help with FireHub products, please refer to the following resources: -```json -{ - "require": { - "the-firehub-project/application": "dev-develop" - } -} -``` -⚠️ Never use dev-develop in production. +- **General questions & discussions**: [GitHub Discussions](https://github.com/orgs/The-FireHub-Project/discussions) +- **Bug reports & issues**: [Support](https://github.com/The-FireHub-Project/Application/issues) +- **Documentation & guides**: [FireHub Docs](https://the-firehub-project.github.io) +- **Commercial support** (Pro/Enterprise): contact your account manager ## FireHub Icon Authors and Contributors @@ -107,4 +159,4 @@ Architecture guidelines, design principles, and ecosystem documentation are avai This software is licensed under the Apache-2.0 License. -For more details, read the full license [here](./LICENSE). \ No newline at end of file +For more details, read the full license [here](./LICENSE).