Smarty Layered Skinning

The default install of smarty is great but simple. I wanted to be able to have multiple template directories so that Smarty would search all of the directories for the requested template, choosing the first one found to be displayed (similar to a UNIX-style PATH environment variable).

This functionality is built-in to smarty (simply specify the template_dir attribute as an array), but only a single compile directory is specifiable, so compiled versions of templates in different directories will conflict. I created a SkinTool as a subclass of smarty to provide skinning. Its code is as follows:

class SkinTool extends Smarty{

         /**
          * Array of registered skins..
          */
	var $skins = array();
	
	/**
           * Overrides smarty method.  This will place compiled version of the template
           * in a subfolder of the compile_dir named after the skin where the template
           * resides.  If this subfolder does not exist, it is created.
           *
           * @param string $resource_name
           * @return string results of {@link _get_auto_filename()}
           */
         function _get_compile_path($resource_name)
         {
    	    $params = array('resource_name'=>$resource_name, 'resource_base_path' => $this->template_dir);
    	    $name = $this->_parse_resource_name($params);
    	    $template_dir = dirname($params['resource_name']);
    	    $skin = $this->skins[$template_dir];
    	    if ( strlen($skin) > 0 and preg_match('/^[0-9a-zA-Z_]+$/', $skin) ){
    		$compile_dir = $this->compile_dir.'/'.$skin;
    		if ( !file_exists($compile_dir) ){
    			mkdir($compile_dir);
    		}
    		if ( !file_exists($compile_dir) ){
    			trigger_error("Failed to create compile directory '$compile_dir'", E_USER_ERROR);
    		}
    	    } else {
    		$compile_dir = $this->compile_dir;
    	    }
             return $this->_get_auto_filename($compile_dir, $resource_name,
                                         $this->_compile_id) . '.php';
        }

	
	/**
	 * Registers a skin to be used as the default skin.  This skin is added to 
	 * the top of the stack so it has the highest priority.  If a template is
	 * requested and this skin does not contain that template, then the SkinTool
	 * will check the next skin in the stack. And so on...
	 *
          * @param $name The name of the skin
	 * @param $template_dir The directory to the templates for this skin.
	 */
	function register_skin( $name, $template_dir){
		if ( !is_array($this->template_dir) ){
			if ( strlen($this->template_dir) > 0 ){
				$this->template_dir = array($this->template_dir);
			} else {
				$this->template_dir = array();
			}
		}
		array_unshift($this->template_dir, $template_dir);
		$this->skins[$template_dir] = $name;
	
	}