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; 分別觸發自己的函式)
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
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回應並解析,到網頁中顯示 ///////4. JavaScript事件回應函數(onclick觸發) //取得form中的值 //設定為參數,對Server端的CSparameter.php進行非同步請求 // initiate reading a file from the server //Server狀態改變回呼函數(Server端接受Client端傳來的參數經過邏輯運算後傳回XML回應,Client端會對XML進行解析,並傳回更新到頁面中) if (xmlHttp.readyState == 4) // obtain the XML's document element Server端的PHP腳本(負責接受Client端的非同步請求做出回應,並以XML格式返回Client端) echo ' ?> PHPDOMXML.php(PHP產生XML回應的第二種方法,使用PHP的DOM API,輸出XML格式的回應) 2. client端的與Server端的參數傳遞: //接受Client端非同步請求的參數 //進行邏輯計算 //產生XML格式的回應向Client端傳回 $resultTag=$dom->createElement('result'); $xmlString=$dom->saveXML(); ?> b)但如果將display_errors設定為on後,錯誤將顯示出來,但是不友善的出錯訊息. 4.server端存取資料庫<使用MySQL存取數據,從而實現真正的動態> 5.server端PHP程式封裝與架構(server端php程式引入設計模式) 一個server端引入設計模式的範例:(設計Server端PHP腳本的程式架構,增強擴充性和重複使用) //向id為show的div顯示server端回應的正確 ///////////////////////////////////////////////// ////////// 最後注意:Server端PHP腳本的程式架構(suggest.php為Server端的主要處理函數,另外suggest.class.php,error_handler.php,config.php等) $action=$_GET['action']; $oSuggest=new suggest(); //建構子, 資料庫鏈接 $strOUT=''; function error_handler($errNo,$errStr,$errFile,$errLine) $error_message='ERRNO: '.$errNo.chr(10).'TEXT: '.$errStr.chr(10).'LOCATION: '.$errFile.' Line: '.$errLine; 6.關於PHP的小知識,以後研究:
// 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
";
// obtain a reference to the
myDiv = document.getElementById("show");
// display the HTML output
myDiv.innerHTML = "Server says:
" + 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);
}
}
}
function CSparameter()
...{
// only continue if xmlHttp isn't void
if (xmlHttp)
...{
// try to connect to the server
try
...{
var firstNumber=document.getElementById("firstNumber").value;
var secondNumber=document.getElementById("secondNumber").value;
var param="firstNumber="+firstNumber+"&secondNumber="+secondNumber;
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());
}
}
}
// handles the response received from the server
function handleRequestStateChangePara()
...{
...{
// 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);
}
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
";
// obtain a reference to the
myDiv = document.getElementById("result");
// display the HTML output
myDiv.innerHTML = "Server says:
" + 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);
}
}
}
PHPechoXML.php(PHP產生XML回應的第一種方法,echo輸出XML內容)
//server端的PHP產生XML檔的第一個方法,直接echo出XML
header('Content-Type: text/xml');
// generate XML header
echo '';
$cityArray=array('Paris','London','NewYork','Beijing','Tokoy');
foreach ($cityArray as $city)
{
echo '
}
echo '
header('Content-Type: text/xml');
$cityArray=array('Shanghai','Beijing','Shanxi','Shandong');
//建立一個XML文檔
$dom=new DOMDocument();
//最外層的Tag
$citiesTag=$dom->createElement('cities');
$dom->appendChild($citiesTag);
//裡面的Tag可以透過循環產生
foreach ($cityArray as $city)
{
$cityTag=$dom->createElement('city');
$cityName=$dom->createTextNode($city);
$cityTag->appendChild($cityName);
$citiesTag->appendChild($cityTag);
}
//將XML結構儲存為字串並輸出
$xmlString=$dom->saveXML();
echo $xmlString;
?>
client端的網頁中可以有form,這樣可以將參數傳遞到server端
請參閱範例:
與1一樣,參數傳遞的PHP端的腳本如下CSparameter.php(接受Client端form非同步請求的參數,進行邏輯處理,並產生XML回應發回Client端) //自訂Server端的錯誤處理函數
require_once('error_handler.php');
header('Content-Type: text/xml');
$firstNumber=$_GET['firstNumber'];
$secondNumber=$_GET['secondNumber'];
$result=$firstNumber/$secondNumber;
$dom=new DOMDocument();
$resultsTag=$dom->createElement('results');
$dom->appendChild($resultsTag);
$resultText=$dom->createTextNode($result);
$resultTag->appendChild($resultText);
$resultsTag->appendChild($resultTag);
echo $xmlString;
3. PHP端的錯誤異常(這裡說的錯誤或異常都是指邏輯錯誤)處理:
a)PHP預設情況下,發生錯誤或異常的時候,不會將異常拋出(這是因為php.ini中display_errors的預設置為off,錯誤將被保存在Apache錯誤日誌記錄中),因此編寫起來很難調試。 <往往瀏覽器顯示的錯誤不太容易定位>
http://www.downcodes.com/
c)可以寫自己的PHP錯誤異常處理函數(不一定要求display_errors設定為on),將錯誤以明顯的方式顯示出來,便於調試;
通常自己編寫異常處理函數如下:
顯示定義的Server端錯誤異常拋出函數error_handler.php(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;
}
?>
這已經很經典了,可以使用MySQL或MSSQL、Oracle等
a)開啟資料庫;b)SQL語句Query c)關閉資料庫
a) appname.php <接受client端請求>
b) appname.class.php <將server端的邏輯、資料庫操作、錯誤處理等封裝成類,包含屬性、方法、建構子、析構函式>
c) config.php
d) error_handler.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發送請求
Welcome to Design Pattern PHP AJAX (keywords suggest DEMO)
suggest keywords:
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
try
...{
// try to create XMLHttpRequest object
xmlHttp = new ActiveXObject(XmlHttpVersions[i]);
}
catch (e) ...{}
}
}
if (!xmlHttp)
alert("Error creating the XMLHttpRequest object.");
else
return xmlHttp;
}
function display(message)
...{
showDIV=document.getElementById("show");
showDIV.innerHTML=message;
}
//向id為show的div顯示server端回應的錯誤訊息
function displayError(errormessage)
...{
//顯示出錯訊息
display("Error retrieving the new message!
"+errormessage);
}
//2. event-driven函式(keyup函式)
////////////////////////////////////////////////// /////////
var keyupAddress="php/suggest.php?action=keyup&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
var textNodes=suggestArray[i].getElementsByTagName("text");
var timesNodes=suggestArray[i].getElementsByTagName("times");
for(var j=0;j
showText+=textNodes[j].childNodes[0].nodeValue+" ("+timesNodes[j].childNodes[0].nodeValue+")
";
}
}
//顯示回應到頁面中
display(showText);
}
catch(e)
...{
displayError(e.toString());
}
}
}
}
////////////////////////////////////////////////// /////////
//2. event-driven函數(search函數)
////////////////////////////////////////////////// /////////
var searchAddress="php/suggest.php?action=search&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);
}
}
}
}
suggest.php(取得Client端的參數,並呼叫suggest類別的兩個方法,產生XML格式的回應發回Client端)
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 MYH:i:s').' GMT' );
header('Cache-Control: no-cache, must-revalidate');
header('Pragma: no-cache');
$keyword=$_GET['keyword'];
if($action=='keyup'&&$keyword!='')
{
$suggestXML=$oSuggest->getSuggests($keyword);
}
if($action=='search'&&$keyword!='')
{
$suggestXML=$oSuggest->submitKeyword($keyword);
}
echo $suggestXML;
?>suggest.class.php類
require_once('error_handler.php');
require_once('config.php');
class suggest
{
//成員變數
private $conn;
function __construct()
{
$this->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->conn->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->conn->query($suggest_query);
$suggest_num=$suggest_result->num_rows;
if($suggest_num==0)
{
//$strOUT=$strOUT.'
}
else
{
$strOUT=$strOUT."
for($i=0;$i<$suggest_num;$i++)
{
$suggest_row = $suggest_result->fetch_row();
$strOUT=$strOUT.'
}
$strOUT=$strOUT.'
}
return $strOUT;
}
//submitKeyword成員函數(此函數主要回應Client端action=search,即使用者點擊search時候的非同步請求)
public function submitKeyword($keyword)
{
$select_query='select * from keywords where keyword=''.$keyword.''';
$select_result=$this->conn->query($select_query);
$select_num=$select_result->num_rows;
//遇到新的keywords加入資料庫中,遇到已有的keywords增加次數
$strOUT='';
//已經存在,增加次數
if($select_num!=0)
{
$select_row = $select_result->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->conn->query($update_query);
$strOUT=$strOUT.'
}
else
{
//不存在保存插入
$insert_query='insert into keywords(keyword, times) values (''.$keyword.'',1)';
$insert_result=$this->conn->query($insert_query);
$strOUT=$strOUT.'
}
return $strOUT;
}
}
?>
最後兩個函數,config.php保存應用程式設定資訊(如資料庫設定資訊)
define('DB_HOST', 'localhost');
define('DB_USER','phpajaxuser');
define('DB_PASSWORD','phpajaxuser');
define('DB_DATABASE','phpajax');
?>
error_handler.php保存自訂異常處理
//設定使用者自訂錯誤處理函數
set_error_handler('error_handler', E_ALL);
{
if(ob_get_length()) ob_clean();
echo $error_message;
exit;
}
?>最後還需要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 ;
PHP從遠端伺服器讀取資料的方法(有點類似Web Crawl):
file_get_contents;
或CURL