稍大一些的网站,通常都会有好几个服务器,每个服务器运行着不同功能的模块,使用不同的二级域名,而一个整体性强的网站,用户系统是统一的,即一套用户名、密码在整个网站的各个模块中都是可以登录使用的。各个服务器共享用户数据是比较容易实现的,只需要在后端放个数据库服务器,各个服务器通过统一接口对用户数据进行访问即可。但还存在一个问题,就是用户在这个服务器登录之后,进入另一个服务器的别的模块时,仍然需要重新登录,这就是一次登录,全部通行的问题,映射到技术上,其实就是各个服务器之间如何实现共享 SESSION 数据的问题。
二、PHP SESSION 的工作原理
在解决问题之前,先来了解一下 PHP SESSION 的工作原理。在客户端(如浏览器)登录网站时,被访问的 PHP 页面可以使用 session_start() 打开 SESSION,这样就会产生客户端的唯一标识 SESSION ID(此 ID 可通过函数 session_id() 获取/设置)。SESSION ID 可以通过两种方式保留在客户端,使得请求不同的页面时,PHP 程序可以获知客户端的 SESSION ID;一种是将 SESSION ID 自动加入到 GET 的 URL 中,或者 POST 的表单中,默认情况下,变量名为 PHPSESSID;另一种是通过 COOKIE,将 SESSION ID 保存在 COOKIE 中,默认情况下,这个 COOKIE 的名字为 PHPSESSID。这里我们主要以 COOKIE 方式进行说明,因为应用比较广泛。
那么 SESSION 的数据保存在哪里呢?当然是在服务器端,但不是保存在内存中,而是保存在文件或数据库中。默认情况下,php.ini 中设置的 SESSION 保存方式是 files(session.save_handler = files),即使用读写文件的方式保存 SESSION 数据,而 SESSION 文件保存的目录由 session.save_path 指定,文件名以 sess_ 为前缀,后跟 SESSION ID,如:sess_c72665af28a8b14c0fe11afe3b59b51b。文件中的数据即是序列化之后的 SESSION 数据了。如果访问量大,可能产生的 SESSION 文件会比较多,这时可以设置分级目录进行 SESSION 文件的保存,效率会提高很多,设置方法为:session.save_path="N;/save_path",N 为分级的级数,save_path 为开始目录。当写入 SESSION 数据的时候,PHP 会获取到客户端的 SESSION_ID,然后根据这个 SESSION ID 到指定的 SESSION 文件保存目录中找到相应的 SESSION 文件,不存在则创建之,最后将数据序列化之后写入文件。读取 SESSION 数据是也是类似的操作流程,对读出来的数据需要进行解序列化,生成相应的 SESSION 变量。
三、多服务器共享 SESSION 的主要障碍及解决办法
通过了解 SESSION 的工作原理,我们可以发现,在默认情况下,各个服务器会各自分别对同一个客户端产生 SESSION ID,如对于同一个用户浏览器,A 服务器产生的 SESSION ID 是 30de1e9de3192ba6ce2992d27a1b6a0a,而 B 服务器生成的则是 c72665af28a8b14c0fe11afe3b59b51b。另外,PHP 的 SESSION 数据都是分别保存在本服务器的文件系统中。如下图所示:
确定了问题所在之后,就可以着手进行解决了。想要共享 SESSION 数据,那就必须实现两个目标:一个是各个服务器对同一个客户端产生的 SESSION ID 必须相同,并且可通过同一个 COOKIE 进行传递,也就是说各个服务器必须可以读取同一个名为 PHPSESSID 的 COOKIE;另一个是 SESSION 数据的存储方式/位置必须保证各个服务器都能够访问到。简单地说就是多服务器共享客户端的 SESSION ID,同时还必须共享服务器端的 SESSION 数据。
第一个目标的实现其实很简单,只需要对 COOKIE 的域(domain)进行特殊地设置即可,默认情况下,COOKIE 的域是当前服务器的域名/IP 地址,而域不同的话,各个服务器所设置的 COOKIE 是不能相互访问的,如 www.aaa.com 的服务器是不能读写 www.bbb.com 服务器设置的 COOKIE 的。
这里我们所说的同一网站的服务器有其特殊性,那就是他们同属于同一个一级域,如:aaa.infor96.com 和 www.infor96.com 都属于域 .infor96.com,那么我们就可以设置 COOKIE 的域为 .infor96.com,这样 aaa.infor96.com、www.infor96.com 等等都可以访问此 COOKIE。PHP 代码中的设置方法如下:
<?php ini_set('session.cookie_domain', '.infor96.com'); ?>
这样各个服务器共享同一客户端 SESSION ID 的目的就达到了。
第二个目标的实现可以使用文件共享方式,如 NFS 方式,但设置、操作上有些复杂。我们可以参考先前所说的统一用户系统的方式,即使用数据库来保存 SESSION 数据,这样各个服务器就可以方便地访问同一个数据源,获取相同的 SESSION 数据了。
解决办法如下图所示:
四、代码实现
首先创建数据表,MySQL 的 SQL 语句如下:
CREATE TABLE `sess` ( `sesskey` varchar(32) NOT NULL default '', `expiry` bigint(20) NOT NULL default '0', `data` longtext NOT NULL, PRIMARY KEY (`sesskey`), KEY `expiry` (`expiry`) ) TYPE=MyISAM
sesskey 为 SESSION ID,expiry 为 SESSION 过期时间,data 用于保存 SESSION 数据。
默认情况下 SESSION 数据是以文件方式保存,想要使用数据库方式保存,就必须重新定义 SESSION 各个操作的处理函数。PHP 提供了session_set_save_handle() 函数,可以用此函数自定义 SESSION 的处理过程,当然首先要先将 session.save_handler 改成 user,可在 PHP 中进行设置:
<?php session_module_name('user'); ?>
接下来着重讲一下 session_set_save_handle() 函数,此函数有六个参数:
session_set_save_handler ( string open, string close, string read, string write, string destroy, string gc )
各个参数为各项操作的函数名,这些操作依次是:打开、关闭、读取、写入、销毁、垃圾回收。PHP 手册中有详细的例子,在这里我们使用 OO 的方式来实现这些操作,详细代码如下:
<?php define('MY_SESS_TIME', 3600); //SESSION 生存时长 //类定义 class My_Sess { function init() { $domain = '.infor96.com'; //不使用 GET/POST 变量方式 ini_set('session.use_trans_sid', 0); //设置垃圾回收最大生存时间 ini_set('session.gc_maxlifetime', MY_SESS_TIME); //使用 COOKIE 保存 SESSION ID 的方式 ini_set('session.use_cookies', 1); ini_set('session.cookie_path', '/'); //多主机共享保存 SESSION ID 的 COOKIE ini_set('session.cookie_domain', $domain); //将 session.save_handler 设置为 user,而不是默认的 files session_module_name('user'); //定义 SESSION 各项操作所对应的方法名: session_set_save_handler( array('My_Sess', 'open'), //对应于静态方法 My_Sess::open(),下同。 array('My_Sess', 'close'), array('My_Sess', 'read'), array('My_Sess', 'write'), array('My_Sess', 'destroy'), array('My_Sess', 'gc') ); } //end function function open($save_path, $session_name) { return true; } //end function function close() { global $MY_SESS_CONN; if ($MY_SESS_CONN) { //关闭数据库连接 $MY_SESS_CONN->Close(); } return true; } //end function function read($sesskey) { global $MY_SESS_CONN; $sql = 'SELECT data FROM sess WHERE sesskey=' . $MY_SESS_CONN->qstr($sesskey) . ' AND expiry>=' . time(); $rs =& $MY_SESS_CONN->Execute($sql); if ($rs) { if ($rs->EOF) { return ''; } else { //读取到对应于 SESSION ID 的 SESSION 数据 $v = $rs->fields[0]; $rs->Close(); return $v; } //end if } //end if return ''; } //end function function write($sesskey, $data) { global $MY_SESS_CONN; $qkey = $MY_SESS_CONN->qstr($sesskey); $expiry = time() + My_SESS_TIME; //设置过期时间 //写入 SESSION $arr = array( 'sesskey' => $qkey, 'expiry' => $expiry, 'data' => $data); $MY_SESS_CONN->Replace('sess', $arr, 'sesskey', $autoQuote = true); return true; } //end function function destroy($sesskey) { global $MY_SESS_CONN; $sql = 'DELETE FROM sess WHERE sesskey=' . $MY_SESS_CONN->qstr($sesskey); $rs =& $MY_SESS_CONN->Execute($sql); return true; } //end function function gc($maxlifetime = null) { global $MY_SESS_CONN; $sql = 'DELETE FROM sess WHERE expiry<' . time(); $MY_SESS_CONN->Execute($sql); //由于经常性的对表 sess 做删除操作,容易产生碎片, //所以在垃圾回收中对该表进行优化操作。 $sql = 'OPTIMIZE TABLE sess'; $MY_SESS_CONN->Execute($sql); return true; } //end function } ///:~ //使用 ADOdb 作为数据库抽象层。 require_once('adodb/adodb.inc.php'); //数据库配置项,可放入配置文件中(如:config.inc.php)。 $db_type = 'mysql'; $db_host = '192.168.212.1'; $db_user = 'sess_user'; $db_pass = 'sess_pass'; $db_name = 'sess_db'; //创建数据库连接,这是一个全局变量。 $GLOBALS['MY_SESS_CONN'] =& ADONewConnection($db_type); $GLOBALS['MY_SESS_CONN']->Connect( $db_host, $db_user, $db_pass, $db_name); //初始化 SESSION 设置,必须在 session_start() 之前运行!! My_Sess::init(); ?>
五、遗留问题
如果网站的访问量很大的话,SESSION 的读写会频繁地对数据库进行操作,这样效率就会明显降低。考虑到 SESSION 数据一般不会很大,可以尝试用 C/Java 写个多线程的程序,用 HASH 表保存 SESSION 数据,并通过 socket 通信进行数据读写,这样 SESSION 就保存在内存中,读写速度应该会快很多。另外还可以通过负载均衡来分担服务器负载。不过这些都只是我自己的一些想法和假设,并没有实践过
======================================
面向对象的PHP开发模式(待完善中。。。)
======================================
一、环境
服务器:Linux (Apache 2.x, MySQL4.1.x, PHP4, Perl, SHELL, CVS, Sambar)
客户端:Windows (Ie6, UltraEdit, 其它辅助工具)
测试机:windows98/2K/xp/Linux (Ie5, Ie6, mozilla, firefox)
二、网页、程序、数据库的三层
所谓的网页并不是一般的静态网页,这里的网页是根据项目分析的具体情况进行拆分
后用html做的模板;这里的数据库包括数据库和与其它部分的接口程序,通常程序和数据库
程序可能会混合在一个文件里,但应该用函数的方式把它们尽量分开,其它程序如果要用数
据库直接调用这些函数即可,不能直接接触SQL语句。
三、项目分析--数据分析
一个项目在得到需求分析后,实际开发前第一步应该做的就是数据分析。数据分析就是
把项目过程中要用到的各式各样的数据堆在一块,根据它们的特点进行分类再分别组织,当
然它们之间还可能存在着各式各样的关联关系。做好这一步就使项目分析工作得到了一个良
好的开端,为下面的项目结构分析及数据处理的流程分析也提供了极大的方便。
四、项目分析--数据抽象
由数据分析后我们的脑子中应该能出现一些大致的数据模型及一些基本数据小模型组合
而成的大模型,一般情况下,我们把一些需要变化的数据建立数据库来进行维护,不需要变
化的数据做成一些常量,并针对这些数据类型抽象出相关的类,并建立进行数据库操作的相
关接口(函数形式,即方法),数据与数据的相关联的操作也可以抽象出一些基本的方法,
我们只需要在程序设计中进行调用即可。
五、项目分析--界面分析
我们分析好了数据,目的是组合出一个或者几个产品,而既然要做产品就要给别人看。
所以我们还要进行界面设计,当各种界面尽量考虑全面后,就将设计的界面制作成模板,并
写出相应的处理接口程序(所以,在程序眼里,界面也是一种数据),在写程序时进行使用。
六、项目分析--流程设计
网站式程序非常简单,按照流程调用我们设计好的各种数据即可。
七、案例分析
用户系统,现在我们分析一个最简单的例子,一个用户系统。
1. 数据分析,我们分析一个最简单的用户系统,所以这里只有两个数据,那就是用户名
和密码,继续分析还会想到我们应该给每条记录加一个编号(id),现在有了三个数据,实在没
有再可以添加的了。
2. 数据抽象,只有三个数据的数据模型,想到它可能出现的操作方法,我们做如下安排,
数据库接口(savetodb(), getfromdb(), delete()),分别为数据入库及出库还有删除;更改密
码(password())。另外考虑到对用户系统的管理及查看,所以会有一个集合类型的数据(list)。
3. 界面分析,登陆,验证成功,验证出错,修改密码,修改密码成功,修改密码出错,用
户注册,注册成功,注册出错;管理--用户列表,管理--用户信息查看,管理--修改用户
密码,管理--删除用户。
4. 示例代码
PHP 代码:
<?php
include_once "include.php";
/*
** 用途:用户系统数据抽象
** 作者:岳信明
** 时间:2005-8-30 10:05
*/
class User {
var $id = 0;
var $Name = "";
var $Password = "";
var $db = "";
var $tpl = "";
/*
** 函数功能:构造函数,指定类使用的数据库连接
** 参数说明:$tpl,显示模板处事句柄;$userdb,数据库连接
** 返 回 值:无
** 作 者:岳信明
** 创建时间:2005-8-30 10:37
*/
function User($vtpl = "", $userdb = "") {
if ($vtpl == "") {
global $tpl; // 外部定义数据库连接
$this->tpl =& $tpl;
} else {
$this->tpl = $vtpl;
}
if ($userdb == "") {
global $db; // 外部定义数据库连接
$this->db =& $db;
} else {
$this->db = $userdb;
}
}
/*
** 函数功能:将数据存入数据库
** 参数说明:无参数
** 返 回 值:true/false,成功/失败
** 作 者:岳信明
** 创建时间:2005-8-30 10:24
*/
function savetodb() {
if ($this->Name == "") {
return false;
}
if ($this->id) {
$strSQL = sprintf("UPDATE user SET Name='%s', Password='%s' "
. "WHERE id='%s'",
$this->Name,
$this->Password,
$this->id
);
} else {
$strSQL = sprintf("INSERT user (Name, Password) "
. "VALUES ('%s', '%s')",
$this->Name,
$this->Password
);
}
if ($this->db->query($strSQL)) {
return true;
} else {
return false;
}
}
/*
** 函数功能:从数据库中获取记录
** 参数说明:$id,记录编号
** 返 回 值:true/false,成功/失败
** 作 者:岳信明
** 创建时间:2005-8-30 10:32
*/
function getfromdb($id = 0) {
if ($id) {
$strSQL = sprintf("SELECT * FROM user WHERE id='%s'", $id);
} else if ($this->id) {
$strSQL = sprintf("SELECT * FROM user WHERE id='%s'",
$this->id
);
} else if ($this->Name != "") {
$strSQL = sprintf("SELECT * FROM user WHERE Name='%s'",
$this->Name
);
} else {
return false;
}
$this->db->query($strSQL);
if ($this->db->next_record()) {
$this->id = $this->db->f("id");
$this->Name = $this->db->f("Name");
$this->Password = $this->db->f("Password");
return true;
} else {
return false;
}
}
/*
** 函数功能:从数据库中删除记录
** 参数说明:$id,记录编号
** 返 回 值:true/false,成功/失败
** 作 者:岳信明
** 创建时间:2005-8-30 10:47
*/
function delete($id = 0) {
if (is_array($id)) { // 同时删除多条记录
foreach($id as $i) {
$strSQL = sprintf("DELETE FROM user WHERE id='%s'", $i);
$this->db->query($strSQL);
}
return true;
} else if ($id) {
$strSQL = sprintf("DELETE FROM user WHERE id='%s'", $id);
} else if ($this->id) {
$strSQL = sprintf("DELETE FROM user WHERE id='%s'", $this->id);
} else {
return false;
}
$this->db->query($strSQL);
return true;
}
/*
** 函数功能:显示登陆界面
** 参数说明:$placeholder,显示位置
** 返 回 值:无
** 作 者:岳信明
** 创建时间:2005-8-30 11:00
*/
function showLogin($placeholder) {
$this->tpl->addBlockfile($placeholder, "user_showLogin",
"tpl.user_showLogin.html"
);
$this->tpl->setCurrentBlock("user_showLogin");
$this->tpl->setVariable(array("user_Logintitle" => "用户登陆",
"strUsername" => "用户名",
"strPassword" => "密 码"
)
);
$this->tpl->parseCurrentBlock("user_showLogin");
}
/*
** 函数功能:处理登陆信息
** 参数说明:$placeholder,显示位置
** 返 回 值:true/false,成功/失败
** 作 者:岳信明
** 创建时间:2005-8-30 11:12
*/
function getLogin($placeholder = "") {
if (isset($_POST["login"])) {
if ($_POST["username"] == "") {
if ($placeholder != "") {
$this->tpl->setVarable($placeholder, "用户名不能为空!");
}
return false;
}
$this->Name = $_POST["username"];
$this->getfromdb();
if ($this->Password() == $_POST["password"]) {
return true;
}
} else {
if ($placeholder != "") {
$this->tpl->setVarable($placeholder, "登陆失败!");
}
return false;
}
}
/*
** 函数功能:显示注册界面
** 参数说明:$placeholder,显示位置
** 返 回 值:无
** 作 者:岳信明
** 创建时间:2005-8-30 13:33
*/
function showRegister($placeholder) {
$this->tpl->addBlockfile($placeholder, "user_showRegister",
"tpl.user_showRegister.html"
);
$this->setCurrentBlock("user_shoRegister");
// 在这里完成处理模板的代码
...
$this->parseCurrentBlock("user_shoRegister");
}
/*
** 函数功能:处理注册信息
** 参数说明:$placeholder,显示位置
** 返 回 值:true/false,注册成功/注册失败
** 作 者:岳信明
** 创建时间:2005-8-30 15:49
*/
function getRegister($placeholder = "") {
if (isset($_POST["register")) {
if ($_POST["username"] == "") { // 用户名合法性检查,可改成其它检查方式
if ($placeholder != "") { // 错误提示
$this->tpl->setVariable($placeholder, "用户名不合法!");
}
return false;
}
if ($_POST["password"] != $_POST["repassword"]) { // 密码合法性检查
if ($placeholder != "") { // 错误提示
$this->tpl->setVariable($placeholder, "两次输入密码不一致!");
}
return false;
}
$strSQL = sprintf("SELECT COUNT(*) FROM user "
. "WHERE Name='%s'",
$this->Name
);
$this->db->query($strSQL);
$this->db->next_record();
if ($this->db->f("COUNT(*)") > 0) {
return false;
} else {
$strSQL = sprintf("INSERT INTO user (Name, Password) "
. "VALUES('%s', '%s')",
$this->Name,
$this->Password
);
$this->db->query($strSQL);
return true;
}
} else {
return false;
}
}
} // 类User定义结束
/*
** 用途:用户系统数据列表抽象
** 作者:岳信明
** 时间:2005-8-30 17:21
*/
class UserList {
var $page = 0;
var $pages = 0;
var $pagesize = 9;
var $recordsum = 0;
var $Users = array();
var $c;
var $db = "";
var $tpl = "";
/*
** 函数功能:构造函数,新建一个类时对一些变量进行初始化
** 参数说明:无参数
** 返 回 值:无
** 作 者:岳信明
** 创建时间:2005-8-30 15:49
*/
function UserList($page = 1, $pagesize = 10,
$c, $vtpl = "", $vdb = "") {
$this->page = $page;
$this->pagesize = $pagesize;
$this->condition = $condition;
if ($vdb != "") {
$this->db = $vdb;
} else {
global $db;
$this->db = $db;
}
if ($vtpl != "") {
$this->tpl = $vtpl;
} else {
$this->tpl = $tpl;
}
$strSQL = sprintf("SELECT COUNT(*) FROM user WHERE '%s'",
$this->condition
);
$this->db->query($strSQL);
$this->db->next_record();
$this->recordsum = $this->db->f("COUNT(*)");
$this->pages = ceil($this->recordsum / $this->pagesize);
$strSQL = sprintf("SELECT * FROM user WHERE '%s' LIMIT '%s', '%s'",
$this->condition,
$this->page * $this->pagesize,
$this->pagesize + 1
);
$this->db->query($strSQL);
for ($i = 0; $this->db->next_record(); $i ++) {
$this->Users[$i] = new User($this->tpl, $this->db);
$this->Users[$i]->id = $this->db->f("id");
$this->Users[$i]->Name = $this->db->f("Name");
$this->Users[$i]->Password = $this->db->f("Password");
}
}
/*
** 函数功能:显示列表
** 参数说明:$placeholder,显示位置
** 返 回 值:无
** 作 者:岳信明
** 创建时间:2005-8-31 9:16
*/
function showUserList($placeholder) {
$this->tpl->addBlockfile($placeholder, "showUserList", "tpl.showUserList.html");
$this->tpl->setCurrentBlock("showUserList");
//在这里添加相应的处理代码
$this->tpl->setVariable("strTitle", "用户列表");
$strTitles = array("用户名", "操作");
$RecordOperations = array("重设密码" => "operate=passwd&id=",
"删除" => "operate=delete&id="
);
// 显示表头
foreach ($strTitles as $title) {
$this->tpl->setCurrentBlock("showRecordsTitle");
$this->tpl->setVariable("strHead", $title);
$this->tpl->parseCurrentBlock("showRecordsTitle");
}
// 显示记录及相关操作
if (is_array($this->Users)) { // 有记录
foreach ($this->Users as $user) {
$this->tpl->setCurrentBlock("showRecords");
$this->tpl->setCurrentBlock("showCell");
$this->tpl->setVariable("strCell", $user);
$this->tpl->parseCurrentBlock("showCell");
$this->tpl->setCurrentBlock("showCell");
foreach ($RecordOperations as $operation => $linkOperation) {
$this->tpl->setCurrentBlock("showOperations");
$this->tpl->setVariable("strOperation", $operation);
$this->tpl->setVariable("strLink", $_SERVER["REQUEST_URI"] . $linkOperation . $user->id);
$this->tpl->parseCurrentBlock("showOperations");
}
$this->tpl->parseCurrentBlock("showCell");
$this->tpl->parseCurrentBlock("showRecords");
}
} else { // 无记录
$this->tpl->setCurrentBlock("showRecords");
$this->tpl->setCurrentBlock("showCell");
$this->tpl->setVariable("strCell", "无记录");
$this->tpl->parseCurrentBlock("showCell");
$this->tpl->setCurrentBlock("showCell");
$this->tpl->setVariable("strCell", " ");
$this->tpl->parseCurrentBlock("showCell");
$this->tpl->parseCurrentBlock("showRecords");
}
$this->tpl->setCurrentBlock("showPageInfo");
$this->tpl->setVariable(array("intColspan" => "2",
"intRecordSum" => $this->recordsum,
"intPage" => $this->page,
"intPages" => $this->pages
)
);
$this->tpl->parseCurrentBlock("showPageInfo");
$this->tpl->parseCurrentBlock("showUserList");
}
}
?> <!-- php buffer end -->
HTML 代码:
把php代码进行语法着色,喜欢PHP的同志可以轻松的看代码啦。
同时添加了html代码运行功能。
此插件是在以前某位同志发布的Discuz!4的php代码高亮显示基础之上修改完成。
另外,本人的php学习进入了停滞状态,
希望有人能帮助我走出php的学习困境
My QQ:5642382 我的QQ群:2577162
功能:发贴时使用[php]php代码[/php]标记来高亮显示php代码,
使用[runcode]Html代码[/runcode]标记来运行HTML代码(在客户端运行非服务器)。
插件添加步骤如下:
1.修改include/common.js
查找
function copycode(obj) {
var rng = document.body.createTextRange();
rng.moveToElementText(obj);
rng.scrollIntoView();
rng.select();
rng.execCommand("Copy");
rng.collapse(false);
}
在这个函数结束的下面添加:
function runCode(obj) {
var winname = window.open('', "_blank", '');
winname.document.open('text/html', 'replace');
winname.document.writeln(obj.value);
winname.document.close();
}
function saveCode(obj) {
var winname = window.open('', '_blank', 'top=10000');
winname.document.open('text/html', 'replace');
winname.document.writeln(obj.value);
winname.document.execCommand('saveas','','code.htm');
winname.close();
}
2.修改include/discuzcode.func.php
查找
$discuzcodes['codecount']++;
return "[\tDISCUZ_CODE_$discuzcodes[pcodecount]\t]";
}
(注:也可以用Editplus直接查找到代码的第110行来进行下面的添加)
在下面继续添加:
//------- 添加 [runcode] Html代码 [/runcode] 来运行Html代码
function runcodedisp($code) {
global $discuzcodes;
$discuzcodes['pcodecount']++;
$code = htmlspecialchars(str_replace('\\"', '"', preg_replace("/^[\n\r]*(.+?)[\n\r]*$/is", "\\1", $code)));
$discuzcodes['codehtml'][$discuzcodes['pcodecount']] = "<br><br><div smalltxt\" margin-left: 2em; margin-right: 2em\"><textarea name=\"runcode$discuzcodes[codecount]\" rows=\"1\" cols=\"95\" height:200px\">$code</textarea><br><input type=\"button\" value=\"运行代码\" onclick=\"runCode(runcode$discuzcodes[codecount])\"> <input type=\"button\" value=\"复制代码\" onclick=\"copycode(runcode$discuzcodes[codecount])\"> <input type=\"button\" value=\"另存代码\" onclick=\"saveCode(runcode$discuzcodes[codecount])\"> <script language=\"JavaScript\">ffcod = delpost.runcode$discuzcodes[codecount] .value; ffcod = ffcod.replace(/<br \/>/g,''); delpost.runcode$discuzcodes[codecount] .value = ffcod;</script> 提示:您可以先修改部分代码再运行</div><br>";
$discuzcodes['codecount']++;
return "[\tDISCUZ_CODE_$discuzcodes[pcodecount]\t]";
}
//------- 添加 [php] php代码 [/php] 来高亮显示php代码,进行php语法着色
function phpcodedisp($code) {
global $discuzcodes;
$discuzcodes['pcodecount']++;
$code = phphighlite(str_replace("\\\"", "\"", $code));
$discuzcodes['codehtml'][$discuzcodes['pcodecount']] = "<br><div msgheader\"><div right\"><a href=/index.html"###\" smalltxt\" onclick=\"copycode($('phpcode$discuzcodes[codecount]'));\">[复制PHP代码]</a></div>PHP代码如下:</div><div msgborder\" id=\"phpcode$discuzcodes[codecount]\">$code</div><br>";
$discuzcodes['codecount']++;
return "[\tDISCUZ_CODE_$discuzcodes[pcodecount]\t]";
}
function phphighlite($code) {
if(!strpos($code,"<?\n") && !strpos($code,'<?') && substr($code,0,2) != '<?') {
$code = '<'.'?'.trim($code).' ?'.'>';
$addedtags = 1;
}
ob_start();
$oldlevel = error_reporting(0);
highlight_string($code);
error_reporting($oldlevel);
$buffer = ob_get_contents();
ob_end_clean();
if ($addedtags) {
$openingpos = strpos($buffer, '<?');
$closingpos = strrpos($buffer, '?');
$buffer = substr($buffer, 0, $openingpos).substr($buffer, $openingpos+5, $closingpos-($openingpos+5)).substr($buffer, $closingpos+5);
}
$buffer = str_replace('"', "\"", $buffer);
$buffer = str_replace('<br />', '', $buffer);
return $buffer;
}
//------- 结束
3.继续修改include/discuzcode.func.php
查找
$message = preg_replace("/\s*\[code\](.+?)\[\/code\]\s*/ies", "codedisp('\\1')", $message);
在其下面继续添加:
//------- runcode
$message = preg_replace("/\s*\[runcode\](.+?)\[\/runcode\]\s*/ies", "runcodedisp('\\1')", $message);
//------- php
$message = preg_replace("/\s*\[php\](.+?)\[\/php\]\s*/ies", "phpcodedisp('\\1')", $message);