]> git.mxchange.org Git - friendica.git/blob - library/HTMLPurifier/Generator.php
more friend suggestions
[friendica.git] / library / HTMLPurifier / Generator.php
1 <?php
2
3 /**
4  * Generates HTML from tokens.
5  * @todo Refactor interface so that configuration/context is determined
6  *       upon instantiation, no need for messy generateFromTokens() calls
7  * @todo Make some of the more internal functions protected, and have
8  *       unit tests work around that
9  */
10 class HTMLPurifier_Generator
11 {
12
13     /**
14      * Whether or not generator should produce XML output
15      */
16     private $_xhtml = true;
17
18     /**
19      * :HACK: Whether or not generator should comment the insides of <script> tags
20      */
21     private $_scriptFix = false;
22
23     /**
24      * Cache of HTMLDefinition during HTML output to determine whether or
25      * not attributes should be minimized.
26      */
27     private $_def;
28
29     /**
30      * Cache of %Output.SortAttr
31      */
32     private $_sortAttr;
33
34     /**
35      * Cache of %Output.FlashCompat
36      */
37     private $_flashCompat;
38
39     /**
40      * Stack for keeping track of object information when outputting IE
41      * compatibility code.
42      */
43     private $_flashStack = array();
44
45     /**
46      * Configuration for the generator
47      */
48     protected $config;
49
50     /**
51      * @param $config Instance of HTMLPurifier_Config
52      * @param $context Instance of HTMLPurifier_Context
53      */
54     public function __construct($config, $context) {
55         $this->config = $config;
56         $this->_scriptFix = $config->get('Output.CommentScriptContents');
57         $this->_sortAttr = $config->get('Output.SortAttr');
58         $this->_flashCompat = $config->get('Output.FlashCompat');
59         $this->_def = $config->getHTMLDefinition();
60         $this->_xhtml = $this->_def->doctype->xml;
61     }
62
63     /**
64      * Generates HTML from an array of tokens.
65      * @param $tokens Array of HTMLPurifier_Token
66      * @param $config HTMLPurifier_Config object
67      * @return Generated HTML
68      */
69     public function generateFromTokens($tokens) {
70         if (!$tokens) return '';
71
72         // Basic algorithm
73         $html = '';
74         for ($i = 0, $size = count($tokens); $i < $size; $i++) {
75             if ($this->_scriptFix && $tokens[$i]->name === 'script'
76                 && $i + 2 < $size && $tokens[$i+2] instanceof HTMLPurifier_Token_End) {
77                 // script special case
78                 // the contents of the script block must be ONE token
79                 // for this to work.
80                 $html .= $this->generateFromToken($tokens[$i++]);
81                 $html .= $this->generateScriptFromToken($tokens[$i++]);
82             }
83             $html .= $this->generateFromToken($tokens[$i]);
84         }
85
86         // Tidy cleanup
87         if (extension_loaded('tidy') && $this->config->get('Output.TidyFormat')) {
88             $tidy = new Tidy;
89             $tidy->parseString($html, array(
90                'indent'=> true,
91                'output-xhtml' => $this->_xhtml,
92                'show-body-only' => true,
93                'indent-spaces' => 2,
94                'wrap' => 68,
95             ), 'utf8');
96             $tidy->cleanRepair();
97             $html = (string) $tidy; // explicit cast necessary
98         }
99
100         // Normalize newlines to system defined value
101         $nl = $this->config->get('Output.Newline');
102         if ($nl === null) $nl = PHP_EOL;
103         if ($nl !== "\n") $html = str_replace("\n", $nl, $html);
104         return $html;
105     }
106
107     /**
108      * Generates HTML from a single token.
109      * @param $token HTMLPurifier_Token object.
110      * @return Generated HTML
111      */
112     public function generateFromToken($token) {
113         if (!$token instanceof HTMLPurifier_Token) {
114             trigger_error('Cannot generate HTML from non-HTMLPurifier_Token object', E_USER_WARNING);
115             return '';
116
117         } elseif ($token instanceof HTMLPurifier_Token_Start) {
118             $attr = $this->generateAttributes($token->attr, $token->name);
119             if ($this->_flashCompat) {
120                 if ($token->name == "object") {
121                     $flash = new stdclass();
122                     $flash->attr = $token->attr;
123                     $flash->param = array();
124                     $this->_flashStack[] = $flash;
125                 }
126             }
127             return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>';
128
129         } elseif ($token instanceof HTMLPurifier_Token_End) {
130             $_extra = '';
131             if ($this->_flashCompat) {
132                 if ($token->name == "object" && !empty($this->_flashStack)) {
133                     $flash = array_pop($this->_flashStack);
134                     $compat_token = new HTMLPurifier_Token_Empty("embed");
135                     foreach ($flash->attr as $name => $val) {
136                         if ($name == "classid") continue;
137                         if ($name == "type") continue;
138                         if ($name == "data") $name = "src";
139                         $compat_token->attr[$name] = $val;
140                     }
141                     foreach ($flash->param as $name => $val) {
142                         if ($name == "movie") $name = "src";
143                         $compat_token->attr[$name] = $val;
144                     }
145                     $_extra = "<!--[if IE]>".$this->generateFromToken($compat_token)."<![endif]-->";
146                 }
147             }
148             return $_extra . '</' . $token->name . '>';
149
150         } elseif ($token instanceof HTMLPurifier_Token_Empty) {
151             if ($this->_flashCompat && $token->name == "param" && !empty($this->_flashStack)) {
152                 $this->_flashStack[count($this->_flashStack)-1]->param[$token->attr['name']] = $token->attr['value'];
153             }
154             $attr = $this->generateAttributes($token->attr, $token->name);
155              return '<' . $token->name . ($attr ? ' ' : '') . $attr .
156                 ( $this->_xhtml ? ' /': '' ) // <br /> v. <br>
157                 . '>';
158
159         } elseif ($token instanceof HTMLPurifier_Token_Text) {
160             return $this->escape($token->data, ENT_NOQUOTES);
161
162         } elseif ($token instanceof HTMLPurifier_Token_Comment) {
163             return '<!--' . $token->data . '-->';
164         } else {
165             return '';
166
167         }
168     }
169
170     /**
171      * Special case processor for the contents of script tags
172      * @warning This runs into problems if there's already a literal
173      *          --> somewhere inside the script contents.
174      */
175     public function generateScriptFromToken($token) {
176         if (!$token instanceof HTMLPurifier_Token_Text) return $this->generateFromToken($token);
177         // Thanks <http://lachy.id.au/log/2005/05/script-comments>
178         $data = preg_replace('#//\s*$#', '', $token->data);
179         return '<!--//--><![CDATA[//><!--' . "\n" . trim($data) . "\n" . '//--><!]]>';
180     }
181
182     /**
183      * Generates attribute declarations from attribute array.
184      * @note This does not include the leading or trailing space.
185      * @param $assoc_array_of_attributes Attribute array
186      * @param $element Name of element attributes are for, used to check
187      *        attribute minimization.
188      * @return Generate HTML fragment for insertion.
189      */
190     public function generateAttributes($assoc_array_of_attributes, $element = false) {
191         $html = '';
192         if ($this->_sortAttr) ksort($assoc_array_of_attributes);
193         foreach ($assoc_array_of_attributes as $key => $value) {
194             if (!$this->_xhtml) {
195                 // Remove namespaced attributes
196                 if (strpos($key, ':') !== false) continue;
197                 // Check if we should minimize the attribute: val="val" -> val
198                 if ($element && !empty($this->_def->info[$element]->attr[$key]->minimized)) {
199                     $html .= $key . ' ';
200                     continue;
201                 }
202             }
203             $html .= $key.'="'.$this->escape($value).'" ';
204         }
205         return rtrim($html);
206     }
207
208     /**
209      * Escapes raw text data.
210      * @todo This really ought to be protected, but until we have a facility
211      *       for properly generating HTML here w/o using tokens, it stays
212      *       public.
213      * @param $string String data to escape for HTML.
214      * @param $quote Quoting style, like htmlspecialchars. ENT_NOQUOTES is
215      *               permissible for non-attribute output.
216      * @return String escaped data.
217      */
218     public function escape($string, $quote = ENT_COMPAT) {
219         return htmlspecialchars($string, $quote, 'UTF-8');
220     }
221
222 }
223
224 // vim: et sw=4 sts=4