Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
02971c6
Allow to create route instance directly
rustamwin Nov 6, 2023
7848f14
Adjust Group
rustamwin Nov 8, 2023
39f05ff
Simplify route classes & add route builders
rustamwin Nov 9, 2023
626151c
Apply fixes from StyleCI
StyleCIBot Nov 9, 2023
3390ea9
Minor improvements
rustamwin Nov 9, 2023
0b0806d
Merge remote-tracking branch 'origin/new-syntax' into new-syntax
rustamwin Nov 9, 2023
a6b7acd
Improvements
rustamwin Nov 9, 2023
bdaec2c
Merge branch 'master' into new-syntax
rustamwin Nov 9, 2023
33f3003
Apply fixes from StyleCI
StyleCIBot Nov 9, 2023
782bf91
Fix
rustamwin Nov 9, 2023
4530d76
Merge remote-tracking branch 'origin/master' into new-syntax
rustamwin Oct 21, 2025
9c2fa2a
Fix tests
rustamwin Oct 21, 2025
ef3eb15
Fix cs
rustamwin Oct 21, 2025
a9af466
Update dependencies, replace redundant `isArrayList` polyfill with ar…
rustamwin Nov 13, 2025
2bc1672
Apply Rector changes (CI)
rustamwin Nov 13, 2025
f025e9d
Update dev dependencies in composer.json
rustamwin Nov 13, 2025
98c0890
Merge remote-tracking branch 'origin/master' into new-syntax
rustamwin Jan 15, 2026
e8d6831
Merge remote-tracking branch 'origin/master' into new-syntax
rustamwin Apr 15, 2026
f26efb4
Refactor tests and route logic: align types, fix assertions, and impr…
rustamwin Apr 15, 2026
c9bacd1
Apply PHP CS Fixer and Rector changes (CI)
rustamwin Apr 15, 2026
2e737fa
Refactor: optimize middleware handling, improve property initializati…
rustamwin Apr 18, 2026
51fca55
Refactor middleware handling: ensure action is included in enabledMid…
rustamwin Apr 18, 2026
3149e61
Fix issues
rustamwin May 31, 2026
f166108
Merge branch 'master' into new-syntax
rustamwin Jul 16, 2026
0a6a801
Merge remote-tracking branch 'origin/master' into new-syntax
rustamwin Jul 29, 2026
7834d97
Apply PHP CS Fixer and Rector changes (CI)
github-actions[bot] Jul 29, 2026
fd88652
Fix route collection mutation leaks
samdark Jul 29, 2026
275471c
Update documentation for route builders
samdark Jul 29, 2026
dfdd2ac
Apply PHP CS Fixer and Rector changes (CI)
github-actions[bot] Jul 29, 2026
a821dcf
Improve mutation test coverage
samdark Jul 29, 2026
bdd3b49
Remove unrelated refactoring changes
samdark Jul 29, 2026
2791884
Keep route factory syntax compatible
samdark Aug 5, 2026
139161b
Apply PHP CS Fixer and Rector changes (CI)
github-actions[bot] Aug 5, 2026
e1d7688
Restore deprecated current route host getter
samdark Aug 5, 2026
dfe7a31
Reduce route data object diff
samdark Aug 5, 2026
20fe6ec
Apply PHP CS Fixer and Rector changes (CI)
github-actions[bot] Aug 5, 2026
4bb70d7
Use compatible route syntax in tests
samdark Aug 5, 2026
b5f8e4c
Preserve fluent builder compatibility
samdark Aug 5, 2026
f5691f4
Clarify builder usage and upgrade guidance
samdark Aug 5, 2026
2871cf8
Merge branch 'master' into new-syntax
samdark Aug 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 0 additions & 18 deletions .phpstorm.meta.php/Group.php

This file was deleted.

19 changes: 0 additions & 19 deletions .phpstorm.meta.php/Route.php

This file was deleted.

6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Yii Router Change Log

## 5.0.0 under development

- Chg #225: Introduce immutable route and group builders, make `Route` and `Group` mutable data objects, add
`RoutableInterface`, replace generic data accessors with explicit getters, and retain static factories as
builder facades (@rustamwin)

## 4.0.3 under development

- Enh #276, #292: Explicitly import classes, functions, and constants in the "use" section (@rustamwin, @vjik)
Expand Down
88 changes: 76 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,14 @@ application middleware processes the request.

### Routes

Route could match for one or more HTTP methods: `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS`. There are
corresponding static methods for creating a route for a certain method. If a route is to handle multiple methods at once,
it could be created using `methods()`.
Define routes with the static methods on `Route`. They return an immutable builder that the route collector accepts
directly. A route could match one or more HTTP methods: `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS`.
There are corresponding static methods for creating a route for a certain method. If a route is to handle multiple
methods at once, it could be created using `methods()`.

