Define custom Page Template without its own .php file

WordPress read the templates from file headers, and then set them in the cache. using wp_cache_set() nad a key that is derived from the md5 has of stylesheet folder.

So, just overriding that cache we can show the templates we want. To override that cache we need to call wp_cache_set() again using same key.

First of all let’s write a function that retrieve the templates we want to display.
There are a lot of ways we can set templates: option, configuration file, a filter, or a combination of them:

function get_custom_page_templates() {
  $templates = array();
  // maybe by options? --> $templates = get_option( 'custom_page_templates' );
  // maybe by conf file? --> $templates = include 'custom_page_templates.php';
  return apply_filters( 'custom_page_templates', $templates );
}

After that we need to retrieve the templates inside the page editing, just after WordPress saved the cache. Moreover we need to get them again when the editing form is posted to allow saving.

We can use 'edit_form_after_editor' hook for first scope, 'load-post.php' and 'load-post.new' for the second:

add_action( 'edit_form_after_editor', 'custom_page_templates_init' );
add_action( 'load-post.php', 'custom_page_templates_init_post' );
add_action( 'load-post-new.php', 'custom_page_templates_init_post' );

function custom_page_templates_init() {
  remove_action( current_filter(), __FUNCTION__ );
  if ( is_admin() && get_current_screen()->post_type === 'page' ) {
    $templates = get_custom_page_templates(); // the function above
    if ( ! empty( $templates ) )  {
      set_custom_page_templates( $templates );
    }
  }
}

function custom_page_templates_init_post() {
  remove_action( current_filter(), __FUNCTION__ );
  $method = filter_input( INPUT_SERVER, 'REQUEST_METHOD', FILTER_SANITIZE_STRING );
  if ( empty( $method ) || strtoupper( $method ) !== 'POST' ) return;
  if ( get_current_screen()->post_type === 'page' ) {
    custom_page_templates_init();
  }
}

Last thing we have to do, write the set_custom_page_templates() function that edit the cache to include our templates, being sure to merge any templates defined by file headers:

function set_custom_page_templates( $templates = array() ) {
  if ( ! is_array( $templates ) || empty( $templates ) ) return;
  $core = array_flip( (array) get_page_templates() ); // templates defined by file
  $data = array_filter( array_merge( $core, $templates ) );
  ksort( $data );
  $stylesheet = get_stylesheet();
  $hash = md5( get_theme_root( $stylesheet ) . "https://wordpress.stackexchange.com/" . $stylesheet );
  $persistently = apply_filters( 'wp_cache_themes_persistently', false, 'WP_Theme' );
  $exp = is_int( $persistently ) ? $persistently : 1800;
  wp_cache_set( 'page_templates-' . $hash, $data, 'themes', $exp );
}

Having this code running, you can setup custom templates just using the method you used in get_custom_page_templates() function, here I use the filter:

add_filter( 'custom_page_templates', function( $now_templates ) {

  $templates = array(
    'some-custom-page-template' => 'Some Custom Page Template',
    'another-custom-page-template' => 'Another Custom Page Template' ,
  );

  return array_merge( $now_templates, $templates );

} );

And you’re done.

Leave a Comment