Ewolucja WordPressa z platformy blogowej do pełnoprawnego CMSa, jednocześnie czyni z niego solidną platformę dla programistów do tworzenia wyjątkowych projektów i aplikacji.

Rdzeń WordPress, zasila nie tylko silnik publikowania użytkowników, ale także zapewnia programistom solidny zestaw klas, interfejsów API i pomocników, zaprojektowany z myślą o szerokim zakresie potrzeb.

Jednym z ukrytych skarbów WordPressa, który umożliwia programistom wykonywanie operacji w lokalnym systemie plików w bezpieczny i niezawodny sposób, jest interfejs API systemu plików WordPress. Abstrahuje on funkcjonalność manipulacji plikami w zestaw powszechnie stosowanych metod, dzięki czemu można ich bezpiecznie używać w różnych środowiskach hostingowych.

Zakres problemu

Może być kilka powodów, dla których chcesz zapisać lokalne pliki w kodzie:

  • Rejestrowanie zdarzeń lub wykonanych operacji
  • Wymiana danych z systemami innymi niż WordPress
  • Utworzyć kopię zapasową

Niezależnie od motywacji pisanie lokalnych plików z kodu PHP może być ryzykowną operacją. Przy wdrażaniu tego w przypadku motywu WordPress, wtyczki lub instalacji niestandardowej należy wziąć pod uwagę co najmniej dwie bardzo ważne pułapki:

  1. Bezpieczeństwo. Podczas zapisywania lokalnych plików z kodem (przez serwer WWW) istnieje ryzyko niepoprawnego posiadania pliku. Ten problem powstaje w źle skonfigurowanych współdzielonych środowiskach hostingu i może prowadzić do utraty kontroli nad plikami.
  2. Zgodność. Ze względu na różnorodność firm hostingowych, konfiguracja serwera danego użytkownika zazwyczaj nie jest znana deweloperowi. W związku z tym programista nie może być pewien, że uprawnienia wymagane do operacji zapisu są możliwe do uzyskania przez użytkownika wtyczki lub motywu.

Jeśli wtyczka WordPress lub kompozycja, która musi zapisywać pliki lokalne, jest przeznaczona do publicznej publikacji, deweloper powinien stale uwzględniać te problemy. Dobra wiadomość jest taka, że ​​sam WordPress ma już narzędzie do rozwiązania tych problemów: interfejs API systemu plików.

Wprowadzenie do API systemu plików WordPress

Interfejs API systemu plików został dodany do WordPressa w wersji 2.6, aby umożliwić własną funkcję aktualizacji WordPress. Abstrakty zapewniają funkcjonalność niezbędną do bezpiecznego wykonywania operacji odczytu / zapisu na różnych typach hostów. Składa się z zestawu klas i pozwala automatycznie wybrać właściwy sposób połączenia z lokalnym systemem plików, w zależności od konfiguracji hosta.

Logika za API jest dość prosta; próbuje bezpośrednio zapisywać lokalne pliki, a w przypadku niepoprawnego właściciela pliku przełącza się na inną metodę opartą na protokole FTP. W zależności od dostępnych bibliotek PHP znajduje odpowiedni sposób na skonfigurowanie połączenia FTP (przez gniazda rozszerzeń lub przez SSH). Zwykle do pracy z plikami lokalnymi wymagane są następujące kroki:

Krok 1. Wykryj, która metoda połączenia jest dostępna

WordPress używa get_filesystem_method do wykrywania dostępności następujących metod (od najwyższego priorytetu do najniższego) Direct, SSH2, FTP PHP Extension, FTP Sockets.

Krok 2. Uzyskaj poświadczenia wymagane dla wykrytej metody

Jeśli wykryty transport wymaga poświadczeń od użytkownika, WordPress używa funkcji request_filesystem_credentials do wyświetlenia formularza żądania. Funkcja ma wiele parametrów pozwalających jej zachować dane między kolejnymi formularzami, kilka razy zapytać o dane uwierzytelniające, jeśli połączenie nie powiodło się, i skierować je do określonego katalogu w instalacji WordPress:

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

Przez podanie pustego parametru $ type do funkcji możemy zmusić go do wykrycia dostępnych metod połączeń, więc wywołałoby to dla nas get_filesystem_method. Jednocześnie możemy zmusić funkcję do korzystania z określonego typu połączenia, określając go za pomocą argumentu typu $.

Jeśli nie podano danych połączenia wymaganych przez wybraną metodę, funkcja wypisze formularz, aby go o to poprosić:

Conneciton information

Po pierwszym żądaniu WordPress przechowuje nazwę hosta FTP i nazwę użytkownika w bazie danych do wykorzystania w przyszłości, ale nie przechowuje hasła. Ewentualnie poświadczenia FTP można określić w pliku wp-config.php, używając następujących stałych:

  • FTP_HOST - nazwa hosta serwera, z którym chcesz się połączyć
  • FTP_USER - nazwa użytkownika, z którym chcesz się połączyć
  • FTP_PASS - hasło, z którym należy się połączyć
  • FTP_PUBKEY - ścieżkę do klucza publicznego używanego do połączenia SSH2
  • FTP_PRIKEY - ścieżkę do klucza prywatnego używanego do połączenia SSH2

