Naar inhoud springen
smallPict
Gratis Starten

Engineering19 min read

CI/CD voor WordPress-plugins: WPCS, PHPStan en PHP 7.4 tot 8.5

CI/CD voor WordPress-plugins in GitHub Actions: WPCS, PHPStan en geautomatiseerde tests op PHP 7.4 tot 8.5, plus een pre-deploy gate vóór WordPress.org.

smallPict Engineering

Engineering

De WordPress-plugin van smallPict zet uploads om naar WebP en AVIF en levert ze aan bezoekers. Wanneer we een release taggen, commit GitHub Actions die naar de SVN-repository van WordPress.org, en van daaruit bereikt de release elke site waarop de plugin is geïnstalleerd. Sites met automatische updates installeren de release zonder dat iemand de wijziging bekijkt. De plugin declareert Requires PHP: 7.4 en Requires at least: 5.9, dus één build moet draaien op een shared host die nog op PHP 7.4 zit en op een container met PHP 8.5.

Een fatal error in een plugin kan wp-admin platleggen. Een waarde die zonder escaping op een instellingenpagina verschijnt, is een XSS-bug op alle sites tegelijk. Een ontbrekende nonce-controle maakt van een beheeractie een CSRF-doelwit. Geen van deze fouten is stilletjes te herstellen: de fix vraagt om een nieuwe release, en sommige sites blijven op de kapotte versie tot die er is.

Dit artikel beschrijft onze CI/CD-pipeline voor de WordPress-plugin: de WordPress Coding Standards (WPCS), PHPStan en geautomatiseerde tests die op zeven PHP-versies draaien, en de pre-deploy gate vóór WordPress.org. Alles hieronder komt uit de repository smallpict-wp bij release 1.2.9.

Belangrijkste punten

  • Elke pull request naar master draait WPCS, PHPStan en 9 geautomatiseerde testscripts op PHP 7.4, 8.0, 8.1, 8.2, 8.3, 8.4 en 8.5, parallel in GitHub Actions.
  • WPCS dwingt sanitization van invoer, escaping van uitvoer, nonces en prepared queries af. PHPStan op level 5 vindt typefouten en dode branches aan de hand van WordPress-stubs, zonder WordPress uit te voeren.
  • Een release-tag draait WPCS opnieuw op alle zeven PHP-versies, en de job die naar WordPress.org SVN commit start pas als alle zeven geslaagd zijn.

De CI/CD-pipeline van de WordPress-plugin in één oogopslag

Twee GitHub Actions-workflows bewaken de plugin.

WorkflowDraait bijJobsControles
phpcs.yml (PHP Code Quality & Standards)push naar master of develop, pull requests naar masteréén job per PHP-versie: 7.4, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5WPCS, PHPStan, geautomatiseerde tests
deploy-all.yml (Deploy to WordPress.org)push van een v*-tagWPCS op elk van de 7 PHP-versies, daarna deployWPCS, daarna build en SVN-commit

Opmerking over de ondersteunde PHP-versies: De schermafbeeldingen in dit artikel tonen onze eerdere CI-runs voor PHP 7.4 tot en met 8.3. Inmiddels omvat onze testmatrix in GitHub Actions PHP 7.4 tot en met PHP 8.5, zodat wij de voorwaartse compatibiliteit met recente PHP-versies doorlopend bewaken, zonder regressies op oudere omgevingen.

Alle tooling is een dev-dependency van Composer, dus niets ervan komt bij gebruikers terecht:

JSON
"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"}

Matrixtests over meerdere PHP-versies in GitHub Actions

Wat er verandert tussen PHP 7.4 en PHP 8.5

