新闻资讯  快讯  焦点  财经  政策  社会
互 联 网   电商  金融  数据  计算  技巧
生活百科  科技  职场  健康  法律  汽车
手机百科  知识  软件  修理  测评  微信
软件技术  应用  系统  图像  视频  经验
硬件技术  知识  技术  测评  选购  维修
网络技术  硬件  软件  设置  安全  技术
程序开发  语言  移动  数据  开源  百科
安全防护  资讯  黑客  木马  病毒  移动
站长技术  搜索  SEO  推广  媒体  移动
财经百科  股票  知识  理财  财务  金融
教育考试  育儿  小学  高考  考研  留学
您当前的位置:首页 > IT百科 > 程序开发 > 语言 > php

浅解用PHP实现MVC

时间:2019-12-16 14:13:42  来源:  作者:

MVC是一个老生常谈的问题,是为了解决一类共同问题总结出来的一套可复用的解决方案,这是软件设计模式产生的初衷。不管是客户端还是移动端,MVC的分层设计模式解决了软件开发中的可复用、单一职责、解耦的问题,php语言中的MVC设置模式也是如此。下面通过PHP语言细说MVC模式如何在PHP中应用,本文主要从如下几方面介绍:

Ø MVC的工作原理

Ø PHP开发框架

a) 开发框架的优势

b) 使用框架进行模块划分

Ø 一个简单MVC框架总体架构分析

a) URL访问方式【URL Parser】

b) 控制器【Controller】

c) 视图【View】

d) 运行时【Runtime】

1、MVC 的工作原理

MVC框架图:

视图View

代表用户交互的页面、可以包含HTML界面、Smarty模板等和界面相关的元素。MVC设计模式对于视图的处理仅限于视图上数据的采集和处理,以及用户的点击、拖动等事件的处理,而不包括在视图上的业务流程处理。业务流程会交给模型层(Model)处理。

模型Model

模型层是对业务流程、状态的处理以及业务规则的指定。业务流程的处理过程对其他层来说是黑箱操作,模型接受视图的请求处理数据,返回最终的处理结果。业务模型还有一个很重要的模型--数据模型,数据模型主要指实体对象的数据保存(持久化)。比如将一张订单保存到数据库,从数据库获取订单,所有和数据库相关的操作限定在该模型中。

控制器Controller

控制层是View层和Model层之间的一个桥梁,接收到用户的请求,将模型和视图匹配在一起,共同完成用户的请求。比如,用户点击一个链接,控制层接收到请求后,把信息传递给模型层,模型层处理完成之后返回视图给用户。

2、PHP开发框架

2.1、开发框架的优势

  • 框架提高开发效率和质量
  • 框架处理了许多基础性工作
  • 框架处理细节工作(事务处理、安全、数据流控制)
  • 框架结构性好、扩张性好
  • 框架划分子问题,易于控制、易于延展、易于分配资源

2.2、使用框架进行模块划分

一个典型的后台应用模块的划分

  • 平台操作管理
    • 登录管理
    • 操作界面管理
  • 系统管理频道
    • 常规管理
    • 公告管理
    • 友情链接挂你
  • 内容管理频道
    • 图片管理
    • 栏目管理
    • 文章管理
    • 幻灯片管理
  • 用户管理频道
    • 用户组管理
    • 用户管理

模块设置操作

  • 每个模块可以设置查看、添加、修改、删除、搜索等操作
  • 模块太大应该划分子模块,适合的模块数量为8~12个


3、一个简单MVC框架的分析

从以下五个方面来分析

  1. URL访问方式(URL Parser)
  2. 控制器(Controller)
  3. 视图(View)
  4. 模型(Model)
  5. 运行时(Runtime)

3.1、URL访问方式(URL Parser)

URL使用PATHINFO模式(index.php/index/index/),应用的访问方式都是采用单一入口的访问方式,所有访问一个应用中的具体模块及模块中的某个操作,都需要在URL中通过入口文件后的参数来访问和执行,所有访问都会变成由URL的参数来统一解析和调度,格式如下:

不带参数的URL
http://example.com/index.php/user/add
带有参数的URL
http://example.com/index.php/user/add/cid/5
http://example.com/index.php/user/add/cid/5/page/6

这种采用单一入口和PATHINFO模式的URL访问是MVC实现的基础,作为单一入口的框架的入口brophp.php文件则负责处理基本的信息,包括了

  • 路径信息:BroPHP框架的路径,用户项目的应用路径,项目的根路径等
  • 包含框架中的函数库文件
  • 包含全局的函数库文件,用户可以自己定义函数在这个文件中
  • __autoload()自动加载类
  • 页面缓存配置
  • 初使化时,创建项目的目录结构
  • 解析处理URL

