ロックオン開発チームブログ -Lockon Knowledge Repositories-

CategoryPHP

特定タグだけEscapeしないSmarty関数

Smartyを使うことが多いのですが、組み込みのescapeだと、何でもかんでもescapeされてしまうので、特定のタグだけはタグとして表示されるような関数を作ってみました。

function smarty_modifier_htmlescape($string, $arrAllowTag)
{
    $string = htmlspecialchars($string);

    foreach($arrAllowTag as $tag) {
        $string = preg_replace_callback("/<\/?".strtolower($tag)." ?.*>/","htmlescape_unhtmlescape", $string);
        $string = preg_replace_callback("/<\/?".strtoupper($tag)." ?.*>/","htmlescape_unhtmlescape", $string);
    }
    return $string;
}

function htmlescape_unhtmlescape($string){
    
    $string = $string[0];
    
    $string = str_replace("&lt;", "<", $string);
    $string = str_replace("&gt;", ">", $string);
    $string = str_replace("&quot;", "\"", $string);
    return $string;
}


というソースをmodifier.htmlescape.phpという名前でSmartyのPluginフォルダに放り込んでやります。<ダウンロード

$smarty->assign("tag", array("img","a"));


こんな感じで、テンプレートに許可したいタグ名の配列を渡しておいてやれば。


{$message|htmlescape:$tag}

こんなかんじで、大体のaタグ、imgタグはエスケープしないでくれると思います。 もっと良い正規表現があるかもしれませんが、とりあえず。

PearのInstallでエラーが出た件

なんじゃこりゃと思ったエラーについてです。

ちょっと古めのサーバにPEARのHTTP_Requestをインストールしようとしたら、なにやら怒られてしまいました。

#pear install HTTP_Request
downloading HTTP_Request-1.3.0.tgz ...
Starting to download HTTP_Request-1.3.0.tgz (13,808 bytes)
.....done: 13,808 bytes
requires package `Net_URL' >= 1.0.12
requires package `Net_Socket' >= 1.0.2
HTTP_Request: Dependencies failed

??
両方入っているのになぜこんなエラーが?

ちょこっと調べたところ、どうやら依存するパッケージが古いみたいでした。

ということでupgrade。

#pear upgrade Net_URL
downloading Net_URL-1.0.14.tgz ...
Starting to download Net_URL-1.0.14.tgz (5,173 bytes)
.....done: 5,173 bytes
upgrade ok: Net_URL 1.0.14

#pear install HTTP_Request
downloading HTTP_Request-1.3.0.tgz ...
Starting to download HTTP_Request-1.3.0.tgz (13,808 bytes)
.....done: 13,808 bytes
install ok: HTTP_Request 1.3.0

無事に入りましたとさ。

EXCEL出力

Webアプリでも当然Excelに出力したいっていう要件って結構あって、PHPをメインで使ってると、選択肢としては普通に考えるとPEARのSpreadsheet_Excel_Writerぐらいしか無いのですが、こいつって、かなり気を遣って書いても、たいていスパゲッティなことになってしまい、後々のメンテで泣きを見ることになります。

そこで最近お世話になっているのが、ExcelのHTML読み込み機能を使っちゃう手法。


たとえば、なんの変哲もないただのHTMLの表組み。 とうぜん、ふつうに出力すればこんな感じですが、、、

このファイルを$htmlに読み込んでやった後に、
Header("Content-Type: application/vnd.ms-excel");
Header("Content-Disposition: attachment; filename=\"hoge.xls\"");
echo $html; //出力内容はSJISにするのをお忘れ無く

とすれば、かなりソレっぽく表示してくれます。
実行例


保存するときに少々ユーザーに手間を掛けてしまうことになりますが、 こちらだと、少々凝ったレイアウトでも生産性高くExcelシートを作成することが可能です。

セッション情報をDBに保存する

$_SESSIONで読み書きができるPHPのセッション情報は、通常は/tmp/ディレクトリの下に保存されますがこれをDBで読み書きできるように切り換えます。
用意するのは以下のような単純なテーブルでよいです。
CREATE TABLE dtb_session (
    sess_id text NOT NULL,
    sess_data text,
    create_date timestamp NOT NULL,
    update_date timestamp NOT NULL
);
セッションを操作する際に呼び出される関数をユーザ定義のものに切り換える必要があるので、 以下のように関数を呼び出します。
session_set_save_handler(
"sfSessOpen",
"sfSessClose",
"sfSessRead",
"sfSessWrite",
"sfSessDestroy",
 "sfSessGc"
);
また、上記で割り当てたユーザ定義の関数の中身は以下のようになります。
// セッションの有効期限(秒)
define("MAX_LIFETIME", 7200);

// セッションオープン時 function sfSessOpen($save_path, $session_name) { return true; }
// セッションクローズ時 function sfSessClose() { return true; }
// セッション読み込み function sfSessRead($id) { $objQuery = new SC_Query(); $arrRet = $objQuery->query("SELECT sess_data FROM dtb_session WHERE sess_id = " . $id); return($arrRet[0]['sess_data']); }
// セッション書込み function sfSessWrite($id, $sess_data) { $objQuery = new SC_Query(); $count = $objQuery->query("SELECT count(*) FOM dtb_session WHERE sess_id = " . $id); $sqlval = array(); if($count > 0) { // レコード更新 $sqlval['sess_data'] = $sess_data; $sqlval['update_date'] = 'Now()'; $objQuery->update("dtb_session", $sqlval, "sess_id = ?", array($id)); } else { // セッションデータがある場合は、レコード作成 if(strlen($sess_data) > 0) { $sqlval['sess_id'] = $id; $sqlval['sess_data'] = $sess_data; $sqlval['update_date'] = 'Now()'; $sqlval['create_date'] = 'Now()'; $objQuery->insert("dtb_session", $sqlval); } } return true; }
// セッション破棄 function sfSessDestroy($id) { $objQuery = new SC_Query(); $objQuery->query("DELETE FROM dtb_session WHERE sess_id = " . $id); return true; }
// ガーベージコレクション function sfSessGc($maxlifetime) { // ※php.iniで設定さえている$maxlifetimeは、利用しない。 // ユーザ定義のMAX_LIFETIME秒以上更新されていないセッションを削除する。 $objQuery = new SC_Query(); $where = "update_date < current_timestamp + '-". MAX_LIFETIME . " secs'"; $objQuery->query("DELETE FROM dtb_session WHERE " . $where); return true; }
ガーベージコレクションは、古いセッション情報を削除するために定期的に呼び出されます。 セッション情報を長く残しておきたい場合には、MAX_LIFETIMEを長く、すぐに消したい場合には、MAX_LIFETIMEを短く設定します。
なお、ソース内で使用されているSC_Queryは、SQL文を簡略化して実行するためのクラスです、開発環境に合わせたものに置き換えて利用してみてください。

EUC-JPフォームの文字化け

開発中、文字化けに出くわしました。
以下のようなシンプルなフォーム(POST情報が少ない)でのみ再現します。
<html lang="ja">
<head>
<meta http-equiv=Content-Type content="text/html; charset=EUC-JP">
</head>

<body>

