| @@ -1,15 +1,16 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | +use BadMethodCallException; | |
| 4 | +use InvalidArgumentException; | |
| 5 | + | |
| 3 | 6 | /** |
| 4 | 7 | * This class provides functions for converting CSS styles into inline style attributes in your HTML code. |
| 5 | 8 | * |
| 6 | 9 | * For more information, please see the README.md file. |
| 7 | - * | |
| 8 | - * @internal This class is deprecated as of v3.0.0 of emogrifier. Need to update to new CSSInliner class. | |
| 9 | 10 | * |
| 10 | - * @link https://github.com/MyIntervals/emogrifier/blob/v1.2.x/Classes/Emogrifier.php | |
| 11 | - * | |
| 11 | + * @version 1.2.0 | |
| 12 | + * | |
| 12 | 13 | * @author Cameron Brooks |
| 13 | 14 | * @author Jaime Prado |
| 14 | 15 | * @author Oliver Klee <[email protected]> |
| 15 | 16 | * @author Roman Ožana <[email protected]> |
| @@ -14,10 +15,9 @@ | ||
| 14 | 15 | * @author Oliver Klee <[email protected]> |
| 15 | 16 | * @author Roman Ožana <[email protected]> |
| 16 | 17 | * @author Sander Kruger <[email protected]> |
| 17 | 18 | */ |
| 18 | -class Emogrifier | |
| 19 | -{ | |
| 19 | +class Emogrifier { | |
| 20 | 20 | /** |
| 21 | 21 | * @var int |
| 22 | 22 | */ |
| 23 | 23 | const CACHE_KEY_CSS = 0; |
| @@ -104,13 +104,13 @@ | ||
| 104 | 104 | /** |
| 105 | 105 | * @var mixed[] |
| 106 | 106 | */ |
| 107 | 107 | private $caches = [ |
| 108 | - self::CACHE_KEY_CSS => [], | |
| 109 | - self::CACHE_KEY_SELECTOR => [], | |
| 110 | - self::CACHE_KEY_XPATH => [], | |
| 108 | + self::CACHE_KEY_CSS => [], | |
| 109 | + self::CACHE_KEY_SELECTOR => [], | |
| 110 | + self::CACHE_KEY_XPATH => [], | |
| 111 | 111 | self::CACHE_KEY_CSS_DECLARATIONS_BLOCK => [], |
| 112 | - self::CACHE_KEY_COMBINED_STYLES => [], | |
| 112 | + self::CACHE_KEY_COMBINED_STYLES => [], | |
| 113 | 113 | ]; |
| 114 | 114 | |
| 115 | 115 | /** |
| 116 | 116 | * the visited nodes with the XPath paths as array keys |
| @@ -175,20 +175,17 @@ | ||
| 175 | 175 | '/(\\w)\\[(\\w+)\\]/' => '\\1[@\\2]', |
| 176 | 176 | // exact attribute |
| 177 | 177 | '/(\\w)\\[(\\w+)\\=[\'"]?([\\w\\s]+)[\'"]?\\]/' => '\\1[@\\2="\\3"]', |
| 178 | 178 | // element attribute~= |
| 179 | - '/([\\w\\*]+)\\[(\\w+)[\\s]*\\~\\=[\\s]*[\'"]?([\\w\\-_\\/]+)[\'"]?\\]/' | |
| 180 | - => '\\1[contains(concat(" ", @\\2, " "), concat(" ", "\\3", " "))]', | |
| 179 | + '/([\\w\\*]+)\\[(\\w+)[\\s]*\\~\\=[\\s]*[\'"]?([\\w-_\\/]+)[\'"]?\\]/' => '\\1[contains(concat(" ", @\\2, " "), concat(" ", "\\3", " "))]', | |
| 181 | 180 | // element attribute^= |
| 182 | - '/([\\w\\*]+)\\[(\\w+)[\\s]*\\^\\=[\\s]*[\'"]?([\\w\\-_\\/]+)[\'"]?\\]/' => '\\1[starts-with(@\\2, "\\3")]', | |
| 181 | + '/([\\w\\*]+)\\[(\\w+)[\\s]*\\^\\=[\\s]*[\'"]?([\\w-_\\/]+)[\'"]?\\]/' => '\\1[starts-with(@\\2, "\\3")]', | |
| 183 | 182 | // element attribute*= |
| 184 | - '/([\\w\\*]+)\\[(\\w+)[\\s]*\\*\\=[\\s]*[\'"]?([\\w\\-_\\s\\/:;]+)[\'"]?\\]/' => '\\1[contains(@\\2, "\\3")]', | |
| 183 | + '/([\\w\\*]+)\\[(\\w+)[\\s]*\\*\\=[\\s]*[\'"]?([\\w-_\\s\\/:;]+)[\'"]?\\]/' => '\\1[contains(@\\2, "\\3")]', | |
| 185 | 184 | // element attribute$= |
| 186 | - '/([\\w\\*]+)\\[(\\w+)[\\s]*\\$\\=[\\s]*[\'"]?([\\w\\-_\\s\\/]+)[\'"]?\\]/' | |
| 187 | - => '\\1[substring(@\\2, string-length(@\\2) - string-length("\\3") + 1) = "\\3"]', | |
| 185 | + '/([\\w\\*]+)\\[(\\w+)[\\s]*\\$\\=[\\s]*[\'"]?([\\w-_\\s\\/]+)[\'"]?\\]/' => '\\1[substring(@\\2, string-length(@\\2) - string-length("\\3") + 1) = "\\3"]', | |
| 188 | 186 | // element attribute|= |
| 189 | - '/([\\w\\*]+)\\[(\\w+)[\\s]*\\|\\=[\\s]*[\'"]?([\\w\\-_\\s\\/]+)[\'"]?\\]/' | |
| 190 | - => '\\1[@\\2="\\3" or starts-with(@\\2, concat("\\3", "-"))]', | |
| 187 | + '/([\\w\\*]+)\\[(\\w+)[\\s]*\\|\\=[\\s]*[\'"]?([\\w-_\\s\\/]+)[\'"]?\\]/' => '\\1[@\\2="\\3" or starts-with(@\\2, concat("\\3", "-"))]', | |
| 191 | 188 | ]; |
| 192 | 189 | |
| 193 | 190 | /** |
| 194 | 191 | * Determines whether CSS styles that have an equivalent HTML attribute |
| @@ -211,39 +208,44 @@ | ||
| 211 | 208 | 'attribute' => 'bgcolor', |
| 212 | 209 | ], |
| 213 | 210 | 'text-align' => [ |
| 214 | 211 | 'attribute' => 'align', |
| 215 | - 'nodes' => ['p', 'div', 'td'], | |
| 216 | - 'values' => ['left', 'right', 'center', 'justify'], | |
| 212 | + 'nodes' => ['p', 'div', 'td'], | |
| 213 | + 'values' => ['left', 'right', 'center', 'justify'], | |
| 217 | 214 | ], |
| 218 | 215 | 'float' => [ |
| 219 | 216 | 'attribute' => 'align', |
| 220 | - 'nodes' => ['table', 'img'], | |
| 221 | - 'values' => ['left', 'right'], | |
| 217 | + 'nodes' => ['table', 'img'], | |
| 218 | + 'values' => ['left', 'right'], | |
| 222 | 219 | ], |
| 223 | 220 | 'border-spacing' => [ |
| 224 | 221 | 'attribute' => 'cellspacing', |
| 225 | - 'nodes' => ['table'], | |
| 222 | + 'nodes' => ['table'], | |
| 226 | 223 | ], |
| 227 | 224 | ]; |
| 228 | 225 | |
| 229 | 226 | /** |
| 227 | + * Emogrifier will throw Exceptions when it encounters an error instead of silently ignoring them. | |
| 228 | + * | |
| 229 | + * @var bool | |
| 230 | + */ | |
| 231 | + private $debug = false; | |
| 232 | + | |
| 233 | + /** | |
| 230 | 234 | * The constructor. |
| 231 | 235 | * |
| 232 | 236 | * @param string $html the HTML to emogrify, must be UTF-8-encoded |
| 233 | - * @param string $css the CSS to merge, must be UTF-8-encoded | |
| 237 | + * @param string $css the CSS to merge, must be UTF-8-encoded | |
| 234 | 238 | */ |
| 235 | - public function __construct($html = '', $css = '') | |
| 236 | - { | |
| 237 | - $this->setHtml($html); | |
| 238 | - $this->setCss($css); | |
| 239 | + public function __construct( $html = '', $css = '' ) { | |
| 240 | + $this->setHtml( $html ); | |
| 241 | + $this->setCss( $css ); | |
| 239 | 242 | } |
| 240 | 243 | |
| 241 | 244 | /** |
| 242 | 245 | * The destructor. |
| 243 | 246 | */ |
| 244 | - public function __destruct() | |
| 245 | - { | |
| 247 | + public function __destruct() { | |
| 246 | 248 | $this->purgeVisitedNodes(); |
| 247 | 249 | } |
| 248 | 250 | |
| 249 | 251 | /** |
| @@ -252,10 +254,9 @@ | ||
| 252 | 254 | * @param string $html the HTML to emogrify, must be UTF-8-encoded |
| 253 | 255 | * |
| 254 | 256 | * @return void |
| 255 | 257 | */ |
| 256 | - public function setHtml($html) | |
| 257 | - { | |
| 258 | + public function setHtml( $html ) { | |
| 258 | 259 | $this->html = $html; |
| 259 | 260 | } |
| 260 | 261 | |
| 261 | 262 | /** |
| @@ -264,10 +265,9 @@ | ||
| 264 | 265 | * @param string $css the CSS to merge, must be UTF-8-encoded |
| 265 | 266 | * |
| 266 | 267 | * @return void |
| 267 | 268 | */ |
| 268 | - public function setCss($css) | |
| 269 | - { | |
| 269 | + public function setCss( $css ) { | |
| 270 | 270 | $this->css = $css; |
| 271 | 271 | } |
| 272 | 272 | |
| 273 | 273 | /** |
| @@ -277,18 +277,17 @@ | ||
| 277 | 277 | * This method places the CSS inline. |
| 278 | 278 | * |
| 279 | 279 | * @return string |
| 280 | 280 | * |
| 281 | - * @throws \BadMethodCallException | |
| 281 | + * @throws BadMethodCallException | |
| 282 | 282 | */ |
| 283 | - public function emogrify() | |
| 284 | - { | |
| 285 | - if ($this->html === '') { | |
| 286 | - throw new \BadMethodCallException('Please set some HTML first before calling emogrify.', 1390393096); | |
| 283 | + public function emogrify() { | |
| 284 | + if ( $this->html === '' ) { | |
| 285 | + throw new BadMethodCallException( 'Please set some HTML first before calling emogrify.', 1390393096 ); | |
| 287 | 286 | } |
| 288 | 287 | |
| 289 | 288 | $xmlDocument = $this->createXmlDocument(); |
| 290 | - $this->process($xmlDocument); | |
| 289 | + $this->process( $xmlDocument ); | |
| 291 | 290 | |
| 292 | 291 | return $xmlDocument->saveHTML(); |
| 293 | 292 | } |
| 294 | 293 | |
| @@ -299,25 +298,25 @@ | ||
| 299 | 298 | * This method places the CSS inline. |
| 300 | 299 | * |
| 301 | 300 | * @return string |
| 302 | 301 | * |
| 303 | - * @throws \BadMethodCallException | |
| 302 | + * @throws BadMethodCallException | |
| 304 | 303 | */ |
| 305 | - public function emogrifyBodyContent() | |
| 306 | - { | |
| 307 | - if ($this->html === '') { | |
| 308 | - throw new \BadMethodCallException('Please set some HTML first before calling emogrify.', 1390393096); | |
| 304 | + public function emogrifyBodyContent() { | |
| 305 | + if ( $this->html === '' ) { | |
| 306 | + throw new BadMethodCallException( 'Please set some HTML first before calling emogrify.', 1390393096 ); | |
| 309 | 307 | } |
| 310 | 308 | |
| 311 | 309 | $xmlDocument = $this->createXmlDocument(); |
| 312 | - $this->process($xmlDocument); | |
| 310 | + $this->process( $xmlDocument ); | |
| 313 | 311 | |
| 314 | 312 | $innerDocument = new \DOMDocument(); |
| 315 | - foreach ($xmlDocument->documentElement->getElementsByTagName('body')->item(0)->childNodes as $childNode) { | |
| 316 | - $innerDocument->appendChild($innerDocument->importNode($childNode, true)); | |
| 313 | + | |
| 314 | + foreach ( $xmlDocument->documentElement->getElementsByTagName( 'body' )->item( 0 )->childNodes as $childNode ) { | |
| 315 | + $innerDocument->appendChild( $innerDocument->importNode( $childNode, true ) ); | |
| 317 | 316 | } |
| 318 | 317 | |
| 319 | - return html_entity_decode($innerDocument->saveHTML()); | |
| 318 | + return html_entity_decode( $innerDocument->saveHTML() ); | |
| 320 | 319 | } |
| 321 | 320 | |
| 322 | 321 | /** |
| 323 | 322 | * Applies $this->css to $xmlDocument. |
| @@ -327,13 +326,12 @@ | ||
| 327 | 326 | * @param \DOMDocument $xmlDocument |
| 328 | 327 | * |
| 329 | 328 | * @return void |
| 330 | 329 | * |
| 331 | - * @throws \InvalidArgumentException | |
| 330 | + * @throws InvalidArgumentException | |
| 332 | 331 | */ |
| 333 | - protected function process(\DOMDocument $xmlDocument) | |
| 334 | - { | |
| 335 | - $xPath = new \DOMXPath($xmlDocument); | |
| 332 | + protected function process( DOMDocument $xmlDocument ) { | |
| 333 | + $xPath = new \DOMXPath( $xmlDocument ); | |
| 336 | 334 | $this->clearAllCaches(); |
| 337 | 335 | |
| 338 | 336 | // Before be begin processing the CSS file, parse the document and normalize all existing CSS attributes. |
| 339 | 337 | // This changes 'DISPLAY: none' to 'display: none'. |
| @@ -340,18 +338,19 @@ | ||
| 340 | 338 | // We wouldn't have to do this if DOMXPath supported XPath 2.0. |
| 341 | 339 | // Also store a reference of nodes with existing inline styles so we don't overwrite them. |
| 342 | 340 | $this->purgeVisitedNodes(); |
| 343 | 341 | |
| 344 | - set_error_handler([$this, 'handleXpathError'], E_WARNING); | |
| 342 | + set_error_handler( [$this, 'handleXpathError'], E_WARNING ); | |
| 345 | 343 | |
| 346 | - $nodesWithStyleAttributes = $xPath->query('//*[@style]'); | |
| 347 | - if ($nodesWithStyleAttributes !== false) { | |
| 344 | + $nodesWithStyleAttributes = $xPath->query( '//*[@style]' ); | |
| 345 | + | |
| 346 | + if ( $nodesWithStyleAttributes !== false ) { | |
| 348 | 347 | /** @var \DOMElement $node */ |
| 349 | - foreach ($nodesWithStyleAttributes as $node) { | |
| 350 | - if ($this->isInlineStyleAttributesParsingEnabled) { | |
| 351 | - $this->normalizeStyleAttributes($node); | |
| 348 | + foreach ( $nodesWithStyleAttributes as $node ) { | |
| 349 | + if ( $this->isInlineStyleAttributesParsingEnabled ) { | |
| 350 | + $this->normalizeStyleAttributes( $node ); | |
| 352 | 351 | } else { |
| 353 | - $node->removeAttribute('style'); | |
| 352 | + $node->removeAttribute( 'style' ); | |
| 354 | 353 | } |
| 355 | 354 | } |
| 356 | 355 | } |
| 357 | 356 | |
| @@ -358,58 +357,60 @@ | ||
| 358 | 357 | // grab any existing style blocks from the html and append them to the existing CSS |
| 359 | 358 | // (these blocks should be appended so as to have precedence over conflicting styles in the existing CSS) |
| 360 | 359 | $allCss = $this->css; |
| 361 | 360 | |
| 362 | - if ($this->isStyleBlocksParsingEnabled) { | |
| 363 | - $allCss .= $this->getCssFromAllStyleNodes($xPath); | |
| 361 | + if ( $this->isStyleBlocksParsingEnabled ) { | |
| 362 | + $allCss .= $this->getCssFromAllStyleNodes( $xPath ); | |
| 364 | 363 | } |
| 365 | 364 | |
| 366 | - $cssParts = $this->splitCssAndMediaQuery($allCss); | |
| 367 | - $excludedNodes = $this->getNodesToExclude($xPath); | |
| 368 | - $cssRules = $this->parseCssRules($cssParts['css']); | |
| 369 | - foreach ($cssRules as $cssRule) { | |
| 365 | + $cssParts = $this->splitCssAndMediaQuery( $allCss ); | |
| 366 | + $excludedNodes = $this->getNodesToExclude( $xPath ); | |
| 367 | + $cssRules = $this->parseCssRules( $cssParts['css'] ); | |
| 368 | + | |
| 369 | + foreach ( $cssRules as $cssRule ) { | |
| 370 | 370 | // query the body for the xpath selector |
| 371 | - $nodesMatchingCssSelectors = $xPath->query($this->translateCssToXpath($cssRule['selector'])); | |
| 371 | + $nodesMatchingCssSelectors = $xPath->query( $this->translateCssToXpath( $cssRule['selector'] ) ); | |
| 372 | 372 | // ignore invalid selectors |
| 373 | - if ($nodesMatchingCssSelectors === false) { | |
| 373 | + if ( $nodesMatchingCssSelectors === false ) { | |
| 374 | 374 | continue; |
| 375 | 375 | } |
| 376 | 376 | |
| 377 | 377 | /** @var \DOMElement $node */ |
| 378 | - foreach ($nodesMatchingCssSelectors as $node) { | |
| 379 | - if (in_array($node, $excludedNodes, true)) { | |
| 378 | + foreach ( $nodesMatchingCssSelectors as $node ) { | |
| 379 | + if ( in_array( $node, $excludedNodes, true ) ) { | |
| 380 | 380 | continue; |
| 381 | 381 | } |
| 382 | 382 | |
| 383 | 383 | // if it has a style attribute, get it, process it, and append (overwrite) new stuff |
| 384 | - if ($node->hasAttribute('style')) { | |
| 384 | + if ( $node->hasAttribute( 'style' ) ) { | |
| 385 | 385 | // break it up into an associative array |
| 386 | - $oldStyleDeclarations = $this->parseCssDeclarationsBlock($node->getAttribute('style')); | |
| 386 | + $oldStyleDeclarations = $this->parseCssDeclarationsBlock( $node->getAttribute( 'style' ) ); | |
| 387 | 387 | } else { |
| 388 | 388 | $oldStyleDeclarations = []; |
| 389 | 389 | } |
| 390 | - $newStyleDeclarations = $this->parseCssDeclarationsBlock($cssRule['declarationsBlock']); | |
| 391 | - if ($this->shouldMapCssToHtml) { | |
| 392 | - $this->mapCssToHtmlAttributes($newStyleDeclarations, $node); | |
| 390 | + $newStyleDeclarations = $this->parseCssDeclarationsBlock( $cssRule['declarationsBlock'] ); | |
| 391 | + | |
| 392 | + if ( $this->shouldMapCssToHtml ) { | |
| 393 | + $this->mapCssToHtmlAttributes( $newStyleDeclarations, $node ); | |
| 393 | 394 | } |
| 394 | - $node->setAttribute( | |
| 395 | + $node->setAttribute( | |
| 395 | 396 | 'style', |
| 396 | - $this->generateStyleStringFromDeclarationsArrays($oldStyleDeclarations, $newStyleDeclarations) | |
| 397 | - ); | |
| 397 | + $this->generateStyleStringFromDeclarationsArrays( $oldStyleDeclarations, $newStyleDeclarations ) | |
| 398 | + ); | |
| 398 | 399 | } |
| 399 | 400 | } |
| 400 | 401 | |
| 401 | 402 | restore_error_handler(); |
| 402 | 403 | |
| 403 | - if ($this->isInlineStyleAttributesParsingEnabled) { | |
| 404 | + if ( $this->isInlineStyleAttributesParsingEnabled ) { | |
| 404 | 405 | $this->fillStyleAttributesWithMergedStyles(); |
| 405 | 406 | } |
| 406 | 407 | |
| 407 | - if ($this->shouldKeepInvisibleNodes) { | |
| 408 | - $this->removeInvisibleNodes($xPath); | |
| 408 | + if ( $this->shouldKeepInvisibleNodes ) { | |
| 409 | + $this->removeInvisibleNodes( $xPath ); | |
| 409 | 410 | } |
| 410 | 411 | |
| 411 | - $this->copyCssWithMediaToStyleNode($xmlDocument, $xPath, $cssParts['media']); | |
| 412 | + $this->copyCssWithMediaToStyleNode( $xmlDocument, $xPath, $cssParts['media'] ); | |
| 412 | 413 | } |
| 413 | 414 | |
| 414 | 415 | /** |
| 415 | 416 | * Applies $styles to $node. |
| @@ -421,14 +422,13 @@ | ||
| 421 | 422 | * @param \DOMNode $node node to apply styles to |
| 422 | 423 | * |
| 423 | 424 | * @return void |
| 424 | 425 | */ |
| 425 | - private function mapCssToHtmlAttributes(array $styles, \DOMNode $node) | |
| 426 | - { | |
| 427 | - foreach ($styles as $property => $value) { | |
| 426 | + private function mapCssToHtmlAttributes( array $styles, DOMNode $node ) { | |
| 427 | + foreach ( $styles as $property => $value ) { | |
| 428 | 428 | // Strip !important indicator |
| 429 | - $value = trim(str_replace('!important', '', $value)); | |
| 430 | - $this->mapCssToHtmlAttribute($property, $value, $node); | |
| 429 | + $value = trim( str_replace( '!important', '', $value ) ); | |
| 430 | + $this->mapCssToHtmlAttribute( $property, $value, $node ); | |
| 431 | 431 | } |
| 432 | 432 | } |
| 433 | 433 | |
| 434 | 434 | /** |
| @@ -435,18 +435,17 @@ | ||
| 435 | 435 | * Tries to apply the CSS style to $node as an attribute. |
| 436 | 436 | * |
| 437 | 437 | * This method maps a CSS rule to HTML attributes and adds those to the node. |
| 438 | 438 | * |
| 439 | - * @param string $property the name of the CSS property to map | |
| 440 | - * @param string $value the value of the style rule to map | |
| 441 | - * @param \DOMNode $node node to apply styles to | |
| 439 | + * @param string $property the name of the CSS property to map | |
| 440 | + * @param string $value the value of the style rule to map | |
| 441 | + * @param \DOMNode $node node to apply styles to | |
| 442 | 442 | * |
| 443 | 443 | * @return void |
| 444 | 444 | */ |
| 445 | - private function mapCssToHtmlAttribute($property, $value, \DOMNode $node) | |
| 446 | - { | |
| 447 | - if (!$this->mapSimpleCssProperty($property, $value, $node)) { | |
| 448 | - $this->mapComplexCssProperty($property, $value, $node); | |
| 445 | + private function mapCssToHtmlAttribute( $property, $value, DOMNode $node ) { | |
| 446 | + if ( !$this->mapSimpleCssProperty( $property, $value, $node ) ) { | |
| 447 | + $this->mapComplexCssProperty( $property, $value, $node ); | |
| 449 | 448 | } |
| 450 | 449 | } |
| 451 | 450 | |
| 452 | 451 | /** |
| @@ -451,28 +450,28 @@ | ||
| 451 | 450 | |
| 452 | 451 | /** |
| 453 | 452 | * Looks up the CSS property in the mapping table and maps it if it matches the conditions. |
| 454 | 453 | * |
| 455 | - * @param string $property the name of the CSS property to map | |
| 456 | - * @param string $value the value of the style rule to map | |
| 457 | - * @param \DOMNode $node node to apply styles to | |
| 454 | + * @param string $property the name of the CSS property to map | |
| 455 | + * @param string $value the value of the style rule to map | |
| 456 | + * @param \DOMNode $node node to apply styles to | |
| 458 | 457 | * |
| 459 | 458 | * @return bool true if the property cab be mapped using the simple mapping table |
| 460 | 459 | */ |
| 461 | - private function mapSimpleCssProperty($property, $value, \DOMNode $node) | |
| 462 | - { | |
| 463 | - if (!isset($this->cssToHtmlMap[$property])) { | |
| 460 | + private function mapSimpleCssProperty( $property, $value, DOMNode $node ) { | |
| 461 | + if ( !isset( $this->cssToHtmlMap[$property] ) ) { | |
| 464 | 462 | return false; |
| 465 | 463 | } |
| 466 | 464 | |
| 467 | - $mapping = $this->cssToHtmlMap[$property]; | |
| 468 | - $nodesMatch = !isset($mapping['nodes']) || in_array($node->nodeName, $mapping['nodes'], true); | |
| 469 | - $valuesMatch = !isset($mapping['values']) || in_array($value, $mapping['values'], true); | |
| 470 | - if (!$nodesMatch || !$valuesMatch) { | |
| 465 | + $mapping = $this->cssToHtmlMap[$property]; | |
| 466 | + $nodesMatch = !isset( $mapping['nodes'] ) || in_array( $node->nodeName, $mapping['nodes'], true ); | |
| 467 | + $valuesMatch = !isset( $mapping['values'] ) || in_array( $value, $mapping['values'], true ); | |
| 468 | + | |
| 469 | + if ( !$nodesMatch || !$valuesMatch ) { | |
| 471 | 470 | return false; |
| 472 | 471 | } |
| 473 | 472 | |
| 474 | - $node->setAttribute($mapping['attribute'], $value); | |
| 473 | + $node->setAttribute( $mapping['attribute'], $value ); | |
| 475 | 474 | |
| 476 | 475 | return true; |
| 477 | 476 | } |
| 478 | 477 | |
| @@ -478,53 +477,57 @@ | ||
| 478 | 477 | |
| 479 | 478 | /** |
| 480 | 479 | * Maps CSS properties that need special transformation to an HTML attribute. |
| 481 | 480 | * |
| 482 | - * @param string $property the name of the CSS property to map | |
| 483 | - * @param string $value the value of the style rule to map | |
| 484 | - * @param \DOMNode $node node to apply styles to | |
| 481 | + * @param string $property the name of the CSS property to map | |
| 482 | + * @param string $value the value of the style rule to map | |
| 483 | + * @param \DOMNode $node node to apply styles to | |
| 485 | 484 | * |
| 486 | 485 | * @return void |
| 487 | 486 | */ |
| 488 | - private function mapComplexCssProperty($property, $value, \DOMNode $node) | |
| 489 | - { | |
| 490 | - $nodeName = $node->nodeName; | |
| 491 | - $isTable = $nodeName === 'table'; | |
| 492 | - $isImage = $nodeName === 'img'; | |
| 487 | + private function mapComplexCssProperty( $property, $value, DOMNode $node ) { | |
| 488 | + $nodeName = $node->nodeName; | |
| 489 | + $isTable = $nodeName === 'table'; | |
| 490 | + $isImage = $nodeName === 'img'; | |
| 493 | 491 | $isTableOrImage = $isTable || $isImage; |
| 494 | 492 | |
| 495 | - switch ($property) { | |
| 493 | + switch ( $property ) { | |
| 496 | 494 | case 'background': |
| 497 | 495 | // Parse out the color, if any |
| 498 | - $styles = explode(' ', $value); | |
| 499 | - $first = $styles[0]; | |
| 500 | - if (!is_numeric(substr($first, 0, 1)) && substr($first, 0, 3) !== 'url') { | |
| 496 | + $styles = explode( ' ', $value ); | |
| 497 | + $first = $styles[0]; | |
| 498 | + | |
| 499 | + if ( !is_numeric( substr( $first, 0, 1 ) ) && substr( $first, 0, 3 ) !== 'url' ) { | |
| 501 | 500 | // This is not a position or image, assume it's a color |
| 502 | - $node->setAttribute('bgcolor', $first); | |
| 501 | + $node->setAttribute( 'bgcolor', $first ); | |
| 503 | 502 | } |
| 504 | 503 | break; |
| 504 | + | |
| 505 | 505 | case 'width': |
| 506 | 506 | // intentional fall-through |
| 507 | 507 | case 'height': |
| 508 | 508 | // Only parse values in px and %, but not values like "auto". |
| 509 | - if (preg_match('/^\d+(px|%)$/', $value)) { | |
| 509 | + if ( preg_match( '/^\d+(px|%)$/', $value ) ) { | |
| 510 | 510 | // Remove 'px'. This regex only conserves numbers and % |
| 511 | - $number = preg_replace('/[^0-9.%]/', '', $value); | |
| 512 | - $node->setAttribute($property, $number); | |
| 511 | + $number = preg_replace( '/[^0-9.%]/', '', $value ); | |
| 512 | + $node->setAttribute( $property, $number ); | |
| 513 | 513 | } |
| 514 | 514 | break; |
| 515 | + | |
| 515 | 516 | case 'margin': |
| 516 | - if ($isTableOrImage) { | |
| 517 | - $margins = $this->parseCssShorthandValue($value); | |
| 518 | - if ($margins['left'] === 'auto' && $margins['right'] === 'auto') { | |
| 519 | - $node->setAttribute('align', 'center'); | |
| 517 | + if ( $isTableOrImage ) { | |
| 518 | + $margins = $this->parseCssShorthandValue( $value ); | |
| 519 | + | |
| 520 | + if ( $margins['left'] === 'auto' && $margins['right'] === 'auto' ) { | |
| 521 | + $node->setAttribute( 'align', 'center' ); | |
| 520 | 522 | } |
| 521 | 523 | } |
| 522 | 524 | break; |
| 525 | + | |
| 523 | 526 | case 'border': |
| 524 | - if ($isTableOrImage) { | |
| 525 | - if ($value === 'none' || $value === '0') { | |
| 526 | - $node->setAttribute('border', '0'); | |
| 527 | + if ( $isTableOrImage ) { | |
| 528 | + if ( $value === 'none' || $value === '0' ) { | |
| 529 | + $node->setAttribute( 'border', '0' ); | |
| 527 | 530 | } |
| 528 | 531 | } |
| 529 | 532 | break; |
| 530 | 533 | default: |
| @@ -536,21 +539,20 @@ | ||
| 536 | 539 | * |
| 537 | 540 | * @param string $value a string of CSS value with 1, 2, 3 or 4 sizes |
| 538 | 541 | * For example: padding: 0 auto; |
| 539 | 542 | * '0 auto' is split into top: 0, left: auto, bottom: 0, |
| 540 | - * right: auto. | |
| 543 | + * right: auto | |
| 541 | 544 | * |
| 542 | 545 | * @return string[] an array of values for top, right, bottom and left (using these as associative array keys) |
| 543 | 546 | */ |
| 544 | - private function parseCssShorthandValue($value) | |
| 545 | - { | |
| 546 | - $values = preg_split('/\\s+/', $value); | |
| 547 | + private function parseCssShorthandValue( $value ) { | |
| 548 | + $values = preg_split( '/\\s+/', $value ); | |
| 547 | 549 | |
| 548 | - $css = []; | |
| 549 | - $css['top'] = $values[0]; | |
| 550 | - $css['right'] = (count($values) > 1) ? $values[1] : $css['top']; | |
| 551 | - $css['bottom'] = (count($values) > 2) ? $values[2] : $css['top']; | |
| 552 | - $css['left'] = (count($values) > 3) ? $values[3] : $css['right']; | |
| 550 | + $css = []; | |
| 551 | + $css['top'] = $values[0]; | |
| 552 | + $css['right'] = ( count( $values ) > 1 ) ? $values[1] : $css['top']; | |
| 553 | + $css['bottom'] = ( count( $values ) > 2 ) ? $values[2] : $css['top']; | |
| 554 | + $css['left'] = ( count( $values ) > 3 ) ? $values[3] : $css['right']; | |
| 553 | 555 | |
| 554 | 556 | return $css; |
| 555 | 557 | } |
| 556 | 558 | |
| @@ -559,41 +561,44 @@ | ||
| 559 | 561 | * |
| 560 | 562 | * @param string $css a string of raw CSS code |
| 561 | 563 | * |
| 562 | 564 | * @return string[][] an array of string sub-arrays with the keys |
| 563 | - * "selector" (the CSS selector(s), e.g., "*" or "h1"), | |
| 564 | - * "declarationsBLock" (the semicolon-separated CSS declarations for that selector(s), | |
| 565 | - * e.g., "color: red; height: 4px;"), | |
| 566 | - * and "line" (the line number e.g. 42) | |
| 565 | + * "selector" (the CSS selector(s), e.g., "*" or "h1"), | |
| 566 | + * "declarationsBLock" (the semicolon-separated CSS declarations for that selector(s), | |
| 567 | + * e.g., "color: red; height: 4px;"), | |
| 568 | + * and "line" (the line number e.g. 42) | |
| 567 | 569 | */ |
| 568 | - private function parseCssRules($css) | |
| 569 | - { | |
| 570 | - $cssKey = md5($css); | |
| 571 | - if (!isset($this->caches[self::CACHE_KEY_CSS][$cssKey])) { | |
| 570 | + private function parseCssRules( $css ) { | |
| 571 | + $cssKey = md5( $css ); | |
| 572 | + | |
| 573 | + if ( !isset( $this->caches[self::CACHE_KEY_CSS][$cssKey] ) ) { | |
| 572 | 574 | // process the CSS file for selectors and definitions |
| 573 | - preg_match_all('/(?:^|[\\s^{}]*)([^{]+){([^}]*)}/mis', $css, $matches, PREG_SET_ORDER); | |
| 575 | + preg_match_all( '/(?:^|[\\s^{}]*)([^{]+){([^}]*)}/mis', $css, $matches, PREG_SET_ORDER ); | |
| 574 | 576 | |
| 575 | 577 | $cssRules = []; |
| 576 | 578 | /** @var string[] $cssRule */ |
| 577 | - foreach ($matches as $key => $cssRule) { | |
| 578 | - $cssDeclaration = trim($cssRule[2]); | |
| 579 | - if ($cssDeclaration === '') { | |
| 579 | + foreach ( $matches as $key => $cssRule ) { | |
| 580 | + $cssDeclaration = trim( $cssRule[2] ); | |
| 581 | + | |
| 582 | + if ( $cssDeclaration === '' ) { | |
| 580 | 583 | continue; |
| 581 | 584 | } |
| 582 | 585 | |
| 583 | - $selectors = explode(',', $cssRule[1]); | |
| 584 | - foreach ($selectors as $selector) { | |
| 586 | + $selectors = explode( ',', $cssRule[1] ); | |
| 587 | + | |
| 588 | + foreach ( $selectors as $selector ) { | |
| 585 | 589 | // don't process pseudo-elements and behavioral (dynamic) pseudo-classes; |
| 586 | 590 | // only allow structural pseudo-classes |
| 587 | - $hasPseudoElement = strpos($selector, '::') !== false; | |
| 588 | - $hasAnyPseudoClass = (bool) preg_match('/:[a-zA-Z]/', $selector); | |
| 589 | - $hasSupportedPseudoClass = (bool) preg_match('/:\\S+\\-(child|type\\()/i', $selector); | |
| 590 | - if ($hasPseudoElement || ($hasAnyPseudoClass && !$hasSupportedPseudoClass)) { | |
| 591 | + $hasPseudoElement = strpos( $selector, '::' ) !== false; | |
| 592 | + $hasAnyPseudoClass = (bool) preg_match( '/:[a-zA-Z]/', $selector ); | |
| 593 | + $hasSupportedPseudoClass = (bool) preg_match( '/:\\S+\\-(child|type\\()/i', $selector ); | |
| 594 | + | |
| 595 | + if ( $hasPseudoElement || ( $hasAnyPseudoClass && !$hasSupportedPseudoClass ) ) { | |
| 591 | 596 | continue; |
| 592 | 597 | } |
| 593 | 598 | |
| 594 | 599 | $cssRules[] = [ |
| 595 | - 'selector' => trim($selector), | |
| 600 | + 'selector' => trim( $selector ), | |
| 596 | 601 | 'declarationsBlock' => $cssDeclaration, |
| 597 | 602 | // keep track of where it appears in the file, since order is important |
| 598 | 603 | 'line' => $key, |
| 599 | 604 | ]; |
| @@ -599,9 +604,9 @@ | ||
| 599 | 604 | ]; |
| 600 | 605 | } |
| 601 | 606 | } |
| 602 | 607 | |
| 603 | - usort($cssRules, [$this, 'sortBySelectorPrecedence']); | |
| 608 | + usort( $cssRules, [$this, 'sortBySelectorPrecedence'] ); | |
| 604 | 609 | |
| 605 | 610 | $this->caches[self::CACHE_KEY_CSS][$cssKey] = $cssRules; |
| 606 | 611 | } |
| 607 | 612 | |
| @@ -612,10 +617,9 @@ | ||
| 612 | 617 | * Disables the parsing of inline styles. |
| 613 | 618 | * |
| 614 | 619 | * @return void |
| 615 | 620 | */ |
| 616 | - public function disableInlineStyleAttributesParsing() | |
| 617 | - { | |
| 621 | + public function disableInlineStyleAttributesParsing() { | |
| 618 | 622 | $this->isInlineStyleAttributesParsingEnabled = false; |
| 619 | 623 | } |
| 620 | 624 | |
| 621 | 625 | /** |
| @@ -622,10 +626,9 @@ | ||
| 622 | 626 | * Disables the parsing of <style> blocks. |
| 623 | 627 | * |
| 624 | 628 | * @return void |
| 625 | 629 | */ |
| 626 | - public function disableStyleBlocksParsing() | |
| 627 | - { | |
| 630 | + public function disableStyleBlocksParsing() { | |
| 628 | 631 | $this->isStyleBlocksParsingEnabled = false; |
| 629 | 632 | } |
| 630 | 633 | |
| 631 | 634 | /** |
| @@ -632,10 +635,9 @@ | ||
| 632 | 635 | * Disables the removal of elements with `display: none` properties. |
| 633 | 636 | * |
| 634 | 637 | * @return void |
| 635 | 638 | */ |
| 636 | - public function disableInvisibleNodeRemoval() | |
| 637 | - { | |
| 639 | + public function disableInvisibleNodeRemoval() { | |
| 638 | 640 | $this->shouldKeepInvisibleNodes = false; |
| 639 | 641 | } |
| 640 | 642 | |
| 641 | 643 | /** |
| @@ -643,10 +645,9 @@ | ||
| 643 | 645 | * corresponding CSS property has been set. |
| 644 | 646 | * |
| 645 | 647 | * @return void |
| 646 | 648 | */ |
| 647 | - public function enableCssToHtmlMapping() | |
| 648 | - { | |
| 649 | + public function enableCssToHtmlMapping() { | |
| 649 | 650 | $this->shouldMapCssToHtml = true; |
| 650 | 651 | } |
| 651 | 652 | |
| 652 | 653 | /** |
| @@ -653,15 +654,14 @@ | ||
| 653 | 654 | * Clears all caches. |
| 654 | 655 | * |
| 655 | 656 | * @return void |
| 656 | 657 | */ |
| 657 | - private function clearAllCaches() | |
| 658 | - { | |
| 659 | - $this->clearCache(self::CACHE_KEY_CSS); | |
| 660 | - $this->clearCache(self::CACHE_KEY_SELECTOR); | |
| 661 | - $this->clearCache(self::CACHE_KEY_XPATH); | |
| 662 | - $this->clearCache(self::CACHE_KEY_CSS_DECLARATIONS_BLOCK); | |
| 663 | - $this->clearCache(self::CACHE_KEY_COMBINED_STYLES); | |
| 658 | + private function clearAllCaches() { | |
| 659 | + $this->clearCache( self::CACHE_KEY_CSS ); | |
| 660 | + $this->clearCache( self::CACHE_KEY_SELECTOR ); | |
| 661 | + $this->clearCache( self::CACHE_KEY_XPATH ); | |
| 662 | + $this->clearCache( self::CACHE_KEY_CSS_DECLARATIONS_BLOCK ); | |
| 663 | + $this->clearCache( self::CACHE_KEY_COMBINED_STYLES ); | |
| 664 | 664 | } |
| 665 | 665 | |
| 666 | 666 | /** |
| 667 | 667 | * Clears a single cache by key. |
| @@ -670,12 +670,11 @@ | ||
| 670 | 670 | * or CACHE_KEY_CSS_DECLARATION_BLOCK |
| 671 | 671 | * |
| 672 | 672 | * @return void |
| 673 | 673 | * |
| 674 | - * @throws \InvalidArgumentException | |
| 674 | + * @throws InvalidArgumentException | |
| 675 | 675 | */ |
| 676 | - private function clearCache($key) | |
| 677 | - { | |
| 676 | + private function clearCache( $key ) { | |
| 678 | 677 | $allowedCacheKeys = [ |
| 679 | 678 | self::CACHE_KEY_CSS, |
| 680 | 679 | self::CACHE_KEY_SELECTOR, |
| 681 | 680 | self::CACHE_KEY_XPATH, |
| @@ -681,10 +680,11 @@ | ||
| 681 | 680 | self::CACHE_KEY_XPATH, |
| 682 | 681 | self::CACHE_KEY_CSS_DECLARATIONS_BLOCK, |
| 683 | 682 | self::CACHE_KEY_COMBINED_STYLES, |
| 684 | 683 | ]; |
| 685 | - if (!in_array($key, $allowedCacheKeys, true)) { | |
| 686 | - throw new \InvalidArgumentException('Invalid cache key: ' . $key, 1391822035); | |
| 684 | + | |
| 685 | + if ( !in_array( $key, $allowedCacheKeys, true ) ) { | |
| 686 | + throw new InvalidArgumentException( 'Invalid cache key: ' . $key, 1391822035 ); | |
| 687 | 687 | } |
| 688 | 688 | |
| 689 | 689 | $this->caches[$key] = []; |
| 690 | 690 | } |
| @@ -693,11 +693,10 @@ | ||
| 693 | 693 | * Purges the visited nodes. |
| 694 | 694 | * |
| 695 | 695 | * @return void |
| 696 | 696 | */ |
| 697 | - private function purgeVisitedNodes() | |
| 698 | - { | |
| 699 | - $this->visitedNodes = []; | |
| 697 | + private function purgeVisitedNodes() { | |
| 698 | + $this->visitedNodes = []; | |
| 700 | 699 | $this->styleAttributesForNodes = []; |
| 701 | 700 | } |
| 702 | 701 | |
| 703 | 702 | /** |
| @@ -711,10 +710,9 @@ | ||
| 711 | 710 | * @param string $tagName the tag name, e.g., "p" |
| 712 | 711 | * |
| 713 | 712 | * @return void |
| 714 | 713 | */ |
| 715 | - public function addUnprocessableHtmlTag($tagName) | |
| 716 | - { | |
| 714 | + public function addUnprocessableHtmlTag( $tagName ) { | |
| 717 | 715 | $this->unprocessableHtmlTags[] = $tagName; |
| 718 | 716 | } |
| 719 | 717 | |
| 720 | 718 | /** |
| @@ -723,13 +721,13 @@ | ||
| 723 | 721 | * @param string $tagName the tag name, e.g., "p" |
| 724 | 722 | * |
| 725 | 723 | * @return void |
| 726 | 724 | */ |
| 727 | - public function removeUnprocessableHtmlTag($tagName) | |
| 728 | - { | |
| 729 | - $key = array_search($tagName, $this->unprocessableHtmlTags, true); | |
| 730 | - if ($key !== false) { | |
| 731 | - unset($this->unprocessableHtmlTags[$key]); | |
| 725 | + public function removeUnprocessableHtmlTag( $tagName ) { | |
| 726 | + $key = array_search( $tagName, $this->unprocessableHtmlTags, true ); | |
| 727 | + | |
| 728 | + if ( $key !== false ) { | |
| 729 | + unset( $this->unprocessableHtmlTags[$key] ); | |
| 732 | 730 | } |
| 733 | 731 | } |
| 734 | 732 | |
| 735 | 733 | /** |
| @@ -738,10 +736,9 @@ | ||
| 738 | 736 | * @param string $mediaName the media type name, e.g., "braille" |
| 739 | 737 | * |
| 740 | 738 | * @return void |
| 741 | 739 | */ |
| 742 | - public function addAllowedMediaType($mediaName) | |
| 743 | - { | |
| 740 | + public function addAllowedMediaType( $mediaName ) { | |
| 744 | 741 | $this->allowedMediaTypes[$mediaName] = true; |
| 745 | 742 | } |
| 746 | 743 | |
| 747 | 744 | /** |
| @@ -750,12 +747,11 @@ | ||
| 750 | 747 | * @param string $mediaName the tag name, e.g., "braille" |
| 751 | 748 | * |
| 752 | 749 | * @return void |
| 753 | 750 | */ |
| 754 | - public function removeAllowedMediaType($mediaName) | |
| 755 | - { | |
| 756 | - if (isset($this->allowedMediaTypes[$mediaName])) { | |
| 757 | - unset($this->allowedMediaTypes[$mediaName]); | |
| 751 | + public function removeAllowedMediaType( $mediaName ) { | |
| 752 | + if ( isset( $this->allowedMediaTypes[$mediaName] ) ) { | |
| 753 | + unset( $this->allowedMediaTypes[$mediaName] ); | |
| 758 | 754 | } |
| 759 | 755 | } |
| 760 | 756 | |
| 761 | 757 | /** |
| @@ -766,10 +762,9 @@ | ||
| 766 | 762 | * @param string $selector the selector to exclude, e.g., ".editor" |
| 767 | 763 | * |
| 768 | 764 | * @return void |
| 769 | 765 | */ |
| 770 | - public function addExcludedSelector($selector) | |
| 771 | - { | |
| 766 | + public function addExcludedSelector( $selector ) { | |
| 772 | 767 | $this->excludedSelectors[$selector] = true; |
| 773 | 768 | } |
| 774 | 769 | |
| 775 | 770 | /** |
| @@ -778,12 +773,11 @@ | ||
| 778 | 773 | * @param string $selector the selector to no longer exclude, e.g., ".editor" |
| 779 | 774 | * |
| 780 | 775 | * @return void |
| 781 | 776 | */ |
| 782 | - public function removeExcludedSelector($selector) | |
| 783 | - { | |
| 784 | - if (isset($this->excludedSelectors[$selector])) { | |
| 785 | - unset($this->excludedSelectors[$selector]); | |
| 777 | + public function removeExcludedSelector( $selector ) { | |
| 778 | + if ( isset( $this->excludedSelectors[$selector] ) ) { | |
| 779 | + unset( $this->excludedSelectors[$selector] ); | |
| 786 | 780 | } |
| 787 | 781 | } |
| 788 | 782 | |
| 789 | 783 | /** |
| @@ -796,14 +790,14 @@ | ||
| 796 | 790 | * @param \DOMXPath $xPath |
| 797 | 791 | * |
| 798 | 792 | * @return void |
| 799 | 793 | */ |
| 800 | - private function removeInvisibleNodes(\DOMXPath $xPath) | |
| 801 | - { | |
| 802 | - $nodesWithStyleDisplayNone = $xPath->query( | |
| 794 | + private function removeInvisibleNodes( DOMXPath $xPath ) { | |
| 795 | + $nodesWithStyleDisplayNone = $xPath->query( | |
| 803 | 796 | '//*[contains(translate(translate(@style," ",""),"NOE","noe"),"display:none")]' |
| 804 | - ); | |
| 805 | - if ($nodesWithStyleDisplayNone->length === 0) { | |
| 797 | + ); | |
| 798 | + | |
| 799 | + if ( $nodesWithStyleDisplayNone->length === 0 ) { | |
| 806 | 800 | return; |
| 807 | 801 | } |
| 808 | 802 | |
| 809 | 803 | // The checks on parentNode and is_callable below ensure that if we've deleted the parent node, |
| @@ -808,11 +802,11 @@ | ||
| 808 | 802 | |
| 809 | 803 | // The checks on parentNode and is_callable below ensure that if we've deleted the parent node, |
| 810 | 804 | // we don't try to call removeChild on a nonexistent child node |
| 811 | 805 | /** @var \DOMNode $node */ |
| 812 | - foreach ($nodesWithStyleDisplayNone as $node) { | |
| 813 | - if ($node->parentNode && is_callable([$node->parentNode, 'removeChild'])) { | |
| 814 | - $node->parentNode->removeChild($node); | |
| 806 | + foreach ( $nodesWithStyleDisplayNone as $node ) { | |
| 807 | + if ( $node->parentNode && is_callable( [$node->parentNode, 'removeChild'] ) ) { | |
| 808 | + $node->parentNode->removeChild( $node ); | |
| 815 | 809 | } |
| 816 | 810 | } |
| 817 | 811 | } |
| 818 | 812 | |
| @@ -822,27 +816,27 @@ | ||
| 822 | 816 | * @param \DOMElement $node |
| 823 | 817 | * |
| 824 | 818 | * @return void |
| 825 | 819 | */ |
| 826 | - private function normalizeStyleAttributes(\DOMElement $node) | |
| 827 | - { | |
| 828 | - $normalizedOriginalStyle = preg_replace_callback( | |
| 820 | + private function normalizeStyleAttributes( DOMElement $node ) { | |
| 821 | + $normalizedOriginalStyle = preg_replace_callback( | |
| 829 | 822 | '/[A-z\\-]+(?=\\:)/S', |
| 830 | - function (array $m) { | |
| 831 | - return strtolower($m[0]); | |
| 823 | + function ( array $m ) { | |
| 824 | + return strtolower( $m[0] ); | |
| 832 | 825 | }, |
| 833 | - $node->getAttribute('style') | |
| 834 | - ); | |
| 826 | + $node->getAttribute( 'style' ) | |
| 827 | + ); | |
| 835 | 828 | |
| 836 | 829 | // in order to not overwrite existing style attributes in the HTML, we |
| 837 | 830 | // have to save the original HTML styles |
| 838 | 831 | $nodePath = $node->getNodePath(); |
| 839 | - if (!isset($this->styleAttributesForNodes[$nodePath])) { | |
| 840 | - $this->styleAttributesForNodes[$nodePath] = $this->parseCssDeclarationsBlock($normalizedOriginalStyle); | |
| 841 | - $this->visitedNodes[$nodePath] = $node; | |
| 832 | + | |
| 833 | + if ( !isset( $this->styleAttributesForNodes[$nodePath] ) ) { | |
| 834 | + $this->styleAttributesForNodes[$nodePath] = $this->parseCssDeclarationsBlock( $normalizedOriginalStyle ); | |
| 835 | + $this->visitedNodes[$nodePath] = $node; | |
| 842 | 836 | } |
| 843 | 837 | |
| 844 | - $node->setAttribute('style', $normalizedOriginalStyle); | |
| 838 | + $node->setAttribute( 'style', $normalizedOriginalStyle ); | |
| 845 | 839 | } |
| 846 | 840 | |
| 847 | 841 | /** |
| 848 | 842 | * Merges styles from styles attributes and style nodes and applies them to the attribute nodes |
| @@ -848,20 +842,19 @@ | ||
| 848 | 842 | * Merges styles from styles attributes and style nodes and applies them to the attribute nodes |
| 849 | 843 | * |
| 850 | 844 | * @return void |
| 851 | 845 | */ |
| 852 | - private function fillStyleAttributesWithMergedStyles() | |
| 853 | - { | |
| 854 | - foreach ($this->styleAttributesForNodes as $nodePath => $styleAttributesForNode) { | |
| 855 | - $node = $this->visitedNodes[$nodePath]; | |
| 856 | - $currentStyleAttributes = $this->parseCssDeclarationsBlock($node->getAttribute('style')); | |
| 857 | - $node->setAttribute( | |
| 846 | + private function fillStyleAttributesWithMergedStyles() { | |
| 847 | + foreach ( $this->styleAttributesForNodes as $nodePath => $styleAttributesForNode ) { | |
| 848 | + $node = $this->visitedNodes[$nodePath]; | |
| 849 | + $currentStyleAttributes = $this->parseCssDeclarationsBlock( $node->getAttribute( 'style' ) ); | |
| 850 | + $node->setAttribute( | |
| 858 | 851 | 'style', |
| 859 | - $this->generateStyleStringFromDeclarationsArrays( | |
| 852 | + $this->generateStyleStringFromDeclarationsArrays( | |
| 860 | 853 | $currentStyleAttributes, |
| 861 | 854 | $styleAttributesForNode |
| 862 | - ) | |
| 863 | - ); | |
| 855 | + ) | |
| 856 | + ); | |
| 864 | 857 | } |
| 865 | 858 | } |
| 866 | 859 | |
| 867 | 860 | /** |
| @@ -874,34 +867,36 @@ | ||
| 874 | 867 | * @param string[] $newStyles |
| 875 | 868 | * |
| 876 | 869 | * @return string |
| 877 | 870 | */ |
| 878 | - private function generateStyleStringFromDeclarationsArrays(array $oldStyles, array $newStyles) | |
| 879 | - { | |
| 880 | - $combinedStyles = array_merge($oldStyles, $newStyles); | |
| 881 | - $cacheKey = serialize($combinedStyles); | |
| 882 | - if (isset($this->caches[self::CACHE_KEY_COMBINED_STYLES][$cacheKey])) { | |
| 871 | + private function generateStyleStringFromDeclarationsArrays( array $oldStyles, array $newStyles ) { | |
| 872 | + $combinedStyles = array_merge( $oldStyles, $newStyles ); | |
| 873 | + $cacheKey = serialize( $combinedStyles ); | |
| 874 | + | |
| 875 | + if ( isset( $this->caches[self::CACHE_KEY_COMBINED_STYLES][$cacheKey] ) ) { | |
| 883 | 876 | return $this->caches[self::CACHE_KEY_COMBINED_STYLES][$cacheKey]; |
| 884 | 877 | } |
| 885 | 878 | |
| 886 | - foreach ($oldStyles as $attributeName => $attributeValue) { | |
| 887 | - if (!isset($newStyles[$attributeName])) { | |
| 879 | + foreach ( $oldStyles as $attributeName => $attributeValue ) { | |
| 880 | + if ( !isset( $newStyles[$attributeName] ) ) { | |
| 888 | 881 | continue; |
| 889 | 882 | } |
| 890 | 883 | |
| 891 | 884 | $newAttributeValue = $newStyles[$attributeName]; |
| 892 | - if ($this->attributeValueIsImportant($attributeValue) | |
| 893 | - && !$this->attributeValueIsImportant($newAttributeValue) | |
| 894 | - ) { | |
| 885 | + | |
| 886 | + if ( $this->attributeValueIsImportant( $attributeValue ) | |
| 887 | + && !$this->attributeValueIsImportant( $newAttributeValue ) | |
| 888 | + ) { | |
| 895 | 889 | $combinedStyles[$attributeName] = $attributeValue; |
| 896 | 890 | } |
| 897 | 891 | } |
| 898 | 892 | |
| 899 | 893 | $style = ''; |
| 900 | - foreach ($combinedStyles as $attributeName => $attributeValue) { | |
| 901 | - $style .= strtolower(trim($attributeName)) . ': ' . trim($attributeValue) . '; '; | |
| 894 | + | |
| 895 | + foreach ( $combinedStyles as $attributeName => $attributeValue ) { | |
| 896 | + $style .= strtolower( trim( $attributeName ) ) . ': ' . trim( $attributeValue ) . '; '; | |
| 902 | 897 | } |
| 903 | - $trimmedStyle = rtrim($style); | |
| 898 | + $trimmedStyle = rtrim( $style ); | |
| 904 | 899 | |
| 905 | 900 | $this->caches[self::CACHE_KEY_COMBINED_STYLES][$cacheKey] = $trimmedStyle; |
| 906 | 901 | |
| 907 | 902 | return $trimmedStyle; |
| @@ -913,11 +908,10 @@ | ||
| 913 | 908 | * @param string $attributeValue |
| 914 | 909 | * |
| 915 | 910 | * @return bool |
| 916 | 911 | */ |
| 917 | - private function attributeValueIsImportant($attributeValue) | |
| 918 | - { | |
| 919 | - return strtolower(substr(trim($attributeValue), -10)) === '!important'; | |
| 912 | + private function attributeValueIsImportant( $attributeValue ) { | |
| 913 | + return strtolower( substr( trim( $attributeValue ), -10 ) ) === '!important'; | |
| 920 | 914 | } |
| 921 | 915 | |
| 922 | 916 | /** |
| 923 | 917 | * Applies $css to $xmlDocument, limited to the media queries that actually apply to the document. |
| @@ -922,24 +916,23 @@ | ||
| 922 | 916 | /** |
| 923 | 917 | * Applies $css to $xmlDocument, limited to the media queries that actually apply to the document. |
| 924 | 918 | * |
| 925 | 919 | * @param \DOMDocument $xmlDocument the document to match against |
| 926 | - * @param \DOMXPath $xPath | |
| 927 | - * @param string $css a string of CSS | |
| 920 | + * @param \DOMXPath $xPath | |
| 921 | + * @param string $css a string of CSS | |
| 928 | 922 | * |
| 929 | 923 | * @return void |
| 930 | 924 | */ |
| 931 | - private function copyCssWithMediaToStyleNode(\DOMDocument $xmlDocument, \DOMXPath $xPath, $css) | |
| 932 | - { | |
| 933 | - if ($css === '') { | |
| 925 | + private function copyCssWithMediaToStyleNode( DOMDocument $xmlDocument, DOMXPath $xPath, $css ) { | |
| 926 | + if ( $css === '' ) { | |
| 934 | 927 | return; |
| 935 | 928 | } |
| 936 | 929 | |
| 937 | 930 | $mediaQueriesRelevantForDocument = []; |
| 938 | 931 | |
| 939 | - foreach ($this->extractMediaQueriesFromCss($css) as $mediaQuery) { | |
| 940 | - foreach ($this->parseCssRules($mediaQuery['css']) as $selector) { | |
| 941 | - if ($this->existsMatchForCssSelector($xPath, $selector['selector'])) { | |
| 932 | + foreach ( $this->extractMediaQueriesFromCss( $css ) as $mediaQuery ) { | |
| 933 | + foreach ( $this->parseCssRules( $mediaQuery['css'] ) as $selector ) { | |
| 934 | + if ( $this->existsMatchForCssSelector( $xPath, $selector['selector'] ) ) { | |
| 942 | 935 | $mediaQueriesRelevantForDocument[] = $mediaQuery['query']; |
| 943 | 936 | break; |
| 944 | 937 | } |
| 945 | 938 | } |
| @@ -944,9 +937,9 @@ | ||
| 944 | 937 | } |
| 945 | 938 | } |
| 946 | 939 | } |
| 947 | 940 | |
| 948 | - $this->addStyleElementToDocument($xmlDocument, implode($mediaQueriesRelevantForDocument)); | |
| 941 | + $this->addStyleElementToDocument( $xmlDocument, implode( $mediaQueriesRelevantForDocument ) ); | |
| 949 | 942 | } |
| 950 | 943 | |
| 951 | 944 | /** |
| 952 | 945 | * Extracts the media queries from $css while skipping empty media queries. |
| @@ -954,15 +947,14 @@ | ||
| 954 | 947 | * @param string $css |
| 955 | 948 | * |
| 956 | 949 | * @return string[][] numeric array with string sub-arrays with the keys "css" and "query" |
| 957 | 950 | */ |
| 958 | - private function extractMediaQueriesFromCss($css) | |
| 959 | - { | |
| 960 | - preg_match_all('/@media\\b[^{]*({((?:[^{}]+|(?1))*)})/', $css, $rawMediaQueries, PREG_SET_ORDER); | |
| 951 | + private function extractMediaQueriesFromCss( $css ) { | |
| 952 | + preg_match_all( '/@media\\b[^{]*({((?:[^{}]+|(?1))*)})/', $css, $rawMediaQueries, PREG_SET_ORDER ); | |
| 961 | 953 | $parsedQueries = []; |
| 962 | 954 | |
| 963 | - foreach ($rawMediaQueries as $mediaQuery) { | |
| 964 | - if ($mediaQuery[2] !== '') { | |
| 955 | + foreach ( $rawMediaQueries as $mediaQuery ) { | |
| 956 | + if ( $mediaQuery[2] !== '' ) { | |
| 965 | 957 | $parsedQueries[] = [ |
| 966 | 958 | 'css' => $mediaQuery[2], |
| 967 | 959 | 'query' => $mediaQuery[0], |
| 968 | 960 | ]; |
| @@ -975,15 +967,14 @@ | ||
| 975 | 967 | /** |
| 976 | 968 | * Checks whether there is at least one matching element for $cssSelector. |
| 977 | 969 | * |
| 978 | 970 | * @param \DOMXPath $xPath |
| 979 | - * @param string $cssSelector | |
| 971 | + * @param string $cssSelector | |
| 980 | 972 | * |
| 981 | 973 | * @return bool |
| 982 | 974 | */ |
| 983 | - private function existsMatchForCssSelector(\DOMXPath $xPath, $cssSelector) | |
| 984 | - { | |
| 985 | - $nodesMatchingSelector = $xPath->query($this->translateCssToXpath($cssSelector)); | |
| 975 | + private function existsMatchForCssSelector( DOMXPath $xPath, $cssSelector ) { | |
| 976 | + $nodesMatchingSelector = $xPath->query( $this->translateCssToXpath( $cssSelector ) ); | |
| 986 | 977 | |
| 987 | 978 | return $nodesMatchingSelector !== false && $nodesMatchingSelector->length !== 0; |
| 988 | 979 | } |
| 989 | 980 | |
| @@ -993,21 +984,20 @@ | ||
| 993 | 984 | * @param \DOMXPath $xPath |
| 994 | 985 | * |
| 995 | 986 | * @return string |
| 996 | 987 | */ |
| 997 | - private function getCssFromAllStyleNodes(\DOMXPath $xPath) | |
| 998 | - { | |
| 999 | - $styleNodes = $xPath->query('//style'); | |
| 988 | + private function getCssFromAllStyleNodes( DOMXPath $xPath ) { | |
| 989 | + $styleNodes = $xPath->query( '//style' ); | |
| 1000 | 990 | |
| 1001 | - if ($styleNodes === false) { | |
| 991 | + if ( $styleNodes === false ) { | |
| 1002 | 992 | return ''; |
| 1003 | 993 | } |
| 1004 | 994 | |
| 1005 | 995 | $css = ''; |
| 1006 | 996 | /** @var \DOMNode $styleNode */ |
| 1007 | - foreach ($styleNodes as $styleNode) { | |
| 997 | + foreach ( $styleNodes as $styleNode ) { | |
| 1008 | 998 | $css .= "\n\n" . $styleNode->nodeValue; |
| 1009 | - $styleNode->parentNode->removeChild($styleNode); | |
| 999 | + $styleNode->parentNode->removeChild( $styleNode ); | |
| 1010 | 1000 | } |
| 1011 | 1001 | |
| 1012 | 1002 | return $css; |
| 1013 | 1003 | } |
| @@ -1019,21 +1009,20 @@ | ||
| 1019 | 1009 | * |
| 1020 | 1010 | * @see https://github.com/jjriv/emogrifier/issues/103 |
| 1021 | 1011 | * |
| 1022 | 1012 | * @param \DOMDocument $document |
| 1023 | - * @param string $css | |
| 1013 | + * @param string $css | |
| 1024 | 1014 | * |
| 1025 | 1015 | * @return void |
| 1026 | 1016 | */ |
| 1027 | - protected function addStyleElementToDocument(\DOMDocument $document, $css) | |
| 1028 | - { | |
| 1029 | - $styleElement = $document->createElement('style', $css); | |
| 1030 | - $styleAttribute = $document->createAttribute('type'); | |
| 1017 | + protected function addStyleElementToDocument( DOMDocument $document, $css ) { | |
| 1018 | + $styleElement = $document->createElement( 'style', $css ); | |
| 1019 | + $styleAttribute = $document->createAttribute( 'type' ); | |
| 1031 | 1020 | $styleAttribute->value = 'text/css'; |
| 1032 | - $styleElement->appendChild($styleAttribute); | |
| 1021 | + $styleElement->appendChild( $styleAttribute ); | |
| 1033 | 1022 | |
| 1034 | - $head = $this->getOrCreateHeadElement($document); | |
| 1035 | - $head->appendChild($styleElement); | |
| 1023 | + $head = $this->getOrCreateHeadElement( $document ); | |
| 1024 | + $head->appendChild( $styleElement ); | |
| 1036 | 1025 | } |
| 1037 | 1026 | |
| 1038 | 1027 | /** |
| 1039 | 1028 | * Returns the existing or creates a new head element in $document. |
| @@ -1041,16 +1030,15 @@ | ||
| 1041 | 1030 | * @param \DOMDocument $document |
| 1042 | 1031 | * |
| 1043 | 1032 | * @return \DOMNode the head element |
| 1044 | 1033 | */ |
| 1045 | - private function getOrCreateHeadElement(\DOMDocument $document) | |
| 1046 | - { | |
| 1047 | - $head = $document->getElementsByTagName('head')->item(0); | |
| 1034 | + private function getOrCreateHeadElement( DOMDocument $document ) { | |
| 1035 | + $head = $document->getElementsByTagName( 'head' )->item( 0 ); | |
| 1048 | 1036 | |
| 1049 | - if ($head === null) { | |
| 1050 | - $head = $document->createElement('head'); | |
| 1051 | - $html = $document->getElementsByTagName('html')->item(0); | |
| 1052 | - $html->insertBefore($head, $document->getElementsByTagName('body')->item(0)); | |
| 1037 | + if ( $head === null ) { | |
| 1038 | + $head = $document->createElement( 'head' ); | |
| 1039 | + $html = $document->getElementsByTagName( 'html' )->item( 0 ); | |
| 1040 | + $html->insertBefore( $head, $document->getElementsByTagName( 'body' )->item( 0 ) ); | |
| 1053 | 1041 | } |
| 1054 | 1042 | |
| 1055 | 1043 | return $head; |
| 1056 | 1044 | } |
| @@ -1075,33 +1063,33 @@ | ||
| 1075 | 1063 | * @param string $css |
| 1076 | 1064 | * |
| 1077 | 1065 | * @return string[] |
| 1078 | 1066 | */ |
| 1079 | - private function splitCssAndMediaQuery($css) | |
| 1080 | - { | |
| 1081 | - $cssWithoutComments = preg_replace('/\\/\\*.*\\*\\//sU', '', $css); | |
| 1067 | + private function splitCssAndMediaQuery( $css ) { | |
| 1068 | + $cssWithoutComments = preg_replace( '/\\/\\*.*\\*\\//sU', '', $css ); | |
| 1082 | 1069 | |
| 1083 | 1070 | $mediaTypesExpression = ''; |
| 1084 | - if (!empty($this->allowedMediaTypes)) { | |
| 1085 | - $mediaTypesExpression = '|' . implode('|', array_keys($this->allowedMediaTypes)); | |
| 1071 | + | |
| 1072 | + if ( !empty( $this->allowedMediaTypes ) ) { | |
| 1073 | + $mediaTypesExpression = '|' . implode( '|', array_keys( $this->allowedMediaTypes ) ); | |
| 1086 | 1074 | } |
| 1087 | 1075 | |
| 1088 | - $media = ''; | |
| 1089 | - $cssForAllowedMediaTypes = preg_replace_callback( | |
| 1076 | + $media = ''; | |
| 1077 | + $cssForAllowedMediaTypes = preg_replace_callback( | |
| 1090 | 1078 | '#@media\\s+(?:only\\s)?(?:[\\s{\\(]' . $mediaTypesExpression . ')\\s?[^{]+{.*}\\s*}\\s*#misU', |
| 1091 | - function ($matches) use (&$media) { | |
| 1079 | + function ( $matches ) use ( &$media ) { | |
| 1092 | 1080 | $media .= $matches[0]; |
| 1093 | 1081 | }, |
| 1094 | 1082 | $cssWithoutComments |
| 1095 | - ); | |
| 1083 | + ); | |
| 1096 | 1084 | |
| 1097 | 1085 | // filter the CSS |
| 1098 | 1086 | $search = [ |
| 1099 | - 'import directives' => '/^\\s*@import\\s[^;]+;/misU', | |
| 1087 | + 'import directives' => '/^\\s*@import\\s[^;]+;/misU', | |
| 1100 | 1088 | 'remaining media enclosures' => '/^\\s*@media\\s[^{]+{(.*)}\\s*}\\s/misU', |
| 1101 | 1089 | ]; |
| 1102 | 1090 | |
| 1103 | - $cleanedCss = preg_replace($search, '', $cssForAllowedMediaTypes); | |
| 1091 | + $cleanedCss = preg_replace( $search, '', $cssForAllowedMediaTypes ); | |
| 1104 | 1092 | |
| 1105 | 1093 | return ['css' => $cleanedCss, 'media' => $media]; |
| 1106 | 1094 | } |
| 1107 | 1095 | |
| @@ -1109,18 +1097,17 @@ | ||
| 1109 | 1097 | * Creates a DOMDocument instance with the current HTML. |
| 1110 | 1098 | * |
| 1111 | 1099 | * @return \DOMDocument |
| 1112 | 1100 | */ |
| 1113 | - private function createXmlDocument() | |
| 1114 | - { | |
| 1115 | - $xmlDocument = new \DOMDocument; | |
| 1116 | - $xmlDocument->encoding = 'UTF-8'; | |
| 1101 | + private function createXmlDocument() { | |
| 1102 | + $xmlDocument = new \DOMDocument(); | |
| 1103 | + $xmlDocument->encoding = 'UTF-8'; | |
| 1117 | 1104 | $xmlDocument->strictErrorChecking = false; |
| 1118 | - $xmlDocument->formatOutput = true; | |
| 1119 | - $libXmlState = libxml_use_internal_errors(true); | |
| 1120 | - $xmlDocument->loadHTML($this->getUnifiedHtml()); | |
| 1105 | + $xmlDocument->formatOutput = true; | |
| 1106 | + $libXmlState = libxml_use_internal_errors( true ); | |
| 1107 | + $xmlDocument->loadHTML( $this->getUnifiedHtml() ); | |
| 1121 | 1108 | libxml_clear_errors(); |
| 1122 | - libxml_use_internal_errors($libXmlState); | |
| 1109 | + libxml_use_internal_errors( $libXmlState ); | |
| 1123 | 1110 | $xmlDocument->normalizeDocument(); |
| 1124 | 1111 | |
| 1125 | 1112 | return $xmlDocument; |
| 1126 | 1113 | } |
| @@ -1130,16 +1117,15 @@ | ||
| 1130 | 1117 | * with added document type and Content-Type meta tag if needed. |
| 1131 | 1118 | * |
| 1132 | 1119 | * @return string the unified HTML |
| 1133 | 1120 | * |
| 1134 | - * @throws \BadMethodCallException | |
| 1121 | + * @throws BadMethodCallException | |
| 1135 | 1122 | */ |
| 1136 | - private function getUnifiedHtml() | |
| 1137 | - { | |
| 1138 | - $htmlWithoutUnprocessableTags = $this->removeUnprocessableTags($this->html); | |
| 1139 | - $htmlWithDocumentType = $this->ensureDocumentType($htmlWithoutUnprocessableTags); | |
| 1123 | + private function getUnifiedHtml() { | |
| 1124 | + $htmlWithoutUnprocessableTags = $this->removeUnprocessableTags( $this->html ); | |
| 1125 | + $htmlWithDocumentType = $this->ensureDocumentType( $htmlWithoutUnprocessableTags ); | |
| 1140 | 1126 | |
| 1141 | - return $this->addContentTypeMetaTag($htmlWithDocumentType); | |
| 1127 | + return $this->addContentTypeMetaTag( $htmlWithDocumentType ); | |
| 1142 | 1128 | } |
| 1143 | 1129 | |
| 1144 | 1130 | /** |
| 1145 | 1131 | * Removes the unprocessable tags from $html (if this feature is enabled). |
| @@ -1147,21 +1133,20 @@ | ||
| 1147 | 1133 | * @param string $html |
| 1148 | 1134 | * |
| 1149 | 1135 | * @return string the reworked HTML with the unprocessable tags removed |
| 1150 | 1136 | */ |
| 1151 | - private function removeUnprocessableTags($html) | |
| 1152 | - { | |
| 1153 | - if (empty($this->unprocessableHtmlTags)) { | |
| 1137 | + private function removeUnprocessableTags( $html ) { | |
| 1138 | + if ( empty( $this->unprocessableHtmlTags ) ) { | |
| 1154 | 1139 | return $html; |
| 1155 | 1140 | } |
| 1156 | 1141 | |
| 1157 | - $unprocessableHtmlTags = implode('|', $this->unprocessableHtmlTags); | |
| 1142 | + $unprocessableHtmlTags = implode( '|', $this->unprocessableHtmlTags ); | |
| 1158 | 1143 | |
| 1159 | - return preg_replace( | |
| 1144 | + return preg_replace( | |
| 1160 | 1145 | '/<\\/?(' . $unprocessableHtmlTags . ')[^>]*>/i', |
| 1161 | 1146 | '', |
| 1162 | 1147 | $html |
| 1163 | - ); | |
| 1148 | + ); | |
| 1164 | 1149 | } |
| 1165 | 1150 | |
| 1166 | 1151 | /** |
| 1167 | 1152 | * Makes sure that the passed HTML has a document type. |
| @@ -1169,12 +1154,12 @@ | ||
| 1169 | 1154 | * @param string $html |
| 1170 | 1155 | * |
| 1171 | 1156 | * @return string HTML with document type |
| 1172 | 1157 | */ |
| 1173 | - private function ensureDocumentType($html) | |
| 1174 | - { | |
| 1175 | - $hasDocumentType = stripos($html, '<!DOCTYPE') !== false; | |
| 1176 | - if ($hasDocumentType) { | |
| 1158 | + private function ensureDocumentType( $html ) { | |
| 1159 | + $hasDocumentType = stripos( $html, '<!DOCTYPE' ) !== false; | |
| 1160 | + | |
| 1161 | + if ( $hasDocumentType ) { | |
| 1177 | 1162 | return $html; |
| 1178 | 1163 | } |
| 1179 | 1164 | |
| 1180 | 1165 | return self::DEFAULT_DOCUMENT_TYPE . $html; |
| @@ -1186,28 +1171,28 @@ | ||
| 1186 | 1171 | * @param string $html |
| 1187 | 1172 | * |
| 1188 | 1173 | * @return string the HTML with the meta tag added |
| 1189 | 1174 | */ |
| 1190 | - private function addContentTypeMetaTag($html) | |
| 1191 | - { | |
| 1192 | - $hasContentTypeMetaTag = stristr($html, 'Content-Type') !== false; | |
| 1193 | - if ($hasContentTypeMetaTag) { | |
| 1175 | + private function addContentTypeMetaTag( $html ) { | |
| 1176 | + $hasContentTypeMetaTag = stristr( $html, 'Content-Type' ) !== false; | |
| 1177 | + | |
| 1178 | + if ( $hasContentTypeMetaTag ) { | |
| 1194 | 1179 | return $html; |
| 1195 | 1180 | } |
| 1196 | 1181 | |
| 1197 | 1182 | // We are trying to insert the meta tag to the right spot in the DOM. |
| 1198 | 1183 | // If we just prepended it to the HTML, we would lose attributes set to the HTML tag. |
| 1199 | - $hasHeadTag = stripos($html, '<head') !== false; | |
| 1200 | - $hasHtmlTag = stripos($html, '<html') !== false; | |
| 1184 | + $hasHeadTag = stripos( $html, '<head' ) !== false; | |
| 1185 | + $hasHtmlTag = stripos( $html, '<html' ) !== false; | |
| 1201 | 1186 | |
| 1202 | - if ($hasHeadTag) { | |
| 1203 | - $reworkedHtml = preg_replace('/<head(.*?)>/i', '<head$1>' . self::CONTENT_TYPE_META_TAG, $html); | |
| 1204 | - } elseif ($hasHtmlTag) { | |
| 1205 | - $reworkedHtml = preg_replace( | |
| 1187 | + if ( $hasHeadTag ) { | |
| 1188 | + $reworkedHtml = preg_replace( '/<head(.*?)>/i', '<head$1>' . self::CONTENT_TYPE_META_TAG, $html ); | |
| 1189 | + } elseif ( $hasHtmlTag ) { | |
| 1190 | + $reworkedHtml = preg_replace( | |
| 1206 | 1191 | '/<html(.*?)>/i', |
| 1207 | 1192 | '<html$1><head>' . self::CONTENT_TYPE_META_TAG . '</head>', |
| 1208 | 1193 | $html |
| 1209 | - ); | |
| 1194 | + ); | |
| 1210 | 1195 | } else { |
| 1211 | 1196 | $reworkedHtml = self::CONTENT_TYPE_META_TAG . $html; |
| 1212 | 1197 | } |
| 1213 | 1198 | |
| @@ -1219,18 +1204,18 @@ | ||
| 1219 | 1204 | * @param string[] $b |
| 1220 | 1205 | * |
| 1221 | 1206 | * @return int |
| 1222 | 1207 | */ |
| 1223 | - private function sortBySelectorPrecedence(array $a, array $b) | |
| 1224 | - { | |
| 1225 | - $precedenceA = $this->getCssSelectorPrecedence($a['selector']); | |
| 1226 | - $precedenceB = $this->getCssSelectorPrecedence($b['selector']); | |
| 1208 | + private function sortBySelectorPrecedence( array $a, array $b ) { | |
| 1209 | + $precedenceA = $this->getCssSelectorPrecedence( $a['selector'] ); | |
| 1210 | + $precedenceB = $this->getCssSelectorPrecedence( $b['selector'] ); | |
| 1227 | 1211 | |
| 1228 | 1212 | // We want these sorted in ascending order so selectors with lesser precedence get processed first and |
| 1229 | 1213 | // selectors with greater precedence get sorted last. |
| 1230 | - $precedenceForEquals = ($a['line'] < $b['line'] ? -1 : 1); | |
| 1231 | - $precedenceForNotEquals = ($precedenceA < $precedenceB ? -1 : 1); | |
| 1232 | - return ($precedenceA === $precedenceB) ? $precedenceForEquals : $precedenceForNotEquals; | |
| 1214 | + $precedenceForEquals = ( $a['line'] < $b['line'] ? -1 : 1 ); | |
| 1215 | + $precedenceForNotEquals = ( $precedenceA < $precedenceB ? -1 : 1 ); | |
| 1216 | + | |
| 1217 | + return ( $precedenceA === $precedenceB ) ? $precedenceForEquals : $precedenceForNotEquals; | |
| 1233 | 1218 | } |
| 1234 | 1219 | |
| 1235 | 1220 | /** |
| 1236 | 1221 | * @param string $selector |
| @@ -1236,24 +1221,24 @@ | ||
| 1236 | 1221 | * @param string $selector |
| 1237 | 1222 | * |
| 1238 | 1223 | * @return int |
| 1239 | 1224 | */ |
| 1240 | - private function getCssSelectorPrecedence($selector) | |
| 1241 | - { | |
| 1242 | - $selectorKey = md5($selector); | |
| 1243 | - if (!isset($this->caches[self::CACHE_KEY_SELECTOR][$selectorKey])) { | |
| 1225 | + private function getCssSelectorPrecedence( $selector ) { | |
| 1226 | + $selectorKey = md5( $selector ); | |
| 1227 | + | |
| 1228 | + if ( !isset( $this->caches[self::CACHE_KEY_SELECTOR][$selectorKey] ) ) { | |
| 1244 | 1229 | $precedence = 0; |
| 1245 | - $value = 100; | |
| 1230 | + $value = 100; | |
| 1246 | 1231 | // ids: worth 100, classes: worth 10, elements: worth 1 |
| 1247 | - $search = ['\\#','\\.','']; | |
| 1232 | + $search = ['\\#', '\\.', '']; | |
| 1248 | 1233 | |
| 1249 | - foreach ($search as $s) { | |
| 1250 | - if (trim($selector) === '') { | |
| 1234 | + foreach ( $search as $s ) { | |
| 1235 | + if ( trim( $selector ) === '' ) { | |
| 1251 | 1236 | break; |
| 1252 | 1237 | } |
| 1253 | - $number = 0; | |
| 1254 | - $selector = preg_replace('/' . $s . '\\w+/', '', $selector, -1, $number); | |
| 1255 | - $precedence += ($value * $number); | |
| 1238 | + $number = 0; | |
| 1239 | + $selector = preg_replace( '/' . $s . '\\w+/', '', $selector, -1, $number ); | |
| 1240 | + $precedence += ( $value * $number ); | |
| 1256 | 1241 | $value /= 10; |
| 1257 | 1242 | } |
| 1258 | 1243 | $this->caches[self::CACHE_KEY_SELECTOR][$selectorKey] = $precedence; |
| 1259 | 1244 | } |
| @@ -1269,52 +1254,53 @@ | ||
| 1269 | 1254 | * @param string $cssSelector a CSS selector |
| 1270 | 1255 | * |
| 1271 | 1256 | * @return string the corresponding XPath selector |
| 1272 | 1257 | */ |
| 1273 | - private function translateCssToXpath($cssSelector) | |
| 1274 | - { | |
| 1275 | - $paddedSelector = ' ' . $cssSelector . ' '; | |
| 1276 | - $lowercasePaddedSelector = preg_replace_callback( | |
| 1258 | + private function translateCssToXpath( $cssSelector ) { | |
| 1259 | + $paddedSelector = ' ' . $cssSelector . ' '; | |
| 1260 | + $lowercasePaddedSelector = preg_replace_callback( | |
| 1277 | 1261 | '/\\s+\\w+\\s+/', |
| 1278 | - function (array $matches) { | |
| 1279 | - return strtolower($matches[0]); | |
| 1262 | + function ( array $matches ) { | |
| 1263 | + return strtolower( $matches[0] ); | |
| 1280 | 1264 | }, |
| 1281 | 1265 | $paddedSelector |
| 1282 | - ); | |
| 1283 | - $trimmedLowercaseSelector = trim($lowercasePaddedSelector); | |
| 1284 | - $xPathKey = md5($trimmedLowercaseSelector); | |
| 1285 | - if (!isset($this->caches[self::CACHE_KEY_XPATH][$xPathKey])) { | |
| 1286 | - $roughXpath = '//' . preg_replace( | |
| 1287 | - array_keys($this->xPathRules), | |
| 1266 | + ); | |
| 1267 | + $trimmedLowercaseSelector = trim( $lowercasePaddedSelector ); | |
| 1268 | + $xPathKey = md5( $trimmedLowercaseSelector ); | |
| 1269 | + | |
| 1270 | + if ( !isset( $this->caches[self::CACHE_KEY_XPATH][$xPathKey] ) ) { | |
| 1271 | + $roughXpath = '//' . preg_replace( | |
| 1272 | + array_keys( $this->xPathRules ), | |
| 1288 | 1273 | $this->xPathRules, |
| 1289 | 1274 | $trimmedLowercaseSelector |
| 1290 | - ); | |
| 1291 | - $xPathWithIdAttributeMatchers = preg_replace_callback( | |
| 1275 | + ); | |
| 1276 | + $xPathWithIdAttributeMatchers = preg_replace_callback( | |
| 1292 | 1277 | self::ID_ATTRIBUTE_MATCHER, |
| 1293 | 1278 | [$this, 'matchIdAttributes'], |
| 1294 | 1279 | $roughXpath |
| 1295 | - ); | |
| 1296 | - $xPathWithIdAttributeAndClassMatchers = preg_replace_callback( | |
| 1280 | + ); | |
| 1281 | + $xPathWithIdAttributeAndClassMatchers = preg_replace_callback( | |
| 1297 | 1282 | self::CLASS_ATTRIBUTE_MATCHER, |
| 1298 | 1283 | [$this, 'matchClassAttributes'], |
| 1299 | 1284 | $xPathWithIdAttributeMatchers |
| 1300 | - ); | |
| 1285 | + ); | |
| 1301 | 1286 | |
| 1302 | 1287 | // Advanced selectors are going to require a bit more advanced emogrification. |
| 1303 | 1288 | // When we required PHP 5.3, we could do this with closures. |
| 1304 | - $xPathWithIdAttributeAndClassMatchers = preg_replace_callback( | |
| 1289 | + $xPathWithIdAttributeAndClassMatchers = preg_replace_callback( | |
| 1305 | 1290 | '/([^\\/]+):nth-child\\(\\s*(odd|even|[+\\-]?\\d|[+\\-]?\\d?n(\\s*[+\\-]\\s*\\d)?)\\s*\\)/i', |
| 1306 | 1291 | [$this, 'translateNthChild'], |
| 1307 | 1292 | $xPathWithIdAttributeAndClassMatchers |
| 1308 | - ); | |
| 1309 | - $finalXpath = preg_replace_callback( | |
| 1293 | + ); | |
| 1294 | + $finalXpath = preg_replace_callback( | |
| 1310 | 1295 | '/([^\\/]+):nth-of-type\\(\s*(odd|even|[+\\-]?\\d|[+\\-]?\\d?n(\\s*[+\\-]\\s*\\d)?)\\s*\\)/i', |
| 1311 | 1296 | [$this, 'translateNthOfType'], |
| 1312 | 1297 | $xPathWithIdAttributeAndClassMatchers |
| 1313 | - ); | |
| 1298 | + ); | |
| 1314 | 1299 | |
| 1315 | 1300 | $this->caches[self::CACHE_KEY_SELECTOR][$xPathKey] = $finalXpath; |
| 1316 | 1301 | } |
| 1302 | + | |
| 1317 | 1303 | return $this->caches[self::CACHE_KEY_SELECTOR][$xPathKey]; |
| 1318 | 1304 | } |
| 1319 | 1305 | |
| 1320 | 1306 | /** |
| @@ -1321,11 +1307,10 @@ | ||
| 1321 | 1307 | * @param string[] $match |
| 1322 | 1308 | * |
| 1323 | 1309 | * @return string |
| 1324 | 1310 | */ |
| 1325 | - private function matchIdAttributes(array $match) | |
| 1326 | - { | |
| 1327 | - return ($match[1] !== '' ? $match[1] : '*') . '[@id="' . $match[2] . '"]'; | |
| 1311 | + private function matchIdAttributes( array $match ) { | |
| 1312 | + return ( $match[1] !== '' ? $match[1] : '*' ) . '[@id="' . $match[2] . '"]'; | |
| 1328 | 1313 | } |
| 1329 | 1314 | |
| 1330 | 1315 | /** |
| 1331 | 1316 | * @param string[] $match |
| @@ -1331,15 +1316,14 @@ | ||
| 1331 | 1316 | * @param string[] $match |
| 1332 | 1317 | * |
| 1333 | 1318 | * @return string |
| 1334 | 1319 | */ |
| 1335 | - private function matchClassAttributes(array $match) | |
| 1336 | - { | |
| 1337 | - return ($match[1] !== '' ? $match[1] : '*') . '[contains(concat(" ",@class," "),concat(" ","' . | |
| 1338 | - implode( | |
| 1320 | + private function matchClassAttributes( array $match ) { | |
| 1321 | + return ( $match[1] !== '' ? $match[1] : '*' ) . '[contains(concat(" ",@class," "),concat(" ","' . | |
| 1322 | + implode( | |
| 1339 | 1323 | '"," "))][contains(concat(" ",@class," "),concat(" ","', |
| 1340 | - explode('.', substr($match[2], 1)) | |
| 1341 | - ) . '"," "))]'; | |
| 1324 | + explode( '.', substr( $match[2], 1 ) ) | |
| 1325 | + ) . '"," "))]'; | |
| 1342 | 1326 | } |
| 1343 | 1327 | |
| 1344 | 1328 | /** |
| 1345 | 1329 | * @param string[] $match |
| @@ -1345,31 +1329,30 @@ | ||
| 1345 | 1329 | * @param string[] $match |
| 1346 | 1330 | * |
| 1347 | 1331 | * @return string |
| 1348 | 1332 | */ |
| 1349 | - private function translateNthChild(array $match) | |
| 1350 | - { | |
| 1351 | - $parseResult = $this->parseNth($match); | |
| 1333 | + private function translateNthChild( array $match ) { | |
| 1334 | + $parseResult = $this->parseNth( $match ); | |
| 1352 | 1335 | |
| 1353 | - if (isset($parseResult[self::MULTIPLIER])) { | |
| 1354 | - if ($parseResult[self::MULTIPLIER] < 0) { | |
| 1355 | - $parseResult[self::MULTIPLIER] = abs($parseResult[self::MULTIPLIER]); | |
| 1356 | - $xPathExpression = sprintf( | |
| 1336 | + if ( isset( $parseResult[self::MULTIPLIER] ) ) { | |
| 1337 | + if ( $parseResult[self::MULTIPLIER] < 0 ) { | |
| 1338 | + $parseResult[self::MULTIPLIER] = abs( $parseResult[self::MULTIPLIER] ); | |
| 1339 | + $xPathExpression = sprintf( | |
| 1357 | 1340 | '*[(last() - position()) mod %u = %u]/self::%s', |
| 1358 | 1341 | $parseResult[self::MULTIPLIER], |
| 1359 | 1342 | $parseResult[self::INDEX], |
| 1360 | 1343 | $match[1] |
| 1361 | - ); | |
| 1344 | + ); | |
| 1362 | 1345 | } else { |
| 1363 | - $xPathExpression = sprintf( | |
| 1346 | + $xPathExpression = sprintf( | |
| 1364 | 1347 | '*[position() mod %u = %u]/self::%s', |
| 1365 | 1348 | $parseResult[self::MULTIPLIER], |
| 1366 | 1349 | $parseResult[self::INDEX], |
| 1367 | 1350 | $match[1] |
| 1368 | - ); | |
| 1351 | + ); | |
| 1369 | 1352 | } |
| 1370 | 1353 | } else { |
| 1371 | - $xPathExpression = sprintf('*[%u]/self::%s', $parseResult[self::INDEX], $match[1]); | |
| 1354 | + $xPathExpression = sprintf( '*[%u]/self::%s', $parseResult[self::INDEX], $match[1] ); | |
| 1372 | 1355 | } |
| 1373 | 1356 | |
| 1374 | 1357 | return $xPathExpression; |
| 1375 | 1358 | } |
| @@ -1378,31 +1361,30 @@ | ||
| 1378 | 1361 | * @param string[] $match |
| 1379 | 1362 | * |
| 1380 | 1363 | * @return string |
| 1381 | 1364 | */ |
| 1382 | - private function translateNthOfType(array $match) | |
| 1383 | - { | |
| 1384 | - $parseResult = $this->parseNth($match); | |
| 1365 | + private function translateNthOfType( array $match ) { | |
| 1366 | + $parseResult = $this->parseNth( $match ); | |
| 1385 | 1367 | |
| 1386 | - if (isset($parseResult[self::MULTIPLIER])) { | |
| 1387 | - if ($parseResult[self::MULTIPLIER] < 0) { | |
| 1388 | - $parseResult[self::MULTIPLIER] = abs($parseResult[self::MULTIPLIER]); | |
| 1389 | - $xPathExpression = sprintf( | |
| 1368 | + if ( isset( $parseResult[self::MULTIPLIER] ) ) { | |
| 1369 | + if ( $parseResult[self::MULTIPLIER] < 0 ) { | |
| 1370 | + $parseResult[self::MULTIPLIER] = abs( $parseResult[self::MULTIPLIER] ); | |
| 1371 | + $xPathExpression = sprintf( | |
| 1390 | 1372 | '%s[(last() - position()) mod %u = %u]', |
| 1391 | 1373 | $match[1], |
| 1392 | 1374 | $parseResult[self::MULTIPLIER], |
| 1393 | 1375 | $parseResult[self::INDEX] |
| 1394 | - ); | |
| 1376 | + ); | |
| 1395 | 1377 | } else { |
| 1396 | - $xPathExpression = sprintf( | |
| 1378 | + $xPathExpression = sprintf( | |
| 1397 | 1379 | '%s[position() mod %u = %u]', |
| 1398 | 1380 | $match[1], |
| 1399 | 1381 | $parseResult[self::MULTIPLIER], |
| 1400 | 1382 | $parseResult[self::INDEX] |
| 1401 | - ); | |
| 1383 | + ); | |
| 1402 | 1384 | } |
| 1403 | 1385 | } else { |
| 1404 | - $xPathExpression = sprintf('%s[%u]', $match[1], $parseResult[self::INDEX]); | |
| 1386 | + $xPathExpression = sprintf( '%s[%u]', $match[1], $parseResult[self::INDEX] ); | |
| 1405 | 1387 | } |
| 1406 | 1388 | |
| 1407 | 1389 | return $xPathExpression; |
| 1408 | 1390 | } |
| @@ -1411,41 +1393,43 @@ | ||
| 1411 | 1393 | * @param string[] $match |
| 1412 | 1394 | * |
| 1413 | 1395 | * @return int[] |
| 1414 | 1396 | */ |
| 1415 | - private function parseNth(array $match) | |
| 1416 | - { | |
| 1417 | - if (in_array(strtolower($match[2]), ['even', 'odd'], true)) { | |
| 1397 | + private function parseNth( array $match ) { | |
| 1398 | + if ( in_array( strtolower( $match[2] ), ['even', 'odd'], true ) ) { | |
| 1418 | 1399 | // we have "even" or "odd" |
| 1419 | - $index = strtolower($match[2]) === 'even' ? 0 : 1; | |
| 1400 | + $index = strtolower( $match[2] ) === 'even' ? 0 : 1; | |
| 1401 | + | |
| 1420 | 1402 | return [self::MULTIPLIER => 2, self::INDEX => $index]; |
| 1421 | 1403 | } |
| 1422 | - if (stripos($match[2], 'n') === false) { | |
| 1404 | + | |
| 1405 | + if ( stripos( $match[2], 'n' ) === false ) { | |
| 1423 | 1406 | // if there is a multiplier |
| 1424 | - $index = (int) str_replace(' ', '', $match[2]); | |
| 1407 | + $index = (int) str_replace( ' ', '', $match[2] ); | |
| 1408 | + | |
| 1425 | 1409 | return [self::INDEX => $index]; |
| 1426 | 1410 | } |
| 1427 | 1411 | |
| 1428 | - if (isset($match[3])) { | |
| 1429 | - $multipleTerm = str_replace($match[3], '', $match[2]); | |
| 1430 | - $index = (int) str_replace(' ', '', $match[3]); | |
| 1412 | + if ( isset( $match[3] ) ) { | |
| 1413 | + $multipleTerm = str_replace( $match[3], '', $match[2] ); | |
| 1414 | + $index = (int) str_replace( ' ', '', $match[3] ); | |
| 1431 | 1415 | } else { |
| 1432 | 1416 | $multipleTerm = $match[2]; |
| 1433 | - $index = 0; | |
| 1417 | + $index = 0; | |
| 1434 | 1418 | } |
| 1435 | 1419 | |
| 1436 | - $multiplier = str_ireplace('n', '', $multipleTerm); | |
| 1420 | + $multiplier = str_ireplace( 'n', '', $multipleTerm ); | |
| 1437 | 1421 | |
| 1438 | - if ($multiplier === '') { | |
| 1422 | + if ( $multiplier === '' ) { | |
| 1439 | 1423 | $multiplier = 1; |
| 1440 | - } elseif ($multiplier === '0') { | |
| 1424 | + } elseif ( $multiplier === '0' ) { | |
| 1441 | 1425 | return [self::INDEX => $index]; |
| 1442 | 1426 | } else { |
| 1443 | 1427 | $multiplier = (int) $multiplier; |
| 1444 | 1428 | } |
| 1445 | 1429 | |
| 1446 | - while ($index < 0) { | |
| 1447 | - $index += abs($multiplier); | |
| 1430 | + while ( $index < 0 ) { | |
| 1431 | + $index += abs( $multiplier ); | |
| 1448 | 1432 | } |
| 1449 | 1433 | |
| 1450 | 1434 | return [self::MULTIPLIER => $multiplier, self::INDEX => $index]; |
| 1451 | 1435 | } |
| @@ -1466,27 +1450,27 @@ | ||
| 1466 | 1450 | * |
| 1467 | 1451 | * @param string $cssDeclarationsBlock the CSS declarations block without the curly braces, may be empty |
| 1468 | 1452 | * |
| 1469 | 1453 | * @return string[] |
| 1470 | - * the CSS declarations with the property names as array keys and the property values as array values | |
| 1454 | + * the CSS declarations with the property names as array keys and the property values as array values | |
| 1471 | 1455 | */ |
| 1472 | - private function parseCssDeclarationsBlock($cssDeclarationsBlock) | |
| 1473 | - { | |
| 1474 | - if (isset($this->caches[self::CACHE_KEY_CSS_DECLARATIONS_BLOCK][$cssDeclarationsBlock])) { | |
| 1456 | + private function parseCssDeclarationsBlock( $cssDeclarationsBlock ) { | |
| 1457 | + if ( isset( $this->caches[self::CACHE_KEY_CSS_DECLARATIONS_BLOCK][$cssDeclarationsBlock] ) ) { | |
| 1475 | 1458 | return $this->caches[self::CACHE_KEY_CSS_DECLARATIONS_BLOCK][$cssDeclarationsBlock]; |
| 1476 | 1459 | } |
| 1477 | 1460 | |
| 1478 | - $properties = []; | |
| 1479 | - $declarations = preg_split('/;(?!base64|charset)/', $cssDeclarationsBlock); | |
| 1461 | + $properties = []; | |
| 1462 | + $declarations = preg_split( '/;(?!base64|charset)/', $cssDeclarationsBlock ); | |
| 1480 | 1463 | |
| 1481 | - foreach ($declarations as $declaration) { | |
| 1464 | + foreach ( $declarations as $declaration ) { | |
| 1482 | 1465 | $matches = []; |
| 1483 | - if (!preg_match('/^([A-Za-z\\-]+)\\s*:\\s*(.+)$/', trim($declaration), $matches)) { | |
| 1466 | + | |
| 1467 | + if ( !preg_match( '/^([A-Za-z\\-]+)\\s*:\\s*(.+)$/', trim( $declaration ), $matches ) ) { | |
| 1484 | 1468 | continue; |
| 1485 | 1469 | } |
| 1486 | 1470 | |
| 1487 | - $propertyName = strtolower($matches[1]); | |
| 1488 | - $propertyValue = $matches[2]; | |
| 1471 | + $propertyName = strtolower( $matches[1] ); | |
| 1472 | + $propertyValue = $matches[2]; | |
| 1489 | 1473 | $properties[$propertyName] = $propertyValue; |
| 1490 | 1474 | } |
| 1491 | 1475 | $this->caches[self::CACHE_KEY_CSS_DECLARATIONS_BLOCK][$cssDeclarationsBlock] = $properties; |
| 1492 | 1476 | |
| @@ -1499,13 +1483,13 @@ | ||
| 1499 | 1483 | * @param \DOMXPath $xPath |
| 1500 | 1484 | * |
| 1501 | 1485 | * @return \DOMElement[] |
| 1502 | 1486 | */ |
| 1503 | - private function getNodesToExclude(\DOMXPath $xPath) | |
| 1504 | - { | |
| 1487 | + private function getNodesToExclude( DOMXPath $xPath ) { | |
| 1505 | 1488 | $excludedNodes = []; |
| 1506 | - foreach (array_keys($this->excludedSelectors) as $selectorToExclude) { | |
| 1507 | - foreach ($xPath->query($this->translateCssToXpath($selectorToExclude)) as $node) { | |
| 1489 | + | |
| 1490 | + foreach ( array_keys( $this->excludedSelectors ) as $selectorToExclude ) { | |
| 1491 | + foreach ( $xPath->query( $this->translateCssToXpath( $selectorToExclude ) ) as $node ) { | |
| 1508 | 1492 | $excludedNodes[] = $node; |
| 1509 | 1493 | } |
| 1510 | 1494 | } |
| 1511 | 1495 | |
| @@ -1516,32 +1500,33 @@ | ||
| 1516 | 1500 | * Handles invalid xPath expression warnings, generated by process() method, |
| 1517 | 1501 | * during querying \DOMDocument and trigger \InvalidArgumentException |
| 1518 | 1502 | * with invalid selector. |
| 1519 | 1503 | * |
| 1520 | - * @param int $type | |
| 1504 | + * @param int $type | |
| 1521 | 1505 | * @param string $message |
| 1522 | 1506 | * @param string $file |
| 1523 | - * @param int $line | |
| 1524 | - * @param array $context | |
| 1507 | + * @param int $line | |
| 1525 | 1508 | * |
| 1526 | 1509 | * @return bool always false |
| 1527 | 1510 | * |
| 1528 | - * @throws \InvalidArgumentException | |
| 1511 | + * @throws InvalidArgumentException | |
| 1529 | 1512 | */ |
| 1530 | - public function handleXpathError($type, $message, $file, $line, array $context) | |
| 1531 | - { | |
| 1532 | - if ($type === E_WARNING && isset($context['cssRule']['selector'])) { | |
| 1533 | - throw new \InvalidArgumentException( | |
| 1534 | - sprintf( | |
| 1535 | - '%s in selector >> %s << in %s on line %s', | |
| 1536 | - $message, | |
| 1537 | - $context['cssRule']['selector'], | |
| 1538 | - $file, | |
| 1539 | - $line | |
| 1540 | - ) | |
| 1541 | - ); | |
| 1513 | + public function handleXpathError( $type, $message, $file, $line, array $context ) { | |
| 1514 | + if ( $this->debug && $type === E_WARNING && isset( $context['cssRule']['selector'] ) ) { | |
| 1515 | + throw new InvalidArgumentException( sprintf( '%s in selector >> %s << in %s on line %s', $message, $context['cssRule']['selector'], $file, $line ) ); | |
| 1542 | 1516 | } |
| 1543 | 1517 | |
| 1544 | 1518 | // the normal error handling continues when handler return false |
| 1545 | 1519 | return false; |
| 1546 | 1520 | } |
| 1547 | -} | |
| 1521 | + | |
| 1522 | + /** | |
| 1523 | + * Sets the debug mode. | |
| 1524 | + * | |
| 1525 | + * @param bool $debug set to true to enable debug mode | |
| 1526 | + * | |
| 1527 | + * @return void | |
| 1528 | + */ | |
| 1529 | + public function setDebug( $debug ) { | |
| 1530 | + $this->debug = $debug; | |
| 1531 | + } | |
| 1532 | +} | |