Developer

魁!小野の塾 小さな管理機能を作ってみようの巻 第18話
2021.08.26
Lv2

魁!小野の塾 小さな管理機能を作ってみようの巻 第18話

小さな管理機能を作ってみよう 第18話

初心者向け、PHPプログラム構築講座です。
初心者といっても、PHPの勉強を少し行い、LAMP環境が自分で構築でき、少しアプリケーションを作成しているレベルを対象とします。
まったくの初心者の場合は、わからない部分が出てくると思います。
できるだけ細かく説明は入れていきますが、説明がわからない場合は、PHPやMySQLの初心者講座をご覧ください。

対象のスキルレベル

  • LAMP環境の構築
  • PHP言語が読める
  • HTML, CSS, Javascriptが少しわかる
  • Bootstrapのドキュメントをみて、HTMLが書ける
  • Ajax(非同期通信)を利用したことがある
  • SESSIONを利用したことがある

構築環境

  • Windows10
  • XAMPP(PHP7.3.2, MariaDB 10.1.38)

index.phpの作成

Controllerの作成も終わりましたが、実際リクエストの受け口を作成する必要があります。
urlのパターンで、フォルダーにアクセスする際は、ウェルカムファイルの指定により、index.phpやindex.htmlが表示されます。
今回は、index.phpにリクエストを全て受けてもらい、返却を担当してもらいます。
リクエストは、非同期通信以外は受け付けたくないので、非同期通信かのチェックを行い、共通のヘッダを設定します。
非同期通信ではない場合は、般若心経をレスポンスする内容になっています。

/api/index.php ファイルを作成します。

/api/index.php
<?php
/**
 * index.php requestの受け口です。
 * 各処理に処理分けします。(process)
 * 関数を実行します。(method)
 */
require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Controller.php';
/* セッションをスタートします */
session_start();
/* 非同期通信かは、HTTP_X_REQUESTED_WITHにxmlhttprequestが入っているかで確認します */
if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'){
	/* 非同期通信なら、Controllerをインスタンス化して、処理を実行します */
	$controller	= new Controller();
	$result	= $controller->process($_POST);
}else{
	/* 非同期通信ではない場合は、般若心経をレスポンスします */
	echo implode('<br>', [
		'<div style="background-color:#ccc;color:#fff;width:1230px;margin:0px auto;padding:30px;">',
		'<h1>アンターナニヤッテルノサー!ウチノブタ、タタクンデネード!</h1>',
		'<span style="font-size:30px;line-height:36px;">仏説摩訶般若波羅蜜多心経',
		'観自在菩薩行深般若波羅蜜多時照見五蘊皆空度一切苦厄舎利子色不異空空不異色色即是空空即是色受想行識亦復如是舎利子是諸法空相不生不滅不垢不浄不増不減是故空中無色無受想行識無眼耳鼻舌身意、無色声香味触法無眼界乃至無意識界無無明亦無無明尽乃至無老死亦無老死尽無苦集滅道無智亦無得以無所得故菩提薩埵依般若波羅蜜多故心無罣礙無罣礙故無有恐怖遠離一切顛倒夢想究竟涅槃三世諸仏依般若波羅蜜多故得阿耨多羅三藐三菩提故知般若波羅蜜多是大神呪是大明呪是無上呪是無等等呪能除一切苦真実不虚故説般若波羅蜜多呪',
		'即説呪曰羯諦羯諦波羅羯諦波羅僧羯諦菩提薩婆訶',
		'般若心経</span></div>'
	]);
	exit();
}
/* 共通のヘッダを設定します。 */
/* レスポンスはjson */
header('Content-Type: application/json; charset=utf-8');
/* CORSの設定 localhost許可 */
header('Access-Control-Allow-Origin', 'http://localhost');
/* CORSの設定 非同期通信などを許可 */
header('Access-Control-Allow-Headers', 'Origin, X-Requested-Width, Content-Type, Accept');
/* CORSの設定 cookieを許可 */
header('Access-Control-Allow-Credentials', 'true');
/* Controllerから受け取った返却を返します。(JSON) */
echo $result;
?>

jQuery.ajaxを利用する予定なので、非同期通信かは、HTTP_X_REQUESTED_WITH をみて判断します。
※参考 [PHP] Ajax 通信かどうかの判定方法

リクエストが非同期通信の場合は、Controllerをインスタンス化し、processメソッドを実行します。Controller内部で、クラスの読込、メソッドの実行を行い、jsonが返却される想定です。

レスポンスには、共通ヘッダを返します。
まず、レスポンスのコンテンツタイプがjsonであることと、CORSの設定を入れています。
※参考 CORSまとめ

認証機能の作成

