📄 xpath.class.php
字号:
* be viewed in a browser, including any XML headers. * FALSE on error. * @see _export() */ function exportAsHtml($absoluteXPath='', $hilightXpathList=array()) { $htmlString = $this->_export($absoluteXPath, $xmlHeader=NULL, $hilightXpathList); if (!$htmlString) return FALSE; return "<pre>\n" . $htmlString . "\n</pre>"; } /** * Given a context this function returns the containing XML * * @param $absoluteXPath (string) The address of the node you would like to export. * If empty the whole document will be exported. * @param $xmlHeader (array) The string that you would like to appear before * the XML content. ie before the <root></root>. If you * do not specify this argument, the xmlHeader that was * found in the parsed xml file will be used instead. * @return (mixed) The Xml fragment/document, suitable for writing * out to an .xml file or as part of a larger xml file, or * FALSE on error. * @see _export() */ function exportAsXml($absoluteXPath='', $xmlHeader=NULL) { $this->hilightXpathList = NULL; return $this->_export($absoluteXPath, $xmlHeader); } /** * Generates a XML string with the content of the current document and writes it to a file. * * Per default includes a <?xml ...> tag at the start of the data too. * * @param $fileName (string) * @param $absoluteXPath (string) The path to the parent node you want(see text above) * @param $xmlHeader (array) The string that you would like to appear before * the XML content. ie before the <root></root>. If you * do not specify this argument, the xmlHeader that was * found in the parsed xml file will be used instead. * @return (string) The returned string contains well-formed XML data * or FALSE on error. * @see exportAsXml(), exportAsHtml() */ function exportToFile($fileName, $absoluteXPath='', $xmlHeader=NULL) { $status = FALSE; do { // try-block if (!($hFile = fopen($fileName, "wb"))) { // Did we open the file ok? $errStr = "Failed to open the $fileName xml file."; break; // try-block } if ($this->properties['OS_supports_flock']) { if (!flock($hFile, LOCK_EX + LOCK_NB)) { // Lock the file $errStr = "Couldn't get an exclusive lock on the $fileName file."; break; // try-block } } if (!($xmlOut = $this->_export($absoluteXPath, $xmlHeader))) { $errStr = "Export failed"; break; // try-block } $iBytesWritten = fwrite($hFile, $xmlOut); if ($iBytesWritten != strlen($xmlOut)) { $errStr = "Write error when writing back the $fileName file."; break; // try-block } // Flush and unlock the file @fflush($hFile); $status = TRUE; } while(FALSE); @flock($hFile, LOCK_UN); @fclose($hFile); // Sanity check the produced file. clearstatcache(); if (filesize($fileName) < strlen($xmlOut)) { $errStr = "Write error when writing back the $fileName file."; $status = FALSE; } if (!$status) $this->_displayError($errStr, __LINE__, __FILE__, FALSE); return $status; } /** * Generates a XML string with the content of the current document. * * This is the start for extracting the XML-data from the node-tree. We do some preperations * and then call _InternalExport() to fetch the main XML-data. You optionally may pass * xpath to any node that will then be used as top node, to extract XML-parts of the * document. Default is '', meaning to extract the whole document. * * You also may pass a 'xmlHeader' (usually something like <?xml version="1.0"? > that will * overwrite any other 'xmlHeader', if there was one in the original source. If there * wasn't one in the original source, and you still don't specify one, then it will * use a default of <?xml version="1.0"? > * Finaly, when exporting to HTML, you may pass a vector xPaths you want to hi-light. * The hi-lighted tags and attributes will receive a nice color. * * NOTE I : The output can have 2 formats: * a) If "skip white spaces" is/was set. (Not Recommended - slower) * The output is formatted by adding indenting and carriage returns. * b) If "skip white spaces" is/was *NOT* set. * 'as is'. No formatting is done. The output should the same as the * the original parsed XML source. * * @param $absoluteXPath (string) (optional, default is root) The node we choose as top-node * @param $xmlHeader (string) (optional) content before <root/> (see text above) * @param $hilightXpath (array) (optional) a vector of xPaths to nodes we wat to * hi-light (see text above) * @return (mixed) The xml string, or FALSE on error. */ function _export($absoluteXPath='', $xmlHeader=NULL, $hilightXpathList='') { // Check whether a root node is given. if (empty($absoluteXpath)) $absoluteXpath = ''; if ($absoluteXpath == '/') $absoluteXpath = ''; if ($this->_indexIsDirty) $this->reindexNodeTree(); if (!isSet($this->nodeIndex[$absoluteXpath])) { // If the $absoluteXpath was '' and it didn't exist, then the document is empty // and we can safely return ''. if ($absoluteXpath == '') return ''; $this->_displayError("The given xpath '{$absoluteXpath}' isn't a node in this document.", __LINE__, __FILE__, FALSE); return FALSE; } $this->hilightXpathList = $hilightXpathList; $this->indentStep = ' '; $hilightIsActive = is_array($hilightXpathList); if ($hilightIsActive) { $this->indentStep = ' '; } // Cache this now $this->parseSkipWhiteCache = isSet($this->parseOptions[XML_OPTION_SKIP_WHITE]) ? $this->parseOptions[XML_OPTION_SKIP_WHITE] : FALSE; /////////////////////////////////////// // Get the starting node and begin with the header // Get the start node. The super root is a special case. $startNode = NULL; if (empty($absoluteXPath)) { $superRoot = $this->nodeIndex['']; // If they didn't specify an xml header, use the one in the object if (is_null($xmlHeader)) { $xmlHeader = $this->parseSkipWhiteCache ? trim($superRoot['textParts'][0]) : $superRoot['textParts'][0]; // If we still don't have an XML header, then use a suitable default if (empty($xmlHeader)) { $xmlHeader = '<?xml version="1.0"?>'; } } if (isSet($superRoot['childNodes'][0])) $startNode = $superRoot['childNodes'][0]; } else { $startNode = $this->nodeIndex[$absoluteXPath]; } if (!empty($xmlHeader)) { $xmlOut = $this->parseSkipWhiteCache ? $xmlHeader."\n" : $xmlHeader; } else { $xmlOut = ''; } /////////////////////////////////////// // Output the document. if (($xmlOut .= $this->_InternalExport($startNode)) === FALSE) { return FALSE; } /////////////////////////////////////// // Convert our markers to hi-lights. if ($hilightIsActive) { $from = array('<', '>', chr(2), chr(3)); $to = array('<', '>', '<font color="#FF0000"><b>', '</b></font>'); $xmlOut = str_replace($from, $to, $xmlOut); } return $xmlOut; } /** * Export the xml document starting at the named node. * * @param $node (node) The node we have to start exporting from * @return (string) The string representation of the node. */ function _InternalExport($node) { $ThisFunctionName = '_InternalExport'; $bDebugThisFunction = in_array($ThisFunctionName, $this->aDebugFunctions); $this->_beginDebugFunction($ThisFunctionName, $bDebugThisFunction); if ($bDebugThisFunction) { echo "Exporting node: ".$node['xpath']."<br>\n"; } //////////////////////////////// // Quick out. if (empty($node)) return ''; // The output starts as empty. $xmlOut = ''; // This loop will output the text before the current child of a parent then the // current child. Where the child is a short tag we output the child, then move // onto the next child. Where the child is not a short tag, we output the open tag, // then queue up on currentParentStack[] the child. // // When we run out of children, we then output the last text part, and close the // 'parent' tag before popping the stack and carrying on. // // To illustrate, the numbers in this xml file indicate what is output on each // pass of the while loop: // // 1 // <1>2 // <2>3 // <3/>4 // </4>5 // <5/>6 // </6> // Although this is neater done using recursion, there's a 33% performance saving // to be gained by using this stack mechanism. // Only add CR's if "skip white spaces" was set. Otherwise leave as is. $CR = ($this->parseSkipWhiteCache) ? "\n" : ''; $currentIndent = ''; $hilightIsActive = is_array($this->hilightXpathList); // To keep track of where we are in the document we use a node stack. The node // stack has the following parallel entries: // 'Parent' => (array) A copy of the parent node that who's children we are // exporting // 'ChildIndex' => (array) The child index of the corresponding parent that we // are currently exporting. // 'Highlighted'=> (bool) If we are highlighting this node. Only relevant if // the hilight is active. // Setup our node stack. The loop is designed to output children of a parent, // not the parent itself, so we must put the parent on as the starting point. $nodeStack['Parent'] = array($node['parentNode']); // And add the childpos of our node in it's parent to our "child index stack". $nodeStack['ChildIndex'] = array($node['pos']); // We start at 0. $nodeStackIndex = 0; // We have not to output text before/after our node, so blank it. We will recover it // later $OldPreceedingStringValue = $nodeStack['Parent'][0]['textParts'][$node['pos']]; $OldPreceedingStringRef =& $nodeStack['Parent'][0]['textParts'][$node['pos']]; $OldPreceedingStringRef = ""; $currentXpath = ""; // While we still have data on our stack while ($nodeStackIndex >= 0) { // Count the children and get a copy of the current child. $iChildCount = count($nodeStack['Parent'][$nodeStackIndex]['childNodes']); $currentChild = $nodeStack['ChildIndex'][$nodeStackIndex]; // Only do the auto indenting if the $parseSkipWhiteCache flag was set. if ($this->parseSkipWhiteCache) $currentIndent = str_repeat($this->indentStep, $nodeStackIndex); if ($bDebugThisFunction) echo "Exporting child ".($currentChild+1)." of node {$nodeStack['Parent'][$nodeStackIndex]['xpath']}\n"; /////////////////////////////////////////// // Add the text before our child. // Add the text part before the current child $tmpTxt =& $nodeStack['Parent'][$nodeStackIndex]['textParts'][$currentChild]; if (isSet($tmpTxt) AND ($tmpTxt!="")) { // Only add CR indent if there were children if ($iChildCount) $xmlOut .= $CR.$currentIndent; // Hilight if necessary. $highlightStart = $highlightEnd = ''; if ($hilightIsActive) { $currentXpath = $nodeStack['Parent'][$nodeStackIndex]['xpath'].'/text()['.($currentChild+1).']'; if (in_array($currentXpath, $this->hilightXpathList)) { // Yes we hilight $highlightStart = chr(2); $highlightEnd = chr(3); } } $xmlOut .= $highlightStart.$nodeStack['Parent'][$nodeStackIndex]['textParts'][$currentChild].$highlightEnd; } if ($iChildCount && $nodeStackIndex) $xmlOut .= $CR; /////////////////////////////////////////// // Are there any more children? if ($iChildCount <= $currentChild) { // Nope, so output the last text before the closing tag $tmpTxt =& $nodeStack['Parent'][$nodeStackIndex]['textParts'][$currentChild+1]; if (isSet($tmpTxt) AND ($tmpTxt!="")) { // Hilight if necessary. $highlightStart = $highlightEnd = ''; if ($hilightIsActive) { $currentXpath = $nodeStack['Parent'][$nodeStackIndex]['xpath'].'/text()['.($currentChild+2).']';
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -