Instradamento

Il router si occupa di tutto ciò che riguarda gli URL, in modo da non doverci più pensare. Mostreremo:

  • come impostare il router in modo che gli URL abbiano l'aspetto desiderato
  • alcune note sul reindirizzamento SEO
  • e vi mostreremo come scrivere il vostro router personale

Gli URL più umani (o gli URL belli o cool) sono più utilizzabili, più memorabili e contribuiscono positivamente alla SEO. Nette ha in mente questo aspetto e soddisfa pienamente i desideri degli sviluppatori. Potete progettare la struttura degli URL per la vostra applicazione esattamente come volete. È possibile progettarla anche dopo che l'applicazione è pronta, senza dover modificare il codice o il modello. È definita in modo elegante in un unico punto, nel router, e non è sparsa sotto forma di annotazioni in tutti i presentatori.

Il router di Nette è speciale perché è bidirezionale, può sia decodificare gli URL delle richieste HTTP sia creare collegamenti. Svolge quindi un ruolo fondamentale nell'applicazione Nette, in quanto decide quale presentatore e quale azione eseguirà la richiesta corrente ed è anche utilizzato per la generazione di URL nel modello, ecc.

Tuttavia, il router non si limita a questo uso: è possibile utilizzarlo in applicazioni in cui i presentatori non vengono utilizzati affatto, per le API REST, ecc. Maggiori informazioni nella sezione Uso separato.

Raccolta di rotte

Il modo più piacevole per definire gli indirizzi URL nell'applicazione è attraverso la classe Nette\Application\Routers\RouteList. La definizione consiste in un elenco di cosiddette rotte, ossia maschere di indirizzi URL e di presentatori e azioni ad essi associati, utilizzando una semplice API. Non è necessario dare un nome alle rotte.

$router = new Nette\Application\Routers\RouteList;
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('article/<id>', 'Article:view');
// ...

L'esempio dice che se apriamo https://any-domain.com/rss.xml con l'azione rss, se https://domain.com/article/12 con l'azione view, ecc. Se non viene trovato un percorso adatto, Nette Application risponde lanciando un'eccezione BadRequestException, che appare all'utente come una pagina di errore 404 Not Found.

Ordine dei percorsi

L'ordine in cui sono elencate le rotte è molto importante perché vengono valutate in sequenza dall'alto verso il basso. La regola è quella di dichiarare le rotte dalla specifica alla generale:

// SBAGLIATO: 'rss.xml' corrisponde alla prima rotta e la interpreta come <slug>.
$router->addRoute('<slug>', 'Article:view');
$router->addRoute('rss.xml', 'Feed:rss');

// BUONO
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('<slug>', 'Article:view');

Le rotte vengono valutate dall'alto verso il basso anche quando vengono generati i collegamenti:

// SBAGLIATO: genera un link a 'Feed:rss' come 'admin/feed/rss'.
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
$router->addRoute('rss.xml', 'Feed:rss');

// BUONO
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');

Non vi nasconderemo che ci vuole una certa abilità per costruire correttamente un elenco. Finché non ci si riesce, il pannello di instradamento sarà uno strumento utile.

Maschera e parametri

La maschera descrive il percorso relativo basato sulla radice del sito. La maschera più semplice è un URL statico:

$router->addRoute('products', 'Products:default');

Spesso le maschere contengono i cosiddetti parametri. Essi sono racchiusi tra parentesi angolari (ad es. <year>) e vengono passati al presentatore di destinazione, ad esempio al metodo renderShow(int $year) o al parametro persistente $year:

$router->addRoute('chronicle/<year>', 'History:show');

L'esempio dice che se apriamo https://any-domain.com/chronicle/2020 e l'azione show con il parametro year: 2020.

Possiamo specificare un valore predefinito per i parametri direttamente nella maschera, che diventa così facoltativa:

$router->addRoute('chronicle/<year=2020>', 'History:show');

La rotta accetterà ora l'URL https://any-domain.com/chronicle/ con il parametro year: 2020.

Naturalmente, anche il nome del presentatore e l'azione possono essere un parametro. Ad esempio:

$router->addRoute('<presenter>/<action>', 'Home:default');

Questo percorso accetta, ad esempio, un URL nella forma /article/edit e /catalog/list e li traduce in presentatori e azioni Article:edit e Catalog:list.

