PHP. Балансировка тегов object

Задача:
Необходимо обеспечить обрезание html текста до определенной длины с балансировкой тегов. Обеспечить полноценную обработку object тегов.

Решение:
Для решения первой части задачи мы возьмем функцию из wordpress, которая называется balance_tags.
Она прекрасно справляется с задачей балансировки тегов до тех пор, пока в тексте не встретится тег object, который содержит вложенные теги param.
Если наша функция truncate обрежет текст на середине тега object? Скажем, таким образом 

<param src=""...

В результате поплывет верстка. Чтобы избежать этого эффекта,
я доработал функцию balance_tags. Алгоритм таков:

1) выгребаем все теги object и засовываем их в массив
2) заменяем их на теги <obj>n</obj>, где n - это порядковый номер тега object
3) обрезаем текст
4) балансируем теги
5) заменяем <obj>n</obj> на соответсвующий тег.

function object2obj($matches)
{
static $n = 0;
return '<obj>'.$n++.'</obj>';
}

function obj2object($matches)
{
global $objects;
$num = (int)$matches[1];
if( isset($matches[0]) && isset($objects[0][$num]) )
{
return $objects[0][$num];
}
return false;
}

function balance_tags( $params ) {
$text = $params['content'];
$truncate = isset($params['truncate']) ? $params['truncate'] : '';

global $objects;


preg_match_all("/(<object.{1,700}/object>)/mis", $text, $objects);
if( isset($objects[0]) && sizeof($objects[0]) > 0 )
{
$pos = strpos($text, '<object');
$text = preg_replace_callback(array('/(<object.{1,700}/object>)/mis'), 'object2obj', $text);

if( $truncate && $pos < $truncate )
{
$objlen = 0;
foreach( $objects[0] as $object )
{
$objlen =+ strlen($object) + 300;
if( $objlen > $truncate ) break;
}
$truncate -= $objlen;
}
}

if( $truncate )
{
$text = truncate($text, $truncate);
}


$tagstack = array();
$stacksize = 0;
$tagqueue = '';
$newtext = '';
$single_tags = array('br', 'hr', 'img', 'input'); // Known single-entity/self-closing tags
$nestable_tags = array('blockquote', 'div', 'span', 'obj'); // Tags that can be immediately nested within themselves

// WP bug fix for comments - in case you REALLY meant to type '< !--'
$text = str_replace('< !--', '< !--', $text);
// WP bug fix for LOVE <3 (and other situations with '<' before a number)
$text = preg_replace('#<([0-9]{1})#', '&lt;$1', $text);

while ( preg_match("/<(/?[w:]*)s*([^>]*)>/", $text, $regex) ) {
$newtext .= $tagqueue;

$i = strpos($text, $regex[0]);
$l = strlen($regex[0]);

// clear the shifter
$tagqueue = '';
// Pop or Push
if ( isset($regex[1][0]) && '/' == $regex[1][0] ) { // End Tag
$tag = strtolower(substr($regex[1],1));
// if too many closing tags
if( $stacksize <= 0 ) {
$tag = '';
// or close to be safe $tag = '/' . $tag;
}
// if stacktop value = tag close value then pop
else if ( $tagstack[$stacksize - 1] == $tag ) { // found closing tag
$tag = '</' . $tag . '>'; // Close Tag
// Pop
array_pop( $tagstack );
$stacksize--;
} else { // closing tag not at top, search for it
for ( $j = $stacksize-1; $j >= 0; $j-- ) {
if ( $tagstack[$j] == $tag ) {
// add tag to tagqueue
for ( $k = $stacksize-1; $k >= $j; $k--) {
$tagqueue .= '</' . array_pop( $tagstack ) . '>';
$stacksize--;
}
break;
}
}
$tag = '';
}
} else { // Begin Tag
$tag = strtolower($regex[1]);

// Tag Cleaning

// If self-closing or '', don't do anything.
if ( substr($regex[2],-1) == '/' || $tag == '' ) {
// do nothing
}
// ElseIf it's a known single-entity tag but it doesn't close itself, do so
elseif ( in_array($tag, $single_tags) ) {
$regex[2] .= '/';
} else { // Push the tag onto the stack
// If the top of the stack is the same as the tag we want to push, close previous tag
if ( $stacksize > 0 && !in_array($tag, $nestable_tags) && $tagstack[$stacksize - 1] == $tag ) {
$tagqueue = '</' . array_pop ($tagstack) . '>';
$stacksize--;
}
$stacksize = array_push ($tagstack, $tag);
}

// Attributes
$attributes = $regex[2];
if( !empty($attributes) )
$attributes = ' '.$attributes;

$tag = '<' . $tag . $attributes . '>';
//If already queuing a close tag, then put this tag on, too
if ( !empty($tagqueue) ) {
$tagqueue .= $tag;
$tag = '';
}
}
$newtext .= substr($text, 0, $i) . $tag;
$text = substr($text, $i + $l);
}

// Clear Tag Queue
$newtext .= $tagqueue;

// Add Remaining text
$newtext .= $text;

// Empty Stack
while( $x = array_pop($tagstack) )
$newtext .= '</' . $x . '>'; // Add remaining tags to close

// WP fix for the bug with HTML comments
$newtext = str_replace("< !--","<!--",$newtext);
$newtext = str_replace("< !--","< !--",$newtext);

if( isset($objects[0]) && sizeof($objects) > 0 )
{
$newtext = preg_replace_callback(array('/<obj>(d*)</obj>/'), 'obj2object', $newtext);
}
return $newtext;

}

function truncate($string, $length = 80, $etc = '', $break_words = false, $middle = false)
{
if ($length == 0)
return '';

if (strlen($string) > $length) {
$length -= min($length, strlen($etc));
if (!$break_words && !$middle) {
$string = preg_replace('/s+?(S+)?$/', '', substr($string, 0, $length+1));
}
if(!$middle) {
return substr($string, 0, $length) . $etc;
} else {
return substr($string, 0, $length/2) . $etc . substr($string, -$length/2);
}
} else {
return $string;
}
}

вот и все

Интересное

Отзыв о FirstVDS
При покупке хостинга у FirstVDS мне предложили скидку за годный отзыв в блоге или на форуме....
Project Glass — Очки будущего от Google
Компания Google представила суперсовременные «очки дополненной реальности» или Project Glass. Впрочем,...
Проверка сайта на вирусы
Если пользоваться привычной терминологией, то web вирусы не являются вирусами в традиционном...
Git и Bitbucket
Git – это распределенная система контроля версий. Пожалуй, на этом теория заканчивается, так как...

Комментарии

    Чтобы оставлять комментарии, авторизируйтесь

    Войти через OpenID

    рейтинг0+-
    Популярные
    Точки входа
    Наверх