一个手写的MVC框架

先大致了解框架流程

目录如下:

include

首先建立一个初始化文件 init.php (关键文件)

include/init.php

<?php
//设置字符编码 防止乱码
header("Content-type:text/html;charset=utf-8");
//获取当前文件的目录所在的目录 也就是先得到网站的根目录 方便后面引用文件
$len = str_replace('\', '/', dirname(dirname(__FILE__))).'/';
//定义为根目录
define('ROOT',$len);
//引入基本函数库文件
include(ROOT.'include/lib.base.php');

//自动加载函数
function webcyhLoad($class){
    //将自动加载的类名的后面5个字符判断是否为Model类到相关的Model目录下加载该文件 防止有些文件大小不一致因此这里先将类名转换成小写再加以判断
    if(strtolower(substr($class, -5)) == 'model'){
        require(ROOT.'Model/'.$class.'.class.php');
    }else if(strtolower(substr($class, -4)) == 'tool'){
        require(ROOT.'tools/'.$class.'.class.php');
    }else{
          require(ROOT.'include/'.$class.'.class.php');
    }
}

//这里先执行MySmarty当中的自动加载 注意这里可能会与smarty模板引擎当中的自动加载类冲突 首先先引入MysqlSmarty文件 等待其加载完后再
include(ROOT.'include/MySmarty.class.php');
//注册自动加载
spl_autoload_register('webcyhLoad');
//全局smarty
$smarty = new MySmarty();
//开启session
session_start();
//定义开启调试模式
define('DEBUG',true);
//根据是否开启调试来设置报错级别
if(defined('DEBUG')){
    error_reporting(E_ALL);
}else{
    error_reporting(0);
}

include/lib.base.php文件

<?php
/*
放置基础函数
*/
//递归转义数组
defined("Hello")||exit("Hello deny!");
function _addslashes($arr){
    foreach ($arr as $key => $value) {
        if(is_string($value)){
            //将数据经过处理防止给每个双引号添加斜杆其转义字符作用
            $arr[$key] = addslashes($value);
        }else if(is_array($value)){
            $arr[$key] = _addslashes($value);
        }
    }
    return $arr;
}

现在创建一个控制器:

这里以一个控制器为例子:

control.php

<?php
define("Hello", true);
//先初始化引入自动加载机制等等
require('../include/init.php');
//这里实例化CateModel类文件 将触发自动加载函数自动引入该文件
$cate = M('CateModel');
//获取所有的分类 调用相关方法
$catelist = $cate->getAll();
//调用全局变量 smarty赋值给模板
$smarty->assign('catelist',$catelist);
//调用相关方法
$category = $cate->getCatTree($catelist,1,0);

    $smarty->assign('category',$category);
    /*
    在这里调用Model当中的相关操作完成逻辑处理
    在这里我就不写太多了

    */
    //返回视图文件
$smarty->display('index.php');

在这个过程中用到Model目录下的CateModel.class.php文件 注意一点Model当中有一个父类 所有其它的Model类都要继承自该Model类内容如下:

<?php

//防止非法访问
defined("Hello")||exit("Hello deny!");
class Model{
    //当前指向的数据表名称
    protected $table = null;
    //数据库资源把柄
    protected $db = null;
    //数据表的关键字段
    protected $pk = null;
    //数据表当中的所有字段
    protected $fields = array();
    //自动填充   表单中有些字段可以为空 所以在入库之前先将没有值得字段用默认值填充
    protected $_auto = array();
    //错误信息
    protected $error = array();
    //字段入库前的验证
    protected $_valid = array();
    //构造函数
    public function __construct(){
        $this->db = Mysql::getInt();
    }
    //将数据传送过来进行验证 验证不通过添加报错提示
    public function _validate($data){
        //先判断是否填写验证规则
        if(empty($this->_valid)){
            return true;
        }
        foreach ($this->_valid as $key => $value) {
            //必须验证
            switch ($value[1]) {
                case 1:
                //必须验证字段 如果不存在或者为空就返回false
                    if(!isset($data[$value[0]])||empty($data[$value[0]])){
                        $this->error[$value[0]] = $value[2];
                    }else{
                        if($this->check($data[$value[0]],$value[3])){

                        }else{
                            $this->error[$value[0]] = $value[2];

                        }
                    }
                    break;
                case 0:
                    if(isset($data[$value[0]])){
                        if($this->check($data[$value[0]],$value[3],$value[4])){
                        }else{
                            $this->error[$value[0]] = $value[2];

                        }
                    }
                    break;
                case 2:
                    if(isset($data[$value[0]])&&!empty($data[$value[0]])){
                        //注意传输的参数 $value[3]
                        if($this->check($data[$value[0]],$value[3],$value[4])){
                            
                            return true;
                        }else{
                            $this->error[$value[0]] = $value[2];
                        }
                    }
                    break;
            }
        }
        return empty($this->error)?true:false;
    }
    //返回错误信息
    public function getError(){
        return $this->error;
    }
    //负责检查 只返回一个错误结果 只要一个错误就返回了
    public function check($value,$rule,$param = ''){
        switch($rule){
            case 'required':
            return !empty($value);
            case 'in':
            //拆成数组
            $data = explode(',',$param);
            //var_dump($data);
            return in_array($value, $data);
            case 'between':
            list($min,$max) = explode(',',$param);
            return $min <= $value&&$max >= $value;
            case 'length':
            //进行赋值 如果在上面将param改为 大小的顺序出现问题必将导致错误
            list($min,$max) = explode(',',$param);
            return $min <= strlen($value)&&$max >= strlen($value);
            case 'number':
            return is_numeric($value);
            case 'email':
            //正则表达式验证邮箱地址
            return preg_match( "/^([0-9A-Za-z\-_\.]+)@([0-9a-z]+\.[a-z]{2,3}(\.[a-z]{2})?)$/i",$value);
            default:
            return false;
        }
    }
    /*
    负责清除 清除多余的单元 将与表对应的字段保留下来
    传递一个数组
    循环数组判断key是否为表的字段
    */
    public function _facade($array = array()){
        //保留需要的字段
        $data = array();
        foreach ($array as $key => $value) {
            if(in_array($key,$this->field)){
                $data[$key] = $value;
            }
        }
        return $data;
    }

    /*
    自动填充
    负责把表当中需要的值 二POST当中没有传过来的字段赋值
    比如 addtime 即商品时间应该返回 time()

    */
    //传递已经经过过滤的数组
    public function _autoFill($data){
        foreach ($this->_auto as $key => $value) {
            //判断数组当中是否有对用的值 如果没有就添加上默认的值
            //var_dump($key);
            if(!array_key_exists($value[0], $data)){
                switch ($value[1]) {
                    case 'value':
                    //value[0] 为表单中的字段名称
                        $data[$value[0]] = $value[2];
                        break;
                    case 'function':
                    //回调函数
                        $data[$value[0]] = call_user_func($value[2]);
                        break;
                }
            }
        }
        return $data;
    }
    public function table($table){
        $this->table = $table;
    }
    public function add($data){
        //调用当前类中保留的db把柄去添加数据
        return $this->db->autoExecute($this->table,$data,$act='insert',$where = 0);
    }
    //获取所有数据
    public function getAll(){
        return $this->db->getAll("select * from ".$this->table);
    }
    //取出一行数据
    public function find($id){
        $sql = 'select * from '.$this->table.' where '.$this->pk.'='.$id;
        $rst=$this->db->getRow($sql);
        //var_dump($rst);
        return $rst;
    }
    //删除栏目
    public function delete($id){
        $sql = "delete from ".$this->table." where ".$this->pk."=".$id;
        // /$this->db->query($sql);
        //返回受影响行数 因为删除返回0也是成功的但根据返回值判断是否执行成功是不行的
        if($this->db->query($sql)){
            return $this->db->affected_rows();    
        }else{
            return false;
        }
        
    }
    //修改数据
    public function update($data){
        return $this->db->autoExecute($this->table,$data,$act='update',$this->pk."=".$data[$this->pk]);
    }
    public function insert_id(){
        return $this->db->insert_id();
    }
}

而该Model在初始化时先 获得数据库操作柄 然后在这里实现扩展大部分Model都用得上的操做

Model/CateModel.class.php类如下

<?php
//防止非法访问
defined("Hello")||exit("Hello deny!");
//继承自Model 再次实现每个业务逻辑   如果业务比较复杂建议再加一层Service层 逻辑处理放在Service当中
/*
比如在控制器当中要判断经过处理一个用户注册信息再入库,
Model当中应该将
过滤字段 填充字段 验证字段分开编写
而service当中应该编写一个方法调用Model当中的 过滤字段 填充字段 验证字段函数完成一次调用 这样在控制器当中只需要调用sservice当中的一个用户注册方法即可完成这样控制器那一层就不会过于复杂


*/
class CateModel extends Model{
    protected $table = "category";
    protected $pk = "cat_id";
    /*递归获取$id下的子孙树*/
    public function getCatTree($arr,$id = 1,$lev=0){
        $tree = array();
        foreach ($arr as $value) {
            if($value['parent_id'] == $id){
                $value['lev'] = $lev;
                $tree[] = $value;
                // 将一个或多个数组的单元合并起来
                $tree = array_merge($tree,$this->getCatTree($arr,$value['cat_id'],$lev+1));
            }
        }
        return $tree;
        //递归排序
    }
    
    //查找祖先
    public function getTree($id){
        $tree = array();
        $cate = $this->getAll();
        //最顶级为1只要还没找到顶级就继续寻找
        while($id>1){
            foreach ($cate as $value){
                //寻找当前的父类$id为当前元素的parent_id只要找到与parent_id一致的cat_id就是找到父级
                if($value['cat_id'] == $id){
                    $tree[] = $value;
                    $id = $value['parent_id'];
                    break;
                }
            }
        }
        //返回当前的祖先们
        return array_reverse($tree);
    }
    
    //得到子孙 这里不用count原因 之后需要查看栏目子栏目
    public function getSon($id){
        $sql = "select * from ".$this->table." where parent_id=".$id;
        return $this->db->getAll($sql);
    }
    public function getCount($id){
        $sql = "select count(*) from goods where cat_id=".$id;
        return $this->db->getOne($sql);
    }

    
}

在Model基类当中要调用到数据库操作类,请读者自动翻看博主前面编写的数据库类

在数据库类当中会用到日志类当中的相关操作因此也会自动加载到日志类(同样请翻看以前的发表记录)

配置文件类

<?php
/*
读取配置文件的类
*/
//include './init.php';
defined("Hello")||exit("Hello deny!");
class config{
    //保护的 且可以通过类访问
    protected static $ins = null;
    protected $data = array();

    final protected function __construct(){
        //引入配置文件
        include(ROOT.'include/config.inc.php');
        //一次性将数组付给成员变量 下次直接找data属性上找
        $this->data = $_CFG;
    }
    final protected function __clone(){

    }
    public static function getIns(){
        //先判断是否有
        if(self::$ins instanceof self){
            return self::$ins;
        }else{
            self::$ins = new self();
            //返回对象
            return self::$ins;
        }
    }
    //用魔术方法读取data信息
    public function __get($key){
        if(array_key_exists($key, $this->data)){
            return $this->data[$key];
        }else{
            return null;
        }
    }
    public function __set($key,$value){
        $this->data[$key] = $value;
    }
}

MySmarty类文件

include(ROOT.'libs/smarty3/Smarty.class.php');
class MySmarty extends Smarty{
    public function __construct(){
        parent::__construct();
        //模板目录
        $this->template_dir = ROOT.'view/index';
        //编译后文件放置的位置
        $this->compile_dir = ROOT.'data/compile';
        //缓存文件的目录
        $this->cache_dir = ROOT.'data/cache';
        //开启缓存
        $this->caching = true;
    }
}

data目录用于存放客户端上传上来的图片 以及 smarty编译后的文件以及缓存 以及数据库产生的错误日志文件

common目录用于存方法视图文件要使用的静态资源比如样式文件 js脚本 图片等等

libs为扩展库

tools为工具类文件 比如购物车类 上传处理类 图片处理类 等非框架核心类文件

view存放视图文件  在MySmarty类文件当中应该设定视图所在路径 如果有必要在后边可以动态设置视图路径

2、 如果启用缓存,再根据编译文件生成缓存文件

3、 之后每次访问都会访问编译文件

如果启用缓存文件而且有缓存文件并且缓存文件没有过期,则直接访问缓存文件

原文地址:https://www.cnblogs.com/webcyh/p/11342979.html