Completed
Push — master ( 4a0b77...2794e7 )
by Lars
04:40
created

CssToInlineStyles   D

Complexity

Total Complexity 90

Size/Duplication

Total Lines 883
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 99.12%

Importance

Changes 1
Bugs 1 Features 0
Metric Value
wmc 90
c 1
b 1
f 0
lcom 1
cbo 4
dl 0
loc 883
ccs 225
cts 227
cp 0.9912
rs 4.4444

25 Methods

Rating   Name   Duplication   Size   Complexity  
A setHTML() 0 7 1
A setCSS() 0 6 1
A sortOnSpecificity() 0 12 2
A cleanupHTML() 0 7 2
A setEncoding() 0 6 1
A __construct() 0 14 4
C convert() 0 65 11
A getCssFromInlineHtmlStyleBlock() 0 19 3
B processCSS() 0 70 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 103 23
C createPropertyChunks() 0 47 8
B splitStyleIntoChunks() 0 30 5
A stripOriginalStyleTags() 0 22 3
A setCleanup() 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(?:\s.*)?>(.*)</style>|isU';
49
50
  /**
51
   * regular expression: style-tag with 'cleanup'-css-class
52
   *
53
   * @var string
54
   */
55
  private static $styleTagWithCleanupClassRegEx = '|<style[^>]+class="cleanup"[^>]*>.*</style>|isU';
56
57
  /**
58
   * regular expression: css-comments
59
   *
60
   * @var string
61
   */
62
  private static $styleCommentRegEx = '/\\/\\*.*\\*\\//sU';
63
64
  /**
65
   * The CSS to use
66
   *
67
   * @var  string
68
   */
69
  private $css;
70
71
  /**
72
   * Should the generated HTML be cleaned
73
   *
74
   * @var  bool
75
   */
76
  private $cleanup = false;
77
78
  /**
79
   * The encoding to use.
80
   *
81
   * @var  string
82
   */
83
  private $encoding = 'UTF-8';
84
85
  /**
86
   * The HTML to process
87
   *
88
   * @var  string
89
   */
90
  private $html;
91
92
  /**
93
   * Use inline-styles block as CSS
94
   *
95
   * @var bool
96
   */
97
  private $useInlineStylesBlock = false;
98
99
  /**
100
   * Use link block reference as CSS
101
   *
102
   * @var bool
103
   */
104
  private $loadCSSFromHTML = false;
105
106
  /**
107
   * Strip original style tags
108
   *
109
   * @var bool
110
   */
111
  private $stripOriginalStyleTags = false;
112
113
  /**
114
   * Exclude conditional inline-style blocks
115
   *
116
   * @var bool
117
   */
118
  private $excludeConditionalInlineStylesBlock = 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 $excludeMediaQueries = true;
126
127
  /**
128
   * Exclude media queries from "$this->css" and keep media queries for inline-styles blocks
129
   *
130
   * @var bool
131
   */
132
  private $excludeCssCharset = true;
133
134
  /**
135
   * Creates an instance, you could set the HTML and CSS here, or load it
136
   * later.
137
   *
138
   * @param  null|string $html The HTML to process.
139
   * @param  null|string $css  The CSS to use.
140
   */
141 55
  public function __construct($html = null, $css = null)
142
  {
143 55
    if (null !== $html) {
144 2
      $this->setHTML($html);
145
    }
146
147 55
    if (null !== $css) {
148 2
      $this->setCSS($css);
149
    }
150
151 55
    if (class_exists('Symfony\Component\CssSelector\CssSelectorConverter')) {
152 55
      $this->cssConverter = new CssSelectorConverter();
153
    }
154 55
  }
155
156
  /**
157
   * Set HTML to process
158
   *
159
   * @param  string $html The HTML to process.
160
   *
161
   * @return $this
162
   */
163 53
  public function setHTML($html)
164
  {
165
    // strip style definitions, if we use css-class "cleanup" on a style-element
166 53
    $this->html = (string)preg_replace(self::$styleTagWithCleanupClassRegEx, ' ', $html);
167
168 53
    return $this;
169
  }
170
171
  /**
172
   * Set CSS to use
173
   *
174
   * @param  string $css The CSS to use.
175
   *
176
   * @return $this
177
   */
178 48
  public function setCSS($css)
