基于AJAX的聊天程序教程(part I)
时间:2006-12-06
来源:互联网
AJAX planetphp Tutorials Zend Framework zendhomepage
by maugrim | 0 comments | Monday, November 20, 2006
概述
实话实说,创建一个聊天的程序不是一个很艰难的程序!
写这篇文章的目的是教你一步一步地用XML作为传输载体来创建一个轻量级的聊天程序。这是一个很有趣的应用,而我写这篇文章的动机也是来自于我平时很喜欢PHP。同时,通过业余时间的钻研,我发现通过Web提供了一个稳定的论坛以后,人们往往希望通过Web能够时间即时的聊天室。这是因为虽然IRC和其它的一些即时的聊天工具已经很普及了,但是许多的用户仍然喜欢基于Web的聊天方式。原因有许多,比如这些基于Web的聊天工具可以通过一些合作的代理与那些即时聊天工具联系起来,或者Web的聊天工具使用很方便。
本文讨论的聊天程序的源代码你可以通过这两个链接进行下载:chat.tar.gz / chat.zip。源代码在本文中基本上都讨论到了(除了一些随后附加的补丁)。
为了创建这个聊天程序,我决定采用标准的第三方的库。因为我觉得既然这些第三方的库已经很好地解决了问题,我就没有必要自己来重新编写。可能有些人会认为使用这些库我们的程序就不是原创的,但是我们不必去理会这种没有意义的争论。
在服务器端,我决定采用Zend Framework 0.20 (发布于2006年10月31号)。客户端那边,通过采用原型的库,我们可以简化AJAX和Javascript的处理。其它的资源我决定使用Scriptaculous。当然,你可以使用其它的如jQuery, Dojo or 或者其它类似的库来实现。
这次我们编写的聊天工具主要基于Zend Framework (PHP5),Javascript和协议库实现,目的是创建一个简单的便捷的聊天程序。另外,我们提供的源代码使用的是新的BSD的许可证,读者可以根据自己的需要自由地修改它。其它的细节我们就不讨论了,我们开始吧!
Zend Framework 介绍
Zend Framework(Zend的开发框架) 虽然出现的时间不长,但是很快就成为我最喜欢的PHP库之一了。从个人的角度来说,我不是很喜欢一些大型的框架,因为它们往往构建大型的架构,然后强迫你去使用没有必要的功能。但是,Zend Framework 却是众多库文件的一个包,包里面的每一个类都是单独运行而且易于使用,同时又能够很方便地与我自己的代码结合起来。
你可以从http://framework.zend.com 去下载Zend的代码。另外,使用文档也写得很丰富,同时我从邮件列表了解到Zend的开发框架一直在丰富中。除了官方的文档,我还推荐http://www.akrabat.com/zend-framework-tutorial/ 它是一个不错的使用手册,而且易于阅读。这里我们感谢Rob Allen为此所作的贡献。
文件夹分布
首先,下载Zend Framework 的0.20版本,可以从http://framework.zend.com/download 下载。在你的Web的发布目录下创建一个新的文件夹叫 "chat-tutorial"。在这个新的文件夹里面,我们将创建新的资文件夹以便放置这个聊天程序需要的各种脚本。我们推荐使用的文件夹分布如下:
chat-tutorial/
/application
/controllers
/views
/library
/incubator
/public
/javascript
/styles
/javascript
/data
从网上下载的Zend开发框架的“ ./library/Zend”文件夹里面的文件需要拷贝到我们的“library”文件夹里面。所以现在“chat-tutorial/library”文件夹里面包含了一个Zend文件夹,里面包含了核心的库文件和Zend.php。你还可以单独下载"incubator"文件夹。"incubator"文件夹里面包含了Zend Framework里面的新的组件。这些组件很多是未定版、未注释的,但是最起码它们都是公开发布的版本。在0.20版本中,"incubator"包含了新的MVC组件,而且在本次的聊天室程序中我们会用到它。
把“./incubator/library” 文件夹复制到我们的“./library/incubator”文件夹当中。
我们将把“./application”文件夹作为我们存放程序的主要的一个文件夹。例如,我们要用它存放controllers 和 models。所谓的controllers (控制器)就是一个包含了应用主逻辑的类。它包含了最高运行级别的代码,并引入许多功能库文件、处理模型、建立视图和对用户数据进行过滤/认证有效性。这些应用主逻辑被分开放置在具体的类的方法中,我们通过访问具有特殊意义的URL(脚本文件名)来引入这些控制器和方法。
如果刚才的讲述中,让你有些不明白,不用担心。我们很快用具体的例子来阐述。
Library的文件夹当中,我们将存放所有的库文件,例如Zend Framewrok(包括了核心文件和扩展功能文件)。这里我们暂时不需要其它的PHP库文件,但是请注意,如果你在其它别的项目中同时使用到Zend Framework和别的PHP库的时候,你可以把那些PHP库文件也放入Library文件夹当中。
在“public”的文件夹当中,我们放置所有需要的脚本文件。这些脚本文件都可以通过URL在Web中访问到。它们包括图片文件、css文件和javascript脚本文档。另外,为了便于管理和使用,我增加了一个javascript脚本文件的根目录,并把我们自己创建的javascript脚本文件放置进去。从本质上说,你需要了解“public”和“javascript”两个目录是用户唯一能够访问的目录。其它所有的脚本文件必须放置在用户无法访问的目录当中(我们不希望用户能访问到)。所以我们将对其它的目录设置访问权限―这是出于最低的安全级别考虑的。我们可以通过配置Apache的.htaccess文件来实现这个权限控制。
最后,因为我们将采用XML来作为数据的存储载体来传递聊天信息。所以我们使用“./data”文件夹来存储XML文件。所以为了保证Apache(PHP的运行脚本程序)能够在这个目录中写文件,你需要注意Apache在这个目录的权限设置。这里我们建议只允许PHP来写XML文件。当然,如果你不是很了解Linux,你也可以简单地把目录的权限设置为777。Windows平台的开发工程师就无需要注意这点了。
从Zend Framework使用开始我们的聊天程序
Zend Framework运行的概念是通过调用的url包含的字符串去找到一个相对应的类,即控制器。我想你能猜到了,包含控制器的类的脚本文件都被放置./application/controllers目录当中。一个控制器类包含了运行动作的许多方法,也是因为这个原因我们刚才叫它们动作。例如,这样的一个urlhttp://www.example.com/chat/refresh可能会指向RefreshAction()方法。而这个方法所在的类是在"application/controllers"目录下的ChatController.php文件中声明的。值得注意的是通过url的格式Zend Framework是怎么找到控制器并找到方法的呢。这个技术上的映射过程,我们在这里就不详细讨论了(你可以从官方的技术文档或者手册看到详细的说明)。
这个映射过程是由Zend_Controller类来处理的,它用来解析所有的url地址。为了让Zend_Controller类来解析所有的url,所有的请求都必须通过一个统一的index.php文件(映射文件)来实现。这个映射文件所做的事情就是启动这个Framework并且把其它的初始化需要的代码加入进来,例如我们通过url寓意的。在这里我们同样需要.htaccess文件来保证所有的访问都指向index.php文件。这种把所有的请求转向到一个固定的入口的想法来源于前段控制器设计模式。如果你并不是很了解这个思想,你可以通过google了解下它具体的信息,而且如果你是一个好奇心强的人的话,你会喜欢上它的。
首先,关于.htaccess文件。我们需要在根目录“chat-tutorial”创建一个.htaccess文件。文件的内容是:
RewriteEngine on
RewriteCond %{REQUEST_URI} !/public.*
RewriteCond %{REQUEST_URI} !/javascript.*
RewriteRule .* index.php
php_flag magic_quotes_gpc off
php_flag register_globals off
以上的设置告诉Apache使用mod_rewrite模块来进行转向处理。当一个请求访问本目录或者本目录下任何一个子目录的时候,Aapche将自动进行转向。配置中的RewriteRule配置告诉Apache转向的地址是index.php(也就是我们实现固定转向的地方)。最后两个php_flag的条目的配置比较简单,它们只是确保“magic_quotes” 和 “register_globals”这两个PHP的配置关闭的。这样的配置是用来降低潜在的安全风险,但是我们这样做的目的是无论是有意还是无意,我们不用依靠系统自己的配置是否关闭。当然,在PHP6发布以后,这两个功能将被彻底关闭。我想Web安全专家听到这个消息后,会放心地去参加聚会了…
另外,我们在.htaccess中还有两个条件规则。它告诉mod_rewrite模块,当请求./public或者./javascript的时候,不需要进行转向处理。所以放置在这两个目录下面的文件是可以被直接访问的,而且这两个目录下面的文件对项目本身不能有什么安全的风险。
在这里,index.php这个映射文件基本上就是在做初始化、加载类或者是对我们调用Zend Framework的类进行初始化。
让我们来看看index.php脚本的代码:
php
<?php
/**
* Bootstrap file for Chat Tutorial
*/
/*
* The basics...
*/
error_reporting(E_ALL|E_STRICT);
ini_set('display_errors', 1); //disable on production servers!
date_default_timezone_set('Europe/London');
/*
* Start our session
*/
session_start();
/*
* Setup the include_path to the ZF library.
* We set the incubator first so the
* incubator classes are loaded in preference
* to core ZF classes where two versions exist.
*
* When 0.21 is released, the MVC classes in
* Incubator will move to the core library.
*/
set_include_path(
'./library/incubator/library'
. PATH_SEPARATOR . './library'
);
include 'Zend.php';
/*
* Use Zend::loadClass() to load essentials
* Probably a good idea to use require_once()
* elsewhere to avoid unnecessary coupling.
*/
Zend::loadClass('Zend_Registry');
Zend::loadClass('Zend_Controller_Front');
Zend::loadClass('Zend_Controller_RewriteRouter');
Zend::loadClass('Zend_View');
/*
* Load and register our View for later use
*/
$view = new Zend_View();
$view->setScriptPath('./application/views');
Zend_Registry::getInstance()->set('view', $view);
/*
* Instantiate a Request to set BaseURL
* See later...
*/
$request = new Zend_Controller_Request_Http();
/*
* Instantiate a RewriteRouter
*/
$router = new Zend_Controller_RewriteRouter();
/*
* On my platform, I need to set the BaseURL for ZF 0.20
* RewriteBase is assumed to be $_SERVER['PHP_SELF'] after
* removing the trailing "index.php" string.
*
* PHP_SELF can be user manipulated. Avoided using SCRIPT_NAME
* or SCRIPT_FILENAME because they may differ depending on SAPI
* being used.
*/
$base_url = substr($_SERVER['PHP_SELF'], 0, -9);
$request->setBaseUrl($base_url);
/*
* Setup and run the Front Controller
*
* Set Controller Dir, add the RewriteRouter, dispatch the
* modified Request (with updated BaseURL) and finally
* get the resulting Response object.
*/
$controller = new Zend_Controller_Front;
$controller->setControllerDirectory('./application/controllers');
$controller->setRouter($router);
$response = $controller->dispatch($request);
/*
* By default Exceptions are not displayed
* That won't do during development.
* Remove this in a live environment though!
*
* $response->renderExceptions(true) will not
* work, it's broken and was fixed in SVN for
* next release. Until then...
*/
if($response->isException())
{
echo $response->getException();
exit; // Stop here - ;)
}
/*
* Echo the response (with headers) to client
* Zend_Controller_Response_Http implements
* __toString().
*/
echo $response;
?>
大多数的映射文件都非常简单,容易理解。
我们把display_errors设置打开,这样我们就能看到所有的错误信息和意料之外的异常情况。错误报告级别设置为E_ALL|E_STRICT,因为我们不去捕捉功能性或者不安全的操作导致的错误(比如没有初始化变量)。在这里我们还设置了默认的时区,因为PHP5.1开始不再直接读取服务器的时间。此外,我们启动一个PHP的Session功能用来存放session数据,实现不同请求之间数据的传递。我们还设置了include的路径来引入./library目录。最后,因为这里我们不需要用到数据库,所以这里我没有把./application/models目录也加进去。否则在通常的情况下,我们是会加进去的。
接下来一个部分就是Zend Framework自己的映射处理过程了。我们先加载基础的Zend类并且通过它来加载其它需要的类。Zend::loadClass()静态方法用来引入需要的PHP脚本文件。和以前加载类文件的时候,文件名用下划线区分出路径和文件名不同(这个在PEAR中我们经常可以见到),它直接加载那些文件。所以这个加载文件的处理方式非常简单实用。
我们还启动Zend_View并且把路径设置到我们的模版(或者称之为视图)路径,在这里就是“./application/views”。这里视图的叫法听起来不容易理解但是它们就是一些简单的HTML模版文件,在里面包含了PHP代码以便控制插入具体的数据。如果你曾经使用过Smarty、Template-Lite或者Savant,你应该对这个概念不是很陌生才是。
在最后一个部分,我们启动控制器,加入RewriteRouter类并且基于Request类设置一个基础的url。最后控制器一次调用匹配于访问请求的url的控制器类和动作方法(使用默认的Zend_Controller_RewriteRouter规则":controller/:action/*")。当然你也可以定义另一个映射过程,比如增加参数,修改参数默认值或者加上必要条件。但是我们这里并不需要一个特殊的RewriteRouter。如果你想了解的话,可以查看Zend Framework的Web Services的RESTful的更多例子。
在使用0.20,我发现我们经常需要告诉Framework我们在webroot目录下的位置。所以我们可以使用setBaseUrl()方法方便地告诉Framework我们的目录结构,以便Framework解析任何uri的时候都是正确的。
剩下的就是处理,然后生成返回的结果,然后我们简单地打印出来。
Index 控制器
因为这个聊天的程序非常简单,所以我们将只需要调用一个控制器。另外,为了确保Zend Framework启动,我们将增加一个非常基础的功能,让它打印出"Hello World!"。这个IndexController 类将被放置在IndexController.php文件当中。而IndexController.php文件我们把它放置在application/controllers目录下。
IndexController.php的代码:
<?php
class IndexController extends Zend_Controller_Action
{
public function IndexAction()
{
/*
* echo() would work also, but let's get used
* to the idea of a Response object.
* This is plain text, so we'll set the content type
* also.
*/
$this->getResponse()->setHeader('Content-Type', 'text/plain');
$this->getResponse()->setBody('Hello World');
}
}
现在,如果访问http://www.example.com/chat-tutorial/ ,屏幕上会打印出"Hello World!" 字样。如果你熟悉响应头信息的话(你可以查看Firefox的WebDeveloper扩展),你会注意到文档类型被定义为text/plain。这里你会发现我们定义文档类型的方式比较复杂。其实通过一个简单的echo命令也能够实现 �C 但是你最好习惯于用标准的响应头信息对象来实现。
最后的工作
到此为止,我们启动了Zend Framework ,并且本章节也要结束了!我们会在后续的章节中具体研究Framework的类,但是ZF并不是我们唯一要重点讨论的。如果你想真的了解ZF的话,我再一次提醒你去看下Rob Allen的手册。
下一章节,我们将测试建立的javascript库。这些javascript脚本将会在以后的聊天程序中被广泛用到。
作者: PHPChina 发布时间: 2006-12-06
作者: dzjzmj 发布时间: 2006-12-06
作者: carra 发布时间: 2006-12-07


作者: fengyun 发布时间: 2006-12-08
作者: 水镜 发布时间: 2006-12-08
作者: cator 发布时间: 2006-12-08


作者: Nickboy 发布时间: 2006-12-08
作者: weiwei 发布时间: 2006-12-10
热门阅读
-
office 2019专业增强版最新2021版激活秘钥/序列号/激活码推荐 附激活工具
阅读:74
-
如何安装mysql8.0
阅读:31
-
Word快速设置标题样式步骤详解
阅读:28
-
20+道必知必会的Vue面试题(附答案解析)
阅读:37
-
HTML如何制作表单
阅读:22
-
百词斩可以改天数吗?当然可以,4个步骤轻松修改天数!
阅读:31
-
ET文件格式和XLS格式文件之间如何转化?
阅读:24
-
react和vue的区别及优缺点是什么
阅读:121
-
支付宝人脸识别如何关闭?
阅读:21
-
腾讯微云怎么修改照片或视频备份路径?
阅读:28