Come creare una pagina "virtuale" in WordPress

20 feb 2011, 06:05:46
Visualizzazioni: 68.3K
Voti: 61

Sto cercando di creare un endpoint API personalizzato in WordPress e ho bisogno di reindirizzare le richieste a una pagina virtuale nella root di WordPress verso una pagina effettiva inclusa nel mio plugin. In sostanza, tutte le richieste a una pagina vengono effettivamente instradate all'altra.

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

L'obiettivo è mantenere l'URL dell'endpoint API il più breve possibile (simile a http://miosito.com/xmlrpc.php), ma fornire il file effettivo dell'endpoint API con il plugin invece di richiedere all'utente di spostare file nell'installazione o modificare il core.

Il mio primo tentativo è stato aggiungere una regola di rewrite personalizzata. Tuttavia, questo ha presentato due problemi:

  1. L'endpoint aveva sempre una barra finale. Diventava http://miosito.com/my-api.php/
  2. La mia regola di rewrite veniva applicata solo parzialmente. Non reindirizzava a wp-content/plugins..., ma a index.php&wp-content/plugins.... Questo portava WordPress a mostrare un errore di pagina non trovata o semplicemente a visualizzare la homepage.

Idee? Suggerimenti?

0
Tutte le risposte alla domanda 9
15
63

Esistono due tipi di regole di riscrittura in WordPress: regole interne (memorizzate nel database e analizzate da WP::parse_request()), e regole esterne (memorizzate in .htaccess e analizzate da Apache). Puoi scegliere uno dei due metodi, a seconda di quanto di WordPress hai bisogno nel tuo file chiamato.

Regole Esterne:

La regola esterna è la più semplice da configurare e da seguire. Eseguirà my-api.php nella directory del tuo plugin, 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 );
    // Il pattern è preceduto da '^'
    // La sostituzione è preceduta dalla "radice home", almeno un '/'
    // Questo è equivalente ad aggiungerlo a `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 variabile di query, poi rendiamo questa variabile di query pubblica, e infine dobbiamo verificare l'esistenza di questa variabile di query per passare il controllo al nostro file del plugin. Quando facciamo questo, l'inizializzazione normale di WordPress sarà già avvenuta (interrompiamo il flusso appena prima della query regolare dei 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;
}
21 feb 2011 15:57:32
Commenti

Voglio solo aggiungere che è importante andare nella pagina Permalink e cliccare su "Salva modifiche" in WP-Admin. Ho perso un'ora prima di pensare che dovevo aggiornare i permalink... A meno che qualcuno non conosca una funzione che possa farlo?

ethanpil ethanpil
28 mag 2013 21:21:50

Per la regola esterna: Poiché il percorso alla mia root web conteneva un carattere spazio, questo faceva crashare apache. Gli spazi devono essere escape se presenti nel percorso della tua installazione WordPress.

Willster Willster
4 giu 2013 12:41:23

Funziona, ma non riesco ad accedere alle variabili di query passate con get_query_vars() in my-api.php. Ho controllato quali variabili sono caricate. E l'unica variabile impostata è un oggetto WP chiamato $wp. Come posso accedere o trasformarlo in un oggetto WP_Query per poter accedere alle variabili passate con get_query_vars()?

Jules Jules
13 ago 2013 13:24:43

@Jules: Quando includi un file con include, questo viene eseguito nello scope corrente. In questo caso, è la funzione wpse9870_parse_request, che ha solo il parametro $wp. È possibile che l'oggetto globale $wp_query non sia stato ancora impostato in questo momento, quindi get_query_var() non funzionerà. Tuttavia, sei fortunato: $wp è la classe che contiene il membro query_vars di cui hai bisogno - lo uso io stesso nel codice sopra.

Jan Fabry Jan Fabry
13 ago 2013 18:49:30

@JanFabry Grazie. L'ho capito dopo un po'. Mi chiedevo solo se ci fosse un modo per convertire un oggetto WP in un oggetto WP_Query. Ma non sono sicuro che ci sarebbe mai bisogno di una cosa del genere, quindi non importa. Grazie per questo e per tutti gli altri tuoi pezzi di codice. Ho già trovato molte informazioni utili su questo sito grazie a te.

