DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> XML學習教程 >> XML詳解 >> 用XSLT 2.0生成PHP
用XSLT 2.0生成PHP
編輯:XML詳解     

 本文示例源代碼或素材下載

  在兩部分組成的關於 XSLT 的系列文章的第一期中,介紹了 XSLT 2.0 的一些新特性,並說明了如何從抽象數據模型生成代碼。為了示范這一過程,我建立了項目,開發一個健壯的代碼生成器,為數據庫服務器生成 SQL,並為 Web 服務器生成用於訪問數據庫的 PHP。我使用多層轉換通過 XSL 構建 SQL。首先將抽象模型轉化成數據庫物理模式模型,然後使用該模式模型構建 SQL 代碼。

  接下來就要構建代碼模型並從這個模型生成 PHP。在結束項目時,您將擁有系統的抽象模型、構建數據庫的 SQL 代碼和用於每個表的 PHP 包裝器。但是在深入討論 PHP 生成之前,我想首先回顧一下影響 XSL 模板設計及使用的 XSL 和 XSLT 2.0 的新特性。

  對 XSL 模板的增強

  要生成成功的代碼則需要對目標語言(這裡是 PHP)和代碼生成語言(該例中即 XSLT)有充分的了解。第 1 部分的重點放在代碼生成的基本原理上。本文對代碼生成等式中的 XSL 一端進行了更深入的分析。

  從本質上說,XSLT 是一種模板化的語言。它接受 XML 作為輸入,然後使用一組模板將 XML 轉化成 XML、Html 或文本。生成器是相關模板的集合,使用兩種模式 —— XML 模式和 Text 模式 —— 將原來輸入的 XML 轉化成代碼。位於抽象模型和代碼模板之間的中間模型使用 XML 模式,生成 PHP 和 SQL 的代碼模板則使用文本模式。

  這個代碼生成系統使用 Saxon XSLT 引擎和一組自定義模板。為了方便起見,這些模板和輸入放在同一個目錄中。模板輸出分別放到 PHP 和 SQL 代碼目錄中。不需要對 Saxon 作專門的擴展,雖然如果發現基本安裝所提供的 XSL 標簽或 XPath 函數不敷使用,那麼可以用 Java? 擴展模板引擎。

  XSL 模板的入口是與輸入 XML 的根節點匹配的 XSL 模板。在 XSL 引擎啟動時,它將輸入 XML 應用於模板庫。如果有與根節點(/)匹配的特殊模板,則首先執行它。下面是生成器中的主模板標簽:

<xsl:template match="/">

  這個匹配系統很重要,因為 XSL 要查看應用於當前正在處理節點的可用模板的列表,然後應用最匹配的模板。看一下清單 1 中的代碼,這是上一期文章中的一個例子。

  清單 1. 帶有模式類型的模板

<xsl:template match="create" mode="sql">
DROP TABLE IF EXISTS <xsl:value-of select="@name" />;
CREATE <xsl:value-of select="@name" /> (
<xsl:apply-templates mode="sql" select="fIEld" />
PRIMARY KEY ( <xsl:value-of select="@primary-key" /> )
    );
</xsl:template>

  代碼告訴 XSL 這個模板應用於 create 標簽。因此,當 XSL 遇到 create 標簽時,它就會執行該模板。此外,還要注意該模板指定了模式。需要像清單 2 那樣在 xsl:apply-templates 標簽中指定模式。

  清單 2. 應用模板的標簽

