WordPress plugin development with hooks, security, REST API, custom post types. Use for plugin creation, $wpdb queries, Settings API, or encountering SQL injection, XSS, CSRF, nonce errors.
npx skills add https://github.com/secondsky/claude-skills --skill wordpress-plugin-core
Status: Production Ready
Last Updated: 2025-11-27
Dependencies: None (WordPress 5.9+, PHP 7.4+)
Latest Versions: WordPress 6.7+, PHP 8.0+ recommended
Three architecture patterns available (see references/plugin-architectures.md for detailed examples):
Every plugin MUST have a header comment in the main file:
<?php
/**
* Plugin Name: My Awesome Plugin
* Description: Brief description.
* Version: 1.0.0
* Requires at least: 5.9
* Requires PHP: 7.4
* Text Domain: my-plugin
*/
if ( ! defined( 'ABSPATH' ) ) exit;
CRITICAL: Plugin Name is required, Text Domain must match plugin slug exactly.
// 1. Unique Prefix (4-5 chars)
function mypl_init() { /* code */ }
add_action( 'init', 'mypl_init' );
// 2. ABSPATH Check (every file)
if ( ! defined( 'ABSPATH' ) ) exit;
// 3. Nonces for Forms
wp_nonce_field( 'mypl_action', 'mypl_nonce' );
// 4. Sanitize Input, Escape Output
$clean = sanitize_text_field( $_POST['input'] );
echo esc_html( $output );
// 5. Prepared Statements
$wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}table WHERE id = %d", $id ) );
Rules: 4-5 chars minimum, apply to functions, classes, constants, options, transients, meta keys. Avoid wp_, __, _.
// GOOD
function mypl_init() {}
class MyPL_Settings {}
add_option( 'mypl_option', 'value' );
// BAD - Will conflict
function init() {}
class Settings {}
// WRONG
if ( is_admin() ) { /* SECURITY HOLE */ }
// CORRECT
if ( current_user_can( 'manage_options' ) ) { /* Secure */ }
Common Capabilities: manage_options (Admin), edit_posts (Editor), publish_posts (Author)
Input → Processing → Output (Sanitize → Validate → Escape):
// SANITIZATION (Input)
$name = sanitize_text_field( $_POST['name'] );
$email = sanitize_email( $_POST['email'] );
$url = esc_url_raw( $_POST['url'] );
$html = wp_kses_post( $_POST['content'] );
// VALIDATION (Logic)
if ( ! is_email( $email ) ) wp_die( 'Invalid email' );
// ESCAPING (Output)
echo esc_html( $name );
echo '<a href="' . esc_url( $url ) . '">' . esc_html( $text ) . '</a>';
Rule: Sanitize INPUT, escape OUTPUT. Never trust user data.
One-time tokens proving requests came from your site.
// Form
<form method="post">
<?php wp_nonce_field( 'mypl_action', 'mypl_nonce' ); ?>
<input type="text" name="data" />
</form>
// Verify
if ( ! wp_verify_nonce( $_POST['mypl_nonce'], 'mypl_action' ) ) wp_die( 'Security check failed' );
// AJAX
check_ajax_referer( 'mypl-ajax-nonce', 'nonce' );
CRITICAL: Always use $wpdb->prepare() for user input.
// WRONG - SQL Injection
$wpdb->get_results( "SELECT * FROM {$wpdb->prefix}table WHERE id = {$_GET['id']}" );
// CORRECT
$wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}table WHERE id = %d", $_GET['id'] ) );
Placeholders: %s (String), %d (Integer), %f (Float)
LIKE Queries: Use $wpdb->esc_like() before adding wildcards:
$search = '%' . $wpdb->esc_like( $term ) . '%';
$wpdb->get_results( $wpdb->prepare( "... WHERE title LIKE %s", $search ) );
✅ Use unique prefix (4-5 chars) for all global code (functions, classes, options, transients)
✅ Add ABSPATH check to every PHP file: if ( ! defined( 'ABSPATH' ) ) exit;
✅ Check capabilities (current_user_can()) not just is_admin()
✅ Verify nonces for all forms and AJAX requests
✅ Use $wpdb->prepare() for all database queries with user input
✅ Sanitize input with sanitize_*() functions before saving
✅ Escape output with esc_*() functions before displaying
✅ Flush rewrite rules on activation when registering custom post types
✅ Use uninstall.php for permanent cleanup (not deactivation hook)
✅ Follow WordPress Coding Standards (tabs for indentation, Yoda conditions)
❌ Never use extract() - Creates security vulnerabilities
❌ Never trust $_POST/$_GET without sanitization
❌ Never concatenate user input into SQL - Always use prepare()
❌ Never use is_admin() alone for permission checks
❌ Never output unsanitized data - Always escape
❌ Never use generic function/class names - Always prefix
❌ Never use short PHP tags <? or <?= - Use <?php only
❌ Never delete user data on deactivation - Only on uninstall
❌ Never register uninstall hook repeatedly - Only once on activation
❌ Never use register_uninstall_hook() in main flow - Use uninstall.php instead
This skill prevents 20 documented issues:
Error: Database compromised via unescaped user input
Source: https://patchstack.com/articles/sql-injection/ (15% of all vulnerabilities)
Why It Happens: Direct concatenation of user input into SQL queries
Prevention: Always use $wpdb->prepare() with placeholders
// VULNERABLE
$wpdb->query( "DELETE FROM {$wpdb->prefix}table WHERE id = {$_GET['id']}" );
// SECURE
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}table WHERE id = %d", $_GET['id'] ) );
Error: Malicious JavaScript executed in user browsers
Source: https://patchstack.com (35% of all vulnerabilities)
Why It Happens: Outputting unsanitized user data to HTML
Prevention: Always escape output with context-appropriate function
// VULNERABLE
echo $_POST['name'];
echo '<div class="' . $_POST['class'] . '">';
// SECURE
echo esc_html( $_POST['name'] );
echo '<div class="' . esc_attr( $_POST['class'] ) . '">';
Error: Unauthorized actions performed on behalf of users
Source: https://blog.nintechnet.com/25-wordpress-plugins-vulnerable-to-csrf-attacks/
Why It Happens: No verification that requests originated from your site
Prevention: Use nonces with wp_nonce_field() and wp_verify_nonce()
// VULNERABLE
if ( $_POST['action'] == 'delete' ) {
delete_user( $_POST['user_id'] );
}
// SECURE
if ( ! wp_verify_nonce( $_POST['nonce'], 'mypl_delete_user' ) ) {
wp_die( 'Security check failed' );
}
delete_user( absint( $_POST['user_id'] ) );
Error: Regular users can access admin functions
Source: WordPress Security Review Guidelines
Why It Happens: Using is_admin() instead of current_user_can()
Prevention: Always check capabilities, not just admin context
// VULNERABLE
if ( is_admin() ) {
// Any logged-in user can trigger this
}
// SECURE
if ( current_user_can( 'manage_options' ) ) {
// Only administrators can trigger this
}
Error: PHP files executed outside WordPress context
Source: WordPress Plugin Handbook
Why It Happens: No ABSPATH check at top of file
Prevention: Add ABSPATH check to every PHP file
// Add to top of EVERY PHP file
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
For comprehensive error coverage beyond the Top 5, load references/error-catalog.md which includes:
Each issue includes: error description, source, why it happens, prevention code, impact severity, and frequency.
Choose the right architecture for your plugin size and complexity:
Simple (Functions Only)
OOP (Singleton Pattern)
PSR-4 (Namespaced + Composer)
For full implementation examples with directory structure, activation hooks, and code patterns, load references/plugin-architectures.md.
This skill provides production-ready patterns for 8 common WordPress plugin features:
For complete implementation code, load references/common-patterns.md when implementing any of these features. Each pattern includes:
Plugins hosted outside WordPress.org can provide automatic updates using Plugin Update Checker by YahnisElsts (recommended).
Quick Solutions:
For complete implementation, load references/github-auto-updates.md which includes:
Required:
Optional:
Fatal Errors: Enable WP_DEBUG, check wp-content/debug.log, verify prefixed names
404 on CPTs: Flush rewrite rules (add flush_rewrite_rules(); temporarily in wp-admin)
Nonce Failures: Check matching names, correct action, 24-hour expiration
AJAX Returns 0/-1: Verify action name matches wp_ajax_{action}, nonce sent/verified, handler hooked
HTML Stripped: Use wp_kses_post() instead of sanitize_text_field()
DB Queries Fail: Always use $wpdb->prepare(), include $wpdb->prefix, verify syntax
This skill uses progressive disclosure - main file contains essentials, reference files have detailed implementation. Load references based on your current task:
references/common-patterns.md (465 lines)Load when: Implementing specific WordPress features
Contains:
references/plugin-architectures.md (220 lines)Load when: Choosing plugin structure or migrating between patterns
Contains:
references/error-catalog.md (Issues #6-20, 573 lines)Load when: Debugging issues beyond Top 5 security vulnerabilities
Contains:
Each issue includes: Error description, source/reference, why it happens, prevention code, impact severity, frequency
references/advanced-topics.md (150 lines)Load when: Implementing i18n, WP-CLI, cron jobs, or dependency checking
Contains:
references/security-checklist.md (527 lines)Load when: Performing security audit or reviewing code for vulnerabilities
Contains:
references/github-auto-updates.md (1,224 lines)Load when: Setting up auto-updates for plugins hosted outside WordPress.org
Contains:
references/common-hooks.md (234 lines)Load when: Working with WordPress hooks and need hook reference
Contains:
Use this checklist to verify your plugin:
Questions? Issues?
references/error-catalog.md for additional issues #6-20Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
Automatically creates user-facing changelogs from git commits by analyzing commit history, categorizing changes, and transforming technical commits into clear, customer-friendly release notes. Turns hours of manual changelog writing into minutes of automated generation.
Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
Next.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling
Use when starting feature work that needs isolation from current workspace or before executing implementation plans - creates isolated git worktrees with smart directory selection and safety verification
Take secondsky/wordpress-plugin-core from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.