{!$htmlConent|truncate:30} Jak ořezat jen text (ignorovat HTML tagy)

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Endrju
Člen | 147
+
0
-

Ahoj, problém je asi jasný z předmětu.

Proměnna $htmlConent obsahuje naformátovaný text pomocí HTML tagů (práce CKEditoru), který pak v šabloně zobrazuji takto:

{!$htmlConent}

Nyní bych chtěl tento text ořezat. Problém je ovšem v tom, že tato konstukce

{!$htmlConent|truncate:30}

k počtu znaků, které mají být ořezány započítává také HTML tagy. Tedy nejen, že může být způsobeno, že se nezobrazí vůbec nic z text, protože po ořezání zbydou jen HTML tagy, ale také nejsou tagy dokončeny, což může rozhodit layout webu.

Jak to udělat, aniž bych ztratil formátování textu (odstraněním HTML tagů) a mohl ořezat jen text? Přitom, by se všchny nedokončené HTML tagy uzavřely?

PS: nebyl jsem si jistý zda to mám hlásit jako chybu (když tak to tam přesuňte), protože možna takové použití nebylo ani zamýšleno. Nicméně něco takového by bylo docela užitečné. Řešili jste to už někdo? Jde to nějak udělat s pomocí Nette?


EDIT: Když jsem před přidáním tohoto tématu hledal na foru, zda tu už někdo něco podobného neřešil, tak jsem narazil na něco takového: {!$htmlConent|html|truncate:30}. To ale nefunguje (ani jsem nezjišťoval co ten parametr html dělá..) a vyhazuje to chybu: MemberAccessException - Call to undefined method Template::html().

Osobně mě napadlo, jak by se to dalo zhruba implementovat, ale nechci předbíhat pokud něco takového už je. Musel bych projít celý htmlContent s tím, že bych si do pomocné proměnné ukládal text, který jsem už prošel a inkrementoval bych čítač znaků jen když bych narazil na text, který není v HTML tagu (text který se na stránce zobrazuje). Až bych narazil na limit, tak bych buď musel nějak dokončit HTML tagy a nebo dojet až do konce s tím, že bych ignoroval text a bral jen HTML tagy.

Editoval Endrju (10. 3. 2010 21:24)

redhead
Člen | 1313
+
0
-

Žádné defaultní řešení asi není. Takže napsat si vlastní helper?

Endrju
Člen | 147
+
0
-

Aha, odpověděl jsi dřív než jsem stihl editovat post.. No, to nerad slyším :-/. Neznáte nějaký efektivní způsob na identifikaci HTML tagů (pro případ, že to budu muset impelmentovat)?

Nebo nemáte nekdo ještě lepší řešení, než to které jsem popsal ve svém předchozím příspěvku?

Mikulas Dite
Člen | 756
+
0
-

No, html neni regulární, takže to přes regexp pude asi složitě. Ale zkusim z hlavy:

Do toho helperu dej něco na styl

<?php
function helperClip($subject, $limit)
{
	$elements = preg_match_all('~<.*?>~s', $subject, $matches);
	$raw_text = preg_replace('~<.*?>~s', '&__splitter;', $subject);
	$raw_text_array = explode('&__splitter;', $raw_text);

	$shortened_by = 0;
	while($shortened_by < $limit)
	{
		if (strlen($raw_text_array[-1]) > $limit) {
			$raw_text_array[-1] = substr($raw_text_array[-1], 0, $limit);
			break;
		} else {
			$shortened_by += strlen($raw_text_array[-1]);
			unset($raw_text_array[-1]);
		}
	}

	return /*tady střídavě poskádat $raw_text_array a $elements (začínat podle počtu prvků)*/;
}
?>

Jo tak to sem se docela rozepsal, to sem ani nečekal. Netestoval sem to, takže tam určitě bude nějaké to typo. Plus závěr sem nedodělal, ale to by neměl být problém, viz. comment v kódu.

//Edit: jo, ty záporné indexy nejdou, je to end($raw_text_array)

Editoval Mikulas Dite (10. 3. 2010 21:32)

toka
Člen | 253
+
0
-

Používám tento helper:

class MyString {
  public static function truncate($string, $length, $e = '&hellip;', $isHTML = TRUE){
    $i = 0;
    $tags = array();

    if($isHTML) {
      preg_match_all('/<[^>]+>([^<]*)/', $string, $match, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);

      foreach($match as $object) {
        if($object[0][1] - $i >= $length) break;

        $tag = substr(strtok($object[0][0], " \t\n\r\0\x0B>"), 1);

        if($tag[0] != '/') $tags[] = $tag;
        elseif(end($tags) == substr($tag, 1)) array_pop($tags);

        $i += $object[1][1] - $object[0][1];
      }
    }
    return substr($string, 0, $length = min(strlen($string),  $length + $i)) . (count($tags = array_reverse($tags)) ? '</' . implode('></', $tags) . '>' : '') . (strlen($string) > $length ? $e : '');
  }
}
Endrju
Člen | 147
+
0
-

to Mikulas Dite: diky :). V hlave me napadalo trochu jine reseni, protze jsem pred par mesici delal Crawler v .NET a tam jsem podobne veci resil. To co poslal toka funguje, takze uz to dale nezkoumam.

to toka: Parada, odzkousel jsem to a funguje (je tam jen jedna drobnost..). Byl bys prosim te jeste laskavy a zkusil trosku okomentovat jednotlive casti kodu, abych vedel co to presne dela? Mohl bych si to samozrejmne pozjistovat nastudovanim tech funkci ktere jsi pouzil, ale takto to bude snazsi.

Pouziti:

<div id="content"><?php echo MyString::truncate($htmlConent, 27); ?></div>

K te male drobnosti.. Vstupni text:

<p style="text-align: justify;">Vážení fotbaloví fanoušci,</p>
<p style="text-align: justify;">vítejte ....</p>

Vystup (text je dlouhý 23 znaků, ale zadával jsem 27):

Vážení fotbaloví fanou�
…

Výstup v HTML:

<p>Vážení fotbaloví fanou�</p>
&hellip;

Nevím jak to je s kódováním a jak se převedou znaky s diakritikou, protže to vypdadá, že ta funkce počíta znak s diakritikou jako více znaků a nebo tam je někde renonc… :).
Když bych to chtěl zkrátit na 28 znaků, tak se znak ‚š‘ vypíše „celý“

No a pak bych ještě chtěl, aby se ty tři tečky zobrazily hned za zkráceným slovem (ještě v tom odstavci). Vypadá to takhle nejasně..

Myslíš, že by jsi to dokázal takto upravit?

Jiank moc děkuji za ušetřenou práci a čas. Fakt moc dík!

Ondřej Mirtes
Člen | 1536
+
0
-

Používáš multibyte string metody? Např. mb_substr.

Endrju
Člen | 147
+
0
-

