Dependency injection
Constructor injection, custom providers, and request/transient scopes.
#[Injectable]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
#[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
Request lifecycle
01 / 11An HTTP request arrives as a PSR-7 ServerRequest.
Batteries included
Official packages on PSR interfaces. Standalone, with no other framework underneath. Install what you use.
Constructor injection, custom providers, and request/transient scopes.
#[Injectable]config/*.php files and .env, read through a typed ConfigService.
new ConfigModule(isGlobal: true)Attribute rules on DTOs, with one error shape: messages grouped per field.
#[IsString, Length(min: 2)]Files are DTO fields, checked by their real content, size and count.
#[IsImage(maxSize: '5MB')]Expose, exclude and group fields on the way out.
#[Expose(groups: ['admin'])]Entities, repositories, relations, a query builder and migrations on PostgreSQL, MySQL, MariaDB and SQLite.
#[Entity('cats')]Local, public and S3 disks, or your own driver, behind one API.
$file->store('avatars', 's3')A PSR-16 cache interceptor and a throttler guard.
#[Throttle(10, 60)]Pluggable strategies, AuthGuard and #[CurrentUser].
#[UseGuards(new AuthGuard('jwt'))]Generates a Swagger UI from your controllers and DTOs, file uploads included.
#[ApiTags('cats')]An event emitter with wildcard listeners.
#[OnEvent('post.*')]Cron, interval and timeout jobs.
#[Cron('0 * * * *')]Background jobs with processors and a worker command.
#[Processor('mail')]Isolated testing modules and end-to-end HTTP requests, uploads included.
Test::createTestingModule([...])FrankenPHP keeps the app and its database connections in memory.
php assistant start:prodOne feature, four files
#[Module(
imports: [new OrmFeatureModule(Cat::class)],
controllers: [CatsController::class],
providers: [CatsService::class],
)]
final class CatsModule {}
final class CreateCatDto
{
#[IsString, Length(min: 2, max: 50), ApiProperty(example: 'Tom')]
public string $name;
#[IsInt, Min(0), IsOptional]
public ?int $age = null;
#[IsOptional, IsImage(maxSize: '2MB')]
public ?File $photo = null;
}
#[Controller('cats')]
#[ApiTags('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);
}
}
#[Injectable]
final class CatsService
{
public function __construct(
#[InjectRepository(Cat::class)] private readonly Repository $cats,
private readonly EventEmitter $events,
) {}
public function create(CreateCatDto $dto): Cat
{
$cat = $this->cats->save($this->cats->create(['name' => $dto->name, 'age' => $dto->age]));
$this->events->emit('cat.created', $cat);
return $cat;
}
}
CLI
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
$ 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
Speaks 6 PSR standards
A blog API, start to finish: entities, JWT auth, validation, uploads, queues, OpenAPI and tests.