phpでメールボックスをつくろう1

まず初期状態

<?php
session_start();
?>
<style>
h1 {
	display:inline;
}
#out {
/*	display: inline;*/
	float:right;
}
.flex-container {
  display: flex;
}
.flex-1{
  flex:2;
  height:400px;
  background-color:#f5f5f5;
}
.flex-2{
  flex:7;
}
</style>
<?php if(isset($_SESSION&#91;"username"&#93;)): ?>
<h1><?php echo $_SESSION&#91;"username"&#93;; ?>さんのメールボックス</h1>
<div id="out"><a href="logout.php">ログアウト</a></div>
<hr>
<div class="flex-container">
<div class="flex-1">
</div>
<div class="flex-2">
</div>
</div>
<?php else: header('Location: login.php'); ?>
<?php endif; ?>

sampleというユーザ名でログインした状態です。

メニューを作ります。
gmailやhotmailではメール新規作成などはgetパラメーターを使っているので、ここでは”?compose=new”とします。

<div class="flex-1">
<a href="?compose=new" class="square_btn">新規作成</a>
<ul>
		<li><a href="">Inbox</a></li>
		<li><a href="">Junk Email</a></li>
		<li><a href="">Sent Items</a></li>
		<li><a href="">Deleted Email</a></li>
</ul>
</div>

getパラメータでcomoseがnewの場合、フォームを表示させたいので、

<div class="flex-2">
   <?php if($_GET&#91;"compose"&#93; == 'new'): ?>
		<p>メール新規作成</p>
   <?php endif; ?>
</div>

うまく動いているようです。

ログインからログアウトまでの挙動

loginとregisterにそれぞれリンクをつけて、logoutはjavascriptのsettimeoutで1秒後に、loginにリダイレクト。

register.php

<h1>新規登録</h1>
<?php if($status == "ok"):?>
	<p>登録完了</p><br>
	<a href="login.php">ログインページ</a>
<?php elseif($status == "failed"): ?>
	<p>エラー:既に存在するユーザ名です。<br>
	ユーザ名(英数字3~32文字)、推測されにくいパスワード(英数字8~32文字)を入力してください。</p>
	<form method="POST" action="">
	ユーザ名:<input type="text" name="username">
	パスワード:<input type="password" name="password">
	<input type="submit" value="登録">
	</form>
	<a href="login.php">ログイン</a>
<?php elseif($status == "none"): ?>
	<p>ユーザ名(英数字3~32文字)、推測されにくいパスワード(英数字8~32文字)を入力してください。</p>
	<form method="POST" action="">
	ユーザ名:<input type="text" name="username">
	パスワード:<input type="password" name="password">
	<input type="submit" value="登録">
	</form>
	<a href="login.php">ログイン</a>

<h1>ログイン</h1>
    <?php if($status == "logged_in"):  header('Location: hoge.php');?>
    <?php elseif($status == "ok"): header('Location: hoge.php'); ?>
    <?php elseif($status == "failed"): ?>
      <p>ログイン失敗</p>
      <form method="POST" action="login.php">
        ユーザ名:<input type="text" name="username" />
        パスワード:<input type="password" name="password" />
        <input type="submit" value="ログイン" />
      </form>
      <a href="register.php">新規登録</a>
    <?php else: ?>
      <form method="POST" action="login.php">
        ユーザ名:<input type="text" name="username" />
        パスワード:<input type="password" name="password" />
        <input type="submit" value="ログイン" />
      </form>
      <a href="register.php">新規登録</a>
 	<?php endif; ?>

hoge.php

<?php
session_start();
?>
<?php if(isset($_SESSION&#91;"username"&#93;)): ?>
<p>コンテンツコンテンツコンテンツ</p>
<a href="logout.php">ログアウト</a>
<?php else: header('Location: login.php'); ?>
<?php endif; ?>

logout.php

<h1>ログアウト</h1>
<p>ログアウト完了しました<p>
<script>
setTimeout(function(){
	window.location.href = "login.php";
}, 1000);
</script>

以下のようにも書けますね。

<?php
session_start();
?>
<?php if(isset($_SESSION&#91;"username"&#93;)): ?>
<p><?php echo $_SESSION&#91;"username"&#93;; ?>さんの会員ページ</p>
<a href="logout.php">ログアウト</a>
<?php else: header('Location: login.php'); ?>
<?php endif; ?>

classでログイン機能

auth.php

<?php 

class Auth{
	const HOST_NAME = "localhost";
	const USER_NAME = "hoge";
	const PASSWORD ="hogehoge";
	const DATABASE_NAME = "mail";

	private $mysqli = null;

	function __construct(){
		$this->mysqli = new mysqli(
			self::HOST_NAME,
			self::USER_NAME,
			self::PASSWORD,
			self::DATABASE_NAME
		);
		session_start();
	}

	public function register($username, $password){
		$password = password_hash($_POST["password"], PASSWORD_DEFAULT);
		$stmt = $this->mysqli->prepare("INSERT INTO users VALUES(?, ?)");
		$stmt->bind_param('ss', $_POST["username"], $password);
		return $stmt->execute();
	}

	public function login($username, $password){
		$stmt = $this->mysqli->prepare(
			"SELECT password FROM users WHERE username = ?");
		$stmt->bind_param('s', $_POST["username"]);
		$stmt->execute();

		$stmt->store_result();
		if($stmt->num_rows == 1){
			$stmt->bind_result($hash);
			while ($stmt->fetch()){
				if(password_verify($_POST['password'], $hash)){
					$_SESSION["username"] = $_POST["username"];
					return true;
				}
			}
		}
		return false;
	}

	public function getUser(){
		if(isset($_SESSION["username"]))
			return $_SESSIOn["username"];
		return null;
	}

	public function logout(){
		$_SESSION = array();
		session_destroy();
	}
}

register.php

<?php

require_once("auth.php");
$auth = new Auth();

$status = "none";

if(!empty($_POST&#91;"username"&#93;) && !empty($_POST&#91;"password"&#93;)){

	if(!preg_match('/^&#91;0-9a-zA-Z&#93;{3,32}$/', $_POST&#91;"username"&#93;))
    		$status = "error_username";
  	else if(!preg_match('/^&#91;0-9a-zA-Z&#93;{8,32}$/', $_POST&#91;"password"&#93;))
    		$status = "error_password";
	elseif($auth->register($_POST["username"], $_POST["password"]))
			$status = "ok";
	else
			$status = "failed";	
	
}
?>
<head>
<script src="http://code.jquery.com/jquery-2.0.0.min.js"></script>
<script src="register_check.js"></script>
</head>
<h1>Jmail新規登録</h1>
<?php if($status == "ok"):?>
	<p>登録完了</p>
<?php elseif($status == "failed"): ?>
	<p>エラー:既に存在するユーザ名です。</p>
<?php elseif($status == "none"): ?>
	<p>ユーザ名(英数字3~32文字)、推測されにくいパスワード(英数字8~32文字)を入力してください。</p>
	<form method="POST" action="">
	ユーザ名:<input type="text" name="username">
	パスワード:<input type="password" name="password">
	<input type="submit" value="登録">
	</form>
<?php else: ?>
	<p>hogehoge</p>
<?php endif; ?>

login.php

<?php 

require_once("auth.php");
$auth = new Auth();

$status = "none";

if($auth->getUser())
	$status = "logged_in";
else if(!empty($_POST["username"]) && !empty($_POST["password"])){
	 if($auth->login($_POST["username"], $_POST["password"]))
        $status = "ok";
     else
        $status = "failed";
}
?>
<h1>ログイン</h1>
    <?php if($status == "logged_in"): ?>
      <p>ログイン済み</p>
    <?php elseif($status == "ok"): ?>
      <p>ログイン成功</p>
    <?php elseif($status == "failed"): ?>
      <p>ログイン失敗</p>
    <?php else: ?>
      <form method="POST" action="login.php">
        ユーザ名:<input type="text" name="username" />
        パスワード:<input type="password" name="password" />
        <input type="submit" value="ログイン" />
      </form>
 	<?php endif; ?>

logout.php

<?php 

require("auth.php");
$auth = new Auth();
$auth->logout();

?>

<h1>ログアウト</h1>
<p>完了<p>

すげー!

ログイン中のページ表示

セッションに値がある場合は、コンテンツを表示させ、それ以外は、ログインページにリダイレクトさせる。

session_start();

if(isset($_SESSION["username"])){
	echo "ログイン中です。";
} else {
	header('Location: login.php');
}

ふむふむ

session destroy もしくは nullの場合はログインページにリダイレクト

ログイン・サインインの基本機能はできたかな?

sign inに英数字のバリデーションを付与

id 英数字3~32文字、パスワード英数字8~32文字のバリデーション

<?php

require_once("auth.php");
$mysqli = connect_mysql();

$status = "none";

if(!empty($_POST&#91;"username"&#93;) && !empty($_POST&#91;"password"&#93;)){

	if(!preg_match('/^&#91;0-9a-zA-Z&#93;{3,32}$/', $_POST&#91;"username"&#93;))
    $status = "error_username";
  //パスワードのチェック
  else if(!preg_match('/^&#91;0-9a-zA-Z&#93;{8,32}$/', $_POST&#91;"password"&#93;))
    $status = "error_password";
	else{
		$password = password_hash($_POST&#91;"password"&#93;, PASSWORD_DEFAULT);
		$stmt = $mysqli->prepare("INSERT INTO users VALUES (?, ?)");
		$stmt->bind_param('ss', $_POST["username"], $password);

		if($stmt->execute())
			$status = "ok";
		else
			$status = "failed";	
		}
	}
?>
<head>
<script src="http://code.jquery.com/jquery-2.0.0.min.js"></script>
<script src="register_check.js"></script>
</head>
<h1>Jmail新規登録</h1>
<?php if($status == "ok"):?>
	<p>登録完了</p>
<?php elseif($status == "failed"): ?>
	<p>エラー:既に存在するユーザ名です。</p>
<?php elseif($status == "none"): ?>
	<p>ユーザ名(英数字3~32文字)、推測されにくいパスワード(英数字8~32文字)を入力してください。</p>
	<form method="POST" action="">
	ユーザ名:<input type="text" name="username">
	パスワード:<input type="password" name="password">
	<input type="submit" value="登録">
	</form>
<?php else: ?>
	<p>hogehoge</p>
<?php endif; ?>

register_check.js

$(function(){
  $("form").submit(function(){
    if(!$("input[name=username]").val().match(/^[0-9a-zA-Z]{3,32}$/)
    || !$("input[name=password]").val().match(/^[0-9a-zA-Z]{8,32}$/)){
      alert("入力エラー");
      return false;
    }
    return true;
  });
});

logout

<?php 

session_start();
$_SESSION = array();
session_destroy();

?>

<h1>ログアウト</h1>
<p>完了<p>

password_verifyが上手くいかないとき

ログイン機能をつくっていた際に、何故かpassword_verifyが上手くいかなかったが、

create tableする際に、passwordの桁数が少なかったのが原因。
passwordを255にして

create table mail.users(
	username varchar(41) unique,
	password varchar(255)
)

再度password_hashしたら、上手く機能しました。

<?php 

session_start();

$mysqli = new mysqli('localhost', 'hoge', 'hogehoge', 'mail');

$status = "none";

if(isset($_SESSION&#91;"username"&#93;))
	$status = "logged_in";
else if(!empty($_POST&#91;"username"&#93;) && !empty($_POST&#91;"password"&#93;)){
	 $stmt = $mysqli->prepare("SELECT password FROM users WHERE username = ?");
  $stmt->bind_param('s', $_POST["username"]);
  $stmt->execute();

$stmt->store_result();

  if($stmt->num_rows == 1){
    $stmt->bind_result($pass);
    while ($stmt->fetch()) {
      if(password_verify($_POST["password"], $pass)){
        $status = "ok";
        $_SESSION["username"] = $_POST["username"];
        break;
      }else{
        $status = "failed";
        break;
      }
    }
  }else
  	$status = "failed";
}
?>
<h1>ログイン</h1>
    <?php if($status == "logged_in"): ?>
      <p>ログイン済み</p>
    <?php elseif($status == "ok"): ?>
      <p>ログイン成功</p>
    <?php elseif($status == "failed"): ?>
      <p>ログイン失敗</p>
    <?php else: ?>
      <form method="POST" action="login.php">
        ユーザ名:<input type="text" name="username" />
        パスワード:<input type="password" name="password" />
        <input type="submit" value="ログイン" />
      </form>
    <?php endif; ?>

php ログイン登録

db table

create table mail.users(
	username varchar(41) unique,
	password varchar(41)
)

register.php

<?php

$mysqli = new mysqli('localhost', 'hoge', 'hogehoge', 'mail');

$status = "none";

if(!empty($_POST&#91;"username"&#93;) && !empty($_POST&#91;"password"&#93;)){
	$password = password_hash($_POST&#91;"password"&#93;, PASSWORD_DEFAULT);

	$stmt = $mysqli->prepare("INSERT INTO users VALUES (?, ?)");
	$stmt->bind_param('ss', $_POST["username"], $password);

	if($stmt->execute())
		$status = "ok";
	else
		$status = "failed";	
}
?>
<h1>新規登録</h1>
<?php if($status == "ok"):?>
	<p>登録完了</p>
<?php elseif($status == "failed"): ?>
	<p>エラー:既に存在するユーザ名です。</p>
<?php else: ?>
	<form method="POST" action="">
	ユーザ名:<input type="text" name="username">
	パスワード:<input type="password" name="password">
	<input type="submit" value="登録">
	</form>
<?php endif; ?>

db

なるほど。

フォルダがなければフォルダを作成する

mkdirでフォルダを作成、コマンドラインと同じです。

$dir = "file/test";
if(!file_exists($dir)){
	mkdir($dir, 0777);
}

出来てますね。

では、約4000個フォルダを作ってみます。

$time_start = microtime(true);

$dsn = "mysql:dbname=equity;host=localhost";
$user = "hoge";
$password = "hogehoge";
try {
    $dbh = new PDO($dsn, $user, $password);
} catch (PDOException $e){
    print('connection failed:'.$e->getMessage());
} 

$sql = "select code from lists";
$stmt = $dbh->query($sql);
while($result = $stmt->fetch(PDO::FETCH_ASSOC)){
		$code_list[] = $result["code"];
}

foreach($code_list as $value){

	$dir = "file/".$value."";
	if(!file_exists($dir)){
		mkdir($dir, 0777);
	}
}

$time = microtime(true) - $time_start;
echo "{$time}秒";

はや!

訪問者のアクセスログ(ip)を取得する

cookieのvalueはGAっぽくします。KeyはAccessLogLDとのことでALID。

$min = 100000000;
$max = 999999999;
$value = "GA1." .rand($min, $max).".".rand($min, $max);

$valueは以下のようになります。
GA1.835471614.729296551

テキストファイルに書き込むので、brではなく\nをつかいます。

$min = 100000000;
$max = 999999999;
$value = "GA1." .rand($min, $max).".".rand($min, $max);

if(empty($_COOKIE['ALID'])){
	$time=date("Y/m/d H:i:s");
	$ip = $_SERVER['REMOTE_ADDR'];
	$hostname = @gethostbyaddr($ip);
	$log = $time ." ".$ip." ".$hostname."\n";

	$fp =@fopen('log.txt','a') or die("ファイルを取得できません");
		if(flock($fp,LOCK_EX)){
			fwrite($fp,$log);
		}
		flock($fp, LOCK_UN);
setcookie("ALID",$value, time()+1);
}

log.txt
2018/03/18 19:21:55 192.168.33.1 192.168.33.1

以下のように書くと、最新のデータが一番上にきます。

$content = file_get_contents('log.txt');
	$fp =@fopen('log.txt','w') or die("ファイルを取得できません");
		if(flock($fp,LOCK_EX)){
			fwrite($fp,$log.$content);
		}
		flock($fp, LOCK_UN);

log.txtで取得するログの数を制限したい場合は、fgetsで1行ずつ読み込めばOKですね。

$limit = 10;

echo $content;
$min = 100000000;
$max = 999999999;
$value = "GA1." .rand($min, $max).".".rand($min, $max);

if(empty($_COOKIE['ALID'])){
	$time=date("Y/m/d H:i:s");
	$ip = $_SERVER['REMOTE_ADDR'];
	$hostname = @gethostbyaddr($ip);
	$log = $time ." ".$ip." ".$hostname."\n";

    $fp = fopen('log.txt', 'r');
	for($i=0;$i <$limit;$i++){
		$content .= fgets($fp);
	}
	fclose($fp);

	$fp =@fopen('log.txt','w') or die("ファイルを取得できません");
		if(flock($fp,LOCK_EX)){
			rewind($fp);
			fwrite($fp,$log.$content);
		}
		flock($fp, LOCK_UN);
setcookie("ALID",$value, time()+1);
}

2018/03/18 20:25:06 192.168.33.1 192.168.33.1
2018/03/18 20:25:00 192.168.33.1 192.168.33.1
2018/03/18 20:24:59 192.168.33.1 192.168.33.1
2018/03/18 20:24:57 192.168.33.1 192.168.33.1
2018/03/18 20:24:53 192.168.33.1 192.168.33.1
2018/03/18 20:24:28 192.168.33.1 192.168.33.1
2018/03/18 20:23:26 192.168.33.1 192.168.33.1
2018/03/18 20:23:18 192.168.33.1 192.168.33.1
2018/03/18 20:23:13 192.168.33.1 192.168.33.1
2018/03/18 20:22:14 192.168.33.1 192.168.33.1
2018/03/18 20:21:45 192.168.33.1 192.168.33.1