<table>
<form name="form1" action="test.php" method="POST">
<tr>
	<td>文字を入力してください</td>
	<td><input type="text" name="string"></td>
	<td><input type="submit" value="送信"></td>
</tr>
</form>
</table>

<?php
    if(isset($_POST['string'])) {
        print("string is " . $_POST['string']);
    }
?>

</body>
</html>



このフォームに「属性」をいれて送信すると、PHP側の出力で

string is °

と、「属性」をEUC-JPと認識せず文字化けを起こしました。
「属性」をEUC-JP文字コードで調べたところ、

C2 B0 C0 AD

となります。
またSHIFT-JISのコード範囲は、

1バイト目 : 0x81 - 0x9F または 0xE0 - 0xFC
2バイト目 : 0x40 - 0x7E または 0x80 - 0xFC

となっており、2バイト目にSHIFT-JISのコードの条件があてはまっているのがわかります。
これが文字化けを起こしていた原因でした。

そして、対策方法ですが、hiddenを利用します。

<input type="hidden" name="dummy" value="美乳">


valueに入っている「美乳」、これがポイントです。

これも同じように16進数に変換すると、

C8 FE C6 FD

となり、1バイト目、2バイト目共に、SHIFT-JISコードに当てはまらないので、POST時に確実にEUC-JPと認識させ、文字化けを解消できました。

PEARでユーザー認証 (Auth_HTTP)

ユーザ認証を行う方法は色々とありますが、今回、PEARのAuth_HTTPを用いたユーザ認証方法を紹介します。

使い方はいたって簡単です。

<!--
"pgsql://test_user:password@test.lockon.co.jp/test_db",	// 接続情報
	'table'=>"dtb_member",    // テーブル名 
	'usernamecol'=>"login_id",  // ユーザ名のカラム
	'passwordcol'=>"password", // パスワードのカラム
	'cryptType'=>"MD5",	    // パスワードの暗号化形式(暗号化なしのときはnone)
	'db_fields'=>"*",        // その他のカラムを取得する場合にはカラムを指定する
);
$objAuthHttp = new Auth_HTTP("DB", $arrDbConn); // オブジェクト生成
$objAuthHttp->setRealm('User Realm');     // 領域 (realm) 名
$objAuthHttp->setCancelText('接続エラー');  // 認証失敗時、表示されるメッセージ
$objAuthHttp->start();	 // 認証開始
// 認証チェック(成功:TRUE 失敗:FALSE)
if($objAuthHttp->getAuth())				
{
	echo "認証成功";
	echo "ようこそ " . $objAuthHttp->getAuthData('name') . "さん"; // 取得したデータを使用する
}
?>
-->

これだけでログイン認証してくれます。
Auth_HTTPでは、DBに保存しているユーザー情報を用いて認証を行うことが可能なため、
.htpasswordなど別ファイルを用意する必要がなくなり、ユーザ情報の一元管理が可能となります。

CAPTCHA(画像)認証

ajaxの勉強中に作ったコネタです。

CAPTCHA認証を非同期で行うというものです。
掲示板などのスパムなどにお困りの方は良かったらお試し下さい。

captcha.gif

動作確認はこちら


■以下の4つのソースから構成されています。


・メインHTML
auth.html


・認証処理を行い、結果を返すPHP
result.php


・認証コード画像を生成するPHP
create_image.php


・非同期通信を行う為のJS
auth.js


▼---ZIP圧縮版-----------------------
auth.zip


かなり簡易版なので改造していろいろ試してみてください^^。

PHPで円グラフを作ろう

ここでは、EC-CUBEで利用しているクラスを使った円グラフの出力方法を案内したいと思います。
今回は以下のようなグラフを出力するためのクラスの使い方を説明いたします。
graph.png

実際のソースは以下のよう記述します。
// クラスファイルを読み込みます。
require_once("./SC_GraphPie.php");

// 出力背景のサイズと円の出力位置を指定して呼び出します。
$objGraphPie = new SC_GraphPie(400, 250, 80, 70);

// データを用意します。
$arrList = array(
	'練習A' => 11,
	'練習B' => 32,
	'練習C' => 48
);
		
// データをセットする
$objGraphPie->setData($arrList);
// 凡例をセットする
$objGraphPie->setLegend(array_keys($arrList));

// 円グラフを作成します。
$objGraphPie->drawGraph();

// できあがった円グラフを表示します。
$objGraphPie->outputGraph();
上記で利用したクラスはEC-CUBEの配布ソースに含まれていますので、
興味のある方は、ご自由にダウンロードしてみてください。

PEARを用いたサービス監視

あるネットワーク内のサーバーを、別のネットワークからサービス監視する場合の方法のひとつとして、
PEARのHTTP_Requestを用いた方法があります。


▽インストール


# pear install HTTP_Request

▽見本


<?php

require_once("HTTP/Request.php");

define("ADMIN_EMAIL", "管理者メールアドレス");

define("WATCH_URL", "http://www.lockon.co.jp");

$objReq =& new HTTP_Request(); //オブジェクト生成

$objReq->setUrl(WATCH_URL); //監視対象のURL

$objReq->sendRequest(); //リクエスト送信

$code = $objReq->getResponseCode(); //レスポンスコード取得

//接続不可

if(!$code) {

$err_subject = WATCH_URL . "からの応答を確認できません。";

} else {

//サーバーエラー

if(ereg("^5", $code)) {

$err_subject = WATCH_URL . "のサーバーエラーが発生しました。";

$err_body = "Code:" . $code;

}

//ユーザーエラー

if(ereg("^4", $code)) {

$err_subject = WATCH_URL . "が見つからない、または不正なリクエストです。";

$err_body = "Code:" . $code;

}

}

if($err_subject) {

mb_send_mail(ADMIN_EMAIL, $err_subject, $err_body);
}

?>



また、phpにopensslが組み込まれていない場合、httpsサイトからのレスポンスが帰ってきませんので、phpコンパイル時に、以下のコマンドでサポートさせます。


# ./configure --with-openssl=(openssl_dir)
# php -m | grep openssl
openssl

HTTP_Requestを使用することにより、ユーザーがブラウザでリクエストを送信する操作を、PHPスクリプト上からでも可能にします。

サービス監視といえば代表的なnagiosなどが挙げられますが,小規模なシステムの場合であれば、上記のような監視スクリプトを用いて、あとはcronなどで定期的に実行させるだけでも、十分事足りるかと思います。

PHP + Flash + Javascript連携

最近はAjaxが何かと話題になっておりますが、 動的なコンテンツと言えばFlashでしょという方も多いと思います。

そこで今回はFlashとPHPの連携(+javascript)のやり方を紹介したいと思います。

簡単な例としてflash側でメールアドレスを入力し、phpでメールを送信し、
javascriptで送信完了メッセージを表示したいと思います。


まずFlashの1フレーム目に以下のActionScriptを記述します。 fla.gif
//メール送信関数
function sendMail() {
	// PHPに渡すデータを設定
	var sl = new LoadVars();
	// email入力エリアのテキストをemailという名前でPHPにPOSTする
	sl.email = email.text
	sl.sendAndLoad("mail.php?rn=" + Math.random(), sl);
	
	// PHPからデータを受信したときの処理
	sl.onLoad = function() {
		// 完了メッセージ表示
		showMessage();
	}
}

