※ パクレゼルヴではWeb開発エンジニアを大募集中!詳細はこちら

Archive

Archive for the ‘携帯電話’ Category

EZのキャッシュに打ち勝つ  

2008/6/3 火曜日 17:34:10

Hi、ボクです。

モバイルにてGDとかで、微々たる修正の度に毎度画像を生成していると、docomoやSoftbankは問題無いのですが、
強力(悪?)なキャッシュ機能をもつau端末では、修正する前の画像が出ちゃうことが多々あります。
その度に、わざわざリロードをかけなきゃいけないのはユーザーにとって最悪。

ファイル名が同じである為に、同じファイルだと勝手に認識して出力してるようです。
ようは、同じファイルと見せなければいいだけであって、ちょっとした修正でできちゃいます。
画像を出力しているところで、適当にタイムスタンプをつけてあげることで対応できるようです。

echo '<img src="filename.gif" />';

 ↓

echo '<img src="filename.gif?t='.time().'" />';

とかしてあげれば、毎度新しい画像が出るはずです〆

下音タヌキ 携帯電話

モバイル端末の判定[USER AGENT]  

2008/5/26 月曜日 20:14:48

のびーにょです。

今回は端末判定のプログラム。キャリアとか端末によって表示変えたいとか多々あると思いますので
携帯用ページを作成する場合必須と言っても過言ではないでしょう。
ってことで行ってみたいと思います。

