网页从静态到MVC的进化史

一、静态网站
(1个html => 1个页面)
************************************
1. 纯静态页面
举例:
1.html

二、动态网站
(数据库中的1条记录 => 1个网页)
************************************
1. 面向过程,写一个文章管理、发布功能。
(请求:1个php页面 => 1个php模块)
举例:
article.php?id=1
// php代码和html混合式开发:1个php页面,写1次「数据库的天龙八部」。

2. 面向对象:使用Model类,翻页类,模板引擎实现文章管理、发布功能。
(自己封装类,提升代码的可重用性)
举例:
article.php?id=1
// 第一步:使用require_once(‘./lib/Model.class.php’) 引入Model类。引入数据库的配置文件。
// 第二步:实例化一个类,$model = new Model(); 准备好SQL语句:$sql=’SELECT * FROM `article`’。
// 第三步:调用Model类中的方法:$ret = $model->select($sql)。返回查询的结果集。

3. 使用组件式开发:
(自己写太麻烦,使用第三方插件,不重复造轮子了… (*^__^*) 嘻嘻……)
如:数据库、验证码组件:

  • catfan/medoo
  • gregwar/captcha

4. MVC设计模式

  • 模型
  • 视图
  • 控制器

(单一入口,在入口文件引入使用到的类,使用命名空间防止类名重复。加入路由负责请求url映射,请求1个页面,实际上是访问1个函数)

举例:
// 第一步:请求文章页面 ../article/1
// 第二步:URL映射到:article控制器,访问showarticle(1)方法传递了参数:文章id
// 第三步:在方法中,调用了Model模型层,获取结果集。
// 第四步:将返回的结果集,传递给视图层,组装成HTML的字符串。
// 第五步:响应:把HTML字符串传送给浏览器

5. 框架

  • Laravel
  • YII
  • ThinkPHP

集成了一些:管理工具
如:包管理工具、数据库迁移、命令行工具
组件:请求、响应

第一个MVC框架

一、MVC基础知识
1、MVC 是单入口,单入口文件index.php, 引入大量类文件,定义一些常量。
2、通过入口文件,实例化一个控制器类,调用控制器类中的方法。
3、运行过程:通过在访问的控制器中的方法里面,去拿数据(访问Model层中方法),然后把数据传给视图层(模板引擎,view层), 最终通过控制器调用显示(show)的方法。

index.php?controller=控制器名&method=方法名

\index.php

Select Code
1
2
3
4
5
6
7
<?php
require_once('testModel.class.php');	// 引入模型
require_once('testView.class.php');		// 引入视图
require_once('testController.class.php');	// 引入控制器

$obj = new testController();
$obj->show();

\testModel.class.php

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class testModel
{
    public function get()
    {
        return 'hello mvc';
    }

    public function delete()
    {
//
    }
}

\testView.class.php

Select Code
1
2
3
4
5
6
7
8
<?php
class testView
{
    public function display($data)
    {
    	echo $data;
    }
}

\testController.class.php

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
<?php
class testController
{
    public function show()
    {
        $model = new testModel();
        $data = $model->get();

        $view = new testView();
        $view->display($data);
    }
}

二、第一个MVC框架
\functions.php

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
function C($name, $method)
{
    require_once('./libs/Controller/' . $name . 'Controller.class.php');
    eval('$obj = new ' . $name . 'Controller(); $obj->' . $method . '();');
}

function M($name)
{
    require_once('./libs/Model/' . $name . 'Model.class.php');
    eval('$obj = new ' . $name . 'Model();');
    return $obj;
}

function V($name)
{
    require_once('./libs/View/' . $name . 'View.class.php');
    eval('$obj = new ' . $name . 'View();');
    return $obj;
}

\index.php

Select Code
1
2
3
4
5
6
7
8
9
10
<?php
include('./functions.php');

// index.php?controller=控制器&method=方法
// index.php?controller=test&method=list

$controller = $_GET['controller'] ? $_GET['controller'] : 'index';
$method = $_GET['method'] ? $_GET['method'] : 'index';

C($controller, $method);

\libs\Controller\testController.class.php

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
class testController
{
    public function show()
    {
        $model = M('test');
        $data = $model->get();

        $view = V('test');
        $view->display($data);
    }

    public function list()
    {
        echo 'list方法';
    }

    public function detail()
    {
        echo '详情页面';
    }
}

\libs\Model\testModel.class.php

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
class testModel extends \PDO
{
    public function __construct()
    {
        $dsn = 'mysql:dbname=web;host=localhost;charset=UTF8';
        $username = 'root';
        $passwd = 'root';
        parent::__construct($dsn, $username, $passwd);
    }

    public function get()
    {
        $sql = "SELECT * FROM `cat`";
        $data = $this->query($sql);
        return $data->fetchAll();
    }

    public function delete()
    {
//
    }
}

\libs\View\testView.class.php

Select Code
1
2
3
4
5
6
7
8
<?php
class testView
{
    public function display($data)
    {
        var_dump($data);
    }
}

自定义一个模板引擎类的5步发展史

