函数文档

copy_dir()

💡 云策文档标注

概述

copy_dir() 函数通过 WordPress 文件系统抽象层将目录从源位置复制到目标位置。它要求 WP_Filesystem() 已调用并设置,支持跳过特定文件或文件夹,并递归处理子目录。

关键要点

  • 函数用途:复制目录,依赖 WordPress 文件系统抽象层(如 $wp_filesystem)进行操作。
  • 参数说明:$from(源目录,字符串,必需),$to(目标目录,字符串,必需),$skip_list(跳过列表,字符串数组,可选,默认为空数组)。
  • 返回值:成功时返回 true,失败时返回 WP_Error 对象。
  • 前提条件:调用前需确保 WP_Filesystem() 已初始化,否则可能无法正常工作。
  • 错误处理:包括目录列表失败、目标目录创建失败、文件复制失败等场景,均返回 WP_Error。
  • 递归机制:自动处理子目录复制,并为子目录生成相应的 $sub_skip_list 以保持跳过逻辑。
  • 文件类型处理:区分文件('f')和目录('d'),文件复制失败时会尝试更改权限后重试,并调用 wp_opcache_invalidate() 清除 opcode 缓存。

代码示例

// 连接到文件系统
if ( ! WP_Filesystem() ) {
    // 无法连接到文件系统,可能需要 FTP 凭据等
    // 可以使用 request_filesystem_credentials() 请求凭据
    exit;
}

// 确保目标目录存在,如果不存在则创建
global $wp_filesystem;
$wp_filesystem->mkdir( $target_dir );

// 复制源目录中的所有文件到目标目录
copy_dir( $src_dir, $target_dir );

注意事项

  • 使用前必须调用 WP_Filesystem() 设置文件系统抽象,否则函数可能无法执行或出错。
  • 目标目录需存在或可创建,否则复制会失败并返回 WP_Error。
  • $skip_list 参数支持跳过特定文件或文件夹,对于子目录,函数会自动调整跳过列表以匹配路径。
  • 复制文件时,如果首次失败,会尝试更改文件权限(chmod 到 FS_CHMOD_FILE)后重试,以提高兼容性。
  • 函数递归处理目录,适用于嵌套结构,但需注意性能影响,尤其是在大型目录中。
  • 相关函数包括 move_dir()、WP_Upgrader::install_package() 等,常用于 WordPress 核心升级和插件/主题安装场景。

📄 原文内容

Copies a directory from one location to another via the WordPress Filesystem Abstraction.

Description

Assumes that WP_Filesystem() has already been called and setup.

Parameters

$fromstringrequired
Source directory.
$tostringrequired
Destination directory.
$skip_liststring[]optional
An array of files/folders to skip copying.

Default:array()

Return

true|WP_Error True on success, WP_Error on failure.

Source

function copy_dir( $from, $to, $skip_list = array() ) {
	global $wp_filesystem;

	$dirlist = $wp_filesystem->dirlist( $from );

	if ( false === $dirlist ) {
		return new WP_Error( 'dirlist_failed_copy_dir', __( 'Directory listing failed.' ), basename( $from ) );
	}

	$from = trailingslashit( $from );
	$to   = trailingslashit( $to );

	if ( ! $wp_filesystem->exists( $to ) && ! $wp_filesystem->mkdir( $to ) ) {
		return new WP_Error(
			'mkdir_destination_failed_copy_dir',
			__( 'Could not create the destination directory.' ),
			basename( $to )
		);
	}

	foreach ( (array) $dirlist as $filename => $fileinfo ) {
		if ( in_array( $filename, $skip_list, true ) ) {
			continue;
		}

		if ( 'f' === $fileinfo['type'] ) {
			if ( ! $wp_filesystem->copy( $from . $filename, $to . $filename, true, FS_CHMOD_FILE ) ) {
				// If copy failed, chmod file to 0644 and try again.
				$wp_filesystem->chmod( $to . $filename, FS_CHMOD_FILE );

				if ( ! $wp_filesystem->copy( $from . $filename, $to . $filename, true, FS_CHMOD_FILE ) ) {
					return new WP_Error( 'copy_failed_copy_dir', __( 'Could not copy file.' ), $to . $filename );
				}
			}

			wp_opcache_invalidate( $to . $filename );
		} elseif ( 'd' === $fileinfo['type'] ) {
			if ( ! $wp_filesystem->is_dir( $to . $filename ) ) {
				if ( ! $wp_filesystem->mkdir( $to . $filename, FS_CHMOD_DIR ) ) {
					return new WP_Error( 'mkdir_failed_copy_dir', __( 'Could not create directory.' ), $to . $filename );
				}
			}

			// Generate the $sub_skip_list for the subdirectory as a sub-set of the existing $skip_list.
			$sub_skip_list = array();

			foreach ( $skip_list as $skip_item ) {
				if ( str_starts_with( $skip_item, $filename . '/' ) ) {
					$sub_skip_list[] = preg_replace( '!^' . preg_quote( $filename, '!' ) . '/!i', '', $skip_item );
				}
			}

			$result = copy_dir( $from . $filename, $to . $filename, $sub_skip_list );

			if ( is_wp_error( $result ) ) {
				return $result;
			}
		}
	}

	return true;
}

Changelog

Version Description
2.5.0 Introduced.

User Contributed Notes

  1. Skip to note 2 content

    // Connecting to the filesystem.
    if ( ! WP_Filesystem() ) {
    	// Unable to connect to the filesystem, FTP credentials may be required or something.
    	// You can request these with request_filesystem_credentials()
    	exit;
    }
    
    // Don't forget that the target directory needs to exist.
    // If it doesn't already, you'll need to create it.
    global $wp_filesystem;
    $wp_filesystem->mkdir( $target_dir );
    
    // Now copy all the files in the source directory to the target directory.
    copy_dir( $src_dir, $target_dir );