/**
  * returns the given template rendered using the provided data and optional compiler
  * @param mixed     $_tpl                                                template, can either be a ITemplate object (i.e. Template\File), a valid path to a template, or
  *                                                                       a template as a string it is recommended to provide a ITemplate as it will probably make things faster,
  *                                                                       especially if you render a template multiple times
  * @param mixed     $data                                                the data to use, can either be a IDataProvider object (i.e. Data) or an associative array. if you're
  *                                                                       rendering the template from cache, it can be left null
  * @param ICompiler $_compiler                                           the compiler that must be used to compile the template, if left empty a default
  *                                                                       Compiler will be used.
  * @param bool      $_output                                             flag that defines whether the function returns the output of the template (false, default) or echoes it directly (true)
  * @throws Exception
  * @throws \Exception
  * @throws Exception\CoreException
  * @return string|null or the template output if $output is false
  */
 public function get($_tpl, $data = array(), $_compiler = null, $_output = false)
 {
     // a render call came from within a template, so we need a new dwoo instance in order to avoid breaking this one
     if ($this->template instanceof ITemplate) {
         $clone = clone $this;
         return $clone->get($_tpl, $data, $_compiler, $_output);
     }
     // auto-create template if required
     if ($_tpl instanceof ITemplate) {
         // valid, skip
     } elseif (is_string($_tpl)) {
         $_tpl = new File($_tpl);
         $_tpl->setIncludePath($this->templateDir);
     } else {
         throw new CoreException('Dwoo->get/Dwoo->output\'s first argument must be a \\Dwoo\\ITemplate (i.e. \\Dwoo\\Template\\File) or a valid path to a template file. Got: ' . $_tpl, E_USER_NOTICE);
     }
     // Put debug mode value
     $_tpl->setDebugMode($this->debugMode);
     // save the current template, enters render mode at the same time
     // if another rendering is requested it will be proxied to a new Core instance
     $this->template = $_tpl;
     // load data
     if ($data instanceof IDataProvider) {
         $this->data = $data->getData();
     } elseif (is_array($data)) {
         $this->data = $data;
     } elseif ($data instanceof \ArrayAccess) {
         $this->data = $data;
     } else {
         throw new CoreException('Dwoo->get/Dwoo->output\'s data argument must be a \\Dwoo\\IDataProvider object (i.e. \\Dwoo\\Data) or an associative array', E_USER_NOTICE);
     }
     $this->globals['template'] = $_tpl->getName();
     $this->initRuntimeVars($_tpl);
     // try to get cached template
     $file = $_tpl->getCachedTemplate($this);
     $doCache = $file === true;
     $cacheLoaded = is_string($file);
     if ($cacheLoaded === true) {
         // cache is present, run it
         if ($_output === true) {
             include $file;
             $this->template = null;
         } else {
             ob_start();
             include $file;
             $this->template = null;
             return ob_get_clean();
         }
     } else {
         // no cache present
         if ($doCache === true) {
             $dynamicId = uniqid();
         }
         $recompile = false;
         do {
             // render template
             try {
                 $compiledTemplate = $_tpl->getCompiledTemplate($this, $_compiler);
                 $out = (include $compiledTemplate);
             } catch (Exception $e) {
                 // output
                 if ($_output === true) {
                     ob_flush();
                 } else {
                     ob_clean();
                 }
                 throw $e;
             }
             if ($out === false) {
                 // template returned false so it needs to be recompiled
                 if ($recompile) {
                     // this is the second time this happened...
                     throw new Exception('Failed to compile template.');
                 }
                 $_tpl->forceCompilation();
                 $recompile = true;
             }
         } while ($recompile);
         if ($doCache === true) {
             $out = preg_replace('/(<%|%>|<\\?php|<\\?|\\?>)/', '<?php /*' . $dynamicId . '*/ echo \'$1\'; ?>', $out);
             $out = BlockDynamic::unescape($out, $dynamicId, $compiledTemplate);
         }
         // process filters
         foreach ($this->filters as $filter) {
             if (is_array($filter) && $filter[0] instanceof Filter) {
                 $out = call_user_func($filter, $out);
             } else {
                 $out = call_user_func($filter, $this, $out);
             }
         }
         if ($doCache === true) {
             // building cache
             $file = $_tpl->cache($this, $out);
             // run it from the cache to be sure dynamics are rendered
             if ($_output === true) {
                 include $file;
                 // exit render mode
                 $this->template = null;
             } else {
                 ob_start();
                 include $file;
                 // exit render mode
                 $this->template = null;
                 return ob_get_clean();
             }
         } else {
             // no need to build cache
             // exit render mode
             $this->template = null;
             // output
             if ($_output === true) {
                 echo $out;
             }
             return $out;
         }
     }
     return null;
 }