A Day In The Boy's Life -46ページ目

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

CakePHPでは、アプリケーションで使う各種情報をセットするcore.phpやbootstrap.phpなどの設定ファイルがあるものの、コアとなる情報とは設定を分離したいという要件も出てきます。

今回は、そんな要件をかなえるために、オリジナルの設定ファイルの作り方と利用方法を解説します。



オリジナルの設定ファイルを作る


CakePHPの設定ファイルは、Symfonyの設定ファイルのようにYAML形式など特殊な構造となっているわけでなく、core.phpなどを見てわかるように単なるPHPファイルとなっています。

(独自のリーダーを定義できるのでXMLやYAMLのリーダーを作ればその設定ファイルを読めるみたいですが)

なので、設定情報はPHPの構文どおりで自由に書けるわけです。


オリジナルの設定ファイルも取りあえずPHPの構文でエラーが無い状態であれば好きに記述して読込むことができます。

例えば、以下のようなオリジナルの設定ファイル(myApp.php)を用意します。

設定ファイルは、Configディレクトリ(/path/to/CakePHP/app/Config/以下)に配置しておきます。


<?php

$config["Dir"] = "/path/to/your/app/";

$config["message"] = array(0 => "hoge",
                           1 => "foo");


設定内容は、$config変数に配列として詰め込んでいきます

これ以外の変数で指定しても無視されます。

上記の設定ファイルを読込むには、コントローラーファイルで以下のように呼び出します。


<?php

App::uses('AppController', 'Controller');

class PagesController extends AppController {

    public $name = 'Pages';

    public function display() {
        Configure::load("myApp.php");
        $dir = Configure::read("Dir");
        $msg1 = Configure::read("message.0");
        $msg2 = Configure::read("message.1");
    }
}


Configure::loadにより、設定ファイルが読込まれConfigure::readにより呼び出したいキーを指定します。

呼び出したい設定値が配列の場合はキーを指定すれば全体が、多次元配列の場合は上記のようにドットでドットで区切ることで指定のキーの値を取り出すことができます


ちなみに、Configure::loadはcore.phpやbootstrap.php内でも記述できます

必ず読込む設定ファイルとなっているようなものであれば、コントローラファイルではなくbootstrap.phpなどから呼び出しておけば、必要なたびにコントローラ内で操作する必要がありません。



設定ファイルの操作方法


既に半分ほどは書いているのですが、設定ファイルのその他の操作方法についてまとめておきます。


設定ファイルの読み込みについては、先ほど書いたようにConfigure::readにより行えますが、呼び出すキーを指定しなかった場合は、現在の設定ファイルの全体が読込まれます

これにより、core.phpやbootstrap.phpなどCakePHPコアに関する設定も含めて現在の設定情報が確認できますので、CakePHPの挙動が意図どおりになっていないというような場合に確認するのに便利です。

(厳密に言えばデフォルトの引数が「debug」となっており、これを指定すると全設定を返します)


設定ファイルは書き込みを行うこともできます。

単体ではあまり利用シーンが思い浮かびませんが、この後紹介する設定ファイルの書き出しと合わせれば、オリジナルの設定ファイルをDBの状況などにあわせて書き換えるといったことができたりします。


<?php

App::uses('AppController', 'Controller');

class PagesController extends AppController {

    public $name = 'Pages';

    public function display() {
        Configure::load("myApp.php");
        // messageキーに情報を追加
        Configure::write("message.2", "fugafuga");
        // 追加した情報を呼び出し
        $msg3 = Configure::read("message.2");
    }
}


設定ファイルに書き出した情報は、グローバル変数のように各メソッドで呼び出すことはできますが、セッション情報のように記録されているわけでなく、ページ遷移をしてしまうと失われます

また、書き出した設定を意図的に削除したい場合はConfigure::delete()で行えます。


設定ファイルを保存したい場合は、下記のConfigure::dumpを使います。


<?php

App::uses('AppController', 'Controller');

class PagesController extends AppController {

    public $name = 'Pages';

    public function display() {
        Configure::load("myApp.php");
        Configure::write("my_flg", "hogehoge");
        Configure::dump("myApp.php", 'default', array('my_flg'));
    }
}


上記の場合、myApp.phpファイルに設定内容を保存します。

ただし、設定する(dump)際に「my_flg」というキーを指定しています。

これは、その上で書き込んだキーとなっており、指定のキーを値を保存するように指定しています。


<?php
$config = array (
  'my_flg' => 'hogehoge',
);

特に特定の情報だけでなく、すべての設定値を保存したい場合は、特にこの第3引数のキーの指定は要りません。

その場合、core.phpやbootstrap.phpの内容も含めてすべての有効な設定が保存されることになりますが。


