Dobre Praktyki Programowania Wtyczek WordPress
Pisanie wtyczek do WordPressa i WooCommerce wymaga zachowania rygorystycznych standardów inżynieryjnych. Nieprzemyślany kod może prowadzić do konfliktów z innymi wtyczkami, podatności bezpieczeństwa (SQLi, XSS, CSRF), a także drastycznego obciążenia bazy danych i pamięci RAM serwera.
W tym przewodniku omawiamy dobre praktyki programistyczne, standardy architektury PHP (PSR-12) oraz wzorce projektowe stosowane w ekosystemie Pluger i Entriso.
1. Architektura i Separacja Odpowiedzialności (Clean Architecture)
Jednym z najczęstszych błędów początkujących deweloperów WordPressa jest umieszczanie całej logiki biznesowej, rejestracji hooków, kodu HTML i zapytań SQL w jednym, gigantycznym pliku głównym (monolithic plugin file).
Rekomendowana struktura modułowa
my-custom-plugin/
├── assets/ # Zasoby frontendowe (CSS, JS, obrazy)
│ ├── css/
│ └── js/
├── includes/ # Logika biznesowa i klasy PHP
│ ├── Admin/ # Panele administracyjne, ustawienia WP Admin
│ │ ├── SettingsPage.php
│ │ └── AdminNotices.php
│ ├── Api/ # Punkty końcowe REST API i webhooki
│ │ └── RestEndpoints.php
│ ├── Core/ # Inicjalizacja, ładowanie hooków, kontener DI
│ │ ├── Plugin.php
│ │ └── Activator.php
│ └── Services/ # Usługi biznesowe (np. integracja z Pluger)
│ └── LicensingService.php
├── languages/ # Pliki tłumaczeń (.pot, .po, .mo)
├── templates/ # Czyste szablony widoków (HTML/PHP)
├── composer.json # Autoloader PSR-4
├── my-custom-plugin.php # Plik wejściowy (bootstrap)
└── readme.txt # Specyfikacja WordPress.org
Wykorzystanie Autoloadera PSR-4
Zamiast dziesiątek instrukcji require_once, używaj standardu Composer PSR-4:
{
"name": "vendor/my-custom-plugin",
"autoload": {
"psr-4": {
"MyVendor\\MyPlugin\\": "includes/"
}
}
}
2. Bezpieczeństwo: Walidacja, Sanityzacja i Escaping Danych
Zasada numer jeden bezpiecznego programowania w WordPressie brzmi: Nigdy nie ufaj danym wejściowym i zawsze zabezpieczaj dane wyjściowe.
A. Sanityzacja danych wejściowych (Input Sanitization)
Przed zapisaniem jakichkolwiek wartości z $_POST, $_GET lub $_REQUEST:
// 1. Zawsze zdejmuj automatyczne slashe WordPressa (wp_unslash)
$raw_email = isset($_POST['user_email']) ? wp_unslash($_POST['user_email']) : '';
// 2. Sanityzuj odpowiednio do typu danych
$clean_email = sanitize_email($raw_email);
$clean_text = sanitize_text_field(wp_unslash($_POST['user_name'] ?? ''));
$clean_int = absint($_POST['item_id'] ?? 0);
$clean_html = wp_kses_post(wp_unslash($_POST['rich_content'] ?? ''));
B. Weryfikacja Uprawnień i Nonce (CSRF Protection)
Każdy formularz i żądanie AJAX musi weryfikować token bezpieczeństwa (Nonce) oraz uprawnienia zalogowanego użytkownika (capabilities):
public function handle_save_settings(): void {
// 1. Sprawdź Nonce
if (!isset($_POST['my_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['my_nonce'])), 'my_save_action')) {
wp_die(__('Błąd autoryzacji tokenu bezpieczeństwa.', 'my-plugin'), 403);
}
// 2. Sprawdź uprawnienia użytkownika
if (!current_user_can('manage_options')) {
wp_die(__('Brak wystarczających uprawnień administracyjnych.', 'my-plugin'), 403);
}
// 3. Bezpieczne wykonanie logiki
update_option('my_plugin_option', sanitize_text_field(wp_unslash($_POST['setting_val'] ?? '')));
}
C. Escaping danych wyjściowych (Output Escaping)
Każda zmienna drukowana do kodu HTML musi być przefiltrowana w momencie wyświetlania:
esc_html($text)— dla zwykłego tekstu wewnątrz znaczników HTML.esc_attr($attr)— dla wartości atrybutów HTML (np.value="",placeholder="").esc_url($url)— dla adresów URL w atrybutachhreflubsrc.esc_js($script)— dla inline JavaScript.
3. Optymalizacja Wydajności i Pamięci RAM
Częstym problemem wtyczek jest spowalnianie czasu ładowania strony (TTFB) oraz psucie wskaźników Core Web Vitals (LCP, INP, CLS).
A. Dynamiczne kolejkowanie skryptów i styli (Conditional Enqueueing)
Nigdy nie ładuj skryptów CSS i JS na każdej podstronie, jeśli wtyczka jest używana tylko na wybranych podstronach (np. tylko w koszyku WooCommerce lub na dedykowanej stronie w WP Admin):
add_action('wp_enqueue_scripts', function () {
// Ładuj skrypty tylko na stronach produktów WooCommerce
if (is_product()) {
wp_enqueue_script(
'my-plugin-script',
plugin_dir_url(__FILE__) . 'assets/js/product-feature.js',
['jquery'],
'1.0.0',
true // ładuj w stopce (defer/async)
);
wp_enqueue_style(
'my-plugin-style',
plugin_dir_url(__FILE__) . 'assets/css/product-feature.css',
[],
'1.0.0'
);
}
});
B. Wykorzystanie Transients API (Buforowanie Zapytań i API)
Gdy wtyczka odpytuje zewnętrzny serwer (np. serwer licencji Pluger) lub wykonuje ciężkie kalkulacje, wynik należy zawsze buforować:
public function get_external_data(): array {
$cache_key = 'my_plugin_cached_payload';
$cached = get_transient($cache_key);
if (false !== $cached) {
return $cached;
}
// Wykonaj zapytanie tylko gdy brak cache
$response = wp_remote_get('https://api.pluger.pl/v1/resource');
if (is_wp_error($response)) {
return [];
}
$data = json_decode(wp_remote_retrieve_body($response), true);
// Zapisz w pamięci podręcznej na 6 godzin
set_transient($cache_key, $data, 6 * HOUR_IN_SECONDS);
return $data;
}
4. Hooki i Filtry: Kiedy i jak rozszerzać WordPressa
WordPress opiera się na wzorcu Publish-Subscribe (Event-Driven Architecture).
| Typ Hooka | Funkcja | Przeznaczenie |
|---|---|---|
| Action Hook | add_action($tag, $callback) | Wykonanie logiki w określonym momencie cyklu życia WP (np. init, template_redirect, admin_menu). |
| Filter Hook | add_filter($tag, $callback) | Modyfikacja i zwrócenie przekazanej wartości (np. the_content, woocommerce_product_price). |
Złota zasada filtrów: Filtr zawsze musi zwracać wartość (
return $value;). Przerwanie łańcucha filtra bezreturnuszkodzi działanie WordPressa lub innych wtyczek.
5. Dobre praktyki licencjonowania i automatycznych aktualizacji
Tworząc wtyczki komercyjne PRO, kluczowe jest wdrożenie bezpiecznego i bezawaryjnego systemu aktualizacji:
- Brak twardych blokad: Błąd połączenia z serwerem licencji nie może wyłączać działania sklepu klienta (Grace Period / Cache statusu licencji na min. 12-24h).
- Standardowy Auto-Updater: Wtyczka powinna integrować się z natywnym filtrem
site_transient_update_plugins, aby administrator mógł aktualizować ją jednym kliknięciem z poziomu kokpitu WP. - One-Click Connect: Zamiast uciążliwego przepisywania kluczy, udostępnij autoryzację jednym kliknięciem z platformą Pluger.pl.
Podsumowanie i Checklist Inżynieryjny
- Używam przestrzeni nazw (Namespaces) i standardu PSR-12.
- Wszystkie zmienne wejściowe są przepuszczane przez
wp_unslash()oraz odpowiednisanitize_*(). - Każde żądanie formularza i AJAX jest zabezpieczone przez
wp_verify_nonce()orazcurrent_user_can(). - Wszystkie dane wyjściowe są zabezpieczane przez
esc_html(),esc_attr()lubesc_url(). - Skrypty JS i style CSS ładują się tylko na tych stronach, na których są faktycznie niezbędne.
- Ciężkie zapytania i odwołania HTTP są buforowane przez Transients API.
Szukasz gotowej infrastruktury dla swojej wtyczki? Zapoznaj się z architekturą licencjonowania Pluger lub dołącz do programu współpracy deweloperskiej.