ajax的server部分(php版)

2007-07-07 10:35:42  作者

Server端的任务通常是根据Client的请求,进行逻辑操作,并将结果响应返回。这个响应通常为XML格式(因此server端需要使用PHP的DOM创建XML响应)

1.PHP使用DOM创建XML响应,供client端的JS解析然后在页面中显示;(因此需要熟练PHP的DOM API)
其实,PHP生成XML的方法有两种
使用DOM API;(方法一)
另一种是直接将XML的内容echo出去即可;(方法二)
见示例
HTML页面(包含三个JS触发函数onmouseover, onmouseout, onclick; 分别触发自己的函数)
%26lt;!doctype html public "-//w3c//dtd html 4.0 tRANSITIONAL//en"%26gt;
%26lt;html%26gt;
%26lt;head%26gt;
%26lt;title%26gt; Server PHP Ajax %26lt;/title%26gt;
%26lt;script type="text/javascript" src="js.js"%26gt;%26lt;/script%26gt;
%26lt;/head%26gt;

%26lt;body%26gt;
%26lt;span onmouseover="PHPechoXML()" onmouseout="PHPDOMXML()"%26gt;Default Words%26lt;/span%26gt;
%26lt;div id="show"%26gt;%26lt;/div%26gt;
divide%26lt;input type="text" id="firstNumber"/%26gt;by
%26lt;input type="text" id="secondNumber"/%26gt;
%26lt;input type="button" value="Send" onclick="CSparameter()"/%26gt;
%26lt;div id="result"%26gt;%26lt;/div%26gt;
%26lt;/body%26gt;
%26lt;/html%26gt;
JS页面(分别定义三个JS触发函数PHPechoXML, PHPDOMXML, 以及CSparameter)
其中有XMLHttpRequest对象创建函数,以及各自的Server响应处理函数
///////1. 创建XMLHttpRequest对象
var xmlHttp = createXmlHttpRequestObject();

function createXmlHttpRequestObject()
...{
var xmlHttp;

try
...{
// try to create XMLHttpRequest object
xmlHttp = new XMLHttpRequest();
}
catch(e)
...{
// assume IE6 or older
var XmlHttpVersions = new Array('MSXML2.XMLHTTP.6.0',
'MSXML2.XMLHTTP.5.0',
'MSXML2.XMLHTTP.4.0',
'MSXML2.XMLHTTP.3.0',
'MSXML2.XMLHTTP',
'Microsoft.XMLHTTP');
for (var i=0; i%26lt;XmlHttpVersions.length %26amp;%26amp; !xmlHttp; i++)
...{
try

...{
// try to create XMLHttpRequest object
xmlHttp = new ActiveXObject(XmlHttpVersions[i]);
}
catch (e) ...{}
}
}
if (!xmlHttp)
alert("Error creating the XMLHttpRequest object.");
else
return xmlHttp;
}


///////2. JavaScript事件响应函数(onmouseover触发)
// read a file from the server
function PHPechoXML()
...{
// only continue if xmlHttp isn't void
if (xmlHttp)
...{
// try to connect to the server
try
...{
// initiate reading a file from the server
//向Server端的PHPechoXML.php文件发送异步请求
xmlHttp.open("GET", "PHPechoXML.php", true);
xmlHttp.onreadystatechange = handleRequestStateChange;
xmlHttp.send(null);
}
// display the error in case of failure
catch (e)
...{
alert("Can't connect to server: " + e.toString());
}
}
}

///////3. JavaScript事件响应函数(onmouseout触发)
function PHPDOMXML()
...{
// only continue if xmlHttp isn't void
if (xmlHttp)
...{
// try to connect to the server
try
...{
// initiate reading a file from the server
//向Server端的PHPDOMXML.php文件发送异步请求
xmlHttp.open("GET", "PHPDOMXML.php", true);
xmlHttp.onreadystatechange = handleRequestStateChange;
xmlHttp.send(null);
}
// display the error in case of failure
catch (e)
...{
alert("Can't connect to server: " + e.toString());
}
}
}

