Google Web Toolkit(GWT)應用程序除了以傳統的 Java™ 方式連接到 servlet 外,還可以使用 PHP Web 服務發送和接收 XML 格式的數據。您將探索以 Java 和 PHP 語言生成和處理 XML 文檔的方法。
通過 GWT 可以方便地訪問用 Java 語言編寫的伺服器端 servlet,並且客戶機和伺服器之間的數據傳遞是透明的。但是,當使用 GWT 時,不僅可以與那些 servlet 通信,還可以隨意地與所有類型的 Web 服務交換數據。在很多情況下(對於簡單的服務),可以用純文本格式傳輸數據,但是每當遇到結構化的數據或較複雜的數據(例如 RSS)時,數據很可能是用 XML 表示的。
|
本文研究一個簡單的 GWT 應用程序和兩個 PHP Web 服務,展示生成和使用 XML 文檔的幾種不同的方法。本文無意成為詳盡的教程或手冊,而是提供一些忠告,使您能更快地開始使用 XML 作為連接 GWT 與 PHP 的橋樑。
測試應用程序
為了展示如何使用 XML 作為 PHP 與 GWT 之間的橋樑,我提供一個簡單的應用程序,這個應用程序基於國家/地區/城市數據。查看 清單 1 中的資料庫創建代碼,可以看到:
|
CREATE DATABASE world DEFAULT CHARACTER SET latin1 COLLATE latin1_general_ci; USE world; CREATE TABLE countries ( countryCode char(2) NOT NULL, countryName varchar(50) NOT NULL, PRIMARY KEY (countryCode) KEY countryName (countryName) ); CREATE TABLE regions ( countryCode char(2) NOT NULL, regionCode char(2) NOT NULL, regionName varchar(50) NOT NULL, PRIMARY KEY (countryCode,regionCode), KEY regionName (regionName) ); CREATE TABLE cities ( countryCode char(2) NOT NULL, cityName varchar(50) NOT NULL, cityAccentedName varchar(50) NOT NULL, regionCode char(2) NOT NULL, population bigint(20) NOT NULL, latitude float(10,7) NOT NULL, longitude float(10,7) NOT NULL, KEY `INDEX` (countryCode,regionCode,cityName), KEY cityName (cityName), KEY cityAccentedName (cityAccentedName) ); |
我創建了一個簡單的 GWT 項目,它只有一個表單和兩個 PHP Web 服務。(下載 小節提供了完整的源代碼)。啟動該應用程序時,可以看到 圖 1 中的窗口。
GWT 表單允許輸入一個城市名的一部分,然後調用一個 PHP 服務獲得與輸入的內容匹配的所有城市。這些城市顯示在一個網格中,可以編輯人口(population)、緯度(latitude)和經度(longitude)欄位。然後,可以將編輯的數據發回到另一個 PHP Web 服務,後者將更新資料庫。所有數據傳輸都是通過 XML 進行的。2009 年是查爾斯達爾文 200 誕辰和他的著作物種起源 誕生 150 周年,您可以查看城市名中包含 DARWIN 的城市;圖 2 顯示了結果。
一些額外的配置
下面是我使用的軟體,僅供參考:
我安裝的所有軟體都是開箱即用的,但是 GWT 要求一個額外的配置步驟,這樣才能測試 GWT-PHP 連接;請參閱側邊欄 SOP 問題,看看是什麼原因。為了禁用內部 GWT 瀏覽器的同源策略(same-origin policy,SOP)檢查,可以編輯 GWT 目錄中的 ./mozilla-1.7.12/greprefs/all.js 文件,在文件的最後添加 清單 2 中的代碼行:
pref("capability.policy.default.XMLHttpRequest.abort", "allAccess"); pref("capability.policy.default.XMLHttpRequest.getAllResponseHeaders","allAccess"); pref("capability.policy.default.XMLHttpRequest.getResponseHeader","allAccess"); pref("capability.policy.default.XMLHttpRequest.open", "allAccess"); pref("capability.policy.default.XMLHttpRequest.send", "allAccess"); pref("capability.policy.default.XMLHttpRequest.setRequestHeader","allAccess"); pref("capability.policy.default.XMLHttpRequest.onreadystatechange","allAccess"); pref("capability.policy.default.XMLHttpRequest.readyState", "allAccess"); pref("capability.policy.default.XMLHttpRequest.responseText","allAccess"); pref("capability.policy.default.XMLHttpRequest.responseXML","allAccess"); pref("capability.policy.default.XMLHttpRequest.status", "allAccess"); pref("capability.policy.default.XMLHttpRequest.statusText", "allAccess"); |
每當更新 GWT 時,需要再次作出這樣的更改。而且,請注意,如果不這樣做,編寫的代碼在 Hosted 模式下會失敗,但是在 compiled 模式下卻可以正確運行。作出以上更改后,您的代碼可能在 Hosted 模式下可以運行,但是在 Compiled 模式下卻會失敗,所以要小心!
|
用 PHP 發送 XML
這個應用程序只定義一個簡單的表單,其中有一些標籤、一個文本框、兩個命令按鈕和一個用於顯示結果的網格。每當單擊 Get cities 時,該應用程序調用一個 PHP 服務,以便獲得一個 XML 文檔,其中包含與文本框中輸入的內容匹配的所有城市。清單 3 顯示從 PHP 服務發送到 GWT 應用程序的一個示例 XML(有所簡化)。生成的 XML 代碼要用於演示一些 XML 功能,所以比用於真正的應用程序的 XML 要長得多。 通常,設計良好的 XML 服務使用更少的標記和更多的屬性,避免縮進,並且更短一些。
<?xml version="1.0" encoding="UTF-8"?> <cities> <city name="tokyo"> <country code="JP" name="Japan"/> <region code="40" name="Tokyo"/> <coords> <lat>35.6850014</lat> <lon>139.7513885</lon> </coords> <pop>31480498</pop> </city> <city name="tokyo"> <country code="PG" name="Papua New Guinea"/> <region code="01" name="Central"/> <coords> <lat>-8.3999996</lat> <lon>147.1499939</lon> </coords> </city> <city name="tokyojitori"> <country code="KR" name="Korea, Republic of"/> <region code="16" name="Cholla-namdo"/> <coords> <lat>34.2380562</lat> <lon>125.9394455</lon> </coords> </city> </cities> |
這個 PHP 服務本身有兩個版本 — getcities1.php 和 getcities2.php —,每個版本展示生成 XML 的不同方法。
到目前為止,生成 XML 的最簡單的方法是連續輸出適當的文本,或者構建一個字元串,然後使用 echo 發出它。這裡應該將 content type 設為 text/xml,使之能夠被正確地識別,並且還要記得包括一個適當的 description 行,以指定 XML 版本和數據編碼方式。另外還必須轉義字元串,使字元串中不包括小於(<)、大於(>)或和(&)字元。最簡單的方法是使用 htmlspecialchars() PHP 函數。代碼很容易編寫,如 清單 4 所示。注意,縮進和換行實際上是不需要的,不過這樣做可以讓代碼更易於閱讀。
... header("Content-type: text/xml"); ... echo '<?xml version="1.0" encoding="UTF-8"?>'."\n"; echo '<cities>'."\n"; ... while ($row= mysql_fetch_assoc($result)) { echo ' <city name="'.htmlspecialchars($row['cityName']).'">'."\n"; echo ' <country code="'.$row['countryCode'].'" '; echo 'name="'.htmlentities($row['countryName']).'"/>'."\n"; echo ' <region code="'.$row['regionCode'].'" '; echo 'name="'.htmlentities($row['regionName']).'"/>'."\n"; echo ' <coords>'."\n"; echo ' <lat>'.$row['latitude'].'</lat>'."\n"; echo ' <lon>'.$row['longitude'].'</lon>'."\n"; echo ' </coords>'."\n"; if ($row['population']>0) { echo ' <pop>'.$row['population'].'</pop>'."\n"; } echo ' </city>'."\n"; } echo '</cities>'."\n"; |
生成 XML 代碼的另一種方法(也不是很長)是使用 XMLWriter。(與之成對的另一個類 XMLReader 可用於 XML 處理)。現在可以不用關心字元的轉義,因為這是自動完成的。雖然這種方法看起來比前面的 echo() 方法冗長一點,但是這種方法編寫的代碼更易於理解。特別注意,這裡使用了 php://output 協議,以便用 echo 命令發出文本。(如 清單 5 所示)。
... $writer= new XMLWriter(); $writer->openURI('php://output') $writer->startDocument('1.0', 'UTF-8'); $writer->startElement("cities"); while ($row= mysql_fetch_assoc($result)) { $writer->startElement("city"); $writer->writeAttribute("name", $row['cityName']); $writer->startElement("country"); $writer->writeAttribute("code", $row['countryCode']); $writer->writeAttribute("name", $row['countryName']); $writer->endElement(); $writer->startElement("region"); $writer->writeAttribute("code", $row['regionCode']); $writer->writeAttribute("name", $row['regionName']); $writer->endElement(); $writer->startElement("coords"); $writer->writeElement("lat", $row['latitude']); $writer->writeElement("lon", $row['longitude']); $writer->endElement(); if ($row['population']>0) { $writer->writeElement("pop", $row['population']); } $writer->endElement(); // city } $writer->endElement(); // cities ... |
如果想體驗更多生成 XML 的方法,PHP 當然提供了很多的選擇。例如,可以使用 SimpleXML;以後您將使用它讀取 XML,但是它還提供 XML 文檔創建功能。另外,還可以看一下 PEAR 框架,它包括一些可用於輕鬆生成 XML 的類。(請參閱 參考資料,獲得更多信息的鏈接)。
用 GWT 處理 XML
GWT 只提供 XMLParser(在 com.google.gwt.xml.client 包中)用於讀、寫 XML。可以使用 parse() 方法創建一個 Document,然後使用 getDocumentElement() 獲得它的根元素;然後,便可以開始處理 XML。
注意,應該使用 removeWhitespace() 方法去掉文檔中的空白。瀏覽器解析器有時候會創建與製表符或換行符對應的空文本節點,如果不去掉它們,處理過程中會遇到不相關的、意外的元素,這樣會破壞邏輯(見 清單 6)。另外還有一點要注意:如果預期有 CDATA 區段,那麼需要檢查瀏覽器是否接受 supportsCDATASection() 方法;如果不接受,那些區段將產生文本節點。請參閱 GWT 文檔(見 參考資料),獲得更多這方面的信息。
protected void loadCities(final String xmlCities) { ... final Document xmlDoc= XMLParser.parse(xmlCities); final Element root= xmlDoc.getDocumentElement(); XMLParser.removeWhitespace(xmlDoc); final NodeList cities= root.getElementsByTagName("city"); for (int i= 0; i < cities.getLength(); i++) { final Element city= (Element)cities.item(i); // show city.getAttributeNode("name").getValue() final Element country= (Element)city.getElementsByTagName("country").item(0); // show country.getAttributeNode("code").getValue() // show country.getAttributeNode("name").getValue() ... final Element population= (Element)city.getElementsByTagName("pop").item(0); if (population != null) { // show population.getFirstChild().getNodeValue() } final Element coords= (Element)city.getElementsByTagName("coords").item(0); final Element lat= (Element)coords.getElementsByTagName("lat").item(0); // show lat.getFirstChild().getNodeValue() ... } ... |
要獲得重複的元素(例如這個例子中的 city),可以使用 getElementsByTagName() 方法,并迭代生成的數組。另一種方法是使用 getFirstChild() 方法,然後使用 getNextSibling() 遍歷同一級別上的其他元素。要獲得屬性,首先需要使用 getAttributeNode() 方法,然後使用 getValue() 方法。還有一些用於處理 CDATA 區段、註釋和所有可能的 XML 組件的方法。
用 GWT 發送 XML
GWT 應用程序讓用戶編輯人口、緯度和經度欄位,然後將城市數據發回到伺服器,以更新資料庫。有兩種演算法:一種是簡單的演算法,這種演算法逐塊構建 XML 字元串,還有一種基於 XMLParser 的演算法,該演算法使用特定的方法創建所需的結構。
getCities1() 方法中顯示了較簡單的演算法。可以使用 String 或 StringBuffer 對象構建 XML。我使用了前者,因為它使代碼更加清晰;但是如果考慮性能,後者很可能更好一些。這裡也存在和 PHP 版本中一樣的字元串轉義問題,所以使用 Html.htmlspecialchars() 方法修改該問題。(見 清單 7,為了看起來更清晰,稍微作了修改)。
protected String getCities1() { String result= ""; result+= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; result+= "<cities>\n"; for (all rows in the grid) { // get cityName, countryCode, regionCode, pop, lat, and lon, from the grid result+= " <city name=\"" + Html.htmlspecialchars(cityName) + "\">\n"; result+= " <country code=\"" + countryCode + "\"/>\n"; result+= " <region code=\"" + regionCode + "\"/>\n"; if (!pop.equals("0") && !pop.isEmpty()) { result+= " <pop>" + pop + "</pop>\n"; } result+= " <coords>\n"; result+= " <lat>" + lat + "</lat>\n"; result+= " <lon>" + lon + "</lon>\n"; result+= " </coords>\n"; result+= "</city>\n"; } result+= "</cities>\n"; return result; } |
另一種創建 XML 的簡單演算法是 XMLParser 類的 createDocument() 方法。這種演算法的風格很容易讓人想起 PHP 中的 SimpleXML 函數,首先創建一個空文檔,然後向其中添加元素。可以創建所有類型的節點,並設置屬性值。最後,標準的 Java toString() 方法生成對象的一個表示。您只需添加初始版本和編碼行,就可以得到需要的字元串。(見 清單 8,為了看起來更清晰,代碼有所修改和刪減)。
protected String getCities2() { Document xml= XMLParser.createDocument(); Element cities= xml.createElement("cities"); xml.appendChild(cities); for (all rows in the grid) { // get cityName, countryCode, regionCode, pop, lat, and lon, from the grid Element city= xml.createElement("city"); city.setAttribute("name", cityName); Element country= xml.createElement("country"); country.setAttribute("code", countryCode); city.appendChild(country); ... if (!pop.equals("0") && !pop.isEmpty()) { Element popEl= xml.createElement("pop"); Text popText= xml.createTextNode(pop); popEl.appendChild(popText); city.appendChild(popEl); } Element coords= xml.createElement("coords"); Element lat= xml.createElement("lat"); Text latText= xml.createTextNode(lat); lat.appendChild(latText); coords.appendChild(lat); ... city.appendChild(coords); cities.appendChild(city); } return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + xml.toString(); } |
在 PHP 中讀取 XML
在 PHP 中處理 XML 是一個老問題,有很多的解決方案。但是,我認為,從清晰和簡潔的角度看,沒有哪種方案能比得上 SimpleXML 。基本上,首先通過將 XML 文檔提供給 simplexml_load_string() 方法創建一個 PHP 對象,然後使用標準的 PHP 操作符遍歷結果。屬性變成數組中的元素(例如,清單 9 展示了如何讀取國家代碼),而元素則可以作為一個數組(通過使用 children() 方法)或作為對象的屬性來訪問。
$xml_str=Google Web Toolkit(GWT)應用程序除了以傳統的 Java™ 方式連接到 servlet 外,還可以使用 PHP Web 服務發送和接收 XML 格式的數據。您將探索以 Java 和 PHP 語言生成和處理 XML 文檔的方法。
本文研究一個簡單的 GWT 應用程序和兩個 PHP Web 服務,展示生成和使用 XML 文檔的幾種不同的方法。本文無意成為詳盡的教程或手冊,而是提供一些忠告,使您能更快地開始使用 XML 作為連接 GWT 與 PHP 的橋樑。 測試應用程序 為了展示如何使用 XML 作為 PHP 與 GWT 之間的橋樑,我提供一個簡單的應用程序,這個應用程序基於國家/地區/城市數據。查看 清單 1 中的資料庫創建代碼,可以看到:
清單 1. 資料庫創建代碼
我創建了一個簡單的 GWT 項目,它只有一個表單和兩個 PHP Web 服務。(下載 小節提供了完整的源代碼)。啟動該應用程序時,可以看到 圖 1 中的窗口。 圖 1. 空的表單 GWT 表單允許輸入一個城市名的一部分,然後調用一個 PHP 服務獲得與輸入的內容匹配的所有城市。這些城市顯示在一個網格中,可以編輯人口(population)、緯度(latitude)和經度(longitude)欄位。然後,可以將編輯的數據發回到另一個 PHP Web 服務,後者將更新資料庫。所有數據傳輸都是通過 XML 進行的。2009 年是查爾斯達爾文 200 誕辰和他的著作物種起源 誕生 150 周年,您可以查看城市名中包含 DARWIN 的城市;圖 2 顯示了結果。 圖 2. 搜索城市名中包含 “Darwin” 的城市 一些額外的配置 下面是我使用的軟體,僅供參考:
我安裝的所有軟體都是開箱即用的,但是 GWT 要求一個額外的配置步驟,這樣才能測試 GWT-PHP 連接;請參閱側邊欄 SOP 問題,看看是什麼原因。為了禁用內部 GWT 瀏覽器的同源策略(same-origin policy,SOP)檢查,可以編輯 GWT 目錄中的 ./mozilla-1.7.12/greprefs/all.js 文件,在文件的最後添加 清單 2 中的代碼行: 清單 2. 修改內部 GWT 瀏覽器的配置
每當更新 GWT 時,需要再次作出這樣的更改。而且,請注意,如果不這樣做,編寫的代碼在 Hosted 模式下會失敗,但是在 compiled 模式下卻可以正確運行。作出以上更改后,您的代碼可能在 Hosted 模式下可以運行,但是在 Compiled 模式下卻會失敗,所以要小心!
用 PHP 發送 XML 這個應用程序只定義一個簡單的表單,其中有一些標籤、一個文本框、兩個命令按鈕和一個用於顯示結果的網格。每當單擊 Get cities 時,該應用程序調用一個 PHP 服務,以便獲得一個 XML 文檔,其中包含與文本框中輸入的內容匹配的所有城市。清單 3 顯示從 PHP 服務發送到 GWT 應用程序的一個示例 XML(有所簡化)。生成的 XML 代碼要用於演示一些 XML 功能,所以比用於真正的應用程序的 XML 要長得多。 通常,設計良好的 XML 服務使用更少的標記和更多的屬性,避免縮進,並且更短一些。 清單 3. 搜索 “tokyo” 時生成的 XML 文檔
這個 PHP 服務本身有兩個版本 — getcities1.php 和 getcities2.php —,每個版本展示生成 XML 的不同方法。 到目前為止,生成 XML 的最簡單的方法是連續輸出適當的文本,或者構建一個字元串,然後使用 echo 發出它。這裡應該將 content type 設為 text/xml,使之能夠被正確地識別,並且還要記得包括一個適當的 description 行,以指定 XML 版本和數據編碼方式。另外還必須轉義字元串,使字元串中不包括小於(<)、大於(>)或和(&)字元。最簡單的方法是使用 htmlspecialchars() PHP 函數。代碼很容易編寫,如 清單 4 所示。注意,縮進和換行實際上是不需要的,不過這樣做可以讓代碼更易於閱讀。 清單 4. 從 PHP 服務生成 XML 的最簡單的方法
生成 XML 代碼的另一種方法(也不是很長)是使用 XMLWriter。(與之成對的另一個類 XMLReader 可用於 XML 處理)。現在可以不用關心字元的轉義,因為這是自動完成的。雖然這種方法看起來比前面的 echo() 方法冗長一點,但是這種方法編寫的代碼更易於理解。特別注意,這裡使用了 php://output 協議,以便用 echo 命令發出文本。(如 清單 5 所示)。 清單 5. XMLWriter 提供逐個元素地構建 XML 文檔的簡單方法
如果想體驗更多生成 XML 的方法,PHP 當然提供了很多的選擇。例如,可以使用 SimpleXML;以後您將使用它讀取 XML,但是它還提供 XML 文檔創建功能。另外,還可以看一下 PEAR 框架,它包括一些可用於輕鬆生成 XML 的類。(請參閱 參考資料,獲得更多信息的鏈接)。 用 GWT 處理 XML GWT 只提供 XMLParser(在 com.google.gwt.xml.client 包中)用於讀、寫 XML。可以使用 parse() 方法創建一個 Document,然後使用 getDocumentElement() 獲得它的根元素;然後,便可以開始處理 XML。 注意,應該使用 removeWhitespace() 方法去掉文檔中的空白。瀏覽器解析器有時候會創建與製表符或換行符對應的空文本節點,如果不去掉它們,處理過程中會遇到不相關的、意外的元素,這樣會破壞邏輯(見 清單 6)。另外還有一點要注意:如果預期有 CDATA 區段,那麼需要檢查瀏覽器是否接受 supportsCDATASection() 方法;如果不接受,那些區段將產生文本節點。請參閱 GWT 文檔(見 參考資料),獲得更多這方面的信息。 清單 6. GWT 中提供了 XMLParser 用於讀取和創建 XML
要獲得重複的元素(例如這個例子中的 city),可以使用 getElementsByTagName() 方法,并迭代生成的數組。另一種方法是使用 getFirstChild() 方法,然後使用 getNextSibling() 遍歷同一級別上的其他元素。要獲得屬性,首先需要使用 getAttributeNode() 方法,然後使用 getValue() 方法。還有一些用於處理 CDATA 區段、註釋和所有可能的 XML 組件的方法。 用 GWT 發送 XML GWT 應用程序讓用戶編輯人口、緯度和經度欄位,然後將城市數據發回到伺服器,以更新資料庫。有兩種演算法:一種是簡單的演算法,這種演算法逐塊構建 XML 字元串,還有一種基於 XMLParser 的演算法,該演算法使用特定的方法創建所需的結構。 getCities1() 方法中顯示了較簡單的演算法。可以使用 String 或 StringBuffer 對象構建 XML。我使用了前者,因為它使代碼更加清晰;但是如果考慮性能,後者很可能更好一些。這裡也存在和 PHP 版本中一樣的字元串轉義問題,所以使用 Html.htmlspecialchars() 方法修改該問題。(見 清單 7,為了看起來更清晰,稍微作了修改)。 清單 7. 使用字元串逐塊構建 XML 很簡單
另一種創建 XML 的簡單演算法是 XMLParser 類的 createDocument() 方法。這種演算法的風格很容易讓人想起 PHP 中的 SimpleXML 函數,首先創建一個空文檔,然後向其中添加元素。可以創建所有類型的節點,並設置屬性值。最後,標準的 Java toString() 方法生成對象的一個表示。您只需添加初始版本和編碼行,就可以得到需要的字元串。(見 清單 8,為了看起來更清晰,代碼有所修改和刪減)。 清單 8. 創建 XML 的 XMLParser 方法類似於 PHP 中的 SimpleXML 方法
在 PHP 中讀取 XML 在 PHP 中處理 XML 是一個老問題,有很多的解決方案。但是,我認為,從清晰和簡潔的角度看,沒有哪種方案能比得上 SimpleXML 。基本上,首先通過將 XML 文檔提供給 simplexml_load_string() 方法創建一個 PHP 對象,然後使用標準的 PHP 操作符遍歷結果。屬性變成數組中的元素(例如,清單 9 展示了如何讀取國家代碼),而元素則可以作為一個數組(通過使用 children() 方法)或作為對象的屬性來訪問。 清單 9. SimpleXML 是目前在 PHP 中進行 XML 處理的最容易的方法
在 PHP 中處理 XML 還有許多種方法,可以從 參考資料 小節找到相關的鏈接。 結束語 有很多方法可以使用 XML 作為 GWT 與 PHP 之間的橋樑,我只是略作探討,不過,本文給出的方法應該足以讓您邁出第一步。需要記住的要點是,GWT 不止局限於它自己的 RPC 方法,還可以與 XML 友好共存,輕鬆生成和使用 XML 文檔。 (責任編輯:A6) POST["xmldata"]; $xml_obj= simplexml_load_string($xml_str); ... foreach($xml_obj->children() as $city) { $name= addslashes($city['name']); $country= $city->country['code']; $region= $city->region['code']; $pop= $city->pop; $lat= $city->coords->lat; $lon= $city->coords->lon; mysql_query("REPLACE INTO cities ". "(cityName, countryCode, regionCode, population, latitude, longitude) VALUES (". "'{$name}', '{$country}', '{$region}', '{$pop}', '{$lat}', '{$lon}')"); } |
在 PHP 中處理 XML 還有許多種方法,可以從 參考資料 小節找到相關的鏈接。
結束語
有很多方法可以使用 XML 作為 GWT 與 PHP 之間的橋樑,我只是略作探討,不過,本文給出的方法應該足以讓您邁出第一步。需要記住的要點是,GWT 不止局限於它自己的 RPC 方法,還可以與 XML 友好共存,輕鬆生成和使用 XML 文檔。 (責任編輯:A6)
[火星人 ] XML:GWT 與 PHP 之間的橋樑已經有591次圍觀