欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

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

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

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

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

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。