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

A Day In The Boy's Life

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

開発規模が大きくなればなるほど、開発が複数の人により分散化されるため、コーディング規約をつくり、それに従って開発を進めることの重要性が増してきます。

経験の浅いメンバーがいれば、コーディングの仕方が統一されていなかったり、その重要性にも気づきにくいだけでなく、独自の実装方法を身につけてしまうと癖にもなってしまうため、定期的にコードレビューをして書き方を修正していく必要もあるでしょう。


しかし、コードレビューだけでは膨大な量のコードを見るのにもかなりの労力となってしまいますし、コーディング規約に準拠しているかどうかは、保守性の観点から重要ではあるものの自分のタスクや迫っているスケジュールに対して直結しなかったりもするため、後回しになったりどちらかというと「各々ちゃんとルールを守ってコーディングしてね!」という観点になってしまったりします。


ということで、PHPで開発する場合でコーディング規約に準拠しているかをプログラム自体がチェックしてくれるPEARのPHP_CodeSniffer を使ってセルフチェックができるようにしてみます。

なお、ここではPHPのコーディング規約として有名なPEAR標準コーディング規約Zend Framework PHP 標準コーディング規約 を中心に書いています。



PHP_CodeSnifferのインストールと利用方法


PHP_CodeSnifferのインストールはpearコマンドを通して行えます。

今回は、RC版ではあるものの最新の1.3.0を使用しています。


- pearコマンドを使ってPHP_CodeSnifferのインストール

$ pear install PHP_CodeSniffer-1.3.0RC1

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

phpcsコマンド自体もPHPスクリプトですが、これを利用してコーディングチェックをすることができます。


$ phpcs -v --standard=PEAR hoge.php

上記のように-vオプション(結果の標準出力)を付け、--standardオプションにてチェックするコーディング規約の指定(上記の場合はPEARのコーディング規約によるチェック)、そして最後に対象のPHPスクリプトファイルを指定します。


--standardオプションで指定できるコーディング規約は、下記により確認できます。


$ phpcs -i
The installed coding standards are PHPCS, PEAR, MySource, Squiz and Zend

PEARとZend(Framework)に関してはわかるのですが、他の2つは・・・?


-vをつけることで標準出力にレポートの内容を出力しますが、ファイルに保存したり出力形式を変更することもできます。


$ phpcs --report=xml --report-file=hoge.xml --standard=PEAR hoge.php

結果は、なぜかPHP_CodeSnifferのインストールディレクトリ内に保存されますが。

その他にも幾つかオプションがあるので、詳細はヘルプ(phpcs --help)を参照してください。



PHP_CodeSnifferを使ってPEARとZend Frameworkのコーディング規約のチェックをする


ということで本題。

全てのコーディング規約をなめてたらかなりの量になるので主要なところを中心に書きます。


PHPタグに関するコーディング規約の確認


<?

echo "hello";

?>

----------------------------------------------------------------------
FOUND 2 ERROR(S) AFFECTING 1 LINE(S)
----------------------------------------------------------------------
 1 | ERROR | Short PHP opening tag used; expected "<?php" but found
   |       | "<?"
 1 | ERROR | Missing file doc comment
----------------------------------------------------------------------

----------------------------------------------------------------------
FOUND 2 ERROR(S) AFFECTING 2 LINE(S)
----------------------------------------------------------------------
 1 | ERROR | Short PHP opening tag used; expected "<?php" but found
   |       | "<?"
 5 | ERROR | A closing tag is not permitted at the end of a PHP file
----------------------------------------------------------------------

いきなり異なる結果が出ました。

どちらもPHPタグは短縮形ではなく、「<?php」の形式で使わなくてはならないというのは共通です(1行目)。

そして、Zend Frameworkの場合は、PHPの終了タグ(?>)は使用してはならないというのがあるので、そのコーディングエラーが表示されています。

PEARの場合、終了タグに関してのルールが記載されていません。

実際にPEAR本体のPEAR.phpには終了タグが書かれていませんが、その他のpearパッケージには終了タグを書いているものがあります。


PEARでのチェック時に出ているもう一つのエラーはドキュメントには必ずコメントを入れるというルールに従っていないというものです。

