L'evoluzione di WordPress da una piattaforma di blogging in un CMS completo, la trasforma simultaneamente in una solida struttura per gli sviluppatori per creare progetti e applicazioni eccezionali.
Il core di WordPress, alimenta non solo il motore di pubblicazione degli utenti, ma fornisce anche agli sviluppatori un set di classi, API e helper robusti, progettati per soddisfare un'ampia gamma di esigenze.
Una delle gemme nascoste di WordPress che consente agli sviluppatori di eseguire operazioni con il file system locale in modo sicuro e affidabile è l'API di WordPress Filesystem. Estrae la funzionalità di manipolazione dei file in una serie di metodi comunemente richiesti in modo che possano essere utilizzati in modo sicuro in diversi ambienti di hosting.
Potrebbero esserci diversi motivi per voler scrivere file locali nel codice:
Indipendentemente dalle motivazioni, la scrittura di file locali dal codice PHP può essere un'operazione rischiosa. Almeno due trappole molto importanti dovrebbero essere prese in considerazione quando si implementa questo per un tema WordPress, plugin o installazione personalizzata:
Se un plug-in WordPress o un tema che richiede di scrivere file locali è destinato alla pubblicazione pubblica, lo sviluppatore dovrebbe tenere costantemente a mente questi problemi. La buona notizia è che WordPress stesso ha già uno strumento per risolvere questi problemi: l'API Filesystem.
L'API Filesystem è stata aggiunta a WordPress nella versione 2.6 per abilitare la funzionalità di aggiornamento di WordPress. Estrae le funzionalità necessarie per eseguire operazioni di lettura / scrittura in modo sicuro e su una varietà di tipi di host. Consiste in un insieme di classi e consente di scegliere automaticamente il modo corretto di connettersi al file system locale, in base alla configurazione del singolo host.
La logica dietro l'API è abbastanza semplice; prova a scrivere direttamente i file locali e, in caso di proprietà errata dei file, passa a un altro metodo basato su FTP. A seconda delle librerie PHP disponibili, trova un modo appropriato per configurare una connessione FTP (tramite socket di estensione o over-SSH). In genere, per lavorare con i file locali sono necessari i seguenti passaggi:
WordPress utilizza get_filesystem_method per rilevare la disponibilità dei seguenti metodi (dalla priorità più alta al più basso) Diretto, SSH2, FTP PHP Extension, FTP Sockets.
Se il trasporto rilevato ha bisogno di credenziali da un utente, WordPress utilizza la funzione request_filesystem_credentials per visualizzare un modulo di richiesta. La funzione ha un certo numero di parametri che gli consentono di conservare i dati tra le richieste di moduli, chiedere le credenziali più volte se la connessione fallisce e scegliere una determinata directory all'interno dell'installazione di WordPress:
request_filesystem_credentials($form_post, $type, $error, $context, $extra_fields);
Fornendo un parametro $ type vuoto alla funzione potremmo costringerlo a eseguire il rilevamento dei metodi di connessione disponibili, quindi chiamerebbe get_filesystem_method per noi. Allo stesso tempo, possiamo forzare la funzione ad usare qualsiasi particolare tipo di connessione specificandolo usando l'argomento $ type.
Quando non vengono forniti i dati di connessione richiesti dal metodo scelto, la funzione stampa il modulo per richiederlo:
Dopo la prima richiesta, WordPress memorizza il nome host FTP e il nome utente nel database per uso futuro, ma non memorizza la password. In alternativa, le credenziali FTP possono essere specificate nel file wp-config.php utilizzando le seguenti costanti:
FTP_HOST
- il nome host del server a cui connettersi FTP_USER
- lo username con cui connettersi FTP_PASS
- la password per connettersi FTP_PUBKEY
- il percorso della chiave pubblica da utilizzare per la connessione SSH2 FTP_PRIKEY
- il percorso della chiave privata da utilizzare per la connessione SSH2 Quando questi dati sono archiviati nel file wp-config.php, il modulo di richiesta delle credenziali non viene visualizzato, ma i limiti di sicurezza sono significativi e le procedure di sicurezza dovrebbero essere verificate triplicando la massima attenzione possibile alla sicurezza di questo file.
Il cuore dell'API del file system WordPress è la funzione WP_Filesystem. Carica e inizializza la classe di trasporto appropriata, memorizza un'istanza ottenuta nell'oggetto $ wp_filesystem globale per ulteriore utilizzo e tenta di connettersi al filesystem con le credenziali fornite:
WP_Filesystem($args, $context);
Un oggetto $ wp_filesystem correttamente inizializzato ha una serie di metodi per comunicare con il file system locale che potrebbero essere utilizzati senza ulteriore preoccupazione per il tipo di connessione. In particolare, ci sono i seguenti metodi comunemente usati:
Mettendo tutto insieme, facciamo un esempio che compie i passaggi sopra menzionati in una situazione semplice: scriveremo un testo inviato in una textarea in un semplice file .txt.
Si noti che questo esempio è a scopo dimostrativo, in una situazione reale non si memorizzerebbero dati di testo semplici in un file .txt, sarebbe invece una soluzione molto più robusta per archiviarlo nel database.
Inseriamo il nostro codice in un plug-in separato, a cui verrà assegnata la propria cartella demo-filesystem. Questo ci fornisce la cartella di destinazione per memorizzare il file .txt e controllare i permessi di scrittura.
Prima di tutto, creiamo la pagina demo per visualizzare il nostro modulo sotto il menu Strumenti:
/*** Create Demo page (under Tools menu)***/add_action('admin_menu', 'filesystem_demo_page');function filesystem_demo_page() {add_submenu_page( 'tools.php', 'Filesystem API Demo page', 'Filesystem Demo', 'upload_files', 'filesystem_demo', 'filesystem_demo_screen' );}function filesystem_demo_screen() {$form_url = "tools.php?page=filesystem_demo";$output = $error = '';/*** write submitted text into file (if any)* or read the text from file - if there is no submission**/if(isset($_POST['demotext'])){//new submissionif(false === ($output = filesystem_demo_text_write($form_url))){return; //we are displaying credentials form - no need for further processing} elseif (is_wp_error ($ output)) {$error = $output->get_error_message();$output = '';} } else {// read from fileif (false === ($ output = filesystem_demo_text_read ($ form_url))) {return; // stiamo visualizzando le credenziali senza necessità di ulteriori elaborazioni} elseif (is_wp_error ($ output)) {$error = $output->get_error_message();$output = '';} } $ output = esc_textarea ($ output); // escaping per la stampa?> Pagina Demo API Filesystem
Quando visualizziamo la nostra pagina (filesystem_demo_screen) controlliamo la disponibilità di invio di testo. Se esiste, proviamo a scriverlo in un file test.txt, altrimenti proviamo a trovare un tale file nella cartella dei plugin e leggiamo il suo contenuto da includere in textarea. Finalmente stampiamo un modulo base per inserire il testo. Per ragioni di leggibilità, queste operazioni di scrittura e lettura sono state separate nelle loro funzioni.
Per evitare la duplicazione degli stessi passi di inizializzazione è stato creato l'helper condiviso. Chiama prima request_filesystem_credentials per rilevare il metodo di connessione disponibile e ottenere le credenziali. Se questo ha avuto successo, chiama WP_Filesystem per avviare $ wp_filesystem con i dati forniti.
/*** Initialize Filesystem object** @param str $form_url - URL of the page to display request form* @param str $method - connection method* @param str $context - destination folder* @param array $fields - fileds of $_POST array that should be preserved between screens* @return bool/str - false on failure, stored text on success**/function filesystem_init($form_url, $method, $context, $fields = null) {global $wp_filesystem;/* first attempt to get credentials */if (false === ($creds = request_filesystem_credentials($form_url, $method, false, $context, $fields))) {/*** if we comes here - we don't have credentials* so the request for them is displaying* no need for further processing**/return false;}/* now we got some credentials - try to use them*/if (!WP_Filesystem($creds)) {/* incorrect connection data - ask for credentials again, now with error message */request_filesystem_credentials($form_url, $method, true, $context);return false;}return true; //filesystem object successfully initiated}
La scrittura sul codice file ha il seguente aspetto:
/*** Perform writing into file** @param str $form_url - URL of the page to display request form* @return bool/str - false on failure, stored text on success**/function filesystem_demo_text_write($form_url){global $wp_filesystem;check_admin_referer('filesystem_demo_screen');$demotext = sanitize_text_field($_POST['demotext']); //sanitize the input$form_fields = array('demotext'); //fields that should be preserved across screens$method = ''; //leave this empty to perform test for 'direct' writing$context = WP_PLUGIN_DIR . '/filesystem-demo'; //target folder$form_url = wp_nonce_url($form_url, 'filesystem_demo_screen'); //page url with nonce valueif(!filesystem_init($form_url, $method, $context, $form_fields))return false; //stop further processign when request form is displaying/** now $wp_filesystem could be used* get correct target file first**/$target_dir = $wp_filesystem->find_folder($context);$target_file = trailingslashit($target_dir).'test.txt';/* write into file */if(!$wp_filesystem->put_contents($target_file, $demotext, FS_CHMOD_FILE))return new WP_Error('writing_error', 'Error when writing file'); //return error objectreturn $demotext;}
In questa parte abbiamo definito alcuni parametri necessari:
Successivamente è stato avviato l'oggetto $ wp_filesystem globale utilizzando la funzione helper che ho descritto in precedenza. In caso di successo rileviamo il percorso corretto per la cartella di destinazione e scriviamo il testo inviato usando il metodo put_contents dell'oggetto $ wp_filesystem.
Il codice per leggere dal file è simile al seguente:
/*** Read text from file** @param str $form_url - URL of the page where request form will be displayed* @return bool/str - false on failure, stored text on success**/function filesystem_demo_text_read($form_url){global $wp_filesystem;$demotext = '';$form_url = wp_nonce_url($form_url, 'filesystem_demo_screen');$method = ''; //leave this empty to perform test for 'direct' writing$context = WP_PLUGIN_DIR . '/filesystem-demo'; //target folderif(!filesystem_init($form_url, $method, $context))return false; //stop further processing when request forms displaying/** now $wp_filesystem could be used* get correct target file first**/$target_dir = $wp_filesystem->find_folder($context);$target_file = trailingslashit($target_dir).'test.txt';/* read the file */if($wp_filesystem->exists($target_file)){ //check for existence$demotext = $wp_filesystem->get_contents($target_file);if(!$demotext)return new WP_Error('reading_error', 'Error when reading file'); //return error object}return $demotext;}
Questa funzione funziona nello stesso modo descritto in precedenza, ma utilizza get_contents per leggere dal file di destinazione.
Quando si lavora con i file locali, uno sviluppatore di temi o plug-in di WordPress entrerà in contatto con problemi di sicurezza e compatibilità, mettendo enorme stress sul team e aggiungendo lunghe ore al ciclo di vita del progetto. Facendo affidamento sull'API di Filesystem, questi problemi possono essere affrontati in modo efficiente. Quindi la prossima volta che ti ritrovi a scrivere fwrite nel codice del tuo plugin, considera questa alternativa l'opzione più salutare.
Puoi scarica qui una demo di questo codice e adattarlo alle tue esigenze.