您的位置:首页 > 编程语言 > PHP开发

[李景山php]thinkphp核心源码注释|functions.php

2016-07-19 09:07 549 查看
<?php
// +----------------------------------------------------------------------
// | 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 <liu21st@gmail.com>
// +----------------------------------------------------------------------

/**
* 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 应用的时候比较少用。
function A($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 {
return false;
}
// 例如: $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
*/
function B($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
*/
function C($name=null, $value=null,$default=null) {
// 定义 初始化容器 ,仅能一次初始化的
static $_config = array();// 经典的静态全局变量注册,执行单一流程时有效,其实,对于多页面不同加载的话,效果不明显。是一个可以优化的地方。
// 无参数时获取所有  情况1
if (empty($name)) { // 这个是一个大招,也就是,当调用 C()的时候,注意,内部为空的时候, 就把你全家的都返回出去了。
return $_config;
}
// 优先执行设置获取或赋值 情况 2
if (is_string($name)) {  // 如果 是个字符串,也不下面数组的形式
if (!strpos($name, '.')) { // 此处可以记作 2.1 如果 没有 连接符号,这个我觉得有点多次一举了,但是 是为了兼容数组的保存形式。老刘啊,你真的不容易啊。
$name = strtoupper($name); // 不关什么 字母,统统大写,这个其实是兼容的一个好的处理方式,同学们可以借鉴哦!
if (is_null($value)) // 这里其实 是可以分的 此处记作2.1.1
return isset($_config[$name]) ? $_config[$name] : $default; // 此处的三元,真的很高明, 可以分成 2.1.1.1 跟 2.1.1.2
$_config[$name] = $value; // 此处记作 2.1.2 你懂了吗
return null; //这些是各种中条件细分
// 总结就是 C('name','zhangsan'); 就是赋值 name 为张三
// 如果 $name = C('name') 就是读取 name的赋值,如果刚刚执行过上面的语句的话
// 那么 $name 就是 张三了
}
// 二维数组设置和获取支持
$name = explode('.', $name); // 这里仅仅是添加了 二维数组的支持 这里有个问题,就是 二维数组的 子元素没有变成大写
$name[0]   =  strtoupper($name[0]);
if (is_null($value))
return isset($_config[$name[0]][$name[1]]) ? $_config[$name[0]][$name[1]] : $default;
$_config[$name[0]][$name[1]] = $value;
return null;
}
// 批量设置  情况3  直接合并数据了 其实并不很常用,原因是容易搞晕,对于我这种小智商的人,就算了,不过,偶尔会用一下。
if (is_array($name)){
$_config = array_merge($_config, array_change_key_case($name,CASE_UPPER));
return null;
}
// 其它 情况
return null; // 避免非法参数
}
// 好的,感谢同学们,我们下节课继续!

// 其实上节课程中我们讲到C函数,这里有一思路,就函数尽量不要收到配置文件的限制,
// 我们今天继续D函数,这个函数在 thinkphp的使用中,是贯穿始终的。
// 这个是一个 实例化 Model 类的 函数
/**
* 实例化模型类 格式 [资源://][模块/]模型
* @param string $name 资源地址
* @param string $layer 模型层名称
* @return Think\Model
*/
function D($name='',$layer='') {
if(empty($name)) return new Think\Model; // 如果输入参数为空,直接返回默认的 Model
static $_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
*/
function E($msg, $code=0) {
throw new Think\Exception($msg, $code);
}
//  这个是一通过文件进行快速 数据 保存跟读取操作的事情。
/**
* 快速文件数据读取和保存 针对简单类型数据 字符串、数组
* @param string $name 缓存名称
* @param mixed $value 缓存值
* @param string $path 缓存路径
* @return mixed
*/
function F($name, $value='', $path=DATA_PATH) {
static $_cache  =   array(); // 老一套啊,看起来用的很顺手啊,
$filename       =   $path . $name . '.php'; // 文件目录,也很简单。 直接使用的php 文件
if ('' !== $value) { // 如果有数值
if (is_null($value)) { // 如果存在的数值为空的话
// 删除缓存
if(false !== strpos($name,'*')){ // 如果保存的对象中中存在 * 号,错误
return false; // TODO
}else{
unset($_cache[$name]);// 删除数据缓存
return Think\Storage::unlink($filename,'F'); // 删除数据文件
}
} else {
Think\Storage::put($filename,serialize($value),'F'); // 用序列化的方式 写入文件
// 缓存数据
$_cache[$name]  =   $value; // 并且写入缓存
return null;
}
}
// 获取缓存数据
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
// 大家 明白我此刻说的应用里面的位置吗?

// 如果作为一个函数的注释来说,该函简洁明了
/**
* 记录和统计时间(微秒)和内存使用情况
* 使用方法:
* <code>
* G('begin'); // 记录开始标记位
* // ... 区间运行代码
* G('end'); // 记录结束标签位
* echo G('begin','end',6); // 统计区间运行时间 精确到小数后6位  时间 用数字
* echo G('begin','end','m'); // 统计区间内存使用情况  内存用m表示
* 如果end标记位没有定义,则会自动以当前作为标记位
* 其中统计内存使用需要 MEMORY_LIMIT_ON 常量为true才有效
* </code>
* @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, ',', ' ');
function G($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();
}
return null;
}
// 无 H 函数
// 今日上午面试,就到这里了,感谢!

//  嗯,昨天有点匆忙,其实这个G就是一个记录时间 跟内存的函数,都过第二,第三个参数的属性
//  进行区分 是记录的时间还是 其它什么的 ,但是不管怎么得瑟,都是 同时记录的时间 给内存
//  通过时间跟内存的记录可以 从一个角度来反映出php 程序运行的性能

// 接下来是我们强大的I输入过滤函数,支持默认值
/**
* 获取输入参数 支持过滤和默认值
* 使用方法:
* <code>
* I('id',0); 获取id参数 自动判断get或者post // 嗯,你举例的这几个,确实很常用
* I('post.name','','htmlspecialchars'); 获取$_POST['name']
* I('get.'); 获取$_GET
* </code>
* @param string $name 变量的名称 支持指定类型
* @param mixed $default 不存在的时候默认值
* @param mixed $filter 参数过滤方法
* @param mixed $datas 要获取的额外数据源
* @return mixed
*/
function I($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 PUT
case '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:
return null;
}
// 第四步:明确获取变量
// 4.1 获取全部数值
if(''==$name) { // 获取全部变量
$data       =   $input;
// 用过滤函数继续过滤
$filters    =   isset($filter)?$filter:C('DEFAULT_FILTER');
if($filters) {
if(is_string($filters)){
$filters    =   explode(',',$filters);
}
foreach($filters as $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)){ // 过滤器支持正则
// 支持正则验证
return   isset($default) ? $default : null;
}
}else{
$filters    =   explode(',',$filters);
}
}elseif(is_int($filters)){
$filters    =   array($filters);
}
// 进行数组过滤
if(is_array($filters)){
foreach($filters as $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) {
return   isset($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
*/
function L($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)){ // 判读 是 设置 还是读取
return isset($_lang[$name]) ? $_lang[$name] : $name; // 有定义返回定义,没有定义,直接返回
}elseif(is_array($value)){ // 如果是数组
// 支持变量
$replace = array_keys($value); //返回包含数组中所有键名的一个新数组:
foreach($replace as &$v){ // 好复杂,这一节没看懂,嘿嘿 能看懂的在楼下回复哈!感谢
$v = '{$'.$v.'}';
}
return str_replace($replace,$value,isset($_lang[$name]) ? $_lang[$name] : $name);
}
$_lang[$name] = $value; // 语言定义  否则就进行定义
return null;
}
// 批量定义
// 第三种方式:数组
if (is_array($name)) // 批量 定义 array_change_key_case() 函数将数组的所有的键都转换为大写字母或小写字母。默认大写
$_lang = array_merge($_lang, array_change_key_case($name, CASE_UPPER));
return null;
}
// 特别常用的 一款 函数 不过 我稍后会  推荐D函数  但是任何函数,都有自己的 特点
/**
* 实例化一个没有模型文件的Model
* @param string $name Model名称 支持指定基础模型 例如 MongoModel:User
* @param string $tablePrefix 表前缀
* @param mixed $connection 数据库连接信息
* @return Think\Model
*/
//
function M($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]; // 这个不多说了,就这样了。
}
/**
* 设置和获取统计数据
* 使用方法:
* <code>
* N('db',1); // 记录数据库操作次数
* N('read',1); // 记录读取次数
* echo N('db'); // 获取当前页面数据库的所有操作次数
* echo N('read'); // 获取当前页面读取次数
* </code>
* @param string $key 标识位置
* @param integer $step 步进值
* @param boolean $save 是否保存结果
* @return mixed
*/
function N($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);
}
return null;
}
// 无 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."<br>";
echo $age;
*
parse_str("name=Bill&age=60",$myArray);
print_r($myArray);
*/

