工大后院

 找回密码
 加入后院

扫一扫,访问微社区

QQ登录

只需一步,快速开始

搜索
查看: 10614|回复: 42

别小看PHP,之框架设计篇。

[复制链接]
发表于 2006-7-15 02:01 | 显示全部楼层 |阅读模式
此栏为专题系列文章。等我有空慢慢写。
 楼主| 发表于 2006-7-15 02:38 | 显示全部楼层
首先,我不是作家,文笔不一定好,逻辑不一定正常,也许,我本来就是一个怪物,但是,只要你看得懂,不会让你失望的,我尽量写好一点并附上我的代码。
我写这个系列文章,一来是期待能遇到志同道合的朋友;二来也是希望能留下一些心得;三来嘛恐怕像hjack wool王之类的师弟都有深刻体会:我们是孤独的一群,我们需要和别人交流、期待被别人所认同;四来也是受wool王的激励。



php能做什么?php的优点是什么?
现在的语言层出不穷,就我自己的经历中,可以分成3大类。
1,专用语言。
像汇编、c语言、科学计算语言、人工智能专用语言等等。这些语言的最佳适用范围比较窄,如c语言可以用来开发驱动、系统底层、嵌入式设备的程序等,但在2006年来说,终究是属于专用语言一类。即特殊场合的语言(c就是应用在性能要求非常高的领域上)。
2,通用语言。
如c++,delphi等等。这类语言的特点是用途广泛,通常我们所使用的80%的软件或者其组件都是使用这类语言所开发出来的。
3,商业语言。
典型的如php、asp、java(包括jsp)、c#(包括那几个生成同一种中间码的dot nei语言),python、perl、javascript、action script。为什么不叫网络语言呢?确实,这里所有的语言都与互联网脱不开关系,但我认为,从来网络就不是设计以公益用途,看众多的com就知道了。

分这个类是想描述一下现在以及未来的行业趋势,如果说80-90年代通用软件是商业软件的标志的话,毫无疑问,现在这个世界是网络软件、平台的世界,也是最能提供机遇以及就业岗位的领域。从我们切身的未来出发,商业语言的地位是无疑最值得我们关注的。这也是我唠唠叨叨要说明的问题,不要小看php的理由之一:网络上众多的解决方案都是由php提供的,包括现在用的论坛。


同时,php不单只能用来做网站。4个月之前,我用它来实现了我按照自己设计的网络爬虫,这时候它扮演着后台程序的角色。2个月之前,我用它来实现了自动标签的核心分词算法,这是它作为关键组件的定位。而现在,我的电脑里储存了大量的脚本文件,只要我打开浏览器的书签(ie叫收藏夹),然后我可以在半小时内把百度的中文歌曲排行榜前100强下载到我的共享文件夹,也可以连接到某公司网站的一个短信接口帮我下发多达200多条节日问候短信。这时它就是一个个强大的应用程序。也是叫大家不要小看它的理由之二。


总结:php的优点就是,适用范围更广,开发迅速。
回复

使用道具 举报

 楼主| 发表于 2006-7-15 02:48 | 显示全部楼层
由于非常罗嗦、十分复杂、尤其晦涩、异常难言的原因,我决定自己跳出来开创自己的事业。
我失业了,同时,我解放了。


于是,我对准我觉得有搞头的网络,开始了我的YY之旅(借用网络小说的惯用说法),于是我开始了我觉得很重要的打根基行动,设计自主研发的php framework。没错,不是汉芯或者方舟之流的东西。
回复

使用道具 举报

 楼主| 发表于 2006-7-15 02:54 | 显示全部楼层
现在java 流行的是MVC,我的框架也是基于MVC,我对MVC理解的不透彻,所以我的设计不一定是你所理解的MVC。


先看看我所设计的结构


    * CMvc
          o CControler
                + CApplication
                      # CAppApplication
                            * CWeb
                + CFeature
                      # CAppFeature
                            * CFAdvancedSearch
                            * CFHomePage
                            * CFHomepage
                            * CFHotel
                            * CFList
                            * CFListByExpert
                            * CFListByProvinceCity
                            * CFListByStar
                            * CFListByUser
                            * CFManager
                            * CFQuickSearch
                + CModule
                      # CAppModule
                            * CCommon
                            * CCustomer
                            * CHomePage
                            * CHotel
                            * CUser
          o CModel
          o CView
                + CCache
                      # CAppCache
                            * CXhtmlCache
                + CTemplate
                      # CAppTemplate
                            * CXhtml
回复

使用道具 举报

 楼主| 发表于 2006-7-15 03:46 | 显示全部楼层
从结构上,基本可以肯定这是一个看起来像MVC的东西,有controler,有view,有个CModel的类。

但后来我基本上抛弃了在CModel下面开发一大堆子类的想法,我觉得,那是愚蠢以及幼稚的。

熟悉MVC的人都认为:M是MVC的核心,因为M是代表面向对象的标志。没有了M,就剥夺了整个MVC的精华所在。在MVC的理论中(我所理解的不一定对):数据模型M的优势在于其封装性,即属于此对象(关联到一个表)的属性(关联到表的字段)数据由对象方法来维护,然后M对象调用自己的DAO方法更新数据库,从而达到理想的圆满状态:重用、优雅、安全。

我摒弃这样的做法,这是java一族可以说是最大的败笔:臃肿。java梦想世界是规律的,是可以用优雅而美丽的继承以及重用关系规律描述的,结果是导致框架库对象数目的剧增。这理所当然是我等自觉优品的人所要避免的。所以,我不要所有的模型类和所有模型的DAO类,即使用这个框架的同志们可以骄傲的对wool王说:同志们辛苦了(等待他们写完Model类和DAO类后回复一句:首长好。呵呵,开玩笑了)。

这个框架提倡使用数组来保存Model数据,即回到了c时代的结构的使用。如果用的是mysql,mysql_fetch_assoc返回的就是一个数组。就算是自己创建的对象,也建议使用数组来存储其属性,这样便于直接输出数组供别的对象处理。

为了操纵实体对象,我们在上面实现一层所谓逻辑对象
CAppBusi

    * CAppBusi
          o CAppBusiDao
                + CBDCity
                + CBDHotel
                + CBDHotelPhoto
                + CBDHotelRoom
看上图,这里是一层逻辑对象,但与真正MVC的DAO有根本的区别,真正MVC的DAO中,一个对象对数据库的操作中Add,Delete,Update,Get,Query是基本基本访问操作,如果要组合成为商业逻辑,如查找某城市的所有下属城市和其各自的酒店数目。那得在另外一个逻辑对象来实现。而上图这一层是真正MVC中的DAO类和逻辑类的组合,直接命名为商业逻辑实现类。其实现的都是原子逻辑方法或者其组合方法。

实际上在此层上面还有一层对象还没体现出来,叫做CBT对象,就是CBusiTransation对象的子对象,顾名思义,这就是最经常说的服务,看名字就知道这是一个事务的起点和终点,其结果直接导致一个完整服务的完成或者失败。这一层根据需要封装上一段所提到逻辑层的某几个原子逻辑或者组合逻辑。

举个例子,
3个表
User Operator Contact
User记录了用户的资料,Operator记录了操作员的资料(某用户可以有几个操作员的角色),Contact记录了每个用户的数种联系方式。

有一个逻辑A叫做增加用户,向User表插一条记录。
有一个逻辑Q叫做查询用户,向User查询某ID的User是否存在。
有一个逻辑B叫做增加操作员,B会检查User是否存在某用户(调用Q),然后才执行插Operator。
有一个逻辑C叫做增加联系方式,C同样检查User是否存在某用户(调用Q),然后才执行插Contact

有一个事务T1叫做用户注册,它会启动事务,然后按顺序调用A、B、C且在所有操作都成功时提交事务,否则回滚事务。
有一个事务T2叫做增加联系方式,它会启动事务,然后调用C且在成功时提交事务,否则回滚事务。




数组+逻辑+事务,就是我所谓的模型层,数组基本上无需代码去设置数据和返回数据,所有数据库操作返回的行都是数组,所有的对象只要遵从用数组来记录属性值,都可以直接方便输出数组。这样发挥了php的高效开发的优势。可比java方便多了。
而逻辑是无论任何语言都要面对的编程问题,简单的add,delete和update可以使用一个统一的CDAO对象来存取(我就是这样做的),但更多的查询(如关联查询)则是需要实实在在写sql的。所以把这两层合在一起简化了层次的耦合,也同样发挥了高效开发的特点。
事务对象则是我觉得为难的一个地方,作为更新用途的事务层无疑可以随文档给予合作的同事更清晰的封装。但web上很多操作是不需要事务的select query,这破坏了我所想要的

controler  -----调用------>  【事务->逻辑->CDAO类(这个类不是一个层次,而是就是一个单一的类,封装一些简单的增删改)】   -------返回-------->数组(实体模型)


