Completed
Push — master ( f944f8...d2d21b )
by Josh
13:59
created

AVTHelper::parse()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 9.2408
c 0
b 0
f 0
cc 5
nc 4
nop 1
1
<?php
2
3
/**
4
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) 2010-2019 The s9e Authors
6
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
7
*/
8
namespace s9e\TextFormatter\Configurator\Helpers;
9
10
use DOMAttr;
11
use RuntimeException;
12
13
abstract class AVTHelper
14
{
15
	/**
16
	* Parse an attribute value template
17
	*
18
	* @link https://www.w3.org/TR/1999/REC-xslt-19991116#dt-attribute-value-template
19
	*
20
	* @param  string $attrValue Attribute value
21
	* @return array             Array of tokens
22
	*/
23
	public static function parse($attrValue)
24
	{
25
		preg_match_all('(\\{\\{|\\{(?:[^\'"}]|\'[^\']*\'|"[^"]*")+\\}|\\{|[^{]++)', $attrValue, $matches);
26
27
		$tokens  = [];
28
		foreach ($matches[0] as $str)
29
		{
30
			if ($str === '{{' || $str === '{')
31
			{
32
				$tokens[] = ['literal', '{'];
33
			}
34
			elseif ($str[0] === '{')
35
			{
36
				$tokens[] = ['expression', substr($str, 1, -1)];
37
			}
38
			else
39
			{
40
				$tokens[] = ['literal', str_replace('}}', '}', $str)];
41
			}
42
		}
43
44
		return $tokens;
45
	}
46
47
	/**
48
	* Replace the value of an attribute via the provided callback
49
	*
50
	* The callback will receive an array containing the type and value of each token in the AVT.
51
	* Its return value should use the same format
52
	*
53
	* @param  DOMAttr  $attribute
54
	* @param  callable $callback
55
	* @return void
56
	*/
57
	public static function replace(DOMAttr $attribute, callable $callback)
58
	{
59
		$tokens = self::parse($attribute->value);
60
		foreach ($tokens as $k => $token)
61
		{
62
			$tokens[$k] = $callback($token);
63
		}
64
65
		$attribute->value = htmlspecialchars(self::serialize($tokens), ENT_NOQUOTES, 'UTF-8');
66
	}
67
68
	/**
69
	* Serialize an array of AVT tokens back into an attribute value
70
	*
71
	* @param  array  $tokens
72
	* @return string
73
	*/
74
	public static function serialize(array $tokens)
75
	{
76
		$attrValue = '';
77
		foreach ($tokens as $token)
78
		{
79
			if ($token[0] === 'literal')
80
			{
81
				$attrValue .= preg_replace('([{}])', '$0$0', $token[1]);
82
			}
83
			elseif ($token[0] === 'expression')
84
			{
85
				$attrValue .= '{' . $token[1] . '}';
86
			}
87
			else
88
			{
89
				throw new RuntimeException('Unknown token type');
90
			}
91
		}
92
93
		return $attrValue;
94
	}
95
96
	/**
97
	* Transform given attribute value template into an XSL fragment
98
	*
99
	* @param  string $attrValue
100
	* @return string
101
	*/
102
	public static function toXSL($attrValue)
103
	{
104
		$xsl = '';
105
		foreach (self::parse($attrValue) as list($type, $content))
106
		{
107
			if ($type === 'expression')
108
			{
109
				$xsl .= '<xsl:value-of select="' . htmlspecialchars($content, ENT_COMPAT, 'UTF-8') . '"/>';
110
			}
111
			elseif (trim($content) !== $content)
112
			{
113
				$xsl .= '<xsl:text>' . htmlspecialchars($content, ENT_NOQUOTES, 'UTF-8') . '</xsl:text>';
114
			}
115
			else
116
			{
117
				$xsl .= htmlspecialchars($content, ENT_NOQUOTES, 'UTF-8');
118
			}
119
		}
120
121
		return $xsl;
122
	}
123
}