PHPとLinuxとApacheとMySQLを組み合わせてみよう

目次

PHPとは

LAMPのPです。

PHPって何?

正式名称は PHP: Hypertext Preprocessorです。

Hypertext Preprocessorを名乗るだけあって、主にHTMLを動的生成するWebアプリケーション用途に重きをおいたスクリプト言語です。

PHPで動的なWebページを作成してみよう

PHPのインストールと動作確認

phpパッケージをインストールして下さい。 ついでに、後ほど使うのでphp-pdophp-mysqlも入れておいてください。

# yum install php php-pdo php-mysql

ApacheのDirectoryIndexの設定の先頭にindex.phpを追加しておいてください。

DirectoryIndex index.php index.html

PHPインストール時に、ApacheのPHP関係のモジュール、設定ファイルが自動的に読み込まれるように設定されるので、 Apacheを再起動してPHPのモジュールを読み込ませます。

# systemctl restart httpd

とりあえず、PHPファイルを作成してみましょう。

# vim /var/www/html/index.php
--⇣vim内に入力--
<?php phpinfo();?>
--↑vim内に入力--

それでは、http://{サーバーのPublicIP}/にアクセスしてみましょう。 下図のような画面が表示されたら成功です。お疲れ様です。

ちなみに、このphpinfoとは、 PHPの内部的な設定状況を出力する関数で、PHPが正しく動いているか確認する手段としてよく利用されます。 なにが書いてあるのかは、難しいのでわからなくて大丈夫です。

PHPとApacheの連携

現在の状況を概念図に落とすと下図のように表すことができます。

つまり、Apacheは、Webブラウザからindex.phpをリクエストされた際に、 index.phpをそのままWebブラウザへ返すのではなく、index.phpをPHP処理系で実行した結果を返しているのです。

なので、<?php phpinfo();?>という文字列自体でなく、<?php phpinfo();?> の処理結果が表示される次第です。

PHPの書き方

冒頭でも説明したとおり、PHPはHTMLを動的生成ことに重きをおいています。

そのため、PHPのコードはHTMLという別の言語の中に埋め込まれることが多々あるため、 特別なタグ<?php {{PHPのコード}} ?>で囲んで、その中にPHPの処理内容を記述します。

参考: PHP タグ

なんか動かないんスけど…

なにかエラーが起きているかもしれません。 エラーログ(なにかマズいことが起きた際にエラーが出力される)を確認して見ましょう。

# tail -f /var/log/httpd/error_log

ちなみに、アクセスログ(正常時もアクセスの履歴が残るファイル)は下記になります。

# tail -f /var/log/httpd/access_log

PHPの使い方について

PHPの使い方だけでも分厚い本がかけてしまうレベルなので、まずは下記を参考にしてください。

PHP入門 – 基本構文の解説からデータベースへのアクセス方法まで

LAMPをすべて組み合わせて簡単なフォームを作ってみよう

作成するもの: ログインフォーム

以下の要件のログインフォームを考えます。

  • ページを開くと、ユーザ名とパスワードを入力するフォームと、ログインボタンが表示される
  • ユーザー名とパスワードを入れてログインボタンを押下すると、サーバー内でログイン処理が実行される
    • データベースを検索して、合致するレコードが存在した場合、ログイン成功
    • データベースを検索して、合致するレコードが存在しない場合、ログイン失敗

ただし、前提条件として、MySQLのデータベースに以前作成したような以下のようなユーザー情報テーブルがあると想定します。

  • データベース名: form
  • テーブル名: user_info
name   password
 user1   user1
 user2   user2
 user3   user3

アクセス時

ログイン成功時

ログイン失敗時

HTMLでフォームを組み立てよう

下記のファイルにフォームを表示するためのHTMLを記述します。 以前作成したファイルが残っていたら、上書きして書き換えてしまってください。

# vim /var/www/html/index.php
---⇣vim内に入力---
<!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>login form</title>
  </head>
  <body>
      <form id="loginForm" name="loginForm" action="#" method="POST">
          <legend>ログインフォーム</legend>
          <label for="username">ユーザ名</label>
          <input type="text" id="username" name="username" value="" />
          <br />
          <label for="password">パスワード</label>
          <input type="password" id="password" name="password" value="" />
          <br />
          <input type="submit" id="login" name="login" value="ログイン" />
      </form>
  </body>
