前の記事 ≪:2009年4月8日 管理人のブックマーク
次の記事 ≫:綺麗で質感がうまく出されたWebアプリ用アイコンセット「Web Injection」

PHPで冗長化したコードを自動で検出してコードの最適化に使える「PHPCPD」

2009年04月09日-はてなブックマーク

スポンサード リンク
[PR] 英単語を忘却曲線アプリを使って超効率よく記憶する方法
Detecting duplicate code in PHP files : CodeDiesel

PHPで冗長化したコードを自動で検出してコードの最適化に使える「PHPCPD」。
プログラムが大きくなってくると、同じような処理を違う場所で何度もやっているということがあります。



ソースコードのファイル数が多くなってくるとそれを探すのは大変ですが、その冗長部分を自動で検出できるPHP Copy Paste Detector(CPD)のご紹介。

使い方の流れが書かれていたので、試してみました(Windows上で実施)

PHPCPDをPEARコマンドでインストールする

1) channel-discover でpearチャネル追加
Windowsの場合はコマンドプロンプトを開いて、pear コマンドを実行。Unix系でも同様にpearコマンドを実行
> pear channel-discover pear.phpunit.de
Adding Channel "pear.phpunit.de" succeeded
Discovery of channel "pear.phpunit.de" succeeded

2) phpunit/phpcpd をインストール
PEAR 1.7.1以降が必要なので、それ以下の場合は、pear upgrade PEAR でアップグレードしておきます。

> pear channel-discover components.ez.no
> pear install phpunit/phpcpd

downloading phpcpd-1.1.1.tgz ...
Starting to download phpcpd-1.1.1.tgz (8,078 bytes)
.....done: 8,078 bytes
install ok: channel://pear.phpunit.de/phpcpd-1.1.1

と、インストールは以上で簡単ですね。

PHPCPDを使う

3) phpcpd コマンドを使う
インストールが終わったら、phpcpdコマンドが使えるので以下のように実行します。
引数には、解析したいプログラムディレクトリを指定します。

> phpcpd ./phpbb

今回は、PHPで有名なオープンソースプログラムの phpBB を解析にかけてみました。
結果を見てみましょう。

> phpcpd ./phpbb
phpcpd 1.1.1 by Sebastian Bergmann.

Found 49 exact clones with 1330 duplicated lines in 42 files:

- includes/acp/acp_attachments.php:254-272
  includes/acp/acp_board.php:535-553

- includes/acp/acp_database.php:1542-1562
  includes/acp/acp_database.php:1636-1656

- includes/acp/acp_styles.php:828-851
  includes/acp/acp_styles.php:1184-1207

- includes/acp/auth.php:461-472
  includes/acp/auth.php:547-558

- includes/acp/auth.php:32-38
  includes/auth.php:40-46

...( 省略 )

0.78% duplicated lines out of 171326 total lines of code.

表示の説明をすると、以下の2ファイルの、それぞれ254?272行と、535-553行が被っているということになります

- includes/acp/acp_attachments.php:254-272
  includes/acp/acp_board.php:535-553

49箇所のコピペが42ファイルで、1330行も見つかりました。
171326行分の1330行ということで、0.78% が重複しているという結果が出ました。

実際に開いてみてファイルを比べてみるとどうでしょうか

includes/acp/acp_attachments.php
        foreach ($display_vars['vars'] as $config_key => $vars)
        {
            if (!is_array($vars) && strpos($config_key, 'legend') === false)
            {
                continue;
            }

includes/acp/acp_board.php
    foreach ($display_vars['vars'] as $config_key => $vars) 
    {
            if (!is_array($vars) && strpos($config_key, 'legend') === false)
            { 
                continue;
    }

インデントの数は違えど、きちんと検出してくれています。

また、phpcpdでは、検出の条件も指定できます。
例えば、デフォルトでは、5行以下や、70トークン以下の共通部分については、無視してしまいます。

行数の指定を--min-lines 4でオプション指定すると4行以下を無視する、という風に無視の条件をゆるく出来ます。
トークンに関しては、--min-tokens 40 のようにして同様に指定することが可能です。
より多くの冗長コードを見つけたい場合に使えるでしょう。

> phpcpd --min-lines 4 --min-tokens 40 ./phpbb  のようにして実行します。

共通部分は、関数やクラスに出来る部分は出来るだけ共通化して冗長性を少なくし、コードのメンテナンス性を高める手助けになりそうなツールですね。

で、この機能を提供している部分はクラスになっているので、他のツールに組み込んだりするのも簡単かも。
phpcpd の中身は以下のように、たったの2行です。

<?php

require 'PHPCPD/TextUI/Command.php';
PHPCPD_TextUI_Command::main();

XML形式での重複部分出力が出来たりもするようなので、別言語から使ったりPHPからrequireせずに読み込んだり出来ますね。

phpcpd --log-pmd projectPhpcpd.xml ./admin
phpBBほどの規模になってしまうと、重複が大きくて直すのが大変かもしれませんが、あまりプログラムの大きくないうちに、これを走らせてできるだけ冗長化をなくす努力をしておくと、メンテナンス性が高まって後々に楽になりそうですね。こうしたツールがないと、冗長を調べるのは大変というか不可能に近いところがあるので覚えておくとよいかも。

関連エントリ
関連の記事検索:PHP, 最適化, あとで試す, チューニング
スポンサード リンク

By.KJ : 2009年04月09日 07:05 livedoor Readerで購読 Twitterに投稿

間違いの指摘をしていただける方はメール、あるいはTwitter/FBでお願いします(クリック)