Tree::parseParentElement()   B
last analyzed

Complexity

Conditions 9
Paths 9

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 19
cts 19
cp 1
rs 8.0555
c 0
b 0
f 0
cc 9
nc 9
nop 3
crap 9
1
<?php
2
3
namespace CHMLib\TOCIndex;
4
5
use CHMLib\CHM;
6
use CHMLib\Map;
7
use DOMDocument;
8
use DOMElement;
9
use Exception;
10
use Iterator;
11
12
/**
13
 * A list of items in the TOC or in the Index of an CHM file.
14
 */
15
class Tree implements Iterator
16
{
17
    /**
18
     * List of Item instances children of this tree.
19
     *
20
     * @var \CHMLib\TOCIndex\Item[]
21
     */
22
    protected $items;
23
24
    /**
25
     * The current index for the Iterator interface.
26
     *
27
     * @var int
28
     */
29
    protected $iteratorIndex;
30
31
    /**
32
     * Initializes the instance.
33
     */
34 1
    public function __construct()
35
    {
36 1
        $this->items = array();
37 1
    }
38
39
    /**
40
     * Get the items contained in this tree.
41
     *
42
     * @return \CHMLib\TOCIndex\Item[]
43
     */
44 1
    public function getItems()
45
    {
46 1
        return $this->items;
47
    }
48
49
    /**
50
     * Get the total number of items in this instance and in all the sub-instances.
51
     *
52
     * @return int
53
     */
54
    public function getItemsCount()
55
    {
56
        $result = count($this->items);
57
        foreach ($this->items as $item) {
58
            $result += $item->getChildren()->getItemsCount();
59
        }
60
61
        return $result;
62
    }
63
64
    /**
65
     * Resolve the items contained in other CHM files.
66
     *
67
     * @param \CHMLib\Map $map
68
     * @param bool $ignoreErrors Set to true to ignore missing CHM and/or entries.
69
     *
70
     * @throws \Exception Throw an Exception in case of errors.
71
     */
72 1
    public function resolve(Map $map, $ignoreErrors = false)
73
    {
74 1
        $result = array();
75 1
        foreach ($this->items as $item) {
76 1
            $result = array_merge($result, $item->resolve($map, $ignoreErrors));
77
        }
78
79 1
        $this->items = $result;
80 1
    }
81
82
    /**
83
     * Create a new instance starting from the whole TOC/Index source 'HTML'.
84
     *
85
     * @param \CHMLib\CHM $chm The parent CHM instance.
86
     * @param string $data The contents of the .hhc/.hhk file.
87
     *
88
     * @throws \Exception Throw an Exception in case of errors.
89
     *
90
     * @return static
91
     */
92 1
    public static function fromString(CHM $chm, $data)
93
    {
94 1
        if (!class_exists('DOMDocument', false) || !class_exists('DOMXpath', false)) {
95
            throw new Exception('Missing PHP extension: php-xml');
96
        }
97 1
        $result = new static();
98 1
        $data = trim((string) $data);
99 1
        if (stripos($data, '<object') !== false) {
100 1
            $m = null;
101 1
            $doc = new DOMDocument();
102 1
            $charset = 'ISO-8859-1';
103 1
            if (preg_match('%^<\?xml\s+encoding\s*=\s*"([^"]+)"%i', $data, $m)) {
104
                $charset = $m[1];
0 ignored issues
show
Unused Code introduced by
$charset 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...
105
            } else {
106 1
                if (preg_match('%<meta\s+http-equiv\s*=\s*"Content-Type"\s+content\s*=\s*"text/html;\s*charset=([^"]+)">%i', $data, $m)) {
107
                    $charset = $m[1];
108
                }
109 1
                $data = '<?xml encoding="'.$charset.'">'.$data;
110
            }
111
            // LI elements are very often malformed/misplaced: let's remove them
112 1
            $data = preg_replace('$</?li((\\s+[^>]*)|\\s*)>$i', '', $data);
113 1
            if (@$doc->loadHTML($data) !== true) {
114
                throw new Exception('Failed to parse the .hhc/.hhk file contents');
115
            }
116 1
            $result->parseParentElement($chm, $doc->documentElement, 0);
117
        }
118
119 1
        return $result;
120
    }
121
122
    /**
123
     * Depth of the found child items.
124
     *
125
     * @var int
126
     */
127
    protected $depth;
128
129
    /**
130
     * Parse a DOMElement and read the items/sub trees.
131
     *
132
     * @param \CHMLib\CHM $chm
133
     * @param \DOMElement $parentElement
134
     * @param int $depth
135
     */
136 1
    protected function parseParentElement(CHM $chm, DOMElement $parentElement, $depth)
137
    {
138 1
        foreach ($parentElement->childNodes as $node) {
139 1
            if ($node->nodeType === XML_ELEMENT_NODE) {
140 1
                switch (strtolower($node->tagName)) {
141 1
                    case 'object':
142 1
                        if (strtolower($node->getAttribute('type')) === 'text/sitemap') {
143 1
                            $this->depth = $depth;
144 1
                            $this->items[] = new Item($chm, $node);
145
                        }
146 1
                        break;
147 1
                    case 'ul':
148 1
                    case 'ol':
149 1
                        $n = count($this->items);
150 1
                        if ($n > 0 && $depth >= $this->depth) {
151 1
                            $this->items[$n - 1]->getChildren()->parseParentElement($chm, $node, $depth + 1);
152
                        } else {
153 1
                            $this->parseParentElement($chm, $node, $depth + 1);
154
                        }
155 1
                        break;
156
                    default:
157 1
                        $this->parseParentElement($chm, $node, $depth + 1);
158 1
                        break;
159
                }
160
            }
161
        }
162 1
    }
163
164
    /**
165
     * {@inheritdoc}
166
     *
167
     * @see \Iterator::current()
168
     */
169
    public function current()
170
    {
171
        return isset($this->items[$this->iteratorIndex]) ? $this->items[$this->iteratorIndex] : null;
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     *
177
     * @see \Iterator::key()
178
     */
179
    public function key()
180
    {
181
        return $this->iteratorIndex;
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     *
187
     * @see \Iterator::next()
188
     */
189
    public function next()
190
    {
191
        ++$this->iteratorIndex;
192
    }
193
194
    /**
195
     * {@inheritdoc}
196
     *
197
     * @see \Iterator::rewind()
198
     */
199
    public function rewind()
200
    {
201
        $this->iteratorIndex = 0;
202
    }
203
204
    /**
205
     * {@inheritdoc}
206
     *
207
     * @see \Iterator::valid()
208
     */
209
    public function valid()
210
    {
211
        return $this->iteratorIndex < count($this->items);
212
    }
213
}
214