- Block Patterns: A predefined group of blocks (text, images, buttons, columns, etc.) inserted inside a post or page. Once inserted, they’re just normal blocks—you can fully edit them
- Template Patterns: Larger structural layouts meant for templates used in the Site Editor (not regular page editing). Often define parts of a page or an entire page layout
- The WordPress commitment is to conform to all WCAG 2.2 Level A and Level AA guidelines.
- Nonce:
class MyPlugin_Admin {
// Display a form in the WP Admin
public function render_settings_page() {
?>
<div class="wrap">
<h1><?php echo esc_html__( 'Plugin Settings', 'my-plugin' ); ?></h1>
<form method="post" action="admin-post.php">
<?php wp_nonce_field( 'save_plugin_settings', 'my_admin_nonce' ); ?>
<input type="hidden" name="action" value="my_save_action">
<input type="text" name="setting_one" value="">
<?php submit_button( esc_html__( 'Save Changes', 'my-plugin' ) ); ?>
</form>
</div>
<?php
}
}
public function handle_save() {
// 1. Verify the nonce
// If this fails, WP automatically calls wp_die() with a 403 error
check_admin_referer( 'save_plugin_settings', 'my_admin_nonce' );
// 2. If we reach here, the request is valid!
// Now sanitize the other inputs
if ( isset( $_POST['setting_one'] ) ) {
$data = sanitize_text_field( $_POST['setting_one'] );
update_option( 'my_plugin_data', $data );
}
// 3. Redirect back
wp_redirect( admin_url( 'admin.php?page=my-plugin-page&status=success' ) );
exit;
}
$action (The “What”)
- Value in your code:
'save_plugin_settings' - Purpose: This is a unique string that describes what is being done.
- Why it matters: It prevents “Context Switching.” If a hacker gets a nonce for “deleting a comment,” they shouldn’t be able to use that same nonce to “save plugin settings.” The action makes the nonce specific to this task.
$name (The “How”)
- Value in your code:
'my_admin_nonce' - Purpose: This becomes the
nameattribute of the hidden HTML input field. - Why it matters: When the form is submitted, this is the key you look for in the
$_POSTarray to find the token.
- In PHP, the leading backslash (
\) indicates that the class belongs to the Global Namespace.
namespace MyPlugin;
// PHP thinks this is MyPlugin\DOMNode (which doesn't exist!)
public function getImageNodes(DOMNode $node) { ... }
// PHP knows to look at the global PHP core for \DOMNode
public function getImageNodes(\DOMNode $node) { ... }
DOMNode,DateTime,Exception, andStdClassare all built into PHP’s core. These live in the global namespace. When you are writing object-oriented code (like yourMyPlugin_Adminclass), it is a best practice to use the backslash for these core classes.- Alternative is to do this:
namespace MyPlugin;
use DOMNode; // Import from global
class Admin {
public function getImageNodes(DOMNode $node) {
// No backslash needed here anymore
}
}
- Creating actions
function my_custom_header_display() {
echo '<header>Welcome to My Site</header>';
// This creates the hook. Anyone can now "attach" to 'after_my_header'
do_action( 'after_my_header', get_the_ID() );
}
[text-domain]-[locale].mo: files are saved in this name explicitly so wp can find it. Ex:rt-movie-db-fr_FR.mo
Metadata
- Metadata is additional information attached to WordPress objects without changing their core database schema. Metadata is stored as key–value pairs.
| Object | Database Table | Meta Table |
|---|---|---|
| Posts (includes pages & CPTs) | wp_posts | wp_postmeta |
| Terms (categories, tags, custom taxonomies) | wp_terms | wp_termmeta |
| Users | wp_users | wp_usermeta |
| Comments | wp_comments | wp_commentmeta |
| The Metadata API is a core WordPress API that provides: |
- CRUD operations (Create, Read, Update, Delete)
- Sanitization and serialization
- Caching
- Consistent access to meta across object types
Core CRUD Functions (Post Meta)
Add
add_post_meta( $post_id, $meta_key, $meta_value, $unique );
Update
update_post_meta( $post_id, $meta_key, $meta_value, $prev_value );
Get
get_post_meta( $post_id, $meta_key, $single );
Delete
delete_post_meta( $post_id, $meta_key, $meta_value );
- They are wrappers to
add_metadata()
get_metadata()
update_metadata()
delete_metadata()
Protected Meta Keys
Start with
_Hidden from:
Custom Fields UI
REST API (unless registered)
add_post_meta()
Adds a new row
Can store multiple values for same key
add_post_meta( 42, 'color', 'red' ); add_post_meta( 42, 'color', 'blue' );
update_post_meta()
Updates existing meta
If it doesn’t exist → creates it
Always results in one value
update_post_meta( 42, 'color', 'green' );
Serialization Formats (XML, JSON, PHP Serialization)
- Internally Uses PHP serialization. Stored as plain text
register_post_meta( 'post', 'event_date', [
'type' => 'string',
'single' => true,
'sanitize_callback' => 'sanitize_text_field',
'show_in_rest' => true,
] );
- Meta boxes allow editors to manage metadata visually.
Add Meta Box
add_meta_box(
'event_details',
'Event Details',
'render_event_meta_box',
'post'
);
save securely
if ( ! isset( $_POST['nonce'] ) ) return;
if ( ! wp_verify_nonce( $_POST['nonce'], 'save_event' ) ) return;
update_post_meta( $post_id, 'event_date', $_POST['event_date'] );
display
$date = get_post_meta( get_the_ID(), 'event_date', true );
if ( $date ) {
echo esc_html( $date );
}
Shortcode API
- The Shortcode API lets you define placeholders inside post content that WordPress replaces with dynamic output at render time.
add_shortcode( 'my_shortcode', 'my_shortcode_callback' );
function my_shortcode_callback( $atts, $content = null, $tag = '' ) {
return 'Hello World';
}
- shortcodes with variables
add_shortcode('wporg', 'wporg_shortcode');
function wporg_shortcode($atts = [], $content = null) {
// 1. Define defaults and merge with user-provided attributes
$pairs = shortcode_atts(
array(
'color' => 'blue', // Default value
'title' => 'Default Title',
),
$atts
);
// 2. Use the variables (with escaping!)
$output = '<div style="color:' . esc_attr($pairs['color']) . '">';
$output .= '<h3>' . esc_html($pairs['title']) . '</h3>';
$output .= do_shortcode($content); // Allow other shortcodes inside
$output .= '</div>';
return $output;
}
- Calling
add_shortcode()adds it to global$GLOBALS['shortcode_tags']
[
'foo' => 'callback',
'gallery' => 'gallery_shortcode',
]
The flow
the_contentfilter runsdo_shortcode()is calledRegex scans content(
get_shortcode_regex()uses recursive parsing for nested shortcodes)Matches
[shortcode attr="value"]Callback is executed
Replacement happens inline
If shortcode is not defined, wordpress displays it as it is in frontend. No errors.
Handling booleans:
[feature enabled]Parsed as:[ 'enabled' => '' ]Best practice:$enabled = array_key_exists( 'enabled', $atts );Executing shortcodes from php:
echo do_shortcode( '[greeting name="Alex"]' );output buffering
function my_shortcode() {
ob_start();
?>
<div class="box">
<h2>Hello</h2>
<p>This is a shortcode.</p>
</div>
<?php
return ob_get_clean();
}
- Shortcode types:
- Self-closing shortcode
[gallery ids="1,2,3"] - Enclosing (wrapping) shortcode
[box]content here[/box]
- Self-closing shortcode