//Javascript呼び出し関数
function showMessage() {
 	//JavaScript側の関数を呼び出す
	getURL("javascript:showMessage('メールを送信しました。')");
}


あとはテキスト入力部分をemailという名前で作成、
ボタンを一つ用意してクリックしたらsendMail()を呼び出すようにします。
on(press) {
	sendMail();
}
あとはtie_php.fla として保存し、パブリッシュを行い、tie_php.html 、tie_php.swfを作成します。
次にメール送信用のmail.phpを作成します。
FlashのLoadVarsを使用して送信されたデータはPOSTで受け取ることができます。
<?php

// Flash側で入力されたemailデータを受け取ります。	
$email = $_POST['email'];
$subject = "flash + javascript + PHP";
$body = "flash + javascript + PHP テスト";

// メールを送信
$result = mb_send_mail( $email, $subject, $body);

//FLASHへ値を送信(成功 = 0, 失敗 = 1)
echo "trans=". $result;

?>
最後にパブリッシュされた tie_php.html にFlash側のactionscriptで定義した
showMessage関数をJavascriptで記述します。
<script type="text/javascript">
	//文字列表示
	function showMessage(message) {
		if(!window.confirm(message)){
			return;
		}
	}
</script>
tie_php.htmlをブラウザから実行して見てください。 入力したメールアドレスに正しく送信され、完了メッセージが表示されれば成功です。 tie_php.jpg
動作確認はこちらから

▼---上記説明のファイルのダウンロード----
tie_php.zip

Flash8をご利用の方ならより高度なjavascriptとの連携も可能です。
第35回 FlashムービーとJavaScriptを連携させてみよう:ITpro

PHPで円グラフを作ろう(2)

大反響を呼んだ「PHPで円グラフを作ろう」の第2弾です!! ここでは、細かい描画処理を追っていきたいと思います。 円柱を描画するのに、まずは影にあたる部分を描画します。 こちらは、灰色の円を書いているだけです。
// 影の描画
if($objGraphPie->shade_on) {
	$objGraphPie->drawShade();
}
1.png

続きまして、影感を出すためにちょっとずらして、 円柱の側面を描画します。imagearcという弓なりの線を描画できる関数を利用しています。 こちら若干癖があるので、数値を補正しています。
// 色数の取得
$c_max = count($objGraphPie->arrColor);
$dc_max = count($objGraphPie->arrDarkColor);

// 側面の描画		
for ($i = ($y + $z - 1); $i >= $y; $i--) {
	$start = 0;
	for($j = 0; $j < $rd_max; $j++) {
		// 角度が0度以上の場合のみ側面を描画する。
		if($arrRad[$j] > 0) {
			$end = $start + $arrRad[$j];
			if($start == 0 && $end == 360) {
				// -90~270で指定すると円が描画できないので0~360に指定
				imagearc($objGraphPie->image, $x, $i, $w, $h, 0, 360, $objGraphPie->arrDarkColor[($j % $dc_max)]);
			} else {
				// -90°は12時の位置から開始するように補正している
				imagearc($objGraphPie->image, $x, $i, $w, $h, $start - 90, $end - 90, $objGraphPie->arrDarkColor[($j % $dc_max)]);	
			}			
			$start = $end;
		}
	}
}
2.png

次に、円柱の蓋の部分です。実はこの蓋の部分だけで、 円グラフとしては成立するのですが。。ここでは、imagefilledarcというピザのような形を描画できる 関数を用います。
// 底面の描画
imagearc($objGraphPie->image, $x, $y + $z, $w, $h, 0, 180 , $objGraphPie->flame_color);

// 上面の描画
$start = 0;
for($i = 0; $i < $rd_max; $i++) {
	$end = $start + $arrRad[$i];
	if($start == 0 && $end == 360) {
		// -90~270で指定すると円が描画できないので0~360に指定
		imagefilledarc($objGraphPie->image, $x, $y, $w, $h, 0, 360, $objGraphPie->arrColor[($i % $c_max)], IMG_ARC_PIE);			
	} else {
		// -90°は12時の位置から開始するように補正している。		
		imagefilledarc($objGraphPie->image, $x, $y, $w, $h, $start - 90, $end - 90, $objGraphPie->arrColor[($i % $c_max)], IMG_ARC_PIE);
	}
	$start = $end;
}
3.png

あとは、縁取りして完成にもっていきます。
// 上面の縁取り
$start = 0;
for($i = 0; $i < $rd_max; $i++) {
	$end = $start + $arrRad[$i];
	if($start == 0 && $end == 360) {
		// -90~270で指定すると円が描画できないので0~360に指定
		imagearc($objGraphPie->image, $x, $y, $w, $h, 0, 360 , $objGraphPie->flame_color);
	}
	// -90°は12時の位置から開始するように補正している。
	imagefilledarc($objGraphPie->image, $x, $y, $w, $h, $start - 90, $end - 90, $objGraphPie->flame_color, IMG_ARC_EDGED|IMG_ARC_NOFILL);
	$start = $end;
}

// 側面の縁取り
imageline($objGraphPie->image, $x + ($w / 2), $y, $x + ($w / 2), $y + $z, $objGraphPie->flame_color);
imageline($objGraphPie->image, $x - ($w / 2), $y, $x - ($w / 2), $y + $z, $objGraphPie->flame_color);
4.png

業務ではあまり利用することはないかもしれませんが、いろいろ試してみると面白いです。
上記で利用したクラスはEC-CUBEの配布ソースに含まれていますので、興味のある方は、ご自由にダウンロードしてみてください。

FPDFでMultiCellを用いて表作成

PHPでPDF出力するには、PDFLib、FPDF辺りが有名ですが(というかこの2つしかない?)

FPDFはフリーで使えるので、これを使って帳票を作成してみました。

チュートリアルも付いていたので、とっつきやすくて非常に良いです。

 

しかし、表を作成するときに、自動で改行してくれないみたいです。。。

 

そこでちょっと改造してみました。

 

まず、MultiCell関数に改行用の引数を1つ追加します。(Cell関数と同じものです。)

function MultiCell($w,$h,$txt,$border=0,$align='J',$fill=0,$ln = 2)

 

次に、追加した引数を関数内のCellに渡してあげます。

$this->Cell($w,$h,substr($s,$j,$i-$j),$b,$ln,$align,$fill);

 

これで改行はしてくれるようになるのですが、表の高さがまだ広がっていないので、次は、表の高さを調整します。

