[함수] 상대경로를 특정URI 기준의 절대경로로 변환하기

글쓴이 Fencer 날 짜 07-05-28 06:29 조 회 97

■ 이 함수가 필요한 경우

브라우저가 사용자에게 링크주소를 보여주거나, 자원을 다운 받아와서 보여주려면
먼저 현재문서에 포함되어 있는 상대경로를 현재문서의 URI에 맞추어 절대경로로 변환해야 하죠.
이를테면 에서 ../../abc.jpg 의 실제위치는
기준이 되는 문서의 URI가 뭐냐에 따라 바뀝니다.
현재문서가 http://test.com/a/b/c/d.php 라면 http://test.com/a/b/abc.jpg 가 되고,
현재문서가 http://test.com/a/b/d.php 라면 http://test.com/a/abc.jpg 가 되겠죠.
(이거 다들 아는 얘기죠. ^^ )

우리들도 굳이(!) 상대경로를 절대경로로 번역한 후 써야 하는 때가 종종 있습니다.
특정사이트의 문서를 긁어서 RSS나 E-mail로 제공한다거나;;;
문서를 파싱하는 봇을 만든다거나;;;
웹상에 존재하는 문서를 로컬에 저장할 수 있게 해준다거나…
이럴 경우 링크나 파일의 위치 지정은 상대경로 말고 절대경로로 지정해 주어야겠죠.
그러지 않으면 기준URI가 바뀌면서 문서내의 주소가 다 깨져버리니까요. ㅡ.ㅡ;;;
만약 를 그대로 저장하면 브라우저는 제대로 해석해내지 못 합니다.
물론 과 같이 써서 기준URI를 지정해줄 수도 있지만,
웹에서 메일을 받아보는 경우나 RSS Reader 등에서는 무용지물인 경우가 생깁니다.
한 페이지 내에 여러 문서의 내용이 한꺼번에 제시되기도 하고,
base href 자체를 허용하지 않는 경우도 있으니까요.

바로 그런 역할을 하는 번역(RFC에는 resolve라고 표현되어있는) 함수입니다.
서버에서 realpath() 함수를 통해 로컬의 상대경로를 절대경로로 바꾸는 거랑 비슷하죠.

1. 기존에 어딘가에 완성된 것이 꽤 존재할 법 한데 찾기 어렵더군요. 벌레 투성이거나.
2. RFC-2396을 최대한 지키고, IE의 번역결과와 똑같은 결과를 제공하고 싶었습니다. ;;;
만들면서 알게 됐는데, FireFox보다 IE가 RFC-2396을 사소한 부분까지 충실히 지키는 듯 싶더군요.

아래 함수는 RFC-2396의 normal, abnormal 예문을 비롯해서 다양한 테스트를 해보았는데요,
제가 테스트하기로는 항상 IE와 똑같았습니다. 애초에 그게 목표였거든요. ㅎㅎ
혹시 있을지도 모르는 벌레 발견하시면 신고 환영합니다. ^^

// 상대경로를 절대경로로 바꾸는 함수
// 작성자 : Fencer
// $path = 찾아낸 상대경로
// $base_uri = 기준이 되는 URI, 즉 현재문서나 또다른 어떤 URI