Pokud je to otázka na mě Ondro, tak bych řekl, že ne. Zatím jsem si vystačil s Nette funkcemi, takže pokud není něco přímo tam, tak ne. Nevím jak jinak mám přijít na to, zda nějakou takovou funkci používám. Text vkladám pomocí CKEditoru, ten je pak v databázi uložen jako utf8_bin a zde se přes phpMyAdmina také v pořádku zobrazuje (žádné podivné znaky).

Zkusil jsem s malou improvizací* předhodit stejný text helperu truncate. Ten funguje tak, že zkracuje celá slova, dokud mu nezbyde jediné slovo, to pak zkracuje po znacích a pak přidá trojtečku. Jinak zanechává celá slova.

Ta improvizace tedy spočívala v tom, že jsem musel slovo se znakem ‚š‘ dát jako první slovo textu (jinak by mi to zkrátilo celé slovo) a pak postupně jsem zkracoval, až jsem narazil na místo, kde by měl být odebrán znak ‚š‘. Po jeho odebrání (zkrácení) tam nezbyde zádný balast.. Čili helper truncate to dělá vpořádku.

Podle mě je to způsobeno tou funkcí, kterou dodal „toca“. Když tu funkci nepoužiju, tak se znaky zobrazí správně. Když ji použiju, tak ne.

Bohužel jsem jetě neanalyzoval, jak přesně ta funkce od toca funguje, tak nevím co s tím textem ta funkce provede.. Any idea?


EDIT: přidávám funkci truncate z Nette.. třeba by šla nějak upravit..

/**
 * Truncates string to maximal length.
 * @param  string  UTF-8 encoding
 * @param  int
 * @param  string  UTF-8 encoding
 * @return string
 */
public static function truncate($s, $maxLen, $append = "\xE2\x80\xA6")
{
	if (iconv_strlen($s, 'UTF-8') > $maxLen) {
		$maxLen = $maxLen - iconv_strlen($append, 'UTF-8');
		if ($maxLen < 1) {
			return $append;

		} elseif (preg_match('#^.{1,'.$maxLen.'}(?=[\s\x00-@\[-`{-~])#us', $s, $matches)) {
			return $matches[0] . $append;

		} else {
			return iconv_substr($s, 0, $maxLen, 'UTF-8') . $append;
		}
	}
	return $s;
}

Editoval Endrju (11. 3. 2010 1:14)

Ondřej Mirtes
Člen | 1536
+
0
-

Aha, nevšim jsem si, že toka tady uveřejnil svůj helper. Změnil bych v něm tuto řádku:

$tag = substr(strtok($object[0][0], " \t\n\r\0\x0B>"), 1);

na tuto:

$tag = mb_substr(strtok($object[0][0], " \t\n\r\0\x0B>"), 1, 1024, 'UTF-8');

Ta hodnota 1024 je maximální možná délka vraceného stringu, kdyžtak si to prodluž. Musel jsem jsem jí tam napsat, protože kýžené kódování je až čtvrtý parametr :o)

Třeba to pomůže.

Endrju
Člen | 147
+
0
-

Diky Ondro, zkusil jsem a ee, pořád stejný problémek..

Jinak ten škaredý znak se zobrazí, i když použiju iconv_substr s kódováním místo mb_substr.

Nebylo by lepší nějak předělat původní Nette String::truncate? Chtěl bych totiž, aby mi to zachovávalo celá slova (pokud je k dispozici více než jedno slovo) jako to dělá Nettovský truncate. Pokuším se tomu trchu porozumět, ale zadím jsem v podstatě na bodu mrazu. Nevím jak na to.

Editoval Endrju (11. 3. 2010 1:52)

Endrju
Člen | 147
+
0
-

Hm, trochu jsem zkoušel analyzovat ten kód od toca.. jetě ne úplně, ale napadla mě pak jedna věc a zatím jsem nenašel žádnou chybu.

na posledním řádku jsem také zaměnil substr za mb_substr tedy na posledním řádku zaměnit tohle:

return substr($string, 0, $length = min(strlen($string),  $length + $i))

za tohle:

return mb_substr($string, 0, $length = min(strlen($string),  $length + $i), 'UTF-8')
  • ten zbytek na tom řádku tam samozřejmě nechat.

Editoval Endrju (11. 3. 2010 23:49)

Endrju
Člen | 147
+
0
-

Ondřej Mirtes napsal(a):
Ta hodnota 1024 je maximální možná délka vraceného stringu, kdyžtak si to prodluž. Musel jsem jsem jí tam napsat, protože kýžené kódování je až čtvrtý parametr :o)

Teď mě napadlo – zkoušel jsem hledat max. délku stringu v proměnné.. – Ondro jsi si jistý, že maximální délka stringu je 1024? http://cz.php.net/…s.string.php

It is no problem for a string to become very large. PHP imposes no boundary on the size of a string; the only limit is the available memory of the computer on which PHP is running.

No a když už je tam třeba dávat nějakou délku, tak možná než tam cpát nějaké vymyšlené číslo a případně si zkrátit vstup by bylo lepší tam dát aspoň délku toho vstupního stringu.

Editoval Endrju (11. 3. 2010 2:21)

iguana007
Člen | 970
+
0
-

A není možné, že v proměnné $htmlContent uchováváš obsah v nějakém jiném kódóvání než v UTF-8?

Ondřej Mirtes
Člen | 1536
+
0
-

Endrju: Však jsem řekl, můžeš si tam dát jaké číslo chceš, klidně i vypočtené :)

Endrju
Člen | 147
+
0
-

okej :).

Jinak to prevedeni na UTF-8 staci pouze na konci u toho return.

public static function truncate($string, $length, $e = "\xE2\x80\xA6", $isHTML = TRUE){
	$i = 0;
	$tags = array();

	if($isHTML) {
		preg_match_all('/<[^>]+>([^<]*)/', $string, $match, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);

		foreach($match as $object) {
			if($object[0][1] - $i >= $length) break;

			$token = strtok($object[0][0], " \t\n\r\0\x0B>");
			$tag = substr($token, 1);

			if($tag[0] != '/') $tags[] = $tag;
			elseif(end($tags) == substr($tag, 1)) array_pop($tags);

			$i += $object[1][1] - $object[0][1];
		}
	}
	return iconv_substr($string, 0, $length = min(strlen($string),  $length + $i), 'UTF-8') . (count($tags = array_reverse($tags)) ? '</' . implode('></', $tags) . '>' : '') . (strlen($string) > $length ? $e : '');
}

Asi (dle meho usudku) nebude úplně v pořádku ten regulární výraz. Můžete to někdo zkontrolovat? Nejlépe i odzkoušet?

Nevím proč se mi ve výstupu zobrazuje ten znak ‚<‘? Je to 27. znak ve výstupnímm textu, ale ve vstupním nic takového není! Za čárkou ve vstupním textu následuje rovnou ukonční odstavce..

Pro odzkoušení použijte toto:

<?php echo MyString::truncate($htmlConent, 27); ?>

A jako vstup $htmlConent:

<p style="text-align: justify;">Vážení fotbaloví fanoušci,</p>
<p style="text-align: justify;">vítejte ....</p>