// print_r(pathinfo("/testweb/test.txt"));
// pathinfo() 返回一个关联数组包含有 path 的信息。
/**
Array
(
[dirname] => /testweb
[basename] => test.txt
[extension] => txt
)
* [dirname]
[basename]
[extension]
*/
/**
Class ClassA
{
function bc($b, $c) {
$bc = $b + $c;
echo $bc;
}
}
call_user_func_array(array('ClassA','bc'), array("111", "222"));
//显示 333
*/
function R($url,$vars=array(),$layer='') {
$info   =   pathinfo($url); // 解析路径
$action =   $info['basename'];  // 获取文件名
$module =   $info['dirname'];   // 获取 文件路径
$class  =   A($module,$layer); // 获取实际 class 实例化  多了一层级的 关系  如Widget
if($class){ // 如果存在 类
if(is_string($vars)) { // 如果有变量 传入
parse_str($vars,$vars); // 解析传入参数到数组
}
return call_user_func_array(array(&$class,$action.C('ACTION_SUFFIX')),$vars); //
}else{
return false;
}
}
// 总结,其实 这个R 就是 一个call_user_func_array 的升级版本,通过 url 直接进行处理。

// 还有一个半小时 今天结束。继续今天的学习
// 这个函数 其实也是个很牛叉的函数 ,好像可以 用F函数,让我们对比一下吧,看看 这两个鬼有什么区别。

/**
* 缓存管理
* @param mixed $name 缓存名称,如果为数组表示进行缓存设置
* @param mixed $value 缓存值
* @param mixed $options 缓存参数
* @return mixed
*/
function S($name,$value='',$options=null) {
static $cache   =   ''; // 仓库 仓库 仓库 又是仓库
//第一步:初始化
if(is_array($options)){ // 如果缓存 参数 其实有点乱 type 可以放到任何一个位置
// 缓存操作的同时初始化 其实就是 就是个 初始化 的过程
$type       =   isset($options['type'])?$options['type']:'';
$cache      =   Think\Cache::getInstance($type,$options);
}elseif(is_array($name)) { // 缓存初始化 // 如果缓存 参数 其实有点乱 type 可以放到任何一个位置
$type       =   isset($name['type'])?$name['type']:'';
$cache      =   Think\Cache::getInstance($type,$name);
return $cache;
}elseif(empty($cache)) { // 自动初始化 还没有的话
$cache      =   Think\Cache::getInstance();  //初始化
}
// 根据对数据 进行 设计
if(''=== $value){ // 获取缓存
return $cache->get($name); // 获取数据
}elseif(is_null($value)) { // 删除缓存
return $cache->rm($name); // 删除数据
}else { // 缓存数据
if(is_array($options)) {
$expire     =   isset($options['expire'])?$options['expire']:NULL;
}else{
$expire     =   is_numeric($options)?$options:NULL;
}
return $cache->set($name, $value, $expire); // 保存数据
}
}

// 总结,其实这个 就是 干什么的呢,关键点是那个 class 类函数
// 其实那个 函数 也没什么了

// 今天学一个新的东西,就是 写 模版引擎
/**
* 获取模版文件 格式 资源://模块@主题/控制器/操作
* @param string $template 模版资源地址
* @param string $layer 视图层(目录)名称
* @return string
*/
function T($template='',$layer=''){

// 解析模版资源地址  第一步:
if(false === strpos($template,'://')){
$template   =   'http://'.str_replace(':', '/',$template);
}
$info   =   parse_url($template); // 第二步:解析到自己的 数组里面
$file   =   $info['host'].(isset($info['path'])?$info['path']:'');
$module =   isset($info['user'])?$info['user'].'/':MODULE_NAME.'/'; // 扩展用户名
$extend =   $info['scheme']; // 扩展 文件扩展名
$layer  =   $layer?$layer:C('DEFAULT_V_LAYER'); // 层次

// 获取当前主题的模版路径
$auto   =   C('AUTOLOAD_NAMESPACE');
if($auto && isset($auto[$extend])){ // 扩展资源
$baseUrl    =   $auto[$extend].$module.$layer.'/';
}elseif(C('VIEW_PATH')){
// 改变模块视图目录
$baseUrl    =   C('VIEW_PATH');
}elseif(defined('TMPL_PATH')){
// 指定全局视图目录
$baseUrl    =   TMPL_PATH.$module;
}else{
$baseUrl    =   APP_PATH.$module.$layer.'/';
}

// 获取主题
$theme  =   substr_count($file,'/')<2 ? C('DEFAULT_THEME') : '';

// 分析模板文件规则
$depr   =   C('TMPL_FILE_DEPR');
if('' == $file) {
// 如果模板文件名为空 按照默认规则定位
$file = CONTROLLER_NAME . $depr . ACTION_NAME;
}elseif(false === strpos($file, '/')){
$file = CONTROLLER_NAME . $depr . $file;
}elseif('/' != $depr){
$file   =   substr_count($file,'/')>1 ? substr_replace($file,$depr,strrpos($file,'/'),1) : str_replace('/', $depr, $file);
}
return $baseUrl.($theme?$theme.'/':'').$file.C('TMPL_TEMPLATE_SUFFIX');
}
// 总结,其实,这货 就是返回了一个 真实的网址路径而已啦