```php
use Yiisoft\Router\Route;
use Yiisoft\Http\Method;

Route::delete('/post/{id}')
->name('post-delete')
Expand Down Expand Up @@ -176,6 +178,7 @@ Besides action, additional middleware to execute before the action itself could

```php
use Yiisoft\Router\Route;
use Yiisoft\Http\Method;

Route::methods([Method::GET, Method::POST], '/page/add')
->middleware(Authentication::class)
Expand All @@ -200,14 +203,34 @@ Route::get('/special')
->override();
```

Every fluent configuration method returns a new builder. The collector converts route builders to `Route` objects while
building the collection. Most applications do not need to import or convert builders directly.

For configuration generated dynamically, a mutable `Route` data object may be constructed directly:

```php
use Yiisoft\Http\Method;
use Yiisoft\Router\Route;

$route = new Route(
methods: [Method::GET, Method::POST],
pattern: '/page/add',
name: 'page-add',
action: [PageController::class, 'actionAdd'],
);

$route->setHosts(['https://example.com']);
```

### Route groups

Routes could be grouped. That is useful for API endpoints and similar cases:
Create route groups with the static `Group::create()` method. It returns an immutable builder and is useful for API
endpoints and similar cases:

```php
use \Yiisoft\Router\Route;
use \Yiisoft\Router\Group;
use \Yiisoft\Router\RouteCollectorInterface;
use Yiisoft\Router\Group;
use Yiisoft\Router\Route;
use Yiisoft\Router\RouteCollectorInterface;

// for obtaining router see adapter package of choice readme
$collector = $container->get(RouteCollectorInterface::class);
Expand All @@ -233,6 +256,49 @@ and `disableMiddleware()`. These middleware are executed prior to matched route'

If host is specified, all routes in the group would match only if the host match.

Every fluent group configuration method returns a new builder, which the collector accepts directly. Most applications
do not need to import or convert group builders. A mutable `Group` data object can also be constructed directly:

```php
use Yiisoft\Router\Group;

$group = new Group(
prefix: '/api',
namePrefix: 'api/',
routes: [$route],
middlewares: [ApiAuthentication::class],
);
```

### Custom route definitions

`RouteCollectorInterface::addRoute()` accepts `Route`, `Group`, and `RoutableInterface` instances. Implement
`RoutableInterface` when an application or package needs its own route-definition abstraction:

```php
use Yiisoft\Http\Method;
use Yiisoft\Router\RoutableInterface;
use Yiisoft\Router\Route;

final class HealthCheckRoute implements RoutableInterface
{
public function toRoute(): Route
{
return new Route(
methods: [Method::GET],
pattern: '/health',
name: 'health',
action: HealthCheckAction::class,
);
}
}

$collector->addRoute(new HealthCheckRoute());
```

The route collection clones the `Route` or `Group` returned by `toRoute()` before applying collection and group
configuration, so implementations may safely return a retained object.

### Automatic OPTIONS response and CORS

