Slide 1

Slide 1 text

夢の無限スパゲッティ製造機 PHPerKaigi 2026 Hideki Kinjyo GitHub: o0h / X: @o0h_ [発表用] v1.0.0

Slide 2

Slide 2 text

夢の無限スパゲッティ製造機 #phperkaigi.2026 Hideki Kinjyo GitHub:o0h / X:@o0h_

Slide 3

Slide 3 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a こんにちは〜〜! 3

Slide 4

Slide 4 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 身近な相手が不意に見せた 普段と違う一面 って、どうですか? 4

Slide 5

Slide 5 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a PHPで言うとgoto どうですか? 5

Slide 6

Slide 6 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 6 昨年の発表

Slide 7

Slide 7 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 7 いち場面

Slide 8

Slide 8 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 8 いち場面

Slide 9

Slide 9 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 9 いち場面

Slide 10

Slide 10 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a それから1年 (One year later) 10

Slide 11

Slide 11 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 11 私 スパゲティをやっている人、 増えたのかな?

Slide 12

Slide 12 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 12 私 どきどき わくわく

Slide 13

Slide 13 text

質問コーナー 最近、「GOTO使ったよ」って人 どのくらい居ますか?🙋 13 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 14

Slide 14 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 14 私 使えないなら 使えるようにしないと!!

Slide 15

Slide 15 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 15 私 夢の無限スパゲッティ製造機だ!

Slide 16

Slide 16 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 16 私 夢の無限スパゲッティ製造機だ!

Slide 17

Slide 17 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a DEMO 17

Slide 18

Slide 18 text

自己紹介 • 金城秀樹 / きんじょうひでき • GitHub: @o0h / 𝕏 : @o0h_ • 好きなスパゲッティはカルボナーラ • アイコンは美味しい鮭親子丼のChef ver.です • 最近はPodcastをやっています • ハッシュタグ: #readlinefm 18 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 19

Slide 19 text

おしながき 1. スパゲッティの世界と製造機の挑戦 2. 実演・基本的な処理 3. 実演・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと 19 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 20

Slide 20 text

1. スパゲッティの世界と製造機の挑戦 2. 実演・基本的な処理 3. 実演・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと

Slide 21

Slide 21 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 「スパゲッティ」序説

Slide 22

Slide 22 text

スパゲッティ? 22 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a https://japan.zdnet.com/glossary/exp/スパゲッティコード/

Slide 23

Slide 23 text

スパゲッティ? 23 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a https://japan.zdnet.com/glossary/exp/スパゲッティコード/

Slide 24

Slide 24 text

現代のプログラミングのパワー 24 スパゲッティつらい 節操ないgoto辛い 構造化プログラミング • 今より言語もハードも未成熟の時代、 「ジャンプ命令」でのロジック整理 • 複雑化・大規模化するにつれ、 読解や保守の「キャパオーバー」 • 「構造化」でロジックを整理しやすく • for文やif文、function そして現代へ─ スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 25

Slide 25 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a GO TO文こそ,皿盛りのスパゲッティにおけるスパ ゲッティ, ネズミの巣におけるボロクズ, つまり 原材料であるわけだ。いっそ GOTO 文がなかった ら, さぞいいだろう, とつい考えたくなる 25 ─ bit 1975年05月号(通巻75号) 共立出版 P367

Slide 26

Slide 26 text

逆に言えば・・・ 26 スパゲッティつらい 節操ないgoto辛い 構造化プログラミング ここの山をとびこえろ!!!! そして現代へ─ スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 27

Slide 27 text

逆に言えば・・・ 27 スパゲッティつらい 節操ないgoto辛い 構造化プログラミング ここの山をとびこえろ!!!! そして現代へ─ 昨今すっかり味わいにくくなった スパゲッティへの回帰 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 28

Slide 28 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a gotoの文法・使い方について まず始めに

Slide 29

Slide 29 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 見たことない・触ったことない人向けに PHPにおける「goto」の文法を おさらいします 29

Slide 30

Slide 30 text

基礎文法 • gotoによって、指定された 場所までジャンプができる • ジャンプ先は「ラベル」を 指定する 30 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 31

Slide 31 text

基礎文法 • gotoによって、指定された 場所までジャンプができる 31 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 32

Slide 32 text

基礎文法 32 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 33

Slide 33 text

基礎文法 33 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 34

Slide 34 text

基礎文法 34 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 35

Slide 35 text

基礎文法 35 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 36

Slide 36 text

基礎文法

Slide 37

