271 lines
9.2 KiB
PHP
271 lines
9.2 KiB
PHP
<?php
|
|
|
|
namespace Hura8\System\Controller;
|
|
|
|
use Hura8\Interfaces\AppResponse;
|
|
use Hura8\Interfaces\FileHandleInfo;
|
|
use Hura8\Interfaces\FileHandleResponse;
|
|
use Hura8\System\FileSystem;
|
|
use Hura8\System\HuraImage;
|
|
use Hura8\System\IDGenerator;
|
|
use League\MimeTypeDetection\FinfoMimeTypeDetector;
|
|
|
|
/**
|
|
* @date 16-Jan-2024
|
|
* @description based-class to handling files, this is used for :
|
|
* - FileUpload class
|
|
* - CopyFileFromUrl class
|
|
* ...
|
|
*
|
|
*/
|
|
|
|
abstract class bFileHandle
|
|
{
|
|
|
|
static $image_extensions = array(".jpg",".jpeg",".gif", ".png", ".webp", '.avif', ".ico");
|
|
|
|
protected $permit_file_extensions = array(
|
|
".jpg",".jpeg",".gif", ".png", ".webp", '.avif',
|
|
".doc", ".docx",".xls",".xlsx", ".ppt", ".pdf",
|
|
".rar", ".zip",
|
|
//".avi",".mov",".mpg", ".wmv", ".mpeg",
|
|
//".mp3",".mp4", ".ogg", ".oga", ".wav", ".wma"
|
|
);
|
|
|
|
protected $permit_mine_types = array(
|
|
'image/jpeg', 'image/png','image/gif', 'image/webp', 'image/avif',
|
|
|
|
// zip files
|
|
'application/zip', 'application/x-zip-compressed', 'multipart/x-zip', 'application/x-compressed',
|
|
);
|
|
|
|
// full system's path where the file will be finally stored or it's currently there for processing
|
|
// example: /var/www/html/domain.com/public_html/media/product/
|
|
protected $target_dir = "";
|
|
|
|
// directory appears to the public
|
|
// example: /media/product/
|
|
protected $public_dir = "";
|
|
|
|
protected $tmp_dir = ROOT_DIR . "/var/tmp_upload/"; // system tmp dir to store uploaded file for security examination before moving to target_dir
|
|
protected $tmp_folder = ""; // tmp folder per session in the $tmp_dir to hold user's files. This folder will be removed when operation is complete
|
|
|
|
protected $setup_success = false;
|
|
|
|
/**
|
|
* @param string $target_dir will be created if not exist i.e. media/product/ or media/user_upload/date('d-m-Y')
|
|
* @param ?array $permit_file_extensions null to allow all default file extensions
|
|
*/
|
|
public function __construct( string $target_dir, ?array $permit_file_extensions = null ) {
|
|
|
|
if(is_array($permit_file_extensions)) {
|
|
$this->permit_file_extensions = $permit_file_extensions;
|
|
}
|
|
|
|
if($target_dir) {
|
|
$this->target_dir = PUBLIC_DIR . DIRECTORY_SEPARATOR. $target_dir;
|
|
$this->public_dir = "/".$target_dir;
|
|
}
|
|
|
|
$setup_res = $this->setUp();
|
|
if($setup_res->getStatus() == AppResponse::SUCCESS) {
|
|
$this->setup_success = true;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @description (rename if set) and resize file
|
|
* @param string $name
|
|
* @param string $new_name
|
|
* @param array $resized_sizes [] array('small' => ['width' => 100, 'height' => 100], 'large' => ['width' => 200, 'height' => 200] )
|
|
* @return array|false
|
|
*/
|
|
public function resizeFile(string $name, string $new_name = '', array $resized_sizes = []) {
|
|
|
|
if($new_name) {
|
|
$renamed = $this->renameFile($name, $new_name);
|
|
if(!$renamed) {
|
|
return false;
|
|
}
|
|
|
|
//$file_name = $renamed['file_name'];
|
|
//$public_path = $renamed['public_path'];
|
|
$local_path = $renamed['local_path'];
|
|
}else{
|
|
//$file_name = $name;
|
|
//$public_path = $this->public_dir . "/".$name;
|
|
$local_path = $this->target_dir . "/" . $name;
|
|
}
|
|
|
|
$objHuraImage = new HuraImage();
|
|
|
|
list(, $expected_files, ) = $objHuraImage->resize($local_path, $resized_sizes);
|
|
|
|
return $expected_files;
|
|
}
|
|
|
|
|
|
/**
|
|
* @description we can rename the uploaded file to new file name, for example: we want product's image have the format [PRODUCT_ID]-name
|
|
* @param string $name
|
|
* @param string $new_name
|
|
* @return array | false
|
|
*/
|
|
public function renameFile(string $name, string $new_name) {
|
|
if(@rename($this->target_dir . "/" . $name, $this->target_dir . "/" . $new_name)){
|
|
return [
|
|
"file_name" => $new_name,
|
|
"public_path" => $this->public_dir . "/".$new_name,
|
|
"local_path" => $this->target_dir . "/" . $new_name,
|
|
];
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// public utility
|
|
public static function getFileExtension($file_name) {
|
|
return strtolower(strrchr($file_name,"."));
|
|
}
|
|
|
|
|
|
/**
|
|
* @description run clean up after finish using the FileHandle instance
|
|
*/
|
|
public function cleanUp() {
|
|
if($this->tmp_folder) {
|
|
FileSystem::removeDir($this->tmp_folder);
|
|
}
|
|
}
|
|
|
|
protected function processFile(
|
|
$original_file_name,
|
|
$original_file_tmp_name, // temporary uploaded file as in case of $_FILES[$input_file_name]["tmp_name"]
|
|
$fixed_file_name="",
|
|
$max_file_size=0
|
|
) : FileHandleResponse {
|
|
|
|
if(!$original_file_name) {
|
|
return new FileHandleResponse(AppResponse::ERROR, 'no file', null);
|
|
}
|
|
|
|
$file_size = filesize($original_file_tmp_name);
|
|
if($max_file_size > 0 && $max_file_size < $file_size) {
|
|
return new FileHandleResponse(AppResponse::ERROR, 'Size is too large: '.round($file_size/1000).'KB', null);
|
|
}
|
|
|
|
//validate extension
|
|
$file_ext = self::getFileExtension($original_file_name);
|
|
|
|
if(!in_array($file_ext, $this->permit_file_extensions)) {
|
|
return new FileHandleResponse(AppResponse::ERROR, "Type ".$file_ext." is not allowed!", null);
|
|
}
|
|
|
|
$file_name = substr($original_file_name, 0, strrpos($original_file_name,"."));
|
|
$file_name = preg_replace("/[^a-z0-9_-]/i","", $file_name);
|
|
$file_name = substr($file_name, 0, 50); // max- length
|
|
$clean_file_name = ($fixed_file_name) ?: $file_name . $file_ext;
|
|
|
|
$tmp_file_path = $this->tmp_folder . "/". $clean_file_name;
|
|
$return_data = null;
|
|
|
|
//debug_var([$original_data, $tmp_file_path]);
|
|
if(@rename($original_file_tmp_name, $tmp_file_path)){
|
|
|
|
$is_file_image = (in_array($file_ext, static::$image_extensions ));
|
|
|
|
if($is_file_image) {
|
|
list($width, $height) = getimagesize($tmp_file_path);
|
|
}else{
|
|
$width = 0;
|
|
$height = 0;
|
|
}
|
|
|
|
$detector = new FinfoMimeTypeDetector();
|
|
$mimeType = $detector->detectMimeTypeFromPath($tmp_file_path);
|
|
|
|
// if image, we re-create and optimize it
|
|
if($is_file_image) {
|
|
if(in_array($mimeType, $this->permit_mine_types)) {
|
|
$objHuraImage = new HuraImage();
|
|
if($objHuraImage->create($tmp_file_path, $this->target_dir . DIRECTORY_SEPARATOR . $clean_file_name)){
|
|
$return_data = new FileHandleInfo([
|
|
"file_name" => $clean_file_name,
|
|
"public_path" => $this->public_dir . "/".$clean_file_name,
|
|
"local_path" => $this->target_dir . "/" . $clean_file_name,
|
|
"mime_type" => $mimeType,
|
|
"file_size" => $file_size,
|
|
"file_ext" => $file_ext,
|
|
"width" => $width,
|
|
"height" => $height,
|
|
]);
|
|
}
|
|
}
|
|
|
|
}elseif(@rename ($tmp_file_path, $this->target_dir . DIRECTORY_SEPARATOR . $clean_file_name )) {
|
|
$return_data = new FileHandleInfo([
|
|
"file_name" => $clean_file_name,
|
|
"public_path" => $this->public_dir . "/".$clean_file_name,
|
|
"local_path" => $this->target_dir . "/" . $clean_file_name,
|
|
"mime_type" => $mimeType,
|
|
"file_size" => $file_size,
|
|
"file_ext" => $file_ext,
|
|
"width" => 0,
|
|
"height" => 0,
|
|
]);
|
|
}
|
|
}
|
|
|
|
// delete tmp file on server
|
|
if(file_exists($original_file_tmp_name)) {
|
|
@unlink($original_file_tmp_name);
|
|
}
|
|
|
|
if($return_data) {
|
|
return new FileHandleResponse(AppResponse::SUCCESS, 'Success', $return_data);
|
|
}
|
|
|
|
return new FileHandleResponse(AppResponse::ERROR, 'Unknown', null);
|
|
}
|
|
|
|
|
|
protected function setUp(): AppResponse
|
|
{
|
|
// check target dir
|
|
if($this->target_dir && !is_dir($this->target_dir)) {
|
|
@mkdir($this->target_dir, 0755, true);
|
|
}
|
|
|
|
if(!file_exists($this->target_dir)) {
|
|
return new AppResponse(AppResponse::ERROR, $this->target_dir.' not exists');
|
|
}
|
|
|
|
// create tmp_folder to upload file to
|
|
$this->tmp_folder = $this->create_tmp_folder();
|
|
if(!$this->tmp_folder) {
|
|
return new AppResponse(AppResponse::ERROR, "Check ".$this->tmp_dir." and make sure it exists and writable");
|
|
}
|
|
|
|
return new AppResponse(AppResponse::SUCCESS);
|
|
}
|
|
|
|
|
|
protected function create_tmp_folder() : ?string {
|
|
$tmp_folder = $this->tmp_dir . IDGenerator::createStringId(5);
|
|
if(!@mkdir($tmp_folder, 0777, true)) {
|
|
return null;
|
|
}
|
|
|
|
// retest
|
|
if(!$tmp_folder || !is_dir($tmp_folder)) {
|
|
return null;
|
|
}
|
|
|
|
return $tmp_folder;
|
|
}
|
|
|
|
|
|
}
|