By default, router responds automatically to OPTIONS requests based on the routes defined:
Expand All @@ -246,8 +312,8 @@ Generally that is fine unless you need [CORS headers](https://developer.mozilla.
case, you can add a middleware for handling it such as [tuupola/cors-middleware](https://github.com/tuupola/cors-middleware):

```php
use Tuupola\Middleware\CorsMiddleware;
use Yiisoft\Router\Group;
use \Tuupola\Middleware\CorsMiddleware;

return [
Group::create('/api')
Expand Down Expand Up @@ -344,7 +410,7 @@ modifying URLs for filtering and/or sorting.
For such a route:

```php
use \Yiisoft\Router\Route;
use Yiisoft\Router\Route;

$routes = [
Route::post('/post/{id:\d+}')
Expand All @@ -358,8 +424,6 @@ The information could be obtained as follows:
use Psr\Http\Message\ResponseInterface
use Psr\Http\Message\UriInterface;
use Yiisoft\Router\CurrentRoute;
use Yiisoft\Router\Route;

final class PostController
{
public function actionEdit(CurrentRoute $currentRoute): ResponseInterface
Expand All @@ -379,7 +443,7 @@ In addition to commonly used `getArgument()` method, the following methods are a

- `getArguments()` - To obtain all arguments at once.
- `getName()` - To get route name.
- `getHost()` - To get route host.
- `getHosts()` - To get route hosts.
- `getPattern()` - To get route pattern.
- `getMethods()` - To get route methods.
- `getUri()` - To get current URI.
Expand Down
128 changes: 128 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,134 @@
This file contains the upgrade notes for the Yii Router.
These notes highlight changes that could break your application when you upgrade it from one major version to another.

## 5.0.0

### Route and group builders

The immutable fluent APIs were moved from `Yiisoft\Router\Route` and `Yiisoft\Router\Group` to dedicated builder classes:

- `Yiisoft\Router\Builder\RouteBuilder`
- `Yiisoft\Router\Builder\GroupBuilder`

The static factory methods on `Route` and `Group` are retained as facades. They now return the corresponding builder
instead of a `Route` or `Group` data object.

- If you only use fluent route declarations such as `Route::get('/')->name('home')` and pass their results to
`RouteCollectorInterface::addRoute()`, then no changes are required.
- If you type a result of `Route::get()`, `Route::post()`, `Route::methods()`, or another route factory as `Route`, then
change the type to `RouteBuilder` or call `toRoute()` to obtain a `Route` data object.
- If you type a result of `Group::create()` as `Group`, then change the type to `GroupBuilder` or call `toRoute()` to
obtain a `Group` data object.
- If you check a fluent factory result with `instanceof Route` or `instanceof Group`, then check for the corresponding
builder instead, or call `toRoute()` before the check.
- If you call `getData()` on a fluent factory result, then call `toRoute()` and use an explicit getter on the resulting
data object.
- If you want imports to reflect the actual types returned by the factories, then import the builders directly. Aliases
allow route declarations to keep the familiar short names:

```php
use Yiisoft\Router\Builder\GroupBuilder as Group;
use Yiisoft\Router\Builder\RouteBuilder as Route;
```

Builders are immutable and can be passed directly to `RouteCollectorInterface::addRoute()`.

### `Route` changes

`Yiisoft\Router\Route` is now a mutable route data object with a public constructor:

```php
use Yiisoft\Http\Method;
use Yiisoft\Router\Route;

$route = new Route(
methods: [Method::GET],
pattern: '/',
name: 'home',
action: HomeAction::class,
);
```

The fluent configuration methods moved to `RouteBuilder`. The static construction methods remain available on `Route`
as facades that return a `RouteBuilder`.

`Route::getData()` was removed. Replace it with the corresponding explicit method:

| Before | After |
|---|---|
| `getData('name')` | `getName()` |
| `getData('pattern')` | `getPattern()` |
| `getData('host')` | `getHosts()[0] ?? null` |
| `getData('hosts')` | `getHosts()` |
| `getData('methods')` | `getMethods()` |
| `getData('defaults')` | `getDefaults()` |
| `getData('override')` | `isOverride()` |
| `getData('hasMiddlewares')` | `getMiddlewares() !== [] || getAction() !== null` |
| `getData('enabledMiddlewares')` | `getEnabledMiddlewaresAndAction()` |

The action is stored separately from route middleware. Use `getAction()` for the action,
`getEnabledMiddlewares()` for filtered middleware only, or `getEnabledMiddlewaresAndAction()` for the dispatch pipeline.
Mutable configuration is available through `setMethods()`, `setPattern()`, `setName()`, `setAction()`,
`setMiddlewares()`, `setDefaults()`, `setHosts()`, `setOverride()`, and `setDisabledMiddlewares()`.

### `Group` changes

`Yiisoft\Router\Group` is now a mutable group data object with a public constructor. The fluent configuration methods
moved to `GroupBuilder`. The static `create()` method remains available on `Group` as a facade that returns a
`GroupBuilder`.

`Group::getData()` was removed. Replace it with the corresponding explicit method:

| Before | After |
|---|---|
| `getData('prefix')` | `getPrefix()` |
| `getData('namePrefix')` | `getNamePrefix()` |
| `getData('host')` | `getHosts()[0] ?? null` |
| `getData('hosts')` | `getHosts()` |
| `getData('routes')` | `getRoutes()` |
| `getData('hasCorsMiddleware')` | `getCorsMiddleware() !== null` |
| `getData('corsMiddleware')` | `getCorsMiddleware()` |
| `getData('enabledMiddlewares')` | `getEnabledMiddlewares()` |

Mutable configuration is available through `setPrefix()`, `setNamePrefix()`, `setRoutes()`, `setMiddlewares()`,
`setHosts()`, `setCorsMiddleware()`, and `setDisabledMiddlewares()`.

### `RoutableInterface`

`RoutableInterface` was added for custom route definitions. Its `toRoute()` method must return a `Route` or `Group`.
The route collector and groups accept routable instances in addition to route and group data objects. The route
collection clones the returned object before applying collection middleware or group transformations.

### `RouteCollectorInterface` changes

`RouteCollectorInterface::addRoute()` now also accepts `RoutableInterface` instances.

If you call or implement `RouteCollectorInterface::getMiddlewareDefinitions()`, then rename the method to
`getMiddlewares()`:

```php
// Before
$collector->getMiddlewareDefinitions();

// After
$collector->getMiddlewares();
```

### `CurrentRoute` changes

`CurrentRoute::getHost()` is deprecated but remains functional and returns the first route host.

- If you only need the first route host, then no changes are required.
- If you need all route hosts, then use `CurrentRoute::getHosts()`:

```php
// Before
$host = $currentRoute->getHost();

// After
$hosts = $currentRoute->getHosts();
```

## 4.0.0

### `Route`, `Group` and `MatchingResult` changes
Expand Down
Loading
Loading