$str) { if ($matches['MARK'][$i] === 'expression') { $tokens[] = ['expression', substr($str, 1, -1)]; } else { $tokens[] = ['literal', strtr($str, ['{{' => '{', '}}' => '}'])]; } } return $tokens; } /** * Replace the value of an attribute via the provided callback * * The callback will receive an array containing the type and value of each token in the AVT. * Its return value should use the same format * * @param DOMAttr $attribute * @param callable $callback * @return void */ public static function replace(DOMAttr $attribute, callable $callback) { $tokens = self::parse($attribute->value); foreach ($tokens as $k => $token) { $tokens[$k] = $callback($token); } $attribute->value = htmlspecialchars(self::serialize($tokens), ENT_NOQUOTES, 'UTF-8'); } /** * Serialize an array of AVT tokens back into an attribute value * * @param array $tokens * @return string */ public static function serialize(array $tokens) { $attrValue = ''; foreach ($tokens as $token) { if ($token[0] === 'literal') { $attrValue .= preg_replace('([{}])', '$0$0', $token[1]); } elseif ($token[0] === 'expression') { $attrValue .= '{' . $token[1] . '}'; } else { throw new RuntimeException('Unknown token type'); } } return $attrValue; } /** * Transform given attribute value template into an XSL fragment * * @param string $attrValue * @return string */ public static function toXSL($attrValue) { $xsl = ''; foreach (self::parse($attrValue) as list($type, $content)) { if ($type === 'expression') { $xsl .= ''; } elseif (trim($content) !== $content) { $xsl .= '' . htmlspecialchars($content, ENT_NOQUOTES, 'UTF-8') . ''; } else { $xsl .= htmlspecialchars($content, ENT_NOQUOTES, 'UTF-8'); } } return $xsl; } }