WordPress FileSystem API:使用本地文件进行操作的正确方法

Anna Ladoshkina. 经过 Anna Ladoshkina.  |  2012年8月21日

WordPress从博客平台进入完全成熟的CMS,同时将其转化为开发人员的坚实框架,以建立未出售的项目和应用程序。

WordPress核心,不仅为用户提供权力’出版引擎还为开发人员提供具有强大的类,API和助手,旨在满足各种需求。

WordPress的隐藏宝石之一,允许开发人员以安全且强大的方式使用本地文件系统执行操作是WordPress文件系统API。它将文件操作功能摘要进入一组常见的请求的方法,以便它们可以安全地使用在不同的托管环境中。

 

问题的范围

想要在代码中编写本地文件可能有几个原因:

  • 执行事件或操作的日志记录
  • 具有非WordPress供电系统的数据交换
  • 备份

无论动机如何,从PHP代码编写本地文件可能是一个危险的操作。在为WordPress主题,插件或自定义安装中实施此项时,应至少考虑两个非常重要的缺陷:

  1. 安全。 使用代码(通过WebServer)编写本地文件时,文件所有权存在不正确的风险。在配置不良的共享托管环境中出现此问题,可能导致对文件的控制丢失。
  2. 兼容性。 由于托管公司的各种公司,特定用户 ’S服务器配置通常是开发人员未知的。因此,开发人员不能确保插件或主题的用户可以实现写入操作所需的权限。

如果需要为公共发布需要写入本地文件的WordPress插件或主题,则开发人员应不断考虑这些问题。好消息是WordPress本身已经有一个解决这些问题的工具:文件系统API。

 

WordPress文件系统API介绍

文件系统API被添加到版本2.6中的WordPress以启用WordPress’自己的更新功能。它摘要安全地和各种主机类型执行读/写操作所需的功能。它由一组类组成,并允许您自动选择连接到本地文件系统的正确方式,具体取决于各个主机设置。

API后面的逻辑非常简单;它试图直接编写本地文件,并在错误的文件所有权的情况下切换到另一种基于FTP的方法。根据可用的PHP库,它找到了设置FTP连接的合适方法(通过扩展套接字或过度SSH)。通常,使用以下步骤使用本地文件:

步骤1.检测可用的连接方法

WordPress使用get_filesystem_method来检测以下方法的可用性(从最高优先级到最低点)直接,SSH2,FTP PHP扩展,FTP套接字。

 

步骤2.获取检测到的方法所需的凭据

如果检测到的传输需要来自用户的凭据,则WordPress使用Request_filesystem_credentials函数来显示请求表单。该函数具有许多参数,允许它在表单提交之间保留数据,如果连接失败,并且目标在WordPress安装中的特定目录中询问凭据:

request_filesystem_credentials($form_post, $type, $error, $context, $extra_fields);

通过向功能提供空的$型参数,我们可以强制它来执行可用连接方法的检测,因此它会调用get_filesystem_method for我们。同时,我们可以通过使用$ type参数指定它来强制使用任何特定的连接类型。

当所选方法所需的连接数据ISN时’t提供,该函数打印表单以请求它:

Conneciton信息

在第一个请求WordPress之后存储数据库中的FTP主机名和用户名以供将来使用,但它不会存储密码。或者,可以使用以下常量在WP-Config.php文件中指定FTP凭据:

  • FTP_HOST –要连接到的服务器的主机名
  • FTP_USER –连接的用户名
  • FTP_PASS –连接密码
  • FTP_PUBKEY –用于SSH2连接的公钥的路径
  • FTP_PRIKEY –用于SSH2连接的私钥的路径

当此数据存储在WP-Config.php文件中时,凭据凭据请求表单不会出现,但安全缺点是显着的,安全程序应具有三重检查,可能应对此文件的安全性最高。

 

步骤3.初始化WordPress文件系统类并连接到文件系统

WordPress文件系统API的核心是WP_FileSystem函数。它加载并初始化相应的传输类,将获得的实例存储在全局$ WP_FileSystem对象中以进行进一步使用,并尝试使用提供的凭据连接到文件系统:

WP_Filesystem($args, $context);

 

步骤4.使用WordPress文件系统方法执行读/写操作

正确初始化$ WP_FileSystem对象具有一组方法来与本地文件系统通信,这些文件系统可以使用,而无需对连接类型的进一步焦虑。特别是,有以下常用方法:

  • get_contents.–将文件读入字符串
  • put_contents.–将字符串写入文件
  • MKDIR.– creates a directory
  • MDIR.– removes a directory
  • wp_content_dir.–将本地文件系统上的路径返回给WP-Content文件夹
  • wp_plugins_dir.–将本地文件系统上的路径返回到插件文件夹
  • wp_themes_dir.–将本地文件系统上的路径返回给主题文件夹

把它整合在一起,让’s提出了一个在简单情况下执行上述步骤的示例 - 我们将在Textarea中提交的一些文本写入普通的.txt文件。

请注意,此示例是用于演示目的,在真实的情况下您’t在.txt文件中存储简单的文本数据,将其存储在数据库中是一个更强大的解决方案。

 

WordPress文件系统API在操作中

让’s将我们的代码包装在一个单独的插件中,将分配自己的文件系统-demo文件夹。为我们提供目标文件夹来存储.txt文件并检查写入权限。

首先,让’s创建演示页面以在“工具”菜单下显示我们的表单:

/**
 * Create Demo page (under Tools menu)
 *
 **/
add_action('admin_menu', 'filesystem_demo_page');

function filesystem_demo_page() {

  add_submenu_page( 'tools.php', 'Filesystem API Demo page', 'Filesystem Demo', 'upload_files', 'filesystem_demo', 'filesystem_demo_screen' );
}

function filesystem_demo_screen() {

$form_url = "tools.php?page=filesystem_demo";
$output = $error = '';

/**
 * write submitted text into file (if any)
 * or read the text from file - if there is no submission
 **/
if(isset($_POST['demotext'])){//new submission

  if(false === ($output = filesystem_demo_text_write($form_url))){
    return; //we are displaying credentials form - no need for further processing

  } elseif(is_wp_error($output)){
    $error = $output->get_error_message();
    $output = '';
  }

} else {//read from file

  if(false === ($output = filesystem_demo_text_read($form_url))){
    return; //we are displaying credentials form no need for further processing

  } elseif(is_wp_error($output)) {
    $error = $output->get_error_message();
    $output = '';
  }
}

$output = esc_textarea($output); //escaping for printing

?>
<div class="wrap">
<div id="icon-tools" class="icon32"></div>
<h2>Filesystem API Demo page</h2>
</div>

<!--?php if(!empty($error)): ?-->
<div class="error below-h2"><!--?php echo $error;?--></div>

<!--?php endif; ?-->
<form method="post">
<!--?php wp_nonce_field('filesystem_demo_screen'); ?-->
<fieldset class="form-table">
  <label for="demotext">
  <textarea id="demotext" class="large-text" rows="8" name="demotext"><?php echo $output;?></textarea>
</label></fieldset>

<!--?php submit_button('Submit', 'primary', 'demotext_submit', true);?-->
</form>

显示我们的页面(FileSystem_Demo_Screen),我们检查文本提交的可用性。如果存在我们尝试在Test.txt文件中将其写入其中,否则,我们尝试在插件文件夹中找到这样的文件,并读取其内容以包含在TextAre中。最后,我们将基本表单打印到输入文本。为了可读性,这些写作和阅读操作分为自己的职能。

文件系统API演示

为避免重复相同的初始化步骤,已创建共享帮助程序。它首先调用Request_filesystem_credential,以检测可用的连接方法并获取凭据。如果这是成功的,那么调用WP_FileSystem以发起$给定数据的$ wp_filesystem。

/**
 * Initialize Filesystem object
 *
 * @param str $form_url - URL of the page to display request form
 * @param str $method - connection method
 * @param str $context - destination folder
 * @param array $fields - fileds of $_POST array that should be preserved between screens
 * @return bool/str - false on failure, stored text on success
 **/
function filesystem_init($form_url, $method, $context, $fields = null) {
  global $wp_filesystem;

  /* first attempt to get credentials */
  if (false === ($creds = request_filesystem_credentials($form_url, $method, false, $context, $fields))) {

    /**
     * if we comes here - we don't have credentials
     * so the request for them is displaying
     * no need for further processing
     **/
    return false;
  }

  /* now we got some credentials - try to use them*/
  if (!WP_Filesystem($creds)) {

    /* incorrect connection data - ask for credentials again, now with error message */
    request_filesystem_credentials($form_url, $method, true, $context);
    return false;
  }

  return true; //filesystem object successfully initiated
}

写入文件代码如下所示:

/**
 * Perform writing into file
 *
 * @param str $form_url - URL of the page to display request form
 * @return bool/str - false on failure, stored text on success
 **/
function filesystem_demo_text_write($form_url){
  global $wp_filesystem;

  check_admin_referer('filesystem_demo_screen');

  $demotext = sanitize_text_field($_POST['demotext']); //sanitize the input
  $form_fields = array('demotext'); //fields that should be preserved across screens
  $method = ''; //leave this empty to perform test for 'direct' writing
  $context = WP_PLUGIN_DIR . '/filesystem-demo'; //target folder

  $form_url = wp_nonce_url($form_url, 'filesystem_demo_screen'); //page url with nonce value

  if(!filesystem_init($form_url, $method, $context, $form_fields))
    return false; //stop further processign when request form is displaying

  /*
   * now $wp_filesystem could be used
   * get correct target file first
   **/
  $target_dir = $wp_filesystem->find_folder($context);
  $target_file = trailingslashit($target_dir).'test.txt';

  /* write into file */
  if(!$wp_filesystem->put_contents($target_file, $demotext, FS_CHMOD_FILE))
    return new WP_Error('writing_error', 'Error when writing file'); //return error object

  return $demotext;
}

在这部分中,我们定义了一些必要的参数:

  • $ demotext - 提交文本写入
  • $ form_fields - $ _post数组中存储我们的文本的项目,并应保留
  • $方法 - 运输方法,我们将其留空以自动检测
  • $ context - target文件夹(插件’s one)

之后,我们使用前面描述的辅助功能启动了全局$ wp_filesystem对象。在成功的情况下,我们会检测目标文件夹的正确路径,并使用$ wp_filesystem对象的put_contents方法将提交的文本写入它。

从文件中读取的代码如下所示:

/**
 * Read text from file
 *
 * @param str $form_url - URL of the page where request form will be displayed
 * @return bool/str - false on failure, stored text on success
 **/
function filesystem_demo_text_read($form_url){
  global $wp_filesystem;

  $demotext = '';

  $form_url = wp_nonce_url($form_url, 'filesystem_demo_screen');
  $method = ''; //leave this empty to perform test for 'direct' writing
  $context = WP_PLUGIN_DIR . '/filesystem-demo'; //target folder  

  if(!filesystem_init($form_url, $method, $context))
    return false; //stop further processing when request forms displaying

  /*
   * now $wp_filesystem could be used
   * get correct target file first
   **/
  $target_dir = $wp_filesystem->find_folder($context);
  $target_file = trailingslashit($target_dir).'test.txt';

  /* read the file */
  if($wp_filesystem->exists($target_file)){ //check for existence

    $demotext = $wp_filesystem->get_contents($target_file);
    if(!$demotext)
      return new WP_Error('reading_error', 'Error when reading file'); //return error object      

  }  

  return $demotext;
}

此函数以与先前描述的方式相同的方式工作,但它使用get_contents从目标文件中读取。

 

结论

使用本地文件时,WordPress主题或插件开发人员将与安全性和兼容性问题接触,对团队的压力施加巨大的压力,并将长时间添加到项目生命周期。通过依赖文件系统API,这些问题可以以有效的方式侧阶段。所以下次你发现自己写入FWRITE进入你的插件’S代码,考虑此替代方案更健康的选项。

你可以 在这里下载此代码的演示,并适应您的需求。