Completed
Branch master (9ae314)
by Michael
02:19 queued 31s
created

XoopsMediaUploader::upload()   D

Complexity

Conditions 10
Paths 161

Size

Total Lines 39
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 20
nc 161
nop 1
dl 0
loc 39
rs 4.606
c 0
b 0
f 0

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
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 67 and the first side effect is on line 62.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/*
3
 * Module: WF-Links
4
 * Version: v1.0.3
5
 * Release Date: 21 June 2005
6
 * Developer: John N
7
 * Team: WF-Projects
8
 * Licence: GNU
9
 */
10
/**
11
 * !
12
 * Example
13
 *
14
 * require_once __DIR__ . '/uploader.php';
15
 * $allowed_mimetypes = array('image/gif', 'image/jpeg', 'image/pjpeg', 'image/x-png');
16
 * $maxfilesize = 50000;
17
 * $maxfilewidth = 120;
18
 * $maxfileheight = 120;
19
 * $uploader = new XoopsMediaUploader('/home/xoops/uploads', $allowed_mimetypes, $maxfilesize, $maxfilewidth, $maxfileheight);
20
 * if ($uploader->fetchMedia($HTTP_POST_VARS['uploade_file_name'])) {
21
 * if (!$uploader->upload()) {
22
 * echo $uploader->getErrors();
23
 * } else {
24
 * echo '<h4>File uploaded successfully!</h4>'
25
 * echo 'Saved as: ' . $uploader->getSavedFileName() . '<br>';
26
 * echo 'Full path: ' . $uploader->getSavedDestination();
27
 * }
28
 * } else {
29
 * echo $uploader->getErrors();
30
 * }
31
 */
32
33
/**
34
 * Upload Media files
35
 *
36
 * Example of usage:
37
 * <code>
38
 * require_once __DIR__ . '/uploader.php';
39
 * $allowed_mimetypes = array('image/gif', 'image/jpeg', 'image/pjpeg', 'image/x-png');
40
 * $maxfilesize = 50000;
41
 * $maxfilewidth = 120;
42
 * $maxfileheight = 120;
43
 * $uploader = new XoopsMediaUploader('/home/xoops/uploads', $allowed_mimetypes, $maxfilesize, $maxfilewidth, $maxfileheight);
44
 * if ($uploader->fetchMedia($HTTP_POST_VARS['uploade_file_name'])) {
45
 *            if (!$uploader->upload()) {
46
 *               echo $uploader->getErrors();
47
 *            } else {
48
 *               echo '<h4>File uploaded successfully!</h4>'
49
 *               echo 'Saved as: ' . $uploader->getSavedFileName() . '<br>';
50
 *               echo 'Full path: ' . $uploader->getSavedDestination();
51
 *            }
52
 * } else {
53
 *            echo $uploader->getErrors();
54
 * }
55
 * </code>
56
 *
57
 * @package       kernel
58
 * @subpackage    core
59
 * @author        Kazumi Ono <[email protected]>
60
 * @copyright (c) XOOPS Project (https://xoops.org)
61
 */
62
mt_srand((double)microtime() * 1000000);
63
64
/**
65
 * Class XoopsMediaUploader
66
 */
