内容


使用 Flex SDK、Mate 和 PHP

使用 Eclipse PDT 创建支持富应用程序的 PHP 服务

Comments

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:

  • Adobe Free 版包含编译应用程序的 Adobe 产品,还包含 Adobe AIR 和其他组件。Adobe Free SDK 通过 Adobe Flex SDK 许可证许可(参见 参考资料)。
  • 另一个 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 项目结构
UI 项目结构
UI 项目结构

下载并安装 Mate

Mate 下载文件是一个已编译文件,扩展名为 .swc。从相关站点下载这个文件,安装方法是将其导入这个 UI 项目中的 libs 文件夹。结果如图 2 所示。

图 2. libs 文件夹中的 Mate .swc 文件
Mate .swc 文件

构建一个简单的服务

这个服务用 PHP 编写,只拥有两个类:一个是执行函数的服务类,一个是 TimeEntry UI 类映射到的 PHP 类。

编写这些服务类之前,下载并安装需要的 AMFPHP 文件。要运行这个示例,只需将 amfphp 文件夹安装到您的 PHP 项目中(如下所示)。

图 3. amfphp 文件夹
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,这个常量表示一个事件将触发一个存储时间输入的函数。随着项目发展,这个类中可能包括更多事件,比如 GETDELETE

使用业务术语命名您的事件并将它们映射到业务函数。不要创建称为 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 构建器
创建 Ant 构建器
创建 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 调用远程对象上的方法的事件。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Open source
ArticleID=422251
ArticleTitle=使用 Flex SDK、Mate 和 PHP
publish-date=08202009