Note: An actual working implementation of the WP_Filesystem can be found in my Pluginception plugin.

Lot of talk amongst theme authors recently about writing local files. Writing files from code, whether it be from a theme or from a plugin, is generally bad mojo. However understanding why you shouldn’t is confusing to many, and then understanding why you shouldn’t do-it-yourself and should use the WP_Filesystem is even more confusing. To further muddy up the waters, many theme authors have expressed confusion at how to use WP_Filesystem in the first place.

So, let’s run the gamut in this tutorial. Note that I wrote it quickly, so it may be uneven in parts. 😉

Why Not To Do It

The most common reasons I see theme authors wanting to write local files falls into three categories:

  1. Dynamic CSS
  2. Configuration Saving
  3. Export/Import

Take the Dynamic CSS case to start with, since it’s the most common one by far. Some theme authors say “I’m making a framework/system/construction-kit where the user can define their own theme/layout/color-schemes and thus produce a bunch of CSS. I want to save this CSS in the theme/uploads directory, and then put a link to it from their page, so that the browser downloads it from there.

Why they shouldn’t do that: There’s half a dozen reasons here, but the big two are a) put just the dynamic parts of CSS inline into the page instead, because it’s faster, simpler, and avoids an extra HTTP roundtrip for the user’s browser to get this CSS file and b) writing a CSS file locally is insecure as hell if you do it in the “natural” way.

Security is going to be a big issue for the next two items, so I’ll cover it right now.

Modern UNIX-like systems have the concept of “users”. When I SSH or FTP into my hosting account, then I’m logged in as me and my user account. However, my webserver doesn’t run as my user account. It runs under some different user account, usually “nobody” or “apache” or “web” or something similar to that.

So when I create files, they’re owned by me. When my webserver creates files, then they’re owned by the webserver’s user, whatever it is. Normally, this isn’t a big deal. The webserver can read and serve files either way, so who cares, right? Well, when you’re creating files that are owned by the webserver, then the webserver has permission to write to those files. It’s the owner of them, after all. What’s important here is that I’m not the only person on this webserver.

Shared hosting systems have many users files all served by the same webserver. So if I allow that “nobody” user to own files that are part of my webpage, then anybody else can use their account on the same webserver to modify those files, and thus modify my webpage. For the case of CSS files, this poses a cross-site-scripting risk. Somebody else on my shared webserver could insert code into my CSS files and change them so as to steal my account information. Bad mojo.

The other two, where theme authors are saving configuration or exporting and importing things have the same basic problems, although the risks might be even higher. In one case, I found a theme saving its configuration settings by creating a PHP file in the uploads directory and then using var_export to export the configuration variable to it. Then it proceeded to include this file when the theme loaded, to load its configuration back in. Talk about insecure, anybody running this theme is basically handing over the entire control of their website to anybody on the same web server.

How To Do It Anyway

“But Otto”, I hear you shouting from halfway around the world, “WordPress itself writes files all the time! It can even upgrade itself. How is this safe?” Well, Mr. Incredbly Loud Person, WordPress uses a system called WP_Filesystem to make this safe.

The WP_Filesystem basically support five different ways of writing files to the system and they all ensure that ownership of those files remains firmly in the hands of the same person that owns the WordPress files. In other words, it writes files using your user account and not as the webserver user.

I’m sure many people have seen this before:

This is the basic connection information screen that appears when you try to upgrade a plugin or themes or even the core code, on certain hosts. Some people may be running on hosts that don’t pop up this screen, but the basic process behind it still exists.

Essentially, what is going on here is that WordPress does some tests to detect how it can write files to the server and keep the same owner for those files. If it can’t do it directly, then it does it indirectly via FTP. It needs your login credentials at this point in order to log in as you and thus write files as you instead of as the webserver user that it’s running under.

How to Implement the WP_Filesystem code

Implementing the WP_Filesystem is easy, really. But to do it from a form or options type of system, then you need to have a form or options type of system already. So let’s start out with an extremely basic form, such as one a theme might implement.

// add the admin options page
add_action('admin_menu', 'otto_admin_add_page');

function otto_admin_add_page() {
	add_theme_page('Otto Test Page', 'Otto Test Options', 'edit_theme_options', 'otto', 'otto_options_page');
}

Okay, now we have a new options menu. Let’s make a form to put on that menu:

function otto_options_page() {
	if ( otto_check_buttons() ) return;
?>
<div>
<h2>My custom options page</h2>
<form action="" method="post">
<?php wp_nonce_field('otto-theme-options'); ?>
<input name="save" type="submit" value="Save a file" />
</form></div>
<?php
}

Simple, no-nonsense form. It calls a function to check the incoming button press first (see more below), then it displays a form. It outputs a nonce, makes a button called “Save a file” since that’s what we’re going to do, then stops. Easy.

Note that the otto_check_buttons() function returns true or false, and this then returns when it is true, thus not displaying the form. This is simply to allow us to display the normal form or not, depending on what we need to do later.

So, when you submit the form, then that otto_check_buttons() function is going to come into effect. Let’s look at the beginning of that function:

function otto_check_buttons() {
	if (empty($_POST)) return false;

	check_admin_referer('otto-theme-options');

The beginning of it is simple. If the user hasn’t submitted the form yet, then it just returns false to make the previous function display the form. Next it checks the referer nonce, to make sure the nonce is correct for the page we’re on. Let’s continue:

$form_fields = array ('save'); // this is a list of the form field contents I want passed along between page views
$method = 'ftp'; // Normally you leave this an empty string and it figures it out by itself, but you can override the filesystem method here

// check to see if we are trying to save a file
if (isset($_POST['save'])) {

Here, I’m defining two variables that I’m going to use later. The form_fields is an array of the names of the fields in the form that I want to pass along through that connection information screen. In this case, I defined my “save” input field before, and it’s something I’ll be checking later, so it has to pass through that form. What happens is that the connection information form generates an extra hidden input with the contents from my first form.

The method variable is not strictly necessary. I’m forcing it to use the “ftp” method for this demonstration code. Normally, you leave this blank and the system figures it out for itself. Changing ‘ftp’ to just ” here works fine.

Finally, you can see where I’m checking to make sure the save button was clicked. Moving on:

// okay, let's see about getting credentials
$url = wp_nonce_url('themes.php?page=otto','otto-theme-options');
if (false === ($creds = request_filesystem_credentials($url, $method, false, false, $form_fields) ) ) {
	// if we get here, then we don't have credentials yet,
	// but have just produced a form for the user to fill in,
	// so stop processing for now
	return true; // stop the normal page form from displaying
}

Now is the part where I get the credentials from the user. The request_filesystem_credentials() does several things.

  • It checks to see what kind of credentials it needs (we’re forcing it to FTP in this case)
  • It checks to see if they’re pre-DEFINEd in the wp-config file
  • It checks the POST input to see if it has just received them and returns them if it has
  • It checks the database to see if it remembers the hostname/username from before
  • It produces the necessary form to display if it doesn’t have them

The bottom line being that if it has the necessary credentials, it will return them. Else, it will output the form to be displayed to get the credentials, and then return false. So when it returns false, we know the form has been displayed, so we just exit and wait for the user to give us the necessary credentials.

When we get those credentials, we have to check to make sure they work. Here’s how we do that:

// now we have some credentials, try to get the wp_filesystem running
if ( ! WP_Filesystem($creds) ) {
	// our credentials were no good, ask the user for them again
	request_filesystem_credentials($url, $method, true, false, $form_fields);
	return true;
}

Simple, really. We call the WP_Filesystem and see if the credentials work. If not, we call request_filesystem_credentials a second time, but this time with the $error flag set. This produces the error message and form to the user so he can correct the information.

Finally, we should have working credentials from the user and all the information we need, so we use the $wp_filesystem global to write out a file.

// get the upload directory and make a test.txt file
$upload_dir = wp_upload_dir();
$filename = trailingslashit($upload_dir['path']).'test.txt';

// by this point, the $wp_filesystem global should be working, so let's use it to create a file
global $wp_filesystem;
if ( ! $wp_filesystem->put_contents( $filename, 'Test file contents', FS_CHMOD_FILE) ) {
	echo 'error saving file!';
}

That’s really all there is to it. I made a file in the upload directory called “test.txt” and wrote “Test file contents” to it. The global $wp_filesystem variable is what our call to WP_Filesystem($creds) created. This $wp_filesystem supports various different file functions, but the put_contents() function is the easiest one to understand. I gave it the filename, some contents, and the correct permissions for a file on the system. Simple and easy.

Reasons to use WP_Filesystem

Two big ones:

  • Compatibility
  • Security

Because of the various permissions systems in use, the WP_Filesystem supports many different ways of writing files to the system, but all abstracted out into simpler functions like put_contents() and get_contents() and such.

The only downside to it is that sometimes you need to ask the user for their account information in order to be able to get the access you need. But this is necessary in order to be secure, since you don’t want everybody else on the same server to be able to write to your files. And while you may have a server all to yourself, most of your users won’t. The most common setup for WP is on shared hosting, and if you’re creating a theme or a plugin, you should design for those users as well.

A copy of all the code above made into a demo plugin can be found here: wpfilesystem-demo.phps.

Shortlink:

81 Comments

  1. Thank you for writing this. Now, I finally have a place to point people to when this crops up, which is just about once a week.

    • Agreed, great article.

      To touch on the shared environment security issues described in the post – these only exist on hosts that have no idea how to handle security. A properly secured shared hosting environment will:

      – Execute PHP as the user so any files written by it are automatically written as the user, not as nobody or apache or web or whatever
      – Not allow 777 permissions
      – Not allow a user to go outside of his home folder (meaning that even if 777 permissions are allowed they mean nothing since you can’t go outside of your environment)
      – Run modsec which if properly configured will stop most things on its own
      – Run suhosin which like modsec stops a whole bunch of stuff on its own

      If your host is not doing the above you should seriously consider switching hosts because sooner or later you’re going to run into a badly coded plugin and / or theme that will ruin your day (not aimed at anyone, just a general statement).

      • FWIW, if you have a host who has set up their webserver using a setuid method (where the web process runs as the user who owns the PHP file), then the $wp_filesystem will be able to detect this and it will use the “direct” method. The direct method will have the request_filesystem_credentials() return true, and WP_Filesystem won’t need any form of FTP credentials. So this code will work and it won’t ask the user for connection information.

        Many shared hosts are using setuid methods because it’s more secure in such a situation. However, on a non-shared hosting setup, a setuid configuration is less secure, and so should not be used.

  2. I’ve thought about “dynamic” styles in themes before and concluded that it doesn’t need to be dynamic per se, only updated when the user changes settings in the admin (no extra requests). The WP_Filesystem used on a settings update doesn’t seem terrible in that case.

  3. really well done, thank you.

    would you recommend securing a WP installation in order to prevent *other* plugins/themes from writing to the filesystem, especially outside of the uploads dir? asked another way, if writing any files at all could be a security risk, should we effectively restrict that by denying the shared account user (that WP is using) write access to everything outside of the uploads dir?

    i already ensure that the shared apache user always has read-only access to everything.

  4. Useful article, thanks Otto.

    Does the theme / plugin editor use WP_Filesystem?

    • No, the theme and plugin editors write the files directly. However, editing a file doesn’t change its ownership, it’s creating a new file that does that. Since the theme/plugin editors can’t create new files, they don’t need to use the WP_Filesystem.

      • So if a theme is creating a file ( a .css, say), then it should use WP_Filesystem when it first creates the file, but can then write the file directly every time the settings are re-saved / the file edited? Is that correct?

        • You could do it that way, but doing so will cause you more headache than it’s worth, and it still won’t work on some server configurations. Best to just always use the WP_Filesystem to write files of any sort.

          Better yet, revamp your code to not have to write files at all. Configuration should be stored in the database, not in files. set_theme_mod() and get_theme_mod() are extremely useful for theme authors.

          • set_theme_mod() and get_theme_mod() aren’t well documented. Reading the source code for examples on how to use these functions puts off theme developers. Now that you opened the pandora’s box of WP_Filesystem, a tutorial on Theme Mods would help stem the flow of new Dynamic CSS themes 😉

            P.S. Theme Check returns warnings all over the place when using the filesystem method, which perhaps will discourage the practice.

          • And would the user need to provide their credentials every time the settings were saved and the .css file was updated (assuming they hadn’t saved their credentials as constants in wp-config.php)?

            For someone who is making small design changes via UI settings, saving, checking, going back and tweaking, it’s going to get very annoying very quickly to potentially have to keep entering their FTP details.

            Are / could the credentials be saved between file-writing requests – perhaps as session variables?

            (For reference, the configuration IS being stored in the database, but also immediately written to style sheets – generating them or creating inline styles on every single request would be painful at best.)

            • I find it difficult to believe that you could possibly have enough UI settings that would necessitate the creation of a separate CSS file instead of the more obvious, faster, safer approach of simply generating the CSS content and putting it inline into the document.

              For small amounts of CSS, even Google recommends putting it inline into the HTML instead of into a separate file. Their PageSpeed module for Apache will even do this for sites automatically.

              That said, it would be entirely possible to remember their FTP credentials and use them again. I won’t tell you how to do it, because storing credentials like passwords and such is a bad-idea™. However, you could mitigate this by storing them in a session cookie and reading them from there.

              • Thanks Otto.

                I’ve not built all the settings yet, but suffice to say that the dynamik options screen shots on http://catalysttheme.com/admin-screenshots/ are a good approximation for the type of thing I’ll be ending up with.

                This won’t be small amounts of CSS – it’ll be enough to populate a typical theme’s style.css-file’s worth – so inline isn’t a feasible option. There will actually be 2 style sheets – one for the saved settings, and another that is the settings and another custom style sheet, minified.

                I guess the session cookie solution would be best for what I’m after.

                • Yikes. Options overload, brother. I’d toss that theme away immediately. I prefer to strive for simplicity. Why make an options page that is way more complicated than just learning how to write CSS? I mean, why go to all that effort in the first place? Configurability is great and all, but come on. 90% of those options shouldn’t be options to begin with.

                  Now you may disagree with this, and that’s perfectly valid. Not judging. 🙂 I’m just saying that I could never recommend anything like that to a client because of the over-complication. Who exactly are you designing this for? What’s your target audience? It certainly isn’t non-technical people, because those options pages are loaded with technical jargon and confusion.

                  I can’t think of any reason for a theme to have more than, say, 5 options pages. And most of them should be controlling one thing only. Header image. Fonts/colors of text. Background Image. Maybe some minor layout adjustments. But I certainly wouldn’t, for example, have a screen where people could redesign their footer using shortcodes. Or to be able to adjust the padding or spacing of elements on the page. The theme is supposed to do that, it’s not supposed to throw that back on the user. Pick a size that works for 90% of people and stick with it. Most people won’t want to change the default padding anyway. Those that do can learn some minor CSS to do it.

                  Just my opinion.

              • This comment (and your later one about options overload) demonstrates a significant loss of touch with real world WordPress theme users, I believe.

                1) There is an obsessive and large group of WordPress users concerned about SEO. Look at the popularity of the SEO plugins, for example. There is a widespread belief (I won’t judge or comment on reality – the belief is enough to make it an issue for a theme writer) that anything more than 10 or 20 lines of CSS inline causes significant SEO degradation.) So including the kind of CSS required for a customizable theme just doesn’t fly with many users.

                2) For users who use customizable themes (like my Weaver), I don’t believe that it is possible to have enough Options, ever. Weaver has been out for more than a year now, and every single option it has (and there are hundreds), has been at the direct request of at least two users. It is impossible to underestimate what users will want to do – and mostly, the users are right. The options they want make sense.

                Maybe some users just want simplicity, but once they make the step to a customizable theme, they really won’t go back to anything less – at least in my experience.

                I suppose beginning users, or users with fairly simple requirements, can get by with a plain theme – that’s why there are so many of them. But once a user gets past the initial site building stage (which many never need to do, for sure), they keep asking – how do I change this little thing or that little thing.

                Interestingly, from the statistics I gather from my own user community, I suspect that a significant number of my own users are really web site developers (I’m guessing well over 1000 developers) who need a single theme they can tweak for customers. I’ve heard over and over that Weaver is as good as, and usually better, than most commercial WordPress themes.

                So – for a significant number of WordPress users, please realize that external custom CSS files are important, as are a seemingly infinite number of options. They really like this stuff!

                • Your viewpoint is fine, but I don’t share it.

                  Firstly, I tend to ignore people speaking about SEO. Most of them are nuts, frankly. In the grand scheme of things, SEO is simply not that important. If your site is clear, well structured, and you have good content, then that is generally enough. All SEO can do is to boost you an ever-so-slight notch higher in the rankings.

                  Secondly, options overload is a fundamental truth. I’ve seen it all too many times. In WordPress itself, we eschew options, and I fully agree with that philosophy. To quote from the WordPress Philosophy page, Decisions, not options.

                  Every time you give a user an option, you are asking them to make a decision. When a user doesn’t care or understand the option this ultimately leads to frustration. As developers we sometimes feel that providing options for everything is a good thing, you can never have too many choices, right? Ultimately these choices end up being technical ones, choices that the average end user has no interest in. It’s our duty as developers to make smart design decisions and avoid putting the weight of technical choices on our end users.

                  Third, most “customizable themes” are terrible. Just… awful. I have not examined Weaver in particular, but from a quick glance at the options pages, it’s honestly not a theme I’d ever give to a client or end-user. You’ve packaged hundreds of options onto the options page, in 6 tabs. Frankly, it’s ridiculous. IMO of course. Your users may love it, but some people love tons of useless technical jargon. As a quick estimate, I’d say that roughly 90% of the options you give there shouldn’t be options at all. Making a theme that can become any theme is silly. If you want a different theme, use a different theme. Your whole concept of “sub-themes” for example, should be child themes. That’s what they exist for.

                  And if your userbase is developers who are tweaking themes, then they should be perfectly happy tweaking the code of the theme, or creating a child theme and tweaking that. What is on the WordPress screen should be designed for the end-user, since they’re the one that’s going to be using it. Somebody writing a blog should never see all that technical stuff. Developers should strive to not present that sort of thing to the user. Again, decisions, not options.

                  If you want to do external custom CSS files, you can do it using the WP_Filesystem and not compromise the security of the user’s website. But IMO, you’re doing-it-wrong.

  5. Thanks Otto, great stuff!

    Cheers,
    Emil

  6. Thank you very much for pointing this out. I’m having the exactly problem as in your screenshot and I hate entering information every time. I’ll take your code to my theme and hope it will wipe the problem away.

  7. Thanks Otto, great info here.
    Another one of those things I didn’t have a grasp on at all.
    I love when cool articles fall into my RSS reader

  8. Do plugins that minify and concatenate styles and scripts use this method? Is it OK in that case?

    • I haven’t seen any themes which do it this way. The ones I’ve seen have used very crude methods of modifying files or output the CSS dynamically from the database on every page load (which isn’t particular efficient – I used to do it this way myself)

      Slightly off-topic:
      IMHO minifying scripts and CSS is much better suited to a plugin than a theme.

  9. WordPress uploads directory is writable, so wont other users on the shared host be able to do the scary things you talked about ?

    • Which was sort of my whole point in saying don’t do it. But if you do this sort of thing, then using the WP_Filesystem will make the files themselves owned by you and not writable by everybody else.

    • There is less benefit in editing the usual content in the uploads folder since it is typically just images. Attacking someone’s site via HTML, CSS or javascript files is trivial whereas images are much harder to utilize for security attacks in modern browsers (although not unheard of). Even worse would be PHP files, but hopefully loading them into the uploads folder is not a common behaviour!

      This is presumably why WordPress core only allows images to be uploaded via the media uploader by default rather than any old content.

      PDF’s are becoming a particularly common way to attack as there are regular security flaws discovered in Adobe Reader so finding a way to edit PDF’s in the uploads folder would create some serious issues if you were on a poorly setup host.

  10. Hi Otto,
    Thanks for taking the time to write this up.

    I’ve been puzzling over this for the past week. I can see that it’s important to ensure that files aren’t written with certain user permissions to cope with hosting setups which are incorrectly configured, but I always thought I was bypassing that issue by simply CHMOD’ing the files after they were written. I’m now worried I may have been doing something wrong all this time, but I’m having trouble understanding exactly what the security flaw is. Any help pointing out where I’m screwing this up would be much appreciated.

    When I write files to the uploads folder, it normally looks something like this:

    file_put_contents( $filename, $file ); // Save file to directory
    @chmod( $filename, 0644 ); // Set file permissions (for security purposes)

    … in the mean time I’ll start routing a few things back through the database until I’m certain I’ve gotten a better handle on this stuff.

    • Permissions aren’t everything. File ownership matters too. And if you’re writing files as the webserver user, then anybody else who can run code on that webserver can modify your files.

      • Ah. I think I see where I was misunderstanding then. I was assuming file permisions were an adequate way to block others from editing files.

        Would you use the WP file system for uploading images for use in themes? I don’t want to use the regular media upload system if I can avoid it, as it results in the files getting dumped into all sorts of random folders and in the list of media uploads, whereas these are intended to be separate from the regular WordPress media library since they are theme specific.

        I tried to upload some images via the WP File system but it kept asking for FTP details, which isn’t going to work for my particular situation as I need to allow untrusted users to do the uploading. I thought keeping the method blank with ” would work, but it still requested FTP details 🙁

      • then anybody else who can run code on that webserver can modify your files.

        Assuming they are somehow able to get into your hosting account? And if they were able to do that, wouldn’t file ownership of a random php file not be the big issue, if they got that far, couldn’t they just put their own malicious code somewhere else?

        • No, the threat is not from them getting into your account, but merely being on any account on the same server. Many hosts do “shared hosting”, where more than one account is running on the same web server.

          Now, if a server is properly locked down, then users cannot access other users files directly or create files in their accounts or names. This is pretty standard.

          However, the web server process is usually running as a different user (say, “www”). It can access your files, and through that, users on your host can potentially change existing files *if* the files have permissions that allow the webserver to do that.

          Say I have a file “test.php” which is owned by the “www” user. Now, anybody else on that shared server can write code, then run it from the webserver. The webserver runs that code under the “www” process, and can write to your test.php file, because it is the owner of it.

          In short, on normally configured shared hosting, your web process runs PHP, and that PHP process should not be able to write to your files.

          This is where the “setuid” method came from, where the PHP process actually changes its user credentials to run as the user who owns the original file that is executed. This is safer on shared hosting, because now some other user running things through the web server process from their own account will have that process run as them, and thus still be denied from writing to another user’s files. But not all shared hosting systems use setuid methods. If you are on shared hosting, and see WordPress ask for FTP credentials, then it’s not running setuid methods, and it’s asking for FTP credentials as a form of protection, because it does not want to write files and have them owned by the webserver user.

          • So you’re saying another user on the same shared host to create a program that would run as www-data (or whatever) and be able to transverse directories into your host directory and manipulate that file? (If your host’s system isn’t locked down, or always?)

            • It depends on the configuration and security settings of the hosting system. But yes, basically that can (and has) been done.

              • I was thinking, this sort of problem seems very unlikely these days from a well known web host, and maybe I could ignore your advice. Then I read Decoding a Russian Hacker’s Code, remembering the time my sites on a shared host got hacked, and shut down because I was blowing out my bandwidth. I repeatedly ending up flagged by Google. Tech support at the host kept trying to congratulate me on suddenly having a avery popular web site, and i needed to upgrade my services… That was a month of headaches I don’t want to live through again. In the end I was only able to permanently fix the problem by starting with a new host and a a clean install. My non-WP static directories had thousands of files in them, so there was no easy or quick way to scan all directories for suspicious files.

                WP_Filesystem it is then. If you only prevented one person from making this mistake, then you’ve done your job!

  11. […] של WP_QUERY  עם האפשרות לייצר שאילות מורכבות לטקסונומיות, שימוש ב-WP_Filesystem, שימוש בפורמטים לפוסטים […]

  12. @otto, I’m planning to make a custom poste type (or use the attachment type) to let users create multi mdia projects. Users will be creating, editing and publishing their files, they can also upload a full package of files. Would you recommend I use the wp_filesystem, or the wp media upload handler?

  13. Thanks for the tutorial. If you are only getting contents of a file, you do not need to worry about getting credentials, correct? You are never writing a file to the server, so there is no issue with file ownership. Similarly, if you are updating the contents of a file that already exists, you are not creating the file, so again, no issues with file ownership, correct?

    • If you’re reading a file, there’s no issues. If you’re updating a file, while there are no ownership issues, you shouldn’t be updating a file in a plugin or a theme because the updates will be lost when the plugin or theme is upgraded. Which means that to maintain across upgrades, you need to create a new file outside the plugin/theme directory, which means you should still use the WP_Filesystem.

  14. Hi Otto, I tried to run this code but always get error(error saving file!)

    that could be wrong

    Pls help me.

  15. Do you have any examples of how to write a file using the WP_Filesystem API that does not require a form submission?

    • Look at where I call request_filesystem_credentials. If the result of that call is anything other than false, then you either don’t need credentials, or already have the necessary credentials. In which case, you continue on as per usual.

      However, if that does return false, or if the WP_Filesystem($creds) call fails, then you don’t have the proper credentials yet, and you must therefore get them from the user somehow, in which case you have to show them some kind of form.

  16. Is it possible to upload files outside the WordPress root directory by using the WP_Filesystem?

  17. Hi Otto, what would be the most secure way to deal with a caching solution? I noticed Total Cache for example writes files directly (no wp file api), I think another caching plugin stores the user credentials (if required) in the database and another article mentions storing credentials in wp-config.php as constants. I’m not sure the latter are that much more secure because if a the credentials are compromised a hacker would have free range.

    • Cacheing, yes! Here’s a mother argument for wanting to write to a static file.

      I’m running a network site, on the main site (as a large aside) I want to pull in the last posts from certain categories across different network sites, these posts each have their own Featured Images, so the amount of queries is going to get out of control quickly, plus I’m on a shared host. It would be quicker for the server to be able to include one static file with all that info instead of making a ton of separate queries. If you could get a post type or post category to write to that file when you publish it, then the whole thing would be automated.

      The alternative could be storing it instead as large array of info in a plugin setting? Reducing it to one added query.

      • What you want is probably site transients. They’re like normal transients, but they apply across the whole network of sites. So you have to use unique keys for each site, which you can do with the site ID.

        So, on each site, you have a plugin (or in the theme) that builds a query to get the list of posts that should be displayed on the main site. Each site can do this differently, or you can use one plugin across all the sites to do it. Doesn’t matter.

        In this code, you do something like this:

        1. Call get_current_site() to get the current site object, including the ID of that site.

        2. Attempt to get a site transient for that site, like so:

        $site_posts = get_site_transient(‘site-posts-‘.$current_site->ID);

        If this returns false, then you execute your query and store the resulting posts with the set_site_transient function, sorta like this:

        set_site_transient( ‘site-posts-‘.$current_site->ID, $site_posts, 12 * HOUR_IN_SECONDS );

        That sets the posts in a site transient for 12 hours. Adjust timing as needed for each site and depending on how often each site is accessed.

        3. Then, on the main site, you have it simply get all those transients in a loop, and display the resulting array of posts from each one. No queries other than to get the transients. Certainly no long queries against the various post tables.

        This lets each site set its posts to be displayed on the main site, and by distributing the load across the various sites like that, you don’t have any big-queries happening only on the main site. The idea is to have each site set the relevant information itself instead of having one site go to all the others to get that same information all at once.

        Advantage of transients happens especially when you use a persistent memory caching solution for object caching, like memcache or APC or XCache or what have you. With these in place, transients are stored in fast memory instead of in the database, so no extra queries are needed.

        Downsite is that your transients have a time on them, so new posts won’t trigger the main site to update (for a while).. unless you also make new-posts on each site call delete_site_transient for their info and then re-run the query to re-set their site transient.

        • That sounds very interesting. *Click* (Sound of me googling “WordPress transients”) Thanks for your thoughtful response.

        • From what I gather, without caching, transients are still stored in a table. so the main advantage is that it stores the results of a complex query that can reached with a simple query. This approach could work well for me, except that certain site transients would likely need to remain available for a long time, (months, even) I haven’t seen a maximum value for retaining it. But even then, I suppose you could code it to replace it if it didn’t exist.

          I don’t want to hijack the WP_Filesystem discussion and turn it into transients… Thanks again for the extra information.

  18. Otto,
    in your example you’re forcing an ftp method. I couldn’t find that one in the code.
    It seems the valid methods are ‘ftpext’, ‘ftpsockets’ and ‘ssh2’

    • The $type value, in this case, can be anything and you’d get much the same result. The only values actually being checked for at this level are ‘direct’ or ‘ftpext’ (for ssl FTP connections, the ftpext method is required). All the other methods require special setup in the wp-config.php file. So specifying ‘ftp’ here is just for convenience, so it won’t use ‘direct’.

  19. […] Editing wp-config.php: WordPress Upgrade Constants Tutorial: Using the WP_Filesystem […]

  20. Hi Otto,

    I’m writing here now, cause you put me in a lot of mess, according to the Theme Check plugin. I’m just kidding.
    I’ve made an WP theme and based on some of your advices and others advices i choose to create an dynamic CSS file for the theme customization. Everything is working well, except the readfile, fopen, fwrite, file_get_contents functions that trigger Theme Check Warnings.

    After a little research,i’ve ended up to your page here hoping to find a solution for those Warnings.

    So in several words … created an style.php file which loads an CSS file … pretty much after your advices. If I remember well … you’ve even posted the content of style.php and the other file.

    I don’t ask from you to give me the exact code for me, that would be totally unprofessionally from my part, but I would kindly ask you if you can give me some guidance for solving those problems.

    Thank you

  21. Hi, thank you for the tutorial.

    I’m trying to use get_contents with $_POST data… does it accept the $contect param as for file_get_contents()? With the regular PHP function I’m able to pass some $_POST data, but, in other words, I can’t find a way to achieve this with $wp_filesystem->get_contents: http://stackoverflow.com/a/2445332/912677

    Thank you.

  22. Thanks for this one mister. I just used this in a plugin to delete files from the back-end. Thanks again 🙂

  23. Really could use some help on this one, the WP_Filesystem doesn’t seem to be working on the customize.php page?

    http://wordpress.stackexchange.com/questions/155480/wp-filesystem-in-custom-customize-control

  24. Thanks for this article (still relevant and going strong 4 years on!)

    I’m developing a plugin that generates a lot of dynamic CSS.

    The user can choose how the CSS is added to the page:
    – Enqueue through admin-ajax (slow, but tidy)
    – Inline style tag in head (fast, but messy)
    – Save the CSS to uploads directory and enqueue from there (using wp_filesystem) (new option)

    To mitigate the security issues mentioned in this post, when the CSS is saved to the uploads directory I’m also caching a copy of the CSS as transient.

    Just before the static file is enqueued, the contents of the file on the filesystem are verified against the content of transient. If they don’t match then we know the file has been meddled with on the filesystem, and fall back to enqueuing the cached version through admin-ajax.

    I don’t foresee any issues with this method (apart from a small performance hit with the extra checks) but would be interested to hear what others think about it 🙂

  25. […] something a little longer (see Otto on WordPress Filesystem API tutorial). Now add TDD to it and have […]

  26. For anyone who’s building a plugin or theme that uses the wp filesystem api, but don’t necessarily want to setup an FTP or SSH user, we’ve built a plugin that can help: http://codex.wordpress.org/Filesystem_API.
    Activate the plugin, and it will simulate requiring a username and password to write to the filesystem (which you can either set in wp-config.php, or enter into the form that appears when you call request_filesystem_credentials) in just the same way as using FTP or SSH would.

  27. Hi Otto, can you help me answer this one Why WordPress not Using WP_Filesystem, WP_Filesystem been in WordPress library for so long, but i’m still confused with when to use WP_Filesystem vs PHP file library.

  28. There’s an interesting class that I’ve written (https://github.com/marco-c/WP_Serve_File) to serve files.
    It uses the filesystem if possible (checking if it can access it safely and without asking for a password), otherwise falls backs to the transient API and WordPress AJAX methods.

  29. Otto,
    Great article. Thank you!
    I could use a bit of advice on something. My objective is to use composer and git to make WP sites easily reproducible so that I can quickly recover or move a site. I have put my uploads directory into an S3 bucket and I am learning how to use s3fs to have the local file system (like ubuntu 14 on DO) point to the s3 bucket and “see” the bucket as though it is the local wp-content/uploads directory. So far so good! : )

    However, is there a way, without using the “Add From Server” plugin, to get the WP File System, namely the media library (uploader) to “see” the fresh wp-content/uploads folder on the new server install? The “Add From Server” plugin requires going through and checkboxing each file. Is there not a faster programmatic way to use the Wp FileSystem to do this?

    Thanks in advance! : )

  30. Man! I thought I went crazy because of the snowflakes! 😛

Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Need to post PHP code? Wrap it in [php] and [/php] tags.