黃聰:PHP轉換網址相對路徑到絕對路徑的一種方法

相信不少程序(尤爲是採集類的程序)都會有須要把網址的相對路徑轉換成絕對路徑的須要,例如採集到某頁面的HTML代碼中包含資源文件常常會看到這樣的文件名:php

  • <link rel="stylesheet" href="css/style.css" />
  • <img src="/logo.png" />
  • <img src="../banner.jpg" />

若是直接用獲取其href屬性或src屬性很明顯這個地址是打不開的,這就須要將其中的相對路徑轉爲絕對路徑。查一下網上相關的代碼也很多,但大多數代碼都太難被讀懂,並且執行效率不見得會高,基於此我開始着手寫一個本身的轉化方法。css

因爲採集來的網址格式大部分爲絕對地址或者位於根目錄的地址,因此代碼對這兩種格式的的地址優先處理並返回,以提升代碼的執行效率。html

源碼以下:函數接收兩個參數,第一個參數爲採集到的相對路徑,第二個參數爲被採集的原始的URL,例如從www.example.com/a/b/c.html中採集到某資源地址爲css/style.css,那麼函數調用方式爲filter_relative_url('css/style.css', 'http://www.example.com/a/b/c.html');(注:函數中省略了對URI參數的驗證和過濾操做,若是URI參數沒加http://前綴會報錯,相關驗證代碼可自行添加)。jquery

  • /**
  • * 把從HTML源碼中獲取的相對路徑轉換成絕對路徑
  • * @param string $url HTML中獲取的網址
  • * @param string $URI 用來參考判斷的原始地址
  • * @return 返回修改過的網址,若是網址有誤則返回FALSE
  • */
  • function filter_relative_url($url, $URI){
  • //STEP1: 先去判斷URL中是否包含協議,若是包含說明是絕對地址則能夠原樣返回
  • if(strpos($url, '://') !== FALSE){
  • return $url;
  • }
  • //STEP2: 解析傳入的URI
  • $URI_part = parse_url($URI);
  • if($URI_part == FALSE)
  • return FALSE;
  • $URI_root = $URI_part['scheme'] . '://' . $URI_part['host'] . (isset($URI_part['port']) ? ':' . $URI_part['port'] : '');
  • //STEP3: 若是URL以左斜線開頭,表示位於根目錄
  • if(strpos($url, '/') === 0){
  • return $URI_root . $url;
  • }
  • //STEP4: 不位於根目錄,也不是絕對路徑,考慮若是不包含'./'的話,須要把相對地址接在原URL的目錄名上
  • $URI_dir = (isset($URI_part['path']) &amp;&amp; $URI_part['path']) ? '/' . ltrim(dirname($URI_part['path']), '/') : '';
  • if(strpos($url, './') === FALSE){
  • if($URI_dir != ''){
  • return $URI_root . $URI_dir . '/' . $url;
  • } else {
  • return $URI_root . '/' . $url;
  • }
  • }
  • //STEP5: 若是相對路徑中包含'../'或'./'表示的目錄,須要對路徑進行解析並遞歸
  • //STEP5.1: 把路徑中全部的'./'改成'/','//'改成'/'
  • $url = preg_replace('/[^\.]\.\/|\/\//', '/', $url);
  • if(strpos($url, './') === 0)
  • $url = substr($url, 2);
  • //STEP5.2: 使用'/'分割URL字符串以獲取目錄的每一部分進行判斷
  • $URI_full_dir = ltrim($URI_dir . '/' . $url, '/');
  • $URL_arr = explode('/', $URI_full_dir);
  • if($URL_arr[0] == '..')
  • return FALSE;
  • //由於數組的第一個元素不可能爲'..',因此這裏從第二個元素開始循環
  • $dst_arr = $URL_arr; //拷貝一個副本,用於最後組合URL
  • for($i = 1; $i < count($URL_arr); $i ++){
  • if($URL_arr[$i] == '..'){
  • $j = 1;
  • while(TRUE){
  • if(isset($dst_arr[$i - $j]) &amp;&amp; $dst_arr[$i - $j] != FALSE){
  • $dst_arr[$i - $j] = FALSE;
  • $dst_arr[$i] = FALSE;
  • break;
  • } else {
  • $j ++;
  • }
  • }
  • }
  • }
  • // 組合最後的URL並返回
  • $dst_str = $URI_root;
  • foreach($dst_arr as $val){
  • if($val != FALSE)
  • $dst_str .= '/' . $val;
  • }
  • return $dst_str;
  • }
php

代碼如上分爲幾步針對不一樣格式的相對路徑進行處理並返回,如下是測試代碼:數組

  • $URI_1 = 'http://www.abc.com/a/b/c/d.html';
  • $URI_2 = 'http://www.abc.com';
  • $test [] = 'http://www.abc.com/css/style.css';
  • $test [] = '/img/banner.jpg';
  • $test [] = 'images/res_03.png';
  • $test [] = '../../js/jquery.min.js';
  • $test [] = './../res/js/../jquery/./1.8.3/jquery.js';
  • foreach($test as $val){
  • echo filter_relative_url($val, $URI_1);
  • }
  • foreach($test as $val){
  • echo filter_relative_url($val, $URI_2);
  • }
php

程序返回:函數

  • http://www.abc.com/css/style.css
  • http://www.abc.com/img/banner.jpg
  • http://www.abc.com/a/b/c/images/res_03.png
  • http://www.abc.com/a/js/jquery.min.js
  • http://www.abc.com/a/b/res/jquery/1.8.3/jquery.js
  • http://www.abc.com/css/style.css
  • http://www.abc.com/img/banner.jpg
  • http://www.abc.com/images/res_03.png

*注:測試代碼的第二個循環只返回了3條結果,是由於後兩條結果返回了FALSE,由於原始URI是位於網站根目錄,再在前面加「../「請求上級目錄是無效的。post

以上代碼的主要優勢是解決了網上流傳的那些代碼對於複雜格式的相對路徑不能完美兼容的問題,同時照顧了執行效率,減小判斷和分解的次數。此代碼目前我本身正在使用,若是有任何問題還請留言或郵件聯繫我,謝謝!測試


2016年5月17日更新

看到PHP官網WIKI上面有一個關於相對地址轉換的寫法,寫在下面以供參考:網站

  • function normalizePath($path)
  • {
  • $parts = array();// Array to build a new path from the good parts
  • $path = str_replace('\\', '/', $path);// Replace backslashes with forwardslashes
  • $path = preg_replace('/\/+/', '/', $path);// Combine multiple slashes into a single slash
  • $segments = explode('/', $path);// Collect path segments
  • $test = '';// Initialize testing variable
  • foreach($segments as $segment)
  • {
  • if($segment != '.')
  • {
  • $test = array_pop($parts);
  • if(is_null($test))
  • $parts[] = $segment;
  • else if($segment == '..')
  • {
  • if($test == '..')
  • $parts[] = $test;
  • if($test == '..' || $test == '')
  • $parts[] = $segment;
  • }
  • else
  • {
  • $parts[] = $test;
  • $parts[] = $segment;
  • }
  • }
  • }
  • return implode('/', $parts);
  • }
php

參考資料:ui

本文連接:https://icewing.cc/post/php-conv-addr-re-ab-2.html

相關文章
相關標籤/搜索