O webu
BB code

V případě, že chceme umožnit návštěvníkům stránky používat základní formátování textu, existuje několik základních způsobů, jak to zařídit.

  1. WYSIWYG editor,
  2. HTML značky,
  3. Markdown/Texy!,
  4. BB code

Každá z možností má své pro a proti.

Co to je BB code?

Stručně řečeno má BB kód zjednodušenou podobu HTML kódu, kdy se místo špičatých <> závorek zapisují hranaté [] (tyto znaky lze relativně pohodlně zapsat i na české klávesnici).

Zkratka BB znamená Bulletin Board, protože se tento způsob formátování nejvíce rozšířil na diskusních fórech.

Základní znaky

Typické BB značky:

BB kód HTML ekvivalent Výsledek
[b]text[/b] <b>text</b> text
[i]text[/i] <i>text</i> text
[code]code[/code] <code>text</code> text
[url=http://example.com]text[/url] <a href='http://example.com'>text</a> text
[img]http://example.com/obrazek.png[/img] <img src='http://example.com/obrazek.png'>text</a> (obrázek)

Asi hlavní výhoda oproti obyčejnému HTML spočívá v tom, že:

  • Není problém zapsat ukázku HTML kódu. HTML se neinterpretuje a zároveň nezahodí, ale převede na entity.
  • V PHP není jednoduchý způsob, jak povolit jen určité HTML značky a atributy. Značky zvládne sice odstranit funkce strip_tags, ale na povolení atributů je potřeba nějaký nástroj jako HTML Purifier.

Převod BB značek na HTML

Jednoduché značky [b], [i], [code]

Nejjednodušší způsob jako převádět [b]text[/b] na ><b>text</b> a podobně je použití regulárních výrazů a funkce preg_replace.

$text = preg_replace(
  "~\[(b)\](.+?)\[/\\1\]~ui", 
  "<\\1>\\2</\\1>", 
  $text
);

Formátování všech jednoduchých značek ([b], [i], [code]) může zajistit jednoduchá funkce, které se předají požadované značky.

function obycejnyBbKod($znacky, $text) {
  foreach ($znacky as $znacka) {
    $text = preg_replace(
      "~\[($znacka)\](.+?)\[/\\1\]~ui", 
      "<\\1>\\2</\\1>", 
      $text
    );
  }
  return $text;
}

Použití.

$text = obycejnyBbKod(
  array("b", "i", "code"), $text
);
echo $text;

Značka [img]

V případě vkládání obrázků je nutné vytvořit jinou funkci (už se nebude nahrazovat značka za značku, ale potřebujeme obsah mezi [img] a [/img] dostat do src atributu značky <img>). Regulární výraz ale bude stejný.

$text = preg_replace(
  "~\[img\](.+?)\[/img]~ui", 
  "<img src='\\1'>", 
  $text
);

Složitější BB značky [url]

BB značky, kde může být nějaký atribut s hodnotou, musí zpracovávat trochu složitější regulární výraz.

$text = preg_replace(
  "~\[url=(.+?)\](.+?)\[/url]~ui", 
  "<a href='\\1'>\\2</a>", 
  $text
);

Bezpečnost

Je potřeba myslet na to, že vstup od uživatele je nutno chránit před XSS a zároveň veškeré akce chránit před CSRF. Možnost vložit obrázek může být skvělá příležitost, jak administrátor načte požadovanou URL (třeba URL pro smazání obsahu), aniž by o tom věděl.

XSS

Proti XSS je vhodný postup ošetřit před převáděním na BB code obsah funkcí htmlspecialchars.

$text = htmlspecialchars($text, ENT_QUOTES);

Uvést druhý parametr ENT_QUOTES je naprosto klíčové. Zabrání to možnosti vložit škodlivý kód typu:

[img]http://example.com' onclick='alert("XSS")[/img]

Který by se jinak přetvořil na validní, funkční a nebezpečný kód:

<img src='http://example.com' onclick='alert("XSS")'>

Zpracovávání obsahu značek

V případě, že je žádoucí zadaný obsah BB značek nějak zpracovávat (upravovat), poslouží k tomu PHP funkce preg_replace_callback.

$text = preg_replace_callback(
  "~\[url=(.+?)\](.+?)\[/url]~ui", 
  function($vyskyty) {
    $cilOdkazu = $vyskyty["1"];
    $textOdkazu = $vyskyty["2"];
    // nějaké operace
    return "<a href='" . $cilOdkazu . "'>" . $textOdkazu . "</a>"; 
  },
  $text
);

Hotové řešení

Hotové řešení BB code v PHP je na GitHubu.

Zpětný převod HTML na BB code

Převádět pro účely editace textů v BB kódu zpětně z HTML sice možné je, ale není to úplně ideální postup. Musí se kromě funkcí „BB Code → HTML“ vytvářet i převody opačné.

Navíc to není jen dopisování zpětných regulárních výrazů. Když se rozhodneme, že HTML značky obrázků mají mít třeba nějakou třídu, bude se muset psát další regulární výraz pro převod staré podoby HTML do nové. Nebo si napsat nějaký skript, co všechen obsah dekóduje do BB a převede zpět do nové podoby HTML.

Většinou lepší řešení je ukládat v DB obě podoby – jednu v BB kódu pro editaci a druhou v HTML pro výpis. Převádět BB kód by sice bylo možné při každém vypsání stránky, ale je to trochu zbytečná zátěž.

Problémy

Převody BB značek na HTML regulárními výrazy nejsou úplně neprůstřelné. Například není problém vytvořit překřížené značky a obecně nevalidní výstup. Je to ale řešení na pár řádek, což by napsání dokonale funkčního parseru, který by těmito problémy netrpěl, nebylo.