通过不加载主题和插件,使WordPress WP-API更快


Make WordPress WP-API faster by not loading theme and plugins

我想更快地向WordPress API发出请求。我的API是在一个插件中实现的(使用register_rest_route注册我的路由)。然而,由于这是一个插件,所有的东西都加载了它(子元素和主题),基本上对这个API的查询需要半秒钟的时间,因为加载了所有这些无用的部分。

难道WordPress API不能以另一种方式使用吗?由于大多数使用WP-API的插件不需要加载任何其他插件,更不用说主题了。。。我不明白他们怎么会错过。

有办法这样做吗?

是的,这是可能的。在我的一个插件中,我需要最小的WordPress核心(没有插件和主题的DB),我做的是:

<?php
define('SHORTINIT', true);  // load minimal WordPress
require_once PATH_TO_WORDPRESS . '/wp-load.php'; // WordPress loader
// use $wpdb here, no plugins or themes were loaded

我编的PATH_TO_WORDPRESS常数;你只需要把它指向正确的路径。例如,在插件中,它可能看起来像:

require_once dirname(__FILE__) . '/../../../wp-load.php'; // backwards 'plugin-dir/plugins/wp-content'

SHORTINIT设置为true确实对性能有所帮助。

禁用WP_DEBUG后,引导WordPress所需的时间如下:

  • 无短路:约0.045秒
  • SHORTINIT:约0.0015秒

如果这是针对您自己需要性能的网站,您可能可以通过启用OpCache(例如,最近版本中的APC或PHP OpCache)来提高性能。

但我相信上面定义SHORTINIT和需要wp-load.php的两行代码就是您想要的。

为了澄清,这个文件是插件的一部分,但它是独立于WordPress本身调用的(通过Ajax和直接调用)。它从未被插件或WP本身的任何其他部分包含或使用。

编辑:由于OP实际上与WP-API有关,而不是一般的WordPress,因此我添加此内容以解决实际问题。我会保留原来的答案内容,以防对其他人有帮助。

我用WP API做了进一步的测试,就像@David在回答中所说的那样,问题可能是其他问题。

除了rest api之外,我还加载了12个插件,一些相当"大"的插件,我的本地安装大约安装了25个主题(当然有一个是活动的)。我编辑了WordPress的index.php文件,并使用microtime(true)记录一切何时开始,然后编辑了其中一个REST控制器,以计算从开始到到达API端点所需的时间。

在我的系统上,结果始终在0.0462-0.0513秒左右(没有PHP OpCache,也没有其他系统负载)。因此,似乎所有WordPress的自举对性能几乎没有影响。

如果请求需要半秒钟的时间,那么瓶颈就在其他地方,去掉插件和主题的影响微乎其微。至少这是我的发现。

我认为您可能关注的问题不对。

加载php文件并不像从数据库中读取那么慢,这可能是500毫秒的加载时间。实际上,你应该考虑减少这种情况(缓存wp选项等),但我建议你使用api缓存输出。使用exit,我们可以从文件加载输出并立即提供。

我们的方法:1.在wp内容文件夹(可能已经存在)中创建一个名为mu-plugins的文件夹

  1. 创建一个名为api-cache.php 的文件

  2. 将此代码输入您的文件:

    function get_api_cache(){
        //dont run if we are calling to cache the file (see later in the code)
        if( isset($_GET['cachecall']) && $_GET['cachecall'] === true)
            return;
        $url = "$_SERVER[REQUEST_URI]";
        //do a little error checking
        $uri= explode('/',$url);
        //we have a array (1st key is blank)
        if( $uri[1] !== 'wp-json' || $uri[2] !== 'wp' || $uri[3] !== 'v2'){
            return;
        } 
        //lock down the possible endpoints we dont want idiots playing with this...
        $allowed_endpoints= array(
            'posts'
        );
        $endpoint= array_pop($uri); // not sure if this is valid or not, is there more structure to some api calls?
        if( !in_array( $endpoint, $allowed_endpoints) ){
            return;
        } 
        //ok reasonably confident its a api call...
        $cache_folder= get_stylesheet_directory().'/api_cache/';
        // prob best if not within php server but to get you going
        if(! file_exists ( $cache_folder ) ){
            mkdir($cache_folder); //warning 777!!
        }
    
        /*
        * Need to choose a method of control for your cached json files
        * you could clear out the folder on update post/ taxonomies etc
        * or cron clear out hourly/weekly whatever freq you want
        */
    
        if( file_exists($cache_folder.$endpoint.'.json') ){
            $json= file_get_contents($cache_folder.$endpoint.'.json');
            header('Content-Type: application/json');
            echo $json;
            exit;// we need nothing else from php exit 
        } else {
            //make sure there will be no errors etc..
            $ch = curl_init();
            $url= "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]?cachecall=true";
            $timeout= 5;
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
            $json = curl_exec($ch);
            curl_close($ch);
            file_put_contents($cache_folder.$endpoint.'.json', $json);  
        }
    }
    get_api_cache();    
    

现在,您应该注意到第二次加载(这是第一次缓存输出)时的加载时间有显著差异。

一些免责声明:

  1. 你应该阅读代码中的注释
  2. 你需要卷曲
  3. 你需要知道缓存文件夹是777,我强烈建议你把它从你的主题文件夹中移走,最好是在你的http可访问文件之外
  4. 没有捕获要缓存的数据的catch-all钩子,因此我使用curl来捕获内容,这在未来可能会改变,钩子/过滤器将在创建缓存文件时稍微缩短处理时间
  5. 我还没有包含更新缓存文件的方法。你需要决定更新的频率,一个每天有很多帖子和大量访问的网站,你可以做一个cron工作来删除文件(例如,每天3次,每小时3次,每隔10分钟一次,等等——在更新时间上有什么合理的折衷?)或者添加一个钩子来保存帖子,只在帖子更改时更新,等等
  6. 将端点添加到它们的数组中(可以删除if语句以允许所有端点,但可能会出现缓存404的情况!)

你应该试试这个。它是一个插件,允许您为帖子类型、页面和其他情况启用/禁用某些插件。

对于主题部分,如果您编写了它,则可以很容易地在function.php中添加一些内容,以防止它在API请求的情况下附加任何挂钩或过滤器。

顺便说一句,你不能直接查询de DB吗?

如果这对你有帮助,很抱歉我的英语不好。

将插件文件夹放在根wordpress安装中。

/public_html/my-plugin/my-plugin.php
and include wordpress main file.
    require dirname( dirname( __FILE__ ) ).'/wp-load.php';

或者在插件文件夹中直接访问

/public_html/wp-content/plugins/my-plugin/my-plugin.php
require_once dirname(__FILE__) . '/../../../wp-load.php';

在检查wp-load.php文件之前,该文件已正确包含并正在工作。

wp-settings.php文件加载整个核心、插件和主题文件。wordpress是加载第一个mu-plugins文件(wp-content/mu-pluins/),并提供操作后挂钩muplugins_loaded。触发此操作可退出已加载的所有其他文件。您还可以在muplugins_loaded之前找到提供的操作挂钩,并停止其他文件和脚本执行。

如果在include wp-load.php之前定义常量SHORTINIT,则其includes一些文件提供DB、插件或基本功能。当我们想要更多的加载核心文件,而不仅仅是以这种方式加载插件和主题文件时,我们找到了一个解决方案。

// file my-plugin.php
//call before include file wp-load.php
global $wp_filter;
$wp_filter = array( 
    // pass wp hook where to want exit extra wp loaded
    'muplugins_loaded' => array(
     // prority
         1 => array(
            // callback function register
            'wp_extra_loaded_exit' => array( 
              'function' => 'wp_extra_loaded_exit',
              'accepted_args' => 1
              )
         ) 
   )
);

function wp_extra_loaded_exit(){
  exit; 
}
require dirname( dirname( __FILE__ ) ).'/wp-load.php';
// plugin code here.

我们检查muplugins_loaded钩子是早期定义wordpress的。您还可以找到哪个钩子是在muplugins_lloaded

当你想测试你的脚本时,打开文件wp-settings.php,找到字符串muplugins_loaded,然后回显语句进行检查。

echo "Wordpress loaded in this point before"; 
do_action( 'muplugins_loaded' );
echo "After this wordpress not loading"; // Output fail bcz we exit