惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Engineering at Meta
Engineering at Meta
博客园_首页
J
Java Code Geeks
Jina AI
Jina AI
B
Blog RSS Feed
量子位
有赞技术团队
有赞技术团队
M
MIT News - Artificial intelligence
L
LangChain Blog
Microsoft Security Blog
Microsoft Security Blog
小众软件
小众软件
博客园 - 聂微东
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 三生石上(FineUI控件)
Last Week in AI
Last Week in AI
MongoDB | Blog
MongoDB | Blog
I
InfoQ
罗磊的独立博客
H
Hackread – Cybersecurity News, Data Breaches, AI and More
爱范儿
爱范儿
Y
Y Combinator Blog
Vercel News
Vercel News
雷峰网
雷峰网

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Building Lightweight PHP Microservices with webrium/core ...
Benyamin Khalife · 2026-06-15 · via DEV Community

Do you really need a full framework to handle a few API endpoints or webhooks?

Laravel and Symfony are excellent tools — for large applications. But when you're building a focused microservice, a webhook receiver, or a lightweight REST API, bootstrapping a full-stack framework means carrying hundreds of files, a massive autoloader, and a dependency tree you'll never fully use.

That's the problem webrium/core was built to solve: a minimalist, zero-dependency PHP micro-framework written entirely from scratch, designed to stay out of your way.


Installation

composer require webrium/core

That's it. No configuration files to publish, no service providers to register.


The Entry Point

Every webrium application starts with the same three lines:

<?php
require_once __DIR__ . '/vendor/autoload.php';

use Webrium\App;
use Webrium\Route;

App::initialize(__DIR__);

// ... your routes here

App::run();

App::initialize() sets the root path and loads the global helper functions. App::run() initializes error handling and dispatches the current request through the router.


Routing

The router supports all standard HTTP methods. Route handlers can be closures, a Controller@method string, or an [Controller::class, 'method'] array.

Basic routes:

Route::get('/status', fn() => ['status' => 'alive']);

Route::post('/items', fn() => ['created' => true]);

Route::put('/items/{id}', fn($id) => ['updated' => $id]);

Route::patch('/items/{id}', fn($id) => ['patched' => $id]);

Route::delete('/items/{id}', fn($id) => ['deleted' => $id]);

Route handlers return an array — the framework automatically encodes it as JSON and sends the correct Content-Type header.

Dynamic parameters:

Route::get('/users/{id}/posts/{postId}', function($id, $postId) {
    return [
        'user_id' => $id,
        'post_id' => $postId,
    ];
});

Named routes:

Route::get('/users/{id}', fn($id) => ['id' => $id])->name('users.show');

// Generate the URL elsewhere:
$url = route('users.show', ['id' => 42]); // /users/42

Route groups let you share a prefix and/or middleware across multiple routes:

Route::group(['prefix' => 'api/v1', 'middleware' => 'AuthMiddleware'], function() {
    Route::get('/profile',  fn() => ['user' => 'James Carter']);
    Route::post('/profile', fn() => ['updated' => true]);
});

Custom 404 handler:

Route::setNotFoundHandler(fn() => ['error' => 'Route not found', 'code' => 404]);


Reading Request Input

The global input() helper reads from $_GET on GET requests and from the request body on POST, PUT, PATCH, and DELETE. It handles both application/json and application/x-www-form-urlencoded automatically.

Route::post('/search', function() {
    $query  = input('q', '');
    $limit  = input('limit', 20);

    return ['query' => $query, 'limit' => $limit];
});

Pass null (or call input() with no arguments) to get the full input array:

Route::post('/echo', fn() => input());


Validation

The Validator class uses a fluent interface. Pass the input array to its constructor, chain your rules, then call isValid(), passes(), or fails().

use Webrium\Validator;

Route::post('/register', function() {
    $v = new Validator(input());

    $v->field('name', 'Full Name')->required()->string()->min(2)->max(50);
    $v->field('email', 'Email')->required()->email();
    $v->field('password', 'Password')->required()->min(8);
    $v->field('password_confirmation')->required()->confirmed('password');

    if ($v->fails()) {
        return respond(['errors' => $v->getErrors()], 422);
    }

    // proceed with registration...
    return ['success' => true, 'message' => 'Account created.'];
});

Available rules include: required, string, integer, numeric, boolean, email, min, max, confirmed, different, in, notIn, regex, date, json, array, nullable, and sometimes.


JWT Authentication

The Jwt class handles token generation and verification with HS256, HS384, and HS512. Signature verification uses hash_equals internally to prevent timing attacks.

use Webrium\Jwt;

$jwt = new Jwt('your-secret-key', 'HS256');

// Generate a token
$token = $jwt->generateToken([
    'sub'  => 42,
    'role' => 'admin',
    'exp'  => time() + 3600,
]);

// Verify and decode
$payload = $jwt->verifyToken($token);

if ($payload === null) {
    return respond(['error' => 'Invalid or expired token.'], 401);
}

return ['user_id' => $payload['sub']];

You can also read the payload without verification (useful for debugging):

$payload = Jwt::getPayload($token);


Password Hashing

The Hash class wraps PHP's password_hash and password_verify with sensible defaults and supports bcrypt, Argon2i, and Argon2id.

use Webrium\Hash;

// Hash a password (bcrypt by default)
$hashed = Hash::make('secret-password');

// Verify
$isValid = Hash::check('secret-password', $hashed); // true

// Argon2id
$hashed = Hash::argon2id('secret-password');

// Generate a secure random token
$token = Hash::token(64);

// Generate a UUID v4
$uuid = Hash::uuid();


Middleware

Middleware can be a closure, a global function, a Class@method string, or a class with a handle() method that returns a boolean. A falsy return blocks the request with a 403.

// Closure middleware
$authCheck = function() {
    $header = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
    return str_starts_with($header, 'Bearer ');
};

Route::group(['prefix' => 'api/v1', 'middleware' => $authCheck], function() {
    Route::get('/me', fn() => ['user' => 'James Carter']);
});

Class-based middleware:

class AuthMiddleware
{
    public function handle(): bool
    {
        $jwt     = new Webrium\Jwt('your-secret-key');
        $header  = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
        $token   = str_replace('Bearer ', '', $header);

        return $jwt->verifyToken($token) !== null;
    }
}

Route::group(['middleware' => 'AuthMiddleware'], function() {
    Route::get('/dashboard', fn() => ['data' => '...']);
});


CORS

CORS is handled at the application level with a single call before App::run().

App::enableCors(['https://app.example.com', 'https://admin.example.com']);

App::run();

For stricter control:

App::configureCors([
    'allowed_origins'   => ['https://app.example.com'],
    'allowed_methods'   => ['GET', 'POST', 'PUT', 'DELETE'],
    'allow_credentials' => true,
]);


Putting It All Together

Here is a minimal but complete authenticated REST API — health check, public registration, and a protected profile endpoint — in a single file:

<?php
require_once __DIR__ . '/vendor/autoload.php';

use Webrium\App;
use Webrium\Route;
use Webrium\Validator;
use Webrium\Hash;
use Webrium\Jwt;

App::initialize(__DIR__);
App::enableCors(['https://app.example.com']);

$jwt = new Jwt('super-secret-key');

// Health check
Route::get('/', fn() => ['status' => 'alive']);

// Public: register
Route::post('/register', function() {
    $v = new Validator(input());
    $v->field('email')->required()->email();
    $v->field('password')->required()->min(8);

    if ($v->fails()) {
        return respond(['errors' => $v->getErrors()], 422);
    }

    $hashed = Hash::make(input('password'));

    // save user to DB...

    return respond(['message' => 'Account created.'], 201);
});

// Public: login
Route::post('/login', function() use ($jwt) {
    $email    = input('email', '');
    $password = input('password', '');

    // fetch user from DB and verify password...
    $user         = ['id' => 1, 'email' => $email, 'hashed' => Hash::make('demo')];
    $passwordValid = Hash::check($password, $user['hashed']);

    if (!$passwordValid) {
        return respond(['error' => 'Invalid credentials.'], 401);
    }

    $token = $jwt->generateToken(['sub' => $user['id'], 'exp' => time() + 3600]);

    return ['token' => $token];
});

// Protected routes
$authMiddleware = function() use ($jwt) {
    $header = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
    $token  = str_replace('Bearer ', '', $header);
    return $jwt->verifyToken($token) !== null;
};

Route::group(['prefix' => 'api', 'middleware' => $authMiddleware], function() use ($jwt) {
    Route::get('/profile', function() use ($jwt) {
        $token   = str_replace('Bearer ', '', $_SERVER['HTTP_AUTHORIZATION'] ?? '');
        $payload = $jwt->verifyToken($token);
        return ['user_id' => $payload['sub']];
    });
});

App::run();

A complete, production-ready authenticated API service. No generated boilerplate, no hidden magic — just PHP.


webrium/core is a good fit when:

  • You're building a webhook receiver or a focused REST API
  • You need minimal cold-start time (serverless, edge environments)
  • You want zero third-party runtime dependencies
  • You're prototyping quickly and don't want framework overhead

It is not a replacement for a full-stack framework when you need an ORM,
a template engine, queue workers, or a large ecosystem of packages.

If you need all of that — but still want to stay in the Webrium ecosystem —
webrium/webrium is the full-stack
version, built on top of this same core. It ships with
FoxDB as its ORM and includes a
dedicated template engine. Same philosophy, full feature set.


Source Code

The project is open source and available on GitHub:

github.com/webrium/core

Contributions, issues, and feedback are welcome.