Completed
Push — new-committers ( 29cb6f...bcba16 )
by Sam
12:18 queued 33s
created

Upload::resolveExistingFile()   D

Complexity

Conditions 10
Paths 12

Size

Total Lines 40
Code Lines 19

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 40
rs 4.8196
cc 10
eloc 19
nc 12
nop 1

4 Methods

Rating   Name   Duplication   Size   Complexity  
A Upload::getFile() 0 3 1
A Upload::setFile() 0 3 1
A Upload::clearErrors() 0 4 1
A Upload::isError() 0 3 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Manages uploads via HTML forms processed by PHP,
4
 * uploads to Silverstripe's default upload directory,
5
 * and either creates a new or uses an existing File-object
6
 * for syncing with the database.
7
 *
8
 * <b>Validation</b>
9
 *
10
 * By default, a user can upload files without extension limitations,
11
 * which can be a security risk if the webserver is not properly secured.
12
 * Use {@link setAllowedExtensions()} to limit this list,
13
 * and ensure the "assets/" directory does not execute scripts
14
 * (see http://doc.silverstripe.org/secure-development#filesystem).
15
 * {@link File::$allowed_extensions} provides a good start for a list of "safe" extensions.
16
 *
17
 * @package framework
18
 * @subpackage filesystem
19
 *
20
 * @todo Allow for non-database uploads
21
 */
22
class Upload extends Controller {
23
24
	private static $allowed_actions = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
25
		'index',
26
		'load'
27
	);
28
29
	/**
30
	 * A File object
31
	 *
32
	 * @var File
33
	 */
34
	protected $file;
35
36
	/**
37
	 * Validator for this upload field
38
	 *
39
	 * @var Upload_Validator
40
	 */
41
	protected $validator;
42
43
	/**
44
	 * Information about the temporary file produced
45
	 * by the PHP-runtime.
46
	 *
47
	 * @var array
48
	 */
49
	protected $tmpFile;
50
51
	/**
52
	 * Replace an existing file rather than renaming the new one.
53
	 *
54
	 * @var boolean
55
	 */
56
	protected $replaceFile;
57
58
	/**
59
	 * Processing errors that can be evaluated,
60
	 * e.g. by Form-validation.
61
	 *
62
	 * @var array
63
	 */
64
	protected $errors = array();
65
66
	/**
67
	 * A foldername relative to /assets,
68
	 * where all uploaded files are stored by default.
69
	 *
70
	 * @config
71
	 * @var string
72
	 */
73
	private static $uploads_folder = "Uploads";
74
75
	/**
76
	 * A prefix for the version number added to an uploaded file
77
	 * when a file with the same name already exists.
78
	 * Example using no prefix: IMG001.jpg becomes IMG2.jpg
79
	 * Example using '-v' prefix: IMG001.jpg becomes IMG001-v2.jpg
80
	 *
81
	 * @config
82
	 * @var string
83
	 */
84
	private static $version_prefix = ''; // a default value will be introduced in SS4.0
85
86
	public function __construct() {
87
		parent::__construct();
88
		$this->validator = Injector::inst()->create('Upload_Validator');
89
		$this->replaceFile = self::config()->replaceFile;
0 ignored issues
show
Documentation introduced by
The property replaceFile does not exist on object<Config_ForClass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
90
	}
91
92
	/**
93
	 * Get current validator
94
	 *
95
	 * @return Upload_Validator $validator
96
	 */
97
	public function getValidator() {
98
		return $this->validator;
99
	}
100
101
	/**
102
	 * Set a different instance than {@link Upload_Validator}
103
	 * for this upload session.
104
	 *
105
	 * @param object $validator
106
	 */
107
	public function setValidator($validator) {
108
		$this->validator = $validator;
109
	}
110
111
	/**
112
	 * Save an file passed from a form post into this object.
113
	 * File names are filtered through {@link FileNameFilter}, see class documentation
114
	 * on how to influence this behaviour.
115
	 *
116
	 * @param $tmpFile array Indexed array that PHP generated for every file it uploads.
117
	 * @param $folderPath string Folder path relative to /assets
118
	 * @return Boolean|string Either success or error-message.
119
	 */
120
	public function load($tmpFile, $folderPath = false) {
121
		$this->clearErrors();
122
123
		if(!$folderPath) $folderPath = $this->config()->uploads_folder;
0 ignored issues
show
Documentation introduced by
The property uploads_folder does not exist on object<Config_ForClass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
124
125
		if(!is_array($tmpFile)) {
126
			user_error("Upload::load() Not passed an array.  Most likely, the form hasn't got the right enctype",
127
				E_USER_ERROR);
128
		}
129
130
		if(!$tmpFile['size']) {
131
			$this->errors[] = _t('File.NOFILESIZE', 'File size is zero bytes.');
132
			return false;
133
		}
134
135
		$valid = $this->validate($tmpFile);
136
		if(!$valid) return false;
137
138
		// @TODO This puts a HUGE limitation on files especially when lots
139
		// have been uploaded.
140
		$base = Director::baseFolder();
141
		$parentFolder = Folder::find_or_make($folderPath);
142
143
		// Generate default filename
144
		$nameFilter = FileNameFilter::create();
145
		$file = $nameFilter->filter($tmpFile['name']);
146
		$fileName = basename($file);
147
148
		$relativeFolderPath = $parentFolder
149
				? $parentFolder->getRelativePath()
150
				: ASSETS_DIR . '/';
151
		$relativeFilePath = $relativeFolderPath . $fileName;
152
153
		// Create a new file record (or try to retrieve an existing one)
154
		if(!$this->file) {
155
			$fileClass = File::get_class_for_file_extension(pathinfo($tmpFile['name'], PATHINFO_EXTENSION));
156
			$this->file = new $fileClass();
157
		}
158
		if(!$this->file->ID && $this->replaceFile) {
159
			$fileClass = $this->file->class;
160
			$file = File::get()
161
				->filter(array(
162
					'ClassName' => $fileClass,
163
					'Name' => $fileName,
164
					'ParentID' => $parentFolder ? $parentFolder->ID : 0
165
				))->First();
166
			if($file) {
167
				$this->file = $file;
168
			}
169
		}
170
171
		// if filename already exists, version the filename (e.g. test.gif to test2.gif, test2.gif to test3.gif)
172
		if(!$this->replaceFile) {
173
			$fileSuffixArray = explode('.', $fileName);
174
			$fileTitle = array_shift($fileSuffixArray);
175
			$fileSuffix = !empty($fileSuffixArray)
176
					? '.' . implode('.', $fileSuffixArray)
177
					: null;
178
179
			// make sure files retain valid extensions
180
			$oldFilePath = $relativeFilePath;
181
			$relativeFilePath = $relativeFolderPath . $fileTitle . $fileSuffix;
182
			if($oldFilePath !== $relativeFilePath) {
183
				user_error("Couldn't fix $relativeFilePath", E_USER_ERROR);
184
			}
185
			while(file_exists("$base/$relativeFilePath")) {
186
				$i = isset($i) ? ($i+1) : 2;
187
				$oldFilePath = $relativeFilePath;
188
189
				$prefix = $this->config()->version_prefix;
0 ignored issues
show
Documentation introduced by
The property version_prefix does not exist on object<Config_ForClass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
190
				$pattern = '/' . preg_quote($prefix) . '([0-9]+$)/';
191
				if(preg_match($pattern, $fileTitle, $matches)) {
192
					$fileTitle = preg_replace($pattern, $prefix . ($matches[1] + 1), $fileTitle);
193
				} else {
194
					$fileTitle .= $prefix . $i;
195
				}
196
				$relativeFilePath = $relativeFolderPath . $fileTitle . $fileSuffix;
197
198
				if($oldFilePath == $relativeFilePath && $i > 2) {
199
					user_error("Couldn't fix $relativeFilePath with $i tries", E_USER_ERROR);
200
				}
201
			}
202
		} else {
203
			//reset the ownerID to the current member when replacing files
204
			$this->file->OwnerID = (Member::currentUser() ? Member::currentUser()->ID : 0);
205
		}
206
207
		if(file_exists($tmpFile['tmp_name']) && copy($tmpFile['tmp_name'], "$base/$relativeFilePath")) {
208
			$this->file->ParentID = $parentFolder ? $parentFolder->ID : 0;
209
			// This is to prevent it from trying to rename the file
210
			$this->file->Name = basename($relativeFilePath);
211
			$this->file->write();
212
			$this->file->onAfterUpload();
213
			$this->extend('onAfterLoad', $this->file, $tmpFile);   //to allow extensions to e.g. create a version after an upload
214
			return true;
215
		} else {
216
			$this->errors[] = _t('File.NOFILESIZE', 'File size is zero bytes.');
217
			return false;
218
		}
219
	}
220
221
	/**
222
	 * Load temporary PHP-upload into File-object.
223
	 *
224
	 * @param array $tmpFile
225
	 * @param File $file
226
	 * @return Boolean
227
	 */
228
	public function loadIntoFile($tmpFile, $file, $folderPath = false) {
229
		$this->file = $file;
230
		return $this->load($tmpFile, $folderPath);
231
	}
232
233
	/**
234
	 * @return Boolean
235
	 */
236
	public function setReplaceFile($bool) {
237
		$this->replaceFile = $bool;
238
	}
239
240
	/**
241
	 * @return Boolean
242
	 */
243
	public function getReplaceFile() {
244
		return $this->replaceFile;
245
	}
246
247
	/**
248
	 * Container for all validation on the file
249
	 * (e.g. size and extension restrictions).
250
	 * Is NOT connected to the {Validator} classes,
251
	 * please have a look at {FileField->validate()}
252
	 * for an example implementation of external validation.
253
	 *
254
	 * @param array $tmpFile
255
	 * @return boolean
256
	 */
257
	public function validate($tmpFile) {
258
		$validator = $this->validator;
259
		$validator->setTmpFile($tmpFile);
260
		$isValid = $validator->validate();
261
		if($validator->getErrors()) {
262
			$this->errors = array_merge($this->errors, $validator->getErrors());
263
		}
264
		return $isValid;
265
	}
266
267
	/**
268
	 * Get file-object, either generated from {load()},
269
	 * or manually set.
270
	 *
271
	 * @return File
272
	 */
273
	public function getFile() {
274
		return $this->file;
275
	}
276
277
	/**
278
	 * Set a file-object (similiar to {loadIntoFile()})
279
	 *
280
	 * @param File $file
281
	 */
282
	public function setFile($file) {
283
		$this->file = $file;
284
	}
285
286
	/**
287
	 * Clear out all errors (mostly set by {loadUploaded()})
288
	 * including the validator's errors
289
	 */
290
	public function clearErrors() {
291
		$this->errors = array();
292
		$this->validator->clearErrors();
293
	}
294
295
	/**
296
	 * Determines wether previous operations caused an error.
297
	 *
298
	 * @return boolean
299
	 */
300
	public function isError() {
301
		return (count($this->errors));
302
	}
303
304
	/**
305
	 * Return all errors that occurred while processing so far
306
	 * (mostly set by {loadUploaded()})
307
	 *
308
	 * @return array
309
	 */
310
	public function getErrors() {
311
		return $this->errors;
312
	}
313
314
}
315
316
/**
317
 * @package framework
318
 * @subpackage filesystem
319
 */
