Pastebin

filesystem.php

von Merluza

Dein Code:
  1. <?php
  2. /**
  3.  *
  4.  * This file is part of the phpBB Forum Software package.
  5.  *
  6.  * @copyright (c) phpBB Limited <https://www.phpbb.com>
  7.  * @license GNU General Public License, version 2 (GPL-2.0)
  8.  *
  9.  * For full copyright and license information, please see
  10.  * the docs/CREDITS.txt file.
  11.  *
  12.  */
  13.  
  14. namespace phpbb\filesystem;
  15.  
  16. use phpbb\filesystem\exception\filesystem_exception;
  17.  
  18. /**
  19.  * A class with various functions that are related to paths, files and the filesystem
  20.  */
  21. class filesystem implements filesystem_interface
  22. {
  23.         /**
  24.          * Store some information about file ownership for phpBB's chmod function
  25.          *
  26.          * @var array
  27.          */
  28.         protected $chmod_info;
  29.  
  30.         /**
  31.          * Stores current working directory
  32.          *
  33.          * @var string|bool             current working directory or false if it cannot be recovered
  34.          */
  35.         protected $working_directory;
  36.  
  37.         /**
  38.          * Symfony's Filesystem component
  39.          *
  40.          * @var \Symfony\Component\Filesystem\Filesystem
  41.          */
  42.         protected $symfony_filesystem;
  43.  
  44.         /**
  45.          * Constructor
  46.          */
  47.         public function __construct()
  48.         {
  49.                 $this->chmod_info                       = array();
  50.                 $this->symfony_filesystem       = new \Symfony\Component\Filesystem\Filesystem();
  51.                 $this->working_directory        = null;
  52.         }
  53.  
  54.         /**
  55.          * {@inheritdoc}
  56.          */
  57.         public function chgrp($files, $group, $recursive = false)
  58.         {
  59.                 try
  60.                 {
  61.                         $this->symfony_filesystem->chgrp($files, $group, $recursive);
  62.                 }
  63.                 catch (\Symfony\Component\Filesystem\Exception\IOException $e)
  64.                 {
  65.                         // Try to recover filename
  66.                         // By the time this is written that is at the end of the message
  67.                         $error = trim($e->getMessage());
  68.                         $file = substr($error, strrpos($error, ' '));
  69.  
  70.                         throw new filesystem_exception('CANNOT_CHANGE_FILE_GROUP', $file, array(), $e);
  71.                 }
  72.         }
  73.  
  74.         /**
  75.          * {@inheritdoc}
  76.          */
  77.         public function chmod($files, $perms = null, $recursive = false, $force_chmod_link = false)
  78.         {
  79.                 if (is_null($perms))
  80.                 {
  81.                         // Default to read permission for compatibility reasons
  82.                         $perms = self::CHMOD_READ;
  83.                 }
  84.  
  85.                 // Check if we got a permission flag
  86.                 if ($perms > self::CHMOD_ALL)
  87.                 {
  88.                         $file_perm = $perms;
  89.  
  90.                         // Extract permissions
  91.                         //$owner = ($file_perm >> 6) & 7; // This will be ignored
  92.                         $group = ($file_perm >> 3) & 7;
  93.                         $other = ($file_perm >> 0) & 7;
  94.  
  95.                         // Does any permissions provided? if so we add execute bit for directories
  96.                         $group = ($group !== 0) ? ($group | self::CHMOD_EXECUTE) : $group;
  97.                         $other = ($other !== 0) ? ($other | self::CHMOD_EXECUTE) : $other;
  98.  
  99.                         // Compute directory permissions
  100.                         $dir_perm = (self::CHMOD_ALL << 6) + ($group << 3) + ($other << 3);
  101.                 }
  102.                 else
  103.                 {
  104.                         // Add execute bit to owner if execute bit is among perms
  105.                         $owner_perm     = (self::CHMOD_READ | self::CHMOD_WRITE) | ($perms & self::CHMOD_EXECUTE);
  106.                         $file_perm      = ($owner_perm << 6) + ($perms << 3) + ($perms << 0);
  107.  
  108.                         // Compute directory permissions
  109.                         $perm = ($perms !== 0) ? ($perms | self::CHMOD_EXECUTE) : $perms;
  110.                         $dir_perm = (($owner_perm | self::CHMOD_EXECUTE) << 6) + ($perm << 3) + ($perm << 0);
  111.                 }
  112.  
  113.                 // Symfony's filesystem component does not support extra execution flags on directories
  114.                 // so we need to implement it again
  115.                 foreach ($this->to_iterator($files) as $file)
  116.                 {
  117.                         if ($recursive && is_dir($file) && !is_link($file))
  118.                         {
  119.                                 $this->chmod(new \FilesystemIterator($file), $perms, true);
  120.                         }
  121.  
  122.                         // Don't chmod links as mostly those require 0777 and that cannot be changed
  123.                         if (is_dir($file) || (is_link($file) && $force_chmod_link))
  124.                         {
  125.                                 if (true !== @chmod($file, $dir_perm))
  126.                                 {
  127.                                         throw new filesystem_exception('CANNOT_CHANGE_FILE_PERMISSIONS', $file,  array());
  128.                                 }
  129.                         }
  130.                         else if (is_file($file))
  131.                         {
  132.                                 if (true !== @chmod($file, $file_perm))
  133.                                 {
  134.                                         throw new filesystem_exception('CANNOT_CHANGE_FILE_PERMISSIONS', $file,  array());
  135.                                 }
  136.                         }
  137.                 }
  138.         }
  139.  
  140.         /**
  141.          * {@inheritdoc}
  142.          */
  143.         public function chown($files, $user, $recursive = false)
  144.         {
  145.                 try
  146.                 {
  147.                         $this->symfony_filesystem->chown($files, $user, $recursive);
  148.                 }
  149.                 catch (\Symfony\Component\Filesystem\Exception\IOException $e)
  150.                 {
  151.                         // Try to recover filename
  152.                         // By the time this is written that is at the end of the message
  153.                         $error = trim($e->getMessage());
  154.                         $file = substr($error, strrpos($error, ' '));
  155.  
  156.                         throw new filesystem_exception('CANNOT_CHANGE_FILE_GROUP', $file, array(), $e);
  157.                 }
  158.         }
  159.  
  160.         /**
  161.          * {@inheritdoc}
  162.          */
  163.         public function clean_path($path)
  164.         {
  165.                 $exploded = explode('/', $path);
  166.                 $filtered = array();
  167.                 foreach ($exploded as $part)
  168.                 {
  169.                         if ($part === '.' && !empty($filtered))
  170.                         {
  171.                                 continue;
  172.                         }
  173.  
  174.                         if ($part === '..' && !empty($filtered) && $filtered[count($filtered) - 1] !== '.' && $filtered[count($filtered) - 1] !== '..')
  175.                         {
  176.                                 array_pop($filtered);
  177.                         }
  178.                         else
  179.                         {
  180.                                 $filtered[] = $part;
  181.                         }
  182.                 }
  183.                 $path = implode('/', $filtered);
  184.                 return $path;
  185.         }
  186.  
  187.         /**
  188.          * {@inheritdoc}
  189.          */
  190.         public function copy($origin_file, $target_file, $override = false)
  191.         {
  192.                 try
  193.                 {
  194.                         $this->symfony_filesystem->copy($origin_file, $target_file, $override);
  195.                 }
  196.                 catch (\Symfony\Component\Filesystem\Exception\IOException $e)
  197.                 {
  198.                         throw new filesystem_exception('CANNOT_COPY_FILES', '', array(), $e);
  199.                 }
  200.         }
  201.  
  202.         /**
  203.          * {@inheritdoc}
  204.          */
  205.         public function dump_file($filename, $content)
  206.         {
  207.                 try
  208.                 {
  209.                         $this->symfony_filesystem->dumpFile($filename, $content);
  210.                 }
  211.                 catch (\Symfony\Component\Filesystem\Exception\IOException $e)
  212.                 {
  213.                         throw new filesystem_exception('CANNOT_DUMP_FILE', $filename, array(), $e);
  214.                 }
  215.         }
  216.  
  217.         /**
  218.          * {@inheritdoc}
  219.          */
  220.         public function exists($files)
  221.         {
  222.                 return $this->symfony_filesystem->exists($files);
  223.         }
  224.  
  225.         /**
  226.          * {@inheritdoc}
  227.          */
  228.         public function is_absolute_path($path)
  229.         {
  230.                 return (isset($path[0]) && $path[0] === '/' || preg_match('#^[a-z]:[/\\\]#i', $path)) ? true : false;
  231.         }
  232.  
  233.         /**
  234.          * {@inheritdoc}
  235.          */
  236.         public function is_readable($files, $recursive = false)
  237.         {
  238.                 foreach ($this->to_iterator($files) as $file)
  239.                 {
  240.                         if ($recursive && is_dir($file) && !is_link($file))
  241.                         {
  242.                                 if (!$this->is_readable(new \FilesystemIterator($file), true))
  243.                                 {
  244.                                         return false;
  245.                                 }
  246.                         }
  247.  
  248.                         if (!is_readable($file))
  249.                         {
  250.                                 return false;
  251.                         }
  252.                 }
  253.  
  254.                 return true;
  255.         }
  256.  
  257.         /**
  258.          * {@inheritdoc}
  259.          */
  260.         public function is_writable($files, $recursive = false)
  261.         {
  262.                 if (defined('PHP_WINDOWS_VERSION_MAJOR') || !function_exists('is_writable'))
  263.                 {
  264.                         foreach ($this->to_iterator($files) as $file)
  265.                         {
  266.                                 if ($recursive && is_dir($file) && !is_link($file))
  267.                                 {
  268.                                         if (!$this->is_writable(new \FilesystemIterator($file), true))
  269.                                         {
  270.                                                 return false;
  271.                                         }
  272.                                 }
  273.  
  274.                                 if (!$this->phpbb_is_writable($file))
  275.                                 {
  276.                                         return false;
  277.                                 }
  278.                         }
  279.                 }
  280.                 else
  281.                 {
  282.                         // use built in is_writable
  283.                         foreach ($this->to_iterator($files) as $file)
  284.                         {
  285.                                 if ($recursive && is_dir($file) && !is_link($file))
  286.                                 {
  287.                                         if (!$this->is_writable(new \FilesystemIterator($file), true))
  288.                                         {
  289.                                                 return false;
  290.                                         }
  291.                                 }
  292.  
  293.                                 if (!is_writable($file))
  294.                                 {
  295.                                         return false;
  296.                                 }
  297.                         }
  298.                 }
  299.  
  300.                 return true;
  301.         }
  302.  
  303.         /**
  304.          * {@inheritdoc}
  305.          */
  306.         public function make_path_relative($end_path, $start_path)
  307.         {
  308.                 return $this->symfony_filesystem->makePathRelative($end_path, $start_path);
  309.         }
  310.  
  311.         /**
  312.          * {@inheritdoc}
  313.          */
  314.         public function mirror($origin_dir, $target_dir, \Traversable $iterator = null, $options = array())
  315.         {
  316.                 try
  317.                 {
  318.                         $this->symfony_filesystem->mirror($origin_dir, $target_dir, $iterator, $options);
  319.                 }
  320.                 catch (\Symfony\Component\Filesystem\Exception\IOException $e)
  321.                 {
  322.                         $msg = $e->getMessage();
  323.                         $filename = substr($msg, strpos($msg, '"'), strrpos($msg, '"'));
  324.  
  325.                         throw new filesystem_exception('CANNOT_MIRROR_DIRECTORY', $filename, array(), $e);
  326.                 }
  327.         }
  328.  
  329.         /**
  330.          * {@inheritdoc}
  331.          */
  332.         public function mkdir($dirs, $mode = 0777)
  333.         {
  334.                 try
  335.                 {
  336.                         $this->symfony_filesystem->mkdir($dirs, $mode);
  337.                 }
  338.                 catch (\Symfony\Component\Filesystem\Exception\IOException $e)
  339.                 {
  340.                         $msg = $e->getMessage();
  341.                         $filename = substr($msg, strpos($msg, '"'), strrpos($msg, '"'));
  342.  
  343.                         throw new filesystem_exception('CANNOT_CREATE_DIRECTORY', $filename, array(), $e);
  344.                 }
  345.         }
  346.  
  347.         /**
  348.          * {@inheritdoc}
  349.          */
  350.         public function phpbb_chmod($files, $perms = null, $recursive = false, $force_chmod_link = false)
  351.         {
  352.                 if (is_null($perms))
  353.                 {
  354.                         // Default to read permission for compatibility reasons
  355.                         $perms = self::CHMOD_READ;
  356.                 }
  357.  
  358.                 if (empty($this->chmod_info))
  359.                 {
  360.                         if (!function_exists('fileowner') || !function_exists('filegroup'))
  361.                         {
  362.                                 $this->chmod_info['process'] = false;
  363.                         }
  364.                         else
  365.                         {
  366.                                 $common_php_owner       = @fileowner(__FILE__);
  367.                                 $common_php_group       = @filegroup(__FILE__);
  368.  
  369.                                 // And the owner and the groups PHP is running under.
  370.                                 $php_uid        = (function_exists('posic_getuid')) ? @posix_getuid() : false;
  371.                                 $php_gids       = (function_exists('posix_getgroups')) ? @posix_getgroups() : false;
  372.  
  373.                                 // If we are unable to get owner/group, then do not try to set them by guessing
  374.                                 if (!$php_uid || empty($php_gids) || !$common_php_owner || !$common_php_group)
  375.                                 {
  376.                                         $this->chmod_info['process'] = false;
  377.                                 }
  378.                                 else
  379.                                 {
  380.                                         $this->chmod_info = array(
  381.                                                 'process'               => true,
  382.                                                 'common_owner'  => $common_php_owner,
  383.                                                 'common_group'  => $common_php_group,
  384.                                                 'php_uid'               => $php_uid,
  385.                                                 'php_gids'              => $php_gids,
  386.                                         );
  387.                                 }
  388.                         }
  389.                 }
  390.  
  391.                 if ($this->chmod_info['process'])
  392.                 {
  393.                         try
  394.                         {
  395.                                 foreach ($this->to_iterator($files) as $file)
  396.                                 {
  397.                                         $file_uid = @fileowner($file);
  398.                                         $file_gid = @filegroup($file);
  399.  
  400.                                         // Change owner
  401.                                         if ($file_uid !== $this->chmod_info['common_owner'])
  402.                                         {
  403.                                                 $this->chown($file, $this->chmod_info['common_owner'], $recursive);
  404.                                         }
  405.  
  406.                                         // Change group
  407.                                         if ($file_gid !== $this->chmod_info['common_group'])
  408.                                         {
  409.                                                 $this->chgrp($file, $this->chmod_info['common_group'], $recursive);
  410.                                         }
  411.  
  412.                                         clearstatcache();
  413.                                         $file_uid = @fileowner($file);
  414.                                         $file_gid = @filegroup($file);
  415.                                 }
  416.                         }
  417.                         catch (filesystem_exception $e)
  418.                         {
  419.                                 $this->chmod_info['process'] = false;
  420.                         }
  421.                 }
  422.  
  423.                 // Still able to process?
  424.                 if ($this->chmod_info['process'])
  425.                 {
  426.                         if ($file_uid === $this->chmod_info['php_uid'])
  427.                         {
  428.                                 $php = 'owner';
  429.                         }
  430.                         else if (in_array($file_gid, $this->chmod_info['php_gids']))
  431.                         {
  432.                                 $php = 'group';
  433.                         }
  434.                         else
  435.                         {
  436.                                 // Since we are setting the everyone bit anyway, no need to do expensive operations
  437.                                 $this->chmod_info['process'] = false;
  438.                         }
  439.                 }
  440.  
  441.                 // We are not able to determine or change something
  442.                 if (!$this->chmod_info['process'])
  443.                 {
  444.                         $php = 'other';
  445.                 }
  446.  
  447.                 switch ($php)
  448.                 {
  449.                         case 'owner':
  450.                                 try
  451.                                 {
  452.                                         $this->chmod($files, $perms, $recursive, $force_chmod_link);
  453.                                         clearstatcache();
  454.                                         if ($this->is_readable($files) && $this->is_writable($files))
  455.                                         {
  456.                                                 break;
  457.                                         }
  458.                                 }
  459.                                 catch (filesystem_exception $e)
  460.                                 {
  461.                                         // Do nothing
  462.                                 }
  463.                         case 'group':
  464.                                 try
  465.                                 {
  466.                                         $this->chmod($files, $perms, $recursive, $force_chmod_link);
  467.                                         clearstatcache();
  468.                                         if ((!($perms & self::CHMOD_READ) || $this->is_readable($files, $recursive)) && (!($perms & self::CHMOD_WRITE) || $this->is_writable($files, $recursive)))
  469.                                         {
  470.                                                 break;
  471.                                         }
  472.                                 }
  473.                                 catch (filesystem_exception $e)
  474.                                 {
  475.                                         // Do nothing
  476.                                 }
  477.                         case 'other':
  478.                         default:
  479.                                 $this->chmod($files, $perms, $recursive, $force_chmod_link);
  480.                         break;
  481.                 }
  482.         }
  483.  
  484.         /**
  485.          * {@inheritdoc}
  486.          */
  487.         public function realpath($path)
  488.         {
  489.                 if (!function_exists('realpath'))
  490.                 {
  491.                         return $this->phpbb_own_realpath($path);
  492.                 }
  493.  
  494.                 $realpath = realpath($path);
  495.  
  496.                 // Strangely there are provider not disabling realpath but returning strange values. :o
  497.                 // We at least try to cope with them.
  498.                 if ((!$this->is_absolute_path($path) && $realpath === $path) || $realpath === false)
  499.                 {
  500.                         return $this->phpbb_own_realpath($path);
  501.                 }
  502.  
  503.                 // Check for DIRECTORY_SEPARATOR at the end (and remove it!)
  504.                 if (substr($realpath, -1) === DIRECTORY_SEPARATOR)
  505.                 {
  506.                         $realpath = substr($realpath, 0, -1);
  507.                 }
  508.  
  509.                 return $realpath;
  510.         }
  511.  
  512.         /**
  513.          * {@inheritdoc}
  514.          */
  515.         public function remove($files)
  516.         {
  517.                 try
  518.                 {
  519.                         $this->symfony_filesystem->remove($files);
  520.                 }
  521.                 catch (\Symfony\Component\Filesystem\Exception\IOException $e)
  522.                 {
  523.                         // Try to recover filename
  524.                         // By the time this is written that is at the end of the message
  525.                         $error = trim($e->getMessage());
  526.                         $file = substr($error, strrpos($error, ' '));
  527.  
  528.                         throw new filesystem_exception('CANNOT_DELETE_FILES', $file, array(), $e);
  529.                 }
  530.         }
  531.  
  532.         /**
  533.          * {@inheritdoc}
  534.          */
  535.         public function rename($origin, $target, $overwrite = false)
  536.         {
  537.                 try
  538.                 {
  539.                         $this->symfony_filesystem->rename($origin, $target, $overwrite);
  540.                 }
  541.                 catch (\Symfony\Component\Filesystem\Exception\IOException $e)
  542.                 {
  543.                         $msg = $e->getMessage();
  544.                         $filename = substr($msg, strpos($msg, '"'), strrpos($msg, '"'));
  545.  
  546.                         throw new filesystem_exception('CANNOT_RENAME_FILE', $filename, array(), $e);
  547.                 }
  548.         }
  549.  
  550.         /**
  551.          * {@inheritdoc}
  552.          */
  553.         public function symlink($origin_dir, $target_dir, $copy_on_windows = false)
  554.         {
  555.                 try
  556.                 {
  557.                         $this->symfony_filesystem->symlink($origin_dir, $target_dir, $copy_on_windows);
  558.                 }
  559.                 catch (\Symfony\Component\Filesystem\Exception\IOException $e)
  560.                 {
  561.                         throw new filesystem_exception('CANNOT_CREATE_SYMLINK', $origin_dir, array(), $e);
  562.                 }
  563.         }
  564.  
  565.         /**
  566.          * {@inheritdoc}
  567.          */
  568.         public function touch($files, $time = null, $access_time = null)
  569.         {
  570.                 try
  571.                 {
  572.                         $this->symfony_filesystem->touch($files, $time, $access_time);
  573.                 }
  574.                 catch (\Symfony\Component\Filesystem\Exception\IOException $e)
  575.                 {
  576.                         // Try to recover filename
  577.                         // By the time this is written that is at the end of the message
  578.                         $error = trim($e->getMessage());
  579.                         $file = substr($error, strrpos($error, ' '));
  580.  
  581.                         throw new filesystem_exception('CANNOT_TOUCH_FILES', $file, array(), $e);
  582.                 }
  583.         }
  584.  
  585.         /**
  586.          * phpBB's implementation of is_writable
  587.          *
  588.          * @todo Investigate if is_writable is still buggy
  589.          *
  590.          * @param string        $file   file/directory to check if writable
  591.          *
  592.          * @return bool true if the given path is writable
  593.          */
  594.         protected function phpbb_is_writable($file)
  595.         {
  596.                 if (file_exists($file))
  597.                 {
  598.                         // Canonicalise path to absolute path
  599.                         $file = $this->realpath($file);
  600.  
  601.                         if (is_dir($file))
  602.                         {
  603.                                 // Test directory by creating a file inside the directory
  604.                                 $result = @tempnam($file, 'i_w');
  605.  
  606.                                 if (is_string($result) && file_exists($result))
  607.                                 {
  608.                                         unlink($result);
  609.  
  610.                                         // Ensure the file is actually in the directory (returned realpathed)
  611.                                         return (strpos($result, $file) === 0) ? true : false;
  612.                                 }
  613.                         }
  614.                         else
  615.                         {
  616.                                 $handle = @fopen($file, 'c');
  617.  
  618.                                 if (is_resource($handle))
  619.                                 {
  620.                                         fclose($handle);
  621.                                         return true;
  622.                                 }
  623.                         }
  624.                 }
  625.                 else
  626.                 {
  627.                         // file does not exist test if we can write to the directory
  628.                         $dir = dirname($file);
  629.  
  630.                         if (file_exists($dir) && is_dir($dir) && $this->phpbb_is_writable($dir))
  631.                         {
  632.                                 return true;
  633.                         }
  634.                 }
  635.  
  636.                 return false;
  637.         }
  638.  
  639.         /**
  640.          * Try to resolve real path when PHP's realpath failes to do so
  641.          *
  642.          * @param string        $path
  643.          * @return bool|string
  644.          */
  645.         protected function phpbb_own_realpath($path)
  646.         {
  647.                 // Replace all directory separators with '/'
  648.                 $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
  649.  
  650.                 $is_absolute_path = false;
  651.                 $path_prefix = '';
  652.  
  653.                 if ($this->is_absolute_path($path))
  654.                 {
  655.                         $is_absolute_path = true;
  656.                 }
  657.                 else
  658.                 {
  659.                         // Resolve working directory and store it
  660.                         if (is_null($this->working_directory))
  661.                         {
  662.                                 if (function_exists('getcwd'))
  663.                                 {
  664.                                         $this->working_directory = str_replace(DIRECTORY_SEPARATOR, '/', getcwd());
  665.                                 }
  666.  
  667.                                 //
  668.                                 // From this point on we really just guessing
  669.                                 // If chdir were called we screwed
  670.                                 //
  671.                                 else if (function_exists('debug_backtrace'))
  672.                                 {
  673.                                         $call_stack = debug_backtrace(0);
  674.                                         $this->working_directory = str_replace(DIRECTORY_SEPARATOR, '/', dirname($call_stack[count($call_stack) - 1]['file']));
  675.                                 }
  676.                                 else
  677.                                 {
  678.                                         //
  679.                                         // Assuming that the working directory is phpBB root
  680.                                         // we could use this as a fallback, when phpBB will use controllers
  681.                                         // everywhere this will be a safe assumption
  682.                                         //
  683.                                         //$dir_parts = explode(DIRECTORY_SEPARATOR, __DIR__);
  684.                                         //$namespace_parts = explode('\\', trim(__NAMESPACE__, '\\'));
  685.  
  686.                                         //$namespace_part_count = count($namespace_parts);
  687.  
  688.                                         // Check if we still loading from root
  689.                                         //if (array_slice($dir_parts, -$namespace_part_count) === $namespace_parts)
  690.                                         //{
  691.                                         //      $this->working_directory = implode('/', array_slice($dir_parts, 0, -$namespace_part_count));
  692.                                         //}
  693.                                         //else
  694.                                         //{
  695.                                         //      $this->working_directory = false;
  696.                                         //}
  697.  
  698.                                         $this->working_directory = false;
  699.                                 }
  700.                         }
  701.  
  702.                         if ($this->working_directory !== false)
  703.                         {
  704.                                 $is_absolute_path = true;
  705.                                 $path = $this->working_directory . '/' . $path;
  706.                         }
  707.                 }
  708.  
  709.                 if ($is_absolute_path)
  710.                 {
  711.                         if (defined('PHP_WINDOWS_VERSION_MAJOR'))
  712.                         {
  713.                                 $path_prefix = $path[0] . ':';
  714.                                 $path = substr($path, 2);
  715.                         }
  716.                         else
  717.                         {
  718.                                 $path_prefix = '';
  719.                         }
  720.                 }
  721.  
  722.                 $resolved_path = $this->resolve_path($path, $path_prefix, $is_absolute_path);
  723.                 if ($resolved_path === false)
  724.                 {
  725.                         return false;
  726.                 }
  727.  
  728.                 if (!@file_exists($resolved_path) || (!@is_dir($resolved_path . '/') && !is_file($resolved_path)))
  729.                 {
  730.                         return false;
  731.                 }
  732.  
  733.                 // Return OS specific directory separators
  734.                 $resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved_path);
  735.  
  736.                 // Check for DIRECTORY_SEPARATOR at the end (and remove it!)
  737.                 if (substr($resolved, -1) === DIRECTORY_SEPARATOR)
  738.                 {
  739.                         return substr($resolved, 0, -1);
  740.                 }
  741.  
  742.                 return $resolved;
  743.         }
  744.  
  745.         /**
  746.          * Convert file(s) to \Traversable object
  747.          *
  748.          * This is the same function as Symfony's toIterator, but that is private
  749.          * so we cannot use it.
  750.          *
  751.          * @param string|array|\Traversable     $files  filename/list of filenames
  752.          * @return \Traversable
  753.          */
  754.         protected function to_iterator($files)
  755.         {
  756.                 if (!$files instanceof \Traversable)
  757.                 {
  758.                         $files = new \ArrayObject(is_array($files) ? $files : array($files));
  759.                 }
  760.  
  761.                 return $files;
  762.         }
  763.  
  764.         /**
  765.          * Try to resolve symlinks in path
  766.          *
  767.          * @param string        $path                   The path to resolve
  768.          * @param string        $prefix                 The path prefix (on windows the drive letter)
  769.          * @param bool          $absolute               Whether or not the path is absolute
  770.          * @param bool          $return_array   Whether or not to return path parts
  771.          *
  772.          * @return string|array|bool    returns the resolved path or an array of parts of the path if $return_array is true
  773.          *                                                              or false if path cannot be resolved
  774.          */
  775.         protected function resolve_path($path, $prefix = '', $absolute = false, $return_array = false)
  776.         {
  777.                 if ($return_array)
  778.                 {
  779.                         $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
  780.                 }
  781.  
  782.                 trim ($path, '/');
  783.                 $path_parts = explode('/', $path);
  784.                 $resolved = array();
  785.                 $resolved_path = $prefix;
  786.                 $file_found = false;
  787.  
  788.                 foreach ($path_parts as $path_part)
  789.                 {
  790.                         if ($file_found)
  791.                         {
  792.                                 return false;
  793.                         }
  794.  
  795.                         if (empty($path_part) || ($path_part === '.' && ($absolute || !empty($resolved))))
  796.                         {
  797.                                 continue;
  798.                         }
  799.                         else if ($absolute && $path_part === '..')
  800.                         {
  801.                                 if (empty($resolved))
  802.                                 {
  803.                                         // No directories above root
  804.                                         return false;
  805.                                 }
  806.  
  807.                                 array_pop($resolved);
  808.                                 $resolved_path = false;
  809.                         }
  810.                         else if ($path_part === '..' && !empty($resolved) && !in_array($resolved[count($resolved) - 1], array('.', '..')))
  811.                         {
  812.                                 array_pop($resolved);
  813.                                 $resolved_path = false;
  814.                         }
  815.                         else
  816.                         {
  817.                                 if ($resolved_path === false)
  818.                                 {
  819.                                         if (empty($resolved))
  820.                                         {
  821.                                                 $resolved_path = ($absolute) ? $prefix . '/' . $path_part : $path_part;
  822.                                         }
  823.                                         else
  824.                                         {
  825.                                                 $tmp_array = $resolved;
  826.                                                 if ($absolute)
  827.                                                 {
  828.                                                         array_unshift($tmp_array, $prefix);
  829.                                                 }
  830.  
  831.                                                 $resolved_path = implode('/', $tmp_array);
  832.                                         }
  833.                                 }
  834.  
  835.                                 $current_path = $resolved_path . '/' . $path_part;
  836.  
  837.                                 // Resolve symlinks
  838.                                 if (is_link($current_path))
  839.                                 {
  840.                                         if (!function_exists('readlink'))
  841.                                         {
  842.                                                 return false;
  843.                                         }
  844.  
  845.                                         $link = readlink($current_path);
  846.  
  847.                                         // Is link has an absolute path in it?
  848.                                         if ($this->is_absolute_path($link))
  849.                                         {
  850.                                                 if (defined('PHP_WINDOWS_VERSION_MAJOR'))
  851.                                                 {
  852.                                                         $prefix = $link[0] . ':';
  853.                                                         $link = substr($link, 2);
  854.                                                 }
  855.                                                 else
  856.                                                 {
  857.                                                         $prefix = '';
  858.                                                 }
  859.  
  860.                                                 $resolved = $this->resolve_path($link, $prefix, true, true);
  861.                                                 $absolute = true;
  862.                                         }
  863.                                         else
  864.                                         {
  865.                                                 $resolved = $this->resolve_path($resolved_path . '/' . $link, $prefix, $absolute, true);
  866.                                         }
  867.  
  868.                                         if (!$resolved)
  869.                                         {
  870.                                                 return false;
  871.                                         }
  872.  
  873.                                         $resolved_path = false;
  874.                                 }
  875.                                 else if (is_dir($current_path . '/'))
  876.                                 {
  877.                                         $resolved[] = $path_part;
  878.                                         $resolved_path = $current_path;
  879.                                 }
  880.                                 else if (is_file($current_path))
  881.                                 {
  882.                                         $resolved[] = $path_part;
  883.                                         $resolved_path = $current_path;
  884.                                         $file_found = true;
  885.                                 }
  886.                                 else
  887.                                 {
  888.                                         return false;
  889.                                 }
  890.                         }
  891.                 }
  892.  
  893.                 // If at the end of the path there were a .. or .
  894.                 // we need to build the path again.
  895.                 // Only doing this when a string is expected in return
  896.                 if ($resolved_path === false && $return_array === false)
  897.                 {
  898.                         if (empty($resolved))
  899.                         {
  900.                                 $resolved_path = ($absolute) ? $prefix . '/' : './';
  901.                         }
  902.                         else
  903.                         {
  904.                                 $tmp_array = $resolved;
  905.                                 if ($absolute)
  906.                                 {
  907.                                         array_unshift($tmp_array, $prefix);
  908.                                 }
  909.  
  910.                                 $resolved_path = implode('/', $tmp_array);
  911.                         }
  912.                 }
  913.  
  914.                 return ($return_array) ? $resolved : $resolved_path;
  915.         }
  916. }
  917.  

Quellcode

Hier kannst du den Code kopieren und ihn in deinen bevorzugten Editor einfügen. Alternativ kannst du den gesamten Eintrag auch als Datei herunterladen.