Slide 37 text

Slide 38

Slide 38 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a これを使いこなして 良い感じにやっていくぞ! 38

Slide 39

Slide 39 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a スパゲッティの定義

Slide 40

Slide 40 text

スパゲッティの定義(1/2) このセッションでは、「縛りあり」のプログラミングをしていきます • ざっくり言うと、 「反復・分岐等の制御構文や関数定義の禁止」 「オブジェクト指向的な記述の禁止」 の2つ • その実現のために、ふんだんにgotoを使っていくことに 40 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 41

Slide 41 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 42

Slide 42 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 43

Slide 43 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 44

Slide 44 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 45

Slide 45 text

スパゲッティの定義(2/2) • 条件分岐が「完全に無し」だとロジックが書けないので、 「goto命令だけを含むif」は許容する 45 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a if ($me !== 'taro') goto taro_igai;

Slide 46

Slide 46 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a ◯ ✕

Slide 47

Slide 47 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a いかにして夢を叶えるか? 〜無限スパゲッティ製造機のアプローチ〜

Slide 48

Slide 48 text

どんなプログラムも根っこは単純 • 高級言語で書かれたコードは、そのまま実行されるわけではない • 掘り下げていけば、低水準な「機械語」まで落とし込まれる • その世界では「オブジェクト指向」も「ループ文」も無いハズだ!! 48 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 49

Slide 49 text

どんなプログラムも根っこは単純 • 高級言語で書かれたコードは、そのまま実行されるわけではない • 掘り下げていけば、低水準な「機械語」まで落とし込まれる • その世界では「オブジェクト指向」も「ループ文」も無いハズだ!! 49 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 下に降りればスパゲッティが生えていそう

Slide 50

Slide 50 text

Slide 51

Slide 51 text

工程1: ソースコード • プログラマーのための表現 • 色々な抽象化/構造化が利用可能 • (説明不要ですね!) 51 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 52

Slide 52 text

工程2: オペコード • VM(仮想マシン)のための表現 • やや抽象化は残るものの、素朴で限定された命令で作られる • 制御フローは、人間よりもCPU(機械)によった形に • ifやforが無くなる! • 代わりに、「ジャンプ命令」 に書き換えらえる 52 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 53

Slide 53 text

• 基本的に上から実行される • 1行 = 1ステップ <補足> オペコードってどんなもの? 53 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 54

Slide 54 text

• 各行は、インデックス・命令・引数の3つのパーツで構成される • 引数の数は命令によって異なる <補足> オペコードってどんなもの? 54 命令(オペコード) 引数(オペランド) インデックス(番号) スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 55

Slide 55 text

• 「CV0にtrueを代入する」 <補足> オペコードってどんなもの? 55 boolのtrue ローカル変数 (CV0 = $iikanji) ASSIGN: 引数1に引数2を代入する スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 56

Slide 56 text

• 「CV0をチェックして、値が `0` だったら、3行目にジャンプする」 <補足> オペコードってどんなもの? 56 JMPZ: 引数1がゼロだったら、 引数2の番号にジャンプする ローカル変数 (CV0 = $iikanji) 番号3 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 57

Slide 57 text

工程3: スパゲッティ • オレ達のための表現 • 制御フローは、オペコードをベースにした形に 57 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 👍

Slide 58

Slide 58 text

1. スパゲッティの世界と製造機の挑戦 2. 実演・基本的な処理 3. 実演・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと done!

Slide 59

Slide 59 text

1. スパゲッティの世界と製造機の挑戦 2. 実演・基本的な処理 3. 実演・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと

Slide 60

Slide 60 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a ここからは スパゲッティの アラカルト 60

Slide 61

Slide 61 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a while

Slide 62

Slide 62 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 63

Slide 63 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 最初に条件の判定に飛んで

Slide 64

Slide 64 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 条件式の処理を行い

Slide 65

Slide 65 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 条件のチェックと、 ジャンプの実行(→反復処理)

Slide 66

Slide 66 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 再び条件式の判定に ループの中身を実行して

Slide 67

Slide 67 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 満たさなかったら次の行へ

Slide 68

Slide 68 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a for

Slide 69

Slide 69 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 70

Slide 70 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 71

Slide 71 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 72

Slide 72 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 73

Slide 73 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 74

Slide 74 text

1. スパゲッティの世界と製造機の挑戦 2. 実演Ⅰ・基本的な処理 3. 実演Ⅱ・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと done!

Slide 75

Slide 75 text