Inoltre, assegna ai parametri presenter e action i valori predefinitiHome e default, che sono quindi facoltativi. Quindi il percorso accetta anche un URL /article e lo traduce in Article:default. O viceversa, un link a Product:default genera un percorso /product, un link al default Home:default genera un percorso /.

La maschera può descrivere non solo il percorso relativo basato sulla radice del sito, ma anche il percorso assoluto quando inizia con una barra, o addirittura l'intero URL assoluto quando inizia con due barre:

// percorso relativo alla radice del documento dell'applicazione
$router->addRoute('<presenter>/<action>', /* ... */);

// percorso assoluto, relativo al nome host del server
$router->addRoute('/<presenter>/<action>', /* ... */);

// URL assoluto che include il nome dell'host (ma relativo allo schema)
$router->addRoute('//<lang>.example.com/<presenter>/<action>', /* ... */);

// URL assoluto comprensivo di schema
$router->addRoute('https://<lang>.example.com/<presenter>/<action>', /* ... */);

Espressioni di validazione

È possibile specificare una condizione di validazione per ogni parametro utilizzando un 'espressione regolare. Per esempio, impostiamo id in modo che sia solo numerico, usando la regexp \d+:

$router->addRoute('<presenter>/<action>[/<id \d+>]', /* ... */);

L'espressione regolare predefinita per tutti i parametri è [^/]+, cioè tutto tranne lo slash. Se un parametro deve corrispondere anche a una barra, si imposta l'espressione regolare a .+.

// accetta https://example.com/a/b/c, il percorso è 'a/b/c'
$router->addRoute('<path .+>', /* ... */);

Sequenze opzionali

Le parentesi quadre indicano le parti opzionali della maschera. Qualsiasi parte della maschera può essere impostata come opzionale, comprese quelle contenenti parametri:

$router->addRoute('[<lang [a-z]{2}>/]<name>', /* ... */);

// URL accettati:      Parametri:
//   /en/download        lang => en, name => download
//   /download           lang => null, name => download

Naturalmente, quando un parametro fa parte di una sequenza opzionale, diventa anch'esso opzionale. Se non ha un valore predefinito, sarà nullo.

Le sezioni opzionali possono anche essere nel dominio:

$router->addRoute('//[<lang=en>.]example.com/<presenter>/<action>', /* ... */);

Le sequenze possono essere liberamente annidate e combinate:

$router->addRoute(
	'[<lang [a-z]{2}>[-<sublang>]/]<name>[/page-<page=0>]',
	'Home:default',
);

// URL accettati:
//   /en/hello
//   /en-us/hello
//   /hello
//   /hello/page-12

Il generatore di URL cerca di mantenere l'URL il più breve possibile, quindi ciò che può essere omesso viene omesso. Pertanto, ad esempio, un percorso index[.html] genera un percorso /index. È possibile invertire questo comportamento scrivendo un punto esclamativo dopo la parentesi quadra sinistra:

// accetta sia /hello che /hello.html, genera /hello
$router->addRoute('<name>[.html]', /* ... */);

// accetta sia /hello che /hello.html, genera /hello.html
$router->addRoute('<name>[!.html]', /* ... */);

I parametri opzionali (cioè quelli che hanno un valore predefinito) senza parentesi quadre si comportano come se fossero avvolti in questo modo:

$router->addRoute('<presenter=Home>/<action=default>/<id=>', /* ... */);

// equivale a:
$router->addRoute('[<presenter=Home>/[<action=default>/[<id>]]]', /* ... */);

Per cambiare il modo in cui viene generato lo slash più a destra, cioè invece di /home/ ottenere un /home, regolare il percorso in questo modo:

$router->addRoute('[<presenter=Home>[/<action=default>[/<id>]]]', /* ... */);

Caratteri jolly

Nella maschera del percorso assoluto, si possono usare i seguenti caratteri jolly per evitare, ad esempio, la necessità di scrivere un dominio nella maschera, che può essere diverso nell'ambiente di sviluppo e in quello di produzione:

  • %tld% = dominio di primo livello, ad esempio comorg
  • %sld% = dominio di secondo livello, ad es. example
  • %domain% = dominio senza sottodomini, ad es. example.com
  • %host% = intero host, ad es. www.example.com
  • %basePath% = percorso della directory principale
