Completed
Push — master ( 301436...602946 )
by Ingo
17:26 queued 05:06
created

UploadTest   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 787
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
dl 0
loc 787
rs 9.7391
c 0
b 0
f 0
wmc 28
lcom 1
cbo 8

16 Methods

Rating   Name   Duplication   Size   Complexity  
A setUp() 0 5 1
A tearDown() 0 4 1
A testUpload() 0 56 2
A testAllowedFilesize() 0 66 2
A testPHPUploadErrors() 0 73 2
A testGetAllowedMaxFileSize() 0 53 1
B testAllowedSizeOnFileWithNoExtension() 0 28 2
B testUploadDoesNotAllowUnknownExtension() 0 28 2
B testUploadAcceptsAllowedExtension() 0 31 2
B testUploadDeniesNoExtensionFilesIfNoEmptyStringSetForValidatorExtensions() 0 28 2
A testUploadTarGzFileTwiceAppendsNumber() 0 68 2
A testUploadFileWithNoExtensionTwiceAppendsNumber() 0 56 2
A testReplaceFile() 0 57 2
B testReplaceFileWithLoadIntoFile() 0 79 2
B testDeleteResampledImagesOnUpload() 0 38 1
B testFileVersioningWithAnExistingFile() 0 97 2
1
<?php
2
3
use SilverStripe\ORM\Versioning\Versioned;
4
use SilverStripe\Assets\Upload;
5
use SilverStripe\Assets\File;
6
use SilverStripe\Assets\Upload_Validator;
7
use SilverStripe\Core\Config\Config;
8
use SilverStripe\Dev\SapphireTest;
9
use SilverStripe\Dev\TestOnly;
10
11
12
13
14
/**
15
 * @package framework
16
 * @subpackage tests
17
 */
