首页  >  文章  >  php教程  >  编写自己的PHP MVC框架笔记

编写自己的PHP MVC框架笔记

WBOY
WBOY原创
2016-09-28 08:38:231320浏览

 

1、MVC

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

PHP中MVC模式也称Web MVC,从上世纪70年代进化而来。MVC的目的是实现一种动态的程序设计,便于后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。除此之外,此模式通过对复杂度的简化,使程序结构更加直观。软件系统通过对自身基本部份分离的同时,也赋予了各个基本部分应有的功能。

MVC各部分的职能:

  • 模型Model – 管理大部分的业务逻辑所有的数据库逻辑。模型提供了连接和操作数据库的抽象层。
  • 控制器Controller - 负责响应用户请求、准备数据,以及决定如何展示数据。
  • 视图View – 负责渲染数据,通过HTML方式呈现给用户。

 

一个典型的Web MVC流程:

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

2、为什么自己开发MVC框架

网络上有大量优秀的MVC框架可供使用,本教程并不是为了开发一个全面的、终极的MVC框架解决方案,而是将它看作是一个很好的从内部学习PHP的机会,在此过程中,你将学习面向对象编程MVC设计模式,并学习到开发中的一些注意事项。

更重要的是,你可以完全控制你的框架,并将你的想法融入到你开发的框架中。虽然不一定是做好的,但是你可以按照你的方式去开发功能和模块。

3、准备开发自己的MVC框架

3.1目录准备

在开始开发前,让我们先来把项目建立好,假设我们建立的项目为 myphp-frame,MVC的框架可以命名为 MyPHP,那么接下来的第一步就是把目录结构先设置好。

 

 

虽然在这个教程中不会使用到上面的所有的目录,但是为了以后程序的可拓展性,在一开始就把程序目录设置好使非常必要的。下面就具体说说每个目录的作用:

  • application – 应用代码
  • config – 程序配置或数据库配置
  • myphp - 框架核心目录
  • public – 静态文件
  • runtime - 临时数据目录

3.2代码规范

在目录设置好以后,我们接下来就要来规定一下代码的规范:

  1. MySQL的表名需小写,如:item,car
  2. 模块名(Models)需首字母大写,,并在名称后添加“Model”,如:ItemModel,CarModel
  3. 控制器(Controllers)需首字母大写,,并在名称中添加“Controller”,如:ItemController,CarController
  4. 视图(Views)部署结构为“控制器名/行为名”,如:item/view.php,car/buy.php

上述的一些规则是为了能在程序中更好的进行互相的调用。接下来就开始真正的PHP MVC编程了

3.3重定向

将所有的数据请求都重定向 index.php 文件,在 myphp-frame 目录下新建一个 .htaccess 文件,文件内容为:

<ifmodule mod_rewrite.c><span style="color: #000000;">

    RewriteEngine On

    </span><span style="color: #008000;">#</span><span style="color: #008000;"> 确保请求路径不是一个文件名或目录</span>
<span style="color: #000000;">
    RewriteCond </span>%{REQUEST_FILENAME} !-<span style="color: #000000;">f

    RewriteCond </span>%{REQUEST_FILENAME} !-<span style="color: #000000;">d

    </span><span style="color: #008000;">#</span><span style="color: #008000;"> 重定向所有请求到 index.php?url=PATHNAME</span>
<span style="color: #000000;">
    RewriteRule </span>^(.*)$ index.php?url=$1 [PT,<span style="color: #000000;">L]

</span></ifmodule>

这样做的主要原因有:

  1. 程序有一个单一的入口;
  2. 除静态程序,其他所有程序都重定向到 index.php 上;
  3. 可以用来生成利于SEO的URL,想要更好的配置URL,后期可能会需要URL路由,这里先不做介绍了。

3.4入口文件

做完上面的操作,就应该知道我们需要做什么了,没错!在 myphp-frame目录下添加 index.php 文件,文件内容为:

<span style="color: #000000;">php 
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 应用目录为当前目录</span>
<span style="color: #008080;">define</span>('APP_PATH', __DIR__.'/'<span style="color: #000000;">);
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 开启调试模式</span>
<span style="color: #008080;">define</span>('APP_DEBUG', <span style="color: #0000ff;">true</span><span style="color: #000000;">);
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 网站根URL</span>
<span style="color: #008080;">define</span>('APP_URL', 'http://localhost/myphp'<span style="color: #000000;">);
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 加载框架</span>
<span style="color: #0000ff;">require</span> './myphp/MyPHP.php';

注意,上面的PHP代码中,并没有添加PHP结束符号”?>”,这么做的主要原因是,对于只有 PHP 代码的文件,结束标志(“?>”)最好不存在,PHP自身并不需要结束符号,不添加结束符号可以很大程度上防止末尾被添加额外的注入内容,让程序更加安全。

3.5配置文件和主请求

在 index.php 中,我们对 myphp  文件夹下的 MyPHP.php 发起了请求,那么 MyPHP.php 这个启动文件中到底会包含哪些内容呢?