179
  {
180 48
    $this->css = (string)$css;
181
182 48
    return $this;
183
  }
184
185
  /**
186
   * Sort an array on the specificity element in an ascending way
187
   * Lower specificity will be sorted to the beginning of the array
188
   *
189
   * @param Specificity[] $e1 The first element.
190
   * @param Specificity[] $e2 The second element.
191
   *
192
   * @return int
193
   */
194 22
  private static function sortOnSpecificity($e1, $e2)
195
  {
196
    // Compare the specificity
197 22
    $value = $e1['specificity']->compareTo($e2['specificity']);
198
199
    // if the specificity is the same, use the order in which the element appeared
200 22
    if (0 === $value) {
201 17
      $value = $e1['order'] - $e2['order'];
202
    }
203
204 22
    return $value;
205
  }
206
207
  /**
208
   * Converts the loaded HTML into an HTML-string with inline styles based on the loaded CSS.
209
   *
210
   * @param bool $outputXHTML            [optional] Should we output valid XHTML?
211
   * @param int|null $libXMLExtraOptions [optional] $libXMLExtraOptions Since PHP 5.4.0 and Libxml 2.6.0,
212
   *                                     you may also use the options parameter to specify additional
213
   *                                     Libxml parameters.
214
   * @param bool $path                   [optional] Set the path to your external css-files.
215
   *
216
   * @return string
217
   *
218
   * @throws Exception
219
   */
220 53
  public function convert($outputXHTML = false, $libXMLExtraOptions = null, $path = false)
221
  {
222
    // init
223 53
    $outputXHTML = (bool)$outputXHTML;
224
225
    // validate
226 53
    if (!$this->html) {
227 1
      throw new Exception('No HTML provided.');
228
    }
229
230
    // use local variables
231 52
    $css = $this->css;
232
233
    // create new HtmlDomParser
234 52
    $dom = HtmlDomParser::str_get_html($this->html, $libXMLExtraOptions);
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...
235
236
    // check if there is some link css reference
237 52
    if ($this->loadCSSFromHTML) {
238 1
      foreach ($dom->find('link') as $node) {
239
240 1
        $file = ($path ?: __DIR__) . '/' . $node->getAttribute('href');
241
242 1
        if (file_exists($file)) {
243 1
          $css .= file_get_contents($file);
244
245
          // converting to inline css because we don't need/want to load css files, so remove the link
246 1
          $node->outertext = '';
247
        }
248
      }
249
    }
250
251
    // should we use inline style-block
252 52
    if ($this->useInlineStylesBlock) {
253
254 30
      if (true === $this->excludeConditionalInlineStylesBlock) {
255 26
        $this->html = preg_replace(self::$excludeConditionalInlineStylesBlockRegEx, '', $this->html);
256
      }
257
258 30
      $css .= $this->getCssFromInlineHtmlStyleBlock($this->html);
259
    }
260
261
    // process css
262 52
    $cssRules = $this->processCSS($css);
263
264
    // create new XPath
265 52
    $xPath = $this->createXPath($dom->getDocument(), $cssRules);
266
267
    // strip original style tags if we need to
268 52
    if ($this->stripOriginalStyleTags === true) {
269 13
      $this->stripOriginalStyleTags($xPath);
270
    }
271
272
    // cleanup the HTML if we need to
273 52
    if (true === $this->cleanup) {
274 3
      $this->cleanupHTML($xPath);
275
    }
276
277
    // should we output XHTML?
278 52
    if (true === $outputXHTML) {
279 6
      return $dom->xml();
280
    }
281
282
    // just regular HTML 4.01 as it should be used in newsletters
283 47
    return $dom->html();
284
  }
285
286
  /**
287
   * get css from inline-html style-block
288
   *
289
   * @param string $html
290
   *
291
   * @return string
292
   */
293 32
  public function getCssFromInlineHtmlStyleBlock($html)
294
  {
295
    // init var
296 32
    $css = '';
297 32
    $matches = array();
298
299
    // match the style blocks
300 32
    preg_match_all(self::$styleTagRegEx, $html, $matches);
301
302
    // any style-blocks found?
303 32
    if (!empty($matches[1])) {
304
      // add
305 31
      foreach ($matches[1] as $match) {
306 31
        $css .= trim($match) . "\n";
307
      }
308
    }
309
310 32
    return $css;
311
  }
