Completed
Pull Request — master (#5)
by Michael
01:37
created

SwUploadHandler::create_thumbnail()   B

Complexity

Conditions 10
Paths 31

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
nc 31
nop 1
dl 0
loc 40
rs 7.6666
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
2
3
namespace XoopsModules\Smallworld;
4
5
/**
6
 * You may not change or alter any portion of this comment or credits
7
 * of supporting developers from this source code or any supporting source code
8
 * which is considered copyrighted (c) material of the original comment or credit authors.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
 */
14
15
/**
16
 * SmallWorld
17
 *
18
 * @copyright    The XOOPS Project (https://xoops.org)
19
 * @copyright    2011 Culex
20
 * @license      GNU GPL (http://www.gnu.org/licenses/gpl-2.0.html/)
21
 * @package      SmallWorld
22
 * @since        1.0
23
 * @author       Michael Albertsen (http://culex.dk) <[email protected]>
24
 */
25
class SwUploadHandler
26
{
27
    private $upload_dir;
28
    private $upload_url;
29
    private $thumbnails_dir;
30
    private $thumbnails_url;
31
    private $thumbnail_max_width;
32
    private $thumbnail_max_height;
33
    private $field_name;
34
35
    /**
36
     * SwUploadHandler constructor.
37
     * @param $options
38
     */
39
    public function __construct($options)
40
    {
41
        $this->upload_dir           = $options['upload_dir'];
42
        $this->upload_url           = $options['upload_url'];
43
        $this->thumbnails_dir       = $options['thumbnails_dir'];
44
        $this->thumbnails_url       = $options['thumbnails_url'];
45
        $this->thumbnail_max_width  = $options['thumbnail_max_width'];
46
        $this->thumbnail_max_height = $options['thumbnail_max_height'];
47
        $this->field_name           = $options['field_name'];
48
    }
49
50
    /**
51
     * @param $file_name
52
     * @return null|\stdClass
53
     */
54
    private function get_file_object($file_name)
55
    {
56
        $file_path = $this->upload_dir . $file_name;
57
        if (is_file($file_path) && '.' !== $file_name[0] && 'index.html' !== $file_name && 'Thumbs.db' !== $file_name) {
58
            $file            = new \stdClass();
59
            $file->name      = $file_name;
60
            $file->size      = filesize($file_path);
61
            $file->url       = $this->upload_url . rawurlencode($file->name);
62
            $file->thumbnail = is_file($this->thumbnails_dir . $file_name) ? $this->thumbnails_url . rawurlencode($file->name) : null;
63
64
            return $file;
65
        }
66
67
        return null;
68
    }
69
70
    /**
71
     * @param $file_name
72
     * @return bool
73
     */
74
    private function create_thumbnail($file_name)
75
    {
76
        $file_path      = $this->upload_dir . $file_name;
77
        $thumbnail_path = $this->thumbnails_dir . $file_name;
78
        list($img_width, $img_height) = @getimagesize($file_path);
79
        if (!$img_width || !$img_height) {
80
            return false;
81
        }
82
        $scale = min($this->thumbnail_max_width / $img_width, $this->thumbnail_max_height / $img_height);
83
        if ($scale > 1) {
84
            $scale = 1;
85
        }
86
        $thumbnail_width  = $img_width * $scale;
87
        $thumbnail_height = $img_height * $scale;
88
        $thumbnail_img    = @imagecreatetruecolor($thumbnail_width, $thumbnail_height);
89
        switch (mb_strtolower(mb_substr(mb_strrchr($file_name, '.'), 1))) {
90
            case 'jpg':
91
            case 'jpeg':
92
                $src_img         = @imagecreatefromjpeg($file_path);
93
                $write_thumbnail = 'imagejpeg';
94
                break;
95
            case 'gif':
96
                $src_img         = @imagecreatefromgif($file_path);
97
                $write_thumbnail = 'imagegif';
98
                break;
99
            case 'png':
100
                $src_img         = @imagecreatefrompng($file_path);
101
                $write_thumbnail = 'imagepng';
102
                break;
103
            default:
104
                $src_img = $write_thumbnail = null;
105
        }
106
        $success = $src_img && @imagecopyresampled($thumbnail_img, $src_img, 0, 0, 0, 0, $thumbnail_width, $thumbnail_height, $img_width, $img_height)
107
                   && $write_thumbnail($thumbnail_img, $thumbnail_path);
108
        // Free up memory (imagedestroy does not delete files):
109
        @imagedestroy($src_img);
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...
110
        @imagedestroy($thumbnail_img);
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...
111
112
        return $success;
113
    }
114
115
    //function to return file extension from a path or file name
116
117
    /**
118
     * @param $path
119
     * @return mixed
120
     */
121
    public function getFileExtension($path)
122
    {
123
        $parts = pathinfo($path);
124
125
        return $parts['extension'];
126
    }
127
128
    /**
129
     * @param $uploaded_file
130
     * @param $name
131
     * @param $size
132
     * @param $type
133
     * @param $error
134
     * @return stdClass
0 ignored issues
show
Documentation introduced by
Should the return type not be \stdClass?

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...
135
     */
136
    private function handle_file_upload($uploaded_file, $name, $size, $type, $error)
137
    {
138
        global $xoopsUser;
139
        $file   = new \stdClass();
140
        $db     = new SwDatabase();
141
        $userid = $xoopsUser->getVar('uid');
142
143
        // Generate new name for file
144
        //$file->name = basename(stripslashes($name));
145
        $file->name = time() . mt_rand(0, 99999) . '.' . $this->getFileExtension($name);
146
        $file->size = (int)$size;
147
        $file->type = $type;
148
        $img        = XOOPS_URL . '/uploads/albums_smallworld/' . $userid . '/' . $file->name;
149
150
        // Save to database for later use
151
        $db->saveImage("'', '" . $userid . "', '" . $file->name . "', '" . addslashes($img) . "', '" . time() . "', ''");
152
153
        if (!$error && $file->name) {
154
            if ('.' === $file->name[0]) {
155
                $file->name = mb_substr($file->name, 1);
156
            }
157
            $file_path   = $this->upload_dir . $file->name;
158
            $append_file = is_file($file_path) && $file->size > filesize($file_path);
159
            clearstatcache();
160 View Code Duplication
            if ($uploaded_file && is_uploaded_file($uploaded_file)) {
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...
161
                // multipart/formdata uploads (POST method uploads)
162
                if ($append_file) {
163
                    file_put_contents($file_path, fopen($uploaded_file, 'rb'), FILE_APPEND);
164
                } else {
165
                    move_uploaded_file($uploaded_file, $file_path);
166
                }
167
            } else {
168
                // Non-multipart uploads (PUT method support)
169
                file_put_contents($file_path, fopen('php://input', 'rb'), $append_file ? FILE_APPEND : 0);
170
            }
171
            $file_size = filesize($file_path);
172
            if ($file_size === $file->size) {
173
                $file->url       = $this->upload_url . rawurlencode($file->name);
174
                $file->thumbnail = $this->create_thumbnail($file->name) ? $this->thumbnails_url . rawurlencode($file->name) : null;
175
            }
176
            $file->size = $file_size;
177
        } else {
178
            $file->error = $error;
179
        }
180
181
        return $file;
182
    }
183
184
    public function get()
185
    {
186
        $file_name = isset($_REQUEST['file']) ? basename(stripslashes($_REQUEST['file'])) : null;
187
        if (null !== $file_name) {
188
            $info = $this->get_file_object($file_name);
189
        } else {
190
            $info = array_values(array_filter(array_map([$this, 'get_file_object'], scandir($this->upload_dir, SCANDIR_SORT_NONE))));
191
        }
192
        header('Cache-Control: no-cache, must-revalidate');
193
        header('Content-type: application/json');
194
        echo json_encode($info);
195
    }
196
197
    public function post()
198
    {
199
        $upload = isset($_FILES[$this->field_name]) ? $_FILES[$this->field_name] : [
200
            'tmp_name' => null,
201
            'name'     => null,
202
            'size'     => null,
203
            'type'     => null,
204
            'error'    => null,
205
        ];
206
        if (is_array($upload['tmp_name']) && count($upload['tmp_name']) > 1) {
207
            $info = [];
208
            foreach ($upload['tmp_name'] as $index => $value) {
209
                $info[] = $this->handle_file_upload($upload['tmp_name'][$index], $upload['name'][$index], $upload['size'][$index], $upload['type'][$index], $upload['error'][$index]);
210
            }
211
        } else {
212
            if (is_array($upload['tmp_name'])) {
213
                $upload = [
214
                    'tmp_name' => $upload['tmp_name'][0],
215
                    'name'     => $upload['name'][0],
216
                    'size'     => $upload['size'][0],
217
                    'type'     => $upload['type'][0],
218
                    'error'    => $upload['error'][0],
219
                ];
220
            }
221
            $info = $this->handle_file_upload(
222
                $upload['tmp_name'],
223
                isset($_SERVER['HTTP_X_FILE_NAME']) ? $_SERVER['HTTP_X_FILE_NAME'] : $upload['name'],
224
                isset($_SERVER['HTTP_X_FILE_SIZE']) ? $_SERVER['HTTP_X_FILE_SIZE'] : $upload['size'],
225
                isset($_SERVER['HTTP_X_FILE_TYPE']) ? $_SERVER['HTTP_X_FILE_TYPE'] : $upload['type'],
226
                $upload['error']
227
            );
228
        }
229
        if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && 'XMLHttpRequest' === $_SERVER['HTTP_X_REQUESTED_WITH']) {
230
            header('Content-type: application/json');
231
        } else {
232
            header('Content-type: text/plain');
233
        }
234
        echo json_encode($info);
235
    }
236
237
    public function delete()
238
    {
239
        global $xoopsUser;
240
        $userid    = $xoopsUser->getVar('uid');
241
        $db        = new SwDatabase();
242
        $file_name = isset($_REQUEST['file']) ? basename(stripslashes($_REQUEST['file'])) : null;
243
        $file_path = $this->upload_dir . $file_name;
244
        $img       = XOOPS_URL . '/uploads/albums_smallworld/' . $userid . '/' . $file_name;
0 ignored issues
show
Unused Code introduced by
$img 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...
245
246
        // Delete file based on user and filename
247
        $db->DeleteImage($userid, $file_name);
248
        $db->DeleteImage($userid, 'Thumbs.db');
249
        $thumbnail_path = $this->thumbnails_dir . $file_name;
250
        $success        = is_file($file_path) && '.' !== $file_name[0] && unlink($file_path);
251
        if ($success && is_file($thumbnail_path)) {
252
            unlink($thumbnail_path);
253
        }
254
        header('Content-type: application/json');
255
        echo json_encode($success);
256
    }
257
}
258