1<?php 2namespace GuzzleHttp\Tests\Psr7; 3 4use ReflectionProperty; 5use GuzzleHttp\Psr7\Stream; 6use GuzzleHttp\Psr7\UploadedFile; 7 8/** 9 * @covers GuzzleHttp\Psr7\UploadedFile 10 */ 11class UploadedFileTest extends BaseTest 12{ 13 private $cleanup; 14 15 protected function setUp() 16 { 17 $this->cleanup = []; 18 } 19 20 protected function tearDown() 21 { 22 foreach ($this->cleanup as $file) { 23 if (is_scalar($file) && file_exists($file)) { 24 unlink($file); 25 } 26 } 27 } 28 29 public function invalidStreams() 30 { 31 return [ 32 'null' => [null], 33 'true' => [true], 34 'false' => [false], 35 'int' => [1], 36 'float' => [1.1], 37 'array' => [['filename']], 38 'object' => [(object) ['filename']], 39 ]; 40 } 41 42 /** 43 * @dataProvider invalidStreams 44 */ 45 public function testRaisesExceptionOnInvalidStreamOrFile($streamOrFile) 46 { 47 $this->expectException('InvalidArgumentException'); 48 49 new UploadedFile($streamOrFile, 0, UPLOAD_ERR_OK); 50 } 51 52 public function invalidSizes() 53 { 54 return [ 55 'null' => [null], 56 'float' => [1.1], 57 'array' => [[1]], 58 'object' => [(object) [1]], 59 ]; 60 } 61 62 /** 63 * @dataProvider invalidSizes 64 */ 65 public function testRaisesExceptionOnInvalidSize($size) 66 { 67 $this->expectException('InvalidArgumentException', 'size'); 68 69 new UploadedFile(fopen('php://temp', 'wb+'), $size, UPLOAD_ERR_OK); 70 } 71 72 public function invalidErrorStatuses() 73 { 74 return [ 75 'null' => [null], 76 'true' => [true], 77 'false' => [false], 78 'float' => [1.1], 79 'string' => ['1'], 80 'array' => [[1]], 81 'object' => [(object) [1]], 82 'negative' => [-1], 83 'too-big' => [9], 84 ]; 85 } 86 87 /** 88 * @dataProvider invalidErrorStatuses 89 */ 90 public function testRaisesExceptionOnInvalidErrorStatus($status) 91 { 92 $this->expectException('InvalidArgumentException', 'status'); 93 94 new UploadedFile(fopen('php://temp', 'wb+'), 0, $status); 95 } 96 97 public function invalidFilenamesAndMediaTypes() 98 { 99 return [ 100 'true' => [true], 101 'false' => [false], 102 'int' => [1], 103 'float' => [1.1], 104 'array' => [['string']], 105 'object' => [(object) ['string']], 106 ]; 107 } 108 109 /** 110 * @dataProvider invalidFilenamesAndMediaTypes 111 */ 112 public function testRaisesExceptionOnInvalidClientFilename($filename) 113 { 114 $this->expectException('InvalidArgumentException', 'filename'); 115 116 new UploadedFile(fopen('php://temp', 'wb+'), 0, UPLOAD_ERR_OK, $filename); 117 } 118 119 /** 120 * @dataProvider invalidFilenamesAndMediaTypes 121 */ 122 public function testRaisesExceptionOnInvalidClientMediaType($mediaType) 123 { 124 $this->expectException('InvalidArgumentException', 'media type'); 125 126 new UploadedFile(fopen('php://temp', 'wb+'), 0, UPLOAD_ERR_OK, 'foobar.baz', $mediaType); 127 } 128 129 public function testGetStreamReturnsOriginalStreamObject() 130 { 131 $stream = new Stream(fopen('php://temp', 'r')); 132 $upload = new UploadedFile($stream, 0, UPLOAD_ERR_OK); 133 134 $this->assertSame($stream, $upload->getStream()); 135 } 136 137 public function testGetStreamReturnsWrappedPhpStream() 138 { 139 $stream = fopen('php://temp', 'wb+'); 140 $upload = new UploadedFile($stream, 0, UPLOAD_ERR_OK); 141 $uploadStream = $upload->getStream()->detach(); 142 143 $this->assertSame($stream, $uploadStream); 144 } 145 146 public function testGetStreamReturnsStreamForFile() 147 { 148 $this->cleanup[] = $stream = tempnam(sys_get_temp_dir(), 'stream_file'); 149 $upload = new UploadedFile($stream, 0, UPLOAD_ERR_OK); 150 $uploadStream = $upload->getStream(); 151 $r = new ReflectionProperty($uploadStream, 'filename'); 152 $r->setAccessible(true); 153 154 $this->assertSame($stream, $r->getValue($uploadStream)); 155 } 156 157 public function testSuccessful() 158 { 159 $stream = \GuzzleHttp\Psr7\stream_for('Foo bar!'); 160 $upload = new UploadedFile($stream, $stream->getSize(), UPLOAD_ERR_OK, 'filename.txt', 'text/plain'); 161 162 $this->assertEquals($stream->getSize(), $upload->getSize()); 163 $this->assertEquals('filename.txt', $upload->getClientFilename()); 164 $this->assertEquals('text/plain', $upload->getClientMediaType()); 165 166 $this->cleanup[] = $to = tempnam(sys_get_temp_dir(), 'successful'); 167 $upload->moveTo($to); 168 $this->assertFileExists($to); 169 $this->assertEquals($stream->__toString(), file_get_contents($to)); 170 } 171 172 public function invalidMovePaths() 173 { 174 return [ 175 'null' => [null], 176 'true' => [true], 177 'false' => [false], 178 'int' => [1], 179 'float' => [1.1], 180 'empty' => [''], 181 'array' => [['filename']], 182 'object' => [(object) ['filename']], 183 ]; 184 } 185 186 /** 187 * @dataProvider invalidMovePaths 188 */ 189 public function testMoveRaisesExceptionForInvalidPath($path) 190 { 191 $stream = \GuzzleHttp\Psr7\stream_for('Foo bar!'); 192 $upload = new UploadedFile($stream, 0, UPLOAD_ERR_OK); 193 194 $this->cleanup[] = $path; 195 196 $this->expectException('InvalidArgumentException', 'path'); 197 $upload->moveTo($path); 198 } 199 200 public function testMoveCannotBeCalledMoreThanOnce() 201 { 202 $stream = \GuzzleHttp\Psr7\stream_for('Foo bar!'); 203 $upload = new UploadedFile($stream, 0, UPLOAD_ERR_OK); 204 205 $this->cleanup[] = $to = tempnam(sys_get_temp_dir(), 'diac'); 206 $upload->moveTo($to); 207 $this->assertFileExists($to); 208 209 $this->expectException('RuntimeException', 'moved'); 210 $upload->moveTo($to); 211 } 212 213 public function testCannotRetrieveStreamAfterMove() 214 { 215 $stream = \GuzzleHttp\Psr7\stream_for('Foo bar!'); 216 $upload = new UploadedFile($stream, 0, UPLOAD_ERR_OK); 217 218 $this->cleanup[] = $to = tempnam(sys_get_temp_dir(), 'diac'); 219 $upload->moveTo($to); 220 $this->assertFileExists($to); 221 222 $this->expectException('RuntimeException', 'moved'); 223 $upload->getStream(); 224 } 225 226 public function nonOkErrorStatus() 227 { 228 return [ 229 'UPLOAD_ERR_INI_SIZE' => [ UPLOAD_ERR_INI_SIZE ], 230 'UPLOAD_ERR_FORM_SIZE' => [ UPLOAD_ERR_FORM_SIZE ], 231 'UPLOAD_ERR_PARTIAL' => [ UPLOAD_ERR_PARTIAL ], 232 'UPLOAD_ERR_NO_FILE' => [ UPLOAD_ERR_NO_FILE ], 233 'UPLOAD_ERR_NO_TMP_DIR' => [ UPLOAD_ERR_NO_TMP_DIR ], 234 'UPLOAD_ERR_CANT_WRITE' => [ UPLOAD_ERR_CANT_WRITE ], 235 'UPLOAD_ERR_EXTENSION' => [ UPLOAD_ERR_EXTENSION ], 236 ]; 237 } 238 239 /** 240 * @dataProvider nonOkErrorStatus 241 */ 242 public function testConstructorDoesNotRaiseExceptionForInvalidStreamWhenErrorStatusPresent($status) 243 { 244 $uploadedFile = new UploadedFile('not ok', 0, $status); 245 $this->assertSame($status, $uploadedFile->getError()); 246 } 247 248 /** 249 * @dataProvider nonOkErrorStatus 250 */ 251 public function testMoveToRaisesExceptionWhenErrorStatusPresent($status) 252 { 253 $uploadedFile = new UploadedFile('not ok', 0, $status); 254 $this->expectException('RuntimeException', 'upload error'); 255 $uploadedFile->moveTo(__DIR__ . '/' . sha1(uniqid('', true))); 256 } 257 258 /** 259 * @dataProvider nonOkErrorStatus 260 */ 261 public function testGetStreamRaisesExceptionWhenErrorStatusPresent($status) 262 { 263 $uploadedFile = new UploadedFile('not ok', 0, $status); 264 $this->expectException('RuntimeException', 'upload error'); 265 $uploadedFile->getStream(); 266 } 267 268 public function testMoveToCreatesStreamIfOnlyAFilenameWasProvided() 269 { 270 $this->cleanup[] = $from = tempnam(sys_get_temp_dir(), 'copy_from'); 271 $this->cleanup[] = $to = tempnam(sys_get_temp_dir(), 'copy_to'); 272 273 copy(__FILE__, $from); 274 275 $uploadedFile = new UploadedFile($from, 100, UPLOAD_ERR_OK, basename($from), 'text/plain'); 276 $uploadedFile->moveTo($to); 277 278 $this->assertFileEquals(__FILE__, $to); 279 } 280} 281