明日は OSC2008 Fukuoka ですよ

明日なのでふたたびポスト。

杖をついてよぼよぼ歩いているのが僕です。当日お会いする方、よろしくお願いします。
勉強会勉強会にてPHP in Fukuokaの人として、LT とパネルディスカッションにでます。こちらもよろしくお願いします。

PHP でアノテーション 2

PHP でアノテーション - devworksの続き。

/*
 * @Required
 *
**/
public $var;

と書いておけば、「annotation」ディレクトリの「Required.php」を include_once して new Required() します。
構想としては、上記のように書いておけば Action 実行前に必須チェックを行う。SAStruts みたいな感じですね。次回はアノテーションを取得して Validation を行うところをやろうと思います。
…先にフロントコントローラから作ればよかったな…。
以下ソース。S2PHP5 のソースを参考に作ってます。感謝感謝。

<?php
include_once 'AnnotationFactory.php';
class AnnotationReader {
    /**
     * ReflectionClass
     * @var ReflectionClass
     */
    private $clazz;
    public function __construct($name) {
        $this->clazz = new ReflectionClass($name);
    }
    public function getClassAnnotation() {
        $comment = $this->getDocComment($this->clazz->getDocComment());
        return $this->getAnnotation($comment);
    }

    public function getMethodAnnotation($name) {
        $method = $clazz->getMethod($name);
        $comment = $this->getDocComment($method->getDocComment());
        return $this->getAnnotation($comment);
    }

    public function getFieldAnnotation($name) {
        $field = $clazz->getProperty($name);
        $comment = $this->getDocComment($field->getDocComment());
        return $this->getAnnotation($comment);
    }

    private function getDocComment($docComment) {
        return preg_split("/[\n\r]/", $docComment, -1, PREG_SPLIT_NO_EMPTY);
    }

    private function getAnnotation($comments) {
        $annoObjects = array();
        foreach ($comments as $line) {
            $line = $this->_removeCommentSlashAster($line);
            if (preg_match("/^@\w+$/",$line) or
                preg_match("/^@\w+\s*\(/",$line)) {
                if (preg_match("/^@(\w+)$/",$line,$matches)) {
                    $annoObj = AnnotationFactory::create($matches[1]);
                    $annoObjects[get_class($annoObj)] = $annoObj;
                }
                if (preg_match("/^@(\w+)\s*\((.*)\)/",$line,$matches)) {
                    $annotationType = $matches[1];
                    $items = preg_split("/,/", $matches[2]);
                    $args = array();
                    $argType = array();
                    foreach ($items as $item) {
                        if (preg_match("/^(.+?)=(.+)/s",$item,$matches)) {
                            $key = $this->_removeQuote($matches[1]);
                            $val = $this->_removeQuote($matches[2]);
                            if ($key == "") {
                                // Error
                            }
                            $args[$key] = $val;
                            $argType = AnnotationFactory::ARGS_TYPE_HASH;
                        } else {
                            $item = $this->_removeQuote($item);
                            $args[] = $item;
                            echo count($args);
                            echo '<br />';
                            $argType = AnnotationFactory::ARGS_TYPE_ARRAY;
                        }
                    }
                    $annoObj = AnnotationFactory::create($annotationType, $args, $argType);
                    $annoObjects[get_class($annoObj)] = $annoObj;
                }
            }
        }
        return $annoObjects;
    }

    private function _removeQuote($str) {
        $str = trim($str);
        $str = preg_replace("/^[\"']/",'',$str);
        $str = preg_replace("/[\"']$/",'',$str);
        return trim($str);
    }

    private function _removeCommentSlashAster($line) {
        $line = trim($line);
        $line = preg_replace("/^\/\*\*/","",$line);
        $line = preg_replace("/\*\/$/","",$line);
        $line = preg_replace("/^\*/","",$line);
        return trim($line);
    }
}
class AnnotationFactory {
    const ARGS_TYPE_ARRAY = 1;
    const ARGS_TYPE_HASH = 2;

    private function __construct() {
    }

    public static function create($name, $args = array(), $argType = array()) {
        $annoObj = new $name();
        if (count($args) == 0) {
            return $annoObj;
        }
        if ($argType == self::ARGS_TYPE_ARRAY) {
            if (count($args) == 1) {
                $annoObj->value = $args[0];
            } else {
                $annoObj->value = $args;
            }
            return $annoObj;
        }
        foreach ($args as $arg=>$val) {
            $annoObj->$arg = $val;
        }
        return $annoObj;
    }

}
function __autoload($class) {
    $dir = dirname(__FILE__);
    $file = $dir . DIRECTORY_SEPARATOR . 'annotation' . DIRECTORY_SEPARATOR . $class . '.php';
    if (is_file($file) && is_readable($file)) {
        include_once $file;
        if (class_exists($class, false)) {
            return true;
        }
    }
    return false;
}
?>

