Exemple #1
0
 protected function getPHP($template, $renderingType)
 {
     $serializer = new Serializer();
     $optimizer = new Optimizer();
     $ir = TemplateParser::parse($template, $renderingType);
     $php = $serializer->serialize($ir->documentElement);
     $php = $optimizer->optimize($php);
     return $php;
 }
 /**
  * Inline the text content of a node or the value of an attribute where it's known
  *
  * Will replace
  *     <xsl:if test="@foo='Foo'"><xsl:value-of select="@foo"/></xsl:if>
  * with
  *     <xsl:if test="@foo='Foo'">Foo</xsl:if>
  *
  * @param  DOMElement $template <xsl:template/> node
  * @return void
  */
 public function normalize(DOMElement $template)
 {
     $xpath = new DOMXPath($template->ownerDocument);
     $query = '//xsl:if | //xsl:when';
     foreach ($xpath->query($query) as $node) {
         // Test whether the map has exactly one key and one value
         $map = TemplateParser::parseEqualityExpr($node->getAttribute('test'));
         if ($map === false || count($map) !== 1 || count($map[key($map)]) !== 1) {
             continue;
         }
         $expr = key($map);
         $value = end($map[$expr]);
         $this->inlineInferredValue($node, $expr, $value);
     }
 }
 protected static function collectCompatibleBranches(DOMNode $node)
 {
     $nodes = array();
     $key = \null;
     $values = array();
     while ($node && self::isXslWhen($node)) {
         $branch = TemplateParser::parseEqualityExpr($node->getAttribute('test'));
         if ($branch === \false || \count($branch) !== 1) {
             break;
         }
         if (isset($key) && \key($branch) !== $key) {
             break;
         }
         if (\array_intersect($values, \end($branch))) {
             break;
         }
         $key = \key($branch);
         $values = \array_merge($values, \end($branch));
         $nodes[] = $node;
         $node = $node->nextSibling;
     }
     return $nodes;
 }
 /**
  * Collect consecutive xsl:when elements that share the same kind of equality tests
  *
  * Will return xsl:when elements that test a constant part (e.g. a literal) against the same
  * variable part (e.g. the same attribute)
  *
  * @param  DOMNode      $node First node to inspect
  * @return DOMElement[]
  */
 protected static function collectCompatibleBranches(DOMNode $node)
 {
     $nodes = [];
     $key = null;
     $values = [];
     while ($node && self::isXslWhen($node)) {
         $branch = TemplateParser::parseEqualityExpr($node->getAttribute('test'));
         if ($branch === false || count($branch) !== 1) {
             // The expression is not entirely composed of equalities, or they have a different
             // variable part
             break;
         }
         if (isset($key) && key($branch) !== $key) {
             // Not the same variable as our branches
             break;
         }
         if (array_intersect($values, end($branch))) {
             // Duplicate values across branches, e.g. ".=1 or .=2" and ".=2 or .=3"
             break;
         }
         $key = key($branch);
         $values = array_merge($values, end($branch));
         // Record this node then move on to the next sibling
         $nodes[] = $node;
         $node = $node->nextSibling;
     }
     return $nodes;
 }
 /**
  * @dataProvider getParseEqualityExprTests
  */
 public function testParseEqualityExpr($expr, $expected)
 {
     $this->assertSame($expected, TemplateParser::parseEqualityExpr($expr));
 }
Exemple #6
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;
 }