學習PHP中的URL相關操作函式

硬核專案經理發表於2021-11-16

在日常的業務開發過程中,我們經常會有處理 URL 連結的需求,所以今天學習的函式其實都是大家經常會使用的一些函式。在之前的工作過程中,其實我對這些函式都只是有一個模糊的概念,知道,但是真要用得時候還是要看下文件才能確定真正要使用的是哪一個函式。因此,今天我們就當做是一次複習練習,主要是區分和搞清楚每個函式真正的用處。

編碼操作函式

首先來看就是 URL 編碼相關的函式。有些瀏覽器在我們複製貼上一個網址後,就會自動地對網址進行 URL 編碼,也就是有很多百分號那種形式的。在 PHP 中,自然也有對應的編解碼函式。

$url = "https://www.zyblog.net?opt=dev&mail=zyblog@net.net&comments=aaa bbb ccc %dfg &==*() cdg&value=“中文也有呀,還有中文符號!!”";

echo $url, PHP_EOL;
// https://www.zyblog.net?opt=dev&mail=zyblog@net.net&comments=aaa bbb ccc %dfg &==*() cdg&value=“中文也有呀,還有中文符號!!”

$enurl = urlencode($url);
echo $enurl, PHP_EOL;
// https%3A%2F%2Fwww.zyblog.net%3Fopt%3Ddev%26mail%3Dzyblog%40net.net%26comments%3Daaa+bbb+ccc+%25dfg+%26%3D%3D%2A%28%29+cdg%26value%3D%E2%80%9C%E4%B8%AD%E6%96%87%E4%B9%9F%E6%9C%89%E5%91%80%EF%BC%8C%E8%BF%98%E6%9C%89%E4%B8%AD%E6%96%87%E7%AC%A6%E5%8F%B7%EF%BC%81%EF%BC%81%E2%80%9D

echo urldecode($enurl), PHP_EOL;
// https://www.zyblog.net?opt=dev&mail=zyblog@net.net&comments=aaa bbb ccc %dfg &==*() cdg&value=“中文也有呀,還有中文符號!!”

這兩個函式估計是大家使用最多的函式了。urlencode() 就是用於 URL 的編碼操作,可以看到,我們準備好的連結已經被編碼成了包含各種百分號的內容。特別是對於中文字元來說,如果是 GET 方式這種在連結中的中文引數,編碼之後的內容就會讓連結變得非常長。urldecode() 則是相對應的解碼功能的函式,可以把編碼過的連結解碼回原始的狀態。

$rawenurl = rawurlencode($enurl);
echo $rawenurl, PHP_EOL;
// https%253A%252F%252Fwww.zyblog.net%253Fopt%253Ddev%2526mail%253Dzyblog%2540net.net%2526comments%253Daaa%2Bbbb%2Bccc%2B%2525dfg%2B%2526%253D%253D%252A%2528%2529%2Bcdg%2526value%253D%25E2%2580%259C%25E4%25B8%25AD%25E6%2596%2587%25E4%25B9%259F%25E6%259C%2589%25E5%2591%2580%25EF%25BC%258C%25E8%25BF%2598%25E6%259C%2589%25E4%25B8%25AD%25E6%2596%2587%25E7%25AC%25A6%25E5%258F%25B7%25EF%25BC%2581%25EF%25BC%2581%25E2%2580%259D

echo rawurldecode($rawenurl), PHP_EOL;
// https%3A%2F%2Fwww.zyblog.net%3Fopt%3Ddev%26mail%3Dzyblog%40net.net%26comments%3Daaa+bbb+ccc+%25dfg+%26%3D%3D%2A%28%29+cdg%26value%3D%E2%80%9C%E4%B8%AD%E6%96%87%E4%B9%9F%E6%9C%89%E5%91%80%EF%BC%8C%E8%BF%98%E6%9C%89%E4%B8%AD%E6%96%87%E7%AC%A6%E5%8F%B7%EF%BC%81%EF%BC%81%E2%80%9D

echo rawurlencode($url), PHP_EOL;
// https%3A%2F%2Fwww.zyblog.net%3Fopt%3Ddev%26mail%3Dzyblog%40net.net%26comments%3Daaa%20bbb%20ccc%20%25dfg%20%26%3D%3D%2A%28%29%20cdg%26value%3D%E2%80%9C%E4%B8%AD%E6%96%87%E4%B9%9F%E6%9C%89%E5%91%80%EF%BC%8C%E8%BF%98%E6%9C%89%E4%B8%AD%E6%96%87%E7%AC%A6%E5%8F%B7%EF%BC%81%EF%BC%81%E2%80%9D

