PHP笔记-自定义MVC框架
Posted IT1995
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PHP笔记-自定义MVC框架相关的知识,希望对你有一定的参考价值。
膜拜
膜拜下黑马大佬程序员的项目,学习到了这样的手写MVC框架的方式,受益匪浅,感觉自己在C/C++和Java方面,还有许多要学习的地方,看看能不能抄下这个php自己撸一个C/C++的MVC框架。
下面记录下PHP自定义MVC框架。
项目结构
首先是项目结构:
├─app
│ ├─admin
│ │ ├─controller
│ │ ├─model
│ │ └─view
│ └─home
├─config
├─core
├─public
├─resources
│ └─views
└─vendor
└─smarty
解释下:
①app中放业务逻辑,admin代表后台方面的,home代表前台方面的,前台后台都有MVC相关的文件夹;
②config中放了配置文件,一般放与数据库连接相关的配置;
③core中放了核心代码,如管理url、文件路径、加载php中各种类,模型父类,控制父类,Dao层,等一系列主要核心功能;
④public中一般放index.php为整个项目的入口,通过输入的url中的各个参数,调用不同的php类(前台还是后台,哪个controller,哪个函数);
⑤resources中有个views存放smarty编译好的页面;
⑥vendor中存放三方的库,如smarty或验证码或分页或上传文件的php类。
整体运行逻辑
这个程序的切入点为public中的index.php:
从中可以看到index.php:
<?php
define("ROOT_PATH", str_replace("\\\\", "/", dirname(__DIR__)) . "/");
include ROOT_PATH . "core/App.php";
\\core\\App::start();
解释下:
①其中__DIR__为当前文件的路径,如我这里的D:\\phpProject\\IT1995Blog\\public;
②dirname()函数的作用是获取父目录的路径,也就是是把ROOT_PATH定义为了D:\\phpProject\\IT1995Blog/,再一个str_replace后变为D:/phpProject/IT1995Blog/;
③然后include ROOT_PATH . "core/App.php"就可以获取到正确的App.php路径了,然后就可以调用他的start()静态方法了。
下面进入App看下start()
这里一共调用了5个解释下:
①setPath()函数中设置了项目中文件的各个路径,生成了各个路径的宏,如core_path、app_path、config_path等
②setConfig(),通过setPath()生成的宏,生成配置文件的全局变量。
③setUrl(),浏览器的url里面一般有3个值,一个是p,一个是c,一个是a,他们在程序中都有默认值,可以缺省。他们分别代表p为前台还是后台(home Or admin),c为控制(如调用Student程序中会给他加个Controller),a为行为也就是这个controller的哪个函数。
④setAutoLoadFile(),里面会调用spl_autoload_register为自动注册函数,第一个参数为回调函数,调用是这样的
其中可以看到,__CLASS__为当前的class名,这里是core\\App,这个参数的意思是,代表回调到core\\App这个类的,setAutoLoadFile函数。每当有对象被new的时候,就会调用到setAutoLoadFile函数,他的参数是$class,后面我们再来说这个。
⑤setDispath()是则有的。
可知他的P为前台还是后台(home or admin), C为controller,如Student,A为函数,代表调用的Student中的方法,这里创建了一个controller,然后调用了对应的方法。最后返回数据,这就是大体的逻辑。
自动注册
在上面的setAutoLoad中调用了spl_autoload_register,在setDispatch中new中了对象,所以会调用setAutoLoadFile这个回调函数。比如调用的是StudentController,这个StudentController父类是Controller。
PHP的继承中构造函数和C/C++、Java的不一样,很奇特。后面会有博文说明,比如下面这个url:
http://localhost:63343/IT1995Blog/public/index.php?p=admin&c=Student&a=student
调用的是后台(admin),StudentController(c为Student),的student(a为student)函数。
程序结构如下:
那么他的自动注册了如下类。
admin\\controller\\StudentController
core\\Controller(StudentController中继承了他,所以他会用这个)
Smarty_Autoloader(Controller中new了这个)
core\\Model(Controller调用了这个)
core\\Dao(Model中使用了Dao,Dao层直接查了数据库)
回调函数代码如下:
Model和Dao
先看下Dao中的代码:
<?php
namespace core;
use PDO;
use PDOException;
class Dao
protected $pdo;
public function __construct($databaseInfo = array())
$type = $databaseInfo["type"] ?? "mysql";
$host = $databaseInfo["host"] ?? "localhost";
$port = $databaseInfo["port"] ?? "3306";
$user = $databaseInfo["user"] ?? "root";
$password = $databaseInfo["password"] ?? "";
$dbName = $databaseInfo["dbName"] ?? "";
$charset = $databaseInfo["charset"] ?? "utf8";
try
$dsn = $type . ":host=" . $host . ";port=" . $port . ";dbname=" . $dbName . ";charset=" . $charset;
$this->pdo = new PDO($dsn, $user, $password);
catch (PDOException $e)
echo "数据库连接失败!<br/>";
echo "错误文件为:" . $e->getFile() . "<br/>";
echo "错误行号为:" . $e->getLine() . "<br/>";
echo "错误描述为:" . $e->getMessage();
exit;
public function daoQuery($sql): array
try
$stmt = $this->pdo->query($sql);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
catch (PDOException $e)
$this->daoException(e);
public function daoExec($sql)
try
return $this->pdo->exec($sql);
catch (PDOException $e)
$this->daoException($e);
private function daoException($e)
echo "SQL执行错误!<br/>";
echo "错误文件为:" . $e->getFile() . "<br/>";
echo "错误行号为:" . $e->getLine() . "<br/>";
echo "错误描述为:" . $e->getMessage();
exit;
这里使用了PDO连接了MySQL数据库,配置文件从$config中读取。直接对数据库进行增删改查。
而Model.php
<?php
namespace core;
class Model
protected $dao;
protected $tableName;
protected $fields;
public function __construct()
global $config;
$this->dao = new Dao($config["database"]);
$this->getFields();
protected function getFields()
$sql = "DESC $this->tableName";
$rows = $this->query($sql);
foreach($rows as $row)
$this->fields[] = $row["Field"];
if($row["Key"] == "PRI")
$this->fields["Key"] = $row["Field"];
protected function query($sql): array
return $this->dao->daoQuery($sql);
protected function exec($sql)
return $this->dao->daoExec($sql);
public function autoInsert($data)
$keys = $values = "";
foreach($this->fields as $k => $v)
if($k == "Key")
continue;
if(array_key_exists($v, $data))
$keys .= $v . ",";
$values .= "'" . $data[$v] . "'.";
$keys = rtrim($keys, ",");
$values = rtrim($values, ",");
$sql = "insert into$this->tableName($keys) values($values)";
return $this->exec($sql);
public function deleteById($id)
if(!isset($this->fields["Key"]))
return false;
$sql = "delete from $this->tableName where $this->fields["Key"] = '$id'";
return $this->exec($sql);
public function autoUpdate($id, $data)
$where = " where $this->fields['Key'] = '$id'";
$sql = "update $this->tableName set";
foreach ($data as $Key => $value)
$sql .= $Key . "='" . $value . "',";
$sql = rtrim($sql, ",") . $where;
return $this->exec($sql);
public function getById($id)
if(!isset($this->fields["Key"]))
return false;
$sql = "select * from $this->tableName where $this->fields['Key'] = '$id'";
return $this->query($sql);
很好理解,在此不再说明。
Controller和Model
这里要看Controller的子类StudentController.php和StudentModel.php
StudentModel.php
<?php
namespace home\\model;
use core\\Model;
class StudentModel extends Model
protected $tableName = "student";
public function getAllStudents($page = 1, $pageCount = 5): array
$offset = ($page - 1) * $pageCount;
$sql = "select id, name, age, sex, email, address, updateTime from $this->tableName order by id asc"
. " limit $offset, $pageCount";
return $this->query($sql);
public function getCounts(): int
$sql = "select count(*) as c from $this->tableName";
$res = $this->query($sql);
return $res["c"] ?? 0;
从中调用了Dao层,查询数据。
StudentController.php
<?php
namespace admin\\controller;
use core\\Controller;
use home\\model\\StudentModel;
class StudentController extends Controller
public function student()
$page = $_REQUEST["page"] ?? 1;
$studentModel = new StudentModel();
$students = $studentModel->getAllStudents($page);
$counts = $studentModel->getCounts();
$this->assign("students", $students);
$this->assign("counts", $counts);
$this->display("student.html");
展示数据给前端
Controller和smarty和model
Controller.php代码:
<?php
namespace core;
class Controller
protected $smarty;
public function __construct()
include VENDOR_PATH . "smarty/Smarty.class.php";
$this->smarty = new \\Smarty();
$this->smarty->template_dir = APP_PATH . P . "/view/";
$this->smarty->compile_dir = RESOURCES_PATH . "views";
protected function assign($name, $value = "")
$this->smarty->assign($name, $value);
protected function display($template = "")
try
$this->smarty->display($template);
catch (\\SmartyException | \\Exception $e)
$e->getMessage();
通过浏览器出入过来的P,来判断是前台还是后台的view,方便进行display。编译完成的前端放到RESOURCES_PATH . "views"中。
数据库
数据结构:
内容:
结束
程序运行截图:
框架源码打包下载地址:
以上是关于PHP笔记-自定义MVC框架的主要内容,如果未能解决你的问题,请参考以下文章