1.验证码生成使用verify
$verify = new 命名空间Verify();
$verify -> entry();
简单生成验证码
shop/Admin/Controller/ManagerController.class.php
程序代码如下:
//制作专门方法实现验证码生成
function verifyImg(){
//以下类Verify在之前并没有include引入,而是使用自动加载
//ThinkPHP/Library/Think/Think.class.php中的autoload()方法
$verify = new \Think\Verify();
//ThinkPHP/Library/Think/Verify.class.php命名空间为Think
//完全限定名称实例化
//实例化,走的是Verify里的构造方法__construct()
//show_bug($verify);
$verify -> entry();
}
运行http://localhost/ThinkPHP/shop/index.php/Admin/Manager/verifyImg

验证码相关类解析
ThinkPHP/Library/Think/Think.class.php中的autoload()方法
程序代码如下:
/**
* 类库自动加载
* @param string $class 对象类名
* @return void
*/
public static function autoload($class) {
//1、show_bug($class);
//将输出以下信息:
//string(13) "Think\Storage"
//string(25) "Think\Storage\Driver\File"
//string(9) "Think\Log"
//string(30) "Behavior\ReadHtmlCacheBehavior"
//string(34) "Admin\Controller\ManagerController"
//string(12) "Think\Verify"
//string(30) "Behavior\ShowPageTraceBehavior"
//string(21) "Think\Log\Driver\File"
//即我们实例化验证码时$class=Think\Verify
// 检查是否存在映射
if(isset(self::$_map[$class])) {
include self::$_map[$class];
}elseif(false !== strpos($class,'\\')){
//2、
//strstr(Think\Verify, '\\'); 将返回Verify
//strstr(Think\Verify, '\\', true); 将返回Think
//上面中'\\'的第一个斜杠是转义符,所以生效的只有一个斜杠
//此时$name=Think
$name = strstr($class, '\\', true);
if(in_array($name,array('Think','Org','Behavior','Com','Vendor')) || is_dir(LIB_PATH.$name)){
// Library目录下面的命名空间自动定位
// 3、
// LIB_PATH= ThinkPHP/Library
$path = LIB_PATH;
}else{
// 检测自定义命名空间 否则就以模块为命名空间
$namespace = C('AUTOLOAD_NAMESPACE');
$path = isset($namespace[$name])? dirname($namespace[$name]).'/' : APP_PATH;
}
//4、
//$filename = ThinkPHP/Library/Think/Verify.class.php
$filename = $path . str_replace('\\', '/', $class) . EXT;
if(is_file($filename)) {
// Win环境下面严格区分大小写
if (IS_WIN && false === strpos(str_replace('/', '\\', realpath($filename)), $class . EXT)){
return ;
}
include $filename;
}
}elseif (!C('APP_USE_NAMESPACE')) {
// 自动加载的类库层
foreach(explode(',',C('APP_AUTOLOAD_LAYER')) as $layer){
if(substr($class,-strlen($layer))==$layer){
if(require_cache(MODULE_PATH.$layer.'/'.$class.EXT)) {
return ;
}
}
}
// 根据自动加载路径设置进行尝试搜索
foreach (explode(',',C('APP_AUTOLOAD_PATH')) as $path){
if(import($path.'.'.$class))
// 如果加载类成功则返回
return ;
}
}
}
ThinkPHP/Library/Think/Verify.class.php的构造方法__construct()
程序代码如下:
/**
* 架构方法 设置参数
* @access public
* @param array $config 配置参数
*/
//当前类中定义的$config作为参数传入
public function __construct($config=array()){
//与类中当前的属性config进行合并,最后传入$this->config
$this->config = array_merge($this->config, $config);
}
当前类有config属性如下:
protected $config = array(
'seKey' => 'ThinkPHP.CN', // 验证码加密密钥
'codeSet' => '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY', // 验证码字符集合
'expire' => 1800, // 验证码过期时间(s)
'useZh' => false, // 使用中文验证码
'zhSet' => '们...休借', // 中文验证码字符串
'useImgBg' => false, // 使用背景图片
'fontSize' => 25, // 验证码字体大小(px)
'useCurve' => true, // 是否画混淆曲线
'useNoise' => true, // 是否添加杂点
'imageH' => 0, // 验证码图片高度
'imageW' => 0, // 验证码图片宽度
'length' => 5, // 验证码位数
'fontttf' => '', // 验证码字体,不设置随机获取
'bg' => array(243, 251, 254), // 背景颜色
'reset' => true, // 验证成功后是否重置
);
验证码个性化配置
shop/Admin/Controller/ManagerController.class.php
程序代码如下:
//制作专门方法实现验证码生成
function verifyImg(){
$config = array(
'fontSize' => 25, // 验证码字体大小(px)
'imageH' => 50, // 验证码图片高度
'imageW' => 250, // 验证码图片宽度
//ThinkPHP/Library/Think/Verify/ttfs
'fontttf' => '4.ttf', // 验证码字体,不设置随机获取
'length' => 5, // 验证码位数
);
//需要传入参数
$verify = new \Think\Verify($config);
$verify -> entry();
}
验证码在模板中使用
<img src="路由地址Manager/verifyImg" alt="">
shop/Admin/View/Manager/login.html
程序代码如下:
<div class="col-sm-6">
<img src="{$smarty.const.__CONTROLLER__}/verifyImg" class="verifyimg reloadverify"/>
</div>
运行http://localhost/ThinkPHP/shop/index.php/Admin/Manager/login

