header image

携帯対応

QRコード

RING

人気ブログランキング

新着記事

2009/10/18 08:41:10 PHP 2 Comments

cakephpではSchema機能を使ってテーブルを作成することが出来る。
で、ついでにマスター系データもまとめて登録する方法が【CakePHP】お手軽便利なCakeSchemaに載っている。
ただ載っている方法には若干問題がある。

  • そもそもcakephpでは、テーブルを使わないモデルでは$useTable=falseに設定しないといけなくて、それ以外の場合は、モデルへのアクセス時にテーブルが存在しないとエラーが発生する。
  • 従って、初期データを保存するためのモデルがアソシエーションが設定されているモデルだと、関連モデルのテーブルがまだ作成されていない場合にアクセスすると、その時点でエラーになってしまう。作成する順番だけを変えれば良いケースもあるだろうけど、相互参照しているようなモデルでは無理。
  • つまり、アソシエーションがある場合は先にテーブルを生成しなければならず、schema.phpのafterメソッドで、都度データを入れることはできないような気がする。(unBindModelとかしてみたけど、そもそもモデルのロードで先にエラーになるので、そもそもダメ)

ということで僕なりにアレンジしたのが以下の方法。
schema.phpのafterメソッド

function after($event = array()) {

    $model_names = array();
    $prop = get_class_vars(get_class($this));
    foreach($prop as $key => $value)
    {
        $s = Inflector::classify($key);
        if (!($s == "Name" || $s == "File" || $s == "Path" || $s == "Log" || $s == "Connection" || $s == "Table"))
        {
            $model_names[] = $s;
        }
    }

    if(!empty($event['create'])){
        if(!isset($this->InitialValues)){
            require_once($this->path.DS.'initial_values.php');
            $this->InitialValues = new InitialValues();
        }
        $modelname = Inflector::classify($event['create']);

        if($modelname == $model_names[count($model_names)-1])
        {
            foreach($model_names as $target_modelname)
            {
                $this->InitialValues->set($target_modelname);
            }
        }
    }
}

肝は、全部のデータを最後のテーブルの作成完了後に作る、というだけ。
NameとかFileとか除外しているところはin_arrayで除外した方がいいけど、まぁいいか。

2009/09/24 16:10:21 PHP, 日記 none Comments

色々試行錯誤したんだけど、最終的に今回の開発は、以下のような環境で進めることにした。

環境とか

  • VMware Player 2.5.2
  • Eclipse 3.4
  • PHPUnit
  • Selenium RC
  • Subversion 1.5系

開発用サーバ

開発用サーバはVMware Player上にCentOS5.3をインストールして、必要なミドルウェアの設定を行ったものを用意した。

VMwareの共有ディレクトリ機能を利用し、/mnt/hgfs/ディレクトリに、チェックアウトしたレポジトリのtrunkを割り当ててある。
レポジトリの構成については、以前Postしたようにtrunk以下に全てのソースコードやドキュメント、ツールが配置されている。

本番と同様のファイル配置にするために、/mnt/hgfs/以下の各ディレクトリに対してシンボリックリンクを張って同じ構成にしている。
この場合ファイルのパーミッション関係はテストできないので、本番環境のパーミッションテスト用のスクリプトを予め用意しておいた。

Eclipseの設定

Eclipseについては、Pleiadesから入手したPHP開発用のものを利用した。特に変わった設定はしていないが、ソースコードについてはSubversiveを使って、Eclipse上からSubversionを操作できるようにしてある。なお、Eclipse上からのSubversion操作以外に仮想OS上の操作、開発用マシンからの操作と複数種類の操作方法が存在するが、この場合Subversionのバージョンが異なると問題になることがある。
従って僕の環境ではSubversionは全て1.5系に揃えてある。
TortoiseSVNは、標準では起動時にバージョンチェックを行い、新しいバージョンが出ているとアップデートを促してくるが、うかつにアップデートすると面倒なことになるので、設定を変更しておく。

またEclipseは各プロジェクト毎にインスタンスを分けておき、設定ファイル等もSubversionに放り込んでおいた。Eclipse本体もSubversionに登録しようとしたのだが、こちらについては、パスが長すぎるせいか登録に失敗するので、zipで固めて登録しておき、新しい開発者が追加になった場合もチェックアウト&解凍ですぐに利用できるようにした。
SubversionのFAQを見る限りWindows上からの255バイトを超えるパスのファイルについてもフルパスで指定することによって登録可能と書いてあったんだが・・・。

