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

ActiveLibrary[Framework](/categories/framework)

jschreuder/middle-skeleton
==========================

A Middle framework setup for a new project.

17PHP

Since Oct 19Pushed 8mo ago1 watchersCompare

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

READMEChangelogDependenciesVersions (1)Used By (0)

Middle Framework Skeleton
=========================

[](#middle-framework-skeleton)

A foundational setup for [Middle Framework](https://github.com/jschreuder/Middle) that demonstrates core architectural patterns while remaining minimal and extensible.

What This Skeleton Provides
---------------------------

[](#what-this-skeleton-provides)

This skeleton demonstrates Middle's core philosophy in practice:

**🔍 Explicit Architecture**: All dependencies and middleware are clearly visible in the `ServiceContainer` and routing setup. No magic, no surprises.

**🔧 Replaceable Components**: Uses proper interfaces throughout (`ControllerInterface`, `RouterInterface`) so you can swap implementations without touching other code.

**🛡️ Safe to Extend**: The middleware pipeline pattern and interface-driven design let you add features confidently without breaking existing functionality.

**🧪 Testing-First**: Comprehensive test setup with both unit and feature tests using [Pest PHP](https://pestphp.com/), demonstrating how Middle's explicit design makes testing straightforward.

### Included Components

[](#included-components)

- **HTTP Foundation**: [Laminas Diactoros](https://docs.laminas.dev/laminas-diactoros/) for PSR-7 HTTP messages with [Laminas HTTP HandlerRunner](https://docs.laminas.dev/laminas-httphandlerrunner/) for response emission
- **Logging**: [Monolog](https://seldaek.github.io/monolog/) with proper error handling integration
- **Routing**: [Symfony Router](https://symfony.com/doc/current/routing.html) with Middle's routing abstraction
- **Database Migrations**: [Phinx](https://phinx.org/) for database schema management
- **Console Commands**: [Symfony Console](https://symfony.com/doc/current/components/console.html) for CLI functionality
- **JSON Support**: Automatic JSON request body parsing middleware
- **Testing**: [Pest PHP](https://pestphp.com/) with [Mockery](http://docs.mockery.io/) for elegant testing

### Demonstrated Patterns

[](#demonstrated-patterns)

- **Middleware Pipeline**: Explicit middleware stack construction in `ServiceContainer::getApp()`
- **Dependency Injection**: Interface-driven design with explicit container configuration
- **Error Handling**: Dedicated controllers for 404 and 500 responses
- **Routing Organization**: Clean separation using `RoutingProviderInterface`
- **Console Integration**: Command registration and service injection
- **Testing Strategy**: Both unit and feature test examples

### Component Selection Philosophy

[](#component-selection-philosophy)

This skeleton deliberately combines components from different framework ecosystems. These choices are both based on their individual quality and demonstrate Middle's core principle: **choose the best tool for each job, regardless of origin**.

Notice the different integration patterns:

- **Interface adaptation**: Symfony Router wrapped behind `RouterInterface`, routing is core application behavior that might need different implementations (some being basic, others very complex). Monolog is used through the standard PSR-3 `LoggerInterface`, this serves same purpose as custom interfaces by abstracting away the concrete implementation.
- **Development tools**: Phinx for migrations and Pest for testing remain unwrapped—they're not part of your application runtime, they're development utilities that don't need abstraction.

This isn't about mixing components randomly, it's about making thoughtful choices based on technical merit. Anything used in your application's domain code is ideally abstracted away for readability and replaceability, but with development tools this would be nearly impossible and not worth your time. When you have clear interfaces where needed and direct usage where appropriate, the source ecosystem becomes irrelevant. Your application remains coherent because you've defined the boundaries, not because everything comes from one vendor.

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

[](#installation)

```
composer create-project jschreuder/middle-skeleton myapp dev-master
cd myapp
```

Set up the environment:

```
# Make logs directory writable
chmod 0755 var/logs

# Configure environment
cp config/env.php.dist config/env.php
cp config/dev.php.dist config/dev.php

# Edit config/dev.php with your database credentials and other settings
```

Quick Start
-----------

[](#quick-start)

**Test the Console Application:**

```
./console middle:example MyName
```

**Test the Web Application:**

```
# Start the development server
./console middle:webserver

# Or manually:
cd web && php -S localhost:8080
```

Visit `http://localhost:8080` - you should see a JSON "Hello World" message.

**Run the Test Suite:**

```
./vendor/bin/pest
```

Application Structure
---------------------

[](#application-structure)

```
config/          # Environment-specific configuration
src/
├── Command/     # Console commands
├── Controller/  # HTTP request handlers
└── Service/     # Business logic services
tests/
├── Feature/     # Integration tests
└── Unit/        # Isolated unit tests
web/             # Web server document root

```

Key Files
---------

[](#key-files)

- **`config/app_init.php`**: Application bootstrapping, loads configuration and sets up dependency injection
- **`web/index.php`**: Web application entry point, processes HTTP requests through middleware pipeline
- **`console`**: CLI application entry point for running commands
- **`src/ServiceContainer.php`**: Dependency injection container with explicit service definitions
- **`src/GeneralRoutingProvider.php`**: Route definitions using Middle's routing abstraction

Design Philosophy: Composition Over Framework Lock-in
-----------------------------------------------------

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

Middle isn't designed to replace other frameworks - it's designed to let you **compose proven components on your terms**. Rather than accepting one framework's architectural decisions, you create your own interfaces and adapt mature libraries to fit your domain.

```
// Your domain interface - exactly what your application needs
interface UserValidatorInterface
{
    public function validateCreateUser(array $data): ValidationResult;
    public function validateUpdateUser(int $userId, array $data): ValidationResult;
}

// Adapter that wraps Symfony's complexity behind your interface
class SymfonyUserValidator implements UserValidatorInterface
{
    public function __construct(private ValidatorInterface $symfonyValidator) {}

    public function validateCreateUser(array $data): ValidationResult
    {
        // Transform your domain needs into Symfony validator calls
        $constraints = new Assert\Collection([
            'email' => new Assert\Email(),
            'name' => new Assert\NotBlank(),
        ]);

        $violations = $this->symfonyValidator->validate($data, $constraints);
        return ValidationResult::fromSymfonyViolations($violations);
    }
}

// Your application uses YOUR interface, not Symfony's
class CreateUserController implements ControllerInterface
{
    public function __construct(private UserValidatorInterface $validator) {}
}
```

This approach gives you:

- **Library Independence**: Swap Symfony Validator for another library by implementing your interface
- **Minimal Framework Risk**: Middle's core is tiny and stable - security updates happen in your chosen components, not the framework
- **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 battle-tested components (for example the included Symfony Routing and Laminas Diactoros) with complete architectural control.

Extending the Application
-------------------------

[](#extending-the-application)

### Adding Routes

[](#adding-routes)

Routes are organized using routing providers:

```
// In GeneralRoutingProvider::registerRoutes()
$router->get('users.list', '/users', function () {
    return new ListUsersController($this->container->getUserRepository());
});

$router->post('users.create', '/users', function () {
    return new CreateUserController($this->container->getUserRepository());
});
```

### Adding Middleware

[](#adding-middleware)

Middleware is added explicitly to the application stack:

```
// In ServiceContainer::getApp()
return new ApplicationStack(
    new ControllerRunner(),
    new JsonRequestParserMiddleware(),
    new SessionMiddleware($this->getSessionProcessor()), // New middleware
    new RoutingMiddleware($this->getAppRouter(), $this->get404Handler()),
    new ErrorHandlerMiddleware($this->getLogger(), $this->get500Handler())
);
```

### Scaling Your Application

[](#scaling-your-application)

As your application grows, Middle's explicit design supports modular organization at multiple levels:

**For Beginners**: Start with concrete implementations, extract interfaces when patterns emerge
**For Teams**: Build organizational base classes with your conventions baked in
**For Scale**: Split into modules using service provider traits

**Within a Single Application:**You can organize services using traits to keep the ServiceContainer manageable:

```
trait DatabaseServices
{
    public function getUserRepository(): UserRepository
    {
        return new UserRepository($this->getDb());
    }

    public function getOrderRepository(): OrderRepository
    {
        return new OrderRepository($this->getDb());
    }
}

trait ViewServices
{
    public function getTwigRenderer(): TwigRenderer
    {
        return new TwigRenderer($this->getTwig(), $this->getResponseFactory());
    }
}

class ServiceContainer
{
    use ConfigTrait, DatabaseServices, ViewServices;

    // Core services remain here
    public function getApp(): ApplicationStack { ... }
    public function getLogger(): LoggerInterface { ... }
}
```

**Across Multiple Modules:**For larger applications, consider splitting functionality into separate modules, each with their own repository. Each module can provide a service provider trait that integrates cleanly with your main application:

```
// In your user-management module repository
trait UserModuleServices
{
    public function getUserRepository(): UserRepository { ... }
    public function getUserController(): UserController { ... }
    public function getUserValidator(): UserValidator { ... }
}

// In your billing module repository
trait BillingModuleServices
{
    public function getInvoiceRepository(): InvoiceRepository { ... }
    public function getPaymentProcessor(): PaymentProcessor { ... }
    public function getBillingController(): BillingController { ... }
}

// In your main application's ServiceContainer
class ServiceContainer
{
    use ConfigTrait, UserModuleServices, BillingModuleServices;

    // Application-level services
    public function getApp(): ApplicationStack { ... }
}
```

This approach maintains Middle's explicitness while being grounded in core PHP language concepts (traits, interfaces, namespaces), allowing you to:

- Develop and test modules independently
- Share modules across applications
- Keep domain boundaries clear
- Scale team development across module ownership

Each module remains a focused, manageable unit while the main application composes them explicitly through the service container traits - no framework magic, just PHP.

### Adding Advanced Features

[](#adding-advanced-features)

This skeleton provides a foundation - Middle Framework includes many additional features not configured here:

- **Request Validation &amp; Filtering**: Automatic request processing using `RequestValidatorInterface` and `RequestFilterInterface`
- **Session Management**: Built-in session middleware with pluggable storage backends
- **Template Rendering**: View layer with Twig integration and response rendering
- **Advanced Error Handling**: HTTP-aware exceptions and custom error pages

For detailed examples and documentation of these features, see the [Middle Framework repository](https://github.com/jschreuder/Middle).

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

[](#why-middle-framework)

Middle Framework prioritizes **long-term maintainability** over rapid prototyping. It's designed for applications that:

- Will be maintained by teams over time
- Need explicit, traceable request flow
- Require confidence when refactoring or extending functionality
- Benefit from interface-driven, testable architecture

If you prefer convention over configuration or need to prototype very quickly, Middle might not be the right choice. But if you want to build applications that remain pleasant to work with as they grow, Middle provides the foundation you need.

---

**Middle Framework: Explicit. Replaceable. Safe.**

###  Health Score

18

—

LowBetter than 8% of packages

Maintenance43

Moderate activity, may be stable

Popularity6

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity13

Early-stage or recently created project

 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.

### Community

Maintainers

![](https://www.gravatar.com/avatar/8179afd54be86d19329fb1eea153ded1f5fd50bfab33577e7abd1ca5039d7ef7?d=identicon)[jschreuder](/maintainers/jschreuder)

---

Top Contributors

[![jschreuder](https://avatars.githubusercontent.com/u/69116?v=4)](https://github.com/jschreuder "jschreuder (20 commits)")

### Embed Badge

![Health badge](/badges/jschreuder-middle-skeleton/health.svg)

```
[![Health](https://phpackages.com/badges/jschreuder-middle-skeleton/health.svg)](https://phpackages.com/packages/jschreuder-middle-skeleton)
```

###  Alternatives

[laravel/socialite

Laravel wrapper around OAuth 1 &amp; OAuth 2 libraries.

5.7k104.3M841](/packages/laravel-socialite)[laravel/dusk

Laravel Dusk provides simple end-to-end testing and browser automation.

1.9k38.6M289](/packages/laravel-dusk)[nineinchnick/edatatables

Grid widget for the Yii Framework, wrapper for the DataTables jQuery plugin

173.2k](/packages/nineinchnick-edatatables)[link-cloud/fast-hyperf

LinkCloud Fast Hyperf

241.2k1](/packages/link-cloud-fast-hyperf)

PHPackages © 2026

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