質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.81%

class内でmove_uploaded_file後、そのファイルパスを取得するには

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 437

arahito

score 18

前提・実現したいこと

https://qiita.com/mpyw/items/73ee77a9535cc65eff1e

発生している問題・エラーメッセージ

おそらくこの部分のどこかにmove_uploaded_fileが使われてファイルが保存されるはずなのですが、!$output(~の部分が何をやっているのか全くわかりません。
また、ハッシュをとってファイル名を名付けた場合、その後どのようにすればファイルパスを取得できるのか全くわかりません。
わからないことばかりで申し訳ないのですが、以下を画像アップロードのバリデーションクラスとして定義して使う場合、保存したファイルのファイルパスをphpファイルで呼び出したい場合、どのように呼び出せば使えるのでしょうか?

 // ファイルデータからSHA-1ハッシュを取ってファイル名を決定し、保存する
            if (!$output(
                $dst,
                sprintf('./resized/%s%s',
                    sha1_file($_FILES['upfile']['tmp_name'][$k]),
                    image_type_to_extension($info[2])
                )
            )) {
                throw new RuntimeException("[{$k}] ファイル保存時にエラーが発生しました");
            }

            $msgs[] = ['green', "[{$k}] リサイズして保存しました"];

        } catch (RuntimeException $e) {

            $msgs[] = ['red', $e->getMessage()];

        }

該当のソースコード

<?php

if (isset($_FILES['upfile']['error']) && is_array($_FILES['upfile']['error'])) {

    // 各ファイルをチェック
    foreach ($_FILES['upfile']['error'] as $k => $error) {

        try {

            // 更に配列がネストしていれば不正とする
            if (!is_int($error)) {
                throw new RuntimeException("[{$k}] パラメータが不正です");
            }

            // $_FILES['upfile']['error'][$k] の値を確認
            switch ($error) {
                case UPLOAD_ERR_OK: // OK
                    break;
                case UPLOAD_ERR_NO_FILE:   // ファイル未選択
                    continue 2;
                case UPLOAD_ERR_INI_SIZE:  // php.ini定義の最大サイズ超過
                case UPLOAD_ERR_FORM_SIZE: // フォーム定義の最大サイズ超過
                    throw new RuntimeException("[{$k}] ファイルサイズが大きすぎます");
                default:
                    throw new RuntimeException("[{$k}] その他のエラーが発生しました");
            }

            // $_FILES['upfile']['mime']の値はブラウザ側で偽装可能なので
            // MIMEタイプを自前でチェックする
            if (!$info = @getimagesize($_FILES['upfile']['tmp_name'][$k])) {
                throw new RuntimeException("[{$k}] 有効な画像ファイルを指定してください");
            }
            if (!in_array($info[2], [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG], true)) {
                throw new RuntimeException("[{$k}] 未対応の画像形式です");
            }

            // 画像処理に使う関数名を決定する
            $create = str_replace('/', 'createfrom', $info['mime']);
            $output = str_replace('/', '', $info['mime']);

            // 縦横比を維持したまま 120 * 120 以下に収まるサイズを求める
            if ($info[0] >= $info[1]) {
                $dst_w = 120;
                $dst_h = ceil(120 * $info[1] / max($info[0], 1));
            } else {
                $dst_w = ceil(120 * $info[0] / max($info[1], 1));
                $dst_h = 120;
            }

            // 元画像リソースを生成する
            if (!$src = @$create($_FILES['upfile']['tmp_name'][$k])) {
                throw new RuntimeException("[{$k}] 画像リソースの生成に失敗しました");
            }

            // リサンプリング先画像リソースを生成する
            $dst = imagecreatetruecolor($dst_w, $dst_h);

            // getimagesize関数で得られた情報も利用してリサンプリングを行う
            imagecopyresampled($dst, $src, 0, 0, 0, 0, $dst_w, $dst_h, $info[0], $info[1]);

            // ファイルデータからSHA-1ハッシュを取ってファイル名を決定し、保存する
            if (!$output(
                $dst,
                sprintf('./resized/%s%s',
                    sha1_file($_FILES['upfile']['tmp_name'][$k]),
                    image_type_to_extension($info[2])
                )
            )) {
                throw new RuntimeException("[{$k}] ファイル保存時にエラーが発生しました");
            }

            $msgs[] = ['green', "[{$k}] リサイズして保存しました"];

        } catch (RuntimeException $e) {

            $msgs[] = ['red', $e->getMessage()];

        }

        // リソースを解放
        if (isset($msg) && is_resource($img)) {
            imagedestroy($img);
        }
        if (isset($dst) && is_resource($dst)) {
            imagedestroy($dst);
        }

    }

}