PHP 7.4 en PHP 8.5 voeren dezelfde pluginbroncode verschillend uit. Een paar van de wijzigingen die WordPress-plugincode raken:

  • PHP 8.0 liet veel interne functies een TypeError gooien bij een argument van het verkeerde type, waar 7.4 null teruggaf met een warning. Code die een array aan strlen() doorgaf, bleef op 7.4 draaien en stopt op 8.0.
  • PHP 8.0 veranderde hoe een getal wordt vergeleken met een niet-numerieke string. 0 == "abc" is true op 7.4 en false op 8.0, waardoor sommige losse vergelijkingen op optiewaarden ongemerkt omslaan.
  • PHP 8.0 voegde str_starts_with() en verwante functies toe. Onze image processor roept str_starts_with() aan. Op PHP 7.4 werkt dat alleen omdat WordPress 5.9 en later een polyfill meeleveren, wat onze Requires at least: 5.9 garandeert.
  • PHP 8.1 markeerde het doorgeven van null aan niet-nullable parameters van interne functies als deprecated. strtolower( wp_parse_url( $url, PHP_URL_HOST ) ) is prima op 7.4 en logt op 8.1 een deprecation wanneer $url relatief is en de host null is. Sommige hosts tonen deprecations in de pagina.
  • PHP 8.2 markeerde dynamische properties als deprecated. Een waarde toekennen aan een niet-gedeclareerde property op een van uw eigen klassen logt bij elke request een deprecation.

Elk van deze verschillen komt pas aan het licht wanneer de code wordt geanalyseerd of uitgevoerd op de PHP-versie die zich anders gedraagt. Daarom draait de pipeline elke pull request naar master tegelijk op alle zeven versies.

De GitHub Actions-matrix

CI-run van een WordPress-plugin in GitHub Actions: PHP-matrixtests met vijf parallelle WPCS- en PHPStan-jobs op PHP 7.4, 8.0, 8.1, 8.2 en 8.3, allemaal geslaagd

Figuur 1: De workflow PHP Code Quality & Standards voor de merge van het 1.2.0-werk naar master. Elke PHP-versie is een eigen job, en de PHP 8.3-job toont de stappen die elke job uitvoert: PHPCS, PHPStan en daarna de geautomatiseerde tests.

Opmerking over de ondersteunde PHP-versies: De schermafbeeldingen hierboven tonen onze eerdere CI-runs voor PHP 7.4 tot en met 8.3. Inmiddels omvat onze testmatrix in GitHub Actions PHP 7.4 tot en met PHP 8.5, zodat wij de voorwaartse compatibiliteit met recente PHP-versies doorlopend bewaken, zonder regressies op oudere omgevingen.

YAML
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          done

fail-fast houdt zijn standaardwaarde true, dus als één versie faalt, annuleert GitHub de jobs die nog lopen. Een kapotte pull request kost zo weinig, met als prijs dat u niet het resultaat van elke versie in dezelfde run ziet.

Wat de matrix bewijst, en wat niet

De drie controles halen niet evenveel uit de matrix. PHPStan analyseert de code voor de PHP-versie waarop het draait, en de tests voeren de code op die versie uit, dus beide kunnen per job een ander antwoord geven. PHPCS werkt op tokens en geeft in de praktijk op alle zeven hetzelfde antwoord; in de matrix bewijst het vooral dat de toolchain op elke versie installeert en draait. Syntaxis en functies in één keer controleren tegen een reeks PHP-versies is de taak van PHPCompatibility, dat onze ruleset nog niet bevat.

WordPress Coding Standards (WPCS) automatiseren

De security-sniffs die er voor een plugin toe doen

WPCS is een set PHP_CodeSniffer-sniffs die voor WordPress zijn geschreven. De regels die het zwaarst wegen voor pluginbeveiliging zitten in de WordPress-ruleset zelf:

  • WordPress.Security.ValidatedSanitizedInput markeert elke uitlezing van $_POST, $_GET, $_REQUEST of $_COOKIE die niet eerst door unslash en sanitization is gegaan.
  • WordPress.Security.EscapeOutput markeert elke echo of print van een waarde die niet door een escapefunctie is gegaan, zoals esc_html(), esc_attr() of esc_url().
  • WordPress.Security.NonceVerification markeert een handler die requestdata leest zonder een nonce te verifiëren.
  • WordPress.DB.PreparedSQL en PreparedSQLPlaceholders markeren een query die met stringconcatenatie is opgebouwd in plaats van met $wpdb->prepare(), en placeholders die niet overeenkomen met hun argumenten.

