RFC – odstranit implicitní nullování v tagu {var}

m.brecher
Generous Backer | 905
+
-6
-

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
+
+2
-

Neintuitivní a neočekávané? Vždyť přesně takhle funguje var v JavaScriptu.