echo rawurldecode($enurl), PHP_EOL;
// https://www.zyblog.net?opt=dev&mail=zyblog@net.net&comments=aaa+bbb+ccc+%dfg+&==*()+cdg&value=“中文也有呀,還有中文符號!!”

緊接著我們看到的是 rawurlencode() 和 rawurldecode() 。很多小夥伴會搞不清楚它們和普通的 urlencode() 、 urldecode() 有什麼區別。其實,它們的區別主要體現在一些特殊的字元上,比如說空格。在 urlencode() 中,空格被編碼為 + 號,而在 urlrawencode() 中,空格是 %20 。在我們的第三段測試程式碼中就可以看出來。

前兩段測試程式碼是針對前面已經編碼過的 \$enurl 進行的操作。第三段測試程式碼是對原始的 $url 進行的編碼。這兩個函式是實現了 RFC3986 規範的函式。而 urlencode() 則是由於歷史原因而保留了一些類似於空格轉換成 + 號這樣的特殊情況。

最後我們再看兩個非常簡單的 Base64 相關的編解碼函式。

$base64url = base64_encode($enurl);
echo $base64url, PHP_EOL;
// aHR0cHMlM0ElMkYlMkZ3d3cuenlibG9nLm5ldCUzRm9wdCUzRGRldiUyNm1haWwlM0R6eWJsb2clNDBuZXQubmV0JTI2Y29tbWVudHMlM0RhYWErYmJiK2NjYyslMjVkZmcrJTI2JTNEJTNEJTJBJTI4JTI5K2NkZyUyNnZhbHVlJTNEJUUyJTgwJTlDJUU0JUI4JUFEJUU2JTk2JTg3JUU0JUI5JTlGJUU2JTlDJTg5JUU1JTkxJTgwJUVGJUJDJThDJUU4JUJGJTk4JUU2JTlDJTg5JUU0JUI4JUFEJUU2JTk2JTg3JUU3JUFDJUE2JUU1JThGJUI3JUVGJUJDJTgxJUVGJUJDJTgxJUUyJTgwJTlE

echo base64_decode($base64url), PHP_EOL;
// https%3A%2F%2Fwww.zyblog.net%3Fopt%3Ddev%26mail%3Dzyblog%40net.net%26comments%3Daaa+bbb+ccc+%25dfg+%26%3D%3D%2A%28%29+cdg%26value%3D%E2%80%9C%E4%B8%AD%E6%96%87%E4%B9%9F%E6%9C%89%E5%91%80%EF%BC%8C%E8%BF%98%E6%9C%89%E4%B8%AD%E6%96%87%E7%AC%A6%E5%8F%B7%EF%BC%81%EF%BC%81%E2%80%9D

其實 Base64 最大的用處不是體現在對這種普通的字串的編碼上,而是體現於 二進位制 字串在編碼後傳輸的作用上。這個想必用過的同學自然心裡有數。主要是對於介面開發來說,如果我們使用 Base64 對資料進行編碼,一是沒有什麼加密的效果,二是還有可能增大資料的長度,所以除非有特殊需求,否則普通傳輸中真的沒有太大的必要來對資料進行 Base64 的編碼。

URL 解析操作

除了對於 URL 連結中的字元進行編解碼之外,解析連結引數也是我們經常會使用的功能。比如:

$urls = parse_url($url);
var_dump($urls);
// array(3) {
//     ["scheme"]=>
//     string(5) "https"
//     ["host"]=>
//     string(14) "www.zyblog.net"
//     ["query"]=>
//     string(119) "opt=dev&mail=zyblog@net.net&comments=aaa bbb ccc %dfg &==*() cdg&value=“中文也有呀,還有中文符號!!”"
//   }

通過 parse_url() 這個函式,我們就可以將連結的各個部分拆解開來。

$parseTestUrl = 'http://username:password@hostname/path?arg=value#anchor';

print_r(parse_url($parseTestUrl));
// Array
// (
//     [scheme] => http
//     [host] => hostname
//     [user] => username
//     [pass] => password
//     [path] => /path
//     [query] => arg=value
//     [fragment] => anchor
// )

上面這個測試連結更加地標準規範,我們也可以看到 parse_url() 可以拆解出 協議、地址、使用者名稱、密碼、路徑、查詢語句、片斷 這些內容,這些也是構成一個 URL 連結的規範標準。我們也可以指定我們需要的內容。

echo parse_url($parseTestUrl, PHP_URL_PATH); // /path

像這樣新增第二個引數,就可以只獲取我們需要的部分內容。當然,對於整個 URL 連結來說,我們最關心的其實是 query 這部分的內容,能不能將它們再拆解出來呢?就像 $_GET 一樣獲得全部的查詢資料結果。

$querys = [];
parse_str($urls['query'], $querys);
var_dump($querys);
// array(4) {
//     ["opt"]=>
//     string(3) "dev"
//     ["mail"]=>
//     string(14) "zyblog@net.net"
//     ["comments"]=>
//     string(15) "aaa bbb ccc �g "
//     ["value"]=>
//     string(48) "“中文也有呀,還有中文符號!!”"
//   }

parse_str() 這個函式就是解析這種 URL 連結查詢語句的函式。需要注意的是,這個函式的第二個引數是可選的,如果不使用一個變數來接收這個函式所解析出來的結果的話,那麼所有解析的結果將直接轉換成變數形式。說得可能有點暈,直接看看程式碼。

parse_str($urls['query']);
echo $value, PHP_EOL; // “中文也有呀,還有中文符號!!”

這下就看明白了吧。為了防止變數汙染問題的出現,最好還是有第二個引數來讓解析的結果儲存到我們指定的地方。最後,我們再看看如何將陣列組合成一段 URL 查詢語句。

echo http_build_query($querys), PHP_EOL;
// opt=dev&mail=zyblog%40net.net&comments=aaa+bbb+ccc+%DFg+&value=%E2%80%9C%E4%B8%AD%E6%96%87%E4%B9%9F%E6%9C%89%E5%91%80%EF%BC%8C%E8%BF%98%E6%9C%89%E4%B8%AD%E6%96%87%E7%AC%A6%E5%8F%B7%EF%BC%81%EF%BC%81%E2%80%9D

echo http_build_query($querys, null, '$||$', PHP_QUERY_RFC3986), PHP_EOL;
// opt=dev$||$mail=zyblog%40net.net$||$comments=aaa%20bbb%20ccc%20%DFg%20$||$value=%E2%80%9C%E4%B8%AD%E6%96%87%E4%B9%9F%E6%9C%89%E5%91%80%EF%BC%8C%E8%BF%98%E6%9C%89%E4%B8%AD%E6%96%87%E7%AC%A6%E5%8F%B7%EF%BC%81%EF%BC%81%E2%80%9D

http_build_query() 其實只要做過對接外部介面開發的同學都不會陌生。因為它太方便了。不過需要注意的是,這個函式自帶地就會將資料進行 rawurlencode() 編碼。另外,它還有幾個可選引數的,比如說我們第二段測試程式碼中修改了連線符號,將原本的 & 符號替換成我們自定義的符號來進行 URL 查詢語句的拼接。

解析檔案或遠端地址的響應頭及 meta 資訊

對於遠端檔案的請求來說,響應頭資訊也是非常重要的內容。其實在 URL 相關的元件中也有直接獲取響應頭的函式。

$url = 'https://www.sina.com.cn';

print_r(get_headers($url));
// Array
// (
//     [0] => HTTP/1.1 200 OK
//     [1] => Server: nginx
//     [2] => Date: Mon, 25 Jan 2021 02:08:35 GMT
//     [3] => Content-Type: text/html
//     [4] => Content-Length: 530418
//     [5] => Connection: close
//     [6] => Vary: Accept-Encoding
//     [7] => ETag: "600e278a-7c65e"V=5965C31
//     [8] => X-Powered-By: shci_v1.13
//     [9] => Expires: Mon, 25 Jan 2021 02:09:12 GMT
//     [10] => Cache-Control: max-age=60
//     [11] => X-Via-SSL: ssl.22.sinag1.qxg.lb.sinanode.com
//     [12] => Edge-Copy-Time: 1611540513080
//     [13] => Age: 24
//     [14] => Via: https/1.1 cmcc.guangzhou.union.82 (ApacheTrafficServer/6.2.1 [cRs f ]), https/1.1 cmcc.jiangxi.union.175 (ApacheTrafficServer/6.2.1 [cRs f ])
//     [15] => X-Via-Edge: 1611540515462770a166fee55a97524d289c7
//     [16] => X-Cache: HIT.175
//     [17] => X-Via-CDN: f=edge,s=cmcc.jiangxi.union.166.nb.sinaedge.com,c=111.22.10.119;f=edge,s=cmcc.jiangxi.union.168.nb.sinaedge.com,c=117.169.85.166;f=Edge,s=cmcc.jiangxi.union.175,c=117.169.85.168
// )

print_r(get_headers($url, 1));
// Array
// (
//     [0] => HTTP/1.1 200 OK
//     [Server] => nginx
//     [Date] => Mon, 25 Jan 2021 02:08:35 GMT
//     [Content-Type] => text/html
//     [Content-Length] => 530418
//     [Connection] => close
//     [Vary] => Accept-Encoding
//     [ETag] => "600e278a-7c65e"V=5965C31
//     [X-Powered-By] => shci_v1.13
//     [Expires] => Mon, 25 Jan 2021 02:09:12 GMT
//     [Cache-Control] => max-age=60
//     [X-Via-SSL] => ssl.22.sinag1.qxg.lb.sinanode.com
//     [Edge-Copy-Time] => 1611540513080
//     [Age] => 24
//     [Via] => https/1.1 cmcc.guangzhou.union.82 (ApacheTrafficServer/6.2.1 [cRs f ]), https/1.1 cmcc.jiangxi.union.175 (ApacheTrafficServer/6.2.1 [cRs f ])
//     [X-Via-Edge] => 1611540515593770a166fee55a97568f1a9d6
//     [X-Cache] => HIT.175
//     [X-Via-CDN] => f=edge,s=cmcc.jiangxi.union.165.nb.sinaedge.com,c=111.22.10.119;f=edge,s=cmcc.jiangxi.union.175.nb.sinaedge.com,c=117.169.85.165;f=Edge,s=cmcc.jiangxi.union.175,c=117.169.85.175
// )

通過 get_headers() 函式就可以直接拿到目標地址伺服器返回的響應頭資訊。它的第二個引數可以以鍵值下標的方式返回資料。除了響應頭之外,我們還可以拿到網站的所有 meta 標籤裡的內容。

var_dump(get_meta_tags($url));
// array(11) {
//     ["keywords"]=>
//     string(65) "新浪,新浪網,SINA,sina,sina.com.cn,新浪首頁,門戶,資訊"
//     ["description"]=>
//     string(331) "新浪網為全球使用者24小時提供全面及時的中文資訊,內容覆蓋國內外突發新聞事件、體壇賽事、娛樂時尚、產業資訊、實用資訊等,設有新聞、體育、娛樂、財經、科技、房產、汽車等30多個內容頻道,同時開設部落格、視訊、論壇等自由互動交流空間。"
//     ["referrer"]=>
//     string(6) "always"
//     ["stencil"]=>
//     string(10) "PGLS000022"
//     ["publishid"]=>
//     string(8) "30,131,1"
//     ["verify-v1"]=>
//     string(44) "6HtwmypggdgP1NLw7NOuQBI2TW8+CfkYCoyeB8IDbn8="
//     ["application-name"]=>
//     string(12) "新浪首頁"
//     ["msapplication-tileimage"]=>
//     string(42) "//i1.sinaimg.cn/dy/deco/2013/0312/logo.png"
//     ["msapplication-tilecolor"]=>
//     string(7) "#ffbf27"
//     ["baidu_ssp_verify"]=>
//     string(32) "c0e9f36397049594fb9ac93a6316c65b"
//     ["sudameta"]=>
//     string(20) "dataid:wpcomos:96318"
//   }

這個函式不僅是對遠端的連結網站有用,而且還可以直接檢視一個本地的靜態檔案中的所有 meta 標籤的內容,我們只需要將引數的遠端連結換成本地檔案的路徑就可以了,大家可以自己嘗試一下。

總結

今天的內容還是比較簡單的,主要這些函式大家在日常的工作中會經常用到。不過有些引數的使用情況可能許多朋友並不清楚,比如 parse_str() 函式的第二個引數的問題。所以就像開頭說的,這篇文章就是個複習鞏固,另外也起到加深理解的作用,深入學習之後融匯貫通實際運用就能掌握得更加手到擒來。

測試程式碼:

https://github.com/zhangyue0503/dev-blog/blob/master/php/2021/01/source/9.學習PHP中的URL相關操作函式.php

參考文件:

https://www.php.net/manual/zh/book.url.php

相關文章