时间:2021-07-01 10:21:17 帮助过:7人阅读
// +----------------------------------------------------------------------// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]// +----------------------------------------------------------------------// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.// +----------------------------------------------------------------------// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )// +----------------------------------------------------------------------// | Author: liu21st // +----------------------------------------------------------------------/**
* Think 系统函数库
*/// 同学们,上节课,我们已经 完成了 thinkphp 各种预定义变量的 定义// 重点可以分成以下几点:// 第一:对于 需要 web 加载的 也就是项目文档 thinkphp 采取的 dirname 的方式进行的组合跟加载// 其特点是 ////// 这样的斜杠// 对于 需要引入的文件 也就是 thinkphp 核心框架部分 其加载方式 采取了 __DIR__的方式进行加载// 其特点是 \\\\ 这样的反斜杠// ps 当然这些都是基于 window 下的方向, 也就分成了 两个 路径的 始祖 app_PATH 跟 think_PATH// 第二:我们可以进行记录的事情是// 作为一个框架程序,需要有能力记录 该脚本执行的 时间 跟 内存的消耗,所以 就毫不犹豫的开启了 mirctime 跟 memory_get_usage// 此刻作为 时间 跟 内存的起点。// 第三:值得我们注意的地方是:// 使用了 const 跟 define 定义了 系统常量,但是 感觉就是,必须一成不变的,用了 const// 就代表这个,彻底就固化死了,define 是可以让用户 在创建 自己的 app 中 进行修改的。 在系统进行定义之前,会判读是否定义,// const 是没有 办法重新定义的// 总结 基本没什么区别,这两个, 唯一就是用法啊,编译上的一个区别!// 第四:对 系统预定义变量[GPC] 跟 文本 数据流// 基本上可以说是进行 非转义处理 么有 addsalshe 之类的// 第五:判读了 php 跟 web服务器 的 通信方式 cgi// 判读了操作系统// 判读 了 是否 脱离服务器运行 的命令行工具// 第六: 针对于 ROOT 跟 _FILE_ 文件的定义 不统一,重新进行了多平台定义,增强了平台的可以移植性。// 总之:就是 规范了定义 以及其 跨平台特性!// 接下来我们讲针对与 functions.php 这些 公共函数 为大家进行讲解 20151205/**
* 实例化多层控制器 格式:[资源://][模块/]控制器
* @param string $name 资源地址
* @param string $layer 控制层名称
* @param integer $level 控制器层次
* @return Think\Controller|false
*/// 此函数 进行 多层控制器实例化 功能 方便其内部调用,在写 app 应用的时候比较少用。functionA($name,$layer='',$level=0) {static$_action = array();// 此处定义静态化 存储数组 为其实现 单列实例化模式$layer = $layer? : C('DEFAULT_C_LAYER'); //'DEFAULT_C_LAYER' => 'Controller', // 默认的控制器层名称$level = $level? : ($layer == C('DEFAULT_C_LAYER')?C('CONTROLLER_LEVEL'):1); // 'CONTROLLER_LEVEL' => 1,if(isset($_action[$name.$layer]))// 根据传入的控制器 以及其对应的层级 默认:Controller 1 层级 返回return$_action[$name.$layer];
$class = parse_res_name($name,$layer,$level); // 根据其传入的控制器 名称 层级 类名 获取对应的 class 名称if(class_exists($class)) { // 如果说 根据上述的生成 class 名称 如果存在 就进行实例化$action = new$class(); // 实例化$_action[$name.$layer] = $action;// 存放 实例化对象到静态数组中return$action;// 返回实例化 情况
}else {
returnfalse;
}
// 例如: $name = 'admin' 结果就是 $class = AdminController.class.php 文件 下的 AdiminController 类。
}
// 总结: 其实这个,就是根据你传入的 $name 返回 不同的 实例化对象。$name 可以存在的选项为:// A('[项目://][分组/]模块','控制器层名称') 目前感觉这个level 基本上用不到。// 等待拯救// 好的,同学们我们今天继续,昨天了解A函数,其实就是一个 根据不同参数去实例化不同 控制器类的 一个功能函数// 注意 A函数中 加入了一个 把不同输入参数 转换的 对应类的名称跟位置// 接下来我们来看一下 B 函数的功能/**
* 执行某个行为
* @param string $name 行为名称
* @param string $tag 标签名称(行为类无需传入)
* @param Mixed $params 传入的参数
* @return void
*/functionB($name, $tag='',&$params=NULL) {if(''==$tag){
$name .= 'Behavior';
}
return \Think\Hook::exec($name,$tag,$params);
}
// 从字面意义上来说,这个是个 执行某个行为的函数,// 如果 没有对应的 标签,也就是 默认的行为就是 找到钩子函数进行执行// 另外注意一点 就是其 $params 其实是一个 引入传值,并不是一个 普通的复制传值,这样,可以无需返回就改变了传入的参数。// 根据其 钩子函数的 特殊情况,一般其配置在 Addons 下面// 默认是 $name Behavior 联合// 默认的执行函数是run// 文件位置 "Addons\\{$name}\\{$name}Addon";// $class = $name.'Behavior';// $tag = 'run';// return $addon->$tag($params);// 总结,其实B函数,就是执行插件【内部/外部】的两种,引入插件的开始位置。执行开始函数。// return $class->run(参数);// 下面继续我们的学习,这个C函数,是一个非常常用的函数,如果说AB我们可以一般的略过,这个就要我们仔细研究一下啦///**
* 获取和设置配置参数 支持批量定义
* @param string|array $name 配置变量
* @param mixed $value 配置值
* @param mixed $default 默认值
* @return mixed
*/functionC($name=null, $value=null,$default=null) {// 定义 初始化容器 ,仅能一次初始化的static$_config = array();// 经典的静态全局变量注册,执行单一流程时有效,其实,对于多页面不同加载的话,效果不明显。是一个可以优化的地方。// 无参数时获取所有 情况1if (empty($name)) { // 这个是一个大招,也就是,当调用 C()的时候,注意,内部为空的时候, 就把你全家的都返回出去了。return$_config;
}
// 优先执行设置获取或赋值 情况 2if (is_string($name)) { // 如果 是个字符串,也不下面数组的形式if (!strpos($name, '.')) { // 此处可以记作 2.1 如果 没有 连接符号,这个我觉得有点多次一举了,但是 是为了兼容数组的保存形式。老刘啊,你真的不容易啊。$name = strtoupper($name); // 不关什么 字母,统统大写,这个其实是兼容的一个好的处理方式,同学们可以借鉴哦!if (is_null($value)) // 这里其实 是可以分的 此处记作2.1.1returnisset($_config[$name]) ? $_config[$name] : $default; // 此处的三元,真的很高明, 可以分成 2.1.1.1 跟 2.1.1.2$_config[$name] = $value; // 此处记作 2.1.2 你懂了吗returnnull; //这些是各种中条件细分// 总结就是 C('name','zhangsan'); 就是赋值 name 为张三// 如果 $name = C('name') 就是读取 name的赋值,如果刚刚执行过上面的语句的话// 那么 $name 就是 张三了
}
// 二维数组设置和获取支持$name = explode('.', $name); // 这里仅仅是添加了 二维数组的支持 这里有个问题,就是 二维数组的 子元素没有变成大写$name[0] = strtoupper($name[0]);
if (is_null($value))
returnisset($_config[$name[0]][$name[1]]) ? $_config[$name[0]][$name[1]] : $default;
$_config[$name[0]][$name[1]] = $value;
returnnull;
}
// 批量设置 情况3 直接合并数据了 其实并不很常用,原因是容易搞晕,对于我这种小智商的人,就算了,不过,偶尔会用一下。if (is_array($name)){
$_config = array_merge($_config, array_change_key_case($name,CASE_UPPER));
returnnull;
}
// 其它 情况returnnull; // 避免非法参数
}
// 好的,感谢同学们,我们下节课继续!// 其实上节课程中我们讲到C函数,这里有一思路,就函数尽量不要收到配置文件的限制,// 我们今天继续D函数,这个函数在 thinkphp的使用中,是贯穿始终的。// 这个是一个 实例化 Model 类的 函数/**
* 实例化模型类 格式 [资源://][模块/]模型
* @param string $name 资源地址
* @param string $layer 模型层名称
* @return Think\Model
*/functionD($name='',$layer='') {if(empty($name)) returnnew Think\Model; // 如果输入参数为空,直接返回默认的 Modelstatic$_model = array(); // 否则就可以建立 静态 实例化仓库$layer = $layer? : C('DEFAULT_M_LAYER'); // 这里进行默认层的确认,就是if(isset($_model[$name.$layer])) // 同样的道理 存在就返回,其实就是单列的应用思想return$_model[$name.$layer];
$class = parse_res_name($name,$layer); //通过解析 获取到对应的 类名 这个函数 是包含导入文件功能的,牛叉吧if(class_exists($class)) { // 如果存在 就直接加载 并且实例化$model = new$class(basename($name));
}elseif(false === strpos($name,'/')){ // 如果说没有找到类文件 也就是没有找到类// 自动加载公共模块下面的模型if(!C('APP_USE_NAMESPACE')){ // 就去 公共模型下面寻找, 如果没有指定公共模型
import('Common/'.$layer.'/'.$class); // 默认公共模型存放位置
}else{
$class = '\\Common\\'.$layer.'\\'.$name.$layer;// 实在不行就去实例化 默认的类了
}
$model = class_exists($class)? new$class($name) : new Think\Model($name);
}else { // 否则的日志记录错误 实例化一个基础的类 给 返回回去
Think\Log::record('D方法实例化没找到模型类'.$class,Think\Log::NOTICE);
$model = new Think\Model(basename($name));
}
$_model[$name.$layer] = $model; // 存入历史记录return$model;// 返回当期实例化的类 3中方式进行的实例化
}
// 抛出异常 基本上就是个封装了 直接转的 但是在他的核心代码里面 也没什么东西了。// 仅仅是 继承了 php 默认的异常类/**
* 抛出异常处理
* @param string $msg 异常消息
* @param integer $code 异常代码 默认为0
* @throws Think\Exception
* @return void
*/functionE($msg, $code=0) {thrownew Think\Exception($msg, $code);
}
// 这个是一通过文件进行快速 数据 保存跟读取操作的事情。/**
* 快速文件数据读取和保存 针对简单类型数据 字符串、数组
* @param string $name 缓存名称
* @param mixed $value 缓存值
* @param string $path 缓存路径
* @return mixed
*/functionF($name, $value='', $path=DATA_PATH) {static$_cache = array(); // 老一套啊,看起来用的很顺手啊,$filename = $path . $name . '.php'; // 文件目录,也很简单。 直接使用的php 文件if ('' !== $value) { // 如果有数值if (is_null($value)) { // 如果存在的数值为空的话// 删除缓存if(false !== strpos($name,'*')){ // 如果保存的对象中中存在 * 号,错误returnfalse; // TODO
}else{
unset($_cache[$name]);// 删除数据缓存return Think\Storage::unlink($filename,'F'); // 删除数据文件
}
} else {
Think\Storage::put($filename,serialize($value),'F'); // 用序列化的方式 写入文件// 缓存数据$_cache[$name] = $value; // 并且写入缓存returnnull;
}
}
// 获取缓存数据if (isset($_cache[$name])) // 跟其 通用 C 很像啊 ,return$_cache[$name];
if (Think\Storage::has($filename,'F')){ // 读取 存在的文件$value = unserialize(Think\Storage::read($filename,'F'));
$_cache[$name] = $value; // 返回数据
} else {
$value = false;
}
return$value; //返回数据
}
// 就是一个缓存数据的读取,跟 file 相比 差得多了// 好的, 各位同学,继续// 这里给大家提示一点,框架中的 叫做 functions.php 应用中的叫做 function.php// 大家 明白我此刻说的应用里面的位置吗?// 如果作为一个函数的注释来说,该函简洁明了/**
* 记录和统计时间(微秒)和内存使用情况
* 使用方法:
*
* G('begin'); // 记录开始标记位
* // ... 区间运行代码
* G('end'); // 记录结束标签位
* echo G('begin','end',6); // 统计区间运行时间 精确到小数后6位 时间 用数字
* echo G('begin','end','m'); // 统计区间内存使用情况 内存用m表示
* 如果end标记位没有定义,则会自动以当前作为标记位
* 其中统计内存使用需要 MEMORY_LIMIT_ON 常量为true才有效
*
* @param string $start 开始标签
* @param string $end 结束标签
* @param integer|string $dec 小数位或者m
* @return mixed
*/// 这里不得不说 thinkphp 的创始人,特别喜欢干的一个事情,就是,根据输入参数的不同实现不同的意义// 如 C 函数 F 函数,都是 ,如果仅仅输入 单一参数 表示读取数字, 2 个参数表示 设定数值,3 个参数一般多加了默认值// number_format — 以千位分隔符方式格式化一个数字// $nombre_format_francais = number_format($number, 2, ',', ' ');functionG($start,$end='',$dec=4) {static$_info = array(); // 这个是时间仓库static$_mem = array(); // 这个是内存仓库if(is_float($end)) { // 记录时间 如果传值如此 G('start',2342353234.453); 就是个记录 跟上面的风格保持一致// 有 小数 传入 就是 结束$_info[$start] = $end; // 或者 如果传值如此 G('start',microtime(TRUE));
}elseif(!empty($end)){ // 统计时间和内存使用 也就是其默认的优先级 是 时间// 有 非数字 结尾 就是 返回 差值if(!isset($_info[$end])) $_info[$end] = microtime(TRUE);
if(MEMORY_LIMIT_ON && $dec=='m'){ // 如果开启了内存记录 并且明确是内存的记录if(!isset($_mem[$end])) $_mem[$end] = memory_get_usage(); // 获取内存记录return number_format(($_mem[$end]-$_mem[$start])/1024); // 获取返回的格式化数值
}else{
return number_format(($_info[$end]-$_info[$start]),$dec); // 返回格式化的位数 默认4位小数
}
}else{ // 记录时间和内存使用// 单独的话,就是同步记录 内存 跟时间的 标志位。$_info[$start] = microtime(TRUE);
if(MEMORY_LIMIT_ON) $_mem[$start] = memory_get_usage();
}
returnnull;
}
// 无 H 函数// 今日上午面试,就到这里了,感谢!// 嗯,昨天有点匆忙,其实这个G就是一个记录时间 跟内存的函数,都过第二,第三个参数的属性// 进行区分 是记录的时间还是 其它什么的 ,但是不管怎么得瑟,都是 同时记录的时间 给内存// 通过时间跟内存的记录可以 从一个角度来反映出php 程序运行的性能// 接下来是我们强大的I输入过滤函数,支持默认值/**
* 获取输入参数 支持过滤和默认值
* 使用方法:
*
* I('id',0); 获取id参数 自动判断get或者post // 嗯,你举例的这几个,确实很常用
* I('post.name','','htmlspecialchars'); 获取$_POST['name']
* I('get.'); 获取$_GET
*
* @param string $name 变量的名称 支持指定类型
* @param mixed $default 不存在的时候默认值
* @param mixed $filter 参数过滤方法
* @param mixed $datas 要获取的额外数据源
* @return mixed
*/functionI($name,$default='',$filter=null,$datas=null) {// 第一步:指定仓库static$_PUT = null; // 默认单数据仓库// 第二步:判定输入类型if(strpos($name,'/')){ // 指定修饰符list($name,$type) = explode('/',$name,2);
}elseif(C('VAR_AUTO_STRING')){ // 默认强制转换为字符串// // 输入变量是否自动强制转换为字符串 如果开启则数组变量需要手动传入变量修饰符获取变量// 其实上面的 这个默认是false$type = 's';
}
// 第三步:数据源获取// 第三步:第一小步骤:就是分解数据源// 在一般的程序中,上面这两个是用不到的,也就是 指定 数据类型, 默认都没有指定。if(strpos($name,'.')) { // 指定参数来源list($method,$name) = explode('.',$name,2);
}else{ // 默认为自动判断$method = 'param';
}
// 第三步:第二小步骤:关联数据源// 指定数据源,常用的就是 get post 了switch(strtolower($method)) { // 其实这个用的很经典 比较之前 先 小写case'get' :
$input =& $_GET; // 取地址 用的也不错,很有想法break;
case'post' :
$input =& $_POST;
break;
case'put' :
if(is_null($_PUT)){
parse_str(file_get_contents('php://input'), $_PUT);
}
$input = $_PUT;
/*
读取POST数据
不能用于multipart/form-data类型
php://input VS $HTTP_RAW_POST_DATA
读取POST数据 */break;
case'param' :// 其实这个最不科学了,为了兼容懒人编程,switch($_SERVER['REQUEST_METHOD']) {
case'POST':
$input = $_POST;
break;
case'PUT':
if(is_null($_PUT)){
parse_str(file_get_contents('php://input'), $_PUT);
}
$input = $_PUT;
break;
default:
$input = $_GET;
}
break;
// 常用的三种输入 获取方式 GET POST PUTcase'path' : // 居然还有路径获取,我调用中从来没用过$input = array();
if(!empty($_SERVER['PATH_INFO'])){
$depr = C('URL_PATHINFO_DEPR');// 路径分隔符//'URL_PATHINFO_DEPR' => '/', // PATHINFO模式下,各参数之间的分割符号$input = explode($depr,trim($_SERVER['PATH_INFO'],$depr));
}
break;
case'request' :
$input =& $_REQUEST;
break;
case'session' :
$input =& $_SESSION;
break;
case'cookie' :
$input =& $_COOKIE;
break;
case'server' :
$input =& $_SERVER;
break;
case'globals' :
$input =& $GLOBALS;
break;
case'data' :
$input =& $datas;
break;
default:
returnnull;
}
// 第四步:明确获取变量// 4.1 获取全部数值if(''==$name) { // 获取全部变量$data = $input;
// 用过滤函数继续过滤$filters = isset($filter)?$filter:C('DEFAULT_FILTER');
if($filters) {
if(is_string($filters)){
$filters = explode(',',$filters);
}
foreach($filtersas$filter){
$data = array_map_recursive($filter,$data); // 参数过滤
}
}
// 4.2 获取 指定数值
}elseif(isset($input[$name])) { // 取值操作 如果明确一个 取值$data = $input[$name]; // 数据获取完成// 开始执行过滤$filters = isset($filter)?$filter:C('DEFAULT_FILTER');
// 存在过滤器 开始过滤if($filters) {
if(is_string($filters)){
if(0 === strpos($filters,'/')){
if(1 !== preg_match($filters,(string)$data)){ // 过滤器支持正则// 支持正则验证returnisset($default) ? $default : null;
}
}else{
$filters = explode(',',$filters);
}
}elseif(is_int($filters)){
$filters = array($filters);
}
// 进行数组过滤if(is_array($filters)){
foreach($filtersas$filter){
if(function_exists($filter)) {
$data = is_array($data) ? array_map_recursive($filter,$data) : $filter($data); // 参数过滤
}else{
$data = filter_var($data,is_int($filter) ? $filter : filter_id($filter));
if(false === $data) {
returnisset($default) ? $default : null;
}
}
}
}
}
// 对
输出数据类型进行指定 默认 字符串if(!empty($type)){
switch(strtolower($type)){
case'a': // 数组$data = (array)$data;
break;
case'd': // 数字$data = (int)$data;
break;
case'f': // 浮点$data = (float)$data;
break;
case'b': // 布尔$data = (boolean)$data;
break;
case's': // 字符串default:
$data = (string)$data;
}
}
//4.3 获取 默认的 数值了
}else{ // 变量默认值$data = isset($default)?$default:null;
}
// 最后在返回数据之前,在进行处理了,就是 如果是数组,就 执行 默认的过滤函数
is_array($data) && array_walk_recursive($data,'think_filter');
return$data;
}
// 总结,其实经过上述函数的分析大致可以这样学习的地方:// 第一:按步骤进行分支 代码书写 类似于 第一步: 1.1 1.2 第二步: 2.1 2.2 这样// 第二:依然贯穿了其传统,通过 参数 调整其输出的特色 就是各种参数的样式进行不同的兼容// 第三:就是 各种过滤函数的方便 搭配。真心不错!// 我看好你哦,哈哈!// 无 J函数// 无 K函数// 遇到 这个 L 函数 一般情况下就是 做的 语言配置。/**
* 获取和设置语言定义(不区分大小写)
* @param string|array $name 语言变量
* @param mixed $value 语言值或者变量
* @return mixed
*/functionL($name=null, $value=null) {static$_lang = array();// 老步调,定义仓库// 空参数返回所有定义// 三种方式// 第一种方式:为空if (empty($name)) // 老步调: 无输入 返回全部return$_lang;
// 判断语言获取(或设置)// 若不存在,直接返回全大写$name// 如果 字符串// 第二种方式:字符串 然后在细分 空 数组 默认 记住这里的return 其实是个神器if (is_string($name)) { // 如果是字符串$name = strtoupper($name); // 第一步:统统转换成为大写if (is_null($value)){ // 判读 是 设置 还是读取returnisset($_lang[$name]) ? $_lang[$name] : $name; // 有定义返回定义,没有定义,直接返回
}elseif(is_array($value)){ // 如果是数组// 支持变量$replace = array_keys($value); //返回包含数组中所有键名的一个新数组:foreach($replaceas &$v){ // 好复杂,这一节没看懂,嘿嘿 能看懂的在楼下回复哈!感谢$v = '{$'.$v.'}';
}
return str_replace($replace,$value,isset($_lang[$name]) ? $_lang[$name] : $name);
}
$_lang[$name] = $value; // 语言定义 否则就进行定义returnnull;
}
// 批量定义// 第三种方式:数组if (is_array($name)) // 批量 定义 array_change_key_case() 函数将数组的所有的键都转换为大写字母或小写字母。默认大写$_lang = array_merge($_lang, array_change_key_case($name, CASE_UPPER));
returnnull;
}
// 特别常用的 一款 函数 不过 我稍后会 推荐D函数 但是任何函数,都有自己的 特点/**
* 实例化一个没有模型文件的Model
* @param string $name Model名称 支持指定基础模型 例如 MongoModel:User
* @param string $tablePrefix 表前缀
* @param mixed $connection 数据库连接信息
* @return Think\Model
*///functionM($name='', $tablePrefix='',$connection='') {static$_model = array();// 一成不变的仓库if(strpos($name,':')) { // 可以组合其 代码 然后 拼接成为 类,跟 类名list($class,$name) = explode(':',$name);
}else{
$class = 'Think\\Model'; // 否则的话,执行 默认的 Model 类 实例化
}
// 这个相当于做了一个唯一值$guid = (is_array($connection)?implode('',$connection):$connection).$tablePrefix . $name . '_' . $class;
if (!isset($_model[$guid])) // 单列 单列$_model[$guid] = new$class($name,$tablePrefix,$connection); // 实例化保存后的单列return$_model[$guid]; // 这个不多说了,就这样了。
}
/**
* 设置和获取统计数据
* 使用方法:
*
* N('db',1); // 记录数据库操作次数
* N('read',1); // 记录读取次数
* echo N('db'); // 获取当前页面数据库的所有操作次数
* echo N('read'); // 获取当前页面读取次数
*
* @param string $key 标识位置
* @param integer $step 步进值
* @param boolean $save 是否保存结果
* @return mixed
*/functionN($key, $step=0,$save=false) {static$_num = array(); // 仓库if (!isset($_num[$key])) { // 如果说没有设置 当前值$_num[$key] = (false !== $save)? S('N_'.$key) : 0; // 如果设置了存储 就在S 函数中,读取处理,否则就0了
}
if (empty($step)){ // 如果没有步进设置return$_num[$key];
}else{ // 否则 按照步进 的方式前进$_num[$key] = $_num[$key] + (int)$step;
}
if(false !== $save){ // 保存结果 其实 这个是通过 缓存 读取 函数的。
S('N_'.$key,$_num[$key],$save);
}
returnnull;
}
// 无 O函数// 无 P函数// 无 Q函数// 今日到此结束,讲述了 L M N 函数 语言包 M 实例化 可以 指定实例化类 跟 连接的数据库 N 记录步骤// 不好意思,糊涂了,昨天没有更新,今天也才更新/**
* 远程调用控制器的操作方法 URL 参数格式 [资源://][模块/]控制器/操作
* @param string $url 调用地址
* @param string|array $vars 调用参数 支持字符串和数组
* @param string $layer 要调用的控制层名称
* @return mixed
*/// 把查询字符串解析到变量中// parse_str() 函数把查询字符串解析到变量中。// 注释:php.ini 文件中的 magic_quotes_gpc 设置影响该函数的输出。如果已启用,那么在 parse_str() 解析之前,变量会被 addslashes() 转换。/**
parse_str("name=Bill&age=60");
echo $name."