显示标签为“php”的博文。显示所有博文
显示标签为“php”的博文。显示所有博文

2008年10月9日星期四

网页游戏开发简明教程

研究所有点缺乏资料啊。讨论的问题怪怪的。只有很少是能用到的。

一、简单的程序框架。
webgame程序构成:
三大部分。
第一是数据流程。第二是程序。第三是美术。
其中,数据流程包括了功能。也只有在功能中才能体现数据流程。

数据流程相当的麻烦,后面再讨论。
比如最简单的卖买产品。
要实现这个功能。
那么需要有产品基础表、产品详细表、商店表、背包表。如果扩展性更强,相应的双表是少不不了的。
表的问题都简单了。关键是这个物品有什么用。这样物品的来源,一大堆数据,物品的走向,又是一大堆数据。
最后,这些数据得绕成一个圈。
绕圈是一件困难的事情。特别是功能和道具多了起来的时候。难度是2的n次方。


美术:

UI。简洁漂亮的界面总会有好处。

小图标。道具,地图,装备。一类至少10个吧?大体上百把个是需要的。


程序分5个部分:
服务器定时器。(C语言或自己设定服务器)定时循环执行某一段代码。而这段代码主要是根据数据库的数据进行更新。这个可以找个C语言程序员来做。对于C语言程序员来讲,这个功能是相当的简单。当然,具体的处理数据的判断和操作数据库,需要你自己写。让C语言程序员给你段标准代码就行了。完全支持sql语句的。

功能页面、功能函数。主要就是数据存取,判断,数据走向。


ajax函数。(可选)某些需要伪即时的功能要用到。

javascript函数。(可选)模拟客户端的数据计算。也就是webgame的与时间相关的数据。分为两部分。一部分是真实数据,是由服务器端的定时器计算的。另一部分是只有初始值,客户端显示用的。不需要即时同步,仅仅需要模拟同步就行。


数据库。一大堆基础数据表和详细数据表。基础数据表:比如等级1到等级100的用户的属性初始值。详细数据表:每个用户的具体属性。

二、一个详细的例子。

单纯的讨论数据流程是件痛苦的事情。
讨论程序而不给代码也是比较痛苦。
这里用的是php+mysql的。

那就按一个超简单的webgame的方式来讨论。配上适当的代码。应该有所帮助。不足的地方也请大家指出,对我个人也是帮助。

我们不去考虑游戏的可玩性,数值平衡等等问题。我们先只考虑一个简单例子的实现。

那么一个webgame的基本内容需要些什么呢?

数据库:玩家、地图、城市、建筑、武器、士兵。

功能:登陆、升级、个人战斗、士兵之间的战斗、与城市的战斗、修建建筑、打造武器、买卖道具。
(注意:每一个功能,必然对应1个或多个数据表。上面数据库中所列的只是基础中的基础。)


首先是地图、城市、建筑。
这里认为,地图可以有多张,城市在地图上,建筑在城市内。


地图表
Map :Map_ID ,X坐标, Y坐标,City_ID(城市ID),描述。
其中Map_ID是指地图的id。不是自动编号。一张地图就是一个Map_ID,可以重复。

城市表
City:City_ID,城市名字,城市所有人,城市等级,城市资源,描述。

建筑表
Build:ID,City_ID,建筑名称,建筑等级,建筑功能。

其中,地图表确定城市的位置,城市表确定城市的相关数据以及所有人,建筑表内的多条信息属于某一个城市。

建表后,显示出来。
一个for循环。把地图表整个取出来就ok。
跟普通网站的新闻列表没太大区别。不同的是,你需要取得X坐标和Y坐标定位。可以用tabel也可以用div。


class Map//地图类
{
var $Map_ID;
function Map_bg_css($Map_ID) {

$this->Map_ID = $Map_ID;

mysql_select_db($db_name,$link);
$sql="select * from map where Map_ID='".$this->Map_ID."' limit 1";
$result=mysql_query($sql,$link);
echo '<Style type='.'text'.'/'.'css>';
$rs=mysql_fetch_array($result);

echo "#map{";
echo "position:absolute;";
echo "width:".$rs[X坐标]."px;";
echo "height:".$rs[Y坐标]."px;";
echo "z-index:0;";
echo "left:0px;top:0px;}";

}

function Map_bg($Map_ID){

$this->Map_ID = $Map_ID;

$sql="select * from map where Map_ID='".$this->Map_ID."'";
$result=mysql_query($sql,$link);
while($rs=mysql_fetch_array($result))
{
echo "
";
echo "
";

}

}
}

上面是一个很简单的地图类。代码可能不太正确,意思是正确的。就是根据map表中的坐标,生成了一组div层,以及这一组层的css。
你可以改为table的。你可以也把坐标放到一个字段里,用数组的形式取。

使用的时候,用

new map;
map(N);

其中N是map表里的地图Map_ID.

城市内的建筑也类似。如果要显示出来的话。

2008年9月15日星期一

PHP防刷新

session_start();
$k=$_GET['k'];
$t=$_GET['t'];
$allowTime = 1800;//防刷新时间
$ip = get_client_ip();
$allowT = md5($ip.$k.$t);
if(!isset($_SESSION[$allowT]))
{
$refresh = true;
$_SESSION[$allowT] = time();
}elseif(time() - $_SESSION[$allowT]>$allowTime){
$refresh = true;
$_SESSION[$allowT] = time();
}else{
$refresh = false;
}
?>

Discuz 通行证的制作方法和详细说明

通行证是个大题目,说白了也就是如何维护客户的会话,在php中所能用的方法无非几种:

● Cookie
● Session
● POST或GET的方法传值

一般中小型的单服务器应用程序站点,如Discuz论坛,采用的cookie+session+mysql的方法来维持用户会话。

考虑到网站规模和复杂性,例如多台服务器多个应用程序,例如163的通行证系统,登陆成功后会在客户端写一个cookie,里面保存用户名等信息。其他应 用程序,都是通过读取这个Cookie重新将客户带入自己的会话中,重而实现通行证功能。163的这种做法不失为一种好办法。

Discuz! Passport 接口技术文档


从 Discuz! 4.0.0 版本开始,Discuz! 内嵌了一个独特的 Passport(通行证) 接口,利用此接口,用户将很容易将论坛与其他应用程序整合,而实现统一登录与退出、用户数据共享、积分同步等功能。可以整合的应用程序包括内容管理系统(CMS)、商城系统、游戏系统等等,如您对这方面功能有兴趣或有需求,请继续阅读本文档。