function MultiCell($w,$h,$txt,$border=0,$align='J',$fill=0, $ln = 2)
{
 //Output text with automatic or explicit line breaks
 $cw=&$this->CurrentFont['cw'];
--- 中略 ---
 $sep=-1;
 $i=0;
 $j=0;
 $l=0;
 $ns=0;
 $nl=1;
 $this->rise_h = 0;  // 高さ計算用

 while($i<$nb)
 {
  //Get next character
  $c=$s{$i};
  if($c=="\n")
  {
   //Explicit line break
   if($this->ws>0)
   {
    $this->ws=0;
    $this->_out('0 Tw');
   }
   $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
   $i++;
   $sep=-1;
   $j=$i;
   $l=0;
   $ns=0;
   $nl++;
   $this->rise_h += $h;  // 増加分の高さを計算
   if($border && $nl==2)
    $b=$b2;
   continue;
  }
  if($c==' ')
  {
   $sep=$i;
   $ls=$l;
   $ns++;
  }
  $l+=$cw[$c];
  if($l>$wmax)
  {
   //Automatic line break
   if($sep==-1)
   {
    if($i==$j)
     $i++;
    if($this->ws>0)
    {
     $this->ws=0;
     $this->_out('0 Tw');
    }
    $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
   }
   else
   {
    if($align=='J')
    {
     $this->ws=($ns>1) ? ($wmax-$ls)/1000*$this->FontSize/($ns-1) : 0;
     $this->_out(sprintf('%.3f Tw',$this->ws*$this->k));
    }
    $this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill);
    $i=$sep+1;
   }
   $this->rise_h += $h;  // 増加分の高さを計算
   $sep=-1;
   $j=$i;
   $l=0;
   $ns=0;
   $nl++;
   if($border && $nl==2)
    $b=$b2;
  }
  else
   $i++;
 }
 //Last chunk
 if($this->ws>0)
 {
  $this->ws=0;
  $this->_out('0 Tw');
 }
 if($border && strpos($border,'B')!==false)
  $b.='B';
 $this->Cell($w,$h,substr($s,$j,$i-$j),$b,$ln,$align,$fill);
 $this->rise_h += $h;  // 増加分の高さを計算
 // 改行なし設定かつ、高さが規定の高さ以上であればY軸を設定しなおす。
 if($ln == 0 and $h < $this->rise_h){
  $this->y = $this->y - $this->rise_h + $h;
 }

 //$this->x=$this->lMargin; ←これは特に必要がないためコメントアウト
}

 

最後に、表作成関数を書き換えてあげます。

//Simple table
function BasicTable($header,$data)
{
	//Header
	foreach($header as $col)
		$this->Cell(40,7,$col,1);
	
	$this->Ln();
	//Data
	foreach($data as $row)
	{
		$h = 7;
		foreach($row as $col){
			this->MultiCell(40,$h,$col,1, 'L', 0, 0); // MultiCellに替える
			$h = $this->rise_h; // 高さを変更する
		}
		$this->Ln();
	}
}
これで完成です。

簡単な帳票であればこれで問題ないと思います。

fopenとPEAR::DBのタイムアウト処理

PHPで、他のサーバに接続するとき、タイムアウトまわりで困ることが時々あったので、それについて。

▼ fopen

タイムアウト秒数を指定するオプションは無い様子ですので、このようなコードを書きました
define("TIMEOUT_SECOND", 1);
$fp = @fsockopen("yahoo.co.jp", 80, $errno, $errstr, TIMEOUT_SECOND);
if ( $fp === false ) {
    // つながらない
} else {
    // つながったっぽい 
    fwrite($fp, "GET /index.html HTTP/1.0\r\n\r\n");
    // setting response timeout
    stream_set_blocking( $fp, TRUE );
    stream_set_timeout($fp, TIMEOUT_SECOND);
    
    $info = stream_get_meta_data($fp);
    while( !feof($fp) && !$info['timed_out'] ){
        $txt .= fgets($fp, 2000);
        $info = stream_get_meta_data($fp);
    }
    fclose($fp);
}

fsokopenでタイムアウト秒数を指定して繋げばいいのですが、それだけだと、繋がった後にレスポンスが無いとか、遅いとかいう場合には、相変わらず待ち続けるので一工夫入ってます。

但し、このコードでは、読み込み途中であろうが、指定秒数が経つと強制的に切断されますので、「読み込みが続いている場合は切りたくない」というときは、ループ前後を比較するなどする処理が、もうひとつ必要になります。


▼ PEAR::DB

メンテナンスモードにはいっているPEAR::DBですが、ロックオンでは、これで事足りちゃっているので、未だに使うことが多いです。

postgreSQLに接続する場合、たぶんマニュアルには載っていないと思うのですが、DSNオプションでタイムアウト秒数を指定することができます。

$dsn = 'pgsql://user:pass@127.0.0.1/dbname?connect_timeout=3';

MDB2も、未検証ですが、それっぽいコードがあるので、同様にいけるんじゃないでしょうか。

Services_LivedoorReader作ってみた

こんにちは、あだちです。


RSSリーダーといえばLivedoorReaderを使ってる方も多いかと思います。
少し前までは若干動作がもっさりしてる印象があったんですが、
最近久々にログインしたところ、だいぶさくさく動くようになっててびっくり。
今ではメインのRSSリーダーとしてお世話になっています。


というわけで(?)Services_LivedoorReaderというライブラリを作ってみました。


インストールは
pear install http://www.lockon.co.jp/blog/Services_LivedoorReader-0.1.0.tgz
で。


Services_JSONとHTTP_Clientが必要です。


下に書いてあるAPIにはだいたい対応してるのかなーと思います。


livedoor ReaderのAPI一覧
http://d.hatena.ne.jp/nTeTs/20060422/1145637483


ちなみに公式の2つのAPIには未対応です(汗


サンプルコードはこんな感じで。
(未読エントリのタイトル一覧を出力)

require_once 'Services/LivedoorReader.php';
$LDR = new Services_LivedoorReader('username', 'password');
try {
$feeds = $LDR->subs(array('unread' => '1'));
} catch (Services_LivedoorReader_Exception $e) {
die($e->__toString());
}
foreach ($feeds as $feed) {
try {
$entries = $LDR->unread(
array('subscribe_id' => $feed->subscribe_id)
);
} catch (Services_LivedoorReader_Exception $e) {
die($e->__toString());
}
foreach ($entries->items as $entry) {
print $entry->title . "\n";
}
}
/api/feed/unsubscribeなどは、
$LDR->feed_unsubscribe(array('subscribe_id' => $subscribe_id));
という風に呼び出してください。


対応してないAPIや動かないAPIがあれば
APIディレクトリ以下に放りこめば拡張できるようにしてます。
(API/Default.phpやAPI/Notify.phpを参考にしてみてください)


PHP5は初めて触ったので例外処理とかいろいろまちがってるかもしれません。
ツッコミどころがあれば指摘していただけるとありがたいです。

PHPでモバイルGPSを利用する

こんにちは。
ロックオン技術開発部の中西です。
今回から開発ブログのリレーに加わることになりました。
あまり役に立たない情報ばかりになるかもしれませんが
よろしくお願いいたします。

何年か前から携帯電話にもGPSGlobal Positioning System)が搭載されるようになりました。
最近はデータ通信の定額料金プランの浸透と相まって気軽にGPSが使えるようになり、GPSを用いたビジネスなども展開されています。
例えば以下のようなものです。

『モバイルワンテクノロジー、GPS機能を使う携帯向け集客支援ASPGcam」』

そこで今回はPHPでのGPSを用いた位置情報の取得方法について記述します。
モバイルGPSの仕様はキャリアごとに異なります。
勝手ながら僕はauユーザーなので今回はauの携帯電話を利用して話を進めます。

