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:
<?phpdefine("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
<?phpnamespace 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
<?phpnamespace 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
<?phpnamespace 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代碼:
<?phpnamespace 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/IT1995Blog at main · fengfanchen/PHP · GitHub
總結
以上是生活随笔為你收集整理的PHP笔记-自定义MVC框架的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数仓dw怎么建_从0建设离线数据仓库
- 下一篇: PHP笔记-学生成绩例子