torann/promise
Torann/Promise provides a lightweight promise implementation for Laravel and PHP, letting you defer work and chain success or failure callbacks through a simple, fluent API. It helps organize asynchronous-style logic while keeping application code readable and easy to manage.
Build vs. buy — settle it at the capability level, not the package level. Any request for torann/promise is really a request for one of two capabilities: role/permission gating, or chained deferred work. Both have maintained, standard answers in the Laravel ecosystem. That reframes the package from "candidate dependency" to "effort reference point," and forces the useful governance decision — one vetted library per capability — instead of re-litigating the choice feature by feature for every team that finds a different package.
Scope definition for access control. Read as the roles-and-permissions package its metadata advertises, it sketches the MVP vocabulary almost by accident: users, roles, permissions, pivot tables, a middleware check, a Blade directive. That's the floor, not the ceiling. Everything the product actually needs — per-workspace or per-team roles, guard-specific permissions, wildcard inheritance, permission caching, audit history, delegated admin ("invite a teammate as Editor") — is scope we must consciously add and price. Using this as the minimum-viable reference converts "how hard is permissions?" from an abstract argument into a concrete, costable list. If an admin panel or plan-based feature gating is anywhere on the near-term roadmap, that capability is a prerequisite and it is cheap; it should neither block a release nor be hand-built.
A hard boundary on what "asynchronous" means in this stack. Read as the promise library its documentation describes, the package invites a product-level mistake worth naming in writing: PHP-FPM requests are synchronous. Chained then()/catch() buys readable control flow and one error handler inside a request — not background execution, retries, durability, or anything that survives the response. The decision it cannot support is "we'll defer the slow part to a promise." PDF exports, third-party syncs, webhook fan-out, and report generation belong on queues and jobs. A PM who lets this blur ships a feature that demos beautifully on a small dataset, times out in production, and gets "fixed" by adding another promise.
A blocking ambiguity that is itself a finding. Name says promise; description says roles and permissions; the repo says something else again. No acceptance criterion can be written against a package whose advertised capability depends on which document you read. Treat "what does this actually do?" as a precondition, and treat the mismatch as evidence of abandonment viewed from the outside: README, metadata, and code have drifted apart with nobody home to reconcile them.
A cheap signal that justifies a governance rule. Zero stars, zero dependents, no visible CI, no security disclosure path, Laravel 5-only, EOL since 2019. The cost of letting two half-reviewed packages into the codebase is paid later by whoever untangles them. Saying no here — and documenting why — is cheaper than saying yes.
Consider it only under narrow, largely historical conditions:
composer require on any modern upgrade path.Look elsewhere in essentially every other case:
try/catch and Http::pool()/dispatch() for control flow and deferred work. The boring answer wins when a chain is three lines in one class.all() fans out sequentially in a synchronous runtime; its payoff is shape and single-point error handling, not latency. race() is misleading absent real I/O promises.catch, or a catch returning null — the observable symptom is an empty page rather than an error, and that debugging tax lands on whoever is on call. If rejection propagation doesn't behave on the first try, stop rather than build on guesses.For executives: This package is not a dependency recommendation — it's a decision prompt. It advertises two different capabilities in two different documents, has no active maintainers, no tests, no security disclosure path, and targets a framework version we left behind in 2019. Adopting it means adopting source code we own forever. The recommendation is to decline it and instead standardize on one maintained library per capability: authorization, and background work. That standardization is a one-time decision that saves us from re-arguing this in every sprint, and it costs days rather than sprints.
For engineering: Two things to internalize. First, resolve the ambiguity before writing code — clone the repo, read composer.json and the src class list, and confirm whether it implements permissions or promises. Two teams reading the two documents will otherwise walk away with different architectures. Pin the answer in docs/ so nobody re-litigates it. Second, if we ever do touch it, wrap it behind an internal interface (DeferredWork or AuthorizationService) so controllers depend on our contract, not on Torann\Promise. With zero upstream dependents, that adapter is not over-engineering — it's the only thing that makes the dependency survivable, and it makes the eventual swap a one-class change rather than forty call sites.
The line to hold: nothing here is asynchronous. A promise chain inside a request gives readable control flow and one error handler. It does not give background execution, retries, or durability. When someone proposes deferring slow work to a promise, the answer is dispatch(). Getting this wrong is the most expensive failure this package enables, and it fails in production at scale rather than in the demo.
How can I help you explore Laravel packages today?