| Home | Getting Started | Core Concepts | Helpers | Extensions | Repo |
Tiny is built on a small set of opinions. This page explains what they are and why.
TINY_AUTOLOAD_HELPERS and call tiny::stripe(). Or don’t, and they cost you nothing.HX-Push-Url, HTMX-aware redirects, server-rendered partials — Tiny was designed around HTMX patterns.tiny::renderReact() gives you SSR + SPA from one controller. No Next.js, no separate Node process.TinyModel::isValid()) but it’s not the kind of static type safety you’d get from Doctrine entities or Eloquent casts.| Tiny | Laravel | Slim / Lumen | |
|---|---|---|---|
| Routes | Filesystem | Declarative + closures | Closures / controllers |
| ORM | None (raw SQL helpers) | Eloquent | None / your choice |
| Templates | PHP + components/layouts | Blade | PHP / Twig |
| HTMX | First-class (HX-Push-Url, redirects) |
Via packages | Manual |
| React | Built-in SSR + SPA (renderReact) |
Inertia | Manual |
| Scheduler | Built-in, second-level | Built-in, minute-level | External |
| Runtime modes | FPM / Swoole / FrankenPHP | FPM (+ Octane) | FPM |
| Helpers | 30+ first-party | Via Composer | Via Composer |
| Composer deps | Minimal (dotenv, cron-expr) | Many | Minimal |
| Learning curve | Hours | Days | Hours |
No DI container. tiny::cache(), tiny::db(), etc. are static accessors that return singletons. This is deliberate: it’s grep-friendly, has zero boot cost, and makes the dependency graph visible at a glance. The cost is that you can’t trivially swap implementations in tests — but the framework’s surface is small enough that we lean on integration tests rather than mocking.
No ORM. A thin PDO wrapper covers 90% of CRUD; the rest is raw SQL. Tiny doesn’t try to abstract over database differences (SERIAL vs AUTO_INCREMENT, RETURNING vs lastInsertId()). If you target one database, that’s a feature; if you target three, you write the dialect-specific code yourself.
No route table. The filesystem mapping is fast and obvious — you can always grep for a URL to find the controller. The cost is that there’s no good way to attach metadata to a route (rate limit tiers, custom middleware, route names). Middleware is global, ordering is in app/middleware.php, and per-route gating is done inside the controller.
HTMX-aware response object. $response->render() automatically emits HX-Push-Url. This is the right default for the workflows Tiny targets, but it means responses are not purely “send these bytes” — they make some decisions for you. Override with explicit tiny::header() calls when needed.
Helpers are opt-in. TINY_AUTOLOAD_HELPERS controls which helper files are loaded. The framework boots without any of them, so unused integrations cost nothing. The flip side is that helpers don’t appear in static analysis until they’re loaded.
tiny::*, TinyController, TinyModel, TinyRequest, TinyResponse, extension accessors).These are likely directions, not commitments:
TINY_* prefix handling (today some legacy variables are read without the prefix)If any of these matter to you, open an issue.
Tiny started as the framework powering aroussi.com and a handful of internal projects. The brief was: “build a website without spending three days configuring a framework first.” Everything in Tiny is there because a real project needed it, and nothing is there because some other framework had it.
The design ethos is closest in spirit to early Sinatra (Ruby) or modern Hono (Go/TS): a small, sharp tool that gets out of your way.