</html>
---↑vim内に入力---

http://{{サーバーのIP}}/にアクセスした際に、ログインフォームが表示されたら成功です。 まだ、ログイン処理を実装していないので、ボタンを押しても反応がありません。

それぞれのタグの意味については、下記の参考サイトなどを調べてみてください。

参考: HTMLタグリファレンス

よく見ると、formタグの中に

  • input type="text" => ユーザ名入力欄(ただの文字列入力欄)
  • input type="password" => パスワード入力欄(ソーシャルエンジニアリング予防のために打った文字列が隠される文字列入力欄)
  • input type="submit" => formをWebブラウザからWebサーバーへ投稿するためのボタン
  • label => 説明用の文字列

といった要素があることが読み取れます。

このようにHTMLでは、タグ例: <tag></tag>を入れ子状に組み立てていくことによって、ページの内容を組み立てていきます。

PHPでサーバーサイドの処理を実装しよう

先ほど作成したindex.phpを下記の通り書き換えましょう。

# vim /var/www/html/index.php
---⇣vim内に入力---
<!-- ↓ここから追加↓ -->
<?php header("X-XSS-Protection: 0");?>
<?php
    $logged_in = false;

    // ログインボタンが押された場合      
    if (isset($_POST["login"])) {
        $username = $_POST["username"];
        $password = $_POST["password"];
        try {
            $dbh = new PDO('mysql:dbname=form;host=localhost', 'root', 'Intern@1234');
        } catch (PDOException $e) {
            exit('データベースに接続できませんでした'.$e->getMessage());
        }
        $sql = "SELECT * FROM user_info WHERE name='$username' AND password='$password'";
        $stmt = $dbh->prepare($sql);
        $stmt->execute();
        $data = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!empty($data)) {
            $logged_in = true;
            echo "ログイン成功";
        } else {
            $logged_in = false;
            echo "ログイン失敗";
        }
    }
?>
<!-- ↑ここまで追加↑ -->

<!doctype html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>login form</title>
    </head>
    <body>
        <!-- ↓この行を変更↓ -->
        <form id="loginForm" name="loginForm" action="<?php print($_SERVER['SCRIPT_NAME']); ?>" method="POST">
        <!-- ↑この行を変更↑ -->
            <legend>ログインフォーム</legend>
            <label for="username">ユーザ名</label>
            <input type="text" id="username" name="username" value="" />
            <br />
            <label for="password">パスワード</label>
            <input type="password" id="password" name="password" value="" />
            <br />
            <input type="submit" id="login" name="login" value="ログイン" />
        </form>
        <!-- ↓ここから追加↓ -->
        <?php if ($logged_in): ?>
            <?php echo "こんにちは".$_POST["username"]."さん"?>
        <?php elseif (!empty($_POST["username"])):?>
            <?php echo $_POST["username"]."さんが見つかりませんでした。入力情報を確認してください"?>
        <?php endif;?>
        <!-- ↑ここまで追加↑ -->
    </body>
</html>
---↑vim内に入力---

噛み砕いて見ていきましょう。

フォーム入力内容の投稿先設定

<form ... action="<?php print($_SERVER['SCRIPT_NAME']); ?>" ... >

この部分は、HTMLのfromタグのaction属性として自分自身のPHPファイル名を設定している記述です。 つまり、Webブラウザ上のログインボタンを押下した際に、 フォームに入力された情報をこのPHPファイルindex.phpで処理させるための指定です。

参考

フォーム入力内容の投稿方法設定

<form ... method="POST">

同様に、HTMLのformタグのmethod属性としてPOSTを指定する記述です。

WebサーバーとWebブラウザ間の通信に使用しているHTTPというプロトコル(通信方法に関する決まり事)には、 単純にWebサーバーからWebページを取得するだけのリクエストの他にも、 Webサーバーへ情報を送るリクエストなど、たくさんのリクエストが定められています。 それらのリクエスト種別を指定する項目がHTTPメソッドです。

今回は、ログインボタンを押下した時に、フォーム入力情報をWebサーバーへ送信したいので、 POSTメソッドで情報をサーバーへ送るように設定します。

参考

Webブラウザ上で入力された文字列の取得

