2012年12月28日金曜日

今年読んだ技術本:2冊だけかい・・・

コードの複雑さを上げずに「世界の複雑さ」と戦うために読んでおきたい良書5選【2012年のインプットlog-和田卓人】を読んで、

今年、2012年に読んだ本は?
と調べてみた。

Amazonの購入履歴をみれば、すぐわかる・・・

いい時代だ・・・

クリック、クリック・・・

たったの2冊 orz


少なすぎだ。


ちなみに去年(2011年)は8冊。
それなりに読んでいる。

だいたい読んだはずだけど、あまり内容を覚えてないなぁ。
それも問題だが。

ということで、来年は、もう少し本を読もう。
体系的に、効率的に、自分の実力を強化するには本を読むのが一番いいと思う。


ちなみに、今年購入した本は次の二冊。
(アフィリエイトリンク付き)

達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ


今すぐ必要ないけれど、少しずつ読んでおこうと思った本。




NOSQLの基礎知識 (ビッグデータを活かすデータベース技術)


ざっくりと、最近の話題について行こうと思った本。
深く理解できたとは思えないが、言葉に慣れた気がする。




で、感想は?


さて、ここでの問題は、ほとんど感想をかけないことだ。

本を読んでも、身になってなければ意味がない。
ノウハウ的な本なら、必要なときにパラパラめくればいいのだろうが。

来年は、本を読んだらブログに内容を書く。
という目標を立てよう。

この目標のために本を読まなくなった、てな事になりませんように。

2012年12月23日日曜日

PHP: 2012年のオレオレ=自作フレームワーク

2012年は2つも、フレームワークを自作してしまった。
俗にいう「オレオレ」である。
来年の抱負を考えるに当たり、一度まとめてみる。

AmidaMVC


恥ずかしながら、まともにMVCフレームワークを使ったことがなかった。ルーティングやらコントローラーぐらいのコードは読んではいるけど、理解しているとは言いがたい。じゃ、一度作ってみるかと…

最初は1ヶ月ぐらい開発して雰囲気がつかめたら終了と思っていたが、意外と面白く、書き直しも含めて数ヶ月近く作ってた気がする。

これを開発しながらPHPStormの使い方を覚えた。
またBEAR.Sundayなどのブログを読みふけっていた。

特徴はChain of Responsibility(CoR)というデザインパターンをフロントディスパッチャーとして使ってること。Request、Routerなどもモジュールの一つと捉えて、モジュールを次々と呼び出す。CoRは極めて柔軟性が高くて、使うモジュールを途中で差し替えたりできる。

もう一つの特徴は、マークダウンのファイルを、そのまま読み込んで、ヘッダー・フッターを付け加えたりできる。

さらに、テンプレートも裏に入っているので、githubからcloneして、中身をマークダウンで書けば、綺麗なサイトができる。エディターまでつけてしまって、もうフレームワークというよりCMSな感じになった。

そしてファットなフロントエンドが出来上がった。
ちょっと太りすぎで、今ひとつな感じになってしまった。

WScore


今、開発中のフレームワーク。
というかデータマッパー。

Doctrine2にフォーム生成をくっつけた感じ。

なので厳密にはフレームワークとは言えないかも。

目標は、

・今まで使っていたライブラリとの互換性を(ほんの少し)残す、
・「Cenaデータ転送エージェント」に対応する、
・できるだけ簡単かつ少ないコードで、
・最近学んだ「正しい」オブジェクト指向で開発する、
などで、個人的な内容が多いかな。

オレオレだから仕方がない。

開発して一ヶ月で意外と早く動き出した。その後、DCIに目移りして一ヶ月ほど寄り道したりしたけど、ただ、動くのと使いやすいのは違っていて… ある程度、動き出してからの修正に時間がかかってる。テスト書いていてよかった、と身を持って経験できた。

今は、多対多のリレーション絡みが少し抽象化が弱くて、Cenaに対応させるのに苦労しそうな感じです。とは言えサンプル・デモでは、Cenaをスキップさせることで動いてしまいましたが。

あとは1+N問題の対応とか、本格的に使うには
もう少し時間が必要そう。

2012年12月22日土曜日

PHP:2012年はPHPStormとBEAR.Sundayの年

年末なので自分のPHP生活を振り返ってみます。
2012年はPHPStormというIDEを使い始めたこと、そしてBEAR.Sundayに出会えたことが大きかった。

これにより開発スタイルやOOPに関する理解が深まりました。
というか、今まで一人で=独学で開発してました。
それによる限界を感じてたのですが、少し超えることができたとおもいます。

PHPStorm

もうすでに一度書いていますが、2012年1月からPHPStormをPHP開発のIDE(統合開発環境)に使い始めました。もっと早くから使っておけばよかったと後悔しています。

Eclipse、netbeansと使ってましたが、今ひとつ馴染めず。
とにかく重くて遅くて。
ちょっと修正するのにも一苦労。
その割には、デバッガーを設定しても一ヶ月もすると動かなくなるし。

それに比べると・・・

さて、PHPStormを使い始めて、やっと分かったことがあります。

・PHPDocsは便利。
・Interfaceは便利。

使うと開発が楽になるから、PHPDocsとinterfaceを使うようになりました。

難しい理屈で考えるより、よいIDEを使って開発すれば
返り値のタイプがあると自動補完で便利だな、
複数のクラスを返す場合でもInterface使えば使えるな…
いや、開発が楽です。

BEAR.Sunday

どこで、出会ったのか記憶にないのですが・・・
おそらくDependency Injectionを調べている時に見つけたのだと思います。

単にDIだけでなく、
オブジェクト指向で大事なのことは何か?
を考えるようになりました。

全てはリソース(Because Everything is A Resource)の言葉通り、リソース思考を極めた革新的なフレームワークですが、一つ一つの要素技術を見ても、そして開発理念も、とても勉強になります。

もう有名になった「PHP: Dis Is It」とか、読んでるだけで元気になってきます。そんな人が作っているフレームワークですから、人を元気にするフレームワークです。

2012年12月13日木曜日

BEAR.Sunday 0.Go.Go インストールログ

BEAR.Sundayの最新パッケージがリリースされたので、インストールしてみました。バージョンは0.GO.GO(0.55)だそうです。

まずはgithubのレポジトリへGo!
そして書いてあるとおりに作業する!

git clone git://github.com/koriym/BEAR.Package.git cd BEAR.Package composer intallphp scripts/check_env.php

すると、NGの印が・・・

まずはデータベースから。
check_env.phpの中を覗くと、DBはmysqlで、環境変数からDB接続に使うユーザー名とパスワードを取得していました。では早速設定して。。。

export BEAR_DB_ID='admin'export BEAR_DB_PASSWORD='*****'

もう一度走らせるとOKに変わりました。

もう一つのNG、apc.enable_cliは無視して先に進むことに。。。

Built-In Web Server

を走らせてみます。
これも書いてあるとおりの作業でした。

chmod -R 777 apps/Sandbox/datacd apps/Sandbox/public/php -S localhost:8088 web.php

ブラウザーで http://localhost:8088 アクセスすると、見慣れたBEAR.Sundayのトップ画面が表示されました。あっけないぐらい簡単に動きました。

で、さり気なくクリックした「Web IDE≫」で腰を抜かすなど。


ブラウザーだけでソースコードが見れて、しかも修正もできそうだ。

とはいえ、デバッガーで追いかけたいので、ローカルのPHPで動かそう。
とか思っていたらPHPのビルトインサーバーでもデバッガーが動いた。xdebugからの出力をポートで受けるので関係なかったのか…

コードリーディング

さて、一時間ほど追いかけたが、ひとまずここまで。
理解したとは到底言えない状態だけれども、
大きくくくれば、
1)ゴニョゴニョとセットアップ、
2)ページリソースを取得、
3)HTMLに展開、
となっている事を確認した。
当たり前すぎて泣けてくる。

リソースのコード自体はBEAR.ResourceにあるResource.phpで、ほとんどDSLのようにマジックメソードを使って、あちこちの機能を呼んでいる。もっともクラスとしては比較的に小さくて分かりやすいと思う。

リソースの本体はSandbox以下にあるResourceフォルダーに入っている。ココらへんで、半年近く前に追いかけた内容を、ようやく思い出してきた。

最初にPageリソースが呼ばれ、その中からAppリソースが呼ばれる。

次に調べたいのは、
・DIコンテナがどこで設定されているか?モジュールかな?
・リソースとコンストラクション、
・$appって何だろう?
あたりから始めよう。

やっと眠くなってきたので、おやすみなさい。

2012年12月6日木曜日

PHPで「かっこういい」HTMLタグ生成クラス

2012年PHPアドベントカレンダー6日目です。

ちょっと前に、HTMLフォーム要素を動的に出力する必要が出てきて、調べても使いやすそうなクラスが見つからなかったので自分で作ったものです。

思った以上に「格好良い」ものができたので、広報してみることにしました。

ソースコード:https://github.com/asaokamei/Tags

コンストラクション


$tags = new Tags();

依存性ゼロです。簡単に使えます。

簡単なサンプル


早速、簡単な使い方の例を見てみます。
echo $tags->a( 'link' )->href( 'tags.php' );
// <a href="tags.php">link</a>

なにか、いい感じですよね!?
タグをネストしてみます。

$ul = $tags->ul(
  $tags->li( 'list #1' ),
  $tags->li( $tags->img()->src( 'img.gif' ) ),
  'just a text'
);
echo $ul;
/*
<ul>
  <li>list #1</li>
  <li><img src="img.gif" /></li>
  just a text
</ul>
*/

htmlタグと同じように使えます。

なおimgの場合はタグで囲まないようになってます。

基本的な使い方



使い方は、
最初にHTMLタグ名のメソード名にして、
後はアトリビュート名をチェーンしてゆきます。

$tags->tag( '中身' ) -> attr1( val1 ) -> attr2( val2 );
// <tag attr1="val1" attr2="val2">中身</tag>

基本、タグ一個につき、オブジェクト一つ作ります。

imgやinputなどのタグは、終端タグを出力しないようになっています。

少し複雑なサンプル


最初にTagsオブジェクトを作っておいて、後から中身を追加することもできます。複雑になりがちなoptgroupのあるSelect文を作ってみます。

$lang = array(
//  array( value, option name, optgroup ), ...
    array( 'zhi',  'chinese',   'asia'   ),
    array( 'jpn',  'japanese',  'asia'   ),
    array( 'kor',  'korean',    'asia'   ),
    array( 'eng',  'english'             ),
    array( 'fra',  'french',    'europe' ),
    array( 'ger',  'german',    'europe' ),
    array( 'spa',  'spanish',   'europe' ),
);
$select = $tags->select()->name( 'language' );
$groups = array();
foreach( $lang as $item ) // ループ languages.
{
    $option = $tags->option( $item[1] )->value( $item[0] );
    if( isset( $item[2] ) ) // has an optgroup.
    {
        if( !isset( $groups[ $item[2] ] ) ) { // 新規 optgroup.
            $groups[ $item[2] ] = $tags->optgroup()->label( $item[2] );
            $select->contain_( $groups[ $item[2] ] );
        }
        $groups[ $item[2] ]->contain_( $option );
    }
    else {
        $select->contain_( $option );
    }
}
echo $select;
/*
<select name="language">
  <optgroup label="asia">
    <option value="zhi">chinese</option>
    <option value="jpn">japanese</option>
    <option value="kor">korean</option>
  </optgroup>
  <option value="eng">english</option>
  <optgroup label="europe">
    <option value="fra">french</option>
    <option value="ger">german</option>
    <option value="spa">spanish</option>
  </optgroup>
</select>
 */

