Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
05c225d
feat: update level-2/dice
GuilhermeBarile Sep 10, 2025
7382255
fix: remove uninitialized vars warnings
GuilhermeBarile May 24, 2025
b3a8b79
feat: Update to level-2/dice v4
GuilhermeBarile Sep 29, 2025
5593b3e
feat: Add Middleware support
GuilhermeBarile Sep 29, 2025
902464d
fix: description
GuilhermeBarile Oct 12, 2025
2f2c4a9
feat: support templates/_index.php
GuilhermeBarile Dec 20, 2025
2028498
Move Middleware to use DI
GuilhermeBarile Dec 21, 2025
7e841e8
Add Router/Template
GuilhermeBarile Dec 21, 2025
d0ee3a4
Update docs
GuilhermeBarile Dec 21, 2025
f98b83a
New Middleware syntax
GuilhermeBarile Dec 22, 2025
8cb5e4a
Pass matched parameters to the Controller constructor
GuilhermeBarile Dec 29, 2025
d1b9200
Improve Template logic
GuilhermeBarile Dec 29, 2025
54d9195
fix: don't use url() for complete redirect urls
GuilhermeBarile Jan 3, 2026
6010f25
Use objectiveweb/Dice
GuilhermeBarile Apr 19, 2026
8063050
fix: composer.json
GuilhermeBarile Apr 19, 2026
66acd8b
Fix Template and Middleware types
GuilhermeBarile Mar 3, 2026
bafe5b5
Encode non-string responses as JSON
objectivebot Sep 27, 2026
55267a1
Build v3 CI on GitHub Actions
objectivebot Sep 28, 2026
2acf376
Fix Composer autoload namespace
objectivebot Sep 28, 2026
08115d5
Configure Objectiveweb Dice repository
objectivebot Sep 28, 2026
510cb04
Only render array responses through templates
objectivebot Sep 28, 2026
5fc0fee
Fix root controller path matching
objectivebot Sep 28, 2026
d7f0d2e
Use JMS attributes in example model
objectivebot Sep 28, 2026
44a52ea
Treat route parameters as strings in tests
objectivebot Sep 28, 2026
e7121be
Drop PHP 8.0 support
objectivebot Sep 28, 2026
77e7181
Require PHPUnit 10+
objectivebot Sep 28, 2026
45058fb
Route all responses through respond()
objectivebot Sep 28, 2026
db806dc
Cover object controller responses
objectivebot Sep 28, 2026
24860ba
Fix template fallback and default root
objectivebot Sep 28, 2026
1c6e3c6
Avoid reloading shared test router
objectivebot Sep 28, 2026
7bfdd67
Avoid reloading shared test router
objectivebot Sep 28, 2026
1f349b8
Harden middleware execution
objectivebot Sep 28, 2026
d57c4c9
Prepare v3 release metadata and verification
objectivebot Sep 28, 2026
e953756
Parse request bodies by Content-Type
objectivebot Sep 28, 2026
232f3d4
Declare JSON content type in controller tests
objectivebot Sep 28, 2026
cad25df
Complete response content negotiation
objectivebot Sep 28, 2026
1db39cb
Fix negotiation docblock syntax
objectivebot Sep 28, 2026
591ecfa
Fix serializer namespace escaping
objectivebot Sep 28, 2026
a710e3d
Catch all throwables at route boundary
objectivebot Sep 29, 2026
54a9972
Use published Objectiveweb Dice 4.1
objectivebot Sep 29, 2026
66aff42
Validate typed request body media types
objectivebot Sep 29, 2026
16c7eb3
Preserve error status during negotiation
objectivebot Sep 29, 2026
6d31f2e
Fail CI on PHP deprecations
objectivebot Sep 29, 2026
f7b37cd
Update GitHub Actions checkout
objectivebot Sep 29, 2026
2fc5be3
Finalize release workflow triggers
objectivebot Sep 29, 2026
e429202
Avoid duplicate CI runs on pull request branches
objectivebot Sep 29, 2026
fb473a9
Unify verb helper callback dispatch
objectivebot Sep 29, 2026
a7ac2fb
Refresh v3 documentation and examples
objectivebot Sep 29, 2026
c55eb8d
Fix middleware Dice instantiation
objectivebot Sep 29, 2026
58bbb48
Narrow Router create API
objectivebot Sep 29, 2026
3852622
Document regex controller paths
objectivebot Sep 29, 2026
1f2fb71
Document middleware termination semantics
objectivebot Sep 30, 2026
76f7b5a
Add global request middleware and CORS
objectivebot Sep 30, 2026
f0b03b1
Run request middleware before route matching
objectivebot Sep 30, 2026
1120b95
Clarify global request middleware lifecycle
objectivebot Sep 30, 2026
1431fe2
Simplify CORS middleware response handling
objectivebot Sep 30, 2026
6340fbe
Revert "Simplify CORS middleware response handling"
objectivebot Sep 30, 2026
374a741
Complete basic HTTP method semantics
objectivebot Sep 30, 2026
fda8886
Harden template rendering
objectivebot Sep 30, 2026
d7ab704
Make template buffer cleanup robust
objectivebot Sep 30, 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
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CI

