文件系统

WordPress 文件系统 API 是一个可让开发者和插件可以方便地在 WordPress 中访问、读取和写入文件的工具。它提供了统一的文件管理接口,可以让开发人员使用不同的文件系统,如本地文件系统、FTP、SSH、Amazon S3 等,而不必担心 API 如何工作。以下是一些常用的文件系统 API 函数和说明。

WP_Filesystem

WP_Filesystem 是 WordPress 文件系统 API 的主要类。它允许开发人员使用命令式 API 访问各种文件系统,而不必考虑文件系统的实现细节。

使用时,首先需要调用 WP_Filesystem 的 init() 方法进行初始化。 WP_Filesystem 使用全局变量 $wp_filesystem 来处理文件系统操作。因此,在正确调用 init() 方法之前,$wp_filesystem 变量只是一个空变量,并且无法对文件系统执行任何操作。

以下是初始化 WP_Filesystem 的示例代码:

// 初始化 WP_Filesystem
if ( ! defined( 'WP_FS_METHOD' ) ) {
  define( 'WP_FS_METHOD', 'direct' );
}

include_once( ABSPATH . 'wp-admin/includes/file.php' );

if ( function_exists( 'WP_Filesystem' ) ) {
  WP_Filesystem();
}

在这个例子中,我们首先定义了 WP_FS_METHOD 常量, 以指定我们希望使用的文件系统方法。 'direct' 是 WordPress 默认的本地文件系统方法。然后,我们 include 了 file.php 文件,这个文件包含了所有 WP_Filesystem 的相关函数。最后,我们调用 WP_Filesystem() 函数,这会自动初始化 WP_Filesystem 并将其赋值给 $wp_filesystem 变量,以及 $method 变量记录所选用的文件系统方法。

WP_Filesystem 的操作函数

初始化 WP_Filesystem 后,就可以使用一组统一的操作来读写文件,如下所示:

  • WP_Filesystem->get_contents($file):获取指定文件的内容
  • WP_Filesystem->put_contents($file, $content):将给定内容写入指定文件中
  • WP_Filesystem->copy($source, $dest, $overwrite):将 $source 文件复制到 $dest 给定的目的地,并选择是否覆盖已有文件
  • WP_Filesystem->delete($file):删除指定文件
  • WP_Filesystem->exists($file):检查给定的文件路径是否存在,并返回 true 或 false
  • WP_Filesystem->mkdir($path, $chmod):创建新目录,并指定它的权限

这些操作函数与不同的文件系统类型兼容。 WP_Filesystem 类中其他的方法也允许开发者执行更高级别的文件系统操作,如列出目录、重命名文件、获取文件信息等等。

使用 temp 文件

WP_Filesystem 还可以创建在 WordPress 安装目录中私有的、不受版本控制的临时文件。为此,我们可以使用如下代码:

$tempfile = $wp_filesystem->wp_tempnam();
file_put_contents( $tempfile, 'Hello World!' );

在这个例子中,我们使用 wp_tempnam() 方法来创建一个临时文件名,并用 file_put_contents() 方法将内容写到临时文件中。

文件系统 API 类参考

  • 类别:  WP_Filesystem_Base
  • 类别:  WP_Filesystem_Direct
  • 类别:  WP_Filesystem_FTPext
  • 类别:  WP_Filesystem_ftpsocket
  • 类别:  WP_Filesystem_SSH2
  • 功能: request_filesystem_credentials()

获取凭证

使用 WP_Filesystem 的第一步是向用户请求凭据。完成此操作的正常方法是在您保存表单输入的结果时,或者您已经确定需要写入文件时。

可以使用以下代码将凭据表单显示在管理页面上:

$url = wp_nonce_url( 'themes.php?page=example', 'example-theme-options' );
if ( false === ( $creds = request_filesystem_credentials( $url, '', false, false, null ) ) ) {
	return; // stop processing here
}

request_filesystem_credentials () 调用有五个参数。

  • 表单应提交到的 URL(在上面的示例中使用了指向主题页面的随机 URL)
  • 一个方法重写(通常你应该将其保留为空字符串:“”)
  • 错误标志(通常为假,除非检测到错误,见下文)
  • 上下文目录(false,或要测试访问权限的特定目录路径)
  • 表单字段(您希望“传递”生成的凭据表单的先前表单中的表单字段名称数组,如果没有,则为 null)

request_filesystem_credentials调用将首先测试它是否能够在没有凭据的情况下直接写入本地文件系统。如果是这种情况,那么它将返回 true 并且不执行任何操作。然后您的代码可以继续使用该类WP_Filesystem

request_filesystem_credentials调用还考虑了wp-config.php使用定义插入到文件中的硬编码信息,例如主机名或用户名或密码。如果这些是在该文件中预定义的,那么此调用将返回该信息而不是显示表单,绕过用户的表单。

如果它确实需要用户的凭据,那么它将输出 FTP 信息表单并返回 false。在这种情况下,您应该停止进一步处理,以允许用户输入凭据。您指定的任何表单字段名称都将作为隐藏输入包含在生成的表单中,并在用户重新提交表单时返回,这次使用 FTP 凭据。

注意:请勿将hostnameusernamepasswordpublic_key或 的保留名称private_key用于您自己的输入。这些由凭据表单本身使用。或者,如果您确实使用它们,该request_filesystem_credentials函数将假定它们是传入的 FTP 凭据。

提交凭据表单时,它将在传入的 POST 数据中查找这些字段,如果找到,它将在适合传递给 WP_Filesystem 的数组中返回它们,这是下一步。

初始化WP_Filesystem_Base

在可以使用 WP_Filesystem 之前,必须使用正确的凭据对其进行初始化。这可以这样做:

if ( ! WP_Filesystem( $creds ) ) {
	request_filesystem_credentials( $url, '', true, false, null );
	return;
}

首先调用该WP_Filesystem函数,将之前的凭据传递给它。然后它将尝试验证凭据。如果它们是好的,那么它将返回 true。如果不是,那么它将返回 false。

在凭据错误的情况下,上面的代码会再次调用request_filesystem_credentials(),但这次错误标志设置为 true。这会强制该函数再次显示表单,这次会向用户显示一条错误消息,说明他们的信息不正确。然后用户可以重新输入他们的信息并重试。

使用WP_Filesystem_Base类

一旦类被初始化,全局$wp_filesystem变量就会被定义并可供您使用。该类WP_Filesystem_Base定义了几种可用于读取和写入本地文件的方法。例如,要写入文件,您可以这样做:

global $wp_filesystem;
$wp_filesystem->put_contents(
  '/tmp/example.txt',
  'Example contents of a file',
  FS_CHMOD_FILE // predefined mode settings for WP files
);

其他可用的方法包括get_contents()读取get_contents_array()文件;wp_content_dir(), wp_plugins_dir(), 并且wp_themes_dir()将返回这些目录的文件系统路径;mkdir()rmdir()创建和删除目录;以及其他几个方便的文件系统相关功能。

技巧和窍门

你什么时候可以调用request_filesystem_credentials()
使用 WP Filesystem API 的开发人员面临的最初挑战之一是您无法在任何地方对其进行初始化。该 request_filesystem_credentials() 功能在操作钩子之后才可用 wp_loaded ,并且仅包含在管理区域中。您可以使用的最早的钩子之一是 admin_init。

WP 文件系统 API 方法论
直接调用的另一个问题 request_filesystem_credentials() 是您无法确定您是否可以直接访问文件系统,或者是否会提示用户输入凭据。从 UX 的角度来看,如果您想在激活插件时更改文件,这就会成为问题。想象一下,用户通过他们的管理区域安装您的插件,输入他们的 FTP 详细信息,完成安装并激活您的插件。但一旦他们这样做,系统就会提示他们再次输入他们的 FTP 详细信息,并且让他们摸不着头脑不明白为什么。

