Notes On <AJAX & PHP> - 1

85 篇文章 0 订阅
58 篇文章 0 订阅

在《AJAX and PHP - Building Responsive Web Applications》一书中,由第四章开始,每章都是一个case study。


ajax-php-cover


AJAX的表单验证


这一章描述了一个基本的动态异步表单验证的做法,这个过程里面最有启发的是作者关于“线程安全”的提醒:


Thread-Safe AJAX


A piece of code is thread-safe if it functions correctly during simultaneous execution by multiple threads. This chapter contains the first example where an external factor—the user—directly influences the AJAX requests. We need to make an asynchronous request to the server to validate the entered data every time the user leaves an input box or changes a selection.


The hidden danger behind this technique is only revealed if the user moves very quickly through the input fields, or the server connection is slow; in these cases, the web application would attempt to make new server requests through an XMLHttpRequest object that is still busy waiting for the response to a previous request (this would generate an error and the application would stop functioning properly).


Depending on the circumstances at hand, the ideal solution to this problem may be:


  • Create a new XMLHttpRequest instance for every message you need to send to the server. This method is easy to implement, but it can degrade server's performance if multiple requests are sent at the same time, and it doesn't guarantee for the order in which you receive the responses.
  • Record the message in a queue and send it later when the XMLHttpRequest object is able to make new requests. The requests are made in the expected order. Using a queue is particularly important in applications where the order of the messages is important.
  • Schedule to automatically retry making the request after a specified amount of time. This method is similar to the one with the queue in that you don't make more than one server request at a time, but it doesn't guarantee for either the order in which the requests are made, or for the order in which the responses are received.
  • Ignore the message.

In this chapter, for the first time in the book, we'll choose to implement a message queue. When the user leaves an input element, a message to validate its value is added to the queue. When the XMLHttpRequest object is clear to make a new request, it takes the first message from the queue.


The queue is a First-In, First-Out (FIFO) structure, which guarantees that the messages are sent in the proper order. To get a feeling about how this works, go to the demo page for this chapter (or implement the code), and press tab quickly multiple times, and then wait to see how the validation responses show up one by one.


…………


简单来讲,即是我们需要在代码里明确地在每次使用一个XHR对象前对其可用性进行判断,否则异步传输的功能将会混乱。而作者提出的解决方案中较为中肯的是通过使用数据结构中的“队列”来存储没能及时发送的请求。


另外一个勘误就是:案例项目中文件validate.class.php内,使用的eregi()函数已经被PHP5舍弃,所以如果直接运行,将会报错,解决办法是将这个函数换成preg_match()函数,具体参见:


How to fix ‘Function eregi() is deprecated’ in PHP 5.3.0?


下载链接


Ajax聊天功能


这章的Ajax代码仍然体现着前一章中的“thread safe”。在chat.js文件里,有两个XHR对象,一个用来与服务器获取聊天信息message(xmlHttpGetMessages),一个用来获取色板上面的颜色值(xmlHttpGetColor)。

顺便也贴出来用js创建XHR的跨浏览器的方法,很多教材都有这段代码:

/* creates an XMLHttpRequest instance */
function createXmlHttpRequestObject() 
{
	// will store the reference to the XMLHttpRequest object
	var xmlHttp;
	// this should work for all browsers except IE6 and older
	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");
		// try every prog id until one works
		for (var i=0; i<XmlHttpVersions.length && !xmlHttp; i++) 
		{
			try 
			{ 
				// try to create XMLHttpRequest object
				xmlHttp = new ActiveXObject(XmlHttpVersions[i]);
			} 
			catch (e) {}
		}
	}
	// return the created object or display an error message
	if (!xmlHttp)
		alert("Error creating the XMLHttpRequest object.");
	else 
		return xmlHttp;
}

这段代码同时也是try-catch语句的实际使用的不错范例。


获取色板图片上鼠标按下的点的逻辑非常简单,鼠标按下事件里,计算鼠标在图片上的本地位置:

// browser specific 
if(window.ActiveXObject)
{
	mouseX = window.event.x + document.body.scrollLeft;
	mouseY = window.event.y + document.body.scrollTop;		
}
else
{
	mouseX = e.pageX;
	mouseY = e.pageY;
}

// initialize the offset position with the mouse current position 
var offsetX = mouseX;
var offsetY = mouseY;
// get references 
var oPalette = document.getElementById("palette");
var oTd = document.getElementById("colorpicker");	
// compute the offset position in our window
if(window.ActiveXObject)
{
	offsetX = window.event.offsetX;
	offsetY = window.event.offsetY;	
} 
else 
{
	offsetX -= oPalette.offsetLeft + oTd.offsetLeft;
	offsetY -= oPalette.offsetTop + oTd.offsetTop;
}

然后将这个位置通过xmlHttpGetColor发给服务器的get_color.php:

// call server asynchronously to find out the clicked color
try
{
	if (xmlHttpGetColor.readyState == 4 || 
			xmlHttpGetColor.readyState == 0) 
	{
		params = "?offsetx=" + offsetX + "&offsety=" + offsetY;				
		xmlHttpGetColor.open("GET", getColorURL+params, true);		
		xmlHttpGetColor.onreadystatechange = handleGettingColor;
		xmlHttpGetColor.send(null);		
	}
}
catch(e)
{
	// display error message
	displayError(xmlHttp.statusText);
}

get_color.php所做的事情是利用GD的函数获取一个图片的某个像素的RGB颜色值,核心代码:


$img = imagecreatefrompng($imgfile);
...
// get the clicked color
$rgb = ImageColorAt($img, $offsetx, $offsety);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;


