php源码建博客4--实现MVC结构微型框架

Posted 遗落的白菜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了php源码建博客4--实现MVC结构微型框架相关的知识,希望对你有一定的参考价值。

主要:

  1. 常量优化路径
  2. 自动加载类
  3. 优化入口文件
  4. 安全访问项目目录

--------------文件结构:--------------------------------------
blog
├─App
│  ├─Model 模型
│  │  └─UserModel.class.php 用户模型类    
│  ├─View 视图
│  │  ├─Back后台
│  │  │  └─Index
│  │  │          └─index.html  后台首页面
│  │  └─Home前台
│  │      └─User 用户视图目录
│  │             └─login.html  登录表单页面
│  ├─Controller 控制器
│  │  ├─Back后台
│  │  │  └─IndexController.class.php 后台首页控制器
│  │  └─Home前台
│  │      └─UserController.class.php 用户控制器
├─Public   静态公共文件(js,css,images)
│  ├─Plugins 插件
│  │  └─layui 前端框架插件
│  ├─Back后台
│  │    ├─js/   js文件
│  │    ├─css/  css样式文件
│  │    └─image img图片
│  └─Home前台
│      ├─js/   js文件
│      ├─css/  css样式文件
│      └─image img图片
├─Frame 公共使用的类
│   ├─BaseModel.class.php 数据库连接类
│   ├─BaseController.class.php 控制器公共操作(设置编码,信息跳转)
│   ├─FactoryModel.class.php  模型工厂类
│   ├─Init.class.php  初始化应用类
│   └─mysqlDB.class.php 数据库操作工具类
└─index.php  入口文件 -----------------------------------------------------------------

上一篇中,提出4个问题待解决,本篇集中解决这4个问题,最终形成完整的微型MVC框架结构, 后续博客项目,或其他项目,均可以直接使用该框架结构进行开发学习。

下载查看该项目源码: https://gitee.com/NewbiesYang/young_blog

常量优化路径

准备: 创建分支

1 $ git checkout master
2 $ git checkout -b "MVC"

  思路

  1)把常用的目录路径定义成常量。如 模型目录,控制器目录等
     2)引入类使用定义的常量替代部分路径。 如 include FRAME.BaseModel.class.php
       3) 载入视图使用常量替代部分路径 如 include VIEW.\'login.html\' 简单形式

  代码实现

 1)操作步骤

step 1: 在入口文件中定义所需要的常量