Měli by jste dostat výstup (v prohlížeči):

Vážení fotbaloví fanoušci,<
…

Výstup v HTML:

<p style="text-align: justify;">Vážení fotbaloví fanoušci,&lt;</p>
…

Když zadám zkrácení na 26 znaků, tak se zobrazí Vážení fotbaloví fanoušci, a ten znak ‚<‘ už tam není, což je prozměnu v pořádku.


Pak se mi nelíbí, že se trojtečka zobrazuje někdy mimo odstavec. Třeba když zadám 26 znaků a méně, tak se trojtečka zobrazí až za ukončeným odstavcem (předpokládám, že to je problém někde v tom uzavírání nedokončených tagů). Když zadám ale 28 znaků, tak se trojtečka zobrazí hned za textem ještě než se ukončí odstavec:

Vážení fotbaloví fanoušci,…

Html:

<p style="text-align: justify;">Vážení fotbaloví fanoušci,…</p>

Zkuste na to prosím někdo mrknout, trápím se tu s tím už pěkně dlouho a nevím co s tím.

Editoval Endrju (11. 3. 2010 3:57)

srigi
Nette Blogger | 558
+
0
-

Chalani chalani asi malo citate Jakuba Vranu, on uz to riesil a riesil aj problem kodovania.

Mikulas Dite
Člen | 756
+
0
-

Nedalo mi to a napsal jsem vylepšenou verzi toho svého helperu.
Nyní perfektrně zvládá rozeznávat html tagy, neselže ani na <p id=">">, používá mb knihovny, započítává délku suffixu do limitu a hlavně, maže prázdné hmtl tagy a vkládá suffix před poslední element.

<?php
public function truncate($string, $limit)
    {
        $suffix = ' …';
        $tag_pattern = '</?\w+(?:(?:\s+\w+(?:\s*=\s*(?:".*?"|\'.*?\'|[^\'">\s]+))?)+\s*|\s*)/?>';
        preg_match_all('~' . $tag_pattern  . '~s', $string, $elements);
        $text_array = preg_split('~' . $tag_pattern . '~s', $string);

        $truncated_by = 0;
        while (mb_strlen(implode('', $text_array)) > $limit - mb_strlen($suffix)){
            $len = mb_strlen(end($text_array));
            $truncated_by += $len;
            if ($len < $limit - $truncated_by) {
                unset( $text_array[count($text_array)-1] );
                unset( $elements[0][count($text_array)-1] );
            } else {
                $text_array[count($text_array)-1] = mb_substr(end($text_array), 0, $limit - $truncated_by);
            }
        }

        $output = '';
        for ($i = 0; $i <= max(array_keys($elements)) || $i <= max(array_keys($text_array)); $i++) {
            if (isset ($text_array[$i]))
                $output .= $text_array[$i];
            if ($i + 1 == max(array_keys($text_array)))
                $output .= $suffix;
            if (isset($elements[0][$i]))
                $output .= $elements[0][$i];
        }
        return trim($output);
    }
?>

Tedy z

<p id=">" style="text-align: justify;">Vážení fotbaloví fanoušci,</p>
<p style="text-align: justify;">vítejte ....</p>

udělá při truncate:30

<p id=">" style="text-align: justify;">Vážení fotbaloví fanoušci, …</p>

Editoval Mikulas Dite (11. 3. 2010 20:17)

Endrju
Člen | 147
+
0
-

Mikulas Dite napsal(a):

Nedalo mi to a napsal jsem vylepšenou verzi toho svého helperu.

Pekna prace :). Ma to ale asi jeste neco nedoreseneho.. Jednak magicke cislo 27 :)… Zkrat text na 27 znaku a 3 tecky se zobrazi pred zkracovanym textem – pred odstavcem.

…
<p style="text-align: justify;">Vážení fotbaloví fanoušci,</p>

Pro delku 20 prozmenu dostanu: Fatal Error: Maximum execution time of 30 seconds exceeded. Ale jinak pekna prace, ja si zatim netroufnul to zkusit napsat, takze smekam..

Endrju
Člen | 147
+
0
-

srigi, diky za ten link.

Zkousel jsem pouzit nejake funkce, ktere tam Jakub Vrana. Konkretne jsem se rozhodl pouzivat a nasledne upravit (vysledek nebyl 100%) tu „function xhtml_cut($s, $limit) { ... }“ a „function html_cut($s, $limit) { ... }“. Nezobrazoval se ale presny pocet znaku, jaky jsem zadal (samozrejme jsem odkomentoval tu cast kodu, ktera zpracovava UTF-8). Tudiz jsem debugoval a zjistil, ze i kdyz budu pouzivat multibyte string funkce, tak pri prochazeni po znacich budou znaky s diakritikou tak jako tak pocitany jako vice znaku. Zkusil jsem tedy vsupni retezec preformatovat z UTF-8 na Windows-1250 pomoci funkce iconv (jen si ted nejsem jisty, zda je mozne, aby se nejaky UTF-8 znak neprevedl korektne na Windows-1250 a naopak – nemate nejakou zkusenost?). Po preformatovani, se pak znaky s diakritikou pocitaji jako jeden celistvy znak (a i pri debugovani – prochazeni znak po znaku – se zobrazuji spravne).

Pridana funkcionalita:

  • Na zacatku funkce je vstupni retezec preveden z UTF-8 na Windows-1250
  • Pridan append (defaultne trojtecka) za zkraceny text (nepovinny parametr funkce)
    • je pridan jeste pred vsemi ukoncovacimi tagy a ostatnimi tagy, ktere jsou za zobrazenym textem. Tedy by se nemelo stat, ze by se append zobrzil za ukoncenym odstavcem, odradkovanim nebo treba vodorovnou carou misto za textem v odstavci.
  • Vysledna delka textu je zkracena o delku retezce append, aby sedela maximalni pozdadovana delka zobrazeneho textu
  • Pokud je adpped ponechan defaultne na trojtecku, tak jsou z konce retezce odmazany vsechny ostatni tecky, aby nevzniklo treba neco jako „A tak tomu tedy bylo .....“ + trojtecka.
  • Nakonec je text preveden zase zpet z Windows-1250 do UTF-8.

Tridu jsem umistil do slozky app/helpers/MyStringHelper.php:

/**
 * MyString Helper
 *
 * @author     Endrju
 * @package    Application
 */
abstract class MyString {