試したこと

補足情報(FW/ツールのバージョンなど)

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+2

前提として、PHPは関数名などを変数にして呼び出すことが可能です。

<?php
function hoge(){
    echo "hoge";    
}

$test = "hoge";
$test();

それは、ユーザー定義関数であっても標準関数であっても同じです。
その基準で追っていくと

$output という変数名で実行する関数が定義されている

$output$output = str_replace('/', '', $info['mime']);で定義されている

$infoif (!$info = @getimagesize($_FILES['upfile']['tmp_name'][$k])) {で定義されている

getimagesize()で一時ファイルから情報を取得していてMIMEタイプであることが分かる

MIMEタイプを調べると例えばjpg画像であればimage/jpegが設定されることがわかる

ということはstr_replace('/', '', $info['mime']);によってimagejpegとなることが分かる

phpの標準関数imagejpeg() が使われることが分かる

という感じですね。
まあ、変数なのでvar_dump()でもprint_r()でもデバッグすれば何が入っているかは一目瞭然ですが、「なぜその情報が入るか」の追い方の参考にしてください。

あと、ご提示の記事は結構丁寧にコメントが書かれているので、どのような意図で処理が書かれたかはコメントと処理をあわせると理解しやすいと思います。


前提その2.

$_FILEで扱ってるのはサーバー上に送信されたファイル。
そもそもmove_uploaded_file()アップロードされたファイルを新しい位置に移動する機能なので、使う時点でサーバーにテンポラリファイルとしてアップロードがすんでいます。

同じ方が書いた記事にファイルアップロードの例外処理はこれぐらいしないと気が済まないというのがありますが、「move_uploaded_file じゃなくて rename じゃだめ?」という提案がなされていて、記事も一本出来上がっています。

よって、今回の質問提示のコードではmove_uploaded_file()ではなく、別の関数で画像を移動させていることになります。
imageX系の関数は 画像をブラウザあるいはファイルに出力する機能を持ちますので、例に挙げたimagejpeg()であれば画像 image(今回はテンポラリファイル) から JPEG ファイルを作成し、第2引数toの宛先に出力(移動)された、ということになります。


よって

画像アップロードのバリデーションクラスとして定義して使う場合

既に画像アップロードまで行ってしまっていることになりますので、このままバリデーションだけで使うのは難しいのではないでしょか。
どこまでを「バリデーション」とするかによりますが、ファイル生成はバリデーションを行った後のものと考えるのが通常なので、例えば、「ファイルが画像か」「サイズ超過していないか」「指定した画像拡張子か」のチェックまでを切り出してバリデーションとすることになるのではないでしょうか。

あとは画像をきちんと移動できるかどうかなので異常系に分類されると思います。


保存したファイルのファイルパスをphpファイルで呼び出したい場合

「保存した」なので「移動済み」でしょうか、それとも「テンポラリファイル」でしょうか。
それによって違います。移動済みなのであればハッシュしているとはいえシステム側から払い出したファイル名です。如何様にもできますよね。
それに「どこでどう使いたいか」によります。
前述の「バリデーションのため」であればわざわざパスを取り出す必要はあません。$_FILEはスーパーグローバル変数なのでPHPプログラムのどこからでも呼び出せます。

「ファイルが画像か」「サイズ超過していないか」「指定した画像拡張子か」くらいならtmp_nameからチェックできます(すでに記事のコードではそのように対応されてますよね)


提示の記事のコードはそれ1つで全て出来上がっているものなので、
そこから何とか切り出そうとすると整合性を取るのが難しくなると思います。
それなりに技術力と経験と発想、ロジックが必要になります。

まずは参考にとどめ、このコードのまま挙動確認・デバッグを行って身にしていってはどうでしょうか。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/04/11 15:53

    PHPで開発をするのならPHPマニュアルはどんな入門書籍やネットの記事よりも重要です。
    いわば仕様書なので。最も正しいことが書いてあります。公式マニュアルです。

    キャンセル

  • 2019/04/11 15:58

    公式ドキュメント重要ですよね。
    解決したい事柄をまだうまく言語化できなくて、PHPマニュアルの該当ページを参照するにまで至らないんですよね。。 コツコツ頑張ります!

    キャンセル

  • 2019/04/11 16:01

    まずは辞書と思って使ったらいいですよ。
    しっかり言語化する必要もなくて、文語過ぎない程度に「システムっぽい感じで」なるべくコードを書くように文章化すれば、キーワードも出てきやすいです。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.81%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る