标签归档:PHP源码

PHP源码阅读笔记二十四 :iterator实现中当值为false时无法完成迭代的原因分析

PHP源码阅读笔记二十四 :iterator实现中当值为false时无法完成迭代的原因分析
在前面有一篇文章迭代器的简单实现及Yii框架中的迭代器实现中有一个简单的迭代器的实现,此处遗留了一个问题,当迭代的值中包含false时,使用foreach循环的时候在这个地方就结束了,原因是什么呢?

在鸟哥的blog中,很久以前一篇文章对iterator的实现作了一些说明:http://www.laruence.com/2008/10/31/574.html
但是并没有对false的值的处理作相关说明
顺着鸟哥的思路在Zend/zend_vm_execute.h文件的8131行找到相关的线索,如下所示代码:

1
2
3
4
5
6
7
8
9
10
/*  */
if (!iter || (iter->index > 0 && iter->funcs->valid(iter TSRMLS_CC) == FAILURE)) {
/* reached end of iteration */
if (EG(exception)) {
array->refcount--;
zval_ptr_dtor(&array);
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_JMP(EX(op_array)->opcodes+opline->op2.u.opline_num);
}

对于实现的简单的迭代器,iter->funcs->valid(iter TSRMLS_CC) 方法调用的valid()方法,
如果我们的值为false时,通过current返回的值为false,此时通过foreach访问时,遍历就在此中断了,程序会继续执行下面的代码,而不是这个循环了

解决方案
将数组中的key和value分开处理
在valid(),rewind(),next()方法中操作key,而不是value
仅在current中返回value
如文章迭代器的简单实现及Yii框架中的迭代器实现中的Yii框架中的CMapIterator的实现

PHP 源码阅读笔记二十三 :urlencode函数

PHP 源码阅读笔记二十三 :urlencode函数
有一段时间没有看PHP的源码了,最近一直在看以前买的书,有一些书已经看过一遍了,但是事隔一年又有不同的感受

urlencode函数在开发的过程中经常有遇到,它作用于字符串编码并将其用于 URL 的请求部分
urlencode函数的作用是编码 URL 字符串
string urlencode ( string str )
返回字符串,此字符串中除了 -_. 之外的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数,空格则编码为加号(+)。此编码与 WWW 表单 POST 数据的编码方式是一样的,同时与 application/x-www-form-urlencoded 的媒体类型编码方式一样。由于历史原因,此编码在将空格编码为加号(+)方面与 RFC1738 编码(参见 rawurlencode())不同。
在standard/url.c文件的493行,可以看到此函数的实现

1
2
out_str = php_url_encode(in_str, in_str_len, &out_str_len);
RETURN_STRINGL(out_str, out_str_len, 0);

在查看php_url_encode函数时,我纠结了好一段时间,因为它有一个#ifndef CHARSET_EBCDIC的编译判断,我一直将#ifndef看成了#ifdef,导致理解起来怪怪的
php_url_encode函数是一个遍历整个字符串,并针对每个字符进行替换的过程,
对于除了 -_. 之外的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数,
此处在编译时有针对EBCDIC编码的处理

空格则编码为加号(+)(
if (c == ‘ ‘) {
*to++ = ‘+’;
}

另外:将urlencode从代码中分离出来的独立程序请猛击:www.wqsl.net/blogs/web/php/10/200804/03-60.html
不过没有测试过此段代码

PHP源码阅读笔记二十二:array_splice函数

PHP源码阅读笔记二十二:array_splice函数
array_splice
(PHP 4, PHP 5)

array_splice — 把数组中的一部分去掉并用其它值取代
说明
array array_splice ( array &input, int offset [, int length [, array replacement]] )

array_splice() 把 input 数组中由 offset 和 length 指定的单元去掉,如果提供了 replacement 参数,则用 replacement 数组中的单元取代。返回一个包含有被移除单元的数组。注意 input 中的数字键名不被保留。

如果 offset 为正,则从 input 数组中该值指定的偏移量开始移除。如果 offset 为负,则从 input 末尾倒数该值指定的偏移量开始移除。

如果省略 length,则移除数组中从 offset 到结尾的所有部分。如果指定了 length 并且为正值,则移除这么多单元。如果指定了 length 并且为负值,则移除从 offset 到数组末尾倒数 length 为止中间所有的单元。小窍门:当给出了 replacement 时要移除从 offset 到数组末尾所有单元时,用 count($input) 作为 length。

如果给出了 replacement 数组,则被移除的单元被此数组中的单元替代。如果 offset 和 length 的组合结果是不会移除任何值,则 replacement 数组中的单元将被插入到 offset 指定的位置。注意替换数组中的键名不保留。如果用来替换的值只是一个单元,那么不需要给它加上 array(),除非该单元本身就是一个数组

array_splice函数调用的是php_splice函数,对于此函数的主要代码说明如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/* 创建并初始化返回数组,此返回数组为php_splice的返回数组,并非array_splice的返回数组(array_splice的返回数组为removed)*/
ALLOC_HASHTABLE(out_hash);    
zend_hash_init(out_hash, 0, NULL, ZVAL_PTR_DTOR, 0);
/*将数组中从开始到offset的元素拷贝到返回数组中 */
for (pos=0, p=in_hash->pListHead; pos<offset && p ; pos++, p=p->pListNext) {
 
    entry = *((zval **)p->pData);
    entry->refcount++;
/*更新返回数组 */
if (p->nKeyLength)
    zend_hash_quick_update(out_hash, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
else
    zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
}
/* 将需要移除的元素赋值给removed,即array_splice的返回数组 */
if (removed != NULL) {
    for ( ; pos<offset+length && p; pos++, p=p->pListNext) {
    entry = *((zval **)p->pData);
    entry->refcount++;
    if (p->nKeyLength)
        zend_hash_quick_update(*removed, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
    else
        zend_hash_next_index_insert(*removed, &entry, sizeof(zval *), NULL);
    }
} else /* 其它情况,跳过这些元素 */
    for ( ; pos<offset+length && p; pos++, p=p->pListNext);
/* 如果有元素需要插入,即有replacement参数 */
if (list != NULL) {
/* 对于每个元素,创建新的zval,拷贝并将它写入到返回数组中*/
    for (i=0; i<list_count; i++) {
        entry = *list[i];
        entry->refcount++;
        zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
    }
}
/* 拷贝剩下的元素 */
for ( ; p ; p=p->pListNext) {
    entry = *((zval **)p->pData);
    entry->refcount++;
    if (p->nKeyLength)
        zend_hash_quick_update(out_hash, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
    else
        zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
}
 
/* 重置数组的游标,相当于reset函数*/
zend_hash_internal_pointer_reset(out_hash);

整个过程:
1、拷贝从开始到offset的元素到返回数组
2、移除从offset开始到offset+length的元素
3、如果有替换的元素,将新元素插入到offset后面的位置
4、将offset+length后面的函数插入到返回数组后面
5、重置数组游标

EOF