这样的理想层次结构。
不过正如我所说的:“java梦想世界是规律的,是可以用优雅而美丽的继承以及重用关系规律描述的,结果是导致框架库对象数目的剧增”,既然世界是多样的、需求也是多样的,根本无法用非常有规律且统一的模式来满足所有的要求,那又何必在意不能实现使用一个按部就班的层次结构呢?须知ISO 7层的网络协议结构还不是被TCP/IP混合结构打败了呢?

看来,不管黑脚白脚,非香港脚才是硬道理。

[ 本帖最后由 sasadong 于 2006-7-15 06:28 编辑 ]
回复

使用道具 举报

 楼主| 发表于 2006-7-15 03:52 | 显示全部楼层
原帖由 iptton 于 2006-7-15 03:00 发表
师兄开创自已的事业呀

厉害

有空我也会YY有朝一日。。。的

现在学CFML  发现没多少人学,有空也要在这发点新手体验……



看了你的留言,其实我也不懂CFML,google一下才知道是什么。其实也不必在意有没人学,我学php,很多人也学php,但我发现能聊得起来的也不是php。重要的是朋友、重要的是人生。

我是来这里骗朋友来的。你看,你就是一个啦。哈哈
回复

使用道具 举报

 楼主| 发表于 2006-7-15 04:32 | 显示全部楼层
下面简单说一下controler

在传统MVC的的理论中,Controler代表一个事件产生以及相关的事件动作的响应。作为MVC的说法中,View视图提供了对Model实体的表现,而Controler则是沟通V合M的桥梁,它描述了当用户在V上搞小动作时是怎么操作M和更新V的状态。
在web这样的结构中,M存在于数据库,或者session,或者文件。而V存在于用户的浏览器,你按下Ctrl+S就能看到V被保存了。那么Controler是什么?就是脚本,在这里就是php程序文件。根据MVC的描述,用户在浏览器上点来点去折腾了半天,最后向Controler即php程序文件发送了一个执行事件、总共也就只有一个执行事件,为了把握好这个事件,为了面子,为了实现伟大的共产主义,为了与法X功决战,我们必须采用老比尔的经验,我们不能说我们只有一个事件,就算只有一个,我们也要厚着脸皮说我们有N个事件。

你了解windows的消息机制么?

在windows中,所有的消息都通过统一的渠道、类似邮政的投递系统一样一层层的派发到具体的邮局,并且分为信件、快件、EMS、报纸等等种类。

在php甚至是jsp、asp、c#中,如果每个程序页面都可以被用户直接访问,那相当于有数个邮政公司,一个叫做中国邮政、一个叫做李红制邮政、一个叫做耶稣邮政,用户可以自由选择自己所需要的邮政公司投递,事实上,中共中央告诫我们,多党执政是可怕的,是问题多多D。起码耶稣把你的邮件送到天堂去或者李红制把你的邮件送到地狱去而你家人在人间,那多么令人郁闷且无奈啊?

所以,这个php framework搞的是一党执政。整个网站只有一个入口,即对外公布的url地址,就只有一个www.example.com/index.php文件,对外投递的所有消息都由此柜台办理,并与邮政类似,在此分为:
Application应用、Module模块、Feature功能、和Step步骤。

消息按照从大区到小的区层层传递下去,因而在Controler的层次结构中就产生了
CMvc

    * CMvc
          o CControler
                + CApplication
                      # CAppApplication
                            * CWeb
                + CFeature
                      # CAppFeature
                            * CFAdvancedSearch
                            * CFHomePage
                            * CFHomepage
                            * CFHotel
                            * CFList
                            * CFListByExpert
                            * CFListByProvinceCity
                            * CFListByStar
                            * CFListByUser
                            * CFManager
                            * CFQuickSearch
                + CModule
                      # CAppModule
                            * CCommon
                            * CCustomer
                            * CHomePage
                            * CHotel
                            * CUser
这几个层次结构,其中,这如CFHotel就是最终执行逻辑的php 功能类文件,其和模块类CHotel和应用类CWeb构成了这一事件的响应对象链。CFHotel内部根据步骤Step的不同执行不同的事件方法,事件方法内部调用“模型层”的事务类对象或者逻辑类对象方法返回实体模型(数组)。再填充到适当的视图里返回到浏览器展现。

从整个流程来看:


用户书写url-----点击查询事件----->浏览器某查询条件页面(视图1的状态A)------点击查询后产生事件----->controler控制器执行(操作模型并更新视图)-------浏览器某查询结果页面(视图1的状态B)

这便是一个web结构的MVC模型了。

[ 本帖最后由 sasadong 于 2006-7-15 04:34 编辑 ]
回复

使用道具 举报

 楼主| 发表于 2006-7-15 05:45 | 显示全部楼层
下面该到View视图了。

什么是视图呢?我们是大学生,使用高级的类比语法,单看一个MM的QQ资料描述:


昵称:╰☆^ō^γ★
真实姓名:╰☆傻卡★
国家和地区:GZ
州/省:我住晒
城市:外星
性别:女
年龄:23

假设,同志们注意了,下面是魔兽的时段,我是一个怪兽,但大家不要K我,不然会死人D。

假设这个女的是四川人,好,她的形象出来了,她长着一副清秀洁白的脸蛋,有着天真可爱的笑容,今年刚毕业来广州工作,且开始喜欢上这个城市,每天打扮得漂漂亮亮的,但外表清纯不说明她内心幼稚。相反,她拥有一颗成熟而挑剔的心,工大牌童子鸡从来不是她的食物。所以我们的同学注定痛苦一生。

再假设,这个女的是正宗广州本地人。好,她的形象也出来了。她长着一副平常人的相貌,喜欢把头发挑染成乱紫色,不时要做一些化小妆上街再美甲穿耳扫潮也之类的勾当,喜欢看台剧并且因为经常出去蒲而没能考上理想中的工大计算机只能在街上开家服装小店,因而喜欢做大笑姑婆的傻状和twins的可爱状去勾引我们善良的从事高科技行业坐在玻璃幕墙现代办公室内高级白色外表胶合板内里外卖120元套的计算机桌椅上埋头coding的闪亮工大牌纯净水、哦错了、是工大牌毕业的二十一世纪大学生,并且在得逞后因为发现QQ里面搞IT的人原来也不是个个都那么帅起码不像想象中电脑城那么多帅哥之后就在见面的一瞬间说她家里发来信息说刚扭开了煤气炉褒童子鸡汤忘记关火她现在要回家去关了。


走题了。努力,不要写水贴。
MM的资料好比是数据库表里的一行数据,也就是一个实体模型。那么这两个看起来完全不同的两个人都可以完全表现这个模型里面的数据。这就是因为我们使用了两种视图的原因:四川人的视图和广州人的视图。
一个实体,可以使用不同的视图去表现,同样,一种视图,可以表现不同的实体对象。
至于如何组合那就是你自己的意愿并且写controler来控制了。


而这个框架中的视图,
# CView

    * CCache
          o CAppCache
                + CXhtmlCache
    * CTemplate
          o CAppTemplate
                + CXhtml

首先是分成两组,一组是缓存,一组是模板。
先说模板,模板提供了一个只需使用名字便可以切换不同视图的好方法。这里我使用smarty作为模板引擎。而CXhtml则根据需要可以输出Xhtml格式的视图,相应的可以有CWml的wap视图,或者CXml的数据输出视图供ajax使用,或者CRss供Rss阅读器使用。而且直接统一可选择性的支持utf8或者gbk编码(配合utf8格式的模板文件使用,完全支持全网utf8,怎么别人费尽心思去utf8化的相关技巧在我这里无需用呢?只要再在mysql查询前面加SET NAMES 'utf8';,在建表时使用utf8格式,一切就那么简单)。更玄妙的是,加上ob_start("ob_gzhandler");,令服务器支持压缩传送。
再说说缓存CCache类簇,缓存被设计成支持本地文件缓存,或者mmcache分布式缓存,所有缓存实现了自动压缩以降低文件占用空间或者网络传输时间(可以禁止压缩),缓存可降低对数据库的交互、提高网站访问的并发性能力。