1)路径信息处理

路径信息会保存在$GLOBALS全局数组中,后面的页面需要使用到直接从$GLOBALS中获取即可

//模板文件中所有要的路径,html\css\JAVAscript\image\link等中用到的路径,从WEB服务器的文档根开始
$spath = dirname($_SERVER["SCRIPT_NAME"]);
if ($spath == "/" || $spath == "\\")
$spath = "";
$GLOBALS["root"] = $spath . '/'; //Web服务器根到项目的根
$GLOBALS["App"] = $_SERVER["SCRIPT_NAME"] . '/'; //当前应用脚本文件
$GLOBALS["url"] = $GLOBALS["app"] . $_GET["m"] . '/'; //访问到当前模块
$GLOBALS["public"] = $GLOBALS["root"] . 'public/'; //项目的全局资源目录
$GLOBALS["res"] = $GLOBALS["root"] . ltrim(APP_PATH, './') . "views/" . TPLSTYLE . "/resource/"; //当前应用模板的资源

2)包含框架中的函数库文件

函数库文件主要是一些常用的工具方法的集合,框架自带的functions.inc.php方法库包含了数据模型创建操作的一些列工具方法,可以开箱即用。此外用户也可以自定义函数库文件保存在对应模块目录下的commons/functions.inc.php位置,框架会自动引入。

 //包含框架中的函数库文件

 include BROPHP_PATH . 'commons/functions.inc.php';

 ​

 // 包含全局的函数库文件,用户可以自己定义函数在这个文件中

 $funfile = PROJECT_PATH . "commons/functions.inc.php";

 if (file_exists($funfile))

 include $funfile;

3)设置包含目录(类所在的全部目录)

这个步骤是__autoload()自动加载类的基础,__autoload()方法中include会自动从这些目录中寻找要包含的类

 //设置包含目录(类所在的全部目录), PATH_SEPARATOR 分隔符号 linux(:) windows(;)

 $include_path = get_include_path(); //原基目录

 $include_path .= PATH_SEPARATOR . BROPHP_PATH . "bases/"; //框架中基类所在的目录

 $include_path .= PATH_SEPARATOR . BROPHP_PATH . "classes/"; //框架中扩展类的目录

 $include_path .= PATH_SEPARATOR . BROPHP_PATH . "libs/"; //模板Smarty所在的目录

 $include_path .= PATH_SEPARATOR . PROJECT_PATH . "classes/"; //项目中用的到的工具类

 $controlerpath = PROJECT_PATH . "runtime/controls/" . TMPPATH; //生成控制器所在的路径

 $include_path .= PATH_SEPARATOR . $controlerpath; //当前应用的控制类所在的目录


 //设置include包含文件所在的所有目录

 set_include_path($include_path);

4)__autoload()自动加载类

__autoload()魔术方法是在用户创建一个没有包含的类的对象之前会调用,所以重写这个方法,在这个方法中处理类文件的包含,省去了类文件包含的工作,当然类名需要符合一定的规则才能使用自动包含,框架定义了类名的规则为“首字母大小的类名.clsss.php”

 //自动加载类

 function __autoload($className)

 {

 if ($className == "memcache") { //如果是系统的Memcache类则不包含

 return;

 } else if ($className == "Smarty") { //如果类名是Smarty类,则直接包含

 include "Smarty.class.php";

 } else { //如果是其他类,将类名转为小写

 include strtolower($className) . ".class.php";

 }

 Debug::addmsg("<b> $className </b>类", 1); //在debug中显示自动包含的类

 }

解析处理URL

解析处理URL步骤调用的是Prourl::parseUrl();

 /**

 * URL路由,转为PATHINFO的格式

 */

 static function parseUrl()

 {

 if (isset($_SERVER['PATH_INFO'])) {

 //获取 pathinfo

 $pathinfo = explode('/', trim($_SERVER['PATH_INFO'], "/"));

 ​

 // 获取 control

 $_GET['m'] = (!empty($pathinfo[0]) ? $pathinfo[0] : 'index');

 ​

 array_shift($pathinfo); //将数组开头的单元移出数组 

 ​

 // 获取 action

 $_GET['a'] = (!empty($pathinfo[0]) ? $pathinfo[0] : 'index');

 array_shift($pathinfo); //再将将数组开头的单元移出数组 

 ​

 for ($i = 0; $i < count($pathinfo); $i += 2) {

 $_GET[$pathinfo[$i]] = $pathinfo[$i + 1];

 }

 ​

 } else {

 $_GET["m"] = (!empty($_GET['m']) ? $_GET['m'] : 'index'); //默认是index模块

 $_GET["a"] = (!empty($_GET['a']) ? $_GET['a'] : 'index'); //默认是index动作

 ​

 if ($_SERVER["QUERY_STRING"]) {

 $m = $_GET["m"];

 unset($_GET["m"]); //去除数组中的m

 $a = $_GET["a"];

 unset($_GET["a"]); //去除数组中的a

 $query = http_build_query($_GET); //形成0=foo&1=bar&2=baz&3=boom&cow=milk格式

 //组成新的URL

 $url = $_SERVER["SCRIPT_NAME"] . "/{$m}/{$a}/" . str_replace(array("&", "="), "/", $query);

 header("Location:" . $url);

 }

 }

 }

