从php核心代码看require和include的区别

前言 五一长假归来,休息长时间很有点不习惯,回到已经有些不适应了。
见到鸟哥的一文:深入理解PHP之require/include顺序 http://www.laruence.com/2010/05/04/1450.html 忍不住继续再深入了一下下,在此记录一下深入的过程,以供以后查阅。

普及 在php手册中:

require() is identical to include() except upon failure it will also produce a fatal E_ERROR level error. In other words, it will halt the script whereas include() only emits a warning (E_WARNING) which allows the script to continue.
就是说在失败的时候,require是会中止php运行的,而include是可以继续运行的。
倒底有什么样的区别呢?我们带着这个疑问来一起进入PHP的核心代码。
下面是一个PHP运行过程的图(这个图是出自哪里的?鸟哥画的?) php

补习一下:lex是代码扫描器,扫描代码用的,yacc是Yet Another Compiler Compiler,作用是把任何一种代码的语法转成yacc语法,yacc就是解析器(真TMD绕)。
lex在c下的后缀是*.l yacc是*.y

正题 下面看作记录:

cc@cc-laptop:/opt/workspace$ svn checkout http://svn.php.net/repository/php/php-src/branches/PHP_5_3 php-src-5.3
从svn取最新的php源代码。
开始深入:
cc@cc-laptop:/opt/workspace/php-src-5.3$ find . -type f -name "*.l" -exec grep -Hn "require_once" {} \;
./Zend/zend_language_scanner.l:1093:"require_once" {
寻找lex代码扫描器文件中出现require_once的地方,zend_language_scanner.l的1093行。
1093 "require_once" {
1094 return T_REQUIRE_ONCE;
1095 }

然后再搜一下T_REQUIRE_ONCE,

cc@cc-laptop:/opt/workspace/php-src-5.3$ find . -type f -name "*.y" -exec grep -Hn "T_INCLUDE" {} \;
./Zend/zend_language_parser.y:52:%left T_INCLUDE T_INCLUDE_ONCE T_EVAL T_REQUIRE T_REQUIRE_ONCE
./Zend/zend_language_parser.y:985: | T_INCLUDE expr { zend_do_include_or_eval(ZEND_INCLUDE, &$$, &$2 TSRMLS_CC); }
./Zend/zend_language_parser.y:986: | T_INCLUDE_ONCE expr { zend_do_include_or_eval(ZEND_INCLUDE_ONCE, &$$, &$2 TSRMLS_CC); }

在985行附近,有这样一群代码:

internal_functions_in_yacc:
T_ISSET '(' isset_variables ')' { $$ = $3; }
| T_EMPTY '(' variable ')' { zend_do_isset_or_isempty(ZEND_ISEMPTY, &$$, &$3 TSRMLS_CC); }
| T_INCLUDE expr { zend_do_include_or_eval(ZEND_INCLUDE, &$$, &$2 TSRMLS_CC); }
| T_INCLUDE_ONCE expr { zend_do_include_or_eval(ZEND_INCLUDE_ONCE, &$$, &$2 TSRMLS_CC); }
| T_EVAL '(' expr ')' { zend_do_include_or_eval(ZEND_EVAL, &$$, &$3 TSRMLS_CC); }
| T_REQUIRE expr { zend_do_include_or_eval(ZEND_REQUIRE, &$$, &$2 TSRMLS_CC); }
| T_REQUIRE_ONCE expr { zend_do_include_or_eval(ZEND_REQUIRE_ONCE, &$$, &$2 TSRMLS_CC); }
;

于是乎,我们需要继续深入寻找zend_do_include_or_eval,

cc@cc-laptop:/opt/workspace/php-src-5.3$ find . -type f -name "*.c" -exec grep -Hn "zend_do_include_or_eval" {} \;
./Zend/zend_compile.c:4317:void zend_do_include_or_eval(int type, znode *result, const znode *op1 TSRMLS_DC) /* */

zend_do_include_or_eval中组装了一个结构体,ZEND_INCLUDE_OR_EVAL。

再在zend_vm_def.h中找到ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL, CONST|TMP|VAR|CV, ANY):
switch (Z_LVAL(opline->op2.u.constant)) {代码略}

中间关键的一句是:
new_op_array = compile_filename(Z_LVAL(opline->op2.u.constant), inc_filename TSRMLS_CC);

在zend_complie.h文件中:
ZEND_API zend_op_array *compile_filename(int type, zval *filename TSRMLS_DC);

