运行

单一入口:index.php

1
2
3
4
5
<?php
define('APP_DEBUG', true);
define('APP_NAME', 'App'); // 定义应用名称
define('APP_PATH', './App2'); // 应用路径,生成目录下 /App/ 文件夹,注意别忘了最后的斜线
require './ThinkPHP/ThinkPHP.php'; // 加载 ThinkPHP

运行流程:索引控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$module = isset($_GET['m']) ? $_GET['m'] : 'Index';
$action = isset($_GET['a']) ? $_GET['a'] : 'Index';

$mooc = new $module();
$mooc->$action();

class index
{
function __construct() {echo "I'm index controler<br />"; }

function Index() {echo "这是 index 控制器的 index 方法<br />"; }

function test() {echo "这是 index 控制器的 test 方法<br />"; }
}

在浏览器地址栏输入: 网站地址/?m=index&a=index 来访问 index 的 index 方法
使用 ?m=index&a=test 来访问 index 的 test 方法

也可以多建几个 class 和 function,达到不同的 module 和 function

目录结构解析

  • Common 存放当前项目的公共函数
  • Conf 存放当前项目的配置文件
  • Lang 存放当前项目的语言包
  • Lib 存放当前项目的控制器和模型 (存放MVC中的M、C)
  • Runtime 存放当前显示的运行时的文件:\Cache\、\Date\、\Logs\、\Temp\、~runtime.php
  • Tpl 存放当前项目的模板文件 (存放MVC中的V)

ThinkPHP 配置文件

全局设置

总的文件路径:\ThinkPHP\Conf\convention.php

每个程序的设置

应用配置路径:\[应用名]\Common\Conf\config.php

1
2
3
4
5
<?php
return array(
//'配置项'=>'配置值'
'name'=>'Hello'
);

如果有自定义配置文件,而且入口的 APP_DEBUG 不是为 true 的话,修改了默认的配置文件后,程序不会重新编译,需要重启才能生效。

控制配置路径:[应用名]\Home\Controller\IndexController.class.php

1
2
3
4
5
6
7
8
9
<?php
// 本类由系统自动生成,仅供测试用途
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function index(){
echo C('name'); // 根据上面的配置项,输出 Hello
}
}

针对每个程序的配置,不会和其他程序公用

用户自定义配置文件

配置文件:\[应用名]\Common\Conf\config.php

1
2
3
4
5
6
<?php
return array(
//'配置项'=>'配置值'
'name'=>'Hello',
'LOAD_EXT_CONFIG'=>'user' // 加上这一句
);

新建\[应用名]\Common\Conf\user.php

1
2
3
4
5
<?php
return array(
//'配置项'=>'配置值'
'user'=>'UUU'
);

可以直接用 echo C('user'); 来输出配置值

URL 模式

URL_MODEL

  • 1 默认模式 pathinfo 模式
  • 0 普通模式
  • 2 重写模式
  • 3 兼容模式

在配置文件:[应用名]\Home\Controller\IndexController.class.php 的 index 方法中加上:

1
echo C('URL_MODEL');

来显示当前的URL模式。

U() 方法

U(‘模块/方法’, array(‘id’=>1), ‘xxx html htm sthml’, true/false, ‘localhost’);

用来显示对应的完整的URL

示例

编辑配置文件:[应用名]\Home\Controller\IndexController.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
// 本类由系统自动生成,仅供测试用途
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function index(){
echo C('URL_MODEL') . "<br />";
echo U('Index/user', array('id'=>1), 'html', false, 'localhost');
}

public function user() {
echo 'id:' . $_GET['id'] . '<br />';
echo "这是 Index 模块的 user 方法";
}
}

打开网站首页,运行结果:

1
2
1
/ThinkPHP/index.php/Home/Index/user/id/1.html

访问网址localhost/ThinkPHP/index.php/Home/Index/user/id/1.html,出现结果:

1
2
id:1
这是 Index 模块的 user 方法

