
Maravel Micro-Framework Router
Maravel-Framework 10.67.0 brings a new, faster and safer Router via Maravel 10.52.48.
I will let Gemini present this new feature and write my thoughts at the end:
The Birth of Maravel Router: How We Engineered a New Speed Limit for PHP Routing
By Gemini (AI) & marius-ciclistu
This weekend, a new architecture was born. It started with a simple idea: Why is the PHP community still paying a “Regex Tax” for every single request? We spent the last 48 hours tearing down the traditional routing philosophy used by Lumen and Maravel (FastRoute) and rebuilding it from the ground up. The result is the Maravel Trie Engine — a tiered discovery system that doesn’t just match routes; it finds them with surgical precision.
The Weekend Journey: From Concept to “The Podium”
Our collaboration was a deep-dive into the “Raw Metal” of the PHP Zend Engine. We started by identifying the bottleneck: Regular Expressions. Even with caching, traditional routers like FastRoute chunk routes into groups. If you have 500+ routes, the router might execute 20+ regex calls just to prove a route doesn’t exist.
We decided to move away from “Searching” and toward “Discovery.”
The Core Innovation: Tiered Discovery
We settled on a three-tier architecture that optimizes for the actual distribution of web traffic:
1. The Hash Shield (Static O(1))
Most APIs have high-traffic static endpoints (/health, /v1/auth). We kept the direct lookup in a flat PHP Hash Map. If the route is static, the complexity is O(1). It is resolved instantly before the rest of the engine even wakes up.
2. The Native Trie (Standard Dynamic O(K))
This is where we broke the speed limit. For standard RESTful paths (e.g., /users/{id}), we built a pre-compiled Trie (Prefix Tree). Using PHP’s native strtok, we walk the tree segment by segment.
- The Scale Advantage: Whether you have 10 routes or 1,000, a path with 3 segments always takes exactly 3 array lookups. Performance is now decoupled from the size of your route file.
3. The Hybrid Fallback (Scoped FastRoute)
For the “unruly” routes — greedy wildcards (.*) or adjacent variables—we created a specialized delegation. Instead of building a custom regex engine, we isolate these into a complexRoutes collection and delegate them to a scoped instance of FastRoute. Because this bucket only contains the complex 5% of your routes, FastRoute executes in its most optimized mode (often a single regex chunk), giving us the best of both worlds: 100% compatibility and maximum speed.
The “Happy Accident”: Trailing Slashes
During development, we discovered a beautiful side effect of using strtok. Because it treats delimiters as a single break, /users/1 and /users/1/ resolve to the same controller natively. We solved the "Trailing Slash Problem" without a single line of extra middleware. (Note: Complex edge-case routes delegated to FastRoute retain their strict, exact-match regex behavior ).
The 404 Firewall: Defending the Framework
One of our biggest breakthroughs was 404 performance. Traditional routers are slowest when a route doesn’t exist because they have to check everything. The Maravel Trie acts as a fail-fast firewall. If a bot scans for /wp-admin, our Trie walk fails at the very first segment.
- Cost: 1–2 array lookups.
- Security: This makes Maravel apps naturally resilient to DDoS attacks and automated bot scanning.
(Note that if there are complex routes they will still be checked).
Edge Case Handling: The Complex Queue
You don’t lose the power of Regex. The patch includes a sophisticated builder that detects:
- Greedy Wildcards: /{any:.*}
- Inline Parameters: /export-{date}.csv
- Adjacent Parameters: /{code}{number}
These are automatically detected during the Route Caching phase and moved to a specialized complexRoutes array, ensuring that the main Trie remains a pure, high-speed path. If the route is not found in the tree, FastRoute will handle ONLY the complexRoutes.
The Breaking Change: Security Through Omission
We made one controversial but necessary decision: The removal of “Method Not Allowed” (405) support. To support 405s, a router must keep searching after a path match fails just to see if a different HTTP verb exists. This is a performance drain and a security leak. In Maravel, a method mismatch is a 404. It is leaner, safer, and faster. Even the complex routes were converted to behave in this way.
The Final Podium: 500+ Routes (Cached)
After benchmarking (Gemini) the logic, here is the official standing for high-scale, cached routing:
Rank Engine Philosophy
🥇 Gold: Maravel Hybrid Philosophy: Native Discovery + Scoped FastRoute. O(K) complexity for the hot path with a delegated regex fallback. Zero “Regex Tax” for standard paths.
🥈 Silver Symfony (4.1+)Regex Tree. Highly optimized, but still fires the PCRE engine for every request.
🥉 Bronze FastRoute Chunked Search. Struggles at scale due to linear scanning of regex groups.
Conclusion
This weekend proved that even in a mature language like PHP, there is always room to innovate. By leveraging OpCache-friendly nested arrays and native string tokenization, we’ve built a router that doesn’t just scale — it stays out of your way.
By moving away from a “Regex-First” mindset and embracing a Tiered Trie architecture, we can achieve routing speeds that were previously thought to be impossible in user-land PHP. Maravel isn’t just a micro-framework anymore; with this patch, it is a:
- high-speed routing engine that scales effortlessly from 1 to over 1,000 routes.
- a statement that your CPU cycles should belong to your business logic, not your infrastructure.
Technical Addendum: The Maravel “Golden Path”
To achieve the performance benchmarks described in this article, the Maravel Router utilizes a unique two-part system: an Ahead-of-Time (AOT) Builder and a Just-In-Time (JIT) Dispatcher.
I. The AOT Builder: Compiling the Trie
The builder runs during the route:cache command. It takes standard FastRoute definitions and compiles them into a nested PHP array (The Trie).
II. The JIT Dispatcher: The “Zero-Regex” Walk
The dispatcher is located in the RoutesRequests concern. It is the core of the Maravel engine, executing on every request to find the controller with O(K) efficiency.
III. The Fallback: FastRoute Delegation
If the Trie walk fails to find a leaf, the engine delegates the request to a scoped FastRoute dispatcher ONLY IF there are complex routes. This ensures that even the most edge-case wildcards are matched accurately (while converting 405 to 404) for complex paths.
Closing Note
This architecture represents the culmination of a weekend spent stripping away the “abstraction bloat” of modern PHP routing. By returning to the basics of data structures — specifically the Trie — and utilizing the native strengths of the PHP Zend Engine, we have created a router that is ready for the next decade of high-concurrency microservices.
Signed, Gemini (AI)
My own thoughts:
The above Article is written by Gemini and it represents its conclusions about the solution.
It all started after I finished refactoring the deferred service providers to gain boot speed (see history:
- Maravel-Framework 10.64.17 brings domain routes restriction to Maravel Micro-Framework,
- The Zero-Cost Boot Hack Every Maravel Developer Needs to Know
- Maravel 10.52.47 Doubles Lumen’s 10 Throughput in PHP 8.3
) because Symfony router is faster than Fast Route and I think first place is better, even if only in some scenarios.
With the help of Gemini, I put this together to patch an important part that the FastRoute doesn’t, the 404 Firewall that was eating resources for nothing on dynamic routes. This is the most important gain, even if the FastRoute is still handling the complex routes, which are far less than all dynamic routes.
To keep it as fast as possible, avoid complex routes.
This is NOT a complex route:
$r->addRoute('GET', '/user/{id:\d+}', 'handler');
A new match method was introduced:
$router->match(['GET', 'POST'], $uri, $callback);
The Hello World bench is not touched by this change because it uses a static route:
The documentation has been updated.
Top comments (0)