on:
push:
branches:
- master
pull_request:

jobs:
test:
name: PHP ${{ matrix.php }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php:
- '8.1'
- '8.2'
- '8.3'
- '8.4'
- '8.5'

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
tools: composer:v2

- name: Validate Composer metadata
run: composer validate --strict

- name: Install dependencies
run: composer update --prefer-dist --no-interaction --no-progress

- name: Run tests
run: composer test
62 changes: 62 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Release verification

on:
push:
tags:
- 'v*'
workflow_dispatch:

jobs:
test:
name: PHP ${{ matrix.php }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php:
- '8.1'
- '8.2'
- '8.3'
- '8.4'
- '8.5'

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
tools: composer:v2

- name: Validate Composer metadata
run: composer validate --strict

- name: Install dependencies
run: composer update --prefer-dist --no-interaction --no-progress

- name: Run tests
run: composer test

production-install:
name: Production install
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
coverage: none
tools: composer:v2

- name: Validate Composer metadata
run: composer validate --strict

- name: Verify production dependencies
run: composer install --no-dev --prefer-dist --no-interaction --no-progress
15 changes: 0 additions & 15 deletions .travis.yml

This file was deleted.

162 changes: 162 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# Changelog

All notable changes to Objectiveweb Router are documented in this file.

## [3.0.0] - Unreleased

### Breaking changes

- `create()` no longer exposes Dice's internal third `share` argument; the supported Router DI API is `create(string $name, array $args = []): object`.
- Require PHP 8.1 or newer.
- Router now composes Dice instead of extending it. Dependency injection remains available through `addRule()` and `create()`, but inherited Dice methods are no longer part of the Router API.
- Replace controller `before()` / `beforePost()` style hooks with attribute-based middleware.
- Middleware `before()` hooks must return the complete controller argument array.
- Method-level middleware replaces broader middleware of the same class; repeated middleware at the same scope is preserved and executed in declaration order.
- Responses are routed through `respond()`, including objects with registered serializers and exceptions.
- Controller object responses bypass template lookup unless they are arrays intended as template data.
- The default template directory is now resolved from the Composer application root as `<project>/templates`.
- The supported PHPUnit baseline is PHPUnit 10+.

### Added

- `PATCH()` route helper with the same Content-Type-aware request-body handling as POST and PUT.
- Automatic HEAD fallback for `GET()` helpers and controller GET actions.
- DI-backed global request middleware with declaration-order `before()` hooks before route matching and reverse-order `after()` unwinding when a route produces a response.
- Built-in `CorsMiddleware` for global CORS headers and terminating OPTIONS preflight requests.
- GitHub Actions CI for PHP 8.1 through 8.5.
- Dedicated release verification workflow for version tags.
- Repeatable middleware execution with reverse-order `after()` unwinding.
- Regression coverage for controller object responses, middleware ordering, template fallback, template root resolution, serializer response dispatch, and current controller routing behavior.
- MIT license.

### Changed

- HEAD responses preserve GET representation semantics while suppressing the response body; 1xx, 204, 205, and 304 responses never carry a body.
- Documentation now explicitly defines Objectiveweb Router as an immediate regex dispatcher rather than a route-table dispatcher.
- `setCors()` now registers the built-in request-level `CorsMiddleware`; controller-specific CORS branching has been removed.
- GitHub Actions workflows now use `actions/checkout@v7`, removing the deprecated Node 20 action runtime warning.
- Test execution now fails on PHP/PHPUnit deprecations so the supported PHP matrix remains deprecation-clean.
- Response negotiation now honors `Accept` media ranges, q-values, wildcards, and q=0 exclusions; HTML/JSON responses use explicit content types and unsupported requests receive 406.
- Require Objectiveweb Dice `^4.1.0`.
- Update JMS Serializer development compatibility to `^3.32`.
- Example JMS metadata now uses PHP attributes.
- Non-string response bodies are JSON-encoded before output.
- Route matching for the root controller now handles nested paths correctly.
- Composer metadata is validated with `composer validate --strict` in CI and release verification.

### Fixed

- Template rendering now uses typed paths/return values, `EXTR_SKIP` variable extraction, protected layout contents, and exception-safe output-buffer cleanup.
- Middleware instantiation no longer passes a synthetic string through Dice's internal `share` argument; repeated middleware remain distinct while normally configured shared dependencies are still reused.
- Public README, controller/middleware documentation, and runnable examples now describe the v3 API and no longer reference legacy Dice includes or removed controller hooks.
- `GET()`, `POST()`, `PUT()`, and `DELETE()` now share the same callback resolution and Throwable boundary as `route()`, including Dice-backed class callbacks and request argument preparation.
- Example controller dependencies are explicitly declared properties, removing the PHP 8.2+ dynamic-property deprecation.
- Default Throwable responses now negotiate HTML or JSON without replacing the original 4xx/5xx status with 406 for HTML clients.
- Route execution now catches all PHP `Throwable` failures, including `TypeError`/`Error`, normalizes invalid exception codes to HTTP 500, and includes dependency-injection/callback resolution inside the HTTP error boundary.
- Request body parsing now follows `Content-Type`: JSON (including `+json` media types), URL-encoded forms, multipart forms, and raw/unknown bodies are handled explicitly.
- Class-typed controller request bodies now require a JSON media type, return 415 for unsupported/missing `Content-Type`, and return 400 for malformed JSON instead of surfacing as a server error.
- HTTP-method template fallback no longer uses an accidental variable-variable expression.
- Controller responses that are not arrays no longer reach the array-only template renderer.
- Overridden `respond()` methods work again through late static binding.
- Middleware without an `after()` method no longer crashes response processing.

## Migrating from 2.x

Version 3 is intentionally breaking and does not provide a compatibility layer for 2.x applications.

### PHP

Update the runtime to PHP 8.1 or newer:

```json
{
"require": {
"php": ">=8.1",
"objectiveweb/router": "^3.0"
}
}
```

### Dependency injection

Router no longer extends `Dice\Dice`.

Continue using the Router-level DI methods:

```php
$router->addRule(Service::class, [
'shared' => true,
]);

$service = $router->create(Service::class);
```

Code that called other inherited Dice methods on the Router should create/configure dependencies through the supported Router API instead.

The Router-level `create()` method accepts only the class name and explicit constructor arguments. Dice's internal object-graph `share` argument is intentionally not part of the Router API.

### Controller hooks and middleware

Controller methods such as `before()` and `beforePost()` are no longer invoked automatically. Move request/response interception to middleware attributes:

```php
use Objectiveweb\Router\Middleware;

#[Middleware(AuthenticationMiddleware::class)]
class Controller
{
#[Middleware(AuditMiddleware::class)]
public function index(array $query)
{
// ...
}
}
```

A middleware `before()` method must return the complete controller argument array:

```php
public function before(string $method, string $fn, array $params): array
{
return $params;
}
```

`after()` is optional. When present, it receives the controller response and may transform it. After hooks execute in reverse middleware order.

### Responses and serializers

Response representation is now negotiated from the request `Accept` header. Controller array results with a matching template can be rendered as `text/html` or returned as `application/json`; missing `Accept` behaves like `*/*` and prefers HTML when a template is available. Requests that reject all available representations receive HTTP 406.

Default error responses support both HTML and JSON representations, so an existing 4xx/5xx status is preserved for clients accepting either representation. A request that accepts neither can still receive HTTP 406.

All routed responses now enter the common `respond()` pipeline. Custom subclasses overriding `respond()` therefore see normal responses, registered-serializer responses, and exceptions.

A renderable object may return either a completed string body or a structured non-string value. Non-string values are JSON-encoded by the router.

### Request bodies

Controller methods whose final body parameter is a class are automatically deserialized with JMS Serializer only for `application/json` and `application/*+json` requests. Other or missing media types receive HTTP 415. Malformed JSON receives HTTP 400.

Array-typed body parameters continue to use the router's Content-Type-aware parser. Unknown media types are preserved as raw bodies where no typed DTO deserialization is requested.

### Templates

The default template directory is now:

```text
<composer project root>/templates
```

Passing an explicit Router root continues to override this default.

Controller templates receive array responses as their data context. Other response types bypass template lookup and continue through the response pipeline.

### Tests and development

The development test suite supports PHP 8.1-8.5 and PHPUnit 10-12. Run:

```bash
composer validate --strict
composer test
```
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) Guilherme Barile

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading
Loading