访问login/index,解析保存在全局的GET数组中的信息如下:

m -> control 表示控制器
a -> action 表示操作

有了这些信息,动态创建控制器,发起对应的流程

 $className = ucfirst($_GET["m"]) . "Action";

 $controler = new $className();

 $controler->run();

3.2、控制器(Controller)

1)控制器的声明

功能模块的控制器类保存在controls目录中,类名和模块名相同,下面是登录模块,定义一个Login类(类的首字母需要大写)保存的文件名为login.class.php

 class Login extends Action

 {

 function __construct()

 {

 parent::__construct();

 }

 ​

 function index()

 {//登陆页面

 $GLOBALS['debug'] = 0;

 $this->display();

 }

 function islogin()

 {

 if ($_POST['user_username'] == null && $_POST['user_password'] == null) {//如果用户名为空

 $this->error('用户名和密码不能为空', 1, '');

 }

 $_POST['user_password'] = md5($_POST['user_password']);

 $_POST['user_repassword'] = md5($_POST['user_repassword']);

 if ($_POST['user_repassword'] != $_POST['user_password']) {//如果用户输入的两次密码不一致

 $this->error('两次密码不一致', 1, '');

 }

 $user = D('user');

 $date = $user->field('uid,user_password')->where(array('user_username' => $_POST['user_username']))->find();

 $_POST['uid'] = $date['uid'];

 if ($_POST['user_password'] != $date['user_password']) {//如果输入的密码与数据库密码不匹配

 $this->error('密码不正确', 1, '');

 }

 if (strtoupper($_POST['code']) != $_SESSION['code']) {//如果输入的验证码不正确

 $this->error('验证码输入不正确', 1, '');

 }

 $_SESSION = $_POST;//把posts所有的数据压入session

 $date = $user->query('SELECT free_user_group.group_muser,free_user_group.group_mweb,free_user_group.group_marticle,free_user_group.group_sendarticle,free_user_group.group_mimage,free_user_group.group_sendcomment,free_user_group.group_sendmessage,free_user.user_lock FROM free_user,free_user_group WHERE free_user.uid=' . $_SESSION['uid'] . ' AND free_user.gid=free_user_group.gid', 'select');

 if ($date[0]['user_lock']) {

 $this->error('您的帐号已被锁定,请与管理员联系后再登录', 3, 'index/index');

 } else {

 if ($date[0]['group_muser'] || $date[0]['group_marticle'] || $date[0]['group_mweb'] || $date[0]['group_mimage']) {

 //查询数据库中是否开启自动记录操作

 $opernote = D('foreground');

 //$_SESSION['oper']=D('OperDate');

 $isOpenNote = $opernote->where(array('fid' => '1'))->field('operateNotes')->find();

 //$_SESSION['operAuthor']=$operAuthior->where(array('id'=>'1'))->find();

 $_SESSION['isOpenNotes'] = $isOpenNote['operateNotes'];

 $_SESSION['islogin'] = true;

 $_SESSION = array_merge($date[0], $_SESSION);

 $user->where($_SESSION['uid'])->update('user_onlinestatus=user_onlinestatus+1');

 $this->success('登陆成功', 1, 'index/index');

 } else {

 $this->error('您的权限不够无法进入后台', 1, '');

 }

 }

 }

 function logout()

 {//退出时销毁session

 $user = D('user');

 $_SESSION['islogin'] = false;

 $_SESSION = array();

 if (isset($_COOKIE[session_name()])) {

 setCookie(session_name(), '', time() - 3600, '/');

 }

 session_destroy();

 $this->redirect('index');

 }

 ​

 function code()

 {//显示验证码

 echo new Vcode();

 }

 }

common.class.php 类

 class Common extends Action

 {

 function init()

 {

 if (!(isset($_SESSION['islogin']) && $_SESSION['islogin'] == true)) {

 $this->redirect("login/index");

 }

 $this->assign('session', $_SESSION);

 }

 }

2) 操作的声明

