dynart/micro

Micro PHP framework.

Installs: 8

Dependents: 1

Suggesters: 0

Security: 0

Stars: 1

Watchers: 1

Forks: 0

Open Issues: 0

pkg:composer/dynart/micro

dev-main 2026-02-22 23:39 UTC

This package is not auto-updated.

Last update: 2026-02-22 23:51:47 UTC


README

PHP License Version

A lightweight PHP micro framework with dependency injection, routing, templating, i18n, form handling, and CLI support. No magic, no bloat — just the essentials.

Quick links

API DocsCoverage ReportTest Repo

📦 Installation

composer require dynart/micro

⚡ Quick Start

index.php

<?php

use Dynart\Micro\Micro;
use Dynart\Micro\WebApp;

require 'vendor/autoload.php';

Micro::run(new WebApp(['config.ini.php']));

Controllers/HomeController.php

namespace App\Controllers;

use Dynart\Micro\Attribute\Route;

class HomeController {
    #[Route('GET', '/')]
    public function index(): string {
        return '<h1>Hello world!</h1>';
    }
}

composer.json

{
    "require": {
        "dynart/micro": "^0.8"
    },
    "autoload": {
        "psr-4": {
            "App\\": "."
        }
    }
}

config.ini.php

;<?php /*
app.root_path = /full/path/to/your/webapp
app.base_url = http://url/to/your/webapp
app.scan_namespaces = App

🏗️ Architecture

Micro::run(App)
  ├── fullInit()
  │   ├── Config loaded (INI files)
  │   ├── Logger created
  │   ├── AbstractApp::init()           ← register routes, services
  │   ├── Middlewares run        ← locale, attributes, custom
  │   └── emit init_finished
  └── fullProcess()
      └── AbstractApp::process()        ← dispatch route / CLI command

🧱 Components

💡 DI Container — Micro

Static service locator with reflection-based auto-wiring. All services are singletons.

// Register
Micro::add(ConfigInterface::class, Config::class);
Micro::add(MyService::class);

// Resolve (auto-creates with constructor injection)
$service = Micro::get(MyService::class);

// Factory (new instance each time)
$instance = Micro::create(MyService::class, ['extraParam']);

Classes with a postConstruct() method get it called automatically after creation.

🛣️ Routing — Router

Path variables use ? wildcards. Controller methods return string for HTML or array for JSON.

$router->add('/users/?/posts/?', [UserController::class, 'posts']);
$router->add('/api/login', [AuthController::class, 'login'], 'POST');
$router->add('/search', [SearchController::class, 'index'], 'BOTH'); // GET + POST

PHP 8 attributes (active by default, controlled by app.use_route_attributes):

class BookController {

    #[Route('GET', '/books')]
    public function list(): string { /* ... */ }

    #[Route('GET', '/books/?')]
    public function show(string $id): array { /* ... */ }
}

⚙️ Configuration — Config

INI-based with dot-notation keys, environment variable substitution, and path aliases.

app.root_path = /var/www/myapp
app.base_url = https://myapp.com
app.environment = prod

; Environment variable override
database.password = {{DB_PASSWORD}}

; Path alias: ~ expands to app.root_path
view.default_folder = ~/views

; Comma-separated values
allowed.origins = http://localhost,https://example.com
$config->get('app.base_url');                       // string
$config->getCommaSeparatedValues('allowed.origins'); // array
$config->getArray('database');                       // nested array
$config->getFullPath('~/uploads');                   // /var/www/myapp/uploads

🎨 View / Templating — View

PHP templates (.phtml) with namespaces, layouts, blocks, and theme overrides.

// Register namespace folder
$view->addFolder('admin', '~/views/admin');

// Set theme (overrides any template)
$view->setTheme('~/themes/dark');

// Render
$html = $view->fetch('admin:dashboard', ['user' => $user]);

Layout system:

<!-- views/layout.phtml -->
<html>
<body><?= $this->block('content') ?></body>
</html>
<!-- views/page.phtml -->
<?php $this->useLayout('layout'); ?>
<?php $this->startBlock('content'); ?>
  <h1>Hello</h1>
<?php $this->endBlock(); ?>

📝 Forms — Form

CSRF protection, field binding, validators, error tracking. Work in progress

$form = new Form($request, $session, 'login');
$form->addFields([
    'email' => ['type' => 'text'],
    'password' => ['type' => 'password']
]);
$form->addValidator('email', new EmailValidator());
$form->generateCsrf();

if ($form->bind() && $form->validate()) {
    // $form->value('email'), $form->value('password')
}

🌐 Internationalization — Translation

