Modular PHP, wired by attributes.

Modules, dependency injection, guards, pipes, interceptors and an ORM, declared with native PHP 8 attributes. Structure that scales, in the language you already ship.

$composer create-project modulaphp/starter my-api
src/Cats/CatsController.php
#[Controller('cats')]
final class CatsController
{
    public function __construct(private readonly CatsService $cats) {}

    #[Get('{id}')]
    public function findOne(#[Param('id')] int $id): Cat
    {
        return $this->cats->findOne($id);
    }

    #[Post]
    #[UseGuards(new AuthGuard('jwt'))]
    public function create(#[Body] CreateCatDto $dto): Cat
    {
        return $this->cats->create($dto);
    }
}

The building blocks

ModulesControllersProvidersGuards PipesInterceptorsException filtersMiddleware Lifecycle hooksDynamic modulesTesting moduleCLI

Request lifecycle

Every request takes the same path. Always in this order.

01 / 11An HTTP request arrives as a PSR-7 ServerRequest.

Batteries included

19 packages. One attribute away.

Official packages on PSR interfaces. Standalone, with no other framework underneath. Install what you use.

01

Core

Dependency injection

Constructor injection, custom providers, and request/transient scopes.

#[Injectable]

Config

config/*.php files and .env, read through a typed ConfigService.

new ConfigModule(isGlobal: true)
02

Input & output

Validation

Attribute rules on DTOs, with one error shape: messages grouped per field.

#[IsString, Length(min: 2)]

File uploads

Files are DTO fields, checked by their real content, size and count.

#[IsImage(maxSize: '5MB')]

Serialization

Expose, exclude and group fields on the way out.

#[Expose(groups: ['admin'])]
03

Data & files

ORM

Entities, repositories, relations, a query builder and migrations on PostgreSQL, MySQL, MariaDB and SQLite.

#[Entity('cats')]

File storage

Local, public and S3 disks, or your own driver, behind one API.

$file->store('avatars', 's3')

Cache & rate limiting

A PSR-16 cache interceptor and a throttler guard.

#[Throttle(10, 60)]
04

Security & API

Auth & JWT

Pluggable strategies, AuthGuard and #[CurrentUser].

#[UseGuards(new AuthGuard('jwt'))]

OpenAPI

Generates a Swagger UI from your controllers and DTOs, file uploads included.

#[ApiTags('cats')]
05

Background work

Events

An event emitter with wildcard listeners.

#[OnEvent('post.*')]

Task scheduling

Cron, interval and timeout jobs.

#[Cron('0 * * * *')]

Queues

Background jobs with processors and a worker command.

#[Processor('mail')]
06

Quality & production

Testing

Isolated testing modules and end-to-end HTTP requests, uploads included.

Test::createTestingModule([...])

Worker mode

FrankenPHP keeps the app and its database connections in memory.

php assistant start:prod

One feature, four files

A complete feature in one screen.

src/Cats/CatsModule.phpphp
#[Module(
    imports: [new OrmFeatureModule(Cat::class)],
    controllers: [CatsController::class],
    providers: [CatsService::class],
)]
final class CatsModule {}

CLI

Generate a whole resource.

One command writes the module, controller, service, DTOs, entity and test, then registers the module in AppModule.

start:prodroutesmigratemigrate:generatedb:seedadd config:publishstorage:linkqueue:workschedule:runmake:commandinfo
~/projects
$ modula new my-api
$ cd my-api
$ php assistant g resource cats --orm
CREATE src/Cats/CatsModule.php
CREATE src/Cats/CatsController.php
CREATE src/Cats/CatsService.php
CREATE src/Cats/Dto/CreateCatDto.php
CREATE src/Cats/Dto/UpdateCatDto.php
CREATE src/Cats/Entities/Cat.php
CREATE tests/Cats/CatsControllerTest.php
UPDATE src/AppModule.php
$ php assistant start

Runs anywhere

FrankenPHP workerPHP-FPMPHP built-in serverRoadRunner workerDocker

Speaks 6 PSR standards

PSR-7 HTTP messagesPSR-11 ContainerPSR-15 Middleware PSR-3 LoggerPSR-16 CachePSR-14 Events

Build your first ModulaPHP API in 10 minutes.

A blog API, start to finish: entities, JWT auth, validation, uploads, queues, OpenAPI and tests.