Die Entwicklung von WordPress von einer Blogging-Plattform zu einem vollwertigen CMS macht es gleichzeitig zu einem soliden Framework für Entwickler, um daraus herausragende Projekte und Anwendungen zu erstellen.

Der WordPress-Kern unterstützt nicht nur die Veröffentlichungs-Engine der Benutzer, sondern bietet Entwicklern auch eine Reihe robuster Klassen, APIs und Helfer, die auf eine Vielzahl von Anforderungen zugeschnitten sind.

Eine der versteckten Juwelen von WordPress, die es Entwicklern ermöglicht, Operationen mit dem lokalen Dateisystem auf eine sichere und robuste Weise durchzuführen, ist die WordPress Dateisystem-API. Es abstrahiert die Dateimanipulationsfunktionalität in eine Reihe häufig angeforderter Methoden, sodass sie in verschiedenen Hosting-Umgebungen sicher verwendet werden können.

Der Umfang des Problems

Es könnte mehrere Gründe geben, lokale Dateien in Code schreiben zu wollen:

  • Protokollierung von durchgeführten Ereignissen oder Vorgängen
  • Datenaustausch mit anderen Systemen als WordPress
  • Backup

Unabhängig von den Beweggründen kann das Schreiben lokaler Dateien aus PHP-Code eine riskante Operation sein. Bei der Implementierung für ein WordPress Theme, Plugin oder eine benutzerdefinierte Installation sollten mindestens zwei sehr wichtige Fallstricke berücksichtigt werden:

  1. Sicherheit. Beim Schreiben lokaler Dateien mit Code (vom Webserver) besteht das Risiko eines fehlerhaften Dateibesitzes. Dieses Problem tritt in schlecht konfigurierten Shared Hosting-Umgebungen auf und kann zum Verlust der Kontrolle über Dateien führen.
  2. Kompatibilität. Aufgrund der Vielzahl von Hosting-Unternehmen ist die Serverkonfiguration des jeweiligen Benutzers dem Entwickler normalerweise unbekannt. Daher kann der Entwickler nicht sicher sein, dass Berechtigungen, die für eine Schreiboperation erforderlich sind, vom Benutzer des Plugins oder Themas erreicht werden können.

Wenn ein WordPress-Plugin oder -Thema, das lokale Dateien schreiben soll, zur Veröffentlichung freigegeben ist, sollte der Entwickler diese Probleme ständig berücksichtigen. Die gute Nachricht ist, dass WordPress selbst bereits ein Tool zur Behebung dieser Probleme hat: die Dateisystem-API.

Einführung in die WordPress-Dateisystem-API

Die Dateisystem-API wurde WordPress in Version 2.6 hinzugefügt, um die eigene Update-Funktion von WordPress zu aktivieren. Es abstrahiert die Funktionalität, die zum sicheren Ausführen von Lese- / Schreibvorgängen auf einer Vielzahl von Hosttypen erforderlich ist. Es besteht aus einer Reihe von Klassen und ermöglicht Ihnen die automatische Auswahl der richtigen Verbindung zum lokalen Dateisystem, abhängig von der individuellen Host-Konfiguration.

Die Logik hinter der API ist ziemlich einfach; Es wird versucht, lokale Dateien direkt zu schreiben, und im Falle eines fehlerhaften Dateibesitzes wechselt es zu einer anderen FTP-basierten Methode. Abhängig von den verfügbaren PHP-Bibliotheken findet es eine geeignete Möglichkeit, eine FTP-Verbindung einzurichten (über Erweiterungs-Sockets oder über-SSH). Im Allgemeinen sind die folgenden Schritte erforderlich, um mit lokalen Dateien zu arbeiten:

Schritt 1. Ermitteln Sie, welche Verbindungsmethode verfügbar ist

WordPress verwendet die Methode get_filesystem_method, um die Verfügbarkeit der folgenden Methoden zu ermitteln (von der höchsten Priorität bis zum niedrigsten) Direct, SSH2, FTP PHP Erweiterung, FTP Sockets.

