WordPress Core
The engine you never touch directly — but always extend. This is what runs before your first line of code executes.
WordPress Execution Lifecycle
What runs first?
Every WordPress request follows the same execution order. Understanding this is the foundation of professional WP development — and the most common interview question.
Entry Point
The very first file executed. Loads wp-blog-header.php.
// index.php
require( dirname( __FILE__ ) . '/wp-blog-header.php' );
Load WordPress
Defines ABSPATH, loads wp-config.php, then wp-settings.php.
define( 'ABSPATH', __DIR__ . '/' );
require_once ABSPATH . 'wp-settings.php';
Core Constants & MU Plugins
Defines core constants, loads essential files, fires muplugins_loaded after must-use plugins.
do_action( 'muplugins_loaded' );
Plugins Loaded
All active plugins are included. plugins_loaded fires — safest hook for plugin-to-plugin interaction.
// Plugins included here
do_action( 'plugins_loaded' );
Setup Theme
Before the theme is loaded. Last chance to override the theme.
do_action( 'setup_theme' );
After Setup Theme
Theme functions.php is loaded and after_setup_theme fires. Theme features (add_theme_support) go here.
// functions.php loaded
do_action( 'after_setup_theme' );
Init
WordPress is fully loaded. Register post types, taxonomies, shortcodes here.
do_action( 'init' );
WP Loaded
Everything is loaded including init. Used by WooCommerce, ACF and similar.
do_action( 'wp_loaded' );
Parse Request
WordPress parses the URL into query variables.
$wp->parse_request();
Send Headers
HTTP headers are sent. Last chance to set headers.
do_action( 'send_headers' );
Query Parsed
Main WP_Query is built. pre_get_posts allows modifying the query before DB hit.
do_action( 'parse_query', $this );
do_action( 'pre_get_posts', $query );
WP Hook
Main query is set. $wp_query is populated. Template selection begins.
do_action( 'wp', $wp );
Template Redirect
Before template is loaded. Redirect users, force different templates.
do_action( 'template_redirect' );
Template Include
Filter which template file gets loaded.
$template = apply_filters( 'template_include', $template );
WP Head
Inside <head>. Enqueue styles, add meta tags, Google Analytics.
do_action( 'wp_head' );
WP Footer
Before </body>. Footer scripts, analytics, deferred JS.
do_action( 'wp_footer' );
Shutdown
Very last hook. Clean up, log, close connections.
do_action( 'shutdown' );
Core API Groups
The Toolbox
WordPress exposes its functionality through grouped APIs. Each is designed to abstract complexity and ensure compatibility.
Reading and writing data to the database.
-
Posts API
get_post(), get_posts(), WP_QueryRetrieve single or multiple posts
Docs -
Meta API
get_post_meta(), update_post_meta()Post, user, term and comment metadata
Docs -
Options API
get_option(), update_option()Site-wide settings and persistent data
Docs -
Transients API
get_transient(), set_transient()Cached temporary data with expiry
Docs -
User API
get_user_by(), wp_insert_user()User management and authentication
Docs
Making external HTTP requests safely.
Read and write files in a portable way.
Register endpoints and interact with the REST layer.
Sanitization, escaping, nonces and capabilities.
-
sanitize_text_field()
sanitize_text_field( $str )Clean user input text
Docs -
esc_html() / esc_attr()
esc_html( $text )Escape output for HTML and attributes
Docs -
wp_nonce_field()
wp_nonce_field( $action )Generate nonce for form protection
Docs -
current_user_can()
current_user_can( $capability )Check user permissions
Docs
Hooks Map
Where to hook in?
All essential WordPress hooks grouped by execution phase. Actions execute code; filters modify data.
- muplugins_loaded MU plugins loaded
- plugins_loaded All plugins loaded
-
setup_themeBefore theme loads - after_setup_theme Theme functions.php done
- init Everything loaded
- wp_loaded After init
- wp_enqueue_scripts Enqueue CSS/JS
- admin_enqueue_scripts Enqueue in admin
- admin_init Admin panel init
-
parse_requestURL parsed - pre_get_posts Before DB query
-
parse_queryQuery vars set -
wpMain query done
- template_redirect Before template loads
- template_include Which template file
- wp_head Inside <head>
- the_content Post content output
- the_title Post title output
- wp_footer Before </body>
- save_post Post saved
-
delete_postPost deleted - user_register User created
-
profile_updateUser updated
Best Practices
The Rules
Principles that separate WordPress developers from WordPress professionals.
Never modify Core files Critical
Any change to wp-includes/ or wp-admin/ will be overwritten on the next WordPress update. Use hooks instead.
Bad
// NEVER do this
// wp-includes/functions.php
function wp_insert_post( $postarr, ... ) {
// your custom code here — GONE after update
}
Good
// Hook into it instead
add_action( 'save_post', function( $post_id ) {
// runs after wp_insert_post — survives updates
do_something( $post_id );
});
Prefix everything Critical
Function names, class names, hooks, option keys — all must have a unique prefix to avoid collisions with Core and other plugins.
Bad
// Too generic — will collide
function get_data() { ... }
add_action( 'init', 'setup' );
$options = get_option( 'settings' );
Good
// Namespaced / prefixed
function myplugin_get_data() { ... }
add_action( 'init', 'myplugin_setup' );
$options = get_option( 'myplugin_settings' );
Sanitize input, Escape output Critical
Always sanitize data when it enters WordPress (from $_POST, $_GET, DB). Always escape data when it leaves (HTML, attributes, SQL).
Bad
// XSS and SQL injection risk
$name = $_POST['name'];
echo $name;
$query = "SELECT * FROM wp_posts WHERE ID = $id";
Good
// Safe input/output
$name = sanitize_text_field( $_POST['name'] );
echo esc_html( $name );
$query = $wpdb->prepare(
"SELECT * FROM wp_posts WHERE ID = %d", $id
);
Never use query_posts() Warning
query_posts() replaces the main query, causing pagination breaks and performance issues. Use WP_Query or pre_get_posts.
Bad
// Breaks pagination and main loop
query_posts( 'post_type=product&posts_per_page=10' );
Good
// Use WP_Query for secondary queries
$products = new WP_Query( [
'post_type' => 'product',
'posts_per_page' => 10,
] );
// Use pre_get_posts to modify the MAIN query
add_action( 'pre_get_posts', function( $query ) {
if ( ! is_admin() && $query->is_main_query() ) {
$query->set( 'posts_per_page', 10 );
}
});
Always verify nonces Critical
Nonces protect forms and AJAX requests from CSRF attacks. Always generate and verify them.
Bad
// No nonce check — CSRF vulnerable
if ( isset( $_POST['save'] ) ) {
update_post_meta( $id, 'key', $_POST['val'] );
}
Good
// Generate nonce in form
wp_nonce_field( 'myplugin_save_meta', 'myplugin_nonce' );
// Verify on save
if ( ! wp_verify_nonce( $_POST['myplugin_nonce'], 'myplugin_save_meta' ) ) {
wp_die( 'Security check failed' );
}
Check capabilities before actions Critical
Always verify that the current user has the right permissions before performing sensitive operations.
Bad
// Any logged-in user can delete!
add_action( 'admin_post_delete_item', function() {
delete_post( $_POST['id'] );
});
Good
// Check capability first
add_action( 'admin_post_delete_item', function() {
if ( ! current_user_can( 'delete_posts' ) ) {
wp_die( 'Insufficient permissions' );
}
delete_post( absint( $_POST['id'] ) );
});
Cache expensive queries with Transients Performance
Expensive WP_Query calls, remote API requests, or complex calculations should be cached using the Transients API.
Bad
// Hits DB on every page load
$results = new WP_Query( [
'post_type' => 'product',
'posts_per_page' => -1,
'meta_query' => [...],
] );
Good
// Cache for 1 hour
$results = get_transient( 'myplugin_products' );
if ( false === $results ) {
$query = new WP_Query( [...] );
$results = $query->posts;
set_transient( 'myplugin_products', $results, HOUR_IN_SECONDS );
}
Use $wpdb for custom queries Warning
When you need custom SQL, use $wpdb with prepared statements — never raw SQL with user input.
Bad
// SQL injection risk
$results = mysql_query(
"SELECT * FROM custom_table WHERE user = '$user'"
);
Good
// Safe with $wpdb->prepare()
global $wpdb;
$results = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}custom_table WHERE user = %s",
$user
)
);
Explore more
Now dive into Hooks & Functions
You know what the Core does. Now learn how to extend it.