そして、この設定ファイルを保存する際の注意点としては、app/ConfigディレクトリへApacheユーザーが書き込むため、パーミッションを設定しておかなくてはなりません。


Warning (2): file_put_contents(/path/to/cakephp/app/Config/myApp.php): failed to open stream: Permission denied [CORE/Cake/Configure/PhpReader.php, line 100]


設定ファイルだけでも多様な操作ができるわけですが、サブシステムごとに設定を切り分けたいという場合や、アクションごとに読込む設定を変えたいという場合は、こういったオリジナルの設定ファイルを使う手段が有効になるかもしれません。





小さなチーム、大きな仕事〔完全版〕: 37シグナルズ成功の法則/早川書房
¥1,575
Amazon.co.jp


どんな大企業に勤めようと、仕事をする上では細分化された組織またはチームで業務をしているという人は多いと思います。

この本はそんな小さなチームをどう動かしていくか、その中での役割や考え方を面白く解説してくれます。

あまり大それた野望や仕事に対してモチベーションを持ってない人、そして世の中でよく言われるビジネスの正攻法に対して疑問を持っている人にも有効かもしれません。

何よりも、この本の中でその対象ははっきりと描かれています。


そして、これは、ひとりでビジネスを始めたいなんて考えもしなかった人たちの本だ。できるわけがないと思っているかもしれない。時間も、資金も、やりぬく意思もないと思っているのかもしれない。あるいは、単に自分自身を試すのを恐れているのかもしれない。ビジネスの世界は汚いと思っているのかも。理由はなんであれ、そういう人のための本でもある。

この本では、仕事はこうするべきだという今まで社会に出てそれなりに経験を積んだ人であれば多分聞いたことであろう金言を否定したりしています。

成長路線を否定し、顧客への細かい要望を全て受け入れていくことを否定し、チームを一箇所に集めて細かいコミュニケーションをとることを否定し、製品のプロモーションも否定したりしています。


私たちは普段仕事をしていて、そんな当たり前といわれることに踊らされ、思考をがんじがらめされているのかもしれないなということを感じたりします。

この本では、そんな堅苦しい思考を随分と楽にしてくれるかもしれません。

成功の仕方というのは、他人がやってきた経験を基にした大げさなことを常に真似続けるというのはあまりに大変に思えるでしょう。

そもそも、成功の種類というのも色んなものがあるわけですしね。


自分にあった成功の仕方を見つけたいという人は読んでみてはいかがでしょうか。



目次


はじめに

まず最初に

見直す
 現実の世界なんて無視しよう
 「失敗から学ぶこと」は課題評価されている
 計画は予想に過ぎない
 会社の規模なんて気にしない
 仕事依存症はバカげている
 「企業家」はもうたくさん

先に進む
 世界にささやかに貢献する
 あなたに必要なものを作る
 まずは作り始めよう
 「時間が無い」は言い訳にならない
 一線を画す
 ミッション・ステートメント・インポッシブル
 外部の資金は最終手段
 必要なものは思ったより少ない
 新興企業ではなく企業を始めよう
 売却するつもりのビジネスは廃却されることになる
 身軽でいること

進展
 制約を受け入れる
 中途半端な一つの製品でなく、よくできた半分の製品
 芯から始める
 始めのうち詳細は気にしない
 決断することで前に進む
 キュレーターになれ
 やることを減らす
 変わらないものに目を向ける
 ツールより中身が大事
 副産物を売る
 いま、始める

生産性
 書類上の合意は幻想
 やめたほうがいいものを考える
 邪魔が入る環境では生産性は上がらない
 会議は有害
 解決策はそこそこのものでかまわない
 小さな勝利を手に入れる
 ヒーローにはなるな
 睡眠をとろう
 あなたの見積もりは最悪だ
 長すぎるToDoリストは終わることがない
 小さな決断をする

競合相手
 商品をありふれたものにしない
 真似てはいけない
 けんかを売る
 競合相手以下のことしかしない
 競合相手が何をしているのかなんて気にしない

進化
 基本的に「ノー」と言おう
 顧客を(あなたよりも)成長させよう
 熱意を優先順位と混同するな
 自宅でも良いもの
 顧客の声を書き留めてはいけない

プロモーション
 無名であることを受け入れる
 顧客をつくる
 競合相手に「教える」
 料理人を見習おう
 舞台裏を公開する
 造花が好きな人はいない
 プレスリリースはスパム
 「ウォール・ストリート・ジャーナル」は忘れよう
 ドラッグの売人の方法は正しい
 マーケティングは部署ではない
 「一日にして成功」はない