中身を追加する際は、contain_メソードを使います。

タグがオブジェクトのままなので、後から好きなだけ中身を追加できます。
昔書いたoptgroupのコードに比べて、短くすっきりできました。

オブジェクトなので


後から中身を強制的に変更することもできます。


$input = $tags->input()->type( 'check' )->name( 'checkMe' )->value( 'Yeap' );
echo $input;
$input->walk( function( $tags ) {
    if( isset( $tags->attributes['name'] ) ) $tags->attributes['name'].='[]';
} );
echo $input . "\n";
/*
<input type="check" name="radioMe" value="Yeap" />
<input type="check" name="radioMe[]" value="Yeap" />
 */

walkというメソードは、変数内のTagsオブジェクトをすべて走査して、クロージャーを適用します。これが理由で、多くのプロパティはpublicだったりします。

例は、nameアトリビュートに「[]」を追加して、配列に変えています。

コードについて少し解説


中身はどうなっているのか?
PHPのマジックメソードを使っています。

public function __call( $name, $args )
{
    // attribute or tag if not set.
    if( is_null( $this->tagName ) ) { // set it as a tag name
        return new static( $name, $args );
    }
    else {
        $this->setAttribute_( $name, $args );
    }
    return $this;
}

と言っても、それほど変なことはしてなくて、

  1. 自分自身にtagNameが設定されていなければ、新しくオブジェクトを作ってタグ名を設定して、返しています。
  2. tagNameが設定されていれば、アトリビュートとして追加して、自分自身を返しています。


なかなか気に入ったものが出来ず、試行錯誤しましたが、一番最初のメソード名をタグ名と考えることに気がついたことで使いやすくなりました。欲しいHTMLと同じ順番でコードをかけるので、迷わず使えます。

文字列への変換する部分です。

public function __toString()
{
    return $this->toString_();
}

オブジェクトが文字列に変換する際には、__toStringメソードが呼ばれるので、そこでHTMLタグとして文字列を返しています。

この先のコードは・・・
出力をキレイにしようとすると、コードが汚くなる不思議。

参考サイト


php-XML_Builder
php-class-html-generator

使っている技法は「メソードチェーン」と呼ばれるものですが、DSL(ドメイン・スペシフィック・ランゲージ)を作っているという考えかたもあるようです。

それで検索して見つけたのが、このプレゼンテーション
使い方は、まだ簡単になりそうですね。

2012年12月2日日曜日

ウェブでのInteractionとContextの実装例

前回前々回に書きなぐったDCIの続きです。
あのあと試行錯誤しながらコードを書いているうちに、自分なりに納得できるところまで来ました。ということで、もう1ヶ月以上コーディングしてるんですね。なお、今回は実際に動かしました。

参考コード: https://gist.github.com/4185350
実際に動かすには、他のオブジェクトが必要です。

interactで登録フォーム



インターアクションを使った登録フォームのコードです
動きも今までと同じ、ただしメソードも含めて
  1. get form : フォームを表示。
  2. post form : フォームからの登録を読み込む。
  3. get confirm : トークンをつけて確認画面を表示。
  4. post save : トークンを確認したらデータを保存して、完了画面を表示。
という処理を行います。

ここにあるContextというのは、処理のまとまり、なわけですが…

コンテキストってなんでしょう?
日本語だと文脈なのですが。

このコードでは、
  • データ(エンティティ)に操作して何らかの処理を行う、
  • アクションがコンテキストに対応しているかどうかを判断する、
というルーチンをコンテキストと呼んでます。

今の実装だと、contextXXXからの返り値がfalse以外の場合はコンテキストに対応するので、ビューを表示します。

一方、返り値がfalseの場合は(なんか処理は行ったかもしれないけれど)コンテキスト外なので次の処理に進みます。

contextFormAndLoad


例として、contextFormAndLoadの処理を簡単に説明すると、
  • 必ず一回はフォームを表示する(trueを返す)。
  • アクションが$formと一致する(trueを返す)。
  • アクションが$formと一致しメソードがポストの場合は$_POSTからデータを読み込んでエンティティに書き込む(コンテキストと関係なく必ず処理されます)。
  • 必ずデータはバリデートする。
  • バリデーションで問題があった場合(trueを返す)。
返り値がtrueの場合、コンテキストに対応するということで登録フォームを表示します。複雑ですね。

そのコードです
複雑怪奇ですね。

最初に納得したとか書きましたが…
自分でしか理解できないコードな気がします。

また、このブログを書きながら、コードを見なおしてみて、切り分け方が今ひとつだなと感じてるし… また後で、書きなおす予定。

Interactionクラス


さて、今回のコードの基本がInteractionクラス。セッションに自分自身の状態を保持・読み出します。エンティティも最初に一度作り、あとはセッションから読みだします。

フォームを一度だけ表示する、データを二重に保存しない、という機能を実現するためにpinPoint/checkPinという簡単なメソードを使うことで実現しました。$this->checkPin( 'save' );とすれば、一度だけsaveの状態を実行できます。

なお、ピンだけだとCSRF対策としては弱いと思うので、
別にトークンを使ってます。

ココらへんをInteractionクラス側に隠したことで、処理フローを制御しているのが$actionのみ(に見える)ため、前回よりコードが(少しは)見通しが良くなったと思ってます。

大事なのは、汎用性が高く、使いやすくて、バグのない、コンテキストをたくさん用意できるか?という事になりそうです。

2012年11月5日月曜日

複雑なInteractionを関数で実装する

先週は、dci(data, context, and interaction)の、特にインターアクションという
言葉からの連想で15行程度の関数を書いた。

そこから、継続とかの難しい言葉を覚えて、更に書き換えていたのだが…
難しい。

ウェブサイトのページをコントロールするので、
ページの順番、入力のバリデーション、DB登録の制御、特にCSRF対策、などをきっちりと行おうと思うと、それなりに複雑になってしまった。

せいぜいif文で2レベルの深さにも関わらず、だ。

で、そのコード。
関数、ではなくて、あるクラス内のメソード。

entityというデータについて、
 ・form1:フォーム1の表示、load1:入力の読み取り、バリデーション、
 ・form2:フォーム2の表示、load2:入力の読み取り、バリデーション、
 ・confirm:確認画面の表示、
 ・save:そしてDBに登録する処理。

ただし確認画面の表示で、入力内容が気に入らなければ、フォーム1あるいは2を再表示して再入力して、入力に問題がなければ確認画面に戻る、という仕様を実装してみた。
(まだ実行してないので動くかどうかは知らない)
($viewやcontextとは何かとかも気にしない)


/**
 * @param string $control
 * @param view $view
 * @return \view
 */
function entityAdd( $control, $view )
{
    // get entity
    $entity = $this->restore( 'entity' );
    $state  = $this->getState();
    if( !$state ) {
        $entity = $this->contextGet( 'entity' );
        $this->register( 'entity', $entity );
        $this->setState( array( 'form1', 'form2', 'confirm', 'save', 'done' ) );
    }
    $role = $this->applyContext( $entity, 'loadable' );
    // form1
    if( $control == 'form1' || $state == 'form1' ) {
        $this->nextStateIf( 'form1' );
        return $view->showForm1( $entity );
    }
    // load1
    if( $control == 'load1' ) $role->load( 'load1' );
    
    if( !$role->verify( 'load1' ) ) return $view->showForm1( $entity );
    // form2
    if( $control == 'form2' || $state == 'form2' ) {
        $this->nextStateIf( 'form2' );
        return $view->showForm2( $entity );
    }
    // load2
    if( $control == 'load2' ) $role->load( 'load2' );
    
    if( !$role->verify( 'load2' ) ) return $view->showForm1( $entity );

    if( $control == 'save' && $state == 'confirm' ) $this->nextState();
    
    // confirm
    if( $state == 'confirm' ) {
        return $view->showConfirm( $entity );
    }
    
    // save
    if( $state == 'save' ) {
        $role = $this->applyContext( $entity, 'active' );
        $role->insert();
        $this->nextState();
    }
    // done
    return $view->showDone( $entity );
}

複雑な理由は、制御変数が2つもあるから。
ひとつは$state、セッションに保持していて「〜〜まで実行」を制御する。
一方は、$control、フォームからの入力値で「〜〜から実行」あるいは「〜〜を実行」を制御する。

ステートが出てくるので、ステートパターンの出番だと思うが、
最初の発想が、処理が複数のクラスの分割されてしまって理解が難しくなる、というOOの問題の提起があって、対処方法として関数で逐次処理するという提案だった。なので、複雑なのでOOでクラスに分割して・・・では解決にならないw

とはいえ、理解しやすいコードとは到底言えない。

2012年11月1日木曜日

PHPUnitからPostgreSQLを使ってテストしたら「FATAL: remaining connection slots are reserved...」

PHPUnitでPostgreSQLを使ったテストを書いてみた。
個々のテストは動くのだけど、テストを一つにまとめてSuiteにすると

FATAL:  remaining connection slots are reserved for non-replication superuser connections
とエラーが続出した。

Googleで調べてみると、Rails、Hibernateなどでも似たような症状が報告されていたけど、解決法が見つからない。

「max_connectionを増やす」と良い、
と書いてあったのを見かけたが、増やすと今度は別のエラーで通らない。というかPostgreSQLが起動しなかったので論外。

9.0から9.2系にアップグレードしたけど、効果なし。
phpPgAdminが未対応で、そこでまた修正が出てきたり。

しかたがないのでPHPUnitの書き方を変更して対応。
単に、setupでPdoを生成してたのを、setUpBeforeClass内で行うように変更した。staticメソードなので、ちょっと修正は必要だったが。

class TestUsingPgSql extends \PHPUnit_Framework_TestCase {
   function setUp() {
        $pdo = new \Pdo( 'some dsn' );
    }
}

class TestUsingPgSql extends \PHPUnit_Framework_TestCase
{
   static function setUpBeforeClass() {
        $pdo = new \Pdo( 'some dsn' );
    }
    public function setUp() {
    }
}

単独でもsuite内で走らせても、問題なし。

しかし、PostgreSQLを使ったテストは3ファイルのみ。
もっとテストが増えてきたら対応できるのだろうか?


2012年10月27日土曜日

MongoDBインストール(Mac OS X Homebrew)

homebrewでmongoDbインストールしてみた。
参考:http://www.milligramme.cc/wp/archives/2994



