使用PHP创建XForms 第2部分
时间:2008-05-17 来源:剑心通明
[url=javascript:;]PHP[/url]
XForms 库:下一个任务是什么?
此时,您所拥有的 PHP 库基本上可以正常运作,但是还未进行过测试。现在,我们来改善输出的外观、验证函数的输入(XForms 预处理器错误并非总是有用),并查看库的操作演示。
因此,现在开始执行这些任务。
对库进行增强
库的核心部分现已完成,因此可继续对 PHP XForms 库进行增强,比如构建一些简单的抛出异常和处理异常的功能,这些功能会很有用,因为并非所有输入的设置都可以接受,或者说在结果的 XHTML 文档中会不可避免地出现错误。另外,您需要一些便利的函数将 XHTML 输出到 Web 浏览器,以及正确地处理数据缩进。我们从错误检查开始介绍。
错误检查
在本节中,通过向库中添加错误检查,可以减少 XForms 处理器中需要调试的错误,从而减少编程时间。处理完这一切后对编程有很大帮助。您将以两种方式执行错误检查:
- 首先,对于允许使用子元素(如您在
第 1 部分
添加的最后四个 action、model、trigger 和 repeat)的 XHTML 标记,您要确保只有允许的 XHTML 标记才作为子元素添加。
- 最后,您将对单个函数的输入执行错误检查,确保不出现某些情况,如果出现这些情况,则抛出一个异常,捕获之并显示在浏览器上。
首先,转到 xforms_lib.php 类文件中的变量列表,添加另外两个变量,如清单 1 所示。
清单 1. 新变量
...
var $namespaceEvents;
var $allowed;
var $tag;
function xforms_lib($ns, $nsxforms, $nsevents){
...
$this->tag = '';
$this->allowed =
array('action' => array('dispatch', 'insert',
'setvalue', 'load'),
'model' => array('instance', 'submission',
'bind', 'action'),
'trigger' => array('label', 'action'),
'root' => array('trigger', 'submit', 'select1',
'repeat', 'input', 'output',
'label', 'model'));
...新变量是 $allowed 和 $tag 变量。在构造函数中,您将初始化这两个变量。在本节中,稍后您将了解关于 $tag 变量的更多信息,但是可以这样说,它保存了当前打开的标记的名称。
清单 1
中的每个值都是顶级的 XForms XHTML 标记。Root 在没有进行任何指定时使用(repeat 和 root 一样,所以当打开 repeat 标记时,root 用于此类错误检查)。例如,查看一下 ‘action’:只有 dispatch、insert、setvalue 和 load 这四个 XHTML 标记可以作为子元素。每个标记各不相同,因此可以允许使用子元素的不同子集。
接下来,您将修改 dispatchTag
[url=javascript:;]方法[/url]
以利用这种新功能,如清单 2 所示。
清单 2. 修改 dispatchTag 函数
function dispatchTag($name, $target){
$this->check('dispatch');
$xml = '";
}
请注意此处的新内容。您将创建的标记名称传给新函数 check(这个函数稍后将做定义)。其余部分应相同。
现在,您将定义 check 函数,由此函数执行实际的检查,如清单 3 所示。
清单 3. check 函数
function check($newTag, $action=''){
$openTag = $this->tag;
if($openTag == '')
$openTag = 'root';
if($action == 'close' && ($openTag != $newTag || $openTag == 'root'))
throw new Exception("Cannot close $openTag with a $newTag ".
"\"close tag\"statement!");
else if($action == 'close' && $openTag == $newTag)
return;
else if(!in_array($newTag, $this->allowed["$openTag"]))
throw new Exception("$newTag is not allowed within $openTag!");
}
- 第一个 if 语句:注意,如果空字符串 ('') 是 $tag 类变量的当前值,则 $openTag 参数默认为 ‘root’。
- 第二个 if 语句:如果 $openTag 中指定的打开标记已关闭,且打开标记($openTag)与关闭的标记($newTag)不同(或打开标记是 root 标记,因为后者不能被关闭),则会抛出一个错误。
- 第一个 else if 语句:另一方面,如果关闭的标记($newTag)与打开标记($openTag)相等,则不会出现错误。
- 第二个 else if 语句:尽管如此,如果打开的/创建的标记($newTag)不是当前打开标记($openTag)中允许打开的标记(比如,尝试在 action 标记中创建的一个 repeat 标记),则会抛出一个错误。这里错误将被捕获并显示给用户。
现在,您将使用 check 函数,方法是将 check 语句添加到函数的其余部分,清单 4 中显示的部分内容可以作为您的指导(其余部分纱?
源代码
下载 获得)。
清单 4. 添加 check 语句
function loadTag($resource = '', $ref = '', $show='replace'){
$this->check('load');
$xml = 'check('trigger', 'open');
$xml = '';
}
function triggerTagClose(){
$this->check('trigger', 'close');
$xml = '';
}
这里,您需要确保向所有创建 XHTML 标记的函数添加一个 $this->check() 函数调用。注意在 triggerTagOpen 和 triggerTagClose 函数中调用 check 函数的区别,在 triggerTagClose 函数中,需要根据标记的打开、关闭情况传入第二个参数(‘open’ 或 ‘close’)。
现在您将创建函数设置当前的打开标记($this->tag),目前
清单 3
中引用了该标记。您将使用此函数设置当前打开标记的值,如清单 5 所示。
清单 5. setTag 函数
function setTag($t){
$this->tag = $t;
}
您将使用此函数设置最高元素的标记。例如,在打开 model 标记后,您将这样调用此函数:setTag('model')。在关闭标记后,您将不继续在 XForms XHTML 标记下操作,而执行如下调用:setTag('root'),将当前的开放标记重新设置为 root。
现在,系统出现错误时将通过抛出异常来验证输入。只有以下五个函数需要这种错误检查功能:submission、bind、load、select1 和 instance。从 submission 开始介绍,如清单 6 所示。
清单 6. submissionTag 函数:对输入错误抛出异常
function submissionTag($id, $action, $method = 'post', $ref='',
$instance = '', $replace = ''){
$this->check('submission');
if(($instance != '' || $replace != '') &&
$replace != '' && $instance != '')
throw new Exception("instance or replace is set, but not both ".
"in a submission tag!");
$xml = '对于 tag 标记,$instance 或 $replace 参数可以分别设置,但是不可以同时设置。使用此函数时将抛出一个异常并显示给用户。
该思想在
[url=javascript:;]其他[/url]
四个函数中类似,如清单 7 所示。
清单 7. 对输入错误抛出异常的更多函数
function bindTag($nodeset, $relevant = '', $calculate = '',
$required = ''){
$this->check('bind');
if($relevant == '' && $calculate == '' && $required == '')
throw new Exception("Must set one of: relevant, calculate or ".
"required in a bind tag!");
$xml = 'check('load');
if($resource != '' && $ref != '')
throw new Exception("Cannot specify both a resource and ref ".
"for the load tag!");
else if($resource == '' && $ref == '')
throw new Exception("Must specify one of: resource or ref ".
"for the load tag!");
$xml = 'check('select1');
if(is_array($itemArray) && is_array($itemset))
throw new Exception("Cannot specify both a list of items ".
"and an itemset for a select1 element!");
else if(!is_array($itemArray) && !is_array($itemset))
throw new Exception("Must specify one of: itemArray or itemset ".
"as arrays in the select1 tag!");
$xml = '';
$xml .= ''.$label.'';
...
}
function instanceTag($id = '', $instanceXML = '', $src = ''){
$this->check('instance');
if($instanceXML != '' && $src != '')
throw new Exception("Must define instance data or a src URL ".
"for the instance tag!");
$xml = '注意,bindTag 函数的输入 $required、$calculate 或 $required 中,必须要设置其中一个。对于 loadTag 函数,$resource 和 $ref 输入不能同时设置,但是必须设置其中一个。select1Tag 函数也需要设置 $itemArray 和 $itemset 中的一个但不能同时设置。最后,instanceTag 函数要求对 $instanceXML 和 $src 都不进行设置。
至此,错误处理一节全部结束。稍后在测试一节中,您将了解如何处理抛出的错误并将错误消息显示给用户。
便利函数
要完成错误处理,您需要添加几个便利函数,帮助开发人员在 XForms 开发中使用 PHP。
首先,您将再定义几个类变量,如清单 8 所示。
清单 8. 更多的类变量
var $allowed;
var $print;
var $indentation;
var $indentationValue;
function xforms_lib($ns, $nsxforms, $nsevents){
...
$this->namespaceEvents = $nsevents;
$this->print = 0;
$this->indentation = 0;
$this->indentationValue = " ";
$this->allowed =
...
}
这里创建了另外三个类变量以帮助格式化 XHTML 输出。$print 变量指定是否使用 printData(稍后定义)在类函数中将 XHTML 直接输出到浏览器(用 echo 语句)。$indentation 变量指定了调用 indentation 函数(稍后定义)时所需输出的缩进级数,$indentationValue 变量指定了需要显示为缩进的内容(这里有四个空间用作 $indentationValue)。
现在,您将创建几个函数设置 $print、$indentation 计数增减量的值,如清单 9 所示。
清单 9. 修改 $indentation 和 $print 的函数
function incrementIndentation(){
$this->indentation++;
}
function decrementIndentation(){
$this->indentation--;
}
function setPrint($p){
if($p)
$this->print = 1;
else
$this->print = 0;
}
前两个函数对构造函数中初始化为零的类变量 $indentation 进行增减,而第三个函数将 $print 设置为 1,前提是传入的变量为 true;否则,$print 设置为 0。
下一个函数显示了缩进,如清单 10 所示。
清单 10. 显示缩进
function indentation(){
$xml = '';
for($i = $this->indentation; $i > 0; $i--)
$xml .= $this->indentationValue;
return $xml;
}
此函数需要迭代的次数与 $indentation 类变量值相同,对于每次迭代,它将 $indentationValue 类变量添加到 $xml,并将后者返回。因此,如果 $indentation 是 2,则在返回 $xml 之前将累计 8 个空间。此函数和 decrementIndentation 及 incrementIndentation 函数使得到的 XHTML 代码具有正确的缩进格式。
清单 11 展示了如何使用 printData 函数显示 XHTML。
清单 11. 显示 XHTML
function printData($xml){
if($this->print){
echo $this->indentation();
echo $xml."\r\n";
return 0;
}
return 1;
}
每个函数的末尾调用 printData 函数,根据 $print 类变量的值返回 XHTML 数据,或显示到浏览器。将其添加到所有的标记创建函数中,如清单 12 中的示例所示。将 incrementIndentation 函数添加到所有的 tagOpen 函数中,并将 decrementIndentation 添加到所有的 tagClose 函数中。
清单 12. 使用 printData、incrementIndentation 和 decrementIndentation 函数
function submissionTag($id, $action, $method = 'post', $ref='',
$instance = '', $replace = ''){
$this->check('submission');
...
$xml .= ' replace="'.$replace.'"';
$xml .= " />";
$this->printData($xml);
return $xml;
}
function triggerTagOpen($ref, $submission = '', $label = 'default'){
$this->check('trigger', 'open');
$xml = '';
$this->incrementIndentation();
$this->printData($xml);
return $xml;
}
function triggerTagClose(){
$this->check('trigger', 'close');
$xml = '';
$this->decrementIndentation();
$this->printData($xml);
return $xml;
}
这里,您会发现上面
清单 12
中的每个函数都使用了 $this->printData 函数。注意,即使已经调用 printData 在浏览器上显示数据,XHTML 数据仍然会返回到调用函数的语句。还要注意,triggerTagOpen 函数使用 incrementIndentation 函数的方式。因此,在打开 trigger 标记后所有内容都将拥有额外的缩进,直至调用 triggerTagClose 关闭 trigger 标记。这将在调用 triggerTagOpen 函数之前将缩进减至原始值。
将 $this->printData($xml); 语句添加到函数其余部分的适当位置(您可以使用
源代码下载
作为指导)。另外,将 $this->decrementIndentation(); 语句添加到所有的 tagClose 语句中,并将 $this->incrementIndentation(); 语句添加到所有的 tagOpen 语句中,使用
清单 12
和
源代码下载
作为指导。
最后,还有几个便利函数让您更轻松地使用 PHP XForms 库,如清单 13 所示。您可以在 PHP 文件中使用这些函数显示所需的常见
[url=javascript:;]HTML[/url]
标记。
清单 13. 便利函数
function doctypeTag(){
$xml = '';
$this->printData($xml);
return $xml;
}
function headTitle($title){
$xml = "$title";
$this->printData($xml);
return $xml;
}
function closeHeadOpenBody(){
$xml = "";
$this->printData($xml);
return $xml;
}
function closeBodyCloseHtml(){
$xml = "";
$this->printData($xml);
return $xml;
}
function outputXHTMLheader(){
header("Content-Type: application/xhtml+xml; charset=UTF-8");
}
在下一节中,您将使用上面的各个函数设置用于测试 XForms PHP 库的表单(稍后创建)。它们只是创建标记,并输出与 XHTML 兼容的报头。
使用 PHP XForms 库创建表单
现在来看看这个 XForms PHP 库的真正用途。在本节中将实例化几个 XForms 小部件并使用 Firefox XForms 插件显示其
[url=javascript:;]工作[/url]
情况。
首先查看一下新索引页面的开头,如清单 14 所示。
清单 14:新索引页面的开头
setPrint(1);
$xformsDoc->outputXHTMLheader();
$xformsDoc->doctypeTag();
$xformsDoc->htmlTag();
$xformsDoc->incrementIndentation();
$xformsDoc->headTitle('XForms served via PHP');
...
?>
第一行将库包含到 PHP 文件中,在该文件中您将实例化一个新的 xforms_lib 类并传入三个名称空间。然后将 $print 类变量设为 1,直接将库指定到输出的 XHTML。然后通过输出适当的报头、文档类型、HTML 标记和标题来设置 XHTML 文档。
接下来,您将查看如何使用库创建 XForms 模型(见清单 15)。
清单 15. 创建 XForms 模型
...
// display model here
$model1 = 'model';
$instance1 = 'instance';
$instance1data = '../here'.
'';
$instance2 = 'instance2';
$instance2data = 'data1'.
'd2third data
';
$submitButton1 = 'submit1';
$submitButton2 = 'submit2';
$xformsDoc->setTag('root');
$xformsDoc->modelTagOpen($model1);
$xformsDoc->setTag('model');
$xformsDoc->incrementIndentation();
$xformsDoc->instanceTag($instance1, $instance1data);
$xformsDoc->instanceTag($instance2, $instance2data);
$xformsDoc->submissionTag($submitButton1,
"receive.php", 'post', "instance('$instance1')");
$xformsDoc->submissionTag($submitButton2,
"receive.php", 'post', "instance('$instance2')");
$xformsDoc->bindTag("instance('$instance1')/data4",
"instance('$instance1')/data3='1'",
"instance('$instance1')/data2");
$xformsDoc->modelTagClose();
$xformsDoc->setTag('root');
$xformsDoc->decrementIndentation();
...
前面几行是 PHP 数据变量,其中包含了 model、instance 和 submission 名称。另外还定义了两个实例文档
[url=javascript:;]XML[/url]
数据。然后调用 setTag 将 $tag 类变量实例化为 ‘root’。
现在,您可以打开 model 标记,下一行中即完成了这一任务。$tag 变量然后被再次设为 ‘model’,因为刚打开了 model 标记,root 不再是顶级标记了。这也意味着需要增加缩进,因此调用 incrementIndentation。
接下来实例化了两个实例文档,后接两个 submission 标记。它们把使用 POST 发送的两个实例文档之一发送给
第 1 部分
中创建的 receive.php 页面。还创建了一个 bind 标记,通过计算将 data4 (ref) 绑定到 data2 (calculate),而 data3 (relevant) 与 data4 相关。最后,关闭 model 标记,$tag 重置为 ‘root’ 并减少缩进。
现在,您可以开始创建 XHTML 主体,如清单 16 所示。
清单 16. 使用 inputTag 和 outputTag
...
$xformsDoc->closeHeadOpenBody();
$xformsDoc->incrementIndentation();
// display form here
$xformsDoc->inputTag("instance('$instance1')//data1",
'data1 (new page input): ');
echo "
";
$xformsDoc->inputTag("instance('$instance1')//data2",
'data2 (data4 is bound to this field): ');
echo "
";
$xformsDoc->inputTag("instance('$instance1')//data3",
'data3 (1 makes data4 relevant): ');
echo "
data4 (bound to data2): ";
$xformsDoc->outputTag("instance('$instance1')//data4");
echo "
data5 (value of select menu shown here): ";
$xformsDoc->outputTag("instance('$instance1')//data5");
echo "
";
...
首先关闭 XHTML head 标记,打开 body 标记。然后创建三个 input 标记,每个标记分别引用 $instance1 的 data1、data2 和 data3,接着创建两个 output 标记,每个标记分别引用 $instance1 的 data4 和 data5。
您可以在表单中验证使用这些值是否将使上面
清单 15
中创建的绑定元素按预期运作。
下面您将定义一个 select1 标记,如清单 17 所示。
清单 17. 定义 select1 标记
...
$itemset['nodeset'] = "instance('$instance2')//datum";
$itemset['label'] = '.';
$itemset['value'] = '.';
$xformsDoc->select1Tag("instance('$instance1')//data5", 'select me',
'', $itemset);
echo "
";
...
首先,您要使用包含 $instance2 中所有数据元素的节点集设置 $itemset 数组。然后实例化 select1 标记并使其引用 $instance1 的 data5。另外要注意的是,使用 select1 标记将导致
清单 16
中定义的第二个 output 标记的值被相应地更改。
接下来定义两个提交按钮。这两个按钮将允许您查看每个实例的实际 XML。如清单 18 所示。
清单 18. 定义提交按钮
...
$xformsDoc->submitTag($submitButton1, 'SubmitTextBoxes');
echo "
";
$xformsDoc->submitTag($submitButton2, 'SubmitSelect');
echo "
";
...
相应地作出标记后,您可以在文本框和 select1 菜单框中使用这些值,然后单击每个提交按钮查看 XML 中的值都有哪些。
现在测试一个 trigger 标记,如清单 19 所示。
清单 19. 实例化 trigger 标记
...
$xformsDoc->triggerTagOpen("instance('$instance1')//data1", '', 'new page');
echo "
";
$xformsDoc->setTag('trigger');
$xformsDoc->actionTagOpen("DOMActivate");
$xformsDoc->setTag('action');
$xformsDoc->loadTag('', "instance('$instance1')/data1",
'new');
$xformsDoc->actionTagClose();
$xformsDoc->setTag('trigger');
$xformsDoc->triggerTagClose();
$xformsDoc->setTag('root');
...
这里,您创建了一个 trigger 标记,其标签为 ‘new page’。打开该标记后,您将 $tag 变量设为 ‘trigger’,并在 trigger 标记中创建一个 action 标记,此标记在 DOMActivate 事件中激活。在此标记中设置 $tag 为 ‘action’ 后,创建一个 load 标记根据 data1 的值加载一个新页面(可以自由地使用 data1 的值并按下此触发器)。最后,关闭 action 和 trigger 标记,并将 $tag 重置为 root。
最后测试的标记为 repeat,如清单 20 所示。
清单 20. 创建 repeat
...
echo "repeat data:";
$xformsDoc->repeatTagOpen("instance('$instance2')//datum", 'id1');
$xformsDoc->setTag('root');
$xformsDoc->outputTag(".");
$xformsDoc->setTag('repeat');
$xformsDoc->repeatTagClose();
$xformsDoc->setTag('root');
$xformsDoc->decrementIndentation();
$xformsDoc->decrementIndentation();
$xformsDoc->closeBodyCloseHtml();
?>
这里,您将 repeat 的内容指定为 $instance2 中的所有数据子元素。因为在添加新元素时,repeat 与 root 等效,因此您将 $tag 设为 ‘root’。然后您将显示一个 output 标记,用于显示当前 XML 数据内容的内容。然后在关闭 repeat 标记之前将 $tag 设为 ‘repeat’,关闭后,将 $tag 重置为 root。
创建完所有的 XHTML XForms 标记后,通过两次减少缩进值并关闭 body 和 html 标记来关闭 XHTML 文档。
您可以在图 1 中预览表单。
图 1. 最后的结果表单
确保右键单击 XHTML 页面并单击 view source,您就可以验证 PHP 的 XHTML 输出(见图 2)。
图 2. 格式化工作
确保使用创建的 PHP 生成的 XForms 小部件处理表单。
结束语
祝贺您。您现在已经拥有了一个相当健壮的 PHP XForms 库,您可以使用该库继续进行开发和扩展。注意,该库和它定义的 XForms XHTML 标记并不一定完整,库本身及其错误检查功能仍然具有扩展的空间。例如,您可以定义一些新函数,在其中定义一些新的 XForms XHTML 标记。本质上讲,一切皆有可能,整个
[url=javascript:;]开源[/url]
项目就是基于开发人员的不懈努力,正如这个分两部分的
系列文章
中介绍的那样。尽情享受您的 PHP 和 XForms 之旅,祝您编程愉快!
相关阅读 更多 +