Beginner Level (0–1 Years)
1. What is the difference between a post and a page in WordPress?
Answer:
Posts are dynamic, date-based content types often used for blogs. Pages are static content, such as “About Us”, and don’t appear in chronological listings.
2. How would you enqueue a stylesheet in WordPress properly?
Answer:
Use wp_enqueue_style()
inside the wp_enqueue_scripts
action hook.
function my_theme_styles() {
// For the theme's main stylesheet (style.css in the current theme's root)
// get_stylesheet_uri() retrieves the URI of the current theme's (child or parent) style.css
wp_enqueue_style('my-main-style', get_stylesheet_uri());
// For other stylesheets within your theme
// If working in a child theme and the stylesheet is in the child theme's 'css' folder:
wp_enqueue_style('my-child-custom-style', get_stylesheet_directory_uri() . '/css/custom-style.css');
// If you specifically need a stylesheet from the parent theme's 'css' folder (e.g., from within a child theme):
// wp_enqueue_style('my-parent-custom-style', get_template_directory_uri() . '/css/parent-custom-style.css');
}
add_action('wp_enqueue_scripts', 'my_theme_styles');
Key points: Using wp_enqueue_style
handles dependencies and prevents duplicate loading. get_stylesheet_directory_uri()
is generally preferred for assets within the currently active theme (especially child themes), while get_template_directory_uri()
specifically targets the parent theme.
3. What is a child theme and why would you use one?
Answer:
A child theme in WordPress inherits the look, feel, and functionality of another theme, called the parent theme. You would use a child theme to make modifications to a parent theme (like customizing styles or adding new functionality) without directly altering the parent theme’s files. This is crucial because it allows you to update the parent theme when new versions are released without losing your custom changes.
4. How do you display dynamic content in a WordPress template file?
Answer:
Use template tags like the_title()
, the_content()
within The Loop. A basic implementation looks like:
if (have_posts()) :
while (have_posts()) : the_post();
the_title();
the_content();
endwhile;
endif;
5. What function retrieves the URL of the current theme directory?
Answer:
get_template_directory_uri()
: This function always retrieves the URL of the parent theme’s directory.
get_stylesheet_directory_uri()
: This function retrieves the URL of the active theme’s directory. If a child theme is active, it will return the child theme’s directory URI. If no child theme is active (i.e., you are using a parent theme directly), it will return the parent theme’s directory URI.
So, when working with a child theme and you want to access assets (like CSS or JavaScript files) within that child theme, you should use get_stylesheet_directory_uri()
. If you need to access assets from the parent theme from within a child theme, you’d use get_template_directory_uri()
.
6. What is the WordPress Loop?
Answer:
A PHP code structure used by WordPress to display posts by cycling through them.
7. What happens when you click “Publish” on a WordPress post?
Answer:
When you click “Publish” on a WordPress post, the post content, title, metadata (like categories, tags, author), and its status are saved to the WordPress database. If its visibility is set to public, the post becomes live and publicly viewable on your website according to its permalink structure. WordPress also triggers various internal actions (hooks like save_post
and publish_post
), which can allow plugins or themes to execute custom functions at this point (e.g., sending notifications, clearing caches, etc.).
8. How would you set a static homepage in WordPress?
Answer:
Go to Settings → Reading and choose “A static page” for homepage and optionally a posts page.
9. What is the difference between the_title()
and get_the_title()
?
Answer:
the_title()
echoes the post title; get_the_title()
returns it for custom use.
10. How can you update a plugin in WordPress?
Answer:
There are several ways to update a plugin in WordPress:
- Via the Plugins section in wp-admin: Navigate to “Plugins” → “Installed Plugins.” If an update is available for a plugin, you’ll see a notification below it with an “Update Now” link.
- Via the Dashboard → Updates screen: WordPress checks for updates periodically. You can go to “Dashboard” → “Updates” to see a list of all available updates (for core, plugins, and themes) and perform bulk updates or individual updates from there.
- Using WP-CLI (for users with command-line access): If WP-CLI is installed on the server, you can update a specific plugin using a command like
wp plugin update plugin-slug
or update all plugins withwp plugin update --all
. This method is often faster for developers managing multiple sites or plugins.
11. Can you edit HTML directly in WordPress? If so, how?
Answer:
Yes, via the Code Editor in the block editor or in the Text tab of the Classic Editor.
12. What is the role of widgets in WordPress?
Answer:
Widgets are components that can be added to sidebars or widget-ready areas for features like recent posts or search. They can be managed via Appearance → Widgets in the WordPress dashboard, allowing users to customize specific areas of their site without coding.
13. What is a permalink?
Answer:
The permanent URL structure of a post, page, or other WordPress content.
14. How do you install a new theme?
Answer:
Via Appearance → Themes → Add New, or by uploading a .zip
file.
15. What is the purpose of categories and tags?
Answer:
To group and relate posts. Categories are hierarchical; tags are non-hierarchical.
16. What happens if you deactivate a plugin?
Answer:
When you deactivate a plugin, WordPress immediately stops running its code, so any features or functionality it added to your site will cease to work. However, deactivating a plugin does not typically remove its settings or any data it might have saved in the database. This is done so that if you choose to reactivate the plugin later, your previous configurations can often be restored. Deactivation is different from uninstallation; uninstalling a plugin usually includes a routine (if the plugin developer provided one) to clean up its database entries and files.
17. Where are uploaded media files stored in WordPress?
Answer:
In the wp-content/uploads
directory, organized by year and month folders.
18. What is the wp-content
directory used for?
Answer:
It stores all user content: themes, plugins, and media uploads.
19. How can you access WordPress admin without the GUI (e.g., via CLI)?
Answer:
You can manage a WordPress site using the command line with WP-CLI (WordPress Command Line Interface). WP-CLI is a powerful tool that allows you to perform many administrative tasks without needing to use a web browser or the WordPress admin dashboard. You need to have WP-CLI installed on your server to use these commands.
wp post list
: Lists recent posts.wp plugin install <plugin-slug> --activate
: Installs and activates a plugin.wp plugin update --all
: Updates all plugins.wp user create <username> <email> --role=author
: Creates a new user.wp core update
: Updates WordPress to the latest version.wp db export
: Exports the WordPress database.
20. What is a shortcode in WordPress?
Answer:
A shortcode is a small piece of code like that executes a predefined function inside posts, pages, or widgets. For example, adding [contact-form-7 id="123"]
to a page will display that specific contact form. Developers can create custom shortcodes using the add_shortcode()
function.
21. How do you make a page private or password-protected?
Answer:
Set the Visibility in the Page editor to Private or Password Protected.
22. What are hooks in WordPress? Explain the difference between an action hook and a filter hook, including a simple example.
Answer:
Hooks are specific points in the WordPress core, themes, and plugins where developers can “hook into” to run their own custom code or modify existing behavior without altering the original files. They are fundamental to WordPress’s flexibility.
There are two main types of hooks:
- Action Hooks (
do_action
,add_action
): Actions allow you to execute custom functions at specific points during WordPress execution. They are used to add new functionality. For example, thewp_head
action hook runs in the<head>
section of a theme, allowing you to add custom scripts or meta tags usingadd_action('wp_head', 'your_custom_function');
. Your function doesn’t return anything; it just does something. - Filter Hooks (
apply_filters
,add_filter
): Filters allow you to modify data before it is saved to the database or displayed to the user. Your custom function will receive data, modify it, and must return the modified data. For example, thethe_content
filter allows you to change a post’s content before it’s displayed. You could useadd_filter('the_content', 'your_custom_content_modifier_function');
to, say, append a message to every post.
23. Where does WordPress store its data, like posts and settings? What is the name of the database system typically used?
Answer:
WordPress stores all of its data, including posts, pages, comments, user information, plugin settings, theme options, and site settings, in a database. The database system most commonly used with WordPress is MySQL (or compatible alternatives like MariaDB).
24. What is the primary purpose of the WordPress REST API at a high level?
Answer:
The WordPress REST API allows other applications and websites to interact with a WordPress site’s data programmatically. Its primary purpose is to enable WordPress to be used in a “headless” manner or to integrate it with other services. For example, a mobile app could use the REST API to fetch posts from a WordPress site, or a JavaScript-based frontend application could use it to display and manage WordPress content. It allows for creating, reading, updating, and deleting (CRUD) WordPress content using standard HTTP requests (GET, POST, PUT, DELETE).
25. What is one simple security measure or best practice you should keep in mind when working with or developing for WordPress?
Answer:
One of the most important security measures is to keep everything updated: WordPress core, all themes (even inactive ones), and all plugins. Updates often include patches for security vulnerabilities that have been discovered. Other important practices include using strong, unique passwords for all user accounts (especially administrators), and only downloading themes and plugins from reputable sources.
Intermediate Level (1–3 Years)
1. What’s the difference between get_template_directory() and get_stylesheet_directory() ?
Answer:
get_template_directory() returns the absolute server path to the parent theme’s directory. get_stylesheet_directory() returns the absolute server path to the current theme’s directory. If you’re using a child theme, get_stylesheet_directory() will point to the child theme’s directory, while get_template_directory() will still point to the parent theme’s directory. If no child theme is active, they both return the same path. These are distinct from their URI counterparts ( get_template_directory_uri() and get_stylesheet_directory_uri() ) which return URLs.
2. Why is wp_head() important in header.php ?
Answer:
It allows WordPress and plugins to insert crucial meta tags, styles, scripts, and more into the page head. Omitting it can break plugin functionality.
3. How do you register a custom post type?
Answer:
You register a custom post type using the register_post_type() function, typically hooked into the init action.
function create_book_post_type() {
$labels = [
'name' => _x( 'Books', 'post type general name', 'your-textdomain' ),
'singular_name' => _x( 'Book', 'post type singular name', 'your-textdomain' ),
'add_new' => _x( 'Add New', 'book', 'your-textdomain' ),
'add_new_item' => __( 'Add New Book', 'your-textdomain' ),
'edit_item' => __( 'Edit Book', 'your-textdomain' ),
'new_item' => __( 'New Book', 'your-textdomain' ),
'all_items' => __( 'All Books', 'your-textdomain' ),
'view_item' => __( 'View Book', 'your-textdomain' ),
'search_items' => __( 'Search Books', 'your-textdomain' ),
'not_found' => __( 'No books found', 'your-textdomain' ),
'not_found_in_trash' => __( 'No books found in the Trash', 'your-textdomain' ),
'parent_item_colon' => '',
'menu_name' => __( 'Books', 'your-textdomain' )
];
$args = [
'labels' => $labels,
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => [ 'slug' => 'book' ],
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => null,
'menu_icon' => 'dashicons-book',
'supports' => [ 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments', 'custom-fields' ],
'show_in_rest' => true, // Important for Gutenberg and REST API
];
register_post_type( 'book', $args);
}
add_action( 'init', 'create_book_post_type' );
Key points: A full labels array improves the admin UI. supports defines meta boxes available. show_in_rest enables REST API access. rewrite controls the slug. Always flush rewrite rules (by visiting Settings > Permalinks) after adding or changing a CPT.
4. What is the functions.php file used for?
Answer:
It’s a theme-specific file for adding custom PHP functions, filters, actions, and feature support.
5. What is the role of add_action() and add_filter() ?
Answer:
- add_action() hooks a function to an event.
- add_filter() modifies data passed through a filter hook.
6. What are template tags in WordPress?
Answer:
PHP functions used to display dynamic content like post titles ( the_title() ), content ( the_content() ), and metadata.
7. What’s the difference between the_content() and get_the_content() ?
Answer:
- the_content() echoes the post content after applying WordPress content filters (like wpautop , shortcode execution, oEmbeds). It’s typically used directly in template files within The Loop to display formatted content.
- get_the_content() returns the raw post content from the database for the current post in The Loop, without most content filters applied by default. This is useful if you need to manipulate the content string in PHP before display. To get filtered content similar to the_content() , you would use apply_filters(‘the_content’, get_the_content());
8. What is a WordPress hook and how does it differ from a filter?
Answer:
Hooks are placeholders in the WordPress code that allow you to run custom functions or modify data at specific points. The two main types are:
- Action Hooks: Used to execute custom code at a specific point during WordPress execution (e.g., when a theme loads, a post is published, or HTML is output to the page). Functions hooked to actions typically perform a task (like outputting HTML, sending an email, saving data) and don’t return a value. They are added using add_action(‘hook_name’, ‘your_function_name’); .
- Filter Hooks: Used to modify data before it’s saved to the database or displayed to the user. Functions hooked to filters must receive data as an argument and must return that data (either modified or original). They are added using add_filter(‘hook_name’, ‘your_function_name’);
9. How would you create and use a custom taxonomy?
Answer:
Use register_taxonomy() within an init action hook. This function associates the taxonomy with one or more post types.
function register_genre_taxonomy() {
$labels = [
'name' => _x( 'Genres', 'taxonomy general name', 'your-textdomain' ),
'singular_name' => _x( 'Genre', 'taxonomy singular name', 'your-textdomain' ),
'search_items' => __( 'Search Genres', 'your-textdomain' ),
'all_items' => __( 'All Genres', 'your-textdomain' ),
'parent_item' => __( 'Parent Genre', 'your-textdomain' ),
'parent_item_colon' => __( 'Parent Genre:', 'your-textdomain' ),
'edit_item' => __( 'Edit Genre', 'your-textdomain' ),
'update_item' => __( 'Update Genre', 'your-textdomain' ),
'add_new_item' => __( 'Add New Genre', 'your-textdomain' ),
'new_item_name' => __( 'New Genre Name', 'your-textdomain' ),
'menu_name' => __( 'Genre', 'your-textdomain' ),
];
$args = [
'hierarchical' => true, // true for category-like, false for tag-like
'labels' => $labels,
'show_ui' => true,
'show_admin_column' => true,
'query_var' => true,
'rewrite' => [ 'slug' => 'genre' ],
'show_in_rest' => true, // Make taxonomy available in REST API
];
register_taxonomy( 'genre', [ 'book', 'post' ], $args); // Assign to 'book' CPT and 'post'
}
add_action( 'init', 'register_genre_taxonomy' );
10. How is AJAX implemented in WordPress?
Answer:
AJAX in WordPress typically involves:
- Enqueueing a JavaScript file: Use wp_enqueue_script() .
- Localizing the script: Use wp_localize_script() to pass the AJAX URL ( admin_url(‘admin-ajax.php’) ) and a nonce for security to your JavaScript file.
- JavaScript making the request: Use jQuery’s $.ajax() , $.post() , $.get() , or the native Fetch API to send data to admin-ajax.php . The request must include an action parameter, which corresponds to a WordPress action hook.
- PHP handling the request: Create functions hooked to wp_ajax_{your_action_name} (for logged-in users) and wp_ajax_nopriv_{your_action_name} (for non-logged-in users).
- Server-side processing: Inside your PHP handler function, verify the nonce (e.g., using check_ajax_referer(‘your_nonce_action_name’, ‘security_param_name’) ), process the request, and send back a response using wp_send_json_success() , wp_send_json_error() , or by echoing data and then calling wp_die() or die() .
// In functions.php or plugin
add_action( 'wp_ajax_my_custom_action', 'my_custom_action_handler' );
add_action( 'wp_ajax_nopriv_my_custom_action', 'my_custom_action_handler' );
function my_custom_action_handler() {
// Verify nonce (passed as 'security' in this example)
check_ajax_referer( 'my_ajax_nonce', 'security' );
// Process data (e.g., from $_POST['data_key'])
$data_to_return = [ 'message' => 'AJAX request processed successfully!', 'received' => $_POST['info'] ];
wp_send_json_success($data_to_return);
// wp_die(); // wp_send_json_success includes wp_die()
}
function enqueue_ajax_scripts() {
wp_enqueue_script( 'my-ajax-script', get_template_directory_uri() . '/js/my-ajax.js', [ 'jquery' ], null, true);
wp_localize_script( 'my-ajax-script', 'myAjax', [
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'my_ajax_nonce' ) // Action name for nonce
]);
}
add_action( 'wp_enqueue_scripts', 'enqueue_ajax_scripts' );
// Example Snippet (JS - my-ajax.js)
jQuery(document).ready(function ($) {
$('#my-button').on('click', function () {
$.ajax({
url: myAjax.ajaxUrl,
type: 'POST',
data: {
action: 'my_custom_action', // Matches PHP hook
security: myAjax.nonce, // Nonce
info: 'Some data from client'
},
success: function (response) {
if (response.success) {
console.log('Server said: ', response.data.message);
} else {
console.error('Error: ', response.data);
}
},
error: function (errorThrown) {
console.error('Request failed: ', errorThrown);
}
});
});
});
11. How do you send and receive data via the REST API?
Answer:
You use standard HTTP methods (GET, POST, PUT, DELETE) to interact with REST API endpoints (e.g., /wp-json/wp/v2/posts , /wp-json/wp/v2/pages/<id> ).
- Receiving Data (GET): Typically public for readable content. Use Workspace in JavaScript or server-side wp_remote_get() . Example: Workspace(‘/wp-json/wp/v2/posts?per_page=5’) .
- Sending Data (POST, PUT, DELETE): Requires authentication to protect write operations.
- Authentication Methods:
- Nonce Authentication: For JavaScript running on a logged-in user’s WordPress frontend. Pass the _wpnonce data parameter or X-WP-Nonce header. Generated with wp_create_nonce(‘wp_rest’) .
- Cookie Authentication: Automatically handled by the browser for logged-in users making requests from the same domain.
- Application Passwords: For third-party applications or scripts. Users can generate these in their profile. The application sends it via Basic Authentication.
- OAuth (1.0a or 2.0): More complex but standard for third-party app authorization.
- Authentication Methods:
- Example (POSTing a new post with nonce auth in JS):
// Assuming 'wpApiSettings' is localized with root URL and nonce
fetch(wpApiSettings.root + 'wp/v2/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': wpApiSettings.nonce // Nonce must be for 'wp_rest'
},
body: JSON.stringify({
title: 'New Post Title from REST API',
content: 'Some interesting content.',
status: 'publish'
})
})
.then(response => response.json())
.then(data => console.log('Post created:', data))
.catch(error => console.error('Error creating post:', error));
12. What are nonces and how do you use them?
Answer:
Nonces are tokens to verify requests and prevent CSRF. Generate with wp_create_nonce() , verify with check_ajax_referer() or wp_verify_nonce() .
13. How do you fetch and display post meta?
Answer:
Use get_post_meta($post_id, ‘meta_key’, true) to retrieve the value.
14. When would you use $wpdb vs get_posts() ?
Answer:
- get_posts() / WP_Query : Use these for most scenarios involving fetching posts or custom post types. They are generally safer, easier to use, interact with WordPress caching (like the object cache), respect WordPress query modifications (e.g., from pre_get_posts ), and return post objects. WP_Query is more flexible than get_posts() (which is a wrapper for WP_Query ).
- $wpdb : Use this global object for direct database interaction when:
- Performing complex SQL queries not easily achievable with WP_Query arguments (e.g., complex joins, subqueries on custom tables, specific GROUP BY clauses).
- Working with custom database tables that are not part of the WordPress posts structure.
- Needing highly optimized queries for specific, performance-critical tasks where WP_Query overhead is a concern (though this requires careful benchmarking).
- Crucially, always use $wpdb->prepare() when including variable data in your $wpdb queries to prevent SQL injection vulnerabilities
15. How do you develop a basic WordPress plugin?
Answer:
- Create a folder: In the wp-content/plugins/ directory, create a uniquely named folder for your plugin (e.g., my-custom-plugin ).
- Create the main plugin file: Inside this folder, create a PHP file with the same name (e.g., my-custom-plugin.php ).
- Add the plugin header: This is a PHP comment block at the top of your main plugin file that tells WordPress about your plugin. At a minimum, it needs Plugin Name:
- Add functionality: Use WordPress hooks (actions and filters), create functions, and define classes to implement your plugin’s features.
- Activate: Go to the Plugins page in the WordPress admin and activate your new plugin.
16. What is the role of WP-CLI?
Answer:
WP-CLI is a command-line tool for managing WordPress: update plugins, create users, manage posts, and more—ideal for automation.
17. How do you enqueue scripts conditionally?
Answer:
You use WordPress conditional tags within the function hooked to wp_enqueue_scripts (for frontend) or admin_enqueue_scripts (for backend) to determine if a script should be loaded.
function my_conditional_scripts() {
// Example 1: Only on the 'contact' page (by slug)
if (is_page('contact')) {
wp_enqueue_script(
'contact-form-script',
get_stylesheet_directory_uri() . '/js/contact-form.js', // Use get_stylesheet_directory_uri() for child theme assets
['jquery'],
'1.0.0',
true
);
}
// Example 2: Only on single blog posts
if (is_single() && 'post' == get_post_type()) {
wp_enqueue_script(
'single-post-script',
get_stylesheet_directory_uri() . '/js/single-post.js',
[],
'1.0.0',
true
);
}
// Example 3: Only for a specific custom post type archive
if (is_post_type_archive('product')) {
wp_enqueue_script(
'product-archive-script',
get_stylesheet_directory_uri() . '/js/product-archive.js',
['jquery'],
'1.0.0',
true
);
}
}
add_action('wp_enqueue_scripts', 'my_conditional_scripts');
Using get_stylesheet_directory_uri() is generally better for assets located within the active theme (especially child themes), while get_template_directory_uri() specifically points to the parent theme.
18. What is the difference between admin_enqueue_scripts and wp_enqueue_scripts ?
Answer:
admin_enqueue_scripts targets the admin dashboard. wp_enqueue_scripts is used for frontend assets.
19. How would you limit access to a plugin settings page?
Answer:
Use current_user_can(‘manage_options’) to check capability before displaying admin UI.
20. How do you sanitize user input in WordPress?
Answer:
Use sanitize_text_field() , esc_url_raw() , intval() , or sanitize_email() based on input type.
21. How do you handle login brute-force protection?
Answer:
Use plugins like Wordfence or implement login attempt limits, CAPTCHAs, or disable XML-RPC.
22. What is a good practice for file and folder permissions?
Answer:
- Files: 644
- Directories: 755
- Avoid 777 unless absolutely necessary
23. What is a transient in WordPress?
Answer:
A temporary cache mechanism using set_transient() and get_transient() with expiration times.
24. What is WordPress Multisite and when would you use it?
Answer:
Multisite allows running multiple websites from a single WordPress installation—ideal for networks or franchises.
25. How do you enable Multisite in an existing installation?
Answer:
- Backup Your Site: This is crucial before making core changes. Backup both files and the database.
- Allow Multisite: Open your wp-config.php file and add the following line before /* That’s all, stop editing! Happy publishing. */ :
define( ‘WP_ALLOW_MULTISITE’ , true);
- Network Setup: Save wp-config.php . Refresh your WordPress admin dashboard. Go to “Tools” -> “Network Setup.”
- Choose Structure: Decide if you want subdomains (e.g., site1.example.com ) or subdirectories (e.g., example.com/site1 ).
- Note for existing sites: If your WordPress installation has been active for more than a month, you might be restricted to subdomains due to potential permalink conflicts with existing content.
- Configure Network: Fill in the network details (Network Title, Admin Email). Click “Install.”
- Update wp-config.php and .htaccess : WordPress will provide you with code snippets to add to your wp-config.php file (again, before the /* That’s all… */ line) and your .htaccess file (replacing any existing WordPress rules).
- Re-login: After saving both files, you will need to log back into your WordPress admin. Your Multisite network is now enabled.
26. How would you make a WordPress site multilingual?
Answer:
Use WPML, Polylang, or a Multisite network with each site in a different language.
27. What are the pros and cons of using WPML vs Polylang?
Answer:
Both WPML and Polylang are popular solutions for creating multilingual WordPress sites.
- WPML (WordPress Multilingual Plugin):
- Pros: Very comprehensive feature set (string translation, media translation, WooCommerce compatibility, advanced translation management workflows, dedicated support). Long history and wide adoption. Often considered robust for complex or large enterprise sites.
- Cons: Premium (paid) plugin only. Can be resource-intensive (heavier) on some servers due to its extensive features. Learning curve can be steeper for some.
- Polylang:
- Pros: Core plugin is free and open-source, with optional paid add-ons for more features (like Polylang Pro). Generally more lightweight than WPML. Good community support. Often considered simpler to get started with for basic multilingual needs.
- Cons: Free version has limitations (e.g., no slug translation for CPTs without Pro, less integrated support for some complex themes/plugins compared to WPML out-of-the-box). Some advanced features found in WPML might require Pro add-ons or custom solutions.
The choice often depends on budget, the complexity of the site, specific feature requirements (like e-commerce or specific integrations), and developer preference.
28. What is ADA compliance and how does it relate to WordPress?
Answer:
ADA requires digital accessibility. WordPress themes and content should meet WCAG 2.1 standards for color contrast, keyboard use, and screen reader support.
29. How can you ensure your theme is accessible? What tools can you use to test accessibility compliance?
Answer:
Use semantic HTML, aria-labels, skip links, proper heading structure, and test with screen readers. Tools include, but are not limited to: WAVE, Axe, Lighthouse, or screen readers like NVDA or VoiceOver.
30. What are semantic HTML tags and why do they matter for accessibility?
Answer:
Tags like <article> , <nav> , <main> , and <section> help assistive technologies understand page structure.
31. How do you extend Elementor with custom widgets?
Answer:
To extend Elementor with custom widgets, you use Elementor’s Widget API. The basic steps are:
- Hook into Elementor: Use the elementor/widgets/register action hook (formerly elementor/widgets/widgets_registered ).
- Create a Widget Class: Your custom widget will be a PHP class that extends \Elementor\Widget_Base.
- Implement Required Methods: Inside your class, you must implement several methods:
- get_name() : Returns a unique machine-readable slug for your widget.
- get_title() : Returns the human-readable title displayed in the Elementor panel.
- get_icon() : Returns the CSS class for an icon (e.g., from eicons or Font Awesome) for your widget.
- get_categories() : Returns an array of categories where your widget will appear in the panel.
- _register_controls() : (Protected method) Defines the settings/controls for your widget using Elementor’s control types (e.g., text input, image chooser, repeater).
- render() : Defines the frontend HTML output of your widget based on its settings.
- content_template() : (Optional) Defines a JavaScript template for live preview rendering in the editor (improves editor performance).
- Register the Widget: Inside your hooked function, instantiate your widget class and register it using \Elementor\Plugin::instance()->widgets_manager->register_widget_type(new Your_Widget_Class()); .
// Example in your plugin or theme's functions.php
class My_Custom_Elementor_Widget extends \Elementor\Widget_Base {
// Implement required methods: get_name, get_title, get_icon, get_categories, _register_controls, render
public function get_name() { return 'my-custom-widget'; }
public function get_title() { return __( 'My Custom Widget', 'your-textdomain' ); }
// ... other methods
protected function _register_controls() { /* ... Add controls ... */ }
protected function render() { /* ... Output HTML ... */ }
}
function register_my_custom_elementor_widgets( $widgets_manager ) {
require_once ( __DIR__ . '/widgets/my-custom-widget.php' ); // Assuming your widget class is in this file
$widgets_manager->register( new My_Custom_Elementor_Widget() );
}
add_action( 'elementor/widgets/register', 'register_my_custom_elementor_widgets' );
32. What are the performance implications of using Elementor?
Answer:
It adds extra scripts and DOM weight. Use caching, defer scripts, and optimize assets to mitigate.
33. How do Elementor templates differ from traditional PHP templates?
Answer:
Traditional PHP Templates: These are physical .php files within your theme’s directory (e.g., single.php , page.php , header.php ). WordPress’s template hierarchy determines which file is used to render a specific view. They primarily use PHP, HTML, and WordPress template tags to dynamically generate content.
Elementor Templates: These are not typically physical PHP files in the theme structure in the same way.
- They are often stored as a custom post type in the WordPress database (e.g., elementor_library or when applied directly to a page/post, the content is stored in post meta).
- They are built using the Elementor visual editor and store their structure and styling as JSON data.
- Elementor’s rendering engine then takes this JSON data and dynamically generates the HTML output, often bypassing or overriding parts of the traditional PHP template hierarchy for the content area it controls (e.g., the_content() is replaced by Elementor’s output).
- While you can create PHP templates for theme elements like headers/footers using Elementor Pro’s Theme Builder, the design itself is still managed through the Elementor interface and stored as data.
34. How would you restrict access to an Elementor section?
Answer:
Use shortcodes with conditionals or membership plugins to show/hide content blocks.
35. How do you use ACF in templates?
Answer:
Use get_field(‘field_name’) or the_field() to display custom field values in theme files.
36. How does get_field() differ from get_post_meta() ?
Answer:
get_field() is from ACF—easier syntax and auto-formatting. get_post_meta() is native and lower-level.
37. How do you override a plugin template from your theme?
Answer:
Copy the template file into a matching path in your theme (e.g., yourtheme/woocommerce/ ) to override.
38. What is the template_include hook used for?
Answer:
To programmatically override which template file is used for rendering a request.
39. How do you apply lazy loading to images in WordPress?
Answer:
Add loading=”lazy” to <img> tags or rely on WordPress native lazy-loading (from v5.5+).
40. What’s the difference between wp_query and query_posts ?
Answer:
WP_Query : This is a PHP class used to create custom queries for posts (or any post type). It’s the preferred and safest method for fetching posts in secondary loops (i.e., loops other than the main page query). You create a new instance: $custom_query = new WP_Query($args); . It does not interfere with the main page query. The global $wp_query object (lowercase) is an instance of WP_Query that holds the main query for the current page.
query_posts() : This function modifies the main WordPress query. It directly alters the global $wp_query object. Its use is strongly discouraged because it can lead to unexpected behavior, break pagination, and is inefficient as it re-runs the main SQL query. For custom loops, always use a new instance of WP_Query . For modifying the main query before it runs, use the pre_get_posts action hook.
41. How can you bulk update metadata in WordPress via CLI?
Answer:
You can bulk update post metadata using WP-CLI with wp post meta update or wp post meta patch update in conjunction with wp post list and shell commands like xargs or a while loop.
Using xargs (ensure wp post list output is compatible):
# Example: Set ‘featured’ meta to ‘true’ for all posts of type ‘product’
wp post list –post_type=product –format=ids | xargs -I % wp post meta update % ‘_featured’ ‘yes’
# Example: Update ‘on_sale’ to ‘yes’ for products with an ‘old_price’ meta key
wp post list –post_type=product –meta_key=old_price –format=ids | xargs -I % wp post meta update % ‘_on_sale’ ‘true’
Using a while loop (often more robust for handling IDs):
wp post list –post_type=product –format=ids | while read id; do
wp post meta update “$id” ‘_stock_status’ ‘instock’
done
For more complex operations: You might write a custom WP-CLI command (a PHP script that extends WP-CLI’s functionality) to perform more intricate logic before updating metadata. Always backup your database before running bulk operations. Consider using –dry-run where available if you’re testing a complex command.
42. How do you handle 301 redirects in WordPress?
Answer:
Use wp_redirect() with 301 status, the Redirection plugin, or .htaccess rules.
43. How do you perform a safe search-and-replace of site URLs?
Answer:
The safest and recommended way to perform a search-and-replace of site URLs in WordPress, especially for serialized data in the database, is to use WP-CLI. The command is wp search-replace .
wp search-replace ‘http://oldurl.com’ ‘https://newurl.com’ –recurse-objects –report-changed-only –all-tables –dry-run
- ‘http://oldurl.com’ : The old string/URL.
- ‘https://newurl.com’ : The new string/URL.
- –recurse-objects : This is crucial as it handles PHP serialized data in the database correctly (e.g., in theme options, widget settings).
- –report-changed-only : Provides a cleaner output.
- –all-tables : Searches all tables in the WordPress database that have the WP prefix. You can also specify tables manually.
- –dry-run : Highly recommended first step. This will show you what changes would be made without actually making them.
After confirming the dry-run output is correct, remove –dry-run to perform the actual replacement:
wp search-replace ‘http://oldurl.com’ ‘https://newurl.com’ –recurse-objects –report-changed-only –all-tables
Always backup your database before running this command.
44. What is the role of wp_options and what are autoloaded options?
Answer:
wp_options stores site settings. Autoloaded options are loaded on every request—keep minimal to avoid bloat.
45. How do you create a custom Gutenberg block?
Answer:
Creating a custom Gutenberg block typically involves JavaScript (ESNext/React) and optionally PHP for server-side rendering or asset registration. The modern approach uses block.json for metadata.
Setup: Use the @wordpress/create-block tool for scaffolding:
block.json (Metadata File): Located in your block's src (or root) directory, this file defines the block's properties (editorScript, editorStyle, and style paths point to build artifacts. WordPress automatically handles enqueuing these):
JavaScript (src/edit.js): The editor interface for your block, using React components.
npx @wordpress/create-block my-custom-block
cd my-custom-block
npm start # For development
{
"apiVersion": 3,
"name": "my-namespace/my-custom-block",
"title": "My Custom Block",
"category": "widgets",
"icon": "smiley",
"description": "A brief description.",
"supports": {
"html": false
},
"attributes": {
"message": {
"type": "string",
"source": "html",
"selector": "p"
}
},
"textdomain": "my-custom-block",
"editorScript": "file:./build/index.js",
"editorStyle": "file:./build/editor.css",
"style": "file:./build/style-index.css"
}
import { useBlockProps, RichText } from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';
export default function Edit({ attributes, setAttributes }) {
const blockProps = useBlockProps();
return (
setAttributes({ message: val })}
placeholder={__('Enter your message here...', 'my-custom-block')}
/>
);
}
JavaScript (src/save.js): Defines how the block's content is saved to post content (serialized).
import { useBlockProps, RichText } from '@wordpress/block-editor';
export default function save({ attributes }) {
const blockProps = useBlockProps.save();
return (
);
}
PHP (Main Plugin File or src/init.php if using @wordpress/create-block structure): Registers the block type using metadata from block.json.
Build: Run npm run build to create production assets.
function my_custom_block_init() {
register_block_type(__DIR__ . '/build'); // Points to the directory containing block.json and build files
}
add_action('init', 'my_custom_block_init');
@wordpress/create-block handles much of the setup for build tools (webpack) and dependencies like wp-blocks, wp-element, wp-block-editor, wp-components, and wp-i18n.
46. How do you implement role-based access control?
Answer:
Implementing Role-Based Access Control (RBAC) in WordPress involves managing roles and their associated capabilities:
Understanding Capabilities: WordPress has a granular system of capabilities (e.g., edit_posts, publish_posts, manage_options). Roles are collections of these capabilities.
Checking Capabilities: Use current_user_can('capability_name') to check if the current user has a specific capability before allowing an action or displaying content.
if ( current_user_can( 'edit_others_posts' ) ) {
// Show or do something only users who can edit others' posts can do
}
Adding/Removing Custom Roles:
add_role('role_slug', 'Display Name', ['capability1' => true, 'capability2' => true]);
remove_role('role_slug'); These are best done on plugin activation/deactivation hooks to avoid re-adding them on every page load.
Adding/Removing Capabilities from Existing Roles:
$editor_role = get_role('editor');
$editor_role->add_cap('my_custom_capability', true);
$editor_role->remove_cap('existing_capability'); Also typically done on activation/deactivation.
Custom Capabilities: Define your own capabilities related to your plugin/theme features to provide fine-grained control. Then, assign these custom capabilities to roles.
For example, to restrict access to a plugin's settings page:
function my_plugin_settings_page() {
if ( ! current_user_can( 'manage_options' ) ) { // 'manage_options' is a common capability for settings
wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
}
// Display settings page HTML
}
Or with a custom capability:
// On plugin activation:
$admin_role = get_role( 'administrator' );
$admin_role->add_cap( 'manage_my_plugin_settings', true );
// In settings page function:
if ( ! current_user_can( 'manage_my_plugin_settings' ) ) {
wp_die( ... );
}
47. What is WP-Cron, how does it work, and what are its limitations? How would you schedule a custom recurring event?
Answer:
WP-Cron is WordPress’s internal system for handling scheduled tasks, like checking for updates, publishing scheduled posts, or running custom plugin/theme events.
- How it works: WP-Cron doesn’t run continuously like a true system cron job. Instead, it’s triggered by page visits. On nearly every page load, WordPress checks if there are any scheduled cron events due. If there are, it spawns an asynchronous (non-blocking) HTTP request back to itself ( wp-cron.php ) to process those jobs.
- Limitations:
- Dependency on Traffic: If a site has very low traffic, scheduled tasks might be delayed significantly because WP-Cron only runs when someone visits the site.
- Performance on High-Traffic Sites: On high-traffic sites, the constant checking on every page load can add overhead. Spawning requests to wp-cron.php can also sometimes lead to race conditions or performance issues if not managed well.
- Not Precise: It’s not suitable for highly time-sensitive tasks requiring exact execution times.
- Scheduling a Custom Recurring Event:
- Define a custom hook: This is the action that will be triggered.
- Schedule the event: Use wp_schedule_event(time(), ‘hourly’, ‘my_custom_cron_hook’); . This is typically done on plugin activation. You need to ensure it’s not scheduled multiple times using wp_next_scheduled() .
- Create the callback function: Hook your function to the custom action defined above.
- Unschedule on deactivation: Use wp_clear_scheduled_hook(‘my_custom_cron_hook’); .
- (Optional) Define custom recurrence intervals: Use the cron_schedules filter.
// On plugin activation
function my_plugin_activate() {
if ( ! wp_next_scheduled( 'my_custom_cron_hook' ) ) {
wp_schedule_event( time(), 'hourly', 'my_custom_cron_hook' ); // Can also use 'daily', 'twicedaily'
}
}
register_activation_hook( __FILE__, 'my_plugin_activate' );
// The action hooked to the cron event
function my_custom_cron_function() {
// Do something, like sending an email, cleaning up data, etc.
wp_mail( 'admin@example.com', 'Hourly Report', 'This is your hourly cron job report.' );
}
add_action( 'my_custom_cron_hook', 'my_custom_cron_function' );
// On plugin deactivation
function my_plugin_deactivate() {
wp_clear_scheduled_hook( 'my_custom_cron_hook' );
}
register_deactivation_hook( __FILE__, 'my_plugin_deactivate' );
For more reliability, especially on production sites, it’s often recommended to disable the default WP-Cron triggering mechanism by adding define(‘DISABLE_WP_CRON’, true); to wp-config.php and setting up a real system cron job on the server to hit wp-cron.php at regular intervals (e.g., every 5 minutes).
48. Where is the data for Custom Post Types (CPTs) and their associated post meta stored in the WordPress database?
Answer:
Data for Custom Post Types and their metadata is stored primarily in two core WordPress database tables:
- wp_posts table:
- Each entry for a CPT is stored as a row in the wp_posts table, just like regular posts and pages.
- The post_type column in this table is what differentiates a CPT entry from a regular post or page (e.g., if your CPT is ‘book’, the post_type column will store ‘book’).
- Other columns like post_title , post_content , post_status , post_author , post_date , post_modified , post_name (slug), menu_order , etc., are used for CPTs as well.
- wp_postmeta table:
- Custom fields (also known as post meta) associated with a CPT are stored in the wp_postmeta table.
- This table has a key-value structure:
- post_id : A foreign key linking back to the ID column in the wp_posts table. This associates the meta with a specific CPT entry.
- meta_key : The name (or key) of the custom field (e.g., ‘_isbn’, ‘_author_name’, ‘_price’).
- meta_value : The value of that custom field. This can store strings, numbers, or even serialized arrays/objects.
- A single CPT entry can have multiple rows in wp_postmeta for its various custom fields.
When you register a CPT, you’re essentially telling WordPress to treat rows in wp_posts with a specific post_type value in a particular way, often with its own admin UI, template files, and rewrite rules.
49. What is object caching in WordPress, and why is it important for performance? Can you name a common external object caching system used with WordPress?
Answer:
Object Caching in WordPress is a mechanism for storing the results of complex or frequently executed operations (especially database queries) in memory or a fast data store, so they can be retrieved quickly without re-executing the operation.
- How it works: WordPress has a built-in WP_Object_Cache class. By default, it provides non-persistent caching, meaning the cache lasts only for the duration of a single page load. However, this can be extended with persistent caching backends. When data is requested (e.g., options, post objects, term objects), WordPress first checks the object cache. If the data is found (a “cache hit”), it’s returned immediately. If not (a “cache miss”), WordPress fetches it from the database, stores it in the cache for future requests, and then returns it.
- Importance for Performance:
- Reduces Database Load: Significantly decreases the number of queries made to the database, which is often the primary bottleneck for site speed.
- Faster Page Loads: Retrieving data from memory (or a fast cache store) is much quicker than querying the database.
- Improved Scalability: Helps the site handle more concurrent users without overwhelming the database server.
- Common External Object Caching Systems: To make object caching persistent across page loads and more effective, WordPress can integrate with external object caching systems. A persistent object cache plugin (like a Redis or Memcached connector) is needed to interface with these systems. Common systems include:
- Redis: An in-memory data structure store, used as a database, cache, and message broker. Very popular for WordPress object caching.
- Memcached: A high-performance, distributed memory object caching system. Also widely used.
Using a persistent object cache is a crucial step in optimizing a WordPress site, especially for sites with dynamic content or high traffic.
50. When creating a custom shortcode using the Shortcode API, how do you handle attributes passed to it and the content enclosed within the shortcode tags? Provide a simple example.
Answer:
When creating a custom shortcode with add_shortcode() , your callback function receives three potential arguments:
function greet_user_shortcode_handler( $atts, $content = null, $shortcode_tag = '' ) {
// 1. Normalize attributes with default values
$atts = shortcode_atts(
[
'name' => 'Guest', // Default name
'greeting' => 'Hello', // Default greeting
'class' => 'greeting-box', // Default CSS class
],
$atts,
$shortcode_tag // Pass the tag for better error reporting if needed
);
// Sanitize attributes before use (example)
$name = sanitize_text_field( $atts['name'] );
$greeting = sanitize_text_field( $atts['greeting'] );
$class = esc_attr( $atts['class'] );
// 2. Handle the enclosed content (if any)
$enclosed_content_html = '';
if ( ! is_null( $content ) ) {
// Process the enclosed content, e.g., run shortcodes within it, wpautop, etc.
$enclosed_content_html = '' . do_shortcode( wpautop( $content ) ) . '';
}
// 3. Construct the output
$output = sprintf(
'%s, %s!%s',
$class,
esc_html( $greeting ),
esc_html( $name ),
$enclosed_content_html // Already processed
);
return $output; // Shortcodes must return their output, not echo it.
}
add_shortcode( 'greet_user', 'greet_user_shortcode_handler' );
Usage in post content:
[greet_user name=”Alice” greeting=”Hi”]Thanks for visiting our site.[greet_user]
(uses default name and greeting, custom class, no enclosed content)
Advanced Level (3+ Years)
1. Describe the full WordPress template hierarchy.
Answer:
The WordPress template hierarchy determines which PHP template file from the active theme is used to render a page, cascading from specific to general templates. For example, for a single post of a custom post type ‘event’ with slug ‘jazz-fest’, WordPress checks:
- single-event.php (single post of CPT ‘event’)
- single.php (any single post)
- singular.php (any single post, page, or CPT)
- index.php (ultimate fallback).
(Note: single-event-jazz-fest.php is possible but rarely used due to its specificity.) Similar hierarchies apply to: - Archives: archive-{post_type}.php, archive.php, index.php.
- Taxonomies: taxonomy-{taxonomy}-{term}.php, taxonomy-{taxonomy}.php, taxonomy.php, archive.php, index.php.
- Categories: category-{slug}.php, category-{id}.php, category.php, archive.php, index.php.
- Pages: page-{slug}.php, page-{id}.php, page.php, singular.php, index.php.
- Front Page: front-page.php, home.php, index.php.
- Blog Index: home.php, index.php.
- Search: search.php, index.php.
- 404: 404.php, index.php.
Modern themes often use get_template_part() to include reusable partials (e.g., get_template_part(‘template-parts/content’, ‘event’)), enhancing modularity. Understanding the hierarchy is critical for theme development and customization.
2. How would you optimize database performance in large sites?
Answer:
Optimizing database performance for large WordPress sites involves:
- Proper Indexing: Add indexes to columns in wp_posts, wp_postmeta, wp_users, wp_usermeta, and custom tables used in WHERE, JOIN, or ORDER BY clauses.
- Object Caching: Use persistent caching (e.g., Redis, Memcached) to reduce database queries.
- Limit Autoloaded Options: Audit wp_options to minimize autoloaded data, which loads on every request.
- Query Optimization: Use Query Monitor or New Relic to identify slow queries. Prefer WP_Query for standard queries; use optimized $wpdb for complex cases with prepare().
- Database Cleanup: Remove old revisions, spam comments, trashed items, and orphaned meta using WP-CLI or plugins like WP-Optimize.
- Transient Management: Cache expensive operations with transients, set appropriate expirations, and clean expired transients.
wp transient delete --expired
- Offload Queries: Use background processes or read replicas for intensive tasks.
- Server Configuration: Optimize MySQL/MariaDB (e.g., InnoDB buffer pool size, max connections) and consider read replicas for high-traffic sites. Regular maintenance ensures scalability and speed.
3. How do you integrate Redis or Memcached with WordPress?
Answer:
Integrate Redis or Memcached to enable persistent object caching, reducing database load.
- Install Server: Install Redis/Memcached on the server (e.g., apt install redis-server for Redis).
- Install PHP Extension: Ensure the PHP extension (php-redis or php-memcached) is installed.
- Use a Plugin: Install a plugin like “Redis Object Cache” or “W3 Total Cache”. For Redis:
- Activate “Redis Object Cache”.
- Add to wp-config.php:
define('WP_REDIS_HOST', '127.0.0.1');
define('WP_REDIS_PORT', 6379);
- Verify: Check the plugin’s diagnostics page to confirm Redis is connected.
- Monitor: Use tools like Redis CLI (redis-cli monitor) to track cache usage.
Note: Ensure the cache is invalidated properly during updates to avoid stale data. Plugins like W3 Total Cache also support Redis/Memcached for full-page caching.
4. What is WordPress VIP and how does it differ from standard hosting?
Answer:
WordPress VIP is an enterprise-grade managed hosting platform for WordPress, offering:
- Infrastructure: High-availability cloud hosting with auto-scaling, load balancing, and CDN integration for global performance.
- Security: Strict code reviews, automated scans, and enterprise-grade firewalls.
- Deployment: Git-based workflows with pull request (PR) reviews and automated deployments.
- Standards: Enforces WordPress VIP coding standards, requiring pre-approved plugins and custom code vetting.
- Support: Dedicated account managers and 24/7 technical support.
Differences from Standard Hosting: - Standard hosting varies widely (shared, VPS, managed), often lacks VIP’s performance optimizations, security rigor, and enterprise support.
- VIP restricts plugin usage and enforces code quality, unlike standard hosting’s flexibility.
- VIP’s pricing is premium, targeting large organizations, while standard hosting suits smaller budgets.
Ideal for high-traffic, mission-critical sites requiring scalability and compliance.
5. What are the limitations of using plugins on VIP?
Answer:
WordPress VIP restricts plugin usage to ensure performance, security, and compatibility:
- Pre-Approved Plugins: Only plugins from VIP’s curated list (e.g., Jetpack, Yoast SEO) are allowed.
- Custom Plugin Review: Custom or third-party plugins require a rigorous code review by VIP’s team, assessing security, performance, and adherence to VIP coding standards. This process can delay deployment.
- No Unsupported Plugins: Plugins not meeting VIP’s standards (e.g., poorly optimized or insecure) are prohibited.
- Workarounds: Developers can replicate plugin functionality via custom code in themes or must-use (MU) plugins, which also undergo review.
- Impact: Ensures stability but limits flexibility compared to standard WordPress environments. Regular communication with VIP support is key for plugin integration.
6. What is the best way to harden a WordPress installation?
Answer:
Hardening a WordPress installation enhances security by:
- Disabling File Editing: Add to wp-config.php:
define('DISALLOW_FILE_EDIT', true);
- Strong Passwords: Enforce via plugins like iThemes Security.
- Security Plugins: Use Wordfence or Sucuri for malware scanning and firewalls.
- Limit Login Attempts: Use plugins like Limit Login Attempts Reloaded or code:
add_filter('xmlrpc_enabled', '__return_false');
- File Permissions: Set files to 644, directories to 755.
- Advanced Measures:
- Enable 2FA with plugins like Two-Factor.
- Use a Web Application Firewall (WAF) via Cloudflare or Sucuri.
- Restrict admin access by IP:
add_action('init', function() {
if (is_admin() && !in_array($_SERVER['REMOTE_ADDR'], ['ALLOWED_IP'])) {
wp_die('Access restricted.');
}
});
- Updates: Keep core, themes, and plugins updated. Regular audits and backups are critical.
7. How do you set up security headers in WordPress?
Answer:
Set security headers to protect against common attacks using PHP hooks or server configuration:
PHP Method: Use wp_headers or send_headers:
function add_security_headers() {
header('X-Frame-Options: DENY');
header('X-Content-Type-Options: nosniff');
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
header('Content-Security-Policy: default-src \'self\'; script-src \'self\' https://trusted.cdn.com;');
}
add_action('send_headers', 'add_security_headers');
Apache (.htaccess):
Header set X-Frame-Options "DENY"
Header set X-Content-Type-Options "nosniff"
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Notes: Content-Security-Policy (CSP) requires careful configuration to avoid blocking legitimate scripts/styles. Test headers with tools like securityheaders.com. Use plugins like HTTP Headers for simpler management.
8. How do you detect and mitigate XSS and SQL injection in WP?
Answer:
- Cross-Site Scripting (XSS):
- Mitigation: Escape output with esc_html(), esc_attr(), esc_url(), or wp_kses_post(). Use nonces for form submissions:
wp_nonce_field('my_action', 'my_nonce');
if (!wp_verify_nonce($_POST['my_nonce'], 'my_action')) {
wp_die('Invalid nonce.');
}
- Detection: Monitor logs with security plugins (e.g., Wordfence) for suspicious <script> injections.
- SQL Injection:
- Mitigation: Use $wpdb->prepare() for database queries:
global $wpdb;
$results = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE post_type = %s",
'book'
));
- Detection: Use Query Monitor to spot unescaped queries; audit code for direct $_POST/ $_GET usage.
Additional Measures: Sanitize inputs with sanitize_text_field(), sanitize_email(), etc. Employ a WAF (e.g., Cloudflare) and regular security audits to catch vulnerabilities.
9. How do you build a custom Gutenberg block with React?
Answer:
Build a custom Gutenberg block using @wordpress/scripts or manually with registerBlockType and React/JSX.
Example:
import { registerBlockType } from '@wordpress/blocks';
import { TextControl } from '@wordpress/components';
registerBlockType('myplugin/custom-block', {
edit: ({ attributes, setAttributes }) => (
setAttributes({ message: val })}
/>
),
save: ({ attributes }) => {attributes.message},
});
10. What’s the difference between dynamic and static blocks?
Answer:
Static Blocks: Save rendered HTML in the post content during editing. Ideal for fixed content.
Dynamic Blocks: Render content via PHP render_callback at runtime, pulling real-time data (e.g., latest posts). Dynamic blocks are better for content that changes frequently, but they require server-side processing.
Example (Dynamic Block):
register_block_type('myplugin/dynamic-block', {
render_callback: function($attributes) {
return '' . esc_html($attributes['message']) . ' (Rendered at ' . current_time('mysql') . ')';
},
attributes: {
message: { type: 'string' },
},
});
11. How do you extend, localize, and register custom patterns/styles for Gutenberg blocks?
Answer:
Use `register_block_pattern()` and `register_block_style()` in a theme or plugin’s PHP files.
- Extending Core Blocks: Use filters like `blocks.registerBlockType` to modify core blocks or add variations:
wp.blocks.registerBlockVariation('core/paragraph', {
name: 'custom-paragraph',
title: 'Custom Paragraph',
attributes: { className: 'custom-para' },
});
- Localizing Blocks: Use `wp.i18n` for translations and `wp_set_script_translations()`:
import { __ } from '@wordpress/i18n';
// In block edit function
{__('Enter text', 'myplugin')}
wp_set_script_translations('myplugin-block-editor', 'myplugin', plugin_dir_path(__FILE__) . 'languages');
- Registering Patterns/Styles: Use `register_block_pattern()` and `register_block_style()`:
// Pattern
register_block_pattern('myplugin/hero', [
'title' => __('Hero Section', 'myplugin'),
'content' => '...',
'categories' => ['header'],
]);
// Style
register_block_style('core/button', [
'name' => 'custom-button',
'label' => __('Custom Button', 'myplugin'),
]);
12. How would you architect a scalable multisite network for 500+ sites?
Answer:
Architecting a scalable multisite network for 500+ sites requires careful planning across infrastructure, database, and application layers:
Infrastructure:
- Load Balancing: Distribute traffic across multiple web servers.
- Hosting: Use high-performance servers with fast I/O and auto-scaling.
- CDN: Serve static assets and pages via Cloudflare or Akamai.
- Database: Use a dedicated MySQL/MariaDB server with read replicas for load distribution.
Database Optimization:
- Object Caching: Use Redis/Memcached to cache queries, especially `get_blog_details`:
function cached_get_blog_details($blog_id) {
$cache_key = 'blog_details_' . $blog_id;
$details = wp_cache_get($cache_key, 'multisite');
if (false === $details) {
$details = get_blog_details($blog_id);
wp_cache_set($cache_key, $details, 'multisite', 3600);
}
return $details;
}
13. How do you manage user roles, themes, plugins, and SEO in a WordPress multisite network?
Answer:
Managing a WordPress multisite network involves:
- User Roles: Use `add_user_to_blog()` to assign roles per site or plugins like User Role Sync for network-wide consistency:
add_user_to_blog($blog_id, $user_id, 'editor');
- Theme Updates: Update themes in the shared `wp-content/themes` folder. Use WP-CLI to flush caches:
wp cache flush --url=https://site.com
- Plugin Management: Activate plugins per site or use `get_current_blog_id()` to restrict execution:
if (get_current_blog_id() === 2) {
// Plugin logic for site ID 2
}
- SEO Optimization: Use per-site sitemaps, correct canonical tags, and plugins like Yoast SEO. Avoid duplicate content with unique site configurations and robots.txt per site:
add_filter('robots_txt', function($output, $public) {
if (get_current_blog_id() === 1) {
$output .= "Disallow: /private/\n";
}
return $output;
}, 10, 2);
14. What’s the difference between internationalization and localization?
Answer:
Internationalization (i18n): Making software translatable. Localization (l10n): Providing translations for specific locales.
15. How do you make a plugin translation-ready?
Answer:
To make a WordPress plugin translation-ready (internationalized or i18n’d):
- Use Gettext Functions: Wrap all translatable strings in your plugin’s PHP code with the appropriate WordPress Gettext functions:
__('Hello World', 'my-plugin');
- Declare Text Domain and Domain Path in Plugin Header:
/**
* Plugin Name: My Awesome Plugin
* Text Domain: my-awesome-plugin
* Domain Path: /languages
*/
- Load the Text Domain:
function my_awesome_plugin_load_textdomain() {
load_plugin_textdomain(
'my-awesome-plugin',
false,
dirname(plugin_basename(__FILE__)) . '/languages/'
);
}
add_action('plugins_loaded', 'my_awesome_plugin_load_textdomain');
16. How do you make a WordPress plugin translation-ready and manage translations?
Answer:
To make a plugin translation-ready and manage translations:
- Internationalization (i18n): Use Gettext functions (`__()`, `_e()`, `_x()`, `_n()`, `esc_html__()`) with a unique text domain:
echo __('Hello', 'myplugin');
- Text Domain Setup: Declare in plugin header:
/*
* Plugin Name: My Plugin
* Text Domain: myplugin
* Domain Path: /languages
*/
- Load Text Domain:
add_action('plugins_loaded', function () {
load_plugin_textdomain('myplugin', false, dirname(plugin_basename(__FILE__)) . '/languages/');
});
- Generate `.pot` File: Use WP-CLI (`wp i18n make-pot . languages/myplugin.pot`) or Poedit.
- Create Translations: Translators create `.po` files (e.g., `myplugin-fr_FR.po`) and compile to `.mo` files for runtime use.
- Manage Updates: Use GlotPress or WordPress.org Translate to crowdsource translations. Review and merge via tools like Poedit or plugins.
- Localization (l10n): Ensure `.mo` files are in the `languages` folder. WordPress loads translations based on the site’s locale. Test with multiple languages to verify.
17. How do you ensure WCAG 2.1 AA compliance and build accessible forms in WordPress themes?
Answer:
To achieve WCAG 2.1 AA compliance and build accessible forms:
- WCAG Compliance:
- Ensure 4.5:1 color contrast for text.
- Support keyboard navigation (e.g., focus states for buttons).
- Use ARIA attributes (e.g., `role=”navigation”`, `aria-label=”Main menu”`).
- Provide text alternatives for images (`alt` attributes).
Skip to content
Email
Invalid email.
18. What are accessible forms and how do you build them in WordPress?
Answer:
Use labeled inputs, keyboard-friendly UI, error summaries, and semantic structure. Use `aria-describedby` for additional context.
Username
Enter your username.
19. How do you use the WAVE tool and Axe plugin in theme audits?
Answer:
Scan your site with WAVE or Axe browser plugins to identify contrast issues, missing labels, and navigation flaws.
20. How do you implement CI/CD, linting, and deployment for a WordPress project?
Answer:
- Use PHP CodeSniffer for linting:
./vendor/bin/phpcs --standard=WordPress .
- Set up GitHub Actions for CI/CD:
name: Deploy
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: composer install
- run: ./vendor/bin/phpcs --standard=WordPress .
- uses: appleboy/ssh-action@v0.1.10
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: rsync -avz ./ wp-content/plugins/myplugin/
21. How do you test database changes in staging vs production?
Answer:
Test database changes safely by:
function myplugin_update_db() {
$current_version = get_option('myplugin_db_version', '1.0');
if (version_compare($current_version, '1.1', '<')) {
global $wpdb;
$wpdb->query("ALTER TABLE {$wpdb->prefix}custom_table ADD new_column VARCHAR(255)");
update_option('myplugin_db_version', '1.1');
}
}
add_action('admin_init', 'myplugin_update_db');
22. How would you rewrite the main query based on custom logic?
Answer:
Use the `pre_get_posts` hook to modify query parameters before execution.
add_action('pre_get_posts', function ($query) {
if (!is_admin() && $query->is_main_query()) {
$query->set('post_type', 'custom_post_type');
}
});
23. What are must-use plugins and how are they different?
Answer:
Must-Use plugins (MU plugins) are plugins located in the `wp-content/mu-plugins` directory (this directory may need to be created). They are automatically loaded and cannot be deactivated via the admin interface.
24. How would you build a REST API endpoint for a CPT archive?
Answer:
Use `register_rest_route()` and return data via a custom callback that queries the CPT:
add_action('rest_api_init', function () {
register_rest_route('myplugin/v1', '/books', [
'methods' => 'GET',
'callback' => function () {
$query = new WP_Query(['post_type' => 'book']);
return $query->posts;
},
]);
});
25. How do you set up and optimize object-oriented plugin architecture?
Answer:
Structure code into classes, use autoloaders (Composer), dependency injection, and split responsibilities.
26. How do you implement secure data sanitization in WordPress plugins?
Answer:
Sanitize data to prevent security issues:
- Input Sanitization: Use functions like `sanitize_text_field()`, `sanitize_email()`, `sanitize_key()`, or `wp_kses_post()` for HTML content:
$input = sanitize_text_field($_POST['name']); // Plain text
$content = wp_kses_post($_POST['content']); // Allow post HTML
- Custom Rules: Define allowed HTML tags for `wp_kses`:
$allowed_tags = array(
'a' => array('href' => true, 'title' => true),
'strong' => array(),
'p' => array(),
);
$content = wp_kses($_POST['content'], $allowed_tags);
- Validation: Combine with validation (e.g., `is_email()`, `is_numeric()`) before sanitization:
if (is_email($_POST['email'])) {
$email = sanitize_email($_POST['email']);
} else {
wp_die('Invalid email');
}
27. How do you perform code profiling in WordPress?
Answer:
Use Query Monitor, New Relic, Xdebug, or Tideways to measure performance of hooks, queries, and function calls.
28. How would you create a role with custom capabilities?
Answer:
Use `add_role()` with custom capabilities array:
function setup_custom_shop_manager_role() {
if (!get_role('shop_manager_custom')) { // Use a unique slug
add_role(
'shop_manager_custom', // Role slug
'Shop Manager (Custom)', // Display name
[
'read' => true, // Basic reading capability
'edit_posts' => false, // Can't edit general posts
'delete_posts' => false, // Can't delete general posts
'edit_products' => true, // Custom capability
'publish_products' => true, // Custom capability
'delete_products' => true, // Custom capability
'edit_others_products' => true, // Custom capability
'delete_others_products' => true, // Custom capability
'read_private_products' => true, // Custom capability
'manage_product_terms' => true, // Custom capability for taxonomies like categories/tags
'upload_files' => true,
]
);
}
}
register_activation_hook(__FILE__, 'setup_custom_shop_manager_role');
29. How do you override user authentication to use an external system?
Answer:
Hook into the `authenticate` filter to validate credentials via API, LDAP, or SSO before WordPress’s default authentication:
function custom_authenticate($user, $username, $password) {
if (is_a($user, 'WP_User') || empty($username) || empty($password)) {
return $user;
}
$external_auth = authenticate_with_external_api($username, $password);
if ($external_auth['success']) {
$wp_user = get_user_by('login', $username);
if (!$wp_user) {
$user_id = wp_create_user($username, $password, $external_auth['email']);
$wp_user = get_user_by('id', $user_id);
}
return $wp_user;
}
return new WP_Error('invalid_credentials', 'Invalid external authentication');
}
add_filter('authenticate', 'custom_authenticate', 20, 3);
30. How would you queue background tasks in WordPress?
Answer:
Use `wp_schedule_event()` or a library like Action Scheduler to offload tasks to cron or async queues.
31. How do you implement advanced caching strategies beyond object caching?
Answer:
Implement page-level caching with Varnish or Nginx FastCGI cache, database query caching with persistent object cache, fragment caching for expensive template parts using transients, and CDN integration for static assets. Use cache invalidation strategies and implement cache warming for critical pages.
32. How would you create a custom WP-CLI command for bulk operations?
Answer:
Create a class that extends `WP_CLI_Command` and register it:
class Custom_CLI_Commands extends WP_CLI_Command {
public function update_post_meta($args, $assoc_args) {
$post_type = $assoc_args['post_type'] ?? 'post';
$meta_key = $assoc_args['meta_key'];
$meta_value = $assoc_args['meta_value'];
$posts = get_posts([
'post_type' => $post_type,
'posts_per_page' => -1,
'post_status' => 'publish',
]);
foreach ($posts as $post) {
update_post_meta($post->ID, $meta_key, $meta_value);
WP_CLI::log("Updated post {$post->ID}");
}
WP_CLI::success("Updated " . count($posts) . " posts");
}
}
WP_CLI::add_command('custom', 'Custom_CLI_Commands');
33. How do you implement proper error handling and logging in WordPress plugins?
Answer:
Use WordPress debugging constants, custom error handlers, and structured logging:
// Enable in wp-config.php
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
// Custom error logging function
function log_plugin_error($message, $context = []) {
if (WP_DEBUG === true) {
if (is_array($message) || is_object($message)) {
error_log('Plugin Error: ' . print_r($message, true));
} else {
error_log('Plugin Error: ' . $message . ' Context: ' . print_r($context, true));
}
}
}
// Exception handling
try {
$result = risky_function();
} catch (Exception $e) {
log_plugin_error('Function failed: ' . $e->getMessage(), [
'file' => $e->getFile(),
'line' => $e->getLine(),
]);
return new WP_Error('operation_failed', 'Operation could not be completed');
}
34. How do you implement advanced custom post type relationships and querying?
Answer:
Implementing advanced CPT relationships (one-to-many, many-to-many) and querying them efficiently involves several approaches:
- Post Meta (for simple one-to-many or one-to-one):
// Example: A 'book' CPT has a meta field '_book_author_id' storing the user ID of the author.
// Find all books by author ID 123.
$args = [
'post_type' => 'book',
'meta_query' => [
[
'key' => '_book_author_id',
'value' => 123,
'compare' => '=',
],
],
];
$books_by_author = new WP_Query($args);
35. How do you implement WordPress Coding Standards with PHPCS in a development workflow?
Answer:
Install PHP_CodeSniffer with WordPress standards and integrate into development workflow:
# Install via Composer
composer require --dev squizlabs/php_codesniffer
composer require --dev wp-coding-standards/wpcs
# Configure phpcs.xml in project root
WordPress Coding Standards
.
*/vendor/*
*/node_modules/*
# Pre-commit hook integration
#!/bin/sh
files=$(git diff --cached --name-only --diff-filter=ACM | grep '\.php$')
if [ -n "$files" ]; then
./vendor/bin/phpcs --standard=phpcs.xml $files
if [ $? -ne 0 ]; then
echo "Fix PHPCS errors before committing"
exit 1
fi
fi
36. Explain the concept of “hydration” in the context of WordPress queries and object caching. Why is it important for performance and memory usage?
Answer:
In WordPress, hydration refers to the process of taking raw data retrieved from the database (e.g., a row from the `wp_posts` table) and transforming it into a fully-formed PHP object, such as a `WP_Post` object. This involves populating the object’s properties, potentially fetching related data (like terms or meta, though often done lazily), and applying filters.
37. How would you design and implement a custom, secure API endpoint in WordPress that requires specific user capabilities and handles data validation and sanitization for input and output?
Answer:
Designing a custom, secure API endpoint involves several key steps:
add_action('rest_api_init', function () {
register_rest_route('myplugin/v1', '/submit-data', [
'methods' => WP_REST_Server::CREATABLE, // POST
'callback' => 'myplugin_handle_submit_data',
'args' => [
'title' => [
'required' => true,
'validate_callback' => function ($param) {
return !empty($param) && strlen($param) < 100;
},
'sanitize_callback' => 'sanitize_text_field',
],
'content' => [
'required' => true,
'sanitize_callback' => 'wp_kses_post',
],
],
'permission_callback' => function () {
return current_user_can('publish_posts'); // Example capability
},
]);
});
function myplugin_handle_submit_data(WP_REST_Request $request) {
$title = $request->get_param('title'); // Already validated & sanitized
$content = $request->get_param('content'); // Already validated & sanitized
$post_id = wp_insert_post([
'post_title' => $title,
'post_content' => $content,
'post_status' => 'pending',
'post_author' => get_current_user_id(),
]);
if (is_wp_error($post_id)) {
return new WP_Error('cant-create', __('Could not create post.', 'my-plugin'), ['status' => 500]);
}
$response_data = ['id' => $post_id, 'message' => __('Data submitted successfully.', 'my-plugin')];
return new WP_REST_Response($response_data, 201); // 201 Created
}
38. Discuss strategies for managing WordPress configuration across different environments (dev, staging, production) without committing sensitive data to version control.
Answer:
Managing WordPress configuration across environments while keeping sensitive data out of version control is crucial. Key strategies include:
// Example in wp-config.php
define('DB_NAME', getenv('DB_NAME'));
define('DB_USER', getenv('DB_USER'));
define('DB_PASSWORD', getenv('DB_PASSWORD'));
define('DB_HOST', getenv('DB_HOST') ?: 'localhost');
define('WP_DEBUG', filter_var(getenv('WP_DEBUG'), FILTER_VALIDATE_BOOLEAN));
define('WP_ENVIRONMENT_TYPE', getenv('WP_ENVIRONMENT_TYPE') ?: 'production');
39. Explain the purpose and use cases of the `pre_get_posts` action hook. Provide an example of how you might use it to modify the main query for a custom post type archive.
Answer:
The `pre_get_posts` action hook allows developers to modify the main query object (`WP_Query`) before the SQL query is executed. Example:
function modify_event_archive_query($query) {
if (!is_admin() && $query->is_main_query() && is_post_type_archive('event')) {
$today = date('Ymd');
$meta_query = $query->get('meta_query') ?: [];
$meta_query[] = [
'key' => '_event_date',
'value' => $today,
'compare' => '>=',
'type' => 'DATE',
];
$query->set('meta_query', $meta_query);
$query->set('meta_key', '_event_date');
$query->set('orderby', 'meta_value');
$query->set('order', 'ASC');
$query->set('posts_per_page', 5);
}
}
add_action('pre_get_posts', 'modify_event_archive_query');
40. Describe a scenario where you’d need to use the Transients API with a custom expiration hook. How would you implement this?
Answer:
Use the Transients API for temporary caching and create a custom expiration hook:
define('TRENDING_ARTICLES_TRANSIENT_KEY', 'my_trending_articles_cache');
define('REFRESH_TRENDING_ARTICLES_HOOK', 'my_refresh_trending_articles_action');
function get_trending_articles_data() {
$cached_articles = get_transient(TRENDING_ARTICLES_TRANSIENT_KEY);
if (false === $cached_articles) {
$articles = [
['id' => 101, 'title' => 'Article A'],
['id' => 102, 'title' => 'Article B'],
];
set_transient(TRENDING_ARTICLES_TRANSIENT_KEY, $articles, HOUR_IN_SECONDS);
return $articles;
}
return $cached_articles;
}
function clear_trending_articles_transient() {
delete_transient(TRENDING_ARTICLES_TRANSIENT_KEY);
}
add_action(REFRESH_TRENDING_ARTICLES_HOOK, 'clear_trending_articles_transient');
41. How do you implement a custom WooCommerce webhook for order updates and ensure its security?
Answer:
Create a WooCommerce webhook to send order updates to an external system:
add_action('woocommerce_webhook_payload', function ($payload, $resource, $resource_id, $id) {
if ($resource === 'order') {
$order = wc_get_order($resource_id);
$payload['custom_data'] = get_post_meta($resource_id, '_custom_field', true);
return $payload;
}
return $payload;
}, 10, 4);
add_action('woocommerce_webhook_delivery', function ($webhook) {
$signature = $_SERVER['HTTP_X_WC_WEBHOOK_SIGNATURE'];
$payload = file_get_contents('php://input');
$secret = 'your-secret-key';
$calculated_signature = base64_encode(hash_hmac('sha256', $payload, $secret, true));
if ($signature !== $calculated_signature) {
wp_die('Invalid signature');
}
});
42. How do you implement lazy-loaded custom queries for a high-performance archive page?
Answer:
Lazy-load a CPT archive using AJAX:
// Enqueue script
add_action('wp_enqueue_scripts', function () {
if (is_post_type_archive('portfolio')) {
wp_enqueue_script('lazy-load', get_theme_file_uri('/js/lazy-load.js'), ['jquery'], '1.0', true);
wp_localize_script('lazy-load', 'lazyLoad', [
'ajaxurl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('lazy_load_nonce'),
]);
}
});
// AJAX handler
add_action('wp_ajax_lazy_load_posts', 'lazy_load_posts');
add_action('wp_ajax_nopriv_lazy_load_posts', 'lazy_load_posts');
function lazy_load_posts() {
check_ajax_referer('lazy_load_nonce', 'security');
$page = isset($_POST['page']) ? intval($_POST['page']) : 1;
$args = [
'post_type' => 'portfolio',
'posts_per_page' => 10,
'paged' => $page,
];
$query = new WP_Query($args);
ob_start();
while ($query->have_posts()) : $query->the_post();
get_template_part('template-parts/content', 'portfolio');
endwhile;
wp_reset_postdata();
wp_send_json_success(ob_get_clean());
}
43. How do you implement a custom WordPress cron job with error handling and monitoring?
Answer:
Schedule a custom cron job with robust error handling:
add_action('init', function () {
if (!wp_next_scheduled('my_custom_cron')) {
wp_schedule_event(time(), 'daily', 'my_custom_cron');
}
});
add_action('my_custom_cron', 'run_custom_cron');
function run_custom_cron() {
try {
$response = wp_remote_get('https://api.example.com/data');
if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
throw new Exception('API request failed');
}
update_option('cron_data', wp_remote_retrieve_body($response));
} catch (Exception $e) {
error_log('Cron error: ' . $e->getMessage());
wp_mail('admin@example.com', 'Cron Failure', $e->getMessage());
}
}
register_deactivation_hook(__FILE__, function () {
wp_clear_scheduled_hook('my_custom_cron');
});
44. How would you optimize WordPress for high-performance REST API responses?
Answer:
Optimize REST API performance by caching responses:
function get_cached_posts(WP_REST_Request $request) {
$cache_key = 'rest_posts_' . md5(serialize($request->get_params()));
$posts = get_transient($cache_key);
if (false === $posts) {
$args = ['post_type' => 'post', 'posts_per_page' => 10];
$posts = (new WP_Query($args))->posts;
set_transient($cache_key, $posts, 3600);
}
return new WP_REST_Response($posts, 200);
}
45. How do you implement a custom WordPress settings API with dynamic fields for a plugin?
Answer:
Use the Settings API for dynamic fields:
add_action('admin_menu', function() {
add_options_page('My Plugin', 'My Plugin', 'manage_options', 'my-plugin', 'my_plugin_page');
});
function my_plugin_page() {
?>
function($input) {
$output = [];
foreach ($input['fields'] as $key => $value) {
$output['fields'][sanitize_key($key)] = sanitize_text_field($value);
}
return $output;
}
]);
add_settings_section('section1', 'Settings', null, 'my-plugin');
// Dynamic fields
$settings = get_option('my_plugin_settings', ['fields' => []]);
foreach (['field1', 'field2'] as $field) {
add_settings_field($field, __($field, 'my-plugin'), function() use ($field) {
$value = get_option('my_plugin_settings')['fields'][$field] ?? '';
echo '';
}, 'my-plugin', 'section1');
}
});
46. How do you implement a custom WooCommerce product type with unique pricing logic?
Answer:
Create a custom WooCommerce product type by extending `WC_Product` and implementing custom pricing logic:
// Register Product Type
add_filter('woocommerce_product_types', function($types) {
$types['subscription'] = __('Subscription', 'myplugin');
return $types;
});
add_filter('product_type_selector', function($types) {
$types['subscription'] = __('Subscription', 'myplugin');
return $types;
});
// Create Product Class
class WC_Product_Subscription extends WC_Product {
public function __construct($product = 0) {
parent::__construct($product);
$this->product_type = 'subscription';
}
public function get_price() {
$base_price = parent::get_price();
$duration = get_post_meta($this->get_id(), '_subscription_duration', true);
$multiplier = $duration ? intval($duration) : 1;
return $base_price * $multiplier;
}
}
add_filter('woocommerce_product_class', function($class, $product_type) {
if ($product_type === 'subscription') {
return 'WC_Product_Subscription';
}
return $class;
}, 10, 2);
// Add Custom Fields
add_action('woocommerce_product_options_general_product_data', function() {
woocommerce_wp_text_input([
'id' => '_subscription_duration',
'label' => __('Duration (months)', 'myplugin'),
'type' => 'number',
'custom_attributes' => ['min' => 1],
]);
});
add_action('woocommerce_process_product_meta_subscription', function($post_id) {
$duration = isset($_POST['_subscription_duration']) ? intval($_POST['_subscription_duration']) : '';
update_post_meta($post_id, '_subscription_duration', $duration);
});
47. How do you implement real-time performance monitoring for a WordPress site?
Answer:
Monitor WordPress performance in real-time using tools and custom logging:
// Example: Log slow queries
add_action('shutdown', function() {
global $wpdb;
$slow_queries = array_filter($wpdb->queries, function($query) {
return $query[1] > 0.1; // Log queries > 100ms
});
if ($slow_queries && defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) {
error_log('Slow Queries: ' . print_r($slow_queries, true));
}
});
48. How do you query and display complex user meta relationships in WordPress?
Answer:
Query complex user meta relationships using `WP_User_Query` or custom `$wpdb` queries:
// Example: WP_User_Query
$args = [
'role' => 'trainer',
'meta_query' => [
'relation' => 'AND',
[
'key' => 'certified',
'value' => '1',
'compare' => '=',
],
[
'key' => 'specialty',
'value' => 'yoga',
'compare' => '=',
],
],
];
$user_query = new WP_User_Query($args);
if ($user_query->get_results()) {
foreach ($user_query->get_results() as $user) {
echo '' . esc_html($user->display_name) . '';
}
} else {
echo 'No trainers found.';
}
// Example: Custom $wpdb Query
global $wpdb;
$users = $wpdb->get_results($wpdb->prepare(
"SELECT u.* FROM {$wpdb->users} u
INNER JOIN {$wpdb->usermeta} m1 ON u.ID = m1.user_id
INNER JOIN {$wpdb->usermeta} m2 ON u.ID = m2.user_id
WHERE m1.meta_key = %s AND m1.meta_value = %s
AND m2.meta_key = %s AND m2.meta_value = %s
AND u.ID IN (SELECT user_id FROM {$wpdb->usermeta} WHERE meta_key = 'wp_capabilities' AND meta_value LIKE %s)",
'certified', '1', 'specialty', 'yoga', '%trainer%'
));
foreach ($users as $user) {
echo '' . esc_html($user->display_name) . '';
}
49. How do you integrate WordPress with a GraphQL API for a headless setup?
Answer:
Use WPGraphQL to expose WordPress data for a headless frontend:
// Extend Schema
add_action('graphql_register_types', function() {
register_graphql_field('Post', 'customField', [
'type' => 'String',
'description' => __('Custom meta field', 'myplugin'),
'resolve' => function($post) {
return get_post_meta($post->ID, 'custom_field', true);
},
]);
});
// Query Example
query {
posts(first: 5) {
nodes {
title
content
customField
}
}
}
50. How do you implement server-side rendering for a headless WordPress site to improve SEO?
Answer:
Use Next.js with WPGraphQL for server-side rendering:
// Next.js Configuration import { ApolloClient, InMemoryCache, gql } from '@apollo/client'; export async function getServerSideProps(context) { const client = new ApolloClient({ uri: 'https://example.com/graphql', cache: new InMemoryCache(), }); const { data } = await client.query({ query: gql` query GetPost($id: ID!) { post(id: $id, idType: URI) { title content } } `, variables: { id: context.params.slug }, }); return { props: { post: data.post }, }; } export default function Post({ post }) { return (
{post.title}
); }