Wtyczka smallPict dla WordPressa konwertuje przesyłane obrazy do WebP i AVIF i serwuje je odwiedzającym. Gdy oznaczamy wydanie tagiem, GitHub Actions commituje je do repozytorium SVN na WordPress.org, a stamtąd trafia ono do każdej witryny z zainstalowaną wtyczką. Witryny z włączonymi automatycznymi aktualizacjami instalują je, zanim ktokolwiek przejrzy zmianę. Wtyczka deklaruje Requires PHP: 7.4 i Requires at least: 5.9, więc jeden build musi działać zarówno na hostingu współdzielonym, który wciąż ma PHP 7.4, jak i w kontenerze z PHP 8.5.
Błąd krytyczny we wtyczce może położyć wp-admin. Wartość bez escapowania na stronie ustawień to błąd XSS na wszystkich witrynach naraz. Brak weryfikacji nonce zamienia akcję administratora w cel ataku CSRF. Żadnego z tych błędów nie da się załatać po cichu: poprawka wymaga kolejnego wydania, a część witryn zostaje na zepsutej wersji, dopóki ono nie dotrze.
Ten wpis opisuje nasz pipeline CI/CD dla wtyczki WordPress: WordPress Coding Standards (WPCS), PHPStan i testy automatyczne uruchamiane na siedmiu wersjach PHP oraz bramkę przed wdrożeniem na WordPress.org. Wszystko poniżej pochodzi z repozytorium smallpict-wp w wydaniu 1.2.9.
Najważniejsze wnioski
- Każdy pull request do
masteruruchamia w GitHub Actions równolegle WPCS, PHPStan i 9 skryptów testów automatycznych na PHP 7.4, 8.0, 8.1, 8.2, 8.3, 8.4 i 8.5.- WPCS wymusza sanityzację wejścia, escapowanie wyjścia, nonce i przygotowane zapytania. PHPStan na poziomie 5 znajduje błędy typów i martwe gałęzie na podstawie stubów WordPressa, bez uruchamiania WordPressa.
- Tag wydania ponownie uruchamia WPCS na wszystkich siedmiu wersjach PHP, a zadanie, które commituje do SVN na WordPress.org, startuje dopiero wtedy, gdy przejdzie wszystkie siedem.
Pipeline CI/CD wtyczki WordPress w skrócie
Wtyczki pilnują dwa workflow w GitHub Actions.
| Workflow | Uruchamiany przy | Zadania | Sprawdzenia |
|---|---|---|---|
phpcs.yml (PHP Code Quality & Standards) | push do master lub develop, pull requesty do master | jedno zadanie na wersję PHP: 7.4, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5 | WPCS, PHPStan, testy automatyczne |
deploy-all.yml (Deploy to WordPress.org) | push tagu v* | WPCS na każdej z 7 wersji PHP, potem deploy | WPCS, potem build i commit do SVN |
Uwaga o zakresie wersji PHP: Zrzuty ekranu w tym wpisie pokazują nasze wcześniejsze przebiegi CI obejmujące PHP od 7.4 do 8.3. Obecnie macierz testów w GitHub Actions obejmuje PHP od 7.4 do PHP 8.5, co zapewnia stałą zgodność z nowymi wersjami PHP bez regresji w starszych środowiskach.
Wszystkie narzędzia są zależnościami deweloperskimi Composera, więc nic z nich nie trafia do użytkowników:
"require-dev": { "wp-coding-standards/wpcs": "^3.4.1", "phpcsstandards/php_codesniffer": "^3.0", "phpstan/phpstan": "^2.1", "szepeviktor/phpstan-wordpress": "^2.0 || ^1.3.5", "phpstan/extension-installer": "^1.4"}Testy na macierzy wersji PHP w GitHub Actions
Co się zmienia między PHP 7.4 a PHP 8.5
PHP 7.4 i PHP 8.5 wykonują ten sam kod wtyczki w różny sposób. Oto niektóre zmiany, które dotyczą kodu wtyczek WordPress:
- PHP 8.0 sprawił, że wiele funkcji wbudowanych rzuca
TypeErrordla argumentu złego typu, podczas gdy 7.4 zwracałnullz ostrzeżeniem. Kod, który przekazywał tablicę dostrlen(), działał dalej na 7.4, a na 8.0 się zatrzymuje. - PHP 8.0 zmienił sposób porównywania liczby z nienumerycznym ciągiem znaków.
0 == "abc"dajetruena 7.4 ifalsena 8.0, co po cichu odwraca wynik niektórych luźnych porównań na wartościach opcji. - PHP 8.0 dodał
str_starts_with()i pokrewne funkcje. Nasz procesor obrazów wywołujestr_starts_with(). Na PHP 7.4 działa to tylko dlatego, że WordPress 5.9 i nowsze zawierają polyfill, a to gwarantuje naszeRequires at least: 5.9. - PHP 8.1 oznaczył jako przestarzałe przekazywanie
nulldo parametrów funkcji wbudowanych, które nie dopuszczają null.strtolower( wp_parse_url( $url, PHP_URL_HOST ) )działa bez zastrzeżeń na 7.4, a na 8.1 zapisuje w logu deprecation, gdy$urljest względny, a host ma wartośćnull. Niektóre hostingi wypisują komunikaty deprecation prosto na stronę. - PHP 8.2 oznaczył jako przestarzałe właściwości dynamiczne. Przypisanie do niezadeklarowanej właściwości jednej z Twoich własnych klas zapisuje deprecation w logu przy każdym żądaniu.
Każda z tych zmian wychodzi na jaw dopiero wtedy, gdy kod jest analizowany lub wykonywany na wersji PHP, która zachowuje się inaczej. Dlatego pipeline uruchamia każdy pull request do master jednocześnie na wszystkich siedmiu wersjach.
Macierz w GitHub Actions

