チャット作るよ。10日目

コメント書き込みとログイン周りを実装する。
昔ながらのバージョンはこれでひとまず完了。

コメント書き込みのアクションを作る。

ログイン済みユーザのコメントPOSTを受けとってファイルに書き込む。


まずテストケース。

<?php
class CommentActionTest extends PHPUnit_Framework_TestCase {

    /**
     * アプリケーションディレクトリ
     */
    const APP_DIR_PATH = '/var/project/chat/fw/site';

    public function setUp() {
        // インスタンスを生成
        $this->act = new CommentAction();
        $this->member = new Member(FILE_MEMBER, 300);
        $this->arc = new Archive(FILE_ARCHIVE, 30);
        // アクションを初期化
        $config = array(
            'appDirPath' => self::APP_DIR_PATH,
            'renderer' => new Renderer()
        );
        $this->act->initialize($config);
    }
    
    public function testRun() {
        // 実行前、データが0件であること
        $this->assertEquals(0, count($this->arc->getAll()));
        // postパラメータをセット
        $_POST['comment'] = 'Test';
        // アクション実行
        $this->act->run();
        // セッションがないのでデータに変化は起きないこと
        $this->assertEquals(0, count($this->arc->getAll()));
        $this->assertEquals(0, count($this->member->getAll()));
    }

    public function testRunWithIdentity() {
        // 実行前、データが0件であること
        $this->assertEquals(0, count($this->arc->getAll()));
        $this->assertEquals(0, count($this->member->getAll()));
        // 新規メンバーとしてデータ登録
        $name = 'Test';
        $id = $this->member->add($name);
        $identity = array(
            'id' => $id,
            'name' => $name
        );
        // セッションにセット
        $_SESSION['identity'] = $identity;
        // 登録データを取得して、保持しておく
        $members = $this->member->getAll();
        $user = $members[0];
        // 更新時間をずらすため1秒待機
        sleep(1);
        // postパラメータをセット
        $comment = 'TestTestTest';
        $_POST['comment'] = $comment;
        // アクション実行
        $this->act->run();
        // ログにpostしたコメントが登録されること
        $logs = $this->arc->getAll();
        $this->assertEquals(1, count($logs));
        $this->assertEquals($name, $logs[0]['name']);
        $this->assertEquals($comment, $logs[0]['comment']);
        // ユーザのタイムスタンプが更新されること
        $members = $this->member->getAll();
        $update = $members[0];
        $this->assertEquals(1, count($members));
        $this->assertEquals($user['id'], $update['id']);
        $this->assertEquals($user['name'], $update['name']);
        $this->assertLessThan($update['tstamp'], $user['tstamp']);
    }

    public static function tearDownAfterClass() {
        // データファイルの後始末
        if(file_exists(FILE_ARCHIVE)) {
            unlink(FILE_ARCHIVE);
        }
        if(file_exists(FILE_MEMBER)) {
            unlink(FILE_MEMBER);
        }
    }
}

アクションクラス。

<?php
class CommentAction extends Action {

    public function run() {
        if(isset($_SESSION['identity']) && isset($_POST['comment'])) {
            // ログイン済みで書き込みポストがある場合
            $identity = $_SESSION['identity']; 
            $comment = $_POST['comment'];
            // アーカイブにデータ追加
            $arc = new Archive(FILE_ARCHIVE, 30);
            $arc->add($identity['name'], $comment);
            // 有効期限を更新して、GCも実行。
            $member = new Member(FILE_MEMBER, 300);
            $member->update($identity['id']);
            $member->gc();
        }
        header("HTTP/1.1 301 Moved Permanently");
        header("Location: /?a=log");
    }
}

ファイル書き込みと同時に、ユーザの期限を延長と期限切れユーザデータの削除も行う。
処理後にリダイレクトさせて書き込みログの表示に飛ばす。

ログインアクションを作る。

ログインっていうかチャットに参加的な部分。


テストケース。

<?php
class LoginActionTest extends PHPUnit_Framework_TestCase {
    
    /**
     * アプリケーションディレクトリ
     */    
    const APP_DIR_PATH = '/var/project/chat/fw/site';

    public function setUp() {
        // インスタンスの生成と初期化
        $this->act = new LoginAction();
        $config = array(
            'appDirPath' => self::APP_DIR_PATH,
            'renderer' => new Renderer()
        );
        $this->act->initialize($config);
    }
    
    public function testRun() {
        // postパラメータを作成
        $name = 'Test';
        $_POST['name'] = $name;
        // 実行前、データがからであること
        $member = new Member(FILE_MEMBER, 300);
        $this->assertEquals(0, count($member->getAll()));
        $arc = new Archive(FILE_ARCHIVE, 30);
        $this->assertEquals(0, count($arc->getAll()));
        // アクション実行
        $this->act->run();
        // 実行後、新規メンバーとして追加されること
        $members = $member->getAll();
        $this->assertEquals(1, count($members));
        $identity = $members[0];
        $this->assertEquals($name, $identity['name']);
        // セッションへユーザデータが登録されていること
        $this->assertEquals($_SESSION['identity']['name'], $identity['name']);
        // ログにユーザ追加データが登録されていること
        $logs = $arc->getAll();
        $this->assertEquals(1, count($logs));
        $this->assertEquals($name, $logs[0]['name']);
        $this->assertEquals('Welcome!', $logs[0]['comment']);
    }

