级别: 中级
Ken Ramirez , 创始人, Axsys Technology Group
2008 年 9 月 23 日
学习使用原生 JavaScript™ 代码和 PHP 编写 Asynchronous JavaScript +XML(Ajax)应用程序的过程。本文介绍几个框架和应用程序编程接口(API),可以用它们减少开发 Ajax Web应用程序所需编写的代码量。
PHP已经流行很多年了。PHP 通常作为服务器端脚本语言,用来快速开发基于 Web 的应用程序,而且效果很不错。实际上,一些最流行的 Web项目(比如 PHP-Nuke、osCommerce 和 Joomla)都是用 PHP 开发的,它们至今仍然很兴旺。
Ajax也已经出现了一阵子了,但是直到最近使用 Ajax 技术开发的 Web 站点才开始大量增加。Ajax 技术使 Web 站点或 Web应用程序能够自动地与服务器通信,而不需要刷新整个页面。从本质上说,Ajax的异步特性使客户机浏览器能够向服务器发送请求或调用服务器端的方法。在客户端,可以使用 JavaScript代码处理服务器返回的结果,然后可以把任何输出合并到现有的前端 HTML 视图中,而不需要刷新页面。在使用 Ajax时,实际上并不使用新的编程语言。实际上,只需组合使用现有技术。
|
PHP和 Ajax 的组合提供一个强大的平台,可以用来创建具备健壮的特性的 Web 站点或 Web 应用程序。本文讨论 PHP 和 Ajax的一些用途,研究如何在自己的 Web 应用程序中使用它们。在阅读本文之前,您应该充分理解 HTML 和 JavaScript编程。您还应该熟悉 PHP 脚本编程语言,虽然也可以使用大多数其他脚本语言。
在 Ajax 中客户机/服务器通信的关键是使用 JavaScript
XMLHttpRequest
对象。大多数浏览器都支持这个对象,包括 Windows® Internet Explorer 5.0 和更高版本、Safari1.2、Mozilla Firefox、Opera 8 和更高版本以及 Netscape 7。为了解释传统客户机/服务器通信和基于 Ajax的客户机/服务器通信之间的差异,下面举一个例子。按照传统方式,为了让客户机浏览器向服务器发送需要处理或存储在数据库中的内容,常常使用
POST
方法把客户端上输入控件收集的内容发送到服务器。服务器使用 PHP(或其他脚本语言)处理这些内容,使用数据库读取或存储数据,最后返回嵌入在 HTML 代码中的结果。然后,浏览器处理 HTML 并向最终用户显示一个新页面。图 1 描述这个场景。
通过使用 Ajax,同样的过程在前端需要的时间更少。其基本思想是让用户感觉不必等待页面更新。实际上,通过使用 Ajax,可以用一个 HTML页面构成整个 Web 应用程序,但是不建议这么做。按照传统方式,如果希望向服务器提交一个表单,就要设置表单的动作并把动作类型指定为
POST
。在使用 Ajax 时,实际上并不向服务器直接提交表单,而是调用一个 JavaScript 函数。这个函数从表单收集数据并执行检验,然后使用
XMLHttpRequest
把数据发送给一个服务器端函数。当结果返回客户机时,客户机处理结果并更新需要更新的页面部分。在这种情况下,页面
并不
全部地刷新。因此,处理 HTML 所花费的时间更少,性能会更好。图 2 说明了使用 Ajax 更新页面与刷新整个页面之间的细微差别。
现在看看通过 JavaScript 代码与服务器进行通信所需的步骤。首先,定义表单的外观,见清单 1。
<body> My First Ajax Page <form name="myForm"> Press button to view server time: <input type="button" value="Update" onClick="ajaxFunction();" /> Server Time Is: <input type="text" name="time" /> </form> </body> |
这个表单产生图 3 所示的输出。
这个表单没有做任何真正有用的事,但是它帮助您了解在自己的代码中的什么地方集成 Ajax。
请注意按钮上指定的
onClick
事件。这个事件调用一个名为
ajaxFunction
的 JavaScript 函数。这里就是 Ajax 中比较有趣的地方。在这个方法中,要执行几个步骤,本节会逐一解释这些步骤:
-
创建
XMLHttpRequest
对象的实例。 - 连接要调用的服务器端服务。
- 告诉 Ajax 在服务器端代码执行完成并返回结果时应该调用哪个方法。
- 发送请求。
- 异步地响应。
需要创建
ajaxFunction
并提供一个变量来容纳创建的
XMLHttpRequest
对象。与任何 JavaScript 方法一样,按照清单 2 所示定义这个方法。
function ajaxFunction() { var xmlHttp = null; . . . } |
大多数现代浏览器都支持
XMLHttpRequest
对象。但是,Internet Explorer 6 等比较陈旧的浏览器要求创建一个 ActiveX 对象来执行异步的服务器调用。这会产生一个问题:必须判断正在运行代码的浏览器类型并创建适当的对象。JavaScript 代码通过它的
try/catch
功能提供了一个解决方案。只需以正确的优先次序尝试创建对象,让
try/catch
块处理其余的事情。清单 3 给出一个代码示例。
function ajaxFunction() { var xmlHttp=null; try { // Firefox, Internet Explorer 7. Opera 8.0+, Safari. xmlHttp = new XMLHttpRequest(); } catch (e) { // Internet Explorer 6. try { xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { return false; } } } } |
可以看到,所有现代浏览器都支持创建
XMLHttpRequest
对象。关于 Microsoft® 是真正支持
XMLHttpRequest
对象,还是使用一个 façade 包装 ActiveX 实现,还有一些争议。
在同一个 JavaScript 函数中,创建
XMLHttpRequest
对象之后,使用
XMLHttpRequest
对象的
open
方法打开一个到服务器端脚本的连接。这个方法有两个必要参数和三个可选参数,见表 1。
method |
指定要使用的 HTTP 方法。有效值包括
GET
、
POST
、
PUT
或
HEAD
。
|
url | 指定要调用的 XML 数据或服务器端 XML Web 服务的绝对路径或相对路径。为了防止跨站点脚本攻击,Ajax 请求针对的 URL必须使用与包含 Ajax 请求的页面相同的协议、主机和端口。尽管一些浏览器可能允许任意的URL,但是并非所有浏览器都支持这么做。如果需要跨站点通信,必须在服务器端使用 cURL 或其他方法进行处理。 |
async |
如果希望异步地向服务器发送请求,应该把这个参数设置为 true。true 值还要求设置
onreadystatechange
属性,稍后讨论这个属性。把这个参数设置为 false 会禁止大多数浏览器接收任何进一步的用户输入。如果应用程序足够灵活,能够在执行后端操作的同时继续接收输入,那么最好异步地执行操作。
|
user | 指定一个用户名,用来在执行脚本之前对用户进行身份验证。只有在脚本要求用户身份验证的情况下,才需要这个参数。 |
password | 指定一个密码,用来在执行脚本之前对用户进行身份验证。只有在脚本要求用户身份验证的情况下,才需要这个参数。 |
对于这个示例,代码只需要执行一个
GET
操作,向服务器上的一个脚本请求时间。还告诉
open
方法我们希望异步地执行操作,见清单 4。
function ajaxFunction() { . . . xmlHttp.open("GET", "time.php", true); } |
在使用 Ajax 调用服务器时,产生的响应是通过调用一个回调函数返回的。可以创建并命名一个函数,也可以创建无名的函数。无论是哪种情况,都需要设置
XMLHttpRequest
对象的
onReadyStateChange
属性,让它知道要使用的回调函数,见清单 5。
清单 5. 设置
onReadyStateChange
属性
function ajaxFunction() { . . . xmlHttp.onreadystatechange=function() { if(xmlHttp.readyState==4) { // Get the data from the server's response. document.myForm.time.value=xmlHttp.responseText; xmlHttp=null; } } } |
可以看到,调用这个方法之后,它会检查
readyState
,寻找有效值 4。共有五个有效状态,见表 2。
0 | 未初始化 |
1 | 正在装载 |
2 | 装载完成 |
3 | 交互状态 |
4 | 已经完成 |
这段代码的基本意思是,“如果状态指出操作已经完成,就进行处理”。当状态为已经完成时,下一步是用服务器响应更新需要更新的页面部分。这需要接收赋值给
responseText
属性的值,这就是服务器响应。最后,通过把
XMLHttpRequest
对象赋值为
null
,停止这个对象。
ajaxFunction
还需要执行一个步骤,也就是把请求发送到服务器。这需要使用
XMLHttpRequest
对象的
send
方法。如果请求是异步的,这个方法会在发送请求之后立即返回。如果请求是同步的,这个方法在收到响应之后才会返回,这意味着 Ajax 函数会一直阻塞,直到这个方法返回。
这个方法有一个参数,可以把它设置为 null 或许多其他值。例如,可以传递一个
DOMDocument
对象、
InputStream
或
String
。如果请求方法是
POST
,这个值就用作 HTTP 请求体。完成的
ajaxFunction
应该与清单 6 相似。
function ajaxFunction() { var xmlHttp=null; try { // Firefox, Internet Explorer 7. Opera 8.0+, Safari xmlHttp = new XMLHttpRequest(); } catch (e) { // Internet Explorer 6. try { xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { alert("Your browser does not support AJAX!"); return false; } } } xmlHttp.open("GET", "time.php", true); xmlHttp.onreadystatechange=function() { if(xmlHttp.readyState==4) { // Get the data from the server's response. document.myForm.time.value=xmlHttp.responseText; xmlHttp=null; } } xmlHttp.send(""); } |
因为 Ajax 在本质上是异步的,所以必须注意并发性并了解处理次序。在发出多个 Ajax 调用并以出乎意料的次序接收响应时,这尤其重要。一般假设次序是无法意料的。
有时候,服务器响应需要以 XML 文档的形式返回给客户机。在这些情况下,可以使用
XMLHttpRequest
对象的
responseXML
属性接收数据。清单 7 给出一个返回 XML 文档的 PHP 脚本示例。
<?php $result = getRecordSet($_GET['query']; echo '<?xml version="1.0" encoding="ISO-8859-1"?>' . '<car>'; while($row = mysql_fetch_array($result)) { echo "<make>" . $row['make'] . "</make>"; echo "<model>" . $row['model'] . "</model>"; echo "<year>" . $row['year'] . "</year>"; echo "<description>" . $row['description'] . "</description>"; } echo '</car>'; ?> |
这段代码创建一个 XML 文档(由
'<?xml version="1.0" encoding="ISO-8859-1"?>'
表示),然后发送响应的其余部分。客户端代码必须先从
responseXML
属性中取出 XML 文档,使用 Document Object Model(DOM)解析数据,然后通过修改页面上现有的适当字段在页面上显示数据。清单 8 演示这些步骤。
清单 8. 处理 XML 文档的 JavaScript 代码
function stateChanged() { if(xmlHttp.readyState==4) { xmlDoc = xmlHttp.responseXML; document.getElementById("make").innerHTML = xmlDoc.getElementsByTagName("make")[0].childNodes[0].nodeValue; document.getElementById("model").innerHTML = xmlDoc.getElementsByTagName("model")[0].childNodes[0].nodeValue; document.getElementById("year").innerHTML = xmlDoc.getElementsByTagName("year")[0].childNodes[0].nodeValue; document.getElementById("description").innerHTML = xmlDoc.getElementsByTagName("description")[0].childNodes[0].nodeValue; } } |
您可能会注意到,为了在前端实现 Ajax 功能,服务器端 PHP 代码并不需要任何特殊的东西。实际上,可以用任何语言编写服务器端脚本,只要能够从Web 服务器调用它并把结果返回给客户机。但是,出于几个目的,希望扩展 Ajax来实现其他功能。例如,许多库提供了外观更好的前端控件,可以实现 Web 2.0 所需的效果。这些库大多数是用 JavaScript代码编写的,并提供了可以减少编写前端所需的 JavaScript 代码量的特性和 API。另外,这些库大多数使用 JavaScriptObject Notation(JSON),这是一种目前在因特网上广泛使用的轻量数据交换格式。下面是可能对您有帮助的一些库:
- Prototype
- Script.aculo.us
- Dojo
- jQuery
Prototype提供用来开发动态 Web 应用程序的类驱动的 JavaScript 代码。另外,Prototype 框架还提供一个全局 Ajax对象,这个对象使 Ajax 功能更容易编写、效果更好、更容易处理。另外,通过使用这个框架进行 Ajax开发,就不再需要处理跨浏览器问题。只需在一次调用中指定目标 URL、HTTP 方法(
POST
或
GET
)和响应回调函数等等。
Script.aculo.us是另一个流行的 JavaScript 框架,它提供一些出色的用户界面控件。这个框架还提供了一些执行 Ajax 功能的控件。甚至还有一个Google 风格的自动补齐文本控件,可以用它收集用户输入的文本。Ajax功能允许轻松地指定查询的服务器端目标。产生的响应用来向最终用户显示服务器返回的字符串集。
Dojo工具集是一个开放源码的 JavaScript 和 Ajax 框架,它速度快和文件小(只有 25 KB),因此很受欢迎。在 Ajax方面,Dojo 提供了用来发送和接收异步服务器调用和响应的 API。在服务器端不需要修改任何代码。对于任何 Ajax 调用,您的 PHP代码仍然可以做出响应。
jQuery 的优点是简化了在页面上搜索元素的机制,并支持在运行时动态地添加新函数、特性和格式。它还提供一些与众不同的 Ajax 特性,支持在 Web 页面上提供 Ajax 交互。
既然了解了基本概念,现在就来讨论如何扩展 PHP 来支持 Ajax。从 PHP 的角度来看,扩展 Ajax 功能并不仅仅是重写
XMLHttpRequest
(XHR)访问代码。这还涉及以更简单更直观的方式提供前端服务,然后通过它们访问用 PHP编写的后端服务。应该包括从前端执行后端服务、访问数据库、执行后端网络服务等特性。我在市场上看到的一些框架允许开发人员用后端语言编写代码,然后由框架创建前端 JavaScript 代码。开发人员告诉框架必须调用哪些后端方法,框架就会用 JavaScript 代码编写前端façade,这些代码可以分配 XHR 对象、发送请求、接收响应,然后把响应传递给您选择的函数,甚至把输出直接赋值给您选择的 HTML元素。这种框架设计思想可以减少必须编写的 JavaScript代码。希望为其提供函数的所有元素都可以被覆盖,可以把它们连接到在发生指定的前端事件时做出响应的后端方法。下面是值得研究的一些框架:
- PHP AJAX
- PHP-Ext
- ExtPHP
PHP AJAX 允许开发人员用自己的类扩展 PHP 类。然后,只需在调用 PHP 文件时调用一个初始化方法,它会替您生成必需的前端 XHR 代码。前端事件必须调用一个与 PHP 类同名的 JavaScript 函数。代码与清单 9 相似。
class ajax_app extends phpajax { function input() { } function loading() { } function main() { } } |
调用覆盖的方法执行 Ajax 生命周期中的各种任务。例如,如果在前端执行一个操作,就调用后端类的
main()
方法处理这个操作。在前端收集的所有输入都传递给
input()
方法。
PHP-Ext提供一个支持 PHP 4 和 5 的库,这个库提供大量前端用户界面控件。这些控件的底层代码实际上是由 Ext JS提供的,但这个框架的特色是开发人员不需要提供 JavaScript 来操作前端控件。相反,与控件的所有交互都是通过用 PHP编写的后端服务来执行的。在需要时,根据指定的事件调用 PHP 代码。当发生指定的事件时,就会调用对应的 PHP 代码。按照这种方式,可以从PHP 代码几乎直接地填写网格和其他控件的内容。
用PHP 编写的另一种 Ext JS 包装器是 ExtPHP(不要与前面提到的 PHP-Ext 混淆)。按照这个项目的开发负责人的说法,可以使用ExtPHP 编写侵入式和非侵入式 JavaScript 代码,编程方式与使用 Ext JS 时相同。这个框架的优点是允许 PHP编辑器探测未知的、拼写错误的或使用不当的方法,这样就不必在 Web 浏览器中调试 JavaScript 代码。ExtPHP的设计思想是生成可以从 PHP 脚本调用的 PHP 类。在实例化一个对象时,会把对应的 JavaScript代码存储在一个内部缓冲区中。如果调用一个对象的方法,代码也被添加到内部缓冲区中。准备好所有 JavaScript 代码之后,只需调用一个方法
jsrender()
。还可以通过调用 ExtPHP 函数在输出中添加内容,这样就可以在输出中直接添加 JavaScript 代码。
本文介绍了如何编写基于 Ajax 的前端代码,以及如何把前端代码和后端 PHP 脚本结合起来。还介绍了一些最新的框架,可以使用这些框架大大加快Ajax Web 应用程序的创建。通过结合使用这些框架、Ajax 和 PHP,可以编写出外观优美、功能丰富的 Web应用程序。实际上,如果您掌握了这些新技术,就会觉得离不开它们了。