$router->addRoute('//www.%domain%/%basePath%/<presenter>/<action>', /* ... */);
$router->addRoute('//www.%sld%.%tld%/%basePath%/<presenter>/<action', /* ... */);

Notazione avanzata

Il secondo parametro della rotta, che spesso scriviamo nel formato Presenter:action, è un'abbreviazione, che possiamo scrivere anche sotto forma di campo, dove indichiamo direttamente i valori (predefiniti) dei singoli parametri:

$router->addRoute('<presenter>/<action>[/<id \d+>]', [
	'presenter' => 'Home',
	'action' => 'default',
]);

Oppure possiamo usare questa forma, notando la riscrittura dell'espressione regolare di validazione:

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>[/<id>]', [
	'presenter' => [
		Route::Value => 'Home',
	],
	'action' => [
		Route::Value => 'default',
	],
	'id' => [
		Route::Pattern => '\d+',
	],
]);

Questi formati più loquaci sono utili per aggiungere altri metadati.

Filtri e traduzioni

È buona norma scrivere il codice sorgente in inglese, ma cosa succede se è necessario che il sito web abbia un URL tradotto in una lingua diversa? Percorsi semplici come:

$router->addRoute('<presenter>/<action>', 'Home:default');

genereranno URL in inglese, come /product/123 o /cart. Se vogliamo che i presenter e le azioni nell'URL siano tradotti in tedesco (per esempio /produkt/123 o /einkaufswagen), possiamo usare un dizionario di traduzione. Per aggiungerlo, abbiamo già bisogno di una variante “più loquace” del secondo parametro:

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>', [
	'presenter' => [
		Route::Value => 'Home',
		Route::FilterTable => [
			// stringa nell'URL => presenter
			'produkt' => 'Product',
			'einkaufswagen' => 'Cart',
			'katalog' => 'Catalog',
		],
	],
	'action' => [
		Route::Value => 'default',
		Route::FilterTable => [
			'liste' => 'list',
		],
	],
]);

È possibile utilizzare più chiavi di dizionario per lo stesso presentatore. Esse creeranno diversi alias per esso. L'ultima chiave è considerata la variante canonica (cioè quella che sarà presente nell'URL generato).

In questo modo, la tabella di traduzione può essere applicata a qualsiasi parametro. Tuttavia, se la traduzione non esiste, viene preso il valore originale. Possiamo modificare questo comportamento aggiungendo Route::FilterStrict => true e la rotta rifiuterà l'URL se il valore non è presente nel dizionario.

Oltre al dizionario delle traduzioni sotto forma di array, è possibile impostare funzioni di traduzione proprie:

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>/<id>', [
	'presenter' => [
		Route::Value => 'Home',
		Route::FilterIn => function (string $s): string { /* ... */ },
		Route::FilterOut => function (string $s): string { /* ... */ },
	],
	'action' => 'default',
	'id' => null,
]);

La funzione Route::FilterIn converte il parametro dell'URL in stringa, che viene poi passata al presentatore, mentre la funzione FilterOut assicura la conversione in senso inverso.

I parametri presenter, action e module hanno già dei filtri predefiniti che convertono tra lo stile PascalCase o camelCase e kebab-case utilizzato nell'URL. Il valore predefinito dei parametri è già scritto nella forma trasformata, quindi, ad esempio, nel caso di un presentatore, si scrive <presenter=ProductEdit> invece di <presenter=product-edit>.

Filtri generali

Oltre ai filtri per parametri specifici, è possibile definire anche filtri generali che ricevono un array associativo di tutti i parametri che possono modificare in qualsiasi modo e poi restituire. I filtri generali sono definiti sotto il tasto null.

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>', [
	'presenter' => 'Home',
	'action' => 'default',
	null => [
		Route::FilterIn => function (array $params): array { /* ... */ },
		Route::FilterOut => function (array $params): array { /* ... */ },
	],
]);

I filtri generali danno la possibilità di regolare il comportamento del percorso in qualsiasi modo. Si possono usare, ad esempio, per modificare i parametri in base ad altri parametri. Ad esempio, la traduzione <presenter> e <action> in base al valore corrente del parametro <lang>.

