CodeIgnitor 缓存驱动类的使用

最近在给 redis 驱动类添加新的方法时又陷入了疑惑中,理不清这里面的类关系,所以这次彻底梳理一下。

CodeIgnitor 中使用缓存是通过 Libaray 下的 CI_Cache 代理调用 drivers 下的缓存驱动实现类(Cache_redis)方式进行的。

  • 下面是一个调用 Apc cache 类的一个示例:
$this->load->driver('cache', array('adapter' => 'apc', 'backup' => 'file'));

if ( ! $foo = $this->cache->get('foo'))
{
        echo 'Saving to the cache!<br />';
        $foo = 'foobarbaz!';

        // Save into the cache for 5 minutes
        $this->cache->save('foo', $foo, 300);
}

echo $foo;
  • CI_Cache 类的基本定义如下:
class CI_Cache extends CI_Driver_Library {
    protected $valid_drivers;
    protected $_cache_path;
    protected $_adapter;
    protected $_backup_driver;
    public $key_prefix;

    public function __construct();
    public function get();
    public function save();
    public function delete();
    public function increment();
    public function decrement();
    public function clean();
    public function cache_info();
    public function get_metadata();
    public function is_supported();
}
  • 上面这些方法也可以不用通过加载adapter的方式,而是直接通过属性的方式进行调用:
$this->load->driver('cache');
$this->cache->apc->save('foo', 'bar', 10);

// 文件缓存
$this->load->driver('cache');
$this->cache->file->save('foo', 'bar', 10);

// memcache 缓存
$this->load->driver('cache');
$this->cache->memcached->save('foo', 'bar', 10);

// redis 缓存
$this->load->driver('cache');
$this->cache->redis->save('foo', 'bar', 10);
  • 每种缓存驱动都有它的特定配置或者注意点,这些注意点都是具体缓存驱动实现类中的内容。

  • CI 框架内置了 Cache_apcCache_fileCache_memecachedCache_redisCache_wincache 这5种缓存方式。这些缓存驱动是通过驱动实现类的方式支持的。相关类文件目录结构如下:

|- app
|- system
 |- libraries
  |- Driver.php
  |- Cache
   |- Cache.php
   |- drivers
    |- Cache_apc.php
    |- Cache_file.php
    |- Cache_memcache.php
    |- Cache_redis.php
    |- Cache_wincache.php
  • 如上目录结构所示,其实所有的驱动类基类都是 Driver.php 中的 CI_Driver_Library 类,这个类只提供了一个__get()方法用于获取实际的驱动实现类,在里面主要进行了load_driver()的操作。

  • 在这里,Cache.phpCI_Cache类就继承了 CI_Driver_Library,从而获取了通过属性名加载缓存驱动实现类的能力。所以说,CI_Cache 实际上是一个驱动代理类,通过它加载具体的驱动类。在 load_driver() 末尾有这么一行代码:

// ... some code
// Instantiate, decorate and add child
$obj = new $class_name();
$obj->decorate($this);
$this->$child = $obj;
return $this->$child;
  • 在这段代码之前的一系列操作是通过属性名获取实际实现类名,然后这里实例化该类,并调用实现类的 decorate($parent) 方法把当前类中的方法和属性添加到实现类中,然后设置为 CI_Cache 类的属性,并返回。下次再调用时就能直接找到该类,不用再次加载。

  • 而每个具体的缓存驱动实现类则继承了 Driver.php 中的 CI_Driver 类,在这个类中提供了上面提到的 decorate($parent) 方法,它的主要用途就是把父类中的方法名和属性名存储到子类中,然后通过子类调用这些子类不存在的方法时,退化为通过 __call() 调用父类的方法。这样的话如果在父类 CI_Cache 中定义了一些与adapter 无关的方法时,也能通过 $this->cache->redis 去调用。

至此,CI_Cache 的所有内置类的关系已经全部说明完毕。

项目扩展

  • 上面基本说明了如何使用 CI 中的 library 中的类以及 driver 类(以 Cache driver 为例,对其他类型也适用),并且还说明内部相关类之间的关系。

  • 但框架内置的缓存驱动类可能有的方法缺失,那么就需要我们在项目中进行扩展。CI 提供了对 Library 内的类的自定义扩展以及创建自己的 driver 类的机制。

  • 这里以我们项目中的实际案例为例,下面是项目中的实际目录:

|- app
 |- libraries
  |- Cache
   |- Che300_Cache.php
   |- drivers
    |- Cache_predis.php
    |- Che300_Cache_redis.php
class Che300_Cache extends CI_Cache
{
    public function __construct()
    {
        parent::__construct();
        $this->valid_drivers[] = 'predis';
    }
}
  • 继承 CI_Cache 并且把 predis 也加入到驱动类数组中,这相当于一种新的驱动方式,所以在 drivers 目录下又按照命名约定新建了一个 Cache_predis 驱动实现类。所以下面的调用可以实现:
$this->load->driver('cache');
$this->cache->predis->set('key', 'value');
  • Che300_Cache_redis 也是创建了新的 redis 缓存驱动实现类,它继承了 CI_Cache_redis 类,实际上是对系统的默认 redis 类的覆盖。所以下面的调用实际返回的是 Che300_Cache_redis 驱动实现类:
$this->load->driver('cache');
$this->cache->redis->set('key', 'value');
2020/11/03 posted in  编程技术

PHP7.4 新特性

Typed properties

  • 现在类的属性支持类型声明了:

    class User {
        public int $id;
    public string $name;
    }
  • 注:设置属性 $id$name 的值时必须要指定类型,否则会报错:
    PHP Fatal error: Uncaught TypeError: Typed property User::$id must be int, string used

  • 注:访问属性之前必须要先初始化,否则会报错:

    • PHP Fatal error: Uncaught Error: Typed property User::$id must not be accessed before initialization
Read more   2020/09/02 posted in  编程技术

几千万数据,更改 MySQL 表结构

数据量大、并发量高场景,如何在流量低峰期,平滑实施表结构变更?

  • 如果是减column,升级程序不使用即可
  • 如果是修改column,程序兼容性容易出问题

方案一:在线修改表结构

alter table add column
数据量大的情况下,锁表时间会较长,造成拒绝服务,一般不可行。
Read more   2020/05/11 posted in  编程技术

《精通Linux》 - 笔记1

2020五一假期计划把这本书刷完,然后留下笔记,书籍扔掉。

Ch01 概述

  • 层次:硬件系统 / Linux内核 / 用户进程
  • 用户空间 / 内核空间
  • 主内存:硬件系统中最重要的部分,内核的几乎所有操作都和主内存有关
  • 内核:(1)管理进程(2)管理内存(3)操纵硬件设备(4)系统调用
  • 一个进程让出CPU使用权给另一个进程称为上下文切换(context switch)。内核是在上下文切换时的时间段间隙中运行的(单核CPU)。
  • fork(),exec()
Read more   2020/05/04 posted in  编程技术

php bool 与 boolean 的区别

2020/04/28 posted in  编程技术

APP集成ApplePay调研

目前国内 接入 Apple Pay 有两种模式:API模式和银联SDK模式。

Apple Pay API 模式

下图为目前国内 Apple Pay 支付接入的一个通用的流程(银联API模式),仅供参考:

整个流程中如下:

1、客户端通过苹果API,在 APP 应用内展示 Apple Pay 支付控件。

2、用户在 Apple Pay 的支付控件上进行生物验证(指纹或者人脸识别)或者手机密码验证。

3、苹果在用户验证通过之后,会生成一个用户选中的银行卡相关的 PaymentToken 加密数据,Apple Pay 必须在有网情况下才能进行,苹果需要从开发者网站上使用证书的公钥进行加密,完成后通过 API 回调返回给客户端前端。

Read more   2020/04/23 posted in  编程技术

Laravel 使用中间件过滤参数

问题

使用 Laravel Validator 验证参数以后,对于某些参数需要手动转换为对应的类型(比如 int,float),那么能不能在验证之前先自动过滤一下参数类型呢?

解决

使用中间件可以实现在 Controller 处理逻辑之前先过滤一下参数。

创建一个中间件类

$ php artisan make:middleware FilterParams
Read more   2020/04/04 posted in  编程技术

国家哀悼日 - 网站全局变灰

国务院公告:4月4日举行全国性哀悼活动,哀悼抗疫牺牲烈士和逝世同胞。因此,在自己网站上加上下面一段CSS代码,就能把网站全局变灰了。

