[ Index ]

PHP Cross Reference of phpBB-3.1.12-deutsch

title

Body

[close]

/vendor/symfony/filesystem/Symfony/Component/Filesystem/ -> Filesystem.php (source)

   1  <?php
   2  
   3  /*
   4   * This file is part of the Symfony package.
   5   *
   6   * (c) Fabien Potencier <fabien@symfony.com>
   7   *
   8   * For the full copyright and license information, please view the LICENSE
   9   * file that was distributed with this source code.
  10   */
  11  
  12  namespace Symfony\Component\Filesystem;
  13  
  14  use Symfony\Component\Filesystem\Exception\IOException;
  15  
  16  /**
  17   * Provides basic utility to manipulate the file system.
  18   *
  19   * @author Fabien Potencier <fabien@symfony.com>
  20   */
  21  class Filesystem
  22  {
  23      /**
  24       * Copies a file.
  25       *
  26       * If the target file is older than the origin file, it's always overwritten.
  27       * If the target file is newer, it is overwritten only when the
  28       * $overwriteNewerFiles option is set to true.
  29       *
  30       * @param string $originFile          The original filename
  31       * @param string $targetFile          The target filename
  32       * @param bool   $overwriteNewerFiles If true, target files newer than origin files are overwritten
  33       *
  34       * @throws IOException When copy fails
  35       */
  36      public function copy($originFile, $targetFile, $overwriteNewerFiles = false)
  37      {
  38          if (stream_is_local($originFile) && !is_file($originFile)) {
  39              throw new IOException(sprintf('Failed to copy %s because file not exists', $originFile));
  40          }
  41  
  42          $this->mkdir(dirname($targetFile));
  43  
  44          $doCopy = true;
  45          if (!$overwriteNewerFiles && null === parse_url($originFile, PHP_URL_HOST) && is_file($targetFile)) {
  46              $doCopy = filemtime($originFile) > filemtime($targetFile);
  47          }
  48  
  49          if ($doCopy) {
  50              // https://bugs.php.net/bug.php?id=64634
  51              $source = fopen($originFile, 'r');
  52              // Stream context created to allow files overwrite when using FTP stream wrapper - disabled by default
  53              $target = fopen($targetFile, 'w', null, stream_context_create(array('ftp' => array('overwrite' => true))));
  54              stream_copy_to_stream($source, $target);
  55              fclose($source);
  56              fclose($target);
  57              unset($source, $target);
  58  
  59              if (!is_file($targetFile)) {
  60                  throw new IOException(sprintf('Failed to copy %s to %s', $originFile, $targetFile));
  61              }
  62          }
  63      }
  64  
  65      /**
  66       * Creates a directory recursively.
  67       *
  68       * @param string|array|\Traversable $dirs The directory path
  69       * @param int                       $mode The directory mode
  70       *
  71       * @throws IOException On any directory creation failure
  72       */
  73      public function mkdir($dirs, $mode = 0777)
  74      {
  75          foreach ($this->toIterator($dirs) as $dir) {
  76              if (is_dir($dir)) {
  77                  continue;
  78              }
  79  
  80              if (true !== @mkdir($dir, $mode, true)) {
  81                  $error = error_get_last();
  82                  if (!is_dir($dir)) {
  83                      // The directory was not created by a concurrent process. Let's throw an exception with a developer friendly error message if we have one
  84                      if ($error) {
  85                          throw new IOException(sprintf('Failed to create "%s": %s.', $dir, $error['message']));
  86                      }
  87                      throw new IOException(sprintf('Failed to create "%s"', $dir));
  88                  }
  89              }
  90          }
  91      }
  92  
  93      /**
  94       * Checks the existence of files or directories.
  95       *
  96       * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to check
  97       *
  98       * @return bool true if the file exists, false otherwise
  99       */
 100      public function exists($files)
 101      {
 102          foreach ($this->toIterator($files) as $file) {
 103              if ('\\' === DIRECTORY_SEPARATOR && strlen($file) > 258) {
 104                  throw new IOException(sprintf('Could not check if file exist because path length exceeds 258 characters for file "%s"', $file));
 105              }
 106  
 107              if (!file_exists($file)) {
 108                  return false;
 109              }
 110          }
 111  
 112          return true;
 113      }
 114  
 115      /**
 116       * Sets access and modification time of file.
 117       *
 118       * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to create
 119       * @param int                       $time  The touch time as a Unix timestamp
 120       * @param int                       $atime The access time as a Unix timestamp
 121       *
 122       * @throws IOException When touch fails
 123       */
 124      public function touch($files, $time = null, $atime = null)
 125      {
 126          foreach ($this->toIterator($files) as $file) {
 127              $touch = $time ? @touch($file, $time, $atime) : @touch($file);
 128              if (true !== $touch) {
 129                  throw new IOException(sprintf('Failed to touch %s', $file));
 130              }
 131          }
 132      }
 133  
 134      /**
 135       * Removes files or directories.
 136       *
 137       * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove
 138       *
 139       * @throws IOException When removal fails
 140       */
 141      public function remove($files)
 142      {
 143          if ($files instanceof \Traversable) {
 144              $files = iterator_to_array($files, false);
 145          } elseif (!is_array($files)) {
 146              $files = array($files);
 147          }
 148          $files = array_reverse($files);
 149          foreach ($files as $file) {
 150              if (is_link($file)) {
 151                  // See https://bugs.php.net/52176
 152                  if (!@(unlink($file) || '\\' !== DIRECTORY_SEPARATOR || rmdir($file)) && file_exists($file)) {
 153                      $error = error_get_last();
 154                      throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, $error['message']));
 155                  }
 156              } elseif (is_dir($file)) {
 157                  $this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS));
 158  
 159                  if (!@rmdir($file) && file_exists($file)) {
 160                      $error = error_get_last();
 161                      throw new IOException(sprintf('Failed to remove directory "%s": %s.', $file, $error['message']));
 162                  }
 163              } elseif (!@unlink($file) && file_exists($file)) {
 164                  $error = error_get_last();
 165                  throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, $error['message']));
 166              }
 167          }
 168      }
 169  
 170      /**
 171       * Change mode for an array of files or directories.
 172       *
 173       * @param string|array|\Traversable $files     A filename, an array of files, or a \Traversable instance to change mode
 174       * @param int                       $mode      The new mode (octal)
 175       * @param int                       $umask     The mode mask (octal)
 176       * @param bool                      $recursive Whether change the mod recursively or not
 177       *
 178       * @throws IOException When the change fail
 179       */
 180      public function chmod($files, $mode, $umask = 0000, $recursive = false)
 181      {
 182          foreach ($this->toIterator($files) as $file) {
 183              if (true !== @chmod($file, $mode & ~$umask)) {
 184                  throw new IOException(sprintf('Failed to chmod file %s', $file));
 185              }
 186              if ($recursive && is_dir($file) && !is_link($file)) {
 187                  $this->chmod(new \FilesystemIterator($file), $mode, $umask, true);
 188              }
 189          }
 190      }
 191  
 192      /**
 193       * Change the owner of an array of files or directories.
 194       *
 195       * @param string|array|\Traversable $files     A filename, an array of files, or a \Traversable instance to change owner
 196       * @param string                    $user      The new owner user name
 197       * @param bool                      $recursive Whether change the owner recursively or not
 198       *
 199       * @throws IOException When the change fail
 200       */
 201      public function chown($files, $user, $recursive = false)
 202      {
 203          foreach ($this->toIterator($files) as $file) {
 204              if ($recursive && is_dir($file) && !is_link($file)) {
 205                  $this->chown(new \FilesystemIterator($file), $user, true);
 206              }
 207              if (is_link($file) && function_exists('lchown')) {
 208                  if (true !== @lchown($file, $user)) {
 209                      throw new IOException(sprintf('Failed to chown file %s', $file));
 210                  }
 211              } else {
 212                  if (true !== @chown($file, $user)) {
 213                      throw new IOException(sprintf('Failed to chown file %s', $file));
 214                  }
 215              }
 216          }
 217      }
 218  
 219      /**
 220       * Change the group of an array of files or directories.
 221       *
 222       * @param string|array|\Traversable $files     A filename, an array of files, or a \Traversable instance to change group
 223       * @param string                    $group     The group name
 224       * @param bool                      $recursive Whether change the group recursively or not
 225       *
 226       * @throws IOException When the change fail
 227       */
 228      public function chgrp($files, $group, $recursive = false)
 229      {
 230          foreach ($this->toIterator($files) as $file) {
 231              if ($recursive && is_dir($file) && !is_link($file)) {
 232                  $this->chgrp(new \FilesystemIterator($file), $group, true);
 233              }
 234              if (is_link($file) && function_exists('lchgrp')) {
 235                  if (true !== @lchgrp($file, $group) || (defined('HHVM_VERSION') && !posix_getgrnam($group))) {
 236                      throw new IOException(sprintf('Failed to chgrp file %s', $file));
 237                  }
 238              } else {
 239                  if (true !== @chgrp($file, $group)) {
 240                      throw new IOException(sprintf('Failed to chgrp file %s', $file));
 241                  }
 242              }
 243          }
 244      }
 245  
 246      /**
 247       * Renames a file or a directory.
 248       *
 249       * @param string $origin    The origin filename or directory
 250       * @param string $target    The new filename or directory
 251       * @param bool   $overwrite Whether to overwrite the target if it already exists
 252       *
 253       * @throws IOException When target file or directory already exists
 254       * @throws IOException When origin cannot be renamed
 255       */
 256      public function rename($origin, $target, $overwrite = false)
 257      {
 258          // we check that target does not exist
 259          if (!$overwrite && $this->isReadable($target)) {
 260              throw new IOException(sprintf('Cannot rename because the target "%s" already exist.', $target));
 261          }
 262  
 263          if (true !== @rename($origin, $target)) {
 264              throw new IOException(sprintf('Cannot rename "%s" to "%s".', $origin, $target));
 265          }
 266      }
 267  
 268      /**
 269       * Tells whether a file exists and is readable.
 270       *
 271       * @param string $filename Path to the file.
 272       *
 273       * @throws IOException When windows path is longer than 258 characters
 274       */
 275      private function isReadable($filename)
 276      {
 277          if ('\\' === DIRECTORY_SEPARATOR && strlen($filename) > 258) {
 278              throw new IOException(sprintf('Could not check if file is readable because path length exceeds 258 characters for file "%s"', $filename));
 279          }
 280  
 281          return is_readable($filename);
 282      }
 283  
 284      /**
 285       * Creates a symbolic link or copy a directory.
 286       *
 287       * @param string $originDir     The origin directory path
 288       * @param string $targetDir     The symbolic link name
 289       * @param bool   $copyOnWindows Whether to copy files if on Windows
 290       *
 291       * @throws IOException When symlink fails
 292       */
 293      public function symlink($originDir, $targetDir, $copyOnWindows = false)
 294      {
 295          if ('\\' === DIRECTORY_SEPARATOR) {
 296              $originDir = strtr($originDir, '/', '\\');
 297              $targetDir = strtr($targetDir, '/', '\\');
 298  
 299              if ($copyOnWindows) {
 300                  $this->mirror($originDir, $targetDir);
 301  
 302                  return;
 303              }
 304          }
 305  
 306          $this->mkdir(dirname($targetDir));
 307  
 308          $ok = false;
 309          if (is_link($targetDir)) {
 310              if (readlink($targetDir) != $originDir) {
 311                  $this->remove($targetDir);
 312              } else {
 313                  $ok = true;
 314              }
 315          }
 316  
 317          if (!$ok && true !== @symlink($originDir, $targetDir)) {
 318              $report = error_get_last();
 319              if (is_array($report)) {
 320                  if ('\\' === DIRECTORY_SEPARATOR && false !== strpos($report['message'], 'error code(1314)')) {
 321                      throw new IOException('Unable to create symlink due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?');
 322                  }
 323              }
 324              throw new IOException(sprintf('Failed to create symbolic link from %s to %s', $originDir, $targetDir));
 325          }
 326      }
 327  
 328      /**
 329       * Given an existing path, convert it to a path relative to a given starting path.
 330       *
 331       * @param string $endPath   Absolute path of target
 332       * @param string $startPath Absolute path where traversal begins
 333       *
 334       * @return string Path of target relative to starting path
 335       */
 336      public function makePathRelative($endPath, $startPath)
 337      {
 338          // Normalize separators on Windows
 339          if ('\\' === DIRECTORY_SEPARATOR) {
 340              $endPath = str_replace('\\', '/', $endPath);
 341              $startPath = str_replace('\\', '/', $startPath);
 342          }
 343  
 344          // Split the paths into arrays
 345          $startPathArr = explode('/', trim($startPath, '/'));
 346          $endPathArr = explode('/', trim($endPath, '/'));
 347  
 348          // Find for which directory the common path stops
 349          $index = 0;
 350          while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) {
 351              ++$index;
 352          }
 353  
 354          // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels)
 355          $depth = count($startPathArr) - $index;
 356  
 357          // When we need to traverse from the start, and we are starting from a root path, don't add '../'
 358          if ('/' === $startPath[0] && 0 === $index && 1 === $depth) {
 359              $traverser = '';
 360          } else {
 361              // Repeated "../" for each level need to reach the common path
 362              $traverser = str_repeat('../', $depth);
 363          }
 364  
 365          $endPathRemainder = implode('/', array_slice($endPathArr, $index));
 366  
 367          // Construct $endPath from traversing to the common path, then to the remaining $endPath
 368          $relativePath = $traverser.('' !== $endPathRemainder ? $endPathRemainder.'/' : '');
 369  
 370          return '' === $relativePath ? './' : $relativePath;
 371      }
 372  
 373      /**
 374       * Mirrors a directory to another.
 375       *
 376       * @param string       $originDir The origin directory
 377       * @param string       $targetDir The target directory
 378       * @param \Traversable $iterator  A Traversable instance
 379       * @param array        $options   An array of boolean options
 380       *                                Valid options are:
 381       *                                - $options['override'] Whether to override an existing file on copy or not (see copy())
 382       *                                - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink())
 383       *                                - $options['delete'] Whether to delete files that are not in the source directory (defaults to false)
 384       *
 385       * @throws IOException When file type is unknown
 386       */
 387      public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array())
 388      {
 389          $targetDir = rtrim($targetDir, '/\\');
 390          $originDir = rtrim($originDir, '/\\');
 391  
 392          // Iterate in destination folder to remove obsolete entries
 393          if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) {
 394              $deleteIterator = $iterator;
 395              if (null === $deleteIterator) {
 396                  $flags = \FilesystemIterator::SKIP_DOTS;
 397                  $deleteIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($targetDir, $flags), \RecursiveIteratorIterator::CHILD_FIRST);
 398              }
 399              foreach ($deleteIterator as $file) {
 400                  $origin = str_replace($targetDir, $originDir, $file->getPathname());
 401                  if (!$this->exists($origin)) {
 402                      $this->remove($file);
 403                  }
 404              }
 405          }
 406  
 407          $copyOnWindows = false;
 408          if (isset($options['copy_on_windows'])) {
 409              $copyOnWindows = $options['copy_on_windows'];
 410          }
 411  
 412          if (null === $iterator) {
 413              $flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS;
 414              $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST);
 415          }
 416  
 417          if ($this->exists($originDir)) {
 418              $this->mkdir($targetDir);
 419          }
 420  
 421          foreach ($iterator as $file) {
 422              $target = str_replace($originDir, $targetDir, $file->getPathname());
 423  
 424              if ($copyOnWindows) {
 425                  if (is_file($file)) {
 426                      $this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
 427                  } elseif (is_dir($file)) {
 428                      $this->mkdir($target);
 429                  } else {
 430                      throw new IOException(sprintf('Unable to guess "%s" file type.', $file));
 431                  }
 432              } else {
 433                  if (is_link($file)) {
 434                      $this->symlink($file->getLinkTarget(), $target);
 435                  } elseif (is_dir($file)) {
 436                      $this->mkdir($target);
 437                  } elseif (is_file($file)) {
 438                      $this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
 439                  } else {
 440                      throw new IOException(sprintf('Unable to guess "%s" file type.', $file));
 441                  }
 442              }
 443          }
 444      }
 445  
 446      /**
 447       * Returns whether the file path is an absolute path.
 448       *
 449       * @param string $file A file path
 450       *
 451       * @return bool
 452       */
 453      public function isAbsolutePath($file)
 454      {
 455          return strspn($file, '/\\', 0, 1)
 456              || (strlen($file) > 3 && ctype_alpha($file[0])
 457                  && substr($file, 1, 1) === ':'
 458                  && strspn($file, '/\\', 2, 1)
 459              )
 460              || null !== parse_url($file, PHP_URL_SCHEME)
 461          ;
 462      }
 463  
 464      /**
 465       * @param mixed $files
 466       *
 467       * @return \Traversable
 468       */
 469      private function toIterator($files)
 470      {
 471          if (!$files instanceof \Traversable) {
 472              $files = new \ArrayObject(is_array($files) ? $files : array($files));
 473          }
 474  
 475          return $files;
 476      }
 477  
 478      /**
 479       * Atomically dumps content into a file.
 480       *
 481       * @param string   $filename The file to be written to.
 482       * @param string   $content  The data to write into the file.
 483       * @param null|int $mode     The file mode (octal). If null, file permissions are not modified
 484       *                           Deprecated since version 2.3.12, to be removed in 3.0.
 485       *
 486       * @throws IOException If the file cannot be written to.
 487       */
 488      public function dumpFile($filename, $content, $mode = 0666)
 489      {
 490          $dir = dirname($filename);
 491  
 492          if (!is_dir($dir)) {
 493              $this->mkdir($dir);
 494          } elseif (!is_writable($dir)) {
 495              throw new IOException(sprintf('Unable to write in the %s directory.', $dir));
 496          }
 497  
 498          $tmpFile = tempnam($dir, basename($filename));
 499  
 500          if (false === @file_put_contents($tmpFile, $content)) {
 501              throw new IOException(sprintf('Failed to write file "%s".', $filename));
 502          }
 503  
 504          if (null !== $mode) {
 505              $this->chmod($tmpFile, $mode);
 506          }
 507          $this->rename($tmpFile, $filename, true);
 508      }
 509  }


Generated: Thu Jan 11 00:25:41 2018 Cross-referenced by PHPXref 0.7.1