// 今天是这个新东西,组装产品
/**
* URL组装 支持不同URL模式
* @param string $url URL表达式,格式:'[模块/控制器/操作#锚点@域名]?参数1=值1&参数2=值2...'
* @param string|array $vars 传入的参数,支持数组和字符串
* @param string|boolean $suffix 伪静态后缀,默认为true表示获取配置值
* @param boolean $domain 是否显示域名
* @return string
* 本函数不是用来验证给定 URL 的合法性的,只是将其分解为下面列出的部分。不完整的 URL 也被接受,parse_url() 会尝试尽量正确地将其解析。
* Array
(
[scheme] => http
[host] => hostname
[user] => username
[pass] => password
[path] => /path
[query] => arg=value   在问号 ? 之后
[fragment] => anchor  在散列符号 # 之后
)
* $url = 'http://username:password@hostname/path?arg=value#anchor';
*/
function U($url='',$vars='',$suffix=true,$domain=false) {
// 解析URL 其实这里传入的 url 不是 正常地址上人的 url 他重新做了组合,个人觉得不是很科学
$info   =  parse_url($url); // 解析参数  这里的解析方式 跟正常的还不太一样
// 情况 1
//    $url = 'Home/Index/index#zhangsan@www.maizi.net?name=lisi&age=32';
//    var_dump(parse_url($url));
//array (size=2)
//'path' => string 'Home/Index/index' (length=16)
//  'fragment' => string 'zhangsan@www.maizi.net?name=lisi&age=32' (length=39)

// 情况2
//    $url = 'Home/Index/index@www.maizi.net?name=lisi&age=32';
//    var_dump(parse_url($url));
//    array (size=2)
//  'path' => string 'Home/Index/index@www.maizi.net' (length=30)
//  'query' => string 'name=lisi&age=32' (length=16)
$url    =  !empty($info['path'])?$info['path']:ACTION_NAME; // 如果解析到了路径,就用解析的路径,否则就用action_name
if(isset($info['fragment'])) { // 解析锚点  就是 网页中 跳转到网站固定位置的 标记
$anchor =   $info['fragment']; // 其实这种是全的  '[模块/控制器/操作#锚点@域名]?参数1=值1&参数2=值2...'
if(false !== strpos($anchor,'?')) { // 解析参数  如果锚点 后面还有跟随的参数
list($anchor,$info['query']) = explode('?',$anchor,2);
}
if(false !== strpos($anchor,'@')) { // 解析域名  如果锚点后,还有@ 域名
list($anchor,$host)    =   explode('@',$anchor, 2);
}
}elseif(false !== strpos($url,'@')) { // 解析域名 把用户名密码 跟 域名拆分
list($url,$host)    =   explode('@',$info['path'], 2);
// '[模块/控制器/操作@域名]?参数1=值1&参数2=值2...' 这种是不全的
}
// 解析子域名  host 就是域名了
if(isset($host)) { // 不是二级域名吗  其实一般情况下是没有这个东西的
// 其实这个用法 很奇怪  一般情况下,就是 $domain = $host  这里可以能是跟随参数的
$domain = $host.(strpos($host,'.')?'':strstr($_SERVER['HTTP_HOST'],'.'));
}elseif($domain===true){ //如果显示域名 如果有添加,这里就不是 true boolen类型才可以哈
$domain = $_SERVER['HTTP_HOST']; // 显示 主机名 域名
if(C('APP_SUB_DOMAIN_DEPLOY') ) { // 开启子域名部署  默认是没有开启
$domain = $domain=='localhost'?'localhost':'www'.strstr($_SERVER['HTTP_HOST'],'.'); // 默认给他缓存了 www.你的域名啦 本地的就不管了
// '子域名'=>array('模块[/控制器]');  找到子域名的 匹配规则 实际上,可能用不到哈
foreach (C('APP_SUB_DOMAIN_RULES') as $key => $rule) {// 处理 子域名规则
$rule   =   is_array($rule)?$rule[0]:$rule;
if(false === strpos($key,'*') && 0=== strpos($url,$rule)) {
$domain = $key.strstr($domain,'.'); // 生成对应子域名
$url    =  substr_replace($url,'',0,strlen($rule));
break;
}
}
}
}

// 解析参数 解析 参数,这个是 后面传入的参数
if(is_string($vars)) { // aaa=1&bbb=2 转换成数组
parse_str($vars,$vars);
}elseif(!is_array($vars)){
$vars = array();
}
// 合并参数
if(isset($info['query'])) { // 解析地址里面参数 合并到vars
parse_str($info['query'],$params);
$vars = array_merge($params,$vars);
}

// 这里总结一下,其实就量大步骤,第一步 拆分
// 第二步:组装
// URL组装
$depr       =   C('URL_PATHINFO_DEPR'); //'/', // PATHINFO模式下,各参数之间的分割符号
$urlCase    =   C('URL_CASE_INSENSITIVE'); //// 默认false 表示URL区分大小写 true则表示不区分大小写
// 如果有 url 地址
if($url) {
if(0=== strpos($url,'/')) {// 定义路由  如果是跟目录
$route      =   true;
$url        =   substr($url,1); // 去掉第一个 斜杠
if('/' != $depr) { // 换成系统的 指定的间隔符号
$url    =   str_replace('/',$depr,$url);
}
}else{ // 也就是, 不是根目录的情况下
if('/' != $depr) { // 安全替换
$url    =   str_replace('/',$depr,$url);
}
// 解析模块、控制器和操作
$url        =   trim($url,$depr); // 删除两端的 间隔符号
$path       =   explode($depr,$url); // 解析路径
$var        =   array();
$varModule      =   C('VAR_MODULE'); // 'VAR_MODULE'            =>  'm',     // 默认模块获取变量
$varController  =   C('VAR_CONTROLLER'); //'VAR_CONTROLLER'        =>  'c',    // 默认控制器获取变量
$varAction      =   C('VAR_ACTION'); // 'VAR_ACTION'            =>  'a',    // 默认操作获取变量
$var[$varAction]       =   !empty($path)?array_pop($path):ACTION_NAME; // 通过这种方式 解析出 action
$var[$varController]   =   !empty($path)?array_pop($path):CONTROLLER_NAME;// 同上 解析出
if($maps = C('URL_ACTION_MAP')) { // 定义路由规则 默认是没有的, 所以说,这里是不执行的
if(isset($maps[strtolower($var[$varController])])) {
$maps    =   $maps[strtolower($var[$varController])];
if($action = array_search(strtolower($var[$varAction]),$maps)){
$var[$varAction] = $action;
}
}
}
if($maps = C('URL_CONTROLLER_MAP')) { // 同上
//                $a=array("a"=>"red","b"=>"green","c"=>"blue");
//                echo array_search("red",$a);
if($controller = array_search(strtolower($var[$varController]),$maps)){
$var[$varController] = $controller;
}
}
if($urlCase) { // 是否区分大小写 默认是true 代表不区分
$var[$varController]   =   parse_name($var[$varController]); // 都转换成统一的格式
}
$module =   ''; // 初始化 为空

if(!empty($path)) { // 如果路径不为空
$var[$varModule]    =   implode($depr,$path);
}else{
if(C('MULTI_MODULE')) { // 如果开启多模块 // 是否允许多模块 如果为false 则必须设置 DEFAULT_MODULE
if(MODULE_NAME != C('DEFAULT_MODULE') || !C('MODULE_ALLOW_LIST')){
$var[$varModule]=   MODULE_NAME;
}
}
}
if($maps = C('URL_MODULE_MAP')) { // 如果这里也设置路由 同上
if($_module = array_search(strtolower($var[$varModule]),$maps)){
$var[$varModule] = $_module;
}
}
if(isset($var[$varModule])){ // 同上
$module =   $var[$varModule];
unset($var[$varModule]);
}

}
}
// 其实这里才开始 真正的组合 分两种方式
// 域名
//
if(C('URL_MODEL') == 0) { // 普通模式URL转换
$url        =   __APP__.'?'.C('VAR_MODULE')."={$module}&".http_build_query(array_reverse($var));
if($urlCase){ // 全部转化小写
$url    =   strtolower($url);
}
if(!empty($vars)) { // 如果参数不为空 加入参数
$vars   =   http_build_query($vars);
$url   .=   '&'.$vars;
}
}else{ // PATHINFO模式或者兼容URL模式
if(isset($route)) {// 如果开启了 路由
$url    =   __APP__.'/'.rtrim($url,$depr);
}else{
$module =   (defined('BIND_MODULE') && BIND_MODULE==$module )? '' : $module;
$url    =   __APP__.'/'.($module?$module.MODULE_PATHINFO_DEPR:'').implode($depr,array_reverse($var));
}
if($urlCase){ // 转换
$url    =   strtolower($url);
}
if(!empty($vars)) { // 添加参数 另外的一种解析方式而已
foreach ($vars as $var => $val){
if('' !== trim($val))   $url .= $depr . $var . $depr . urlencode($val);
}
}
if($suffix) {// 如果定义了 文件后缀
$suffix   =  $suffix===true?C('URL_HTML_SUFFIX'):$suffix;
if($pos = strpos($suffix, '|')){
$suffix = substr($suffix, 0, $pos);
}
if($suffix && '/' != substr($url,-1)){
$url  .=  '.'.ltrim($suffix,'.');
}
}
}
if(isset($anchor)){ // 如果有锚点 组合上
$url  .= '#'.$anchor;
}
if($domain) { // 组合上域名
$url   =  (is_ssl()?'https://':'http://').$domain.$url;
}
return $url;
}
// 无 V函数
// 总结:
// 指导 parse_url 是对 url 地址进行解析的函数
// 其实我觉得这个函数他复杂了,就是对不同输入的 url 方式解析成为自己的方式
// 常用的是 U('Home/Index/index',array('name'=>'lijingshan','age'=>'12'));
// 默认的情况下 不会用域名 跟 锚点的,不过这两个还是不错的,哈哈,就是解析起来,不复杂,但是,组合的时候,那个路由规则有点费劲。

