Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Next.jsのClient-side RenderingでPHPを動かす
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
uutan1108
May 31, 2024
Programming
690
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Next.jsのClient-side RenderingでPHPを動かす
エンジニアコネクト【LT勉強会&交流会】vol.3
https://cnctor-japan.connpass.com/event/317526/
uutan1108
May 31, 2024
More Decks by uutan1108
See All by uutan1108
四畳半神話大系から学ぶ判断における経験の必要性
ohmori_yusuke
1
43
エンジニアとQAエンジニアを繋いでくれた振る舞い駆動開発(Behaviour-Driven Development)
ohmori_yusuke
0
280
Designing a Cross-Disciplinary Engineering Community Through Anime
ohmori_yusuke
3
120
コミュニティ・勉強会を作るのは目的じゃない
ohmori_yusuke
1
410
映画のエンドロール風の動画を自動生成するツールを作った
ohmori_yusuke
2
110
アウトプットは“成果物”じゃなく“副産物”だった話
ohmori_yusuke
1
160
MDN Web Docs に日本語翻訳でコントリビュート
ohmori_yusuke
0
930
仕様がそのままテストになる!Javaで始める振る舞い駆動開発
ohmori_yusuke
8
6.2k
勉強会から始まった僕のDevRel~新卒エンジニアがつないだ2年間の軌跡~
ohmori_yusuke
2
540
Other Decks in Programming
See All in Programming
Railsバージョンを4倍にした話
optim
0
350
作って学ぶSpring BootのAuto Configuration / JJUG Night Seminar 2026-10-19
rshindo
0
140
Workers Cache を知る
syumai
0
360
Verilogで学ぶCPU自作入門
uyuki234
8
4.1k
APNsからLive Activityを開始する話
yumnumm
0
170
最近”作らせてる” ローカルAIアプリの話
akit37
0
470
複雑なUIコンポーネントとの ⻑い付き合い⽅:波及するバグからテスト戦略を経て、安全な再設計へ
mixplace
0
410
Long Living Tokens, Die, Die, Die! (or: The One Ring is Banned in Modern)
sylph01
0
300
Atomic Design, Enforced: Scaling Mobile Design Systems (next.app devCon / droidCon Berlin 2026)
steliosf
PRO
0
170
コードレビューのボトルネックを"する側"と"される側"の両面から解消する
yub0n
2
1.6k
現場と育てるRailsモデリング 〜Spreadsheetから始めたモデル設計〜
siroemk
2
590
Railsのためのパスキー実践入門 〜マルチテナントSaaSでの意思決定〜
utakaha
2
540
Featured
See All Featured
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
2
2.9k
From π to Pie charts
rasagy
1
400
ラッコキーワード サービス紹介資料
rakko
1
5.3M
Why Our Code Smells
bkeepers
PRO
340
58k
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
360
Utilizing Notion as your number one productivity tool
mfonobong
4
620
VelocityConf: Rendering Performance Case Studies
addyosmani
331
25k
A better future with KSS
kneath
240
18k
How STYLIGHT went responsive
nonsquared
100
6.3k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
33
5.2k
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
550
StorybookのUI Testing Handbookを読んだ
zakiyama
31
7k
Transcript
Next.jsの Client-side RenderingでPHPを 動かす エンジニアコネクト【LT勉強会&交流会】vol.3 うーたん
自己紹介
自己紹介 • うーたん ◦ X:@uutan1108 • 株式会社ゆめみ ◦ 新卒2年目 ◦
サーバーサイドエンジニア • 趣味 ◦ アニメを観ること 3
今日はブラウザでPHPを実行する方法を 紹介します
None
サーバーにPOSTしない
ソースコード https://github.com/OHMORIYUSUKE/php-web 7
サーバー側で実行したい場合は、 プログラミング&実行 できるWEBアプリを手作り PyCon Kyushu 2024 KAGOSHIMA を見てください!
https://speakerdeck.com/ohmori_yusuke/hurokuraminku-and-shi-xing-tekiruweba huriwoshou-zuo-ri
見てください!
Next.jsのClient-side Renderingで PHPを動かしていく
Client-side Rendering とは
Client-side Rendering とは Reactによるクライアント・サイド・レンダリング(CSR) では、ブラウザは最小限のHTMLページと、そのページに必 要なJavaScriptをダウンロードする。 In Client-Side Rendering (CSR)
with React, the browser downloads a minimal HTML page and the JavaScript needed for the page. Client-side Rendering (CSR) https://nextjs.org/docs/pages/building-your-application/rendering/client-side-r endering
作る
これを作る! - テキストエリアにPHP のコードを入力 - [Run PHP]のボタンを 押下 - 実行結果が表示される
コード
src/app/page.tsx page.tsx について
"use client"; import React, { useState, useEffect, FormEvent } from
"react"; import { PhpWeb } from "php-wasm/PhpWeb.mjs"; const PHPWasmComponent: React.FC = () => { const [output, setOutput] = useState<string>(""); const [phpCode, setPhpCode] = useState<string>( `<?php\nprint("hello php");\n?>` ); const [phpInstance, setPhpInstance] = useState<PhpWeb | null>(null); useEffect(() => { const initializePhp = async () => { const php = new PhpWeb(); php.addEventListener("output", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("error", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("ready", () => { setPhpInstance(php); }); }; initializePhp(); }, []); const handleSubmit = async (event: FormEvent) => { event.preventDefault(); if (phpInstance) { setOutput(""); try { await phpInstance.run(phpCode); } catch (error) { setOutput((error as Error).message); } } }; return ( <div> <textarea value={phpCode} onChange={(e) => setPhpCode(e.target.value)} rows={20} cols={50} /> <button onClick={handleSubmit}>Run PHP</button> <pre>{output}</pre> </div> ); }; export default PHPWasmComponent; page.tsx
package.json package.json について
{ "name": "php-web", "version": "0.1.0", "private": true, "scripts": { "dev":
"next dev", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { "next": "14.2.3", "php-wasm": "^0.0.8", "react": "^18", "react-dom": "^18" }, "devDependencies": { "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", "eslint": "^8", "eslint-config-next": "14.2.3", "typescript": "^5" } } package.json "dependencies": { "next": "14.2.3", "php-wasm": "^0.0.8", "react": "^18", "react-dom": "^18" }, ライブラリ
これだけ
ライブラリについて 22
{ "name": "php-web", "version": "0.1.0", "private": true, "scripts": { "dev":
"next dev", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { "next": "14.2.3", "php-wasm": "^0.0.8", "react": "^18", "react-dom": "^18" }, "devDependencies": { "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", "eslint": "^8", "eslint-config-next": "14.2.3", "typescript": "^5" } } package.json "dependencies": { "next": "14.2.3", "php-wasm": "^0.0.8", "react": "^18", "react-dom": "^18" }, ライブラリ php-wasm とは
php-wasm とは PHPをWebAssembly(Wasm)上で動作させるための技術で す。これにより、ブラウザ上でPHPコードを実行することが 可能になります。 https://github.com/seanmorris/php-wasm
WebAssembly(Wasm) とは
WebAssembly(Wasm) とは WebAssembly は現代のウェブブラウザーで実行できる新し い種類のコードです。ネイティブに近いパフォーマンスで動 作する、コンパクトなバイナリー形式の低レベルなアセンブ リー風言語です。さらに、 C/C++、C# や Rust
などの言語 のコンパイル先となり、それらの言語をウェブ上で実行する ことができます。 WebAssembly は JavaScript と並行して 動作するように設計されているため、両方を連携させること ができます。 WebAssembly https://developer.mozilla.org/ja/docs/WebAssembly
php-wasm の使い方
"use client"; import React, { useState, useEffect, FormEvent } from
"react"; import { PhpWeb } from "php-wasm/PhpWeb.mjs"; const PHPWasmComponent: React.FC = () => { const [output, setOutput] = useState<string>(""); const [phpCode, setPhpCode] = useState<string>( `<?php\nprint("hello php");\n?>` ); const [phpInstance, setPhpInstance] = useState<PhpWeb | null>(null); useEffect(() => { const initializePhp = async () => { const php = new PhpWeb(); php.addEventListener("output", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("error", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("ready", () => { setPhpInstance(php); }); }; initializePhp(); }, []); const handleSubmit = async (event: FormEvent) => { event.preventDefault(); if (phpInstance) { setOutput(""); try { await phpInstance.run(phpCode); } catch (error) { setOutput((error as Error).message); } } }; return ( <div> <textarea value={phpCode} onChange={(e) => setPhpCode(e.target.value)} rows={20} cols={50} /> <button onClick={handleSubmit}>Run PHP</button> <pre>{output}</pre> </div> ); }; export default PHPWasmComponent; page.tsx
"use client"; import React, { useState, useEffect, FormEvent } from
"react"; import { PhpWeb } from "php-wasm/PhpWeb.mjs"; const PHPWasmComponent: React.FC = () => { const [output, setOutput] = useState<string>(""); const [phpCode, setPhpCode] = useState<string>( `<?php\nprint("hello php");\n?>` ); const [phpInstance, setPhpInstance] = useState<PhpWeb | null>(null);
"use client"; import React, { useState, useEffect, FormEvent } from
"react"; import { PhpWeb } from "php-wasm/PhpWeb.mjs"; const PHPWasmComponent: React.FC = () => { const [output, setOutput] = useState<string>(""); const [phpCode, setPhpCode] = useState<string>( `<?php\nprint("hello php");\n?>` ); const [phpInstance, setPhpInstance] = useState<PhpWeb | null>(null); インポート
"use client"; import React, { useState, useEffect, FormEvent } from
"react"; import { PhpWeb } from "php-wasm/PhpWeb.mjs"; const PHPWasmComponent: React.FC = () => { const [output, setOutput] = useState<string>(""); const [phpCode, setPhpCode] = useState<string>( `<?php\nprint("hello php");\n?>` ); const [phpInstance, setPhpInstance] = useState<PhpWeb | null>(null); 入力された PHP の実行結果を管理
"use client"; import React, { useState, useEffect, FormEvent } from
"react"; import { PhpWeb } from "php-wasm/PhpWeb.mjs"; const PHPWasmComponent: React.FC = () => { const [output, setOutput] = useState<string>(""); const [phpCode, setPhpCode] = useState<string>( `<?php\nprint("hello php");\n?>` ); const [phpInstance, setPhpInstance] = useState<PhpWeb | null>(null); 入力された PHP のコードを管理
"use client"; import React, { useState, useEffect, FormEvent } from
"react"; import { PhpWeb } from "php-wasm/PhpWeb.mjs"; const PHPWasmComponent: React.FC = () => { const [output, setOutput] = useState<string>(""); const [phpCode, setPhpCode] = useState<string>( `<?php\nprint("hello php");\n?>` ); const [phpInstance, setPhpInstance] = useState<PhpWeb | null>(null); PhpWeb インスタ ンスをステートで 管理
"use client"; import React, { useState, useEffect, FormEvent } from
"react"; import { PhpWeb } from "php-wasm/PhpWeb.mjs"; const PHPWasmComponent: React.FC = () => { const [output, setOutput] = useState<string>(""); const [phpCode, setPhpCode] = useState<string>( `<?php\nprint("hello php");\n?>` ); const [phpInstance, setPhpInstance] = useState<PhpWeb | null>(null); useEffect(() => { const initializePhp = async () => { const php = new PhpWeb(); php.addEventListener("output", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("error", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("ready", () => { setPhpInstance(php); }); }; initializePhp(); }, []); const handleSubmit = async (event: FormEvent) => { event.preventDefault(); if (phpInstance) { setOutput(""); try { await phpInstance.run(phpCode); } catch (error) { setOutput((error as Error).message); } } }; return ( <div> <textarea value={phpCode} onChange={(e) => setPhpCode(e.target.value)} rows={20} cols={50} /> <button onClick={handleSubmit}>Run PHP</button> <pre>{output}</pre> </div> ); }; export default PHPWasmComponent; page.tsx
useEffect(() => { const initializePhp = async () => {
const php = new PhpWeb(); php.addEventListener("output", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("error", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("ready", () => { setPhpInstance(php); }); }; initializePhp(); }, []);
useEffect(() => { const initializePhp = async () => {
const php = new PhpWeb(); php.addEventListener("output", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("error", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("ready", () => { setPhpInstance(php); }); }; initializePhp(); }, []); インスタンス化
useEffect(() => { const initializePhp = async () => {
const php = new PhpWeb(); php.addEventListener("output", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("error", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("ready", () => { setPhpInstance(php); }); }; initializePhp(); }, []); PhpWeb インスタンスに イベントリスナーを設定
useEffect(() => { const initializePhp = async () => {
const php = new PhpWeb(); php.addEventListener("output", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("error", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("ready", () => { setPhpInstance(php); }); }; initializePhp(); }, []); output, error, ready を 設定
"use client"; import React, { useState, useEffect, FormEvent } from
"react"; import { PhpWeb } from "php-wasm/PhpWeb.mjs"; const PHPWasmComponent: React.FC = () => { const [output, setOutput] = useState<string>(""); const [phpCode, setPhpCode] = useState<string>( `<?php\nprint("hello php");\n?>` ); const [phpInstance, setPhpInstance] = useState<PhpWeb | null>(null); useEffect(() => { const initializePhp = async () => { const php = new PhpWeb(); php.addEventListener("output", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("error", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("ready", () => { setPhpInstance(php); }); }; initializePhp(); }, []); const handleSubmit = async (event: FormEvent) => { event.preventDefault(); if (phpInstance) { setOutput(""); try { await phpInstance.run(phpCode); } catch (error) { setOutput((error as Error).message); } } }; return ( <div> <textarea value={phpCode} onChange={(e) => setPhpCode(e.target.value)} rows={20} cols={50} /> <button onClick={handleSubmit}>Run PHP</button> <pre>{output}</pre> </div> ); }; export default PHPWasmComponent; page.tsx
return ( <div> <textarea value={phpCode} onChange={(e) => setPhpCode(e.target.value)} rows={20} cols={50}
/> <button onClick={handleSubmit}>Run PHP</button> <pre>{output}</pre> </div> ); }; export default PHPWasmComponent;
return ( <div> <textarea value={phpCode} onChange={(e) => setPhpCode(e.target.value)} rows={20} cols={50}
/> <button onClick={handleSubmit}>Run PHP</button> <pre>{output}</pre> </div> ); }; export default PHPWasmComponent; 管理している PHP のコード
return ( <div> <textarea value={phpCode} onChange={(e) => setPhpCode(e.target.value)} rows={20} cols={50}
/> <button onClick={handleSubmit}>Run PHP</button> <pre>{output}</pre> </div> ); }; export default PHPWasmComponent; handleSubmit 関数 を実行
"use client"; import React, { useState, useEffect, FormEvent } from
"react"; import { PhpWeb } from "php-wasm/PhpWeb.mjs"; const PHPWasmComponent: React.FC = () => { const [output, setOutput] = useState<string>(""); const [phpCode, setPhpCode] = useState<string>( `<?php\nprint("hello php");\n?>` ); const [phpInstance, setPhpInstance] = useState<PhpWeb | null>(null); useEffect(() => { const initializePhp = async () => { const php = new PhpWeb(); php.addEventListener("output", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("error", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("ready", () => { setPhpInstance(php); }); }; initializePhp(); }, []); const handleSubmit = async (event: FormEvent) => { event.preventDefault(); if (phpInstance) { setOutput(""); try { await phpInstance.run(phpCode); } catch (error) { setOutput((error as Error).message); } } }; return ( <div> <textarea value={phpCode} onChange={(e) => setPhpCode(e.target.value)} rows={20} cols={50} /> <button onClick={handleSubmit}>Run PHP</button> <pre>{output}</pre> </div> ); }; export default PHPWasmComponent; page.tsx
const handleSubmit = async (event: FormEvent) => { event.preventDefault(); if
(phpInstance) { setOutput(""); try { await phpInstance.run(phpCode); } catch (error) { setOutput((error as Error).message); } } };
const handleSubmit = async (event: FormEvent) => { event.preventDefault(); if
(phpInstance) { setOutput(""); try { await phpInstance.run(phpCode); } catch (error) { setOutput((error as Error).message); } } }; PhpWeb インスタンスの run メソッドを実行
useEffect(() => { const initializePhp = async () => {
const php = new PhpWeb(); php.addEventListener("output", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("error", (event: any) => { setOutput((prevOutput) => prevOutput + event.detail + "\n"); }); php.addEventListener("ready", () => { setPhpInstance(php); }); }; initializePhp(); }, []); イベントリスナー output, error が実行さ れる
完成! - テキストエリアにPHP のコードを入力 - [Run PHP]のボタンを 押下 - 実行結果が表示される
おまけ
None
None
リッチなエディターにしてみる
@monaco-editor/react
@monaco-editor/react とは Monaco Editor は、Microsoft がオープンソースとして開発 しているコードエディターです。Monaco Editor は VSコー
ドを動かすコード・エディターです。 Monaco Editor を React で簡単に扱えるようにしたラッパー です。 https://monaco-react.surenatoyan.com/
使い方
"use client"; import React from "react"; import Editor from "@monaco-editor/react";
const PHPWasmComponent: React.FC = () => { return ( <Editor height="100%" width="50%" language="php" theme="vs-dark" /> ); }; export default PHPWasmComponent; これだけです
Visual Studio Code のようなエディタを Web に組み込めます https://monaco-react.surenatoyan.com/
Ruby を Next.js で実行するのもあるので 見てください!
https://speakerdeck.com/ohmori_yusuke/next-dot-js-de-ruby-wopuroguramingu-a nd-shi-xing-dekiruapuriwozuo-ru
宣伝 59
ゆめみ大技林 '24 技術書典で頒布している技術同人誌に 寄稿したので読んでください!ダウン ロードできます! 「読むとLTがしたくてたまらなくなる マニュアル」で書きました! 60
61
62
ご清聴ありがとうございました!