MobileProfile

勉強会で気になったので 自分で作ったモバイル端末用判別・詳細情報取得用クラス めっさ仕事で使ってます。

<?php
/**
 * 機種情報取得(おおよその端末データ取得)
 * @auther のびー
 */
class MobileProfile{
  static $ins= null;
  // モバイルの情報
  var $ua;
  var $carrier;
  var $minor;
  var $width;
  var $height;
  var $htmltype;
  var $uid;
  //モバイルのプロフィール詳細
  var $id;
  var $device_name;
  var $name;
  var $maker;
  var $series;
  var $language;
  var $language_ver;
  var $browser_size;
  var $display_size;
  var $is_decome;
  var $decome_ver;
  var $connect_type;
  var $color;
  var $is_gif;
  var $is_jpg;
  var $is_png;
  var $camera;
  var $sub_camera;
  var $appli;
  var $appli_ver;
  var $appli_size;
  var $arrival_melody;
  var $is_arrival_music;
  var $is_arrival_full_music;
  var $is_arrival_movie;
  var $movie_type;
  var $flash;
  var $is_gps;
  var $is_ssl;
  var $mobile_customize;
  var $device_cash;
  var $release_date;
  var $modify_deta;
  var $mobileData;
  /**
   * CONSTRUCTOR
   */
  function MobileProfile()
  {
    self::$ins =& $this;
    self::$ins->setUa($_SERVER['HTTP_USER_AGENT']);
    self::$ins->carrierSet();
    self::$ins->setMobile();
  }
  /**
   * 静的に呼び出された時に自身でインスタンスを取得する
   */
  function getInstance(){
    if (null === self::$ins){
      new MobileProfile();
    }
  }
  