最后,总结一下:使用php+上这个framework,可以支持下面特性:
1,完全轻松utf8(这对于辛辛苦苦调试在jsp上实现utf8发现众多问题的人们是什么样的福音啊)
2,压缩传送(当pconline首页达到惊人的174KB时,使用压缩传送最多不超过15KB,这对于使用虚拟主机的站长来说,流量就是金钱啊)
3,压缩本地文件缓存(对于小站点使用虚拟主机来说,空间就是金钱啊)
4,支持分布式缓存(实现大型网站完全不是问题,统一的分布式缓存mmcache可以让你轻松实现session的统一,即单点认证,这可是很多大公司的解决方案,要收钱的,可怜的很多公司,上10万弄个单点认证系统居然被这么轻松实现了,同时分布式缓存在支持上百万同时在线的网站上可以达到非常好的命中效果,这个大家请看“一个藏袍”的网站)
5,支持网页静态化。(写多一个CHtmlStatic类,然后挂在CView下,使用时给个文件名,就这么简单)。
6,支持多语言(同一个页面,copy多一份,然后把中文翻译成繁体、英文、西班牙语、火星语。在controler里面的应用程序类已经做了多国语言的支持去读取不同的模板目录)。
7,应用和逻辑分离。总的来说,开发一个页面就是:想好一个功能该怎么做,如登录页面分为2步骤,一是输出登录表单页面,二是提交登录请求。然后用DW画出2个页面,一个页面是登录的两个输入框。一个是登录结果页面,里面含有两种结果:失败和成功。然后写相应的数据逻辑,也就是CBDOperator,写一个Login方法,能重用已有的返回单行方法就重用,然后简单做一个密码匹配。最后写两个方法,一个简单输出“登录模板视图”,一个调用登录逻辑根据结果设置一下模板输出视图。大功告成。这个html逻辑和脚本逻辑分离可说是多少人的夙愿。
8,统一简单的防止sql注入。在一个成功的框架中,例如dot net框架,例如java框架,就是提供了很多非常有用的功能类供应用程序快速建立。因为这个框架的入口脚本只有一个,因而在上面过滤关键字select update delete之类的文字并对'"之类的引号进行加工即可。
9,统一的错误处理模型。这是尚未完成的,因为目前环境下商业化支持php5暂时还未能完成,且商业化的PEAR不被支持,所以等待完成,不过毫无例外将使用exception来处理异常,并且将在Feature未能处理的时候由上层自动处理,以后出现的文件未能找到或者查询出错等问题将能自动换成非常人性化的提醒页面。
10,先进的内容自动关联组件。这是完全自主知识产权的东西,已经写好设计了,就等实现,这也是框架中比较有创新性的东西。其作用就是自动对内容进行分析、分类、关联。形象点来说就是类似于新浪的新闻下方的相关链接,我不知道新浪是否是自动的,但这个组件可以自动的完成这个过程。其实核心就是识词、分词、归类。
11,所有更加多的组件:分页组件、表单校验组件只要你想得到,你可以用最简单的方法加入组件库。

[ 本帖最后由 sasadong 于 2006-7-15 06:37 编辑 ]
回复

使用道具 举报

 楼主| 发表于 2006-7-15 05:52 | 显示全部楼层
下面是一个典型的Controler层的Feature类对象方法,其中前面将近一半代码是每个页面逻辑都使用的


  /**
  * 返回某省份的酒店列表页面
  *
  * @param  CCommand $oCommand 命令对象
  * @return CView $oView 视图对象
  * @access  private
  */       
        function ProcessProvince( $oCommand )  {
                //获取参数,处理参数
                $oFormValue = new CFormValue() ;
                $oFormValue->GetOldFormIfExists() ; //为翻页读取旧表单数据
               
                //准备分页用的查询字符串,缓存ID
                $sQueryString = 'r=' . $oFormValue->ToFormValueString() ;       
                $sCacheID = 'Province_' . md5( $sQueryString ) .'_p_' . $oFormValue->GetValue( 'p' ) ;               
               
                //检查缓存,如果有缓存,即可返回缓存
                $oCache = CAppFactory::CreateCache( TEMPLATE_TYPE_XHTML, $sCacheID, &$this );
                if ( $oCache->CacheExists() ) {
                        return $oCache ;
                }                       
               
                //准备数据库对象,以及用到的数据方位逻辑对象(DAO)
                $oDB = CAppFactory::CreateDB( false/*bCacheExecute*/ ) ;
                $oBDHotel = new CBDHotel( $oDB ) ;       
                $oBDCity = new CBDCity( $oDB ) ;               
               
                //处理并返回视图页面
                $oView = CAppFactory::CreateTemplate( TEMPLATE_TYPE_XHTML, 'Province', &$this );
               
                //1,分页
                $aHotelsCount = $oBDHotel->QueryByProvince( $oFormValue->GetValue( 'PROVINCE_ID' ), $oCommand->_iPage, DEFAULT_ROWS_PER_PAGE, GET_COUNT ) ;
                $oPageSpliter = new CPageSpliter( $oCommand->_iPage, $aHotelsCount, DEFAULT_ROWS_PER_PAGE, DEFAULT_RANGE_PER_PAGE ) ;
                $oView->Assign( 'aPageSpliter', $oPageSpliter->GetArray() ) ;               
               
                //2,酒店列表
                $aHotels = $oBDHotel->QueryByProvince( $oFormValue->GetValue( 'PROVINCE_ID' ), $oCommand->_iPage, DEFAULT_ROWS_PER_PAGE, GET_ROWS, WITH_PHOTOS, WITH_ROOMS, WITH_AREAIDS, NOT_WITH_DETAILS, WITH_HONOURS ) ;
                $oView->Assign( 'aHotels', $aHotels ) ;               
               
                //3,主要城市列表
                $aMainCities = $oBDCity->GetMainCitiesHaveHotel() ;
                $oView->Assign( 'aMainCities', $aMainCities ) ;                       
               
                //4,城市/区县列表
                $aCityCities = $oBDCity->QueryCityCitiesHaveHotelByProvince( $oFormValue->GetValue( 'PROVINCE_ID' ) ) ;
                $oView->Assign( 'aCityCities', $aCityCities ) ;       
               
                //5,省实体
                $aProvince = $oBDCity->GetByID( $oFormValue->GetValue( 'PROVINCE_ID' ) ) ;
                $oView->Assign( 'aProvince', $aProvince ) ;       
               
                //兄弟省份列表
                $aBrotherProvinces = $oBDCity->QueryBrotherProvincesByID( $oFormValue->GetValue( 'PROVINCE_ID' ) ) ;
                $oView->Assign( 'aBrotherProvinces', $aBrotherProvinces ) ;       
                $oView->SetCache( $oCache ) ;
                return $oView ;
        }

[ 本帖最后由 sasadong 于 2006-7-15 06:38 编辑 ]
回复

使用道具 举报

 楼主| 发表于 2006-7-15 06:04 | 显示全部楼层
这是一个典型的CBD类对象,在实体层中,这一类对象实际操纵数据库,并且完成相当多的商业逻辑。
看看这些代码,应该不会让你难过吧?

<?php

/**
* 省份,城市,区县数据访问对象
*
* @author Toney Zeng<[email protected]>
* @version  V1.0
* @access  public
* @package  HOTEL
*/
class CBDCity extends CAppBusiDao
{
        /**
        * 构造函数
        *
        * @param CMysqlDB $oDB CMysqlDatabase object
        * @return void
        * @access  public
        */
        function CBDCity( $oDB ) {
                parent::CAppBusiDao( $oDB, 'CITY' ) ;
        }
       
        /**
        * 返回实体属性数组
        * 返回以属性名为key,属性类型为值的二维数组
        *
        * @return array $oEntityFields 实体属性数组
        * @access  private
        */
        function GetEntityFields() {
                $aFields = array(
                        'ID' =>  TYPE_AUTO,
                        'NAME' =>  TYPE_STRING,
                        'PROVINCE' =>  TYPE_STRING ,
                        'CITY_NO'  =>  TYPE_STRING,
                        'ZIP'  =>  TYPE_STRING,
                        'HUMAN'  =>  TYPE_STRING,
                        'AREA'  =>  TYPE_STRING,
                        'PROVINCE_FLG'  =>  TYPE_INT,
                        'CITY_FLG' =>  TYPE_INT ,
                        'COUNTY_FLG' =>  TYPE_INT,
                        'PARENT_ID' =>  TYPE_INT
                ) ;
               
                return $aFields ;
        }
       
        /**
        * 返回省份数组
        * 返回国家行政区的省份数组
        *
        * @return array $aProvinces 省份数组
        * @access  private
        */
        function GetProvinces() {
                $aConditions = array( 'PROVINCE_FLG' => 1 );               
                $aProvinces = $this->Query( $aConditions ) ;
                if ( $aProvinces === false )                 {
                        return false ;
                }
               
                $aNewProvinces = array() ;
                foreach( $aProvinces as $aRow ) {
                        $aNewProvinces[ $aRow['ID'] ] = $aRow['NAME'] ;
                }
                return  $aNewProvinces ;
        }
       
       
       
