Silver Assist — Quality Checks
This skill covers running, configuring, and troubleshooting the code quality tools used across all Silver Assist WordPress plugins.
When to Use
- Running PHPCS, PHPStan, or PHPUnit checks
- Fixing code style errors or warnings
- Troubleshooting CI/CD quality check failures
- Configuring quality tool settings
- Understanding PHPCS rules and exclusions
- Resolving PHPStan level 8 type errors
Prerequisites
composer installcompleted in the plugin directory- PHP 8.2+ available
- For PHPUnit: WordPress Test Suite installed (or
--skip-wp-setupfor local)
Quick Reference
# Auto-fix formatting.
vendor/bin/phpcbf
# Check code standards (must pass with 0 errors).
vendor/bin/phpcs
# Static analysis (must pass level 8).
vendor/bin/phpstan analyse --memory-limit=1G
# Run tests.
vendor/bin/phpunit
# Run all via composer scripts.
composer test
# Run all via quality script.
./scripts/run-quality-checks.sh all
# Quick local checks (skip WordPress Test Suite setup).
./scripts/run-quality-checks.sh --skip-wp-setup phpcs phpstan
PHPCS (PHP_CodeSniffer)
Configuration
File: phpcs.xml — a thin file across all Silver Assist plugins that references
the shared SilverAssistWP ruleset from silverassist/wp-coding-standards
(require-dev, alongside silverassist/coding-standards) plus the plugin's own
PrefixAllGlobals prefixes:
<rule ref="SilverAssistWP"/>
SilverAssistWP (the shared ruleset) is WordPress-Extra with these exclusions:
Generic.Arrays.DisallowShortArraySyntax— short arrays allowed ([]notarray())WordPress.Files.FileName.NotHyphenatedLowercase— PSR-4 PascalCase filenamesWordPress.Files.FileName.InvalidClassFileName— PSR-4 class filenamesGeneric.Functions.OpeningFunctionBraceKernighanRitchie— K&R brace style allowedGeneric.Classes.OpeningBraceSameLine— same-line braces allowed
Plus, also from the shared ruleset:
WordPress-Docs— PHPDoc coverageGeneric.CodeAnalysis.UnusedFunctionParameterGeneric.Commenting.Todo- PHP compatibility:
8.2- - Min WP version:
6.5
Declared per-plugin, on top of the shared ruleset (it can't live in the shared file):
WordPress.NamingConventions.PrefixAllGlobals— plugin-specific prefixes
See the plugin-creation skill for the full phpcs.xml template.
Common Commands
# Auto-fix what can be fixed.
vendor/bin/phpcbf
# Check all files.
vendor/bin/phpcs
# Check specific file.
vendor/bin/phpcs includes/Core/Plugin.php
# Check with summary report.
vendor/bin/phpcs --report=summary
# Ignore warnings (for CI - only fail on errors).
vendor/bin/phpcs --runtime-set ignore_warnings_on_exit 1
# Show only errors (suppress warnings).
vendor/bin/phpcs --warning-severity=0
Common PHPCS Errors and Fixes
1. Missing global variable prefix
// ❌ ERROR: Non-prefixed global variable.
$autoload_path = PLUGIN_PATH . 'vendor/autoload.php';
// ✅ FIX: Add plugin prefix.
$plugin_prefix_autoload_path = PLUGIN_PREFIX_PATH . 'vendor/autoload.php';
2. Inline comment missing punctuation
// ❌ ERROR: Comment missing period.
// Initialize the component
// ✅ FIX: Add period.
// Initialize the component.
3. Double quotes without interpolation
// ❌ ERROR: Unnecessary double quotes.
$status = "active";
// ✅ FIX: Use single quotes.
$status = 'active';
4. Missing PHPDoc
// ❌ ERROR: Missing doc comment.
public function process(): void {
// ✅ FIX: Add PHPDoc.
/**
* Process the request.
*
* @return void
*/
public function process(): void {
5. Unordered sprintf placeholders
// ❌ ERROR: Non-positional placeholders with multiple args.
sprintf( __( 'Form "%s" has %d submissions', 'text-domain' ), $name, $count );
// ✅ FIX: Use positional placeholders with translator comment.
sprintf(
/* translators: %1$s: form name, %2$d: submission count */
__( 'Form "%1$s" has %2$d submissions', 'text-domain' ),
$name,
$count
);
6. Missing backslash for WordPress functions
// ❌ ERROR in namespaced code (may cause issues).
add_action( 'init', [ $this, 'init' ] );
// ✅ FIX: Backslash prefix.
\add_action( 'init', [ $this, 'init' ] );
PHPCS in CI/CD
CRITICAL: PHPCS warnings (even with 0 errors) cause exit code 1/2 under bash -e. Always use:
vendor/bin/phpcs --runtime-set ignore_warnings_on_exit 1
This ensures the step only fails on actual errors, not warnings.
PHPStan (Static Analysis)
Configuration
File: phpstan.neon. level: 8 is set by the shared
silverassist/coding-standards base config — do not redeclare level: in a
plugin's own phpstan.neon. All three shared files are includes:-ed
directly (relative includes: inside wp-coding-standards/phpstan/base.neon
can't chain to the other two once consumed as a dependency):
includes:
- vendor/silverassist/coding-standards/phpstan/base.neon
- vendor/silverassist/wp-coding-standards/phpstan/base.neon
- vendor/szepeviktor/phpstan-wordpress/extension.neon
parameters:
paths:
- includes
bootstrapFiles:
- plugin-main-file.php
excludePaths:
- tests/*
- build/*
See the plugin-creation skill for the full phpstan.neon template.
Level 8 Requirements
Level 8 is the strictest level. It requires:
- No unused variables
- Strict type checking on all operations
- Full PHPDoc coverage with accurate types
- No
mixedtypes without explicit reason - Correct nullable handling
- Accurate return types
Common Commands
# Standard analysis.
vendor/bin/phpstan analyse --memory-limit=1G
# Analyze specific directory.
vendor/bin/phpstan analyse includes/Service/ --memory-limit=1G --no-progress
# Generate baseline (accept existing errors temporarily).
vendor/bin/phpstan analyse --generate-baseline
# Clear cache (if results seem stale).
vendor/bin/phpstan clear-result-cache
Common PHPStan Errors and Fixes
1. Nullable property access
// ❌ ERROR: Cannot call method on possibly null value.
$this->service->process();
// ✅ FIX: Null guard.
if ( ! $this->service ) {
return;
}
$this->service->process();
// ✅ Alternative: Null-safe operator.
$this->service?->process();
2. Missing return type
// ❌ ERROR: Method has no return type.
public function get_data() {
// ✅ FIX: Add return type.
/**
* Get data.
*
* @return array<string, mixed>
*/
public function get_data(): array {
3. PHPDoc/code type mismatch
// ❌ ERROR: PHPDoc tag @param has invalid type.
/** @param array $items */
public function process( array $items ): void {
// ✅ FIX: Be specific in PHPDoc.
/** @param array<int, string> $items */
public function process( array $items ): void {
4. WordPress function return types
// ❌ ERROR: get_option returns mixed.
$value = \get_option( 'key' );
$this->method_expecting_string( $value );
// ✅ FIX: Type assertion.
$value = \get_option( 'key', '' );
if ( is_string( $value ) ) {
$this->method_expecting_string( $value );
}
Memory Issues
PHPStan defaults to 128MB which is insufficient for most plugins:
# ❌ Crashes at default memory.
vendor/bin/phpstan analyse
# ✅ Increase memory.
vendor/bin/phpstan analyse --memory-limit=1G
# ✅ Or via composer script (already configured).
composer phpstan
PHPUnit
Quick Commands
# Run all tests.
vendor/bin/phpunit
# Run specific suite.
vendor/bin/phpunit --testsuite unit
vendor/bin/phpunit --testsuite integration
# Run specific file.
vendor/bin/phpunit tests/Unit/Core/PluginTest.php
# Run specific method.
vendor/bin/phpunit --filter testMethodName
# With coverage.
vendor/bin/phpunit --coverage-html coverage/
vendor/bin/phpunit --coverage-text
# Human-readable output.
vendor/bin/phpunit --testdox
WordPress Test Suite Required
PHPUnit tests extend WP_UnitTestCase which requires the WordPress Test Suite. If not installed:
Warning: WordPress Test Suite not found. Tests will run with limited functionality.
Install via:
bash scripts/install-wp-tests.sh wordpress_test root 'root' localhost latest true
scripts/install-wp-tests.sh is a ~10-line thin wrapper delegating to the
real implementation in vendor/silverassist/wp-coding-standards/scripts/ —
CLI usage above is unchanged, only the file's own contents changed from a
full standalone script to a wrapper. See the plugin-creation skill for
the exact wrapper contents.
See the testing skill for full details on test patterns and bootstrap.
Quality Check Script
File: scripts/run-quality-checks.sh
This script centralizes all quality checks for consistent execution.
For a brand-new plugin whose quality checks are generic, prefer thin-wrapping
this script too — delegate to
vendor/silverassist/wp-coding-standards/scripts/run-quality-checks.sh, the
same pattern as install-wp-tests.sh above — rather than hand-maintaining
the full script below. Only keep the full local script (and the pattern
described next) when the plugin's checks do genuinely repo-specific extra
setup that the shared script can't do generically — e.g. installing Contact
Form 7, WPGraphQL, or ACF before running PHPUnit.
Usage
# Run all checks (with WordPress Test Suite setup).
./scripts/run-quality-checks.sh all
# Skip WP setup (faster for local development).
./scripts/run-quality-checks.sh --skip-wp-setup all
# Run specific checks only.
./scripts/run-quality-checks.sh phpcs phpstan
./scripts/run-quality-checks.sh phpunit
Script Pattern
CRITICAL: All check functions must return proper exit codes:
run_phpcs() {
print_header "🔍 Running PHPCS"
cd "$PROJECT_ROOT"
# ✅ CORRECT: Capture exit code and return appropriate value.
if vendor/bin/phpcs --warning-severity=0; then
print_success "PHPCS passed - No errors found"
return 0
else
print_error "PHPCS failed - Code style errors found"
return 1
fi
}
Pre-PR Checklist (MANDATORY)
Before every commit/PR, run these checks in order:
# 1. Auto-fix formatting.
vendor/bin/phpcbf
# 2. Check standards (must be 0 errors).
vendor/bin/phpcs
# 3. Static analysis (must pass level 8).
vendor/bin/phpstan analyse --memory-limit=1G
# 4. Run tests (must pass all).
vendor/bin/phpunit
# 5. Update translations.
wp i18n make-pot . languages/plugin-text-domain.pot --domain=plugin-text-domain
Troubleshooting
PHPCS: "phpcs: command not found"
# Install via composer.
composer install
# Use vendor path.
vendor/bin/phpcs
PHPCS: warnings cause CI failure
PHPCS exit codes: 0 = clean, 1 = warnings, 2 = errors. Under bash -e (CI), exit code 1 aborts.
Fix: Add --runtime-set ignore_warnings_on_exit 1 to CI scripts.
PHPStan: "Allowed memory size exhausted"
vendor/bin/phpstan analyse --memory-limit=1G
# or
php -d memory_limit=1G vendor/bin/phpstan analyse
PHPStan: stale results after code changes
vendor/bin/phpstan clear-result-cache
vendor/bin/phpstan analyse --memory-limit=1G
PHPUnit: "Class WP_UnitTestCase not found"
The WordPress Test Suite is not installed. Either:
- Install it:
bash scripts/install-wp-tests.sh wordpress_test root 'root' localhost latest true - Run in CI where it's automatically set up
- Use
--skip-wp-setupflag with the quality check script for local non-test checks
PHPCBF: "No fixable errors were found"
This is success — all auto-fixable issues are already resolved.
CI/CD Integration
Workflow Strategy
| Workflow | WordPress Tests | Time | Purpose |
|---|---|---|---|
ci.yml |
✅ Yes | ~8-10 min | Full integration testing on PRs |
release.yml |
✅ Yes | ~10-12 min | Exhaustive validation before release |
dependency-updates.yml |
❌ No | ~2-3 min | Fast Composer package validation |
Quality Checks in Release Workflow
The release workflow runs PHPCS and PHPStan before building:
- name: Code quality
run: |
HAS_CHECKS=false
if [ -f phpcs.xml ] || [ -f .phpcs.xml.dist ] || [ -f phpcs.xml.dist ]; then
echo "▶ PHPCS"
vendor/bin/phpcs --runtime-set ignore_warnings_on_exit 1
HAS_CHECKS=true
fi
if [ -f phpstan.neon ] || [ -f phpstan.neon.dist ]; then
echo "▶ PHPStan"
vendor/bin/phpstan analyse --no-progress
HAS_CHECKS=true
fi
if [ "$HAS_CHECKS" = false ]; then
echo "ℹ️ No quality tool configs found — skipping"
fi