tp框架中的一些疑点知识-2
tp中有三种常量:
预定义常量, 这个设置后不会随环境的改变而改变的,比如 'URL_MODEL' => 1 注意是 model, 不是 url_mode
路径常量, 也不会随环境的改变而改变的, 比如 define('THINK_PATH', './ThinkPHP/'), define('LIB_PATH', THINK_PHP.'Library/');
系统常量, 这个会随着环境/文件的不同而不同的, 比如: 在模板中常用到的各种地址和名字等 : __ROOT__, __CONTROLLER__, __CONTROLLER_NAME__
等等
命名空间和根命名空间?
根命名空间, 有两个, 一个是 LIB_PATH下的各个子目录,和 APP_PATH下的各个模块目录; 是不是总共有三个根命名空间? ? 还包括: 网站的根目录??
相应的, 核心类的起始命名空间是LIB_PATH, 应用的模块的 "起始命名空间" 是 APP_PATH, 即应用所在目录.
- 定义类的时候, 指定namespace从 模块名写起, 不用反斜杠:
namespace Home\Controller;
- 要引用基类的时候, 从Think写起, 不用反斜杠:
use Think\Controller;
如果不用use, 可以直接写继承基类:
class FooController extends Think\Controller { ...}
- 凡是要在函数中, new/初始化一个对象, 要用反斜杠 开头, 比如:
new \Think\Foo();
要使用 php这种语言本身内置的类, 或 第三方没有使用命名空间的类/类库, 要从 根命名空间, 网站的根路径 \ (这时候的 起始命名空间就是网站的根路径
) 开始写起?? 比如: $class= new \stdClass(); $xml= new \SimpleXmlElement($xmlStr);
tp的配置文件
- 全局函数 C 支持类似三元运算符的形式, 如果没有设置该配置名称, 则取默认值:
$name = C('user_name', null, 'anonymous_user');
- C 函数支持二维配置, 比如:
C('USER_CONFIG.USER_TYPE');
配置文件加载的顺序?
惯例-> 应用公共配置 -> 模式(这个一般不用)->模块配置->调试-> 模块的扩展配置 -> 动态配置.
动态配置支持 批量配置. 即 C($multi_config) 多个配置项组成的数组
默认情况下, 应用下面的子目录模块 , 都可以访问(当然除了 Common, Runtime模块外), 你可以通过设置:
MODULE_ALLOW_LIST, 或 MODULE_DENY_LIST, 或 MULTI_MODULE =>false, DEFAULT_MODULE => 'Home' 等来设置
公共模块 Common 可以通过设置 ' define('COMMON_PATH', ....)` 来设置, 以增加安全,实际上, 一般都没有必要.
因为, 你要注意, 如果公共模块没有设置在Application下, 那么就不会创建父目录Common, 即 公共函数目录和公共配置目录将会散放在某个目录中,
即Common和Conf将没有父目录Common.
你可以同时绑定模块和控制器, 这时访问时就可以省略(而且必须省略, 不省略会报错"非法的操作! "!! )模块和控制器名称, 直接写操作名称.
因为一个入口文件, 同时只能创建一个模块, 因此, 要创建多个模块, 要么你使用多个入口(每个入口对应一个模块), 要么你要对一个入口文件进行修改...
### 一直搞不懂tp的url_case_insensitive是怎样的, 好像只是支持 "首字母不区分大小写", 好像并不支持模块控制器的完全的大小写混合: 只是支持: 模块和控制器首字母可以是大写或小写, 但是中间字母必须是小写, 操作的名称可以是任意的大小写混合. 而实际上, 用户在访问网站的时候, 也是通过页面内的导航链
接, 而不是通过地址栏输入的, 所以, 只要在代码内严格区分大小写就是了, 事实上, 在linux中, 严格区分大小写也是一种基本常识.
对于复杂的应用, 也只要三个模块: Home, Admin(用于后台文章/数据管理) , User(用户信息/数据管理) 就可以了.一般的应用, 比如企业宣传网站, 只要一个单模
块 Home 就足够了.
### 多个模块之间的链接/跳转 U方法???? 1. 使用 跨控制器/跨模块/跨应用的调用函数是A或R函数 1. 链接方法是:.... 调用 U方法! - U方法 只能支持在 同一个应用/同一个项目内的 地址链接跳转( 可以支持 跨模块/ 跨控制器/跨操作的 地址链接跳转), 不能跨域支持. 所谓地址表达式 后面的 `@ 域名` 也只是域名的别名, 就是要求这两个域名必须指向同一个应用. - U方法 共有四个参数, ``第一个是地址表达式, 第二个是通过url地址所传参数, 第三个是 '是否获取为静态后缀名', 第四个是 '是否显示域名', 就是 url地址前面的 `http://domain/` - U方法等全局函数, 可以在任何地方都可以使用, 可以在 控制器/操作方法中使用, 也可以在 模板.html文件中使用.. - 模板中使用的变量? 要注意 , 模板文件是html文件, 不是php文件! 所以 你不能使用 `$变量`, 也不能使用 `类似define这样的常量`, 所以 你要使用的 _ _ TempName_ _ 这种形式的东西, 叫做 "模板常量"
if(isset($anchor)){
$url .= '#'.$anchor;
}
if($domain) {
$url = (is_ssl()?'https://':'http://').$domain.$url;
}
return $url;
### 关于模板常量?? 在tp 3.2.3中的 TMPL_PARSE_STRING?
----------------------
tp 自动创建多个控制器和模型?
模块, 控制器, 模型等 可以自己手动创建, 也可以用代码自动创建, 在事先规划好了之后, 用这种自动生成的方式还是很方便的
define('BIND_MODULE', 'User');
define('BUILD_CONTROLLER_LIST', 'UserLogReg,UserManage'); // 这个逗号后, 不能有空格, 否则, 空格会作为控制器名称的一部分
define('BUILD_MODEL_LIST', 'UserLogModel,UserManageModel');
或者, 直接调用 Think下的Build类方法: 注意buildController 和 BUILD_CONTROLLER_LIST的区别: 后者是一个整体的字符串, (因为默认
隐含就知道了是哪个模块), 而前者 必须指明 模块名和控制器名称$module, $controller
, 所以 一次调用只能生成一个控制器.
require THINK_PATH.'ThinkPHP.php';
/* 下面的内容要放在 框架入口文件 ThinkPHP.php 的最后! */
\Think\Build::buildController('User', 'UserLogReg'); // 默认的Index控制器不需要创建
\Think\Build::buildController('User', 'UserManage');
\Think\Build::buildModel( 'User', 'UserLogModel');
### 注意, thinkphp下面的类的 函数名称, 第一个单词的首字母 是小写的, 然后如果有后面单词的, 首字母才大写, 以 IndexController类的 index方法为记住
.
控制器和模型的理解?
控制器, 是对 相同或相关 对象/事物 的 一些/一系列的 相关操作的集合. 因此, 操作相当于函数, 操作用函数来实现, 那么将一系列相关函数 进行封装的,
就是类.
所以控制器是 类. (一大类 相关操作 的集合)
模型, 是表, 因为在tp开发中, 只有一个数据库, 数据库下面是表, 要对表进行相关操作, 操作是函数, 所以 表就是 Model模型类, Model模型类封装的函数, 就是
对表进行 操作的 一系列相关函数.
is_file和 is_dir等文件操作函数?
// 创建控制器类
static public function buildController($module,$controller='Index') {
$file = APP_PATH.$module.'/Controller/'.$controller.'Controller'.EXT;
if(!is_file($file)){
$content = str_replace(array('[MODULE]','[CONTROLLER]'),array($module,$controller),self::$controller);
if(!C('APP_USE_NAMESPACE')){
$content = preg_replace('/namespace\s(.*?);/','',$content,1);
}
$dir = dirname($file);
// is_file()和is_dir()需要参数表示的文件/目录要存在而且有效,才为真,所以大多数的时候,是用来判断文件或目录是否存在的!
// 相当于使用 file_exists/dir_exists只是没有这样的函数
// 创建目录的函数 是:mkdir, 不是makedir...
if(!is_dir($dir)){
mkdir($dir, 0755, true);
}
// file_put_contents()函数,相当于fopen + fwrite +fclose, 所以肯定愿意使用这个!
file_put_contents($file,$content);
}
}
clearstatcache()函数的目的是?
- 这个主要是用在对文件的操作上, 因为对文件的操作函数,很多函数都对结果进行了缓存,而当对文件进行了**修改/写入/删除等操作后,函数的操作结果就要改变
了,但是下一次执行同样的操作时取出的是之前缓存的结果,这个就不对了,所以需要在 编辑/修改/删除文件等操作后, 要对之前的 缓存结果进行 清除,这样才能得
到真实的结果.
clearstatcache(): 删除文件缓存信息 , 在很多文件操作函数之后,要进行这个操作, 否则再次得到的结果可能会出错.
static和访问符public/private/protected的先后位置可以是 任意的,这个在很多地方都看到的。
除了case语句外, 通常在编程的结构化语句中, 都不推荐直接使用"常量,数值型的常量",而是将常量/常数值等赋值给变量,要充分使用变量, 要深刻理解变量
的作用,变量的作用主要就是用来接收常量常数的赋值的, 在结构化语句中尽量使用变量,这样以后修改起来,只需要给变量赋予不同的值就好了, 也可以在函数中传
入不同的数值给参数变量, 这样就不必修改后面的程序代码了.
要注意,dirname($file),获取的目录字符串, 最后面是没有斜杠的,所以如果后面要连接子目录的话, 要在子路经的前面加上 斜杠/,否则前面的字符串
就会和后面的子目录字符串连接在一起。 比如:$dir = dirname($file) . '/test/'
如果test前面没有斜杠的话,就成了wwwtest/目录,而不是 www/test/
URL_MODEL的问题 如果你的应用下面的模块都采用统一的url模式(模型,是URL_MODEL),就可以在应用项目的公共配置文件中设置url模式,如果不同的模块需
要设置不同的url模式,就应该在模块配置中设置url模式。
- 即使是 pathinfo的url模式, 也支持问号 get传参的格式
- pathinfo模式中, 分隔符可以定制:
'URL_PATHINFO_DEPR' => '-'
要搞清楚 convention.php配置 文件中一些配置项的作用和含义是什么?
- 比如: VAR_MODULE, VAR_CONTROLLER, ... 是指在pathinfo的 url_model中, 为了获取url地址中的 模块值, 控制器和操作的值, 需要用什么变量名 来获取
, 即在 程序中通过超全局变量 $module = $_GET['VAR_MODULE => 所对应的配置值, 默认的是m']
来取得模块名...
- 又比如: 'DEFAULT_C_LAYER' => 'Controller', 'DEFAULT_M_LAYER' => 'Model' 是为了解决 复杂应用中, 使用多层设计时, 比如数据层 都是从 Model继承,
但是分成 UserModel, UserLogic, UserService三层. 这三层的操作都是类似的, 都是 用 D(...)函数来初始化的. 那么 这三层究竟哪一层是默认的Model层呢? 因
为默认的模型层, 用D('User') 来初始化, 其他层就要加上 层的名字, 比如: D('User', 'Logic'), 或 D('User', 'Service');
- 说到tp的多层设计, 视图层View的多层设计是通过 目录来实现的, 比如 根据主题Theme/Default+Blue等来实现分层; 而控制器层, 是通过 业务控制器和
事件控制器等来实现分层的, 而模型类的分层也是类似的....
- **但是 , 要注意 ,在 VAR_MODULE, VAR_CONTROLLER, VAR_ACTION, 三个变量中, VAR_MODULE 变量 必须在 应用配置文件, 即
Application/Common/Conf/config.php 中配置, 其他两个变量 可以 在 模块配置文件中配置.**
在多个控制器, 模块, 项目/应用 之间的协同操作: 当然在同一个控制器内部的函数, 是可以随意的互相调用的, 因为一个控制器(就是类) 内部的任何函数(
即使是private) 都是相互可见的, 但是要在一个控制器内的某个操作函数中, 调用另一个控制器的方法, 或者 调用 另一个模块中(比如前台或后台模块之间的) 的
控制器方法, 甚至调用 另一个/其他 应用的方法. tp提供了A方法和 R方法, 就是用来实现这个母的的.
- A方法和R 方法是实现 跨 控制器/跨模块/跨应用之间的 方法调用
- A方法仅仅是 实例化 控制器类对象, 而R方法是在 A对象基础上, 更进一步, 直接调用控制器的方法
- A或者R调用的格式是: 如果是同一模块内跨控制器, 用
A('User') ; R('User/fooMethod');
, 跨模块的话, 用 : `A('Admin/User'); R
('Admin/User/fooMethod'); , 如果是跨应用的话,
A('AnotherApp://User/fooMethod'); `, 所以, 如果是跨应用的话, 格式类似于 网站的格式:
http://User/fooMethod
关于二进制文件和文本文件的区别?
- php中对文件的操作函数, 很多都要进行 判断, 用if(...)判断每一步的操作是否成功, 比如 首先要判断文件打开是否成功, 成功后才能继续操作, 要判断文
件写入是否成功,, 成功后才能继续后面的操作...?
- 如果对文件的操作比较信任, 相信不会出错, 那么你可以省略fopen, fread, fwrite, fclose这一系列的操作, 直接使用`file_put_contents(..), 或
file_get_contents(..)` 更高效.
-
文件文件和二进制文件并没有本质上的差别, 在物理存储上, 都是存储的比特流. 读取的时候, 都是读取的比特流。
-
任何一个文件, 如果操作系统允许, 你可以用二进制打开,也可以用文本编辑器打开, 只不过读取的是乱码而已.
- 只有在windos上才区分文本文件和二进制文件,在类linux系统上都不区分它们的
- 区别是它们的编码、解码/译码的方式不一样
- 文本文件的编码和译码是以 字符编码/定长(gbk2个字节/ascii1个字节/utf3个字节)来进行的, 而二进制文件是以值编码的, 是变长/不定长比特流/字节流编码
的, 可能是一个字节表示一个意思, 可能是多个字节合起来表示某个功能或作用, 甚至是某一个比特位的不同取值表示某个含义...因此, 二进制文件的编码规则是
非常灵活/多变/自由/复杂的. 所以,你解码/译码的时候, 也就要求必须知道文件原来的编码方式或编码规则/编码器. 用对应的编码器或解码器才能. 否则就不能
打开...
- 在可读性上, 由于文本文件会按 定长字节依次读取, 并按定长字节的规则进行解码/译码, 所以读出的都是"可读human-readable"字符. 可读性好. 而二进制文
件, 每个字节单独来看, 并没有确切的/有意义的意思, 所以, 你一个字节一个字节的 用记事本程序读出来时, 必然是乱码.
-
在存储效率上, 二进制文件的效率更高, 因为你甚至可以 用一个比特位来存储/表示 信息...
-
在windows中, 除了文本文件.txt外,其他文件基本上都是二进制文件,比如: 图片文件, 音频/视频文件/ word文件等等.
-
二进制文件 是变长值 编码, 可能 "00000000 00000000 00000000 00000001" 表示一个四字节的整数 int 1 , 但是如果用文本文件来解析的话, 就变成 四个
特殊的控制字符了. 很明显跟 实际的含义就差得太远了. 用hex查看文件 那些显示的 空白或点点 就表示的是 "不可见, 不可读"的 控制字符/ 特殊字符.
- 所谓二进制安全函数, 是指 这些函数在操作 二进制文件的时候, 不会损坏 源文件的内容...
在php中所说的二进制安全?
主要是针对 字符串操作函数 来说的. 针对于比特率中的\0 等特殊字符字节而言的. 因为php来源于c语言. 在C语言中, 针对字符串操作的函数, 在遇到 '\0'
'\b'...等 字符时, 会作为特殊字符来处理, 将会结束函数操作.
php函数二进制安全,是说, 在处理从文件中读取到的 比特流, 会将所有的字符 都一视同仁, 不会担心有特殊字符影响处理, 不用担心特殊编码字节字符\0 的影响,
php "二进制安全" 函数, 会将\0字符当成普通字符处理, 不会当做字符串结束符. 后面的字符继续接受处理.
### 一个基本常识, 在函数中, 最好不要直接 用 echo /print 等输出语句来 输出字符串等内容, ,最好是 返回 某个数值 , 或返回 某个 字符串. 这样, 一个是便于调用函数的时候, 统一的知道 用 echo , print等来输出, 更重要的是, 可以让 函数 的调用, 返回值 可以和 字符串或 数字等 进行 相连 或 相加等操
作 ....
标签和行为?
- 行为发生作用的位置 就是 标签(位). 也成为 "钩子"
- 当程序执行到某个标签位置的时候, 就会被拦截下来, 统一执行相关的行为. 类似AOP编程中的 "切面 " 概念
- 给标签绑定行为的方式 就类似于 aop编程的思想.
- ??? 标签位置的定义是放在 控制器类的操作/方法中 , 用
\Think\Hook::listen('tag_name', $params);
绑定标签 和行为是在 tag.php文件中进行定义
: return array( 'tag_name1' => '/path/to/behavior1', ....);
标签 默认的 会首先 绑定 系统行为, 如果要 取消系统行为/ 覆盖系统行为, 需要使
用 在 标签文件中 使用 '_overlay' => true ????
-
可以单独的, 直接调用行为
echo B('CheckFooBehavior');
-
**行为扩展和函数的区别? ** 行为扩展是tp系统底层的一种 架构扩展, 是标签位和行为的绑定, 类似于 aop编程思想, 不管什么时候, 只要执行到标签位, 程
序就被拦截下来. 自动执行规定的行为. 而函数,是要 显式地调用的 .
- CBD模式: 是指 CORE, BEHAVIOR DRIVER. 在Think下, 除了核心类 之外, 基本上就是 对应的 驱动文件所在的目录 Driver.
类库和类库文件?
通常来说, 类库 library, 是表示的 目录 , 是 文件夹,
类 / 类文件, 类库文件 应该目录下的 文件. file.
类库映射?
-
目的: 是指, 在命名空间很多的时候, 为了提高加载效率而建立的类库文件 的 映射 /别名. 这样在初始化类对象的时候, 就不用管命名空间, 直接加载了.
-
添加类库映射的方法是:
-
??? 在操作方法中, 在要初始化对象实例之前, 用Think类的静态方法
Think\Think::addMap('Think\Log', THINK_PATH.'Think\Log.php') ....
addMap静态方法, 也支持 批量导入 类库映射, 使用 数组做为addMap的参数.. -
在 模块配置目录Conf下, 建立 alias.php 文件 , 在别名文件中创建映射:
return array(
'Think\Log' => THINK_PATH.'Think\Log.php',
...
//
);
- 类库文件自动加载的优先顺序? 比如 :
Test\My.class.php
注意Test前面加上反斜杠和不加的区别!
首先是 你定义的类库映射;
然后是 系统的类库目录下面的命名空间, 如: LIB_PATH . '/ Foo/Bar.class.php'
然后是 你定义的 注册命名空间, 用AUTOLOAD_NAMESPACE' => ...
最后是 应用项目的 模块 下的子目录...
最后, 不管是哪里的类文件, 都可以用 import 手动加载: import('Class_Name', '加载的相对目录', '非默认的文件后缀')
正有点混乱!
### 总之, 关于tp中的类库加载问题就清楚了: 首先, 类库(文件) 的加载问题, 是针对 类对象初始化 实例化 这个问题而言的. 因为你要初始化类对象, 你首
先得要 加载这个类对象的类; 也就是是, 要让tp系统 能够找得到这个类, 知道在哪里去加载这个类(是通过 命名空间 来 告诉tp 加载类所在的位置, 因为
namespace所在的目录位置, 跟 类名 是一致的...) 第二, tp加载类, 可以有两种方式, 一种是通过自动加载的方式, 优先级顺序是: 类库映射(别名) ->
LIB_PATH下的子目录(根命名空间) -> 自定义注册的namespace -> 应用项目下的模块 中的类. 另一种方式是, 通过手动加载的方式, 即不管什么 namespace, 我
在要实例化一个;类对象的时候, 就用import 函数去手动 加载这个类...
关于 编译缓存?
- 编译缓存的原理是: 第一次运行的时候, 将tp核心需要加载的文件 去掉空白和注释合并到一个文件中。 以后运行时, 直接加载编译过的缓存文件, 省去很
多io开销, 加快执行速度。
缓存文件的命名是: 应用模式~runtime.php 比如: common~runtime.php, 或者: sae~runtime.php 这里的common是应用模式.
.
- 另一种使用缓存文件的方式是 使用lite文件。
- lite : adj.
清淡的; 低糖的, 低盐的, 低脂的, 低热量的... - 在什么时机? 生成lite文件, 是在 生产环境下, 关闭调试模式后, 就可以生成lite文件
- 在入口文件中 , 定义
define(' BUILD_LITE_FILE', true) ;
这样执行入口文件后, 就会生成 lite.php 文件, 这个是默认的lite文件名 - 可以自己制定lite文件的名称和路径: 在 应用配置文件App/Common/Conf/config.php中, 定义
'RUNTIME_LITE_FILE' => APP_PATH . 'lite.php'
- 具体规定lite.php中要编译缓存的内容是: 在 应用配置目录下 App/Common/Conf下 或你前面制定的路径下, 新建lite.php文件 并输入:
return array(
THINK_PATH.'Common/functions.php',
COMMON_PATH.'Common/function.php', // 公共模块中的函数 , 文件名称 一定要用 function.php , 不能用 复数的functions!
CORE_PATH.'Think'.EXT,
CORE_PATH.'Hook'.EXT,
CORE_PATH.'App'.EXT,
....
BEHAVIOR_PATH.'ParseTemplateBehavior'.EXT,
BEHAVIOR_PATH.'ContentReplaceBehavior'.EXT,
等等...
);
所有在lite.php中定义的文件都会 纳入lite文件的编译缓存中 , 当然 还可以对已经生成的lite.php文件进行修改
关于Behavior的设置?
- 定义自己的行为, 在模块目录下创建一个Behavior目录, 然后将自己的行为类文件 放在Home\Behavior\MyBehavior.class.php中。
- 行为标签绑定文件, 可以放在项目 或 模块的 Behavior目录中, 因为绑定行为时使用的是完整路径。
很重要的一个概念: 编译缓存文件或 lite.php文件, 应该放在 应用/项目的 配置目录中, 这样多个模块 前台/后台都可以 使用这个缓存文件; 当我们修
改了 应用或模块的配置文件, 修改了载入到缓存文件中的类文件之后, 就应该重新 去生成 缓存编译文件, 否则, 继续使用原来的缓存文件就会出错!
lite文件生成后, 就可以将原来的 "框架入口文件" (注意不是index.php文件), 替换为 lite.php: 即 : `将 require THINK_PATH. 'ThinkPHP.php' ;
语句该成: require './Runtime/lite.php' ; `
当生成和替换 lite文件后, 应用的编译缓存 就不再需要了...
控制器 到模板 的变量输出?
-
在模板中, 输出变量使用
{$**}
的方式, 输出函数 使用{: **}
的方式 -
使用 assign方法, assign方法 的 参数是两个, 一个必须 有 变量名, 一个是变量值. 相当于 等号赋值的 变化方式. 比如:
$this -> assign( 'cart', $cart);
就表示给cart这个要传递到模板的变量 赋值为$cart
. 否则, 如果直接写$this -> assgin($cart)
那么 这个cart值 其实是没有 付给 任何变量的, 所以, 你在 模板文件中, 输出{$cart}
得到的结果只能是空, 因为根本就没有 cart变量. -
如果模板变量是 数组, 可以用 点语法 和 中括号输出 都可以 , 如果是 对象, 支持 冒号和 箭头语法输出 都可以 , 比如:
{$arr.name} {$arr['name']}, {$person::name}, {$person->name}
但是 建议 还是 用比较传统的方式输出, 即 数组 使用 中括号语法, 对象使用 箭头语法:{$arr['name']}, {$person->name}
-
整个模板变量, 只有 两种, 一种是 普通的 通过 assign 赋值的模板变量; 另一种就是 所谓的系统变量, 所有的系统 变量, 统一的用 同一种 方式输出, 就是:
$Think.**
支持: 超全局变量, 如$_SERVER; 常量; 配置变量, 语言变量等等. -
有时候, 我们可能不只是 要 原样输出模板变量, 还可能需要输出 经过函数处理/ 四则运算符处理后的 模板变量 , 这时 , 可以借鉴 linux 中 "管道" 的 思想, 把前面的模板变量作为 后面的函数处理的 管道 输入: 即
{$name|md5|strtoupper|substr=###, 1, 3}
也支持默认值:{$name|default='默认名称'|substr=###, 1,3 }