PHP

phpで作る簡素なログイン機能

自己紹介

プログラミング初心者
プログラマーに転職したいと思い勉強を始めた20代前半
ドットインストールさんなんかの入門を流し見て、次に何をしようと迷える子羊になったので、そんな昨日の自分みたいな人に参考になればと思います

最低限記事にしても問題のないコードを心がけてます。間違いがあれば随時修正します。
気づいた事があれば是非コメントで教えてください。Qiitaも初投稿なので、マナー違反等あれば教えて下さい。
必要であればすぐ削除します。

最低限の知識

HTML/PHP/MySQLの基礎知識

全部説明したかったのですが、すごい長くなってしまったので説明はだいぶ省きました。
コードを読んでてわからない事があっても
検索すれば山ほど出るような簡単な事しかしてないつもりなので(できないので)、調べてみてください。

作業の流れ

PDOを使ってPHPで簡易なLogin機能を作ります。

データベースの設定
新規登録・ログイン用フォーム画面の作成
新規登録機能
ログイン機能
ログアウト機能

データベースの設定

データベースの設定を行っていきます。
全く分からない人はこちら

データベース名:test_login

データベース管理ユーザーの設定を
名前:hogeUser
ホスト:localhost
パスワード:hogehoge

テーブル名:UserDeta
id int not null auto_increment primary key
email varchar(255) ,
email varchar(255) unique,
password varchar(255),

とします。hogeはプログラマーの間で適当に名前を付ける際に良く使う言葉らしい。
(追記)uniqueを指定することで、データベース側でEmailの重複を許可しない設定ができました。

config.php
<?php

//ini_set('display_errors', 1);

define('DSN', 'mysql:host=localhost;dbname=test_login');
define('DB_USER', 'hogeUser');
define('DB_PASS', 'hogehoge');

データベースに接続する際に必要な情報なので、ここでわかりやすいように定数に定義しておきます。
よくわからない人は後でも出てくるので、読み飛ばしてください。
display_errorsはデバッグに便利ですが、本番環境ではOffにする事をわすれないようにしましょう。

新規登録・ログイン用フォーム画面の作成

signUp.php
<?php

function h($s){
  return htmlspecialchars($s, ENT_QUOTES, 'utf-8');
}

session_start();
//ログイン済みの場合
if (isset($_SESSION['EMAIL'])) {
  echo 'ようこそ' .  h($_SESSION['EMAIL']) . "さん<br>";
  echo "<a href='/logout.php'>ログアウトはこちら。</a>";
  exit;
}

 ?>

<!DOCTYPE html>
<html lang="ja">
 <head>
   <meta charset="utf-8">
   <title>Login</title>
 </head>
 <body>
   <h1>ようこそ、ログインしてください。</h1>
   <form  action="login.php" method="post">
     <label for="email">email</label>
     <input type="email" name="email">
     <label for="password">password</label>
     <input type="password" name="password">
     <button type="submit">Sign In!</button>
   </form>
   <h1>初めての方はこちら</h1>
   <form action="signUp.php" method="post">
     <label for="email">email</label>
     <input type="email" name="email">email
     <label for="password">password</label>
     <input type="password" name="password">
     <button type="submit">Sign Up!</button>
     <p>※パスワードは半角英数字をそれぞれ1文字以上含んだ、8文字以上で設定してください。</p>
   </form>
 </body>
</html>

$_SESSION['EMAIL']はログイン機能の項目で設定するので、今はわからなくても大丈夫ですが
sessionとは何か?という方はこちら
今回はログイン状態をブラウザを閉じるか、ログアウトするまで保つために使っています。
htmlspecialなんじゃこらって人はXSS対策等で検索してください。とても大事なことです。

新規登録機能

signUp.php
<?php
require_once('config.php');
//データベースへ接続、テーブルがない場合は作成
try {
  $pdo = new PDO(DSN, DB_USER, DB_PASS);
  $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  $pdo->exec("create table if not exists userDeta(
      id int not null auto_increment primary key,
      email varchar(255),
      password varchar(255),
      created timestamp not null default current_timestamp
    )");
} catch (Exception $e) {
  echo $e->getMessage() . PHP_EOL;
}
//POSTのValidate。
if (!$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
  echo '入力された値が不正です。';
  return false;
}
//パスワードの正規表現
if (preg_match('/\A(?=.*?[a-z])(?=.*?\d)[a-z\d]{8,100}+\z/i', $_POST['password'])) {
  $password = password_hash($_POST['password'], PASSWORD_DEFAULT);
} else {
  echo 'パスワードは半角英数字をそれぞれ1文字以上含んだ8文字以上で設定してください。';
  return false;
}
//登録処理
try {
  $stmt = $pdo->prepare("insert into userDeta(email, password) value(?, ?)");
  $stmt->execute([$email, $password]);
  echo '登録完了';
} catch (\Exception $e) {
  echo '登録済みのメールアドレスです。';
}

