PHPackages                             jschreuder/middle - 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. jschreuder/middle

ActiveLibrary[Framework](/categories/framework)

jschreuder/middle
=================

Middleware based micro-framework build on components by everyone else

3.0.0(8mo ago)5659MITPHPPHP &gt;=8.3CI passing

Since Sep 11Pushed 7mo ago1 watchersCompare

[ Source](https://github.com/jschreuder/Middle)[ Packagist](https://packagist.org/packages/jschreuder/middle)[ RSS](/packages/jschreuder-middle/feed)WikiDiscussions master Synced 2d ago

READMEChangelog (10)Dependencies (10)Versions (20)Used By (0)

Middle Framework
================

[](#middle-framework)

[![Build](https://github.com/jschreuder/middle/actions/workflows/ci.yml/badge.svg)](https://github.com/jschreuder/middle/actions/workflows/ci.yml/badge.svg)[![Security Rating](https://camo.githubusercontent.com/881584c54080340affb4005f6e7fcb4933ceeda4bc2e29309258b9c98891fb75/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d6a7363687265756465725f4d6964646c65266d65747269633d73656375726974795f726174696e67)](https://sonarcloud.io/dashboard?id=jschreuder_Middle)[![Reliability Rating](https://camo.githubusercontent.com/0aeec1eef348d5b14530521616fbce65a60a7b1e2eb50aabb3f4267a3a1b63c7/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d6a7363687265756465725f4d6964646c65266d65747269633d72656c696162696c6974795f726174696e67)](https://sonarcloud.io/dashboard?id=jschreuder_Middle)[![Maintainability Rating](https://camo.githubusercontent.com/c5743b53cd7b8f1ffc92f2bd7ab734fcb0630a7ea42687660fb1bc4e9db5b710/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d6a7363687265756465725f4d6964646c65266d65747269633d7371616c655f726174696e67)](https://sonarcloud.io/dashboard?id=jschreuder_Middle)[![Coverage](https://camo.githubusercontent.com/7b87b9b103f795adf45f26866890205b6408ba491fd3b8981fad769037b4ad3d/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d6a7363687265756465725f4d6964646c65266d65747269633d636f766572616765)](https://sonarcloud.io/dashboard?id=jschreuder_Middle)

**A micro-framework built around one simple principle: everything should be explicit, replaceable, and safe to change.**

Middle takes a different approach to web application architecture. Instead of magic and conventions, it provides clear interfaces and explicit composition. You can use it to build your organization's perfect framework, not be forced to fit into someone else's choices. Instead of hidden coupling, it enforces clean boundaries. Instead of "getting started quickly," it optimizes for "maintaining confidently."

Why Middle?
-----------

[](#why-middle)

### 🔍 **No Magic, No Surprises**

[](#-no-magic-no-surprises)

Every dependency is explicit. Every behavior is visible. No hidden auto-wiring, no surprise side effects, no debugging mysterious framework behavior at 3 AM.

### 🔧 **Everything is Replaceable**

[](#-everything-is-replaceable)

Don't like how routing works? Swap the router. Need custom session handling? Implement `SessionProcessorInterface`. Every component is designed to be replaced without touching anything else.

### 🛡️ **Safe to Change and Extend**

[](#️-safe-to-change-and-extend)

The architecture prevents accidental coupling. You can modify any part of your application with confidence that you're not breaking something unexpected elsewhere.

### 🧪 **Built for Testing**

[](#-built-for-testing)

Interface-driven design means every component can be easily mocked, tested in isolation, and verified independently. No complex test setup, no framework mocking libraries needed.

### How Middle Compares

[](#how-middle-compares)

Like other micro-frameworks, Middle is for composing applications. Unlike them, Middle nudges towards architectural boundaries through interfaces, making SOLID principles and domain-driven design the path of least resistance.

**Choose Middle when:** You want simplicity with architecture meant for SOLID and Domain Driven Development. **Choose Others when:** You disagree with the philosophy or convention- or configuration-driven frameworks.

Core Philosophy
---------------

[](#core-philosophy)

**Explicitness over Convenience**: Middle makes you write a bit more code upfront, but prevents the hidden complexity that accumulates in applications over time.

**Objects over Arrays**: Configuration, data, and behavior are represented by proper classes with clear contracts, not deeply nested arrays or magic properties.

**Immutability by Default**: Adding or removing middleware creates new application instances. No shared mutable state, no spooky action-at-a-distance.

**Standards Compliance**: Full PSR-1, PSR-2, PSR-3, PSR-4, PSR-7, PSR-15, and PSR-17 compliance. Not because we have to, but because standards represent solved problems.

**Minimal Attack Surface**: The framework core is tiny, reducing security vulnerabilities and update risks. Your chosen components update independently.

Security Through Architecture
-----------------------------

[](#security-through-architecture)

### 🔒 **Minimal Attack Surface**

[](#-minimal-attack-surface)

Middle Framework's core contains almost no security-sensitive code. The entire framework is essentially middleware orchestration—no input parsing, no template engines, no ORMs, no file handling. What can't be exploited, won't be exploited.

### 🛡️ **Security by Delegation**

[](#️-security-by-delegation)

Critical security functionality is handled by specialized, battle-tested libraries that you choose and control:

```
// Security-critical components are external and replaceable
$app = $app->withMiddleware(
    new RoutingMiddleware(
        new SymfonyRouter($baseUrl),          // Symfony's battle-tested routing
        $fallbackController
    )
);

$app = $app->withMiddleware(
    new SessionMiddleware(
        new LaminasSessionProcessor()         // Laminas's proven session handling
    )
);
```

### 🔄 **Future-Proof Security Updates**

[](#-future-proof-security-updates)

When security vulnerabilities are discovered, updates happen in the concrete libraries—not the framework. Your application code remains unchanged:

- **Symfony Router vulnerability?** → `composer update symfony/routing`
- **Session handling issue?** → `composer update laminas/laminas-session`
- **Template engine exploit?** → `composer update twig/twig`

No framework rewrites, no breaking changes, no security debt.

### **A Framework You'll Never Replace for Security**

[](#a-framework-youll-never-replace-for-security)

Traditional frameworks become security liabilities over time—their monolithic nature means security updates can break your application. Middle's architectural approach minimizes this problem. Security is handled by focused libraries that update independently, while your business logic remains protected behind stable interfaces.

**The result:** A framework with virtually no inherent security vulnerabilities and a security posture that improves over time as underlying libraries mature.

Composition Over Framework Lock-in
----------------------------------

[](#composition-over-framework-lock-in)

Middle doesn't compete with mature frameworks - it lets you **compose their proven components on your terms**. Instead of accepting a framework's architectural decisions, you define your own interfaces and adapt battle-tested libraries to fit your domain.

```
// Your domain interface - exactly what your application needs
interface UserRepositoryInterface
{
    public function findByEmail(string $email): ?User;
    public function save(User $user): void;
    public function findActiveUsers(): array;
}

// Adapter that wraps Doctrine behind your interface
class DoctrineUserRepository implements UserRepositoryInterface
{
    public function __construct(private EntityManagerInterface $em) {}

    public function findByEmail(string $email): ?User
    {
        return $this->em->getRepository(User::class)->findOneBy(['email' => $email]);
    }

    public function save(User $user): void
    {
        $this->em->persist($user);
        $this->em->flush();
    }

    public function findActiveUsers(): array
    {
        return $this->em->createQuery('SELECT u FROM User u WHERE u.active = true')
                         ->getResult();
    }
}

// Your controllers depend on YOUR interface, not Doctrine's
class UserController implements ControllerInterface
{
    public function __construct(private UserRepositoryInterface $repository) {}

    public function execute(ServerRequestInterface $request): ResponseInterface
    {
        // Clean, domain-focused code
        $users = $this->repository->findActiveUsers();
        // ...
    }
}
```

This approach delivers:

- **Library Independence**: Replace Doctrine with another ORM by implementing your interface
- **Domain Clarity**: Your interfaces reflect business needs, not library abstractions
- **Future-Proof Evolution**: Library updates only require adapter changes, not application rewrites
- **Focused Testing**: Mock exactly what your application needs, not complex library interfaces

You get mature, battle-tested components (Symfony Routing, Laminas Diactoros) with complete architectural control.

How It Works
------------

[](#how-it-works)

Middle is built around the **middleware pipeline pattern**. Your application is a stack of middleware that processes requests in LIFO (last in, first out) order:

```
$app = new ApplicationStack(
    new ControllerRunner()              // Executes the matched controller
);

$app = $app->withMiddleware(
    new RoutingMiddleware($router, $fallback)  // Matches routes, adds controller to request
);

$app = $app->withMiddleware(
    new SessionMiddleware($sessionProcessor)   // Adds session to request
);

$app = $app->withMiddleware(
    new ErrorHandlerMiddleware($logger, $errorHandler)  // Catches exceptions
);
```

Each middleware gets a chance to:

- Modify the incoming request
- Pass control to the next middleware
- Modify the outgoing response

Getting Started
---------------

[](#getting-started)

Check out the [Middle skeleton](https://github.com/jschreuder/Middle-skeleton) application for a complete example setup.

### Minimal Setup

[](#minimal-setup)

```
