This post comes from the first version of this blog.
Please send me an email if anything needs an update. Thanks!

Wracamy na poważnie. :) Jakiś czas temu miałem problem z usuwaniem “niewidzialnych znaków” ze stringa w PHP. Mam na myśli oczywiście wszystkie te, które normalnie zapisujemy jako “slash-coś” - \n, \r, \t i tak dalej. W dzisiejszym wpisie pokażę Wam jak sobie z tym poradzić w chyba najbardziej elegancki do tej pory sposób.

Fotografia: lugolabt, CC-BY-SA.

Po co się męczyć...

Jaki jest cel? W moim przypadku było to dynamiczne wstawienie bloku tekstu w pole `` w dynamicznie generowanym formularzu wstępnie wypełnianym danymi z dynamicznie generowanego kodu JavaScript. Wszystko fajnie, taka dynamika, że Lamborghini Murciélago LP 670-4 SuperVeloce (wybaczcie, ale choruję na ten samochód od kiedy po raz pierwszy zagrałem w Need For Speed: Hot Pursuit :)) widzimy tylko w lusterku wstecznym, jednak Firefox szybko sprowadził mnie na ziemię krzycząc coś o "Unterminated string literals".

<dygresja> Jak wszystkim dobrze wiadomo, JavaScript nie radzi sobie najlepiej z wieloliniowymi literałami w kodzie (tj. radzi sobie całkiem nieźle, tylko deklaracje tego typu zmiennych są strasznie denerwujące) - aby uzyskać wydruk w stylu:

ala jest mała i ma kota
potrzebna będzie deklaracja stringa w następujący sposób:
1
2
3
var str =
    "ala jest mała \
    i ma kota";

Znakami “" mówimy interpreterowi, że “w tym miejscu Pan Programista życzy sobie znak końca linii”. </dygresja>

Rozplątując nieco opisaną wyżej dynamikę - w skrócie chodzi o to, aby w kodzie PHP wygenerować taki kod JavaScript, który następnie wygeneruje odpowiednio wypełnione danymi elementy formularza w HTMLu. O ile na pierwszy rzut oka tego typu manipulacje wydają się karkołomne, to da się je jednak zrobić w całkiem przyjemny sposób, dopóki oczywiście niektóre z elementów tej układanki nie zaczną ujawniać swoich “dziwnych zachowań”.

W moim przypadku tymi problematycznymi elementami były (co nie powinno dziwić) dane pochodzące z bazy danych. Okazało się, że proste na pierwszy rzut oka pole tekstowe “tytuł wpisu” zawierało znaki specjalne, takie właśnie jak znak końca linii, znak “powrotu karetki”, itd. Wiadomo, że w takim przypadku próba wstawienia tego typu danych w wieloliniowy blok tekstu będący deklaracją zmiennej w JavaScripcie skończy się wysypaniem całego skryptu, dlatego musimy wiedzieć, jak sobie z takimi przypadkami poradzić.

Wyrażenia regularne to the rescue!

Oczywiście możemy sobie z tym poradzić w sposób tradycyjny, tj. "na piechotę" usuwając wszelkie dane przejawiające destrukcyjny charakter dla naszych skryptów, nie zmienia to jednak faktu, że to jest raczej "lepienie" kodu, a nie faktyczne rozwiązywanie problemów.

Szukając czegoś, co pomogłoby mi uporać się z tym “raz a dobrze” trafiłem na StackOverflow, gdzie znalazłem ciekawą dyskusję na ten właśnie temat. Pomijając opisane akapit wyżej rozwiązania, których wykorzystanie zdecydowanie przypomniałoby o sobie za kilka tygodni / miesięcy / lat telefonem od zdenerwowanego klienta, znalazłem ciekawy fragment informacji na temat klasy znaków [[:cntrl:]] wyrażeniach regularnych:

cntrl: control characters
Okazuje się, że ta klasa (control characters) zawiera w sobie wszystkie "niewidoczne" znaki, czyli zakres kodowy ASCII od 0x00 (NUL) do 0x1F (US), wraz ze znakiem 0x7F (DEL). W związku z tym, jeśli chcemy "oczyścić" string z wszelkich tego typu danych, wystarczy prosta instrukcja:
1
$str = preg_replace('/[:cntrl:]/', '', $str);

Jest jednak mały haczyk - w PHP 5.3 będzie potrzebna dodatkowa para nawiasów okalająca nazwę klasy znaków:

1
$str = preg_replace('/[[:cntrl:]]/', '', $str);

Niestety nie udało mi się ustalić dlaczego. Liczę jednak, że uważne grono Czytelników poradzi sobie z tym problemem i doedukuje blogera. :) W każdym razie to jedno wywołanie preg_replace() powinno upewnić nas, że string zawiera już tylko pożądane informacje i na pewno jest “jednoliniowy”.

Życzę wszystkiego dobrego i zapraszam do dyskusji - może znacie jakieś ciekawsze rozwiązania tego problemu? Chętnie się z nimi zapoznam. Do usłyszenia!