rewrite就是伪静态, 伪静态就是 rewirte, 可以把入口地址隐藏掉.
兼容模式就是 普通模式 和 pathinfo模式的 结合, 前面是普通模式 ?s=
, 后面的 模块/控制器/操作和参数是用 pathinfo方式给出的, 比如: http://localhost/index.php?s=/home/user/login/var/value
这个s叫做var_pathinfo, 是兼容模式下获取模块等变量的符号, 可以自己定义: 'VAR_PATHINFO' => 'path', 也可以定义兼容模式下参数的分隔符: 'URL_PATHINFO_DEPR' => '-' 默认的是斜杠
tp的路由目的, 就是 为了让 url地址 好看, 让url看起来更简洁 (更优雅???)
路由分为 规则路由和 正则路由. 规则路由中分为 静态路由和动态 路由, 其中的一种特殊的 完全 静态路由 也叫 "静态映射". 可以单独的 用 URL_MAP_RULES
来定义.
其实tp的路由不是必须的, 在开发的初期, 你完全可以无视这个, 只是在 后期进行 seo优化的时候, 可以考虑这个路由的问题.
模板View下的子目录比如 View/User 通常对应着 控制器, 控制器的操作方法, 对应着 子目录下的文件 , 比如: login操作对应着 User/login.html
为了减少View下的目录层次, 让 控制器和操作方法对应的模板文件, 直接放在 View下面, 可以 定义 'TMPL_FILE_DEPR' => '_'
控制器中的方法, 控制着 对应的模板文件 是否显示, 就是通过 $this -> display() 或 show()
来控制的. 如果没有这句话, 就不会显示模板文件, 即使你有模板文件也不会被显示
以后写模板文件的头部, 通用的内容是: 就是 js 和css内容还有图片等内容. 都使用 模板常量 .
这些方案很多, 看你自己的喜欢, 和安排.
你可以将所有的资源文件(不分模块的)放在Public下, 但是 我觉得更好的方法是:
- 将前台后台(多个模块) 共用的资源文件,比如 css, 比如js: jquery...js, 比如共用的bootstrap.js jquery-ui.js easyui.js等, 比如各个模块都要用到的logo.png等图片 放在 跟 App同位置的 Public目录的相应子目录中, 比如:
__PUBLIC__/Css/... __PUBLIC__/Js/...
- 然后, 按模块来分, 各个模块单独要使用的资源文件, 单独放在 Public/下面的子目录中, 同时定义相应的模块资源目录. 比如
在模块自身的配置文件比如 /Home/Conf/config.php 中, 定义
'TMPL_PARSE_STRING' => array(
'__PUBHOME__' => __ROOT__.'/Public/Home',
'__PUBADMIN__' => __ROOT__.'Public//Admin',
),
这样的话, 就只是定义 几个Public常量就好了. 不用再去定义__JS__, __CSS__了, 同时, 公共资源的目录也很清晰.
但是 要注意的是,
- 在定义 TMPL_PARSE_STRING的时候 , 后面的具体目录路径, 必须使用 系统 已经定义好了的 路径常量才行, 而且不能是 在
ContentReplaceBehavior.php中定义的模板常量, 比如 __URL__
也不行 - 其次, 就是, 虽然在behavior中, 有
$replace = array_merge($replace, C('TMPL_PARSE_STRING') );
模板变量的融合, 但是 你并不能 将$replace
中的模板常量 使用在 TMPL_PARSE_STRING的定义中, 因为系统并不会 用$replace
中的内容去替换TMPL_PARSE_STRING中的内容! 比如在TMPL_PARSE_STRING的定义中就不 能使用 PUBLIC.
几个容易混淆的单词
merge: 融合, 合并
emerge: 突显, 显露, 出现
emergen: ===== emergency 紧急, 急诊, 危机
Many people live a tangled life, for they want to gain other's recognition, but are unable to polish themselves peacefully.
// 通用的模板文件头部
<html>
<head>
<title>...</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="__PUBLIC__/Css/header.css" />
<script src="__PUBLIC__/Js/jquery-min.js"> </script> // 前面两个是用于各个模块都使用的公共模块的css和js文件
<script src="__PUBHOME__/Js/common.js"></script> // 这个是只用于前台Home模块的js文件
</head>
<body>
....
在html页面中, 要 缩放图片, 但是又要保持图片的等比例缩放. 不变形? 方法是: 只是指定图片的 宽度 而不指定图片的高度 就可以了.
css的类, 样式等, 主要是针对 大量的 相同样式设置的 元素 比较有用, 但是如果是 单一元素, 或者是 少量的 个别的 元素, 则不必/不推荐使用 css样式, 更推荐的是使用 标签本身的属性! 这样的话, 更简洁. 比如: 对于 img 元素, 你 完全 可以 指定 图片的 width, height, border 等 属性...
### 在tp中, 所有的路径中, 的末尾是否有斜杠? 这个牵涉到后面连接的字符串前面要不要斜杠的问题. 有两种情况下没有斜杠, 有一种情况下有斜杠: 1. 除了 dirname 后面没有 斜杠 '/' 1. 除了 ContentReplaceBehavior.php中的 常量也没有 斜杠, 包括(从根开始 ): __ROOT__, __APP__, __MODULE__, __CONTROLLER__, __ACTION__等. 要注意的是, 上面这些常量, 既可以在 框架的 php文件中使用, 也可以在 模板文件中使用. 但是要注意, 有个 __URL__, 是只能在 模板文件中使用, 不能在 php文件中使用, 或者说, 在 php文件中使用时 只能按原样输出.
- 除了上面的两种情况外, 其他的 路径常量 (在ThinkPHP.php中定义的常量 , 如 THINK_PATH, CORE_PATH, LIB_PATH等等) 最后都有 斜杠 '/'
命名空间, 通常是控制器文件(类)所在的目录, 不是文件本身, 因此, 对于形如: FooController.class.php的类文件, namespace就是 HomeController , 而不是HomeFoo
使用命名空间的好处, 就是 在tp3.2.3中, 可以 让系统 通过命名空间 来自动定位到类(文件), 从而自动加载类, 在创建类的实例时, 不需要手动加载类, 而是可以直接 new.
### html文档中的任何内容(主要是文本!) , 如果你不放在 标签内, 不管这个内容放在什么地方(即使 放在head (没有放在title标签中)中, 或者放在 html开头标签的 前面或者html结束标签的最后) , 都会在页面内 按原样 输出! 也就是说, 即使是 html标签, 也只是被看作是普通标签; 在html开头标签之前, 也可以放置其他内容的. 或者说, 整个html文件中的内容, 都会被浏览器解析输出....
在php文件中和 定义模板 常量中, 可以使用的 系统常量包括: APP, CONTROLLER.
注意, __CONTROLLER__是指的控制器所在的路径: 即: /index.php/Home/Index , __APP__是指的系统应用所在的目录,即: /index.php
controller的显示方法有 show和display的区别?
- 从语义上讲, display是 长时间展示, show是短时间的出示一下的意思。
- display主要是用于显示 控制器、方法对应的模板文件, 而show的应用就更灵活, 比如: 你根本就没有定义/创建任何模板文件, 或者你的内容是存放在数据库中, 或者你就是想自己输出一些 html代码内容,那么你就可以使用show方法.
- show 的参数,包括‘content’, ‘charset’, ‘doctype’
- 在show中, 你可以包含任何在 模板中使用的内容, 比如: html标签, css代码, 甚至是模板常量 :
$this -> show('i am show,<font color="red"><b>__PUBLIC__</b></font>', 'utf-8', 'text/html');
网页错误代码?
- 2** 表示操作成功
- 3** 表示重定向, 301是永久移动(了网页文件), 302表示 临时移动了(网页文件)
- 4** 表示客户端错误, 401是客户端未授权错误, 403 表示客户端被拒绝(可能是文件的acl等保护设定,404是文件或目录没有找到
- 5** 表示 服务器端错误... 500 501 502 等表示服务器内部错误.
关于跳转和重定向?
- 都是 中间的/ 临时显示信息的/ 表示过渡的文件
- 跳转和重定向的区别, 是他们的机制不一样, 重定向redirect是通过 php header实现的. 而跳转是通过meta http-equiv
meta跳转的刷新 **注意, http-equiv和content 属性总是结合起来使用的.
<meta http-equiv="Refresh" content="5; url=******.htm">
<meta http-equiv="content-type" content='text/html; charset=UTF-8'>
路由功能是针对pathinfo模式和兼容模式设计的,对普通url模式不支持 , 注意, 这里的pathinfo模式只是说, 针对 前面的 路由表达式而言的, 在 路由地址中 还是可以 使用 url普通模式的 字符串
路由配置位置, 分全局路由和模块路由, 全局路由可以针对模块,需要放在全局配置文件中,模块路由放在模块配置文件中,
两者的区别是: 全局路由一定要带上 模块名称
路由表达式, 是指在 http://serverName/
后面或 localhost后面直接写的访问地址
路由具有"短路" 特性: 系统会按定义的顺序依次匹配路由规则, 一旦匹配到的话, 就会定位到路由定义的控制器/方法去执行(除了路由地址外, 还可以传入额外的/其他参数), 同时会短路, 后面的其他路由规则不会继续检查.
因此, 针对"路由短路" 而引起的逻辑错误:
- 可以调整路由条目的顺序, 通常是比较复杂的/特殊的路由规则放在前面...
- 任何时候,你都可以使用 正则路由 来解决这些路由错误.
正则路由表达式必须使用 斜杠开头和结尾, 或者说, 正则表达式一定要用斜杠做 分隔符, 不能使用其他符合比如# 等做分隔符,否则会认为是规则路由.
在 后面的路由地址中, 支持对参数的函数过滤操作.
同规则路由的动态参数一样, 如果在后面的路由地址中要使用前面的动态参数, 采用 :1, :2等的形式
比如:
'URL_ROUTER_ON' => true. // 注意这里是router, 表示路由器开启
'URL_ROUTE_RULES' => array( // 而这里是route 表示路由动作
'/^news/(d{4})/(d{2})$/' => 'News/achive?year=:1format_year&moth=:2',
........... 其他条目,
),
在路由规则中, 如果路由地址以斜杠开头, 则认为是 :根目录下的一个重定向地址 , 如果是以http开头的, 则认为是一个外部地址.
在路由规则中的写法:
- 前面的 路由表达式 必须/只能 使用 字符串方式, 那么路由条目中就必须加单引号了
- 路由地址可以使用 字符串引号表达式或数组两种方式来定义
- 注意参数, 除了可以使用路由表达式中的动态参数外, 还有另外两种参数, 一种是额外参数/隐藏参数, 另一种是 "路由参数", 注意额外参数跟路由参数 是不一样的: 路由参数是限制前面的路由规则生效的条件.
要定义路由参数, 路由地址必须使用 数组方式, 而且第二个数组元素表示的额外参数 要用 字符串的方式, 路由参数要用数组方式放在最后面. 比如 array('ext' => 'html') 比如: array('method' => 'get'), 比如: array('callback' => 'checkFun') 注意这样的话, 当checkfun 返回true时, 路由生效, 而返回false时则路由不生效.
前面说的都是路由地址的约束和过滤, 同样的, 也可以对路由表达式进行过滤和约束:
-
路由地址中的动态参数,你可以在后面的路由地址中使用, 也可以不在路由地址中使用。 另外,路由表达式即(URL地址)中的动态参数,是对应着
$_GET[":1"]
等变量的,这些动态参数通常是要在编码、代码即(控制器的操作方法)中使用的,作为获取数据的依据和限定 -
路由表达式中的动态参数可以使用 “数字类型”的检查和约束过滤,就是说,只有是数字的时候,该条路由才会被匹配到而生效,只支持数字类型,不支持数字长度(如果要限定长度,用正则路由)。 那么也就是说, 路由的匹配和过滤、生效规则,既可以在前面的路由表达式中过滤 也可以在后面的路由地址中过滤限定 但是要注意, 两者限定生效的 可选项不同。
-
路由表达式和路由地址 都支持函数过滤。 但是作用不同: 前者是影响传递给 操作方法的参数。 而后者影响的是url的形式
-
路由表达式中, 支持可选参数, 这样可以将原来的两条或多条路由规则 合并为一条 路由规则
-
规则路由 支持完全匹配, 注意, 规则路由不能限定开头, 因为规则路由 总是从url开头开始匹配的(而且只要包含了定义的路由规则都会被匹配到) 因此要想完全匹配,必须使用
$
来限定结束。 比如:new/:cate$
-
路由排除,当路由表达式中的变量名称和实际的控制器、方法名称相同时, 可能分不清到底是路由变量还是模块、控制器、方法名, 会发生混淆。 所以可以使用路由排除:
:cate^add|del|edit
等方式来排除。 但是最好的, 还是要避免在路由表达式中使用 同名的 模块、控制器、方法名
关于tp的模型类操作
-
三种实例化模型类的方法和情况, 一是最基本的实例化类, 使用基类的方法
$user = new ThinkModel('User');
简化的方法是:$user = M('User');
二是实例化自己定义的模型类 使用 D方法, D方法会自动判断当前定义的类是否已经自定义创建了的, 如果没有自定义创建的类, 则调用基类模型类来实例化, 三是, 为了支持使用原生的 sql语句, 采用 实例化空模型类. -
对数据库的操作, 基本的操作方法是 增删改查curd操作. 而除了curd之外, 其他的辅助操作/过滤操作/条件/排序/限数等操作, 叫做 连贯操作
tp的curd操作, 包括delete, select, 但是增加操作不是原生的insert, 而是add, 更新操作不是原生的update, 而是save. -
对数据库的操作 , 总的来说, 有两种方式, 一是传统的curd方式, 即 模型类方式 , 另一种是 映射类 <-> 表操作, 即** ActiveRecord: AR方式. 两者的主要区别是
- 前者在 操作之前, 通常要 进行" 创建 数据对象 " 的操作, 而后者 则不需要 创建数据对象, 直接对 对象进行操作.
- 是因为采用"模型类"的方式时, 创建的对象是空的, 所以需要 进行赋值操作. 比如:$user = M('User'); $user -> create(); 或者$user -> create($data); 或要使用 $user ->data ($data);
- 而 AR方式, 由于采用的是 ORM模型, 表映射到类, 对象映射到记录, 所以$user = M('User');
时, 'User' 就是表了, 而$user
就已经是一个表的记录对象了...所以, 在操作时,就不需要再创建 数据对象了. 直接进行curd操作就可以了. 比如:$user = M('User'); $user -> name = 'foo'; $user -> add();
- 而AR方式最大的特点是, 他的curd操作 主要/全都是 直接 通过 主键 来操作实现的
, 比如:查询$user -> find(10); 删除 $user -> delete(8); 更新 $user->id = 2; $user -> name= after_update; $user -> save();...
-
采用 "模型类" 的方式 进行数据对象的创建方式? 要注意: curd操作中, add方法和save方法 都要先 创建 数据对象!
有两种
一种是 create方式, create 默认的是从 表单 post传递 过来的数据获取, 但是也可以指定 从其他 数组或对象 来create 数据对象
另一种是 用 data方法. 从自己 手动创建的 数组来创建数据对象.
两者的区别是 create方法 可以支持 自动验证和 自动完成 , 而 data则必须自己手动实现验证... -
tp查询的方式主要有三种:
一是 获取单条记录, 采用 find 方式, 注意, 这里find 总是 只 获取 一条记录, 即使符合条件的有多条记录, 也只会 返回第一条 满足条件的记录数据.
二是 获取多条记录, 记录集, 采用 select方式:$user -> where('addr=beijing') -> select();
三是, 获取字段值, 采用 getField, 最多可以指定 1个, 2个, 3个字段. 和 返回记录的条目数量. -
数据库 模型类 跟表单 验证/ 或自动完成 是 相关联的. 相类似的.
- 验证的方式有动态验证和静态验证. 它们都是通过 create() 方法 调用时 执行验证的. 静态验证是将 验证规则写在 类的内部, 用
protected $_validate = array(...)
的方式, 而动态验证 是通过 自己写验证规则数组, 显式的调用 validate函数:$user -> validate($validate_rules) -> create() ....
- 验证的规则格式是:
protected $_validate = array(
array('验证字段1', '验证规则', '出错信息', ['验证条件', 附加规则, 验证时间]),
// 这里要和 自动完成 相对比: array('自动完成字段1', '完成规则', [完成时间, 附加规则]).....
array(),
);
验证条件是: 0, 1, 2 或: self::EXISTS_VALIDATE, :::MUST_VALIDATE, ::VALUE_VALIDATE(值不为空时要验证)
附加规则是和前面的验证规则结合使用的, 前面的 验证规则通常是 值或空串, 后面的附加规则是对前面的验证规则的说明, 比如: regex, function, confirm, unique, in/notin, between, equal/notequal, 等等
验证时间 是: 1, 2, 3 或: self::MODEL_INSERT, self: MODEL_UPDATE, self::MODEL_BOTH.
模型类首次实例化的时候, 会缓存字段信息在类似文件runtime/data/_fields/demo.think_user.php中 , 通过DB_FIELDS_CACHE来决定是否缓存, 也可以通过 getDbFields来获取字段信息, 模型类的字段不一定要跟数据表完全一致, 你完全可以自己手动指定 $fields, $pk , 以及$fields数组中的 '_type'来指定字段类型.
路由规则URL_ROUTE_RULES中的元素, 可以采用 '路由表达式' => '路由地址和参数' 的方式来定义, 每个规则/元素 也可以用 array('路由表达式', '路由地址', 参数) 等的方式来写.
'URL_ROUTER_ON' => false, // 是否开启URL路由
'URL_ROUTE_RULES' => array(), // 默认路由规则 针对模块
'URL_MAP_RULES' => array(), // URL映射定义规则
- 说明url路由默认是关闭的, 所以要使用路由的话, 得要开启路由器
- 静态路由, 是规则路由的特例. 要实现静态路由, 你可以在规则路由中, 完全使用静态参数和地址, 也可以单独地/ 分开 , 专门的使用静态路由规则: URL_MAP_RULES => array( ... );
要注意 静态路由 是完全 的 规则匹配, 是完全匹配, 不只是前面的部分匹配即可
静态路由中的 "路由地址包括后面的参数 var=1&rav=2...', 只能使用字符串的形式, 不能使用 数组的形式.
关于路由闭包的使用
-
为什么要使用闭包? 是为了在 响应一些特殊的 url 地址时, 要 执行一些特殊的需求和功能, 不让程序去执行 控制器的方法, 而是直接执行闭包函数中的 行为
-
闭包中的函数 参数传递, 如果是 规则路由, 那么前面路由表达式中的 动态参数, 就是后面的闭包函数中的参数, 名称相同, 顺序不要求; 如果是 正则路由, 那么路由表达式中的 括号参数, 将会 按顺序 地 传递到 闭包中, 作为参数, 而且名称是任意的, 但是 位置顺序严格对应.
-
路由闭包的继续执行?
通常情况下, 闭包执行完后, 不会继续执行 控制器和操作方法了, 如果要继续执行,应该这样做:
- 指定$_SERVER['PATH_INFO'] = 'controller/action/name'.$name_param 等等
($_SERVER['PATH_INFO' ]=> string 'index/foo/name/obama/age/20' (length=27) 就是pathinfo模式下包括 从控制器/到方法/到后面的附加参数的所有内容... 修改了这个, 就相当于重新给url地址传递 控制器和方法...)
- 然后在闭包中返回false.
这样执行完闭包的功能后, 会继续执行控制器和方法的检测和执行!
在一个项目中, 对数据库的操作主要 是在 同一个数据库中进行的, 很少会使用多个数据库. 数据库的操作, 也主要是对数据表进行操作的
表映射为类, 类对象就是一个表的记录.
正则路由和规则路由中的 "路由表达式" 中的 动态参数?
-
这些动态参数, 你可以写在/出现在 后面的路由地址中, 也可以不写在路由地址中.
但是这些动态参数 总是会 传递给/ 存在于 $_GET数组的. 你总是/一般情况下, 也是需要的, 需要使用的, 需要用这些动态参数去过滤/获取
具体的数据 -
注意这些参数 还不是 路由地址中的额外参数, 路由地址中还可以传递 额外参数...
解决路由冲突的方法?
- 尽量让复杂的路由规则放在前面
- 如果冲突时, 可以使用完全匹配: 在路由表达式的最后用
$
来限定. - 利用正则路由, 总是可以解决路由冲突的.
控制器的方法, 控制着对应的模板的显示(与否), 可以让模板显示,也可以不显示模板的内容. 甚至可以通过条件语句来控制模板的显示. 要传递到模板的变量, 有两种方
法, 一是, 用assign方法(必须指定变量名, 否则是没有对应的模板变量的), 第二是,直接使用 类对象的成员变量赋值方法, 用箭头语法, 比如: `$this -> foo = 'bar';
` 推荐使用后者, 更直观更简洁.
tp有些地方和功能完全就是没用, 实际开发中也不会有人去用,他就是说, 昨晚一个框架, 别人有的他也要有, 比如操作方法绑定到类, ACTION_BIND_CLASS, 只是将
控制器的目录再细化了一下, 比如原来直接放在Controller/目录下的IndexController.class.php文件, 再次做了分类细化, 放在了
Controller/Index/IndexController.class.php中, 这个其实将目录层次放得更深了. 一般都不会有人这么用的.
在定义控制器的操作方法时, 要注意操作名不能 跟系统的保留 方法名称相同. 比如: 类的 assign, error, success等, 如果确实要这样定义, 可以 配置:
ACTION_SUFFIX = 'action
来解决, 这样你自定义的方法/操作/函数名后面就要加上 Action才行了, 比如 successAction()...
URL参数绑定?
操作的前置和后置?
**php的魔术常量和魔术方法的区别? 魔术常量的两边, 前后都是下划线__, 比如: __FILE__
, 而魔术方法, 只有 开头才有 两个下划线__, 比如 __construct() **
-
首先, 前置和后置操作, 不是ThinkController基类的成员方法, 在Controller类中没有 什么
__before__, __before
方法, 倒是有 : __construct(), __destruct(), __get(), __set()等特殊函数. -
其次, 所谓的前置和后置方法, 都应该是 继承类中的 公共方法public. 函数名是在对应的方法名称前面加上 _before_或 after, 比如: index方法的前置和后置函数是
_before_index(), _after_index()
-
再次, 前置和后置方法, 只有当对应的那个方法存在时, 才有效.否则 无效.
-
当对应的方法中, 使用了 exit, die, 或 error等语句时, 对应的后置方法将不会再执行.
-
说到魔术方法, 比如php的__call() 函数和 __callStatic函数, 会 监视 类对象中的 成员函数的调用,或 静态函数的调用. (即动态方法和静态方法的调用) 当调用的 某个方法不存在时, 将会自动调用类的__call()函数和 __callStatic函数
作用是: 容错机制, 避免报错和崩溃;
另一个是, 专门的有意的使用这个机制, 来实现 类似c++语言中的多态/重载.
因为__call()函数有两个参数: $method, $args. 前者是你调用的不存在的那个方法名, 后者是 不存在的方法中的参数组成的数组. 因为__call 要 接手 来处理, 不存在的操作方法嘛, 那么它自然要知道, 要处理的方法名称是什么, 被处理函数带的参数...
重载机制的实现, 是通过 if条件判断 参数 的 类型 / 数值等来 决定 调用 哪些 内部分支处理函数. 比如: -
tp的 空操作, _empty方法, 在 作用和 实现上 就类似于 php 的 __call() 方法, 就是在内部 调用了php的__call函数??
public function __call($method , $args){
if($method == 'foo'){
if(is_int($args[0])) {
handle_nonexist_int($args[0]);
}else if(is_string($args[0])){
handle_nonexist_string($args[0]);
}
}
}
protected function handle_nonexist_int($int){
echo $int;
}
protected function handle_nonexist_string($str) {
echo $str;
}
实现重载的方法是:
foo(10);
foo('i am another);
### 一个很痛苦的 体验: 要输出 3.2.3中的 配置的二维数组( 二级数组) , 一是要使用 `{$Think.config....` 的点号 的语法 格式, 不能使用 中括号语法, 因为使用 中括号 语法 会 返回 空 : null. 另一个 就是: 别的二级数组中的 配置参数 都是可以输出的, 但是 唯独 TMPL_PARSE_STRING中的二级数组 不能用 `{$Think.config....`输出. ???? 但是将 TMPL_PARSE_STRING 这个改一个名字, 就可以用 $Think.config.... 输出了. 不知道这个是为什么???