// 好的,今天我们继续
// 这个函数,其实就是个封装
/**
* 渲染输出Widget
* @param string $name Widget名称
* @param array $data 传入的参数
* @return void
*/
function W($name, $data=array()) {
return R($name,$data,'Widget');
}
// 无 X函数
// 无 Y函数
// 无 Z函数
// 26 字母 函数写完了,
// A 函数中调用的 函数

/**
* 解析资源地址并导入类库文件
* 例如 module/controller addon://module/behavior
* @param string $name 资源地址 格式:[扩展://][模块/]资源名
* @param string $layer 分层名称
* @param integer $level 控制器层次
* @return string
*/
function parse_res_name($name,$layer,$level=1){
// 解析扩展资源
if(strpos($name,'://')) {// 指定扩展资源
list($extend,$name)  =   explode('://',$name);// 解析扩展资源
}else{
$extend  =   '';
}
// 解析普通模块
// echo substr_count("I love Shanghai. Shanghai is the biggest city in china.","Shanghai");
// 计算 "Shanghai" 在字符串中出现的次数:
// 默认第一个 模块
if(strpos($name,'/') && substr_count($name, '/')>=$level){ // 指定模块
list($module,$name) =  explode('/',$name,2);
}else{
$module =   defined('MODULE_NAME') ? MODULE_NAME : '' ;
}
// 解析资源
$array  =   explode('/',$name);
if(!C('APP_USE_NAMESPACE')){ // 关闭应用 类库的命名空间处理
$class  =   parse_name($name, 1);
import($module.'/'.$layer.'/'.$class.$layer);// 导入对应文件即可
}else{
$class  =   $module.'\\'.$layer;
foreach($array as $name){
$class  .=   '\\'.parse_name($name, 1);
}
// 导入资源类库
if($extend){ // 扩展资源
$class      =   $extend.'\\'.$class;
}
}
return $class.$layer; // 返回资源
}
// 今日结束

// 首先我们先回顾一下,上一节的东西, W parse_res_name
// 这里 我们可以 简单 看出 W 其实就是 规定了 Widget 的 一个指定的扩展
// parse_res_name  这里要看出的关键点 就是 parse  很明细是个解析, 那么针对解析的函数来说,明确输入 形式 跟输出形式很重要

// 好的,开启我们今天的学习
/**
* 加载配置文件 支持格式转换 仅支持一级配置
* @param string $file 配置文件名
* @param string $parse 配置解析方法 有些格式需要用户自己解析
* @return array
*/
// 如果是 .ini 配置文件,就解析 配置文件
//parse_ini_file() 函数解析一个配置文件,并以数组的形式返回其中的设置
//      test.ini
//      [names]
//      me = Robert
//      you = Peter
//
//      [urls]
//      first = "http://www.example.com"
//      second = "http://www.w3school.com.cn"
//          php 代码
//        print_r(parse_ini_file("test.ini"));
//          输出
//          Array
//          (
//              [me] => Robert
//          [you] => Peter
//          [first] => http://www.example.com //          [second] => http://www.w3school.com.cn //          )
//          print_r(parse_ini_file("test.ini",true));
//          Array
//          (
//              [names] => Array
//              (
//                  [me] => Robert
//                    [you] => Peter
//                    )
//                  [urls] => Array
//                          (
//                              [first] => http://www.example.com //                    [second] => http://www.w3school.com.cn //                    )
//                  )
// 从字面意思上不难理解 这个是加载配置文件的意思
// php 文件 直接包含到内存里面
// 通过不同的文件类型  不同的加载 方式
function load_config($file,$parse=CONF_PARSE){
$ext  = pathinfo($file,PATHINFO_EXTENSION); // 此处的意思,仅仅返回其扩展名 简单点说 也就是 什么类型的文件
switch($ext){
case 'php':
return include $file;
case 'ini':
return parse_ini_file($file);
case 'yaml':
// 一种新的数据格式
return yaml_parse_file($file);
case 'xml':
// 解析 xml
return (array)simplexml_load_file($file);
case 'json':
// 解析 json
return json_decode(file_get_contents($file), true);
default:
// 默认的话,使用 自己配置的专用解析 函数  那如果是这样的话,就不用这个 函数 得瑟了
// 否则 报错
if(function_exists($parse)){
return $parse($file);
}else{
E(L('_NOT_SUPPORT_').':'.$ext);
}
}
}

/**
* 解析yaml文件返回一个数组
* @param string $file 配置文件名
* @return array
*/
// 下面的这种定义很经典
if (!function_exists('yaml_parse_file')) {
function yaml_parse_file($file) {
vendor('spyc.Spyc');// 快速 导入第三方框架 处理这个问题
return Spyc::YAMLLoad($file);
}
}
// 今日结束,感谢大家

// 今日我们开始,这个 ,又一个 封存的函数 飘过.....
/**
* 添加和获取页面Trace记录
* @param string $value 变量
* @param string $label 标签
* @param string $level 日志级别
* @param boolean $record 是否记录日志
* @return void|array
*/
function trace($value='[think]',$label='',$level='DEBUG',$record=false) {
return Think\Think::trace($value,$label,$level,$record);
}

/**
* 编译文件
* @param string $filename 文件名
* @return string
*/
// 这里进行文件 编译 php_strip_whitespace — 返回删除注释和空格后的PHP源码
function compile($filename) {
$content    =   php_strip_whitespace($filename); // php_strip_whitespace — 返回删除注释和空格后的PHP源码
$content    =   trim(substr($content, 5)); // 删除 开始 的 <?php 标签 删除 两端的空格
// 替换预编译指令  正则替换 如 //[RUNTIME](.*?)//[/RUNTIME\]   变成 ''
$content    =   preg_replace('/\/\/\[RUNTIME\](.*?)\/\/\[\/RUNTIME\]/s', '', $content);
if(0===strpos($content,'namespace')){// 如果存在命名空间 就
$content    =   preg_replace('/namespace\s(.*?);/','namespace \\1{',$content,1);
}else{
// 否则 就直接 使用
$content    =   'namespace {'.$content;
}
// 判读是否存在结束 符号,有的话,就
if ('?>' == substr($content, -2))
$content    = substr($content, 0, -2);
return $content.'}';
}
// 总结 经过运行后
// 第一:其输入参数 $filename = '1.php';
// 1.php 的源码如下
// 第二:  <?php
//          //[RUNTIME]张三//[/RUNTIME\]
//          echo 123;
// 第三:得到的结果:namespace {echo 123; }

// 周六函数解析:
// 首先从字面以上来说,这个肯定就是个 数组遍历函数
// 从recursive  发现其实好像这个是个递归的函数
// 从输入参数的定义可以看出 两个参数 分别是 过滤函数 跟 输入的数据
// 也就是可以支持多维数组
function array_map_recursive($filter, $data) {
$result = array(); // 建立临时的返回仓库
foreach ($data as $key => $val) { //遍历数组
$result[$key] = is_array($val)
? array_map_recursive($filter, $val) // 如果是数组递归处理
: call_user_func($filter, $val); // 如果非数组 调用  过滤函数处理
}
return $result;// 返回处理结果
}

// 周日函数补充