##第1版:代码混编(PHP和HTML混编)

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<table border="1" width="350">
	<caption><h2>用户表</h2></caption>
	<tr>
		<th>ID</th>
		<th>Username</th>
		<th>AddTime</th>
	</tr>
	<?php
		/*第1版:代码混编*/
		$link = mysqli_connect('127.0.0.1', 'root', '', 'ishop') or die("数据库连接失败:".mysqli_error());
		mysqli_set_charset($link, 'utf8');

		$sql = "SELECT * FROM `shop_user`";
		$res = mysqli_query($link, $sql);

		if($res){
			while($row = mysqli_fetch_assoc($res)){
				echo "<tr>";
				echo "<td>{$row['id']}</td>";
				echo "<td>{$row['username']}</td>";
				echo "<td>".date('Y-m-d H:i:s', $row['addtime'])."</td>";
				echo "</tr>";
			}
		}

		mysqli_close($link);
	?>
</table>

##第2版:高级分离。页面顶部放PHP代码处理业务逻辑

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
	$link = mysqli_connect('127.0.0.1', 'root', '', 'ishop') or die("数据库连接失败:".mysqli_error());
	mysqli_set_charset($link, 'utf8');

	$sql = "SELECT * FROM `shop_user`";
	$res = mysqli_query($link, $sql);

	if($res){
		while($row = mysqli_fetch_assoc($res)){
			$userlist[] = $row;
		}
	}
?>

<!-- 省略部分html代码 -->
<table border="1" width="350">
	<caption><h2>用户表</h2></caption>
	<tr>
		<th>ID</th>
		<th>Username</th>
		<th>AddTime</th>
	</tr>
	<?php foreach($userlist as $v): ?>
		<tr>
			<td><?php echo $v['id'] ?></td>
			<td><?php echo $v['username'] ?></td>
			<td><?php echo $v['addtime'] ?></td>
		</tr>
	<?php endforeach; ?>
</table>
<!-- 省略部分html代码 -->

<?php mysqli_close($link); ?>

##第3版:PHP和HTML页面分离技术, 使用include引入模板文件

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
	/* 第3版:PHP和HTML页面分离技术, 使用include引入模板文件 */
	$link = mysqli_connect('127.0.0.1', 'root', '', 'ishop') or die("数据库连接失败:".mysqli_error());
	mysqli_set_charset($link, 'utf8');

	$sql = "SELECT * FROM `shop_user`";
	$res = mysqli_query($link, $sql);

	if($res){
		while($row = mysqli_fetch_assoc($res)){
			$userlist[] = $row;
		}
	}
	
	// 使用include引入模板文件
	include './test.tpl';

	mysqli_close($link);
?>

问题:所有的变量都可以在模板文件中输出。

解决问题:实现PHP和页面代码的分离

1. PHP分配数据给HTML页面(使用对象来传递变量和显示模板,可以解决数据安全问题)
2. HTML页面,由美工负责,不能出现PHP代码

##第4版:封装1个模板类,使用对象的方式分配数据和显示模板

PHP文件:引入模板引擎类,传递变量

Select Code
1
2
3
4
5
6
7
8
// 引入模板引擎类
include './MySmarty.class.php';

// 创建1个模板对象
$tpl = new MySmarty();

$tpl->assign('userlist', $userlist);
$tpl->display('./test.tpl');

模板引擎类:

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MySmarty
{
	// 定义一个数组存储变量
	public $data = array();

	public function assign($key, $val)
	{
		$this->data[$key] = $val;
	}

	public function display($tpl)
	{
		// var_dump($this->data);    //测试$data是否存储了传过来的变量
		foreach ($this->data as $key => $val) {
			// 把数组里的值$data['userlist'],传递到该方法内$$key等同于$userlist
			$$key = $val;
		}

		// 引入模板,遍历$userlist的数组
		include $tpl;
	}
}

##第5版:完成模板文件的编译过程

PHP文件:

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
	/* 第5版:完成模板文件的编译过程 */

	// 引入模板引擎类
	include './MySmarty.class.php';

	// 创建1个模板对象
	$tpl = new MySmarty();
	$title = "这是一个测试标题";

	$tpl->assign('title', $title);
	$tpl->display('./test.tpl');
?>

模板引擎类:

这里完成了一个简单的编译,把模板中的变量{$title}编译成php代码:

Select Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php 
	class MySmarty
	{
		// 定义一个数组存储变量
		public $data = array();

		public function assign($key, $val)
		{
			$this->data[$key] = $val;
		}

		public function display($tpl)
		{
			foreach ($this->data as $key => $val) {
				$$key = $val;
			}

			// 模板编译
			$filename = $this->compile($tpl);

			// 引入模板,遍历$userlist的数组
			include $filename;
		}

		// 这里进行一个简单的编译,把{$title}编译成php代码(部分省略): php echo $title
		public function compile($tpl)
		{
			$tpl_String = file_get_contents($tpl);

			$preg = "/\{(.*?)\}/";
			$match = "<?php echo $1; ?>";

			// 文件后缀随便取名
			$filename = './template_c/'.md5($tpl).".php";

			$new_String = preg_replace($preg, $match, $tpl_String);
			
			//如果目录不存在则,创建编译后的存放目录
			if (!file_exists('./template_c/')) mkdir('./template_c/');
			file_put_contents($filename, $new_String);

			return $filename;
		}
	}

模板文件:

Select Code
1
2
3
4
5
6
7
8
9
//编译前:test.tpl
<body>
	<?php echo $title ?>
</body>

//编译后:aa757471baa99de5a5035424b8cad130.php
<body>
	<?php echo $title ?>
</body>

完成编译后的代码,实际上还是PHP代码,然后通过display()方法,显示给前台。

以上就是模板引擎的5步发展史,对于理解Smarty等模板引擎有学习的帮助。

源码:https://github.com/webjust/case-study