Beispiel #1
0
 /**
  * Generate an XSL stylesheet based on given rendering configuration
  *
  * @param  Rendering $rendering
  * @return string
  */
 public function getXSL(Rendering $rendering)
 {
     $groupedTemplates = [];
     $prefixes = [];
     $templates = $rendering->getTemplates();
     // Replace simple templates if there are at least 3 of them
     TemplateHelper::replaceHomogeneousTemplates($templates, 3);
     // Group tags with identical templates together
     foreach ($templates as $tagName => $template) {
         $template = $this->optimizer->optimizeTemplate($template);
         $groupedTemplates[$template][] = $tagName;
         // Record the tag's prefix if applicable
         $pos = strpos($tagName, ':');
         if ($pos !== false) {
             $prefixes[substr($tagName, 0, $pos)] = 1;
         }
     }
     // Declare all the namespaces in use at the top
     $xsl = '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"';
     // Append the namespace declarations to the stylesheet
     $prefixes = array_keys($prefixes);
     sort($prefixes);
     foreach ($prefixes as $prefix) {
         $xsl .= ' xmlns:' . $prefix . '="urn:s9e:TextFormatter:' . $prefix . '"';
     }
     /**
      * Exclude those prefixes to keep the HTML neat
      *
      * @link http://lenzconsulting.com/namespaces-in-xslt/#exclude-result-prefixes
      */
     if (!empty($prefixes)) {
         $xsl .= ' exclude-result-prefixes="' . implode(' ', $prefixes) . '"';
     }
     // Start the stylesheet with the boilerplate stuff
     $xsl .= '><xsl:output method="html" encoding="utf-8" indent="no"';
     $xsl .= '/>';
     // Add stylesheet parameters
     foreach ($rendering->getAllParameters() as $paramName => $paramValue) {
         $xsl .= '<xsl:param name="' . htmlspecialchars($paramName) . '"';
         if ($paramValue === '') {
             $xsl .= '/>';
         } else {
             $xsl .= '>' . htmlspecialchars($paramValue) . '</xsl:param>';
         }
     }
     // Add templates
     foreach ($groupedTemplates as $template => $tagNames) {
         // Open the template element
         $xsl .= '<xsl:template match="' . implode('|', $tagNames) . '"';
         // Make it a self-closing element if the template is empty
         if ($template === '') {
             $xsl .= '/>';
         } else {
             $xsl .= '>' . $template . '</xsl:template>';
         }
     }
     $xsl .= '</xsl:stylesheet>';
     return $xsl;
 }
Beispiel #2
0
 public function getXSL(Rendering $rendering)
 {
     $groupedTemplates = array();
     $prefixes = array();
     $templates = $rendering->getTemplates();
     TemplateHelper::replaceHomogeneousTemplates($templates, 3);
     foreach ($templates as $tagName => $template) {
         $template = $this->optimizer->optimizeTemplate($template);
         $groupedTemplates[$template][] = $tagName;
         $pos = \strpos($tagName, ':');
         if ($pos !== \false) {
             $prefixes[\substr($tagName, 0, $pos)] = 1;
         }
     }
     $xsl = '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"';
     $prefixes = \array_keys($prefixes);
     \sort($prefixes);
     foreach ($prefixes as $prefix) {
         $xsl .= ' xmlns:' . $prefix . '="urn:s9e:TextFormatter:' . $prefix . '"';
     }
     if (!empty($prefixes)) {
         $xsl .= ' exclude-result-prefixes="' . \implode(' ', $prefixes) . '"';
     }
     $xsl .= '><xsl:output method="html" encoding="utf-8" indent="no"';
     $xsl .= '/>';
     foreach ($rendering->getAllParameters() as $paramName => $paramValue) {
         $xsl .= '<xsl:param name="' . \htmlspecialchars($paramName) . '"';
         if ($paramValue === '') {
             $xsl .= '/>';
         } else {
             $xsl .= '>' . \htmlspecialchars($paramValue) . '</xsl:param>';
         }
     }
     foreach ($groupedTemplates as $template => $tagNames) {
         $xsl .= '<xsl:template match="' . \implode('|', $tagNames) . '"';
         if ($template === '') {
             $xsl .= '/>';
         } else {
             $xsl .= '>' . $template . '</xsl:template>';
         }
     }
     $xsl .= '</xsl:stylesheet>';
     return $xsl;
 }
