16
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

UploadedFile::fake() について調べてみた

Last updated at Posted at 2019-01-17
1 / 22

$ php artisan help @tetsunosuke

  • 会場スポンサーの中の人です
  • PHP歴:15年 / Laravel歴:2週間
  • 人事歴:9ヶ月半(いまここ)
    • 研修(ワークショップデザイン)
    • 採用(エンジニアをWe are hiring!)
    • イベント(会場貸してます!)

はじめに

リファレンスの HTTP Tests のところで、下記のような記述があり、ファイルを生成してそれをアップロードするテストが書けると知りました。

UploadedFile::fake()->image('avatar.jpg', $width, $height)
                    ->size(100);

ニーズとしては

  • アップロードサイズ制限でのエラー: upload_max_filesize 
  • HTTP POSTのサイズ上限: post_max_size

あたりが決まっている場合にテストできるよねということです。


...でもこれはphp.iniとか .htaccessの設定。

Apacheとかじゃないと動かないとかありそう


リファレンス通りにやってみる

<?php
namespace Tests\Feature;
    public function testAvatarUpload()
    {
        Storage::fake('avatars');
        // リファレンスから変更  w:200, h:100, size:1000(kB?) 
        $file = UploadedFile::fake()->image('avatar.jpg', 200, 100)->size(1000);

        $response = $this->json('POST', '/avatar', [
            'avatar' => $file,
        ]);
        // ...
    }

アップロードされた画像を見てみる

*Controller.php
    public function upload(Request $request)
    {
        // これにより tmp/upload/phpq2D07W のようなファイルができる
        $filename = $request->file->move('tmp/upload');
        return redirect('/');
    }

こんな。

確かに w:200, h:100。 image.png


ファイルのサイズを見てみると

$ wc -c < tmp/upload/phpq2D07W
    1055

あれ?


そもそも

真っ黒なファイルで縦横サイズを決めて、ファイルサイズを決めるなんて可能なのだろうか...?

(...メタデータに無駄なバイトを書き込んでおく?)


このファイルがどのようにFakeで生成されるか調べた


これっぽい

Illuminate/Http/Testing/File.php
    /**
     * Create a new fake image.
     * 略
     * @return \Illuminate\Http\Testing\File
     */
    public static function image($name, $width = 10, $height = 10)
    {
        // デフォルトで `$file = UploadedFile::fake()->image('avatar.jpg');` 
        // したときは 10x10 の画像だった。

        return (new FileFactory)->image($name, $width, $height);
    }


その他追っていくと

Illuminate/Http/Testing/FileFactory.php
    /**
     * Generate a dummy image of the given width and height.
     * 略
     */
    protected function generateImage($width, $height, $type)
    {
        return tap(tmpfile(), function ($temp) use ($width, $height, $type) {
            // ...
            // 確かに縦横ベースで画像データを作っている!
            $image = imagecreatetruecolor($width, $height);
            // ...ここでpngにするかjpegにするか変換している処理も
        });
    }

tap ってなんだ?!

tap(tmpfile(), function () {});

ヘルパー関数だ!

Illuminate\Support\helpers.php
if (! function_exists('tap')) {
    /**
     * Call the given Closure with the given value then return the value.
     * 略
     */
    function tap($value, $callback = null)
    {
        // 略
    }
}

Laravel作者はtapがオキニの様子


一時ファイルの生成はわかったけどsize() はどうなってんだ?


リクエストのHTTPヘッダー見てみる

app/Http/Controllers/HomeController.php
public function upload(Request $request)
{
    // header() 引数なしで呼ぶと全部
    // 引数あり、たとえば Content-typeを渡すと
    // application/x-www-form-urlencoded とかが取れる
    $headers = $request->header();
    var_dump($headers);
}

Content-Length もない...

array(6) {
  'host' =>
  array(1) {
    [0] =>
    string(14) "127.0.0.1:8000"
  }
  'user-agent' =>
  array(1) {
    [0] =>
    string(7) "Symfony" ← へーそうなんだ!!
  }
  'accept' =>
  array(1) {
    [0] =>
    string(63) "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
  }
  'accept-language' =>
  array(1) {
    [0] =>
    string(14) "en-us,en;q=0.5"
  }
  'accept-charset' =>
  array(1) {
    [0] =>
    string(30) "ISO-8859-1,utf-8;q=0.7,*;q=0.7"
  }
  'content-type' =>
  array(1) {
    [0] =>
    string(33) "application/x-www-form-urlencoded"
  }
}

アップロード側のデータを見てみる

tests/Feature/Upload.php
        $file = UploadedFile::fake()->image('avatar.jpg', 200, 100)->size(1000);
        var_dump($file);
class Illuminate\Http\Testing\File#296 (10) {
...
  public $sizeToReport => int(1024000)
...
}

ファイルのサイズは

$request->file('file') ->getSize() で取得できますね。

app/Http/Controllers/HomeController.php
public function upload(Request $request)
{
    // これでアップロードされたファイルサイズに応じた処理ができる
    // $_FILES['file']['size']相当    
    $size = $request->file('file')->getSize(); 
}

まとめ

  • フレームワークの中のコードを追っていくと納得できる!
  • php.ini まで面倒見てくれないこともあるようだ!
  • 今度はDuskの内部挙動を調べてみたい

余談:Qiitaのスライドモードムズい。

$ php artisan follow @tetsunosukeito 
16
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?