1. スパゲッティの世界と製造機の挑戦 2. 実演Ⅰ・基本的な処理 3. 実演Ⅱ・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと

Slide 76

Slide 76 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a function

Slide 77

Slide 77 text

初めてのfunction 関数をスパゲッティにするために、次の挙動を再現する必要がある 1. 呼び出した場所へ戻れる 2. 1つの値を呼び出し元に返せる 3. 関数の中から関数を呼び出せる 4. ローカルな変数を扱える 77 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 78

Slide 78 text

初めてのfunction① 関数をスパゲッティにするために、次の挙動を再現する必要がある 1. 呼び出した場所へ戻れる 2. 1つの値を呼び出し元に返せる 3. 関数の中から関数を呼び出せる 4. ローカルな変数を扱える 78 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 79

Slide 79 text

帰りたい コレ、どうしますか? 79 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a today(); echo 'end!!'; function today() { echo date('Y-m-d'); }

Slide 80

Slide 80 text

帰りたい 80 goto today; echo 'end!!'; exit(); today: echo date('Y-m-d'); 関数を ひとかたまりの手続きに 名前をつけて呼び出すもの と捉える 関数(名)に対応する ラベルを作ってジャンプ? スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 81

Slide 81 text

関数(名)に対応する ラベルを作ってジャンプ? 帰りたい 81 goto today; echo 'end!!'; exit(); today: echo date('Y-m-d'); これだと 元の位置に戻ってこれない ここに処理が来ない ✕ そのまま次の行へ スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 82

Slide 82 text

帰りたい 82 goto today; return_1: echo 'endʂ'; exit(); today: echo date('Y-m-d'); goto return_1; 関数呼び出し後に 復帰する位置を設定し、 関数処理の最後にジャンプ スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 83

Slide 83 text

帰りたい 83 today(); echo '---'; today(); echo 'endʂ'; 関数が 2回呼び出される時は? スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 84

Slide 84 text

帰りたい 84 goto today; return_1: echo '---'; goto today; return_2: echo 'endʂ'; today: echo date('Y-m-d'); goto return_1; 関数が 2回呼び出される時は? さっきと同じく 復帰する位置を設定して ジャンプ? スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 85

Slide 85 text

さっきと同じく 復帰する位置を設定して ジャンプ? 帰りたい 85 goto today; return_1: echo '---'; goto today; return_2: echo 'endʂ'; today: echo date('Y-m-d'); goto return_1; ここに処理が来ない ✕ 戻り先を決め打ちだと 対応できない!! スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 86

Slide 86 text

PHPのgotoは綺麗なgoto① • 飛び先のラベルは静的にしか指定できない 86 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 87

Slide 87 text

PHPのgotoは綺麗なgoto② • 同じファイル上にしかジャンプできない • 同じコンテキストにしかジャンプできない • 関数の内外や、ループの内外を行き来できない 87 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 88

Slide 88 text

ちゃんと帰りたい 88 関数を呼び出す前に 「戻り先」を記録しておき $returnTo = 'return_1'; goto today; return_1: echo '---'; $returnTo = 'return_2'; return_2: echo 'endʂ'; exit(); スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 89

Slide 89 text

ちゃんと帰りたい 89 関数の最後は 「振り分けロジック」へと 飛ぶようにして today: echo date('Y-m-d'); goto return_1; goto return_dispatcher; スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 90

Slide 90 text

ちゃんと帰りたい 90 「戻り先」のメモに応じて 振り分けてジャンプ return_dispatcher: if ($returnTo === 'return_1') goto return_1; if ($returnTo === 'return_2') goto return_2; 力技で 飛び先となるラベルを全列挙★ スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 91

Slide 91 text

初めてのfunction② 関数をスパゲッティにするために、次の挙動を再現する必要がある ✔ 呼び出した場所へ戻れる 2. 1つの値を呼び出し元に返せる 3. 関数の中から関数を呼び出せる 4. ローカルな変数を扱える 91 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 92

Slide 92 text

返して欲しい コレ、どうしますか? 92 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $date = today(); echo $date; function today() { return date('Y-m-d'); } 値を受け取って 変数に代入

Slide 93

Slide 93 text

返して欲しい 返り値用の専用変数を作る。 呼び出し元・呼び出し先で 同じ変数を読み書きする 93 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $returnTo = 'return_1'; goto today; return_1: $today = $returnVal; echo $today; exit(); today: $returnVal = date('Y-m-d'); goto return_dispatcher; グローバル変数、 最高〜!

Slide 94

