Come si crea una pagina "virtuale" in WordPress


52

Sto cercando di creare un endpoint API personalizzato in WordPress e devo reindirizzare le richieste a una pagina virtuale nella radice di WordPress su una pagina effettiva fornita con il mio plug-in. Quindi, in pratica, tutte le richieste a una pagina vengono effettivamente instradate all'altra.

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

Il punto è rendere l'URL dell'endpoint dell'API il più breve possibile (simile a http://mysite.com/xmlrpc.phpma per spedire il file dell'endpoint dell'API effettivo con il plug-in anziché richiedere all'utente di spostare i file nella loro installazione e / o core di hacking .

Il mio primo colpo è stato quello di aggiungere una regola di riscrittura personalizzata. Tuttavia, questo ha avuto due problemi.

  1. L'endpoint ha sempre avuto una barra finale. Diventòhttp://mysite.com/my-api.php/
  2. La mia regola di riscrittura è stata applicata solo parzialmente. Non reindirizzerebbe a wp-content/plugins..., reindirizzerebbe a index.php&wp-content/plugins.... Ciò ha portato WordPress a visualizzare un errore di pagina non trovata o semplicemente il default alla homepage.

Idee? Suggerimenti?

Risposte:


55

Esistono due tipi di regole di riscrittura in WordPress: regole interne (archiviate nel database e analizzate da WP :: parse_request () ) e regole esterne (archiviate .htaccesse analizzate da Apache). Puoi scegliere in entrambi i modi, a seconda della quantità di WordPress di cui hai bisogno nel tuo file chiamato.

Regole esterne:

La regola esterna è la più semplice da impostare e da seguire. Verrà eseguito my-api.phpnella directory dei plug-in, senza caricare nulla da 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 );
}

Regole interne:

La regola interna richiede un po 'più di lavoro: prima aggiungiamo una regola di riscrittura che aggiunge una query vars, quindi rendiamo pubblica questa query var e quindi dobbiamo verificare l'esistenza di questa var var per passare il controllo al nostro file plugin. Quando lo facciamo, sarà avvenuta la solita inizializzazione di WordPress (ci interrompiamo subito prima della normale query post).

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
Voglio solo aggiungere che è importante andare alla pagina Permalink e fare clic su "Salva modifiche" in WP-Admin. Ci stavo giocando da un'ora prima di pensare che dovevo rinfrescare i permalink ... A meno che qualcuno non conosca una funzione in grado di farlo?
ethanpil,

Per la regola esterna: poiché il percorso della mia radice web aveva un carattere di spazio bianco, questo causava la caduta di apache. Gli spazi bianchi devono essere salvati se esistono nel percorso dell'installazione di wordpress.
Willster,

1
Funziona, ma non riesco ad accedere ad alcuna variabile di query passata con get_query_vars()in my-api.php. Ho verificato quali variabili sono state caricate. E l'unico var impostato è aa WP objectchiamato $wp. Come accedo o trasformo questo in un WP_Queryoggetto in modo da poter accedere alle variabili passate con get_query_vars()?
Jules il

1
@Jules: quando si esegue includeun file, questo viene eseguito nell'ambito corrente. In questo caso, è la wpse9870_parse_requestfunzione che ha solo il $wpparametro. È possibile che l' $wp_queryoggetto globale non sia stato impostato in questo momento, quindi get_query_var()non funzionerà. Tuttavia, sei fortunato: $wpè la classe che contiene il query_varsmembro di cui hai bisogno - lo uso io stesso nel codice sopra.
Jan Fabry,

1
cercando di creare regole di riscrittura esterne. aggiunto il tuo pugno di codice ma sto ancora ottenendo 404. btw: arrossito regole di riscrittura
Sisir

12

Questo ha funzionato per me. Non tocco mai l'API di riscrittura, ma sono sempre pronto a spingermi in nuove direzioni. Quanto segue ha funzionato sul mio server di prova per 3.0 che si trova in una sottocartella di localhost. Non vedo alcun problema se WordPress è installato nella radice web.

Rilascia semplicemente questo codice in un plugin e carica il file "taco-kittens.php" direttamente nella cartella del plugin. Dovrai scrivere una scala reale per i tuoi permalink. Penso che dicono che il momento migliore per farlo sia l'attivazione del plugin.

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' );

I migliori auguri, -Mike