PHPUnitとSelenium RC

今回のプロジェクトはレガシーコードの改修なので、ユニットテストが用意されているわけでもなく、どうやって品質担保するかが課題。

改修していくモジュールについてはできる限り単体レベルでテストできるようにテスト追加とリファクタリングをしていくが、複雑度が高かったり、他のモジュールとの関連性が見えない中でむやみやたらにリファクタリングすると危険なので、まずはアプリケーションの動作レベルで検証できるようにSelenium RCとPHPUnitを組み合わせて、動作のテストを行えるよう準備した。

この際、PHPUnit_Extensions_Database_TestCaseを利用して、予め用意しておいたテスト用のfixtureをロードしてテストできるようにしてある。
以前はひたすら1つのDBにいろんなレコードを突っ込んでテストをしていたようだが、テストの再現性が確保できるはずもないので、最初の段階で仕込んである。

またテスト用のfixtureはCSV形式で用意する必要があるのだが、いちいち手でCSVを作成するのは困難なので、予めテーブルのスキーマを全て記述したExcelシートを用意し、マクロのようなもので複数CSVファイルに変換している。本当はPHPUnitを継承して、Excelをデータセットとして読み込めるようにすると尚良いのだが、今回は時間もなかったので見送った。将来的にはこの部分をもうちょっと工夫したいところ。

Selenium RCについては、htmlでテストケースを書くという手もあるのだが、データの初期化や細かいテストに不向きなので、phpでテストケースを記述している。もちろんテストケースの作成にはSelenium IDEを利用して、ベースとなるテストケースについては、ある程度自動で生成している。

Selenium RCでは複数のブラウザに対応しているが、自身でProfileをカスタマイズできることからFirefoxを利用してのテストとしている。(テストサーバがオレオレ証明書なんだが、現在のところSelenium RCでオレオレ証明書に対応できるのは、カスタマイズしたProfileを使えるFirefoxのみのようだ)

その他工夫している点

開発者が今後増えていくことを考えると、開発環境構築のスピードアップと均質化は重要だと考えている。
その観点で今回は自動で開発環境を整えるバッチファイルを用意して以下のことをやらせている。

  • ソースコード配置ディレクトリの自動作成とSubversionレポジトリからのチェックアウト
  • EclipseをSubversionから取得して自動解凍し、所定の場所に配置する。設定ファイルやSelenium RCについても同様
  • その他必要なディレクトリを自動で生成
  • Subversionへ登録しないファイルについて予めsvn:ignore設定する
  • Selenium RCで利用するFirefoxのProfileを所定ディレクトリにチェックアウトして配置

これによって全ての開発者は、同じツールを使って同じディレクトリ構成で開発を行うことができるようになっている。
さぁどうなるかな。

2009/07/10 11:31:46 PHP none Comments

CakePHPで一括でカバレージを測定するときに、Windows環境だと修正が必要と書いたけど、面倒なのでパッチ作っておいた。ご自由にどうぞ。
simpletest.patch

2009/07/07 14:00:57 PHP none Comments

とうとう見つけたよ!

#7/8にcoverage_reporter.phpの修正について若干追記

前提

XAMPP1.6.6
CakePHP1.2

環境準備

SimpleTestの開発レポジトリにあるカバレージ測定ツールを使う。
標準のアーカイブには含まれていないので、svnから取得する。

svn co https://simpletest.svn.sourceforge.net/svnroot/simpletest/simpletest/trunk

取得したモジュールを適当な場所に配置する。今回はC:/dev/simpletestに配置した。

次に、php.iniを修正する。

  1. 上記で取得したモジュールの中にある、extensions/coverageをinclude_pathに追加する。
  2. auto_prepend_fileにautocoverage.phpを指定する

※この設定が難しければPHPのコマンドラインパラメータに値を指定することが出来る。例は以下の通り。

php -d  include_path=".;C:/dev/simpletest/extensions/coverage/;" -d auto_prepend_file=autocoverage.php

