PHP项目中CodeIgniter使用的一些建议

澳门新葡亰娱乐在线 6

本文由码农网 –
邱康原创,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划!

    业余花了点时间看看CodeIgniter框架(简称CI),CI目前的稳定版本是
3.X,4.0版本已经出来了,但还在测试中,所以我分析的还是 3.x 版本。

最近再给一个APP写API,同时还要写相应的后台管理网站。为了便于开发和代码组织与管理,我决定采用一个现有的框架。Codeigniter由于其轻量容易自定制的特点吸引了我,一路开发过来也有大半年时间了,写下一些自己在开发过程中的一些体会来与大家共勉。

CI是一个很轻便的框架,整个下载包也就2M多,而且使用起来方便快捷,适用于一些简单的功能开发,以及做app
接口。

开发工具

开发工具有好多,每个人的习惯也不同。Eclipse一直是我的最爱,那么怎么让Eclipse支持codeigniter呢?

首先这是个PHP项目,所以得让Eclipse支持PHP。假设你的计算机已经安装了PHP,那么最简单的做法就是在Eclipse的market space中搜索PDT并安装。具体操作:Help -> Eclipse
Marketplace。在输入框中输入PDT,点击find,如图:

澳门新葡亰娱乐在线 1

点击右下方的install,等待几分钟即可。

现在可以在Eclipse中写PHP了,不过对于codeigniter来说,经常用到的语句类似如下:

$this->load->model ( 'user_model' );

$this->input->post ('phone');

每次都要手动输入这么长的类似的语句对于追求高效的人来说显然是不可取的,所以得让Eclipse支持codeigniter特定的代码提示,包括Controller和Model。把如下代码添加到

system/core/
目录下的Controller文件中CI_Controller类的构造函数之前,这其实就是进行

变量声明,Model文件也是同样操作。

 /**
       * @var CI_Config
       */

       var $config;

       /**
       * @var CI_DB_active_record
       */

       var $db;

       /**
       * @var CI_Email
       */

       var $email;

       /**
       * @var CI_Form_validation
       */

       var $form_validation;

       /**
       * @var CI_Input
       */

       var $input;

       /**
       * @var CI_Loader
       */

       var $load;

       /**
       * @var CI_Router
       */

       var $router;

       /**
       * @var CI_Session
       */

       var $session;

       /**
       * @var CI_Table
       */

       var $table;

       /**
       * @var CI_Unit_test
       */

       var $unit;

       /**
       * @var CI_URI
       */

       var $uri;

       /**
       * @var CI_Pagination
       */

       var $pagination;

现在效果如下:

澳门新葡亰娱乐在线 2

只需输入$this 即可产生代码提示,效率提高不少。

 

自定义辅助函数和库

一般一个项目中会有许多相同的逻辑,会使用到相同的代码。如果每次都去复制粘贴不仅麻烦而且不利于后续修改,想到需要修改一个逻辑就要到每个文件里修改代码就觉得可怕(涉及到设计模式这一块了,值得我们学学)。所以你要善于利用那些自定义辅助函数(helpers)和自定义的库(libraries)。把相同的逻辑封装成函数或者类,每次遇到相同的逻辑之时只需调用现成的helper或者library即可,修改也只需要在helper或者library中一处修改,不需要到处找代码。

举一个我项目中的例子。API需要给APP发送的请求返回json格式数据,那么在PHP中的一种实现方法就是:

echo json_encode ( $data);//$data 是array类型的数据

但是如果$data中包含中文字符的话,输出的json数据会把中文变成Unicode编码,为了避免客户端需要再次处理数据,需要加入一个参数,即:

echo json_encode ( $data , JSON_UNESCAPED_UNICODE);//$data 是array类型的数据

这样就能输出原始的中文了。但是问题又来了,每次都要写这么长一段代码实在是麻烦,所以封装成一个辅助函数。在application/helpers
文件夹中新建一个文件render_helper.php。其中代码如下:

<?php

defined ( 'BASEPATH' ) or exit ( 'No direct script access allowed' );

/**

 * 打印不转义中文的json

 * @param [array] $data

 */

function echo_json($data) {

       echo json_encode ( $data, JSON_UNESCAPED_UNICODE );

}

那么每次调用之时只需先在类中的构造函数载入该辅助函数,如下:

$this->load->helper ( 'render' ); // 载入打印json的自定义辅助函数

就可以在本类的所有成员函数中使用该方法:

echo_json ( $data);

这样省时省力(加上Eclipse的代码提示,你只需要输入echo,这个函数就可以出来了)。以后需要修改成编码后中文,或者转换成英文等等(奇怪的需求~)你都可以只修改一行代码,效率提高不少。

    该框架整个流程图如下:

善用缓存

