Build WordPress plugins from scratch: actions and filters, shortcodes, settings pages, custom post types, meta boxes, AJAX, REST endpoints, custom tables, cron and blocks.
Lessons
WordPress gives plugins three lifecycle moments — activation, deactivation and uninstall — so you can create default settings when a plugin is switched on, pause scheduled work when it is switched off, and delete every trace of it when it is removed.
<?php
/**
* Runs once, when the plugin is activated.
*/
function gdc_books_activate() {
// Default settings, only if they don't exist yet (re-activation keeps old ones).
add_option(
'gdc_books_settings',
array(
'books_per_page' => 6,
'show_isbn' => true,
)
);
// Register the post type now, then flush, so /books/ works immediately.
gdc_books_register_post_type();
flush_rewrite_rules();
}
register_activation_hook( __FILE__, 'gdc_books_activate' );register_activation_hook() must be called from the main plugin file with __FILE__.add_option() does nothing if the option already exists — exactly what you want, so re-activating does not reset a user's settings.flush_rewrite_rules() rebuilds the URL rules so your post type's pretty URLs work. It is expensive: call it only on activation and deactivation, never on init.<?php
function gdc_books_deactivate() {
// Stop scheduled events (lesson 21).
wp_clear_scheduled_hook( 'gdc_books_daily_cleanup' );
// Remove our rewrite rules.
flush_rewrite_rules();
}
register_deactivation_hook( __FILE__, 'gdc_books_deactivate' );Watch out: Deactivation is not uninstall. People deactivate plugins temporarily to debug a problem. Never delete their data on deactivation — they would lose every book when they switch the plugin back on.
When someone clicks Delete on the Plugins screen, WordPress looks for uninstall.php in your plugin folder and runs it. Create gdc-books/uninstall.php:
<?php
/**
* Removes all GDC Books data when the plugin is deleted.
*/
// Only run when WordPress itself is uninstalling the plugin.
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
exit;
}
// Settings.
delete_option( 'gdc_books_settings' );
delete_option( 'gdc_books_db_version' );
// Transients (lesson 21).
delete_transient( 'gdc_books_stats' );
// All Book posts and their meta.
$book_ids = get_posts(
array(
'post_type' => 'gdc_book',
'post_status' => 'any',
'numberposts' => -1,
'fields' => 'ids',
)
);
foreach ( $book_ids as $book_id ) {
wp_delete_post( $book_id, true ); // true: skip the trash.
}
// A custom table (lesson 20).
global $wpdb;
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}gdc_books_favourites" );The WP_UNINSTALL_PLUGIN check is vital: without it, anyone requesting the file's URL directly could wipe your users' data.
Tip: Some users want to delete a plugin but keep its content — for example, to reinstall later. Respect that with a "Delete all data on uninstall" checkbox in your settings, and check it at the top of uninstall.php before deleting anything.On a multisite network, options and posts exist per site. A thorough uninstall.php loops through each site with get_sites() and switch_to_blog() / restore_current_blog() to clean up everywhere.
gdc_books_settings option appears in Settings → All Options (/wp-admin/options.php).uninstall.php, delete the plugin, and confirm the option is gone.In accordance with Section 6 & Section 7 of the DPDP Act 2023, third-party analytics and marketing trackers are strictly gated behind your affirmative consent. You may withdraw consent at any time under Section 6(4).
Essential for session authentication (ci_session), Cross-Site Request Forgery security (csrf_cookie_name), and Cloudflare DDoS/bot prevention. Cannot be disabled.
Gated under DPDP Section 7. Allows Google Analytics 4 (Google Consent Mode v2) to measure aggregate, anonymized tool usage.
Enables browser local storage to preserve your theme selection (dark/light mode) and code editor layout between visits.
Used to display developer sponsorships and native tool partner notices.