CodeIgniter3

 

TL;DR

CodeIgniter3是一个相当轻量、简便的并且上手难度低的PHP应用开发框架。在CodeIgnitor2时代曾经接触并开发了一些项目。目前最新版本是3.1.5。优点个人认为有:

  • 轻量
  • 对MySQL查询有较为友好的代码编写方式
  • 功能扩展较为简便
  • 可以支持较低版本的PHP(5.4.8+)
  • 可以不使用模板引擎

同时,个人也认为以下功能还可以有所变化:

  • 内置日志功能不够强大
  • 内置读写分离
  • RESTful API开发支持

文章的剩余内容将会针对以上的各个方面详细说明。

优点

轻量

文件数量

首先通过cloc统计以下代码数量:

仅仅198个php文件,对于一个拥有完整的数据库查询支持,表单验证,Session支持,XSS/CSRF防护的框架来说,并不算臃肿。

文件数量一定程度上反映了层次划分的粒度,如果文件数量过多,带来的首先是源码阅读的难度和改造的难度。

基本业务功能实现

对于一个号称轻量的框架来说,实现一个数据库的CRUD操作的Hello World的代码量应该最能反映问题。

CI3实现这样的一个Controller,需要多少代码量呢?答案因人而异。但是步骤上却是相当简单的。考虑如下数据表:

解压缩CI3之后,在application中的config目录下的database.php中配置好数据库配置:

application中的config目录下的routes.php中配置好路由规则:

只需要编写少许几行代码即可完成上述操作。

如果在Model层做一些封装,相信对于这类基本的CRUD操作的编写会更加的便捷。

友好的MySQL查询代码编写方式

日常开发中,个人更倾向于使用纯SQL进行数据库的查询工作,但是纯SQL的可读性不一定比精心设计的查询工具类更好,同时实用查询记录构造工具,可以规避一些错误的SQL编写方式(如不转义用户输入,直接拼接参数到SQL语句之中)。

如手册中的例子:

CI3的查询构造器在调用get()方法前可以声明各个查询条件的组合。通过链式方法,让代码拥有的了一定的可读性。

CI3数据库查询过程简述(PDO)

虽然在上述例子中使用的是mysqli作为示例,但是PDO在工作中更为常用,简要的描述一下CI3使用PDO作为driver进行一次查询过程的流程。

  1. CI3中,在database.php中配置了数据库之后,通过load对象中的database()方法加载数据库对象。
  2. database()方法实际上会将数据库配置传入DB()这一方法,如果传入参数为空,则会查找应用目录下的database.php配置文件并引入。如果配置了DSN或者配置组的名字,则会解析或者查找配置文件中是否存在对应的配置组。所以,假设要编写一个新的DB Driver,需要一些特殊的配置,可以在项目的database.php配置中增加一个特定的配置组,增加自己独有的字段。
  3. 根据配置的dbdriver字段,到框架目录下查找是否存在对应的Driver,之后实例化并连接。由于PDO可以支持多种数据库,所以对于PDO来说,还会根据DSN查找对应的数据库的Driver类型(即在subdrivers目录下找到对应的类,mysql则是CI_DB_pdo_mysql_driver)完成实例化。
  4. 通过CI_DB_query_builder的各个方法(select()/where())构造出SQL语句之后,通过类中的get()/insert()/update()/replace()/delete()等方法对数据库进行操作,实际上都是交由query()方法对对应的SQL语句进行执行。
  5. query()方法会通过simple_query()这一个方法执行SQL语句,simple_query()这一方法首先会检测是否已经初始化数据库连接对象,即成员变量conn_id,如果没有则通过initialize()方法进行连接。初始化完成后,执行上就需要各个DB Driver实现一个_execute方法了。
  6. PDO的driver实现的_execute方法则是直接调用内部成员属性conn_idquery()方法,即我们熟知的PHP MySQL PDO的query()方法,这一方法会返回一个 PDOStatement 对象。SQL语句的执行结果会存储到result_id成员变量之中。
  7. 针对不同的数据库,会对加载不同的结果集Driver,即我们调用get()/get_where()等等操作之后返回的对象,我们通过这一个Driver对象,调用result()/result_array()/row()/unbufferred_row()等方法得到期望查询的结果。

