カテゴリー ‘ framework

Exceptionとか

出張で、家に2週間いなかったのも手伝って、全然更新してませんでした。

その間、初めて業務関係でZend Frameworkをいじれていました。


ある程度の規模のものを作っても全然楽。開発スピードが違う。

特に、ErrorHandler、ErrorControllerを使ったエラー処理は便利。

Exceptionをthrowしておけば、エラーは拾ってくれるし、

Exception内のデータから、エラーの原因も特定できる。

何より、エラーのための処理をソースにあまり書かなくていいから、

可読性がものすごくあがる。これはかなりメンテナンスしやすい!!

Exceptionは、エラー発生ファイル、ライン数、そこまでのトレース情報までわかる。


あと、Smartyの便利さに改めて気づきました。

Smartyから、post、sessionの情報を参照できるのは使うと、手放せません。

まだやったことない方はぜひお試しください。

  • Exceptionとか はコメントを受け付けていません。

Zend Frameworkバージョンアップ

今まで使っていた、Zend Framework 1.0.3を、1.5.1にバージョンアップしました。


まだ細かいところは検証してませんが、最初に引っかかった問題を書いておきます。


今まで、下記の【1】のように書いていた index.php を、

【2】のように書き換えないと動かなかったです。

不便だなと感じましたが、1行の修正なので大した修正ではないです。

※ /xxx/yyy/xxx/ 配下に contrllersディレクトリが存在します


【1】

1
2
3
4
5
6
7
8
9
10
11
$add_path = . '/xxx/yyy/ZendFramework/library' . PATH_SEPARATOR
      . '/xxx/yyy/zzz';
 
ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . $add_path);
 
require_once('Zend/Loader.php');
Zend_Loader::loadClass('Zend_Controller_Front');
 
$front = Zend_Controller_Front::getInstance();
$front->setControllerDirectory('controllers')
         ->dispatch();



【2】

1
2
3
4
5
6
7
8
9
10
11
$add_path = . '/xxx/yyy/ZendFramework/library' . PATH_SEPARATOR
       . '/xxx/yyy/zzz';
 
ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . $add_path);
 
require_once('Zend/Loader.php');
Zend_Loader::loadClass('Zend_Controller_Front');
 
$front = Zend_Controller_Front::getInstance();
$front->setControllerDirectory('/xxx/yyy/zzz/controllers')
    ->dispatch();



相対パス、絶対パスでの指定では動作したのですが、

パス設定をした後に、パスを利用したディレクトリ指定ではNGでした。

  • Zend Frameworkバージョンアップ はコメントを受け付けていません。

fetchRowとfetchAllの違い

fetchRow、fetchAllどちらもDBから行を取得するメソッドです。


相違点は以下


fetchRow

 1. 単一の行を取得

 2. 返り値の型は、Zend_Db_Table_Row

 3. 0件取得の場合は、nullを返却


fetchAll

 1. 複数の行を取得

 2. 返り値の型は、Zend_Db_Table_Rowset

 3. 0件取得の場合は、Zend_Db_Table_Rowsetを返却


注目するところは、3です。

fetchAllは、0件でもZend_Db_Table_Rowsetを返却します。

なので、取得の有無で、nullかどうかでは判定できません。


私はどちらもnullが返ってくると勘違いしていたので、ミスりました。

  • fetchRowとfetchAllの違い はコメントを受け付けていません。

Zend_Mailで日本語のFrom情報を扱う

以前の記事で、Fromの名前の部分が化けてしまうということを書きましたが、

解決方法がわかったので、ここに残しておきます。


参考になったのは http://iandeth.dyndns.org/mt/ian/archives/000628.html です。

ここに、日本語のヘッダーを扱うためには、ISO-2022-JPに文字コードを変換して、

さらにbase64でエンコードして、さらに “=?ISO-2022-JP?B?”と”?=”で囲む必要があると。

今まで、mb_encode_mimeheaderを何も考えずに使っていましたが、

内部では上記のbase64エンコード以降のことをやってくれていたんですね。


ここまでを踏まえて、Zend_Mailのソースを見たところ、メールヘッダーはすべて、

base64ではなく、printableでエンコードされていました。

文字化けの原因はここでした。日本語に対応した処理でなかったとうことです。


しかし、printableでエンコードしている部分( _encodeHeaderメソッド)を、

base64でエンコードするようにしてしまうと、送信日時等の日本語が入ることがない

ヘッダーが受信側で正確にデコードされなくなってしまいます。


base64でエンコードしないとおかしくなるのは、Fromです。(場合によっては、Subjectも)

なので、setFromメソッドで、_encodeHeaderメソッドを呼び出している部分を、

mb_encode_mimeheaderを使ってエンコードするように変更したらOKです。


