本文示例源代碼或素材下載
PHP XForms 庫:下一個任務是什麼?
此時,您所擁有的 PHP 庫基本上可以正常運作,但是還未進行過測試。現在,我們來改善輸出的外觀、驗證函數的輸入(XForms 預處理器錯誤並非總是有用),並查看庫的操作演示。
因此,現在開始執行這些任務。
對庫進行增強
庫的核心部分現已完成,因此可繼續對 PHP XForms 庫進行增強,比如構建一些簡單的拋出異常和處理異常的功能,這些功能會很有用,因為並非所有輸入的設置都可以接受,或者說在結果的 XHTML 文檔中會不可避免地出現錯誤。另外,您需要一些便利的函數將 XHtml 輸出到 Web 浏覽器,以及正確地處理數據縮進。我們從錯誤檢查開始介紹。
錯誤檢查
在本節中,通過向庫中添加錯誤檢查,可以減少 XForms 處理器中需要調試的錯誤,從而減少編程時間。處理完這一切後對編程有很大幫助。您將以兩種方式執行錯誤檢查:
首先,對於允許使用子元素(如您在 第 1 部分 添加的最後四個 action、model、trigger 和 repeat)的 XHTML 標記,您要確保只有允許的 XHtml 標記才作為子元素添加。
最後,您將對單個函數的輸入執行錯誤檢查,確保不出現某些情況,如果出現這些情況,則拋出一個異常,捕獲之並顯示在浏覽器上。
首先,轉到 xforms_lib.PHP 類文件中的變量列表,添加另外兩個變量,如清單 1 所示。
清單 1. 新變量...
新變量是 $allowed 和 $tag 變量。在構造函數中,您將初始化這兩個變量。在本節中,稍後您將了解關於 $tag 變量的更多信息,但是可以這樣說,它保存了當前打開的標記的名稱。清單 1 中的每個值都是頂級的 XForms XHTML 標記。Root 在沒有進行任何指定時使用(repeat 和 root 一樣,所以當打開 repeat 標記時,root 用於此類錯誤檢查)。例如,查看一下 ‘action’:只有 dispatch、insert、setvalue 和 load 這四個 XHtml 標記可以作為子元素。每個標記各不相同,因此可以允許使用子元素的不同子集。
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'));
...
接下來,您將修改 dispatchTag 方法以利用這種新功能,如清單 2 所示。
清單 2. 修改 dispatchTag 函數 function dispatchTag($name, $target){
請注意此處的新內容。您將創建的標記名稱傳給新函數 check(這個函數稍後將做定義)。其余部分應相同。
$this->check('dispatch');
$XML = '<xforms:dispatch';
if($name != '')
$XML .= ' name="'.$name.'"';
if($target != '')
$XML .= ' target="'.$target.'"';
$XML .= " />";
}
現在,您將定義 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'){
這裡,您需要確保向所有創建 XHtml 標記的函數添加一個 $this->check() 函數調用。注意在 triggerTagOpen 和 triggerTagClose 函數中調用 check 函數的區別,在 triggerTagClose 函數中,需要根據標記的打開、關閉情況傳入第二個參數(‘open’ 或 ‘close’)。
$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>';
}
現在您將創建函數設置當前的打開標記($this->tag),目前 清單 3 中引用了該標記。您將使用此函數設置當前打開標記的值,如清單 5 所示。
清單 5. setTag 函數 function setTag($t){
您將使用此函數設置最高元素的標記。例如,在打開 model 標記後,您將這樣調用此函數:setTag('model')。在關閉標記後,您將不繼續在 XForms XHtml 標記下操作,而執行如下調用:setTag('root'),將當前的開放標記重新設置為 root。
$this->tag = $t;
}
現在,系統出現錯誤時將通過拋出異常來驗證輸入。只有以下五個函數需要這種錯誤檢查功能:submission、bind、load、select1 和 instance。從 submission 開始介紹,如清單 6 所示。
清單 6. submissionTag 函數:對輸入錯誤拋出異常 function submissionTag($id, $action, $method = 'post', $ref='',
對於 tag 標記,$instance 或 $replace 參數可以分別設置,但是不可以同時設置。使用此函數時將拋出一個異常並顯示給用戶。
$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.'"';
...
}
該思想在其他四個函數中類似,如清單 7 所示。
清單 7. 對輸入錯誤拋出異常的更多函數 function bindTag($nodeset, $relevant = '', $calculate = '',
注意,bindTag 函數的輸入 $required、$calculate 或 $required 中,必須要設置其中一個。對於 loadTag 函數,$resource 和 $ref 輸入不能同時設置,但是必須設置其中一個。select1Tag 函數也需要設置 $itemArray 和 $itemset 中的一個但不能同時設置。最後,instanceTag 函數要求對 $instanceXML 和 $src 都不進行設置。
$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';
...
}
至此,錯誤處理一節全部結束。稍後在測試一節中,您將了解如何處理拋出的錯誤並將錯誤消息顯示給用戶。
便利函數
要完成錯誤處理,您需要添加幾個便利函數,幫助開發人員在 XForms 開發中使用 PHP。
首先,您將再定義幾個類變量,如清單 8 所示。
清單 8. 更多的類變量 var $allowed;
這裡創建了另外三個類變量以幫助格式化 XHTML 輸出。$print 變量指定是否使用 printData(稍後定義)在類函數中將 XHtml 直接輸出到浏覽器(用 echo 語句)。$indentation 變量指定了調用 indentation 函數(稍後定義)時所需輸出的縮進級數,$indentationValue 變量指定了需要顯示為縮進的內容(這裡有四個空間用作 $indentationValue)。
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 =
...
}
現在,您將創建幾個函數設置 $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(){
此函數需要迭代的次數與 $indentation 類變量值相同,對於每次迭代,它將 $indentationValue 類變量添加到 $xml,並將後者返回。因此,如果 $indentation 是 2,則在返回 $XML 之前將累計 8 個空間。此函數和 decrementIndentation 及 incrementIndentation 函數使得到的 XHtml 代碼具有正確的縮進格式。
$XML = '';
for($i = $this->indentation; $i > 0; $i--)
$XML .= $this->indentationValue;
return $XML;
}
清單 11 展示了如何使用 printData 函數顯示 XHtml。
清單 11. 顯示 XHtml
function printData($XML){
if($this->print){
echo $this->indentation();
echo $XML."rn";
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 小部件處理表單。
結束語
祝賀您。您現在已經擁有了一個相當健壯的 PHP XForms 庫,您可以使用該庫繼續進行開發和擴展。注意,該庫和它定義的 XForms XHTML 標記並不一定完整,庫本身及其錯誤檢查功能仍然具有擴展的空間。例如,您可以定義一些新函數,在其中定義一些新的 XForms XHtml 標記。本質上講,一切皆有可能,整個開源項目就是基於開發人員的不懈努力,正如這個分兩部分的 系列文章 中介紹的那樣。盡情享受您的 PHP 和 XForms 之旅,祝您編程愉快!