PHP学习笔记-MVC框架

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiaoxiao20121314/article/details/80823051

MVC学习笔记

1.什么使MVC框架

    MVC是一种软件开发框架,MVC将程序分为三个部分:模型层(M)、视图层(V)和控制层(C),对不同的层进行分层管理和控制,方便程序的修改和扩展

2. 为什么使用MVC框架

    在PHP中使用MVC框架,可以实现了分层、分类开发,实现了web的分离,使前端代码与后端分离,某一层的调整,不会对另一层的代码和逻辑造成影响,使用MVC开发框架更加方便程序的扩展,使开发的代码整体更加清晰

3.MVC的含义

    M(Model)模型层:提供了对数据库操作和链接的抽象层,主要完成大部分的业务逻辑和数据逻辑的处理

    V(view)视图层:主要负责处理结果的显示(渲染),用于与用户进行交互

    C(controller)控制层:根据请求进行相关的转发,调用相应的M(模型)对请求进行处理,并将请求结果返回给用户,决定结果的展示形式

    浏览器向服务器发起请求,首先由控制器将URL拦截,根据URL中的参数调用相应的模型层,模型层操作数据库,并将操作结果返回给Controller(控制层),控制层调用View层的相关代码对结果进行渲染,View层将渲染结果呈现给用户。

4. MVC目录结构

    项目根目录

    |-app层

    | |-controller

    | |-model

    | |-view

    |-config

5.重定向的作用:

    可以直接访问项目中的静态文件,除静态文件外,其他文件都有统一的程序入口,一般为index.php文件,如果使用nginx服务器则可以通过配置php.config中的重定向功能,是对应的字段重定向到指定路径下的指定入口文件,并将参数赋值给REQUEST_URI变量。PHP代码自身并不需要结束符号,不加结束符可以让程序更加安全,因为可以防止在程序的尾部注入

location / {   
     # 重新向所有非真是存在的请求到index.php    
    try_files $uri $uri/ /index.php$args;
}

6.入口文件

    主要分为两类,一类为项目的入口文件,规定程序访问时的统一入口;另一类是每一模块的入口文件,可以实现对模块的统一管理。入口文件主要实现程序的一些初始化设置,例如一些基础类、配置文件、数据库和模式等。例如:

    

<?php
    define('APP_PATH', __DIR__ . '/');// 应用目录为当前目录
    define('APP_DEBUG', true);// 开启调试模式
    require(APP_PATH . 'fastphp/Fastphp.php');// 加载框架文件
    $config = require(APP_PATH . 'config/config.php');// 加载配置文件
    (new fastphp\Fastphp($config))->run();// 实例化框架类通常为程序中的bootstrap.php文件

7.配置文件:

    配置文件分为两种,一种为程序的全局配置,例如数据库、默认控制器名和操作名等;另一种为局部配置,这种模块配置只有当访问到相关文件时,才会被对应的加载。全局的配置文件一般为命名为config.php内容如下:

<?php
    $config['db']['host'] = 'localhost';// 连接的数据库的域名
    $config['db']['username'] = 'root';//用户名
    $config['db']['password'] = '123456';//密码
    $config['db']['dbname'] = 'project';//使用的数据库名字

    $config['defaultController'] = 'Item';//控制器的名字
    $config['defaultAction'] = 'index';//通用才操作的名字
    return $config;

8.框架

    入口文件中调用了配置文件初始化了环境配置。定义了加载的控制器以及操作,所以在后续的调用中会根据配置文件的结果实例化框架的内容,实例化时会接受config中的相关参数(例如控制层、模型层和视图层),随后在入口文件中调用run函数进行相关的实例化。run函数的主要功能如下:

    1)类自动加载

    2)环境检查

    3)过滤敏感字符

    4)移除全局变量的老用法

    5)路由处理

代码:

<?phpnamespace fastphp;
    defined('CORE_PATH') or define('CORE_PATH', __DIR__);// 框架根目录
    class Fastphp{
        protected $config = [];// 配置内容
        public function __construct($config)
        {
            $this->config = $config;
        }
        public function run()
        {// 运行程序
            spl_autoload_register(array($this, 'loadClass'));
            $this->setReporting();
            $this->removeMagicQuotes();
            $this->unregisterGlobals();
            $this->setDbConfig();
            $this->route();
        }
    public function route()// 路由处理
    {
        $controllerName = $this->config['defaultController'];
        $actionName = $this->config['defaultAction'];
        $param = array();
        $url = $_SERVER['REQUEST_URI'];
        $position = strpos($url, '?');// 清除?之后的内容
        $url = $position === false ? $url : substr($url, 0, $position);
        $url = trim($url, '/');// 删除前后的“/”
        if ($url) {
            $urlArray = explode('/', $url);// 使用“/”分割字符串,并保存在数组中
            $urlArray = array_filter($urlArray);// 删除空的数组元素          
            $controllerName = ucfirst($urlArray[0]);// 获取控制器名
            
            array_shift($urlArray);// 获取动作名
            $actionName = $urlArray ? $urlArray[0] : $actionName;
            
            $array_shift($urlArray);// 获取URL参数
            $param = $urlArray ? $urlArray : array();
        }
        $controller = 'app\\controllers\\'. $controllerName . 'Controller';
	// 判断控制器和操作是否存在
        if (!class_exists($controller)) {
            exit($controller . '控制器不存在');
        }
        if (!method_exists($controller, $actionName)) {
            exit($actionName . '方法不存在');
        }
        // 如果控制器和操作名存在,则实例化控制器,
        $dispatch = new $controller($controllerName, $actionName);
        // $dispatch保存控制器实例化后的对象,我们就可以调用它的方法,
        call_user_func_array(array($dispatch, $actionName), $param);
    }
    public function setReporting()
    {// 检测开发环境
        if (APP_DEBUG === 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');
        }
    }
    public function stripSlashesDeep($value)
    {// 删除敏感字符
        $value = is_array($value) ? array_map(array($this, 'stripSlashesDeep'), $value) : stripslashes($value);
        return $value;
    }
    public function removeMagicQuotes()
    {// 检测敏感字符并删除
        if (get_magic_quotes_gpc()) {
            $_GET = isset($_GET) ? $this->stripSlashesDeep($_GET ) : '';
            $_POST = isset($_POST) ? $this->stripSlashesDeep($_POST ) : '';
            $_COOKIE = isset($_COOKIE) ? $this->stripSlashesDeep($_COOKIE) : '';
            $_SESSION = isset($_SESSION) ? $this->stripSlashesDeep($_SESSION) : '';
        }
    }
    // 检测自定义全局变量并移除。因为 register_globals 已经弃用,如果
    // 已经弃用的 register_globals 指令被设置为 on,那么局部变量也将
    // 在脚本的全局作用域中可用。 例如, $_POST['foo'] 也将以 $foo 的
    // 形式存在,这样写是不好的实现,会影响代码中的其他变量。 相关信息,
    // 参考: http://php.net/manual/zh/faq.using.php#faq.register-globals
    public 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]);
                    }
                }
            }
        }
    }
    public function setDbConfig()
    {// 配置数据库信息
        if ($this->config['db']) {
            define('DB_HOST', $this->config['db']['host']);
            define('DB_NAME', $this->config['db']['dbname']);
            define('DB_USER', $this->config['db']['username']);
            define('DB_PASS', $this->config['db']['password']);
        }
    }
    public function loadClass($className)
{// 自动加载类,可以设置为自动加载一个文件夹下的所以文件
        $classMap = $this->classMap();
        if (isset($classMap[$className])) {
            // 包含内核文件
            $file = $classMap[$className];
        } elseif (strpos($className, '\\') !== false) {
            // 包含应用(application目录)文件
            $file = APP_PATH . str_replace('\\', '/', $className) . '.php';
            if (!is_file($file)) {
                return;
            }
        } else {
            return;
        }
        include $file;    }
    protected function classMap()
    {// 内核文件命名空间映射关系
        return [
            'fastphp\base\Controller' => CORE_PATH . '/base/Controller.php',
            'fastphp\base\Model' => CORE_PATH . '/base/Model.php',
            'fastphp\base\View' => CORE_PATH . '/base/View.php',
            'fastphp\db\Db' => CORE_PATH . '/db/Db.php',
            'fastphp\db\Sql' => CORE_PATH . '/db/Sql.php',
        ];
    }
}

9.路由方法route():截取URL,并解析出控制器名、方法名和URL参数

    例如:https://my.csdn.net/?ref=toolbar当浏览器访问上面的URL,经过nginx的重写到指定的入口文件,并将参数ref=toolbar传递到调用的文件,route()从全局变量 $_SERVER['REQUEST_URI']中获取到字符串对应调取相应的控制器和方法。