	/**
	 * Truncates string containing XHTML tags to maximal length
	 * @param string UTF-8 encoding
	 * @param int
	 * @param string UTF-8 encoding
	 * @return string
	 * @copyright Jakub Vrána, http://php.vrana.cz/
	 * @author Endrju (modifications)
	 */
	public static function xhtmlTruncate($s, $maxLen, $append = "\xE2\x80\xA6")
	{
		// ma vubec smysl retezec zkracovat?
		if (iconv_strlen($s, 'UTF-8') > $maxLen) {
			// prekodujeme z UTF-8 do windows-1250,
			// znaky s diakritikou atp pak budou pocitany jako jeden cely znak
			$s = iconv('UTF-8', 'windows-1250//TRANSLIT', $s);
			$append = iconv('UTF-8', 'windows-1250//TRANSLIT', $append);

			$length = 0;
			$tags = array(); // dosud neuzavřené značky
			for ($i=0; $i < strlen($s) && $length < $maxLen; $i++) {
				switch ($s[$i]) {
					case '<':
						// načtení značky
						$start = $i+1;
						while ($i < strlen($s) && $s[$i] != '>' && !ctype_space($s[$i])) {
							$i++;
						}
						$tag = substr($s, $start, $i - $start);
						// přeskočení případných atributů
						$in_quote = '';
						while ($i < strlen($s) && ($in_quote || $s[$i] != '>')) {
							if (($s[$i] == '"' || $s[$i] == "'") && !$in_quote) {
								$in_quote = $s[$i];

							} elseif ($in_quote == $s[$i]) {
								$in_quote = '';
							}
							$i++;
						}
						if ($s[$start] == '/') { // uzavírací značka
							array_shift($tags); // v XHTML dokumentu musí být vždy uzavřena poslední neuzavřená značka

						} elseif ($s[$i-1] != '/') { // otevírací značka
							array_unshift($tags, $tag);
						}
						break;

					case '&':
						$length++;
						while ($i < strlen($s) && $s[$i] != ';') {
							$i++;
						}
						break;

					default:
						$length++;

						// V případě kódování UTF-8:
						while ($i+1 < strlen($s) && ord($s[$i+1]) > 127 && ord($s[$i+1]) < 192) {
							$i++;
						}
				}
			}

			// zkratime reztezec o delku $append, pokud neni $apped delsi nez samotny retezec
			$s = ($length > strlen($append)) ? substr($s, 0, $i - strlen($append)) : substr($s, 0, $i);

			// uzavreme vsechny tagy
			$enclosingTags = "";
			if ($tags) {
				$enclosingTags .= "</" . implode("></", $tags) . ">";
			}

			// Nyni potrebujeme probublat od konce pres vsechny tagy na konci textu $s,
			$s_beforeInnerEnclosingTags = $s;
			$innerEnclosingTags = "";
			while (substr(rtrim($s_beforeInnerEnclosingTags), - 1, 1) == ">") {
				$innerEnclosingTags = strrchr($s_beforeInnerEnclosingTags, "<");
				$s_beforeInnerEnclosingTags = substr($s_beforeInnerEnclosingTags, 0, strlen($s_beforeInnerEnclosingTags) - strlen($innerEnclosingTags));
			}

			// Pokud je nastaven $append na trojtecku,
			// orezeme jeste samotne tecky na konci rezce (pokud nejake jsou)
			if ($append == iconv('UTF-8', 'windows-1250//TRANSLIT', "\xE2\x80\xA6")) {
				$s_beforeInnerEnclosingTags = rtrim($s_beforeInnerEnclosingTags, '.');
			}

			// Nyni vse spojime dohromady a pridame $append
			$s = $s_beforeInnerEnclosingTags . $append . $innerEnclosingTags . $enclosingTags;

			// Vystupni retezec prekodovany zpet z windows-1250 do UTF-8
			$s = iconv('windows-1250', 'UTF-8//TRANSLIT', $s);
		}

		return $s;
	}


	/**
	 * Truncates string containing HTML tags to maximal length
	 * @param string UTF-8 encoding
	 * @param int
	 * @param string UTF-8 encoding
	 * @return string
	 * @copyright Jakub Vrána, http://php.vrana.cz/
	 * @author Endrju (modifications)
	 */
	public static function htmlTruncate($s, $maxLen, $append = "\xE2\x80\xA6")
	{
		// ma vubec smysl retezec zkracovat?
		if (iconv_strlen($s, 'UTF-8') > $maxLen) {
			static $empty_tags = array('area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param');

			// prekodujeme z UTF-8 do windows-1250,
			// znaky s diakritikou atp pak budou pocitany jako jeden cely znak
			$s = iconv('UTF-8', 'windows-1250//TRANSLIT', $s);
			$append = iconv('UTF-8', 'windows-1250//TRANSLIT', $append);

			$length = 0;
			$tags = array(); // dosud neuzavřené značky
			for ($i=0; $i < strlen($s) && $length < $maxLen; $i++) {
				switch ($s{$i}) {

					case '<':
						// načtení značky
						$start = $i+1;
						while ($i < strlen($s) && $s{$i} != '>' && !ctype_space($s{$i})) {
							$i++;
						}
						$tag = strtolower(substr($s, $start, $i - $start));
						// přeskočení případných atributů
						$in_quote = '';
						while ($i < strlen($s) && ($in_quote || $s{$i} != '>')) {
							if (($s{$i} == '"' || $s{$i} == "'") && !$in_quote) {
							    $in_quote = $s{$i};
							} elseif ($in_quote == $s{$i}) {
							    $in_quote = '';
							}
							$i++;
						}
						if ($s{$start} == '/') { // uzavírací značka
							$tags = array_slice($tags, array_search(substr($tag, 1), $tags) + 1);
						} elseif ($s{$i-1} != '/' && !in_array($tag, $empty_tags)) { // otevírací značka
							array_unshift($tags, $tag);
						}
						break;

					case '&':
						$length++;
						while ($i < strlen($s) && $s{$i} != ';') {
							$i++;
						}
						break;

					default:
						$length++;

						// V případě kódování UTF-8:
						while ($i+1 < strlen($s) && ord($s[$i+1]) > 127 && ord($s[$i+1]) < 192) {
							$i++;
						}
				}
			}

			// zkratime reztezec o delku $append, pokud neni $apped delsi nez samotny retezec
			$s = ($length > strlen($append)) ? substr($s, 0, $i - strlen($append)) : substr($s, 0, $i);

			// uzavreme vsechny tagy
			$enclosingTags = "";
			if ($tags) {
				$enclosingTags .= "</" . implode("></", $tags) . ">";
			}

			// Nyni potrebujeme probublat od konce pres vsechny tagy na konci textu $s,
			$s_beforeInnerEnclosingTags = $s;
			$innerEnclosingTags = "";
			while (substr(rtrim($s_beforeInnerEnclosingTags), - 1, 1) == ">") {
				$innerEnclosingTags = strrchr($s_beforeInnerEnclosingTags, "<");
				$s_beforeInnerEnclosingTags = substr($s_beforeInnerEnclosingTags, 0, strlen($s_beforeInnerEnclosingTags) - strlen($innerEnclosingTags));
			}

			// Pokud je nastaven $append na trojtecku,
			// orezeme jeste samotne tecky na konci rezce (pokud nejake jsou)
			if ($append == iconv('UTF-8', 'windows-1250//TRANSLIT', "\xE2\x80\xA6")) {
				$s_beforeInnerEnclosingTags = rtrim($s_beforeInnerEnclosingTags, '.');
			}

			// Nyni vse spojime dohromady a pridame $append
			$s = $s_beforeInnerEnclosingTags . $append . $innerEnclosingTags . $enclosingTags;

			// Vystupni retezec prekodovany zpet z windows-1250 do UTF-8
			$s = iconv('windows-1250', 'UTF-8//TRANSLIT', $s);
		}

		return $s;
	}
}