<span style="color: #000000;">php
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 初始化常量</span>
<span style="color: #008080;">defined</span>('FRAME_PATH') or <span style="color: #008080;">define</span>('FRAME_PATH', __DIR__.'/'<span style="color: #000000;">);
</span><span style="color: #008080;">defined</span>('APP_PATH') or <span style="color: #008080;">define</span>('APP_PATH', <span style="color: #008080;">dirname</span>(<span style="color: #800080;">$_SERVER</span>['SCRIPT_FILENAME']).'/'<span style="color: #000000;">);
</span><span style="color: #008080;">defined</span>('APP_DEBUG') or <span style="color: #008080;">define</span>('APP_DEBUG', <span style="color: #0000ff;">false</span><span style="color: #000000;">);
</span><span style="color: #008080;">defined</span>('CONFIG_PATH') or <span style="color: #008080;">define</span>('CONFIG_PATH', APP_PATH.'config/'<span style="color: #000000;">);
</span><span style="color: #008080;">defined</span>('RUNTIME_PATH') or <span style="color: #008080;">define</span>('RUNTIME_PATH', APP_PATH.'runtime/'<span style="color: #000000;">);
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 包含配置文件</span>
<span style="color: #0000ff;">require</span> APP_PATH . 'config/config.php'<span style="color: #000000;">;
</span><span style="color: #008000;">//</span><span style="color: #008000;">包含核心框架类</span>
<span style="color: #0000ff;">require</span> FRAME_PATH . 'Core.php'<span style="color: #000000;">; 
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 实例化核心类</span>
<span style="color: #800080;">$fast</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Core;
</span><span style="color: #800080;">$fast</span>->run();

以上文件都其实可以直接在 index.php 文件中包含,常量也可以直接在 index.php 中定义,我们这么做的原因是为了在后期管理和拓展中更加的方便,所以把需要在一开始的时候就加载运行的程序统一放到一个单独的文件中引用。

先来看看config文件下的 config .php 文件,该文件的主要作用是设置一些程序的配置项及数据库连接等,主要内容为:

<span style="color: #000000;">php
</span><span style="color: #008000;">/*</span><span style="color: #008000;">* 变量配置 *</span><span style="color: #008000;">*/</span>
<span style="color: #008080;">define</span>('DB_NAME',<span style="color: #000000;"> mydb);
</span><span style="color: #008080;">define</span>('DB_USER', 'root'<span style="color: #000000;">);
</span><span style="color: #008080;">define</span>('DB_PASSWORD', 'root'<span style="color: #000000;">);
</span><span style="color: #008080;">define</span>('DB_HOST', 'localhost');

应该说 config.php 涉及到的内容并不多,不过是一些基础数据库的设置,再来看看 myphp下的共用框架入口文件 Core.php 应该怎么写。

<span style="color: #000000;">php
</span><span style="color: #008000;">/*</span><span style="color: #008000;">*
 * MyPHP核心框架
 </span><span style="color: #008000;">*/</span>