/**
 * 機種情報取得(おおよその端末データ取得)
 * @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;
	}
}

長いですねぇ・・・
検索エンジン回りが常にFALSEなのはわざとです。
後、WILLCOMも未対応です。(サイトまだ作ってないので放置です)

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

'SH904i' => array('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-25 00:00:00',
				'modify_deta' => '0000-00-00 00:00:00',
				),・・・

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

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

んで、使い方
超簡単

//newしてやって(この時点で大雑把な一般的な数値が取得可能)
$LIB_MP = new MobileProfile();
//端末名称取得
$device_name = $LIB_MP->get("device_name");
//PCからのアクセスの場合、
//もしくは端末名が取れなかった場合(登録漏れ等)
if($device_name == 'PC' || $device_name == FALSE){
  //SH904用の端末情報を取得する
  $device_name = 'SH904i';
}
//で、詳細情報取得
$LIB_ARR_MP = $LIB_MP->getMobileDetail($device_name);
//とりあえず出力してみる
if($LIB_ARR_MP){
  foreach($LIB_ARR_MP as $key => $val){
    $str .= $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

というような実行結果が得られると思います。

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

PEAR::Net_UserAgent_Mobileなんかもありますが
端末情報がベタ書きな上に情報が足りない部分が多々あるので
自作クラスで対応しています。

後更新してないみたいですし。

0.31.0 2008-02-10 beta
↑この間1年更新がなかった(自分で端末情報更新してもいいと思いますけどね)
0.30.0 2007-02-20 beta
0.29.0 2006-11-07 beta
0.28.0 2006-09-25 beta
0.27.0 2006-07-13 beta
0.26.0 2006-02-01 beta

最近RC1が出たみたいですけどね

端末情報は色々な企業さんが販売してますね。
今までに発売した端末情報すべてとか。
買い切りだと4,5万だと思うので、携帯サイトをがっつりやる場合は買って損はないと思います。
後は発売される度に管理画面から追加って形取ればそこまでめんどくさくないですし。

公式CP(IP)やってると詳細情報を手に入れることができるのでそのデータを自社内だけだと存分に活用できるのも含めて
自社オリジナルクラス使ってます。

色々なやり方あるとは思いますが、弊社の一部のコンテンツはこんな感じで運用しております

のびーにょ PHP, キャリア, 携帯電話

メール配信  

2008/5/16 金曜日 18:08:10

暑かったり寒かったり雨が降ったり降らなかったり。

皆様いかがお過ごしでしょうか。こんにちわ。かーつんです。

さてさて、今回はなにを書こうか悩んでいたのですが、

良いネタが見つからなかったので、直近のお仕事の様子でも少々。

現在、会員様向けにメール配信を行う用のスクリプトを作ってるんですが、

色々と難航しております。

3つのサーバから1つのDB参照して、同時稼働。

なんか昔バッチ組んでるときに似たようなことしたなぁと思いながら、

正直覚えて無くて、超抜けまくり。

のびーにょ先生に怒られツッコまれながら四苦八苦しております。

で、気をつけるべき点で、残しておきたいと思ったことを以下に記しておきます。

1.コネクション数・SQL発行回数を極限まで減らして一括でデータ取得すると、

  同時稼働している3つのサーバに同じデータが取得される可能性がある。

  ⇒回避策⇒

   データテーブルとは別にカウント用のテーブルをテンポラリとして作成して、

   取得した件数をそっちに残しておけば、

   サーバジョブ起動タイミングを少しずらすだけで異なるデータを一括取得できる。

   但し、ジョブを途中で中断したときにデータに不整合が起こるので、

   回避策が更に必要。(´・д・`)

2.MySQLはUPDATEの対象テーブルと、

  WHERE句で使用するSELECT(サブクエリ)の対象テーブルが同じだと、

ERROR 1093 (HY000): You can't specify target table ・・・

  とかって怒る。

  ⇒回避策⇒

   素直にSQL分けるか、DB変える。

   MySQLは少し面倒な処理をさせようとすると、すぐ怒る。。。

   Oracleなどは怒られないのにネ・・・。

3.メールが送れなかった場合の処理が必要なら、

  メールヘッダのReturn-Pathにエラーメールを送って欲しいアドレスを記載する。

  ⇒具体的には⇒

mb_send_mail('送信先メールアドレス','タイトル','本文',
	メールヘッダ(送信元アドレスとか文字コードとか),
	'ヘッダオプション');

⇒⇒⇒ Return-Pathはヘッダオプションに、
‘-f エラーメールを受信したいアドレス’
を書くとよし。

mb_send_mail("hogehoge@hoge.jp","タイトル","本文",
	"FROM:hogetyo@hoge.jp \n
	Content-Type: text/plain; charset=ISO-2022-JP",
	"-f hoge-error@hoge.jp");

  こんな感じ。

4.送信間隔を考えないと、携帯キャリアからスパム扱いされる。

  ⇒回避策⇒

   ん~模索中。リアルタイムで調整できると一番良いんだけど・・・。(´・д・`)

と、まぁ、こんなとこでしょうか。

他にも色々あったような気がするけど、

自身で理解し切れてないことが多いので、

またの機会と言うことで。

でわ、おばかーつんがおおくりしました。

かーつん Linux, MySQL, PHP, 携帯電話

各キャリアのHTMLの違い(FORM)  

2008/4/24 木曜日 17:55:11

おいっす

のびーにょです

今日は各キャリアでのHTMLの表示の違いについて

まずは以下(XHTMLです)

TEST FORM
<form method="get" action="http://example.com/">
<input type="submit" />
</form>
TEST FORM

って書いた場合

ドコモ ソフトバンク
ドコモ(改行無し) ソフトバンク(改行無し)

まぁこうなると思いますが
流石Ezweb
Ezweb端末(改行無し)
こうなります

んじゃ、改行入れればいいんじゃね?的な乗りでbrタグ入れてみると

TEST FORM<br />
<form method="get" action="http://example.com/">
<input type="submit" />
</form><br />
TEST FORM

Ezweb
Ezweb(改行あり)

を できたできた ってな
他のキャリアがな。。。

ドコモ ソフトバンク
ドコモ(改行あり) ソフトバンク(改行あり)

ってなるわけです。

だからこそそれぞれのキャリア用のテンプレート書くとか色々やるんですが
正直めんどくさいのでデザインがそれほど崩れなければそのまま行きます。
もしくはformタグの閉じる場所をHTMLの最下層ぐらいまで持って行ってキャリアの差を吸収する感じ

<form method="get" action="http://example.com/">
TEST FORM<br />
<input type="submit" />
TEST FORM<br />
</form>

って感じでやってます。

キャリア毎にテンプレート用意して、それぞれのテンプレートを書く なんてよく聞きますが
数年コンテンツ触っててもそんな事やったことないわけです。

僕は流行りに乗り遅れた部類の人なのでしょうか・・・

のびーにょ HTMLとか, キャリア, 携帯電話

ドコモのiモードID取得について  

2008/3/31 月曜日 11:06:07

こんにちは のびーにょです
今日(2008/3/31)からドコモのiモードIDが取得できるようになりましたね

ってことで早速取得してみました

URLに以下のクエリパラメータ追加

guid=ON

で、プログラム側で取得です。
HTTPの拡張ヘッダにて取得可能

X-DCMGUID: *******

ってことでPHPで取得するならこんな感じ

echo $_SERVER['HTTP_X_DCMGUID'];

既存のサイトはUTNでユーザ識別してたのでその部分を今から対応してみます。

詳しい仕様はこちら
http://www.nttdocomo.co.jp/
service/imode/make/content/ip/index.html#imodeid

のびーにょ PHP, Tips, キャリア, 携帯電話