PHP MVC模式学习小结

最近进行代码审计的时候发现传统的cms已经变得极少数了,现在市面上大多数的cms都是mvc架构模式,故此特意去学习一下MVC模式。 ——前言

0x01 概念

MVC模式(Model-View-Controller)是一种软件架构模式,把软件系统分为三个基本部分:模型(Model)视图(View)控制器(Controller)

视图(View)

用户交互界面,对于WEB应用来说可看为HTML界面。

功能
1. 显示模型状态
2. 接受数据更新请求
3. 把用户输入数据传给控制器

模型(Model)

业务流程/状态的处理以及业务规则的制定。模型接受视图请求数据,并返回最终的处理结果

功能
1. 代表应用程序的状态
2. 响应状态查询
3. 处理业务流程
4. 通知视图业务状态

控制(Controller)

从用户接收请求,将模型和视图匹配在一起,共同完成用户的请求。其可以理解为一个分发器,选择什么样的模型,选择什么样的视图。

功能
1. 接受用户请求
2. 调用模型响应用户请求
3. 选择视图显示响应结果


0x02 流程

  1. Controller截获用户发出的请求
  2. Controller调用Model完成状态的读写操作
  3. Controller把数据传递给View
  4. View渲染最终结果并呈现给用户

0x03目录结构

  • application(应用代码)
  • config(程序配置或数据库配置)
  • xxxcms(框架核心目录)
  • public(静态文件)
  • runtime(临时数据目录)
  • script(命令行工具)

一般还会在目录下建一个.htaccess文件

<IfModule mod_rewrite.c>
    RewriteEngine On

    # 确保请求路径不是一个文件名或目录
    RewriteCond %{REQUEST_FILENAME} !-f    
    RewriteCond %{REQUEST_FILENAME} !-d    

    # 重定向所有请求到 index.php?url=PATHNAME
    RewriteRule ^(.*)$ index.php?url=$1 [PT,L]
</IfModule>

目的是:
1. 除静态程序,所有程序数据都重定向到index.php上
2. 程序有一个单一入口

入口文件

即public目录下的index.php文件

<?php 

// 应用目录为当前目录
define('APP_PATH', __DIR__.'/');

// 开启调试模式
define('APP_DEBUG', true);

// 网站根URL
define('APP_URL', 'http://localhost/xxxcms');

// 加载框架
require './xxxcms/xxxPHP.php';

框架文件

即入口文件中加载的xxxPHP.php

<?php

// 初始化常量
defined('FRAME_PATH') or define('FRAME_PATH', __DIR__.'/');
defined('APP_PATH') or define('APP_PATH', dirname($_SERVER['SCRIPT_FILENAME']).'/');
defined('APP_DEBUG') or define('APP_DEBUG', false);
defined('CONFIG_PATH') or define('CONFIG_PATH', APP_PATH.'config/');
defined('RUNTIME_PATH') or define('RUNTIME_PATH', APP_PATH.'runtime/');

// 包含配置文件
require APP_PATH . 'config/config.php';

//包含核心框架类
require FRAME_PATH . 'Core.php';

// 实例化核心类
$fast = new Core;
$fast->run();

核心框架

  1. 路由处理:
function Route()
    {
        $controllerName = 'Index';
        $action = 'index';        
        if (!empty($_GET['url'])) {
            $url = $_GET['url'];
            $urlArray = explode('/', $url);            

            // 获取控制器名
            $controllerName = ucfirst($urlArray[0]);            

            // 获取动作名
            array_shift($urlArray);
            $action = empty($urlArray[0]) ? 'index' : $urlArray[0];            
            //获取URL参数
            array_shift($urlArray);
            $queryString = empty($urlArray) ? array() : $urlArray;
        }       
  1. 敏感字符处理:
    // 删除敏感字符
    function stripSlashesDeep($value)
    {
        $value = is_array($value) ? array_map('stripSlashesDeep', $value) : stripslashes($value);        
        return $value;
    }    
    // 检测敏感字符并删除
    function removeMagicQuotes()
    {       
        if ( get_magic_quotes_gpc()) {
            $_GET = stripSlashesDeep($_GET );
            $_POST = stripSlashesDeep($_POST );
            $_COOKIE = stripSlashesDeep($_COOKIE);
            $_SESSION = stripSlashesDeep($_SESSION);
        }
    }
  1. 自动加载控制器和模型类:
  2. // 自动加载控制器和模型类
    static function loadClass($class)
    {
        $frameworks = FRAME_PATH . $class . '.class.php';
        $controllers = APP_PATH . 'application/controllers/' . $class . '.class.php';
        $models = APP_PATH . 'application/models/' . $class . '.class.php';        
        if (file_exists($frameworks)) {           
            // 加载框架核心类
            include $frameworks;
        } elseif (file_exists($controllers)) {    
            // 加载应用控制器类
            include $controllers;
        } elseif (file_exists($models)) {   
            //加载应用模型类
            include $models;
        } else {    
            /* 错误代码 */
        }
    }

控制器基类文件

Controller.class.php的功能是调度其他两层。实现所有控制器、模型和视图(View类)的通信。

<?php /**
 * 控制器基类
 */
class Controller
{    
     protected $_controller;    
     protected $_action;    
     protected $_view; 

     // 构造函数,初始化属性,并实例化对应模型
     function __construct($controller, $action)
     {
          $this->_controller = $controller;
          $this->_action = $action;
          $this->_view = new View($controller, $action);
    }

    // 分配变量
    function assign($name, $value)
    {
        $this->_view->assign($name, $value);
    }    

    // 渲染视图
    function __destruct()
    {
        $this->_view->render();
    }
}

模型基类

Model.class.php

<?php

class Model extends Sql
{    
    protected $_model;
    protected $_table; 

    function __construct()
    { 
        // 连接数据库
        $this->connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);        

        // 获取模型名称
        $this->_model = get_class($this);
        $this->_model = rtrim($this->_model, 'Model');        

        // 数据库表名与类名一致
        $this->_table = strtolower($this->_model);
    } 
    function __destruct()
    {
    }
}

由于模型基类是对数据库进行处理,所以要另建一个Sql.class.php来编写对数据库的操作

具体代码就不给出了,主要是对数据库的增删改查功能。

视图基类

View.class.php

<?php/**
 * 视图基类
 */class View
 {    
    protected $variables = array();    
    protected $_controller;    
    protected $_action;    
    function __construct($controller, $action)
    {
        $this->_controller = $controller;
        $this->_action = $action;
    } 
    /** 分配变量 **/
    function assign($name, $value)
    {
        $this->variables[$name] = $value;
    } 
    /** 渲染显示 **/
    function render()
    {
        extract($this->variables);
        $defaultHeader = APP_PATH . 'application/views/header.php';
        $defaultFooter = APP_PATH . 'application/views/footer.php';
        $controllerHeader = APP_PATH . 'application/views/' . $this->_controller . '/header.php';
        $controllerFooter = APP_PATH . 'application/views/' . $this->_controller . '/footer.php';        
        // 页头文件
        if (file_exists($controllerHeader)) {            include ($controllerHeader);
        } else {            include ($defaultHeader);
        }        // 页内容文件
        include (APP_PATH . 'application/views/' . $this->_controller . '/' . $this->_action . '.php');        
        // 页脚文件
        if (file_exists($controllerFooter)) {            include ($controllerFooter);
        } else {            include ($defaultFooter);
        }
    }
}

配置文件

即config目录下的config.php

<?php
/** 变量配置 **/
define('DB_NAME', 'test');
define('DB_USER', 'root');
define('DB_PASSWORD', 'root');
define('DB_HOST', 'localhost');
...

主要都是数据库的配置,不过绝大多数cms中还会有一些对程序的配置


0x04 URL处理

现在大多数mvc模式的cms对url的处理都是用PATHINFO模式(index.php/index/index)

应用的访问方式都是采用单一入口的访问方式,所以访问一个应用中的具体模块及模块中的某个操作,都需要在url中通过入口文件参数进行访问和执行

格式:

http://localhost/入口文件/模块名/操作名/参数1/值1

例如:访问用户模块(user),再去执行添加操作(add)

https://localhost/index.php/user/add

0x05 MVC设计模式优缺点

  • 优点
  1. 三个层各施其职,互不干涉
  2. 由于按层把系统分开,那么就能更好的实现开发中的分工。
  3. 分层后更有利于组件的重用。
  • 缺点
  1. 增加了系统结构和实现的复杂性。
    对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。
  2. 视图与控制器间的过于紧密的连接。
    视图与控制器是相互分离,但确实联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。
  3. 视图对模型数据的低效率访问

依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。

参考:《细说PHP》

手把手编写自己的PHPMVC框架实例

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据