brew install mongodb
==> Downloading http://fastdl.mongodb.org/osx/mongodb-osx-x86_64-2.2.0.tgz
######################################################################## 100.0%
==> Caveats
To have launchd start mongodb at login:
    ln -s /usr/local/opt/mongodb/*.plist ~/Library/LaunchAgents/
Then to load mongodb now:
    launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.mongodb.plist
Or, if you don't want/need launchctl, you can just run:
    mongod
==> Summary
/usr/local/Cellar/mongodb/2.2.0-x86_64: 20 files, 170M, built in 15 seconds

と一発でインストール完了。
続いて、

mongod &
mongo

> j = { name: "mongo" };
{ "name" : "mongo" }
> db.thingy.save(j);
> db.thingy.find();
{ "_id" : ObjectId("508b83edb38f2b4b29878958"), "name" : "mongo" }



動いている。

次はPHPからMongoDBを動かしてみる。
参考:http://blog.ville.jp/2012/07/18/1058

sudo pecl install mongo
php.iniに次の項目を追加。

[mongodb]
extension=mongo.so

phpinfo()で確認。

ここまで簡単でした。
しかし、上の記事ではMongoDBが散々に書かれてますねw

DCIを調べてみて、MVCについて思ったこと

Data, Context, and Interaction (DCI)というのが流行っているらしい、と聞いて調べてみた。なにか「とてつもなく面白そう」なので、何か書いて見る。

全然理解はできていないので、あくまで連想したことです。

MVCは1リクエストに対して1リスポンス

自分の理解では、ウェブ・アプリケーションでのMVCとはリクエストに対してリスポンスを返すためのデザインパターンであること。

MVCについては、MOVEやらCが太るとか言われてますが、基本的にはhttpの仕組み上、この考え方は理にかなっていると思う。

ただ、人間が操作する場合を考えると、MVCだと何かが足りない気がする。

非常に単純なケースとして、新規データの登録を考えてみる。
 1)フォームを表示(form)、
 2)入力されたデータの確認画面(confirm)、
 3)データベースに登録(insert)、
という流れで処理が行われていると思う。
実際には、もうちょっと複雑なことが多い気がする。

さて、MVCだと、上の処理を3回に分けて記述する。
一つ一つの処理は理解しやすいのだけど、流れとして見た場合に処理のつながりが追いかけにくい。と思う。

Interactionをコードにする

DCIだと、どうなるのだろう?
こういった処理(インターアクション)をコードとして記述しましょう。
という事らしい。

早速擬似コーディング。
こんなかんじかな。

function add_entity( $action, $ctrl, $view )
{
    $role = $ctrl->getRole( 'entity' 
);
    if( $action == 'form' ) {
        return $view->showForm( $role );
    }
    $ok = $ctrl->verifyInput( $role );
    if( !$ok ) {
        return $view->showForm( $role );
    }
    if( $action == 'confirm' ) {

        $view->setToken( $ctrl->getToken() );
        return $view->showConfirm( $role );
    }
    if( $action == 'insert' && $ctrl->tokenOk() ) {
        $role->insert();
        $role->vanish();
    }
    return $view->showDone( $role );
}

$ctrlや$viewが何かというのは考えないで。

実際に操作する対象は「entity」。
それをRoleで包んでいる。この例だと、単にActiveRecord化してる。


さて、$actionは「form」「confirm」「insert」と進んでゆく。
$view->showForm()からは「confirm」をアクションとして、
$view->showConfirm()からは「insert」をアクションとして、
呼び出す。

そして、すべてのアクションで同じ処理(add_entity)が走る

どうだろう?
これだと何が起きているのか、分かりやすいと思う。
まぁ、ちゃんと動くのが前提ですが。

2012年10月10日水曜日

Interfaceを使ったDIの問題点

PHPのインターフェースを使って、簡単に依存注入するコードを考えました。
が、これぐらい誰かがすでにやっているはず。

と思って探してたら、見つけた。
Symfony2: Dependency Injection Types – Update

これによると、過去にSymfonyでインターフェースを使ったインジェクションがサポートされていたらしい。で、ここのスレッドでの話し合いの結果、問題があるので機能そのものを削除することになった。

何度も読んだが、今ひとつ理解し切れないのだけど、
おそらく、

  1. インターフェースインジェクションを持つクラスAを継承して別のクラスBを作成、
  2. 別クラスBに対して「自分でインジェクション」を行う、
  3. が、SymfonyのDIコンテナがクラスAとBを混乱、
  4. クラスBに対して再びインジェクション実行
  5. 最初に自分でインジェクトした結果が上書き、

という流れのよう。

ようするに、
インターフェースは継承したクラスにも含まれるので、
依存注入のコントロールが難しい、
ということだと思う。
正直に言えば、同じインターフェースで別の依存性を注入するのが問題な気がするが、できるのだから、できるようにしないといけない。

とにかく、見つけてられて、すっきりした。
インターフェースを使った依存性の注入はやめておこう。

2012年9月10日月曜日

PHPのinterfaeを使った簡単なDiContainerを考えた

性懲りもなく、こんなことを考えてます。

DIコンテナは便利なのですが、設定を作るのが面倒です。
クラスのコード内に、必要なオブジェクトを記述して、コンテナがよしなに注入してくれる方法が楽そうです。

BEAR.SundayのDIモジュールがまさにそうです。

ただアノテーションの処理にDoctrineが必要など、それはそれで面倒な感です。そこで、PHPのインターフェースを使った簡単Diコンテナについて考えてみました。

簡単な流れは:
  1. コンテナ内で、オブジェクトを生成。
  2. オブジェクトのインターフェースを検索。
  3. インターフェース名から注入するクラスを決定。
  4. オブジェクトを生成して、適当なメソードに注入。

具体例です。
namespace Database {
  interface InjectDbaInterface {}
  class Dba {...}
}
namespace Invoice {
  class Billing implements \Database\InjectDbaInterface {
    function injectDba( $Dba ) {
      $this->Dba = $Dba;
    }
  }
  $bill = $container( 'Billing' ); // 注入済みのオブジェクト
}
ポイントは:
  • 必要とされる(であろう)Dbaのクラスと同じnamespaceに、注入用のinterfaceを前もって作っておく。
  • インターフェース名は、\name\space\Inject{クラス名}Interface、と決めておくことで、注入するクラスとネームスペースを特定できる。
  • Invoiceのクラス宣言で、注入してほしいオブジェクトのinterfaceを実装(implements)する。
なお、コンテナ側のコードは、こんな感じ。
if( $interfaces = class_implements( $object ) )
foreach( $interfaces as $interface ) { // インターフェース一覧
  if( preg_match( '/^(.*)Inject([_a-zA-Z0-9]+)Interface$/i',
    $interface, $matches ) ) {
    $className = $matches[1] . $matches[2]; // 注入するクラス
    $injector  = "inject" . $matches[2]; // 注入方式は決め打ち
    $injObj = new $className;
    $object->$injector( $injObj );
  }
}
非常に簡単に動く(はず)。
【追記:2012年9月15日】
簡単なサンプルコードを書きました(https://gist.github.com/3725826)
確かに簡単に動きました。

インターフェースを使って依存性の自動解決をするコンテナがないか気になったのですが、ちょっと探したところでは見当たりません。見つけたのはアノテーションを使っているものばかりでした。(例:Seasarの自動バインディング

2012年8月12日日曜日

ssh_exchange_identification: Connection closed by remote host

今までのように、sshでサーバーに入ろうとしたら、怒られた。
ssh_exchange_identification: Connection closed by remote host

最近、何もいじくってないのに。
いや、MacBook Airから鍵認証で入れるようにしたが・・・
サーバー側はいじってないはず。

ググると、いろいろかかれてた。
ひとつずつ、対応してみた・・・

/etc/hosts.deny


まずは、一番怪しそうなこのファイル。
中身を見たら、いろんなサイトが追加されてた。

そういえばdenyhostsをインストールしていた。
アタックしてるサイトのIPアドレスが追加されてる。

えっ、こんなにアタックがあるんだ。
ポート変更しとこう・・・

ちなみに、自分のアクセス元はありませんでした。
これが原因ではない。

/etc/ssh/sshd_config


このなかの「MaxStartups」を設定しておくのがよいらしい。
見たら、コメントアウトされてたので、「#」マークを削除。

リスタートして・・・まだ動かない。

/etc/hosts.allow


まさかね。
これじゃないよね。でも何も書いてないよな。

このページを参考にして「sshd:all」と一行追加。


動いた。


こうなると、今まで動いていたのが不思議だが、

ま、少しセキュリティも向上して、よかった、よかった。

2012年8月11日土曜日

gitのレポジトリをサクラVPSに作ってみた

サクラさんのVPSを借りた理由の一つが、自分のgitレポジトリを持ちたいということ。これでパスワード付きのソースコードだって気にしないで管理できる。

最初はgitosisかgitoliteをインストールする予定だった。
が、Stackoverflowの両者の比較ページにgitだけでレポジトリを作る方法が説明されていた。簡単そうだったので、まずはこれでゆくことにした。

ちなみに、日本語での説明はこのページに
もう書いてあるそのまま。

サーバー側でgitがインストールされているとして、
ssh [user@]server
cd repos/
mkdir test.git
cd test.git
git init --bare

ローカル側でもgitが入っていれば、
cd src/test
git init
git remote add origin [user@]server:repos/test.git
git push
これでレポジトリができて動いた。

ひとつだけ、ローカル側でgit remote add originを走らせると、こんなエラーが出た。
fatal: Not a git repository (or any of the parent directories): .git

原因はgit initを走らせていないこと、だって。

2012年7月28日土曜日

BEAR.Sundayでコンタクトフォームでも作ってみる

BEAR.Sundayをインストールしたので、
簡単なコンタクトフォームでも作ってみることにした。
何しろtwigすら使ったこと無いので、何もかもが手作業・・・

そして、コードが見にくいですね。すいません。
bloggerがmarkdownに対応してほしい。

Contact.phpを作る



sandbox/Resource/Page/Contact.phpとして以下を作成。

<?phpnamespace sandbox\Resource\Page;
use BEAR\Framework\Resource\AbstractPage as Page;
class Contact extends Page{
    public $body = [];
    public function onGet()
    {
        $this->body[ 'message' ] = 'Please fill in the form.';
        return $this;
    }
}

これが「リソースオブジェクト」になる。
namespaceとファイルの場所を一致させる。違うと何かエラーが出た。
ちなみに、これだけだと何も表示されない。
内容は$this->bodyに入る。そしてonGetメソードの最後のreturn $thisで、内容とともに全ての情報をフレームワークに返している。


テンプレートファイルを作成


sandbox/Resource/Page/Contact.tplとして以下を作成。


<!DOCTYPE html>
<html lang="en">
<body>
<h1>Contact Me</h1>

<p>{$message}</p>
</body>
</html>
アクセスすると「Contact Me」と「Please fill in the form」と表示された。

リソースオブジェクトのクラス名からテンプレートファイルを決定(言うなれば拡張子をtplに変えてる)、テンプレートファイルから表示しているようです。

contact  フォームを作る


次はコンタクトフォームを作ってみる。
以下のformをbody内に書き込んでみた。

<form action="/contact" method="POST">
    <input name="X-HTTP-Method-Override" type="hidden" value="POST" />
    <div class="control-group">
        <div class="controls">
            <input type="text" id="title" name="title" >
            <p class="help-inline"></p>
        </div>
        <div class="controls">
            <textarea type="text" id="body" name="body" rows="8"></textarea>
            <p class="help-inline"></p>
        </div>
    </div>
    <input type="submit" value="Submit">
</form>
大事なのは隠しタグでHTTPメソードをPOSTにしてるところ。
SubmitするとonPostメソードが無いと怒られるので、次のメソードをContact.phpに追加してみた。

    public function onPost()
    {
        $this->body[ 'message' ] = 'You have tried to contact me.';
        return $this;
    }
まさにHTTPメソードがオブジェクトのメソード名に一対一に対応しているのがよくわかります。アクセスすると、メッセージが「コンタクトしてくれたね」みたいな感じになったので動いてますね。結構、簡単です。

フォームの内容を受け取る


onPostの引数に「$title」と「$body」を追加。
    public function onPost( $title, $body )
すると、フォームの内容を受け取れました。

簡単すぎる。
どうやって動いてるのだろう?

本来なら、フォームを受け取ったら、データをモデルに渡して処理を行ってもらうのが正しいと思います。Blogのサンプルコードを見ると、次のようにして処理を受け渡せるようです。
$this->resource->post
        ->uri('app://self/blog/contact')
        ->withQuery(['title' => $title, 'body' => $body])
        ->eager->request();
この場合、Resource/App/Contact.phpクラスを作り、その中で実際の処理を行うことになります。

今回はサンプルですし、メールを出すだけなので、ここではPageのクラス内で処理してしまいます。mail_jp( $mailto, $title, $body );とか書くだけですし。(サンプルなので実際にメールを送信はしません。)

Page内で処理を行うべきか?
行うべきではないですが、まぁいいか、と思う場合はありますよね?

エラー処理をPageとTemplateに加える


フォームと言えばエラー処理。
必須項目の処理を加えました。
エラー表示も他のテンプレートを参考にして修正。

結果:

sandbox/Resource/Page/Contact.php:
<?php
namespace sandbox\Resource\Page;
use BEAR\Framework\Resource\AbstractPage as Page;
class Contact extends Page
{
    public $body = [
        'title' => '',
        'body'  => '',
        'error' => [ 'title' => '', 'body' => '' ],
    ];
    /**
     * @return Contact
     */
    public function onGet()
    {
        $this->body[ 'message' ] = 'Please fill in the contact form.';
        return $this;
    }
    /**
     * @param $title
     * @param $body
     * @return Contact
     */
    public function onPost( $title, $body )
    {
        $this->body[ 'message' ] = 'You have tried to contact me.';
        $this->body[ 'title' ]   = $title;
        $this->body[ 'body' ]    = $body;
     
        if( !$title ) $this->body[ 'error' ][ 'title' ] = 'missing title';
        if( !$body  ) $this->body[ 'error' ][ 'body'  ] = 'missing message body';
     
        if( $title && $body ) {
            // mail_jp( $mail_to, $title, $body );
        }
        return $this;
    }
}


