Item   B
last analyzed

Complexity

Total Complexity 47

Size/Duplication

Total Lines 391
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 4

Test Coverage

Coverage 49.59%

Importance

Changes 0
Metric Value
wmc 47
lcom 2
cbo 4
dl 0
loc 391
ccs 61
cts 123
cp 0.4959
rs 8.64
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A getCHM() 0 4 1
A getName() 0 4 1
A isNew() 0 4 1
A getComment() 0 4 1
A getKeyword() 0 4 1
A getSeeAlso() 0 4 1
A getLocal() 0 4 1
A getURL() 0 4 1
A getFrameName() 0 4 1
A getWindowName() 0 4 1
A getMerge() 0 4 1
A getImageNumber() 0 4 1
A getXCondition() 0 4 1
A getChildren() 0 4 1
D __construct() 0 75 20
B resolve() 0 28 7
B findEntry() 0 20 6

How to fix   Complexity   

Complex Class

Complex classes like Item 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 Item, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace CHMLib\TOCIndex;
4
5
use CHMLib\CHM;
6
use CHMLib\Map;
7
use DOMElement;
8
use Exception;
9
10
/**
11
 * A list of items in the TOC or in the Index of an CHM file.
12
 */
13
class Item
14
{
15
    /**
16
     * The parent CHM instance.
17
     *
18
     * @var \CHMLib\CHM
19
     */
20
    protected $chm;
21
22
    /**
23
     * The name of the tree item.
24
     *
25
     * @var string
26
     */
27
    protected $name;
28
29
    /**
30
     * Is this item marked as new?
31
     *
32
     * @var bool
33
     */
34
    protected $isNew;
35
36
    /**
37
     * The comment of the tree item.
38
     *
39
     * @var string
40
     */
41
    protected $comment;
42
43
    /**
44
     * The keyword of the tree item.
45
     *
46
     * @var string
47
     */
48
    protected $keyword;
49
50
    /**
51
     * The value of the "See Also" parameter.
52
     *
53
     * @var string
54
     */
55
    protected $seeAlso;
56
57
    /**
58
     * The local path to the tree item.
59
     *
60
     * @var string
61
     */
62
    protected $local;
63
64
    /**
65
     * The URL to the tree item.
66
     *
67
     * @var string
68
     */
69
    protected $url;
70
71
    /**
72
     * The frame name for this item.
73
     *
74
     * @var string;
75
     */
76
    protected $frameName;
77
78
    /**
79
     * The window name for this item.
80
     *
81
     * @var string;
82
     */
83
    protected $windowName;
84
85
    /**
86
     * The path to an entry in another CHM file.
87
     *
88
     * @var string|array|null If it's an array, it has two keys: 'chm' and 'entry'.
89
     */
90
    protected $merge;
91
92
    /**
93
     * The image number attribute.
94
     *
95
     * @var int|null
96
     */
97
    protected $imageNumber;
98
99
    /**
100
     * The value of the X-Condition parameter.
101
     *
102
     * @var string
103
     */
104
    protected $xCondition;
105
106
    /**
107
     * The sub-elements of this Item.
108
     *
109
     * @var \CHMLib\TOCIndex\Tree
110
     */
111
    protected $children;
112
113
    /**
114
     * Initializes the instance.
115
     *
116
     * @param \CHMLib\CHM $chm The parent CHM instance.
117
     * @param \DOMElement $object The OBJECT element.
118
     *
119
     * @throws \Exception Throw an Exception in case of errors.
120
     *
121
     * @return static
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
122
     */
123 1
    public function __construct(CHM $chm, DOMElement $object)
124
    {
125 1
        $this->chm = $chm;
126 1
        $this->name = '';
127 1
        $this->isNew = false;
128 1
        $this->comment = '';
129 1
        $this->keyword = '';
130 1
        $this->seeAlso = '';
131 1
        $this->local = '';
132 1
        $this->url = '';
133 1
        $this->frameName = '';
134 1
        $this->windowName = '';
135 1
        $this->merge = null;
136 1
        $this->imageNumber = null;
137 1
        $this->xCondition = '';
138 1
        $this->children = new Tree();
139 1
        $matches = null;
140 1
        foreach ($object->childNodes as $p) {
141 1
            if ($p instanceof DOMElement && strcasecmp($p->tagName, 'param') === 0) {
142 1
                $name = trim((string) $p->getAttribute('name'));
143 1
                $value = trim((string) $p->getAttribute('value'));
144 1
                switch (strtolower($name)) {
145 1
                    case 'name':
146
                        // Multiple values are allowed: we keep only the last one
147 1
                        $this->name = $value;
148 1
                        break;
149 1
                    case 'new':
150
                        $this->isNew = !empty($value);
151
                        break;
152 1
                    case 'comment':
153
                        $this->comment = $value;
154
                        break;
155 1
                    case 'keyword':
156
                        $this->keyword = $value;
157
                        break;
158 1
                    case 'see also':
159
                        $this->seeAlso = $value;
160
                        break;
161 1
                    case 'local':
162 1
                        $this->local = '/'.str_replace('\\', '/', str_replace('%20', ' ', $value));
163 1
                        break;
164 1
                    case 'url':
165
                        $this->url = $value;
166
                        break;
167 1
                    case 'framename':
168
                        $this->frameName = $value;
169
                        break;
170 1
                    case 'windowname':
171
                        $this->windowName = $value;
172
                        break;
173 1
                    case 'merge':
174 1
                        if ($value !== '') {
175 1
                            if (preg_match('%^([^:\\\\/]+.chm)::(.+)$%i', $value, $matches)) {
176 1
                                $this->merge = array('chm' => $matches[1], 'entry' => '/'.ltrim(str_replace('\\', '/', $matches[2]), '/'));
177
                            } else {
178
                                $this->merge = $value;
179
                            }
180
                        }
181 1
                        break;
182
                    case 'imagenumber':
183
                        if (is_numeric($value)) {
184
                            $this->imageNumber = (int) $value;
185
                        } elseif ($value !== '') {
186
                            throw new Exception("Invalid value of the '$name' attribute: $value");
187
                        }
188
                        break;
189
                    case 'x-condition':
190
                        $this->xCondition = $value;
191
                        break;
192
                    default:
193
                        throw new Exception("Unknown parameter name '$name' of a tree item (value: '$value')");
194
                }
195
            }
196
        }
197 1
    }
198
199
    /**
200
     * Get the parent CHM instance.
201
     *
202
     * @return \CHMLib\CHM
203
     */
204
    public function getCHM()
205
    {
206
        return $this->chm;
207
    }
208
209
    /**
210
     * Get the name of the tree item.
211
     *
212
     * @return string
213
     */
214 1
    public function getName()
215
    {
216 1
        return $this->name;
217
    }
218
219
    /**
220
     * Is this item marked as new?
221
     *
222
     * @return bool
223
     */
224
    public function isNew()
225
    {
226
        return $this->isNew;
227
    }
228
229
    /**
230
     * Get the comment of the tree item.
231
     *
232
     * @return string
233
     */
234
    public function getComment()
235
    {
236
        return $this->comment;
237
    }
238
239
    /**
240
     * Get the keyword of the tree item.
241
     *
242
     * @return string
243
     */
244
    public function getKeyword()
245
    {
246
        return $this->keyword;
247
    }
248
249
    /**
250
     * Get the value of the "See Also" parameter.
251
     *
252
     * @return string
253
     */
254
    public function getSeeAlso()
255
    {
256
        return $this->seeAlso;
257
    }
258
259
    /**
260
     * Get the local path to the tree item.
261
     *
262
     * @return string
263
     */
264
    public function getLocal()
265
    {
266
        return $this->local;
267
    }
268
269
    /**
270
     * Get the URL to the tree item.
271
     *
272
     * @return string
273
     */
274 1
    public function getURL()
275
    {
276 1
        return $this->url;
277
    }
278
279
    /**
280
     * Get the frame name for this item.
281
     *
282
     * @return string
283
     */
284
    public function getFrameName()
285
    {
286
        return $this->frameName;
287
    }
288
289
    /**
290
     * Get the window name for this item.
291
     *
292
     * @return string
293
     */
294
    public function getWindowName()
295
    {
296
        return $this->frameName;
297
    }
298
299
    /**
300
     * Get the path to an entry in another CHM file.
301
     *
302
     * @var array|null If not null, it's an array with two keys: 'chm' and 'entry'.
303
     */
304 1
    public function getMerge()
305
    {
306 1
        return $this->merge;
307
    }
308
309
    /**
310
     * Get the image number attribute.
311
     *
312
     * @return int|null
313
     */
314
    public function getImageNumber()
315
    {
316
        return $this->imageNumber;
317
    }
318
    
319
    /**
320
     * Get the value of the X-Condition parameter.
321
     *
322
     * @return string
323
     */
324
    public function getXCondition()
325
    {
326
        return $this->xCondition;
327
    }
328
329
    /**
330
     * Get the sub-elements of this Item.
331
     *
332
     * @return \CHMLib\TOCIndex\Tree
333
     */
334 1
    public function getChildren()
335
    {
336 1
        return $this->children;
337
    }
338
339
    /**
340
     * Resolve the items contained in other CHM files.
341
     *
342
     * @param \CHMLib\Map $map
343
     * @param bool $ignoreErrors Set to true to ignore missing CHM and/or entries.
344
     *
345
     * @throws \Exception Throw an Exception in case of errors.
346
     *
347
     * @return static[]
348
     */
349 1
    public function resolve(Map $map, $ignoreErrors = false)
350
    {
351 1
        $result = array($this);
352 1
        if (is_array($this->merge)) {
353 1
            $chm = $map->get($this->merge['chm']);
354 1
            if ($chm === null) {
355
                if (!$ignoreErrors) {
356
                    throw new Exception("Missing CHM reference from map: {$this->merge['chm']}");
357
                }
358
            } else {
359 1
                $entry = $chm->getEntryByPath($this->merge['entry']);
360 1
                if ($entry === null) {
361
                    if (!$ignoreErrors) {
362
                        throw new Exception("Missing entry '{$this->merge['entry']}' in CHM file {$this->merge['chm']}");
363
                    }
364
                } else {
365 1
                    $tree = Tree::fromString($chm, $entry->getContents());
366 1
                    $tree->resolve($map, $ignoreErrors);
367 1
                    $result = $tree->getItems();
368
                }
369
            }
370
        }
371 1
        foreach ($result as $newItem) {
372 1
            $newItem->children->resolve($map, $ignoreErrors);
373
        }
374
375 1
        return $result;
376
    }
377
378
    /**
379
     * Search the associated entry in the CHM file.
380
     *
381
     * @return \CHMLib\Entry|null
382
     */
383
    public function findEntry()
384
    {
385
        $result = null;
386
        if ($this->local !== '') {
387
            $path = '/'.ltrim(str_replace('\\', '/', $this->local), '/');
388
            $entry = $this->chm->getEntryByPath($path);
389
            if ($entry === null) {
390
                $p = strpos($path, '#');
391
                if ($p !== false) {
392
                    $path = substr($path, 0, $p);
393
                    $entry = $this->chm->getEntryByPath($path);
394
                }
395
            }
396
            if ($entry !== null && $entry->isFile()) {
397
                $result = $entry;
398
            }
399
        }
400
401
        return $result;
402
    }
403
}
404