フレームワークの名前2

前回のエントリで「Mote」を英訳すると「ちり」という意味ということが発覚。大層しょんぼりしていたところに 我流さんが

MoteGram(読み:モテグラム)も候補で!

フレームワークの名前 - devworks

というコメントをくれました!

Mote(ちり) + Gram(重さの単位) = ちりのように軽いフレームワーク

どうでしょ! いいかんじだと思います! ということで自作フレームワークのコンセプトは「軽いフレームワーク」です。この「軽い」は「軽く作れる」という意味での「軽い」です。現段階では速度的な意味で「軽い」ものを作るというのは目標に置いていません。
まずは、とっつきやすく気のきいたサクサク Webアプリケーションが作れるフレームワークを目指したいと思います。
PS.
我流さんからこんなコメントをいただきました!

おー採用されちゃった!w
「MoteGram(モテグラム)」には、こんな意味を持たせて欲しいです!

・塵ぐらい、そしてグラムの最小単位で図るぐらい軽い
・「モテる男」は細かい所に気を配れて、きさくでとっつきやすくて、身軽な振る舞い!

「気を配れる」ってのはあれこれ機能がつくとかじゃなくて、
自由に細かい所も好きにいじれるみたいな意味合いです。

どげんでしょ?

フレームワークの名前2 - devworks

よいと思います! 特に「モテる男」のくだりがよいですね! 頑張ります!

フレームワークの名前

今作ろうとしているフレームワークの名前ですがmutsukingさんにこんなコメントをいただきました。

自作するんですか!?
体力・気力がいりそうですが、無理なさらずに(^^)

で、名前は「モテ」が付くといいと思います〜

PHP フレームワーク構想 - devworks

…! それだ! 僕といえば「モテグラマ」を自称するくらい「モテたいプログラマ」として一部ではへんな評判を呼んでいるので、ここはフレームワーク名も「モテ」を含めるべきだという主張は素晴らしいと思います。
早速エキサイト翻訳で「Mote」で英訳してみたところ、結果が
「ちり」
…もうちょっと考えることにします。

PHP でアノテーション

PHP フレームワーク構想 - devworksのエントリで

Validation は PHP コメントアノテーションを読み込んで自動実行

PHP フレームワーク構想 - devworks

と、書いたのでちょっとアノテーションについて調べてみました。まずは Class/Method/Field のDoccomment を取得してみます。

<?php
/* 
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 * Description of index
 * @author devworks
 * @clazz_ano1
 * @clazz_ano2(hoge)
 * @clazza_ano3(foo, bar)
 */
class AnnotationTest {
    /**
     * @field_ano1
     * @field_ano2(hoge)
     * @field_ano3(foo, bar)
     * @var <type> 
     */
    private $ano;
    /**
     * @method_ano1
     * @method_ano2(hoge)
     * @method_ano3(foo, bar)
     */
    public function anoTest() {

    }
}
$clazz = new ReflectionClass("AnnotationTest");
echo $clazz -> getDocComment();
echo "<br />";
$method = $clazz -> getMethod("anoTest");
echo $method -> getDocComment();
echo "<br />";
$field = $clazz -> getProperty("ano");
echo $field -> getDocComment();
?>

結果はこんな感じ。

/** * Description of index * @author devworks * @clazz_ano1 * @clazz_ano2(hoge) * @clazza_ano3(foo, bar) */
/** * @method_ano1 * @method_ano2(hoge) * @method_ano3(foo, bar) */
/** * @field_ano1 * @field_ano2(hoge) * @field_ano3(foo, bar) * @var */

Doccomment が全て取得できてますね。では「@」の部分だけ取得してみましょう。

<?php
// AnnotationTest は省略

class AnnotationReader {
    /**
     * ReflectionClass
     * @var ReflectionClass
     */
    public $clazz;
    public function __construct($name) {
        $this->clazz = new ReflectionClass($name);
    }
    public function getClassAnnotation() {
        $comment = $this->getDocComment($this->clazz->getDocComment());
        $this->getAnnotation($comment);
    }

    public function getMethodAnnotation($name) {
        $method = $clazz->getMethod($name);
        $comment = $this->getDocComment($method->getDocComment());
        $this->getAnnotation($comment);
    }