sandbox/Resource/Page/Contact.tpl:

<!DOCTYPE html>
<html lang="en">
<body>
<h1>Contact Me</h1>
<p>{$message}</p>
<form action="/contact" method="POST">
    <input name="X-HTTP-Method-Override" type="hidden" value="POST" />
    <div class="control-group {if $error.title}error{/if}">
        <div class="controls">
            <input type="text" id="title" name="title" value="{$title}">
            <p class="help-inline">{$error.title}</p>
        </div>
    </div>
    <div class="control-group {if $error.body}error{/if}">
        <div class="controls">
            <textarea type="text" id="body" name="body" rows="8">{$body}</textarea>
            <p class="help-inline">{$error.body}</p>
        </div>
    </div>
    <input type="submit" value="Submit">
</form>
</body>
</html>

これでエラー処理が出来ました。


もっとも、メール送信が出来た場合の処理が不十分です。

送信できた場合、今のままだと同じテンプレートを読み込んでしまいます。新たにコントロール変数を追加して、表示内容を変えてもいいのですが、間違った対応方法な気がします。

別に「送信完了、ありがとう」ページを作って、そこに飛ばすのが一番簡単でしょう。

あるいはPageオブジェクト内で、処理の結果により読み込むテンプレートを変更できるとよいのですが。今のところ可能なのかどうか、どうやるのか、さらにはそれが正しい処理なのか、分かりません。

2012年7月23日月曜日

MacにPHP5.4をインストール

PHP5.4を使いたくなった。
最初はVirtualPCインストールしてUbuntuでLAMP環境作ったが、動くのはいいが開発には今ひとつな感じ。

homebrewを使ってインストール


まずは、本体のMac OS X Lionにhomebrewを使ってインストールすることにした。参考にしたのは「Mac OS X Lion: Homebrew, PHP54, MySQL and PEAR」で、ほぼ書いてある通り。


brew tap josegonzalez/php
brew update
brew install php54


多少、あれが無い、これが無いと言われた。
が正確には、あれが無いのでこうしろ、という親切なメッセージなので英語さえ理解できれば簡単に対応できそう。

さ、動いた。
phpinfoを見ると・・・インストールされてない。

pear絡みでエラーが出てるようだ。
Xcodeを4.3.3にアップデートしろと書いてあるので、アップデートしようとしたら・・・何もしてくれない。

仕方が無い。

Macで一番簡単にPHP5.4をインストールする方法


を試してみた。
書いてある通りにcurlをコピペして待つこと5分ぐらいか。
何も応答がなくなったので、心配になったが、ちゃんとインストールしてくれた。
localhostで確認したらphp5.4.5になっていた。


2012年7月21日土曜日

BEAR.Sundayミートアップ#0でLTしました

BEAR.Sunday meetup#0 (2012年7月19日開催)で
LTしてきました。

タイトルは
「Cena-DTA:リソース指向データ転送プロトコル」



Cena-DTAとは2009年の後半から考え始めて、2010年の春頃から開発を始めた技術です。一応、動くところまで持ってきましたが、開発方針で行き詰まってしまい、この一年ほど開発が止まってました。

BEAR.Sundayのmeetup#0に参加すること決めてから何かひらめく物が無いか考えてました。適当なキーワードを散りばめたツイートを書いたり…

そんなとき、LTしませんか?というツイートが!


即、応募しました。
何となく感じてたことを言葉にするいい機会です。

BEAR.Sundayとは関係ない話なのですが、
「気にしないで」という@koriymの言葉に励まされてLTしてきました。

珍しい内容だと思うので、何かの参考になればと思います。

リソース指向とURI


個人的には、
BEAR.Sundayは、全てをURIで表している、という話が面白かったです。

URIというのは間違った理解かも知れません。
ページも含めて、全てを表現するディレクトリ風階層構造、とでもいうのでしょうか。パッと見、URIのような記述

一方Cena-DTAは、ばっさりURIを切り落としてます。
少なくとも細かなリソースごとにURIを用意することはしてません。

これはアプリケーションのモデルが違うからかもしれません。
何しろ「クライアントデバイス上にしか存在してないデータもリソースとして考える」という無茶ぶりで理屈を構築してみたもので。


ともあれ、いまだ興奮冷めやらぬmeetupでした。

2012年7月17日火曜日

UbuntuでJavaが走らない(Error: could not open `/usr/lib/jvm/java-6-sun-1.6.0.26/jre/lib/amd64/jvm.cfg')

Ubuntuで快適に仕事していたが、
突然PhpStormが動かなくなった。

直接端末からPHPStormを起動したらJavaのエラーメッセージが出た。

Error: could not open `/usr/lib/jvm/java-6-sun-1.6.0.26/jre/lib/amd64/jvm.cfg'

そこで、端末から
java -version

と打ってみたら同じエラーが出た。
Javaのインストールエラーだ。

適当に修正



ちょっとググったが、ぴったりのがなかったので、
適当にcdしながら追いかけてみた。
cd /usr/lib/jvm/java-6-sun/jre/lib/amd64

何のことはない、上記のファイルはリンクになっていた。
jvm.cfg -> /etc/java-6-sun/jvm.cfg
リンク先を見ると、java-6-sunというディレクトリがない。
代わりに「java-sun」というのがある。

そこで
sudo ln -s /etc/java-sun/jvm.cfg .

としたら治った。

ググるより、自分で解決したほうが早そうな、珍しいパターンでした。

壊れた原因は・・・


思いつくのは、さくらインターネットさんでVPSを借りた。
Ubuntuをインストールするには、VNCクライアントが必要で、それを動かすためにJREをインストールした覚えがある。それで壊れたのか。

些細な原因ですが、いちいち面倒だな。
その点、WindowsやMac/OS Xとかは、きちんとしてそう。
その代わり有償だし色々と不自由だけど。

2012年6月20日水曜日

データベースでテーブル一覧を取得する方法

もう複数のDBについてまとめられてました。

◆PostgreSQLがなかったので追加。

ちなみに、このページから取得しました。
SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = 'public'


2012年6月14日木曜日

PHP:COMでMSワードにアクセスするメモ

仕事がらみでPHPを使ってMS-Wordの中身を抜き出す方法を調べたメモ。

PHP:COMモジュール

PHPの標準で、MS-Wordを直接扱えるCOMモジュールが提供されている。
COMというのはOLE2の基盤技術のことで、アプリケーションから別のアプリケーションを呼び出して使うためのプロトコル(?)らしい。余り詳しくはないので、間違っているかも。

ともあれ、COMモジュールを使うには、
・OSがWindowsで、
・MS-Wordがインストールされている、
のが条件。
PHP:COMモジュール自体もウィンドウズ版でのみ提供されている。

自分の環境は、
Windows7(64bit)、MSOffice2003、XAMPP、という条件で動いた。

php.ini設定

php.iniの設定で、COMを使えるように変更する必要がある。
ここの実行時設定にあるディレクティブを、
デフォルトのままコメントを削除したら動いた。

dcomcnfgの設定

スターとメニューの「プログラムとファイルの検索」に
dcomcnfg
と入力して、dcomcnfg.exeを実行。
すると、こんなウィンドウが開きました。




後は…書くのが面倒なので、こちらの素晴らしいページを参考に作業を進める。

つまり、

1)コンポーネントサービス > コンピューター > マイ コンピューター
のプロパティを表示
2)プロパティタブの「COMセキュリティ」を選択
3)起動とアクティブ化のアクセス許可の既定値の編集
4)IUSR_を追加する
追加ボタン > 詳細設定ボタン > 検索ボタン > IUSRを選択してOK連打

といった感じで動きました。

Bookmarkとの連携

ワードをテンプレートの用に使うことが出来ます。
そのときに利用するのが「ブックマーク」という機能。

HTMLのフォーム要素のような存在です。
ブックマークにも名前があるので、名前を使って要素を指定し、要素の値を読み込んだり書き込むことがPHPから出来ます。

便利そうですよね。

サンプルコード

こんなので動きます。


$word = new COM( "Word.Application", NULL, CP_UTF8 ) or die( 'no word!?' );
$word->Visible = 0;
$word->Documents->Open( 'word.doc' );
// show all bookmarks
$num_bookmarks = $word->ActiveDocument->Bookmarks->Count;
for( $i = 1; $i <= $num_bookmarks; $i ++ ) {
    try {
        $mark = $word->ActiveDocument->Bookmarks($i)->Name;
        $wdMark = $word->ActiveDocument->Bookmarks( $mark );
        $range = $wdMark->Range;
        $range->Text = "#{$i}:$mark";
    }
    catch( Exception $e ) {}
}
$word->Documents[1]->SaveAs( 'word_new.doc' );