step 2: 控制器中引入视图时, 使用常量进行优化 
操作步骤思路

  2) 入口文件中定义常用路径常量 【index.php】

 1 <?php
 2 /**
 3  * 入口文件
 4  */
 5 $p = !empty($_GET[\'p\']) ? $_GET[\'p\'] : \'Home\';  //平台
 6 $c = !empty($_GET[\'c\']) ? $_GET[\'c\'] : \'User\';  //控制器
 7 $a = !empty($_GET[\'a\']) ? $_GET[\'a\'] : \'login\'; //动作
 8 
 9 define(\'PLAT\', $p);  //平台常量
10 define(\'CTR\', $c);  //控制器
11 define(\'ACTION\', $a); //动作
12 
13 
14 define(\'DS\', DIRECTORY_SEPARATOR); //目录分割符
15 define(\'ROOT\', getcwd().DS);  //当前所在目录 项目目录
16 define(\'FRAME\', ROOT.\'Frame\'.DS);
17 define(\'APP\', ROOT.\'App\'.DS);
18 define(\'PUB\', ROOT.\'Public\'.DS);
19 define(\'ADMIN\', PUB.\'Admin\'.DS);
20 define(\'HOME\', PUB.\'Home\'.DS);
21 
22 //MVC目录
23 define(\'MODEL\', APP.\'Model\'.DS);
24 define(\'VIEW\', APP.\'View\'.DS.PLAT.DS.CTR.DS);
25 define(\'CTRONLLER\', APP.\'Controller\'.DS.PLAT.DS);
26 
27 $ctr = $c."Controller";
28 
29 require_once FRAME.\'Db.class.php\';  //数据库操作类
30 require_once FRAME.\'BaseModel.class.php\';  //基础模型类
31 require_once MODEL.\'UserModel.class.php\';  //用户模型类
32 require_once FRAME.\'FactoryModel.class.php\';//模型工厂类
33 require_once FRAME.\'BaseController.class.php\'; //基础控制器类
34 require_once CTRONLLER.$ctr.\'.class.php\';
35 
36 
37 //实例化控制器
38 $userCtr = new $ctr();
39 
40 $userCtr -> $a();

2) 常量的使用:

  后台首页控制器【App/Controller/Admin/IndexController.class.php】

 1 <?php
 2 /**
 3  * IndexController控制器类
 4  * 后台相关操作
 5  * User: young
 6  */
 7 
 8 class IndexController extends BaseController
 9 {
10     //展示后台首页
11     public function index()
12     {
13         include VIEW.\'index.html\';
14     }
15 }
后台首页控制器引入视图路径修改

  用户控制器 登录视图引入路径【App/Controller/Home/UserController.class.php】

 1 <?php
 2 /**
 3  * UserController.class.php 用户控制器
 4  */
 5 
 6 class UserController  extends  BaseController{
 7     /**
 8      * 展示登录界面
 9      * @access public
10      */
11     public function login()
12     {
13         include VIEW."login.html";
14     }
15 。。。
16 。。。
17 。。。
用户控制器登录视图引入路径

3)提交代码

$  git add -A
$  git commit -m "常量使用"

自动加载类

   思路

  问题: 入口文件中已经require_once 引入6个类,既增加一个需要引入一个,容易遗漏,重复和出错。

  解决方法:自动加载类文件

     方式1: 使用自动加载类函数__autoload()可以实现自动加载
          方式2: 实际项目中,多人开发,根据实用性,更多的是使用 sql_autoload_register()注册函数自动加载

  根据目录的特点实现自动加载
          Model类文件特点,以Model结尾的类名  substr($className,-5)
          Controller文件特点: 以Controller结尾的类名, substr($class,-10)

    公共类: 类名没有统一形式,可以将Fame下的公共类放入到数组中,然后判断类是否在数组中, 从而自动加载该目录下的类文件

  代码实现

   1) 入口文件实现类的自动加载

 1 <?php
 2 /**
 3  * 入口文件
 4  */
 5 $p = !empty($_GET[\'p\']) ? $_GET[\'p\'] : \'Home\';  //平台
 6 $c = !empty($_GET[\'c\']) ? $_GET[\'c\'] : \'User\';  //控制器
 7 $a = !empty($_GET[\'a\']) ? $_GET[\'a\'] : \'login\'; //动作
 8 
 9 define(\'PLAT\', $p);  //平台常量
10 define(\'CTR\', $c);  //控制器
11 define(\'ACTION\', $a); //动作
12 
13 
14 define(\'DS\', DIRECTORY_SEPARATOR); //目录分割符
15 define(\'ROOT\', getcwd().DS);  //当前所在目录 项目目录
16 define(\'FRAME\', ROOT.\'Frame\'.DS);
17 define(\'APP\', ROOT.\'App\'.DS);
18 define(\'PUB\', ROOT.\'Public\'.DS);
19 define(\'ADMIN\', PUB.\'Admin\'.DS);
20 define(\'HOME\', PUB.\'Home\'.DS);
21 
22 //MVC目录
23 define(\'MODEL\', APP.\'Model\'.DS);
24 define(\'VIEW\', APP.\'View\'.DS.PLAT.DS.CTR.DS);
25 define(\'CTRONLLER\', APP.\'Controller\'.DS.PLAT.DS);
26 
27 $ctr = $c."Controller";
28 
29 spl_autoload_register(\'autoload\'); //注册自动加载函数
30 //自动加载类
31 /**
32  * 实自动加载类文件
33  * @param  string $className 类名
34  */
35 function autoload($className)
36 {
37     $upperClassName = strtoupper($className);
38     $frame = array(\'BaseController\',\'BaseModel\',\'Db\',\'FactoryModel\');
39     if(in_array($className, $frame)) {  //加载公共Frame目录中的类文件
40         require_once FRAME."$className.class.php";
41     } elseif(substr($upperClassName, -5) == \'MODEL\'){  //加载模型Model目录中的类文件
42         require_once MODEL."$className.class.php";
43     } elseif(substr($upperClassName, -10) == \'CONTROLLER\'){  //加载控制器目录中的类文件
44         require_once CTRONLLER."$className.class.php";
45     }
46 }
47 
48 //实例化控制器
49 $userCtr = new $ctr();
50 $userCtr -> $a();

2) 提交代码

1 $  git add -A
2 $  git commit -m "自动加载类完成"

优化入口文件

  思路

  问题: 此时,入口文件代码零碎增多,随着后续代码的增加,入口文件会更加臃肿复杂,不易管理

  解决方法: 封装入口文件中的操作称为一个类,这样只需要在入口文件调用类的方法即可

    创建Init.class.php类文件,放入到Frame中
         将入口文件所有操作封装成类方法
              loadClass() 设置自动加载函数
              autoload()自动加载类
              setConst()  定义常量
              dispatch()  前端分发器

  代码实现

   1) 在Frame目录中创建Init.class.php文件, 将入口文件index中的代码复制进行修改为类

  【Frame/Init.class.php】

 1 <?php
 2 /**
 3  * 应用初始化操作类
 4  * User: young
 5  */
 6 
 7 class Init
 8 {
 9     protected static $frame = array(\'BaseController\',\'BaseModel\',\'Db\',\'FactoryModel\'); //Frame目录公共操作类
10     public static function run()
11     {
12         //平台
13         self::dispatch();
14 
15         //定义常量
16         self::setConst();
17 
18         //自动加载类
19         self::loadClass();
20 
21         $ctr = CTR."Controller";  //拼接控制器名称
22 
23         //实例化控制器
24         $ctrObj = new $ctr();
25         $a = ACTION;
26         $ctrObj -> $a();
27     }
28     /**
29      * 设置自动加载类方法
30      */
31     private static function loadClass()
32     {
33         spl_autoload_register(\'self::autoload\');
34     }
35 
36     /**
37      * 实现自动加载
38      * @param  string $className 类名
39      */
40     private static function autoload($className)
41     {
42         $upperClassName = strtoupper($className);
43         if(in_array($className, static::$frame)) {
44             require_once FRAME."$className.class.php";
45         } elseif(substr($upperClassName, -5) == \'MODEL\'){
46             require_once MODEL."$className.class.php";
47         } elseif(substr($upperClassName, -10) == \'CONTROLLER\'){
48             require_once CTRONLLER."$className.class.php";
49         }
50     }
51 
52     /**
53      * 定义常量
54      */
55     private static function setConst()
56     {
57         define(\'DS\', DIRECTORY_SEPARATOR); //目录分割符
58         define(\'ROOT\', getcwd().DS);
59         define(\'FRAME\', ROOT.\'Frame\'.DS);
60         define(\'APP\', ROOT.\'App\'.DS);
61         define(\'PUB\', ROOT.\'Public\'.DS);
62         define(\'ADMIN\', PUB.\'Admin\'.DS);
63         define(\'HOME\', PUB.\'Home\'.DS);
64 
65 
66         define(\'MODEL\', APP.\'Model\'.DS);
67         define(\'VIEW\', APP.\'View\'.DS.PLAT.DS.CTR.DS);
68         define(\'CTRONLLER\', APP.\'Controller\'.DS.PLAT.DS);
69     }
70 
71     /**
72      * 获取 p c a 的GET值,并设置为常量
73      * @return void
74      */
75     private static function dispatch()
76     {
77         $p = !empty($_GET[\'p\']) ? $_GET[\'p\'] : \'Home\';  //平台
78         $c = !empty($_GET[\'c\']) ? $_GET[\'c\'] : \'User\';  //控制器
79         $a = !empty($_GET[\'a\']) ? $_GET[\'a\'] : \'login\'; //动作
80 
81         define(\'PLAT\', $p);
82         define(\'CTR\', $c);
83         define(\'ACTION\', $a);
84     }
85 }

2) 入口文件引入初始化类,并调用其方法 【index.php】

1 <?php
2 /**
3  * 入口文件
4  */
5 
6 require_once \'./Frame/Init.class.php\';
7 Init::run();

3) 提交代码

1 $  git add -A
2 $  git commit -m "优化入口文件,封装初始化类"

安全访问项目目录

   思路

  问题: 此时,项目中所有目录都是可以通过浏览器访问的,如直接访问Frame/Db.class.php文件 直接可以去查看数据库登录信息,显然是不安全的。

  解决方法:

    方式1: 在可以访问的文件开始处定义常量,访问是判断是否定义常量defined(..), 没有定义指定常量则直接exit(\'Access Deny\');

    方式2: 开启分布式权限配置,编写.htaccess文件, 如禁止访问, 将该文件放置在禁止访问的目录中

   实现

  1)使用上述方式2的形式来实现, 站点配置中加入一项(环境搭建时已经加入了): 详细见: PHP源码搭建博客1-环境搭建

  apache配置文件httpd-vhosts.conf 中站点配置

1  #允许分布式权限配置(允许重写)(.htacess)
2   AllowOverride All

  2) 重启apache后,编写 .htaccess文件, 该文件内容:

deny from all

   3) 将.htaccess文件放置禁止访问的目录中。  如App/ ,  Frame/ 目录下。只用放在第一层即可,内层目录自动不允许直接访问。

                               

  4) 访问测试

                   

小结:

主要实现了 引入路径优化, 类的自动加载, 封装优化入口文件,目录访问限制

MVC微型框架到此基本完成。其实还有很多还是可以继续扩展,如

  1, 类文件命名此处都用了 .class.php结尾, 实质可以优化直接使用.php结尾

  2, 引入命名空间,更方便的加载类

       3, 项目中出现错误,此时是直接显示在浏览器上的, 可以写一个日志类,发生错误写入文件或数据库都可

  4, 数据库连接信息此处是直接写在DB类和BaseModel中了, 是不安全的。 可以创建一个配置目录,将这些信息写入配置文件,再写一个加载配置文件的类。

  5.   此架构目录 ,是在C,V中分平台,如Controller/Home, Controller/Admin;  实际也可以写成 平台下分MVC结构, 如Admin/Controller, Admin/Model, Home/Controller,Home/View ..  这个是比较灵活的,可以根据需求选择更加合适的方式

  实际上线项目,还是建议使用框架,安全快捷; 自己模仿定义的框架结构适合学习研究使用,容易遗漏,造成安全隐患,操作不便等问题

下一步:根据博客前端模板,分析创建数据表, 开始搭建博客后台程序,后续首先准备实现 “分类模块”。既分类的展示,修改,添加,删除功能

以上是关于php源码建博客4--实现MVC结构微型框架的主要内容,如果未能解决你的问题,请参考以下文章

php源码建博客5--建库建表-配置文件-错误日志

php源码建博客1--搭建站点-实现登录页面

ASP.NET MVC 使用 Petapoco 微型ORM框架+NpgSql驱动连接 PostgreSQL数据库

基于scrapy源码实现的自定义微型异步爬虫框架

神级Java程序员 开车教你基础开发,最简单 微型Java Web框架

ASP.NET MVC 使用 Petapoco 微型ORM框架+NpgSql驱动连接 PostgreSQL数据库(问题总结)