Completed
Push — master ( 570c19...df330f )
by Vitaly
03:09
created

Tree::prepare()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
rs 9.4285
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
namespace samsonframework\html2less;
3
4
/**
5
 * Created by Vitaly Iegorov <[email protected]>.
6
 * on 15.04.16 at 12:43
7
 */
8
class Tree
9
{
10
    /** @var array Collection of ignored DOM nodes */
11
    public static $ignoredNodes = array(
12
        'head',
13
        'html',
14
        '.clear',
15
        '&.clear',
16
        '.clearfix',
17
        '&.clearfix',
18
        'body',
19
        'meta',
20
        'script',
21
        'link',
22
        'title',
23
        'br'
24
    );
25
26
    /**
27
     * Build destination code tree from source code.
28
     *
29
     * @param string $source Source code
30
     */
31
    public function build($source)
32
    {
33
        // Prepare source code
34
        $source = $this->prepare($source);
35
36
        // Build destination node tree
37
        $tree = $this->analyze($source);
38
39
        trace($tree);
40
    }
41
42
    /**
43
     * Source code cleaner.
44
     *
45
     * @param string $source
46
     *
47
     * @return string Cleared source code
48
     */
49
    protected function prepare($source)
50
    {
51
        // Remove all PHP code from view
52
        return trim(preg_replace('/<\?(php|=).*?\?>/', '', $source));
53
    }
54
55
    /**
56
     * Analyze source code and create destination code tree.
57
     *
58
     * @param string $source Source code
59
     *
60
     * @return HTMLDOMNode Internal code tree
61
     */
62
    protected function &analyze($source)
63
    {
64
        libxml_use_internal_errors(true);
65
66
        /** @var \DOMNode Pointer to current dom element */
67
        $dom = new \DOMDocument();
68
        $dom->loadHTML($source);
69
70
        // Perform recursive node analysis
71
        return $this->analyzeSourceNode($dom, new HTMLDOMNode(new \DOMNode()));
72
    }
73
74
    /**
75
     * Perform source node analysis.
76
     *
77
     * @param \DOMNode    $domNode
78
     * @param HTMLDOMNode $parent
79
     *
80
     * @return HTMLDOMNode
81
     */
82
    protected function &analyzeSourceNode(\DOMNode $domNode, HTMLDOMNode $parent)
83
    {
84
        /** @var \DOMNode[] $children */
85
        $children = [];
86
        /** @var array $tags tag name => count collection */
87
        $tags = [];
88
89
        // Work only with DOMElements
90
        foreach ($domNode->childNodes as $child) {
91
            if ($child->nodeType === 1) {
92
                $children[] = $child;
93
94
                // Get child node tag and count them
95
                $tag = $child->nodeName;
96
                if (!array_key_exists($tag, $tags)) {
97
                    $tags[$tag] = 1;
98
                } else {
99
                    $tags[$tag]++;
100
                }
101
            }
102
        }
103
104
        // Iterate all normal DOM nodes
105
        foreach ($children as $child) {
106
            // Create internal node instance
107
            $node = new HTMLDOMNode($child, $parent);
0 ignored issues
show
Documentation introduced by
$parent is of type object<samsonframework\html2less\HTMLDOMNode>, but the function expects a null|object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
108
109
            // Go deeper in recursion
110
            $this->analyzeSourceNode($child, $node);
111
        }
112
113
        return $parent;
114
    }
115
116
    /**
117
     * Handle current DOM node and transform it to LESS node
118
     *
119
     * @param \DOMNode $node Pointer to current analyzed DOM node
120
     * @param array    $path
121
     *
122
     * @internal param \samsonos\php\skeleton\Node $parent Pointer to parent LESS Node
123
     */
124
    protected function handleNode(\DOMNode & $node, &$path = array())
125
    {
126
        // Collect normal HTML DOM nodes
127
        /** @var \DOMNode[] $children */
128
        $children = array();
129
        foreach ($node->childNodes as $child) {
130
            // Work only with DOMElements
131
            if ($child->nodeType == 1) {
132
                $children[] = $child;
133
            }
134
        }
135
        // Group current level HTML DOM nodes by tag name and count them
136
        $childrenTagArray = array();
137
        foreach ($children as $child) {
138
            $tag = $child->nodeName;
139
            if (!isset($childrenTagArray[$tag])) {
140
                $childrenTagArray[$tag] = 1;
141
            } else $childrenTagArray[$tag]++;
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
142
        }
143
        // Iterate all normal DOM nodes
144
        foreach ($children as $child) {
145
            // Create LESS node
146
            $childNode = new HTMLDOMNode($child);
147
            // If this LESS node has NO CSS classes
148
            if (sizeof($childNode->class) == 0) {
149
                // Create new multidimensional array key group
150
                if (!isset($path[$child->nodeName])) {
151
                    $path[$child->nodeName] = array();
152
                }
153
                // Go deeper in recursion with current child node and new path
154
                $this->handleNode($child, $path[$child->nodeName]);
155
            } else { // This child DOM node has CSS classes
156
                // Get first node class and remove it from array og classes
157
                $firstClass = array_shift($childNode->class);
158
                // Save current LESS path
159
                $oldPath = &$path;
160
                // If there is more than one DOM child node with this tag name at this level
161
                if ($childrenTagArray[$childNode->tag] > 1 && $childNode->tag != 'div') {
162
                    // Create correct LESS class name
163
                    $class = '&.' . $firstClass;
164
                    // Create new multidimensional array key group with tag name group
165
                    if (!isset($path[$child->nodeName][$class])) {
166
                        $path[$child->nodeName][$class] = array();
167
                    }
168
                    // Go deeper in recursion with current child node and new path with tag name group and CSS class name group
169
                    $this->handleNode($child, $path[$child->nodeName][$class]);
170
                    // Make new path as current
171
                    $path = &$path[$child->nodeName][$class];
172
                } else { // There is only on child with this tag name at this level
173
                    // Create correct LESS class name
174
                    $class = '.' . $firstClass;
175
                    // Create new multidimensional array key group without tag name group
176
                    if (!isset($path[$class])) {
177
                        $path[$class] = array();
178
                    }
179
                    // Go deeper in recursion with current child node and new path with CSS class name group
180
                    $this->handleNode($child, $path[$class]);
181
                    // Make new path as current
182
                    $path = &$path[$class];
183
                }
184
                // Iterate all other classes starting from second class
185
                foreach ($childNode->class as $class) {
186
                    // Create correct LESS class name
187
                    $class = '&.' . $class;
188
                    // Create new multidimensional array key group with tag name group
189
                    if (!isset($path[$class])) {
190
                        $path[$class] = array();
191
                    }
192
                    // Go deeper in recursion with current child node and new path with tag name group and CSS class name group
193
                    $this->handleNode($child, $path[$class]);
194
                }
195
                // Return old LESS path tree
196
                $path = &$oldPath;
197
            }
198
        }
199
    }
200
}
201