312
313
  /**
314
   * Process the loaded CSS
315
   *
316
   * @param string $css
317
   *
318
   * @return array
319
   */
320 52
  private function processCSS($css)
321
  {
322
    //reset current set of rules
323 52
    $cssRules = array();
324
325
    // init vars
326 52
    $css = (string)$css;
327
328 52
    $css = $this->doCleanup($css);
329
330
    // rules are splitted by }
331 52
    $rules = (array)explode('}', $css);
332
333
    // init var
334 52
    $i = 1;
335
336
    // loop rules
337 52
    foreach ($rules as $rule) {
338
      // split into chunks
339 52
      $chunks = explode('{', $rule);
340
341
      // invalid rule?
342 52
      if (!isset($chunks[1])) {
343 52
        continue;
344
      }
345
346
      // set the selectors
347 39
      $selectors = trim($chunks[0]);
348
349
      // get css-properties
350 39
      $cssProperties = trim($chunks[1]);
351
352
      // split multiple selectors
353 39
      $selectors = (array)explode(',', $selectors);
354
355
      // loop selectors
356 39
      foreach ($selectors as $selector) {
357
        // cleanup
358 39
        $selector = trim($selector);
359
360
        // build an array for each selector
361 39
        $ruleSet = array();
362
363
        // store selector
364 39
        $ruleSet['selector'] = $selector;
365
366
        // process the properties
367 39
        $ruleSet['properties'] = $this->processCSSProperties($cssProperties);
368
369
        // calculate specificity
370 39
        $ruleSet['specificity'] = Specificity::fromSelector($selector);
371
372
        // remember the order in which the rules appear
373 39
        $ruleSet['order'] = $i;
374
375
        // add into rules
376 39
        $cssRules[] = $ruleSet;
377
378
        // increment
379 39
        $i++;
380
      }
381
    }
382
383
    // sort based on specificity
384 52
    if (0 !== count($cssRules)) {
385 39
      usort($cssRules, array(__CLASS__, 'sortOnSpecificity'));
386
    }
387
388 52
    return $cssRules;
389
  }
390
391
  /**
392
   * @param string $css
393
   *
394
   * @return string
395
   */
396 52
  private function doCleanup($css)
397
  {
398
    // remove newlines & replace double quotes by single quotes
399 52
    $css = str_replace(
400 52
        array("\r", "\n", '"'),
401 52
        array('', '', '\''),
402
        $css
403
    );
404
405
    // remove comments
406 52
    $css = preg_replace(self::$styleCommentRegEx, '', $css);
407
408
    // remove spaces
409 52
    $css = preg_replace('/\s\s+/', ' ', $css);
410
411
    // remove css charset
412 52
    if (true === $this->excludeCssCharset) {
413 52
      $css = $this->stripeCharsetInCss($css);
414
    }
415
416
    // remove css media queries
417 52
    if (true === $this->excludeMediaQueries) {
418 51
      $css = $this->stripeMediaQueries($css);
419
    }
420
421 52
    return (string)$css;
422
  }
423
424
  /**
425
   * remove css media queries from the string
426
   *
427
   * @param string $css
428
   *
429
   * @return string
430
   */
431 51
  private function stripeMediaQueries($css)
432
  {
433
    // remove comments previously to matching media queries
434 51
    $css = preg_replace(self::$styleCommentRegEx, '', $css);
435
436 51
    return (string)preg_replace(self::$cssMediaQueriesRegEx, '', $css);
437
  }
438
439
  /**
440
   * remove charset from the string
441
   *
442
   * @param string $css
443
   *
444
   * @return string
445
   */
446 52
  private function stripeCharsetInCss($css)
447
  {
448 52
    return (string)preg_replace(self::$cssCharsetRegEx, '', $css);
449
  }
450
451
  /**
452
   * Process the CSS-properties
453
   *
454
   * @return array
455
   *
456
   * @param  string $propertyString The CSS-properties.
457
   */
458 39
  private function processCSSProperties($propertyString)
