spatial / core
Spatial API Core Contains Attributes
Installs: 452
Dependents: 3
Suggesters: 0
Security: 0
Stars: 3
Watchers: 2
Forks: 0
Open Issues: 0
pkg:composer/spatial/core
Requires
- php: ^8.2
- dflydev/fig-cookies: ^3.0
- guzzlehttp/guzzle: ^7.10
- http-interop/http-factory-guzzle: ^1.0
- monolog/monolog: ^3.0
- open-telemetry/exporter-otlp: ^1.0
- open-telemetry/opentelemetry: ^1.0
- open-telemetry/opentelemetry-logger-monolog: ^1.0
- open-telemetry/sdk: ^1.0
- php-di/php-di: ^7.0
- php-http/httplug: 2.x-dev
- psr/http-factory: ^1.0
- psr/http-message: ^2.0
- spatial/mediator: ^4.0.0-RC3.0
Requires (Dev)
- ext-json: *
- openswoole/ide-helper: ~22.1.5
- dev-main
- 4.0.0-RC3.2
- 4.0.0-RC3.1
- 4.0.0-RC3.0
- 4.0.0-RC2.8
- 4.0.0-RC2.7
- 4.0.0-RC2.6
- 4.0.0-RC2.5
- 4.0.0-RC2.4
- 4.0.0-RC2.2
- 4.0.0-RC2.1
- 4.0.0-RC2
- 4.0.0-RC1
- 4.0.0-alpha3
- 4.0.0-alpha2
- 4.0.0-alpha1
- 3.4.3
- 3.4.2
- 3.4.1
- 3.4.0
- 3.3.3
- 3.3.1
- 3.3.0
- 3.2.23
- 3.2.22
- 3.2.21
- 3.2.13
- 3.2.12
- 3.2.11
- 3.2.2
- 3.2.1
- 3.2.0
- 3.1.93
- 3.1.92
- 3.1.91
- 3.1.9.1
- 3.1.9
- 3.1.8
- 3.1.7
- 3.1.6
- 3.1.5
- 3.1.4
- 3.1.3
- 3.1.2
- 3.1.1
- 3.1.0
- 3.0.5
- 3.0.4
- 3.0.3
- 3.0.2
- 3.0.1
- 3.0.0
- 3.0.0-alpha
- v2.x-dev
- 2.0.1
- 2.0.0
- 0.0.2
- 0.0.1
This package is auto-updated.
Last update: 2025-12-14 14:27:07 UTC
README
The core package of the Spatial PHP Framework - a modern, attribute-based web framework for building high-performance APIs with PHP 8.2+ and OpenSwoole.
Features
- 🚀 High Performance - Built for OpenSwoole async server
- 🎯 Attribute-Based Routing - Define routes with PHP 8 attributes
- 📦 Modular Architecture - Angular-inspired module system
- 🔄 CQRS Ready - Built-in Mediator pattern support
- ✅ Validation Attributes - Declarative DTO validation
- 🛠️ CLI Tools - Code generators for rapid development
- 🔐 Authorization - Attribute-based auth guards
- 📝 PSR-7 Compliant - Standard HTTP message interfaces
Installation
composer require spatial/core
Quick Start
1. Create a Controller
<?php use Spatial\Core\Attributes\ApiController; use Spatial\Core\Attributes\Route; use Spatial\Core\ControllerBase; use Spatial\Common\HttpAttributes\HttpGet; use Spatial\Common\HttpAttributes\HttpPost; use Spatial\Common\BindSourceAttributes\FromBody; use Psr\Http\Message\ResponseInterface; #[ApiController] #[Route('api/[controller]')] class UsersController extends ControllerBase { #[HttpGet] public function index(): ResponseInterface { return $this->ok(['users' => []]); } #[HttpGet('{id:int}')] public function show(int $id): ResponseInterface { return $this->ok(['id' => $id]); } #[HttpPost] public function create(#[FromBody] CreateUserDto $dto): ResponseInterface { if ($error = $this->validate($dto)) { return $error; // Returns 400 with validation errors } return $this->created($dto); } }
2. Create a DTO with Validation
<?php use Spatial\Common\ValidationAttributes\Required; use Spatial\Common\ValidationAttributes\Email; use Spatial\Common\ValidationAttributes\MinLength; use Spatial\Common\ValidationAttributes\MaxLength; class CreateUserDto { #[Required] #[Email] public string $email; #[Required] #[MinLength(8)] public string $password; #[MaxLength(100)] public ?string $name = null; }
3. Create a Module
<?php use Spatial\Core\Attributes\ApiModule; #[ApiModule( imports: [], declarations: [UsersController::class], providers: [UserService::class] )] class AppModule { public function configure(ApplicationBuilderInterface $app): void { $app->useRouting(); $app->useEndpoints(fn($endpoints) => $endpoints->mapControllers()); } }
4. Bootstrap the Application
<?php // public/index.php require __DIR__ . '/../vendor/autoload.php'; $app = new \Spatial\Core\App(); $app->boot(\Presentation\AppModule::class);
Core Components
Attributes
| Attribute | Target | Description |
|---|---|---|
#[ApiModule] |
Class | Defines an application module |
#[ApiController] |
Class | Marks a class as an API controller |
#[Route('path')] |
Class/Method | URL route template |
#[HttpGet], #[HttpPost], etc. |
Method | HTTP verb mapping |
#[FromBody], #[FromQuery] |
Parameter | Request data binding |
#[Authorize] |
Class/Method | Authorization requirement |
Response Helpers
ControllerBase provides convenient response methods:
$this->ok($data); // 200 OK $this->created($data, $location); // 201 Created $this->noContent(); // 204 No Content $this->badRequest($errors); // 400 Bad Request $this->notFound($message); // 404 Not Found $this->unauthorized($message); // 401 Unauthorized $this->forbidden($message); // 403 Forbidden $this->json($data, $status); // Custom JSON response $this->validate($dto); // Validate DTO, returns error or null
Validation Attributes
Declarative validation for DTOs:
| Attribute | Description | Example |
|---|---|---|
#[Required] |
Not null/empty | #[Required] |
#[Email] |
Valid email format | #[Email] |
#[MinLength(n)] |
Minimum length | #[MinLength(8)] |
#[MaxLength(n)] |
Maximum length | #[MaxLength(255)] |
#[Range(min, max)] |
Numeric bounds | #[Range(0, 100)] |
#[Pattern(regex)] |
Regex match | #[Pattern('/^\d+$/')] |
Usage in Controller:
#[HttpPost] public function create(#[FromBody] CreateUserDto $dto): ResponseInterface { if ($error = $this->validate($dto)) { return $error; // Automatic 400 response with errors } // DTO is valid, continue... }
Route Caching
In production, routes are automatically cached for faster boot times:
// Cache is stored in: var/cache/routes.cache.php // Clear cache with CLI: php spatial cache:clear
Caster - JSON/Array to Object Hydration
The Caster class provides powerful JSON/array to PHP object hydration with type safety.
Basic Usage
use Spatial\Common\Helper\Caster; // Cast JSON string to object $dto = Caster::cast(CreateUserDto::class, '{"email":"user@example.com"}'); // Cast array to object $dto = Caster::cast(CreateUserDto::class, ['email' => 'user@example.com']); // Cast array of items $users = Caster::castMany(UserDto::class, $usersArray);
With Validation
// Enable auto-validation globally Caster::configure(['autoValidate' => true]); $dto = Caster::cast(CreateUserDto::class, $data); // Throws if invalid // Validate on specific cast $dto = Caster::cast(CreateUserDto::class, $data, validate: true); // Try cast (returns result instead of throwing) $result = Caster::tryCast(CreateUserDto::class, $data); if (!$result->isValid()) { return $this->badRequest($result->getErrors()); } $dto = $result->object;
Property Name Mapping
Map between JSON naming conventions and PHP property names:
// JSON uses snake_case, PHP uses camelCase Caster::configure(['nameStrategy' => 'snake_case']); // {"user_name": "John", "created_at": "2024-01-01"} // → $dto->userName, $dto->createdAt $dto = Caster::cast(UserDto::class, $jsonString);
Supported Types
| Type | Description |
|---|---|
| Scalars | int, float, string, bool, array |
| Objects | Nested object hydration (recursive) |
| Enums | Backed enums and unit enums |
| DateTime | DateTime, DateTimeImmutable (from string or timestamp) |
| Nullable | ?Type and union types with null |
| Union Types | int|string (tries each type) |
DateTime Handling
class EventDto { public DateTime $startDate; public DateTimeImmutable $createdAt; } // From ISO string $dto = Caster::cast(EventDto::class, [ 'startDate' => '2024-12-25T10:00:00Z', 'createdAt' => '2024-01-01' ]); // From Unix timestamp $dto = Caster::cast(EventDto::class, [ 'startDate' => 1703505600, 'createdAt' => 1704067200 ]);
Enum Handling
enum Status: string { case Active = 'active'; case Inactive = 'inactive'; } class ProductDto { public Status $status; } // Cast backed enum $dto = Caster::cast(ProductDto::class, ['status' => 'active']); echo $dto->status->value; // 'active'
CLI Tool
Spatial includes a CLI tool for code generation:
# Show help php spatial --help # Generate a CRUD controller php spatial make:controller Product # Generate a CQRS command with handler php spatial make:command CreateOrder --domain=Orders # Generate a CQRS query with handler php spatial make:query GetProducts --domain=Products # Generate an API module php spatial make:module IdentityModule # Generate a DTO with validation php spatial make:dto CreateProductDto --domain=Products # List all registered routes php spatial route:list # Clear cached routes php spatial cache:clear
Generated Code Examples
Controller (make:controller User):
#[ApiController] #[Route('[area]/[controller]')] class UserController extends ControllerBase { #[HttpGet] public function index(): ResponseInterface { ... } #[HttpGet('{id:int}')] public function show(int $id): ResponseInterface { ... } #[HttpPost] public function create(#[FromBody] array $data): ResponseInterface { ... } #[HttpPut('{id:int}')] public function update(int $id, #[FromBody] array $data): ResponseInterface { ... } #[HttpDelete('{id:int}')] public function delete(int $id): ResponseInterface { ... } }
Architecture
Package Structure
spatial-core/src/
├── core/ # Core framework
│ ├── App.php # Application bootstrap
│ ├── ControllerBase.php # Base controller class
│ ├── ConfigurationLoader.php # YAML config loading
│ ├── ModuleRegistrar.php # Module registration
│ ├── RouteTableBuilder.php # Route table construction
│ ├── RouteTableRenderer.php # API docs generation
│ ├── RouteCache.php # Production route caching
│ ├── AppHandler.php # PSR-15 request handler
│ ├── ApplicationBuilder.php # App configuration
│ ├── Attributes/ # Core attributes
│ └── Interfaces/ # Interfaces
│
├── common/ # Shared utilities
│ ├── HttpAttributes/ # HTTP verb attributes
│ ├── BindSourceAttributes/ # Parameter binding
│ ├── ValidationAttributes/ # Validation rules
│ ├── Helper/ # Utilities
│ │ └── Caster.php # JSON/Array hydration
│ └── Middleware/ # Built-in middlewares
│
├── router/ # Routing system
│ ├── RouterModule.php # Route resolution
│ └── RouteBuilder.php # Route configuration
│
├── swoole/ # OpenSwoole integration
│ └── BridgeManager.php # Swoole ↔ PSR-7 bridge
│
├── console/ # CLI tools
│ ├── Application.php # CLI application
│ ├── CommandInterface.php # Command interface
│ ├── AbstractCommand.php # Base command
│ └── Commands/ # Built-in commands
│
└── telemetry/ # OpenTelemetry integration
Request Flow
Client Request
↓
OpenSwoole Server
↓
BridgeManager (Swoole → PSR-7)
↓
App.process()
↓
MiddlewareProcessor (Auth, CORS, etc.)
↓
AppHandler (Route matching)
↓
RouterModule (Controller invocation)
↓
Caster (JSON → DTO hydration)
↓
Controller → Mediator → Handler
↓
ResponseInterface
↓
BridgeManager (PSR-7 → Swoole)
↓
Client Response
Configuration
YAML Configuration
# config/packages/framework.yaml enableProdMode: false cors: allowedOrigins: ['*'] allowedMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'] allowedHeaders: ['Content-Type', 'Authorization']
Environment Variables
Use %env(VAR_NAME)% syntax in YAML:
# config/packages/doctrine.yaml database: host: '%env(DB_HOST)%' user: '%env(DB_USER)%' password: '%env(DB_PASSWORD)%'
Advanced Features
Authorization Guards
use Spatial\Core\Attributes\Authorize; #[ApiController] #[Authorize(JwtGuard::class)] // Controller-level auth class AdminController extends ControllerBase { #[HttpGet] #[Authorize(AdminGuard::class)] // Method-level auth public function dashboard(): ResponseInterface { return $this->ok(['admin' => true]); } }
Custom Validation Attributes
use Spatial\Common\ValidationAttributes\ValidationAttribute; use Spatial\Common\ValidationAttributes\ValidationResult; #[Attribute(Attribute::TARGET_PROPERTY)] class UniqueEmail extends ValidationAttribute { public function validate(mixed $value, string $propertyName): ValidationResult { // Check database for uniqueness $exists = $this->checkEmailExists($value); if ($exists) { return ValidationResult::failure( "Email already exists", $propertyName ); } return ValidationResult::success(); } protected function getDefaultMessage(string $propertyName): string { return "The {$propertyName} must be unique."; } }
CQRS with Mediator
// In controller #[HttpGet] public function index(): ResponseInterface { return $this->mediator->process(new GetUsersQuery()); } #[HttpPost] public function create(#[FromBody] CreateUserDto $dto): ResponseInterface { return $this->mediator->process(new CreateUserCommand($dto)); }
Requirements
- PHP 8.2+
- OpenSwoole extension
- Composer
Dependencies
php-di/php-di- Dependency injectionguzzlehttp/psr7- PSR-7 implementationsymfony/yaml- YAML parsing
License
MIT License - see LICENSE for details.
Contributing
Contributions are welcome! Please read the contributing guidelines before submitting PRs.
Credits
Created by the Spatial Framework Team.