PHP8.1.21版本已发布
vue8.1.21版本已发布
jquery8.1.21版本已发布

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

原创
2016-07-28 08:25:53 679浏览

// +----------------------------------------------------------------------// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]// +----------------------------------------------------------------------// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.// +----------------------------------------------------------------------// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )// +----------------------------------------------------------------------// | Author: liu21st // +----------------------------------------------------------------------/**
 * Think 系统函数库
 */// 同学们,上节课,我们已经 完成了 thinkphp 各种预定义变量的 定义// 重点可以分成以下几点:// 第一:对于 需要 web 加载的 也就是项目文档 thinkphp 采取的 dirname 的方式进行的组合跟加载// 其特点是 ////// 这样的斜杠// 对于 需要引入的文件 也就是 thinkphp 核心框架部分 其加载方式 采取了 __DIR__的方式进行加载// 其特点是 \\\\ 这样的反斜杠// ps 当然这些都是基于 window 下的方向, 也就分成了 两个 路径的 始祖 app_PATH  跟 think_PATH// 第二:我们可以进行记录的事情是// 作为一个框架程序,需要有能力记录 该脚本执行的 时间 跟 内存的消耗,所以 就毫不犹豫的开启了 mirctime 跟 memory_get_usage// 此刻作为 时间 跟 内存的起点。// 第三:值得我们注意的地方是:// 使用了 const 跟 define 定义了 系统常量,但是 感觉就是,必须一成不变的,用了 const// 就代表这个,彻底就固化死了,define 是可以让用户 在创建 自己的 app 中 进行修改的。 在系统进行定义之前,会判读是否定义,// const 是没有 办法重新定义的// 总结 基本没什么区别,这两个, 唯一就是用法啊,编译上的一个区别!// 第四:对 系统预定义变量[GPC] 跟 文本 数据流// 基本上可以说是进行 非转义处理 么有 addsalshe 之类的// 第五:判读了 php 跟 web服务器 的 通信方式 cgi// 判读了操作系统// 判读 了 是否 脱离服务器运行 的命令行工具// 第六: 针对于 ROOT 跟 _FILE_ 文件的定义 不统一,重新进行了多平台定义,增强了平台的可以移植性。// 总之:就是 规范了定义 以及其 跨平台特性!// 接下来我们讲针对与 functions.php 这些 公共函数 为大家进行讲解 20151205/**
 * 实例化多层控制器 格式:[资源://][模块/]控制器
 * @param string $name 资源地址
 * @param string $layer 控制层名称
 * @param integer $level 控制器层次
 * @return Think\Controller|false
 */// 此函数 进行 多层控制器实例化 功能 方便其内部调用,在写 app 应用的时候比较少用。functionA($name,$layer='',$level=0) {static$_action = array();// 此处定义静态化 存储数组 为其实现 单列实例化模式$layer  =   $layer? : C('DEFAULT_C_LAYER'); //'DEFAULT_C_LAYER'       =>  'Controller', // 默认的控制器层名称$level  =   $level? : ($layer == C('DEFAULT_C_LAYER')?C('CONTROLLER_LEVEL'):1); //    'CONTROLLER_LEVEL'      =>  1,if(isset($_action[$name.$layer]))// 根据传入的控制器 以及其对应的层级 默认:Controller 1 层级 返回return$_action[$name.$layer];

    $class  =   parse_res_name($name,$layer,$level); // 根据其传入的控制器 名称 层级 类名 获取对应的 class 名称if(class_exists($class)) { // 如果说 根据上述的生成 class 名称 如果存在 就进行实例化$action             =   new$class(); // 实例化$_action[$name.$layer]     =   $action;// 存放 实例化对象到静态数组中return$action;// 返回实例化 情况
    }else {
        returnfalse;
    }
    // 例如: $name = 'admin'  结果就是 $class  = AdminController.class.php 文件 下的 AdiminController 类。
}
// 总结: 其实这个,就是根据你传入的 $name 返回 不同的 实例化对象。$name 可以存在的选项为:// A('[项目://][分组/]模块','控制器层名称')   目前感觉这个level 基本上用不到。// 等待拯救// 好的,同学们我们今天继续,昨天了解A函数,其实就是一个 根据不同参数去实例化不同 控制器类的 一个功能函数// 注意 A函数中 加入了一个 把不同输入参数 转换的 对应类的名称跟位置// 接下来我们来看一下 B 函数的功能/**
 * 执行某个行为
 * @param string $name 行为名称
 * @param string $tag 标签名称(行为类无需传入)
 * @param Mixed $params 传入的参数
 * @return void
 */functionB($name, $tag='',&$params=NULL) {if(''==$tag){
        $name   .=  'Behavior';
    }
    return \Think\Hook::exec($name,$tag,$params);
}
// 从字面意义上来说,这个是个 执行某个行为的函数,// 如果 没有对应的 标签,也就是 默认的行为就是 找到钩子函数进行执行// 另外注意一点 就是其 $params 其实是一个 引入传值,并不是一个 普通的复制传值,这样,可以无需返回就改变了传入的参数。// 根据其 钩子函数的 特殊情况,一般其配置在 Addons 下面// 默认是 $name Behavior 联合// 默认的执行函数是run// 文件位置 "Addons\\{$name}\\{$name}Addon";// $class   =  $name.'Behavior';// $tag    =   'run';// return $addon->$tag($params);// 总结,其实B函数,就是执行插件【内部/外部】的两种,引入插件的开始位置。执行开始函数。// return $class->run(参数);// 下面继续我们的学习,这个C函数,是一个非常常用的函数,如果说AB我们可以一般的略过,这个就要我们仔细研究一下啦///**
 * 获取和设置配置参数 支持批量定义
 * @param string|array $name 配置变量
 * @param mixed $value 配置值
 * @param mixed $default 默认值
 * @return mixed
 */functionC($name=null, $value=null,$default=null) {// 定义 初始化容器 ,仅能一次初始化的static$_config = array();// 经典的静态全局变量注册,执行单一流程时有效,其实,对于多页面不同加载的话,效果不明显。是一个可以优化的地方。// 无参数时获取所有  情况1if (empty($name)) { // 这个是一个大招,也就是,当调用 C()的时候,注意,内部为空的时候, 就把你全家的都返回出去了。return$_config;
    }
    // 优先执行设置获取或赋值 情况 2if (is_string($name)) {  // 如果 是个字符串,也不下面数组的形式if (!strpos($name, '.')) { // 此处可以记作 2.1 如果 没有 连接符号,这个我觉得有点多次一举了,但是 是为了兼容数组的保存形式。老刘啊,你真的不容易啊。$name = strtoupper($name); // 不关什么 字母,统统大写,这个其实是兼容的一个好的处理方式,同学们可以借鉴哦!if (is_null($value)) // 这里其实 是可以分的 此处记作2.1.1returnisset($_config[$name]) ? $_config[$name] : $default; // 此处的三元,真的很高明, 可以分成 2.1.1.1 跟 2.1.1.2$_config[$name] = $value; // 此处记作 2.1.2 你懂了吗returnnull; //这些是各种中条件细分// 总结就是 C('name','zhangsan'); 就是赋值 name 为张三// 如果 $name = C('name') 就是读取 name的赋值,如果刚刚执行过上面的语句的话// 那么 $name 就是 张三了
        }
        // 二维数组设置和获取支持$name = explode('.', $name); // 这里仅仅是添加了 二维数组的支持 这里有个问题,就是 二维数组的 子元素没有变成大写$name[0]   =  strtoupper($name[0]);
        if (is_null($value))
            returnisset($_config[$name[0]][$name[1]]) ? $_config[$name[0]][$name[1]] : $default;
        $_config[$name[0]][$name[1]] = $value;
        returnnull;
    }
    // 批量设置  情况3  直接合并数据了 其实并不很常用,原因是容易搞晕,对于我这种小智商的人,就算了,不过,偶尔会用一下。if (is_array($name)){
        $_config = array_merge($_config, array_change_key_case($name,CASE_UPPER));
        returnnull;
    }
    // 其它 情况returnnull; // 避免非法参数
}
// 好的,感谢同学们,我们下节课继续!// 其实上节课程中我们讲到C函数,这里有一思路,就函数尽量不要收到配置文件的限制,// 我们今天继续D函数,这个函数在 thinkphp的使用中,是贯穿始终的。// 这个是一个 实例化 Model 类的 函数/**
 * 实例化模型类 格式 [资源://][模块/]模型
 * @param string $name 资源地址
 * @param string $layer 模型层名称
 * @return Think\Model
 */functionD($name='',$layer='') {if(empty($name)) returnnew Think\Model; // 如果输入参数为空,直接返回默认的 Modelstatic$_model  =   array();    // 否则就可以建立 静态 实例化仓库$layer          =   $layer? : C('DEFAULT_M_LAYER'); // 这里进行默认层的确认,就是if(isset($_model[$name.$layer]))    // 同样的道理 存在就返回,其实就是单列的应用思想return$_model[$name.$layer];
    $class          =   parse_res_name($name,$layer); //通过解析 获取到对应的 类名 这个函数 是包含导入文件功能的,牛叉吧if(class_exists($class)) {  // 如果存在 就直接加载 并且实例化$model      =   new$class(basename($name));
    }elseif(false === strpos($name,'//m.sbmmt.com/m/')){ // 如果说没有找到类文件 也就是没有找到类// 自动加载公共模块下面的模型if(!C('APP_USE_NAMESPACE')){ // 就去 公共模型下面寻找, 如果没有指定公共模型
            import('Common/'.$layer.'//m.sbmmt.com/m/'.$class); // 默认公共模型存放位置
        }else{
            $class      =   '\\Common\\'.$layer.'\\'.$name.$layer;// 实在不行就去实例化 默认的类了
        }
        $model      =   class_exists($class)? new$class($name) : new Think\Model($name);
    }else { // 否则的日志记录错误 实例化一个基础的类 给 返回回去
        Think\Log::record('D方法实例化没找到模型类'.$class,Think\Log::NOTICE);
        $model      =   new Think\Model(basename($name));
    }
    $_model[$name.$layer]  =  $model; // 存入历史记录return$model;// 返回当期实例化的类  3中方式进行的实例化
}
// 抛出异常 基本上就是个封装了 直接转的  但是在他的核心代码里面 也没什么东西了。// 仅仅是 继承了 php 默认的异常类/**
 * 抛出异常处理
 * @param string $msg 异常消息
 * @param integer $code 异常代码 默认为0
 * @throws Think\Exception
 * @return void
 */functionE($msg, $code=0) {thrownew Think\Exception($msg, $code);
}
//  这个是一通过文件进行快速 数据 保存跟读取操作的事情。/**
 * 快速文件数据读取和保存 针对简单类型数据 字符串、数组
 * @param string $name 缓存名称
 * @param mixed $value 缓存值
 * @param string $path 缓存路径
 * @return mixed
 */functionF($name, $value='', $path=DATA_PATH) {static$_cache  =   array(); // 老一套啊,看起来用的很顺手啊,$filename       =   $path . $name . '.php'; // 文件目录,也很简单。 直接使用的php 文件if ('' !== $value) { // 如果有数值if (is_null($value)) { // 如果存在的数值为空的话// 删除缓存if(false !== strpos($name,'*')){ // 如果保存的对象中中存在 * 号,错误returnfalse; // TODO
            }else{
                unset($_cache[$name]);// 删除数据缓存return Think\Storage::unlink($filename,'F'); // 删除数据文件
            }
        } else {
            Think\Storage::put($filename,serialize($value),'F'); // 用序列化的方式 写入文件// 缓存数据$_cache[$name]  =   $value; // 并且写入缓存returnnull;
        }
    }
    // 获取缓存数据if (isset($_cache[$name])) // 跟其 通用 C 很像啊 ,return$_cache[$name];
    if (Think\Storage::has($filename,'F')){ // 读取 存在的文件$value      =   unserialize(Think\Storage::read($filename,'F'));
        $_cache[$name]  =   $value; // 返回数据
    } else {
        $value          =   false;
    }
    return$value; //返回数据
}
// 就是一个缓存数据的读取,跟 file 相比 差得多了// 好的, 各位同学,继续// 这里给大家提示一点,框架中的 叫做 functions.php 应用中的叫做 function.php// 大家 明白我此刻说的应用里面的位置吗?// 如果作为一个函数的注释来说,该函简洁明了/**
 * 记录和统计时间(微秒)和内存使用情况
 * 使用方法:
 * 
 * G('begin'); // 记录开始标记位
 * // ... 区间运行代码
 * G('end'); // 记录结束标签位
 * echo G('begin','end',6); // 统计区间运行时间 精确到小数后6位  时间 用数字
 * echo G('begin','end','m'); // 统计区间内存使用情况  内存用m表示
 * 如果end标记位没有定义,则会自动以当前作为标记位
 * 其中统计内存使用需要 MEMORY_LIMIT_ON 常量为true才有效
 * 
 * @param string $start 开始标签
 * @param string $end 结束标签
 * @param integer|string $dec 小数位或者m
 * @return mixed
 */// 这里不得不说 thinkphp 的创始人,特别喜欢干的一个事情,就是,根据输入参数的不同实现不同的意义// 如 C 函数 F 函数,都是 ,如果仅仅输入 单一参数 表示读取数字, 2 个参数表示 设定数值,3 个参数一般多加了默认值// number_format — 以千位分隔符方式格式化一个数字// $nombre_format_francais = number_format($number, 2, ',', ' ');functionG($start,$end='',$dec=4) {static$_info       =   array(); // 这个是时间仓库static$_mem        =   array(); // 这个是内存仓库if(is_float($end)) { // 记录时间  如果传值如此 G('start',2342353234.453); 就是个记录 跟上面的风格保持一致// 有 小数 传入 就是 结束$_info[$start]  =   $end; // 或者 如果传值如此 G('start',microtime(TRUE));
    }elseif(!empty($end)){ // 统计时间和内存使用  也就是其默认的优先级 是 时间// 有 非数字 结尾 就是 返回 差值if(!isset($_info[$end])) $_info[$end]       =  microtime(TRUE);
        if(MEMORY_LIMIT_ON && $dec=='m'){ // 如果开启了内存记录 并且明确是内存的记录if(!isset($_mem[$end])) $_mem[$end]     =  memory_get_usage(); // 获取内存记录return number_format(($_mem[$end]-$_mem[$start])/1024); // 获取返回的格式化数值
        }else{
            return number_format(($_info[$end]-$_info[$start]),$dec); // 返回格式化的位数 默认4位小数
        }

    }else{ // 记录时间和内存使用// 单独的话,就是同步记录 内存 跟时间的 标志位。$_info[$start]  =  microtime(TRUE);
        if(MEMORY_LIMIT_ON) $_mem[$start]           =  memory_get_usage();
    }
    returnnull;
}
// 无 H 函数// 今日上午面试,就到这里了,感谢!//  嗯,昨天有点匆忙,其实这个G就是一个记录时间 跟内存的函数,都过第二,第三个参数的属性//  进行区分 是记录的时间还是 其它什么的 ,但是不管怎么得瑟,都是 同时记录的时间 给内存//  通过时间跟内存的记录可以 从一个角度来反映出php 程序运行的性能// 接下来是我们强大的I输入过滤函数,支持默认值/**
 * 获取输入参数 支持过滤和默认值
 * 使用方法:
 * 
 * I('id',0); 获取id参数 自动判断get或者post // 嗯,你举例的这几个,确实很常用
 * I('post.name','','htmlspecialchars'); 获取$_POST['name']
 * I('get.'); 获取$_GET
 * 
 * @param string $name 变量的名称 支持指定类型
 * @param mixed $default 不存在的时候默认值
 * @param mixed $filter 参数过滤方法
 * @param mixed $datas 要获取的额外数据源
 * @return mixed
 */functionI($name,$default='',$filter=null,$datas=null) {// 第一步:指定仓库static$_PUT   =   null;  // 默认单数据仓库// 第二步:判定输入类型if(strpos($name,'//m.sbmmt.com/m/')){ // 指定修饰符list($name,$type)    =   explode('//m.sbmmt.com/m/',$name,2);
    }elseif(C('VAR_AUTO_STRING')){ // 默认强制转换为字符串// // 输入变量是否自动强制转换为字符串 如果开启则数组变量需要手动传入变量修饰符获取变量// 其实上面的 这个默认是false$type   =   's';
    }
    // 第三步:数据源获取// 第三步:第一小步骤:就是分解数据源// 在一般的程序中,上面这两个是用不到的,也就是 指定 数据类型, 默认都没有指定。if(strpos($name,'.')) { // 指定参数来源list($method,$name) =   explode('.',$name,2);
    }else{ // 默认为自动判断$method =   'param';
    }
    // 第三步:第二小步骤:关联数据源// 指定数据源,常用的就是 get post 了switch(strtolower($method)) { // 其实这个用的很经典 比较之前 先 小写case'get'     :
            $input =& $_GET; // 取地址 用的也不错,很有想法break;
        case'post'    :
            $input =& $_POST;
            break;
        case'put'     :
            if(is_null($_PUT)){
                parse_str(file_get_contents('php://input'), $_PUT);
            }
            $input 	=	$_PUT;
