実際にWebアプリを作ってみよう
ローカルサーバーについて
Webアプリを動かすにあたって、
- Webサーバー
- APサーバー
- DBサーバー
の3つのサーバーが基本的には必要になります。
が、「ちょっと作ってみよう!」のレベルでこれを全部用意するのはめちゃくちゃ面倒です...
実際の開発として考えても、テスト用の環境があったとしてもちょっとした動作確認のためにサーバーへ資材をアップロードすることは面倒極まりないです。
そこで、自分のPC上でWeb/AP/DBの役割をまとめて動かせる環境を用意することができます。こうした環境を一般的にローカルサーバーと言います。
自分のPC以外のコンピューターを使わないので追加料金がかかったりはしません。
一方、自分のPCしか使っていないので実際にみんなが使えるWebアプリを公開することはできません。
本記事では、このローカルサーバーの準備を行い、入力内容をDBに登録・参照できる簡単なWebアプリを作成してWebアプリのイメージをつかんでもらえればなと思います。
PHPとSQLiteのインストール
さて、作っていくWebアプリですが、表題のPHPとSQLiteを使って製作していきます。
PHPはプログラミング言語、SQLiteがDBにあたります。
なぜこの2つをチョイスしたかというと、動かすのが簡単だからです
PHPのインストール
こちらのページへアクセスし、PHPをダウンロードしましょう。
一番上が最新版になるので、そこのZipリンクをクリックしてZipファイルをダウンロードします。
ダウンロードできたら、それを任意のフォルダに展開します。
私はわかりやすいようにCドライブ直下に「php」というフォルダ名で配置しました。
続いて、環境変数への追加を行います。
初心者が躓きやすいポイントかと思います(私も何回か躓いています)が、難しく考えすぎず、PHPはここに保存したからねとPCに登録する作業になります。
まずは、左下の検索バーに「環境変数」と入力して検索、すると「環境変数の編集」という検索結果が表示されるかと思います。
そこをクリックして表示される画面にて、Pathにカーソルを合わせて「編集」
「新規」をクリックして、保存したフォルダのパスを入力してください。
私はCドライブ直下に保存したので「C:\php」を入力して「OK」
これで登録完了です。
「C:\php」にPHPがあるからね!とPCに登録できたのか確認してみましょう。
コマンドプロンプトを立ち上げて(※)
php --version
を入力し、結果が返ってくればOKです
※
コマンドプロンプトは検索バーに「コマンドプロンプト」を打ち込む、またはWin + Rのショートカットで入力欄を表示して「cmd」と入力することで立ち上がります。
php.iniの設定
ダウンロードしたphpフォルダの中に「php.ini-development」「php.ini-production」のファイルが見つかるかと思います。
どちらかをコピーして、ファイル名を「php.ini」に変更して下さい。
これが、phpの設定ファイルになります。
テキストファイルとして開いてもらい、「sqlite」で検索をかけます。
extension=pdo_sqliteとextension=sqlite3という記述を見つけたら、文頭に;があると思うので、これを削除してください。
これで、後続で使用するSQLiteをPHPから利用できるようになります。
SQLiteのインストール
DBにあたるSQLiteをインストールしていきます。
これは、1つのファイルでDBの管理を行うことができるツールになります。
DBをエクセルファイルのようにコピーしたり移動したりすることができるということですね。
1つのファイルにまとめる関係上、規模の大きなアプリには向きません。
一方で直感的に扱いやすく、別途DBサーバーを準備する必要もないので、「試しに動かしてみる」には最適なツールになります。
こちらにアクセスし、SQLiteをダウンロードしましょう。
Precompiled Binaries for Windowsに表示されているもののうち、tools、win-x64の記載のあるZipファイルをダウンロード
ダウンロードしたら適当なフォルダーに展開します。
私はPHPと同様に、Cドライブ直下に保存しました。
その後、PHPの時同様に環境変数へ登録
コマンドプロンプトにて、
sqlite3 --version
を実行し、結果が表示されればOKです。
注意:sqliteではなく、sqlite3というコマンドになるので注意してください
ローカルサーバーの起動
適当なフォルダに、「test.php」を作成し、下記を入力します。
<!DOCTYPE html>
<html>
<head>
<meta lang="ja" />
<meta charset="utf-8" />
<title>PHP</title>
</head>
<body>
<a>ローカルサーバーテスト</a>
</body>
</html>
※現時点ではPHPのコードはなく、HTMLのみの記述です。
その後、そのフォルダパスの部分に「cmd」を入力して、コマンドプロンプトを起動します。
すると、そのフォルダを基準としたコマンドプロンプトが立ち上がります。
※もちろんコマンドプロンプトを立ち上げて、cdコマンドで移動してもらっても大丈夫です。
その後
php -S localhost:8080
を入力すると、ローカルサーバーが立ち上がります。
その後、下記アドレスにアクセス
http://localhost:8080/test.php
作成したHTMLの中身が、ブラウザで表示されればOKです!
なお、ローカルサーバーを止めるときは、コマンドプロンプト上でCtrl + Cにて止められます。
DBの設定
続いて、DBの準備を進めていきます。
ローカルサーバーを立ち上げたとき同様に、test.phpが存在しているフォルダで実行されるようにコマンドプロンプトを立ち上げましょう。
sqlite3 temp.db
上記コマンドを入力して、「temp.db」というSQLiteファイルに書き込む準備をします。
※この時点ではファイルは作成されない。
sqlite> という記述になっていればOKです。
そのままコマンドプロンプトにて、下記を実行しusersというテーブルを作成します。
create table
users
(
user_id text not null primary key
, password text not null
, check(length(user_id) <= 11)
);
※そのまま張り付けてもいいし、普通に改行しながら打ち込んでもらっても大丈夫です。
エラーにならずに実行され、temp.dbというファイルが作成されればOKです。
SQLの基本
(作成中)
ログイン処理の実装
注)実際のログイン処理は例えばパスワードを暗号化するなどセキュリティも意識する必要がありますが、今回は自分のPCだけで動かすのでその辺は考慮しません
ファイル構成
- signup.php (アカウント作成ページ)
- login.php (ログインフォーム)
- main.php (ログイン後にアクセスするページ)
- temp.db (先ほど作成したDBファイル)
アカウント作成ページ(signup.php)
<?php
if($_SERVER["REQUEST_METHOD"] === "POST"){ // POST以外の時は実行しない
// POSTで入力情報を取得する
$userId = $_POST["userId"] ?? "";
$password = $_POST["password"] ?? "";
try{
// 英数字とアンダースコアのみ、1文字以上からしか受け付けない
$pattern = "/^[A-Za-z0-9_]+$/";
if(!preg_match($pattern, $userId)){
throw new Exception("ユーザーIDが不正です");
}
if(!preg_match($pattern, $password)){
throw new Exception("パスワードが不正です");
}
// DBに接続してデータを登録する
$pdo = new PDO("sqlite:". __DIR__ . "/temp.db");
$sql = "INSERT INTO users VALUES (:user_id, :password);";
$result = $pdo->prepare($sql);
$result -> bindValue(":user_id", $userId);
$result -> bindValue(":password", $password);
$result -> execute();
// 正常終了を通知
echo "<script>alert('ユーザーが登録されました')</script>";
}catch(Exception $e){
echo "<script>alert('". $e->getMessage() ."')</script>"; // エラーメッセージをブラウザに表示
}
}
?>
<!DOCTYPE html>
<html>
<head>
<meta lang="ja" />
<meta charset="utf-8" />
<title>アカウント作成ページ</title>
</head>
<body>
<form name="form1" method="post" action="">
<label>新しいユーザーID</label>
<input type="text" name="userId" maxlength=11 /><br />
<label>パスワード</label>
<input type="password" name="password" /><br />
<button type="submit">登録</button>
</form>
</body>
</html>
各種コードの説明は後にするとして、signup.phpというファイルをtemp.dbと同じフォルダに作成してください。
ローカルサーバーを起動し、signup.phpにアクセスすると、入力フォームが表示されます。
ユーザーIDとパスワードを入力して「登録」をクリックすると、入力した内容が先ほど作成したusersテーブルに登録されるはずです。
select * from users;
sqliteのコマンドで、上記結果として入力内容が表示されればOKです。
コード解説
if($_SERVER["REQUEST_METHOD"] === "POST"){ // POST以外の時は実行しない
ここはコメントにある通り、POSTでアクセスされたとき以外処理しないように制御しています。
これがないと、例えばこのページに普通に飛んできたときも「ユーザーIDが不正です」というエラーメッセージが出てしまうので、この制御を入れています。
じゃあ、いつこのページにデータがPOSTされるのか?ですが、「登録」ボタンを押下したときです。
formタグに、action=""を設定しているので、submitを行うと自分自身(signup.php)にデータをPOSTする挙動になっています。
// POSTで入力情報を取得する
$userId = $_POST["userId"] ?? "";
$password = $_POST["password"] ?? "";
ここも記述の通り、POSTされたデータを変数に格納しています。
※PHPでは変数を $... という形で記述します。
POSTされたデータのうち、「userId」「password」という名称のデータを取得しています。
この名称はどこで設定しているかというと、formタグの中に存在しているinputタグ・name属性で設定しています。
<input type="text" name="userId" maxlength=11 />
※[?? ""]という記述は、NULLだったら空文字を代入してという意味です。
try-catch構文
try{ ... }catch(Exception $e){ ... }
という記述は、tryブロック内でエラーが発生したときに、catchブロックに処理が移るという記述です。
エラーが起きたらその後半は実行されず、catchブロック内の処理が行われるようになります。
catch(Exception $e){
echo "<script>alert('". $e->getMessage() ."')</script>"; // エラーメッセージをブラウザに表示
}
catchブロック内の処理としては、javascriptのalertという関数を利用してメッセージをブラウザに表示しています。
$e->getMessage() はエラーメッセージを取得する構文なので、try内で起こったエラーメッセージを画面に表示するような処理になっています。
// 英数字とアンダースコアのみ、1文字以上からしか受け付けない
$pattern = "/^[A-Za-z0-9_]+$/";
if(!preg_match($pattern, $userId)){
throw new Exception("ユーザーIDが不正です");
}
ここも記載の通り、ユーザーIDに使える文字を制限しています。
英数字、アルファベット以外が存在しているか?を検索する手法として正規表現というものを使用しています。
正規表現
throw new Exception の記述は、意図的にエラーを起こすという記述です。
例外をスローする、なんて言ったりします。
$userIdに例えば「あいう」という文字列が入ったとして、PHPとしては別にエラーでも何でもないので実行に支障はありません。
一方、今回ユーザーIDには英数字とアンダースコア1文字以上を設定させたいので、条件に当てはまっていない場合は「エラーってことにして」と指示しているわけです。
tryブロック内でエラーを起こしているので、catchブロックに処理が飛ばされて、エラーメッセージとして「ユーザーIDが不正です」が表示されることになります。
// DBに接続してデータを登録する
$pdo = new PDO("sqlite:". __DIR__ . "/temp.db");
$sql = "INSERT INTO users VALUES (:user_id, :password)";
$result = $pdo->prepare($sql);
$result -> bindValue(":user_id", $userId);
$result -> bindValue(":password", $password);
$result -> execute();
// 正常終了を通知
echo "<script>alert('ユーザーが登録されました')</script>";
PDOというのは、DB接続に使用するクラスです。
クラスとは?
このPDOを使って、「同じディレクトリに存在するtemp.dbに接続」しているのが2行目です。
その後、テーブルにデータを登録するSQLのINSERTクエリを使用して作成したusersテーブルにuser_idとpasswordを登録します。(3~4行目)
user_idとpasswordに、具体的に何の値が入るの?というのを設定しているのが5~6行目、POSTデータから取得した
7行目でINSERTクエリを実行し、うまくいったらjavascriptのalertによって正常終了を通知します。
ログインページ(login.php)
signup.phpと同様に、temp.dbが存在するフォルダにlogin.phpを作成してローカルサーバーを起動、login.phpにアクセスしてください。
<?php
if($_SERVER["REQUEST_METHOD"] === "GET"){
$error = $_GET["error"];
$errorMessage = "";
if($error === "login"){
$errorMessage = "ユーザーIDまたはパスワードが異なります";
}
if($error === "db"){
$errorMessage = "データベースエラーです";
}
if($errorMessage != ""){
echo "<script>alert('". $errorMessage ."')</script>"; // エラーメッセージをブラウザに表示
}
}
?>
<!DOCTYPE html>
<html>
<head>
<meta lang="ja" />
<meta charset="utf-8" />
<title>ログインページ</title>
</head>
<body>
<form name="form1" method="post" action="main.php">
<label>ユーザーID</label>
<input type="text" name="userId" maxlength=11 /><br />
<label>パスワード</label>
<input type="password" name="password" /><br />
<button type="submit">ログイン</button>
</form>
</body>
</html>
こんな感じのログイン画面が表示されます。
formタグを見てもらうとわかりますが、データのsubmit先はmain.phpです。
main.phpにてログイン認証を行い、失敗した場合はerrorというパラメータをもってlogin.phpへ戻ることで、ログイン失敗を通知するコードになっています。
ログイン後のページ(main.php)
前述、login.phpよりPOSTデータを受け取り、ログインに成功したらユーザーIDを表示する画面を作成します。
<?php
session_start();
if($_SERVER["REQUEST_METHOD"] === "POST"){ // POST以外の時は実行しない
// 入力データを取得
$userId = $_POST["userId"] ?? "";
$password = $_POST["password"] ?? "";
try{
// DBに接続してログイン情報を確認
$pdo = new PDO("sqlite:". __DIR__. "/temp.db");
$sql = "SELECT user_id FROM users WHERE user_id = :user_id AND password = :password";
$result = $pdo->prepare($sql);
$result -> bindValue(":user_id", $userId);
$result -> bindValue(":password", $password);
$result -> execute();
$row = $result->fetch(PDO::FETCH_ASSOC);
// 結果が取得出来たらセッションにユーザーIDを書き込む
if ($row){
$db_userId = $row["user_id"];
$_SESSION["userId"] = $db_userId;
}else{
header("Location: login.php?error=login");
exit;
}
}catch(Exception $e){
header("Location: login.php?error=db");
exit;
}
}
// ログイン状態の確認
if(empty($_SESSION["userId"])){
header("Location: login.php");
exit;
}
?>
<!DOCTYPE html>
<html>
<head>
<meta lang="ja" />
<meta charset="utf-8" />
<title>メインページ</title>
</head>
<body>
<label>あなたのユーザーIDは<?= $_SESSION["userId"] ?>です</label>
</body>
</html>
コード解説
session_start();
セッション管理の開始を宣言しています。
セッションとは?
$row = $result->fetch(PDO::FETCH_ASSOC);
// 結果が取得出来たらセッションにユーザーIDを書き込む
if ($row){
$db_userId = $row["user_id"];
$_SESSION["userId"] = $db_userId;
}else{
header("Location: login.php?error=login");
exit;
}
DB接続してSQLを実行する部分はsignup.phpと同様です。
一方、今回はすでにusersテーブルに存在しているデータをSELECTで検索しているので、検索結果を$rowとして受け取っています。
SQLが
SELECT user_id FROM users WHERE ...
というSELECTクエリなので、user_idの結果を$row["user_id"]として取得し、セッションにuserIdという名称でIDを書き込んでいます。
結果が取得できなかった場合、$rowはfalseになるので、error=loginを渡す形でlogin.phpのページに戻しています。
// ログイン状態の確認
if(empty($_SESSION["userId"])){
header("Location: login.php");
exit;
}
先ほどセッションに書き込んだuserIdが存在していなければ、これもlogin.phpへ戻します。
逆に一度ログインすると、セッションにuserIdが書き込まれているので、直接
にアクセスしてもユーザーIDを画面に表示してくれる状態になります。<label>あなたのユーザーIDは<?= $_SESSION["userId"] ?>です</label>
.phpファイルでは、HTMLの記述の中に<?php ... ?>とすることでPHPの処理を埋め込むことができます。
これをコードスぺニットと呼びます。
なお、<?= ... ?>という書き方は、<?php echo ... ?>の短縮記法です。
Discussion