Slide 94 text

初めてのfunction③ 関数をスパゲッティにするために、次の挙動を再現する必要がある ✔ 呼び出した場所へ戻れる ✔ 1つの値を呼び出し元に返せる 3. 関数の中から関数を呼び出せる 4. ローカルな変数を扱える 94 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 95

Slide 95 text

呼びたい コレ、どうしますか? 95 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a function countDown() { $days = calcDays(); return "͋ͱ{$days}೔!!"; } function calcDays() { $kaigi = strtotime('3/20'); return date('z', $kaigi) - date('z'); } echo countDown();

Slide 96

Slide 96 text

呼びたい 先程の例で導入した 戻り先の振り分けを使うと こんな感じに 96 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $returnTo = 'return_1'; goto countDown; return_1: echo $returnVal; exit(); countDown: $returnTo = 'return_2'; goto calcDays; return_2: $returnVal = "͋ͱ{$returnVal}೔!!"; goto return_dispatcher; calcDays: $kaigi = strtotime('3/20'); $returnVal = date('z', $kaigi) - date('z'); goto return_dispatcher;

Slide 97

Slide 97 text

呼びたい 制御フローだけ抜き出すと こんな感じ 97 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $returnTo = 'return_1'; goto countDown; return_1: exit(); countDown: $returnTo = 'return_2'; goto calcDays; return_2: goto return_dispatcher; calcDays: goto return_dispatcher;

Slide 98

Slide 98 text

呼びたい $returnToを使い回す。 「直近の1つ」しか 戻り先を保持できない 98 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $returnTo = 'return_1'; goto countDown; return_1: exit(); countDown: $returnTo = 'return_2'; goto calcDays; return_2: goto return_dispatcher; calcDays: goto return_dispatcher; 呼び出し先の関数内で 上書きされてしまう

Slide 99

Slide 99 text

呼びたい 99 $returnTo = 'return_1'; goto countDown; return_1: exit(); countDown: $returnTo = 'return_2'; goto calcDays; return_2: goto return_dispatcher; calcDays: goto return_dispatcher; 永久に return_2から 抜け出せない $returnToを使い回す。 「直近の1つ」しか 戻り先を保持できない ✕ スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 100

Slide 100 text

呼びたい 100 $returnTo = 'return_1'; goto countDown; return_1: exit(); countDown: $returnTo = 'return_2'; goto calcDays; return_2: goto return_dispatcher; calcDays: goto return_dispatcher; 複数の戻り先を管理したい 「階層」の概念の導入 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 101

Slide 101 text

呼びたい 101 $stackLevel = 0; $stackLevel++; $returnTo[$stackLevel] = 'return_1'; goto countDown; 「現在の階層」を管理して スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 102

Slide 102 text

呼びたい 102 $stackLevel = 0; $stackLevel++; $returnTo[$stackLevel] = 'return_1'; goto countDown; 関数へのジャンプ前 (=「次に進む」前)に インクリメントする スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 103

Slide 103 text

呼びたい 103 $stackLevel = 0; $stackLevel++; $returnTo[$stackLevel] = 'return_1'; goto countDown; 戻り先を 対応する階層と紐づけて 複数レベルで管理する スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 104

Slide 104 text

呼びたい 104 $returnTo = [ 0 => 'return_1', 1 => 'return_2', ]; 今いる階層に応じて、 return_dispatcherが 戻り先を区別できるように スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 105

Slide 105 text

呼びたい 105 return_dispatcher: $stackLevel--; if ($returnTo[$stackLevel] === 'return_1') goto return_1; if ($returnTo[$stackLevel] === 'return_2') goto return_2; return_dispatcherで、 「戻り先に戻す」直前で 階層レベルを引き下げておく スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 106

Slide 106 text

呼びたい 106 // $returnTo = 'return_1'; countDown: $returnTo = 'return_2'; goto calcDays; return_2: goto return_dispatcher; calcDays: goto return_dispatcher; 永久に return_2から 抜け出せない さっきまでの状況 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 107

Slide 107 text

呼びたい 107 // $returnTo[$stackLevel] // = 'return_1' countDown: $stackLevel++; $returnTo[$stackLevel] = 'return_2'; goto calcDays; return_2: goto return_dispatcher; calcDays: goto return_dispatcher; leve=0なので、 return_1に戻る 新しい状況 leve=1なので、 return_2に戻る スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 108

Slide 108 text

