第1章 入 门

1.1 PRADO是什么?
  PRADO是在PHP5环境下的一个基于事件驱动和基于组件的WEB应用研发框架。使用PRADO研发WEB应用,你主要需要做的工作就是使用已有的组件(原文是”实例化已定义好的组件”,熟悉面向对象编程的研发者可能比较容易理解这句话),设置组件的属性,为控件组件的各种事件编写对应的处理函数,然后把这些组织成一个个的页面。(译者注:个人倾向于将component翻译为组件,control翻译为控件,事实上大多数场合两者没有什么差别,不过为了读者能够更加容易理解原文,本文一律按此规则翻译。)这个过程和使用RAD研发工具(比如Delphi或Visual Basic)来研发视窗系统应用程式非常类似。
  下面用一个简单的例子来说明PRADO框架的工作原理。假定我们目前要使用PRADO来研发一个常见的登录页面。首先我们选择PRADO中的2个TTextBox控件和1个TButton控件来分别对应用户名输入,密码输入和提交按钮。然后把对应密码输入的TTextBox控件的TextMode属性设置为”Password”,并为TButton控件的OnClick事件指定一个处理函数login。最后把这些控件放到页面模板的合适的位置上(后面会解释什么是页面模板,实际上他和一个html页面非常相似),一个功能完善的登录页面就实现了。控件会自己处理好怎么显示数据,怎么得到用户输入的数据,怎么响应用户的操作等这些事情。例如,TTextBox控件会显示一个输入框让用户输入;用户输入的密码会被显示为*号,而研发者在代码中能通过读取TTextBox控件的Text属性来获取用户实际输入的密码;当用户点击了提交按钮的时候,login 函数将会被自动调用。这和传统的PHP编程处理方式非常不相同。使用传统的PHP编程方式,研发人员需要使用HTML标签来显示用户输入框和按钮,同时需要自己解析POST数据来获得用户的输入及判断按钮是否被点击了等等这些事情。

PRADO基于组件的编程方式使代码的重用极为方便。一些重复性的单调的工作比如表单输入的验证,页面状态的管理等都能使用PRADO已提供的组件来处理。研发者也能通过继承现有的组件或组合现有的组件来研发新的组件满足需求。另外一些新的组件即将发布。

  总来的说,PRADO 为PHP的编程人员提供了以下的有益之处:
可重用性 – 符合PRADO组件标准的代码是高度可重用的。
易于使用 – 使用PRADO组件非常简单,通常只需要设置他们的属性,编写相关的事件处理函数。
健壮性 – PRADO让研发者不再被那些令人厌烦的有非常多错误的的代码所困扰。他的代码都是有对象,方法和属性组成的;而不是Url地址和各种query参数。PHP5最新的Exception处理机制也被引入进来,让研发人员能够精确定位到错误代码的所在行数。
高效 – PRADO研发的Web应用运行非常快。PRADO实现了一种缓存机制,使用其研发的应用的运行效率能够和那些通常使用的模版引擎研发的应用的运行效率相媲美。
团队协作 – PRADO允许内容和表现方式分离。组件,尤其是页面,将内容(逻辑)和表现方式分别保存在不同的文件中。
  目前已有非常多PHP的框架,Mojavi, WACT , PHP.MVC, Phrame等等。他们主要是建立一套松散的PHP代码组织方式(比如 Model-view-controller)。其中一些更多的面向研发一个内容管理系统,并提供了诸如用户管理模块等更多的功能。
  和这些框架相比,PRADO更专注于建立一个代码重用和事件驱动的编程标准。如果你原来用Delphi或Visual Basic等这些RAD研发工具来研发视窗系统程式的话,那么你会发现使用PRADO来研发WEB应用是非常类似的,你能非常快就掌控PRADO来进行WEB应用的研发。大多数情况下,你要做的仅仅是设置组件的属性和响应组件的事件。更高层次的代码重用,比如前面所说的用户管理模块,能基于PRADO组件来实现。
  PRADO的思想最先我是从Apache Tapestry项目想到的。在设计和实现的时候, Borland Delphi 和Microsoft ASP.NET的设计思想起到了非常重要的作用 。实际上,使用过ASP.NET的人能发现PRADO中有非常多地方和他是相似的。

1.2  安装
  PRADO需要PHP5的环境,及SimpleXML模块(PHP5缺省安装后的环境下已包括了这个模块)。在视窗系统 XP和RedHat Linux环境下,已在Apache和IIS Web服务器上都通过了测试。
  PRADO框架使用一个.zip文件发布。在Linux下能使用unzip,在视窗系统下能使用Winzip来解压。解压完毕后,会在当前目录下创建一个目录prado。这个目录主要包括以下这些文件和目录:
README.html            此项目的说明文件
.html             和README.html中的内容是相同的
Copyright              此项目的版权信息
HISTORY                版本升级信息
framework/             PRADO框架的核心代码
examples/              PRADO的一些例子
docs/                  PRADO的帮助文件
  完成了解压之后,把prado目录的所有东西复制到Web服务器的根目录下。(也能把他复制到某一个子目录下,甚至更深层次的目录下,PRADO对此没有限制。如果你将他复制到了某一个子目录下,以下的URL地址需要做相应的修改。)   非常简单,安装已完成了!目前你能试一下PRADO发布包中的三个例子了:“Hello, world!”, the Hangman Game(一个猜单词的游戏), 和PRADO通讯录,他们的URL地址是:
“Hello, world!” – http://<Web-server-address>/examples/helloworld.php
the Hangman game – http://<Web-server-address>/examples/hangman.php
the PRADO phonebook – http://<Web-server-address>/examples/phonebook.php

1.3  范例: Hello, World!
  在这一节中,我们主要来说明”Hello, world!”
  这个例子,让你对使用PRADO研发有一个印象。这个例子非常的简单,只有一个页面,上面显示了一个”Click me”的按钮。当用户点击了这个按钮,这个按钮的标题会变成”Hello, world!”。
  要完成这个功能需要这些文件,
helloworld.php , 应用的主入口;
helloworld/application.spec, 应用的设置文件;
helloworld/HomePage.php, 页面的类文件;
helloworld/HomePage.tpl, 页面的模板文件.
  注意,目录helloworld应该设置为Web用户不能访问的(读的权限也不能给),因为其中的文件包括了一些重要的数据信息(比如数据库的连接口令等等)。不用担心这么设置Web用户会不能正常的访问,因为实际上他们根本不用去访问helloworld 目录中的文件。在其他使用PRADO研发的应用中你也应该这么做。
  在helloworld.php 文件中,程式主要是实例化 TApplication 这个类,(译者注:如果对实例化这个词不怎么理解,你也能这么理解,就是申明一个类型为TApplication的变量。)并开始运行应用,代码如下:
<?php require_once(’../framework/TApplication.php’); $app=TApplication::getInstance(’helloworld/application.spec’); $app->run(); ?>
  这里我们假定框架的代码位于 ../framework 目录下。
  每一个 PRADO应用都应该有这么一个入口文件。他们之间主要的差别在于 getInstance 函数的参数的不同,这个参数指明了应用的设置文件的位置。 application.spec 是个XML文件,用来设置应用级别的参数。(就好象全局变量相同,当然目前是只读的,我们正在决定是否下一版本中允许可读写的)在这个例子中,他包含了如下的代码:
<?xml version=”1.0″ encoding=”UTF-8″?> <application default-page=”HomePage” cache-path=””>    <alias name=”Pages” path=”.” />    <using namespace=”System.Web.UI.WebControls” />    <using namespace=”Pages” /> </application>
  <alias> 元素(译者注:按XML文件通常的译法,<alias>…</alias>整个翻译为元素,其中alias翻译为元素的标签,一对闭合的alias标签中的值翻译为元素值,name, path这些则翻译为属性名,他们的值翻译为属性值,参考”<标签 属性名= 属性值>元素值</标签>”)定义了路径的别名。别名System 是系统内部定义好的,用来指示框架核心代码所在的目录。能使用路径别名来定义命名空间。命名空间实际上就是个加入到PHP搜索路径 include_path中的目录。 <using>元素指明启动应用使用的命名空间。(也能在代码中调用 using() 函数来使用一个命名空间)
  <application>的default-page 属性指明了应用启动时缺省进入哪一个页面。在这里我们指定了HomePage页面,他对应了一个页面类HomePage(关于页面和页面类后面会专门对他们进行解释的)。
  HomePage.tpl 文件是页面类HomePage一个模板文件,包含如下的代码:
<html> <head> <title>Hello, world!</title> </head> <body>   <com:TForm>     <com:TButton Text=”Click me” OnClick=”clickMe” />   </com:TForm> </body> </html>
  他和HTML页面非常的相似。唯一不同的地方就是元素 <com:TButton> ,他代表了一个 TButton 组件。 TButton 组件将会显示为一个表单的按钮,按钮的标题是”Click Me”,”Click Me”是在 Text 属性中被指定的。通过设置 OnClick ,能指定页面方法 clickMe 来响应按钮的 OnClick 事件。因此,当按钮被点击的时候,方法clickMe就会被自动调用了。