これは、PEARのコーディング規約に記載 があるものの、一方でZend Frameworkにもファイルの先頭にもドキュメントブロックを入れなければならないというルールがあるのにエラーとしてはでてきません。


文字列、配列に関するコーディング規約の確認


Zend Frameworkの方には文字列や変数の記述に関するコーディングルールがきっちり定義されています。

一方、PEARでは細かい規約を書いた項目がありません。

そして、PHP_CodeSnifferでも文字列に関してはチェックを行ってくれないようです。


<?php

$str = "hello " .
"world";

echo "${str}";

このコードはZend Frameworkでは正しい書き方ではない(※)のですが、チェックしてもエラーは出てくれません。

(というか、シェルスクリプトの変数の扱いのように${val}のような書き方ってできるんですね・・・・)

変数展開がないのならダブルクォートではなくシングルクォートを使うとか、文字列連結する場合は=の下に.を持ってくるようにするとか。


そして、配列の定義に関するコーディング規約もZendにはありますがチェックしてもエラーは出ません。

(PEARにはそもそもルールが記載されていない)


制御構造に関するコーディング規約の確認


<?php
for($i=0;$i<10;$i++) {
if($i==5){
echo "hello";
}
}

----------------------------------------------------------------------
FOUND 6 ERROR(S) AFFECTING 4 LINE(S)
----------------------------------------------------------------------
 2 | ERROR | Missing file doc comment
 3 | ERROR | Expected "for (...) {\n"; found "for(...) {\n"
 4 | ERROR | Line indented incorrectly; expected 4 spaces, found 0
 4 | ERROR | Expected "if (...) {\n"; found "if(...){\n"
 4 | ERROR | There must be a single space between the closing
   |       | parenthesis and the opening brace of a multi-line IF
   |       | statement; found 0 spaces
 5 | ERROR | Line indented incorrectly; expected at least 8 spaces,
   |       | found 0
----------------------------------------------------------------------

----------------------------------------------------------------------
FOUND 2 ERROR(S) AFFECTING 2 LINE(S)
----------------------------------------------------------------------
 3 | ERROR | Expected "for (...) {\n"; found "for(...) {\n"
 4 | ERROR | Expected "if (...) {\n"; found "if(...){\n"
----------------------------------------------------------------------


Zend Frameworkでチェックした結果は随分と少ないのですが、実際にはifやfor文の中での字下げに関するルールが存在したりします。

PEARの方はうるさく言われるんですが・・・。

共通してエラー表示されるのは、ifやfor文の条件を指定する()の前後には空白を1つ入れることというもの(3行目、4行目)。


その他にも、エラーはでないのですがZendの方では制御構造内の各条件の演算子の前後には空白を入れるというものがあったりしますが、エラーを検出してくれません。

関数定義、関数コールに関するコーディング規則の確認


<?php

foo(1,2,3);

function Foo($num1,$num2,$num3) {

return $num1+$num2+$num3;

}

----------------------------------------------------------------------
FOUND 7 ERROR(S) AFFECTING 4 LINE(S)
----------------------------------------------------------------------
 2 | ERROR | Missing file doc comment
 3 | ERROR | No space found after comma in function call
 3 | ERROR | No space found after comma in function call
 5 | ERROR | Function name "Foo" is invalid; consider "foo" instead
 5 | ERROR | Missing function doc comment
 5 | ERROR | Opening brace should be on a new line
 7 | ERROR | Line indented incorrectly; expected at least 4 spaces,
   |       | found 0
----------------------------------------------------------------------

----------------------------------------------------------------------
FOUND 3 ERROR(S) AFFECTING 3 LINE(S)
----------------------------------------------------------------------
 3 | ERROR   | No space found after comma in function call
 3 | ERROR   | No space found after comma in function call
 5 | WARNING | Consider putting global function "Foo" in a static
   |         | class
 5 | WARNING | Variable "num1" contains numbers but this is
   |         | discouraged
 5 | WARNING | Variable "num2" contains numbers but this is
   |         | discouraged
 5 | WARNING | Variable "num3" contains numbers but this is
   |         | discouraged
 5 | ERROR   | Opening brace should be on a new line
 7 | WARNING | Variable "num1" contains numbers but this is
   |         | discouraged
 7 | WARNING | Variable "num2" contains numbers but this is
   |         | discouraged
 7 | WARNING | Variable "num3" contains numbers but this is
   |         | discouraged