Onze WPCS-ruleset

phpcs.xml.dist voegt WordPress-Extra en WordPress-Docs toe, legt het text domain vast voor de vertaalsniffs en eist dat elke globale functie, klasse en constante ons prefix draagt, zodat de plugin niet kan botsen met de namen van een andere plugin op dezelfde site:

XML
<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>

Een AJAX-handler die door WPCS komt

Deze handler uit de plugin zet media die naar cloudopslag zijn verplaatst in batches terug. Alle plekken waar externe input binnenkomt, worden in de eerste regels afgehandeld:

PHP
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        )    );    // ...}

De nonce en de capability worden samen gecontroleerd, omdat een geldige nonce bewijst dat de request van onze beheerpagina komt, niet dat de gebruiker de actie mag uitvoeren. $_POST['after'] gaat eerst door unslash en wordt met absint() omgezet voordat het de query bereikt, en de query gaat toch door $wpdb->prepare(), ook al is de waarde al een integer.

Onderdrukkingen vragen om een sniffnaam en een reden

De regel phpcs:ignore hierboven is bewust. WPCS waarschuwt voor directe databasequeries zonder object caching, wat goed advies is voor reads aan de front-end en verkeerd voor een beheeractie waarvan de rijen bij elke aanroep veranderen. Onze regel is dat elke ignore de exacte sniff noemt en de reden geeft na --. Een kale // phpcs:ignore komt niet door de review.

In CI schrijft PHPCS een checkstyle-rapport en zet cs2pr dat om in annotaties op de diff van de pull request, zodat een reviewer elke overtreding ziet op de regel die haar veroorzaakte in plaats van in een log.

Logicafouten vinden met statische analyse in PHPStan

WPCS leest code regel voor regel. Het weet dat een echo een escape-aanroep mist, maar niet welk type een variabele bevat of dat een branch ooit kan worden uitgevoerd. PHPStan bouwt een model van de types die door elke functie stromen en meldt code die daarmee in strijd is: een methode aanroepen die niet bestaat, een string|false doorgeven waar een string vereist is, waarden vergelijken die nooit gelijk kunnen zijn, condities die altijd waar of altijd onwaar zijn.

PHPStan gebruiken met WordPress zonder WordPress uit te voeren

szepeviktor/phpstan-wordpress geeft PHPStan via stubbestanden de signatures van de functies en klassen van WordPress core, plus een paar WordPress-specifieke type-extensies. phpstan/extension-installer registreert het pakket automatisch. PHPStan leest die declaraties en laadt nooit WordPress of een database.

Onze PHPStan-configuratie

YAML
parameters:    level: 5    paths:        - smallpict.php        - includes/        - admin/    treatPhpDocTypesAsCertain: false    bootstrapFiles:        - phpstan-bootstrap.php

Level 5 controleert het type van elk argument dat aan een functie of methode wordt doorgegeven, bovenop de controles van de lagere levels op onbekende klassen, methoden en properties, returntypes en dode code. treatPhpDocTypesAsCertain: false zegt tegen PHPStan dat het onze eigen @param-docblocks niet als bewijs mag behandelen. WordPress geeft door wat een filter ook teruggeeft, dus een parameter die als int is gedocumenteerd, kan vanuit een andere plugin toch als string binnenkomen, en we willen dat PHPStan de defensieve controles die dat opvangen laat staan. Het bootstrapbestand definieert de constanten van de plugin (SMALLPICT_PATH, SMALLPICT_URL en andere), die normaal pas tijdens runtime worden gezet.

Log van statische PHPStan-analyse voor WordPress in GitHub Actions: WPCS-stap zonder meldingen, PHPStan analyseerde 11 van 11 pluginbestanden met "[OK] No errors"

Figuur 2: De PHPCS- en PHPStan-stappen van de PHP 8.3-job. PHPCS print niets als er niets te melden is. PHPStan analyseert de 11 pluginbestanden (het hoofdbestand van de plugin en de 10 klassen in includes/) en meldt geen fouten.

