vendor\klio\klio-bundle\src\Files\File.php line 202

  1. <?php
  2. namespace Klio\KlioBundle\Files;
  3. use finfo;
  4. use Klio\KlioBundle\Database\DB;
  5. use Klio\KlioBundle\Security\Check;
  6. use Klio\KlioBundle\Security\Response;
  7. /**
  8.  * Cette classe permet de gérer des url locales ou distantes de fichiers
  9.  * soit pour les afficher inline, soitr pour les télécharger
  10.  */
  11. class File
  12. {
  13.     public bool|array $row false;
  14.     public bool|string $id false;
  15.     public string $url "";
  16.     private string $path "";
  17.     private string $name "";
  18.     private string $extension "";
  19.     private string $mime "";
  20.     private int $size 0;
  21.     private string $buffer "";
  22.     private string $mode "";
  23.     private string $hash "";
  24.     private string $link "";
  25.     private string $folder "";
  26.     public function __construct($file)
  27.     {
  28.         if (is_array($file) and $file['files__num']) $this->row $file;
  29.         else $this->url $file;
  30.     }
  31.     public function stream(): void
  32.     {
  33.         if (ob_get_level()) ob_clean();
  34.         header('Expires: 0');
  35.         header('Cache-Control: must-revalidate');
  36.         header("Content-Type: " $this->getMime());
  37.         header('Content-Disposition: inline; filename="' $this->getName() . '"');
  38.         header("Content-Length: " $this->getSize());
  39.         //open the file
  40.         $fd fopen($this->getPath(), "rb");
  41.         while (!feof($fd)) {
  42.             print(fread($fd1024 1024)); // buffer de 1Mo
  43.             ob_flush();
  44.             flush();
  45.         }
  46.         fclose($fd);
  47.         exit();
  48.     }
  49.     public function getStreamUrl(): string
  50.     {
  51.         return '/f/' $this->getLink() . '/' $this->getName();
  52.     }
  53.     public function download()
  54.     {
  55.         if (ob_get_level()) ob_clean();
  56.         header('Content-Description: File Download');
  57.         header('Content-Type: application/octet-stream');
  58.         header('Content-Disposition: attachment; filename="' $this->getName() . '"');
  59.         header("Content-Length: " $this->getSize());
  60.         header('Cache-Control: must-revalidate');
  61.         //open the file
  62.         $fd fopen($this->getPath(), "rb");
  63.         while (!feof($fd)) {
  64.             print(fread($fd1024 1024)); // buffer de 1Mo
  65.             ob_flush();
  66.             flush();
  67.         }
  68.         fclose($fd);
  69.         exit();
  70.     }
  71.     public function getDownloadUrl(): string
  72.     {
  73.         return '/d/' $this->getLink() . '/' $this->getName();
  74.     }
  75.     /**
  76.      * Get the value of hash
  77.      *
  78.      * @return string
  79.      */
  80.     public function getHash(): string
  81.     {
  82.         return $this->hash;
  83.     }
  84.     /**
  85.      * Get the value of link
  86.      *
  87.      * @return string
  88.      */
  89.     public function getLink(): string
  90.     {
  91.         if ($this->link) return $this->link;
  92.         if ($this->url or $this->row$this->setPath();
  93.         return $this->link $this->setLink();
  94.     }
  95.     private function setLink(): string
  96.     {
  97.         if ($this->link) return $this->link;
  98.         $this->setRow();
  99.         if ($this->row) return $this->link $this->row['files__link'];
  100.         return $this->link false;
  101.     }
  102.     public function getPath()
  103.     {
  104.         if ($this->path) return $this->path;
  105.         return $this->path $this->setPath();
  106.     }
  107.     private function setPath(): string|bool
  108.     {
  109.         if ($this->path) return $this->path;
  110.         if ($this->row) return $this->path $this->setPathFromRow();
  111.         if (stripos($this->url'/f/') === 0) return $this->setPathFromUrl_F(); // gérer FW4 et FW7
  112.         if (stripos($this->url'/d/') === 0) return $this->setPathFromUrl_D(); // gérer FW4 et FW7
  113.         //if (stripos($this->url, '/i/') === 0) return $this->getPathFromUrl(); // gérer FW4 et FW7
  114.         //if (stripos($this->url, '/p/') === 0) return $this->getPathFromUrl(); // gérer FW4 et FW7
  115.         // sinon, on cherche à récupérer les infos via l'url du construct si on a envoyé id, link ou hash
  116.         $this->setRow();
  117.         if ($this->row) return $this->path $this->setPathFromRow();
  118.         return false;
  119.     }
  120.     private function setPathFromRow(): string
  121.     {
  122.         if (!$this->folder$this->setFolder();
  123.         if (!$this->hash$this->setHash();
  124.         if (!$this->folderResponse::Status404("Invalid File Folder"'UploadedFile::getPathFromUrlF : ' $this->folder);
  125.         if (!$this->hashResponse::Status404("Invalid File Hash"'UploadedFile::getPathFromUrlF : ' $this->hash);
  126.         return $this->path __STORE__ "/upload/" $this->folder "/" substr($this->hash03) . "/" substr($this->hash361) . "/" substr($this->hash64);
  127.     }
  128.     private function setPathFromUrl_F(): string
  129.     {
  130.         // on enlève le /f/ du début
  131.         $url substr($this->url3);
  132.         $parts explode('/'$url);
  133.         $partsCount count($parts);
  134.         $part1 $parts[0];
  135.         // la premiere partie peut être le hash, ou le link
  136.         if (strlen($part1) == 66$this->link $part1;
  137.         else if (strlen($part1) == 128$this->hash $part1;
  138.         // si on a juste le link et pas le hash, il faut faire une requete
  139.         /*
  140.         $folder = substr($url, 0, strpos($url, '/'));
  141.         if (!$folder or $folder == "" or !preg_match('/^[a-zA-z0-9_]+$/', $folder)) Response::Status404("Invalid folder invalid.", $folder);
  142.         */
  143.         if (!$this->folder$this->setFolder();
  144.         if (!$this->hash$this->setHash();
  145.         if (!$this->folderResponse::Status404("Invalid File Folder"'UploadedFile::getPathFromUrlF : ' $this->folder);
  146.         if (!$this->hashResponse::Status404("Invalid File Hash"'UploadedFile::getPathFromUrlF : ' $this->hash);
  147.         return $this->path __STORE__ "/upload/" $this->folder "/" substr($this->hash03) . "/" substr($this->hash361) . "/" substr($this->hash64);
  148.     }
  149.     private function setPathFromUrl_D(): string
  150.     {
  151.         // on enlève le /d/ du début
  152.         $url substr($this->url3);
  153.         $parts explode('/'$url);
  154.         $partsCount count($parts);
  155.         $part1 $parts[0];
  156.         // la premiere partie peut être le hash, ou le link
  157.         if (strlen($part1) == 66) {
  158.             $this->link $part1;
  159.             Check::uid($this->link66);
  160.         } else if (strlen($part1) == 128) {
  161.             $this->hash $part1;
  162.             Check::uid($this->hash128);
  163.         }
  164.         // si on a juste le link et pas le hash, il faut faire une requete
  165.         if (isset($parts[1])) $this->name $parts[1];
  166.         if (!$this->folder$this->setFolder();
  167.         if (!$this->hash$this->setHash();
  168.         if (!$this->folderResponse::Status404("Invalid File Folder"'UploadedFile::getPathFromUrlF : ' $this->folder);
  169.         if (!$this->hashResponse::Status404("Invalid File Hash"'UploadedFile::getPathFromUrlF : ' $this->hash);
  170.         return $this->path __STORE__ "/upload/" $this->folder "/" substr($this->hash03) . "/" substr($this->hash361) . "/" substr($this->hash64);
  171.     }
  172.     private function setPathFromHash(): string
  173.     {
  174.     }
  175.     private function setPathFromLink(): string
  176.     {
  177.         return "";
  178.     }
  179.     private function setPathFromId(): string
  180.     {
  181.         return "";
  182.     }
  183.     public function getName(): string|bool
  184.     {
  185.         if ($this->name) return $this->name;
  186.         else return $this->setName();
  187.     }
  188.     public function getNameByUrl(): string|bool
  189.     {
  190.         if ($this->url) return end(explode('/'$this->url));
  191.         return false;
  192.     }
  193.     private function setName(): string
  194.     {
  195.         if ($this->name) return $this->name;
  196.         if ($this->row) return $this->name $this->row['files__name'];
  197.         else $this->row $this->setRow();
  198.         return $this->name $this->row['files__name'];
  199.     }
  200.     private static function getBuffer($url): string|bool
  201.     {
  202.         return false;
  203.     }
  204.     private function setDbName(): string
  205.     {
  206.         if ($this->dbName) return $this->dbName;
  207.         if ($this->row) return $this->row['files__name'];
  208.         else $this->row $this->setRow();
  209.         return $this->row['files__name'];
  210.     }
  211.     public function getMime(): string
  212.     {
  213.         if ($this->mime) return $this->mime;
  214.         return $this->mime $this->setMime();
  215.     }
  216.     private function setMime(): string
  217.     {
  218.         $this->setPath();
  219.         $this->setName();
  220.         // si le fichier n'existe pas, on renvoe le mime type
  221.         if ($this->path and !file_exists($this->path)) return $this->setMimeByName();
  222.         if ($this->path) return $this->mime mime_content_type($this->path);
  223.         if ($this->name) return $this->mime $this->setMimeByName();
  224.         Response::Status404("Unable to find mime type""Name : " $this->name);
  225.     }
  226.     private function setMimeByName(): string
  227.     {
  228.         $mime '';
  229.         $mimeTypes = [
  230.             'txt'  => 'text/plain',
  231.             'csv'  => 'text/csv',
  232.             'htm'  => 'text/html',
  233.             'html' => 'text/html',
  234.             'php'  => 'text/html',
  235.             'css'  => 'text/css',
  236.             'js'   => 'application/javascript',
  237.             'json' => 'application/json',
  238.             'xml'  => 'application/xml',
  239.             'swf'  => 'application/x-shockwave-flash',
  240.             'flv'  => 'video/x-flv',
  241.             // Images
  242.             'png'  => 'image/png',
  243.             'jpe'  => 'image/jpeg',
  244.             'jpeg' => 'image/jpeg',
  245.             'jpg'  => 'image/jpeg',
  246.             'gif'  => 'image/gif',
  247.             'bmp'  => 'image/bmp',
  248.             'ico'  => 'image/vnd.microsoft.icon',
  249.             'tiff' => 'image/tiff',
  250.             'tif'  => 'image/tiff',
  251.             'svg'  => 'image/svg+xml',
  252.             'svgz' => 'image/svg+xml',
  253.             // Archives
  254.             'zip'  => 'application/zip',
  255.             'rar'  => 'application/x-rar-compressed',
  256.             'exe'  => 'application/x-msdownload',
  257.             'msi'  => 'application/x-msdownload',
  258.             'cab'  => 'application/vnd.ms-cab-compressed',
  259.             // Audio/video
  260.             'mpg'  => 'audio/mpeg',
  261.             'mp2'  => 'audio/mpeg',
  262.             'mp3'  => 'audio/mpeg',
  263.             'mp4'  => 'audio/mp4',
  264.             'qt'   => 'video/quicktime',
  265.             'mov'  => 'video/quicktime',
  266.             'ogg'  => 'audio/ogg',
  267.             'oga'  => 'audio/ogg',
  268.             'wav'  => 'audio/wav',
  269.             'webm' => 'audio/webm',
  270.             'aac'  => 'audio/aac',
  271.             // Adobe
  272.             'pdf'  => 'application/pdf',
  273.             'psd'  => 'image/vnd.adobe.photoshop',
  274.             'ai'   => 'application/postscript',
  275.             'eps'  => 'application/postscript',
  276.             'ps'   => 'application/postscript',
  277.             // MS Office
  278.             'doc'  => 'application/msword',
  279.             'dot'  => 'application/msword',
  280.             'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  281.             'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
  282.             'docm' => 'application/vnd.ms-word.document.macroEnabled.12',
  283.             'dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
  284.             'odt'  => 'application/vnd.oasis.opendocument.text',
  285.             'rtf'  => 'application/rtf',
  286.             'xls'  => 'application/vnd.ms-excel',
  287.             'xlt'  => 'application/vnd.ms-excel',
  288.             'xla'  => 'application/vnd.ms-excel',
  289.             'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  290.             'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
  291.             'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
  292.             'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
  293.             'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
  294.             'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
  295.             'ppt'  => 'application/vnd.ms-powerpoint',
  296.             'pot'  => 'application/vnd.ms-powerpoint',
  297.             'pps'  => 'application/vnd.ms-powerpoint',
  298.             'ppa'  => 'application/vnd.ms-powerpoint',
  299.             'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
  300.             'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
  301.             'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
  302.             'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
  303.             'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
  304.             'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
  305.             'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
  306.             'mdb'  => 'application/vnd.ms-access',
  307.             'ods'  => 'application/vnd.oasis.opendocument.spreadsheet',
  308.         ];
  309.         $extension $this->getExtension();
  310.         $mime $mimeTypes[$extension];
  311.         if (!$mime)  $mime 'application/octet-stream';
  312.         return $mime;
  313.     }
  314.     public function getExtension(): string
  315.     {
  316.         if ($this->extension) return $this->extension;
  317.         if ($this->row) return $this->extension $this->row['files__extension'];
  318.         if ($this->hash or $this->link) {
  319.             $this->row $this->setRow();
  320.             if ($this->row) return $this->extension $this->row['files__extension'];
  321.         }
  322.         if ($this->name) return $this->extension $this->getExtensionByName();
  323.         return false;
  324.     }
  325.     private function getExtensionByName(): string
  326.     {
  327.         if ($this->extension) return $this->extension;
  328.         if ($this->row) return $this->folder $this->row['files__extension'];
  329.         if ($this->hash or $this->link) {
  330.             $this->row $this->setRow();
  331.             if ($this->row) return $this->folder $this->row['files__extension'];
  332.         }
  333.         if ($this->name) return $this->extension end(explode('.'$this->name));
  334.         return false;
  335.     }
  336.     /* === sizes  === */
  337.     public function getSize(): string|bool
  338.     {
  339.         if ($this->size) return $this->size;
  340.         return $this->size $this->setSize();
  341.     }
  342.     private function setSize(): string|bool
  343.     {
  344.         if ($this->row and !$this->path$this->setPath();
  345.         if ($this->path) return $this->size filesize($this->path);
  346.         return false;
  347.     }
  348.     public function getHumanSize(): string
  349.     {
  350.         $size $this->getSize();
  351.         $dec 2;
  352.         $sizeTypes   = array('B''kB''MB''GB''TB''PB''EB''ZB''YB');
  353.         $factor floor((strlen($size) - 1) / 3);
  354.         if ($factor == 0$dec 0;
  355.         return sprintf("%.{$dec}f %s"$size / (1024 ** $factor), $sizeTypes[$factor]);
  356.     }
  357.     public function getFolder(): string
  358.     {
  359.         if ($this->folder) return $this->folder;
  360.         return $this->folder $this->setFolder();
  361.     }
  362.     private function setFolder(): string|bool
  363.     {
  364.         if ($this->folder) return $this->folder;
  365.         if ($this->row) return $this->folder $this->row['files__folder'];
  366.         else $this->row $this->setRow();
  367.         if ($this->row) return $this->folder $this->row['files__folder'];
  368.         return false;
  369.     }
  370.     private function setHash(): string
  371.     {
  372.         if ($this->hash) return $this->hash;
  373.         if ($this->row) return $this->hash $this->row['files__hash'];
  374.         else $this->row $this->setRow();
  375.         return $this->hash $this->row['files__hash'];
  376.     }
  377.     private function setRow(): array|bool
  378.     {
  379.         if ($this->row) return $this->row;
  380.         // 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
  381.         if ($this->link) {
  382.             $this->row = (new DB())->query('SELECT * FROM files WHERE files__link = :link', ['link' => $this->link], 'row');
  383.             if (!$this->rowResponse::Status404("Invalid file link"'UploaderFile::setRow : ' $this->link);
  384.         }
  385.         // le hash peut renvoyer plusieurs ligne si le même fichier est utilisé dans plusieurs contextes
  386.         else if ($this->hash) {
  387.             $this->row = (new DB())->query('SELECT * FROM files WHERE files__hash = :hash', ['hash' => $this->hash], 'row');
  388.             if (!$this->rowResponse::Status404("Invalid file hash"'UploaderFile::setRow : ' $this->hash);
  389.         }
  390.         // par l'url quand c'est un hash
  391.         else if (preg_match('/^[a-zA-z0-9]{128}$/'$this->url)) {
  392.             $this->row = (new DB())->query('SELECT * FROM files WHERE files__hash = :url', ['url' => $this->url], 'row');
  393.             if (!$this->rowResponse::Status404("Invalid file hash"'UploaderFile::setRow  by Url:hash : ' $this->url);
  394.         }
  395.         // par l'url quand c'est un link
  396.         else if (preg_match('/^[a-zA-z0-9]{66}$/'$this->url)) {
  397.             $this->row = (new DB())->query('SELECT * FROM files WHERE files__link = :url', ['url' => $this->url], 'row');
  398.             if (!$this->rowResponse::Status404("Invalid file hash"'UploaderFile::setRow by Url:Link : ' $this->url);
  399.         }
  400.         // par l'url quand c'est un hash
  401.         else if (preg_match('/^[a-zA-z0-9]{40}$/'$this->url) or preg_match('/^[a-zA-z0-9]{43}$/'$this->url)) {
  402.             $this->row = (new DB())->query('SELECT * FROM files WHERE files__id = :url', ['url' => $this->url], 'row');
  403.             if (!$this->rowResponse::Status404("Invalid file hash"'UploaderFile::setRow by Url:Id : ' $this->url);
  404.         }
  405.         return $this->row;
  406.     }
  407.     public function getId(): bool|string
  408.     {
  409.         if ($this->id) return $this->id;
  410.         return $this->id $this->setId();
  411.     }
  412.     private function setId(): bool|string
  413.     {
  414.         if ($this->id) return $this->id;
  415.         if ($this->row) return $this->row['files__id'];
  416.         return false;
  417.     }
  418.     public function isImage(): bool
  419.     {
  420.         $this->getMime();
  421.         if (stripos($this->mime'image') === 0) return true;
  422.         return false;
  423.     }
  424.     public function isViewable(): bool
  425.     {
  426.         $extension strtolower($this->getExtension());
  427.         if ($extension == 'jpg') return true;
  428.         if ($extension == 'jpeg') return true;
  429.         if ($extension == 'png') return true;
  430.         if ($extension == 'gif') return true;
  431.         if ($extension == 'giff') return true;
  432.         if ($extension == 'svg') return true;
  433.         if ($extension == 'pdf') return true;
  434.         return false;
  435.     }
  436.     public function getFaIcon($fixedWidth false)
  437.     {
  438.         $faIcons = [
  439.             // Media
  440.             'image'                                                          => 'fa-file-image',
  441.             'audio'                                                          => 'fa-file-audio',
  442.             'video'                                                          => 'fa-file-video',
  443.             // Documents
  444.             'application/pdf'                                                => 'fa-file-pdf',
  445.             'pdf'                                                            => 'fa-file-pdf',
  446.             'application/msword'                                             => 'fa-file-word',
  447.             'application/vnd.ms-word'                                        => 'fa-file-word',
  448.             'application/vnd.oasis.opendocument.text'                        => 'fa-file-word',
  449.             'application/vnd.openxmlformats-officedocument.wordprocessingml' => 'fa-file-word',
  450.             'application/vnd.ms-excel'                                       => 'fa-file-excel',
  451.             'application/vnd.openxmlformats-officedocument.spreadsheetml'    => 'fa-file-excel',
  452.             'application/application/vnd.oasis.opendocument.spreadsheet'     => 'fa-file-excel',
  453.             'application/vnd.oasis.opendocument.spreadsheet'                 => 'fa-file-excel',
  454.             'csv'                                                            => 'fa-file-excel',
  455.             'application/vnd.ms-powerpoint'                                  => 'fa-file-powerpoint',
  456.             'application/vnd.openxmlformats-officedocument.presentationml'   => 'fa-file-powerpoint',
  457.             'application/vnd.oasis.opendocument.presentation'                => 'fa-file-powerpoint',
  458.             // Other
  459.             'text/plain'                                                     => 'fa-file-text',
  460.             'text/html'                                                      => 'fa-file-code',
  461.             'application/json'                                               => 'fa-file-code',
  462.             // Archives
  463.             'application/gzip'                                               => 'fa-file-zipper',
  464.             'application/zip'                                                => 'fa-file-zipper',
  465.             'application/x-rar-compressed'                                   => 'fa-file-zipper',
  466.         ];
  467.         $fileMime $this->getMime();
  468.         $faIcon $faIcons[$fileMime];
  469.         if (!$faIcon and $this->isImage()) $faIcon 'fa-file-image';
  470.         if (!$faIcon$faIcon 'fa-file';
  471.         return $faIcon . ($fixedWidth ' fa-fw' null);
  472.     }
  473.     public function exist()
  474.     {
  475.         if (file_exists($this->getPath())) return true;
  476.         else return false;
  477.     }
  478. }