function http_path_to_url($path, $base_uri)
{
if (preg_match(“@^[a-z]{1}[a-z0-9+-.]+:[/]{2,}@i”, $path)) return $path;

$base_a = parse_url($base_uri);
$base_a[‘shp’] = substr($base_uri, 0, strlen($base_uri) – strlen($base_a[‘path’].(isset($base_a[‘query’]) ? ‘?’.$base_a[‘query’] : ”).(isset($base_a[‘fragment’]) ? ‘#’.$base_a[‘fragment’] : ”)));

if (preg_match(“@^//@i”, $path)) {
return $base_a[‘scheme’].”:”.$path;
} else if (preg_match(“@^?@”, $path)) {
return $base_a[‘shp’].$base_a[‘path’].$path;
} else if (preg_match(“@^#@”, $path)) {
return preg_replace(“@#$@”, “”, substr($base_uri, 0, strlen($base_uri)-strlen($base_a[‘fragment’]))).$path;
} else {
if (preg_match(“@^/@”, $path)) {
return $base_a[‘shp’].$path;
} else {
if (isset($base_a[‘path’]) && $base_a[‘path’]!=”) {
$base_a[‘file’] = str_replace(‘/’, ”, strrchr($base_a[‘path’], ‘/’)); // 파일명
if (!preg_match(“@/@”, $base_a[‘path’])) $base_a[‘file’] = $base_a[‘path’]; // 파일 만으로 되어 있을 경우 위에서 “/” 검색이 안 되므로
$base_a[‘dir’] = substr($base_a[‘path’], 0, strlen($base_a[‘path’]) – strlen($base_a[‘file’])); // 디렉토리, “/” 포함
}

$op_a = parse_url($path);
$tp_a = explode(“/”, $base_a[‘dir’].$op_a[‘path’]);
$tp_c = count($tp_a);
$ap_a = array();
for ($i=0; $i < $tp_c; $i++) {
if ($tp_a[$i]==”..”) {
if (count($ap_a) >= 1) $ap_a = array_slice($ap_a, 0, count($ap_a)-1);
if ($i==$tp_c-1) $ap_a[] = “”; // ..이 마지막일 경우
} else if ($tp_a[$i]==”.”) {
if ($i==$tp_c-1) $ap_a[] = “”; // .이 마지막일 경우
} else {
$ap_a[] = $tp_a[$i];
}
}

$ap = implode(“/”, $ap_a);
if (!preg_match(“@^/@”, $ap)) $ap = “/”.$ap;

return $base_a[‘shp’] .$ap .(isset($op_a[‘query’]) ? ‘?’.$op_a[‘query’] : ”) .(isset($op_a[‘fragment’]) ? ‘#’.$op_a[‘fragment’] : ”);
}
}
}

?>

////////////////////////////////////////////////////////////////////////
// 함수 사용 예

$rel = “허허ss;df/.././../../싱하ㅤㅎㅛㅎ.htm?dsfs=../sdfs.aaa#5”;

echo ““;
echo “상대경로 : “.$rel.”


“;
echo “기본URI : “.$base_uri.”


“;
echo “브라우저의 변환결과 : 결과


“;
echo “함수의 변환결과 : “.http_path_to_url($rel, $base_uri);

?>

하시면…

상대경로 : 허허ss;df/.././../../싱하ㅤㅎㅛㅎ.htm?dsfs=../sdfs.aaa#5
기본URI : http://id:pw@ddd.com:443/a/b/c/d/하;하/f/아ㅤㅎㅐㅎㅤㅎㅐㅎ.php?x=1#2
IE의 변환결과 : http://id:pw@ddd.com:443/a/b/c/d/싱하ㅤㅎㅛㅎ.htm?dsfs=../sdfs.aaa#5
함수의 변환결과 : http://id:pw@ddd.com:443/a/b/c/d/싱하ㅤㅎㅛㅎ.htm?dsfs=../sdfs.aaa#5

…로 나옵니다. IE와 함수의 변환결과가 똑같죠.

////////////////////////////////////////////////////////////////////////
// 활용 예 : 문서내의 여러 상대경로를 모두 절대경로로 바꿔놓기

이제 HTML 속에 있는 src, href의 상대경로를
base_uri 기준의 절대경로로 재구성하려면 다음과 같이 하면 되겠죠.
href, src 정도만 검색해서 절대경로로 변환해줍니다.

아, 아래 내용은 그냥 예시입니다. ^^;
문서내의 모든 주소를 찾아내려면 좀 더(!) 세밀한 정규표현이 필요합니다.

{
$pattern_a = array(“@(s*href|s*src)(s*=s*'{1})([^’]+)(‘{1})@ie”
, “@(s*href|s*src)(s*=s*”{1})([^”]+)(“{1})@ie”
, “@(s*href|s*src)(s*=s*)([^s>”‘]+)(s|>)@ie”);
$replace_a = array(“‘\1\2’.http_path_to_url(‘\3’, ‘$base_uri’).’\4′”
, “‘\1’.stripslashes(‘\2’).http_path_to_url(‘\3’, ‘$base_uri’).stripslashes(‘\4’)”
, “‘\1\2’.http_path_to_url(‘\3’, ‘$base_uri’).’\4′”);
return preg_replace($pattern_a, $replace_a, $content);
}

$base_uri = 문서 주소;
$content = 문서 내용;

$content = http_src_to_abs($content, $base_uri);

?>