PHPackages                             monkeyscloud/monkeyslegion-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. monkeyscloud/monkeyslegion-skeleton

ActiveProject[Framework](/categories/framework)

monkeyscloud/monkeyslegion-skeleton
===================================

Starter project for the MonkeysLegion framework

1.0.17(2mo ago)13508↓50%1[1 PRs](https://github.com/MonkeysCloud/MonkeysLegion-Skeleton/pulls)MITHackPHP ^8.4CI passing

Since Jul 25Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/MonkeysCloud/MonkeysLegion-Skeleton)[ Packagist](https://packagist.org/packages/monkeyscloud/monkeyslegion-skeleton)[ Docs](https://github.com/monkeyscloud/monkeyslegion-skeleton)[ RSS](/packages/monkeyscloud-monkeyslegion-skeleton/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (2)Dependencies (14)Versions (27)Used By (0)

MonkeysLegion Skeleton
======================

[](#monkeyslegion-skeleton)

[![PHP Version](https://camo.githubusercontent.com/2aed50cc19486e0407775311c22f530383b2791ca08b8c9583ebb80cb5e364e7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d382e342532422d626c75652e737667)](https://php.net)[![License](https://camo.githubusercontent.com/8bb50fd2278f18fc326bf71f6e88ca8f884f72f179d3e555e20ed30157190d0d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e2e737667)](LICENSE)[![Packagist](https://camo.githubusercontent.com/a5304e80158a958029bd67caf0813a50d98097429b2afbeb7c9931738fc0685d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d6f6e6b657973636c6f75642f6d6f6e6b6579736c6567696f6e2d736b656c65746f6e2e737667)](https://packagist.org/packages/monkeyscloud/monkeyslegion-skeleton)

**A production-ready starter for building web apps &amp; APIs with the MonkeysLegion framework.**

---

✨ Features Overview
-------------------

[](#-features-overview)

CategoryFeatures**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 &amp; 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 &amp; localization support**Telemetry**Prometheus metrics, distributed tracing, PSR-3 logging**Mail**SMTP, Markdown templates, DKIM support**Caching**Multiple drivers (File, Redis, Memcached)---

🚀 Quick-start
-------------

[](#-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
----------------

[](#-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)
------------------------------

[](#-package-ecosystem-detailed)

MonkeysLegion is built as a modular ecosystem of packages. Below is comprehensive documentation for each package with examples.

---

### 🔧 Core Framework

[](#-core-framework)

#### `monkeyslegion` (Meta-package)

[](#monkeyslegion-meta-package)

Installs the complete MonkeysLegion stack with a single command.

```
composer require monkeyscloud/monkeyslegion
```

---

#### `monkeyslegion-core`

[](#monkeyslegion-core)

Core runtime providing kernel, events, and helpers.

---

#### `monkeyslegion-di`

[](#monkeyslegion-di)

Tiny PSR-11 compliant dependency injection container.

---

#### `monkeyslegion-mlc`

[](#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 &amp; Routing

[](#-http--routing)

#### `monkeyslegion-http`

[](#monkeyslegion-http)

PSR-7 HTTP message implementations and SAPI emitter.

---

#### `monkeyslegion-router`

[](#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`

[](#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 &amp; ORM

[](#-database--orm)

#### `monkeyslegion-database`

[](#monkeyslegion-database)

Native PDO-powered MySQL 8.4 connection and query helpers.

---

#### `monkeyslegion-query`

[](#monkeyslegion-query)

Powerful, fluent Query Builder &amp; 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', '
