Доброго времени суток. Я очень люблю регулярные выражения, но довольно часто в них плаваю, когда решаю не самую тривиальную задачу. Вот и сейчас… У меня есть rss, и моя задача заменить в нём все относительные пути на абсолютные. Я пришёл к следующему решению:
static public function relative_to_absolute( $text )
{
$rg =
'#'.
'(<\w+\s.*)'. // "<img "
'(href|src)\s*=\s*'. // "src = "
'(?:'.
'\'([^\']+)\''.'|'. // 'relative'
'\"([^\"]+)\"'. // "relative"
')'.
'#';
$replace = $text;
$index = Kohana::$base_url !== '/';
do
{
$text = $replace;
$replace = preg_replace_callback( $rg, function( $m ) use ( $index )
{
$url = empty( $m[ 3 ] ) ? $m[ 4 ] : $m[ 3 ];
if( strpos( $url, '//' ) === false )
{
return $m[ 1 ].$m[ 2 ].'="'.URL::site( $url, 'http', $index ).'"';
}
else
{
return $m[ 0 ];
}
}, $replace );
}
while( $text !== $replace );
return $replace;
}
Оно работает, но мне не нравится по следующим причинам:
1. Железно вбитые аттрибуты. Оно как бы и не критично, но хочется более универсальное решение. Проблема в том — как отличить относительный url от просто значения аттрибута.
2. Дубляж того участка кода, который отвечает за саму ссылку в кавычках, в виду того, что они могут быть как ' так и ". Если написать что-то вроде '|" то как быть со ссылками содержащими кавычку (например транслит от «ь»).
3. Регулярка пропускает все url, и уже внутри определяет какие относительные, а какие нет. Как можно отфильтровать абсолютные ещё на стадии preg_replace-а? Я догадываюсь что там нужна ретроспективная отрицательная проверка, но вот как её туда воткнуть не понимаю.
В общем, мне кажется, что сию задачу можно решить без _callback-а. Интересует не готовое решение, а понимание его принципа работы :)