10.基类    

    在框架中创建MVC基类,包括控制器模型视图三个基类,实现程序的调度。Controller 类用assign()方法实现把变量保存到View对象中。这样,在调用$this->render() 后视图文件就能显示这些变量。Model基类涉及到3个类:Model基类本身,它的父类SQL,以及提供数据库连接句柄的Db类。

<?php
namespace fastphp\base;
use fastphp\db\Sql;
class Model extends Sql{
    protected $model;
    public function __construct()
    {
        if (!$this->table) {// 获取数据库表名
            $this->model = get_class($this);// 获取模型类名称
            $this->model = substr($this->model, 0, -5);// 删除类名最后的 Model 字符
            $this->table = strtolower($this->model);// 数据库表名与类名一致
        }
    }
}
<?php
namespace fastphp\db;
use \PDOStatement;
class Sql{
    protected $table;// 数据库表名
    protected $primary = 'id';// 数据库主键
    private $filter = '';// WHERE和ORDER拼装后的条件
    private $param = array();// Pdo bindParam()绑定的参数集合
    public function where($where = array(), $param = array())
    {
        if ($where) {
            $this->filter .= ' WHERE ';
            $this->filter .= implode(' ', $where);
            $this->param = $param;
        }
        return $this;
    }
  public function order($order = array())
    {
        if($order) {
            $this->filter .= ' ORDER BY ';
            $this->filter .= implode(',', $order);
        }
        return $this;
    }
    public function fetchAll()// 查询所有
    {
        $sql = sprintf("select * from `%s` %s", $this->table, $this->filter);
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, $this->param);
        $sth->execute();
        return $sth->fetchAll();
    }
    public function fetch()// 查询一条
    {
        $sql = sprintf("select * from `%s` %s", $this->table, $this->filter);
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, $this->param);
        $sth->execute();
        return $sth->fetch();
    }
    public function delete($id)// 根据条件 (id) 删除
    {
        $sql = sprintf("delete from `%s` where `%s` = :%s", $this->table, $this->primary, $this->primary);
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, [$this->primary => $id]);
        $sth->execute();
        return $sth->rowCount();
    }
    public function add($data) // 新增数据
    {
        $sql = sprintf("insert into `%s` %s", $this->table, $this->formatInsert($data));
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, $data);
        $sth = $this->formatParam($sth, $this->param);
        $sth->execute();
       return $sth->rowCount();
    }
    public function update($data)// 修改数据
    {
        $sql = sprintf("update `%s` set %s %s", $this->table, $this->formatUpdate($data), $this->filter);
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, $data);
        $sth = $this->formatParam($sth, $this->param);
        $sth->execute();
        return $sth->rowCount();
    }
    public function formatParam(PDOStatement $sth, $params = array())
    {
        foreach ($params as $param => &$value) {
            $param = is_int($param) ? $param + 1 : ':' . ltrim($param, ':');
            $sth->bindParam($param, $value);
        }
      return $sth;
    }
    private function formatInsert($data)
    {
        $fields = array();
        $names = array();
        foreach ($data as $key => $value) {
            $fields[] = sprintf("`%s`", $key);
            $names[] = sprintf(":%s", $key);
        }
        $field = implode(',', $fields);
        $name = implode(',', $names);
        return sprintf("(%s) values (%s)", $field, $name);
    }
    private function formatUpdate($data)
    {
        $fields = array();
        foreach ($data as $key => $value) {
            $fields[] = sprintf("`%s` = :%s", $key, $key);
        }
        return implode(',', $fields);
    }
}

    Sql类里面有用到Db:pdo()方法,这是我们创建的Db类,它提供一个PDO单例。在fastphp/db/目录下创建Db.php文件,内容:

<?php
namespace fastphp\db;
use PDO;use PDOException;
class Db{
    private static $pdo = null;
    public static function pdo()
    {
        if (self::$pdo !== null) {
            return self::$pdo;
        }
        try {
            $dsn    = sprintf('mysql:host=%s;dbname=%s;charset=utf8', DB_HOST, DB_NAME);
            $option = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC);
            return self::$pdo = new PDO($dsn, DB_USER, DB_PASS, $option);
        } catch (PDOException $e) {
            exit($e->getMessage());
        }
    }
}

猜你喜欢

转载自blog.csdn.net/xiaoxiao20121314/article/details/80823051