/**
* 字符串命名风格转换
* type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
* @param string $name 字符串
* @param integer $type 转换类型
* @return string
*/
// 字符串命名 风格转换
function parse_name($name, $type=0) {
if ($type) { //如果是java 风格
// 这句话的意思 就是 把所有 带有 下划线  去掉下划线,然后首字母大写  $match[0] = '_jingshan'  $match[1] = ]jingshan'
return ucfirst(preg_replace_callback('/_([a-zA-Z])/', function($match){return strtoupper($match[1]);}, $name));
} else {
// \0 全部的大写字母 都改成下划线 并且 加上原来的字母
// 第一步:Jingshan==>_Jingshan
// 第二步:删除去多于的字符
// 第三步:转换成为i小写字母
return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
}
}
// 总结:
// C 风格字符串
//echo parse_name('zhangsan_jingshanLisi');
//zhangsan_jingshan_lisi
// Java 风格字符串
//echo parse_name('zhangsan_jingshanLisi',1);
//ZhangsanJingshanLisi

// 周一代码
/**
* 优化的require_once
* @param string $filename 文件地址
* @return boolean
*/
function require_cache($filename) {
static $_importFiles = array(); // 久违了, 数据仓库
if (!isset($_importFiles[$filename])) { //如果乜嘢包含文件
if (file_exists_case($filename)) { // 如果文件都存在
require $filename;// 包含文件
$_importFiles[$filename] = true; //确认 已经包含
} else {
$_importFiles[$filename] = false; // 标志位
}
}
return $_importFiles[$filename]; // 返回文件
}

/**
* 区分大小写的文件存在判断
* @param string $filename 文件地址
* @return boolean
*/
// 首先进行 文件的判断
// realpath — 返回规范化的绝对路径名
// D:\WWW\www.maizi.net\1.css
// 如果使用 realpath('1.css') 显示 为 上面的这个
// basename 解析后的代码 就变成了 1.css
function file_exists_case($filename) {
if (is_file($filename)) {
if (IS_WIN && APP_DEBUG) {
if (basename(realpath($filename)) != basename($filename))
return false;
}
return true;
}
return false;
}
// 总结:其知识点是这样的,就是 上面的 重点,一个是 realpath 获取文件的 盘符之下的绝对路径
// basename 获取 文件名

// 这个是 thinkphp 里面自己最常用的 导入 手法
/**
* 导入所需的类库 同java的Import 本函数有缓存功能
* @param string $class 类库命名空间字符串
* @param string $baseUrl 起始路径
* @param string $ext 导入的文件扩展名
* @return boolean
*/
//$find = array("Hello","world");
//$replace = array("B",'s');
//$arr = array("Hello","world","!");
//print_r(str_replace($find,$replace,$arr));
//Array ( [0] => B [1] => s [2] => ! )

function import($class, $baseUrl = '', $ext=EXT) {
static $_file = array(); // 作为导入 数据的 思路,或者仓库来讲,都有这样的一个思路 就是静态仓库
$class = str_replace(array('.', '#'), array('/', '.'), $class);
// 上面采用的相对不是很常用的 数组替换
if (isset($_file[$class . $baseUrl]))
return true; // 如果文件已经加载了,就返回真
else
$_file[$class . $baseUrl] = true; // 否则进行加载动作,进行加载标志位的 修改
$class_strut     = explode('/', $class); // 拆分数组
if (empty($baseUrl)) {// 如果没有 跟目录 也就是基础目录
// 当默认目录 为空的情况下,会进行不同情况下的一个加载
// 就是各种模式下的一个加载
if ('@' == $class_strut[0] || MODULE_NAME == $class_strut[0]) {
//加载当前模块的类库
$baseUrl = MODULE_PATH;
$class   = substr_replace($class, '', 0, strlen($class_strut[0]) + 1);
}elseif ('Common' == $class_strut[0]) {
//加载公共模块的类库
$baseUrl = COMMON_PATH;
$class   = substr($class, 7);
}elseif (in_array($class_strut[0],array('Think','Org','Behavior','Com','Vendor')) || is_dir(LIB_PATH.$class_strut[0])) {
// 系统类库包和第三方类库包
$baseUrl = LIB_PATH;
}else { // 加载其他模块的类库
$baseUrl = APP_PATH;
}
}
if (substr($baseUrl, -1) != '/') // -1 代表倒序  判读如果 基础目录没有这个,
$baseUrl    .= '/'; // 就添加上这个
$classfile       = $baseUrl . $class . $ext;
if (!class_exists(basename($class),false)) { // 如果类不存在 base
// print_r(basename('Think/Util/Aay')) ; 结果为 Aay
// 如果类不存在 则导入类库文件
return require_cache($classfile);
}
return null;
}
// 总结,其实上面也没什么,就是个,加载类文件,针对于不同的类的输入参数,进行不同的加载而已

// 今日 的另外一种导入方式
/**
* 基于命名空间方式导入函数库
* load('@.Util.Array')
* @param string $name 函数库命名空间字符串
* @param string $baseUrl 起始路径
* @param string $ext 导入的文件扩展名
* @return void
*/
function load($name, $baseUrl='', $ext='.php') {
$name = str_replace(array('.', '#'), array('/', '.'), $name);
// 规则演化
if (empty($baseUrl)) {
// 不同输入参数的 不同输出
if (0 === strpos($name, '@/')) {//加载当前模块函数库
$baseUrl    =   MODULE_PATH.'Common/';
$name       =   substr($name, 2);
} else { //加载其他模块函数库
$array      =   explode('/', $name);
$baseUrl    =   APP_PATH . array_shift($array).'/Common/';
$name       =   implode('/',$array);
}
}
// 不为空,就清空 然后在添加
if (substr($baseUrl, -1) != '/')
$baseUrl       .= '/';
require_cache($baseUrl . $name . $ext); // 用升级版 替换包含文件
}
// 总结,这个函数 相对于 import 简直就是弱爆了,太容易了。这个让我想起来javascript 里面的 call
// 貌似很强大,然没感觉有什么鸟用

//24 日
/**
* 快速导入第三方框架类库 所有第三方框架的类库文件统一放到 系统的Vendor目录下面
* @param string $class 类库
* @param string $baseUrl 基础目录
* @param string $ext 类库后缀
* @return boolean
*/
// 又是一个变种
function vendor($class, $baseUrl = '', $ext='.php') {
if (empty($baseUrl))
$baseUrl = VENDOR_PATH;
return import($class, $baseUrl, $ext);
}
// 总结,其实,今日我是昏迷的,我26日,重新补上了,这个,感谢大家的支持
// 这里也没什么特殊的了,其实,就是一个 修改地址

// 25 日
/**
* 用于实例化访问控制器
* @param string $name 控制器名
* @param string $path 控制器命名空间(路径)
* @return Think\Controller|false
*/
function controller($name,$path=''){
$layer  =   C('DEFAULT_C_LAYER'); // 这个显然是记不住的,但是, 我搜索了一下,默认是 controller
if(!C('APP_USE_NAMESPACE')){ // 如果没有定义 用户默认的 控制器 // 自动加载的应用类库层 关闭APP_USE_NAMESPACE后有效
$class  =   parse_name($name, 1).$layer;// 这个 找到对应的类名
import(MODULE_NAME.'/'.$layer.'/'.$class); // 找到对应的 controller 控制器名称
}else{
// 也就是 个人设置的起到效果了
$class  =   ( $path ? basename(ADDON_PATH).'\\'.$path : MODULE_NAME ).'\\'.$layer;
$array  =   explode('/',$name);
foreach($array as $name){
$class  .=   '\\'.parse_name($name, 1);
}
$class .=   $layer; // 不知道上面怎么折腾的,反正最后就是返回 类名,感谢!
}
if(class_exists($class)) {
return new $class(); // 返回实例化的 类
}else {
return false;
}
}
// 总结 :没什么特殊的,这个我见多了,就是,换一个名字,实例化了,两中方式下的 控制器类
// 然后,成功了呢,就返回,否则,就是 不行。

// 26 日
/**
* 处理标签扩展
* @param string $tag 标签名称
* @param mixed $params 传入参数
* @return void
*/
function tag($tag, &$params=NULL) {
\Think\Hook::listen($tag,$params);
}
// 总结:我能默默的说,这个,我能飘过吗,其实还是有个知识点的,
// 第一:命名空间的 根目录浏览
// 静态函数的调用

