关键词搜索

源码搜索 ×
×

PHP笔记-自定义MVC框架

发布2022-01-10浏览1452次

详情内容

膜拜

膜拜下黑马大佬程序员的项目,学习到了这样的手写MVC框架的方式,受益匪浅,感觉自己在C/C++和Java方面,还有许多要学习的地方,看看能不能抄下这个php自己撸一个C/C++的MVC框架。

下面记录下PHP自定义MVC框架

项目结构

首先是项目结构:

  1. ├─app
  2. │ ├─admin
  3. │ │ ├─controller
  4. │ │ ├─model
  5. │ │ └─view
  6. │ └─home
  7. ├─config
  8. ├─core
  9. ├─public
  10. ├─resources
  11. │ └─views
  12. └─vendor
  13. └─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:

  1. <?php
  2. define("ROOT_PATH", str_replace("\\", "/", dirname(__DIR__)) . "/");
  3. include ROOT_PATH . "core/App.php";
  4. \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中的代码:

  1. <?php
  2. namespace core;
  3. use PDO;
  4. use PDOException;
  5. class Dao{
  6. protected $pdo;
  7. public function __construct($databaseInfo = array()){
  8. $type = $databaseInfo["type"] ?? "mysql";
  9. $host = $databaseInfo["host"] ?? "localhost";
  10. $port = $databaseInfo["port"] ?? "3306";
  11. $user = $databaseInfo["user"] ?? "root";
  12. $password = $databaseInfo["password"] ?? "";
  13. $dbName = $databaseInfo["dbName"] ?? "";
  14. $charset = $databaseInfo["charset"] ?? "utf8";
  15. try{
  16. $dsn = $type . ":host=" . $host . ";port=" . $port . ";dbname=" . $dbName . ";charset=" . $charset;
  17. $this->pdo = new PDO($dsn, $user, $password);
  18. }
  19. catch (PDOException $e){
  20. echo "数据库连接失败!<br/>";
  21. echo "错误文件为:" . $e->getFile() . "<br/>";
  22. echo "错误行号为:" . $e->getLine() . "<br/>";
  23. echo "错误描述为:" . $e->getMessage();
  24. exit;
  25. }
  26. }
  27. public function daoQuery($sql): array{
  28. try{
  29. $stmt = $this->pdo->query($sql);
  30. return $stmt->fetchAll(PDO::FETCH_ASSOC);
  31. }
  32. catch (PDOException $e){
  33. $this->daoException(e);
  34. }
  35. }
  36. public function daoExec($sql){
  37. try{
  38. return $this->pdo->exec($sql);
  39. }
  40. catch (PDOException $e){
  41. $this->daoException($e);
  42. }
  43. }
  44. private function daoException($e){
  45. echo "SQL执行错误!<br/>";
  46. echo "错误文件为:" . $e->getFile() . "<br/>";
  47. echo "错误行号为:" . $e->getLine() . "<br/>";
  48. echo "错误描述为:" . $e->getMessage();
  49. exit;
  50. }
  51. }

这里使用了PDO连接了MySQL数据库,配置文件从$config中读取。直接对数据库进行增删改查。