        /**
        * 根据ID返回城市对象
        * 返回对应ID的城市对象
        *
        * @param int $iID 某城市ID
        * @return array $aCity 城市对象数组
        * @access  private
        */
        function GetByID( $iID ) {
                return $this->Get( array( 'ID' => intval( $iID ) ) ) ;
        }
       
       
        /**
        * 根据名称返回城市
        * 返回具有名称的城市
        *
        * @param string $sName 某城市ID
        * @return array $aCity 城市对象数组
        * @access  private
        */
        function QueryByName( $sName ) {
                $sName = strtoupper( trim( $sName ) ) ;
                $sSql = "SELECT * FROM CITY WHERE (NAME LIKE '%$sName%' OR EN_NAME LIKE '%$sName%')" ;
                $aRows = $this->QuerySql( $sSql ) ;
                if ( $aRows === false )                 {
                        return false ;
                }
                return  $aRows ;
        }
       
       
        /**
        * 根据所属省份返回城市数组
        * 查询指定省份下的城市
        *
        * @param int $iProvinceID 某省份ID
        * @return array $aCities 城市数组
        * @access  private
        */
        function GetCities( $iProvinceID ) {
                $aConditions = array( 'CITY_FLG' => 1, 'PARENT_ID' => $iProvinceID );               

                $aCities = $this->Query( $aConditions ) ;
                if ( $aCities === false )                 {
                        return false ;
                }
               
                $aNewaCities = array() ;
                foreach( $aCities as $aRow ) {
                        $aNewaCities[ $aRow['ID'] ] = $aRow['NAME'] ;
                }
                return  $aNewaCities ;
        }
       
        /**
        * 根据所属城市返回区县数组
        * 查询指定城市下属的区县数组
        *
        * @param int $iCityID 某城市ID
        * @return array $aCounties 区县数组
        * @access  private
        */
        function GetCounties( $iCityID  ) {
                $aConditions = array( 'COUNTY_FLG' => 1, 'PARENT_ID' => $iCityID  ) ;       
                $aCounties = $this->Query( $aConditions ) ;
                if ( $aCounties === false )                 {
                        return false ;
                }
               
                $aNewaCounties = array() ;
                foreach( $aCounties as $aRow ) {
                        $aNewaCounties[ $aRow['ID'] ] = $aRow['NAME'] ;
                }
                return  $aNewaCounties ;
        }
       
       
        /**
        * 按照酒店真实存在返回主要城市数组
        * 返回的城市数组有两大特性,1、该城市下有酒店存在,2、该城市为主要城市。
        * 返回的城市数组带有酒店数目
        *
        * @return array $aCities 省份数组
        * @access  private
        */
        function GetMainCitiesHaveHotel( ) {
                $sSql = "SELECT B.*,A.COUNT FROM (SELECT COUNT(*) AS COUNT,CITY_ID FROM HOTEL WHERE CITY_ID IN (SELECT ID FROM CITY WHERE IMP=1 AND CITY_FLG=1) GROUP BY CITY_ID) A LEFT JOIN CITY B ON A.CITY_ID=B.ID" ;
                $aRows = $this->QuerySql( $sSql ) ;
                if ( $aRows === false )                 {
                        return false ;
                }
                return  $aRows ;
        }
       
        /**
        * 按照酒店真实存在返某区县的兄弟区县数组
        * 返回的区县数组有如下特性,1、该区县下有酒店存在。
        * 返回的区县数组带有酒店数目
        *
        * @param int $iCountyID 某区县ID
        * @return array $aCounties 区县数组
        * @access  private
        */
        function QueryBrotherCountiesByID( $iCountyID ) {
                $sSql = "SELECT B.ID,B.NAME,A.COUNT FROM (SELECT COUNT(*) AS COUNT,COUNTY_ID FROM HOTEL WHERE COUNTY_ID>0 AND CITY_ID=(SELECT PARENT_ID FROM CITY WHERE ID=$iCountyID) AND COUNTY_ID != $iCountyID GROUP BY COUNTY_ID) A LEFT JOIN CITY B ON A.COUNTY_ID=B.ID " ;
                $aCityRows = $this->QuerySql( $sSql ) ;
                if ( $aCityRows === false )                 {
                        return false ;
                }               
                return  $aCityRows ;
        }

        /**
        * 按照酒店真实存在返某城市的兄弟城市数组
        * 返回的城市数组有如下特性,1、该城市下有酒店存在。
        * 返回的城市数组带有酒店数目
        *
        * @param int $iCityID 某城市ID
        * @return array $aCities 城市数组
        * @access  private
        */       
        function QueryBrotherCitiesByID( $iCityID ) {
                $sSql = "SELECT B.ID,B.NAME,A.COUNT FROM (SELECT COUNT(*) AS COUNT,CITY_ID FROM HOTEL WHERE CITY_ID>0 AND PROVINCE_ID=(SELECT PARENT_ID FROM CITY WHERE ID=$iCityID) AND CITY_ID != $iCityID GROUP BY CITY_ID) A LEFT JOIN CITY B ON A.CITY_ID=B.ID " ;
                $aCityRows = $this->QuerySql( $sSql ) ;
                if ( $aCityRows === false )                 {
                        return false ;
                }               
                return  $aCityRows ;
        }
       

        /**
        * 按照酒店真实存在返某省份的兄弟省份数组
        * 返回的省份数组有如下特性,1、该省份下有酒店存在。
        * 返回的省份数组带有酒店数目
        *
        * @param int $iProvinceID 某省份ID
        * @return array $aCities 省份数组
        * @access  private
        */       
        function QueryBrotherProvincesByID( $iProvinceID ) {
                $sSql = "SELECT B.ID,B.NAME,A.COUNT FROM (SELECT COUNT(*) AS COUNT,PROVINCE_ID FROM HOTEL WHERE PROVINCE_ID>0 AND PROVINCE_ID != $iProvinceID GROUP BY PROVINCE_ID) A LEFT JOIN CITY B ON A.PROVINCE_ID=B.ID" ;
                $aCityRows = $this->QuerySql( $sSql ) ;
                if ( $aCityRows === false )                 {
                        return false ;
                }               
                return  $aCityRows ;
        }
       
        /**
        * 按照酒店真实存在返某城市的下属区县数组
        * 返回的区县数组有如下特性,1、该区县下有酒店存在。
        * 返回的区县数组带有酒店数目
        *
        * @param int $iCityID 某城市ID
        * @return array $aCounties 区县数组
        * @access  private
        */
        function QueryCountiesHaveHotelByCity( $iCityID ) {
                $sSql = "SELECT B.*,A.COUNT FROM (SELECT COUNT(*) AS COUNT,COUNTY_ID FROM HOTEL WHERE COUNTY_ID>0 AND CITY_ID=$iCityID GROUP BY COUNTY_ID) A LEFT JOIN CITY B ON A.COUNTY_ID=B.ID" ;
                $aRows = $this->QuerySql( $sSql ) ;
                if ( $aRows === false )                 {
                        return false ;
                }               
                return  $aRows ;
        }
       
        /**
        * 按照酒店真实存在返某省份下的城市数组
        * 返回的城市数组有如下特性,1、该省份下有酒店存在。
        * 返回的城市数组带有酒店数目
        *
        * @param int $iProvinceID 某省份ID
        * @return array $aCities 省份数组
        * @access  private
        */               
        function QueryCityCitiesHaveHotelByProvince( $iProvinceID ) {

                $sWhere = '' ;
                $iProvinceID = intval( $iProvinceID ) ;
                if ( $iProvinceID >= 0 ) {
                        if ( strlen( $sWhere ) > 0 ) {
                                $sWhere .= ' AND ' ;
                        }
                        $sWhere .= ( ' PROVINCE_ID=' . $iProvinceID . ' ' ) ;
                }
                if ( strlen( $sWhere ) > 0 ) {
                        $sWhere = ' AND ' . $sWhere ;
                }
               
                //指定省份的所有城市统计
                $sSql = "SELECT B.ID,B.NAME,A.COUNT FROM (SELECT COUNT(*) AS COUNT,CITY_ID FROM HOTEL WHERE CITY_ID>0 " . $sWhere . " GROUP BY CITY_ID) A LEFT JOIN CITY B ON A.CITY_ID=B.ID" ;
                $aCityRows = $this->QuerySql( $sSql ) ;
                if ( $aCityRows === false )                 {
                        return false ;
                }
                $aNewCities = array() ;
                foreach( $aCityRows as $aCity ) {
                        $aNewCities[ $aCity['ID'] ] = $aCity ;
                }
               
                //指定省份的所有区县统计
                $sSql = "SELECT B.*,A.COUNT FROM (SELECT COUNT(*) AS COUNT,COUNTY_ID FROM HOTEL WHERE COUNTY_ID>0 " . $sWhere . " GROUP BY COUNTY_ID) A LEFT JOIN CITY B ON A.COUNTY_ID=B.ID" ;
                $aRows = $this->QuerySql( $sSql ) ;
                if ( $aRows === false )                 {
                        return false ;
                }               
                foreach( $aRows as $aCounty ) {
                        $aNewCities[ $aCounty['PARENT_ID'] ]['CITIES'][] = $aCounty ;
                }
                return  $aNewCities ;
        }
       