67
class XoopsMediaUploader
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
68
{
69
    public $mediaName;
70
    public $mediaType;
71
    public $mediaSize;
72
    public $mediaTmpName;
73
    public $mediaError;
74
    public $uploadDir        = '';
75
    public $allowedMimeTypes = array();
76
    public $maxFileSize      = 0;
77
    public $maxWidth;
78
    public $maxHeight;
79
    public $targetFileName;
80
    public $prefix;
81
    public $ext;
82
    public $dimension;
83
    public $errors           = array();
84
    public $savedDestination;
85
    public $savedFileName;
86
    /**
87
     * No admin check for uploads
88
     */
89
    public $noadmin_sizecheck;
90
91
    /**
92
     * Constructor
93
     *
94
     * @param string    $uploadDir
95
     * @param array|int $allowedMimeTypes
96
     * @param int       $maxFileSize
97
     * @param int       $maxWidth
98
     * @param int       $maxHeight
99
     *
100
     * @internal param int $cmodvalue
101
     */
102
    public function __construct($uploadDir, $allowedMimeTypes = 0, $maxFileSize, $maxWidth = 0, $maxHeight = 0)
103
    {
104
        if (is_array($allowedMimeTypes)) {
105
            $this->allowedMimeTypes =& $allowedMimeTypes;
106
        }
107
        $this->uploadDir   = $uploadDir;
108
        $this->maxFileSize = (int)$maxFileSize;
109
        if (isset($maxWidth)) {
110
            $this->maxWidth = (int)$maxWidth;
111
        }
112
        if (isset($maxHeight)) {
113
            $this->maxHeight = (int)$maxHeight;
114
        }
115
    }
116
117
    /**
118
     * @param $value
119
     */
120
    public function noAdminSizeCheck($value)
121
    {
122
        $this->noadmin_sizecheck = $value;
123
    }
124
125
    /**
126
     * Fetch the uploaded file
127
     *
128
     * @param string  $media_name Name of the file field
129
     * @param int     $index      Index of the file (if more than one uploaded under that name)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $index not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
130
     *
131
     * @global        $HTTP_POST_FILES
132
     * @return bool
133
     */
134
    public function fetchMedia($media_name, $index = null)
0 ignored issues
show
Coding Style introduced by
fetchMedia uses the super-global variable $_FILES which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
135
    {
136
        global $_FILES;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
137
138
        if (!isset($_FILES[$media_name])) {
139
            $this->setErrors(_AM_WFL_READWRITEERROR);
140
141
            return false;
142
        } elseif (isset($index) && is_array($_FILES[$media_name]['name'])) {
143
            $index              = (int)$index;
144
            $this->mediaName    = get_magic_quotes_gpc() ? stripslashes($_FILES[$media_name]['name'][$index]) : $_FILES[$media_name]['name'][$index];
145
            $this->mediaType    = $_FILES[$media_name]['type'][$index];
146
            $this->mediaSize    = $_FILES[$media_name]['size'][$index];
147
            $this->mediaTmpName = $_FILES[$media_name]['tmp_name'][$index];
148
            $this->mediaError   = !empty($_FILES[$media_name]['error'][$index]) ? $_FILES[$media_name]['error'][$index] : 0;
149
        } else {
150
            $media_name         = @$_FILES[$media_name];
151
            $this->mediaName    = get_magic_quotes_gpc() ? stripslashes($media_name['name']) : $media_name['name'];
152
            $this->mediaName    = $media_name['name'];
153
            $this->mediaType    = $media_name['type'];
154
            $this->mediaSize    = $media_name['size'];
155
            $this->mediaTmpName = $media_name['tmp_name'];
156
            $this->mediaError   = !empty($media_name['error']) ? $media_name['error'] : 0;
157
        }
158
        $this->dimension = getimagesize($this->mediaTmpName);
159
160
        $this->errors = array();
161
162
        if ((int)$this->mediaSize < 0) {
163
            $this->setErrors(_AM_WFL_INVALIDFILESIZE);
164
165
            return false;
166
        }
167
        if ($this->mediaName === '') {
168
            $this->setErrors(_AM_WFL_FILENAMEEMPTY);
169
170
            return false;
171
        }
172
173
        if ($this->mediaTmpName === 'none') {
174
            $this->setErrors(_AM_WFL_NOFILEUPLOAD);
175
176
            return false;
177
        }
178
179
        if (!is_uploaded_file($this->mediaTmpName)) {
180
            switch ($this->mediaError) {
181
                case 0: // no error; possible file attack!
182
                    $this->setErrors(_AM_WFL_UPLOADERRORZERO);
183
                    break;
184
                case 1: // uploaded file exceeds the upload_max_filesize directive in php.ini
185
                    //if ($this->noAdminSizeCheck)
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
186
                    //{
187
                    //    return true;
188
                    //}
189
                    $this->setErrors(_AM_WFL_UPLOADERRORONE);
190
                    break;
191
                case 2: // uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the html form
192
                    $this->setErrors(_AM_WFL_UPLOADERRORTWO);
193
                    break;
194
                case 3: // uploaded file was only partially uploaded
195
                    $this->setErrors(_AM_WFL_UPLOADERRORTHREE);
196
                    break;
197
                case 4: // no file was uploaded
198
                    $this->setErrors(_AM_WFL_UPLOADERRORFOUR);
199
                    break;
200
                default: // a default error, just in case!  :)
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
201
                    $this->setErrors(_AM_WFL_UPLOADERRORFIVE);
202
                    break;
203
            }
204
205
            return false;
206
        }
207
208
        return true;
209
    }
210
211
    /**
212
     * Set the target filename
213
     *
214
     * @param string $value
215
     */
216
    public function setTargetFileName($value)
217
    {
218
        $this->targetFileName = trim($value);
219
    }
220
221
    /**
222
     * Set the prefix
223
     *
224
     * @param string $value
225
     */
226
    public function setPrefix($value)
227
    {
228
        $this->prefix = trim($value);
229
    }
230
231
    /**
232
     * Get the uploaded filename
233
     *
234
     * @return string
235
     */
236
    public function getMediaName()
237
    {
238
        return $this->mediaName;
239
    }
240
241
    /**
242
     * Get the type of the uploaded file
243
     *
244
     * @return string
245
     */
246
    public function getMediaType()
247
    {
248
        return $this->mediaType;
249
    }
250
251
    /**
252
     * Get the size of the uploaded file
253
     *
254
     * @return int
255
     */
256
    public function getMediaSize()
257
    {
258
        return $this->mediaSize;
259
    }
260
261
    /**
262
     * Get the temporary name that the uploaded file was stored under
263
     *
264
     * @return string
265
     */
266
    public function getMediaTmpName()
267
    {
268
        return $this->mediaTmpName;
269
    }
270
271
    /**
272
     * Get the saved filename
273
     *
274
     * @return string
275
     */
276
    public function getSavedFileName()
277
    {
278
        return $this->savedFileName;
279
    }
280
281
    /**
282
     * Get the destination the file is saved to
283
     *
284
     * @return string
285
     */
286
    public function getSavedDestination()
287
    {
288
        return $this->savedDestination;
289
    }
290
291
    /**
292
     * Check the file and copy it to the destination
293
     *
294
     * @param int $chmod
295
     *
296
     * @return bool
297
     */
298
    public function upload($chmod = 0644)
299
    {
300
        if ($this->uploadDir === '') {
301
            $this->setErrors(_AM_WFL_NOUPLOADDIR);
302
303
            return false;
304
        }
305
306
        if (!is_dir($this->uploadDir)) {
307
            $this->setErrors(_AM_WFL_FAILOPENDIR . $this->uploadDir);
308
        }
309
310
        if (!is_writable($this->uploadDir)) {
311
            $this->setErrors(_AM_WFL_FAILOPENDIRWRITEPERM . $this->uploadDir);
312
        }
313
314
        if (!$this->checkMaxFileSize()) {
315
            $this->setErrors(sprintf(_AM_WFL_FILESIZEMAXSIZE, $this->mediaSize, $this->maxFileSize));
316
        }
317
318
        if (is_array($this->dimension)) {
319
            if (!$this->checkMaxWidth($this->dimension[0])) {
320
                $this->setErrors(sprintf(_AM_WFL_FILESIZEMAXWIDTH, $this->dimension[0], $this->maxWidth));
321
            }
322
            if (!$this->checkMaxHeight($this->dimension[1])) {
323
                $this->setErrors(sprintf(_AM_WFL_FILESIZEMAXHEIGHT, $this->dimension[1], $this->maxHeight));
324
            }
325
        }
326
327
        if (!$this->checkMimeType()) {
328
            $this->setErrors(_AM_WFL_MIMENOTALLOW . $this->mediaType);
329
        }
330
331
        if (!$this->_copyFile($chmod)) {
332
            $this->setErrors(_AM_WFL_FAILEDUPLOADING . $this->mediaName);
333
        }
334
335
        return !(count($this->errors) > 0);
336
    }
337
338
    /**
339
     * Copy the file to its destination
340
     *
341
     * @param $chmod
342
     *
343
     * @return bool
344
     */
345
    public function _copyFile($chmod)
346
    {
347
        $matched = array();
348
        if (!preg_match("/\.([a-zA-Z0-9]+)$/", $this->mediaName, $matched)) {
349
            return false;
350
        }
351
        if (isset($this->targetFileName)) {
352
            $this->savedFileName = $this->targetFileName;
353
        } elseif (isset($this->prefix)) {
354
            $this->savedFileName = uniqid($this->prefix, true) . '.' . strtolower($matched[1]);
355
        } else {
356
            $this->savedFileName = strtolower($this->mediaName);
357
        }
358
        $this->savedFileName    = preg_replace('!\s+!', '_', $this->savedFileName);
359
        $this->savedDestination = $this->uploadDir . $this->savedFileName;
360
        if (is_file($this->savedDestination) && !!is_dir($this->savedDestination)) {
361
            $this->setErrors(_AM_WFL_FILE . $this->mediaName . _AM_WFL_ALREADYEXISTTRYAGAIN);
362
363
            return false;
364
        }
365
        if (!move_uploaded_file($this->mediaTmpName, $this->savedDestination)) {
366
            return false;
367
        }
368
        @chmod($this->savedDestination, $chmod);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
369
370
        return true;
371
    }
372
373
    /**
374
     * Is the file the right size?
375
     *
376
     * @return bool
377
     */
378
    public function checkMaxFileSize()
379
    {
380
        if ($this->noadmin_sizecheck) {
381
            return true;
382
        }
383
        if ($this->mediaSize > $this->maxFileSize) {
384
            return false;
385
        }
386
387
        return true;
388
    }
389
390
    /**
391
     * Is the picture the right width?
392
     *
393
     * @param $dimension
394
     *
395
     * @return bool
396
     */
397
    public function checkMaxWidth($dimension)
398
    {
399
        if (!isset($this->maxWidth)) {
400
            return true;
401
        }
402
        if ($dimension > $this->maxWidth) {
403
            return false;
404
        }
405
406
        return true;
407
    }
408
409
    /**
410
     * Is the picture the right height?
411
     *
412
     * @param $dimension
413
     *
414
     * @return bool
415
     */
416
    public function checkMaxHeight($dimension)
417
    {
418
        if (!isset($this->maxHeight)) {
419
            return true;
420
        }
421
        if ($dimension > $this->maxWidth) {
422
            return false;
423
        }
424
425
        return true;
426
    }
427
428
    /**
429
     * Is the file the right Mime type
430
     *
431
     * (is there a right type of mime? ;-)
432
     *
433
     * @return bool
434
     */
435
    public function checkMimeType()
436
    {
437
        return !(count($this->allowedMimeTypes) > 0 && !in_array($this->mediaType, $this->allowedMimeTypes));
438
    }
439
440
    /**
441
     * Add an error
442
     *
443
     * @param string $error
444
     */
445
    public function setErrors($error)
446
    {
447
        $this->errors[] = trim($error);
448
    }
449
450
    /**
451
     * Get generated errors
452
     *
453
     * @param bool $ashtml Format using HTML?
454
     *
455
     * @return array |string    Array of array messages OR HTML string
0 ignored issues
show
Documentation introduced by
Should the return type not be array|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
456
     */
457
    public function getErrors($ashtml = true)
458
    {
459
        if (!$ashtml) {
460
            return $this->errors;
461
        } else {
462
            $ret = '';
463
            if (count($this->errors) > 0) {
464
                $ret = _AM_WFL_ERRORSRETURNUPLOAD;
465
                foreach ($this->errors as $error) {
466
                    $ret .= $error . '<br>';
467
                }
468
            }
469
470
            return $ret;
471
        }
472
    }
473
}
474