这个函数定义在zend_language_scaner.l文件中,找出最核心的代码:

if (open_file_for_scanning(file_handle TSRMLS_CC)==FAILURE) {
// require与include的差别:错误信息的显示级别(有bailout和无bailout)
if (type==ZEND_REQUIRE) { //require时
zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename TSRMLS_CC);
zend_bailout();
} else {
zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename TSRMLS_CC);
compilation_successful=0;
} else {代码略}

继续追踪zend_message_dispatcher可以在main/main.c文件中找到php_message_handler_for_zend函数:

//include输出错误信息时的级别为:E_WARNING
case ZMSG_FAILED_INCLUDE_FOPEN:
php_error_docref("function.include" TSRMLS_CC, E_WARNING, "Failed opening '%s' for inclusion (include_path='%s')", php_strip_url_passwd((char *) data), STR_PRINT(PG(include_path)));
break;
//require输出错误信息时的级别为:E_COMPILE_ERROR
代码略

总结 和开头PHP手册所说完全一致,require和include的区别在于,出现错误时,一个是error一个是warning。

QCon回忆录

在前天(2010年4月24日),有幸参加了第二届的QCon全球企业开发大会。会上有来自\weibo等国际知名企业的技术高手,和来自国内一线互联网公司的高手,对相应研究的主题进行了深刻的交流。

大早爬起床来,困顿中来到大钟寺附近,寻找到目标酒店。

找到目标、进入,入口很,不需要有效证件,能找到自己的牌子就能进去了。领了写上自己名字的牌子挂上脖子,感觉质量差了点。

凭感觉上楼,无特别标识。找到主会场。

未遇到同事,领同声传译耳机,无身份证,不给领。正巧电话淘宝老同事正赴现场,帮领,一枚。

会议开始,现场满座。

第一场,万众期待的eclipse之父Erich Gamma没有出现,颇有些失望。取而代之的是一场敏捷开发管理的内容。相关的现场笔录在 http://www.54chen.com/architecture/april-24-live-stream-qcon-posts.html

第二场,的大侠,讲他们咋用memcached,这哥们具说是设计了个memcache的udp读取的方法,得了个佬的奖。同声传译的影响太大,断断续续的理解了些大概,感觉他们用的二级甚至三级的memcache的方案是很不错的。这哥们自己也同意,方法有些山寨,但好用才是王道。他一定不知道什么是“山寨”。

第三场,weibo的大侠上场,兄弟胡子好浓密呀,哪国人仕?胡子浓密的一定是高手。讲了他们在使用时间片的分步数据方案时的问题。

然后基本上就到了中午十二点了。。。开饭时间一到,拖了十分钟,结束了早上的会议。

同淘宝的老同事一起吃饭,不到四十分钟吃饭的时间。

回到主会议现场,下午是国内讲师。这时第一次见到了台上的Fenng,平心而论,要比头像上帅,还是特意处理过的?

然后第四场一开始,是阿里巴巴的国际站做镜像的一些经验。东西讲得不深,很大面上的来讲~~听一半,笔记本没有电了,撤到外面去上了个厕所。

回来的途中,遇到了Fenng。第一眼他没认出来。看牌子看出来了,看来我的头像也没有本人帅。。。拉拉家长换了名片后,他语重心长的说,科学院一定要坚持办下去。那情景,和某某革命电影情节类似,不过我一时想不出来了,很久没有看革命电影了。激动之下,我解释了下为什么是“五四陈科学院”,是为了纪念一场胜利的革命(1919年5月4日在爆发的人民彻底的反对帝国、封建的运动)和70年零一个月后另一场革命未胜利的惨剧,借以勉励,同志仍需努力的道理。

再进入到现场,阿里的大侠已经行文渐尾。

第六场是去哪儿网站的吴永强大侠讲他们的一些运维经验,这哥们也是雅虎出来的,雅虎的运维肯定被继承了过去,所以稳定是没得说的。

掌声中,进入到第六场。人人网的高级技术总兼,我们的老大sir,飘逸地带着mac大本上场。这里要多讲一讲,sir一向重视技术,对人人网从小到大的发展了如指掌。几个主题下来,从人人网的发展,到我们的nuclear项目介绍,再到王志亮大侠的rose框架,行云流水。