        /**
        * 按照酒店真实存在返全部省份城市数组
        * 返回的省份城市数组有两大特性,1、该城市下有酒店存在。
        * 返回的省份城市数组带有酒店数目,数组结构为数组->省(名称,城市)->市
        *
        * @return array $aCities 省份数组
        * @access  private
        */
        function GetAllPrivinceCitiesHaveHotel( ) {
                $sSql = "SELECT B.ID,B.NAME,A.COUNT FROM (SELECT COUNT(*) AS COUNT,PROVINCE_ID FROM HOTEL WHERE PROVINCE_ID>0 GROUP BY PROVINCE_ID) A LEFT JOIN CITY B ON A.PROVINCE_ID=B.ID" ;
                $aProvinceRows = $this->QuerySql( $sSql ) ;
                if ( $aProvinceRows === false )                 {
                        return false ;
                }
                $aNewProvinces = array() ;
                foreach( $aProvinceRows as $aProvince ) {
                        $aNewProvinces[ $aProvince['ID'] ] = $aProvince ;
                }
               
                $sSql = "SELECT B.*,A.COUNT FROM (SELECT COUNT(*) AS COUNT,CITY_ID FROM HOTEL WHERE CITY_ID>0 GROUP BY CITY_ID) A LEFT JOIN CITY B ON A.CITY_ID=B.ID" ;
                $aRows = $this->QuerySql( $sSql ) ;
                if ( $aRows === false )                 {
                        return false ;
                }               
                foreach( $aRows as $aCity ) {
                        $aNewProvinces[ $aCity['PARENT_ID'] ]['CITIES'][] = $aCity ;
                }
                return  $aNewProvinces ;
        }
}
?>
回复

使用道具 举报

 楼主| 发表于 2006-7-15 06:22 | 显示全部楼层
写在最后,
如果你喜欢php,请继续往下看,否则,你可以离开了。

如果你是初学者,你先回去看php的语法,去别的php网站去看别人如何写的程序,或者干脆下载一个Discuz!研究一下其中的一些代码文件。当你觉得别人写的文章好像没什么意思时,你再来回头看这篇我相信你到那时才能看懂的文章。

如果你是php老手。那么你一定很想说出你的疑问或者你可以直接指出文章观点或者设计的错误。我很期待,因为我虽然快完成这个框架了但是无时不刻我都想提高它的效率和高效开发的优势,以便时间合适的话我就公布给大家。例如我已经知道smarty有自己的cache系统,PEAR也有一个模块用于实现文件cache,但我不知道自己的模块的性能与别人相比是否有优势。例如我知道有许多开源项目实现了php的framework,但不知道其性能如何。例如我这个framework里面实现了一个超级简陋的ajax不敢拿出来见人。
我期待有志的同学们能提出有用的东西和观点。




前天在这里发现有个人说工大BBS里就这个德行。
我很郁闷,白天大家都要上班上课准备毕业,看我今晚忙活一个晚上才写了不过2-3千字。你只留下一句话就带着自己“高尚”的外衣走掉了。结果是你把马蜂窝捅开了,wool王开始努力,powerwind也要发力,我就做神州系列的火箭推进器,等待神6、7、8、9、10。。。。。。火箭们遍地开花。
回复

使用道具 举报

 楼主| 发表于 2006-7-15 07:03 | 显示全部楼层
框架里有用的几个小类,有些注释原来是gbk编码的转成utf8弄坏了。

<?php
/**
* mysql数据库处理对象
* 此对象提供对mysql数据库的访问操作
*
* @author   Toney Zeng<[email protected]>
* @version  V1.2
* @access  public
* @package  MVC
*/
class CMysqlDB
{
        var $_rDBLink ;
        var $_bCacheExecute ;
        var $_sSql ;
        var $_sLogDir = '' ;
        var $_bDBConnected = false ;

  /**
  * 构造函数
  *
  * @param string $sServer 服务器
  * @param string $sUser 用户
  * @param string $sPass 密码
  * @param string $sDatabase 数据库
  * @param boolean $bCacheExecute 是否缓存查询
  * @return void
  * @access  public
  */
  function CMysqlDB( $sServer, $sUser, $sPass, $sDatabase, $bCacheExecute = false )  {
                if ( false === ($this->_rDBLink = mysql_connect( $sServer, $sUser, $sPass )) ) {                       
                        $sContent .= " [ERR_CONNECT_DB] " . $this->GetErrorString() ;
                        $this->_SaveToLog( $sContent ) ;                       
                }
                $this->_bCacheExecute = $bCacheExecute ;
                if ( $this->_rDBLink !== false ) {
                        if ( false === mysql_select_db( $sDatabase, $this->_rDBLink ) ) {       
                                $sContent .= " [ERR_SELECT_DB] " . $this->GetErrorString() ;
                                $this->_SaveToLog( $sContent ) ;                                               
                        }
                        $this->Execute( "SET NAMES 'utf8';" ) ;
                        $this->_bDBConnected = true ;
                }
  }
  
  /**
  * 返回当前的连接状态
  *
  * @return boolean 是否连接
  * @access  public
  */
  function GetDBConnected() {
          return $this->_bDBConnected ;
  }
       
  /**
  * 设置数据库操作的日志存放目录
  *
        * @param string $sDir 日志目录
  * @return void
  * @access  public
  */
        function SetLogDir( $sDir ) {
                $this->_sLogDir = $sDir ;
        }

  /**
  * 返回出错字符串
  *
  * @return string 出错字符串
  * @access  public
  */
        function GetErrorString() {
                $sError = mysql_errno( $this->_rDBLink ) . ':' . mysql_error( $this->_rDBLink ) ;
                return $sError ;
        }

  /**
  * 构建并执行查询
  *
        * @param string $sTable 表
        * @param string $sField 字段
        * @param string $sWhere 条件
        * @param string $sOrder 排序
        * @param int $iCountPerPage 每页记录数
        * @param int $iPage 当前
        * @param boolean $bCountOnly 是否只是查询数量
  * @return mixed false或者查询结果数组
  * @access  public
  */
  function Query( $sTable, $sField = '*', $sWhere = '', $sOrder = '', $iCountPerPage = -1, $iPage = 1, $bCountOnly = false ) {
                //where
                if ( strlen( $sWhere ) > 0 ) {
                        $sWhere = " WHERE $sWhere " ;
                }
      
                //fields
                if ( $bCountOnly ) {
                        $sField = 'COUNT(*) AS COUNT' ;
                }
               
                //order
                if ( strlen( $sOrder ) > 0 ) {
                        $sOrder = " ORDER BY $sOrder " ;
               
                }

                //limit
                $sLimit = '' ;
                if ( $iCountPerPage > 0 ) {
                        if ( $iPage <= 0 ) {
                                $iPage = 1 ;
                        }
                        $iStart = ( $iPage - 1 ) * $iCountPerPage ;
                        $sLimit = " LIMIT $iStart,$iCountPerPage " ;
                }
   
                //query
                $sQuery = "SELECT $sField FROM $sTable" . $sWhere . $sOrder . $sLimit ;
                $rResult = $this->_MysqlQuery( $sQuery, $this->_rDBLink ) ;
                if ( $rResult === false ) {
                        return false ;
                }
               
                if ( $bCountOnly === false ) {
                        return new CMysqlResult( $rResult ) ;
                }
               
                //return
                if ( ( $aRow = mysql_fetch_assoc( $rResult ) ) === false ) {
                        return false ;
                }
                return $aRow['COUNT'] ;
  }
       
  /**
  * 构建并执行查询(仅数量)
  *
        * @param string $sTable 表
        * @param string $sWhere 条件
        * @param int $iCountPerPage 每页记录数
        * @param int $iPage 当前
  * @return mixed false或者查询结果数组
  * @access  public
  */
        function QueryCount( $sTable, $sWhere, $iCountPerPage = -1, $iPage = 1 ) {
                return $this->Query( $sTable, '', $sWhere, '', $iCountPerPage, $iPage, true ) ;
        }
       
  /**
  * 查询sql
  *
        * @param string $sSql sql
  * @return mixed false或者查询结果数组
  * @access  public
  */
        function QuerySql( $sSql ) {
                $rResult = $this->_MysqlQuery( $sSql, $this->_rDBLink ) ;
                if ( $rResult === false ) {
                        return false ;
                }
                return new CMysqlResult( $rResult ) ;
        }
       
  /**
  * 查询sql
  *
        * @param string $sSql sql
        * @param resource $rDBLink 数据库链接
  * @return mixed false或者查询结果数组
  * @access  private
  */
        function _MysqlQuery( $sSql, $rDBLink ) {
                $sContent = " [SQL] $sSql" ;
                $this->_SaveToLog( $sContent ) ;  

                $rResult = mysql_query( $sSql, $this->_rDBLink ) ;
                if ( $rResult === false ) {
                        $sContent = ( " [ERR] " . $this->GetErrorString() ) ;
                        $this->_SaveToLog( $sContent ) ;
                }

                return $rResult ;
        }