Do app/presenters/BasePresenter.php jsem zaregistroval helpery:

abstract class BasePresenter extends Presenter
{
	public $oldLayoutMode = FALSE;
	public $oldModuleMode = FALSE;

	protected function beforeRender()
	{
		$this->template->registerHelper('xhtmlTruncate', array('MyString', 'xhtmlTruncate'));
		$this->template->registerHelper('htmlTruncate', array('MyString', 'htmlTruncate'));
	}

}

Pouziti pak v jakekoli sablone napr:

{block content}
	<div id="xhtmlContent">{!$newsItem->content|xhtmlTruncate:300}</div>
	<div id="htmlContent">{!$newsItem->content|htmlTruncate:300}</div>
{/block}

Premyslel jsem taky jak docilit toho, aby se zkracovala cela slova – aby to nezkracovalo v pulce slova (pokud v retezci teda neni jen jedine slovo). Tedy presne tak, jak funguje Nette\String::truncate()

Bohuzel jsem narazil na problem, ze kdyz jsem zkracoval text o zbytky slov, tak se napriklad stalo, ze nebyly odstraneny tagy, ktere to slovo obaluji a pak mi to rozhodilo layout nebo narusilo jine tagy.. Kdyby jste si s tim nekdo zkusili pohrat a prijit na to, tak by to bylo strasne fajn :).

Jinak diky vsem za pomoc :)

Editoval Endrju (12. 3. 2010 18:28)

MzK
Člen | 127
+
0
-

uff, todle je už pro mě vyšší dívčí… Ale záleží na html vs xhtml.. Dobře to popsal Jakub Vrána.
http://php.vrana.cz/…znackami.php

Endrju
Člen | 147
+
0
-

Editoval jsem svuj predchozi post + znacne upravil kod a pridal tam i upravenou funkci pro HtmlTruncate. Testoval jsem obe funkce na vstupech, ktere vygeneroval CKEditor a vysledek byl naprosto stejny.

zacatecnik: Funkce kterou zde v komentarich postnul „Mike“ nefunguje. On na zacatku funkce zkrati cely text na pozadovanou delku (nebere v potazt to, ze tam muzou byt HTML znacky a ze tim prichazi o zobrazovany text. Pak v tom co mu zbylo najde posledni mezeru v domeni ze je to posledni slovo zobrazeneho textu, tak to co predchazi te mezere pak pusti dal do funkce. No a v nejhorsim pripade pusti do funkce jen cast nejake HTML znacky s parametry a zadny text..

Editoval Endrju (12. 3. 2010 18:26)

Endrju
Člen | 147
+
0
-

No nedalo mi to a do ted jsem na tom sedel… :-). Jednak tam byly jeste nejake nedostatky a ja to chtel funkcni v podstate uplne stejne jako Nette\String::truncate(), coz se mi nakonec, huraaaa, povedlo :-).

Mozna se budu castecne opakovat, ale kdyz uz je to (doufam) funkcni, tak at je to pohromade.. Funkce ma tri parametry, stejne jako jste zvykli u String::truncate($s, $maxLen, $append = "\xE2\x80\xA6") a funguje to uplne stejne, s tim rozdilem, ze jako vstup nedavate plain text, ale (X)HTML text. Odzkousene to mam na vstupech, ktere generuje CKEditor (ver. 3.1) a pak na pár co jsem si jen tak zkoušel…

Na pocet radku je to mozna dlouhe, ale hodne delaji ty komentare (zvyk). Coz mi ale za rok treba pomuze rozpomenout jak jsem to delal nebo kdyz by to po me nekdo cetl :-).

Obe metody xhtmlTruncate a htmlTruncate funguji stejne:

  • Pridan append (defaultne trojtecka) za zkraceny text (nepovinny parametr funkce).
    • je pridan jeste pred vsechny ukoncovaci tagy a ostatnimi tagy, ktere jsou za zobrazenym textem. Tedy by se nemelo stat, ze by se append zobrzil za ukoncenym odstavcem, odradkovanim nebo treba vodorovnou carou misto za textem v odstavci.
  • Vysledna delka textu je zkracena o delku retezce append, aby sedela maximalni pozdadovana delka zobrazeneho textu.
  • Pokud je append ponechan defaultne na trojtecku, tak jsou z konce retezce odmazany vsechny ostatni tecky, aby nevzniklo treba neco jako „A tak tomu tedy bylo .....“ + trojtecka.
  • Zkracovana jsou cela slova, ale tak aby nebyla prekrocena maximalni pozadovana delka textu.
    • Jako separatory pro oddelovani slov jsem zvolil tyto (muzete si je upravit): $separators = array (' ', ',', '.', ';', '?', '!', ':');
      • Dva a vice stejnych separatoru bezprostredne za sebou nejsou brany jako separatory (zduvodneno je to v kodu v komentarich)
    • Pokud zustava jen posledni slovo k zobrazeni, je zkracovano po znacich.

To je asi vsechno, pokud jsem na neco nezapomel.

Tridu jsem umistil do slozky app/helpers/MyStringHelper.php:

/**
 * MyString Helper
 *
 * @author     Endrju
 * @package    Application
 */
abstract class MyString {