再掌声中,大家对nuclear的设计很感兴趣,会后纷纷在外面探讨起来。按压不住,我也去和前辈们过过招。这一过招,认识了不少朋友,其中还有来自盛大的俞前辈,对dynamo原理理解深刻,经验丰富,似乎盛大在考虑开拓S3类似的业务。另外也和weibo上一系列的同志对上了号,交换了名片。

一番探讨之后,回到现场,新浪微博的大侠timyang已经讲了大半了,听了个大概,作了些笔录。

时间过得很快,一天过去了,最后的时候,找到timyang简单聊了下新浪微博的一些动向。

然后,同原同事、现场weibo上一些朋友,一一告别。Fenng和infoQ的老大(神似)忙里忙外,没顾上去打招呼,领了个t-shirt作纪念,离开。

酒店里一天没有自然光线,感觉很晕。。。(不排除是因为起太早)

国内类似的技术聚会,聚到一起的大侠们越来越多啦。。。感叹一下。

4月24日QCon现场直播流水帖

才连接上3G,开始直播

Eric Gamma因为冰岛火山灰原因,没能到,更换成了Scurm的

 

1.scrum agile
30+years管理经验
存在的问题:
1.在公司和团队的环境中,存在沟通问题
2.组织的管理层没有关注到知识性的工作与体力性工作之间的差异
3.高技术的工作人员存在优先选择的问题
4.人们觉得我们不了解我们工作的复杂性

agile宣言
四项价值观:
个体和交互重于过程和工具
可工作软件重于详尽的文档
。。
12个原则:

要经常性地交互可用的软件,周期从几周到几个月不等到,交互的时间越短越好(经常和客户沟通)
最重要的一点:激励项目人员,调动工作经验,钱不是万能的
非常有意思的原则
面对面的交谈
可用的软件是衡量进度的主要指标
项目周期需要保持长期稳定,要有节奏,不要过多催促
技术的精益求精及对设计的不断完善将提升敏捷性
尽最大可能简洁
允许团队自己放手去做,相信你的团队成员,他们会犯错误但是可以理解的
团队经常地自我反省如何提高技术能力

Scrum 不仅仅用在软件上,其他用途也是可以的
Scrum的角色职能
ScrumMaster的误解:这个角色不是项目经理,是为这个团队提供服务的。5-12个人组织团队
产品的backlog:产品功能表
任务表:sprint backlog 在实现上述的功能,我们要做些什么,这并不是真正意义上的时间表

sprint planning:30天要完成这个工作
日常会议
sprint review
sprint retrospective

是一种风险管理的方式,把风险降到最低,特别是大规模开发某个程序的话,可以规避这种风险
可以经常性问客户这样行不行
它是一个非常科学的方式

三个原则能解决很多问题:
询问团队人员
检查代码、检查工作程序
快速提供成绩

有许多问题会导致失败:
1.文化和不信任
2.投资人之间的交流不畅通
3.设计人员同时做多个项目
4.没有遵循sprint
5.外行人加以干涉
6.团队成员没有全力投入(我帮不了你,这不是我的事情)
7.scrum master成了项目经理
8.工作不够迅速

被误解的一些:
Done:必须是可交互了
团队没有投入到改善工作中去
他们经常去探索问题而不解决
对个人的奖励而不是团队奖励

Scrum doesn't work in china
这哥们的图很牛B

是共产,是可以使用scrum的

文化对scrum的影响

讲一个指数,power distance index
另一个:Cultural agility index
个人和集体的文化

根据agility index 在是可行的 而且是更有利的执行

管理人员的指令

专业经理人官僚的作风

的小道消息文化

绩效的评估

避免员工与家庭分开

对成员要有耐心

下一个是的memcache实战:

memcache@ 企业的周五,穿正装
how big is ?
4亿用户
1000个服务器???错了,上千个服务器

memcache规模
400M gets/s
28M sets/s
2T
收:9.7M/s

图很cool
terms
tier:层级
cluster:数据中心
ToR:top of rack switch
memcache/memcached

游戏中的memcache规则
get : on miss query data and set(这点我赞同)

short profile
PHP并行数据查询:php polling IO  / 有一个PHP异步IO的方案?
用户的好友信息缓存效果并不是很好
把大数据和小数据分类

序列化与压缩
fb-serialization:快3倍 小30%
gzompress serialized string

mcproxy
scaling servers

增加服务器
查5000个好友的时候 用UDP解决这个问题 测量时间
热门的页面:把key复制 公布热键
replicated keys

举例说在profile表里的一个字段confirmed int:Mirrored keys

mutexes:锁 (这个方案用图演示的,感觉会有可能出现一次出不来信息)