修改URL模式

程序配置文件 \[应用名]\Common\Conf\config.php 加上配置项:

1
'URL_MODEL'=>0

或者可以在全局配置文件 \ThinkPHP\Conf\convention.php 中修改

模式说明

网站路径为:http://localhost/ThinkPHP/

隐藏 index.php

重写模式

打开 Apache 的配置文件 httpd.conf,搜索 rewrite.so ,去除前面的 “#” 号

1
#LoadModule rewrite_module modules/mod_rewrite.so // 去除前面的 # 号

重启 Apache

项目目录下建立一个文件:.htaccess,写入以下内容:

1
2
3
4
5
6
<Ifmodule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
</Ifmodule>

分别是:开启重写引擎、重写判断条件、重写规则(正则)

404 Not Found : No input file specified

在Fastcgi模式下,php不支持rewrite的目标网址的PATH_INFO的解析,当我们的 ThinkPHP运行在URL_MODEL=2时,就会出现 No input file specified.的情况。
这时可以修改网站目录的.htaccess文件: 将 RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] 改为 RewriteRule ^(.*)$ index.php?s=$1 [QSA,PT,L]

URL 伪静态

把 shtml 改成和 html 一样作为后缀名来识别

全局配置文件,‘URL_HTML_SUFFIX' => ‘html’

在单个项目配置文件中(不建议改全局)改为:

1
2
3
4
5
<?php
return array(
// ... => ...,
'URL_HTML_SUFFIX' => ‘html|shtml’
);

伪静态有益于搜索引擎的抓取

自定义函数库

  1. 自动加载自定义函数文件
    函数文件放在项目公共目录Common下。
    项目目录下,需要在配置文件Conf/config.php配置自动加载的函数文件的文件名。配置项为"LOAD_EXT_FILE",如"LOAD_EXT_FILE"=>"user,mysqldb"表示在项目目录下Common/user.phpCommon/mysqldb.php两个文件会自动加载。自动加载多项用逗号隔开。
  2. 手动加载函数文件
    使用load函数,如load('@.myfunction');表示系统会加载Common/myfunction.php这个文件。
  3. 自动加载自定义类文件
    类文件放在项目类库Lib下。
    项目目录下,需要在配置文件Conf/conf.php配置自动加载的函数文件的文件夹。配置项为'APP_AUTOLOAD_PATH' =>'‘, ,如'APP_AUTOLOAD_PATH' =>'@.Common',表示在项目目录下Lib/Common文件夹下的类文件都会被自动加载,当然,类文件名要以***.class.php文件命名。 自动加载多项用逗号隔开。
  4. 手动加载类文件
    使用import()函数。如 import('@.Common.mysqldb');则系统会加载Lib/Common/mysqldb.class.php文件

模板技术

建立项目模板

模板路径\[应用名]\Home\View\模块名\方法名.html

例如 Index 模块的 index 方法:\Application\Home\View\Index\index.html

使用模板

1
2
3
4
$this->display();              // 默认 Index/index
$this->display(""); // 等同于上一个
$this->display("Index/test"); // 非默认模块方法
$this->display("test"); // 默认的模块 Index 可省略

模板的赋值和输出

方法一:display()

\[应用名]\Home\Controller\IndexController.class.php

1
2
3
4
5
6
7
8
9
10
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function index(){
$name = 'WXY';
$this->myname = $name;
$this->display();
}
}

\[应用名]\Home\View\Index\index.html

1
2
3
4
5
6
<html>
Hello World~
<?php
echo $myname;
?>
</html>

结果:

Hello World~WXY

方法二:assign()

$this->assign('变量名', 变量值);

\Home\Controller\IndexController.class.php:

1
2
3
$date = date("Y-m-d");
$this->assign('today', $date)->assign('weather', 'sun'); // 可连起来
$this->display();

与直接赋值的区别在于,可以多次连续赋值

模板引擎

ThinkTemplate.class.php(文件位置未找到)

