Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP][funkcja] substr_safe, koniec z koszmarem niedomkniętych tagów
Forum PHP.pl > Forum > Gotowe rozwiązania > Algorytmy, klasy, funkcje
Louner
Funkcja służy do zapobiegania pozostawaniu otwartych tagów HTML i innych śmiesznych rzeczy. Przykładowo, tekst wejściowy:

Kod
<b>test1<i></u>test2<u></b>test3</i></div><i


po przejściu przez funkcję substr_safe, daje:

Kod
<b>test1<i>test2<u>test3</u></i></b>


Funkcję mozna też stosować tylko z jednym parametrem; wtedy tylko nieco uporządkuje kod bez żadnego przycinania.

Miałem wcześniej kilka problemów, które wpływały na działanie funkcji m.in. z interpretacją błędnego HTML'u, niedomkniętymi tagami, tagami zamykającymi bez tagów otwierających itp, prosiłem o pomoc na forum ale wszystko wyszło na prostą i wklejam działający kod. smile.gif

Pozdrawiam i życzę miłego użytkowania

parametry:
$str - string, tekst wejściowy
$start,$count - int, tak jak drugi i trzeci parametr do zwykłego substr
$cut - bool, określa, czy ucinać wyrazy

na wyjściu funkcja daje odpowiednio sformatowany tekst wyjściowy

  1. <?php
  2. /* substr_safe by Louner ( rozjazd@gmail.com ) */
  3. function substr_safe($str,$start=0,$count=0,$cut = false) {
  4.    $whitespace = array(
  5.        " ",
  6.        "\n",
  7.        "r",
  8.        "\t"
  9.    );
  10.  
  11.    $str = trim($str);
  12.    if( $count == 0 ) {
  13.        $count = strlen($str) - $start;
  14.    }
  15.    if( $cut == false ) {
  16.        $cut_front = false;
  17.        $cut_back = false;
  18.        $stop = $start+$count-1;
  19.        if( isset($str[$start-1]) && !in_array($str[$start-1],$whitespace) && !in_array($str[$start],$whitespace) && $start != 0 ) {
  20.            $cut_front = true;
  21.        }
  22.        if( isset($str[$stop+1]) && !in_array($str[$stop+1],$whitespace) && !in_array($str[$stop],$whitespace) ) {
  23.            $cut_back = true;
  24.        }
  25.    }
  26.    $str = substr($str,$start,$count);
  27.    if( $cut == false && ( $cut_back == true || $cut_front == true ) ) {
  28.        $min = strlen($str);
  29.        $max = 0;
  30.        foreach( $whitespace as $c ) {
  31.            if( ( $c_min = strpos($str,$c) ) !== false ) {
  32.                $c_max = strrpos($str,$c);
  33.  
  34.                if( $c_min < $min ) {
  35.                    $min = $c_min;
  36.                }
  37.                if( $c_max > $max ) {
  38.                    $max = $c_max;
  39.                }
  40.            } else {
  41.                continue;
  42.            }
  43.        }
  44.        if( $min < $max ) {
  45.            if( $cut_back == true ) {
  46.                $str = substr($str,0,$max);
  47.            }
  48.            if( $cut_front == true ) {
  49.                $str = substr($str,$min);
  50.            }
  51.        }
  52.    }
  53.  
  54.    $str = trim($str);
  55.    $str = preg_replace("#<([^<>s]+)$#s","$2",$str); // cut incorrect html
  56.  
  57.    $stack = array();
  58.  
  59.    if( preg_match_all('#<([^><]+)>#s',$str,$matches) ) {
  60.        $matches_full = $matches[0];
  61.        $matches = $matches[1];
  62.        foreach( $matches as $key => $tag ) {
  63.            if( $s = strpos($tag,' ') ) {
  64.                $tag = substr($tag,0,$s);
  65.            } else {
  66.                $s = strlen($tag);
  67.            }
  68.            $tag = substr($tag,0,$s);
  69.            if( substr($tag,0,1) == '/' ) { // closing
  70.                $tag = substr($tag,1);
  71.                if( end($stack) == $tag ) {
  72.                    array_pop($stack);
  73.                } else {
  74.                    $str = preg_replace("#(".preg_quote($matches_full[$key],"#").")#s","",$str,1);
  75.                }
  76.            } else { // opening
  77.                array_push($stack,$tag);
  78.            }
  79.        }
  80.    }
  81.    $stack = array_reverse($stack);
  82.    foreach( $stack as $tag ) {
  83.        $str .= '</'.$tag.'>';
  84.    }
  85.    return $str;
  86. }
  87. ?>
