RFC – odstranit implicitní nullování v tagu {var}
- m.brecher
- Generous Backer | 905
Ahoj,
pro IDE completing v Latte šablonách používáme tag {varType}:
{varType App\Model\Token\Reason $reason}
alternativně lze použít i tag {var}, který není primárně určený pro typování, ale tuto funkci umožňuje:
{var
App\Model\Token\Reason $reason,
string $message = match($reason->value){'expired' => 'Po expiraci', 'revoked' => 'Zneplatněn'},
}
IDE rozpozná typ u obou proměnných $reason i $message, ale POZOR!!, i když je hodnota $reason do šablony injektována, tag {var} ji přepíše IMPLICITNĚ na null !! Velmi neočekávané a nepříjemné chování.
V dokumentaci latte není toto implicitní nullování proměnné nijak zmíněno, ani před ním není varováno:
https://latte.nette.org/en/tags#…
https://latte.nette.org/en/type-system#…
Latte filozofie je „nevymýšlet novou syntaxi tam, kde lze použít existující syntaxi PHP“. Z tohoto pohledu je implicitní nullování proměnné v tagu {var}:
- neintuitivní a neočekávané
- nebezpečné – bez varování přepíše hodnotu ne null
- zbytečné, null lze nastavit explicitně, ušetřené množství kódu je zanedbatelné
Možnosti nápravy:
- nechat být, ale řádně zdokumentovat a varovat!
- úplně zrušit, vyhodit v latte výjimku
- ponechat syntaxi jak je, ale odstranit implicitní nastavení hodnoty null
Návrh řešení
Z praktického pohledu je ideální řešení využít možnosti otypovat si injektované proměnné, včetně otypování nově nastavovaných proměnných v jednom syntaktickém prvku {var}, pouze odstranit problematické implicitní nullování, a zjednodušit kód šablon.
Nyní:
{varType App\Model\User\UserModel $userModel}
{varType App\Model\Token\Reason $reason}
{var $message = match($reason->value){
'expired' => 'Po expiraci',
'revoked' => 'Zneplatněn'
}}
Po změně – jeden deklarační blok:
{var
App\Model\User\UserModel $userModel, // typehinty
App\Model\Token\Reason $reason,
$message = match($reason->value){ // inicializace
'expired' => 'Po expiraci',
'revoked' => 'Zneplatněn'
},
}
Tato úprava, vedle odstranění nebezpečného chování, zjednoduší každodenní psaní typových šablon.
A co BC break ?
Mohou existovat šablony, kde bezpochyby nevědomky autor nastaví proměnnou implicitně na null a s touto null hodnotou dále pracuje. Po změně nebude proměnná mít hodnotu null, ale bude neinicializovaná. Vyhodí se Warning Undefined variable ! O chybě se autor dozví a může ji opravit.
Naproti tomu existující chování Latte injektovanou hodnotu potichu přepíše na null a naopak v tomto případě se může funkce šablony neočekávaně změnit, aniž by se vyhodila výjimka. A to v případě, že autor PHP kódu do šablony vědomě injektuje proměnnou s nastavenou hodnotou, u které předpokládá i možnost null, a nenápadný typehint hodnotu přenastaví na null.
Díky za komentáře.
Editoval m.brecher (18. 7. 13:31)
- David Grudl
- Nette Core | 8284
Neintuitivní a neočekávané? Vždyť přesně takhle funguje var v JavaScriptu.