  /**
   * METHOD
   */
  function carrierSet()
  {
    self::getInstance();
    self::$ins->ins->carrier = "";
    self::$ins->minor = "";
    
    switch(TRUE){
      //DOCOMO端末
      case (self::$ins->isDocomo()):
        self::$ins->setCarrier("DoCoMo");
      break;
      //SOFTBANK端末
      case (self::$ins->isSoftbank()):
        self::$ins->setCarrier("Softbank");
      break;
      //KDDI端末
      case (self::$ins->isEzweb()):
        self::$ins->setCarrier("au");
      break;
      //WILLCOM端末
      case (self::$ins->isWillcom()):
        self::$ins->setCarrier("WILLCOM");
      break;
      //googlebot判定
      case (self::$ins->isGooglebot()):
        self::$ins->setCarrier("GOOGLE");
      break;
      //i-robot判定
      case (self::$ins->isIrobot()):
        self::$ins->setCarrier("I-ROBOT");
      break;
      //検証用PC
      default:
        self::$ins->setCarrier('OTHER');
    }
  }
  /**
   * 端末の詳細情報をセット
   */
  function setMobile()
  {
    self::getInstance();
    switch(self::$ins->carrier){
      case ('DoCoMo'):
        preg_match("/^DoCoMo\/[0-9\.]{3}[ \/]([^\/\( ]*)/",self::$ins->ua,$ma);
        $deviceName = $ma[1];
        preg_match("/[\(|\/]c([0-9]{2,3})[\)|;|\/]/",self::$ins->ua."/",$ma);
        self::$ins->setDeviceCash($ma[1]*1000);
        //下位端末レベルのFOMAはMOVA扱い
        $lowar = array("F2051"=>true,"N2051"=>true,"P2102V"=>true,
        "F2102V"=>true,"N2102V"=>true,
        "N2701"=>true,"NM850iG"=>true,
        "N2001"=>true,"N2002"=>true,"P2002"=>true,"D2101V"=>true,
        "P2101V"=>true,"SH2101V"=>true,"T2101V"=>true);
        //MOVA(一部FOMA端末を含む)
        if(preg_match("/^DoCoMo\/1\.0/",self::$ins->ua) ||
         isset($lowar[$deviceName])){
          self::$ins->setMinor('MOVA');
          if($ma[1] < '20'){
            self::$ins->setWidth(120);
            self::$ins->setHeight(180);
          }else{
            self::$ins->setWidth(200);
            self::$ins->setHeight(180);
          }
          self::$ins->setHtmlType("HTML");
          $buf = explode("/",self::$ins->ua);
          self::$ins->setDeviceName($buf[2]);
        //FOMA
        }else{
          self::$ins->setMinor('FOMA');
          self::$ins->setWidth(240);
          self::$ins->setHeight(320);
          self::$ins->setHtmlType("XHTML");
          self::$ins->setDeviceName($deviceName);
        }
      break;
      case('au'):
        //WAP1
        if (preg_match("/^UP\.Browser/",self::$ins->ua)) {
          self::$ins->setMinor('WAP1');
          self::$ins->setHtmlType("HTML");
        //WAP2
        }else{
          self::$ins->setMinor('WAP2');
          self::$ins->setHtmlType("XHTML");
        }
        if(isset($_SERVER['HTTP_X_UP_DEVCAP_SCREENPIXELS'])){
          list($w,$h) = split(',',$_SERVER['HTTP_X_UP_DEVCAP_SCREENPIXELS']);
        }else{
          $w = 96;
          $h = 96;
        }
        self::$ins->setWidth($w);
        self::$ins->setHeight($h);
        if(isset($_SERVER['HTTP_X_UP_DEVCAP_MAX_PDU'])){
          self::$ins->setDeviceCash($_SERVER['HTTP_X_UP_DEVCAP_MAX_PDU']);
        }else{
          self::$ins->setDeviceCash(200000);
        }
        $n = explode("-",self::$ins->ua);
        $device = explode(" ",$n[1]);
        self::$ins->setDeviceName($device[0]);
      break;
      case('Softbank'):
        if(preg_match("/^J\-PHONE\/2/",self::$ins->ua) ||
         preg_match("/^J\-PHONE\/3/",self::$ins->ua)){
          //C型
          self::$ins->setMinor("TYPEC");
          self::$ins->setHtmlType("HTML");
          self::$ins->setDeviceCash(6000);
        }elseif(preg_match("/^J\-PHONE\/4/",self::$ins->ua)){
          //P型
          self::$ins->setMinor("TYPEP");
          self::$ins->setHtmlType("HTML");
          self::$ins->setDeviceCash(12000);
        }else if(preg_match("/^J\-PHONE\/5/",self::$ins->ua)){
          //W型
          self::$ins->setMinor("TYPEW");
          self::$ins->setHtmlType("XHTML");
          self::$ins->setDeviceCash(200000);
        }else if(preg_match("/^MOT\-/",self::$ins->ua) ||
         preg_match("/^Vodafone\//",self::$ins->ua) ||
          preg_match("/^SoftBank\//",self::$ins->ua)){
          //3GC型
          self::$ins->setMinor("3GC");
          self::$ins->setHtmlType("XHTML");
          self::$ins->setDeviceCash(300000);
        }
        if(isset($_SERVER['HTTP_X_JPHONE_DISPLAY'])){
          list($w,$h) = split('\*',$_SERVER['HTTP_X_JPHONE_DISPLAY']);
        }else{
          $w = 96;
          $h = 96;
        }
        self::$ins->setWidth($w);
        self::$ins->setHeight($h);
        if(isset($_SERVER['HTTP_X_JPHONE_MSNAME'])){
          self::$ins->setDeviceName($_SERVER['HTTP_X_JPHONE_MSNAME']);
        }else{
          self::$ins->setDeviceName("");
        }
      break;
      case('OTHER'):
        self::$ins->setMinor("PC");
        self::$ins->setWidth(240);
        self::$ins->setHeight(320);
        self::$ins->setHtmlType("XHTML");
        self::$ins->setDeviceCash(99999999);
        $pc = (isset($_REQUEST['pc']))?$_REQUEST['pc']:"";
        self::$ins->setDeviceName("PC");
      break;
    }
  }
  
  /**
   * 端末詳細データ取得(共通管理ファイルより)
   * @param  String  device_name    端末ID
   */
  function getMobileDetail($device_name=null)
  {
    self::getInstance();
    if(!$device_name){
      $device_name = self::$ins->device_name;
    }
    $dirfile = __FILE__;
    $dir_name = str_replace(basename(__FILE__),'',$dirfile);
    $dir_name .= 'MobileProfileData/';
    $carrier = self::$ins->carrier;
    $carrier = strtolower($carrier);
    //データ配列読み込み
    if(file_exists($dir_name.$carrier.'Data.php')){
      require_once $dir_name.$carrier.'Data.php';
      if(!$this->mobileData){
        $this->mobileData = $modile_detail;
      }
    }else{
      return FALSE;
    }
    if(isset($this->mobileData[$device_name])){
      foreach($this->mobileData[$device_name] as $key => $val){
        self::$ins->{$key} = $val;
      }
      if(isset(self::$ins->display_size)){
        list(self::$ins->height,self::$ins->width) =
         explode('*',self::$ins->display_size);
      }
      return $this->mobileData[$device_name];
      //TODO
      //すでにセットしてあるプロパティの値を上書き

    }else{
      return FALSE;
    }
  }
  
