1. 程式人生 > >使用PHP搭建自己的MVC框架

使用PHP搭建自己的MVC框架

模型(Model) “資料模型”(Model)用於封裝與應用程式的業務邏輯相關的資料以及對資料的處理方法。“模型”有對資料直接訪問的權力,例如對資料庫的訪問。“模型”不依賴“檢視”和“控制器”,也就是說,模型不關心它會被如何顯示或是如何被操作。但是模型中資料的變化一般會通過一種重新整理機制被公佈。為了實現這種機制,那些用於監視此模型的檢視必須事先在此模型上註冊,從而,檢視可以瞭解在資料模型上發生的改變。

檢視(View) 檢視層能夠實現資料有目的的顯示(理論上,這不是必需的)。在檢視中一般沒有程式上的邏輯。為了實現檢視上的重新整理功能,檢視需要訪問它監視的資料模型(Model),因此應該事先在被它監視的資料那裡註冊。

控制器(Controller) 控制器起到不同層面間的組織作用,用於控制應用程式的流程。它處理事件並作出響應。“事件”包括使用者的行為和資料模型上的改變。

二、為什麼要自己開發MVC框架

網路上有大量優秀的MVC框架可供使用,本教程並不是為了開發一個全面的、終極的MVC框架解決方案,而是將它看作是一個很好的從內部學習PHP的機會,在此過程中,你將學習面向物件程式設計和設計模式,並學習到開放中的一些注意事項。

更重要的是,你可以完全控制你的框架,並將你的想法融入到你開發的框架中。雖然不一定是做好的,但是你可以按照你的方式去開發功能和模組。

三、開始開發自己的MVC框架

在開始開發前,讓我們先來把專案建立好,假設我們建立的專案為todo,那麼接下來的第一步就是把目錄結構先設定好。

雖然在這個教程中不會使用到上面的所有的目錄,但是為了以後程式的可拓展性,在一開始就把程式目錄設定好使非常必要的。下面就具體說說每個目錄的作用:

* application – 存放程式程式碼
* config – 存放程式配置或資料庫配置
* db – 用來存放資料庫備份內容
* library – 存放框架程式碼
* public – 存放靜態檔案
* scripts – 存放命令列工具
* tmp – 存放臨時資料

在目錄設定好以後,我們接下來就要來頂一下一些程式碼的規範:

  1. MySQL的表名需小寫並採用複數形式,如items,cars
  2. 模組名(Models)需首字母大寫,並採用單數模式,如Item,Car
  3. 控制器(Controllers)需首字母大寫,採用複數形式並在名稱中新增“Controller”,如ItemsController, CarsController
  4. 檢視(Views)採用複數形式,並在後面新增行為作為檔案,如:items/view.php, cars/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>

這麼做的主要原因有:

  1. 可以使程式有一個單一的入口,將所有除靜態程式以外的程式都重定向到index.php上;
  2. 可以用來生成利於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 檢視新建的站點。


相關推薦

使用PHP搭建自己MVC框架

模型(Model) “資料模型”(Model)用於封裝與應用程式的業務邏輯相關的資料以及對資料的處理方法。“模型”有對資料直接訪問的權力,例如對資料庫的訪問。“模型”不依賴“檢視”和“控制器”,也就是說,模型不關心它會被如何顯示或是如何被操作。但是模型中資料的變化一般會通過一種重新整理機制被公佈。為了實現

PHP搭建自己MVC框架 1

在PHP中使用MVC越來越流行了,特別是在一些開源的框架當中。MVC足以應對大多數的情況,但還有一些情況是其不太適合的,如比較簡單的個人部落格,對於只有幾百篇文章量級的部落格,使用MVC讓人覺得有些太複雜了;同樣對於新浪等入口網站,使用MVC,將有大量的檔案被載入,對於速度

PHP搭建自己MVC框架8 檢視層