18
class UploadTest extends SapphireTest {
19
20
	protected $usesDatabase = true;
21
22
	public function setUp() {
23
		parent::setUp();
24
		Versioned::set_stage(Versioned::DRAFT);
25
		AssetStoreTest_SpyStore::activate('UploadTest');
26
	}
27
28
	public function tearDown() {
29
		AssetStoreTest_SpyStore::reset();
30
		parent::tearDown();
31
	}
32
33
	public function testUpload() {
34
		// create tmp file
35
		$tmpFileName = 'UploadTest-testUpload.txt';
36
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
37
		$tmpFileContent = '';
38
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
39
		file_put_contents($tmpFilePath, $tmpFileContent);
40
41
		// emulates the $_FILES array
42
		$tmpFile = array(
43
			'name' => $tmpFileName,
44
			'type' => 'text/plaintext',
45
			'size' => filesize($tmpFilePath),
46
			'tmp_name' => $tmpFilePath,
47
			'extension' => 'txt',
48
			'error' => UPLOAD_ERR_OK,
49
		);
50
51
		$v = new UploadTest_Validator();
52
53
		// test upload into default folder
54
		$u1 = new Upload();
55
		$u1->setValidator($v);
56
		$u1->loadIntoFile($tmpFile);
57
		$file1 = $u1->getFile();
58
		$this->assertEquals(
59
			'Uploads/UploadTest-testUpload.txt',
60
			$file1->getFilename()
61
		);
62
		$this->assertEquals(
63
			BASE_PATH . '/assets/UploadTest/.protected/Uploads/315ae4c3d4/UploadTest-testUpload.txt',
64
			AssetStoreTest_SpyStore::getLocalPath($file1)
65
		);
66
		$this->assertFileExists(
67
			AssetStoreTest_SpyStore::getLocalPath($file1),
68
			'File upload to standard directory in /assets'
69
		);
70
71
		// test upload into custom folder
72
		$customFolder = 'UploadTest-testUpload';
73
		$u2 = new Upload();
74
		$u2->loadIntoFile($tmpFile, null, $customFolder);
75
		$file2 = $u2->getFile();
76
		$this->assertEquals(
77
			'UploadTest-testUpload/UploadTest-testUpload.txt',
78
			$file2->getFilename()
79
		);
80
		$this->assertEquals(
81
			BASE_PATH . '/assets/UploadTest/.protected/UploadTest-testUpload/315ae4c3d4/UploadTest-testUpload.txt',
82
			AssetStoreTest_SpyStore::getLocalPath($file2)
83
		);
84
		$this->assertFileExists(
85
			AssetStoreTest_SpyStore::getLocalPath($file2),
86
			'File upload to custom directory in /assets'
87
		);
88
	}
89
90
	public function testAllowedFilesize() {
91
		// create tmp file
92
		$tmpFileName = 'UploadTest-testUpload.txt';
93
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
94
		$tmpFileContent = '';
95
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
96
		file_put_contents($tmpFilePath, $tmpFileContent);
97
98
		// emulates the $_FILES array
99
		$tmpFile = array(
100
			'name' => $tmpFileName,
101
			'type' => 'text/plaintext',
102
			'size' => filesize($tmpFilePath),
103
			'tmp_name' => $tmpFilePath,
104
			'extension' => 'txt',
105
			'error' => UPLOAD_ERR_OK,
106
		);
107
108
		// test upload into default folder
109
		$u1 = new Upload();
110
		$v = new UploadTest_Validator();
111
112
		$v->setAllowedMaxFileSize(array('txt' => 10));
113
		$u1->setValidator($v);
114
		$result = $u1->loadIntoFile($tmpFile);
115
		$this->assertFalse($result, 'Load failed because size was too big');
116
117
		$v->setAllowedMaxFileSize(array('[document]' => 10));
118
		$u1->setValidator($v);
119
		$result = $u1->loadIntoFile($tmpFile);
120
		$this->assertFalse($result, 'Load failed because size was too big');
121
122
		$v->setAllowedMaxFileSize(array('txt' => 200000));
123
		$u1->setValidator($v);
124
		$result = $u1->loadIntoFile($tmpFile);
125
		$this->assertTrue($result, 'Load failed with setting max file size');
126
127
		// check max file size set by app category
128
		$tmpFileName = 'UploadTest-testUpload.jpg';
129
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
130
		file_put_contents($tmpFilePath, $tmpFileContent . $tmpFileContent);
131
132
		$tmpFile = array(
133
			'name' => $tmpFileName,
134
			'type' => 'image/jpeg',
135
			'size' => filesize($tmpFilePath),
136
			'tmp_name' => $tmpFilePath,
137
			'extension' => 'jpg',
138
			'error' => UPLOAD_ERR_OK,
139
		);
140
141
		$v->setAllowedMaxFileSize(array('[image]' => '40k'));
142
		$u1->setValidator($v);
143
		$result = $u1->loadIntoFile($tmpFile);
144
		$this->assertTrue($result, 'Load failed with setting max file size');
145
146
		$v->setAllowedMaxFileSize(array('[image]' => '1k'));
147
		$u1->setValidator($v);
148
		$result = $u1->loadIntoFile($tmpFile);
149
		$this->assertFalse($result, 'Load failed because size was too big');
150
151
		$v->setAllowedMaxFileSize(array('[image]' => 1000));
152
		$u1->setValidator($v);
153
		$result = $u1->loadIntoFile($tmpFile);
154
		$this->assertFalse($result, 'Load failed because size was too big');
155
	}
156
157
	public function testPHPUploadErrors() {
158
		$configMaxFileSizes = ['*' => '1k'];
159
		Config::inst()->update(
160
			'SilverStripe\\Assets\\Upload_Validator',
161
			'default_max_file_size',
162
			$configMaxFileSizes
163
		);
164
		// create tmp file
165
		$tmpFileName = 'myfile.jpg';
166
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
167
		$tmpFileContent = '';
168
		for($i=0; $i<100; $i++) $tmpFileContent .= '0';
169
		file_put_contents($tmpFilePath, $tmpFileContent);
170
171
		// Build file
172
		$upload = new Upload();
173
		$tmpFile = array(
174
			'name' => $tmpFileName,
175
			'type' => '',
176
			'tmp_name' => $tmpFilePath,
177
			'size' => filesize($tmpFilePath),
178
			'error' => UPLOAD_ERR_OK,
179
		);
180
181
		// Test ok
182
		$this->assertTrue($upload->validate($tmpFile));
183
184
		// Test zero size file
185
		$upload->clearErrors();
186
		$tmpFile['size'] = 0;
187
		$this->assertFalse($upload->validate($tmpFile));
188
		$this->assertContains(
189
			_t('File.NOFILESIZE', 'Filesize is zero bytes.'),
190
			$upload->getErrors()
191
		);
192
193
		// Test file too large
194
		$upload->clearErrors();
195
		$tmpFile['error'] = UPLOAD_ERR_INI_SIZE;
196
		$this->assertFalse($upload->validate($tmpFile));
197
		$this->assertContains(
198
			_t(
199
				'File.TOOLARGE',
200
				'Filesize is too large, maximum {size} allowed',
201
				'Argument 1: Filesize (e.g. 1MB)',
202
				array('size' => '1 KB')
203
			),
204
			$upload->getErrors()
205
		);
206
207
		// Test form size
208
		$upload->clearErrors();
209
		$tmpFile['error'] = UPLOAD_ERR_FORM_SIZE;
210
		$this->assertFalse($upload->validate($tmpFile));
211
		$this->assertContains(
212
			_t(
213
				'File.TOOLARGE',
214
				'Filesize is too large, maximum {size} allowed',
215
				'Argument 1: Filesize (e.g. 1MB)',
216
				array('size' => '1 KB')
217
			),
218
			$upload->getErrors()
219
		);
220
221
		// Test no file
222
		$upload->clearErrors();
223
		$tmpFile['error'] = UPLOAD_ERR_NO_FILE;
224
		$this->assertFalse($upload->validate($tmpFile));
225
		$this->assertContains(
226
			_t('File.NOVALIDUPLOAD', 'File is not a valid upload'),
227
			$upload->getErrors()
228
		);
229
	}
230
231
	public function testGetAllowedMaxFileSize() {
232
		Config::nest();
233
234
		// Check the max file size uses the config values
235
		$configMaxFileSizes = array(
236
			'[image]' => '1k',
237
			'txt' => 1000
238
		);
239
		Config::inst()->update('SilverStripe\\Assets\\Upload_Validator', 'default_max_file_size', $configMaxFileSizes);
240
		$v = new UploadTest_Validator();
241
242
		$retrievedSize = $v->getAllowedMaxFileSize('[image]');
243
		$this->assertEquals(1024, $retrievedSize, 'Max file size check on default values failed (config category set check)');
244
245
		$retrievedSize = $v->getAllowedMaxFileSize('txt');
246
		$this->assertEquals(1000, $retrievedSize, 'Max file size check on default values failed (config extension set check)');
247
248
		// Check instance values for max file size
249
		$maxFileSizes = array(
250
			'[document]' => 2000,
251
			'txt' => '4k'
252
		);
253
		$v = new UploadTest_Validator();
254
		$v->setAllowedMaxFileSize($maxFileSizes);
255
256
		$retrievedSize = $v->getAllowedMaxFileSize('[document]');
257
		$this->assertEquals(2000, $retrievedSize, 'Max file size check on instance values failed (instance category set check)');
258
259
		// Check that the instance values overwrote the default values
260
		// ie. The max file size will not exist for [image]
261
		$retrievedSize = $v->getAllowedMaxFileSize('[image]');
262
		$this->assertFalse($retrievedSize, 'Max file size check on instance values failed (config overridden check)');
263
264
		// Check a category that has not been set before
265
		$retrievedSize = $v->getAllowedMaxFileSize('[archive]');
266
		$this->assertFalse($retrievedSize, 'Max file size check on instance values failed (category not set check)');
267
268
		// Check a file extension that has not been set before
269
		$retrievedSize = $v->getAllowedMaxFileSize('mp3');
270
		$this->assertFalse($retrievedSize, 'Max file size check on instance values failed (extension not set check)');
271
272
		$retrievedSize = $v->getAllowedMaxFileSize('txt');
273
		$this->assertEquals(4096, $retrievedSize, 'Max file size check on instance values failed (instance extension set check)');
274
275
		// Check a wildcard max file size against a file with an extension
276
		$v = new UploadTest_Validator();
277
		$v->setAllowedMaxFileSize(2000);
278
279
		$retrievedSize = $v->getAllowedMaxFileSize('.jpg');
280
		$this->assertEquals(2000, $retrievedSize, 'Max file size check on instance values failed (wildcard max file size)');
281
282
		Config::unnest();
283
	}
284
285
	public function testAllowedSizeOnFileWithNoExtension() {
286
		// create tmp file
287
		$tmpFileName = 'UploadTest-testUpload';
288
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
289
		$tmpFileContent = '';
290
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
291
		file_put_contents($tmpFilePath, $tmpFileContent);
292
293
		// emulates the $_FILES array
294
		$tmpFile = array(
295
			'name' => $tmpFileName,
296
			'type' => 'text/plaintext',
297
			'size' => filesize($tmpFilePath),
298
			'tmp_name' => $tmpFilePath,
299
			'extension' => '',
300
			'error' => UPLOAD_ERR_OK,
301
		);
302
303
		$v = new UploadTest_Validator();
304
		$v->setAllowedMaxFileSize(array('' => 10));
305
306
		// test upload into default folder
307
		$u1 = new Upload();
308
		$u1->setValidator($v);
309
		$result = $u1->loadIntoFile($tmpFile);
310
311
		$this->assertFalse($result, 'Load failed because size was too big');
312
	}
313
314
	public function testUploadDoesNotAllowUnknownExtension() {
315
		// create tmp file
316
		$tmpFileName = 'UploadTest-testUpload.php';
317
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
318
		$tmpFileContent = '';
319
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
320
		file_put_contents($tmpFilePath, $tmpFileContent);
321
322
		// emulates the $_FILES array
323
		$tmpFile = array(
324
			'name' => $tmpFileName,
325
			'type' => 'text/plaintext',
326
			'size' => filesize($tmpFilePath),
327
			'tmp_name' => $tmpFilePath,
328
			'extension' => 'php',
329
			'error' => UPLOAD_ERR_OK,
330
		);
331
332
		$v = new UploadTest_Validator();
333
		$v->setAllowedExtensions(array('txt'));
334
335
		// test upload into default folder
336
		$u = new Upload();
337
		$u->setValidator($v);
338
		$result = $u->loadIntoFile($tmpFile);
339
340
		$this->assertFalse($result, 'Load failed because extension was not accepted');
341
	}
342
343
	public function testUploadAcceptsAllowedExtension() {
344
		// create tmp file
345
		$tmpFileName = 'UploadTest-testUpload.txt';
346
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
347
		$tmpFileContent = '';
348
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
349
		file_put_contents($tmpFilePath, $tmpFileContent);
350
351
		// emulates the $_FILES array
352
		$tmpFile = array(
353
			'name' => $tmpFileName,
354
			'type' => 'text/plaintext',
355
			'size' => filesize($tmpFilePath),
356
			'tmp_name' => $tmpFilePath,
357
			'extension' => 'txt',
358
			'error' => UPLOAD_ERR_OK,
359
		);
360
361
		$v = new UploadTest_Validator();
362
		$v->setAllowedExtensions(array('txt'));
363
364
		// test upload into default folder
365
		$u = new Upload();
366
		$u->setValidator($v);
367
		$u->loadIntoFile($tmpFile);
368
		$file = $u->getFile();
369
		$this->assertFileExists(
370
			AssetStoreTest_SpyStore::getLocalPath($file),
371
			'File upload to custom directory in /assets'
372
		);
373
	}
374
375
	public function testUploadDeniesNoExtensionFilesIfNoEmptyStringSetForValidatorExtensions() {
376
		// create tmp file
377
		$tmpFileName = 'UploadTest-testUpload';
378
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
379
		$tmpFileContent = '';
380
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
381
		file_put_contents($tmpFilePath, $tmpFileContent);
382
383
		// emulates the $_FILES array
384
		$tmpFile = array(
385
			'name' => $tmpFileName,
386
			'type' => 'text/plaintext',
387
			'size' => filesize($tmpFilePath),
388
			'tmp_name' => $tmpFilePath,
389
			'extension' => '',
390
			'error' => UPLOAD_ERR_OK,
391
		);
392
393
		$v = new UploadTest_Validator();
394
		$v->setAllowedExtensions(array('txt'));
395
396
		// test upload into default folder
397
		$u = new Upload();
398
		$result = $u->loadIntoFile($tmpFile);
399
400
		$this->assertFalse($result, 'Load failed because extension was not accepted');
401
		$this->assertEquals(1, count($u->getErrors()), 'There is a single error of the file extension');
402
	}
403
404
	public function testUploadTarGzFileTwiceAppendsNumber() {
405
		// create tmp file
406
		$tmpFileName = 'UploadTest-testUpload.tar.gz';
407
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
408
		$tmpFileContent = '';
409
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
410
		file_put_contents($tmpFilePath, $tmpFileContent);
411
412
		// emulates the $_FILES array
413
		$tmpFile = array(
414
			'name' => $tmpFileName,
415
			'type' => 'text/plaintext',
416
			'size' => filesize($tmpFilePath),
417
			'tmp_name' => $tmpFilePath,
418
			'extension' => 'tar.gz',
419
			'error' => UPLOAD_ERR_OK,
420
		);
421
422
		// test upload into default folder
423
		$u = new Upload();
424
		$u->loadIntoFile($tmpFile);
425
		$file = $u->getFile();
426
		$this->assertEquals(
427
			'UploadTest-testUpload.tar.gz',
428
			$file->Name,
0 ignored issues
show
Bug introduced by
Accessing Name on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
429
			'File has a name without a number because it\'s not a duplicate'
430
		);
431
		$this->assertFileExists(
432
			AssetStoreTest_SpyStore::getLocalPath($file),
433
			'File exists'
434
		);
435
436
		$u = new Upload();
437
		$u->loadIntoFile($tmpFile);
438
		$file2 = $u->getFile();
439
		$this->assertEquals(
440
			'UploadTest-testUpload-v2.tar.gz',
441
			$file2->Name,
0 ignored issues
show
Bug introduced by
Accessing Name on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
442
			'File receives a number attached to the end before the extension'
443
		);
444
		$this->assertFileExists(
445
			AssetStoreTest_SpyStore::getLocalPath($file2),
446
			'File exists'
447
		);
448
		$this->assertGreaterThan(
449
			$file->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
450
			$file2->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
451
			'File database record is not the same'
452
		);
453
454
		$u = new Upload();
455
		$u->loadIntoFile($tmpFile);
456
		$file3 = $u->getFile();
457
		$this->assertEquals(
458
			'UploadTest-testUpload-v3.tar.gz',
459
			$file3->Name,
0 ignored issues
show
Bug introduced by
Accessing Name on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
460
			'File receives a number attached to the end before the extension'
461
		);
462
		$this->assertFileExists(
463
			AssetStoreTest_SpyStore::getLocalPath($file3),
464
			'File exists'
465
		);
466
		$this->assertGreaterThan(
467
			$file2->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
468
			$file3->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
469
			'File database record is not the same'
470
		);
471
	}
472
473
	public function testUploadFileWithNoExtensionTwiceAppendsNumber() {
474
		// create tmp file
475
		$tmpFileName = 'UploadTest-testUpload';
476
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
477
		$tmpFileContent = '';
478
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
479
		file_put_contents($tmpFilePath, $tmpFileContent);
480
481
		// emulates the $_FILES array
482
		$tmpFile = array(
483
			'name' => $tmpFileName,
484
			'type' => 'text/plaintext',
485
			'size' => filesize($tmpFilePath),
486
			'tmp_name' => $tmpFilePath,
487
			'extension' => 'txt',
488
			'error' => UPLOAD_ERR_OK,
489
		);
490
491
		$v = new UploadTest_Validator();
492
		$v->setAllowedExtensions(array(''));
493
494
		// test upload into default folder
495
		$u = new Upload();
496
		$u->setValidator($v);
497
		$u->loadIntoFile($tmpFile);
498
		$file = $u->getFile();
499
500
		$this->assertEquals(
501
			'UploadTest-testUpload',
502
			$file->Name,
0 ignored issues
show
Bug introduced by
Accessing Name on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
503
			'File is uploaded without extension'
504
		);
505
		$this->assertFileExists(
506
			AssetStoreTest_SpyStore::getLocalPath($file),
507
			'File exists'
508
		);
509
510
		$u = new Upload();
511
		$u->setValidator($v);
512
		$u->loadIntoFile($tmpFile);
513
		$file2 = $u->getFile();
514
		$this->assertEquals(
515
			'UploadTest-testUpload-v2',
516
			$file2->Name,
0 ignored issues
show
Bug introduced by
Accessing Name on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
517
			'File receives a number attached to the end'
518
		);
519
		$this->assertFileExists(
520
			AssetStoreTest_SpyStore::getLocalPath($file2),
521
			'File exists'
522
		);
523
		$this->assertGreaterThan(
524
			$file->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
525
			$file2->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
526
			'File database record is not the same'
527
		);
528
	}
529
530
	public function testReplaceFile() {
531
		// create tmp file
532
		$tmpFileName = 'UploadTest-testUpload';
533
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
534
		$tmpFileContent = '';
535
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
536
		file_put_contents($tmpFilePath, $tmpFileContent);
537
538
		// emulates the $_FILES array
539
		$tmpFile = array(
540
			'name' => $tmpFileName,
541
			'type' => 'text/plaintext',
542
			'size' => filesize($tmpFilePath),
543
			'tmp_name' => $tmpFilePath,
544
			'extension' => 'txt',
545
			'error' => UPLOAD_ERR_OK,
546
		);
547
548
		$v = new UploadTest_Validator();
549
		$v->setAllowedExtensions(array(''));
550
551
		// test upload into default folder
552
		$u = new Upload();
553
		$u->setValidator($v);
554
		$u->loadIntoFile($tmpFile);
555
		$file = $u->getFile();
556
557
		$this->assertEquals(
558
			'UploadTest-testUpload',
559
			$file->Name,
0 ignored issues
show
Bug introduced by
Accessing Name on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
560
			'File is uploaded without extension'
561
		);
562
		$this->assertFileExists(
563
			AssetStoreTest_SpyStore::getLocalPath($file),
564
			'File exists'
565
		);
566
567
		$u = new Upload();
568
		$u->setValidator($v);
569
		$u->setReplaceFile(true);
570
		$u->loadIntoFile($tmpFile);
571
		$file2 = $u->getFile();
572
		$this->assertEquals(
573
			'UploadTest-testUpload',
574
			$file2->Name,
0 ignored issues
show
Bug introduced by
Accessing Name on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
575
			'File does not receive new name'
576
		);
577
		$this->assertFileExists(
578
			AssetStoreTest_SpyStore::getLocalPath($file2),
579
			'File exists'
580
		);
581
		$this->assertEquals(
582
			$file->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
583
			$file2->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
584
			'File database record is the same'
585
		);
586
	}
587
588
	public function testReplaceFileWithLoadIntoFile() {
589
		// create tmp file
590
		$tmpFileName = 'UploadTest-testUpload.txt';
591
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
592
		$tmpFileContent = '';
593
		for ($i = 0; $i < 10000; $i++)
594
			$tmpFileContent .= '0';
595
		file_put_contents($tmpFilePath, $tmpFileContent);
596
597
		// emulates the $_FILES array
598
		$tmpFile = array(
599
			'name' => $tmpFileName,
600
			'type' => 'text/plaintext',
601
			'size' => filesize($tmpFilePath),
602
			'tmp_name' => $tmpFilePath,
603
			'extension' => 'txt',
604
			'error' => UPLOAD_ERR_OK,
605
		);
606
607
		$v = new UploadTest_Validator();
608
609
		// test upload into default folder
610
		$u = new Upload();
611
		$u->setValidator($v);
612
		$u->loadIntoFile($tmpFile);
613
		$file = $u->getFile();
614
615
		$this->assertEquals(
616
			'UploadTest-testUpload.txt',
617
			$file->Name,
0 ignored issues
show
Bug introduced by
Accessing Name on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
618
			'File is uploaded without extension'
619
		);
620
		$this->assertFileExists(
621
			AssetStoreTest_SpyStore::getLocalPath($file),
622
			'File exists'
623
		);
624
625
		// replace=true
626
		$u = new Upload();
627
		$u->setValidator($v);
628
		$u->setReplaceFile(true);
629
		$u->loadIntoFile($tmpFile, new File());
630
		$file2 = $u->getFile();
631
		$this->assertEquals(
632
			'UploadTest-testUpload.txt',
633
			$file2->Name,
0 ignored issues
show
Bug introduced by
Accessing Name on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
634
			'File does not receive new name'
635
		);
636
		$this->assertFileExists(
637
			AssetStoreTest_SpyStore::getLocalPath($file2),
638
			'File exists'
639
		);
640
		$this->assertEquals(
641
			$file->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
642
			$file2->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
643
			'File database record is the same'
644
		);
645
646
		// replace=false
647
		$u = new Upload();
648
		$u->setValidator($v);
649
		$u->setReplaceFile(false);
650
		$u->loadIntoFile($tmpFile, new File());
651
		$file3 = $u->getFile();
652
		$this->assertEquals(
653
			'UploadTest-testUpload-v2.txt',
654
			$file3->Name,
0 ignored issues
show
Bug introduced by
Accessing Name on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
655
			'File does receive new name'
656
		);
657
		$this->assertFileExists(
658
			AssetStoreTest_SpyStore::getLocalPath($file3),
659
			'File exists'
660
		);
661
		$this->assertGreaterThan(
662
			$file2->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
663
			$file3->ID,
0 ignored issues
show
Bug introduced by
Accessing ID on the interface SilverStripe\Assets\Storage\AssetContainer suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
664
			'File database record is not the same'
665
		);
666
	}
667
668
	public function testDeleteResampledImagesOnUpload() {
669
		$tmpFileName = 'UploadTest-testUpload.jpg';
670
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
671
672
		$uploadImage = function() use ($tmpFileName, $tmpFilePath) {
673
			copy(__DIR__ . '/gdtest/test_jpg.jpg', $tmpFilePath);
674
675
			// emulates the $_FILES array
676
			$tmpFile = array(
677
				'name' => $tmpFileName,
678
				'type' => 'text/plaintext',
679
				'size' => filesize($tmpFilePath),
680
				'tmp_name' => $tmpFilePath,
681
				'extension' => 'jpg',
682
				'error' => UPLOAD_ERR_OK,
683
			);
684
685
			$v = new UploadTest_Validator();
686
687
			// test upload into default folder
688
			$u = new Upload();
689
			$u->setReplaceFile(true);
690
			$u->setValidator($v);
691
			$u->loadIntoFile($tmpFile);
692
			return $u->getFile();
693
		};
694
695
		// Image upload and generate a resampled image
696
		$image = $uploadImage();
697
		$resampled = $image->ResizedImage(123, 456);
698
		$resampledPath = AssetStoreTest_SpyStore::getLocalPath($resampled);
699
		$this->assertFileExists($resampledPath);
700
701
		// Re-upload the image, overwriting the original
702
		// Resampled images should removed when their parent file is overwritten
703
		$image = $uploadImage();
704
		$this->assertFileExists($resampledPath);
705
	}
706
707
	public function testFileVersioningWithAnExistingFile() {
708
		$upload = function($tmpFileName) {
709
			// create tmp file
710
			$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
711
			$tmpFileContent = '';
712
			for ($i = 0; $i < 10000; $i++) {
713
				$tmpFileContent .= '0';
714
			}
715
			file_put_contents($tmpFilePath, $tmpFileContent);
716
717
			// emulates the $_FILES array
718
			$tmpFile = array(
719
				'name' => $tmpFileName,
720
				'type' => 'text/plaintext',
721
				'size' => filesize($tmpFilePath),
722
				'tmp_name' => $tmpFilePath,
723
				'extension' => 'jpg',
724
				'error' => UPLOAD_ERR_OK,
725
			);
726
727
			$v = new UploadTest_Validator();
728
729
			// test upload into default folder
730
			$u = new Upload();
731
			$u->setReplaceFile(false);
732
			$u->setValidator($v);
733
			$u->loadIntoFile($tmpFile);
734
			return $u->getFile();
735
		};
736
737
		// test empty file version prefix
738
		Config::inst()->update('SilverStripe\\Assets\\Storage\\DefaultAssetNameGenerator', 'version_prefix', '');
739
740
		$file1 = $upload('UploadTest-IMG001.jpg');
741
		$this->assertEquals(
742
			'UploadTest-IMG001.jpg',
743
			$file1->Name,
744
			'File does not receive new name'
745
		);
746
747
		$file2 = $upload('UploadTest-IMG001.jpg');
748
		$this->assertEquals(
749
			'UploadTest-IMG002.jpg',
750
			$file2->Name,
751
			'File does receive new name'
752
		);
753
754
		$file3 = $upload('UploadTest-IMG002.jpg');
755
		$this->assertEquals(
756
			'UploadTest-IMG003.jpg',
757
			$file3->Name,
758
			'File does receive new name'
759
		);
760
761
		$file4 = $upload('UploadTest-IMG3.jpg');
762
		$this->assertEquals(
763
			'UploadTest-IMG3.jpg',
764
			$file4->Name,
765
			'File does not receive new name'
766
		);
767
768
		$file1->delete();
769
		$file2->delete();
770
		$file3->delete();
771
		$file4->delete();
772
773
		// test '-v' file version prefix
774
		Config::inst()->update('SilverStripe\\Assets\\Storage\\DefaultAssetNameGenerator', 'version_prefix', '-v');
775
776
		$file1 = $upload('UploadTest2-IMG001.jpg');
777
		$this->assertEquals(
778
			'UploadTest2-IMG001.jpg',
779
			$file1->Name,
780
			'File does not receive new name'
781
		);
782
783
		$file2 = $upload('UploadTest2-IMG001.jpg');
784
		$this->assertEquals(
785
			'UploadTest2-IMG001-v2.jpg',
786
			$file2->Name,
787
			'File does receive new name'
788
		);
789
790
		$file3 = $upload('UploadTest2-IMG001.jpg');
791
		$this->assertEquals(
792
			'UploadTest2-IMG001-v3.jpg',
793
			$file3->Name,
794
			'File does receive new name'
795
		);
796
797
		$file4 = $upload('UploadTest2-IMG001-v3.jpg');
798
		$this->assertEquals(
799
			'UploadTest2-IMG001-v4.jpg',
800
			$file4->Name,
801
			'File does receive new name'
802
		);
803
	}
804
}
805
806
class UploadTest_Validator extends Upload_Validator implements TestOnly {
807
808
	/**
809
	 * Looser check validation that doesn't do is_upload_file()
810
	 * checks as we're faking a POST request that PHP didn't generate
811
	 * itself.
812
	 *
813
	 * @return boolean
814
	 */
815
	public function validate() {
816
		$pathInfo = pathinfo($this->tmpFile['name']);
817
		// filesize validation
818
819
		if(!$this->isValidSize()) {
820
			$ext = (isset($pathInfo['extension'])) ? $pathInfo['extension'] : '';
821
			$arg = File::format_size($this->getAllowedMaxFileSize($ext));
822
			$this->errors[] = _t(
823
				'File.TOOLARGE',
824
				'File size is too large, maximum {size} allowed',
825
				'Argument 1: File size (e.g. 1MB)',
826
				array('size' => $arg)
827
			);
828
			return false;
829
		}
830
831
		// extension validation
832
		if(!$this->isValidExtension()) {
833
			$this->errors[] = _t(
834
				'File.INVALIDEXTENSION',
835
				'Extension is not allowed (valid: {extensions})',
836
				'Argument 1: Comma-separated list of valid extensions',
837
				array('extensions' => implode(',', $this->allowedExtensions))
838
			);
839
			return false;
840
		}
841
842
		return true;
843
	}
844
845
}
846