    public function testRunWithNoName() {
        // 実行前データがからであること
        $member = new Member(FILE_MEMBER, 300);
        $this->assertEquals(0, count($member->getAll()));
        $arc = new Archive(FILE_ARCHIVE, 30);
        $this->assertEquals(0, count($arc->getAll()));
        // アクション実行
        $this->act->run();
        // アクション実行後、Unknownユーザとしてユーザ追加されていること
        $members = $member->getAll();
        $this->assertEquals(1, count($members));
        $identity = $members[0];
        $this->assertEquals('Unknown', $identity['name']);
        // セッションへユーザデータが登録されていること
        $this->assertEquals($_SESSION['identity']['name'], $identity['name']);
    }

    public function tearDown() {
        // テストデータの後始末
        if(file_exists(FILE_ARCHIVE)) {
            unlink(FILE_ARCHIVE);
        }
        if(file_exists(FILE_MEMBER)) {
            unlink(FILE_MEMBER);
        }
    }
}

アクションクラス。

<?php
class LoginAction extends Action {
    
    public function run() {
        // ユーザ名初期化
        $name = 'Unknown';
        if(isset($_POST['name']) && mb_strlen($_POST['name']) > 0) {
            // nameがPOSTパラメータにあればユーザ名に使う
            $name = $_POST['name'];
        }
        // メンバーに追加
        $member = new Member(FILE_MEMBER, 300);
        $id = $member->add($name);
        // 認証をセッションに保持
        $_SESSION['identity'] = array(
            'id' => $id,
            'name' => $name
        );
        // 書き込みログに入室を追加
        $arc = new Archive(FILE_ARCHIVE, 30);
        $arc->add($name, 'Welcome!');
        // トップへリダイレクト
        header("HTTP/1.1 301 Moved Permanently");
        header("Location: /");
    }
}

ユーザ名がなければ名無しさん扱いにする。
参加をログに追加して、リダイレクトさせる。

ログアウトアクションを作る。

退場的な部分。


テストケース。

<?php
class LogoutActionTest extends PHPUnit_Framework_TestCase {
    
    /**
     * アプリケーションディレクトリ
     */ 
    const APP_DIR_PATH = '/var/project/chat/fw/site';

    public function setUp() {
        // インスタンスの生成と初期化
        $this->act = new LogoutAction();
        $config = array(
            'appDirPath' => self::APP_DIR_PATH,
            'renderer' => new Renderer()
        );
        $this->act->initialize($config);
    }
    
    public function testRun() {
        // テストユーザを登録
        $name = 'Test';
        $member = new Member(FILE_MEMBER, 300);
        $id = $member->add($name);
        $this->assertEquals(1, count($member->getAll()));
        // セッションにテストユーザデータをセット
        $_SESSION['identity'] = array(
            'id' => $id,
            'name' => $name
        );
        // アクション実行
        $this->act->run();
        // ログに退室データが追加されていること
        $arc = new Archive(FILE_ARCHIVE, 30);
        $log = $arc->getAll();
        $this->assertEquals(1, count($log));
        $this->assertEquals('Good Bye!', $log[0]['comment']);
        $this->assertEquals($name, $log[0]['name']);
        // テストユーザデータが削除されていること
        $this->assertEquals(0, count($member->getAll()));
        // セッションからデータが削除されていること
        $this->assertFalse(isset($_SESSION['identity']));
    }

    public static function tearDownAfterClass() {
        // テストデータの後始末
        if(file_exists(FILE_ARCHIVE)) {
            unlink(FILE_ARCHIVE);
        }
        if(file_exists(FILE_MEMBER)) {
            unlink(FILE_MEMBER);
        }
    }
}

アクションクラス。

<?php
class LogoutAction extends Action {
    
    public function run() {
        if(isset($_SESSION['identity'])) {
            // ログイン済みの場合、解除処理
            $identity = $_SESSION['identity'];
            // 書き込みログに退出を記録
            $arc = new Archive(FILE_ARCHIVE, 30);
            $arc->add($identity['name'], 'Good Bye!');
            // メンバーから削除
            $member = new Member(FILE_MEMBER, 300);
            $member->remove($identity['id']);
            // 認証を解除
            unset($_SESSION['identity']);
        }
        // トップへリダイレクト
        header("HTTP/1.1 301 Moved Permanently");
        header("Location: /");
    }
}

退場を書き込みして、ユーザデータとセッション消してリダイレクト。


昔ながらの版はさくっと作ろうと思ったのに、
「10日で…」本の1日相当に随分かかってしまった…。無念。
作りが甘い部分いっぱいあるし。中途半端になっちゃった。
作りながら記録付けるのって大変だ。