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

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

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

変更した箇所

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

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

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

carriers = array(
        array(
          "carrier" => self::cc_return_value_docomo
          , "uri" => "http://www.nttdocomo.co.jp/service/imode/make/content/ip/"
          , "pattern" =&gt; "/<li>([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,2})</li>/"
        )
        ,
        array(
          "carrier" =&gt; self::cc_return_value_au
          , "uri" =&gt; "http://www.au.kddi.com/ezfactory/tec/spec/ezsava_ip.html"
          , "pattern" =&gt; "/<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" =&gt; self::cc_return_value_yahoo
          , "uri" =&gt; "http://creation.mb.softbank.jp/web/web_ip.html"
          , "pattern" =&gt; "/<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-&gt;carriers as $ca){
        if (($arr = $this-&gt;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 &lt; $b) ? -1 : 1;&#039;);
      usort($arrCidr, $func);

      // IPアドレス範囲が連続する箇所を連結して配列を作り直す
      $list = array();
      $cnt = count($arrCidr);
      $prev = array(0, 0, &#039;none&#039;, &#039;0.0.0.0/32&#039;);
      $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-&gt;ipRange($buf[1]) - 1;
      }
      return array(
          (float)$start
        , (float)$end
        , $carrier
        , $cidr
      );
    }

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

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

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

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

?&gt;





<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>&lt;?php
  // array(&#039;期待する値&#039;, &#039;IPアドレス&#039;)
  $arrTest = array(
    array(&#039;other&#039;, &#039;192.168.0.1&#039;)
    , array(&#039;docomo&#039;, &#039;210.153.84.50&#039;)
    , array(&#039;au&#039;, &#039;121.111.231.1&#039;)
    , array(&#039;au&#039;, &#039;121.111.231.10&#039;)
    , array(&#039;au&#039;, &#039;121.111.231.161&#039;)
    , array(&#039;au&#039;, &#039;121.111.231.168&#039;)
    , array(&#039;yahoo&#039;, &#039;123.108.237.243&#039;)
    , array(&#039;yahoo&#039;, &#039;123.108.236.115&#039;)
    , array(&#039;yahoo&#039;, &#039;123.108.237.31&#039;)
    , array(&#039;docomo&#039;, &#039;124.146.174.10&#039;)
    , array(&#039;docomo&#039;, &#039;124.146.174.123&#039;)
    , array(&#039;docomo&#039;, &#039;124.146.175.52&#039;)
    , array(&#039;docomo&#039;, &#039;124.146.175.48&#039;)
  );

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

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



広告

1 個のコメント