Home > アーカイブ > 2010-10

2010-10

GPSリアル勇者シリーズ「誰がドラゴンを殺したか?」

この記事の所要時間: 113

10/31(日)、京都でGPS携帯を利用したゲームイベント「誰がドラゴンを殺したか?」が開催されます。

このイベントは、「リアル脱出ゲーム」など熱狂的なファンを持つイベントを手がけているSCRAPさんが開催するイベントです。

こちらで利用するWebシステムの開発を1×1が担当させて頂いています。

GPS 携帯をゲームに利用したイベントというのはこれまでなく、さらに、こういったイベントが東京ではなく関西(京都)で開催されるというのは珍しいと思います。

先日、実地試験というわけで京都に行ってゲームを体験してきたのですが、これはなかなか新しい感覚ですね。具体的な内容については書けないのですが、こういった新しいイベントは東京先行が多いので、今回は関西で体験できる貴重な機会になりそうです。

ゲームに参加するには、GPS対応携帯(docomo/au/Softbank)もしくはiPhoneが必要です。つまりGPS必須なイベントというわけです。

そろそろ涼しくなってきて観光にも良い季節になりました。次の日曜日は携帯もって秋の京都にお越し下さい:D

チケットは絶賛発売中なので、下記リンクからお買い求め下さいー。

=> 誰がドラゴンを殺したか|イープラス
=> フリーペーパー:SCRAP:京都全域で配布中 » GPSリアル勇者シリーズ第一弾「誰がドラゴンを殺したか?」

PHPerのためのYAPC::Asia2010

この記事の所要時間: 21

YAPC::Asia2010 の発表資料、動画が面白いので、まとめ。

YAPC=Yet Another Perl Conferenceは、その名のとおりPerlのイベントです。PHPで言うならPHPカンファレンスにあたるでしょうか。(違ったらごめんなさい)

Perl のイベントということで特に気にしていなかったのですが、先日行われたYAPC::Asia2010の発表資料を見ると PHPer が見てもかなり面白いです。もちろんPerlならではの発表もあるのですが、言語を問わずWebシステム開発をするなら参考になる内容が多くあります。

Perl だから、という理由だけで避けるのはホントに勿体無いので是非見てみて下さい。

PHPerにオススメな発表

PHPユーザの自分が見て面白かった発表です。リンク下にある一言は自分が面白いと思った点で、要約では無いです。内容は各発表を見てみて下さい。

来年は

PPTやKeynoteなどいわゆるプレゼンツールを使っていない発表が多いのも特徴的ですね。

こうしてあげてみるとPHPerでも楽しめる発表が多いです。来年は行ってみようかな。

Facebookをスーパーマリオに例えると

この記事の所要時間: 113
  • 登録(復帰)直後は、通常マリオ。ジャンプ力も無いから投稿する内容も無難なものだし、クリボーにちょっと当たるとダメ。
  • 戦々恐々としながら投稿していくと、キノコ(いいね!)やフラワー(コメント)がもらえる。
    => スーパーマリオやファイヤーマリオになると(気持ちの中で)色んなことができるようになる。写真も投げてみたり、他の人にも絡んでいったり。
    => 手にするアイテムによっては、グループやファンページを作ったりも。
  • でも、やっぱりクリボーに当たるとびっくりするくらいすぐに前の状態に逆戻り。イケイケで投稿していたのが、いきなり遠慮気味になったり。
  • 何かの拍子にスターを手に入れれば、無敵マリオに!なんでも来い状態で、バンバン投稿するし、ガンガン絡んでいく。そんな空気は伝播するのか、いいね!やコメントが付きまくり。まさに光り輝いてる状態。
  • でも無敵マリオは長くは続かない。無敵状態を維持するにはスターを取り続けるしかない。
  • だから、誰かが通常マリオで不安げにウロウロしていたら、キノコ(いいね!)やフラワー(コメント)でパワーアップさせて上げましょう。

「クリボーに当たる」というのは「批判」とかより「無反応」ですかね。ここ数日はボーナスステージ状態の盛り上がり方なので、体感してみたい方は混ざっとくと良いですよ。

ちょっと無理矢理感がありますが、まあfacebook面白いよってことで。

スクリプト言語間における「lexical closure」の違い – PHPの場合

  • 2010-10-09 (土)
  • PHP
この記事の所要時間: 045

via. スクリプト言語間における「lexical closure」の違い、それともプログラムの違い? – karasuyamatenguの日記