459
  {
460
    // split into chunks
461 39
    $properties = $this->splitIntoProperties($propertyString);
462
463
    // init var
464 39
    $pairs = array();
465
466
    // loop properties
467 39
    foreach ($properties as $property) {
468
      // split into chunks
469 39
      $chunks = (array)explode(':', $property, 2);
470
471
      // validate
472 39
      if (!isset($chunks[1])) {
473 33
        continue;
474
      }
475
476
      // cleanup
477 38
      $chunks[0] = trim($chunks[0]);
478 38
      $chunks[1] = trim($chunks[1]);
479
480
      // add to pairs array
481
      if (
482 38
          !isset($pairs[$chunks[0]])
483
          ||
484 38
          !in_array($chunks[1], $pairs[$chunks[0]], true)
485
      ) {
486 38
        $pairs[$chunks[0]][] = $chunks[1];
487
      }
488
    }
489
490
    // sort the pairs
491 39
    ksort($pairs);
492
493
    // return
494 39
    return $pairs;
495
  }
496
497
  /**
498
   * Split a style string into an array of properties.
499
   * The returned array can contain empty strings.
500
   *
501
   * @param string $styles ex: 'color:blue;font-size:12px;'
502
   *
503
   * @return array an array of strings containing css property ex: array('color:blue','font-size:12px')
504
   */
505 39
  private function splitIntoProperties($styles)
506
  {
507 39
    $properties = (array)explode(';', $styles);
508 39
    $propertiesCount = count($properties);
509
510
    /** @noinspection ForeachInvariantsInspection */
511 39
    for ($i = 0; $i < $propertiesCount; $i++) {
512
      // If next property begins with base64,
513
      // Then the ';' was part of this property (and we should not have split on it).
514
      if (
515 39
          isset($properties[$i + 1])
516
          &&
517 39
          strpos($properties[$i + 1], 'base64,') !== false
518
      ) {
519 1
        $properties[$i] .= ';' . $properties[$i + 1];
520 1
        $properties[$i + 1] = '';
521 1
        ++$i;
522
      }
523
    }
524
525 39
    return $properties;
526
  }
527
528
  /**
529
   * create XPath
530
   *
531
   * @param \DOMDocument $document
532
   * @param array        $cssRules
533
   *
534
   * @return \DOMXPath
535
   */
536 52
  private function createXPath(\DOMDocument $document, array $cssRules)
537
  {
538 52
    $propertyStorage = new \SplObjectStorage();
539 52
    $xPath = new \DOMXPath($document);
540
541
    // any rules?
542 52
    if (0 !== count($cssRules)) {
543
      // loop rules
544 39
      foreach ($cssRules as $rule) {
545
546 39
        $ruleSelector = $rule['selector'];
547 39
        $ruleProperties = $rule['properties'];
548
549 39
        if (!$ruleSelector || !$ruleProperties) {
550 3
          continue;
551
        }
552
553
        try {
554 38
          $query = $this->cssConverter->toXPath($ruleSelector);
555 5
        } catch (ExceptionInterface $e) {
556 5
          $query = null;
557
        }
558
559
        // validate query
560 38
        if (null === $query) {
561 5
          continue;
562
        }
563
564
        // search elements
565 37
        $elements = $xPath->query($query);
566
567
        // validate elements
568 37
        if (false === $elements) {
569
          continue;
570
        }
571
572
        // loop found elements
573 37
        foreach ($elements as $element) {
574
575
          /**
576
           * @var $element \DOMElement
577
           */
578
579
          if (
580 37
              $ruleSelector == '*'
581
              &&
582
              (
583 1
                  $element->tagName == 'html'
584 1
                  || $element->tagName === 'title'
585 1
                  || $element->tagName == 'meta'
586 1
                  || $element->tagName == 'head'
587 1
                  || $element->tagName == 'style'
588 1
                  || $element->tagName == 'script'
589 37
                  || $element->tagName == 'link'
590
              )
591
          ) {
592 1
            continue;
593
          }
594
595
          // no styles stored?
596 37
          if (!isset($propertyStorage[$element])) {
597
598
            // init var
599 37
            $originalStyle = $element->attributes->getNamedItem('style');
600
601 37
            if ($originalStyle) {
602 12
              $originalStyle = $originalStyle->value;
0 ignored issues
show
Bug introduced by
The property value does not seem to exist in DOMNode.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
603
            } else {
604 35
              $originalStyle = '';
605
            }
606
607
            // store original styles
608 37
            $propertyStorage->attach($element, $originalStyle);
609
610
            // clear the styles
611 37
            $element->setAttribute('style', '');
612
          }
613
614
          // set attribute
615 37
          $propertiesString = $this->createPropertyChunks($element, $ruleProperties);
616 37
          if ($propertiesString) {
617 37
            $element->setAttribute('style', $propertiesString);
618
          }
619
        }
620
      }
621
622 39
      foreach ($propertyStorage as $element) {
623 37
        $originalStyle = $propertyStorage->getInfo();
624 37
        if ($originalStyle) {
625 12
          $originalStyles = $this->splitIntoProperties($originalStyle);
626 12
          $originalProperties = $this->splitStyleIntoChunks($originalStyles);
627
628
          // set attribute
629 12
          $propertiesString = $this->createPropertyChunks($element, $originalProperties);
630 12
          if ($propertiesString) {
631 37
            $element->setAttribute('style', $propertiesString);
632
          }
633
        }
634
      }
635
    }
636
637 52
    return $xPath;
638
  }
