De evolutie van WordPress van een blogplatform naar een volwaardig CMS, maakt tegelijkertijd een solide raamwerk voor ontwikkelaars om uitstekende projecten en applicaties op te bouwen.

De kern van WordPress drijft niet alleen de publishing-engine van de gebruiker aan, maar biedt ontwikkelaars ook een robuuste set klassen, API's en helpers, ontworpen om aan een breed scala aan behoeften tegemoet te komen.

Een van de verborgen pareltjes van WordPress waarmee ontwikkelaars op een veilige en krachtige manier bewerkingen met het lokale bestandssysteem kunnen uitvoeren, is de WordPress Filesystem API. Het vat de functionaliteit voor bestandsmanipulatie samen in een set veelgebruikte methoden, zodat ze veilig kunnen worden gebruikt in verschillende hostingomgevingen.

De omvang van het probleem

Er kunnen verschillende redenen zijn om lokale bestanden in code te willen schrijven:

  • Logging van gebeurtenissen of verrichte operaties
  • Gegevensuitwisseling met niet-WordPress-systemen
  • backup

Ongeacht de beweegredenen kan het schrijven van lokale bestanden uit PHP-code een riskante handeling zijn. Bij het implementeren van deze voor een WordPress-thema, plug-in of aangepaste installatie, moet ten minste rekening worden gehouden met twee zeer belangrijke valkuilen:

  1. Veiligheid. Er bestaat een risico op onjuiste bestandseigenschappen bij het schrijven van lokale bestanden met code (door de webserver). Dit probleem doet zich voor in slecht geconfigureerde shared hosting-omgevingen en kan leiden tot verlies van controle over bestanden.
  2. Verenigbaarheid. Vanwege de verscheidenheid aan hostingbedrijven die er zijn, is de serverconfiguratie van de specifieke gebruiker meestal onbekend voor de ontwikkelaar. De ontwikkelaar kan er dus niet zeker van zijn dat toestemmingen die vereist zijn voor een schrijfbewerking, haalbaar zijn voor de gebruiker van de plug-in of het thema.

Als een WordPress-plug-in of -thema dat lokale bestanden moet schrijven, is bedoeld voor openbare publicatie, moet de ontwikkelaar voortdurend rekening houden met deze problemen. Het goede nieuws is dat WordPress zelf al een tool heeft om deze problemen aan te pakken: de Filesystem API.

Inleiding tot de WordPress Filesystem API

De Filesystem API is toegevoegd aan WordPress in versie 2.6 om de eigen updatefunctie van WordPress in te schakelen. Het vat de functionaliteit samen die nodig is om lees- / schrijfbewerkingen veilig uit te voeren en op een verscheidenheid aan hosttypen. Het bestaat uit een reeks klassen en biedt u de mogelijkheid om automatisch de juiste manier te kiezen om verbinding te maken met het lokale bestandssysteem, afhankelijk van de individuele hostinstelling.

De logica achter de API is vrij eenvoudig; het probeert lokale bestanden rechtstreeks te schrijven en in het geval van onjuiste bestandseigenschappen schakelt het over naar een andere op FTP gebaseerde methode. Afhankelijk van de beschikbare PHP-bibliotheken, vindt het een geschikte manier om een ​​FTP-verbinding tot stand te brengen (via uitbreidingscontactdozen of over-SSH). Over het algemeen zijn de volgende stappen vereist om met lokale bestanden te werken:

Stap 1. Detecteer welke verbindingsmethode beschikbaar is

WordPress gebruikt de get_filesystem_method om de beschikbaarheid van de volgende methoden te detecteren (van de hoogste tot de laagste) Direct, SSH2, FTP PHP Extension, FTP Sockets.

Stap 2. Verkrijg de referenties die vereist zijn voor de gedetecteerde methode

Als het gedetecteerde transport referenties van een gebruiker nodig heeft, gebruikt WordPress de functie request_filesystem_credentials om een ​​aanvraagformulier weer te geven. De functie heeft een aantal parameters die het mogelijk maken om gegevens te bewaren tussen het indienen van formulieren, meerdere keren om referenties te vragen als de verbinding is mislukt en het doelwit naar een bepaalde map binnen de WordPress-installatie:

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

Door een lege parameter van $ type aan de functie toe te voegen, kunnen we deze dwingen om detectie van de beschikbare verbindingsmethoden uit te voeren, dus zou dit de methode get_filesystem_od voor ons oproepen. Tegelijkertijd kunnen we de functie forceren om een ​​bepaald verbindingstype te gebruiken door het op te geven met het argument $ type.

Wanneer de verbindingsgegevens vereist door de gekozen methode niet zijn opgegeven, drukt de functie het formulier af om dit aan te vragen:

Conneciton information

Na het eerste verzoek slaat WordPress de FTP-hostnaam en gebruikersnaam op in de database voor toekomstig gebruik, maar het slaat het wachtwoord niet op. Als alternatief kunnen FTP-referenties worden opgegeven in het bestand wp-config.php met behulp van de volgende constanten:

  • FTP_HOST - de hostnaam van de server waarmee verbinding moet worden gemaakt
  • FTP_USER - de gebruikersnaam om verbinding mee te maken
  • FTP_PASS - het wachtwoord om verbinding mee te maken
  • FTP_PUBKEY - het pad naar de openbare sleutel die moet worden gebruikt voor de SSH2-verbinding
  • FTP_PRIKEY - het pad naar de privésleutel om te gebruiken voor de SSH2-verbinding

