ブラウザが吐き出すユーザエージェントがアクセスしてきた端末を特定する(たぶん唯一の)手がかりである。各キャリアのユーザエージェントに関する仕様は以下のとおり。

ユーザエージェント | サービス・機能 | NTTドコモ

KDDI au: そのほかの技術情報 > ユーザーエージェント

ソフトバンク – 技術資料 – ユーザーエージェントについて

アクセスしてきたユーザを特定する情報として「個体識別番号」がある。取得方法は以下のとおり。

◆DoCoMo: aタグ / formタグに「utn=“utn”」を付与する必要があり、ユーザがクリックすると送信を知らせるダイアログが表示される。

◆au: リクエストヘッダ[HTTP_X_UP_SUBNO]として垂れ流し。

◆SoftBank: 端末側で送出する設定にしていればリクエストヘッダ[HTTP_X_JPHONE_UID]およびユーザエージェントに乗ってくる。

ということでちょっと長いがKwappaで使っている機種情報取得クラス。

<?php
/**
*  端末情報クラス
*/
class MobileUtil
{
    private $type = "" ;        // キャリア種別("dcm"とか)
    private $name = "" ;        // 端末名("J-SH02"とか)
    private $icc  = "" ;        // icc : au / SB / FOMA
    private $ser  = "" ;        // ser : DoCoMoのみ
    private $ua   = "" ;        // ユーザエージェント
    public function __construct()
    {
        // ユーザエージェントを控える
        $this->ua = getenv("HTTP_USER_AGENT") ;
        $uaSlash = explode("/", $this->ua) ;
        //======================================================================
        // UAがSBM関係の文字列で開始 = sb
        //======================================================================
        if (preg_match("/^(J-PHONE)|(Vodafone)|(SoftBank)|(MOT-[VC]980)/", $uaSlash[]))
        {
            $this->type = KWAPPA_TERM_SB ;
            // HTTP_X_JPHONE_MSNAMEに値があればそのまま取得
            if (($this->name = getenv("HTTP_X_JPHONE_MSNAME")) != "")
            {
                $this->uid  = getenv("HTTP_X_JPHONE_UID") ;
            }
            // HTTP_X_JPHONE_MSNAMEが未設定ならUAから切り出し
            else
            {
                // モトローラ2端末
                if ($uaSlash[] == "MOT-V980")
                {
                    $this->name = "V702MO" ;
                }
                else if ($uaSlash[] == "MOT-C980")
                {
                    $this->name = "V702sMO" ;
                }
                // 一般端末
                else
                {
                    $this->name = $uaSlash[2] ;
                }
            }
            // iccの取り出し
            if (preg_match("/^SN\d+/", $uaSlash[4], $result))
            {
                $this->icc = substr($result[], 2) ;
            }
        }
        //======================================================================
        // その他端末
        //======================================================================
        else
        {
            //==================================================================
            // 先頭が DoCoMo = DoCoMo
            //==================================================================
            if ($uaSlash[] == "DoCoMo")
            {
                $this->type = KWAPPA_TERM_DCM ;
                // PDC -----------------------------------------------------
                if ($uaSlash[1] == "1.0")
                {
                    // 端末名
                    $this->name = $uaSlash[2] ;
                    // ser(製造番号)
                    if (preg_match("/ser(.{11})/", $this->ua, $result))
                    {
                        $this->ser = $result[1] ;
                    }
                }
                // FOMA ----------------------------------------------------
                else
                {
                    // 端末名
                    $t = substr($uaSlash[1], 4) ;
                    $this->name = substr($t, , strpos($t, "(")) ;
                    // ser(製造番号) / icc(FOMAカードシリアル)
                    if (preg_match("/ser(.{15}).*icc(.{20})/", $this->ua, $result))
                    {
                        $this->ser = $result[1] ;
                        $this->icc = $result[2] ;
                    }
                }
            }
            //==================================================================
            // "UP.Browser"文字列を発見 = au
            //==================================================================
            else if (($upbPos = strpos($this->ua, "UP.Browser")) !== false)
            {
                $this->type = KWAPPA_TERM_AU ;
                // UAを" "で分割、最初のブロックのハイフン以降を取得
                $uaBlank = explode(" ", $this->ua, 2) ;
                $this->name = substr($uaBlank[], strrpos($uaBlank[], "-") + 1) ;
                // icc
                $this->icc = getenv("HTTP_X_UP_SUBNO") ;
            }
            //==================================================================
            // それ以外はPCからのアクセス
            //==================================================================
            else
            {
                $this->type = KWAPPA_TERM_PC ;
            }
        }
    }
    // アクセサ略
}
?>

なお、DoCoMoは近日「iモードID」として端末識別情報の送出をSoftBankに近い仕様にする予定。

重要なお知らせ : 『iモードID』の提供開始について | お知らせ | NTTドコモ

送出される文字列はおそらくいままでの個体識別番号と違うものになるだろう。個体識別による認証が随時行えるのは大変ありがたいのだが、仕事が増えるのが確定しているので今からちょっと憂鬱である。