|
大多数程序并非这么简单。它们接受特定格式的输入,并以某种方式来处理输入。在 XML-RPC 例子中,仅需要读取输入中的一个元素:double 元素,该元素应该只有一个。为此,查找名称为 double 的元素的起点:
if ($reader->name == "double" && $reader->nodeType == XMLReader::ELEMENT) { // ... }
该元素可能有单个文本子节点,可以通过将解析器前进到下一个节点来进行读取,如下所示:
if ($reader->name == "double" && $reader->nodeType == XMLReader::ELEMENT) { $reader->read(); respond($reader->value); }
在这里 respond() 函数构建了 XML-RPC 响应并将它发送到客户机。但是,在展示上述操作前,还有一些事情需要处理。不能绝对保证请求文档中的 double 元素仅包含一个文本节点。可能包含多个文本节点,以及注释和处理指令。例如,可能看起来像以下代码:
<value><double> <!--value follows-->6.<!--fractional part next-->0 </double></value>phpma.com
嵌套元素
该模式存在一个潜在的缺陷。嵌套的 double 元素(例如 61.2)将违背该算法。然而它将成为无效的 XML-RPC;并且不久您将看到如何使用 RELAX NG 验证来拒绝所有此类文档。在诸如可扩展超文本标记语言(Extensible Hypertext Markup Language,XHTML)之类的文档类型中,允许相同元素互相包含(例如 table 元素包含在另一个 table 元素中),因此您还需要知道元素的深度,从而确保结束标记与开始标记之间进行正确匹配。
一个健壮的解决方案需要获得 double 元素的所有文本子节点,将它们连接起来,并且仅将结果转换为 double。必须小心避免任何注释或可能出现的其它非文本节点。这有一点复杂,但并不是十分复杂,如清单 5 所示。
清单 5. 累积来自一个元素的所有文本内容
while ($reader->read()) { if ($reader->nodeType == XMLReader::TEXT || $reader->nodeType == XMLReader::CDATA || $reader->nodeType == XMLReader::WHITESPACE || $reader->nodeType == XMLReader::SIGNIFICANT_WHITESPACE) { $input .= $reader->value; } else if ($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == "double") { break; } }
您可以暂时忽略文档中的其它任何内容。(稍后将添加更多的错误处理。)
构建响应
正如它的名称所暗示的,XMLReader 仅仅用于读取。相应的 XMLWriter 类正在开发中,但还不能投入到生产。幸运的是,写入 XML 比读取 XML 要容易得多。首先,应使用 header() 函数来设置响应的媒体类型。对于 XML-RPC 来说,媒体类型是 application/xml。例如:
header('Content-type: application/xml');
通常直接将内容显示在页面上,如清单 6 中的 respond() 函数所示。
清单 6. Echo XML
function respond($input) { echo " " . sqrt($input) . " "; }
甚至可以将响应的文字部分直接嵌入 PHP 页面中,就像使用 HTML 时一样。清单 7 展示了该技术。
清单 7. 文字表示的 XML
function respond($input) { ?> " echo sqrt($input); ?> }
错误处理
到现在为止,一直隐含假定输入文档是格式规范的文档。但是不能保证情况都是如此。像任何 XML 解析器一样,只要发现一个规范格式错误,XMLReader 就必须停止处理。如果是这样的话,read() 函数将返回 false。
从理论上讲,解析器将报告数据直到发现第一个错误。但是在对小型文档进行试验时,几乎是立刻显示错误信息。底层解析器将预解析大块文档,对它进行缓存,然后每次分发出一小块文档。因此往往会过早地检查错误。出于安全考虑,不要假定在发现第一个规范格式错误之前能够解析内容。此外,也不要假设解析错误出现之前看不到任何内容。如果希望只接受完整的、格式规范的文档,那么请确保在看到文档终点之前脚本不能进行任何不可逆操作。
如果解析器检测到规范格式错误,那么 read() 函数将显示如下错误消息(如果启用了详细错误报告,且位于开发服务器上时):phpma.com
Warning: XMLReader::read() [function.read]: < value>10 in /var/www/root.php on line 35
您可能不希望将它复制到用户所看到的 HTML 页面中。更好的方法是在 $php_errormsg 环境变量中捕获错误消息。为此,需要启用 php.ini 文件中的 track_errors 配置选项:
track_errors = On
默认情况下,track_errors 选项是关闭的;这在 php.ini 中是显式指定的,因此请确保更改了该行代码。如果提早在 php.ini 中添加了上述一行代码(正如最初我所进行的操作),则后面的 track_errors = Off 代码将重写先前的代码。
该程序仅将响应发送到完整的、格式良好的输入。(也是有效的,不过将实现这点。)因此您需要等待,直到完成了文档的解析(已经跳出 while 循环)。这时,检查是否设置了 $php_errormsg 变量。如果没有进行设置,则文档是格式良好的文档,然后发送 XML-RPC 响应消息。如果设置了该变量,则文档不是格式良好的文档,并发送 XML-RPC 错误响应。如果有人请求负数的平方根,也将发送错误响应。清单 8 展示以上操作。
清单 8. 检查文档格式是否良好
// set up the request $request = $HTTP_RAW_POST_DATA; error_reporting(E_ERROR | E_WARNING | E_PARSE); if (isset($php_errormsg)) unset(($php_errormsg); // create the reader $reader = new XMLReader(); // $reader->setRelaxNGSchema("request.rng"); $reader->XML($request); $input = ""; while ($reader->read()) { if ($reader->name == "double" && $reader->nodeType == XMLReader::ELEMENT) { while ($reader->read()) { if ($reader->nodeType == XMLReader::TEXT || $reader->nodeType == XMLReader::CDATA || $reader->nodeType == XMLReader::WHITESPACE || $reader->nodeType == XMLReader::SIGNIFICANT_WHITESPACE) { $input .= $reader->value; } else if ($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == "double") { break; } } break; } } // make sure the input was well-formed if (isset($php_errormsg) ) fault(21, $php_errormsg); else if ($input < 0) fault(20, "Cannot take square root of negative number"); else respond($input);
(阅读次数:)
共3页: 上一页 [1] 2 [3] 下一页
|