Initial commit
Couldnt keep the history unfortunately.
This commit is contained in:
@@ -0,0 +1,282 @@
|
||||
<?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
|
||||
Reference in New Issue
Block a user