[BUG] Chyba routy „<module>/<presenter>/<action>“ pri generovani URL

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
falkon
Člen | 17
+
0
-

Chcel by som reportovat chybu v Route::constructUrl()stable verzii Nette 0.9.1. Routa, ktora ma ako mask zadany vyraz <module>/<presenter>/<action> a zaroven ma udanu defaultnu hodnotu parametra module nepracuje spravne pri generovani URL na niektore moduly.

Symptomy:

V bootstrap.php je nadefinovana routa:

<?php
$router[] = new Route('<module>/<presenter>/<action>', array(
	'module' => 'public',
	'presenter' => 'homepage',
	'action' => 'default'
));
?>

ktorej tvar je tak-nejak intuitivny. Problem je, ze pre niektore moduly (napr. modul ‚auth‘) je vyhadzovana chyba No route for Auth:Homepage:default(). Zda sa, ze takato routa nefunguje pre moduly, ktore maju dlzku nazvu roznu od dlzky hodnoty udanej ako defaultna hodnota parametru module (v tomto pripade ‚public‘, cize dlzky 6). Preto napr. modul ‚Auth‘ nefunguje, ale ‚Authhh‘ funguje.

Problem spociva v generovani URL na dany modul – co sa prejavuje bud pri canonizacii URL (ak je zapnuta, vyhodenim vynimky No route for <module>:<presenter>:<action>().), alebo pri generovani linkov v sablone).

To, ze dana chyba ma nieco spolocne s defaultnou hodnotou parametru module naznacuje aj to, ze takato definicia:

<?php
$router[] = new Route('<module>/<presenter>/<action>', array(
	'presenter' => 'homepage',
	'action' => 'default'
));
?>

routuje korektne a generuje korektne linky. Problemom je, ze taketo zadanie samozrejme nie je semanticky ekvivalentne tomu vyssieuvedenemu – pride sa o funkcionalitu (ne)doplnania nazvu implicitneho modulu do URL.

Myslim si, ze chyba je v triede Route v subore Nette\Application\Routers\Route.php okolo riadku 273:

<?php
// public function constructUrl(PresenterRequest $appRequest, IHttpRequest $httpRequest) :
// line 269:

	$presenter = $appRequest->getPresenterName();
	if (isset($metadata[self::MODULE_KEY])) {
		if (isset($metadata[self::MODULE_KEY]['fixity'])) {
			$a = strlen($metadata[self::MODULE_KEY][self::VALUE]);	// <-- PRAVDEPODOBNA CHYBA?
			if (substr($presenter, $a, 1) !== ':') { 		// <-- PRAVDEPODOBNA CHYBA?
				return NULL; // module not match		// <-- tento kod sa vykona - vrati sa NULL hodnota, co sa reprezentuje akoze URL nemohla byt skonstruovana
			}
		} else {
			$a = strrpos($presenter, ':');
		}

//...
?>

Napr. pri spracovani dotazu <host>/auth/bar/baz sa v polozke $metadata[self::MODULE_KEY]['fixity'] nachadza hodnota 1 a v polozke $metadata[self::MODULE_KEY][self::VALUE] hodnota 'public' (co je definovana defaultna hodnota parametru module z definicie routy). V pripade, ze sa defaultna hodnota parametra module v definicii routy nezada, tak polozka $metadata[self::MODULE_KEY]['fixity'] ani $metadata[self::MODULE_KEY][self::VALUE] v danom mieste kodu nie je nastavena (pre rovnaky dotaz).

Bohuzial, zmysel niektorych premennych a priznakov (ako napr. zmysel priznaku „fixinity“ a presny vyznam vsetkych atributov v poli $metadata) mi zostava mierne utajeny, a tak sa neodvazujem navrhnut patch.

Vzorovy projekt, ktory demonstruje popisovanu chybu je dostupny k stiahnutiu na http://stoki.pasique.sk/…ting-bug.zip. Je to Skeleton projekt, ktory je rozsireny o par modulov/prezenterov/template-ov. Su v nom vytvorene 3 moduly – ‚Public‘ (ktory je nastaveny ako implicitny a funguje spravne), ‚Auth‘ (ktory nefunguje) a ‚Authhh‘ (ktoreho nazov bol predlzeny na 6 znakov, aby fungoval).


P.S.: Zbeznym hladanim na fore sa mi zdalo, ze s podobnym problemom sa potykalo uz davnejsie niekolko uzivatelov (napr. 1 , 2 ), no vzdy dostali skor rady na workaround, nez odpoved kde je chyba. Preto by som sa chcel obratit priamo na Davida a poprosit ho o vyjadrenie, ci je to naozaj chyba, alebo ci to je feature a nieco mi tak seredne unika.

Vopred dakujem!

Ondřej Mirtes
Člen | 1536
+
0
-

Hezký bugreport, uvidíme co na to David.

Já bych zkusil dát v definicích rout velká písmena pro modul a pro presenter, tak to má totiž být.

$router[] = new Route('<module>/<presenter>/<action>', array(
        'module' => 'Public',
        'presenter' => 'Homepage',
        'action' => 'default'
));

$router[] = new Route('<module>/<presenter>/<action>', array(
        'presenter' => 'Homepage',
        'action' => 'default'
));
falkon
Člen | 17
+
0
-

Ano, pravda, niekde som videl doporucenie, ze by sa to malo pisat velkymi – ale na tom to nezavisi. Ja som to povodne mal s velkymi pismenami, ale po dlhom case testovania a skusania roznych verzii mi tam zostali tie male pismena… nicmenej „na funkci rostlinare to vplyv nema“ :)

Jan Jakeš
Člen | 177
+
0
-

