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
masterdraait 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.
| Workflow | Draait bij | Jobs | Controles |
|---|---|---|---|
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.5 | WPCS, PHPStan, geautomatiseerde tests |
deploy-all.yml (Deploy to WordPress.org) | push van een v*-tag | WPCS op elk van de 7 PHP-versies, daarna deploy | WPCS, 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:
"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
TypeErrorgooien bij een argument van het verkeerde type, waar 7.4nullteruggaf met een warning. Code die een array aanstrlen()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"istrueop 7.4 enfalseop 8.0, waardoor sommige losse vergelijkingen op optiewaarden ongemerkt omslaan. - PHP 8.0 voegde
str_starts_with()en verwante functies toe. Onze image processor roeptstr_starts_with()aan. Op PHP 7.4 werkt dat alleen omdat WordPress 5.9 en later een polyfill meeleveren, wat onzeRequires at least: 5.9garandeert. - PHP 8.1 markeerde het doorgeven van
nullaan 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$urlrelatief is en de hostnullis. 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

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.
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 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.ValidatedSanitizedInputmarkeert elke uitlezing van$_POST,$_GET,$_REQUESTof$_COOKIEdie niet eerst door unslash en sanitization is gegaan.WordPress.Security.EscapeOutputmarkeert elkeechoofprintvan een waarde die niet door een escapefunctie is gegaan, zoalsesc_html(),esc_attr()ofesc_url().WordPress.Security.NonceVerificationmarkeert een handler die requestdata leest zonder een nonce te verifiëren.WordPress.DB.PreparedSQLenPreparedSQLPlaceholdersmarkeren 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:
<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:
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
parameters: level: 5 paths: - smallpict.php - includes/ - admin/ treatPhpDocTypesAsCertain: false bootstrapFiles: - phpstan-bootstrap.phpLevel 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"](/blog-assets/02_phpcs_phpstan_log.png)
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:

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:
// 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:
$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:
- 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. wp_delete_file()kan sowieso tegen een ontbrekend bestand. De functie stuurt het pad door het filterwp_delete_fileen 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:
-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:
for t in tests/test_.php do echo "== $t" php "$t" exit 1doneEr 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:
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:
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 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.

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:
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 doneOm 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:
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:
- De pre-deploy gate draait alleen WPCS. PHPStan en de geautomatiseerde tests draaien bij pushes naar
masterendevelopen bij pull requests, niet op de release-tag. Beide stappen toevoegen aan delint-job van de deploy-workflow dicht dat gat. - Onze ruleset bevat geen PHPCompatibility. Met
phpcompatibility/phpcompatibility-wpen eentestVersionvan7.4-zouden syntaxis en functies die op 7.4 niet bestaan in één run worden gemarkeerd. - 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.