我的PHPFrame

来源:互联网 发布:c语言bmp转化为灰度图 编辑:程序博客网 时间:2024/06/05 20:47


我的PHPFrame

引言

至于为什么要写这样一套框架,正如我在博客中说到,2010年的4月份接到某大学研究生院的PHP项目开发任务(纯属朋友帮忙,或者叫私活),当时还在读研的女朋友(现在的老婆)作为我唯一的Partner项目经理。我们两个人拿着需求讨论了很久,反反复复地和客户沟通了很久,客户还是没有给一个明确的答复。但是活总得开干,决定来一场独角戏的敏捷开发。

在这之前做这种项目都是用大学时候写的那一套新闻发布系统,硬是把所有的业务都归结给内容发布,对于那种企业网站的小项目确实快捷,一天的美工加一天的程序。可是,这一次给我出难题了,不确定的流程和不确定的业务明细统统都是要老框架的命。刚好,那时候花了一个多月完成了公司新一代.net开发框架的升级,脑子里面还转着那些复用性的代码。

我的PHP应该不算很熟,算上大学两年的PHP开发,也就算得上一个业余选手,如果要写出像刚写好的.net框架那么庞大和复杂逻辑关系的系统,估计一时半会儿很难。朋友这个项目要求三个月之内看效果,我跟自己说,赌一把,我一个月之内拿下这一套开发框架,至少可以省去一个月重复代码开发的时间,特别是把以后需求改动给我带来的影响降低到最少。于是就有这一套我自称为PHPFrame的框架,一直都是自己在用,项目多也灵活,但是不大,所谓商用项目也就是小不点的缴费系统和报名系统之类的。由于我一直以来的工作主业都不是PHP开发,因此,我的专业水平和那些专门从事PHP的朋友不敢相提并论。这几年来,我确实太忙了,没有时间整理这一套框架,我发过给几个位友,但是后来是否用起来了也不知与否。最近有朋友说起对这个框架感兴趣,我抽空整理一下,准备把核心部分的源码和原理分享给大家。我这里是不会提供具体的应用代码,主要还是涉及到部分项目的保密问题。如果有任何疑问或者想获取源码和进行交流的可以给我发邮件youlins@qq.com。

 

总体设计

逻辑关系

先声明一下,我的PHPFrame框架不是什么业务系统,我不管你用什么权限模型,不管你用什么工作流引擎,更不管你用哪一个外部接口。我这里给大家展示的是一个开发系统,或者是一个简单的系统原型,是给我们程序员自己使用的开发模型。逻辑关系如下图所示,

 

软件层次结构

所谓框架提供的是基础平台,然后对于某一个独立业务而言能够快速开发,尽量减少重复的繁琐工作,并且让代码的风格高度一致。软件层次结构如下图所示,是不是非常简单?是的,我们就是要做得简单,越简单越好用。写PHP的人,估计很多都不会去关注面向对象那些所谓的设计模式,巴不得所有都是面向过程。我这里是基于面向对象设计的,这点应该已经很让部分程序员烦了,不急不急,我的面向对象没有那么复杂,都是很简单的,都是为了让你用得舒适。

其中表现层和业务层(业务逻辑和数据访问层)的代码这里是统一由Coder代码生成的。事件驱动层分为前端JS和后台的响应模式,至于权限过滤,根据个人需要控制,不属于核心代码,但是只是提醒大家务必在这一层做好权限过滤,否则安全则成了系统的问题。我通常用的是基于角色控制访问的权限模型。

 

 

美工

包括我的同事和我的同学,估计很多人都不知道我会美工。我大学的时候,在学生会里面就是一位专门做平面美工的部长(主要是当时他们认为美工也算是一门计算机技术,我管的是其中一个技术部门,另外一个技术部门负责人是我的同班同学Darren Fu),足足做了半年美工,不敢说水平有多高。后来带了一个徒弟出来,他接手我的工作,据说后来还当上了计算机学院的团总支书记。

因为写作能力还不错,被拉到红岩网校做网站策划,跟一班专业美工的人混在一起,再耳濡目染了一些PS技术和设计理念,当然我认为我的美工距离真正的专业水平还差得远。我是学计算机的,天然条件让我对Js,CSS和HTML还是比较了解,不懂可以问欧阳他们。网校有一个极其锻炼程序员的地方时,美工只做到平面设计,切图重构以及之后的事情都交给程序员去处理,因此,网校出来的技术人员都前端和后台通吃的。我自身就是一个大杂烩,因此,我带的干事毕业以后竟然有做测试的,有做产品的,当然还有做开发的。