  /**
   * 公式UIDの取得
   */
  function getPublicUid()
  {
    self::getInstance();
    $uid = '';
    switch(self::$ins->carrier){
      case('DOCOMO'):
    //CPの守秘義務に引っかかるかもなので消しときます 
      break;
      case('EZWEB'):
    //CPの守秘義務に引っかかるかもなので消しときます 
      break;
      case('SOFTBANK'):
    //CPの守秘義務に引っかかるかもなので消しときます 
      break;
      default :
        $uid = TESTUID; //定数で適当なUIDを定義
      break;
    }
    return $uid;
  }
  
  /**
   * 非公式UTN取得
   *
   */
  function getNotPublicUid()
  {
    $uid = FALSE;
    switch(self::$ins->carrier){
      case('DOCOMO'):
        //FOMAの場合はFOMAカード識別番号を使う
        if(ereg("DoCoMo/2\.0.*;icc(.*)\)$",self::$ins->ua,$regs1) == TRUE){
          $uid = $regs1[1];
        }elseif(ereg("DoCoMo/1\.0/.*ser(.*)$",self::$ins->ua,$regs2) == TRUE){
          //movaの場合は端末製造番号を使う
          $uid = $regs2[1];
        }
      break;
      case('EZWEB'):
        if(isset($_SERVER['HTTP_X_UP_SUBNO'])){
          $uid = $_SERVER['HTTP_X_UP_SUBNO'];
        }
      break;
      case('SOFTBANK'):
        if(isset($_SERVER['HTTP_X_JPHONE_UID'])){
          $uid = substr($_SERVER['HTTP_X_JPHONE_UID'],1);
        }elseif(isset($_SERVER["x-jphone-uid"])){
          $uid = $_SERVER["x-jphone-uid"];
        }
      break;
      default :
        $uid = 'TESTUID';
      break;
    }
    return $uid;
  }
  /**
   * キャリア判定(DOCOMO)
   */
  function isDocomo()
  {
    if(preg_match("/^DoCoMo/",self::$ins->ua)){
      return TRUE;
    }else{
      return FALSE;
    }
  }
  /**
   * キャリア判定(SOFTBANK)
   */
  function isSoftbank()
  {
    if(preg_match("/^J\-PHONE/",self::$ins->ua) ||
     preg_match("/^MOT\-/",self::$ins->ua) ||
      preg_match("/^Vodafone\//",self::$ins->ua) ||
       preg_match("/^SoftBank\//",self::$ins->ua)){
      return TRUE;
    }else{
      return FALSE;
    }
  }
  /**
   * キャリア判定(EZWEB)
   */
  function isEzweb()
  {
    if(preg_match("/^UP\.Browser/",self::$ins->ua) ||
     preg_match("/^KDDI/",self::$ins->ua)){
      return TRUE;
    }else{
      return FALSE;
    }
  }
  /**
   * キャリア判定(WILLCOM)
   */
  function isWillcom()
  {
    return FALSE;
  }
  /**
   * キャリア判定(GOOGLEBOT)
   */
  function isGooglebot()
  {
    return FALSE;
  }
  
  function isIrobot()
  {
    return FALSE;
  }
  /**
   * SETTER
   */
  function setDeviceName($arg)
  {
    self::$ins->device_name = $arg;
  }
  function setHtmlType($arg)
  {
    self::$ins->htmltype = $arg;
  }
  
  function setUa($arg)
  {
    self::$ins->ua = $arg;
  }
  function setUid($arg)
  {
    self::$ins->uid = $arg;
  }

  function setDeviceCash($arg)
  {
    self::$ins->device_cash = $arg;
  }
  function setWidth($arg)
  {
    self::$ins->width = $arg;
  }
  function setHeight($arg)
  {
    self::$ins->height = $arg;
  }
  function setMinor($arg)
  {
    self::$ins->minor = $arg;
  }
  function setCarrier($arg)
  {
    self::$ins->carrier = $arg;
  }  
  /**
   * GETTER
   */
  //自由に取得できるgeter
  function get($arg)
  {
    self::getInstance();
    if(!$arg){
      return FALSE;
    }
    $ret = FALSE;
    if(is_array($arg)){
      foreach($arg as $val){
        if(isset(self::$ins->{$val})){
          $ret[$val] = self::$ins->{$val};
        }
      }
    }else{
      if(isset(self::$ins->{$arg})){
        $ret = self::$ins->{$arg};
      }else{
        return FALSE;
      }
    }
    return $ret;
  }
}
?>

