May 2008
May 30, 2008
Sabel 1.1 - Mail, MimeDecode
Sabel 1.1でSabel_Mail_MimeDecodeが追加されます。PEARのMail_mimeDecodeと同じようなものですが、PEARではマルチパートの場合にpartオブジェクトの再帰構造で結果が返されるため、どこに本文があるのか、どこにHTML本文があるのかの判断が面倒です。同じ内容でもThunderbirdとOutlook Expressで送信すると構造が異なることがあるため、厄介です。
Sabel_Mail_MimeDecodeでは常に同じように本文やHTML本文などを取得できます。一般的なメールに限られますが、以下の形式に対応しています。
・本文
使い方は以下のようになります。
1.1の最新版はSVNでチェックアウトできます。
http://svn.sabel.jp/branches/1_1
Sabel_Mail_MimeDecodeでは常に同じように本文やHTML本文などを取得できます。一般的なメールに限られますが、以下の形式に対応しています。
・本文
[本文]・HTMLメール
[HTML本文]・HTMLメール(インライン画像有)
multipart/related
[HTML本文]
[インライン画像]
...
・HTMLメール + 添付ファイル
multipart/mixed
multipart/related
[HTML本文]
[インライン画像]
...
[添付ファイル]
...
・本文 + HTMLメール
multipart/alternative
[本文]
[HTML本文]
・本文 + HTMLメール(インライン画像有)
multipart/alternative
[本文]
multipart/related
[HTML本文]
[インライン画像]
...
・本文 + HTMLメール(インライン画像有)
multipart/related
multipart/alternative
[本文]
[HTML本文]
[インライン画像]
...
・本文 + HTMLメール + 添付ファイル
multipart/mixed
multipart/alternative
[本文]
[HTML本文]
[添付ファイル]
...
・本文 + HTMLメール(インライン画像有) + 添付ファイル
multipart/mixed
multipart/alternative
[本文]
multipart/related
[HTML本文]
[インライン画像]
...
[添付ファイル]
...
・本文 + HTMLメール(インライン画像有) + 添付ファイル
multipart/mixed
multipart/related
multipart/alternative
[本文]
[HTML本文]
[インライン画像]
...
[添付ファイル]
...
・ダイジェスト
multipart/digest
message/rfc822
[上記のいずれかの形式]
...
・本文 + ダイジェストメール
multipart/mixed
[本文]
multipart/digest
message/rfc822
[上記のいずれかの形式]
...
・本文 + ダイジェストメール + 添付ファイル
multipart/mixed
[本文]
multipart/digest
message/rfc822
[上記のいずれかの形式]
...
[添付ファイル]
...
使い方は以下のようになります。
$decoder = new Sabel_Mail_MimeDecode();
$decoded = $decoder->decode(file_get_contents("/path/to/mail.eml"));
if ($decoded->body) {
echo $decoded->body->getContent(); // 本文
}
if ($decoded->html) { // HTMLメール
echo $decoded->html->getContent(); // HTML本文
if ($images = $decoded->html->getImages()) { // インライン画像
foreach ($images as $image) {
echo $image["cid"]; // コンテンツID
echo $image["mimetype"]; // MIMEタイプ
echo $image["data"]; // 画像データ
}
}
}
if ($decoded->attachments) { // 添付ファイル
foreach ($decoded->attachments as $attachement) {
echo $attachment->getName(); // ファイル名
echo $attachment->getType(); // MIMEタイプ
echo $attachment->getContent(); // 添付ファイルデータ
}
}
if ($decoded->mails) { // ダイジェスト
foreach ($decoded->mails as $mail) {
// $decodedと$mailは同じように扱える
}
}
1.1の最新版はSVNでチェックアウトできます。
http://svn.sabel.jp/branches/1_1
Sabel PHP Framework
May 25, 2008
Sabel 1.1 - Request Validation
Sabel 1.1のRCがそろそろきられるかと思います。
Sabel 1.1で、リクエストバリデータがPOST以外のメソッドにも対応しました。GETパラメータ(URIクエリー)にも使用できるようになったのは結構良いです。
例えばid=100のようなクエリを必要とする時、まずnullでないかをチェックし、数値(整数)であるかのチェックをします。この作業は(多く発生するため)面倒で、実際にこのようなチェックをしていないかのような動作をするWebアプリも少なからず存在します。
Sabel 1.1のリクエストバリデータはGETにも対応したので、アクションの@checkアノテーションを使用できます。
Sabel 1.1で、リクエストバリデータがPOST以外のメソッドにも対応しました。GETパラメータ(URIクエリー)にも使用できるようになったのは結構良いです。
例えばid=100のようなクエリを必要とする時、まずnullでないかをチェックし、数値(整数)であるかのチェックをします。この作業は(多く発生するため)面倒で、実際にこのようなチェックをしていないかのような動作をするWebアプリも少なからず存在します。
Sabel 1.1のリクエストバリデータはGETにも対応したので、アクションの@checkアノテーションを使用できます。
class SomeController extends Sabel_Controller_Page
{
/**
* @check id integer
*/
public function someAction()
{
}
}
また、一つの入力に対して複数のバリデーションを設定するには、スイートを作成する必要がありましたが、複数のルールをアノテーションで指定できるようになりました。
/**
* @check id required integer
*/
public function someAction()
{
}
アノテーションを書くだけでバリデーションが行われるため非常に楽です。バリデーションに失敗するとアクションは実行されず、GETメソッドの場合は"400 Bad Request"がクライアントに返されます。
May 18, 2008
PHP & Ajax - Upload Progress Bar
Sabel 1.1ではAjax Uploaderが追加されます。php側は特に大した処理をしないので、SabelのJSライブラリを持ってこれば他でも使えます。
作成した理由はプログレスバーをスムーズに増加させるものが無かったのと、Perlが必要だったりしたためです。動画や音声ファイルなど、数メガ以上のファイルのアップロードに適しています。また、APC必須です。
実装のポイントは、一定のインターバル(i)で常にバーの幅を増加させることで、スムーズなプログレスバーを実現しています。定期的にAjaxリクエストを発行し、現在の転送量をサーバ(php)から取得します。前回のレスポンスからの時間(t)、その間の転送量(x)、ファイルサイズ(F)からファイル全体を転送するために必要な時間(T)を算出します。
この方式だと実際の現在の転送量と、バーの位置に誤差が生じます。
バーが遅れていたら上記で算出したxを増やし、進んでいたらxを減らすための補正値が必要になります。
まずはAjaxリクエストの回数とAjaxリクエストに消費した時間のトータルから、リクエスト(レスポンス)にかかる時間の平均を算出します。
-------------------- 追記(2008/07/14) --------------------
こちらで動作の様子を確認できます(100Mまで)。
作成した理由はプログレスバーをスムーズに増加させるものが無かったのと、Perlが必要だったりしたためです。動画や音声ファイルなど、数メガ以上のファイルのアップロードに適しています。また、APC必須です。
実装のポイントは、一定のインターバル(i)で常にバーの幅を増加させることで、スムーズなプログレスバーを実現しています。定期的にAjaxリクエストを発行し、現在の転送量をサーバ(php)から取得します。前回のレスポンスからの時間(t)、その間の転送量(x)、ファイルサイズ(F)からファイル全体を転送するために必要な時間(T)を算出します。
t : x = T : F T(msec) = t / (x / F)Tが算出できたら、i(msec)インターバルでT(msec)かけて100%にすればいいので、iインターバルでのバーの増加量(x)を算出します。
T : 100 = i : x x(%) = 100 * i / T次のレスポンスまで、iインターバルでx(%)ずつバーを増加させます。
この方式だと実際の現在の転送量と、バーの位置に誤差が生じます。
バーが遅れていたら上記で算出したxを増やし、進んでいたらxを減らすための補正値が必要になります。
まずはAjaxリクエストの回数とAjaxリクエストに消費した時間のトータルから、リクエスト(レスポンス)にかかる時間の平均を算出します。
avg = totalReqTime / reqCount平均リクエストタイムとインターバルから、次のリクエスト(レスポンス)までのバーの増加回数を算出できます。例えばリクエストに平均1000msecかかり、インターバルが100msecなら、増加回数は10回になります。
num = avg / i現在の総転送量よりバーの位置が3%進んでいる場合、10回でその3%の誤差を打ち消す必要があります。つまり、算出したバーの増加量xから0.3%引くことになります。この-0.3という数値の算出は以下のようになります。
rev = (現在の総転送量 - バーの位置) / numこの値を増加量xに足すことで、誤差を補正することができます。
x = x + rev
-------------------- 追記(2008/07/14) --------------------
こちらで動作の様子を確認できます(100Mまで)。
May 16, 2008
Sabel 1.1
Sabel 1.1の開発が少し落ち着いてきました。
全てではありませんが、概要は以下の通りです。
・DIコンテナ追加 (Container)
・Aspect追加 (Aspect)
・get_temp_dir()関数追加 (Functions)
・md5hash()関数追加 (Functions)
・バイナリデータ(画像・ファイル)の保存・取得に対応 (DB)
・行のバージョニングに対応 (DB)
・sabel.db.mssql(Microsoft SQL Server)パッケージの追加 (DB)
・Sabel_DB_ModelのselectWithChildrenメソッドを削除 (DB)
・Sabel_DB_Modelの各メソッドに対する前後処理メソッドの対応が変更 (DB)
・setDefaultOrder()メソッド追加、uriの指定を省略可能に (Paginate)
・アップロードプログレスバー (JS/JS Helper)
・カレンダーによるデートピッカー (JS/JS Helper)
・Timeout機能をAjaxに追加 (JS)
・メール送信ライブラリ追加 (Mail)
・HTTPリクエスト追加 (Http)
・各種ジェネレータ追加 (Tasks)
コントローラ (登録,一覧,編集,削除)
フローコントローラ (登録,編集に確認画面有り)
ログインコントローラ
アップロードプログレスバー
・外部サイトにリダイレクトするためのurl()メソッドが正常に動作しない問題の修正 (Controller)
・rewriteモジュールがロードされていない場合にInternal Server Errorになる問題の修正
多くの機能が追加され、少しの設計の改善がされています。
リリースは5/30〜6/6になる予定です。
1.1の最新版はSVNでチェックアウトできます。
http://svn.sabel.jp/branches/1_1
全てではありませんが、概要は以下の通りです。
・DIコンテナ追加 (Container)
・Aspect追加 (Aspect)
・get_temp_dir()関数追加 (Functions)
・md5hash()関数追加 (Functions)
・バイナリデータ(画像・ファイル)の保存・取得に対応 (DB)
・行のバージョニングに対応 (DB)
・sabel.db.mssql(Microsoft SQL Server)パッケージの追加 (DB)
・Sabel_DB_ModelのselectWithChildrenメソッドを削除 (DB)
・Sabel_DB_Modelの各メソッドに対する前後処理メソッドの対応が変更 (DB)
・setDefaultOrder()メソッド追加、uriの指定を省略可能に (Paginate)
・アップロードプログレスバー (JS/JS Helper)
・カレンダーによるデートピッカー (JS/JS Helper)
・Timeout機能をAjaxに追加 (JS)
・メール送信ライブラリ追加 (Mail)
・HTTPリクエスト追加 (Http)
・各種ジェネレータ追加 (Tasks)
コントローラ (登録,一覧,編集,削除)
フローコントローラ (登録,編集に確認画面有り)
ログインコントローラ
アップロードプログレスバー
・外部サイトにリダイレクトするためのurl()メソッドが正常に動作しない問題の修正 (Controller)
・rewriteモジュールがロードされていない場合にInternal Server Errorになる問題の修正
多くの機能が追加され、少しの設計の改善がされています。
リリースは5/30〜6/6になる予定です。
1.1の最新版はSVNでチェックアウトできます。
http://svn.sabel.jp/branches/1_1
Sabel PHP Framework
May 08, 2008
SQL Server - Default Constraint
SQL Serverはカラムのデフォルト値を制約として扱っているため、時々面倒なことがある。例えばデフォルト値が設定されているカラムを削除する場合にエラーになる。
先にデフォルト制約を削除し、それからカラムを削除する。
デフォルト制約の削除には制約名が必要で、それは以下のようなSQLで取得できる。
先にデフォルト制約を削除し、それからカラムを削除する。
デフォルト制約の削除には制約名が必要で、それは以下のようなSQLで取得できる。
SELECT dc.name
FROM sys.schema s
INNER JOIN sys.objects obj ON obj.schema_id = s.schema_id
INNER JOIN sys.columns cols ON cols.object_id = obj.object_id
INNER JOIN sys.default_constraints dc ON dc.object_id = cols.default_object_id
WHERE s.name = 'SCHEMA_NAME'
AND obj.name = 'TABLE_NAME'
AND cols.name = 'COLUMN_NAME'
取得した制約名で、デフォルト制約を削除する。ALTER TABLE TABLE_NAME DROP CONSTRAINT DEFAULT_CONSTRAINT_NAMEその後、カラムを削除する。
ALTER TABLE TABLE_NAME DROP COLUMN COLUMN_NAME
May 02, 2008
mail - attachment file
日本語名のファイルを添付するときのヘッダ。
RFC2231に準拠するとOutlookが駄目なようなので、シェアを考慮すると準拠できない。Docomoのメールアドレスと同じ類の問題。
$format = <<<FORMAT
Content-Disposition: attachment; filename="%1\$s"
Content-Transfer-Encoding: %2\$s
Content-Type: %3$s; name="%1\$s"
FORMAT;
$fileName = mb_encode_mimeheader("日本語.gif", "ISO-2022-JP", "UTF-8");
$header = sprintf($format, $fileName, "base64", "image/gif");
ちなみにこのエンコードされたファイル名はRFC2231に準拠していない。が、確認したところで、WindowsのOutlook Express・Window/LinuxのThunderbird・MacOSXのMail・Gmailで正しく扱うことができる。RFC2231に準拠するとOutlookが駄目なようなので、シェアを考慮すると準拠できない。Docomoのメールアドレスと同じ類の問題。
May 01, 2008
behavior of empty(), isset()
たまに忘れるハマりどころ。
<?php
class Foo
{
protected $values = array();
public function __set($key, $val)
{
$this->values[$key] = $val;
}
public function __get($key)
{
return (isset($this->values[$key])) ? $this->values[$key] : null;
}
}
$foo = new Foo();
$foo->a = 10;
var_dump($foo->a); // int(10)
var_dump(isset($foo->a)); // bool(false)
var_dump(empty($foo->a)); // bool(true)