Bespoke application as Wordpress sibling

21 August 2019

Recently I was asked to build a bespoke web application to run alongside an existing Wordpress website. the application would be entirely separate, being placed in a subdirectory of the web root, but needed to have common header and footer areas, with a common menu structure, to the parent website. In order to do that and avoid having to manually update menus every time the parent site was updated here's a handy little idea I used that you may find useful or be able to adapt/extend for your own use.

 

Menus

My approach was to load the wordpress core only and retrieve a named menu using wp_nav_menu() with 'echo' => false. The menu is cached for future use; updating the cache only periodically to avoid a regular performance hit. You could also run the caching script separately from a cron job.

Here's the snippet that lives in the base controller for the public facing content of my application:

$cache       = 'wp-menu-cache.txt';
$date        = 'wp-menu-cache-date.txt';
$currentTime = time();
$datefile    = fopen($date, 'r');
$cacheDate   = fgets($datefile);
fclose($datefile);

if (floor(abs(($currentTime - $cacheDate) / 3600)) <= 2 && $cacheDate) {
    $wpmenu = file_get_contents($cache);
}
else {
    include(dirname($_SERVER['DOCUMENT_ROOT']).'wp-load.php');

    $wpmenu = wp_nav_menu( array('menu' => 'main-menu','menu_class' => 'main-menu', 'container' => '', 'echo' => false) );
    $cachefile = fopen($cache, 'wb');
    fwrite($cachefile, utf8_encode($wpmenu));
    fclose($cachefile);
    $datefile = fopen($date, 'wb');
    fwrite($datefile, utf8_encode(time()));
    fclose($datefile);
}

 

The menu contents are retrieved and cached for 2 hours. The menu, stored in $wpmenu, will be in html that you can directly echo out into the public facing application template.

 

Footer

The footer was a little bit more involved as it contained a bunch of static content that also needed to be reflected, so in this case I wrote a small utility function in the base controller to  'scrape' the parent site footer. That content being cached in the same way as for the menus above, albeit with a much longer cache duration on the basis that footer is much less likely to change. The function uses PHP's DOMDocument to search for elements by classname (there was no ID in this case and return that node.

private function get_element($url, $classname)
{
	if (!filter_var($url, FILTER_VALIDATE_URL) === false) {
		$html = $this->get_html($url);
		   
	    if(!empty($html)) {
	 		$innerHTML = '';
	        $dom  =	new DOMDocument();

	        @$dom->loadHTML($html);

	        $finder = new DomXPath($dom);
			
			$nodes = $finder->query("//div[contains(concat(' ', normalize-space(@class), ' '), '".$classname."')]");

	      	$tmp_dom = new DOMDocument(); 

			foreach ($nodes as $node) {
			    $tmp_dom->appendChild($tmp_dom->importNode($node,true));
			    break;
			}
			$innerHTML.=trim($tmp_dom->saveHTML()); 
	       	
	       	return $innerHTML;

	    }
	    else {
		    return false;
		}
	}
	else {
	    return false;
	}

}
private function get_html($url)
{
 $ch     =	curl_init();

  curl_setopt_array(  
        $ch,    
        array
        (
            CURLOPT_URL             =>	$url,
            CURLOPT_RETURNTRANSFER  =>	true,
            CURLOPT_FOLLOWLOCATION  =>	true
        )
     );

  $html   =	curl_exec($ch);

  return $html;
}	

 

The call in the base controller then simply looked like:

$wpfooter  = $this->parse_doc->get_element('https://parent-site-url', 'footer-class');

 

Really simple but quite handy given how ubiquitous Wordpress is.