PHPも5.3からクロージャがあります。クロージャの指定の仕方によって挙動が異なりますが、PHPで書くと以下のようになります。

ループでiを0から4まで回す
ループブロック内で:
iを埋め込んだlexical variable、’localvar’を定義。
localvarを参照したclosureをリストに追加。
5つのclosureの値をプリント

スクリプト言語間における「lexical closure」の違い、それともプログラムの違い? – karasuyamatenguの日記

各クロージャの$localvarが独立しているパターン。

foo0 foo1 foo2 foo3 foo4

各クロージャで$localvarを共有しているパターン。(use で指定する変数が参照渡しになっている。)

foo4 foo4 foo4 foo4 foo4

Lithiumのフィルタシステム

この記事の所要時間: 1624

次世代CakePHPとも言うべきフレームワークLithiumのフィルタシステムを見てみました。

Lithiumは対象がPHP5.3以上ということで、5.3ならではの機能を活用したアーキテクチャになっています。中でも特徴的なのがフィルタシステムです。

全体のアーキテクチャとしては、CakePHPの流れを汲んで標準的なMVCフレームワークになっています。ただそれを実現する手段としてフィルタシステムを多用しています。これまでのフレームワークとは異なる点があり、いざフレームワークの動きを掴もうとすると戸惑います。

そこでLithiumのフィルタシステムをざっくりと見てみましょう。

サンプルソース

サンプルとして、SampleControllerとそのビューテンプレートを用意します。

フィルタの動きを見るだけなので、indexアクションでは、ログに__METHOD__を記録するだけです。

[app/controllers/SampleController.php]

<?php
namespace app\controllers;

class SampleController extends \lithium\action\Controller {
  public function index() {
    \lithium\analysis\Logger::debug(__METHOD__);
  }
}
&#91;/php&#93;
<p>[app/views/sample/index.html.php]</p>

<h1>Sample</h1>

ログをファイル(app/resources/tmp/logs/debug.log)に出力するようにしておきます。

[app/webroot/index.php]

// ログをファイルに出力
\lithium\analysis\Logger::config(array('default' => array('adapter' => 'File')));

echo lithium\action\Dispatcher::run(new lithium\action\Request());

?>

これでブラウザから「http://localhost/sample」にアクセスすると以下の内容がログに記録されます。

2010-09-30 00:11:16 app\controllers\SamplesController::index

フィルタを追加する

では、Dispatcher#runメソッドにフィルタを追加してみましょう。

まず、フィルタをクロージャで定義します。(__invokeメソッドを定義したクラスでも可)

そしてフィルタを追加する際は、applyFilterメソッドを利用します。この際に、第一引数にフィルタを追加する対象のメソッド名を、第二引数にフィルタ(クロージャ)を指定します。

[app/webroot/index.php]

// フィルタを定義
$c = function($self, $params, $chain) {
  \lithium\analysis\Logger::debug('Before');

  // 次フィルタを実行
  $res = $chain->next($self, $params, $chain);

  \lithium\analysis\Logger::debug('After');

  // 次フィルタの戻り値を返す
  return $res;
};

// フィルタを追加
lithium\action\Dispatcher::applyFilter('run', $c);

echo lithium\action\Dispatcher::run(new lithium\action\Request());

フィルタの内容は単純で、lithium\action\Dispatcher::runの処理の前後でログに文字列を出力しています。

これを実行すると以下のログが出力されます。フィルタで定義したログ出力部分が実行されており、フィルタとして機能しているのが分かります。

2010-09-30 00:11:16 Before
2010-09-30 00:11:16 app\controllers\SamplesController::index
2010-09-30 00:11:16 After

フィルタ定義でのポイントは、$chain->nextの箇所です。LithiumのフィルタシステムはChain of Responsibilityパターンになっており、フィルタ自らが次ぎのフィルタを呼び出す構造になっています。

あくまでもフィルタ自身が次のフィルタを実行するかどうかを決定できるので、サンプルのように前後に処理を実行することもできますし、あえて次のフィルタを実行させないということもできます。

CakePHPのフックメソッド(beforeSave()/afterSave()等)をフィルタとして書くというように考えるとイメージしやすいかもしれません。フックメソッドと異なるのは、まずサブクラスと作ってメソッドを継承する必要が無いと言う点があります。さらに大きく違うのはフィルタは複数追加することできるので多彩な処理をプラグインのように加えることが可能という点です。