// 这个是一个 重点 的提升中
/**
* 去除代码中的空白和注释
* @param string $content 代码内容
* @return string
*/
function strip_whitespace($content) {
$stripStr   = ''; // 这个,估计是返回的 数据仓库,没有特殊的原理
//分析php源码
$tokens     = token_get_all($content);
// token_get_all() 解析提供的 source 源码字符,然后使用 Zend 引擎的语法分析器获取源码中的 PHP 语言的解析器代号
// 这个表示 没怎么看懂
// 不是没有看明白函数的意思,是不明白这个用他干什么呢?
$last_space = false; // 最后一个 不容易啊
// 明白了,上面是个解析啊
for ($i = 0, $j = count($tokens); $i < $j; $i++) { // 这个 for 用的牛叉,很少这样用,不过,真心不错
if (is_string($tokens[$i])) {
$last_space = false;
$stripStr  .= $tokens[$i];
} else {
switch ($tokens[$i][0]) {
//过滤各种PHP注释
case T_COMMENT:
case T_DOC_COMMENT:
break;
//过滤空格
case T_WHITESPACE:
if (!$last_space) {
$stripStr  .= ' ';
$last_space = true;
}
break;
case T_START_HEREDOC:
$stripStr .= "<<<THINK\n";
break;
case T_END_HEREDOC:
$stripStr .= "THINK;\n";
for($k = $i+1; $k < $j; $k++) {
if(is_string($tokens[$k]) && $tokens[$k] == ';') {
$i = $k;
break;
} else if($tokens[$k][0] == T_CLOSE_TAG) {
break;
}
}
break;
default:
$last_space = false;
$stripStr  .= $tokens[$i][1];
}
}
}
return $stripStr;
}
// 总结,就是,各种过滤呗,感谢

/***
array (size=5)
0 =>
array (size=3)
0 => int 368
1 => string '<?php ' (length=6)
2 => int 1
1 =>
array (size=3)
0 => int 316
1 => string 'echo' (length=4)
2 => int 1
2 => string ';' (length=1)
3 =>
array (size=3)
0 => int 371
1 => string ' ' (length=1)
2 => int 1
4 =>
array (size=3)
0 => int 370
1 => string '?>' (length=2)
2 => int 1
*/

// 27 日
/**
* 自定义异常处理
* @param string $msg 异常消息
* @param string $type 异常类型 默认为Think\Exception
* @param integer $code 异常代码 默认为0
* @return void
*/
// 建议使用 自定义异常类进行处理
function throw_exception($msg, $type='Think\\Exception', $code=0) {
// 记录 异常类的方式
Think\Log::record('建议使用E方法替代throw_exception',Think\Log::NOTICE);
if (class_exists($type, false)) // 指定的存在的话,就这么执行
throw new $type($msg, $code);
else
Think\Think::halt($msg);        // 异常类型不存在则输出错误信息字串
// 核心框架中的 重新封装了 各种异常处理
}
// 总结:就是个打包的过程,然 没什么实质上的意义,但是,这样方便的错误的管理

// 这个是 跟 var_dump() 有的一拼的属性, 其实这个 经常用了
// 来今天我们来看看他的真面貌
/**
* 浏览器友好的变量输出
* @param mixed $var 变量
* @param boolean $echo 是否输出 默认为True 如果为false 则返回输出字符串
* @param string $label 标签 默认为空
* @param boolean $strict 是否严谨 默认为true
* @return void|string
*/
// 其实我们常用的参数里面,基本上就只有一个参数
function dump($var, $echo=true, $label=null, $strict=true) {
$label = ($label === null) ? '' : rtrim($label) . ' '; // 对标签替换我为空,经典的写法。
// 真心不错,我个人很喜欢
// 这种写法,证明 了作者,不仅仅是停留在了 函数 三元运算符的形式上,跟深入掌握理解并且灵活运用了该运算符。
if (!$strict) { // 很明显,从字面意思上可以看出是非严格算法
if (ini_get('html_errors')) {// 如果 php.ini 设置了 html_errors
// 是否在出错信息中使用HTML标记。 当前默认为打开状态
$output = print_r($var, true); // 格式化输出 注意,这里是不直接输出的,而是整理成为输出的字符串的样式。
$output = '<pre>' . $label . htmlspecialchars($output, ENT_QUOTES) . '</pre>';
} else {
// 否则 执行此格式化输出
$output = $label . print_r($var, true);
}
} else {// 否则先输出到缓存
ob_start();
var_dump($var);
$output = ob_get_clean();
if (!extension_loaded('xdebug')) { // 查看是否加载 xdebug
$output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output);
$output = '<pre>' . $label . htmlspecialchars($output, ENT_QUOTES) . '</pre>';
}
}
if ($echo) { // 如果是普通的 echo 默认是这个,就输出就可以了 不需要返回
echo($output);
return null;
}else // 否则返回
return $output;
}
// 总结:默认严谨输出,其实,就是 在print_r 的基础上,加个了pre 格式化而已
// 并且 区分了,输出,跟返回,默认是输出。就ok 了,其它的没了

// 今日播报
/**
* 设置当前页面的布局
* @param string|false $layout 布局名称 为false的时候表示关闭布局
* @return void
*/
function layout($layout) {// 这个是一个布局的函数,其实我没怎么搞懂,不,准确的说,我不怎么用这个函数
if(false !== $layout) {// 绝对的恒不等判定,看来要求,很严格啊
// 开启布局
C('LAYOUT_ON',true);// 配置其 开启布局
if(is_string($layout)) { // 设置新的布局模板
C('LAYOUT_NAME',$layout); // 进行布局 分析
}
}else{// 临时关闭布局
C('LAYOUT_ON',false);
}
}
// 总结:就是各种变量的配置
// 然在实际使用过程中,我更喜欢 exttions 继承的方式
// 可能是代码写多了的原因吧

/**
* 判断是否SSL协议
* @return boolean
*/
// 谁能告诉我什么是ssl协议
// SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层对网络连接进行加密。
// 百科上这么说的
// SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。SSL协议可分为两层: SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。 SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。
// HTTPS(Hypertext Transfer Protocol Secure)安全超文本传输协议
function is_ssl() {
if(isset($_SERVER['HTTPS']) && ('1' == $_SERVER['HTTPS'] || 'on' == strtolower($_SERVER['HTTPS']))){
return true;
}elseif(isset($_SERVER['SERVER_PORT']) && ('443' == $_SERVER['SERVER_PORT'] )) {
return true;
}
return false;
}
// 总结:就是看看传送的协议是否是安全的而已了

// 重新定向的代码发送
/**
* URL重定向
* @param string $url 重定向的URL地址
* @param integer $time 重定向的等待时间(秒)
* @param string $msg 重定向前的提示信息
* @return void
*/
function redirect($url, $time=0, $msg='') {
//多行URL地址支持
$url        = str_replace(array("\n", "\r"), '', $url);
if (empty($msg))
$msg    = "系统将在{$time}秒之后自动跳转到{$url}!";
if (!headers_sent()) {
// redirect
if (0 === $time) {// 立即跳转
header('Location: ' . $url);
} else { // 刷新跳转
header("refresh:{$time};url={$url}");
echo($msg);
}
exit();
} else { // 另外的一种跳转方式
$str    = "<meta http-equiv='Refresh' content='{$time};URL={$url}'>";
if ($time != 0)
$str .= $msg;
exit($str);
}
}
// 总结:进行了多种不同情况的一个跳转 没发送就用头跳转 发送了,就用 meta 跳转
//headers_sent() 函数检查 HTTP 标头是否已被发送以及在哪里被发送。
//如果报头已发送,则返回 true,否则返回 false。

// 今日继续
/**
* 根据PHP各种类型变量生成唯一标识号
* @param mixed $mix 变量
* @return string
*/
// 这个其实就是变量的 选择性加密 增强了我的加密判断
function to_guid_string($mix) {
if (is_object($mix)) {
return spl_object_hash($mix);
} elseif (is_resource($mix)) {
$mix = get_resource_type($mix) . strval($mix);
} else {
$mix = serialize($mix);
}
return md5($mix);
}

