年も明けたことだしそろそろ一言言っとくか

あけおめ!



実は昨年、それはそれは可愛いムスメが産まれました。
毎日知らない人から必ず(誇張ではなく)可愛いと言われるので、
親バカフィルタのせいではないと確信しております。
今年も新たな気持ちでがんばって行こうと思います。

PHPで代理クラス(プロキシクラス)を使って、PHP_CodeSnifferでのERRORをWARNINGとして扱う。

※一般化して例を出したいけどちょっと面倒なので、PHP_CodeSnifferに限った例を出します。


警告は出したいけどERRORほどではない、ってときにWARNINGにしたいのだけど、
PHP_CodeSniffer自体にそういう機能が無いので、自前で何とかしないといけない。

何も考えずに実装しようとすると、該当メソッドをオーバーライドして"addError"の部分を"addWarning"に書き換えるなんてやってしまうのだけど、

<?php
// 親クラス
class Generic_Sniffs_SomeSniff
{
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) {
        // 長い長い記述A
        $phpcsFile->addError($error, $stackPtr);
        // 長い長い記述B
    }
}

// 自前クラス
class My_Sniffs_SomeSniff extends Generic_Sniffs_SomeSniff
{
    // override
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) {
        // 長い長い記述Aのコピー
        $phpcsFile->addWarning($error, $stackPtr);
        // 長い長い記述Bのコピー
    }
}

ってなってしまって、これは本当にお寒い。
DRYに反してるから親クラスに変更があったときに対応するのが非常に面倒。


$phpcsFile->addErrorを呼んだ時に代わりに$phpcsFile->addWarningを呼んでくれれば良いだけなので、どうにかしてaddErrorを書き換えられないかな、と。
以前色々試してPHPでは無理っぽいというのは分かっていたので、ラッパーかませたら何とかなるんじゃないかと思ってやってみたら、とりあえず何とかなった。

<?php
// 親クラス
class Generic_Sniffs_SomeSniff
{
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) {
        // 長い長い記述A
        $phpcsFile->addError($error, $stackPtr);
        // 長い長い記述B
    }
}

// PHP_CodeSniffer_Fileの代理クラス
class PHP_CodeSniffer_File_WarningProxy extends PHP_CodeSniffer_File
{
    private $ref;
    public function __construct(PHP_CodeSniffer_File $phpcsFile) {
        $this->ref = $phpcsFile;
    }
    // method proxy
    private function _proxy()
    {
        $backtrace = debug_backtrace();
        return call_user_func_array(array($this->ref, $backtrace[1]['function']), $backtrace[1]['args']);
    }
    // overrides
    public function setActiveListener() {return $this->_proxy();}
    public function addTokenListener() {return $this->_proxy();}
    public function removeTokenListener() {return $this->_proxy();}
    public function getTokens() {return $this->_proxy();}
    public function start() {return $this->_proxy();}
    public function cleanUp() {return $this->_proxy();}
    public function addError($error, $stackPtr, $code='')
    {
        return $this->ref->addWarning($error, $stackPtr, $code);
    }
    public function addWarning() {return $this->_proxy();}
    public function getErrorCount() {return $this->_proxy();}
    public function getWarningCount() {return $this->_proxy();}
    public function getIgnoredLines() {return $this->_proxy();}
    public function getErrors() {return $this->_proxy();}
    public function getWarnings() {return $this->_proxy();}
    public function getFilename() {return $this->_proxy();}
    public function getDeclarationName() {return $this->_proxy();}
    public function isAnonymousFunction() {return $this->_proxy();}
    public function getMethodParameters() {return $this->_proxy();}
    public function getMethodProperties() {return $this->_proxy();}
    public function getMemberProperties() {return $this->_proxy();}
    public function isReference() {return $this->_proxy();}
    public function getTokensAsString() {return $this->_proxy();}
    public function findPrevious() {return $this->_proxy();}
    public function findNext() {return $this->_proxy();}
    public function findFirstOnLine() {return $this->_proxy();}
    public function hasCondition() {return $this->_proxy();}
    public function findExtendedClassName() {return $this->_proxy();}
}

// 自前クラス
class My_Sniffs_SomeSniff extends Generic_Sniffs_SomeSniff
{
    // override
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) {
        return parent::process(new PHP_CodeSniffer_File_WarningProxy($phpcsFile), $stackPtr);
    }
}

