Analyze Your Code Automatically with PHPSTAN
In WordPress projects, maintaining clean and error-free code is essential to ensure quality, security, and scalability. Tools like PHPStan (static analysis) and PHPLint (syntax validation) easily integrate into your automated testing, helping you detect issues before they reach production.
In this post, you will learn how to add both tools to your WordPress tests step by step.
Why use PHPStan?
- PHPStan: analyzes your code without executing it, detecting type errors, calls to non-existent methods, and bad practices.
- Both tools improve the quality of the code and facilitate collaboration in teams of WordPress development.
Installation with Composer
Make sure you have Composer installed in your development environment. Then add the dependencies:
composer require --dev phpstan/phpstan wp-coding-standards/wpcs szepeviktor/phpstan-wordpress phpstan/extension-installer
This will install both tools inside your folder vendor/.
PHPStan Configuration
- Create the file
phpstan.neon.distin the root of the project (include WooCommerce):
parameters:
level: 1
paths:
- includes
bootstrapFiles:
- tests/phpstan-bootstrap.php
excludePaths:
- vendor/
- node_modules (?)
Example phpstan
<?php
/**
* PHPStan Bootstrap File
*
* This file defines constants and functions that PHPStan needs to understand
* but are not available during static analysis.
*/
// Define plugin constants that are used throughout the codebase
if (!defined('CONHOLD_PLUGIN_URL')) {
define('CONHOLD_PLUGIN_URL', 'http://localhost/wp-content/plugins/connect-ecommerce/');
}
if (!defined('CONHOLD_VERSION')) {
define('CONHOLD_VERSION', '1.0.0');
}
if (!defined('CONHOLD_FILE')) {
define('CONHOLD_FILE', __FILE__);
}
// Define WordPress constants that might be missing
if (!defined('DOING_AJAX')) {
define('DOING_AJAX', false);
}
if (!defined('WP_DEBUG')) {
define('WP_DEBUG', false);
}
if (!defined('ABSPATH')) {
define('ABSPATH', '/path/to/wordpress/');
}
// Mock WordPress functions that PHPStan can't find
if (!function_exists('wp_doing_ajax')) {
function wp_doing_ajax() {
return defined('DOING_AJAX') && DOING_AJAX;
}
}
if (!function_exists('CONHOLD_get_options')) {
function CONHOLD_get_options() {
return [];
}
}
// Mock Action Scheduler function
if (!function_exists('as_schedule_recurring_action')) {
function as_schedule_recurring_action($timestamp, $interval_in_seconds, $hook, $args = [], $group = '') {
return true;
}
}
// Mock WP_CLI class
if (!class_exists('WP_CLI')) {
class WP_CLI {
public static function line($message) {
echo $message . "\n";
}
public static function add_command($command, $class) {
return true;
}
}
}
Configure composer.json:
"scripts": {
"format": "phpcbf --standard=phpcs.xml.dist",
"lint": "phpcs --standard=phpcs.xml.dist",
"phpstan": "phpstan analyse --memory-limit=2048M"
}
- Run the analysis:
composer phpstan
Configuration with WooCommerce
composer require --dev php-stubs/wordpress-stubs php-stubs/woocommerce-stubs
And add the include in the configuration file
parameters:
bootstrapFiles:
- vendor/php-stubs/wordpress-stubs/wordpress-stubs.php
- vendor/php-stubs/woocommerce-stubs/woocommerce-stubs.php
#- vendor/php-stubs/woocommerce-stubs/woocommerce-packages-stubs.php
PHPLint Configuration
- Create a file
.phplint.yml:
path: .
jobs: 10
extensions:
- php
- Run the analysis:
vendor/bin/phplint
Integration with PHPUnit and GitHub Actions
You can add both checks in your CI/CD pipelines so that every commit or pull request goes through these validations.
And add the file with your own Coding Standards rules:
<?xml version="1.0"?>
<ruleset name="Coding Standards for Internal Scanner Tool">
<description>Description of Plugin.</description>
<exclude-pattern>*/vendor/*</exclude-pattern>
<exclude-pattern>reports/*</exclude-pattern>
<exclude-pattern>php/tests/*</exclude-pattern>
<exclude-pattern>php/prt_phpunit/*</exclude-pattern>
<arg value="ps"/>
<arg name="extensions" value="php"/>
<file>./plugin.php</file>
<file>./includes</file>
<file>./templates</file>
<rule ref="WordPress">
<exclude name="Universal.Arrays.DisallowShortArraySyntax"/>
<exclude name="WordPress.DB" />
<exclude name="WordPress.Files.FileName"/>
<exclude name="WordPress.NamingConventions.ValidFunctionName" />
</rule>
<rule ref="WordPress.WP.I18n">
<properties>
<property name="text_domain" type="array" value="plugin-name" />
</properties>
</rule>
<rule ref="WordPress.Utils.I18nTextDomainFixer">
<properties>
<property name="old_text_domain" type="array">
<element value="" />
</property>
<property name="new_text_domain" value="plugin-name" />
</properties>
</rule>
</ruleset>
This way, when running composer format or composer lint, PHPUnit, PHPStan, and PHPLint will run together.
Add in .github/workflows/php-lint.yml
name: PHP Code Linting
on:
push:
branches:
- trunk
- 'release/**'
# Only run if PHP-related files changed.
paths:
- '.github/workflows/php-lint.yml'
- '**.php'
- 'phpcs.xml.dist'
- 'composer.json'
- 'composer.lock'
pull_request:
branches:
- trunk
- 'release/**'
- 'feature/**'
# Only run if PHP-related files changed.
paths:
- '.github/workflows/php-lint.yml'
- '**.php'
- 'phpcs.xml.dist'
- 'composer.json'
- 'composer.lock'
types:
- opened
- reopened
- synchronize
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/trunk' }}
jobs:
php-lint:
name: PHP
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.0'
- name: Validate Composer configuration
run: composer validate
- name: Install PHP dependencies
uses: ramsey/composer-install@a2636af0004d1c0499ffca16ac0b4cc94df70565
with:
composer-options: '--prefer-dist'
- name: PHP Lint
run: composer lint
- name: PHP PHPStan
run: composer phpstan
Conclusion
Integrating PHPStan and PHPLint into your WordPress TESTs is an easy way to elevate the quality of development, detect errors early, and improve the robustness of your plugins and themes.
If you want to take your project to the next level, start by adding these tools to your workflow. 🚀👉 Do you want me to prepare this article in complete markdown format ready to publish on WordPress (with headings h2, h3, code blocks, and links to the tools), or would you prefer I leave it in plain text for you to adapt?
References:
- PHPStan in WordPress https://pascalbirchler.com/phpstan-wordpress/
Leave a Reply