次は、複数のフィルタを追加してみます。

複数フィルタを追加する

3つのフィルタを追加します。フィルタの内容は単純で、文字列をログに出力するだけです。ただ、どのフィルタから出力された文字列かを識別するためにフィルタごとに番号を追加しています。

ここでは、applyFilterの第二引数に直接クロージャ定義を記載しています。

[app/webroot/index.php]

$no = 1;
lithium\action\Dispatcher::applyFilter('run', function($self, $params, $chain) use ($no) {
  \lithium\analysis\Logger::debug('Before'.$no);
  $res = $chain->next($self, $params, $chain);
  \lithium\analysis\Logger::debug('After'.$no);

  return $res;
});

$no = 2;
lithium\action\Dispatcher::applyFilter('run', function($self, $params, $chain) use ($no) {
  \lithium\analysis\Logger::debug('Before'.$no);
  $res = $chain->next($self, $params, $chain);
  \lithium\analysis\Logger::debug('After'.$no);

  return $res;
});

$no = 3;
lithium\action\Dispatcher::applyFilter('run', function($self, $params, $chain) use ($no) {
  \lithium\analysis\Logger::debug('Before'.$no);
  $res = $chain->next($self, $params, $chain);
  \lithium\analysis\Logger::debug('After'.$no);

  return $res;
});

echo lithium\action\Dispatcher::run(new lithium\action\Request());

実行すると以下のログが出力されます。これを見ると$chain->next前の処理は、フィルタ追加順に実行され、$chain->next後の処理はその逆順で実行されているのが分かります。

2010-10-01 01:20:46 Before1
2010-10-01 01:20:46 Before2
2010-10-01 01:20:46 Before3
2010-10-01 01:20:46 app\controllers\SampleController::index
2010-10-01 01:20:46 After3
2010-10-01 01:20:46 After2
2010-10-01 01:20:46 After1

このように複数の処理を任意に追加できるのがフィルタシステムの特徴です。

フィルタシステム

ここまでフィルタがどのように動作するかを見てきました。

ではフィルタがどのように呼ばれているかを掴むためにフレームワークのソースを見てみましょう。

まずlithium\action\Dispatcher#runメソッドのソースを見てみましょう。

20行弱のソースなのですが、実質は3行しかありません。まず2行でパラメータのセットを行います。あとはstatic::_filter()を実行して終わりです。

lithium\action\Dispatcher#runメソッドとしての実質の処理(MVCの実行)は、static::_filter()の第3引数にあるクロージャの中に記載されています。

[libraries/lithium/action/Dispatcher.php]

  public static function run($request, array $options = array()) {
    // パラメータセット
    $router = static::$_classes['router'];
    $params = compact('request', 'options');

    // static::_filter()を実行
    // クロージャの中身が、本当の処理
    return static::_filter(__FUNCTION__, $params, function($self, $params) use ($router) {
      $request = $params['request'];
      $options = $params['options'];

      if (($result = $router::process($request)) instanceof Response) {
        return $result;
      }   
      $params = $self::applyRules($result->params);

      if (!$params) {
        throw new DispatchException('Could not route request');
      }   
      $callable = $self::invokeMethod('_callable', array($result, $params, $options));
      return $self::invokeMethod('_call', array($callable, $result, $params));
    }); 
  }

では、次はstatic::_filter()のソースです。lithium\action\Dispatcherはlithium\core\StaticObjectを継承しているので、実際に実行されるのはlithium\core\StaticObject#_filterメソッドです。

第三引数の$callbackにlithium\action\Dispatcher#runメソッドで定義したクロージャが格納されています。applyFilterメソッドによってフィルタが追加されていれば、フィルタチェイン(フィルタリスト)の最後に追加され、Filters::run()でフィルタ実行が開始します。フィルタが無い場合は、直接$callbackを実行します。

つまりポイントは、lithium\action\Dispatcher#runメソッドの処理自体もフィルタとして扱われるということです。これによりフィルタチェインを順々に実行すれば、メソッド自身の処理が実行できるというわけです。

