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