Een echte PHPStan-vondst: regel 626 van de image processor

Op 2 oktober 2026 faalde de pipeline op PHPStan met één fout:

PHPStan vangt een bug in een WordPress-plugin: "Right side of && is always true" in includes/class-smallpict-image-processor.php op regel 626, regel booleanAnd.rightAlwaysTrue, job gefaald op PHP 8.2

Figuur 3: De PHP 8.2-job stopte bij de PHPStan-stap met booleanAnd.rightAlwaysTrue op regel 626 van includes/class-smallpict-image-processor.php. Op dat moment had de plugin 9 bestanden in de geanalyseerde paden.

De code is de laatste stap van een conversie. Wanneer de instelling Uitvoerformaat van de plugin op WebP of AVIF staat, wordt het geconverteerde bestand de bijlage en wordt de oorspronkelijke upload verwijderd om schijfruimte te besparen. De andere keuzes, "AVIF, met WebP als reserve" en "Origineel formaat behouden", houden de originele JPEG- en PNG-bestanden. Dit was de verwijderbranch:

PHP
// 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 beweerde dat file_exists( $main_file_path ) op dat punt altijd waar is. De reden staat 150 regels eerder, bovenaan dezelfde methode:

PHP
$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.' );}

De methode gaat alleen verder als het bestand bestaat, en PHPStan neemt dat gegeven mee tot regel 626.

We hebben de melding niet zomaar aangenomen. file_exists() vraagt het bestandssysteem, en het antwoord kan tussen twee aanroepen veranderen. PHPStan onthoudt standaard het resultaat van zo'n aanroep (de optie rememberPossiblyImpureFunctionValues), in de veronderstelling dat er tussendoor niets op de schijf is veranderd. Voordat we de controle verwijderden, hebben we door de code te lezen twee dingen bevestigd:

  1. Niets tussen de guard en regel 626 verwijdert of verplaatst het bronbestand. De conversie schrijft elke output naar een tijdelijk bestand en verplaatst dat naar een nieuw pad, en de enige wp_delete_file()-aanroepen op dat pad verwijderen tijdelijke bestanden. De offload-stap, die wel lokale bestanden verwijdert, draait na regel 626.
  2. wp_delete_file() kan sowieso tegen een ontbrekend bestand. De functie stuurt het pad door het filter wp_delete_file en roept @unlink() aan, wat geen schade doet als het bestand al weg is.

Met beide bevestigd was de conditie dode code, en dode code in een verwijderbranch doet de volgende lezer denken dat het bestand halverwege de methode kan verdwijnen. De fix haalde haar weg:

DIFF
-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 ); }

De vondst was geen crash. De waarde zat in wat ze over de methode zei: de aannames van de code waren afgedreven van haar structuur, uitgerekend in de enige branch die bestanden van gebruikers verwijdert. De les die we eruit trokken is beperkter dan "vertrouw de analyzer". Een melding dat een conditie altijd waar of altijd onwaar is bij een bestandssysteem-, netwerk- of tijdfunctie verdient een handmatige controle van wat er tussen de twee aanroepen gebeurt, voordat de conditie verdwijnt.

Geautomatiseerd testen van een WordPress-plugin zonder PHPUnit

De laatste stap van elke matrixjob voert de bestanden in tests/ uit. Dit zijn geen PHPUnit-tests. Elk bestand is een gewoon PHP-script dat minimale stubs definieert voor de WordPress-functies die het nodig heeft (get_option(), update_option(), sanitize_key() enzovoort), de te testen pluginklasse laadt, die aan het werk zet en bij de eerste fout stopt met een exitcode die niet nul is:

Shell
for t in tests/test_.php do  echo "== $t"  php "$t"  exit 1done

Er zijn er vandaag 9. Ze dekken de paden waar een fout een klant data of geld kost: de WebP- en AVIF-conversiepipeline en de fallbacks daarvan, de levering van afbeeldingen, het offloaden en verwijderen van bestanden in cloudopslag, het in batches terugzetten van verplaatste bestanden, het gedrag wanneer een account stopt met betalen, het opslagquotum en de deïnstallatie. De deïnstallatietest draait de echte deïnstallatiequeries tegen een SQLite-database in het geheugen en controleert wat er overblijft:

PHP
check(    array( '_smallpict_cdn_base', '_smallpict_cdn_key', '_smallpict_offloaded', '_smallpictx', '_wp_attached_file' ) === $meta,    'offload markers and foreign meta remain (got ' . implode( ',', $meta ) . ')');

Die assertion beschermt een beslissing die per ongeluk makkelijk ongedaan wordt gemaakt. Het verwijderen van de plugin wist de instellingen, maar houdt de post meta die vastlegt welke bestanden alleen in cloudopslag staan, zodat een site die de plugin opnieuw installeert zijn afbeeldingen nog kan vinden. Een slordige DELETE ... LIKE '_smallpict%' zou die markeringen wissen, en dan faalt de test.

We kozen voor gewone scripts in plaats van de PHPUnit-testsuite van WordPress vanwege snelheid en setupkosten: ze hebben geen databaseserver en geen WordPress-checkout nodig, en alle 9 draaien in minder dan een seconde, dus ze op zeven PHP-versies draaien voegt bijna niets toe aan een pull request. Het nadeel is dat stubs alleen modelleren wat we erin hebben geschreven. Gedrag dat afhangt van de echte WordPress core, zoals de eigen metadataverwerking van de mediabibliotheek, valt buiten deze tests.

De pre-deploy gate naar WordPress.org

Releases gaan via een aparte workflow de deur uit. Een push van een tag zoals v1.2.9 start die, en de job die naar WordPress.org SVN commit, declareert een afhankelijkheid van de lint-matrix:

YAML
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-org

needs: lint betekent dat de deploy-job pas start als alle zeven lint-jobs slagen. Faalt WPCS op een van de versies, dan slaat GitHub deploy over en vindt er geen SVN-commit plaats. De buildstap draait op PHP 7.4, de oudste versie die we ondersteunen. Het token van de job heeft alleen leestoegang tot de repository; de enige schrijfactie gaat naar WordPress.org SVN, met credentials die als repository secrets zijn opgeslagen.

Pre-deploy gate in de GitHub Actions-workflow Deploy to WordPress.org: WPCS Check geslaagd op PHP 7.4, 8.0, 8.1, 8.2 en 8.3 voordat de Deploy-job draaide

Figuur 4: De workflow Deploy to WordPress.org. De vijf WPCS Check-jobs draaien eerst; de Deploy-job daaronder draait pas als alle vijf geslaagd zijn.

De gate draait WPCS opnieuw op de getagde commit in plaats van te vertrouwen op de eerdere run van de pull request. Een tag kan naar elke commit wijzen, ook naar een commit die direct is gepusht of een waarvan de controles nooit klaar zijn gekomen, en de release-workflow kan op geen andere manier weten wat hij uitlevert.

De CI-controles lokaal draaien

De Composer-scripts verpakken dezelfde commando's die CI draait, zodat u een fout kunt reproduceren voordat u pusht:

Shell
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 done

Om een fout uit één matrixjob te reproduceren, voert u dezelfde commando's uit onder die PHP-versie, bijvoorbeeld met de officiële image na composer install:

Shell
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 herstelt alleen wat een sniff mechanisch kan herstellen. Bij meldingen over escaping, sanitizing en nonces moet een mens beslissen welke functie in de context past, en die laten we niet automatisch herstellen.

Wat de pipeline nog niet dekt