Discuz! Passport 的优点

Discuz! Passport 系统使用了 Discuz! 独有的技术,并不等同于以往使用过的一些方法,与传统的实现方式相比,具备(不限于)以下优势:


基于私有密匙的低相关性可逆加密算法,配合 MD5 校检码技术,使得暴力破解或伪造几乎不可能。
应用程序可与论坛放置于不同的服务器及不同的域名下。可基于不同操作系统、不同程序语言和不同数据库平台,具备真正的平台无关性。
不需要任何形式的数据库连接、或强制把两套应用程序的数据放在同一数据库甚至同一数据表中。论坛与应用程序都有各自的用户数据表,只是在需要时进行无缝同步操作。
对应用程序的代码改动简便易行,可最快速的完成应用程序与论坛间的整合。
Discuz! Passport 的局限

您在开始利用 Discuz! Passport 进行二次开发时,需要了解这个系统的局限性,以对未来的工作进行正确的评估与安排。

只能工作在用户密码不加密、可逆加密或 MD5 加密的情况下,否则论坛后台无法登录。
只能与一种应用程序关联,即二方关联。不能实现三方关联或与更多的应用程序进行关联。
应用程序需具有独立的注册、登录、退出页面和链接,否则需要自行修改论坛中的相应表单或程序。
由于论坛的注册人数可能很多,例如百万级以上,且应用程序和论坛间的用户数据是同步的,因此要求应用程序能够稳定的负载大量用户的访问。


Discuz! Passport 原理与流程


假设已设置如下变量或参数

挂接 Discuz! Passport 的应用程序假设为一套 PHP 语言编写的 CMS 系统
Discuz! 的 URL 为 http://www.myforums.com
应用程序的 URL 为 http://www.mywebsite.com
应用程序的注册页面为 http://www.mywebsite.com/register.php
应用程序的登录页面为 http://www.mywebsite.com/login.php?action=login
应用程序的退出页面为 http://www.mywebsite.com/login.php?action=logout
开启通行证后的用户登录流程


