Completed
Push — master ( f68711...7035f9 )
by Lars
02:28
created

CssToInlineStyles   D

Complexity

Total Complexity 90

Size/Duplication

Total Lines 890
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 99.12%

Importance

Changes 35
Bugs 12 Features 1
Metric Value
wmc 90
c 35
b 12
f 1
lcom 1
cbo 4
dl 0
loc 890
ccs 224
cts 226
cp 0.9912
rs 4.4444

25 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 4
A setHTML() 0 7 1
A setCSS() 0 6 1
A sortOnSpecificity() 0 12 2
C convert() 0 66 11
A getCssFromInlineHtmlStyleBlock() 0 19 3
B processCSS() 0 71 5
B doCleanup() 0 27 3
A stripeMediaQueries() 0 7 1
A stripeCharsetInCss() 0 4 1
B processCSSProperties() 0 38 5
B splitIntoProperties() 0 22 4
D createXPath() 0 115 23
C createPropertyChunks() 0 47 8
B splitStyleIntoChunks() 0 30 5
A stripOriginalStyleTags() 0 22 3
A cleanupHTML() 0 7 2
A setCleanup() 0 6 1
A setEncoding() 0 6 1
A setUseInlineStylesBlock() 0 6 1
A setLoadCSSFromHTML() 0 6 1
A setStripOriginalStyleTags() 0 6 1
A setExcludeMediaQueries() 0 6 1
A setExcludeCssCharset() 0 6 1
A setExcludeConditionalInlineStylesBlock() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like CssToInlineStyles often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CssToInlineStyles, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace voku\CssToInlineStyles;
4
5
use Symfony\Component\CssSelector\CssSelectorConverter;
6
use Symfony\Component\CssSelector\Exception\ExceptionInterface;
7
use voku\helper\HtmlDomParser;
8
9
/**
10
 * CSS to Inline Styles class
11
 *
12
 * @author     Tijs Verkoyen <[email protected]>
13
 */