HomePage.php 文件包含了定义页面类 HomePage 的代码,如下:
<?php
class HomePage extends TPage {
function clickMe($sender,$param) {
$sender->Text=”Hello, world!”;
}
}
?>
HomePage 类继承了 TPage 类。TPage 类是PRADO框架本身提供的,他是所有页面类的基类。HomePage 类只包含了一个方法clickMe ,这个方法就是刚才我们提到的页面模板中响应按钮OnClick 事件的方法clickMe  。这个方法的代码的作用是设置事件的发送者$sender(这里就是这个按钮)的Text 属性为 “Hello, world!”。我们期望当用户点击这个按钮的时候,按钮的标题改为“Hello, world!” 。   这个包含在 PRADO 发布包中的例子能通过此URL来访问:href=””>http://<Web-server-address>/examples/helloworld.php .
  你可能会说完成这么一个简单的功能有什么大惊小怪的。是的,这个功能确实非常简单,用几行php代码就能完成。不过这个例子主要是目的是为了让你感受一下PRADO编程方式,他是面向对象的,基于组件和基于事件驱动的。用类似的编程方式能实现更复杂的功能,请继续看下面几章。

第2章 用PRADO编程

  目前让我们更深入的了解一下PRADO框架。这一章我们会介绍一些框架的基本概念和怎么使用现有的PRADO组件来研发一个PRADO应用。

    2.1 组件
  根据Clemens Szyperski的说法, “软件组件是个具有特定接口的组合单元,能独立存在,参和组成其他组建。”
  在PRADO中, 组件是 TComponent 或其子类的实例。PRADO框架中已包括了TComponent类,他主要实现了组件的属性和事件机制。
属性能被认为是描述组件某一特性的公共变量,比如背景颜色,字体大小等等。属性是有类型的,比如 string, integer , 等等。属性能是只读的,也能是可读写的。
事件使代理函数能对组件中进行的一些活动作出响应,比如鼠标的点击,输入文本的变化等。
  一个组件会继承他所有父类组件的属性和方法,因为组件本身其实就是个类。
  一个完整的组件类定义包括两个文件:
一个类文件,他定义了组件类的逻辑。这个文件主要是PHP代码。这个文件的文件名必须和类的名字相同,而且以.php 作为文件后缀。
一个规格文件,他定义了组件类的属性和方法。这个文件是XML格式。如果这个组件类没有定义父类之外的新的属性和事件,那么这个文件是能省略不要的。缺省情况下,这个文件和组件的类文件在一个目录中,而且名字是相同的,唯一不同的是他们的文件名后缀(类文件的后缀是.php ,规格文件是 .spec )。值得注意的是,PRADO也允许你用不同的文件名来命名规格文件和模板文件,不过类文件名还是必须遵循和类同名,并且后缀为.php 的规则的。这时候你需要在类的常量SPECIFICATION_FILE中指定规格文件的包含路径的文件名,能使用绝对或相对路径(相对类文件所在位置)。
  注意:PRADO 是区分大小写的,组件类型,属性名称,事件名称,文件名等等这些都是区分大小写的。

  2.1.1  控件
  控件是定义了用户界面的PRADO组件。每一个控件都有一个父控件和一些子控件(注意,这里的父,子这些关系和面向对象中的父类子类的概念是完全不相同的。一个控件是另外一个控件的父控件只是说明了前者拥有后者,这个拥有关系能理解为一个目录下包含了一个子目录;而绝不是后者是前者的子类、是从前者继承而来的这个概念)。页面是最高级别的控件,他没有父控件(当然你也能认为Application是他的父控件)。一个PRADO的应用是个页面的集合,每一个页面都代表了一个层次树状结构的控件集,控件和控件之间关系是通过父子关系联系在一起的。
  这种父子关系能通过模板文件来建立。模板文件的格式和HTML是非常像的,只不过在HTML的基础上增加了一些特别的标签来定应控件。如果一个控件的标签被另外一个控件的标签所包括,那么前者就被认为是后者的子控件。静态文本也被认为是子控件。
  控件能拥有一个模板文件来描述他的界面内容。缺省情况下模板文件和类文件在同一个目录下,而且文件名是相同的,模板文件的后缀则为.tpl 。同规格文件相同,如果你需要把模板文件放在不同地方或用鳖的文件名,能通过设置类常量 TEMPLATE_FILE 的值来指明模板文件的位置。
  每一个控件都有一个ID,他能在同一级别的控件中来唯一标识控件本身。ID路径则是在控件的树状结构中,从当前控件到目标控件的所有控件ID的序列,他能用于访问一个控件。比如,在
