Subversion Repositories php_utils

Rev

Rev 2 | Rev 12 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 daniel-mar 1
<?php
2
 
3
/*
4
 * PHP SimpleXML-Supplement
5
 * Copyright 2020 - 2021 Daniel Marschall, ViaThinkSoft
6
 * Revision 2021-05-25
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
 
21
 
22
// ======== ATTENTION, PLEASE READ ========
23
// This supplement script was created to support rare PHP installations that
24
// do not contain SimpleXML, for example at PHP you need to explicitly
25
// install the package "php-xml" if you want to have SimpleXML (In the PHP
26
// documentation, it is written that SimpleXML is available to all, which is
27
// not true).
28
//
29
// Beware that the supplement behaves differently than the real SimpleXML!
30
// (If you know how to improve this, please feel free to send me a patch)
31
//
32
// Just a few differences towards the original SimpleXML
33
// - print_r() looks different
34
// - The supplement requires that an XML string begins with "<!DOCTYPE" or "<?xml",
35
//   otherwise, the first element will not be stripped away
36
// - The supplement is slow because of regular expressions
37
// - Many functions like "asXML" are not implemented
38
// - There might be other incompatibilities
39
//
40
// So, if you want to use the SimpleXML supplement, then please carefully
41
// test it with your application if it works.
42
// ========================================
43
 
44
if (!function_exists('simplexml_load_string')) {
45
 
46
        // We cannot store the number 0, 1, 2, ... as items in the SimpleXMLElement, because PHP 7.0 had a bug
47
        // that prevented /get_object_vars() from working correctly
48
        // https://stackoverflow.com/questions/46000541/get-object-vars-returning-different-results-depending-on-php-version
49
        // https://stackoverflow.com/questions/4914376/failed-to-get-dynamic-instance-variables-via-phps-reflection/4914405#comment76610293_4914405
50
        define('SIMPLEXML_SUPPLEMENT_MAGIC', '_SIMPLEXML_SUPPLEMENT_IDX_');
51
 
52
        function _simplexml_supplement_isnumeric($x) {
53
                return substr($x,0,strlen(SIMPLEXML_SUPPLEMENT_MAGIC)) === SIMPLEXML_SUPPLEMENT_MAGIC;
54
        }
55
        function _simplexml_supplement_getnumber($x) {
56
                return (int)substr($x,strlen(SIMPLEXML_SUPPLEMENT_MAGIC));
57
        }
58
        function _simplexml_supplement_addnumberprefix($x) {
59
                return SIMPLEXML_SUPPLEMENT_MAGIC.$x;
60
        }
61
 
62
        // We may not store the fields "position" and "attrs" in the SimpleXMLElement object,
63
        // otherweise the typecast SimpleXMLElement=>array will include them
64
        $_simplexml_supplement_properties = array();
65
 
66
        function simplexml_load_file($file): SimpleXMLElement {
67
                return simplexml_load_string(file_get_contents($file));
68
        }
69
 
70
        function simplexml_load_string($testxml): SimpleXMLElement {
71
                $out = new SimpleXMLElement(); /** @phpstan-ignore-line */
72
 
73
                $testxml = preg_replace('@<!\\-\\-.+\\-\\->@','',$testxml); // remove comments
74
                $testxml = preg_replace('@<([^>\\s]+)\\s*/>@smU','<\\1></\\1>',$testxml); // <x/> => <x></x>
75
 
76
                if ((stripos($testxml, '<?xml') !== false) || (stripos($testxml, '<!doctype') !== false)) {
77
                        $testxml = preg_replace('@<\\?.+\\?>@','',$testxml);
78
                        $testxml = preg_replace('@<!doctype.+>@i','',$testxml);
79
                        $m = array();
80
                        preg_match('@<(\\S+?)[^>]*>(.*)</\\1>@smU',$testxml,$m); // find root element
81
                        $root_element = $m[1];
82
                } else {
83
                        $root_element = null;
84
                }
85
 
86
                $m = array();
87
                preg_match_all('@<(\\S+?)([^>]*)>(.*)</\\1>@smU', $testxml, $m, PREG_SET_ORDER);
