以php檢查ip網段
隨者機器越來越多,將單一IP以字串方式檢測的方式,當網路臨時調整時、或者要檢測的IP數很多時,就有點麻煩了。使用判斷網段的方式,會比較有彈性。
依照tcp/ip的邏輯,利用位元運算先做出了第一版
想說,是否有更快、更好的的作法?在PHP: ip2long - Manual看到了兩個作法。一個作法,很有效率。另一個卻比較差。
下面這個PHP程式碼,根據實際測試的結果,速度是最快的。不過…程式碼的可讀性比較差,需要想一下才能瞭解其邏輯。
這個寫法,可讀性最好(其實,這樣講也不太對)。但是有個極大的缺點,就是他是以字串的方式做比對,需要額外的使用sprintf()、substr_compare()。導致這樣的程式碼執行速度是三個中最慢的一個。且,在某些情況下,判斷結果有問題。
為何會舉上面兩個例子?其實,在流量很大的系統中,任何的資源都是珍貴的,須錙銖必較。刻意比較這三個判斷方式,就是要表明這一點。
下面,則是利用vld所呈現的php opcode。藉由opcode來瞭解,為何這兩個作法效能上會有差異?有興趣瞭解者,不妨參考一下opcode、以及PHP: Opcode list - Manual(如何安裝、使用vld?可以參考我之前的文章 - 如何使用vld看php的opcode)
附註:以下資料,非vld呈現的完整資訊。我僅列出重點。
依照tcp/ip的邏輯,利用位元運算先做出了第一版
- function matchCIDR($ip, $cidr) {
- list($ip, $mask) = explode('/', $cidr);
- $mask =(32 - $mask);
- return ((ip2long($ip) >> $mask) == (ip2long($ip) >> $mask));
- }
下面這個PHP程式碼,根據實際測試的結果,速度是最快的。不過…程式碼的可讀性比較差,需要想一下才能瞭解其邏輯。
- function netMatch($IP, $CIDR) {
- list ($net, $mask) = explode('/', $CIDR);
- return ( ip2long($IP) & ~((1 << (32 - $mask)) - 1) ) == ip2long($net);
- }
這個寫法,可讀性最好(其實,這樣講也不太對)。但是有個極大的缺點,就是他是以字串的方式做比對,需要額外的使用sprintf()、substr_compare()。導致這樣的程式碼執行速度是三個中最慢的一個。且,在某些情況下,判斷結果有問題。
- function ip_in_network($ip, $net_addr, $net_mask) {
- if ($net_mask <= 0) {
- return false;
- }
- $ip_binary_string = sprintf("%032b", ip2long($ip));
- $net_binary_string = sprintf("%032b", ip2long($net_addr));
- return (substr_compare($ip_binary_string, $net_binary_string, 0, $net_mask) === 0);
- }
下面,則是利用vld所呈現的php opcode。藉由opcode來瞭解,為何這兩個作法效能上會有差異?有興趣瞭解者,不妨參考一下opcode、以及PHP: Opcode list - Manual(如何安裝、使用vld?可以參考我之前的文章 - 如何使用vld看php的opcode)
- <?php
- //方法一,比較慢
- function matchCIDR($ip, $cidr) {
- list($ip, $mask) = explode('/', $cidr);
- $mask =(32 - $mask);
- return ((ip2long($ip) >> $mask) == (ip2long($ip) >> $mask));
- }
- //方法二,最快
- function netMatch($IP, $CIDR) {
- list ($net, $mask) = explode('/', $CIDR);
- return ( ip2long($IP) & ~((1 << (32 - $mask)) - 1) ) == ip2long($net);
- }
附註:以下資料,非vld呈現的完整資訊。我僅列出重點。
function name: matchCIDR number of ops: 21 compiled vars: !0 = $ip, !1 = $cidr, !2 = $mask line # * op fetch ext return operands --------------------------------------------------------------- 3 0 > RECV 1 1 RECV 2 4 2 SEND_VAL '%2F' 3 SEND_VAR !1 4 DO_FCALL 2 'explode' 5 FETCH_DIM_R $1 $0, 1 6 ASSIGN !2, $1 7 FETCH_DIM_R $3 $0, 0 8 ASSIGN !0, $3 5 9 SUB ~5 32, !2 10 ASSIGN !2, ~5 6 11 SEND_VAR !0 12 DO_FCALL 1 'ip2long' 13 SR ~8 $7, !2 14 SEND_VAR !0 15 DO_FCALL 1 'ip2long' 16 SR ~10 $9, !2 17 IS_EQUAL ~11 ~8, ~10 18 > RETURN ~11 7 19* RETURN null 20* > ZEND_HANDLE_EXCEPTION function name: netMatch number of ops: 22 compiled vars: !0 = $IP, !1 = $CIDR, !2 = $net, !3 = $mask line # * op fetch ext return operands --------------------------------------------------------------- 10 0 > RECV 1 1 RECV 2 11 2 SEND_VAL '%2F' 3 SEND_VAR !1 4 DO_FCALL 2 'explode' 5 FETCH_DIM_R $1 $0, 1 6 ASSIGN !3, $1 7 FETCH_DIM_R $3 $0, 0 8 ASSIGN !2, $3 12 9 SEND_VAR !0 10 DO_FCALL 1 'ip2long' 11 SUB ~6 32, !3 12 SL ~7 1, ~6 13 SUB ~8 ~7, 1 14 BW_NOT ~9 ~8 15 BW_AND ~10 $5, ~9 16 SEND_VAR !2 17 DO_FCALL 1 'ip2long' 18 IS_EQUAL ~12 ~10, $11 19 > RETURN ~12 13 20* RETURN null 21* > ZEND_HANDLE_EXCEPTION
留言