Root/
<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Filesystem; use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Exception\FileNotFoundException; /** * Provides basic utility to manipulate the file system. * * @author Fabien Potencier <fabien@symfony.com> */ class Filesystem { /** * Copies a file. * * This method only copies the file if the origin file is newer than the target file. * * By default, if the target already exists, it is not overridden. * * @param string $originFile The original filename * @param string $targetFile The target filename * @param bool $override Whether to override an existing file or not * * @throws FileNotFoundException When originFile doesn't exist * @throws IOException When copy fails */ public function copy ( $originFile , $targetFile , $override = false) { if (stream_is_local( $originFile ) && ! is_file ( $originFile )) { throw new FileNotFoundException(sprintf( 'Failed to copy "%s" because file does not exist.' , $originFile ), 0, null, $originFile ); } $this -> mkdir (dirname( $targetFile )); if (! $override && is_file ( $targetFile ) && null === parse_url ( $originFile , PHP_URL_HOST)) { $doCopy = filemtime ( $originFile ) > filemtime ( $targetFile ); } else { $doCopy = true; } if ( $doCopy ) { $source = fopen ( $originFile , 'r' ); $target = fopen ( $targetFile , 'w' ); stream_copy_to_stream( $source , $target ); fclose( $source ); fclose( $target ); unset( $source , $target ); if (! is_file ( $targetFile )) { throw new IOException(sprintf( 'Failed to copy "%s" to "%s".' , $originFile , $targetFile ), 0, null, $originFile ); } } } /** * Creates a directory recursively. * * @param string|array|\Traversable $dirs The directory path * @param int $mode The directory mode * * @throws IOException On any directory creation failure */ public function mkdir ( $dirs , $mode = 0777) { foreach ( $this ->toIterator( $dirs ) as $dir ) { if ( is_dir ( $dir )) { continue ; } if (true !== @ mkdir ( $dir , $mode , true)) { throw new IOException(sprintf( 'Failed to create "%s".' , $dir ), 0, null, $dir ); } } } /** * Checks the existence of files or directories. * * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to check * * @return bool true if the file exists, false otherwise */ public function exists( $files ) { foreach ( $this ->toIterator( $files ) as $file ) { if (! file_exists ( $file )) { return false; } } return true; } /** * Sets access and modification time of file. * * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to create * @param int $time The touch time as a Unix timestamp * @param int $atime The access time as a Unix timestamp * * @throws IOException When touch fails */ public function touch( $files , $time = null, $atime = null) { foreach ( $this ->toIterator( $files ) as $file ) { $touch = $time ? @touch( $file , $time , $atime ) : @touch( $file ); if (true !== $touch ) { throw new IOException(sprintf( 'Failed to touch "%s".' , $file ), 0, null, $file ); } } } /** * Removes files or directories. * * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove * * @throws IOException When removal fails */ public function remove( $files ) { $files = iterator_to_array( $this ->toIterator( $files )); $files = array_reverse ( $files ); foreach ( $files as $file ) { if (! file_exists ( $file ) && ! is_link ( $file )) { continue ; } if ( is_dir ( $file ) && ! is_link ( $file )) { $this ->remove( new \FilesystemIterator( $file )); if (true !== @ rmdir ( $file )) { throw new IOException(sprintf( 'Failed to remove directory "%s".' , $file ), 0, null, $file ); } } else { if (defined( 'PHP_WINDOWS_VERSION_MAJOR' ) && is_dir ( $file )) { if (true !== @ rmdir ( $file )) { throw new IOException(sprintf( 'Failed to remove file "%s".' , $file ), 0, null, $file ); } } else { if (true !== @unlink( $file )) { throw new IOException(sprintf( 'Failed to remove file "%s".' , $file ), 0, null, $file ); } } } } } /** * Change mode for an array of files or directories. * * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change mode * @param int $mode The new mode (octal) * @param int $umask The mode mask (octal) * @param bool $recursive Whether change the mod recursively or not * * @throws IOException When the change fail */ public function chmod ( $files , $mode , $umask = 0000, $recursive = false) { foreach ( $this ->toIterator( $files ) as $file ) { if ( $recursive && is_dir ( $file ) && ! is_link ( $file )) { $this -> chmod ( new \FilesystemIterator( $file ), $mode , $umask , true); } if (true !== @ chmod ( $file , $mode & ~ $umask )) { throw new IOException(sprintf( 'Failed to chmod file "%s".' , $file ), 0, null, $file ); } } } /** * Change the owner of an array of files or directories * * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change owner * @param string $user The new owner user name * @param bool $recursive Whether change the owner recursively or not * * @throws IOException When the change fail */ public function chown ( $files , $user , $recursive = false) { foreach ( $this ->toIterator( $files ) as $file ) { if ( $recursive && is_dir ( $file ) && ! is_link ( $file )) { $this -> chown ( new \FilesystemIterator( $file ), $user , true); } if ( is_link ( $file ) && function_exists( 'lchown' )) { if (true !== @lchown( $file , $user )) { throw new IOException(sprintf( 'Failed to chown file "%s".' , $file ), 0, null, $file ); } } else { if (true !== @ chown ( $file , $user )) { throw new IOException(sprintf( 'Failed to chown file "%s".' , $file ), 0, null, $file ); } } } } /** * Change the group of an array of files or directories * * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change group * @param string $group The group name * @param bool $recursive Whether change the group recursively or not * * @throws IOException When the change fail */ public function chgrp ( $files , $group , $recursive = false) { foreach ( $this ->toIterator( $files ) as $file ) { if ( $recursive && is_dir ( $file ) && ! is_link ( $file )) { $this -> chgrp ( new \FilesystemIterator( $file ), $group , true); } if ( is_link ( $file ) && function_exists( 'lchgrp' )) { if (true !== @lchgrp( $file , $group )) { throw new IOException(sprintf( 'Failed to chgrp file "%s".' , $file ), 0, null, $file ); } } else { if (true !== @ chgrp ( $file , $group )) { throw new IOException(sprintf( 'Failed to chgrp file "%s".' , $file ), 0, null, $file ); } } } } /** * Renames a file or a directory. * * @param string $origin The origin filename or directory * @param string $target The new filename or directory * @param bool $overwrite Whether to overwrite the target if it already exists * * @throws IOException When target file or directory already exists * @throws IOException When origin cannot be renamed */ public function rename( $origin , $target , $overwrite = false) { // we check that target does not exist if (! $overwrite && is_readable ( $target )) { throw new IOException(sprintf( 'Cannot rename because the target "%s" already exists.' , $target ), 0, null, $target ); } if (true !== @rename( $origin , $target )) { throw new IOException(sprintf( 'Cannot rename "%s" to "%s".' , $origin , $target ), 0, null, $target ); } } /** * Creates a symbolic link or copy a directory. * * @param string $originDir The origin directory path * @param string $targetDir The symbolic link name * @param bool $copyOnWindows Whether to copy files if on Windows * * @throws IOException When symlink fails */ public function symlink( $originDir , $targetDir , $copyOnWindows = false) { if (!function_exists( 'symlink' ) && $copyOnWindows ) { $this ->mirror( $originDir , $targetDir ); return ; } $this -> mkdir (dirname( $targetDir )); $ok = false; if ( is_link ( $targetDir )) { if ( readlink ( $targetDir ) != $originDir ) { $this ->remove( $targetDir ); } else { $ok = true; } } if (! $ok ) { if (true !== @symlink( $originDir , $targetDir )) { $report = error_get_last(); if ( is_array ( $report )) { if (defined( 'PHP_WINDOWS_VERSION_MAJOR' ) && false !== strpos ( $report [ 'message' ], 'error code(1314)' )) { 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?' ); } } throw new IOException(sprintf( 'Failed to create symbolic link from "%s" to "%s".' , $originDir , $targetDir ), 0, null, $targetDir ); } } } /** * Given an existing path, convert it to a path relative to a given starting path * * @param string $endPath Absolute path of target * @param string $startPath Absolute path where traversal begins * * @return string Path of target relative to starting path */ public function makePathRelative( $endPath , $startPath ) { // Normalize separators on Windows if (defined( 'PHP_WINDOWS_VERSION_MAJOR' )) { $endPath = strtr ( $endPath , '\\' , '/' ); $startPath = strtr ( $startPath , '\\' , '/' ); } // Split the paths into arrays $startPathArr = explode ( '/' , trim( $startPath , '/' )); $endPathArr = explode ( '/' , trim( $endPath , '/' )); // Find for which directory the common path stops $index = 0; while (isset( $startPathArr [ $index ]) && isset( $endPathArr [ $index ]) && $startPathArr [ $index ] === $endPathArr [ $index ]) { $index ++; } // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels) $depth = count ( $startPathArr ) - $index ; // Repeated "../" for each level need to reach the common path $traverser = str_repeat ( '../' , $depth ); $endPathRemainder = implode( '/' , array_slice ( $endPathArr , $index )); // Construct $endPath from traversing to the common path, then to the remaining $endPath $relativePath = $traverser .( strlen ( $endPathRemainder ) > 0 ? $endPathRemainder . '/' : '' ); return ( strlen ( $relativePath ) === 0) ? './' : $relativePath ; } /** * Mirrors a directory to another. * * @param string $originDir The origin directory * @param string $targetDir The target directory * @param \Traversable $iterator A Traversable instance * @param array $options An array of boolean options * Valid options are: * - $options['override'] Whether to override an existing file on copy or not (see copy()) * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink()) * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false) * * @throws IOException When file type is unknown */ public function mirror( $originDir , $targetDir , \Traversable $iterator = null, $options = array ()) { $targetDir = rtrim( $targetDir , '/\\' ); $originDir = rtrim( $originDir , '/\\' ); // Iterate in destination folder to remove obsolete entries if ( $this ->exists( $targetDir ) && isset( $options [ 'delete' ]) && $options [ 'delete' ]) { $deleteIterator = $iterator ; if (null === $deleteIterator ) { $flags = \FilesystemIterator::SKIP_DOTS; $deleteIterator = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( $targetDir , $flags ), \RecursiveIteratorIterator::CHILD_FIRST); } foreach ( $deleteIterator as $file ) { $origin = str_replace ( $targetDir , $originDir , $file ->getPathname()); if (! $this ->exists( $origin )) { $this ->remove( $file ); } } } $copyOnWindows = false; if (isset( $options [ 'copy_on_windows' ]) && !function_exists( 'symlink' )) { $copyOnWindows = $options [ 'copy_on_windows' ]; } if (null === $iterator ) { $flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS; $iterator = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( $originDir , $flags ), \RecursiveIteratorIterator::SELF_FIRST); } foreach ( $iterator as $file ) { $target = str_replace ( $originDir , $targetDir , $file ->getPathname()); if ( $copyOnWindows ) { if ( is_link ( $file ) || is_file ( $file )) { $this -> copy ( $file , $target , isset( $options [ 'override' ]) ? $options [ 'override' ] : false); } elseif ( is_dir ( $file )) { $this -> mkdir ( $target ); } else { throw new IOException(sprintf( 'Unable to guess "%s" file type.' , $file ), 0, null, $file ); } } else { if ( is_link ( $file )) { $this ->symlink( $file ->getLinkTarget(), $target ); } elseif ( is_dir ( $file )) { $this -> mkdir ( $target ); } elseif ( is_file ( $file )) { $this -> copy ( $file , $target , isset( $options [ 'override' ]) ? $options [ 'override' ] : false); } else { throw new IOException(sprintf( 'Unable to guess "%s" file type.' , $file ), 0, null, $file ); } } } } /** * Returns whether the file path is an absolute path. * * @param string $file A file path * * @return bool */ public function isAbsolutePath( $file ) { if ( strspn ( $file , '/\\' , 0, 1) || ( strlen ( $file ) > 3 && ctype_alpha( $file [0]) && substr ( $file , 1, 1) === ':' && ( strspn ( $file , '/\\' , 2, 1)) ) || null !== parse_url ( $file , PHP_URL_SCHEME) ) { return true; } return false; } /** * Atomically dumps content into a file. * * @param string $filename The file to be written to. * @param string $content The data to write into the file. * @param null|int $mode The file mode (octal). If null, file permissions are not modified * Deprecated since version 2.3.12, to be removed in 3.0. * @throws IOException If the file cannot be written to. */ public function dumpFile( $filename , $content , $mode = 0666) { $dir = dirname( $filename ); if (! is_dir ( $dir )) { $this -> mkdir ( $dir ); } elseif (! is_writable ( $dir )) { throw new IOException(sprintf( 'Unable to write to the "%s" directory.' , $dir ), 0, null, $dir ); } $tmpFile = tempnam( $dir , basename ( $filename )); if (false === @ file_put_contents ( $tmpFile , $content )) { throw new IOException(sprintf( 'Failed to write file "%s".' , $filename ), 0, null, $filename ); } $this ->rename( $tmpFile , $filename , true); if (null !== $mode ) { $this -> chmod ( $filename , $mode ); } } /** * @param mixed $files * * @return \Traversable */ private function toIterator( $files ) { if (! $files instanceof \Traversable) { $files = new \ArrayObject( is_array ( $files ) ? $files : array ( $files )); } return $files ; } } |