[Utility.php] 携帯電話を判別する。IPアドレスから。【確定版】

昨日、[Utility.php] 携帯電話を判別する。IPアドレスから。 で、コードを掲載したのだけど、やっぱり気に入らない箇所があるので、書き直した。

昨日の記事と重複する内容は省略するので、昨日の記事と合わせてお読み下さい。

変更した箇所

  • IPアドレス帯域を手動更新してたのを、各webサイトから自動取得するようにした
  • 生成するPHPコードをスリム化した。(263行→188行に)
  • テストコードを追加した。
  • PHPコードを生成するコードをclass化した。(※)

(※) システムに組み込んで本格的に使う箇所じゃないので、わざわざclassにする必要もないと思う。ブログに掲載するために、public と private の関係(functionのスコープ)なんかを明示したかっただけなので。

下記のコードを実行するとこのように出力されます。
生成された【 isMobile 】関数を、お使いのシステムにコピーして使用します。

$this->carriers = array(
array(
"carrier" => self::cc_return_value_docomo
, "uri" => "http://www.nttdocomo.co.jp/service/imode/make/content/ip/"
, "pattern" => "/<li>([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,2})</li>/"
)
,
array(
"carrier" => self::cc_return_value_au
, "uri" => "http://www.au.kddi.com/ezfactory/tec/spec/ezsava_ip.html"
, "pattern" => "/<td><div class="TableText">([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3})</div></td>s+<td><div class="TableText">(/[0-9]{1,2})</div></td>/"
)
,
array(
"carrier" => self::cc_return_value_yahoo
, "uri" => "http://creation.mb.softbank.jp/web/web_ip.html"
, "pattern" => "/<td bgcolor="#eeeeee">&nbsp;&nbsp;([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,2})</td>/"
)
);
}

/*
* デスクトラクタ
*/
public function __destruct(){}

/*
* PHPコードを取得する
*/
public function getPhpCode()
{
// IPアドレス帯域の配列
$arrCidr = array();
foreach ($this->carriers as $ca){
if (($arr = $this->getIpRangeFromUri($ca["carrier"], $ca["uri"], $ca["pattern"])) === false){
return 'load error : ' . $ca["carrier"] . ' site' . C_LF;
}
foreach ($arr as $ar){
array_push($arrCidr, $ar);
}
}

// IPアドレスの最小値で昇順にソート
$func = create_function('$va, $vb'
, ' $a = $va[0]; $b = $vb[0]; if ($a == $b) return 0; return ($a < $b) ? -1 : 1;');
usort($arrCidr, $func);

// IPアドレス範囲が連続する箇所を連結して配列を作り直す
$list = array();
$cnt = count($arrCidr);
$prev = array(0, 0, 'none', '0.0.0.0/32');
$j = 0;
for ($i = 0; $i getCode($list, $harf, 0, $upper, 0);
}

/*
* 携帯電話会社がIPアドレス帯を公開しているURIからリストを取得
*/
private function getIpRangeFromUri($carrier, $uri, $pattern){
$retArr = array();
$html = file_get_contents($uri);
$html = mb_convert_encoding($html, "utf-8", "utf-8,sjis,euc");
if (preg_match_all($pattern, $html, $regs)){
$cnt = count($regs[1]);
for ($i = 0; $i getRangeAndValue($cidr, $carrier);
array_push($retArr, $arr);
}
return $retArr;
}
return false;
}

/*
* CIDRから、IPアドレスの上限・下限とその他を配列で返す
*/
private function getRangeAndValue($cidr, $carrier){
$buf = explode('/', $cidr);
$start = sprintf('%u', ip2long($buf[0]));
$end = $start;
if (isset($buf[1])){
$end += $this->ipRange($buf[1]) - 1;
}
return array(
(float)$start
, (float)$end
, $carrier
, $cidr
);
}

/*
* IPアドレスの範囲の個数を返す
*/
private function ipRange($range){
if ($range > 32){
return 0;
} else if ($range < 0){
return 0;
} else {
return pow(2, (32 - $range));
}
}

/*
* IPアドレスを二分探索する条件式を作る
*/
private function getCode(&$list, $harf, $lower, $upper, $kaisou, $elseif = false){
$ret = '';

// 階層に合わせて、行の先頭にタブ文字を挿入
$tab = C_TAB2;
for ($i = 0; $i = ' . $list[$harf][0] . '){'
. ' // $list[' . $harf . ' to ' . $upper . ']' . C_LF;
$ret .= $tab . C_TAB1 . 'if ($ip 0){
$n = ($n + ($n % 2)) / 2 + $harf2;
$ret .= $tab . C_TAB1 . '} else ';
$ret .= $this->getCode($list, $n, $harf2, $upper, $kaisou, true);
} else if ($n == 0){
$ret .= $tab . C_TAB1 . '} else if ($ip >= ' . $list[$harf2][0] . '){' . C_LF;
$ret .= $tab . C_TAB2 . 'if ($ip 0){
$n = ($n - ($n % 2)) / 2 + $lower;
$ret .= $tab . '} else ';
$ret .= $this->getCode($list, $n, $lower, $harf2, $kaisou-1, true);
}
if ( ! $elseif) $ret .= $tab . '}' . C_LF;
return $ret;
}
}

// 実行
$mobileIpRange = new mobileIpRange();

?>

<title>携帯電話のIPアドレス判別</title>

<!--
body, textarea{
font-size : 12px;
line-height:135%;
font-family:"MS Pゴシック", sans-serif;
}
textarea{
width:600px;
height:500px;
}
-->

<h1>携帯電話のIPアドレス判別</h1>
<form>
<h2>テストコード</h2>
<textarea><?php
// array('期待する値', 'IPアドレス')
$arrTest = array(
array('other', '192.168.0.1')
, array('docomo', '210.153.84.50')
, array('au', '121.111.231.1')
, array('au', '121.111.231.10')
, array('au', '121.111.231.161')
, array('au', '121.111.231.168')
, array('yahoo', '123.108.237.243')
, array('yahoo', '123.108.236.115')
, array('yahoo', '123.108.237.31')
, array('docomo', '124.146.174.10')
, array('docomo', '124.146.174.123')
, array('docomo', '124.146.175.52')
, array('docomo', '124.146.175.48')
);

echo '期待する値と戻り値が異なれば赤色で表示';
echo '<ul>';
foreach ($arrTest as $arr){
$ret = isMobile($arr[1]);
echo '<li>'
. $arr[0] . ' : ' . $arr[1] . ' : ' . $ret . '</li>';
}
echo '</ul>';
?></textarea>
<h2>判別関数</h2>
<textarea>
/*
* IPアドレスから、携帯キャリアを判別する
* IPアドレス帯域は追加・削除されることがあります。
* 携帯電話各社の発表事項に注意してください。
* 生成日時 :
*/
function isMobile($ipAddress){
$ip = sprintf('%u', ip2long($ipAddress));

getPhpCode();?>
return '';
}
</textarea>
</form>