更好的解决方案是添加一个通知(例如使用 admin_notice),向用户解释您的插件需要写入文件系统才能完成安装。除了该通知,您还可以添加一个按钮或链接来触发您对 request_filesystem_credentials().

但是让我们进一步扩展这个场景,假设这个插件每次更新时都需要访问文件系统。如果您定期发布更新和错误修复,用户每次升级时单击您的可操作按钮很快就会变得脆弱。最好在调用之前确定我们是否具有直接写访问权限 request_filesystem_credentials() 并静默进行安装。这就是 get_filesystem_method() 函数发挥作用的地方。

$access_type = get_filesystem_method();
if ( $access_type === 'direct' )
{
	/* you can safely run request_filesystem_credentials() without any issues and don't need to worry about passing in a URL */
	$creds = request_filesystem_credentials( site_url() . '/wp-admin/', '', false, false, array() );
	/* initialize the API */
	if ( ! WP_Filesystem( $creds ) ) {
		/* any problems and we exit */
		return false;
	}	
	global $wp_filesystem;
	/* do our file manipulations below */
}	
else
{
	/* don't have direct write access. Prompt user with our notice */
	add_action( 'admin_notices', 'you_admin_notice_function' ); 	
}

这种方法适用于所有相关人员。没有直接写入权限的用户会收到更改文件系统的提示,而插件在可以直接写入文件系统的网站上不会被注意到(以一种好的方式)。

使用路径
值得称道的 WordPress 开发人员应该熟悉设置常量或变量来访问他们的插件路径。它通常看起来像这样:

define( 'MY_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); 

使用文件系统 API 时需要考虑的是文件的路径并不总是相同的。使用直接方法时,您可以安全地使用 MY_PLUGIN_DIR 常量,但如果您在使用 FTP 或 SSH 方法时尝试这样做,那么您可能会遇到问题。这是因为 FTP 和 SSH 通常根植于绝对路径中某处的目录。$wp_filesystem->wp_content_dir() 现在,文件系统 API 为我们提供了使用和 之类的方法来克服这个问题的方法 $wp_filesystem->wp_plugins_dir(),但是两次定义插件的路径是不切实际的。

/* replace the 'direct' absolute path with the Filesystem API path */
 $plugin_path = str_replace( ABSPATH, $wp_filesystem->abspath(), MY_PLUGIN_DIR );
/* Now we can use $plugin_path in all our Filesystem API method calls */
if ( ! $wp_filesystem->is_dir( $plugin_path . '/config/' ) ) {
	/* directory didn't exist, so let's create it */
	$wp_filesystem->mkdir( $plugin_path . '/config/' );
}

unzip_file($file, $to);

虽然此函数需要初始化 Filesystem API,但它不是对象的方法 $wp_filesystem ,这可能是其参数彼此不一致的原因。第一个参数 $file需要是文件的绝对“直接”路径,同时 $toparameter需要指向文件系统的绝对路径。

define( 'MY_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); 
global $wp_filesystem; // already initialised the Filesystem API previously
$plugin_path = str_replace( ABSPATH, $wp_filesystem->abspath(), MY_PLUGIN_DIR ); // get remote system absolute path
/* Acceptable way to use the function */
$file = MY_PLUGIN_DIR . '/plugin-file.zip'; 
$to = $plugin_path;
$result = unzip_file( $file, $to ); 
if ( $result !== true ) {
	// unzip failed. Handle Error
}
/* Not acceptable */
$file = MY_PLUGIN_DIR . '/plugin-file.zip';
$to = MY_PLUGIN_DIR; // $to cannot be the 'direct' absolute path to the folder otherwise FTP and SSH methods are left in the cold
unzip_file( $file, $to ); 
$file = $plugin_path . '/plugin-file.zip'; // If $file isn't the 'direct' absolute path then users not using FTP and SSH methods are left in the cold
$to = $plugin_path;
unzip_file( $file, $to );