  /**
  * 保存日志到文件
  *
        * @param string $sContent 日志内容
  * @return void
  * @access  private
  */       
        function _SaveToLog( $sContent ) {
                $sContent = "[" . date('Y-m-d H:i:s') . "]" . $sContent . "\n" ;
                $rFile = fopen( $this->_sLogDir . 'sql.log', 'a' ) ;
                if ( $rFile !== false ) {
                        $iRetFile = fwrite( $rFile, $sContent ) ;
                        fclose( $rFile );
                }
        }

  /**
  * 执行一个更新
  *
        * @param string $sSql sql
  * @return mixed 失败时返回false,成功时返回影响行数,假如有数据行返回CMysqlResult对象
  * @access  public
  */       
        function Execute( $sSql ) {
                $sSql = trim( $sSql ) ;
                if ( substr( $sSql, -1 ) != ';' ) {
                        $sSql .= ';' ;
                }
                if ( $this->_bCacheExecute ) {
                        $this->_sSql .= $sSql ;
                        return true ;
                }
                if ( ( $rResult = $this->_MysqlQuery( $sSql, $this->_rDBLink ) ) === false ) {
                        return false ;
                }
                else if ( $rResult === true ) {
                        return mysql_affected_rows( $this->_rDBLink ) ;
                }
                else {
                        return new CMysqlResult( $rResult ) ;
                }
        }

        function SetCacheExecute( $bValue ) {
                $this->_bCacheExecute = $bValue ;
        }

        function ExecuteCache() {
                $bCacheExecute = $this->_bCacheExecute ;
                $this->_bCacheExecute = true ;
                $this->Execute( $this->_sSql ) ;
                $this->_bCacheExecute = $bCacheExecute ;
        }

        function BeginTransaction() {
                return $this->Execute( 'START TRANSACTION;' ) ;
        }

        function CommitTransaction() {
                return $this->Execute( 'COMMIT;' ) ;
        }

        function RollbackTransaction() {
                return $this->Execute( 'ROLLBACK;' ) ;
        }

        function GetInsertID( ) {
                return mysql_insert_id( $this->_rDBLink ) ;
        }
}
?>
回复

使用道具 举报

 楼主| 发表于 2006-7-15 07:04 | 显示全部楼层
<?php
/**
* mysql&#1589;&#317;
*
* @author   Toney Zeng<[email protected]>
* @version  v0.1
* @access  public
* @package  MVC
*/
class CMysqlResult
{
        var $_rResult ;

  /**
  * &#52911;
  *
  * @param string $sName &#291;
  * @param CApplication $oApplication &#291;&#1254;&#246;
  * @return void
  * @access  public
  */
  function CMysqlResult( $rResult )  {
                $this->_rResult = $rResult ;
  }

        function FetchRow( ) {
                return mysql_fetch_assoc( $this->_rResult ) ;
        }
        function RowCount() {
                return mysql_num_rows( $this->_rResult ) ;
        }
}
?>
回复

使用道具 举报

 楼主| 发表于 2006-7-15 07:20 | 显示全部楼层
这个是比较独立的关键词词库操控类,结构比较复杂,会用就好了,还没写注释呢。发现人的精力真是TM的有限啊。

用法
        require_once( 'CKeyword .class.php' ) ;
        $sContent = file_get_contents( 'general.dct' ) ;  //假如词库文件已经准备好了足够多的关键词
        $oKeyword = new CKeyword( $sContent ) ;       

        $sWord = 要分析的文章内容。

        $aKeywordArray = $oKeyword->QueryKeywords( $sWord, KEY_TYPE_ID ) ;
        print_r( $aKeywordArray ) ;
        $aKeywordArray = $oKeyword->QueryKeywords( $sWord, KEY_TYPE_STRING ) ;
        print_r( $aKeywordArray ) ;       

生成词库用法
        require_once( 'CKeyword .class.php' ) ;
        $oKeyword = new CKeyword( ) ;       
        p( 'general.word', $oKeyword ) ;        //其中general.word是用回车换行分开的一个个词组,一般去网上搜索拼音加加词库然后去掉拼音(用正则去掉),之后就得到20万的词库了。

function p( $sFile, &$oKeyword ) {
        $handle = @fopen( $sFile, "r");
        if ( $handle ) {
            while ( !feof( $handle ) ) {
                                $buffer = fgets( $handle );
                                $buffer = substr( $buffer, 0, strlen( $buffer ) - 1 ) ;
                                $sGBChar = str_replace( array( "\r","\n","\t" ), "", $buffer ) ;
                                $sUnicodeChar = iconv( 'gbk', 'UNICODEBIG', $sGBChar ) ;
                                if ( strlen( $sUnicodeChar ) < 2 ) {
                                        echo "bad keyword: $sGBChar\r\n" ;
                                }
                                $oKeyword->AddKeyword( $sUnicodeChar ) ;
           }
           fclose( $handle );
        }
        else {
                die('error') ;
        }
}


下面是关键词操控对象类。部分识词相关方法我删除了,因为还没测试好。听说有一个开源的搜索引擎的识词算法挺好的。不知道有没有了解的人回复一下?它的算法如何?我没时间去阅读了。

<?php
        //注意此文件权利全部属于本人,如需使用请保留作者署名以及此声明。        

        define( 'WORD_MID', 0 ) ;
        define( 'WORD_LAST', 1 ) ;
       
        define( 'TOTAL_COUNT_LENGTH', 4 ) ;
        define( 'TOTAL_COUNT_START_ADDR', 0 ) ;
       
        define( 'INDEX_LENGTH', 5 ) ;       
        define( 'INDEX_COUNT', 65535 ) ;       
        define( 'INDEX_START_ADDR', TOTAL_COUNT_START + TOTAL_COUNT_LENGTH ) ;
       
        define( 'ITEM_LENGTH', 11 ) ;
        define( 'ITEM_START_ADDR', INDEX_LENGTH * INDEX_COUNT + TOTAL_COUNT_LENGTH + TOTAL_COUNT_START ) ;
        define( 'ITEM_NULL_ADDR', 0 ) ;
       
       
        define( 'KEY_TYPE_STRING', 0 ) ;
        define( 'KEY_TYPE_ID', 1 ) ;

/**
* 关键词操控对象
* 关键词操控,添加关键词,分词,统计识词(尚未),智能识词(尚未),词库输出,关键词删除(尚未)
*  
* @author Toney Zeng<[email protected]>
* @version  V1.4
* @access  public
* @package  HOTEL
*/
class CKeyword {
       
        var $_sDictContent ;
       
        function CKeyword( $sDictContent = '' ) {
                if ( strlen( $sDictContent ) <= ITEM_START_ADDR ) {
                        $sBlank = pack( 'NC', 0, 0 ) ;
                        $this->_sDictContent = str_repeat( $sBlank, INDEX_COUNT ) ;
                        $sBlank = pack( 'N', 0 ) ;
                        $this->_sDictContent .= $sBlank ;
                }
                else {
                        $this->_sDictContent = $sDictContent ;
                }
        }
       
        function QueryKeywords( $sContent, $iKeyType = KEY_TYPE_STRING ) {
                $iContentLength = strlen( $sContent ) ;
                $aKeywordArray = array() ;
                for( $i = 0; $this->_GetKeywordArray( $sContent, $iContentLength, $i, $aKeywordArray, $iKeyType ); $i++ ) {}
                //print_r( $aKeywordArray ) ;
                return $aKeywordArray ;
        }
       
        function _GetKeywordArray( &$sContent, $iContentLength, $iOffset, &$aKeywordArray,$iKeyType  ) {
                if ( $iOffset == $iContentLength ) {
                        return false ;
                }
                $sWordTmp = '' ;
               
                $sUnicodeChar = $this->_PickUnicodeChar( $sContent, $iOffset ) ;
                $iUnicodeChar = $this->_UnicodeChar2Int( $sUnicodeChar ) ;
                if ( $iKeyType == KEY_TYPE_STRING ) {
                        $sWordTmp .= $sUnicodeChar ;
                }
                $iIndex = $iUnicodeChar ;
                $iKeywordID = $this->_GetIndex( $iIndex, $iAddr, $iFlag ) ;
                if ( $iFlag == WORD_LAST ) {
                        if ( $iKeyType == KEY_TYPE_STRING ) {
                                $mKey = $this->_OutputKeyword( $sWordTmp ) ;
                        }
                        else {
                                $mKey = $iKeywordID ;
                        }
                        $this->_AddKeywordCountToArray( $mKey, $aKeywordArray ) ;
                }               
                if ( $iAddr < ITEM_START_ADDR  ) {
                        return true ;
                }
               
                $iAddrWork = $iAddr ;
                for( $i = $iOffset + 1; $i < $iContentLength; $i++ ) {
                        $sUnicodeChar = $this->_PickUnicodeChar( $sContent, $i ) ;
                        $iUnicodeChar = $this->_UnicodeChar2Int( $sUnicodeChar ) ;
                        if ( $iKeyType == KEY_TYPE_STRING ) {
                                $sWordTmp .= $sUnicodeChar ;
                        }
                        $iKeywordID = $this->_FindItem( $iAddrWork, $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub, $iLastAddr ) ;
                        if ( $iKeywordID === false ) {
                                return true ;
                        }
                        else {
                                if ( $iFlag == WORD_LAST ) {
                                        if ( $iKeyType == KEY_TYPE_STRING ) {
                                                $mKey = $this->_OutputKeyword( $sWordTmp ) ;
                                        }
                                        else {
                                                $mKey = $iKeywordID ;
                                        }
                                        $this->_AddKeywordCountToArray( $mKey, $aKeywordArray ) ;
                                }
                        }
                        $iAddrWork = $iAddrSub ;
                       
                }
                return true ;
        }
       