而Model.php

  1. <?php
  2. namespace core;
  3. class Model{
  4. protected $dao;
  5. protected $tableName;
  6. protected $fields;
  7. public function __construct(){
  8. global $config;
  9. $this->dao = new Dao($config["database"]);
  10. $this->getFields();
  11. }
  12. protected function getFields(){
  13. $sql = "DESC {$this->tableName}";
  14. $rows = $this->query($sql);
  15. foreach($rows as $row){
  16. $this->fields[] = $row["Field"];
  17. if($row["Key"] == "PRI"){
  18. $this->fields["Key"] = $row["Field"];
  19. }
  20. }
  21. }
  22. protected function query($sql): array{
  23. return $this->dao->daoQuery($sql);
  24. }
  25. protected function exec($sql){
  26. return $this->dao->daoExec($sql);
  27. }
  28. public function autoInsert($data){
  29. $keys = $values = "";
  30. foreach($this->fields as $k => $v) {
  31. if($k == "Key")
  32. continue;
  33. if(array_key_exists($v, $data)){
  34. $keys .= $v . ",";
  35. $values .= "'" . $data[$v] . "'.";
  36. }
  37. }
  38. $keys = rtrim($keys, ",");
  39. $values = rtrim($values, ",");
  40. $sql = "insert into{$this->tableName}({$keys}) values({$values})";
  41. return $this->exec($sql);
  42. }
  43. public function deleteById($id){
  44. if(!isset($this->fields["Key"])){
  45. return false;
  46. }
  47. $sql = "delete from {$this->tableName} where {$this->fields["Key"]} = '{$id}'";
  48. return $this->exec($sql);
  49. }
  50. public function autoUpdate($id, $data){
  51. $where = " where {$this->fields['Key']} = '{$id}'";
  52. $sql = "update {$this->tableName} set";
  53. foreach ($data as $Key => $value){
  54. $sql .= $Key . "='" . $value . "',";
  55. }
  56. $sql = rtrim($sql, ",") . $where;
  57. return $this->exec($sql);
  58. }
  59. public function getById($id){
  60. if(!isset($this->fields["Key"])){
  61. return false;
  62. }
  63. $sql = "select * from {$this->tableName} where {$this->fields['Key']} = '{$id}'";
  64. return $this->query($sql);
  65. }
  66. }

 很好理解,在此不再说明。

Controller和Model

这里要看Controller的子类StudentController.php和StudentModel.php

StudentModel.php

  1. <?php
  2. namespace home\model;
  3. use core\Model;
  4. class StudentModel extends Model{
  5. protected $tableName = "student";
  6. public function getAllStudents($page = 1, $pageCount = 5): array{
  7. $offset = ($page - 1) * $pageCount;
  8. $sql = "select id, name, age, sex, email, address, updateTime from {$this->tableName} order by id asc"
  9. . " limit {$offset}, {$pageCount}";
  10. return $this->query($sql);
  11. }
  12. public function getCounts(): int{
  13. $sql = "select count(*) as c from {$this->tableName}";
  14. $res = $this->query($sql);
  15. return $res["c"] ?? 0;
  16. }
  17. }

 从中调用了Dao层,查询数据。

StudentController.php

  1. <?php
  2. namespace admin\controller;
  3. use core\Controller;
  4. use home\model\StudentModel;
  5. class StudentController extends Controller{
  6. public function student(){
  7. $page = $_REQUEST["page"] ?? 1;
  8. $studentModel = new StudentModel();
  9. $students = $studentModel->getAllStudents($page);
  10. $counts = $studentModel->getCounts();
  11. $this->assign("students", $students);
  12. $this->assign("counts", $counts);
  13. $this->display("student.html");
  14. }
  15. }

 展示数据给前端

Controller和smarty和model

Controller.php代码:

  1. <?php
  2. namespace core;
  3. class Controller{
  4. protected $smarty;
  5. public function __construct(){
  6. include VENDOR_PATH . "smarty/Smarty.class.php";
  7. $this->smarty = new \Smarty();
  8. $this->smarty->template_dir = APP_PATH . P . "/view/";
  9. $this->smarty->compile_dir = RESOURCES_PATH . "views";
  10. }
  11. protected function assign($name, $value = ""){
  12. $this->smarty->assign($name, $value);
  13. }
  14. protected function display($template = ""){
  15. try {
  16. $this->smarty->display($template);
  17. }
  18. catch (\SmartyException | \Exception $e){
  19. $e->getMessage();
  20. }
  21. }
  22. }

通过浏览器出入过来的P,来判断是前台还是后台的view,方便进行display。编译完成的前端放到RESOURCES_PATH . "views"中。

数据库

数据结构:

内容:

结束

程序运行截图:

框架源码打包下载地址:

PHP/IT1995Blog at main · fengfanchen/PHP · GitHub

相关技术文章

点击QQ咨询
开通会员
返回顶部
×
微信扫码支付
微信扫码支付
确定支付下载
请使用微信描二维码支付
×

提示信息

×

选择支付方式

  • 微信支付
  • 支付宝付款
确定支付下载