/*
//DB内のメールアドレスを取得
$stmt = $pdo->prepare("select email from userDeta where email = ?");
$stmt->execute([$email]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
//DB内のメールアドレスと重複していない場合、登録する。
if (!isset($row['email'])) {
  $stmt = $pdo->prepare("insert into userDeta(email, password) value(?, ?)");
  $stmt->execute([$email, $password]);
  echo "登録完了";
} else {
  echo '既に登録されたメールアドレスです。';
  return false;
}
*/

ここで、最初に定義したDSNやらDB_USERが登場します。
こういう風に書けば接続されるんだなくらいで良いと思います。

私は正規表現というのが難解だなと思ってるんですが、emailアドレスを受け取った際にフィルタリングしてくれる便利な関数filter_varを見つけました。
パスワードはハッシュしてデータベースに保存するので問題無さそうですが
わかりやすすぎるパスワードを設定してもらっては困るよ!という事で半角英数字1文字以上含んだ8文字以上でお願いねと書いてます。
自信が無いため、古い記事ではありますが僕はこちらの記事のコードを引用させていただきました。
*書いた方が自分の古い記事を鵜呑みにしないよう注意喚起してます。よく検討して使ってください。(書いた方すいません。)

(追記)データベース側で重複のチェックができたため、コードを短くかけるようになりました。

ログイン機能

login.php
<?php

require_once('config.php');

session_start();
//POSTのvalidate
if (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
  echo '入力された値が不正です。';
  return false;
}
//DB内でPOSTされたメールアドレスを検索
try {
  $pdo = new PDO(DSN, DB_USER, DB_PASS);
  $stmt = $pdo->prepare('select * from userDeta where email = ?');
  $stmt->execute([$_POST['email']]);
  $row = $stmt->fetch(PDO::FETCH_ASSOC);
} catch (\Exception $e) {
  echo $e->getMessage() . PHP_EOL;
}
//emailがDB内に存在しているか確認
if (!isset($row['email'])) {
  echo 'メールアドレス又はパスワードが間違っています。';
  return false;
}
//パスワード確認後sessionにメールアドレスを渡す
if (password_verify($_POST['password'], $row['password'])) {
  session_regenerate_id(true); //session_idを新しく生成し、置き換える
  $_SESSION['EMAIL'] = $row['email'];
  echo 'ログインしました';
} else {
  echo 'メールアドレス又はパスワードが間違っています。';
  return false;
}

新規登録の時もそうですが、ユーザーから受け取った値をSQL文に入れる際には

$id = $_POST['id'];
$sql = "select * from users where id = " .$id;

等とすると、SQLインジェクションによる攻撃の的になるので、気を付けてください。

ここで最初に出てきた$_SESSION['EMAIL']に値を入れています。

ログアウト機能

logout.php
<?php
session_start();

if (isset($_SESSION["EMAIL"])) {
  echo 'Logoutしました。';
} else {
  echo 'SessionがTimeoutしました。';
}
//セッション変数のクリア
$_SESSION = array();
//セッションクッキーも削除
if (ini_get("session.use_cookies")) {
    $params = session_get_cookie_params();
    setcookie(session_name(), '', time() - 42000,
        $params["path"], $params["domain"],
        $params["secure"], $params["httponly"]
    );
}
//セッションクリア
@session_destroy();

公式マニュアル

参考になったサイト

クリーンなPHPコードを書くためのガイド
PHPでデータベースに接続するときのまとめ
Qiita書き方メモ
個人的にPHPで開発する上で頭に入れておきたいと思っている事
PSR-2 コーディングガイド

最後に

初めて記事を書いてみて、人に役立つ記事を書くのは難しい事を痛感しました。
初めて他人が自分のコードを見ることを意識して書いたので、私個人は新しく得るものが非常に多かった。
誰か一人でも役に立ったなと思ってくれれば幸い