1: | <?php
|
2: |
|
3: | |
4: | |
5: | |
6: | |
7: | |
8: | |
9: |
|
10: | class HTMLPurifier_Generator
|
11: | {
|
12: |
|
13: | |
14: | |
15: | |
16: |
|
17: | private $_xhtml = true;
|
18: |
|
19: | |
20: | |
21: | |
22: |
|
23: | private $_scriptFix = false;
|
24: |
|
25: | |
26: | |
27: | |
28: | |
29: |
|
30: | private $_def;
|
31: |
|
32: | |
33: | |
34: | |
35: |
|
36: | private $_sortAttr;
|
37: |
|
38: | |
39: | |
40: | |
41: |
|
42: | private $_flashCompat;
|
43: |
|
44: | |
45: | |
46: | |
47: |
|
48: | private $_innerHTMLFix;
|
49: |
|
50: | |
51: | |
52: | |
53: | |
54: |
|
55: | private $_flashStack = array();
|
56: |
|
57: | |
58: | |
59: | |
60: |
|
61: | protected $config;
|
62: |
|
63: | |
64: | |
65: | |
66: |
|
67: | public function __construct($config, $context)
|
68: | {
|
69: | $this->config = $config;
|
70: | $this->_scriptFix = $config->get('Output.CommentScriptContents');
|
71: | $this->_innerHTMLFix = $config->get('Output.FixInnerHTML');
|
72: | $this->_sortAttr = $config->get('Output.SortAttr');
|
73: | $this->_flashCompat = $config->get('Output.FlashCompat');
|
74: | $this->_def = $config->getHTMLDefinition();
|
75: | $this->_xhtml = $this->_def->doctype->xml;
|
76: | }
|
77: |
|
78: | |
79: | |
80: | |
81: | |
82: |
|
83: | public function generateFromTokens($tokens)
|
84: | {
|
85: | if (!$tokens) {
|
86: | return '';
|
87: | }
|
88: |
|
89: |
|
90: | $html = '';
|
91: | for ($i = 0, $size = count($tokens); $i < $size; $i++) {
|
92: | if ($this->_scriptFix && $tokens[$i]->name === 'script'
|
93: | && $i + 2 < $size && $tokens[$i+2] instanceof HTMLPurifier_Token_End) {
|
94: |
|
95: |
|
96: |
|
97: | $html .= $this->generateFromToken($tokens[$i++]);
|
98: | $html .= $this->generateScriptFromToken($tokens[$i++]);
|
99: | }
|
100: | $html .= $this->generateFromToken($tokens[$i]);
|
101: | }
|
102: |
|
103: |
|
104: | if (extension_loaded('tidy') && $this->config->get('Output.TidyFormat')) {
|
105: | $tidy = new Tidy;
|
106: | $tidy->parseString(
|
107: | $html,
|
108: | array(
|
109: | 'indent'=> true,
|
110: | 'output-xhtml' => $this->_xhtml,
|
111: | 'show-body-only' => true,
|
112: | 'indent-spaces' => 2,
|
113: | 'wrap' => 68,
|
114: | ),
|
115: | 'utf8'
|
116: | );
|
117: | $tidy->cleanRepair();
|
118: | $html = (string) $tidy;
|
119: | }
|
120: |
|
121: |
|
122: | if ($this->config->get('Core.NormalizeNewlines')) {
|
123: | $nl = $this->config->get('Output.Newline');
|
124: | if ($nl === null) {
|
125: | $nl = PHP_EOL;
|
126: | }
|
127: | if ($nl !== "\n") {
|
128: | $html = str_replace("\n", $nl, $html);
|
129: | }
|
130: | }
|
131: | return $html;
|
132: | }
|
133: |
|
134: | |
135: | |
136: | |
137: | |
138: |
|
139: | public function generateFromToken($token)
|
140: | {
|
141: | if (!$token instanceof HTMLPurifier_Token) {
|
142: | trigger_error('Cannot generate HTML from non-HTMLPurifier_Token object', E_USER_WARNING);
|
143: | return '';
|
144: |
|
145: | } elseif ($token instanceof HTMLPurifier_Token_Start) {
|
146: | $attr = $this->generateAttributes($token->attr, $token->name);
|
147: | if ($this->_flashCompat) {
|
148: | if ($token->name == "object") {
|
149: | $flash = new stdClass();
|
150: | $flash->attr = $token->attr;
|
151: | $flash->param = array();
|
152: | $this->_flashStack[] = $flash;
|
153: | }
|
154: | }
|
155: | return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>';
|
156: |
|
157: | } elseif ($token instanceof HTMLPurifier_Token_End) {
|
158: | $_extra = '';
|
159: | if ($this->_flashCompat) {
|
160: | if ($token->name == "object" && !empty($this->_flashStack)) {
|
161: |
|
162: | }
|
163: | }
|
164: | return $_extra . '</' . $token->name . '>';
|
165: |
|
166: | } elseif ($token instanceof HTMLPurifier_Token_Empty) {
|
167: | if ($this->_flashCompat && $token->name == "param" && !empty($this->_flashStack)) {
|
168: | $this->_flashStack[count($this->_flashStack)-1]->param[$token->attr['name']] = $token->attr['value'];
|
169: | }
|
170: | $attr = $this->generateAttributes($token->attr, $token->name);
|
171: | return '<' . $token->name . ($attr ? ' ' : '') . $attr .
|
172: | ( $this->_xhtml ? ' /': '' )
|
173: | . '>';
|
174: |
|
175: | } elseif ($token instanceof HTMLPurifier_Token_Text) {
|
176: | return $this->escape($token->data, ENT_NOQUOTES);
|
177: |
|
178: | } elseif ($token instanceof HTMLPurifier_Token_Comment) {
|
179: | return '<!--' . $token->data . '-->';
|
180: | } else {
|
181: | return '';
|
182: |
|
183: | }
|
184: | }
|
185: |
|
186: | |
187: | |
188: | |
189: | |
190: | |
191: | |
192: |
|
193: | public function generateScriptFromToken($token)
|
194: | {
|
195: | if (!$token instanceof HTMLPurifier_Token_Text) {
|
196: | return $this->generateFromToken($token);
|
197: | }
|
198: |
|
199: | $data = preg_replace('#//\s*$#', '', $token->data);
|
200: | return '<!--//--><![CDATA[//><!--' . "\n" . trim($data) . "\n" . '//--><!]]>';
|
201: | }
|
202: |
|
203: | |
204: | |
205: | |
206: | |
207: | |
208: | |
209: | |
210: |
|
211: | public function generateAttributes($assoc_array_of_attributes, $element = '')
|
212: | {
|
213: | $html = '';
|
214: | if ($this->_sortAttr) {
|
215: | ksort($assoc_array_of_attributes);
|
216: | }
|
217: | foreach ($assoc_array_of_attributes as $key => $value) {
|
218: | if (!$this->_xhtml) {
|
219: |
|
220: | if (strpos($key, ':') !== false) {
|
221: | continue;
|
222: | }
|
223: |
|
224: | if ($element && !empty($this->_def->info[$element]->attr[$key]->minimized)) {
|
225: | $html .= $key . ' ';
|
226: | continue;
|
227: | }
|
228: | }
|
229: |
|
230: |
|
231: |
|
232: |
|
233: |
|
234: |
|
235: |
|
236: |
|
237: |
|
238: |
|
239: |
|
240: |
|
241: |
|
242: |
|
243: |
|
244: |
|
245: |
|
246: |
|
247: |
|
248: |
|
249: |
|
250: | if ($this->_innerHTMLFix) {
|
251: | if (strpos($value, '`') !== false) {
|
252: |
|
253: |
|
254: | if (strcspn($value, '"\' <>') === strlen($value)) {
|
255: |
|
256: | $value .= ' ';
|
257: | }
|
258: | }
|
259: | }
|
260: | $html .= $key.'="'.$this->escape($value).'" ';
|
261: | }
|
262: | return rtrim($html);
|
263: | }
|
264: |
|
265: | |
266: | |
267: | |
268: | |
269: | |
270: | |
271: | |
272: | |
273: | |
274: |
|
275: | public function escape($string, $quote = null)
|
276: | {
|
277: |
|
278: |
|
279: | if ($quote === null) {
|
280: | $quote = ENT_COMPAT;
|
281: | }
|
282: | return htmlspecialchars($string, $quote, 'UTF-8');
|
283: | }
|
284: | }
|
285: |
|
286: |
|
287: | |