您如何在WordPress中创建“虚拟”页面


52

我正在尝试在WordPress中创建自定义API端点,并且需要将请求重定向到WordPress根目录中的虚拟页面,并将其重定向到我的插件随附的实际页面。因此,基本上,对一个页面的所有请求实际上都会路由到另一页面。

示例:
http://mysite.com/my-api.php=>http://mysite.com/wp-content/plugins/my-plugin/my-api.php

这样做的目的是使API端点的URL尽可能短(类似于http://mysite.com/xmlrpc.php但将实际的API端点文件随插件一起提供,而不是要求用户在其安装和/或hack核心中四处移动文件) 。

我的第一个选项是添加一个自定义重写规则。但是,这有两个问题。

  1. 端点总是有一个斜杠。它变成了http://mysite.com/my-api.php/
  2. 我的重写规则仅部分适用。它不会重定向到wp-content/plugins...,而是会重定向到index.php&wp-content/plugins...。这导致WordPress显示页面未找到错误或仅默认为主页。

有想法吗?有什么建议吗?

Answers:


55

WordPress中有两种重写规则:内部规则(存储在数据库中并由WP :: parse_request()解析)和外部规则(存储在.htaccessApache中并由Apache解析)。您可以选择任何一种方式,具体取决于所调用文件中需要多少WordPress。

外部规则:

外部规则是最容易设置和遵循的。它将my-api.php在您的插件目录中执行,而无需从WordPress加载任何内容。

add_action( 'init', 'wpse9870_init_external' );
function wpse9870_init_external()
{
    global $wp_rewrite;
    $plugin_url = plugins_url( 'my-api.php', __FILE__ );
    $plugin_url = substr( $plugin_url, strlen( home_url() ) + 1 );
    // The pattern is prefixed with '^'
    // The substitution is prefixed with the "home root", at least a '/'
    // This is equivalent to appending it to `non_wp_rules`
    $wp_rewrite->add_external_rule( 'my-api.php$', $plugin_url );
}

内部规则:

内部规则需要做更多的工作:首先添加一个重写规则,该规则添加一个查询var,然后将该查询var公开,然后我们需要检查该查询var是否存在以将该控件传递给我们的插件文件。到我们这样做时,通常的WordPress初始化就已经发生了(我们在常规的后期查询之前就中断了)。

add_action( 'init', 'wpse9870_init_internal' );
function wpse9870_init_internal()
{
    add_rewrite_rule( 'my-api.php$', 'index.php?wpse9870_api=1', 'top' );
}

add_filter( 'query_vars', 'wpse9870_query_vars' );
function wpse9870_query_vars( $query_vars )
{
    $query_vars[] = 'wpse9870_api';
    return $query_vars;
}

add_action( 'parse_request', 'wpse9870_parse_request' );
function wpse9870_parse_request( &$wp )
{
    if ( array_key_exists( 'wpse9870_api', $wp->query_vars ) ) {
        include 'my-api.php';
        exit();
    }
    return;
}

3
我只想补充一点,很重要的一点是转到“永久链接”页面,然后在WP-Admin中单击“保存更改”。在考虑需要刷新永久链接之前,我已经玩了一个小时了……除非有人知道可以做到这一点的功能?
ethanpil

对于外部规则:由于到我的Web根的路径具有空格字符,因此导致apache掉线。如果在您的wordpress安装路径中存在空白,则需要对其进行转义。
Willster 2013年

1
get_query_vars()可行,但我似乎无法在my-api.php中访问任何传递的查询变量。我检查了加载了哪些变量。和设置唯一的变种是AA WP object$wp。如何访问或将其转换为WP_Query对象,以便可以使用来访问传递的变量get_query_vars()
2013年

1
@Jules:当您include使用文件时,它将在当前作用域中执行。在这种情况下,wpse9870_parse_request只有$wp参数的是函数。可能$wp_query此时尚未设置全局对象,因此get_query_var()将无法使用。但是,您很幸运:$wp是包含所需query_vars成员的类-我在上面的代码中亲自使用它。
2013年

1
试图创建一个外部重写规则。添加了您的第一段代码,但是我仍然得到404。btw:刷新的重写规则
Sisir 2013年

12

这对我有用。我从未接触过重写API,但始终致力于将自己推向新的方向。以下内容适用于位于本地主机子文件夹中的3.0版测试服务器。如果Web根目录中安装了WordPress,我看不到任何问题。

只需将此代码放在插件中,然后直接在插件文件夹中上传名为“ taco-kittens.php”的文件。您将需要为永久链接编写一个硬刷新。我认为他们说,最好的时机是在插件激活上。

function taco_kitten_rewrite() {
    $url = str_replace( trailingslashit( site_url() ), '', plugins_url( '/taco-kittens.php', __FILE__ ) );
    add_rewrite_rule( 'taco-kittens\\.php$', $url, 'top' );
}
add_action( 'wp_loaded', 'taco_kitten_rewrite' );

祝你好运-迈克


1
尝试此代码时出现访问拒绝错误。我怀疑我的服务器或WP不喜欢绝对URL。另一方面,此方法运行良好:add_rewrite_rule( 'taco-kittens', 'wp-content/plugins/taco-kittens.php', 'top' );
Jules

您能告诉我,我应该放在taco-kittens.php中吗,我不知道.htaccess或url rewrite。
Prafulla Kumar Sahu's

9

有什么理由不做这样的事情吗?

http://mysite.com/?my-api=1

然后只需将您的插件挂接到'init'并检查该get变量即可。如果存在,请执行您的插件需要执行的操作,然后die()


5
那会起作用,但是我试图在查询变量和实际端点之间提供一个非常清晰的区别。将来可能还会有其他查询参数,但我不希望用户混淆。
EAMann 2011年

如果您保留重写,但将其重写为GET var怎么办?您可能还会查看robots.txt的重写方式。它可能会帮助您弄清楚如何避免重定向到my-api.php /
Will Anderson

如果您不喜欢机器人之类的API调用,这是一个完美的解决方案。
beytarovski


1

我还没有处理那么多的重写,所以这可能有点粗糙,但这似乎可行:

function api_rewrite($wp_rewrite) {
    $wp_rewrite->non_wp_rules['my-api\.php'] = 'wp-content/plugins/my-plugin/my-api.php';
    file_put_contents(ABSPATH.'.htaccess', $wp_rewrite->mod_rewrite_rules() );
}

如果将其挂接到“ generate_rewrite_rules”中,它会起作用,但是必须有更好的方法,因为您不想在每次页面加载时都重写.htaccess。
似乎我无法停止编辑自己的帖子...它可能应该进入激活回调并引用全局$ wp_rewrite。然后从non_wp_rules中删除该条目,并在停用回调中再次输出到.htaccess。

最后,编写.htaccess的内容应该更加复杂,您只想替换其中的wordpress部分即可。


1

我有一个类似的要求,并希望基于指向插件生成的内容的唯一块创建多个端点。

看看我的插件的来源:https : //wordpress.org/extend/plugins/picasa-album-uploader/

我使用的技术首先添加了用于the_posts检查传入请求的过滤器。如果插件可以处理,则会生成一个虚拟帖子,并为添加一个操作template_redirect

template_redirect调用该操作时,它必须导致输出要显示的页面的全部内容并退出,否则它应该返回而不会生成任何输出。查看其中的代码wp_include/template-loader.php,您将明白原因。



0

我使用的是类似于上述Xavi Esteve的方法,据我所知,由于WordPress升级,该方法在2013年下半年停止了工作。

它的详细记录在这里:https : //stackoverflow.com/questions/17960649/wordpress-plugin-generating-virtual-pages-and-using-theme-template

我方法的关键部分是使用现有模板,因此结果页面看起来像是网站的一部分。我希望它与所有主题尽可能兼容,希望跨WordPress版本兼容。时间会证明我是否正确!


0

这是一个生产就绪示例,首先创建虚拟页面类:


class VirtualPage
{

    private $query;
    private $title;
    private $content;
    private $template;
    private $wp_post;

    function __construct($query = '/index2', $template = 'page', $title = 'Untitled')
    {
        $this->query = filter_var($query, FILTER_SANITIZE_URL);
        $this->setTemplate($template);
        $this->setTitle($title);
    }

    function getQuery()
    {
        return $this->query;
    }

    function getTemplate()
    {
        return $this->template;
    }

    function getTitle()
    {
        return $this->title;
    }

    function setTitle($title)
    {
        $this->title = filter_var($title, FILTER_SANITIZE_STRING);

        return $this;
    }

    function setContent($content)
    {
        $this->content = $content;

        return $this;
    }

    function setTemplate($template)
    {
        $this->template = $template;

        return $this;
    }

    public function updateWpQuery()
    {

        global $wp, $wp_query;

        // Update the main query
        $wp_query->current_post = $this->wp_post->ID;
        $wp_query->found_posts = 1;
        $wp_query->is_page = true;//important part
        $wp_query->is_singular = true;//important part
        $wp_query->is_single = false;
        $wp_query->is_attachment = false;
        $wp_query->is_archive = false;
        $wp_query->is_category = false;
        $wp_query->is_tag = false;
        $wp_query->is_tax = false;
        $wp_query->is_author = false;
        $wp_query->is_date = false;
        $wp_query->is_year = false;
        $wp_query->is_month = false;
        $wp_query->is_day = false;
        $wp_query->is_time = false;
        $wp_query->is_search = false;
        $wp_query->is_feed = false;
        $wp_query->is_comment_feed = false;
        $wp_query->is_trackback = false;
        $wp_query->is_home = false;
        $wp_query->is_embed = false;
        $wp_query->is_404 = false;
        $wp_query->is_paged = false;
        $wp_query->is_admin = false;
        $wp_query->is_preview = false;
        $wp_query->is_robots = false;
        $wp_query->is_posts_page = false;
        $wp_query->is_post_type_archive = false;
        $wp_query->max_num_pages = 1;
        $wp_query->post = $this->wp_post;
        $wp_query->posts = array($this->wp_post);
        $wp_query->post_count = 1;
        $wp_query->queried_object = $this->wp_post;
        $wp_query->queried_object_id = $this->wp_post->ID;
        $wp_query->query_vars['error'] = '';
        unset($wp_query->query['error']);

        $GLOBALS['wp_query'] = $wp_query;

        $wp->query = array();
        $wp->register_globals();

    }

    public function createPage()
    {
        if (is_null($this->wp_post)) {
            $post = new stdClass();
            $post->ID = -99;
            $post->ancestors = array(); // 3.6
            $post->comment_status = 'closed';
            $post->comment_count = 0;
            $post->filter = 'raw';
            $post->guid = home_url($this->query);
            $post->is_virtual = true;
            $post->menu_order = 0;
            $post->pinged = '';
            $post->ping_status = 'closed';
            $post->post_title = $this->title;
            $post->post_name = sanitize_title($this->template); // append random number to avoid clash
            $post->post_content = $this->content ?: '';
            $post->post_excerpt = '';
            $post->post_parent = 0;
            $post->post_type = 'page';
            $post->post_status = 'publish';
            $post->post_date = current_time('mysql');
            $post->post_date_gmt = current_time('mysql', 1);
            $post->modified = $post->post_date;
            $post->modified_gmt = $post->post_date_gmt;
            $post->post_password = '';
            $post->post_content_filtered = '';
            $post->post_author = is_user_logged_in() ? get_current_user_id() : 0;
            $post->post_content = '';
            $post->post_mime_type = '';
            $post->to_ping = '';

            $this->wp_post = new WP_Post($post);
            $this->updateWpQuery();

            @status_header(200);
            wp_cache_add(-99, $this->wp_post, 'posts');

        }


        return $this->wp_post;
    }
}

在下一步中,执行钩子template_redirect操作并处理您的虚拟页面,如下所示

    add_action( 'template_redirect', function () {


                    switch ( get_query_var( 'name' ,'') ) {

                        case 'contact':
                            // http://yoursite/contact  ==> loads page-contact.php
                            $page = new VirtualPage( "/contact", 'contact',__('Contact Me') );
                            $page->createPage();
                            break;

                        case 'archive':
                            // http://yoursite/archive  ==> loads page-archive.php
                            $page = new VirtualPage( "/archive", 'archive' ,__('Archives'));
                            $page->createPage();
                            break;

                        case 'blog':
                            // http://yoursite/blog  ==> loads page-blog.php
                            $page = new VirtualPage( "/blog", 'blog' ,__('Blog'));
                            $page->createPage();
                            break;


                }


            } );
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.