Kernel Overview

The minimal, no-magic core. Everything else is a capability you plug in when you need it.

In short: The Kernel handles bootstrapping, dependency injection, module loading, CLI and config. Router, view, database, ORM, auth, storage — those live in capabilities. You assemble: Kernel + Capabilities + Your Code = Your App.

What is the Kernel

The minimal, no-magic core of Forge. It handles bootstrapping, dependency injection, module loading, configuration, and CLI infrastructure. Everything else — database, routing, authentication, storage, templating — is a capability you plug in when you need it.

You don't build a “Forge app”. You build your app on top of the Kernel. The Kernel stays lean. You stay in control. No baked-in opinions about databases, routers, or templating.

final class Kernel {
    public static function init(): void {
        if (FileExistenceCache::exists(BASE_PATH . "/.env")) {
            EnvParser::load(BASE_PATH . "/.env");
        }
        Bootstrap::getInstance();
    }
}

That's the whole entry point. Both the web request and the CLI go through it. The details live in the Lifecycle page.

What's In the Kernel

A small set of essentials. If it's not here, it's a capability.

DI Container

Automatic wiring. Add a type-hint to your constructor and it resolves. Supports singletons and interface binding.

Module System

Discovers modules in modules/ and capabilities/, orders them, and runs lifecycle hooks.

CLI Kernel

Command routing, generators, and the interactive browser you get with php forge.php.

Configuration

Simple config files plus environment variables. Helpers config() and env() everywhere.

Bootstrap

Creates storage folders, loads .env, sets timezone and secure session defaults.

Autoloader

PSR-4, with a cache so it doesn't scan the filesystem on every request.

Cache & Session

Driver-based cache with transparent proxy, and session handling with secure cookies.

Validation

Small, explicit validation rules you can use anywhere.

Contracts

Interfaces for things like database and views — the Kernel defines the shape, capabilities provide the code.

Helpers & Traits

Small helpers like env(), cache(), e() and shared traits you can reuse.

Debug & Observability

Metrics and small utilities to understand what's happening.

Structure

Where files live and what namespace they use — and you can change it if you want. See Anatomy.

What's NOT in the Kernel

If it touches HTTP, HTML, or the database — it's a capability. The Kernel only provides the contract. The capability provides the implementation.

Not built-in. Install when you need it: Every item below needs php forge.php package:install-module --module=Name. If you don't install it, nothing breaks — it just isn't there.

HTTP & Rendering

Data & Auth

Infra & Tooling

Kernel stays lean

Capabilities, not built-ins. Database, ORM, authentication, storage — they aren't in the kernel. They're capabilities you plug in when you need them. No hidden dependencies.

Contracts vs Implementations

The Kernel defines interfaces. Capabilities implement them. Your code depends on the interface, so you can swap the implementation or write your own.

interface DatabaseConnectionInterface {
    public function getPdo(): PDO;
    public function exec(string $statement): int|false;
    public function prepare(string $statement): PDOStatement;
    public function query(string $statement): PDOStatement;
    public function beginTransaction(): bool;
    public function commit(): bool;
    public function rollBack(): bool;
    public function getDriver(): string; // sqlite | mysql | pgsql
}

Without the capability

Container::get(DatabaseConnectionInterface::class)
→ MissingServiceException
// Kernel has the interface, no implementation

After installing it

php forge.php package:install-module --module=ForgeDatabaseSQL
Container::get(DatabaseConnectionInterface::class)
→ PDO-backed Connection

Same pattern for views, cache, and events. If you want your own database layer, implement the interface and bind it in your module's register() — the Kernel doesn't need to know which one you chose.

Philosophy

This isn't a product. There's no company behind it. No support SLA. No roadmap you should depend on.

Forge exists because I like to understand and own my stack. If others find value in it — awesome. If not — that's cool too.

You're not a user here. You're a builder.

  • If you use Forge, it's yours now. Your rules. Your path.
  • If I publish updates, they're for my use case. Pull them in if they help.
  • If my direction doesn't match yours, fork it. Change everything. That's the point.

MIT licensed. Take what helps, ignore what doesn't. See Forging Your Own for how to fork the whole thing.

Capabilities vs Modules

To the Kernel they're the same. It scans both modules/ and capabilities/. The difference is just convention and namespace.

LocationNamespaceUse forExample
capabilities/Capability\Primitives, reusable building blockscapabilities/ForgeHtmx
modules/Modules\Your app featuresmodules/Blog

Your choice. Follow the convention, or put everything in modules/, or everything in capabilities/, or mix them. The Kernel doesn't enforce it. It's a slow migration — most primitives still live in modules/ and will move over time. ForgeHtmx is already in capabilities/ as the reference.

Put your services in the injectable folders — app/Services or app/Listeners for your app, src/Services, src/Listeners or src/Providers inside a module — and they are found automatically. No attribute needed. If you want a custom id or non-singleton, you can add #[Injectable] — it's optional and just a leftover from before the refactor. Only those folders are scanned — not every folder — which keeps things fast. You can change the folders if you want (see Anatomy).

Customizing Folder Structure

Don't like the defaults? Change them. You don't have to create the file by hand — run the wizard and it will make it for you:

php forge.php structure:init   # wizard: pick app / modules / roots, or partial
php forge.php structure:info   # see current config (interactive)

That's the easy path. If you prefer, you can still create forge_structure.php by hand at the project root and override any path or namespace — app root, module roots, or where controllers, views, and models live. The wizard just writes the same file for you.

A quick warning from the wizard itself: changing paths won't move your files for you, and deleting the file won't move them back. Back up first, and make sure your filesystem matches what you configure. Modules that define their own structure with #[Structure] have the final say — they aren't affected by this file.

Each capability or module can also define its own internal layout. The details for that live in the capability's own page, not here.

Full examples and the interactive viewer are in Anatomy → Customizing Folder Structure & Namespaces.