IE 盒模型

很多网站在写HTML的时候,没有指定DTD,也就是没有设置DOCTYPE。

上学的时候,老师说,DreamWeaver 生成的页面,头部的DOCTYPE都是垃圾信息,应该直接写成<html>。

学院派真是误人子弟……

W3C标准是要制定DOCTYPE的,最显著的区别,我认为就是IE显示Div等元素时采用的盒模型问题。

也就是说,对margin、border等样式所占的面积,是算在Div内部还是外部?

在设定DOCTYPE之前和之后,显示方式是完全不同的。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<style type="text/css">
#box    {
  width:720px;!important;    /* For Modern Browsers */
  width:704px;            /* For IE */
}
</style>

可以在

http://leakon.googlecode.com/svn/trunk/leakon/html/ie_box/

看到源码。

通过设置dtd参数,可以看到在两种DOCTYPE下显示方式的不同。

但在Firefox等现代浏览器下,两种显示方式是一样的。

Symfony 温故而知新

使用Symfony的同学,连接数据库时,如果想指定编码,需要在得到数据库Connection的时候发送一条SET NAMES UTF-8语句,以保证统一。

针对这个问题,我以前写过2篇Blog,地址:

里面是通过修改Creole的源码,symfony/vendor/creole/Creole.php,加入一个setUTF8方法。

这个方式管用,但不是最好。

这两天看了Symfony的 Code snippets ,发现很多Symfony的运行机制,看到一个FilterChains,可以由用户添加一个Filter到调用链中,可以在系统初始化时自动运行。

利用这个特点,把设置连接编码的语句,添加到一个filter中,每次自动运行:

<?php
// 首次执行时,设置数据库连接编码为 GBK
class FilterGBK extends sfFilter {
    
public function execute($filterChain) {
        
// execute this filter only once
        
// 这里确保了这几行代码只执行一次
        
if ($this->isFirstCall()) {
            
$connection = Propel::getConnection();
            
$query = 'SET NAMES gbk';
            
$statement = $connection->prepareStatement($query);
            
$statement->executeQuery();
        
}
        
// execute next filter
        
$filterChain->execute();
    
}
}

再编辑 apps/front/config/filters.yml 文件,在开头加入:

FilterGBK:
  class: FilterGBK

这样就可以达到目的,而以前的实现方式需要修改插件源码,无法在一个系统中运行多个Symfony项目。

随着逐渐深入Symfony,并反复查看文档和代码,可以学到很多Web开发的经验和技巧。

这回,深刻体会到了“温故而知新”的内涵。

Symfony Apache Url Rewrite

今天在服务器上新装了apache,把用Symfony开发的后台程序部署到服务器上,发现生产版本的环境无法使用,居然报404 Not Found。

刚开始没想到是什么原因,反复翻文档,检查虚拟主机的配置,和目录权限,以及.htaccess文件,都正确无误。

在自己的Windows环境是好好的,但在服务器上却不行。

后来就拿.htaccess开刀,因为受到一点Apache手册的启发,就是在.htaccess中添加一些错误代码,以检查.htaccess是否正确运行。

其实本来就应该能想到这样做的,只是觉得问题可能出现在其他地方。

果不其然,就是.htaccess中的<IfModule mod_rewrite.c>,在这个分支里面随便写任何东西,都不会起作用。

看来Apache没有正确安装mod_rewrite.so。

一时也不知道如何验证,后来在网上查了半天才查到。

先列一串命令吧:

  • apache/bin/httpd -l
    列出当前环境中安装了哪些module
  • apache2/bin/apxs -c mod_rewrite.c -lgdbm
    在Apache源码路径的modules/mappers/中,可以找到mod_rewrite.c等所有模块的源码,在这个路径执行apache2/bin/apxs(注意,这里的apxs是apache安装的绝对路径),就会自动编译这个模块,生成.so文件到modules/mappers/.libs/,刚才那个mod_rewrite.so就生成在这里
  • apache2/bin/apxs -i -A -n rewrite mod_rewrite.so
    在modules/mappers/.libs/这个目录下执行上面的命令,会在apache2/conf/httpd.conf中加入一行
    #LoadModule rewrite_module     modules/mod_rewrite.so
    注意前面是带#的
  • apache2/bin/apachectl configtest
    检查当前的配置文件语法是否正确,不用等restart不成功再去检查错误了

通过这样几步,检查没问题后,重启apache,mod_rewrite模块就安装完成了。

再打开Symfony的后台,所有页面都可以正确打开。

我通常都在自己的笔记本上开发程序,用xampp套件,很多常用模块都是默认安装好的,使用之前只要把httpd.conf文件中相应模块前面的#去掉就可以了,一下子把程序部署到服务器,很多模块都没装,还真是不适应。

编码检查 UTF-8 浏览器编码

编码问题一直是做网站开发的工程师们很头疼的事,如果你希望自己的网站能够被更多不同语言环境的人浏览和使用,那就一定要解决好编码的问题。

我的经验是,从HTML页面的编码,到PHP程序文件的编码,到数据库的设计以及与PHP之间连接的编码,全部使用UTF-8,这样就能保证你的页面不会出现乱码。

不要总是觉得,你的网站是给中国人用的,不给其他国家的人使用。如果你的中国用户希望保存一些日文、韩文等亚洲文字, 你也没有理由拒绝吧?

还是在一开始就解决好编码的问题为好,做网站,免不了这一步,今天不做,以后迟早要还的。

如果你认为我说的毫无道理,那我给大家引用一篇W3C组织的官方说明吧:

http://www.w3.org/International/questions/qa-forms-utf-8.en.php

注意回答部分的第一句:The best way to deal with encoding issues in (X)HTML forms is to serve all your pages in UTF-8.

用UTF-8吧,肯定没错的,你可以避免很多很多问题。

不过,就算页面使用了UTF-8,也还是会遇到一个问题:

浏览器接收编码的转换问题。

就这个问题的说明,我做了一个测试页面,可以在我的Google SVN下载:

http://leakon.googlecode.com/svn/trunk/leakon/php/detect_utf8/

在浏览器里打开test.php页面即可。

这个页面是UTF-8编码的,如果你不是用SVN软件CheckOut而是直接复制的源码,请记得要把test.php这个文件保存成UTF-8格式。

这个页面有一个Form表单,你可以在输入框内输入中文,然后看地址栏里word字段的值,一个汉字对应3个%,因为UTF-8是变长编码的,针对不同的文字,%的数量是1-4个。

如果你熟悉GBK编码的页面,应该注意到每个汉字对应2个%,因为汉字都是双字节编码的。

这个时候,如果你足够细心,应该可以发现一个冲突的问题。

如果,我在浏览器的地址栏的word字段后面直接输入汉字,得到的是什么结果呢?给大家一个提示,这里最好使用Firefox浏览器,如果你在地址栏输入中文,Firefox会按照GBK的编码方式,按双字节编码,也就是一个汉字对应2个%。

可是你的页面默认是接收UTF-8编码的字符的,给你一个GBK编码,你会解析成乱码。

很多网站存在这个问题,包括Google和Yahoo。 他们只对各自的中文网站就解决了这个问题,英文的和日文的都没有处理。下面我们逐一测试一下:

我们给一个测试用例,中文:百度;GBK:%B0%D9%B6%C8;UTF-8:%E7%99%BE%E5%BA%A6。

看到了吧,GBK是4个%,UTF-8是6个%。

  1. http://www.google.cn/search?hl=zh-CN&q=%E7%99%BE%E5%BA%A6&btnG=Google+%E6%90%9C%E7%B4%A2&meta=
  2. http://www.google.cn/search?hl=zh-CN&q=%B0%D9%B6%C8&btnG=Google+%E6%90%9C%E7%B4%A2&meta=
  3. http://yahoo.cn/s?p=%E7%99%BE%E5%BA%A6&v=web
  4. http://yahoo.cn/s?p=%B0%D9%B6%C8&v=web
  5. http://www.google.com/search?hl=en&q=%E7%99%BE%E5%BA%A6&btnG=Google+Search
  6. http://www.google.com/search?hl=en&q=%B0%D9%B6%C8&btnG=Google+Search
  7. http://search.yahoo.com/search?p=%E7%99%BE%E5%BA%A6&fr=yfp-t-501&toggle=1&cop=mss&ei=UTF-8&vc=&fp_ip=CN
  8. http://search.yahoo.com/search?p=%B0%D9%B6%C8&fr=yfp-t-501&toggle=1&cop=mss&ei=UTF-8&vc=&fp_ip=CN
  9. http://www.google.co.jp/search?hl=ja&newwindow=1&q=%E7%99%BE%E5%BA%A6&btnG=%E6%A4%9C%E7%B4%A2&lr=
  10. http://www.google.co.jp/search?hl=ja&newwindow=1&q=%B0%D9%B6%C8&btnG=%E6%A4%9C%E7%B4%A2&lr=
  11. http://search.yahoo.co.jp/search?p=%E7%99%BE%E5%BA%A6&ei=UTF-8&fr=sfp_as&x=wrt
  12. http://search.yahoo.co.jp/search?ei=UTF-8&fr=sfp_as&p=%B0%D9%B6%C8&meta=vc%3D