Er staan drie gaten op onze lijst:

  1. De pre-deploy gate draait alleen WPCS. PHPStan en de geautomatiseerde tests draaien bij pushes naar master en develop en bij pull requests, niet op de release-tag. Beide stappen toevoegen aan de lint-job van de deploy-workflow dicht dat gat.
  2. Onze ruleset bevat geen PHPCompatibility. Met phpcompatibility/phpcompatibility-wp en een testVersion van 7.4- zouden syntaxis en functies die op 7.4 niet bestaan in één run worden gemarkeerd.
  3. De tests gebruiken stubs in plaats van een echte WordPress-installatie. Een integratiejob tegen WordPress 5.9 (ons minimum) en de nieuwste release zou het core-gedrag dekken dat de stubs niet kunnen nabootsen.

FAQ

Waarom zijn matrixtests over PHP 7.4 tot 8.5 essentieel voor WordPress-plugins?

WordPress.org levert dezelfde release aan elke site, en de pluginheader (Requires PHP: 7.4) is het enige filter op welke PHP-versies hem installeren. Tussen 7.4 en 8.5 veranderde PHP hoe interne functies omgaan met argumenten van het verkeerde type, hoe getallen met strings worden vergeleken en welke patronen deprecations opleveren, dus code die op de ene versie correct is, kan op een andere een fatal error geven of warnings loggen. Een CI-run op één PHP-versie bewijst alleen dat de plugin op die versie werkt. Door PHPStan en de tests op elke ondersteunde versie te draaien, vangt u die verschillen af voordat de release gebruikers bereikt.

Vervangt WPCS traditionele security-audits?

Nee. WPCS controleert patronen op de regel waar ze voorkomen: een requestvariabele die zonder sanitizing wordt gelezen, een echo zonder escaping, een handler zonder nonce-controle, een query zonder $wpdb->prepare(). Het kan niet beoordelen of een handler de juiste capability controleert, of de escapefunctie bij de outputcontext past, of een server-side request naar een URL gaat die een gebruiker in handen heeft, of dat de ene gebruiker bij de data van een andere kan. Een phpcs:ignore-commentaar kan bovendien een sniff het zwijgen opleggen. Wij zien WPCS als de ondergrens die bekende fouten buiten houdt, en we reviewen beveiligingsrelevante wijzigingen met de hand.

Hoe gaat PHPStan om met functies van WordPress core zonder code uit te voeren?

PHPStan voert de code die het analyseert nooit uit. Het leest declaraties en docblocks om een model van types op te bouwen. Voor WordPress levert szepeviktor/phpstan-wordpress stubbestanden: de signatures, parametertypes en returntypes van de functies en klassen van WordPress core, zonder hun body. PHPStan leest die stubs alsof ze WordPress zijn, en kan dus controleren of een aanroep van wp_delete_file() of get_attached_file() de juiste types doorgeeft en terugkrijgt. Constanten die WordPress of de plugin tijdens runtime definieert, komen in een bootstrapbestand dat PHPStan vóór de analyse laadt.

De plugin staat op WordPress.org als smallPict. Installatie en de instellingen voor het Uitvoerformaat worden behandeld in de installatiehandleiding en de referentie voor afbeeldingsinstellingen.

Geschreven door smallPict Engineering

Engineering

Wij bouwen beeldconversie en CDN-levering voor websites en apps.

Probeer het met uw eigen afbeeldingen.

20 MB per maand gratis. Ongeveer twee minuten instellen in WordPress.

Meer over beeldprestaties, Core Web Vitals en cloudopslag.

Alle artikelen

Cloud & CDN3 min read

SmallPict API/SDK: BYOK vs. Beheerd CDN, wat moet u kiezen?

Ignite heeft geen CDN (resultaten gaan direct terug naar uw app), bij Velocity en Momentum kiest u tussen opslag en CDN van SmallPict (de standaard) of uw eigen opslag en CDN. Zo bepaalt u welk model bij uw project past.

WordPress2 min read

WordPress-beeldoptimalisatie met CDN: wat is inbegrepen en wat is fair use

Betaalde WordPress-abonnementen van SmallPict bevatten snelle lokale levering (Starter) of een optioneel beheerd wereldwijd CDN (Pro en Business). Hier leest u precies wat er is inbegrepen, wat als fair use geldt en hoe onze gedeelde gebruikspool over meerdere sites werkt.