これで、長い長い記述Aのコピーと長い長い記述Bのコピーを書かなくて済むし、親クラスに変更があった場合にも対応不要というわけで素晴らしす。

  • 外部に公開されているインターフェイスは全て委譲しないといけないので、全メソッドオーバーライドという虚しいことしてる。
    マジックメソッド__call使ったら一発で対応できるかなと思ったけど、親クラスに定義されてたらそっちが呼ばれちゃって__callが呼ばれなかった。
  • public function getFilename() {$this->getFilename();}とか書くのがかったるいので、backtrace使って楽した(function _proxy)
  • オーバーライドしてるメソッドの引数は紙面の都合上全部省略してる。E_NOTICE大量に出るから引数書いておいても良い。
  • 実は手元のコードでは全てのメソッドを上書きできるようになってるけど、紙面の都合上割愛。また後日掲載する予定。

PHPでディレクトリ一覧を取得するスマートではない方法

あるディレクトリに存在するディレクトリ一覧を取得する例。

<?php
$dirs = array();
if ($dh = opendir($dirName)) {
    while ($entry = readdir($dh)) {
        if (is_dir($entry) && $entry != "." && $entry != ".." ) {
            array_push($dirs, $entry);
        }
    }
    closedir($dh);
}

こんなんアホらしくて書いてられん。。。
カレントディレクトリならこれでOK。

<?php
$dirs = array_slice(array_filter(scandir('.', 0), is_dir), 2);


さて、$dirNameに対応するか。

<?php
$dirs = array_slice(array_filter(scandir($dirName, 0), is_dir), 2);

と、これが全くダメ。is_dirがfalse返してくる。
scandirがエントリ名だけ返してくるらしく、CWDから見たらそんなエントリは無いってことになってしまう。
そしてCWDからの相対パスを返してくれるオプションがねぇ。なんなんだよー。perlのFile::Findぐらい融通効かせてくれ。

とりあえずこんなんで行けるっちゃあ行ける。

<?php
$cwd = getcwd();
chdir($dirName);
$dirs = array_slice(array_filter(scandir(".", 0), is_dir), 2);
chdir($cwd);

けど、chdirしたくなかったので、

