$ 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('/');
}
こんな。
ファイルのサイズを見てみると
$ 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)
{
// 略
}
}
一時ファイルの生成はわかったけど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