PHPackages                             stein/stein - 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. stein/stein

ActiveLibrary[Framework](/categories/framework)

stein/stein
===========

The PSR-assembled, FrankenPHP-ready, high-performance micro-framework.

1.2.0(1mo ago)07[1 PRs](https://github.com/debuss/stein-framework/pulls)MITPHPPHP ^8.3

Since Jan 28Pushed 1mo agoCompare

[ Source](https://github.com/debuss/stein-framework)[ Packagist](https://packagist.org/packages/stein/stein)[ RSS](/packages/stein-stein/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (4)Dependencies (29)Versions (10)Used By (0)

⚡ Stein
-------

[](#-stein)

**The PSR-assembled, FrankenPHP-ready, high-performance micro-framework.**

**Stein** is a "Modern Prometheus" experiment in PHP. Instead of reinventing the wheel, Stein stitches together the most powerful, industry-standard PSR components into a cohesive, lightning-fast organism.

Born to run in **FrankenPHP's Worker Mode**, Stein is lean, mean, and obsessed with clean architecture (DDD).

🧪 The "Parts" (Stitched with ❤️)
--------------------------------

[](#-the-parts-stitched-with-️)

Stein doesn't hide its seams. It's proud to be built on the shoulders of giants:

- **Container:** `league/container` (with auto-wiring &amp; `afterResolve` hooks).
- **Routing:** `league/route` (with route caching for production).
- **HTTP Messages:** `laminas/laminas-diactoros` (PSR-7/17 factories &amp; responses).
- **Middleware:** `middlewares/error-handler`, `middlewares/payload`.
- **Logging:** `monolog` (pre-configured for Docker/K8s, JSON output to stderr).
- **Templating:** `mezzio/mezzio-platesrenderer` (swappable via Mezzio Template bridge).
- **Configuration:** `borschphp/config` (INI-based `.env` aggregation).
- **Awareness:** `debuss-a/awareness` (PSR-aware interfaces &amp; traits for clean DI).
- **Server:** Native **FrankenPHP** worker loop integration.

🚀 Key Concepts
--------------

[](#-key-concepts)

#### 1. Route Configuration

[](#1-route-configuration)

Routes are defined in `config/routes.php` as a simple closure receiving the router and container. They support grouping, per-group middleware, and FastRoute-style path patterns.

```
// config/routes.php
return static function (RouterInterface $router, ContainerInterface $container): void {

    $router->map('GET', '/', HomePageController::class);

    $router->group('/api/v1', function (RouteGroup $group) {
        $group->map('GET', '/users[/{id:\d+}]', UserController::class);
    })->lazyMiddlewares([JsonPayload::class]);

};
```

In production (`APP_ENV=production`), routes are automatically cached to a file via `league/route`'s `CachedRouter`, giving you zero-overhead routing after the first request.

#### 2. The Worker-First Mindset

[](#2-the-worker-first-mindset)

Stein is built for 2026. It stays in memory between requests thanks to FrankenPHP. `worker.php` handles the request loop and boots the container **once**, keeping your app screamingly fast by avoiding the "boot-everything-every-time" tax of traditional FPM.

A configurable restart threshold prevents memory creep:

```
; .env
FRANKENPHP_NB_REQUEST_TO_RESTART=1000
```

#### 3. Dependency Injection via Awareness

[](#3-dependency-injection-via-awareness)

Stein uses `league/container`'s `afterResolve` hooks to automatically inject PSR services into any class that implements the corresponding `AwareInterface` — with **zero constructor pollution**.

If your class implements `LoggerAwareInterface`, the container calls `setLogger()` automatically after resolving it. The same pattern works for `TemplateRendererAwareInterface`, `ResponseFactoryAwareInterface`, and any custom aware interface you define.

#### 4. DDD Project Structure

[](#4-ddd-project-structure)

Stein scaffolds a clean Domain-Driven Design layout out of the box:

```
src/
├── Application    # Controllers, Service Providers, Use Cases
├── Domain         # Entities, Repository Interfaces, Value Objects, Domain Exceptions
└── Infrastructure # Repository implementations, third-party adapters

```

💉 Smart Dependency Injection (The Awareness Pattern)
----------------------------------------------------

[](#-smart-dependency-injection-the-awareness-pattern)

If your class needs a PSR service, implement the corresponding `AwareInterface` and Stein's container injects it automatically after resolution — no manual wiring, no constructor bloat.

**The base `Controller` class demonstrates this:**

```
namespace Application\Controller;

use Awareness\{TemplateRendererAwareInterface, TemplateRendererAwareTrait};
use Psr\Log\{LoggerAwareInterface, LoggerAwareTrait};
use Psr\Http\Server\RequestHandlerInterface;

abstract class Controller implements
    LoggerAwareInterface,
    TemplateRendererAwareInterface,
    RequestHandlerInterface
{
    // The container calls setLogger() and setTemplateRenderer() automatically.
    use LoggerAwareTrait,
        TemplateRendererAwareTrait;
}
```

**A controller using those injected services:**

```
class HomePageController extends Controller
{
    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        $this->logger->info('Displaying home page');

        return new HtmlResponse($this->templateRenderer->render('home'));
    }
}
```

**Why this is better than constructor injection for cross-cutting concerns:**

- **Clean Constructors:** Your `__construct` only contains what's strictly necessary for your business logic (like Repositories).
- **PSR-Ready:** Easily swap any implementation (Monolog → another PSR-3 logger, Plates → Twig).
- **Automatic Wiring:** No need to manually define how to inject the logger or template renderer into every controller.

🔌 Plug-and-Play Extensibility
-----------------------------

[](#-plug-and-play-extensibility)

Adding a new capability (e.g., PSR-6 Caching, PSR-14 Event Dispatching) follows a simple three-step pattern:

1. **Define your Provider:** Create a Service Provider that registers your service and hooks `afterResolve`.
2. **Implement &amp; Use:** Add the `AwareInterface` and `AwareTrait` to any class that needs the service.

**Example — wiring the logger into any `LoggerAwareInterface` class:**

```
class LoggingServiceProvider extends AbstractServiceProvider implements BootableServiceProviderInterface
{
    public function boot(): void
    {
        $this
            ->getContainer()
            ->afterResolve(
                LoggerAwareInterface::class,
                fn (LoggerAwareInterface $class) => $class->setLogger(
                    $this->getContainer()->get(Logger::class)->withName(get_class($class))
                )
            );
    }

    // register() binds LoggerInterface and Logger::class ...
}
```

Now any class implementing `LoggerAwareInterface` is automatically equipped with a logger named after the class itself — no constructor changes, no manual wiring.

🎨 Powerful Templating (via Mezzio)
----------------------------------

[](#-powerful-templating-via-mezzio)

Stein doesn't force a template engine on you. Thanks to the **Mezzio Template** bridge, you can swap engines with a single `composer require`:

- `composer require mezzio/mezzio-platesrenderer` ← included by default (Plates)
- `composer require mezzio/mezzio-twigrenderer` (for Twig)
- `composer require mezzio/mezzio-laminasviewrenderer` (for Laminas View)

Just update `ViewServiceProvider` to bind the new renderer — nothing else changes.

**Usage in a Controller:**

```
public function handle(ServerRequestInterface $request): ResponseInterface
{
    return new HtmlResponse($this->templateRenderer->render('profile', ['name' => 'Frankie']));
}
```

Templates live in `storage/views/` and support layouts out of the box:

```
// storage/views/home.php
