PHP triky: generování JavaScriptu
Výstupem PHP skriptu bývá nejčastěji HTML stránka. S jejím generováním na úrovni značek může pomoci třeba knihovna NHtml. Ale ať už výstup generujete jakkoliv, je třeba mít na paměti, že každý řetězec se musí ošetřit funkcí htmlSpecialChars. To ji staví do pozice téměř nejdůležitější funkce, škoda proto, že nemá kratší název. Ačkoliv i to se dá napravit:
function h($s) {
return htmlSpecialChars($s, ENT_QUOTES);
}
Nojo, ale co když potřebujeme ve stránce vygenerovat JavaScriptový kód? Jakou funkcí v něm ošetříme řetězce?
Je to s podivem, ale až do verze PHP 5.2.0 s touto eventualitou tvůrci
nepočítali. Žádnou funkci pro „escapování“ řetězců jazyk
nenabízel. Jako náhrada se občas používá addSlashes, jenže ta nevkládá lomítka
na všechna potřebná místa a navíc je závislá na nastavení direktivy
magic_quotes_sybase. Teprve s příchodem verze 5.2 přišla
spása nazvaná json_encode.
$foo = ...; // libovolný řetězec v UTF-8
echo '<script type="text/javascript">';
echo 'var foo = ', json_encode((string) $foo), ';';
echo 'alert(foo);';
echo '</script>';
Poznámka: pokud píšete v XHTML a obsah skriptu uzavíráte mezi značky
<![CDATA[ a ]]>, je třeba ještě zajistit, aby
se v textu skriptu neobjevila koncová značka. Ale lepší spíš bude se
těmto Xvěcem vyhnout.
Sice za několik dní už žádného správného vývojáře verze nižší
než 5.2.0 zajímat nebudou
, než k tomu však dojde, podělím se s vámi
o alternativní řešení. To umí nejen sanitizovat řetězce, ale je
plnohodnotnou náhradu funkce json_encode (potřebujete-li ošetřit pouze
řetězce, stačí vám část za if (is_string($val))):
if (!function_exists('json_encode')) { // since PHP 5.2.0
/**
* JSON encode
*
* @author David Grudl
* @copyright Copyright (c) 2007 David Grudl
*/
function json_encode($val)
{
// indexed array
if (is_array($val) && (!$val
|| array_keys($val) === range(0, count($val) - 1))) {
return '[' . implode(',', array_map('json_encode', $val)) . ']';
}
// associative array
if (is_array($val) || is_object($val)) {
$tmp = array();
foreach ($val as $k => $v) {
$tmp[] = json_encode((string) $k) . ':' . json_encode($v);
}
return '{' . implode(',', $tmp) . '}';
}
if (is_string($val)) {
$val = addslashes($val); // ' " \ NUL - due to bug #40915
return '"' . addcslashes($val, "\x8..\xA\xC\xD/") . '"';
}
if (is_int($val) || is_float($val)) {
return (string) $val;
}
if (is_bool($val)) {
return $val ? 'true' : 'false';
}
return 'null';
}
}
Tato funkce se oproti předloze liší v tom, že neodhalí rekurzivně zanořené pole nebo objekty. V případě polí by se jednalo o docela netriviální úkol a tudíž nejlepším řešením je ho neřešit vůbec.
Komentáře
» přidat
Tento článek byl uzavřen. Už není možné k němu přidávat komentáře.

#1 Jakub Vrána http://php.vrana.cz/ nový
JavaScriptové řetězce se dají ošetřit funkcí
addcslashes. Viz můj článek.#2 David Grudl http://davidgrudl.com nový
#1 Jakub Vrána: addcslashes používám také, ale nemyslím si, že jsi zvolil správný výčet znaků. Také bacha na bug #40915.
#3 filer nový
Ta rekurzia cez volanie callback funkcie v array_map je elegantna.
#4 v6ak nový
Pokud vím, tak do JS to takt jde, ale když je JS v HTML, tak to může být problém…