[libraries/lithium/core/StaticObject.php]

  protected static function _filter($method, $params, $callback, $filters = array()) {
    $class = get_called_class();
    $hasNoFilters = empty(static::$_methodFilters[$class][$method]);

    if ($hasNoFilters && !$filters && !Filters::hasApplied($class, $method)) {
      // フィルタが空ならそのまま$callbackを実行
      return $callback($class, $params, null);
    }   
    if (!isset(static::$_methodFilters[$class][$method])) {
      static::$_methodFilters += array($class => array());
      static::$_methodFilters[$class][$method] = array();
    }   
    // $callbackをフィルタチェインの最後に追加する
    $data = array_merge(static::$_methodFilters[$class][$method], $filters, array($callback));
    return Filters::run($class, $params, compact('data', 'class', 'method'));
  }

最後にフィルタチェインを実行している\lithium\util\collection\Filters#runメソッドです。

最後の3行で、フィルタチェインの先頭にあるフィルタを実行して終了します。ここではあくまで先頭のフィルタだけを実行しています。それ以降のフィルタを実行するかどうかはフィルタ自身に委ねられています。

[libraries/lithium/util/collection/Filters.php]

  public static function run($class, $params, array $options = array()) {
    $defaults = array('class' => null, 'method' => null, 'data' => array());
    $options += $defaults;
    $lazyFilterCheck = (is_string($class) && $options['method']);

    if (($lazyFilterCheck) && isset(static::$_lazyFilters[$class][$options['method']])) {
      $filters = static::$_lazyFilters[$class][$options['method']];
      unset(static::$_lazyFilters[$class][$options['method']]);
      $options['data'] = array_merge($filters, $options['data']);

      foreach ($filters as $filter) {
        $class::applyFilter($options['method'], $filter);
      }   
    }   

    // 先頭のフィルタを実行
    $chain = new Filters($options);
    $next = $chain->rewind();
    return $next($class, $params, $chain);
  }

フィルタ実行が可能なメソッド

このようにフィルタシステムでは、メソッド自身の処理をクロージャにして、フィルタとして動作させることで実現しています。つまりどのメソッドでもフィルタシステムが利用できるわけではなく、あらかじめそれを想定して実装されたメソッドのみにフィルタが適用できます。

lithium\core\StaticObjectもしくは\lithium\core\Object(同様のフィルタ実行メソッドがあります)を継承しており、メソッド内部でstatic::_filter()もしくは$this->_filter()を実行しているメソッドが対象となります。

具体的には、\lithium\action\Controller、\lithium\data\Model、\lithium\data\source\Database、\lithium\util\Validator、他といったクラスに該当メソッドがあります。クラス名を見るとCakePHPでフックメソッドが定義されていたクラスですね。

フィルタシステムは便利?

Lithiumのフィルタシステムを見てきました。なんとなくイメージできたでしょうか。

フレームワークの主要な箇所はフィルタシステムを利用しているので、ここをおさえておくと処理が追いやすくなります。(反対に理解しないと?になります。。。)

このようなフィルタシステムをフレームワークの中核に利用しているフレームワークは、PHPではあまりありませんでした。使ってみると上手く実装されているなあというのが率直な印象です。利用の仕方では柔軟な実装ができそうなので楽しみです。

ただ実際問題、不満というかマイナスな点もあります。

まず、理解しにくということ。フレームワークのソースを読むならCakePHPのようにフックメソッドになっている方がgrepもしやすいし、一目瞭然で分かります。

次に、フィルタチェインの中身が掴めないということ。フィルタが格納される変数をvar_dump()してもクロージャが入っていることしか分からず、どのフィルタが入っているのかが分かりません。特にlithium\core\StaticObjectのサブクラスでは、フィルタを様々な箇所で追加できるので、どのフィルタがどの順序で入っているかは掴んでおきたいところです。

最後に、フィルタならではの利用方法がまだ見えてません。CakePHPのフックメソッドは実用には十分なものでした。正直、私自身は実用でフィルタならではの有効性がそれほど見えていないところです。

今後、有効な利用方法を見ていきたいを思います。

PHP5.3を体感するフレームワーク

冒頭にも触れましたが、LithiumはPHP5.3の機能が多くの箇所で活用されています。

ここで取り上げたフィルタシステムはまさに5.3の機能を利用しているので、5.3を理解するにはもってこいの教材です。言語仕様と簡単なサンプルソースを触ったあとは、実際に動くシステムを触ることでさらに理解を深めることができます。

PHP5.3を体感したい方は、Lithiumに触れてみるのはいかがでしょうか。

=> Lithium

Home > アーカイブ > 2010-10

検索
フィード
メタ情報

Return to page top