CURRICULUM  /  LAYER 01  /  CHAPTER 05

PHPを読む

PHPは、ブラウザではなくサーバーの中で動く言語です。

「動く場所が違う」というただ一点が、できることの違いを生み、セキュリティの考え方まで変えていきます。

PHPはどこで動いているのか

前の章で、JavaScriptは「ブラウザの中で動く言語」だと話しました。

あなたがページを開くと、HTMLと一緒にJavaScriptがブラウザに送られてきて、あなたのパソコンやスマホの中で実行される。

PHPは、これとは正反対です。

あなたのブラウザには届きません

サーバーの中で動いて、その結果として出来上がったHTMLだけがブラウザに送られてくるのです。

[ブラウザ]                          [サーバー]
    │                                  │
    │  「このページをください」        │
    │ ─────────────────────────▶       │
    │                                  │  ① PHPが実行される
    │                                  │  ② データベースから情報を取る
    │                                  │  ③ HTMLを組み立てる
    │                                  │
    │  HTMLだけが返ってくる            │
    │ ◀─────────────────────────       │
    │                                  │
  JavaScriptはここで動く           PHPはここで動く

この違いは、ちょっとした哲学の違いに見えて、実はとても大きな意味を持っています。

— Key Concept

PHPのコードはユーザーには絶対に見えません

ブラウザに届くのは、PHPが組み立てた後のHTMLだけ。

だからこそPHPでは、データベースのパスワードや、クレジットカード決済の秘密鍵のような、見られてはいけない情報を扱えるのです。

JavaScriptで同じことをすると、コードごとユーザーに丸見えになってしまいます。

PHPの基本構文 ─ JavaScriptとの差分だけ

PHPには変数があり、関数があり、条件分岐があります。

ここまでは前章で学んだJavaScriptとまったく同じ。

違うのは、書き方の細部です。

重要な差分だけ並べていきます。

差分1: PHPコードは <?php ... ?> で囲む

PHPの最大の特徴は、HTMLファイルの中に直接埋め込めることです。

そのため、「ここからここまでがPHPコードですよ」と区切るための印が必要になります。

それが <?php?> です。

<!DOCTYPE html>
<html>
<body>
  <h1>こんにちは</h1>
  <p>
    <?php
      echo "今日は良い天気です";
    ?>
  </p>
</body>
</html>

このファイルをブラウザが受け取ったとき、<?php ... ?> の部分は消えていて、代わりに「今日は良い天気です」という文字列が入っています。

サーバー側で echo が実行されて、その結果がHTMLに差し込まれるわけです。

差分2: 変数は $ で始まる

PHPの変数は、必ず $(ドル記号)から始めます。

これは強制ルールで、忘れると動きません。

<?php
$name = "山田";
$age  = 30;

echo "私の名前は" . $name . "です";
// → 私の名前は山田です
?>

もう一つ、JavaScriptと違うのが、文字列の連結です。

JavaScriptでは + で文字列をくっつけましたが、PHPでは .(ドット)を使います。

これはPHPの伝統的なクセで、初めて書く人がまず間違えるポイントです。

— Note

PHPには constlet のような宣言キーワードはなく、いきなり $変数名 = 値; と書きます。

シンプルですが、その代わり「このファイルにどんな変数があるか」を一覧で把握しにくいという難点もあります。

変数名にコメントで意味を添えておく習慣を早めに身に付けましょう。

差分3: 関数定義は function、呼び出しは同じ

関数はJavaScriptとほとんど同じです。

function で定義して、引数を受け取り、return で値を返す。

<?php
function add($a, $b) {
  return $a + $b;
}

$sum = add(3, 5);
echo $sum;  // → 8
?>

ほぼ同じに見えますね。

違いは、引数名にも $ が付くことだけ。

差分4: 条件分岐もほぼ同じ

if / else if / else の構文は、JavaScriptとほぼ同じです。

比較記号も同じ(===!==<>= など)。

<?php
$score = 75;

if ($score >= 90) {
  echo "優";
} elseif ($score >= 70) {
  echo "良";
} else {
  echo "もう一歩";
}
?>

ほぼ同じですが、細かい差分が一つだけ。

JavaScriptでは else if(半角スペース入り)と書きましたが、PHPでは elseif(スペースなし)と書くのが伝統的です。

