PluginProbe
weForms – Easy Drag & Drop Contact Form Builder For WordPress / 1.4.7
weForms – Easy Drag & Drop Contact Form Builder For WordPress v1.4.7
1.6.7 1.6.8 1.6.9 1.6.12 1.6.13 1.6.14 1.6.15 1.6.16 1.6.17 1.6.18 1.6.19 1.6.2 1.6.20 1.6.21 1.6.22 1.6.23 1.6.24 1.6.25 1.6.26 1.6.27 1.6.28 1.6.3 1.6.4 1.6.5 1.6.6 All 74 releases
← All changes | includes/library/Emogrifier.php +429 -444 1.6.121.4.7 View file →
@@ -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 +}