如果用户在论坛点击“登录”,则转向到事先设置好的应用程序登录页面(http://www.mywebsite.com/login.php?action=login),并在登录页面的 URL 中加入参数 forward(加入 forward 后的链接例如 http://www.mywebsite.com/login.p ... orums.com/index.php),用于在登录后将用户导向到指定的 URL。


应用程序收到此请求后,按照惯例生成表单,并增加一个表单变量 ,将 GET 方式传递过来的 forward 参数通过表单进行传递。


用户在应用程序的表单中填写登录信息,并提交到应用程序的登录验证程序。应用程序验证用户提交的用户名和密码的合法性:


如果不通过:提示用户名密码错误,要求其返回上一页重新填写。


如果通过,需要进行如下操作:


设置自身 Cookie 或 Session,使得应用程序自身处于登录状态。


检查表单中是否提交了 forward 变量,如有,则意味着登录请求可能是由论坛而来,将此变量传递到后面的请求中;如没有,自行生成 forward 变量,使得论坛登录后能够跳转回到应用程序中。


通过 header('Location: http://www.myforums.com/api/pass ... /yyy&verify=zzz') 的方式,将登录请求传递到论坛进行处理。其中 auth 用来将用户信息与资料以特定的格式,加密传递给论坛,forward 用于告知论坛 Passport API 完成自身操作后转向到的 URL 地址,verify 用于验证前面两个变量的有效性。auth、forward、verify 格式与结构将在后面进行说明。



Discuz! Passport API 在接收到由应用程序通过 header() 提交过来的请求后,进行如下操作:


根据 verify 判断 auth 和 forward 变量是否合法,如合法则继续,否则终止。


将 auth 根据既定算法解密,并还原成数组,数组的内容与格式将在后面进行说明。根据数组中的内容,检查此用户是否存在。如存在,则根据上述数组中的内容 UPDATE 论坛中相应的用户资料;如不存在,则使用数组中的信息 INSERT 到论坛用户资料表中。


论坛设置 Cookie 或 Session,使得论坛自身处于登录状态。


根据应用程序反馈的 forward 值,通过 header('Location: http://xxx') 的形式将页面跳转到 forward 变量指定的 URL。



至此,登录流程结束
开启通行证后的用户退出流程


如果用户在论坛点击“退出”,则转向到事先设置好的应用程序退出页面(http://www.mywebsite.com/login.php?action=logout),并在登录页面的 URL 中加入参数 forward(例如 http://www.mywebsite.com/login.p ... orums.com/index.php),用于在退出后将用户导向到指定的 URL。


应用程序收到此请求后,清除自身 Cookie 或 Session,使得应用程序自身处于非登录状态。


检查是否提交了 forward 变量,如有,则意味着登录请求可能是由论坛而来,将此变量传递到后面的请求中;如没有,自行生成 forward 变量,使得论坛登录后能够跳转回到应用程序中。


通过 header('Location: http://www.myforums.com/api/pass ... /yyy&verify=zzz') 的方式,将退出请求传递到论坛进行处理。其中 forward 用于告知论坛 Passport API 完成自身操作后转向到的 URL 地址,verify 用于验证 forward 变量的有效性。forward、verify 格式与结构将在后面进行说明。


Discuz! Passport API 在接收到由应用程序通过 header() 提交过来的请求后,进行如下操作:


根据 verify 判断 forward 变量是否合法,如合法则继续,否则终止。


清楚论坛的 Cookie 或 Session,使得论坛自身处于非登录状态。


根据应用程序反馈的 forward 值,通过 header('Location: http://xxx') 的形式将页面跳转到 forward 变量指定的 URL。



至此,退出流程结束。
开启通行证后的用户注册流程


如果用户在论坛点击“注册”,则转向到事先设置好的应用程序注册页面(http://www.mywebsite.com/register.php),并在注册页面的 URL 中加入参数 forward(例如 http://www.mywebsite.com/registe ... orums.com/index.php),用于在注册后将用户导向到指定的 URL


应用程序收到此请求后,按照惯例生成表单,并增加一个表单变量 ,将 GET 方式传递过来的 forward 参数通过表单进行传递


用户在应用程序的表单中填写注册信息,并提交到应用程序的注册验证程序。应用程序验证用户提交信息的完整性和合法性:


如果不通过:提示其问题所在,要求其返回上一页重新填写


如果通过,需要进行如下操作:


将用户资料插入到应用程序自身用户数据库中


设置自身 Cookie 或 Session,使得应用程序自身处于登录状态

检查表单中是否提交了 forward 变量,如有,则意味着注册请求可能是由论坛而来,将此变量传递到后面的请求中;如没有,自行生成 forward 变量,使得论坛注册后能够跳转回到应用程序中


通过 header('Location: http://www.myforums.com/api/pass ... /yyy&verify=zzz') 的方式,将注册请求传递到论坛进行处理。其中 auth 用来将用户信息与资料以特定的格式,加密传递给论坛,forward 用于告知论坛 Passport API 完成自身操作后转向到的 URL 地址,verify 用于验证前面两个变量的有效性。auth、forward、verify 格式与结构将在后面进行说明



Discuz! Passport API 在接收到由应用程序通过 header() 提交过来的请求后,进行如下操作:


根据 verify 判断 auth 和 forward 变量是否合法,如合法则继续,否则终止


将 auth 根据既定算法解密,并还原成数组,数组的内容与格式将在后面进行说明。根据数组中的内容,检查此用户是否存在。如存在,则根据上述数组中的内容 UPDATE 论坛中相应的用户资料;如不存在,则使用数组中的信息 INSERT 到论坛用户资料表中


论坛设置 Cookie 或 Session,使得论坛自身处于登录状态


根据应用程序反馈的 forward 值,通过 header('Location: http://xxx') 的形式将页面跳转到 forward 变量指定的 URL



至此,注册流程结束
本部分中,加下划线显示的部分,是需要对您的应用程序进行更改的部分,事实上,这部分更改会非常容易和方便。


Discuz! Passport 参数规格与加密方式


私有密匙(passport_key)

由于一些关键参数采用了 GET 方式进行传递,即便两次 header 跳转并不会直接将链接显示在外面,但我们仍然对关键的参数进行了加密,私有密匙共有两个作用:其一是供下面提到的可逆加密算法(AzDGCrypt)进行 数据的加解密;其二是生成不可逆验证字串(verify),以防止关键信息被伪造。

在启用 Discuz! Passort 后,您需要在应用程序和 Discuz! 后台配置两处私有密匙,这两处的内容必须完全相同,这样应用程序和论坛之间才能正常通信。私有密匙决定了加密算法的强度,因此密匙长度请不要小于 10 个字节,并包含字母、数字和符号,以保证系统的安全。

加密算法

Discuz! Passport 采用 Azerbaijan Development Group(AzDG)开发的可逆加密算法 AzDGCrypt 对用户资料进行加密。如提供正确的私有密匙,可通过本加密算法对数据进行加密及解密,因此只要保证私有密匙的保密性,即可确保数据传递过程中的安全。以下 为 Discuz! Passport 中应用到的可逆加密算法,为了生成可以被 Discuz! Passport 正确解密的 auth 字串,需要将如下函数放置于应用程序中,并可在登录及注册时调用。

passport_encrypt()是加密函数,用法为 passport_encrypt($txt, $key),其中 $txt 是待加密的字串,$key 是私有密匙。
passport_decrypt()是解密函数,用法为 passport_decrypt($txt, $key),其中 $txt 是加密后的字串,$key 是私有密匙。


/**
* Passport 加密函数
*
* @param string 等待加密的原字串
* @param string 私有密匙(用于解密和加密)
*
* @return string 原字串经过私有密匙加密后的结果
*/
function passport_encrypt($txt, $key) {

// 使用随机数发生器产生 0~32000 的值并 MD5()
srand((double)microtime() * 1000000);
$encrypt_key = md5(rand(0, 32000));

// 变量初始化
$ctr = 0;
$tmp = '';

// for 循环,$i 为从 0 开始,到小于 $txt 字串长度的整数
for($i = 0; $i < strlen($txt); $i++) {
// 如果 $ctr = $encrypt_key 的长度,则 $ctr 清零
$ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;
// $tmp 字串在末尾增加两位,其第一位内容为 $encrypt_key 的第 $ctr 位,
// 第二位内容为 $txt 的第 $i 位与 $encrypt_key 的 $ctr 位取异或。然后 $ctr = $ctr + 1
$tmp .= $encrypt_key[$ctr].($txt[$i] ^ $encrypt_key[$ctr++]);
}

// 返回结果,结果为 passport_key() 函数返回值的 base65 编码结果
return base64_encode(passport_key($tmp, $key));

}

/**
* Passport 解密函数
*
* @param string 加密后的字串
* @param string 私有密匙(用于解密和加密)
*
* @return string 字串经过私有密匙解密后的结果
*/
function passport_decrypt($txt, $key) {

// $txt 的结果为加密后的字串经过 base64 解码,然后与私有密匙一起,
// 经过 passport_key() 函数处理后的返回值
$txt = passport_key(base64_decode($txt), $key);

// 变量初始化
$tmp = '';

// for 循环,$i 为从 0 开始,到小于 $txt 字串长度的整数
for ($i = 0; $i < strlen($txt); $i++) {
// $tmp 字串在末尾增加一位,其内容为 $txt 的第 $i 位,
// 与 $txt 的第 $i + 1 位取异或。然后 $i = $i + 1
$tmp .= $txt[$i] ^ $txt[++$i];
}

// 返回 $tmp 的值作为结果
return $tmp;

}

/**
* Passport 密匙处理函数
*
* @param string 待加密或待解密的字串
* @param string 私有密匙(用于解密和加密)
*
* @return string 处理后的密匙
*/
function passport_key($txt, $encrypt_key) {

// 将 $encrypt_key 赋为 $encrypt_key 经 md5() 后的值
$encrypt_key = md5($encrypt_key);

// 变量初始化
$ctr = 0;
$tmp = '';

// for 循环,$i 为从 0 开始,到小于 $txt 字串长度的整数
for($i = 0; $i < strlen($txt); $i++) {
// 如果 $ctr = $encrypt_key 的长度,则 $ctr 清零
$ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;
// $tmp 字串在末尾增加一位,其内容为 $txt 的第 $i 位,
// 与 $encrypt_key 的第 $ctr + 1 位取异或。然后 $ctr = $ctr + 1
$tmp .= $txt[$i] ^ $encrypt_key[$ctr++];
}

// 返回 $tmp 的值作为结果
return $tmp;

}

/**
* Passport 信息(数组)编码函数
*
* @param array 待编码的数组
*
* @return string 数组经编码后的字串
*/
function passport_encode($array) {

// 数组变量初始化
$arrayenc = array();

// 遍历数组 $array,其中 $key 为当前元素的下标,$val 为其对应的值
foreach($array as $key => $val) {
// $arrayenc 数组增加一个元素,其内容为 "$key=经过 urlencode() 后的 $val 值"
$arrayenc[] = $key.'='.urlencode($val);
}

// 返回以 "&" 连接的 $arrayenc 的值(implode),例如 $arrayenc = array('aa', 'bb', 'cc', 'dd'),
// 则 implode('&', $arrayenc) 后的结果为 ”aa&bb&cc&dd"
return implode('&', $arrayenc);

}




passport_encode()是将数组转换合成为字串形式存储的函数:变量名和数值之间用等号连接,如果数值包含特殊字符,使用 urlencode() 将其转码;多个变量间使用 & 分割。例如原始数组内容为 array('username' => 'abc', 'email' => 'my+discuz@gmail.com'),经过 passport_encode() 编码后结果为 username=abc&email=my%2Bdiscuz%40gmail.com。

信息字串(auth)

应用程序在收到登录或注册请求,并读取到用户资料后,请按如下的要求将用户资料及部分其他信息存放于一个数组之中。数组各键值的含义为:



cookietime
应用程序保存该用户登录记录的时间,可为非负整数,单位秒,Discuz! Passport 收到此参数后,会设置同样的 Cookie 过期时间,这样应用程序和论坛可以保证同样的登录有效性。如不传递此参数,或参数数值不正确,则 Discuz! Passport 按照 0 设置 Cookie 有效期。


time
应用程序所在服务器当前时间(9 或 10 位数字 Unix Timestamp),此参数用于 Discuz! 所在服务器当前时间进行比对,如果早于当前时间超过若干秒(取决于 Discuz! Passport 中的“验证字串有效期”设定),则视为本 auth 内容无效,避免此URL被人得知后可能的安全问题。


username
用户登录或注册的用户名。Discuz! 的注册用户名规则为:
长度 1~15 个字符,不得为空
不得为 c:concon、游客(gb2312 或 big5 内码)、Guest
不得包含 (,)、(*)、(")、([TAB])、([SPACE])、([r])、([n])、(<)、(>)、(&)其中之一

如果应用程序提交过来的用户名不符合上述规则,Passport 将自动去处其中的特殊字符,将过滤后的结果写入数据库中。


password
用户密码经 MD5 不可逆加密后的值。如果此密码使用非 MD5 加密,则应用程序和 Passport 不能正常关联和使用。


email
用户 Email 地址(50 个字节以内)。


isadmin
当前用户是否是应用程序的最高管理员,1=是,0=否。最高管理员的权限,将同步到论坛中去,其他下级管理员的身份将不进行同步,而由最高管理员分别在不同的系统中进行设置。

credits
当前用户在应用程序中的积分值,范围 -2147483648 到 2147483647,如果 Discuz! Passport 中设置了目标积分项,则用户登录时,Passport 会把应用程序传递过来的 credits 值同步到指定的论坛的指定积分项目中


gender
当前用户的性别,1=男,2=女,0=未知。


bday
当前用户的生日,格式 yyyy-mm-dd。


regip
当前用户注册时的 IP 地址。


regdate
当前用户注册的时间(9 或 10 位数字 Unix Timestamp)。


nickname
当前用户的昵称(30 个字节以内,如传递此参数,必须打开相应用户组的昵称权限,否则用户在控制面板中提交个人资料时,会导致昵称失效)。


site
当前用户的主页地址(包含http://)。


qq
当前用户的 QQ 号码。


ICQ
当前用户的 ICQ 账号。


msn
当前用户的 MSN Messenger 账号。


yahoo
当前用户的 Yahoo! Messanger 账号。
以上参数中,以黑体下划线显示的 time、username、password、email 是必须传递的参数,缺少上述参数 Passport 将无法正常工作;其他的参数是可选的,如果不传递某些参数,则 Passport 会进行识别,自动不更新没有传递的参数所在的字段;所有数值,请提供原始值,而非经过反斜线转义(addslashes)后的结果。

把上述信息存放于数组中,假定为如下的形式:


$member = array
(
'cookietime' => 31536000,
'time' => 1117415922,
'username' => 'Abcd',
'password' => 'e2fc714c4727ee9395f324cd2e7f331f',
'email' => 'abcd@efgh.com',
'credits' => 123,
'regip' => '210.120.222.111',
'regdate' => '1012752000',
'msn' => 'email@hotmail.com'
);




将其经过如下的加密变换,即可得到 auth 的值:


$auth = passport_encrypt(passport_encode($member), $passport_key);




其中,passport_encode() 在前文已做了说明,用于将数组内容存放于特定的格式,$passport_key 是私有密匙。

切记:由于 $auth 中可能含有等号、加号等特殊字符,请将 $auth 经过 rawurlencode() 编码后再在 URL 中传递,否则可能会产生问题。

导向字串(forward)

导向字串用于通知 Discuz! Passport 在完成自身操作后,返回到哪一个 URL 地址,例如 http://www.myforums.com/forumdisplay.php?fid=2。如果 forward 为空,则默认导向到应用程序的首页

切记:由于 $forward 中可能含有冒号、问号、等号等特殊字符,请将 $forward 经过 rawurlencode() 编码后再在 URL 中传递,否则可能会产生问题。

验证字串(verify)

验证字串用户检验 auth 和 forward 两个参数的合法性,避免非法构造参数进行破坏的可能。无论 auth 和 forward 变量是否存在,验证字串(verify)的值均为:


$verify = md5($action.$auth.$forward.$passport_key);




其中,$action 是当前执行的 Passport 操作,如 login 等等;$auth 是用户信息加密后,并经 rawurlencode() 之前的内容;$forward 是经 rawurlencode() 前的导向字串;$passport_key 是私有密匙。如果 verify 的值不匹配,则 Passport 拒绝进行下一步操作。


Discuz! Passport 设置与启用


内置关联

Discuz! 以战略合作的方式,与业内知名的产品实现了 Passport 关联,目前内置了 SiteEngine 建站引擎(http://www.siteengine.net)和 Shopex 通用型网上商店系统(http://www.shopex.cn)的相关接口,这样用户只须透过在两套软件中简单的设置,即可开启这些关联。

其他应用程序

由于 Discuz! Passport 的高可扩展性和平台无关性,使得您可以参照前文的说明,稍稍改动小部分的代码,便将任何 B/S 模式的应用程序与 Discuz! 进行关联。

参数设置

您可以在 Discuz! 系统设置中,看到相应的通行证设置功能,在 Discuz! 合作伙伴的软件中,也可以找到这些设置入口。相关的操作已比较简单,在此不再详细叙述。

特别说明

如果您先运营了论坛,后与其他应用程序启用了 Passport 关联,由于之前论坛中的用户数据没有同步,您需要先写一个导入程序,将论坛的用户数据导入到应用程序的用户表中,否则以往在论坛注册的用户将无法通过 Passport 登录。已成功关联后新注册的用户无此问题。

在开启了 Discuz! 通行证后,您仍然可以通过 logging.php?action=login 这个链接来登录论坛,以备调试之用,但页面上显示的链接将改为应用程序的登录 URL。注意:开启通行证后,建议您通过 Discuz! 选项关闭论坛本身的注册功能,以免用户通过论坛注册而产生无法同步的问题。

您可以在 Discuz! 的 api/passport.php 找到 Discuz! Passport 的全部源程序,您也许通过他更好的理解 Passport 的原理,更快的完成应用程序与 Discuz! 之间的整合。


典型错误提示


Illegal request

非法请求,当验证字串 verify 不匹配时会产生此提示。可能是应用程序与 Discuz! 配置的私有密匙不同,或是通过 URL 传递前,未将必要的参数(如 auth、forward 等)进行 URL 编码,也有可能是使用了经过 URL 编码的参数值用来计算 verify 的 md5 值造成。以 PHP 语言为例,正确的代码应当是类似于的如下的格式:


$action = 'login';
$auth = passport_encrypt(passport_encode($autharray), $passport_key);
$forward = 'http://www.discuz.net/index.php';
$verify = md5($action.$auth.$forward.$passport_key);

header("Location: http://www.discuz.net/api/passport.php".
"?action=$action".
"&auth=".rawurlencode($auth).
"&forward=".rawurlencode($forward).
"&verify=$verify");


Lack of required parameters

auth 内容解密后,缺少必要的信息 time、username、password、email。

Request expired

请求过期。当前服务器时间与应用程序提交过来的 time 之差大于 Discuz! Passport 中设置的请求有效期。可能是使用以往的代码非法尝试,也可能是由于应用程序和 Discuz! 论坛所在的两台服务器,时间设置有误造成。

Invalid action

没有指定 Passport 所执行的 action。

2008年9月3日星期三

PHP 商品最新浏览

/*
RecentlyGoods File
Urchin Studio FoxCMS
Version: 1.1.6
Author: luckyfox (xhx@163.net)
Copyright: Urchin Studio (www.itocean.net)
Last Modified: 2008-9-3

*/

//TempNum 显示临时记录数

$TempNum=5;

//setcookie("RecentlyGoods", "12,31,90,39");

//RecentlyGoods 最近商品RecentlyGoods临时变量

if (isset($_COOKIE['RecentlyGoods']))
{
$RecentlyGoods=$_COOKIE['RecentlyGoods'];
$RecentlyGoodsArray=explode(",", $RecentlyGoods);
$RecentlyGoodsNum=count($RecentlyGoodsArray); //RecentlyGoodsNum 当前存储的变量个数

}

if($_GET['Id']!="")
{

$Id=$_GET['Id']; //ID 为得到请求的字符


//如果存在了,则将之前的删除,用最新的在尾部追加

if (strstr($RecentlyGoods, $Id))
{
//echo "已经存在,则不写入COOKIES
";
}
else
{
if($RecentlyGoodsNum <$TempNum) //如果COOKIES中的元素小于指定的大小,则直接进行输入COOKIES
{
if($RecentlyGoods=="")
{
setcookie("RecentlyGoods",$Id,time()+3600);
}
else
{
$RecentlyGoodsNew=$RecentlyGoods.",".$Id;
setcookie("RecentlyGoods", $RecentlyGoodsNew,time()+3600);
}
}
else //如果大于了指定的大小后,将第一个给删去,在尾部再加入最新的记录。
{
$pos=strpos($RecentlyGoods,",")+1; //第一个参数的起始位置
$FirstString=substr($RecentlyGoods,0,$pos); //取出第一个参数
$RecentlyGoods=str_replace($FirstString,"",$RecentlyGoods); //将第一个参数删除
$RecentlyGoodsNew=$RecentlyGoods.",".$Id; //在尾部加入最新的记录
setcookie("RecentlyGoods", $RecentlyGoodsNew,time()+3600);
}

}


}
?>

2008年8月31日星期日

php 中文转拼音函数

function g($num)
{
$d=array(
array("a",-20319),
array("ai",-20317),
array("an",-20304),
array("ang",-20295),
array("ao",-20292),
array("ba",-20283),
array("bai",-20265),
array("ban",-20257),
array("bang",-20242),
array("bao",-20230),
array("bei",-20051),
array("ben",-20036),
array("beng",-20032),
array("bi",-20026),
array("bian",-20002),
array("biao",-19990),
array("bie",-19986),
array("bin",-19982),
array("bing",-19976),
array("bo",-19805),
array("bu",-19784),
array("ca",-19775),
array("cai",-19774),
array("can",-19763),
array("cang",-19756),
array("cao",-19751),
array("ce",-19746),
array("ceng",-19741),
array("cha",-19739),
array("chai",-19728),
array("chan",-19725),
array("chang",-19715),
array("chao",-19540),
array("che",-19531),
array("chen",-19525),
array("cheng",-19515),
array("chi",-19500),
array("chong",-19484),
array("chou",-19479),
array("chu",-19467),
array("chuai",-19289),
array("chuan",-19288),
array("chuang",-19281),
array("chui",-19275),
array("chun",-19270),
array("chuo",-19263),
array("ci",-19261),
array("cong",-19249),
array("cou",-19243),
array("cu",-19242),
array("cuan",-19238),
array("cui",-19235),
array("cun",-19227),
array("cuo",-19224),
array("da",-19218),
array("dai",-19212),
array("dan",-19038),
array("dang",-19023),
array("dao",-19018),
array("de",-19006),
array("deng",-19003),
array("di",-18996),
array("dian",-18977),
array("diao",-18961),
array("die",-18952),
array("ding",-18783),
array("diu",-18774),
array("dong",-18773),
array("dou",-18763),
array("du",-18756),
array("duan",-18741),
array("dui",-18735),
array("dun",-18731),
array("duo",-18722),
array("e",-18710),
array("en",-18697),
array("er",-18696),
array("fa",-18526),
array("fan",-18518),
array("fang",-18501),
array("fei",-18490),
array("fen",-18478),
array("feng",-18463),
array("fo",-18448),
array("fou",-18447),
array("fu",-18446),
array("ga",-18239),
array("gai",-18237),
array("gan",-18231),
array("gang",-18220),
array("gao",-18211),
array("ge",-18201),
array("gei",-18184),
array("gen",-18183),
array("geng",-18181),
array("gong",-18012),
array("gou",-17997),
array("gu",-17988),
array("gua",-17970),
array("guai",-17964),
array("guan",-17961),
array("guang",-17950),
array("gui",-17947),
array("gun",-17931),
array("guo",-17928),
array("ha",-17922),
array("hai",-17759),
array("han",-17752),
array("hang",-17733),
array("hao",-17730),
array("he",-17721),
array("hei",-17703),
array("hen",-17701),
array("heng",-17697),
array("hong",-17692),
array("hou",-17683),
array("hu",-17676),
array("hua",-17496),
array("huai",-17487),
array("huan",-17482),
array("huang",-17468),
array("hui",-17454),
array("hun",-17433),
array("huo",-17427),
array("ji",-17417),
array("jia",-17202),
array("jian",-17185),
array("jiang",-16983),
array("jiao",-16970),
array("jie",-16942),
array("jin",-16915),
array("jing",-16733),
array("jiong",-16708),
array("jiu",-16706),
array("ju",-16689),
array("juan",-16664),
array("jue",-16657),
array("jun",-16647),
array("ka",-16474),
array("kai",-16470),
array("kan",-16465),
array("kang",-16459),
array("kao",-16452),
array("ke",-16448),
array("ken",-16433),
array("keng",-16429),
array("kong",-16427),
array("kou",-16423),
array("ku",-16419),
array("kua",-16412),
array("kuai",-16407),
array("kuan",-16403),
array("kuang",-16401),
array("kui",-16393),
array("kun",-16220),
array("kuo",-16216),
array("la",-16212),
array("lai",-16205),
array("lan",-16202),
array("lang",-16187),
array("lao",-16180),
array("le",-16171),
array("lei",-16169),
array("leng",-16158),
array("li",-16155),
array("lia",-15959),
array("lian",-15958),
array("liang",-15944),
array("liao",-15933),
array("lie",-15920),
array("lin",-15915),
array("ling",-15903),
array("liu",-15889),
array("long",-15878),
array("lou",-15707),
array("lu",-15701),
array("lv",-15681),
array("luan",-15667),
array("lue",-15661),
array("lun",-15659),
array("luo",-15652),
array("ma",-15640),
array("mai",-15631),
array("man",-15625),
array("mang",-15454),
array("mao",-15448),
array("me",-15436),
array("mei",-15435),
array("men",-15419),
array("meng",-15416),
array("mi",-15408),
array("mian",-15394),
array("miao",-15385),
array("mie",-15377),
array("min",-15375),
array("ming",-15369),
array("miu",-15363),
array("mo",-15362),
array("mou",-15183),
array("mu",-15180),
array("na",-15165),
array("nai",-15158),
array("nan",-15153),
array("nang",-15150),
array("nao",-15149),
array("ne",-15144),
array("nei",-15143),
array("nen",-15141),
array("neng",-15140),
array("ni",-15139),
array("nian",-15128),
array("niang",-15121),
array("niao",-15119),
array("nie",-15117),
array("nin",-15110),
array("ning",-15109),
array("niu",-14941),
array("nong",-14937),
array("nu",-14933),
array("nv",-14930),
array("nuan",-14929),
array("nue",-14928),
array("nuo",-14926),
array("o",-14922),
array("ou",-14921),
array("pa",-14914),
array("pai",-14908),
array("pan",-14902),
array("pang",-14894),
array("pao",-14889),
array("pei",-14882),
array("pen",-14873),
array("peng",-14871),
array("pi",-14857),
array("pian",-14678),
array("piao",-14674),
array("pie",-14670),
array("pin",-14668),
array("ping",-14663),
array("po",-14654),
array("pu",-14645),
array("qi",-14630),
array("qia",-14594),
array("qian",-14429),
array("qiang",-14407),
array("qiao",-14399),
array("qie",-14384),
array("qin",-14379),
array("qing",-14368),
array("qiong",-14355),
array("qiu",-14353),
array("qu",-14345),
array("quan",-14170),
array("que",-14159),
array("qun",-14151),
array("ran",-14149),
array("rang",-14145),
array("rao",-14140),
array("re",-14137),
array("ren",-14135),
array("reng",-14125),
array("ri",-14123),
array("rong",-14122),
array("rou",-14112),
array("ru",-14109),
array("ruan",-14099),
array("rui",-14097),
array("run",-14094),
array("ruo",-14092),
array("sa",-14090),
array("sai",-14087),
array("san",-14083),
array("sang",-13917),
array("sao",-13914),
array("se",-13910),
array("sen",-13907),
array("seng",-13906),
array("sha",-13905),
array("shai",-13896),
array("shan",-13894),
array("shang",-13878),
array("shao",-13870),
array("she",-13859),
array("shen",-13847),
array("sheng",-13831),
array("shi",-13658),
array("shou",-13611),
array("shu",-13601),
array("shua",-13406),
array("shuai",-13404),
array("shuan",-13400),
array("shuang",-13398),
array("shui",-13395),
array("shun",-13391),
array("shuo",-13387),
array("si",-13383),
array("song",-13367),
array("sou",-13359),
array("su",-13356),
array("suan",-13343),
array("sui",-13340),
array("sun",-13329),
array("suo",-13326),
array("ta",-13318),
array("tai",-13147),
array("tan",-13138),
array("tang",-13120),
array("tao",-13107),
array("te",-13096),
array("teng",-13095),
array("ti",-13091),
array("tian",-13076),
array("tiao",-13068),
array("tie",-13063),
array("ting",-13060),
array("tong",-12888),
array("tou",-12875),
array("tu",-12871),
array("tuan",-12860),
array("tui",-12858),
array("tun",-12852),
array("tuo",-12849),
array("wa",-12838),
array("wai",-12831),
array("wan",-12829),
array("wang",-12812),
array("wei",-12802),
array("wen",-12607),
array("weng",-12597),
array("wo",-12594),
array("wu",-12585),
array("xi",-12556),
array("xia",-12359),
array("xian",-12346),
array("xiang",-12320),
array("xiao",-12300),
array("xie",-12120),
array("xin",-12099),
array("xing",-12089),
array("xiong",-12074),
array("xiu",-12067),
array("xu",-12058),
array("xuan",-12039),
array("xue",-11867),
array("xun",-11861),
array("ya",-11847),
array("yan",-11831),
array("yang",-11798),
array("yao",-11781),
array("ye",-11604),
array("yi",-11589),
array("yin",-11536),
array("ying",-11358),
array("yo",-11340),
array("yong",-11339),
array("you",-11324),
array("yu",-11303),
array("yuan",-11097),
array("yue",-11077),
array("yun",-11067),
array("za",-11055),
array("zai",-11052),
array("zan",-11045),
array("zang",-11041),
array("zao",-11038),
array("ze",-11024),
array("zei",-11020),
array("zen",-11019),
array("zeng",-11018),
array("zha",-11014),
array("zhai",-10838),
array("zhan",-10832),
array("zhang",-10815),
array("zhao",-10800),
array("zhe",-10790),
array("zhen",-10780),
array("zheng",-10764),
array("zhi",-10587),
array("zhong",-10544),
array("zhou",-10533),
array("zhu",-10519),
array("zhua",-10331),
array("zhuai",-10329),
array("zhuan",-10328),
array("zhuang",-10322),
array("zhui",-10315),
array("zhun",-10309),
array("zhuo",-10307),
array("zi",-10296),
array("zong",-10281),
array("zou",-10274),
array("zu",-10270),
array("zuan",-10262),
array("zui",-10260),
array("zun",-10256),
array("zuo",-10254));
//global $d;
if(($num>0) && ($num<160))
{
return chr($num);
}
elseif(($num<-20319) || ($num>-10247))
{
return "";
}
else
{
for($i=count($d)-1;$i>=0;$i--)
{
if($d[$i][1]<=$num)
break;
}
return $d[$i][0];
}
}

function pinyin($str)
{
$ret="";
for($i=0;$i {
$p=ord(substr($str,$i,1));
if($p>160)
{
$q=ord(substr($str,++$i,1));
$p=$p*256+$q-65536;
}
$ret.=g($p);
}
return $ret;
}

2008年8月27日星期三

用你想不到的MySQL运算符获得更多数据比较功能

如果你在最近进行过SELECT或UPDATE查询,那么你很可能用到过一个或者多个MySQL的比较运算符来限制查询的输出结果。比较是大多数SELECT查询不可分割的一部分,而MySQL为这一功能提供了很多函数;根据最近一次统计,它有超过20个这样的运算符和函数,从著名的=和LIKE到更加难懂的NOT IN和STRCMP()。
本文把重点放在一些不太常见的MySQL数据比较函数和运算符上,讨论它们可以被如何运用在应用程序里比较表格字段里的字符串、数字和日期/时间或者用户提供的值。

BETWEEN

BETWEEN运算符是测试在某个范围内是否存在一个数值或者日期值的有用方式。这个运算符接受两个参数——最大值和最小值,并测试所提供的值是否在这两个值的范围之内。如果在这个范围之内,运算符就返回一个布尔值——真;否则,它就返回一个伪值。下面就是一个例子:

mysql> SELECT 2350 BETWEEN 100 AND 10000;
+----------------------------+
| 2350 BETWEEN 100 AND 10000 |
+----------------------------+
| 1 |
+----------------------------+
1 row in set (0.18 sec)

下面两个例子用到了日期值:

mysql> SELECT 20060405 BETWEEN 20060101 AND 20070101;
+----------------------------------------+
| 20060405 BETWEEN 20060101 AND 20070101 |
+----------------------------------------+
| 1 |
+----------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT 20060405 BETWEEN 20060101 AND 20060401;
+----------------------------------------+
| 20060405 BETWEEN 20060101 AND 20060401 |
+----------------------------------------+
| 0 |
+----------------------------------------+
1 row in set (0.00 sec)

GREATEST和LEAST

GREATEST和LEAST运算符为判断一组数字或者时间值中最大和最小值提供了一个便捷的途径。你一看名字就知道这两个运算符的作用——下面是一个对一组日期使用GREATEST运算符的例子:

mysql> SELECT GREATEST(20000601, 20000529, 20000604);
+----------------------------------------+
| GREATEST(20000601, 20000529, 20000604) |
+----------------------------------------+
| 20000604 |
+----------------------------------------+
1 row in set (0.00 sec)

而下面是一个对一组数值使用LEAST运算符的例子:

mysql> SELECT LEAST(100, 200, 50, -6, -73, 1000);
+------------------------------------+
| LEAST(100, 200, 50, -6, -73, 1000) |
+------------------------------------+
| -73 |
+------------------------------------+
1 row in set (0.03 sec)

IN

IN是一种用来测试在预先指定的选项列表里是否存在某个特定值的有用运算符。它可以用在数字、字符和时间值上。它接受两个参数——要被测试的值和选项列表。下面就是一个使用字符串的例子:

mysql> SELECT 'c' IN ('a', 'b', 'c', 'd');
+-----------------------------+
| 'c' IN ('a', 'b', 'c', 'd') |
+-----------------------------+
| 1 |
+-----------------------------+
1 row in set (0.02 sec)

下面一个例子用到了数字:

mysql> SELECT 1 IN (3,4,5);
+--------------+
| 1 IN (3,4,5) |
+--------------+
| 0 |
+--------------+
1 row in set (0.00 sec)

COALESCE

COALESCE运算符在区分列表中NULL和非NULL值上十分有用。利用提供的值的列表,它会返回列表中第一个非NULL的值,或者,如果不存在非NULL的值,它就会返回一个NULL。这在你有一个同时含有NULL和实际内容的列,并且希望快速地“跳到”第一个非NULL的记录时尤其方便。下面就是一个例子:

mysql> SELECT COALESCE(NULL, 'bat', 12, NULL);
+---------------------------------+
| COALESCE(NULL, 'bat', 12, NULL) |
+---------------------------------+
| bat |
+---------------------------------+
1 row in set (0.02 sec)

INTERVAL

INTERVAL运算符提供另外一种对数字及其范围的控制。它接受一个数字列表(N0, N1, N2...),将N0与剩下的所有数字比较,返回大于N0的最小数字所处的位置索引。数字N1, N2...必须按升序排列。

看看下面的例子,它将数字24与列表里的数字(5, 10, 15, 20, 25, 30)进行比较:

mysql> SELECT INTERVAL (24, 5, 10, 15, 20, 25, 30);
+--------------------------------------+
| INTERVAL (24, 5, 10, 15, 20, 25, 30) |
+--------------------------------------+
| 4 |
+--------------------------------------+

1 row in set (0.07 sec)

在这个例子中,24比25(索引位置4)小,但是比20(索引位置3)大,因此它的INTERVAL是4。

STRCMP

STRCMP()函数是MySQL里比较字符串的最简单方式之一。这个函数接受两个参数——要被比较的字符串。如果这个两个字符串相同,它就返回0;如果第一个大于第二个,它就返回1;如果第一个小于第二个,它就返回-1。比较的结果完全依赖当前使用时的字符集。下面就是几个例子:

mysql> SELECT STRCMP('hell', 'hell');
+------------------------+
| STRCMP('hell', 'hell') |
+------------------------+
| 0 |
+------------------------+
1 row in set (0.00 sec)mysql> SELECT STRCMP('bell', 'hell');
+------------------------+
| STRCMP('bell', 'hell') |
+------------------------+
| -1 |
+------------------------+
1 row in set (0.00 sec)

REGEXP

要进行更加复杂的字符串比较,REGEXP函数常常要比用滥了的LIKE语句更加强大。这个函数会把一个表达式与一个正则表达式模式进行比较,如果找到匹配的,它就返回布尔值——真。下面是这样一个例子:

mysql> SELECT 'The Matrix' REGEXP '[u-z]$';
+------------------------------+
| 'The Matrix' REGEXP '[u-z]$' |
+------------------------------+
| 1 |
+------------------------------+
1 row in set (0.00 sec)

DATEDIFF

正如其名字所表示的,DATEDIFF()函数是一个快速获得两个日期之间天数的工具。这对于计算到某个事件发生时的天数,或者在给定某人的生日时计算他/她的年龄很有用。

mysql> SELECT DATEDIFF(20060101, 20051201);
+------------------------------+
| DATEDIFF(20060101, 20051201) |
+------------------------------+
| 31 |
+------------------------------+
1 row in set (0.00 sec)

DATEDIFF()通常与NOW()函数一起使用——例如,计算今天到过去或者未来任意一天之间的天数。下面就是一个例子:

mysql> SELECT DATEDIFF(NOW(), 20060203);
+---------------------------+
| DATEDIFF(NOW(), 20060203) |
+---------------------------+
| 176 |
+---------------------------+
1 row in set (0.00 sec)

以上就是本文的内容。要记住的是,你可以在MySQL手册里发现更多关于这些函数的信息。玩得高兴!

2008年8月16日星期六

在用limit查询的同时取得总的记录数

从Mysql4.0.0开始,我们可以选择使用这种方式:

SELECT SQL_CALC_FOUND_ROWS name, email FROM users WHERE name LIKE ‘a%’ LIMIT 10;
SELECT FOUND_ROWS();

其中SQL_CALC_FOUND_ROWS 告诉Mysql将sql所处理的行数记录下来,FOUND_ROWS() 则取到了这个纪录。
虽然也是两个语句,但是只执行了一次主查询,所以效率比原来要高很多。

怎样利用mysql让存储结果分页,用于复杂查询

似乎讨论分页的人很少,难道大家都沉迷于limit m,n?在有索引的情况下,limit m,n速度足够,可是在复杂条件搜索时,where somthing order by somefield+somefieldmysql会搜遍数据库,找出“所有”符合条件的记录,然后取出m,n条记录。如果你的数据量有几十万条,用户又搜索一些很通俗的词,然后要依次读最后几页重温旧梦。。。mysql该很悲壮的不停操作硬盘。
所以,可以试着让mysql也存储分页,当然要程序配合。(这里只是提出一个设想,欢迎大家一起讨论)
ASP的分页:在ASP系统中有Recordset对象来实现分页,但是大量数据放在内存中,而且不知道什么时候才失效(请ASP高手指点). SQL数据库分页:用存储过程+游标方式分页,具体实现原理不是很清楚,设想如果用一次查询就得到需要的结果,或者是id集,需要后续页时只要按照结果中的IDs读出相关记录。这样只要很小的空间保留本次查询的所有IDs. (SQL中的查询结果不知道怎样清楚过期垃圾?)
这样,可以让mysql模拟存储分页机制:1. select id from $table where $condition order by $field limit $max_pages*$count;查询符合条件的IDs.限定最大符合条件的记录数量,也可以不加。2. 因为php在执行结束后所有变量都要lost,所以可以考虑:方案a. 在mysql建立临时表,查询结果用一个时间或随机数作为唯一标志插入。其中建立page1~pagen个字段,每个字段保存该页中需要的ids, 这样一个id对一条记录.方案b. 如果打开session,也可以放在session中保存,实际上是放在文件中保存。建立一个$IDs数组,$IDs[1]~$IDs[$max_pages]. 考虑到有时候用户会开几个窗口同时查询,要为$ids做一个唯一标志,避免查询结果相互覆盖。二维数组和$$var都是好办法。3. 在每页页的请求中,直接找到对应的IDs,中间以","间隔:select * from $table where id in ($ids); 速度绝对快4. 收尾要考虑查询结果的自动清楚,可以设置定时或者按比例随机清楚。如果用mysql临时表要加上一个时间标志字段,session中要加入$IDs["time"]=time(); 在一定时间以后不操作视为过期数据。
5. 如果要优化,可以考虑用把1和2.a中的语句合并成select ...... into ....
Note:1.以上只是针对mysql的修补方案,希望mysql哪天能把这些功能加进去2.其它数据库也可以套用。3.如果其它数据库还有更先进的分页方式,请告诉我或mailto: steeven@kali.com.cn4.如果真的有很多数据要查询,还是和mysql再见吧,sql,oracle都提供了更先进的关键词索引查询。
精益求精,以上只是抛砖引玉,欢迎共同探讨分页问题。(也可关于其它数据库)希望有一天能把各种分页方式整理出来供新手参考。