65.9K
CodeProject 正在变化。 阅读更多。
Home

Phalanger,适用于 .NET 的 PHP:.NET 开发人员入门

starIconstarIconstarIconstarIconstarIcon

5.00/5 (38投票s)

2007年1月24日

CPOL

29分钟阅读

viewsIcon

257319

downloadIcon

4120

Phalanger 是 .NET Framework 的 PHP 语言编译器,它将 PHP 引入为一流的 .NET 公民。

下载次数

目录

Phalanger 是 Microsoft .NET 平台的 PHP 语言编译器。在 1.0 版本中,主要目标是能够编译任何现有的 PHP 应用程序,但在 Phalanger 的 2.0 版本中,PHP 成为了 .NET 的一流公民。在本文中,我将从 .NET 开发人员的角度描述 Phalanger 项目中最重要的和最有趣的功能。这意味着我将更多地关注 .NET、互操作性和使用其他 .NET 语言扩展 PHP 应用程序的可能性,而不是 Phalanger 对运行现有 PHP 应用程序的支持。

  1. Phalanger 概述 - 项目和许可介绍。
  2. 在 Phalanger 上运行 PHP 应用程序 - 可以在 Phalanger 上运行大多数现有 PHP 应用程序,无需(或只需少量)修改源代码。本节有两个有趣的主题——首先是配置,由于基于分层配置文件结构 (2.1 节) 的 ASP.NET 配置模型,Phalanger 中的配置更加灵活;其次我想写的是对扩展的支持 (2.2 节)。
  3. PHP 作为一流的 .NET 公民 - 在本节中,我将展示为什么 PHP 对 .NET 开发人员来说可能是一种有趣的语言,我们将了解 Phalanger 中可用的两种编译模式 (3.1 节)。作为真正的一流公民,Phalanger 也存在于 Visual Studio 2005 中 (3.1 节),我们将简要介绍 PHP/CLR 语言扩展,它使得在 PHP 中使用几乎所有 .NET 对象成为可能 (3.3 节)。
  4. Phalanger 传统编译模式 允许您使用 .NET 增强现有 PHP 应用程序,但同时保持 PHP 代码结构和部署模型——您可以使用现有 PHP 应用程序并使用 .NET 功能,例如在仅一个类或函数中 (4.1 节)。这对于需要修改 PHP 应用程序的 .NET 开发人员非常有用。
  5. Phalanger 纯粹编译模式 另一方面,它允许您将 PHP 作为所有现代 .NET 项目的语言,通过修改 PHP 使其更像 C# 或 VB.NET。在此模式下,用 PHP 编写的对象可以轻松地在 C# 中使用 (5.1 节),并且您可以编写 Windows Forms 应用程序 (5.2 节) 以及 ASP.NET 2.0 应用程序 (5.3 节)。
  6. 其他说明 - 在最后一节中,我们将介绍 Phalanger 对 MONO 的支持 (6.1 节)。您还可以在此处找到一些基准测试 (6.2 节),这是使用 Phalanger 的另一个重要原因。
  7. 作者与历史 - 描述项目历史并列出 Phalanger 项目的所有作者。

Phalanger 概述

正如我之前所说,Phalanger 的 1.0 版本只专注于为 .NET 框架编译现有的 PHP 应用程序。2.0 版本增加了实现 PHP 和 .NET 世界互操作性的目标。这意味着在 2.0 版本中,可以直接从 PHP 代码中使用大多数 .NET 对象。为了实现这一点,Phalanger 以几种重要方式扩展了 PHP 语法(但它仍然完全向后兼容)。语言扩展被称为 PHP/CLR。我稍后会写到这些,但简而言之,它允许您从 PHP 中使用泛型和自定义属性等 .NET 功能,并且还可以使用类似 C# 的编译模式(其中应用程序由类而不是脚本组成)。Phalanger 在 2.0 版本中成为开源项目,并根据 Microsoft Shared Source Permissive License 发布(它允许商业使用、修改和重新分发,详见许可)。您可以在 Phalanger 主页 找到有关项目的更多信息,其中包含 Phalanger wiki、文章和新闻,或者在 CodePlex 上的 Phalanger 主页,您可以在其中找到最新版本、源代码、讨论和问题跟踪器。Phalanger 足够稳定,可以在企业应用程序中使用,Skilldrive 公司正在使用 Phalanger。

在 Phalanger 上运行 PHP 应用程序

