使用PHP创建XForms 下


PHP 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 方法以利用这种新功能,如清单 2 所示。
清单 2. 修改 dispatchTag 函数
               
    function dispatchTag($name, $target){
        $this->check('dispatch');
        $xml = '<xforms:dispatch';
        if($name != '')
            $xml .= ' name="'.$name.'"';
        if($target != '')
            $xml .= ' target="'.$target.'"';
        $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 = '<xforms:load show="'.$show.'"';
...
    }

    function triggerTagOpen($ref, $submission = '', $label = 'default'){
        $this->check('trigger', 'open');
        $xml = '<xforms:trigger ref="'.$ref.'"';
        if($submission != '')
            $xml .= ' submission="'.$submission.'"';
        $xml .= ' >';
    }

    function triggerTagClose(){
        $this->check('trigger', 'close');
        $xml = '</xforms:trigger>';
    }

 
这里,您需要确保向所有创建 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 = '<xforms:submission id="'.$id.'" action="'.$action.
            '" method="'.$method.'"';
...
    }
 
 
对于 tag 标记,$instance 或 $replace 参数可以分别设置,但是不可以同时设置。使用此函数时将抛出一个异常并显示给用户。

该思想在其他四个函数中类似,如清单 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 = '<xforms:bind nodeset="'.$nodeset.'"';
...
    }

    function loadTag($resource = '', $ref = '', $show='replace'){
        $this->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 = '<xforms:load show="'.$show.'"';
...
    }

    function select1Tag($ref, $label, $itemArray, $itemset){
        $this->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 = '<xforms:select1 ref="'.$ref.'">';
        $xml .= '<xforms:label>'.$label.'</xforms: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 = '<xforms:instance';
...
    }

 
注意,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 = '<xforms:trigger ref="'.$ref.'"';
        if($submission != '')
            $xml .= ' submission="'.$submission.'"';
        $xml .= ' >';
        $this->incrementIndentation();
        $this->printData($xml);
        return $xml;
    }

    function triggerTagClose(){
        $this->check('trigger', 'close');
        $xml = '</xforms:trigger>';
        $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 文件中使用这些函数显示所需的常见 HTML 标记。


清单 13. 便利函数
               
    function doctypeTag(){
        $xml = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
        $this->printData($xml);
        return $xml;
    }

    function headTitle($title){
        $xml = "<head><title>$title</title>";
        $this->printData($xml);
        return $xml;
    }

    function closeHeadOpenBody(){
        $xml = "</head><body>";
        $this->printData($xml);
        return $xml;
    }

    function closeBodyCloseHtml(){
        $xml = "</body></html>";
        $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 插件显示其工作情况。

首先查看一下新索引页面的开头,如清单 14 所示。


清单 14:新索引页面的开头
               
<?php
include('lib/xforms_lib.php');
$xformsDoc = new xforms_lib("http://www.w3.org/1999/xhtml",
                            "http://www.w3.org/2002/xforms",
                            "http://www.w3.org/2001/xml-events");

$xformsDoc->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 = '<root xmlns=""><data1>../here</data1><data2/>'.
    '<data3/><data4/><data5/></root>';
$instance2 = 'instance2';
$instance2data = '<root xmlns=""><data><datum>data1</datum>'.
    '<datum>d2</datum><datum>third data</datum></data>
              </root>';
$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 名称。另外还定义了两个实例文档 XML 数据。然后调用 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 "<br />";
$xformsDoc->inputTag("instance('$instance1')//data2",
                     'data2 (data4 is bound to this field): ');
echo "<br />";
$xformsDoc->inputTag("instance('$instance1')//data3",
                     'data3 (1 makes data4 relevant): ');
echo "<br />data4 (bound to data2): ";
$xformsDoc->outputTag("instance('$instance1')//data4");
echo "<br />data5 (value of select menu shown here): ";
$xformsDoc->outputTag("instance('$instance1')//data5");
echo "<br />";

...

 


首先关闭 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 "<br />";

...

 


首先,您要使用包含 $instance2 中所有数据元素的节点集设置 $itemset 数组。然后实例化 select1 标记并使其引用 $instance1 的 data5。另外要注意的是,使用 select1 标记将导致 清单 16 中定义的第二个 output 标记的值被相应地更改。

接下来定义两个提交按钮。这两个按钮将允许您查看每个实例的实际 XML。如清单 18 所示。


清单 18. 定义提交按钮
               
...

$xformsDoc->submitTag($submitButton1, 'SubmitTextBoxes');
echo "<br />";

$xformsDoc->submitTag($submitButton2, 'SubmitSelect');
echo "<br />";

...

 


相应地作出标记后,您可以在文本框和 select1 菜单框中使用这些值,然后单击每个提交按钮查看 XML 中的值都有哪些。

现在测试一个 trigger 标记,如清单 19 所示。


清单 19. 实例化 trigger 标记
               
...

$xformsDoc->triggerTagOpen("instance('$instance1')//data1", '', 'new page');
echo "<br />";
$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 "<br/>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 库,您可以使用该库继续进行开发和扩展。注意,该库和它定义的 XForms XHTML 标记并不一定完整,库本身及其错误检查功能仍然具有扩展的空间。例如,您可以定义一些新函数,在其中定义一些新的 XForms XHTML 标记。本质上讲,一切皆有可能,整个开源项目就是基于开发人员的不懈努力,正如这个分两部分的 系列文章 中介绍的那样。尽情享受您的 PHP 和 XForms 之旅,祝您编程愉快!

下载
第 2 部分的示例代码

本文作者:
« 
» 
快速导航

Copyright © 2016 phpStudy | 豫ICP备2021030365号-3