PHPの最近のブログ記事

メール一斉配信をしたときに届かなかったエラーメールだけを
受信できないかと思い作ってみました。

PEARのNet_POP3を使うとメールサーバーに
簡単にアクセスできるのでまずはそちらでメールの一覧を受信。

配信されなかったメールヘッダーの[X-Failed-Recipients]には
メールアドレスが入っているのでそれを取り出す。

後は日付順に並べれば配信エラーの一覧表ができる。

deleteMsg()メソッドを使うとメールの削除もできるので
古いメールはこれで自動的に削除していくといいかもしれない。


説明だけだと簡単なのだが、なかなかうまくいかずこれは意外と難問だった。

参考にしたサイトはこちら
PEAR::Net_POP3を使ってPOP3サーバにアクセスしよう - PHP TIPS:ITpro

<?php
require_once('Net/POP3.php');

$account = array(
  'host' => 'ホスト名',
  'port' => '110',
  'username' => 'ユーザ名',
  'password' => 'パスワード',
);
$pop3 =& new Net_POP3();
$pop3->connect($account['host'], $account['port']);
$pop3->login($account['username'], $account['password']);
   
    $mail_list = $pop3->getListing();
   
    $i = 0;
    foreach($mail_list as $key => $val) {
              
        $header_list = $pop3->getParsedHeaders($val["msg_id"]);
        $errdate = $header_list["Date"];
        $errmail = $header_list["X-Failed-Recipients"];

        if($errmail != "") {
            $errdata[$i]["errdate"] = $errdate;
            $errdata[$i]["errmail"] = $errmail;
            $i ++;
        }//end if
    }//end foreach
   
    $pop3->disconnect();
   
    $smarty->assign("errdata", $errdata);       
    $smarty->display("index.html");
?>
デフォルトのセッションパスを使用するとどういうわけか動作が不安定になるので
(レンタルサーバー会社に連絡してサーバーの再起動をしてもらうと直るのだが・・・面倒!)
パスを変えて使ってみると安定した。

ただひとつ問題が・・・
セッションタイムアウトしない!

php.iniもいじれず・・・

session_cache_expire()を使ってもタイムアウトせず・・・

困ったなーと思っていたが何とか解決しました。

結局、セッションファイルをチェックして一定時間が経過したら削除するという
ちょっと強引な方法で今のところ問題なく動作しています。

filemtime関数でファイルの更新時刻を取得し
指定した時間が経過したら削除するという方法で。
session_start() をするとこの更新時刻は更新されるため
使っている間は削除されないということになります。

使用方法はページを移動するたびにタイムアウト関数を走らせています。
※ session_start() よりも前に書きます。


以下の例ではタイムアウトの時間は10分に設定しています。

参考にしたページはこちら
http://www.phppro.jp/qa/449

// =========================================================
// セッションのタイムアウト制御
// =========================================================
function session_timeout() {
    //セッション有効時間(秒)
    $KeepAliveTime = 600;
    //セッションデータパス
    $sesspath = session_save_path();
     
    //タイムアウトしたセッションデータを検索&削除
    foreach (glob("$sesspath/sess_*") as $fname) {
        if(filemtime($fname) + $KeepAliveTime < time()) {
            @unlink($fname);
        }//end if
    }//end foreach
}//end function

多重配列のエンコード

| コメント(0)
PHP+Smartyで携帯サイトを制作すると絶対にぶつかる問題。

「文字コードの変換」

パソコン用であれば、「UTF-8」で統一しておけば何も問題ないのだが
携帯サイトに関しては、基本的に「SHIFT-JIS」

ドコモの一部機種とソフトバンクは、「UTF-8」対応しているが
auは対応しておらず、すべての携帯で見れる文字コードは
今のところ「SHIFT-JIS」のみ。

ただし、PHP+Smartyは「UTF-8」か「EUC」しか対応していないので
PHPは上記どちらかで保存し、ビュー用のテンプレートファイルを「SHIFT-JIS」で保存。

そして、アウトプットさせるデータを出力時に「SHIFT-JIS」にエンコード。
これでうまくいくはずが・・・文字コードはハマると結構面倒なんだなぁ(泣)

今回気がついたことは、エンコードの関数「mb_convert_encoding」って
配列に対応してないのね・・・(><)

ということで、多重配列に対応したエンコード関数を作ってみました。
これで携帯でうまく表示されました!

