-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathPlistParser.inc
155 lines (142 loc) · 4.54 KB
/
PlistParser.inc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
<?php
class plistParser extends XMLReader
{
public function parse($file) {
trigger_error(
'plistParser::parse() is deprecated, please use plistParser::parseFile()',
E_USER_NOTICE
);
return $this->parseFile($file);
}
public function parseFile($file) {
if(basename($file) == $file) {
throw new Exception("Non-relative file path expected", 1);
}
$fileData = file_get_contents("file://" .$file);
//replaces non self closing empty array elements with self closing elements
$fileData = preg_replace('/<array>\s+<\/array>/ism','<array />',$fileData);
$this->xml($fileData);
return $this->process();
}
public function parseString($string) {
$this->XML($string);
return $this->process();
}
private function process() {
// plist's always start with a doctype, use it as a validity check
$this->read();
if($this->nodeType !== XMLReader::DOC_TYPE || $this->name !== "plist") {
throw new Exception(sprintf("Error parsing plist. nodeType: %d -- Name: %s", $this->nodeType, $this->name), 2);
}
// as one additional check, the first element node is always a plist
if(!$this->next("plist") || $this->nodeType !== XMLReader::ELEMENT || $this->name !== "plist") {
throw new Exception(sprintf("Error parsing plist. nodeType: %d -- Name: %s", $this->nodeType, $this->name), 3);
}
$plist = array();
while($this->read()) {
if($this->nodeType == XMLReader::ELEMENT) {
$plist[] = $this->parse_node();
}
}
if(count($plist) == 1 && $plist[0]) {
// Most plists have a dict as their outer most tag
// So instead of returning an array with only one element
// return the contents of the dict instead
return $plist[0];
} else {
return $plist;
}
}
private function parse_node() {
// If not an element, nothing for us to do
if($this->nodeType !== XMLReader::ELEMENT) return;
switch($this->name) {
case 'data':
return base64_decode($this->getNodeText());
break;
case 'real':
return floatval($this->getNodeText());
break;
case 'string':
return $this->getNodeText();
break;
case 'integer':
return intval($this->getNodeText());
break;
case 'date':
return $this->getNodeText();
break;
case 'true':
return true;
break;
case 'false':
return false;
break;
case 'array':
//tests for empty arrays and gracefully moves on keeping the structure of the document intact.
$tmpAarrayTest = $this->readInnerXML();
$tmpAarrayTest = trim($tmpAarrayTest);
if (empty($tmpAarrayTest)) {
return array();
} else {
return $this->parse_array();
}
$tmpAarrayTest = null;
break;
case 'dict':
return $this->parse_dict();
break;
default:
// per DTD, the above is the only valid types
throw new Exception(sprintf("Not a valid plist. %s is not a valid type", $this->name), 4);
}
}
private function parse_dict() {
$array = array();
$this->nextOfType(XMLReader::ELEMENT);
do {
if($this->nodeType !== XMLReader::ELEMENT || $this->name !== "key") {
// If we aren't on a key, then jump to the next key
// per DTD, dicts have to have <key><somevalue> and nothing else
if(!$this->next("key")) {
// no more keys left so per DTD we are done with this dict
return $array;
}
}
$key = $this->getNodeText();
$this->nextOfType(XMLReader::ELEMENT);
$array[$key] = $this->parse_node();
$this->nextOfType(XMLReader::ELEMENT, XMLReader::END_ELEMENT);
} while($this->nodeType && !$this->isNodeOfTypeName(XMLReader::END_ELEMENT, "dict"));
return $array;
}
private function parse_array() {
$array = array();
$this->nextOfType(XMLReader::ELEMENT);
do {
$array[] = $this->parse_node();
// skip over any whitespace
$this->nextOfType(XMLReader::ELEMENT, XMLReader::END_ELEMENT);
} while($this->nodeType && !$this->isNodeOfTypeName(XMLReader::END_ELEMENT, "array"));
return $array;
}
private function getNodeText() {
$string = $this->readString();
// now gobble up everything up to the closing tag
$this->nextOfType(XMLReader::END_ELEMENT);
return $string;
}
private function nextOfType() {
$types = func_get_args();
// skip to next
$this->read();
// check if it's one of the types requested and loop until it's one we want
while($this->nodeType && !(in_array($this->nodeType, $types))) {
// node isn't of type requested, so keep going
$this->read();
}
}
private function isNodeOfTypeName($type, $name) {
return $this->nodeType === $type && $this->name === $name;
}
}