变量运算和输出

控制 \Home\Controller\IndexController.class.php

1
2
3
4
5
6
7
8
9
10
11
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function index(){
$me['name'] = 'WXY';
$me['age'] = '20';
$this->assign('me', $me);
$this->display();
}
}

模板 \[应用名]\Home\View\Index\index.html

1
2
3
4
5
6
7
8
Hello World~ <br />
{$me['name']} <br />
{$me.name} <br />
{//$me.sex 空的是输出不了的} <br />
{$me.sex|default='man'} <br />

{// $me.age+1 这种是输出不了的} <br />
{$me['age']+1} <br />

注释是前面加两个斜杠。(也可以在中间加,但是可能会出错)

$me.sex|default='man' 如果 sex 没有定义,那么就能输出默认值。


算术运算可以使用+-*/++–-,此时必须使用$me['age']这种方式。

调用函数和系统参数

控制 \Home\Controller\IndexController.class.php

1
2
3
4
5
6
7
8
9
10
11
12
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function index(){
$me['name'] = 'WXY';
$me['age'] = '20';
$this->now = time();
$this->assign('me', $me);
$this->display();
}
}

模板 \[应用名]\Home\View\Index\index.html

1
2
3
4
MD5加密:{$me['name']|md5}
加密后并截取前五位:{$me['name']|md5|substr=0,5}
输出时间:{$now}
指定格式的时间:{$now|date='Y-m-d H:i:s',###} 三个#表示传时间戳(即$now)的值

函数等同于在控制文件中执行:

1
echo substr(md5($me['name']), 0, 5);

系统变量:

1
2
3
系统时间:{$Think.now} 输出格式化后的时间
系统版本:{$Think.version}
系统版本:{$Think.server.http_host} 输出 localhost

volist和foreach循环

控制 \[应用名]\Home\Controller\IndexController.class.php

1
2
3
4
5
6
7
8
9
// 套在外面的class等已略写
$person = array(
1 => array('name'=>'Jack','age'=>'18'),
2 => array('name'=>'Tom','age'=>'19'),
3 => array('name'=>'Peter','age'=>'20'),
4 => array('name'=>'Mary','age'=>'21'),
);
$this->assign('person', $person);
$this->display();

模板文件 \[应用名]\Home\View\Index\index.html

1
2
3
4
name='原变量名' id='循环中的变量名'
<volist name='person' id='data'>
{$data['name']}-----{$data['age']} <br/>
</volist>

截取一部分:

1
2
3
从第二个到第三个
<volist name='person' id='data' offset='1' length='2'>
</volist>

如果没有数据:

1
2
3
变量不存在时,输出empty中的内容
<volist name='person' id='data' empty='我没数据'>
</volist>

模板文件 \[应用名]\Home\View\Index\index.html

1
2
3
4
name='原变量名' item='循环中的变量名'
<foreach name='person' item='data'>
{$data['name']}-----{$data['age']} <br/>
</volist>

foreach 循环没有截取功能

for循环

  • eq = neq !=
  • gt > egt >=
  • lt < elt <=
  • heq === nheq !===
1
2
3
<for start='1' end='10' comparison='elt' name='k'>
{$k}
</for>

等同于:

1
2
3
for ($i = 0; $i < 10; $i++){
echo $i;
}

if 判断

1
2
3
4
<if condition='$num gt 10'> num 大于 10
<elseif condition='$num lt 10' /> num 小于 10
<else /> num 等于 10
</if>

别忘了 <elseif /><else /> 后面结束的/符号,如果没有则会继续运行到下一句(类似于break没写)。

switch 判断

1
2
3
4
5
<switch name='peopleName'>
<case value='laoshi'>老师</case>
<case value='xiaoming|xiaohong'>学生</case>
<default />谁都不是
</switch>

同样别忘了 <default /> 后面结束的/符号

比较标签

1
<比较标签 name='变量名' value='比较的值' > </比较标签>
1
2
3
4
5
6
7
8
9
<eq name='num' value='10'> num = 10 
<else /> num != 10
</eq>

<neq name='num' value='12'> num != 12 </neq>

<compare name='num' value='12' type='eq'> num = 12
<else /> num != 12
</compare>

区间标签

  • in 在区间里
  • notin 不在区间里
  • between 连续区间
  • notbetween 不在连续区间
  • range 只能替换 in 和 notin,不能替换 between
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<in name='num' value='1,2,3'> 在这个区间
<else /> 不在这个区间
</in>

<notin name='num' value='1,2,3'> 不在这个区间 </notin>

<between name='num' value='1,10'> 在这个区间
</between>

<notbetween name='num' value='1,10'> 不在这个区间
<else /> 居然在这个区间
</notbetween>

<range name='num' value='1,11,12' type='in'> 有11这个数
<else /> 没有11这个数
</range>

三元运算符

1
{$num > 10 ? '大于10' : '不大于10'}

案例实战

判断人物有没有成年

1
2
3
4
5
<foreach name='person' item='data'>
<egt name='data.age' value='18'>{$data.name}已经成年了
<else/>{$data.name}还是个孩子
</egt> <br/>
</foreach>

原生态PHP标签

官方推荐使用<php>而非<?php,因为后者可能会被屏蔽掉。

1
2
3
4
5
<html>
<php>
echo "Hello!";
</php>
</html>

ThinkPHP调试方法

开启调试模式 define('APP_DEBUG', true);

创建文件 \[应用名]\Conf\debug.php

1
2
3
4
5
<?php
return array(
'name'=>'WXY_Debug',
'SHOW_PAGE_TRACE'=>true, // 显示页面 trace 信息
);

控制 \Home\Controller\IndexController.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function index(){

}
public function user(){
echo C('name'); // 方法一:输出调试配置中的变量
trace('name', C('name')); // 方法三:trace中的调试 监视变量
dump($_SERVER); // 方法四:输出变量信息
{ // 方法五:输出运行时间(毫秒)
G('run');
for ($i = 0; $i < 100000; $i++) {
$count += $i;
}
echo G('run','end');
}
$this->displace(); // 方法二:开启trace后右下角会出现信息框
}
}

访问路径:ThinkPHP路径/index.php/Index/user

输出:WXY_Debug

define('APP_DEBUG', true);取消后,就不会输出了

同名变量会覆盖普通配置的变量

数据库

连接数据库

配置文件:\[应用名]\Common\Conf\config.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
return array(
/* ...... */
'DB_TYPE' => 'mysql', // 数据类型
'DB_HOST' => 'localhost', // 数据服务地址
'DB_NAME' => 'test', // 数据库名
'DB_USER' => 'root', // 数据库用户名
'DB_PWD' => 'root', // 数据库用户密码
'DB_PORT' => '3306', // 数据库端口
'DB_PREFIX' => 't_', // 数据库表前缀

// 开启主从读写分离
'DB_RW_SEPARATE' => true,
// 多个主数据库服务器
'DB_MASTER_NUM' => 2,
);

实例化模型

1、实例化基础模型

控制文件:[应用名]/Home/Controller/IndexController.class.php

1
2
3
4
5
6
7
8
9
10
11
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function index(){
// $user = new Model('notes'); // 这句不知道为什么会导致崩溃,只能用下面的M方法
$user = M('notes');
$data = $user->select();
dump($data); // 输出所有的信息(数组+映射格式)
}
}

2、实例化用户自定义模型

新建 Model 文件[应用名]/Home/Model/[名字]Model.class.php
格式要规范,例如:UserModel.class.php

1
2
3
4
5
6
7
8
<?php
namespace Home\Model;
use Think\Model; // 加上这两行,D('Model/User') 可以省略 'Model/',否则必须全路径
class UserModel extends Model {
public function getInfo() {
return "Hello World";
}
}

Think/Model 方法不存在 解决方法:

需要继承RelationModel,引入use Think\Model\RelationModel ;

1
2
3
4
> use Think\Model\RelationModel ;
> class UserModel extends RelationModel { /*...*/ }
>
>

Controller 文件

1
2
3
4
// $user = new UserModel(); // 要放到 Home\Controller 下才能用
$user = D('User');
$data = $user->select(); // 需要表存在
dump($data);

如果 Model 文件不存在,D()方法会自动执行M()方法,即构建基础Model

3、实例化公共模型

能进行一些通用的方法

新建 Model 文件[应用名]/Home/Model/CommonModel.class.php

1
2
3
4
5
6
7
8
<?php
namespace Home\Model;
use Think\Model\RelationModel;
class CommonModel extends RelationModel {
public function strMake($str) {
return md5(sha1(md5($str))); // 统一的字符串加密
}
}

Controller 文件

如果提示找不到 CommonModel,顶部加上声明:use Home\Model\CommonModel;

1
2
3
4
public function index(){
$user = new CommonModel();
echo $user->strMake('aaa');
}

用户自定义公共模型

自定义的XxxModel不要继承Model,继承CommonModelCommonModel再继承自RelationModel

4、实例化空模型

Controller 文件:

1
2
3
$model = M();
$model->query($sql); // 日常读取 select
$model->execute($sql); // 写入 update insert,返回修改行数(int)

CURD操作

插入数据

controller 文件

1
2
3
4
5
$data = array(
'username' => '',
'password' => '',
);
echo M('User')->add($data); // 返回所在行值

插入多条数据

addAll() 方法 (只适合MySQL)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$data = array(
0 => array(
'username' => '000',
'password' => '00',
),
1 => array(
'username' => '111',
'password' => '11',
),
2 => array(
'username' => '222',
'password' => '22',
),
);
echo M('User')->addAll($data); // 返回第一次添加的行数(int)

查询数据

M(‘User’)->select() 会查询两次:

  • Show columns from `user`
  • SELECT * from `user`

如果 debug 开启,那么每次 SELECT 前都会查询字段,并缓存起来。

1、带条件查询

1
$data = M('User')->where('id=1')->select();

2、多个条件查询

1
2
3
4
$where['id'] = 1;
$where['name'] = 'a';
$where['_logic'] = 'or'; // 默认where逻辑是 AND
$data = M('User')->where($where)->select();

3、表达式查询

1
2
3
4
5
6
7
8
$where['id'] = array('gt', 1); // id > 1
$data = M('User')->where($where)->select();

$where['id'] = array('not between', '1,8'); // id < 1 || id > 8

$where['username'] = array('like', '%a'); // name 以 a 结尾

$where['username'] = array('like', array('%e', 's%')); // e结尾 OR s开头

4、区间查询

1
2
$where['id'] = array(array('gt',1), array('lt',10)); // 1<id<10,默认 AND 运算
$where['id'] = array(array('lt',3), array('gt',100), 'or'); // id<3 OR id>100

5、混合用法

1
2
3
$where['id'] = array('gt', 10);
$where['_string'] = 'score > 10'; // 字符串方式(不建议用,容易被SQL注入)
$data = M('User')->where($where)->select();

尽量不要用字符串方式,表达式查询会进行字符串格式化,安全

6、统计用法

1
2
3
4
$data = M('User')->count();
dump($data); // 例如 100条结果 会输出: string(3) "100"

$data = M('User')->max('score'); // max min avg sum 都需要带一个参数

更新数据

1
2
3
4
5
6
7
8
$where['id'] = 1;
$data = M('User')->where($where)->select();
dump($data);

$update['score'] = 60;
$where['id'] = 1;
$data = M('User')->where($where)->save($update); // 千万不要写成 update()方法
dump($data); // 返回影响的行数

删除数据

1
2
3
$where['id'] = 1;
//$data = M('User')->where($where)->select();
echo M('User')->where($where)->delete(); // 输出1
1
echo M('User')->delete(3); // 主要传入主键值(也只能传主键值)

连贯操作

排序 order

1
$data = M('User')->order('score desc, id asc')->select(); // score从大到小,ID、从小到大

筛选数据 field

field($string, false);

  • 参数一 string 传入多个字段用英文逗号分开
  • 参数二默认为false,即只显示string中的字段;为true时只有string中的字段不显示
1
$data = M('User')->field('id, username')->select(); // 只显示 id 和 username

限制数据量 limit 和 page

1
2
$data = M('User')->limit(5)->select(); // 0 ~ 5,序号从0开始
$data = M('User')->limit(2, 5)->select(); // 2(第三条) ~ 5
1
2
$data = M('User')->page(1)->select(); // 第一页,参数二默认每页20条
$data = M('User')->page(1, 5)->select(); // 第一页,每页5条

分组 group 和 having

1
M('User')->field('score, count(*) as total')->group('score')->select(); // 每个成绩的数据数量

输出结果示例:

1
2
3
4
5
6
7
8
9
10
11
arrar(10) {
[0] => array(2) {
["score"] => string(2) "15"
["total"] => string(1) "6"
}
[1] => array(2) {
["score"] => string(2) "16"
["total"] => string(1) "3"
}
[2]...
}

having

1
2
3
4
5
M('User')
->field('score, count(*) as total')
->having('score >= 20')
->group('score')
->select(); // 超过20分的成绩的数据数量

多表查询

table 方法

table(array('完整表名'=>'别名'))

如果有前缀,表名一定要加前缀(M方法中的表名参数不用加config中设置的前缀)

1
2
3
M()->table(array('tb_user'=>'user', 'tb_info'=>'info'))
->where('user.id=info.userid')
->select(); // tb_ 为表前缀

join 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
M('User')
->join('tb_info on tb_info.userid=tb_user.id')
->select(); // 默认是左关联,即tb_info中有但tb_user中没有这条信息,也会查出来,此时相当于tb_user中的内容为空

M('User')
->join('Right join tb_info on tb_info.userid=tb_user.id')
->select(); // 右关联

M('User')
->join(/**/)
->join(/**/)
->select(); // 使用多个join关联

M('User')
->join(array('tb_info on tb_info.userid=tb_user.id', '/**/', '/**/'))
->select(); // 使用join数组关联

union 方法

union(string/array, false/true)

  • 参数二默认false,使用union查询;true 为uni查询(去掉重复结果)
1
2
3
4
5
6
7
8
9
10
M('User')
->field('username', 'id')
->union('select username, id from tb_user2')
->select(); // 查询 tb_user 和 tb_user2 中的 username 和 id

M('User')
->field('username')
->union(array('field'=>'username', 'table'=>'tb_user2'))
->union(array('field'=>'username', 'table'=>'tb_user3'))
->select();

查询字段的类型(包括field和union中的顺序)必须是一样的

过滤查询 distinct

1
$data = M('User')->distinct(true)->field('score')->order('score asc')->select(); // 参数 true 为过滤

关于命名范围的使用

查询分数超过60的

[应用名]/Home/Model/UserModel.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
namespace Home\Model;
use Think\Model\RelationModel;
class UserModel extends CommonModel {
protected $_scope = array(
/*'命名范围的标识名' => array(
'属性' => '值',
支持的方法有:where limit field order table page having group distinct
)*/

'jige' => array(
'where' => array(
'score'=>array('egt', 60),
),
'order' => 'id asc',
),

'ziduan' => array(
'field' => 'nickname, score'
)
);
}

[应用名]/Home/Controller/IndexController.class.php

1
2
3
4
5
6
public function fanwei()
{
$user = D('User');
$data = $user->scope('jige', 'ziduan')->select();
echo M()->getLastSql();
}

如果两个命名范围冲突(如一个 limit 10,另一个 limit 5,则后面的会覆盖前面的),没冲突(例如 where 条件)则会用 AND 连接