26. Combiscriptový komentářový přepínač

Ilustrační obrázek k článku: Combiscriptový komentářový přepínač
Ukážu, jak vytvořit komentářový přepínač, který umožní rozdělit polyglotní PHP a JS kód do samostatných větví. Tato technika je klíčová pro sdílený kód.

Proč klasický if nestačí

V jednom z minulých dílů jsme si ukázali jak můžeme pomocí podmínky if deklarovat větev funkce zvlášť pro JS a PHP. A zároveň jsme si ukázali jaké jsou nevýhody tohoto řešení, což je především to, že každý jazyk vidí zápis i druhého a na nějakých syntaktických pravidlech může zhavarovat. V jiném díle jsme si zároveň řekli, že funkci chceme deklarovat jen jednou, abychom společné části psali jen jednou.

Potřebujeme tedy uvnitř funkce udělat nějaký rozbočovač, kde by JS šlo jednou větví komentáře a PHP druhou.

K tomuto bychom potřebovali nějaký řetězec, který by v JS začal komentář a v PHP ne (nebo naopak). Je zřejmě, že // ani /* to nebudou protože ty fungují v JS i PHP stejně. Takže zkusíme již probíraný <!--. V JS je to komentář, ale co by tento string mohl znamenat v PHP, aby jeho zápis byl syntakticky správný?

První pokusy o přepínač

Pokud se nad ním zamyslíme, tak by jej šlo pochopit jako větší než, negace a decrement. Takže zkusíme doplnit proměnné tak, aby výraz dával smysl. Například do proměnné $a přiřadíme porovnání mezi $b a $c, kde $c bude decrementováno a negováno. Získáme takovýto script:

  1. function nm_sqrt($x)
  2. {
  3. if ($x < 0) throw new Error('nm_sqrt-ArgumentIsNegative');
  4. $a = $b <!-- $c; /*
  5. return Math.sqrt($x);
  6. /*/
  7. return sqrt($x);
  8. //*/
  9. }

Kde řádku 4. JS vidí jako přiřazení $b do $a následovaný řádkovým komentářem. Kdežto PHP tuto řádku interpretuje jako porovnání $b s negovaným dekrementem $c a přiřazení výsledku do $a, načež začíná blokový komentář.

Což je to co jsme potřebovali - v jednom jazyce otevřít blokový komentář a v druhém ne, protože je zamaskovaný za řádkovým.

Pozn: Díky tomu, že v JS není za každým příkazem povinný středník jej nemusíme uvádět na začátku řádku 5.

Finální konstrukce s 0&&0

Ovšem ani tento script není dokonalý a finální. Hlavní nevýhodou je to, že dochází k nějakému přiřazení (v JS) a v PHP dokonce k nějakému porovnání a dekrementaci. Tomuto se určitě chceme vyhnout protože by to vytvářelo zdržení a obecně to jsou vedlejší efekty.

Takže potřebujeme znaky <!-- zařadit do kódu tak, aby v něm byly, ale neprováděly se. Tady je potřeba se zamyslet a vybočit trochu ze zajetých myšlenkových kolejí, protože málokdy píšeme takový kód, který nechceme, aby se prováděl :-).

Kdo zná JS a PHP tak ví, že existuje způsob jak zapsat kód, který se nebude vykonávat. Například pokud je použit operátor && a již první člen vyjde false, tak se druhý člen vůbec nevyhodnocuje. Čehož jde využít např. ke kratšímu zápisu větvení kódu. Tyto dva řádky kódu jsou ekvivalentní:

  1. if ($Tiskni) print_r($Vysledek);
  2. $Tiskni && print_r($Vysledek);

Místo false můžeme pro zkrácení zápisu využít 0. A za 0 && vlastně můžeme napsat cokoliv, co bude syntakticky správné v obou jazycích, protože se to stejně nebude provádět. Jen potřebujeme, aby to obsahovalo <!-- aby to v JS otevřelo řádkový komentář. Výsledný kód naší funkce pak může vypadat třeba takto:

  1. function nm_sqrt($x)
  2. {
  3. if ($x < 0) throw new Error('nm_sqrt-AgumentIsNegative');
  4. 0&&0<!--$_;/*
  5. return Math.sqrt($x);
  6. /*/
  7. return sqrt($x);
  8. //*/
  9. }

Klíčové je 0&&0<!--$_;/*. JS to chápe jako 0 && 0, které se nikam nepřiřadí a už při překladu je jasné, že bude false. Takže JIT kompilátor by jej měl zcela vyhodit. Stejně tak PHP by už při překladu do opkódů mělo zjistit, že tento výraz má nulový efekt (tzv. dead code) a vyhodit jej. Toto řešení je zároveň bez vedlejších efektů

Jako základní komentářový rozlišovač jazyka tedy budeme používat konstrukci

0&&0<!--$_;/*

A tuto konstrukci můžeme typicky použít buď pro víceřádkový blok PHP příkazů takto:

  1. {
  2. Shared_Code;
  3. 0&&0<!--$_;/*
  4. JS_Code;
  5. JS_Code;
  6. /*/
  7. PHP_Code;
  8. PHP_Code;
  9. //*/
  10. Shared_Code;
  11. }

Anebo pro jednořádkový PHP příkaz takto:

  1. {
  2. Shared_Code;
  3. 0&&0<!--$_; PHP_Code; /*
  4. JS_Code; //*/
  5. Shared_Code;
  6. }

Tento přepínač nám konečně umožňuje psát společný kód bez duplikací a vedlejších efektů.

A v příštích dílech si začneme vytvářet knihovnu matematických funkcí pro CombiScript využívajících tohoto principu. Což na první pohled může vypadat jako nuda - dokud nezjistíme jak jsou některé funkce v JS a v PHP různé, jak hluboko musíme jít pro jejich pochopení a co vše udělat pro jejich stejné chování.

Předchozí