Completed
Push — master ( 56c2c4...605748 )
by Kevin
02:18
created

StyleSheetExtractor::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 2
eloc 3
nc 2
nop 1
1
<?php namespace Luminaire\Premailer;
2
3
/**
4
 * Created by Sublime Text 3
5
 *
6
 * @user     Kevin Tanjung
7
 * @website  http://kevintanjung.github.io
8
 * @email    [email protected]
9
 * @date     03/08/2016
10
 * @time     13:58
11
 */
12
13
use DOMDocument;
14
use DOMElement;
15
use RuntimeException;
16
17
/**
18
 * The Style Sheet Extractor class
19
 *
20
 * @package  \Luminaire\Premailer
21
 */
22
class StyleSheetExtractor
23
{
24
25
    /**
26
     * The DOM Document instance
27
     *
28
     * @var \DOMDocument
29
     */
30
    protected $document;
31
32
    /**
33
     * Create a new instance of Style Sheet Extractor
34
     *
35
     * @param  \DOMDocument|null  $document
36
     */
37
    public function __construct(DOMDocument $document = null)
38
    {
39
        if ($document)
40
        {
41
            $this->setDocument($document);
42
        }
43
    }
44
45
    /**
46
     * Get the DOM Document instance
47
     *
48
     * @return \DOMDocument|null
49
     */
50
    public function getDocument()
51
    {
52
        return $this->document;
53
    }
54
55
    /**
56
     * Set the DOM Document instance
57
     *
58
     * @param  \DOMDocument  $document
59
     * @return $this
60
     */
61
    public function setDocument(DOMDocument $document)
62
    {
63
        $this->document = $document;
64
65
        return $this;
66
    }
67
68
    /**
69
     * Get all CSS in the mail template
70
     *
71
     * @return string
72
     *
73
     * @throws \RuntimeException
74
     */
75
    public function extract()
76
    {
77
        if ( ! $this->document)
78
        {
79
            throw new RuntimeException('There are no [DOMDocument] instance to work with. Use the [setDocument] method to pass an instance of the [DOMDocument].');
80
        }
81
82
        $stylesheet = "";
83
84
        foreach ($this->getStyleTags($this->document) as $node)
85
        {
86
            if ($content = $this->getStyleTagContent($node))
87
            {
88
                $stylesheet .= $content . "\r\n";
89
            }
90
91
            $node->parentNode->removeChild($node);
92
        }
93
94
        return $stylesheet;
95
    }
96
97
    /**
98
     * Get all DOM element that has "style" attribute
99
     *
100
     * @param  \DOMDocument  $doc
101
     * @return array
102
     */
103
    protected function getStyleTags(DOMDocument $doc)
104
    {
105
        $nodes = [];
106
107
        foreach ($doc->getElementsByTagName('style') as $element)
108
        {
109
            $nodes[] = $element;
110
        }
111
112
        return $nodes;
113
    }
114
115
    /**
116
     * Get the HTML <style> tag CSS content
117
     *
118
     * @param  \DOMElement  $node
119
     * @return string|null
120
     */
121
    protected function getStyleTagContent(DOMElement $node)
122
    {
123
        if ( ! $this->isStyleTypeAllowed($node) || ! $this->isStyleMediaAllowed($node))
124
        {
125
            return null;
126
        }
127
128
        return (string) $node->nodeValue;
129
    }
130
131
    /**
132
     * Check if the HTML <style> tag has no [media] attribute or if it has a
133
     * [media] attribute, then it must either have a value of "all" or "screen".
134
     *
135
     * @param  \DOMElement  $style_node
136
     * @return bool
137
     */
138
    private function isStyleMediaAllowed(DOMElement $style_node)
139
    {
140
        $media = $style_node->attributes->getNamedItem('media');
141
142
        if (is_null($media)) return true;
143
144
        $media       = str_replace(' ', '', (string) $media->nodeValue);
0 ignored issues
show
Unused Code introduced by
$media is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
145
        $media_types = explode(',', $media_types);
0 ignored issues
show
Bug introduced by
The variable $media_types seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
146
147
        return in_array('all', $media_types) || in_array('screen', $media_types);
148
    }
149
150
    /**
151
     * Check if the HTML <style> tag has the default [type] attribute or the value
152
     * of the [type] attribute is set to "text/css".
153
     *
154
     * @param  \DOMElement  $style_node
155
     * @return bool
156
     */
157
    private function isStyleTypeAllowed(DOMElement $style_node)
158
    {
159
        $type = $style_node->attributes->getNamedItem('type');
160
161
        return is_null($type) || (string) $type->nodeValue == 'text/css';
162
    }
163
164
}
165