以上是对Google和Yahoo两大搜索引擎的中文、英文、日文网站进行UTF-8、GBK编码的访问。

可以看到,2家公司只对中文网站做了编码检查,发现不是UTF-8就对关键词进行编码转换,得到了正确的结果,英文和日文都没有处理,给GBK编码时得到的是乱码。

他们就忽略了英文和日文用户搜索中文的需求。

其实,这个问题不是不可以解决的,中文网站都做到了,其他语言的怎么就不行呢?

这个不讨论了,还是说说怎么检查UTF-8编码吧。

其实就是一段正则表达式:

$regex    = '/^('
.
'[\x09\x0A\x0D\x20-\x7E]|'        # ASCII
.
'[\xC2-\xDF][\x80-\xBF]|'        # non-overlong 2-byte
.
'\xE0[\xA0-\xBF][\x80-\xBF]|'        # excluding overlongs
.
'[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|'    # straight 3-byte
.
'\xED[\x80-\x9F][\x80-\xBF]|'        # excluding surrogates
.
'\xF0[\x90-\xBF][\x80-\xBF]{2}|'    # planes 1-3
.
'[\xF1-\xF3][\x80-\xBF]{3}|'        # planes 4-15
.
'\xF4[\x80-\x8F][\x80-\xBF]{2}'    # plane 16
.
')*\z/x';

大家从SVN里可以下载源码。

我封装了一个类,DetectUT8,用于检测和转换编码。

过程是这样的:如果检查到不是UTF-8编码,就进行GBK -> UTF-8的转换。因为在地址栏输入的非ASCII字符都会按照GBK编码。

转换函数会检查系统是否启用了 mbstring 函数库,如果没有,则改用 iconv 转换。

这里要注意一点,不要把UTF-8写成utf8,也不要写成其他格式,iconv 的检查比较严格。

如果内置了 mbstring 函数,还是用 mb_detect_encoding 比较保险,但经测试表明,用上面这段正则,编码检测的成功率和 mb_detect_encoding 没有差别。

还要重申一个问题,编码检测的可靠性不是 100%,但应用这种方法已经极大地改善了用户体验,建议大家都采用这种方式。

PHP 文件锁 flock 负载均衡

最近有个项目,采用单台前端服务器提供Web服务,程序需要实时访问后端服务器。后端一共有几十台服务器,但有压力限制,单台负载不能过高,必须做负载均衡。

最简单的方式是用随机数,前端来请求的时候,随机挑选一台后端服务器,但这并不能保证压力平均分布,很有可能在某一段时间内请求都落到同一台服务器上,很容易导致这台服务器停止服务。

后来想到用文件锁的方式,来标记访问计数,顺序访问后端的每一台服务器,让每一台服务器一个周期只被访问一次。

在进行了多次功能测试和压力测试后,验证了这种想法的可行性,然后写了一个IDService类,封装了整个过程。

我在Google提供的SVN服务器上保存了源码,大家可以在

http://leakon.googlecode.com/svn/trunk/leakon/php/flock/flock.php

这个地址看到源码,或者用SVN工具CheckOut到本地。

核心过程,就是初始化的时候给一个ID范围,默认是从0开始,如果你的server_count是32,那么调用getId()方法的时候,我会顺序给你31至0这32个ID,采用文件锁就是考虑到并发请求之间彼此独立,一个进程读数据文件的时候要加独占锁,解锁前,其他进程无法读取数据文件。

ID分配给你了,每个ID对应哪个服务器,就是你自己做映射的事了,保证了这个模块的无关性和独立性,和其他所有模块保持无耦合。

这是在PHP5的环境下写的,const 定义了3个类常量:

LINE_FEED 是换行符,Windows 下是 \r\n,Linux 下是 \n,只是为了方便测试的时候实时查看数据,可以是任意字符,只要不是数字就OK;

MAX_LOAD 是计数器的最大值,计数器都是从0开始,如果有任何一个ID达到了最大值,则所有ID计数器全部归零,开始新的一轮计数,其实这个设置只要大于0就可以,最好不要太大,因为存储数字也是要占用存储空间的,越小,id_data_file的尺寸就越小,硬盘读取就越快;

DATA_BLOCK 是设置一次读文件的数据大小,硬盘的一个文件至少要占一个簇,一般文件系统一个簇是4K,这个取值要跟ID的总量有关系,如果你的LINE_FEED是\n,MAX_LOAD是99(采用文本方式存储,2字节),那么一个ID占用3个字节,如果你有100个ID,那么数据文件占用空间就是300Byue,因此DATA_BLOCK设置为300是最佳值,需要注意,如果ID范围变大,需要同步更改此值,因此我默认设置了2048字节,小于硬盘的一个簇,相对于300字节来说没有性能损失,因为都在一个簇内,数据存储是连续的,硬盘只需一次寻道和一次读写。

源码里有使用说明,很简单,在实例化对象的时候指定ID范围和数据文件位置即可。

已经经过测试,给一些压力测试数据吧:

在AMD3000+和7200转80G硬盘的台式机环境,可以提供到 1300+次/秒 的速度,此时磁盘IO是瓶颈;
换上Linux服务器,具体配置不太清楚,反正是SCSI硬盘,100多G,只是开发用机,性能并不高,但可以提供 5000+次/秒 的速度。

综合2中环境的测试数据,以目前前端服务器的最高负载(最高也就 200+次/秒),以及项目的实际负载,此代码性能足够满足需要。

Web在线任务管理

今天终于找到一个好用的在线任务管理网站:

Remember The Milk

http://www.rememberthemilk.com/

中文名怎么翻译呢?别忘了喝奶?

挺有意思的,小奶牛的图标很可爱,一下就吸引了我,让我决心好好研究一番。

是我自己要寻找这样的网站,最近好忙,每天都有好多工作要做,还有好多事想做。事情总是这样,有时候突然想做点什么,但工作很忙,没有时间;等周末休息的时候,有时间了,却突然不知道该做点什么。

工作的时候,会有很多很多任务,如何安排这些事,每件事应该在什么时候完成,都应该有预先的安排。新的工作环境,对我有很大影响。这里做事都必须有安排。

每周的开始,都要列出本周要完成的任务,以及每个任务的时间点。而且要明确任务结果,必须设置预期结果。

比如,今天是周一,制定本周计划:

  1. 某项目的升级详细设计,周三完成文档,周五完成评审;
  2. 某项目的升级模块,周二完成开发,周三完成测试,周四完成上线;

注意“完成”两个字,每件事,都要早明确的时间点,取得明确的结果。

这里不能使用应该、差不多、也许、大概和基本等等模糊的词汇。

现在我的工作状态越来越好,我觉得这背后的原因,就是做事有安排,合理分配,按时完成。

不过,我还缺少一个工具,来帮我规划个人任务。

以前我是写便签纸,就是黄色的小方块,上面有一条不干胶,记录我今天要做什么。

便签容易写,但不容易改,小小的空间,记不下什么东西,而且不容易保存,过后也想不起来都做了什么。

以前有人教我用Outlook的任务管理来提醒我什么时候该做什么。

我用着不习惯,因为这些都是提醒工具,而互联网工作的性质就是要应对突如其来的各种事件。

就算我订好了下午3点开始写文档,但可能2点半的时候发现线上服务的Bug,必须立即修复,等改完的时候已经是4点半了。当然,3点的时候在屏幕上出现提示框提醒我写文档,但那个时候我可能去理会可能去处理吗?事情过去了,很可能我就会忘了,也许直到第二天早上才会想起来昨天的事情没做完。

这类例子不用再举了,大家肯定都有各自的经历。

今天上午,搜了一下在线任务管理,找到了一个网站列表:

19个在线个人任务管理系统

我一眼就看见了这个RememberTheMilk,小奶牛还挺显眼的。

