Sabel
July 18, 2008
Sabel Release Announcement
Sabel-1.1.1がリリースされました。
Sabel 1.1.0で発見されたいくつかのバグが修正されています。
(Http-Request)リクエスト後にGET値やPOST値を破棄するように変更, POST値が0個の場合にファイルアップロードできない問題の修正
(Task-Fixture)Windowsにおける絶対パス判定の正規表現の不具合を修正
(Util-String)エンコーディングにUTF-8を使用している際のtrim()メソッドの正規表現の不具合を修正
(JS)Stringクラス使用時の返り値の不具合修正
(JS)String.capitalizeメソッドのバグ修正
ダウンロードはこちらから。
Sabel 1.1.0で発見されたいくつかのバグが修正されています。
(Http-Request)リクエスト後にGET値やPOST値を破棄するように変更, POST値が0個の場合にファイルアップロードできない問題の修正
(Task-Fixture)Windowsにおける絶対パス判定の正規表現の不具合を修正
(Util-String)エンコーディングにUTF-8を使用している際のtrim()メソッドの正規表現の不具合を修正
(JS)Stringクラス使用時の返り値の不具合修正
(JS)String.capitalizeメソッドのバグ修正
ダウンロードはこちらから。
July 14, 2008
Sabel AjaxUploader Sample
Sabel AjaxUploaderのサンプルを用意しました。
こちらで動作の様子を確認できます(100Mまで)。
コードは以下のような感じです。ジェネレータで生成することもできますが、コード量が少ないのでコピペでも楽かと思います。
コントローラ例

こちらで動作の様子を確認できます(100Mまで)。
コードは以下のような感じです。ジェネレータで生成することもできますが、コード量が少ないのでコピペでも楽かと思います。
コントローラ例
<?php
class Index_Controllers_Upload extends Sabel_Controller_Page
{
public function upload()
{
$this->uploadId = md5hash();
}
public function fetchStatus()
{
$status = apc_fetch("upload_" . $this->request->fetchGetValue("uploadId"));
echo json_encode($status);
exit;
}
public function uploaded()
{
echo "uploaded";
exit;
}
}
uploadアクションのテンプレート例(Sabelレンダラ使用)
<script type="text/javascript" src="<?= linkto("js/Sabel.js") ?>"></script>
<script type="text/javascript" src="<?= linkto("js/helpers/AjaxUploader.js") ?>"></script>
<style type="text/css">
@import url("<?= linkto("js/helpers/css/Sabel.css") ?>");
</style>
<div id="progressBar"></div>
<form id="upload_form" enctype="multipart/form-data" action="<?= uri("a: uploaded") ?>" method="post">
<p>
<input type="hidden" name="APC_UPLOAD_PROGRESS" value="<?= $uploadId ?>" />
<input type="file" name="upfile" /><br />
<input type="submit" value="upload" />
</p>
</form>
<script type="text/javascript">
new Sabel.PHP.AjaxUploader("upload_form", "<?= uri("a: fetchStatus") ?>?uploadId=<?= $uploadId ?>", "progressBar");
</script>

July 05, 2008
Sabel JS - DatePicker(Sabel.Widget.Calendar)
Sabel 1.1で追加されるSabel.Widget.Calendarのサンプル。
カレンダーで日付を選択し、その選択された日付をインプットに適用するDatePicker.jsを書いてみた。
* 実際にサンプルのように表示するにはSabelに含まれるcssや画像が必要
カレンダーで日付を選択し、その選択された日付をインプットに適用するDatePicker.jsを書いてみた。
DatePicker.js
function datepicker(inputs) {
Sabel.Array.each(inputs, function(input) {
var div = document.createElement("div");
div.style.position = "absolute";
div.style.zIndex = 100;
document.body.appendChild(div);
var self = {
element: Sabel.get(input),
calendar: new Sabel.Widget.Calendar(div, {
callback: function(date) {
self.element.value = date.join("-");
}
}),
render: function() {
self.calendar.render();
}
};
div.style.top = Sabel.Element.getCumulativeTop(self.element) + "px";
div.style.left = Sabel.Element.getCumulativeLeft(self.element) + "px";
self.calendar.WeekDays = ["日", "月", "火", "水", "木", "金", "土"];
self.element.observe("mousedown", self.render);
});
}
インプットのidをdatepicker()関数に渡すだけで使用できる(簡単に使えるようにしているため、任意のタイミングでカレンダーを消すなどの処理はできない)。インプットをクリックすると、そのインプットの位置にカレンダーが表示され、日付をクリックするとインプットに年-月-日が入る。
<script type="text/javascript" src="Sabel.js"></script>
<script type="text/javascript" src="DatePicker.js"></script>
<script type="text/javascript">
new Sabel.Event(window, "load", function() {
datepicker(["inputid1", "inputid2", ...]);
});
</script>
動作の様子はこちら。* 実際にサンプルのように表示するにはSabelに含まれるcssや画像が必要
July 02, 2008
Sabel 1.1 Release Announcement
Sabel-1.1のリリースが7/7(月)に決定しました。
概要は以下の通りです。