Wanneer deze gegevens worden opgeslagen in het bestand wp-config.php, wordt het aanmeldingsformulier voor referenties niet weergegeven, maar de beveiligingsnadeelen zijn aanzienlijk en de veiligheidsprocedures moeten driemaal worden gecontroleerd en er moet zoveel mogelijk aandacht worden besteed aan de beveiliging van dit bestand.

Stap 3. Initialiseer de WordPress Filesystem-klasse en maak verbinding met het bestandssysteem

Het hart van de WordPress Filesystem API is de WP_Filesystem-functie. Het laadt en initialiseert de juiste transportklasse, slaat een verkregen exemplaar op in het globale $ wp_filesystem-object voor verder gebruik en probeert verbinding te maken met het bestandssysteem met de opgegeven inloggegevens:

WP_Filesystem($args, $context);

Stap 4. Gebruik de WordPress Filesystem-methoden om lees- / schrijfbewerkingen uit te voeren

Een correct geïnitialiseerd $ wp_filesystem-object heeft een set methoden om te communiceren met het lokale bestandssysteem dat kan worden gebruikt zonder verdere zorgen over het verbindingstype. In het bijzonder zijn er de volgende veelgebruikte methoden:

  • get_contents - leest het bestand in een string
  • put_contents - schrijft een tekenreeks naar een bestand
  • mkdir - maakt een map
  • mdir - verwijdert een map
  • wp_content_dir - geeft het pad op het lokale bestandssysteem terug naar de map wp-content
  • wp_plugins_dir - geeft het pad op het lokale bestandssysteem terug naar de map met plug-ins
  • wp_themes_dir - geeft het pad op het lokale bestandssysteem terug naar de themamap

Laten we het allemaal bij elkaar brengen, laten we een voorbeeld bedenken dat de bovengenoemde stappen in een eenvoudige situatie uitvoert - we zullen een aantal tekst die in een tekstgebied is ingediend, in een gewoon .txt-bestand schrijven.

Merk op dat dit voorbeeld bedoeld is voor demonstratiedoeleinden, in een reële situatie waarin u geen eenvoudige tekstgegevens opslaat in een .txt-bestand, zou het een veel robuustere oplossing zijn om het in de database op te slaan.

De WordPress Filesystem-API in actie

Laten we onze code inpakken in een aparte plug-in, die zijn eigen bestandssysteem-demomap krijgt toegewezen. Dat biedt ons een doelmap om het .txt-bestand op te slaan en de schrijfrechten te controleren.

Laten we eerst de demopagina maken om ons formulier onder het menu Tools weer te geven:

/*** 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))) {ga terug;  // we geven inloggegevens weer zonder verdere bewerking} elseif (is_wp_error ($ output)) {$error = $output->get_error_message();$output = '';}  } $ output = esc_textarea ($ output);  // Ontsnappen voor afdrukken?> 

Filesystem API Demo-pagina

Bij het weergeven van onze pagina (filesystem_demo_screen) controleren we of de tekst ingeleverd kan worden. Als dit bestaat, proberen we het in een test.txt-bestand te schrijven, anders proberen we een dergelijk bestand in de map van de plug-in te vinden en de inhoud ervan in het tekstgebied te lezen. Als laatste drukken we een basisformulier naar de invoertekst. Omwille van de leesbaarheid werden deze schrijf- en leesbewerkingen gescheiden in hun eigen functies.

Filesystem API demo

Om duplicatie van dezelfde initialisatiestappen te voorkomen, is de gedeelde helper gemaakt. Het roept eerst request_filesystem_credentials aan om de beschikbare verbindingsmethode te detecteren en referenties te verkrijgen. Als dat lukte, roept het vervolgens WP_Filesystem aan om $ wp_filesystem met gegeven gegevens te initiëren.

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

Het schrijven naar bestandscode ziet er als volgt uit:

/*** 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 dit deel hebben we enkele noodzakelijke parameters gedefinieerd:

  • $ demotext - ingestuurde tekst om te schrijven
  • $ form_fields - item in de array $ _POST die onze tekst opslaat en moet worden bewaard
  • $ methode - transportmethode, we laten het leeg om het automatisch te detecteren
  • $ context - doelmap (de plug-in)

Daarna hebben we het globale $ wp_filesystem-object gestart met behulp van de helperfunctie die ik eerder heb beschreven. In het geval van succes detecteren we het juiste pad naar de doelmap en schrijven we de ingediende tekst erin met de methode put_contents van het object $ wp_filesystem.

De code voor het lezen van het bestand ziet er als volgt uit:

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

Deze functie werkt op dezelfde manier als eerder beschreven, maar gebruikt get_contents om te lezen vanuit het doelbestand.

Conclusie

Bij het werken met lokale bestanden komen WordPress-thema's of ontwikkelaars van plug-ins in contact met beveiligings- en compatibiliteitsproblemen, waardoor het team enorm wordt belast en lange uren aan de levenscyclus van het project worden toegevoegd. Door te vertrouwen op de Filesystem API kunnen deze problemen op een efficiënte manier worden genegeerd. Dus de volgende keer dat u merkt dat u fwrite in de code van uw plug-in schrijft, beschouw dit alternatief dan de gezondere optie.

Jij kan download hier een demo van deze code en pas het aan uw behoeften aan.