---
name: wordpress
description: Comprehensive guide for contributing to WordPress core development, including environment setup, coding standards, hooks system, database API, REST API, testing, and Trac workflow.
metadata:
author: mte90
version: 1.0.0
tags:
- wordpress
- php
- cms
- open-source
- core-contribution
- hooks
- testing
---
# WordPress Core Contribution
Complete guide for contributing to WordPress core development.
## Overview
WordPress is an open-source CMS written in PHP. This skill covers contributing to WordPress core.
**Key Resources:**
- Developer Docs: https://developer.wordpress.org
- Core Handbook: https://make.wordpress.org/core/handbook/
- Trac: https://core.trac.wordpress.org
- Slack: https://make.wordpress.org/chat/
## Development Environment
### Requirements
```bash
# PHP 7.4+ (8.0+ recommended)
php -v
# Node.js (for building JS)
node -v
npm -v
# Git
git --version
# Database (MySQL 5.7+ or MariaDB 10.2+)
mysql --version
# PHPUnit 8.x (for testing)
phpunit --version
# Composer (for dependencies)
composer --version
```
### Setting Up WordPress
```bash
# Clone the repository
git clone https://github.com/WordPress/wordpress-develop.git
cd wordpress-develop
# Copy config file
cp wp-tests-config-sample.php wp-tests-config.php
# Edit wp-tests-config.php
# Set database credentials and test URLs
```
### wp-tests-config.php
```php
phpcs.xml << 'EOF'
Added content
'; } // With multiple parameters add_filter( 'wp_insert_post_data', 'my_insert_post_data_filter', 10, 2 ); /** * Filter post data before insertion. * * @since 1.0.0 * * @param array $data Parsed post data. * @param array $postarr Raw post data. * @return array Modified data. */ function my_insert_post_data_filter( $data, $postarr ) { // Validate and modify data return $data; } ``` ### Hook Priority ```php // Priority determines execution order // Lower priority = earlier execution add_action( 'init', 'early_hook', 1 ); // First add_action( 'init', 'default_hook', 10 ); // Default add_action( 'init', 'late_hook', 20 ); // Last // Example: Customize login form add_action( 'login_form', 'custom_login_html', 5 ); // Early add_action( 'login_form', 'custom_login_input', 10 ); // Default add_action( 'login_form', 'custom_login_js', 20 ); // Late ``` ### Removing Hooks ```php // Remove default WordPress hook remove_action( 'wp_head', 'wp_generator' ); // Remove with priority remove_action( 'wp_enqueue_scripts', 'wp_enqueue_block_library_footer', 10 ); // Remove from class add_action( 'wp_footer', 'remove_footer_scripts', 999 ); function remove_footer_scripts() { remove_action( 'wp_footer', 'wp_enqueue_scripts', 20 ); } // Conditional removal function my_plugin_init() { if ( ! current_user_can( 'manage_options' ) ) { remove_filter( 'the_content', 'wptexturize' ); } } add_action( 'init', 'my_plugin_init' ); ``` ### Class Methods as Hooks ```php class My_Plugin { public function __construct() { add_action( 'init', array( $this, 'init' ) ); add_filter( 'the_content', array( $this, 'filter_content' ) ); } /** * Initialize plugin. */ public function init() { // Initialization code } /** * Filter content. * * @since 1.0.0 * * @param string $content Content. * @return string Modified content. */ public function filter_content( $content ) { return $content; } } new My_Plugin(); ``` ### Dynamic Hooks ```php // Dynamically create hooks function create_dynamic_hooks() { $taxonomies = get_taxonomies( array( 'public' => true ) ); foreach ( $taxonomies as $taxonomy ) { $taxonomy_name = $taxonomy->name; // Create dynamic filter add_filter( "{$taxonomy_name}_row_actions", array( $this, 'taxonomy_row_actions' ), 10, 2 ); } } /** * Add actions to taxonomy row. * * @since 1.0.0 * * @param array $actions Actions. * @param WP_Term $term Term object. * @return array Modified actions. */ public function taxonomy_row_actions( $actions, $term ) { $actions['my_action'] = sprintf( '%s', esc_html__( 'My Action', 'text-domain' ) ); return $actions; } ``` ## Query System ### WP_Query ```php $args = array( 'post_type' => 'post', 'posts_per_page' => 10, 'paged' => get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1, 'orderby' => 'date', 'order' => 'DESC', 'meta_query' => array( array( 'key' => 'featured', 'value' => 'yes', 'compare' => '=', ), ), 'tax_query' => array( array( 'taxonomy' => 'category', 'field' => 'slug', 'terms' => array( 'news', 'updates' ), 'operator' => 'IN', ), ), ); $query = new WP_Query( $args ); if ( $query->have_posts() ) { while ( $query->have_posts() ) { $query->the_post(); the_title(); the_content(); } // Pagination next_posts_link(); previous_posts_link(); } wp_reset_postdata(); ``` ### pre_get_posts ```php // Modify main query add_action( 'pre_get_posts', 'modify_main_query', 1 ); /** * Modify main query. * * @since 1.0.0 * * @param WP_Query $query Query object. */ function modify_main_query( $query ) { // Only modify main query if ( ! $query->is_main_query() ) { return; } // Only on front page if ( ! $query->is_home() ) { return; } // Show only specific category $query->set( 'category_name', 'featured' ); $query->set( 'posts_per_page', 5 ); } ``` ### get_posts() ```php // Simple posts retrieval $posts = get_posts( array( 'post_type' => 'post', 'posts_per_page' => -1, // All posts 'orderby' => 'title', 'order' => 'ASC', ) ); foreach ( $posts as $post ) { setup_postdata( $post ); echo esc_html( get_the_title( $post->ID ) ); } wp_reset_postdata(); ``` ## Post Types and Taxonomies ### Registering Custom Post Types ```php add_action( 'init', 'register_custom_post_type' ); /** * Register custom post type. * * @since 1.0.0 */ function register_custom_post_type() { $labels = array( 'name' => __( 'Books', 'text-domain' ), 'singular_name' => __( 'Book', 'text-domain' ), 'add_new' => __( 'Add New', 'text-domain' ), 'add_new_item' => __( 'Add New Book', 'text-domain' ), 'edit_item' => __( 'Edit Book', 'text-domain' ), 'new_item' => __( 'New Book', 'text-domain' ), 'view_item' => __( 'View Book', 'text-domain' ), 'search_items' => __( 'Search Books', 'text-domain' ), 'not_found' => __( 'No books found', 'text-domain' ), 'not_found_in_trash' => __( 'No books found in Trash', 'text-domain' ), ); $args = array( 'labels' => $labels, 'public' => true, 'publicly_queryable' => true, 'show_ui' => true, 'show_in_menu' => true, 'show_in_nav_menus' => true, 'show_in_admin_bar' => true, 'show_in_rest' => true, // For Gutenberg 'query_var' => true, 'rewrite' => array( 'slug' => 'books', 'with_front' => false, ), 'capability_type' => 'post', 'has_archive' => true, 'hierarchical' => false, 'menu_position' => 20, 'menu_icon' => 'dashicons-book', 'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt', 'custom-fields', ), 'rest_base' => 'books', 'rest_controller_class' => 'WP_REST_Posts_Controller', ); register_post_type( 'book', $args ); } ``` ### Registering Custom Taxonomies ```php add_action( 'init', 'register_custom_taxonomy' ); /** * Register custom taxonomy. * * @since 1.0.0 */ function register_custom_taxonomy() { $labels = array( 'name' => __( 'Genres', 'text-domain' ), 'singular_name' => __( 'Genre', 'text-domain' ), 'search_items' => __( 'Search Genres', 'text-domain' ), 'all_items' => __( 'All Genres', 'text-domain' ), 'parent_item' => __( 'Parent Genre', 'text-domain' ), 'parent_item_colon' => __( 'Parent Genre:', 'text-domain' ), 'edit_item' => __( 'Edit Genre', 'text-domain' ), 'update_item' => __( 'Update Genre', 'text-domain' ), 'add_new_item' => __( 'Add New Genre', 'text-domain' ), 'new_item_name' => __( 'New Genre Name', 'text-domain' ), ); $args = array( 'labels' => $labels, 'public' => true, 'publicly_queryable' => true, 'hierarchical' => true, 'show_ui' => true, 'show_in_menu' => true, 'show_in_nav_menus' => true, 'show_in_rest' => true, 'show_admin_column' => true, 'query_var' => true, 'rewrite' => array( 'slug' => 'genre', 'with_front' => true, 'hierarchical' => true, ), 'capabilities' => array( 'manage_terms', 'edit_terms', 'delete_terms', 'assign_terms', ), ); register_taxonomy( 'genre', array( 'book' ), $args ); } ``` ### Meta Boxes ```php add_action( 'add_meta_boxes', 'add_custom_meta_box' ); /** * Add meta box to post type. * * @since 1.0.0 */ function add_custom_meta_box() { add_meta_box( 'custom_meta_box', __( 'Custom Meta Box', 'text-domain' ), 'book', 'normal', 'default' ); } add_action( 'save_post', 'save_custom_meta_box_data', 10, 2 ); /** * Save meta box data. * * @since 1.0.0 * * @param int $post_id Post ID. * @param WP_Post $post Post object. */ function save_custom_meta_box_data( $post_id, $post ) { // Check autosave if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } // Check permissions if ( ! current_user_can( 'edit_post', $post_id ) ) { return; } // Verify nonce if ( ! isset( $_POST['custom_meta_box_nonce'] ) || ! wp_verify_nonce( $_POST['custom_meta_box_nonce'], 'custom_meta_box' ) ) { return; } // Save data if ( isset( $_POST['custom_field'] ) ) { $sanitized = sanitize_text_field( $_POST['custom_field'] ); update_post_meta( $post_id, '_custom_field', $sanitized ); } } ``` ## Database API ### $wpdb Basics ```php global $wpdb; // Select $results = $wpdb->get_results( "SELECT ID, post_title FROM {$wpdb->posts} WHERE post_status = 'publish'" ); // Get row $row = $wpdb->get_row( "SELECT * FROM {$wpdb->options} WHERE option_name = 'siteurl'" ); // Get variable $site_url = $wpdb->get_var( "SELECT option_value FROM {$wpdb->options} WHERE option_name = 'siteurl'" ); // Insert $wpdb->insert( $wpdb->postmeta, array( 'post_id' => 1, 'meta_key' => 'custom_key', 'meta_value' => 'custom_value', ), array( '%d', '%s', '%s' ) ); // Update $wpdb->update( $wpdb->options, array( 'option_value' => 'new_value' ), array( 'option_name' => 'siteurl' ), array( '%s', '%s' ) ); // Delete $wpdb->delete( $wpdb->postmeta, array( 'post_id' => 1, 'meta_key' => 'old_key' ), array( '%d', '%s' ) ); ``` ### $wpdb->prepare() ```php global $wpdb; // Single placeholder $post_id = 1; $post_title = $wpdb->get_var( $wpdb->prepare( "SELECT post_title FROM {$wpdb->posts} WHERE ID = %d", $post_id ) ); // Multiple placeholders $wpdb->insert( $wpdb->postmeta, $wpdb->prepare( "(post_id, meta_key, meta_value) VALUES (%d, %s, %s)", $post_id, 'my_key', 'my_value' ), array( '%d', '%s', '%s' ) ); // LIKE queries $search_term = 'test'; $results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->posts} WHERE post_title LIKE %s", '%' . $wpdb->esc_like( $search_term ) . '%' ) ); ``` ### dbDelta (Table Creation) ```php // Register activation hook register_activation_hook( __FILE__, 'create_custom_table' ); /** * Create custom database table. * * @since 1.0.0 */ function create_custom_table() { global $wpdb; $table_name = $wpdb->prefix . 'custom_data'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE {$table_name} ( id mediumint(9) NOT NULL AUTO_INCREMENT, user_id bigint(20) NOT NULL, data text NOT NULL, created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, PRIMARY KEY (id), KEY user_id (user_id) ) {$charset_collate};"; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta( $sql ); add_option( 'custom_db_version', '1.0' ); } // Register activation hook register_deactivation_hook( __FILE__, 'drop_custom_table' ); /** * Drop custom database table. * * @since 1.0.0 */ function drop_custom_table() { global $wpdb; $table_name = $wpdb->prefix . 'custom_data'; $wpdb->query( "DROP TABLE IF EXISTS {$table_name}" ); } ``` ## REST API ### Custom Endpoints ```php add_action( 'rest_api_init', 'register_custom_endpoints' ); /** * Register custom REST endpoints. * * @since 1.0.0 */ function register_custom_endpoints() { register_rest_route( 'my-plugin/v1', '/books', array( 'methods' => 'GET, POST', 'callback' => 'handle_books_request', 'permission_callback' => 'check_permission', 'args' => array( 'book_id' => array( 'validate_callback' => 'validate_book_id', ), ), ) ); } /** * Handle books request. * * @since 1.0.0 * * @param WP_REST_Request $request Request object. * @return WP_REST_Response|WP_Error Response. */ function handle_books_request( $request ) { $params = $request->get_params(); if ( isset( $params['book_id'] ) ) { // Get single book $book = get_post( $params['book_id'] ); if ( ! $book || 'book' !== $book->post_type ) { return new WP_Error( 'book_not_found', __( 'Book not found', 'text-domain' ), array( 'status' => 404 ) ); } $data = array( 'id' => $book->ID, 'title' => $book->post_title, 'content' => $book->post_content, ); return rest_ensure_response( $data ); } // Get all books $args = array( 'post_type' => 'book', 'posts_per_page' => 10, ); $books = get_posts( $args ); $data = array(); foreach ( $books as $book ) { $data[] = array( 'id' => $book->ID, 'title' => $book->post_title, ); } return rest_ensure_response( $data ); } /** * Check permissions. * * @since 1.0.0 * * @return bool True if allowed. */ function check_permission() { return current_user_can( 'read' ); } /** * Validate book ID. * * @since 1.0.0 * * @param int $book_id Book ID. * @return bool True if valid. */ function validate_book_id( $book_id ) { return is_numeric( $book_id ) && $book_id > 0; } ``` ### Custom REST Controller ```php class Books_Controller extends WP_REST_Controller { /** * Register routes. * * @since 1.0.0 */ public function register_routes() { register_rest_route( 'my-plugin/v1', '/books/(?P