639
640
  /**
641
   * @param \DOMElement $element
642
   * @param array       $ruleProperties
643
   *
644
   * @return string
645
   */
646 37
  private function createPropertyChunks(\DOMElement $element, array $ruleProperties)
647
  {
648
    // init var
649 37
    $properties = array();
650
651
    // get current styles
652 37
    $stylesAttribute = $element->attributes->getNamedItem('style');
653
654
    // any styles defined before?
655 37
    if (null !== $stylesAttribute) {
656
      // get value for the styles attribute
657 37
      $definedStyles = (string)$stylesAttribute->value;
0 ignored issues
show
Bug introduced by
The property value does not seem to exist in DOMNode.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
658
659
      // split into properties
660 37
      $definedProperties = $this->splitIntoProperties($definedStyles);
661
662 37
      $properties = $this->splitStyleIntoChunks($definedProperties);
663
    }
664
665
    // add new properties into the list
666 37
    foreach ($ruleProperties as $key => $value) {
667
      // If one of the rules is already set and is !important, don't apply it,
668
      // except if the new rule is also important.
669
      if (
670 37
          !isset($properties[$key])
671
          ||
672 12
          false === stripos($properties[$key], '!important')
673
          ||
674 37
          false !== stripos(implode('', (array)$value), '!important')
675
      ) {
676 37
        unset($properties[$key]);
677 37
        $properties[$key] = $value;
678
      }
679
    }
680
681
    // build string
682 37
    $propertyChunks = array();
683
684
    // build chunks
685 37
    foreach ($properties as $key => $values) {
686 37
      foreach ((array)$values as $value) {
687 37
        $propertyChunks[] = $key . ': ' . $value . ';';
688
      }
689
    }
690
691 37
    return implode(' ', $propertyChunks);
692
  }
693
694
  /**
695
   * @param array $definedProperties
696
   *
697
   * @return array
698
   */
699 37
  private function splitStyleIntoChunks(array $definedProperties)
700
  {
701
    // init var
702 37
    $properties = array();
703
704
    // loop properties
705 37
    foreach ($definedProperties as $property) {
706
      // validate property
707
      if (
708 37
          !$property
709
          ||
710 37
          strpos($property, ':') === false
711
      ) {
712 37
        continue;
713
      }
714
715
      // split into chunks
716 19
      $chunks = (array)explode(':', trim($property), 2);
717
718
      // validate
719 19
      if (!isset($chunks[1])) {
720
        continue;
721
      }
722
723
      // loop chunks
724 19
      $properties[$chunks[0]] = trim($chunks[1]);
725
    }
726
727 37
    return $properties;
728
  }
729
730
  /**
731
   * Strip style tags into the generated HTML.
732
   *
733
   * @param  \DOMXPath $xPath The DOMXPath for the entire document.
734
   */
735 13
  private function stripOriginalStyleTags(\DOMXPath $xPath)
