Skip to main content

Course Progress

Loading...

🛠️ Custom Functions & Helpers

Build reusable utilities and custom functionality

Master creating efficient, maintainable theme functions

Learning Objectives

  • Create custom helper functions for common tasks
  • Build utility functions for theme customization
  • Implement custom template tags
  • Create AJAX handlers for dynamic functionality
  • Build custom shortcodes
  • Organize functions for maintainability
  • Implement security best practices
  • Create reusable code snippets

Why Custom Functions?

Custom functions extend WordPress functionality, reduce code repetition, and make your theme more maintainable. They encapsulate complex logic into reusable components.

💡
Key Principle
Follow the DRY principle (Don't Repeat Yourself). If you use code more than twice, create a function for it.

📝 Content Helpers

Functions for excerpts, read more, content formatting

🖼️ Image Utilities

Custom image sizes, placeholders, responsive images

🔍 Query Helpers

Custom queries, related posts, popular posts

👤 User Functions

User avatars, author info, login checks

📊 Analytics

View counts, reading time, post statistics

🎨 Theme Options

Custom settings, theme mods, options

Content Helper Functions

Custom Excerpt Functions

<?php
/**
 * Custom excerpt length
 */
function mytheme_excerpt_length( $length ) {
    if ( is_home() ) {
        return 20;
    } elseif ( is_archive() ) {
        return 30;
    }
    return $length;
}
add_filter( 'excerpt_length', 'mytheme_excerpt_length', 999 );

/**
 * Custom excerpt more string
 */
function mytheme_excerpt_more( $more ) {
    return '...';
}
add_filter( 'excerpt_more', 'mytheme_excerpt_more' );

/**
 * Get custom excerpt with specific word count
 */
function mytheme_get_excerpt( $limit = 20, $source = null ) {
    $excerpt = $source ? $source : get_the_excerpt();
    $excerpt = preg_replace( ' (\[.*?\])', '', $excerpt );
    $excerpt = strip_shortcodes( $excerpt );
    $excerpt = strip_tags( $excerpt );
    $excerpt = substr( $excerpt, 0, $limit );
    $excerpt = substr( $excerpt, 0, strripos( $excerpt, " " ) );
    $excerpt = trim( preg_replace( '/\s+/', ' ', $excerpt ) );
    $excerpt = $excerpt . '...';
    return $excerpt;
}

/**
 * Estimated reading time
 */
function mytheme_reading_time() {
    $content = get_post_field( 'post_content', get_the_ID() );
    $word_count = str_word_count( strip_tags( $content ) );
    $reading_time = ceil( $word_count / 200 ); // Average reading speed
    
    if ( $reading_time == 1 ) {
        $timer = " minute";
    } else {
        $timer = " minutes";
    }
    
    return $reading_time . $timer;
}

/**
 * Display reading time
 */
function mytheme_the_reading_time() {
    echo '<span class="reading-time">';
    echo '<svg class="icon">...</svg> ';
    echo mytheme_reading_time() . ' read';
    echo '</span>';
}

Content Formatting Helpers

<?php
/**
 * Add custom classes to body tag
 */
function mytheme_body_classes( $classes ) {
    // Add page slug
    if ( is_page() ) {
        global $post;
        $classes[] = 'page-' . $post->post_name;
    }
    
    // Add user state
    if ( is_user_logged_in() ) {
        $classes[] = 'logged-in-user';
    } else {
        $classes[] = 'logged-out-user';
    }
    
    // Add sidebar status
    if ( is_active_sidebar( 'sidebar-1' ) ) {
        $classes[] = 'has-sidebar';
    } else {
        $classes[] = 'no-sidebar';
    }
    
    return $classes;
}
add_filter( 'body_class', 'mytheme_body_classes' );

/**
 * Custom "Read More" link
 */
function mytheme_read_more_link( $text = 'Read More' ) {
    return '<a class="read-more-link" href="' . get_permalink() . '">' . 
           $text . ' <span class="screen-reader-text">about ' . 
           get_the_title() . '</span></a>';
}

/**
 * Truncate text with preservation of words
 */
function mytheme_truncate( $text, $length = 100, $ending = '...' ) {
    if ( strlen( $text ) <= $length ) {
        return $text;
    }
    
    $text = substr( $text, 0, $length );
    $text = substr( $text, 0, strrpos( $text, ' ' ) );
    $text = $text . $ending;
    
    return $text;
}

Image & Media Helpers

Image Utility Functions

<?php
/**
 * Get featured image URL
 */
function mytheme_get_featured_image_url( $size = 'full', $post_id = null ) {
    if ( ! $post_id ) {
        $post_id = get_the_ID();
    }
    
    if ( has_post_thumbnail( $post_id ) ) {
        $image = wp_get_attachment_image_src( get_post_thumbnail_id( $post_id ), $size );
        return $image[0];
    }
    
    // Return placeholder
    return get_template_directory_uri() . '/assets/images/placeholder.jpg';
}

/**
 * Display responsive image
 */
function mytheme_responsive_image( $attachment_id, $sizes = array() ) {
    $default_sizes = array(
        'small'  => 480,
        'medium' => 768,
        'large'  => 1200,
    );
    
    $sizes = wp_parse_args( $sizes, $default_sizes );
    $srcset = array();
    
    foreach ( $sizes as $name => $width ) {
        $image = wp_get_attachment_image_src( $attachment_id, array( $width, 9999 ) );
        if ( $image ) {
            $srcset[] = $image[0] . ' ' . $width . 'w';
        }
    }
    
    $src = wp_get_attachment_image_url( $attachment_id, 'full' );
    $alt = get_post_meta( $attachment_id, '_wp_attachment_image_alt', true );
    
    echo '<img src="' . esc_url( $src ) . '" ';
    echo 'srcset="' . esc_attr( implode( ', ', $srcset ) ) . '" ';
    echo 'sizes="(max-width: 480px) 100vw, (max-width: 768px) 50vw, 33vw" ';
    echo 'alt="' . esc_attr( $alt ) . '">';
}

/**
 * Get first image from post content
 */
function mytheme_get_first_image() {
    global $post;
    $first_img = '';
    
    preg_match_all( '/<img.+src=[\'"]([^\'"]+)[\'"].*>/i', $post->post_content, $matches );
    
    if ( isset( $matches[1][0] ) ) {
        $first_img = $matches[1][0];
    }
    
    if ( empty( $first_img ) ) {
        $first_img = get_template_directory_uri() . '/assets/images/default.jpg';
    }
    
    return $first_img;
}

/**
 * Lazy load images
 */
function mytheme_lazy_load_images( $content ) {
    if ( is_feed() || is_preview() ) {
        return $content;
    }
    
    $content = preg_replace( '/(<img.*?)src=/i', '$1loading="lazy" src=', $content );
    return $content;
}
add_filter( 'the_content', 'mytheme_lazy_load_images' );

Navigation & Query Helpers

Custom Query Functions

<?php
/**
 * Get related posts
 */
function mytheme_get_related_posts( $post_id = null, $number = 3 ) {
    if ( ! $post_id ) {
        $post_id = get_the_ID();
    }
    
    $categories = wp_get_post_categories( $post_id );
    
    $args = array(
        'category__in'        => $categories,
        'post__not_in'        => array( $post_id ),
        'posts_per_page'      => $number,
        'ignore_sticky_posts' => 1,
        'orderby'             => 'rand',
    );
    
    return new WP_Query( $args );
}

/**
 * Get popular posts by view count
 */
function mytheme_get_popular_posts( $number = 5 ) {
    $args = array(
        'posts_per_page'      => $number,
        'meta_key'            => 'post_views_count',
        'orderby'             => 'meta_value_num',
        'order'               => 'DESC',
        'ignore_sticky_posts' => 1,
    );
    
    return new WP_Query( $args );
}

/**
 * Track post views
 */
function mytheme_track_post_views( $post_id ) {
    if ( ! is_single() ) return;
    
    if ( empty( $post_id ) ) {
        global $post;
        $post_id = $post->ID;
    }
    
    $count_key = 'post_views_count';
    $count = get_post_meta( $post_id, $count_key, true );
    
    if ( $count == '' ) {
        $count = 0;
        delete_post_meta( $post_id, $count_key );
        add_post_meta( $post_id, $count_key, '0' );
    } else {
        $count++;
        update_post_meta( $post_id, $count_key, $count );
    }
}
add_action( 'wp_head', 'mytheme_track_post_views' );

/**
 * Custom breadcrumbs
 */
function mytheme_breadcrumbs() {
    $separator = ' > ';
    $home = 'Home';
    
    echo '<nav class="breadcrumbs">';
    echo '<a href="' . home_url() . '">' . $home . '</a>' . $separator;
    
    if ( is_category() || is_single() ) {
        the_category( ', ' );
        
        if ( is_single() ) {
            echo $separator;
            the_title();
        }
    } elseif ( is_page() ) {
        the_title();
    } elseif ( is_search() ) {
        echo 'Search Results for: ' . get_search_query();
    }
    
    echo '</nav>';
}

AJAX Handler Functions

Load More Posts AJAX

<?php
/**
 * AJAX Load More Posts Handler
 */
function mytheme_load_more_posts() {
    // Verify nonce
    if ( ! wp_verify_nonce( $_POST['nonce'], 'mytheme_load_more_nonce' ) ) {
        wp_die( 'Security check failed' );
    }
    
    $page = isset( $_POST['page'] ) ? intval( $_POST['page'] ) : 1;
    $posts_per_page = isset( $_POST['posts_per_page'] ) ? intval( $_POST['posts_per_page'] ) : 6;
    
    $args = array(
        'post_type'      => 'post',
        'posts_per_page' => $posts_per_page,
        'paged'          => $page,
        'post_status'    => 'publish',
    );
    
    $query = new WP_Query( $args );
    
    if ( $query->have_posts() ) {
        ob_start();
        
        while ( $query->have_posts() ) {
            $query->the_post();
            get_template_part( 'template-parts/content', 'card' );
        }
        
        $content = ob_get_clean();
        
        wp_send_json_success( array(
            'content' => $content,
            'max_pages' => $query->max_num_pages,
            'found_posts' => $query->found_posts,
        ) );
    } else {
        wp_send_json_error( 'No more posts found' );
    }
    
    wp_die();
}
add_action( 'wp_ajax_load_more_posts', 'mytheme_load_more_posts' );
add_action( 'wp_ajax_nopriv_load_more_posts', 'mytheme_load_more_posts' );

/**
 * AJAX Search
 */
function mytheme_ajax_search() {
    check_ajax_referer( 'mytheme_ajax_search_nonce', 'nonce' );
    
    $search_term = sanitize_text_field( $_POST['search'] );
    
    $args = array(
        's'              => $search_term,
        'post_type'      => 'post',
        'posts_per_page' => 5,
    );
    
    $query = new WP_Query( $args );
    
    if ( $query->have_posts() ) {
        $results = array();
        
        while ( $query->have_posts() ) {
            $query->the_post();
            $results[] = array(
                'title' => get_the_title(),
                'url'   => get_permalink(),
                'date'  => get_the_date(),
            );
        }
        
        wp_send_json_success( $results );
    } else {
        wp_send_json_error( 'No results found' );
    }
    
    wp_die();
}
add_action( 'wp_ajax_ajax_search', 'mytheme_ajax_search' );
add_action( 'wp_ajax_nopriv_ajax_search', 'mytheme_ajax_search' );

Custom Shortcodes

Useful Shortcode Examples

<?php
/**
 * Button shortcode
 * Usage: [button url="http://example.com" text="Click Me" color="primary"]
 */
function mytheme_button_shortcode( $atts ) {
    $atts = shortcode_atts( array(
        'url'    => '#',
        'text'   => 'Button',
        'color'  => 'primary',
        'target' => '_self',
    ), $atts );
    
    return sprintf(
        '<a href="%s" class="btn btn-%s" target="%s">%s</a>',
        esc_url( $atts['url'] ),
        esc_attr( $atts['color'] ),
        esc_attr( $atts['target'] ),
        esc_html( $atts['text'] )
    );
}
add_shortcode( 'button', 'mytheme_button_shortcode' );

/**
 * Current year shortcode
 * Usage: [year]
 */
function mytheme_year_shortcode() {
    return date( 'Y' );
}
add_shortcode( 'year', 'mytheme_year_shortcode' );

/**
 * Recent posts shortcode
 * Usage: [recent_posts number="3" category="news"]
 */
function mytheme_recent_posts_shortcode( $atts ) {
    $atts = shortcode_atts( array(
        'number'   => 5,
        'category' => '',
    ), $atts );
    
    $args = array(
        'posts_per_page' => intval( $atts['number'] ),
        'category_name'  => sanitize_text_field( $atts['category'] ),
    );
    
    $query = new WP_Query( $args );
    
    if ( ! $query->have_posts() ) {
        return 'No posts found';
    }
    
    $output = '<ul class="shortcode-recent-posts">';
    
    while ( $query->have_posts() ) {
        $query->the_post();
        $output .= '<li><a href="' . get_permalink() . '">' . get_the_title() . '</a></li>';
    }
    
    $output .= '</ul>';
    
    wp_reset_postdata();
    
    return $output;
}
add_shortcode( 'recent_posts', 'mytheme_recent_posts_shortcode' );

General Utility Functions

Useful Helper Functions

<?php
/**
 * Check if current page is a blog page
 */
function mytheme_is_blog() {
    return ( is_archive() || is_author() || is_category() || is_home() || is_single() || is_tag() ) && 'post' == get_post_type();
}

/**
 * Get current page URL
 */
function mytheme_current_url() {
    global $wp;
    return home_url( add_query_arg( array(), $wp->request ) );
}

/**
 * Format phone number for tel: links
 */
function mytheme_format_phone( $phone ) {
    return preg_replace( '/[^0-9+]/', '', $phone );
}

/**
 * Get social share links
 */
function mytheme_social_share_links() {
    $url = urlencode( get_permalink() );
    $title = urlencode( get_the_title() );
    
    $links = array(
        'facebook'  => 'https://www.facebook.com/sharer/sharer.php?u=' . $url,
        'twitter'   => 'https://twitter.com/intent/tweet?url=' . $url . '&text=' . $title,
        'linkedin'  => 'https://www.linkedin.com/shareArticle?mini=true&url=' . $url . '&title=' . $title,
        'pinterest' => 'https://pinterest.com/pin/create/button/?url=' . $url . '&description=' . $title,
    );
    
    return $links;
}

/**
 * Get attachment ID from URL
 */
function mytheme_get_attachment_id_from_url( $url ) {
    $attachment_id = 0;
    $dir = wp_upload_dir();
    
    if ( false !== strpos( $url, $dir['baseurl'] . '/' ) ) {
        $file = basename( $url );
        
        $query_args = array(
            'post_type'   => 'attachment',
            'post_status' => 'inherit',
            'fields'      => 'ids',
            'meta_query'  => array(
                array(
                    'value'   => $file,
                    'compare' => 'LIKE',
                    'key'     => '_wp_attachment_metadata',
                ),
            )
        );
        
        $query = new WP_Query( $query_args );
        
        if ( $query->have_posts() ) {
            foreach ( $query->posts as $post_id ) {
                $meta = wp_get_attachment_metadata( $post_id );
                $original_file = basename( $meta['file'] );
                $cropped_image_files = wp_list_pluck( $meta['sizes'], 'file' );
                
                if ( $original_file === $file || in_array( $file, $cropped_image_files ) ) {
                    $attachment_id = $post_id;
                    break;
                }
            }
        }
    }
    
    return $attachment_id;
}

/**
 * Human-readable time difference
 */
function mytheme_time_ago() {
    return human_time_diff( get_the_time( 'U' ), current_time( 'timestamp' ) ) . ' ago';
}

Organizing Custom Functions

Keep your functions organized by splitting them into separate files:

  • theme-folder/
    • functions.php
    • inc/
      • setup.php (theme setup functions)
      • assets.php (styles and scripts)
      • widgets.php (widget areas)
      • navigation.php (menu functions)
      • template-functions.php (template helpers)
      • customizer.php (customizer settings)
      • ajax-handlers.php (AJAX functions)
      • shortcodes.php (shortcode definitions)
      • helpers.php (utility functions)

Including Files in functions.php

<?php
/**
 * Theme functions and definitions
 */

// Define constants
define( 'MYTHEME_VERSION', '1.0.0' );
define( 'MYTHEME_DIR', get_template_directory() );
define( 'MYTHEME_URI', get_template_directory_uri() );

// Include function files
$mytheme_includes = array(
    '/inc/setup.php',           // Theme setup
    '/inc/assets.php',          // Scripts and styles
    '/inc/widgets.php',         // Widget areas
    '/inc/navigation.php',      // Custom navigation
    '/inc/template-functions.php', // Template helpers
    '/inc/customizer.php',      // Customizer additions
    '/inc/ajax-handlers.php',   // AJAX functions
    '/inc/shortcodes.php',      // Shortcodes
    '/inc/helpers.php',         // Helper functions
);

foreach ( $mytheme_includes as $file ) {
    $filepath = MYTHEME_DIR . $file;
    if ( file_exists( $filepath ) ) {
        require_once $filepath;
    }
}

Security in Custom Functions

Security Best Practices

  • Sanitize Input: Always sanitize user input with appropriate functions
  • Escape Output: Escape all dynamic output
  • Validate Data: Validate data before processing
  • Use Nonces: Implement nonces for forms and AJAX
  • Check Capabilities: Verify user permissions
  • Prefix Functions: Use unique prefixes to avoid conflicts
  • Use Prepared Statements: For database queries

Security Examples

<?php
/**
 * Secure custom function example
 */
function mytheme_secure_function() {
    // Check user capabilities
    if ( ! current_user_can( 'edit_posts' ) ) {
        wp_die( 'Unauthorized access' );
    }
    
    // Verify nonce
    if ( ! isset( $_POST['mytheme_nonce'] ) || 
         ! wp_verify_nonce( $_POST['mytheme_nonce'], 'mytheme_action' ) ) {
        wp_die( 'Security check failed' );
    }
    
    // Sanitize input
    $title = sanitize_text_field( $_POST['title'] );
    $content = wp_kses_post( $_POST['content'] );
    $url = esc_url_raw( $_POST['url'] );
    $number = intval( $_POST['number'] );
    
    // Validate data
    if ( empty( $title ) || strlen( $title ) > 100 ) {
        return new WP_Error( 'invalid_title', 'Invalid title provided' );
    }
    
    // Escape output
    echo '<h2>' . esc_html( $title ) . '</h2>';
    echo '<div>' . wp_kses_post( $content ) . '</div>';
    echo '<a href="' . esc_url( $url ) . '">Link</a>';
}

Useful Code Snippets

Disable Emojis
function mytheme_disable_emojis() {
    remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
    remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
    remove_action( 'wp_print_styles', 'print_emoji_styles' );
    remove_action( 'admin_print_styles', 'print_emoji_styles' );
}
add_action( 'init', 'mytheme_disable_emojis' );
Custom Login Logo
function mytheme_login_logo() {
    echo '<style>
        #login h1 a {
            background-image: url(' . get_template_directory_uri() . '/logo.png);
            width: 200px;
            height: 80px;
            background-size: contain;
        }
    </style>';
}
add_action( 'login_enqueue_scripts', 'mytheme_login_logo' );
Remove WordPress Version
remove_action( 'wp_head', 'wp_generator' );
add_filter( 'the_generator', '__return_empty_string' );

Practice Exercise

💻
Create Custom Function Library

Build a comprehensive set of custom functions including:

  1. Custom excerpt function with variable length
  2. Reading time calculator and display
  3. Related posts function with caching
  4. AJAX load more posts handler
  5. Social share buttons generator
  6. Custom breadcrumb navigation
  7. Image lazy loading implementation
  8. View counter for popular posts
  9. Three custom shortcodes
  10. Organize all functions into separate files

Additional Resources