每个操作对应的是控制器中的一个方法,比如在上面的Login控制器中

  • code()是一个获取验证码的操作,可以通过 yourhost:port/.../Login/code 这种方式访问该操作
  • logout()是一个退出登录的操作,可以通过yourhost:port/.../Login/logout这种方式访问该操作

3.3、视图(View)

视图的显示是基于Smarty模板引擎的,继承了Smarty类,并且重写了__construct,display,is_cached,clear_cache 方法。

<?php
class Mytpl extends Smarty
{
/**
* 构造方法,用于初使化Smarty对象中的成员属性
*
*/
function __construct()
{
$this->template_dir = APP_PATH . "views/" . TPLSTYLE; //模板目录
$this->compile_dir = PROJECT_PATH . "runtime/comps/" . TPLSTYLE . "/" . TMPPATH; //里的文件是自动生成的,合成的文件
$this->caching = CSTART; //设置缓存开启
$this->cache_dir = PROJECT_PATH . "runtime/cache/" . TPLSTYLE; //设置缓存的目录
$this->cache_lifetime = CTIME; //设置缓存的时间
$this->left_delimiter = "<{"; //模板文件中使用的“左”分隔符号
$this->right_delimiter = "}>"; //模板文件中使用的“右”分隔符号
parent::__construct(); //调用父类被覆盖的构造方法
}

/*
* 重载父类Smarty类中的方法
* @param string $resource_name 模板的位置
* @param mixed $cache_id 缓存的ID
*/
function display($resource_name = null, $cache_id = null, $compile_id = null)
{

//将部分全局变量直接分配到模板中使用
$this->assign("root", rtrim($GLOBALS["root"], "/"));
$this->assign("app", rtrim($GLOBALS["app"], "/"));
$this->assign("url", rtrim($GLOBALS["url"], "/"));
$this->assign("public", rtrim($GLOBALS["public"], "/"));
$this->assign("res", rtrim($GLOBALS["res"], "/"));

if (is_null($resource_name)) {
$resource_name = "{$_GET["m"]}/{$_GET["a"]}." . TPLPREFIX;
} else if (strstr($resource_name, "/")) {
$resource_name = $resource_name . "." . TPLPREFIX;
} else {
$resource_name = $_GET["m"] . "/" . $resource_name . "." . TPLPREFIX;
}
Debug::addmsg("使用模板 <b> $resource_name </b>");
parent::display($resource_name, $cache_id, $compile_id);
}

/*
* 重载父类的Smarty类中的方法
* @param string $tpl_file 模板文件
* @param mixed $cache_id 缓存的ID
*/
function is_cached($tpl_file = null, $cache_id = null, $compile_id = null)
{
if (is_null($tpl_file)) {
$tpl_file = "{$_GET["m"]}/{$_GET["a"]}." . TPLPREFIX;
} else if (strstr($tpl_file, "/")) {
$tpl_file = $tpl_file . "." . TPLPREFIX;
} else {
$tpl_file = $_GET["m"] . "/" . $tpl_file . "." . TPLPREFIX;
}
return parent::is_cached($tpl_file, $cache_id, $compile_id);
}

/*
* 重载父类的Smarty类中的方法
* @param string $tpl_file 模板文件
* @param mixed $cache_id 缓存的ID
*/

function clear_cache($tpl_file = null, $cache_id = null, $compile_id = null, $exp_time = null)
{
if (is_null($tpl_file)) {
$tpl_file = "{$_GET["m"]}/{$_GET["a"]}." . TPLPREFIX;
} else if (strstr($tpl_file, "/")) {
$tpl_file = $tpl_file . "." . TPLPREFIX;
} else {
$tpl_file = $_GET["m"] . "/" . $tpl_file . "." . TPLPREFIX;
}
return parent::clear_cache($tpl_file = null, $cache_id = null, $compile_id = null, $exp_time = null);
}
}

比如访问登录页面

 function index()

 {//登陆页面

 $GLOBALS['debug'] = 0;

 $this->display();

 }

类Mytpl的构造方法会自动初始化Smarty的模板目录、编译目录、缓存目录等Smarty模板引擎需要的内容

 $this->template_dir = APP_PATH . "views/" . TPLSTYLE; //模板目录

 $this->compile_dir = PROJECT_PATH . "runtime/comps/" . TPLSTYLE . "/" . TMPPATH; //里的文件是自动生成的,合成的文件

 $this->caching = CSTART; //设置缓存开启

 $this->cache_dir = PROJECT_PATH . "runtime/cache/" . TPLSTYLE; //设置缓存的目录

主要内容如下:

 template_dir = "./admin/views/default"

 compile_dir = "./runtime/comps/default/admin_php/"

 cache_dir = "./runtime/cache/default"

 cache_lifetime = 604800

在Login控制器中调用无参的$this->display();方法,会自动从$this->template_dir文件夹下面查找模板文件,模板文件的是保存在_GET["m"]子文件夹下的名称为_GET["a"]的文件,比如,Login控制器对应的index模板位于如下位置:

最后使用Smarty模板引擎完成页面内容的渲染工作,最终把编译后的模板文件保存在$this->compile_dir目录下面,如下所示:

3.4、模型(Model)

模型层分为业务模型和数据模型,业务模型用于处理业务流程中的数据验证、数据处理、结果输出等等步骤;数据模型处理数据的持久化(增删改查等操作),数据模型承担了重要的责任,所以会围绕数据模型的底层处理展开来说。

  • insert
  • delete
  • update

模型层的基类是抽象的DB类,有以下几个重要的公有属性

 protected $tabName = ""; //表名,自动获取

 protected $fieldList = array(); //表字段结构,自动获取

 protected $auto;

 //SQL的初使化

 protected $sql = array("field" => "", "where" => "", "order" => "", "limit" => "", "group" => "", "having" => "");

$sql变量保存了以下信息

  • field 表字段
  • where where字句
  • order order by 字句
  • limit limit 字句
  • group group 字句
  • having having 字句

调用field() where() order() limit() group() having()方法,会把对应的参数值保存在$sql关联数key对应的value中,这些方法在实际中不存在,而是通过重写__call方法实现了

 /**

 *连贯操作调用field() where() order() limit() group() having()方法,组合SQL语句

 */

 function __call($methodName, $args)

 {

 $methodName = strtolower($methodName);

 if (array_key_exists($methodName, $this->sql)) {

 if (empty($args[0]) || (is_string($args[0]) && trim($args[0]) === '')) {

 $this->sql[$methodName] = "";

 } else {

 $this->sql[$methodName] = $args;

 }

 ​

 if ($methodName == "limit") {

 if ($args[0] == "0")

 $this->sql[$methodName] = $args;

 }

 } else {

 Debug::addmsg("<font color='red'>调用类" . get_class($this) . "中的方法{$methodName}()不存在!</font>");

 }

 return $this;

 }

比如执行下面的代码:

 $date=$user->field('uid,user_password')->where(array('user_username'=>$_POST['user_username']))->find();

最终$sql中保存的数据如下:

 arra (

 "field" => 'uid,user_password',

 "where" => array('user_username'=>"xxxxx")

 )

表名称和表字段结构
protected $fieldList = array(); 和 $tabName,在dpdp类setTable方法中自动获取

 /**

 * 自动获取表结构

 */

 function setTable($tabName)

 {

 $cachefile = PROJECT_PATH . "runtime/data/" . $tabName . ".php";

 $this->tabName = TABPREFIX . $tabName; //加前缀的表名

 ​

 if (!file_exists($cachefile)) {

 try {

 $pdo = self::connect();

 $stmt = $pdo->prepare("desc {$this->tabName}");

 $stmt->execute();

 $auto = "yno";

 $fields = array();

 while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

 if ($row["Key"] == "PRI") {

 $fields["pri"] = strtolower($row["Field"]);

 } else {

 $fields[] = strtolower($row["Field"]);

 }

 if ($row["Extra"] == "auto_increment")

 $auto = "yes";

 }

 //如果表中没有主键,则将第一列当作主键

 if (!array_key_exists("pri", $fields)) {

 $fields["pri"] = array_shift($fields);

 }

 if (!DEBUG)

 file_put_contents($cachefile, "<?php " . json_encode($fields) . $auto);

 $this->fieldList = $fields;

 $this->auto = $auto;

 } catch (PDOException $e) {

 Debug::addmsg("<font color='red'>异常:" . $e->getMessage() . '</font>');

 }

 } else {

 $json = ltrim(file_get_contents($cachefile), "<?ph ");

 $this->auto = substr($json, -3);

 $json = substr($json, 0, -3);

 $this->fieldList = (array)json_decode($json, true);

 }

 Debug::addmsg("表<b>{$this->tabName}</b>结构:" . implode(",", $this->fieldList), 2); //debug

 }

3.5、运行时(Runtime)

· 处理模块类的隐式集成common类,处理统一的业务,比如用户验证

· 处理运行时生成对应数据库驱动的数据模型

1)运行时数据模型

以一个简单的数据库表查询作为例子,

 function test_query() {

 $article = D("article");

 $data = $article->query("SELECT * FROM article", "select");

 print_r($data);

 }

整体步骤流程:

 整体步骤流程:

 D("article") -> Structure::model($className, $app);

 -> $model->setTable($className);

 $article->query("SELECT * FROM article", "select");

1、 D("article")中D方法的职责如下:

