Yii框架中有点意思的文件缓存

Yii框架中有点意思的文件缓存

在Yii框架的framework/caching目录下是Yii框架的所有缓存操作。包括文件缓存,APC缓存、Db缓存,Memcache缓存等。 今天我们要说的是文件缓存。

【文件和类结构】

它总共包括CFileCache.php、CCache.php和接口文件。 缓存基类CCache抽象类实现了CApplicationComponent类,ICache, ArrayAccess接口。 实现类CFileCache继承自CCache基类。其它如Memcache等缓存也是继承这个基类。 在ICache接口中统一缓存接口,在CCache基类中统一对外的接口,以类似于模板方法模式的方式将对于增加,修改,删除操作延迟到子类实现。

【缓存方案】

指定缓存目录,以一条数据一个文件的方式存储。序列化使用PHP自带的serialize函数。 如果缓存数据过多,作者建议使用多级目录,多级目录会自动生成,默认为0,即当前目录,推荐使用3级以下的子目录。 对于缓存过期是以文件的修改时间为准(filemtime函数)。在获取数据时,如果文件过期,则取的数据为空。

【有点意思的地方】

个人觉得有点意思的地方在于其对于过期缓存的批量处理,在程序中作者称其为garbage collection。 看其实现代码:

if(!$this->_gced && mt_rand(0,1000000)<$this->_gcProbability)
{
    $this->gc();    //  清除过期的缓存文件
    $this->_gced=true;
}

各位看官,你应该知道这段代码是啥意思了。 有点意思的地方是和PHP源码中对于session过期的处理。如下所示源码:

if (PS(mod_data) && PS(gc_probability) > 0) {
    int nrdels = -1;

    nrand = (int) ((float) PS(gc_divisor) * php_combined_lcg(TSRMLS_C));
    if (nrand < PS(gc_probability)) {
        //  调用定义的垃圾收集方法
        PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &nrdels TSRMLS_CC);
        ...
    }
}

对比下变量名,对比下调用方式。 ^_^

TIPI0102–PHP源码结构、阅读代码方法

第二节 PHP源码结构、阅读代码方法

PHP源码目录结构:


俗话讲:大巧不工。PHP的源码在结构上非常清晰甚至简单。下面,先简单介绍一下PHP源码的目录结构。

  • build: 源码编译相关文件,包括buildconft等sh文件,还有一些awk的脚本。
  • ext 官方扩展目录,包括了绝大多数PHP的函数的定义和实现,如array系列,pdo系列,spl系列等函数的实现,都在此处相对的子目录中。
  • main PHP宏定义与实现,在需要扩展PHP时,经常要使用的PHP_*系列宏就在这里定义。
  • Zend 包含Zend引擎文件,Zend API宏的定义和实现。
  • pear “PHP 扩展与应用仓库”, 包含PEAR的核心文件。
  • sapi 包含了各种服务器抽象层的代码,以目录区分。
  • TSRM “线程安全资源管理器” (TSRM) 目录。
  • tests 测试脚本目录。
  • win32 Windows 下编译PHP相关的脚本。

PHP源码阅读方法:


使用VIM + Ctags查看并追踪源码:

VIM是一个非常给力的编辑器,在纯命令终端下,它几乎是无可替代的(Emacs?)。
ctags可以将源代码中的各种函数、宏等信息做上标记。这样,使用VIM就可以很方便的查看源码。
简洁使用说明:

#在PHP源码目录(假定为/server/php-src)执行:
$ cd /server/php-src
$ ctags -R

#在~/.vimrc中添加:
set tags+=/server/php-src/tags

再用vim打开各种php源码文件时,将光标移到想查看的函数、宏、变量上面, 使用 Ctrl+p 就可以自动跳转至定义,Ctrl+o 可以返回上一次查看位置。

使用Visual Studio + editplus查看并追踪源码:

看源码还是用IDE舒服一些,windows下我们还是用Visual Studio 2010看吧。 在win32目录下已经存在了可以直接打开的工程文件,如果由于版本原因无法打开,可以在此源码目录上新建一个基于现有文件的Win32 Console Application工程。
常用快捷键