320
class Upload_Validator {
321
322
	/**
323
	* Contains a list of the max file sizes shared by
324
	* all upload fields. This is then duplicated into the
325
	* "allowedMaxFileSize" instance property on construct.
326
	*
327
	* @config
328
	* @var array
329
	*/
330
	private static $default_max_file_size = array();
331
332
	/**
333
	 * Information about the temporary file produced
334
	 * by the PHP-runtime.
335
	 *
336
	 * @var array
337
	 */
338
	protected $tmpFile;
339
340
	protected $errors = array();
341
342
	/**
343
	 * Restrict filesize for either all filetypes
344
	 * or a specific extension, with extension-name
345
	 * as array-key and the size-restriction in bytes as array-value.
346
	 *
347
	 * @var array
348
	 */
349
	public $allowedMaxFileSize = array();
350
351
	/**
352
	 * @var array Collection of extensions.
353
	 * Extension-names are treated case-insensitive.
354
	 *
355
	 * Example:
356
	 * <code>
357
	 * 	array("jpg","GIF")
358
	 * </code>
359
	 */
360
	public $allowedExtensions = array();
361
362
	/**
363
	 * Return all errors that occurred while validating
364
	 * the temporary file.
365
	 *
366
	 * @return array
367
	 */
368
	public function getErrors() {
369
		return $this->errors;
370
	}
371
372
	/**
373
	 * Clear out all errors
374
	 */
375
	public function clearErrors() {
376
		$this->errors = array();
377
	}
378
379
	/**
380
	 * Set information about temporary file produced by PHP.
381
	 * @param array $tmpFile
382
	 */
383
	public function setTmpFile($tmpFile) {
384
		$this->tmpFile = $tmpFile;
385
	}
386
387
	/**
388
	 * Get maximum file size for all or specified file extension.
389
	 *
390
	 * @param string $ext
391
	 * @return int Filesize in bytes
392
	 */
393
	public function getAllowedMaxFileSize($ext = null) {
394
395
		// Check if there is any defined instance max file sizes
396
		if (empty($this->allowedMaxFileSize)) {
397
			// Set default max file sizes if there isn't
398
			$fileSize = Config::inst()->get('Upload_Validator', 'default_max_file_size');
399
			if (isset($fileSize)) {
400
				$this->setAllowedMaxFileSize($fileSize);
401
			} else {
402
				// When no default is present, use maximum set by PHP
403
				$maxUpload = File::ini2bytes(ini_get('upload_max_filesize'));
404
				$maxPost = File::ini2bytes(ini_get('post_max_size'));
405
				$this->setAllowedMaxFileSize(min($maxUpload, $maxPost));
406
			}
407
		}
408
409
		$ext = strtolower($ext);
410
		if ($ext) {
411
			if (isset($this->allowedMaxFileSize[$ext])) {
412
				return $this->allowedMaxFileSize[$ext];
413
			}
414
415
			$category = File::get_app_category($ext);
416
			if ($category && isset($this->allowedMaxFileSize['[' . $category . ']'])) {
417
				return $this->allowedMaxFileSize['[' . $category . ']'];
418
			}
419
420
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Upload_Validator::getAllowedMaxFileSize of type integer.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
421
		} else {
422
			return (isset($this->allowedMaxFileSize['*'])) ? $this->allowedMaxFileSize['*'] : false;
423
		}
424
	}
425
426
	/**
427
	 * Set filesize maximums (in bytes or INI format).
428
	 * Automatically converts extensions to lowercase
429
	 * for easier matching.
430
	 *
431
	 * Example:
432
	 * <code>
433
	 * array('*' => 200, 'jpg' => 1000, '[doc]' => '5m')
434
	 * </code>
435
	 *
436
	 * @param array|int $rules
437
	 */
438
	public function setAllowedMaxFileSize($rules) {
439
		if(is_array($rules) && count($rules)) {
440
			// make sure all extensions are lowercase
441
			$rules = array_change_key_case($rules, CASE_LOWER);
442
			$finalRules = array();
443
			$tmpSize = 0;
0 ignored issues
show
Unused Code introduced by
$tmpSize is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
444
445
			foreach ($rules as $rule => $value) {
446
				if (is_numeric($value)) {
447
					$tmpSize = $value;
448
				} else {
449
					$tmpSize = File::ini2bytes($value);
450
				}
451
452
				$finalRules[$rule] = (int)$tmpSize;
453
			}
454
455
			$this->allowedMaxFileSize = $finalRules;
456
		} elseif(is_string($rules)) {
457
			$this->allowedMaxFileSize['*'] = File::ini2bytes($rules);
458
		} elseif((int) $rules > 0) {
459
			$this->allowedMaxFileSize['*'] = (int)$rules;
460
		}
461
	}
462
463
	/**
464
	 * @return array
465
	 */
466
	public function getAllowedExtensions() {
467
		return $this->allowedExtensions;
468
	}
469
470
	/**
471
	 * Limit allowed file extensions. Empty by default, allowing all extensions.
472
	 * To allow files without an extension, use an empty string.
473
	 * See {@link File::$allowed_extensions} to get a good standard set of
474
	 * extensions that are typically not harmful in a webserver context.
475
	 * See {@link setAllowedMaxFileSize()} to limit file size by extension.
476
	 *
477
	 * @param array $rules List of extensions
478
	 */
479
	public function setAllowedExtensions($rules) {
480
		if(!is_array($rules)) return false;
481
482
		// make sure all rules are lowercase
483
		foreach($rules as &$rule) $rule = strtolower($rule);
484
485
		$this->allowedExtensions = $rules;
486
	}
487
488
	/**
489
	 * Determines if the bytesize of an uploaded
490
	 * file is valid - can be defined on an
491
	 * extension-by-extension basis in {@link $allowedMaxFileSize}
492
	 *
493
	 * @return boolean
494
	 */
495
	public function isValidSize() {
496
		$pathInfo = pathinfo($this->tmpFile['name']);
497
		$extension = isset($pathInfo['extension']) ? strtolower($pathInfo['extension']) : null;
498
		$maxSize = $this->getAllowedMaxFileSize($extension);
499
		return (!$this->tmpFile['size'] || !$maxSize || (int) $this->tmpFile['size'] < $maxSize);
500
	}
501
502
	/**
503
	 * Determines if the temporary file has a valid extension
504
	 * An empty string in the validation map indicates files without an extension.
505
	 * @return boolean
506
	 */
507
	public function isValidExtension() {
508
		$pathInfo = pathinfo($this->tmpFile['name']);
509
510
		// Special case for filenames without an extension
511
		if(!isset($pathInfo['extension'])) {
512
			return in_array('', $this->allowedExtensions, true);
513
		} else {
514
			return (!count($this->allowedExtensions)
515
				|| in_array(strtolower($pathInfo['extension']), $this->allowedExtensions));
516
		}
517
	}
518
519
	/**
520
	 * Run through the rules for this validator checking against
521
	 * the temporary file set by {@link setTmpFile()} to see if
522
	 * the file is deemed valid or not.
523
	 *
524
	 * @return boolean
525
	 */
526
	public function validate() {
527
		// we don't validate for empty upload fields yet
528
		if(!isset($this->tmpFile['name']) || empty($this->tmpFile['name'])) return true;
529
530
		$isRunningTests = (class_exists('SapphireTest', false) && SapphireTest::is_running_test());
531
		if(isset($this->tmpFile['tmp_name']) && !is_uploaded_file($this->tmpFile['tmp_name']) && !$isRunningTests) {
532
			$this->errors[] = _t('File.NOVALIDUPLOAD', 'File is not a valid upload');
533
			return false;
534
		}
535
536
		$pathInfo = pathinfo($this->tmpFile['name']);
537
		// filesize validation
538 View Code Duplication
		if(!$this->isValidSize()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
539
			$ext = (isset($pathInfo['extension'])) ? $pathInfo['extension'] : '';
540
			$arg = File::format_size($this->getAllowedMaxFileSize($ext));
541
			$this->errors[] = _t(
542
				'File.TOOLARGE',
543
				'File size is too large, maximum {size} allowed',
544
				'Argument 1: File size (e.g. 1MB)',
545
				array('size' => $arg)
0 ignored issues
show
Documentation introduced by
array('size' => $arg) is of type array<string,string,{"size":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
546
			);
547
			return false;
548
		}
549
550
		// extension validation
551 View Code Duplication
		if(!$this->isValidExtension()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
552
			$this->errors[] = _t(
553
				'File.INVALIDEXTENSION',
554
				'Extension is not allowed (valid: {extensions})',
555
				'Argument 1: Comma-separated list of valid extensions',
556
				array('extensions' => wordwrap(implode(', ', $this->allowedExtensions)))
0 ignored issues
show
Documentation introduced by
array('extensions' => wo...s->allowedExtensions))) is of type array<string,string,{"extensions":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
557
			);
558
			return false;
559
		}
560
561
		return true;
562
	}
563
564
}
565