is_callable() のススメ

PHP で、クラスに関数が定義されている場合のみ当該関数を call する〜なんてことをする際に、下記のようなコードを書いていたのですが、、、

<?php
if (method_exists($hoge, 'func')) {
    $hoge->func();
}

「これって、func() が存在するけどその scope から呼べないケースどうなるん?private 関数とか。」という声を聞いて、これはまずそうだなと。
こんなとき、is_callable() ってのを使うと良いみたい。
PHP: is_callable - Manual
以下、サンプルコード。

<?php
class Hoge {
    public function this_is_public() {}
    protected function this_is_protected() {}
    private function this_is_private() {}
}

$hoge = new Hoge();
if (method_exists($hoge, 'this_is_public')) { echo 'aaa' . PHP_EOL; }
if (method_exists($hoge, 'this_is_protected')) { echo 'bbb' . PHP_EOL; }
if (method_exists($hoge, 'this_is_private')) { echo 'ccc' . PHP_EOL; }
if (is_callable(array($hoge, 'this_is_public'))) { echo 'ddd' . PHP_EOL; }
if (is_callable(array($hoge, 'this_is_protected'))) { echo 'eee' . PHP_EOL; }
if (is_callable(array($hoge, 'this_is_private'))) { echo 'fff' . PHP_EOL; }

$hoge->this_is_public();
//$hoge->this_is_protected(); // Fatal error: Call to protected method Hoge::this_is_protected() from context '' in ...
//$hoge->this_is_private(); // Fatal error: Call to private method Hoge::this_is_private() from context '' in ...
$ php ./test.php
aaa
bbb
ccc
ddd

method_exists() はアクセス指定が何であろうと、関数さえ定義されてれば true 判定。
is_callable() はアクセス可能な場合のみ、true 判定。
冒頭のケースでは、明らかに後者のが適切ですね。。。

試しに上のクラスを継承したら protected な関数も is_callable() = true と判定されました。

<?php
class Hoge { ... }
class Fuga extends Hoge {
    public function test() {
        if (is_callable(array($this, 'this_is_public')))    { echo 'ddd' . PHP_EOL; }
        if (is_callable(array($this, 'this_is_protected'))) { echo 'eee' . PHP_EOL; }
        if (is_callable(array($this, 'this_is_private')))   { echo 'fff' . PHP_EOL; }
    }
}

$fuga = new Fuga();
$fuga->test();
$ php ./test.php
aaa
bbb
ccc
ddd
ddd
eee

git でタグを切る/チェックアウトする

修正を push。

$ git push git.sample.jp:path.to.repos

このタイミングでタグを切っておこうと思い立つ。
hash で指定してみる。

git log --pretty=oneline

目的の hash でタグ切る。今回は tags_ver_1.0.0 という名前で。

git tag tags_ver_1.0.0 123456789012345678901234567890abcdeabcd

レポジトリに反映。

git push origin tags_ver_1.0.0

タグを指定してチェックアウトしてみる。

$ git clone git.sample.jp:path.to.repos
$ git branch
* master

branch は master のみ。
タグ一覧を確認。

$ git tag -l
tags_ver_1.0.0

さっき切ったタグが表示される。こいつをチェックアウト。

$ git checkout tags_ver_1.0.0
Note: checking out 'tags_ver_1.0.0'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

HEAD is now at 123456... fix invalid permission description
$ git branch
* (no branch)
  master

何かプチ怒られしました。
branch を作成せずに tag を checkout すると、detached HEAD なる状態になるようです。
タグを切った状態のスナップショットが欲しいだけならばコレで全く問題ありませんが、ここを起点にさらにバリバリ開発を継続するのであれば、ブランチを作成してチェックアウトしたほうがよさそうです。

$ git checkout -b tags_ver_1.0.0

print_a でお手軽 debug

php 開発で多用するのが、var_dump やら var_export やらによる debug 出力。
こんなカンジ。ちょっとめんどいですよね。

var_dump(var_export($hoge, true));

print_a を使うと、とてもお手軽です。


print_a を利用するには、debuglib.php というファイルを読み込む必要があるので、下記から取得。
phpdebuglib.de - このウェブサイトは販売用です! -&nbspphpdebuglib リソースおよび情報
使い方はとっても簡単。例えば下記のようなソースコードだと、

