Passed
Pull Request — 4.8 (#10059)
by
unknown
10:10
created

FileFieldTest::testWeUseConfigForSizingIfDefined()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 1
eloc 7
c 3
b 0
f 0
nc 1
nop 0
dl 0
loc 15
rs 10
1
<?php
2
3
namespace SilverStripe\Forms\Tests;
4
5
use ReflectionMethod;
6
use SilverStripe\Core\Convert;
7
use SilverStripe\Assets\Upload_Validator;
8
use SilverStripe\Dev\FunctionalTest;
9
use SilverStripe\Control\Controller;
10
use SilverStripe\Forms\FileField;
11
use SilverStripe\Forms\FieldList;
12
use SilverStripe\Forms\Form;
13
use SilverStripe\Forms\RequiredFields;
14
15
class FileFieldTest extends FunctionalTest
16
{
17
    /**
18
     * Test a valid upload of a required file in a form. Error is set to 0, as the upload went well
19
     *
20
     * @skipUpgrade
21
     */
22
    public function testUploadRequiredFile()
23
    {
24
        $form = new Form(
25
            Controller::curr(),
26
            'Form',
27
            new FieldList(
28
                $fileField = new FileField('cv', 'Upload your CV')
29
            ),
30
            new FieldList()
31
        );
32
        $fileFieldValue = [
33
            'name' => 'aCV.txt',
34
            'type' => 'application/octet-stream',
35
            'tmp_name' => '/private/var/tmp/phpzTQbqP',
36
            'error' => 0,
37
            'size' => 3471
38
        ];
39
        $fileField->setValue($fileFieldValue);
40
41
        $this->assertTrue($form->validationResult()->isValid());
42
    }
43
44
    /**
45
     * Test that FileField::validate() is run on FileFields with both single and multi-file syntax
46
     * By default FileField::validate() will return true early if the $_FILES super-global does not contain the
47
     * corresponding FileField::name. This early return means the files was not fully run through FileField::validate()
48
     * So for this test we create an invalid file upload on purpose and test that false was returned which means that
49
     * the file was run through FileField::validate() function
50
     */
51
    public function testMultiFileSyntaxUploadIsValidated()
52
    {
53
        $names = [
54
            'single_file_syntax',
55
            'multi_file_syntax_a[]',
56
            'multi_file_syntax_b[0]',
57
            'multi_file_syntax_c[key]'
58
        ];
59
        foreach ($names as $name) {
60
            $form = new Form(
61
                Controller::curr(),
62
                'Form',
63
                new FieldList($fileField = new FileField($name, 'My desc')),
64
                new FieldList()
65
            );
66
            $fileData = $this->createInvalidUploadedFileData($name, "FileFieldTest.txt");
67
            // FileFields with multi_file_syntax[] files will appear in the $_FILES super-global
68
            // with the [] brackets trimmed e.g. $_FILES[multi_file_syntax]
69
            $_FILES = [preg_replace('#\[(.*?)\]#', '', $name) => $fileData];
70
            $fileField->setValue($fileData);
71
            $validator = $form->getValidator();
72
            $isValid = $fileField->validate($validator);
73
            $this->assertFalse($isValid, "$name was run through the validate() function");
74
        }
75
    }
76
77
    protected function createInvalidUploadedFileData($name, $tmpFileName): array
78
    {
79
        $tmpFilePath = TEMP_PATH . DIRECTORY_SEPARATOR . $tmpFileName;
80
81
        // multi_file_syntax
82
        if (strpos($name, '[') !== false) {
83
            $key = 0;
84
            if (preg_match('#\[(.+?)\]#', $name, $m)) {
85
                $key = $m[1];
86
            }
87
            return [
88
                'name' => [$key => $tmpFileName],
89
                'type' => [$key => 'text/plaintext'],
90
                'size' => [$key => 0],
91
                'tmp_name' => [$key => $tmpFilePath],
92
                'error' => [$key => UPLOAD_ERR_NO_FILE],
93
            ];
94
        }
95
        // single_file_syntax
96
        return [
97
            'name' => $tmpFileName,
98
            'type' => 'text/plaintext',
99
            'size' => 0,
100
            'tmp_name' => $tmpFilePath,
101
            'error' => UPLOAD_ERR_NO_FILE,
102
        ];
103
    }
104
105
    /**
106
     * @skipUpgrade
107
     */
108
    public function testGetAcceptFileTypes()
109
    {
110
        $field = new FileField('image', 'Image');
111
        $field->setAllowedExtensions('jpg', 'png');
0 ignored issues
show
Unused Code introduced by
The call to SilverStripe\Forms\FileF...:setAllowedExtensions() has too many arguments starting with 'png'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

111
        $field->/** @scrutinizer ignore-call */ 
112
                setAllowedExtensions('jpg', 'png');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
'jpg' of type string is incompatible with the type array expected by parameter $rules of SilverStripe\Forms\FileF...:setAllowedExtensions(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

111
        $field->setAllowedExtensions(/** @scrutinizer ignore-type */ 'jpg', 'png');
Loading history...
112
113
        $method = new ReflectionMethod($field, 'getAcceptFileTypes');
114
        $method->setAccessible(true);
115
        $allowed = $method->invoke($field);
116
117
        $expected = ['.jpg', '.png', 'image/jpeg', 'image/png'];
118
        foreach ($expected as $extensionOrMime) {
119
            $this->assertContains($extensionOrMime, $allowed);
120
        }
121
    }
122
123
    /**
124
     * Test different scenarii for a failed upload : an error occured, no files where provided
125
     * @skipUpgrade
126
     */
127
    public function testUploadMissingRequiredFile()
128
    {
129
        $form = new Form(
130
            Controller::curr(),
131
            'Form',
132
            new FieldList(
133
                $fileField = new FileField('cv', 'Upload your CV')
134
            ),
135
            new FieldList(),
136
            new RequiredFields('cv')
137
        );
138
        // All fields are filled but for some reason an error occured when uploading the file => fails
139
        $fileFieldValue = [
140
            'name' => 'aCV.txt',
141
            'type' => 'application/octet-stream',
142
            'tmp_name' => '/private/var/tmp/phpzTQbqP',
143
            'error' => 1,
144
            'size' => 3471
145
        ];
146
        $fileField->setValue($fileFieldValue);
147
148
        $this->assertFalse(
149
            $form->validationResult()->isValid(),
150
            'An error occured when uploading a file, but the validator returned true'
151
        );
152
153
        // We pass an empty set of parameters for the uploaded file => fails
154
        $fileFieldValue = [];
155
        $fileField->setValue($fileFieldValue);
156
157
        $this->assertFalse(
158
            $form->validationResult()->isValid(),
159
            'An empty array was passed as parameter for an uploaded file, but the validator returned true'
160
        );
161
162
        // We pass an null value for the uploaded file => fails
163
        $fileFieldValue = null;
164
        $fileField->setValue($fileFieldValue);
165
166
        $this->assertFalse(
167
            $form->validationResult()->isValid(),
168
            'A null value was passed as parameter for an uploaded file, but the validator returned true'
169
        );
170
    }
171
  
172
    /**
173
     * Test the file size validation will use the PHP max size setting if
174
     * no config for the Upload_Validator::default_max_file_size has been defined
175
     */
176
    public function testWeWillDefaultToPHPMaxUploadSizingForValidation()
177
    {
178
        // These 3 lines are how SilverStripe works out the default max upload size as defined in Upload_Validator
179
        $phpMaxUpload = Convert::memstring2bytes(ini_get('upload_max_filesize'));
180
        $maxPost = Convert::memstring2bytes(ini_get('post_max_size'));
181
        $defaultUploadSize = min($phpMaxUpload, $maxPost);
182
183
        $fileField = new FileField('DemoField');
184
185
        $this->assertEquals($defaultUploadSize, $fileField->getValidator()->getAllowedMaxFileSize('jpg'));
186
        $this->assertEquals($defaultUploadSize, $fileField->getValidator()->getAllowedMaxFileSize('png'));
187
    }
188
189
    /**
190
     * Test the file size validation will use the default_max_file_size validation config if defined
191
     */
192
    public function testWeUseConfigForSizingIfDefined()
193
    {
194
        $configMaxFileSizes = [
195
            'jpg' => $jpgSize = '2m',
196
            '*' => $defaultSize = '1m',
197
        ];
198
199
        Upload_Validator::config()->set('default_max_file_size', $configMaxFileSizes);
200
201
        $fileField = new FileField('DemoField');
202
203
        $this->assertEquals(Convert::memstring2bytes($jpgSize), $fileField->getValidator()->getAllowedMaxFileSize('jpg'));
204
205
        // PNG is not explicitly defined in config, so would fall back to *
206
        $this->assertEquals(Convert::memstring2bytes($defaultSize), $fileField->getValidator()->getAllowedMaxFileSize('png'));
207
    }    
208
209
}
210