Rysunek 1: Workflow PHP Code Quality & Standards dla scalenia prac nad 1.2.0 do master. Każda wersja PHP to osobne zadanie, a zadanie PHP 8.3 pokazuje kroki, które wykonuje każde zadanie: PHPCS, PHPStan, a potem testy automatyczne.
Uwaga o zakresie wersji PHP: Powyższe zrzuty ekranu pokazują nasze wcześniejsze przebiegi CI obejmujące PHP od 7.4 do 8.3. Obecnie macierz testów w GitHub Actions obejmuje PHP od 7.4 do PHP 8.5, co zapewnia stałą zgodność z nowymi wersjami PHP bez regresji w starszych środowiskach.
jobs: code-quality: name: WPCS & PHPStan (PHP ${{ matrix.php-version }}) runs-on: ubuntu-26.04 strategy: matrix: php-version: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] steps: - uses: actions/checkout@v7 - uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-version }} tools: composer:v2, cs2pr ini-values: memory_limit=2G coverage: none - run: composer install --prefer-dist --no-progress --no-interaction - name: Run PHPCS (WordPress Coding Standards) run: vendor/bin/phpcs -q --standard=phpcs.xml.dist --report=checkstyle | cs2pr - name: Run PHPStan (WordPress Static Analysis) run: vendor/bin/phpstan analyse --memory-limit=2G - name: Run the standalone tests run: | for t in tests/test_*.php; do echo "== $t" php "$t" || exit 1 donefail-fast zachowuje domyślną wartość true, więc gdy jedna wersja zawiedzie, GitHub anuluje zadania, które jeszcze trwają. Zepsuty pull request kosztuje niewiele, za cenę tego, że w jednym przebiegu nie widać wyników wszystkich wersji.
Czego dowodzi macierz, a czego nie
Trzy sprawdzenia nie zyskują na macierzy w tym samym stopniu. PHPStan analizuje kod pod kątem wersji PHP, na której działa, a testy wykonują kod na tej wersji, więc oba mogą dać inny wynik w każdym zadaniu. PHPCS pracuje na tokenach i w praktyce daje ten sam wynik na wszystkich siedmiu wersjach; w macierzy dowodzi głównie tego, że narzędzia instalują się i działają na każdej wersji. Sprawdzanie składni i funkcji względem zakresu wersji PHP w jednym przebiegu to zadanie PHPCompatibility, którego nasz zestaw reguł jeszcze nie zawiera.
Automatyzacja WordPress Coding Standards (WPCS)
Sniffy bezpieczeństwa ważne dla wtyczki
WPCS to zestaw sniffów PHP_CodeSniffer napisanych dla WordPressa. Reguły najważniejsze dla bezpieczeństwa wtyczki znajdują się w samym zestawie reguł WordPress:
WordPress.Security.ValidatedSanitizedInputzgłasza każdy odczyt$_POST,$_GET,$_REQUESTlub$_COOKIE, który nie przeszedł unslash i sanityzacji.WordPress.Security.EscapeOutputzgłasza każdeecholubprintwartości, która nie przeszła przez funkcję escapującą, taką jakesc_html(),esc_attr()lubesc_url().WordPress.Security.NonceVerificationzgłasza handler, który odczytuje dane żądania bez weryfikacji nonce.WordPress.DB.PreparedSQLiPreparedSQLPlaceholderszgłaszają zapytanie zbudowane przez konkatenację ciągów zamiast$wpdb->prepare()oraz placeholdery, które nie pasują do swoich argumentów.
Nasz zestaw reguł WPCS
phpcs.xml.dist dodaje WordPress-Extra i WordPress-Docs, ustala text domain dla sniffów tłumaczeń i wymaga, aby każda globalna funkcja, klasa i stała miała nasz prefiks. Dzięki temu nazwy wtyczki nie kolidują z nazwami innej wtyczki na tej samej witrynie:
<rule ref="WordPress"> <exclude name="Universal.Arrays.DisallowShortArraySyntax"/></rule><rule ref="WordPress-Extra"/><rule ref="WordPress-Docs"/>
<rule ref="WordPress.WP.I18n"> <properties> <property name="text_domain" type="array"> <element value="smallpict"/> </property> </properties></rule>
<rule ref="WordPress.NamingConventions.PrefixAllGlobals"> <properties> <property name="prefixes" type="array"> <element value="smallpict"/> <element value="SmallPict"/> <element value="SMALLPICT"/> </property> </properties></rule>Handler AJAX, który przechodzi WPCS
Ten handler z wtyczki przywraca partiami media przeniesione przez offload. Każda granica wejścia jest obsłużona w jego pierwszych liniach:
public function ajax_restore_offloaded() { if ( ! check_ajax_referer( 'smallpict_restore_offloaded', 'nonce', false ) || ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => 'forbidden' ), 403 ); } global $wpdb; $after = isset( $_POST['after'] ) ? absint( wp_unslash( $_POST['after'] ) ) : 0; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- admin action, the rows change on every call. $ids = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT post_id FROM {$wpdb->postmeta} WHERE meta_key = %s AND meta_value = %s AND post_id > %d ORDER BY post_id ASC LIMIT %d", '_smallpict_offloaded', '1', $after, self::RESTORE_BATCH ) ); // ...}Nonce i uprawnienie (capability) są sprawdzane razem, bo poprawny nonce dowodzi, że żądanie przyszło z naszej strony administracyjnej, a nie tego, że użytkownik może wykonać akcję. $_POST['after'] przechodzi unslash i rzutowanie przez absint(), zanim trafi do zapytania, a zapytanie i tak przechodzi przez $wpdb->prepare(), choć wartość jest już liczbą całkowitą.
Wyciszenie wymaga nazwy sniffa i powodu
Linia phpcs:ignore powyżej jest celowa. WPCS ostrzega przed bezpośrednimi zapytaniami do bazy danych bez cache'owania obiektów. To dobra rada dla odczytów na froncie, ale zła dla akcji administratora, której wiersze zmieniają się przy każdym wywołaniu. Nasza zasada: każdy ignore podaje dokładną nazwę sniffa i powód po --. Gołe // phpcs:ignore nie przechodzi code review.
W CI PHPCS zapisuje raport checkstyle, a cs2pr zamienia go w adnotacje na diffie pull requesta, więc recenzent widzi każde naruszenie w linii, która je spowodowała, a nie w logu.
Wykrywanie błędów logicznych analizą statyczną PHPStan
WPCS czyta kod linia po linii. Wie, że przy echo brakuje wywołania escapującego, ale nie wie, jaki typ ma zmienna ani czy dana gałąź może się kiedykolwiek wykonać. PHPStan buduje model typów przepływających przez każdą funkcję i zgłasza kod, który mu przeczy: wywołanie nieistniejącej metody, przekazanie string|false tam, gdzie wymagany jest string, porównanie wartości, które nigdy nie mogą być równe, warunki zawsze prawdziwe lub zawsze fałszywe.
PHPStan z WordPressem bez uruchamiania WordPressa
szepeviktor/phpstan-wordpress dostarcza PHPStanowi sygnatury funkcji i klas rdzenia WordPressa w plikach stubów oraz kilka rozszerzeń typów specyficznych dla WordPressa. phpstan/extension-installer rejestruje je automatycznie. PHPStan czyta te deklaracje i nigdy nie ładuje WordPressa ani bazy danych.
Nasza konfiguracja PHPStan
parameters: level: 5 paths: - smallpict.php - includes/ - admin/ treatPhpDocTypesAsCertain: false bootstrapFiles: - phpstan-bootstrap.phpPoziom 5 sprawdza typ każdego argumentu przekazanego do funkcji lub metody, oprócz sprawdzeń z niższych poziomów: nieznanych klas, metod i właściwości, typów zwracanych i martwego kodu. treatPhpDocTypesAsCertain: false mówi PHPStanowi, żeby nie traktował naszych własnych docblocków @param jako dowodu. WordPress przekazuje to, co zwróci filtr, więc parametr udokumentowany jako int może przyjść jako string z innej wtyczki, a my chcemy, żeby PHPStan zostawił sprawdzenia obronne, które to obsługują. Plik bootstrap definiuje stałe wtyczki (SMALLPICT_PATH, SMALLPICT_URL i inne), które normalnie są ustawiane w czasie działania.
![Log analizy statycznej PHPStan dla WordPressa w GitHub Actions: krok WPCS bez uwag, PHPStan przeanalizował 11 z 11 plików wtyczki z wynikiem „[OK] No errors”](/blog-assets/02_phpcs_phpstan_log.png)
Rysunek 2: Kroki PHPCS i PHPStan w zadaniu PHP 8.3. PHPCS nic nie wypisuje, gdy nie ma czego zgłosić. PHPStan analizuje 11 plików wtyczki (główny plik wtyczki i 10 klas w includes/) i nie zgłasza błędów.
Prawdziwe znalezisko PHPStan: linia 626 procesora obrazów
2 października 2026 roku pipeline zatrzymał się na PHPStan z jednym błędem:

Rysunek 3: Zadanie PHP 8.2 zatrzymało się na kroku PHPStan z booleanAnd.rightAlwaysTrue w linii 626 pliku includes/class-smallpict-image-processor.php. Wtedy wtyczka miała 9 plików w analizowanych ścieżkach.
Ten kod to ostatni krok konwersji. Gdy ustawienie wtyczki Format wyjściowy ma wartość WebP lub AVIF, przekonwertowany plik staje się załącznikiem, a oryginalny przesłany plik jest usuwany, żeby oszczędzić miejsce na dysku. Pozostałe opcje, „AVIF, z WebP jako zapasowym” i „Zachowaj oryginalny format”, zachowują oryginalne pliki JPEG i PNG. Tak wyglądała gałąź usuwania:
// C. Pure 1-to-1 Mode: Delete raw uncompressed source file to free 100% hosting space.if ( ( $is_single_webp || $is_single_avif ) && $canonical_path !== $main_file_path && file_exists( $main_file_path ) ) { wp_delete_file( $main_file_path );}PHPStan twierdził, że file_exists( $main_file_path ) jest w tym miejscu zawsze prawdziwe. Powód leży 150 linii wcześniej, na początku tej samej metody:
$main_file_path = get_attached_file( $attachment_id );if ( ! $main_file_path || ! file_exists( $main_file_path ) ) { return new WP_Error( 'file_missing', 'File not found on disk.' );}Metoda działa dalej tylko wtedy, gdy plik istnieje, a PHPStan przenosi ten fakt aż do linii 626.
Nie przyjęliśmy tego znaleziska bez sprawdzenia. file_exists() pyta system plików, a odpowiedź może się zmienić między dwoma wywołaniami. PHPStan domyślnie zapamiętuje wynik takiego wywołania (opcja rememberPossiblyImpureFunctionValues), zakładając, że w międzyczasie nic nie zmieniło stanu dysku. Zanim usunęliśmy sprawdzenie, potwierdziliśmy w kodzie dwie rzeczy:
- Nic między warunkiem ochronnym a linią 626 nie usuwa ani nie przenosi pliku źródłowego. Konwersja zapisuje każdy wynik do pliku tymczasowego i przenosi go pod nową ścieżkę, a jedyne wywołania
wp_delete_file()na tej ścieżce usuwają pliki tymczasowe. Krok offloadu, który rzeczywiście usuwa pliki lokalne, wykonuje się po linii 626. wp_delete_file()i tak toleruje brak pliku. Przepuszcza ścieżkę przez filtrwp_delete_filei wywołuje@unlink(), które nie robi nic szkodliwego, gdy pliku już nie ma.
Skoro oba punkty się potwierdziły, warunek był martwym kodem, a martwy kod w gałęzi usuwania sugeruje następnemu czytelnikowi, że plik może zniknąć w trakcie metody. Poprawka go usunęła:
-if ( ( $is_single_webp || $is_single_avif ) && $canonical_path !== $main_file_path && file_exists( $main_file_path ) ) {+if ( ( $is_single_webp || $is_single_avif ) && $canonical_path !== $main_file_path ) { wp_delete_file( $main_file_path ); }To znalezisko nie dotyczyło awarii. Jego wartość leżała w tym, co mówiło o metodzie: założenia kodu rozjechały się z jego strukturą, i to w jedynej gałęzi, która usuwa pliki użytkowników. Nasz wniosek jest węższy niż „ufaj analizatorowi”. Gdy analizator zgłasza „zawsze prawdziwe” lub „zawsze fałszywe” dla funkcji systemu plików, sieci lub czasu, przed usunięciem warunku trzeba ręcznie sprawdzić, co dzieje się między dwoma wywołaniami.
Testy automatyczne wtyczki WordPress bez PHPUnit
Ostatni krok każdego zadania macierzy uruchamia pliki z tests/. To nie są testy PHPUnit. Każdy plik to zwykły skrypt PHP, który definiuje minimalne stuby potrzebnych mu funkcji WordPressa (get_option(), update_option(), sanitize_key() i tak dalej), ładuje testowaną klasę wtyczki, wykonuje ją i kończy działanie z niezerowym kodem przy pierwszym błędzie:
for t in tests/test_.php do echo "== $t" php "$t" exit 1doneObecnie jest 9 takich skryptów. Obejmują ścieżki, na których pomyłka kosztuje klienta dane lub pieniądze: pipeline konwersji do WebP i AVIF wraz z wariantami rezerwowymi, dostarczanie obrazów, offload i usuwanie plików w magazynie w chmurze, przywracanie przeniesionych plików partiami, zachowanie, gdy konto przestaje płacić, limit przestrzeni i odinstalowanie. Test odinstalowania wykonuje prawdziwe zapytania deinstalacyjne na bazie SQLite w pamięci i sprawdza, co zostało:
check( array( '_smallpict_cdn_base', '_smallpict_cdn_key', '_smallpict_offloaded', '_smallpictx', '_wp_attached_file' ) === $meta, 'offload markers and foreign meta remain (got ' . implode( ',', $meta ) . ')');Ta asercja chroni decyzję, którą łatwo przypadkiem cofnąć. Odinstalowanie wtyczki usuwa jej ustawienia, ale zachowuje post meta, w których zapisano, które pliki istnieją tylko w magazynie w chmurze. Dzięki temu witryna, która ponownie zainstaluje wtyczkę, nadal znajdzie swoje obrazy. Nieostrożne DELETE ... LIKE '_smallpict%' usunęłoby te znaczniki, a wtedy test nie przejdzie.
Wybraliśmy zwykłe skrypty zamiast zestawu testów PHPUnit dla WordPressa ze względu na szybkość i koszt konfiguracji: nie potrzebują serwera bazy danych ani kopii WordPressa, a wszystkie 9 wykonuje się w mniej niż sekundę, więc uruchomienie ich na siedmiu wersjach PHP prawie nic nie dodaje do pull requesta. Kosztem jest to, że stuby modelują tylko to, co w nich zapisaliśmy. Zachowanie zależne od prawdziwego rdzenia WordPressa, na przykład własna obsługa metadanych w bibliotece mediów, nie jest objęte tymi testami.
Bramka przed wdrożeniem na WordPress.org
Wydania wychodzą przez osobny workflow. Uruchamia go push tagu, na przykład v1.2.9, a zadanie, które commituje do SVN na WordPress.org, deklaruje zależność od macierzy lintowania:
jobs: lint: name: WPCS Check (PHP ${{ matrix.php-version }}) runs-on: ubuntu-26.04 strategy: matrix: php-version: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] steps: - uses: actions/checkout@v7 - uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-version }} - run: composer install --prefer-dist --no-progress --no-interaction - name: PHP CodeSniffer Check run: vendor/bin/phpcs --standard=phpcs.xml.dist
deploy: name: Deploy needs: lint runs-on: ubuntu-26.04 permissions: contents: read steps: - uses: actions/checkout@v7 - uses: shivammathur/setup-php@v2 with: php-version: '7.4' - name: Build WP.org Version run: | chmod +x build-wporg.sh ./build-wporg.sh - name: Deploy to WordPress.org uses: 10up/action-wordpress-plugin-deploy@stable with: generate-zip: false env: SVN_USERNAME: ${{ secrets.SVN_USERNAME }} SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} SLUG: smallpict BUILD_DIR: build-wporg/smallpict ASSETS_DIR: .wordpress-orgneeds: lint oznacza, że zadanie deploy startuje dopiero wtedy, gdy wszystkie siedem zadań lint zakończy się sukcesem. Jeśli WPCS zawiedzie na którejkolwiek wersji, GitHub pomija deploy i commit do SVN nie następuje. Krok budowania działa na PHP 7.4, najstarszej wspieranej przez nas wersji. Token zadania ma dostęp do repozytorium tylko do odczytu; jedyny zapis trafia do SVN na WordPress.org, z danymi logowania przechowywanymi jako sekrety repozytorium.

