PHPackages                             grogorick/php-routing - 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. [Framework](/categories/framework)
4. /
5. grogorick/php-routing

ActiveLibrary[Framework](/categories/framework)

grogorick/php-routing
=====================

Minimal PHP routing with zero dependencies

2.2.1(1y ago)023PHP

Since Jun 21Pushed 1y ago1 watchersCompare

[ Source](https://github.com/grogorick/php-routing)[ Packagist](https://packagist.org/packages/grogorick/php-routing)[ RSS](/packages/grogorick-php-routing/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependenciesVersions (11)Used By (0)

Minimal PHP Routing
===================

[](#minimal-php-routing)

Set callbacks for hierarchical routes in a simple associative array structure.

Setup
-----

[](#setup)

```
composer require grogorick/php-routing
```

Example
-------

[](#example)

```
# api.php

require_once 'vendor/autoload.php';
use Grogorick\PhpRouting as R;

...

function get_feed($GET_data) {
  $feed_data = ...
  if ($feed_data)
    R\respond($feed_data);
  else
    R\respond(null, R\Response::ERROR_NOT_FOUND);
}

...

R\route([
  'v1' => [
    'sign-in' => [
      'POST' => 'App\Auth\sign_in'
    ],
    '(authenticated)' => R\Check('App\Auth\verify_authorization_header', [
      'accounts' => R\Entity([
        'POST' => fn() => R\respond('create account'),
        'GET' => fn() => R\respond('list accounts'),
        '/\w+/' => fn($account_slug) => R\Item([
          'GET' => fn() => R\respond('get account ' . $account_slug),
          'PUT' => fn() => R\respond('replace account ' . $account_slug),
          'PATCH' => fn() => R\respond('update account ' . $account_slug),
          'DELETE' => fn() => R\respond('delete account ' . $account_slug)
        ]),
        ...
      ]),
      'posts' => R\Entity([
        /* post_id */ '/\d+/' => R\IntParam([
          'GET' => fn($post_id) => R\respond('get post ' . $post_id),
          ...
        ]),
        ...
      ]),
      'feed' => [
        'GET' => fn($GET_data) => get_feed($GET_data),
        ...
      ],
      ...
    ]),
    ...
  ],
  ...
]);
```

Routes Array Syntax
-------------------

[](#routes-array-syntax)

`route-literal => [subroutes array]`
static *route literal* string =&gt; *subroutes array*

`/regex/ => subroutes function`
*regex* to match a URL parameter =&gt; *subroutes function* getting the parsed parameter, generating a subroutes array

`(subroutes group) => subroutes function`
*subroutes group* name in parentheses =&gt; *subroutes function* generating a subroutes array

`METHOD => callable`
request *METHOD* in full uppercase =&gt; action *callable* to be called for this route

Action callables will be called with the following arguments:

> *URL parameters* — in their order withing the respective route
> *form data* — via `$_POST` or `file_get_contents('php://input')`
> *search parameters* — via `$_GET`

Reference
---------

[](#reference)

> `route($routes)`
> Main function to parse the current request URL and call the corresponding callback function.
> **$routes** (associative array)
> — route literal string =&gt; subroute array
> — route parameter regex =&gt; closure with parsed parameter as argument, returning a subroute array
> — request method (POST/GET/PUT/PATCH/DELETE) =&gt; callback function

> `respond($response, $code = 200)`
> Helper function to output the retrieved response as JSON-encoded string, and set an optional status code. Stops execution afterwards.
> **$response** (any) — JSON-serializable response object
> **$code** (int) — HTTP response status code

> `Check($check, $subroutes)`
> Wrapper for subroutes array with restricted access.
> **$check** (callable) — callback function to check for access permission
> **$subroutes** — see *route($routes)*

> `respond_error_if_no_other_route_matches($error)`
> Report error during `Check(...)` without directly stopping routing.
> **$error** (string) — error message

> `Param($convert, $subroutes)`
> Wrapper for subroutes array to convert a parsed url parameter.
> **$convert** (callable) — callback function to convert the latest parameter
> **$subroutes** — see *route($routes)*

> `IntParam($subroutes)`
> Shorthand for `Param('intval', $subroutes)`
> **$subroutes** — see *route($routes)*

> `Entity($subroutes)`
> `Item($subroutes)`
> Wrapper for subroutes array to generate recommended response status codes for undefined request methods.
> **$subroutes** — see *route($routes)*

> `set_response_headers($headers)`
> `add_response_header($header)`
> Replace/add headers that are automatically applied when using *respond(...)*.
> **$headers** (array) — headers to replace all default/previously set headers
> **$header** (string) — header to add

> `set_options($options)`
> Set options available in *\\Options*.
> **$options** (associative array) — options to set, using values from *\\Options* as array keys

Server Configuration
--------------------

[](#server-configuration)

### Additional Request Methods

[](#additional-request-methods)

By default, most Apache configurations only allow *GET* and *POST* requests. Add the following to allow further methods (*PUT, PATCH, DELETE, HEAD, OPTIONS*).

```
# .htaccess

    Require all granted

Header always set Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS"
```

For this to work, the `httpd.conf` should include

```

        ...
        AllowOverride All
        ...
```

### Response Headers

[](#response-headers)

Response headers can be set either via PHP, which allows to set them dynamically, e.g., to support multiple specific origins:

```
R\set_response_headers([
  "Access-Control-Allow-Origin: $approved_request_origin",
  'Access-Control-Allow-Headers: content-type',
  'Content-Type: application/json; charset=UTF-8'
]);
```

or via `.htaccess` if static settings are sufficient:

```
Header always set Access-Control-Allow-Origin "https://your-app.domain"
Header always set Access-Control-Allow-Headers "content-type"
Header always set Content-Type "application/json; charset=UTF-8"
```

### URL Syntax

[](#url-syntax)

#### Short

[](#short)

https://your-api.domain **/v1/accounts/42**

```
# .htaccess

    RewriteEngine on
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ /api.php/$1 [L,QSA]

```

#### Medium

[](#medium)

https://your-api.domain **/api.php/v1/accounts/42**
*(pre-configured on most systems)*

```
# httpd.conf

    ...
    AcceptPathInfo On
    ...
```

or

```
# .htaccess
AcceptPathInfo On
```

#### Long

[](#long)

https://your-api.domain **/api.php?request=/v1/accounts/42**
*(vanilla PHP)*

###  Health Score

26

—

LowBetter than 43% of packages

Maintenance33

Infrequent updates — may be unmaintained

Popularity6

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity50

Maturing project, gaining track record

 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 ~42 days

Recently: every ~87 days

Total

10

Last Release

676d ago

Major Versions

1.4.0 → 2.0.02023-07-30

### Community

Maintainers

![](https://www.gravatar.com/avatar/b8afa529c650501916be53d7bce7baccd5aed9bc6672c07607fafc4cf4862649?d=identicon)[grogorick](/maintainers/grogorick)

---

Top Contributors

[![grogorick](https://avatars.githubusercontent.com/u/96060961?v=4)](https://github.com/grogorick "grogorick (42 commits)")

---

Tags

composerregexrouting

### Embed Badge

![Health badge](/badges/grogorick-php-routing/health.svg)

```
[![Health](https://phpackages.com/badges/grogorick-php-routing/health.svg)](https://phpackages.com/packages/grogorick-php-routing)
```

###  Alternatives

[laravel/telescope

An elegant debug assistant for the Laravel framework.

5.2k67.8M192](/packages/laravel-telescope)[spiral/roadrunner

RoadRunner: High-performance PHP application server and process manager written in Go and powered with plugins

8.4k12.2M84](/packages/spiral-roadrunner)[nolimits4web/swiper

Most modern mobile touch slider and framework with hardware accelerated transitions

41.8k177.2k1](/packages/nolimits4web-swiper)[laravel/dusk

Laravel Dusk provides simple end-to-end testing and browser automation.

1.9k36.7M259](/packages/laravel-dusk)[laravel/prompts

Add beautiful and user-friendly forms to your command-line applications.

708181.8M596](/packages/laravel-prompts)[cakephp/chronos

A simple API extension for DateTime.

1.4k47.7M121](/packages/cakephp-chronos)

PHPackages © 2026

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