Schritt 2. Erwerben Sie die für die erkannte Methode erforderlichen Anmeldeinformationen

Wenn der erkannte Transport Anmeldeinformationen von einem Benutzer benötigt, verwendet WordPress die Funktion request_filesystem_credentials, um ein Anfrageformular anzuzeigen. Die Funktion verfügt über eine Reihe von Parametern, die es ermöglichen, Daten zwischen Formulareinreichungen zu bewahren, bei fehlgeschlagener Verbindung mehrmals nach Anmeldedaten zu fragen und auf ein bestimmtes Verzeichnis innerhalb der WordPress-Installation zu zielen:

request_filesystem_credentials($form_post, $type, $error, $context, $extra_fields);

Durch die Angabe eines leeren $ type-Parameters für die Funktion könnten wir die Erkennung der verfügbaren Verbindungsmethoden erzwingen, so dass wir die Methode get_filesystem_method für uns aufrufen würden. Gleichzeitig können wir die Funktion zwingen, einen bestimmten Verbindungstyp zu verwenden, indem Sie sie mit dem Argument $ type angeben.

Wenn die von der gewählten Methode benötigten Verbindungsdaten nicht bereitgestellt werden, druckt die Funktion das Formular, um es anzufordern:

Conneciton information

Nach der ersten Anfrage speichert WordPress den FTP-Hostnamen und den Benutzernamen in der Datenbank für die zukünftige Verwendung, aber es speichert das Passwort nicht. Alternativ können FTP-Anmeldedaten in der Datei wp-config.php mit den folgenden Konstanten angegeben werden:

  • FTP_HOST - Der Hostname des Servers, mit dem eine Verbindung hergestellt werden soll
  • FTP_USER - Der Benutzername, mit dem Sie sich verbinden
  • FTP_PASS - das Passwort für die Verbindung
  • FTP_PUBKEY - Der Pfad zum öffentlichen Schlüssel, der für die SSH2-Verbindung verwendet werden soll
  • FTP_PRIKEY - Der Pfad zum privaten Schlüssel, der für die SSH2-Verbindung verwendet werden soll

Wenn diese Daten in der Datei wp-config.php gespeichert sind, erscheint das Anmeldeformular nicht, aber die Sicherheitsnachteile sind signifikant und Sicherheitsverfahren sollten dreifach überprüft werden, wobei der Sicherheit dieser Datei die größtmögliche Aufmerksamkeit gewidmet werden sollte.

Schritt 3. Initialisieren Sie die WordPress-Dateisystem-Klasse und stellen Sie eine Verbindung zum Dateisystem her

Das Herz der WordPress-Dateisystem-API ist die WP_Filesystem-Funktion. Es lädt und initialisiert die entsprechende Transportklasse, speichert eine abgerufene Instanz im globalen $ wp_filesystem-Objekt für die weitere Verwendung und versucht, eine Verbindung zum Dateisystem mit den angegebenen Anmeldeinformationen herzustellen:

WP_Filesystem($args, $context);

Schritt 4. Verwenden Sie die WordPress-Dateisystemmethoden zum Ausführen von Lese- / Schreibvorgängen

Ein ordnungsgemäß initialisiertes $ wp_filesystem-Objekt verfügt über eine Reihe von Methoden zur Kommunikation mit dem lokalen Dateisystem, die ohne weitere Ängste bezüglich des Verbindungstyps verwendet werden können. Insbesondere gibt es folgende allgemein gebräuchliche Methoden:

  • get_contents - liest die Datei in eine Zeichenfolge
  • put_contents - schreibt eine Zeichenkette in eine Datei
  • mkdir - erstellt ein Verzeichnis
  • mdir - Entfernt ein Verzeichnis
  • wp_content_dir - gibt den Pfad im lokalen Dateisystem zum Ordner wp-content zurück
  • wp_plugins_dir - gibt den Pfad auf dem lokalen Dateisystem zum Plugins-Ordner zurück
  • wp_themes_dir - gibt den Pfad im lokalen Dateisystem zum Ordner themes zurück