· 运行时生成数据模型

· 获取数据模型对应的数据库表的字段以及其他表信息

 /**

 * 创建Models中的数据库操作对象

 * @param string $className 类名或表名

 * @param string $app 应用名,访问其他应用的Model

 * @return object 数据库连接对象

 */

 function D($className = null, $app = "")

 {

 $db = null;

 //如果没有传表名或类名,则直接创建DB对象,但不能对表进行操作

 if (is_null($className)) {

 $class = "D" . DRIVER;

 $db = new $class;

 } else {

 $className = strtolower($className);

 $model = Structure::model($className, $app);

 $model = new $model();

 //如果表结构不存在,则获取表结构

 $model->setTable($className);

 $db = $model;

 }

 if ($app == "")

 $db->path = APP_PATH;

 else

 $db->path = PROJECT_PATH . strtolower($app) . '/';

 return $db;

 }

1.1、 运行时数据模型
运行时生成数据模型由Structure::model这个方法处理

 static function model($className, $app)

 {

 //父类名,使用PDO链接对应的父类名为Dpdo

 $driver = "D" . DRIVER;

 $rumtimeModelPath = PROJECT_PATH . "runtime/models/" . TMPPATH;

 if ($app == "") {

 // 数据模型类源码的位置:eg ./test/models/article.class.php,用户可以自定义数据模型保存在该位置

 $src = APP_PATH . "models/" . strtolower($className) . ".class.php";

 // 数据模型父类源码的位置(___表示占位符,后面会有替换步骤) eg ./test/models/___.class.php

 $psrc = APP_PATH . "models/___.class.php";

 // 运行时数据模型类名称,规则为原始类名添加"Model"后缀

 $className = ucfirst($className) . 'Model';

 // 运行时数据模型父类名称(___表示占位符,后面会有替换步骤)

 $parentClass = '___model';

 // 运行时保存的数据模型类位置 /Users/aron/git-repo/PhpLearning/Foundation/26-brophp/runtime/models/Foundation_26-brophp_test_php/articlemodel.class.php

 $to = $rumtimeModelPath . strtolower($className) . ".class.php";

 // 运行时保存的数据模型父类位置 eg /Users/aron/git-repo/PhpLearning/Foundation/26-brophp/runtime/models/Foundation_26-brophp_test_php/___model.class.php

 $pto = $rumtimeModelPath . $parentClass . ".class.php";

 } else {

 $src = PROJECT_PATH . $app . "/models/" . strtolower($className) . ".class.php";

 $psrc = PROJECT_PATH . $app . "/models/___.class.php";

 $className = ucfirst($app) . ucfirst($className) . 'Model';

 $parentClass = ucfirst($app) . '___model';

 $to = $rumtimeModelPath . strtolower($className) . ".class.php";

 $pto = $rumtimeModelPath . $parentClass . ".class.php";

 }

 // 如果有原model存在,用户自定义了数据模型类

 if (file_exists($src)) {

 $classContent = file_get_contents($src);

 $super = '/extends\s+(.+?)\s*{/i';

 // 如果已经有父类

 if (preg_match($super, $classContent, $arr)) {

 $psrc = str_replace("___", strtolower($arr[1]), $psrc);

 $pto = str_replace("___", strtolower($arr[1]), $pto);

 if (file_exists($psrc)) {

 if (!file_exists($pto) || filemtime($psrc) > filemtime($pto)) {

 $pclassContent = file_get_contents($psrc);

 $pclassContent = preg_replace('/class\s+(.+?)\s*{/i', 'class ' . $arr[1] . 'Model extends ' . $driver . ' {', $pclassContent, 1);

 file_put_contents($pto, $pclassContent);

 }

 } else {

 Debug::addmsg("<font color='red'>文件{$psrc}不存在!</font>");

 }

 $driver = $arr[1] . "Model";

 include_once $pto;

 }

 if (!file_exists($to) || filemtime($src) > filemtime($to)) {

 $classContent = preg_replace('/class\s+(.+?)\s*{/i', 'class ' . $className . ' extends ' . $driver . ' {', $classContent, 1);

 // 生成model

 file_put_contents($to, $classContent);

 }

 } else {

 // 数据模型不存在,用户没有定义对应的数据模型,如果没有生成,则生成该数据模型

 if (!file_exists($to)) {

 // 继承Driver对应的父类,PDO的父类为Dpdo,MySQLi的父类为Dmysqli

 $classContent = "<?php\n\tclass {$className} extends {$driver}{\n\t}";

 // 运行时生成model

 file_put_contents($to, $classContent);

 }

 }


 // 包含数据模型类,返回数据模型的类名

 include_once $to;

 return $className;

 }

1.2、 获取数据库结构信息

