[李景山php]thinkphp核心源码注释|functionsphp
程序员文章站
2024-01-03 10:42:40
...
// +----------------------------------------------------------------------// | 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
*声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。