monkeyscloud / monkeyslegion-skeleton
Starter project for the MonkeysLegion framework
Installs: 139
Dependents: 0
Suggesters: 0
Security: 0
Stars: 9
Watchers: 1
Forks: 1
Language:Hack
Type:project
pkg:composer/monkeyscloud/monkeyslegion-skeleton
Requires
- php: ^8.4
- laminas/laminas-diactoros: ^3.6
- monkeyscloud/monkeyslegion: ^1.0
- monolog/monolog: ^3.9
Requires (Dev)
- http-interop/http-factory-tests: ^2.2
- phpunit/phpunit: ^11.0
README
A production-ready starter for building web apps & APIs with the MonkeysLegion framework.
β¨ Features Overview
| Category | Features |
|---|---|
| HTTP Stack | PSR-7/15 compliant, middleware pipeline, SAPI emitter |
| Routing | Attribute-based v2, auto-discovery, constraints, caching |
| Dependency Injection | PSR-11 container with config-first definitions |
| Database | Native PDO MySQL 8.4, Query Builder, Micro-ORM |
| Authentication | JWT, RBAC, 2FA, OAuth, API keys |
| API Documentation | Live OpenAPI 3.1 & Swagger UI |
| Validation | DTO binding with attribute constraints |
| Rate Limiting | Sliding-window (IP + User buckets) |
| Templating | MLView with components, slots, caching |
| CLI | Migrations, cache, key-gen, scaffolding, Tinker REPL |
| Files | Multi-driver storage, image processing, chunked uploads |
| I18n | Full internationalization & localization support |
| Telemetry | Prometheus metrics, distributed tracing, PSR-3 logging |
| SMTP, Markdown templates, DKIM support | |
| Caching | Multiple drivers (File, Redis, Memcached) |
π Quick-start
composer create-project monkeyscloud/monkeyslegion-skeleton my-app cd my-app cp .env.example .env # configure DB, secrets composer install php ml key:generate composer serve # or php vendor/bin/dev-server open http://127.0.0.1:8000 # your first MonkeysLegion page
π Project Layout
my-app/
ββ app/
β ββ Controller/ # HTTP controllers (auto-scanned)
β ββ Dto/ # Request DTOs with validation attributes
β ββ Entity/ # DB entities
β ββ Auth/ # Authentication contracts & traits
ββ config/
β ββ app.php # DI definitions (services & middleware)
β ββ database.php # DSN + creds
β ββ *.mlc # key-value config (CORS, cache, auth,β¦)
ββ public/ # Web root (index.php, assets)
ββ resources/
β ββ views/ # MLView templates & components
ββ var/
β ββ cache/ # compiled templates, rate-limit buckets
β ββ migrations/ # auto-generated SQL
ββ database/
β ββ seeders/ # generated seeder stubs
ββ storage/ # file uploads, logs
ββ tests/ # PHPUnit integration/unit tests
β ββ IntegrationTestCase.php
ββ vendor/ # Composer deps
ββ bin/ # Dev helpers (ml, dev-server)
ββ phpunit.xml # PHPUnit config
ββ README.md
π¦ Package Ecosystem (Detailed)
MonkeysLegion is built as a modular ecosystem of packages. Below is comprehensive documentation for each package with examples.
π§ Core Framework
monkeyslegion (Meta-package)
Installs the complete MonkeysLegion stack with a single command.
composer require monkeyscloud/monkeyslegion
monkeyslegion-core
Core runtime providing kernel, events, and helpers.
monkeyslegion-di
Tiny PSR-11 compliant dependency injection container.
monkeyslegion-mlc
Production-ready .mlc configuration file parser with environment variable support.
Features:
- π Secure - Path traversal prevention, file permission checks
- β‘ Fast - File-based caching with automatic invalidation
- π― Type-Safe - Strong typing with getters (
getString(),getInt(), etc.)
MLC File Format:
# Comments start with #
app_name = "My Application"
port = 8080
debug = true
# Sections with nesting
database {
host = ${DB_HOST:localhost} # Env vars with defaults
port = 3306
credentials {
username = root
password = ${DB_PASSWORD}
}
}
# Arrays
allowed_origins = ["https://example.com", "https://app.example.com"]
Usage:
use MonkeysLegion\Mlc\Loader; use MonkeysLegion\Mlc\Parser; $loader = new Loader(new Parser(), '/path/to/config'); // Load and merge multiple files $config = $loader->load(['app', 'database', 'cache']); // Type-safe getters $port = $config->getInt('database.port', 3306); $debug = $config->getBool('app.debug', false); $hosts = $config->getArray('database.hosts', []); // Required values (throws if missing) $secret = $config->getRequired('app.secret');
π HTTP & Routing
monkeyslegion-http
PSR-7 HTTP message implementations and SAPI emitter.
monkeyslegion-router
Comprehensive HTTP router with attribute-based routing, middleware, named routes, and caching.
Features:
- β Attribute-Based Routing - PHP 8 attributes on controller methods
- β Middleware Support - Route, controller, and global middleware
- β Named Routes - URL generation from route names
- β Route Constraints - Built-in and custom parameter validation
- β Route Groups - Organize routes with shared prefixes and middleware
- β Optional Parameters - Support for optional route segments
- β Route Caching - Production-ready caching
- β CORS Support - Built-in CORS middleware
Basic Routes:
use MonkeysLegion\Router\Router; use MonkeysLegion\Router\RouteCollection; $router = new Router(new RouteCollection()); // Simple routes $router->get('/users', fn($request) => new Response(...)); $router->post('/users', fn($request) => new Response(...)); $router->put('/users/{id}', fn($request, $id) => new Response(...)); $router->patch('/users/{id}', fn($request, $id) => new Response(...)); $router->delete('/users/{id}', fn($request, $id) => new Response(...)); // Multiple methods $router->match(['GET', 'POST'], '/submit', $handler); // Any HTTP method $router->any('/webhook', $handler);
Route with Parameters:
// Required parameter $router->get('/users/{id}', function ($request, string $id) { return new Response("User ID: {$id}"); }); // Parameter with regex constraint $router->get('/users/{id:\d+}', $handler); // Only digits // Multiple parameters $router->get('/posts/{year}/{month}/{slug}', function ($request, $year, $month, $slug) { // Access all parameters }); // Optional parameters $router->get('/posts/{page?}', function ($request, ?string $page = '1') { // $page defaults to '1' if not provided }); // Multiple optional parameters $router->get('/archive/{year}/{month?}/{day?}', $handler);
Built-in Constraints:
$router->get('/users/{id:int}', $handler); // Integer only $router->get('/users/{id:\d+}', $handler); // Alternative regex $router->get('/posts/{slug:slug}', $handler); // Slug format (a-z0-9-) $router->get('/posts/{slug:[a-z0-9-]+}', $handler); // Alternative regex $router->get('/items/{uuid:uuid}', $handler); // UUID format $router->get('/verify/{email:email}', $handler); // Email format $router->get('/price/{amount:numeric}', $handler); // Numeric values $router->get('/category/{name:alpha}', $handler); // Alphabetic only $router->get('/code/{code:alphanum}', $handler); // Alphanumeric
Attribute-Based Controllers:
use MonkeysLegion\Router\Attributes\Route; use MonkeysLegion\Router\Attributes\RoutePrefix; use MonkeysLegion\Router\Attributes\Middleware; #[RoutePrefix('/api/users')] #[Middleware(['cors', 'throttle'])] class UserController { #[Route('GET', '/', name: 'users.index', summary: 'List all users', tags: ['Users'])] public function index(): Response { // List users } #[Route('GET', '/{id:\d+}', name: 'users.show', summary: 'Get user by ID')] public function show(string $id): Response { // Show single user } #[Route('POST', '/', name: 'users.create')] #[Middleware('auth')] public function create(): Response { // Create user (requires auth) } #[Route(['PUT', 'PATCH'], '/{id:\d+}', name: 'users.update')] #[Middleware(['auth', 'can:update,user'])] public function update(string $id): Response { // Update user } #[Route('DELETE', '/{id:\d+}', name: 'users.destroy')] #[Middleware(['auth', 'admin'])] public function destroy(string $id): Response { // Delete user } } // Register the controller $router->registerController(new UserController());
Middleware:
use MonkeysLegion\Router\Middleware\MiddlewareInterface; // Create custom middleware class AuthMiddleware implements MiddlewareInterface { public function process(ServerRequestInterface $request, callable $next): ResponseInterface { if (!$this->isAuthenticated($request)) { return new Response(Stream::createFromString('Unauthorized'), 401); } return $next($request); } } // Register middleware by name $router->registerMiddleware('auth', AuthMiddleware::class); $router->registerMiddleware('cors', new CorsMiddleware()); $router->registerMiddleware('throttle', new ThrottleMiddleware(60, 1)); // Middleware groups $router->registerMiddlewareGroup('api', ['cors', 'throttle', 'json']); $router->registerMiddlewareGroup('web', ['cors', 'csrf', 'session']); // Global middleware (applied to all routes) $router->addGlobalMiddleware('cors'); $router->addGlobalMiddleware('logging'); // Route-specific middleware $router->add('GET', '/admin', $handler, 'admin.dashboard', ['auth', 'admin']);
Route Groups:
// Group with prefix and middleware $router->group(function (Router $router) { $router->get('/users', $usersHandler); // /api/v1/users $router->get('/posts', $postsHandler); // /api/v1/posts $router->get('/comments', $commentsHandler); // /api/v1/comments }) ->prefix('/api/v1') ->middleware(['cors', 'throttle', 'auth']) ->group(fn() => null); // Nested groups $router->group(function (Router $router) { // Admin routes: /admin/* $router->get('/dashboard', $dashboardHandler); $router->group(function (Router $router) { // User management: /admin/users/* $router->get('/', $listHandler); $router->get('/{id}', $showHandler); $router->post('/', $createHandler); }) ->prefix('/users') ->middleware(['can:manage-users']) ->group(fn() => null); }) ->prefix('/admin') ->middleware(['auth', 'admin']) ->group(fn() => null);
URL Generation:
// Define named routes $router->get('/users', $handler, 'users.index'); $router->get('/users/{id}', $handler, 'users.show'); $router->get('/posts/{year}/{slug}', $handler, 'posts.show'); // Get URL generator $urlGen = $router->getUrlGenerator(); $urlGen->setBaseUrl('https://example.com'); // Generate URLs echo $router->url('users.index'); // Output: /users echo $router->url('users.show', ['id' => 123]); // Output: /users/123 echo $router->url('users.show', ['id' => 123], absolute: true); // Output: https://example.com/users/123 // Extra parameters become query string echo $router->url('posts.show', ['year' => '2024', 'slug' => 'hello', 'preview' => 1]); // Output: /posts/2024/hello?preview=1
CORS Middleware:
use MonkeysLegion\Router\Middleware\CorsMiddleware; $cors = new CorsMiddleware([ 'allowed_origins' => ['https://example.com', 'https://app.example.com'], 'allowed_methods' => ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], 'allowed_headers' => ['Content-Type', 'Authorization', 'X-Requested-With'], 'exposed_headers' => ['X-Total-Count', 'X-Page-Count'], 'max_age' => 86400, 'credentials' => true, ]); $router->registerMiddleware('cors', $cors);
Throttle Middleware:
use MonkeysLegion\Router\Middleware\ThrottleMiddleware; // 60 requests per minute $throttle = new ThrottleMiddleware(maxRequests: 60, perMinutes: 1); $router->registerMiddleware('throttle', $throttle); // Different limits for different routes $router->registerMiddleware('throttle:strict', new ThrottleMiddleware(10, 1)); $router->registerMiddleware('throttle:relaxed', new ThrottleMiddleware(200, 1));
Route Caching (Production):
use MonkeysLegion\Router\RouteCache; $cache = new RouteCache(__DIR__ . '/var/cache'); $collection = new RouteCollection(); // Load from cache if available if ($cache->has()) { $data = $cache->load(); $collection->import($data); } else { // Register all routes $router = new Router($collection); // ... register routes ... // Save to cache $exported = $collection->export(); $cache->save($exported['routes'], $exported['namedRoutes']); } // Clear cache when deploying $cache->clear(); // Check cache stats $stats = $cache->getStats();
Custom Error Handlers:
// Custom 404 handler $router->setNotFoundHandler(function (ServerRequestInterface $request) { return new Response( Stream::createFromString(json_encode([ 'error' => 'Not Found', 'path' => $request->getUri()->getPath(), ])), 404, ['Content-Type' => 'application/json'] ); }); // Custom 405 handler $router->setMethodNotAllowedHandler( function (ServerRequestInterface $request, array $allowedMethods) { return new Response( Stream::createFromString(json_encode([ 'error' => 'Method Not Allowed', 'allowed' => $allowedMethods, ])), 405, [ 'Content-Type' => 'application/json', 'Allow' => implode(', ', $allowedMethods), ] ); } );
Dispatching Requests:
// Get request from globals $request = ServerRequestFactory::fromGlobals(); // Dispatch and get response $response = $router->dispatch($request); // Send response to client header('HTTP/1.1 ' . $response->getStatusCode()); foreach ($response->getHeaders() as $name => $values) { foreach ($values as $value) { header("{$name}: {$value}", false); } } echo $response->getBody();
Route Metadata for OpenAPI:
#[Route(
'GET',
'/users',
name: 'users.index',
summary: 'List all users',
description: 'Returns a paginated list of users with optional filters',
tags: ['Users', 'API'],
meta: ['version' => '1.0', 'deprecated' => false]
)]
public function index(): Response { }
monkeyslegion-validation
Attribute-driven DTO binding and validation layer.
Define a DTO:
namespace App\Dto; use MonkeysLegion\Validation\Attributes as Assert; final readonly class CreateUserRequest { public function __construct( #[Assert\NotBlank] #[Assert\Email] public string $email, #[Assert\NotBlank] #[Assert\Length(min: 8, max: 64)] public string $password, #[Assert\Range(min: 0.01, max: 9999.99)] public float $price, #[Assert\Url] public string $website, #[Assert\UuidV4] public string $categoryId, ) {} }
Available Constraints:
#[NotBlank]- Value cannot be empty#[Email]- Valid email format#[Length(min, max)]- String length range#[Range(min, max)]- Numeric range#[Pattern(regex)]- Regex pattern match#[Url]- Valid URL format#[UuidV4]- Valid UUIDv4 format
Validation Response (400):
{
"errors": [
{ "field": "email", "message": "Value must be a valid e-mail." },
{ "field": "password", "message": "Length constraint violated." }
]
}
πΎ Database & ORM
monkeyslegion-database
Native PDO-powered MySQL 8.4 connection and query helpers.
monkeyslegion-query
Powerful, fluent Query Builder & Micro-ORM.
Features:
- π Fluent API - Chainable, expressive query building
- π‘οΈ SQL Injection Protection - Automatic parameter binding
- π Transactions - Full ACID compliance with savepoints
- π Advanced Queries - Joins, subqueries, unions, CTEs
Basic Queries:
use MonkeysLegion\Query\QueryBuilder; $qb = new QueryBuilder($connection); // Simple query $users = $qb->from('users') ->where('status', '=', 'active') ->orderBy('created_at', 'DESC') ->limit(10) ->fetchAll(); // With joins $posts = $qb->from('posts', 'p') ->leftJoin('users', 'u', 'u.id', '=', 'p.user_id') ->select(['p.*', 'u.name as author']) ->where('p.published', '=', true) ->fetchAll();
WHERE Clauses:
// Basic conditions $qb->where('status', '=', 'active') ->where('age', '>', 18) ->orWhere('role', '=', 'admin'); // IN / BETWEEN $qb->whereIn('id', [1, 2, 3, 4, 5]) ->whereBetween('age', 18, 65) ->whereNull('deleted_at'); // Grouped conditions $qb->where('status', '=', 'active') ->whereGroup(function($q) { $q->where('role', '=', 'admin') ->orWhere('role', '=', 'moderator'); }); // Produces: WHERE status = 'active' AND (role = 'admin' OR role = 'moderator')
Insert / Update / Delete:
// Insert $userId = $qb->insert('users', [ 'name' => 'John Doe', 'email' => 'john@example.com' ]); // Batch insert $qb->insertBatch('users', [ ['name' => 'Alice', 'email' => 'alice@example.com'], ['name' => 'Bob', 'email' => 'bob@example.com'], ]); // Update $qb->update('users', ['status' => 'inactive']) ->where('last_login', '<', date('Y-m-d', strtotime('-1 year'))) ->execute(); // Delete $qb->delete('users') ->where('status', '=', 'deleted') ->execute();
Aggregates & Pagination:
$total = $qb->from('users')->count(); $revenue = $qb->from('orders')->sum('amount'); $avgPrice = $qb->from('products')->avg('price'); // Pagination $result = $qb->from('posts') ->where('published', '=', true) ->paginate(page: 2, perPage: 15); // Returns: ['data' => [...], 'total' => 150, 'page' => 2, 'lastPage' => 10, ...]
Transactions:
$result = $qb->transaction(function($qb) { $userId = $qb->insert('users', ['name' => 'Alice']); $qb->insert('profiles', ['user_id' => $userId]); return $userId; });
monkeyslegion-entity
Attribute-based data-mapper, entity scanner, and repository layer.
use MonkeysLegion\Entity\Attributes\Entity; use MonkeysLegion\Entity\Attributes\Field; #[Entity(table: 'users')] class User { #[Field(type: 'integer')] public int $id; #[Field(type: 'string', length: 255)] public string $email; #[Field(type: 'datetime')] public \DateTimeImmutable $created_at; }
monkeyslegion-migration
Entity-schema diff engine and SQL migration runner.
php ml make:migration # Generate migration from entity differences php ml migrate # Run pending migrations php ml rollback # Revert last migration
π Authentication & Security
monkeyslegion-auth
Comprehensive authentication and authorization package.
Features:
- π JWT Authentication - Stateless auth with access/refresh token pairs
- π₯ RBAC - Role-based access control with permission inheritance
- π Two-Factor (2FA) - TOTP compatible with Google Authenticator
- π OAuth - Google, GitHub providers (easily extensible)
- ποΈ API Keys - Scoped keys for M2M authentication
- β±οΈ Rate Limiting - Brute force protection
Basic Setup:
use MonkeysLegion\Auth\Service\AuthService; use MonkeysLegion\Auth\Service\JwtService; use MonkeysLegion\Auth\Service\PasswordHasher; $jwt = new JwtService( secret: $_ENV['JWT_SECRET'], accessTtl: 1800, // 30 minutes refreshTtl: 604800, // 7 days ); $auth = new AuthService( users: $userProvider, hasher: new PasswordHasher(), jwt: $jwt, );
Login Flow:
try { $result = $auth->login($email, $password, $request->ip()); if ($result->requires2FA) { // Show 2FA form return response()->json([ 'requires_2fa' => true, 'challenge' => $result->challengeToken, ]); } return response()->json([ 'access_token' => $result->tokens->accessToken, 'refresh_token' => $result->tokens->refreshToken, 'expires_at' => $result->tokens->accessExpiresAt, ]); } catch (InvalidCredentialsException $e) { return response()->json(['error' => 'Invalid credentials'], 401); }
User Entity Setup:
use MonkeysLegion\Auth\Contract\AuthenticatableInterface; use MonkeysLegion\Auth\Contract\HasRolesInterface; use MonkeysLegion\Auth\Trait\AuthenticatableTrait; use MonkeysLegion\Auth\Trait\HasRolesTrait; class User implements AuthenticatableInterface, HasRolesInterface { use AuthenticatableTrait; use HasRolesTrait; public function getAuthIdentifier(): int|string { return $this->id; } public function getAuthPassword(): string { return $this->password_hash; } public function hasTwoFactorEnabled(): bool { return $this->two_factor_secret !== null; } }
PHP Attributes for Authorization:
use MonkeysLegion\Auth\Attribute\Authenticated; use MonkeysLegion\Auth\Attribute\RequiresRole; use MonkeysLegion\Auth\Attribute\RequiresPermission; use MonkeysLegion\Auth\Attribute\Can; #[Authenticated] class PostController { #[RequiresPermission('posts.create')] public function create(): Response { } #[Can('update', Post::class)] public function update(Post $post): Response { } #[RequiresRole('admin', 'moderator')] public function delete(Post $post): Response { } }
RBAC Configuration:
use MonkeysLegion\Auth\RBAC\RoleRegistry; $roles = new RoleRegistry(); $roles->registerFromConfig([ 'super-admin' => [ 'permissions' => ['*'], ], 'admin' => [ 'permissions' => ['users.*', 'posts.*'], ], 'editor' => [ 'permissions' => ['posts.*'], 'inherits' => ['viewer'], ], 'viewer' => [ 'permissions' => ['posts.view'], ], ]);
2FA Setup:
use MonkeysLegion\Auth\TwoFactor\TotpProvider; use MonkeysLegion\Auth\Service\TwoFactorService; $twoFactor = new TwoFactorService(new TotpProvider(), issuer: 'YourApp'); // Generate setup data (QR code) $setup = $twoFactor->generateSetup($user->email); // Returns: secret, qr_code (base64), uri, recovery_codes // Verify and enable $twoFactor->enable($setup['secret'], $code, $user->id);
π Caching & Storage
monkeyslegion-cache
PSR-16 compliant cache with multiple drivers.
Drivers: File, Redis, Memcached, Array (in-memory)
Configuration:
return [ 'default' => 'redis', 'stores' => [ 'file' => [ 'driver' => 'file', 'path' => __DIR__ . '/../storage/cache', ], 'redis' => [ 'driver' => 'redis', 'host' => '127.0.0.1', 'port' => 6379, ], ], ];
Usage:
use MonkeysLegion\Cache\Cache; // Basic operations Cache::set('key', 'value', 3600); $value = Cache::get('key', 'default'); Cache::delete('key'); // Remember pattern $users = Cache::remember('users', 3600, function() { return User::all(); }); // Cache tagging Cache::tags(['users', 'premium'])->set('user:1', $user, 3600); Cache::tags(['users'])->clear(); // Clear all tagged // Incrementing Cache::increment('counter'); Cache::decrement('counter', 5);
CLI Commands:
php ml cache:clear # Clear default store php ml cache:clear --store=redis # Clear specific store php ml cache:get user:123 # Get value php ml cache:set key value # Set value
monkeyslegion-files
Production-ready file storage and upload management.
Features:
- π Chunked Uploads - Resume-capable multipart uploads
- βοΈ Multi-Storage - Local, S3, MinIO, DigitalOcean, GCS, Firebase
- πΌοΈ Image Processing - Thumbnails, optimization, watermarks
- π Security - Signed URLs, rate limiting, virus scanning
- π Database Tracking - File metadata with soft deletes
Basic Usage:
use MonkeysLegion\Files\FilesManager; // Store a file $path = $files->put($_FILES['upload']['tmp_name']); $path = $files->putString("Hello World", 'text/plain'); // Read / Check $contents = $files->get($path); if ($files->exists($path)) { $size = $files->size($path); $mime = $files->mimeType($path); } // Signed URLs $url = ml_files_sign_url('/files/' . $path, ttl: 600);
Image Processing:
use MonkeysLegion\Files\Image\ImageProcessor; $processor = new ImageProcessor(driver: 'gd', quality: 85); $thumbPath = $processor->thumbnail($path, 300, 300, 'cover'); $optimized = $processor->optimize($path, quality: 80); $webp = $processor->convert($path, 'webp'); $watermarked = $processor->watermark($path, $watermarkPath, 'bottom-right'); // Batch conversions $conversions = $processor->processConversions($path, [ 'thumb' => ['width' => 150, 'height' => 150, 'fit' => 'cover'], 'medium' => ['width' => 800, 'height' => 600], 'webp' => ['format' => 'webp', 'quality' => 80], ]);
Chunked Uploads:
use MonkeysLegion\Files\Upload\ChunkedUploadManager; // Initialize upload session $uploadId = $chunked->initiate('large-video.mp4', $totalSize, 'video/mp4'); // Upload chunks foreach ($chunks as $index => $chunk) { $chunked->uploadChunk($uploadId, $index, $chunk['data'], $chunk['size']); } // Complete $finalPath = $chunked->complete($uploadId); // Check progress $progress = $chunked->getProgress($uploadId); // ['uploaded_chunks' => 5, 'total_chunks' => 10, 'percent' => 50]
S3 Storage:
use MonkeysLegion\Files\Storage\S3Storage; $s3 = new S3Storage( bucket: 'my-bucket', region: 'us-east-1', accessKey: $key, secretKey: $secret, ); // Pre-signed URLs $uploadUrl = $s3->getUploadUrl('uploads/file.jpg', 'image/jpeg', 3600); $downloadUrl = $s3->getTemporaryUrl('private/doc.pdf', 3600);
π¨ Templating & Views
monkeyslegion-template
MLView template engine with components, slots, and caching.
Syntax:
// resources/views/welcome.ml.php {{-- Escaped output --}} <h1>{{ $title }}</h1> {{-- Raw HTML --}} {!! $html !!} {{-- Control structures --}} @if ($user->isAdmin()) <span class="badge">Admin</span> @endif @foreach ($items as $item) <li>{{ $item->name }}</li> @endforeach {{-- Components --}} <x-alert type="success"> Operation completed! </x-alert> {{-- Layout inheritance --}} @extends('layouts.app') @section('content') <p>Page content here</p> @endsection {{-- Slots --}} <x-card> @slot('header') Card Title @endslot Card body content </x-card>
π CLI & Development
monkeyslegion-cli
Command-line interface and developer tooling.
# General Commands php ml key:generate # Generate APP_KEY php ml cache:clear # Clear view cache php ml route:list # Display routes php ml tinker # Interactive REPL # Database php ml db:create # Create database php ml make:migration # Generate migration php ml migrate # Run migrations php ml rollback # Undo migrations php ml db:seed # Run seeders # Scaffolding php ml make:entity User # Generate Entity php ml make:controller User # Generate Controller php ml make:middleware Auth # Generate Middleware php ml make:policy User # Generate Policy # API php ml openapi:export # Export OpenAPI spec
monkeyslegion-dev-server
Hot-reload development server.
composer serve # Start dev server composer server:start:public # Start on 0.0.0.0:8000 composer server:stop # Stop server composer server:restart # Restart server
π§ Communication & Events
monkeyslegion-mail
Feature-rich mail package with DKIM, queues, and templates.
Features:
- π§ Multiple Transports - SMTP, Sendmail, Mailgun, Null
- π‘οΈ DKIM Signing - Email authentication
- π Queue System - Background processing with Redis
- π¨ Mailable Classes - Object-oriented email composition
Direct Sending:
use MonkeysLegion\Mail\Mailer; $mailer->send( 'user@example.com', 'Welcome to Our App', '<h1>Welcome!</h1><p>Thanks for joining us.</p>', 'text/html' );
Mailable Classes:
// Generate: php ml make:mail OrderConfirmation use MonkeysLegion\Mail\Mail\Mailable; class OrderConfirmationMail extends Mailable { public function __construct( private array $order, private array $customer ) { parent::__construct(); } public function build(): self { return $this->view('emails.order-confirmation') ->subject('Order Confirmation #' . $this->order['id']) ->withData([ 'order' => $this->order, 'customer' => $this->customer, ]) ->attach('/path/to/invoice.pdf'); } } // Send $mail = new OrderConfirmationMail($order, $customer); $mail->setTo('john@example.com')->send(); // Or queue $mail->setTo('john@example.com')->queue();
CLI Commands:
php ml mail:test user@example.com # Test sending php ml make:mail WelcomeMail # Generate Mailable php ml make:dkim-pkey storage/keys # Generate DKIM keys php ml mail:work # Process queue php ml mail:list # List pending jobs
monkeyslegion-events
PSR-14 event-bus for core lifecycle events.
π Internationalization
monkeyslegion-i18n
Production-ready I18n & localization.
Features:
- π Multiple Sources - JSON, PHP, Database loaders
- π ICU Pluralization - Plural rules for 200+ languages
- π― Auto Detection - URL, Session, Headers, Cookies
- π¦ Namespacing - Package-level translations
Translation Files:
// resources/lang/en/messages.json { "welcome": "Welcome!", "greeting": "Hello, :name!", "items": "{0} No items|{1} One item|[2,*] :count items" }
Usage:
use MonkeysLegion\I18n\TranslatorFactory; $translator = TranslatorFactory::create([ 'locale' => 'es', 'fallback' => 'en', 'path' => __DIR__ . '/resources/lang' ]); echo $translator->trans('messages.welcome'); // Output: Β‘Bienvenido! echo $translator->trans('messages.greeting', ['name' => 'John']); // Output: Β‘Hola, John! // Pluralization echo $translator->choice('messages.items', 5); // Output: 5 artΓculos
Helper Functions:
__('messages.welcome'); __('messages.greeting', ['name' => 'John']); trans_choice('cart.items', $count); lang(); // Get current locale lang('es'); // Set locale
π Observability & Logging
monkeyslegion-logger
PSR-3 compliant advanced logger.
monkeyslegion-telemetry
Comprehensive telemetry with metrics, logging, and distributed tracing.
Features:
- π Prometheus Metrics - Counter, Gauge, Histogram, Summary
- π StatsD Support - Alternative metrics backend
- π Distributed Tracing - W3C Trace Context propagation
- π― Head Sampling - Configurable trace sampling
Initialization:
use MonkeysLegion\Telemetry\Telemetry; Telemetry::init([ 'metrics' => [ 'driver' => 'prometheus', 'namespace' => 'myapp', ], 'tracing' => [ 'enabled' => true, 'service_name' => 'myapp', 'sample_rate' => 1.0, ], 'logging' => [ 'stream' => 'php://stderr', 'json' => true, ], ]);
Metrics:
// Counter Telemetry::counter('http_requests_total', 1, ['method' => 'GET', 'status' => '200']); // Gauge Telemetry::gauge('active_connections', 42); // Histogram Telemetry::histogram('http_request_duration_seconds', 0.123, ['endpoint' => '/api/users']); // Timer $stopTimer = Telemetry::timer('operation_duration_seconds'); $this->heavyOperation(); $duration = $stopTimer(['operation' => 'heavy_task']);
Distributed Tracing:
use MonkeysLegion\Telemetry\Tracing\SpanKind; // Simple tracing $result = Telemetry::trace('fetch-user', function () use ($userId) { return $this->userRepository->find($userId); }, SpanKind::CLIENT, ['user.id' => $userId]); // Nested traces (automatic parent-child) $result = Telemetry::trace('process-order', function () use ($order) { $inventory = Telemetry::trace('check-inventory', function () use ($order) { return $this->inventory->check($order->items); }, SpanKind::CLIENT); $payment = Telemetry::trace('process-payment', function () use ($order) { return $this->payment->charge($order); }, SpanKind::CLIENT); return ['inventory' => $inventory, 'payment' => $payment]; }); // Get trace ID for correlation $traceId = Telemetry::traceId();
Logging with Trace Correlation:
$logger = Telemetry::log(); $logger->info('User logged in', ['user_id' => 123]); // JSON output includes trace_id and span_id automatically
π¨ Configuration & DI
All services are wired in config/app.php. Customize:
- Database DSN & credentials (
config/database.php) - CORS, cache, auth (
.mlcfiles) - Middleware order, validation, rate-limit thresholds
- CLI commands registered in
CliKernel
β Testing & Build
Test Harness
A base PHPUnit class tests/IntegrationTestCase.php provides:
- DI bootstrapping from
config/app.php - PSR-15 pipeline via
MiddlewareDispatcher createRequest($method, $uri, $headers, $body)to craft HTTP requestsdispatch($request)to get aResponseInterface- Assertions:
assertStatus(Response, int)assertJsonResponse(Response, array)
Example:
namespace Tests\Controller; use Tests\IntegrationTestCase; final class HomeControllerTest extends IntegrationTestCase { public function testIndexReturnsHtml(): void { $request = $this->createRequest('GET', '/'); $response = $this->dispatch($request); $this->assertStatus($response, 200); $this->assertStringContainsString('<h1>', (string)$response->getBody()); } }
Running Tests
composer test # Run PHPUnit tests ./vendor/bin/phpunit # Direct PHPUnit execution
π Requirements
- PHP 8.4+ - Required for all packages
- MySQL 8.4 - Recommended database
- Composer 2.x - Dependency management
Recommended PHP Extensions
| Extension | Purpose |
|---|---|
pdo_mysql |
Database connectivity |
redis |
Caching, rate limiting, session storage |
mbstring |
Multi-byte string handling |
json |
JSON processing |
gd or imagick |
Image processing |
intl |
Advanced I18n formatting |
posix |
CLI process management |
pcntl |
Signal handling |
π€ Contributing
- Fork π΄
- Create a feature branch π±
- Submit a PR π
Happy hacking with MonkeysLegion! π
π License
MIT License - see LICENSE for details.
Contributors
![]() Jorge Peraza |
![]() Amanar Marouane |