获取数据模型对应的数据库表的字段以及其他表信息由setTable该方法处理,不同的数据驱动程序处理数据库操作的方法是不同的,所以对应的数据库驱动类需要重写该方法,下面是PDO驱动对应的setTable方法

 /**

 * 自动获取表结构

 */

 function setTable($tabName)

 {

 $cachefile = PROJECT_PATH . "runtime/data/" . $tabName . ".php";

 $this->tabName = TABPREFIX . $tabName; //加前缀的表名


 if (!file_exists($cachefile)) {

 try {

 $pdo = self::connect();

 $stmt = $pdo->prepare("desc {$this->tabName}");

 $stmt->execute();

 $auto = "yno";

 $fields = array();

 while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

 if ($row["Key"] == "PRI") {

 $fields["pri"] = strtolower($row["Field"]);

 } else {

 $fields[] = strtolower($row["Field"]);

 }

 if ($row["Extra"] == "auto_increment")

 $auto = "yes";

 }

 //如果表中没有主键,则将第一列当作主键

 if (!array_key_exists("pri", $fields)) {

 $fields["pri"] = array_shift($fields);

 }

 if (!DEBUG)

 file_put_contents($cachefile, "<?php " . json_encode($fields) . $auto);

 $this->fieldList = $fields;

 $this->auto = $auto;

 } catch (PDOException $e) {

 Debug::addmsg("<font color='red'>异常:" . $e->getMessage() . '</font>');

 }

 } else {

 $json = ltrim(file_get_contents($cachefile), "<?ph ");

 $this->auto = substr($json, -3);

 $json = substr($json, 0, -3);

 $this->fieldList = (array)json_decode($json, true);

 }

 Debug::addmsg("表<b>{$this->tabName}</b>结构:" . implode(",", $this->fieldList), 2); //debug

 }

2、 查询

$article->query("SELECT * FROM article", "select");代码执行的是查询的功能,查询方法是最基础的方法,上层的total()、select()、find()、insert()、update()、delete() 等数据库操作的方法都依赖于该方法的处理,不同的数据驱动程序处理数据库操作的方法是不同的,所以对应的数据库驱动类需要重写该方法,下面是PDO驱动对应的query方法

/**
* 执行SQL语句的方法
* @param string $sql 用户查询的SQL语句
* @param string $method SQL语句的类型(select,find,total,insert,update,other)
* @param array $data 为prepare方法中的?参数绑定值
* @return mixed 根据不同的SQL语句返回值
*/
function query($sql, $method, $data = array())
{
$startTime = microtime(true);
$this->setNull(); //初使化sql

$value = $this->escape_string_array($data);
$marr = explode("::", $method);
$method = strtolower(array_pop($marr));
if (strtolower($method) == trim("total")) {
$sql = preg_replace('/select.*?from/i', 'SELECT count(*) as count FROM', $sql);
}
$addcache = false;
$memkey = $this->sql($sql, $value);
if (defined("USEMEM")) {
global $mem;
if ($method == "select" || $method == "find" || $method == "total") {
$data = $mem->getCache($memkey);
if ($data) {
return $data; //直接从memserver中取,不再向下执行
} else {
$addcache = true;
}
}
}

try {
$return = null;
$pdo = self::connect();
$stmt = $pdo->prepare($sql); //准备好一个语句
$result = $stmt->execute($value); //执行一个准备好的语句

//如果使用mem,并且不是查找语句
if (isset($mem) && !$addcache) {
if ($stmt->rowCount() > 0) {
$mem->delCache($this->tabName); //清除缓存
Debug::addmsg("清除表<b>{$this->tabName}</b>在Memcache中所有缓存!"); //debug
}
}

switch ($method) {
case "select": //查所有满足条件的
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);

if ($addcache) {
$mem->addCache($this->tabName, $memkey, $data);
}
$return = $data;
break;
case "find": //只要一条记录的
$data = $stmt->fetch(PDO::FETCH_ASSOC);

if ($addcache) {
$mem->addCache($this->tabName, $memkey, $data);
}
$return = $data;
break;

case "total": //返回总记录数
$row = $stmt->fetch(PDO::FETCH_NUM);

if ($addcache) {
$mem->addCache($this->tabName, $memkey, $row[0]);
}

$return = $row[0];
break;
case "insert": //插入数据 返回最后插入的ID
if ($this->auto == "yes")
$return = $pdo->lastInsertId();
else
$return = $result;
break;
case "delete":
case "update": //update
$return = $stmt->rowCount();
break;
default:
$return = $result;
}
$stopTime = microtime(true);
$ys = round(($stopTime - $startTime), 4);
Debug::addmsg('[用时<font color="red">' . $ys . '</font>秒] - ' . $memkey, 2); //debug
return $return;
} catch (PDOException $e) {
Debug::addmsg("<font color='red'>SQL error: " . $e->getMessage() . '</font>');
Debug::addmsg("请查看:<font color='#005500'>" . $memkey . '</font>'); //debug
}
}