// handles the response received from the server,Server端状态回调函数
function handleRequestStateChange()
...{

if (xmlHttp.readyState == 4)
...{
// continue only if HTTP status is "OK"
if (xmlHttp.status == 200)
...{
try
...{
// read the message from the server
var xmlResponse = xmlHttp.responseXML;

//捕捉IE和Opera潜在的错误
if(!xmlResponse||!xmlResponse.documentElement)
...{
throw("Invalid XML structure: "+xmlHttp.responseText);
}
//捕捉FireFox的潜在错误
var rootNodeName=xmlResponse.documentElement.nodeName;
if(rootNodeName=="parsererror")
...{
throw("Invalid XML structure: "+xmlHttp.responseText);
}

//获取Server端响应的XML响应并解析,到网页中显示
// obtain the XML's document element
xmlRoot = xmlResponse.documentElement;
// obtain arrays with book titles and ISBNs
cityArray=xmlRoot.getElementsByTagName("city");
// generate HTML output
var html = "";
// iterate through the arrays and create an HTML structure
for (var i=0; i%26lt;cityArray.length; i++)
html += cityArray.item(i).firstChild.data + "%26lt;br/%26gt;";
// obtain a reference to the %26lt;div%26gt; element on the page
myDiv = document.getElementById("show");
// display the HTML output
myDiv.innerHTML = "Server says: %26lt;br /%26gt;" + html;
}
catch(e)
...{
// display error message
alert("Error reading the response: " + e.toString());
}
}
else
...{
// display status message
alert("There was a problem retrieving the data: " +
xmlHttp.statusText);
}
}
}

///////4. JavaScript事件响应函数(onclick触发)
function CSparameter()
...{
// only continue if xmlHttp isn't void
if (xmlHttp)
...{
// try to connect to the server
try
...{

//获取form中的值
var firstNumber=document.getElementById("firstNumber").value;
var secondNumber=document.getElementById("secondNumber").value;

//设置为参数,对Server端的CSparameter.php进行异步请求
var param="firstNumber="+firstNumber+"%26amp;secondNumber="+secondNumber;

// initiate reading a file from the server
xmlHttp.open("GET", "CSparameter.php?"+param, true);
xmlHttp.onreadystatechange = handleRequestStateChangePara;
xmlHttp.send(null);
}
// display the error in case of failure
catch (e)
...{
alert("Can't connect to server: " + e.toString());
}
}
}

//Server状态改变回调函数(Server端接受Client端传来的参数经过逻辑计算之后返回XML响应,Client端对XML进行解析,返回更新到页面中)
// handles the response received from the server
function handleRequestStateChangePara()
...{

if (xmlHttp.readyState == 4)
...{
// continue only if HTTP status is "OK"
if (xmlHttp.status == 200)
...{
try
...{
// read the message from the server
var xmlResponse = xmlHttp.responseXML;

//捕捉IE和Opera潜在的错误
if(!xmlResponse||!xmlResponse.documentElement)
...{
throw("Invalid XML structure: "+xmlHttp.responseText);
}
//捕捉FireFox的潜在错误
var rootNodeName=xmlResponse.documentElement.nodeName;
if(rootNodeName=="parsererror")
...{
throw("Invalid XML structure: "+xmlHttp.responseText);
}

// obtain the XML's document element
xmlRoot = xmlResponse.documentElement;

cityArray=xmlRoot.getElementsByTagName("result");
// generate HTML output
var html = "";
// iterate through the arrays and create an HTML structure
for (var i=0; i%26lt;cityArray.length; i++)
html += cityArray.item(i).firstChild.data + "%26lt;br/%26gt;";
// obtain a reference to the %26lt;div%26gt; element on the page
myDiv = document.getElementById("result");
// display the HTML output
myDiv.innerHTML = "Server says: %26lt;br /%26gt;" + html;
}
catch(e)
...{
// display error message
alert("Error reading the response: " + e.toString());
}
}
else
...{
// display status message
alert("There was a problem retrieving the data: " +
xmlHttp.statusText);
}
}
}

Server端的PHP脚本(负责接受Client端的异步请求做出响应,并以XML格式返回到Client端)
PHPechoXML.php(PHP生成XML响应的第一种方法,echo输出XML内容)
%26lt;?php
//server端的PHP生成XML文件的第一种方法,直接echo出XML
header('Content-Type: text/xml');
// generate XML header
echo '%26lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?%26gt;';