初めてのfunction④ 関数をスパゲッティにするために、次の挙動を再現する必要がある ✔ 呼び出した場所へ戻れる ✔ 1つの値を呼び出し元に返せる ✔ 関数の中から関数を呼び出せる 4. ローカルな変数を扱える 108 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 109

Slide 109 text

混ざりたくない コレ、どうしますか? 109 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $v = 14; echo foo($v); function foo($v): float { $rate = 1.05; return $v * $rate; }

Slide 110

Slide 110 text

混ざりたくない 110 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $v = 14; echo foo($v); function foo($v): float { $rate = 1.05; return $v * $rate; } 関数の外の変数と 名前が被っているけど 区別しなくてはいけない

Slide 111

Slide 111 text

混ざりたくない 関数呼び出し = 新しい階層の作成。 ローカル変数の区別にも 「階層で分ける」が使える 111 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $v = 14; echo foo($v); function foo($v): float { $rate = 1.05; return $v * $rate; }

Slide 112

Slide 112 text

混ざりたくない 呼び出し側: 引数の位置ごとに、 階層と値をマッピングした 辞書を作成する 112 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $v = 14; $stackLevel++; $arg0[$stackLevel] = $v; $returnTo[$stackLevel] = 'return_1'; goto foo; return_1: echo $returnVal; exit(); $v = 1; echo foo($v);

Slide 113

Slide 113 text

混ざりたくない 関数側: 引数の辞書から 「ローカル変数」に写して 処理を進める 113 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a foo: $paramFuncFoo_v = $arg0[$stackLevel]; // $rateΛνΣοΫͯ͠ॳظԽ; $rate[$stackLevel] = 1.05; $returnVal = $paramFuncFoo_v * $rate[$stackLevel]; goto return_dispatcher; function foo($v) { $rate = 1.05; return $v * $rate; }

Slide 114

Slide 114 text

混ざりたくない 関数側: 通常のローカル変数も 「何階層目で呼ばれたか」 を管理する 114 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a foo: $paramFuncFoo_v = $arg0[$stackLevel]; // $rateΛνΣοΫͯ͠ॳظԽ; $rate[$stackLevel] = 1.05; $returnVal = $paramFuncFoo_v * $rate[$stackLevel]; goto return_dispatcher; function foo($v) { $rate = 1.05; return $v * $rate; }

Slide 115

Slide 115 text

初めてのfunction 関数をスパゲッティにするために、次の挙動を再現する必要がある ✔ 呼び出した場所へ戻れる ✔ 1つの値を呼び出し元に返せる ✔ 関数の中から関数を呼び出せる ✔ ローカルな変数を扱える 115 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a コレで 関数の基本的な機能が できた!

Slide 116

Slide 116 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 次の話 116

Slide 117

Slide 117 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a ・・・に行きたいのですが、 時間は有限なので ココにいる皆さんで次の話を決めません? ※ 残り時間を5分以上残せていた場合 117

Slide 118

Slide 118 text

goto ??? <§3後編: 実演Ⅱ・入り組んだ処理> 1. クラスの基本的な話(プロパティやメソッドの表現) 2. `$this` ってどうやってるの? <§4: 限界、そして言語の壁> 1. 組み込みクラス 2. オペコード上に表れてこないデータ 3. 対応が難しそうで見送っているもの <それ以外> 1. 継承やinstanceofの再現 118 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 119

Slide 119 text

1. スパゲッティの世界と製造機の挑戦 2. 実演Ⅰ・基本的な処理 3. 実演Ⅱ・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと

Slide 120

Slide 120 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a class

Slide 121

Slide 121 text

初めてのclass クラスを、基本的に「データ」「手続き」の集合であるとみなしてみる 1. プロパティ = 連想配列のキーと値みたいなもの 2. メソッド = goto + return_dispatcherを使った手続きみたいなもの 121 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 122

Slide 122 text

まとめたい 例えばこんなの 122 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a class Foo { public $v; public function get7X() { return $this->v * 7; } } $foo = new Foo(); $foo->v = 1111; echo $foo->get7X();

Slide 123

Slide 123 text

まとめたい 基本的な構造はこうなる 123 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $foo = ['class' => 'Foo']; $foo['v'] = 1111; $stackLevel++; $returnTo[$stackLevel] = 'return_1'; goto Foo_get7x; $foo = new Foo(); $foo->v = 1111; echo $foo->get7X();

Slide 124

Slide 124 text

