[ Index ] |
PHP Cross Reference of phpBB-3.3.14-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * @package s9e\TextFormatter 5 * @copyright Copyright (c) 2010-2022 The s9e authors 6 * @license http://www.opensource.org/licenses/mit-license.php The MIT License 7 */ 8 namespace s9e\TextFormatter\Plugins\HTMLElements; 9 10 use InvalidArgumentException; 11 use RuntimeException; 12 use s9e\TextFormatter\Configurator\Helpers\RegexpBuilder; 13 use s9e\TextFormatter\Configurator\Items\Tag; 14 use s9e\TextFormatter\Configurator\Items\UnsafeTemplate; 15 use s9e\TextFormatter\Configurator\JavaScript\Dictionary; 16 use s9e\TextFormatter\Configurator\Validators\AttributeName; 17 use s9e\TextFormatter\Configurator\Validators\TagName; 18 use s9e\TextFormatter\Plugins\ConfiguratorBase; 19 20 class Configurator extends ConfiguratorBase 21 { 22 /** 23 * @var array 2D array using HTML element names as keys, each value being an associative array 24 * using HTML attribute names as keys and their alias as values. A special empty entry 25 * is used to store the HTML element's alias 26 */ 27 protected $aliases = []; 28 29 /** 30 * @var array Default filter of a few known attributes 31 * 32 * It doesn't make much sense to try to declare every known HTML attribute here. Validation is 33 * not the purpose of this plugin. It does make sense however to declare URL attributes as such, 34 * so that they are subject to our constraints (disallowed hosts, etc...) 35 * 36 * @see scripts/patchHTMLElementConfigurator.php 37 */ 38 protected $attributeFilters = [ 39 'action' => '#url', 40 'cite' => '#url', 41 'data' => '#url', 42 'formaction' => '#url', 43 'href' => '#url', 44 'icon' => '#url', 45 'itemtype' => '#url', 46 'longdesc' => '#url', 47 'manifest' => '#url', 48 'ping' => '#url', 49 'poster' => '#url', 50 'src' => '#url' 51 ]; 52 53 /** 54 * @var array Hash of allowed HTML elements. Element names are lowercased and used as keys for 55 * this array 56 */ 57 protected $elements = []; 58 59 /** 60 * @var string Namespace prefix of the tags produced by this plugin's parser 61 */ 62 protected $prefix = 'html'; 63 64 /** 65 * {@inheritdoc} 66 */ 67 protected $quickMatch = '<'; 68 69 /** 70 * @var array Blacklist of elements that are considered unsafe 71 */ 72 protected $unsafeElements = [ 73 'base', 74 'embed', 75 'frame', 76 'iframe', 77 'meta', 78 'object', 79 'script' 80 ]; 81 82 /** 83 * @var array Blacklist of attributes that are considered unsafe, in addition of any attribute 84 * whose name starts with "on" such as "onmouseover" 85 */ 86 protected $unsafeAttributes = [ 87 'style', 88 'target' 89 ]; 90 91 /** 92 * Alias the HTML attribute of given HTML element to a given attribute name 93 * 94 * NOTE: will *not* create the target attribute 95 * 96 * @param string $elName Name of the HTML element 97 * @param string $attrName Name of the HTML attribute 98 * @param string $alias Alias 99 * @return void 100 */ 101 public function aliasAttribute($elName, $attrName, $alias) 102 { 103 $elName = $this->normalizeElementName($elName); 104 $attrName = $this->normalizeAttributeName($attrName); 105 106 $this->aliases[$elName][$attrName] = AttributeName::normalize($alias); 107 } 108 109 /** 110 * Alias an HTML element to a given tag name 111 * 112 * NOTE: will *not* create the target tag 113 * 114 * @param string $elName Name of the HTML element 115 * @param string $tagName Name of the tag 116 * @return void 117 */ 118 public function aliasElement($elName, $tagName) 119 { 120 $elName = $this->normalizeElementName($elName); 121 122 $this->aliases[$elName][''] = TagName::normalize($tagName); 123 } 124 125 /** 126 * Allow an HTML element to be used 127 * 128 * @param string $elName Name of the element 129 * @return Tag Tag that represents this element 130 */ 131 public function allowElement($elName) 132 { 133 return $this->allowElementWithSafety($elName, false); 134 } 135 136 /** 137 * Allow an unsafe HTML element to be used 138 * 139 * @param string $elName Name of the element 140 * @return Tag Tag that represents this element 141 */ 142 public function allowUnsafeElement($elName) 143 { 144 return $this->allowElementWithSafety($elName, true); 145 } 146 147 /** 148 * Allow a (potentially unsafe) HTML element to be used 149 * 150 * @param string $elName Name of the element 151 * @param bool $allowUnsafe Whether to allow unsafe elements 152 * @return Tag Tag that represents this element 153 */ 154 protected function allowElementWithSafety($elName, $allowUnsafe) 155 { 156 $elName = $this->normalizeElementName($elName); 157 $tagName = $this->prefix . ':' . $elName; 158 159 if (!$allowUnsafe && in_array($elName, $this->unsafeElements)) 160 { 161 throw new RuntimeException("'" . $elName . "' elements are unsafe and are disabled by default. Please use " . __CLASS__ . '::allowUnsafeElement() to bypass this security measure'); 162 } 163 164 // Retrieve or create the tag 165 $tag = ($this->configurator->tags->exists($tagName)) 166 ? $this->configurator->tags->get($tagName) 167 : $this->configurator->tags->add($tagName); 168 169 // Rebuild this tag's template 170 $this->rebuildTemplate($tag, $elName, $allowUnsafe); 171 172 // Record the element name 173 $this->elements[$elName] = 1; 174 175 return $tag; 176 } 177 178 /** 179 * Allow an attribute to be used in an HTML element 180 * 181 * @param string $elName Name of the element 182 * @param string $attrName Name of the attribute 183 * @return \s9e\Configurator\Items\Attribute 184 */ 185 public function allowAttribute($elName, $attrName) 186 { 187 return $this->allowAttributeWithSafety($elName, $attrName, false); 188 } 189 190 /** 191 * Allow an unsafe attribute to be used in an HTML element 192 * 193 * @param string $elName Name of the element 194 * @param string $attrName Name of the attribute 195 * @return \s9e\Configurator\Items\Attribute 196 */ 197 public function allowUnsafeAttribute($elName, $attrName) 198 { 199 return $this->allowAttributeWithSafety($elName, $attrName, true); 200 } 201 202 /** 203 * Allow a (potentially unsafe) attribute to be used in an HTML element 204 * 205 * @param string $elName Name of the element 206 * @param string $attrName Name of the attribute 207 * @param bool $allowUnsafe 208 * @return \s9e\Configurator\Items\Attribute 209 */ 210 protected function allowAttributeWithSafety($elName, $attrName, $allowUnsafe) 211 { 212 $elName = $this->normalizeElementName($elName); 213 $attrName = $this->normalizeAttributeName($attrName); 214 $tagName = $this->prefix . ':' . $elName; 215 216 if (!isset($this->elements[$elName])) 217 { 218 throw new RuntimeException("Element '" . $elName . "' has not been allowed"); 219 } 220 221 if (!$allowUnsafe) 222 { 223 if (substr($attrName, 0, 2) === 'on' 224 || in_array($attrName, $this->unsafeAttributes)) 225 { 226 throw new RuntimeException("'" . $attrName . "' attributes are unsafe and are disabled by default. Please use " . __CLASS__ . '::allowUnsafeAttribute() to bypass this security measure'); 227 } 228 } 229 230 $tag = $this->configurator->tags->get($tagName); 231 if (!isset($tag->attributes[$attrName])) 232 { 233 $attribute = $tag->attributes->add($attrName); 234 $attribute->required = false; 235 236 if (isset($this->attributeFilters[$attrName])) 237 { 238 $filterName = $this->attributeFilters[$attrName]; 239 $filter = $this->configurator->attributeFilters->get($filterName); 240 241 $attribute->filterChain->append($filter); 242 } 243 } 244 245 // Rebuild this tag's template 246 $this->rebuildTemplate($tag, $elName, $allowUnsafe); 247 248 return $tag->attributes[$attrName]; 249 } 250 251 /** 252 * Validate and normalize an element name 253 * 254 * Accepts any name that would be valid, regardless of whether this element exists in HTML5. 255 * Might be slightly off as the HTML5 specs don't seem to require it to start with a letter but 256 * our implementation does. 257 * 258 * @link http://dev.w3.org/html5/spec/syntax.html#syntax-tag-name 259 * 260 * @param string $elName Original element name 261 * @return string Normalized element name, in lowercase 262 */ 263 protected function normalizeElementName($elName) 264 { 265 if (!preg_match('#^[a-z][a-z0-9]*$#Di', $elName)) 266 { 267 throw new InvalidArgumentException("Invalid element name '" . $elName . "'"); 268 } 269 270 return strtolower($elName); 271 } 272 273 /** 274 * Validate and normalize an attribute name 275 * 276 * More restrictive than the specs but allows all HTML5 attributes and more. 277 * 278 * @param string $attrName Original attribute name 279 * @return string Normalized attribute name, in lowercase 280 */ 281 protected function normalizeAttributeName($attrName) 282 { 283 if (!preg_match('#^[a-z][-\\w]*$#Di', $attrName)) 284 { 285 throw new InvalidArgumentException("Invalid attribute name '" . $attrName . "'"); 286 } 287 288 return strtolower($attrName); 289 } 290 291 /** 292 * Rebuild a tag's template 293 * 294 * @param Tag $tag Source tag 295 * @param string $elName Name of the HTML element created by the template 296 * @param bool $allowUnsafe Whether to allow unsafe markup 297 * @return void 298 */ 299 protected function rebuildTemplate(Tag $tag, $elName, $allowUnsafe) 300 { 301 $template = '<' . $elName . '>'; 302 foreach ($tag->attributes as $attrName => $attribute) 303 { 304 $template .= '<xsl:copy-of select="@' . $attrName . '"/>'; 305 } 306 $template .= '<xsl:apply-templates/></' . $elName . '>'; 307 308 if ($allowUnsafe) 309 { 310 $template = new UnsafeTemplate($template); 311 } 312 313 $tag->setTemplate($template); 314 } 315 316 /** 317 * Generate this plugin's config 318 * 319 * @return array|null 320 */ 321 public function asConfig() 322 { 323 if (empty($this->elements) && empty($this->aliases)) 324 { 325 return; 326 } 327 328 /** 329 * Regexp used to match an attributes definition (name + value if applicable) 330 * 331 * @link http://dev.w3.org/html5/spec/syntax.html#attributes-0 332 */ 333 $attrRegexp = '[a-z][-a-z0-9]*(?>\\s*=\\s*(?>"[^"]*"|\'[^\']*\'|[^\\s"\'=<>`]+))?'; 334 $tagRegexp = RegexpBuilder::fromList(array_merge( 335 array_keys($this->aliases), 336 array_keys($this->elements) 337 )); 338 339 $endTagRegexp = '/(' . $tagRegexp . ')'; 340 $startTagRegexp = '(' . $tagRegexp . ')((?>\\s+' . $attrRegexp . ')*+)\\s*/?'; 341 342 $regexp = '#<(?>' . $endTagRegexp . '|' . $startTagRegexp . ')\\s*>#i'; 343 344 $config = [ 345 'quickMatch' => $this->quickMatch, 346 'prefix' => $this->prefix, 347 'regexp' => $regexp 348 ]; 349 350 if (!empty($this->aliases)) 351 { 352 // Preserve the aliases array's keys in JavaScript 353 $config['aliases'] = new Dictionary; 354 foreach ($this->aliases as $elName => $aliases) 355 { 356 $config['aliases'][$elName] = new Dictionary($aliases); 357 } 358 } 359 360 return $config; 361 } 362 363 /** 364 * {@inheritdoc} 365 */ 366 public function getJSHints() 367 { 368 return ['HTMLELEMENTS_HAS_ALIASES' => (int) !empty($this->aliases)]; 369 } 370 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Nov 25 19:05:08 2024 | Cross-referenced by PHPXref 0.7.1 |