前項までの設定により、ログインボタンを押下した後に、 フォーム入力内容がApacheを介してPHPへ送信されるようになりました。 ここからはフォーム入力内容の受け取り方、受け取ったあとの処理をみていきます。

PHPでは、POSTメソッドにより送信された情報は$_POSTという変数に格納されています。

見覚えがある感じの変数ですが、実は、PHPにはスーパーグローバル というスーパーな変数があり、 $_SERVER$_POST のような名前の変数に、アクセス時の情報が自動的に格納されるようになっています。

なので、$_POST["username"]$_POST["password"]のような記述により、 フォーム入力内容が取得できます。

そのログインボタンは押されたか

実は、このindex.phpファイルが実行されるのは、以下の2つのケースのいずれかです。

  1. ユーザーがWebブラウザにURLを入力して、最初にログインフォームを表示する場合(ログインボタンは押下されていない)
  2. ユーザーがログインボタンを押下してログイン処理を行った場合(ログインボタン押下された)

自明ですが、1. の場合、まだユーザーはログイン情報を入力していないため、 データベースでSELECT文を実行する必要はありません。

そのため、 以下のif文で、ログインボタンが押されたのかどうかを判定しています。

    // ログインボタンが押された場合      
    if (isset($_POST["login"])) {
        // ...
    }

すでに説明しましたが、2. の場合のログインボタン押下後にフォーム入力情報をWebサーバーへ送信する際は、 HTTPのPOSTメソッドで送信するようにしました。

対して、1. の場合のWebブラウザでいつもどおりWebサイトへアクセスする場合は、GETメソッドでリクエストが送信されます。

したがって、上記の様なif文でログインボタン押下有無の判定を行うことができます。

PHP <-> MySQLサーバーの連携

PHPからMySQLサーバーとのやり取りを行うためには、 PHPのPDOというライブラリを使用します。

とはいっても、処理内容としては、以前ターミナルから手動実行してもらったような作業を プログラムから行うようにしたというという程度です。

以下の行でmysqlサーバーへログインします。

try {
    $dbh = new PDO('mysql:dbname=form;host=localhost', 'root', '');
} catch (PDOException $e) {
    exit('データベースに接続できませんでした'.$e->getMessage());
}

このtry-catchという記述は例外処理といい、 名前通りに予期せぬ自体(例外)が起きた際の処理を記述するための記法です。

このコードの場合、何らかの原因(データベースサーバーが起動していなかった、データベースログインのパスワードが間違っていた) により、データベースサーバーにログインできなかった場合に諦めてプログラムを終了するという内容になっています。

$sql = "SELECT count(*) as cnt FROM user_info WHERE name='{$username}' AND password='{$password}'";
$stmt = $dbh->prepare($sql);
$stmt->execute();
$data = $stmt->fetch(PDO::FETCH_ASSOC);

これらの行では、データベースサーバーでSELECT文を実行して、その結果をPHP側に取得してくる処理になります。 prepareってなんじゃ?PDO::FETCH_ASSOCっておいしいの?という気分かもしれませんが、 マニアックな話題なので解説しません。興味がある方は下記を参照してください。

参考

ログイン処理結果の出力

下記のとおり判定を行っています。

  • SELECT文の結果が0件だったら => ログイン失敗
  • SELECT文の結果が0件ではなかったら => ログイン成功
if (!empty($data)) {
    echo "ログイン成功";
} else {
    echo "ログイン失敗";
}

課題

前述したように、PHPにはスーパーグローバルというスーパーな変数が存在します。

下記のデバッグコードを追加して、これらのスーパーグローバル変数の具体的な内容について、どんな情報が格納されているか確認してみましょう。

  1. ユーザーがWebブラウザにURLを入力して、最初にログインフォームを表示する場合(ログインボタンは押下されていない)
  2. ユーザーがログインボタンを押下してログイン処理を行った場合(ログインボタン押下された)
    ...
    <body>
        ...
        <h3>$_SERVER</h3>
        <pre><?php var_dump($_SERVER); ?></pre>
        <h3>$_POST</h3>
        <pre><?php var_dump($_POST); ?></pre>
    </body>
</html>

確認が終わったら、追加したデバッグコードは消しておいてください。

より詳しい情報

フォームデータを送信する – MDN