You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
282 lines
6.4 KiB
282 lines
6.4 KiB
<?php |
|
|
|
namespace App\Classes; |
|
|
|
use App\Model\MediaModel; |
|
|
|
class Media { |
|
|
|
public static $pagination = 20; |
|
public static $directory = 'media'; |
|
|
|
public static function errorMessage(int $errorCode): string |
|
{ |
|
// https://www.php.net/manual/en/features.file-upload.errors.php |
|
$errorMessages = [ |
|
0 => 'Uploaded with success.', |
|
1 => 'Uploaded file exceeds the maximum upload size. (1)', // php.ini |
|
2 => 'Uploaded file exceeds the maximum upload size. (2)', // HTML Form |
|
3 => 'Uploaded file was only partially uploaded.', |
|
4 => 'No file was uploaded.', |
|
6 => 'Missing a temporary folder.', |
|
7 => 'Failed to write file to disk.', |
|
8 => 'A PHP extension stopped the file upload.', |
|
9 => 'User was not logged in.', |
|
10 => 'Missing media folder.', |
|
11 => 'Uploaded file has invalid MIME type.', |
|
12 => 'Uploaded file exceeds the maximum upload size. (3)', // Media.php |
|
13 => 'Uploaded file already exists.', |
|
14 => 'DB entry creation failed.', |
|
15 => 'Moving file from temporary location failed.', |
|
]; |
|
|
|
return $errorMessages[$errorCode]; |
|
} |
|
|
|
public static function deleteMedia(int $id): bool |
|
{ |
|
if (!User::check()) { |
|
return false; |
|
} |
|
|
|
$media = MediaModel::find($id); |
|
|
|
if (!$media->exists()) { |
|
return false; |
|
} |
|
|
|
// Delete file |
|
$file = self::$directory . '/' . $media->filename . '.' . $media->extension; |
|
if (file_exists($file)) { |
|
unlink($file); |
|
} |
|
|
|
return $media->delete(); |
|
} |
|
|
|
public static function uploadMedia(bool $overwrite = false): int |
|
{ |
|
// Check if User is logged in |
|
if (!User::check()) { |
|
return 9; |
|
} |
|
|
|
// Check if "media" directory exists |
|
if (!is_dir(self::$directory) && !mkdir(self::$directory, 0755)) { |
|
return 10; |
|
} |
|
|
|
$files = $_FILES['file']; |
|
|
|
// Check for file errors |
|
foreach ($files['error'] as $error) { |
|
if ($error != 0) { |
|
return $error; |
|
} |
|
} |
|
|
|
if (!Media::checkMimeType($files['type'], $files['tmp_name'])) { |
|
return 11; |
|
} |
|
|
|
if (!Media::checkSize($files['size'])) { |
|
return 12; |
|
} |
|
|
|
// Append random string to filename that already exists |
|
$nameExt = Media::duplicateName($files['name'], $overwrite); |
|
|
|
// Check if the file already exists |
|
$hash = Media::hashExists($files['tmp_name']); |
|
if (!$hash[0]) { |
|
return 13; |
|
} |
|
|
|
$count = count($files['name']); |
|
for ($i = 0; $i < $count; $i++) { |
|
$filename = $nameExt[0][$i]; |
|
$extension = $nameExt[1][$i]; |
|
$md5 = $hash[1][$i]; |
|
$tmpName = $files['tmp_name'][$i]; |
|
|
|
// Store record |
|
$media = MediaModel::create([ |
|
'filename' => $filename, |
|
'extension' => $extension, |
|
'md5' => $md5, |
|
]); |
|
|
|
if (!$media->exists()) { |
|
return 14; |
|
} |
|
|
|
// Store image |
|
$name = self::$directory . '/'. $filename . '.' . $extension; |
|
if (!move_uploaded_file($tmpName, $name)) { |
|
return 15; |
|
} |
|
|
|
// After storing successfully, remove old entries with duplicate names |
|
if ($overwrite) { |
|
Media::destroyDuplicates($filename, $extension); |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
//-------------------------------------// |
|
|
|
private static function checkMimeType(array $fileTypes, array $fileTmpNames): bool |
|
{ |
|
$allowedMimeType = [ |
|
// .tar.gz |
|
'application/gzip', |
|
'application/json', |
|
'application/octet-stream', |
|
'application/pdf', |
|
'application/sql', |
|
// .tar.xz |
|
'application/x-xz', |
|
'application/xml', |
|
'application/zip', |
|
'audio/mp3', |
|
'audio/mpeg', |
|
'audio/ogg', |
|
'audio/wav', |
|
'audio/webm', |
|
'image/jpg', |
|
'image/jpeg', |
|
'image/png', |
|
'image/gif', |
|
'text/plain', |
|
'text/csv', |
|
'text/xml', |
|
'video/mp4', |
|
'video/webm', |
|
// .doc |
|
'application/msword', |
|
// .xls |
|
'application/vnd.ms-excel', |
|
// .ppt |
|
'application/vnd.ms-powerpoint', |
|
// .docx |
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document', |
|
// .xlsx |
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', |
|
// .pptx |
|
'application/vnd.openxmlformats-officedocument.presentationml.presentation', |
|
]; |
|
|
|
// Files type check |
|
$count = count($fileTypes); |
|
for ($i = 0; $i < $count; $i++) { |
|
if ($fileTypes[$i] != mime_content_type($fileTmpNames[$i]) || |
|
!in_array($fileTypes[$i], $allowedMimeType)) { |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
private static function checkSize(array $fileSizes): bool |
|
{ |
|
// Files should not exceed 10MiB |
|
foreach ($fileSizes as $fileSize) { |
|
if ($fileSize > 10485760) { |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
private static function duplicateName(array $filenames, bool $overwrite): array |
|
{ |
|
// Split name from extension |
|
$names = []; |
|
$extensions = []; |
|
foreach ($filenames as $name) { |
|
$dotPos = strrpos($name, '.'); |
|
$names[] = substr($name, 0, $dotPos); |
|
$extensions[] = substr($name, $dotPos + 1); |
|
} |
|
|
|
// Early return if names are specified to be overwritten |
|
if ($overwrite) { |
|
return [$names, $extensions]; |
|
} |
|
|
|
// Get duplicate filenames |
|
$in1 = str_repeat('?, ', count($names) - 1) . '?'; |
|
$in2 = str_repeat('?, ', count($extensions) - 1) . '?'; |
|
$data = array_merge($names, $extensions); |
|
$duplicates = MediaModel::selectAll( |
|
'*', "WHERE filename IN ($in1) AND extension IN ($in2)", $data, '?' |
|
); |
|
|
|
foreach ($filenames as $key => $filename) { |
|
$hasDuplicate = false; |
|
foreach ($duplicates as $duplicate) { |
|
if ($filename == $duplicate['filename'] . '.' . $duplicate['extension']) { |
|
$hasDuplicate = true; |
|
break; |
|
} |
|
} |
|
|
|
// Append to filename if there are duplicates |
|
if ($hasDuplicate) { |
|
$names[$key] = $names[$key] . '-' . _randomStr(5); |
|
} |
|
} |
|
|
|
return [$names, $extensions]; |
|
} |
|
|
|
private static function hashExists(array $fileTmpNames): array |
|
{ |
|
$md5 = []; |
|
foreach ($fileTmpNames as $tmpName) { |
|
$md5[] = md5_file($tmpName); |
|
} |
|
|
|
// If exact file already exists |
|
$in = str_repeat('?, ', count($md5) - 1) . '?'; |
|
$exists = MediaModel::selectAll( |
|
'*', "WHERE md5 IN ($in)", $md5, '?' |
|
); |
|
|
|
if (!empty($exists)) { |
|
return [false]; |
|
} |
|
|
|
return [true, $md5]; |
|
} |
|
|
|
private static function destroyDuplicates(string $filename, string $extension): void |
|
{ |
|
$media = MediaModel::selectAll( |
|
'*', 'WHERE filename = ? AND extension = ? ORDER BY id ASC', |
|
[$filename, $extension], '?' |
|
); |
|
|
|
if (!_exists($media)) { |
|
return; |
|
} |
|
|
|
foreach ($media as $key => $value) { |
|
|
|
// Dont delete the new entry |
|
if ($key === array_key_last($media)) { |
|
return; |
|
} |
|
|
|
MediaModel::destroy($value['id']); |
|
} |
|
} |
|
|
|
} |
|
|
|
// @Todo |
|
// - If a file fails to store in the loop, destruct all files of that request, by tracking all IDs
|
|
|