/* -------------------------------------------
 文字列(配列)のエンコード
 $param = ターゲットとなる配列もしくは文字列
 $to_encoding = 変換後の文字コード
 $from_encoding = 変換前の文字コード(デフォルトはauto)
------------------------------------------- */
function arr_mb_convert_encoding($param, $to_encoding, $from_encoding="auto"){
    if(is_array($param)){
        foreach($param as $key => $val){
            if(is_array($val)){
                foreach($val as $k => $v){
                    $param[$key][$k] = mb_convert_encoding($v, $to_encoding, $from_encoding);
                }//end foreach
            }else{
                $param[$key] = mb_convert_encoding($val, $to_encoding, $from_encoding);
            }//end if
        }//end foreach
    }else{
        $param = mb_convert_encoding($param, $to_encoding, $from_encoding);
    }//end if
    return $param;
}//end function


一つのURLにアクセスしてもらって、PCか携帯かに
振り分けるリダイレクトプログラムを入れたら
ドコモだけ「サイトが移動しました(301)」と一旦表示されて
その後正常に表示される。

Smartyを使って、DisplayしているのでURLは変えないんだから
移動しましたっていちいち言うなよっ!という感じです。

結果的に超単純な方法で改善しましたが、これを改善といっていいのかどうか。
URLの最後に"/"(スラッシュ)を必ずつけるということでした。

QRコードや広告、名刺に記載するモバイルURLの最後に
しっかりと"/"(スラッシュ)を付けてもらうということでしょうか・・・。
何だか腑に落ちませんが。

突然、セッションエラー

| コメント(0)
作業中に突然セッションエラーが発生するようになった・・・なぜ?
エラーの行を見てみると、最初のsession_start();の部分。
思い当たる節もなく、レンタルサーバー会社に即連絡。
サーバーの再起動で直ったもののしばらくするとまた発生。何なの?

エラーの内容は下記の通り。
**********************************************
Warning: session_start() [function.session-start]: open(/tmp/sess_e102d79a5e5f8a2accd0e3bc1dfbc1f8, O_RDWR) failed: Permission denied (13) in /public_html/index.php on line 7

Warning: Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct () in Unknown on line 0
**********************************************

またサーバーの再起動をしてもらったところで根本的な解決にはならないので
セッションパスを変えてみることにした。

新たにsessionというフォルダを作り、パスを変更。
session_save_path('/home/*****/session');

そして、エラーの内容からパーミッションに問題がありそうなので
777に設定変更し、再度実行してみると・・・なんと解決!

777でセキュリティ的にどうなのかという問題もあるが、public_htmlより
上の階層に作ってあるからまぁいいかということで・・・。

ちなみにsession_save_pathは、session_start()よりも先に書かなければならない。
ついでにタイムアウトの時間も変更。
デフォルトは180分らしい。そんなにはいらないので、30分くらいに・・・。

<?php
session_save_path('/home/*****/session');
session_cache_expire(30);
session_start();


?>

先の記事で、fgetcsv関数を紹介しましたが
どうもこの関数は処理速度が遅いらしい。

ともかく少しでも速くしたいので、別の方法を試してみた。
fgetcsvに変えて、file_get_contentsを使ってみる。
ただし、file_get_contentsだと1行ずつ取り出してくれないので
explodeを2回入れる必要がある。
改行(\n)での分割とカンマ(,)での分割。
ちょっとコードが長くなるが、速いに越したことはない。

$fname = "csv/sample.csv";
$file = file_get_contents($fname);
$file_line = explode("\n", $file);
$i = 0;
foreach($file_line as $value) {
    list($sp_date, $sp_title, $sp_detail, $sp_url, $sp_photo) = explode(",", $value);
    $sp_data[$i]["sp_date"] = $sp_date;
    $sp_data[$i]["sp_title"] = $sp_title;
    $sp_data[$i]["sp_detail"] = $sp_detail;
    $sp_data[$i]["sp_url"] = $sp_url;
    $sp_data[$i]["sp_photo"] = $sp_photo;    
    $i++;
}// end foreach

ストップウォッチ片手にベンチマークしてみると
Firefoxにおいては、たしかに1秒ほど速くなった。

ただIE7.0でのテストは、わずかに速いかなという程度で
ほとんど変わらなかった・・・というかどちらも遅い。