88
                foreach ($m as $n) {
89
                        $name = $n[1];
90
                        $other = $n[2];
91
                        $val  = $n[3];
92
 
93
                        $val = str_replace('<![CDATA[', '', $val);
94
                        $val = str_replace(']]>', '', $val);
95
                        $val = trim($val);
96
 
97
                        $new = $out->addChild($name, $val);
98
 
99
                        $m2 = array();
100
                        preg_match_all('@(\S+)=\\"([^\\"]+)\\"@smU', $other, $m2, PREG_SET_ORDER);
101
                        foreach ($m2 as $n2) {
102
                                $att_name = $n2[1];
103
                                $att_val = $n2[2];
104
                                $new->addAttribute($att_name, $att_val);
105
                        }
106
                }
107
 
108
                if (!is_null($root_element)) {
109
                        $out = $out->$root_element;
110
                }
111
 
112
                return $out;
113
        }
114
 
115
        class SimpleXMLElement implements ArrayAccess, Iterator {
116
 
117
                function __destruct() {
118
                        global $_simplexml_supplement_properties;
119
                        unset($_simplexml_supplement_properties[spl_object_hash($this)]);
120
                }
121
 
122
                public function addAttribute($name, $val) {
123
                        global $_simplexml_supplement_properties;
124
                        $_simplexml_supplement_properties[spl_object_hash($this)]['attrs'][$name] = $val;
125
                }
126
 
127
                public function attributes() {
128
                        global $_simplexml_supplement_properties;
129
                        return $_simplexml_supplement_properties[spl_object_hash($this)]['attrs'];
130
                }
131
 
132
                public function isSupplement() {
133
                        return true;
134
                }
135
 
136
                public function __construct($val=null) {
137
                        global $_simplexml_supplement_properties;
138
                        $_simplexml_supplement_properties[spl_object_hash($this)] = array(
139
                                "position" => 0,
140
                                "attrs" => array()
141
                        );
142
                        if (!is_null($val)) {
143
                                $this->{_simplexml_supplement_addnumberprefix(0)} = $val;
144
                        }
145
                }
146
 
147
                public function isArray() {
148
                        $vars = get_object_vars($this);
149
                        $max = -1;
150
                        foreach ($vars as $x => $dummy) {
151
                                if (!_simplexml_supplement_isnumeric($x)) {
152
                                        $max = -1;
153
                                        break;
154
                                } else {
155
                                        $num = _simplexml_supplement_getnumber($x);
156
                                        if ($num > $max) $max = $num;
157
                                }
158
                        }
159
                        return $max > 0;
160
                }
161
 
162
                public function addToArray($val) {
163
                        $vars = get_object_vars($this);
164
                        $max = -1;
165
                        foreach ($vars as $x => $dummy) {
166
                                if (!_simplexml_supplement_isnumeric($x)) {
167
                                        $max = -1;
168
                                        break;
169
                                } else {
170
                                        $num = _simplexml_supplement_getnumber($x);
171
                                        if ($num > $max) $max = $num;
172
                                }
173
                        }
174
                        $max++;
175
                        $this->{_simplexml_supplement_addnumberprefix($max)} = $val;
176
                }
177
 
178
                public function __toString() {
179
                        $data = get_object_vars($this);
180
                        if (is_array($data)) {
181
                                if (isset($data[_simplexml_supplement_addnumberprefix(0)])) {
182
                                        return $data[_simplexml_supplement_addnumberprefix(0)];
183
                                } else {
184
                                        return '';
185
                                }
186
                        } else {
187
                                return $data;
188
                        }
189
                }
190
 
191
                public function offsetExists($offset) {
192
                        return isset($this->$offset);
193
                }
194
 
195
                public function offsetGet($offset) {
196
                        return $this->$offset;
197
                }
198
 
199
                public function offsetSet($offset, $value) {
200
                        $this->$offset = $value;
201
                }
202
 
203
                public function offsetUnset($offset) {
204
                        unset($this->$offset);
205
                }
206
 
207
                public function __get($name) {
208
                        // Output nothing
209
                        return new SimpleXMLElement(); /** @phpstan-ignore-line */
210
                }
211
 
212
                public function addChild($name, $val=null) {
213
                        global $_simplexml_supplement_properties;
214
 
215
                        if ($val == null) $val = new SimpleXMLElement(); /** @phpstan-ignore-line */
216
 
217
                        if ((substr(trim($val),0,1) === '<') || (trim($val) == '')) {
218
                                $val = simplexml_load_string($val);
219
                        }
220
 
221
                        $data = get_object_vars($this);
222
 
223
                        if (!isset($data[$name])) {
224
                                if ($val instanceof SimpleXMLElement) {
225
                                        $this->$name = $val;
226
                                } else {
227
                                        $this->$name = new SimpleXMLElement($val);
228
                                }
229
                        } else {
230
                                if (!($val instanceof SimpleXMLElement)) {
231
                                        $val = new SimpleXMLElement($val);
232
                                }
233
 
234
                                if ($data[$name]->isArray()) {
235
                                        $data[$name]->addToArray($val);
236
                                } else {
237
                                        $tmp = new SimpleXMLElement(); /** @phpstan-ignore-line */
238
                                        $tmp->addToArray($data[$name]);
239
                                        $tmp->addToArray($val);
240
                                        $this->$name = $tmp;
241
                                        $_simplexml_supplement_properties[spl_object_hash($this)]['attrs'] = array();
242
                                }
243
                                return $val;
244
                        }
245
 
246
                        return $this->$name;
247
                }
248
 
249
                public function rewind() {
250
                        global $_simplexml_supplement_properties;
251
                        $_simplexml_supplement_properties[spl_object_hash($this)]['position'] = 0;
252
                }
253
 
254
                public function current() {
255
                        global $_simplexml_supplement_properties;
256
                        $vars = get_object_vars($this);
257
                        $cnt = 0;
258
                        foreach ($vars as $x => $dummy) {
259
                                if (($dummy instanceof SimpleXMLElement) && !_simplexml_supplement_isnumeric($x) && $dummy->isArray()) {
260
                                        $vars2 = get_object_vars($dummy);
261
                                        foreach ($vars2 as $x2 => $dummy2) {
262
                                                if ($cnt == $_simplexml_supplement_properties[spl_object_hash($this)]['position']) {
263
                                                        if ($dummy2 instanceof SimpleXMLElement) {
264
                                                                return $dummy2;
265
                                                        } else {
266
                                                                return new SimpleXMLElement($dummy2);
267
                                                        }
268
                                                }
269
                                                $cnt++;
270
                                        }
271
                                } else {
272
                                        if ($cnt == $_simplexml_supplement_properties[spl_object_hash($this)]['position']) {
273
                                                if ($dummy instanceof SimpleXMLElement) {
274
                                                        return $dummy;
275
                                                } else {
276
                                                        return new SimpleXMLElement($dummy);
277
                                                }
278
                                        }
279
                                        $cnt++;
280
                                }
281
                        }
282
 
283
 
284
                }
285
 
286
                public function key() {
287
                        global $_simplexml_supplement_properties;
288
                        $vars = get_object_vars($this);
289
                        $cnt = 0;
290
                        foreach ($vars as $x => $dummy) {
291
                                if (($dummy instanceof SimpleXMLElement) && !_simplexml_supplement_isnumeric($x) && $dummy->isArray()) {
292
                                        $vars2 = get_object_vars($dummy);
293
                                        foreach ($vars2 as $x2 => $dummy2) {
294
                                                if ($cnt == $_simplexml_supplement_properties[spl_object_hash($this)]['position']) return $x/*sic*/;
295
                                                $cnt++;
296
                                        }
297
                                } else {
298
                                        if ($cnt == $_simplexml_supplement_properties[spl_object_hash($this)]['position']) return $x;
299
                                        $cnt++;
300
                                }
301
                        }
302
                }
303
 
304
                public function next() {
305
                        global $_simplexml_supplement_properties;
306
                        ++$_simplexml_supplement_properties[spl_object_hash($this)]['position'];
307
                }
308
 
309
                public function valid() {
310
                        global $_simplexml_supplement_properties;
311
 
312
                        $vars = get_object_vars($this);
313
                        $cnt = 0;
314
                        foreach ($vars as $x => $dummy) {
315
                                if (($dummy instanceof SimpleXMLElement) && !_simplexml_supplement_isnumeric($x) && $dummy->isArray()) {
316
                                        $vars2 = get_object_vars($dummy);
317
                                        foreach ($vars2 as $x2 => $dummy2) {
318
                                                $cnt++;
319
                                        }
320
                                } else {
321
                                        $cnt++;
322
                                }
323
                        }
324
 
325
                        return $_simplexml_supplement_properties[spl_object_hash($this)]['position'] < $cnt;
326
                }
327
 
328
        }
329
}