vendor\klio\klio-bundle\src\Files\File.php line 121
<?php
namespace Klio\KlioBundle\Files;
use finfo;
use Klio\KlioBundle\Database\DB;
use Klio\KlioBundle\Security\Check;
use Klio\KlioBundle\Security\Response;
/**
* Cette classe permet de gérer des url locales ou distantes de fichiers
* soit pour les afficher inline, soitr pour les télécharger
*/
class File
{
public bool|array $row = false;
public bool|string $id = false;
public string $url = "";
private string $path = "";
private string $name = "";
private string $extension = "";
private string $mime = "";
private int $size = 0;
private string $buffer = "";
private string $mode = "";
private string $hash = "";
private string $link = "";
private string $folder = "";
public function __construct($file)
{
if (is_array($file) and $file['files__num']) $this->row = $file;
else $this->url = $file;
}
public function stream(): void
{
if (ob_get_level()) ob_clean();
header('Expires: 0');
header('Cache-Control: must-revalidate');
header("Content-Type: " . $this->getMime());
header('Content-Disposition: inline; filename="' . $this->getName() . '"');
header("Content-Length: " . $this->getSize());
//open the file
$fd = fopen($this->getPath(), "rb");
while (!feof($fd)) {
print(fread($fd, 1024 * 1024)); // buffer de 1Mo
ob_flush();
flush();
}
fclose($fd);
exit();
}
public function getStreamUrl(): string
{
return '/f/' . $this->getLink() . '/' . $this->getName();
}
public function download()
{
if (ob_get_level()) ob_clean();
header('Content-Description: File Download');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $this->getName() . '"');
header("Content-Length: " . $this->getSize());
header('Cache-Control: must-revalidate');
//open the file
$fd = fopen($this->getPath(), "rb");
while (!feof($fd)) {
print(fread($fd, 1024 * 1024)); // buffer de 1Mo
ob_flush();
flush();
}
fclose($fd);
exit();
}
public function getDownloadUrl(): string
{
return '/d/' . $this->getLink() . '/' . $this->getName();
}
/**
* Get the value of hash
*
* @return string
*/
public function getHash(): string
{
return $this->hash;
}
/**
* Get the value of link
*
* @return string
*/
public function getLink(): string
{
if ($this->link) return $this->link;
if ($this->url or $this->row) $this->setPath();
return $this->link = $this->setLink();
}
private function setLink(): string
{
if ($this->link) return $this->link;
$this->setRow();
if ($this->row) return $this->link = $this->row['files__link'];
return $this->link = false;
}
public function getPath()
{
if ($this->path) return $this->path;
return $this->path = $this->setPath();
}
private function setPath(): string|bool
{
if ($this->path) return $this->path;
if ($this->row) return $this->path = $this->setPathFromRow();
if (stripos($this->url, '/f/') === 0) return $this->setPathFromUrl_F(); // gérer FW4 et FW7
if (stripos($this->url, '/d/') === 0) return $this->setPathFromUrl_D(); // gérer FW4 et FW7
//if (stripos($this->url, '/i/') === 0) return $this->getPathFromUrl(); // gérer FW4 et FW7
//if (stripos($this->url, '/p/') === 0) return $this->getPathFromUrl(); // gérer FW4 et FW7
// sinon, on cherche à récupérer les infos via l'url du construct si on a envoyé id, link ou hash
$this->setRow();
if ($this->row) return $this->path = $this->setPathFromRow();
return false;
}
private function setPathFromRow(): string
{
if (!$this->folder) $this->setFolder();
if (!$this->hash) $this->setHash();
if (!$this->folder) Response::Status404("Invalid File Folder", 'UploadedFile::getPathFromUrlF : ' . $this->folder);
if (!$this->hash) Response::Status404("Invalid File Hash", 'UploadedFile::getPathFromUrlF : ' . $this->hash);
return $this->path = __STORE__ . "/upload/" . $this->folder . "/" . substr($this->hash, 0, 3) . "/" . substr($this->hash, 3, 61) . "/" . substr($this->hash, 64);
}
private function setPathFromUrl_F(): string
{
// on enlève le /f/ du début
$url = substr($this->url, 3);
$parts = explode('/', $url);
$partsCount = count($parts);
$part1 = $parts[0];
// la premiere partie peut être le hash, ou le link
if (strlen($part1) == 66) $this->link = $part1;
else if (strlen($part1) == 128) $this->hash = $part1;
// si on a juste le link et pas le hash, il faut faire une requete
/*
$folder = substr($url, 0, strpos($url, '/'));
if (!$folder or $folder == "" or !preg_match('/^[a-zA-z0-9_]+$/', $folder)) Response::Status404("Invalid folder invalid.", $folder);
*/
if (!$this->folder) $this->setFolder();
if (!$this->hash) $this->setHash();
if (!$this->folder) Response::Status404("Invalid File Folder", 'UploadedFile::getPathFromUrlF : ' . $this->folder);
if (!$this->hash) Response::Status404("Invalid File Hash", 'UploadedFile::getPathFromUrlF : ' . $this->hash);
return $this->path = __STORE__ . "/upload/" . $this->folder . "/" . substr($this->hash, 0, 3) . "/" . substr($this->hash, 3, 61) . "/" . substr($this->hash, 64);
}
private function setPathFromUrl_D(): string
{
// on enlève le /d/ du début
$url = substr($this->url, 3);
$parts = explode('/', $url);
$partsCount = count($parts);
$part1 = $parts[0];
// la premiere partie peut être le hash, ou le link
if (strlen($part1) == 66) {
$this->link = $part1;
Check::uid($this->link, 66);
} else if (strlen($part1) == 128) {
$this->hash = $part1;
Check::uid($this->hash, 128);
}
// si on a juste le link et pas le hash, il faut faire une requete
if (isset($parts[1])) $this->name = $parts[1];
if (!$this->folder) $this->setFolder();
if (!$this->hash) $this->setHash();
if (!$this->folder) Response::Status404("Invalid File Folder", 'UploadedFile::getPathFromUrlF : ' . $this->folder);
if (!$this->hash) Response::Status404("Invalid File Hash", 'UploadedFile::getPathFromUrlF : ' . $this->hash);
return $this->path = __STORE__ . "/upload/" . $this->folder . "/" . substr($this->hash, 0, 3) . "/" . substr($this->hash, 3, 61) . "/" . substr($this->hash, 64);
}
private function setPathFromHash(): string
{
}
private function setPathFromLink(): string
{
return "";
}
private function setPathFromId(): string
{
return "";
}
public function getName(): string|bool
{
if ($this->name) return $this->name;
else return $this->setName();
}
public function getNameByUrl(): string|bool
{
if ($this->url) return end(explode('/', $this->url));
return false;
}
private function setName(): string
{
if ($this->name) return $this->name;
if ($this->row) return $this->name = $this->row['files__name'];
else $this->row = $this->setRow();
return $this->name = $this->row['files__name'];
}
private static function getBuffer($url): string|bool
{
return false;
}
private function setDbName(): string
{
if ($this->dbName) return $this->dbName;
if ($this->row) return $this->row['files__name'];
else $this->row = $this->setRow();
return $this->row['files__name'];
}
public function getMime(): string
{
if ($this->mime) return $this->mime;
return $this->mime = $this->setMime();
}
private function setMime(): string
{
$this->setPath();
$this->setName();
// si le fichier n'existe pas, on renvoe le mime type
if ($this->path and !file_exists($this->path)) return $this->setMimeByName();
if ($this->path) return $this->mime = mime_content_type($this->path);
if ($this->name) return $this->mime = $this->setMimeByName();
Response::Status404("Unable to find mime type", "Name : " . $this->name);
}
private function setMimeByName(): string
{
$mime = '';
$mimeTypes = [
'txt' => 'text/plain',
'csv' => 'text/csv',
'htm' => 'text/html',
'html' => 'text/html',
'php' => 'text/html',
'css' => 'text/css',
'js' => 'application/javascript',
'json' => 'application/json',
'xml' => 'application/xml',
'swf' => 'application/x-shockwave-flash',
'flv' => 'video/x-flv',
// Images
'png' => 'image/png',
'jpe' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'jpg' => 'image/jpeg',
'gif' => 'image/gif',
'bmp' => 'image/bmp',
'ico' => 'image/vnd.microsoft.icon',
'tiff' => 'image/tiff',
'tif' => 'image/tiff',
'svg' => 'image/svg+xml',
'svgz' => 'image/svg+xml',
// Archives
'zip' => 'application/zip',
'rar' => 'application/x-rar-compressed',
'exe' => 'application/x-msdownload',
'msi' => 'application/x-msdownload',
'cab' => 'application/vnd.ms-cab-compressed',
// Audio/video
'mpg' => 'audio/mpeg',
'mp2' => 'audio/mpeg',
'mp3' => 'audio/mpeg',
'mp4' => 'audio/mp4',
'qt' => 'video/quicktime',
'mov' => 'video/quicktime',
'ogg' => 'audio/ogg',
'oga' => 'audio/ogg',
'wav' => 'audio/wav',
'webm' => 'audio/webm',
'aac' => 'audio/aac',
// Adobe
'pdf' => 'application/pdf',
'psd' => 'image/vnd.adobe.photoshop',
'ai' => 'application/postscript',
'eps' => 'application/postscript',
'ps' => 'application/postscript',
// MS Office
'doc' => 'application/msword',
'dot' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'docm' => 'application/vnd.ms-word.document.macroEnabled.12',
'dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
'odt' => 'application/vnd.oasis.opendocument.text',
'rtf' => 'application/rtf',
'xls' => 'application/vnd.ms-excel',
'xlt' => 'application/vnd.ms-excel',
'xla' => 'application/vnd.ms-excel',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'ppt' => 'application/vnd.ms-powerpoint',
'pot' => 'application/vnd.ms-powerpoint',
'pps' => 'application/vnd.ms-powerpoint',
'ppa' => 'application/vnd.ms-powerpoint',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
'mdb' => 'application/vnd.ms-access',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
];
$extension = $this->getExtension();
$mime = $mimeTypes[$extension];
if (!$mime) $mime = 'application/octet-stream';
return $mime;
}
public function getExtension(): string
{
if ($this->extension) return $this->extension;
if ($this->row) return $this->extension = $this->row['files__extension'];
if ($this->hash or $this->link) {
$this->row = $this->setRow();
if ($this->row) return $this->extension = $this->row['files__extension'];
}
if ($this->name) return $this->extension = $this->getExtensionByName();
return false;
}
private function getExtensionByName(): string
{
if ($this->extension) return $this->extension;
if ($this->row) return $this->folder = $this->row['files__extension'];
if ($this->hash or $this->link) {
$this->row = $this->setRow();
if ($this->row) return $this->folder = $this->row['files__extension'];
}
if ($this->name) return $this->extension = end(explode('.', $this->name));
return false;
}
/* === sizes === */
public function getSize(): string|bool
{
if ($this->size) return $this->size;
return $this->size = $this->setSize();
}
private function setSize(): string|bool
{
if ($this->row and !$this->path) $this->setPath();
if ($this->path) return $this->size = filesize($this->path);
return false;
}
public function getHumanSize(): string
{
$size = $this->getSize();
$dec = 2;
$sizeTypes = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
$factor = floor((strlen($size) - 1) / 3);
if ($factor == 0) $dec = 0;
return sprintf("%.{$dec}f %s", $size / (1024 ** $factor), $sizeTypes[$factor]);
}
public function getFolder(): string
{
if ($this->folder) return $this->folder;
return $this->folder = $this->setFolder();
}
private function setFolder(): string|bool
{
if ($this->folder) return $this->folder;
if ($this->row) return $this->folder = $this->row['files__folder'];
else $this->row = $this->setRow();
if ($this->row) return $this->folder = $this->row['files__folder'];
return false;
}
private function setHash(): string
{
if ($this->hash) return $this->hash;
if ($this->row) return $this->hash = $this->row['files__hash'];
else $this->row = $this->setRow();
return $this->hash = $this->row['files__hash'];
}
private function setRow(): array|bool
{
if ($this->row) return $this->row;
// en priorité le link qui a l'avantage de renvoyer une ligne spécifique, le nom du fichier peut par exemple être différent selon les contextes
if ($this->link) {
$this->row = (new DB())->query('SELECT * FROM files WHERE files__link = :link', ['link' => $this->link], 'row');
if (!$this->row) Response::Status404("Invalid file link", 'UploaderFile::setRow : ' . $this->link);
}
// le hash peut renvoyer plusieurs ligne si le même fichier est utilisé dans plusieurs contextes
else if ($this->hash) {
$this->row = (new DB())->query('SELECT * FROM files WHERE files__hash = :hash', ['hash' => $this->hash], 'row');
if (!$this->row) Response::Status404("Invalid file hash", 'UploaderFile::setRow : ' . $this->hash);
}
// par l'url quand c'est un hash
else if (preg_match('/^[a-zA-z0-9]{128}$/', $this->url)) {
$this->row = (new DB())->query('SELECT * FROM files WHERE files__hash = :url', ['url' => $this->url], 'row');
if (!$this->row) Response::Status404("Invalid file hash", 'UploaderFile::setRow by Url:hash : ' . $this->url);
}
// par l'url quand c'est un link
else if (preg_match('/^[a-zA-z0-9]{66}$/', $this->url)) {
$this->row = (new DB())->query('SELECT * FROM files WHERE files__link = :url', ['url' => $this->url], 'row');
if (!$this->row) Response::Status404("Invalid file hash", 'UploaderFile::setRow by Url:Link : ' . $this->url);
}
// par l'url quand c'est un hash
else if (preg_match('/^[a-zA-z0-9]{40}$/', $this->url) or preg_match('/^[a-zA-z0-9]{43}$/', $this->url)) {
$this->row = (new DB())->query('SELECT * FROM files WHERE files__id = :url', ['url' => $this->url], 'row');
if (!$this->row) Response::Status404("Invalid file hash", 'UploaderFile::setRow by Url:Id : ' . $this->url);
}
return $this->row;
}
public function getId(): bool|string
{
if ($this->id) return $this->id;
return $this->id = $this->setId();
}
private function setId(): bool|string
{
if ($this->id) return $this->id;
if ($this->row) return $this->row['files__id'];
return false;
}
public function isImage(): bool
{
$this->getMime();
if (stripos($this->mime, 'image') === 0) return true;
return false;
}
public function isViewable(): bool
{
$extension = strtolower($this->getExtension());
if ($extension == 'jpg') return true;
if ($extension == 'jpeg') return true;
if ($extension == 'png') return true;
if ($extension == 'gif') return true;
if ($extension == 'giff') return true;
if ($extension == 'svg') return true;
if ($extension == 'pdf') return true;
return false;
}
public function getFaIcon($fixedWidth = false)
{
$faIcons = [
// Media
'image' => 'fa-file-image',
'audio' => 'fa-file-audio',
'video' => 'fa-file-video',
// Documents
'application/pdf' => 'fa-file-pdf',
'pdf' => 'fa-file-pdf',
'application/msword' => 'fa-file-word',
'application/vnd.ms-word' => 'fa-file-word',
'application/vnd.oasis.opendocument.text' => 'fa-file-word',
'application/vnd.openxmlformats-officedocument.wordprocessingml' => 'fa-file-word',
'application/vnd.ms-excel' => 'fa-file-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml' => 'fa-file-excel',
'application/application/vnd.oasis.opendocument.spreadsheet' => 'fa-file-excel',
'application/vnd.oasis.opendocument.spreadsheet' => 'fa-file-excel',
'csv' => 'fa-file-excel',
'application/vnd.ms-powerpoint' => 'fa-file-powerpoint',
'application/vnd.openxmlformats-officedocument.presentationml' => 'fa-file-powerpoint',
'application/vnd.oasis.opendocument.presentation' => 'fa-file-powerpoint',
// Other
'text/plain' => 'fa-file-text',
'text/html' => 'fa-file-code',
'application/json' => 'fa-file-code',
// Archives
'application/gzip' => 'fa-file-zipper',
'application/zip' => 'fa-file-zipper',
'application/x-rar-compressed' => 'fa-file-zipper',
];
$fileMime = $this->getMime();
$faIcon = $faIcons[$fileMime];
if (!$faIcon and $this->isImage()) $faIcon = 'fa-file-image';
if (!$faIcon) $faIcon = 'fa-file';
return $faIcon . ($fixedWidth ? ' fa-fw' : null);
}
public function exist()
{
if (file_exists($this->getPath())) return true;
else return false;
}
}