Jules Jules
14 ago 2013 19:59:29

sto cercando di creare delle regole di rewrite esterne. Ho aggiunto il tuo primo pezzo di codice ma ottengo ancora 404. btw: ho svuotato le regole di rewrite

Sisir Sisir
30 nov 2013 10:36:36

Ricevo ancora un errore 404 con le regole di riscrittura esterne. Solo per chiarire, il codice sopra va nel file PHP principale del plugin, non nel file delle funzioni dei template, corretto?

Lee Lee
6 feb 2015 12:37:56

@ethanpil puoi (ora?) svuotare le regole per includere le tue nuove regole di riscrittura se le regole di riscrittura di WP non le includono. Il metodo è documentato qui http://codex.wordpress.org/Class_Reference/WP_Rewrite

Ejaz Ejaz
31 ago 2015 11:31:21

Nel mio caso funziona sull'URL index.php?wpse9870_api=1 e anche my-api.php?wpse9870_api=1 come posso rimuovere la query string?

er.irfankhan11 er.irfankhan11
12 feb 2016 16:51:58

@Irfan Sto cercando di ottenere lo stesso risultato, puoi dirmi cosa dovrei scrivere nel mio my-api.php?

Prafulla Kumar Sahu Prafulla Kumar Sahu
4 mag 2016 13:32:31

