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 さんですね。おねがいします。