PHPackages                             georgeff/kernel - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. georgeff/kernel

ActiveLibrary[Framework](/categories/framework)

georgeff/kernel
===============

A lightweight application kernel with service container bootstrapping and lifecycle events

1.9.0(2w ago)044510MITPHPPHP ^8.2CI passing

Since Feb 10Pushed 2w agoCompare

[ Source](https://github.com/MikeGeorgeff/kernel)[ Packagist](https://packagist.org/packages/georgeff/kernel)[ RSS](/packages/georgeff-kernel/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (30)Versions (15)Used By (10)

Kernel
======

[](#kernel)

A lightweight application kernel with service container bootstrapping, module system, lifecycle callbacks, and PSR-14 event dispatching.

Installation
------------

[](#installation)

```
composer require georgeff/kernel
```

Usage
-----

[](#usage)

### Basic Bootstrapping

[](#basic-bootstrapping)

```
use Georgeff\Kernel\Environment;
use Georgeff\Kernel\Kernel;

$kernel = new Kernel(Environment::Production);

$kernel
    ->addDefinition('logger', fn() => new FileLogger('/var/log/app.log'), shared: true)
    ->addDefinition('mailer', fn() => new SmtpMailer('localhost'), shared: true);

$kernel->boot();

$container = $kernel->getContainer();
$logger = $container->get('logger');
```

### Environments

[](#environments)

The `Environment` enum provides five application environments:

- `Environment::Production`
- `Environment::Staging`
- `Environment::Development`
- `Environment::Testing`
- `Environment::Local`

`Local` is for local development machines. `Development` is the remote dev/integration tier.

```
$kernel = new Kernel(Environment::Local, debug: true);

$kernel->getEnvironment(); // 'local'
$kernel->isDebug();        // true
```

### Service Definitions

[](#service-definitions)

Register service definitions before booting. Each definition takes a factory callable, an optional shared flag, and optional aliases:

```
$kernel->addDefinition(
    'db.connection',
    fn() => new PdoConnection($dsn, $user, $pass),
    shared: true,
    aliases: [ConnectionInterface::class],
);
```

Definitions registered later with the same ID will overwrite earlier ones, allowing base definitions to be overridden.

### Fluent Definition Builder

[](#fluent-definition-builder)

`define()` is an alternative to `addDefinition()` that returns the definition for fluent configuration:

```
$kernel->define('db.connection', fn() => new PdoConnection($dsn, $user, $pass))
    ->share()
    ->alias(ConnectionInterface::class)
    ->tag('db.connections');
```

The builder methods are:

MethodDescription`share()`Register the service as a singleton`alias(string $alias)`Add a container alias`tag(string $tag)`Add a tagAll three return the same definition instance, so they can be chained in any order. `addDefinition()` remains available for cases where all options are known upfront.

### Definition Tags

[](#definition-tags)

Tags group service definitions under a shared label so they can be collected and resolved together. Pass a `tags` array to `addDefinition()`:

```
$kernel->addDefinition(
    FirstMiddleware::class,
    fn() => new FirstMiddleware(),
    shared: true,
    tags: ['http.middleware'],
);

$kernel->addDefinition(
    SecondMiddleware::class,
    fn() => new SecondMiddleware(),
    shared: true,
    tags: ['http.middleware'],
);
```

Retrieve all services for a tag via `TagRegistryInterface` after boot:

```
use Georgeff\Kernel\DI\TagRegistryInterface;

$kernel->boot();

$registry   = $kernel->getContainer()->get(TagRegistryInterface::class);
$middleware = $registry->getTagged('http.middleware');
// [FirstMiddleware, SecondMiddleware] — resolved in registration order
```

`tag()` is available as a standalone method for cases where the definition comes from another module or package:

```
final class MiddlewareModule implements ModuleInterface
{
    public function register(KernelInterface $kernel): void
    {
        // Tag a service defined by a different module
        $kernel->tag(RouterMiddleware::class, ['http.middleware']);
    }
}
```

Both `addDefinition()` and `tag()` throw `KernelException` if called after boot. Registering the same ID/tag pair more than once is idempotent.

### Service Decoration

[](#service-decoration)

`decorate()` wraps an existing service definition with a decorator. The decorator callable receives the resolved inner service and the container:

```
$kernel->addDefinition(
    LoggerInterface::class,
    fn() => new FileLogger('/var/log/app.log'),
    shared: true,
);

$kernel->decorate(
    LoggerInterface::class,
    fn(LoggerInterface $inner, ContainerInterface $c) => new TimestampLogger($inner),
);

$kernel->boot();

$logger = $kernel->getContainer()->get(LoggerInterface::class);
// TimestampLogger wrapping FileLogger
```

The decorated service automatically inherits the original's shared flag, aliases, and tags — existing consumers resolve the decorated version transparently.

`decorate()` can be called from a module's `register()` method to decorate a service contributed by another module. Because decoration is applied after all modules have registered, load order does not matter:

```
final class LoggingModule implements ModuleInterface
{
    public function register(KernelInterface $kernel): void
    {
        $kernel->decorate(
            CacheInterface::class,
            fn(CacheInterface $inner, ContainerInterface $c) => new LoggingCache(
                $inner,
                $c->get(LoggerInterface::class),
            ),
        );
    }
}
```

`decorate()` throws `KernelException` if called after boot or for a reserved service ID. A `KernelException` is also thrown at boot time if the target definition does not exist.

### Modules

[](#modules)

Modules are self-contained units that contribute service definitions, configuration, and boot logic to the kernel. They replace ad-hoc `addDefinition()` calls with composable, reusable pieces.

#### Defining a Module

[](#defining-a-module)

Every module implements `ModuleInterface` with a single `register()` method:

```
use Georgeff\Kernel\KernelInterface;
use Georgeff\Kernel\Module\ModuleInterface;

final class DatabaseModule implements ModuleInterface
{
    public function register(KernelInterface $kernel): void
    {
        $kernel->addDefinition(
            'db.connection',
            fn() => new PdoConnection(getenv('DB_DSN')),
            shared: true,
            aliases: [ConnectionInterface::class],
        );
    }
}
```

#### Configuration

[](#configuration)

Modules that need to declare configuration implement `ConfigurableModuleInterface`. The returned array is merged into the `kernel.config` container service during boot:

```
use Georgeff\Kernel\Environment;
use Georgeff\Kernel\KernelInterface;
use Georgeff\Kernel\Module\ConfigurableModuleInterface;

final class DatabaseModule implements ConfigurableModuleInterface
{
    public function register(KernelInterface $kernel): void
    {
        $kernel->addDefinition(
            'db.connection',
            fn(ContainerInterface $c) => new PdoConnection($c->get('db.dsn')),
            shared: true,
        );
    }

    public function config(Environment $env): array
    {
        return [
            'db.dsn'  => getenv('DB_DSN') ?: 'sqlite::memory:',
            'db.host' => getenv('DB_HOST') ?: 'localhost',
        ];
    }
}
```

The `$env` parameter is available for structural differences — for example, swapping a real driver for an in-memory one in testing:

```
public function config(Environment $env): array
{
    return [
        'db.dsn' => $env === Environment::Testing
            ? 'sqlite::memory:'
            : getenv('DB_DSN'),
    ];
}
```

Config from multiple modules is merged in registration order. Later definitions overwrite earlier ones for the same key.

`Env::get()` is available for reading environment variables with automatic type coercion — useful in `config()` implementations:

```
use Georgeff\Kernel\Support\Env;

public function config(Environment $env): array
{
    return [
        'db.dsn'  => Env::get('DB_DSN', 'sqlite::memory:'),
        'db.port' => Env::get('DB_PORT', '3306'),
        'db.log'  => Env::get('DB_LOG', false),
    ];
}
```

Coercion rules:

ValueResult`'true'`, `'TRUE'`, `'(true)'``true``'false'`, `'FALSE'`, `'(false)'``false``'null'`, `'NULL'`, `'(null)'``null`Valid JSON object or array`array`Anything elseraw `string`Numeric strings are intentionally left as strings — port numbers and similar values are most useful as strings.

#### Module Boot

[](#module-boot)

Modules that need access to the built container implement `BootableModuleInterface`. `boot()` is called after the container is fully initialized:

```
use Georgeff\Kernel\KernelInterface;
use Georgeff\Kernel\Module\BootableModuleInterface;
use Psr\Container\ContainerInterface;

final class MigrationModule implements BootableModuleInterface
{
    public function register(KernelInterface $kernel): void { /* ... */ }

    public function boot(ContainerInterface $container): void
    {
        $container->get(Migrator::class)->run();
    }
}
```

Because the container is already built when `boot()` is called, new service definitions cannot be added here — use `register()` for that.

#### Registering Modules

[](#registering-modules)

Register modules on the kernel before booting:

```
$kernel = new Kernel(Environment::Production);

$kernel
    ->addModule(new DatabaseModule())
    ->addModule(new CacheModule())
    ->addModule(new MigrationModule());

$kernel->boot();
```

Each module class may only be registered once. Registering the same class twice throws a `KernelException`.

#### Module Repositories

[](#module-repositories)

Repositories group related modules and can conditionally include them based on the environment. Packages ship a repository rather than exposing individual modules:

```
use Georgeff\Kernel\Environment;
use Georgeff\Kernel\Module\ModuleInterface;
use Georgeff\Kernel\Module\ModuleRepositoryInterface;

final class DatabaseRepository implements ModuleRepositoryInterface
{
    public function modules(Environment $env): array
    {
        $modules = [
            new DatabaseModule(),
            new MigrationModule(),
        ];

        if ($env !== Environment::Production) {
            $modules[] = new DatabaseDebugModule();
        }

        return $modules;
    }
}
```

```
$kernel->addRepository(new DatabaseRepository());
```

#### Boot Phase Order

[](#boot-phase-order)

When `boot()` is called, modules are processed in this order:

1. `onBooting` callbacks
2. **Module load** — repositories are flattened into the module list; `config()` is called on all `ConfigurableModuleInterface` modules and the result is bound as `kernel.config`
3. **Module registration** — `register()` is called on all modules
4. **Service decoration** — pending decorators are applied after all modules have registered
5. Container initialization
6. **Module boot** — `boot()` is called on all `BootableModuleInterface` modules
7. `KernelBooted` event dispatched + `onBooted` callbacks

### Lifecycle Callbacks

[](#lifecycle-callbacks)

The kernel provides four hooks for tapping into the boot and shutdown lifecycle. All callbacks receive the full `KernelInterface` instance and all hook methods return the kernel for fluent chaining.

#### Boot callbacks

[](#boot-callbacks)

`onBooting` runs before service definitions are registered with the container. Use it to add definitions dynamically or configure the kernel before boot:

```
$kernel->onBooting(function (KernelInterface $kernel) {
    $kernel->addDefinition('dynamic', fn() => new SomeService(), shared: true);
});
```

`onBooted` runs after boot completes and the `KernelBooted` event has been dispatched. The container is available at this point:

```
$kernel->onBooted(function (KernelInterface $kernel) {
    $kernel->getContainer()->get('logger')->info('Kernel booted');
});
```

Both must be registered before `boot()` is called.

#### Shutdown callbacks

[](#shutdown-callbacks)

`onShutdown` runs before the kernel is marked as shut down. `afterShutdown` runs after. Both can be registered any time before `shutdown()` is called — including after boot:

```
$kernel->onShutdown(function (KernelInterface $kernel) {
    // isShutdown() is still false here
});

$kernel->afterShutdown(function (KernelInterface $kernel) {
    // isShutdown() is true here
});
```

### Shutdown

[](#shutdown)

Call `shutdown()` to run the shutdown lifecycle. It is idempotent and a no-op if the kernel has not been booted:

```
$kernel->boot();

// handle a request, run a command, etc.

$kernel->shutdown();

$kernel->isShutdown(); // true
```

Shutdown runs in this order:

1. `onShutdown` callbacks
2. Kernel marked as shut down (`isShutdown()` becomes `true`)
3. `afterShutdown` callbacks

### Events

[](#events)

After boot completes, the kernel dispatches a `KernelBooted` event via PSR-14 if an `EventDispatcherInterface` is registered in the container:

```
use Georgeff\Kernel\Event\KernelBooted;
use Psr\EventDispatcher\EventDispatcherInterface;

$kernel->addDefinition(
    EventDispatcherInterface::class,
    fn() => new MyEventDispatcher(),
    shared: true,
);

$kernel->boot(); // dispatches KernelBooted

// In your listener:
function handleBooted(KernelBooted $event): void {
    $kernel = $event->kernel; // readonly public property
}
```

If no `EventDispatcherInterface` is registered, boot completes without dispatching.

### Custom Service Registrar

[](#custom-service-registrar)

The kernel uses a `ServiceRegistrar` interface to register definitions with the container. `DefaultServiceRegistrar`, backed by `georgeff/container`, is used by default. Provide your own to use a different container implementation:

```
$registrar = new MyServiceRegistrar();
$kernel = new Kernel(Environment::Production, $registrar);
```

If the registrar also implements `ResolvingAwareServiceRegistrar`, the kernel registers a post-resolution hook in debug mode to track which services have been resolved and collect debug info from `DebuggableInterface` implementations. Custom registrars that do not implement it will function normally — debug resolution tracking simply will not be available.

### Debug Mode

[](#debug-mode)

When debug mode is enabled, the kernel profiles the boot process and tracks service resolutions. If the registrar implements `ResolvingAwareServiceRegistrar`, the kernel also collects debug info from any resolved service that implements `DebuggableInterface`:

```
$kernel = new Kernel(Environment::Development, debug: true);
$kernel->boot();

$kernel->getStartTime(); // float (microtime)
$kernel->getDebugInfo(); // boot profile + service resolution data
```

The `getDebugInfo()` array contains:

- `bootProfile` — timing for each boot phase (`preBoot`, `moduleLoad`, `moduleRegistration`, `serviceDecoration`, `serviceRegistration`, `containerInit`, `moduleBoot`)
- `modules` — module loader state: which module classes were loaded and whether each phase has run
- `services` — present only when the registrar implements `ResolvingAwareServiceRegistrar`; tracks which services have been resolved and which remain unresolved; each resolved entry includes a `resolutionCount` and, for services implementing `DebuggableInterface`, a `debugInfo` key containing the output of their `getDebugInfo()` method

When debug is disabled, `getStartTime()` returns `-INF` and `getDebugInfo()` returns `[]`.

#### DebuggableInterface

[](#debuggableinterface)

Services can implement `DebuggableInterface` to expose debug data. When the registrar implements `ResolvingAwareServiceRegistrar` and debug mode is enabled, their `getDebugInfo()` output is collected automatically after each factory resolution and included in the kernel's debug info under `services.resolved`:

```
use Georgeff\Kernel\Debug\DebuggableInterface;

final class ConnectionPool implements DebuggableInterface
{
    public function getDebugInfo(): array
    {
        return ['active' => $this->activeCount, 'idle' => $this->idleCount];
    }
}
```

### KernelException Helpers

[](#kernelexception-helpers)

`KernelException` provides three static helpers for throwing from guard conditions without an inline `if`:

```
use Georgeff\Kernel\KernelException;

// Always throws
KernelException::throw('Something went wrong');

// Throws if $condition is true
KernelException::throwIf($this->isBooted(), 'Kernel is already booted');

// Throws if $condition is false
KernelException::throwIfNot($this->isBooted(), 'Kernel has not been booted');
```

These are primarily useful when authoring custom kernel subclasses or modules that need guard conditions consistent with the kernel's own error type.

### Reserved Services

[](#reserved-services)

The kernel registers the following services in the container during boot:

- `kernel` (aliased to `KernelInterface`)
- `kernel.environment` — the environment string value (e.g. `'production'`)
- `kernel.debug` — the debug flag (`bool`)
- `kernel.config` — the merged config array from all `ConfigurableModuleInterface` modules (`[]` if none)
- `kernel.tag.registry` (aliased to `TagRegistryInterface`) — the tag registry

These IDs cannot be overwritten via `addDefinition`. The `kernel.*` namespace is reserved for the kernel — any service ID with that prefix should be considered owned by the package and subject to change between minor versions.

### Extending the Kernel

[](#extending-the-kernel)

The `Kernel` class can be extended for specialized use cases such as HTTP or console kernels. A `RunnableKernelInterface` is provided for kernels that serve as an application entry point:

```
use Georgeff\Kernel\RunnableKernelInterface;

class ConsoleKernel extends Kernel implements RunnableKernelInterface
{
    public function run(): int
    {
        $this->boot();

        // dispatch console command...

        return 0;
    }
}
```

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md).

License
-------

[](#license)

MIT

###  Health Score

48

—

FairBetter than 93% of packages

Maintenance98

Actively maintained with recent releases

Popularity17

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~11 days

Recently: every ~4 days

Total

12

Last Release

14d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/c8147ce1d901e9fa2ec95d6408e5862025211f9ae60131e41fb115a5a0916ce4?d=identicon)[georgeff](/maintainers/georgeff)

---

Top Contributors

[![MikeGeorgeff](https://avatars.githubusercontent.com/u/6169468?v=4)](https://github.com/MikeGeorgeff "MikeGeorgeff (34 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/georgeff-kernel/health.svg)

```
[![Health](https://phpackages.com/badges/georgeff-kernel/health.svg)](https://phpackages.com/packages/georgeff-kernel)
```

###  Alternatives

[symfony/symfony

The Symfony PHP framework

31.4k87.2M2.2k](/packages/symfony-symfony)[laravel/framework

The Laravel Framework.

34.8k543.8M20.1k](/packages/laravel-framework)[drupal/core-recommended

Locked core dependencies; require this project INSTEAD OF drupal/core.

6942.5M421](/packages/drupal-core-recommended)[typo3/cms

TYPO3 CMS is a free open source Content Management Framework initially created by Kasper Skaarhoj and licensed under GNU/GPL.

1.2k1.9M122](/packages/typo3-cms)[spiral/framework

Spiral, High-Performance PHP/Go Framework

2.1k2.2M66](/packages/spiral-framework)[typo3/cms-core

TYPO3 CMS Core

3713.2M5.1k](/packages/typo3-cms-core)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