@Prafulla Kumar Sahu Sto usando in questo modo: `add_filter( 'query_vars', 'wpse9870_query_vars' ); function wpse9870_query_vars( $query_vars ) { $query_vars[] = 'getrequest'; return $query_vars; }

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

e l'URL è http://homeurl/?getrequest

er.irfankhan11 er.irfankhan11
6 mag 2016 10:53:38

La regola interna è veramente memorizzata nel database? add_rewrite_rule esegue un INSERT nel database? Sembra che sia memorizzata solo nel codice sorgente.

Jeff Jeff
10 apr 2017 19:03:20

@ethanpil flush_rewrite_rules( true );

mircobabini mircobabini
16 mag 2017 11:01:32

Scusa la domanda da principiante ma... dove dovrebbe andare la chiamata add_action( 'init'...? L'ho messa nel metodo __construct() del mio plugin, ma il metodo callback non viene mai eseguito. Ho provato a riavviare il server, ecc.

T Nguyen T Nguyen
14 apr 2021 11:34:34

Sto cercando di usare la regola External ma ricevo un errore 403 quando provo ad accedere direttamente. Il file .htaccess predefinito in wp-content blocca l'accesso al file php nella directory del mio plugin, e nessuna delle regole .htaccess che ho aggiunto per consentire l'accesso funziona. Qualche suggerimento?

AutoBaker AutoBaker
14 feb 2023 11:09:04
Mostra i restanti 10 commenti
2
13

Questo ha funzionato per me. Non ho mai toccato l'API di riscrittura, ma sono sempre pronto a spingermi in nuove direzioni. Quanto segue ha funzionato sul mio server di test per la versione 3.0 situato in una sottocartella di localhost. Non prevedo alcun problema se WordPress è installato nella radice del sito web.

Basta inserire questo codice in un plugin e caricare il file denominato "taco-kittens.php" direttamente nella cartella dei plugin. Sarà necessario eseguire un hard flush per i tuoi permalink. Credo che il momento migliore per farlo sia durante 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' );

Buona fortuna, -Mike

20 feb 2011 08:00:23
Commenti

Ho ricevuto un errore di accesso negato mentre provavo questo codice. Sospetto che il mio server o WP non abbiano apprezzato l'URL assoluto. Questo invece ha funzionato bene: add_rewrite_rule( 'taco-kittens', 'wp-content/plugins/taco-kittens.php', 'top' );

Jules Jules
13 ago 2013 12:46:54

Puoi per favore dirmi cosa devo mettere in taco-kittens.php, non ho conoscenza di .htaccess o url rewrite.

Prafulla Kumar Sahu Prafulla Kumar Sahu
4 mag 2016 13:47:01
3
11

Qualche motivo per non fare qualcosa del genere invece?

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

Quindi collega semplicemente il tuo plugin a 'init' e controlla quella variabile GET. Se esiste, fai ciò che il tuo plugin deve fare e usa die()

20 feb 2011 06:49:28
Commenti

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

EAMann EAMann
20 feb 2011 06:53:50

E se mantenessi la riscrittura, ma la riscrivessi alla variabile GET? Potresti anche dare un'occhiata a come funziona la riscrittura per robots.txt. Potrebbe aiutarti a capire come evitare il reindirizzamento a my-api.php/

Will Anderson Will Anderson
20 feb 2011 07:34:25

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

kuzey beytar kuzey beytar
11 mar 2017 19:43:15
0

Potrei non capire appieno le tue domande, ma un semplice shortcode potrebbe risolvere il tuo problema?

Passaggi:

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

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

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

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

20 feb 2011 10:13:21
0

Non ho lavorato molto con le rewrite, quindi probabilmente questa soluzione è un po' approssimativa, 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 colleghi questa funzione all'hook 'generate_rewrite_rules', ma ci deve essere un modo migliore, perché non vuoi riscrivere il file .htaccess ad ogni caricamento di pagina.
Sembra che non riesca a smettere di modificare i miei stessi post...probabilmente sarebbe meglio inserirlo nella callback di attivazione e fare riferimento a global $wp_rewrite. E poi rimuovere la voce da non_wp_rules e riscrivere nel file .htaccess nella callback di disattivazione.

Infine, la scrittura su .htaccess dovrebbe essere un po' più sofisticata, dovresti sostituire solo la sezione relativa a WordPress.

20 feb 2011 07:26:48
0

Ho avuto un requisito simile e volevo creare diversi endpoint basati su slug unici che puntassero a contenuti generati dal plugin.

Dai un'occhiata al codice sorgente del mio plugin: https://wordpress.org/extend/plugins/picasa-album-uploader/

La tecnica che ho utilizzato inizia aggiungendo un filtro per the_posts per esaminare la richiesta in ingresso. Se il plugin dovesse gestirla, viene generato un post fittizio e viene aggiunta un'azione per template_redirect.

Quando viene chiamata l'azione template_redirect, deve risultare nell'output dell'intero contenuto della pagina da visualizzare e terminare l'esecuzione, oppure deve ritornare senza generare alcun output. Guarda il codice in wp-include/template-loader.php e capirai il perché.

9 apr 2011 01:22:31
0

Sto utilizzando un altro approccio che consiste nel forzare la pagina principale a caricare un titolo personalizzato, contenuto e un modello di pagina personalizzato.

La soluzione è molto pulita poiché può essere implementata quando un utente segue un link amichevole come http://example.com/?plugin_page=myfakepage

È molto semplice da implementare e dovrebbe consentire un numero illimitato di pagine.

Codice e istruzioni qui: Genera una pagina Wordpress personalizzata/fittizia/virtuale al volo

18 gen 2012 13:50:34
0

Ecco un esempio pronto per la produzione, prima crea la classe per la pagina virtuale:


class VirtualPage
{

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

    function __construct($query = '/index2', $template = 'page', $title = 'Senza titolo')
    {
        $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;

        // Aggiorna la query principale
        $wp_query->current_post = $this->wp_post->ID;
        $wp_query->found_posts = 1;
        $wp_query->is_page = true; // parte importante
        $wp_query->is_singular = true; // parte importante
        $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); // aggiungi numero casuale per evitare conflitti
            $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'azione template_redirect e gestisci la tua pagina virtuale come mostrato di seguito

    add_action( 'template_redirect', function () {


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

                        case 'contact':
                            // http://tuosito/contact  ==> carica page-contact.php
                            $page = new VirtualPage( "/contact", 'contact',__('Contattami') );
                            $page->createPage();
                            break;

                        case 'archive':
                            // http://tuosito/archive  ==> carica page-archive.php
                            $page = new VirtualPage( "/archive", 'archive' ,__('Archivi'));
                            $page->createPage();
                            break;

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


                }


            } );
12 lug 2019 11:29:35
0

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

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

La parte chiave del mio approccio è utilizzare il template esistente in modo che la pagina risultante appaia come parte del sito; volevo che fosse il più compatibile possibile con tutti i temi, si spera attraverso le diverse versioni di WordPress. Il tempo dirà se avevo ragione!

1 mar 2014 07:39:01