14
class CssToInlineStyles
15
{
16
17
  /**
18
   * @var CssSelectorConverter
19
   */
20
  private $cssConverter;
21
22
  /**
23
   * regular expression: css media queries
24
   *
25
   * @var string
26
   */
27
  private static $cssMediaQueriesRegEx = '#@media\\s+(?:only\\s)?(?:[\\s{\\(]|screen|all)\\s?[^{]+{.*}\\s*}\\s*#misU';
28
29
  /**
30
   * regular expression: css charset
31
   *
32
   * @var string
33
   */
34
  private static $cssCharsetRegEx = '/@charset [\'"][^\'"]+[\'"];/i';
35
36
  /**
37
   * regular expression: conditional inline style tags
38
   *
39
   * @var string
40
   */
41
  private static $excludeConditionalInlineStylesBlockRegEx = '/<!--.*<style.*-->/isU';
42
43
  /**
44
   * regular expression: inline style tags
45
   *
46
   * @var string
47
   */
48
  private static $styleTagRegEx = '|<style(.*)>(.*)</style>|isU';
49
50
  /**
51
   * regular expression: css-comments
52
   *
53
   * @var string
54
   */
55
  private static $styleCommentRegEx = '/\\/\\*.*\\*\\//sU';
56
57
  /**
58
   * The CSS to use
59
   *
60
   * @var  string
61
   */
62
  private $css;
63
64
  /**
65
   * Should the generated HTML be cleaned
66
   *
67
   * @var  bool
68
   */
69
  private $cleanup = false;
70
71
  /**
72
   * The encoding to use.
73
   *
74
   * @var  string
75
   */
76
  private $encoding = 'UTF-8';
77
78
  /**
79
   * The HTML to process
80
   *
81
   * @var  string
82
   */
83
  private $html;
84
85
  /**
86
   * Use inline-styles block as CSS
87
   *
88
   * @var bool
89
   */
90
  private $useInlineStylesBlock = false;
91
92
  /**
93
   * Use link block reference as CSS
94
   *
95
   * @var bool
96
   */
97
  private $loadCSSFromHTML = false;
98
99
  /**
100
   * Strip original style tags
101
   *
102
   * @var bool
103
   */
104
  private $stripOriginalStyleTags = false;
105
106
  /**
107
   * Exclude conditional inline-style blocks
108
   *
109
   * @var bool
110
   */
111
  private $excludeConditionalInlineStylesBlock = true;
112
113
  /**
114
   * Exclude media queries from "$this->css" and keep media queries for inline-styles blocks
115
   *
116
   * @var bool
117
   */
118
  private $excludeMediaQueries = true;
119
120
  /**
121
   * Exclude media queries from "$this->css" and keep media queries for inline-styles blocks
122
   *
123
   * @var bool
124
   */
125
  private $excludeCssCharset = true;
126
127
  /**
128
   * Creates an instance, you could set the HTML and CSS here, or load it
129
   * later.
130
   *
131
   * @param  null|string $html The HTML to process.
132
   * @param  null|string $css  The CSS to use.
133
   */
134 52
  public function __construct($html = null, $css = null)
135
  {
136 52
    if (null !== $html) {
137 2
      $this->setHTML($html);
138
    }
139
140 52
    if (null !== $css) {
141 2
      $this->setCSS($css);
142
    }
143
144 52
    if (class_exists('Symfony\Component\CssSelector\CssSelectorConverter')) {
145 52
      $this->cssConverter = new CssSelectorConverter();
146
    }
147 52
  }
148
149
  /**
150
   * Set HTML to process
151
   *
152
   * @param  string $html The HTML to process.
153
   *
154
   * @return $this
155
   */
156 50
  public function setHTML($html)
157
  {
158
    // strip style definitions, if we use css-class "cleanup" on a style-element
159 50
    $this->html = (string)preg_replace('/<style[^>]+class="cleanup"[^>]*>.*<\/style>/Usi', ' ', $html);
160
161 50
    return $this;
162
  }
163
164
  /**
165
   * Set CSS to use
166
   *
167
   * @param  string $css The CSS to use.
168
   *
169
   * @return $this
170
   */
171 46
  public function setCSS($css)
172
  {
173 46
    $this->css = (string)$css;
174
175 46
    return $this;
176
  }
177
178
  /**
179
   * Sort an array on the specificity element
180
   *
181
   * @return int
182
   *
183
   * @param Specificity[] $e1 The first element.
184
   * @param Specificity[] $e2 The second element.
185
   */
186 18
  private static function sortOnSpecificity($e1, $e2)
187
  {
188
    // Compare the specificity
189 18
    $value = $e1['specificity']->compareTo($e2['specificity']);
190
191
    // if the specificity is the same, use the order in which the element appeared
192 18
    if (0 === $value) {
193 13
      $value = $e1['order'] - $e2['order'];
194
    }
195
196 18
    return $value;
197
  }
198
199
  /**
200
   * Converts the loaded HTML into an HTML-string with inline styles based on the loaded CSS
201
   *
202
   * @param bool $outputXHTML                             [optional] Should we output valid XHTML?
203
   * @param int  $libXMLOptions                           [optional] $libXMLOptions Since PHP 5.4.0 and Libxml 2.6.0,
204
   *                                                      you may also use the options parameter to specify additional
205
   *                                                      Libxml parameters. Recommend these options:
206
   *                                                      LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
207
   * @param bool $path                                    [optional] Set the path to your external css-files.
208
   *
209
   * @return string
210
   *
211
   * @throws Exception
212
   */
213 50
  public function convert($outputXHTML = false, $libXMLOptions = 0, $path = false)
0 ignored issues
show
Unused Code introduced by
The parameter $libXMLOptions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
214
  {
215
    // redefine
216 50
    $outputXHTML = (bool)$outputXHTML;
217
218
    // validate
219 50
    if (!$this->html) {
220 1
      throw new Exception('No HTML provided.');
221
    }
222
223
    // use local variables
224 49
    $css = $this->css;
225
226
    // create new HtmlDomParser
227 49
    $dom = HtmlDomParser::str_get_html($this->html);
0 ignored issues
show
Unused Code introduced by
The call to HtmlDomParser::str_get_html() has too many arguments starting with $this->html.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
228
229
    // check if there is some link css reference
230 49
    if ($this->loadCSSFromHTML) {
231 1
      foreach ($dom->find('link') as $node) {
232
233
        /** @noinspection PhpUndefinedMethodInspection */
234 1
        $file = ($path ?: __DIR__) . '/' . $node->getAttribute('href');
235
236 1
        if (file_exists($file)) {
237 1
          $css .= file_get_contents($file);
238
239
          // converting to inline css because we don't need/want to load css files, so remove the link
240 1
          $node->outertext = '';
241
        }
242
      }
243
    }
244
245
    // should we use inline style-block
246 49
    if ($this->useInlineStylesBlock) {
247
248 27
      if (true === $this->excludeConditionalInlineStylesBlock) {
249 23
        $this->html = preg_replace(self::$excludeConditionalInlineStylesBlockRegEx, '', $this->html);
250
      }
251
252 27
      $css .= $this->getCssFromInlineHtmlStyleBlock($this->html);
253
    }
254
255
    // process css
256 49
    $cssRules = $this->processCSS($css);
257
258
    // create new XPath
259 49
    $xPath = $this->createXPath($dom->getDocument(), $cssRules);
260
261
    // strip original style tags if we need to
262 49
    if ($this->stripOriginalStyleTags === true) {
263 13
      $this->stripOriginalStyleTags($xPath);
264
    }
265
266
    // cleanup the HTML if we need to
267 49
    if (true === $this->cleanup) {
268 3
      $this->cleanupHTML($xPath);
269
    }
270
271
    // should we output XHTML?
272 49
    if (true === $outputXHTML) {
273 6
      return $dom->xml();
274
    }
275
276
    // just regular HTML 4.01 as it should be used in newsletters
277 44
    return $dom->html();
278
  }
279
280
  /**
281
   * get css from inline-html style-block
282
   *
283
   * @param string $html
284
   *
285
   * @return string
286
   */
287 29
  public function getCssFromInlineHtmlStyleBlock($html)
288
  {
289
    // init var
290 29
    $css = '';
291 29
    $matches = array();
292
293
    // match the style blocks
294 29
    preg_match_all(self::$styleTagRegEx, $html, $matches);
295
296
    // any style-blocks found?
297 29
    if (!empty($matches[2])) {
298
      // add
299 28
      foreach ($matches[2] as $match) {
300 28
        $css .= trim($match) . "\n";
301
      }
302
    }
303
304 29
    return $css;
305
  }
306
307
  /**
308
   * Process the loaded CSS
309
   *
310
   * @param string $css
311
   *
312
   * @return array
313
   */
314 49
  private function processCSS($css)
315
  {
316
    //reset current set of rules
317 49
    $cssRules = array();
318
319
    // init vars
320 49
    $css = (string)$css;
321
322 49
    $css = $this->doCleanup($css);
323
324
    // rules are splitted by }
325 49
    $rules = (array)explode('}', $css);
326
327
    // init var
328 49
    $i = 1;
329
330
    // loop rules
331 49
    foreach ($rules as $rule) {
332
      // split into chunks
333 49
      $chunks = explode('{', $rule);
334
335
      // invalid rule?
336 49
      if (!isset($chunks[1])) {
337 49
        continue;
338
      }
339
340
      // set the selectors
341 36
      $selectors = trim($chunks[0]);
342
343
      // get cssProperties
344 36
      $cssProperties = trim($chunks[1]);
345
346
      // split multiple selectors
347 36
      $selectors = (array)explode(',', $selectors);
348
349
      // loop selectors
350 36
      foreach ($selectors as $selector) {
351
        // cleanup
352 36
        $selector = trim($selector);
353
354
        // build an array for each selector
355 36
        $ruleSet = array();
356
357
        // store selector
358 36
        $ruleSet['selector'] = $selector;
359
360
        // process the properties
361 36
        $ruleSet['properties'] = $this->processCSSProperties($cssProperties);
362
363
364
        // calculate specificity
365 36
        $ruleSet['specificity'] = Specificity::fromSelector($selector);
366
367
        // remember the order in which the rules appear
368 36
        $ruleSet['order'] = $i;
369
370
        // add into rules
371 36
        $cssRules[] = $ruleSet;
372
373
        // increment
374 36
        $i++;
375
      }
376
    }
377
378
    // sort based on specificity
379 49
    if (0 !== count($cssRules)) {
380 36
      usort($cssRules, array(__CLASS__, 'sortOnSpecificity'));
381
    }
382
383 49
    return $cssRules;
384
  }
385
386
  /**
387
   * @param string $css
388
   *
389
   * @return string
390
   */
391 49
  private function doCleanup($css)
392
  {
393
    // remove newlines & replace double quotes by single quotes
394 49
    $css = str_replace(
395 49
        array("\r", "\n", '"'),
396 49
        array('', '', '\''),
397
        $css
398
    );
399
400
    // remove comments
401 49
    $css = preg_replace(self::$styleCommentRegEx, '', $css);
402
403
    // remove spaces
404 49
    $css = preg_replace('/\s\s+/', ' ', $css);
405
406
    // remove css charset
407 49
    if (true === $this->excludeCssCharset) {
408 49
      $css = $this->stripeCharsetInCss($css);
409
    }
410
411
    // remove css media queries
412 49
    if (true === $this->excludeMediaQueries) {
413 48
      $css = $this->stripeMediaQueries($css);
414
    }
415
416 49
    return (string)$css;
417
  }
418
419
  /**
420
   * remove css media queries from the string
421
   *
422
   * @param string $css
423
   *
424
   * @return string
425
   */
426 48
  private function stripeMediaQueries($css)
427
  {
428
    // remove comments previously to matching media queries
429 48
    $css = preg_replace(self::$styleCommentRegEx, '', $css);
430
431 48
    return (string)preg_replace(self::$cssMediaQueriesRegEx, '', $css);
432
  }
433
434
  /**
435
   * remove charset from the string
436
   *
437
   * @param string $css
438
   *
439
   * @return string
440
   */
441 49
  private function stripeCharsetInCss($css)
442
  {
443 49
    return (string)preg_replace(self::$cssCharsetRegEx, '', $css);
444
  }
445
446
  /**
447
   * Process the CSS-properties
448
   *
449
   * @return array
450
   *
451
   * @param  string $propertyString The CSS-properties.
452
   */
453 36
  private function processCSSProperties($propertyString)
454
  {
455
    // split into chunks
456 36
    $properties = $this->splitIntoProperties($propertyString);
457
458
    // init var
459 36
    $pairs = array();
460
461
    // loop properties
462 36
    foreach ($properties as $property) {
463
      // split into chunks
464 36
      $chunks = (array)explode(':', $property, 2);
465
466
      // validate
467 36
      if (!isset($chunks[1])) {
468 30
        continue;
469
      }
470
471
      // cleanup
472 35
      $chunks[0] = trim($chunks[0]);
473 35
      $chunks[1] = trim($chunks[1]);
474
475
      // add to pairs array
476
      if (
477 35
          !isset($pairs[$chunks[0]])
478
          ||
479 35
          !in_array($chunks[1], $pairs[$chunks[0]], true)
480
      ) {
481 35
        $pairs[$chunks[0]][] = $chunks[1];
482
      }
483
    }
484
485
    // sort the pairs
486 36
    ksort($pairs);
487
488
    // return
489 36
    return $pairs;
490
  }
491
492
  /**
493
   * Split a style string into an array of properties.
494
   * The returned array can contain empty strings.
495
   *
496
   * @param string $styles ex: 'color:blue;font-size:12px;'
497
   *
498
   * @return array an array of strings containing css property ex: array('color:blue','font-size:12px')
499
   */
500 36
  private function splitIntoProperties($styles)
501
  {
502 36
    $properties = (array)explode(';', $styles);
503 36
    $propertiesCount = count($properties);
504
505
    /** @noinspection ForeachInvariantsInspection */
506 36
    for ($i = 0; $i < $propertiesCount; $i++) {
507
      // If next property begins with base64,
508
      // Then the ';' was part of this property (and we should not have split on it).
509
      if (
510 36
          isset($properties[$i + 1])
511
          &&
512 36
          strpos($properties[$i + 1], 'base64,') !== false
513
      ) {
514 1
        $properties[$i] .= ';' . $properties[$i + 1];
515 1
        $properties[$i + 1] = '';
516 1
        ++$i;
517
      }
518
    }
519
520 36
    return $properties;
521
  }
522
523
  /**
524
   * create XPath
525
   *
526
   * @param \DOMDocument $document
527
   * @param array        $cssRules
528
   *
529
   * @return \DOMXPath
530
   */
531 49
  private function createXPath(\DOMDocument $document, array $cssRules)
532
  {
533 49
    $xPath = new \DOMXPath($document);
534
535
    // any rules?
536 49
    if (0 !== count($cssRules)) {
537
      // loop rules
538 36
      foreach ($cssRules as $rule) {
539
540 36
        $ruleSelector = $rule['selector'];
541 36
        $ruleProperties = $rule['properties'];
542
543 36
        if (!$ruleSelector || !$ruleProperties) {
544 3
          continue;
545
        }
546
547
        try {
548 35
          $query = $this->cssConverter->toXPath($ruleSelector);
549 3
        } catch (ExceptionInterface $e) {
550 3
          $query = null;
551
        }
552
553
        // validate query
554 35
        if (null === $query) {
555 3
          continue;
556
        }
557
558
        // search elements
559 34
        $elements = $xPath->query($query);
560
561
        // validate elements
562 34
        if (false === $elements) {
563
          continue;
564
        }
565
566
        // loop found elements
567 34
        foreach ($elements as $element) {
568
569
          /**
570
           * @var $element \DOMElement
571
           */
572
573
          if (
574 34
              $ruleSelector == '*'
575
              &&
576
              (
577 1
                  $element->tagName == 'html'
578 1
                  || $element->tagName === 'title'
579 1
                  || $element->tagName == 'meta'
580 1
                  || $element->tagName == 'head'
581 1
                  || $element->tagName == 'style'
582 1
                  || $element->tagName == 'script'
583 34
                  || $element->tagName == 'link'
584
              )
585
          ) {
586 1
            continue;
587
          }
588
589
          // no styles stored?
590 34
          if (null === $element->attributes->getNamedItem('data-css-to-inline-styles-original-styles')) {
591
592
            // init var
593 34
            $originalStyle = '';
594
595 34
            if (null !== $element->attributes->getNamedItem('style')) {
596
              /** @noinspection PhpUndefinedFieldInspection */
597 9
              $originalStyle = $element->attributes->getNamedItem('style')->value;
598
            }
599
600
            // store original styles
601 34
            $element->setAttribute('data-css-to-inline-styles-original-styles', $originalStyle);
602
603
            // clear the styles
604 34
            $element->setAttribute('style', '');
605
          }
606
607 34
          $propertiesString = $this->createPropertyChunks($element, $ruleProperties);
608
609
          // set attribute
610 34
          if ('' != $propertiesString) {
611 34
            $element->setAttribute('style', $propertiesString);
612
          }
613
        }
614
      }
615
616
      // reapply original styles
617
      // search elements
618 36
      $elements = $xPath->query('//*[@data-css-to-inline-styles-original-styles]');
619
620
      // loop found elements
621 36
      foreach ($elements as $element) {
622
        // get the original styles
623
        /** @noinspection PhpUndefinedFieldInspection */
624 34
        $originalStyle = $element->attributes->getNamedItem('data-css-to-inline-styles-original-styles')->value;
625
626 34
        if ('' != $originalStyle) {
627 9
          $originalStyles = $this->splitIntoProperties($originalStyle);
628
629 9
          $originalProperties = $this->splitStyleIntoChunks($originalStyles);
630
631 9
          $propertiesString = $this->createPropertyChunks($element, $originalProperties);
632
633
          // set attribute
634 9
          if ('' != $propertiesString) {
635 9
            $element->setAttribute('style', $propertiesString);
636
          }
637
        }
638
639
        // remove placeholder
640 34
        $element->removeAttribute('data-css-to-inline-styles-original-styles');
641
      }
642
    }
643
644 49
    return $xPath;
645
  }
646
647
  /**
648
   * @param \DOMElement $element
649
   * @param array       $ruleProperties
650
   *
651
   * @return string
652
   */
653 34
  private function createPropertyChunks(\DOMElement $element, array $ruleProperties)
654
  {
655
    // init var
656 34
    $properties = array();
657
658
    // get current styles
659 34
    $stylesAttribute = $element->attributes->getNamedItem('style');
660
661
    // any styles defined before?
662 34
    if (null !== $stylesAttribute) {
663
      // get value for the styles attribute
664
      /** @noinspection PhpUndefinedFieldInspection */
665 34
      $definedStyles = (string)$stylesAttribute->value;
666
667
      // split into properties
668 34
      $definedProperties = $this->splitIntoProperties($definedStyles);
669
670 34
      $properties = $this->splitStyleIntoChunks($definedProperties);
671
    }
672
673
    // add new properties into the list
674 34
    foreach ($ruleProperties as $key => $value) {
675
      // If one of the rules is already set and is !important, don't apply it,
676
      // except if the new rule is also important.
677
      if (
678 34
          !isset($properties[$key])
679
          ||
680 10
          false === stripos($properties[$key], '!important')
681
          ||
682 34
          false !== stripos(implode('', (array)$value), '!important')
683
      ) {
684 34
        $properties[$key] = $value;
685
      }
686
    }
687
688
    // build string
689 34
    $propertyChunks = array();
690
691
    // build chunks
692 34
    foreach ($properties as $key => $values) {
693 34
      foreach ((array)$values as $value) {
694 34
        $propertyChunks[] = $key . ': ' . $value . ';';
695
      }
696
    }
697
698 34
    return implode(' ', $propertyChunks);
699
  }
700
701
  /**
702
   * @param array $definedProperties
703
   *
704
   * @return array
705
   */
706 34
  private function splitStyleIntoChunks(array $definedProperties)
707
  {
708
    // init var
709 34
    $properties = array();
710
711
    // loop properties
712 34
    foreach ($definedProperties as $property) {
713
      // validate property
714
      if (
715 34
          !$property
716
          ||
717 34
          strpos($property, ':') === false
718
      ) {
719 34
        continue;
720
      }
721
722
      // split into chunks
723 17
      $chunks = (array)explode(':', trim($property), 2);
724
725
      // validate
726 17
      if (!isset($chunks[1])) {
727
        continue;
728
      }
729
730
      // loop chunks
731 17
      $properties[$chunks[0]] = trim($chunks[1]);
732
    }
733
734 34
    return $properties;
735
  }
736
737
  /**
738
   * Strip style tags into the generated HTML.
739
   *
740
   * @param  \DOMXPath $xPath The DOMXPath for the entire document.
741
   */
742 13
  private function stripOriginalStyleTags(\DOMXPath $xPath)
743
  {
744
    // get all style tags
745 13
    $nodes = $xPath->query('descendant-or-self::style');
746 13
    foreach ($nodes as $node) {
747 12
      if ($this->excludeMediaQueries === true) {
748
749
        // remove comments previously to matching media queries
750 11
        $node->nodeValue = preg_replace(self::$styleCommentRegEx, '', $node->nodeValue);
751
752
        // search for Media Queries
753 11
        preg_match_all(self::$cssMediaQueriesRegEx, $node->nodeValue, $mqs);
754
755
        // replace the nodeValue with just the Media Queries
756 11
        $node->nodeValue = implode("\n", $mqs[0]);
757
758
      } else {
759
        // remove the entire style tag
760 12
        $node->parentNode->removeChild($node);
761
      }
762
    }
763 13
  }
764
765
  /**
766
   * Remove id and class attributes.
767
   *
768
   * @param  \DOMXPath $xPath The DOMXPath for the entire document.
769
   */
770 3
  private function cleanupHTML(\DOMXPath $xPath)
771
  {
772 3
    $nodes = $xPath->query('//@class | //@id');
773 3
    foreach ($nodes as $node) {
774 3
      $node->ownerElement->removeAttributeNode($node);
775
    }
776 3
  }
777
778
  /**
779
   * Should the IDs and classes be removed?
780
   *
781
   * @param  bool $on Should we enable cleanup?
782
   *
783
   * @return $this
784
   */
785 3
  public function setCleanup($on = true)
786
  {
787 3
    $this->cleanup = (bool)$on;
788
789 3
    return $this;
790
  }
791
792
  /**
793
   * Set the encoding to use with the DOMDocument
794
   *
795
   * @param  string $encoding The encoding to use.
796
   *
797
   * @return $this
798
   *
799
   * @deprecated Doesn't have any effect
800
   */
801
  public function setEncoding($encoding)
802
  {
803
    $this->encoding = (string)$encoding;
804
805
    return $this;
806
  }
807
808
  /**
809
   * Set use of inline styles block.
810
   *
811
   * Info: If this is enabled the class will use the style-block in the HTML.
812
   *
813
   * @param  bool $on Should we process inline styles?
814
   *
815
   * @return $this
816
   */
817 27
  public function setUseInlineStylesBlock($on = true)
818
  {
819 27
    $this->useInlineStylesBlock = (bool)$on;
820
821 27
    return $this;
822
  }
823
824
  /**
825
   * Set use of inline link block.
826
   *
827
   * Info: If this is enabled the class will use the links reference in the HTML.
828
   *
829
   * @param  bool [optional] $on Should we process link styles?
830
   *
831
   * @return $this
832
   */
833 2
  public function setLoadCSSFromHTML($on = true)
834
  {
835 2
    $this->loadCSSFromHTML = (bool)$on;
836
837 2
    return $this;
838
  }
839
840
  /**
841
   * Set strip original style tags.
842
   *
843
   * Info: If this is enabled the class will remove all style tags in the HTML.
844
   *
845
   * @param  bool $on Should we process inline styles?
846
   *
847
   * @return $this
848
   */
849 17
  public function setStripOriginalStyleTags($on = true)
850
  {
851 17
    $this->stripOriginalStyleTags = (bool)$on;
852
853 17
    return $this;
854
  }
855
856
  /**
857
   * Set exclude media queries.
858
   *
859
   * Info: If this is enabled the media queries will be removed before inlining the rules.
860
   *
861
   * WARNING: If you use inline styles block "<style>" the this option will keep the media queries.
862
   *
863
   * @param bool $on
864
   *
865
   * @return $this
866
   */
867 14
  public function setExcludeMediaQueries($on = true)
868
  {
869 14
    $this->excludeMediaQueries = (bool)$on;
870
871 14
    return $this;
872
  }
873
874
  /**
875
   * Set exclude charset.
876
   *
877
   * @param bool $on
878
   *
879
   * @return $this
880
   */
881 1
  public function setExcludeCssCharset($on = true)
882
  {
883 1
    $this->excludeCssCharset = (bool)$on;
884
885 1
    return $this;
886
  }
887
888
  /**
889
   * Set exclude conditional inline-style blocks.
890
   *
891
   * e.g.: <!--[if gte mso 9]><style>.foo { bar } </style><![endif]-->
892
   *
893
   * @param bool $on
894
   *
895
   * @return $this
896
   */
897 6
  public function setExcludeConditionalInlineStylesBlock($on = true)
898
  {
899 6
    $this->excludeConditionalInlineStylesBlock = (bool)$on;
900
901 6
    return $this;
902
  }
903
}
904