Další BUG report – pravděpodobně jsou významné chyby v Route!

1. BUG:

Mějme routu:

$router[] = new Route('<id>?action=<action>', array(
	'presenter' => 'Page',
	'action' => 'default',
));

V nejnovější unstable verzi neprojde touto routo ani document_root/nejakeid?action=edit. Problém nastává už ve fázi match, zřejmě se šaptně podle masky vygeneruje regexp.

Navíc pokud změním masku na toto:

$router[] = new Route('<id>{?action=<action>}', array(
	'presenter' => 'Page',
	'action' => 'default',
));

Výraz routou sice projde, ale nastaví mi action na hodnotu default(!) a vygeneruje URL document_root/nejakeid.


2. BUG:
Další chyba se projevuje zajímavým způsobem. Mějme routu:

$router[] = new Route('<id>', array(
	'presenter' => 'Page',
	'action' => 'default',
));

Když do ní pošleme document_root/nejakeid, vše proběhne OK. Jenže… Když v definici routy zaměním slovo Page za page, najednou routa uvedenou adresu nevezme. To by ještě tak nevadilo, ale když routu změníme na:

$router[] = new Route('<id>/<presenter>', array(
	'presenter' => 'Page',
	'action' => 'default',
));

Vezme najednou obě varianty…

Třída Route se teď chová opravdu podivně. Sám nevím, jestli už delší dobu nebo až od nejnovějších revizí. Zatímco druhý mnou popsaný bug je spíš detail (který ale ukazuje na nějakou nekonzistenci uvnitř Route), první bug mi připadá dost závažný. Zdá se mi, že Route selže i v naprosto jednoduché kombinaci masky a defaultních dat.

Editoval Juan (11. 10. 2009 0:42)

David Grudl
Nette Core | 8229
+
0
-

Juan napsal(a):

$router[] = new Route('<id>?action=<action>', array(
	'presenter' => 'Page',
	'action' => 'default',
));

Z technických důvodů bylo potřeba kolem otazníku mít mezeru, dnes jsem toto omezení odstranil.

$router[] = new Route('<id>{?action=<action>}', array(
	'presenter' => 'Page',
	'action' => 'default',
));

Tento zápis skutečně podporován není, maska se kolem otazníku rozpadne na dvě samostatné části.

Další chyba se projevuje zajímavým způsobem. Mějme routu:

$router[] = new Route('<id>', array(
	'presenter' => 'Page',
	'action' => 'default',
));

Když do ní pošleme document_root/nejakeid, vše proběhne OK. Jenže… Když v definici routy zaměním slovo Page za page, najednou routa uvedenou adresu nevezme. To by ještě tak nevadilo, ale když routu změníme

Co znamená „adresu nevezme“? Routa pracuje správně, jiná věc je, že aplikace nemusí umět najít pagePresenter.

deric
Člen | 93
+
0
-

v poslední revizi (050aec1) není akceptována routa

$router[] = new Route('index.php', array(
    'presenter' => 'Front:Default',
    'action' => 'default',
), Route::ONE_WAY);

protože pole $parts obsahuje pouze jeden prvek

array(1) {
   0 => string(9) "index.php"
}

Route.php: 425

		$i = count($parts) - 1;
		$part = $parts[$i - 1];

je to tak správně?

Jan Jakeš
Člen | 177
+
0
-

Z technických důvodů bylo potřeba kolem otazníku mít mezeru, dnes jsem toto omezení odstranil.

Aha, to jsem netušil :) Místo toho tu vytvářím flame, jak je tam závažný bug. Sorry…

Co znamená „adresu nevezme“? Routa pracuje správně, jiná věc je, že aplikace nemusí umět najít pagePresenter.

Nevezme nemyslím, že nenajde presenter, ale jako No route for Page:default(id=nejakeid). Jen pro shrnutí – uvedl jsem tam dvě routy, v první je presenter volitelný, v druhé konstantní. Obě routy správně matchnou 'presenter' => 'Page', ale 'presenter' => 'page' první routa nematchne, zatímco druhá ano…

David Grudl
Nette Core | 8229
+
0
-

fixed

David Grudl
Nette Core | 8229
+
0
-

falkon napsal(a):

Chcel by som reportovat chybu v Route::constructUrl()stable verzii Nette 0.9.1. Routa, ktora ma ako mask zadany vyraz <module>/<presenter>/<action> a zaroven ma udanu defaultnu hodnotu parametra module nepracuje spravne pri generovani URL na niektore moduly.

Díky za výborný report včetně ukázkového kódu. Mělo by to být opravené.

David Grudl
Nette Core | 8229
+
0
-

Juan napsal(a):

Aha, to jsem netušil :) Místo toho tu vytvářím flame, jak je tam závažný bug. Sorry…

V poho, potřeboval jsem nakopnout, abych to upravil ;)

Nevezme nemyslím, že nenajde presenter, ale jako No route for Page:default(id=nejakeid). Jen pro shrnutí – uvedl jsem tam dvě routy, v první je presenter volitelný, v druhé konstantní. Obě routy správně matchnou 'presenter' => 'Page', ale 'presenter' => 'page' první routa nematchne, zatímco druhá ano…

Ono se tím ‚matchne‘ obvykle spíš myslí akceptování URL než generování, ale už rozumím. Změnil jsem interní cache v MultiRouter na case-insensitive, takže by mělo fungovat i použití malého písmenka.

Jan Jakeš
Člen | 177
+
0
-

Ono se tím ‚matchne‘ obvykle spíš myslí akceptování URL než generování…

Nojo, už jsem mimo :) S match fází to samozřejmě nesouviselo…

Jinak obojí už funguje supr. Thx!