Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Framework Laravel Package

pinatra/framework

Pinatra Framework is a Laravel-based foundation for building modular PHP applications. It offers reusable components and conventions for structuring code, extending functionality, and maintaining web projects, helping teams develop faster with a consistent architecture.

View on GitHub
Deep Wiki
Context7

Getting Started

Pinatra is a single-class, static-DSL micro-framework. If you think "Laravel route closures without the container," you have the right mental model.

Install:

composer require pinatra/framework

Minimal app (front controller index.php):

require __DIR__ . '/vendor/autoload.php';

use Pinatra\Pinatra as P;

P::get('/', function () {
    return 'Hello world';
});

P::get('/hello/:name', function ($name) {
    return "Hello $name";
});

P::run();

Point your web server's rewrite rules at index.php (Apache .htaccess or an nginx try_files $uri /index.php?$query_string;), and you have a working app. P::run() must be the last statement — it dispatches the current request and emits the response.

Where to look first:

  1. README.md on the repo — the canonical syntax reference; it is short and worth reading end-to-end.
  2. src/Pinatra/Pinatra.php — the whole framework is essentially this one file. Skim it once; you'll understand every behavior from it.
  3. The test suite under tests/ — the most reliable documentation of edge cases (route params, filters, view rendering).

First useful use case: a small JSON endpoint or internal webhook receiver. Pinatra is strongest when you need 50–500 lines of routing and a couple of templates, deployed as a standalone vhost, without pulling in a full framework.

Day-one checklist:

  • Set P::config('views', __DIR__ . '/views') before your first P::render().
  • Set the environment (P::config('environment', 'development') or 'production') so error output is sane.
  • Register P::notFound() and P::error() handlers immediately — leaving them default is the fastest way to leak stack traces in production.

Implementation Patterns

Verbs and parameters. Standard Sinatra-style routing:

P::get('/users/:id', function ($id) { /* ... */ });
P::post('/users', function () { /* ... */ });
P::put('/users/:id', function ($id) { /* ... */ });
P::delete('/users/:id', function ($id) { /* ... */ });

Matched segments are passed positionally to the closure. Wildcards (/files/*) pass the remainder.

Filters as middleware. Pinatra has no middleware stack; before/after filters are the substitute. Use them for auth, headers, and request logging:

P::before(function () {
    header('X-App: my-app');
});

P::before('/admin/*', function () {
    if (!isset($_SESSION['user'])) {
        P::halt(401, 'Unauthorized');
    }
});

P::after(function () {
    // runs after the route; good for cleanup/logging
});

Filters can be pattern-scoped (P::before('/admin/*', ...)) and run in registration order — treat that ordering as your middleware pipeline.

Control-flow primitives. These are the three you'll reach for daily:

  • P::halt($status, $body) — stop immediately and emit a response. This is your abort().
  • P::redirect($url, $status = 302) — must be followed by return; if anything comes after it.
  • P::pass() — decline the current route and let routing continue. Useful for conditional/fallback handlers.

Views. Enable rendering by configuring a views directory, then render templates with data:

P::config('views', __DIR__ . '/views');

P::get('/profile/:id', function ($id) {
    return P::render('profile.php', ['id' => $id]);
});

Return the rendered string from the route closure (or echo it) — Pinatra does not auto-wrap output. There is no Blade; templates are plain PHP. Escape output yourself (htmlspecialchars($x, ENT_QUOTES, 'UTF-8')); there is no auto-escaping safety net.

Config and settings. P::config($key, $value) sets values consumed by the framework (views path, environment). Keep app-specific constants in your own config.php required at bootstrap rather than overloading Pinatra's config namespace — it's untyped and string-keyed.

Custom helpers. Register reusable closures and call them through the facade:

P::helper('json', function ($data, $status = 200) {
    http_response_code($status);
    header('Content-Type: application/json');
    return json_encode($data);
});

P::get('/api/ping', function () {
    return P::json(['ok' => true]);
});

Helpers are the idiomatic extension point — use them instead of global functions.

Error handling. Map these once at bootstrap:

P::notFound(function () {
    return P::render('404.php');
});

P::error(function ($e) {
    error_log($e->getMessage());
    return P::render('500.php');
});

Embedding in a larger app. Because Pinatra uses static global state and dispatches the request itself, the practical integration is a separate front controller (a subdirectory or dedicated vhost) rather than trying to mount it inside an existing Laravel/Symfony kernel. If you must share auth, share the session and a small user-lookup helper, not the frameworks' internals.

Testing. There is no request/response object to inject, so test by driving the front controller. A pragmatic pattern is to extract route registration into routes.php, require it in both index.php and tests, then exercise endpoints over HTTP via curl/Guzzle against a PHP built-in server, or capture P::run() output with output buffering and set $_SERVER['REQUEST_METHOD'] / REQUEST_URI before dispatch.

Suggested project layout:

public/index.php        # bootstrap, config, P::run()
app/routes.php          # all route registrations
app/helpers.php         # P::helper definitions
app/filters.php         # P::before / P::after
views/                  # plain PHP templates

Gotchas and Tips

  • Maintenance risk. The last release was September 2018. Expect friction on PHP 8+: dynamic properties, curly-brace string offsets, and each()-era idioms may surface as deprecations or warnings. Pin it in composer.json, run the test suite on your target PHP version before committing, and be prepared to patch Pinatra.php locally if needed.
  • Global static state. Pinatra is a static facade with process-wide state. You cannot run two apps in one process, and tests can bleed configuration between cases. Isolate tests in separate processes or reset static state in tearDown by reflecting on the class if you need to reconfigure.
  • P::run() is terminal and non-idempotent. Call it once, last. Calling it twice, or calling it from inside a route closure, will produce confusing behavior.
  • Route order matters. First match wins; there is no specificity sorting. Register static routes (/users/new) before parameterized ones (/users/:id) or the parameterized route will swallow them.
  • P::config vs P::set vs P::helper. The README uses config for framework settings. Don't assume any of these are validated — typos in keys fail silently. Confirm the exact method names for your pinned version against Pinatra.php rather than trusting blog posts, since forks of the original Breeze API drifted.
  • Return vs echo. Route closures that return are handled cleanly; closures that echo mid-route can produce headers-already-sent errors and interleaved output. Pick one convention (prefer return) and enforce it in code review.
  • redirect() does not exit. Always return; after it, otherwise subsequent code in the closure keeps running.
  • halt() inside a before filter is the correct way to reject a request. Do not exit() — you'll skip after filters and your cleanup.
  • Debugging. In development, set the environment to development so errors are visible; in production, register P::error() and log via error_log() or your PSR-3 logger, because Pinatra has no built-in logging channel. When routing "fails," check the rewrite rules and REQUEST_URI before suspecting the framework — misconfigured path info is the most common cause of 404s.
  • Sessions and headers. Start session_start() before P::run() if you need sessions. Because routes can emit output, any code that sets headers must run before any echo.
  • No auto-escaping, CSRF, validation, or ORM. Everything Laravel gives you is your responsibility here. Use PDO/your preferred query layer directly and add CSRF tokens manually on any form handling.
  • Extension strategy. Prefer helpers and filters over forking the class. If you need real structure (DI, middleware, validation), that's the signal to graduate to Slim or a full framework — Pinatra's sweet spot is a few hundred lines, not a growing product.
  • Adoption tip. The low "opportunity" score reflects a small, quiet ecosystem. That's fine for internal tools and side projects; for anything long-lived, budget for vendoring the framework or a fork you control, since upstream fixes are unlikely.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource