Как создать «виртуальную» страницу в WordPress?

52

Я пытаюсь создать пользовательскую конечную точку API в WordPress, и мне нужно перенаправить запросы на виртуальную страницу в корне WordPress на фактическую страницу, которая поставляется с моим плагином. Таким образом, в основном все запросы к одной странице фактически направляются на другую.

Пример:
http://mysite.com/my-api.php=>http://mysite.com/wp-content/plugins/my-plugin/my-api.php

Смысл этого состоит в том, чтобы сделать URL-адрес для конечной точки API как можно более коротким (аналогично тому, http://mysite.com/xmlrpc.phpчтобы поставлять фактический файл конечной точки API с помощью плагина, а не требовать от пользователя перемещения файлов в их установке и / или взломать ядро). ,

Моим первым ударом было добавить пользовательское правило перезаписи. Однако у этого были две проблемы.

  1. Конечная точка всегда имела косую черту. Это сталоhttp://mysite.com/my-api.php/
  2. Мое правило переписывания применялось только частично. Он не будет перенаправлять на wp-content/plugins..., он будет перенаправлять на index.php&wp-content/plugins.... Это приводит к тому, что WordPress отображает либо ошибку «страница не найдена», либо просто значение по умолчанию на главной странице.

Идеи? Предложения?

EAMann
источник

Ответы:

55

В WordPress есть два типа правил перезаписи: внутренние правила (хранящиеся в базе данных и анализируемые WP :: parse_request () ), и внешние правила (хранящиеся в .htaccessApache и анализируемые в них). Вы можете выбрать любой из способов, в зависимости от того, сколько 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 );
}

Внутренние правила:

Внутреннее правило требует дополнительной работы: сначала мы добавляем правило перезаписи, которое добавляет запрос vars, затем мы делаем этот запрос 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. Я играл с этим в течение часа, прежде чем подумать, что мне нужно обновить постоянные ссылки ... Разве кто-то знает функцию, которая может это сделать?
Этанпил
Для внешнего правила: поскольку путь к моему веб-корню имел символ пробела, это привело к падению apache. Пробельные символы необходимо экранировать, если они существуют на пути к вашей установке WordPress.
Уилстер
1
Работает, но я не могу получить доступ к каким-либо переданным переменным запроса get_query_vars()в my-api.php. Я проверил, какие переменные загружены. И единственная переменная, которая установлена, WP objectназывается $wp. Как я могу получить доступ или преобразовать это в WP_Queryобъект, чтобы я мог получить доступ к переданным переменным с get_query_vars()?
Жюль
1
@Jules: когда вы includeфайл, он выполняется в текущей области. В данном случае это wpse9870_parse_requestфункция, которая имеет только $wpпараметр. Возможно, что глобальный $wp_queryобъект еще не был установлен, поэтому get_query_var()не будет работать. Тем не менее, вам повезло: $wpкласс, который содержит query_varsчлен, который вам нужен - я использую его сам в приведенном выше коде.
Ян Фабри
1
пытаясь создать внешние правила переписывания. Добавил свой первый кусок кода, но я все еще получаю 404. Кстати: правила перезаписи
flished
12

Это сработало для меня. Я никогда не касался API переписывания, но всегда готов подтолкнуть себя в новых направлениях. Следующее работает на моем тестовом сервере для 3.0, расположенном в подпапке localhost. Я не вижу проблем, если 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' );

С наилучшими пожеланиями, Майк

mfields
источник
1
Я получил сообщение об ошибке доступа при попытке этого кода. Я подозреваю, что моему серверу или WP не понравился абсолютный URL. Это, с другой стороны, прекрасно работали:add_rewrite_rule( 'taco-kittens', 'wp-content/plugins/taco-kittens.php', 'top' );
Жюль
Подскажите, пожалуйста, что я должен поместить в taco-kittens.php, у меня нет знаний о .htaccess или перезаписи URL.
Прафулла Кумар Саху
9

Есть ли причина не делать что-то подобное вместо этого?

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

Затем просто подключите ваш плагин к 'init' и проверьте переменную get. Если он существует, сделайте то, что нужно вашему плагину, и умрите ()

Уилл Андерсон
источник
5
Это бы сработало, но я пытаюсь обеспечить очень четкое различие между переменными запроса и фактической конечной точкой. В будущем могут появиться другие аргументы запросов, и я не хочу, чтобы пользователи все перемешивали.
EAMann
Что если вы сохранили переписать, но переписали его в GET var? Вы также можете посмотреть, как работает rewrite for robots.txt. Это может помочь вам понять, как избежать перенаправления на my-api.php /
Уилл Андерсон
Это идеальное решение, если вам не нужны такие роботы, как вызовы API.
бейтаровски
4

Возможно, я не совсем понимаю ваши вопросы, но решит ли ваш простой код короткий номер?

шаги:

  1. Попросите клиента создать страницу, например, http://mysite.com/my-api.
  2. Попросите клиента добавить шорткод на этой странице, т.е. [my-api-shortcode]

Новая страница действует как конечная точка API, и ваш шорткод отправляет запросы на код вашего плагина по адресу http://mysite.com/wp-content/plugins/my-plugin/my-api.php.

(конечно, это означает, что my-api.php будет иметь определенный шорткод)

Вероятно, вы можете автоматизировать шаги 1 и 2 с помощью плагина.

rexposadas
источник
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.

wyrfel
источник
1

У меня было похожее требование, и я хотел создать несколько конечных точек на основе уникальных слагов, которые указывают на контент, сгенерированный плагином.

Посмотрите на источник для моего плагина: https://wordpress.org/extend/plugins/picasa-album-uploader/

Техника, которую я использовал, начинается с добавления фильтра для the_postsпроверки входящего запроса. Если плагин должен обработать его, генерируется фиктивная запись и добавляется действие для template_redirect.

Когда template_redirectвызывается действие, оно должно привести к выводу всего содержимого отображаемой страницы и завершению, или оно должно возвращаться без генерируемого вывода. Посмотрите код, wp_include/template-loader.phpи вы поймете, почему.

кругозор
источник
1

Я использую другой подход, который заключается в том, чтобы заставить домашнюю страницу загружать пользовательский заголовок, контент и шаблон страницы .

Решение очень простое, так как оно может быть реализовано, когда пользователь переходит по дружественной ссылке, такой как http://example.com/ ? Plugin_page = myfakepage

Это очень легко реализовать и должно допускать неограниченное количество страниц.

Код и инструкции здесь: Создайте пользовательскую / поддельную / виртуальную страницу Wordpress на лету

Хави Эстеве
источник
0

Я использую подход, аналогичный описанному выше Хави Эстеве, который перестал работать из-за обновления 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;


                }


            } );
Mr.Hosseini
источник