From 0766039bd9e6b9f5e6334e84666f5be698d41fc3 Mon Sep 17 00:00:00 2001
From: nsfisis
#\
+ #\
i\
n\
c\
@@ -143,7 +143,7 @@ l\
u\
d\
e\
-<\
+<\
s\
t\
d\
@@ -151,14 +151,14 @@ i\
o\
.\
h\
->\
-/*
-*/
+>\
+/*
+*/
i\
n\
t\
-/*
-*/
+/*
+*/
m\
a\
i\
@@ -170,30 +170,30 @@ r(
i\
n\
t\
-/*
-*/
+/*
+*/
i=
-1;
-i<
-1\
-0\
-0;
+1;
+i<
+1\
+0\
+0;
i\
+\
+)
-if
+if
(i
%\
-15
+15
==
-0)
+0)
p\
r\
i\
n\
t\
f(
-"\
+"\
F\
i\
z\
@@ -204,11 +204,11 @@ z\
z\
%\
c\
-",
-10
+",
+10
);
-/* あとは同じように普通のプログラムを変形するだけなので省略 */
+/* あとは同じように普通のプログラムを変形するだけなので省略 */
バックスラッシュを使った行継続がトークンを区切らない、というのがポイントだ。
@@ -268,9 +268,9 @@ c\
また、2文字だと文字列がまともに書けないのも辛い。''だけで 2文字使うので、「1文字の文字列リテラル」というものを書くことができない。PHP では文字列リテラル中に生の改行が書けるので
$a
-='
-a'
+ $a
+='
+a'
;;
@@ -290,10 +290,10 @@ a'
まずは普通に書くとしよう。
- <?php
+ <?php
-for ($i = 1; $i < 100; $i++) {
- echo (($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n";
+for ($i = 1; $i < 100; $i++) {
+ echo (($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n";
}
@@ -307,13 +307,13 @@ for ($i = 1; $i < 100; $i++) {
forは、3文字もある長いキーワードである。こんなものは使えない。array_系の関数を使って、適当に置き換えるとしよう。
- <?php
+ <?php
-$s = range(1, 100);
-array_walk(
- $s,
- fn($i) =>
- printf((($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n"),
+$s = range(1, 100);
+array_walk(
+ $s,
+ fn($i) =>
+ printf((($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n"),
);
@@ -327,17 +327,17 @@ array_walk(
range、array_walk、printfは長すぎるのでどうにかせねばならない。ここで、PHP の可変関数を使う。可変関数とは、関数名が文字列として入った変数を経由して、関数を呼び出す機能である。
- <?php
+ <?php
-$r = 'range';
-$w = 'array_walk';
-$p = 'printf';
+$r = 'range';
+$w = 'array_walk';
+$p = 'printf';
-$s = $r(1, 100);
-$w(
- $s,
- fn($i) =>
- $p((($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n"),
+$s = $r(1, 100);
+$w(
+ $s,
+ fn($i) =>
+ $p((($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n"),
);
@@ -365,7 +365,7 @@ $w(
というルールがない場合、「未定義の定数が評価された場合、その定数の名前が値になる」という PHP 7.x までの仕様が利用できる。例えば、Fizzという文字列が欲しければ、次のようにする。
- $f
+ $f
=F
.i
.z
@@ -376,13 +376,13 @@ $w(
こうして簡単に文字列を作れる。なお、この仕様は 7.x 時点でも警告を受けるので、@演算子を使って抑制してやるとよい。
- $f
+ $f
=@
F.
@i
-.#
+.#
@z
-.#
+.#
@z
;;
@@ -401,66 +401,66 @@ F.
ずばり、文字列同士のビット演算を使う。PHP では、文字列同士でビット演算 (&、|、^) をした場合、文字列の各バイトごとに指定したビット演算がなされ、それを結合したものが演算結果となる。
- $a = "12345";
-$b = "world";
+ $a = "12345";
+$b = "world";
-// $a ^ $b は次のコードと同じ
-$result = '';
-for ($i = 0; $i < min(strlen($a), strlen($b)); $i++) {
-$result .= $a[$i] ^ $b[$i];
+// $a ^ $b は次のコードと同じ
+$result = '';
+for ($i = 0; $i < min(strlen($a), strlen($b)); $i++) {
+$result .= $a[$i] ^ $b[$i];
}
-echo $result;
-// => F]AXQ
+echo $result;
+// => F]AXQ
これを踏まえ、次のコードを見てみよう。
- $x = "x\nOm\n";
-$y = "\nk!\no";
-$r = $x ^ $y;
-echo "$r\n";
+ $x = "x\nOm\n";
+$y = "\nk!\no";
+$r = $x ^ $y;
+echo "$r\n";
実行すると、rangeが表示される。さて、PHP では文字列リテラル中に生の改行を直接書いてもよいのだった (「主な障害」の節を参照のこと)。書きかえてみよう。
- $x
-='x
+ $x
+='x
Om
-';
-$y
-='
+';
+$y
+='
k!
-o'
+o'
;
-$r = $x ^ $y;
-echo "$r\n";
+$r = $x ^ $y;
+echo "$r\n";
さらに#を使って適当に調整すると、次のようになる。
- $x
-=#
-'x
+ $x
+=#
+'x
Om
-';
-$y
-='
+';
+$y
+='
k!
-o'
-;#
-$r
-=#
-$x
-^#
-$y
-;#
+o'
+;#
+$r
+=#
+$x
+^#
+$y
+;#
-echo "$r\n";
+echo "$r\n";
1行あたり2文字で、rangeという文字列を生成することに成功した。他の必要な文字列にも、同様の処理をほどこす。
@@ -478,154 +478,154 @@ echo "$r\n";
完成したものがこちら。
- <?php
+ <?php
-$x
-=#
-'i
-S'
+$x
+=#
+'i
+S'
;;
-$y
-='
+$y
+='
b!
-';
-$c
-=#
-$x
-^#
-$y
-;#
-$x
-=#
-'x
+';
+$c
+=#
+$x
+^#
+$y
+;#
+$x
+=#
+'x
Om
-';
-$y
-='
+';
+$y
+='
k!
-o'
-;#
-$r
-=#
-$x
-^#
-$y
-;#
-$x
-=#
-'k
+o'
+;#
+$r
+=#
+$x
+^#
+$y
+;#
+$x
+=#
+'k
Sk
~}
Ma
-';
-$y
-='
+';
+$y
+='
x!
s!
k!
-';
-$w
-=#
-$x
-^#
-$y
-;#
-$x
-=#
-'z
+';
+$w
+=#
+$x
+^#
+$y
+;#
+$x
+=#
+'z
Hd
-G'
-;#
-$y
-='
+G'
+;#
+$y
+='
x!
~!
-';
-$p
-=#
-$x
-^#
-$y
-;#
-$x
-=#
-'L
+';
+$p
+=#
+$x
+^#
+$y
+;#
+$x
+=#
+'L
[p
-';
-$y
-='
+';
+$y
+='
c!
-';
-$f
-=#
-$x
-^#
-$y
-;#
-$x
-=#
-'H
+';
+$f
+=#
+$x
+^#
+$y
+;#
+$x
+=#
+'H
[p
-';
-$y
-='
+';
+$y
+='
_!
-';
-$b
-=#
-$x
-^#
-$y
-;#
-$b
-[1
+';
+$b
+=#
+$x
+^#
+$y
+;#
+$b
+[1
]=
-$c
-(#
-13
-*9
+$c
+(#
+13
+*9
);
-$s
-=#
-$r
-(1
+$s
+=#
+$r
+(1
,(
-10
+10
**
-2)
+2)
);
-$w
-(#
-$s
-,#
-fn
-(#
-$i
-)#
-=>
-$p
+$w
+(#
+$s
+,#
+fn
+(#
+$i
+)#
+=>
+$p
((
-(#
-$i
-%3
-?#
-''
-:#
-$f
+(#
+$i
+%3
+?#
+''
+:#
+$f
).
-(#
-$i
-%5
-?#
-''
-:#
-$b
+(#
+$i
+%5
+?#
+''
+:#
+$b
)?
-:#
-$i
-)#
-.'
-')
+:#
+$i
+)#
+.'
+')
);
@@ -646,17 +646,17 @@ $i
PHP では、バッククォートを使ってシェルを呼び出せる。これはshell_exec関数と等価である。さて、PHP ではバックスラッシュによる行継続が使えないと書いたが、シェルでは使える (当然だが、呼び出されるシェルに依存する。Bash なら大丈夫だろう。知らんけど)。
- <?php
+ <?php
-printf(`
+printf(`
e\
c\
h\
o\
\
-1\
-2\
-3\
+1\
+2\
+3\
`);
@@ -685,34 +685,34 @@ o\
もうこれ以上は不可能だと思っていたのだが、この記事の執筆中に解決する方法を思いついたので載せておく。
- <?php
+ <?php
-$c = 'chr';
+$c = 'chr';
${
-'_
-'}
-=#
-$c
-(#
-32
+'_
+'}
+=#
+$c
+(#
+32
).
-$c
-(#
-92
+$c
+(#
+92
);
-printf(`
+printf(`
e\
c\
h\
o\
${
-'_
-'}
-1\
-2\
-3\
+'_
+'}
+1\
+2\
+3\
`);
@@ -720,8 +720,8 @@ ${
${
-'_
-'}
+'_
+'}
は変数で、中にはスペースとエスケープが入っている (chr(32) . chr(92))。シェルに渡されている文字列は次のようになる。
@@ -749,8 +749,8 @@ o\
${
-'_
-'}
+'_
+'}
最新版で警告が出るというのも美しくないので、私としては本編の解法を推す。
--
cgit v1.2.3-70-g09d2