<xsl:apply-templates mode="sql" select="$sql-model/sql" />

  XSL 中的模式

  模式是 XSL 的一個重要概念。因為內存中可能同時有多個 XML 分層結構,需要有一種方法對特定分層結構應用一組特殊的模板,或者可用於單個 XML 源的一組轉換。於是就引入了模式。這個代碼生成器中有一組模板以 PHP 作為模式,另一組則用 SQL 作為模式。通過使用模式將這兩種語言的模板邏輯區分開來。

  xsl:apply-templates 標簽告訴 XSL 將可以用的模板應用於 XML 模型中 select 標簽所指定的那一部分。此外,還規定了模式 sql,因此 XSL 只尋找那些采用 sql 模式的模板。這是一種方便的模板名稱空間機制。

 但是這些模板的輸出怎麼辦呢?這就要用到新的 xsl:result-document 標簽(參見清單 3)。

  清單 3. 調用 SQL 模板的代碼

    <xsl:result-document href="db/gen-tables.sql" format="sql" >
      <xsl:apply-templates mode="sql" select="$sql-model/sql" />
    </xsl:result-document>

  一般說來,模板輸出到運行 XSL 轉換器時所指定的輸出文件,通常是一個文件或者標准輸出(及控制台或者默認的輸出文件)。在 XSL 模板中使用 xsl:result-document 標簽,可以指定新的文件來保存輸出。該例中將把輸出發送到文件 db/gen-tables.sql 中。XSL 的這一新特性對於代碼生成而言至關重要,因為常常需要從一段 XML 輸入創建多個輸出文件。

  XSLT 2.0 中修改的另一個重要標簽是 xsl:variable。和 xsl:result-document 標簽一樣,xsl:variable 改變了其中嵌套標簽的輸出結果的走向。這些嵌套標簽不是輸出到文件中,而是輸出到內存中的臨時變量中。該例中將調用的模板的輸出放到變量 sql-model 中(參見清單 4)。這個變量實際上是一個 XML 樹,以後可將其他模板應用於該樹。

  清單 4. 創建臨時樹變量的代碼

    <xsl:variable name="sql-model">
      <xsl:call-template name="gen-sql-model">
        <xsl:with-param name="model" select="."/>
      </xsl:call-template>
    </xsl:variable>

  同時在內存中擁有多個 XML 樹這種能力大大增強了 XSL 的應用范圍。通過它可以建立更大的模板系統,同時又降低了每個模板的復雜性。

 XSLT 中另一個重要的改進是引入了 xsl:function 標簽,可用它創建新的 XPath 函數(參見清單 5)。

  清單 5. 創建 XPath 函數的代碼

<xsl:function name="gen:model-type-to-sql">

  我使用該標簽創建了一個新的函數,將抽象模型(原始輸入文件)中指定的類型映射到輸出文件中的 SQL 原生類型(清單 6)。使用模板也能做到這一點,但是通過這種新語法可以聲明在 XPath 中使用的函數,而且方便得多。

  清單 6. 調用 XPath 函數的代碼

<fIEld name="{lower-case(@name)}" type="{gen:model-type-to-sql(@type)}"/>

  可以看到,清單 6 中的代碼要比 XSLT 1.0 中使用的 xsl:call-template 功能簡單得多。關於 XSLT 和 XSLT 2.0 新增特性的更多信息,請參閱參考資料。

  現在我們來看看如何利用剛剛介紹的 XSLT 新特性從模型中生成 PHP。首先要介紹抽象模型,即 XML 輸入文件。在第一篇文章中,我使用模板創建了 SQL 模型,然後從該模型構建 SQL 代碼。這裡用同樣的技術從 PHP 模型構建 PHP 代碼。。

  從抽象數據模型生成 PHP

  要生成 PHP 首先要建立需要生成的代碼的模型,然後將模板應用於這個新的模型。對於上一次介紹的 Author 表,新建的數據庫訪問模型如清單 7 中的代碼所示。

  清單 7. 對應於抽象輸入的語言模型

<?XML version="1.0" encoding="UTF-8"?>
<modules XMLns:gen="http://www.codegeneration.Net/">
  <module name="Author">
   <class name="Author">
     <constructor name="Author"/>
     <method name="selectAll">
      <parameters/>
      <implementation>
        <selectAll table="author">
         <fIEld name="author_id"/>
         <fIEld name="first"/>
         <fIEld name="last"/>
        </selectAll>
      </implementation>
     </method>
     <method name="selectOne">
      <parameters>
        <variable name="author_id"/>
      </parameters>
      <implementation>
        <selectOne table="author" by="author_id">
         <fIEld name="author_id"/>
         <fIEld name="first"/>
         <fIEld name="last"/>
        </selectOne>
      </implementation>
     </method>
     <method type="insert" name="insert">
      <parameters>
        <variable name="first"/>
        <variable name="last"/>
      </parameters>
      <implementation>
        <insert table="author">
         <fIEld name="first"/>
         <fIEld name="last"/>
        </insert>
      </implementation>
     </method>
   </class>
  </module>

這種 XML 結構和最終將看到的代碼結構完全匹配。但是,要注意這些代碼沒有一點是 PHP 專用的。可以用該模型生成任何潛在類型化的語言。比如,只要在模型中增加適當的類型信息就可以生成 Java 代碼。要了解這種 XML 構造在該方案中的重要性,看一看清單 8 中由此產生的 PHP 就知道了。

  清單 8. 輸出的 Author 表

