我从Photoshop到写HTML,JS,CSS到PHP一路走来,写PHP已经7个年头,靠它养活了一家老小,谈谈自己对好程序的一些理解吧。
首先一个问题就是什么样的PHP程序是一个强悍的程序?
我的答案是在你的程序里看不到SQL语句。在我的项目里几乎看不到什么SQL语句,所有的SQL语句都是通过程序自动构造,数据库和对象对应,完全ORM实现,保证了业务逻辑完整。
如何做到?
首先,要有一个适合的而且熟悉的框架,推荐使用一个轻量级的框架自己容易驾驭,然后根据自己的项目扩展些底层类库,,团队开发最好自己开发一个适合自己的ORM系统或者至少要实现DAO,我用的ORM就是自己设计的,为什么不用开源的PHP ORM后面说。
然后,整个项目只有20%的代码是最核心和复杂的。在项目中,这20%的代码基本上都是我来开发维护,它涉及到了数据库的设计、ORM对象的抽象设计等等,这些弄好,都可以放心的让那些只有1年php经验的新手来做相应的业务开发。
做好底层。这确实不是简单的事情,要对项目有足够的前瞻性,更要确保底层的统一和稳定,需要有很多项目经验。没有项目经验怎么办?可以多学习多看别人的项目设计原理,特别是自己项目中可以看到源代码的核心部分,去弄明白它。
选好工具。不必太在意它的速度和功能,但是至少完整的代码提示和函数跟踪是需要的,最重要的就是你要熟悉它,我用的就是zend studio
充分了解语言本身的强项和弱项。一个大型网站绝对不是就靠PHP一项语言就可以开发出来的,用PHP做它擅长的东西,其它的东西就交给其它语言吧。
下面我就对最重要的底层数据数据层面的在ORM上的实现来介绍下:
先设计好数据库的表结构,定义好ORM或DAO对象。
DAO是什么?英文是Data Access Objects,可以自己google下
简单的说DAO就只这样的东西:假如你有个表叫news,字段有 id,title,create_time 然后这个表对应的DAO就是类似这样的东西:
- class dao_news{ public $id;
- public $title;
- public $create_time;}
复制代码
然后可以:
$test = new dao_news();
$test->title = 'test';
$test->create_time = time();
当然,这只是举例,实际中不可能这么简单,比如可实现更新,插入,删除,数据格式校验等。
然后说下QueryBuilder,通过它可以构造出各种数据库的语句,比如我写了MySQL和MongoDB的解析驱动。这样做的好处是:PHP程序可以尽量不用考虑数据库的差异,可以跨数据库移植,可以被ORM调用构造SQL语句,然后就是安全,有效的控制了SQL注入。
例如:查询news表里id=1的数据,通过QueryBuilder就可以这样简单的实现,不管是什么数据库都是这样写
$db = new Database();
$data = $db->from('news')->where('id',1)->get()->as_array();
DAO实现了数据表单行数据和PHP对象的对应,操作对象然后调用保存函数就可以实现对数据库的更新,如果你的表结构比较复杂,然后可以和我一样把整个update()和insert()等方法封装在一起,这样就不用担心外部的修改会出错,一切皆在你的掌控。
在我的ORM中定义了3种类,分别是Finder,Result和Data。
Finder的作用就跟传统的MVC中的Model一样实现对数据的查询模块;
Result就是Finder的查询的返回结果集;
Data类似Dao,每个Data就对应了数据库中的一行数据;
还是上面那个news表,我可这样设置
// 继承到一个数据库基层ORM Finder 对象上
class ORM_News_Finder extends ORM_Finder_DB
{
// 设置这个对象用到是news表
protected $tablename = 'news';/**
* 根据ID获取一个对象
* @return ORM_News_Data
*/
public function get_by_id($id){
return $this->where('id',$id)->find()->current();
}
}// 对应返回数据的对象
class ORM_News_Result extends ORM_Result
{}
// 对应单行数据的对象
class ORM_News_Data extends ORM
{
public $id;public $title;
public $create_time;
public function update()
{
//...处理更新逻辑
}public function insert()
{
//...处理插入逻辑
}
}
这样这个ORM定义好后,假如我们要更新一条数据,可以这样:
$news_finder = new ORM_News_Finder();
// 获取ID=1的这个对象
$news = $news_finder->get_by_id(1);// 输出旧的标题
echo $news->title;// 修改标题
$news->title = 'new title';// 更新到数据库
$news->update();
这样你会发现,整个过程没有任何SQL语句,而这其实已经执行了2条SQL:
SELECT FROM `news` WHERE `id` = ‘1’;
UPDATE `news` SET `title` = 'new title' WHERE `id` = '1';
通过对这种数据操作的实现,我们可以轻而易举的驾驭复杂的数据关系的各种操作。对ORM的实现和QueryBuilder的支持,不但可以减少对SQL语句的依赖,更可以让程序变得更加优雅和更加强大,就像给程序插上了翅膀。。
我在实际项目使用中会对那些主要的表的ORM进行了充分的封装,实现整个数据库内部的数据层到对象的关系转换和逻辑的封装。封装的不仅仅是数据的操作,更重要的是各个表之间业务的逻辑关系。比如当程序更新某个表数据时会同时去操作关联表数据,而不需要在业务逻辑程序里去写。
另外底层还需提供了对数据的校验,数据的转换,关联表的操作管理,缓存处理等等。
例如数据校验,news表的create_time是int(10)的,假设程序在接受POST数据并设置时没有判断,误将create_time设置成了一个字符串,那么在ORM底层执行update()更新操作的时候会对数据进行校验,发现这个类型错了,则直接抛出异常,避免错误更新。。。
所有核心的封装做好后,接下来你只需要维护好这些底层关系,可放心大胆的把业务逻辑交给经验不足的新手去开发了。
PS,很多人说ORM性能不行,这里解释下为什么我前面为什么不提倡用开源的ORM,因为它们太通用,不具备项目或者你自己的特性需求,另外一个问题就是强大的ORM其实是很难驾驭和控制的,并且性能也不会好到哪,所以尽量自己开发实现优化或用我的框架。
强悍的程序不仅仅是对数据层面的操作,而是对开发调试,环境配置有更上层的支持。
因为大量SQL语句都是通过程序去构造,在开发时会面临一个问题就是一个页面执行完不知道它都执行了哪些SQL。所以这时就需要在QueryBuilder后可以把相应的SQL语句的执行情况汇总输出出来,包括执行时间,具体SQL语句,Explain,连接的是哪个数据库(因为可能有主从嘛)等等。
还有就是缓存的支持,强悍的程序肯定是离不开数据缓存功能,怎样缓存,怎样方便的缓存,怎样有效的管理缓存都是值得去探讨的话题,我在项目里会根据项目的具体情况做一个基于项目比较通用的缓存解决方案,80%都用它,然后再针对几个重点模块单独做缓存处理逻辑,这样就可以让整个系统健步如飞。缓存媒介无非就是memcache,文件,redis等。。
可移植性和简单部署
很多项目基本上不具备可移植性,部署起来也是相对的复杂。。。在我的项目中,基本上就是典型的LAMP架构,当然会对实际生成环境做一些优化,但是绝对是不影响移植部署的。。。甚至可以把数据库从mysql迁移到mongodb,这得益于QueryBuilder使得整个项目查询、更新、插入保持一致。缓存也可以从memcache更换到redis或文件等,只要简单的改下配置,完全不用修改代码,因为缓存功能和数据库功能采用了类似的驱动封装模式,把差异留在底层,在业务层面的代码都是相同的。