/** * The compilation launcher. It executes the proper compilation steps * according to the inheritance rules etc. * * @param String $code The source code to be compiled. * @param String $filename The source template filename. * @param String $compiledFilename The output template filename. * @param Int $mode The compilation mode. */ public function compile($code, $filename, $compiledFilename, $mode) { $manager = $this->getCdfManager(); // Initialize the context. $this->_contextStack->push(new Opt_Compiler_Context($this, Opt_Compiler_Context::TEMPLATE_CTX, $filename)); try { // First, we select a parser. if (!isset($this->_parsers[$mode])) { $this->_parsers[$mode] = new $mode(); if (!$this->_parsers[$mode] instanceof Opt_Parser_Interface) { throw new Opt_InvalidParser_Exception($mode); } } $parser = $this->_parsers[$mode]; $parser->setCompiler($this); // We cannot compile two templates at the same time if (!is_null($this->_template)) { throw new Opt_CompilerLocked_Exception($filename, $this->_template); } // Detecting recursive inclusion if (is_null(self::$_recursionDetector)) { self::$_recursionDetector = array(0 => $filename); $weFree = true; } else { if (in_array($filename, self::$_recursionDetector)) { $exception = new Opt_CompilerRecursion_Exception($filename); $exception->setData(self::$_recursionDetector); throw $exception; } self::$_recursionDetector[] = $filename; $weFree = false; } // Cleaning up the processors foreach ($this->_processors as $proc) { $proc->reset(); } // Initializing the template launcher $this->set('template', $this->_template = $filename); $this->set('mode', $mode); $this->set('currentTemplate', $this->_template); array_push(self::$_templates, $filename); $this->_stack = new SplStack(); $i = 0; $extend = $filename; $memory = 0; // The inheritance loop do { // Stage 1 - code compilation if ($this->_tpl->debugConsole) { $initial = memory_get_usage(); $tree = $parser->parse($extend, $code); // Migration stage - only if backwards compatibility is on if ($this->_tpl->backwardCompatibility) { $tree = $this->_migrate($tree); } // Stage 2 - PHP tree processing $this->_stack = null; $this->_stage2($tree); $this->set('escape', NULL); unset($this->_stack); $memory += memory_get_usage() - $initial; unset($code); } else { $tree = $parser->parse($extend, $code); unset($code); // Migration stage - only if backward compatibility is on if ($this->_tpl->backwardCompatibility) { $this->_migrate($tree); } // Stage 2 - PHP tree processing $this->_stack = array(); $this->_stage2($tree); $this->set('escape', NULL); unset($this->_stack); } // if the template extends something, load it and also process if (isset($extend) && $extend != $filename) { $this->addDependantTemplate($extend); } if (!is_null($snippet = $tree->get('snippet'))) { $tree->dispose(); unset($tree); // Change the specified snippet into a root node. $tree = new Opt_Xml_Root(); $attribute = new Opt_Xml_Attribute('opt:use', $snippet); $this->processor('snippet')->processAttribute($tree, $attribute); $this->processor('snippet')->postprocessAttribute($tree, $attribute); $this->_stage2($tree, true); break; } if (!is_null($extend = $tree->get('extend'))) { $tree->dispose(); unset($tree); $this->set('currentTemplate', $extend); array_pop(self::$_templates); array_push(self::$_templates, $extend); $code = $this->_tpl->_getSource($extend); } $i++; } while (!is_null($extend)); // There are some dependant templates. We must add a suitable PHP code // to the output. if (sizeof($this->_dependencies) > 0) { $this->_addDependencies($tree); } if ($this->_tpl->debugConsole) { Opt_Support::addCompiledTemplate($this->_template, $memory); } // Stage 3 - linking the last tree if (!is_null($compiledFilename)) { $this->_output = ''; $this->_newQueue = null; $this->_dynamicBlocks = array(); $this->_stage3($output, $tree); $tree->dispose(); unset($tree); $this->_output = str_replace('?><' . '?php', '', $this->_output); // Build the directories, if needed. if (($pos = strrpos($compiledFilename, '/')) !== false) { $path = $this->_tpl->compileDir . substr($compiledFilename, 0, $pos); if (!is_dir($path)) { mkdir($path, 0750, true); } } // Save the file if (sizeof($this->_dynamicBlocks) > 0) { file_put_contents($this->_tpl->compileDir . $compiledFilename . '.dyn', serialize($this->_dynamicBlocks)); } file_put_contents($this->_tpl->compileDir . $compiledFilename, $this->_output); $this->_output = ''; $this->_dynamicBlocks = null; } else { $tree->dispose(); } array_pop(self::$_templates); $this->_inheritance = array(); if ($weFree) { // Do the cleanup. $this->_dependencies = array(); self::$_recursionDetector = NULL; foreach ($this->_processors as $processor) { $processor->reset(); } } $this->_template = NULL; $manager->clearLocals(); // Free the context. while ($this->_contextStack->count() > 0) { $ctx = $this->_contextStack->pop(); $ctx->dispose(); } // Run the new garbage collector, if it is available. /* if(version_compare(PHP_VERSION, '5.3.0', '>=')) { gc_collect_cycles(); }*/ } catch (Exception $e) { // Free the context while ($this->_contextStack->count() > 0) { $ctx = $this->_contextStack->pop(); $ctx->dispose(); } // Free the memory if (isset($tree)) { $tree->dispose(); } // Clean the compiler state in case of exception $this->_template = NULL; $this->_dependencies = array(); self::$_recursionDetector = NULL; foreach ($this->_processors as $processor) { $processor->reset(); } $manager->clearLocals(); // And throw it forward. throw $e; } }