WordPress PHP plugin development using Test-Driven Development (TDD) and functional programming principles. Use this skill when developing, testing, or maintaining WordPress plugins with focus on PSR-12 compliance, Composer integration, automated testing with PHPUnit, coding standards enforcement with phpcs/phpcbf, and git version control. Triggers include requests to create WordPress plugins, write WordPress PHP code, set up testing infrastructure, implement TDD workflows, or apply functional programming patterns in WordPress.
67
80%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./wordpress-php-dev/SKILL.mdThis skill provides comprehensive guidance for developing WordPress plugins following professional development practices:
Use the initialization script to create a complete plugin structure:
python3 scripts/init_plugin.py my-plugin-name --path ~/projects
cd ~/projects/my-plugin-name
composer installThis creates a plugin with:
composer lint:fixgit commit -m "feat(module): add functionality"# 1. Write failing test
# Edit tests/Unit/FeatureTest.php
composer test # Should fail (RED)
# 2. Commit failing test
git add tests/Unit/FeatureTest.php
git commit -m "test(feature): add test for new behavior"
# 3. Write minimal code to pass
# Edit src/Module.php
composer test # Should pass (GREEN)
# 4. Auto-fix coding standards
composer lint:fix
# 5. Commit working code
git add src/Module.php
git commit -m "feat(feature): implement new behavior"
# 6. Refactor if needed
# Edit src/Module.php
composer test # Should still pass
composer lint:fix
# 7. Commit refactoring
git add src/Module.php
git commit -m "refactor(feature): improve implementation"composer test # Run all tests
composer test:coverage # Generate coverage report
composer lint # Check coding standards
composer lint:fix # Auto-fix coding standards
composer lint:errors # Show only errors, skip warningsplugin-name/
├── plugin-name.php # Main plugin file
├── composer.json # Dependencies and scripts
├── phpcs.xml.dist # Coding standards config
├── phpunit.xml.dist # Testing configuration
├── .gitignore # Git ignore rules
├── README.md # Documentation
├── src/ # Source code (PSR-4 autoloaded)
│ ├── Plugin.php # Main plugin class
│ └── functions.php # Pure helper functions
├── tests/ # PHPUnit tests
│ ├── bootstrap.php # Test setup
│ ├── Unit/ # Unit tests (pure functions)
│ └── Integration/ # Integration tests (WordPress hooks)
└── assets/ # Frontend assets
├── css/
├── js/
└── images/Read the TDD patterns reference for comprehensive examples:
view references/tdd_patterns.mdKey patterns covered:
Read the functional programming reference for detailed patterns:
view references/functional_patterns.mdKey concepts covered:
Read the git workflow reference for complete workflow:
view references/git_workflow.mdKey practices:
Pure Functions (Easy to test, in src/functions.php):
// Pure - deterministic transformation
function sanitize_username( string $username ): string {
return strtolower( trim( $username ) );
}
// Pure - data validation
function is_valid_email( string $email ): bool {
return (bool) filter_var( $email, FILTER_VALIDATE_EMAIL );
}Impure Functions (Isolate in classes/hooks):
// Impure - database access
function get_user_preferences( int $user_id ): array {
return get_user_meta( $user_id, 'preferences', true );
}
// Impure - output
function render_template( string $template, array $data ): void {
extract( $data );
include $template;
}Example unit test for pure function:
public function test_sanitize_username_lowercase(): void {
$result = sanitize_username( 'JohnDoe' );
$this->assertEquals( 'johndoe', $result );
}Example integration test with Brain Monkey:
use Brain\Monkey\Functions;
public function test_saves_user_preference(): void {
Functions\expect( 'update_user_meta' )
->once()
->with( 123, 'theme', 'dark' )
->andReturn( true );
$result = save_user_preference( 123, 'theme', 'dark' );
$this->assertTrue( $result );
}The plugin enforces:
Update in phpcs.xml.dist:
<rule ref="WordPress.WP.I18n">
<properties>
<property name="text_domain" type="array">
<element value="your-plugin-slug"/>
</property>
</properties>
</rule>
<rule ref="WordPress.NamingConventions.PrefixAllGlobals">
<properties>
<property name="prefixes" type="array">
<element value="your_plugin_prefix"/>
</property>
</properties>
</rule>Always run before committing:
composer lint:fixThis automatically fixes:
Copy and customize these templates:
assets/composer.json.distassets/phpcs.xml.distassets/phpunit.xml.distassets/bootstrap.php.distOr use the initialization script:
python3 scripts/init_plugin.py my-plugin --path /path/to/directorytest_returns_error_when_email_invalid()composer lint:fix before committingcomposer testcomposer lint:fixcomposer test:coveragegit diff --staged// Main plugin file
add_action( 'plugins_loaded', function() {
Plugin::get_instance()->init();
} );
// Plugin class
class Plugin {
private static ?Plugin $instance = null;
public static function get_instance(): Plugin {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
public function init(): void {
$this->register_hooks();
}
private function register_hooks(): void {
add_action( 'init', [ $this, 'on_init' ] );
add_filter( 'the_content', [ $this, 'filter_content' ] );
}
}// Pure function - easy to test
function apply_custom_discount( float $price, float $rate ): float {
return $price * ( 1 - $rate );
}
// WordPress filter using pure function
add_filter( 'product_price', function( $price ) {
return apply_custom_discount( $price, 0.1 );
} );
// Test is simple
public function test_applies_discount(): void {
$result = apply_custom_discount( 100.0, 0.1 );
$this->assertEquals( 90.0, $result );
}// Compose transformations
function process_user_input( string $input ): string {
return pipe(
fn( $s ) => trim( $s ),
fn( $s ) => strtolower( $s ),
fn( $s ) => sanitize_text_field( $s )
)( $input );
}
// Helper for function composition
function pipe( ...$functions ) {
return fn( $value ) => array_reduce(
$functions,
fn( $carry, $fn ) => $fn( $carry ),
$value
);
}# Ensure dependencies installed
composer install
# Clear cache
rm -rf .phpunit.cache
# Run with verbose output
vendor/bin/phpunit --verbose# Auto-fix most issues
composer lint:fix
# Check what can't be auto-fixed
composer lint:errors
# View detailed error
composer lintEnsure proper setup/teardown in tests:
protected function setUp(): void {
parent::setUp();
\Brain\Monkey\setUp();
}
protected function tearDown(): void {
\Brain\Monkey\tearDown();
parent::tearDown();
}references/tdd_patterns.md - Comprehensive testing patternsreferences/functional_patterns.md - FP in WordPress contextreferences/git_workflow.md - Complete version control workflow1721217
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.