<?php
$dirs = (array_slice(array_filter(scandir($dirName, 0), create_function('$entry', 'return is_dir("' . $dirName . '/$entry");')), 2);
/*
$dirName = '...';
$dirs = array_slice(array_filter(scandir($dirName, 0), function($entry){return is_dir("$dirName/$entry");}), 2);
って書けないのもどうかと思うよ!!
→なんと!function($entry) use ($dirName)って書くっぽい!
*/

って・・・お寒い感じ。。。もうやだこの言語・・・。

NTEmacsでw32-symlinksを使えるようにする


; windows ショートカットを有効にする
; http://www.emacswiki.org/emacs/w32-symlinks.el
; ntemacs だと使えないぽい
;(setq w32-symlinks-handle-shortcuts t)
;(require 'w32-symlinks)
同じように悩んでいてとりあえず無理やり解決したので載せておく。

ショートカットかどうかを判断するために、ショートカットファイル内の
「L^@^@^@^A^T^B^@^@^@^@^@\300^@^@^@^@^@^@F」と
「"L\0\0\0\ \x01\x14\x02\0\0\0\0\0\xC0\0\0\0\0\0\0\x46"」を
string=してるんだけども、何故か\300の部分が\xC0がマッチしないのが原因。
ただ、(string= (buffer-substring 13 14) "\xC0")はnilなのに、
scratchバッファで(string= "\300" "\xC0)とするとtになる。C-q 3 0 0 RETがÀになるのが怪しいんだけど。

多分8ビット目が立ってるからstring=が失敗するんだと思うんだけど、良く分からんのでそこだけ飛ばして比較するようにしたら動いた。

Index: w32-symlinks.el
===================================================================
--- w32-symlinks.el     (リビジョン 1097)
+++ w32-symlinks.el     (作業コピー)
@@ -271,8 +271,8 @@
      ;; Parse the File Header Table.
      ;; Check for Shell Link identifier (4 bytes)
      ;; followed by Shell Link GUID (16 bytes):
-     (string= (buffer-substring 1 21)  ; otherwise not a shortcut file
-             "L\0\0\0\ \x01\x14\x02\0\0\0\0\0\xC0\0\0\0\0\0\0\x46")
+     (string= (store-substring (buffer-substring 1 21) 12 "_")  ; otherwise not a shortcut file
+             "L\0\0\0\ \x01\x14\x02\0\0\0\0\0_\0\0\0\0\0\0\x46")
      ;; Get the main flags dword at offset 14h.
      (let ((flags (w32-symlinks-buffer-substring-as-int (+ (point) ?\x14) 4))
           target)
(注意)requireする前にw32-symlinks-handle-shortcutsをtにしないといけない。
(setq w32-symlinks-handle-shortcuts t)
(require 'w32-symlinks)
これはシンボリックリンクの実装をショートカットにしている場合の話です。 具体的にはCYGWIN=winsymlinks ln -sで生成したシンボリックリンクか、Windows側で生成したショートカットに対応するものです。

Google日本語入力で10進数→{2,8,16}進数変換がすごくイイ!

日本語入力に関しても良かったからずっと使ってて、"きょう"で変換すると"2010/12/18"とか"平成22年12月18日"になったりとかは知ってたけど、
"42"を変換すると"0x2a"、"052"、"0b101010"が変換候補に出ることを知った。
今まで関数電卓とかワンライナーとか使ってたけど、これでいらなくなったよ!ありがとう!




でもね、これ逆変換ができないの。10進数→{2,8,16}進数だけ。
"0x2a"や"0b101010"では変換候補に出てこないし、
"052"に至っては"52"だと思われて"0x34"、"064"、"0b110100"が出ちゃう。まあ別にいいけど。

mv filename.{old,new} とは?

Linuxで使えるクレージーな小技コマンドいろいろ | IDEA*IDEA


mv filename.{old,new}
mvの機能だと勘違いする人がいるかも知れないので補足。
{}にカンマ区切りの文字列を入れとくと、シェルが{}前後の文字列をくっつけながら展開してくれるのです。ファイル名とか関係なく。

$ echo {a,b,c}
a b c

$ echo ^{a,b,c}$
^a$ ^b$ ^c$

スペースを入れると展開されないので

$ echo ^{a, b ,c}$
^{a, b ,c}$

その場合はエスケープすると良い。

$ echo ^{a,\ b\ ,c}$
^a$ ^ b $ ^c$

mv以外にも色々応用できますね!



例えば、

$ echo /usr/{local/,}bin/

とすると、

$ echo /usr/{local/,}bin/
/usr/local/bin/ /usr/bin/

と展開されるので、

$ echo /usr/{local/,}bin/perl*

のようにすれば、/usr/binと/usr/local/binに入ってる"perl"で始まるファイルの一覧を取得できます。

$ echo /usr/{local/,}bin/perl*
/usr/local/bin/perl /usr/local/bin/perl5.12.2 /usr/local/bin/perlbug /usr/local/bin/perldoc /usr/local/bin/perlivp /usr/local/bin/perlthanks /usr/bin/perl /usr/bin/perl5.8.8 /usr/bin/perlbug /usr/bin/perlcc /usr/bin/perlcritic /usr/bin/perldoc /usr/bin/perlivp /usr/bin/perltidy

便利だね!

Re: ディレクトリ配下のjarをまとめてCLASSPATHに入れるシェル : mwSoft blog

ものっっっっっすごく久しぶりにJava書いてるid:holidays-lです。
"public static void main"の引数"String[] args"を忘れて頭の中が?になるぐらい久しぶりです。
更に言うと、CLASSPATHだっけ?JAVA_CLASSPATHだっけ?CLASS_PATH???みたいなことになってて検索したり(笑)
ほんで、複数のjarをまとめて追加しようと CLASSPATH='./lib/*.jar:.'とかしてみたりとかw(←javaがよしなにやってくれるかなーってちょっと期待した)


まぁその道中でたまったま見かけたブログに言及するわけですが、
ディレクトリ配下のjarをまとめてCLASSPATHに入れるシェル : mwSoft blog
これって

$ export CLASSPATH=$(find ./lib -name '*.jar' -printf '%p:')

でいいよね。ぷりんとえふ可愛いよプリンとエフ。