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

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

    业余花了点时间看看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 库。

 

如果你喜欢MVC的开发模式,也可以添加model类,然后加载 model
模型类,处理相应的业务逻辑。

 

最后在自己定义的controller控制器处理好的数据结果渲染在html
页面上,展示给用户。

 

 

下面看一下CI框架几个重要部分:

 

控制器

    开发者在 application/controller 目录下添加自己的controller
控制器,但是每个控制器都要继承核心库里的基类
CI_Controller,它已获取到整个框架的核心类库对象,通过它基本可以调用CI框架下的核心方法。

 

模型

     模型就是专门用来和数据库打交道的 PHP 类,开发者在
application/models 目录下定义自己的模型类,都要继承 模型基类
CI_Mode。当你在控制下调用模型,只需要下面一行代码就实例化了:

$this->load->model(‘model_name’);

 

辅助函数

当然开发者也可以创建自己的辅助类,文件存放在 application/helpers
目录下,调用的方式与系统

的辅助类一致。

 

$this->load 就是 Loader.php 文件CI_Load 类实例, 我们来看看 CI_Load
类下的 helper() 函数:

/**
     * Helper Loader
     *
     * @param    string|string[]    $helpers    Helper name(s)
     * @return    object
     */
    public function helper($helpers = array())
    {
        is_array($helpers) OR $helpers = array($helpers);
        foreach ($helpers as &$helper)
        {
            $filename = basename($helper);
            $filepath = ($filename === $helper) ? '' : substr($helper, 0, strlen($helper) - strlen($filename));
            $filename = strtolower(preg_replace('#(_helper)?(.php)?$#i', '', $filename)).'_helper';
            $helper   = $filepath.$filename;

            if (isset($this->_ci_helpers[$helper]))
            {
                continue;
            }

            // Is this a helper extension request?
            $ext_helper = config_item('subclass_prefix').$filename;
            $ext_loaded = FALSE;
            foreach ($this->_ci_helper_paths as $path)
            {
                if (file_exists($path.'helpers/'.$ext_helper.'.php'))
                {
                    include_once($path.'helpers/'.$ext_helper.'.php');
                    $ext_loaded = TRUE;
                }
            }

            // If we have loaded extensions - check if the base one is here
            if ($ext_loaded === TRUE)
            {
                $base_helper = BASEPATH.'helpers/'.$helper.'.php';
                if ( ! file_exists($base_helper))
                {
                    show_error('Unable to load the requested file: helpers/'.$helper.'.php');
                }

                include_once($base_helper);
                $this->_ci_helpers[$helper] = TRUE;
                log_message('info', 'Helper loaded: '.$helper);
                continue;
            }

            // No extensions found ... try loading regular helpers and/or overrides
            foreach ($this->_ci_helper_paths as $path)
            {
                if (file_exists($path.'helpers/'.$helper.'.php'))
                {
                    include_once($path.'helpers/'.$helper.'.php');

                    $this->_ci_helpers[$helper] = TRUE;
                    log_message('info', 'Helper loaded: '.$helper);
                    break;
                }
            }

            // unable to load the helper
            if ( ! isset($this->_ci_helpers[$helper]))
            {
                show_error('Unable to load the requested file: helpers/'.$helper.'.php');
            }
        }

        return $this;
    }

     这段代码主要是 加载(include_once) system/helpers 与
appliation/helpers 目录下的 $name_helper.php
名称文件,自定义的辅助函数文件需要添加 前缀 来与
系统的辅助函数区分开。当执行完加载函数,就能得到 $this->name
实例,然后调用它里面的函数。

所有辅助函数如下:

 

  • 数组辅助函数
  • 验证码辅助函数
  • Cookie
    辅助函数
  • 日期辅助函数
  • 目录辅助函数
  • 下载辅助函数
  • 邮件辅助函数
  • 文件辅助函数
  • 表单辅助函数
  • HTML
    辅助函数
  • Inflector
    辅助函数
  • 语言辅助函数
  • 数字辅助函数
  • 路径辅助函数
  • 安全辅助函数
  • 表情辅助函数
  • 字符串辅助函数
  • 文本辅助函数
  • 排版辅助函数
  • URL
    辅助函数
  • XML
    辅助函数  

CI 类库

 

所有的系统类库都位于 system/libraries/ 目录下,大多数情况下,在使用之前,
你要先在控制器中初始化它,使用下面的方法:

$this->load->library(‘class_name’);

‘class_name’
是你想要调用的类库名称,例如,要加载 表单验证类库,你可以这样做:

$this->load->library(‘form_validation’);

一旦类库被载入,你就可以根据该类库的用户指南中介绍的方法去使用它了,这个类似于辅助函数。

同样拓展自己的类库也是在application/libraries 目录下。

 