----------------------------------------------------------------------


こちらも随分と異なるチェック結果が出てきました。

共通のエラーとして表示されるのが、関数コール時の複数ある引数の区切りのコンマの後にはスペースを入れろというもの(3行目)。

そして、関数の始まりの{は改行してから書けというものです(5行目)。

異なる点は、PEARは関数内のタブをちゃんと入れろ(7行目)と言われ、そして関数名は小文字で始めろ(5行目)というエラーが出てきます。

Zend Frameworkでも関数名は小文字で始めるというルールがあるのですが、エラーが表示されません。


また、これはWARNINGとして出ているものですが、Zend Frameworkの方では関数をクラス内で定義しろというものが・・・(5行目)。

コーディング規則の中には、「グローバルスコープの関数はできるだけ使わないようにしましょう」とあるので、それによるものでしょうか。
もう一つ、Zend FrameworkのほうでWARNINGが出ているのは、変数名に数字を使うなというもの(7行目)です。変数名は英数字を使ってよいとはしていますが、数字を使うことはお勧めしないという風に規則には定義されています。


余談ですが、チェック結果の中でWARNINGを消したい場合は、下記のように-nオプションをつけて実行すれば表示されなくなります。


$ phpcs -v -n --standard=Zend hoge.php


クラス定義に関するコーディング規則の確認


<?php

$obj = new foo();
$obj->Bar();

class foo {

public $base = "hello";
private $str = "world";

function Bar() {
echo $this->base . $this->str;
}

}

----------------------------------------------------------------------
FOUND 12 ERROR(S) AFFECTING 6 LINE(S)
----------------------------------------------------------------------
  2 | ERROR | Missing file doc comment
  6 | ERROR | Class name must begin with a capital letter
  6 | ERROR | Missing class doc comment
  6 | ERROR | Opening brace of a class must be on the line after the
    |       | definition
  8 | ERROR | Line indented incorrectly; expected at least 4 spaces,
    |       | found 0
  9 | ERROR | Line indented incorrectly; expected at least 4 spaces,
    |       | found 0
  9 | ERROR | Private member variable "str" must be prefixed with an
    |       | underscore
 11 | ERROR | Method name "foo::Bar" is not in camel caps format
 11 | ERROR | Missing function doc comment
 11 | ERROR | Line indented incorrectly; expected 4 spaces, found 0
 11 | ERROR | Opening brace should be on a new line
 12 | ERROR | Line indented incorrectly; expected at least 8 spaces,
    |       | found 0
----------------------------------------------------------------------

----------------------------------------------------------------------
FOUND 3 ERROR(S) AFFECTING 3 LINE(S)
----------------------------------------------------------------------
  6 | ERROR | Opening brace of a class must be on the line after the
    |       | definition
  9 | ERROR | Private member variable "str" must contain a leading
    |       | underscore
 11 | ERROR | Opening brace should be on a new line
----------------------------------------------------------------------


PEARの方は、色々厳しく出ますね・・・。

共通のエラーとしては、クラスの初めの{は改行入れて書けというもの(6行目)と、プライベートスコープのメンバ変数は頭にアンダースコアを入れろというもの(9行目)、そして関数の初めの{は改行入れて書けというもの(11行目)です。


PEARの方は他にも、クラス名は大文字で始めろ(6行目)とか、関数名が大文字で始まることによりcamelCaps方式になっていない(11行目)とか、クラスや関数に関するコメントが書いてないよと言ったエラーが出てきます。



まとめ


現時点でのPHP_CodeSnifferでは、全体的にPEARはルールに沿ってかなり厳しくエラーが出てきますが、Zend Frameworkのコーディング規則のチェックは少し甘いかなというのが感想です。
規則は規則として、チェックする側はいいとこ取りしてくれればちょうどいいのにとか思ったり。


全体的に、規則には書いているけどエラーは出なかったりする部分があるので、このツールを使っても完璧に規則に沿ったコードが出てくるということはなさそうですが、目視チェックするよりは全然効率的です。

何よりも作ったプログラマー自身がセルフチェックできますからね。


ここで紹介した以外にもそれぞれのコーディング規則には細かなルールが設定されています。

こういったツールを使えば、コーディングのルールというものも覚えやすいかもしれませんね。




会員制のサービスなど、ログインした状態でないとコンテンツをユーザーに見せたくないという仕様はよくありますが、HTMLなどはプログラムから簡単に保護ができるものの、ドキュメントや画像やFlashファイルなどを保護するのは若干の工夫が必要になってきます。


今回は、Apacheのモジュールの一つであるmod_actions を使って、ドキュメントルート以下に置いているWordやExcelファイル、画像やFlashなどプログラムから保護しにくいファイルに対して、セキュリティ実装を簡単に行ってみたいと思います。



一般的なドキュメントや画像ファイルを保護する実装


mod_actionsでの実装を行ってみる前に、ドキュメントや画像やFlashファイルなどをログインしていないユーザーから保護するためにはどういった方法があるかを考えてみます。


最も簡単な方法は、Basic認証をかませてログインしていないユーザーに強制的にIDとパスワードを求めるというものがあります。

指定のディレクトリ以下を全てBasic認証で保護するので、ユーザーがダイレクトにドキュメントファイルにアクセスしても、正規のログイン手段と同様にIDとパスワードを求めることができます。

しかしこれは、認証方法が固定化されるとともに、独自の認証を実装している環境では適用が難しかったりもします。


もう一つはPHPなどのプログラムを通してファイルを保護する方法があります。


<html>
<body>
<img src="load_image.php?img_file=hoge.gif" />
</body>
</html>

<?php

// 認証チェック
if (empty($_COOKIE['my_session'])) {
    header('HTTP/1.0 401 Unauthorized');
    echo "Unauthorized. Please login";
} else {
// ログインした状態であれば画像ファイルを呼び出し
    $file = pathinfo($_REQUEST['img_file']);
    readfile("/var/www/no-htdocs/images/" . $file['basename']);
}


imgタグ内でPHPファイルを指定し、load_image.phpによりドキュメントルート外に保存しているイメージファイルを読み込んで出力するというような実装になります。

ドキュメントルート外にイメージファイルを保存しておくことにより、ダイレクトにその画像ファイルにアクセスすることができません。

また、上記の例では簡単な実装にしていますが、ログイン時にセットされたCOOKIE情報などからログインされているかどうかをチェックすることで、このプログラムを悪用して画像ファイルが呼び出されることが無いようにしておく必要もあるでしょう。

しかし、この場合でも画像ファイルの保存先とその呼び出し方法が特殊となることにより、コンテンツが作りこみにくい状態になってしまいます。



mod_actionsでファイルを保護するためのセキュリティ実装を行う


mod_actionsを使えば、.htaccessファイルに数行書くのと認証チェック用のプログラムを配置するだけで、呼び出しもとのHTMLファイルには手を加えずにファイルを保護することができます。

まずは、Apacheの設定ファイルからmod_actionsのモジュールが呼び出されているか確認しておきます。


LoadModule actions_module modules/mod_actions.so

次に、セキュリティ保護したいディレクトリ直下に.htaccessファイルを配置します。


Action image/gif /check_login.php


上記のように、Actionディレクティブの後ろにファイルのMIMEタイプとそのMIMEに該当した場合の呼び出し先(check_login.php)を指定しておきます。

指定のMIMEに引っかかったらプログラムをコールしてくれるといったことがApacheの中で実装可能になるわけです。

これだけで、GIFファイルにアクセスされたらcheck_login.phpファイルが自動的に呼び出されます。


<?php

// 認証チェック
if (empty($_COOKIE['my_session'])) {
    header('HTTP/1.0 401 Unauthorized');
    echo "Unauthorized. Please login";
} else {
// ログインした状態であれば元ファイルを呼び出し
    readfile($_SERVER['DOCUMENT_ROOT'] . $_SERVER['PATH_INFO']);
}


上記の場合も簡単な認証チェック用のプログラムになりますが、ログイン時にセットされるはずのCOOKIEが存在しなかった場合は401のHTTPステータスコードとともにエラーメッセージを、正常にログインされた状態である場合は画像ファイルを呼び出して出力をしています。

この状態で、画像ファイルを呼び出してみます。

呼び出すHTMLは、単純にこんな感じで書いています。


<html>
<body>
<img src="/images/image.gif">
</body>
</html>


これを、ログインしていない(COOKIEがセットされていない)状態でアクセスしてみて、その時の通信状況をFireBugで見てみると、


- FireBugで通信状態を見た例(ログインしていない場合)

A Day In The Boy&#39;s Life-mod_actionsでセキュリティ保護


画像だけがHTTPステータス401が返され、ブラウザ上に表示されません。

また、mod_actionsを使わない場合で説明したHTMLとは違い、小細工なしのそのままのHTMLで実装することを可能にしてくれます。
デザイナーさんと分業するといった場合に、特にややこしい仕様を取り決めておく必要もなくなります。


ここでは、画像ファイルをセキュリティ保護する実装例を書いてみましたが、HTML自体であったりFlashやドキュメントファイルももちろん対応可能です。

それぞれ、.htaccessにそのファイルのMIMEを指定するだけで実装できます。


# Word File
Action application/msword /check_login.php
# Power Point
Action application/vnd.ms-powerpoint /check_login.php
# Excel File
Action application/vnd.ms-exce /check_login.php
# Flash File
Action application/x-shockwave-flash /check_login.php

ただし、当然ながら保護対象を増やせば増やすほど処理数が増えるためオーバーヘッドが大きくなってしまいます。

必要最低限の対象に絞るか、保護するファイルを保管するディレクトリを固定し、そのディレクトリに対して.htaccessファイルを配置するなどの工夫は必要になってきます。


また、こちらで検証してみたところPHPファイル自体はActionによりフックして別のPHPプログラムをコールするというようなことはできませんでした。

PHP自体のMIMEがうまく認識してくれないようです。(httpd.confに記述しているapplication/x-httpd-phpとかを指定してもダメ)

.phpの拡張子でActionをフックして、コールされる認証プログラムなどを別の拡張子にてPHPを動作可能にしておけばうまくいくかもしれません。

ただ、PHP自体には認証ロジックなどを入れることは可能なので、あまりmod_actionsを通すことの意味はない気もします。


ということで、一般的にロジックなどを埋め込むことができないファイルに対してログインのチェックなど何らかの前処理を行わせたい、といった場合は今回のmod_actionsを使えば既存の環境にあまり手を加えることなしに、簡単に実装することが可能になります。





先週の土日で紅葉を見るために中禅寺湖まで旅行に行ったのですが、そのときにiPhoneで撮った写真をアプリで加工してみました。

天気は、最悪の季節外れの台風というものだったので、あまりいい写真がないのですがね・・・。



- 旧イタリア大使館周辺1

A Day In The Boy&#39;s Life-中禅寺湖旧イタリア大使館周辺-1


加工したのは、moreMono アプリ。
赤に近い色をより強調させ、その他をモノトーン調に加工してくれます。

なので、紅葉で色づいた葉っぱを加工してみるとよりくっきり見えたり。


- 旧イタリア大使館周辺2
A Day In The Boy&#39;s Life-中禅寺湖旧イタリア大使館周辺-2

上記のiPhoneカメラオリジナルの画像をmoreMonoアプリで加工してみると


A Day In The Boy&#39;s Life-中禅寺湖旧イタリア大使館周辺-3

紅葉したもみじの葉がよりくっきり。


もう一つ、この画像を使って線画風カメラ というアプリで加工したものが下記。

あまり被写体が複雑だと線が多すぎてよくわからない画像になってしまいますが、まったく違った見え方でとても面白いです。


A Day In The Boy&#39;s Life-中禅寺湖旧イタリア大使館周辺-4

- 竜頭の滝上流

A Day In The Boy&#39;s Life-竜頭の滝上流-1


加工したのは、moreLomo アプリ。

moreLomoアプリを使えば、トイカメラ風に写真を加工してくれます。


- 中禅寺湖周辺
A Day In The Boy&#39;s Life-中禅寺湖周辺-1


加工したのは、レトロカメラ アプリ。
フィルムを通してセピア調に加工したり、上記の写真のように粒子ノイズを入れることで画像を粗く見せたりすることができます。


今回紹介したiPhoneアプリは全て無料なので、iPhoneを使って写真を撮るのも楽しくなりますね。

あとは、天気を味方につけることでしょうか・・・。