WordPress.org

Make WordPress Core

Opened 3 years ago

Closed 3 years ago

#39961 closed enhancement (wontfix)

Make SHORTINIT accessible to plugins and themes.

Reported by: majick Owned by:
Milestone: Priority: normal
Severity: normal Version: 4.8
Component: Bootstrap/Load Keywords:
Focuses: performance Cc:
PR Number:

Description

As it stands there's no safe way for a WordPress plugin or theme (to be clear, specifically one permitted in the repository) to make use of the SHORTINIT load constant for a performance increase. This performance increase can be significant in terms of speed and memory for loading of dynamic content delivery (eg. of scripts or styles etc.) as well as other use cases (due to not having to load some core, but mostly not loading all the plugins and the theme too.)

(Discovered in discourse with plugin review team for a plugin submission, with one known exception here: backup plugins may be permitted in the plugin repository where they can find wp-load.php (or wp-settings.php or wp-config.php) so as to be able to access database credentials for backup purposes.)

This is of course because SHORTINIT must be defined before requiring wp-load.php, and so a plugin/theme needs to know where wp-load.php is to be able to load it after defining SHORTINIT to make use of it - BUT there is no current way for a standalone plugin/theme file to safely determining that file path when it is loading first - meaning directly and externally. (ie. ../../wp-load.php is definitely not good enough, and a recursive upwards directory searching function is not good enough either because even though WP_CONTENT_DIR is defined from ABSPATH, it does not account for a possible alternatives to wp-content/plugins or wp-content/themes.)

My proposed solution is that the ABSPATH path can be defined in a file that is written to the base plugins directory (that is, in WP_PLUGIN_DIR) and the theme root directory (via get_theme_root()) so that either a plugin or theme can simply include this load file from it's parent directory in order to get the already defined and accurate ABSPATH for the purpose of loading wp-load.php (ie. ABSPATH.'/wp-load.php) - and then just proceed to do what it needs to do. ATTACHED is the working code to do this with (place in an mu-plugin file for testing):

It may seem a little strange at first to write this to a PHP file with a just a single define in it, but on the other hand it really seems to me to just be the simplest solution to this problem (which is more complex than it first seems.) I chose a PHP file because if it were a text file it would expose the absolute server path to external HTTP access unecessarily. And anything other than a file available consistently one directory up would not provide an accessible solution (eg. the path cannot be retrieved from the database because of course, database access is not available before load.)

As can be seen this initial proposal only checks for the direct file method of writing (to keep things simple to start with), but this could be expanded upon in future to better handle the other filesystem write methods by checking credentials etc. (Obviously we don't want to request credentials via a user form here, we just want to know if we have them so we can write the new file or not. The need for this could be bypassed by somewhat by including an empty file in the package, as updating a file can use relaxed file permissions while writing a new one cannot. But I digress on this point.)

For usage, a plugin or theme would then need to check that the wp-loadpath.php file exists, and to be safer that it's contents contain the ABSPATH. Doing it this way also prevents having to check the filesystem write method (which can be handled in the previous step and is overkill for plugin/theme authors to check on here.) Again, this being for a performance improvement, plugins/themes should never rely on this method, but rather, they would be able to access this more-performant method if it available (again basically if the file path is writeable with correct permissions) and fallback to a standard less-performant method if it is not, which it would need to do for backwards compatbility anyway. For a simple example, before enqueueing a dynamic stylesheet:

<?php
    add_action( 'wp_enqueue_scripts', 'wp_shortinit_load_example' );
    function wp_shortinit_load_example() {
      $loadpathfile = dirname(dirname(__FILE__)) . '/wp-loadpath.php';
      if ( (file_exists( $loadpathfile )) && (strstr(file_get_contents( $loadpathfile, ABSPATH ))) ) {
      wp_enqueue_style( 'dynamic-style-example', plugins_url( 'example.css.php', __FILE__ ) );
      } else {wp_enqueue_style( 'static-style-example', plugins_url( 'example.css', __FILE__ ) );}
    }

Then the dynamic example.css.php could contain something as simple as:

<?php
    define( 'SHORTINIT', true );
    require ( dirname( dirname( __FILE__ ) ) .'/wp-loadpath.php' );
    require ( ABSPATH . '/wp-load.php' );
    header( 'Content-type: text/css; charset: UTF-8' );
    echo esc_attr( get_option( 'shortinit_example_css' ) );
    exit;

I'm not looking to debate here whether using SHORTINIT for this example is a good idea or not, that is up to each use case. It has definite and well-tested advantages in terms of performance which is why it was created and now just needs to be exposed in a reliable way to truly be useable for that purpose. But if there are definite problems with this solution or a more elegant one (I have really scratched my brain to come up with this and don't see a more reliable and secure way, but who knows?) we can get into those. To my mind there are really only a few easy questions I can see that need to be answered before this could be implemented in core:

  1. Is wp-load.php always found in the ABSPATH directory? It would seem so, but just to check for certain, are there any known edge cases where this is actually not so?
  2. Does this create any possible security hole? It would seem not, as simply defining ABSPATH in a PHP file does not actually do anything and is no more accessible than any other file and so poses no real security risk.
  3. Since the solution proposed is to work with both plugins and themes, are there any considerations missing here that would mean they would be better treated as separately? It seems simple enough that they can be handled together at this stage.
  4. What is the best way of handling the other (non-direct) file write methods? A check for credentials and using the filesystem to write if they are already available seems to be all that is needed, beyond that seems unnecessary.
  5. Since the only change needed to implement this solution in core would be to add the mu-plugins code above to somewhere in a core file, where would the best place for that be? Perhaps just somewhere towards the end of wp-settings.php but probably there is a more appropriate place.
  6. After writing and testing this I realized even having a new file may not even be necessary, since index.php already exists (a definite advantage when handling the file writing) in both plugin and theme root directories and contains no actual PHP code, it could be put to this particular use instead of adding any new files (wp-loadpath.php). Unless of course index.php is ever used for any other purpose than blocking directory view access? It seems like it would be fine to use since it is a core file it shouldn't be changed by anyone else anyway.

Attachments (2)

loadpath.1.php (1.6 KB) - added by majick 3 years ago.
To check/create a load path definition file (mu-plugin)
loadpath.2.php (3.9 KB) - added by majick 3 years ago.
Improved version (auto-detect wp-load.php path, use filesystem)

Download all attachments as: .zip

Change History (3)

@majick
3 years ago

To check/create a load path definition file (mu-plugin)

#1 in reply to: ↑ description @dd32
3 years ago

  • Milestone Awaiting Review deleted
  • Resolution set to wontfix
  • Status changed from new to closed

Replying to majick:

  1. Is wp-load.php always found in the ABSPATH directory? It would seem so, but just to check for certain, are there any known edge cases where this is actually not so?

Yes. ABSPATH is the path which contains wp-load.php.

  1. Does this create any possible security hole? It would seem not, as simply defining ABSPATH in a PHP file does not actually do anything and is no more accessible than any other file and so poses no real security risk.

Yes, Maybe. It depends on what code is being executed, until WordPress is fully loaded anything is possible. No user authentication or capability checks can be used within a SHORTINIT scenario, and the majority of the codebase is not loaded.

  1. What is the best way of handling the other (non-direct) file write methods? A check for credentials and using the filesystem to write if they are already available seems to be all that is needed, beyond that seems unnecessary.

I'd estimate ~80% of installs can probably write to the filesystem directly. The rest have no file writability.

I don't think this is something we want to add to WordPress overall.

Use of the SHORTINIT method is not something plugins or themes should utilise - it should be classified as deprecated functionality. I'd argue it shouldn't be used by anything, ever.

The argument that WordPress is slow isn't enough to convince me here, WordPress's power is in it's extendability, the ability for other code to hook in and alter your code, the ability for you to alter other plugins code by using hooks, filters, etc, by doing this kind of thing you're explicitly opting out of that, and making it harder for other WordPress developers to use your code as part of theirs. Plus, WordPress isn't exactly as slow as people make it out to be - if you're having speed issues you should consider looking at other parts of your stack, and/or add appropriate caching.

I'm going to be blunt and close this as wontfix. Another committer can re-open if they deem it interesting to explore, discussion can continue while the ticket is closed, please don't re-open this just to ask me to reconsider it.

@majick
3 years ago

Improved version (auto-detect wp-load.php path, use filesystem)

Note: See TracTickets for help on using tickets.