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.

La portata del problema

Potrebbero esserci diversi motivi per voler scrivere file locali nel codice:

  • Registrazione di eventi o operazioni eseguite
  • Scambio di dati con sistemi alimentati senza WordPress
  • di riserva

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:

  1. Sicurezza. Esiste il rischio di errata proprietà dei file durante la scrittura di file locali con codice (dal server web). Questo problema si verifica in ambienti di hosting condiviso mal configurati e potrebbe portare alla perdita di controllo sui file.
  2. Compatibilità. A causa della varietà di società di hosting disponibili, la configurazione del server dell'utente in genere è sconosciuta allo sviluppatore. Pertanto, lo sviluppatore non può essere sicuro che le autorizzazioni richieste per un'operazione di scrittura siano raggiungibili dall'utente del plugin o del tema.

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.

Introduzione all'API del file system WordPress

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:

Passaggio 1. Rileva il metodo di connessione disponibile

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.

Passaggio 2. Ottenere le credenziali richieste per il metodo rilevato

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:

Conneciton information

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.

Passaggio 3. Inizializzare la classe del file system WordPress e connettersi al file system

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

Passaggio 4. Utilizzare i metodi di WordPress Filesystem per eseguire operazioni di lettura / scrittura

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:

  • get_contents - legge il file in una stringa
  • put_contents: scrive una stringa in un file
  • mkdir - crea una directory
  • mdir - rimuove una directory
  • wp_content_dir - restituisce il percorso sul file system locale alla cartella del contenuto wp
  • wp_plugins_dir - restituisce il percorso sul file system locale nella cartella plugins
  • wp_themes_dir - restituisce il percorso sul file system locale nella cartella dei temi

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.

L'API di WordPress Filesystem in azione

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.

Filesystem API demo

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:

  • $ demotext - testo inviato da scrivere
  • $ form_fields - elemento nell'array $ _POST che memorizza il nostro testo e deve essere conservato
  • $ metodo - metodo di trasporto, lo lasciamo in bianco per rilevare automaticamente
  • $ context - cartella di destinazione (quella del plugin)

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.

Conclusione

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.