$_FILESの持ち回し

PHPアイキャッチ

今回ですが、スーパーグローバル変数の$_FILESについてです。
例えば掲示板で画像を投稿する時、長々と文章を書いて、画像も選択して、いざ投稿!・・・あれ?エラー。名前書き忘れた!
こういう場面では画像は消されてしまいます。
また、プロフィール編集画面でプロフィール画像を設定・・・なんて時もプレビュー画面がほしいという事があるかもしれません。

$_FILESは画面遷移後まで入力保持できません。しかし、やらなければいけない時があります。

hiddenを使ったやり方と、SESSIONを使ったやり方の2パターンありますが、今回はSESSIONを使った方法で私はやってみました。hiddenを使った方法は

https://www.systemexpress.co.jp/php/fileupload.html

上記を参考にしてください。


処理の流れとしては、

  1. ファイル投稿
  2. ファイルのパスを一時保管場所に保存
  3. そのパスをセッションに保存
  4. バリデーションがかかったらセッションにあるパスを表示(持ち回せる)
  5. データベースに登録する前にrename関数で本アップロードするディレクトリに移動させ、そのパスをデータベースに登録する

①ファイル投稿し、②ファイルのパスを一時保管場所に保存

// 画像処理(仮)
function uploadImgTemp($file, $key){
  
  if (isset($file['error']) && is_int($file['error'])) {
    try {
      switch ($file['error']) {
        case UPLOAD_ERR_OK:
        break;
        case UPLOAD_ERR_NO_FILE:
          throw new RuntimeException('ファイルが選択されていません');
          case UPLOAD_ERR_INI_SIZE:
            case UPLOAD_ERR_FORM_SIZE:
              throw new RuntimeException('ファイルサイズが大きすぎます');
              default:
              throw new RuntimeException('その他のエラーが発生しました');
      }
      
      $type = @exif_imagetype($file['tmp_name']);
      if (!in_array($type, [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG], true)) {
      }
      

      $path = 'tmp_uploads/'.sha1_file($file['tmp_name']).image_type_to_extension($type);
      if (!move_uploaded_file($file['tmp_name'], $path)) {
        throw new RuntimeException('ファイル保存時にエラーが発生しました');
      }

      chmod($path, 0644);
      
      return $path;
      
    } catch (RuntimeException $e) {
      
      global $err_msg;
      $err_msg[$key] = $e->getMessage();
      
    }
  }
}

上記のような関数を使って、$_FILESのパスを移動させて、それを取得させます。
この段階ではtmp_uploads/というディレクトリに入っています。

③そのパスをセッションに保存


投稿ボタンが押されたら、以下のようなコードでパスをセッションに保存します。

if(empty($_SESSION['pic1'])) {
  $_SESSION['pic1'] = ( !empty($_FILES['pic1']['name']) ) ? uploadImgTemp($_FILES['pic1'],'pic1') : '';
} else {
  if(!empty($_FILES['pic1']['name'])) {
    $_SESSION['pic1'] = uploadImgTemp($_FILES['pic1'],'pic1');
  }
}

これは、そもそもセッションが無い場合(初回のボタン押下か、画像を投稿していない場合)に、ファイルの投稿があったら先述の関数を使ってパスをセッションに格納しています。
画像投稿が無い場合は何も入りません。

そして、セッションにパスが入っている場合(他の入力項目がバリデーションにひっかかり、再度投稿する場合など)は再度画像がアップロードされているかをチェックし、再度アップロードされていたら、そのパスをまたセッションに上書きしています。

バリデーションがかかったらセッションにあるパスを表示(持ち回せる)

この処理は、フォームによります。通常の選択するフォームなら、フォームの下にセッションに入っているパスを表示したり、画像そのものを表示してもいいかもしれません。
画像プレビュー機能のついたフォームなら、セッションでその画像を引っ張ってこれます。
さらに、確認画面を付ける際は、画像確認ができます。

データベースに登録する前にrename関数で本アップロードするディレクトリに移動させ、そのパスをデータベースに登録する

最後ですが、現段階では仮のアップロードディレクトリに画像が入っている事になります。これを、本アップロードディレクトリに移動させて、そのパスをDBに登録したら完了となります。

最後のDBに入れるところで、以下の処理をします。

$pic1 = $_SESSION['pic1'];

function startsWith($str1, $str2) {
  $length = mb_strlen($str2);
  return (mb_substr($str1, 0, $length) === $str2);
}

if(startsWith($pic1, 'tmp_uploads')) {
  $kari1 = mb_substr($pic1, 12);
  $kari2 = 'uploads/' . $kari1;
  rename($pic1, $kari2);
  $pic1 = $kari2;
}

startsWithという関数(その文字列が指定した文字列から始まっているか判定する)はJavaとかにはあるようですが、 PHPにはないので、有名なユーザー定義関数を引っ張らせてもらいました。
tmp_uploadsで始まっているパスの場合、trueが返ってきます。それをif文で判定し、trueならば、「tmp_uploads/」を抜いたその後のパス名を取り出します。
そして、uploads/という本アップロード用ディレクトリにその取り出したパス名をくっつけて、それをrename関数を使って移動させます。
最後にそのパスをDBに登録すれば完成となります。