人を雇う
 まずは自分自身から
 限界で人を雇う
 無用な人は雇わない
 会社を「知人のいないパーティ」にしない
 履歴書はばかばかしい
 経験年数は意味がない
 学歴は忘れること
 全員が働く
 「自分マネージャー」を雇う
 文章力のある人を雇う
 最高の逸材はどこにでも
 社員をテストドライブする

ダメージ・コントロール
 過ちへの対応は自分でひきうける
 対応の速度はすべてを変える
 誤り方を知る
 全員を最前線へ
 文句は放っておく

文化
 文化はつくるものではない
 決定は一時的なもの
 ロックスターは環境がつくる
 従業員はガキではない
 五時に帰宅させる
 大げさに反応しない
 あなたらしく話す
 四文字言葉
 「なるたけ早く」は毒

最後に
 ひらめきには賞味期限がある



いけないとわかっていても時間の関係や取りあえずという誘惑からコードをコピーしてしまい、リファクタリングの際にはどこをどうしたのかわからなくなってしまって、そのまま放置してしまうというのはよくあったりもします。

また、コードレビューの際にも「これと似たような処理をどこかで見かけたな」と思うこともよくあったりして、そういったPHPの冗長なコードを効率的に発見してくれるツールがphpcpdです。



phpcpdをインストールする


phpcpdはPEARで提供されているのでpearコマンドでさっくりインストール、といきたいのですが自分の環境だと結構めんどくさかったです。

ちなみに、PEAR自信のバージョンが1.9.4以上を求められますので、環境が合わない場合は


# pear upgrade PEAR

とかしてPEARのバージョンをあげておきましょう。


最初に、PEARのチャネルを登録して、phpcpdをインストールします。


# pear channel-discover pear.phpunit.de

# pear install phpunit/phpcpd
Unknown remote channel: components.ez.no
Unknown remote channel: pear.netpirates.net
Unknown remote channel: pear.symfony.com
phpunit/phpcpd requires package "channel://components.ez.no/ConsoleTools" (version >= 1.6)
phpunit/FinderFacade requires package "channel://pear.netpirates.net/fDOMDocument" (version >= 1.3.1)
phpunit/FinderFacade requires package "channel://pear.symfony.com/Finder" (version >= 2.1.0)
downloading PHP_Timer-1.0.4.tgz ...
Starting to download PHP_Timer-1.0.4.tgz (3,694 bytes)
....done: 3,694 bytes
install ok: channel://pear.phpunit.de/PHP_Timer-1.0.4


しかし、上記のようにPHP_Timerはインストールされていますが、phpcpdはインストールされません。

幾つか必要なパッケージが要るみたいなのでそれを先にインストールします。

といっても、新たなチャネルを登録してphpcpdのインストールをリトライをしてみます。


# pear channel-discover components.ez.no
# pear channel-discover pear.netpirates.net
# pear channel-discover pear.symfony.com

# pear install phpunit/phpcpd
downloading phpcpd-1.4.0.tgz ...
Starting to download phpcpd-1.4.0.tgz (10,439 bytes)
.....done: 10,439 bytes
downloading FinderFacade-1.0.5.tgz ...
Starting to download FinderFacade-1.0.5.tgz (4,498 bytes)
...done: 4,498 bytes
downloading fDOMDocument-1.3.2.tgz ...
Starting to download fDOMDocument-1.3.2.tgz (13,907 bytes)
...done: 13,907 bytes
install ok: channel://pear.netpirates.net/fDOMDocument-1.3.2
install ok: channel://pear.phpunit.de/FinderFacade-1.0.5
install ok: channel://pear.phpunit.de/phpcpd-1.4.0


これでようやくphpcpdがインストールされました。

インストールするとphpcpdコマンドが利用可能になっています。


# which phpcpd
/usr/bin/phpcpd

おまけ的な内容ですが、phpcpdを実行した際に下記のようなエラーが出た場合は、必要なパッケージがインストールされていないか、単純にPEARディレクトリにパスが通ってないためだと思われます。


$ phpcpd
PHP Warning:  require(SebastianBergmann/PHPCPD/autoload.php): failed to open stream: No such file or directory in /usr/bin/phpcpd on line 52
PHP Fatal error:  require(): Failed opening required 'SebastianBergmann/PHPCPD/autoload.php' (include_path='.:/var/www/lib:/usr/local/lib/php') in /usr/bin/phpcpd on line 52

後者の場合は、php.iniのinclude_pathにPEAR用のディレクトリを追加しておきましょう。

PEARディレクトリは、pear config-showで確認できます。



phpcpdの使い方


準備ができたところで、早速phpcpdの使い方を見ていきます。