結構、問題が・・・

実際に動かすと分かりますが、
すべてのブックマークを読み込めませんでした。

また日本語を書き込むと後ろにゴミのようなものが・・・!

そこそこ動きますが、仕事に必要な精度は出せませんでした。

2012年6月13日水曜日

JavaScriptでBookmarklet


何となく、JavaScriptを使ったBookmarkletを作って見た。
ブックマークにJavaScriptを書き込んで、ページ上で走らせることができる。
参考:Bookmarklet - ブックマークレットを作成する際のポイント

使ったことはあったけど、自分で作ったことがなかったので
ちょっとやってみた。

基本形:

javascript:( function() { var v; /* do it */ })()



  1. 上の無名関数内に実行するスクリプトを書いて、
  2. 改行をとって(つまり一行にして)、
  3. ブックマークのURLの部分に貼り付けると
  4. Bookmarkletになる。


無名関数を使うのは、varで宣言された変数がローカルになるので。
上の基本形を元に作る方が安全と思う。

作ったブックマークボタンを押すと、表示されてるページでスクリプトが実行されます。

いくつか作ってみたので、解説でも。

inputタグの名前を抜き出す。


  1. 全input要素を抜き出して:document.getElementsByTagName("input")
  2. 要素をループして:for(i=0;i<theForms.length;i++)
  3. list変数に書き込んで:list = list + ','+theForms[i].name;
  4. alertとして書き出す:alert( list );

javascript:(function() { /* do something */ var theForms = document.getElementsByTagName("input");var list = '';for(i=0;i<theForms.length;i++) list = list + ','+theForms[i].name;alert( list );})()


inputタグの自動入力

今度はinputタグの名前を抜きだし、値として使ってみる。

javascript:(function() { /* do something */ var theForms=document.getElementsByTagName("input");var list='';for(i=0;i<theForms.length;i++) theForms[i].value=theForms[i].name;})()
開発していて、大量の入力フォームがある時に便利かなと思って作って見た。

想定どおりに動くんだけれど、数字のみにしたい、セレクト・チェックボックスなどに対応して内など、細かな問題が多くて使いづらい。

一行なので修正も動作確認も面倒だし、
Bookmarkletは使いどころが難しい、という感想。

2012年5月16日水曜日

DIを擬人化して理解してみる

Dependency Injection(DI)とか依存性の注入などと言われてもピンとこない。
仕方がないので、実験的に作っているAmidaMVCというPHPフレームワークでDIコンテナを自作してみました。

まぁDIとはこういうものかな?
というのを擬人化して説明を試みた結果です。

◆ DI以前のオブジェクトとは

あるオブジェクトが動作するため、別のオブジェクトに依存している状況を「依存性がある」と言います。依存性があること自体は問題はありませんが、
依存性が固定されている
のが問題と考えられます。

これを擬人化すると
「俺は、こいつと永遠に一緒だぜ」
となるのではないでしょうか?

コードで書くと

class foo {
  protected $love = NULL;
  function __construct() {
    $this->love = new \My\Love();
  }
}

というところでしょう。

一途というのはいいものだけれど、融通がきかない。
これを何とかするのがDIです。

◆ DI(依存性を注入する)

ようは
「誰とでもOK、注入してして」。
という軽さを売りにするのがDI。

コードにすると、

class foo {
  protected $love = NULL;
  function injectLove( $love ) {
    $this->love = $love;
  }
}

こういうのを汎用性が高いと言うのでしょう。
テストしやすい、というのがDIを正当化するのによく言われます。

ただ実際には使いづらい面があります。

◆ IoC(Inversion of Control)

何しろオブジェクトを作るには何かを注入してやらなければいけない。
独り立ちではなく、逆に誰かに依存してしまいます。

これを擬人化すると、
「一人だと何も出来ないんです。誰か注入してください」
と言う「ひ弱な僕」というのがピッタリな表現でしょう。

このように、使う方から使われる側へとオブジェクトの実装を変更することを
制御の反転(Inversion of Control)
と呼びます(多分)。

◆ Container(コンテナ)

こんなひ弱なオブジェクトたちにはママが必要です。
「あ~、坊やにはこれを注入してあげるからね」
これをコンテナと呼びます。
いろんな作り方があるけど、例えばこんな感じ?

class DiMom {
  function loveFoo() {
    $foo = new foo();
    $foo->injectLove( new \I\Love\You() );
    return $foo;
  }
}
オブジェクトの生成を一手に担い、適切なオブジェクトを注入して依存性を解決してあげる。そんなBig Motherのようなクラスをコンテナと呼ぶのでしょう。

実際には、コードでゴリゴリ書くのではなく設定できるようにすると思いますが。


◆ Service Locator(SL)

以上、色々書きましたが、未だにSLとコンテナの違いがよく分かってません。似ているようで違う、というか、多分自分の書いたコンテナはSLの事な気がしています。いや、何が違うのだろう?

思うに、オブジェクトがコンテナに依存している状態をSLと呼ぶのではないでしょうか?

自分が開発しているフレームワークはChain of Responsibilityを使っていて、必要に応じてモジュール(オブジェクトのことです)を呼び出します。
正確に言うと、ある処理を行うのに必要なモジュールを判断するモジュールがあって、そのモジュールがチェーンに必要なモジュールを追加します。
フレームワークは順番にオブジェクトを生成・実行するだけですが、生成部分にコンテナを使いますが、これだと多分SLという区分になる気がします。

また理解できたと思ったら書いてみます。


◆ 参考文献

色々読んだけど、今ブックマークしてあるもののみリスト。


2012年5月7日月曜日

Dependency Injection とリソース指向について

GW最後の夜、ストレッチをしながらDependency Injection(DI)について考えてみた。DIについて調べ始めてまだ3ヶ月の新人なので、未だによく分からないことだらけだが。

DIってリソース指向と似ているのでは?

と思った気がした。
生成するオブジェクトをリソースとして捉えると、
  1. リソース(オブジェクト)の生成
  2. リソースの操作(依存性の注入)
という手順が成立する。

リソースの操作っていうのは、要するに設定の代入と依存性の注入になる。

そこで、さらに手順を細分化してみると

  1. 生成するオブジェクトを特定する
    new、Singleton、staticなど生成方法を含めて一意に特定する
  2. 設定の注入
    オブジェクトにconfigを代入する
  3. 依存性の注入
    任意のオブジェクトを注入可能とする

という手順になるのかな。

◆ オブジェクトを特定するDIN表記

まずはオブジェクトを特定する記述について考えてみる。
こんな感じかな。
['DIN' => [ $className, $type, $idName] ]
ここで
$classNameは生成するクラス名。
$typeはnew、get、static、のどれか。意味は明白だよね。
$idNameは前に生成したオブジェクトを指定するID名。省略可。英数字ならOKとか。

