Blog

Przycinanie stringów

Przy okazji przygotowywania do publikacji ostatniego artykułu, trafiłem na pewien problem. W oryginalnym tekście jest kilka bardzo długich adresów URL. Długie ciągi pozbawione spacji lub innych białych znaków mają niemiłą skłonność niszczenia układu strony i tak było też w tym wypadku. Jak sobie z tym poradzić?

Najprostsze rozwiązanie to reguła CSS:

#content {
    overflow: hidden;
}

Ale skorzystanie z overflow tworzy nowe problemy. Układ strony jest określony dość precyzyjnie (z pikselową dokładnością) i aby równie dokładnie ustalić, co ma zostać ukryte przez overflow, musiałbym skorzystać z kolejnej własności CSS, clip. Tu z kolei okazuje się, że nie działa ona ani w IE, ani w Mozilli, jeśli element nie jest pozycjonowany absolutnie. Specyfikacja milczy na ten temat, a ja nie miałem ochoty zagłębiać się w temat. Przed chwilą, dla pewności, stworzyłem sobie prosty dokument, w którym znajduje się warstwa z określonym parametrem clip. I rzeczywiście, działa on tylko wtedy, gdy warstwie nada się własność position: absolute.

Jak nie pięścią, to młotkiem… W tym wypadku młotkiem okazały się wyrażenia regularne. Napisałem filtr wyjścia dla Smarty, który zawierał taki fragment:

preg_replace('/(?<=s|>)([^"'s<>]{50,})(?=s|<)/e',
  'substr("$1", 0, 47)."..."',
  $tpl_output);

Co robi ten kod? Wyszukuje wszystkie łańcuchy znaków pozbawione białych znaków, cudzysłowów oraz znaków < i >, które dodatkowo są poprzedzone spacją, domknięciem znacznika (X)HTML, a po nich następuje spacja lub początek nowego znacznika. Innymi słowy, dopasowane zostają tylko te z długich stringów, które znajdują się we wnętrzu znaczników (a więc w tekście strony), a nie zostaną uwzględnione wartości atrybutów znaczników XHTML. Dzięki takiemu rozwiązaniu można np. bez obaw podawać długie adresy URL w znaczniku <a> i nie zostaną one obcięte. Natomiast długie ciągi (tu: powyżej 50 znaków) wewnątrz tagów zostaną skrócone, a na ich końcu pojawi się trzykropek, aby poinformować, że coś zostało usunięte.

Jeśli ktoś nie rozumie, o czym tu piszę i chciałby to zmienić, zapraszam do rozdziału poświęconego wyrażeniom regularnym w manualu PHP.

Jak napisałem, filtrowanie odbywa się na etapie wyświetlania strony przez Smarty. Bardziej efektywne byłoby obcinanie stringów przed zapisem w bazie danych – dzięki temu przy ich wyświetlaniu nie trzeba by by się martwić o ich długość. Zdecydowałem się jednak na to rozwiązanie, bo nigdy nie wiadomo, czy ten długi łańcuch kiedyś nie będzie potrzebny – zawsze dobrze mieć go w bazie :).

Już po fakcie znalazłem w podręczniku Smarty wbudowaną funkcję wordwrap. Myślałem, że może zastąpić mój filtr, ale ma jedną sporą wadę: dodaje znaki nowego wiersza (\n) także w obrębie długich atrybutów XHTML, psuje więc np. długie URL-e.

Ale przecież wyrażeń regularnych należy unikać jak ognia! Dokładnie. Szczególnie ten wzorzec, który napisałem powyżej, jest dość zabójczy – wystarczy że kilku użytkowników zacznie stosować go na często odwiedzanych stronach i można wykończyć niejeden serwer. Z ciekawości zrobiłem testy szybkości: włączenie tego filtra wydłużało czas generowania strony z artykułem średnio 10%. To bardzo dużo, jak na jedną funkcję. Na pewno nie należy stosować takich rozwiązań, jeśli strona generuje spory ruch. W takim wypadku lepiej filtrować przed zapisaniem tekstu w bazie.

Przy okazji, moja klasa PHP do testowania szybkości generowania stron jest na serwerze. Jeśli ktoś nie potrzebuje kombajnu w rodzaju pakietu Benchmark z PEAR, może przydać mu się mój 1,5-kilobajtowy skrypt. W pliku jest również dokumentacja.

Comments are closed