echo '%26lt;cities%26gt;';
$cityArray=array('Paris','London','NewYork','Beijing','Tokoy');
foreach ($cityArray as $city)
{
echo '%26lt;city%26gt;'.$city.'%26lt;/city%26gt;';
}
echo '%26lt;/cities%26gt;';

?%26gt;

PHPDOMXML.php(PHP生成XML响应的第二种方法,使用PHP的DOM API,输出XML格式的响应)
%26lt;?php
header('Content-Type: text/xml');

$cityArray=array('Shanghai','Beijing','Shanxi','Shandong');
//创建一个XML文档
$dom=new DOMDocument();
//最外层的Tag
$citiesTag=$dom-%26gt;createElement('cities');
$dom-%26gt;appendChild($citiesTag);
//里面的Tag可以通过循环产生
foreach ($cityArray as $city)
{
$cityTag=$dom-%26gt;createElement('city');
$cityName=$dom-%26gt;createTextNode($city);
$cityTag-%26gt;appendChild($cityName);
$citiesTag-%26gt;appendChild($cityTag);
}
//将XML结构保存为字符串并输出
$xmlString=$dom-%26gt;saveXML();
echo $xmlString;
?%26gt;

2. client端的与Server端的参数传递
client端的网页中可以有form,这样可以将参数传递到server端%26lt;GET或POST方法,变量或XML%26gt;,server端根据传递的参数,生成满足要求的XML响应;(实现了client端和server端的参数交互)
见示例
与1中一样,参数传递的PHP端的脚本如下CSparameter.php(接受Client端form异步请求的参数,进行逻辑处理,并生成XML响应发回Client端)%26lt;?php
//自定义Server端的错误处理函数
require_once('error_handler.php');
header('Content-Type: text/xml');

//接受Client端异步请求的参数
$firstNumber=$_GET['firstNumber'];
$secondNumber=$_GET['secondNumber'];

//进行逻辑计算
$result=$firstNumber/$secondNumber;

//生成XML格式的响应向Client端返回
$dom=new DOMDocument();
$resultsTag=$dom-%26gt;createElement('results');
$dom-%26gt;appendChild($resultsTag);

$resultTag=$dom-%26gt;createElement('result');
$resultText=$dom-%26gt;createTextNode($result);
$resultTag-%26gt;appendChild($resultText);
$resultsTag-%26gt;appendChild($resultTag);

$xmlString=$dom-%26gt;saveXML();
echo $xmlString;

?%26gt;
3. PHP端的错误异常(这里说的错误或异常都是指逻辑错误)处理
a)PHP默认情况下,发生错误或异常的时候,不会将异常抛出(这是因为php.ini中display_errors的默认置为off,错误将被保存在Apache错误日志记录中),因此编写起来很难调试。%26lt;往往浏览器显示的错不太轻易定位%26gt;

b)但假如将display_errors设置为on后,错误将显示出来,但是不友好的出错信息.
http://www.knowsky.com/
c)可以编写自己的PHP错误异常处理函数(不一定要求display_errors设置为on),将错误以明显的方式显示出来,便于调试;
通常自己编写异常处理函数如下
显示定义的Server端错误异常抛出函数error_handler.php(PHP程序中可以方便地复用)
%26lt;?php
//set a user-defined error handler function用户自定义出错异常处理方法
set_error_handler('error_handler', E_ALL);
function error_handler($errNo,$errStr,$errFile,$errLine)
{
//假如输出缓存非空,将其置空
if(ob_get_length()) ob_clean();
//定义自定义输出
$error_message='ERRNO: '.$errNo.chr(10).'TEXT: '.$errStr.chr(10).'LOCATION: '.$errFile.', Line'.$errLine;
echo $error_message;
exit;
}
?%26gt;

4.server端存取数据库%26lt;使用MySQL存取数据,从而实现真正的动态%26gt;
这个已经很经典了,可以使用MySQL或MSSQL、Oracle等
a)打开数据库;b)SQL语句Query c)关闭数据库

