CodeIgniter的扩展方法

   
    
    以下例子皆需要php5.4以上版本。
    
    CodeIgniter这个框架的规则比较少,另外核心类都心CI_开头,很方便扩展。CodeIgniter的扩展有很多种方法,有替换核心类的,有继承扩展核心类,及使用钩子的,和__autoload加载的。

    先说怎么扩展smarty的例子。

    本人作为一个程序员,觉得php原生的模版是比smarty更强大的,只要在格式上用<?php if (..): ?><?php endif; ?>,另外view用的函数必须是php原生的或公共的,不要让view越界做M和C的事,就可以了。不过用smarty写不太复杂的代码模版是不错的选择,至少可以直接写<?php 什么的, 还少打字。

    当然,你可以把smarty的两个文件夹直接放ci的根目录,这样最好配,但味道不好。

    我是这样配的:

    首先把smarty的libs文件夹(核心库)拷到 application/third_party/文件夹, 把libs改名成smarty。Smarty.class.php文件改名成 Smarty.php(实际上没必要)。把demo文件夹拷到application/, 改名成smarty。

    配上这个函数就OK了。
    
function get_smarty($conf = [])
{
    $smarty_path = APPPATH.'smarty/';

    require_once(APPPATH.'third_party/smarty/Smarty.php');
    
    $smarty = new Smarty;
    
    foreach (array_merge([
        'compile_check' => 'true',
        // 'debugging' => 'true',
        'caching' => 'true',
        'cache_lifetime' => 120,
        // 'force_compile' => 'true',
        // 'left_delimiter' => '<{',  
        // 'right_delimiter' => '}>',
        'template_dir' => "{$smarty_path}templates",
        'compile_dir' => "{$smarty_path}templates_c",
        'cache_dir' => "{$smarty_path}cache",
        'config_dir' => "{$smarty_path}configs",
        ],
        $conf) as $k => $v)
    {
        $smarty->$k = $v;
    }
    
    return $smarty;
}
/* 结束 */

    至于这个函数是放MY_Controller作static 函数,还是写在专门扩展third_party的静态类中,或是helpers的MY_。。。里直接用函数,这看个人喜好了。

/* __autoload开始 */

    CI的__autoload有一些要注意的事项,比如CI的类一般以CI_或MY_开头。__autoload我是这样写的。
    