Rysunek 4: Workflow Deploy to WordPress.org. Najpierw uruchamia się pięć zadań WPCS Check; zadanie Deploy pod nimi startuje dopiero, gdy przejdą wszystkie pięć.
Bramka ponownie uruchamia WPCS na otagowanym commicie, zamiast ufać wcześniejszemu przebiegowi dla pull requesta. Tag może wskazywać dowolny commit, w tym wypchnięty bezpośrednio albo taki, którego sprawdzenia nigdy się nie zakończyły, a workflow wydania nie ma innego sposobu, by wiedzieć, co wypuszcza.
Lokalne uruchamianie sprawdzeń CI
Skrypty Composera opakowują te same polecenia, które uruchamia CI, więc błąd można odtworzyć przed pushem:
composer installcomposer lint # vendor/bin/phpcs --standard=phpcs.xml.distcomposer lint-fix # vendor/bin/phpcbf: fixes spacing and formatting sniffscomposer phpstan # vendor/bin/phpstan analysefor t in tests/test_.php do php "$t" break doneAby odtworzyć błąd z jednego zadania macierzy, uruchom te same polecenia pod tą wersją PHP, na przykład w oficjalnym obrazie po composer install:
docker run --rm -v "$PWD":/app -w /app php:7.4-cli \ sh -c 'vendor/bin/phpstan analyse --memory-limit=2G && for t in tests/test_*.php; do php "$t" || exit 1; done'phpcbf naprawia tylko to, co sniff może poprawić mechanicznie. W przypadku znalezisk dotyczących escapowania, sanityzacji i nonce człowiek musi zdecydować, która funkcja pasuje do kontekstu, i nie poprawiamy ich automatycznie.
Czego pipeline jeszcze nie obejmuje
Na naszej liście są trzy luki:
- Bramka przed wdrożeniem uruchamia tylko WPCS. PHPStan i testy automatyczne działają przy pushach do
masterideveloporaz przy pull requestach, ale nie przy tagu wydania. Lukę zamknie dodanie obu kroków do zadanialintw workflow wdrożenia. - Nasz zestaw reguł nie zawiera PHPCompatibility. Dodanie
phpcompatibility/phpcompatibility-wpztestVersionustawionym na7.4-pozwoliłoby w jednym przebiegu zgłaszać składnię i funkcje, które nie istnieją w 7.4. - Testy używają stubów zamiast prawdziwej instalacji WordPressa. Zadanie integracyjne na WordPressie 5.9 (nasze minimum) i najnowszym wydaniu obejmie zachowanie rdzenia, którego stuby nie odwzorują.
Najczęstsze pytania
Dlaczego testy na macierzy PHP od 7.4 do 8.5 są kluczowe dla wtyczek WordPress?
WordPress.org dostarcza to samo wydanie do każdej witryny, a nagłówek wtyczki (Requires PHP: 7.4) jest jedynym filtrem tego, na jakich wersjach PHP się ona instaluje. Między 7.4 a 8.5 PHP zmienił to, jak funkcje wbudowane traktują argumenty złego typu, jak liczby porównują się z ciągami znaków i które konstrukcje zgłaszają deprecation, więc kod poprawny na jednej wersji może na innej skończyć się błędem krytycznym lub ostrzeżeniami w logu. Przebieg CI na jednej wersji PHP dowodzi tylko tego, że wtyczka działa na tej wersji. Uruchamianie PHPStan i testów na każdej wspieranej wersji wyłapuje te różnice, zanim wydanie dotrze do użytkowników.
Czy WPCS zastępuje tradycyjny audyt bezpieczeństwa?
Nie. WPCS sprawdza wzorce w linii, w której występują: zmienną żądania odczytaną bez sanityzacji, echo bez escapowania, handler bez sprawdzenia nonce, zapytanie bez $wpdb->prepare(). Nie potrafi ocenić, czy handler sprawdza właściwe uprawnienie, czy funkcja escapująca pasuje do kontekstu wyjścia, czy żądanie po stronie serwera trafia pod URL kontrolowany przez użytkownika ani czy jeden użytkownik może dostać się do danych innego. Komentarz phpcs:ignore może też wyciszyć sniff. Traktujemy WPCS jako dolną granicę, która nie przepuszcza znanych błędów, a zmiany istotne dla bezpieczeństwa przeglądamy ręcznie.
Jak PHPStan obsługuje funkcje rdzenia WordPressa bez wykonywania kodu?
PHPStan nigdy nie uruchamia analizowanego kodu. Czyta deklaracje i docblocki, aby zbudować model typów. Dla WordPressa szepeviktor/phpstan-wordpress dostarcza pliki stubów: sygnatury, typy parametrów i typy zwracane funkcji i klas rdzenia WordPressa, bez ich ciał. PHPStan czyta te stuby tak, jakby były WordPressem, więc może sprawdzić, czy wywołanie wp_delete_file() lub get_attached_file() przekazuje i otrzymuje właściwe typy. Stałe, które WordPress lub wtyczka definiują w czasie działania, trafiają do pliku bootstrap, który PHPStan ładuje przed analizą.
Wtyczka jest dostępna na WordPress.org jako smallPict. Instalację i ustawienia „Format wyjściowy” opisują przewodnik instalacji i dokumentacja ustawień obrazów.