        function _AddKeywordCountToArray( $mKey, &$aKeywordArray ) {
                if ( array_key_exists( $mKey, $aKeywordArray ) ) {
                        $aKeywordArray[ $mKey ] ++ ;
                }
                else {
                        $aKeywordArray[ $mKey ] = 1 ;
                }               
        }
       
        function AddKeyword( $sUnicodeWord ) {
                if ( $this->_AddWord( $sUnicodeWord ) ) {                       
                        $this->_GetTotalCount( $iTotalCount ) ;
                        $iTotalCount++ ;               
                        $this->_SetTotalCount( $iTotalCount ) ;               
                }
        }
       
        function _AddWord( $sUnicodeWord ) {
                $iWordLength = (int)( strlen( $sUnicodeWord ) / 2 ) ;
               
                if ( $iWordLength == 0 ) {
                        return false ;
                }
               
                $iUnicodeChar = $this->_UnicodeChar2Int( $this->_PickUnicodeChar( $sUnicodeWord, 0 ) ) ;
                $iIndex = $iUnicodeChar ;
                $this->_GetIndex( $iIndex, $iAddr, $iFlag ) ;
                $iAddrRoot = $iAddr ;
                if ( $iWordLength == 1 ) {
                        if ( $iFlag != WORD_LAST ) {
                                $this->_SetIndex( $iIndex, $iAddr, WORD_LAST ) ;
                                return true ;
                        }
                        return false ;
                }

                $iUnicodeChar = $this->_UnicodeChar2Int( $this->_PickUnicodeChar( $sUnicodeWord, 1 ) ) ;
                if ( $iAddrRoot < ITEM_START_ADDR ) {
                        if ( $iWordLength == 2 ) {
                                $this->_AddFirstItem( $iIndex, $iUnicodeChar, WORD_LAST, ITEM_NULL_ADDR, ITEM_NULL_ADDR ) ;
                                return true ;
                        }
                        else {
                                $iAddr = $this->_AddFirstItem( $iIndex, $iUnicodeChar, WORD_MID, ITEM_NULL_ADDR, ITEM_NULL_ADDR ) ;
                        }
                        $iAddrParent = $iAddr ;
                        $iAddrWork = ITEM_NULL_ADDR ;
                        $iStart = 2 ;
                }
                else {
                        $iAddrParent = ITEM_NULL_ADDR ;
                        $iAddrWork = $iAddrRoot ;
                        $iStart = 1 ;
                }
               
                $iStop = $iWordLength - 1 ;
                for( $i = $iStart; $i <= $iStop; $i++ ) {
                        $iUnicodeChar = $this->_UnicodeChar2Int( $this->_PickUnicodeChar( $sUnicodeWord, $i ) ) ;
                       
                        if ( $i == $iStop ) {
                                $iFlagNew = WORD_LAST ;
                        }
                        else {
                                $iFlagNew = WORD_MID ;
                        }

                        if ( $iAddrWork < ITEM_START_ADDR ) {
                                $iAddr = $this->_AddSubItem( $iAddrParent, $iUnicodeChar, $iFlagNew, ITEM_NULL_ADDR, ITEM_NULL_ADDR ) ;
                                $iAddrParent = $iAddr ;
                                $iAddrWork = ITEM_NULL_ADDR ;
                                continue ;
                        }
                       
                        if ( $iAddr = $this->_FindItem( $iAddrWork, $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub, $iLastAddr ) ) {
                                if ( $iFlagNew == WORD_LAST ) {
                                        if ( $iFlag != WORD_LAST ) {
                                                $this->_SetItem( $iAddr, $iUnicodeChar, WORD_LAST, $iAddrNext, $iAddrSub ) ;
                                                return true ;
                                        }
                                        else {
                                                return false ;
                                        }
                                }
                                $iAddrParent = $iAddr ;
                                $iAddrWork = $iAddrSub ;
                        }
                        else {
                                $iAddr = $this->_AddNextItem( $iLastAddr, $iUnicodeChar, $iFlagNew, ITEM_NULL_ADDR, ITEM_NULL_ADDR ) ;
                                $iAddrParent = $iAddr ;
                                $iAddrWork = ITEM_NULL_ADDR ;
                        }
                }
                return true ;
        }
回复

使用道具 举报

 楼主| 发表于 2006-7-15 07:20 | 显示全部楼层
function _FindItem( $iAddrEntry, $iUnicodeCharToFind, &$iFlag, &$iAddrNext, &$iAddrSub, &$iLastAddr ) {
                $iAddrWork = $iAddrEntry ;
                while( $this->_GetItem( $iAddrWork, $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub ) ) {
                        if ( $iUnicodeCharToFind == $iUnicodeChar ) {
                                return $iAddrWork ;
                        }
                        $iLastAddr = $iAddrWork ;
                        $iAddrWork = $iAddrNext ;
                }
                return false ;
        }

        function _GetItem( $iDictContentPos, &$iUnicodeChar, &$iFlag, &$iAddrNext, &$iAddrSub ) {
                if ( $iDictContentPos < ITEM_START_ADDR || ($iDictContentPos + ITEM_LENGTH) > strlen( $this->_sDictContent ) ) {
                        return false ;
                }
                $sBString = substr( $this->_sDictContent, $iDictContentPos, ITEM_LENGTH ) ;
                $this->_BString2Item( $sBString, $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub ) ;
                return $iDictContentPos ;
        }

        function _SetItem( $iDictContentPos, $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub ) {
                $sBString = $this->_Item2BString( $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub ) ;
                $this->_StringReplace( $this->_sDictContent, $sBString, $iDictContentPos ) ;
                return true ;
        }
       
        function _AddFirstItem( $iIndex, $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub ) {
                $iAddrNew = $this->_AddItem( $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub ) ;
                if ( $this->_GetIndex( $iIndex, $iAddr, $iFlag ) ) {
                        $this->_SetIndex( $iIndex, $iAddrNew, $iFlag ) ;
                }
                else {
                        return false ;
                }
                return $iAddrNew ;
        }

        function _AddSubItem( $iDictContentPos, $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub ) {
                $iAddrNew = $this->_AddItem( $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub ) ;
                if ( $this->_GetItem( $iDictContentPos, $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub ) ) {
                        $this->_SetItem( $iDictContentPos, $iUnicodeChar, $iFlag, $iAddrNext, $iAddrNew ) ;
                }
                else {
                        return false ;
                }
                return $iAddrNew ;
        }

        function _AddItem( $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub ) {
                $sBString = $this->_Item2BString( $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub ) ;
                $iAddrNew = strlen( $this->_sDictContent ) ;
                $this->_sDictContent .= $sBString ;
                return $iAddrNew ;
        }

        function _AddNextItem( $iDictContentPos, $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub ) {
                $iAddrNew = $this->_AddItem( $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub ) ;
                if ( $this->_GetItem( $iDictContentPos, $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub ) ) {
                        $this->_SetItem( $iDictContentPos, $iUnicodeChar, $iFlag, $iAddrNew, $iAddrSub ) ;
                }
                else {
                        return false ;
                }
                return $iAddrNew ;
        }
       

        function _GetTotalCount( &$iTotalCount ) {
                $sBString = substr( $this->_sDictContent, TOTAL_COUNT_START_ADDR, TOTAL_COUNT_LENGTH ) ;
                $this->_BString2TotalCount( $sBString, $iTotalCount ) ;
                return true ;
        }

        function _SetTotalCount( $iTotalCount ) {
                $sBString = $this->_TotalCount2BString( $iTotalCount ) ;
                $this->_StringReplace( $this->_sDictContent, $sBString, TOTAL_COUNT_START_ADDR ) ;
                return true ;
        }

        function _GetIndex( $iIndex, &$iAddr, &$iFlag  ) {
                $iIndexAddr = (INDEX_LENGTH * $iIndex + TOTAL_COUNT_LENGTH) ;
                $sBString = substr( $this->_sDictContent, $iIndexAddr, INDEX_LENGTH ) ;
                $this->_BString2Index( $sBString, $iAddr, $iFlag ) ;
                return $iIndexAddr ;
        }

