PHP Advent Calendar 2013 に参加しています。昨日の @yando さんから引き継いで2日目。
以前 PHP を使った手品を人前でやったら、
会場から「えっ」「えっ?」「ええっ!?」
という反応があって楽しかったので書いてみます。
演じ方
まずはこちらをご覧ください。
これを実行したらどうなるでしょうか。
できれば、答え合わせをする前に
上記コードの右下にある view raw
からkeys.php
をダウンロードして実行してみてください。
普通に考えたらこうなると思います。
Array ( [key] => value_7 )
では実行してみますね。
% php keys.php Array ( [key] => value_0 [key] => value_1 [key] => value_2 [key] => value_3 [key] => value_4 [key] => value_5 [key] => value_6 [key] => value_7 )
こうなりました。
ここで「えっ!」とか「何これ気持ち悪い!」とか反応があったら成功です。
タネ明かし
上記コードの print_r
のところを var_dump
にすると
何かが見えてきます。
array(8) { 'key\0' => string(7) "value_0" 'key' => string(7) "value_1" 'key' => string(7) "value_2" 'key' => string(7) "value_3" 'key' => string(7) "value_4" 'key' => string(7) "value_5" 'key' => string(7) "value_6" 'key' => string(7) "value_7" }
一つ目のキーがちょっと違って見えてますね。
ソースコードを Vim で開くともっとわかりやすい。
制御文字ですね。
PHP は配列のキーに制御文字が使える。
今回のようにブラウザで表示したり
ターミナルでソースコードを cat
したりしても
制御文字は目に見えないので、
見た目では不可能に思えることができるというネタでした。
変なコードの書き方
いくつかのエディタでは、
制御文字をキーボードから入力することができます。
Vim だったら control + V を押してから制御文字を打つと
そのまま入力できますね。
Emacs だったらcontrol + Q ですか。
けど、どのコードがどのキーに割り当てられてるかいちいち覚えてないし、
そもそも 0x00
(Null) なんてキーボードから入力できるのかどうかもわからない。
ということで、上記のコードはこんなのを書いて生成しました。
いろんな文字を試してみる
せっかくなので ASCII 0x00
から 0x1F
までの制御文字を
全部キーに入れてから出力してみました。
これを
実行。
% php keys_sample.php Array ( [] => 00 [] => 01 [] => 02 [] => 03 [] => 04 [] => 05 [] => 06 [] => 07 ] => 08 [ ] => 09 [ ] => 0A [ ] => 0B [ ] => 0C ] => 0D [] => 0E [] => 0F [] => 10 [] => 11 [] => 12 [] => 13 [] => 14 [] => 15 [] => 16 [] => 17 [] => 18 [] => 19 [] => 1A [%
00
から 07
まではさっきと同じく単に見えないだけだけど、
その次からわけのわからないものが見えますね。
] => 08
08
は BS
(バックスペース) なので
1文字戻って最初の [
が消えてます。
[ ] => 09
HT
(タブ)。正しくは「水平タブ」ですか。
確かにタブが出てます。
[ ] => 0A
0A
は LF
(改行)。\n
にあたるやつ。
[ ] => 0B
0B
は、使ったことないけど VT
(垂直タブ)。
垂直にタブしてます。
[ ] => 0C
FF
(書式送り) なんだけど、
なんでこうなるのかよくわかりません。
] => 0D
これが CRLF
の CR
にあたるやつ。 「復帰」ですね。LF
との違いが味わい深い。
一番の謎がここ。
[] => 19 [] => 1A [%
1F
まで回したはずなのにここで止まってる。
1B
が ESC
(エスケープ) だから。
エスケープが出力された時点で
そこから先は回避されてます。
他の言語では
他のっていっても確認したのは Ruby だけですが。
キーに制御文字を入れることはできるんだけど、
何かちゃんとエスケープしてくれるせいで、できませんでした。
% ruby keys.rb {"key\u0000"=>"value_0", "key\u0001"=>"value_1", "key\u0002"=>"value_2", "key\u0003"=>"value_3", "key\u0004"=>"value_4", "key\u0005"=>"value_5", "key\u0006"=>"value_6", "key\a"=>"value_7"}
というわけで、この手品は PHP でやりましょう。
この技の使いどころ
使わない方がいいと思います。
明日は @yohgaki さんですね。おねがいします。