首先,我想谈谈在 .NET Framework 上使用 Phalanger 运行现有 PHP 应用程序的可能性。我们网站 (http://www.php-compiler.net/) 就是一个这样的例子,它基于一个用 PHP 编写的优秀 wiki,名为 DokuWiki [^]。与 Phalanger 配合良好的应用程序列表仍在不断增长,目前包括 PhpBB(论坛系统)、PhpMyAdmin(MySQL 管理工具)和 DokuWiki。

Phalanger 配置

IIS properties

IIS configuration

(点击图片查看大图)

如果您想配置现有应用程序以使用 Phalanger,您需要遵循以下简单步骤:

  • 安装 Phalanger - 这会将几个程序集注册到 GAC 中,并在 machine.config 中注册 phpNet 配置节。它还会安装和配置一些示例应用程序。
  • 配置 IIS/Apache - 我稍后会描述 Apache 和 Mono 配置,对于 IIS,您需要将 php 扩展映射到 ASP.NET 请求处理程序。在 *管理工具* 中选择 *Internet 信息服务*,选择您的应用程序文件夹或将其添加为虚拟目录。选择您的 PHP 应用程序所在目录的属性(图 #1),点击 *配置* 按钮,并在对话框中添加 .php 扩展到可执行文件 C:\WINDOWS\Microsoft.Net\Framework\v2.0.50727\aspnet_isapi.dll 的映射。
  • 添加 web.config 文件 - 将 .php 扩展映射到 ASP.NET 处理程序后,您需要将 web.config 添加到应用程序目录。web.config 告诉 ASP.NET 将目录作为 ASP.NET 应用程序处理,但大多数设置都在全局 machine.config 文件中配置。以下片段显示了默认配置文件,它只将 .php 扩展映射到 Phalanger 处理程序
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.web>
        <httpHandlers>
          <add verb="*" path="*.php" type="PHP.Core.PageFactory,
              PhpNetCore, Version=2.0.0.0, Culture=neutral,
              PublicKeyToken=0a8e8c4c76728c71" />
        </httpHandlers>
      </system.web>
    </configuration>

    您也可以通过在 C:\WINDOWS\Microsoft.Net\Framework\v2.0.50727\CONFIG 目录下的根 ASP.NET web.config 中添加 add 元素来全局配置整个服务器。

PHP 扩展支持

为了使 Phalanger 尽可能与标准 PHP 实现兼容,我们提供了使用原生 PHP4 扩展的方法。由于此机制,大多数流行的 PHP 扩展,如 pdfimagecalendar 都可以在 Phalanger 中使用。在内部,Phalanger 的这一部分是用(不安全的)C++/CLI 编写的,并且仅在 Windows 平台上可用。原生扩展可以在单独的进程中加载,这是安全的,因为原生扩展的失败不会影响应用程序的其余部分,但它也更慢。第二种选择是在同一个进程中加载原生扩展,这不安全,但速度更快。

让我们从一个非常简单的例子开始,它打印公历月份的名称。它使用函数 cal_info,该函数返回有关日历的信息。此函数是 calendar 扩展的一部分

<ol>
<?php
  $nf = cal_info(CAL_GREGORIAN);
  foreach($nf["months"] as $m)
    echo "<li>$m</li>";
?>
</ol>

如您所见,PHP 源代码中没有出现任何特殊内容。要告诉 Phalanger 您要使用哪些本机扩展,您必须将其包含在配置文件的 classLibrary 部分中

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <!-- http handler as in perevious sample -->
  </system.web>
  <phpNet>
    <classLibrary>
      <add assembly="php_calendar.mng, Version=2.0.0.0, 
        Culture=neutral, PublicKeyToken=4ef6ed87c53048a3" 
        section="calendar" />
    </classLibrary>
  </phpNet>
</configuration>

另外请注意,您需要将托管包装器安装到 GAC。托管包装器是包含 Phalanger 用于调用扩展的接口的库。Phalanger 安装程序会自动执行此操作,但如果您想手动执行,可以在 Wrappers 目录中找到托管包装器。日历扩展的包装器名称是 php_calendar.mng.dll

PHP 作为一流的 .NET 公民

借助 Phalanger 项目,您可以使用 PHP 作为开发几乎任何 .NET 项目的语言。您还可以将使用 .NET 的代码添加到现有 PHP 应用程序中(该应用程序使用 PHP 脚本模型),但这将在稍后讨论(5.1 节)。为什么要使用 PHP?有几个非常重要的原因——首先,存在大量现有 PHP 代码,它们运行良好,并且在 .NET 世界中没有等效项;此外,许多了解 PHP 的人可能想尝试 .NET,但不想学习另一种语言或只是更喜欢 PHP。PHP 本身是一种动态语言,这意味着它可以非常简单,并且更容易解决其中的一些任务。您还可以很好地使用它来组合用其他语言编写的更多组件,因为动态语言非常适合此目的。我最喜欢的示例,展示了 PHP 语言的强大功能,是使用 SimpleXML 扩展处理 XML

<?php
  // load the RSS feed
  $xml = simplexml_load_file
    ("http://www.nytimes.com/services/xml/rss/
      nyt/HomePage.xml");

  // print the contents of 'title' element 
  echo $xml->rss->channel->title."\n";

  // print latest posts
  echo "Latest posts:\n";
  foreach($xml->rss->channel->xpath("item") as 
          $item) 
  {
    echo "  - ".$item->title."\n";
  }
?>

我认为代码非常不言自明。这里的动态语言“技巧”是使用标准成员表示法(PHP 中的 -> 运算符)来访问子元素,例如在读取 RSS 提要名称时使用 $xml->rss->channel->title。您无需定义 XML 文档的任何结构,代码非常直观。介绍有点长,但我想展示为什么 PHP 作为一种语言很有趣,以及为什么它对 .NET 语言家族来说是一个有价值的补充。

Visual Studio 支持

作为一流的 .NET 语言,您还可以在 Visual Studio 中创建 PHP 应用程序。Phalanger 扩展不是核心 Phalanger 安装程序的一部分,因此您需要单独下载它。由于 Visual Studio 的限制,无法将 Phalanger 扩展与 VS Express 版本一起使用。

Phalanger in Visual Studio

(点击图片查看完整截图 [^])

以下概述描述了您可以使用 Visual Studio 中的 Phalanger 开发哪些项目

  • PHP Web 应用程序 - 创建新的 PHP Web 应用程序,该应用程序与标准 PHP 解释器兼容(除非您使用某些 Phalanger 特有的功能)。
  • 传统控制台/WinForms 应用程序 - 生成使用传统 PHP 模式编写的控制台或 Windows Forms 应用程序,在该模式中您可以使用全局代码、包含文件和其他 PHP 功能。
  • 控制台/WinForms 应用程序 - 创建将使用纯 Phalanger 模式编译的控制台或 Windows Forms 应用程序 - 在此模式下,PHP 编译器更像其他 .NET 语言(例如 C#),并且不允许包含或全局代码。
  • 类库 - 在此项目中,您可以使用 PHP 语言(纯编译模式)创建 .NET 库,该库可以轻松地从其他 .NET 语言中使用
  • Web 项目(ASP.NET 网站,...) - 在这三个项目中,您可以创建使用 aspx 和代码隐藏文件的标准 ASP.NET 应用程序,但您使用 PHP 作为编写内联和代码隐藏代码的语言。

Phalanger .NET projects

“新建 - 项目...”中的 Phalanger 项目

Phalanger web projects

“新建 - 网站...”中的 Phalanger 项目

由于 .NET 强大的调试支持,也可以在 Visual Studio 中调试 PHP 应用程序(如截图所示)。在调试期间,您还可以使用监视和局部变量,但由于 PHP 语言的动态特性,您可能需要在哈希表中搜索变量值,其中值通常存储在那里。Visual Studio 支持仍处于开发的早期阶段,我们仍在努力改进编辑和调试体验。

纯模式与传统模式

在标准 PHP 中,脚本是顺序执行的。这意味着应用程序执行从一个主脚本开始,此文件中的全局代码逐行运行,最终可以使用 includerequire 指令包含其他脚本。如果您想将此逻辑与 Phalanger 一起使用,您可以使用 *传统* 编译模式(它是默认模式)。在传统模式下,脚本包含全局代码,如果您想使用在另一个脚本中声明的类或函数,您必须首先包含它。此模式与标准 PHP 解释器完全兼容,因此您可以使用 Phalanger 和 PHP 运行应用程序。

另一方面,*纯* 模式更类似于 C# 中已知的逻辑。在此模式下,脚本在编译期间合并在一起(不允许包含),并且每个脚本只能包含类和函数的顶级声明。入口点是选定主类中名为 Main 的静态函数。纯模式对于 .NET 开发人员非常有趣,因为它允许您将 PHP 语言用于例如依赖于此对象模型的 ASP.NET 2.0。标准 PHP 解释器没有与此模式等效的功能。

PHP/CLR 语言扩展

让我们看看 PHP 语言扩展,称为 PHP/CLR,它允许您在 Phalanger 中使用 .NET 对象。请注意,这些扩展可以在传统模式和纯模式下使用,这意味着您可以在现有 PHP 应用程序中使用 .NET 对象,稍后您将看到。

CLR 对象和命名空间

由于所有 .NET 库都以命名空间结构组织,因此当您想要使用 .NET 库时,您必须能够在 PHP 中使用命名空间。Phalanger 中可用的命名空间基于 PHP 6 提案,它允许您将 PHP 代码组织到命名空间中,并使用现有的 CLR 命名空间。在 PHP 6 提案中,用于访问命名空间的运算符是 :::,因此您必须使用 System:::Web 而不是 System.Web。我们使用了此语法,因为 Phalanger 需要知道您是指类还是命名空间(与 C# 不同,在 C# 中可以在编译期间检查此项)。以下示例显示了如何在 PHP 中使用 System 命名空间中的简单 Random

<?php 
  $rnd = new System:::Random;
  echoNext(10);
?>

您也可以使用导入命名空间语句来简化代码
更简洁

<?php
  import namespace System;

  $rnd = new Random;
  echoNext(10);
?>

现在您知道如何使用现有命名空间中的类(甚至可以使用 CLR 命名空间中的 CLR 类!)。要运行此示例,您需要将以下内容添加到应用程序配置文件中

<?xml version="1.0" encoding="utf-8"?>
<configuration><phpNet>
  <compiler>
    <set name="LanguageFeatures">
      <add value="PhpClr" />
    </set>
  </compiler>
  <classLibrary>
    <add assembly="mscorlib" />
    <add assembly="System, Version=2.0.0.0, 
    Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  </classLibrary>
</phpNet></configuration>

这告诉 Phalanger 两件事:首先,您希望启用 PHP/CLR 语言扩展(包括命名空间等);其次,您正在使用 CLR 类,并且希望引用指定的 .NET 程序集。要构建此应用程序,您可以创建一个新的 *传统控制台应用程序*(来自 Phalanger 项目模板)——请注意,我们使用了传统应用程序,这意味着可以在脚本文件中使用全局代码。第二种构建选项是使用 Phalanger 命令行中的以下命令(这也会生成控制台应用程序)

phpc /lang:CLR program.php

标识符

下一个对标准 PHP 语法有用的扩展允许您编写与 PHP 关键字冲突的标识符。这类似于 C# 中的 @something 语法。我们必须使其成为可能,因为 .NET Framework 中的一些重要类(例如泛型 List)的名称与 PHP 关键字冲突。例如,如果您有一个名为 function 的函数(这是 C# 编译器可接受的名称),您可以使用以下语法在 Phalanger 中调用它

<?php
  // Calls function called 'function'
  i'function'("Hello world!");
?>  

Generics

在这一段中,我提到了您可以使用 .NET Framework 中的泛型类,例如 List。在 Phalanger 中引入泛型的主要原因是在使用 .NET 泛型类时能够指定类型,但泛型类型对 PHP 开发人员也可能有用——例如,您可以使用泛型集合并限制可以插入到集合中的类型(但是,正如您对动态语言的期望,这在运行时进行检查)。Phalanger 还允许您编写自己的泛型类或函数,并添加一些 PHP 特有的可能性。第一个简单示例将展示如何在 .NET 中使用 Dictionary

<?php
  import namespace System:::Collections:::Generic;

  // Add values to dictionary
  $d = new Dictionary<:string,string:>;
  $d->Add("cz", "Ahoj svete!");
  $d->Add("en", "Hello world!");
  $d->Add("fr", "Salut le Monde!");
  $d->Add("de", "Hallo Welt!");
  
  // OK - Implicit int to string conversion
  $d->Add(0, "Something less evil!");
  
  // Invalid - can't add instance of Random as a key 
  // $d->Add(new System:::Random, 
     "Something more evil!");
    
  // Read value
  $out = "";
  if ($d->TryGetValue("cz",$out))
    echo $out;
?>

还有一件有趣的事情需要注意——因为我们希望保持 PHP 语言的动态性,所以我们也允许使用变量指定泛型类型参数,例如,如果您的变量 $t 包含 "string",您可以使用以下表达式创建 Dictionary 的新实例:new Dictionary<:$t, $t:>;

正如你所期望的,你也可以使用 PHP/CLR 扩展在 PHP 中声明接受泛型参数的新泛型类。因为 PHP 不像 C# 那样要求严格的类型检查,所以你可以使用默认值来指定当调用函数或实例化类时未指定类型时将使用的默认类型。以下代码显示了一些示例

// Inherited dictionary
class PhpDict extends Dictionary
{ /* ... */ }

// Inherited list with default type parameter 
class PhpListobject extends i’List’
{ /* ... */ }

// Function with one type parameter
function PhpFunc 
{ /* ... */ }

自定义属性

.NET 中另一个非常重要的概念,在 PHP 语言中没有很好的等价物,就是属性,您可以使用它们来注释源代码并向您的类、方法等添加一些额外信息。属性用于许多重要的场景——例如,如果您正在创建 Web 服务,您可以使用 WebServiceAttribute 来注释类,并使用 WebMethodAttribute 来注释所有您想公开的方法。

目前对属性的支持有些受限(如果你想用 PHP 编写 Web 服务,你必须等待下一个版本),所以我将用一个更简单的例子来演示 PHP 中的属性使用。在这个例子中,我将使用 DescriptionAttribute 为 PHP 函数添加注释(可以在运行时访问)

<?php
  import namespace System:::ComponentModel;

  [Description("Description for the function foo")]
  function foo($a)
  {
    echo $a;
  }
?>

如您所见,语法与 C# 相同。我们还计划支持 C# 风格的命名参数,但正如我所提到的,这仍在开发中(如果您正在考虑使用 Phalanger 并且需要使用此功能,请告诉我们,以便我们可以专注于用户需求!)。

我们还引入了两个伪属性,编译器会以不同方式处理它们——您可以使用它们来指示编译器,生成的代码应该是什么样子。第一个属性是 [AppStatic],它指示编译器将字段编译为在整个应用程序中共享的静态字段(请注意,PHP 中其他静态字段仅在每次请求中共享,这对应于 C# 中的线程静态字段)。[Export] 属性控制编译类的结构,使其可以从其他 .NET 语言(如 C#)访问。我稍后会讨论这个属性。以下示例显示了 [AppStatic] 属性的使用。如果您将以下代码放入由 Phalanger 作为 Web 应用程序处理的脚本中,它将计数请求(直到应用程序被 IIS 重启)

<?
  class Counter
  { 
    [AppStatic]
    public static $N;
  }

  // display and increment counter value
  echo Counter::$N;
  Counter::$N++;
?>

其他扩展

还有一些其他扩展我们不会详细讨论,所以我只简要概述一下 PHP/CLR 中可用的这些功能。我们添加了对分部类的支持(这仅在纯模式下有意义),因为这对于允许与 ASP.NET 和其他在 .NET 2.0 中生成部分源代码的技术一起工作很重要。我们还在努力添加属性(带有 getter 和 setter 方法),因为这在从 CLR 类继承时很有用。目前,您只能通过创建具有相同名称的类成员(这是 PHP 中与 .NET 字段等效的)来覆盖继承的属性。最后,我们还宣布我们正在努力支持 LINQ 项目 [^] 作为 PHP/CLR 的一部分,以使 PHP 中的数据访问更加容易。

Phalanger 传统编译模式

您已经看过一些演示在 PHP 中使用 .NET 对象的示例。大多数以前的示例(您可以在附加的 zip 文件中获取完整版本)都使用了传统编译模式,这对于简单的任务来说更容易使用。从之前的示例中可以看出,您可以将使用 .NET CLR 对象的代码“注入”到任何 PHP 应用程序中,包括复杂的 PHP 应用程序,因为无论您使用传统模式(与 PHP 兼容)还是纯模式(仅在 Phalanger 中),这都无关紧要。由于这种可能性,您可以例如从 PHP 应用程序中使用用 C# 编写的数据访问层来向用户呈现内容!

从 PHP 使用 .Net 对象

让我们看一个示例应用程序(完整源代码是所附演示之一),它使用分层架构,数据访问层/业务逻辑用 C# 编写,表示层用 PHP 编写。该应用程序非常简单,它使用 SQL Server Northwind 数据库中的产品和类别。数据层包含表示产品和类别的类,具有多个属性,以及用于从数据库读取数据的 Data 类。类的结构如下

Data layer diagram

Data 类包含公共方法,用于返回所有类别(GetCategories)、由其 ID 指定的类别(GetCategory)、类别中的所有产品(GetProducts)以及由其 ID 指定的一个产品(GetProduct)。所有数据访问方法都返回泛型 List,其类型参数设置为 ProductCategory。例如,读取类别的方法如下所示

// Returns all categories
public List<Category> GetCategories() 
{
  List<Category> ret = new List<Category>();
  using(SqlConnection cn = new SqlConnection(connStr))
  {
    // Select all categories from database
    cn.Open();
    SqlCommand cmd = new SqlCommand
      ("SELECT [CategoryID], [CategoryName] FROM 
               [Categories]", cn);
    using (SqlDataReader reader = cmd.ExecuteReader())
    {
      // Add all categories to the list
      while (reader.Read())
      {
        ret.Add(new Category((int)reader["CategoryID"], 
          (string)reader["CategoryName"], GetTheme(
          (int)reader["CategoryID"])));
      }
    }
  }
  return ret;
}  

现在我们有了数据访问层,我们可以看看如何在 PHP 中使用它。首先,您需要创建一个 Visual Studio 项目(当然,您也可以直接创建一个映射到 IIS 的目录并将所有文件放入其中)——在菜单中选择“文件”->“新建项目”,然后在 Phalanger 类别中选择“PHP Web 应用程序”。

最困难的部分可能是正确配置 Phalanger。您已经看到了如何在 web.config 中配置 HTTP 处理程序,如何启用 PHP/CLR 语言扩展,以及如何添加对要在 PHP 代码中使用的程序集的引用,因此您知道如何正确完成此操作。web.config 文件如下所示

<?xml version="1.0"?>
<configuration>
  <!-- configure ASP.NET -->
  <system.web>
    <httpHandlers>
      <add verb="*" path="*.php" type="PHP.Core
      .PageFactory, PhpNetCore, 
        Version=2.0.0.0, Culture=neutral, PublicKeyToken=
        0a8e8c4c76728c71" />
    </httpHandlers>
  </system.web>
  <!-- configure Phalanger -->
  <phpNet>
    <compiler>
      <set name="LanguageFeatures">
        <add value="PhpClr" />
      </set>
    </compiler>
    <classLibrary>
      <add assembly="mscorlib" />
      <add assembly="DemoDataLayer" />
    </classLibrary>
  </phpNet>
</configuration>

您可以看到,在配置文件的 classLibrary 部分中,我们引用了 mscorlib 程序集(包含基本的 .NET 类)和 DemoDataLayer 程序集,后者是包含我前面描述的 C# 代码的程序集。此程序集必须放置在 Web 应用程序的 bin 目录中,以便 Phalanger 可以引用它,或者可以使用强名称密钥签名并放置在 GAC 中(在这种情况下,您必须在配置文件中包含完整的程序集名称)。

现在我们可以编写 PHP 脚本,它将在项目主页上显示类别列表

<?
  // load data layer
  import namespace DemoDataLayer;
  $dl = newGetCategories();
?>
<html><body>
  <h1>Categories</h1>
  <ul>
  <?
    // Write link to detail for every category
    foreach($categories as $c) { 
  ?>
    <li><a href="products.php?id=<? echo $c->
    ID ?>"><? echo $c->Name ?></a><
    /li>
  <? } ?> 
  </ul>
</body></html>

第一行包含 import namespace 指令,使得无需每次都指定完整命名空间即可使用数据访问层中的类。之后,创建数据访问层中 Data 类的实例,并使用 GetCategories 方法从数据库中填充所有类别列表,存储在 $categories 变量中。稍后,当我们需要打印所有类别时,我们可以使用标准的 PHP foreach 构造来枚举返回列表中的所有项目。正如您所期望的,您可以使用 PHP 语法读取在 C# 中声明的所有属性,因此如果您想打印类别名称,只需写入 echo $c->Name

更完整的示例源代码可以在附加的演示中找到。将用 C# 编写的数据访问层与用 PHP 编写的表示层结合起来非常强大,因为它在您需要时提供了 C# 的安全性,并在您受益时提供了 PHP 的简洁性和灵活性。

Phalanger 纯编译模式

在介绍中,我写了关于传统模式和纯模式之间的区别。如果您想在不修改的情况下使用现有 PHP 源代码(为标准解释器开发),则必须使用传统模式,但当您想开发新的 PHP 项目时,可以考虑使用纯模式。如果您正在开发基于 ASP.NET 2.0 的应用程序,则需要纯模式,也建议用于开发 Windows Forms 应用程序。它还使您能够将用 PHP 编写的对象提供给其他 .NET 语言,因为您可以指示编译器生成可轻松从 C# 使用的重载。

使用 PHP 对象与其他 .Net 语言

我将从一个示例开始,它展示了如何编写可从 C# 访问的对象。在编译 PHP 对象时,Phalanger 生成的类不包含您期望的带参数的方法——这是因为 PHP 是动态语言,内部有一个相当复杂的方法调用机制。例如,如果您编译一个名为 Test 的类,其中包含方法 Foo($arg),则 Phalanger 生成的方法签名如下(Phalanger 甚至生成了两种不同的方法!)

// First generated overload
public virtual object Foo(ScriptContext ctx);
// Second (static) method
public static object Foo(object instance, PhpStack stack);

我不想用前面的例子吓唬你,我只是想解释为什么从 C# 调用 PHP 对象不会像看起来那么简单,如果我们没有实现特殊选项来简化它!这个选项叫做 [Export] 属性,你可以用它来告诉 Phalanger 你想从另一个 .NET 语言中使用类。以下示例显示了一个用 PHP 编写的类,它使用 PHP 的能力通过名称创建对象实例或调用方法——这在 PHP 中被称为间接方法访问。例如,如果你有一个存储在变量 $obj 中的对象和一个存储在变量 $method 中的方法名,你可以使用 $obj->$method() 通过名称调用方法。在 C# 中,你必须使用反射来实现这一点。这个类(我称之为 DynamicObject)有一个构造函数,它接受要创建的类型名称,以及一个 Call 方法,它调用对象的成员方法

<?
  namespace PhpClassLib 
  {
    // Export tells compiler to generate methods that
    // can be used from other .NET languages
    [Export]
    class DynamicObject 
    {
      var $obj;
      
      // Creates object by class name
      function __construct($obj) {
        $this->obj = new $obj;
      }

      // Dynamically calls method
      function Call($name) {
        return $this->obj->$name();
      }
    }
  }
?>

编译代码后(无论是使用 Visual Studio 创建新的 Phalanger 类库,还是使用命令行 phpc /target:dll /pure PhpClassLib.cs),您就可以从 C# 项目中引用编译后的 dll。如果您在对象浏览器中打开该库,您将看到 Phalanger 还添加了以下方法(C# 语法)

// Constructor 
public DynamicObject(object obj);
// Call method
public object Call(object name);

显然,在没有静态类型检查的语言中,函数的参数和返回类型必须是 object 类型。以下示例演示了如何从 C# 项目中使用用 PHP 编写的类,动态创建 Random 类的实例并调用其 Next() 方法

// Note that the name passed to the object uses PHP namespace notation
DynamicObject obj = new DynamicObject("System:::Random");
int n = (int)obj.Call("Next");
Console.WriteLine("Returned = {0}", n);

而且您无需使用 System.Reflection 命名空间中的任何类!这只是 [Export] 功能的众多有趣用途之一。如果您想使用大量优秀的 PHP 开源项目中实现的一些功能,也可以使用它。项目源代码需要稍作修改(以确保它不依赖于包含文件,并且不包含任何全局代码),然后它就可以使用纯 Phalanger 编译模式编译为 .NET 程序集,并且您可以从任何 .NET 语言中使用它。

Windows Forms 应用程序

Web browser in PHP

点击图片查看原始截图。

您还可以使用 Phalanger 开发 Windows Forms 应用程序。WinForms 是一个有趣的话题,因为它需要创建事件处理程序。我们对事件处理程序的实现不需要对语言进行任何修改,但它是允许 PHP 与 .NET 类互操作的重要组成部分。

许多人使用 PHP 编写 Web 应用程序,但编写 Web 浏览器呢?这并不困难,但我将从典型的“Hello world!”示例开始。您可以在附加的演示中找到 Web 浏览器示例(是的,它基于 IE ActiveX 控件,不,它目前不支持选项卡式浏览 ;-))。

我将示例做得尽可能简单,所以我将 Main 静态函数(入口点)放在 MainForm 类中,该类代表窗口。该类包含一个构造函数,它使用强大的 CreateCtrl 函数(我稍后会谈到它)创建控件,它还包含一个 btnClicked 函数,用于处理按钮的 Click 事件。

<?
import namespace System;
import namespace System:::Drawing;
import namespace System:::Windows:::Forms;

class MainForm extends System:::Windows:::Forms:::Form
{
  function __construct() {
    // Form properties
    $this->ClientSize = new Size(400, 400);
    $this->Text = 'PHP Hello world!';

    // Create button
    $btn = CreateCtrl("Button", array(
      "Left" => 10, "Top" => 10, "Width" => 380, 
      "Height" => 380, "Text" => "Click me!"Add($btn);  
    
    // Set event handler
    $btn->Click->Add
      (new EventHandler(array($this, "btnClicked")));
  }
    
  // Show message box when button is clicked
  function btnClicked() {
    MessageBox::Show("Hello world!");
  }

  // Entry-point function
  static function Main() {
    // Enable XP visual styles and start with MainForm
    Application::EnableVisualStyles();
    Application::Run(new MainForm());
  }
}
?>

在前面的代码中,我们使用了 CreateCtrl 函数,我认为它非常有趣!它是一个不能在静态类型语言中轻松编写的函数。它接受您要创建的控件的名称,以及您要分配给指定名称属性的名称和值的关联数组。它不是标准库的一部分,因此您必须自己编写。它使用了我之前提到的对属性的间接访问——这意味着如果我们需要设置对象 $ctrlText 属性,并且我们有一个包含字符串 "Text" 的变量 $name,我们也可以编写 $ctrl->$name = "Something"(直接访问的语法是 $ctrl->Text = "Something")。完整的函数如下所示

// Helper WinForms function
function CreateCtrl($name, $props)
{
  // Create instance of control with specified name
  $ctl = new $name;
  // Set all control properties
  foreach(array_keys($props) as$prop = $props[$prop];
  return $ctl;
}

您可能想知道当它作为 .NET 程序集编译时,它在内部是如何工作的。调用本身不使用反射,这使得它非常高效。第一次间接调用时会消耗一些 CPU 来生成一些内部数据结构,但之后它运行得非常好。这可能又是另一篇文章的主题,所以我在此不详细描述。

Asp.Net 2.0 应用程序

现在我想展示如何使用 PHP 作为开发 ASP.NET 2.0 应用程序的语言。您还可以访问我们的网站并下载众所周知的应用程序——已移植到 PHP 的 Personal Web Site StarterKit(请参阅我们的发布页面 [^])。ASP.NET 2.0 应用程序是指基于 ASP.NET 2.0 对象模型的应用程序,您可以将其与 C# 或 VB.NET 一起使用,包括代码隐藏、ASP.NET 控件、母版页、主题等。如果您想使用 ASP.NET 对象模型,但由于某种原因更喜欢 PHP 作为语言(例如,您在 PHP 方面经验丰富或想使用一些已有的 PHP 代码),这会很有用。

要创建新的 ASP.NET 2.0 Phalanger 应用程序,请在 Visual Studio 中选择 *文件* - *新建* - *网站*,在语言框中选择 PHP,然后选择“ASP.NET 网站”。该模板包含两个文件 - Default.aspx 包含 ASP.NET 标记,而 Default.aspx.php 包含代码隐藏类。让我们看下面的示例

<%@ Page Language="PHP" CodeFile="Default.aspx.php" 
  Inherits="DefaultPage" %>
<html><head runat="server">
  <title>Phalanger ASP.NET Demo</title>
</head><body>
<form id="form" runat="server">
  <!-- Demonstrates inline PHP code (use standard 
  ASP.NET <% .. %> -->
  <% 
    // Prints header with random color
    srand((double)microtime()*1000000);
    $colors = array("#800000", "#008000", 
      "#000080", "#606000", "#600060", "#006060");
    echo "<h1 style=\"color:".$colors[rand(0,6)].
      "\">Phalanger ASP.NET Demo</h1>"; 
  %>
  
  <!-- Some ASP.NET controls -->    
  Enter your name: <asp:TextBox runat="server" id="txtName" />
  <asp:Button runat="server" text="Ok" id="btnOk" 
    OnClick="btnOk_Click" /><br />
  <h2><asp:Label runat="server" id="lblOutput" 
    /></h2>
  
</form>
</body></html>

此源代码显示了 default.aspx 文件。它包含标准 ASP.NET @Page 指令,其中 Language 属性设置为 PHP,您还可以看到一些标准 ASP.NET 控件,例如 <head runat="server" />、表单、按钮、标签和文本框。源代码还显示您可以使用 PHP 作为内联代码的语言(就像您可以使用 C# 或 VB.NET 一样),但是您必须使用 ASP.NET 样式标签来嵌入代码,因为整个页面由 ASP.NET 处理,Phalanger 仅用于编译。

尽管您必须使用 ASP.NET 样式标签,但您可以使用任何 PHP 函数,因为 Phalanger 的实现是建立在 ASP.NET 框架之上的,因此大多数函数都能很好地与 ASP.NET 集成。如果您正在使用 ASP.NET,Phalanger 会以纯模式编译源代码,这意味着您不能使用 include,但其他函数在内联代码中运行良好。如果您想从现有 PHP 应用程序复制一些功能,这是一个有趣的功能。现在让我们看看代码隐藏类

<?
  import namespace System;

  // Partial code-behind class
  partial class DefaultPage extends System::Web::UI::Page
  {
    // Called when user clicks on the button
    function btnOk_Click($sender,$e) {
      // lblOutput and txtName are declared in 
      // second part of partial class
      $this->lblOutput->Text = 
        "Hello ".$this->txtName->Text."!";
    }
  }
?>

如您所见,这里我们需要使用 partial 类,因为 ASP.NET 会自动生成类的第二部分。类的第二部分包含页面上每个服务器控件的类成员/字段(如 lblOutput),这使得可以在代码隐藏中访问它们。有关将 PHP 与 ASP.NET 结合使用的更多示例,请参阅前面提到的 Personal Web Site StarterKit。

其他备注

Mono 支持

Phalanger 2.0 版本支持 Mono 平台(在第一个版本中不可能,因为 Phalanger 的一部分是用 Managed C++ 编写的)。最大的限制是,您不能使用仅在 Windows 上支持的本地扩展。在 Mono 下还有一些问题,但我们正在努力解决这些问题。

Mono 提供了许多有趣的场景——您可以将 Phalanger 与 Apache Web 服务器和 mod-mono 扩展结合使用,让您运行由 Phalanger 驱动的 PHP Web 应用程序,或者您可以使用 PHP 开发可移植的 Mono 应用程序。您还可以从 Phalanger 中使用任何 Mono 库,因此您可以例如使用 Gtk# 库开发 GUI 应用程序。

基准测试

由于 Phalanger 将 PHP 应用程序编译为 CLR,因此与标准 PHP 解释器相比,应用程序的执行速度更快。效率取决于 Phalanger 运行时和 CLR 本身。由于在 2.0 版本中添加了一些允许更好地编译动态语言的功能,Phalanger 2.0 比 1.0 版本更快。

我们尝试了两种性能测试——微基准测试显示了 Phalanger 和 PHP 解释器上最基本的关键操作(如调用函数、按名称调用函数、数组访问等)的执行速度。结果非常好——在总共 25 项测试中,Phalanger 在 19 项测试中更快,其余 6 项测试的差异非常小。第二次测试比较了运行在 PHP 和 Phalanger 上的 PhpBB 论坛应用程序的性能。结果显示,Phalanger 每秒能够处理的页面几乎是两倍!

作者与历史

Phalanger 项目始于 2003 年初,是布拉格查理大学数学物理学院的一项学生项目,代号 PHP.NET。第一版开发期间的项目成员包括 Tomas Matousek、Ladislav Prosek、Vaclav Novak、Pavel Novak、Jan Benda 和 Martin Maly。该项目的目标是找出在 .NET Framework 上编译动态语言(特别是 PHP 语言)是否可行,以及与解释器和各种加速工具相比,是否能带来任何性能提升。成功答辩后,项目团队成员继续以志愿者身份为 Phalanger 贡献了一年多。目标是提供一个功能完善且与 PHP 4 和 5 版本兼容的编译器和运行时,最终的 1.0 版本于 2006 年 2 月发布。

2006年3月,Tomas Matousek 和 Ladislav Prosek 开始设计并实现 Phalanger 的第二个版本。基于他们在雷德蒙德编译器实验室对 Phalanger 的演示,他们获得了微软公司的慷慨支持。2.0 版本基于前一个版本,但有些部分完全重新设计和重写,并引入了新的 PHP/CLR 语言扩展。2006年10月,Tomas 和 Ladislav 搬到雷德蒙德,开始在微软的 CLR 团队工作,Tomas Petricek 成为新的项目负责人,并与新的开发团队一起致力于完成 2.0 版本的发布,以及发布更多关于 Phalanger 的文章。

链接和参考文献

© . All rights reserved.