Kiedy dane te są przechowywane w pliku wp-config.php, formularz prośby o poświadczenia nie pojawia się, ale wady bezpieczeństwa są znaczące, a procedury bezpieczeństwa powinny być potrójnie sprawdzane, przy czym najwyższa uwaga powinna być zwrócona na bezpieczeństwo tego pliku.

Krok 3. Zainicjuj klasę systemów plików WordPress i połącz się z systemem plików

Sercem API systemu plików WordPress jest funkcja WP_Filesystem. Ładuje i inicjuje odpowiednią klasę transportową, przechowuje otrzymaną instancję w globalnym obiekcie $ wp_filesystem do dalszego wykorzystania i próbuje połączyć się z systemem plików przy użyciu podanych poświadczeń:

WP_Filesystem($args, $context);

Krok 4. Użyj metod systemu plików WordPress do wykonywania operacji odczytu / zapisu

Prawidłowo zainicjowany obiekt $ wp_filesystem ma zestaw metod komunikacji z lokalnym systemem plików, który może być używany bez dalszych obaw związanych z typem połączenia. W szczególności istnieją następujące powszechnie stosowane metody:

  • get_contents - odczytuje plik w postaci ciągu znaków
  • put_contents - zapisuje ciąg do pliku
  • mkdir - tworzy katalog
  • mdir - usuwa katalog
  • wp_content_dir - zwraca ścieżkę w lokalnym systemie plików do folderu wp-content
  • wp_plugins_dir - zwraca ścieżkę lokalnego systemu plików do folderu wtyczek
  • wp_themes_dir - zwraca ścieżkę do lokalnego systemu plików do folderu motywów

Łącząc to wszystko razem, przedstawmy przykład, który wykonuje powyższe kroki w prostej sytuacji - napiszemy tekst przesłany w textarea do zwykłego pliku .txt.

Zauważ, że ten przykład służy do celów demonstracyjnych, w sytuacji rzeczywistej nie przechowujesz prostych danych tekstowych w pliku .txt, byłoby to znacznie bardziej niezawodne rozwiązanie do przechowywania ich w bazie danych.

Interfejs API systemu plików WordPress w akcji

Owińmy nasz kod w osobną wtyczkę, która otrzyma własny folder filesystem-demo. Daje nam to folder docelowy do przechowywania pliku .txt i sprawdzania uprawnień do zapisu.

Przede wszystkim stwórz stronę demonstracyjną, aby wyświetlić nasz formularz w menu Narzędzia:

/*** 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 {// odczyt z fileif (false === ($ output = filesystem_demo_text_read ($ form_url))) {return;  // wyświetlamy referencje bez potrzeby dalszego przetwarzania} elseif (is_wp_error ($ output)) {$error = $output->get_error_message();$output = '';}  } $ output = esc_textarea ($ output);  // escaping do drukowania?> 

Strona demonstracyjna API systemu plików

Podczas wyświetlania naszej strony (filesystem_demo_screen) sprawdzamy dostępność przesłanego tekstu. Jeśli istnieje, staramy się zapisać go w pliku test.txt, w przeciwnym razie staramy się znaleźć taki plik w folderze wtyczek i odczytać jego zawartość, która ma być zawarta w textarea. Na koniec drukujemy podstawowy formularz do tekstu wejściowego. Ze względu na czytelność te operacje pisania i czytania zostały rozdzielone na ich własne funkcje.

Filesystem API demo

Aby uniknąć powielania tych samych kroków inicjowania, utworzono współdzielonego pomocnika. Najpierw wywołuje request_filesystem_credentials, aby wykryć dostępną metodę połączenia i uzyskać referencje. Jeśli to się powiedzie, to wywołuje WP_Filesystem, aby zainicjować $ wp_filesystem z danymi.

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

Zapisywanie do kodu pliku wygląda następująco:

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

W tej części zdefiniowaliśmy kilka niezbędnych parametrów:

  • $ demotext - przesłany tekst do napisania
  • $ form_fields - element w tablicy $ _POST, który przechowuje nasz tekst i powinien zostać zachowany
  • Metoda $ - metoda transportu, pozostawiamy puste pole do automatycznego wykrywania
  • $ context - folder docelowy (plik wtyczki)

Następnie zainicjowaliśmy globalny obiekt $ wp_filesystem za pomocą funkcji pomocnika, którą opisałem wcześniej. W przypadku sukcesu wykrywamy poprawną ścieżkę do folderu docelowego i zapisujemy do niego przesłany tekst za pomocą metody put_contents obiektu $ wp_filesystem.

Kod do czytania z pliku wygląda następująco:

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

Ta funkcja działa w taki sam sposób jak poprzednio opisany, ale używa get_contents do odczytu z pliku docelowego.

Wniosek

Podczas pracy z plikami lokalnymi programista WordPress lub programista wtyczek wejdzie w kontakt z kwestiami bezpieczeństwa i kompatybilności, kładąc ogromny nacisk na zespół i dodając długie godziny do cyklu życia projektu. Opierając się na API systemu plików, te problemy mogą zostać efektywnie usunięte. Gdy następnym razem napiszę fwrite do kodu wtyczki, rozważ alternatywę, która jest zdrowsza.

Możesz pobierz wersję demonstracyjną tego kodu tutaj i dostosuj go do swoich potrzeb.