HomePage中,有一个 Menu 控件,他拥有一个子控件 Button 。对于控件HomePage而言,能通过ID(Menu,Button)路径来访问 Button 控件。如果用PHP代码来书写,就是 $this->Menu->Button
 
  2.1.2  实例化组件
  组件有两种方式来实例化。一种方法是在一个控件的模板文件中定义他,当这个控件被创建的时候,框架会自动的去实例化这个组件。更有一种方法是在PHP代码中手动的实例化。我们先来看一下前面一种静态的创建方法,稍后再来看一下动态的创建方法。
  静态创建组件
  通常只有控件在模板文件中被静态创建。
  一个控件在模板文件组件标签来声明,其语法如下:
……
<com:ComponentType ID=”…” PropertyName=”…” EventName=”…”>
    ….body contents….
</com:ComponentType>
……
  这里ComponentType, PropertyName, 和 EventName 应该被真正的组件类名,属性名和事件名所替代。 ID属性是可选的。如果定义了ID属性,那么ID的值必须在平级的控件中是唯一的。如果没有定义,那么框架会自动为这个控件分配一个唯一的ID的。当然,这需求组件的标签被正确的嵌套,每一个开放的组件标签都应该和一个闭合的组件标签组成一对,这个规则和XML的规则是相同的。(译者著:如果你对XML一点都不了解,建议你先看一下一些基本的XML概念,需求并不高,你只需要知道怎么正确书写一个XML文件即可。)
  注意:控件的ID必须是个以字母开头的,后面只包含字母,数字和下划线的字符串。
  模板文件中属性的初始值字符串会被自动转换为正确的属性类型。目前有六种属性类型: string, integer, float, boolean, array  face=”Courier New”>和object. 前面三种类型的字符串格式是非常简单的。 boolean 类型只允许使用两个字符串: true  face=”Courier New”>和false。 array类型接受如(value1,value2,key3=>value3,…) 格式相同的字符串,这和PHP数组的初始化是相同的。 而object类型就比较复杂了,他取决于属性是怎么被定义的。一些属性能允许使用字符串,并会把他转换为对象,不过有些属性就不行了。
  当组件被创建(实例化)的时候,通过规格文件定义的组件的属性和对应事件处理方法就会即时生效了。

  动态创建组件
  PRADO 允许研发者在自己的PHP代码中实例化组件。组件能通过调用一个静态类方法Application::createComponent($type)来被实例化,该方法的参数$type指明了要创建的组件的名称。组件也能使用new操作符来实例化。这两者方法的差别是:前者会使用一种缓存机制,下一次创建相同的组件时速度会快非常多;而后者不会使用缓存机制,每次都要完全重复执行实例化的步骤。通常情况你应该使用TApplication::createComponent($type)来实例化(如果你想了解更多,请参考下面的注释)。
  如果新创建的组件是个控件,那么能通过调用把这个控件作为其他控件的子控件。注意,如果你需要指定这个控件的ID,应该在将他添加作为其他控件的子控件之前就指定ID;否则的话框架会自动为他分配一个ID,而且这个ID是不能更改的。
  注:如果在构造函数中使用或申请了资源句柄的话,只能使用new 操作符来实例化。因为PRADO使用了缓存机制来实例化组件,因此如果在构造函数中使用了资源句柄的话,下次实例化的时候从缓存读出来的数据中的资源句柄部分依然对应着原来的那块内存地址,这样就极容易导致系统崩溃。所以如果你要设计一个自己的组件,也尽可能的不要在构造函数中使用和申请资源句柄,而是应该把这些代码组织在一个别的方法中,在页面的OnInit事件或其他事件中调用。一般来说构造函数只需要实现简单的变量初始化即可。在PRADO的核心代码中,所有的组件的构造函数都没有使用到资源句柄。你在研发自己的组件的时候,能参考一下他们的原始码.

  2.1.3  访问组件属性
  PHP5使用了一种非常好的方式来访问组件的属性。你能把一个组件的属性当作组件的成员变量来使用。比如要设置TButton 控件的Text属性,你能使用$button->Text=”xxx”的代码,这里$button 代表了控件的实例。对于控件来说,你还能使用他的ID路径来访问属性。假定目前HomePage页面有一个子控件MenuBar ,MenuBar 控件有一个子控件HyperLink ,那么在页面环境中,能使用的代码$this->MenuBar->HyperLink->NavigateUrl来读取HyperLink 控件的NavigateUrl 属性。
  注意,由于PHP5.0中的一个BUG,如果你需要设置一个属性的值,那么你必须首先通过他的控件的ID路径来获得控件,然后再来设置这个属性的值。在上面这个例子中,需要用下面两行代码来设置NavigateUrl 属性。