进去试用了一下,都是Ajax开发的,用户体验很好,可以按标签区分不同类型的任务,每个任务可以指定不同的优先级,还有详细的设置,感觉很好用。

我把一天的工作都写了进去,然后一项一项做,做完一样改一下状态,再做另一个,感觉一天都很有条理,也不会遗漏细小的任务。

具体的设计,很人性化,真的是个不错的工具。

我喜欢这样的网站。

Remember The Milk

简单快捷地管理日常任务。
设计优良的界面让您管理待办事务更加愉快。您甚至可以使用类似下周五两周内来定义事务到期时间。当然您也可以使用键盘快捷方式加速您的日程管理。

Remember The Milk

提醒,无处不在。
通过电邮、短信和即时通讯软件(支持AIM, Gadu-Gadu, Google Talk, ICQ, Jabber, MSN, Skype 和 Yahoo! )接收提醒。

Remember The Milk

按您的方式管理信息。
您可以根据需要创建任意多个列表。所有事件都可以定义标签,并且您可以通过“标签云”看到需要待办的事务。另外,您也可以针对事件创建便签记事。

Remember The Milk

定位您的任务。
使用地图来查看您的任务在现实世界所处的地点,查看他们是否就在附近,并且有计划的以最佳方式完成任务。

Remember The Milk

合作搞掂。
通过你的联络对象或整个世界分享、邮寄和发布任务与列表。提醒亲友做家务。

Remember The Milk

无论何时何地添加任务。
添加任务像发射一封邮件(甚至是打电话)一样简单。在网上查看一个重要的约会?用快速添加项将它添加到你的列表中。

Remember The Milk

随时随地访问你的待办事务。
用你支持web的手机来查看任务。打印表现未来工作的整个列表或者一份星期计划手册。用Apple iCal或者Google Calendar在你的日历中查看工作。订阅使用Atom/RSS的feeds。

Remember The Milk

分配您的时间。
看看今明两天该做的事与你错过未做的事情。设定其为优先,估计你的时间并作适当的推迟。设定每周重复的事情或者两个月后还要做的事情。

Remember The Milk

智能搜索。
您可以使用高级搜索功能查找事件。同时还可以将搜索保存成为智能列表,方便以后查看。

Remember The Milk

享受有条理的生活。
“撤消”功能意味着您再也不必担心操作失误。因此您可以放心地注册账户,并开始使用Remember The Milk。

PHP require 绝对路径 autoload

在写PHP程序时,如果文件比较多,目录也比较多,有很多require_once的情况。

如果程序的入口不唯一,并且分布在不同的目录,必须require绝对路径,如果是相对路径,PHP会把入口文件的路径作为基本路径,require里的相对路径都是相对于入口文件的。

这样带来最大的问题就是自己部署的程序,给别人提供接口时,别人根本无法使用!

所以要用绝对路径。

做法就是选一个require的入口文件,大家都要包含这个文件,这个文件的开头加上这么一句:

<?php define("PROJECT_BASE_PATH", realpath(dirname(__FILE__) . "/../"));

这个时候PROJECT_BASE_PATH就是从根开始的绝对路径了。

后续的所有require文件,都用这个宏定义去拼接,例如:

<?php require_once(PROJECT_BASE_PATH .'lib/mysql.lib.php');

就绝对保证不会再出错!

当然,如果你是PHP5的环境,推荐使用__autoload函数,把所有功能都封装到类中,然后在需要的时候自动调用,避免到处require。

数据库导入 乱码 数据被截断 数据不全

今天碰到一个Case,是数据库导入的,很顺利地解决了,跟大家分享一下过程。

环境:

数据源服务器

mysql>show variables like ‘char%’;
character_set_client gbk
character_set_connection gbk
character_set_database gbk
character_set_results gbk
character_set_server gbk
character_set_system utf8

所有环境都是gbk,但备份脚本是这样的:
mysqldump my_db_name /home/leakon/db_bak/dump.sql

打开dump.sql,里面的中文字符都变成乱码了,在这个文件的头部,可以看到这么一行:
/*!40101 SET NAMES utf8 */;

也就是说,mysql在导出的时候,按照utf8字符集,进行了编码转换。

你可以这么理解,你和我都是中国人,说汉语,但我们之间沟通的时候用了一个翻译,这个翻译是英国人。

我就是那个数据源,英国的翻译就是dump.sql,我要跟你说中国话, 但翻译先把汉语翻译成英语,存在他的脑子里了。

这个时候你看他的内容,是没法理解的,这就是你看到的乱码。

下面,要把导出的数据,导入到新数据库中。

注意了,应该用下面的脚本:
mysql –default-character-set=latin1 -uroot my_new_db < dump.sql

我指定了latin1,作为连接mysql服务器的字符集。

实际上,导入的过程,就是让那个英国翻译,把英语再转换成汉语告诉你。这时,指定了latin1字符集, 就是告诉翻译,你让他把脑子里存的话,用英语翻译成汉语告诉你。

这里必须指定latin1,我也很奇怪为什么不指定utf8,我试过,不成功,服务器提示不支持这种编码。

具体原因我还会再研究,目前解决办法就是这样了。

指定字符集,就是让翻译按英语->汉语的转换程序进行翻译,否则,翻译可能是按英语->法语的顺序转换的,也就证明了,为什么导入数据库时,不指定编码,会是奇怪的乱码字符。

这里还有一个重要因素,就是数据库的字符集。

这个 my_new_db 创建的时候指定的是 gbk 字符集,建表语句是:
CREATE DATABASE my_new_db DEFAULT CHARACTER SET gbk COLLATE gbk_chinese_ci;

如果数据库的字符集和源数据库不一样,那么,在导入时,很肯能出现导入到一半,MySQL报语法错,或者本来10万行数据只导入了2万行就停了。

原因就是编码转换时解析引擎会把其中的某些字符理解为引号等字符,导致语法错乱。

其实,有彻底解决这些编码问题的办法,那就是保证所有过程都指定正确的编码。

下面就以gbk为例:

  1. 源数据库,数据库、表和字符串字段的编码统一设定为gbk
  2. PHP调用程序时,在创建MySQL连接后立即发送一条 set names ‘gbk’ 语句
  3. 导出时指定gbk字符集:mysqldump -uroot –opt –default-character-set=gbk my_db_name > dump.sql
  4. 检查文件内容,应该是正确编码的字符,用普通文本编辑器可以阅读
  5. 目标数据库建库脚本:CREATE DATABASE my_new_db DEFAULT CHARACTER SET gbk COLLATE gbk_chinese_ci
  6. 导入时:mysql –default-character-set=gbk -uroot my_new_db < dump.sql

就这几个简单的步骤,就完全避免编码问题!

如果你要用utf8编码,把上述脚本的gbk都替换成utf8,把 gbk_chinese_ci 替换成 utf8_general_ci。

再重新导入,编码没问题,数据也完全导入了吧。

单元测试

有些道理,大家都明白的,但真正要做的时候,却很少有人能落到实处。

在学软件工程的时候,第一次听到了单元测试和测试用例这些词,印象很深刻 。

书上讲的都是软件开发这个行业几十年来总结的经验,很有道理,也很有说服力,我也十分认同书上的做法。

我也感觉自己是一个喜欢追求完美的人,看到书上说的那些测试的方法,比如边缘测试之类的,在感叹前人总结的方法很优秀的同时,自己也暗下决心,以后我写的程序,也要有规范的接口,有明确的输入输出,要有完备的测试用例,把每一个细节都测试到,让自己的程序表现完美。

然而,在现实中,总是事与愿违。

大学期间写的程序就不说了,我到现在正式工作已经两年半了,写的代码少说也就几万行。Bug是难免的,但总是被别人发现。

我一直没有做过规范的单元测试。

原因是多方面的,但主要在自己。

懒惰,是程序员最大的敌人!

当需求明确时,我会立即投入编码,把脑子里那些自认为不错的逻辑,用程序代码实现出来,这个过程是十分开心的。程序完成后,输入预定的参数,得到预想的结果,那种成就感,难以用语言形容。

我想问一句,这个时候,这个程序算是开发完毕了吗?

以前,我是这样认为的。我也坚信,国内绝大部分的程序员,都是这样认为的。

实际上,这就好像万里长征刚迈出第一步而已。虽然有些夸张,但事实如此。

你给你的程序写出测试用例了吗?你对程序的每一个模块,做了单元测试了吗?在输入数据的时候,是否输入了边界值?是否输入了非法值?有没有试过不给输入,看看程序输出什么?

当然,那些自认为做事严谨的人,多少是会做一些测试的,这也使得他们的程序,比一般人的更稳定一些。

我想说的是,自己写的程序,定势思维会很强的,你很难想象出一些匪夷所思的参数去测试你的程序。

程序就像你的孩子,你希望他健壮,也许你会让你的孩子参加一些艰苦的锻炼,但你绝对不会拿个大铁棍砸向孩子的胳膊以检验他的骨骼是否强壮。

开个玩笑地说,拿铁棍砸别人的孩子,大多数人还是做的出来的。

我曾经自认为考虑的十分周到的程序,被人一眼就挑出一个bug,自己回去改,这个bug好了,另一个bug也随之而来,最后搞的svn被我当成磁盘一样用,改个字母都会提交一次!

话题有些远了,这篇文章的出发点,就是今天彻彻底底的进行了一次单元测试,真的是感受良多。

在做这个项目的时候,伴随着开发,我也一直在写测试用例,也一直在反复测试。我很早就知道PHP的单元测试框架,也看过很多文章,里面的很多过程和逻辑我都很清楚。可能还是因为懒吧,我只是把所有的测试用例放在一起,每次循环跑一下程序,把输出打在屏幕上,测试的时候看一眼,没什么问题就过去了。

这样很不好,还是思维定势的问题,有时候出现问题,可能就在一些细节的地方,面对满屏的输出,你是无法准确判断每个输出是否完全正确的。

后来,试着按照标准的测试框架的方式,对每一个输入,认真地写出人工判断的期望输出,然后由程序自动执行,看看是否能全部通过。

结果当然不会出乎意料,有一半的测试没有通过。

再返回头去改程序,这时才发现以前考虑逻辑的时候想法有多么简单,有那么多遗漏的地方。

你也可以邀请你的同事,鼓励他们想出各种办法来搞垮你的程序,你需要做的,就是把他们每一个稀奇古怪的输入都认真地记下来,这才是你最宝贵的财富。

我听过以前的同事讨论程序,说他新做的模块,测试了上千万的数据,一个bug都没有,但一上线,就出大问题,而且每次都这样。
另一个同事对他说,合法的输入,测试多少都没有意义,只有那些非预期的,非法的和异常的输入,才是重要的测试资源,而且,这些case都应该好好保留,以后每次测试,就从这些bad case入手。

我对这番对话记忆深刻,从那时起,我也在逐渐应用这种方式去测试我的程序。

今天,终于切身地感受到这种开发方式的好处。

我在线下修改一个bug,就可以避免上万的用户在线上碰到bug。

作为互联网产品,线上服务的稳定性绝对是第一位的!

话题基本到这里,我想再引申出另一个问题,就是关于“落实”这两个字。

现代人类文明,大概有2000年了,我们的前辈,给我们总结出了大量的经验教训,希望我们能少走弯路。

我领悟出一个道理,世上绝大部分事,都很容易做,每一份事业,都很容易成功,因为很多是前人做过的,他们总结的经验,我们很容易就能得到。

可实际情况却是,大部分人都不能把事做好,都不能做出一份成功的事业。

是他们不懂道理,没听过经验教训吗?

我想不是,原因就在于,他们没有很好地落实每一项应该完成的事。

就拿上学来说,我们都经历过至少十几年的校园生活,大部分人的学习成绩应该都不是所在学校的No.1吧?

你回想一下,那个时候的年级第一,他比你聪明吗?不见得吧。

那个时候老师都说,要好好学习,课前要预习,上课要认真听讲,课后要按时完成作业,不懂就问。很简单的道理,不是吗?我的印象中,学校里学习好的人,平时都很贪玩的。他们不比我聪明,晚上熬夜也没我时间长,他们比我学习好,只有一个原因,他们听话。他们认真地做到了老师要求的每一件事,而且一点也不难,很容易就超过了别人。

思维再跳跃一下,就说这开车吧。

不准酒后驾车,不准疲劳驾驶,交通路口要减速慢行,要与前车保持安全距离。

很简单的四条吧,大家看看新闻,哪次事故不是因为酒后驾车,超速行驶呢?出了事,轻则误事赔钱,重则车毁人亡,你说做到这几条有多难呢?还是难以落实。

再回到程序开发上,就像我遇到的问题,我们有很多开发经验,有软件工程,有测试框架,有编程习惯,有代码标准,可真正做到的有多少呢?有几项是我们真正落实的呢?

还有创业,我认为也一样。

很多成功的老总,都会总结他的经历。很多人都说,他之所以成功,就是因为坚持不懈,锲而不舍地做了一件事,把这件事认真地做好,他就成功了。

而现在的很多创业公司,拿着几千万美元的风险投资,就不知道该干嘛了,这也干,那也干,这么大的互联网,没有他不干的。讲起道理来,他比谁都能说,真正做起事来,前人总结的经验,就完全抛在脑后了。

还是一句话,落实很难。

还是收起浮躁的心,踏踏实实,脚踏实地,把最简单的事做好,每落实一件事,离成功就会更近一步,我坚信!

说点什么

现在是周五22:20,本来想玩会儿游戏,可想起blog已经好几天没写东西了,攒了好多想说的,再不说就忘了。

这周有点郁闷,但也比以前更充实一些。

上周给新部门的同事讲了一些PHP网站开发方面的经验,也聊了一些优化方面的东西。来的人不少,弄得我挺紧张的。

因为比较忙,在做一个紧急上线的新项目,没有做充分的准备,有些东西没有讲得很深入。后来还讲了一个IP查询算法,是我自己想的,讲出来才知道,原来大家都有过研究。

肯定让来参加讨论会的同学们有些失望,我也觉得很郁闷。

后来和同事聊了聊,我给人的感觉是知道的东西挺多,但都不深入。

我不希望自己是这样,可实事求是地讲我确实一直存在这样的缺点。

可是话又说回来,我所了解的东西,也没有完全讲出来,我有自己深入了解的东西,工作2年了,每天都很忙,积累了很多经验,恐怕不可能在2个小时内全都讲完吧。

从另一个角度看,我觉得自己被别人小看了,也许这才是我郁闷的真正原因。

每个人都有虚荣心,像我这样的开发人员,最希望得到的,恐怕就是得到别人对我技术的认可吧。

恰巧昨天看到公司内部的技术周刊,有人讲了很多Web开发方面的东西,主要是html、css和javascript的,写得很长,很深入,也很具体。

我做Web开发也2年多了,那篇文章里说的每一条,我都知道,而且有些方面比文章里写的有更深的研究,但,我唯一没有做到的,就是把这每一点都写下来,都积累成文字记录,传给大家看。

这个问题我前一段时间已经意识到了,通过这件事,也让我有了更深的体会。

因此,我建了这个blog。

我也要把我积累的点点滴滴,都写下来,为自己记录,也为别人分享。

互联网最大的魅力,就是信息传播不需要时间。但,中国的互联网环境,大多还是抄袭和模仿,真正写原创的很少。

不知道是因为中国传统的文化底蕴中略带一些保守的思想,还是其他什么的,我不知道。

以前我也是这样,自己学点东西,很怕让别人知道,殊不知学的这点东西,N年前就被人家研究透了。

我的好朋友Lqx,我都叫他星爷,曾经告诉我,计算机的知识,只有先知和后知,没有能知和不能知。这句话我一直记忆犹新。

我的很多经验,都是星爷教我的,他从来不保守,慢慢的我也变得乐意与人分享,最重要的是在与人分享的同时,也极大地提高了自己,也许保守的人永远也无法体会分享带来的乐趣。

前2天我更新了blog右下角about的内容,想告诉大家我很愿意为互联网创造更多的内容。

我也想带动屏幕前的你,利用各种方式,用互联网记录下你的事。

前一段时间去过一次故宫,看到太和殿和乾清宫,感觉里面很破旧,我就想几百年前,康熙和乾隆在这里执政的时候到底是什么样呀?我站在大殿门外的御道上,联想几百年前的大臣,当年他们从这里走过的时候,在想些什么呢?他们会不会想到几百年后会有一个年轻人站在这里揣摩他的心思呢?

可惜的是,当时的科技水平有限,没法用影像去记录,以留给后人。

今天,我们有互联网,有完善的信息化记录设备,为什么不记录下一些东西,分享给今天的你,流传给我们的下一代呢?

就像本文的标题,“说点什么”。

我会经常来说点什么,你呢?

Google

Google
LAMP-Linux-redhat LAMP-Apache LAMP-MySQL LAMP-Php Leakon-Wiki Leakon-BBS XueBaoBao Xyoyou