多说一点,result()/result_array()/result_object()/custom_result_object()等方法会把所有匹配的记录都读出,存储到当前结果对象的一个成员变量数组之中,在大结果集的情况下,可能会带来内存的大量使用,这里也是手册中提及过的,同样的使用row()/row_array()/row_object()/first_row()/last_row()/next_row()/previous_row()等方法也会调用上述方法之一,所以调用了这些方法,实际上也会触发大量的内存使用的情况。而unbufferred_row()这个方法实际上是通过逐个获取PDOStatement对象的每一个结果数据,在内存占用上会有较好的表现。编写实例测试:

数据库中存在2条记录。

Controller:

routes.php

/test1/fetch_one

/test1/fetch_all

显然fetch_all操作占用的内存2106256 > 2104920

便捷的扩展功能的方式

application/libraries目录中编写一个同名类即可替换掉系统提供的非数据库类库。通过get_instance()方法获得CI超级对象之后可以在扩展的类库中获得原有类库的支持。

同样的,对内置的部分类库进行扩展也不一定需要在system目录下修改。

譬如希望对Session的Driver类进行扩展,Session在加载Driver类时会先在APPPATH目录的libraries/Session/drivers/下寻找声明的Driver文件。

Hook机制的提供,可以在一个请求的几个阶段上对请求进行干预。比如身份验证可以在自定义的Controller基类中完成,也可以通过钩子在pre_controller阶段进行。

可以支持低版本的PHP

现在PHP已进入7时代,但是如果要在老环境上做开发,CI3是个好选择,可以支持5.4.8+的版本。从代码上看,数组仍然使用了传统的array()方法进行声明,显然是PHP 5.4之前的编写方法。

不强制使用模板引擎

没看错,这个也是我个人认为的优点。模板引擎的使用对于PHPer来说并没有太大意义,徒增了转换回PHP代码的成本,如果说会引起页面可读性降低,这个方面保留意见,对于PHPer来说,有什么会比PHP代码更容易读懂和控制呢?

当然Twig之类的新兴模板引擎自然有可取之处,但是作为PHPer,首选当然是直接编写模板。

可以有所变化的方面

日志功能

CI3的日志功能比较简单,简单带来的好处是易用,然而在定制化和输出丰富程度方面,就稍微欠缺一些。

CI3的日志只能设定保存位置($config['log_path'])、扩展名($config['log_file_extension'])、时间戳格式($config['log_date_format'])、日志级别等配置。存储形式是文件。这里多提一句,CI3写入文件的方式很有意思,先上锁(flock($fp, LOCK_EX))再写入,最后解锁。由于使用了LOCK_EX标记位,多个进程同时写文件时会阻塞住。

这个应该与CI轻量级的设计思想有关,好在CI设计成可以支持Composer autoload,可以考虑引入强大的 monolog 扩展日志功能。

内置数据库读写分离

小项目读写分离可能并没有显得那么重要,但是对于访问量级千万级别甚至更高,或者是有访问峰值的项目来说,单库读写意味着可能处在服务挂掉的边缘。项目一般读写比10比1的经验值也告诉我们,可以利用数据库(主要指MySQL)多从库的方式,对服务进行一定程度的性能扩展。

然而CI3并没有看到内置的读写分离功能,在query()方法中倒是对操作通过is_write_type()方法进行了判断,但是这里只是为检查是否返回查询结果缓存而服务的(写操作通过缓存返回结果不合理)。

可以考虑改造CI3框架实现这一部分功能,这样可以做到对业务透明。

如果不改造,也可以在Model的基类中实现一些方法,如基类中有获取DB对象的共用方法,可以在这里进行一些处理。

RESTful API开发支持

Laravel中的artisan工具提供了很多实用的工具入口。其中一个我认为很实用的是RESTful api单指令生成

这一指令执行之后可以帮助生成从Controller到Model的大部分代码,对于简单对象的CRUD操作来说,这样的代码编写方式很是方便。

相比之下CI3的这类开发工具支持稍微逊色,但是考虑到两个框架出现时间的差别,也是可以接受的。

附上一个可以参考的CI3 RESTful API解决方案chriskacerguis/codeigniter-restserver

最后

CodeIgniter4目前已经放出alpha版本,通过命名空间组织代码,日志的种类也有所丰富,并且较为激进的只支持PHP7及以上版本。不过遗憾的是现在还是没有看到数据库读写分离,代码量也有所涨幅:

数据库Driver目前只有mysqli和postgre,只能期待后续的版本中能够有更强大的CodeIgniter了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">