au
のモバイルGPSGPS One(参考:ITmedia)と呼ばれる測位方式で
測位の際に端末と基地局との通信を行います。
基地局との通信にはGETメソッドが用いられ、GETメソッドでパラメーターをサーバーに送ると位置情報が返ってくる、という流れになっています。

サーバーへのリクエストは以下のような形式で行います。
URL
のところに受け取った位置情報を処理するプログラムを置いてください。
パラメータは測地系や返ってくるデータの形式などを表しています。


device:gpsone?url=
(任意のURL&ver=1&datum=0&unit=1&acry=0&number=0"

帰ってくるデータは経度、緯度などの他に精度や高度誤差の情報も含まれているようです。(参考:しいしせねっと

さて、今回はデモンストレーションとしてGPSで取得した位置情報をCSVに記録するプログラムを作成しました。
基本的には受け取った位置情報データをCSVに書き出すという、とてもシンプルなプログラムです。
何もしないと一回こっきりしか測位してくれないので
モバイルサイト用のタグを使って定期的にURLにアクセスしてGETして、測位を行うようにしています。
端末側で設定をしておかないと測位を試みるたびに許可を求められるので
鬱陶しい場合は確認しない設定にしてください。

<?php
    ini_set("mbstring.script_encoding","utf-8");
    ini_set("default_charset","utf-8");
   
    //
緯度を取得する
    $lon = $_GET["lon"];
    $lon = str_replace("+","",$lon);
   
    //
経度を取得する
    $lat = $_GET["lat"];
    $lat = str_replace("+","",$lat);
   
    //GPS
の時間を取得する
    $time = $_GET["time"];
    $year = substr($time,0,4);
    $mon = substr($time,4,2);
    $day = substr($time,6,2);
    $hour = substr($time,8,2);
    $min = substr($time,10,2);
    $sec = substr($time,12,2);
    $time = $year."/".$mon."/".$day.",".$hour.":".$min.":".$sec;
   
    $csv[] = $time.",".$lat.",".$lon.","."\n";
   
        $fp = fopen("log.csv","a+");
    foreach($csv as $line){
            fputs($fp,$line);
        }
    fclose($fp);
?>
<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head><title>GPS
送信</title></head>
 <wml:card ontimer="device:gpsone?url=
(任意のURL&ver=1&datum=0&unit=1&acry=0&number=0">
  <wml:timer value="150" />
  <p align="center">
    <font color='red'>15
</font>ごとに現在の位置を測定します。
  </p>
 </wml:card>
</html>


これだけだと単に数字を記録してるだけで全く面白くないので
本当にGPSが正しいのか検証しましょう。
geocoding.jp
では経度、緯度から場所を検索できます。
きちんと計測できていれば経度、緯度からGPSによる観測を行った位置の地図が表示されるはずですが、いかがでしょうか。

ためしにロックオン本社で計測したところです。
50M
ほどずれていますが、加速度センサのないモバイルに搭載されているGPSではこんなもんかな、と思います。

GoogleMapsAPI等の地図サービスでは
経度、緯度を利用して様々な処理を行えるのでGPSを利用するとWebアプリの色々な可能性が見えてくるように思います。

PHPでモバイルGPSを利用する-2

こんにちは。
個人的な話で恐縮ですが、最近ケータイをDoCoMoのP904iに買い換えました。
904シリーズはGPSが標準搭載です。

こりゃなかにしの記事もあったことですし、DoCoMoのケータイ版も試してみなけりゃ漢がすたるってなもんです。
ただ使ってみたいだけですが(^^;)


今回は、携帯からURLにアクセスして、コメントを送信すると、位置情報とともにDBへ格納してみます。
その情報の最終10件をgoogleMapsに表示してみるというのをやってみたいとおもいます。

チャリンコで旅してるとき(?)に行った場所の情報をポストしておき、それをリアルタイムで自分のblogに表示させておく、なーんて使い方もできるかもしれません。


さて、GPS対応のDoCoMoの携帯から、以下のURLへアクセスしてください。
http://www.lockon.co.jp/blog/source/docomoGmaps/gps.php
地図ページで送信した位置とコメントがマッピングされていれば成功です。


簡単に解説します。


まずデータの格納領域を用意します。
前回はCSVにはき出していましたが、今回はPostgreSQLに書き出してみたいと思います。
せっかく位置情報を扱うのでPostGISをインストールしたいところですが用意が面倒なので、ここでは幾何データ型を利用してみます。
これで応用編も視野に入れていますが、本当にあるかはわかりません。w
create table dtb_geo (
     id     serial primary key
    ,geo    point NOT NULL
    ,comment text
    ,access_time timestamp NOT NULL DEFAULT NOW()
);

これで、DBの用意は完了です。

次に携帯から呼び出すページの作成です。
流れをおいやすくするためにエラー処理は最小限にしています。


●gps.php
<?php
require_once("DB.php");

if ( $_GET["mode"]=='confirm' ){
	
	if ( isset($_GET["lat"]) && isset($_GET["lon"]) ){
		// connect DB 
		$dsn = "pgsql://user:pass@127.0.0.1/dbname";
		$conn = DB::connect($dsn);
		
		// Get location
		$lat = str_replace("+", "", $_GET["lat"]);
		$lon = str_replace("+", "", $_GET["lon"]);
		$lat = split("\.", $lat); $lat[2] = $lat[2].".".$lat[3];
		$lon = split("\.", $lon); $lon[2] = $lon[2].".".$lon[3];
		$lat = $lat[0] + $lat[1]/60 + $lat[2]/3600;
		$lon = $lon[0] + $lon[1]/60 + $lon[2]/3600;
		
		$sql = "INSERT INTO dtb_geo (comment,geo) VALUES (?, point(?,?) )";
		$result = $conn->query($sql, array($_GET["comment"], $lat, $lon) );
		
		if ( $conn->isError($result) ){
			$body = "登録に失敗しました";
		} else {
			$body = "緯度:${lat}、経度:${lon}を登録しますた";
		}
		
		// write XML
		$sql = "SELECT geo[0] as lat, geo[1] as lon, comment, to_char(access_time, 'YYYY/MM/DD HH24:MI') as access_time FROM dtb_geo ORDER BY access_time DESC LIMIT 10";
		$result = $conn->getAll($sql, DB_FETCHMODE_ASSOC);
		
		$fp = fopen("marker.xml", "w");
		fwrite($fp, "<markers>\n");
		for ( $i=0; $i<count($result); $i++){
			$no = $i + 1;
			$lat = $result[$i]["lat"];
			$lon = $result[$i]["lon"];
			$comment = mb_convert_encoding($result[$i]["comment"], "UTF-8", "EUC-JP");
			$access_time = $result[$i]["access_time"];
			fwrite($fp, "<marker lon='${lon}' lat='${lat}' no='${no}' comment='${comment}' time='${access_time}' />\n");
		}
		fwrite($fp, "</markers>\n");
		fclose($fp);
	} else {
		$body = "位置情報がありません";	
	}
} else {
	$body = '<form method="get" action="./gps.php" lcs>
				<input type="hidden" name="mode" value="confirm" />
				<input type="text" name="comment" value="" />
				<input type="submit" name="test" value="送信" />
			</form>';
}
?>
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.1//EN' 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja">
<head>
<meta http-equiv="content-type" content="application/xhtml+xml; charset=EUC-JP" />
<title>GPSテスト</title>
</head>
<body>
<?php echo $body ?>
</body>

ポイントは
<form method="get" action="./gps.php" lcs>
です。formタグにlcs属性をつけてやることで確認ダイアログが表示されるようになり, 位置情報を送出するようになります。
Aタグにつける方法もあります。
あとは、渡されてきた位置情報を利用するだけです。,

さて、これで、登録した情報を格納し登録できるようになりましたので、あとは表示です。

googleMapsのキーは取得しておいてください。残り必要なファイルは2つです。
まずは、画像を表示させるための、htmlです。


●sample.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=euc-jp" />

<title>足あと</title>
<script src="http://maps.google.com/maps?file=api&v=1&key=xxxxxxx" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" src="Gmaps.js"></script>
</head>
<body  onload="init()" >

<div id="map" style="width:800px; height:600px; white-space: nowrap;"></div>

</body>


つぎにXMLファイルを読み込んでマッピングするjsファイルです。


●Gmaps.js
var map;
var markers;
    function loadData(entryURL){
        var request = GXmlHttp.create();
        var filename = "marker.xml";
		filename += "?rand=" + Math.random(); //add random
        request.open("GET", filename, true);
        request.send(null);
        request.onreadystatechange = function() {
            if (request.readyState == 4 && request.status == 200) {
                var xmlDoc = request.responseXML;
                markers = xmlDoc.documentElement.getElementsByTagName("marker");
                plotMarkers();
            }
        }
    }

    function plotMarkers(){
        if(markers != null && markers.length > 0){
			map.centerAndZoom(new GPoint(parseFloat(markers[0].getAttribute("lon")),parseFloat(markers[0].getAttribute("lat"))),2);
			for (var i = 0; i < markers.length; i++) {
				var point = new GPoint(parseFloat(markers[i].getAttribute("lon")),
				parseFloat(markers[i].getAttribute("lat")));
				var iconFlag = 0;
        		if ( markers[i].getAttribute("no") == "1" ){
					map.centerAndZoom(new GPoint(parseFloat(markers[i].getAttribute("lon")),parseFloat(markers[i].getAttribute("lat"))),2);
					iconFlag = 1;
				}
                var title = markers[i].getAttribute("no") + '<br />' + markers[i].getAttribute("comment") + "<br />" + markers[i].getAttribute("time")
				var marker = createMarker(point, i, title, iconFlag);
				map.addOverlay(marker);
			}
			
        }
    }
    
    function createMarker(point, index, title, iconFlag) {
    	
        var icon = new GIcon();
        var icon_uri = "http://labs.google.com/ridefinder/images/mm_20_blue.png";
        
        if ( iconFlag == 1 ){
            icon_uri = 'http://labs.google.com/ridefinder/images/mm_20_red.png'
        }
        
        icon.image = icon_uri;
        icon.shadow = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
        icon.iconSize = new GSize(12, 20);
        icon.shadowSize = new GSize(22, 20);
        icon.iconAnchor = new GPoint(6, 20);
        icon.infoWindowAnchor = new GPoint(5, 1);
        var marker = new GMarker(point, icon);
          GEvent.addListener(marker, "click", function() {
            marker.openInfoWindowHtml(title);
        });

        return marker;
    }

    function init(){
    	
        loadData();
        map = new GMap(document.getElementById("map"));
        
		map.addControl(new GLargeMapControl());
        map.addControl(new GMapTypeControl());
        var types = map.getMapTypes() ;
        map.setMapType(types[0]);
    }




かなり即席なので、間違っている点があるかもしれませんが、その際は、ぜひご指摘ください。
次回は、せっかくDBに放り込んでいるので、そのデータを利用して、ごにょごにょやってみたいと思います。



【参考】
作ろうiモードコンテンツ-GPS
Web屋のネタ帳 - データベース上の位置情報を効率的に検索する方法(PostgreSQL編)

ローカル環境の Apache で複数のサイトを平行して開発する

EC-CUBE 開発チームの大河内です.

僕は普段, FreeBSD や Mac OS X といった PC-UNIX をクライアントにして開発をしています. 自機がサーバーになってしまうので, EC-CUBE などの開発は非常に楽なのですが, PHP4 と, PHP5 の動作確認を行ったり, 複数の EC-CUBE を同時にテストしようと思うと, 少々工夫が必要です.

DNS サーバーを自前で管理できれば, サブドメインを作って, ネームベースの VirtualHost で複数のドメインを動作させることができるので楽なのですが, 現在の開発環境では, localhost のみで何とかしなければならないのです.

そこで, 以下の方法を考えました.

  • Apache は ネームベースの VirtualHost ではなく, 複数のポートで動かす.
  • PHP4 と PHP5 の Apache module をインストールしておき, httpd.conf の簡単な修正で変更できるようにする.

設定方法

  1. Apache と PHP5 をインストール.

    まずは特に特殊な設定は不要なので, ports から入れてしまいました.
    Linux なら rpm や apt-get でも大丈夫だと思います.

    # portinstall -Ccr www/apache20
    # portinstall -Ccr lang/php5
    # portinstall -Ccr lang/php5-extensions
  2. PHP4 のインストール.

    PHP5 と PHP4 を簡単に切り変えるために PHP4 もインストールします.
    単純には共存できないため, こちらはソースからビルドします.

    $ tar xvjf php-4.4.7.tar.bz2
    $ cd php-4.4.7
    $ './configure' \
    '--with-layout=GNU' \
    '--with-config-file-scan-dir=/usr/local/etc/php' \
    '--with-apxs2=/usr/local/sbin/apxs' \
    '--with-regex=php' \
    '--enable-zend-multibyte' \
    '--prefix=/usr/local' \
    '--without-pear' \
    '--disable-cgi' \
    '--disable-cli' \
    '--with-gd' \
    '--enable-gd-native-ttf' \
    '--enable-gd-jis-conv' \
    '--with-iconv' \
    '--enable-mbstring' \
    '--with-mysql' \
    '--with-pgsql=/usr/local/pgsql/8.2.4' \
    '--with-zlib-dir=/usr/local' \
    '--with-jpeg-dir=/usr/local' \
    '--with-png-dir=/usr/local'
    $ make
    $ sudo make install
    

    configure オプションは各環境に合わせて調節して下さい.
    僕の場合は, Apache2 モジュール版の PHP4 のみをインストールするようにし, PostgreSQL は幾つかのバージョンを同居させているので, 8.2.4 のライブラリを読むようにしました

  3. PHP の共存設定

    PHP4 と PHP5 はインストールできたので, どちらのモジュールを有効にするかは, httpd.conf で行います.
    下記のように, LoadModule 行のどちらかをコメントアウトしてやれば良いです.(Apache2.0系の場合)

    #LoadModule php4_module        libexec/apache2/libphp4.so
    LoadModule php5_module        libexec/apache2/libphp5.so
    

    PHP4 を有効にしたければ, php5_module の行をコメントアウトして, Apache を再起動しましょう.

  4. 複数ホスト同居させる

    複数のホストを同居させるには, VirtualHost を使えば良いのですが, localhost のみではサブドメインが使えないため, Apache を複数のポートで立ち上げて対応するようにします.
    僕は, httpd.conf に VirtualHost の設定をたくさん書くのはいやなので, httpd.conf の最終行付近に下記のような設定を追加しました.

    Include etc/apache2/Includes/*.conf

    こうすると, $PREFIX/etc/apache2/Includes 以下の *.conf ファイルは, すべて httpd.conf が 読み込んでくれます.

    あとは, ホストごとに, *.conf ファイルを作っていけば良いです.

    ##--------------------------------------------------------------------
    # EC-CUBE module-update 開発用
    ##--------------------------------------------------------------------
    Listen 15000
    NameVirtualHost *:15000
    <VirtualHost *:15000>
        ServerAdmin     root@loop-az.jp
        DocumentRoot    /home/foo-user/workspace/eccube-branches/feature-module-update/html
        DirectoryIndex  index.php
        ServerName       127.0.0.1
        ErrorLog        /var/log/15000-httpd-error.log
        CustomLog       /var/log/15000-httpd-access.log combined
        AddType application/x-httpd-php .php
        php_value include_path .:/usr/local/share/pear
        <Directory /home/foo-user/workspace/eccube-branches/feature-module-update/html>
            AllowOverride All
            Options All
            <Limit GET POST OPTIONS PROPFIND>
                Order allow,deny
                Allow from all
            </Limit>
            <LimitExcept GET POST OPTIONS PROPFIND>
                Order deny,allow
                Deny from all
            </LimitExcept>
        </Directory>
    </VirtualHost>
    ##--------------------------------------------------------------------
    # EC-CUBE comu-utf8 用
    ##--------------------------------------------------------------------
    Listen 16000
    NameVirtualHost *:16000
    <VirtualHost *:16000>
        ServerAdmin     root@example.jp
        DocumentRoot    /home/foo-user/workspace/eccube-branches/comu-utf8/html
        DirectoryIndex  index.php
        ServerName       127.0.0.1
        ErrorLog        /var/log/16000-httpd-error.log
        CustomLog       /var/log/16000-httpd-access.log combined
        AddType application/x-httpd-php .php
        php_value include_path .:/usr/local/share/pear
        <Directory /home/foo-user/workspace/eccube-branches/comu-utf8/html>
            AllowOverride All
            Options All
            <Limit GET POST OPTIONS PROPFIND>
                Order allow,deny
                Allow from all
            </Limit>
            <LimitExcept GET POST OPTIONS PROPFIND>
                Order deny,allow
                Deny from all
            </LimitExcept>
        </Directory>
    </VirtualHost>
    

    ポイントは, VirtualHost ごとに, Listen するポートを分けることです. こうすることで, 定義したポートごとに VirtualHost の作成ができます.
    Listen させるポートは, 1024 番以降の空いているポートを使用するようにしましょう.
    ドメイン名は localhost のみなので, NameVirtualHost と, VirtualHost ディレクティブは, *:ポート番号 と指定すると良いです.
    上記の設定だと, ブラウザから, http://localhost:15000 及び, http://localhost:16000 にアクセスすることで, 各 VirtualHost へのアクセスが確認できると思います.

上記サンプルは, あくまでもローカル環境でのテスト用なので, AllowOverride All なんてしてありますが, VPS 等の外部サーバーでテストする場合は, 的確な設定をした方が良いでしょう...

EC-CUBEのDB接続をmdb2に変更

ITproの記事にもありましたが、PHPでDB接続にPEAR::DBを使っている場合にはPEAR::MDB2への移行を推奨するとのことです。

http://itpro.nikkeibp.co.jp/article/COLUMN/20070827/280396/

 

現在、EC-CUBEではDB接続にPEAR::DBを用いていますので、移行できるか試して見ました。

まず、MDB2のパッケージをダウンロードし、解凍したものを data/module 配下に置きます。

今回はPostgreSQLで以降の検証を行いたいと思います。

http://pear.php.net/package/MDB2/download/2.4.0 http://pear.php.net/package/MDB2_Driver_pgsql/download

 

次にMDB2を用いてDB接続を行うようにプログラムを改造します。

 

変更ファイル:data/class/SC_DbConn.php

class SC_DbConn{    
    var $conn;
    var $conn_mdb2; // MDB2の接続オブジェクト    
    var $result; 
    var $includePath;
    var $error_mail_to;
    var $error_mail_title;        
    var $dsn;        
    var $err_disp = true;

    // コンストラクタ        
    function SC_DbConn($dsn = "", $err_disp = true, $new = false){        
        global $objDbConn;    
        global $objDbConnMDB2;
        // Debugモード指定    
        $options['debug'] = PEAR_DB_DEBUG;    
        // 既に接続されていないか、新規接続要望の場合は接続する。    
        if(!isset($objDbConn->connection) || $new) {    
            if($dsn != "") {
                $objDbConn = DB::connect($dsn, $options);    
                $objDbConnMDB2 = MDB2::factory($dsn, $options);  // MDB2で接続を行う    
                $objDbConnMDB2->loadModule('Extended', null, false);  // getAllなどのPEAR::DBで使用していた関数をそのまま使えるようにするためのモジュール                    
                $this->dsn = $dsn;    
            } else {        
                if(defined('DEFAULT_DSN')) {    
                    $objDbConn = DB::connect(DEFAULT_DSN, $options);
                    $objDbConnMDB2 = MDB2::factory(DEFAULT_DSN, $options);  // MDB2で接続を行う
                    $objDbConnMDB2->loadModule('Extended', null, false); // getAllなどのPEAR::DBで使用していた関数をそのまま使えるようにするためのモジュール                    
                    $this->dsn = DEFAULT_DSN;
                } else {    
                    return;
                }    
            }        
        }            

        $this->conn = $objDbConn;
        $this->conn_mdb2 = $objDbConnMDB2; // MDB2で接続を行ったオブジェクト
        $this->error_mail_to = DB_ERROR_MAIL_TO;
        $this->error_mail_title = DB_ERROR_MAIL_SUBJECT;
        $this->err_disp = $err_disp;
    }    

*** 中略 ***

    // SELECT文の実行結果を全て取得    
    function getAll($n, $arr = ""){    
        // mysqlの場合にはビュー表を変換する
        if (DB_TYPE == "mysql") $n = sfChangeMySQL($n);

        if(PEAR::isError($this->conn)) {        
            sfErrorHeader("DBへの接続に失敗しました。");            
            gfPrintLog("couldn't connect : " . $this->dsn);            
            return 0;            
        }            

        if ( $arr ){    
            //$result = $this->conn->getAll($n, $arr, DB_FETCHMODE_ASSOC);
            $result = $this->conn_mdb2->extended->getAll($n, null, $arr, null,DB_FETCHMODE_ASSOC, false);  // MDB2で実行            
        } else {    
            //$result = $this->conn->getAll($n, DB_FETCHMODE_ASSOC);
            $result = $this->conn_mdb2->extended->getAll($n, null, null, null,DB_FETCHMODE_ASSOC, false);  // MDB2で実行            
        }    
        if ($this->conn->isError($result)){    
            $this->send_err_mail ($result, $n);
        }    
        $this->result = $result;

        return $this->result;
    }

 

他に、MDB2モジュールのパスを正しく設定してあげれば終了です。

 

今回は検証ということで、getAllのみをMDB2で取得するようにしております。

そのため、無駄に2回DB接続を行ったりしていますので、正式に移行する場合にはPEAR::DBは削除するようにしてください。

また、まだデータ更新部分は検証していなかったり、色々やることはあるのですが、 移行自体は思ったより簡単に行うことが出来そうです。

(なんとなくですみません。。。)

 

今回は移行できるかの検証をさせていただきまが、MDB2の方が速いといったメリットもあるようですので、次回くらいには速度面からも検証していければと思います。

RSSを使ったブログパーツ風のもの

技術開発部の松村です。
今日はPHPでRSS解析したものををJavascriptのタグで読み込んで表示させる、というのをやってみたいと思います。
よくブログパーツとしてあるようなものを想像して頂いたらよいかと思います。

今回は、PEARのXML_RSSを使ってXMLを解析しています。
よって、

# pear install -a XML_RSS

というふうにインストールしてください。XM_PARSERが入っていない場合は、こちらもインストールをする必要があるようです。

以下のようなJavascriptで表示させます。

<script type="text/javascript">
<!--
var url = 'http://www.example.com/rss_parse.php?url=http://q.hatena.ne.jp/list/computer?mode=rss';
document.write("<scr" + "ipt type=\"text\/javascript\" src=\"" + url + "\"><\/scr" + "ipt>");
// -->
</script>

次に実際にRSSを解析して、表示させるPHPのソースです。
RSSを解析し、その中からランダムな記事を取ってくるようにしています。
Javacriptで読ませるURLが非常にダサいんですが、一応いろいろなサイトのRSSを取ってこれるので、この方法にしました。(確認はあまりしていませんが。。。)

<?php
require_once("XML/RSS.php");

// $_GETの引数が2つ以上のときは、その引数も付けてあげる
if(count($_GET) > 1 ) {
foreach($_GET as $key => $val) {
if(empty($rssUrl)) {
$rssUrl = $val;
} else {
$rssUrl .= "&" . $key . "=" . $val;
}
}
} else {
$rssUrl = $_GET['url'];
}
//RSSファイルへのURIをコンストラクタの引数に渡す
$rss =& new XML_RSS($rssUrl);
// RSSファイルをパースする
$rss->parse();

// RSSの説明情報取得
$arrChannelInfo = $rss->getChannelInfo();
$big_title = mb_convert_encoding(ereg_replace("\n", "", $arrChannelInfo['title']), mb_internal_encoding(), 'auto');

// getItemsメソッドを使用して全item要素を取得し、表示する
$arrItems = $rss->getItems();
$count = count($arrItems);
// ランダムな値を取得
$i = rand(0, $count-1);

// リンクURL
$link = $arrItems[$i]['link'];

// タイトル
$title = $arrItems[$i]['title'];
//エスケープ処理
$title = ereg_replace("\"", "\\\"", $title);
$title = ereg_replace("'", "\\'", $title);
$title = mb_convert_encoding(ereg_replace("\n", "", $title), mb_internal_encoding(), 'auto');

// 空の場合は注意書きを代入
if(!empty($arrItems[$i]['description'])) {
$description = $arrItems[$i]['description'];
//エスケープ処理
$description = ereg_replace("\"", "\\\"", $description);
$description = ereg_replace("'", "\\'", $description);
$description = mb_convert_encoding(ereg_replace("\n", "", $description), mb_internal_encoding(), 'auto');
} else {
$description = "詳細情報登録なし!!";
}
// 実際に表示される
$disp = "<div id=\\\"rss_body\\\"><ul id=\\\"in_rss_body\\\"><li id=\\\"big_title\\\">" . $big_title . "</li><li id=\\\"title\\\"><a href='" . $link . "' target=\\\"_blank\\\"><span>" . $title . "</span></a></li></ul></div>";

?>

document.write("<link rel=\"stylesheet\" type=\"text/css\" href=\"rss.css\">");
document.write("<?print($disp);?>");
document.write("<div id=\"popup\"><p><? print($description);?></p></div>");
document.write("<scr" + "ipt type=\"text\/javascript\" src=\"popup.js\"><\/scr" + "ipt>");

下記は、詳細内容をPOPUPで表示させるJavascriptのファイル、popup.jsです。

init_popup();

function init_popup() {
if(!document.createAttribute) return;

var div = document.getElementById("popup");
var span = document.getElementsByTagName("SPAN");

setHandler(span[0], div);
}

function setHandler(elem, div) {
elem.onmouseover = function(evt) {
if(!evt) evt = window.event;

div.style.left = (evt.clientX + 4) + "px";
div.style.top = (evt.clientY + 4) + "px";
div.style.display = "block";
}
elem.onmouseout = function(evt) {
div.style.display = "none";
}
}

最後にCSSのファイルのrss.cssです。
文字だけというのも悲しいので、CSSを知らないなりにちょっとしたデザインも入れてみました。
また、POPUPで出てくるウィンドウを半透明にしています。

div#rss_body{
	width: 140;
	background-color: black;
}
ul#in_rss_body{
	padding:9px;
	margin:1px;
	width: 120;
	background-color: white;
}
li#big_title {
	margin:1px;
	list-style-type: none;
	list-style-position:outside;
	font-size: 12px;
	width: 120;
	left: 0;
}
li#title {
	list-style-type: none;
	font-size: 13px;
	width: 120px;
	left: 0;
}
div#popup {
	position:absolute; left: 0; top: 0;
	font-size: 10px;
	width: 300px; height:100px;
	display: none;
	filter:alpha(opacity=75); /*IE*/
		   -moz-opacity:0.75; /*FireFox*/
	background-image: url(back.gif);
	padding: 8px;
}

これが実際どういうふうに表示されるかは、本当に申し訳ありませんが、自分のサーバでやって頂けると幸いです。
セキュリティ上どうしても自社サーバに置くことができませんでした。
リロードすると、ランダムに取ってきていることがわかったりします。

余談ですが、IEとFireFoxで表示のされ方が違うようですね。
あと、たまにPOPUP文字列がはみ出すことがあります。
Javascriptでがんばって制御しようと思ったのですが、無理でした。。。

PHPでopenSSLが有効でないとハマるとき

PEARのHTTP_RequestやHTTP_Clientで、通常のHTTPなら返ってくるのですが、SSLサイトを取りに行こうとすると、中身が返ってこないという現象に遭遇しました。
 
 
ブラウザで叩くとしっかり存在します。

試しにfopenやfile_get_contetnsで開こうと思ってもダメ。

というところまできて、php.iniの設定を洗ってみたところ、やっぱPHPのインストール時にミスっていてopenSSLが有効になっていないことに気づきました。

--with-openssl=[dir]
が通っていないときは、無言で返ってくるようです。
 
 
ってことで、PHPでSSLが?ってときは、OpenSSLが組み込まれているか、真っ先に確認すると幸せになれそうです。(・∀・)

Copyright(c) LOCKON CO.,LTD. All Rights Reserved.