L'évolution de WordPress d'une plateforme de blogs à un système de gestion de contenu à part entière en fait simultanément un cadre solide permettant aux développeurs de créer des projets et des applications exceptionnels.

Le noyau WordPress alimente non seulement le moteur de publication des utilisateurs, mais fournit également aux développeurs un ensemble robuste de classes, d’API et d’aides conçus pour répondre à un large éventail de besoins.

L'un des joyaux cachés de WordPress qui permet aux développeurs d'effectuer des opérations avec le système de fichiers local de manière sécurisée et robuste est l'API WordPress Filesystem. Il résume les fonctionnalités de manipulation de fichiers dans un ensemble de méthodes fréquemment demandées afin qu'elles puissent être utilisées de manière sécurisée dans différents environnements d'hébergement.

L'étendue du problème

Il pourrait y avoir plusieurs raisons pour vouloir écrire des fichiers locaux dans le code:

  • Enregistrement des événements ou des opérations effectuées
  • Échange de données avec des systèmes non WordPress
  • Sauvegarde

Indépendamment des motivations, l’écriture de fichiers locaux à partir de code PHP peut être une opération risquée. Au moins deux pièges très importants doivent être pris en compte lors de la mise en œuvre de ce thème pour un thème WordPress, un plug-in ou une installation personnalisée:

  1. Sécurité. Il existe un risque d’appropriation incorrecte des fichiers lors de l’écriture de fichiers locaux avec du code (par le serveur Web). Ce problème se pose dans des environnements d'hébergement partagés mal configurés et pourrait entraîner une perte de contrôle des fichiers.
  2. Compatibilité. En raison de la diversité des sociétés d’hébergement, la configuration du serveur de l’utilisateur est généralement inconnue du développeur. Ainsi, le développeur ne peut pas être sûr que les autorisations requises pour une opération d'écriture sont réalisables par l'utilisateur du plug-in ou du thème.

Si un plugin ou un thème WordPress devant écrire des fichiers locaux est destiné à une publication publique, le développeur doit constamment garder à l'esprit ces problèmes. La bonne nouvelle est que WordPress lui-même dispose déjà d'un outil pour résoudre ces problèmes: l'API Filesystem.

Introduction à l'API WordPress Filesystem

L'API Filesystem a été ajoutée à WordPress dans la version 2.6 pour activer la fonctionnalité de mise à jour de WordPress. Il résume les fonctionnalités nécessaires pour effectuer des opérations de lecture / écriture en toute sécurité et sur divers types d’hôtes. Il consiste en un ensemble de classes et vous permet de choisir automatiquement la méthode de connexion appropriée au système de fichiers local, en fonction de la configuration de chaque hôte.

