ログ日記

作業ログと日記とメモ

PHPのフレームワークを作った

ソースはここ: https://github.com/nishimura/laiz2

composerの使い方を見るのも兼ねてサンプルアプリを置いたのですぐにインストールできるはず。
https://github.com/nishimura/laiz-sample-task

composer.phar create-project laiz/laiz-sample-task laiz-sample-task
cd laiz-sample-task
mkdir logs cache
chmod o+w logs cache


やりたかったことは
https://github.com/nishimura/laiz-sample-task/blob/master/src/Laiz/Sample/Task/Page/Dir/Information.php
ほぼこのファイルに集約した。

<?php
...
class Information
{
    /**
     * @Converter(["upper", PlusLengthConverter])
     */
    public $plainText;

    /** @var bool */
    public $flag;

    /**
     * @var MyModel
     * @Converter(["name" => "wordseparatortocamelcase",
     *             "value" => ["wordseparatortodash", "upper"]])
     */
    public $model;
...

アノテーションによるフィルターで型変換。
フォームとDBの操作は
https://github.com/nishimura/laiz-sample-task/blob/master/src/Laiz/Sample/Task/Page/Task.php
こんな感じ。

<?php
...
class Task
{
    public $action;
    public $pager;
    public $TASKS;

    /**
     * @var TransactionToken
     * @Validator("transactiontoken.ini")
     */
    public $transaction;

    /**
     * @var Vo_Task
     * @Validator("task.ini")
     */
    public $task;

    /**
     * @Validator("check.ini")
     * @var bool
     */
    public $check;

    public function index(Db $db)
    {
        $iterator = $db->from('Task')
            ->order(array('subject', 'taskId'))
            ->iterator();

        $pager = new Pager($iterator, 5);
        $this->pager = $pager->getHtml();
        $this->TASKS = $iterator;
    }

    public function info()
    {
        // nothing
    }

    public function add(Db $db, AuthenticationService $auth, $valid = null)
    {
        $this->action = "Create New Task";
        $this->editInternal($db, $auth, $valid, 'Task was created.');
        return 'task_edit.html';
    }
    public function edit(Db $db, AuthenticationService $auth, $valid = null)
    {
        $this->action = "Edit the Task";
        if ($valid === true)
            $this->task->updatedAt = date('Y-m-d H:i:s');
        $this->editInternal($db, $auth, $valid, 'Task was edited.');
    }
    private function editInternal($db, $auth, $valid, $msg)
    {
        if ($valid === null){
            $this->task->userName = $auth->getIdentity();
        }else if ($valid === true){
            try {
                $db->save($this->task);
                throw new RedirectMessageException('/task_info.html?task[taskId]=' . $this->task->taskId,
                                                   $msg,
                                                   Message::SUCCESS);
            }catch (Exception $e){
                Message::add($e->getMessage(), Message::ERROR);
            }
        }
    }
    public function delete(Db $db)
    {
        try {
            $db->delete($this->task);
            throw new RedirectMessageException('/task.html',
                                               'Task was deleted.',
                                               Message::SUCCESS);
        }catch (Exception $e){
            Message::add($e->getMessage(), Message::ERROR);
            return;
        }
    }
}

今まで使っていた自作フレームワークと今回のフレームワークの違い

  • バージョン
  • バージョン
    • 全体的な考え方はDI+アノテーション
      • Zend\DI
      • 特に参考にしたフレームワーク無し
      • Zend Framework2 を使っているので、そういう感じの設定になる
      • composer.phar を使っているので、割とマシな名前空間に強制される
    • CRUD 1クラス
      • または1アクション1クラス
    • 全体的な設定ファイルがいくつか
    • URLはhtmlメイン
      • 例えば、デザイナーがアップロードした画像やCSSやHTMLの構成をそのまま使う
    • 独自ORM(Tsukiyo、S2JDBC風)
      • DB設計重視、複数プライマリキーの外部結合など対応
    • 独自テンプレートエンジン(Yokaze、今のところ機能縮小版Flexy、HTMLタグ解析は少しやってるのでフォームに対応したいところ)


主な設定(?)と機能

  • アクション|ページクラスは一つのnamespace以下に収める
  • @var アノテーションを書くと型変換する
    • ORMのVOの型を書くと、リクエストにプライマリーキーがある場合はDBからデータを取得し、さらにリクエスト変数で上書きする
  • @Converter アノテーションで変換
    • trimとか
  • @Validatorアノテーションで入力チェック
    • 実体はiniファイルに書く
    • validかどうかは三値で取得する。null=動作なし、false=invalid、true=valid
  • アクションフィルター的なものはURLに対する正規表現で書く
    • 一ヶ所に集約
  • Zend\Di に対する設定はdi.iniに書く


その他

  • モデルやサービス的なことは何もしない
    • 自分でApp\Modelなどのnamespaceを作る
  • メールとかファイルとか権限管理はまだ無い
    • ログイン処理はサンプルとしてiniファイル版とDB版がある
  • Wicketのように例外でリダイレクトする
    • アクションフォワード的な機能は無し


現状はこんなところ。