0

0

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

php中文网

php中文网

发布时间:2016-07-28 08:25:53

|

1172人浏览过

|

来源于php中文网

原创

Regie.ai
Regie.ai

一个使用AI生成产品描述的网络平台

下载
<?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 ThinkController|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 ThinkHook::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 ThinkModel
 */functionD($name='',$layer='') {if(empty($name)) returnnew ThinkModel; // 如果输入参数为空,直接返回默认的 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 ThinkModel($name);
    }else { // 否则的日志记录错误 实例化一个基础的类 给 返回回去
        ThinkLog::record('D方法实例化没找到模型类'.$class,ThinkLog::NOTICE);
        $model      =   new ThinkModel(basename($name));
    }
    $_model[$name.$layer]  =  $model; // 存入历史记录return$model;// 返回当期实例化的类  3中方式进行的实例化
}
// 抛出异常 基本上就是个封装了 直接转的  但是在他的核心代码里面 也没什么东西了。// 仅仅是 继承了 php 默认的异常类/**
 * 抛出异常处理
 * @param string $msg 异常消息
 * @param integer $code 异常代码 默认为0
 * @throws ThinkException
 * @return void
 */functionE($msg, $code=0) {thrownew ThinkException($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 ThinkStorage::unlink($filename,'F'); // 删除数据文件
            }
        } else {
            ThinkStorage::put($filename,serialize($value),'F'); // 用序列化的方式 写入文件// 缓存数据$_cache[$name]  =   $value; // 并且写入缓存returnnull;
        }
    }
    // 获取缓存数据if (isset($_cache[$name])) // 跟其 通用 C 很像啊 ,return$_cache[$name];
    if (ThinkStorage::has($filename,'F')){ // 读取 存在的文件$value      =   unserialize(ThinkStorage::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, ',', ' ');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输入过滤函数,支持默认值/**
 * 获取输入参数 支持过滤和默认值
 * 使用方法:
 * <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
 */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 ThinkModel
 *///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]; // 这个不多说了,就这样了。
}
/**
 * 设置和获取统计数据
 * 使用方法:
 * <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
 */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."<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
 */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      =   ThinkCache::getInstance($type,$options);
    }elseif(is_array($name)) { // 缓存初始化 // 如果缓存 参数 其实有点乱 type 可以放到任何一个位置$type       =   isset($name['type'])?$name['type']:'';
        $cache      =   ThinkCache::getInstance($type,$name);
        return$cache;
    }elseif(empty($cache)) { // 自动初始化 还没有的话$cache      =   ThinkCache::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速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

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

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

705

2026.02.13

微博网页版主页入口与登录指南_官方网页端快速访问方法
微博网页版主页入口与登录指南_官方网页端快速访问方法

本专题系统整理微博网页版官方入口及网页端登录方式,涵盖首页直达地址、账号登录流程与常见访问问题说明,帮助用户快速找到微博官网主页,实现便捷、安全的网页端登录与内容浏览体验。

233

2026.02.13

Flutter跨平台开发与状态管理实战
Flutter跨平台开发与状态管理实战

本专题围绕Flutter框架展开,系统讲解跨平台UI构建原理与状态管理方案。内容涵盖Widget生命周期、路由管理、Provider与Bloc状态管理模式、网络请求封装及性能优化技巧。通过实战项目演示,帮助开发者构建流畅、可维护的跨平台移动应用。

117

2026.02.13

TypeScript工程化开发与Vite构建优化实践
TypeScript工程化开发与Vite构建优化实践

本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。

22

2026.02.13

Redis高可用架构与分布式缓存实战
Redis高可用架构与分布式缓存实战

本专题围绕 Redis 在高并发系统中的应用展开,系统讲解主从复制、哨兵机制、Cluster 集群模式及数据分片原理。内容涵盖缓存穿透与雪崩解决方案、分布式锁实现、热点数据优化及持久化策略。通过真实业务场景演示,帮助开发者构建高可用、可扩展的分布式缓存系统。

61

2026.02.13

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

30

2026.02.12

雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法
雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法

本专题系统整理雨课堂网页版官方入口及在线登录方式,涵盖账号登录流程、官方直连入口及平台访问方法说明,帮助师生用户快速进入雨课堂在线教学平台,实现便捷、高效的课程学习与教学管理体验。

15

2026.02.12

豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法
豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法

本专题汇总豆包AI官方网页版入口及在线使用方式,涵盖智能写作工具、图片生成体验入口和官网登录方法,帮助用户快速直达豆包AI平台,高效完成文本创作与AI生图任务,实现便捷智能创作体验。

669

2026.02.12

PostgreSQL性能优化与索引调优实战
PostgreSQL性能优化与索引调优实战

本专题面向后端开发与数据库工程师,深入讲解 PostgreSQL 查询优化原理与索引机制。内容包括执行计划分析、常见索引类型对比、慢查询优化策略、事务隔离级别以及高并发场景下的性能调优技巧。通过实战案例解析,帮助开发者提升数据库响应速度与系统稳定性。

58

2026.02.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP函数之array数组函数视频讲解
PHP函数之array数组函数视频讲解

共76课时 | 26.2万人学习

深入剖析redis教程
深入剖析redis教程

共55课时 | 8.2万人学习

Redis中文开发手册
Redis中文开发手册

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号