else if と書いても動きますが、コーディングスタイルとしては elseif が主流です。

PHPらしい特徴 ─ フォーム入力を受け取る

ここまでは「JavaScriptとそっくり」という話をしてきました。

ここからは「PHPならではの仕事」の話です。

PHPがサーバー側で動くということは、ユーザーが送ってきたデータを受け取って処理できるということ。

これがPHPの本領です。

たとえば、お問い合わせフォームから「名前」と「メッセージ」が送られてきたとき、PHPはそれを次のように受け取ります。

<?php
// フォームから POST で送られてきたデータを受け取る
$name    = $_POST['name'];
$message = $_POST['message'];

echo "こんにちは、" . $name . "さん!";
?>

$_POST というのは、PHPがあらかじめ用意してくれている特別な変数です。

ブラウザから送られてきたフォームデータが、自動的にこの中に入っているので、キー名を指定して取り出すことができます。

— 重要な警告

実は、上のコードには致命的な問題があります。

ユーザーが送ってきた $_POST['name'] を、そのまま画面に表示しているからです。

もしユーザーが名前欄に悪意のあるスクリプトを入れてきたら、そのまま実行されてしまいます(これが後の章で出てくるXSSという攻撃の正体です)。

「ユーザー入力はそのまま使ってはいけない」。

この一行だけ、今のうちに頭に刻んでおいてください。

詳しくはLayer 02の章で。

PHPからHTMLを「組み立てる」

PHPの一番面白いところは、HTMLと自由に混ぜて書けることです。

たとえば、ユーザー一覧をテーブルで表示したいとき、PHPでループを回しながらHTMLを繰り返し出力できます。

<?php
$users = [
  ['name' => '山田', 'age' => 30],
  ['name' => '田中', 'age' => 25],
  ['name' => '佐藤', 'age' => 42],
];
?>

<table>
  <tr><th>名前</th><th>年齢</th></tr>

  <?php foreach ($users as $user): ?>
    <tr>
      <td><?= htmlspecialchars($user['name']) ?></td>
      <td><?= $user['age'] ?></td>
    </tr>
  <?php endforeach; ?>

</table>

ここには新しい要素がたくさん出ています。

落ち着いて一つずつ見ていきましょう。

htmlspecialchars は、これから何度も見ることになります。

ユーザー入力をHTMLに出すときに、この関数を通すかどうかで、サイトが安全か危険かが決まります。

今は「ユーザーから来た文字列には必ずこれを被せる」とだけ覚えておいてください。

— Tip

このサイト(CodeReader)のPHPコードでは、htmlspecialchars を何度も書くのが面倒なので、e() という短い名前のラッパー関数を作って使っています。

やっていることは同じです。

自分のプロジェクトでも、よく使う関数に短い別名を付けておくと、コードが読みやすくなります。

解読演習

— EXERCISE 01-05-A

このPHPは、ブラウザに何を返すか

次のPHPファイルをブラウザから開いたとき、ユーザーには最終的に何が表示されますか? PHPコードの部分がサーバーで実行されて、結果だけがブラウザに届くことに注意してください。

<?php
$hour = 15;

if ($hour < 12) {
  $greeting = "おはようございます";
} elseif ($hour < 18) {
  $greeting = "こんにちは";
} else {
  $greeting = "こんばんは";
}
?>
<!DOCTYPE html>
<html>
<body>
  <h1><?= $greeting ?></h1>
</body>
</html>
解答例を見る

ユーザーのブラウザには、次のHTMLだけが届きます。

<!DOCTYPE html>
<html>
<body>
  <h1>こんにちは</h1>
</body>
</html>

$hour は15なので、「12未満」には該当せず、「18未満」に該当します。

そのため $greeting には「こんにちは」が入ります。

重要なのは、PHPの条件分岐部分はブラウザには一切届いていないということです。

ユーザーから見ると、サーバーは最初から「<h1>こんにちは</h1>」というHTMLを返してくれたようにしか見えません。

ブラウザの「ページのソースを表示」で見ても、PHPコードは残っていないはずです。

ここがJavaScriptとの決定的な違いです。

— EXERCISE 01-05-B

このPHPコードの問題点を3つ挙げてください

AIに「お問い合わせフォームから送られた名前とメッセージを画面に表示するPHPを書いて」と頼んだら、次のようなコードが返ってきました。

