aboutsummaryrefslogtreecommitdiffhomepage
# これは何? 2025-12-20 に開催された [PHPer大忘LT会 2025](https://connpass.com/event/372021/) の発表資料・ソースコードです。 ``` $ python 0.py > 1.py $ python 1.py > 2.py $ python 2.py > 3.py ... $ python 107.py > 108.php $ php 108.php > 0-2.py $ diff 0.py 0-2.py ``` 変則 Quine であり、最初の `0.py` は `gen.py` によって生成されます (`make` で出力可能)。 # 基本動作 `0.py` は「巳」の字の形をしています。これは、2025 年が巳年であることにちなんだものです。Python を使っているのも同様の理由です。 ![0.py](assets/0.py.png) `0.py` を Python で実行すると、次のような出力が得られます。 ![0.py output](assets/1.py.png) これは `0.py` と同じく Python コードであり、その大部分が `0.py` と共通しています。 この出力を `1.py` として保存し、実行結果を `2.py` として保存するという動作を繰り返すと、108 回目に異なる動作となります (「108」という数字は、除夜の鐘から取ったものです)。 ``` $ python 0.py > 1.py $ python 1.py > 2.py $ python 2.py > 3.py ... $ python 107.py ``` ![107.py output](assets/107.py.png) この実行結果は、「午」の字の形をしています。来年 2026 年は午年ですね! また、Python のソースコードではなく PHP のソースコードとなっています。PHP のマスコットは馬......であればよかったのですが残念ながら象でした。 これを PHP インタプリタで実行すると、次のようになります。 ![108.php output](assets/108.php.png) これは、最初の `0.py` と完全に一致します。以降は同じ挙動を繰り返します。 # 解説 基本となるのは、mame 氏による「Quine リレー」の記事 (https://mametter.hatenablog.com/entry/20130715/p1) にて説明されているテクニックです。 > 普通の Quine は、こんな感じの構造です。 > > ``` > s = (自分自身のソースコード文字列を得る) > puts s > ``` > > 一方、例えば何かを出力する Perl プログラム、を出力する Ruby プログラムはこんな感じです。 > > ``` > puts "print(\"...\");" > ``` > > これと Quine を組み合わせれば、Ruby と Perl を行き来する multi-Quine のできあがり。 > > ``` > s = (自分自身のソースコード文字列を得る) > puts "print(\"#{ s }\");" > ``` 実装に用いた言語こそ異なりますが、基本となるのはこのアイデアです。 ここに、「巳」「午」の字形を出力したり、108 回のカウントをしたりする機構を加えると、今回のソースコードができます。 `0.py` は `gen.py` というプログラムによって生成しており、その `template1` 変数に記述されたソースコードが動作の基盤となるものです。 その大部分を占めている出力の形を整えるコード (`fmt`) を除くと、次のようになっています。 ```python from base64 import b64encode from zlib import compress, decompress j = b'107' # 108 回のカウント s = b"@@" # この変数にソースコードが埋め込まれる z = decompress(b64decode(s)).replace(b"j = b'%s'" % j, b"j = b'%03d'" % ((int(j)+1) % 108)) t = z.replace(b"@"+b"@", b64encode(compress(z))) u = b64encode(t) def fmt(u, f, php): # 整形 ... F1 = ["巳の字形データ"] F2 = ["午の字形データ"] v = fmt(u, F1, False) # Python で「巳」に整形 if j == b"107": # 108 回目。PHP で「午」に整形 print(fmt(b64encode(compress(v)), F2, True).decode("utf-8")) else: print(v.decode("utf-8")) ``` 言語間での文字列エスケープを簡単にするため、Base64 エンコードを用いています。これによってソースコード中のクォートやバックスラッシュが消えるので、ソースコードを内部に埋め込む際のエスケープについて考える必要がなくなります。 また、PHP コード側で整形処理を書く手間を減らすため、ソースコードの整形処理はすべて Python 側でおこなうようにしています。「午」の PHP コードは、内部に「巳」の形へ整形済みの Python コードを保持し、それを加工なしで出力するようになっています。