5.server端PHP程序封装和体系结构(server端php程序引入设计模式)
a) appname.php %26lt;接受client端请求%26gt;
b) appname.class.php %26lt;将server端的逻辑、数据库操作、错误处理等封装成类,包含属性、方法、构造函数、析构函数%26gt;
c) config.php
d) error_handler.php

一个server端引入设计模式的例子(设计Server端PHP脚本的程序架构,增强扩展性和重用性)
一个很简单的Keywords Suggest程序(包括index.html, css/style.css, js.js以及PHP代码php/suggest.php, suggest.class.php, error_handler.php, config.php 支持数据库)
index.html(css/style.css, js.js; 注重两个JS客户端事件触发onkeyup,onclick)
onkeyup在用户输入的时候实时异步地向Server端发送请求,Server给出响应;onclick在用户点击search的时候,向Server发送请求
%26lt;!doctype html public "-//w3c//dtd html 4.0 tRANSITIONAL//en"%26gt;
%26lt;html%26gt;
%26lt;head%26gt;
%26lt;title%26gt; Design Pattern PHP AJAX (keywords suggest DEMO) %26lt;/title%26gt;
%26lt;link type="text/css" rel="stylesheet" href="css/style.css"/%26gt;
%26lt;script type="text/javascript" src="js.js"%26gt;%26lt;/script%26gt;
%26lt;/head%26gt;
%26lt;body%26gt;
%26lt;noscript%26gt;
%26lt;strong%26gt;This example requires a JavaScript-enabled browser!%26lt;/strong%26gt;
%26lt;/noscript%26gt;
%26lt;div class="project"%26gt;
%26lt;span class="title"%26gt;Welcome to Design Pattern PHP AJAX (keywords suggest DEMO) %26lt;/span%26gt;
%26lt;br /%26gt;
%26lt;br /%26gt;
%26lt;div class="news"%26gt;
%26lt;br /%26gt;%26lt;input type="text" id="keyword" onkeyup="keyup()" /%26gt;%26lt;input type="button" id="search" onclick="search()" value="search"%26gt;%26lt;br /%26gt;%26lt;br /%26gt;
suggest keywords:
%26lt;div id="show"%26gt;%26lt;/div%26gt;
%26lt;/div%26gt;
%26lt;/div%26gt;
%26lt;/body%26gt;
%26lt;/html%26gt;
css/style.css
body
{...}{
font-family: Arial;
font-size: small;
background-color: #fff;
}
.title
{...}{
font-size:x-large;
}
div.project
{...}{
background-color: #99ccff;
padding:5px;
border:#000099 1px solid;
}
div.news
{...}{
background-color:#fffbb8;
padding:2px;
border: 1px dashed;
}
#show
{...}{
color: #008000;
font-style: italic;
}js.js(JS中定义响应函数以及Client处理Server响应的回调函数)
///////////////////////////////////////////////////////////
//1.创建XMLHttpRequest对象
///////////////////////////////////////////////////////////
var xmlHttp = createXmlHttpRequestObject();
function createXmlHttpRequestObject()
...{
var xmlHttp;
try
...{
// try to create XMLHttpRequest object
xmlHttp = new XMLHttpRequest();
}
catch(e)
...{
// assume IE6 or older
var XmlHttpVersions = new Array('MSXML2.XMLHTTP.6.0',
'MSXML2.XMLHTTP.5.0',
'MSXML2.XMLHTTP.4.0',
'MSXML2.XMLHTTP.3.0',
'MSXML2.XMLHTTP',
'Microsoft.XMLHTTP');
for (var i=0; i%26lt;XmlHttpVersions.length %26amp;%26amp; !xmlHttp; i++)
...{
try

...{
// try to create XMLHttpRequest object
xmlHttp = new ActiveXObject(XmlHttpVersions[i]);
}
catch (e) ...{}
}
}
if (!xmlHttp)
alert("Error creating the XMLHttpRequest object.");
else
return xmlHttp;
}

//向id为show的div显示server端响应的正确
function display(message)
...{
showDIV=document.getElementById("show");
showDIV.innerHTML=message;
}
//向id为show的div显示server端响应的错误信息
function displayError(errormessage)
...{
//显示出错信息
display("Error retrieving the new message!%26lt;br/%26gt;"+errormessage);
}