Se per un parametro è stato definito un filtro personalizzato e contemporaneamente esiste un filtro generale, il filtro personalizzato FilterIn viene eseguito prima del generale e viceversa il generale FilterOut viene eseguito prima del personalizzato. Quindi, all'interno del filtro generale si trovano i valori dei parametri presenter risp. action scritti in stile PascalCase risp. camelCase.

Bandiera unidirezionale

Le rotte a senso unico sono utilizzate per preservare la funzionalità di vecchi URL che l'applicazione non genera più, ma che accetta ancora. Le segnaliamo con OneWay:

// vecchio URL /product-info?id=123
$router->addRoute('product-info', 'Product:detail', $router::ONE_WAY);
// nuovo URL /product/123
$router->addRoute('product/<id>', 'Product:detail');

Quando si accede al vecchio URL, il presentatore reindirizza automaticamente al nuovo URL, in modo che i motori di ricerca non indicizzino queste pagine due volte (vedere SEO e canonizzazione).

Instradamento dinamico con callback

L'instradamento dinamico con callback consente di assegnare direttamente delle funzioni (callback) alle rotte, che verranno eseguite quando viene visitato il percorso specificato. Questa funzione flessibile consente di creare in modo rapido ed efficiente vari endpoint per l'applicazione:

$router->addRoute('test', function () {
	echo 'You are at the /test address';
});

È anche possibile definire parametri nella maschera, che verranno passati automaticamente alla callback:

$router->addRoute('<lang cs|en>', function (string $lang) {
	echo match ($lang) {
		'cs' => 'Welcome to the Czech version of our website!',
		'en' => 'Welcome to the English version of our website!',
	};
});

Moduli

Se abbiamo più rotte che appartengono a un modulo, possiamo usare withModule() per raggrupparle:

$router = new RouteList;
$router->withModule('Forum') // i seguenti router fanno parte del modulo Forum
	->addRoute('rss', 'Feed:rss') // il presentatore è Forum:Feed
	->addRoute('<presenter>/<action>')

	->withModule('Admin') // i seguenti router fanno parte del modulo Forum:Admin
		->addRoute('sign:in', 'Sign:in');

Un'alternativa è usare il parametro module:

// L'URL manage/dashboard/default si riferisce al presentatore Admin:Dashboard
$router->addRoute('manage/<presenter>/<action>', [
	'module' => 'Admin',
]);

Sottodomini

Le raccolte di rotte possono essere raggruppate per sottodomini:

$router = new RouteList;
$router->withDomain('example.com')
	->addRoute('rss', 'Feed:rss')
	->addRoute('<presenter>/<action>');

È inoltre possibile utilizzare i caratteri jolly nel nome del dominio:

$router = new RouteList;
$router->withDomain('example.%tld%')
	// ...

Prefisso di percorso

Le raccolte di rotte possono essere raggruppate per percorso nell'URL:

$router = new RouteList;
$router->withPath('eshop')
	->addRoute('rss', 'Feed:rss') // corrisponde all'URL /eshop/rss
	->addRoute('<presenter>/<action>'); // corrisponde all'URL /eshop/<presenter>/<action>

Combinazioni

L'uso sopra descritto può essere combinato:

$router = (new RouteList)
	->withDomain('admin.example.com')
		->withModule('Admin')
			->addRoute(/* ... */)
			->addRoute(/* ... */)
		->end()
		->withModule('Images')
			->addRoute(/* ... */)
		->end()
	->end()
	->withDomain('example.com')
		->withPath('export')
			->addRoute(/* ... */)
			// ...

Parametri della query