リクエストの受け口も作成しましたので、まずは認証機能(ログイン)から作成していきたいと思います。
Auth.phpはControllerで require_once されるため、Controllerで読み込まれているファイルを全て利用できます。
まず、api側を作成し、共通のJavascriptを作成、HTML側を修正する流れで説明していきたいと思います。

Auth.php

認証機能を実装するためのプログラムになります。
/api/Auth.php ファイルを作成します。

/api/Auth.php
<?php
/* Authクラスを作成します */
class Auth {
	/* Traitを利用するための宣言 */
	use Result;

	/* ログイン判定 */
	function isLogin($con, $request){
		/* 認証情報があるか確認します */
		if(isset($_SESSION['login'])){
			/* レスポンスに成功をセットします。 */
			$this->success();
			/* ログイン情報をレスポンスに設定します */
			$this->setList('login', $_SESSION['login']);
		}else{
			/* 記憶するにチェックがあるか確認します */
			if(is_null($request['re'])){
				/* ない場合は、未ログインとして処理します */
				$this->error('success', '*', '未ログイン');
			}else{
				/* 記憶するにチェックがある場合は、メールアドレスからログイン情報を取得します */
				$user	= $this->getUser($con, $request['re']);
				/* ユーザ情報が取得できたか確認します */
				if(!is_null($user)){
					/* ユーザ情報からパスワードの情報を除去します */
					unset($user['hash']);
					/* ユーザ情報をセッションに設定します */
					$_SESSION['login']	= $user;
					/* セッションIDを再振出します */
					session_regenerate_id();
					/* レスポンスに成功をセットします。 */
					$this->success();
					$this->setList('login', $_SESSION['login']);
				}else{
					/* 未ログインとして処理します */
					$this->error('success', '*', '未ログイン');
				}
			}
		}
		return $this->result();
	}

	/* ログアウト処理 */
	function logout(){
		/* セッションIDを再振出します() */
		session_regenerate_id();
		/* セッションからログイン情報を削除します */
		unset($_SESSION['login']);
		/* レスポンスに成功をセットします。 */
		$this->success();
		return $this->result();
	}

	/* ログイン処理 */
	function login($con, $request){
		/* メールアドレスからユーザ情報を取得します */
		$rs	= $this->getUser($con, $request['mail']);
		/* ユーザ情報があるかを確認します */
		if(!is_null($rs)){
			/* パスワードはハッシュ化されているので、PHPのアルゴリズムで正しいパスワードかを確認します */
			if(password_verify($request['passwd'], $rs['hash'])){
				/* パスワードが正しければ、パスワード情報をユーザ情報から除去します */
				unset($rs['hash']);
				/* セッションにユーザ情報を登録します */
				$_SESSION['login']	= $rs;
				/* セッションIDを再振出します() */
				session_regenerate_id();
				/* レスポンスに成功をセットします。 */
				$this->success();
			}else{
				/* エラー内容を設定します */
				$this->error('error', 'mail', 'ユーザ又はパスワードが異なります。');
			}
		}else{
			/* エラー内容を設定します */
			$this->error('error', 'mail', 'ユーザ又はパスワードが異なります。');
		}
		return $this->result();
	}

	/* ユーザ情報取得 */
	function getUser($con, $mail){
		$sql	= "select * from users where mail = ?";
		$rs		= null;
		if($stmt = $con->prepare($sql)){
			$stmt->bind_param("s", $mail);
			$stmt->execute();
			$row	= [];
			$stmt->bind_result(
				$row['user_id'],
				$row['name'],
				$row['mail'],
				$row['tel'],
				$row['addr'],
				$row['dept_id'],
				$row['hash'],
				$row['created'],
				$row['modified']
			);
			if($stmt->fetch()){
				$rs	= $row;
			}
			$stmt->close();
		}
		return $rs;
	}
}
?>

処理は、大きく4つあります。

  1. isLogin($con, $request) ログインしているかの確認
  2. login($con, $request) ログイン処理
  3. logout() ログアウト処理
  4. getUser($con, $mail) ユーザ情報の取得(DBから)

use Result; は、Traitを利用するための宣言で、Controllerでファイルの読み込みが完了しているので、ここでは use を利用して宣言するだけで、Resultに書かれている関数を利用することができます。

isLogin では、ログイン状態かのチェックを行うのですが、画面にログイン状態を記憶するのチェックがありますので、少し複雑になっています。

login 処理では、パスワード確認に、password_verify を利用しています。この関数は、password_hash で生成したHashを比較してくれます。password_hashは優れもので、soltやpepperなどを利用せずに、毎回違うHashを出力してくれる関数です。

※参考 password_verify
※参考 password_hash()とpassword_verify()を用いた暗号化と認証の方法について

login処理、logout処理は、ともに成功した場合、session_regenerate_id() を実行しています。セッションハイジャック対応です。

※参考 セッションハイジャックとは?
※参考 セッションハイジャックとは?その原因と対策を徹底解説

魁!小野の塾