如果一个APP的活跃用户不是很多,那么传统的LAMP架构就可以应付了。但是随着用户的逐渐增多,要想提高并发量,最好还是加一个缓存。在memcached和redis中,我选择了redis,主要因为它数据类型更丰富(不仅支持key-value型的数据,同时还提供list,set,zset,hash等数据结构的存储),能帮你实现一部分逻辑(避免重复造轮子)。与Eclipse配置同理,首先得让计算机上的PHP能够和redis通讯(假设你的计算机已经安装了redis),那么首选的是phpredis扩展,这里介绍一下我在Debain服务器上的配置过程,执行的命令如下:

wget https://github.com/nicolasff/phpredis/archive/master.tar.gz  #下载扩展

tar xvf master.tar.gz      #解压目录

cd phpredis-master/     #进入解压后的目录

phpize

./configure --enable-redis

make && make install    #安装

#但是报错-bash: phpize: command not found(在使用apt-get install php5安装php时,默

#认是没有安装phpize的,我们安装phpredis时,需要用到phpize,因此,需要先安装#phpize。我们通过安装php开发者工具来获取phpize。执行如下命令即可: apt-get install #php5-dev)

ls  /usr/lib/php5/20100525/#根据安装提示的文件,结果:curl.so  gd.so  redis.so

vim /etc/php5/apache2/php.ini   #打开PHP配置文件

# Dynamic Extensions 后面添加extension=redis.so,因为上面命令结果显示有redis.so

/etc/init.d/apache2 restart       #重启Apache服务器

现在PHP就能和redis通讯了,测试如下:

<?php

 $redis = new Redis();

 $redis->connect('127.0.0.1',6379);

 $redis->auth('你的密码');//为了安全,要给redis设置密码

 $redis->set('tom','hanks');

 echo ' tom:'.$redis->get('tom'). '</br>';// tom:hanks

echo 'will:'.$redis->get('will'); //will:

?>

澳门新葡亰娱乐在线 ,好了,现在来到配置codeigniter使其能够使用redis的步骤了。Codeigniter3.0.0本身具有redis支持,但是它实现的功能过于单一,不能够满足我的业务需求,所以必须要修改。但是最好不要直接在/system/libraries/Cache/drivers/Cache_redis.php
中修改,因为以后codeigniter升级采用直接覆盖system文件夹的形式,会覆盖掉你的代码。所以最明智的做法还是自己创建一个库,具体步骤如下:

1、在application/config/redis.php 中加入配置信息:

<?php

defined ( 'BASEPATH' ) or exit ( 'No direct script access allowed' );

$config ['socket_type'] = 'tcp';

config ['host'] = '你的服务器域名或者IP';

$config ['password'] = '你的redis密码';

$config ['port'] = 6379;

$config ['timeout'] = 0;

2、在application/libraries中建立如下图的文件结构

澳门新葡亰娱乐在线 3

Rediscli.php 代码如下:

<?php

defined ( 'BASEPATH' ) or exit ( 'No direct script access allowed' );

class Rediscli extends CI_Driver_Library {

       public $valid_drivers;

       public $CI;

       function __construct() {

              $this->CI = & get_instance ();

              $this->valid_drivers = array (

                            'default'

              );

       }

}

Rediscli_default.php
代码从/system/libraries/Cache/drivers/Cache_redis.php
直接拷贝过来,唯一的不同是把类名从CI_Cache_redis改为Rediscli_default,这样就能按照官方的使用方法使用我们自己写的库。

3、现在可以在Rediscli_default.php中添加或者修改方法,具体操作取决于你自己的应用的需求。比如在我的应用中,需要保持一个一定长度的队列,所以在Rediscli_default类中添加如下两个方法:

/**

       * 弹出链表头元素

       * @param unknown $key,链表名              

       */

       public function lpop($key) {

              return $this->_redis->lPop ( $key );

       }

       /**

       * 插入元素到表尾

       * @param unknown $key,链表名              

       * @param unknown $value,待插入值              

       */

       public function rpush($key, $value) {

              return $this->_redis->rPush ( $key, $value );

       }

某个类需要调用该库时只需在其构造函数中添加如下代码:

$this->load->driver ( 'rediscli' ); // 加载redis自定义库

$this->rediscli->default->is_supported ();//判断是否支持redis并打开连接

则该类的每个函数就都可以使用Rediscli_default类中的任一方法,示例如下:

$this->rediscli->default->lpop('delnews');//弹出名为delnews的链表头元素

$this->rediscli->default->rpush('delnews',$nid);//该链表加入尾元素$nid

澳门新葡亰娱乐在线 4

登陆逻辑的实现

