оптимизация кода - вопрос экспертам

idler

Новичок
оптимизация кода - вопрос экспертам

контент сайта хранится в БД

перед выпечатыванием его на страницу нужно сделать автолинковку - т.е. определенные слова сделать ссылками на определенные страницы
Но задача усложнена тем, что если слово находится между определенными тегами, то линковать его не нужно.

долго мучался с регулярными выражениями, наконец понял, что под данную задачу только ими обойтись не получится.
в итоге написал небольшую функцию, которая работает от 5 до 10 секунд, если записей в БД около 200, а тексты по 9-15 тыс. знаков.

помогите оптимизировать , может быть я выбрал абсолютно не правильный алгоритм
вот код:
PHP:
function ck($a){
    
    $sql="SELECT a.name AS NAME,
      a.url AS URL, s.site_id AS SITE 
	      FROM autolink a 
		  INNER JOIN autolink2site s
		  ON s.link_id=a.id
WHERE length(a.url)>1 and s.site_id='".SITE_ID."'";
	$res=mysql_query($sql);	 // забираем слова , и урлы, на которые линковать
	$ar1=$ar=array();
	while($r=mysql_fetch_assoc($res)){
	$ar1[]='<a class="newstext" style="cursor: default; text-decoration: none;" href="'.$r['URL'].'" >$1</a>';
	$ar[]='#('.$r['NAME'].')(?!([^<]*?\>))#is';
	////  созданы два массива СЛОВО и УРЛ
	}
    $l=strlen($a);// длина строки 
$o=''; /// измененная-пролинкованная строка
$match=array();
for($i=0;$i<$l;$i++){/// внимание плохой стиль изменение счетчика внутри цикла
	$u='';//// временный буфер
	while($a[$i]!='<'){/// заливаем во временный , пока не встретим начало тега
		$u.=$a[$i];
		$i++;
		if($i>=$l) break;/// не допускаем выхода за пределы строки 

if($a[$i]==='<' && !preg_match('#^\<((a)|(b(!=r))|(h\d)|(strong))#is',substr($a,$i,8),$match)){
// если следующий символ <  и он начинает один из запрещенных для линковки тегов
//перебрасываем счетчик еще на один, чтобы не прервать выполнения этого цикла...
    $u.=$a[$i];
      $i++;
      if($i>=$l) break;
           }
	}
	$o.=preg_replace($ar,$ar1,$u);
	$u='';
	/// здесь символ открывает тег, тен не является запрещенным...
///выполняем замену и сбрасываем временный буфер в возвращаемую переменную
	if(($i+1)>=$l) break;
	$z=strtolower($match[1]);/// выясняем какой тег открылся, докручиваем счетчик до его закрытия
	while(strtolower(substr($a,$i,strlen($z)+2))!='</'.$z){
			$u.=$a[$i];
			$i++;
			if($i>=$l) break;
	}
	while ($a[$i-1]!='>'){
	    $u.=$a[$i];
	    $i++;
	    if($i>=$l) break;
	}
	$o.=$u.$a[$i];
	/// пошли на следующий виток
}
return $o;
}
 

zerkms

TDD infected
Команда форума
может если ты подробнее исходную задачу объяснишь будет проще тебе подсказать чем разбираться в 30 строках ужасного кода
 

idler

Новичок
Да, код действительно УЖАСЕН
:)
но другого алгоритма мне в голову не пришло....

Излагаю задачу:

Нужна функция, чтобы использовать ее таким образом:
PHP:
ob_start('func_name');
Хотя думаю это не важно.....

функция получает на вход строку (HTML-код)
возвращает строку, где определенные слова, посредством тегов <a> ссылаются на определенные страницы

Ссылки не должны вставать внутри тегов: a b h1-h6 strong так же не должны вставать естественно ВНУТРИ тега
например <img alt=" и тут встретилось ключевое слово" src="some.gif" />

список слов и урлов получается этим запросом

SELECT
a.name AS NAME,
a.url AS URL
FROM autolink a
INNER JOIN autolink2site s
ON s.link_id=a.id
WHERE length(a.url)>1 and s.site_id=4

т.е.
можно предположить , что есть два списка
$ar и $ar1
где $ar[$y] должно ссылаться на урл, хранящийся в $ar1[$y]
 

idler

Новичок
смотри условие задачи внимательнее.....
я не зря написал, что вопрос к экспертам

str_replace тут наделает ТАКИХ делов.....
 

idler

Новичок
ну тогда как?

я не нашел решения ТОЛЬКО в регулярных выражениях.....

предложи вариант?
 

zerkms

TDD infected
Команда форума
угу, несколько поспешил, всё не так просто. ;)

PHP:
$from = array('слово', 'asd');
$to = array('word', 'йцу');
$str = 'asd <dqwe> слово <b> слово asd</b> <img alt=" и тут встретилось ключевое слово" src="some.gif" /> 234';


function smart_parse($str, $from, $to) {
        $regexp = '#(<(\w+)>.*</\2>|<.*>)#U';
        $text = preg_split($regexp, $str);
        $size = preg_match_all($regexp, $str, $matches);

        $res = '';
        foreach($text as $key => $val) {
            $res .= str_replace($from, $to, $val);
            if ($key != $size) {
                $res .= $matches[0][$key];
            }
        }
        return $res;
}

echo smart_parse($str, $from, $to);
с форматированием и без форум добавляет непонятный пробел между > и ) в строке $regexp = ...
 

zerkms

TDD infected
Команда форума
idler
условие почитал, уточни что не так

-~{}~ 07.04.06 23:33:

если ты намекаешь на
Ссылки не должны вставать внутри тегов: a b h1-h6 strong так же не должны вставать естественно ВНУТРИ тега
например <img alt=" и тут встретилось ключевое слово" src="some.gif" />
то я понадеялся на то что ты сам в состоянии исправить регулярное выражение
для вышеуказанных условий это будет:

PHP:
$regexp = '#(<(b|h[1-6]|strong)>.*</\2>|<a[^>]+>.*</a>|<.*>)#U';
-~{}~ 07.04.06 23:34:

и снова в конце регулярного выражения между > и ) не должно быть пробела
 

idler

Новичок
Да, действительно, идея хорошая, но то ли ты не понял задачи, то ли не силен в регулярках.
Регулярку сам додумаю...

исправление первой ошибки:
(<(b|h[1-6]|strong)>.*</\3> или (\<(b|h[1-6]|strong)\>.*\</\2\>

второе правильнее!
и ошибок КУЧА.
Ну да ладно, не в этом суть, спасибо за помощь.
 

zerkms

TDD infected
Команда форума
idler
укажи мне тёмному и убогому на мои ошибки и расскажи чем же моя регулярка тебя не устроила (желательно с входной строкой + ожидаемыми и действительным результатами)
 

idler

Новичок
Извиняюсь , погорячился. Ошибка одна - логическая

например <h1 class="header" >ФЫВА</h1> оно не схватит

и еще скобки: {}<> тоже захватывают выражения внутри себя.
 
Сверху