PHPで<?="{K]X^\31\31EYG_Q\31"^"".M_PI;
今月上旬にPHP開発者の一人Andrea Fauldsさんが以下のツイートをしていました。
PHP is a very beginner-friendly language, hello world is as easy as <?="{K]X^\31\31EYG_Q\31"^"".M_PI;
— Andrea! (@notajf) 9 October 2016
このtweetに含まれる、
<?="{K]X^\31\31EYG_Q\31"^"".M_PI;
と記述されたファイルをPHPで実行すると、
Hello, world!
とプログラムの初歩で有名な一節が出力されます。3v4lでの実行結果
もちろんAndreaさんが言ってるvery beginner-friendly language
というのは冗談だと思いますが、
私も「あー^
(ハット)が2つあるのが紛らわしいけどまあちょっと難読な"Hello, world!"だろうね!」
...とは全く思えないので確認がてら雑な解説をします。
PHPにはあまり詳しくない人向けの解説
上のコードを置き換えると
<?php echo "{K]X^\31\31EYG_Q\31" ^ "".M_PI;
さらに置き換えると
<?php echo "{K]X^\31\31EYG_Q\31" ^ (string) M_PI;
となります。PHPにちなむところについてそれぞれ見ていくと、
<?=
は、<?php echo
へのショートカットです。 PHP5.4からは常に使えます。- M_PI は 円周率の定義済み定数です。
"".(floatの数値)
にて文字列型へのキャストをおこせます。キャストされたさいの(string)M_PIの値は3.1415926535898
です。^
は 排他的論理和のビット演算子 です。
&、| そして ^ 演算子の左右のオペランドが文字列の場合、その演算は、文字列を構成する文字の ASCII 値を 使って行います。その結果は文字列になります。 [PHP: ビット演算子 - Manual](http://php.net/manual/ja/language.operators.bitwise.php)
とのことです。↓の返信が元のtweetについてますとおり、
@notajf what the fuck does ^ do
— [ʞ] (@velartrill) 9 October 2016
PHPでのプログラムではまず ^
での文字列の排他的論理和は使われないかなと思います。
ここまで踏まえて、なぞの文字列・円周率の文字・そして"Hello, world!"の桁を揃えると
{K]X^ \31\31 EYG_Q\31 |
3.141 5 9 265358 98 |
Hello , world! |
となります。
ここまで来れば、PHPには詳しくない方は分かるのかと思われます。
— 大川ぶくぶ/bkub (@bkub_comic) 8 May 2016
文字列のビット演算ってどうなってるの人向けの説明
バイナリ処理や文字列演算になじみがないと、上の変換内容が理解しにくい部分があります。
PHPにて解釈のためのスクリプトに落とし込んでみます。 上述のPHP: ビット演算子 - Manual に沿いASCII値を利用とのことで、文字のASCII値を取得するのにord()関数を、またdecbin()関数を利用することで10進数を2進数に変換できます。
よって、個々の文字を二進数フォーマットで表記するには、
<?php "0b" . sprintf("%08d", decbin(ord($chr)));
と変換が行えます。*1
なぞの文字列・円周率の文字をそれぞれ二進数フォーマットにて変換した表が下記であり、
なぞの文字列 0b01111011 { 円周率の文字 0b00110011 3 変換結果 0b01001000 H ------------------------------------ なぞの文字列 0b01001011 K 円周率の文字 0b00101110 . 変換結果 0b01100101 e ------------------------------------ なぞの文字列 0b01011101 ] 円周率の文字 0b00110001 1 変換結果 0b01101100 l ------------------------------------ なぞの文字列 0b01011000 X 円周率の文字 0b00110100 4 変換結果 0b01101100 l ------------------------------------ なぞの文字列 0b01011110 ^ 円周率の文字 0b00110001 1 変換結果 0b01101111 o ------------------------------------ なぞの文字列 0b00011001 円周率の文字 0b00110101 5 変換結果 0b00101100 , ------------------------------------ なぞの文字列 0b00011001 円周率の文字 0b00111001 9 変換結果 0b00100000 ------------------------------------ なぞの文字列 0b01000101 E 円周率の文字 0b00110010 2 変換結果 0b01110111 w ------------------------------------ なぞの文字列 0b01011001 Y 円周率の文字 0b00110110 6 変換結果 0b01101111 o ------------------------------------ なぞの文字列 0b01000111 G 円周率の文字 0b00110101 5 変換結果 0b01110010 r ------------------------------------ なぞの文字列 0b01011111 _ 円周率の文字 0b00110011 3 変換結果 0b01101100 l ------------------------------------ なぞの文字列 0b01010001 Q 円周率の文字 0b00110101 5 変換結果 0b01100100 d ------------------------------------ なぞの文字列 0b00011001 円周率の文字 0b00111000 8 変換結果 0b00100001 ! ------------------------------------
文字列の排他的論理和の結果としてHello, world!
であることを確認できます。
上記確認出力用のスクリプトは↓です。
<?php $text = "{K]X^\31\31EYG_Q\31"; foreach (preg_split("//", $text, -1, PREG_SPLIT_NO_EMPTY) as $i => $hello) { $pichr = substr((string) M_PI, $i, 1); echo "なぞの文字列\t0b", chr2b($hello), "\t", $hello, PHP_EOL; echo "円周率の文字\t0b", chr2b($pichr), "\t", $pichr, PHP_EOL; $chr = xorchr($hello, $pichr); echo "変換結果\t0b", chr2b($chr), "\t", $chr, PHP_EOL; echo "------------------------------------", PHP_EOL; } function chr2b($chr) { return sprintf("%08d", decbin(ord($chr))); } function xorchr($a, $b) { $a = "0b" . chr2b($a); $b = "0b" . chr2b($b); $xor = eval("return $a ^ $b ;"); return chr($xor); }