PHP 画像のアップロード

#画像をアップロードし表示させる
【大まかな流れ】
◆フォームから画像を選択
◆画像ファイルかのチェック
◆DBではなくサーバーのimageディレクトリに画像を保存
◆DBにはファイル名保存
◆DBのファイル名を元に画像表示

##ファイル構造
```
test
 |-images (画像ファイル保存ディレクトリ)
 |-image.php（画像表示ファイル）
 |-upload.php（画像アップロード処理）
```

##画像ファイル送信フォーム

```upload.php
<h1>画像アップロード</h1>
<!--送信ボタンが押された場合-->
<?php if (isset($_POST['upload'])): ?>
    <p><?php echo $message; ?></p>
<?php else: ?>
    <form method="post" enctype="multipart/form-data">
        <p>アップロード画像</p>
        <input type="file" name="image">
        <button><input type="submit" name="upload" value="送信"></button>
    </form>
<?php endif;?>
```
・`送信`ボタンが押された場合の処理を記載
・アップロードされたファイルが画像ファイルではなかった場合はエラーメッセージ
・inputタグで`file`を指定

##

```upload.php
<?php
$dsn = "mysql:host=localhost; dbname=xxx; charset=utf8";
$username = "xxx";
$password = "xxx";
try {
	$dbh = new PDO($dsn, $username, $password);
} catch (PDOException $e) {
	echo $e->getMessage();
}
	if (isset($_POST['upload'])) {//送信ボタンが押された場合
		$image = uniqid(mt_rand(), true);//ファイル名をユニーク化
		$image .= '.' . substr(strrchr($_FILES['image']['name'], '.'), 1);//アップロードされたファイルの拡張子を取得
		$file = "images/$image";
        $sql = "INSERT INTO images(name) VALUES (:image)";
		$stmt = $dbh->prepare($sql);
		$stmt->bindValue(':image', $image, PDO::PARAM_STR);
		if (!empty($_FILES['image']['name'])) {//ファイルが選択されていれば$imageにファイル名を代入
			move_uploaded_file($_FILES['image']['tmp_name'], './images/' . $image);//imagesディレクトリにファイル保存
			if (exif_imagetype($file)) {//画像ファイルかのチェック
                $message = '画像をアップロードしました';
                $stmt->execute();
			} else {
				$message = '画像ファイルではありません';
			}
		}
	}
?>

<h1>画像アップロード</h1>
<!--送信ボタンが押された場合-->
<?php if (isset($_POST['upload'])): ?>
    <p><?php echo $message; ?></p>
    <p><a href="image.php">画像表示へ</a></p>
<?php else: ?>
    <form method="post" enctype="multipart/form-data">
        <p>アップロード画像</p>
        <input type="file" name="image">
        <button><input type="submit" name="upload" value="送信"></button>
    </form>
<?php endif;?>
```
**【保存している画像名について】**
ユーザーが上げた画像名が使われてしまう形になっていると下記の問題が発生する。
・悪意のあるユーザーに、サーバーに不具合が発生するような名前を設定される恐れがある
・長い名前を設定されていると保存できない
・保存できないファイル名がある（?.jpgなど）
`$image = uniqid(mt_rand(), true);`
画像ファイル名をサーバー側でユニーク化して保存

**【画像アップロードについて】**
画像ファイルかの確認を行わないと画像ファイル以外をアップロードした際も保存が完了され、空の画像が表示される
`$image .= '.' . substr(strrchr($_FILES['image']['name'], '.'), 1);`で拡張子を取得
`exif_imagetype`関数で画像ファイルかの確認
参考：[exif_imagetype](https://www.php.net/manual/ja/function.exif-imagetype.php)

**【画像の保存方法について】**
データベースに画像を保存してしまうと、ユーザーデータを取得する時に画像データが大きい為、データ取得の時間が長くなってしまう恐れがある。
`move_uploaded_file($_FILES['image']['tmp_name'], './images/' . $image);` でディレクトリに画像を保存
そのため下記手順で表示させる
1. サーバー内に画像を保存する為のディレクトリを作成（images）
2. 1のディレクトリに画像を保存、画像のファイル名をデータベースに保存（ユニーク化されたもの）
3. データベースの画像のファイル名を元に画像を表示させる

##画像表示
```image.php
<?php
$dsn = "mysql:host=localhost; dbname=xxx; charset=utf8";
$username = "xxx";
$password = "xxx";
$id = rand(1, 5);
try {
	$dbh = new PDO($dsn, $username, $password);
} catch (PDOException $e) {
	echo $e->getMessage();
}
	$sql = "SELECT * FROM images WHERE id = :id";
	$stmt = $dbh->prepare($sql);
	$stmt->bindValue(':id', $id);
	$stmt->execute();
	$image = $stmt->fetch();
?>

<h1>画像表示</h1>
<img src="images/<?php echo $image['name']; ?>" width="300" height="300">
<a href="upload.php">画像アップロード</a>
```

##参考
[アップロードファイルの情報を取得する：$_FILES](https://gray-code.com/php/get-the-data-of-uploaded-file/)
スーパーグローバル変数の`$_FILES`は5つの情報が格納されている