samaphp / microapp
A minimal PHP microframework router.
Requires
- php: >=7.4
- ext-json: *
This package is auto-updated.
Last update: 2025-06-15 09:59:06 UTC
README
MicroApp is a minimal PHP 7.4+ microframework for building super-microservices with clean routing and zero dependencies. Perfect for fast bootstraps, tiny APIs, internal tools, or focused endpoints where simplicity wins. Designed for developers who value clarity, control, zero framework overhead and long-term maintainability. Built for microservices that can live for decades without requiring upgrades or major refactors. Youβre encouraged to follow future-proof design principles when building on top of it.
π Features
- β
GET
,POST
,PUT
,DELETE
, andPATCH
method support - β
Named route parameters like
/user/{id}
- β
JSON response helper via
$app->jsonResponse()
- β Centralized response lifecycle with override support
- β PSR-4 structure with Composer autoloading
- β Simple and readable one-file implementation
- β Ready to be used as a Composer package
- β Auto-discovery of controller classes with route definitions inside the class itself
- β
Optional CLI available via the
microapp-dev
package - β Middleware (before/after) for logging, authentication, CSRF protection, and other cross-cutting concerns
π Getting Started
- Install via Composer:
composer require samaphp/microapp
- You can set things up manually (see sections below) or automate it using the
microapp-dev
package from the Developer Tools section.
π οΈ Developer Tools
Looking to scaffold .htaccess
, index.php
, or generate controllers?
Use the official companion package:
β‘οΈ samaphp/microapp-dev
π basePath Support
If your application is served from a subdirectory (e.g., example.com/myapp/), you can pass the base path to MicroApp during initialization:
$app = new MicroApp('/myapp');
This ensures all routes are matched correctly regardless of where your app is hosted.
π You must also update your .htaccess rewrite rule to reflect the same subdirectory.
π¦ .htaccess example
<IfModule mod_rewrite.c>
RewriteEngine On
# Enable this to support subdirectory installations after injecting basePath value to MicroApp('basePathHere')
#RewriteBase /basePathHere/
# Redirect everything except existing files and directories to index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]
</IfModule>
π¦ index.php Example
The auto generated index.php
will be like this:
<?php require __DIR__ . '/vendor/autoload.php'; use MicroApp\MicroApp; $app = new MicroApp(); $app->loadRoutesFrom(__DIR__ . '/src/Controller', 'App\\Controller'); $app->dispatch();
π¦ Controller Example
Your controller class should be in the src/Controller
directory and follow the PSR-4 autoloading standard. For example, if you create a controller named HomeController.php
, it should look like this:
<?php namespace App\Controller; use MicroApp\MicroApp; class HomeController { public function routes(MicroApp $app): void { $app->get('/home', [$this, 'index']); } public function index(): void { echo 'Hello from HomeController'; } }
π§© Middleware Support
Middleware are invoked as class instances with __invoke(MicroApp $app)
support.
They can inspect the request, modify headers, or override the response.
You can register global or route-specific middleware to execute custom logic before or after your routes.
πΉ Global Middleware
You can apply middleware from the index.php
file:
$app->before('auth'); // Runs before all routes if inserted in index.php or at the beganning of controller routes() method. $app->after('logger'); // Runs after all routes if inserted in index.php or at the beganning of controller routes() method.
πΉ Route Middleware
// 'auth' will run before the handler // 'audit' will run after the handler $app->get('/admin', [$this, 'admin'], 'auth', 'audit'); // or applying multiple middlewares per route $app->get('/admin', [$this, 'admin'], ['auth', 'admin'], 'audit'); // You can also define quick inline routes using closures $this->app->get('/ping', function () { $this->app->setResponse('pong'); }); // Or attach a middleware inline from within the same controller $this->app->get('/ping', function () { $this->app->setResponse('pong'); }, $this->ExampleInternalMiddleware());
β οΈ Best Practice: While calling middleware from internal methods is supported (since they are callable), itβs strongly recommended to define middleware as standalone classes under
App\Middleware
. This keeps your codebase clean, modular, and reusable across routes.
π¨ Middleware Example
Your middleware class should be placed in the src/Middleware
directory and follow the PSR-4 autoloading standard. Middleware must implement the __invoke
method and receive the MicroApp
instance.
For example, if you create a middleware named SecretPasswordMiddleware.php
, it should look like this:
<?php namespace App\Middleware; use MicroApp\MicroApp; class SecretPasswordMiddleware { public function __invoke(MicroApp $app): void { $key = $app->input('secret'); if ($key !== '123') { $app->jsonResponse([ 'error' => ['code' => 401, 'message' => 'Unauthorized'] ], 401); } } }
π§ Service Injection (Minimalist, Explicit)
MicroApp provides a clean and explicit way to register and access services using registerService()
and getService()
methods. This allows controllers and middleware to share reusable services like loggers, configuration, or database connections β while respecting the principle of command-query separation.
β How It Works
- Register a service using
$app->registerService('name', $instance)
- Retrieve a service using
$app->getService('name')
- Works both globally and dynamically inside route handlers and middleware
π Registering Services
// from index.php $app->registerService('logger', new Logger()); // from controller/middleware classes $this->app->registerService('logger', new Logger());
π§ Accessing Services
$logger = $this->app->getService('logger');
π§Ό Notes
- Calling
registerService()
with the same name will override the existing service. getService()
throws aRuntimeException
if the service is not found.- Designed for simplicity β no external container or auto-wiring required.
π§© Extending MicroApp Class
You can extend the MicroApp
class to customize internal behavior β such as centralized error handling:
class MyApp extends MicroApp { protected function handleException(\Throwable $e): void { error_log('Caught: ' . $e->getMessage()); self::json(['error' => 'Something went wrong'], 500); } }
π½ Accessing Request Input
You can use MicroApp::input()
to safely retrieve and sanitize input from various sources:
Usage:
$name = MicroApp::input('name'); // GET by default $email = MicroApp::input('email', 'POST', 'email'); $id = MicroApp::input('user_id', 'BODY', 'int'); $token = MicroApp::input('Authorization', 'HEADER'); MicroApp::input(string $key, string $method = 'GET', string $filter = 'string')
Parameters:
key
: The input name to fetch.method
: One of 'GET', 'POST', 'JSON', or 'HEADER'.filter
: Sanitization type: 'string', 'int' or 'email'.
π¦ Accessing Headers
To retrieve a request header (case-insensitive):
$auth = $app->getRequestHeader('Authorization');
To set a response header:
$app->addResponseHeader('X-Custom-Header', 'value');
π§ͺ Public API Reference
MicroApp exposes a set of simple, well-defined public methods:
before()
/after()
β Define global or per-controller middleware hooks.setResponse()
β Define a custom response body and can be used for Text/HTML response, status, and headers.jsonResponse()
β Send a JSON response with optional status code.getResponse()
β Retrieve the full response array (body, status, headers).getRequest()
β Get a specific request section and inputs (GET, POST, HEADER, etc.).getRequestHeader()
β Retrieve a specific request header.addResponseHeader()
β Add or override a response header.getAllRoutes()
β Debug utility to list all registered routes.getMiddlewares()
β Debug utility to inspect middleware configuration.
These methods form the MicroApp API surface and are safe to use directly from controllers or custom logic.
π£οΈ Roadmap
MicroApp aims to remain minimal and dependency-free while gradually improving developer experience and production readiness.
β Core Roadmap
- β
Middleware (before/after) for logging, authentication, CSRF protection, and other cross-cutting concerns - πΉ PHP 8+ Compatibility Check β audit for deprecated functions to ensure forward compatibility
- πΉ SQLite Support for lightweight, embedded persistence (potentially via a dedicated utility package)
- πΉ File Storage / Upload Handling for managing uploaded files and saving them to disk (potentially via a dedicated utility package)
- πΉ Standards-Based Middleware implement PSR-15 with minimal PSR-7 support (potentially via a dedicated utility package).
- πΉ Lazy Route Registration β defer loading route definitions until needed during dispatch. Reduces memory footprint and improves cold start times
- πΉ Middleware chain middleware chaining. and
middlewareGroup
to allow foradminOnly
to have multiple middlewares applied - πΉ Code ehancements Shortcuts for Early Return return
$app->abort(403, 'Forbidden');
orredirect()
- πΉ Shared request context Allow injecting state Use
$app->context['key'] = value
π§ Under Consideration
These features are being explored and may be added if they fit the minimalist design:
- πΈ Route Grouping / Prefixing for modular organization (e.g.,
/api/v1
) - πΈ Route Caching to speed up route resolution (no external dependencies)
- πΈ Service Container or DI for clean dependency injection in handlers
- πΈ Static File Serving / File Upload Handling β define usage and scope
- πΈ Logging Integration via a simple
log()
method or PSR-3 support
βοΈ Tradeoffs
MicroApp intentionally leaves out features that are outside its core responsibility as a router. This keeps the framework lightweight, dependency-free, and easy to reason about.
β Not handled by design
-
Authentication & Authorization
You are free to implement auth logic using your own classes or middleware. -
CSRF Protection
CSRF validation should be enforced at the application level, not in the router. -
Session Management
MicroApp does not handle PHP sessions or session storage by default. -
Rate Limiting
Should be implemented at the proxy (e.g. NGINX, Cloudflare) or middleware level. -
Output Rendering (Templates/Views)
MicroApp is designed for APIs and microservices, not traditional HTML applications. Template engines are out of scope β return raw JSON, plain text, or HTML directly as needed. -
Advanced Database Abstraction / ORM
MicroApp doesnβt include a database layer or ORM. You can use PDO directly or integrate any lightweight ORM as needed.
These tradeoffs allow MicroApp to stay focused, composable, and small β making it ideal for microservices, APIs, and focused backends.
π« Deferred by Design
The following are intentionally left out of the core to preserve MicroAppβs no-dependency philosophy:
- β Config File Support Loading
.env
files or configuration files is out of scope for the core. If needed, you can use a community-maintained package such asvlucas/phpdotenv
.
π§Ό Clean principles to follow
- If it's just one line wrapping an existing method, and itβs not used internally, donβt include it in the core.
π§ Disclaimer
- MicroApp is considered stable for use in real projects, though it has not yet reached version
1.0.0
. APIs are unlikely to change significantly, but some refinements may still occur as the ecosystem evolves. - The codebase has been reviewed with security scanning tools, and fixes have been applied to address identified concerns. Ongoing best-practice audits will continue to ensure safe use in production environments.