marcio
Jesli nie masz miec w bbcode'zie zagniezdzonych kodow bbcode to poprostu sprawdzaj tylko pierwsze <> i ostatnie <> i czy w ostatniej parze masz "/".

A jak nie to dodawaj do licznika 1 za kazdym razem jak znadziej <> i do drugiego jak znajdziesz </> jesli licznik sie zgadza to okej jak nie to szukasz ktore nie sa domkniete i dodajesz "/".
Louner
Nie do końca zrozumiałem, ale mam wrażenie, że robię to samo, tylko na stosie i mam lekki problem z błędnymi tagami
marcio
Elo.

Rozpisujac to sobie wychodzi takie cos:

Pobierasz html potem za pomoca preg_match_all() szukasz 2 rzeczy.
1.)Tag <.*>
2.)Tag <.*/>

Do 2 roznych zmiennych powiedzmy ze masz taki wpis:

Kod
<div><p>Elo</p></div>

I w zmiennych:

$tag_open -> masz <div> i <p>
$tag_close -> masz </p> </div>

I teraz jesli dasz jakis warunek ktory sprawdzi czy tag o index'ie [0] w tablicy $tag_close i $tag_open jest taki sam czyli czy element jest taki sam jak masz <p> to tag zamykajacy ma byc </p> a nie np: </b> i czy tag z tab $tag_close jest dobrze napisany czyli:
Cytat
</znacznik_rowny_temu_z_$tab_open>

Jesli tak to good jesli nie to domykasz.

Tylko musialbys dodac array_reverse() na jedna z tablic by indexy sie zgadzaly bo jak nie dasz to to by wygladalo tak:
  1. <?php
  2. $tag_open[0] = '<div>';
  3. $tag_open[1] = '<p>';
  4. $tag_close[0] = '</p>';
  5. $tag_close[1] = '</div>';
  6. ?>

Czyli indexu sa na odwort dasz array_reverse() i sie juz zgadzaja.

Sorki troche zamotalem ale taka mala idea.
Louner
Dzięki, ale nie chodzi mi o sam sposób sprawdzania poprawnej kolejności, tylko o te skrajne przypadki, które wcześniej poruszyłem.

Kolejność tagów sprawdzam tak, jeśli o to chodzi:
  1. wyszukuję wszystkie elementy tekstu wejściowego, które pasują do wyrażenia '<[^\>]+>', czyli teoretycznie kazdy tag
  2. wsystkie tagi sa zapisywane do tablicy $matches ( np $matches[1] = '<b>', $matches[2] = '<i>', itd )
  3. potem jade po każdej komórce tablicy $matches i jeżeli trafiam na tag otwierający, odkładam go na stos ( $stack, np: $stack[1] = '<b>', $stack[2] = '<i>' )
  4. jeżeli trafiam na tag zamykający, sprawdzam co jest na szczycie stosu, przykładowo: trafiam na '</i>', widzę, że na szczycie stosu ( $stack[2] ) jest już <i>, więc zdejmuję <i> ze stosu i terazm mam na nim tylko <b>
  5. potem przykładowo wejście się kończy, a na stosie zostało mi <b>, więc doklejam '</b>' na koniec tekstu wejściowego
Samo sprawdzanie poprawnej kolejności tagów jest proste, mniejsza o to. Bardziej mi zależy na jakimś sensownym sposobie poradzenia sobie z błędnym htmlem, jak i z 'toksycznymi' znakami, jak '<', czy '>', które są powstawiane w dziwnych miejscach. Przykładowo:

'<b>pięć < siedem </b>' będzie wyłapane przez '<[^\>]+>' jako dwa tagi otwierające: "<b>" i "< siedem </b>". Nie mam tez pomysłu na to, co z tym zrobić

edit:
już nie ważne
graczu
Fajna funkcja, tylko gdy w treści się pojawia "<br>" bądź poprawnie "<br />" też jest zakańczany. Tagi które się kończą <tag "/>" powinny być omijane?.

Kod
$tresc = "<b> dupa <i>biskupa</i> <br />";

echo htmlspecialchars(substr_safe($tresc));

>>>

<b> dupa <i>biskupa</i> <br /></br></b>
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2024 Invision Power Services, Inc.