运行效果
2.验证码有效性验证
本身验证码信息存储在session中,session存储的验证按是加密后的数据,Verity类提供了一个方法进行验证码的有效性校验。
$verify -> check(用户输入验证码); 返回真,表示验证码是正确的
shop/Admin/View/Manager/login.html
程序代码如下:
<form class="form-horizontal" action="{$smarty.const.__SELF__}" role="form" method="post">
...
<input type="text" class="form-control" id="verify" name="verify" placeholder="验证码" required />
...
</form>
shop/Admin/Controller/ManagerController.class.php
程序代码如下:
public function login(){
if (!empty($_POST)) {
//可以输出当前返回的信息
//print_r($_POST);
//验证码校验
$verify = new \Think\Verify();
if (!$verify->check($_POST['verify'])) {
echo "<script>alert('验证码错误')</script>";
} else {
echo "<script>alert('验证码正确')</script>";
}
}
$this->display();
}
运行http://localhost/ThinkPHP/shop/index.php/Admin/Manager/login

运行效果
3.登录功能用户名密码判断、获取session信息
判断用户名、密码是否是正确的,如果是正确的,再把信息存入session中表示登录状态。
传统方法:select * from sw_manager where name=$name and pwd=$pwd;
同时查询用户名和密码的信息记录,如果存在就是正确的,否则用户名或密码错误。
该方式不安全,会增加SQL语句注入的风险。
可以使用:select * from sw_manager where name=$name;
首先,查询指定名字的记录信息
其次,如果记录存在,再把记录中的密码拿出来与用户输入的密码进行比较
如果比较一致,就说明用户名和密码正确。
session(name,value,有效时间); 设置session
session([name]); 获取session
session(name,null); 删除指定session
session(null); 清空全部session
新建shop/Model/ManagerModel.class.php
程序代码如下:
//制作一个方法对用户名和密码进行校验
function checkNamePwd($name,$pwd){
//1、根据$name查询是否有此记录
//select * from sw_manager where mg_name=$name;
//select() find()
//2、根据指定字段进行查询getByXxx(),getByMg_name($name)
//getBymg_pwd() 父类Model利用__call()封装的方法
//getByXxx()函数返回一维数组信息
$info = $this ->getByMg_name($name);
//$info = null; 说明用户名错误
//$info = 一维数组;说明用户名正确
//show_bug($info);
//$info不为null,就可以继续验证密码
if ($info != null) {
if ($info['mg_pwd' != $pwd]) {
return false;
} else {
//返回$info来设置session信息
return $info;
}
} else {
return false;
}
}
shop/Admin/Controller/ManagerController.class.php
程序代码如下:
public function login(){
if (!empty($_POST)) {
$verify = new \Think\Verify();
if (!$verify->check($_POST['verify'])) {
echo "<script>alert('验证码错误')</script>";
} else {
//判断用户名和密码
//在Model模型中制作一个专门方法进行验证
$user = new \Model\ManagerModel();
$rst=$user->checkNamePwd($_POST['username'],$_POST['password']);
if ($rst === false) {
echo "用户名或密码错误";
} else {
print_r($rst['mg_name']) ;
//登录信息持久化$_SESSION
session('username',$rst['mg_name']);
session('id',$rst['mg_id']);
//页面跳转到后台首页
//$this -> redirect($url, $params, $delay, $msg);
//$this -> redirect('Index/index', array('id'=>100,'name'=>'Tom'), 2, '即将跳转');
$this -> redirect('Index/index');
}
}
}
$this->display();
}
//退出系统
function logout(){
session(null);
$this -> redirect('Manager/login');
}
shop/Admin/View/Index/head.html
程序代码如下:
<td style="font-weight: bold; color: #fff; padding-top: 20px"
align=middle>当前用户:{$smarty.session.username}
<a style="color: #fff" href="" target=main>修改口令</a>
<a style="color: #fff" onclick="if (confirm('确定要退出吗?')) return true; else return false;" href="{$smarty.const.__MODULE__}/Manager/logout" target=_top>退出系统</a>
</td>
运行http://localhost/ThinkPHP/shop/index.php/Admin/Manager/login.html

