123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883 |
- <?php
- class xmddoc
- {
-
- var $names;
- var $numbers;
- var $textstring;
- var $error;
- var $_nesting;
- var $_ns;
- var $_concat;
- var $_nsp;
- var $_last;
- var $_strings;
- var $parent;
- var $name;
- var $ns;
- var $attributes;
- var $atns;
- var $children;
- function xmd_get_element($parent = 0) // for convenience, readonly copy
- {
-
-
- if ($parent < 0 || $parent > $this->_last) return array();
- return array_merge($this->children[$parent], $this->attributes[$parent],
- array('?name' => $this->name[$parent],
- '?parent' => $this->parent[$parent]));
- }
- function xmd_get_ns_uri($parent = 0, $attName = '')
- {
- if ($parent < 0 || $parent > $this->_last) return '';
- return $attName ? $this->names[$this->atns[$parent][$attName]] :
- $this->names[$this->ns[$parent]];
- }
- function xmd_remove_element($child) // success = TRUE
- {
- if ($child <= 0 || $child > $this->_last) return FALSE;
- $parent = $this->parent[$child];
- foreach ($this->children[$parent] as $key => $value)
- if ($value === $child)
- {
- unset($this->children[$parent][$key]); return TRUE;
- }
- return FALSE;
- }
- function xmd_remove_nodes($children, $parent = 0) // success = TRUE
- {
- if ($parent < 0 || $parent > $this->_last) return FALSE;
- if (!is_array($children)) $children = array($children);
- foreach ($children as $child)
- {
- $childFound = FALSE;
- foreach ($this->children[$parent] as $key => $value)
- if ($value === $child)
- {
- unset($this->children[$parent][$key]);
- $childFound = TRUE; break;
- }
- if (!$childFound) return FALSE;
- }
- return TRUE;
- }
- function xmd_update($xmPath, $text = '', $parent = 0) // success = TRUE
- {
- if ($parent < 0 || $parent > $this->_last ||
- !is_string($text) || !is_string($xmPath)) return FALSE;
- $m = array();
- if (api_ereg('^(.*)([~!@])(.*)$', $xmPath, $m))
- {
- $xmPath = $m[1]; $op = $m[2]; $name = $m[3];
- }
- if (($elem = $this->xmd_select_single_element($xmPath, $parent)) == -1)
- return FALSE;
- if (isset($op))
- {
- if ($op == '!' && $name)
- {
- $this->xmd_add_text_element($name, $text, $elem); return TRUE;
- }
- elseif ($op == '@' && $name)
- {
- $this->attributes[$elem][$name] = $text; return TRUE;
- }
- elseif ($op == '~' && !$name)
- return $this->xmd_remove_element($elem);
- return FALSE;
- }
- if (($nch = count($this->children[$elem])) > 1) return FALSE;
- $this->children[$elem][0] = $text; return TRUE;
- }
- function xmd_update_many($xmPaths, $subPath = '', $text = '', $parent = 0)
- {
- $result = TRUE;
- foreach (explode(',', $xmPaths) as $xmPath)
- foreach ($this->xmd_select_elements($xmPath, $parent) as $elem)
- $result &= $this->xmd_update($subPath, $text, $elem);
-
- return $result;
- }
- function xmd_copy_foreign_child($fdoc, $fchild = 0, $parent = 0)
- {
- $my_queue = array($fchild, $parent);
- while (!is_null($fchild = array_shift($my_queue)))
- {
- $parent = array_shift($my_queue);
- if (is_string($fchild))
- $this->xmd_add_text($fchild, $parent);
- elseif (isset($fdoc->name[$fchild]))
- {
- $fullname = $fdoc->name[$fchild];
- $attribs = array(); $nsdefs = array();
- if (($nsn = $fdoc->ns[$fchild]))
- $fullname = $fdoc->names[$nsn] . ':' . $fullname;
- foreach ($fdoc->attributes[$fchild] as $name => $value)
- {
- if (($p = strrpos($name, ':')) !== FALSE)
- $nsdefs[$name] = $value;
- else
- {
- if (($nsn = $fdoc->atns[$fchild][$name]))
- $name = $fdoc->names[$nsn] . ':' . $name;
- $attribs[$name] = $value;
- }
- }
- $child = $this->xmd_add_element($fullname, $parent,
- array_merge($attribs, $nsdefs));
- foreach ($fdoc->children[$fchild] as $ch)
- array_push($my_queue, $ch, $child);
-
- }
- }
- }
- function xmd_add_element($name, $parent = 0, $attribs = array())
- {
- if (!is_string($name) || $name == '') return -1;
- if (($p = strrpos($name, ':')) !== FALSE)
- if ($p == 0 || $p == api_strlen($name) - 1) return -1;
- $child = ($this->_last += 1); $uris = array(); $uri = '';
- if ($p)
- {
- $uri = api_substr($name, 0, $p); $name = api_substr($name, $p + 1);
- $uris[] = $uri;
- }
- $this->parent[$child] = $parent; $this->name[$child] = $name;
- $this->ns[$child] = $uri ? $this->_lookup($uri) : 0;
- $this->children[$child] = array();
- $this->attributes[$child] = array(); $this->atns[$child] = array();
- foreach ($attribs as $name => $value)
- if (($uri = $this->xmd_set_attribute($child, $name, $value, FALSE)))
- $uris[] = $uri;
- if ($parent >= 0 && $parent <= $this->_last)
- $this->children[$parent][] = $child;
- foreach ($uris as $uri) $this->_nsPfx($child, $uri);
-
- return $child;
- }
- function xmd_set_attribute($parent, $name, $value, $checkurihaspfx = TRUE)
- {
- if (!is_string($name) || $name == '') return '';
- if (($p = strrpos($name, ':')) !== FALSE)
- if ($p == 0 || $p == api_strlen($name) - 1) return '';
- $uri = '';
- if ($p) if (api_substr($name, 0, 6) != 'xmlns:')
- {
- $uri = api_substr($name, 0, $p); $name = api_substr($name, $p + 1);
- }
- $this->attributes[$parent][$name] = $value;
- $this->atns[$parent][$name] = $uri ? $this->_lookup($uri) : 0;
- if ($checkurihaspfx) if ($uri) $this->_nsPfx($parent, $uri);
- if (api_substr($name, 0, 6) == 'xmlns:')
- $this->_nsp[api_substr($name, 6)] = $value;
- return $uri;
- }
- function xmd_add_text($text, $parent = 0) // success = TRUE
- {
- if ($parent < 0 || $parent > $this->_last || !is_string($text))
- return FALSE;
- if ($text) $this->children[$parent][] = $text; return TRUE;
- }
- function xmd_add_text_element($name, $text, $parent = 0, $attribs = array())
- {
- $this->xmd_add_text($text,
- $child = $this->xmd_add_element($name, $parent, $attribs));
- return $child;
- }
- function xmd_text($parent = 0)
- {
- if ($parent < 0 || $parent > $this->_last) return '';
- $text = '';
- foreach ($this->children[$parent] as $child)
- $text .= is_string($child) ? $child : $this->xmd_text($child);
- return $text;
- }
- function xmd_xml($increase = ' ', $indent = '', $lbr = "\n", $parent = 0)
- {
- global $charset;
- if ($parent < 0 || $parent > $this->_last) return '';
- $uri = $this->names[$this->ns[$parent]];
- $pfxc = ($uri == '') ? '' : $this->_nsPfx($parent, $uri);
- $dbg = '';
- $result = $indent . '<' . ($element = $pfxc . $this->name[$parent]);
- $atnsp = $this->atns[$parent];
- foreach ($this->attributes[$parent] as $name => $value)
- {
- if (isset($atnsp[$name]))
- $atnsn = $atnsp[$name];
- elseif (isset($atnsn))
- unset($atnsn);
- $uri = isset($atnsn) && isset($this->names[$atnsn]) ?
- $this->names[$atnsn] : '';
- $pfxc = ($uri == '') ? '' : $this->_nsPfx($parent, $uri);
- $result .= ' ' . $pfxc . $name
- . '="' . htmlspecialchars($value, ENT_QUOTES, $charset) . '"';
- }
- if (count($this->children[$parent]) == 0)
- return $result . ' />' . $dbg;
- $result .= '>';
- foreach ($this->children[$parent] as $child)
- $result .= is_string($child) ? htmlspecialchars($child, ENT_QUOTES, $charset) : ($lbr .
- $this->xmd_xml($increase, $indent.$increase, $lbr, $child));
- if (!is_string($child)) $result .= $lbr . $indent;
- return $result . '</' . $element . '>' . $dbg;
- }
- function xmd_value($xmPath, $parent = 0, $fix = array(), $fun = '')
- {
-
-
- if ($parent < 0 || $parent > $this->_last || !is_string($xmPath))
- return '';
- if (($p = strrpos($xmPath, '@')) !== FALSE)
- {
- $attName = api_substr($xmPath, $p+1); $xmPath = api_substr($xmPath, 0, $p);
- }
- if (!($elems = $this->xmd_select_elements($xmPath, $parent))) return '';
- $result = ''; $fixin = isset($fix['in']) ? $fix['in'] : '';
- foreach ($elems as $elem)
- {
- $value = isset($attName) && api_strlen($attName) >= 1 ?
- ($attName == '.' ? $this->name[$elem] :
- ($attName{0} == '*' ?
- $this->_sibnum($elem, api_substr($attName, 1)) :
- $this->attributes[$elem][$attName])) :
- $this->xmd_text($elem);
- $result .= $fixin . ($fun ? $fun($value) : $value);
- }
- return (isset($fix['pre']) ? $fix['pre'] : '') .
- api_substr($result, api_strlen($fixin)) .
- (isset($fix['post']) ? $fix['post'] : '');
- }
- function xmd_html_value($xmPath, $parent = 0, $fun = 'htmlspecialchars')
- {
- if (!is_string($xmPath)) return '';
- $fix = array();
- if (($p = api_strpos($xmPath, ' -% ')) !== FALSE)
- {
- $fix['pre'] = api_substr($xmPath, 0, $p);
- $xmPath = api_substr($xmPath, $p+4);
- }
- if (($p = api_strpos($xmPath, ' %- ')) !== FALSE)
- {
- $fix['post'] = api_substr($xmPath, $p+4);
- $xmPath = api_substr($xmPath, 0, $p);
- }
- if (($p = api_strpos($xmPath, ' ')) !== FALSE)
- {
- $fix['in'] = api_substr($xmPath, $p+1);
- $xmPath = api_substr($xmPath, 0, $p);
- }
- return $this->xmd_value($xmPath, $parent, $fix, $fun);
- }
- function xmd_select_single_element($xmPath, $parent = 0) // for convenience
- {
- $elements = $this->xmd_select_elements($xmPath, $parent);
- if (count($elements) == 0) return -1;
- return $elements[0];
- }
- function xmd_select_elements_where($xmPath,
- $subPath = '.', $value = '', $parent = 0)
- {
- if (!is_string($subPath)) return array();
- $elems = array(); if ($subPath == '.') $subPath = '';
- foreach ($this->xmd_select_elements($xmPath, $parent) as $elem)
- if ($this->xmd_value($subPath, $elem) == $value) $elems[] = $elem;
- return $elems;
- }
- function xmd_select_elements_where_notempty($xmPath,
- $subPath = '.', $parent = 0)
- {
- if (!is_string($subPath)) return array();
- $elems = array(); if ($subPath == '.') $subPath = '';
- foreach ($this->xmd_select_elements($xmPath, $parent) as $elem)
- if ($this->xmd_value($subPath, $elem)) $elems[] = $elem;
- return $elems;
- }
- function xmd_select_elements($xmPath, $parent = 0)
- {
-
-
-
-
-
- if (api_substr($xmPath, 0, 3) == '/*/')
- {
- $xmPath = api_substr($xmPath, 3); $parent = 0;
- }
- if ($parent < 0 || $parent > $this->_last) return array();
- while (api_substr($xmPath, 0, 1) == '/') $xmPath = api_substr($xmPath, 1);
- while (api_substr($xmPath, -1) == '/') $xmPath = api_substr($xmPath, 0, -1);
- if ($xmPath == '' || $xmPath == '.') return array($parent);
- if ($xmPath == '..')
- {
- if ($parent > 0) return array($this->parent[$parent]);
- return array($parent);
- }
- if ($xmPath{0} == '-' || $xmPath{0} == '+')
- {
- $sib = $this->_sibnum($parent, api_substr($xmPath, 1), $xmPath{0});
- if ($sib == -1) return array(); return array($sib);
- }
- $m = array();
- if (api_ereg('^(.+)/([^/]+)$', $xmPath, $m))
- {
- if (!($set = $this->xmd_select_elements($m[1], $parent)))
- return $set;
- if (count($set) == 1)
- return $this->xmd_select_elements($m[2], $set[0]);
- $bigset = array(); $m2 = $m[2];
- foreach ($set as $e)
- $bigset = array_merge($bigset,
- $this->xmd_select_elements($m2, $e));
- return $bigset;
- }
- $xmName = $xmPath; $xmNum = 0; $elems = array();
- if (api_ereg('^(.+)\[(-?[0-9]+)\]$', $xmPath, $m))
- {
- $xmName = $m[1]; $xmNum = (int) $m[2];
- }
- foreach ($this->children[$parent] as $child) if (!is_string($child))
- if ($xmName == '*' || ($this->name[$child]) == $xmName)
- $elems[] = $child;
- if ($xmNum == 0) return $elems;
- $xmNum = ($xmNum > 0) ? $xmNum - 1 : count($elems) + $xmNum;
- return ($xmNum < count($elems)) ? array($elems[$xmNum]) : array();
- }
-
-
-
-
-
-
-
-
- function xmd_cache() // store all data in numbers+names+textstring
- {
- $this->numbers = array(); $this->textstring = ''; $this->_strings = 0;
-
- for ($n = 0; $n <= $this->_last; $n++)
- {
- $this->numbers[] = count($this->children[$n]);
- foreach ($this->children[$n] as $ch)
- {
- if (is_string($ch))
- {
- $this->numbers[] = 0; $this->_strings += 1;
- $this->numbers[] = strlen($ch); $this->textstring .= $ch;
- }
- else
- {
- $this->numbers[] = ($ch-$n);
- }
- }
- $this->numbers[] = count($this->attributes[$n]);
- foreach ($this->attributes[$n] as $name => $value)
- {
- $this->numbers[] = $this->_lookup($name);
- $this->numbers[] = $this->atns[$n][$name];
- $this->numbers[] = strlen($value); $this->textstring .= $value;
- }
- $this->numbers[] = $this->_lookup($this->name[$n]);
- $this->numbers[] = $this->ns[$n];
- $this->numbers[] = $n - $this->parent[$n];
- }
- }
- function xmddoc($strings, $charset = null, $textstring = '')
- {
- if (empty($charset))
- {
- $charset = api_get_system_encoding();
- }
- $this->parent = array(); $this->name = array();
- $this->ns = array(); $this->attributes = array();
- $this->atns = array(); $this->children = array();
- $this->error = ''; $this->_nesting = array();
- $this->_ns = array(); $this->_last = -1;
- $this->_nsp = array();
- foreach (explode(',', 'eb,tn,eg,ut,as,ne,jt,ne,ah,en,er') as $pfx)
- $this->_nsp[$pfx] = '';
- if (is_array($charset))
- {
- $this->names = $strings; $this->numbers = $charset;
- $this->textstring = $textstring; $this->_uncache(); return;
- }
- $this->names = array(); $this->_lookup('');
-
-
-
-
- $xml_parser = xml_parser_create_ns(api_is_utf8($charset) ? 'UTF-8' : 'ISO-8859-1', ':');
- xml_set_object($xml_parser,$this);
-
- xml_set_element_handler($xml_parser, '_startElement', '_endElement');
- xml_set_character_data_handler($xml_parser, '_cData');
- xml_set_start_namespace_decl_handler($xml_parser, '_startNs');
-
-
- xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, FALSE);
- if (!is_array($strings)) $strings = array($strings);
- if (count($strings) && (api_substr($strings[0], 0, 5) != '<?xml') &&
- !xml_parse($xml_parser,
- '<?xml version="1.0" encoding="' . $charset . '"?>', FALSE))
- {
- $this->error = 'Encoding ' . $charset . ': ' .
- xml_error_string(xml_get_error_code($xml_parser));
- $strings = array();
- }
- foreach ($strings as $s)
- {
- if (api_substr($s, -1) != "\n") $s .= "\n";
- if (!xml_parse($xml_parser, $s, FALSE))
- {
- $errCode = xml_get_error_code($xml_parser);
- $this->error = 'Line '. xml_get_current_line_number($xml_parser) .
- ' (c' . xml_get_current_column_number($xml_parser) .
-
- '): error ' . $errCode . '= ' . xml_error_string($errCode);
- break;
- }
- }
- xml_parse($xml_parser, '', TRUE); xml_parser_free($xml_parser);
- }
-
- function _sibnum($parent, $name = '', $pmn = 'N') // sibling or number
- {
- if ($parent <= 0) return -1;
- $found = FALSE; $prev = -1; $next = -1; $num = 0;
- foreach ($this->children[$this->parent[$parent]] as $child)
- {
- if (is_string($child)) continue;
- $name_ok = $name ? ($this->name[$child] == $name) : TRUE;
- if ($found && $name_ok)
- {
- $next = $child; break;
- }
- elseif ($parent === $child)
- {
- $num ++; $found = TRUE;
- }
- elseif ($name_ok)
- {
- $num ++; $prev = $child;
- }
- }
- return ($pmn == '-') ? $prev : (($pmn == '+') ? $next : $num);
- }
- function _uncache() // restore all data from numbers+names+textstring
- {
- $n = -1; $numptr = -1; $txtptr = 0; $count = count($this->numbers);
- $A0 = array();
- while (++$numptr < $count)
- {
- $n++;
- if (($countdown = $this->numbers[$numptr]) == 0)
- {
- $this->children[$n] = $A0;
- }
- else while (--$countdown >= 0)
- {
- if (($chc = $this->numbers[++$numptr]) == 0)
- {
- $this->children[$n][] = api_substr($this->textstring,
- $txtptr, ($len = $this->numbers[++$numptr]));
- $txtptr += $len;
- }
- else
- {
- $this->children[$n][] = $n + $chc;
- }
- }
- if (($countdown = $this->numbers[++$numptr]) == 0)
- {
- $this->attributes[$n] = $this->atns[$n] = $A0;
- }
- else while (--$countdown >= 0)
- {
- $name = $this->names[$this->numbers[++$numptr]];
- $this->atns[$n][$name] = $this->numbers[++$numptr];
- $this->attributes[$n][$name] = api_substr($this->textstring,
- $txtptr, ($len = $this->numbers[++$numptr]));
- $txtptr += $len;
- }
- $this->name[$n] = $this->names[$this->numbers[++$numptr]];
- $this->ns[$n] = $this->numbers[++$numptr];
- $this->parent[$n] = $n - $this->numbers[++$numptr];
- }
- $this->_last = $n;
- }
- function _startElement($parser, $name, $attribs)
- {
- $level = count($this->_nesting);
- $parent = ($level == 0) ? -1 : $this->_nesting[$level-1];
- $child = $this->xmd_add_element($name, $parent,
- array_merge($attribs, $this->_ns));
- $this->_nesting[] = $child; $this->_ns = array();
- $this->_concat = FALSE;
- }
- function _endElement($parser, $name)
- {
- array_pop($this->_nesting); $this->_concat = FALSE;
- }
- function _cData($parser, $data)
- {
- if (!ltrim($data)) return;
- $level = count($this->_nesting);
- $parent = ($level == 0) ? -1 : $this->_nesting[$level-1];
- if ($parent >= 0)
- {
- $nc = count($this->children[$parent]);
- $pcs = ($nc > 0 && is_string($this->children[$parent][$nc - 1]));
- if ($pcs && api_strlen($data) == 1) $this->_concat = TRUE;
-
- if ($pcs && $data{0} > '~') $this->_concat = TRUE;
-
- if ($this->_concat)
- {
- $this->children[$parent][$nc - 1] .= $data;
- $this->_concat = (api_strlen($data) == 1);
- }
- else
- $this->children[$parent][] = $pcs ? "\n" . $data : $data;
- }
- }
- function _startNs($parser, $pfx, $uri) // called before _startElement
- {
- $this->_ns['xmlns' . ($pfx ? ':'.$pfx : '')] = $uri;
- $this->_nsp[$pfx] = $uri;
- }
- function _nsPfx($ppar, $uri) // find namespace prefix
- {
- while ($ppar >= 0)
- {
- foreach ($this->attributes[$ppar] as $name => $value)
- if (api_substr($name, 0, 5) == 'xmlns' && $value == $uri)
- {
- $pfxc = api_substr($name, 6) . api_substr($name, 5, 1); break 2;
- }
- $ppar = $this->parent[$ppar];
- }
- if ($ppar >= 0) return $pfxc; if ($uri == '') return '';
- if ($uri == 'http://www.w3.org/XML/1998/namespace') return 'xml:';
- foreach($this->_nsp as $pfx => $used) if (!$used) break;
- $this->_nsp[$pfx] = $uri; $xmlnspfx = 'xmlns:' . $pfx;
- $this->attributes[0][$xmlnspfx] = $uri; $this->atns[0][$xmlnspfx] = 0;
- return $pfx . ':';
- }
- function _lookup($name) // for namespaces + see cache
- {
- $where = array_search($name, $this->names);
- if ($where === FALSE || $where === NULL)
- {
- $where = count($this->names); $this->names[] = $name;
- }
- return $where;
- }
- }
- ?>
|