ラベル PHP の投稿を表示しています。 すべての投稿を表示
ラベル PHP の投稿を表示しています。 すべての投稿を表示

2013年8月28日水曜日

Symfony2のblog tutorialを2.3に対応させる。

Symfony2、これまでほぼノータッチだったんですが、触る必要が出てきたので、
ざっと基本的な部分を把握するために、blogチュートリアルをやってみました。

URLはこちら。
※海外有志の方が作成した、symblogチュートリアルじゃない方です。

こちら、ヘッダ部分に記載されているとおり、バージョン2.0.7向けの内容です。
ですが、現在最新のSymfony2は2.3.4。
deprecatedになっているメソッドがあったり、そもそも存在しないAPIがあったりするので、
いくつか、気づいた変更点をまとめておきます。

  1. getEntityManegerメソッドがdeprecated。
    DB操作を行う、EntityManagerを取得するメソッド名が変更になっています。
    使い方はそのままなので、単純にメソッド名を変えれば良さそう。
    http://docs.symfony.gr.jp/sf2-blog-tutorial/05-list-page.html
    例えば、上のページのDefaultController.phpのindexActionは以下のようになります。
    public function indexAction()
    {
    //    $em = $this->getDoctrine()->getEntityManager();  // 旧
        $em = $this->getDoctrine()->getManager();  // 新
        $posts = $em->getRepository('MyBlogBundle:Post')->findAll();
  2. FormのbindRequestメソッドが廃止。
    リクエストをバインドする時は、単純にbindメソッドを使えば良いよう。
    http://docs.symfony.gr.jp/sf2-blog-tutorial/07-create-form.html
    例えば、上のページのDefaultController.phpのnewActionのバリデーション周りは以下のようになります。
    // バリデーション
    $request = $this->getRequest();
    if ('POST' === $request->getMethod()) {
    //    $form->bindRequest($request);  // 修正前
        $form->bind($request);    // 修正後
        if ($form->isValid()) {
    
  3. フラッシュメッセージ関連
    Session周りのAPIが変更になった影響で、フラッシュメッセージの設定方法、表示方法共に変更となっています。
    まず、大きな変更として、フラッシュメッセージは常に配列で扱われるようになった、という所があるようです。
    ですので、表示の際は配列であることを意識したビューの書き方をする必要があります。

    フラッシュメッセージを設定するController側の変更です。
    http://docs.symfony.gr.jp/sf2-blog-tutorial/customize/03-session-flash.html
    上のページのDefaultController.phpのnewActionは以下のようになります。
    public function newAction()
        {
            // ...
    //        $this->get('session')->setFlash('my_blog', '記事を追加しました');  // 修正前
            $this->get('session')->getFlashBag()->add('my_blog', '記事を追加しました');    // 修正後
            return $this->redirect($this->generateUrl('blog_index'));
            // ...
        }
    ちなみに、FlashBagにmessageを設定するメソッドは、add以外にsetもあります。
    違いは何かというと、FlashBag.phpのソースを直接見るとわかりやすいのですが、
    add: $this->flashes[$type][] = $message;
    set: $this->flashes[$type] = $messages;
    と、addは指定したtypeの配列にメッセージ(文字列)を追加するのに対し、
    setは指定したtypeの配列そのものを上書きする形になります。
    なので、普通に使う分にはadd使ったほうが良いんじゃないかと思います。

    次、テンプレート側の変更。
    http://docs.symfony.gr.jp/sf2-blog-tutorial/customize/03-session-flash.html
    同じく上のページのindex.html.twigのフラッシュメッセージ周りは以下のようになります。
    {% for type, messages in app.session.flashbag.all() %}
        {% for message in messages %}
            
    {{ message }}
    {% endfor %} {% endfor %}
    ちょっとこれは、変更前/後の比較表示が難しいので、変更後だけ。
    一見するとコードが増えて複雑なように見えますが、システムが複雑化してきて、
    フラッシュメッセージの量や種類が増えてくると、便利なんじゃないかな、と。
    (とはいえ、一度に大量のフラッシュメッセージが表示されるサービスは、微妙なようにも思いますが…ww)
  4. FormTypeのgetDefaultOptionsメソッドが廃止。
    変わりに追加となった、setDefaultOptionsを利用することになります。
    http://docs.symfony.gr.jp/sf2-blog-tutorial/customize/05-create-form-class.html
    上のページの"PostType.php"のgetDefaultOptionsは以下のようになります。
    // use周りも追加・変更があるので注意
    use Symfony\Component\Form\AbstractType;
    // use Symfony\Component\Form\FormBuilder; // 削除
    use Symfony\Component\Form\FormBuilderInterface; // 追加
    use Symfony\Component\OptionsResolver\OptionsResolverInterface; // 追加
    
    // 中略
    
        public function setDefaultOptions(OptionsResolverInterface $resolver) {
            $resolver->setDefaults(array(
                'data_class' => 'My\BlogBUndle\Entity\Post',
            ));
        }
    戻り値として返すのではなく、$resolverのsetDefaultsに設定する形になった、と覚えればOKでしょう。たぶん。
    ちなみに、同時に廃止となったgetAllowedValuesメソッドの役割も$resolverに対して行うようです。
気づいたのは大体こんな感じですね。
deprecatedなメソッドは、もしかしたら見落としがまだあるかもしれません。
詳しくは、SymfonyのUPGRADE-2.x.mdを見るのがいいと思います。
2.1, 2.2までは有志の方が日本語に訳してくださったものがあるのでそれを参考にするとわかりやすいと思います。
上で書かれている内容は、2.1のタイミングで変更になったものばかりでした。

2011年12月8日木曜日

Symfony2を試してみて[一人Web系AC2011]

連続でお送りする、PHPの有名FWお試し&比較記事シリーズです。
今日は、Symfony2についてです。

まずは、これまでと同じ仕様のサンプルを作成しました。
「同じ仕様って、どんな仕様なんじゃい?」
と言う方は、こちらの記事をどうぞ。最初のほうに書いてあります。

内容的にですが、Symfony2もblogチュートリアルがあるので、そちらを参考に一部手を加えました。
ページャ追加したり、formをせっかくだからクラスで作ってみたり、ですね。

そのソースが、こち…ら…。
すんませんまた上げ忘れて手元にありません何やって(ry
後日上げなおします。

■大体の流れ
前回と同様、Symfony2を読むための流れです。
  1. app/config/routing.ymlから、利用するバンドルを調べる
    今回のサンプルの場合、以下のような形になっています。
    sample:
        resource: "@MySampleBundle/Resources/config/routing.yml"
        prefix:   /sample
    resourceが挿す先は、バンドルのrouting設定ファイルです。
  2. バンドルのrouting.ymlを見て、コントローラと対応するメソッドを調べる。
    バンドル自体はsrcディレクトリの先にあります。
    今回の場合、パスは以下になります。
    src/My/SampleBundle/Resources/config/routing.yml
    patternにURIのパターンと、そのパターンでリクエストが来た場合に呼び出される内容がdefaultsに記載されています。
    sample_index:
        pattern: /
        defaults: { _controller: MySampleBundle:Default:index }
    この場合、MySampleBundleのDefaultControllerのindexAction()が呼ばれる事になります。
    {}で囲まれたものがある場合、それはアクションの引数になります。
  3. 対応するコントローラの処理を読む
    この辺の内容は、基本的なPHPです。
    MVCでいうMにあたる、DB操作についてはdoctrineを利用します。
    "MySampleBundle:Data"の場合、接続先DBの"Data"テーブルを読むと言う事さえ判れば、なんとなく読めると思います。
    ビューについては、$this->render()にて指定しています。
    最初の引数がテンプレート、次の引数がテンプレートに引き渡す値の配列ですね。
  4. テンプレートを読む
    テンプレートは[バンドルルート]/Resources/viewsにあります。
    コントローラ上で
    'MySampleBundle:Default:index.html.twig'
    として呼び出されていた場合、テンプレートのパスは以下になります。
    src/My/SampleBundle/Resources/views/Default/index.html.twig
    Symfonyの場合、テンプレートはTwigと言う物を使っています。
    書式的に難しい物では無いので、見れば大体は判ると思います。
    Twigは継承タイプのテンプレートなので、{% extends %}により他のテンプレートを継承している場合、まずそちらの内容が読み込まれます。
■実装しての感想
  • JavaのStruts/SpringFrameworkの組み合わせを思い出した。
    書式自体はぜんぜん違うけど、この、設定ファイルありきの書き方は、本当にソレっぽいなと思います。
    設定ファイルの書式自体は簡単なので、Springほど設定ファイルがゴミゴミすることもありませんが…。
  • 癖が強い。
    慣れの問題もあるかとは思うのですが、中々に癖が強いように感じられました。
    doctrineやTwig等、他のFW利用時にはあまり使わないものが多くあるからかもしれません。
  • Pagerにあたる機能はコアに無い。
    らしいです。
    今回は、本当に簡単なページャだったので、直接一覧ページのコードに埋め込む形で実装してしまいました。
    実際にサイトを実装する場合は、使い回しが出来るよう、独自バンドルを作ったりするんじゃないかと思います。
今日はここまで。
次回はCodeIgniterで実装してみようと思います。

2011年12月7日水曜日

PHPのボトルネック調査方法[Web系一人AC2011]

昨日は、ツレと布団の取り合いに負けた結果、体調不良で死にました。OMG...。
いい年して何たる失態。すみませんでした。

気を取り直して今日の記事。
今日の記事はSymfony2...ではなく、PHPのボトルネック調査方法について、です。
検証用のコードが一部間に合わなかったため、予定を変更してお送りさせていただきます…。

皆さん、PHPで作ったWebシステムのボトルネックって、どうやって調査してますか?
割と「コレ!」と言う手段を聞かなかったので、ちょっと自分が調査するときのポイントをまとめてみました。
まずは、全体的にざっくりと、どんなツールを使って見ているのか?

それでは、本題に参りましょう。


■どのリソースが重たいのかチェック
ブラウザ上からどのリソースの取得に時間がかかっているのかを調査します。
ここで利用するのが、FireFoxならFireBug、Chromeならデベロッパーツールです。
(デベロッパーツール=右クリックして「要素を検証」を選ぶと出てくるアレ)
得に、Networkタブを良く利用します。
実際に読み込んだすべてのリソースを、どの順番でどのくらい時間をかけて読み込んだのか?等が見れます。
ここで、実際に読み込みに時間がかかっているリソースを絞り込みます。

■関連するログを追跡
基本ですが…。
サーバ上で取っているログを一通り追います。
とくに、MySQL環境にてマズいSQLが問題になっている場合、slow.logはとても参考になります。
まれに、slow.logにたくさんクエリログが残っているのに放置しているプロジェクトなんてものもあります…。オソロシイ。
SQLに問題がある場合、大抵ログからもはっきりと追えるので、ここで原因がはっきりすれば、DBまわりのチューニングを行い、実際の効果を一度見てみても良いと思います。
時間帯によって重い時間帯がある、と言う場合、munin等リソースをグラフ化して見れるツールを入れておくと役に立ちます。

■試験環境で再現する場合は、本番を想定した負荷をかけながらやる
処理の内容や使っているライブラリによっては、一定以上の負荷があって始めて再現するようなボトルネックも多くあります。
あらかじめ、JMeter等のツールを利用して、本番を想定した負荷をかけながらチューニングしていきます。
※試験環境によっては、JMeterで負荷をかけすぎると同じ回線/サーバの利用者に迷惑がかかる可能性もあるので、気をつけてくださいね。
※くれぐれも、本番稼働中のサイトや他所のサイトに打ち込まないように。

■Xdebugのプロファイラを利用する
PHPスクリプトに対するプロファイラです。
これで、実際のどのメソッドのどのコードで時間がかかっているのか?を調べることができます。
ボトルネックがありそうなコードを見つけたときに、すぐに生のソースを見て重たそうなコードを目視で調べて直す、という行動を取ってしまう人も結構見てきましたが、
実際のボトルネックは目に見えない部分にあるケースもあるので、ちゃんとプロファイラでチェックしたほうが、経験上回り道になりません。
Xdebugが導入済みなのであれば、php.iniのxdebug.profiler_enableをOnにすると利用できます。
※プロファイリングするときは当然、普段以上に負荷がかかるので、本番サイトでONにしないように。
また、プロファイラの吐いたログは、単体では可読性にかけるので、GUIで表示してくれるツールを利用します。
私は、Linux環境だということと、コールグラフ等がグラフィカルに表示されるのがうれしいので、KCachegrindを使っています。



JavaやObjective-c(これはWeb系ではありませんが…)だと、割とボトルネック調査やプロファイラの使い方についての記事が多いのですが、いまいちPHPには無かったので挙げてみました。
自己流の部分が多く、当然これが最善とは言えませんが、参考になれば、と思います。

2011年12月5日月曜日

[CakePHP]Model名’Datum’、テーブル名'data'の罠[Web系一人AC2011]

土日は、YAuth的な理由(僕の場合はDですが。)により、blogを書けないことが判明しました。
と言うわけで、平日のみの更新とさせていただきます…。すいません。

さて。
今回は、前回話をした、CakePHPにてDatumというモデル名(テーブル名はdata)を使うとハマる、と言う件についてです。

■まえがき。
実はこの問題、「CakePHP Datum」というキーワードで検索すると、@uechocoさんのblogがクリティカルヒットします。
http://labs.uechoco.com/blog/2010/12/cakephp-model-lovers.html
こちらの記事です。
しかし、当時の自分は
「CakePHP data テーブル名」
なんて、ノイズが多そうな検索ばっかり試していて、自力で気づくまで延々と悩むと言う恐ろしい失態をしてしまいました。OMG....

せっかく自分でもソースを追ったので、記事にしておきます。

べ、別にネタが無いわけじゃないんだからねっ

■きっかけ

  • 最初に作った、ただのPHP版のサンプルでは、テーブル名を「data」にしていた。
  • CakePHPは、モデル名とテーブル名の命名に「モデル名は単数形、テーブル名は複数形の英単語」というルールがある。
    このルールを守ることにより、コーディング時に設定ファイルを書かなくても良い。
    モデル名/テーブル名とした場合の例):Sample/samples, User/users, Woman/women
  • 例を見て解るとおり、こちら単純に英単語に「s」をつければ良いわけではなく、ちゃんと英語のルールに沿っている。
    具体的には、以下のソースで判別しているので、詳細が気になるなら参考にするとおk
    https://github.com/cakephp/cakephp/blob/master/lib/Cake/Utility/Inflector.php
  • 「じゃあ、テーブル名を'datas'に変えないといけないのかな?」と思ったが、「'data'って実は複数形だったらしいよ」と最近話題になっていたのを思い出し、調べる。
  • 調べた結果、dataの単数形はdatumであることが判明。
    CakePHPでも問題なく利用できるルールのようだ。
    ちなみに、このサイトで、規約に沿ったワードを調べられます。
    http://www.cpa-lab.com/tech2/inflects/
  • よーし、じゃあ、「Datum/data」の組み合わせでやろう!
  • 一覧表示、データ登録、削除はうまく動いたぞ、さて後は更新…あれ?FatalErrorが出るよ?あれ?
    Fatal error: Call to a member function getColumnType() on a non-object in /var/www/php_samples/cakephp/lib/Cake/Model/Model.php on line 1258

■原因調査
エラーが発生したコードはこちら。
https://github.com/cakephp/cakephp/blob/master/lib/Cake/Model/Model.php

getColumnType呼び出し時の引数$columnに渡される値は、多くの場合
「モデル名.カラム名」
となっています。
例えばDatum/dataの場合なら
Datum.id
Datum.title
Datum.body
等となります。

しかし、こちら更新時には、更新対象列の絞込みのため
「テーブル名.カラム名」
という値で呼ばれる事があります。
(おそらく、更新以外にも複雑なことをやろうとした場合、同じ呼ばれ方をします。今回発生はしませんでしたが。)
具体的には、プライマリキーのカラムのみそのような呼ばれ方をするので、
data.id
となります。

さて、問題のコードを見てみましょう。
Datum.idDatum.title にて呼ばれた場合、問題の1257行目のIF文はFalseとなり、
その先の$colsよりカラムの型を取得し、返します。
しかし data.id にて呼ばれた場合、Modelクラスは$dataというメンバ変数を保持しているため、問題のIF文がTRUEとなり、
$this->data->getColumnType($column);
という呼び出しを行おうとします。
しかし、実際にModelが持っている$dataは配列なので、メソッドを呼び出せずFatal Errorとなるわけです。

■解決策
モデル名をSample, テーブル名をsamplesに変更

■まとめ/教訓

  • フレームワーク利用時に一般的過ぎる単語を使うときは、注意する。
    というか、実際にサイトを作るときに、dataなんて一般的過ぎる単語は誤用が発生する可能性が高いので、NG。
  • 予約語リストがあれば、真っ先に参照するべき。
    CakePHPの場合、僕が探した限りでは見つかりませんでした。
    あったら是非教えてください。
  • 予約語リスト等が見つからない場合、関係するクラスのメンバ変数名はチェックしておいたほうが良い、かも。
    頻度的には、問題が起きたら疑うレベルでも良いけど、一度見ておくとそのフレームワークの命名のルールやよく使われる単語が見えてくるので。
  • Google先生に聞くときは、なるべくノイズが混じらない検索方法を行う。
  • 迷ったら、机上で考える前に一通りソースを舐めろ。デバッガ使え。
    (実は「あれー?おかしいなぁ、あってるはずなのになー。」と、コードと公式ドキュメントを見比べる時間が30分以上あったり…orz)


今回は以上になります。
次回は、予告どおりSymfony2にてサンプルの実装を行ってみます。

2011年12月2日金曜日

CakePHP 2.0 を試してみて。 [Web系一人AC2011]

PHP各種FWとただのPHPの比較シリーズ
開催です。

■前書き
さて、比較と言うからには、条件が必要になります。
シンプルなPHPによるWebサイトってどんなのだろう?と考えた結果、以下のような仕様のサイトとしてみました。
  • MySQLと接続し、テスト用のテーブルに対して以下の基本的な操作を行う。
    • データの一覧(10行でページング有り)
    • データの登録
    • データの更新
    • データの削除
  • レイアウト等の機能外の部分については、基本的にこだわらない。
  • バリデーション等も、基本的に無し。
  • まずはなるべくシンプルに実装する。早くするための細かいテクニック等はナシ。
  • セキュリティについても、最低限のみ考慮する。実際に運用するサイトではないので細かい部分まで調整はしない。
  • フレームワークのバージョンについては、最新の安定板を利用する。1系、2系と分かれているものについては、基本的に2系のみ。
まずは、ただのPHPによって比較用の仕様を満たすサイトを作りました。
比較用なので、
「クラスや関数に処理を分けず、一直線に書く」
という縛りを追加して書きました。

そのソースが、こちらです。
https://github.com/FAL/php_samples/tree/master/normal
なんという、前時代のコード!!!
CGIなんて単語が日常的に使われていた時代を思い出しますね!



実は、昨日の記事はこのノーマル版の解説にしようかな?と考えていたのですが、
自分がこんなイケてないコードしか書けないって言っているような気がして悲しかったのでやめました。
動作中のサンプルは出しませんが、ソースコード的にも単純なので、見ていただければ大体の動作はわかるはずです。

■CakePHP2.0版
ようやく本題。
CakePHP2.0にて、同様の仕様のサイトを実装してみました。
それがこちらです。
https://github.com/FAL/php_samples/tree/master/cakephp
…と、やろうと思ったんですが!
リポジトリに反映漏れがあって、ちょっと足りないファイルが多いので、後々あげなおします。

内容ですが、基本的にblogチュートリアルの内容をベースに、
テーブル名を変えて、
画面構成をちょっと変えてページングを実装して、
defaultレイアウトを簡素にしたりしたくらいです。

■大体の流れ
サンプルコードを読む際に、大体こんな感じで追って行けば、7割程度はすぐにわかるよ、という、大体の流れです。
単純なCakePHPのアプリであれば、同様の流れで追えると思います。
コーディングする側が手を入れないような、コアの部分でのデータの流れについては割愛します。

  1. /app/config/route.php
    mod_rewriteで飛んできたリクエストの、ルーティングのルールが書いてある。
  2. /app/Controller/[コントローラ名]Controller.php
    コントローラ名は、route.phpを参考に。ただし頭は大文字になる。
    最近流行の形式のとおり、"/"はindex()。"/[アクション名]/"は[アクション名]()にマッピングされる。
  3. /app/Model/[モデル名].php
    モデル名は、コントローラ名を単数形にしたもの。
    (コントローラクラス内にて$usesで定義してある場合は、そちらも。)
    今回のサンプルの場合、データ入力時のバリデーションルールがここにあります。
  4. /app/View/Layouts/default.ctp
    全てのページの基本的なレイアウトが記述されている。
    $content_for_layoutに、後述のビューの内容が展開される。
  5. /app/View/[コントローラ名]/[アクション名].ctp
    各アクションに対応したビュー。内容はPHP。
■実装しての感想
最終的な比較まとめは、一通りコードがそろった時点でやります。
まずは、今回CakePHP 2.0で実装していて感じた率直な内容です。
  • CakePHP1.x系に比べて、命名規約が感覚的にわかりやすくなった。
    1.x系はかつて触った事があったのですが、ファイル名の命名規約が結構独特な部分が多く、慣れるまで面倒だった記憶があります。
    2系になってから、命名規約が普通っぽく(Javaっぽく?)なったので、あまり戸惑う事なく決めれるようになりました。
    逆に、1.x系の記憶があって「え!?こんな命名でいいの?」とびっくりしたくらい…。
  • モデル周りが楽!
    規約に沿った名前付けさえしていれば、何も記述しなくてもデータ取得や更新が出来るのは楽です。
    今メインで使っているフレームワーク(職場伝統の俺俺…)は、単純なデータ取得でもいちいちモデルにメソッドを追加しなくてはいけないので、かなりうらやましいです。
  • "data"の罠。
    テーブル名を"data"でやろうとすると、データ更新でFatal errorになります。
    詳細を追った内容を書こうとすると、それだけで1記事になっちゃいますので今は省略します。(後日のネタになりますw)
  • ちょっとアクが取れた?
    Cake1.x系は、どこか「これがCakeのルールだ!」って感じのルールが多かったように感じたのですが、2系は結構素直に理解できる記述が増えたように思います。
    PHP5.2以降対応となった事で、自然な記述が出来るようになったのかもしれません。
    (単純に、私のコーディングスタイルに合っているだけ、のような気もしますが;)
今日はここまで、です。
まずはCakePHPのインプレッションという形で。

明日はSymfony2…の前に、dataの罠についてちょっとだけ書こうと思います。

2011年10月12日水曜日

Access-Control-Allow-Origin

 ひっさびさにjQueryを使ってajaxであれこれやってたら、
 他サイトAPIはたたけても、自作したテスト用のサイトのJSONが取得できないという謎に躓いたので、メモ。

 ドメインが異なっている場合、表題のヘッダ(Access-Control-Allow-Origin)を設定していないと、データを読むことが出来ない。
 特定のサイトからのみ読み込みOKとする場合、サイトのアドレスを、全サイトからアクセスOKとする場合、*を指定する。
 とりあえずPHPだったので、以下の1文を追加して、解決。

header("Access-Control-Allow-Origin:https://**********.jp");

 さて、上記ヘッダが正しく設定されていない場合、JSON出力側のサイト(サーバ)はどうなるのかというと
 これ、ヘッダを見て、JSON取得側が表示を拒否っているだけなので、出力側の処理は正常終了扱いになっています。
 PHP等々が動いていて、サーバ上でデータの処理やメール送信を行っている場合、そこまでは正常に動くわけです。
 ブラウザからURLを直でたたいても、正常出力されちゃうわけです。

 ブラウザのエラー出力をしっかり読めばすぐわかる話なのですが(苦笑)結構解決に時間がかかったので、メモ代わりに載せときます。
 

2011年9月5日月曜日

TDDBC 1.7 行ってきた

と、いうわけで。
先日行われたTDDBC 1.7に参加して参りました。
え、もう先日じゃないって?ぅぅ、すいません……。

内容そのもののお話は、ほかの参加者の方々のblogで語りつくされていると思いますので
私は、自分視点の感想等々を書くことにします。
時系列別に、どういう内容だったか知りたい方は、主催の@shishi4twさんの記事をご覧ください。

■前提
参加時点での私のTDDに関する技術レベル(?)は
興味があってPHPUnit(あるいはJUnit)のサンプルソースを動かしたことはあるけど、業務等の実際の開発で使ったことは無い程度、です。
一応、「車窓からのTDD」をPHPで書き直すという予習は、事前にやってました。
ついでに言うと、勉強会自体、片手で数えられるほどしか参加しておらず、これほど少人数の勉強会は初だったのでガッチガチに緊張してました…。

■TDDそのものについて。
最初に基調講演を聴いて感じたのは、TDDにおけるテストコードは、自然言語の仕様と実際のコードの間をつなぐ中間言語なんだな、ということ。
元々、日本語の仕様があって、それを元にプログラマが頭の中で考えてコードに起こしていくのが、旧来のTDDではない開発手法。
で、その仕様書をいかにプログラマにわかりやすくするか?っていうので、たとえばUMLとかが出てきたように思うんですよ。
(UMLにはそれ以外にも色々な意味合いがあるので、一概にUMLの用途を決める発言じゃないです。念のため)
実際、授業でプログラミングを習った際には、一度アルゴリズムを文字で書くための簡易言語っぽいものが利用されてました。
・iに1を足す
■iが10になるまで繰り返す
  ・iを出力する
こんな感じの。
ただ、自然言語や図などの人間が解釈するための仕様だと、プログラムコードに直す際のギャップが激しい。
なんせ、すっごい曖昧じゃないですか。日本語が曖昧って問題もありますが、そもそも自然言語って時点で、白黒はっきりしないことが多い。
一番大切な土台となる仕様が曖昧な状態で開発をしてたわけですね。今までは。泥の上にいきなり家を建てるような感じ。
そこで、いきなり家を建てるのではなくて、基礎やったりコンクリで固めたりして、土台をしっかりさせる作業が、TDDにおけるテストケースを書くって事。
土台がしっかりしてるから、プログラマも安心して作業できるし、完成するものもちょっとやそっとじゃ倒れない。最高です。
さらに言えば、テストケースを書くためには、問題の粒度を細かくする必要がどうしても出てくる。すると、小さな単位から問題を片付けることが出来るようになって、開発の速度と制度もUPする。
良い事尽くめですね。

ただひとつ、問題としては、どうしてもその利点を他者に伝えるのが難しいということ。
特に、よく知らずに「テスト」という単語だけで否定している人も多いんですよねー…。
こればっかりは、自分がしっかりTDDできるようになって、実際の成果を見てもらうほか無いのかな、って感じたり。

■ペアプロについて。
ペアプロという単語をはじめて聞いたのが、よりによってこのページだったんですよねー!
http://www.eclipsewiki.net/eclipse/index.php?%A5%BD%A5%ED%A5%D7%A5%ED%A5%B0%A5%E9%A5%DF%A5%F3%A5%B0%A5%D7%A5%E9%A5%B0%A5%A4%A5%F3
(Javaメインで使ってたころ、Eclipseのプラグインあさってたら見っけた)
Wikiからのリンクは死んでますが、配布元のページの製品とサービスあたりからたどると見れます。バーディ君…。

と、言う訳で、そこまで凄い物って印象はありませんでした。むしろ、割とキワモノなイメージが無意識のうちに…(苦笑)

実際やってみての感想ですが…。
開発中に出てくる小さな悩みの解決は、本当に早いです。
「正規表現のあの記法ってどんなんだったっけ?」「str_lenだっけstrlenだっけ?」等の文法的な話とか
「ここって別メソッドにしたほうがいいかな?そうでもない?」「この変数名何にしよっか?」等リファクタリング的なところまで。
特に、開発初期はこういうしょうもない部分で時間をとられたりするものですが、そういった事が無くサクサク進むのは凄くよかったです。

開発が熟すまでは定期的にペアプロを行うとイイカンジかな、と思いました。

ただ、私自身がすっごいガッチガチだったため、あんまり突っ込んだ話が出来なかったのが非常に申し訳無いです…。

■コードレビュー
1回目が私のペアでした。ただでさえ緊張してたのに…!
コードレビューをして思い出したのは、学生時代の部活動でした 。コンピュータ部だったんですよ。
ただのゲーム部というわけではなく、ちゃんとしたコンテストに参加することを目的とした部だったので
入部試験として、fizzbuzzでは無いものの、難易度的にはそれに近いコーディングのお題が出されるんです。
で、実装したコードを先輩の方々に見てもらって評価してもらう、と。
当時はコードレビューなんて単語も知りませんでしたが、やってることはまさにコードレビューですね。
(このお題の話については、そのうち書いてみようと思ってます。身元バレが怖いケド)

業務でも、たまにコードレビューが行われることがありますが、業務上のレビューは、上司に採点してもらうって形がほとんどなので、「ひとつのコードを叩き台にみんなで論議する」というタイプのコードレビューは本当に懐かしかったです。
また、何か機会があればやってみたいですね。

■ツール
「道具を大切に」「ひとつのツールをカスタマイズして使い込む」
という話が何度もありました。
が。
私自身、それとは対極に居る人間だったので、今回一番びっくりした事です。

学生時代、誰かにヘルプを頼まれそのまま人の環境で作業することが多かった身としては、自分用のカスタマイズされた環境じゃないと作業が出来ない、っていうのは避けたかったんです。
その経験を今になってもずるずる引きずっているため、開発環境はなるべくデフォルトで、ってやってました。

それも悪くは無いとは思うのですが、色々な人の環境(今回はVimが多かったです)に触れるにあたって、自分用の環境を1個持っておくのも悪くないと思いました。
TDDBC直後、職場の開発環境が吹っ飛んだので、これを期にってことでVim入れたりもしてます。
設定ファイルいじったり、小さいスクリプトを書くのには凄く便利です。
ただ、FW等の大きいソースコードを追う時は、どうしてもEclipseやNetBeansのほうが便利なので、そこは使い分けでやってます。

■お題について
最初のお題は、割と素直に解けるお題だったと思います。
TDDとしては、正規表現の確認のために、いちいちテスト用のコードを作って実行して確かめて、なんて手間がかからないのが楽でしたね。
テストケースを書くことによって、お題の中で言及されていない仕様(=の数が左右違う時、とか)に気づくことが多かったのも良い事でした。

2つ目のお題については、お題自体が複雑なのと、書きたいことも多くもう少し時間がほしいので、別にまとめ…る、予定です。

■その他いろいろ
箇条書きで。

  • 緊張しまくりでホントすいませんでした。小規模の勉強会への参加が初だったんですよー!っていう言い訳。
  • リストバンドほしかったけど、Mサイズがでかすぎて腕にはまらなかった…orz
  • PHP関係のいろんな人と知り合えた!良かった。でもSymfonyノータッチノーチェックですいませ…ん…。最近、Symfony2のチュートリアル触り始めました!
  • TDDを実務に組み込めるか?は悩ましい所。コーディング担当が自分のみってプロジェクトでは、やれそうだけど、そもそもオブジェクト指向メンドクセって人が居る環境なので、環境全体に浸透させるには壁が分厚いデス。
  • 懇親会は予定があったので行けませんでした。楽しかったようなのですごくうらやましいです。次の機会があれば参加したいですね。
  • Objective-cのOpenGL ESプログラミングを、一時期やっていたんだけど、さすがにああいう所だとTDDは難しそうだな、とか。
  • TDDは、グラフを描くために式を考えるんじゃなくて、先に重要なポイントの点をたくさん取っておいて、近似値のグラフを描いて、そっから式を逆算するようなイメージ。
  • キイロイトリの巨大なぬいぐるみ、家にあります(?)

とりとめも無くなってきたので、以上ということで!

2011年8月7日日曜日

VirtualBox上のUbuntu11.04にPHP開発環境

tddbc1.7参加にむけて、サブPCのEeePC 1215Nに、PHP開発環境を立てることになったので、作業メモ。
※仮想環境で作業してみた結果、サブPCのスペックでは快適に作業できなかったため、実際にはUbuntu11.04をデュアルブートで利用しています。
NetBeansのインストール以降の作業は、仮想・物理マシン関係なく行えるので、そのまま残しておきます。

■仕様
  • VirtualBox上のUbuntu11.04をメインにする。
  • Apache、PHPはゲストOS内で動かす。
  • ホストOSのブラウザからも、開発中のサイトの確認ができるようにする。
  • PHPUnitも動くように。
  • IDEはNetBeans7.0を選択。一応、Eclipseも動くようにはしておく。
■前準備
  • Ubuntu Japanese Teamのサイトから、日本語版のUbuntu Desktop 11.04のisoをDLしておく。
  • VirtualBoxは最新版をDL、インストールしておく。
■VirtualBoxにUbuntu11.04をインストールする。
  1. VirtualBoxで新規ボタンを押して、ウィザードにしたがって新規仮想マシンを作成。
  2. まずは設定で、
    システム→メインメモリ:1024MB,
    ディスプレイ→ビデオメモリ:128MB, 拡張機能:3D...にチェック,
    ネットワーク→アダプタ2を有効化、割り当てをホストオンリーアダプタに。
  3. 仮想マシンを起動。初回起動ウィザードにて、Ubuntu11.04のisoイメージを選択する。
  4. Ubuntuのインストールウィザードに従って、インストール。
  5. 再起動後、ログイン。クラシックモードでの起動になることを確認。
  6. アップデート・マネージャが起動すると思われるので、そのままアップデートを実行。
  7. Guest Additionsのインストール。
    VirtualBoxのメニューのデバイスから、Guest Additionsのインストールを選択。
    Ubuntu上でターミナルを起動し、GuestAdditionsインストールスクリプトを実行。
    完了後、再起動。
  8. テーマ崩れの調整。
    こちら[http://akira.matrix.jp/?p=393]を参考に、/etc/gdm/Xsessionを修正し、再起動。
  9. ccsmを入れて見た目を好みに調整。この辺はお好みで。
■NetBeans7.0.1のインストール
  1. NetBeansの公式サイト[http://netbeans.org/]から、インストーラをDLする。
    ダウンロードバンドルは、とりあえず「すべて」にしておく。
  2. ダウンロードしたインストールシェルスクリプトを、ターミナルから叩く。sudoつけて。
  3. ウィザードに従ってインストール。
    基本的には「Next」でOKだが、JUnitのインストールは行うこと。
  4. 文字を滑らかにする。
    /usr/local/netbeans-7.0.1/etc以下にあるnetbeans.confを開き、netbeans_default_optionsの最後に以下を追記。
    -J-Dawt.useSystemAAFontSettings=lcd
  5. NetBeansを起動。
    ヘルプ→更新の確認。更新があれば行っておく。今回は無かった。
■Apache+PHPのインストール。
  1. ホストOS→ゲストOSにpingが通ることを確認しておく。
    Ubuntu側でifconfigを実行し、eth1のIPアドレスを確認。
    そのIPアドレスにホストOSからpingを打つ。
  2. apacheのインストール。
    sudo apt-get install apache2
  3. apacheの起動確認。http://ゲストOSのIPアドレス/にアクセス。It works!と表示されればOK。
  4. PHPのインストール。
    sudo apt-get install php5
  5. 念のため、apacheを再起動。sudo /etc/init.d/apache2 restart
  6. ドキュメントルートの/var/wwwのパーミッションを変更。一般ユーザからかけるようにしておく。
  7. /var/wwwにphpinfo()を実行するだけのPHPスクリプトを配置し、ブラウザからアクセスする。
■PHPUnitのインストール
PHPUnitのマニュアル[http://www.phpunit.de/manual/current/en/installation.html]を参考にインストールを行う。
  1. pearをインストール
    sudo apt-get install php-pear
  2. pearのバージョンをあげておく。
    sudo pear upgrade PEAR
  3. pear channel-discoverにて、上記マニュアルに記載されている3つのチャンネルを登録する。
  4. PHPUnitをインストールしようとすると、いくつか足りないパッケージがあり失敗するので、都度インストールを行う。
    最終的には、以下のようになる。
    sudo sudo pear install channel://pear.php.net/Net_URL2-0.3.1
    
    sudo pear install channel://pear.php.net/HTTP_Request2-2.0.0RC1
    sudo pear install phpunit/PHPUnit
  5. phpunit --versionと叩いて、バージョンが表示されることを確認しておく。
■NetbeansとPHPUnitの連携を確認

  1. NetBeansを起動。PHPプロジェクトを作成。
  2. 適当なクラスを新規作成。
    空で良いのでメソッドを1個追加しておく。
  3. 「ツール→PHPUnitテストを作成」を実行。
  4. テストフォルダを選べと言われるので、参照ボタンを押してフォルダを選ぶ。
    プロジェクトフォルダと同一だと怒られるので、新規に"test"というディレクトリを作って、選択しておく。
  5. プロジェクトをテスト(Alt+F6)で、テストを実行。100%通ることを確認。