ちなみにDINはDependency Injection Notationの略(笑
で、DIN表記と呼んでみよう。

◆ 設定の注入

さっき作ったオブジェクトに対して、設定を注入する。

['DIN' => [ $className, $type, $idName],
'config' => [ 'A' => 'a', 'B' => 'b', ] ]

と表現できるでしょう。

◆ 依存性の注入

他のオブジェクトをDIN使って表しておいて、依存性を注入。


['DIN' => [ $className, $type, $idName],
'config' => [ 'A' => 'a', 'B' => 'b', ]
'inject' => [
    'name1' => [ 'class1', 'get', 'test' ],
    'name2' => [ 'class2', 'new' ],
  ]
]
これで、DIN表記を導入したメリットが見えるのでは。

必ずしもnewしたオブジェクトを代入する必要はなくて、get(singleton)で作ったオブジェクトを使うことができる。あるいは、複数のオブジェクトで共通したオブジェクトに依存する、という場合もあろう。


◆ PHPでの実装を想像してみる

DIN表記は、こうだよね。
$di->din( 'className', 'new' );
$di->din( 'className', 'get' );
あるいは
$di->din( [ 'className', 'get', 'test' ] );
$di->din( [ 'className', 'static' ] );
設定を注入してみよう。
( Di::start() )->din( 'className', 'new' )->config( $config );
とか格好良くない?

で、依存性を注入。
( Di::start() )->din( 'className', 'get', 'test' )
->config( $config )
->inject( 'loader', [ 'myLoader', 'new', '1' ] )
->inject( 'loader2', [ 'yourLoader', 'get' ] );
さらに
( Di::continue() )->din( 'className', 'get', 'main' )
->config( $config )
->inject( 'loader', [ 'myLoader', 'new', '1' ] )
->inject( 'loader2', [ 'myLoader', 'new', '1' ] );
と別のオブジェクトも続けて生成できたら、便利かなぁ?

あ、まだオブジェクトを入手してない。
( Di::continue() )->forge()
->obtain( $obj1, [ 'className', 'get', 'test' ] )
->obtain( $obj2, [ 'className', 'get', 'main'' ] )
->saveAs( 'myTest' );
設定を保存できたりして。
で、いつでも呼び出せる。
( Di::start() )->forge( 'myTest' )
->...


う~ん。
全部を実装すると大変そうだ。

特に最後の設定の保存あたり。多分このAPIでは使えない気がする。
たとえば、注入されるオブジェクトを保存した設定で生成したい、という場合に対応できない。また生成されるオブジェクトの性質が一定になってしまう。常にnewとか。ちょっと使い勝手が悪そうだ。

それとデフォルトの依存性を誰が管理するか、決めないと。
コンテナ側でデフォルトは持たない、設定の保存などはコンテナの責任、として
どういうAPIで実装すると分かりやすいのか?


まぁGWの夜の夢ということですが、
実際に実装しないとDIとは何かすら分からない状態なので、
もう少し調べてみてから実装してみよう。

2012年4月24日火曜日

Kinesis KB700PB-us エルゴノミクスキーボード

腱鞘炎になってからキーボードはエルゴノミクスを使っている。
これで治るわけではないが、手が楽になるので気に入っている。


最近はKinesisのKB700PB-usという、二つに分かれるキーボードを購入した。(右はアフィリエイトリンク)。

日本では手に入りにくいエルゴノミクスな英語キーボード。

ちゃんとしたフルキーボードで、
ファンクションキーもあるし、
レイアウトも比較的普通で使いやすい。

キーの感触も悪くない。
値段を考えるとPFUのハッピーハッキングぐらいは期待したけど、まぁ十分かな。

そしてテンキーがない。
これも大事だと思う。



エルゴノミクスというと、こんなすごいキーボードがある。

自分では使ったことはないけど、昔の同僚が腱鞘炎になったときに、これを使っていた。

これが「使いにくい」らしい。

なんでも「わざと」使いにくくなっているらしい。ゆっくりとしかタイプできないので、腱鞘炎がよくなるとか。

そこまで腱鞘炎がひどいわけではないので、今ので十分と思う。




この前までは、マイクロソフトのカーブしたキーボードを使っていた(右上)。

これでも十分にエルゴノミクスだったと思う。ひどい腱鞘炎にはならなかったし、タイプもしやすい。

ただ日本語配列だったのとテンキーが邪魔に思っていて、新しいキーボードが欲しくなった。

下のは英語キーボードと勘違いして買ってしまったBluetoothキーボード。

これもカーブしていているが、最初はキーの配置が使いづらくてとまどった。慣れの問題と思うが、なれる前にKinesisのキーボードを買ってしまったので分からない。

ほんのちょっと、斜めに打てるだけで、手が楽になるので
エルゴノミクスキーボードを気に入っているのだけど、
あまり選べる種類がないのが痛い。

2012年4月20日金曜日

もっと早くUbuntu+PHPStormの開発環境にすればよかった

ちょっと感動したので、書いてみます。

Ubuntuにしてから1ヶ月ほど。
あまりPHPStormを使う機会がなかったのですが、先ほどデバッグ環境を設定しました。
あまりに簡単で書くことにしました。

なお、ローカルでApache2を動かしているという前提です。

PHPStorm

PHPの開発環境として、PHPStormは最強だと思います。
何が最強か上手に指摘できないのですが、意外と軽いわりに機能が豊富。かゆいところに手が届く、といった感じです。そしてデバッガーがちゃんと動きます。今までEclipse+PDTやNetBeans使ったことはありますが、設定に偉い苦労して、やっと動いたと思ったら次の日になると動かないとか、そんなことが多かった気がします。

一度、PHPStormを使ったら、ヘビーな開発は
PHPStormを使う以外は考えられません。

もっともお仕事の作業ではDreamWeaverも捨てがたく。
仕事だとライブラリやらフレームワークをいじくることはほとんどなくて、逆にHTMLやらフロントをいじくることが多いのでHTML←→ソースビューを行き来できるDreamWeaverは便利だと思います。

xdebug

そもそも過去デバッグが動かなかった原因は、xdebugかもしれません。
Windowsのバージョンにバグが多いのか、だいたい正しいdllを選ばないと動きません。
が、Ubuntuでxdebugインストールするのはとても簡単。

Ubuntuソフトウェアセンターでxdebugを検索、
インストールボタンを押したらインストール完了。
アパッチをリスタートして、xdebugが動いてました。

コマンドで言えば(多分)
sudo apt-get install xdebug
sudo service apache2 restart
で終了。

php.iniの修正

さすがに、これだけでデバッガーは動きませんでした。
ここのPHPStorm用xdebugの設定を見ながらphp.iniを修正。

ターミナルから
sudo vi /etc/php5/apache2/conf.d/xdebug.ini

を開いて(一行しか入ってないや)、
そして以下のように修正。


[XDebug]
  zend_extension=/usr/lib/php5/20090626/xdebug.so
  xdebug.remote_enable=1
  xdebug.remote_port=9000
  xdebug.profiler_enable=1
  xdebug.profiler_output_dir=/tmp/

アパッチをリスタートして、phpinfoでxdebugが動いているのを確認。

PHPStormの設定

最後はここの「何の設定なしでxdebugとPHPStorm2でデバッグ」という少々誇張されたタイトルに書いてあるとおりに設定します。

xdebug用のブックマークを作ること。

個人的には「Start debugger」と「Stop debugger」だけブックマークを作って、デバッグを始めるときにブックマークを押します。

そしてPHPStormでは「赤い電話機アイコン」を押して緑色に変わればOK。
ブラウザーをリフレッシュすると、PHPStormでデバッグが開始されます。

path mapping

以上で、動くと思うのですが・・・
たまに「path mapping」を設定してください、といわれます。

どうも、PHPStormがデバッグするには、xdebugからデバッグ中のファイル名を受け取っているようなのですが、ときどきファイルがどれか混乱するようです。

同じファイル名がある場合など、どのファイルをデバッグするか聞いてくることがありますので、適宜選択してあげるとデバッグが始まります。

So, Happy debugging with PHPStorm!

高解像度の27インチモニターが安くなってきている

アマゾンをウィンドウショッピングしていたら、
高解像度の27インチモニターが安くなっていた。

普通の27インチモニターは、これ以上安くならないだろうと思えるぐらいに安い。
大体2~3万円で27インチの大画面が手に入る。

ただし、解像度は1920×1080という「俗に言うフルHD」
作業するには、もう少し解像度が欲しい。

ひとつ上の解像度で27インチを買おうと思うと、
ちょっと前まで、10万円以上していた。

それが6万円にまで下がっていた。
ほ、欲しい。
でも、去年~今年と散財したからなぁ。

心を落ち着かせる為にDPIを計算してみた。

Monitor        横    縦  インチ  DPI
-----------------------------------
NANAO 21     1600  1200  21    95.2
FullHD 27    1920  1080  27    81.6 
MoreHD 27    2560  1440  27   108.8 

Nanaoというのが今使っているディスプレー。
もう6~7年ぐらい使っている。

ちなみに右がちょっと前の作業環境。モニター3台あるけど、使っているのは左の2台だけ。

この右にある古い19インチを27インチモニターにしたいのだけど、フルHDだと解像度が落ちるので、気が乗らない。
でも、一つ上の解像度ならいい感じだ。

もっとも、アップルのiPadに使われるレティーナディスプレーが300dpiぐらい、それより大きなMacだと200dpiを目指しているというから、まだ半分ぐらいの解像度しかないのか・・・

この春のアップルの発表待ちだな。

下はアフィリエイトリンク。
今のところ、この2機種だけのようです。


2012年4月16日月曜日

PostgreSQLのinitdbと違うロケールでDB作成

Ubuntu上にPostgreSQLをインストールして、
phpPgAdminでログインして、EUC-JPでDBを作成しようとしたら、
ERROR: encoding EUC_JP does not match locale ja_JP.UTF-8

と言われた。
SQL文はこんな感じ。
CREATE DATABASE "example" WITH TEMPLATE="template1" ENCODING='EUC_JP'
古いDBをテストするために、EUC-JPでDBを作成する必要がある。
どうやら、initdbの文字コードと違う文字コードでDB作成できないらしい。検索してみると、結構な書き込みを見つけたので、かなりFAQな問題らしい。

ただし、initdbからやり直すという面倒な方法しか見つからない。

色々探して、initdbの文字コードと違う文字コードでDB作成を見つけて
簡単な解決をやっと発見。

要するに、
CREATE DATABASE "example" WITH
TEMPLATE="template0" ENCODING='EUC_JP'
LC_COLLATE='C' LC_CTYPE='C';
とSQLを発行することで解決した。

ポイントは
1)template0を使うこと。
2)LC_CTYPEをCで指定すること。
の2つだけ。
これでUTF-8でinitdbしていてもEUC-JPでDBが作成できる。

◆ただし、もう一つ、この作業の前に行ったことがある。

先に、こんなページを見つけていた。
PostgreSQL 9.1 installation and database encoding

つまり、OSでサポートされるロケールでなければいけないらしい。

Ubuntuでja_JP.EUC-JPを使用するを参考に調べたところ、
今のUbuntuにはEUC-JPが入っていなかった。

そこで、ja_JP.EUC-JPというロケールをOSにインストールしてから
上記のCREATE DATABASEコマンドを発行している。

どんな順序で動くかはテストしてないので、メモということで。



2012年4月2日月曜日

Chrome: Your Profile Could Not Be Opened Correctly [solved]

Took me more than a week to figure this out...

Problem: 

When using Chrome's sync function using Google's account, Chrome does not shutdown gracefuly, leaving zombie process in Ubuntu OS.

The zombie process, somehow, prevents Chrome to load account data, and reports,
Your profile could not be opened correctly...
When this happens, Chrome fails to load any of the password, cookies, from the past history, even if it is on the local data. This is very annoying.

Find out the cause:

After Googled around, found out how to kill the zombie process:
pkill -9 chrome

Then, found Chrome's history data is causing this, so removing it should help:
rm -rf .config/google-chrome/Default/History*
These worked fine, but when Chrome synchronize the account, the History data came back, and the problem occurs again.

To summarize the situation,
some bad data in History is the cause of this problem,
and the bad data is in the Google Sync (up somewhere),
so Chrome sync always downloads the bad data. 

OK, the solution is
to erase the bad data from Google account.

I tried to erase the sync'ed data from Chrome's Preference menu. But nothing  worked. So, I dived into the Google's maze of account setting pages...

This worked for me:

0) disconnect from Google Account.

Not sure if this is necessary, but I was disconnected when I did the following.

1) login to Google Account Setting. 

I used this url to get into my account setting.
https://www.google.com/settings/

no link, I found so far. 

2) turn off the Google Sync

From the account setting page, click on "Profile and privacy". 
then, scroll down to "Sign in to Dashboard" and click.
find "Chrome Sync", and click on "Stop sync and delete data from Google" or something 
(well I'm a Japanese, and I changed the language to English to write this entry. But this page was still in Japanese. so I cannot be sure how the link is written in English.)
wait for a few minutes according to the message. 

3) delete web history

From the account setting page, click on "Go to web history".
Click on "Remove all Web History", and clicked just yes to whatever the question was. 

4) start Chrome and synchronization

and it worked for me!


The Google account pages were just like a maze. 
So, many of the above may change at anytime. But I hope that this list gives some clue about this problem. 

[additional note: 2012/04/20]

The same problem happened again, about two weeks after I wrote this blog entry. So, I stopped syncing 'address bar history', and the problem went away (for now). 

2012年2月25日土曜日

Simple@高円寺(2012/02/25)

高円寺でSimple勉強会。
今回は人数少なめ。おいしいカキフライ弁当で腹ごしらえして
黙々と作業開始。

◆AmidaMVCという名前について。
多分、MVCは使っていない。

だいたいMVCが分かっていない。


◆最初は1月で開発をやめるつもりだったが、面白いので2月も続けてしまった。で、フレームワークからCMSになりつつある。FWとCMSの違いって何だろう?

◆今はMarkdownを使ったPukiwikiみたい。
これでdiffも出来るようになったし、ほぼ機能はそろった?
自動で登録したファイルを一覧するのが足りないか。

twitterのbootstrap使ってみた。
最初は戸惑ったが、ちょっと触れば簡単。jQuery Mobileのようにマークアップだけでボタンの色・形、そしてモーダルの動きを指定できる。というかクラスを指定すればOK。クラス名だけ調べれば簡単。
ただ途中でbootstrap使い始めたので、競合が起きて面倒だった。

simplediffというPHPのテキストdiffツールを使ってみた。
Pearを試したが、ウォーニングばかりで動かなかったので、こちらを試したら一発で動いた。遅いらしいが、ま速度を気にする段階ではないので。動く方が大事でしょう。

◆アミダ式チェーンは、
開発はしやすい。
ただ、次のチェーンを理解する必要がある。
ので、あまり長くアクションを使い回すのはよくないかも。

◆コンポーネントは汎用性を高くするのは難しい。
CMSごとのコンポーネント群を作って管理するがあるかも。
例:ルーター・管理者機能なし、ファイルの閲覧のみのCMS。

◆自分のコーディングスキルに絶望、まではしないが、残念な気持ちになった。

2012年2月3日金曜日

AmidaMVC:設計方針を決める

さてコントローラーのアミダ式チェーンが出来たので、
今度は中身のフレームワーク。

