存档

2009年9月 的存档

preg_match 匹配中文出现错误bug的解决办法

2009年9月28日 没有评论

论坛上的一个网友提出的问题,在ascii编码的php文件中,定义的一个函数,简单描述如下:

function checkpost()

{

global $subject

$censoww=array(‘婊”);

preg_match(‘/’ . implode(‘|’, $censoww) . ‘/i’, $subject,$matches) ) { echo “非法内容”;}

这是一个简单的禁字检测程序,广泛应用在bbs等程序中.可是这里出现了个问题,但$subject=’存活’ 或者 ‘存货’等时,这个程序竟然也提示非法内容。首先我提倡在程序中统一使用utf-8编码,当然这个程序在utf-8编码下应该没有问题的,可是在ascii下,就会有一些奇怪的bug。这位网页的程序都是基于gbk的,不可能全部转码,只能打打补丁了,下面是简单的解决方案:

mb_internal_encoding(‘gbk’);//指定网络编码为gbk,这同时也作为正则匹配的编码

function checkpost()

{

global $subject

$censoww=array(‘婊”);

//这里使用mb_eregi 代替preg_match,mb_eregi本身支持多字节的正则匹配

if(mb_eregi( implode(‘|’, $censoww) , $subject,$matches) ) { echo “非法内容”;}

顺便翻译了下手册中关于这个函数的简单说明

mb_eregi() executes the regular expression match with multibyte support, and returns 1 if matches are found. This function ignore case. If the optional third parameter was specified, the function returns the byte length of matched part, and the array regs will contain the substring of matched string.
mb_eregi(),支持忽略大小写的多字节正则匹配,如果匹配成功,返回1,如果指定第三个可选参数,函数就返回匹配部分字节数,同时把所有匹配到的子串放在第三个参数指定的数组变量中

div+css 图片垂直居中解决办法

2009年9月27日 没有评论

一个简单的问题实现在div+css中实现起来可没有那么轻松,参考网上找到的一些资料,找到了一个相对比较简单,而且效果还不错的方案,如下:

html结构很简单如下

<div class=”img_wrap”><img src =”http://www.baidu.com/img/baidu_logo.gif” /></div>

css样式

.img_wrap{/*非IE的主流浏览器识别的垂直居中的方法*/
display: table-cell;
vertical-align:middle;
text-align:center;
*display: block;
*font-size: 131px;/*约为高度的0.873,150*0.873 约为131*/
width:150px;
height:150px;
border: 1px solid #ccc;}
.img_wrap img{vertical-align:middle;}

php mkdir 递归创建多级目录

2009年9月14日 2 条评论

php默认的mkdir一次只能创建一层目录,如果在当前目录下创建一个div/css/layout 的目录就需要逐层逐层的先创建div,再创建div/css 再创建 div/css/layout,然而我们希望能让程序自动帮我们完成这个过程。

其实思路也很简单,1.先判断 div目录是否存在,不存在则创建;2.判断子目录 div/css 是否存在,不能存在则创建,3.在第二步中以子目录作为参数递归调用函数本身。也可以按相反顺序来,1.先判断最底层目录div/css/layout是否存在;2.判断div/css/layout的上层目录div/css是否存在,不存在则以div/css作为参数递归进行。。

下面是程序代码:

function mkdirs($dir)
{
if(!is_dir($dir))
{
if(!mkdirs(dirname($dir))){
return false;
}
if(!mkdir($dir,0777)){
return false;
}
}
return true;
}
mkdirs('div/css/layout');

同样的思路,php用rmdir和unlink递归删除多级目录的代码:

function rmdirs($dir)
{
$d = dir($dir);
while (false !== ($child = $d->read())){
if($child != '.' && $child != '..'){
if(is_dir($dir.'/'.$child))
rmdirs($dir.'/'.$child);
else unlink($dir.'/'.$child);
}
}
$d->close();
rmdir($dir);
}

图形验证码的破解与设计【转】

2009年9月10日 没有评论

图形验证码设计目的是利用人脑的不可模拟性来防止机器自动识别.但是一个设计低级的图形验证码(可以被快速破解)除了增加网络流量以外没有任何意义.网上太多的”生成验证码”的教程把重点放在如何生成图片上,而实用性却几乎为零.生成图形本身是零基础技能,任何平台都提供内存图形环境和设备上下文(DC)让你操作,vc中的CDC,java/.NET中的Graphics,都提供比你需要的还要多的绘图API.可以说介绍这些东西根本没有必要.(竟然还在某些地方看到图形叠加叫做水印的,图片水印是指可分离的但合成后不可视的图形透明通道,用于象电子印章之类的加密验证技术).设计一个复杂的难以破解的图形验证码需要了解
常规的可以破解图形验证码的技术种类.
利用session生存期来凭肉眼设别一次后无限次使用同一图形验证码并不算图形验证码的破解.这只是没有经验的程序员设计上的逻辑BUG.即图形验证码的session存活期是全局的.而不是针对某次验证过程的.具体过程如下:

客户端请求一个图形验证码.服务器生成一个图形验证码并将验证码的内容放在session中.当客户端凭肉眼识别通过输入框提交验证码内容后,服务端和session中的内容比较通过.用户其它信息校验成功后成功登录.但这时验证码的session还没有过期,客户端用相同的内容还可以为另一次验证使用.所以每个验证过的验证码的session应该立即销毁.这种逻辑上的BUG可以被没有任何技术经验的人所破解.真正的对图形识别码进行破解,大多数是对验证码进行切割比对.假设图形验证码生成的图片上数字是1234

一.切割:首先利用一定算法可以将其切割成最小的四张图片.将四周的空白最大可能地去除.
二.退色:将彩色图片退色成黑白的.用两极法,在0-255中小于128的视为黑色,128到255视为白色.
三.去躁点,将连续黑色范围小于某值,比如小于最小笔划中点的区域做成白色.
四.再进行最小切割.
五.比对,利用已经做好的图形库进行象素比对.因为经过上面的处理,图象都成了黑白两色,以尺寸最匹配的图片进行比对.先拿图形库中干净的标本图片和没有去躁点的目标比对,看标本图片中每个黑色象素在目标中是否存在,如果都存在比对通过.目标图片中的黑点在标本图片中没有的应该为躁点.然后拿去躁后的目标图片的黑色象素去标本图片比对,看是标本图片中是否存在,如果都存在为通过.有可能去躁不切底,这个过程只能作为参考,通过则为充分条件,不通过不是必要否决条件.从上面比对过程我们可以看出.比对的最重要的一步是切割,如何能保证目标图片被成功要割成已有标本图的大小匹配是最关键的技术.如果你的图片内容生成时本身就是按规则生存有,比如drawString时把一行内容串完整地画出来.那么间隔都是固定的,字符大小也是固定的.即使每次只画一个字,每个字意隔不同,但只要按最小切割,也就是把所有的行列中没有有效点的空白去切去,再以一套按最小切割的标本图来比较就很容易了.字体大小和样式(斜体,下划线,加粗),体型(黑体,宋体)的变化对增加难度不大,只要按不同字号和式样以及体型多备几套标本库,当然变化越多比对出错可能性越大,但从字体大小和式样上变化不是根本手段.如果你的验证码本身只有黑白两色那真正是让破解者太感谢了.复杂的颜色可以让其在退色过程中增加出错几率.长条形类似笔画的躁点,在比对时并不起多大作用,因为可以以标本图的象素去找目标图对应象素,躁点就是多余出来的.但长条形类型笔画的躁点加上不规则间隔对切割起到了巨大的阻碍作用.最最关键的要点是重叠技术.
两个字之间的部份重叠对于肉眼识别基本上没有障碍,但对于依赖切割比对的机器而言却是致命的克星.所以保证你的验证码内容中有一些文字内容部份的重叠.如果字数较长,比如8位,其中有两至三处的重叠,那么基本破解程序就死掉了.有些高级的破解程序利用色差切割,两个字的相交处的不同颜色来作为切割界限,在这里可以将重叠的字设同色.增加切割难度.
只要无法切割,那么其它方法就无计可施.所以设计一个难以切割的验证码是保证不被破解的最有力的保证.文字内容只增加比对时间而已,你用18030个中文字符和用10个数字,比对过程可能会增加1000多倍,对于机器比对而言难度不大,但很大地加强了标本图库的制作的难度.
下面是我用c#做的一个简单例子.复杂的设计用简单来说明,其实抓住最关键的地方就是至少保证有一次重叠,因为只是例子.真正实用的时候我会做出三次以上重叠.我用不同字号来何证间隔的不规则性.将原始阿位佰数字和转换后的数字都保存起来,用户可以根据图片内容只输入阿位佰数字或图片上的内容都可以.注意如果输入的内容中有不好输入的字应该提供一个软键盘之类的输入界面.
string[] CharList = { “零壹贰叁肆伍陆柒捌玖”, “○一二三四五六七八九” };
int[] size = { 10, 12, 14 };
string[] fm = { “宋体”,”楷体_GB2312″,”黑体”};
这些常量定义都可以再复杂一些.
DateTime dt = DateTime.Now;
Random r = new Random();
int x = r.Next(10000, 100000);
string tmp = “”;
string src = x.ToString();
Bitmap bmp = new Bitmap(100, 20);
Graphics g = Graphics.FromImage(bmp);
g.FillRectangle(Brushes.White, 0, 0, 100, 20);
Console.WriteLine(src);
int lastSize = 0;
Color lastColor;
bool into = false;
for (int i = 0; i < 5; i++)
{
char ch = CharList[r.Next(0, 2)][Convert.ToInt32(src[i].ToString())];
tmp += ch;
int sz = size[r.Next(0, 3)];
if (i == 3 && !into) //总共5字,到了第4个还没的重叠的话那第四个要设成14号字
//保证重叠发生
sz = 14;
int cr = r.Next(0, 200);
int cg = r.Next(0, 200);
int cb = r.Next(0, 200);
Color c = Color.FromArgb(cr, cg, cb);
int sub = 0;
if (lastSize == 14) {//如果上一个字是14号,那么下一字向左入侵保证重叠
//对大字号重叠的可视性要比小字号强.
//重叠时最好将当前字符的颜色设为上一字符颜色
c = lastColor;
into = true;
sub = 8;
}
g.DrawString(ch.ToString(),
new Font(new FontFamily(fm[r.Next(0, fm.Length)]), sz),
new SolidBrush(c),
new Point(i * 20 – sub, 0));
//这里因为字号不同,间隔也不同,但每个字的起始点相同,可以修改根据上一个字
//的大小再调整起始点.
lastSize = sz;
lastColor = c;
}
bmp.Save(“d:/aaa.gif”);
Console.WriteLine(tmp);

TimeSpan ts = DateTime.Now – dt;
Console.WriteLine(ts.Milliseconds);

这样的过程在我的台式机上一般在15毫秒左右,在服务器上应该更快.
效果:
肉眼很容易识别,但机器却很难切割
其实这其中还有更多的技巧,但从总体而言就是保证不能被机器正确地切割.没有切割,那么就不能处理比对.
(当然利用银河机进行组合整体比对理论上也是可以的).在防切割上的重点设计才是真正的设计目标.

javascript 图片loading的实现

2009年9月8日 2 条评论

我们在设计一些图片比较多的网页时,为了增强用户体验,希望图片加载的时候有个loading动画效果,而不是由空白到一下子出来。在zen cart的二次开发过程中同样也遇到了这个问题,下面是我的解决方案。

首页给图片设置一个默认的loading动画,再分配一个id,如<img  id=”loading1″  src=”loading.gif”>实际上加载过程通过一个函数来完成

function addListener(element, type, expression, bubbling)
{
bubbling = bubbling || false;
if(window.addEventListener)    { // Standard
element.addEventListener(type, expression, bubbling);
return true;
} else if(window.attachEvent) { // IE
element.attachEvent('on' + type, expression);
return true;
} else return false;
}

var ImageLoader = function(url){
this.url = url;
this.image = null;
this.loadEvent = null;
};

ImageLoader.prototype = {
load:function(){
this.image = document.createElement('img');
var url = this.url;
var image = this.image;
var loadEvent = this.loadEvent;
addListener(this.image, 'load', function(e){
if(loadEvent != null){
loadEvent(url, image);
}
}, false);
this.image.src = this.url;
},
getImage:function(){
return this.image;
}
};

function loadImage(objId,urls){
var loader = new ImageLoader(urls);
loader.loadEvent = function(url){
obj = document.getElementById(objId);
obj.src = url;
}
loader.load();
}

通过loadImage函数就可以为指定的图片添加加载过程,其中通过addListener 函数注册事件,使得在图片加载完成的时候能够自动替换掉loading.gif这个动画过渡图片。使用代码很简单

<img  id=”loading1″  src=”loading.gif”  />

<script language=”javascript”>

loadImage(“loading1″,”http://www.baidu.com/img/baidu_logo.gif”);

</script>

zen cart 中使用自定义 session

2009年9月5日 1 条评论

    上次给zen cart添加图片验证码功能的时候同时碰到了一个问题,自己定义的session在zen cart程序中读取不到值,后来分析了一下得出如下结论。

    zen cart自定义了session的处理函数,session_save_path设在了/cache/文件夹,在include/configure.php里有个选项 define(‘STORE_SESSIONS’, ‘db’); // use ‘db’ for best support, or ” for file-based storage 设置session的保存方式。

    不同的程序间要想共享session数据,至少要遵循以下几个条件:session_save_path 相同,session_id相同,当然session的保存方式肯定要一样,php默认的session保存方式就是文件存储,而保存目录就在系统的临时目录。

    这里我们要想zen cart 读取到我们自己定义的 session变量,就需要我们在自己的程序里把session的存储目录放到zc目录下的cache文件夹,例如 session_save_path(‘e:/zc/cache’),然后修改zc/include/configure.php文件中 define(‘STORE_SESSIONS’, ‘db’); 修改为 define(‘STORE_SESSIONS’, ”);这样就让zen cart的session也以文件形式存储,就可以实现自定义session的共享了!

    以上的原理适用于其他类似的不同程序间session共享的问题,其实 zen cart中使用自定义 session的最简单的方法是 直接在自己的程序最顶部 require进来zen cart 的application_top.php即可,简单固然简单,但了解原理更重要!