        function _SetIndex( $iIndex, $iAddr, $iFlag ) {
                $sBString = $this->_Index2BString( $iAddr, $iFlag ) ;
                $this->_StringReplace( $this->_sDictContent, $sBString, INDEX_LENGTH * $iIndex + TOTAL_COUNT_LENGTH ) ;
                return true ;
        }
       
        function _StringReplace ( &$sContent, $sReplace, $iOffset ) {
                $iReplaceLen = strlen( $sReplace ) ;
                if ( $iOffset < 0 ) {
                        die( 'error while iOffset=' . $iOffset );
                }
                if ( ( $iOffset + $iReplaceLen ) > strlen( $sContent )  ) {
                        die( 'error while $iReplaceLen too big ' . $iReplaceLen );
                }
                for( $i = $iReplaceLen - 1; $i >= 0; $i -- ) {
                        $sContent{ $iOffset + $i } = $sReplace{ $i } ;
                }
        }
       
        function _PickUnicodeChar(        $sUnicodeWord, $iIndex        ) {
                return substr( $sUnicodeWord, $iIndex << 1, 2 ) ;
        }

        function _Item2BString( $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub ) {
                return pack( 'nCNN', $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub ) ;
        }

        function _BString2Item( $sBString, &$iUnicodeChar, &$iFlag, &$iAddrNext, &$iAddrSub ) {
                $a = unpack( 'n1n/C1C/N1N1/N1N2', $sBString ) ;
                $iUnicodeChar = $a['n'] ;
                $iFlag = $a['C'] ;
                $iAddrNext = $a['N1'] ;
                $iAddrSub = $a['N2'] ;
        }
       
        function _TotalCount2BString( $iTotalCount ) {
                return pack( 'N', $iTotalCount ) ;
        }

        function _BString2TotalCount( $sBString, &$iTotalCount ) {
                $a = unpack( 'N1N', $sBString ) ;
                $iTotalCount = $a[ 'N' ] ;
        }
       
        function _Index2BString( $iAddr, $iFlag ) {
                return pack( 'NC', $iAddr, $iFlag ) ;
        }

        function _BString2Index( $sBString, &$iAddr, &$iFlag ) {
                $a = unpack( 'N1N/C1C', $sBString ) ;
                $iAddr = $a[ 'N' ] ;
                $iFlag = $a[ 'C' ] ;
        }
       
        function _UnicodeChar2Int( $sUnicodeChar ) {
                $iCode = 0 ;
                $iByte1 = ord( $sUnicodeChar{ 0 } ) ;
                $iByte2 = ord( $sUnicodeChar{ 1 } ) ;
                if ( $iByte1 == 0 && $iByte2 > 64 && $iByte2 < 91 ) {
                        $iByte2 += 32 ;
                }
                $iCode = ( $iByte1 << 8 ) + $iByte2 ;
                return $iCode ;
        }

        function _Int2UnicodeChar( $iUnicodeInt ) {
                $iByte2 = $iUnicodeInt & 511 ;
                $iByte1 = $iUnicodeInt >> 8 ;
                return chr( $iByte1 ) . chr( $iByte2 ) ;
        }

        function Output( $bEchoItem = false, $bEchoTotalCount = true ) {
                $sOutput = '' ;
                $iTotalCount = 0 ;
                for( $i = 0; $i<INDEX_COUNT; $i++ ) {
                        $this->_GetIndex( $i, $iAddr, $iFlag ) ;
                        if ( $iAddr >= ITEM_START_ADDR ) {
                                $sOutput .=  $this->_OutputWordsFromIndexAddr( $i, $iAddr, $iFlag, $bEchoItem, $iTotalCount ) ;
                        }
                }
                if ( $bEchoTotalCount ) {
                        $this->_GetTotalCount( $iRecordTotalCount ) ;
                        $sOutput .=  "The total count recorded is " . $iRecordTotalCount . " and the actual total count is " . $iTotalCount . ".\r\n" ;
                }
                return $sOutput ;
        }

        function _OutputWordsFromIndexAddr( $iIndex, $iAddr, $iFlag, $bEchoItem, &$iTotalCount ) {
                $sOutput = '' ;
                $sUnicodeChar = $this->_Int2UnicodeChar( $iIndex ) ;
                if ( $iFlag == WORD_LAST ) {
                        if ( $bEchoItem ) {
                                        $sOutput .= $this->_OutputKeyword( $sUnicodeChar ) ;
                        }
                        $iTotalCount ++ ;
                }
                $sOutput .= $this->_OutputWordsRegu( $sUnicodeChar, $iAddr, $bEchoItem, $iTotalCount ) ;
                return $sOutput ;
        }

        function _OutputWordsRegu( $sBaseChars, $iAddr, $bEchoItem, &$iTotalCount ) {
                $sOutput = '' ;
                while( $this->_GetItem( $iAddr, $iUnicodeChar, $iFlag, $iAddrNext, $iAddrSub ) ) {
                        $sUnicodeChar = $this->_Int2UnicodeChar( $iUnicodeChar ) ;
                        if ( $iFlag == WORD_LAST ) {
                                if ( $bEchoItem ) {
                                        $sOutput .= $this->_OutputKeyword( $sBaseChars . $sUnicodeChar ) ;
                                }
                                $iTotalCount ++ ;
                        }
                        $sOutput .= $this->_OutputWordsRegu( $sBaseChars . $sUnicodeChar, $iAddrSub, $bEchoItem, $iTotalCount ) ;
                        $iAddr = $iAddrNext ;
                }
                return $sOutput ;
        }
       
        function _OutputKeyword( $sKeyword ) {
                $sKeyword = iconv( 'UNICODEBIG', 'gbk', $sKeyword ) ;
                return $sKeyword ;               
        }
}
        function file_put_contents( $filename, $content )
        {
                $filehandle = fopen( $filename, 'w' ) ;
                fwrite( $filehandle, $content ) ;
                fclose( $filehandle ) ;
        }

?>
回复

使用道具 举报

发表于 2006-7-15 11:23 | 显示全部楼层
师兄文章好长.我憋住忍住终于看完大半(后面代码部分没看,也看不懂)...

我看杂志或看网上文章的时候偶尔会听说现在企业应用软件开发分三个阵营:j2ee,.NET和面向对象的PHP(ruby那些动态语言暂时不成气候).对于j2ee和.NET我很理解,因为我本身就是j2ee阵营的一个狂热参与者,而.NET做为"主要竞争者"我也时刻关注着.但PHP为什么算一个阵营?我一直都不理解,因为在我的认识中(我好歹算"用过"PHP),PHP无非就是一个类似ASP的语言,它能做的就是在HTML标签中嵌入自己的程序逻辑.用PHP,只能构建功能丰富的网站,而难以构建业务复杂的企业应用软件.

我不知道师兄文章说的是不是面向对象的PHP,不过确实让我很吃惊,PHP也能严格的分层.呵呵.师兄扫盲的时候把我扫了.
回复

使用道具 举报

 楼主| 发表于 2006-7-15 12:15 | 显示全部楼层
php做企业运用,framework是第一步。
事在人为。
回复

使用道具 举报

发表于 2006-7-15 15:15 | 显示全部楼层
师兄,不得不服了你。。。

希望更多地支持本版。做过php,但对底层的不了解,只知基本的应用,抽时间会再次拜读本文。谢谢
回复

使用道具 举报

 楼主| 发表于 2006-7-16 02:02 | 显示全部楼层
我才是服了你,你们才是工大的骄傲。
说实在话,比我们强比我们厉害的人多的是,但他们不愿意与别人分享他自己的成果,更不用说要他写循循善诱的初学者文章来带新人了。
所以,你们才是伟大的。我也从你们的文章学到了东西。期待你们把有用的设计模式的研究和应用心得写成文章。
回复

使用道具 举报

发表于 2006-7-16 09:59 | 显示全部楼层
原帖由 sasadong 于 2006-7-16 02:02 发表
我才是服了你,你们才是工大的骄傲。
说实在话,比我们强比我们厉害的人多的是,但他们不愿意与别人分享他自己的成果,更不用说要他写循循善诱的初学者文章来带新人了。
所以,你们才是伟大的。我也从你们的文章 ...


师兄竟用到伟大一词,呵呵!
Hjack和Wool对我们新手确实是循循善诱!我想以他们为榜样,可惜只能写出 HelloWorld 这样的小文章。希望师兄和像师兄这样的人多多参与本版讨论,指导新人。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 加入后院

本版积分规则

QQ|Archiver|手机版|小黑屋|广告业务Q|工大后院 ( 粤ICP备10013660号 )

GMT+8, 2024-4-30 22:35

Powered by Discuz! X3.5

Copyright © 2001-2024 Tencent Cloud.

快速回复 返回顶部 返回列表