$link=$this->MenuBar->HyperLink; $link->NavigateUrl=”…”;
  如果直接使用 $this->MenuBar->HyperLink->NavigateUrl 来设置属性值会产生一个错误。不过你还能下面这段代码来设置属性的值,这样就能避免那个错误了。
$this->MenuBar->HyperLink->setNavigateUrl(“…”);

  2.1.4  使用事件
  事件响应函数通常在规格文件或模板文件中指定给对应的事件,指定事件响应函数和指定属性的初始值是类似的。注意,在规格文件或模板文件中指定的事件响应函数,必须在此规格文件或模板文件对应的组件中定义,他的语法如下:
function handlerName($sender,$param) {    … }
  这里 $sender 指向的是触发这个事件的控件, $param 是事件的参数,他的内容取决于事件的类型。
  在编程的时候也能使用TComponent::addEventHandler() 方法来动态的指定一个事件响应方法。
  你能为一个事件指定多个响应方法。当这个事件被触发的时候,所有指定的响应方法都被自动调用。所以,PRADO实现的是多点派发事件触发机制。

  2.1.5  数据绑定(Data Binding)
  只有控件才能数据绑定。
  你能给控件的属性绑定一个表达式,当这个控件的dataBind()方法被调用的时候,这个属性的值回自动被设置为这个表达式的值。数据绑定在研发数据组件时是非常有用的,这些组件的非常多属性值都是来源于数据源提供的数据的。你能在组件的规格或模版文件中设定数据绑定,也能在代码中设定。
  在模版中设定数据绑定的话,只要给属性的值指定一个有效的PHP表达式的字符串,并在前面加上一个#作为前缀。比如在页面模版文件中使用如下的代码:
<com:TLabel Text=”#$this->Page->ID” />
  这段代码给TLabel组件的Text属性绑定了一个的表达式$this->Page->ID 。这个表达式的作用是获得当前控件所在页面的ID。注意,这个表达式中的$this指的是TLabel控件本身,因为$this所在上下文环境是在TLabel 中。
  在代码中要设定数据绑定,能调用组件的bindProperty() 方法,这时候不必在前面加上字符# 。
  注意,给属性绑定的表达式只有在dataBind() 被调用时才会计算该表达式的值,并把他赋值给属性。具体内容能参考dataBind() 的相关文件。
  另外,如果在模板文件中你需要给一个属性赋初始值,而不是数据绑定的话,如果这个值是以#开头的,那么应该将#重复一次,就像PropertyName=”##….” 相同。

  2.1.6  PRADO 组件类树
  目前发布的 PRADO 包括如下所示的组件树,这些组件的属性,事件和类的方法在PRADO文件中都能查到。
TComponent
    TAdodb
    TControl
        TExpression
        TForm
        TLiteral
        TPage
TPlaceHolder
        TRepeater
TRepeaterItem
        TStatements
        TWebControl
            TButton
            TCheckBox
                TRadioButton
            TDropDownList
            THyperLink
            TImage
                TImageButton
            TLabel
            TLinkButton
            TListBox
            TPanel
            TTextBox
                TDatePicker
                THtmlArea
            TValidationSummary
            TValidator
                TCompareValidator
                TCustomValidator
                TRangeValidator
                TRegularExpressionValidator
                    TEmailAddressValidator
                TRequiredFieldValidator

2.2  页 面

阿里云-推广AD

  页面是 TPage 或他的子类的一个实例。他是最高级别的组件,即没有父组件也不包含在一个容器中。PRADO的应用是由一些页面组成的。

  2.2.1  页面的生命周期
  理解页面的生命周期对掌控PRADO编程是非常重要的。
  首先我们要介绍一下postback的概念。我们把一个form的提交称之为postback,如果form的数据是提交给包含该form的页面的。postback能被认为是由用户在客户端触发的一个事件。PRADO会区分出把postback事件交给哪一个服务器端的组件来响应。如果找到了这个组件,比如是个TButton,那么我们就把这个TButton 组件称为事件的发送者(sender)。
  页面在被请求调用的时候会经过几个状态。当一个页面是由于他发生了一个postback而被调用的时候,这个页面会经历以下的生命周期:
页面的创建:页面和他所有子组件是被递归创建的。框架根据各个组件的规格文件和模板文件的设置来初始化组件的属性。你一个能重载页面的构造函数来做一些其他的初始化工作,比如给一些属性赋一些缺省值。不过需要注意的是,在这一步的时候,是不能够访问其他组件,因为组件之间的关系这时候还没有被建立。
OnInit事件: OnInit事件会被广播给整个页面的组件树(也就是说所有的组件都会被触发OnInit事件)。能重载页面的onInit()方法或给OnInit事件指定一个处理方法来做一些初始化的工作,比如建立数据库连接等等。这时候组件和组件的关系已被建立了。
导入显示状态(Viewstate):将先前的显示状态导入到页面中。页面自己会将上次显示给用户看的状态保存起来。关于显示状态,下一章会有更周详的解释。
导入提交的数据:页面中的一些组件的属性会根据用户提交的数据而更新。比如,TTextBox 组件的Text属性会被用户在此组件的文本输入框中输入的文本而更新。
OnLoad事件:在这个阶段,页面会设置为提交前用户看到的状态。OnLoad 事件也会被广播给整个页面的组件树。同样也能重载onLoad()方法或给OnLoad 事件指定一个处理函数。
导入提交的数据:一些在OnLoad步骤创建的组件在这时候有机会导入提交的数据。
产生提交数据变化事件。如果一个组件的数据被用户改动了,那么就会机会产生一个事件来指明这个变化。比如用户改动了一个TTextBox 组件的文本输入框的值,那么这个TTextBox 组件就会触发一个OnTextChanged 事件。研发者能够为这个事件指定处理方法。
输入验证:如果事件的提交者需要验证,这时候页面注册的验证组件会来对应的数据。
Postback事件:postback事件通过事件的发送者被触发。比如,用户点击了一个按钮引起了一次postback,那么这个按钮对应的TButton组件就触发一个事件OnClick。你能指定这个事件的处理函数来响应这个事件。
OnPreRender事件:在这一步,页面已完成了被提交的数据的导入和postback事件的处理。OnPreRender事件会被广播给整个页面的组件树 。在页面被绘制之前,你能重载onPreRender() 函数或给OnPreRender 事件指定一个事件处理函数。
整个页面的组件树的显示状态被保存在一个固定的存储空间(比如表单的隐藏字段,session,或数据库中)。
通过递归的方式页面类来显示这个页面,页面中的组件会自己显示自己(意思是说每一个组件都会负责显示他自己)。通常情况下,组件会在对应所在模板的位置上来显示自己。你也能重载TComponent 的render() 方法来自己控制组件的显示。
OnUnload 事件:页面和他所有的子组件从内存中被释放。同样,这个事件也会被广播给整个页面的组件树。你能通过重载onUnload() 函数或给OnUnload 事件指定一个处理函数来完成一些清除工作,比如断开数据库的连接等等。
  当页面是第一次被请求的时候,上述的生命周期会简单一些。具体来说,导入显示状态,导入提交的数据,产生提交数据变化事件,输入验证和postback事件这几个状态是没有的。

2.3  应 用

  每一个 PRADO Web应用都有且只有一个的实例。他主要是负责编码解码用户请求,服务器的页面请求,和维护应用级别的参数。

  2.3.1  应用的设置
  每一个PRADO应用都应该有一个XML格式的设置文件。在”hello world”这个例子中,这个文件的文件名为 application.spec .
  应用设置文件的格式如下:
<?xml version=”1.0″ encoding=”UTF-8″?>
<application default-page=”…” cache-path=”…” session-class=”…” user-class=”…”>  
<alias name=”…” path=”…” />   
<alias name=”…” path=”…” />   
<using namespace=”…” />   
<using namespace=”…” />   
<secured page=”…” role=”…” />   
<secured page=”…” role=”…” />   
<parameter name=”…”>…</parameter>   
<parameter name=”…”>…</parameter>   
<parameter file=”…” /> </application>
  alias 元素定义了文件路径的别名,路径能是绝对是绝对路径也能是相对路径。
  using 元素定义了要增加到PHP搜索路径include_path 变量中的命名空间。命名空间是用”.”号连接的。第一段是路径的别名,之后是子目录。比如System.Web.UI 代表了框架所在目录下的Web/UI/子目录。框架的目录的别名已被定义成System 。在编程的时候,也能调用using() 函数来增加一个命名空间。
  secured 标签知名了页面是否需要验证/授权,page属性指名的是页面的名称或是页面名称的匹配表达式。如果一个页面被标明是的,那么说明这个页面需要用户验证的。另外如果role属性不是空的,那么需求用户是属于指定的角色的。
  parameter 元素定义了用户参数。能通过设置parameter 元素的file属性来导入一个参数文件。参数文件的格式如下:
<?xml version=”1.0″ encoding=”UTF-8″?>
<parameters>   
<parameter name=”…”>…</parameter>   
<parameter name=”…”>…</parameter>
</parameters>
  default-page 属性和 cache-path 属性分别知名了缺省的页面和缓存路径。如果应用需要使用session, session-class 属性也需要设置。如果应用需要使用框架的验证/授权支持,那么还应该指定user-class属性。

  2.3.2  页面服务
  PRADO使用GET变量page来指明要请求的页面。比如下面的这个URL:
/examples/phonebook.php?page=AddEntryPage
  这段代码将会请求 AddEntryPage 页面。如果没有指定page 变量,那么就会调用应用设置中的缺省页面。
  你也能使用 TApplication::constructUrl() 方法来请求一个页面。

  2.3.3  数据编码和解码
  缺省情况TApplication 会对所有的POST和GET数据进行HTML编码。 ’, “, <, >, & 会被转化为&#039;,&quote;,&lt;,&gt;和&amp;。之所以要进行编码主要是为了让这些字符能被保存到数据库中,并且能正确显示回给用户。如果有必要的话,能调用pradoDecodeData()函数来进行解码。也能重载TApplication::beginRequest() 函数不进行编码。

2.4  定义新组件

  有两种方法来定义新的组件类:继承和组合。
  继承是面向对象的设计概念。派生类定义他的父类的一些内容,还能提供一些其他的功能。在PRADO框架中,组件的属性和事件都是能继承的。派生类也能提供更多的属性和事件。所有的组件类都应该从TComponent 或他的派生类继承。
  所有父组件的属性和事件会被派生组件类继承。
  组合是用于基于组件的框架的。在PRADO中,主要对控件使用。一个新的控件类能被定义为几个其他几个控件的组合。新的类负责协调这些控件之间的通信,并代表他们和外界通信。比如能定义一个LabeledTextBox 控件类(继承TControl ),他由TLabel 一个控件和一个TLabel
控件组成。新的类负责设置这两个组成控件的属性和方法。
  一般的约定(并不强求),控件的ID和属性的首字母应该大写,比如HomePage, NavigateUrl,
LogoPict;事件以On开头,比如OnClick 。ID和属性的名字命名和变量命名相同,必须是以字母开头的,仅包括字母数字和下划线的字符串。
  要定义一个新的组件通常需要写一个组件类文件,一个规格文件和一个模板文件。有些时候后面两个文件能不要的。

  2.4.1  定义属性
  组件的属性在组件类的规格文件中定义,规格文件的语法如下:
<?xml version=”1.0″ encoding=”UTF-8″?> <component>    ……    <property name=”<property name>”              get=”<getter method>”              set=”<setter method>”              type=”<property type>”              default=”<default value>” />    …… </component>
  在这里,name用来唯一标识属性;get定义了一个类方法来读取属性,set 定义了属性的类型(string, integer, float, boolean, array 和object),default 标识了属性的缺省值。其中name属性是必须的。getter方法的语法如下: 
function getterMethodName() {    ….    return $value. }
  如果一个属性是不可读的,那么不要设置get属性。setter方法的语法如下:
function setterMethodName($value) {    // set some variable with $value }
  如果一个属性是不可写的,那么不要设置set属性。如果这个属性的类型是string,那么也能不指明。类型的属性用来自动转换设置在规格文件或模板文件中的属性初始值。
default 属性一般用来作为引用的目的,也是可选的。如果一个属性是可读的而且之前没有被设置,那么getter方法应该返回一个缺省值。
  能用下面的语法定义组件的组件属性:
<?xml version=”1.0″ encoding=”UTF-8″?> <component>    ……    <component type=”component type” ID=”component ID”>        <property name=”property name” value=”property value”/>        ….        <event name=”event name” handler=”function name”/>        ….    </component>    …… </component>
  这里type 属性指明了组件类的名称,IDID 属性指明了属性名称。property 元素和event 元素指明了对应的初值。
  当组件被创建的时候,他的组件类型属性会自动被实例化。

  2.4.2  定义事件
  组件事件在规格文件中定义,而其内在的实现机制在类文件中定义。在规格文件中定义事件的语法如下:
<?xml version=”1.0″ encoding=”UTF-8″?> <component>    ……    <event name=”…” />    …… </component>
  事件的name应该是个合法的变量名。
  在类文件中,通常会实现这么一个对应的方法(假定事件的名称叫 OnClick):
function onClick($param) {    $this->raiseEvent(’OnClick’,$this,$param); }
  当这个事件实际发生的时候,这个 onClick 方法应该确实被调用了(参考框架的TButton.php
文件)。raiseEvent 方法在TComponent 类中实现,他调用了所有对应OnClick 的事件响应函数,并把参数$param 传递给他们。

  2.4.3  编写模板文件
  在这一节里,会讲一些关于怎么编写模板文件。模板是用于控件的,当时也不是所有的控件都需要模板。比如TTextBox, TButton 这些控件因为没有内容,因此就不必模板。模板文件一般用于页面或基于组合定义的控件。
  模板中的注释,比如 <!– … –> 被作为静态文本处理。
  能在组件标签之外通过属性标签<prop:…>来设置组件的属性。比如能用以下的代码设置模板中TTextBox 控件的Text 属性。
… <com:TTextBox ID=”profile”>   <prop:Text>     …   </prop:Text> </com:TTextBox> …
  在一对属性标签中间的内容作为对应的组件属性的值。如果一些属性的值需要非常大的数据,这样就非常方便。
  在模板文件中能使用3种特别的标签:
<%include … %> – 框架会在解析模板前在这个位置包含一个外部文件。比如,你能用他在页面模板中包含一个公共头文件<%include header.tpl %> 。外部文件的路径应该是这个模板文件的相对路径。
<%= … %> -会直接在这个位置上输出一个PHP表达式的值,比如<%= date(’Y-M-d’,time())
%>。注意,表达式后面不必输入分号。这个表达式的上下文环境是这个模板对应的组件,能使用$this 变量来在表达式中使用组件。表达式的值在组件的绘制状态时计算。
<% … %> – 代表了一系列PHP的语句。如果他们当中有echo或print,那么其结果会显示在标签对应的位置。注意,这里PHP语句需要使用分号隔开,和标准的PHP语法是相同的。同样,这里的上下文环境依然是拥有此模板文件的组件。(译者注:非常多地方都提到了上下文环境,这个词在有些面向对象的书籍中是这么翻译的,而有时候也被翻译成其他文字。他所指的是当前代码中代表的$this是哪一个组件。因为一个组件中可能包含了其他组件,比如页面中就经常包含了非常多组件,那么如果这个模板是这个页面类的,那么我们此时的上下文环境是这个页面。有些时候,比如我们给页面中的某一个组件进行数据绑定的时候,上下文环境就改动成了这个组件,这时候,$this代表了此组件,而不再是页面)。在模板文件中,除了在组件标签范围内,在其他以外的地方能自由使用其他各种标签。

2.5  总 结

  这一节我们来总结一下怎么基于PRADO来研发一个Web应用。
  首先需要创建两个文件:应用的入口文件和应用的设置文件。这一部分请参考本手册前面的”Hellow,world!”这个例子。
  对于一个完整的Web应用而言,你还需要创建一个或多个页面。每一个页面都需要有一个页面类文件,另外可能还需要一个模板文件或规格文件。在模板文件中,你能把组件和其他静态文本根据最终需要显示给用户看的样子组织在一起。在规格文件中,能定义页面的属性,事件和子控件,这些定义能在模板文件和代码中使用。这两个中都能设置组件的初始值和指定事件的响应函数。在页面类文件中编写事件响应函数和其他函数。
  有些时候,为了重用代码,你能定义新的组件。比如你能定一个SideBar组件来显示用户能看到的菜单,这样在各个页面中就都能直接使用这个SideBar 组件来显示菜单了。
  PRADO引入了PHP5新的例外处理机制,能够显示堆栈中的错误信息。这样在调试的时候你能精确的找到什么方法发生了什么错误。
  发布 PRADO 应用则非常简单。框架建议使用相对路径,因此如果你是这么做的话,只需要把包含你的应用的代码拷贝到你想放置的目录中就能了。