一旦加载,你就可以使用小写字母名称来访问你的类,调用方法:

$this->someclass->some_method();

所有的类库如下:

  • 基准测试类
  • 缓存驱动器
  • 日历类
  • 购物车类
  • 配置类
  • Email
  • 加密类
  • 加密类(新版)
  • 文件上传类
  • 表单验证类
  • FTP 类
  • 图像处理类
  • 输入类
  • Javascript
  • 语言类
  • 加载器类
  • 迁移类
  • 输出类
  • 分页类
  • 模板解析类
  • 安全类
  • Session
  • HTML
    表格类
  • 引用通告类
  • 排版类
  • 单元测试类
  • URI 类
  • 用户代理类
  • XML-RPC 与 XML-RPC
    服务器类
  • Zip
    编码类

数据库

   
 CI框架封装了多种数据库驱动与连接方法,让你轻松配置就能连接上,且封装了一些查询构造器与生成查询结果,让代码看起来方便简洁。

你只需 在application/config/database.php 文件下配置你链接的参数:

$db['default'] = array(
    'dsn'    => '',
    'hostname' => 'localhost',
    'username' => '',
    'password' => '',
    'database' => '',
    'dbdriver' => 'mysqli',
    'dbprefix' => '',
    'pconnect' => FALSE,
    'db_debug' => (ENVIRONMENT !== 'production'),
    'cache_on' => FALSE,
    'cachedir' => '',
    'char_set' => 'utf8',
    'dbcollat' => 'utf8_general_ci',
    'swap_pre' => '',
    'encrypt' => FALSE,
    'compress' => FALSE,
    'stricton' => FALSE,
    'failover' => array(),
    'save_queries' => TRUE
);

然后在 Controller 里调用一句 :$this->load->database();
就能连接上数据库。

接着,你可以查询你想要的结果:

$this->db->where(‘name’,$name);

$query=$this->db->get(‘mytable’,10,20);

// Executes: SELECT * FROM mytable where name=$nameLIMIT 20, 10

CI框架也提供了数据库的事务处理,如:

$this->db->trans_start();$this->db->query(‘AN SQL
QUERY…’);$this->db->query(‘ANOTHER
QUERY…’);$this->db->query(‘AND YET ANOTHER
QUERY…’);$this->db->trans_complete();

 

提供了简单的查询缓存:

将查询结果对象会被序列化并保存到服务器上的一个文本文件中。
当下次再访问该页面时,会直接使用缓存文件而不用访问数据库了。只有读类型(SELECT)的查询可以被缓存。
这个相对应 Java 的hibernate 数据库映射
就弱化了很多,Java提供了三级的缓存方式,而且在查询数据库的时候,并不会每次都断开,再连接。

 

以上都是CI框架提供的重要组成部分,也许它可能满足不了你所有的需求,但也提供了一些给你拓展的方式,如在application/core目录下添加你的核心类,这都是CI框架已考虑到的问题。当然它在处理一些繁杂的业务逻辑的时候,还是比较薄弱的,比如说权限使用,模块灵活增删等。

CI框架主要是以轻便,快捷上手为主要的优势,让你去处理一些简单的项目。它介于一个没有框架与一个比较笨重的框架之间,所以一个框架好不好用,还要基于你的需求。

 

CI框架还提供了一些其它便捷的开发帮助,它有有自己的模板引擎,也有程序分析:

你可以在你的 控制器 方法的任何位置添加一行下面的代码:

$this->output->enable_profiler(TRUE);

 

设置基准测试点

$this->benchmark->mark(‘code_start’);// Some code happens
here$this->benchmark->mark(‘code_end’);echo$this->benchmark->elapsed_time(‘code_start’,’code_end’);

 

最后输出分析的信息:

$sections=array(‘config’=>TRUE,’queries’=>TRUE);

$this->output->set_profiler_sections($sections);

 

下表列出了可用的分析器字段和用来访问这些字段的 key :

Key Description Default
benchmarks 在各个计时点花费的时间以及总时间 TRUE  
config CodeIgniter 配置变量 TRUE  
controller_info 被请求的控制器类和调用的方法 TRUE  
get 请求中的所有 GET 数据 TRUE  
http_headers 本次请求的 HTTP 头部 TRUE  
memory_usage 本次请求消耗的内存(单位字节) TRUE  
post 请求中的所有 POST 数据 TRUE  
queries 列出所有执行的数据库查询,以及执行时间 TRUE  
uri_string 本次请求的 URI TRUE  
session_data 当前会话中存储的数据 TRUE  
query_toggle_count 指定显示多少个数据库查询,剩下的则默认折叠起来 25  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

以上是我对CI框架大致的了解与分析,我做php的经验不多,这是我第一个尝试去深入了解的php框架,请大家多多指教。

 

发表评论

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