/**
* $c = mysql_connect();
echo get_resource_type($c)."\n";
// 打印:mysql link

$fp = fopen("foo","w");
echo get_resource_type($fp)."\n";
// 打印:file

$doc = new_xmldoc("1.0");
echo get_resource_type($doc->doc)."\n";
// 打印:domxml document
*/
// strval 将变量转成字符串类型。
// 总结:就是个不同的加密,嘿嘿

// 今日继续 30 日
// 这个其实 是一个补充的编码类型
/**
* XML编码
* @param mixed $data 数据
* @param string $root 根节点名
* @param string $item 数字索引的子节点名
* @param string $attr 根节点属性
* @param string $id   数字索引子节点key转换的属性名
* @param string $encoding 数据编码
* @return string
*/
function xml_encode($data, $root='think', $item='item', $attr='', $id='id', $encoding='utf-8') {
if(is_array($attr)){ // 判读是否是数组
$_attr = array();
foreach ($attr as $key => $value) {
$_attr[] = "{$key}=\"{$value}\"";
} // 将数组进行转存
$attr = implode(' ', $_attr); // 空格 变成其 字符串
}
// 上面就是重新规整一下数组。
$attr   = trim($attr); // 清楚两端多于空格
$attr   = empty($attr) ? '' : " {$attr}"; // 为空为 : 否则为数据
$xml    = "<?xml version=\"1.0\" encoding=\"{$encoding}\"?>";
$xml   .= "<{$root}{$attr}>";
$xml   .= data_to_xml($data, $item, $id); // 又是一内核函数
$xml   .= "</{$root}>";
return $xml;
}
// 总结:就是 转换形式。

/**
* 数据XML编码
* @param mixed  $data 数据
* @param string $item 数字索引时的节点名称
* @param string $id   数字索引key转换为的属性名
* @return string
*/
function data_to_xml($data, $item='item', $id='id') {
$xml = $attr = '';
foreach ($data as $key => $val) {
if(is_numeric($key)){
$id && $attr = " {$id}=\"{$key}\"";
$key  = $item;
}
$xml    .=  "<{$key}{$attr}>";
$xml    .=  (is_array($val) || is_object($val)) ? data_to_xml($val, $item, $id) : $val;
$xml    .=  "</{$key}>";
}
return $xml;
}
// 这个是 对上面的一个 补充,处理其内部编码。
// 我最不喜欢这个了‘

// 今日继续  31日
/**
* session管理函数
* @param string|array $name session名称 如果为数组则表示进行session设置
* @param mixed $value session值
* @return mixed
*/
// 这个 太重要了
// 两个参数,一个是
/**
* php的session是可以程序恢复的,这个和java不太一样。session的恢复机制可以实现多个应用程序session的共享,因为php的session都是以文件形式或者数据库存储的。首先是session_id的获取是通过session_id()函数获取,这个值可以进行传递。
*/
function session($name='',$value='') {
$prefix   =  C('SESSION_PREFIX'); // 设置session 前缀:
if(is_array($name)) { // session初始化 在session_start 之前调用
// 如果是数组,可能意味着,批量化 赋值
if(isset($name['prefix'])) C('SESSION_PREFIX',$name['prefix']);
// 如果在传入的数组重新定义了 prefix  修改默认其前缀配置
if(C('VAR_SESSION_ID') && isset($_REQUEST[C('VAR_SESSION_ID')])){
session_id($_REQUEST[C('VAR_SESSION_ID')]);
// 获取 session_id
}elseif(isset($name['id'])) {
session_id($name['id']); // 如果在 数组中设置了ID 那么就有了
}
if('common' == APP_MODE){ // 其它模式可能不支持
ini_set('session.auto_start', 0);// 禁止其自动启动
}
// 处理 session 名字
if(isset($name['name']))            session_name($name['name']);
// 处理 session 路径
if(isset($name['path']))            session_save_path($name['path']);
// 处理 session 域名
if(isset($name['domain']))          ini_set('session.cookie_domain', $name['domain']);
// 处理其生成中秋
if(isset($name['expire']))          {
ini_set('session.gc_maxlifetime',   $name['expire']);
ini_set('session.cookie_lifetime',  $name['expire']);
}
// PHP中的session有效期默认是1440秒(24分钟)【weiweiok 注:php5里默认的是180分】,也就是说,客户端超过24分钟没有刷新,当前session就会失效。很明显,这是不能满足需要的。

if(isset($name['use_trans_sid']))   ini_set('session.use_trans_sid', $name['use_trans_sid']?1:0);// 设置这个
if(isset($name['use_cookies']))     ini_set('session.use_cookies', $name['use_cookies']?1:0);// 设置这个
if(isset($name['cache_limiter']))   session_cache_limiter($name['cache_limiter']);// 设置这个
if(isset($name['cache_expire']))    session_cache_expire($name['cache_expire']);// 设置这个
if(isset($name['type']))            C('SESSION_TYPE',$name['type']);// 设置这个
if(C('SESSION_TYPE')) { // 读取session驱动
$type   =   C('SESSION_TYPE');// 获取不同的session 驱动
$class  =   strpos($type,'\\')? $type : 'Think\\Session\\Driver\\'. ucwords(strtolower($type));
$hander =   new $class();
session_set_save_handler(
array(&$hander,"open"),
array(&$hander,"close"),
array(&$hander,"read"),
array(&$hander,"write"),
array(&$hander,"destroy"),
array(&$hander,"gc"));
}
// 启动session
if(C('SESSION_AUTO_START'))  session_start();// 开启session
// 如果是数组的话,这个配置项就多多了
}elseif('' === $value){ // 如果没有数值 默认就是读取
if(''===$name){
// 获取全部的session
return $prefix ? $_SESSION[$prefix] : $_SESSION; // 读取全部
}elseif(0===strpos($name,'[')) { // session 操作 // 特殊操作
if('[pause]'==$name){ // 暂停session
session_write_close();
}elseif('[start]'==$name){ // 启动session
session_start();
}elseif('[destroy]'==$name){ // 销毁session
$_SESSION =  array(); // 各种销毁啊
session_unset(); // 各种销毁啊
session_destroy(); // 各种销毁啊
}elseif('[regenerate]'==$name){ // 重新生成id
session_regenerate_id();
}
}elseif(0===strpos($name,'?')){ // 检查session 如果有 ?的话
$name   =  substr($name,1); //删掉问号
if(strpos($name,'.')){ // 支持数组
list($name1,$name2) =   explode('.',$name); // 分析其代码
return $prefix?isset($_SESSION[$prefix][$name1][$name2]):isset($_SESSION[$name1][$name2]);
}else{
return $prefix?isset($_SESSION[$prefix][$name]):isset($_SESSION[$name]);
}
// 返回其 数组 如 company.nanme $_SESSION[$prefix][company][nanme]
}elseif(is_null($name)){ // 清空session // 如果传入的参数 为null
// 清空
if($prefix) {
unset($_SESSION[$prefix]);
}else{
$_SESSION = array();
}
}elseif($prefix){ // 获取session 跟上面的很相近
if(strpos($name,'.')){
list($name1,$name2) =   explode('.',$name);
return isset($_SESSION[$prefix][$name1][$name2])?$_SESSION[$prefix][$name1][$name2]:null;
}else{
return isset($_SESSION[$prefix][$name])?$_SESSION[$prefix][$name]:null;
}
}else{// 默认返回二维数组
if(strpos($name,'.')){
list($name1,$name2) =   explode('.',$name);
return isset($_SESSION[$name1][$name2])?$_SESSION[$name1][$name2]:null;
}else{
return isset($_SESSION[$name])?$_SESSION[$name]:null;
}
}
}elseif(is_null($value)){ // 删除session
if(strpos($name,'.')){ // 二维数组删除
list($name1,$name2) =   explode('.',$name);
if($prefix){
unset($_SESSION[$prefix][$name1][$name2]);
}else{
unset($_SESSION[$name1][$name2]);
}
}else{ // 普通删除
if($prefix){
unset($_SESSION[$prefix][$name]);
}else{
unset($_SESSION[$name]);
}
}
}else{ // 设置session
if(strpos($name,'.')){ // 设置
list($name1,$name2) =   explode('.',$name);
if($prefix){
$_SESSION[$prefix][$name1][$name2]   =  $value;
}else{
$_SESSION[$name1][$name2]  =  $value;
}
}else{// 设置
if($prefix){
$_SESSION[$prefix][$name]   =  $value;
}else{
$_SESSION[$name]  =  $value;
}
}
}
return null;
}
// 总结:这个真的很重要:
// 然发现没有使用起来感觉那么高大上
// 唯一的惊奇的地方,就是 其 数组参数,可以完全设置各种参数。

