Missing comma before tag arguments - proč Latte chce čárku?

David Grudl
founder | 8328
+
+1
-

Pokud vám po aktualizaci na nette/application 3.3 vyskočila hláška Missing comma before tag arguments, tenhle příspěvek je pro vás. Kdo chce šablony jen rychle opravit, nemusí číst dál: chybějící čárky za vás doplní skript, návod najdete na konci příspěvku. Ostatním vysvětlím, co se změnilo, proč a kam to celé míří.

Co se stalo

Tag {control} nově hlásí deprecaci, když mezi jménem komponenty a argumenty chybí čárka:

{control poll 1, 'foo'}     {* deprecated *}
{control poll, 1, 'foo'}    {* správně *}

Šablona se zkompiluje úplně stejně jako dřív, Latte jen upozorní na zastaralý zápis. Proč je zastaralý? Protože bez čárky musí Latte odhadovat, kde končí jméno komponenty a kde začínají argumenty. Většinou se trefí. Když se ale splete, často o tom neřekne ani slovo:

{control cart:small -1}    {* metoda 'render' . 'small' - 1, tedy TypeError místo renderSmall(-1) *}
{control poll:type [1]}    {* metoda podle konstanty type[1] místo renderType([1]) *}
{control $comp ($x)}       {* zavolá $comp($x) a komponentu vykreslí bez argumentů *}

U odkazů je to ještě zrádnější:

{link change! -1}    {* funguje: signál change! s argumentem -1 *}
{link change! +1}    {* odkaz na akci change s argumentem false *}

Ve druhém případě odkaz přijde o vykřičník, takže už nevede na signál, a z !+1 se stane argument false. Žádná chyba, žádné varování, odkaz se normálně vygeneruje a vede jinam. A proč mínus funguje a plus ne? To si nikdo nemá šanci pamatovat.

Stejně dopadne i {include}:

{include $file (id: 1)}    {* zavolá $file(id: 1) jako funkci *}

S čárkou fungují všechny tyhle případy přesně tak, jak čekáte:

{control cart:small, -1}
{control poll:type, [1]}
{control $comp, ($x)}
{link change!, +1}
{include $file, id: 1}

Proč to neopravit v parseru

Nabízí se otázka, proč Latte prostě nepozná, že jméno končí mezerou. Jenže výrazy v Latte se chovají jako v PHP: mezery uvnitř výrazu nic neznamenají. $a -1 je totéž co $a - 1 a $f ($x) je volání funkce stejně jako $f($x). Metoda za dvojtečkou i jméno komponenty v proměnné jsou celé výrazy, takže si k sobě přitáhnou všechno, co za nimi následuje, a mezera je nezastaví. Rozlišovat podle mezer by znamenalo, že $f ($x) v Latte znamená něco jiného než v PHP, a to by bylo ještě matoucí.

U jednoduchého jména jako poll nebo change! to Latte zkouší: za jménem hledá znaky, kterými může začínat argument. Seznam těch znaků jsem postupně rozšiřoval: plus, zavináč, desetinné číslo začínající tečkou, spread. Pokaždé to prošlo testy a pokaždé se hned našel další případ. Ten seznam nikdy nebude úplný a každé rozšíření jen přesune místo, kde to příště potichu selže.

Jediný zápis, který je jednoznačný vždycky, je čárka.

Čárka přitom není jen daň za jednoznačnost. Šablony se s ní i lépe čtou, protože na první pohled vidíte, kde končí jméno a kde začínají argumenty:

{input price size: 5}                  {* "price size" se čte jako jedno slovo *}
{input price, size: 5}

<a n:href="this page: $page + 1">      {* "this page" se čte jako sousloví *}
<a n:href="this, page: $page + 1">

{control poll $active ? 1 : 2}         {* patří $active ještě ke jménu? *}
{control poll, $active ? 1 : 2}

U volání funkce v PHP tuhle práci dělá závorka, v Latte čárka.

Kam to směřuje

Latte čárku vyžaduje postupně, tag po tagu:

  • Vyžadují ji nové zápisy, které nemusí brát ohled na staré šablony. Například {layout 'base.latte', title: 'Home'} s předáním proměnných do layoutu (Latte 3.1.5); bez čárky skončí chybou kompilace.
  • Deprecaci hlásí {form}, {input} a {label} od nette/forms 3.3 a {control} od nette/application 3.3.
  • Zatím volitelná je u {link}, {plink}, n:href, {include}, {import} a {embed}. Jak ukazují příklady výše, i tady se vyplatí ji psát.

Na další deprecace ale čekat nemusíte. Skript popsaný níže doplní čárku do všech těchto tagů naráz, i tam, kde je zatím volitelná. Šablony tak srovnáte jedním během.

Co s tím udělat

Nejrychlejší je nechat čárky doplnit skript. Stáhněte ho do kořene projektu a nejdřív ho pusťte nanečisto:

curl -O https://gist.githubusercontent.com/dg/ec6ee5335db26443660eb9dd367b1bf6/raw/latte-comma.php
php latte-comma.php --dry-run app

Vypíše každý tag, který by upravil. Když výsledek sedí, pusťte ho bez --dry-run a změny si projděte v git diff. Opravuje {control}, {form}, {input}, {label}, {link}, {plink}, n:href, {include}, {import}, {embed}, {define} a {sandbox}.

Čárku vloží jen tam, kde se šablona zkompiluje na úplně stejný kód jako předtím, takže nemůže změnit, co šablona dělá. Tagy, kde by čárka význam změnila, jen vypíše k ruční kontrole; nejspíš jsou už teď rozbité, jako {link change! +1} výše. Pokud v šablonách používáte vlastní tagy, předejte skriptu svůj Latte engine přes volbu --bootstrap, popis je v hlavičce skriptu.

Kdo chce opravovat ručně, má to snadné: deprecace říká přesné místo, například Missing comma before tag arguments on line 12 at column 15. Ať tak či onak, nejlepší je psát čárku všude, i tam, kde ji Latte zatím nevyžaduje. Šablona je pak jednoznačná a žádná z pastí výše se jí netýká.