<?php
require_once "/path/to/debuglib.php";
class Hoge
{
    var $foo = 'foo';
    var $bar = 123;
};
$hoge = new Hoge();
$fuga = array(
    'aaa' => '111',
    'bbb' => '222',
    'ccc' => array(
        'ddd' => '333',
        'eee' => '444',
    ),
    'fff' => '555',
    'ggg' => $hoge,
);
print_a($fuga); // 使ってみた

ブラウザ上でこんな出力。グラフィカルでよいですね。

使うときに毎回 require するのはめんどくさいので、php.ini で自動で読み込んでおくとよいと思います。
自分の環境では下記に。
/etc/php5/apache/php.ini

; Automatically add files before or after any PHP document.
auto_prepend_file = "/path/to/debuglib.php"

追加した後は apache の reload をお忘れなく。

OS やら kernel やらの version を調べるコマンド

掲題の件、下記 2 コマンドをよく使う。
主に debian 環境にて。
■OS

$ cat /proc/version 
Linux version 2.6.26-2-xen-686 (Debian 2.6.26-26lenny2) (dannf@debian.org) (gcc version 4.1.3 20080704 (prerelease) (Debian 4.1.2-25)) #1 SMP Thu Jan 27 05:44:37 UTC 2011

■kernel

$ uname -a
Linux hostname 2.6.26-2-xen-686 #1 SMP Thu Jan 27 05:44:37 UTC 2011 i686 GNU/Linux

レポジトリからディレクトリを削除

下記コマンドで消せる。

 git rm -r hoge/
 git rm -r --cached hoge/

前者だとディレクトリ自体も消えます。
後者だとディレクトリは残った上で、インデックスからのみ削除されます。


上のコマンド叩いて git status すると、deleted 状態になっていることが確認出来ます。

git status
# On branch master
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       deleted:    hoge/foo
#       deleted:    hoge/bar

あとはいつも通り commit してあげれば OK。


なお、削除したディレクトリを復元はこんな。

git reset

削除時に --cached つけないとディレクトリ自体が消えてしまうので、復元するにはもう一度 checkout しなおす必要があるような。

input buffer overflow, can't enlarge buffer because scanner uses REJECT

gtags -v したら、下記エラーメッセージを出力して処理が止まってしまいました。

(snip)
[31363] extracting tags of path/to/file/xxx.php
[31364] extracting tags of path/to/file/yyy.php
input buffer overflow, can't enlarge buffer because scanner uses REJECT

エラーメッセージでググってみると、下記サイトがヒット。
http://www.stack.nl/~dimitri/doxygen/faq.html

一度に256K 以上の入力文字にマッチする 場合に起こるとか。ファイルがでかすぎる、と。
解決策として、「ファイル分割」か「ファイル自体を無視」の二択が紹介されているので、今回は後者で対処。

gtags.conf を修正します。
自分の debian 環境では、/usr/share/gtags/gtags.conf にありました。
念のため、home ディレクトリにコピって作業。

cp /usr/share/gtags/gtags.conf ~/conf/
emacs ~/conf/gtags.conf

デフォルトではこんなカンジ。

default:\
        :tc=gtags:tc=htags:
#---------------------------------------------------------------------
# Configuration for gtags(1)
# See gtags(1).
#---------------------------------------------------------------------
common:\
        :skip=GPATH,GTAGS,GRTAGS,GSYMS,HTML/,HTML.pub/,html/,tags,TAGS,ID,y.tab.c,y.tab.h,cscope.out,cscope.po.out,cscope.in.out,SCCS/,RCS/,CVS/,CVSROOT/,{arch}/,autom4te.cache/:

この :skip に、問題となったファイルを追記。今回はフォルダごとまるっと追記。

default:\
        :tc=gtags:tc=htags:
#---------------------------------------------------------------------
# Configuration for gtags(1)
# See gtags(1).
#---------------------------------------------------------------------
common:\
        :skip=GPATH,GTAGS,GRTAGS,GSYMS,HTML/,HTML.pub/,html/,tags,TAGS,ID,y.tab.c,y.tab.h,cscope.out,cscope.po.out,cscope.in.out,SCCS/,RCS/,CVS/,CVSROOT/,{arch}/,autom4te.cache/, path/to/file/

この gtags.conf を使用して、再度 gtags -v 実施。

gtags -v --gtagsconf ~/conf/gtags.conf

これで、先ほど止まった箇所(path/to/file/)を華麗にパスして処理継続してくれるはず。
その先で詰まったら、また同様に無視する対象を gtags.conf に追記。



ちなみに、、、今回問題となったと思われるファイル。

$ wc -l path/to/file/yyy.php 
65598 path/to/file/yyy.php 

うん。でかいな。