F12 转到定义
CTRL + F12转到声明

F3: 查找下一个
Shift+F3: 查找上一个

Ctrl+G: 转到指定行

CTRL + -向后定位
CTRL + SHIFT + -向前定位

对于一些搜索类的操作,可以考虑使用editplus或其它文本编辑工具进行,这样的搜索速度相对来说会快一些。
如果使用editplus进行搜索,一般是选择 【搜索】 中的 【在文件中查找…】

作者:TIPI Team

TIPI0103–PHP实现中的常用代码

第三节 PHP实现中的常用代码

在PHP的源码中经常会看到一些宏或一些对于刚开始看源码的童鞋比较纠结的代码。这里提取中间的一些进行说明。

1. 关于##和#


在PHP的宏定义中,最常见的要数双井号。

双井号

在C语言的宏中,”##”被称为 连接符(concatenator),用来把两个语言符号(Token)组合成单个语言符号。这里的语言符号不一定是宏的变量。并且双井号不能作为第一个或最后一个元素存在。如下所示源码:

#define PHP_FUNCTION            ZEND_FUNCTION
#define ZEND_FUNCTION(name)             ZEND_NAMED_FUNCTION(ZEND_FN(name))
#define ZEND_FN(name) zif_##name
#define ZEND_NAMED_FUNCTION(name)       void name(INTERNAL_FUNCTION_PARAMETERS)
#define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, \
zval *this_ptr, int return_value_used TSRMLS_DC

PHP_FUNCTION(count);

//  预处理器处理以后, PHP_FUCNTION(count);就展开为如下代码
void zif_count(int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC)

宏ZEND_FN(name)中有一个”##”,它的作用一如之前所说,是一个连接符,将zif和宏的变量name得值连接起来。

单井号

“#”的功能是将其后面的宏参数进行 字符串化操作 ,简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号,用比较官方的话说就是将语言符号(Token)转化为字符串。

2. 关于宏定义中的do-while


如下所示为PHP5.3新增加的垃圾收集机制中的一段代码:

#define ALLOC_ZVAL(z)                                   \
do {                                                \
    (z) = (zval*)emalloc(sizeof(zval_gc_info));     \
    GC_ZVAL_INIT(z);                                \
} while (0)

如上所示的代码,在宏定义中使用了 do{ }while(0) 语句格式。如果我们搜索整个PHP的源码目录,会发现这样的语句还有很多。那为什么在宏定义时需要使用do-while语句呢? 我们知道do-while循环语句是先执行再判断条件是否成立, 所以说至少会执行一次。当使用do{ }while(0)时代码肯定只执行一次, 肯定只执行一次的代码为什么要放在do-while语句里呢? 这种方式适用于宏定义中存在多语句的情况。如下所示代码:

#define TEST(a, b)  a++;b++;

if (expr)
    TEST(a, b);
else
    do_else();

代码进行预处理后,会变成:

if (expr)
    a++;b++;
else
    do_else();

这样if-else的结构就被破坏了: if后面有两个语句, 这样是无法编译通过的, 那为什么非要do-while而不是简单的用{}括起来呢.这样也能保证if后面只有一个语句。 例如上面的例子,在调用宏TEST的时候后面加了一个分号, 虽然这个分号可有可无, 但是出于习惯我们一般都会写上. 那如果是把宏里的代码用{}括起来,加上最后的那个分号. 还是不能通过编译. 所以一般的多表达式宏定义中都采用do-while(0)的方式.

3. #line 预处理


#line 838 "Zend/zend_language_scanner.c"

#line预处理用于改变当前的行号和文件名。
如上所示代码,将当前的行号改变为838,文件名Zend/zend_language_scanner.c
它的作用体现在编译器的编写中,我们知道编译器对C 源码编译过程中会产生一些中间文件,通过这条指令,可以保证文件名是固定的,不会被这些中间文件代替,有利于进行调试分析。

作者:TIPI Team