接下來實現檢視層 檢視層包括變數賦值(assign) 和 呼叫模板(display) 控制器繼承基類 <?php namespace app\ctrl; class indexCtrl extends \core\mymvc { public functio

PHP搭建自己的web框架-前言

        目前使用PHP開發專案已經有幾年時間,雖然不是專職的PHPer,但在現在的公司,走過了PHP從入門到熟悉到熟練應用過程,一直在使用,不敢言專業,屬於應用PHPer。現在希望能很好地總結並分享一下自己PHP歷程和經驗,也希望看過文章的朋友能有所收穫。    

PHP搭建自己的web框架-檢視/模板引擎

        檢視,MVC中的V,View,如何將資料通過合適的格式展現給使用者或呼叫方。         當然使用什麼格式展現由控制器直接控制,但根本原因由人或系統決定。         本文主要描述的是如何在MVC的web框架中輸出網頁檢視,也就是HTML格式的檢視

模仿spring-aop的功能,利用註解搭建自己框架

屬性 def name rac java8 out fault 一個 lte 入JAVA坑7月有余,也嘗試自己手動搭建框架,最近對spring aop的這種切面很著迷,為此記錄下自己目前搭出來的小小的demo,後續有時間也會繼續改進自己的demo。望大神們不吝賜教。   主

搭建Spring MVC框架(一站式服務)

注:建議大家選擇預設安裝路徑,出現錯誤也容易百度解決。 java環境配置 tomcat安裝 IDEA安裝與破解 搭建Spring MVC 開源專案管理工具Maven介紹 搭建Spring MVC(Maven版) 一,Java環境搭配(win10情況下) 二,

[筆記]架構探險-從零開始寫JavaWeb框架-1. 之搭建輕量級mvc框架

囉嗦一句: 看md語法寫的文章,注意檢視 上面 的目錄. 一般是很有節奏的導航. ヽ(ˋ▽ˊ)ノヽ(ˋ▽ˊ)ノ 終於到了不會的地步了,該書的前面兩章節都是從零開始講解怎麼使用 idea搭建專案,從servlet開始講解怎麼使用. (idea的使用目錄)

PHP簡單實現MVC框架路由功能模式

        說到使用PHP進行網站開發,自然離不開各種PHP的開發框架,開發框架為我們提供了靈活的開發方式,MVC層分離,業務解耦等等,讓我們的開發更為快捷方便。但是很多人只是會使用框架,卻從

搭建自己php框架-----------------day1

公司 index.php 重定向 今天開始 訪問 log gogo post 閱讀 一直想寫一個自己的框架,但是不知道從何處下手,也用過composer構建過自己的框架,感覺基礎不是太好,一直依賴各種類庫和插件,框架寫出來感覺就是一個類庫的集合, 公司現在的框架就是模仿CI

Asp.net MVC 搭建屬於自己框架(一)

C4D pagedlist del tran 6.0 ext 才有 應該 frame 網址:https://www.cnblogs.com/sggx/p/4555255.html 為什麽要自己搭框架?   大家夥別急,讓我慢慢地告訴你!大家有沒有這種感覺,從一家跳槽到另一家

寫一個屬於自己PHPMVC框架(二)

第一篇文章已經把所需的目錄搭建好了,接下來的工作就是寫一些程式碼了 用編輯器開啟public/index.php檔案,寫上下面的程式碼 <?php define(DS, DIRECTORY_SEPARATOR); define(ROOT, dirna

寫一個屬於自己PHPMVC框架(一)

最近想做個PHP的個人部落格作為學習用,但是發現儘管把PHP函式用得很熟悉了,按照常規的辦法,寫一個頁面處理一個請求,僅僅一個部落格就可能有很多個頁面,而且php程式碼和html程式碼都結合的非常緊密,如果想要實現更換面板的功能,就顯得非常無力。在網上找了好多framework框架,但似乎又要開始學

自己動手寫PHP-MVC框架(一)

自己動手模仿寫一個php的框架,首先是要明白原理,然後寫的話思路就比較清晰。 當前應用的基本組成是有一堆的資料夾和一個index的檔案組成         |-Conf       &n

編寫自己PHP MVC框架

1 什麼是MVC MVC模式(Model-View-Controller)是軟體工程中的一種軟體架構模式。 MVC把軟體系統分為三個基本部分:模型(Model)、檢視(View)和控制器(Controller)。 PHP中MVC模式也稱Web MVC,從上世紀7

JavaWeb之搭建自己MVC框架

. 介紹         MVC全名是Model View Controller,是模型(model)-檢視(view)-控制器(controller)的縮寫,一種軟體設計典範,用一種業務邏輯、資料、介面顯示分離的方法組織程式碼,將業務邏輯聚集到一個部件裡面,在改進和個性化

做一個自己MVC框架[php]

外包的活幹多了,總有一些小專案,這些小專案甚至不需要考慮安全。這時候用框架顯得浪費。自己手寫原生程式碼又有些不太習慣(框架用多了,有時候連基本的都忘了,不能忘本啊。。。) 許久不更的部落格。反正也沒多少人看,哈~就當給自己一個馬克,也希望能給一些剛踏入程式猿大

1小時內打造你自己PHP MVC框架

簡介 MVC框架在現在的開發中相當流行,不論你使用的是JAVA,C#,PHP或者IOS,你肯定都會選擇一款框架。雖然不能保證100%的開發語言都會使用框架,但是在PHP社群當中擁有最多數量的MVC框架。今天你或許還在使用Zend,明天你換了另一個專案也許就會轉投

php搭建mvc框架三(路由類)

在這裡補充一下:“大家可以先把目錄建一下,”; 首先呢在mvc下面建立.htaccess 內容: <IfModule mod_rewrite.c> Options +FollowS

php搭建mvc框架二(類的自動載入)

首先呢這是在上一篇的基礎上繼續寫的!! 目錄: 自動載入類: 下面的就是在上一篇的程式碼基礎上,寫的自動載入類的程式碼展示。 <?php /* * 入口檔案 * 定義常量 * 載入函