Completed
Push — master ( eb0ce8...2a8299 )
by SuRaMoN
02:02
created

PathIterator::valid()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace SimpleXmlReader;
4
5
use XMLReader;
6
use Iterator;
7
use DOMDocument;
8
9
class PathIterator implements Iterator
10
{
11
    const IS_MATCH = 'IS_MATCH';
12
    const DESCENDANTS_COULD_MATCH = 'DESCENDANTS_COULD_MATCH';
13
    const DESCENDANTS_CANT_MATCH = 'DESCENDANTS_CANT_MATCH';
14
15
    protected $reader;
16
    protected $searchPath;
17
    protected $searchCrumbs;
18
    protected $crumbs;
19
    protected $currentDomExpansion;
20
    protected $rewindCount;
21
    protected $isValid;
22
    protected $returnType;
23
24
    public function __construct(ExceptionThrowingXMLReader $reader, $path, $returnType)
25
    {
26
        $this->reader = $reader;
27
        $this->searchPath = $path;
28
        $this->searchCrumbs = explode('/', $path);
29
        $this->crumbs = array();
30
        $this->matchCount = -1;
0 ignored issues
show
Bug introduced by
The property matchCount does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
31
        $this->rewindCount = 0;
32
        $this->isValid = false;
33
        $this->returnType = $returnType;
34
    }
35
36
    public function current()
37
    {
38
        return $this->currentDomExpansion;
39
    }
40
41
    public function key()
42
    {
43
        return $this->matchCount;
44
    }
45
46
    public function next()
47
    {
48
        $this->isValid = $this->tryGotoNextIterationElement();
49
50
        if ($this->isValid) {
51
            $this->matchCount += 1;
52
            $this->currentDomExpansion = $this->getXMLObject();
53
        }
54
    }
55
56
    public function rewind()
57
    {
58
        $this->rewindCount += 1;
59
        if ($this->rewindCount > 1) {
60
            throw new XmlException('Multiple rewinds not supported');
61
        }
62
        $this->next();
63
    }
64
65
    public function valid()
66
    {
67
        return $this->isValid;
68
    }
69
70
    protected function getXMLObject()
71
    {
72
        switch ($this->returnType) {
73
            case SimpleXMLReader::RETURN_DOM:
74
                return $this->reader->expand();
75
76
            case SimpleXMLReader::RETURN_INNER_XML_STRING:
77
                return $this->reader->readInnerXML();
78
79
            case SimpleXMLReader::RETURN_OUTER_XML_STRING:
80
                return $this->reader->readOuterXML();
81
82
            case SimpleXMLReader::RETURN_SIMPLE_XML:
83
                $simplexml = simplexml_import_dom($this->reader->expand(new DOMDocument('1.0')));
84
                if (false === $simplexml) {
85
                    throw new XMlException('Failed to create a SimpleXMLElement from the current XML node (invalid XML?)');
86
                }
87
88
                return $simplexml;
89
90
            default:
91
                throw new Exception(sprintf("Unknown return type: %s", $this->returnType));
92
        }
93
    }
94
95
    protected function pathIsMatching()
96
    {
97
        if (count($this->crumbs) > count($this->searchCrumbs)) {
98
            return self::DESCENDANTS_CANT_MATCH;
99
        }
100
        foreach ($this->crumbs as $i => $crumb) {
101
            $searchCrumb = $this->searchCrumbs[$i];
102
            if ($searchCrumb == $crumb || $searchCrumb == '*') {
103
                continue;
104
            }
105
            return self::DESCENDANTS_CANT_MATCH;
106
        }
107
        if (count($this->crumbs) == count($this->searchCrumbs)) {
108
            return self::IS_MATCH;
109
        }
110
        return self::DESCENDANTS_COULD_MATCH;
111
    }
112
113
    public function tryGotoNextIterationElement()
114
    {
115
        $r = $this->reader;
116
117
        if ($r->nodeType == XMLReader::NONE) {
118
            // first time we do a read from the xml
119
            if (! $r->tryRead()) { return false; }
120
        } else {
121
            // if we have already had a match
122
            if (! $r->tryNext()) { return false; }
123
        }
124
125
        while (true) {
126
            // search for open tag
127
            while ($r->nodeType != XMLReader::ELEMENT) {
128
                if (! $r->tryRead()) { return false; }
129
            }
130
131
            // fill crumbs
132
            array_splice($this->crumbs, $r->depth, count($this->crumbs), array($r->name));
133
134
            switch ($this->pathIsMatching()) {
135
136
                case self::DESCENDANTS_COULD_MATCH:
137
                    if (! $r->tryRead()) { return false; }
138
                    continue 2;
139
140
                case self::DESCENDANTS_CANT_MATCH:
141
                    if (! $r->tryNext()) { return false; }
142
                    continue 2;
143
144
                case self::IS_MATCH:
145
                    return true;
146
            }
147
148
            return false;
149
        }
150
    }
151
}
152