<script language="PHP">
class Author extends DatabaseTable {
  function Author ( ) { }
  function selectAll ( )
  {
  $dbh = getDbh();
  return $dbh->doQuery(
  "SELECT author_id, first, last FROM author"
  );
}
  function selectOne ( $author_id )
  {
  $dbh = getDbh();
  return $dbh->doQuery(
  "SELECT author_id, first, last FROM author WHERE author = ?",
  $author_id );
}
  function insert ( $first, $last )
  {
  $dbh = getDbh();
  $dbh->doCommand( "INSERT INTO author ( first, last ) VALUES ( ?, ? )",
  $first, $last );
}
}
</script>

  如果回頭再看一看清單 7 中的 XML,就會發現 XML 表示中的每個標簽都代表 PHP 中的一個結構。比如,模型中 insert 類型的 method 標簽,以及輸出類中的結果 input 函數。

  我將介紹其中的幾個代碼模板,說明其工作原理。

  代碼模板的例子

  第一個模板是 Module 模板,用於建立新的 .PHP 文件,如清單 9 所示。

  清單 9. Module 模板

 <!-- PHP: Module tag handler -->
<xsl:template match="module" mode="PHP">
<xsl:result-document href="php/{@name}.PHP">
<script language="PHP">
<xsl:apply-templates select="*" mode="PHP" />
</script>
</xsl:result-document>
</xsl:template>

  注意,該模板使用 xsl:result-document 標簽為每個模塊創建新文件,然後通過 xsl:apply-templates 標簽將代碼填寫到文件中。

  為了完成 insert 方法的實現部分,我使用了清單 10 所示的模板。

  清單 10. SQL Insert 模板

  <!-- PHP: SQL Insert tag handler -->
<xsl:template match="insert" mode="PHP">
<xsl:call-template name="PHP-db-function">
<xsl:with-param name="content">
<xsl:call-template name="PHP-sql-command">
<xsl:with-param name="command" select="gen:build-insert(.)" />
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
</xsl:template>

  另一個重要的調用是 build-insert 函數。清單 11 顯示了該函數的代碼。

  清單 11. insert 命令構造器

  <!-- Builds an SQL insert command -->
<xsl:function name="gen:build-insert">
<xsl:param name="query" />
<xsl:variable name="out">
<sql>
  <query>
INSERT INTO <xsl:value-of select="$query/@table" />
(
<xsl:for-each select="$query/fIEld"><xsl:if test="position() > 1">,
  </xsl:if><xsl:value-of select="@name" /></xsl:for-each>
) VALUES (
<xsl:for-each select="$query/fIEld"><xsl:if test="position() > 1">,
  </xsl:if>?</xsl:for-each>
)
  </query>
  <inputs>
    <xsl:for-each select="$query/fIEld">
  <input name="{@name}" />
  </xsl:for-each>
  </inputs>
</sql>
</xsl:variable>
  <xsl:sequence select="$out" />
</xsl:function>

該函數返回完整的數據結構,包括查詢和要求的輸入。該函數也可在 XSLT 1.0 中實現,但是 XSLT 2.0 中的自定義函數更方便。

  php-sql-command 模板使用 SQL 數據結構生成 PHP,參見清單 12。

  清單 12. SQL 命令模板

  <!-- SQL command template -->
<xsl:template name="PHP-sql-command">
<xsl:param name="command" />$dbh->doCommand(
  "<xsl:value-of select="normalize-space( $command/sql/query )" />"
  <xsl:for-each select="$command/sql/inputs/input">,
         $<xsl:value-of select="@name" />
  </xsl:for-each> );
</xsl:template>

  在 $command/sql/query 和 $command/sql/inputs/input 標簽中進行 SQL 查詢,並在這些標簽中將輸入轉換成代碼。

  結束語

  雖然看起來似乎很小,但是這裡有可能建立數百個 PHP 數據庫訪問類。當然,這兩篇文章中所舉的僅僅是一個例子,要使用生成器還必須實現 DatabaseTable 基類,並增加刪除和更新的方法。為了避免文中的代碼過長,這裡沒有列出這些方法。

  這些文章中的內容可以分為兩類:技術性的和理論性的。

  從技術上來說有兩點:首先,XSLT 非常適合用於代碼生成,而 XSLT 2.0 進一步簡化了這個過程;其次,在生成器中使用多個模型和多個轉換層次使應用程序更容易理解、開發和維護。

  從理論上來說,可以看到利用代碼生成技術可以迅速、准確地生成大量重復性的代碼。這有幾方面的好處,至少可以把精力集中到編寫代碼上,而不必再重復性的工作中浪費精神。此外,還可以快速改變代碼庫,以適應變化的需求。還可以根據需要相對較快地改變技術,因為重要的設計信息是一個抽象模型,沒有嵌入到代碼中。

  幾年來,我一直撰寫、講授和宣傳代碼生成技術。其間和很多工程師交談過,他們都對項目中使用的代碼生成持肯定態度。其中一位甚至對我說,“如果您有一輛美國造的福特車,這輛車的零部件都是通過代碼生成根據產品生產需要提供給福特的。”通過代碼生成這種富有創建、注重實效的方法,可以大大減少生產時間、提高代碼質量,讓編程人員專心編寫那些感興趣的代碼。


 


 

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved