PHPackages                             decodelabs/greenleaf - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. decodelabs/greenleaf

ActiveLibrary[HTTP &amp; Networking](/categories/http)

decodelabs/greenleaf
====================

Super-fast directory based HTTP router

v0.10.5(6mo ago)11.7k3MITPHPPHP ^8.4CI passing

Since Nov 9Pushed 5mo ago2 watchersCompare

[ Source](https://github.com/decodelabs/greenleaf)[ Packagist](https://packagist.org/packages/decodelabs/greenleaf)[ RSS](/packages/decodelabs-greenleaf/feed)WikiDiscussions develop Synced 1mo ago

READMEChangelog (10)Dependencies (20)Versions (45)Used By (3)

Greenleaf
=========

[](#greenleaf)

[![PHP from Packagist](https://camo.githubusercontent.com/c516468b522c9f2b792dfdbe5a650b7fc82772f53df55d7db574b717889c9531/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f6465636f64656c6162732f677265656e6c6561663f7374796c653d666c6174)](https://packagist.org/packages/decodelabs/greenleaf)[![Latest Version](https://camo.githubusercontent.com/36aa12b8bc3156c7307139741ef20b7fd0d05cf4927886c488d896bd7575c80c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6465636f64656c6162732f677265656e6c6561662e7376673f7374796c653d666c6174)](https://packagist.org/packages/decodelabs/greenleaf)[![Total Downloads](https://camo.githubusercontent.com/f36fc30aacee66b0b2e77e20d2496e34c06ebf18c80e746580abb3ccabd06867/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6465636f64656c6162732f677265656e6c6561662e7376673f7374796c653d666c6174)](https://packagist.org/packages/decodelabs/greenleaf)[![GitHub Workflow Status](https://camo.githubusercontent.com/2174344b910987eccef1b1b79251e3b9d76174930d948a3ff575282216adfb7f/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6465636f64656c6162732f677265656e6c6561662f696e746567726174652e796d6c3f6272616e63683d646576656c6f70)](https://github.com/decodelabs/greenleaf/actions/workflows/integrate.yml)[![PHPStan](https://camo.githubusercontent.com/e25c14ce011edabdd0fbd2e10415b41cc5d66ed11ef3e5b7edd074c5bdd35a2d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d656e61626c65642d3434434331312e7376673f6c6f6e6743616368653d74727565267374796c653d666c6174)](https://github.com/phpstan/phpstan)[![License](https://camo.githubusercontent.com/268b13853b0816c5100ae417c96f34efc0f31a2b4e9d6e382bae1b59b3d95332/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6465636f64656c6162732f677265656e6c6561663f7374796c653d666c6174)](https://packagist.org/packages/decodelabs/greenleaf)

### Super-fast directory based HTTP router

[](#super-fast-directory-based-http-router)

Greenleaf provides a fast and flexible way to route HTTP requests to your actions and pages.

---

Installation
------------

[](#installation)

This package requires PHP 8.4 or higher.

Install via Composer:

```
composer require decodelabs/greenleaf
```

Usage
-----

[](#usage)

The main entry point to use Greenleaf is a PSR-15 middleware that can be used with any PSR-15 compatible framework, such as [Harvest](https://github.com/decodelabs/harvest). It will parse the request and attempt to match it against a set of configured routes.

The system is comprised of a set of generators that can scan and find available routes, a collection of flexible route types and an extensible dispatcher architecture that allows for different matching strategies.

### Dispatcher

[](#dispatcher)

While the dispatcher is usually accessed through middleware, you can if needed instantiate the Dispatcher directly and treat it as a standard PSR HTTP Handler.

```
use DecodeLabs\Harvest;
use DecodeLabs\Monarch;

$greenleaf = Monarch::getService(Greenleaf::class);
$harvest = Monarch::getService(Harvest::class);

$dispatcher = $greenleaf->createDispatcher();
$request = $harvest->createRequestFromEnvironment();
$response = $dispatcher->handle($request);
```

### Namespace

[](#namespace)

The router will use [Archetype](https://github.com/decodelabs/archetype) to resolve Action classes from the URL - you can use Archetype's namespace mapping functionality to mount a directory for Http related classes:

```
use DecodeLabs\Archetype;
use MyApp\Http;

$archetype = Monarch::getService(Archetype::class);
$archetype->alias('DecodeLabs\\Greenleaf\\*', Http::class);
```

If your app is based on the [Fabric](https://github.com/decodelabs/fabric) framework, this mapping is taken care of for you automatically, based on the app namespace in your config.

### Generators

[](#generators)

Generators are used to load and configure routes. The `Generator` interface defines a simple mechanism for implementations to find and load routes.

By default, Greenleaf will load a `Scanner` Generator which in turn will scan the directory tree for other Generators and load the Actions they provide.

To define Routes in your directory tree, you can start with a generic `Routes` Generator which expects routes to be defined by hand:

```
namespace MyApp\Http;

use DecodeLabs\Greenleaf\Generator;
use DecodeLabs\Greenleaf\Route\Action;
use DecodeLabs\Greenleaf\Route\Parameter;
use DecodeLabs\Greenleaf\Route\Redirect;

class Routes implements Generator
{
    public function generateRoutes(): iterable
    {
        // Basic route
        yield new Action('/', 'home');

        // Basic route with parameter
        yield new Action('test/{slug}', 'test')

        // Route with inset parameters
        yield new Action('test-{slug}/', 'test?hello', parameters: [
            new Parameter\Slug('name')
        ])

        // Route with multi-part path parameters
        yield new Action('assets/{path}', 'assets', parameters: [
            new Parameter\Path('path')
        ]);

        // Redirect
        yield new Redirect('old/path', 'new/path');
    }
}
```

### Router

[](#router)

When the Dispatcher runs, it loads an appropriate `Router` to take care of matching the request to the configured routes.

As of the current release, Greenleaf uses a generated switch based `PatternSwitch` Router that is extremely fast and efficient. There is also a `CheckEach` Router that runs a brute force loop over each route in turn - this is a very stable and predictable model, but is not recommended for production use.

When a Router implementation finds a match, it transforms the route pattern into a custom "leaf URL" in the format `leaf:/path/to/file?query#fragment` and a set of parameters that are then passed to the target of the route.

This is one of Greenleaf's biggest strengths as both input and output forms resolve to URL formats that can be easily looked up and matched against, both when matching *in* and when generating *out*.

### Leaf URL

[](#leaf-url)

The URL format is mostly just a subset of HTTP URLs, with `leaf` as the scheme, standard path, query and fragment components.

For example:

```
// Route
new Action('test/{slug}', 'test?hello');

// Creates URL
$greenleaf->url('leaf:/test?hello');
// $parameters = ['slug' => 'value-of-slug-in-request']

// Resolves to:
$actionClass = MyApp\Http\Test::class;

// --------------------------
// Or
new Action('blog/articles', 'blog/articles');

// Creates URL
$greenleaf->url('leaf:/blog/articles');

// Resolves to:
$actionClass = MyApp\Http\Blog\Articles::class;
```

### Routes

[](#routes)

There are currently three types of route available in Greenleaf:

- **Action**: A route that maps to an Action class. The action must implement the `DecodeLabs\Greenleaf\Action` interface and provides the most flexibility in responding to requests
- **Page**: A route that maps to page components, in the same vein as the likes of Next.js. `PageAction` adapters handle loading different types of page content
- **Redirect**: A route that redirects the request to a different URL. This is useful for handling legacy URLs or for redirecting to a different domain

#### Action

[](#action)

Actions need to implement an `execute(DecodeLabs\Greenleaf\Request $request)` method, and return a Response. Greenleaf provides a number of traits that can be used to add additional functionality and simplify writing logic for your views.

The `ByMethodTrait` for example will attempt to invoke a method on the Action based on the HTTP method of the request.

Note that most traits that work in this fashion will use `Slingshot` to invoke the method with deep dependency injection support. In this example, the slug from the matched route request URL is passed as a string to the action handlers.

```
namespace MyApp\Http;

use DecodeLabs\Harvest\Response\Text as TextResponse;
use DecodeLabs\Greenleaf\Action;
use DecodeLabs\Greenleaf\Action\ByMethodTrait;

class Test implements Action
{
    use ByMethodTrait;

    public function get(
        string $slug
    ): TextResponse {
        return new TextResponse('Get response');
    }

    public function post(
        string $slug
    ): TextResponse {
        return new TextResponse('Post response');
    }
}
```

Actions don't necessarily need to return PSR-7 responses directly - Greenleaf uses [Harvest's](https://github.com/decodelabs/harvest) response transformation system to convert all types of content responses to full PSR-7 HTTP responses.

#### Page

[](#page)

A page route is usually resolved to a content file or other type of component. Greenleaf provides a basic HTML file adapter that will load a file from the filesystem and return it as a response, however other packages provide more complex implementations, such as [Horizon](https://github.com/decodelabs/horizon) `Page` and `Fragment` component structures.

When a page route is matched, the file path is resolved using [Monarch's](https://github.com/decodelabs/monarch) path alias system, from a base path of `@pages`. This alias if not defined by your app, defaults to the `@run/src/components/pages` directory.

```
...

use DecodeLabs\Greenfleaf\Route\Page;

// HTML file /src/components/pages/about.html
yield new Page('about', 'about.html');

// Horizon Page /src/components/pages/blog.php
yield new Page('blog', 'blog.php');
```

### HTTP URLs

[](#http-urls)

One of the main benefits of Greenleaf is that it allows you to generate URLs for your routes in a simple and flexible way by creating leaf URLs with many of the required URL constructs already in place.

The router will then be able to match these URLs to the correct route and pass the parameters to the HTTP URL generator.

```
// route pattern: test/{slug}

$url = $greenleaf->url(
    'test?hello#fragment',
    ['slug' => 'my-slug']
);

// https://mydomain.localtest.me/test/my-slug?hello#fragment
```

Licensing
---------

[](#licensing)

Greenleaf is licensed under the MIT License. See [LICENSE](./LICENSE) for the full license text.

###  Health Score

43

—

FairBetter than 91% of packages

Maintenance69

Regular maintenance activity

Popularity17

Limited adoption so far

Community12

Small or concentrated contributor base

Maturity63

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~17 days

Recently: every ~12 days

Total

43

Last Release

201d ago

PHP version history (2 changes)v0.1.0PHP ^8.1

v0.4.0PHP ^8.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/8a241d64d12b3b5ee94197862ec1ec30b82ed2efa34a0cd7f4c3565a021daddd?d=identicon)[betterthanclay](/maintainers/betterthanclay)

---

Top Contributors

[![betterthanclay](https://avatars.githubusercontent.com/u/1273586?v=4)](https://github.com/betterthanclay "betterthanclay (174 commits)")

---

Tags

actionhttpmiddlewarephppsr-15router

### Embed Badge

![Health badge](/badges/decodelabs-greenleaf/health.svg)

```
[![Health](https://phpackages.com/badges/decodelabs-greenleaf/health.svg)](https://phpackages.com/packages/decodelabs-greenleaf)
```

###  Alternatives

[cakephp/cakephp

The CakePHP framework

8.8k18.5M1.6k](/packages/cakephp-cakephp)[thecodingmachine/graphqlite

Write your GraphQL queries in simple to write controllers (using webonyx/graphql-php).

5723.1M30](/packages/thecodingmachine-graphqlite)[neos/flow

Flow Application Framework

862.0M451](/packages/neos-flow)[neos/flow-development-collection

Flow packages in a joined repository for pull requests.

144179.3k3](/packages/neos-flow-development-collection)[mezzio/mezzio-authentication-oauth2

OAuth2 (server) authentication middleware for Mezzio and PSR-7 applications.

28483.0k2](/packages/mezzio-mezzio-authentication-oauth2)[windwalker/framework

The next generation PHP framework.

25639.1k1](/packages/windwalker-framework)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