HTTPのリクエストはコントローラーが受け取り、
コンポーネントを次々と実行する。

で、コンポーネントで何をするか?
まずは、
  1. リクエストから読み込むファイルを探して、
  2. ファイルを読み込んで、
  3. テンプレートに流し込んで、出力。
これが最低必要な流れでしょう。
それぞれ、Router、Loader、そしてRenderという名前のコンポーネントを開発することにしました。
試してませんが、今でもこの3つのコンポーネントだけで動くんじゃないかな?

Router

コントローラーからpath_infoを読み込む。
単純に最後のパスがファイル名で、残りがフォルダー名。
で、探し出せればLoaderにファイル情報を渡す。 
今はルートマップから探索できるようになりました。どちらの方法でもファイルが見つから無ければPageNotFoundにアクションを変更。 

Loader

ファイル情報からファイルの中身を読み込む。
拡張子から中身の種別を判断(HTML、markdown、テキスト、PHPのソースコード)したうえで読み込む。
PageNotFoundの場合は、前もって決めておいたファイルを読み込む。最初、ファイルはLoader内に直書きしてたが、今は設定コンポーネント(Config)で設定するよう変更済み。ファイルが正常に読み込めれば、アクションをデフォルトに戻してRenderに処理を渡す。

Render

中身の種別により、マークダウンならHTMLに、テキストならnl2brとhtmlspecialcharsをかけて、PHPならソースコードハイライトして、と前処理を行う。

それから中身のデータをテンプレートに流し込む。
とにかく簡単に。単なるPHPをインクルードするだけ。

◆$siteObj

コンポーネントで色んなデータを扱う必要があるので、必要な情報は全部このオブジェクトにぶち込んで、コンポーネント間で持ち回します。デザインパターンで言えばData Transfer Objectですな。

◆Toolsフォルダー

コンポーネントが行う実際の処理はここに書く。
コンポーネントは、処理を行うルーチンを呼び出すのが仕事。

こういう汎用的なルーチンは再発明しても仕方ないので、出来るだけ自分で書かないのが方針。実際、『パーフェクトPHP』のフレームワークの章から、ほとんどのコードを書き写しました。とても参考になったいい本だと思います。

理想はsymfonyなどのフレームワークのライブラリを再利用できるといいな。

◆以上、簡潔に方針をまとめると
・コントローラーは単純に、
・コンポーネントは薄く、
・ツールは汎用的かつコピペで書く、
のが開発方針です。

2012年2月2日木曜日

AmidaMVC:Amida式チェーンを思いついた流れ

何故Amidaなんてチェーンを思いついたか?

昔作った簡単なページコントローラーがあったけど、
コードが汚くて使いづらくて。

つい書き直し始めたのが始まりだった・・・

最初は入力の値によって、決まった関数を呼び出すだけ。
次に、ひとつのクラス内のメソードを呼び出すようにしてみた。

ふむ。簡単すぎるぐらいの改造。

それではと、複数のクラスを登録して、
次のクラスを呼び出せるようにしてみた。

何か面白いぞ。

じゃぁ、デフォルトで次のクラスに進んでみよう。
お~、いい感じ。

おや?MVCフレームワークにちょうどいいんじゃない

じゃ作ってみようか。

作ってみた ← いまここ。

Windows 7 買ってVirtualPCでXPを立ち上げてみる

もう10年前のデスクトップが未だに必要なのはXPで納入したお客さんのサポートのため。今回こそは廃棄に回せるようWindows 7 Professionalの入ったノートブックを購入した。

◆まずは作業開始

ノートブックが来て最初にしたのは
avastというフリーのウィルス対策ソフト。
気が利いてることに一緒にChromeをインストールをしてくれる。

それからOSのアップデート。
半日近くかかったがダウンロードして再起動。
それを2~3回繰り返したら最新の状態になったようだ。

◆Virtual PC/XP Modeのインストール

スタート→全てのプログラム→Windows Virtual PCの
Windwos XP ModeをクリックするとMicrosoftのページが立ち上がる。

そこからXP Modeをダウンロードするんだけど、
RunしてOpenして・・・
どうにも動かない。

そうだ、Chromeだからだ。
IEで同じURLにアクセスしたら、なるほど、こういう意味だったのか。

Windows XP Modeをダウンロードして実行して、
しばらくするとXPが立ち上がった。パスワードを設定して・・・

あれ?Virtual PCはダウンロードしなくても動くようだ。

◆ファイルの共有のセットアップ

XPのコントロールパネルから
ネットワークセットアップウィザードを立ち上げて設定。
懐かしいねぇ。最初に見たときはすごいと思ったけど、今見ると面倒なだけ。OSも進歩している。

が、ファイルを共有してくれない。
XPの設定を色々いじくったけど、駄目だ。

これはホスト側のネットワークの設定を変更することで解決。
デフォルトでは「共有ネットワーク(NAT)」になっているので、
ここにホストPCのLANカードを指定して動きました。

◆ホストPCとのファイル共有が遅い

ホストPCからネットワークを開いて、VirtualXPの共有フォルダーへ、30MBほどのファイルをコピー。時間が30分とでた。何これ。遅い。

原因と対策はここに一緒に書いてありました。

しかし。
面倒だ。

別のPCからVirtualXPの共有フォルダーへコピーしたら、
早い。問題なし。

これなら使えそうだ。

何とかなりそうです。

AmidaMVC:アミダ式チェーンとChain of Responsibilityパターン

Amida式Chainのコードです。


function dispatch( $action, &$data=NULL ) {
    // set current action.
    $return = NULL;
    $this->_dispatchAct = $action;
    $this->setAction( $action );
    $this->fireStart();
    // -----------------------------
    // chain of responsibility loop.
    while( $this->moreModels() )     {
        $this->fireDispatch();
        $action = $this->getAction();
        $return = $this->execAction( $action, $data, $return );
        if( $this->useNextComponent() ) {
            // go to next component. 
            $this->nextComponent();
        }
        else {
            $this->useNextComponent( TRUE ); // reset to TRUE. 
        }
    }
    // -----------------------------
    return $return;
}


内部に$this->_componentsという配列にコンポーネントを登録しておいてwhile文でループしているだけです。

コンポーネントの扱い

nextComponent()でarray_sliceをして、
次のコンポーネントに進みます。つまり、後へは戻れません。

ただしコンポーネント内で別のコンポーネントを登録できます。
例:prependComponent( 'appHello', 'app' );

これで自分自身の次に実行するコンポーネントを追加します。
その他、チェーンの最後に追加する、チェーンを終了するする、指定したコンポーネントまでスキップする、などが出来ます(まだAPIで全てに対応はしてませんが)。

アクションの扱い

$actionはコンポーネント内で必要に応じて変更されます。それ以外は変わらず同じアクション(つまり同じ名前のメソード)を呼び続けます。

Chain of Responsibilityパターン

Amida式チェーンは、Chain of Responsibility (CoR)パターンの一種だと思ってます。WikipediaでのCoRのページを見てもよく分かりません。

自分の理解では、何かを達成するのに複数のオブジェクトを使う、複数のオブジェクトを使うためにAPIを共通にする、そして実行するオブジェクトの順番を適宜変更できるようにする、というパターンだと思ってます。

なのでCoRの実装方法はいろいろあると思います。
多分、LithiumというPHPのフレームワークは、各オブジェクト内で次に呼ぶオブジェクトを指定しているようです。リンクトリストみたいですね。

それに比べるとAmida式チェーンは比較的単純だと思います。実行するオブジェクト(あるいはクラス)を最初に配列で持っています。一方、途中でオブジェクトの順番を変えたり、呼び出すメソード名を変更できるのが特徴です。


2012年2月1日水曜日

AmidaMVC:appHelloを書いてみよう

今度はちゃんと俗に言うModelみたいな書き方を。

「demo/hello」フォルダーを作ります。
その下に「_App.php」ファイルを作って、



<?php
$_ctrl->prependComponent( 'appHello',  'app' );
class appHello extends \AmidaMVC\Component\Model
{
    static function actionDefault(
        \AmidaMVC\Framework\Controller $ctrl,
        \AmidaMVC\Component\SiteObj &$siteObj  )
    {
        $html = '<h1>Hello Application</h1>' .
            '<p>Hello World from Application</p>';
        $siteObj->setContents( $html );
    }
}

と書き込みます。

次のURLにアクセス。
http://your.host.here/pathto/AmidaMVC/demo/hello/

your.host.herepathtoは適宜環境に合わせてください。
すると、


と表示されます。

今度はたくさんのルールがあります。

_App.php

アプリケーションは_App.phpファイルにあるという規約になってます。

自分自身をコンポーネントとして登録

Componentとして自分自身をチェーンに登録する必要があります。
AmidaMVCのコントローラーが$_ctrlで取得できるので、prependComponentメソードを使って登録しています。

$_ctrl->prependComponent( 'appHello',  'app' );

ここにクラス名(appHello)を入れます。
この時点では、Loaderコンポーネント内で動いています。次に実行するコンポーネントとしてクラスを登録することになります。
正直、これは格好悪いと思いってます。クラス名を固定にしてコントローラー側で自動で読み込ませようかと思ったのですが、規約を増やすより、自分でコード書いた方がいいかな、と思って。というのは嘘で、ここでViewも読み込ませたいのですが、二つのクラスを上手に読み込ませるよい規約を思いつかなかったからです。

actionDefaultメソード

Loaderがファイルを読み込んだ後、appHelloクラスを実行します。
このURLだとアクションはデフォルトになります。なのでactionDefaultメソードが呼ばれることになります。

変数は3つあります。
  • $ctrl:AmidaMVCのコントローラーオブジェクト。
  • $siteObj:ウェブサイトに関するデータを収集するオブジェクト。
  • $data:前のコンポーネントから受け渡されるデータ(必要なければ無視)。

HTMLを返す

結果のHTMLは$siteObjに登録します。
これがRenderに運ばれて、テンプレートなどの処理を行います。

$siteObj->setContents( $html );


個人的な感想・・・

ちょっと書いてますが、もっと規約を増やして自動で処理する項目を増やしたほうがいいかもしれません。

  • たとえばRenderが受け取る$data(3番目の変数)は常にコンテンツにするとか。するとappあるいはviewメソードの最後にHTMLをreturnすればRenderが受け取ってコンテンツとして処理してくれるとかです。
  • それから、コンポーネントの登録を自動で行うとか。
  • _initメソードを前もって呼び出すとか。

フレームワークは作っていて楽しい理由は、ここら辺でしょうね。
やりすぎて使いにくくならないようにするのが難しいのかもしれません。

AmidaMVC:Hello Worldを書いてみよう

何はともあれ「Hello World」
一番簡単な方法からです。

マークダウンでHello World

AmidaMVCを落としてくるとdemoフォルダーがあります。
この下に「hello.md」というファイルを作ります。

ここに
#Hello World
Hello to AmidaMVC.  
と書きます。

そして次のURLにアクセス。
http://your.host.here/pathto/AmidaMVC/demo/hello.md

your.host.herepathtoは適宜環境に合わせてください。
すると、最初のHello World画面が出るはず。