$foo = new Foo(); $foo->v = 1111; echo $foo->get7X(); まとめたい インスタンス化 = クラス情報を与えた配列 124 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $foo = ['class' => 'Foo']; $foo['v'] = 1111; $stackLevel++; $returnTo[$stackLevel] = 'return_1'; goto Foo_get7x;

Slide 125

Slide 125 text

$foo = new Foo(); $foo->v = 1111; echo $foo->get7X(); まとめたい プロパティへの代入 = 連想配列へのセット 125 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $foo = ['class' => 'Foo']; $foo['v'] = 1111; $stackLevel++; $returnTo[$stackLevel] = 'return_1'; goto Foo_get7x;

Slide 126

Slide 126 text

$foo = new Foo(); $foo->v = 1111; echo $foo->get7X(); まとめたい メソッド呼び出し = gotoでのジャンプ 126 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $foo = ['class' => 'Foo']; $foo['v'] = 1111; $stackLevel++; $returnTo[$stackLevel] = 'return_1'; goto Foo_get7x;

Slide 127

Slide 127 text

初めてのclass クラスが基本的に「データ」「手続き」の集合であるとみなしても、 他にも考えないことがいくつもある 1. 同じクラスから別々のインスタンスを作成できる 2. インスタンスごとに`$this` を使える 127 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 128

Slide 128 text

初めてのclass クラスが基本的に「データ」「手続き」の集合であるとみなしても、 他にも考えないことがいくつもある 1. 同じクラスから別々のインスタンスを作成できる 2. インスタンスごとに`$this` を使える 128 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a このトークでは こちらだけ紹介!

Slide 129

Slide 129 text

$thisしたい もちろん、 これだと問題がある 129 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a Foo_get7x: $returnVal = $this->v * 7; goto return_dispatcher; public function get7X() { return $this->v * 7; } 😠

Slide 130

Slide 130 text

$thisしたい 考えてみると、 $this表現が使われるのは メソッドの中だけ メソッドの内側のことだけ 考えれば良い 130 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a Foo_get7x: $returnVal = $this->v * 7; goto return_dispatcher;

Slide 131

Slide 131 text

$thisしたい メソッドの内側でだけ・・? それなら、 ローカル変数と同じ考え方が 通用しそう 階層ごとに 今の$thisを記録しておく 131 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a Foo_get7x: $returnVal = $this->v * 7; goto return_dispatcher;

Slide 132

Slide 132 text

$thisしたい 呼び出し側: 生成したインスタンスごとに 固有のIDを用意し、 IDを使ってオブジェクトを参 照できるようにする 132 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a // ॳظԽ $objects = []; $objIdCounter = 0; $_this = []; $objIdCounter++; $objects[$objIdCounter] = [ 'class' => 'Foo', ]; $objects[$objIdCounter]['v'] = 1111; 生成(new Class)する度に インクリメント

Slide 133

Slide 133 text

$thisしたい 呼び出し側: 生成したインスタンスごとに 固有のIDを用意し、 IDを使ってオブジェクトを参 照できるようにする 133 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a // ॳظԽ $objects = []; $objIdCounter = 0; $_this = []; $objIdCounter++; $objects[$objIdCounter] = [ 'class' => 'Foo', ]; $objects[$objIdCounter]['v'] = 1111; IDを使って インスタンス管理

Slide 134

Slide 134 text

$thisしたい 呼び出し側: メソッドへのジャンプ前に、 $thisという配列で、 インスタンスIDを 今から使う階層にマッピング 134 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $stackLevel++; $returnTo[$stackLevel] = 'return_1'; $this[$stackLevel] = $objIdCounter; goto Foo_get7x; 引数と同じノリ。 「階層に閉じた値」となる

Slide 135

Slide 135 text

$thisしたい 関数側: 現在の階層の情報を用いて、 オブジェクト(ID)マップから 自インスタンスでのthisに当 たるデータを取得する 135 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a Foo_get7x: $tmpObjectId = $this[$stackLevel]; $tmpThis = $objects[$tmpObjectId]; $returnVal = $tmpThis['v'] * 7; goto return_dispatcher;

Slide 136

Slide 136 text

$thisしたい 136 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a Foo_get7x: $tmpObjectId = $this[$stackLevel]; $tmpThis = $objects[$tmpObjectId]; $returnVal = $tmpThis['v'] * 7; goto return_dispatcher; 今のコンテキストでの オブジェクトのIDを取得

Slide 137

Slide 137 text

$thisしたい 137 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a Foo_get7x: $tmpObjectId = $this[$stackLevel]; $tmpThis = $objects[$tmpObjectId]; $returnVal = $tmpThis['v'] * 7; goto return_dispatcher; そのIDに紐づいている データを取得

