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

#自己紹介
プログラミング初心者
プログラマーに転職したいと思い勉強を始めた20代前半。
[ドットインストールさん](http://php.net/manual/ja/mysqlinfo.api.choosing.php)なんかの入門を流し見て、次に何をしようと迷える子羊になったので、そんな昨日の自分みたいな人に参考になればと思います

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

#最低限の知識

HTML/PHP/MySQLの基礎知識

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

#作業の流れ
[PDO](http://php.net/manual/ja/mysqlinfo.api.choosing.php)を使ってPHPで簡易なLogin機能を作ります。

①[データベースの設定](#データベースの設定)
②[新規登録・ログイン用フォーム画面の作成](#新規登録ログイン用フォーム画面の作成)
③[新規登録機能](#ログイン機能)
④[ログイン機能](#ログイン機能)
⑤[ログアウト機能](#ログアウト機能)

#データベースの設定

データベースの設定を行っていきます。
全く分からない人は[こちら](https://dotinstall.com/lessons/basic_mysql_v3)

データベース名：test_login

データベース管理ユーザーの設定を以下の様にします。

|名前     |値                 |
|----------|--------------------------|
|ユーザー名    |hogeUser                |
|ホスト  |localhost         |
|パスワード|hogehoge|
|テーブル名    |UserDeta                |

```
id int not null auto_increment primary key
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にする事をわすれないようにしましょう。

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

```index.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>※パスワードは半角英数字をそれぞれ１文字以上含んだ、８文字以上で設定してください。</p>
   </form>
 </body>
</html>

```
`$_SESSION['EMAIL']`はログイン機能の項目で設定するので、今はわからなくても大丈夫ですが
sessionとは何か？という方は[こちら](https://techacademy.jp/magazine/4970)
今回はログイン状態をブラウザを閉じるか、ログアウトするまで保つために使っています。
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) unique,
      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 '登録済みのメールアドレスです。';
}

```

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

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

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


#ログイン機能

```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;
}

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

#ログアウト機能

```logout.php
<?php
session_start();
$output = '';
if (isset($_SESSION["EMAIL"])) {
  $output = 'Logoutしました。';
} else {
  $output = '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();

echo $output;

```

[公式マニュアル](http://php.net/manual/ja/function.session-destroy.php)



#参考になったサイト
[クリーンなPHPコードを書くためのガイド](https://qiita.com/rana_kualu/items/87ff47935f544441237c)
[PHPでデータベースに接続するときのまとめ](https://qiita.com/mpyw/items/b00b72c5c95aac573b71)
[Qiita書き方メモ](https://qiita.com/hiroyuki_hon/items/f2a779bb295fd12646ab)
[個人的にPHPで開発する上で頭に入れておきたいと思っている事
](https://qiita.com/n_mogi/items/252dc4dcf75f3b2ea977)[PSR-2 コーディングガイド](http://www.infiniteloop.co.jp/docs/psr/psr-2-coding-style-guide.html)

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