thinkPHP
来源:互联网 发布:先科s608数据升级网址 编辑:程序博客网 时间:2024/06/03 03:15
一、 Introduction
Php框架是很多代码的集合。这些代码是程序结构的代码而不是业务代码。代码中有许多函数、类、功能类包。框架的代码按照一定标准组成一个有机的功能体,这个功能体里边有许多设计模式如mvc、单例、ar等。
使用框架的好处,快速、稳定、高效的搭建程序系统;维护性、灵活性、适应客户需求得到最大化加强;注意力集中在业务层面;节省代码工作量。
常见的框架
Zendframeword,php语言公司发布的,功能丰富,重量级框架。
Yii,自己人的框架,重量级框架,纯oop框架,将代码的重用性发挥到了极致,速度非常快。
Cakephp,运用诸如ActiveRecord、AssociationDataMapping、FrontController和MVC等著名设计模式的快速开发框架。
Symfony,一个基于MVC模式的面向对象的php5框架。
thinkPHP,轻量级框架。内如oop和面向过程代码都存在,自己人的框架。早起的思想来源于struts,使用面向对象的开发结构和MVC模式,融合了struts的action和dao思想和jsp的tablig,ror的orm映射和activerecord模式,封装了crud和一些常用操作,单一入口模式。
Thinkphp的目录结构
到官网下载,然后解压。其中,Application目录,存放开发的项目;public,存放静态资源文件。thinkPHP,存放框架资源文件。其中,commem,存放函数库;conf,放配置文件;lang,语言包;library,资源文件目录。ThinkPHP.php,框架接口文件,每个应用都要引入。在library目录中,behavior,行为文件目录;think,框架功能类目录;vendor,第三方完整功能目录包,比如smarty。在think目录中,App.class.php、Think.class.php是外界使用需要的文件。
二、 使用入门
1. 自动搭建项目系统架构
将thinkphp目录拷贝到工作站目录下,然后创建三个项目目录,比如shop、book、car等。
在shop目录下创建一个接入文件,index.php,内容为:
<?php
//利用thinkphp开发shop商城系统
//入口程序文件
//引入接口文件,ThinkPHP/ThinkPHP.php
include ("../ThinkPHP/ThinkPHP.php");
然后,运行这个接口目录,就会在shop目录中,自动生成项目系统的目录架构。
2. 路由形式
路由是,系统从RUL参数中分析当前请求的分组、控制器和操作的过程。
为了是访问路由看起来好看,更具有隐蔽性,thinkphp提供了4种书写路由的形式。
基本的get形式http://网址/index.php?m=分组&c=控制器&a=操作方法
Pathinfo路径形式默认的方式http://网址/index.php/分组/控制器/操作方法
Rewrite重写形式,使用伪静态技术省略index.php入口文件http://网址/分组/控制器/操作方法
兼容形式http://网址/index.php?s=/分组/控制器/操作方法
3. 创建控制器
<?php
/*
* 创建前台分组的会员控制器
*/
namespace Home\Controller;
use Think\Controller;
//父类Controller:ThinkPHP/LIbrary/Think/Controller.class.php
class UserController extends Controller {
//登陆系统
public function login(){
echo"loginning";
}
}
4. 创建视图模版
为了便于开发,可以设置错误模板模式。在index.php入口文件设置define(“APP_DEBUG”, true);//生产模式define(“APP_DEBUG”, false)//生产模式。
在文件目录中,创建好模板。在控制器中,调用模板:
public function login(){
//调用视图
//display(),是父类controller的方法
//$this->display();//没有传参,视图模版名称与当前操作方法名称一致
// $this->display("register");//调用当前User视图模板下的其他的模板文件
$this->display("Goods/showlist");//调用其他控制器下的模板文件
}
5. 将已有的模板与tp框架相结合
在home目录下创建Public目录,在Public目录下创建css目录、js目录、img目录。将已有模板的对应文件copy到对应目录下。然后在视图中引入css等文件。
在模板中引入css文件,最好不要使用相对路径,会受到路由的影响。请使用绝对路径。如果是外部css文件中的图片,请使用相对路径引入,是相对于css文件本身的。为了维护的方便,可以在入口文件中设置css、图片文件、js的引用地址为常量。比如:
//设置css、图片文件、js的引用地址常量
define("CSS_URL","/tp/shop/Home/Public/css/");
define("IMG_URL","/tp/shop/Home/Public/img/");
define("JS_URL","/tp/shop/Home/Public/js/");
6. 后台页面搭建
关于分组设置,同一个项目中,由于业务规则的划分,有多个相关的功能模板,它们都有独立的控制器、view视图、配置文件、函数库文件等,为了开发维护方便,就给它们创建独立的分组,每个分组有自己的一套。
在shop目录下,创建一个Admin目录。在Admin目录下创建Controller目录、View目录。
创建后台控制器,ManagerController.class.php。类似前端的方式,在后台,结合tp框架和模板文件。
比如,首页控制器:
<?php
/*
* 后台首页控制器
*/
namespace Admin\Controller;
use Think\Controller;
class IndexController extends Controller {
//头部
public function head(){
$this->display();
}
//左侧
public function left(){
$this->display();
}
//右侧
public function right(){
$this->display();
}
//集成页面
public function index(){
$this->display();
}
}
在页面中,如果使用了frame,其中有src引入文件,不要使用相对路径,要使用绝对路径,以免受路由影响。Tp框架提供了一些常量,以减少代码工作量,其中部分如下:
__MODULE__:路由地址分组信息(/shop/index.php/分组)
__CONTROLLER__:路由地址控制器信息(/shop/index.php/分组/控制器)
__ACTION__:路由地址操作方法信息(/shop/index.php/分组/控制器/操作方法)
__SELF__:路由地址的全部信息(/tp/shop/index.php/分组/控制器/操作方法/名称/值/名称/值)
MODULE_NAME:分组名称
CONTROLLER_NAME:控制器名称
ACTION_NAME:操作方法名称
比如:
<frame name=left src=”__CONTROLLER__/left.html” frameborder=0 noresize />
7. 相关细节
关于配置文件,有ThinkPHP/Conf/conversion.php,系统主要配置文件;shop/Common/Conf/Config.php,当前shop项目的配置文件;shop/Home/Conf/config.php,当前shop项目的Home分组的配置文件。如果存在同名的配置变量,后者会覆盖前者。系统里面并不是全部变量都是在conversion.php里面定义,此外,在Behavior行为文件里边有定义一部分,在框架的代码角落里边有零星的一点。
关于跟踪信息,需要在配置文件里面定义配置变量,SHOW_PAGE_TRACE,C()函数可以读取或设置配置变量,如C(name, value)设置,C(name)读取。在自己项目里面设置跟踪信息,比如在shop的common/conf/config.php中,
<?php
return array(
//'配置项'=>'配置值'
//使页面底部显示跟踪信息
'SHOW_PAGE_TRACE' => true,
);
可以在控制器中,设置哪里需要显示跟踪,哪里不需要显示跟踪,比如:
public function head(){
//设置配置信息,使得头部不要显示跟踪信息
C('SHOW_PAGE_TRACE',false);
$this->display();
}
关于默认分组设置,如果不设置分组,浏览器会默认index.php紧跟着的为分组。因为tp框架系统没有比较,要给其设置一个比较参数。复制conversion.php中的DEFUALT_MODULE到shop的配置文件下。比如:
<?php
return array(
//'配置项'=>'配置值'
//使页面底部显示跟踪信息
'SHOW_PAGE_TRACE' => true,
//设置默认分组
'DEFAULT_MODULE' =>'Home',//系统默认
//允许访问的分组信息
'MODULE_ALLOW_LIST' =>array('Home', 'Admin'),
);
8. 关于框架的两种模式
即开发设计模式和生产模式。
开发模式,每次请求会加载每个应用程序文件,比较耗费资源,错误提示友好,会自动清除common-runtime.php文件,会依次加载每个需要的文件。
生产模式,该模式比较节省资源,会把请求过程中的一些通用程序文件给编译到一个文件里边,错误信息模糊,不方便调试。
三、 开启smarty模版引擎
在conversion.php文件中,可以设置模板引擎。将'TMPL_ENGINE_TYPE' => 'Think', // 默认模板引擎 以下设置仅对使用Think模板引擎有效拷贝到shop项目的配置文件中。将值改为Smarty,比如:
<?php
return array(
//'配置项'=>'配置值'
//使页面底部显示跟踪信息
'SHOW_PAGE_TRACE' => true,
//设置默认分组
'DEFAULT_MODULE' =>'Home',//系统默认
//允许访问的分组信息
'MODULE_ALLOW_LIST' =>array('Home', 'Admin'),
//设置smarty模板引擎
'TMPL_ENGINE_TYPE' => 'Smarty', // 默认模板引擎
);
关于smarty模板{}标记有冲突的解决。
{}与css或js有冲突,在{}与内容中间设置空格;使得{}左右标记符换行;外部引入css、js;设置{literal}{/literal};变换smarty的标记符号。
可以通过配置TMPL_ENGINE_CONFIG配置变量为Smarty做相关配置。比如:
<?php
return array(
//'配置项'=>'配置值'
//使页面底部显示跟踪信息
'SHOW_PAGE_TRACE' => true,
//设置默认分组
'DEFAULT_MODULE' =>'Home',//系统默认
//允许访问的分组信息
'MODULE_ALLOW_LIST' =>array('Home', 'Admin'),
//设置smarty模板引擎
'TMPL_ENGINE_TYPE' => 'Smarty', // 默认模板引擎
//配置smarty相关变量
'TMPL_ENGINE_CONFIG' =>array(
'left_delimiter' =>'**',
'right_delimiter' => '##',
),
);
在模板页面中
**literal##内部内容不会被smarty解析**/literal##
四、 数据库操作Model模型
1. 数据库设置
在conversion.php中,有相关的设置变量。由于同一个框架下有多个项目,每个项目的数据库信息有所不同,所以拷贝到shop项目,进行设置。比如:
/* 数据库设置 */
'DB_TYPE' => 'mysql', // 数据库类型
'DB_HOST' => 'localhost', // 服务器地址
'DB_NAME' => 'shop', // 数据库名
'DB_USER' => 'root', // 用户名
'DB_PWD' => '123456', // 密码
'DB_PORT' => '3306', // 端口
'DB_PREFIX' => 'sw', // 数据库表前缀
'DB_PARAMS' => array(), // 数据库连接参数
'DB_DEBUG' => TRUE, // 数据库调试模式 开启后可以记录SQL日志
'DB_FIELDS_CACHE' => true, // 启用字段缓存
'DB_CHARSET' => 'utf8', // 数据库编码默认采用utf8
2. 创建Model类
<?php
/*
*goods模型
*/
namespace Model;
use Think\Model;
//父类Model:Think/Library/Think/Model.class.php
class GoodsModel extends Model {
}
然后实例化goodsmodel,需要体现命名空间
public function showlist(){
$goods = new\Model\GoodsModel();
dump($goods);
$this->display();
}
对于数据库表中没有前缀的表,或者特殊的表,在定义model时需要声明表的真名,比如:
<?php
/*
*特殊表的模型
*/
namespace Model;
use Think\Model;
//父类Model:Think/Library/Think/Model.class.php
class EnglishModel extends Model {
protected $trueTableName ="english";
}
3. 实例Model对象的两种方式
实例化普通model对象,使用$model = new \Model\xxxModel();
实例化基类Model对象,可以实现对数据库的基本操作。
$model = D();//实例化基类Model,没有关联任何表
$model = D(‘Goods’);//实例化父类Model对象,并操作xx_goods业务数据表。即使不创建具体model模型类文件,也可以对数据表的数据进行操作。
如果一个数据表没有特殊的方法要求,可以通过D(xxx)进行操作。
比如:
public function showlist(){
//实例化普通model对象
// $goods = new\Model\GoodsModel();
// dump($goods);
//实例化基类model对象
// $obj = D();//实例化父类model对象
$obj = D('User');
dump($obj);
$this->display();
}
五、 数据基本操作
1. 数据查询操作
调用方法,model对象->select();
具体:
$model->select();
$model->select(主键,id值);
$model->select(‘id1,id2,id3,…’);//查询主键信息在条件范围内的记录
使用select()方法会始终返回一个二维数组。
比如:
public function showlist(){
//实例化普通model对象
$goods = new\Model\GoodsModel();
$info =$goods->select();
$info =$goods->select(12);
$info =$goods->select("12,13,14");
// dump($info);
//以下两个方法被直接定义到父类controller里面,对smarty相关方法的封装
$this->assign('info',$info);
$this->display();
}
查询中的辅助方法
Where()条件$model->where(条件值);//条件值就是sql语句中where后边的结果值
Limit()限制条数$model->limit(数字);//严格查询数字条数的记录
Field()限制查询字段$model->field(字段1,字段2,字段3);
Order()排序$model->order(‘排序条件asc/desc’);
Group()分组查询groupby,$model->group(分组条件);
Having()条件设置方法。Having设置条件的效果与where使用效果类似。区别是where语句的字段,必须是数据表中存在的字段;having语句字段必须是查询结果集中存在的字段。
比如:
public function showlist(){
//实例化普通model对象
$goods = new\Model\GoodsModel();
// $info =$goods->select(12);
// $info =$goods->select("12,13,14");
// dump($info);
//设置查询条件
$goods ->where("goods_name like '%基%' and goods_price > 1500");
$info =$goods->select();
//limit限制条数
$goods -> limit(5);
$info =$goods->select();
$goods -> limit(21,9);
$info =$goods->select();
//field,限制查询字段
$goods ->field('goods_name,goods_price');
$info =$goods->select();
//排序
$goods ->order("goods_price desc");
$info =$goods->select();
//分组查询
$goods ->group("goods_brand_id");
$goods ->field("goods_brand_id,count(*)");
$info = $goods->select();
// dump($info);
//having
$goods ->having("goods_price > 1000");
$info =$goods->select();
//以下两个方法被直接定义到父类controller里面,对smarty相关方法的封装
$this->assign('info',$info);
$this->display();
}
以上具体方法使用的时候可以一并使用多个,形成连贯操作,没有顺序要求。比如:
public function showlist(){
//实例化普通model对象
$goods = new\Model\GoodsModel();
// $info =$goods->select(12);
// $info =$goods->select("12,13,14");
// dump($info);
// $info = $goods ->group('goods_brand_id') -> field("goods_brand_id,count(*)") ->select();
// dump($info);
$info = $goods ->where('goods_price > 1000') -> field("goods_name,goods_price")-> select();
$this->assign('info',$info);
$this->display();
}
2. 添加数据
调用方法,model对象->add()
具体有两种使用方式
数组方式$数组 = array(元素(键名=>值),元素(键名=>值),…);$model(普通对象)->(数组);
注意,数组的元素键名与数据表字段的名称必须一致。
AR(Active Record活跃记录)方式
$model->属性= 值;
$model->属性= 值;
$model->add();
AR规范要求,一个model模型类与一个具体的数据表对应;model模型的实例化对象与数据表的一条记录对应;model对象的属性与记录的字段对应。Tp框架的AR是仿真产品,因为每个业务model模型类里并不存在对应数据表的字段。
add()方法返回新纪录的主键id值。
比如:
//添加商品
public function tianjia(){
$goods = D('Goods');
//数组方式添加
// $arr = array(
// 'goods_name' =>'haha',
// 'goods_price' =>500,
// 'goods_weight'=> 12,
// 'goods_number'=>23,
// );
// $z =$goods->add($arr);
// dump($z);
//AR方式添加
$goods -> goods_name ="heihei";
$goods -> goods_price= 443;
$goods -> goods_number= 23;
$goods -> goods_weight= 13;
$z = $goods->add();
dump($z);
$this->display();
}
3. 数据修改操作
调用方法:model对象->save();
有两种方式
数组方式,$model->save(数组);
注意:数组的元素下标属性字段必须与数据表字段保持一致。
AR方式,$model->属性(字段)=值; $model->属性(字段)=值;$model->save();
save()方法返回为受影响的记录条数。
注意:数据修改必须设置条件,主键id或where()方法,二选一,否则失败。
public function upd(){
$goods = D('Goods');
// $goods -> goods_id =155;
$goods -> goods_name ="heheh";
$goods -> goods_price= 323;
$goods -> goods_number= 23;
$z = $goods ->where("goods_id > 160") -> save();
dump($z);
$this->display();
}
4. 后台实现商品添加
1) 制作添加表单
注意表单元素的属性名与数据表字段的对应。
2) 商品控制器,进行判断添加
public function tianjia(){
$goods = D('Goods');
//判断是展示还是收集信息
if (!empty($_POST)) {
// dump($_POST);
$z = $goods ->add($_POST);
if ($z) {
//页面跳转
//$this->redirect(分组/控制器/操作方法,参数array,间隔时间,提示信息);
// $this ->redirect("showlist",array(), 2, '数据添加成功');
//如果设置参数,可以get方法传递,比如
$this ->redirect("showlist",array('name' => 'hah','age' => 12), 2, '数据添加成功');
} else {
$this ->redirect("tianjia",array(), 2, '数据添加失败');
}
} else {
$this ->display();
}
}
3) 窗体顶端
5. 后台实现商品修改
首先get参数的传递,需要修改为路由形式,比如:<a href=”{$smarty.const.__CONTROLLER__}/upd/goods_id/{$v.goods_id}”>修改</a>
然后,控制操作方法接收get参数,可以不需要使用$_GET方法,而是通过方法的形参接收,这类似某些其他语言的框架的方法。
//三个形参的方式是给upd方法传递的形参
public functionupd($goods_id){
$goods = D('Goods');
//dump($goods_id);
// dump($height);
// dump($addr);
//find()方法获得数据表记录,每次通过一维数组返回一个记录结果
//$model对象->find();没有设置参数获得第一条结果
//$model对象->find(数字);获得主键为参数值的记录结果
// $info = D('Goods')-> select($goods_id);
// 两个业务逻辑,展示和收集
if (!empty($_POST)) {
$z = $goods->save($_POST);
if ($z) {
//页面跳转
//$this->redirect(分组/控制器/操作方法,参数array,间隔时间,提示信息);
// $this ->redirect("showlist",array(), 2, '数据添加成功');
//如果设置参数,可以get方法传递,比如
$this ->redirect("showlist",array(), 2, '数据修改成功');
} else {
$this ->redirect("upd",array('goods_id' => $goods_id), 2, '数据修改失败');
}
} else {
$info = D('Goods')-> find($goods_id);
$this ->assign('info', $info);
$this ->display();
}
}
六、 实现表单的自动验证
1. 实现前台用户注册
创建控制器及其操作方法
//注册
function register(){
$user = D('user');
//两个逻辑,显示和收集
if (!empty($_POST)) {
$_POST['user_hobby']= implode(',', $_POST['user_hobby']);
dump($_POST);
$z = $user ->add($_POST);
// dump($z);
} else {
$this->display();
}
}
2. 实现表单自动验证
重写父类的_validate成员,定义验证规则
<?php
/*
*user模型
*/
namespace Model;
use Think\Model;
//父类Model:Think/Library/Think/Model.class.php
class UserModel extends Model {
//是否批量获得全部验证信息
protected $patchValidate =true;
//通过定义$_validate成员,设置表单验证规则
protected $_validate = array(
// array(字段,规则,错误提示,验证条件,附加规则,验证时间);
array('username', 'require','用户名必须填写'),
array('username', '', '该用户名已经被使用', 0, 'unique'),
array('password','require', '密码必须填写'),
array('password2','require', '确认密码必须填写'),
array('password2','password', '两次密码必须一致', 0, 'confirm'),
array('user_email','email', '邮箱不能为空', 0),
array('user_email','email', '邮箱格式不正确', 2),
array('user_qq','number', 'qq必须是数字组成'),
array('user_qq', '5,12','qq必须是5到12位', 0, 'length'),
array('user_xueli','2,3,4,5', '学历必须选择一个', 0, 'in'),
array('user_hobby','check_hobby', '爱好必须选择两项以上', 1, 'callback'),
);
//定义一个方法进行爱好的验证
function check_hobby($arg){
if (count($arg) < 2) {
return false;
}
return true;
}
}
编写控制器的方法,调用create方法,进行验证
function register(){
$user = new\Model\UserModel();
//两个逻辑,显示和收集
if (!empty($_POST)) {
// $_POST['user_hobby'] = implode(',', $_POST['user_hobby']);
$info = $user ->create();//收集表单,过滤表单信息,非法字段过滤,表单自动验证
//通过create的返回值判断验证是否成功
if ($info) {
$info['user_hobby'] = implode(',', $info['user_hobby']);
$z = $user ->add($info);
if ($z) {
$this->redirect('Index/index');
}
} else {
$this ->assign('errorinfo',$user -> getError());
}
}
$this->display();
}
在注册模板中使用输出的验证提示信息,如:
<span style=”coloe:red;”>{$errorinfo.user_hobby|default:''}</span>
七、 命名空间
php5.3后引入的技术,java中的老技术,被php引用。
1. 命名空间的含义
在php程序中,语法规则要求同名称的函数、类名、常量在一个请求里边不允许出现多次。如果有的应用程序不得已必须出现多个同名的函数、类名、常量,可以把它们放到不同的空间里面做请求。这个不同的空间称为命名空间。
2. 命名空间的使用
通过namespace关键字可以声明命名空间。语法:namespace 空间名称;
命名空间针对函数、类名、const常量三部分起作用,并统称为元素。
常量的声明,define(名称,值);//在类外部声明常量与命名空间没有关系,同名常量只能声明一次
const 名称=值;//与命名空间有关系。
Const可以在类内部声明常量信息。Const也可以在类外部声明常量。
使用命名空间的时候const可以放到类外部声明常量。
同名称的多个常量,可以分别定义到不同的命名空间里。
比如:
<?php
namespace haha;
function getInfo(){
echo "hah";
}
const HOST = "localhost";
namespace haha2;
function getInfo(){
echo "heihei";
}
const HOST = "127.0.0.1";
任素在没有任何限制的时候,会访问当前空间的元素,当前空间按照就近原则算。比如:
<?php
namespace haha;
function getInfo(){
echo "hah";
}
const HOST = "localhost";
namespace haha2;
function getInfo(){
echo "heihei";
}
const HOST = "127.0.0.1";
getInfo();
echo HOST;
如果访问指定空间的元素,需要进行空间限制,\空间名\访问的信息,比如:
<?php
namespace haha;
function getInfo(){
echo "hah";
}
const HOST = "localhost";
namespace haha2;
function getInfo(){
echo "heihei";
}
const HOST = "127.0.0.1";
getInfo();
echo HOST;
echo "<hr />";
\haha\getInfo();
echo \haha\HOST;
3. 多级命名空间及三种方式访问元素
命名空间中存放的元素,有时候多了,为了便于管理,可以对元素进行分门别类的存储。也就是说命名空间可以设置为多级空间。多级空间的最后一级空间就称为子级空间。
三种访问元素的方式分别是,非限定名称方式;完全限定名称方式;限定名称方式。
比如:
<?php
//多级命名空间
namespace beijing\haha\heieh;
class Person {
public $name ="jim";
}
namespace tianin\hll\looe;
class Person {
public $name ="tom";
}
$obj = new Person();
echo $obj -> name;
echo "<hr />";
$obj1 = new \beijing\haha\heieh\Person();
echo $obj1 -> name;
以限定名称方式
<?php
namespace tianjin\hie\ah\hei;
const USER = "user3";
namespace beijing\haha\hei;
const USER = "user1";
namespace tianjin\hie\ah;
const USER = "user2";
//限定名称方式
//表示当前空间+本身空间\元素
echo hei\USER;
4. 引入机制
命名空间声明为多级空间,在其他空间内部访问的时候,需用通过完全限定名称方式,这个完全限定方式不便于开发、维护,为了降低复杂度,可以在当前空间把指定的空间给引入进来,进而可以方便地通过限定名称的方便的形式使用其他空间的元素。
1) 空间引入的例子
<?php
namespace bj\hd\sd;
const USER = "haha1";
namespace tj\bu\zhi;
const USER = "haha2";
function getInfo(){
echo "eeeee";
}
const NAME = "hehe";
namespace sz\nh\gm;
const USER = "haha3";
//项目需要频繁访问其他空间元素,如果使用完全限定,有麻烦且后续难维护
//引入其他空间,可以使用限定名称方式访问,限定名称就是引入空间的最后一级
use tj\bu\zhi;
echo zhi\NAME;
zhi\getInfo();
2) 元素引入
语法:use 空间\空间\空间\类元素;
空间引入可以解决完全限定名称访问元素的繁琐型,但是还需通过限定名称方式访问,如果引入空间的元素是类,就可以直接把这个类引入到当前空间,使用时就可以通过非限定名称方式访问。注意,只能做类元素引入,函数和常量不可以。
为了防止引入类与当前空间存在同名类导致冲突,可以在引入类元素时使用别名,语法:use 空间\元素 as 别名;
比如:
<?php
namespace bj\hd\sd;
class Person {
static $name ="user1";
}
namespace sz\nh\gm;
class Person {
static $name ="user2";
}
const USER = "haha3";
//bj\hd\sd\Person类元素直接引入
use bj\hd\sd\Person as P;
echo Person::$name;
echo "<hr />";
echo P::$name;
5. 公共空间
一个php文件里边没有namespace关键字声明,则该文件的元素都存在于公共空间,访问公共空间的元素统一设置为\元素。
比如:
<?php
namespace bj;
function f1(){
echo "haha";
}
const NAME = "hehe";
//引入文件空间对当前空间没有影响
include("demo7.php");//公共空间
f1();//当前空间是bj
echo "<br />";
echo \NAME;
echo "<br />";
echo NAME;
f2();
<?php
const NAME = "heih";
function f2(){
echo "xixi";
}
6. 使用命名空间的注意问题
声明命名空间的当前脚本的第一个namespace关键字前面不能有任何代码,header头也要下在下边;
命名空间是虚拟抽象的空间,不是真实存在的目录;
同一个请求的多个文件可以使用同名命名空间名称,只要这些文件里没有同名称、同类型的元素就可以。
7. 关于tp框架中的命名空间
比如在UserController类中
namespaceHome\Controller;//声明命名空间
useThink\Controller;//空间类元素引入
八、 实现验证码
1. 生成验证码
使用tp框架提供的验证码类
注意在类开始前引入验证码类
use Think\Verify;
//生成验证码
public function verifyImg(){
$cfg = array(
'imageH' => 45,
'imageW' => 100,
'length' => 4,
'fontSize' => 15,
'fontttf' =>'1.ttf',
);
//实例化verify类对象
$very = newVerify($cfg);//完全限定名称方式
$very -> entry();
}
2. 输出验证码
在登陆模板显示验证码
<img src=”{$smarty.const.__CONTROLLER__/verifyImg}” alt=”yzm” onclick=”this.src=’ {$smarty.const.__CONTROLLER__/verifyImg}’/Math.random()”/>
3. 校验验证码
public function login(){
//两个业务逻辑,展示表单,收集表单
if ($_POST) {
//验证码的校验
$vry = new\Think\Verify();
if ($vry ->check($_POST['captcha'])) {
echo "正确";
} else {
echo "错误";
}
}
$this->display();
}
九、 附件上传
1. 上传图片
使用tp框架下的Upload类。
编写模板页面中的上传商品信息的表单,注意form中添加enctype=”multipart/form-data”属性。
public function tianjia(){
$goods = D('Goods');
//判断是展示还是收集信息
if (!empty($_POST)) {
// dump($_POST);
//处理上传的商品图片
if($_FILES['goods_pic']['error'] == 0) {
$cfg = array(
'rootPath'=> 'uploads/',//保存根目录
);
$up = new\Think\Upload($cfg);
//uploadOne()会返回上传文件的存储在服务器的名字和路径等信息
$z = $up->uploadOne($_FILES['goods_pic']);
// dump($up ->getError());
//把上传的图片保存到数据表记录里面
$bigpathname =$up -> rootPath . $z['savepath'] . $z['savename'];
$_POST['goods_big_img'] = $bigpathname;
}
$info = $goods->create();
$z = $goods ->add($info);
if ($z) {
//页面跳转
//$this->redirect(分组/控制器/操作方法,参数array,间隔时间,提示信息);
// $this ->redirect("showlist",array(), 2, '数据添加成功');
//如果设置参数,可以get方法传递,比如
$this ->redirect("showlist",array('name' => 'hah','age' => 12), 2, '数据添加成功');
} else {
$this ->redirect("tianjia",array(), 2, '数据添加失败');
}
} else {
$this ->display();
}
}
2. 制作缩略图
把一个已有图片打开,裁出某个部分,经过放大、缩小,把处理好的部分放到另外一个图片里边,显示出来。
public functiontianjia(){
$goods = D('Goods');
//判断是展示还是收集信息
if (!empty($_POST)) {
// dump($_POST);
//处理上传的商品图片
if($_FILES['goods_pic']['error'] == 0) {
$cfg = array(
'rootPath'=> './uploads/',//保存根目录
);
$up = new\Think\Upload($cfg);
//uploadOne()会返回上传文件的存储在服务器的名字和路径等信息
$z = $up->uploadOne($_FILES['goods_pic']);
// dump($up ->getError());
//把上传的图片保存到数据表记录里面
$bigpathname =$up -> rootPath . $z['savepath'] . $z['savename'];
$_POST['goods_big_img'] = $bigpathname;
//给上传图片制作缩略图
$im = new\Think\Image();
$im ->open($bigpathname);//打开被处理的图片
$im ->thumb(125,125);//制作缩略图
//保存缩略图到服务器
$smallpathname =$up -> rootPath . $z['savepath'] . "small_" . $z['savename'];
$im ->save($smallpathname);//保存缩略图到服务器
//把制作好的缩略图保存到数据表记录
$_POST['goods_small_img'] = $smallpathname;
}
//exit;
$info = $goods->create();
$z = $goods ->add($info);
if ($z) {
//页面跳转
//$this->redirect(分组/控制器/操作方法,参数array,间隔时间,提示信息);
// $this ->redirect("showlist",array(), 2, '数据添加成功');
//如果设置参数,可以get方法传递,比如
$this ->redirect("showlist",array('name' => 'hah','age' => 12), 2, '数据添加成功');
} else {
$this ->redirect("tianjia",array(), 2, '数据添加失败');
}
} else {
$this ->display();
}
3. 在模板中显示图片
设置图片路径的常量在index.php中
//设置静态图片地址
define('SITE_URL','http://www.php2.com/tp/shop/');
在添加方法中,去除图片的./
$_POST['goods_big_img']= ltrim($bigpathname, './');
$_POST['goods_small_img']= ltrim($smallpathname, './');
十、 数据分页
在shop项目下,创建Tools目录,创建Page.class.php类
<?php
/*
* 分页工具类
*/
namespace Tools;
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(){
returnmin($this->page*$this->listRows,$this->total);
}
private function first(){
$html = "";
if($this->page==1)
$html.='';
else
$html.=" <ahref='{$this->uri}&page=1'>{$this->config["first"]}</a> ";
return $html;
}
private function prev(){
$html = "";
if($this->page==1)
$html.='';
else
$html.=" <ahref='{$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.=" <ahref='{$this->uri}&page={$page}'>{$page}</a> ";
}
$linkPage.=" {$this->page} ";
for($i=1; $i<=$inum;$i++){
$page=$this->page+$i;
if($page<=$this->pageNum)
$linkPage.=" <ahref='{$this->uri}&page={$page}'>{$page}</a> ";
else
break;
}
return $linkPage;
}
private function next(){
$html = "";
if($this->page==$this->pageNum)
$html.='';
else
$html.=" <ahref='{$this->uri}&page=".($this->page+1)."'>{$this->config["next"]}</a> ";
return $html;
}
private function last(){
$html = "";
if($this->page==$this->pageNum)
$html.='';
else
$html.=" <ahref='{$this->uri}&page=".($this->pageNum)."'>{$this->config["last"]}</a> ";
return $html;
}
private function goPage(){
return' <input type="text"onkeydown="javascript:if(event.keyCode==13){varpage=(this.value>'.$this->pageNum.')?'.$this->pageNum.':this.value;location=\''.$this->uri.'&page=\'+page+\'\'}"value="'.$this->page.'" style="width:25px"><inputtype="button" value="GO" onclick="javascript:varpage=(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;
}
}
在商品列表方法中实现分页效果
public function showlist(){
//实现分页效果
//实例化普通model对象
$goods = new\Model\GoodsModel();
//获得数据的总记录数
$total = $goods ->count();
$per = 7;
//实例化分页对象
$page_obj = new\Tools\Page($total, $per);
//拼接sql语句获得每页信息
$sql = "select *from sw_goods order by goods_id desc " . $page_obj -> limit;
$info = $goods ->query($sql);
//获得页码列表
$pagelist = $page_obj-> fpage();
$this ->assign('pagelist', $pagelist);
$this ->assign('info', $info);
$this -> display();
}
十一、 登陆功能的实现
实现后台的登陆功能。
在管理员控制类的登陆方法中
public function login(){
//两个业务逻辑,展示表单,收集表单
if ($_POST) {
//验证码的校验
$vry = new\Think\Verify();
if ($vry ->check($_POST['captcha'])) {
//验证用户名和密码
$manager = new\Model\ManagerModel();
$info = $manager-> checkNamePwd($_POST['admin_user'], $_POST['admin_psd']);
if ($info) {
//给用户信息做session持久化操作
session('admin_id', $info['mg_id']);
session('admin_name', $info['mg_name']);
//页面跳转到后台页面
$this ->redirect('Index/index');
} else {
echo"cuowu";
}
} else {
echo "错误";
}
}
$this->display();
}
实现退出方法
//退出系统
function logout(){
session(null);
$this ->redirect("login");
}
十二、 RBAC
role base access control,基于角色的用户访问权限。
在每个角色下有一些权限。用户à组à权限。用户和组对应,组和权限对应。管理员只需要考虑用户是哪个组即可,操作非常容易、简便、高效。是科学的权限设置方式。
1. 数据表的设计
一个管理员表,一个角色表,一个权限表。
管理员表中有管理员的基本信息和角色id。角色表中有角色信息,角色具有的权限的id,角色的权限的控制器和操作组成的字符串。权限表中有权限的基本信息,父权限的id,权限的控制器,权限的操作,权限的全路径(父权限id-父权限id-本身id),权限等级。
2. 用户登陆系统显示对应权限
//左侧
public function left(){
//计划,admin_id-->role_id-->auth_ids
$admin_id =session('admin_id');
$admin_name =session('admin_name');
//获得管理员信息
$admin_info =D('Manager') -> find($admin_id);
//获得角色id
$role_id =$admin_info['mg_role_id'];
//获得权限ids
$role_info = D('Role')-> find($role_id);
$auth_ids =$role_info['role_auth_ids'];
//获得权限信息
$auth_infoA = D('Auth') ->where("auth_level=0 and auth_id in ($auth_ids)") -> select();
$auth_infoB = D('Auth')-> where("auth_level=1 and auth_id in ($auth_ids)") ->select();
$this ->assign('auth_infoA', $auth_infoA);
$this ->assign('auth_infoB', $auth_infoB);
// dump($auth_infoA);
// dump($auth_infoB);
$this->display();
}
3. 设置超级管理员显示全部权限
使用判断显示全部权限
public function left(){
//计划,admin_id-->role_id-->auth_ids
$admin_id =session('admin_id');
$admin_name =session('admin_name');
//获得管理员信息
$admin_info =D('Manager') -> find($admin_id);
//获得角色id
$role_id =$admin_info['mg_role_id'];
//获得权限ids
$role_info = D('Role')-> find($role_id);
$auth_ids = $role_info['role_auth_ids'];
//获得权限信息
//admin要显示全部权限
if($admin_name==='admin') {
$auth_infoA =D('Auth') -> where("auth_level=0") -> select();
$auth_infoB =D('Auth') -> where("auth_level=1") -> select();
}else {
$auth_infoA =D('Auth') -> where("auth_level=0 and auth_id in ($auth_ids)")-> select();
$auth_infoB =D('Auth') -> where("auth_level=1 and auth_id in ($auth_ids)")-> select();
}
$this -> assign('auth_infoA',$auth_infoA);
$this ->assign('auth_infoB', $auth_infoB);
// dump($auth_infoA);
// dump($auth_infoB);
$this->display();
}
然后,注意,将功能标签与超链接修改
href=” {$smarty.const.__MODULE__}/{$v1.auth_c}/{$v1.auth_a}”;
4. 角色列表展示
创建RoleController控制器showlist方法
<?php
/*
* 后台角色控制器
*/
namespace Admin\Controller;
use Think\Controller;
class RoleController extends Controller {
function showlist(){
//获取角色全部数据
$info = D('Role') ->select();
$this ->assign('info', $info);
$this -> display();
}
}
5. 权限分配
创建分配权限的方法,展示权限表单
//权限分配
function fenpei($role_id){
//根据$role_id获得被分配权限的角色信息
$role_info = D('Role')-> find($role_id);
//获得被分配的全部权限并分配给模板使用
$auth_infoA = D('Auth')-> where("auth_level=0") -> select();
$auth_infoB = D('Auth')-> where("auth_level=1") -> select();
$this ->assign('auth_infoA', $auth_infoA);
$this ->assign('auth_infoB', $auth_infoB);
$this ->assign('role_info', $role_info);
$this -> display();
}
6. 收集权限表单的信息
注意给角色分配角色,需要以隐藏域传递其id。<input type="hidden" name="role_id"value="{$role_info.role_id}" />
完善分配权限的方法
//权限分配
function fenpei($role_id){
//两个逻辑,展示,收集
$role = new\Model\RoleModel();
if(!empty($_POST['authid'])) {
//处理表单传递的数据
$z = $role ->saveAuth($_POST['authid'], $_POST['role_id']);
if ($z) {
$this ->redirect('showlist', array(), 2, '分配权限成功');
} else {
$this ->redirect('fenpei', array('role_id' => $role_id), 2, '分配权限失败');
}
} else {
//根据$role_id获得被分配权限的角色信息
$role_info = $role-> find($role_id);
//获得被分配的全部权限并分配给模板使用
$auth_infoA =D('Auth') -> where("auth_level=0") -> select();
$auth_infoB =D('Auth') -> where("auth_level=1") -> select();
$this ->assign('auth_infoA', $auth_infoA);
$this ->assign('auth_infoB', $auth_infoB);
$this ->assign('role_info', $role_info);
$this ->display();
}
}
创建RoleModel类和处理authid和更新数据表的方法
<?php
/*
*role模型
*/
namespace Model;
use Think\Model;
//父类Model:Think/Library/Think/Model.class.php
class RoleModel extends Model {
//权限分配
function saveAuth($authid,$role_id){
//把$authid由array变为string
$authids = implode(',',$authid);
//根据字符串的authid查询分配的全部权限信息获得它们的控制器和方法
$auth_info = D('Auth')-> select($authids);
$s = "";
foreach ($auth_info as $k=> $v) {
if(!empty($v['auth_c']) && !empty($v['auth_a'])) {
$s .=$v['auth_c'] . "-" . $v['auth_a'] . ",";
}
}
$s = rtrim($s, ',');//去除右侧,
$sql = "updatesw_role set role_auth_ids='$authids',role_auth_ac='$s' whererole_id='$role_id'";
return $this ->execute($sql);
}
}
7. 在更新权限时显示已有权限
在显示分配的方式中,需要获得该角色的权限,将其转为数组,然后在模板中,判断权限是否在这个角色的权限数组中,如果在就checked。
//权限分配
function fenpei($role_id){
//两个逻辑,展示,收集
$role = new \Model\RoleModel();
if(!empty($_POST['authid'])) {
//处理表单传递的数据
$z = $role ->saveAuth($_POST['authid'], $_POST['role_id']);
if ($z) {
$this ->redirect('showlist', array(), 2, '分配权限成功');
} else {
$this ->redirect('fenpei', array('role_id' => $role_id), 2, '分配权限失败');
}
} else {
//根据$role_id获得被分配权限的角色信息
$role_info = $role-> find($role_id);
//获得已经拥有的权限信息,并变为array
$haveauth =explode(',', $role_info['role_auth_ids']);
//获得被分配的全部权限并分配给模板使用
$auth_infoA =D('Auth') -> where("auth_level=0") -> select();
$auth_infoB =D('Auth') -> where("auth_level=1") -> select();
$this ->assign('auth_infoA', $auth_infoA);
$this ->assign('auth_infoB', $auth_infoB);
$this ->assign('role_info', $role_info);
$this ->assign('haveauth', $haveauth);
$this ->display();
}
}
在模板中判断,比如:
<input type="checkbox"name="authid[]" value="{$v1.auth_id}" {ifin_array($v.auth_id, $haveauth)}checked="checked"{/if} />
8. 权限维护
展示权限列表
控制器,AuthController,操作,showlist。
使用全路径字段排序。
<?php
/*
* 后台权限控制器
*/
namespace Admin\Controller;
use Think\Controller;
class AuthController extends Controller {
//权限列表展示
function showlist(){
//获取权限全部数据
$info = D('Auth') ->order ('auth_path') -> select();
$this ->assign('info', $info);
$this -> display();
}
}
在模板中,使用权限等级字段实现缩进,比如:{$v.auth_name|indent:$v.auth_level:'../'}
9. 权限添加
展示权限表单
//添加权限
function tianjia(){
//两个逻辑,展示,收集
$auth = new\Model\AuthModel();
if ($_POST) {
dump($_POST);
} else {
//获得被选取的上级权限
$auth_infoA = $auth-> where('auth_level=0') -> select();
$this ->assign('auth_infoA', $auth_infoA);
$this ->display();
}
}
收集表单信息,并录入数据表
function tianjia(){
//两个逻辑,展示,收集
$auth = new\Model\AuthModel();
if ($_POST) {
//表单收集到4个信息,另两个字段需要计算
//在saveData方法中实现path/level的制作,最终完成整条记录信息
$z = $auth ->saveData($_POST);
if ($z) {
$this ->redirect('showlist', array(), 2, '添加权限成功');
} else {
$this ->redirect('tianjia', array(), 2, '添加权限失败');
}
} else {
//获得被选取的上级权限
$auth_infoA = $auth-> where('auth_level=0') -> select();
$this ->assign('auth_infoA', $auth_infoA);
$this ->display();
}
}
创建AuthModel中的制作path和level,更新整条数据的方法
<?php
/*
*auth模型
*/
namespace Model;
use Think\Model;
//父类Model:Think/Library/Think/Model.class.php
class AuthModel extends Model {
//实现path/level的制作,并完成新记录的全部字段的写入
function saveData($data){
//先将4个字段插入数据表,并获得生成的id
$newid = $this ->add($data);
//根据新生成的id,制作path和level
//path,如果本身是顶级权限,path就是新生成的id
//level,如果本身是顶级权限,level为0,否则为1
if ($data['auth_pid'] ==0) {
$path = $newid;
$level = 0;
} else {
$path =$data['auth_pid'] . "-" . $newid;
$level = 1;
}
//生成level也可以计算path中-的个数得到
// $level =substr_count($path, '-');
//更新新生成的记录的path和level
$sql = "updatesw_auth set auth_path='$path',auth_level=$level where auth_id=$newid";
return $this -> execute($sql);
}
}
10. 禁止翻墙访问
如果只是根据程序的逻辑设计根据用户-角色显示其对应的权限,但是并没有限制用户访问一些其不具备的权限,就会出现访问漏洞。也可以通过手动输入控制器、操作方法路由信息进而访问本身不存在的权限。这就是翻墙访问。
制作一个AdminController,通过构造方法做限制,然后让后台控制器都继承这个AdminController。然后AdminController继承tp的Controller。
<?php
/*
* 限制访问的控制器,后台控制器的父类控制器
*/
namespace Tools;
use Think\Controller;
class AdminController extends Controller {
//构造方法
function __construct() {
//先执行父类构造方法,避免功能缺失
parent::__construct();
//用户访问权限的控制过滤功能
//获得目前访问的控制器和操作方法,并连接为字符串,然后判断是否在其应该访问的范围内
$nowac = CONTROLLER_NAME. "-" . ACTION_NAME;
//获得用户的权限列表信息
$admin_id =session('admin_id');
$admin_name = session('admin_name');
$admin_info =D('Manager') -> find($admin_id);
$role_id =$admin_info['mg_role_id'];
$role_info = D('Role')-> find($role_id);
$auth_ac =$role_info['role_auth_ac'];
//strpos($s1, $s2),判断$s1从左开始第一次出现$s2下标信息,并返回该下标信息,没有出现返回false
//当前权限没有出现在权限里面
//当前权限没有出现在默认允许的权限里面
//当前用户还不是超级管理admin
$allow_ac ="Manager-login,Manager-logout,Manager-verifyImg,Index-left,Index-index,Index-head,Index-right";
if (strpos($auth_ac,$nowac) === false && strpos($allow_ac, $nowac) === false &&$admin_name != 'admin') {
exit("没有权限");
}
}
}
11. 登陆验证
没有登陆系统的用户访问后台,要禁止其自动跳转到登陆页面去。在AdminController中控制登陆验证。
<?php
/*
* 限制访问的控制器,后台控制器的父类控制器
*/
namespace Tools;
use Think\Controller;
class AdminController extends Controller {
//构造方法
function __construct() {
//先执行父类构造方法,避免功能缺失
parent::__construct();
//用户访问权限的控制过滤功能
//获得目前访问的控制器和操作方法,并连接为字符串,然后判断是否在其应该访问的范围内
$nowac = CONTROLLER_NAME. "-" . ACTION_NAME;
//获得用户的权限列表信息
$admin_id = session('admin_id');
$admin_name =session('admin_name');
//session中admin_id或admin_name为空
//没有登陆系统的判断
$group_url = __MODULE__;
$yunxu_ac ="Manager-login, Manager-verifyImg";
// if (empty($admin_id)&& $nowac != 'Manager-login' && $nowac != 'Manager-verifyImg'){
// $this ->redirect('Manager/login');
// }
if (empty($admin_id)&& strpos($yunxu_ac, $nowac) === false) {
// $this ->redirect('Manager/login');
$js = <<<eof
<scripttype="text/javascript">
//top使得整个frame框架都条状
window.top.location.href = "$group_url/Manager/login";
</script>
eof;
echo $js;
}
$admin_info =D('Manager') -> find($admin_id);
$role_id =$admin_info['mg_role_id'];
$role_info = D('Role')-> find($role_id);
$auth_ac =$role_info['role_auth_ac'];
//strpos($s1, $s2),判断$s1从左开始第一次出现$s2下标信息,并返回该下标信息,没有出现返回false
//当前权限没有出现在权限里面
//当前权限没有出现在默认允许的权限里面
//当前用户还不是超级管理admin
$allow_ac ="Manager-login,Manager-logout,Manager-verifyImg,Index-left,Index-index,Index-head,Index-right";
if (strpos($auth_ac, $nowac) === false&& strpos($allow_ac, $nowac) === false && $admin_name !='admin') {
exit("没有权限");
}
}
}
- thinkphp
- thinkphp
- ThinkPHP
- Thinkphp
- ThinkPHP
- thinkphp
- ThinkPHP
- THINKPHP
- thinkphp
- thinkphp ....
- thinkphp
- thinkphp
- thinkPhp
- thinkphp
- thinkphp
- thinkphp
- ThinkPHP
- thinkphp
- 【POJ2778】
- 生活小记73
- ACE中的Proactor和Reactor
- IT风投
- 2018 最具就业前景的 7 大编程语言,Java、Python 和 JavaScript 前三无悬念?
- thinkPHP
- 12月20日 数据结构 周三
- chmod -chown-umask-隐藏权限lsattr/chattr
- kali linux java的安装目录
- 数据结构 排序 直接插入排序(1)
- Spark中的稀疏向量SparseVector类的源码解读
- AsyncTask源码分析
- 浅析Java web程序之客户端和服务器端交互原理
- HDU 1574 RP问题 (类0 1 背包)