gitignoreまとめ
Gitで無視ファイルを細かく設定する際にはまったので、メモ
前提
まずは前提を抑えておかないと、はまる
既にトラックされたファイルはgitignoreが効かない
$ git init $ touch hoge.txt $ git add hoge.txt # トラックされた後に $ vim .gitignore # 無視設定しても hoge.txt $ git status # 効かない
既にトラックされたファイルを無視対象にしたければ、git rm --cached
$ git rm --cached hoge.txt # 上記のトラックされたファイルをインデックスから削除すれば(ワークツリーはそのまま) $ git status #gitignoreが効く # もしhoge.txtをcommit済みの場合 $ git commit -m 'delete hoge.txt' # コミットしてリポジトリからも消す
空のディレクトリは管理されない
$ git init $ mkdir aaa # 空のディレクトリを作成 $ git add . $ git status # git管理下にならない
Git管理下にしたければ空ファイルを置けばよい。名前は何でもよいが.gitkeepとするのが慣習
$ touch aaa/.gitkeep # 空のディレクトリに.gitkeepという名前の空ファイルを作成 $ git add . $ git status # git管理下になる
.gitignore vs .gitkeep
http://stackoverflow.com/questions/7229885/gitignore-vs-gitkeep
優先順位
gitignoreは以下4つの方法で設定できて、それぞれ優先順位がある
↑優先度「高」
(1) コマンドラインからの指定
・git ls-filesなどのコマンドがコマンドラインから無視パターンを指定できる。
<例 >
untrackedなファイルから*.txtにマッチするファイルを除いて表示
$ git ls-files -o --exclude-standard -x '*.txt'
(2) .gitignoreファイル
・プロジェクトのメンバー全員で共通して無視したいパターンに使う。
・通常、バージョンコントロール内に入れてプロジェクトメンバー間でシェアする。
・複数の.gitignoreファイルがある場合、階層が深い方が優先される
<例>
.gitignore
*.txt
100/.gitignore
!*.txt
結果
. ├── .gitignore ├── 100/ │ ├── .gitignore │ └── hoge.txt # 無視しない(100/.gitignoreが適用) ├── 200/ │ └── hoge.txt # 無視する └── hoge.txt # 無視する
(3) $GIT_DIR/info/excludeファイル
・自分だけの設定。自分がこのプロジェクトにおいてだけ無視したいパターンに使う。
・例えば、自分だけがこのプロジェクトで使っているデバッグ用の俺俺スクリプトなどに。
・$GIT_DIRは、特に設定していなければワークツリーのトップにある.gitディレクトリを指す
<例>
# このプロジェクトに特有な自分だけのスクリプトmy_script.rbをバージョン管理外に $ vim .git/info/exclude # git ls-files --others --exclude-from=.git/info/exclude # Lines that start with '#' are comments. # For a project mostly in C, the following would be a good set of # exclude patterns (uncomment them if you want to use them): # *.[oa] # *~ my_script.rb
(4) core.excludesfile(設定用変数)で指定されたファイル
・自分だけの設定。自分がどのようなプロジェクトであっても常に無視したいパターンに使う。
・例えば、vimの.swpやmacの.DS_Storeなど
<例>
# git configコマンドで設定(ファイル名は.gitignoreでなくてもよい) $ git config --global core.excludesfile ~/.gitignore # すると~/.gitconfigに以下のような記載が追加される [core] excludesfile = /Users/hidenorimaehara/.gitignore # 対象ファイルに無視パターンを設定 $ vim ~/.gitignore .DS_Store
↓優先度「低」
無視パターンの書き方
無視パターンの書き方は以下。適用される階層に注意
どこにも/なし
・shell glob pattern(※1)として扱う
・.gitignoreファイルにおいては、その.gitignoreファイルを基準とした相対パスでパターン解析
・つまり、同階層以下全てに適用される(上位階層には適用されない)。
・.gitignoreファイル以外(※2)では、ワークツリーのトップレベル(※3)を基準とした相対パスでパターン解析
<例>
100/.gitignore
*.txt
結果
. ├── 100/ │ ├── .gitignore │ ├── 110/ │ │ └── hoge.txt #無視する │ └── hoge.txt #無視する └── hoge.txt #無視しない(100/.gitignoreの上位階層のため)
※1 http://hgbook.red-bean.com/read/file-names-and-pattern-matching.html#id381291
※2 $GIT_DIR/info/excludeファイルなど
※3 git rev-parse --show-toplevelで確認できる
末尾に/
・ディレクトリだけに適用される(ファイルには適用されない)
<例>
100/.gitignore
[0-9][0-9][0-9]/
結果
├── 100/ │ ├── .gitignore │ ├── 110/ #無視する │ │ └── hoge.txt │ ├── 120 #無視しない(120はファイルであり、ディレクトリでないため) │ └── hoge.txt └── hoge.txt
先頭に/
・その.gitignoreファイルと同階層にだけ適用される
・「/」はその.gitignoreファイルを基点としたパスのことであり、ファイルシステムのルートではない
<例>
100/.gitignore
/hoge.txt
結果
├── 100/ │ ├── .gitignore │ ├── 110/ │ │ └── hoge.txt #無視しない(100/.gitignoreの直下ではないため) │ └── hoge.txt #無視する └── hoge.txt #無視しない
途中に/
・ワイルドカード(*など)が「/」にマッチしないshell glob patternとして解析する
・これは例を見た方がはやい
<例>
100/.gitignore
*/hoge.txt
結果
├── 100/ │ ├── .gitignore │ ├── 110/ │ │ ├── 111/ │ │ │ └── hoge.txt #無視しない(100/.gitignoreを基準とした*/hoge.txtに合致しないため) │ │ └── hoge.txt #無視する │ └── hoge.txt #無視しない(100/.gitignoreを基準とした*/hoge.txtに合致しないため) └── hoge.txt #無視しない
!
・逆の意味になる(無視しない)
・これを用いることで、ホワイトリスト方式が実現できる
<例>
.gitignore
# まず直下のファイルとディレクトリを全て無視する /* # ただし、以下は無視しない !/.gitignore !/100
結果
├── .gitignore #無視しない ├── 100/ #無視しない │ └── hoge.txt ├── 200/ #無視する │ └── hoge.txt ├── fuge.txt #無視する └── hoge.txt #無視する
終わり
Githubには各フレームワークや環境におけるデフォルトのgitignoreが集まっており便利
https://github.com/github/gitignore
PHPを愛する試み 〜self:: parent:: static:: および遅延静的束縛〜
PHPを愛する試みというものを個人的にやっている
今回は、self:: parent:: static:: 遅延静的束縛について図で整理してみた。
スコープ定義演算子 (::)
まず「::」について。これはスコープ定義演算子という。マニュアルには以下のようにある。
スコープ定義演算子 (::)
スコープ定義演算子 (またの名を Paamayim Nekudotayim)、 平たく言うと「ダブルコロン」は、トークンのひとつです。 static, 定数 およびオーバーライドされたクラスのプロパティやメソッドにアクセスすることができます。これらの要素をクラス定義の外から参照する際には、 クラスの名前を使用してください。PHP 5.3.0 以降では、変数を用いてクラスを参照することも可能です。 変数の値に (self や parent、 static といった) キーワードを指定することはできません。
...
http://www.php.net/manual/ja/language.oop5.paamayim-nekudotayim.php
分かりづらいので、自分流に解釈する。例えば、以下の図ように沢山のfoo()があるとして、どのfoo()なのかを決定するための演算子が::である。「::の右側にあるメソッドやプロパティが、::の左側のスコープに属すると決定する演算子」と考えればしっくりくる。
self::やparent::というのは、このスコープを表す方法の1つ。全部で以下の4つがある。
方法 | スコープ | 使える場所 |
---|---|---|
クラス名:: | 明示したクラス | クラス定義の外or中 |
self:: | self::が記載されたクラス | クラス定義の中 |
parent:: | parent::が記載されたクラスの親クラス | クラス定義の中 |
static:: | 直近の "非転送コール" のクラス | クラス定義の中 |
1つずつみていく
クラス名::
クラス名::とすると、スコープは「明示したクラス」となる。
クラス定義の外でも中でも使える。
<?php class C { public static function foo() { D::foo(); //foo()のスコープはD } } class D { public static function foo() { echo 'D_foo' . PHP_EOL; } } C::foo(); //foo()のスコープはC。結果:D_foo
self::
self::とすると、スコープは「self::が記載されたクラス」となる。
クラス定義の中でしか使えない。
<?php class C { public static function foo () { self::bar(); //bar()のスコープはself::が記載されたクラス(C) } public static function bar () { echo 'C_foo' . PHP_EOL; } } class CC extends C { public static function bar () { echo 'CC_foo' . PHP_EOL; } } C::foo(); //C_foo CC::foo(); //C_foo
parent::
parent::とすると、スコープは「parent::が記載されたクラスの親クラス」となる。
クラス定義の中でしか使えない。
<?php class C { public static function bar () { echo 'C_bar' . PHP_EOL; } } class CC extends C { public static function foo () { parent::bar(); //bar()のスコープはparent::が記載されたクラス(CC)の親クラス(C) } public static function bar () { echo 'CC_bar' . PHP_EOL; } } CC::foo(); //C_bar
static::
これが本題。static::とすると、スコープは「直近の "非転送コール" のクラス」となる。
クラス定義の中でしか使えない。
ややこしいのが「直近の "非転送コール" のクラス」というもの。マニュアルにはこう書いてある。
遅延静的束縛 (Late Static Bindings)
...
より正確に言うと、遅延静的束縛は直近の "非転送コール" のクラス名を保存します。 静的メソッドの場合、これは明示的に指定されたクラス (通常は :: 演算子の左側に書かれたもの) となります。静的メソッド以外の場合は、そのオブジェクトのクラスとなります。 "転送コール" とは、self:: や parent::、static:: による静的なコール、 あるいはクラス階層の中での forward_static_call() によるコールのことです。 get_called_class() 関数を使うとコール元のクラス名を文字列で取得できます。 static:: はこのクラスのスコープとなります。
...
http://php.net/manual/ja/language.oop5.late-static-bindings.php
整理すると、
非転送コール: | C::foo()や$c->foo()といったクラス名(もしくはオブジェクト)を明示した呼び出し |
---|---|
転送コール: | self:: parent:: static::もしくはクラス階層の中での forward_static_call() での呼び出し |
遅延静的束縛 | 非転送コール時に、明示されたクラス名(もしくはオブジェクトのクラス名)を保持する機能 |
となる。
これらをふまえて、static::を使ったコードを書いてみる
<?php class C { public static function foo() { static::bar(); // bar()のスコープは直近の "非転送コール" のクラス(C::foo()ならC。CC::foo()ならCC) } public static function bar() { echo 'C_bar' . PHP_EOL; } } class CC extends C { public static function bar() { echo 'CC_bar' . PHP_EOL; } } C::foo(); //C_bar CC::foo(); //CC_bar
C::foo()と呼び出した時とCC::foo()で呼び出した時の動きが変わった!
絵にすると以下のようになる
「直近の非転送コール」とは? 〜例1〜
「直近の非転送コール」が何を表すのか、もう少し詳しくみてみる。
例えば、以下のように途中で転送コール(self::など)をはさんだ場合はどうなるか
<?php class C { public static function foo() { self::hoge(); //self::は転送コール } public static function hoge() { static::bar(); // bar()のスコープは直近の "非転送コール" のクラス(この場合CC) } public static function bar() { echo 'C_bar' . PHP_EOL; } } class CC extends C { public static function bar() { echo 'CC_bar' . PHP_EOL; } } CC::foo(); //CC_bar
転送コールは無視して直近の非転送コールまで遡っていることがわかる
絵にするとこうなる
「直近の非転送コール」とは? 〜例2〜
非転送コールが複数存在する場合はどうか
<?php class C { public static function foo() { C::hoge(); //非転送コール } public static function hoge() { static::bar(); // bar()のスコープは直近の "非転送コール" のクラス(この場合、C) } public static function bar() { echo 'C_bar' . PHP_EOL; } } class CC extends C { public static function bar() { echo 'CC_bar' . PHP_EOL; } } CC::foo(); //C_bar
非転送コールが複数存在する場合は、直近の非転送コールに解決されている
絵にするとこうなる
いずれにおいてもstatic::からコールスタックを遡って、一番近い非転送コール(転送コールは除く)で明示されたクラスに解決されていることがわかる。
静的メソッドの呼び出しではない場合
これまで静的メソッドの呼び出しを例としてきたが、さきほど引用したマニュアルにあるように、「静的メソッド以外の場合は、そのオブジェクトのクラス」となる
<?php class C { public function foo() { static::bar(); } public static function bar() { echo 'C_bar' . PHP_EOL; } } class CC extends C { public static function bar() { echo 'CC_bar' . PHP_EOL; } } $cc = new CC(); $cc->foo(); //CC_bar
静的メソッドでなくても直近の非転送コールまで遡って行く仕組みは同じだ。
これを絵にするとこうなる
注意:「メソッド・プロパティ定義のstatic」「静的変数static」とは違う
static::について混乱するのは、staticが遅延静的束縛以外の意味でも使われるからだ。マニュアルには以下のように書いてある
static キーワード
このページでは、static キーワードを使って静的なメソッドやプロパティを定義する方法を説明します。 static は、 静的な変数の定義 や 静的遅延束縛 にも使えます。これらの場合の static の使い方は、 それぞれのページを参照ください。
http://php.net/manual/ja/language.oop5.static.php
つまり、staticはこれまで見てきたように遅延静的束縛以外に、以下2つの意味で使われる。
・静的なメソッド・プロパティを定義するためのstatic
<?php class C { public static $v = 'hoge'; public static function foo() { echo 'foo' . PHP_EOL; } } var_dump(C::$v); //hoge C::foo(); //foo
・ 静的変数のstatic
<?php class C { public function countup() { static $count; //静的変数 $count++; var_dump($count); } } $c = new C(); $c->countup(); //1 $c->countup(); //2 $c->countup(); //3
同じキーワードが色々な意味で使われるところがややこしい。。。
以上
長かったがstaticについては大分整理できた。
余談
self::は__CLASS__。static::はget_called_class()で参照できる
<?php class C { public static function foo() { var_dump(__CLASS__); //常にC self::bar(); //常にC::foo() var_dump(get_called_class()); //CかCC static::bar(); //C::foo()かCC::foo() } public static function bar() { echo 'C_bar' . PHP_EOL; } } class CC extends C { public static function bar() { echo 'CC_bar' . PHP_EOL; } } C::foo(); CC::foo();
new self() new parent() new static()
self, parent, staticキーワードはオブジェクト生成にも使える。
<?php class C { public static function foo() { var_dump(new self()); } public static function hoge() { var_dump(new static()); } } class CC extends C { public static function bar() { var_dump(new parent()); } } C::foo(); //object(C) CC::bar(); //object(C) C::hoge(); //object(C) CC::hoge(); //object(CC)
PHPを愛する試み 〜調教編〜
PHPを愛する試みというのを個人的にやっている。
最近仕事でPHPをガリガリ使わなければならない状況になってしまった。そのため可及的速やかにPHPを愛する必要がある。
どうやるか
ざっと調べたところ、
・どんなオブジェクトでもtapとpを使いたい。
・けどPHPにはRubyのように基底クラスを簡単に書き換えるような仕組みはなさそう
・かといって全てのクラスにメソッド実装するのはダルイ
・C言語で拡張とか僕には無理だお
という感じだった。しかし、さらに調べるとrubyのactivesupportっぽいことをPHPでやってる洋モノの変態さんを発見した。
ActiveSupport for PHP - Ruby style
なんかよさそう。ということで、ここから発想を得て以下のような仕組みを考えてみた。
・任意のオブジェクト$objをruby()に食わせると、->tap()と->p()が使えるようになる
・->tap()と->p()はメソッドチェインで->tap()->p()のように繋げることができる
・ruby()に食わせたオブジェクトは->php()で元のオブジェクトに戻せる
Rubyっぽいメソッドを叩き込むドSファイル
<?php class Ruby { public function __construct($origin) { $this->origin = $origin; } public function tap($callback) { $callback($this->origin); return $this; } public function p() { var_dump($this->origin); return $this; } public function php() { return $this->origin; } } function ruby($origin) { return new Ruby($origin); }
使ってみる
rubynize.php
<?php require 'ruby.php'; class C { public function __construct() { $this->name = 'hoge'; } } // オブジェクトをvar_dump $c = new C(); ruby($c)->p(); // オブジェクトのnameプロパティにfugeをセット $c = new C(); ruby($c)->tap(function ($x) {$x->name = 'fuge';}); var_dump($c); // オブジェクトのnameプロパティにfugeをセットし、オブジェクトをvar_dump $c = new C(); ruby($c)->tap(function ($x) {$x->name = 'fuge';})->p(); // 上記の返り値をRubynizeする前のものにする $c = new C(); $new_c = ruby($c)->tap(function ($x) {$x->name = 'fuge';})->p()->php(); var_dump($new_c);
チェイン(鎖)ー!
さらに使ってみる
rubynize2.php
<?php require 'ruby.php'; class C { public function setName($name) { $this->name = $name; return $this; } public function setSex($sex) { $this->sex = $sex; return $this; } } // 普通のメソッドチェイン $c = new C(); $c->setName('hoge')->setSex('man'); var_dump($c); // tapでnameをhoge->fugeに $c = new C(); ruby($c->setName('hoge')) ->tap(function ($x) {$x->name = 'fuge';}) ->php() ->setSex('man'); var_dump($c); // tapでnameをhoge->fugeに。sexをman->womanに。さらにチェーンでvar_dump $c = new C(); ruby( ruby( ruby($c->setName('hoge')) ->tap(function ($x) {$x->name = 'fuge';}) ->php() ->setSex('man') ) ->tap(function ($x) {$x->sex = 'woman';}) )->p();
チェインチェイン(鎖鎖)ー!
終わり
ruby()が必要だったり->php()しなきゃだったりで、もっとすっきり出来そう。
だけど自分の能力ではこの辺が限界だお...
もっとPHPのことを知らなきゃですな...
※ 個人的にPHPとたわむれる試みなので、実用性とか(ry
※シリーズもので頑張ってる
PHPを愛する試み
PHPを愛する試み 〜調教編〜
PHPを愛する試み 〜self:: parent:: static:: および遅延静的束縛〜
unshift, shift, pop, pushが混乱するので、絵で整理した
配列の追加・取り出しに関する4つのメソッド(unshift, shift, pop, push)がいつも混乱するので絵で整理した。
絵にすれば覚えられそう!いちお練習のため、自分がよく使うRuby, PHP, JavaScriptでコードも書いた。
・いずれの言語においても上記4つのメソッドは似たような名前で存在しており、用途も同じ。
・いずれの言語のいずれのメソッドも破壊的メソッド。
・言語、バージョンによってメソッドの返り値は異なる。
という具合だった。
Ruby
ruby 1.9.2
# unshift a = [1, 2, 3] b = a.unshift 0 p a #[0, 1, 2, 3] p b #[0, 1, 2, 3] # shift a = [1, 2, 3] b = a.shift p a #[2, 3] p b #1 # pop a = [1, 2, 3] b = a.pop p a #[1, 2] p b #3 # push a = [1, 2, 3] b = a.push 4 p a #[1, 2, 3, 4] p b #[1, 2, 3, 4] # << a = [1, 2, 3] b = a << 4 p a #[1, 2, 3, 4] p b #[1, 2, 3, 4]
PHP
PHP 5.3.10
<?php // unshift $a = array(1, 2, 3); $b = array_unshift($a, 0); var_dump($a); //array(0, 1, 2, 3) var_dump($b); //4(結果配列の要素数) // shift $a = array(1, 2, 3); $b = array_shift($a); var_dump($a); //array(2, 3) var_dump($b); //1 // pop $a = array(1, 2, 3); $b = array_pop($a); var_dump($a); // array(1, 2) var_dump($b); // 3 // push $a = array(1, 2, 3); $b = array_push($a, 4); var_dump($a); //array(1, 2, 3, 4) var_dump($b); //4 (結果配列の要素数) // [] = $a = array(1, 2, 3); $b = $a[] = 4; var_dump($a); //array(1, 2, 3, 4) var_dump($b); //4(追加した値)
JavaScript
node 0.6.12
var a = [1, 2, 3]; var b = a.unshift(0); console.log(a); //[0, 1, 2, 3] console.log(b); //4(結果配列の要素数) var a = [1, 2, 3]; var b = a.shift(); console.log(a); //[2, 3] console.log(b); //1 var a = [1, 2, 3]; var b = a.pop(); console.log(a); //[1, 2] console.log(b); //3 var a = [1, 2, 3]; var b = a.push(4); console.log(a); //[1, 2, 3, 4] console.log(b); //4(結果配列の要素数)
一瞬で「今日のディレクトリ」を作成・移動できるRuby/Bashスクリプト
ちょっとしたファイルを作成・保存しておきたい時、自分にとっては「今日の日付のディレクトリ」にまとめておくのが丁度よい。これまでは毎日手作業で「今日の日付のディレクトリ」を作成・移動していたのだけど、これを毎回やるのが面倒臭い。ということでスクリプトで自動化した。ちなみに環境はruby1.9.2。bash 3.2.48。
※追記
ブコメでご指摘いただいた通り、Ruby使わないでBashだけでできます...なぜわざわざRubyを使ったのか...そこにRubyがあったからです...
機能
・シェルで「. today」と入力すると「~/daily/今日の日付(yyyy_mm_dd)」ディレクトリを作成し移動する
実装にあたって
Rubyでディレクトリを作るのは簡単なのだが、現在のシェルを移動するのに一工夫必要だった。RubyでDir.chdirするだけでは、現在のシェルのディレクトリは移動しない(あくまでもrubyコマンドを実行したシェルの子プロセスとして動くから)。そこで「Rubyはディレクトリ作成後、移動コマンド(cd ...)を標準出力に吐き出す→吐き出されたコマンドを現在のシェルで実行する」という処理にした。
標準出力に吐き出された文字列をBashのコマンドとして実行するには「コマンド置換($()か``)」を使う
コマンド置換
コマンド置換 (command substitution) を用いると、 コマンド名をコマンドの出力で置き換えられます。...
http://linuxjm.sourceforge.jp/html/GNU_bash/man1/bash.1.html
コマンドを現在のシェルで実行するには「.かsource」を使う
. filename [arguments]
source filename [arguments]filename からコマンドを読み込み、現在のシェル環境の下で実行します。...
http://linuxjm.sourceforge.jp/html/GNU_bash/man1/bash.1.html
コードを書く
Ruby
~/script/today.rb
today = Time.now.strftime "%Y_%m_%d" today_dir = File.expand_path("~/daily/#{today}") Dir.mkdir(today_dir) unless File.exist? today_dir puts "cd #{today_dir}"
実行
※追記
ご指摘いただいたので、lsの表示結果を掲載しました
例えば、今日の日付が2013年1月20日で、この状態から
$ pwd /Users/hidenorimaehara $ ls ~/daily 2013_01_18 2013_01_19
シェルで以下のコマンドを打つと
$ . today
※「.」は「source」でもOK
今日の日付のディレクトリが作成され移動できる。
$ pwd /Users/hidenorimaehara/daily/2013_01_20 $ ls ../ 2013_01_18 2013_01_19 2013_01_20
となる
Macなら、こうすれば、ディレクトリ移動後にそのディレクトリをFinderを開けて便利。
$ . today
$ open .
終わり
ちょっとしたスクリプトだけど、自分にとっては結構便利だった!
PHPを愛する試み
僕はRubyが好きだ。プライベートではRubyばっかり使っている。でも、仕事ではPHPを使わなければならない。これまでPHPは書きにくいーと思い込んでいてあまり好きではなかったのだけど、仕事で使う以上PHPを好きになった方がきっと幸せになれる。何かを好きになるにはどうすればよいか。そう、相手のことを知る努力をすればいいんだ!ということで、PHPについて知る努力をしてみた。
これは...
PHP好きになれるかも!
普段Rubyで書いてるコードをPHPで書いてみる
折角なので自分が普段Rubyを使っていて便利だなーと感じていることをPHPではどう書くのか(厳密では無いにしても大体同じ内容はどう書くか)試してみた。Rubyは1.9.2、PHPは5.3.10。
多重代入っぽいもの
a, b = [1, 2] puts a puts b
<?php list($a, $b) = array(1, 2); echo "$a\n"; echo "$b\n";
ほう!
可変長引数
def foo(*args) p args end foo 1, 2 foo 1, 2, 3
<?php function foo() { var_dump(func_get_args()); } foo(1, 2); foo(1, 2, 3);
ほほう!
無名関数・クロージャ
a = [1, 2, 3] b = 10 r = a.map {|v| v * b} p r
<?php $a = array(1, 2, 3); $b = 10; $r = array_map(function($v) use ($b) {return $v * $b;}, $a); var_dump($r);
名前空間
module A class C def self.foo; puts 'A_foo'; end end end module B class C def self.foo; puts 'B_foo'; end end end A::C.foo B::C.foo
<?php namespace A{ class C { static function foo() {echo "A_foo\n";} } } namespace B{ class C { static function foo() {echo "B_foo\n";} } } namespace { \A\C::foo(); \B\C::foo(); }
キモい!けど、きっと慣れる!
呼び出し元を一覧表示
def foo1; foo2; end def foo2; foo3; end def foo3; puts caller; end foo1
<?php function foo1() {foo2();} function foo2() {foo3();} function foo3() {debug_print_backtrace();} foo1();
これできるんだ!
オブジェクトの情報をいろいろ表示
class C; def foo1; end end class CC < C; def foo2; end end class CCC < CC; def foo3; end end ccc = CCC.new puts ccc.class.ancestors puts ccc.methods
<?php class C { function foo1() {} } class CC extends C { function foo2() {} } class CCC extends CC { function foo3() {} } $ccc = new CCC(); var_dump(get_class($ccc)); var_dump(class_parents($ccc)); var_dump(get_class_methods($ccc)); //さらにReflectionObject::exportを使うと、メソッドの定義場所やインスタンス変数まで表示してくれる ReflectionObject::export($ccc);
ソースコード読むとき助かる!
メソッドチェイン
class C def foo1; self; end def foo2; self; end def foo3; puts 'done'; end end C.new.foo1.foo2.foo3
<?php class C { function foo1() {return $this;} function foo2() {return $this;} function foo3() {echo 'done';} } $c = new C(); $c->foo1()->foo2()->foo3();
流れてる...
メタプログラミング(動的な関数定義と呼び出し)
class C def method_missing(method_name, *args) puts method_name end end c = C.new [:foo1, :foo2].each {|m| c.__send__(m)}
<?php class C { function __call($method_name, $args) { echo "$method_name\n"; } } $c = new C(); foreach(array("foo1", "foo2") as $m) {$c->$m();}
こいつ・・・動くぞ!
さらにPHP5.4にすると
スゴイらしい...
http://php.net/manual/ja/migration54.new-features.php
配列のリテラルとして[]が使えたり、ビルトインサーバが使えたりするらしいですお...
PHP
好きになれるかも!今度はフレームワークとかも触ってみよう!
※シリーズもので頑張ってる
PHPを愛する試み
PHPを愛する試み 〜調教編〜
PHPを愛する試み 〜self:: parent:: static:: および遅延静的束縛〜
Rubyで内部DSLっぽいのやってみる
少し前に仕事中、一瞬の隙も見逃さずに情報収集できるRubyワンライナーとスクリプトというRubyスクリプトを書いたのだが、スクレイピング対象サイトをハッシュで持ってるあたりが使いづらい。良い機会なので内部DSLってのに挑戦してみた。
しかし、内部DSLについてざっと調べたものの、まだ租借しきれていない。とりあえず、Sinatraっぽい構文でスクレイピング対象サイトを指定できるようにした。
sites.rb(ユーザー側)
fetch 'yahoo' do url 'http://www.yahoo.co.jp' regexp /topics.+?>([^<]+?)</ end fetch 'hatena' do url 'http://b.hatena.ne.jp/hotentry' regexp /entry-link.+>(.+?)</ end fetch 'naver' do url 'http://matome.naver.jp/' regexp /matomename.+?-->(.+?)<!--/m end fetch '2ch' do url 'http://uni.2ch.net/newsplus/' regexp /<a.+?>\d+?:\s(.+?)</ end
news(定義側)
#!/usr/bin/env ruby require 'open-uri' require 'nkf' class Site attr_accessor :name, :url, :regexp def initialize(n) @name = n end def url(u) @url = u end def regexp(r) @regexp = r end def get open(@url) do |f| f.read.scan(@regexp) {|m| puts NKF.nkf('-w', m.join)} end end end @defined_sites = [] def fetch(name, &block) s = Site.new(name) s.instance_eval &block @defined_sites << s end load File.expand_path('../sites.rb', __FILE__) defined_site_names = @defined_sites.map(&:name) site_names = ARGV.empty? ? defined_site_names : ARGV sites = [] site_names.each do |site_name| raise 'not defined' unless defined_site_names.include? site_name @defined_sites.map {|s| sites << s if s.name === site_name} end sites.map(&:get)
実行
上記2ファイルを同じディレクトリに配置し、newsに実行権限を与え、パスを通せば前回と同様に動く。