1
Ho riscontrato un errore di accesso negato durante il tentativo di questo codice. Sospetto che al mio server o WP non sia piaciuto l'URL assoluto. Questo, d'altra parte, ha funzionato bene:add_rewrite_rule( 'taco-kittens', 'wp-content/plugins/taco-kittens.php', 'top' );
Jules il

Potete per favore fatemi sapere cosa dovrei inserire in taco-kittens.php, non ho conoscenza di .htaccess o riscrittura dell'URL.
Prafulla Kumar Sahu,

9

Qualche motivo per non fare qualcosa del genere invece?

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

Quindi aggancia il tuo plug-in a "init" e verifica che sia variabile. Se esiste, fai ciò che il tuo plugin deve fare e muori ()


5
Funzionerebbe, ma sto cercando di fornire una chiara distinzione tra le variabili della query e l'endpoint effettivo. Potrebbero esserci altri argomenti di query in futuro e non voglio che gli utenti mescolino le cose.
EAMann,

Che cosa succede se si mantiene la riscrittura, ma si riscrive su GET var? Potresti anche vedere come funziona la riscrittura per robots.txt. Potrebbe aiutarti a capire come evitare il reindirizzamento a my-api.php /
Will Anderson,

È la soluzione perfetta se non ti interessano i robot come le chiamate API.
beytarovski

4

Potrei non capire completamente le tue domande, ma un semplice shortcode risolverebbe il tuo problema?

passi:

  1. Chiedi al cliente di creare una pagina, ad esempio http://mysite.com/my-api
  2. Chiedi al cliente di aggiungere uno shortcode in quella pagina, ad esempio [my-api-shortcode]

La nuova pagina funge da endpoint API e il tuo shortcode invia richieste al tuo codice plugin in http://mysite.com/wp-content/plugins/my-plugin/my-api.php

(ovviamente questo significa che my-api.php avrebbe definito lo shortcode)

Probabilmente puoi automatizzare i passaggi 1 e 2 tramite il plugin.


1

Non mi sono ancora occupato di riscrivere così tanto, quindi probabilmente è un po 'approssimativo, ma sembra funzionare:

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() );
}

Funziona se lo colleghi a 'generate_rewrite_rules', ma deve esserci un modo migliore, poiché non vuoi riscrivere .htaccess ad ogni caricamento della pagina.
Sembra che non riesca a smettere di modificare i miei post ... probabilmente dovrebbe piuttosto entrare in te attivare la callback e fare riferimento invece a $ wp_rewrite globale. Quindi rimuovere la voce da non_wp_rules e inviarla nuovamente a .htaccess nella disattivazione della richiamata.

E infine, la scrittura su .htaccess dovrebbe essere un po 'più sofisticata, vuoi solo sostituire la sezione wordpress lì dentro.


1

Avevo un requisito simile e volevo creare diversi end-point basati su lumache uniche che puntavano al contenuto generato dal plugin.

Dai un'occhiata alla fonte del mio plugin: https://wordpress.org/extend/plugins/picasa-album-uploader/

La tecnica che ho usato inizia aggiungendo un filtro per the_postsesaminare la richiesta in arrivo. Se il plugin deve gestirlo, viene generato un post fittizio e viene aggiunta un'azione per template_redirect.

Quando template_redirectviene chiamata l' azione, deve risultare nell'output dell'intero contenuto della pagina da visualizzare e uscire o dovrebbe tornare senza generare output. Vedi il codice wp_include/template-loader.phpe vedrai perché.



0

Sto usando un approccio simile a quello di Xavi Esteve, che ha smesso di funzionare a causa di un aggiornamento di WordPress, per quanto ho potuto dire nella seconda metà del 2013.

È documentato in dettaglio qui: https://stackoverflow.com/questions/17960649/wordpress-plugin-generating-virtual-pages-and-using-theme-template

La parte fondamentale del mio approccio sta usando il modello esistente in modo che la pagina risultante sembri far parte del sito; Volevo che fosse il più compatibile possibile con tutti i temi, si spera nelle versioni di WordPress. Il tempo dirà se avevo ragione!


0

è un esempio readey di produzione, prima crea una classe di pagine virtuali:


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;
    }
}

Nel passaggio successivo aggancia l' template_redirectazione e gestisci la tua pagina virtuale come di seguito

    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;


                }


            } );
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.