Slide 138

Slide 138 text

$thisしたい 138 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a Foo_get7x: $tmpObjectId = $this[$stackLevel]; $tmpThis = $objects[$tmpObjectId]; $returnVal = $tmpThis['v'] * 7; goto return_dispatcher; あとはそのまま参照すればOK

Slide 139

Slide 139 text

1. スパゲッティの世界と製造機の挑戦 2. 実演Ⅰ・基本的な処理 3. 実演Ⅱ・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと done!

Slide 140

Slide 140 text

1. スパゲッティの世界と製造機の挑戦 2. 実演Ⅰ・基本的な処理 3. 実演Ⅱ・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと

Slide 141

Slide 141 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 組み込みクラス 141

Slide 142

Slide 142 text

組み込みクラス - 問題 • 夢の無限スパゲッティ製造機は、 ソースコードを読むことで「メソッドごとにラベルに」変換している • 標準/拡張で定義されているクラスの内容を、PHPで読むのは厳しい 142 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 143

Slide 143 text

組み込みクラス - 対応 • 不思議なヘルパー関数を用意することで対処 • FFIのような、PHPの機能をPHPで呼び出すための仕組み 143 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 144

Slide 144 text

クラスの例 ネイティブの \DateTimeImmutable を 利用しているクラスを定義 144 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a class D extends \DateTimeImmutable { } $d = new D(); echo $d->format('c');

Slide 145

Slide 145 text

クラスの例 new や メソッドコールを ヘルパーを介して実行する 145 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $d = __builtin_new('D', []); $x = __builtin_call( $d, 'format', ['c'] ); echo __builtin_unwrap($x);

Slide 146

Slide 146 text

組み込みクラス - 対応 • __builtin〜〜系メソッドの対象か否かを判別する必要がある • bootstrap処理で、get_declared_classes() からクラスを洗い出す • オペコードからの変換時に定義済みクラス or NOTを区別 146 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 147

Slide 147 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a オペコード上に表れてこないデータ 147

Slide 148

Slide 148 text

定数配列 • 次のコードのオペコードを生成すると、 • 通常は、このように配列の中身が現れない • 夢の無限スパゲッティ製造機だと、こうなる $config1 = ['name' => 'test', 'value' => 42]; L0003 0000 ASSIGN CV0($config) array(...) 3: ASSIGN CV0($config1) "[\'name\' => \'test\',\'value\' => 42]" 148 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 149

Slide 149 text

valueに 定数を使った配列

Slide 150

Slide 150 text

🍝

Slide 151

Slide 151 text

値がない配列を見つけたら

Slide 152

Slide 152 text

元のコードの行番号を取って

Slide 153

Slide 153 text

token_get_allで解析

Slide 154

Slide 154 text

"=", [397, " ", 2], "[", [269, "'name'", 2], [397, " ", 2], [391, "=>", 2], [397, " ", 2], [269, "'test'", 2], ",", [397, " ", 2], [269, "'value'", 2], [397, " ", 2], [391, "=>", 2], [397, " ", 2], token_get_allの結果 (説明用に整形しています)

Slide 155

Slide 155 text

"=", [397, " ", 2], "[", [269, "'name'", 2], [397, " ", 2], [391, "=>", 2], [397, " ", 2], [269, "'test'", 2], ",", [397, " ", 2], [269, "'value'", 2], [397, " ", 2], [391, "=>", 2], [397, " ", 2], 0:トークンの識別ID 1: ソース 2: 行番号

Slide 156

Slide 156 text

"=", [397, " ", 2], "[", [269, "'name'", 2], [397, " ", 2], [391, "=>", 2], [397, " ", 2], [269, "'test'", 2], ",", [397, " ", 2], [269, "'value'", 2], [397, " ", 2], [391, "=>", 2], [397, " ", 2], array(…)があった 「2行目」のトークンを探して

Slide 157

Slide 157 text

"=", [397, " ", 2], "[", [269, "'name'", 2], [397, " ", 2], [391, "=>", 2], [397, " ", 2], [269, "'test'", 2], ",", [397, " ", 2], [269, "'value'", 2], [397, " ", 2], [391, "=>", 2], [397, " ", 2], array の開始位置を見つけたら

Slide 158

Slide 158 text