測定手順

  1. コマンドプロンプトを開く
  2. 測定対象アプリのディレクトリに移動する
  3. 以下のコマンドを叩く。(実際は1行ね)
    php C:/dev/simpletest/extensions/coverage/bin/php-coverage-open.php
    --include=app/controllers/.*\.php$
    --include=app/models/.*\.php$
    --exclude='.*/tests/.*'
    --exclude='./*.svn/.*'
    --maxdepth=1

    なお、上記の例ではモデルとコントローラのディレクトリのみを対象としているが、自作のライブラリ等があればそれも指定する。

  4. これによってカバレージ測定の準備が完了し、測定対象アプリのルートディレクトリにcoverage.sqliteという名前でsqlite2のデータベースが作成される。
  5. SimpleTestのテストケースをコマンドラインから実行する。
    cake\console\cake testsuite app all

  6. テストの実行が終了したらカバレージ測定を停止する。
    php C:/dev/simpletest/extensions/coverage/bin/php-coverage-close.php

  7. 最後にレポートを生成する。
    php C:/dev/simpletest/extensions/coverage/bin/php-coverage-report.php

  8. できあがったレポートはこんな感じ
    SimpletestCoverage

注意点とか

  • Windows環境で動作させる場合はモジュールの改修が必要になる。具体的にはcoverage.phpの140行目付近のisFileIncluded関数
  • この関数はWindowsを考慮していないため、パス区切り文字が/(スラッシュ)でないと正しく動作しない。従って関数の開始直後に
    $file = str_replace("\\", "/", $file);

    を入れておく。

  • 同様にcoverage.phpの60行目付近のfclose($parent);がエラーメッセージをじゃんじゃん出すので、頭に@をつけるなりしてエラーを抑止しておく
  • 上の方で書いた通り、php.iniでauto_prepend_fileを設定すると開発に影響が出てきたりすることも考えられるので、この設定するのが嫌であればコマンドライン引数で対応する
  • 7/8追記レポート出力の箇所でWindowsだと動作しない箇所がある。具体的にはcoverage_reporter.phpの55行目。
    static function reportFilename($filename) {
            return preg_replace('|[/\\\\]|', '_', $filename) . '.html';
        }

    となっているが、これを以下に修正する。

    static function reportFilename($filename) {
            return preg_replace('|[/:\\\\]|', '_', $filename) . '.html';
        }

    というのもWindowsの場合ドライブレターに:がつくのでファイル名に:が含まれてしまうことになり、正常にファイルが出力されないためだ。

  • phpUnderControlに組み込むことも出来そうだ。その場合は、execコマンドで呼び出して、カバレージ測定結果をbuild以下に出力するようにすると良いだろう。あとはartifactspublisherの設定をしておけば画面上からカバレージの閲覧が可能になる。
2009/07/06 06:22:54 PHP none Comments

phpUnderControl連携をしていて気づいた問題。
SimpleTestでは、assertWantedPattern等で、文字列中から該当する文字列を正規表現で探すといったテストが出来る。このテスト結果を表示する際に検索範囲文字列が100~200文字を超えると、間の文字列がカットされて...と表示されるのだが、日本語環境だと場合によって文字化けしてしまう。

simpletest/dumper.phpの118行目から

function clipString($value, $size, $position = 0) {
    $length = strlen($value);
    if ($length <= $size) {
        return $value;
    }
    $position = min($position, $length);
    $start = ($size/2> $position ? 0 : $position - $size/2);
    if ($start + $size> $length) {
        $start = $length - $size;
    }
    $value = substr($value, $start, $size);
    return ($start> 0 ? "..." : "") . $value . ($start + $size <$length ? "..." : "");
}

で文字列の切り出しをしているが、2バイト文字が一切考慮されていないため、このような問題が起こっている。
ということで修正は

function clipString($value, $size, $position = 0) {
    mb_language("Japanese");
    $length = mb_strlen($value, 'UTF-8');
    if ($length <= $size) {
        return $value;
    }
    $position = min($position, $length);
    $start = ($size/2> $position ? 0 : $position - $size/2);
    if ($start + $size> $length) {
        $start = $length - $size;
    }
    $value = mb_substr($value, $start, $size, 'UTF-8');
    return ($start> 0 ? "..." : "") . $value . ($start + $size <$length ? "..." : "");
}

