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

# $ php artisan help @tetsunosuke

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

----
## はじめに

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

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

----
## ニーズとしては

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

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

----

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

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


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

```php
<?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](https://qiita-image-store.s3.amazonaws.com/0/13879/eaa3f87f-5b6a-c1b5-ab2d-80fc17de48d9.png)


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

```bash
$ 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がオキニの様子](https://qiita-image-store.s3.amazonaws.com/0/13879/4122362f-46d3-1677-09a0-ae9a192300fb.png)



----
## 一時ファイルの生成はわかったけど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 
```