Lassen Sie uns ein Beispiel geben, das die oben genannten Schritte in einer einfachen Situation ausführt: Wir schreiben einen Text, der in einem Textfeld eingereicht wurde, in eine einfache .txt-Datei.

Beachten Sie, dass dieses Beispiel Demonstrationszwecken dient. In einer realen Situation, in der Sie keine einfachen Textdaten in einer TXT-Datei speichern würden, wäre es eine weitaus robustere Lösung, sie stattdessen in der Datenbank zu speichern.

Die WordPress-Dateisystem-API in Aktion

Lassen Sie uns unseren Code in ein separates Plugin einbinden, dem ein eigener Dateisystem-Demo-Ordner zugewiesen wird. Das liefert uns einen Zielordner zum Speichern der TXT-Datei und prüft die Schreibberechtigungen.

Zunächst erstellen wir die Demo-Seite, um unser Formular im Menü Extras anzuzeigen:

/*** 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 ($ ausgabe)) {$error = $output->get_error_message();$output = '';}  } else {// Aus Datei lesen (false === ($ output = filesystem_demo_text_read ($ form_url))) {return;  // wir zeigen die Anmeldeinformationen an, ohne weitere Verarbeitung zu benötigen} elseif (is_wp_error ($ output)) {$error = $output->get_error_message();$output = '';}  } $ output = esc_textarea ($ output);  // zum Drucken entkommen?> 

Dateisystem-API-Demoseite

Bei der Anzeige unserer Seite (filesystem_demo_screen) prüfen wir die Verfügbarkeit der Texteingabe. Wenn es existiert, versuchen wir, es in eine test.txt Datei zu schreiben, ansonsten versuchen wir, eine solche Datei im Plugin-Ordner zu finden und deren Inhalt zu lesen, um in textarea aufgenommen zu werden. Schließlich drucken wir ein Basisformular zur Eingabe von Text. Aus Gründen der Lesbarkeit wurden diese Schreib- und Lesevorgänge in eigene Funktionen aufgeteilt.

Filesystem API demo

Um eine Wiederholung der gleichen Initialisierungsschritte zu vermeiden, wurde der freigegebene Helfer erstellt. Es ruft zunächst request_filesystem_credentials auf, um die verfügbare Verbindungsmethode zu ermitteln und Anmeldeinformationen zu erhalten. Wenn dies erfolgreich war, ruft es dann WP_Filesystem auf, um $ wp_filesystem mit gegebenen Daten zu initiieren.

/*** 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}

Das Schreiben in den Dateicode sieht folgendermaßen aus:

/*** 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 diesem Teil haben wir einige notwendige Parameter definiert:

  • $ demotext - gesendeter Text zum Schreiben
  • $ form_fields - Element im Array $ _POST, das unseren Text speichert und beibehalten werden soll
  • $ method - transport method, lassen wir es leer, um automatisch zu erkennen
  • $ context - Zielordner (der des Plugins)

Danach haben wir das globale $ wp_filesystem-Objekt mit der zuvor beschriebenen Hilfsfunktion gestartet. Im Erfolgsfall erkennen wir den korrekten Pfad zum Zielordner und schreiben den übermittelten Text mit der Methode put_contents des $ wp_filesystem-Objekts hinein.

Der Code zum Lesen aus der Datei sieht folgendermaßen aus:

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

Diese Funktion funktioniert genauso wie zuvor beschrieben, verwendet aber get_contents, um aus der Zieldatei zu lesen.

Fazit

Bei der Arbeit mit lokalen Dateien kommt ein WordPress-Theme- oder Plugins-Entwickler mit Sicherheits- und Kompatibilitätsproblemen in Kontakt, was das Team enorm belasten und den Projektlebenszyklus um viele Stunden verlängern wird. Wenn Sie sich auf die Dateisystem-API verlassen, können diese Probleme effizient umgangen werden. Wenn Sie also das nächste Mal den Code Ihres Plugins schreiben, sollten Sie diese Alternative als die gesündere Option betrachten.

Sie können Laden Sie eine Demo dieses Codes hier herunter und passen Sie es Ihren Bedürfnissen an.