///////////////////////////////////////////////////////////
//2. event-driven函数(keyup函数)
///////////////////////////////////////////////////////////
var keyupAddress="php/suggest.php?action=keyup%26amp;keyword=";
function keyup()
...{
if(xmlHttp)
...{
//server不忙的时候发送异步请求
if(xmlHttp.readyState==0||xmlHttp.readyState==4)
...{
try
...{
var keyword=document.getElementById("keyword").value;
//发出异步请求
xmlHttp.open("GET",keyupAddress+keyword,true);
xmlHttp.onreadystatechange=handlereadystatechange;
xmlHttp.send(null);
}
catch(e)
...{
displayError(e.toString);
}
}
}
}
///////////////////////////////////////////////////////////
//3. 回调函数,server端响应状态发生变化后激发该函数
///////////////////////////////////////////////////////////
function handlereadystatechange()
...{
if(xmlHttp.readyState==4)
...{
if(xmlHttp.status==200)
...{
try
...{
//获取server端响应
var xmlResponse = xmlHttp.responseXML;
suggestArray=xmlResponse.getElementsByTagName("suggest");
var showText="";
for(var i=0;i%26lt;suggestArray.length;i++)
...{
var textNodes=suggestArray[i].getElementsByTagName("text");
var timesNodes=suggestArray[i].getElementsByTagName("times");
for(var j=0;j%26lt;textNodes.length;j++)
...{
showText+=textNodes[j].childNodes[0].nodeValue+" ("+timesNodes[j].childNodes[0].nodeValue+") %26lt;br /%26gt;";
}
}
//显示响应到页面中
display(showText);
}
catch(e)
...{
displayError(e.toString());
}
}
}
}
///////////////////////////////////////////////////////////
//2. event-driven函数(search函数)
///////////////////////////////////////////////////////////
var searchAddress="php/suggest.php?action=search%26amp;keyword=";
function search()
...{
if(xmlHttp)
...{
//server不忙的时候发送异步请求
if(xmlHttp.readyState==0||xmlHttp.readyState==4)
...{
try
...{
var keyword=document.getElementById("keyword").value;
//发出异步请求
xmlHttp.open("GET",searchAddress+keyword,true);
xmlHttp.onreadystatechange=handlereadystatechange;
xmlHttp.send(null);
}
catch(e)
...{
displayError(e.toString);
}
}
}
}

最后注重Server端PHP脚本的程序架构(suggest.php为Server端的主要处理函数,另外suggest.class.php,error_handler.php,config.php等)
suggest.php(获取Client端的参数,并调用suggest类的两个方法,生成XML格式的响应发回到Client端)
%26lt;?php
require_once('suggest.class.php');
header('Content-Type: text/xml');
//确定用户浏览器不会缓存结果
header('Expires: Wed, 23 Dec 1980 00:30:00 GMT');
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT' );
header('Cache-Control: no-cache, must-revalidate');
header('Pragma: no-cache');

$action=$_GET['action'];
$keyword=$_GET['keyword'];

