vendor/contao/core-bundle/src/Resources/contao/library/Contao/File.php line 259

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Contao.
  4.  *
  5.  * (c) Leo Feyer
  6.  *
  7.  * @license LGPL-3.0-or-later
  8.  */
  9. namespace Contao;
  10. use Contao\CoreBundle\Exception\ResponseException;
  11. use Contao\Image\DeferredImageInterface;
  12. use Contao\Image\ImageDimensions;
  13. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  14. use Symfony\Component\HttpFoundation\ResponseHeaderBag;
  15. use Symfony\Component\String\UnicodeString;
  16. /**
  17.  * Creates, reads, writes and deletes files
  18.  *
  19.  * Usage:
  20.  *
  21.  *     $file = new File('test.txt');
  22.  *     $file->write('This is a test');
  23.  *     $file->close();
  24.  *
  25.  *     $file->delete();
  26.  *
  27.  *     File::putContent('test.txt', 'This is a test');
  28.  *
  29.  * @property integer  $size          The file size
  30.  * @property integer  $filesize      Alias of $size
  31.  * @property string   $name          The file name and extension
  32.  * @property string   $basename      Alias of $name
  33.  * @property string   $dirname       The path of the parent folder
  34.  * @property string   $extension     The lowercase file extension
  35.  * @property string   $origext       The original file extension
  36.  * @property string   $filename      The file name without extension
  37.  * @property string   $tmpname       The name of the temporary file
  38.  * @property string   $path          The file path
  39.  * @property string   $value         Alias of $path
  40.  * @property string   $mime          The mime type
  41.  * @property string   $hash          The MD5 checksum
  42.  * @property string   $ctime         The ctime
  43.  * @property string   $mtime         The mtime
  44.  * @property string   $atime         The atime
  45.  * @property string   $icon          The mime icon name
  46.  * @property string   $dataUri       The data URI
  47.  * @property array    $imageSize     The file dimensions (images only)
  48.  * @property integer  $width         The file width (images only)
  49.  * @property integer  $height        The file height (images only)
  50.  * @property array    $imageViewSize The viewbox dimensions
  51.  * @property integer  $viewWidth     The viewbox width
  52.  * @property integer  $viewHeight    The viewbox height
  53.  * @property boolean  $isImage       True if the file is an image
  54.  * @property boolean  $isGdImage     True if the file can be handled by the GDlib
  55.  * @property boolean  $isSvgImage    True if the file is an SVG image
  56.  * @property integer  $channels      The number of channels (images only)
  57.  * @property integer  $bits          The number of bits for each color (images only)
  58.  * @property boolean  $isRgbImage    True if the file is an RGB image
  59.  * @property boolean  $isCmykImage   True if the file is a CMYK image
  60.  * @property resource $handle        The file handle (returned by fopen())
  61.  * @property string   $title         The file title
  62.  */
  63. class File extends System
  64. {
  65.     /**
  66.      * File handle
  67.      * @var resource
  68.      */
  69.     protected $resFile;
  70.     /**
  71.      * File name
  72.      * @var string
  73.      */
  74.     protected $strFile;
  75.     /**
  76.      * Temp name
  77.      * @var string
  78.      */
  79.     protected $strTmp;
  80.     /**
  81.      * Files model
  82.      * @var FilesModel
  83.      */
  84.     protected $objModel;
  85.     /**
  86.      * Root dir
  87.      * @var string
  88.      */
  89.     protected $strRootDir;
  90.     /**
  91.      * Pathinfo
  92.      * @var array
  93.      */
  94.     protected $arrPathinfo = array();
  95.     /**
  96.      * Image size
  97.      * @var array
  98.      */
  99.     protected $arrImageSize = array();
  100.     /**
  101.      * Image size runtime cache
  102.      * @var array
  103.      */
  104.     protected static $arrImageSizeCache = array();
  105.     /**
  106.      * Image view size
  107.      * @var array
  108.      */
  109.     protected $arrImageViewSize = array();
  110.     /**
  111.      * Instantiate a new file object
  112.      *
  113.      * @param string $strFile The file path
  114.      *
  115.      * @throws \Exception If $strFile is a directory
  116.      */
  117.     public function __construct($strFile)
  118.     {
  119.         // Handle open_basedir restrictions
  120.         if ($strFile == '.')
  121.         {
  122.             $strFile '';
  123.         }
  124.         $this->strRootDir System::getContainer()->getParameter('kernel.project_dir');
  125.         // Make sure we are not pointing to a directory
  126.         if (is_dir($this->strRootDir '/' $strFile))
  127.         {
  128.             throw new \Exception(sprintf('Directory "%s" is not a file'$strFile));
  129.         }
  130.         $this->import(Files::class, 'Files');
  131.         $this->strFile $strFile;
  132.     }
  133.     /**
  134.      * Close the file handle if it has not been done yet
  135.      */
  136.     public function __destruct()
  137.     {
  138.         if (\is_resource($this->resFile))
  139.         {
  140.             $this->Files->fclose($this->resFile);
  141.         }
  142.     }
  143.     /**
  144.      * Return an object property
  145.      *
  146.      * @param string $strKey The property name
  147.      *
  148.      * @return mixed The property value
  149.      */
  150.     public function __get($strKey)
  151.     {
  152.         switch ($strKey)
  153.         {
  154.             case 'size':
  155.             case 'filesize':
  156.                 return filesize($this->strRootDir '/' $this->strFile);
  157.             case 'name':
  158.             case 'basename':
  159.                 if (!isset($this->arrPathinfo[$strKey]))
  160.                 {
  161.                     $this->arrPathinfo $this->getPathinfo();
  162.                 }
  163.                 return $this->arrPathinfo['basename'];
  164.             case 'dirname':
  165.             case 'filename':
  166.                 if (!isset($this->arrPathinfo[$strKey]))
  167.                 {
  168.                     $this->arrPathinfo $this->getPathinfo();
  169.                 }
  170.                 return $this->arrPathinfo[$strKey];
  171.             case 'extension':
  172.                 if (!isset($this->arrPathinfo['extension']))
  173.                 {
  174.                     $this->arrPathinfo $this->getPathinfo();
  175.                 }
  176.                 return strtolower($this->arrPathinfo['extension']);
  177.             case 'origext':
  178.                 if (!isset($this->arrPathinfo['extension']))
  179.                 {
  180.                     $this->arrPathinfo $this->getPathinfo();
  181.                 }
  182.                 return $this->arrPathinfo['extension'];
  183.             case 'tmpname':
  184.                 return basename($this->strTmp);
  185.             case 'path':
  186.             case 'value':
  187.                 return $this->strFile;
  188.             case 'mime':
  189.                 return $this->getMimeType();
  190.             case 'hash':
  191.                 return $this->getHash();
  192.             case 'ctime':
  193.                 return filectime($this->strRootDir '/' $this->strFile);
  194.             case 'mtime':
  195.                 return filemtime($this->strRootDir '/' $this->strFile);
  196.             case 'atime':
  197.                 return fileatime($this->strRootDir '/' $this->strFile);
  198.             case 'icon':
  199.                 return $this->getIcon();
  200.             case 'dataUri':
  201.                 if ($this->extension == 'svgz')
  202.                 {
  203.                     return 'data:' $this->mime ';base64,' base64_encode(gzdecode($this->getContent()));
  204.                 }
  205.                 return 'data:' $this->mime ';base64,' base64_encode($this->getContent());
  206.             case 'imageSize':
  207.                 if (empty($this->arrImageSize))
  208.                 {
  209.                     $strCacheKey $this->strFile '|' . ($this->exists() ? $this->mtime 0);
  210.                     if (isset(static::$arrImageSizeCache[$strCacheKey]))
  211.                     {
  212.                         $this->arrImageSize = static::$arrImageSizeCache[$strCacheKey];
  213.                     }
  214.                     else
  215.                     {
  216.                         $imageFactory System::getContainer()->get('contao.image.factory');
  217.                         try
  218.                         {
  219.                             $dimensions $imageFactory->create($this->strRootDir '/' $this->strFile)->getDimensions();
  220.                             if (!$dimensions->isRelative() && !$dimensions->isUndefined())
  221.                             {
  222.                                 $mapper = array
  223.                                 (
  224.                                     'gif' => IMAGETYPE_GIF,
  225.                                     'jpg' => IMAGETYPE_JPEG,
  226.                                     'jpeg' => IMAGETYPE_JPEG,
  227.                                     'png' => IMAGETYPE_PNG,
  228.                                     'webp' => IMAGETYPE_WEBP,
  229.                                     'avif' => \defined('IMAGETYPE_AVIF') ? IMAGETYPE_AVIF 19,
  230.                                     'heic' => IMAGETYPE_UNKNOWN// TODO: replace with IMAGETYPE_HEIC once available
  231.                                     'jxl' => IMAGETYPE_UNKNOWN// TODO: replace with IMAGETYPE_JXL once available
  232.                                 );
  233.                                 $this->arrImageSize = array
  234.                                 (
  235.                                     $dimensions->getSize()->getWidth(),
  236.                                     $dimensions->getSize()->getHeight(),
  237.                                     $mapper[$this->extension] ?? 0,
  238.                                     'width="' $dimensions->getSize()->getWidth() . '" height="' $dimensions->getSize()->getHeight() . '"',
  239.                                     'bits' => 8,
  240.                                     'channels' => 3,
  241.                                     'mime' => $this->getMimeType()
  242.                                 );
  243.                             }
  244.                         }
  245.                         catch (\Exception $e)
  246.                         {
  247.                             // ignore
  248.                         }
  249.                     }
  250.                     if (!isset(static::$arrImageSizeCache[$strCacheKey]))
  251.                     {
  252.                         static::$arrImageSizeCache[$strCacheKey] = $this->arrImageSize;
  253.                     }
  254.                 }
  255.                 return $this->arrImageSize;
  256.             case 'width':
  257.                 return $this->imageSize[0] ?? null;
  258.             case 'height':
  259.                 return $this->imageSize[1] ?? null;
  260.             case 'imageViewSize':
  261.                 if (empty($this->arrImageViewSize))
  262.                 {
  263.                     if ($this->imageSize)
  264.                     {
  265.                         $this->arrImageViewSize = array
  266.                         (
  267.                             $this->imageSize[0],
  268.                             $this->imageSize[1]
  269.                         );
  270.                     }
  271.                     elseif ($this->isSvgImage)
  272.                     {
  273.                         try
  274.                         {
  275.                             $dimensions = new ImageDimensions(
  276.                                 System::getContainer()
  277.                                     ->get('contao.image.imagine_svg')
  278.                                     ->open($this->strRootDir '/' $this->strFile)
  279.                                     ->getSize()
  280.                             );
  281.                             $this->arrImageViewSize = array
  282.                             (
  283.                                 (int) $dimensions->getSize()->getWidth(),
  284.                                 (int) $dimensions->getSize()->getHeight()
  285.                             );
  286.                             if (!$this->arrImageViewSize[0] || !$this->arrImageViewSize[1] || $dimensions->isUndefined())
  287.                             {
  288.                                 $this->arrImageViewSize false;
  289.                             }
  290.                         }
  291.                         catch (\Exception $e)
  292.                         {
  293.                             $this->arrImageViewSize false;
  294.                         }
  295.                     }
  296.                 }
  297.                 return $this->arrImageViewSize;
  298.             case 'viewWidth':
  299.                 // Store in variable as empty() calls __isset() which is not implemented and thus always true
  300.                 $imageViewSize $this->imageViewSize;
  301.                 return !empty($imageViewSize) ? $imageViewSize[0] : null;
  302.             case 'viewHeight':
  303.                 // Store in variable as empty() calls __isset() which is not implemented and thus always true
  304.                 $imageViewSize $this->imageViewSize;
  305.                 return !empty($imageViewSize) ? $imageViewSize[1] : null;
  306.             case 'isImage':
  307.                 return $this->isGdImage || $this->isSvgImage;
  308.             case 'isGdImage':
  309.                 return \in_array($this->extension, array('gif''jpg''jpeg''png''webp''avif''heic''jxl'));
  310.             case 'isSvgImage':
  311.                 return \in_array($this->extension, array('svg''svgz'));
  312.             case 'channels':
  313.                 return $this->imageSize['channels'];
  314.             case 'bits':
  315.                 return $this->imageSize['bits'];
  316.             case 'isRgbImage':
  317.                 return $this->channels == 3;
  318.             case 'isCmykImage':
  319.                 return $this->channels == 4;
  320.             case 'handle':
  321.                 if (!\is_resource($this->resFile))
  322.                 {
  323.                     $this->resFile fopen($this->strRootDir '/' $this->strFile'r');
  324.                 }
  325.                 return $this->resFile;
  326.             default:
  327.                 return parent::__get($strKey);
  328.         }
  329.     }
  330.     /**
  331.      * Create the file if it does not yet exist
  332.      *
  333.      * @throws \Exception If the file cannot be written
  334.      */
  335.     protected function createIfNotExists()
  336.     {
  337.         // The file exists
  338.         if (file_exists($this->strRootDir '/' $this->strFile))
  339.         {
  340.             return;
  341.         }
  342.         // Handle open_basedir restrictions
  343.         if (($strFolder \dirname($this->strFile)) == '.')
  344.         {
  345.             $strFolder '';
  346.         }
  347.         // Create the folder
  348.         if (!is_dir($this->strRootDir '/' $strFolder))
  349.         {
  350.             new Folder($strFolder);
  351.         }
  352.         // Open the file
  353.         if (!$this->resFile $this->Files->fopen($this->strFile'wb'))
  354.         {
  355.             throw new \Exception(sprintf('Cannot create file "%s"'$this->strFile));
  356.         }
  357.     }
  358.     /**
  359.      * Check whether the file exists
  360.      *
  361.      * @return boolean True if the file exists
  362.      */
  363.     public function exists()
  364.     {
  365.         return file_exists($this->strRootDir '/' $this->strFile);
  366.     }
  367.     /**
  368.      * Truncate the file and reset the file pointer
  369.      *
  370.      * @return boolean True if the operation was successful
  371.      */
  372.     public function truncate()
  373.     {
  374.         if (\is_resource($this->resFile))
  375.         {
  376.             ftruncate($this->resFile0);
  377.             rewind($this->resFile);
  378.         }
  379.         return $this->write('');
  380.     }
  381.     /**
  382.      * Write data to the file
  383.      *
  384.      * @param mixed $varData The data to be written
  385.      *
  386.      * @return boolean True if the operation was successful
  387.      */
  388.     public function write($varData)
  389.     {
  390.         return $this->fputs($varData'wb');
  391.     }
  392.     /**
  393.      * Append data to the file
  394.      *
  395.      * @param mixed  $varData The data to be appended
  396.      * @param string $strLine The line ending (defaults to LF)
  397.      *
  398.      * @return boolean True if the operation was successful
  399.      */
  400.     public function append($varData$strLine="\n")
  401.     {
  402.         return $this->fputs($varData $strLine'ab');
  403.     }
  404.     /**
  405.      * Prepend data to the file
  406.      *
  407.      * @param mixed  $varData The data to be prepended
  408.      * @param string $strLine The line ending (defaults to LF)
  409.      *
  410.      * @return boolean True if the operation was successful
  411.      */
  412.     public function prepend($varData$strLine="\n")
  413.     {
  414.         return $this->fputs($varData $strLine $this->getContent(), 'wb');
  415.     }
  416.     /**
  417.      * Delete the file
  418.      *
  419.      * @return boolean True if the operation was successful
  420.      */
  421.     public function delete()
  422.     {
  423.         $return $this->Files->delete($this->strFile);
  424.         // Update the database
  425.         if (Dbafs::shouldBeSynchronized($this->strFile))
  426.         {
  427.             Dbafs::deleteResource($this->strFile);
  428.         }
  429.         return $return;
  430.     }
  431.     /**
  432.      * Set the file permissions
  433.      *
  434.      * @param integer $intChmod The CHMOD settings
  435.      *
  436.      * @return boolean True if the operation was successful
  437.      */
  438.     public function chmod($intChmod)
  439.     {
  440.         return $this->Files->chmod($this->strFile$intChmod);
  441.     }
  442.     /**
  443.      * Close the file handle
  444.      *
  445.      * @return boolean True if the operation was successful
  446.      */
  447.     public function close()
  448.     {
  449.         if (\is_resource($this->resFile))
  450.         {
  451.             $this->Files->fclose($this->resFile);
  452.         }
  453.         // Create the file path
  454.         if (!file_exists($this->strRootDir '/' $this->strFile))
  455.         {
  456.             // Handle open_basedir restrictions
  457.             if (($strFolder \dirname($this->strFile)) == '.')
  458.             {
  459.                 $strFolder '';
  460.             }
  461.             // Create the parent folder
  462.             if (!is_dir($this->strRootDir '/' $strFolder))
  463.             {
  464.                 new Folder($strFolder);
  465.             }
  466.         }
  467.         // Move the temporary file to its destination
  468.         $return $this->Files->rename($this->strTmp$this->strFile);
  469.         $this->strTmp null;
  470.         // Update the database
  471.         if (Dbafs::shouldBeSynchronized($this->strFile))
  472.         {
  473.             $this->objModel Dbafs::addResource($this->strFile);
  474.         }
  475.         return $return;
  476.     }
  477.     /**
  478.      * Return the files model
  479.      *
  480.      * @return FilesModel The files model
  481.      */
  482.     public function getModel()
  483.     {
  484.         if ($this->objModel === null && Dbafs::shouldBeSynchronized($this->strFile))
  485.         {
  486.             $this->objModel FilesModel::findByPath($this->strFile);
  487.         }
  488.         return $this->objModel;
  489.     }
  490.     /**
  491.      * Generate the image if the current file is a deferred image and does not exist yet
  492.      *
  493.      * @return bool True if a deferred image was resized otherwise false
  494.      */
  495.     public function createIfDeferred()
  496.     {
  497.         if (!$this->exists())
  498.         {
  499.             try
  500.             {
  501.                 $image System::getContainer()->get('contao.image.factory')->create($this->strRootDir '/' $this->strFile);
  502.                 if ($image instanceof DeferredImageInterface)
  503.                 {
  504.                     System::getContainer()->get('contao.image.legacy_resizer')->resizeDeferredImage($image);
  505.                     return true;
  506.                 }
  507.             }
  508.             catch (\Throwable $e)
  509.             {
  510.                 // ignore
  511.             }
  512.         }
  513.         return false;
  514.     }
  515.     /**
  516.      * Return the file content as string
  517.      *
  518.      * @return string The file content without BOM
  519.      */
  520.     public function getContent()
  521.     {
  522.         $this->createIfDeferred();
  523.         $strContent file_get_contents($this->strRootDir '/' . ($this->strTmp ?: $this->strFile));
  524.         // Remove BOMs (see #4469)
  525.         if (strncmp($strContent"\xEF\xBB\xBF"3) === 0)
  526.         {
  527.             $strContent substr($strContent3);
  528.         }
  529.         elseif (strncmp($strContent"\xFF\xFE"2) === 0)
  530.         {
  531.             $strContent substr($strContent2);
  532.         }
  533.         elseif (strncmp($strContent"\xFE\xFF"2) === 0)
  534.         {
  535.             $strContent substr($strContent2);
  536.         }
  537.         return $strContent;
  538.     }
  539.     /**
  540.      * Write to a file
  541.      *
  542.      * @param string $strFile    Relative file name
  543.      * @param string $strContent Content to be written
  544.      */
  545.     public static function putContent($strFile$strContent)
  546.     {
  547.         $objFile = new static($strFile);
  548.         $objFile->write($strContent);
  549.         $objFile->close();
  550.     }
  551.     /**
  552.      * Return the file content as array
  553.      *
  554.      * @return array The file content as array
  555.      */
  556.     public function getContentAsArray()
  557.     {
  558.         return array_map('rtrim'file($this->strRootDir '/' $this->strFile));
  559.     }
  560.     /**
  561.      * Rename the file
  562.      *
  563.      * @param string $strNewName The new path
  564.      *
  565.      * @return boolean True if the operation was successful
  566.      */
  567.     public function renameTo($strNewName)
  568.     {
  569.         $strParent \dirname($strNewName);
  570.         // Create the parent folder if it does not exist
  571.         if (!is_dir($this->strRootDir '/' $strParent))
  572.         {
  573.             new Folder($strParent);
  574.         }
  575.         $return $this->Files->rename($this->strFile$strNewName);
  576.         // Update the database AFTER the file has been renamed
  577.         $syncSource Dbafs::shouldBeSynchronized($this->strFile);
  578.         $syncTarget Dbafs::shouldBeSynchronized($strNewName);
  579.         // Synchronize the database
  580.         if ($syncSource && $syncTarget)
  581.         {
  582.             $this->objModel Dbafs::moveResource($this->strFile$strNewName);
  583.         }
  584.         elseif ($syncSource)
  585.         {
  586.             $this->objModel Dbafs::deleteResource($this->strFile);
  587.         }
  588.         elseif ($syncTarget)
  589.         {
  590.             $this->objModel Dbafs::addResource($strNewName);
  591.         }
  592.         // Reset the object AFTER the database has been updated
  593.         if ($return)
  594.         {
  595.             $this->strFile $strNewName;
  596.             $this->arrImageSize = array();
  597.             $this->arrPathinfo = array();
  598.         }
  599.         return $return;
  600.     }
  601.     /**
  602.      * Copy the file
  603.      *
  604.      * @param string $strNewName The target path
  605.      *
  606.      * @return boolean True if the operation was successful
  607.      */
  608.     public function copyTo($strNewName)
  609.     {
  610.         $strParent \dirname($strNewName);
  611.         // Create the parent folder if it does not exist
  612.         if (!is_dir($this->strRootDir '/' $strParent))
  613.         {
  614.             new Folder($strParent);
  615.         }
  616.         $return $this->Files->copy($this->strFile$strNewName);
  617.         // Update the database AFTER the file has been renamed
  618.         $syncSource Dbafs::shouldBeSynchronized($this->strFile);
  619.         $syncTarget Dbafs::shouldBeSynchronized($strNewName);
  620.         // Synchronize the database
  621.         if ($syncSource && $syncTarget)
  622.         {
  623.             Dbafs::copyResource($this->strFile$strNewName);
  624.         }
  625.         elseif ($syncTarget)
  626.         {
  627.             Dbafs::addResource($strNewName);
  628.         }
  629.         return $return;
  630.     }
  631.     /**
  632.      * Resize the file if it is an image
  633.      *
  634.      * @param integer $width  The target width
  635.      * @param integer $height The target height
  636.      * @param string  $mode   The resize mode
  637.      *
  638.      * @return boolean True if the image could be resized successfully
  639.      */
  640.     public function resizeTo($width$height$mode='')
  641.     {
  642.         if (!$this->isImage)
  643.         {
  644.             return false;
  645.         }
  646.         System::getContainer()
  647.             ->get('contao.image.factory')
  648.             ->create($this->strRootDir '/' $this->strFile, array($width$height$mode), $this->strRootDir '/' $this->strFile)
  649.         ;
  650.         $this->arrPathinfo = array();
  651.         $this->arrImageSize = array();
  652.         // Clear the image size cache as mtime could potentially not change
  653.         unset(static::$arrImageSizeCache[$this->strFile '|' $this->mtime]);
  654.         return true;
  655.     }
  656.     /**
  657.      * Send the file to the browser
  658.      *
  659.      * @param string  $filename An optional filename
  660.      * @param boolean $inline   Show the file in the browser instead of opening the download dialog
  661.      *
  662.      * @throws ResponseException
  663.      */
  664.     public function sendToBrowser($filename=''$inline=false)
  665.     {
  666.         $response = new BinaryFileResponse($this->strRootDir '/' $this->strFile);
  667.         $response->setPrivate(); // public by default
  668.         $response->setAutoEtag();
  669.         $response->setContentDisposition
  670.         (
  671.             $inline ResponseHeaderBag::DISPOSITION_INLINE ResponseHeaderBag::DISPOSITION_ATTACHMENT,
  672.             $filename,
  673.             (new UnicodeString($this->basename))->ascii()->toString()
  674.         );
  675.         $response->headers->addCacheControlDirective('must-revalidate');
  676.         $response->headers->set('Connection''close');
  677.         $response->headers->set('Content-Type'$this->getMimeType());
  678.         throw new ResponseException($response);
  679.     }
  680.     /**
  681.      * Check if any parent folder contains a .public file
  682.      *
  683.      * @return bool
  684.      */
  685.     public function isUnprotected()
  686.     {
  687.         return (new Folder(\dirname($this->strFile)))->isUnprotected();
  688.     }
  689.     /**
  690.      * Write data to a file
  691.      *
  692.      * @param mixed  $varData The data to be written
  693.      * @param string $strMode The operation mode
  694.      *
  695.      * @return boolean True if the operation was successful
  696.      */
  697.     protected function fputs($varData$strMode)
  698.     {
  699.         if (!\is_resource($this->resFile))
  700.         {
  701.             $this->strTmp 'system/tmp/' md5(uniqid(mt_rand(), true));
  702.             // Copy the contents of the original file to append data
  703.             if (strncmp($strMode'a'1) === && file_exists($this->strRootDir '/' $this->strFile))
  704.             {
  705.                 $this->Files->copy($this->strFile$this->strTmp);
  706.             }
  707.             // Open the temporary file
  708.             if (!$this->resFile $this->Files->fopen($this->strTmp$strMode))
  709.             {
  710.                 return false;
  711.             }
  712.         }
  713.         fwrite($this->resFile$varData);
  714.         return true;
  715.     }
  716.     /**
  717.      * Return the mime type and icon of the file based on its extension
  718.      *
  719.      * @return array An array with mime type and icon name
  720.      */
  721.     protected function getMimeInfo()
  722.     {
  723.         return $GLOBALS['TL_MIME'][$this->extension] ?? array('application/octet-stream''iconPLAIN.svg');
  724.     }
  725.     /**
  726.      * Get the mime type of the file based on its extension
  727.      *
  728.      * @return string The mime type
  729.      */
  730.     protected function getMimeType()
  731.     {
  732.         $arrMime $this->getMimeInfo();
  733.         return $arrMime[0];
  734.     }
  735.     /**
  736.      * Return the file icon depending on the file type
  737.      *
  738.      * @return string The icon name
  739.      */
  740.     protected function getIcon()
  741.     {
  742.         $arrMime $this->getMimeInfo();
  743.         return $arrMime[1];
  744.     }
  745.     /**
  746.      * Return the MD5 hash of the file
  747.      *
  748.      * @return string The MD5 hash
  749.      */
  750.     protected function getHash()
  751.     {
  752.         return md5_file($this->strRootDir '/' $this->strFile);
  753.     }
  754.     /**
  755.      * Return the path info (binary-safe)
  756.      *
  757.      * @return array The path info
  758.      *
  759.      * @see https://github.com/PHPMailer/PHPMailer/blob/master/class.phpmailer.php#L3520
  760.      */
  761.     protected function getPathinfo()
  762.     {
  763.         $matches = array();
  764.         $return = array('dirname'=>'''basename'=>'''extension'=>'''filename'=>'');
  765.         preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^.\\\\/]+?)|))[\\\\/.]*$%m'$this->strFile$matches);
  766.         if (isset($matches[1]))
  767.         {
  768.             $return['dirname'] = $this->strRootDir '/' $matches[1]; // see #8325
  769.         }
  770.         if (isset($matches[2]))
  771.         {
  772.             $return['basename'] = $matches[2];
  773.         }
  774.         if (isset($matches[5]))
  775.         {
  776.             $return['extension'] = $matches[5];
  777.         }
  778.         if (isset($matches[3]))
  779.         {
  780.             $return['filename'] = $matches[3];
  781.         }
  782.         return $return;
  783.     }
  784. }
  785. class_alias(File::class, 'File');