DBが使える環境であれば、CSVよりも間違いなく
DBをおすすめする・・・当たり前か(^^;)

どちらにせよ、DBが使えない特殊な環境もしくは
制限がある場合は、CSVを使うしかないのだが
データを取り出す際には、fgetcsvよりもfile_get_contentsの方が
いいかもしれない。
他にもfile関数やfopen関数があるが、あまり変わらない
ということなので今回は試していない。

参考にしたサイト: 2Y's BLOG

CSVファイルから取り出したデータを配列に入れて
Smartyで呼び出してみる。
DBが使えない環境でとりあえずの策として利用してみた。

$fname = "csv/sample.csv";
$file = fopen($fname, "r");
$i = 0;
while(list($sp_date, $sp_title, $sp_detail, $sp_url, $sp_photo) = fgetcsv($file, 1000, ",")) {
    $sp_data[$i]["sp_date"] = $sp_date;
    $sp_data[$i]["sp_title"] = $sp_title;
    $sp_data[$i]["sp_detail"] = $sp_detail;
    $sp_data[$i]["sp_url"] = $sp_url;
    $sp_data[$i]["sp_photo"] = $sp_photo;    
    $i++;
}//end while
fclose($file);

$smarty = new Smarty();
$smarty -> assign("sp_data", $sp_data);

携帯キャリア振り分け

| コメント(0)
携帯サイトを製作する際にもっとも考えるところは、携帯キャリアごとに
違う仕様をどう対応するのかということ。

そこで便利なツールが、Net_UserAgent_Mobile
PEARサイトからダウンロードできる。
PEAR :: Package :: Net_UserAgent_Mobile

これを使えば、キャリアはもちろんのこと携帯の機種やブラウザまで判別できる。
(サーバーにインストールする場合には、各ファイルのインクルードパスを
環境に応じて変更する必要がある。)

あとは、キャリア毎に用意したURLに振り分けるか読み込むCSSを変えるとよい。
今回製作した携帯サイトでは、絵文字を使っているためあらかじめ用意した
URLに振り分けることにした。
ドコモ・au・ソフトバンク以外(ウィルコムなど)は、基本がフルブラウザなので
PCと同様の扱いでPC用サイトに誘導するようにしてみました。

<?php
//PEAR::Net_UserAgent_Mobile
require_once('Mobile.php');

$retval = mobile_useragent_carrier();
$path = $retval["path"];

if ($path == "i" || $path == "ez" || $path == "s") {
    //起動ファイル
    header("location: $path/index.html");
} else {
    //DoCoMo, au, SoftBank以外(PCなど)
    header("location: http://www.xxxxx.com/");
}//end if

//アクセス端末のキャリア名/機種名/タイプ名を判別する
function mobile_useragent_carrier() {
   
    //Net_UserAgent_Mobileをインスタンス化する
    $agent = Net_UserAgent_Mobile::singleton();
   
    //キャリアをチェックする
    if ($agent->isDoCoMo()) {
        //DoCoMo
        $carrier = 'docomo';
        //フォルダパス
        $path = "i";
       
    } else if ($agent->isEZweb()) {
        //au
        $carrier = 'au';
        //フォルダパス
        $path = "ez";
           
    } else if ($agent->isSoftBank()) {
        //SoftBank / Vodafone / J-PHONE
        $carrier = 'softbank';
        //フォルダパス
        $path = "s";
       
    } else {
        //携帯以外の場合(ウィルコム・イーモバイルもPC用サイトを表示)
        //フォルダパス
        $path = "pc";   
    }//end if   
   
    //返り値を準備
    $output = array(carrier=>$carrier, path=>$path);
   
    return $output;
   
}//end function

?>


例えば検索条件を入力してもらう画面を作る場合
スペースで区切って条件を入力してもらう場合など
スペースを目印にして文字列を分割することになる。

最近作った例では、データベースから名前で検索する場合。
(このデータベースには、姓と名が分割されて登録されている。)

例えば、「田中 太郎」と入力があった場合
姓⇒田中、 名⇒太郎と分割したい。

その際に姓と名のフィールドを分けてしまう対応はあまりスマートではない。
他の条件で検索する場合に、またフィールドを増やさなくてはならないからだ。
ここでのポイントは、区切りスペースの位置。
そして、スペースはすべて半角に統一しておく。