Le maschere possono contenere anche parametri di query (parametri dopo il punto interrogativo nell'URL). Non possono definire un'espressione di validazione, ma possono cambiare il nome con cui vengono passati al presentatore:

// utilizzare il parametro di query 'cat' come 'categoryId' nell'applicazione
$router->addRoute('product ? id=<productId> & cat=<categoryId>', /* ... */);

Parametri Foo

Ora stiamo andando più a fondo. I parametri Foo sono fondamentalmente parametri senza nome che permettono di corrispondere a un'espressione regolare. Il percorso seguente corrisponde a /index, /index.html, /index.htm e /index.php:

$router->addRoute('index<? \.html?|\.php|>', /* ... */);

È anche possibile definire esplicitamente una stringa che sarà usata per la generazione dell'URL. La stringa deve essere posta direttamente dopo il punto interrogativo. Il percorso seguente è simile al precedente, ma genera /index.html invece di /index, perché la stringa .html è impostata come “valore generato”.

$router->addRoute('index<?.html \.html?|\.php|>', /* ... */);

Integrazione

Per collegare il nostro router all'applicazione, dobbiamo comunicarlo al contenitore DI. Il modo più semplice è preparare il factory che costruirà l'oggetto router e dire al contenitore di configurazione di usarlo. Diciamo quindi di scrivere un metodo a questo scopo App\Core\RouterFactory::createRouter():

namespace App\Core;

use Nette\Application\Routers\RouteList;

class RouterFactory
{
	public static function createRouter(): RouteList
	{
		$router = new RouteList;
		$router->addRoute(/* ... */);
		return $router;
	}
}

Poi scriviamo nella configurazione:

services:
	- App\Core\RouterFactory::createRouter

Tutte le dipendenze, come la connessione al database, ecc. vengono passate al metodo factory come parametri, utilizzando l'autowiring:

public static function createRouter(Nette\Database\Connection $db): RouteList
{
	// ...
}

Router semplice

Un router molto più semplice della Route Collection è SimpleRouter. Può essere usato quando non c'è bisogno di un formato URL specifico, quando mod_rewrite (o alternative) non è disponibile o quando semplicemente non si vuole ancora preoccuparsi di URL facili da usare.

Genera indirizzi più o meno in questa forma:

http://example.com/?presenter=Product&action=detail&id=123

Il parametro del costruttore SimpleRouter è un presentatore e un'azione predefiniti, cioè l'azione da eseguire se si apre, ad esempio, http://example.com/ senza ulteriori parametri.

// default al presenter 'Home' e all'azione 'default'
$router = new Nette\Application\Routers\SimpleRouter('Home:default');

Si consiglia di definire SimpleRouter direttamente nella configurazione:

services:
	- Nette\Application\Routers\SimpleRouter('Home:default')

SEO e canonizzazione

Il framework aumenta la SEO (ottimizzazione per i motori di ricerca) impedendo la duplicazione di contenuti in URL diversi. Se più indirizzi rimandano a una stessa destinazione, ad esempio /index e /index.html, il framework determina il primo come primario (canonico) e reindirizza gli altri ad esso utilizzando il codice HTTP 301. In questo modo, i motori di ricerca non indicizzeranno le pagine due volte e non ne comprometteranno il page rank. .

Questo processo è chiamato canonizzazione. L'URL canonico è quello generato dal router, cioè dal primo percorso corrispondente nella raccolta senza il flag OneWay. Pertanto, nella raccolta, vengono elencati per primi i percorsi primari.

La canonizzazione viene eseguita dal presentatore, come descritto nel capitolo Canonizzazione.

HTTPS

Per utilizzare il protocollo HTTPS, è necessario attivarlo sull'hosting e configurare il server.

Il reindirizzamento dell'intero sito verso HTTPS deve essere eseguito a livello di server, ad esempio utilizzando il file .htaccess nella directory principale della nostra applicazione, con il codice HTTP 301. Le impostazioni possono variare a seconda dell'hosting e hanno un aspetto simile a questo:

<IfModule mod_rewrite.c>
	RewriteEngine On
	...
	RewriteCond %{HTTPS} off
	RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
	...
</IfModule>

Il router genera un URL con lo stesso protocollo con cui è stata caricata la pagina, quindi non è necessario impostare altro.

Tuttavia, se abbiamo eccezionalmente bisogno di rotte diverse da eseguire con protocolli diversi, lo inseriremo nella maschera di rotta:

// Genererà un indirizzo HTTP
$router->addRoute('http://%host%/<presenter>/<action>', /* ... */);

// Genererà un indirizzo HTTPS
$router->addRoute('https://%host%/<presenter>/<action>', /* ... */);

Debug del router

La barra di routing visualizzata in Tracy Bar è un utile strumento che mostra un elenco di percorsi e anche i parametri che il router ha ottenuto dall'URL.

La barra verde con il simbolo ✓ rappresenta il percorso che corrisponde all'URL corrente, le barre blu con i simboli ≈ indicano i percorsi che corrisponderebbero all'URL se il verde non li superasse. Vediamo ulteriormente il presentatore e l'azione corrente.

Allo stesso tempo, se c'è un reindirizzamento inaspettato dovuto alla canonicalizzazione, è utile guardare nella barra redirect per vedere come il router ha originariamente compreso l'URL e perché ha reindirizzato.

Quando si esegue il debug del router, si consiglia di aprire gli Strumenti per sviluppatori nel browser (Ctrl+Maiusc+I o Cmd+Opzione+I) e di disabilitare la cache nel pannello Rete, in modo che i reindirizzamenti non vengano memorizzati.

Prestazioni

Il numero di rotte influisce sulla velocità del router. Il loro numero non dovrebbe superare le poche decine. Se il sito ha una struttura di URL troppo complicata, si può scrivere un router personalizzato.

Se il router non ha dipendenze, ad esempio da un database, e il suo factory non ha argomenti, possiamo serializzare la sua forma compilata direttamente in un contenitore DI e quindi rendere l'applicazione leggermente più veloce.

routing:
	cache: true

Router personalizzato

Le righe seguenti sono destinate a utenti molto avanzati. È possibile creare il proprio router e aggiungerlo naturalmente alla propria collezione di rotte. Il router è un'implementazione dell'interfaccia Nette\Routing\Router con due metodi:

use Nette\Http\IRequest as HttpRequest;
use Nette\Http\UrlScript;

class MyRouter implements Nette\Routing\Router
{
	public function match(HttpRequest $httpRequest): ?array
	{
		// ...
	}

	public function constructUrl(array $params, UrlScript $refUrl): ?string
	{
		// ...
	}
}

Il metodo match elabora la $httpRequest corrente, da cui è possibile recuperare non solo l'URL, ma anche le intestazioni ecc. in un array contenente il nome del presentatore e i suoi parametri. Se non può elaborare la richiesta, restituisce null. Quando si elabora la richiesta, è necessario restituire almeno il presentatore e l'azione. Il nome del presentatore è completo e include eventuali moduli:

[
	'presenter' => 'Front:Home',
	'action' => 'default',
]

Il metodo constructUrl, invece, genera un URL assoluto dall'array di parametri. Può utilizzare le informazioni del parametro $refUrl, che è l'URL corrente.

Per aggiungere un router personalizzato alla collezione di rotte, utilizzare add():

$router = new Nette\Application\Routers\RouteList;
$router->add($myRouter);
$router->addRoute(/* ... */);
// ...

Uso separato

Per uso separato si intende l'uso delle funzionalità del router in un'applicazione che non utilizza l'applicazione Nette e i presentatori. Quasi tutto ciò che è stato mostrato in questo capitolo è applicabile, con le seguenti differenze:

Quindi creeremo di nuovo un metodo che costruirà un router, ad esempio:

namespace App\Core;

use Nette\Routing\RouteList;

class RouterFactory
{
	public static function createRouter(): RouteList
	{
		$router = new RouteList;
		$router->addRoute('rss.xml', [
			'controller' => 'RssFeedController',
		]);
		$router->addRoute('article/<id \d+>', [
			'controller' => 'ArticleController',
		]);
		// ...
		return $router;
	}
}

Se si usa un contenitore DI, cosa che consigliamo, si aggiunge nuovamente il metodo alla configurazione e si ottiene il router insieme alla richiesta HTTP dal contenitore:

$router = $container->getByType(Nette\Routing\Router::class);
$httpRequest = $container->getByType(Nette\Http\IRequest::class);

Oppure creeremo direttamente gli oggetti:

$router = App\Core\RouterFactory::createRouter();
$httpRequest = (new Nette\Http\RequestFactory)->fromGlobals();

Ora dobbiamo far funzionare il router:

$params = $router->match($httpRequest);
if ($params === null) {
	// non è stata trovata alcuna rotta corrispondente, verrà inviato un errore 404
	exit;
}

// elaboriamo i parametri ricevuti
$controller = $params['controller'];
// ...

E viceversa, useremo il router per creare il collegamento:

$params = ['controller' => 'ArticleController', 'id' => 123];
$url = $router->constructUrl($params, $httpRequest->getUrl());
versione: 4.0