Passed
Pull Request — Comments (#65)
by Stone
05:43 queued 02:26
created

ImageUpload::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace App\Controllers\Ajax;
4
5
use Cocur\Slugify\Slugify;
6
use Core\AjaxController;
7
use Core\Config;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, App\Controllers\Ajax\Config. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
8
use Core\Container;
9
10
class ImageUpload extends AjaxController
11
{
12
    /**
13
     * @var string the image upload folder, must be writable
14
     */
15
    private $imageFolder;
16
    private $configFolder;
17
    private $userFolder;
18
19
    public function __construct(Container $container)
20
    {
21
        parent::__construct($container);
22
        $this->imageFolder = Config::UPLOADED_IMAGES;
23
        $this->configFolder = Config::CONFIG_IMAGES;
24
        $this->userFolder = Config::USER_IMAGES;
25
    }
26
27
    /**
28
     * check if the image name is valid
29
     * @param $image string filename to check
30
     * @return bool if image name is valid
31
     *
32
     */
33
    private function isImageValid($image): bool
34
    {
35
        // Sanitize input
36
        if (preg_match("/([^\w\s\d\-_~,;:\[\]\(\).])|([\.]{2,})/", $image)) {
37
            return false;
38
        }
39
40
        // Verify extension
41
        if (!in_array(strtolower(pathinfo($image, PATHINFO_EXTENSION)), array("gif", "jpg", "png"))) {
42
            return false;
43
        }
44
45
        return true;
46
    }
47
48
    /**
49
     * Check if file exists and add a number to avoid overwrite
50
     * @param string $folder destination folder
51
     * @param string $file destination filename
52
     * @return string the unique file name
53
     */
54
    private function getFilename(string $folder, string $file): string
55
    {
56
        //slugify the file name to avoid security errors or bugs with special characters.
57
        $fileName = pathinfo($file, PATHINFO_FILENAME );
58
        $fileExtension = pathinfo($file, PATHINFO_EXTENSION );
59
        $slugify = new Slugify();
60
        $fileName = $slugify->slugify($fileName);
61
        //if the filename has only special chars, the slugify will be empty, create a unique ID
62
        if($fileName ==="")
63
        {
64
            $fileName = uniqid();
65
        }
66
        $file = $fileName.".".$fileExtension;
67
        $fileUrl = $folder . $file;
68
        $docRoot = $this->request->getDocumentRoot();
69
        $filePath = $docRoot . "/public/" . $fileUrl;
70
        if (file_exists($filePath) !== 1) {
71
            $fileNum = 0;
72
            while (file_exists($filePath)) {
73
                $fileUrl = $folder . $fileNum . "_" . $file;
74
                $filePath = $docRoot . "/public/" . $fileUrl;
75
                $fileNum += 1;
76
            }
77
        }
78
        return $fileUrl;
79
    }
80
81
    /**
82
     * @param $tempFile array
83
     * @param $folder string
84
     */
85
    private function fileInputUpload(array $tempFile, string $folder)
86
    {
87
        if (is_uploaded_file($tempFile['tmp_name'])) {
88
            if (!$this->isImageValid($tempFile['name'])) {
89
                echo json_encode(array('error' => 'Invalid name or file extension'));
90
                return;
91
            }
92
93
            $filetowrite = $this->getFilename($folder, basename($tempFile['name']));
94
            move_uploaded_file($tempFile['tmp_name'], $filetowrite);
95
96
            // Respond to the successful upload with JSON.
97
            echo json_encode(array('location' => $filetowrite));
98
        } else {
99
            // Notify editor that the upload failed
100
            echo json_encode(array('error' => 'Upload failed, file might be too big'));
101
102
        }
103
104
    }
105
106
    /**
107
     * Upload images from TinyMCE
108
     * grabbed from https://www.codexworld.com/tinymce-upload-image-to-server-using-php/
109
     */
110
    public function tinymceUpload()
111
    {
112
        //security checks, only admins can upload images to posts
113
        $this->onlyAdmin();
114
        $this->onlyPost();
115
116
        $tempFile = $this->request->getUploadedFiles();
117
118
        //need to clean up
119
        if (is_uploaded_file($tempFile['tmp_name'])) {
120
            if (!$this->isImageValid($tempFile['name'])) {
121
                header("HTTP/1.1 400 Invalid file name or file extension.");
122
                return;
123
            }
124
125
            $filetowrite = $this->getFilename($this->imageFolder, basename($tempFile['name']));
126
            move_uploaded_file($tempFile['tmp_name'], $filetowrite);
127
128
            // Respond to the successful upload with JSON.
129
            echo json_encode(array('location' => $filetowrite));
130
        } else {
131
            // Notify editor that the upload failed
132
            header("HTTP/1.1 500 Server Error");
133
        }
134
    }
135
136
137
    /**
138
     * Upload for the file input in the configuration
139
     */
140
    public function fileInputConfigUpload()
141
    {
142
        //security checks, only admins can upload images to config
143
        $this->onlyAdmin();
144
        $this->onlyPost();
145
        $tempFile = $this->request->getUploadedFiles();
146
147
        $this->fileInputUpload($tempFile, $this->configFolder);
148
149
    }
150
151
    /**
152
     * Upload for the file input in the configuration
153
     */
154
    public function fileInputPostUpload()
155
    {
156
        //security checks, only admins can upload images to config
157
        $this->onlyAdmin();
158
        $this->onlyPost();
159
        $tempFile = $this->request->getUploadedFiles();
160
        $this->fileInputUpload($tempFile, $this->imageFolder);
161
    }
162
163
    /**
164
     * Upload for the file input in the configuration
165
     */
166
    public function fileInputUserUpload()
167
    {
168
        //security checks, only admins can upload images to config
169
        $this->onlyUser();
170
        $this->onlyPost();
171
        $tempFile = $this->request->getUploadedFiles();
172
        $this->fileInputUpload($tempFile, $this->userFolder);
173
    }
174
175
}