对于我们的APP来说,每个请求都要判断用户是否已登陆,对于登陆的合法用户正常显示请求,对于未登录的用户提示“请先登录”。一般的web应用采用cookie-session机制,一般的session都是以文件形式保存在服务器上,考虑到文件访问慢于内存访问,我们可以配置codeigniter的session保存于redis中。我这里模仿这种机制:用户正常login后返回一个特定的id和特定的token(你可以自定义产生算法),服务器以id为key、token为value直接保存在redis中。以后的每次访问APP都必须带上用appsecret(预定义)加密后的参数id和token,服务器端校验成功即正常返回数据,否则提示“请先登录”。

那么是否每个类都需要写一遍检查登陆的代码呢?当然不能这么干,我们可以自定义一个基类,在该基类的构造函数中检查登陆状态(还可以在这里加载常用的辅助函数和库,比如上面的render_helper

rediscli)。然后其他的业务逻辑的类就继承该基类,并完成其自身逻辑即可。

具体操作就是在 application/core
中新建文件 MY_Controller.php,该文件对应MY_Controller类(继承CI_Controller类)。该类只需要一个构造函数完成上述功能即可。

然后其它逻辑类只需继承MY_Controller类就不必再次校验登陆状态,而只需完成自身逻辑。

  1. index.php 文件作为前端控制器,初始化运行 CodeIgniter
    所需的基本资源;
  2. Router 检查 HTTP 请求,以确定如何处理该请求;
  3. 如果存在缓存文件,将直接输出到浏览器,不用走下面正常的系统流程;
  4. 在加载应用程序控制器之前,对 HTTP
    请求以及任何用户提交的数据进行安全检查;
  5. 控制器加载模型、核心类库、辅助函数以及其他所有处理请求所需的资源;
  6. 最后一步,渲染视图并发送至浏览器,如果开启了缓存,视图被会先缓存起来用于
    后续的请求。

总结

Codeigniter是我最喜欢的一个PHP框架(相比于thinkPHP,Zend
Framework等等),主要在于其易安装,轻量,易自定制的优点。把握好这些优点,我们就能写出不逊于那些重量级框架写出来的应用,并且省时省力。

作为一个学生,在摸索学习的路上还有很多问题需要解决,本文的目的在于和大家分享,更重要的是大家有什么建议或者批评请一定不吝赐教哈,我的微博:邱康singasong。

 

下载框架源码,解压得到如下代码结构:

澳门新葡亰娱乐在线 5

 

 

主要有三个目录

1、application目录:用于开发者编写相应的配置以及逻辑处理,开发者只需在这个目录下添加自己需要的开发文件。

2、system目录:框架的系统库,里面包括核心库,类库,辅助类库,数据库等,这些文件,开发者最好不要擅自修改,它是整个框架的龙脉。

3、user_guide:用户手册。

 

接下来看看源码的请求流程:

澳门新葡亰娱乐在线 6

 

 

首先假设有一个 URL 请求,入口就是
index.php,该文件定义了几个常量,应用的路径,以及核心库的路径等。

 

接着引入 核心库system/core下的
CodeIgniter.php文件,该文件初始化核心库system/core里的类库,分别是:

{
  ● benchmark: "Benchmark",
  ● hooks: "Hooks",
  ● config: "Config",
  ● log: "Log",
  ● utf8: "Utf8",
  ● uri: "URI",
  ● router: "Router",
  ● output: "Output",
  ● security: "Security",
  ● input: "Input",
  ● lang: "Lang",
  ● loader: "Loader"
}

每个类库的注释在上图已有解释。

     同时也加载 应用项目
application/config目录下的配置文件(这些配置文件都是开发者根据自己的需要去添加与配置),

根据判断加载字符串处理库mbstring,添加错误异常预处理方法。在加载的同时,也把钩子部署到了相应位置,如果开发者定义了相应钩子实现方法,就会在相应的位置执行。

    在 CodeIgniter.php 初始化核心库的时候定义了五个钩子,分别如下:

 

  • pre_system 在系统执行的早期调用,这个时候只有 基准测试类 和 钩子类
    被加载了, 还没有执行到路由或其他的流程。
  • pre_controller 在你的控制器调用之前执行,所有的基础类都已加载,路由和安全检查也已经完成。
  • post_controller_constructor 在你的控制器实例化之后立即执行,控制器的任何方法都还尚未调用。
  • post_controller 在你的控制器完全运行结束时执行。
  • post_system 在最终的页面发送到浏览器之后、在系统的最后期被调用。

然后,实例化 CI_Controller 类:

function &get_instance()

{

      return CI_Controller::get_instance();

}

 

通过路由 router 及 uri 得到请求的 controller控制器、method方法
以及参数,执行该方法。

 

期间根据开发者在application/config目录下的配置,会加载相应的 librays
类库、 helper辅助函数 及 DB 库。

 

发表评论

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

相关文章

网站地图xml地图