Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
夢の無限スパゲッティ製造機 #phperkaigi
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
hideki kinjyo
PRO
March 22, 2026
Technology
0
3
夢の無限スパゲッティ製造機 #phperkaigi
https://fortee.jp/phperkaigi-2026/proposal/40eed9f2-69a1-4b38-8301-bcf21e902eb7
の発表資料です
hideki kinjyo
PRO
March 22, 2026
Tweet
Share
More Decks by hideki kinjyo
See All by hideki kinjyo
PHPer Book Revue 「雑に作る」 #phperkaigi
o0h
PRO
0
170
俺にも私がAIと作った オススメの個人ツールを語らせてくれ
o0h
PRO
0
28
#phperbiglt のLT
o0h
PRO
0
73
手軽に積ん読を増やすには?/読みたい本と付き合うには?
o0h
PRO
1
240
symfony/mcp-bundleで、既存アプリケーションもお手軽にMCPサーバー化
o0h
PRO
1
120
組織もソフトウェアも難しく考えない、もっとシンプルな考え方で設計する #phpconfuk
o0h
PRO
10
5.6k
Composerが「依存解決」のためにどんな工夫をしているか #phpcon
o0h
PRO
1
690
Composerの依存解決 #phpstudy
o0h
PRO
0
180
「影響が少ない」を自分の目でみてみる
o0h
PRO
4
2.4k
Other Decks in Technology
See All in Technology
アーキテクチャモダナイゼーションを実現する組織
satohjohn
1
1.1k
visionOS 開発向けの MCP / Skills をつくり続けることで XR の探究と学習を最大化
karad
1
590
NewSQL_ ストレージ分離と分散合意を用いたスケーラブルアーキテクチャ
hacomono
PRO
4
390
AWS CDK「読めるけど書けない」を脱却するファーストステップ
smt7174
3
170
Oracle Cloud Infrastructure IaaS 新機能アップデート 2025/12 - 2026/2
oracle4engineer
PRO
0
170
猫でもわかるKiro CLI(AI 駆動開発への道編)
kentapapa
0
260
Go標準パッケージのI/O処理をながめる
matumoto
0
220
Claude Code Skills 勉強会 (DevelersIO向けに調整済み) / claude code skills for devio
masahirokawahara
1
22k
AWS DevOps Agent vs SRE俺 / AWS DevOps Agent vs me, the SRE
sms_tech
3
900
品質を経営にどう語るか #jassttokyo / Communicating the Strategic Value of Quality to Executive Leadership
kyonmm
PRO
2
490
スケールアップ企業でQA組織が機能し続けるための組織設計と仕組み〜ボトムアップとトップダウンを両輪としたアプローチ〜
tarappo
1
150
2026年もソフトウェアサプライチェーンのリスクに立ち向かうために / Product Security Square #3
flatt_security
1
650
Featured
See All Featured
30 Presentation Tips
portentint
PRO
1
250
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
980
Documentation Writing (for coders)
carmenintech
77
5.3k
How to Talk to Developers About Accessibility
jct
2
150
AI in Enterprises - Java and Open Source to the Rescue
ivargrimstad
0
1.2k
A designer walks into a library…
pauljervisheath
210
24k
The agentic SEO stack - context over prompts
schlessera
0
700
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
250
Rails Girls Zürich Keynote
gr2m
96
14k
Facilitating Awesome Meetings
lara
57
6.8k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
49
9.9k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
Transcript
夢の無限スパゲッティ製造機 PHPerKaigi 2026 Hideki Kinjyo GitHub: o0h / X: @o0h_
[発表用] v1.0.0
夢の無限スパゲッティ製造機 #phperkaigi.2026 Hideki Kinjyo GitHub:o0h / X:@o0h_
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a こんにちは〜〜! 3
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 身近な相手が不意に見せた 普段と違う一面 って、どうですか? 4
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a PHPで言うとgoto どうですか? 5
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 6 昨年の発表
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 7 いち場面
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 8 いち場面
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 9 いち場面
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a それから1年 (One year later)
10
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 11 私 スパゲティをやっている人、 増えたのかな?
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 12 私 どきどき わくわく
質問コーナー 最近、「GOTO使ったよ」って人 どのくらい居ますか?🙋 13 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 14 私 使えないなら 使えるようにしないと!!
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 15 私 夢の無限スパゲッティ製造機だ!
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 16 私 夢の無限スパゲッティ製造機だ!
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a DEMO 17
自己紹介 • 金城秀樹 / きんじょうひでき • GitHub: @o0h / 𝕏
: @o0h_ • 好きなスパゲッティはカルボナーラ • アイコンは美味しい鮭親子丼のChef ver.です • 最近はPodcastをやっています • ハッシュタグ: #readlinefm 18 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
おしながき 1. スパゲッティの世界と製造機の挑戦 2. 実演・基本的な処理 3. 実演・入り組んだ処理 4. 限界、そして言語の壁 5.
最後に言いたいこと 19 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
1. スパゲッティの世界と製造機の挑戦 2. 実演・基本的な処理 3. 実演・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 「スパゲッティ」序説
スパゲッティ? 22 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a https://japan.zdnet.com/glossary/exp/スパゲッティコード/
スパゲッティ? 23 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a https://japan.zdnet.com/glossary/exp/スパゲッティコード/
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 今日使われている形のgo to文はまったく原始的 すぎます。それはわけのわからないプログラムを 作ることへのあまりにも強い誘惑になります。
24 E.W.ダイクストラ『go to文有害説』 ─ bit 1975年05月号(通巻75号) 共立出版 P88
現代のプログラミングのパワー 25 スパゲッティつらい 節操ないgoto辛い 構造化プログラミング • 今より言語もハードも未成熟の時代、 「ジャンプ命令」でのロジック整理 • 複雑化・大規模化するにつれ、
読解や保守の「キャパオーバー」 • 「構造化」でロジックを整理しやすく • for文やif文、function そして現代へ─ スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
逆に言えば・・・ 26 スパゲッティつらい 節操ないgoto辛い 構造化プログラミング ここの山をとびこえろ!!!! そして現代へ─ スライド → https://20260322.nichiyou.be
ハッシュタグ →#phperkaigi #a
逆に言えば・・・ 27 スパゲッティつらい 節操ないgoto辛い 構造化プログラミング ここの山をとびこえろ!!!! そして現代へ─ 昨今すっかり味わいにくくなった スパゲッティへの回帰 スライド
→ https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a gotoの文法・使い方について まず始めに
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 見たことない・触ったことない人向けに PHPにおける「goto」の文法を おさらいします 29
基礎文法 • gotoによって、指定された 場所までジャンプができる • ジャンプ先は「ラベル」を 指定する 30 スライド →
https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a <?php goto C; A: echo "AʹདྷͨΑ\n"; exit(); B: echo "BʹདྷͨΑ\n"; goto A; C: echo "CʹདྷͨΑ\n"; goto B;
基礎文法 • gotoによって、指定された 場所までジャンプができる 31 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi
#a <?php goto C; A: echo "AʹདྷͨΑ\n"; exit(); B: echo "BʹདྷͨΑ\n"; goto A; C: echo "CʹདྷͨΑ\n"; goto B; ラベル = 「文字列+コロン」 A: B: C:
基礎文法 32 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a <?php goto
C; A: echo "AʹདྷͨΑ\n"; exit(); B: echo "BʹདྷͨΑ\n"; goto A; C: echo "CʹདྷͨΑ\n"; goto B; ジャンプ = 「goto+ラベル+セミコロン」 goto A; goto B;
基礎文法 33 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a <?php goto
C; A: echo "AʹདྷͨΑ\n"; exit(); B: echo "BʹདྷͨΑ\n"; goto A; C: echo "CʹདྷͨΑ\n"; goto B; 最初にCにジャンプして
基礎文法 34 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a <?php goto
C; A: echo "AʹདྷͨΑ\n"; exit(); B: echo "BʹདྷͨΑ\n"; goto A; C: echo "CʹདྷͨΑ\n"; goto B; C → Bに
基礎文法 35 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a <?php goto
C; A: echo "AʹདྷͨΑ\n"; exit(); B: echo "BʹདྷͨΑ\n"; goto A; C: echo "CʹདྷͨΑ\n"; goto B; B → A
基礎文法 <?php goto C; A: echo "AʹདྷͨΑ\n"; exit(); B: echo
"BʹདྷͨΑ\n"; goto A; C: echo "CʹདྷͨΑ\n"; goto B; Cに来たよ Bに来たよ Aに来たよ 36 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a <?php goto C; A: echo "AʹདྷͨΑ\n"; exit(); B: echo "BʹདྷͨΑ\n"; goto A; C: echo "CʹདྷͨΑ\n"; goto B;
<?php goto C; A: echo "AʹདྷͨΑ\n"; exit(); B: echo "BʹདྷͨΑ\n";
goto A; C: echo "CʹདྷͨΑ\n"; goto B; 基礎文法 <?php goto C; A: echo "AʹདྷͨΑ\n"; exit(); B: echo "BʹདྷͨΑ\n"; goto A; C: echo "CʹདྷͨΑ\n"; goto B; • ラベルは「ジャンプ先」に 指定ができるが、外からの 進入を防ぐものではない 37 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a <?php goto C; A: echo "AʹདྷͨΑ\n"; exit(); B: echo "BʹདྷͨΑ\n"; goto A; C: echo "CʹདྷͨΑ\n"; goto B; A → B → A → ・・・ と無限ループに陥るので、 Aの内側で離脱処理(exitなど)を 呼ぶ必要がある
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a これを使いこなして 良い感じにやっていくぞ! 38
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a スパゲッティの定義
スパゲッティの定義(1/2) このセッションでは、「縛りあり」のプログラミングをしていきます • ざっくり言うと、 「反復・分岐等の制御構文や関数定義の禁止」 「オブジェクト指向的な記述の禁止」 の2つ • その実現のために、ふんだんにgotoを使っていくことに 40
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
スパゲッティの定義(2/2) • 条件分岐が「完全に無し」だとロジックが書けないので、 「goto命令だけを含むif」は許容する 45 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi
#a if ($me !== 'taro') goto taro_igai;
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a ◯ ✕
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a いかにして夢を叶えるか? 〜無限スパゲッティ製造機のアプローチ〜
どんなプログラムも根っこは単純 • 高級言語で書かれたコードは、そのまま実行されるわけではない • 掘り下げていけば、低水準な「機械語」まで落とし込まれる • その世界では「オブジェクト指向」も「ループ文」も無いハズだ!! 48 スライド →
https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
どんなプログラムも根っこは単純 • 高級言語で書かれたコードは、そのまま実行されるわけではない • 掘り下げていけば、低水準な「機械語」まで落とし込まれる • その世界では「オブジェクト指向」も「ループ文」も無いハズだ!! 49 スライド →
https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 下に降りればスパゲッティが生えていそう
<?php $tmp0 = … sub: ————— goto hoge; end: アプローチ
1. PHPで書かれたソースコードをインプットとして 2. いったんオペコードに変換し 3. 再びPHPの表現に戻す! 50 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a ソースコード オペコード <?php function hoge($x) { ————— } 0 INIT_FCALL 'hoge' 1 SEND_VAL 1 2 SEND_VAL 10 3 DO_ICALL $0 4 ECHO $0 5 RETURN 1 オペコード スパゲッティ
工程1: ソースコード • プログラマーのための表現 • 色々な抽象化/構造化が利用可能 • (説明不要ですね!) 51 スライド
→ https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
工程2: オペコード • VM(仮想マシン)のための表現 • やや抽象化は残るものの、素朴で限定された命令で作られる • 制御フローは、人間よりもCPU(機械)によった形に • ifやforが無くなる!
• 代わりに、「ジャンプ命令」 に書き換えらえる 52 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
• 基本的に上から実行される • 1行 = 1ステップ <補足> オペコードってどんなもの? 53 スライド
→ https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
• 各行は、インデックス・命令・引数の3つのパーツで構成される • 引数の数は命令によって異なる <補足> オペコードってどんなもの? 54 命令(オペコード) 引数(オペランド) インデックス(番号)
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
• 「CV0にtrueを代入する」 <補足> オペコードってどんなもの? 55 boolのtrue ローカル変数 (CV0 = $iikanji)
ASSIGN: 引数1に引数2を代入する スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
• 「CV0をチェックして、値が `0` だったら、3行目にジャンプする」 <補足> オペコードってどんなもの? 56 JMPZ: 引数1がゼロだったら、 引数2の番号にジャンプする
ローカル変数 (CV0 = $iikanji) 番号3 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
工程3: スパゲッティ • オレ達のための表現 • 制御フローは、オペコードをベースにした形に 57 スライド → https://20260322.nichiyou.be
ハッシュタグ →#phperkaigi #a 👍
1. スパゲッティの世界と製造機の挑戦 2. 実演・基本的な処理 3. 実演・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと
done!
1. スパゲッティの世界と製造機の挑戦 2. 実演・基本的な処理 3. 実演・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a ここからは スパゲッティの アラカルト 60
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a while
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 最初に条件の判定に飛んで
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 条件式の処理を行い
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 条件のチェックと、 ジャンプの実行(→反復処理)
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 再び条件式の判定に ループの中身を実行して
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 満たさなかったら次の行へ
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a for
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
1. スパゲッティの世界と製造機の挑戦 2. 実演Ⅰ・基本的な処理 3. 実演Ⅱ・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと
done!
1. スパゲッティの世界と製造機の挑戦 2. 実演Ⅰ・基本的な処理 3. 実演Ⅱ・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a function
初めてのfunction 関数をスパゲッティにするために、次の挙動を再現する必要がある 1. 呼び出した場所へ戻れる 2. 1つの値を呼び出し元に返せる 3. 関数の中から関数を呼び出せる 4. ローカルな変数を扱える
77 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
初めてのfunction① 関数をスパゲッティにするために、次の挙動を再現する必要がある 1. 呼び出した場所へ戻れる 2. 1つの値を呼び出し元に返せる 3. 関数の中から関数を呼び出せる 4. ローカルな変数を扱える
78 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
帰りたい コレ、どうしますか? 79 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a today();
echo 'end!!'; function today() { echo date('Y-m-d'); }
帰りたい 80 goto today; echo 'end!!'; exit(); today: echo date('Y-m-d');
関数を ひとかたまりの手続きに 名前をつけて呼び出すもの と捉える 関数(名)に対応する ラベルを作ってジャンプ? スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
関数(名)に対応する ラベルを作ってジャンプ? 帰りたい 81 goto today; echo 'end!!'; exit(); today:
echo date('Y-m-d'); これだと 元の位置に戻ってこれない ここに処理が来ない ✕ そのまま次の行へ スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
帰りたい 82 goto today; return_1: echo 'endʂ'; exit(); today: echo
date('Y-m-d'); goto return_1; 関数呼び出し後に 復帰する位置を設定し、 関数処理の最後にジャンプ スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
帰りたい 83 today(); echo '---'; today(); echo 'endʂ'; 関数が 2回呼び出される時は?
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
帰りたい 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
さっきと同じく 復帰する位置を設定して ジャンプ? 帰りたい 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
PHPのgotoは綺麗なgoto① • 飛び先のラベルは静的にしか指定できない 86 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
PHPのgotoは綺麗なgoto② • 同じファイル上にしかジャンプできない • 同じコンテキストにしかジャンプできない • 関数の内外や、ループの内外を行き来できない 87 スライド →
https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
ちゃんと帰りたい 88 関数を呼び出す前に 「戻り先」を記録しておき $returnTo = 'return_1'; goto today; return_1:
echo '---'; $returnTo = 'return_2'; return_2: echo 'endʂ'; exit(); スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
ちゃんと帰りたい 89 関数の最後は 「振り分けロジック」へと 飛ぶようにして today: echo date('Y-m-d'); goto return_1;
goto return_dispatcher; スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
ちゃんと帰りたい 90 「戻り先」のメモに応じて 振り分けてジャンプ return_dispatcher: if ($returnTo === 'return_1') goto
return_1; if ($returnTo === 'return_2') goto return_2; 力技で 飛び先となるラベルを全列挙★ スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
初めてのfunction② 関数をスパゲッティにするために、次の挙動を再現する必要がある ✔ 呼び出した場所へ戻れる 2. 1つの値を呼び出し元に返せる 3. 関数の中から関数を呼び出せる 4. ローカルな変数を扱える
91 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
返して欲しい コレ、どうしますか? 92 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $date
= today(); echo $date; function today() { return date('Y-m-d'); } 値を受け取って 変数に代入
返して欲しい 返り値用の専用変数を作る。 呼び出し元・呼び出し先で 同じ変数を読み書きする 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; グローバル変数、 最高〜!
初めてのfunction③ 関数をスパゲッティにするために、次の挙動を再現する必要がある ✔ 呼び出した場所へ戻れる ✔ 1つの値を呼び出し元に返せる 3. 関数の中から関数を呼び出せる 4. ローカルな変数を扱える
94 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
呼びたい コレ、どうしますか? 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();
呼びたい 先程の例で導入した 戻り先の振り分けを使うと こんな感じに 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;
呼びたい 制御フローだけ抜き出すと こんな感じ 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;
呼びたい $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; 呼び出し先の関数内で 上書きされてしまう
呼びたい 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
呼びたい 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
呼びたい 101 $stackLevel = 0; $stackLevel++; $returnTo[$stackLevel] = 'return_1'; goto
countDown; 「現在の階層」を管理して スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
呼びたい 102 $stackLevel = 0; $stackLevel++; $returnTo[$stackLevel] = 'return_1'; goto
countDown; 関数へのジャンプ前 (=「次に進む」前)に インクリメントする スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
呼びたい 103 $stackLevel = 0; $stackLevel++; $returnTo[$stackLevel] = 'return_1'; goto
countDown; 戻り先を 対応する階層と紐づけて 複数レベルで管理する スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
呼びたい 104 $returnTo = [ 0 => 'return_1', 1 =>
'return_2', ]; 今いる階層に応じて、 return_dispatcherが 戻り先を区別できるように スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
呼びたい 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
呼びたい 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
呼びたい 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
初めてのfunction④ 関数をスパゲッティにするために、次の挙動を再現する必要がある ✔ 呼び出した場所へ戻れる ✔ 1つの値を呼び出し元に返せる ✔ 関数の中から関数を呼び出せる 4. ローカルな変数を扱える
108 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
混ざりたくない コレ、どうしますか? 109 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $v
= 14; echo foo($v); function foo($v): float { $rate = 1.05; return $v * $rate; }
混ざりたくない 110 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a $v =
14; echo foo($v); function foo($v): float { $rate = 1.05; return $v * $rate; } 関数の外の変数と 名前が被っているけど 区別しなくてはいけない
混ざりたくない 関数呼び出し = 新しい階層の作成。 ローカル変数の区別にも 「階層で分ける」が使える 111 スライド → https://20260322.nichiyou.be
ハッシュタグ →#phperkaigi #a $v = 14; echo foo($v); function foo($v): float { $rate = 1.05; return $v * $rate; }
混ざりたくない 呼び出し側: 引数の位置ごとに、 階層と値をマッピングした 辞書を作成する 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);
混ざりたくない 関数側: 引数の辞書から 「ローカル変数」に写して 処理を進める 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; }
混ざりたくない 関数側: 通常のローカル変数も 「何階層目で呼ばれたか」 を管理する 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; }
初めてのfunction 関数をスパゲッティにするために、次の挙動を再現する必要がある ✔ 呼び出した場所へ戻れる ✔ 1つの値を呼び出し元に返せる ✔ 関数の中から関数を呼び出せる ✔ ローカルな変数を扱える
115 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a コレで 関数の基本的な機能が できた!
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 次の話 116
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a ・・・に行きたいのですが、 時間は有限なので ココにいる皆さんで次の話を決めません? ※
残り時間を5分以上残せていた場合 117
goto ??? <§3後編: 実演Ⅱ・入り組んだ処理> 1. クラスの基本的な話(プロパティやメソッドの表現) 2. `$this` ってどうやってるの? <§4:
限界、そして言語の壁> 1. 組み込みクラス 2. オペコード上に表れてこないデータ 3. 対応が難しそうで見送っているもの <それ以外> 1. 継承やinstanceofの再現 118 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
1. スパゲッティの世界と製造機の挑戦 2. 実演Ⅰ・基本的な処理 3. 実演Ⅱ・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a class
初めてのclass クラスを、基本的に「データ」「手続き」の集合であるとみなしてみる 1. プロパティ = 連想配列のキーと値みたいなもの 2. メソッド = goto
+ return_dispatcherを使った手続きみたいなもの 121 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
まとめたい 例えばこんなの 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();
まとめたい 基本的な構造はこうなる 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();
$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;
$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;
$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;
初めてのclass クラスが基本的に「データ」「手続き」の集合であるとみなしても、 他にも考えないことがいくつもある 1. 同じクラスから別々のインスタンスを作成できる 2. インスタンスごとに`$this` を使える 127 スライド
→ https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
初めてのclass クラスが基本的に「データ」「手続き」の集合であるとみなしても、 他にも考えないことがいくつもある 1. 同じクラスから別々のインスタンスを作成できる 2. インスタンスごとに`$this` を使える 128 スライド
→ https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a このトークでは こちらだけ紹介!
$thisしたい もちろん、 これだと問題がある 129 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
Foo_get7x: $returnVal = $this->v * 7; goto return_dispatcher; public function get7X() { return $this->v * 7; } 😠
$thisしたい 考えてみると、 $this表現が使われるのは メソッドの中だけ メソッドの内側のことだけ 考えれば良い 130 スライド → https://20260322.nichiyou.be
ハッシュタグ →#phperkaigi #a Foo_get7x: $returnVal = $this->v * 7; goto return_dispatcher;
$thisしたい メソッドの内側でだけ・・? それなら、 ローカル変数と同じ考え方が 通用しそう 階層ごとに 今の$thisを記録しておく 131 スライド →
https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a Foo_get7x: $returnVal = $this->v * 7; goto return_dispatcher;
$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)する度に インクリメント
$thisしたい 呼び出し側: 生成したインスタンスごとに 固有のIDを用意し、 IDを使ってオブジェクトを参 照できるようにする 133 スライド → https://20260322.nichiyou.be
ハッシュタグ →#phperkaigi #a // ॳظԽ $objects = []; $objIdCounter = 0; $_this = []; $objIdCounter++; $objects[$objIdCounter] = [ 'class' => 'Foo', ]; $objects[$objIdCounter]['v'] = 1111; IDを使って インスタンス管理
$thisしたい 呼び出し側: メソッドへのジャンプ前に、 $thisという配列で、 インスタンスIDを 今から使う階層にマッピング 134 スライド → https://20260322.nichiyou.be
ハッシュタグ →#phperkaigi #a $stackLevel++; $returnTo[$stackLevel] = 'return_1'; $this[$stackLevel] = $objIdCounter; goto Foo_get7x; 引数と同じノリ。 「階層に閉じた値」となる
$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;
$thisしたい 136 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a Foo_get7x: $tmpObjectId
= $this[$stackLevel]; $tmpThis = $objects[$tmpObjectId]; $returnVal = $tmpThis['v'] * 7; goto return_dispatcher; 今のコンテキストでの オブジェクトのIDを取得
$thisしたい 137 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a Foo_get7x: $tmpObjectId
= $this[$stackLevel]; $tmpThis = $objects[$tmpObjectId]; $returnVal = $tmpThis['v'] * 7; goto return_dispatcher; そのIDに紐づいている データを取得
$thisしたい 138 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a Foo_get7x: $tmpObjectId
= $this[$stackLevel]; $tmpThis = $objects[$tmpObjectId]; $returnVal = $tmpThis['v'] * 7; goto return_dispatcher; あとはそのまま参照すればOK
1. スパゲッティの世界と製造機の挑戦 2. 実演Ⅰ・基本的な処理 3. 実演Ⅱ・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと
done!
1. スパゲッティの世界と製造機の挑戦 2. 実演Ⅰ・基本的な処理 3. 実演Ⅱ・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 組み込みクラス 141
組み込みクラス - 問題 • 夢の無限スパゲッティ製造機は、 ソースコードを読むことで「メソッドごとにラベルに」変換している • 標準/拡張で定義されているクラスの内容を、PHPで読むのは厳しい 142 スライド
→ https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
組み込みクラス - 対応 • 不思議なヘルパー関数を用意することで対処 • FFIのような、PHPの機能をPHPで呼び出すための仕組み 143 スライド →
https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
クラスの例 ネイティブの \DateTimeImmutable を 利用しているクラスを定義 144 スライド → https://20260322.nichiyou.be ハッシュタグ
→#phperkaigi #a class D extends \DateTimeImmutable { } $d = new D(); echo $d->format('c');
クラスの例 new や メソッドコールを ヘルパーを介して実行する 145 スライド → https://20260322.nichiyou.be ハッシュタグ
→#phperkaigi #a $d = __builtin_new('D', []); $x = __builtin_call( $d, 'format', ['c'] ); echo __builtin_unwrap($x);
組み込みクラス - 対応 • __builtin〜〜系メソッドの対象か否かを判別する必要がある • bootstrap処理で、get_declared_classes() からクラスを洗い出す • オペコードからの変換時に定義済みクラス
or NOTを区別 146 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a オペコード上に表れてこないデータ 147
定数配列 • 次のコードのオペコードを生成すると、 • 通常は、このように配列の中身が現れない • 夢の無限スパゲッティ製造機だと、こうなる $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
valueに 定数を使った配列
🍝
値がない配列を見つけたら
元のコードの行番号を取って
token_get_allで解析
"=", [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の結果 (説明用に整形しています)
"=", [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: 行番号
"=", [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行目」のトークンを探して
"=", [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 の開始位置を見つけたら
"=", [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], そこから「配列の中身」を抜き出す = "[" と "]" の間のトークン
"=", [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]
ちなみに: valueに変数を使った配列の場合 160 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 技術的制約を感じて 実装を見送っているもの 161
include/requireの(完全な)対応 • 夢の無限スパゲッティ製造機は、 include/requireをインラインに展開することで対応している • そのため、`include $file` のような形式は変換できない 162 スライド
→ https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
include/requireの(完全な)対応 〜それでも〜 • `include $file` のようなコードはComposerのautoloadでも出てくる • 夢の無限スパゲッティ製造機はComposerの夢を見るか? • →はい、見ます
• 別のアプローチで対応の実装済み • 気になる人はQ&AかAsk The Speakerで聞いてください 163 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
destructor • 通常の実行フローとは異なるタイミングで発火する • これを夢の無限スパゲッティ製造機で扱うことは可能か・・? いずれにせよ「技術的には可能です」のレベル • すなわち、ガベージコレクションに相当するものを作ればOK。多分。 164 スライド
→ https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
1. スパゲッティの世界と製造機の挑戦 2. 実演Ⅰ・基本的な処理 3. 実演Ⅱ・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと
done!
ちょっとオマケ
発表では触れていないけど、こんなコードも行けます 気になるものがあったら、Ask The Speakerや懇親会でもどうぞ! ※ 上手く答えられるかはモノによります • クラスの継承 • try-catch-finally
• 可変長引数・名前付き引数 • ジェネレータ(yield) ※一部非対応 • 無名関数・無名クラス 167 スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a
おまけ • なんやかんやで、 Slimを使ったアプリケーションの スパゲッティ化に成功しました • →のコードが変換元(32行) => 53,957行 •
github.com/o0h/the-spaghetti-dream-gallery を見てみてください • 僕は怖くて中身を見ていません • ただし変換前後のコードに対して 同じ内容のE2Eテストはパス済み(thx runn!) スライド → https://20260322.nichiyou.be 168 ハッシュタグ →#phperkaigi #a
1. スパゲッティの世界と製造機の挑戦 2. 実演Ⅰ・基本的な処理 3. 実演Ⅱ・入り組んだ処理 4. 限界、そして言語の壁 5. 最後に言いたいこと
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a return、やばい めっちゃ凄いし、超便利、 これは人類の叡智。 まだ使ったことない人、急いだ方が良いです!
170
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a ・・・というのは、一分一厘は冗談なのですが 171
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 「日常の範囲に染み付いた構造」から 抜け出すことで、 「当たり前」が「実はスゴい!!」になった 172
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 夢の無限スパゲッティ製造機が 教えてくれたこと 173
スライド → https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 我々が生きている、この日常こそが 実は夢だったのかも知れない 174
おしまい! お付き合いいただき ありがとうございました!!
Appendix-Ⅰ 夢の無限スパゲッティ製造機、その処理の全体像
全体フロー 177 コマンド起動/src受け取り オペコード変換 w/phpdbg include require 部分のインライン化 クラス階層・静的プロパティ・enumや定数の解決 スライド
→ https://20260322.nichiyou.be ハッシュタグ →#phperkaigi #a 🍝化 冗長なコードのクリーンアップ
Appendix-Ⅱ o0h/phpstan-spaghetti
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