demoフォルダーの下に置いたのは、AmidaMVC直下だとルータマップが効いてしまって直接ファイルを指定してもアクセスできないためです。
中身のマークダウンファイルをHTMLに変換しています。

PHPでHello World?

次は「hello.php」を作ってみましょう。
「demo/hello.php」ファイルを作って、

<?php
echo 'Hello World from AmidaMVC';
?>
と入力して、アクセス。すると、どうでしょう。
PHPのソースコードが出てきました。


面白いでしょう?

現状でPHPを走らせるには・・・

「demo/hello.html」ファイルを作って、こう書きます。
<h1>Hello World</h1>
<?php echo '<p>Hello World from Html</p>'; ?>
と入力して、アクセスすると・・・


ちゃんとPHPとして動きました。

スキャンモードで各種ファイルを表示

AmidaMVCは要求されたURLを元にファイルシステムをスキャンできます。
ファイルを見つけたら、拡張子から中身を推測、HTMLに変換して、テンプレートに放り込んだ上で表示します。

ルートマップもあります。ルートマップに引っかかったファイルを優先して読み込みます。

対応している拡張子は:
  • マークダウン(md、markdown)
  • HTML(html)
  • テキスト(text,txt)
  • PHP ソースコード (php)
があります。
ソースコードを表示する機能は、ちょっとgitHubみたいで気に入っているのですが、さすがに変すぎるので、PHPファイルなら実行して、HTMLとして表示する予定です。

2012年1月31日火曜日

AmidaMVC:ComponentとAction

AmidaMVCが何をしているのか?

一言で言えば、コンポーネント(部品)を次々と実行するだけ。
格好良くコンポーネントと言ってますが、要するにクラスです。

コンポーネントの登録

たとえば、こんなコードです。


// Controllerを作成、
$ctrl = new \AmidaMVC\Framework\Controller();
// コンポーネントを追加。
$ctrl
    ->addComponent( 'Config', 'config' )
    ->addComponent( 'Router', 'router' )
    ->addComponent( 'Loader', 'loader' )
    ->addComponent( 'Render', 'render' )
;
// フレームワーク実行
$data = new \AmidaMVC\Component\SiteObj();
$ctrl->start( $data );

最初にSiteObjとControllerのオブジェクトを作成してから、
コントローラーにコンポーネントを追加してゆきます。

これらのコンポーネントを順番に実行してゆきます。

クラスなので、実際に走らせるにはメソード名が必要です。
最初は「actionDefault」というメソードを呼び続けます。つまり正常に動いている場合の処理をこのメソード内に書きます。

上の例だと、
・Config:設定用クラス。必要そうな値を設定。
・Router:URLから読み込むファイルを決定する。
・Loader:見つけたファイルを読み込む。
・Render:テンプレートを呼び出してHTMLを出力する。
という処理を順番に行います。

アクション

実行するメソード名は、アクションと読んでいます。
実際はactionAction、最初に「action」をつけて呼び出します。
例えばactionHelloとかです。

もしアクションが存在しない場合。
デフォルトのアクション「Default」を呼び出します。
つまり「actionDefault」のメソードが呼ばれます。

エラー処理の例

途中でエラーが起きたとします。
たとえばRouterでファイルがみつからなかった場合について考えてみます。
  • Routerでファイルがみつからない。
    この場合、Routerはアクションを「_PageNotFound」に変えて、次のコンポーネントに処理を渡します。
  • これ以降、「_PageNotFound」が呼び出されます。無ければデフォルトのアクションを実行します。
  • さて、Loaderの「_PageNotFound」のアクションでは、設定で指定していたファイル(PageNotFound.md)を読み込みます。
    この時点で、表示に必要なデータはそろったことになるので、正常系に戻します。つまり「actionDefault」を呼ぶようにコントローラーに命令します。
  • Renderはファイルが読み込まれているので、正常系の処理と同じになります。
とまぁ、こんな感じで、エラーがあった場合に異常系の処理をして、処理が終われば正常系の処理に戻したりが出来ます。これでフレームワークの設計が簡単になるんじゃないかと思った訳です。

2012年1月30日月曜日

AmidaMVC:今更PHPマイクロフレームワーク作ってみた

ちょこちょこ書いてますが、PHPのmicroframework作ってみました。
今更の感覚が強いですが、
  • 勉強のため、
と思って作ってみました。

でも勉強のためだけで作ることは無かったですね。
実は
  • アミダ式Dispatcher(と勝手に命名)
というロジックを思いついてしまって。
それをMVCフレームワークに応用したら面白そうだなと思ったら、作ってみたくて仕方がなくなり作ったという次第です。

ウェブサイトを作っていて、面倒なのがエラー処理。
ページが見つからないなどの処理をきれいに行いたいと思ってました。

アミダ式の説明は後で行いますが、
標準の処理が簡単なだけでなく、エラーなどの異常系の処理も簡単にできるのではないかと思っています。
まぁ自分が初めて考えついたロジックとは思いませんし、誰かがMVCに応用していてもおかしくはない、というか実はこれがMVCの標準的な処理だった、なんてオチはないといいのですが。こういうのは広い知識を持ってないのがつらいところです。

◆特徴は?

せっかく作ったので、何か特徴があるはず…
  • モデルやビューが無くても動く。
    一つのファイルにPHPとHTMLが混在していても、ちゃんとテンプレート内に入るようになります。もちろんクラスを作っても大丈夫。
  • ルートを設定してもしなくても動く。
  • ファイルビューワーとして、マークダウンファイルやテキストファイルをHTMLとして表示することが出来ます。PHPファイルもソースコードとして表示できたりします。
  • ファイルシステムがベース
    特徴なのかどうか分かりませんがデータベースは必要ありません。遅いと思いがちなファイルですが、今後SSDが普及するにつれ十分なスピードが出るようになるのではないでしょうか?すると簡単にファイルで作れるフレームワークも便利かなぁと。
その他、ファイルを直接修正したりすることも(多分簡単に)出来るはずなのですが、こうなるとフレームワークなのかCMSなのか分からなくなるので、適当なところで切り上げないと。

2012年1月17日火曜日

クロージャーを使ったPSR-0準拠のクラスローダー

PSR-0をベースにしたクラスローダーを探したのですが、全てクラスベースのクラスローダーしか見つかりませんでした。でコピーするのが面倒だったので、クロージャーを使ったクラスローダー作ってみました。
こういうのにクロージャーって便利なんですね。
初めてクロージャーをちゃんと使った気がする。
ソースコードはgitHubのGistにて公開しました。
https://gist.github.com/1620960#file_class_loader_ns.php


特徴は、パラメータ無しで動きます。

◆使い方は、たとえば次のようなクラスをロードする場合。
new AmidaMVC\Folder\SomeClass();

対応するクラスのが次の場所だったとします。
pathto/src/AmidaMVC/Folder/SomeClass.class

こういう場合は、pathto/src/AmidaMVC/bootstrap.php
などに上のGistコードを貼り付けてください。

spl_autoload_register( ClassLoader() );
普通、多分、他のコードを見る感じだとクラスをロードするには、ライブラリで使っているネームスペースとパスを指定する必要があります。
この関数は、呼ばれた場所がライブラリの一番上と仮定して、現在のフォルダーをネームスペース(例の場合はAmidaMVC)、そして現在のフォルダーをのぞいたパス(例の場合はpathto/src/)を設定します。

◆ただし、関数の定義が別ファイルにある場合は、先のbootstrap.phpで

spl_autoload_register( ClassLoader( __DIR__ ) );

とすれば動く「はず」です(動作は未確認)。

◆注意

今コードを見たら、バグがありますね。
少なくとも自分の環境では動いているので、ちょっと修正すれば動くと思います。また時間を見つけたら修正します。

2012年1月11日水曜日

[PHP] Chain of Reponsibility (CoR)を使ったDispatcher

なぜかChain of Reponsibility (CoR)を使ったDispatcherをPHPで書いている。今、必要なこととも思えないが、一度始めたら見切りを付けるまで頭から離れない。仕方ないので書いている感じ。

そもそもは、Html5のローカルアプリ開発にJavaScriptのMVCいるよね、という話から始まって、Mediatorパターンいいよと言われたところからスタートした気がする。

で、JavaScriptの簡易MVC・フレームワークを探したら…
簡単に見つかる。
で、コードを読んでも追いかけられない。
モデルが既に存在しているので、Cenaをそのまま使えない。

じゃあ自分で書いてみるか、となって、
MVCを勉強することにした。


MVCモデルって、要はリクエスト(httpの要求ですね)をもとに、
処理するルーチン(関数でもクラスでも)を決めるだけだよね?
それってなんて言うDispatch?
だからDispatchを書き始めた。

◆車輪の再発明だけど

書いているうちに、簡単なのだと使いづらいと思い始めた。
実際にデータを読み込むところをモデルと考えた場合、
その前後にたくさんの処理が必要になる。

例えば認証、キャッシュ。
認証でエラーがあればモデルなんか読み込む必要がない。
キャッシュにヒットすれば、これまたモデルを読み込む必要がない。
一方、モデルの処理中に致命的エラーが起きたら、まったく別の画面に飛ばす場合もあるでしょう。
で、全部OKだったら、読み込んだデータをHTMLに直す処理を始められる。

つまり途中で処理をかえる必要がある。
思ったより、複雑。

◆CoR

で、CoRを使ってみることにした。
これしか使えそうなパターンを知らなかったからだけど、symfonyやstrutsでも使われてるとどこかで読んだので間違った選択ではなかったみたい。

と、この辺りで気づいたのは、
リクエストから処理するルーチンをDispatchする部分(Router)と、
実際に処理を行うルーチンでのDispatchする部分(なんて言うんだ?)と、
別の話だなと。


2012年1月6日金曜日

2012年の抱負

おめでとうございます。
2012年(平成24年)が明けました。

せっかくなので今年の抱負を書きます。

アウトプットを増やす

もともと書くのは遅いと思うので、今年は余り推敲せずに書くことにして、アウトプットを増やしてみたいです。

もう上の一行で何度も推敲してますがw

Cena-DTAを何とかする

ひとまず日本での特許を取得。
今はアメリカで特許の出願済み、まだ取得は出来てません。

今年はCena-DTAを広める動きをしたいです。

また、コーディングも「着々と」進める予定。

ちゃんと仕事する

今まで適当に仕事をしてきたわけではないのですが。

つい後回しにする悪い癖があるので、今年は頑張りたいです。請求書をちゃんと書く、とかですかね。

それと、受注した仕事が中心だったので、今年は少し能動的に仕事したいです。というかCena-DTAに力を入れてみたいです。

個人的なその他の目標

家族サービスは今までどおり(むしろ増やしたい)
勉強会を月2回ぐらい参加する(家族との協議が必要)
体重を5kgぐらい減らす(減らしすぎないようにするw
東日本大震災を受けて

今年の抱負を考えるにあたり、忘れられないのが東日本大震災。
家族・親戚で直接的な被害をこうむってはいませんが、

「今まで通りでは駄目だ」

という思いをいっそう強くしました。
一昨年のリーマンショック以来感じてたことでもあります。

まだ大きな地震が来るという予想もあります。
いろんなことを見直して、しっかりと生き延びてゆきたいです。