概要は以下の通りです。
- (Core)Sabel::using()でクラスファイルを読み込めたかどうかに応じてbool値を返すように変更
- (Function)get_temp_dir()関数追加
- (Function)md5hash()関数追加
- (Function)remove_nullbyte()関数追加
- (DI)DIコンテナ追加
- (Aspect)Aspect追加(AOPアライアンス準準拠)
- (Mail)メール(送信/Mimeデコード)追加
- (Http)HTTPクライアント追加
- (Annotation)バックスラッシュによるクオートのエスケープに対応
- (Response)設計の改善・ステータスオブジェクト追加
- (Request-Validator)一つの入力に対し複数のバリデーションメソッドを登録できるように改善
- (Controller-Redirector)外部サイトにリダイレクトする際などに使用するurl()メソッドが正常に動作しない問題の修正
- (View-Pager)実装の改善(3割程度の高速化)
- (View-PageViewer)実装のスリム化(setPriorityPrevious(), setPriorityNext(), setIgnoreEmpty()メソッド削除)
- (Session-Memcache)createメソッドでポート番号を指定できるように改善, addServer()メソッド追加
- (Storage-Memcache)addServer()メソッド追加
- (Cache-Memcache)addServer()メソッド追加
- (Util-FileSystem)Sabel_Util_FileSystemによりディレクトリやファイルを作成する際のデフォルトのパーミッションを744から755に変更
- (DB)バイナリデータ(画像・ファイル)の保存・取得に対応
- (DB)行のバージョニング(楽観的ロック)に対応
- (DB)sabel.db.mssql(Microsoft SQL Server)パッケージの追加
- (DB)Sabel_DB_Modelの各メソッドに対するコールバックの対応が変更
- (DB)Joinオブジェクト使用時のカラムの指定を可能に改善
- (DB-Model)selectWithChildrenメソッドを削除
- (DB-Model)save()メソッドによりモデルの状態をデータベースに反映する際、プライマリキーの値が変更されている場合に例外を投げるように変更
- (Test)Sabel_Test_Fixtureクラス追加
- (Processor-Action)リクエストバリデーションがURIクエリに対応
- (Processor-Session)session.use_trans_sidがOnかつクライアントがCOOKIEを無効にしている時にセッションIDが2重に付加される問題の修正, セッションが開始されていない際にリンクやリダイレクト先URIにセッション名が付加される問題の修正
- (JS)Sabel.Environment追加
- (JS)Sabel.Class追加
- (JS)Sabel.KeyEvent追加
- (JS)Sabel.Number追加
- (JS)Ajaxアップローダ追加(apc必須)
- (JS)Dateピッカー(カレンダー)追加
- (JS)ElementにgetRegion(), replaceClass()メソッド追加
- (JS)Sabel.Util.UriをSabel.Uriに変更
- (JS)Element.getDimentionsメソッドのバグ修正
- (JS)Effectのバグ修正
- (JS)要素の絶対位置取得メソッドのバグ修正
- (JS)現在のスクロール量取得メソッド追加
- (JS)Stringクラスの大幅な改善(sprintf, htmlspecialchars, chr, repeat追加など)
- (JS)Ajaxのオプションにtimeout, scope追加
- (JS)Eventにscope追加
- (JS)widget.Overlayの改善(Widget.Overlayにリネーム)
- (Task)各種ジェネレータ追加
- (Task)バッチ実行ファイル(sabel.php)で正常なアプリケーションルートパスが定義されない問題の修正
- (Addon-Form)Formオブジェクトが保持するHTMLを書き出すオブジェクトの初期化に不具合があったのを修正, モデルにバージョンカラムがある場合にclose()メソッドで</form>タグとともにバージョン値をhiddenで書き出すように対応
- (Addon-Renderer)ショートタグ形式の場合にHTMLエスケープするようにSabelレンダラを改善, SabelレンダラのHTMLタグを抜き出す正規表現を最小マッチに変更
- (Lib-Paginate)setOrderColumn()メソッドをsetOrderColumns()に変更, setDefaultOrder()メソッド追加, uriの指定を省略可能に改善
- rewriteモジュールがロードされていない場合にInternal Server Errorになる問題の修正
- Scaffold(sabelコマンド)にlangオプション(ja)追加
- $_SERVER["HTTP_HOST"]を参照している箇所を$_SERVER["SERVER_NAME"]を参照するように変更

June 15, 2008
Javascript - mouseover, mouseout
Javascriptでdivなどのmouseoverやmouseoutイベントを扱う際、そのdivが他の要素を内包していると厄介な挙動をする。
IEでは以前からonmouseenterやonmouseleaveがあるようで、これを使うとbやcは関係なく、aに入った時と出た時だけイベントが発生してくれる。
他のブラウザでも同様の振る舞いを実現するにはそれ用の実装をする必要があるが、Sabel JS(Sabel 1.1以降)だと以下のようにできる。
<div id="a" onmouseover="alert('over');" onmouseout="alert('out');">
<div id="b">
<div id="c"></div>
</div>
</div>
例えば上記のように、a内にbがあり、b内にcがある状態で、aのmouseover, mouseoutイベントでアラートを表示すると、マウスがb上に移動すると一度outし直後に再びoverする。c上に移動した時も同様。IEでは以前からonmouseenterやonmouseleaveがあるようで、これを使うとbやcは関係なく、aに入った時と出た時だけイベントが発生してくれる。
他のブラウザでも同様の振る舞いを実現するにはそれ用の実装をする必要があるが、Sabel JS(Sabel 1.1以降)だと以下のようにできる。
<div id="a">
<div id="b">
<div id="c"></div>
</div>
</div>
<script type="text/javascript">
var a = Sabel.get("a");
a.observe("mouseenter", function(){ alert('over'); });
a.observe("mouseleave", function(){ alert('out'); });
</script>
動作の様子はこちらで。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