とすれば良さそうだ。strlenをmb_strlenにし、文字コードを指定。同じくsubstrもmb_substrにして文字コードを指定。
※ソース全体がEUC-JPの場合とかを考慮すると本当はもうちょっと真面目に対応した方が良いかもしれないけど、僕は最近UTF-8しか使わないので。

Webベースのテストなら多少文字列が文字化けしていたくらいではどうってこともないが、phpUnderControl等のCIツールを使って、XMLをパースするような処理があったりすると文字コード関連のこの不具合は面倒なことになるので、気をつけよう。

4月から開発してきた案件が終わるのでちょっくら振り返りするぞ。

アーキテクチャ

  • CentOS5 (開発はXAMPP)
  • PHP5.1.6
  • CakePHP1.2
  • MySQL5

案件規模

  • コントローラー13本(AppController含む)
  • モデル14本
  • ビュー80本
  • コンポーネント4本
  • PHPのソースで約1万行。テストコードで4000行。開発者は僕だけ。

ツール系

  • XAMPP 開発環境
  • Subversion ソースコード管理、ドキュメント管理に最初から利用。接続にはTortoiseSVNを利用。
  • Trac 課題管理、バグ管理、要件管理に利用
  • A5:SQL Mk-2 データベース関連の作業に利用。
  • Eclipse PHP開発用のIDE。Pleiadesを利用
  • Poedit i18nの言語ファイルの編集用
  • Lingoes 無償の辞書ツール。モジュール名や関数の命名に利用。
  • SimpleTest テスト自動化
  • Xdebug カバレージ測定
  • Cygwin 便利コマンドとして利用。tailとか出来るし。
  • Radish ローカルのSMTPサーバ。Windows環境で開発する時に実際のメールは送らなくてもメールを確認できる。

開発方式とか

  • 完全アジャイルプロジェクト
  • 提案時点でイテレーションの回数を先に決めた。
  • 定額契約モデルで、イテレーションに入らない場合は別途交渉を明示。バーター有りもあわせて明示。
  • 成果物は最初に規定。単体テストケース表とか詳細設計書は作らないことを明示
  • プロジェクト立ち上げ後、定石通り重要機能から構築開始
  • テスト自動化をすすめた結果、仕様変更、仕様追加の対応も容易だった
  • プロジェクト期間中の残業は0時間。週40時間稼動を達成。
  • 余計な事務作業とか割り込みが無いので、Velocityが高かった。
  • bakeコマンドで骨格を自動生成
  • 画面デザインは最終イテレーションで取り込み。
  • 各種メッセージ系はなるべくi18nを利用

課題とか

  • リファクタリングをどこまでやるか、について指針が無いと、時間がある限りリファクタリングしたくなってしまう。
  • 入力→確認→完了という画面遷移はもっと汎用化できそうな気がするが、現状似たようなコードが一杯ある。
  • Ajaxとか使いだすとViewの見通しが悪くなりやすい気がする。やっぱりViewに凝るのは最後の最後がよさそう。そしてViewもリファクタリング対象。
  • i18nをどこまでやるか指針を決める必要がある。
  • CodeSnifferはかけなかったので今後はやる。
  • 継続的統合ツール(phpUnderControl)は未使用。次回は使うつもり。

開発は楽しいねぇ。

2009/06/27 06:48:56 PHP none Comments Tags: , ,

前回の話はこちらを参照