La logique derrière l'API est assez simple. il essaie d'écrire des fichiers locaux directement et en cas de propriété de fichier incorrecte, il bascule vers une autre méthode basée sur FTP. Selon les bibliothèques PHP disponibles, il trouve un moyen approprié de configurer une connexion FTP (via des sockets d'extension, ou over-SSH). En règle générale, les étapes suivantes sont requises pour utiliser des fichiers locaux:

Étape 1. Détectez la méthode de connexion disponible

WordPress utilise la méthode get_filesystem_method pour détecter la disponibilité des méthodes suivantes (de la plus haute priorité à la plus basse) Direct, SSH2, extension PHP FTP, sockets FTP.

Étape 2. Obtenir les informations d'identification requises pour la méthode détectée

Si le transport détecté a besoin d'informations d'identification d'un utilisateur, WordPress utilise la fonction request_filesystem_credentials pour afficher un formulaire de demande. La fonction possède un certain nombre de paramètres lui permettant de conserver les données entre les envois de formulaires, de demander plusieurs fois les informations d'identification si la connexion a échoué et de cibler un répertoire particulier dans l'installation WordPress:

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

En fournissant un paramètre $ type vide à la fonction, nous pourrions le forcer à détecter les méthodes de connexion disponibles. Par conséquent, il appellerait la méthode get_filesystem_method pour nous. En même temps, nous pouvons forcer la fonction à utiliser un type de connexion particulier en le spécifiant à l'aide de l'argument $ type.

Lorsque les données de connexion requises par la méthode choisie ne sont pas fournies, la fonction imprime le formulaire pour le demander:

Conneciton information

Après la première demande, WordPress stocke le nom d'hôte et le nom d'utilisateur FTP dans la base de données pour une utilisation ultérieure, mais il ne stocke pas le mot de passe. Les informations d'identification FTP peuvent également être spécifiées dans le fichier wp-config.php en utilisant les constantes suivantes:

  • FTP_HOST - le nom d'hôte du serveur auquel se connecter
  • FTP_USER - le nom d'utilisateur pour se connecter avec
  • FTP_PASS - le mot de passe pour se connecter avec
  • FTP_PUBKEY - le chemin de la clé publique à utiliser pour la connexion SSH2
  • FTP_PRIKEY - le chemin de la clé privée à utiliser pour la connexion SSH2

Lorsque ces données sont stockées dans le fichier wp-config.php, le formulaire de demande d'informations d'identification n'apparaît pas, mais les inconvénients liés à la sécurité sont importants et les procédures de sécurité doivent être vérifiées avec la plus grande attention possible.

Étape 3. Initialisez la classe WordPress Filesystem et connectez-vous au système de fichiers

Le cœur de l'API WordPress Filesystem est la fonction WP_Filesystem. Il charge et initialise la classe de transport appropriée, stocke une instance obtenue dans l'objet global $ wp_filesystem pour une utilisation ultérieure et tente de se connecter au système de fichiers avec les informations d'identification fournies:

WP_Filesystem($args, $context);

Étape 4. Utiliser les méthodes WordPress Filesystem pour effectuer des opérations de lecture / écriture

Un objet $ wp_filesystem correctement initialisé a un ensemble de méthodes pour communiquer avec le système de fichiers local qui pourrait être utilisé sans autre anxiété concernant le type de connexion. En particulier, il existe des méthodes couramment utilisées:

  • get_contents - lit le fichier dans une chaîne
  • put_contents - écrit une chaîne dans un fichier
  • mkdir - crée un répertoire
  • mdir - supprime un répertoire
  • wp_content_dir - renvoie le chemin du système de fichiers local vers le dossier wp-content
  • wp_plugins_dir - renvoie le chemin du système de fichiers local vers le dossier plugins
  • wp_themes_dir - renvoie le chemin du système de fichiers local vers le dossier des thèmes

En mettant tout cela ensemble, imaginons un exemple qui effectue les étapes mentionnées ci-dessus dans une situation simple: nous allons écrire du texte soumis dans une zone de texte dans un fichier .txt simple.

Notez que cet exemple est à des fins de démonstration, dans une situation réelle, vous ne stockeriez pas de données texte simples dans un fichier .txt, ce serait une solution beaucoup plus robuste pour le stocker dans la base de données à la place.

L'API WordPress Filesystem en action

Enveloppons notre code dans un plug-in distinct, auquel sera attribué son propre dossier de système de fichiers-démo. Cela nous fournit un dossier cible pour stocker le fichier .txt et vérifier les autorisations d'écriture.

Tout d'abord, créons la page de démonstration pour afficher notre formulaire dans le menu Outils:

/*** 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 {// lu depuis fileif (false === ($ output = filesystem_demo_text_read ($ form_url))) {return;  // nous affichons les informations d'identification sous la forme d'aucun traitement supplémentaire} elseif (is_wp_error ($ output)) {$error = $output->get_error_message();$output = '';}  } $ output = esc_textarea ($ output);  // échapper à l'impression?> 

Page de démonstration de l'API Filesystem

Lors de l'affichage de notre page (filesystem_demo_screen), nous vérifions la disponibilité de la soumission de texte. S'il existe, nous essayons de l'écrire dans un fichier test.txt, sinon nous essayons de trouver un tel fichier dans le dossier du plugin et de lire son contenu pour qu'il soit inclus dans textarea. Enfin, nous imprimons un formulaire de base pour saisir du texte. Par souci de lisibilité, ces opérations d'écriture et de lecture ont été séparées en fonction de leurs propres fonctions.

Filesystem API demo

Pour éviter la duplication des mêmes étapes d'initialisation, l'assistant partagé a été créé. Il appelle d'abord request_filesystem_credentials pour détecter la méthode de connexion disponible et obtenir des informations d'identification. Si cela réussissait, il appelle alors WP_Filesystem pour lancer $ wp_filesystem avec des données données.

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

L'écriture dans le code du fichier ressemble à ceci:

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

Dans cette partie, nous avons défini certains paramètres nécessaires:

  • $ demotext - texte soumis à écrire
  • $ form_fields - élément dans le tableau $ _POST qui stocke notre texte et doit être conservé
  • Méthode $ - méthode de transport, nous la laissons vide pour détecter automatiquement
  • $ context - dossier cible (celui du plugin)

Après cela, nous avons lancé l'objet global $ wp_filesystem en utilisant la fonction d'assistance que j'ai décrite précédemment. En cas de succès, nous détectons le chemin correct vers le dossier cible et y écrivons le texte soumis à l'aide de la méthode put_contents de l'objet $ wp_filesystem.

Le code de lecture du fichier ressemble à ceci:

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

Cette fonction fonctionne de la même manière que celle décrite précédemment, mais utilise get_contents pour lire le fichier cible.

Conclusion

Lorsque vous travaillez avec des fichiers locaux, un développeur de thèmes ou de plugins WordPress entrera en contact avec des problèmes de sécurité et de compatibilité, imposant un stress énorme à l’équipe et ajoutant de longues heures au cycle de vie du projet. En s'appuyant sur l'API Filesystem, ces problèmes peuvent être évités de manière efficace. Donc, la prochaine fois que vous vous retrouvez à écrire fwrite dans le code de votre plugin, considérez cette alternative comme une option plus saine.

Vous pouvez télécharger une démo de ce code ici , et l'adapter à vos besoins.