$this->_encodeHeader(‘”‘.$name.'”‘)


  ↓


mb_encode_mimeheader(‘”‘.$name.'”‘, ‘ISO-2022-JP’)


mb_encode_mimeheaderについては、下記をご参照ください。

http://phpspot.net/php/man/php/function.mb-encode-mimeheader.html


プログラムを設置しているサーバからZend_Mailだけを使って送信する場合は、

必ず文字化けするようなので、この対応は必須です。

メールサーバにSMTP認証して送信する場合(Zend_Mail_Transport_Smtp利用)は、

上記対応をしなくても、文字化けしていない場合もありました。

メールサーバ側で、うまいことやってくれているのかもしれません。

  • Zend_Mailで日本語のFrom情報を扱う はコメントを受け付けていません。

instanceof って便利

Zendを使うようになってから、同時に利用するようになったもの。

if 文での instanceof 判定。判定対象のオブジェクトの型を判定してくれる。


Dbから値を取得した際の処理に便利。


if ($rows instanceof Zend_Db_Table_Row)  の場合、

if ($rows instanceof Zend_Db_Table_RowSet) の場合。両方の処理を準備する。


Zend_Db_Table_Rowsetの場合は、Zend_Db_Table_Rowの場合の処理を

再帰的に呼び出すことでOKな場合も多々あります。


感覚でいうと1次元配列、2次元配列を気にせずに処理できるメソッドができる。

1次元配列と2次元配列を簡単に判定する方法を知らないから、とても便利に感じる。

たとえば、Dbから取得したカラム毎の合計値を取得するなら、

下記のように処理すればOKです。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public function getSumList($rows, $sumList=null)
{
 if (is_null($sumList)) $sumList = $this->getInitial();
 
 if (!is_null($rows)){
  if ($rows instanceof Zend_Db_Table_Row){
   $list = $rows->toArray();
   foreach ($list as $key => $value) $sumList[$key] += $value;
  }else if ($rows instanceof Zend_Db_Table_Rowset){
   foreach ($rows as $row) $sumList = $this->getSumList($row, $sumList);
  }
 }
 
 return $sumList;
}



$this->getInitial()は、$sumListに初期値を設定する関数です。

+= するためにnullだと気持ちが悪いので、0を設定しているだけの処理です。

カラム名は、Zend_Db_Table_Abstractクラスのinfo()関数で簡単にとってこれます。


カラム名の取得については、

http://framework.zend.com/manual/ja/zend.db.table.html#zend.db.table.info

10.5.10. テーブルのメタデータ情報の取得 を見てみてください。

  • instanceof って便利 はコメントを受け付けていません。

ブラウザからのファイルダウンロード

http://tkoshima.net/wp/archives/48


IEでだけ、ブラウザ経由のファイルダウンロードがうまくいきませんでした。

ネットで検索して、見つけたのが上記のリンク。

httpヘッダのキャッシュ設定を public にしないと正常にダウンロードできないとのこと。

下記ソースの※の部分を追加したら、IEでも正常に動作するようになりました。

(このソースは、Zend FrameWorkのActionコントローラ内に記述しています)


$fileName は、ダウンロードダイアログに出力するファイル名、

$path は、ダウンロード対象ファイルへのパスになります。


$fileNameは、ブラウザがIEの場合 SJIS に変換しないと日本語が文字化けします。


1
2
3
4
5
6
7
$this->getResponse()
   ->setHeader("Cache-Control", "public") ※
   ->setHeader("Pragma", "public")        ※
   ->setHeader("Content-Type", "application/octet-stream; name=\"$fileName\"")
   ->setHeader("Content-Disposition", "attachment; filename=\"$fileName\"")
   ->setHeader("Content-Length", filesize($path))
   ->setBody(file_get_contents($path));

  • ブラウザからのファイルダウンロード はコメントを受け付けていません。

メールヘッダーのデコード(Zend_Mail)

Zend_Mail_Storage_Imapを利用して、メッセージを取得する場合、

Zend_Mail_Storage_ImapのgetMessageメソッドにて、メッセージデータを

取得したタイミングで、メールヘッダーもデコードされて、オブジェクトに設定されます。


この際のデコードは、iconv_mime_decode_headersというphp5から使える

phpライブラリで行われています。Zend_Mime_Decodeクラスの中で。

しかし、このとき文字コードが、iconv.internal_encoding(default “ISO-8859-1″)

にてデコードされるため、日本語を含んでいる場合は、正常にデコードされません。


iconv_mime_decode_headersの第3パラメタに’UTF-8’を指定したら、

日本語もUTF-8にて、正常にデコードされて取得することができます。


この対応策にたどり着くまでに、4,5時間悩んでましたが、解決策は1行修正。

Zendのソースには、あまり修正を入れたくないと思っていたので、手間取りました。

  • メールヘッダーのデコード(Zend_Mail) はコメントを受け付けていません。

メール受信の注意点(Zend_Mail)

例えばPop3でメールを取得する場合、以前書いたこの記事のような方法で取得します。



取得した際、件名などのヘッダー情報は、

$message->subject

としてとりだすことが可能です。


しかし、取り出そうとしたヘッダー情報がない場合は、

nullが返ってくるわけではなく、exceptionが発生します。


そのため、try catchを大きなくくりでしか行っていないと、

大部分の処理が実行されないという結果になります。


件名、本文がないメールも存在するので、ここは注意が必要です。

私もこれで、しばらく作りかけのプログラムが止まっていました。

  • メール受信の注意点(Zend_Mail) はコメントを受け付けていません。

Zend_Mail_Storage_Imapのバグ

私が使っているZend Frameworkは、最新のStable版(1.0.3)です。


その中で見つけた不具合。

Zend_Mail_Storage_Imapのメソッド、copyMessage。


第一パラメタにメッセージ番号、第2パラメタにコピー先フォルダの

フォルダクラス(Zend_Mail_Storage_Folder)または、

コピー先フォルダのグローバル名($folder->getGlobalName())を

渡すというコメントが書かれています。


しかし、実際はグローバル名しか渡せません。

通常、こういうコメントが書かれているメソッドは、

処理内でパラメタがフォルダクラスかの判定をして、クラスならば

getGlobalName()メソッドで、グローバル名におきかえてくれるのですが、

なぜかcopyMessageの中では行われていませんでした。


お気をつけください。

  • Zend_Mail_Storage_Imapのバグ はコメントを受け付けていません。

悩みどころ

Zendを使ってみて、より意識をするようになったカプセル化。


カプセル化を進めると、再利用性が増すが、ソースの視認性を保つには、

メソッド名、クラス名がわかりやすいことが大前提だと思う。


あと、どういう単位で処理を分けるか。改めてこの辺で悩む。

  • 悩みどころ はコメントを受け付けていません。

return top