//文字列の中にあるスペースをすべて半角スペースに変換
$search_key = str_replace(" ", " ", param["search_seimei"]);
//文字列の中からスペース位置を探す
$space_num = mb_strpos($search_key, " ");

if($space_num == "") {//姓名の間に空白がなかったら姓のみで検索
    $search_sei = $search_key;
    $search_mei = "";
    //検索対象変数
    $s_target = "sei";
} else {//姓名の間に空白があれば、姓と名に分割する
    $search_sei = mb_substr($search_key, 0, $space_num);
    $search_mei = mb_substr($search_key, $space_num, strlen($search_key));
    //前後の空白を取り除く
    $search_mei = trim($search_mei);
    //検索対象変数
    $s_target = "sei_mei";        
}//end if

switch ($s_target) {
  case "sei":
    //「姓」のみで検索のSQL
  case "sei_mei":
    //「姓・名」で検索のSQL
}//end switch





PEAR DBからMDB2へ

| コメント(4)
Smartyの問題が解消できたかと思ったら今度はPEAR DBも認識しなくなっていた。
ほんとに突然ナンなの?と思いつつ・・・PEARも自前でインストールすることに。

しかし、Smartyのようにうまくいかない・・・。
go-pear.phpを使ったインストールを試すもなぜか60%でストップ。
リロードしても60%・・・そこに何がいるんだよ!と
コードを見ても原因は分からず、違う方法を試すことにした。

ローカルでダウンロードファイルを解凍しアップすることに。
今回は緊急措置としてとりあえずPEAR DBだけ使えればいいので
個別にダウンロードして解凍・・・ここで注意!
なぜかLhacaやLhaplusなどの解凍ツールではうまく解凍してくれません。
そこで使ったのは、「解凍レンジ
これで解凍すると必要なすべてのファイルが出てきました。

これをSmartyと同じくlibsフォルダの中にpearフォルダを作ってその中にアップロード。
そしてすべてのDB.phpパスを変更し画面を確認。
動いているようだが、DBにコネクトしてくれない・・・なぜ(^^;)

それから1時間・・・。
PEAR DBをあきらめ、PEARのウェブサイトでも推奨されているMDB2を使ってみることにした。

MDB2をダウンロードし、同じく解凍レンジで解凍。

MDB2.phpとMDB2フォルダをlibs/pearフォルダにアップロード。
そして、コードを一部書き直して画面をリロード・・・。
おぉっと復旧しました! ハフゥ?(汗)


使用例は下記の通り。

//PEAR::MDB2 require_once('パス/libs/pear/MDB2.php'); $dsn="$dbtype://$user:$pass@$sv/$dbname"; $mdb2 = MDB2::factory($dsn); if(MDB2::isError($mdb2)){ echo "データーベース接続失敗"; }else{
echo "データーベース接続成功";
}//end if

while($row = $res->fetchRow(MDB2_FETCHMODE_ASSOC)){
$db_data[] = $row;
}//end while


基本的にはPEAR::DBとさほど変わらないが、コネクトと展開の書き方が違う。
$db = DB::connect($dsn);
       ↓
$mdb2 = MDB2::factory($dsn);

展開するときもDB_FETCHMODE_ASSOCから
MDB2_FETCHMODE_ASSOCに変わる。

PEAR::DBのマニュアルにも下記のようなメッセージがあるので今後のことを
考えるとその方がいいのかも・・・。

「This package been superseded by MDB2 but is still maintained for bugs and security fixes」(このパッケージの代わりにMDB2の使用が推奨されますが、バグの修正、セキュリティフィックスは引き続き行われます。)


今回は、ロリポサーバーには散々振り回された・・・。


ウェブページ

★過去に製作したウェブサイト(一部紹介)
イビキノン
いびきでよく眠れない方のサプリ
日本トレーラーハウス協会
トレーラーハウスの協会(JTHA)
トレーラーハウスデベロップメント
トレーラーハウスの製造・販売 シュミレーションゴルフのレンタル スクリーンゴルフのレンタル
健楽ショップ 【ボイスリッチEX】
声が出にくい症状の方に朗報
早稲田大学 スキー部
2007年 インカレ総合優勝!
NPO体験学習支援センター
ブナの植林100年計画実行中
AJC油汚染研究所
油ゲル化剤オイルフェンスの販売
ジャズシンガー 奥土居美可
NYで絶賛されたジャズボーカル