get_color.php返回的是颜色值,当xmlHttpGetColor得到返回值时,便尝试将网页中相应控件的属性进行更新。


下面來看看獲取信息的部份,即關於xmlHttpGetMessages


它發出的HTTP請求的參數的格式為:


< mode = SendAndRetrieveNew | RetrieveNew | DeleteAndRetrieveNew > | [&id = (value)] | [&color = (value)] | [&name = (value)] | [&message = (value)]

mode表示的是一個動作指令,告訴chat.php應該執行怎樣的操作。


實際上,作者考慮到這三種情況:


1. 發送一個新的message同時獲取lastMessageID之後新的所有message,這時的參數為:

// if we need to send and retrieve messages
params =	"mode=SendAndRetrieveNew" +
					"&id=" + encodeURIComponent(lastMessageID) + 
					"&color=" + encodeURIComponent(currentColor) + 
					"&name=" + encodeURIComponent(currentUser) + 
					"&message=" + encodeURIComponent(oCurrentMessage.value);

2. 清除所有message而不發送任何參數:

params = "mode=DeleteAndRetrieveNew";

3. 非前兩種情況下的默認情況,沒有新信息發送,只是獲取lastMessageID之後新的所有message:

params = "mode=RetrieveNew" + "&id=" +lastMessageID;

至於這三種情況分別是被怎樣產生的,關鍵是一個叫cache的數組,存放著請求後面的參數,它扮演的角色就是在闡述“線程安全”的方案時採用的“隊列”(queue),requestNewMessages()負責每次發送請求時從cache里拿出一個最舊的參數,而當沒有參數時,就是第三種情況出現了。所以說隊列里只會出現前兩種情況的參數,它們是被sendMessage()和deleteMessages()插進去的。它們的關係可以描述成:

ajax-chat-overall-structure


requestNewMessages()由一開始init()觸動之後,它就會不斷地再次調用自己,不論HTTP請求成功返回與否。而sendMessage()和deleteMessages()是由頁面中的按鈕被按下時觸動的。


而服務器上,分別針對這三種請求做不同的處理:


SendAndRetrieveNew:插入一條信息到數據庫;

DeleteAndRetrieveNew:清空數據表,將id字段的增長起始值置 0,詳見MySQL中的truncate操作。

RetrieveNew:作為默認情況,此操作是一個永遠都會被執行的操作,但放在針對前兩種情況所做的處理之後。


然後輸出XML數據返回給客戶端。作者將chat的數據庫訪問操作封裝到一個chat類中,為此,retrieveNewMessages($id),postMessage($name, $message, $color)和deleteMessages()是分別針對mode的三個指令的三個方法。

if($mode == 'SendAndRetrieveNew')
{
	...
	
	// check if we have valid values 
	if ($name != '' && $message != '' && $color != '') 
	{
		// post the message to the database	   
		$chat->postMessage($name, $message, $color); 
	}
}
// if the operation is DeleteAndRetrieve
elseif($mode == 'DeleteAndRetrieveNew')
{
	// delete all existing messages
	$chat->deleteMessages();         
}
// if the operation is Retrieve
elseif($mode == 'RetrieveNew')
{
	...   
}

...

echo $chat->retrieveNewMessages($id);

而lastMessageId是一個用來追蹤當前更新的信息索引的變量,它同時也是用來判斷清除操作的依據:


lastid-round-trip


在retrieveNewMessages()中,如果lastId是一個有效值(大於0),則返回晚於它的所有信息,否則便執行一個默認操作,即返回數據表當下前五十條信息,接下來判斷是否數據庫被清除,而插入相應的標記,最後再將從數據庫中返回的信息記錄插進XML里返回給客戶端。


其實這個示例並不能滿足真正的聊天,當客戶端有兩個時,如果一方的請求回應時間過長,而另一方頻繁做清除操作,那麼前者將看不見應該看見的信息,當然作為一個簡單的示例,這個問題可以理解。


不過,這個程序的邏輯仍然忽略了一個問題,而可能會帶來異常:




感谢提供您的代码。根据您提供的代码,我注意到您在 `<actions>` 标签中缺少了一个闭合的 `</action>` 标签,导致 XML 结构错误。请将您的代码修改如下: ```xml <idea-plugin> <id>com.your.company.unique.plugin.id</id> <name>wkx_plugin</name> <version>1.0</version> <vendor email="wangkexin6@yourcompany.com" url="http://www.hikvision.com">HikVision</vendor> <!-- description><![CDATA[ xml和excel互相转换插件.<br> www.wkx666.com</description --> <!-- change-notes><![CDATA[ Add change notes here.<br> most HTML tags may be used ]]> </change-notes--> <!-- please see https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description --> <idea-version since-build="173.0"/> <!-- please see https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html on how to target different products --> <depends>com.intellij.modules.platform</depends> <extensions defaultExtensionNs="com.intellij"> <!-- Add your extensions here --> <actions> <action id="myAction" class="com.example.MyAction" text="My Action" description="My description"> <add-to-group group-id="MainMenu.Tools" anchor="last"/> <keyboard-shortcut first-keystroke="control alt A"/> </action> <!-- 添加一个闭合的 </action> 标签 --> </actions> </extensions> <actions> <!-- Add your actions here --> </actions> </idea-plugin> ``` 请确保 `<action>` 标签被正确地嵌套在 `<actions>` 标签内,并且每个 `<action>` 标签都有相应的闭合标签 `</action>`。 完成修改后,重新编译和运行插件,您应该能够在 "Tools" 菜单中看到您的新 action。如果问题仍然存在,请提供更多关于错误信息或其他相关代码的详细信息,以便我们能够更好地帮助您解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值