12
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

Organization

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

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

by tetsunosukeito
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 
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
12
Help us understand the problem. What are the problem?