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本文などを取得できます。一般的なメールに限られますが、以下の形式に対応しています。

・本文
[本文]
・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アノテーションを使用できます。
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)を算出します。
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

Sabel PHP Framework




May 08, 2008

SQL Server - Default Constraint

SQL Serverはカラムのデフォルト値を制約として扱っているため、時々面倒なことがある。例えばデフォルト値が設定されているカラムを削除する場合にエラーになる。

先にデフォルト制約を削除し、それからカラムを削除する。
デフォルト制約の削除には制約名が必要で、それは以下のような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

日本語名のファイルを添付するときのヘッダ。
$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)


Sabel

Sabel PHP Frameworkを開発しています。
http://www.sabel.jp/

Search
Categories
Tags
Recent Articles
Archives