	/**
	 * Truncates string containing XHTML tags to maximal length
	 * @param string UTF-8 encoding
	 * @param int
	 * @param string UTF-8 encoding
	 * @return string
	 * @copyright Jakub Vrána, http://php.vrana.cz/
	 * @author Endrju (modifications)
	 */
	public static function xhtmlTruncate($s, $maxLen, $append = "\xE2\x80\xA6")
	{
		// ma vubec smysl retezec zkracovat?
		if (iconv_strlen($s, 'UTF-8') > $maxLen) {
			// zkratime $maxLen o delku $append
			$maxLen = $maxLen - iconv_strlen($append, 'UTF-8');
			// pokud je nyni $maxLen kratsi bez $appedm vratime samotonty $append
			if ($maxLen < iconv_strlen($append, 'UTF-8')) {
				return $append;
			}

			// vybrane znaky, ktere muzou ukoncovat vetu nebo vyznam casti vety
			$separators = array (' ', ',', '.', ';', '?', '!', ':');
			$pos = 0; // pozice posledniho nalezeneho separatoru

			// prekodujeme z UTF-8 do windows-1250,
			// znaky s diakritikou atp pak budou pocitany jako jeden cely znak
			$s = iconv('UTF-8', 'windows-1250//TRANSLIT', $s);
			$INITAL_S = $s; // originalni vstupni retezec $s, ktery nebude po dobu behu programu zmenen.
			$append = iconv('UTF-8', 'windows-1250//TRANSLIT', $append);

			// odstranime neviditelne znaky,
			// ktere se ve vygenerovanem a zobrazenem HTML textu stejne nezobrazi
			$customWhitespaces = array("\0x09", "\0x0A", "\0x0D", "\0x00", "\0x0B");
			foreach ($customWhitespaces AS $customWhitespace) {
				$s = trim($s, $customWhitespace);
			}

			$length = 0;
			$tags = array(); // dosud neuzavřené značky
			for ($i=0; $i < strlen($s) && $length < $maxLen; $i++) {
				switch ($s[$i]) {
					case '<':
						// načtení značky
						$start = $i+1;
						while ($i < strlen($s) && $s[$i] != '>' && !ctype_space($s[$i])) {
							$i++;
						}
						$tag = strtolower(substr($s, $start, $i - $start));
						// přeskočení případných atributů
						$in_quote = '';
						while ($i < strlen($s) && ($in_quote || $s[$i] != '>')) {
							if (($s[$i] == '"' || $s[$i] == "'") && !$in_quote) {
								$in_quote = $s[$i];

							} elseif ($in_quote == $s[$i]) {
								$in_quote = '';
							}
							$i++;
						}
						if ($s[$start] == '/') { // uzavírací značka
							array_shift($tags); // v XHTML dokumentu musí být vždy uzavřena poslední neuzavřená značka

						} elseif ($s[$i-1] != '/') { // otevírací značka
							array_unshift($tags, $tag);
						}
						break;

					case '&':
						$length++;
						while ($i < strlen($s) && $s[$i] != ';') {
							$i++;
						}
						break;

					default:
						$length++;

						/* V případě kódování UTF-8:
						while ($i+1 < strlen($s) && ord($s[$i+1]) > 127 && ord($s[$i+1]) < 192) {
							$i++;
						}*/

						// je znak separatorem?
						if (in_array($s[$i], $separators)) {
							// * a neni nasledujici (nebo predchozi) znak totozny,
							// jako nyni nacteny separator?
							if (($s[$i] != $s[$i+1]) && ($s[$i-1] != $s[$i])) {
								$pos = $i; // pak ulozime pozici separatoru
							} // nakonec tak ziskame pozici posledniho separatoru

							// * tou druhou podminkou chceme zachytit pripady, kdy je v textu
							// nekolik separatotu (napr. tecek) za sebou, pak to nebudeme povazovat za separator,
							// ale jako vyznam k predchazejicimu slovu (vete).
							// Navic pokud by takovy separator byl na konci oriznuteho textu, bude tak v podstate
							// nahrazen volitelnym retezcem $append (defaultne trojtecka).
						}
				}
			}

			// pokud nenalezneme hledany pocet znaku, obsah je nejspis slozen ciste z HTML tagu
			// (napr. flashova videa a jine medialni objekty)
			if ($length >= $maxLen) {

				$s = substr($s, 0, $i);

				// uzavreme vsechny tagy
				$enclosingTags = "";
				if ($tags) {
					$enclosingTags .= "</" . implode("></", $tags) . ">";
				}

				// Nyni potrebujeme probublat od konce pres vsechny tagy az na konec zobrazovaneho textu $s
				// (tam pak budeme pridavat $append)
				$s_beforeInnerEnclosingTags = $s;
				$innerEnclosingTags = "";
				while (substr(rtrim($s_beforeInnerEnclosingTags), - 1, 1) == ">") {
					$innerEnclosingTags = strrchr($s_beforeInnerEnclosingTags, "<");
					$s_beforeInnerEnclosingTags = substr($s_beforeInnerEnclosingTags, 0, strlen($s_beforeInnerEnclosingTags) - strlen($innerEnclosingTags));
				}

				// Pokud je nastaven $append na trojtecku,
				// orezeme jeste samotne tecky na konci rezce (pokud nejake jsou)
				if ($append == iconv('UTF-8', 'windows-1250//TRANSLIT', "\xE2\x80\xA6")) {
					$s_beforeInnerEnclosingTags = rtrim($s_beforeInnerEnclosingTags, '.');
				}

				// pokud byl nejaky separator nalezen
				// a zaroven znak za zkracovanym textem neni separatorem
				if (($pos > 0) && (!in_array(substr($INITAL_S, strlen($s_beforeInnerEnclosingTags), 1), $separators))) {
					// tak orizneme text za poslednim nalezenym separatorem
					$s_beforeInnerEnclosingTags = substr($s_beforeInnerEnclosingTags, 0, $pos);
				}
				// nebo take pokud byl nejaky separator nalezen
				// a zaroven 2 znaky za zkracovanym textem jsou 2 stejne separatory (viz. * o neco vyse - stejny pripad)
				else if (($pos > 0) && (in_array(substr($INITAL_S, strlen($s_beforeInnerEnclosingTags), 1), $separators)) && ((substr($INITAL_S, strlen($s_beforeInnerEnclosingTags), 1) == ((substr($INITAL_S, strlen($s_beforeInnerEnclosingTags) + 1, 1)))))) {
					// tak orizneme text za poslednim nalezenym separatorem a dva vyse zminene ignorujeme
					$s_beforeInnerEnclosingTags = substr($s_beforeInnerEnclosingTags, 0, $pos);
				}

				// Nyni vse spojime dohromady a pripojime $append
				$s = $s_beforeInnerEnclosingTags . $append . $innerEnclosingTags . $enclosingTags;
			    }

			// Vystupni retezec prekodujeme zpet z windows-1250 do UTF-8
			$s = iconv('windows-1250', 'UTF-8//TRANSLIT', $s);
		}

		return $s;
	}