Beispiel #3
0
 /**
  * Generate the source for a PHP class that renders an intermediate representation according to
  * given rendering configuration
  *
  * @param  Rendering $rendering
  * @return string
  */
 public function generate(Rendering $rendering)
 {
     // Copy some options to the serializer
     $this->serializer->useMultibyteStringFunctions = $this->useMultibyteStringFunctions;
     // Gather templates and optimize simple templates
     $templates = $rendering->getTemplates();
     // Group templates by content to deduplicate them
     $groupedTemplates = [];
     foreach ($templates as $tagName => $template) {
         $groupedTemplates[$template][] = $tagName;
     }
     // Record whether the template has a <xsl:apply-templates/> with a select attribute
     $hasApplyTemplatesSelect = false;
     // Assign a branch number to each unique template and record the value for each tag
     $tagBranch = 0;
     $tagBranches = [];
     // Store the compiled template for each branch
     $compiledTemplates = [];
     // Other branch tables
     $branchTables = [];
     // Parse each template and serialize it to PHP
     foreach ($groupedTemplates as $template => $tagNames) {
         // Parse the template
         $ir = TemplateParser::parse($template);
         // Test whether this template uses an <xsl:apply-templates/> element with a select
         if (!$hasApplyTemplatesSelect) {
             foreach ($ir->getElementsByTagName('applyTemplates') as $applyTemplates) {
                 if ($applyTemplates->hasAttribute('select')) {
                     $hasApplyTemplatesSelect = true;
                 }
             }
         }
         // Serialize the representation to PHP
         $templateSource = $this->serializer->serialize($ir->documentElement);
         if (isset($this->optimizer)) {
             $templateSource = $this->optimizer->optimize($templateSource);
         }
         // Record the branch tables used in this template
         $branchTables += $this->serializer->branchTables;
         // Record the source for this branch number and assign this number to each tag
         $compiledTemplates[$tagBranch] = $templateSource;
         foreach ($tagNames as $tagName) {
             $tagBranches[$tagName] = $tagBranch;
         }
         ++$tagBranch;
     }
     // Unset the vars we don't need anymore
     unset($groupedTemplates, $ir, $quickRender);
     // Store the compiled template if we plan to create a Quick renderer
     $quickSource = false;
     if ($this->enableQuickRenderer) {
         $quickRender = [];
         foreach ($tagBranches as $tagName => $tagBranch) {
             $quickRender[$tagName] = $compiledTemplates[$tagBranch];
         }
         $quickSource = Quick::getSource($quickRender);
         unset($quickRender);
     }
     // Coalesce all the compiled templates
     $templatesSource = Quick::generateConditionals('$tb', $compiledTemplates);
     unset($compiledTemplates);
     // Test whether any templates needs an XPath engine
     if ($hasApplyTemplatesSelect) {
         $needsXPath = true;
     } elseif (strpos($templatesSource, '$this->getParamAsXPath') !== false) {
         $needsXPath = true;
     } elseif (strpos($templatesSource, '$this->xpath') !== false) {
         $needsXPath = true;
     } else {
         $needsXPath = false;
     }
     // Start the code right after the class name, we'll prepend the header when we're done
     $php = [];
     $php[] = ' extends \\s9e\\TextFormatter\\Renderer';
     $php[] = '{';
     $php[] = '	protected $params=' . self::export($rendering->getAllParameters()) . ';';
     $php[] = '	protected static $tagBranches=' . self::export($tagBranches) . ';';
     foreach ($branchTables as $varName => $branchTable) {
         $php[] = '	protected static $' . $varName . '=' . self::export($branchTable) . ';';
     }
     if ($needsXPath) {
         $php[] = '	protected $xpath;';
     }
     $php[] = '	public function __sleep()';
     $php[] = '	{';
     $php[] = '		$props = get_object_vars($this);';
     $php[] = "\t\tunset(\$props['out'], \$props['proc'], \$props['source']" . ($needsXPath ? ", \$props['xpath']" : '') . ');';
     $php[] = '		return array_keys($props);';
     $php[] = '	}';
     $php[] = '	public function renderRichText($xml)';
     $php[] = '	{';
     if ($quickSource !== false) {
         // Try the Quick renderer first and if anything happens just keep going with the normal
         // rendering
         $php[] = '		if (!isset($this->quickRenderingTest) || !preg_match($this->quickRenderingTest, $xml))';
         $php[] = '		{';
         $php[] = '			try';
         $php[] = '			{';
         $php[] = '				return $this->renderQuick($xml);';
         $php[] = '			}';
         $php[] = '			catch (\\Exception $e)';
         $php[] = '			{';
         $php[] = '			}';
         $php[] = '		}';
     }
     $php[] = '		$dom = $this->loadXML($xml);';
     if ($needsXPath) {
         $php[] = '		$this->xpath = new \\DOMXPath($dom);';
     }
     $php[] = "\t\t\$this->out = '';";
     $php[] = '		$this->at($dom->documentElement);';
     if ($needsXPath) {
         $php[] = '		$this->xpath = null;';
     }
     $php[] = '		return $this->out;';
     $php[] = '	}';
     if ($hasApplyTemplatesSelect) {
         $php[] = '	protected function at(\\DOMNode $root, $xpath = null)';
     } else {
         $php[] = '	protected function at(\\DOMNode $root)';
     }
     $php[] = '	{';
     $php[] = '		if ($root->nodeType === 3)';
     $php[] = '		{';
     $php[] = '			$this->out .= htmlspecialchars($root->textContent,' . ENT_NOQUOTES . ');';
     $php[] = '		}';
     $php[] = '		else';
     $php[] = '		{';
     if ($hasApplyTemplatesSelect) {
         $php[] = '			foreach (isset($xpath) ? $this->xpath->query($xpath, $root) : $root->childNodes as $node)';
     } else {
         $php[] = '			foreach ($root->childNodes as $node)';
     }
     $php[] = '			{';
     $php[] = '				if (!isset(self::$tagBranches[$node->nodeName]))';
     $php[] = '				{';
     $php[] = '					$this->at($node);';
     $php[] = '				}';
     $php[] = '				else';
     $php[] = '				{';
     $php[] = '					$tb = self::$tagBranches[$node->nodeName];';
     $php[] = '					' . $templatesSource;
     $php[] = '				}';
     $php[] = '			}';
     $php[] = '		}';
     $php[] = '	}';
     // Add the getParamAsXPath() method if necessary
     if (strpos($templatesSource, '$this->getParamAsXPath') !== false) {
         $php[] = '	protected function getParamAsXPath($k)';
         $php[] = '	{';
         $php[] = '		if (!isset($this->params[$k]))';
         $php[] = '		{';
         $php[] = '			return "\'\'";';
         $php[] = '		}';
         $php[] = '		$str = $this->params[$k];';
         $php[] = '		if (strpos($str, "\'") === false)';
         $php[] = '		{';
         $php[] = '			return "\'$str\'";';
         $php[] = '		}';
         $php[] = '		if (strpos($str, \'"\') === false)';
         $php[] = '		{';
         $php[] = '			return "\\"$str\\"";';
         $php[] = '		}';
         $php[] = '		$toks = [];';
         $php[] = '		$c = \'"\';';
         $php[] = '		$pos = 0;';
         $php[] = '		while ($pos < strlen($str))';
         $php[] = '		{';
         $php[] = '			$spn = strcspn($str, $c, $pos);';
         $php[] = '			if ($spn)';
         $php[] = '			{';
         $php[] = '				$toks[] = $c . substr($str, $pos, $spn) . $c;';
         $php[] = '				$pos += $spn;';
         $php[] = '			}';
         $php[] = '			$c = ($c === \'"\') ? "\'" : \'"\';';
         $php[] = '		}';
         $php[] = '		return \'concat(\' . implode(\',\', $toks) . \')\';';
         $php[] = '	}';
     }
     // Append the Quick renderer if applicable
     if ($quickSource !== false) {
         $php[] = $quickSource;
     }
     // Close the class definition
     $php[] = '}';
     // Assemble the source
     $php = implode("\n", $php);
     // Finally, optimize the control structures
     if (isset($this->controlStructuresOptimizer)) {
         $php = $this->controlStructuresOptimizer->optimize($php);
     }
     // Generate a name for that class if necessary, and save it
     $className = isset($this->className) ? $this->className : $this->defaultClassPrefix . sha1($php);
     $this->lastClassName = $className;
     // Prepare the header
     $header = "\n" . "/**\n" . "* @package   s9e\\TextFormatter\n" . "* @copyright Copyright (c) 2010-2015 The s9e Authors\n" . "* @license   http://www.opensource.org/licenses/mit-license.php The MIT License\n" . "*/\n";
     // Declare the namespace and class name
     $pos = strrpos($className, '\\');
     if ($pos !== false) {
         $header .= 'namespace ' . substr($className, 0, $pos) . ";\n\n";
         $className = substr($className, 1 + $pos);
     }
     // Prepend the header and the class name
     $php = $header . 'class ' . $className . $php;
     return $php;
 }
 public function generate(Rendering $rendering)
 {
     $this->serializer->useMultibyteStringFunctions = $this->useMultibyteStringFunctions;
     $templates = $rendering->getTemplates();
     $groupedTemplates = array();
     foreach ($templates as $tagName => $template) {
         $groupedTemplates[$template][] = $tagName;
     }
     $hasApplyTemplatesSelect = \false;
     $tagBranch = 0;
     $tagBranches = array();
     $compiledTemplates = array();
     $branchTables = array();
     foreach ($groupedTemplates as $template => $tagNames) {
         $ir = TemplateParser::parse($template);
         if (!$hasApplyTemplatesSelect) {
             foreach ($ir->getElementsByTagName('applyTemplates') as $applyTemplates) {
                 if ($applyTemplates->hasAttribute('select')) {
                     $hasApplyTemplatesSelect = \true;
                 }
             }
         }
         $templateSource = $this->serializer->serialize($ir->documentElement);
         if (isset($this->optimizer)) {
             $templateSource = $this->optimizer->optimize($templateSource);
         }
         $branchTables += $this->serializer->branchTables;
         $compiledTemplates[$tagBranch] = $templateSource;
         foreach ($tagNames as $tagName) {
             $tagBranches[$tagName] = $tagBranch;
         }
         ++$tagBranch;
     }
     unset($groupedTemplates, $ir, $quickRender);
     $quickSource = \false;
     if ($this->enableQuickRenderer) {
         $quickRender = array();
         foreach ($tagBranches as $tagName => $tagBranch) {
             $quickRender[$tagName] = $compiledTemplates[$tagBranch];
         }
         $quickSource = Quick::getSource($quickRender);
         unset($quickRender);
     }
     $templatesSource = Quick::generateConditionals('$tb', $compiledTemplates);
     unset($compiledTemplates);
     if ($hasApplyTemplatesSelect) {
         $needsXPath = \true;
     } elseif (\strpos($templatesSource, '$this->getParamAsXPath') !== \false) {
         $needsXPath = \true;
     } elseif (\strpos($templatesSource, '$this->xpath') !== \false) {
         $needsXPath = \true;
     } else {
         $needsXPath = \false;
     }
     $php = array();
     $php[] = ' extends \\s9e\\TextFormatter\\Renderer';
     $php[] = '{';
     $php[] = '	protected $params=' . self::export($rendering->getAllParameters()) . ';';
     $php[] = '	protected static $tagBranches=' . self::export($tagBranches) . ';';
     foreach ($branchTables as $varName => $branchTable) {
         $php[] = '	protected static $' . $varName . '=' . self::export($branchTable) . ';';
     }
     if ($needsXPath) {
         $php[] = '	protected $xpath;';
     }
     $php[] = '	public function __sleep()';
     $php[] = '	{';
     $php[] = '		$props = get_object_vars($this);';
     $php[] = "\t\tunset(\$props['out'], \$props['proc'], \$props['source']" . ($needsXPath ? ", \$props['xpath']" : '') . ');';
     $php[] = '		return array_keys($props);';
     $php[] = '	}';
     $php[] = '	public function renderRichText($xml)';
     $php[] = '	{';
     if ($quickSource !== \false) {
         $php[] = '		if (!isset($this->quickRenderingTest) || !preg_match($this->quickRenderingTest, $xml))';
         $php[] = '		{';
         $php[] = '			try';
         $php[] = '			{';
         $php[] = '				return $this->renderQuick($xml);';
         $php[] = '			}';
         $php[] = '			catch (\\Exception $e)';
         $php[] = '			{';
         $php[] = '			}';
         $php[] = '		}';
     }
     $php[] = '		$dom = $this->loadXML($xml);';
     if ($needsXPath) {
         $php[] = '		$this->xpath = new \\DOMXPath($dom);';
     }
     $php[] = "\t\t\$this->out = '';";
     $php[] = '		$this->at($dom->documentElement);';
     if ($needsXPath) {
         $php[] = '		$this->xpath = null;';
     }
     $php[] = '		return $this->out;';
     $php[] = '	}';
     if ($hasApplyTemplatesSelect) {
         $php[] = '	protected function at(\\DOMNode $root, $xpath = null)';
     } else {
         $php[] = '	protected function at(\\DOMNode $root)';
     }
     $php[] = '	{';
     $php[] = '		if ($root->nodeType === 3)';
     $php[] = '		{';
     $php[] = '			$this->out .= htmlspecialchars($root->textContent,' . \ENT_NOQUOTES . ');';
     $php[] = '		}';
     $php[] = '		else';
     $php[] = '		{';
     if ($hasApplyTemplatesSelect) {
         $php[] = '			foreach (isset($xpath) ? $this->xpath->query($xpath, $root) : $root->childNodes as $node)';
     } else {
         $php[] = '			foreach ($root->childNodes as $node)';
     }
     $php[] = '			{';
     $php[] = '				if (!isset(self::$tagBranches[$node->nodeName]))';
     $php[] = '				{';
     $php[] = '					$this->at($node);';
     $php[] = '				}';
     $php[] = '				else';
     $php[] = '				{';
     $php[] = '					$tb = self::$tagBranches[$node->nodeName];';
     $php[] = '					' . $templatesSource;
     $php[] = '				}';
     $php[] = '			}';
     $php[] = '		}';
     $php[] = '	}';
     if (\strpos($templatesSource, '$this->getParamAsXPath') !== \false) {
         $php[] = '	protected function getParamAsXPath($k)';
         $php[] = '	{';
         $php[] = '		if (!isset($this->params[$k]))';
         $php[] = '		{';
         $php[] = '			return "\'\'";';
         $php[] = '		}';
         $php[] = '		$str = $this->params[$k];';
         $php[] = '		if (strpos($str, "\'") === false)';
         $php[] = '		{';
         $php[] = '			return "\'$str\'";';
         $php[] = '		}';
         $php[] = '		if (strpos($str, \'"\') === false)';
         $php[] = '		{';
         $php[] = '			return "\\"$str\\"";';
         $php[] = '		}';
         $php[] = '		$toks = array();';
         $php[] = '		$c = \'"\';';
         $php[] = '		$pos = 0;';
         $php[] = '		while ($pos < strlen($str))';
         $php[] = '		{';
         $php[] = '			$spn = strcspn($str, $c, $pos);';
         $php[] = '			if ($spn)';
         $php[] = '			{';
         $php[] = '				$toks[] = $c . substr($str, $pos, $spn) . $c;';
         $php[] = '				$pos += $spn;';
         $php[] = '			}';
         $php[] = '			$c = ($c === \'"\') ? "\'" : \'"\';';
         $php[] = '		}';
         $php[] = '		return \'concat(\' . implode(\',\', $toks) . \')\';';
         $php[] = '	}';
     }
     if ($quickSource !== \false) {
         $php[] = $quickSource;
     }
     $php[] = '}';
     $php = \implode("\n", $php);
     if (isset($this->controlStructuresOptimizer)) {
         $php = $this->controlStructuresOptimizer->optimize($php);
     }
     $className = isset($this->className) ? $this->className : $this->defaultClassPrefix . \sha1($php);
     $this->lastClassName = $className;
     $header = "\n/**\n* @package   s9e\\TextFormatter\n* @copyright Copyright (c) 2010-2016 The s9e Authors\n* @license   http://www.opensource.org/licenses/mit-license.php The MIT License\n*/\n";
     $pos = \strrpos($className, '\\');
     if ($pos !== \false) {
         $header .= 'namespace ' . \substr($className, 0, $pos) . ";\n\n";
         $className = \substr($className, 1 + $pos);
     }
     $php = $header . 'class ' . $className . $php;
     return $php;
 }