<style type="text/css">
    html {
        filter: grayscale(100%);
        -webkit-filter: grayscale(100%);
        -moz-filter: grayscale(100%);
        -ms-filter: grayscale(100%);
        -o-filter: grayscale(100%);
        filter: url("data:image/svg+xml;utf8,#grayscale");
        filter: progid:DXImageTransform.Microsoft.BasicImage(grayscale=1);
        -webkit-filter: grayscale(1);
    }
</style>
2020/04/03 posted in  编程技术

RabbitMQ实现延时任务

场景:下单之后的15分钟内如果没有下单,则自动取消订单。

第一个想到的方法是开启一个定时任务,每隔1分钟就去扫描订单状态,如果有符合条件的,则取消之。这种方式缺点很明显,很浪费资源,因为大部分查询的结果都是无效的。

下面进入正题:使用 RabbitMQ 的死信队列(Dead Letter Exchange)来实现延时任务。

什么是死信?

首先,什么是死信?一个消息在一个队列中处于下列三种状态:(1)消息被拒绝并且不再重新投递.(2)消息超期。(3)队列超载。就变成了死信。如果队列中出现了死信,就会被重新投递到另一个交换机,那么该队列就是死信队列。该交换机根据绑定规则转发到对应的队列上,监听该队列就可以重新消费。简单地说,就是(因为某些原因)没有被消费的消息换个地方重新被消费。

Read more   2020/03/06 posted in  编程技术

组合与组合模式

继承的问题

继承是一种强大的设计方式,但是它也会限制灵活性,特别是类承担多职责时。

下面是一个继承的简单示例。

抽象类 Food 表示食物,它定义了抽象方法 make() 方法。两个实现类 SweetFoodSaltyFood 分别代表了食物的两种口味。

通过使用这种继承机制,客户端代码只知道它在使用一个 Food 对象,食物制作的细节被封装了。

Read more   2019/09/24 posted in  编程技术

CodeIgnitor 3.0.x 之 Load 加载机制

CI 框架中的 library 和很多内置的类都是通过 CI_Loader 这个类加载的,也就是 Controller 中常常出现的 $this->load 属性。那么它本身是如何被加载的。

CI_Loader 初始化

这一切又要从最初的入口点 index.php 说起了。首先,外界的请求定位到了 index.php,所有的请求的处理都是从这里开始的。大略看一下 index.php 的内容,其实也很简单,就是利用当前文件的位置,定义了一些环境和路径常量,比如 ENVIRONMENT, BASEPATH, APPPATH, VIEWPATH,基于这些常量,又定义了一些全局变量:$system_path, $application_folder, $view_folder,最后加载了CI框架的核心类:core/CodeIgnitor.php,完成了它的使命。

Read more   2019/08/20 posted in  编程技术

CodeIgnitor 配置类的使用

CI 的配置文件统一放在 application/config/ 目录下面,框架有一个默认的主配置文件 application/config/config.php。其部分内容如下:

<?php
$config['uri_protocol'] = 'REQUEST_URI';

// ...

$config['charset'] = 'UTF-8';

// ...

$config['subclass_prefix'] = 'My_';
Read more   2019/06/04 posted in  编程技术

变量存储区:堆和栈

最近在看PHP源码解析,涉及到堆栈存储区的知识,而我对于这个却不太清楚,因此,看了一下相关资料,总结一下。

栈,存储函数中的局部变量(临时变量),存储函数地址,栈是后进先出的结构,由CPU管理和优化。

使用栈存储变量的优势在于:你不用再管理内存了,不必手动分配内存或释放它,此外,由于CPU相关的优化,读取写入的效率也很高。

关于栈需要注意的一点是:存储在栈上的变量的大小是有限制的,而堆却不是。

Read more   2019/05/19 posted in  编程技术

《Modern PHP》 - 笔记1 - 最佳实践

Read more   2019/04/03 posted in  编程技术

Distinct 与 Group by 的比较

看了很多文章,这两个SQL语句在不同的数据库上面的实现上可能有相同或有不同,但是应当要明确它们在功能概念上的区别,最终得出结论:

GROUP BY 用来使用聚集函数获得值,比如 AVG, MAX, MIN, SUM 和 COUNT,而 DISTINCT 用于去除重复值。

要根据实际的应用场景来使用(即使它们有时候返回的结果是一样的)

2019/04/01 posted in  编程技术