PHPackages                             waffle-commons/waffle - 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. waffle-commons/waffle

ActiveLibrary[Framework](/categories/framework)

waffle-commons/waffle
=====================

A modern, minimalist, and security-focused PHP micro-framework.

0.1.0-beta4(2w ago)364[1 issues](https://github.com/waffle-commons/waffle/issues)1MITPHPPHP ^8.5CI passing

Since Oct 18Pushed 2w ago1 watchersCompare

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

READMEChangelog (10)Dependencies (84)Versions (29)Used By (1)

[![Discord](https://camo.githubusercontent.com/b30f41baece56d71f7f496f7e39fd33a2a096221c66c648b350dd4fe14276c2e/68747470733a2f2f696d672e736869656c64732e696f2f646973636f72642f3735353238383030313539323033333339313f6c6f676f3d646973636f7264)](https://discord.gg/eKgywnfXr2)[![PHP Version Require](https://camo.githubusercontent.com/e741105aa9459daa0f9697aa60927fede3b93f0778738a647d63e56364e60765/687474703a2f2f706f7365722e707567782e6f72672f776166666c652d636f6d6d6f6e732f776166666c652f726571756972652f706870)](https://packagist.org/packages/waffle-commons/waffle)[![PHP CI](https://github.com/waffle-commons/waffle/actions/workflows/main.yml/badge.svg)](https://github.com/waffle-commons/waffle/actions/workflows/main.yml)[![codecov](https://camo.githubusercontent.com/7986e8c58d2e557120b63d6d97ae4c9a2903a4ed91b15eba1d2c2bfb89feb8a3/68747470733a2f2f636f6465636f762e696f2f67682f776166666c652d636f6d6d6f6e732f776166666c652f67726170682f62616467652e7376673f746f6b656e3d64373461633632612d373837322d343033352d386238622d626363336166313939316530)](https://codecov.io/gh/waffle-commons/waffle)[![Latest Stable Version](https://camo.githubusercontent.com/7ff8ba34826ef7fd9261aadf19983e244eaecb90f3dbfe55dfe8037724070680/687474703a2f2f706f7365722e707567782e6f72672f776166666c652d636f6d6d6f6e732f776166666c652f76)](https://packagist.org/packages/waffle-commons/waffle)[![Latest Unstable Version](https://camo.githubusercontent.com/8a629c1c566ef2e56c8d73846dbd182f471b64095fb8bd3fa83faa643e005e5c/687474703a2f2f706f7365722e707567782e6f72672f776166666c652d636f6d6d6f6e732f776166666c652f762f756e737461626c65)](https://packagist.org/packages/waffle-commons/waffle)[![Total Downloads](https://camo.githubusercontent.com/9c57c532f97738204876f678145f8b4bc3fa3c9a0e4bb431abfa6cc603c0049f/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f776166666c652d636f6d6d6f6e732f776166666c652e737667)](https://packagist.org/packages/waffle-commons/waffle)[![Packagist License](https://camo.githubusercontent.com/3f69333d583c5526abb3c8c9f537edad5bc847effb9b52eb02a1c5057943894e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f776166666c652d636f6d6d6f6e732f776166666c65)](https://github.com/waffle-commons/waffle/blob/main/LICENSE.md)

Waffle — the Kernel
===================

[](#waffle--the-kernel)

> **Release:** `0.1.0-beta4` | [`CHANGELOG.md`](./CHANGELOG.md)

The application kernel. Orchestrates request handling against the PSR-15 middleware stack, dispatches `RequestReceivedEvent` / `ResponseGeneratedEvent` / `TerminateEvent`, and resolves controllers via the container. The kernel itself stays agnostic of routing, security, logging, and HTTP — every concrete dependency is injected.

🆕 Beta-1 highlights
-------------------

[](#-beta-1-highlights)

- **Kernel decoupling.** `AbstractKernel::handle()` resolves the terminal handler from the container under `Psr\Http\Server\RequestHandlerInterface` — there is no hard-coded `new ControllerDispatcher(...)` on the hot path. `configure()` registers a default `ControllerDispatcher` only when the slot is empty (`has()`-gated, idempotent), so an app can swap in its own terminal handler by pre-registering one.
- **Native DTO validation → 422.** `ControllerArgumentResolver` hydrates `#[Dto]`-tagged parameters from the parsed request body, letting PHP 8.5 *set* property hooks run their assertions during construction. A hook failure is trapped and re-thrown as a unified `ValidationException`, which the error handler renders as RFC 7807 `422 Unprocessable Entity` — closing the Mass-Assignment gap without any external validation package.

📦 Installation
--------------

[](#-installation)

```
composer require waffle-commons/waffle
```

🧱 Surface
---------

[](#-surface)

ClassRole`Waffle\Kernel`Concrete kernel. Extends `AbstractKernel`.`Waffle\Abstract\AbstractKernel`Implements `KernelInterface::boot/configure/handle/reset`; provides DI setters for `setContainerImplementation`, `setConfiguration`, `setSecurity`, `setMiddlewareStack`, `setEventDispatcher`.`Waffle\Abstract\AbstractController`Convenient base for app controllers; provides PSR-7 helper accessors.`Waffle\Abstract\AbstractSystem`Internal lifecycle binder used by `Waffle\Core\System`.`Waffle\Core\System`Boots the security / container linkage once the kernel reaches `configure()`.`Waffle\Core\BaseController`Default `BaseControllerInterface` implementation.`Waffle\Handler\ControllerDispatcher`Terminal PSR-15 handler that the middleware stack falls through to. Calls the controller method resolved from `_controller` and `_route_params`.`Waffle\Handler\ControllerArgumentResolver`Hydrates controller parameters, including `#[Dto]`-tagged DTOs from the parsed body.`Waffle\Handler\ControllerResponseConverter`Converts a controller's scalar / array return into a PSR-7 `ResponseInterface`.`Waffle\Exception\*`Domain exceptions (`WaffleException`, `RouteNotFoundException`, `RenderingException`, `ValidationException`, `InvalidConfigurationException`).🚀 Composing a kernel
--------------------

[](#-composing-a-kernel)

```
use Waffle\Kernel;
use Waffle\Commons\Container\Container;
use Waffle\Commons\Config\Config;
use Waffle\Commons\Pipeline\MiddlewareStack;
use Waffle\Commons\Security\Security;
use Waffle\Commons\EventDispatcher\Dispatcher\EventDispatcher;
use Waffle\Commons\EventDispatcher\Provider\ListenerProvider;
use Waffle\Commons\Log\StreamLogger;

$config = new Config(__DIR__ . '/config', getenv('APP_ENV') ?: 'prod');

$container = new Container();
$container->set(ConfigInterface::class, $config);

$kernel = new Kernel(new StreamLogger());
$kernel->setConfiguration($config);
$kernel->setContainerImplementation($container);
$kernel->setSecurity(new Security($config));
$kernel->setMiddlewareStack((new MiddlewareStack())
    ->add(new ErrorHandlerMiddleware($renderer, $logger))
    ->add(new TrustedHostMiddleware($config->getArray('waffle.trusted_hosts', []) ?? []))
    ->add(new CoreRoutingMiddleware($router))
    ->add(new SecurityMiddleware($kernel->security))
);
$kernel->setEventDispatcher(new EventDispatcher(new ListenerProvider()));
```

The setter signatures, verbatim from `src/Abstract/AbstractKernel.php`:

```
public function setContainerImplementation(PsrContainerInterface $container): void;
public function setConfiguration(ConfigInterface $config): void;
public function setSecurity(SecurityInterface $security): void;
public function setMiddlewareStack(MiddlewareStackInterface $stack): void;
public function setEventDispatcher(EventDispatcherInterface $dispatcher): void;
```

The constructor takes a PSR-3 logger only (defaults to `NullLogger`):

```
public function __construct(protected LoggerInterface $logger = new NullLogger())
```

🔁 Request lifecycle
-------------------

[](#-request-lifecycle)

1. `handle(ServerRequestInterface)` ensures `boot()` + `configure()` have run (idempotent — the `$booted` guard short-circuits subsequent calls).
2. `validateState()` rejects an unconfigured kernel with `RuntimeException` / `ContainerException` / `NotFoundException` as appropriate.
3. `RequestReceivedEvent` is dispatched; listeners may swap the request.
4. The middleware stack runs; the terminal handler is a `ControllerDispatcher` resolving the route's `_controller` + `_route_params`.
5. `ResponseGeneratedEvent` is dispatched; listeners may swap the response.
6. The response is returned to `WaffleRuntime` which emits it and calls `terminate()` for post-emission listeners.
7. `reset()` clears request-scoped state (container reset).

📨 Built-in events
-----------------

[](#-built-in-events)

- `Waffle\Event\RequestReceivedEvent` — fires before the middleware pipeline runs.
- `Waffle\Event\ResponseGeneratedEvent` — fires after the pipeline returns.
- `Waffle\Event\TerminateEvent` — fires after the response is emitted (for heavy async work).

All three are PSR-14 events; `RequestReceivedEvent` and `ResponseGeneratedEvent` are *not* stoppable — they expose mutator methods (`getRequest()`, `getResponse()`) so listeners can replace the message.

🐘 PHP 8.5 features used
-----------------------

[](#-php-85-features-used)

- `protected(set)` asymmetric visibility on `$system` and `$middlewareStack`.
- Constructor property promotion on `$logger`.
- Typed-constant defaults (`Constant::ENV_PROD`).
- `#[\Override]` on every method overriding `KernelInterface`.

🧭 Architectural boundary (`mago guard`)
---------------------------------------

[](#-architectural-boundary-mago-guard)

An active dependency **perimeter** is enforced on every CI run by `vendor/bin/mago guard` (bundled into `composer mago`; zero baselines). The rules live in [`mago.toml`](./mago.toml) under `[guard.perimeter]` — a forbidden `use` statement fails the build, not a reviewer.

As the framework assembly package, `waffle` lives under the top-level `Waffle` namespace (not `Waffle\Commons\*`). Production code under `Waffle` may depend **only** on:

- `Waffle\**` — itself (the kernel, handlers, events, and factories)
- `Waffle\Commons\Contracts\**` — the shared contracts package
- `Waffle\Commons\Utils\**` — the `ClassParser` discovery helper
- `Psr\**` — PSR interfaces (PSR-7 / PSR-11 / PSR-15 / PSR-17)
- `@global` + `Psl\**` — PHP core and the PHP Standard Library

Test code under `WaffleTests` is unrestricted (`@all`). Structural rules are guarded too: interfaces must be named `*Interface`, `Exception\**` classes must end in `*Exception`, and any `Enum\**` namespace may hold only `enum` declarations.

Note: `waffle` depends only on `contracts` + `utils` directly. The concrete components (`http`, `routing`, `security`, …) are wired at the **application** layer (e.g. the skeleton's `AppKernelFactory`), not pulled in here — that is what keeps the kernel component-agnostic.

🧪 Testing
---------

[](#-testing)

```
docker exec -w /waffle-commons/waffle waffle-dev composer tests
```

📄 License
---------

[](#-license)

MIT — see [LICENSE.md](./LICENSE.md).

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance85

Actively maintained with recent releases

Popularity14

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity49

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 ~20 days

Recently: every ~5 days

Total

13

Last Release

19d ago

PHP version history (2 changes)0.1.0-alpha1.0PHP ^8.4

0.1.0-alpha4PHP ^8.5

### Community

Maintainers

![](https://www.gravatar.com/avatar/34a7557a3fb23aaf788ca3892b9b7efdf96e753264bafd0599153c9e8a921316?d=identicon)[LesliePetrimaux](/maintainers/LesliePetrimaux)

---

Top Contributors

[![supa-chayajin](https://avatars.githubusercontent.com/u/695448?v=4)](https://github.com/supa-chayajin "supa-chayajin (215 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm

Type Coverage Yes

### Embed Badge

![Health badge](/badges/waffle-commons-waffle/health.svg)

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

###  Alternatives

[symfony/symfony

The Symfony PHP framework

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

The PHP framework that gets out of your way.

2.2k34.4k15](/packages/tempest-framework)[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)[cakephp/cakephp

The CakePHP framework

8.9k19.5M1.8k](/packages/cakephp-cakephp)[typo3/cms-core

TYPO3 CMS Core

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

The next generation PHP framework.

25740.3k1](/packages/windwalker-framework)

PHPackages © 2026

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