動きはしますが、実務で使うには危険な点や、お作法に反する点があります。

少なくとも3つ指摘してください。

<?php
$name = $_POST['name'];
$msg  = $_POST['message'];
?>
<html><body>
  <h1><?php echo $name; ?>さんからのメッセージ</h1>
  <p><?php echo $msg; ?></p>
</body></html>
解答例を見る

指摘ポイントは少なくとも3つあります。

(1) ユーザー入力をエスケープしていない(XSS脆弱性) ── これが一番深刻な問題です。

$name$msg はユーザーが自由に入力できる値で、そこに <script>悪意のあるコード</script> のようなHTMLタグを含めてくる可能性があります。

そのまま echo すると、ブラウザはそれを本物のHTMLとして解釈して実行してしまいます。

必ず htmlspecialchars($name, ENT_QUOTES, 'UTF-8') に通してから出力すべきです。

(2) キーが存在するかチェックしていない ── フォームが送信されずにこのページが直接アクセスされた場合、$_POST['name'] は存在せず、PHPが警告を出します。

isset($_POST['name']) で存在を確認するか、$_POST['name'] ?? '' のように「なければ空文字」と書くのが作法です。

(3) DOCTYPE と文字コード指定がない ── HTML文書としての基本がそろっていません。

先頭に <!DOCTYPE html><head> 内に <meta charset="UTF-8"> がないと、ブラウザによっては文字化けや誤認識が起きます。

動くコードと、安全で正しいコードは、別物です。

AIが書いたPHPを受け取ったら、まず「ユーザー入力をそのまま出してないか」を真っ先にチェックする習慣を付けましょう。

— EXERCISE 01-05-C

JavaScriptとPHP、どちらで書くべき?

次の4つの機能は、それぞれJavaScriptで書くべきでしょうか、それともPHPで書くべきでしょうか? 理由もあわせて考えてみてください。

  1. 入力フォームで、メールアドレスの欄に「@」が含まれているかチェックし、含まれていなければ赤く警告する
  2. ユーザーから送られたパスワードを、データベースに保存する前にハッシュ化する
  3. ボタンを押したら、画面上の数字が1ずつ増えるカウンター
  4. クレジットカード決済を行うために、決済会社のAPIを秘密鍵を使って呼び出す
解答例を見る

(1) JavaScript ── ユーザーの入力にその場で反応して画面を変える、いわゆるリアルタイムな検証です。

サーバーに送る前にブラウザ側でチェックできるので、ユーザー体験が良くなります。

ただし、「JavaScriptの検証は補助であって、セキュリティには使えない」点に注意。

ユーザーはJavaScriptを無効化したりツールを使ってバイパスできるので、本物の検証は必ずサーバー側(PHP)でも行う必要があります。

(2) PHP ── パスワードのハッシュ化は絶対にサーバー側で行います。

JavaScriptでやってしまうと、ハッシュ化の処理自体がユーザーに見えてしまい、せっかくの安全対策が意味を失います。

そもそもパスワードという機密情報を扱う処理は、ユーザーに見えない場所(サーバー)でしかやってはいけません。

(3) JavaScript ── 「ボタンを押したら画面が変わる」という純粋なUIの動きです。

サーバーとのやり取りは必要ないので、JavaScriptで完結させます。

これをPHPでやろうとすると、ボタンを押すたびにサーバーにリクエストが飛んでページが再読み込みされ、使い心地が最悪になります。

(4) PHP ── 決済APIの秘密鍵は、絶対にユーザーに見せてはいけない情報の代表格です。

JavaScriptで書くと秘密鍵がブラウザに届いてしまい、誰にでも盗まれます。

サーバー側のPHPの中でだけ使い、ユーザーには結果(決済成功・失敗)だけを返すのが鉄則です。

この4つの判別ができれば、「クライアント側(JS)とサーバー側(PHP)のどちらでやるべきか」という最重要の設計判断を、もう身に付けはじめています。

見せていい情報・処理はJS、見せてはいけない情報・処理はPHP

この線引きが、ウェブセキュリティの出発点です。

この章のまとめ

次の章では、PHPと二人三脚で動く相棒、データベースの話に入ります。

データはどこに、どう保管されているのか。

SQLというデータ操作の言語を、読めるようになることを目指します。