不好意思,扯远了,以上的吹牛,只想告诉你们,美工是我自己设计的。我的PSD原图如下:

导航条我觉得不是很好看,后来改掉了,整体的风格和原来差不多,切图后的图片原件如下:


我也不知道为什么当时心血来潮切成了GIF格式的图片,后来一直没有时间也懒得管了。我对PHPFrame的定位很明确,主要是做运维管理的后台开发。因此,图片的加载效率这些互联网前端网站必须考虑的问题我这里可以忽略比较,也许就是因为这个原因,就没有将图片合并成PNG格式的图片了。

美工的核心的部分应该还是CSS文件,它决定了这个框架的风格,因此,对CSS的变量的定义我还是花了不少功夫的。源码如下:

文件:admin/css/frame.css

/* CSS Document */

 

body{margin:0pxauto;text-align:center;}

div,span,td{ font-size:12px;font-family:"宋体";}

a {font-weight : normal;font-style:normal;color:black;text-decoration:none;}

a:link {   color:#000000;  text-decoration:none;}

a:hover {text-decoration:none;color:#FF3300;}

 

/*同步记载*/

.AsLoadding{ background:#FFFFFF;text-align:center;width:300px;font-size:12px;font-family:"宋体";font-weight:normal;line-height:30px;position:absolute;top:0px;left:0px;z-index:999;color:red;}

.AsImage{background:url(Images/websedit-ag-bar.gif) no-repeat center;width:300px;height:30px;}

 

 

/*--头部--*/

.top{ height:60px;width:100%;background:url(../images/top_bg.gif);}

.top_mainlink{ position:absolute; padding:10px;padding-right:20px;right:0px;top:0px;}

.top_mainlink a{ color:#FFFFFF; text-decoration:none;}

.top_wellcome{ position:absolute; padding:10px;left:197px;top:0px;color:#FFFFFF;direction:ltr;line-height:24px;}

 

/*----左边菜单----*/

.frame_menu{ background:url(../images/left_bg.gif);vertical-align:top;overflow:hidden;}

.module{background:url(../images/left_model_bg.gif);width:165px!important;width:200px;height:34px;line-height:34px;overflow:hidden;padding-left:35px;color:#FFFFFF;cursor:pointer;}

.module a{ color:#FFFFFF;}

.module a:hover{ font-weight:bold; color:#FFFFFF;}

 

.menu{background:url(../images/left_menu_bg.gif);width:165px!important;width:200px;height:30px;line-height:30px;overflow:hidden;padding-left:35px;color:#000000;}

.menu a:hover{ font-weight:bold; color:#333333;}

 

 

.main_content{}

 

/*---标题----*/

.frame_title{ height:30px;text-align:left;line-height:30px;padding-left:20px;background:#E4F0FA;border-top:1px #FFFFFF solid;color:#6C6C6C;}

.title_menu{position:absolute;padding-right:20px;right:0px;top:0px;}

.c_menu{ margin-left:10px;background:url(../images/title_menu_close.gif) no-repeat;padding-left:20px;cursor:pointer}

.c_help{margin-left:10px;background:url(../images/title_help_close.gif) no-repeat;padding-left:20px;cursor:pointer}

.o_menu{margin-left:10px;background:url(../images/title_help_close.gif) no-repeat;padding-left:20px;cursor:pointer}

.o_help{margin-left:10px;background:url(../images/title_help_close.gif) no-repeat;padding-left:20px;cursor:pointer}

.title_menu a{ color:#6C6C6C;line-height:30px;}

 

/*----------帮助说明------*/

.frame_explain{height:60px;background:url(../images/explain_bg.gif);text-align:left;overflow:hidden;vertical-align:middle;overflow:hidden;}

.frame_explain *{ vertical-align:middle}

.explain_logo{ text-align:center; vertical-align:middle; height:60px;width:80px;float:left;background:url(../images/explain_logo.gif) center no-repeat;}

.explain_inner{

    position:absolute;

    top:35px;

    text-align:left;

    vertical-align:middle;

    padding:5px;

    line-height:24px;

    color:#6C6C6C;

    left:79px;

}

 

 

/*---------操作菜单-----------*/

.frame_toolbar{background:#FFFFFF;vertical-align:middle;margin:0px auto;min-height:40px;}

.toolbar_menu{ float:left;padding:10px;}

.toolbar_menu A,.toolbar_query A{BORDER-RIGHT:#ff96001px solid;PADDING-RIGHT: 7px;BACKGROUND-POSITION:50% bottom;BORDER-TOP:#ff9600 1px solid;PADDING-LEFT: 7px;PADDING-BOTTOM:5px;BORDER-LEFT:#ff9600 1px solid;COLOR:#ff6500;MARGIN-RIGHT:3px;PADDING-TOP:5px;BORDER-BOTTOM:#ff9600 1px solid;TEXT-DECORATION: none

}

.toolbar_menu A:hover,.toolbar_query A:hover {   BORDER-RIGHT:#ff9600 1px solid;BORDER-TOP:#ff96001px solid;BACKGROUND-IMAGE:none; BORDER-LEFT:#ff9600 1px solid; COLOR:#ff6500;BORDER-BOTTOM:#ff9600 1px solid;BACKGROUND-COLOR:#ffc794}

.toolbar_menu A:active,.toolbar_query A:active {BORDER-RIGHT: #ff96001px solid;BORDER-TOP:#ff9600 1px solid;BACKGROUND-IMAGE:none;BORDER-LEFT:#ff9600 1px solid;COLOR:#ff6500;BORDER-BOTTOM:#ff96001px solid;BACKGROUND-COLOR: #ffc794}

 

.toolbar_query{float:right;padding:10px;}

 

 

/*----表格----*/

.frame_content{text-align:center;padding:10px;margin:0px auto;}

.frame_table{ background:#BAC2C8;border:0px;}

.frame_table thead td{ background:url(../images/table_head_bg.gif);height:24px;color:#2871AF;text-align:center;vertical-align:middle;}

.frame_table tbody td{background:#CBFFC9;line-height:24px;vertical-align:middle;}

.frame_table tbody tr:hover{background-color:#FFFFFF;cursor:default}

.table_mouse_over{ background:#CBFFC9}

 

 

/*---编辑表格---*/

.table_edit{ background:#BAC2C8;width:98%;border:0px;}

.edit_text{text-align:right;background:#8EBCEA;height:30px;padding-right:5px;vertical-align:middle;}

.edit_input{text-align:left;background:#FFFFFF;height:30px;padding-left:5px;vertical-align:middle;}

 

.frame_query_table{ background:#BAC2C8; width:98%;border:0px;}

 

 

/*---详细信息---*/

.table_view{ background:#BAC2C8;width:98%;border:0px;}

.view_text{text-align:right;background:#8EBCEA;height:30px;padding-right:5px;vertical-align:middle;}

.view_value{text-align:left;background:#FFFFFF;height:30px;padding-left:5px;vertical-align:middle;}

 

.frame_list{}

.frame_edit{ position:absolute;border:#999999 1px solid;top:100;left:250; width:600px;height:500px;display:none}

 

 

/*翻页功能*/

/*CSS manu style pagination,下面这部分样式是在别人的基础上改动的,忘了出自哪里的*/

 

.manu {    PADDING-RIGHT:3px;PADDING-LEFT:3px;PADDING-BOTTOM:3px;MARGIN:3px;PADDING-TOP:3px;TEXT-ALIGN:center;color:#036cb4}

.manu A BORDER-RIGHT:#eee1px solid;PADDING-RIGHT:5px;BORDER-TOP:#eee 1px solid;PADDING-LEFT:5px;PADDING-BOTTOM:2px;MARGIN:2px;BORDER-LEFT:#eee 1px solid;COLOR: #036cb4;PADDING-TOP:2px;BORDER-BOTTOM:#eee1px solid;TEXT-DECORATION:none}

.manu A:hover {   BORDER-RIGHT: #9991px solid; BORDER-TOP: #999 1px solid; BORDER-LEFT:#999 1px solid; COLOR: #666;BORDER-BOTTOM:#9991px solid}

.manu A:active BORDER-RIGHT: #9991px solid; BORDER-TOP: #999 1px solid; BORDER-LEFT:#999 1px solid; COLOR: #666;BORDER-BOTTOM:#9991px solid}

.manu .current BORDER-RIGHT: #036cb41px solid; PADDING-RIGHT: 5px; BORDER-TOP: #036cb4 1px solid; PADDING-LEFT:5px; FONT-WEIGHT:bold; PADDING-BOTTOM:2px; MARGIN:2px; BORDER-LEFT:#036cb4 1px solid; COLOR: #fff;PADDING-TOP:2px;BORDER-BOTTOM:#036cb41px solid;BACKGROUND-COLOR: #036cb4}

.manu .disabled { BORDER-RIGHT: #eee1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #eee 1px solid; PADDING-LEFT:5px; PADDING-BOTTOM:2px; MARGIN:2px; BORDER-LEFT:#eee 1px solid; COLOR: #ddd;PADDING-TOP:2px;BORDER-BOTTOM:#eee1px solid}

 

说实话,美工部分这两年来基本上没有太多改动,因为大部分用户都比较喜欢这个风格,当然不算很好,够用就好了。

 

用程序来写程序

PHPFrame的最最核心的任务就是帮我写程序。所以,我来演示一下PHPFrame是如何帮我写程序的。

第一步设计表

E_R图的绘制我相信做数据库设计的人都会了,我这里不多说。我们要实现对分组信息的管理,表结果如图所示:

基于对表的维护和后面的Coder程序的需要,请对每一列进行注视,如下所示:

 

第二步如何使用Coder

将Coder程序部署到PHP环境下面,这个我也不罗嗦,在浏览器里面打开install.php,提示如下:

一般开发环境的数据库的端口我默认是3306。点击“提交”,在点击“确定,进入下一步”,数据库的所有信息表都显示出来了,如下所示:

将我们需要生成业务逻辑对应的数据库表勾上,如下所示:

点击“确定”,显示如下所示:

 

第三步生成的代码用于程序之中

将wrms_group.php拷贝到网站目录com/wrms/下面,wrms_group_edit.php,wrms_group_list.php和wrms_group_view.php拷贝到admin/wrms下面,并且这三个文件中将

include_once'../../com/framework/wrms_group.php';

改为

include_once'../../com/wrms/wrms_group.php';

打开admin/wrms/wrms_group_list.php的效果如下:

点击“新增”,如下所示


回到列表页面,点击“查看”,如下所示


就这样一个基本对分组管理的基本页面生成了。

第四步程序微调

Coder不是万能,它只是为你的提供了基础框架的方便。从页面上看,你需要去掉一些你不想显示和加上一些你想显示的。

我的调整的效果如下:

 

 

是不是很简单,到时候我会提供一个完整的Demo程序的,不用担心,至于怎么演化过来,你自己消化一下。

 

原理

风格

所有的页面都是同一风格,就通过CSS同一定义的而已。仔细分析一下,其实我们无非用到几种控件:表格、按钮、输入框、分页控件以及查询控件等等。我无非就是针对这些常用的控件做好一些预设,让开发来得更加简单一些,无需再顾虑风格。排版主用到的是DIV层,当然,你要搞清楚它怎么和CSS结合的,可以了解一下盒子模型,唯一难度的是其平台存在差异。

 

前端驱动

进入Edit页面,你会发现一下代码:

functionSave(){

    if(!checkForm ()) return;

    takeActions("save","确定保存?");

}

checkForm函数用于判断表单是否数据完整了,当然这个函数是有开发人员来决定里面的内容的,不是我们核心要讨论的。takeActions("save","确定保存?");这个函数可以说是整个交互框架的核心,从字面上可以理解,save就是需要执行的操作,“确定保持?”是对我们执行操作前的一种提示。仔细的读者,可能已经发现每一个页面中都有一个<input type="hidden"value="" name="page_action" id="page_action"/>的表单,这个.net的很像吧?.net也有用于记录当前状态的隐藏表单,只有表单中的数据用户无需关心。这个表单当然目前只用用于保存我们的行为,并没有保存行为的数据。

我们看一下takeActions的实现?

functiontakeActions(page_action,word,page_temp,_target,_action)

    {  

        var flag=true;

        if(word!=null)

        {

           flag=window.confirm(word);

        

        if(flag) 

        

           if(page_temp!=null)

           {

              getObj("page_temp").value=page_temp;

           }

           getObj("page_action").value=page_action;

           action_temp=getObj("mainform").action;

           target_temp = getObj("mainform").target;

           if(_action!=null)

           {

               getObj("mainform").action=_action;

              

           }

          

           if(_target!=null)

           {  

               getObj("mainform").target=_target;

           }

              getObj("mainform").submit();

             

           getObj("mainform").action=action_temp;

           getObj("mainform").target=target_temp;

        }

    }

是不是很想说,哇塞,这么简单,太没技术含量了。哈哈,其实所谓的框架并不是要求你用什么多先进的技术,而是让你用得舒适,让自己的开发更加得心应手。其实所谓大部分操作用到的脚本我都打包到js/frame.js里面了,我们看看list页面,里面的用到这些函数的代码:

functionAdd(){   pageTo("wrms_group_edit.php");}

functionDelete(){takeActions("delete","确定要删除?");}

functionSearch(){takeActions("query");}

functionDeleteByID(_id){takeActions("deletebyid","确定要删除?",_id);}

functioninitPage(){getObj("keytype").value="<?phpecho$table_obj->keytype==""?"all":$table_obj->keytype;?>";getObj("keyword").value="<?phpecho$table_obj->keyword;?>";}

当然,还有其他的相关的,比如表格排序的,还会用到另外的程序tableSorter.js

vart=null;

AddEventHandler(this,"load",tablesort=function(){initPage();t=newTableSorter("frame_table");t.initA("t");});

是不是大失所望?我所谓的前端驱动就是这样的?是不是同时有疑问,这些封装不过是把表单的操作封装了一下,有啥特别的?好,我们继续来关注下面的,看看前端后端是怎么配合工作的?

 

后端驱动

我们先看一下list页面的吧,它用到的PHP的代码(注意我这里不是MVC框架,做后台嘛,没有必要搞那么复杂)如下:

<?php

    /*这里加入你的权限控制*/

    include_once '../../com/wrms/wrms_group.php';

    $table_obj=new wrms_group();

    $table_obj->Page_Load();

    $list_query=$table_obj->getList();

?>

<?phpecho Pager($table_obj->PageSize,$table_obj->Total,$table_obj->Page);?>

“../../com/wrms/wrms_group.php”这个文件是Coder帮你生成的,里面主要存放了group模块的相关业务操作和数据库操作。里面的东西跟JavaBean的风格很像吧。

Page_Load函数是这个后台引擎的核心部分,是每一个业务逻辑类都必须实现的函数,他的代码如下所示:

public function Page_Load()

       {

           $this->PageAction=$this->getStrValue("page_action");//获取行为名称

           $this->PageAction_Load();//加载行为方法和执行

       }

//获取页面请求并执行

       public function PageAction_Load()

       {

          

           $function_name=$this->PageAction;

           if($this->PageAction!="")

           {

              if(method_exists($this,$function_name))

              {

                  echo$args=null;

                   returncall_user_func_array(array(&$this,$function_name),$args); 

              }

              else

              {

                  echo"调用的方法:".$function_name."不存在!";

              }

           }

           returnnull;

       }

PageAction_Load是BaseControl的方法,而BaseControl是所有业务逻辑类的基类,这里用的是call_user_func_array的原来直接调用业务逻辑类中同名的函数。比如save,

publicfunction save(){}

前端的调用只需要执行takeActions("save","确定保存?");

是不是在想,这个函数是有风险或者是有效率问题,很肯定地告诉你,不会,这个函数的效率不是你考虑的,要清楚我们的定位是业务管理,而不是门户网站。如果是门户网站,一大把MVC的框架,我这里无意和他们的对着干,特别是Smarty这种做得非常优秀的框架。

Pager是我封装的工具之一,为你的分页提供最简单快捷的支持。详细的实现参考tools.php文件中,这个文件里面封装了我认为用得比较,而且比较好用的Function,试一下,没有错的。

 

 

 

总结

我这里提供的是框架最原始的代码,因为应用代码都是商用了的,所以这里不方便开源,唯有这部分框架代码和整个框架原理是可以开源和开放,惊醒一下使用本框架的用户,务必做好权限控制,我这里只是确保你快速开放,不确保你的系统是安全。

另外,我的PHPFrame的实现是非常简单的,其实也是为PHP初学者提供一个快速入门的参考代码,用兴趣的朋友可以完全改掉我的框架,毕竟这只是一个原型,只是现实中的需求的千变万化的。我遇到的需求目前都能有框架支持着,暂时没有遇到什么瓶颈的功能。

这两年的改动并没有加入到这个部分代码中,稍后会有专门的文章说明改动的部分原理和如何加入到原来的框架中,还针对框架加入了对PHP5.3.0以及以前版本的支持,加入对Oracle的支持,还有一些导入导出控件,也改了一些开源的代码加入其中,用得还不错。也敬请关注。

由于个人能力有限,难免有错漏,欢迎指出!如果有任何疑问或者想获取源码和进行交流的可以给我发邮件youlins@qq.com。

原创粉丝点击