phpcpdは基本的に対象ファイルを引数に指定すれば重複コードのチェックを行ってくれますが、幾つかのオプションを指定するとチェックする内容を柔軟に変更できます。


$ phpcpd --min-lines 5 --min-tokens 5 foo.php
phpcpd 1.4.0 by Sebastian Bergmann.

Found 1 exact clones with 11 duplicated lines in 1 files:

  - /home/work/workspace/foo.php:7-18
    /home/work/workspace/foo.php:28-39

25.58% duplicated lines out of 43 total lines of code.

Time: 0 seconds, Memory: 2.00Mb


上記は、foo.phpを解析するように指定し、その結果7行目から18行目と28行目から39行目に重複しているコードがあるという判定が出ています。

実際にチェックしてみたソースコードはこちら。


<?php

function getId() {
    $conn = pg_connect("host=localhost port=5432 dbname=fuga");

    $result = pg_query($conn, "SELECT id FROM dogss");
    if (!$result) {
        echo "Error!.\n";
        exit;
     }

    $arr = pg_fetch_all($result);

    $cnt = count($arr);
    for ($i = 0; $i < $cnt; $i++) {
        $key = key($arr[$i]);
        $data[$key][$i] = $arr[$i][$key];
    }

    pg_close($conn);
    return $data;
}

function getName() {
    $conn = pg_connect("host=localhost port=5432 dbname=fuga");

    $result = pg_query($conn, "SELECT name FROM dogs");
    if (!$result) {
        echo "Error!.\n";
        exit;
     }

    $arr = pg_fetch_all($result);

    $cnt = count($arr);
    for ($i = 0; $i < $cnt; $i++) {
        $key = key($arr[$i]);
        $data[$key][$i] = $arr[$i][$key];
    }

    pg_close($conn);
    return $arr;
}


DBから値を取得するメソッドが2つありますが、実質違うのはメソッド名とSQLだけです。


であれば、結構判定が漏れているのではないかと思うかもしれませんが、あまり細かいレベルで重複コードをチェックしていると、例えばIF文の処理一つでも冗長と判定されかねません。

phpcpdでは、その辺の調整を「--min-lines」オプション(指定の行以下の重複は無視する)と「--min-tokens」オプション(指定のPHPトークンの数以下は無視する)というオプションでできます。

先ほどの例だと、5行以下の重複とPHPトークンの数が5以下の該当コードは無視しています。

ですので、最初のpg_connectや最後のpg_closeやreturnの行は判定されていません。


ちなみに、PHPトークンはPHPコードの字句解析の単位でtoken_get_all メソッドで調べることができたりします。

先ほどのfoo.phpのコードのPHPトークンを調べてみると、


<?php

$file = file_get_contents("./foo.php");
$tokens = token_get_all($file);

var_dump(count($tokens));

表示される数は「282」です。

これは、コード全体でのPHPトークンですので、phpcpdの解析ではそれが「--min-lines」オプションを超える行数で、「--min-tokens」を超えるトークン数で一致していたら重複コードと判定されているようです。

これらのオプションは、デフォルトで「--min-lines」が5、「--min-tokens」が70となっているので、少し判定が厳しいかもしれません(これはソースコードの量にもよるので状況によって調整が必要でしょうが)。


あと、phpcpdは特定のPHPファイルを指定しなければならないというわけではなく、ディレクトリ単位や特定の2つのPHPファイルを解析対称にするということもできたりします。


# カレントディレクトリ以下を全て解析
$ phpcpd ./*

# foo.phpとbar.phpの2つを解析する
$ phpcpd foo.php bar.php


デフォルトでは、拡張子が「.php」のもののみが解析対象になるので、それを変更したい場合は「--names」オプションで拡張子を指定できます。

その他にも解析対象外の指定や重複コード部分を表示してくれるというようなオプションもあるので、ヘルプもあわせて参照してください。


$ phpcpd
phpcpd 1.4.0 by Sebastian Bergmann.

Usage: phpcpd [switches] <directory|file> ...

  --log-pmd <file>         Write report in PMD-CPD XML format to file.

  --min-lines <N>          Minimum number of identical lines (default: 5).
  --min-tokens <N>         Minimum number of identical tokens (default: 70).

  --exclude <dir>          Exclude <dir> from code analysis.
  --names <names>          A comma-separated list of file names to check.
                           (default: *.php)

  --help                   Prints this usage information.
  --version                Prints the version and exits.

  --progress               Show progress bar.
  --quiet                  Only print the final summary.
  --verbose                Print duplicated code.


自分自身でコードのチェックをしてみたり、コードレビューで冗長な書き方をしていないかをチェックしたいというときに作業が効率的に行えるのではないでしょうか。