📄 xmd.lib.php
字号:
<?php /* <!-- xmd.lib.php --> <!-- XML MiniDom, 2006/12/13 --><!-- Copyright (C) 2005 rene.haentjens@UGent.be - see note at end of text --><!-- Released under the GNU GPL V2, see http://www.gnu.org/licenses/gpl.html -->*//**============================================================================== * This is the XML Dom library for Dokeos.* Include/require it in your code to use its functionality.** @author Rene Haentjens* @package dokeos.library============================================================================== */class xmddoc{ /* This MiniDom for XML essentially implements an array of elements, each with a parent, a name, a namespace-URI, attributes & namespace definitions, and children. Child nodes are a mix of texts and subelements. Parent and subelements are stored as elementnumbers, the root is element 0. Parsing is built on James Clark's expat, by default enabled in PHP. The MiniDom is an alternative to the experimental DOM XML functions. It is open source PHP and requires no extra libraries. Restrictions of the MiniDom: - no two attributes with same name (different namespaces) on one element; - only 'ISO-8859-1' right now; author will investigate 'UTF-8' later; - processing instructions & external entities are ignored; - no distinction between text and cdata child nodes; - xmd_xml(nonrootelement) may not generate all needed namespace definitions; - xmd_value, xmd_html_value, xmd_select_xxx, xmd_update, xmd_update_many: path parameter uses names without namespaces and supports only a small subset of XPath, with some extensions; - maximum 11 auto-generated namespace prefixes (can be changed in xmddoc) Namespace definitions are stored as attributes, with name = 'xmlns...' e.g. xmlns:xml='http://www.w3.org/XML/1998/namespace' e.g. xmlns='http://www.imsglobal.org/xsd/imscp_v1p1' (default namespace) Exposed methods: new xmddoc(array_of_strings, charset = 'ISO-8859-1'): parse strings new xmddoc(names, numbers, textstring): restore from cached arrays & string xmd_add_element(complete_name, parent, attributes_with_complete_names) complete name = [ URI + ':' + ] name xmd_set_attribute(element, complete_attribute_name, value) (id. as above) xmd_add_text(text, element) xmd_add_text_element(complete_name, text, parent, attributes) = xmd_add_text(text, xmd_add_element(complete_name, parent, attributes)) xmd_get_element(element) => children, attributes, '?name', '?parent' xmd_get_ns_uri(element [, attribute_name_without_uri] ) xmd_text(element): combines element and subelements' text nodes xmd_xml(): generate XML-formatted string (reverse parsing) xmd_xml(indent_increase, initial_indent, lbr): e.g. ' ', '', "\n" xmd_value(path): follow path from root, return attribute value or text e.g. 'manifest/organizations/@default' 'body/p[1]' (1=first, -1=last) xmd_value(path, parent, fix, function): find value(s) with path from parent, apply function and decorate with fix = ('pre'=>..., 'in'=>..., 'post') e.g. 'general/title/*' array('in' => ', ') extensions to XPath: - and + for previous and next sibling, e.g. general/title/+/string -name and +name for sibling with specific name, e.g. item[2]/+item .. for parent, e.g. general/title/../../technical/format (stops at root) @* for location (element number within siblings, starts at 1) @*name for location in siblings with specific name @. for element name, e.g. organization/*[1]/@. namespaces are not supported in paths: they use names without URI xmd_html_value(pathFix, parent, fun): 'path' 'path infix' 'prefix -% path' 'path infix %- postfix': fun = 'htmlspecialchars' by default xmd_select_elements(path, parent): find element nodes with path (see above) xmd_select_single_element (id.) returns -1 or elementnumber xmd_select_elements_where(path, subpath, value, parent): e.g. '@id', '12' is like XPath with path[@id='12']; subpath = '.' means text xmd_select_elements_where_notempty(path, subpath, parent): e.g. '@id' xmd_select_xxx methods only select elements, not attributes xmd_remove_element(childelement_number) xmd_remove_nodes(childelement_numbers_and_strings, parent) xmd_update(path, text, parent): select single element, then: text element: replace text by new text attribute: give attribute new value = text somepath/!newtag: create new child element containing text somepath/~: delete single (first or only) element xmd_update_many(paths, subpath, ...): paths can be path1,path2,...: for all elements selected by all paths, update with subpath xmd_copy_foreign_child(fdoc, child, parent): copies fdoc's child as a new child of parent; note this method hasn't been tested for all cases (namespaces...) xmd_cache(): dump xmddoc into names+numbers+textstring for serialization Order of parameters (if present) for xmd_xxx methods: name, text, children, path, subPath, value, parent, fix, fun, attributes (name value) Properties: (G)lobal to xmddoc or array (one for each xmddoc (E)lement) e.g. $this->name[0] is the name of the document root element e.g. $this->names[$this->ns[0]] is its namespace URI e.g. $this->attributes[0]['title'] is the value of its attribute 'title' e.g. $this->attributes[0]['xmlns:a'] is the URI for prefix 'a:' */ var $names; //G array: n => namespace URI (0 => '') var $numbers; //G array: numeric dump of xmddoc for caching var $textstring; //G string: string dump of xmddoc for caching var $error; //G string: empty or parsing error message var $_nesting; //G array: nested elements while parsing (internal) var $_ns; //G array: namespace defs for upcoming element (id.) var $_concat; //G bool: concatenate cData with previous (id.) var $_nsp; //G array: namespace prefixes in use somewhere (id.) var $_last; //G int: last used elementnumber (id.) var $_strings; //G int: number of string child nodes cached (id.) var $parent; //E int: elementnumber: 0 is root, -1 is parent of root var $name; //E string: element name, without namespace var $ns; //E int: index into $names to find namespace URI var $attributes; //E array: attribute name(without namespace) => value var $atns; //E array: attribute name(id.) => index into $names var $children; //E array: elementnumbers and strings (text children) function xmd_get_element($parent = 0) // for convenience, readonly copy { // returns mixed array: children + texts have numeric key, // other elements are attributes, '?name' and '?parent' 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 (ereg('^(.*)([~!@])(.*)$', $xmPath, $m)) // split on ~ or ! or @ { $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); // '&=' always evaluates rhs, '&&=' skips it if $result is FALSE return $result; } function xmd_copy_foreign_child($fdoc, $fchild = 0, $parent = 0) { $my_queue = array($fchild, $parent); // optimization, see below 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) // 'xmlns:...' $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); // recursive call was 10 times slower... } } } function xmd_add_element($name, $parent = 0, $attribs = array()) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -