/**
Вырезает все url-ы вида href=[url] из текста $str.
Преобразует url-ы к абсолютным, как это делают браузеры,
если считать, что они находятся на странице по адресу $base_url.
*/
function fetch_all_urls($base_url, $str)
{
$tmp = parse_url($base_url);
// формируем имя сервера
if (!isset($tmp['scheme'])) $tmp['scheme'] = 'http';
if (!isset($tmp['host'])) $tmp['host'] = 'microsoft.com';
$server = $tmp['scheme'] . '://' . $tmp['host'];
// формируем путь к документу на сервере
if (!isset($tmp['path'])) $tmp['path'] = '/';
$doc_path = $tmp['path'];
// удаляем из пути вложенные каталоги типа . и ..
$tmp1 = parse_doc_path($doc_path);
$doc_path = $tmp1['path'];
$dir_name = $tmp1['dir'];
// добавляем к пути строку query_string, если она есть
if (isset($tmp['query'])) $doc_path .= '?' . $tmp['query'];
// достаем url-ы, взятые в кавычки (одинарные и двойные)
$a = array();
preg_match_all('/href\s*=\s*(?:' . '([\'"])(.*?)\\1)/s', $str, $a);
$urls = $a[2];
// достаем url-ы без кавычек
$a = array();
preg_match_all('/href\s*=\s*([^\\s>"\'][^\\s>]*)/', $str, $a);
$urls = array_merge($urls, $a[1]);
// обрабатываем url-ы
$n = sizeof($urls);
for ($i = 0; $i < $n; $i++) {
// декодируем htmlentities во всех url-ах
$url = html_entity_decode($urls[$i]);
$url = preg_replace('/&#(?:0*)(\d+);/e', 'chr(\\1)', $url);
// преобразуем все url-ы в асболютные ссылки (начинающиеся с http://)
if (strlen($url) == 0) {
// пустая ссылка
$url = $server . $doc_path . (isset($tmp['fragment']) ? ('#' . $tmp['fragment']) : '');
} else if ($url{0} == '#') {
// якорь
$url = $server . $doc_path . $url;
} else {
if (!preg_match('/^(http|ftp|mailto):/', $url)) {
// относительные ссылки
if ($url{0} != '/') {
// ссылка, относительно текущего каталога
$url = $dir_name . $url;
}
$tmp1 = parse_doc_path($url);
$url = $server . $tmp1['path'];
}
}
$urls[$i] = $url;
}
// удаляем повторяющиеся url-ы
$urls = array_unique($urls);
return $urls;
}
/***********************************************************************/
/**
Фильтрует абсолютный путь к каталогу (т.е. начинающийся со слэша /)
Удаляет из него каталоги вида . и ..
Добавляет в начало пути слэш, если его там не было
Возвращает ассоциативный массив со следующими элементами:
[path] - отфильтрованный путь к файлу (фактически [dir] + [file])
[file] - имя файла
[dir] - путь к каталогу, в котором лежит файл
*/
function parse_doc_path($path)
{
$path = strtr($path, '\\', '/'); // заменяем бэкслэши на обычные слэши
$a = explode('/', $path);
if (!sizeof($a)) {
return array('path' => '/', 'file' => '', 'dir' => '/');
}
$b = array();
foreach ($a as $part) {
if (!strlen($part)) continue; // пропускаем строку нулевой длины
if ($part == '.') continue; // пропускаем одинарную точку (указывает сама на себя)
if (preg_match('/\\.{2,}/', $part)) {
if (sizeof($b)) array_pop($b);
continue;
}
array_push($b, $part);
}
$file = array_pop($a);
if (strlen($file)) array_pop($b);
$dir = '/' . implode('/', $b);
if (strlen($dir) > 1) $dir .= '/';
return array('path' => $dir . $file, 'file' => $file, 'dir' => $dir);
}