ThinkPHP框架学习(含数据库及I函数)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 参考文献:https://www.kancloud.cn/manual/thinkphphttps://www.jianshu.com/p/ef3ee8260b2dwww WEB子目录├─index.php 入口文件├─README.md README文件仅用于说明,实际部署的时候可以删除。

参考文献:https://www.kancloud.cn/manual/thinkphp
https://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.phpInfoModel类的文件名是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,执行的都是字符串拼接,只要可以外部操作就可以产生注入

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
6天前
|
存储 NoSQL Java
使用 Java 了解和学习 NoSQL 数据库:三个主要优势
使用 Java 了解和学习 NoSQL 数据库:三个主要优势
8 0
|
2月前
|
搜索推荐 前端开发 算法
基于用户画像及协同过滤算法的音乐推荐系统,采用Django框架、bootstrap前端,MySQL数据库
本文介绍了一个基于用户画像和协同过滤算法的音乐推荐系统,使用Django框架、Bootstrap前端和MySQL数据库构建,旨在为用户提供个性化的音乐推荐服务,提高推荐准确性和用户满意度。
125 7
基于用户画像及协同过滤算法的音乐推荐系统,采用Django框架、bootstrap前端,MySQL数据库
|
2月前
|
SQL Java 数据库连接
Hibernate 是一款开源 ORM(对象关系映射)框架,封装了 JDBC,允许以面向对象的方式操作数据库,简化了数据访问层的开发。
Hibernate 是一款开源 ORM(对象关系映射)框架,封装了 JDBC,允许以面向对象的方式操作数据库,简化了数据访问层的开发。通过映射机制,它可以自动处理对象与数据库表之间的转换,支持主流数据库,提高了代码的可移植性和可维护性。其核心接口包括 SessionFactory、Session 和 Transaction 等,通过它们可以执行数据库的 CRUD 操作。配置方面,需在项目中引入 Hibernate 及数据库驱动依赖,并创建 `hibernate.cfg.xml` 配置文件来设置数据库连接和 Hibernate 行为参数。
38 1
|
2月前
|
SQL 关系型数据库 MySQL
SQL Server、MySQL、PostgreSQL:主流数据库SQL语法异同比较——深入探讨数据类型、分页查询、表创建与数据插入、函数和索引等关键语法差异,为跨数据库开发提供实用指导
【8月更文挑战第31天】SQL Server、MySQL和PostgreSQL是当今最流行的关系型数据库管理系统,均使用SQL作为查询语言,但在语法和功能实现上存在差异。本文将比较它们在数据类型、分页查询、创建和插入数据以及函数和索引等方面的异同,帮助开发者更好地理解和使用这些数据库。尽管它们共用SQL语言,但每个系统都有独特的语法规则,了解这些差异有助于提升开发效率和项目成功率。
176 0
|
2月前
|
SQL 关系型数据库 MySQL
「Go开源」goose:深入学习数据库版本管理工具
「Go开源」goose:深入学习数据库版本管理工具
「Go开源」goose:深入学习数据库版本管理工具
|
3月前
|
开发框架 缓存 NoSQL
基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用
基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用
|
2月前
|
SQL 存储 关系型数据库
如何系统地学习数据库?
如何系统地学习数据库?【8月更文挑战第25天】
43 0
|
3月前
|
开发框架 Oracle 关系型数据库
ABP框架使用Oracle数据库,并实现从SQLServer中进行数据迁移的处理
ABP框架使用Oracle数据库,并实现从SQLServer中进行数据迁移的处理
|
2月前
|
存储 算法 数据库
带你学习DM数据库的基本操作
带你学习DM数据库的基本操作
195 0
|
2月前
|
druid Java 数据库连接
SpringBoot项目整合MybatisPlus持久层框架+Druid数据库连接池,以及实现增删改查功能
SpringBoot项目整合MybatisPlus和Druid数据库连接池,实现基本的增删改查功能。
205 0