Subversion Repositories php_utils

Compare Revisions

Regard whitespace Rev 1 → Rev 2

/trunk/simplexml_supplement.inc.phps
0,0 → 1,329
<?php
 
/*
* PHP SimpleXML-Supplement
* Copyright 2020 - 2021 Daniel Marschall, ViaThinkSoft
* Revision 2021-05-25
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
 
 
// ======== ATTENTION, PLEASE READ ========
// This supplement script was created to support rare PHP installations that
// do not contain SimpleXML, for example at PHP you need to explicitly
// install the package "php-xml" if you want to have SimpleXML (In the PHP
// documentation, it is written that SimpleXML is available to all, which is
// not true).
//
// Beware that the supplement behaves differently than the real SimpleXML!
// (If you know how to improve this, please feel free to send me a patch)
//
// Just a few differences towards the original SimpleXML
// - print_r() looks different
// - The supplement requires that an XML string begins with "<!DOCTYPE" or "<?xml",
// otherwise, the first element will not be stripped away
// - The supplement is slow because of regular expressions
// - Many functions like "asXML" are not implemented
// - There might be other incompatibilities
//
// So, if you want to use the SimpleXML supplement, then please carefully
// test it with your application if it works.
// ========================================
 
if (!function_exists('simplexml_load_string')) {
 
// We cannot store the number 0, 1, 2, ... as items in the SimpleXMLElement, because PHP 7.0 had a bug
// that prevented /get_object_vars() from working correctly
// https://stackoverflow.com/questions/46000541/get-object-vars-returning-different-results-depending-on-php-version
// https://stackoverflow.com/questions/4914376/failed-to-get-dynamic-instance-variables-via-phps-reflection/4914405#comment76610293_4914405
define('SIMPLEXML_SUPPLEMENT_MAGIC', '_SIMPLEXML_SUPPLEMENT_IDX_');
 
function _simplexml_supplement_isnumeric($x) {
return substr($x,0,strlen(SIMPLEXML_SUPPLEMENT_MAGIC)) === SIMPLEXML_SUPPLEMENT_MAGIC;
}
function _simplexml_supplement_getnumber($x) {
return (int)substr($x,strlen(SIMPLEXML_SUPPLEMENT_MAGIC));
}
function _simplexml_supplement_addnumberprefix($x) {
return SIMPLEXML_SUPPLEMENT_MAGIC.$x;
}
 
// We may not store the fields "position" and "attrs" in the SimpleXMLElement object,
// otherweise the typecast SimpleXMLElement=>array will include them
$_simplexml_supplement_properties = array();
 
function simplexml_load_file($file): SimpleXMLElement {
return simplexml_load_string(file_get_contents($file));
}
 
function simplexml_load_string($testxml): SimpleXMLElement {
$out = new SimpleXMLElement(); /** @phpstan-ignore-line */
 
$testxml = preg_replace('@<!\\-\\-.+\\-\\->@','',$testxml); // remove comments
$testxml = preg_replace('@<([^>\\s]+)\\s*/>@smU','<\\1></\\1>',$testxml); // <x/> => <x></x>
 
if ((stripos($testxml, '<?xml') !== false) || (stripos($testxml, '<!doctype') !== false)) {
$testxml = preg_replace('@<\\?.+\\?>@','',$testxml);
$testxml = preg_replace('@<!doctype.+>@i','',$testxml);
$m = array();
preg_match('@<(\\S+?)[^>]*>(.*)</\\1>@smU',$testxml,$m); // find root element
$root_element = $m[1];
} else {
$root_element = null;
}
 
$m = array();
preg_match_all('@<(\\S+?)([^>]*)>(.*)</\\1>@smU', $testxml, $m, PREG_SET_ORDER);
foreach ($m as $n) {
$name = $n[1];
$other = $n[2];
$val = $n[3];
 
$val = str_replace('<![CDATA[', '', $val);
$val = str_replace(']]>', '', $val);
$val = trim($val);
 
$new = $out->addChild($name, $val);
 
$m2 = array();
preg_match_all('@(\S+)=\\"([^\\"]+)\\"@smU', $other, $m2, PREG_SET_ORDER);
foreach ($m2 as $n2) {
$att_name = $n2[1];
$att_val = $n2[2];
$new->addAttribute($att_name, $att_val);
}
}
 
if (!is_null($root_element)) {
$out = $out->$root_element;
}
 
return $out;
}
 
