Best way of passing PHP variable between partials?

I have a variable in header.php, such as:

$page_extra_title = get_post_meta($this_page->ID, "_theme_extra_title", true);

Once I do:


I always get NULL outside of header.php (var_dump works properly in header.php only). I’ve been pasting the same variable everywhere I need it (page.php, post.php, footer.php etc.), but it’s madness and makes everything almost impossible to maintain.

I’m wondering what’s the best way of passing a variable through all the files in my theme? I guess using functions.php along with “get_post_meta” might not be the best idea? 🙂


Basic separated data structures

To pass around data, you normally utilize a Model (that’s the “M” in “MVC”). Let’s look at a very simple interface for data. Interfaces are just used as “Recipes” for our building blocks:

namespace WeCodeMore\Package\Models;
interface ArgsInterface
    public function getID();
    public function getLabel();

Above is what we pass around: A common ID and a “Label”.

Displaying data by combining atomic pieces

Next we need some View that negotiates between our Model and … our template.

namespace WeCodeMore\Package;
interface PackageViewInterface
     * @param Models\ArgsInterface $args
     * @return int|void
    public function render( Models\ArgsInterface $args );

Basically that Interface says

“We can render something and a Model is mandatory for that task”

Finally we need to implement above and build the actual View. As you can see, the constructor tells that the mandatory thing for our view is a Template and that we can render it. For the sake of easy development we even check if the template file actually is present so we can make other developers lives (and ours as well) much easier and note that.

In a second step in the render function we use a Closure to build the actual template wrapper and bindTo() the Model to the template.

namespace WeCodeMore\Package;

use WeCodeMore\Package\Models\ArgsInterface;

/** @noinspection PhpInconsistentReturnPointsInspection */
class PackageView implements PackageViewInterface
    /** @var string|\WP_Error */
    private $template;
     * @param string $template
    public function __construct( $template )
        $this->template = ! file_exists( $template )
            ? new \WP_Error( 'wcm-package', 'A package view needs a template' )
            : $template;
     * @param Models\ArgsInterface $args
     * @return int|void
    public function render( Models\ArgsInterface $args )
        if ( is_wp_error( $this->template ) )
            return print $this->template->get_error_message();

        /** @var $callback \Closure */
        $callback = function( $template )
            extract( get_object_vars( $this ) );
            require $template;
            $callback->bindTo( $args ),

Separating the View and Rendering

This means that we can use a very simple template like the following

<!--suppress HtmlFormInputWithoutLabel -->
<p><?= $label ?></p>

to render our content. Putting the pieces together we would get something around the following lines (in our Controller, Mediator, etc.):

namespace WeCodeMore\Package;

$view = new PackageView( plugin_dir_path( __FILE__ ).'tmpl/label.tmpl.php' );
$view->render( new Models\Args );

What did we gain?

This way we can

  1. Easily exchange templates without changing the data structure
  2. Have easy to read tempaltes
  3. Avoid global scope
  4. Can Unit-Test
  5. Can exchange the Model/the data without harming other components

Combining OOP PHP with the WP API

Of course this is hardly possible by using basic theming functionality like get_header(), get_footer(), etc., right? Wrong. Just call your classes in whatever template or template part you would like. Render it, transform the data, do whatever you want. If you are really nice you even just add your own bunch of custom filters and have some negotiator to take care of what gets rendered by which controller on which route/conditional template load.


You can work with stuff like above in WP without a problem and still stick to the basic API and reuse code and data without calling a single global or messing up and polluting the global name space.

Leave a Comment