运行效果

运行效果
4.分页效果及自定义类文件引入框架
1、TP框架爱使用自定义功能类
2、具体分页类实现
新建自定义类文件夹并将该类放入
shop/Tool/Page.class.php
程序代码如下:
<?php
namespace Tool;
class Page {
private $total; //数据表中总记录数
private $listRows; //每页显示行数
private $limit;
private $uri;
private $pageNum; //页数
private $config=array('header'=>"个记录", "prev"=>"上一页", "next"=>"下一页", "first"=>"首 页", "last"=>"尾 页");
private $listNum=8;
/*
* $total
* $listRows
*/
public function __construct($total, $listRows=10, $pa=""){
$this->total=$total;
$this->listRows=$listRows;
$this->uri=$this->getUri($pa);
$this->page=!empty($_GET["page"]) ? $_GET["page"] : 1;
$this->pageNum=ceil($this->total/$this->listRows);
$this->limit=$this->setLimit();
}
private function setLimit(){
return "Limit ".($this->page-1)*$this->listRows.", {$this->listRows}";
}
private function getUri($pa){
$url=$_SERVER["REQUEST_URI"].(strpos($_SERVER["REQUEST_URI"], '?')?'':"?").$pa;
$parse=parse_url($url);
if(isset($parse["query"])){
parse_str($parse['query'],$params);
unset($params["page"]);
$url=$parse['path'].'?'.http_build_query($params);
}
return $url;
}
function __get($args){
if($args=="limit")
return $this->limit;
else
return null;
}
private function start(){
if($this->total==0)
return 0;
else
return ($this->page-1)*$this->listRows+1;
}
private function end(){
return min($this->page*$this->listRows,$this->total);
}
private function first(){
$html = "";
if($this->page==1)
$html.='';
else
$html.=" <a href='{$this->uri}&page=1'>{$this->config["first"]}</a> ";
return $html;
}
private function prev(){
$html = "";
if($this->page==1)
$html.='';
else
$html.=" <a href='{$this->uri}&page=".($this->page-1)."'>{$this->config["prev"]}</a> ";
return $html;
}
private function pageList(){
$linkPage="";
$inum=floor($this->listNum/2);
for($i=$inum; $i>=1; $i--){
$page=$this->page-$i;
if($page<1)
continue;
$linkPage.=" <a href='{$this->uri}&page={$page}'>{$page}</a> ";
}
$linkPage.=" {$this->page} ";
for($i=1; $i<=$inum; $i++){
$page=$this->page+$i;
if($page<=$this->pageNum)
$linkPage.=" <a href='{$this->uri}&page={$page}'>{$page}</a> ";
else
break;
}
return $linkPage;
}
private function next(){
$html = "";
if($this->page==$this->pageNum)
$html.='';
else
$html.=" <a href='{$this->uri}&page=".($this->page+1)."'>{$this->config["next"]}</a> ";
return $html;
}
private function last(){
$html = "";
if($this->page==$this->pageNum)
$html.='';
else
$html.=" <a href='{$this->uri}&page=".($this->pageNum)."'>{$this->config["last"]}</a> ";
return $html;
}
private function goPage(){
return ' <input type="text" onkeydown="javascript:if(event.keyCode==13){var page=(this.value>'.$this->pageNum.')?'.$this->pageNum.':this.value;location=\''.$this->uri.'&page=\'+page+\'\'}" value="'.$this->page.'" style="width:25px"><input type="button" value="GO" onclick="javascript:var page=(this.previousSibling.value>'.$this->pageNum.')?'.$this->pageNum.':this.previousSibling.value;location=\''.$this->uri.'&page=\'+page+\'\'"> ';
}
function fpage($display=array(0,1,2,3,4,5,6,7,8)){
$html[0]=" 共有<b>{$this->total}</b>{$this->config["header"]} ";
$html[1]=" 每页显示<b>".($this->end()-$this->start()+1)."</b>条,本页<b>{$this->start()}-{$this->end()}</b>条 ";
$html[2]=" <b>{$this->page}/{$this->pageNum}</b>页 ";
$html[3]=$this->first();
$html[4]=$this->prev();
$html[5]=$this->pageList();
$html[6]=$this->next();
$html[7]=$this->last();
$html[8]=$this->goPage();
$fpage='';
foreach($display as $index){
$fpage.=$html[$index];
}
return $fpage;
}
}
shop/Admin/Controller/GoodsController.class.php
程序代码如下:
//商品列表展示
public function showlist(){
$goods = D("Goods");
//1、获取当前记录总条数
$total = $goods -> count();
$per = 3;
//2、实例化分页类对象
$page = new \Tool\Page($total,$per);
//3、拼装SQL语句获得每页信息
$sql = "select * from sw_goods ".$page->limit;
$info = $goods -> query($sql);
//4、获得页码列表
$pagelist = $page ->fpage();
$this -> assign('info',$info);
$this->assign('page',$pagelist);
$this -> display();
}
shop/Admin/View/Goods/showlist.html
程序代码如下:
<tr>
<td colspan="20" style="text-align: center;" >{$page}</td>
</tr>
5.缓存应用
数据缓存介质:file文件、memory内存、数据库。
可以把一些不经常使用的MySQL数据临时放入缓存中,每次用户获得信息就从缓存中获得,大大提升MySQL的性能,减少服务器发的开销。
Smarty中也有缓存,是页面静态化的缓存——页面缓存
文件缓存
fopen() fwrite() fread()
memory内存缓存
安装内存缓存软件。启动软件服务,set() get()方法对内存数据进行读入读出操作
数据库缓存
建立临时数据表。启动MySQL数据,insert() select() update()
不同介质的缓存操作的手段也不同。TP框架已经把各种缓存设置好了,我们可以通过统一的行为操作不同类型的的缓存。
S(n**ame,value,有效期); 设置缓存
S(name); 读取缓存变量信息
S(name,null); 删除指定的缓存变量
TP默认缓存为文件缓存
ThinkPHP/Conf/convention.php
/* 数据缓存设置 */
'DATA_CACHE_TIME' => 0, // 数据缓存有效期 0表示永久缓存
'DATA_CACHE_COMPRESS' => false, // 数据缓存是否压缩缓存
'DATA_CACHE_CHECK' => false, // 数据缓存是否校验缓存
'DATA_CACHE_PREFIX' => '', // 缓存前缀
'DATA_CACHE_TYPE' => 'File', // 数据缓存类型,支持:File|Db|Apc|Memcache|Shmop|Sqlite|Xcache|Apachenote|Eaccelerator
'DATA_CACHE_PATH' => TEMP_PATH,// 缓存路径设置 (仅对File方式缓存有效)
'DATA_CACHE_KEY' => '', // 缓存文件KEY (仅对File方式缓存有效)
'DATA_CACHE_SUBDIR' => false, // 使用子目录缓存 (自动根据缓存标识的哈希创建子目录)
'DATA_PATH_LEVEL' => 1, // 子目录缓存级别
shop/Admin/Controller/GoodsController.class.php
程序代码如下:
//设置缓存
function s1(){
//name在10秒后失效
S('name','Tom',10);
S('age',25);
S('hobby',array('篮球','排球','乒乓球'));
echo "设置文件缓存成功";
}
//读取缓存
function s2(){
echo S('name'),"<br/>";
echo S('age'),"<br/>";
print_r(S('hobby'));
echo "<br/>";
}
//删除缓存
function s3(){
S('name',null);
S('age',null);
echo "删除文件缓存成功";
}
缓存信息存储在shop/Runtime/Temp
缓存中有数据直接返回使用
否则去数据库查询信息,再缓存,再供使用
下次就会到缓存中获得指定的信息,过了有效期重新到数据库获取数据
shop/Admin/Controller/GoodsController.class.php
程序代码如下:
function y1(){
//外部用户访问的方法
show_bug($this -> y2());
}
function y2(){
//被其他方法调用的方法,获得指定信息
//第一次从数据库中获得,后续在有效期从缓存中获得
$info = S('goods_info');
if ($info) {
return $info;
} else {
//没有缓存数据,就从数据库中获得数据,再把数据缓存起来
//模仿连接数据库
$dt = "这是加了时间戳的缓存".time();
S('goods_info',$dt,10);
return dt;
}
}
6.图片附件上传
前台上传:<form enctype="multipart/form-data">
后台接收:$_FILES
调用函数:move_uploaded_file(临时路径名,真实路径名);
TP系统类:ThinkPHP/Library/Think/Upload.class.php
该类设置的上传驱动对应的是常量'FILE_UPLOAD_TYPE' => 'Local', // 文件上传方式
即调用系统类:ThinkPHP/Library/Think/Upload/Driver/Local.class.php
该类是实现文件上传的具体操作
ThinkPHP/Library/Think/Upload.class.php
程序代码如下:
private $config = array(
'mimes' => array(), //允许上传的文件MiMe类型
'maxSize' => 0, //上传的文件大小限制 (0-不做限制)
'exts' => array(), //允许上传的文件后缀
'autoSub' => true, //自动子目录保存文件
'subName' => array('date', 'Y-m-d'), //子目录创建方式,[0]-函数名,[1]-参数,多个参数使用数组
'rootPath' => './Uploads/', //保存根路径
'savePath' => '', //保存路径
'saveName' => array('uniqid', ''), //上传文件命名规则,[0]-函数名,[1]-参数,多个参数使用数组
'saveExt' => '', //文件保存后缀,空则使用原后缀
'replace' => false, //存在同名是否覆盖
'hash' => true, //是否生成hash编码
'callback' => false, //检测文件是否存在回调,如果存在返回文件信息数组
'driver' => '', // 文件上传驱动
'driverConfig' => array(), // 上传驱动配置
);
shop/Admin/Controller/GoodsController.class.php
程序代码如下:
//添加商品
public function add(){
$goods = D("Goods");
if (!empty($_POST)) {
//判断附件是否上传
//如果有则实例化Upload,把附件上传到服务器指定位置
//然后把附件的路径名得到,存入$_POST
if (!empty($_FILES)) {
//print_r($_FILES);
$upload = new \Think\Upload();
//show_bug($upload);
//uploadOne会返回已经上传的附件信息
$z = $upload -> uploadOne($_FILES['goods_img']);
$config = array(
'rootPath' => '', //保存根路径
'savePath' => 'Uploads/', //保存路径
);
//附件上传到路径:根目录/保存目录路径/创建日期目录
if (!$z) {
show_bug($upload -> getError());
} else {
//show_bug($z);
//拼装图片的路径名
$bigimg = $z['savepath'].$z['savename'];
$_POST['goods_big_img'] = $bigimg;
}
}
$goods -> create();
$z = $goods -> add($ar);
if ($z) {
echo "success";
} else {
echo "failure";
}
} else {
}
$this->display();
}
建立上传目录ThinkPHP\shop\Uploads
定义上传路径常量
shop/index.php
程序代码如下:
//为上传图片设置路径
define("IMG_UPLOAD",SITE_URL."../ThinkPHP/shop/Uploads/");
shop/Admin/View/Goods/add.html
程序代码如下:
<form action="{$smarty.const.__SELF__}" method="post" enctype="multipart/form-data">
...
<tr>
<td>商品图片</td>
<td><input type="file" name="goods_img" /></td>
</tr>
...
</form>
shop/Admin/View/Goods/showlist.html
程序代码如下:
<img src="{$smarty.const.IMG_UPLOAD}{$v.goods_big_img}" height="60" width="60"/>
7.给已上传图片生成缩略图
原理:利用已有图片创建一个画板
把画板给缩小一点imagecopyresampled
imagepng($img,路径名); 保存到系统一个指定目录
ThinkPHP/Library/Think/Image.class.php
open() 打开一个图像资源
thumb() 制作缩略图,设置图像的宽度、高度、达到制作缩略图效果
save() 保存图像
TP系统类:ThinkPHP/Library/Think/Image.class.php
shop/Admin/Controller/GoodsController.class.php
程序代码如下:
//添加商品
public function add(){
$goods = D("Goods");
if (!empty($_POST)) {
//判断附件是否上传
//如果有则实例化Upload,把附件上传到服务器指定位置
//然后把附件的路径名得到,存入$_POST
if (!empty($_FILES)) {
//print_r($_FILES);
$upload = new \Think\Upload();
//show_bug($upload);
//uploadOne会返回已经上传的附件信息
$z = $upload -> uploadOne($_FILES['goods_img']);
$config = array(
'rootPath' => '', //保存根路径
'savePath' => 'Uploads/', //保存路径
);
//附件上传到路径:根目录/保存目录路径/创建日期目录
if (!$z) {
show_bug($upload -> getError());
} else {
//show_bug($z);
//拼装图片的路径名
$bigimg = $z['savepath'].$z['savename'];
$_POST['goods_big_img'] = $bigimg;
//把已经上传好的图片制作缩略图Image.class.php
$image = new \Think\Image();
//open();打开图像资源,通过路径名找到图像
//$upload -> rootPath 直接调用
$srcimg = $upload -> rootPath.$bigimg;
$image -> open($srcimg);
$image -> thumb(150,150);//按照比例缩小
$smallimg = $z['savepath']."small_".$z['savename'];
$image ->save($upload->rootPath.$smallimg);
$_POST['goods_small_img'] = $smallimg;
}
}
$goods -> create();
$z = $goods -> add($ar);
if ($z) {
echo "success";
} else {
echo "failure";
}
} else {
}
$this->display();
}
shop/Admin/View/Goods/showlist.html
程序代码如下:
<img src="{$smarty.const.IMG_UPLOAD}{$v.goods_small_img}" height="40" width="40"/>
8.面向切面编程Behavior分析
面向切面是一种设置模式
把一个大块的功能分解成小块的功能
这样小块的功能利于开发、维护、升级、部署
1、框架执行流程分析:index.php、ThinkPHP.php、Think.class.php(主要体现)、App.class.php
//TP框架的面向切面编程设计体现在Behavior行为中
2、Think::start();
//index.php包含ThinkPHP.php,ThinkPHP.php应用初始化Think::start();
3、ThinkPHP/Conf/Mode/common.php中的tags标签内容就是行为的内容
//Think.class.php的start()方法读取、加载应用模式(ThinkPHP/Conf/Mode/common.php)并进行相关操作
4、自定义shop/Common/conf/tags.php文件
//自定义tags.php内容会覆盖指定的common.php的配置内容
Think.class.php调用App.class.php
1、function run();
//Think.class.php的start()方法中运行应用App::run();
2、Hook::listen('app_begin');
//App.class.php中run()方法,调用Hook.class.php的listen()方法
3、tags标签的内容解析出来:CheckLang
//Hook.class.php的listen()处理传入的tags标签内容
4、self::exec('CheckLang');
//listen()方法内调用当前类的exec()方法实例化对象new CheckLangBehavior并处理CheckLangBehavior
5、obj -> run(); 运行具体行为