	/**
	 * Truncates string containing HTML tags to maximal length
	 * @param string UTF-8 encoding
	 * @param int
	 * @param string UTF-8 encoding
	 * @return string
	 * @copyright Jakub Vrána, http://php.vrana.cz/
	 * @author Endrju (modifications)
	 */
	public static function htmlTruncate($s, $maxLen, $append = "\xE2\x80\xA6")
	{
		// ma vubec smysl retezec zkracovat?
		if (iconv_strlen($s, 'UTF-8') > $maxLen) {
			// zkratime $maxLen o delku $append
			$maxLen = $maxLen - iconv_strlen($append, 'UTF-8');
			// pokud je nyni $maxLen kratsi bez $appedm vratime samotonty $append
			if ($maxLen < iconv_strlen($append, 'UTF-8')) {
				return $append;
			}

			static $empty_tags = array('area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param');

			// vybrane znaky, ktere muzou ukoncovat vetu nebo vyznam casti vety
			$separators = array (' ', ',', '.', ';', '?', '!', ':');
			$pos = 0; // pozice posledniho nalezeneho separatoru

			// prekodujeme z UTF-8 do windows-1250,
			// znaky s diakritikou atp pak budou pocitany jako jeden cely znak
			$s = iconv('UTF-8', 'windows-1250//TRANSLIT', $s);
			$INITAL_S = $s; // originalni vstupni retezec $s, ktery nebude po dobu behu programu zmenen.
			$append = iconv('UTF-8', 'windows-1250//TRANSLIT', $append);

			// odstranime neviditelne znaky,
			// ktere se ve vygenerovanem a zobrazenem HTML textu stejne nezobrazi
			$customWhitespaces = array("\0x09", "\0x0A", "\0x0D", "\0x00", "\0x0B");
			foreach ($customWhitespaces AS $customWhitespace) {
				$s = trim($s, $customWhitespace);
			}

			$length = 0;
			$tags = array(); // dosud neuzavřené značky
			for ($i=0; $i < strlen($s) && $length < $maxLen; $i++) {
				switch ($s[$i]) {
					case '<':
						// načtení značky
						$start = $i+1;
						while ($i < strlen($s) && $s[$i] != '>' && !ctype_space($s[$i])) {
							$i++;
						}
						$tag = strtolower(substr($s, $start, $i - $start));
						// přeskočení případných atributů
						$in_quote = '';
						while ($i < strlen($s) && ($in_quote || $s[$i] != '>')) {
							if (($s[$i] == '"' || $s[$i] == "'") && !$in_quote) {
							    $in_quote = $s[$i];

							} elseif ($in_quote == $s[$i]) {
							    $in_quote = '';
							}
							$i++;
						}
						if ($s{$start} == '/') { // uzavírací značka
							$tags = array_slice($tags, array_search(substr($tag, 1), $tags) + 1);

						} elseif ($s{$i-1} != '/' && !in_array($tag, $empty_tags)) { // otevírací značka
							array_unshift($tags, $tag);
						}
						break;

					case '&':
						$length++;
						while ($i < strlen($s) && $s[$i] != ';') {
							$i++;
						}
						break;

					default:
						$length++;

						/* V případě kódování UTF-8:
						while ($i+1 < strlen($s) && ord($s[$i+1]) > 127 && ord($s[$i+1]) < 192) {
							$i++;
						}*/

						// je znak separatorem?
						if (in_array($s[$i], $separators)) {
							// * a neni nasledujici (nebo predchozi) znak totozny,
							// jako nyni nacteny separator?
							if (($s[$i] != $s[$i+1]) && ($s[$i-1] != $s[$i])) {
								$pos = $i; // pak ulozime pozici separatoru
							} // nakonec tak ziskame pozici posledniho separatoru

							// * tou druhou podminkou chceme zachytit pripady, kdy je v textu
							// nekolik separatotu (napr. tecek) za sebou, pak to nebudeme povazovat za separator,
							// ale jako vyznam k predchazejicimu slovu (vete).
							// Navic pokud by takovy separator byl na konci oriznuteho textu, bude tak v podstate
							// nahrazen volitelnym retezcem $append (defaultne trojtecka).
						}
				}
			}

			// pokud nenalezneme hledany pocet znaku, obsah je nejspis slozen ciste z HTML tagu
			// (napr. flashova videa a jine medialni objekty)
			if ($length >= $maxLen) {

				$s = substr($s, 0, $i);

				// uzavreme vsechny tagy
				$enclosingTags = "";
				if ($tags) {
					$enclosingTags .= "</" . implode("></", $tags) . ">";
				}

				// Nyni potrebujeme probublat od konce pres vsechny tagy az na konec zobrazovaneho textu $s
				// (tam pak budeme pridavat $append)
				$s_beforeInnerEnclosingTags = $s;
				$innerEnclosingTags = "";
				while (substr(rtrim($s_beforeInnerEnclosingTags), - 1, 1) == ">") {
					$innerEnclosingTags = strrchr($s_beforeInnerEnclosingTags, "<");
					$s_beforeInnerEnclosingTags = substr($s_beforeInnerEnclosingTags, 0, strlen($s_beforeInnerEnclosingTags) - strlen($innerEnclosingTags));
				}

				// Pokud je nastaven $append na trojtecku,
				// orezeme jeste samotne tecky na konci rezce (pokud nejake jsou)
				if ($append == iconv('UTF-8', 'windows-1250//TRANSLIT', "\xE2\x80\xA6")) {
					$s_beforeInnerEnclosingTags = rtrim($s_beforeInnerEnclosingTags, '.');
				}

				// pokud byl nejaky separator nalezen
				// a zaroven znak za zkracovanym textem neni separatorem
				if (($pos > 0) && (!in_array(substr($INITAL_S, strlen($s_beforeInnerEnclosingTags), 1), $separators))) {
					// tak orizneme text za poslednim nalezenym separatorem
					$s_beforeInnerEnclosingTags = substr($s_beforeInnerEnclosingTags, 0, $pos);
				}
				// nebo take pokud byl nejaky separator nalezen
				// a zaroven 2 znaky za zkracovanym textem jsou 2 stejne separatory (viz. * o neco vyse - stejny pripad)
				else if (($pos > 0) && (in_array(substr($INITAL_S, strlen($s_beforeInnerEnclosingTags), 1), $separators)) && ((substr($INITAL_S, strlen($s_beforeInnerEnclosingTags), 1) == ((substr($INITAL_S, strlen($s_beforeInnerEnclosingTags) + 1, 1)))))) {
					// tak orizneme text za poslednim nalezenym separatorem a dva vyse zminene ignorujeme
					$s_beforeInnerEnclosingTags = substr($s_beforeInnerEnclosingTags, 0, $pos);
				}

				// Nyni vse spojime dohromady a pripojime $append
				$s = $s_beforeInnerEnclosingTags . $append . $innerEnclosingTags . $enclosingTags;
			}

			// Vystupni retezec prekodujeme zpet z windows-1250 do UTF-8
			$s = iconv('windows-1250', 'UTF-8//TRANSLIT', $s);
		}

		return $s;
	}
}

Do app/presenters/BasePresenter.php jsem zaregistroval helpery:

abstract class BasePresenter extends Presenter
{
	public $oldLayoutMode = FALSE;
	public $oldModuleMode = FALSE;

	protected function beforeRender()
	{
		$this->template->registerHelper('xhtmlTruncate', array('MyString', 'xhtmlTruncate'));
		$this->template->registerHelper('htmlTruncate', array('MyString', 'htmlTruncate'));
	}
}

No a pak v sablonach (ted uz z vesela :-)) lze pouzivat:

{block content}
	<div id="content">{!$newsItem->content|xhtmlTruncate:400}</div>
	<!-- nebo -->
	<div id="content">{!$newsItem->content|htmlTruncate:400}</div>

{/block}

Tak.. a je to, diky vsem za spolupraci a popostuchovani :-). Bylo by dobre, kdyby jste to otestovali a chyby pripadne napsali.

cuga
Člen | 210
+
0
-

hoj, stalo se vam nekomu, ze vam to orezava prvni znak? mam zdrojovy texy v texy, ten prozenu parserem a na vyplivnute xHTML aplikuju truncate… akorat ze v nekterych pripadech mi to odrizne prvni pismeno… zkusim vysledovat nejaky duvod…

Honza Kuchař
Člen | 1662
+
0
-

Endrju napsal(a):

Vypadá to dobře. Nechceš to přidat do extras? :-)

cuga
Člen | 210
+
0
-

mozna by bylo dobre pred pridanim do extras odstranit ten bug ;)

