Session 6.8 – WordPress Development

Module 6: Advanced Web Technologies | Duration: 1 hr

Learning Objectives

By the end of this session, students will be able to:

  • Understand WordPress architecture and file structure
  • Create custom WordPress themes from scratch
  • Develop WordPress plugins with custom functionality
  • Work with WordPress hooks (actions and filters)
  • Use the WordPress database ($wpdb) class
  • Apply WordPress development best practices

Introduction to WordPress Development

WordPress powers over 43% of all websites on the internet. Understanding WordPress development opens opportunities for creating custom themes, plugins, and complete web solutions.

WordPress Overview
  • Core: WordPress core system (don't modify)
  • Themes: Control website appearance and layout
  • Plugins: Extend functionality
  • Content: Posts, pages, custom post types
  • Database: MySQL database for content storage
WordPress Directory Structure
wordpress/ ├── wp-admin/ # Admin panel files ├── wp-content/ # Customizable content │ ├── themes/ # Your themes here │ ├── plugins/ # Your plugins here │ └── uploads/ # Media files ├── wp-includes/ # Core WordPress files ├── wp-config.php # Configuration file └── index.php # Entry point

WordPress Theme Development

Theme Structure
mytheme/ ├── style.css # Required: Theme stylesheet & metadata ├── index.php # Required: Main template ├── functions.php # Theme functions and features ├── header.php # Header template ├── footer.php # Footer template ├── sidebar.php # Sidebar template ├── single.php # Single post template ├── page.php # Page template ├── archive.php # Archive template ├── 404.php # 404 error template ├── searchform.php # Search form template ├── screenshot.png # Theme thumbnail └── assets/ ├── css/ ├── js/ └── images/
style.css (Theme Header)
/* Theme Name: My Custom Theme Theme URI: https://example.com/my-theme Author: Your Name Author URI: https://example.com Description: A custom WordPress theme Version: 1.0.0 License: GNU General Public License v2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Text Domain: mytheme */ body { font-family: Arial, sans-serif; margin: 0; padding: 0; } .container { max-width: 1200px; margin: 0 auto; padding: 0 15px; }
functions.php
<?php // Theme setup function mytheme_setup() { // Add theme support add_theme_support('title-tag'); add_theme_support('post-thumbnails'); add_theme_support('html5', array('search-form', 'comment-form', 'comment-list')); add_theme_support('custom-logo'); // Register navigation menus register_nav_menus(array( 'primary' => __('Primary Menu', 'mytheme'), 'footer' => __('Footer Menu', 'mytheme') )); // Add image sizes add_image_size('mytheme-featured', 800, 400, true); add_image_size('mytheme-thumbnail', 300, 200, true); } add_action('after_setup_theme', 'mytheme_setup'); // Enqueue scripts and styles function mytheme_scripts() { // Enqueue stylesheet wp_enqueue_style('mytheme-style', get_stylesheet_uri(), array(), '1.0.0'); // Enqueue custom CSS wp_enqueue_style('mytheme-custom', get_template_directory_uri() . '/assets/css/custom.css', array(), '1.0.0'); // Enqueue JavaScript wp_enqueue_script('mytheme-script', get_template_directory_uri() . '/assets/js/custom.js', array('jquery'), '1.0.0', true); // Enqueue comment reply script if (is_singular() && comments_open() && get_option('thread_comments')) { wp_enqueue_script('comment-reply'); } } add_action('wp_enqueue_scripts', 'mytheme_scripts'); // Register widget areas function mytheme_widgets_init() { register_sidebar(array( 'name' => __('Sidebar', 'mytheme'), 'id' => 'sidebar-1', 'description' => __('Add widgets here.', 'mytheme'), 'before_widget' => '<section class="widget">', 'after_widget' => '</section>', 'before_title' => '<h2 class="widget-title">', 'after_title' => '</h2>', )); register_sidebar(array( 'name' => __('Footer', 'mytheme'), 'id' => 'footer-1', 'description' => __('Footer widget area.', 'mytheme'), 'before_widget' => '<div class="footer-widget">', 'after_widget' => '</div>', 'before_title' => '<h3>', 'after_title' => '</h3>', )); } add_action('widgets_init', 'mytheme_widgets_init'); ?>
header.php
<!DOCTYPE html> <html <?php language_attributes(); ?>> <head> <meta charset="<?php bloginfo('charset'); ?>"> <meta name="viewport" content="width=device-width, initial-scale=1"> <?php wp_head(); ?> </head> <body <?php body_class(); ?>> <?php wp_body_open(); ?> <header class="site-header"> <div class="container"> <div class="site-branding"> <?php if (has_custom_logo()) : ?> <?php the_custom_logo(); ?> <?php else : ?> <h1 class="site-title"> <a href="<?php echo esc_url(home_url('/')); ?>"> <?php bloginfo('name'); ?> </a> </h1> <p class="site-description"><?php bloginfo('description'); ?></p> <?php endif; ?> </div> <nav class="main-navigation"> <?php wp_nav_menu(array( 'theme_location' => 'primary', 'menu_class' => 'primary-menu', 'fallback_cb' => false, )); ?> </nav> </div> </header>
index.php (The Loop)
<?php get_header(); ?> <main class="site-main"> <div class="container"> <?php if (have_posts()) : ?> <?php while (have_posts()) : the_post(); ?> <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>> <header class="entry-header"> <h2 class="entry-title"> <a href="<?php the_permalink(); ?>"> <?php the_title(); ?> </a> </h2> <div class="entry-meta"> <span class="posted-on"> <?php echo get_the_date(); ?> </span> <span class="byline"> by <?php the_author(); ?> </span> </div> </header> <?php if (has_post_thumbnail()) : ?> <div class="post-thumbnail"> <?php the_post_thumbnail('mytheme-featured'); ?> </div> <?php endif; ?> <div class="entry-content"> <?php the_excerpt(); ?> <a href="<?php the_permalink(); ?>" class="read-more">Read More</a> </div> <footer class="entry-footer"> <?php $categories = get_the_category(); if ($categories) { echo '<span class="cat-links">'; echo 'Posted in '; echo get_the_category_list(', '); echo '</span>'; } $tags = get_the_tags(); if ($tags) { echo '<span class="tags-links">'; echo 'Tagged '; echo get_the_tag_list('', ', '); echo '</span>'; } ?> </footer> </article> <?php endwhile; ?> <!-- Pagination --> <div class="pagination"> <?php the_posts_pagination(array( 'mid_size' => 2, 'prev_text' => __('« Previous', 'mytheme'), 'next_text' => __('Next »', 'mytheme'), )); ?> </div> <?php else : ?> <p><?php _e('Sorry, no posts found.', 'mytheme'); ?></p> <?php endif; ?> </div> </main> <?php get_sidebar(); ?> <?php get_footer(); ?>

WordPress Plugin Development

Basic Plugin Structure
<?php /* Plugin Name: My Custom Plugin Plugin URI: https://example.com/my-plugin Description: A custom WordPress plugin Version: 1.0.0 Author: Your Name Author URI: https://example.com License: GPL v2 or later Text Domain: my-custom-plugin */ // Prevent direct access if (!defined('ABSPATH')) { exit; } // Define plugin constants define('MCP_VERSION', '1.0.0'); define('MCP_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('MCP_PLUGIN_URL', plugin_dir_url(__FILE__)); // Plugin activation function mcp_activate() { // Create custom database table global $wpdb; $table_name = $wpdb->prefix . 'custom_data'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE $table_name ( id mediumint(9) NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL, data text NOT NULL, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); // Set default options add_option('mcp_option_name', 'default_value'); } register_activation_hook(__FILE__, 'mcp_activate'); // Plugin deactivation function mcp_deactivate() { // Cleanup tasks flush_rewrite_rules(); } register_deactivation_hook(__FILE__, 'mcp_deactivate'); // Add admin menu function mcp_admin_menu() { add_menu_page( 'My Plugin Settings', // Page title 'My Plugin', // Menu title 'manage_options', // Capability 'my-custom-plugin', // Menu slug 'mcp_settings_page', // Callback function 'dashicons-admin-generic', // Icon 30 // Position ); } add_action('admin_menu', 'mcp_admin_menu'); // Settings page callback function mcp_settings_page() { ?> <div class="wrap"> <h1>My Plugin Settings</h1> <form method="post" action="options.php"> <?php settings_fields('mcp_settings'); do_settings_sections('my-custom-plugin'); submit_button(); ?> </form> </div> <?php } // Register settings function mcp_register_settings() { register_setting('mcp_settings', 'mcp_option_name'); add_settings_section( 'mcp_main_section', 'Main Settings', 'mcp_section_callback', 'my-custom-plugin' ); add_settings_field( 'mcp_option_field', 'Option Name', 'mcp_option_field_callback', 'my-custom-plugin', 'mcp_main_section' ); } add_action('admin_init', 'mcp_register_settings'); function mcp_section_callback() { echo 'Configure your plugin settings below:'; } function mcp_option_field_callback() { $value = get_option('mcp_option_name'); echo '<input type="text" name="mcp_option_name" value="' . esc_attr($value) . '" />'; } ?>
Shortcode Example
<?php // Create a shortcode function mcp_contact_form_shortcode($atts) { // Extract attributes $atts = shortcode_atts(array( 'title' => 'Contact Us', 'button_text' => 'Submit' ), $atts); ob_start(); ?> <div class="mcp-contact-form"> <h3><?php echo esc_html($atts['title']); ?></h3> <form method="post" action="<?php echo admin_url('admin-ajax.php'); ?>"> <input type="hidden" name="action" value="mcp_submit_contact"> <?php wp_nonce_field('mcp_contact_nonce', 'mcp_nonce'); ?> <div class="form-group"> <label>Name:</label> <input type="text" name="name" required> </div> <div class="form-group"> <label>Email:</label> <input type="email" name="email" required> </div> <div class="form-group"> <label>Message:</label> <textarea name="message" required></textarea> </div> <button type="submit"><?php echo esc_html($atts['button_text']); ?></button> </form> </div> <?php return ob_get_clean(); } add_shortcode('contact_form', 'mcp_contact_form_shortcode'); // Usage: [contact_form title="Get in Touch" button_text="Send Message"] // Handle form submission via AJAX function mcp_submit_contact() { // Verify nonce if (!isset($_POST['mcp_nonce']) || !wp_verify_nonce($_POST['mcp_nonce'], 'mcp_contact_nonce')) { wp_send_json_error('Invalid nonce'); } // Sanitize input $name = sanitize_text_field($_POST['name']); $email = sanitize_email($_POST['email']); $message = sanitize_textarea_field($_POST['message']); // Validate if (empty($name) || empty($email) || empty($message)) { wp_send_json_error('All fields are required'); } // Send email $to = get_option('admin_email'); $subject = 'Contact Form Submission from ' . $name; $body = "Name: $name\nEmail: $email\n\nMessage:\n$message"; $headers = array('Content-Type: text/plain; charset=UTF-8'); if (wp_mail($to, $subject, $body, $headers)) { wp_send_json_success('Message sent successfully'); } else { wp_send_json_error('Failed to send message'); } } add_action('wp_ajax_mcp_submit_contact', 'mcp_submit_contact'); add_action('wp_ajax_nopriv_mcp_submit_contact', 'mcp_submit_contact'); ?>

WordPress Hooks (Actions & Filters)

Actions

Execute code at specific points in WordPress execution

// Add action add_action('init', 'my_function'); function my_function() { // Your code here } // Do action do_action('custom_hook');
Filters

Modify data before it's displayed or saved

// Add filter add_filter('the_content', 'my_filter'); function my_filter($content) { return $content . 'Added text'; } // Apply filter apply_filters('custom_filter', $data);
Common Hooks Examples
<?php // Modify post content function add_copyright_notice($content) { if (is_single()) { $content .= '<p>© ' . date('Y') . ' ' . get_bloginfo('name') . '</p>'; } return $content; } add_filter('the_content', 'add_copyright_notice'); // Add custom body class function add_custom_body_class($classes) { if (is_page('about')) { $classes[] = 'about-page'; } return $classes; } add_filter('body_class', 'add_custom_body_class'); // Modify excerpt length function custom_excerpt_length($length) { return 30; // 30 words } add_filter('excerpt_length', 'custom_excerpt_length'); // Add content before post function add_content_before_post($content) { if (is_single()) { $before = '<div class="before-post">Read time: 5 minutes</div>'; $content = $before . $content; } return $content; } add_filter('the_content', 'add_content_before_post'); // Disable WordPress auto-updates add_filter('auto_update_core', '__return_false'); // Custom login logo function custom_login_logo() { ?> <style type="text/css"> #login h1 a { background-image: url(<?php echo get_template_directory_uri(); ?>/images/logo.png); background-size: contain; width: 320px; } </style> <?php } add_action('login_enqueue_scripts', 'custom_login_logo'); ?>

WordPress Database ($wpdb)

Using $wpdb Class
<?php global $wpdb; // INSERT $wpdb->insert( $wpdb->prefix . 'custom_table', array( 'name' => 'John Doe', 'email' => 'john@example.com', 'status' => 'active' ), array('%s', '%s', '%s') ); $insert_id = $wpdb->insert_id; // SELECT (get_results) $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}custom_table WHERE status = 'active'"); foreach ($results as $row) { echo $row->name; } // SELECT (get_row) $user = $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}custom_table WHERE id = %d", $user_id )); // SELECT (get_var) - single value $count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}custom_table"); // SELECT (get_col) - single column $emails = $wpdb->get_col("SELECT email FROM {$wpdb->prefix}custom_table"); // UPDATE $wpdb->update( $wpdb->prefix . 'custom_table', array('status' => 'inactive'), // Data to update array('id' => 1), // WHERE clause array('%s'), // Data format array('%d') // WHERE format ); $updated_rows = $wpdb->rows_affected; // DELETE $wpdb->delete( $wpdb->prefix . 'custom_table', array('id' => 1), array('%d') ); // Prepared statements (security) $safe_query = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}custom_table WHERE name = %s AND status = %s", $name, $status ); $results = $wpdb->get_results($safe_query); // Error handling if ($wpdb->last_error) { error_log('Database error: ' . $wpdb->last_error); } ?>
Custom Post Types
<?php // Register custom post type function register_portfolio_post_type() { $labels = array( 'name' => 'Portfolio', 'singular_name' => 'Portfolio Item', 'add_new' => 'Add New', 'add_new_item' => 'Add New Portfolio Item', 'edit_item' => 'Edit Portfolio Item', 'new_item' => 'New Portfolio Item', 'view_item' => 'View Portfolio Item', 'search_items' => 'Search Portfolio', 'not_found' => 'No portfolio items found', 'not_found_in_trash' => 'No portfolio items found in trash' ); $args = array( 'labels' => $labels, 'public' => true, 'has_archive' => true, 'publicly_queryable' => true, 'query_var' => true, 'rewrite' => array('slug' => 'portfolio'), 'capability_type' => 'post', 'hierarchical' => false, 'supports' => array('title', 'editor', 'thumbnail', 'excerpt'), 'menu_icon' => 'dashicons-portfolio', 'show_in_rest' => true // Gutenberg support ); register_post_type('portfolio', $args); } add_action('init', 'register_portfolio_post_type'); // Register custom taxonomy function register_portfolio_taxonomy() { $labels = array( 'name' => 'Portfolio Categories', 'singular_name' => 'Portfolio Category', 'search_items' => 'Search Categories', 'all_items' => 'All Categories', 'parent_item' => 'Parent Category', 'parent_item_colon' => 'Parent Category:', 'edit_item' => 'Edit Category', 'update_item' => 'Update Category', 'add_new_item' => 'Add New Category', 'new_item_name' => 'New Category Name', 'menu_name' => 'Categories' ); $args = array( 'labels' => $labels, 'hierarchical' => true, // Like categories 'public' => true, 'show_ui' => true, 'show_admin_column' => true, 'query_var' => true, 'rewrite' => array('slug' => 'portfolio-category'), 'show_in_rest' => true ); register_taxonomy('portfolio_category', array('portfolio'), $args); } add_action('init', 'register_portfolio_taxonomy'); ?>

WordPress Development Best Practices

Do's
  • Use WordPress coding standards
  • Sanitize all input data
  • Escape all output
  • Use nonces for security
  • Enqueue scripts and styles properly
  • Use translation functions
  • Follow the template hierarchy
  • Use child themes for customization
  • Implement proper error handling
  • Document your code
Don'ts
  • Don't modify WordPress core files
  • Don't hardcode URLs or paths
  • Don't use deprecated functions
  • Don't ignore security best practices
  • Don't use direct database queries without $wpdb
  • Don't forget to validate and sanitize
  • Don't create security vulnerabilities
  • Don't use too many plugins
  • Don't ignore performance optimization
  • Don't forget about mobile responsiveness
Security Best Practices
<?php // 1. Sanitize input $name = sanitize_text_field($_POST['name']); $email = sanitize_email($_POST['email']); $content = wp_kses_post($_POST['content']); // 2. Escape output echo esc_html($user_input); echo esc_url($url); echo esc_attr($attribute); // 3. Use nonces wp_nonce_field('my_action_name', 'my_nonce_field'); if (!wp_verify_nonce($_POST['my_nonce_field'], 'my_action_name')) { die('Security check failed'); } // 4. Check capabilities if (!current_user_can('manage_options')) { wp_die('You do not have sufficient permissions'); } // 5. Prepared statements $wpdb->prepare("SELECT * FROM {$wpdb->prefix}table WHERE id = %d", $id); ?>

Session Summary

Key Points
  • WordPress themes control appearance; plugins extend functionality
  • style.css and functions.php are essential theme files
  • The Loop is the core of WordPress content display
  • Template hierarchy determines which template file loads
  • Hooks (actions and filters) enable customization
  • $wpdb class provides safe database interactions
  • Always sanitize input and escape output
  • Use nonces for security validation
  • Follow WordPress coding standards
  • Custom post types extend content capabilities
Course Complete!

Congratulations on completing SWE4001 - Internet and Web Technologies! You've learned the fundamentals of web development, from HTML and CSS to advanced PHP, databases, and content management systems. Continue practicing and building projects to master these skills.