公共主页的数据更新方案:多级memcache方案

删除的一致性:schema-version

delete replay:
delete can fail
log failed deletes
用了iptable

McSQL

测试:
你的动作要快,不要影响系统

python and ctypes
write tests not test frameworks

memcache今后的发展规划
有限的解决方案

下一位是:weibo

weibo的大胡子哥们 what's real-time data?

在讲什么是
memcached for reads

同声传译太烂了。。。
根据主键进行分区的例子

根据主键进行分区的例子和根据用户进行分区会有困难

weibo是根据时间来分区,一开始考虑不是很好的办法?这是一个非常临时性的分区方案,但也能满足99%的查询
将来的解决方式:nosql

实时数据的解决方案:适时的服务

通过offline的解决方案

发布一个新的在timeline里排序,当你的时候开始推送

通过提前的处理,可以提高处理量

一个表格对比08和10的s速度

只把数据保存到了内存里,是昂贵的

发一博给所有人的时候,尽量在一秒内,而且要保证120万的推送量

social graph:

所有数据都从内存里读,单元性地读取数据,把问题从大化小。
按照用户分解数据

数据二级索引
(看上去是复制两个维度的数据)

flockDB?开源的

分布性的存储中的数据一致性

分区和索引变得越来越复杂了。。。

(时间不够了。。)

吃饭时间,下午再来。。。

回来了,在大宴会厅听阿里巴巴国际站的架构。主要是境像。

数据同步1.0

SQL复制的方式会带来数据冲突,比如说批量作。

双站点应用拆分的问题。

CAP原理

写数据一致性和可用性

读数据容忍一致性,境像

结论:读应用更容易实现跨IDC的部署

数据同步会放大数据不一致

从设计上避免数据完整性的问题

设计业务流程的时候需要避免跨IDC的WEB Flow

阿里的读应用基本是从搜索来或者是cache来

缺陷:

双向同步的冲突

解决业务冲突

双站点扩展性已经好了很多

进入第三阶段:多站点 全境像

目标是所有机房的所有应用都必须有跨IDC的备份,数据亦然。

数据同步瓶颈分析

受制于为了满足数据一致性而对写入作进行的排序

数据同步2.0设计原则

在数据库层面记录数据变更

并行所有可以并行的内容

有选择的侵入业务

merge作

写应用的镜像方案:

双master在实现复杂性上会带来问题

可维护性提高

(笔记本没电了。。。)

后面还有“去哪儿”网站架构、人人网架构、新浪微博架构。。。晚上回去再补上来。。。

PHP上传进度条深度解析

随着互联网的发展,越来越多的技术开始注重用户体验,以人为本才是长久之道,于是在上传的时候,大家都不再满足一个单一的“浏览”按钮,纷纷推出了带上传进度条的功能。而作为解释型语言的PHP,如何做到对上传文件的检测,如何实现上传进度条以其背后的原理,54chen将在本文中一步步展开。

一. 实现篇

一般情况,用PHP实现上传进度条就下面两种方法:

1.APC扩展(作者是PHP的创始人,5.2后PHP已经加入APC扩展)

2.PECL扩展模块 uploadprogress

不论是APC还是uploadprogress,都需要编译源码,因为原有的PHP函数根本不可能读取到临时文件夹里的东西。下面来看如何使用以及关键的代码:

APC实现方法:
1.安装APC
2.配置php.ini,设置参数 apc.rfc1867=1
3.关键代码:
if ($_SERVER['REQUEST_METHOD'] == 'POST') {  //上传请求
$status = apc_fetch('upload_' . $_POST['APC_UPLOAD_PROGRESS']);
$status['done'] = 1;
echo json_encode($status);  //输出给用户端页面里的ajax调用,相关文档请自己寻找
exit;
} elseif (isset($_GET['progress_key'])) {   //读取上传进度
$status = apc_fetch('upload_'.$_GET['progress_key']);
echo json_encode($status);
exit;
}
uploadprogress实现方法:
1.使用PECL 安装uploadprogress
2.php.ini里面设置 uploadprogress.file.filename_template = “/tmp/upd_%s.txt”
3.关键代码:
if($_SERVER['REQUEST_METHOD']=='POST') {
if (is_uploaded_file($_FILES['upfile']['tmp_name'])) {
$upload_dir = 'your_path/';
$ext        = strrchr($_FILES['video']['name'], '.');
$sessid     = $_POST['UPLOAD_IDENTIFIER'] ;
$tmpfile    = $upload_dir . $sessid;
$sessfile   = $upload_dir . $sessid .$ext;
if (move_uploaded_file($_FILES['upfile']['tmp_name'],$tmpfile)) {
//上传成功
}
}
} elseif (!empty($_GET['sessid'])) {
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Content-Type:text/html;charset=UTF-8");
$unique_id = $_GET['sessid'];
$uploadvalues = uploadprogress_get_info($unique_id);
if (is_array($uploadvalues)) {
echo json_encode($uploadvalues);
} else {
//读取进度失败,另外处理逻辑
}
}
二. 原理篇

