Gdzie mieszka Twój kod w WordPress? Mapa dla tych, którzy chcą wiedzieć dlaczego

Widzę dwa typy WordPress developerów. Jedni wrzucają wszystko do functions.php. Drudzy wszystko do snippet pluginu. Oboje mają rację i oboje się mylą.
Nie dlatego, że jedno narzędzie jest złe. Dlatego, że każde z nich istnieje z innego powodu. I kiedy zrozumiesz ten powód, przestajesz się zastanawiać „gdzie wrzucić ten kod” i zaczyna_sz wiedzieć.
Mamy cztery miejsca na kod w WordPress. functions.php, mu-plugins, snippet plugin i własny plugin. Każde z nich rozwiązuje inny problem. Każde ma sytuacje, w których jest jedynym sensownym wyborem. I każde ma sytuacje, w których nie powinno tam nic być.
Przechodzę przez każde z osobna, z konkretnymi przykładami z projektów które robiliśmy w important.is.
functions.php: nie zasługuje na złą sławę
functions.php ma złą reputację. Słyszałem to wiele razy. „Nie tknij functions.php.” „Wszystko przez plugin.” „To pułapka.”
Ale functions.php nie jest zły. Jest źle używany.
Jego rola jest prosta. Plik żyje w katalogu motywu. Tam jest jego miejsce i tam ma sens, ale wyłącznie dla kodu, który dotyczy tego motywu. Rejestracja menu, wsparcie dla miniatur, rozmiary obrazków specyficzne dla layoutu. Rzeczy, które mają sens tylko razem z tym konkretnym wyglądem strony.
// To ma sens w functions.php — bez tego motyw nie wie jak się wyświetlić
add_theme_support( 'post-thumbnails' );
add_image_size( 'hero-banner', 1440, 600, true );
register_nav_menus( array(
'primary' => 'Menu główne',
'footer' => 'Menu stopki',
) );
Problem zaczyna się, gdy do functions.php trafia kod który nie ma nic wspólnego z wyglądem. Widziałem to setki razy. Sklep WooCommerce na Astra Theme, klient aktualizuje motyw po roku, i nagle znikają: własny status zamówienia „W realizacji u dostawcy”, modyfikacja emaila potwierdzającego, integracja z systemem magazynowym. Wszystko było w functions.php. Wszystko zniknęło po kliknięciu „Aktualizuj”.
To nie jest kod motywu. To jest logika biznesowa, która powinna przeżyć zmianę szablonu.
Child theme rozwiązuje połowę problemu
Child theme to dobry ruch, ale nie rozwiązuje wszystkiego. Owszem, aktualizacja motywu nadrzędnego nie nadpisuje functions.php dziecka. Ale jeśli zmienisz motyw z Astry na Kadence, kod w functions.php child theme nadal znika razem z całym motywem.
Zasada jest prosta. Zadaj sobie pytanie: czy ten kod miałby sens w innym motywie? Jeśli tak, to nie należy do functions.php.
Kod który zmienia rozmiar miniatur dla layoutu opartego na siatce 3-kolumnowej, może nie mieć sensu w motywie z layoutem jednopanelowym. Zostaje w functions.php.
Kod który automatycznie kompletuje zamówienia z produktami wirtualnymi, ma sens w każdym sklepie WooCommerce niezależnie od motywu. Nie należy do functions.php.
Plusy functions.php
Zawsze dostępny przez FTP — żaden plugin nie musi działać żeby edytować plik. Ładuje się jako część motywu bez dodatkowego narzutu. Nie ma zależności od zewnętrznego pluginu. Jeśli trzymasz tam wyłącznie kod powiązany z motywem, to uczciwe i przewidywalne miejsce.
Minusy functions.php
Brak error isolation. Jeden błąd składniowy i masz biały ekran, który naprawiasz przez FTP. Brak UI, brak opisów, brak tagów. Za rok nie wiesz co i dlaczego. Nie możesz wyłączyć jednej funkcji bez usunięcia kodu. Brak conditional loading, kod uruchamia się wszędzie. I ten największy: kod znika razem z motywem przy zmianie szablonu, nawet przy child theme.
Potrzebujesz pomocy z tym tematem?
Pomagam firmom wdrażać nowoczesne rozwiązania. Umów bezpłatną 30-minutową rozmowę.
Umów bezpłatną rozmowę →
mu-plugins: najmniej znane, najbardziej niedoceniane
Folder /wp-content/mu-plugins/ jest niemal niewidoczny dla większości osób pracujących z WordPress. Większość developerów wie że istnieje, ale niewielu go używa. A szkoda, bo rozwiązuje bardzo konkretny problem.
Kod umieszczony w mu-plugins ładuje się przed wszystkim. Przed pluginami. Przed motywem. Przed snippet managerem. I nie można go wyłączyć z panelu admina.
To ostatnie jest ważniejsze niż się wydaje. Jeśli klient albo nieuważny developer wyłączy wszystkie pluginy, mu-plugins nadal działają. Jeśli ktoś przejmie konto admina i zacznie demolować stronę, nie może przez panel zdeaktywować kodu który tam siedzi.
Co tam trzymamy
Na każdym projekcie mamy plik security.php w mu-plugins z kilkoma rzeczami, które muszą działać zawsze:
<?php
// Wyłącza XML-RPC — wciąż popularny wektor ataków brute-force
add_filter( 'xmlrpc_enabled', '__return_false' );
// Usuwa wersję WordPress z nagłówków i kodu źródłowego remove_action( 'wp_head', 'wp_generator' ); add_filter( 'the_generator', '__return_empty_string' );
// Blokuje enumerację użytkowników przez URL /?author=1 add_action( 'init', function() { if ( isset( $_SERVER['QUERY_STRING'] ) && preg_match( '/author=([0-9]*)/i', $_SERVER['QUERY_STRING'] ) ) { wp_redirect( home_url( '/' ), 301 ); exit; } });
// Wyłącza edytor plików w panelu admina if ( ! defined( 'DISALLOW_FILE_EDIT' ) ) { define( 'DISALLOW_FILE_EDIT', true ); }
Trzy, cztery rzeczy. Żadna nie potrzebuje UI. Żadna nie powinna być wyłączalna. Mu-plugins to ich naturalne miejsce.
Podfolderowy loader
Jedno ograniczenie mu-plugins: pliki muszą leżeć bezpośrednio w folderze, nie w podfolderach. Jeśli chce_sz mieć porządek i trzymać kilka plików, tworzysz loader:
<?php
// /wp-content/mu-plugins/loader.php
require_once __DIR__ . '/important-mu/security.php';
require_once __DIR__ . '/important-mu/performance.php';
Pliki security.php i performance.php siedzą w podfolderze important-mu/. Loader je ładuje. Porządek zachowany.
Kiedy nie używać mu-plugins
Plusy mu-plugins
Ładuje się przed wszystkim, więc kod działa zanim jakikolwiek plugin zdąży cokolwiek zmienić. Nie można wyłączyć przez panel admina, co w kontekście security hardening jest zaletą, nie wadą. Zero zależności od innych pluginów. Edytowalny przez FTP jak każdy plik PHP. OPcache-friendly, PHP kompiluje pliki do bytecode. Przeżywa reset bazy danych, bo siedzi w plikach.
Minusy mu-plugins
Brak UI, brak opisów, brak historii zmian. Jeśli popełni_sz błąd składniowy w pliku PHP, strona przestaje działać i musisz naprawić przez FTP lub SSH. Nie ma możliwości szybkiego wyłączenia przez panel bez usunięcia lub skomentowania kodu. Trudny do zarządzania dla osoby bez dostępu do serwera. Niewidoczny w panelu admina, więc ktoś może nie wiedzieć że w ogóle istnieje.
Dlatego trzymam tam tylko rzeczy, które znam na pamięć i które są krótkie. Wszystko co ma być elastyczne, włączane i wyłączane, komentowane przez kogoś innego, wychodzi z mu-plugins i ląduje w snippet pluginie.
Snippet plugins: właściwe narzędzie do właściwej roboty
Mam snippety, które działają na stronach klientów od kilku lat. Tweaki WooCommerce, modyfikacje emaili, drobne zmiany w działaniu formularzy, własne style CSS na konkretnych podstronach. Każdy z opisem, każdy z informacją kiedy i dlaczego powstał.
I na tym polega prawdziwa wartość snippet pluginu. Nie w tym, że zastępuje functions.php. W tym, że robi kilka rzeczy, których ani functions.php, ani mu-plugins nie potrafią.
Organizacja i dokumentacja
Snippet ma nazwę, opis, tagi. Możesz go wyłączyć bez usuwania. Możesz zostawić komentarz dla siebie z przyszłości albo dla innego dewelopera. Brzmi jak drobiazg. Nie jest.
Pracowałem ze stronami, gdzie poprzedni developer zostawił trzydzieści niezidentyfikowanych snippetów. Żaden bez opisu. Żaden z datą. Żaden z informacją dlaczego w ogóle powstał. To jest dług, który ktoś musi spłacić.
Dobry snippet dokumentuje się w dwóch miejscach. W opisie w managerze, i w komentarzu na początku kodu:
/**
* WooCommerce — autocomplete zamówień wirtualnych
*
* Zamówienia zawierające tylko produkty wirtualne (PDF, dostęp online)
* automatycznie przechodzą w status "Zrealizowane" po płatności.
* Bez tego klient musi czekać na ręczne potwierdzenie.
*
* @since 2024-03-12
* @author Łukasz / important.is
*/
add_action( 'woocommerce_payment_complete', function( $order_id ) {
$order = wc_get_order( $order_id );
if ( $order && $order->needs_processing() === false ) {
$order->update_status( 'completed' );
}
});
Za rok, gdy wróci_sz do tej strony, wiesz co to jest, dlaczego tu jest, i kto to napisał.
Error isolation
To chyba największa zaleta snippet pluginu i najrzadziej wymieniana.
Wyobraź sobie że edytujesz functions.php i robisz literówkę. Brakuje średnika. WordPress próbuje załadować plik, napotyka błąd składni PHP i wyświetla biały ekran. Strona niedostępna. Musisz połączyć się przez FTP, otworzyć plik, znaleźć błąd, naprawić, zapisać. O 22:00, gdy strona klienta jest nieosiągalna, to jest bardzo nieprzyjemne doświadczenie.
Przy snippet pluginie jest inaczej. Dobry manager, kiedy snippet wywołuje fatal error, wyłączy ten jeden snippet. WP Code robi to szczególnie elegancko, ma safe mode który możesz aktywować przez URL:
https://twoja-strona.pl/?wpcode-safe-mode=true
Strona ładuje się normalnie, wszystkie snippety wyłączone. Widzisz który crashował, naprawiasz, wchodzisz z powrotem. Bez FTP, bez paniki.
Ale jest jedno „ale”. I to ważne.
Snippety trzymane w bazie danych, a tak działają Code Snippets i WP Code, są dostępne wyłącznie przez panel admina albo bezpośrednio przez bazę. Jeśli masz dostęp tylko do FTP i panel nie działa, nie możesz edytować snippetów tak jak poprawiłbyś plik PHP. Przy functions.php otwierasz FileZillę, wchodzisz do katalogu motywu, edytujesz plik, zapisujesz. Przy snippecie w bazie musisz wejść do phpMyAdmin albo użyć WP-CLI:
# przez WP-CLI możesz wyłączyć snippet z linii komend
wp snippet deactivate 42 --allow-root
To działa, ale wymaga dostępu do WP-CLI albo do phpMyAdmin. Nie każdy hosting to ma wygodnie skonfigurowane. Nie każdy developer ma dostęp do panelu hostingowego klienta.
Fluent Snippets rozwiązuje ten problem inaczej. Snippety siedzą jako pliki PHP w /wp-content/uploads/fluent-snippets/. Masz FTP? Masz dostęp do swojego kodu. Możesz otworzyć plik, skomentować linię która powoduje błąd, zapisać. Strona wraca do życia bez logowania do panelu, bez WP-CLI, bez phpMyAdmin.
To jest jedna z konkretnych, praktycznych przewag podejścia file-based. Szczególnie istotna w sytuacjach awaryjnych o 22:00, gdy dostęp do panelu klienta nie jest oczywisty.
Conditional loading
Snippet który zmienia tekst przycisku „Dodaj do koszyka” nie musi ładować się na stronie „O nas”, w panelu admina ani w RSS. To brzmi banalnie, ale w sklepach z setkami produktów i dziesiątkami snippetów suma się.
// Ten kod uruchamia się TYLKO na stronach produktów — ustawiasz to w UI managera
add_filter( 'woocommerce_product_add_to_cart_text', function() {
return 'Zamów teraz';
});
Conditional loading to jedna z funkcji, które odróżniają snippet plugin od wklejania kodu do functions.php. W UI wybierasz warunek: is_product(), is_checkout(), konkretna kategoria, konkretny typ użytkownika. Kod nie odpala się nigdzie indziej.
CSS i JS snippety, o których wszyscy zapominają
PHP to nie wszystko. Snippet plugin to też wygodne miejsce na CSS i JavaScript, które powinny ładować się warunkowo.
Typowy przykład: klient chce niestandardowy wygląd strony koszyka. Trzy linie CSS. Możesz to zrobić w Customizer (ale to zniknie z motywem), w style.css child theme (ale ładuje się na każdej stronie) albo jako CSS snippet z warunkiem is_cart().
/* Snippet: WooCommerce koszyk — dostosowanie wyglądu tabeli
Ładuje się tylko na: is_cart()
Autor: Łukasz, 2024-08 */
.woocommerce-cart table.cart td.product-name {
font-size: 15px;
line-height: 1.5;
}
.cart_totals .order-total {
font-weight: 700;
font-size: 18px;
}
Albo Google Tag Manager. Oficjalny sposób to przez header/footer motywu. Lepszy sposób to snippet z HTML/JS załadowany globalnie albo warunkowo:
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXXX');</script>
Jeden snippet. Łatwy do wyłączenia, łatwy do podmiany, z opisem „GTM — dodano 2024-01-15 na prośbę klienta”.
WP Code, Code Snippets, Fluent Snippets — który wybrać
Code Snippets to klasyk. Działa od 2012 roku, ponad milion instalacji, darmowa wersja pokrywa 90% potrzeb. Dobry punkt startowy dla każdego.
WP Code ma dwie unikalne rzeczy. Po pierwsze, bibliotekę gotowych snippetów, dziesiątki gotowych rozwiązań dla WooCommerce, bezpieczeństwa, wydajności. Po drugie, safe mode przez URL, który omawiałem wyżej. Jeśli zarządza_sz stroną gdzie klient sam może coś zepsuć, WP Code jest bezpieczniejszym wyborem.
Fluent Snippets idzie inną drogą architektonicznie. Trzyma snippety w plikach na serwerze, nie w bazie danych. Przy każdym ładowaniu strony zero zapytań do bazy, pełne wsparcie OPcache, PHP kompiluje je do bytecode i trzyma w pamięci. Na stronach z dużym ruchem różnica jest odczuwalna. Fluent Snippets ma też standalone mode, snippety działają nawet po odinstalowaniu pluginu bo są po prostu plikami PHP na serwerze. Dla nas to często decydujący argument.
Różnica praktyczna: Fluent Snippets na projekcie z dużym ruchem potrafi oszczędzić kilka-kilkanaście milisekund per request i kilka zapytań do bazy. Przy tysiącu wizyt dziennie to ma znaczenie. Przy pięćdziesięciu, nie.
Plusy snippet pluginu
UI z opisami, tagami, historią. Error isolation, jeden błąd nie crashuje całej strony. Conditional loading bez pisania ręcznych ifów. Możliwość szybkiego wyłączenia bez usuwania kodu. Niezależność od motywu, kod przeżywa zmianę szablonu. Przy Fluent Snippets: edytowalny przez FTP, zero DB queries, OPcache, standalone mode.
Minusy snippet pluginu
Snippety w bazie danych (Code Snippets, WP Code free) nie są dostępne przez FTP przy awarii, potrzebujesz phpMyAdmin albo WP-CLI. Zależność od pluginu, jeśli deaktywujesz manager, kod przestaje działać (wyjątek: Fluent Snippets standalone mode). Snippety w bazie komplikują workflow staging/produkcja przy push tylko plików. Przy dużej liczbie snippetów bez opisów, zarządzanie staje się bałaganem równie złym co functions.php. I jeden ważny: snippet plugin jest narzędziem developerskim, nie powinien być dostępny dla każdego admina na stronie klienta.
Własny plugin: kiedy snippet wyrasta ze swojego miejsca
Jest jeden sygnał, który mówi mi że coś przestało być snippetem.
Kiedy zaczynam myśleć „powinienem to przetestować”.
Snippet to kawałek kodu z jednym zadaniem. Ale czasem jeden snippet staje się dwoma, dwa stają się pięcioma, i nagle masz w snippet managerze osiem powiązanych ze sobą kawałków kodu które muszą działać razem. Żaden z nich nie ma sensu bez pozostałych. To nie jest kolekcja snippetów. To jest plugin, który udaje że nim nie jest.
Przykład z życia: integracja z kurierem
Zaczyna się od jednego snippetu, który dodaje pole „uwagi do kuriera” w koszyku. Potem drugi, który zapisuje to pole do metadanych zamówienia. Potem trzeci, który wysyła te dane do API kuriera po złożeniu zamówienia. Potem czwarty, który obsługuje webhook zwrotny i aktualizuje status. I piąty, który loguje błędy gdy API nie odpowiada.
Pięć snippetów, powiązanych ze sobą, gdzie wyłączenie jednego psuje pozostałe. To jest plugin. Tylko że rozrzucony po managerze zamiast mieć swój folder.
/wp-content/plugins/important-kurier/
important-kurier.php <- nagłówek pluginu, wersja, autor
includes/
class-api.php <- komunikacja z API kuriera
class-hooks.php <- hooki WooCommerce (pole w koszyku, zapis metadanych)
class-webhook.php <- obsługa zwrotnych powiadomień
class-logger.php <- logowanie błędów
readme.txt
W tej formie możesz go wrzucić do Git, zrobić pull request, napisać testy dla class-api.php, przenieść na inną stronę przez kopiowanie folderu. Snippety tego nie dają.
Inne sygnały że czas na własny plugin
Pojawia się Composer. Jeśli kod potrzebuje zewnętrznej biblioteki, zarządzanej przez Composer, nie trzymaj tego w snippecie.
Pojawia się własna tabela w bazie danych. Snippet który robi $wpdb->query("CREATE TABLE...") w środku managera to zły pomysł. To jest infrastruktura, która należy do właściwego pluginu z install/uninstall hooks.
Pojawia się potrzeba migracji. Kod który zmienia strukturę danych albo przepisuje coś w bazie, musi mieć kontrolowaną wersję. Snippet tego nie zapewnia.
Kod jest zbyt złożony żeby czytać go w okienku przeglądarki. To subiektywny próg, ale realny. Jak czytanie kodu w snippet managerze zaczyna być uciążliwe, to znak że potrzebuje IDE i proper struktury plików.
Plusy własnego pluginu
Pełna kontrola nad strukturą. Git, testy, Composer, pull requesty. Przenośny między stronami przez kopiowanie folderu. Widoczny w panelu jako osobny plugin z wersją i autorem. Można go deaktywować niezależnie od innych. Działa bez żadnego zewnętrznego managera.
Minusy własnego pluginu
Wymaga więcej czasu i wiedzy, żeby dobrze zacząć. Overkill dla trzech linii kodu. Brak wbudowanego UI do zarządzania ustawieniami, jeśli ich potrzebujesz, musisz go sam napisać. Klient nie może łatwo sprawdzić co robi bez dewelopera. I jeden nieoczywisty: własny plugin bez dokumentacji i bez Git staje się czarną skrzynką tak samo jak niezidentyfikowany snippet.
Najczęstsze snippety WooCommerce które warto mieć
Żeby nie kończyć na teorii. Oto snippety które pojawiają się prawie na każdym projekcie z WooCommerce. Każdy z nich należy do snippet managera, nie do functions.php.
Usuń zbędne zakładki ze strony produktu. Domyślnie WooCommerce pokazuje „Opis”, „Dodatkowe informacje” i „Opinie”. Jeśli sklep nie używa opinii ani tabel atrybutów, te zakładki tylko szumią.
add_filter( 'woocommerce_product_tabs', function( $tabs ) {
unset( $tabs['reviews'] ); // usuwa zakładkę Opinie
unset( $tabs['additional_information'] ); // usuwa Dodatkowe informacje
return $tabs;
});
Warunek conditional loading: is_product(). Nie ma powodu żeby ten filtr odpala się na stronie głównej.
Zmień minimalną kwotę zamówienia. Sklep który wysyła produkty kurierem nie opłaca się przy zamówieniach poniżej pewnej kwoty. Zamiast wyłączać sklep albo pisać do klientów, ustawiasz minimalną kwotę:
add_action( 'woocommerce_check_cart_items', function() {
$minimum = 50; // minimalna kwota w PLN
$total = WC()->cart->get_cart_contents_total();
if ( $total < $minimum ) { wc_add_notice( sprintf( 'Minimalna kwota zamówienia to %s PLN. Brakuje Ci %s PLN.', $minimum, number_format( $minimum - $total, 2 ) ), 'error' ); } });
Usuń pole „Firma” z checkoutu. Większość sklepów B2C nie potrzebuje pola NIP i nazwy firmy. Uproszczony formularz to mniej tarcia, więcej konwersji.
add_filter( 'woocommerce_checkout_fields', function( $fields ) {
unset( $fields['billing']['billing_company'] );
return $fields;
});
Zmień kolejność pól w checkoucie. WooCommerce domyślnie pokazuje „Imię” i „Nazwisko” jako dwa osobne pola w jednym wierszu. Jeśli layout wymaga innej kolejności, priority to rozwiązuje:
add_filter( 'woocommerce_checkout_fields', function( $fields ) {
$fields['billing']['billing_first_name']['priority'] = 10;
$fields['billing']['billing_last_name']['priority'] = 20;
$fields['billing']['billing_phone']['priority'] = 25; // telefon wyżej
$fields['billing']['billing_email']['priority'] = 30;
return $fields;
});
Dodaj własną wiadomość na stronie potwierdzenia zamówienia. Po złożeniu zamówienia klient ląduje na stronie „Dziękujemy”. Domyślna treść jest generyczna. Jeden snippet zmienia to na coś konkretnego dla danego sklepu.
add_action( 'woocommerce_thankyou', function( $order_id ) {
echo '<p class="order-note">Twoje zamówienie zostanie spakowane i wysłane w ciągu 24h.
Na mailu dostaniesz numer do śledzenia paczki.</p>';
});
Wszystkie powyższe to po kilka linii kodu. Każdy ma jeden konkretny cel. Każdy działa niezależnie od pozostałych. To jest idealny kandydat na snippet.
Co się dzieje z Twoim kodem przy migracji strony
Migracja strony to moment, w którym wybór miejsca przechowywania kodu staje się bardzo widoczny.
Przy migracji przenosi się dwie rzeczy: pliki serwera i bazę danych. Jeśli zrobiłe_ś to przez WP Migrator, All-in-One Migration albo ręczny dump SQL, zazwyczaj obie rzeczy idą razem. Ale nie zawsze.
Kod w bazie danych (Code Snippets, WP Code w wersji darmowej) ląduje w tabelach wp_posts i wp_postmeta. Przy migracji bazy, przenosi się razem z resztą treści. Przy przejściu ze stagingu na produkcję przez push bazy, wszystkie snippety idą razem. Przy resecie bazy do kopii z tygodnia temu, tracisz snippet który dodałeś wczoraj.
Kod w plikach (Fluent Snippets, własny plugin) siedzi na dysku. Przy migracji przez rsync albo Git, idzie razem z plikami. Przy resecie bazy, pliki zostają niezmienione. Ale przy migracji „tylko baza bez plików”, snippety nie przeniosą się.
Praktyczna konsekwencja: jeśli pracuje_sz ze środowiskiem staging/produkcja i robisz push bazy danych w jedną stronę, Fluent Snippets może cię zaskoczyć. Snippety które dodałe_ś na stagingu nie pojawią się na produkcji, bo są w plikach stagingowego serwera, nie w bazie.
Nie ma jednego lepszego podejścia. Jest podejście które pasuje do twojego workflow. Jeśli migrujesz przez bazę danych, WP Code albo Code Snippets będą zachowywać się przewidywalnie. Jeśli migrujesz przez Git i synchronizację plików, Fluent Snippets jest naturalniejszy.
Bezpieczeństwo: kilka rzeczy których nie mówi większość tutoriali
Snippet plugin daje adminom ogromną władzę. Każda osoba z dostępem do panelu i uprawnieniami administratora może przez snippet plugin wykonać dowolny kod PHP na serwerze. To jest funkcjonalność, nie błąd. Ale warto wiedzieć co z tego wynika.
Standardowe zabezpieczenie WordPress, DISALLOW_FILE_EDIT = true, blokuje edytor motywów i pluginów w panelu. Nie blokuje snippet managerów. Bo snippet managery nie edytują plików systemowych, a rekordy w bazie danych albo pliki w uploads. To jest świadoma decyzja architektury, ale jej konsekwencja jest taka, że ktoś ze skradzionym kontem admina może wykonać kod nawet przy włączonym DISALLOW_FILE_EDIT.
Rozwiązanie praktyczne: ogranicz liczbę adminów do minimum. Używaj ról. Jeśli klient potrzebuje zarządzać treścią, nie daj mu roli Administrator, jeśli nie jest absolutnie konieczna.
Drugi aspekt: Code Snippets w wersji darmowej wykonuje PHP przez dynamiczne uruchomienie kodu ze stringa. Nie będę wchodzić w szczegóły, ale jest to metoda z ograniczonym wsparciem OPcache i pewnym profilem bezpieczeństwa. WP Code Pro i Fluent Snippets używają include() na plikach, co jest czystszym rozwiązaniem. Dla większości projektów ta różnica nie ma znaczenia. Dla projektów z restrykcyjnymi wymaganiami bezpieczeństwa ma.
Jak to wygląda u nas w important.is
Nie ma jednego szablonu. Każdy projekt jest trochę inny. Ale pewne rzeczy powtarzają się na każdym projekcie.
mu-plugins są zawsze. Jeden plik security.php z hardeningiem, czasem drugi config.php z niestandardowymi stałymi dla środowiska. Nigdy więcej niż dwa-trzy pliki. Krótkie, bez zależności.
functions.php w child theme ma zwykle dosłownie kilka linijek. Rejestracja menu, rozmiary obrazków, add_theme_support. Nic więcej. Jeśli otwieramy functions.php i widzimy tam coś związanego z WooCommerce albo zewnętrznym API, to jest sygnał że poprzedni developer wrzucił kod w złe miejsce.
Snippet plugin jest na każdym projekcie z aktywnymi customizacjami. Wybieramy między WP Code a Fluent Snippets w zależności od tego kto zarządza stroną. WP Code gdy klient ma dostęp do panelu i może coś zmienić, safe mode jest wtedy bezcenny. Fluent Snippets gdy tylko my, liczymy zapytania do bazy i zależy nam na OPcache. Każdy snippet ma nazwę, opis i datę. Bez wyjątków. Kiedyś zrezygnowaliśmy z tego wymagania na jednym projekcie i po dwóch latach nikt, łącznie z autorem, nie wiedział co robi siedem z dwunastu snippetów.
Własny plugin pojawia się gdy zaczyna się coś złożonego. Integracja z zewnętrznym systemem, niestandardowy CPT z rozbudowaną logiką, własne REST API, cokolwiek co ma więcej niż pięć powiązanych haków. Wtedy tworzy się folder w /wp-content/plugins/, plugin dostaje własne repozytorium w Git, i traktujemy go jak normalny produkt z wersjonowaniem.
Mapa decyzyjna
Przed dodaniem kodu gdziekolwiek zadaję sobie trzy pytania.
Czy ten kod jest ściśle związany z wyglądem aktywnego motywu? Jeśli tak, functions.php w child theme.
Czy ten kod musi działać zawsze, przed wszystkim, i nie może być wyłączony przez panel? Jeśli tak, mu-plugins.
Czy to kod który ktoś inny będzie musiał rozumieć, włączać i wyłączać, opisywać? Jeśli tak, snippet plugin.
Czy to zestaw powiązanych funkcji, który musi być testowalny, ma zewnętrzne zależności albo zarządza strukturą danych? Własny plugin.
Gdzie trzymasz swój kod? Pytanie brzmi technicznie, ale odpowiedź mówi więcej o tym jak myślisz o projekcie, który oddajesz klientowi. Czy za rok ktoś, kto nie był przy tym projekcie, będzie wiedział co tu jest, dlaczego tu jest, i co się stanie jak to wyłączy?
To jest ten standard.
Zostań w pętli
Nowe artykuły, narzędzia i case study — prosto na maila.