INI-based locale files with variable substitution.

; translations/en.ini
greeting = Hello, {name}!
$translation->add('app', '~/translations');
echo $translation->get('app:greeting', ['name' => 'World']); // Hello, World!

Auto locale detection via LocaleResolver middleware (Accept-Language header + URL prefix):

$this->addMiddleware(LocaleResolver::class);
// Routes become: /en/books, /hu/books, etc.

📡 Events — EventService

Pub/sub observer pattern with DI-compatible callables.

$events->subscribe('user:created', [NotificationService::class, 'onUserCreated']);
$events->emit('user:created', [$user]);

Built-in events: app:init_finished, webapp:route_matched, cliapp:command_matched

💻 CLI Support — CliApp

Argument parsing with named params and boolean flags.

class MyCliApp extends CliApp {
    public function init(): void {
        $commands = Micro::get(CliCommandsInterface::class);
        $commands->add('migrate', [MigrateCommand::class, 'run']);
        $commands->add('seed', [SeedCommand::class, 'run']);
    }
}

// Usage: php cli.php migrate -step 5 --fresh

🔐 JWT Authentication — JwtAuth

Attribute-driven authorization backed by JWT tokens. Enable with useJwtAuth(). Route attributes are active by default — no extra setup needed.

class App extends WebApp {
    public function init(): void {
        parent::init();
        $this->useJwtAuth();

        // Map sub → permissions (your DB lookup)
        Micro::get(JwtAuthInterface::class)->setUserResolver(
            function(string $sub, object $payload): JwtUserInterface {
                $perms = Micro::get(UserService::class)->getPermissionsForSub($sub);
                return new JwtUser($sub, $perms);
            }
        );
    }
}

Controller annotations:

#[Authorize]               // class-level: all methods require authentication
class AdminController {

    #[Route('GET', '/api/admin/stats')]
    public function stats(): array { /* inherits class-level auth */ }

    #[Route('GET', '/api/admin/ping')]
    #[AllowAnonymous]                  // overrides class-level
    public function ping(): array { return ['ok' => true]; }
}

class ApiController {

    #[Route('GET', '/api/books')]
    #[Authorize]                       // any authenticated user
    public function list(): array { /* ... */ }

    #[Route('DELETE', '/api/books/?')]
    #[Authorize('admin')]              // requires 'admin' permission
    public function delete(string $id): array { /* ... */ }
}

Config:

jwt.secret = {{JWT_SECRET}}
jwt.algorithm = HS256

The framework only validates the token and resolves permissions. Issuing tokens (e.g. after OAuth with Google/Facebook) is the application's responsibility. Requires firebase/php-jwt ^7.0.

Events emitted by JwtAuth: jwtauth:user_set, jwtauth:authorization_granted, jwtauth:authorization_denied

🔌 Middleware

Implement MiddlewareInterface and register. Runs after init(), before process().

class AuthMiddleware implements MiddlewareInterface {
    public function run(): void {
        // check auth, redirect if needed
    }
}

// In AbstractApp::init():
$this->addMiddleware(AuthMiddleware::class);

🗂️ Project Structure

src/
├── Micro.php                 DI container
├── AbstractApp.php                   Abstract application base
├── WebApp.php                HTTP application
├── CliApp.php                CLI application
├── Config.php                INI configuration
├── Router.php                URL routing
├── View.php                  Template engine
├── Form.php                  Form handling + CSRF
├── Translation.php           i18n
├── EventService.php          Pub/sub events
├── Session.php               Session wrapper
├── Request.php               HTTP request
├── Response.php              HTTP response
├── Logger.php                PSR-3 logging
├── Pager.php                 Pagination helper
├── AbstractValidator.php             Abstract validator base
├── UploadedFile.php          File upload wrapper
├── JwtAuth.php               JWT authorization service
├── JwtUser.php               Default JWT user value object
├── AuthorizationException.php  401/403 exception
├── Attribute/
│   ├── Route.php             #[Route] attribute
│   ├── Authorize.php         #[Authorize('permission')] attribute
│   └── AllowAnonymous.php    #[AllowAnonymous] attribute
├── AttributeHandler/
│   ├── RouteAttributeHandler.php
│   ├── AuthorizeAttributeHandler.php
│   └── AllowAnonymousAttributeHandler.php
└── Middleware/
    ├── AttributeProcessor.php
    ├── JwtValidator.php      Decodes Bearer token, resolves user
    └── LocaleResolver.php

🔍 Related Packages

Package Description
dynart-micro-entities ORM / entity layer with PDO, query builder, dirty tracking

📄 License

Apache 2.0