ってなクラスを使ってます

クラスと同一ディレクトリ内に
『MobileProfileData』
ってディレクトリを作成して
その中に
auData.php   ←au用の端末詳細情報配列
docomoData.php ←ドコモ用
otherData.php  ←全キャリア用
softbankData.phpソフトバンク
って各ファイルを作成
ファイルの中身は

<?php
'HI3C' => array('id' => '661','device_name' => 'HI3C','name' => 'W61H',
'carrier' => 'EZweb','maker' => '日立','series' => 'W6x',
'language' => 'XHTML','language_ver' => '0','browser_size' => 380*240',
'display_size' => '400*240','is_decome' => 'YES','decome_ver' => '2',
'connect_type' => 'CDMA2000 1X EV-DO','color' => '65535','is_gif' => 'YES',
'is_jpg' => 'YES','is_png' => 'YES','camera' => '200','sub_camera' => '0',
'appli' => 'BREW','appli_ver' => '3.1','appli_size' => '0',
'arrival_melody' => '0','is_arrival_music' => 'YES',
'is_arrival_full_music' => 'YES','is_arrival_movie' => 'YES',
'movie_type' => 'amc,3gp,3g2','flash' => 'FL2.0','is_gps' => 'YES',
'is_ssl' => 'YES','mobile_customize' => 'NO',
'device_cash' => '131072',
'release_date' => '9999-12-31 23:59:59',
'modify_deta' => '2008-04-02 11:25:14',)
?>

って配列が大量に並んでます。

ちなみに弊社ではこの配列はDBで保存して端末が発売されるたびに更新かけてます
バッチ組んで各キャリアごとの上記ファイルを生成。
配列が大きすぎるとか思うなら世代ごとにさらに詳細にファイル分割してやればすむかなぁと思ってますよ。

んで、使い方
超簡単

<?php
//newしてやって
$LIB_MP = new MobileProfile();
//端末名称取得 この時点で大雑把な一般的な数値が取得可能
$device_name = $LIB_MP->get("device_name");
//端末が登録されていなければ
if($device_name == 'PC'){
  //SH904用のProfileでも入れとけばいいんじゃね?
  $device_name = 'SH904i';
}
//で、詳細情報取得
$LIB_ARR_MP = $LIB_MP->getMobileDetail($device_name);
//なんでか取れなかったら
if(!$LIB_ARR_MP){
  //もっかいSH904を取得してみる
  $LIB_ARR_MP = $LIB_MP->getMobileDetail("SH904i");
}

//とりあえず出力してみる?
if($LIB_ARR_MP){
  foreach($LIB_ARR_MP as $key => $val){
    $str .= sprintf("%20s %s",$key,":".$val)."\n";
  }
}

?>


上記やってやれば

id                   :395
device_name          :SH904i
name                 :SH904i
carrier              :DoCoMo
maker                :SHARP
series               :FOMA904i
language             :XHTML
language_ver         :2.1
browser_size         :320*240
display_size         :400*240
is_decome            :YES
decome_ver           :3
connect_type         :WCDMA
color                :262144
is_gif               :YES
is_jpg               :YES
is_png               :NO
camera               :320
sub_camera           :11
appli                :Java
appli_ver            :5
appli_size           :1024
arrival_melody       :128
is_arrival_music     :YES
is_arrival_full_music:YES
is_arrival_movie     :YES
movie_type           :3gp
flash                :FL1.1
is_gps               :YES
is_ssl               :YES
mobile_customize     :
device_cash          :102400
release_date         :2007-05-2500:00:00
modify_deta          :0000-00-0000:00:00

ってなデータが取得できるんでないかな?

ちなみに弊社は検証はほぼPCです。
おれおれふれーむわーく(笑)でPCからの検証しやすくやってるんで。
細かい表示周りや最終的なチェックは全部端末でやりますけどね もちろん。

正直ドコモの端末以外は環境変数でほぼ全ての情報が取れますよ。
ドコモのためだけにDBにデータ持ってるようなもんなんで
少なくとも環境変数にブラウザサイズだけでも提供してもらえるとありがたいんですけどね・・・

ってことで上記方法で端末判定行ってます。
DBさえ作ってしまえば端末発売毎の登録の煩わしさだけで済むので
普段の対応考えると管理画面を作っちゃえば割りと楽です。


ってことで次は絵文字変換行って見ますか?
気が向いたら書きます。