前回以降もテストしているんだけど、「メモっとかないと忘れちゃうねー」的な話を以下列挙。
それにしても今回は真面目にテストしているぞ(笑

  • カバレージを測定したかったらテストモジュールの分割方法は、テスト対象モジュール1つにつきテストモジュール1つにするべきだ。
     僕が作っているシステムは一般向け画面と管理者向け画面に分かれていて、管理者画面へのアクセスはadmin routingを使っているのだけれど、こういう場合のテストでもコントローラー単位、モデル単位で分割すべきで、利用シーンで分離すべきではない。但し受け入れテスト等、カバレージではなく業務要件への充足確認が中心になる場合は別だけど。

  • setUp、startTest、startCase等の初期化処理の違いを押さえておくこと。bakeでControllerのテストケースを作るとsetUp()が自動で作成されるが、このメソッドは全テストの直前に毎回呼び出されるので、ここでDBの初期化をしたりすると、やたらと時間がかかるテストが出来上がる。

  • 例えば常にredirectするようなメソッドではViewを用意しないことがあるが、テストの際にredirectを再定義してしまうと、Missing Viewというシステム的なエラーが発生する。これを避けるために空白でもよいのでViewを用意しておくと良い。

  • 続きを読む »

2009/06/19 14:04:02 PHP none Comments Tags: ,

タイトルに深い意味は無いんだけど、CakePHPのSimpleTestでコードカバレージを真面目に取得してみたので、そのメモとか。

使っている環境

XAMPP1.7.0
PHP5.2.8
MySQL 5.1.30
Apache 2.2.10
 

Xdebugの入手

このバージョンのXAMPPに含まれているXdebug2.0.3ではカバレージが取得できず、Apacheごと死んでしまう問題があるので、Xdebug2.0.0を利用する。
http://www.xdebug.org/download.phpにアクセスして、環境にあったモジュールを取得する。
xdebug2
僕の環境の場合は、XAMPPのPHP5.2.8だから、図の赤枠で囲んだリンクをクリックしてファイルを保存する。
php_xdebug-2.0.0-5.2.2.dllをphp_xdebug.dllにリネームしてphp\ext\の中に上書き保存する。
 

Xdebugの設定

続きを読む »

2009/06/12 07:00:48 PHP none Comments Tags: ,

今やっている案件ではCakePHPを使ったアジャイル開発で、当然テストも自動化している。

テストの自動化を徹底的にやったので楽なんだけど、次回の案件のためにどういう観点でテストを組んでおくと良いか、またどこに嵌りがあるかメモとして残しておく。

CakePHPに限らない話

  • テストしやすい実装にする。例えばメソッドに複数の異なる役割を持たせない。引数と戻り値が明確。適切な行数など
  • MVCの複数レイヤーにまたがる処理を書かない。例えばコントローラの中でSQLじゃぶじゃぶ投げたり、バリデーションチェックをぐちゃぐちゃやったりしない
  • 自動でテスト実行できる仕掛け作り。例えばPHPならphpUnderControl。JAVAならCruiseControlとかHudson。
  • 基本に返って、テストを先に書くという意識付け
  • テストがいっぱいありすぎたら今度はテストのリファクタリング。似たようなテストがコピペで沢山書かれたり、やたらと長いテストは注意
  • そもそもテストの内容が分かりにくいというのは、アーキテクチャか仕様上、隠れた問題があることが多い(経験則上)
  • データベースへの書き込みや更新は、全フィールドが想定値と同じか比較する。自動テストなら簡単。(面倒だからサボったら1フィールド更新されていなかったので懲りた)
  • モデルのテストでは境界値まわりとバリデーション周りを徹底チェック
  • UIのテストに凝りすぎない。UIは一番変えることが多いから、作りこみ過ぎるとテストのメンテナンスが重荷になる。それにAjaxが絡むUIの自動テストなんてやる気しない

CakePHPに関する話

  • CakePHP1.2だと、cake\tests\libにある、cake_reporter.phpにおいて、htmlの出力文字コードがiso-8859-1になっているので、ユニットテストの中で最終引数に日本語メッセージを入れるとテスト結果が文字化けする。これは修正しておく
  • アクションをテストできるtestAction()というメソッドがあるのだが、このテストで、呼び出し先が最終的に他のアクションにリダイレクトしている場合、テストがそこで中断されてしまう。これの対応のためにはrunkitを使うと簡単。(こちらを参照)
  • CakeTestCaseにおいては、無条件に本番用DBの中身をコピーしてテスト用に作成して使用する仕組みになっている(cake_test_case.phpの201行目付近)。で、せっかくモデルのテスト用にfixtureを用意しているんだから、コントローラのテストでもこのfixtureをそのまま使えるようにしてあげたほうが楽だと思う。僕はcake_test_case.phpの201行目付近に手を入れた
  • 少なくとも単体テストレベルでのカバレージの確保という観点においては、WebTestCaseは最後の手段。WebTestCaseはあくまでHTTPのレイヤーを通してのテストだから、Seleniumで試験するのと変わらない。大多数の部分のテストはモデルとコントローラのテストに実装されるはず
  • ファイルアップロードのテストは、CakePHPならSimpleTestのWebTestCaseで対応可能。残念ながらtestAction()関数では出来なかった
  • testAction()で引数にPOSTパラメータを渡せるのだが、僕のところではdata[モデル名][attribute]の形式でしかデータを渡せなかった
  • CakePHPのブラウザUI上から長いテストを実行すると、HTTPDでタイムアウトする可能性がある。テストスクリプトの先頭等で、set_time_limit(0)と設定しておきつつ、HTTPD自体のタイムアウトも長くしておいた方が良い
  • CakePHPの場合Model->save()では標準で書き込み可能なカラムの制御がなされていない(引数で変更するカラムを配列で渡すことは出来る。ただしあちこちのWebに掲載されているサンプルでそこまで書かれていることはあまりない)。POST元のページいじくると他のカラムの書き換えも出来てしまう可能性があるので、その点ケアしておくこと

他に思いついたら付け足そう。

参考

CakePHP1.2ガイドブック

著者/訳者:安藤 祐介 新原 雅司 堂園 俊郎

出版社:毎日コミュニケーションズ( 2009-06-27 )

定価:¥ 3,360

単行本(ソフトカバー) ( 400 ページ )

ISBN-10 : 4839932468

ISBN-13 : 9784839932466


CakePHPによる実践Webアプリケーション開発

著者/訳者:安藤 祐介 岸田 健一郎 新原 雅司

出版社:毎日コミュニケーションズ( 2009-04-08 )

定価:¥ 3,150

Amazon価格:¥ 3,150

単行本(ソフトカバー) ( 264 ページ )

ISBN-10 : 4839930651

ISBN-13 : 9784839930653



CakePHP ポケットリファレンス (Pocket Reference)

著者/訳者:株式会社ブルーオーシャン 岡田 佳典

出版社:技術評論社( 2008-06-18 )

定価:¥ 2,604

Amazon価格:¥ 2,604

単行本(ソフトカバー) ( 448 ページ )

ISBN-10 : 4774135038

ISBN-13 : 9784774135038



2009/05/28 05:29:22 PHP none Comments

以前にも書いたんだけど、CentOS5系を利用していると、バリデーションのAlphaNumericが動作してくれない。その回答としては、環境に問題あるので、PCREをバージョンアップして、という風に書いたが、これだと、納品後どのサーバに配置するかによって、アプリケーションの動作に差が出てしまう。環境差によって差があるのであれば、それを検証するテスト書かなきゃいけなくなるので、今回は本体を修正して、環境非依存な状態にする方法を紹介する。

libsディレクトリの中にあるvalidation.phpを修正する。

修正前

function alphaNumeric($check) {
    $_this =& Validation::getInstance();
    $_this->__reset();
    $_this->check = $check;

    if (is_array($check)) {
        $_this->_extract($check);
    }

    if (empty($_this->check) && $_this->check != '0') {
        return false;
    }
    $_this->regex = '/^[\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]+$/mu';
    return $_this->_check();
}

修正後。$_this->regexのところが普通の正規表現に変わっている。

function alphaNumeric($check) {
    $_this =& Validation::getInstance();
    $_this->__reset();
    $_this->check = $check;

    if (is_array($check)) {
        $_this->_extract($check);
    }

    if (empty($_this->check) && $_this->check != '0') {
        return false;
    }
    $_this->regex = '/^[a-z\d]*$/i';
    return $_this->_check();
}

ちゅーか、なんでPCREつかっているのか分からん。

 

日記 PHP オープンソース インストールマニアックス IIS Trac MySQL Perl Linux Agile・生産性向上 wordpress フリーソフト 自宅サーバ 書評 ブックマーク phpMyFaq TraM Plugin 早起き Delphi apache CakePHP Firefox Ruby eclipse セキュリティ プラグイン アジャイル mojavi Subversion Ajax/Web2.0 SQLServer Zope サーバ フレームワーク phpBB 仮想化 PostgreSQL OpenVZ scuttle CMS 文字化け 自宅 翻訳・日本語化 ApacheDS LDAP Excel 生産性向上 CodeIgniter XAMPP hacks taskfreak 修正 言語ファイル Ajax SBM ダウンロード HTML::FillInForm mod_security 情報共有


ads

読まなきゃモグリ