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

<?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));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值