如玫瑰一般的PHP與C#混合程式設計
故事背景是這樣的,有一套專案,伺服器端是用C#寫的,為了完成某種事情,它需要使用到一個元件,這個元件很小但很重要,很不巧的是,這個這個元件是用PHP語言寫的,如果為了使用這個元件而專門搭建一個PHP的環境顯得有點高射炮打蚊子(況且還有其他不可預見的阻力)。或許有讀者會提出“抗議”:不是PHP寫的麼,直接看原始碼翻譯出一份C#版的不就行了?然而事實並不如想象的美好,總之就是短期內無法這麼做了。
如今的C#已經非常強大了,它除了可以做我們普通的站點開發、桌面開發和原生的Windows Phone、應用商店開發,還可以做其他諸如IOS、安卓開發;也通過用CLR來托起一個JVM(這裡指IKVM.NET)來跑Java應用程式,當然也可以通過把PHP編譯成IL來跑PHP的網站程式了。
本篇中,我們就如何進行PHP與C#混合程式設計作如下討論:
(1)、PHP與C#的膠水:“Phalanger”
(2)、牛刀小試跑跑PHP
(3)、如何新增PHP類庫
(4)、C#與PHP混合互調
(5)、美麗如玫瑰,採摘須謹慎
本文中的示例程式碼請點選這裡進行下載。
1、什麼是Phalanger
什麼事Phalanger,最簡單的概括就是,它能夠把PHP編譯成DLL供我們C#呼叫。
我們要做混合程式設計,首先得先從官網那裡下載一個安裝包。
下載下來之後,開啟壓縮包,雙擊Setup開始安裝。
把這些都給安裝了,前面兩個必裝,最後一個是模板檔案,不想裝就不裝,已經安裝的話它會寫著“Already installed”。由圖中,我們可以得知一個資訊,那就是它只支援Framework 4.0。
把上面的東西安裝好之後,我們需要的“膠水”就得到了。
2、在.NET裡面跑一個php
嗯,phpinfo(),我沒有猜出,這個函式應該是各位讀者們第一個敲的,也是搭建完PHP環境之後必敲寫的一個函式。我們也牛刀小試一番,試試執行這個函式。
我們先建立一個空的WebApplication
然後新增一個PHP檔案,由於我們沒有直接的PHP檔案新增,所以我們隨便添加了一個內容少一點的檔案,然後改它的字尾為php並刪光裡面的程式碼。
寫上我們的程式碼(還帶有智慧提示,先進!!!):
然後按下“F5”
然後就奇蹟般的出現了我們想要的頁面。
3、新增PHP的類庫
事實上,即使我們是寫原生的PHP,我們都需要使用到大量的PHP庫,譬如MySQL庫、GD庫、CURL庫等。在這裡當然也不例外,我們也需要使用那些庫。不過在這裡,我們所使用的庫並不是PHP/ext中的那些庫,而是Phalanger給我們準備好的庫,它們隨著Phalanger的安裝一同安裝到我們的電腦當中,有興趣的讀者可以翻開GAC目錄,裡面會多了很多php打頭的資料夾,那些就是與Phalanger相關的庫了。
需要使用哪個庫,就自行的在WebConfig的phpNet節點下新增,譬如我需要用MySQL的庫,則在WebConfig這樣配置
<?xml version="1.0" encoding="utf-8"?> <!-- 有關如何配置 ASP.NET 應用程式的詳細訊息,請訪問 http://go.microsoft.com/fwlink/?LinkId=169433 --> <configuration> <system.web> <compilation debug="true" targetFramework="4.0" /> </system.web> <phpNet> <classLibrary> <add assembly="PhpNetMySql, Version=3.0.0.0, Culture=neutral, PublicKeyToken=2771987119c16a03" section="mysql"/> </classLibrary> </phpNet> </configuration>Web.Config
這裡還有另外一點需要注意,CodePlex上的擴充套件下載下來之後是這樣的。
MySql的庫命名有問題,正確的應該是“MySql.Data.dll”,各位讀者請留意。
然後就是寫下我們讀取資料庫資訊的程式碼:
<?php /***** 判斷庫是否被載入 *****/ $extensionName="mysql"; if(!extension_loaded($extensionName)){ echo $extensionName.'沒有被載入進來'; exit; } /**** 操作mysql ****/ $host="192.168.70.128"; $name="root"; $pwd="root"; $conn=mysql_connect($host,$name,$pwd) or die("mysql資料庫連線失敗"); mysql_select_db("phalangerdb",$conn)or die("無法選擇資料庫"); mysql_query("set names utf8"); $sqlstr="select * from person"; $result = mysql_query($sqlstr); echo '<pre>'; while($row=mysql_fetch_row($result)){ print_r($row); } echo '</pre>'; mysql_free_result($result); mysql_close($conn);mysqlextension.php
然後執行並檢視結果:
它成功的讀取到我們資料庫的東西並輸出到頁面中。
4、C#與PHP互調
既然是混合程式設計,如果沒有兩種語言之間的相互呼叫那又如何能夠稱得上混合程式設計呢?本節中,我們主要分兩個部分,其一就是PHP調C#函式,其二就是C#掉PHP函式。
PHP呼叫C#,Phalanger官網中主要的範例均是此,這裡就不作講解了,有需要的讀者可移步到Phalanger官網中看裡面的Blog。本文中講解的是使用概率更高同時也是官網中比較缺乏資料的C#呼叫PHP的函式。
首先我們先建立一個PHP的函式:
<?php function Sum($a,$b){ return $a+$b; } function SayHello(){ return 'Hello,我是小蝶驚鴻'; }Fun.php
然後新建一個WebForm頁面程式,並在CodeBehind中呼叫它(這裡是簡單加法例子):
namespace PhalangerDemo.demo3 { using System; public partial class WebForm1 : System.Web.UI.Page { private PHP.Core.ScriptContext phpContext; public WebForm1() { phpContext = PHP.Core.ScriptContext.CurrentContext; phpContext.Include("Fun.php", true); } protected void Page_Load(object sender, EventArgs e) { var context = phpContext.Call("SayHello", new object[] { }).ToString(); Response.Write(context.StartsWith("&") ? context.Substring(1) : context); } protected void btnAdd_Click(object sender, EventArgs e) { var num1 = txtNum1.Text as object; var num2 = txtNum2.Text as object; var result = phpContext.Call("Sum", new object[] { num1, num2 }).ToString(); txtRes.Text = result.StartsWith("&") ? result.Substring(1) : result; } } }View Code
然後再頁面中執行看看,Cool:
這裡,我要對CodeBehind(C#)部分的程式碼進行下講解,它的原理大概如下:
(1)、先獲取PHP的上下文物件PHPContext
(2)、然後往PHPContext中require_once我們寫的“Fun.php”
(3)、通過Call方法,第一個引數傳入方法名,第二個引數傳入Object陣列型別的引數。呼叫PHP中的函式
(4)、在被呼叫的PHP函式執行完成之後,將它的返回接收回來。並過濾開頭的“&”字元。
至於更深入的,譬如如何New一個PHP的Class之類的,我沒有進行深入的研究,所以這裡就不作描述,有興趣的讀者可以自行深入研究,同時也歡迎有此經驗的讀者進行分享。
5、這美麗又醜陋的Phalanger
在前面的幾章中,Phalanger的表現是如此的美麗優秀,它好比一朵玫瑰,看起來是那麼的鮮豔,聞起來是那麼的幽香,但是當你想採摘它的時候,手中拿著的確是幽豔玫瑰之下的荊棘。也只有你劃破傷口流著血的時候,你才感覺到原來採摘這朵玫瑰是那麼的痛。
我把開篇時的故事繼續說完,到了這一步,讀者們大概也可以猜到是個什麼情況了,沒錯,我的嘗試失敗了,這個PHP的元件無法正常的執行。
下面,我給各位讀者分享兩個這個元件所遇到致命性傷痕:
(1)、if中對byte的真假識別不一致。
同樣是讀取一個檔案,通過if(byte[])來判斷這個檔案是否為空,原生的PHP中可以根據傳入的byte[]是否為null來決定true/false,而Phalanger則無論如何一直返回false。
(2)、同樣的PHP內建函式,執行的效果卻不一致。
這裡的所說的函式執行效果不一致並不是說效果完全的不一致,普通的使用還是沒有問題的(正如我剛才讀取mysql那樣),而是有極少數的函式在特殊的條件下執行後得出的結果在原生PHP與Phalanger中是不一樣的。
當然了,一百個讀者有一百個海姆萊特,蘿蔔青菜各有所愛,Phalanger到底值不值得使用,應該怎麼使用,還是全憑各位讀者自己博弈了。