Completed
Push — master ( 571a82...7b3023 )
by Josh
03:40
created

XSLT   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 187
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 21
lcom 1
cbo 1
dl 0
loc 187
ccs 49
cts 49
cp 1
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 2
A __sleep() 0 12 2
A __wakeup() 0 8 2
A setParameter() 0 22 5
A renderRichText() 0 30 3
A load() 0 10 2
A normalizeAttribute() 0 9 2
A normalizeAttributes() 0 4 1
A normalizeElement() 0 9 2
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\Renderers;
9
10
use XSLTProcessor;
11
use s9e\TextFormatter\Renderer;
12
13
class XSLT extends Renderer
14
{
15
	/**
16
	* @var XSLTProcessor The lazy-loaded XSLTProcessor instance used by this renderer
17
	*/
18
	protected $proc;
19
20
	/**
21
	* @var bool Whether parameters need to be reloaded
22
	*/
23
	protected $reloadParams = false;
24
25
	/**
26
	* @var string The stylesheet used by this renderer
27
	*/
28
	protected $stylesheet;
29
30
	/**
31
	* Constructor
32
	*
33
	* @param  string $stylesheet The stylesheet used to render intermediate representations
34
	*/
35 29
	public function __construct($stylesheet)
36
	{
37 29
		$this->stylesheet = $stylesheet;
38
39
		// Capture the parameters' values from the stylesheet
40 29
		preg_match_all('#<xsl:param name="([^"]+)"(?>/>|>([^<]+))#', $stylesheet, $matches);
41 29
		foreach ($matches[1] as $k => $paramName)
42
		{
43 8
			$this->params[$paramName] = htmlspecialchars_decode($matches[2][$k]);
44
		}
45
	}
46
47
	/**
48
	* Serializer
49
	*
50
	* @return string[] List of properties to serialize
51
	*/
52 4
	public function __sleep()
53
	{
54 4
		$props = get_object_vars($this);
55 4
		unset($props['proc']);
56
57 4
		if (empty($props['reloadParams']))
58
		{
59 3
			unset($props['reloadParams']);
60
		}
61
62 4
		return array_keys($props);
63
	}
64
65
	/**
66
	* Unserialize helper
67
	*
68
	* Will reload parameters if they were changed between generation and serialization
69
	*
70
	* @return void
71
	*/
72 2
	public function __wakeup()
73
	{
74 2
		if (!empty($this->reloadParams))
75
		{
76 1
			$this->setParameters($this->params);
77 1
			$this->reloadParams = false;
78
		}
79
	}
80
81
	/**
82
	* {@inheritdoc}
83
	*/
84 6
	public function setParameter($paramName, $paramValue)
85
	{
86
		/**
87
		* @link https://bugs.php.net/64137
88
		*/
89 6
		if (strpos($paramValue, '"') !== false && strpos($paramValue, "'") !== false)
90
		{
91 1
			$paramValue = str_replace('"', "\xEF\xBC\x82", $paramValue);
92
		}
93
		else
94
		{
95 5
			$paramValue = (string) $paramValue;
96
		}
97
98 6
		if (!isset($this->params[$paramName]) || $this->params[$paramName] !== $paramValue)
99
		{
100 6
			$this->load();
101 6
			$this->proc->setParameter('', $paramName, $paramValue);
102 6
			$this->params[$paramName] = $paramValue;
103 6
			$this->reloadParams = true;
104
		}
105
	}
106
107
	/**
108
	* {@inheritdoc}
109
	*/
110 16
	protected function renderRichText($xml)
111
	{
112
		// Load the intermediate representation
113 16
		$dom = $this->loadXML($xml);
114
115
		// Load the stylesheet
116 12
		$this->load();
117
118
		// Perform the transformation and cast it as a string because it may return NULL if the
119
		// transformation didn't output anything
120 12
		$output = (string) $this->proc->transformToXml($dom);
121
122
		// XSLTProcessor does not correctly identify <embed> as a void element. We fix it by
123
		// removing </embed> end tags
124 12
		$output = str_replace('</embed>', '', $output);
125
126
		// Remove the \n that XSL adds at the end of the output, if applicable
127 12
		if (substr($output, -1) === "\n")
128
		{
129 12
			$output = substr($output, 0, -1);
130
		}
131
132
		// Force HTML attributes to use double quotes to be consistent with the PHP renderer
133 12
		if (strpos($output, "='") !== false)
134
		{
135 2
			$output = $this->normalizeAttributes($output);
136
		}
137
138 12
		return $output;
139
	}
140
141
	/**
142
	* Create an XSLTProcessor and load the stylesheet
143
	*
144
	* @return void
145
	*/
146 15
	protected function load()
147
	{
148 15
		if (!isset($this->proc))
149
		{
150 15
			$xsl = $this->loadXML($this->stylesheet);
151
152 15
			$this->proc = new XSLTProcessor;
153 15
			$this->proc->importStylesheet($xsl);
154
		}
155
	}
156
157
	/**
158
	* Normalize given attribute's value to use double quotes
159
	*
160
	* @param  string[] $m
161
	* @return string
162
	*/
163 2
	protected function normalizeAttribute(array $m)
164
	{
165 2
		if ($m[0][0] === '"')
166
		{
167 2
			return $m[0];
168
		}
169
170 1
		return '"' . str_replace('"', '&quot;', substr($m[0], 1, -1)) . '"';
171
	}
172
173
	/**
174
	* Normalize all attributes in given HTML to use double quotes
175
	*
176
	* @param  string $html
177
	* @return string
178
	*/
179 2
	protected function normalizeAttributes($html)
180
	{
181 2
		return preg_replace_callback('(<\\S++ [^>]++>)', [$this, 'normalizeElement'], $html);
182
	}
183
184
	/**
185
	* Normalize attributes in given element to use double quotes
186
	*
187
	* @param  string[] $m
188
	* @return string
189
	*/
190 2
	protected function normalizeElement(array $m)
191
	{
192 2
		if (strpos($m[0], "='") === false)
193
		{
194 1
			return $m[0];
195
		}
196
197 2
		return preg_replace_callback('((?:"[^"]*"|\'[^\']*\'))S', [$this, 'normalizeAttribute'], $m[0]);
198
	}
199
}