/*
读取POST数据
不能用于multipart/form-data类型
php://input VS $HTTP_RAW_POST_DATA
读取POST数据 */break;
        case'param'   :// 其实这个最不科学了,为了兼容懒人编程,switch($_SERVER['REQUEST_METHOD']) {
                case'POST':
                    $input  =  $_POST;
                    break;
                case'PUT':
                    if(is_null($_PUT)){
                        parse_str(file_get_contents('php://input'), $_PUT);
                    }
                    $input 	=	$_PUT;
                    break;
                default:
                    $input  =  $_GET;
            }
            break;
        // 常用的三种输入 获取方式 GET POST PUTcase'path'    : // 居然还有路径获取,我调用中从来没用过$input  =   array();
            if(!empty($_SERVER['PATH_INFO'])){
                $depr   =   C('URL_PATHINFO_DEPR');// 路径分隔符//'URL_PATHINFO_DEPR'     =>  '//m.sbmmt.com/m/',  // PATHINFO模式下,各参数之间的分割符号$input  =   explode($depr,trim($_SERVER['PATH_INFO'],$depr));
            }
            break;
        case'request' :
            $input =& $_REQUEST;
            break;
        case'session' :
            $input =& $_SESSION;
            break;
        case'cookie'  :
            $input =& $_COOKIE;
            break;
        case'server'  :
            $input =& $_SERVER;
            break;
        case'globals' :
            $input =& $GLOBALS;
            break;
        case'data'    :
            $input =& $datas;
            break;
        default:
            returnnull;
    }
    // 第四步:明确获取变量// 4.1 获取全部数值if(''==$name) { // 获取全部变量$data       =   $input;
        // 用过滤函数继续过滤$filters    =   isset($filter)?$filter:C('DEFAULT_FILTER');
        if($filters) {
            if(is_string($filters)){
                $filters    =   explode(',',$filters);
            }
            foreach($filtersas$filter){
                $data   =   array_map_recursive($filter,$data); // 参数过滤
            }
        }
        // 4.2  获取 指定数值
    }elseif(isset($input[$name])) { // 取值操作 如果明确一个 取值$data       =   $input[$name]; // 数据获取完成// 开始执行过滤$filters    =   isset($filter)?$filter:C('DEFAULT_FILTER');
        // 存在过滤器 开始过滤if($filters) {
            if(is_string($filters)){
                if(0 === strpos($filters,'//m.sbmmt.com/m/')){
                    if(1 !== preg_match($filters,(string)$data)){ // 过滤器支持正则// 支持正则验证returnisset($default) ? $default : null;
                    }
                }else{
                    $filters    =   explode(',',$filters);
                }
            }elseif(is_int($filters)){
                $filters    =   array($filters);
            }
            // 进行数组过滤if(is_array($filters)){
                foreach($filtersas$filter){
                    if(function_exists($filter)) {
                        $data   =   is_array($data) ? array_map_recursive($filter,$data) : $filter($data); // 参数过滤
                    }else{
                        $data   =   filter_var($data,is_int($filter) ? $filter : filter_id($filter));
                        if(false === $data) {
                            returnisset($default) ? $default : null;
                        }
                    }
                }
            }
        }
        // 对输出数据类型进行指定 默认 字符串if(!empty($type)){
            switch(strtolower($type)){
                case'a':   // 数组$data 	=	(array)$data;
                    break;
                case'd':   // 数字$data 	=	(int)$data;
                    break;
                case'f':   // 浮点$data 	=	(float)$data;
                    break;
                case'b':   // 布尔$data 	=	(boolean)$data;
                    break;
                case's':   // 字符串default:
                    $data   =   (string)$data;
            }
        }
        //4.3 获取 默认的 数值了
    }else{ // 变量默认值$data       =    isset($default)?$default:null;
    }
    // 最后在返回数据之前,在进行处理了,就是 如果是数组,就 执行 默认的过滤函数
    is_array($data) && array_walk_recursive($data,'think_filter');
    return$data;
}
// 总结,其实经过上述函数的分析大致可以这样学习的地方:// 第一:按步骤进行分支 代码书写 类似于 第一步: 1.1 1.2 第二步: 2.1 2.2 这样// 第二:依然贯穿了其传统,通过 参数 调整其输出的特色 就是各种参数的样式进行不同的兼容// 第三:就是 各种过滤函数的方便 搭配。真心不错!// 我看好你哦,哈哈!// 无 J函数// 无 K函数// 遇到 这个 L 函数 一般情况下就是 做的 语言配置。/**
 * 获取和设置语言定义(不区分大小写)
 * @param string|array $name 语言变量
 * @param mixed $value 语言值或者变量
 * @return mixed
 */functionL($name=null, $value=null) {static$_lang = array();// 老步调,定义仓库// 空参数返回所有定义// 三种方式// 第一种方式:为空if (empty($name)) // 老步调: 无输入 返回全部return$_lang;
    // 判断语言获取(或设置)// 若不存在,直接返回全大写$name// 如果 字符串// 第二种方式:字符串  然后在细分  空 数组 默认 记住这里的return 其实是个神器if (is_string($name)) { // 如果是字符串$name   =   strtoupper($name); // 第一步:统统转换成为大写if (is_null($value)){ // 判读 是 设置 还是读取returnisset($_lang[$name]) ? $_lang[$name] : $name; // 有定义返回定义,没有定义,直接返回
        }elseif(is_array($value)){ // 如果是数组// 支持变量$replace = array_keys($value); //返回包含数组中所有键名的一个新数组:foreach($replaceas &$v){ // 好复杂,这一节没看懂,嘿嘿 能看懂的在楼下回复哈!感谢$v = '{$'.$v.'}';
            }
            return str_replace($replace,$value,isset($_lang[$name]) ? $_lang[$name] : $name);
        }
        $_lang[$name] = $value; // 语言定义  否则就进行定义returnnull;
    }
    // 批量定义// 第三种方式:数组if (is_array($name)) // 批量 定义 array_change_key_case() 函数将数组的所有的键都转换为大写字母或小写字母。默认大写$_lang = array_merge($_lang, array_change_key_case($name, CASE_UPPER));
    returnnull;
}
// 特别常用的 一款 函数 不过 我稍后会  推荐D函数  但是任何函数,都有自己的 特点/**
 * 实例化一个没有模型文件的Model
 * @param string $name Model名称 支持指定基础模型 例如 MongoModel:User
 * @param string $tablePrefix 表前缀
 * @param mixed $connection 数据库连接信息
 * @return Think\Model
 *///functionM($name='', $tablePrefix='',$connection='') {static$_model  = array();// 一成不变的仓库if(strpos($name,':')) { // 可以组合其 代码 然后 拼接成为 类,跟 类名list($class,$name)    =  explode(':',$name);
    }else{
        $class      =   'Think\\Model'; // 否则的话,执行 默认的 Model 类 实例化
    }
    // 这个相当于做了一个唯一值$guid           =   (is_array($connection)?implode('',$connection):$connection).$tablePrefix . $name . '_' . $class;
    if (!isset($_model[$guid])) // 单列  单列$_model[$guid] = new$class($name,$tablePrefix,$connection); // 实例化保存后的单列return$_model[$guid]; // 这个不多说了,就这样了。
}
/**
 * 设置和获取统计数据
 * 使用方法:
 * 
 * N('db',1); // 记录数据库操作次数
 * N('read',1); // 记录读取次数
 * echo N('db'); // 获取当前页面数据库的所有操作次数
 * echo N('read'); // 获取当前页面读取次数
 * 
 * @param string $key 标识位置
 * @param integer $step 步进值
 * @param boolean $save 是否保存结果
 * @return mixed
 */functionN($key, $step=0,$save=false) {static$_num    = array(); // 仓库if (!isset($_num[$key])) { // 如果说没有设置 当前值$_num[$key] = (false !== $save)? S('N_'.$key) :  0; // 如果设置了存储 就在S 函数中,读取处理,否则就0了
    }
    if (empty($step)){ // 如果没有步进设置return$_num[$key];
    }else{ // 否则 按照步进 的方式前进$_num[$key] = $_num[$key] + (int)$step;
    }
    if(false !== $save){ // 保存结果  其实 这个是通过 缓存  读取 函数的。
        S('N_'.$key,$_num[$key],$save);
    }
    returnnull;
}
// 无 O函数// 无 P函数// 无 Q函数// 今日到此结束,讲述了 L M N 函数 语言包 M 实例化 可以 指定实例化类 跟 连接的数据库 N 记录步骤// 不好意思,糊涂了,昨天没有更新,今天也才更新/**
 * 远程调用控制器的操作方法 URL 参数格式 [资源://][模块/]控制器/操作
 * @param string $url 调用地址
 * @param string|array $vars 调用参数 支持字符串和数组
 * @param string $layer 要调用的控制层名称
 * @return mixed
 */// 把查询字符串解析到变量中// parse_str() 函数把查询字符串解析到变量中。// 注释:php.ini 文件中的 magic_quotes_gpc 设置影响该函数的输出。如果已启用,那么在 parse_str() 解析之前,变量会被 addslashes() 转换。/**
parse_str("name=Bill&age=60");
echo $name."
"; 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 */functionR($url,$vars=array(),$layer='') {$info = pathinfo($url); // 解析路径$action = $info['basename']; // 获取文件名$module = $info['dirname']; // 获取 文件路径$class = A($module,$layer); // 获取实际 class 实例化 多了一层级的 关系 如Widgetif($class){ // 如果存在 类if(is_string($vars)) { // 如果有变量 传入 parse_str($vars,$vars); // 解析传入参数到数组 } return call_user_func_array(array(&$class,$action.C('ACTION_SUFFIX')),$vars); // }else{ returnfalse; } } // 总结,其实 这个R 就是 一个call_user_func_array 的升级版本,通过 url 直接进行处理。// 还有一个半小时 今天结束。继续今天的学习// 这个函数 其实也是个很牛叉的函数 ,好像可以 用F函数,让我们对比一下吧,看看 这两个鬼有什么区别。/** * 缓存管理 * @param mixed $name 缓存名称,如果为数组表示进行缓存设置 * @param mixed $value 缓存值 * @param mixed $options 缓存参数 * @return mixed */functionS($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 */functionT($template='',$layer=''){// 解析模版资源地址 第一步:if(false === strpos($template,'://')){ $template = 'http://'.str_replace(':', '//m.sbmmt.com/m/',$template); } $info = parse_url($template); // 第二步:解析到自己的 数组里面$file = $info['host'].(isset($info['path'])?$info['path']:''); $module = isset($info['user'])?$info['user'].'//m.sbmmt.com/m/':MODULE_NAME.'//m.sbmmt.com/m/'; // 扩展用户名$extend = $info['scheme']; // 扩展 文件扩展名$layer = $layer?$layer:C('DEFAULT_V_LAYER'); // 层次// 获取当前主题的模版路径$auto = C('AUTOLOAD_NAMESPACE'); if($auto && isset($auto[$extend])){ // 扩展资源$baseUrl = $auto[$extend].$module.$layer.'//m.sbmmt.com/m/'; }elseif(C('VIEW_PATH')){ // 改变模块视图目录$baseUrl = C('VIEW_PATH'); }elseif(defined('TMPL_PATH')){ // 指定全局视图目录$baseUrl = TMPL_PATH.$module; }else{ $baseUrl = APP_PATH.$module.$layer.'//m.sbmmt.com/m/'; } // 获取主题$theme = substr_count($file,'//m.sbmmt.com/m/')2
? C('DEFAULT_THEME') : ''; // 分析模板文件规则$depr = C('TMPL_FILE_DEPR'); if('' == $file) { // 如果模板文件名为空 按照默认规则定位$file = CONTROLLER_NAME . $depr . ACTION_NAME; }elseif(false === strpos($file, '//m.sbmmt.com/m/')){ $file = CONTROLLER_NAME . $depr . $file; }elseif('//m.sbmmt.com/m/' != $depr){ $file = substr_count($file,'//m.sbmmt.com/m/')>1 ? substr_replace($file,$depr,strrpos($file,'//m.sbmmt.com/m/'),1) : str_replace('//m.sbmmt.com/m/', $depr, $file); } return$baseUrl.($theme?$theme.'//m.sbmmt.com/m/':'').$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'; */functionU($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_nameif(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'); //'//m.sbmmt.com/m/', // PATHINFO模式下,各参数之间的分割符号$urlCase = C('URL_CASE_INSENSITIVE'); //// 默认false 表示URL区分大小写 true则表示不区分大小写// 如果有 url 地址if($url) { if(0=== strpos($url,'//m.sbmmt.com/m/')) {// 定义路由 如果是跟目录$route = true; $url = substr($url,1); // 去掉第一个 斜杠if('//m.sbmmt.com/m/' != $depr) { // 换成系统的 指定的间隔符号$url = str_replace('//m.sbmmt.com/m/',$depr,$url); } }else{ // 也就是, 不是根目录的情况下if('//m.sbmmt.com/m/' != $depr) { // 安全替换$url = str_replace('//m.sbmmt.com/m/',$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_MODULEif(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__.'//m.sbmmt.com/m/'.rtrim($url,$depr); }else{ $module = (defined('BIND_MODULE') && BIND_MODULE==$module )? '' : $module; $url = __APP__.'//m.sbmmt.com/m/'.($module?$module.MODULE_PATHINFO_DEPR:'').implode($depr,array_reverse($var)); } if($urlCase){ // 转换$url = strtolower($url); } if(!empty($vars)) { // 添加参数 另外的一种解析方式而已foreach ($varsas$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 && '//m.sbmmt.com/m/' != substr($url,-1)){ $url .= '.'.ltrim($suffix,'.'); } } } if(isset($anchor)){ // 如果有锚点 组合上$url .= '//m.sbmmt.com/m/faq/#'.$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 */functionW($name, $data=array()) {return R($name,$data,'Widget'); } // 无 X函数// 无 Y函数// 无 Z函数// 26 字母 函数写完了,// A 函数中调用的 函数/** * 解析资源地址并导入类库文件 * 例如 module/controller addon://module/behavior *
声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。