Tags:MVC   点击:()  评论:()
声明:本站部分内容来自互联网,内容观点仅代表作者本人,如有任何版权侵犯请与我们联系,我们将立即删除。
▌相关评论
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表
▌相关推荐
通常,在Spring MVC中,我们编写一个控制器类来处理来自客户端的请求。然后,控制器调用业务类来处理与业务相关的任务,然后将客户端重定向到逻辑视图名称,该名称由Spring的调度程序...【详细内容】
2020-03-11   MVC  点击:(7)  评论:(0)  加入收藏
这节介绍SpringMVC,SpringMVC是一种基于Java的实现MVC设计模式的请求驱动类型的轻量级Web框架。本章会介绍相关概念,流程,再从源码进行讲解。1. MVC&emsp;MVC(Model View Contr...【详细内容】
2020-01-07   MVC  点击:(3)  评论:(0)  加入收藏
MVC是一个老生常谈的问题,是为了解决一类共同问题总结出来的一套可复用的解决方案,这是软件设计模式产生的初衷。不管是客户端还是移动端,MVC的分层设计模式解决了软件开发中的...【详细内容】
2019-12-16   MVC  点击:(23)  评论:(0)  加入收藏
在使用SSM整合的时候,spring mvc 添加@ResponseBody的时候,正常情况下都会返回json的。但是又的时候如果没有配置好的话,如果想要返回Map的json对象会报:No converter found for...【详细内容】
2019-12-13   MVC  点击:(38)  评论:(0)  加入收藏
一。DAO类:一个DAO类封装的是针对某一个表的增删改查实现细节二。Service类:业务处理类/服务类一个Service类封装的一组关联的业务实现的细节三。业务:1.【定义】: 一个业务...【详细内容】
2019-12-04   MVC  点击:(34)  评论:(0)  加入收藏
MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。...【详细内容】
2019-11-22   MVC  点击:(41)  评论:(0)  加入收藏
SQL注入简介通俗的讲,SQL注入就是恶意黑客或者竞争对手利用现有的B/S或者C/S架构的系统,将恶意的SQL语句通过表单等传递给后台SQL数据库引擎执行。比如,一个黑客可以利用网站的...【详细内容】
2019-10-18   MVC  点击:(72)  评论:(0)  加入收藏
Jean-Jacques Dubray是一名资深工程师,他最近引入了一个新的模式:状态-行为-模(State-Action-Model,SAM)。SAM是一个函数式反应型的编程模式,它致力于简化数据Model和View之间的交互。它究竟有何优点值得作者弃用MVC呢?...【详细内容】
2019-10-08   MVC  点击:(15)  评论:(0)  加入收藏
什么是MVCCMulti-Version Concurrency Control 多版本并发控制,MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问;在编程语言中实现事务内存。MVCC有...【详细内容】
2019-09-25   MVC  点击:(32)  评论:(0)  加入收藏
我们今天来介绍一个SpringMVC的基本配置,灵活的使用这些配置,可以让我们在开发中更加灵活的处理业务逻辑。OK,废话不多说,那就开始吧。SpringMVC基础配置(通过注解配置,非xml配...【详细内容】
2019-08-29   MVC  点击:(50)  评论:(0)  加入收藏
一个人善于使用模式,相当于把一些特定问题进行了抽象概括,大脑其实可以腾出更大的空间处理别的事情(具体的业务等)。所以,这一两年我也比较喜欢尝试使用一些流行的模式或者开源框...【详细内容】
2019-08-15   MVC  点击:(32)  评论:(0)  加入收藏
在本例中,我们将使用Spring MVC框架构建一个入门级web应用程序。Spring MVC 是Spring框架最重要的的模块之一。它以强大的Spring IoC容器为基础,并充分利用容器的特性来简化...【详细内容】
2019-08-07   MVC  点击:(41)  评论:(0)  加入收藏
SpringMVC架构实现原理一、SpringMVC介绍Spring mvc是一个基于mvc的web框架。其中核心类是DispatcherServlet,它是一个Servlet,顶层是实现的Servlet接口。Spring mvc是spring...【详细内容】
2019-08-01   MVC  点击:(56)  评论:(0)  加入收藏
一、MVC设计模式1.MVC的概念首先我们需要知道MVC模式并不是javaweb项目中独有的,MVC是一种软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控...【详细内容】
2019-07-25   MVC  点击:(212)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条