测试驱动开发(Test Driven Development,英文缩写TDD)是极限编程的一个重要组成部分,它的基本思想就是在开发功能代码之前,先编写测试代码。也就是说在明确要开发某 个功能后,首先思考如何对这个功能进行测试,并完成测试代码的编写,然后编写相关的代码满足这些测试用例。然后循环进行添加其他功能,直到完成全部功能的 开发。代码整洁可用(clean code that works)是测试驱动开发所追求的目标。
使用PHP调用Httpwatch.controller 来分析httpwatch的log文件
httpwatch 的帮助文档中有关于使用javascript和ruby 、c#来分析httpwatch的log文件的例子,但是还没有PHP的;最开始我使用javascript来写了一点,但是在解析 Request.Stream 和Response.Stream时遇到了问题,不知道该怎么解,因为Stream是字节数组的形式出现的,怎么才能变成字符串呢?尤其是当 Response是gzip压缩过的,用javascript就更不好分析了,于是就想到了PHP。下面是PHP的一个小例子:
—— httpwatch.log.php ———– <?php
$ct = new COM("HttpWatch.Controller");
$log = $ct->OpenLog($argv[1]);
echo "The log file contains " . $log->Entries->Count . " entries\n";
$entries = $log->Entries;
$cnt = $log->Entries->Count;
for ($i = 0 ; $i < $cnt; $i++) {
$entry = $entries->item($i); // item is a method , not a array
echo $entry->ClientIp .":" . $entry->ClientPort. "n";
echo "=========== Request Stream =============n";
echo decode($entry->Request->Stream);
echo "\n";
echo "\n";
echo $entry->ServerIp .":". $entry->ServerPort ."n";
echo "=========== Response Stream =============n";
$arrResponse = getResponse($entry);
echo $arrResponse["header"];
echo "\n\n";
echo $arrResponse["content"];
echo "\n";
echo "\n";
echo "\n";
}
function getResponse($entry) {
$stream = decode($entry->Response->Stream);
$pos = strpos($stream, "\r\n\r\n");
$header = substr($stream, 0, $pos);
$content = substr($stream, $pos + 4);
$arrHeaders = getHeader($entry, "Response");
if ($arrHeaders["Transfer-Encoding"] == "chunked") {
$content = getChunkedContent($content);
}
if ($arrHeaders["Content-Encoding"] == "gzip") {
$content = gzuncompress($content); // 目前这里是有问题的,不知道为什么 }
return array("header"=>$header, "content"=>$content);
}
function getChunkedContent($content) {
$start = 0;
$result = "";
while(1) {
$pos = strpos($content, "\r\n", $start);
$size = hexdec(substr($content, $start, $pos - $start));
if ($size == 0) break;
$result .= substr($content, $pos + 2, $size);
$start = $pos + 2 + $size + 2;
}
return $result;
}
function getHeader($entry, $type) {
$cnt = $entry->$type->Headers->Count;
for ($i = 0; $i < $cnt; $i++) {
$header = $entry->$type->Headers->item($i);
$arrResult[$header->Name] = $header->Value;
}
return $arrResult;
}
function decode($stream) {
$cnt = count($stream); // 非常注意: 这里用strlen是不行的
for( $i = 0 ; $i < $cnt ; $i ++) {
$result .= chr($stream[$i]);
}
return $result;
}
?>
运行: php httpwatch.log.php a.hwl
关于对浏览器兼容性的一点点理解
原来对浏览器兼容性的理解主要是:
1. 要对浏览器兼容性做判断
2. 不是所有浏览器都支持的东西尽量不要用
今天看了QQ空间的一点代码,有所启发:
1. 要善于利用浏览器的一些特性;比如,现在的主流浏览器是IE和FF,IE中有很多功能是FF中所没有的,如果用js来实现这些功能可能是很大一段的js;按照原来的想法就是不去使用IE的那个功能,统一使用js来实现,这样自然没有兼容性问题。但是今天的想法却是: 在IE中就是用IE提供的功能,只有在FF中才通过js来实现; 这样做的好处就是,对于大部分的用户不需要下载那一大段的js了,对用户和服务器都是有好处的。有些功能是FF中有的,IE中没有,我们也可以这么来做。
2. 下面给出一段QQ空间的代码:http://b.cnc.qzone.qq.com/proxy.html
<html><head><meta http-equiv="Content-Type" content="text/html; charset=gb2312" /><SCRIPT>
document.domain = "qq.com";var b=false;
if (window.execScript)try{execScript("b=Asc(‘啊’)=-20319","vbscript");
}catch(e){status = "系统vbscript引擎可能已经被损坏或者反注册。尝试运行 regsvr32 vbscript.dll 命令有可能解决此问题";
}
if(!b){
document.write("<script language=’javascript’ src=’http://imgcache.qq.com/qzone/client/js/gbk.js’><\/script>");
}else{
document.write("<script language=’vbscript’ src=’http://imgcache.qq.com/qzone/proxy.vbs’><\/script>");
String.prototype.GBEncode=function(){return URLEncoding(this)}
}
</SCRIPT><script src="http://imgcache.qq.com/qzone/proxy.js"></SCRIPT><script>
if (typeof proxyLoaded=="undefined"){
document.createElement("SCRIPT").src="http://imgcache.qq.com/qzone/proxy.js?"+Math.random();
alert("proxy脚本无法正常下载,可能与您当地的网络状况有关系。\n请您关闭浏览器,重新打开空间进行尝试。");
}
</script>
</head></html>
怎样在Apache2下开启gzip压缩
原来听说使用script标签动态加载js时,加载源不能做gzip压缩,今天测试了一下,没这回事儿,不管ff和ie都没有这回事儿。
顺便了解了一下怎样在Apache2下开启gzip压缩。
- # 加载deflate模块
- LoadModule deflate_module modules/mod_deflate.so
- # 设置压缩频率,取值范围在 1(最低压缩率) 到 9(最高压缩率)之间
- # 不建议设置太高,虽然有很高的压缩率,但是占用更多的CPU资源
- DeflateCompressionLevel 3
- # Compress everything except images
- <Location />
- # 插入过滤器
- SetOutputFilter DEFLATE
- # Netscape 4.x 有一些问题…
- BrowserMatch ^Mozilla/4 gzip-only-text/html
- # Netscape 4.06-4.08 有更多的问题
- BrowserMatch ^Mozilla/4\.0[678] no-gzip
- # MSIE 会伪装成 Netscape ,但是事实上它没有问题
- BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
- # 不压缩图片
- SetEnvIfNoCase Request_URI \\.(?:gif|jpe?g|png)$ no-gzip dont-vary
- </Location>
利用httpwatch做web自动化测试和分析
httpwatch 是个不错的东西,它是web开发者的一把利器;另一把利器就是Firebug 。
关于httpwatch自动化测试和分析的例子详见httpwatch的帮助文档之: Automating HttpWatch
另外, 如果你有visual studio的话,可以在对象浏览器中选择“编辑自定义组件集”,添加httpwatch的com组件,这样查看httpwatch的api更加方便一些。非常推荐的哦
svn 命令行实例
关于命令行svn的学习:
检出: 需要 url 用户名 密码
注意url中的地址不能写根目录,checkout使用的http header里的方法也是很特殊的
D:\svn_test\test2>svn checkout "https://localhost:8443/svn/test2/" –username svnuser –password lijunjie ./
更新
svn update
添加
svn add
删除
svn delete
提交
svn commit -m “提交的注释”
或
使用vim编辑注释
svn commit –editor-cmd vim
比较
svn diff
和上一个版本比较
svn diff -r PREV
关于svnserve服务器的配置,参考svn-book.pdf
1. 安装 (略)
2. 配置/etc/svnserve.conf
#content , detail reference svn-book.pdf
[general]
password-db = /etc/svnserve.pass
realm = example realm
# annonymous users can only read the repository
anon-access = read
# authenticated users can both read and write
aut-access = write
3. 配置权限文件 /etc/svnserve.pass
[users]
junjie = junjie
4. 创建版本库
svnadmin create /var/svnroot/test1
5. 启动
svnserve -d -r /var/svnroot/ –config-file /etc/svnserve.conf
6. checkout
svn checkout "svn://phpor.net/test1"
svn checkout –non-interactive –username lijunji –password xxxx "https://phpor.net/" ./
7. 比较
svn diff -r 4974:17599 getst.php
8. 移动
svn move –force a b\
9. 提交
svn commit -m "comment for move "
10. merge
如: branches\branche1 是 trunk的一个分支,当truck有修改后,假设从版本11升到版本12,要想将版本11到版本12的修改在branche1里也
体现出来,则可以使用merge命令,即:
svn merge -r 11:12 trunk branches\branche1
这时branche1里面的文件也做了相应的修改,只是我们还需要人工确认一次,确认修改的没有问题后,再提交一次
svn commit -m "merge from trunk"
这时,相应的修改在分支里面体现为版本13,就是说分支里永远找不到版本12的修改
merge 的实质是diff and apply
11. 查看log
svn log –limit 3 -v
–limit n 查看最近n条更新
-v 显示修改的详细信息
-r 指定要查看的版本号,也可以指定日期
svn log -r{2009-05-10}:{2009-06-10} -v
在服务器上使用svn命令时总出现乱码,处理办法,使用icovn做转码:
svn log -r 4472 |iconv –from utf-8 –to gb2312
PHP 模块编写需要注意的一个问题—- php模块及函数名都定义成小写吧
这是一个困扰我很长时间的问题,曾经由于没有思路,一直搁置没有去追究,现在要对该模块做一些修改,就想顺便把这个问题给查明白了,事情是这样的…
我们写了一个PHP模块String2Hash,其实里面只有一个函数叫: String2Hash() ,只是要将一个字符串hash成一个整数,由于PHP中的整型都是有符号的,该算法涉及移位,求和,求模,等操作,用PHP实现比较麻烦,就用C写了一个PHP的模块。
编译完之后,执行:
php –re String2Hash
就报错如下:
PHP Warning: Internal error: Cannot find extension function String2Hash in global function table in Unknown on line 0
虽然有警告,但是该模块时可以正常加载和使用的; 一般来讲,程序员只关心error,不关心Warning的,但是我就想知道个究竟。
第一步: 先做一个helloworld模块,这个完全是生成的,只有很少的修改,结果是没有报错的。
第二步: 将自己的函数添加进去,开始报错。
第三步: 将自己的函数重命名为myhash, 编译不再报错。问题基本出现在函数名上了。可能是大小写的问题吧?突然想到报错提示,可能是global function table这个数组的key是区分大小写的?
第四步: 将String2Hash 重命名为string2hash, 为了保险,将所有出现的地方都改成小写吧,反正PHP函数是不区分大小写的,改成小写也无妨。 编译不再报错,测试通过,问题解决
have fun!!!
c代码翻译为PHP时遇到的问题
最近由于想把下面这段c代码翻译为PHP的,未能成功:
unsigned long myhash(const char *s, long mod){
unsigned char ch;
unsigned int h = 5381;
int len = strlen(s);
while (len > 0) {
ch = *s++ - 'A';
if (ch <= 'Z' - 'A'){
ch += 'a' - 'A';
}
h = ((h << 5) + h) ^ ch;
--len;
}
return h%mod;
}
遇到的问题是:
1. PHP 的整型总是有符号的
2. PHP 的整型在32位机上是32位的,在64位机上是64位的
期望能通过pack,unpack函数来实现,研究了半天,也没有实现。
还想搞清楚的问题:
一个负数 a 模上一个数 M, 和将a强制转换成无符号后模上相同的M,其结果之间是什么关系?
如:
-100 % 15 = -10;
((unsigned) (-100)) % 15 = 6
[root@login ~]# php -r "echo sprintf('%u',-100);"
4294967196
然后用bc算一下就知道了
关于gethostname系统调用
关于hostname的事情,我不知道是存放在系统的什么地方的,想知道时却发现是通过系统调用gethostname实现的,给系统调用在linux上只要包含<unistd.h>,就可以使用;
但是在windows上就没有在这个里面,windows上我没有使用visual studio,而是下载了一个dev-c++;因为对于命令行的东西比较熟悉一些,同样的程序在linux上是没问题的,但是在windows上就是编译不过去;
代码如下:
#include <unistd.h>
#include <iostream>
using namespace std;
int main(int ac, char ** av) {
char name[256];
size_t len = 256;
gethostname(name, len);
cout << name << endl;
return 0;
}
在windows上需要添加:
#include <winsock2.h>
而在编译时也需要把libwsock32.a 带上,如:
g++ -L D:\Program\DEV-CPP\Lib -o gethostname gethostname.cpp D:\Program\DEV-CPP\Lib\libwsock32.a
你可能会问,为什么已经使用了-L了libwsock32.a还要使用全路径,这个我也不知道,反正不给全路径就是找不到该文件。
虽然windows上也编译成功了,但是输出的结果却不是hostname命令所看到的,难道是这个libwsock32.a里面的gethostname的实现和hostname命令不太一样?通过denpendency woker这个小软件查了一下,使用的是同一个dll中的系统调用。 为什么结果不一样就不知道了,windows上的东西就是不好查。
apache 的AcceptMutex 的理解
当apache监听多个端口或多个ip上的端口时,多个子进程是怎么分工的呢?和只监听一个端口有什么区别吗?
1. 如果apache的多个进程只监听一个端口,则不需要AcceptMutex,每个apache进程都在执行accept操作,发现请求后就开始执行,执行完继续accept,多个进程之间不会相互干扰,这是由accept的机制来完成的。
2. 如果apache的多个进程只监听socket多于一个,则不能直接accept了,就必须使用select或poll机制来发现知否有请求需要执行,发现之后,再由accept来接受请求;然而select或poll没有accept那么能自动处理互斥的问题,这时候就需要一个“锁”了,就是所谓的AcceptMutex,AcceptMutex的实现形式有多种,这个不属于本文讨论的范围。
下面是apache在监听三个socket([16 17 18])时,其中一个进程的表现:
semop(524288, 0x81dd334, 1) = 0
select(19, [16 17 18], NULL, NULL, NULL) = 1 (in [17])
accept(17, {sa_family=AF_INET, sin_port=htons(52932), sin_addr=inet_addr("10.55.38.9")}, [16]) = 29
semop(524288, 0x81dd33a, 1) = 0
第一步:先获取锁(0x81dd334), 同一时刻只有一个进程能获取锁
第二步:开始select(对于apache2这里好像是poll,poll的效率更高一些),并且发现17需要处理了
第三步:accept 17
第四步:释放锁,这样其它进程就可以获取锁并处理后续的请求了