    public function getFieldAnnotation($name) {
        $field = $clazz->getProperty($name);
        $comment = $this->getDocComment($field->getDocComment());
        $this->getAnnotation($comment);
    }

    private function getDocComment($docComment) {
        return preg_split("/[\n\r]/", $docComment, -1, PREG_SPLIT_NO_EMPTY);
    }

    private function getAnnotation($comments) {
        foreach ($comments as $line) {
            $line = $this->_removeCommentSlashAster($line);
            if (preg_match("/^@\w+$/",$line) or
                preg_match("/^@\w+\s*\(/",$line)) {
                echo $line."<br />";
            }
        }
    }
    private function _removeCommentSlashAster($line) {
        $line = trim($line);
        $line = preg_replace("/^\/\*\*/","",$line);
        $line = preg_replace("/\*\/$/","",$line);
        $line = preg_replace("/^\*/","",$line);
        return trim($line);
    }
}

で、以下みたいに実行すると

$ano = new AnnotationReader("AnnotationTest");
$ano->getClassAnnotation();
@clazz_ano1
@clazz_ano2(hoge)
@clazza_ano3(foo, bar)

とれましたね。S2PHP5 のソースをぱくったのでここまでは OK。あとは取得できたアノテーションと同名のクラスをインスタンス化しなければなりません。S2PHP5 の場合はコンテナから取得しているのですが、僕のフレームワークでは DI の予定はないので __autoload を実装して annotation ディレクトリにいるアノテーションと同名のファイル名を探して includeしようと思います。
というわけで残りは次回。

PHP フレームワーク構想

最近色々なフレームワークを触っているのですが*1、どれもしっくりこないので、自分で作ってみようと思います。

  • MVC モデル
  • PHP5 専用
    • もう PHP4 に対応する理由がない
  • フルスタック
    • でも M/V/C は疎結合。別々に使えるようにする
  • 設定ファイルは PHP
  • 配列は極力使用しない
    • CakePHP の Validation で心が折れかけたので
  • __set, __get は使用しない
    • 普通に setter/getter を書いてコード補完させる。typo のリスクを軽減させる。そのかわりジェネレータを作る
  • __call, __autoload は使う
  • プラグイン機構を採用
  • RESTful な URL
    • mod_rewrite を使用しなくても動くようにする
  • Validation は PHP コメントアノテーションを読み込んで自動実行
  • Model
    • あんまり凝ったものを作らない
    • PDO 一択
      • もう他はいらないと思う
    • MySQL/PostgreSQL 対応
      • アダプタが自作できるようにする。
    • DbUtils + insert/update/delete の SQL 自動生成。select は外だしされたファイルを読み込む感じ(Entity名_sql.phpとかいう規約を定める)
  • View
    • POHTML(でも拡張子は .php ) + binding する PHP ファイルの組み合わせ。Mayaa みたいな感じ。 id 属性でバインディング(title 属性がいいよな、と思いつつも一意制限ができないので id 属性で)
  • Controller
    • メンバ変数名 == リクエストパラメータ名の場合は自動でセッティング。ActionForm 相当は今のところ考えてない。
    • メンバ変数にアノテーション定義すると Validation が Action 実行前に動く
    • Controller に定義されている funciton 毎に Validation を実行するかどうかが設定可能

随時書き足していきます。
あ、あと、大事なこと。名前! なんて名前にしよう…。
今月ぼんやり設計して来年から本格的に書き始めよう。

*1:Ruby on Rails, CakePHP, ZendFramework等

オープンソースカンファレンス2008 Fukuoka

に参加します。自分が忘れないために参加するセミナーをメモ。
セミナープログラムは下記 URL を参照。
オープンソースカンファレンス2008 Fukuoka - Event Reserve

  • 10:15-11:00
    • CubbyでRESTfulなWebアプリを
  • 11:15-12:00
    • AIPが目指す「これからのIT人材」育成 〜AIPと天神・大名WiFiの活用法〜
  • 13:00-13:45
    • 特にみたいセミナーがないのでそのへんでぼんやりしてるかも
  • 14:00-14:45
    • 仲間同士のコミュニティサイト(SNSエンジン、OpenPNE)の紹介
      • 見ないかも
  • 15:15-17:00
    • 【特別企画】勉強会勉強会(2コマ連続)
      • 今年はたいした活動をしていないので恐縮ですが、壇上に上がることになっています。すみっこで大人しくしている予定。

勉強会勉強会は他のみなさんがすごくためになる話をする予定ですので、時間のある方はぜひご参加下さい。