class SimpleXMLElement implements ArrayAccess, Iterator {
 
function __destruct() {
global $_simplexml_supplement_properties;
unset($_simplexml_supplement_properties[spl_object_hash($this)]);
}
 
public function addAttribute($name, $val) {
global $_simplexml_supplement_properties;
$_simplexml_supplement_properties[spl_object_hash($this)]['attrs'][$name] = $val;
}
 
public function attributes() {
global $_simplexml_supplement_properties;
return $_simplexml_supplement_properties[spl_object_hash($this)]['attrs'];
}
 
public function isSupplement() {
return true;
}
 
public function __construct($val=null) {
global $_simplexml_supplement_properties;
$_simplexml_supplement_properties[spl_object_hash($this)] = array(
"position" => 0,
"attrs" => array()
);
if (!is_null($val)) {
$this->{_simplexml_supplement_addnumberprefix(0)} = $val;
}
}
 
public function isArray() {
$vars = get_object_vars($this);
$max = -1;
foreach ($vars as $x => $dummy) {
if (!_simplexml_supplement_isnumeric($x)) {
$max = -1;
break;
} else {
$num = _simplexml_supplement_getnumber($x);
if ($num > $max) $max = $num;
}
}
return $max > 0;
}
 
public function addToArray($val) {
$vars = get_object_vars($this);
$max = -1;
foreach ($vars as $x => $dummy) {
if (!_simplexml_supplement_isnumeric($x)) {
$max = -1;
break;
} else {
$num = _simplexml_supplement_getnumber($x);
if ($num > $max) $max = $num;
}
}
$max++;
$this->{_simplexml_supplement_addnumberprefix($max)} = $val;
}
 
public function __toString() {
$data = get_object_vars($this);
if (is_array($data)) {
if (isset($data[_simplexml_supplement_addnumberprefix(0)])) {
return $data[_simplexml_supplement_addnumberprefix(0)];
} else {
return '';
}
} else {
return $data;
}
}
 
public function offsetExists($offset) {
return isset($this->$offset);
}
 
public function offsetGet($offset) {
return $this->$offset;
}
 
public function offsetSet($offset, $value) {
$this->$offset = $value;
}
 
public function offsetUnset($offset) {
unset($this->$offset);
}
 
public function __get($name) {
// Output nothing
return new SimpleXMLElement(); /** @phpstan-ignore-line */
}
 
public function addChild($name, $val=null) {
global $_simplexml_supplement_properties;
 
if ($val == null) $val = new SimpleXMLElement(); /** @phpstan-ignore-line */
 
if ((substr(trim($val),0,1) === '<') || (trim($val) == '')) {
$val = simplexml_load_string($val);
}
 
$data = get_object_vars($this);
 
if (!isset($data[$name])) {
if ($val instanceof SimpleXMLElement) {
$this->$name = $val;
} else {
$this->$name = new SimpleXMLElement($val);
}
} else {
if (!($val instanceof SimpleXMLElement)) {
$val = new SimpleXMLElement($val);
}
 
if ($data[$name]->isArray()) {
$data[$name]->addToArray($val);
} else {
$tmp = new SimpleXMLElement(); /** @phpstan-ignore-line */
$tmp->addToArray($data[$name]);
$tmp->addToArray($val);
$this->$name = $tmp;
$_simplexml_supplement_properties[spl_object_hash($this)]['attrs'] = array();
}
return $val;
}
 
return $this->$name;
}
 
public function rewind() {
global $_simplexml_supplement_properties;
$_simplexml_supplement_properties[spl_object_hash($this)]['position'] = 0;
}
 
public function current() {
global $_simplexml_supplement_properties;
$vars = get_object_vars($this);
$cnt = 0;
foreach ($vars as $x => $dummy) {
if (($dummy instanceof SimpleXMLElement) && !_simplexml_supplement_isnumeric($x) && $dummy->isArray()) {
$vars2 = get_object_vars($dummy);
foreach ($vars2 as $x2 => $dummy2) {
if ($cnt == $_simplexml_supplement_properties[spl_object_hash($this)]['position']) {
if ($dummy2 instanceof SimpleXMLElement) {
return $dummy2;
} else {
return new SimpleXMLElement($dummy2);
}
}
$cnt++;
}
} else {
if ($cnt == $_simplexml_supplement_properties[spl_object_hash($this)]['position']) {
if ($dummy instanceof SimpleXMLElement) {
return $dummy;
} else {
return new SimpleXMLElement($dummy);
}
}
$cnt++;
}
}
 
 
}
 
public function key() {
global $_simplexml_supplement_properties;
$vars = get_object_vars($this);
$cnt = 0;
foreach ($vars as $x => $dummy) {
if (($dummy instanceof SimpleXMLElement) && !_simplexml_supplement_isnumeric($x) && $dummy->isArray()) {
$vars2 = get_object_vars($dummy);
foreach ($vars2 as $x2 => $dummy2) {
if ($cnt == $_simplexml_supplement_properties[spl_object_hash($this)]['position']) return $x/*sic*/;
$cnt++;
}
} else {
if ($cnt == $_simplexml_supplement_properties[spl_object_hash($this)]['position']) return $x;
$cnt++;
}
}
}
 
public function next() {
global $_simplexml_supplement_properties;
++$_simplexml_supplement_properties[spl_object_hash($this)]['position'];
}
 
public function valid() {
global $_simplexml_supplement_properties;
 
$vars = get_object_vars($this);
$cnt = 0;
foreach ($vars as $x => $dummy) {
if (($dummy instanceof SimpleXMLElement) && !_simplexml_supplement_isnumeric($x) && $dummy->isArray()) {
$vars2 = get_object_vars($dummy);
foreach ($vars2 as $x2 => $dummy2) {
$cnt++;
}
} else {
$cnt++;
}
}
 
return $_simplexml_supplement_properties[spl_object_hash($this)]['position'] < $cnt;
}
 
}
}