// 16 年 1号,嘿嘿,跨年了
/**
* Cookie 设置、获取、删除
* @param string $name cookie名称
* @param mixed $value cookie值
* @param mixed $option cookie参数
* @return mixed
*/
function cookie($name='', $value='', $option=null) {
// 默认设置
$config = array(
'prefix'    =>  C('COOKIE_PREFIX'), // cookie 名称前缀
'expire'    =>  C('COOKIE_EXPIRE'), // cookie 保存时间
'path'      =>  C('COOKIE_PATH'), // cookie 保存路径
'domain'    =>  C('COOKIE_DOMAIN'), // cookie 有效域名
'secure'    =>  C('COOKIE_SECURE'), //  cookie 启用安全传输
'httponly'  =>  C('COOKIE_HTTPONLY'), // httponly设置
); // 各种设置
// 参数设置(会覆盖黙认设置)
if (!is_null($option)) { // 非默认值,就开启
if (is_numeric($option))// 数字,直接就是过期时间
$option = array('expire' => $option);
elseif (is_string($option)) // 字符串,解析字符串
parse_str($option, $option);
// parse_str("name=Bill&age=60");
$config     = array_merge($config, array_change_key_case($option));// 合并设置
}
if(!empty($config['httponly'])){ // 默认仅仅支持 http 其的顾管
ini_set("session.cookie_httponly", 1);
}
// 清除指定前缀的所有cookie
if (is_null($name)) { // 清空全部
if (empty($_COOKIE))
return null;
// 要删除的cookie前缀,不指定则删除config设置的指定前缀
$prefix = empty($value) ? $config['prefix'] : $value; // 前缀
if (!empty($prefix)) {// 如果前缀为空字符串将不作处理直接返回
foreach ($_COOKIE as $key => $val) {
if (0 === stripos($key, $prefix)) {
setcookie($key, '', time() - 3600, $config['path'], $config['domain'],$config['secure'],$config['httponly']);
unset($_COOKIE[$key]);
}
}
}
return null;
}elseif('' === $name){
// 获取全部的cookie
return $_COOKIE;
}
$name = $config['prefix'] . str_replace('.', '_', $name); // 二维数组 变成字符串
if ('' === $value) {
if(isset($_COOKIE[$name])){
$value =    $_COOKIE[$name];
if(0===strpos($value,'think:')){
$value  =   substr($value,6);
return array_map('urldecode',json_decode(MAGIC_QUOTES_GPC?stripslashes($value):$value,true));
}else{
return $value;
}
}else{
return null;
}
} else { // 删除数值
if (is_null($value)) {
setcookie($name, '', time() - 3600, $config['path'], $config['domain'],$config['secure'],$config['httponly']);
unset($_COOKIE[$name]); // 删除指定cookie
} else {
// 设置cookie
if(is_array($value)){
$value  = 'think:'.json_encode(array_map('urlencode',$value));
}
$expire = !empty($config['expire']) ? time() + intval($config['expire']) : 0;
setcookie($name, $value, $expire, $config['path'], $config['domain'],$config['secure'],$config['httponly']);
$_COOKIE[$name] = $value;
}
}
return null;
}
// 总结:发现也没什么用 关键函数还是这个setcookie

// 16 年 2号
/**
* 加载动态扩展文件
* @var string $path 文件路径
* @return void
*/
function load_ext_file($path) {
// 加载自定义外部文件
if($files = C('LOAD_EXT_FILE')) { // 批量加载外部文件 ,指定路径下的外部文件
$files      =  explode(',',$files);
foreach ($files as $file){
$file   = $path.'Common/'.$file.'.php';
if(is_file($file)) include $file;
}
}
// 加载自定义的动态配置文件
if($configs = C('LOAD_EXT_CONFIG')) {// 加载配置文件 也是文件,不是配置选项
if(is_string($configs)) $configs =  explode(',',$configs);
foreach ($configs as $key=>$config){
$file   = is_file($config)? $config : $path.'Conf/'.$config.CONF_EXT;
if(is_file($file)) { // 并且动态加载到配置的选项
is_numeric($key)?C(load_config($file)):C($key,load_config($file));
}
}
}
}
// 总结,没什么的,就是个批量加载文件,或者配置选项而已了。

// 16年 3号
/**
* 获取客户端IP地址
* @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
* @param boolean $adv 是否进行高级模式获取(有可能被伪装)
* @return mixed
*/
function get_client_ip($type = 0,$adv=false) {
$type       =  $type ? 1 : 0; // 个人感觉这个写的太无良了,毛用没有啊,不够处理了个 函数的强制转换数值
static $ip  =   NULL; // 默认返回IP地址 非 IPV4数字
if ($ip !== NULL) return $ip[$type];// 如果已经有了,就直接返回
if($adv){ // 是否高级默认,默认非高级模式,去掉伪装
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
// 不同形势下的IP 获得
$arr    =   explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$pos    =   array_search('unknown',$arr);
if(false !== $pos) unset($arr[$pos]);
$ip     =   trim($arr[0]);
}elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
$ip     =   $_SERVER['HTTP_CLIENT_IP'];
}elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ip     =   $_SERVER['REMOTE_ADDR'];
}
}elseif (isset($_SERVER['REMOTE_ADDR'])) { // 不检测伪装
$ip     =   $_SERVER['REMOTE_ADDR'];
}
// IP地址合法验证
$long = sprintf("%u",ip2long($ip));// 检测是否合格IP
$ip   = $long ? array($ip, $long) : array('0.0.0.0', 0); // 合格就返回获取的数据,否则是0
return $ip[$type];
}
// 总结,其实这个函数没有那么神秘,就是返回,并且自己同时处理好了 ip 跟数字,需要就个返回那个
// ip 转数字的函数 是ip2long

/**
* 发送HTTP状态
* @param integer $code 状态码
* @return void
*/
// 发送状态码
function send_http_status($code) {
static $_status = array(
// Informational 1xx
100 => 'Continue',
101 => 'Switching Protocols',
// Success 2xx
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
// Redirection 3xx
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Moved Temporarily ',  // 1.1
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
// 306 is deprecated but reserved
307 => 'Temporary Redirect',
// Client Error 4xx
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
// Server Error 5xx
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported',
509 => 'Bandwidth Limit Exceeded'
);
if(isset($_status[$code])) { // 两种模式下发送
header('HTTP/1.1 '.$code.' '.$_status[$code]);
// 确保FastCGI模式下正常
header('Status:'.$code.' '.$_status[$code]);
}
}

// 查询过滤
function think_filter(&$value){
// TODO 其他安全过滤

// 过滤查询特殊字符
if(preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i',$value)){
$value .= ' ';// 如果匹配了,加一个空格,感觉什么用的没有呢
}
}

// 不区分大小写的in_array实现
// 直接转换为小写,就不用区分大小写了
function in_array_case($value,$array){
return in_array(strtolower($value),array_map('strtolower',$array));
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  thinkphp php 注释 源码 it