thinkphp学习笔记3—项目编译和调试模式
原文:thinkphp学习笔记3—项目编译和调试模式
1.项目编译
在章节2.4项目编译中作者讲到使用thinkphp的项目在第一次运行的时候会吧核心需要加载的文件去掉空白和注释合并到一个文件中编译并缓存,第二次运行时直接载入编译缓存,这样省去一些IO开销,加快执行速度。并且在3.0以上的版本中海做了一些优化:
1.合并和兴编译缓存和项目编译缓存,不再生成两个缓存文件
2.直接对本地环境生成设置和常量定义减少环境判断
3.编译缓存可以直接替换框架入口甚至项目入口,甚至脱离框架独立运行
4.通过参数设置,生成的编译缓存载入外部的常量定义,便于产品用户定义
貌似高上大,后面继续讲在没有开启调试模式的情况下会在thinkphp\Application\Runtime目录下生成一个~runtime.php的文件,就是编译缓存文件。我实验了一下,在项目入口文件中有一句define('APP_DEBUG',True);修改成define('APP_DEBUG',False);,再次运行,在Runtime目录果然生成了一个类似文件,不过名称有点差别,是common~runtime.php,截图如下:
图1
打开这个common~runtime.php文件,我们可以看到内容是一行没有空格的代码,类似于压缩过的js代码,如下:
图2
注意环境改变之后这个缓存文件应该删除,不能把本地生成的编译缓存拷贝到其他服务器或者环境直接使用,只能再次生成。
编译缓存的内容包含:系统函数库,系统基础核心类库,扩展定义的核心行为类库,项目配置文件,项目函数文件。在项目入口文件中可以更改这个文件的生成目录例如:
define('RUNTIME_PATH','./App/temp/');
持此之外开可以弟子你故意缓存文件名,如下:
define('RUNTIME_FILE','./App/temp/runtime_cache.php');
我发现可以定义路径,但是文件名确没有如愿生成runtime_cache.php,始终是common~runtime.php。
我的修改如下:
define('RUNTIME_PATH','./temp/');define('RUNTIME_FILE','./temp/runtime_cache.php');
最后得到结果如下截图:
图3
这里面生成了一系列的目录和文件,都是和编译缓存相关的。下面来替换框架入口文件为这个缓存文件看看能否正常运行,修改代码如下:
// 引入ThinkPHP入口文件require './temp/common~runtime.php';//require './ThinkPHP/ThinkPHP.php';
结果出现了错误,代码如下:
Fatal error: Class 'Think\Think' not found inD:\Serious\phpdev\thinkphp\temp\common~runtime.php on line 1
好吧,一开始只要知道有这么个东西就行了,不要玩这么高级的东西,先看看基本功能如何实现的吧。
2.调试模式
在项目入口文件中可以通过语句define('APP_DEBUG',True);来设置当前项目是否使用debug模式,待完成测试要部署到生产环境时再将这个值设置成False,就是部署模式。
使用调试模式给程序员带来以下便利:
错误信息会提交到日志记录中便于调试
关闭末班缓存,修改末班可以即时生效
记录sql日志,方便分析sql语句
关闭字段缓存,数据字段修改不受缓存影响
严格检查大小写,避免部署到linux上之后出现问题
上面很多特性都需要在配置文件中修改配置,下一篇文章中再做介绍。
thinkphp学习笔记3—项目编译和调试模式
1.项目编译
在章节2.4项目编译中作者讲到使用thinkphp的项目在第一次运行的时候会吧核心需要加载的文件去掉空白和注释合并到一个文件中编译并缓存,第二次运行时直接载入编译缓存,这样省去一些IO开销,加快执行速度。并且在3.0以上的版本中海做了一些优化:
1.合并和兴编译缓存和项目编译缓存,不再生成两个缓存文件
2.直接对本地环境生成设置和常量定义减少环境判断
3.编译缓存可以直接替换框架入口甚至项目入口,甚至脱离框架独立运行
4.通过参数设置,生成的编译缓存载入外部的常量定义,便于产品用户定义
貌似高上大,后面继续讲在没有开启调试模式的情况下会在thinkphp\Application\Runtime目录下生成一个~runtime.php的文件,就是编译缓存文件。我实验了一下,在项目入口文件中有一句define('APP_DEBUG',True);修改成define('APP_DEBUG',False);,再次运行,在Runtime目录果然生成了一个类似文件,不过名称有点差别,是common~runtime.php,截图如下:
图1
打开这个common~runtime.php文件,我们可以看到内容是一行没有空格的代码,类似于压缩过的js代码,如下:
图2
注意环境改变之后这个缓存文件应该删除,不能把本地生成的编译缓存拷贝到其他服务器或者环境直接使用,只能再次生成。
编译缓存的内容包含:系统函数库,系统基础核心类库,扩展定义的核心行为类库,项目配置文件,项目函数文件。在项目入口文件中可以更改这个文件的生成目录例如:
define('RUNTIME_PATH','./App/temp/');
持此之外开可以弟子你故意缓存文件名,如下:
define('RUNTIME_FILE','./App/temp/runtime_cache.php');
我发现可以定义路径,但是文件名确没有如愿生成runtime_cache.php,始终是common~runtime.php。
我的修改如下:
define('RUNTIME_PATH','./temp/');define('RUNTIME_FILE','./temp/runtime_cache.php');
最后得到结果如下截图:
图3
这里面生成了一系列的目录和文件,都是和编译缓存相关的。下面来替换框架入口文件为这个缓存文件看看能否正常运行,修改代码如下:
// 引入ThinkPHP入口文件require './temp/common~runtime.php';//require './ThinkPHP/ThinkPHP.php';
结果出现了错误,代码如下:
Fatal error: Class 'Think\Think' not found inD:\Serious\phpdev\thinkphp\temp\common~runtime.php on line 1
好吧,一开始只要知道有这么个东西就行了,不要玩这么高级的东西,先看看基本功能如何实现的吧。
2.调试模式
在项目入口文件中可以通过语句define('APP_DEBUG',True);来设置当前项目是否使用debug模式,待完成测试要部署到生产环境时再将这个值设置成False,就是部署模式。
使用调试模式给程序员带来以下便利:
错误信息会提交到日志记录中便于调试
关闭末班缓存,修改末班可以即时生效
记录sql日志,方便分析sql语句
关闭字段缓存,数据字段修改不受缓存影响
严格检查大小写,避免部署到linux上之后出现问题
上面很多特性都需要在配置文件中修改配置,下一篇文章中再做介绍。
作者:Tyler Ning
出处:http://www.cnblogs.com/tylerdonet/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,如有问题,可以通过以下邮箱地址williamningdong@gmail.com
联系我,非常感谢。
ThinkPHP框架学习(含数据库及I函数)
参考文献:https://www.kancloud.cn/manual/thinkphphttps://www.jianshu.com/p/ef3ee8260b2d
www WEB子目录
├─index.php 入口文件
├─README.md README文件仅用于说明,实际部署的时候可以删除。
├─Application 应用目录,默认是空的,但是第一次访问入口文件会自动生成
├─Public 资源文件目录
└─ThinkPHP 框架目录
│ ├─Common 核心公共函数目录
│ ├─Conf 核心配置目录
│ ├─Lang 核心语言包目录
│ ├─Library 框架类库目录
│ │ ├─Think 核心Think类库包目录
│ │ ├─Behavior 行为类库目录
│ │ ├─Org Org类库包目录
│ │ ├─Vendor 第三方类库目录
│ │ ├─ ... 更多类库目录
│ ├─Mode 框架应用模式目录
│ ├─Tpl 系统模板目录
│ ├─LICENSE.txt 框架授权协议文件
│ ├─logo.png 框架LOGO文件
│ ├─README.txt 框架README文件
│ └─ThinkPHP.php 框架入口文件
入口文件(index.php)
//定义应用入口文件
define('APP_PATH','./Application/');
require './ThinkPHP/ThinkPHP.php';
APP_PATH的定义支持相对路径和绝对路径,但必须以“/”结束
自动创建目录(第一次访问index.php):
Application
├─Common 应用公共模块(不能直接访问)
│ ├─Common 应用公共函数目录
│ └─Conf 应用公共配置文件目录
├─Home 默认生成的Home模块
│ ├─Conf 模块配置文件目录
│ ├─Common 模块公共函数目录
│ ├─Controller 模块控制器目录
│ ├─Model 模块模型目录
│ └─View 模块视图文件目录
├─Runtime 运行时目录
│ ├─Cache 模版缓存目录
│ ├─Data 数据目录
│ ├─Logs 日志目录
│ └─Temp 缓存目录
在自动生成目录结构的同时,在各个目录下面还生成了index.html文件,这是ThinkPHP自动生成的目录安全文件。为了避免某些服务器开启了目录浏览权限后可以直接在浏览器输入URL地址查看目录,系统默认开启了目录安全文件机制,会在自动生成目录的时候生成空白的index.html文件
控制器
在自动生成的Application/Home/Controller目录下面有一个 IndexController.class.php 文件,这就是默认的Index控制器文件。控制器类的命名方式是:
控制器名(驼峰法,首字母大写)+Controller
控制器文件的命名方式是:类名+class.php(类文件后缀)
命名规范
类文件都是以.class.php为后缀(这里是指的ThinkPHP内部使用的类库文件,不代表外部加载的类库文件),使用驼峰法命名,并且首字母大写,例如 DbMysql.class.php;
类的命名空间地址和所在的路径地址一致,例如Home\Controller\UserController类所在的路径应该是 Application/Home/Controller/UserController.class.php;
确保文件的命名和调用大小写一致,是由于在类Unix系统上面,对大小写是敏感的(而ThinkPHP在调试模式下面,即使在Windows平台也会严格检查大小写);
类名和文件名一致(包括上面说的大小写一致),例如 UserController类的文件命名是UserController.class.php, InfoModel类的文件名是InfoModel.class.php, 并且不同的类库的类命名有一定的规范;
函数、配置文件等其他类库文件之外的一般是以.php为后缀(第三方引入的不做要求);
函数的命名使用小写字母和下划线的方式,例如 get_client_ip;
方法的命名使用驼峰法,并且首字母小写或者使用下划线“_”,例如 getUserName,_parseType,通常下划线开头的方法属于私有方法;
属性的命名使用驼峰法,并且首字母小写或者使用下划线“_”,例如 tableName、_instance,通常下划线开头的属性属于私有属性;
以双下划线__打头的函数或方法作为魔法方法,例如 __call 和 __autoload;
常量以大写字母和下划线命名,例如 HAS_ONE和 MANY_TO_MANY;
配置参数以大写字母和下划线命名,例如HTML_CACHE_ON;
语言变量以大写字母和下划线命名,例如MY_LANG,以下划线打头的语言变量通常用于系统语言变量,例如 CLASS_NOT_EXIST;
对变量的命名没有强制的规范,可以根据团队规范来进行;
ThinkPHP的模板文件默认是以.html 为后缀(可以通过配置修改);
数据表和字段采用小写加下划线方式命名,并注意字段名不要以下划线开头,例如 think_user 表和 user_name字段是正确写法,类似 _username 这样的数据表字段可能会被过滤。
配置加载
惯例配置->应用配置->模式配置->调试配置->状态配置->模块配置->扩展配置->动态配置//配置的优先顺序从右到左
框架内置有一个惯例配置文件(ThinkPHP/Conf/convention.php)
应用配置文件也就是调用所有模块之前都会首先加载的公共配置文件(Application/Common/Conf/config.php)
如果使用了普通应用模式之外的应用模式的话,还可以为应用模式单独定义配置文件,文件命名规范是: Application/Common/Conf/config_应用模式名称.php(仅在运行该模式下面才会加载)(可选)
开启调试模式,会自动加载框架的调试配置文件(ThinkPHP/Conf/debug.php)和应用调试配置文件(Application/Common/Conf/debug.php)(可选)
在公司和家里分别设置不同的数据库测试环境。在公司,在入口文件中定义:define('APP_STATUS','office');就会自动加载该状态对应的配置文件(Application/Common/Conf/office.php)。回家后,修改为:define('APP_STATUS','home');就会自动加载该状态对应的配置文件(Application/Common/Conf/home.php)。(可选)
读取配置
配置文件,统一使用系统提供的C方法来读取已有的配置。(ThinkPHP/Common/functions.php)
用法:C('参数名称')
$model = C('URL_MODEL');//读取当前的URL模式配置参数
C('my_config',null,'default_config');//如果my_config尚未设置的话,则返回default_config字符串
因为配置参数是全局有效的,因此C方法可以在任何地方读取任何配置,即使某个设置参数已经生效过期了。
加载扩展配置
①在Application/Home/Conf目录下新建user.php,内容如下
<?php
return array(
'USER_TYPE' => 2, //用户类型
'USER_AUTH_ID' => 10, //用户认证ID
'USER_AUTH_TYPE' => 2, //用户认证模式
);//可以不用加?>
②修改同级的config.php
<?php
return array(
'LOAD_EXT_CONFIG'=> array('USER'=>'user')
);
③修改Application/Home/Controller/IndexController.class.php
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function index(){
print_r(C('USER'));
}
}
打开localhost,会出现Array ( [USER_TYPE] => 2 [USER_AUTH_ID] => 10 [USER_AUTH_TYPE] => 2 )
数据库操作分析
①系统公共函数库: \ThinkPHP\Common\functions.php(封装了TP开放给外部的函数)
②ThinkPHP Model模型类: ThinkPHP\Library\Think\Model.class.php (TP的数据库架构类,提供curd类库,是一个对外的接口 )
③TP内部curd类: ThinkPHP\Library\Think\Db\Driver.class.php (这个类的函数都被Model类中的curd操作间接的调用)
Tp在执行数据库操作之前
函数M使用了以后会自动创建new Model类并且会实例化为一个对象返回此资源。
接着这个对象调用了where方法并且格式化处理以后,会将这个值赋值给此对象的一个成员变量$options(注:如果说我们在此对象中有调用其他的方法赋值例如where,table,alias,data,field,order,limit,page,group,having,join,union,distinct,lock,cache,comment等等这种操作方法,那么都会先赋值给此对象,而不是在代码直接进行sql语句拼接,所以我们使用Tp的连贯操作的时候,就不需要像SQL语句拼接那样需要考虑到关键字的顺序问题
处理完了前面选项之后,接下来就会去调用我们的find()方法去调用底层的一个select方法(Driver.class.php这个类中的select方法)来获取数据。所谓的find()方法就是等同于先给此对象的一个成员变量$options赋值操作limit=1然后进行select操作来获取对应的数据。
最终的sql语句:SELECT * FROMblog_adminWHERE username='admin' limit 1
如果给赋值了一个操作:M('admin')->field('username,password')->where( array('username'=>$username) )->find();
执行的语句为SELECTusername,password FROMblog_adminWHERE username='admin' limit 1
①thinkphp\ThinkPHP\Library\Think\Model.class.php 里的重要成员变量:
protected $pk = 'id';// 主键名称
protected $fields = array(); // 字段信息
protected $options = array(); // 数据信息
protected $methods = array('strict','order','alias','having','group','lock','distinct','auto','filter','validate','result','token','index','force');// 链操作方法列表
②where()方法的执行过程:
$parse = array_map(array($this->db,'escapeString'),$parse);//对传递到字符串类型的数据,调用mysql_escape_string函数来处理任何返回
可以通过官方文档查看TP如何防止SQL注入:(http://document.thinkphp.cn/manual_3_2.html#sql_injection)where() 方法如果 传递的是$Model->where("id=%d and username='%s' and xx='%f'",array($id,$username,$xx)) 这种格式的,就会进行 mysql 的mysql_escape_string函数进行处理(mysql_escape_string总是将“ ' ”转换成“ \ ”)。 处理完成以后就会将已处理完成的数组赋值到M对象的成员函数$this->options['where'](options为success,error,display.....) 随后返回,供我们进行下一步的处理。
③find()方法的执行过程:
$options = $this->_parseOptions($options);//分析表达式,获取执行的表的名称,获取模型的名称,最后对表的字段进行处理。
$resultSet = $this->db->select($options);//调用db对象里的select方法查询数据
此方法的功能就是 获取主键,完善model类的成员变量,options数组,然后实例化 db类,调用select 方法获取数据,然后处理数据完以后返回数据
Find方法使用的 $this->_parseOptions()
这个方法的主要就是 获取操作的表名,查看是否有取别名,获取操作的模型,比对当前表的数据库字段是否一致,若有不一致的字段 $this->options['strict'] 设置了的时侯,进行报错处理 否则进行删除多余字段的处理。 执行过滤的方法为_parseType ,功能是数据类型检测并且进行强制转换(强制转换的类型为int,float,bool 三种类型)
Find方法使用的$this->db->select() 方法
$this->db 是在ThinkPHP\Library\Think\Db类中的方法
①其中parseSql()这个函数的主要功能是拼接sql语句$this->parseWhere(!empty($options['where'])?$options['where']:''),parseWhere 方法比其他的要复杂的多,其他的都是拼接字符串,过滤,然后返回。)
②parseWhere()方法
这方法会去判断传进来的变量内容是否是字符串,如果是的话,就直接返回,如果不是字符串而是数组的话,那么就会挨个的解析,并且判断是否是特殊的条件表达式,如果是,调用parseThinkWhere()方法(主要是解析特殊的条件并且调用parseValue ()方法),已上条件都不匹配的情况下就认为是普通查询 普通查询都会调用parseWhereItem 方法
③parseWhereItem()方法
protected function parseWhereItem($key,$val)//$val值是从where函数接收到的数据,然后经过这个方法会先去判断是否是数组,不是数组就会到下面的流程
......
$exp = strtolower($val[0]);//$exp的值=执行表达式(可以操作sql拼接的流程)=$val[0]
······
elseif('bind' == $exp ){ // 使用表达式
$whereStr .= $key.' = :'.$val[1];
}elseif('exp' == $exp ){ // 使用表达式
$whereStr .= $key.' '.$val[1];
}elseif(preg_match('/^(notin|not in|in)$/',$exp)){ // IN 运算
if(isset($val[2]) && 'exp'==$val[2]) {
$whereStr .= $key.' '.$this->exp[$exp].' '.$val[1];//这三句 $whereStr .没有使用TP的过滤。
进入此方法以后会发现这个方法会根据 $exp 变量的不同 拼接不同的sql语句 而在这个方法中看到最多的就是parseValue()方法了
④parseValue ()方法
这个方法会去调用escapeString()方法 (将传进来的变量进行addslashes 然后返回)
嗯。。。。。。。整体流程看起来我们可以发现TP使用的过滤方法就是一个简单addslashes 来防止过滤
I 函数(官方http://document.thinkphp.cn/manual_3_2.html#input_filter)
路径:ThinkPHP\Common\functions.php
方法名:function I($name,$default='',$filter=null,$datas=null)主要功能
确定数据类型
对数据进行循环取值
调用think_filter 函数进行过滤
在think_filter中:
if(preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i',$value)){
$value .= ' ';//正则匹配,过滤查询特殊字符,然后在这些条件表达式后面添加一个空格
例如:
没有使用think_filter 函数时:
goods_name[0]=in&goods_name[1]=(true) and (updatexml(1,concat(1,(select user())),1))--&goods_name[2]=exp
使用了think_filter函数时:
goods_name[0]=in &goods_name[1]=(true) and (updatexml(1,concat(1,(select user())),1))--&goods_name[2]=exp
其中elseif(preg_match('/^(notin|not in|in)$/',$exp))中If( in(空格) == 'in')不匹配,也就防止了sql注入的产生
审计方式
①进入目录直接搜索 $_POST $_GET 查看是否带入了 where 查询。
② 全局搜索 where 查看是否有字符串拼接的痕迹
③全局搜索 order,having,group,alias (例:当我们可以操控 order传进去的参数时,也是可以进行注入的,这是因为tp对order方法只是一个字符串拼接的操作 )
④全局搜索 join ,field,执行的都是字符串拼接,只要可以外部操作就可以产生注入
将ThinkPHP和UCenter整合
最近应公司的要求,要开发一个有点像QQ空间那样的会员管理中心网站,发现UCenter的很多功能酷似QQ空间,于是选择了UCenter作为程序的会员管理中心。前台嘛就选择我之前基于ThinkPHP3.1.2框架开发的WBlog好了。但是问题又来了:要求在WBlog前台注册的会员登录时要与UCenter同步,这就是我这次要解决的问题--ThinkPHP与UCenter的整合。我在网上搜索了一下,ThinkPHP与UCenter的整合并不少,但是似乎越看越觉得头晕,不是少这就是少那,到头来差之毫厘,谬以千里,真够折腾的。我仔细阅读了UCenter的开发文档后,经过多次的调试,终于完成了ThinkPHP与UCenter的整合。感觉这个结果是从苦水里泡出来的一样,呵呵。。。现在把整合的记录分享给需要的童鞋。温馨提示:在thinphp与UCenter整合中,您需要的基本条件是,有一定的PHP基础,对ThinkPHP框架有所了解,会布署目录和配置数据。好了,我们开始吧!下载并安装下面的程序WBlog3.1.2UCenter_Home_2.0_SC_UTF8UCenter 1.6.0安装WBlog博客程序已发布的WBlog博客程序还没有会员中心,本次测试的会员中心是后面才写的。如果你能等的话要待我整理好WBlog的会员中心程序才发布,不能等的话也不会影响下面的程序整合,因为你可以找或者自己写简单的满足以下两个条件的thikphp程序:1、可以注册会员2、可以登录和退出。这里我就使用WBlog3.1.2了。在服务器的根目录新建目录wblog1,把下载的WBlog3.1.2解压得到的WBlog目录里的所有文件复制到wblog1。在浏览器输入http://127.0.0.1/wblog1/install/,安装WBlog3.1.2。温馨提示:在整合时最容易搞错的就是路径问题,在接下来的整合操作的根目录都是指wblog1目录,所以要和服务器根目录区别开来。安装UCenter 1.6.0(简体UTF-8)这个程序到官方去下载吧。在网站根目录wblog1新建 ucenter 目录,解压UCenter 1.6.0,把解压得到的upload目录里的所有文件复制到刚才新建的ucenter目录里。在浏览器输入http://127.0.0.1/wblog1/ucenter/install/,安装UCenter 1.6.0。安装UCenter_Home_2.0_SC_UTF8(简体中文版)在网站根目录wblog1新建 uh目录并解压UCenter_Home_2.0_SC_UTF8,把解压得到的upload目录里的所有文件复制到刚才新建的uh目录里。在浏览器输入http://127.0.0.1/wblog1/uh/install/,安装UCenter_Home_2.0_SC_UTF8。需要注意的是,安装UCenter 1.6.0和UCenter_Home_2.0_SC_UTF8时顺序不能颠倒,否则无法安装。我们安装完了WBlog3.1.2、UCenter_Home_2.0_SC_UTF8和UCenter 1.6.0三个程序后。接下来找到下载的UCenter 1.6.0,把 advanced 目录里面的uc_client 和 examples下面的api 文件夹复制到网站根目录wblog1下,和ThinkPHP在同一目录。找到项目W3note的配置文件夹wblog1/ Conf/,在其新建一个UCenter的配置文件 config_ucenter.php ,然后在WBlog1\W3note\Lib\ORG目录下新建一个UCenter的通讯处理类文件UcService.class.php,我们先不要管文件里面写什么代码,后面将会讲到。好了到这里已经把后面要操作的目录和文件都布局好了。为了理清目录和文件之间的层次关系,我把目录和文件制成目录树列出来:wblog1根目录| – index.php//前台入口文件| – admin.php| – W3note//前台项目| | – Lib| | | – ORG| | | | –UcService.class.php //UCenter的通讯处理类文件| | – Conf//W3note项目的配置目录| | | –config_ucenter.php //UCenter的配置文件| || – Admin| – ThinkPHP //thinkphp3.1.2核心包和一些扩展| – install| – api| | – uc.php|| – uc_client| – ucenter| – uh这样我们就可以一目了然了。现在开始api目录里面的uc.php 配置了,首先打开这个文件,找到36行的位置这一行代码
1
require_once DISCUZ_ROOT.'./config.inc.php';
把'./config.inc.php'这一部分替换成'./W3note/Conf/config_ucenter.php'往下找还会看到几处的'./config.inc.php',按照上面的操作全部替换掉。这样做目的是把前面建的配置文件config_ucenter.php导进来。 接下来我登录http://127.0.0.1/wblog1/ucenter,在打开左边菜单“应用管理”这一项,然后添加一个新应用,这时我们发现好多东西要填!照着下面操作就是了。先看应用类型,因为这是我们自己开发的程序,所以就选其它吧,再看应用名称,随便填,只要不超过20字节就行了,我这里填wblog。接下来是应用的主URL,这里填网站的主页http;//127.0.0.1/wblog1,注意了,后面没有“/”。至于应用的其他URL还有应用IP这两项就跳过,不用管它了。接下来是通信密钥,就按其旁边的提示填就是了,我这里填abc123456。往下是应用的物路径,提示说默认为空,那就留空吧,还有查看个人资料页面地址也留空吧。接下来是应用接口文件名称,默认为uc.php,保持原状吧。继续往下看,标签单条显示模板还有标签模板标记说明这两项也不用理它,跳过。是否开启同步登录,选是;是否接受通知也选是吧。终于填完了,那就点击提交吧!还记得前面在uc.php导入的文件config_ucenter.php吗,里面可是一片空白啊,现在我们就来放些配置信息进去。刚才我们填好的信息提交后,会在提交按钮下面的“应用的UCenter配置信息”下面生成了一些配置信息,我们直接把它复制,然后粘帖到config_ucenter.php,保存。网上很多教程到这里就表示通信成功了,在这里我非常惊讶!因为通信并未成功!我在这里折腾了一些时间,后来仔细检了uc.php文件的代码,发现59行:
1
require_once DISCUZ_ROOT.'./include/db_mysql.class.php';
我在布局的目录中始终找不到db_mysql.class.php,后来发现db_mysql.class.php存UCenter_1.6.0_SC_UTF8\advanced\examples\include目录中,这就是问题所在!因为我们之前复制的只是UCenter_1.6.0_SC_UTF8\advanced\examples\中的api文件。解决这一问题的办法就是把上面的代码修改为
1
require_once DISCUZ_ROOT.'./uc_client/lib/db.class.php';
导入的数据库类文件变了,那么我们也要对其所涉及的内容作相应的修改,在uc.php59行的下面如
1
2
3
4
5
6
7
$GLOBALS['db'] = new dbstuff;
$GLOBALS['db']->connect($dbhost, $dbuser, $dbpw, $dbname, $pconnect, true, $dbcharset);
$GLOBALS['tablepre'] = $tablepre;
修改为
$GLOBALS['db'] = new ucclient_db;
$GLOBALS['db']->connect(UC_DBHOST, UC_DBUSER, UC_DBPW, UC_DBNAME, UC_DBCONNECT, true, UC_DBCHARSET);
$GLOBALS['tablepre'] = UC_DBTABLEPRE;//关键
上面数据库的链接信息的静态变量来自config_ucenter.php。好了,我们回到后台ucenter刷新一下页面,发现什么---通信成功了!那么接下来我们的目标:在thinkphp中会员注册成功时,UCenter Home也同时注册成功。首先在项目W3note入口文件index.php配置一个常量define('WBLOG_ROOT_PATH', rtrim(dirname(__FILE__), '/\\') . DIRECTORY_SEPARATOR);//物理根目录常量WBLOG_ROOT_PATH是网站根目录wblog1的物理根目录,在我本地服务器打印输出:D:\phpsever\apache2\htdocs\wblog1\有必要在这里强调一下,理解WBLOG_ROOT_PATH很重要,因为在调试过程中路径最容易出错。还记得前面我们建的UcService.class.php 文件吗?如果忘记了请看一下前面的目录树。打开UcService.class.php文件,新建一个类UcService,然后写一个构造方法导入W3note/Conf/config_ucenter.php和uc_client/client.php两个文件。代码:
1
2
3
4
5
class UcService{
public function __construct(){
include_once(WBLOG_ROOT_PATH . 'W3note/Conf/config_ucenter.php');
include_once(WBLOG_ROOT_PATH . 'uc_client/client.php');
}
接下来我们写一个会员注册方法register,如public function register($username, $password, $email){}方法体放什么代码呢?其实很简单,因为UCenter的开发文档已经为我们准备好了!找到之前下载的UCenter_1.6.0_SC_UTF8,用浏览器打开UCenter_1.6.0_SC_UTF\advanced\document\index.htm,然后在左边的菜单栏找到“用户接口”,看到用户注册示例 (PHP),把其下的代码复制过来。如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 会员注册
*/
public function register($username, $password, $email){
$uid = uc_user_register($username, $password, $email);//UCenter的注册验证函数
if($uid <= 0) {
if($uid == -1) {
return '用户名不合法';
} elseif($uid == -2) {
return '包含不允许注册的词语';
} elseif($uid == -3) {
return '用户名已经存在';
} elseif($uid == -4) {
return 'Email 格式有误';
} elseif($uid == -5) {
return 'Email 不允许注册';
} elseif($uid == -6) {
return '该 Email 已经被注册';
} else {
return '未定义';
}
} else {
return intval($uid);//返回一个非负数
}
}
}
这个注册方法register的作用是,在wblog1的会员注册中成功注册一个会员时,也会成功注册UCenter Home的会员中心中。UCenter的注册方法我们在上面已经写好了,现在回到thinkphp。我们在前台W3note项目的控制器MemberAction写一个注册方法,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
用户名:$username,
密码:$password,
邮箱:$email
*/
public function addmember(){
if($this->isPost()){
$username = $_POST['username'];
$email = $_POST['email'];
$password = trim($_POST['password']);
import("@.ORG.UcService");//导入UcService.class.php类
$ucService = new UcService;//实例化UcService类
$uid = $ucService->register($username, $password, $email);//注册到UCenter
if($uid){//如果上面注册成功将返回一个int类型的数字
$M = D('Member');
if ($vo = $M->create()) {
if ($M->add()) {
$this->success('注册成功!');
} else {
$this->error('注册失败!');
}
} else {
$this->error();
}
}else{
exit($uid);
}
}else{
$this->error('非法数据!');
}
}
1
2
3
我们在thinkphp会员注册页面注册一个帐号,提交表单后,查看wblog1和UCenter的会员数据表,发现两张表都存相同的帐号,说明同步注册已经成功了!
有了帐号我们就可以来做同步登录了。
打开UcService.class.php文件,添加一个UC登录和一个登出的方法,代码到UCenter接口开发手册的用户接口那里找用户登录示例代码,把它复制过来,然后稍微更改一下,使其带有返回值,以便下一步的操作,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public function uc_login($username, $password){
list($uid, $username, $password, $email) = uc_user_login($username, $password);
if($uid > 0) {
return array(
'uid' => $uid,
'username' => $username,
'password' => $password,
'email' => $email
);
} elseif($uid == -1) {
return '用户不存在,或者被删除';
} elseif($uid == -2) {
return '密码错误';
} elseif($uid == -3) {
return '安全提问错误';
} else {
return '未定义';
}
}
继续在用户接口那里找到同步登录的代码示例,找到“uc_user_synlogin($uid);”其作用是执行同步登录,然后写成uc_synlogin方法如下:
1
2
3
public function uc_synlogin($uid){
return uc_user_synlogin($uid);
}
到这里UcService.class.php文件的登录方法已经写好,接下来打开前台W3note项目的控制器MemberAction.class.php文件写一个同步登录的方法,看代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public function checkLogin() {
if(!$_POST['username']) $this->error('帐号错误!');
if(!$_POST['password']) $this->error('密码错误!');
if(empty($_POST['verify'])) $this->error('验证码必须!');
import("@.ORG.UcService");//导入UcService.class.php类
$ucService = new UcService;
$uidarray = $ucService->uc_login($_POST['username'], $_POST['password']);
//dump($uidarray);
$loginurl=$ucService->uc_synlogin($uidarray);
echo $loginurl;//输出同步登录代码,否则无法同步登录
if(!is_string($uidarray)){
//生成认证条件
$map = array();
// 支持使用绑定帐号登录
$map['username'] = $_POST['username'];
$map["status"] = array('gt',0);
if($_SESSION['verify'] != md5($_POST['verify'])) {
$this->error('验证码错误!');
}
$memberinfo=$this->Member->where($map)->find();
if(false === $memberinfo) {
$this->error('帐号不存在或已禁用!');
}elseif($memberinfo['status']==0){
$this->error('帐号已禁用!');
}else {
$password = pwdHash($_POST['password']);
if($memberinfo['password'] != $password) {
$this->error('密码错误!');
}
session(C('USER_AUTH_KEY'), $memberinfo['id']);
session('email', $memberinfo['email'] );
session('loginUserName', $memberinfo['loginUserName']);
session('lastLoginTime', $memberinfo['lastLoginTime']);
session('loginnum', $memberinfo['loginnum']);
session('lastloginip', $memberinfo['lastloginip']);
//保存登录信息(相当于更新信息)
$data = array();
$data['id'] = $memberinfo['id'];
$data['lastlogintime'] = time();
$data['loginnum'] = array('exp','loginnum+1');
$data['lastloginip'] = get_client_ip();
//$data['verify'] = $authInfo['verify'];
$this->Member->save($data);
$this->success('登录成功!',U('Member/index'));
}
}
}
我们来看一下checkLogin()方法的执行过程。在项目W3note注册的一个帐号,然后在项目W3note提交登录表单后,首先执行UCenter的登录,前面我们写了两个UCenter的登录方法,在调用之前需要使用“import("@.ORG.UcService");”把UcService.class.php文件加载进来,实例化后得到$ucService,然后就可以使用用$ucService访问UCenterr的登录方法uc_login,返回一个$uidarray值,$uidarray包函什么数据?使用“dump($uidarray);”打印出来,以便下一步的操作,打印结果如下:
1
2
3
4
5
6
array(4) {
["uid"] => string(1) "1"
["username"] => string(5) "qqabc"
["password"] => string(6) "123456"
["email"] => string(9) "qq@qq.com"
}
下一步就是以此$uidarray作为参数传给同步登录方法uc_synlogin($uidarray),最后echo 一下uc_synlogin($uidarray)的返回值$loginurl,就可以实现帐号"qqabc"在UCenter登录了。帐号"qqabc"在UCenter登录成功后程序将继续往下执行项目W3note的登录,这里就不多说了。最后的结果是,帐号"qqabc"实现了在UCenter和项目W3note的同步登录!
本文转自 3147972 51CTO博客,原文链接:http://blog.51cto.com/a3147972/1229781,如需转载请自行联系原作者