wordpress-plugin-core
from jezweb/claude-skills
Skills for Claude Code CLI such as full stack dev Cloudflare, React, Tailwind v4, and AI integrations.
npx skills add https://github.com/jezweb/claude-skills --skill wordpress-plugin-coreSKILL.md
WordPress Plugin Development (Core)
Last Updated: 2026-01-21 Latest Versions: WordPress 6.9+ (Dec 2, 2025), PHP 8.0+ recommended, PHP 8.5 compatible Dependencies: None (WordPress 5.9+, PHP 7.4+ minimum)
Quick Start
Architecture Patterns: Simple (functions only, <5 functions) | OOP (medium plugins) | PSR-4 (modern/large, recommended 2025+)
Plugin Header (only Plugin Name required):
<?php
/**
* Plugin Name: My Plugin
* Version: 1.0.0
* Requires at least: 5.9
* Requires PHP: 7.4
* Text Domain: my-plugin
*/
if ( ! defined( 'ABSPATH' ) ) exit;
Security Foundation (5 essentials before writing functionality):
// 1. Unique Prefix
define( 'MYPL_VERSION', '1.0.0' );
function mypl_init() { /* code */ }
add_action( 'init', 'mypl_init' );
// 2. ABSPATH Check (every PHP file)
if ( ! defined( 'ABSPATH' ) ) exit;
// 3. Nonces
wp_nonce_field( 'mypl_action', 'mypl_nonce' );
wp_verify_nonce( $_POST['mypl_nonce'], 'mypl_action' );
// 4. Sanitize Input, Escape Output
$clean = sanitize_text_field( $_POST['input'] );
echo esc_html( $output );
// 5. Prepared Statements
global $wpdb;
$wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}table WHERE id = %d", $id ) );
Security Foundation (Detailed)
Unique Prefix (4-5 chars minimum)
Apply to: functions, classes, constants, options, transients, meta keys. Avoid: wp_, __, _.
function mypl_function() {} // ✅
class MyPL_Class {} // ✅
function init() {} // ❌ Will conflict
Capabilities Check (Not is_admin())
// ❌ WRONG - Security hole
if ( is_admin() ) { /* delete data */ }
// ✅ CORRECT
if ( current_user_can( 'manage_options' ) ) { /* delete data */ }
Common: manage_options (Admin), edit_posts (Editor/Author), read (Subscriber)
Security Trinity (Input → Processing → Output)
// Sanitize INPUT
$name = sanitize_text_field( $_POST['name'] );
$email = sanitize_email( $_POST['email'] );
$html = wp_kses_post( $_POST['content'] ); // Allow safe HTML
$ids = array_map( 'absint', $_POST['ids'] );
// Validate LOGIC
if ( ! is_email( $email ) ) wp_die( 'Invalid' );
// Escape OUTPUT
echo esc_html( $name );
echo '<a href="' . esc_url( $url ) . '">';
echo '<div class="' . esc_attr( $class ) . '">';
Nonces (CSRF Protection)
// Form
<?php wp_nonce_field( 'mypl_action', 'mypl_nonce' ); ?>
if ( ! wp_verify_nonce( $_POST['mypl_nonce'], 'mypl_action' ) ) wp_die( 'Failed' );
// AJAX
check_ajax_referer( 'mypl-ajax-nonce', 'nonce' );
wp_localize_script( 'mypl-script', 'mypl_ajax_object', array(
'ajaxurl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'mypl-ajax-nonce' ),
) );
Prepared Statements
// ❌ SQL Injection
$wpdb->get_results( "SELECT * FROM table WHERE id = {$_GET['id']}" );
// ✅ Prepared (%s=String, %d=Integer, %f=Float)
$wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}table WHERE id = %d", $_GET['id'] ) );
// LIKE Queries
$search = '%' . $wpdb->esc_like( $term ) . '%';
$wpdb->get_results( $wpdb->prepare( "... WHERE title LIKE %s", $search ) );
Critical Rules
Always Do
✅ 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 Do
❌ 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
Known Issues Prevention
This skill prevents 29 documented issues:
Issue #1: SQL Injection
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( "DELET
...