cuga
Člen | 210
+
0
-

hlasi mi

<b>Notice</b>:  Uninitialized string offset: 11 in <b>C:\xampp\htdocs\vs\libs\Extras\MyString.php</b> on line <b>98</b><br />

na radku je

if (($s[$i] != $s[$i+1]) && ($s[$i-1] != $s[$i])) {
Endrju
Člen | 147
+
0
-

cuga napsal(a):

hoj, stalo se vam nekomu, ze vam to orezava prvni znak? mam zdrojovy texy v texy, ten prozenu parserem a na vyplivnute xHTML aplikuju truncate… akorat ze v nekterych pripadech mi to odrizne prvni pismeno… zkusim vysledovat nejaky duvod…

cuga napsal(a):

hlasi mi

<b>Notice</b>:  Uninitialized string offset: 11 in <b>C:\xampp\htdocs\vs\libs\Extras\MyString.php</b> on line <b>98</b><br />

na radku je

if (($s[$i] != $s[$i+1]) && ($s[$i-1] != $s[$i])) {

Na Texy jsem to vubec nezkousel (zatim jsem ji nepotreboval). Je to testovano na CKEditoru a tam jsem problem neshledal. Pokud tam je nejaka chybka, zkus si tam dat breakponty a debugovat to, vysledovat, kde se ti ztrati nejaky znak atd.. Treba najdes v cem je problem. Jen upozornim, ze jako vstup je pozadovan string v UTF-8.

Co se tyka debugovani, tak by se ti hodilo nejake IDE a pokud nemas tu moznost, tak by mozna pomohlo si v patricnych mistech kodu dat Debug::dump($promenna); coz ti vypise hodnotu te promenne a zastavi kod v tom miste (nemylim-li se).

honzakuchar napsal(a):

Endrju napsal(a):

Vypadá to dobře. Nechceš to přidat do extras? :-)

Pokud tam jsou nejake chyby, tak by to asi nebylo dobre, co :). Bohuzel ted a ani nasledujici mesic az dva nebudu mit cas (diplomka, statnice) hledat v cem je problem, pokud by jste to nekdo opravili mezititm, tak to bude super :).

A kdyz uz jsem u toho, tak jsem nedavno udelal drobnou upravu, ktera jeste vice optimalizuje zpracovani (nevim proc me to driv nenapadlo :))

Na prvnich dvou radcich v obou metodach (xhtmlTruncate() a htmlTruncate()) nahradte tento kod:

	// ma vubec smysl retezec zkracovat?
	if (iconv_strlen($s, 'UTF-8') > $maxLen) {

Za tento kod:

	// ma vubec smysl retezec zkracovat?
	if (iconv_strlen(strip_tags($s), 'UTF-8') > $maxLen) {

Puvodne to kontrolovalo delku celeho stringu vcetne (x)HTML tagu a pripadne nemuselo k fyzickemu zkraceni cisteho textu fubec dojit, ale funkce to stejne cele musela zpracovat. Oprava zpusobi, ze se bude kontrolovat pouze zda onen cisty text nepresahuje povolenou delku (tim ze se pri kontrole odstrani HTML tagy) a kod se bude dal zpracovavat jen pokud cisty text je delsi nez maximalni povolena delka.
Jeste me ted napadlo, ze by se ten text po odstraneni HTML znacek mohl trimnout o bile znaky na zacatku a na konci. Tedy by to vypadlo takto:

	// ma vubec smysl retezec zkracovat?
	if (iconv_strlen(trim(strip_tags($s), 'UTF-8')) > $maxLen) {

Tohle ale nemam odzkouseno, tak nevim, zda by to nemohlo delat nejakou neplechu. Dovolim si ale tvrdit, ze by to bylo naprosto v poradku.

Editoval Endrju (16. 4. 2010 22:29)

Quinix
Člen | 108
+
0
-

cuga napsal(a):

hoj, stalo se vam nekomu, ze vam to orezava prvni znak? mam zdrojovy texy v texy, ten prozenu parserem a na vyplivnute xHTML aplikuju truncate… akorat ze v nekterych pripadech mi to odrizne prvni pismeno… zkusim vysledovat nejaky duvod…

Žraní znaků způsobuje tento kód:

<?php
$customWhitespaces = array("\0x09", "\0x0A", "\0x0D", "\0x00", "\0x0B");
foreach ($customWhitespaces AS $customWhitespace) {
   $s = trim($s, $customWhitespace);
}
?>

Důvodem je nejspíš to, že řetězce v poli $customWhitespaces to bere jako řetězec např. „\0×09“ a trim() pak odstraní vysloveně tyhle znaky, čili i třeba A, B, D, pokud jsou na krajích textu.
Nevim, jestli jsem správně pochopil myšlenku toho kódu, ale pole by se mělo imho nahradit něčím takovýmhle:

<?php
$customWhitespaces = array(chr(0x09),chr(0x0a),chr(0x0d),chr(0x00),chr(0x0b));
?>

Pak už to nic nežere :)

Endrju
Člen | 147
+
0
-

Cílem bylo trimnout jen určité znaky na krajích textu, takže zdá se chápeš správně :).

Quinix napsal(a):

Hm, to zní asi celkem roumně, ne :)? Dík za fix .)

Cuga: Mohl bys to testnout i u sebe?

Editoval Endrju (27. 5. 2010 19:45)

Panda
Člen | 569
+
0
-

Nestačilo by to napsat takto?

$customWhitespaces = array("\x09", "\x0A", "\x0D", "\x00", "\x0B");
cuga
Člen | 210
+
0
-

potrvzuju ze uprava od Quinixe pomohla :) diky

aannubis
Člen | 33
+
0
-

Narazil jsem na zajimavou chybu. Pokud mame treba takovyto kod

<p><strong>Text text text</strong><em>OPRAVDUHODNEDLOUHESLOVO</em></p>

a pokusime se ho zkratit treba na 15 znaku, tak implementace nezkracujici slova, se nam pokusi vratit Text text text. Ovsem tak jak je funkce napsana, se takto odstrani uzavreni znacky <strong> a zacatecni znacka <em>.

Zpusobuje to tento kod:

if (($pos > 0) && (!in_array(substr($INITAL_S, strlen($s_beforeInnerEnclosingTags), 1), $separators))) {
    // tak orizneme text za poslednim nalezenym separatorem
    $s_beforeInnerEnclosingTags = substr($s_beforeInnerEnclosingTags, 0, $pos);
}

Ten ma jako pozici posledniho separatoru ulozeno misto po poslednim slovu text, tam retezec orizne, avsak jako neuzavrene tagy ma ulozeno v tu chvili em a p, ale strong, ktery oriz uz ulozeny nema, takze ho neuzavre, a naopak uzavre em, ktery ovsem orizl, takze vznikne toto:

<p><strong>Text text text</em></p>

Nema autor jiz k dispozici nejakou opravu? Nevim moc ciste jako to opravit :(

Editoval aannubis (4. 5. 2011 18:13)