736
  {
737
    // get all style tags
738 13
    $nodes = $xPath->query('descendant-or-self::style');
739 13
    foreach ($nodes as $node) {
740 12
      if ($this->excludeMediaQueries === true) {
741
742
        // remove comments previously to matching media queries
743 11
        $node->nodeValue = preg_replace(self::$styleCommentRegEx, '', $node->nodeValue);
744
745
        // search for Media Queries
746 11
        preg_match_all(self::$cssMediaQueriesRegEx, $node->nodeValue, $mqs);
747
748
        // replace the nodeValue with just the Media Queries
749 11
        $node->nodeValue = implode("\n", $mqs[0]);
750
751
      } else {
752
        // remove the entire style tag
753 12
        $node->parentNode->removeChild($node);
754
      }
755
    }
756 13
  }
757
758
  /**
759
   * Remove id and class attributes.
760
   *
761
   * @param  \DOMXPath $xPath The DOMXPath for the entire document.
762
   */
763 3
  private function cleanupHTML(\DOMXPath $xPath)
764
  {
765 3
    $nodes = $xPath->query('//@class | //@id');
766 3
    foreach ($nodes as $node) {
767 3
      $node->ownerElement->removeAttributeNode($node);
768
    }
769 3
  }
770
771
  /**
772
   * Should the IDs and classes be removed?
773
   *
774
   * @param  bool $on Should we enable cleanup?
775
   *
776
   * @return $this
777
   */
778 3
  public function setCleanup($on = true)
779
  {
780 3
    $this->cleanup = (bool)$on;
781
782 3
    return $this;
783
  }
784
785
  /**
786
   * Set the encoding to use with the DOMDocument.
787
   *
788
   * @param  string $encoding The encoding to use.
789
   *
790
   * @return $this
791
   *
792
   * @deprecated Doesn't have any effect
793
   */
794
  public function setEncoding($encoding)
795
  {
796
    $this->encoding = (string)$encoding;
797
798
    return $this;
799
  }
800
801
  /**
802
   * Set use of inline styles block.
803
   *
804
   * Info: If this is enabled the class will use the style-block in the HTML.
805
   *
806
   * @param  bool $on Should we process inline styles?
807
   *
808
   * @return $this
809
   */
810 30
  public function setUseInlineStylesBlock($on = true)
811
  {
812 30
    $this->useInlineStylesBlock = (bool)$on;
813
814 30
    return $this;
815
  }
816
817
  /**
818
   * Set use of inline link block.
819
   *
820
   * Info: If this is enabled the class will use the links reference in the HTML.
821
   *
822
   * @param  bool [optional] $on Should we process link styles?
823
   *
824
   * @return $this
825
   */
826 2
  public function setLoadCSSFromHTML($on = true)
827
  {
828 2
    $this->loadCSSFromHTML = (bool)$on;
829
830 2
    return $this;
831
  }
832
833
  /**
834
   * Set strip original style tags.
835
   *
836
   * Info: If this is enabled the class will remove all style tags in the HTML.
837
   *
838
   * @param  bool $on Should we process inline styles?
839
   *
840
   * @return $this
841
   */
842 17
  public function setStripOriginalStyleTags($on = true)
843
  {
844 17
    $this->stripOriginalStyleTags = (bool)$on;
845
846 17
    return $this;
847
  }
848
849
  /**
850
   * Set exclude media queries.
851
   *
852
   * Info: If this is enabled the media queries will be removed before inlining the rules.
853
   *
854
   * WARNING: If you use inline styles block "<style>" the this option will keep the media queries.
855
   *
856
   * @param bool $on
857
   *
858
   * @return $this
859
   */
860 14
  public function setExcludeMediaQueries($on = true)
861
  {
862 14
    $this->excludeMediaQueries = (bool)$on;
863
864 14
    return $this;
865
  }
866
867
  /**
868
   * Set exclude charset.
869
   *
870
   * @param bool $on
871
   *
872
   * @return $this
873
   */
874 1
  public function setExcludeCssCharset($on = true)
875
  {
876 1
    $this->excludeCssCharset = (bool)$on;
877
878 1
    return $this;
879
  }
880
881
  /**
882
   * Set exclude conditional inline-style blocks.
883
   *
884
   * e.g.: <!--[if gte mso 9]><style>.foo { bar } </style><![endif]-->
885
   *
886
   * @param bool $on
887
   *
888
   * @return $this
889
   */
890 6
  public function setExcludeConditionalInlineStylesBlock($on = true)
891
  {
892 6
    $this->excludeConditionalInlineStylesBlock = (bool)$on;
893
894 6
    return $this;
895
  }
896
}
897