if ( ! function_exists('__autoload'))
{
    function __autoload($class)

    {

        /* 这样写是错误的

        if ( ! isset($prefix))
        {
            static $prefix;
            $prefix = config_item('subclass_prefix');

        }; */

        static $prefix;

        ($prefix === NULL) && $prefix = config_item('subclass_prefix');

        
        if (strpos($class, $prefix) === 0)
        {
            require(APPPATH . "core/{$class}.php");
        }
        else
        {
            strpos($class, 'CI_') !== 0 && require(APPPATH . "third_party/{$class}.php");
        }
    }
};
/* 结束 */

    if (strpos($class, $prefix) === 0)
    {
        require(APPPATH . "core/{$class}.php");
    }
    这句CI有内部类,会自己加载。。所以可以改成strpos($class, $prefix) === 0 && strpos。。。
    
    require(APPPATH . "third_party/{$class}.php")

    这句是要依赖于PHP的命名空间的。命名空间有很多\符号,这个在win环境下require没有问题。linux的还没有试过,你如果想保险,就用strtr($str, '\\', '/')或str_replace('\\', '/', $str)替换下, strtr效率高些,但区别不大;另外可能还要注意文件的大小写。


    所以直接这样写是最简的。

    function __autoload($class)
    {
        (strpos($class, '\\') !== FALSE) && require(APPPATH . 'third_party/'.strtr($class, '\\', '/').'.php');
    }


    至于复杂的,可以看Yii这个框架。这个里面的YiiBase.php文件中的YiiBase类有autoload函数是用于自动载入的,深思熟虑的代码,且效率不错。import和getPathOfAlias的写法也类似。

    
    下面看例子我用了doitphp的类库,不过为了方便改了名字,另外改了点代码。
    
    如果你仍然想用csv.class.php这样的名字, 可以写
    elseif (strpos($class, 'doitphp') !== 0)
    {
        strpos($class, 'CI_') !== 0 && require(APPPATH . "third_party/{$class}.php");
    }
    else
    {
        require(APPPATH . "third_party/{$class}.class.php");
    }
    
    如果像doitphp这样的文件夹多了,不想写太多if,可以把doitphp们放到third_party/dot_class文件夹。
    至于重构问题,用IDE应该可以把命名空间和文件名字替换下。
    也可以直接用php批量改文件名后缀及str_replace('doitphp\', 'dot_class\doitphp\', 文件读的值)。
    
    也可以这样写:不过我觉得strpos应该比file_exists快些。
    
    $path = APPPATH . "third_party/{$class}";
    if (file_exists("{$path}.php"))
    {
        require("{$path}.php");
    }
    elseif (file_exists("{$path}.class.php"))
    {
        require("{$path}.class.php");
    };
    
    
application/third_partly/doitphp/libraries/csv.php :

<?php
namespace doitphp\libraries;
use doitphp\core as core;

/**
 * CSV操作类
 *
 * CSV文件的读取及生成
 * @author tommy <streen003@gmail.com>
 * @copyright Copyright (c) 2010 Tommycode Studio
 * @link http://www.doitphp.com
 * @license New BSD License.{@link http://www.opensource.org/licenses/bsd-license.php}
 * @version $Id: csv.class.php 1.0 2011-12-24 18:08:57Z tommy $
 * @package libraries
 * @since 1.0
 */

(defined('BASEPATH')) OR exit();

class csv extends core\Base {
    
    protected static $_csv_conf = ['delim' => ",",
            'newline' => "\n",
            'enclosure' => '"',];
    
    protected $_conf = ['class csv extends core\\Base'];
    private $_private_conf = ['class csv _private_conf'];
    
    /**
     * 将CSV文件转化为数组
     *
     * @access public
     * @param string $fileName csv文件名(路径)
     * @param string $delimiter 单元分割符(逗号或制表符)
     * @return array
     */
    public static function readCsv($fileName, $delimiter = ",") {

        //参数分析
        if (!$fileName) {
            return false;
        }

        setlocale(LC_ALL, 'en_US.UTF-8');

        //读取csv文件内容
        $handle       = fopen($fileName, 'r');
        $outputArray  = array();
        $row          = 0;
        while ($data = fgetcsv($handle, 1000, $delimiter)) {
            $num = count($data);
            for ($i = 0; $i < $num; $i ++) {
                $outputArray[$row][$i] = $data[$i];
            }
            $row++;
        }
        fclose($handle);

        return $outputArray;
    }

    /**
     * 生成csv文件
     *
     * @access public
     * @param string $fileName 所要生成的文件名
     * @param array $data csv数据内容, 注:本参数为二维数组
     * @return void
     */
    public static function makeCsv($fileName, $data, $csv_conf = []) {

        //参数分析
        if (!$fileName || !$data || !is_array($data)) {
            return false;
        }
        if (stripos($fileName, '.csv') === false) {
            $fileName .= '.csv';
        }
        
        // 'delim' => ",",
        // 'newline' => "\n",
        // 'enclosure' => '"',
        extract(array_merge(self::$_csv_conf, $csv_conf));

        //分析$data内容
        $content = '';
        foreach ($data as $lines) {
            if ($lines && is_array($lines)) {
                foreach ($lines as $key=>$value) {
                    if (is_string($value)) {
                        $lines[$key] = $enclosure . str_replace($enclosure, $enclosure.$enclosure, $value) . $enclosure;
                    }
                }
                $content .= implode($delim, $lines) . $newline;
            }
        }

        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
        header('Expires:0');
        header('Pragma:public');
        header("Cache-Control: public");
        header("Content-type:text/csv");
        header("Content-Disposition:attachment;filename=" . $fileName);

        echo $content;
    }
}

/* application/third_partly/doitphp/libraries/csv.php */
/* 结束 */

    Base.php 也可以放 libraries,这样写csv extends Base就行。如果写csv extends doitphp\core\Base,那么实际引用的路径就是application/third_partly/doitphp/libraries/doitphp/core/了, 所以有必要用use语句。
    感觉下面的Base.php类没有什么用,csv类没有什么protected属性要改,虽然csv还可以有子类,protected属性子类又可以用。

    子类继承了父类的__get和__set方法,却也不能作用于自己子类的static属性。所以有必要把子类的属性设成private或static的,就算父类有这些方法,也不会影响封装。

    再说下Yii,它的CComponent类中的__get和__set方法写得——还是用深思熟虚这句话吧。


application/third_partly/doitphp/core/Base.php

<?php

namespace doitphp\core;

/**
 * Base.class.php
 *
 * DoitPHP框架基类,用于被其它类继承。
 * @author tommy <streen003@gmail.com>
 * @copyright  Copyright (c) 2010 Tommy Software Studio
 * @link http://www.doitphp.com
 * @license New BSD License.{@link http://www.opensource.org/licenses/bsd-license.php}
 * @version $Id: Base.class.php 1.3 2010-11-13 21:12:01Z tommy $
 * @package core
 * @since 1.0
 */
(defined('BASEPATH')) OR exit();

abstract class Base {
    
    private static $_base_conf = ['class Base'];
    /**
     * 自动变量设置
     *
     * 程序运行时自动完成类中作用域为protected及private的变量的赋值 。
     *
     * @access public
     * @param string $name 属性名
     * @param string $value 属性值
     * @return void
     */
    public function __set($name, $value) {

        if (property_exists($this, $name)) {

            $this->$name = $value;
        }
    }

    /**
     * 自动变量获取
     *
     * 程序运行时自动完成类中作用域为protected及private的变量的获取。
     *
     * @access public
     * @param string $name 属性名
     * @return mixed
     */
    public function __get($name) {

        return isset($this->$name) ? $this->$name : false;
    }
    
    public function get($name) {

        if (isset($this->$name)) {
            return $this->$name;
        } elseif (isset(self::${$name})) {
            return self::${$name};
        }
    }
    
    public function static_get($name) {
        if (isset(self::${$name})) {
            return self::${$name};
        }
    }

    /**
     * 函数: __call()
     *
     * 用于处理类外调用本类不存在的方法时的信息提示
     *
     * @access public
     * @param string $method 方法名称
     * @param string $args   参数名称
     * @return string
     */
    public function __call($method, array $args) {

        echo 'Method:' . $method . '() is not exists in Class:' . get_class($this) . '!<br/>The args is:<br/>';
        foreach ($args as $value) {
            echo $value, '<br/>';
        }
    }

    /**
     * 输出类的实例化对象
     *
     * 直接调用函数,输出内容。
     *
     * @access public
     * @return string
     */
    public function __toString() {

        return (string) 'This is ' . get_class($this) . ' Class!';
    }

}

/* application/third_partly/doitphp/core/Base.php */
/* 结束 */

    // 下面是控制器的代码
    // 我可以直接这样写doitphp\libraries\csv::makeCsv(。。。实际上是为了省字
    $csv = new doitphp\libraries\csv();
    
    // var_dump($csv->get('_base_conf') /* 父类方法可以得到自己的静态属性 */
        // , $csv->_conf /* 父类方法可以得到子类的非静态属性 */
        // , $csv->get('_csv_conf') /* 父类方法无法得到子类静态属性 */
        // , $csv->static_get('_csv_conf') /* 父类静态方法也无法得到子类静态属性 */
        // , $csv->_private_conf /* 父类方法无法得到子类私有属性 */);
    $csv::makeCsv('test', [['1','1',], ['2','2',], ['3','3','3',],]);
    
/* __autoload结束 */


在CI中BASEPATH是绝对路径,但APPPATH这个不是绝对路径,有一点效率问题,所以需要改CI根目录的index.php。

if (is_dir($application_folder))
{
    define('APPPATH', $application_folder.'/');
}
改成
if (is_dir($application_folder))
{
    define('APPPATH', dirname($_SERVER['SCRIPT_FILENAME'])."/{$application_folder}/");
}

当然,你也可以用realpath这个函数。
   
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值