<span style="color: #0000ff;">class</span><span style="color: #000000;"> Core
{
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 运行程序</span>
    <span style="color: #0000ff;">function</span><span style="color: #000000;"> run()
    {
        spl_autoload_register(</span><span style="color: #0000ff;">array</span>(<span style="color: #800080;">$this</span>, 'loadClass'<span style="color: #000000;">));

        </span><span style="color: #800080;">$this</span>-><span style="color: #000000;">setReporting();

        </span><span style="color: #800080;">$this</span>-><span style="color: #000000;">removeMagicQuotes();

        </span><span style="color: #800080;">$this</span>-><span style="color: #000000;">unregisterGlobals();

        </span><span style="color: #800080;">$this</span>-><span style="color: #000000;">Route();
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 路由处理</span>
    <span style="color: #0000ff;">function</span><span style="color: #000000;"> Route()
    {
       </span><span style="color: #800080;">$controllerName</span> = 'Index'<span style="color: #000000;">;
        </span><span style="color: #800080;">$action</span> = 'index'<span style="color: #000000;">;
        </span><span style="color: #0000ff;">if</span> (!<span style="color: #0000ff;">empty</span>(<span style="color: #800080;">$_GET</span>['url'<span style="color: #000000;">])) {
            </span><span style="color: #800080;">$url</span> = <span style="color: #800080;">$_GET</span>['url'<span style="color: #000000;">];
            </span><span style="color: #800080;">$urlArray</span> = <span style="color: #008080;">explode</span>('/', <span style="color: #800080;">$url</span><span style="color: #000000;">);
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> 获取控制器名</span>
            <span style="color: #800080;">$controllerName</span> = <span style="color: #008080;">ucfirst</span>(<span style="color: #800080;">$urlArray</span>[0<span style="color: #000000;">]);
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> 获取动作名</span>
            <span style="color: #008080;">array_shift</span>(<span style="color: #800080;">$urlArray</span><span style="color: #000000;">);
            </span><span style="color: #800080;">$action</span> = <span style="color: #0000ff;">empty</span>(<span style="color: #800080;">$urlArray</span>[0]) ? 'index' : <span style="color: #800080;">$urlArray</span>[0<span style="color: #000000;">];
            </span><span style="color: #008000;">//</span><span style="color: #008000;">获取URL参数</span>
            <span style="color: #008080;">array_shift</span>(<span style="color: #800080;">$urlArray</span><span style="color: #000000;">);
            </span><span style="color: #800080;">$queryString</span> = <span style="color: #0000ff;">empty</span>(<span style="color: #800080;">$urlArray</span>) ? <span style="color: #0000ff;">array</span>() : <span style="color: #800080;">$urlArray</span><span style="color: #000000;">;
        }
        </span><span style="color: #008000;">//</span><span style="color: #008000;"> 数据为空的处理</span>
        <span style="color: #800080;">$queryString</span>  = <span style="color: #0000ff;">empty</span>(<span style="color: #800080;">$queryString</span>) ? <span style="color: #0000ff;">array</span>() : <span style="color: #800080;">$queryString</span><span style="color: #000000;">;
        </span><span style="color: #008000;">//</span><span style="color: #008000;"> 实例化控制器</span>
        <span style="color: #800080;">$controller</span> = <span style="color: #800080;">$controllerName</span> . 'Controller'<span style="color: #000000;">;
        </span><span style="color: #800080;">$dispatch</span> = <span style="color: #0000ff;">new</span> <span style="color: #800080;">$controller</span>(<span style="color: #800080;">$controllerName</span>, <span style="color: #800080;">$action</span><span style="color: #000000;">);
        </span><span style="color: #008000;">//</span><span style="color: #008000;"> 如果控制器存和动作存在,这调用并传入URL参数</span>
        <span style="color: #0000ff;">if</span> ((int)<span style="color: #008080;">method_exists</span>(<span style="color: #800080;">$controller</span>, <span style="color: #800080;">$action</span><span style="color: #000000;">)) {
            </span><span style="color: #008080;">call_user_func_array</span>(<span style="color: #0000ff;">array</span>(<span style="color: #800080;">$dispatch</span>, <span style="color: #800080;">$action</span>), <span style="color: #800080;">$queryString</span><span style="color: #000000;">);
        } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {
            </span><span style="color: #0000ff;">exit</span>(<span style="color: #800080;">$controller</span> . "控制器不存在"<span style="color: #000000;">);
        }
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 检测开发环境</span>
    <span style="color: #0000ff;">function</span><span style="color: #000000;"> setReporting()
    {
        </span><span style="color: #0000ff;">if</span> (APP_DEBUG === <span style="color: #0000ff;">true</span><span style="color: #000000;">) {
            </span><span style="color: #008080;">error_reporting</span>(<span style="color: #ff00ff;">E_ALL</span><span style="color: #000000;">);
            </span><span style="color: #008080;">ini_set</span>('display_errors','On'<span style="color: #000000;">);
        } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {
            </span><span style="color: #008080;">error_reporting</span>(<span style="color: #ff00ff;">E_ALL</span><span style="color: #000000;">);
            </span><span style="color: #008080;">ini_set</span>('display_errors','Off'<span style="color: #000000;">);
            </span><span style="color: #008080;">ini_set</span>('log_errors', 'On'<span style="color: #000000;">);
            </span><span style="color: #008080;">ini_set</span>('error_log', RUNTIME_PATH. 'logs/error.log'<span style="color: #000000;">);
        }
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 删除敏感字符</span>
    <span style="color: #0000ff;">function</span> stripSlashesDeep(<span style="color: #800080;">$value</span><span style="color: #000000;">)
    {
        </span><span style="color: #800080;">$value</span> = <span style="color: #008080;">is_array</span>(<span style="color: #800080;">$value</span>) ? <span style="color: #008080;">array_map</span>('stripSlashesDeep', <span style="color: #800080;">$value</span>) : <span style="color: #008080;">stripslashes</span>(<span style="color: #800080;">$value</span><span style="color: #000000;">);
        </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$value</span><span style="color: #000000;">;
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 检测敏感字符并删除</span>
    <span style="color: #0000ff;">function</span><span style="color: #000000;"> removeMagicQuotes()
    {
        </span><span style="color: #0000ff;">if</span> ( <span style="color: #008080;">get_magic_quotes_gpc</span><span style="color: #000000;">()) {
           </span><span style="color: #800080;">$_GET</span> = stripSlashesDeep(<span style="color: #800080;">$_GET</span><span style="color: #000000;"> );
            </span><span style="color: #800080;">$_POST</span> = stripSlashesDeep(<span style="color: #800080;">$_POST</span><span style="color: #000000;"> );
            </span><span style="color: #800080;">$_COOKIE</span> = stripSlashesDeep(<span style="color: #800080;">$_COOKIE</span><span style="color: #000000;">);
            </span><span style="color: #800080;">$_SESSION</span> = stripSlashesDeep(<span style="color: #800080;">$_SESSION</span><span style="color: #000000;">);
        }
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 检测自定义全局变量(register globals)并移除</span>
    <span style="color: #0000ff;">function</span><span style="color: #000000;"> unregisterGlobals()
    {
        </span><span style="color: #0000ff;">if</span> (<span style="color: #008080;">ini_get</span>('register_globals'<span style="color: #000000;">)) {
            </span><span style="color: #800080;">$array</span> = <span style="color: #0000ff;">array</span>('_SESSION', '_POST', '_GET', '_COOKIE', '_REQUEST', '_SERVER', '_ENV', '_FILES'<span style="color: #000000;">);
           </span><span style="color: #0000ff;">foreach</span> (<span style="color: #800080;">$array</span> <span style="color: #0000ff;">as</span> <span style="color: #800080;">$value</span><span style="color: #000000;">) {
                </span><span style="color: #0000ff;">foreach</span> (<span style="color: #800080;">$GLOBALS</span>[<span style="color: #800080;">$value</span>] <span style="color: #0000ff;">as</span> <span style="color: #800080;">$key</span> => <span style="color: #800080;">$var</span><span style="color: #000000;">) {
                    </span><span style="color: #0000ff;">if</span> (<span style="color: #800080;">$var</span> === <span style="color: #800080;">$GLOBALS</span>[<span style="color: #800080;">$key</span><span style="color: #000000;">]) {
                        </span><span style="color: #0000ff;">unset</span>(<span style="color: #800080;">$GLOBALS</span>[<span style="color: #800080;">$key</span><span style="color: #000000;">]);
                    }
                }
            }
        }
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 自动加载控制器和模型类</span>
    <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">function</span> loadClass(<span style="color: #800080;">$class</span><span style="color: #000000;">)
    {
        </span><span style="color: #800080;">$frameworks</span> = FRAME_PATH . <span style="color: #800080;">$class</span> . '.class.php'<span style="color: #000000;">;
        </span><span style="color: #800080;">$controllers</span> = APP_PATH . 'application/controllers/' . <span style="color: #800080;">$class</span> . '.class.php'<span style="color: #000000;">;
        </span><span style="color: #800080;">$models</span> = APP_PATH . 'application/models/' . <span style="color: #800080;">$class</span> . '.class.php'<span style="color: #000000;">;
        </span><span style="color: #0000ff;">if</span> (<span style="color: #008080;">file_exists</span>(<span style="color: #800080;">$frameworks</span><span style="color: #000000;">)) {
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> 加载框架核心类</span>
            <span style="color: #0000ff;">include</span> <span style="color: #800080;">$frameworks</span><span style="color: #000000;">;
        } </span><span style="color: #0000ff;">elseif</span> (<span style="color: #008080;">file_exists</span>(<span style="color: #800080;">$controllers</span><span style="color: #000000;">)) {
            </span><span style="color: #008000;">//</span><span style="color: #008000;"> 加载应用控制器类</span>
            <span style="color: #0000ff;">include</span> <span style="color: #800080;">$controllers</span><span style="color: #000000;">;
        } </span><span style="color: #0000ff;">elseif</span> (<span style="color: #008080;">file_exists</span>(<span style="color: #800080;">$models</span><span style="color: #000000;">)) {
            </span><span style="color: #008000;">//</span><span style="color: #008000;">加载应用模型类</span>
            <span style="color: #0000ff;">include</span> <span style="color: #800080;">$models</span><span style="color: #000000;">;
        } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {
            </span><span style="color: #008000;">/*</span><span style="color: #008000;"> 错误代码 </span><span style="color: #008000;">*/</span><span style="color: #000000;">
        }
    }
}</span>

下面重点讲解主请求方法 callHook(),首先我们想看看我们的 URL 会这样:

yoursite.com/controllerName/actionName/queryString

callHook()的作用就是,从全局变量 $_GET['url']变量中获取 URL,并将其分割成三部分:$controller、$action 和 $queryString。

例如,URL链接为:myphp.com/item/view/1/first-item,那么

  • $controller 就是:item
  • $action 就是:view
  • 查询字符串Query String就是:array(1, first-item)

分割完成后,会实例化一个新的控制器:$controller.'Controller'(其中“.”是连字符),并调用其方法 $action。

3.6控制器/Controller基类

接下来的操作就是在 myphp 中建立程序所需的基类,包括控制器模型视图的基类。

新建控制器基类为 Controller.class.php,控制器的主要功能就是总调度,具体具体内容如下:

<span style="color: #000000;">php 
</span><span style="color: #008000;">/*</span><span style="color: #008000;">*
 * 控制器基类
 </span><span style="color: #008000;">*/</span>
<span style="color: #0000ff;">class</span><span style="color: #000000;"> Controller
{
    </span><span style="color: #0000ff;">protected</span> <span style="color: #800080;">$_controller</span><span style="color: #000000;">;
    </span><span style="color: #0000ff;">protected</span> <span style="color: #800080;">$_action</span><span style="color: #000000;">;
    </span><span style="color: #0000ff;">protected</span> <span style="color: #800080;">$_view</span><span style="color: #000000;">;
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 构造函数,初始化属性,并实例化对应模型</span>
    <span style="color: #0000ff;">function</span> __construct(<span style="color: #800080;">$controller</span>, <span style="color: #800080;">$action</span><span style="color: #000000;">)
    {
        </span><span style="color: #800080;">$this</span>->_controller = <span style="color: #800080;">$controller</span><span style="color: #000000;">;
        </span><span style="color: #800080;">$this</span>->_action = <span style="color: #800080;">$action</span><span style="color: #000000;">;
        </span><span style="color: #800080;">$this</span>->_view = <span style="color: #0000ff;">new</span> View(<span style="color: #800080;">$controller</span>, <span style="color: #800080;">$action</span><span style="color: #000000;">);
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 分配变量</span>
    <span style="color: #0000ff;">function</span> assign(<span style="color: #800080;">$name</span>, <span style="color: #800080;">$value</span><span style="color: #000000;">)
    {
        </span><span style="color: #800080;">$this</span>->_view->assign(<span style="color: #800080;">$name</span>, <span style="color: #800080;">$value</span><span style="color: #000000;">);
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 渲染视图</span>
    <span style="color: #0000ff;">function</span><span style="color: #000000;"> __destruct()
    {
        </span><span style="color: #800080;">$this</span>->_view-><span style="color: #000000;">render();
    }
}</span>

Controller 类实现所有控制器、模型和视图(View类)的通信。在执行析构函数时,我们可以调用 render() 来显示视图(view)文件。

3.7模型Model基类

新建模型基类为 Model.class.php,模型基类 Model.class.php 代码如下:

<span style="color: #000000;">php
</span><span style="color: #0000ff;">class</span> Model <span style="color: #0000ff;">extends</span><span style="color: #000000;"> Sql
{
    </span><span style="color: #0000ff;">protected</span> <span style="color: #800080;">$_model</span><span style="color: #000000;">;
    </span><span style="color: #0000ff;">protected</span> <span style="color: #800080;">$_table</span><span style="color: #000000;">;
    </span><span style="color: #0000ff;">function</span><span style="color: #000000;"> __construct()
    {
        </span><span style="color: #008000;">//</span><span style="color: #008000;"> 连接数据库</span>
        <span style="color: #800080;">$this</span>->connect(DB_HOST, DB_USER, DB_PASSWORD,<span style="color: #000000;"> DB_NAME);  
        </span><span style="color: #008000;">//</span><span style="color: #008000;"> 获取模型名称</span>
        <span style="color: #800080;">$this</span>->_model = <span style="color: #008080;">get_class</span>(<span style="color: #800080;">$this</span><span style="color: #000000;">);
        </span><span style="color: #800080;">$this</span>->_model = <span style="color: #008080;">rtrim</span>(<span style="color: #800080;">$this</span>->_model, 'Model'<span style="color: #000000;">); 
        </span><span style="color: #008000;">//</span><span style="color: #008000;"> 数据库表名与类名一致</span>
        <span style="color: #800080;">$this</span>->_table = <span style="color: #008080;">strtolower</span>(<span style="color: #800080;">$this</span>-><span style="color: #000000;">_model);
    }
    </span><span style="color: #0000ff;">function</span><span style="color: #000000;"> __destruct()
    {
    }
}</span>

考虑到模型需要对数据库进行处理,所以单独建立一个数据库基类 Sql.class.php,模型基类继承 Sql.class.php,代码如下:

<span style="color: #000000;">php 
</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Sql
{
    </span><span style="color: #0000ff;">protected</span> <span style="color: #800080;">$_dbHandle</span><span style="color: #000000;">;
    </span><span style="color: #0000ff;">protected</span> <span style="color: #800080;">$_result</span><span style="color: #000000;">;
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 连接数据库</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> connect(<span style="color: #800080;">$host</span>, <span style="color: #800080;">$user</span>, <span style="color: #800080;">$pass</span>, <span style="color: #800080;">$dbname</span><span style="color: #000000;">)
    {
        </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
            </span><span style="color: #800080;">$dsn</span> = <span style="color: #008080;">sprintf</span>("mysql:host=%s;dbname=%s;charset=utf8", <span style="color: #800080;">$host</span>, <span style="color: #800080;">$dbname</span><span style="color: #000000;">);
            </span><span style="color: #800080;">$this</span>->_dbHandle = <span style="color: #0000ff;">new</span> PDO(<span style="color: #800080;">$dsn</span>, <span style="color: #800080;">$user</span>, <span style="color: #800080;">$pass</span>, <span style="color: #0000ff;">array</span>(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::<span style="color: #000000;">FETCH_ASSOC));
        } </span><span style="color: #0000ff;">catch</span> (PDOException <span style="color: #800080;">$e</span><span style="color: #000000;">) {
            </span><span style="color: #0000ff;">exit</span>('错误: ' . <span style="color: #800080;">$e</span>-><span style="color: #000000;">getMessage());
        }
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 查询所有</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> selectAll()
    {
        </span><span style="color: #800080;">$sql</span> = <span style="color: #008080;">sprintf</span>("select * from `%s`", <span style="color: #800080;">$this</span>-><span style="color: #000000;">_table);
        </span><span style="color: #800080;">$sth</span> = <span style="color: #800080;">$this</span>->_dbHandle->prepare(<span style="color: #800080;">$sql</span><span style="color: #000000;">);
        </span><span style="color: #800080;">$sth</span>-><span style="color: #000000;">execute();
 
        </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$sth</span>-><span style="color: #000000;">fetchAll();
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 根据条件 (id) 查询</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> select(<span style="color: #800080;">$id</span><span style="color: #000000;">)
    {
        </span><span style="color: #800080;">$sql</span> = <span style="color: #008080;">sprintf</span>("select * from `%s` where `id` = '%s'", <span style="color: #800080;">$this</span>->_table, <span style="color: #800080;">$id</span><span style="color: #000000;">);
        </span><span style="color: #800080;">$sth</span> = <span style="color: #800080;">$this</span>->_dbHandle->prepare(<span style="color: #800080;">$sql</span><span style="color: #000000;">);
        </span><span style="color: #800080;">$sth</span>-><span style="color: #000000;">execute();
        
        </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$sth</span>-><span style="color: #000000;">fetch();
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 根据条件 (id) 删除</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> delete(<span style="color: #800080;">$id</span><span style="color: #000000;">)
    {
        </span><span style="color: #800080;">$sql</span> = <span style="color: #008080;">sprintf</span>("delete from `%s` where `id` = '%s'", <span style="color: #800080;">$this</span>->_table, <span style="color: #800080;">$id</span><span style="color: #000000;">);
        </span><span style="color: #800080;">$sth</span> = <span style="color: #800080;">$this</span>->_dbHandle->prepare(<span style="color: #800080;">$sql</span><span style="color: #000000;">);
        </span><span style="color: #800080;">$sth</span>-><span style="color: #000000;">execute();
 
        </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$sth</span>-><span style="color: #000000;">rowCount();
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 自定义SQL查询,返回影响的行数</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> query(<span style="color: #800080;">$sql</span><span style="color: #000000;">)
    {
        </span><span style="color: #800080;">$sth</span> = <span style="color: #800080;">$this</span>->_dbHandle->prepare(<span style="color: #800080;">$sql</span><span style="color: #000000;">);
        </span><span style="color: #800080;">$sth</span>-><span style="color: #000000;">execute();
 
        </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$sth</span>-><span style="color: #000000;">rowCount();
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 新增数据</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> add(<span style="color: #800080;">$data</span><span style="color: #000000;">)
    {
        </span><span style="color: #800080;">$sql</span> = <span style="color: #008080;">sprintf</span>("insert into `%s` %s", <span style="color: #800080;">$this</span>->_table, <span style="color: #800080;">$this</span>->formatInsert(<span style="color: #800080;">$data</span><span style="color: #000000;">));
 
        </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$this</span>->query(<span style="color: #800080;">$sql</span><span style="color: #000000;">);
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 修改数据</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> update(<span style="color: #800080;">$id</span>, <span style="color: #800080;">$data</span><span style="color: #000000;">)
    {
        </span><span style="color: #800080;">$sql</span> = <span style="color: #008080;">sprintf</span>("update `%s` set %s where `id` = '%s'", <span style="color: #800080;">$this</span>->_table, <span style="color: #800080;">$this</span>->formatUpdate(<span style="color: #800080;">$data</span>), <span style="color: #800080;">$id</span><span style="color: #000000;">);
 
        </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$this</span>->query(<span style="color: #800080;">$sql</span><span style="color: #000000;">);
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 将数组转换成插入格式的sql语句</span>
    <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">function</span> formatInsert(<span style="color: #800080;">$data</span><span style="color: #000000;">)
    {
        </span><span style="color: #800080;">$fields</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">();
        </span><span style="color: #800080;">$values</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">();
        </span><span style="color: #0000ff;">foreach</span> (<span style="color: #800080;">$data</span> <span style="color: #0000ff;">as</span> <span style="color: #800080;">$key</span> => <span style="color: #800080;">$value</span><span style="color: #000000;">) {
            </span><span style="color: #800080;">$fields</span>[] = <span style="color: #008080;">sprintf</span>("`%s`", <span style="color: #800080;">$key</span><span style="color: #000000;">);
            </span><span style="color: #800080;">$values</span>[] = <span style="color: #008080;">sprintf</span>("'%s'", <span style="color: #800080;">$value</span><span style="color: #000000;">);
        }
 
        </span><span style="color: #800080;">$field</span> = <span style="color: #008080;">implode</span>(',', <span style="color: #800080;">$fields</span><span style="color: #000000;">);
        </span><span style="color: #800080;">$value</span> = <span style="color: #008080;">implode</span>(',', <span style="color: #800080;">$values</span><span style="color: #000000;">);
 
        </span><span style="color: #0000ff;">return</span> <span style="color: #008080;">sprintf</span>("(%s) values (%s)", <span style="color: #800080;">$field</span>, <span style="color: #800080;">$value</span><span style="color: #000000;">);
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 将数组转换成更新格式的sql语句</span>
    <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">function</span> formatUpdate(<span style="color: #800080;">$data</span><span style="color: #000000;">)
    {
        </span><span style="color: #800080;">$fields</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">();
        </span><span style="color: #0000ff;">foreach</span> (<span style="color: #800080;">$data</span> <span style="color: #0000ff;">as</span> <span style="color: #800080;">$key</span> => <span style="color: #800080;">$value</span><span style="color: #000000;">) {
            </span><span style="color: #800080;">$fields</span>[] = <span style="color: #008080;">sprintf</span>("`%s` = '%s'", <span style="color: #800080;">$key</span>, <span style="color: #800080;">$value</span><span style="color: #000000;">);
        }
        </span><span style="color: #0000ff;">return</span> <span style="color: #008080;">implode</span>(',', <span style="color: #800080;">$fields</span><span style="color: #000000;">);
    }
}</span>

应该说,Sql.class.php 是框架的核心部分。为什么?因为通过它,我们创建了一个 SQL 抽象层,可以大大减少了数据库的编程工作。虽然 PDO 接口本来已经很简洁,但是抽象之后框架的可灵活性更高。

3.8视图View类

视图类 View.class.php 内容如下:

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

这样我们的核心的PHP MVC框架就编写完成了,下面我们开始编写应用来测试框架功能。

4、应用

4.1数据库部署

在 SQL 中新建一个 mydb 数据库,使用下面的语句增加 item 数据表并插入2条记录:

CREATE DATABASE ` mydb ` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE ` mydb `;
CREATE TABLE `item` (
    `id` int(11) NOT NULL auto_increment,
    `item_name` varchar(255) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO `item` VALUES(1, 'Hello World.');
INSERT INTO `item` VALUES(2, 'Lets go!');

4.2部署模型

然后,我们还需要在 models 目录中创建一个 ItemModel.php 模型,内容如下:

<span style="color: #000000;">php
 
</span><span style="color: #0000ff;">class</span> ItemModel <span style="color: #0000ff;">extends</span><span style="color: #000000;"> Model
{
    </span><span style="color: #008000;">/*</span><span style="color: #008000;"> 业务逻辑层实现 </span><span style="color: #008000;">*/</span><span style="color: #000000;">
}</span>

模型内容为空。因为 Item 模型继承了 Model,所以它拥有 Model 的所有功能。

4.3部署控制器

在 controllers 目录下创建一个 ItemController.php 控制器,内容如下:

<span style="color: #000000;">php
</span><span style="color: #0000ff;">class</span> ItemController <span style="color: #0000ff;">extends</span><span style="color: #000000;"> Controller
{
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 首页方法,测试框架自定义DB查询</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> index()
    {
        </span><span style="color: #800080;">$items</span> = (<span style="color: #0000ff;">new</span> ItemModel)-><span style="color: #000000;">selectAll();
 
        </span><span style="color: #800080;">$this</span>->assign('title', '全部条目'<span style="color: #000000;">);
        </span><span style="color: #800080;">$this</span>->assign('items', <span style="color: #800080;">$items</span><span style="color: #000000;">);
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 添加记录,测试框架DB记录创建(Create)</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> add()
    {
        </span><span style="color: #800080;">$data</span>['item_name'] = <span style="color: #800080;">$_POST</span>['value'<span style="color: #000000;">];
        </span><span style="color: #800080;">$count</span> = (<span style="color: #0000ff;">new</span> ItemModel)->add(<span style="color: #800080;">$data</span><span style="color: #000000;">);
 
        </span><span style="color: #800080;">$this</span>->assign('title', '添加成功'<span style="color: #000000;">);
        </span><span style="color: #800080;">$this</span>->assign('count', <span style="color: #800080;">$count</span><span style="color: #000000;">);
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 查看记录,测试框架DB记录读取(Read)</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> view(<span style="color: #800080;">$id</span> = <span style="color: #0000ff;">null</span><span style="color: #000000;">)
    {
        </span><span style="color: #800080;">$item</span> = (<span style="color: #0000ff;">new</span> ItemModel)->select(<span style="color: #800080;">$id</span><span style="color: #000000;">);
 
        </span><span style="color: #800080;">$this</span>->assign('title', '正在查看' . <span style="color: #800080;">$item</span>['item_name'<span style="color: #000000;">]);
        </span><span style="color: #800080;">$this</span>->assign('item', <span style="color: #800080;">$item</span><span style="color: #000000;">);
    }
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 更新记录,测试框架DB记录更新(Update)</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> update()
    {
        </span><span style="color: #800080;">$data</span> = <span style="color: #0000ff;">array</span>('id' => <span style="color: #800080;">$_POST</span>['id'], 'item_name' => <span style="color: #800080;">$_POST</span>['value'<span style="color: #000000;">]);
        </span><span style="color: #800080;">$count</span> = (<span style="color: #0000ff;">new</span> ItemModel)->update(<span style="color: #800080;">$data</span>['id'], <span style="color: #800080;">$data</span><span style="color: #000000;">);
 
        </span><span style="color: #800080;">$this</span>->assign('title', '修改成功'<span style="color: #000000;">);
        </span><span style="color: #800080;">$this</span>->assign('count', <span style="color: #800080;">$count</span><span style="color: #000000;">);
    }
    
    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 删除记录,测试框架DB记录删除(Delete)</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> delete(<span style="color: #800080;">$id</span> = <span style="color: #0000ff;">null</span><span style="color: #000000;">)
    {
        </span><span style="color: #800080;">$count</span> = (<span style="color: #0000ff;">new</span> ItemModel)->delete(<span style="color: #800080;">$id</span><span style="color: #000000;">);
 
        </span><span style="color: #800080;">$this</span>->assign('title', '删除成功'<span style="color: #000000;">);
        </span><span style="color: #800080;">$this</span>->assign('count', <span style="color: #800080;">$count</span><span style="color: #000000;">);
    }
}</span>

4.4部署视图

在 views 目录下新建 header.php 和 footer.php 两个页头页脚模板,内容如下。

header.php,内容:


    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>
<?php <span style="color: #0000ff;">echo <span style="color: #800080;">$title</span> ?></title>
    <style>
        .<span style="color: #000000;">item {
            width:<span style="color: #000000;">400px;
        }
 
        input {
            color:<span style="color: #008000;">#<span style="color: #008000;">222222;
            font-family:georgia,<span style="color: #000000;">times;
            font-size:<span style="color: #000000;">24px;
            font-weight:<span style="color: #000000;">normal;
            line-height:1.<span style="color: #000000;">2em;
            color:<span style="color: #000000;">black;
        }
 
        a {
            color:<span style="color: #000000;">blue;
            font-family:georgia,<span style="color: #000000;">times;
            font-size:<span style="color: #000000;">20px;
            font-weight:<span style="color: #000000;">normal;
            line-height:1.<span style="color: #000000;">2em;
            text-decoration:<span style="color: #000000;">none;
         }
 
        a:<span style="color: #000000;">hover {
            text-decoration:<span style="color: #000000;">underline;
        }
 
        h1 {
            color:<span style="color: #008000;">#<span style="color: #008000;">000000;
            font-size:<span style="color: #000000;">41px;
            letter-spacing:-<span style="color: #000000;">2px;
            line-height:<span style="color: #000000;">1em;
            font-family:helvetica,arial,sans-<span style="color: #000000;">serif;
            border-bottom:1px dotted <span style="color: #008000;">#<span style="color: #008000;">cccccc;
<span style="color: #000000;">        }
 
        h2 {
            color:<span style="color: #008000;">#<span style="color: #008000;">000000;
            font-size:<span style="color: #000000;">34px;
            letter-spacing:-<span style="color: #000000;">2px;
            line-height:<span style="color: #000000;">1em;
            font-family:helvetica,arial,sans-<span style="color: #000000;">serif;
        }
    </style>


    <h1>
<?php <span style="color: #0000ff;">echo <span style="color: #800080;">$title</span> ?></h1><span style="color: #000000;">
footer</span>.<span style="color: #000000;">php,内容:

</span>

然后,在 views/item 创建以下几个视图文件。

index.php,浏览数据库内 item 表的所有记录,内容:

add.php,添加记录,内容:

<a style="color: #0000ff;">class="big" href="<?php echo APP_URL ?>/item/index">成功添加<?php <span style="color: #0000ff;">echo <span style="color: #800080;">$count</span> ?>条记录,点击返回</a>

view.php,查看单条记录,内容:


$item['item_name'] ?>"> $item['id'] ?>">
class="big" href="/item/index">返回

update.php,更改记录,内容:

<a style="color: #0000ff;">class="big" href="<?php echo APP_URL ?>/item/index">成功修改<?php <span style="color: #0000ff;">echo <span style="color: #800080;">$count</span> ?>项,点击返回</a>

delete.php,删除记录,内容:

<a href="<?php%20echo%20APP_URL%20?>/item/index">成功删除<?php <span style="color: #0000ff;">echo <span style="color: #800080;">$count</span> ?>项,点击返回</a>

4.5应用测试

这样,在浏览器中访问 myphp程序:http://localhost/myphp/item/index/,就可以看到效果了。

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn