使用
Eclipse PDT 创建支持富应用程序的 PHP 服务
Mate 是一个轻量级的事件驱动框架,该框架支持以 Model-View-Controller
(MVC) 模式构建用户界面(UI)和服务。本文学习如何联合使用 Eclipse PHP 开发工具(PDT)和
Flex 软件开发工具(SDK)构建使用 Mate 框架的应用程序。本文详细讲解现有的 Mate 文档,因为它关注使用
Eclipse PDT 作为工具。
Adobe? Flash? 是另一种构建富 Internet 应用程序(RIA)的好方法。使用
Eclipse 和 Adobe Flex SDK,您可以将项目编译进 Flash Player 中运行的应用程序。Mate
是一个轻量级的事件驱动框架,它支持通过调用函数轻松调用远程对象。这些远程对象可能是用 PHP 编写的服务。
要运行本文中的示例,您需要 Eclipse PDT。本文还将介绍您需要的其他组件。
Mate 和 Flex SDK 概述
Flex SDK 允许您构建 Adobe Flex 应用程序。Flex
应用程序使用 XML 格式和 ActionScript V3.0 语言的源文件。使用编译器,您可以将 .mxml
文件和 ActionScript 文件编译为 Adobe Flex 应用程序。
可以下载两个版本的 Flex SDK:
1.Adobe Free 版包含编译应用程序的 Adobe 产品,还包含
Adobe AIR 和其他组件。Adobe Free SDK 通过 Adobe Flex SDK 许可证许可(参见
参考资料)。
2.另一个 SDK 版本是 Open Source Flex SDK 版,它遵循
Mozilla Public License (MPL) 许可。它包含一个编译器和运行本文中的示例需要的所有组件。但是,MPL
许可可能满足不了您的需要。咨询您的法律部门,验证这个许可是否适用。
另外,Adobe 提供一个称为 Flex Builder 的产品,您可以将其作为一个
Eclipse 插件或者作为一个完整的产品下载。为运行本文中的示例,请下载开源 Flex SDK。下载
ZIP 文件并将其解压缩到一个简单易记的本地位置,本文将这个位置称为 FLEX_HOME。本文将演示如何使用这个免费
SDK 设置 Eclipse,以便您可以只使用 Eclipse、PDT 和 Flex SDK 构建 RIA。
Mate 是一个帮助您构建遵循 MVC 模式的应用程序的框架。Mate
以事件驱动,因此您需要创建一些 ActionScript 类事件。然后将这些事件映射到远程对象调用,以便将代码细节置于视图和模型之外。
创建项目结构
在本文中,一个简单的时间输入应用程序 — 称为 ChronoLog —
将演示如何使用 PHP 服务构建一个 Flex UI。这个示例需要在您的 Eclipse 工作空间中建立两个项目:一个是
PHP 项目,用于服务;另一个是普通项目,它包含一个作为 Ant 构建器建立的 Ant 文件。这个 Ant
构建器用于执行 Flex 编译器,以编译用于 UI 的项目。
要创建服务 PHP 项目,从 Eclipse 菜单中选择 File >
New > Project。选择 PHP Project,然后选择 Create project
from existing source 并在文件服务器的文档根目录下选择一个位置。这将允许您快速测试所创建的
PHP 服务。
要创建 UI 项目,使用 File > New > Project
并选择 Project 创建一个空项目。
创建这个 UI 项目后,创建一个名为 src 的文件夹,并在该文件夹中创建一个文件夹
chronolog。在 chronolog 文件夹下,分别创建 events、model、views 和
maps 文件夹。这个项目的结构如下图所示。
图 1. UI 项目结构
下载并安装 Mate
Mate 下载文件是一个已编译文件,扩展名为 .swc。从相关站点下载这个文件,安装方法是将其导入这个
UI 项目中的 libs 文件夹。结果如图 2 所示。
图 2. libs 文件夹中的 Mate
.swc 文件
构建一个简单的服务
这个服务用 PHP 编写,只拥有两个类:一个是执行函数的服务类,一个是
TimeEntry UI 类映射到的 PHP 类。
编写这些服务类之前,下载并安装需要的 AMFPHP 文件。要运行这个示例,只需将 amfphp 文件夹安装到您的
PHP 项目中(如下所示)。
图 3. amfphp 文件夹
这个服务类显示在 清单 1 中。这个服务类只是在一个临时位置中以一个 XML 小文件的形式写出一些值。在您的计算机上,您也许需要修改这个位置(粗体),以便它能够被成功写出。您需要将它更改为一个能够被您的
Web 服务器读写的文件的名称。
清单 1. amfphp/services/ChronoLogManager.php 文件
<?php require_once('./vo/TimeEntry.php'); /* * A service for managing time entries. */ class ChronoLogManager {
public function saveTimeEntry($entry)
{
$myFile = "/tmp/time.xml";
$fh = fopen($myFile, 'w') or die("can't open
file");
$stringData = "<projects>";
$stringData .= "<entry project=\"$entry->project\"
" .
"time=\"$entry->time\" user=\"$entry->username\"
/>";
$stringData .= "</projects>";
fwrite($fh, $stringData);
fclose($fh);
}
}
?> |
服务方模型对象 TimeEntry.php 显示在 清单 2 中。AMFPHP 要求值对象存储在 services/vo
文件夹中,因此,这个 PHP 文件相对于 PHP 项目的根目录的相对路径名称是 amfphp/service/vo/TimeEntry.php。AMFPHP
要求类名和文件名(不包含扩展名)一致。
清单 2. amfphp/services/vo/TimeEntry.php 文件
<?php class TimeEntry { var $_explicitType = 'model.TimeEntry';
public $username;
public $project;
public $time;
}
?> |
使用类作为参数对象以代替多个参数的好处是,如果这个时间输入字段要添加更多字段,只需向这个对象添加一个新字段。但也有一个坏处,使用即时通讯框架时遇到的任何问题可能会伴随复杂的类型发生。有时,简单的类型(字符串、整数和布尔型)更容易使用,因为它们不容易出问题。
使用一个模型对象
模型对象是一个 ActionScript 类,它携带应用程序中的数据并映射到
PHP 服务层中的一个远程对象(TimeEntry.php)。您可以将这个对象作为一个方法 — 或一个方法的结果
— 的参数,而无须构建数组在 UI 和服务之间传递数据。
通常,用一种技术构建 UI,用另一种技术构建服务时,您必需编写或找到一个框架,以两种技术都理解的某种格式序列化这些类。经常使用
XML,因为通常很容易找到用现代编程语言编写的、读写 XML 的工具。
在这个应用程序中,AMFPHP 用于将 Flex 对象映射到远程 PHP
对象。Adobe Message Format (AMF) 是 Adobe 用于序列化和反序列化对象的格式。使用
AMF 的一个好处是它允许您连接这些对象,无需自己编写在 UI 和服务之间序列化和反序列化的代码。有几个框架用于通过
AMF 连接 Flex 和 PHP,其中一个来自 Zend(见 参考资料)。
清单 3 中显示了 TimeEntry 的模型对象(包含每个时间输入字段的信息)。将它存储到
UI 项目的 src/chronolog/model 文件夹中。
清单 3. src/chronolog/model/TimeEntry.as
文件
package model { [Bindable] [RemoteClass(alias='TimeEntry')] public class TimeEntry { private var _username : String; private var _project : String; private var _time : String; public function TimeEntry() { } public function get username() : String { return _username; } public function set username(value : String) : void { _username = value; } public function get project() : String { return _project; } public function set project(value : String) : void { _project = value; } public function get time() : String { return _time; } public function set time(value : String) : void { _time = value; }
}
} |
除了在这个类中声明的 RemoteObject 属性外,这个类就像您创建来支持应用程序的其他 ActionScript
类一样。
构建一个事件
Mate 是一个事件驱动的框架,因此使用 Mate 时要创建许多事件。一个 Mate 事件是一个 ActionScript
类,它从基 flash.events.Event 类扩展而来。一个字符串常量识别一个特定事件,一个事件类中可以有一个或多个常量。我使用这个功能来根据事件在业务领域中的目的对事件分组。例如,在这个
ChronoLog 示例中有一个 TimeEntryEvent 类。这个类使用常量 SAVE,这个常量表示一个事件将触发一个存储时间输入的函数。随着项目发展,这个类中可能包括更多事件,比如
GET 或 DELETE。
使用业务术语命名您的事件并将它们映射到业务函数。不要创建称为 ButtonEvent 的事件类,也不要添加称为
SAVE_CLICKED 的常量。否则,您将使用不好的命名方法,从而限制了事件的重用。如果用户改变主意且按钮改为一个链接,这种命名方法将导致那些过于紧密地绑定到
UI 的事件出现问题。
清单 4 中显示了 TimeEntryEvent ActionScript 类的一个示例。除了添加 SAVE
常量之外,构造器中的 bubbles 参数的默认值更改为 true。我为这个事件添加了一个类型为公共变量的参数
TimeEntry。
清单 4. src/chronolog/events/TimeEntryEvent.as 文件
package events { import flash.events.Event; import model.TimeEntry;
public class TimeEntryEvent extends Event
{
public static const SAVE : String = 'TimeEntryEvent_SAVE';
public var entry : TimeEntry;
public function TimeEntryEvent(type:String, bubbles:Boolean=true,
cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
} |
构建一个视图
除了 ActionScript 类,UI 项目中的其余文件都是 Flex 组件。这些组件放置在扩展名为
.mxml 的文件中。MXML 文件是格式良好的 XML 文件,所以我将它们在 Eclipse 中关联到
XML 编辑器,以利用 XML 编辑器的功能,比如格式化和标记闭合。要创建 MXML 文件,选择 File
> New > File 并给文件一个适当的名称。
对于 UI,主程序文件(main.mxml)只包含对主 EventMap 和对视图的引用。这个 main.xml
的源在清单 5 中显示,main.mxml 文件存储在这个 UI 项目的 src/chronolog 文件夹中。
清单 5. src/chronolog/main.mxml 文件
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:maps="maps.*" xmlns:views="views.*"> <mx:ViewStack> <views:DetailView id="detailView" /> </mx:ViewStack> </mx:Application> |
视图 detailView 是一个 Flex 组件,它扩展 Flex Panel 对象。这个视图的整个内容在下面显示。
清单 6. src/chronolog/views/DetailView.mxml 文件
<?xml version="1.0" encoding="utf-8"?> <mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal" width="100%" height="100%" xmlns:mate="http://mate.asfusion.com/"> <mx:Script> <![CDATA[ import com.asfusion.mate.events.ResponseEvent; import mx.controls.Alert; import model.TimeEntry; import events.TimeEntryEvent; [Bindable] public var entry : TimeEntry; private function save() : void { entry = new TimeEntry(); entry.username = 'jdoe'; entry.project = projectInput.text; entry.time = timeInput.text; } private function handleResult(event : ResponseEvent) : void { Alert.show('Success!'); } private function handleFault(event : ResponseEvent) : void { Alert.show(event.fault.toString()); } ]]> </mx:Script> <mx:HBox> <mx:FormItem label="Project Number"> <mx:TextInput id="projectInput" /> </mx:FormItem> <mx:FormItem label="Time"> <mx:TextInput id="timeInput" /> </mx:FormItem> <mx:Button label="Save" click="save()" /> </mx:HBox> </mx:Panel> |
这个视图包含两个字段和一个按钮。这些字段是项目编号和一个表示已工作时间的值。一个用户名(它也用于这个时间输入字段)是硬编码的。随着应用程序运行,用户名来自用户登录该程序时获取的数据。将这个视图作为一个
MXML 文件保存在 views 文件夹中,名为 DetailView.mxml。
构建 EventMap
一个扩展 Mate 的 EventMap 的组件用于将事件映射到动作,这是将事件链接到远程对象。另外,EventMap
可以用于使事件在结束时调用其他事件,设置本地对象上的值,以及在本地启动方法。
应特别指出的是,链接事件功能强大。通过链接事件,您可以避免对许多交互进行硬编码,使项目更适应不断变化的业务需求。例如,如果用户希望在成功保存数据后界面变为另一个视图,您可以创建一个
NavigateEvent 来实现这种更改。TimeEntry.SAVE 事件能够调用 NavigateEvent。这样,如果用户稍后改变主意,您只需更新
EventMap 来反映新的需求。
清单 7 中显示了 MainEventMap 的一个示例。它只是在 maps 文件夹中创建的一个 .mxml
文件。
清单 7. src/chronolog/maps/MainEventMap.mxml 文件
<?xml version="1.0" encoding="utf-8"?> <mate:EventMap xmlns:mate="http://mate.asfusion.com/" xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script> <![CDATA[ import events.TimeEntryEvent; ]]> </mx:Script> <mate:EventHandlers type="{TimeEntryEvent.SAVE}"> <mate:RemoteObjectInvoker destination="amfphp" source="ChronoLogManager" method="saveTimeEntry" arguments="{event.entry}"> <mate:resultHandlers> <mate:ServiceResponseAnnouncer type="result" /> </mate:resultHandlers> <mate:faultHandlers> <mate:ServiceResponseAnnouncer type="fault" /> </mate:faultHandlers> </mate:RemoteObjectInvoker> </mate:EventHandlers> </mate:EventMap> |
在 EventMap 中,RemoteObjectInvoker 包含将事件类型 TimeEntryEvent.SAVE
映射到远程对象(ChronoLogManger)上的正确方法(saveTimeEntry)的信息。RemoteObjectInvoker
输入还指定参数 —{event.entry} 记录的事件的输入字段。目标 amfphp 是在 services-config.xml
文件中指定的目标的 ID。
创建 EventMap 之后,将它包含到主程序中(如清单 8 所示)。
清单 8. main.mxml 文件中的 MainEventMap
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:maps="maps.*" xmlns:views="views.*"> <maps:MainEventMap id="eventMap" /> <mx:ViewStack> <views:DetailView id="detailView" /> </mx:ViewStack> </mx:Application> |
分发视图中的事件
使用 Mate 可以以多种方式分发事件。您可以使用在每个组件中出现的分发器。也可以使用 Mate 的
Dispatcher,这个 ChronoLog 应用程序使用的就是 Dispatcher。使用 Dispatcher
的一个好处是您可以将事件上的参数轻松设置为本地值(比如文本输入),而无需编写任何 ActionScript
代码来完成这个任务。而且,使用 Dispatcher 可以设置在事件结束时执行方法。
这个 Dispatcher 在下面的代码中用粗体显示。
清单 9. 在 src/chronolog/views/DetailView.mxml 文件中显示的
Dispatcher
<?xml version="1.0" encoding="utf-8"?> <mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal" width="100%" height="100%" xmlns:mate="http://mate.asfusion.com/"> <mx:Script> <![CDATA[ import com.asfusion.mate.events.ResponseEvent; import mx.controls.Alert; import model.TimeEntry; import events.TimeEntryEvent; [Bindable] public var entry : TimeEntry; private function save() : void { entry = new TimeEntry(); entry.username = 'jdoe'; entry.project = projectInput.text; entry.time = timeInput.text; saveDispatcher.generateEvent(); } private function handleResult(event : ResponseEvent) : void { Alert.show('Success!'); } private function handleFault(event : ResponseEvent) : void { Alert.show(event.fault.toString()); } ]]> </mx:Script> <mx:HBox> <mx:FormItem label="Project Number"> <mx:TextInput id="projectInput" /> </mx:FormItem> <mx:FormItem label="Time"> <mx:TextInput id="timeInput" /> </mx:FormItem> <mx:Button label="Save" click="save()" /> </mx:HBox> <mate:Dispatcher id="saveDispatcher" generator="{TimeEntryEvent}" type="{TimeEntryEvent.SAVE}"> <mate:eventProperties> <mate:EventProperties entry="{entry}" /> </mate:eventProperties> <mate:ServiceResponseHandler result="handleResult(event)" fault="handleFault(event)" /> </mate:Dispatcher> </mx:Panel> |
调用服务
要调用服务,您需要在 services-config.xml 文件中的 AMFPHP 服务配置文件中建立
PHP 类。services-config.xml 文件包含服务的端点 URL,这个 URL 适用于支持
AMF 的框架。services-config.xml 文件的示例如下所示。
清单 10. src/services-config.xml 文件
<?xml version="1.0" encoding="UTF-8"?> <services-config> <services> <service id="amfphp-flashremoting-service" class="flex.messaging.services.RemotingService" messageTypes="flex.messaging.messages.RemotingMessage">
<destination id="amfphp">
<channels>
<channel ref="my-amfphp" />
</channels>
<properties>
<source>*</source>
</properties>
</destination>
</service>
</services>
<channels>
<channel-definition id="my-amfphp"
class="mx.messaging.channels.AMFChannel">
<endpoint uri="http://localhost/chronolog/amfphp/gateway.php"
class="flex.messaging.endpoints.AMFEndpoint"
/>
</channel-definition>
</channels>
</services-config> |
注意,您需要将 AMFPHP 使用的网关 URL 更改为您的环境使用的值。
构建项目
所有 Flex 文件就绪以后,Ant 构建器使用 Flex SDK 自带的 mxml.jar 文件编译项目。这个
Ant 文件如下所示。
清单 11. build.xml 文件
<?xml version="1.0"?> <project name="chronologUI" basedir="." default="build"> <property name="flex.home" value="/home/ngood/bin/flex3-sdk" /> <property name="mxmlc.jar" value="${flex.home}/lib/mxmlc.jar" /> <property name="project.home" value="${basedir}" /> <property name="project.src" value="${project.home}/src" /> <property name="build.out" value="${project.home}/bin" /> <target name="build"> <java jar="${mxmlc.jar}" fork="true" failonerror="true"> <jvmarg value="-Xms256m"/> <jvmarg value="-Xmx256m"/> <arg value="-compiler.source-path=${project.src}" /> <arg value="+flexlib=${flex.home}/frameworks" /> <arg value="-compiler.library-path+=${project.home}/libs/Mate_08_8_1.swc" /> <arg value="-file-specs=${project.src}/chronolog/main.mxml" /> <arg value="-locale=en_US" /> <arg value="-services=${project.src}/services-config.xml" /> <arg value="-compiler.strict=true" /> <arg value="-warnings=false" /> <arg value="-output=${build.out}/chronolog.swf" /> </java> </target> </project> |
build.xml 文件添加到项目之后,按照以下操作方法添加一个 Ant Builder:从 Project
Explorer 选择 chronologUI 项目,然后选择 Project > Properties;从
Properties 窗口单击 New;从列表中选择 Ant Builder;在下一个界面中(见 图 4),单击
Browse Workspace 以查找并选择 build.xml 文件。这个构建器安装程序自动使用默认目标,因此您可以单击
Finish 关闭浏览器。要获取关于创建自己的项目构建器的更详细的说明,请参见 参考资料。
图 4. 创建 Ant 构建器
现在构建器已经添加到项目中,只需从 Eclipse 中的菜单中选择 Project > Build
就可以运行它。
由于这个示例使用 AMFPHP,必须有一个 AMF 目录以支持 AMFPHP 框架。关于如何建立和安装
AMFPHP 的更多信息请参见 参考资料。
运行示例
构建这个 UI 项目后,将 chronolog.swf 文件放置到您的 Web 浏览器的文档根目录之下,并在浏览器中导航到该文件(如下图所示)。
图 5. 运行完成后的应用程序
当您输入数据并单击 Save 之后,服务层将把数据写到在服务中指定的位置中的一个 XML 小文件。
结束语
使用 Flex SDK、Mate 和 PHP 可以构建富 Web 应用程序。只需对
Eclipse 项目设置进行一些微调,您就可以使用 Flex SDK 包含的 Flex Builder
在 Eclipse 中构建 Flex 应用程序了,尽管 Flex Builder 的 WYSIWYG UI
编辑和调试功能使构建 Flex 应用程序更加容易。
使用 Eclipse PDT 和 AMFPHP,您可以构建供 Flex
应用程序使用的服务。远程项目(使用 AMF)允许您快速编写从 Flex 调用远程对象上的方法的事件。 |