Heim >Backend-Entwicklung >PHP-Tutorial >Verwenden Sie PHP, um Ihr eigenes MVC-Framework zu erstellen
1. Was ist MVC?
MVC-Muster (Model-View-Controller) ist ein Software-Architekturmuster in der Softwareentwicklung. Das Softwaresystem ist in drei grundlegende Teile unterteilt: Modell (Modell) und Ansicht (Ansicht). ) und Controller (Controller).
Der Zweck des MVC-Musters besteht darin, einen dynamischen Programmentwurf zu implementieren, nachträgliche Änderungen und Erweiterungen des Programms zu vereinfachen und die Wiederverwendung bestimmter Programmteile zu ermöglichen. Darüber hinaus macht dieser Modus die Programmstruktur intuitiver, indem er die Komplexität vereinfacht. Das Softwaresystem trennt seine Grundteile und gibt jedem Grundteil auch die ihm zustehenden Funktionen. Fachleute können nach ihrem eigenen Fachwissen gruppiert werden:
(Controller) – Verantwortlich für die Weiterleitung von Anfragen und deren Bearbeitung.
(Ansicht) – Interface-Designer entwerfen grafische Schnittstellen.
(Modell) – Programmierer schreiben die Funktionen, die das Programm haben sollte (Implementierung von Algorithmen usw.), und Datenbankexperten führen die Datenverwaltung und den Datenbankentwurf durch (können bestimmte Funktionen realisieren).
Modell „Datenmodell“ (Modell) wird verwendet, um Daten im Zusammenhang mit der Geschäftslogik der Anwendung und der Verarbeitung der Daten zu kapseln. Ein „Modell“ hat direkten Zugriff auf Daten, beispielsweise eine Datenbank. Das „Modell“ hängt nicht von der „Ansicht“ und dem „Controller“ ab, das heißt, dem Modell ist es egal, wie es angezeigt oder manipuliert wird. Allerdings werden Datenänderungen im Modell im Allgemeinen über einen Aktualisierungsmechanismus angekündigt. Um diesen Mechanismus zu implementieren, müssen die zur Überwachung dieses Modells verwendeten Ansichten vorab in diesem Modell registriert werden, damit die Ansichten die im Datenmodell aufgetretenen Änderungen verstehen können.
Ansicht Die Ansichtsebene ermöglicht die gezielte Anzeige von Daten (theoretisch ist dies nicht erforderlich). Ansichten weisen im Allgemeinen keine Verfahrenslogik auf. Um die Aktualisierungsfunktion für die Ansicht zu implementieren, muss die Ansicht auf das von ihr überwachte Datenmodell (Modell) zugreifen und sollte daher vorab bei den von ihr überwachten Daten registriert werden.
Controller (Controller) Der Controller spielt eine organisatorische Rolle zwischen verschiedenen Ebenen und wird verwendet, um den Ablauf der Anwendung zu steuern. Es verarbeitet Ereignisse und reagiert. Zu den „Ereignissen“ zählen Nutzerverhalten und Änderungen im Datenmodell.
2. Warum sollten Sie Ihr eigenes MVC-Framework entwickeln?
Im Internet gibt es eine große Anzahl hervorragender MVC-Frameworks Lösung, aber betrachten Sie es als eine großartige Gelegenheit, PHP von innen zu erlernen, und dabei lernen Sie etwas über objektorientierte Programmierung und Designmuster sowie einige der Vor- und Nachteile einer Öffnung.
Darüber hinaus haben Sie die volle Kontrolle über Ihr Framework und integrieren Ihre Ideen in das von Ihnen entwickelte Framework. Obwohl es nicht unbedingt gut gemacht ist, können Sie Funktionen und Module auf Ihre eigene Art und Weise entwickeln.
3. Beginnen Sie mit der Entwicklung Ihres eigenen MVC-Frameworks
Bevor wir mit der Entwicklung beginnen, legen wir zunächst das Projekt fest, das wir erstellen. Anschließend ist der nächste Schritt das Einrichten der Verzeichnisstruktur Erste.
Obwohl alle oben genannten Verzeichnisse in diesem Tutorial nicht verwendet werden, ist es für die zukünftige Skalierbarkeit des Programms sehr nützlich, das Programmverzeichnis zu Beginn festzulegen . Notwendig. Lassen Sie uns im Detail über die Rolle jedes Verzeichnisses sprechen:
application – zum Speichern des Programmcodes
config – zum Speichern der Programmkonfiguration oder Datenbankkonfiguration
db – zum Speichern der Datenbanksicherung Inhalt
Bibliothek – speichert Framework-Code
öffentlich – speichert statische Dateien
Skripte – speichert Befehlszeilentools
tmp – speichert temporäre Daten
Nachdem das Verzeichnis eingerichtet ist, folgen wir einigen Code-Spezifikationen:
MySQL-Tabellennamen müssen in Kleinbuchstaben und im Plural geschrieben werden, z. B. Artikel, Autos
Modulnamen (Modelle) müssen dies tun Schreiben Sie den ersten Buchstaben groß und verwenden Sie den Singularmodus, z. B. Item, Car
Controller müssen den ersten Buchstaben großschreiben, die Pluralform verwenden und „Controller“ zum Namen hinzufügen, z. B. ItemsController, CarsController
Ansichten (Ansichten) verwenden die Pluralform und fügen das Verhalten am Ende als Datei hinzu, z. B.: items/view.php, car/buy.php
上述的一些规则是为了能在程序钟更好的进行互相的调用。接下来就开始真正的编码了。
第一步将所有的的请求都重定向到public目录下,解决方案是在todo文件下添加一个.htaccesss文件,文件内容为:
<IfModule mod_rewrite.c> RewriteEngine on RewriteRule ^$ public/ [L] RewriteRule (.*) public/$1 [L] </IfModule>
在我们把所有的请求都重定向到public目录下以后,我们就需要将所有的数据请求都再重定向到public下的index.php文件,于是就需要在public文件夹下也新建一个.htaccess文件,文件内容为:
<IfModule mod_rewrite.c> RewriteEngine On #如果文件存在就直接访问目录不进行RewriteRule RewriteCond %{REQUEST_FILENAME} !-f #如果目录存在就直接访问目录不进行RewriteRule RewriteCond %{REQUEST_FILENAME} !-d #将所有其他URL重写到 index.php/URL RewriteRule ^(.*)$ index.php?url=$1 [PT,L] </IfModule>
这么做的主要原因有:
可以使程序有一个单一的入口,将所有除静态程序以外的程序都重定向到index.php上;
可以用来生成利于SEO的URL,想要更好的配置URL,后期可能会需要URL路由,这里先不做介绍了。
做完上面的操作,就应该知道我们需要做什么了,没错!在public目录下添加index.php文件,文件内容为:
<?php define('DS',DIRECTORY_SEPARATOR); define('ROOT',dirname(dirname(__FILE__))); $url = $_GET['url']; require_once(ROOT.DS.'library'.DS.'bootstrap.php');
注意上面的PHP代码中,并没有添加PHP结束符号”?>”,这么做的主要原因是:对于只包含PHP代码的文件,结束标志(“?>”)最好不存在,PHP自身并不需要结束符号,不添加结束符号可以很大程度上防止末尾被添加额外的注入内容,让程序更加安全。
在index.php中,我们对library文件夹下的bootstrap.php发起了请求,那么bootstrap.php这个启动文件中到底会包含哪些内容呢?
<?php require_once(ROOT.DS.'config'.DS .'config.php'); require_once(ROOT.DS.'library'.DS .'shared.php');
以上文件都可以直接在index.php文件中引用,我们这么做的原因是为了在后期管理和拓展中更加的方便,所以把需要在一开始的时候就加载运行的程序统一放到一个单独的文件中引用。
先来看看config文件下的config .php文件,该文件的主要作用是设置一些程序的配置项及数据库连接等,主要内容为:
<?php # 设置是否为开发状态 define('DEVELOPMENT_ENVIRONMENT',true); # 设置数据库连接所需数据 define('DB_HOST','localhost'); define('DB_NAME','todo'); define('DB_USER','root'); define('DB_PASSWORD','root');
应该说config.php涉及到的内容并不多,不过是一些基础数据的一些设置,再来看看library下的共用文件shared.php应该怎么写。
<?php /* 检查是否为开发环境并设置是否记录错误日志 */ function setReporting(){ if (DEVELOPMENT_ENVIRONMENT == true) { error_reporting(E_ALL); ini_set('display_errors','On'); } else { error_reporting(E_ALL); ini_set('display_errors','Off'); ini_set('log_errors','On'); ini_set('error_log',ROOT.DS. 'tmp' .DS. 'logs' .DS. 'error.log'); } } /* 检测敏感字符转义(Magic Quotes)并移除他们 */ function stripSlashDeep($value){ $value = is_array($value) ? array_map('stripSlashDeep',$value) : stripslashes($value); return $value; } function removeMagicQuotes(){ if (get_magic_quotes_gpc()) { $_GET = stripSlashDeep($_GET); $_POST = stripSlashDeep($_POST); $_COOKIE = stripSlashDeep($_COOKIE); } } /* 检测全局变量设置(register globals)并移除他们 */ function unregisterGlobals(){ if (ini_get('register_globals')) { $array = array('_SESSION','_POST','_GET','_COOKIE','_REQUEST','_SERVER','_ENV','_FILES'); foreach ($array as $value) { foreach ($GLOBALS[$value] as $key => $var) { if ($var === $GLOBALS[$key]) { unset($GLOBALS[$key]); } } } } } /* 主请求方法,主要目的拆分URL请求 */ function callHook() { global $url; $urlArray = array(); $urlArray = explode("/",$url); $controller = $urlArray[0]; array_shift($urlArray); $action = $urlArray[0]; array_shift($urlArray); $queryString = $urlArray; $controllerName = $controller; $controller = ucwords($controller); $model = rtrim($controller, 's'); $controller .= 'Controller'; $dispatch = new $controller($model,$controllerName,$action); if ((int)method_exists($controller, $action)) { call_user_func_array(array($dispatch,$action),$queryString); } else { /* 生成错误代码 */ } } /* 自动加载控制器和模型 */ function __autoload($className) { if (file_exists(ROOT . DS . 'library' . DS . strtolower($className) . '.class.php')) { require_once(ROOT . DS . 'library' . DS . strtolower($className) . '.class.php'); } else if (file_exists(ROOT . DS . 'application' . DS . 'controllers' . DS . strtolower($className) . '.php')) { require_once(ROOT . DS . 'application' . DS . 'controllers' . DS . strtolower($className) . '.php'); } else if (file_exists(ROOT . DS . 'application' . DS . 'models' . DS . strtolower($className) . '.php')) { require_once(ROOT . DS . 'application' . DS . 'models' . DS . strtolower($className) . '.php'); } else { /* 生成错误代码 */ } } setReporting(); removeMagicQuotes(); unregisterGlobals(); callHook();
接下来的操作就是在library中建立程序所需要的基类,包括控制器、模型和视图的基类。
新建控制器基类为controller.class.php,控制器的主要功能就是总调度,具体具体内容如下:
<?php class Controller { protected $_model; protected $_controller; protected $_action; protected $_template; function __construct($model, $controller,$action) { $this->_controller = $controller; $this->_action = $action; $this->_model = $model; $this->$model =& new $model; $this->_template =& new Template($controller,$action); } function set($name,$value) { $this->_template->set($name,$value); } function __destruct() { $this->_template->render(); } }
新建控制器基类为model.class.php,考虑到模型需要对数据库进行处理,所以可以新建一个数据库基类sqlquery.class.php,模型去继承sqlquery.class.php。
新建sqlquery.class.php,代码如下:
<?php class SQLQuery { protected $_dbHandle; protected $_result; /** 连接数据库 **/ function connect($address, $account, $pwd, $name) { $this->_dbHandle = @mysql_connect($address, $account, $pwd); if ($this->_dbHandle != 0) { if (mysql_select_db($name, $this->_dbHandle)) { return 1; }else { return 0; } }else { return 0; } } /** 中断数据库连接 **/ function disconnect() { if (@mysql_close($this->_dbHandle) != 0) { return 1; } else { return 0; } } /** 查询所有数据表内容 **/ function selectAll() { $query = 'select * from `'.$this->_table.'`'; return $this->query($query); } /** 查询数据表指定列内容 **/ function select($id) { $query = 'select * from `'.$this->_table.'` where `id` = \''.mysql_real_escape_string($id).'\''; return $this->query($query, 1); } /** 自定义SQL查询语句 **/ function query($query, $singleResult = 0) { $this->_result = mysql_query($query, $this->_dbHandle); if (preg_match("/select/i",$query)) { $result = array(); $table = array(); $field = array(); $tempResults = array(); $numOfFields = mysql_num_fields($this->_result); for ($i = 0; $i < $numOfFields; ++$i) { array_push($table,mysql_field_table($this->_result, $i)); array_push($field,mysql_field_name($this->_result, $i)); } while ($row = mysql_fetch_row($this->_result)) { for ($i = 0;$i < $numOfFields; ++$i) { $table[$i] = trim(ucfirst($table[$i]),"s"); $tempResults[$table[$i]][$field[$i]] = $row[$i]; } if ($singleResult == 1) { mysql_free_result($this->_result); return $tempResults; } array_push($result,$tempResults); } mysql_free_result($this->_result); return($result); } } /** 返回结果集行数 **/ function getNumRows() { return mysql_num_rows($this->_result); } /** 释放结果集内存 **/ function freeResult() { mysql_free_result($this->_result); } /** 返回MySQL操作错误信息 **/ function getError() { return mysql_error($this->_dbHandle); } }
新建model.class.php,代码如下:
<?php class Model extends SQLQuery{ protected $_model; function __construct() { $this->connect(DB_HOST,DB_USER,DB_PASSWORD,DB_NAME); $this->_model = get_class($this); $this->_table = strtolower($this->_model)."s"; } function __destruct() { } }
新建视图基类为template.class.php,具体代码如下:
<?php class Template { protected $variables = array(); protected $_controller; protected $_action; function __construct($controller,$action) { $this->_controller = $controller; $this->_action =$action; } /* 设置变量 */ function set($name,$value) { $this->variables[$name] = $value; } /* 显示模板 */ function render() { extract($this->variables); if (file_exists(ROOT.DS. 'application' .DS. 'views' .DS. $this->_controller .DS. 'header.php')) { include(ROOT.DS. 'application' .DS. 'views' .DS. $this->_controller .DS. 'header.php'); } else { include(ROOT.DS. 'application' .DS. 'views' .DS. 'header.php'); } include (ROOT.DS. 'application' .DS. 'views' .DS. $this->_controller .DS. $this->_action . '.php'); if (file_exists(ROOT.DS. 'application' .DS. 'views' .DS. $this->_controller .DS. 'footer.php')) { include (ROOT.DS. 'application' .DS. 'views' .DS. $this->_controller .DS. 'footer.php'); } else { include (ROOT.DS. 'application' .DS. 'views' .DS. 'footer.php'); } } }
做完了以上这么多操作,基本上整个MVC框架已经出来了,下面就该制作我们的站点了。我们要做的站点其实很简单,一个ToDo程序。
首先是在我们的/application/controller/ 目录下面新建一个站点控制器类为ItemsController,命名为itemscontroller.php,内容为:
<?php class ItemsController extends Controller { function view($id = null,$name = null) { $this->set('title',$name.' - My Todo List App'); $this->set('todo',$this->Item->select($id)); } function viewall() { $this->set('title','All Items - My Todo List App'); $this->set('todo',$this->Item->selectAll()); } function add() { $todo = $_POST['todo']; $this->set('title','Success - My Todo List App'); $this->set('todo',$this->Item->query('insert into items (item_name) values (\''.mysql_real_escape_string($todo).'\')')); } function delete($id) { $this->set('title','Success - My Todo List App'); $this->set('todo',$this->Item->query('delete from items where id = \''.mysql_real_escape_string($id).'\'')); } }
接下来就是先建站点的模型,在我们的/application/model/ 目录下面先建一个站点模型类为Item,内容直接继承Model,代码如下:
<?php class Item extends Model { }
最后一步是设置我们站点的视图部分,我们现在/application/views/目录下新建一个items的文件夹,再在items文件夹下建立与控制器重Action相同的文件,分别为view.php,viewall.php,add.php,delete.php,考虑到这么页面中可能需要共用页首和页尾,所以再新建两个文件,命名为header.php,footer.php,每个文件的代码如下:
view.php文件:查看单条待处理事务
<h2><?php echo $todo['Item']['item_name']?></h2> <a href="../../../items/delete/<?php echo $todo['Item']['id']?>"> <span>Delete this item</span> </a>
viewall.php文件:查看所有待处理事务
<form action="../items/add" method="post"> <input type="text" value="I have to..." onclick="this.value=''" name="todo"> <input type="submit" value="add"> </form> <br/><br/> <?php $number = 0?> <?php foreach ($todo as $todoitem):?> <a href="../items/view/<?php echo $todoitem['Item']['id']?>/<?php echo strtolower(str_replace(" ","-",$todoitem['Item']['item_name']))?>"> <span> <?php echo ++$number?> <?php echo $todoitem['Item']['item_name']?> </span> </a><br/> <?php endforeach?>
add.php文件:添加待处理事务
<a href="../items/viewall">Todo successfully added. Click here to go back.</a><br/>
delete.php文件:删除事务
<a href="../../items/viewall">Todo successfully deleted. Click here to go back.</a><br/>
header.php:页首文件
<html> <head> <title><?php echo $title?></title> <style> .item {width:400px;} input {color:#222222;font-family:georgia,times;font-size:24px;font-weight:normal;line-height:1.2em;color:black;} a {color:#222222;font-family:georgia,times;font-size:24px;font-weight:normal;line-height:1.2em;color:black;text-decoration:none;} a:hover {background-color:#BCFC3D;} h1 {color:#000000;font-size:41px;letter-spacing:-2px;line-height:1em;font-family:helvetica,arial,sans-serif;border-bottom:1px dotted #cccccc;} h2 {color:#000000;font-size:34px;letter-spacing:-2px;line-height:1em;font-family:helvetica,arial,sans-serif;} </style> </head> <body> <h1>My Todo-List App</h1>
footer.php:页尾文件
</body> </html>
当然还有一个必不可少的操作就是在数据中中建立一张表,具体代码如下:
CREATE TABLE IF NOT EXISTS `items` ( `id` int(11) NOT NULL AUTO_INCREMENT, `item_name` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=17 ;
至此一个使用MVC开发的网站就开发完成了,你现在可以通过访问http://localhost/todo/items/viewall 查看新建的站点。