$oSuggest=new suggest();
if($action=='keyup'%26amp;%26amp;$keyword!='')
{
$suggestXML=$oSuggest-%26gt;getSuggests($keyword);
}
if($action=='search'%26amp;%26amp;$keyword!='')
{
$suggestXML=$oSuggest-%26gt;submitKeyword($keyword);
}
echo $suggestXML;
?%26gt;suggest.class.php类
%26lt;?php
require_once('error_handler.php');
require_once('config.php');
class suggest
{
//成员变量
private $conn;

//构造函数, 数据库链接
function __construct()
{
$this-%26gt;conn=new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_DATABASE);
if (mysqli_connect_errno()) {
printf("Connect failed: %s ", mysqli_connect_error());
exit();
}
}
//析构函数,断开数据库链接
function __destruct()
{
$this-%26gt;conn-%26gt;close();
}
//getSuggests成员函数(该函数主要响应Client端action=keyup,即用户正在输入时候的异步请求)
public function getSuggests($keyword)
{
//产生suggest(产生数据库中与所输入要害词前半部分相同的要害词)
$suggest_query='select * from keywords where keyword like ''.$keyword.'%' order by times desc limit 5';
$suggest_result=$this-%26gt;conn-%26gt;query($suggest_query);
$suggest_num=$suggest_result-%26gt;num_rows;

$strOUT='%26lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?%26gt;';
if($suggest_num==0)
{
//$strOUT=$strOUT.'%26lt;suggests%26gt;%26lt;suggest%26gt;%26lt;text%26gt;'.$keyword.'%26lt;/text%26gt;%26lt;times%26gt;0%26lt;/times%26gt;%26lt;/suggest%26gt;%26lt;/suggests%26gt;';
}
else
{
$strOUT=$strOUT."%26lt;suggests%26gt;";
for($i=0;$i%26lt;$suggest_num;$i++)
{
$suggest_row = $suggest_result-%26gt;fetch_row();
$strOUT=$strOUT.'%26lt;suggest%26gt;%26lt;text%26gt;'.$suggest_row[1].'%26lt;/text%26gt;%26lt;times%26gt;'.$suggest_row[2].'%26lt;/times%26gt;%26lt;/suggest%26gt;';
}
$strOUT=$strOUT.'%26lt;/suggests%26gt;';
}
return $strOUT;
}
//submitKeyword成员函数(该函数主要响应Client端action=search,即用户点击search时候的异步请求)
public function submitKeyword($keyword)
{
$select_query='select * from keywords where keyword=''.$keyword.''';
$select_result=$this-%26gt;conn-%26gt;query($select_query);
$select_num=$select_result-%26gt;num_rows;

//碰到新的keywords添加到数据库中,碰到已有的keywords增加次数
$strOUT='%26lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?%26gt;';
//已经存在,增加次数
if($select_num!=0)
{
$select_row = $select_result-%26gt;fetch_row();
$times_now=$select_row[2];
$times_now=$times_now+1;
$update_query='update keywords set times ='.$times_now.' where keyword=''.$keyword.''';
$update_result=$this-%26gt;conn-%26gt;query($update_query);
$strOUT=$strOUT.'%26lt;suggests%26gt;%26lt;suggest%26gt;%26lt;text%26gt;'.$keyword.'%26lt;/text%26gt;%26lt;times%26gt;'.$times_now.'%26lt;/times%26gt;%26lt;/suggest%26gt;%26lt;/suggests%26gt;';
}
else
{
//不存在保存插入
$insert_query='insert into keywords(keyword, times) values (''.$keyword.'',1)';
$insert_result=$this-%26gt;conn-%26gt;query($insert_query);
$strOUT=$strOUT.'%26lt;suggests%26gt;%26lt;suggest%26gt;%26lt;text%26gt;'.$keyword.'%26lt;/text%26gt;%26lt;times%26gt;1%26lt;/times%26gt;%26lt;/suggest%26gt;%26lt;/suggests%26gt;';
}
return $strOUT;
}
}
?%26gt;
最后两个函数,config.php保存应用程序配置信息(如 数据库配置信息)
%26lt;?php
define('DB_HOST', 'localhost');
define('DB_USER','phpajaxuser');
define('DB_PASSWORD','phpajaxuser');
define('DB_DATABASE','phpajax');
?%26gt;
error_handler.php保存自定义异常处理
%26lt;?php
//设置用户自定义错误处理函数
set_error_handler('error_handler', E_ALL);

function error_handler($errNo,$errStr,$errFile,$errLine)
{
if(ob_get_length()) ob_clean();

$error_message='ERRNO: '.$errNo.chr(10).'TEXT: '.$errStr.chr(10).'LOCATION: '.$errFile.' Line: '.$errLine;
echo $error_message;
exit;
}
?%26gt;最后还需要sql语句向数据库中添加用于保存keywords的database
CREATE TABLE `keywords` (
`id` int(10) unsigned NOT NULL auto_increment,
`keyword` varchar(32) NOT NULL default '',
`times` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;

6.关于PHP的小知识,以后研究
PHP从远程服务器读取数据的方法(有点儿类似Web Crawl)
file_get_contents;
或者CURL%26lt;Client URL Library%26gt;www.php.net/curl