注意上一篇中的红色函数。

下载到uploadprogress1.0.1进行源码分析,在代码中作了注释。

static void uploadprogress_file_php_get_info(char * id, zval * return_value)

{

char s[1024];

char * filename;

char * template;

FILE *F;

TSRMLS_FETCH();

template = INI_STR("uploadprogress.file.filename_template"); <<这里读取设置好的模板

if (strcmp(template, "") == 0)  {

return;

} else {

filename = uploadprogress_mk_filename( id, template );<<<存在的话,会创建

if (!filename) return;

F = VCWD_FOPEN(filename, "rb");

if (F) {

array_init(return_value);

while ( fgets(s, 1000, F) ) {<<<从流中读取一字符串 *s结果数据的首地址;1000-1:一次读入数据块的长度,其默认值为1k,即1024;F文件指针

char *k, *v, *e;

int index = 0;

e = strchr(s,'='); <<<查找字符串s中首次出现字符=的位置

if (!e) continue;

*e = 0; /* break the line into 2 parts */

v = e+1;

k = s;

/* trim spaces in front of the name/value */

while (*k && *k <= 32) k++;

while (*v && *v <= 32) v++;

/* trim spaces everywhere in the name */

for (e=k; *e; e++) if (*e <= 32) { *e = 0; break; }

/* trim spaces only at the end of the value */

/* http://pecl.php.net/bugs/bug.php?id=14525 */

//for (e=v; *e; e++) if (*e <= 32) { *e = 0; break; }

if (v != NULL) {<<<当文件有内容时

for (index = strlen(v); index > 0; index--) {

if (v[index] > 32) break;<<<累计

v[index] = 0;

}

}

add_assoc_string( return_value, k, v, 1 );

}

fclose(F);

}

if (filename) efree(filename);

return;

}

} 在源码中还能发现:

PHP_MINIT_FUNCTION(uploadprogress)

{

REGISTER_INI_ENTRIES();

php_rfc1867_callback = uploadprogress_php_rfc1867_file;

return SUCCESS;

} 在MINIT中修改了php_rfc1867_callback,抽取uploadprogress_php_rfc1867_file的关键代码:

upload_id = emalloc(strlen(*e_data->value) + 1);

strcpy(upload_id, *e_data->value);

progress->upload_id = upload_id;

progress->time_last  = time(NULL);

progress->speed_average  = 0;

progress->speed_last     = 0;

progress->bytes_uploaded = read_bytes;

progress->files_uploaded = 0;

progress->est_sec        = 0;

progress->identifier = uploadprogress_mk_filename(upload_id, template);<<<在指定的模板位置放下了临时文件

progress->identifier_tmp = emalloc(strlen( progress->identifier) + 4);

sprintf( progress->identifier_tmp, "%s.wr", progress->identifier ); 关于php_rfc1867_callback是什么,可以看哥学社另一哥鸟哥的分析 http://www.laruence.com/2008/11/07/586.html

三.总结

在探测临时文件大小的时候,APC和 uploadprogress其实是大同小异的方法,先记录,再取大小百分比。

关于哥学社: 哥学社是一个博客组织,以提高原创技术博客质量,为成员博客增加更有效评论,由来自腾讯、百度、盛大、人人网、新浪、奇虎、TOM等各大企业的哥们共同起草发起。

关于五四陈:
54chen(陈臻),人人网分布式存储研究人员,业余时间混迹于各技术组织且乐此不疲。目前关注实施PHP培训。对flex等前端技术有一点研究。
个人技术站点:http://www.54chen.com/ 。可以通过电子邮件 cc0cc@126.com 联系到他。

本文提及的文章:

http://www.php.net/releases/5_2_0.php

http://www.ibm.com/developerworks/cn/opensource/os-php-v525/

http://www.laruence.com/2008/11/07/586.html