"=", [397, " ", 2], "[", [269, "'name'", 2], [397, " ", 2], [391, "=>", 2], [397, " ", 2], [269, "'test'", 2], ",", [397, " ", 2], [269, "'value'", 2], [397, " ", 2], [391, "=>", 2], [397, " ", 2], そこから「配列の中身」を抜き出す = "[" と "]" の間のトークン

Slide 159

Slide 159 text

"=", [397, " ", 2], "[", [269, "'name'", 2], [397, " ", 2], [391, "=>", 2], [397, " ", 2], [269, "'test'", 2], ",", [397, " ", 2], [269, "'value'", 2], [397, " ", 2], [391, "=>", 2], [397, " ", 2], ['name' => 'test', 'value' => 42]

Slide 160

Slide 160 text

ちなみに: valueに変数を使った配列の場合 160 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 161

Slide 161 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 技術的制約を感じて 実装を見送っているもの 161

Slide 162

Slide 162 text

include/requireの(完全な)対応 • 夢の無限スパゲッティ製造機は、 include/requireをインラインに展開することで対応している • そのため、`include $file` のような形式は変換できない 162 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 163

Slide 163 text

include/requireの(完全な)対応 〜それでも〜 • `include $file` のようなコードはComposerのautoloadでも出てくる • 夢の無限スパゲッティ製造機はComposerの夢を見るか? • →はい、見ます • 別のアプローチで対応の実装済み • 気になる人はQ&AかAsk The Speakerで聞いてください 163 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 164

Slide 164 text

destructor • 通常の実行フローとは異なるタイミングで発火する • これを夢の無限スパゲッティ製造機で扱うことは可能か・・? いずれにせよ「技術的には可能です」のレベル • すなわち、ガベージコレクションに相当するものを作ればOK。多分。 164 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 165

Slide 165 text

1. スパゲッティの世界と製造機の挑戦 2. 実演Ⅰ・基本的な処理 3. 実演Ⅱ・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと done!

Slide 166

Slide 166 text

ちょっとオマケ

Slide 167

Slide 167 text

発表では触れていないけど、こんなコードも行けます 気になるものがあったら、Ask The Speakerや懇親会でもどうぞ! ※ 上手く答えられるかはモノによります • クラスの継承 • try-catch-finally • 可変長引数・名前付き引数 • ジェネレータ(yield) ※一部非対応 • 無名関数・無名クラス 167 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a

Slide 168

Slide 168 text

おまけ • なんやかんやで、 Slimを使ったアプリケーションの スパゲッティ化に成功しました • →のコードが変換元(32行) => 53,957行 • github.com/o0h/the-spaghetti-dream-gallery を見てみてください • 僕は怖くて中身を見ていません • ただし変換前後のコードに対して 同じ内容のE2Eテストはパス済み(thx runn!) スライド → https://20260322.nichiyou.be 168 ハッシュタグ →#phperkaigi #a

Slide 169

Slide 169 text

1. スパゲッティの世界と製造機の挑戦 2. 実演Ⅰ・基本的な処理 3. 実演Ⅱ・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと

Slide 170

Slide 170 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a return、やばい めっちゃ凄いし、超便利、 これは人類の叡智。 まだ使ったことない人、急いだ方が良いです! 170

Slide 171

Slide 171 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a ・・・というのは、一分一厘は冗談なのですが 171

Slide 172

Slide 172 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 「日常の範囲に染み付いた構造」から 抜け出すことで、 「当たり前」が「実はスゴい!!」になった 172

Slide 173

Slide 173 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 夢の無限スパゲッティ製造機が 教えてくれたこと 173

Slide 174

Slide 174 text

スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 我々が生きている、この日常こそが 実は夢だったのかも知れない 174

Slide 175

Slide 175 text

おしまい! お付き合いいただき ありがとうございました!!

Slide 176

Slide 176 text

Appendix-Ⅰ 夢の無限スパゲッティ製造機、その処理の全体像

Slide 177

Slide 177 text

全体フロー 177 コマンド起動/src受け取り オペコード変換 w/phpdbg include require 部分のインライン化 クラス階層・静的プロパティ・enumや定数の解決 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 🍝化 冗長なコードのクリーンアップ

Slide 178

Slide 178 text

Appendix-Ⅱ o0h/phpstan-spaghetti

Slide 179

Slide 179 text

PHPStan用のルール • https://packagist.org/packages/o0h/phpstan-spaghetti • 静的解析で制御構文等を禁止などの実現 • the-spaghetti-dream.nichiyou.be で利用知れいるのはコレ • 背景などはブログにも書きました https://daisuki.nichiyoubi.land/entry/2026/02/01/122707 179 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a