2011-03-26 07:37:25 +00:00
< ? 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 .
*/
2011-12-22 18:36:46 +00:00
namespace Symfony\Component\Filesystem ;
2011-03-26 07:37:25 +00:00
2012-06-18 11:41:52 +01:00
use Symfony\Component\Filesystem\Exception\IOException ;
2013-09-27 15:40:55 +01:00
use Symfony\Component\Filesystem\Exception\FileNotFoundException ;
2012-06-18 11:41:52 +01:00
2011-03-26 07:37:25 +00:00
/**
* Provides basic utility to manipulate the file system .
*
* @ author Fabien Potencier < fabien @ symfony . com >
*/
class Filesystem
{
/**
* Copies a file .
*
2016-03-16 14:32:55 +00:00
* If the target file is older than the origin file , it ' s always overwritten .
* If the target file is newer , it is overwritten only when the
* $overwriteNewerFiles option is set to true .
2011-03-26 07:37:25 +00:00
*
2016-03-16 14:32:55 +00:00
* @ param string $originFile The original filename
* @ param string $targetFile The target filename
* @ param bool $overwriteNewerFiles If true , target files newer than origin files are overwritten
2012-05-18 15:19:51 +01:00
*
2014-12-04 20:26:11 +00:00
* @ throws FileNotFoundException When originFile doesn ' t exist
* @ throws IOException When copy fails
2011-03-26 07:37:25 +00:00
*/
2016-03-16 14:32:55 +00:00
public function copy ( $originFile , $targetFile , $overwriteNewerFiles = false )
2011-03-26 07:37:25 +00:00
{
2017-07-05 08:25:28 +01:00
$originIsLocal = stream_is_local ( $originFile ) || 0 === stripos ( $originFile , 'file://' );
if ( $originIsLocal && ! is_file ( $originFile )) {
2013-09-27 15:53:24 +01:00
throw new FileNotFoundException ( sprintf ( 'Failed to copy "%s" because file does not exist.' , $originFile ), 0 , null , $originFile );
2013-04-28 16:32:44 +01:00
}
2011-03-26 07:37:25 +00:00
$this -> mkdir ( dirname ( $targetFile ));
2015-03-20 16:17:32 +00:00
$doCopy = true ;
2016-03-16 14:32:55 +00:00
if ( ! $overwriteNewerFiles && null === parse_url ( $originFile , PHP_URL_HOST ) && is_file ( $targetFile )) {
2011-08-26 09:34:23 +01:00
$doCopy = filemtime ( $originFile ) > filemtime ( $targetFile );
2011-03-26 07:37:25 +00:00
}
2011-08-26 09:34:23 +01:00
if ( $doCopy ) {
2013-04-28 16:32:44 +01:00
// https://bugs.php.net/bug.php?id=64634
2014-05-21 19:12:51 +01:00
if ( false === $source = @ fopen ( $originFile , 'r' )) {
throw new IOException ( sprintf ( 'Failed to copy "%s" to "%s" because source file could not be opened for reading.' , $originFile , $targetFile ), 0 , null , $originFile );
}
2014-09-03 10:12:11 +01:00
2014-08-26 12:48:14 +01:00
// Stream context created to allow files overwrite when using FTP stream wrapper - disabled by default
2014-09-04 22:09:17 +01:00
if ( false === $target = @ fopen ( $targetFile , 'w' , null , stream_context_create ( array ( 'ftp' => array ( 'overwrite' => true ))))) {
2014-05-21 19:12:51 +01:00
throw new IOException ( sprintf ( 'Failed to copy "%s" to "%s" because target file could not be opened for writing.' , $originFile , $targetFile ), 0 , null , $originFile );
}
2014-09-03 10:12:11 +01:00
2014-09-05 10:25:44 +01:00
$bytesCopied = stream_copy_to_stream ( $source , $target );
2013-04-28 16:32:44 +01:00
fclose ( $source );
fclose ( $target );
unset ( $source , $target );
if ( ! is_file ( $targetFile )) {
2013-09-27 15:53:24 +01:00
throw new IOException ( sprintf ( 'Failed to copy "%s" to "%s".' , $originFile , $targetFile ), 0 , null , $originFile );
2012-05-18 15:19:51 +01:00
}
2014-09-05 10:25:44 +01:00
2017-07-05 08:25:28 +01:00
if ( $originIsLocal ) {
// Like `cp`, preserve executable permission bits
@ chmod ( $targetFile , fileperms ( $targetFile ) | ( fileperms ( $originFile ) & 0111 ));
2014-11-29 10:51:28 +00:00
2017-07-05 08:25:28 +01:00
if ( $bytesCopied !== $bytesOrigin = filesize ( $originFile )) {
throw new IOException ( sprintf ( 'Failed to copy the whole content of "%s" to "%s" (%g of %g bytes copied).' , $originFile , $targetFile , $bytesCopied , $bytesOrigin ), 0 , null , $originFile );
}
2014-09-05 10:25:44 +01:00
}
2011-03-26 07:37:25 +00:00
}
}
/**
* Creates a directory recursively .
*
2012-05-15 21:19:31 +01:00
* @ param string | array | \Traversable $dirs The directory path
2014-04-12 18:54:57 +01:00
* @ param int $mode The directory mode
2011-03-26 07:37:25 +00:00
*
2012-06-18 11:41:52 +01:00
* @ throws IOException On any directory creation failure
2011-03-26 07:37:25 +00:00
*/
public function mkdir ( $dirs , $mode = 0777 )
{
foreach ( $this -> toIterator ( $dirs ) as $dir ) {
if ( is_dir ( $dir )) {
continue ;
}
2012-05-18 15:19:51 +01:00
if ( true !== @ mkdir ( $dir , $mode , true )) {
2014-08-19 09:20:36 +01:00
$error = error_get_last ();
if ( ! is_dir ( $dir )) {
// The directory was not created by a concurrent process. Let's throw an exception with a developer friendly error message if we have one
if ( $error ) {
2014-08-31 04:18:18 +01:00
throw new IOException ( sprintf ( 'Failed to create "%s": %s.' , $dir , $error [ 'message' ]), 0 , null , $dir );
2014-08-19 09:20:36 +01:00
}
2014-08-31 04:18:18 +01:00
throw new IOException ( sprintf ( 'Failed to create "%s"' , $dir ), 0 , null , $dir );
2014-08-19 09:20:36 +01:00
}
2012-05-18 15:19:51 +01:00
}
2011-03-26 07:37:25 +00:00
}
}
2012-06-15 17:09:23 +01:00
/**
* Checks the existence of files or directories .
*
* @ param string | array | \Traversable $files A filename , an array of files , or a \Traversable instance to check
*
2014-11-30 13:33:44 +00:00
* @ return bool true if the file exists , false otherwise
2012-06-15 17:09:23 +01:00
*/
public function exists ( $files )
{
foreach ( $this -> toIterator ( $files ) as $file ) {
2016-02-18 15:12:50 +00:00
if ( '\\' === DIRECTORY_SEPARATOR && strlen ( $file ) > 258 ) {
2016-02-18 16:03:55 +00:00
throw new IOException ( 'Could not check if file exist because path length exceeds 258 characters.' , 0 , null , $file );
2015-12-12 16:45:35 +00:00
}
2012-06-15 17:09:23 +01:00
if ( ! file_exists ( $file )) {
return false ;
}
}
return true ;
}
2011-03-26 07:37:25 +00:00
/**
2012-05-18 15:19:51 +01:00
* Sets access and modification time of file .
2011-03-26 07:37:25 +00:00
*
2012-04-07 00:05:37 +01:00
* @ param string | array | \Traversable $files A filename , an array of files , or a \Traversable instance to create
2014-04-12 18:54:57 +01:00
* @ param int $time The touch time as a Unix timestamp
* @ param int $atime The access time as a Unix timestamp
2012-05-18 15:19:51 +01:00
*
2012-06-18 11:41:52 +01:00
* @ throws IOException When touch fails
2011-03-26 07:37:25 +00:00
*/
2012-05-18 15:19:51 +01:00
public function touch ( $files , $time = null , $atime = null )
2011-03-26 07:37:25 +00:00
{
foreach ( $this -> toIterator ( $files ) as $file ) {
2013-01-04 11:28:25 +00:00
$touch = $time ? @ touch ( $file , $time , $atime ) : @ touch ( $file );
if ( true !== $touch ) {
2013-09-27 15:53:24 +01:00
throw new IOException ( sprintf ( 'Failed to touch "%s".' , $file ), 0 , null , $file );
2012-05-18 15:19:51 +01:00
}
2011-03-26 07:37:25 +00:00
}
}
/**
* Removes files or directories .
*
* @ param string | array | \Traversable $files A filename , an array of files , or a \Traversable instance to remove
2012-05-18 15:19:51 +01:00
*
2012-06-18 11:41:52 +01:00
* @ throws IOException When removal fails
2011-03-26 07:37:25 +00:00
*/
public function remove ( $files )
{
2016-04-12 09:59:55 +01:00
if ( $files instanceof \Traversable ) {
$files = iterator_to_array ( $files , false );
} elseif ( ! is_array ( $files )) {
$files = array ( $files );
}
2011-03-26 07:37:25 +00:00
$files = array_reverse ( $files );
foreach ( $files as $file ) {
2016-03-02 13:20:42 +00:00
if ( is_link ( $file )) {
2016-03-08 07:38:51 +00:00
// See https://bugs.php.net/52176
2016-04-12 09:59:55 +01:00
if ( !@ ( unlink ( $file ) || '\\' !== DIRECTORY_SEPARATOR || rmdir ( $file )) && file_exists ( $file )) {
$error = error_get_last ();
throw new IOException ( sprintf ( 'Failed to remove symlink "%s": %s.' , $file , $error [ 'message' ]));
}
2016-03-02 13:20:42 +00:00
} elseif ( is_dir ( $file )) {
2016-04-12 09:59:55 +01:00
$this -> remove ( new \FilesystemIterator ( $file , \FilesystemIterator :: CURRENT_AS_PATHNAME | \FilesystemIterator :: SKIP_DOTS ));
2011-03-26 07:37:25 +00:00
2016-04-12 09:59:55 +01:00
if ( !@ rmdir ( $file ) && file_exists ( $file )) {
2016-03-02 13:20:42 +00:00
$error = error_get_last ();
throw new IOException ( sprintf ( 'Failed to remove directory "%s": %s.' , $file , $error [ 'message' ]));
2012-05-18 15:19:51 +01:00
}
2016-04-12 09:59:55 +01:00
} elseif ( !@ unlink ( $file ) && file_exists ( $file )) {
2016-03-08 07:38:51 +00:00
$error = error_get_last ();
throw new IOException ( sprintf ( 'Failed to remove file "%s": %s.' , $file , $error [ 'message' ]));
2011-03-26 07:37:25 +00:00
}
}
}
/**
* Change mode for an array of files or directories .
*
2012-05-18 15:19:51 +01:00
* @ param string | array | \Traversable $files A filename , an array of files , or a \Traversable instance to change mode
2014-04-12 18:54:57 +01:00
* @ param int $mode The new mode ( octal )
* @ param int $umask The mode mask ( octal )
* @ param bool $recursive Whether change the mod recursively or not
2012-05-18 15:19:51 +01:00
*
2012-06-18 11:41:52 +01:00
* @ throws IOException When the change fail
2011-03-26 07:37:25 +00:00
*/
2012-05-18 15:19:51 +01:00
public function chmod ( $files , $mode , $umask = 0000 , $recursive = false )
2011-03-26 07:37:25 +00:00
{
2011-03-26 11:54:13 +00:00
foreach ( $this -> toIterator ( $files ) as $file ) {
2012-05-18 15:19:51 +01:00
if ( true !== @ chmod ( $file , $mode & ~ $umask )) {
2013-09-27 15:53:24 +01:00
throw new IOException ( sprintf ( 'Failed to chmod file "%s".' , $file ), 0 , null , $file );
2012-05-18 15:19:51 +01:00
}
2015-12-02 09:12:52 +00:00
if ( $recursive && is_dir ( $file ) && ! is_link ( $file )) {
$this -> chmod ( new \FilesystemIterator ( $file ), $mode , $umask , true );
}
2012-05-18 15:19:51 +01:00
}
}
/**
2014-12-21 17:00:50 +00:00
* Change the owner of an array of files or directories .
2012-05-18 15:19:51 +01:00
*
* @ 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
2014-04-12 18:54:57 +01:00
* @ param bool $recursive Whether change the owner recursively or not
2012-05-18 15:19:51 +01:00
*
2012-06-18 11:41:52 +01:00
* @ throws IOException When the change fail
2012-05-18 15:19:51 +01:00
*/
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 )) {
2013-09-27 15:53:24 +01:00
throw new IOException ( sprintf ( 'Failed to chown file "%s".' , $file ), 0 , null , $file );
2012-05-18 15:19:51 +01:00
}
} else {
if ( true !== @ chown ( $file , $user )) {
2013-09-27 15:53:24 +01:00
throw new IOException ( sprintf ( 'Failed to chown file "%s".' , $file ), 0 , null , $file );
2012-05-18 15:19:51 +01:00
}
}
}
}
/**
2014-12-21 17:00:50 +00:00
* Change the group of an array of files or directories .
2012-05-18 15:19:51 +01:00
*
* @ param string | array | \Traversable $files A filename , an array of files , or a \Traversable instance to change group
* @ param string $group The group name
2014-04-12 18:54:57 +01:00
* @ param bool $recursive Whether change the group recursively or not
2012-05-18 15:19:51 +01:00
*
2012-06-18 11:41:52 +01:00
* @ throws IOException When the change fail
2012-05-18 15:19:51 +01:00
*/
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' )) {
2015-06-30 07:51:58 +01:00
if ( true !== @ lchgrp ( $file , $group ) || ( defined ( 'HHVM_VERSION' ) && ! posix_getgrnam ( $group ))) {
2013-09-27 15:53:24 +01:00
throw new IOException ( sprintf ( 'Failed to chgrp file "%s".' , $file ), 0 , null , $file );
2012-05-18 15:19:51 +01:00
}
} else {
if ( true !== @ chgrp ( $file , $group )) {
2013-09-27 15:53:24 +01:00
throw new IOException ( sprintf ( 'Failed to chgrp file "%s".' , $file ), 0 , null , $file );
2012-05-18 15:19:51 +01:00
}
}
2011-03-26 07:37:25 +00:00
}
}
/**
2013-06-03 13:55:30 +01:00
* Renames a file or a directory .
2011-03-26 07:37:25 +00:00
*
2014-11-30 13:33:44 +00:00
* @ 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
2011-03-26 07:37:25 +00:00
*
2013-06-03 13:55:30 +01:00
* @ throws IOException When target file or directory already exists
2012-06-18 11:41:52 +01:00
* @ throws IOException When origin cannot be renamed
2011-03-26 07:37:25 +00:00
*/
2013-04-23 21:32:37 +01:00
public function rename ( $origin , $target , $overwrite = false )
2011-03-26 07:37:25 +00:00
{
// we check that target does not exist
2015-12-12 16:45:35 +00:00
if ( ! $overwrite && $this -> isReadable ( $target )) {
2013-09-27 15:53:24 +01:00
throw new IOException ( sprintf ( 'Cannot rename because the target "%s" already exists.' , $target ), 0 , null , $target );
2011-03-26 07:37:25 +00:00
}
2012-05-18 15:19:51 +01:00
if ( true !== @ rename ( $origin , $target )) {
2017-06-07 12:57:47 +01:00
if ( is_dir ( $origin )) {
// See https://bugs.php.net/bug.php?id=54097 & http://php.net/manual/en/function.rename.php#113943
$this -> mirror ( $origin , $target , null , array ( 'override' => $overwrite , 'delete' => $overwrite ));
$this -> remove ( $origin );
return ;
}
2013-09-27 15:53:24 +01:00
throw new IOException ( sprintf ( 'Cannot rename "%s" to "%s".' , $origin , $target ), 0 , null , $target );
2012-04-09 19:56:50 +01:00
}
2011-03-26 07:37:25 +00:00
}
2015-12-12 16:45:35 +00:00
/**
* Tells whether a file exists and is readable .
*
2016-06-28 06:50:50 +01:00
* @ param string $filename Path to the file
2015-12-12 16:45:35 +00:00
*
2016-09-02 19:13:13 +01:00
* @ return bool
*
2015-12-12 16:45:35 +00:00
* @ throws IOException When windows path is longer than 258 characters
*/
private function isReadable ( $filename )
{
2016-02-18 15:12:50 +00:00
if ( '\\' === DIRECTORY_SEPARATOR && strlen ( $filename ) > 258 ) {
2016-02-18 16:03:55 +00:00
throw new IOException ( 'Could not check if file is readable because path length exceeds 258 characters.' , 0 , null , $filename );
2015-12-12 16:45:35 +00:00
}
return is_readable ( $filename );
}
2011-03-26 07:37:25 +00:00
/**
* Creates a symbolic link or copy a directory .
*
2014-11-30 13:33:44 +00:00
* @ param string $originDir The origin directory path
* @ param string $targetDir The symbolic link name
* @ param bool $copyOnWindows Whether to copy files if on Windows
2012-05-18 15:19:51 +01:00
*
2012-06-18 11:41:52 +01:00
* @ throws IOException When symlink fails
2011-03-26 07:37:25 +00:00
*/
public function symlink ( $originDir , $targetDir , $copyOnWindows = false )
{
2016-01-30 17:41:36 +00:00
if ( '\\' === DIRECTORY_SEPARATOR ) {
$originDir = strtr ( $originDir , '/' , '\\' );
$targetDir = strtr ( $targetDir , '/' , '\\' );
2014-12-05 20:11:30 +00:00
2016-01-30 17:41:36 +00:00
if ( $copyOnWindows ) {
$this -> mirror ( $originDir , $targetDir );
return ;
}
2011-03-26 07:37:25 +00:00
}
2012-04-19 18:29:30 +01:00
$this -> mkdir ( dirname ( $targetDir ));
2011-03-26 07:37:25 +00:00
$ok = false ;
if ( is_link ( $targetDir )) {
if ( readlink ( $targetDir ) != $originDir ) {
2012-05-18 15:19:51 +01:00
$this -> remove ( $targetDir );
2011-03-26 07:37:25 +00:00
} else {
$ok = true ;
}
}
2015-03-20 16:17:32 +00:00
if ( ! $ok && true !== @ symlink ( $originDir , $targetDir )) {
$report = error_get_last ();
if ( is_array ( $report )) {
if ( '\\' === DIRECTORY_SEPARATOR && false !== strpos ( $report [ 'message' ], 'error code(1314)' )) {
2015-08-26 19:16:18 +01:00
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?' , 0 , null , $targetDir );
2012-06-18 09:34:56 +01:00
}
2012-05-18 15:19:51 +01:00
}
2015-08-26 19:16:18 +01:00
throw new IOException ( sprintf ( 'Failed to create symbolic link from "%s" to "%s".' , $originDir , $targetDir ), 0 , null , $targetDir );
2011-03-26 07:37:25 +00:00
}
}
2011-09-28 16:31:08 +01:00
/**
2014-12-21 17:00:50 +00:00
* Given an existing path , convert it to a path relative to a given starting path .
2011-09-28 16:31:08 +01:00
*
2012-04-07 00:05:37 +01:00
* @ param string $endPath Absolute path of target
* @ param string $startPath Absolute path where traversal begins
2011-09-28 16:31:08 +01:00
*
* @ return string Path of target relative to starting path
*/
public function makePathRelative ( $endPath , $startPath )
{
2013-12-27 15:08:19 +00:00
// Normalize separators on Windows
2014-12-29 12:05:07 +00:00
if ( '\\' === DIRECTORY_SEPARATOR ) {
2015-08-25 13:59:33 +01:00
$endPath = str_replace ( '\\' , '/' , $endPath );
$startPath = str_replace ( '\\' , '/' , $startPath );
2012-04-19 11:37:15 +01:00
}
2012-07-10 23:22:37 +01:00
// Split the paths into arrays
$startPathArr = explode ( '/' , trim ( $startPath , '/' ));
$endPathArr = explode ( '/' , trim ( $endPath , '/' ));
2017-03-21 08:40:59 +00:00
if ( '/' !== $startPath [ 0 ]) {
array_shift ( $startPathArr );
}
if ( '/' !== $endPath [ 0 ]) {
array_shift ( $endPathArr );
}
$normalizePathArray = function ( $pathSegments ) {
$result = array ();
foreach ( $pathSegments as $segment ) {
if ( '..' === $segment ) {
array_pop ( $result );
} else {
$result [] = $segment ;
}
}
return $result ;
};
$startPathArr = $normalizePathArray ( $startPathArr );
$endPathArr = $normalizePathArray ( $endPathArr );
2012-07-10 23:22:37 +01:00
// Find for which directory the common path stops
$index = 0 ;
while ( isset ( $startPathArr [ $index ]) && isset ( $endPathArr [ $index ]) && $startPathArr [ $index ] === $endPathArr [ $index ]) {
2015-03-31 00:07:44 +01:00
++ $index ;
2011-09-28 16:31:08 +01:00
}
// Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels)
2016-07-29 15:40:15 +01:00
if ( count ( $startPathArr ) === 1 && $startPathArr [ 0 ] === '' ) {
$depth = 0 ;
} else {
$depth = count ( $startPathArr ) - $index ;
}
2011-09-28 16:31:08 +01:00
2015-03-26 10:06:01 +00:00
// When we need to traverse from the start, and we are starting from a root path, don't add '../'
2016-07-29 15:40:15 +01:00
if ( '/' === $startPath [ 0 ] && 0 === $index && 0 === $depth ) {
2015-03-26 10:06:01 +00:00
$traverser = '' ;
} else {
// Repeated "../" for each level need to reach the common path
$traverser = str_repeat ( '../' , $depth );
}
2011-09-28 16:31:08 +01:00
2012-07-10 23:22:37 +01:00
$endPathRemainder = implode ( '/' , array_slice ( $endPathArr , $index ));
2011-09-28 16:31:08 +01:00
// Construct $endPath from traversing to the common path, then to the remaining $endPath
2015-03-20 16:17:32 +00:00
$relativePath = $traverser . ( '' !== $endPathRemainder ? $endPathRemainder . '/' : '' );
2012-07-10 23:22:37 +01:00
2015-03-20 16:17:32 +00:00
return '' === $relativePath ? './' : $relativePath ;
2011-09-28 16:31:08 +01:00
}
2011-03-26 07:37:25 +00:00
/**
* Mirrors a directory to another .
*
2012-05-15 21:19:31 +01:00
* @ 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
2014-11-30 13:33:44 +00:00
* 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 )
2011-03-26 07:37:25 +00:00
*
2012-06-18 11:41:52 +01:00
* @ throws IOException When file type is unknown
2011-03-26 07:37:25 +00:00
*/
public function mirror ( $originDir , $targetDir , \Traversable $iterator = null , $options = array ())
{
2012-11-14 21:58:01 +00:00
$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 );
}
}
}
2011-08-26 09:47:18 +01:00
$copyOnWindows = false ;
2014-07-05 12:15:16 +01:00
if ( isset ( $options [ 'copy_on_windows' ])) {
2011-08-26 09:47:18 +01:00
$copyOnWindows = $options [ 'copy_on_windows' ];
}
2011-03-26 07:37:25 +00:00
if ( null === $iterator ) {
2011-08-26 09:47:18 +01:00
$flags = $copyOnWindows ? \FilesystemIterator :: SKIP_DOTS | \FilesystemIterator :: FOLLOW_SYMLINKS : \FilesystemIterator :: SKIP_DOTS ;
$iterator = new \RecursiveIteratorIterator ( new \RecursiveDirectoryIterator ( $originDir , $flags ), \RecursiveIteratorIterator :: SELF_FIRST );
2011-03-26 07:37:25 +00:00
}
2014-11-08 19:35:51 +00:00
if ( $this -> exists ( $originDir )) {
$this -> mkdir ( $targetDir );
}
2011-03-26 07:37:25 +00:00
foreach ( $iterator as $file ) {
2012-04-09 15:14:36 +01:00
$target = str_replace ( $originDir , $targetDir , $file -> getPathname ());
2011-03-26 07:37:25 +00:00
2012-11-19 21:14:30 +00:00
if ( $copyOnWindows ) {
2016-03-08 07:38:51 +00:00
if ( is_file ( $file )) {
2012-11-19 21:14:30 +00:00
$this -> copy ( $file , $target , isset ( $options [ 'override' ]) ? $options [ 'override' ] : false );
2012-12-11 10:40:22 +00:00
} elseif ( is_dir ( $file )) {
2012-11-19 21:14:30 +00:00
$this -> mkdir ( $target );
} else {
2013-09-27 15:53:24 +01:00
throw new IOException ( sprintf ( 'Unable to guess "%s" file type.' , $file ), 0 , null , $file );
2012-11-19 21:14:30 +00:00
}
2011-03-26 07:37:25 +00:00
} else {
2012-11-19 21:14:30 +00:00
if ( is_link ( $file )) {
2015-06-26 21:41:07 +01:00
$this -> symlink ( $file -> getLinkTarget (), $target );
2012-12-11 10:40:22 +00:00
} elseif ( is_dir ( $file )) {
2012-11-19 21:14:30 +00:00
$this -> mkdir ( $target );
2012-12-11 10:40:22 +00:00
} elseif ( is_file ( $file )) {
2012-11-19 21:14:30 +00:00
$this -> copy ( $file , $target , isset ( $options [ 'override' ]) ? $options [ 'override' ] : false );
} else {
2013-09-27 15:53:24 +01:00
throw new IOException ( sprintf ( 'Unable to guess "%s" file type.' , $file ), 0 , null , $file );
2012-11-19 21:14:30 +00:00
}
2011-03-26 07:37:25 +00:00
}
}
}
2011-05-31 10:04:23 +01:00
/**
* Returns whether the file path is an absolute path .
*
* @ param string $file A file path
*
2014-04-16 11:30:19 +01:00
* @ return bool
2011-05-31 10:04:23 +01:00
*/
public function isAbsolutePath ( $file )
{
2015-12-01 11:58:24 +00:00
return strspn ( $file , '/\\' , 0 , 1 )
2011-05-31 10:04:23 +01:00
|| ( strlen ( $file ) > 3 && ctype_alpha ( $file [ 0 ])
2012-06-13 13:39:17 +01:00
&& substr ( $file , 1 , 1 ) === ':'
2016-01-06 13:34:50 +00:00
&& strspn ( $file , '/\\' , 2 , 1 )
2011-05-31 10:04:23 +01:00
)
2011-12-08 12:44:49 +00:00
|| null !== parse_url ( $file , PHP_URL_SCHEME )
2015-12-01 11:58:24 +00:00
;
2011-05-31 10:04:23 +01:00
}
2013-04-21 08:24:34 +01:00
/**
* Atomically dumps content into a file .
*
2016-06-28 06:50:50 +01:00
* @ param string $filename The file to be written to
* @ param string $content The data to write into the file
2014-11-30 13:33:44 +00:00
* @ 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 .
2013-04-21 08:24:34 +01:00
*/
public function dumpFile ( $filename , $content , $mode = 0666 )
{
$dir = dirname ( $filename );
if ( ! is_dir ( $dir )) {
$this -> mkdir ( $dir );
2017-01-08 12:55:49 +00:00
}
if ( ! is_writable ( $dir )) {
2013-09-27 15:53:24 +01:00
throw new IOException ( sprintf ( 'Unable to write to the "%s" directory.' , $dir ), 0 , null , $dir );
2013-04-21 08:24:34 +01:00
}
$tmpFile = tempnam ( $dir , basename ( $filename ));
if ( false === @ file_put_contents ( $tmpFile , $content )) {
2013-09-27 15:53:24 +01:00
throw new IOException ( sprintf ( 'Failed to write file "%s".' , $filename ), 0 , null , $filename );
2013-04-21 08:24:34 +01:00
}
2014-03-19 15:50:37 +00:00
if ( null !== $mode ) {
2015-04-23 22:36:48 +01:00
if ( func_num_args () > 2 ) {
2015-06-07 07:33:05 +01:00
@ trigger_error ( 'Support for modifying file permissions is deprecated since version 2.3.12 and will be removed in 3.0.' , E_USER_DEPRECATED );
2015-04-23 22:36:48 +01:00
}
2015-12-31 08:33:31 +00:00
$this -> chmod ( $tmpFile , $mode );
2017-03-06 18:56:39 +00:00
} elseif ( file_exists ( $filename )) {
@ chmod ( $tmpFile , fileperms ( $filename ));
2014-03-19 15:50:37 +00:00
}
2017-03-01 18:26:18 +00:00
2015-12-31 08:33:31 +00:00
$this -> rename ( $tmpFile , $filename , true );
2013-04-21 08:24:34 +01:00
}
2013-09-27 15:40:55 +01:00
/**
* @ param mixed $files
*
* @ return \Traversable
*/
private function toIterator ( $files )
{
if ( ! $files instanceof \Traversable ) {
$files = new \ArrayObject ( is_array ( $files ) ? $files : array ( $files ));
}
return $files ;
}
2011-03-26 07:37:25 +00:00
}