Phalanger是一種PHP語言編譯器,也是針對.NET的PHP運行時。 它可以用於把PHP web項目編譯成.NET位元組碼,並在Windows中使用IIS或者在Linux上使用Mono和Apache作為ASP.NET應用程序來執行。 然而,Phalanger不僅僅是把已經存在的PHP應用編譯到.NET中。
我們可以使用Phalanger創建組合.NET和PHP的解決方案,所採用的方式用標準的PHP解釋器是不可能做到的。 有了Phalanger擴展,PHP程序可以直接使用.NET類,而.NET程序(比方說用C#編寫的)也可以動態地調用PHP腳本,或者使用在PHP中實現的函數和類【6】。
本文中,我么會簡要地介紹Phalanger,然後查看三種使用方案。 我們會討論如何整合PHP應用程序和.NET;如何高效地在Windows上運行PHP應用程序,以及如何使用PHP作為ASP.NET的視圖引擎(view engine)。
Phalanger已經存在一段時間了。 第一版Phalanger是於2003年在布拉格的查爾斯大學作為軟體項目創建的。稍後就開始了2.0版本的開發,並且於2006年在CodePlex作為開源項目發布。 微軟支持了這個項目一段時間,後來一位Phalanger開發者加入微軟,並從事動態語言運行時方面的工作。
Phalanger相關活動在2008年恢復,這多虧有了與Jadu的合作,它使用Phalanger為在PHP中開發的CMS構建了.NET版本。 從2010年開始,Phalanger的開發主要由DEVSENSE提供資金支持,它也為Phalanger提供了商業支持。 最近發布的版本Phalanger 2.1【7】,其中提升了與標準PHP實現的兼容性,在動態操作的實現過程中利用了DLR,並提供了PHP和其他.NET語言(像C#、F#和Visual Basic)之間的互操作性。
Phalanger包括多個部分獨立的組件,可以用來開發運行在.NET上的PHP應用程序,並使用.NET或Mono來運行它們:
Phalanger在很大程度上與PHP 5兼容,可以運行大量開源的PHP項目,包括WordPress和MediaWiki。 我們可以使用它把這些項目集成到.NET生態系統中,也可以開發新的項目,它會兼有PHP和.NET的優勢。 在本文剩下的內容中,我們會討論以下三種使用案例:
以下三個部分會詳細討論各種方案。 我們首先會給出概覽,然後查看一些技術細節,它會說明Phalanger中讓你感興趣的內容。
Phalanger之所以能夠高效地運行PHP應用程序,是因為以下兩個原因。 首先,它會編譯PHP源代碼,而不是解釋它;其次,它會把應用作為ASP.NET應用程序運行,那會在Windows下提供額外的性能優勢。
編譯過程如圖1所示。正如圖上所顯示的,Phalanger會把PHP源代碼編譯成.NET IL(中間語言),那是與架構獨立的低級位元組碼。 編譯后的代碼會使用PHP核心庫(Phalanger的一部分)和動態語言運行時(DLR)來執行標準的PHP操作。 當應用程序啟動時,.NET JIT(just-in-time)編譯器會把這些組件轉換為針對當前處理器架構優化過的本地代碼。
圖1. 使用Phalanger把PHP源代碼編譯成本地代碼的過程
正如Phalanger評測顯示【10】,使用Phalanger編譯的WordPress在Windows下的性能比通過FashCGI使用標準PHP解釋器的好,也比通過WinCache使用PHP的稍好一些。 然而,評測沒有測試Phalanger最新的版本,它使用DLR進行了進一步優化。
Phalanger應用程序的運行方式和ASP.NET應用程序完全相同。 這讓它具有了重要的性能優勢,特別是在Windows系統下,進程要比線程耗費更多資源。
圖2顯示了運行PHP應用程序的不同可選方案。
當使用標準CGI模式時,web伺服器會為每個進入的請求啟動新的進程。 在Windows下,這樣做的效率不高,它還阻止了共享位於共享內存中的狀態,也很難進行進程中緩存(in-process caching)。 當使用FashCGI模式時,web伺服器會重用進程,這樣它不需要為每個請求啟動新的進程。 然而,這還是無法共享內存中的狀態,因為不同的進程擁有不同的狀態。
(點擊圖像可以放大)
圖2. 使用CGI、FashCGI和Phalanger運行PHP
Phalanger的行為方式和所有ASP.NET應用一樣。 單獨的叫做應用池(Application Pool)的ASP.NET進程會處理所有進入的請求。 我們甚至可以在單一進程(應用池)中配置多個PHP應用程序(像多個WordPress的獨立實例)。 在進程中,會有多個線程,這些線程會被重用以處理單獨的請求。 在Windows下,線程要比過程更輕量級,所以這種解決方案更有效率,並且會消耗更少的內存。
對於運行在單一進程中的應用程序,我們可以進行進一步的優化,並採用其它有趣的方案。 例如,Phalanger會使用動態語言運行時(DLR)來做動態方法調用。 DLR會使用與時間相適應的緩存機制,因此在幾次請求之後,DLR就會“知道”應用程序使用的是哪個方法,並變得更快一些。 這隻有在單一進程中處理請求的情況下才可能做到。
在單一進程中運行所有代碼也意味著應用程序可以在內存中存儲全局狀態。 這可以用於實現與WinCache提供的User Cache類似的功能,但是不會有跨進程通信造成的負載。
PHP的一點優勢就在於擁有大量優秀的開源CMS系統(WordPress、Joomla等等)、表單應用程序(phpBB及其他)和wiki(Mediawiki及其他),其中很多都通過了Phalanger的測試。
這些應用通常會比.NET平台下類似的程序包提供更多特性。 開發基於ASP.NET網頁的公司可能會面臨以下情況:
ASP.NET應用程序可以使用ASP.NET的成員管理(ASP.NET Membership),它還用來管理用戶、角色和功能的標準機制。 有了Phalanger,我們就可以修改開源的PHP項目,從而使用同樣的機制。 下一部分會演示使用WordPress如何做到這一點。
如果你對代碼不感興趣,那麼就可以略過這個部分,直接查看第三種情況。 但是,我們不會查看技術細節,只是對讓PHP調用.NET程序庫的PHP擴展做簡要的概述。
我們可以使用插件輕鬆地自定義在WordPress管理用戶的方式。 管理用戶的插件需要實現一個PHP類,其中有各種成員函數。 其中最值得期待的功能就是身份驗證,它會獲得用戶名和密碼。 它應該填充當前用戶的信息,或者,當用戶不存在的時候,就會把名稱設置為NULL。
為了使用.NET中的ASP.NET成員管理來實現身份驗證功能,我們可以使用System.Web.Security命名空間中的功能。 靜態方法Membership.ValidateUser會檢查密碼是否正確,而Membership.GetUser會返回用戶的基本信息。 使用Phalanger,我們可以訪問.NET對象,就像它是標準的PHP對象一樣,這樣實現驗證機制就很簡單了。 代碼1展示了簡化后的代碼。
代碼1 在WordPress插件中實現身份驗證功能的函數
import namespace System:::Web:::Security;function authenticate(&$username,$password) {
global $errors;// Test whether the password is correct
if (Membership::ValidateUser($username,$password)) {
// Get information about the user and fill $userarray
$user = Membership::GetUser($username);
$userarray['user_login'] = $user->UserName;
$userarray['user_email'] = $user->Email;
$userarray['display_name'] = $username;
$userarray['user_pass'] = $password;
// Loading of roles & profiles omitted for simplicity// Update or create the user information in WordPress
if ($id = username_exists($username)) {
$userarray['ID'] = $id;
wp_update_user($userarray);
}
else
wp_insert_user($userarray);
} else {
// Report error if the login failed
$errors->add('user-rejected', 'Log-in failed!');
$username = NULL;
}
}
代碼首先聲明了重要的命名空間。 這是一個非標準的Phalanger擴展,它從引用的程序庫的.NET命名空間中導入了功能(我們可以使用web.config文件來引用程序庫)。 在將來的版本中,Phalanger會使用PHP 5.3支持的標準命名空間,但是這項改變還沒有完全實現。
剩餘部分的代碼看起來和標準的PHP代碼一樣。 然而,Membership類實際上是標準的.NET類。 Phalanger會把PHP類和.NET類同等對待,所以我們可以使用標準的語法來調用.NET方法。 函數ValidateUser和GetUser都是靜態函數,所以使用::語法來調用。 GetUser的結果是一個.NET的MembershipUser對象, 其中帶有各種屬性,包括關於用戶的基本信息。 我們仍然可以使用標準的標記法來訪問對象的欄位(它們被實現為.NET的屬性)。
正如你所看到的,我們可以很自然地在PHP中使用.NET功能。 由於代碼會被編譯成.NET程序,所以在調用.NET庫時不會有任何負載。 下一部分展示的是反方向的整合——從.NET應用程序中調用PHP。
PHP的主要優勢就在於靈活性和簡單性,這使得它成為編寫腳本和實現渲染HTML很棒的語言。 然而,有些人發現,想要實現大型應用程序,那麼在靜態類型語言——像Java或C#——會更容易一些。 使用Phalanger,我們可以同時獲得兩方面的優勢。
這個部分所討論的方案演示了一種組合ASP.NET和PHP的方式。 它基於先進的ASP.NET MVC(模型、視圖、控制器)框架,將表現層、負責交互的層和應用程序的業務邏輯分離開來。 我們可以使用不同的語言來開發單獨的組件:
還有一些情況,從C#中調用PHP會很有用。 例如,你可以在大型的C#項目中使用PHP作為腳本語言。 這也非常有用,因為PHP是一種廣為所知的語言。 另一種情況是,當在C#中使用PHP程序庫的時候——正因為有了Phalanger的duck typing機制,這才得到了很大程度的簡化,該機制甚至可以為調用文檔齊備的PHP代碼生成靜態類型的C#介面。
在本文剩餘的內容中,我們會著重討論使用PHP實現ASP.NET應用程序表現層的方案。 你可以在文章末尾找到其他方案(像編寫腳本)的參考信息。
首先讓我們看下使用C#和PHP組合創建出來的簡單應用程序。 應用程序的模型和控制器都是使用C#編寫的,如代碼2所示。在這個例子中,模型只是一個簡單的C#類,它表示的是產品信息。 在現實情況下,這個類可能會負責從資料庫載入數據,並且可能使用LINQ來實現。
代碼2: 示例web應用程序(C#)的模型和控制器
public class Product { public string ProductName { get; set } public double Price { get; set } }public class HomeController : Controller {
public ActionResult Index() {
ViewData.Model = new Product { ProductName = "John Doe", Price = 99.9 };
return View();
}
}
控制器組件是通過HomeController類實現的,它會繼承ASP.NET MVC控制器。 類中只包含一個動作,展現應用程序的索引頁面。 當用戶訪問/Home/Index(或者根URL)的時候就會觸發這個動作。 它會創建模型(Product類的實例)並把它傳遞給視圖組件。
在標準的ASP.NET MVC 應用程序中,視圖組件通常會使用ASPX頁面或者使用帶有使用C#或Visual Basic編寫的代碼的Razor視圖來實現。 Phalanger讓我們可以使用PHP來實現視圖。 代碼3展示了這個例子。
代碼3 示例Web應用程序(PHP)的視圖
<html><head> <title>Sample view written in PHPtitle> head> <body> <h1>Product Listing using Phalangerh1> Product: $MODEL->ProductName; ?><br /> Price: $MODEL->Price; ?> body>html>
視圖會使用下面描述的ASP.NET MVC擴展來渲染。 擴展會執行代碼3中所示的PHP腳本,並定義名為$MODEL的全局變數,其中會包含控制器返回的數據。 在上述示例中,$MODEL是對標準.NET類的引用。 Phalanger會對.NET類和PHP對象同等對待,所以使用echo結構,我們很容易就可以顯示產品的屬性。
示例顯示了應用程序的基本結構,但是它極為簡單,所以不會真正顯示出在表現層使用PHP所能給我們帶來的好處:
為了讓你更好地了解這個方案的工作方式,以下部分會說明關於PHP和C#整合的技術細節。 如果你對細節不感興趣,那麼就可以直接跳到總結部分。
這個部分所描述的方案基於PicoMVC項目【4】,它讓我們可以組合PHP和F#。 為了讓示例更簡單,我把代碼從F#轉換為C#。 在PicoMVC中PHP整合的核心是一個簡單的函數,它會取得PHP腳本的文件名,並使用Phalanger運行時來運行。 函數如代碼4所示。
代碼4 從ASP.NET web應用程序調用PHP腳本
void PhalanagerView(string fileName, object model, HttpContext current) { // Initialize PHP request context and output stream using(var rc = RequestContext.Initialize(ApplicationContext.Default, current)) using(var byteOut = HttpContext.Current.Response.OutputStream) using(var uftOut = new StreamWriter(byteOut)) {// Current context for evaluating PHP scripts
var phpContext = ScriptContext.CurrentContext;// Redirect PHP output to the HTTP output stream
phpContext.Output = uftOut;
phpContext.OutputStream = byteOut;// Declare global $MODEL variable (if model is set)
if (model != null)
Operators.SetVariable(phpContext, null, "MODEL",
ClrObject.WrapDynamic(model));
phpContext.Include(fileName, false);
}
}
PhalangeriView方法會獲得文件名(指向PHP腳本)、代表作為模型返回的數據的.NET對象以及當前的HTTP上下文。 它首先會初始化RequestContext,從而Phalanger知道它是在處理作為HTTP請求一部分的腳本。 然後,它會確保所有PHP腳本生成的輸出都會直接作為HTTP響應發送。 當作為腳本運行PHP的時候,輸出可以重定向到內存流,從而以不同的方式處理。 最後,方法會聲明全局變數MODEL,並使用Phalanger所提供的Include方法來執行PHP腳本。
這個例子並不完全是從C#調用PHP的指引,你可以在Phalanger博客的文章中找到更詳細的信息。 然而,它應該可以說明,使用Phalanger從C#調用PHP腳本相當容易。 這在本節討論的web編程情況下會很有用,但是它給了我們更多選擇。
本文簡要地介紹了Phalanger——針對.NET的PHP編譯器——以及幾種方案,我們可以在實踐中使用它來解決重要問題。 最近Phalanger項目非常活躍,2.1版本中包含了很多兼容性方面的改善、使用動態語言運行時(DLR)以獲得更好的性能,以及與Visual Studio 2010的集成。
我們看了三種可以在web開發中使用Phalanger的方案。 第一種方案是使用Phalanger在Windows環境下運行未經修改的開源PHP項目(像WordPress)。 使用Phalanger編譯的應用程序可以運行在ASP.NET下,這種主機會更輕量級,運行效率也更高。
在第二種方案中,我們查看了集成在.NET生態系統中的PHP應用程序。 有了Phalanger擴展,我們就可以在PHP代碼中直接調用.NET程序庫。 例如,這可以用來整合ASP.NET應用程序和WordPress之間的用戶資料庫。
最後一種方案演示了一種web框架,它使用PHP作為在ASP.NET MVC中編寫視圖的語言。 通過這種方式,.NET開發者可以很容易地提供應用程序的業務功能,而PHP開發者可以在表現層中直接使用它。
Tomas Petricek是微軟的C# MVP和F#佈道師。 他和Jon Skeet一起編寫了《Real-World Functional Programming》一書,說明了函數式的概念,並向有C#背景的開發者說明如何使用F#。 Tomas還是DEVSENSE的共同創始人,他對很多開源項目都做出了貢獻,包括Phalanger和F#語言針對MonoDevelop的集成。
[1] The Phalanger網站包含了關於項目的最新消息。
[2] 文檔和介紹(Phalanger Wiki)
[3] Phalanger 2.1下載包括安裝程序和源代碼(CodePlex)
[4] PicoMVC 項目使用Phalanger作為輕量級的視圖引擎(Robert Pkckering的Strange博客)
[5] PHP作為針對C#的腳本語言 討論了另一種對Phalanger的用法(Phalanger博客)
[6]以類型安全的方式在C#中使用PHP對象(Using PHP objects from C# in a type-safe way) 說明了Phalanger提供的,用於從C#訪問PHP對象的安全機制。
[7]Phalanger利用DLR的優勢宣布了Phalanger 2.1的發布(Phalanger博客)
[8] Jadu CMS和Microsoft .NET ——使用Phalanger把PHP應用程序編譯到.NET的案例學習 [9]Phalanger支持包含了額外託管的PHP5擴展(DEVSENSE)
[10] Phalanger評測 包含額外的性能信息(Phalanger博客)
查看英文原文:Integrating PHP with .NET Using Phalanger
[火星人 ] 使用Phalanger整合PHP和.NET已經有616次圍觀