htmlspecialchars vs htmlentities

很多人都以为htmlentities跟htmlspecialchars的功能是一样的,都是格式化html代码的,我以前也曾这么认为,但是今天我发现并不是这样的。

这两个函数在格式化带有英文字符的html代码的时候基本没啥问题,但是htmlentities对中文字符也不放过,这样得出来的结果是中文字符部分变为一堆乱码。当时做英文站的时候根本就没觉察到这个问题,而今天公司的一个收藏站却因为有有非英文字符而出现了问题,我最终查出来是htmlentities这个函数的问题,同时我也找到了htmlspecialchars这个函数。

对于这两个函数,php手册上都是英文做的解释,其中在htmlentities函数的说明部分有这么一段英文:

This function is identical to htmlspecialchars() in all ways, except with htmlentities(), all characters which have HTML character entity equivalents are translated into these entities.

从这句话中我们也可以看出来这两个函数虽然基本功能差不多,但是还是有细微的差别在里面的。再仔细看htmlspecialchars函数里面的一段话:

The translations performed are:

‘&’ (ampersand) becomes ‘&’

‘”‘ (double quote) becomes ‘”‘ when ENT_NOQUOTES is not set.

”’ (single quote) becomes ”’ only when ENT_QUOTES is set.

‘<’ (less than) becomes ‘<’

‘>’ (greater than) becomes ‘>’

可以了解到htmlspecialchars只转化上面这几个html代码,而htmlentities却会转化所有的html代码,连同里面的它无法识别的中文字符也给转化了。

我们可以拿一个简单的例子来做比较:

$str=<a href=”test.html”>测试页面</a>;
echo htmlentities($str);
$str=<a href=”test.html”>测试页面</a>;
echo htmlspecialchars($str);

Tags:

MooTools Essentials: The Official MooTools Reference for JavaScript and Ajax Development

MooTools,是 My Object Oriented JavaScript Tools 的缩写,我的面向对象的Javascript工具!

看别人的评论,说这个Javascript框架在扩展性上得益于面向对象的设计,比JQuery做得更好。

今天找到了介绍这个框架的电子书,顺便提供下载。

下载地址:

http://down.leakon.com/software/2008/11/Apress.MooTools.Essentials.Aug.2008.pdf

Tags: ,

与互联网产品设计有关的讨论 (二)

Jeff刚刚仔细读过《赢在用户》-WEB人物角色创建和应用实践指南,书评可以到豆瓣看看:
http://www.douban.com/subject/2157554/

他说,我们现在缺乏一套用户角色模型。这本书里面提到一个网站首先要建立起一套对应的用户角色模型,知道自己要给哪些用户提供服务,然后再根据不同类型用户的需求制定项目优先级。如果没有这套模型,那么我们会总是遇到这样那样的产品问题,针对这些具体问题的讨论将会永无休止。因为这样的讨论不是基于一套被每个人都认同的概念标准的。

比如,Jeff用1天时间搞了几个单机版的Flash小游戏,作为一个“游戏”模块挂到“应用”分类下面。我觉得,这项功能跟我们的网站毫无关系,没必要在这上面浪费时间,哪怕是1分钟都不应该。而Jeff说他确实听到了用户有这方面的需求。

这就是问题的根源,我们的网站要做什么?要给谁提供服务?想玩游戏的用户是不是我们的“重要”用户?

如果我们确定好了用户角色模型,统一了意见,知道我们主要为谁提供服务,那么这个问题就好解决了。

他还提到,当网站增加了一项新功能时,会给四类用户带来不同的反应(我记得大概是这几种):

  1. 迫切希望网站增加新功能,不管是否和网站主题相一致
  2. 增加的功能正好是用户锁盼望的,这样会应得用户的好感
  3. 增加的功能是用户不愿意看到的,会让用户产生反感
  4. 用户不在乎你增加什么功能,他需要的已经有了,对其他功能无所谓
当时是吃饭的时候说的,印象中是这几项。

这本书我没看过,我今天要说的也不是书里的内容,我是有另一种感觉,需要我用插叙的方式引入我对“规律”二字的看法。

写这类书的作者,大部分都是对书中内容所属的行业有很长时间的工作经验。他们应该是把这个行业的规律都看透了,已一种“过来人”的角度来总结自己的经验。

我举个例子,Yoyo总爱看跟星座有关的内容,她认为说的都特别准。以前我看的时候,也觉得似乎很准。现在我却发现了这类内容的一个规律:一个星座的人,所谓的性格特点,都是说那些优秀的性格,比如说你这个星座的人做事很细心,很谨慎,很有风度,很会为他人着想,有时候比较任性……。其实他说的这些性格,是每个人都会有的性格。我们可以做一个测试,找一个不是经常看星座的人,我们给他看一套特殊的星座性格特点,还是分12个部分,只是每个部分只有性格说明,没有所述星座说明。然后让他说哪个部分说的是他所处的星座。估计没有人能答对。

这只是一个浅显的规律,我觉得还有一个更深层次的规律。

就算你描述的那些性格,确实是跟一个人的出生季节有关。那我想问个问题,全世界60亿人,相当于每个星座有5亿人。难道这5亿人的性格都是一模一样的吗?

好像中国传统的教育方式,就是教学生把所有看到的、听到的东西都划分到某个已知的范围内。比如就像本文开始的那个引子一样,一个非常有经验的人,把他最为熟悉的行业划分成了4个类别,然后告诉人们,这个行业内的每个个体,都应当属于这4个类别之一。

07年有本书比较流行:《长尾理论》。

我也只是粗略地看过,肤浅地理解了这本书所要阐述的一个核心思想:需求是非常个性化的,绝不是简单的几个大热门可以涵盖的。

互联网正是可以满足这一需求的最好的载体。

就好像,世界上最大的图书馆,也不能保证每个人都能从中得到所需的内容。

如果一个人总要给一个行业进行有限种类的划分,那么他的这种做法就是“反长尾”的,说严重一点可以用“反人类”来形容。

有兴趣的朋友可以算算,2的128次方是一个什么样的数字,用一个形象的比喻,可以帮你理解:
假如一个小方块的体积是1立方厘米(长、宽、高都是1cm), 那么2的128次方个小方块的体积,可以塞满半个银河系,顺便说一句,太阳系只是银河系中可以忽略的一个小点点。

人的DNA序列呢?我不太懂,不过我知道2的128次方,对应于DNA序列的可能的组合数,可以用九牛一毛,沧海一粟来形容吧。

因此你也大概能推算出,地球上任意的2个人的性格特点是否有可能完全一致。

这就是有点跟书本理论相违背了,书里讲究的是“万变不离其宗”,而事实似乎是一个“无穷阶的发散的矩阵”。

所以,我有点不太爱看这类“下定义”或“总结归纳”之类的书,尽管有可能它是外国人写的。

这次的讨论和感触发生的时间,跟昨天写的那个讨论挨得很近,所以我的观点也是一致的。

我们决不可能满足所有人的需求,甚至是满足1个人的所有需求都是不可能的。

但有一点却是一定的(你可以用概率去计算),某一个事实存在的需求,一定有不少人都同时拥有。我们就把这一个需求做到极致,就是我们第一步需要做的唯一的事。

《与互联网产品设计有关的讨论》系列的内容,都是我想到哪写到哪儿,不是像连载小说那样都策划好了然后逐一发表。

思想总是在变,因此需要真实记录我过去的某个时刻的思想是一个什么样的状况。如果人脑也能像MySQL一样可以随时DUMP成文件作为备份就好了……

 

Tags: ,

带链接的 img 标签不宜应用 margin 或 padding 样式

上述5个配图,分别是IE6、Firefox3、Opera9、Safari3和Chrome浏览器下的不同效果。

应用场景是,图片有链接,HTML片段是:

<a href=”…”><img src=”…” alt=”…” /></a>

img 标签有样式表定义,为了使图片与周围的内容有一些间距,我加入了 margin 定义。

加入之后,在现代浏览器中,会出现上图中显示的蓝色 hover 块与图片不匹配的情况,在这点上,IE6、IE7都能正常显示。

定位问题用了很久,最后发现是 margin 或 padding 的问题。

现在不知道是为什么,只知道解决的办法是去掉这2个样式定义。

如果需要图片有边距,可以在外层套上 p 标签,给 p 标签应用 margin 或 padding 可以达到同样的效果。

Tags: ,

与互联网产品设计有关的讨论 (一)

以前只是搞程序开发,不太关注产品设计,现在要开发一个产品,就要转换一下自身的角色,从用户的角度考虑问题。

随着讨论时间的增加,我对一个产品的理解,对用户定位的理解,都有了很大变化。

今天是这么看待问题,十天后,观点可能完全相反了。

所以有必要把每天对某个问题的看法、思路落实到字面上,保存下来,以备日后的反思、参考。

先说说一个月以前,我觉得,一个SNS网站,就应该像开心、海内和校内一样,只做18-30岁这个年龄段的网民需要的产品,因为这部分用户是中国互联网的中坚力量,占了绝大部分比例。

作为一个刚起步的网站,用户从零开始积累的时候,应该放下自己的特色,放下自己的品味,把热门网站的热门功能,抄袭过来,说句俗话,先“圈人”,等有了一定数量的用户后,再推出自己有特色的产品。

看似挺有道理的一个观点,当时我还挺坚持的,认为这个就是真理。

一个月后的今天,我觉得这种做法是错误的。

随波逐流,是不会被用户认可的。先不谈那些热门网站已经先入为主,就说当SNS这股风潮一下子涌现到人们面前的时候,大家会产生一种SNS疲劳,这么多网站,都是一样的模式,一样的界面,一样的游戏,唯独不一样的是在每个网站都有各自的朋友圈,经常性的是一个朋友,要在三个网站都加一次好友,而另一些朋友,都在不同的网站注册,我想跟某个人联系之前,先要努力回忆一下这个朋友是在哪个网站加为好友的。甚至是当我收到过某个朋友的消息,过了几天再想看看时,实在是想不起来在哪个网站看过的。

渐渐地,我怕了,看到一个网站注册时填那么多信息我就烦,总是重复地做着相同的事,也没有从中得到什么有用的帮助。

现在的情况是,中文名叫开心的网站有4、5个,中国人就擅长搞“山寨版”,这东西搭起来也容易,UCenterHome就可以直接用,换个图片和皮肤,一个SNS就搭出来了。

就好像06年大家都玩儿视频一样,看看现在这些网站都什么状况?几家加一起估计有几个亿的资金,都投入到服务器和带宽上面了,是他们扶持了中国IDC的发展!

据说六间房要换办公地点了,优酷的情况也不是很乐观。记得北京台有个CEO访谈的节目,邀请李善友,我印象最深的一句话:在中国做视频,没有一个亿就别玩儿了。

相比,SNS的成本应该还低廉的多,所以跟风的就数不过来了。一个行业里,赚钱的就是带头的那几家,优胜劣汰的自然定律是地球上永恒的真理,没有创新的跟风者,遭到淘汰是必然的,剩下的就是根据你忽悠VC的能力决定着你烧完资金的时间问题了。

我也是受到Jeff的启发,他提到了宝宝树,这个专注于母婴的网站,让我认识到跟风是个错误的方式。

举个例子,西单的客流量很大,按理说在那里开个火锅店肯定赚钱。这句话是没错,可是现在西单已经有20家火锅店了,你再进去已经晚了,而且你的火锅店又毫无特色,甚至于还比不上现存的那20家,那你这家店必然会被淘汰掉。

如果你选择在东单开一家烤鱼店,尽管客流量远远比不上西单,但烤鱼是你的特色,而且这一带鲜有烤鱼店,那你的赚钱的机会就大得多。

这就意味着,不是非得满足大比例用户群的大比例需求,才能做得好,才能得到认可。只要你有特色,能切实满足一小部分用户的迫切需求,一样可以得到认可,得到用户的依赖。注意词组:切实满足迫切需求

按说,初为父母的人,大部分在26到30岁之间,这部分人比18到25岁之间的学生和公司职员的数量少得多,而且前提是刚有了孩子,数量就更少了。

但这部分人都有一些很明确的需求,就是:

  1. 记录与分享孩子的成长历程
  2. 与其他年轻的父母们交流育儿经验

需求也不多,就简单的几项,但这些需求我认为都是刚当上爸爸妈妈的人们非常盼望得到满足的。

因此只要你把这2项功能做深入,做精彩,让这一小部分用户满意,那你这第一步就算成功地走了出去。

宝宝树算这第一步走得好的,用户数和VC的支持就充分说明了这一点。

我只是简单用了用,体验了一下。他不是靠玲琅满目的小功能堆积出来的,而是把社区网站都有的几个基本功能根据宝宝这个主题关联到了一起,说的通俗点就是宝宝树教你一套新玩法,功能还是那些,但玩法变了,变得能让你接受,让你记住,让你知道这是他们的特色。

我自己也在设想做一个网站,列了不少功能点,我认为比较好的一个特点是这些功能都是围绕着一个核心功能展开的。这些小功能,哪个单独拿出去都毫无意义,但都跟一个核心绑定起来后,却能发挥很大作用。

Jeff问我,你的这个单一的功能点,有多少人愿意用呢?

我的回答是,当我自己是一个用户的时候,是我提出了这个需求,所以我相信也有很多用户跟我有同样的需求。尽管有这样需求的用户并不多,但我的第一步是把这有限的小部分用户“伺候”好了,其他的都是水到渠成的事儿了。

相反,那些是个人就有的需求,大部分人也知道怎么去满足,做这件事的网站也已经不在少数,如果我没有特色,没有创新,肯定搞不过已经存在的领头羊们了。

总结一下现在的观点:一开始,心不要太大,把目标定位在一小部分人,明确1、2个这部分人最迫切的需求,把它做深,做精,同时还需要创新的内容,把这第一步走好,才是现在应该做的唯一的事。

Tags: ,

Godaddy 被封 今天解封 GFW

好消息!!

Godaddy 貌似解封了!!

这几天发现Godaddy.com打不开,Ping域名可以得到IP,但icmp包收不到,明显是IP被封了,可恶的GFW,你知道全世界有多少域名托管在Godaddy麽!

现在可以Ping通,打开网站时很慢,浏览器只能得到网页的Title,页面一直下载不完,估计是部分路由通了,还有一些由于各个级别的TTL导致暂时仍然无法访问吧。

不管怎么说,有希望就好!

听说mya.godaddy.com这个域名管理平台是一直可以访问的,不过速度也很慢。

如果今天暂时Ping通是“回光返照”的话,以后只能依赖于mya.godaddy.com了。

我很担忧,刚刚起步的中国互联网,会被一些封建守旧的、反人类的、无知无能的、昏庸官僚的ZF败家子儿们一步一步地毁灭掉!

Tags: ,

PDO Quick Guide

Book: PHP Data Objects Layer (PDO)

Author: Ilia Alshanetsky

Download: http://down.leakon.com/software/2008/11/quebec_PDO.pdf

其实这不能算是一本书,只是一个用于演示的文档,算是一个大纲。

这个简明扼要的大纲告诉你什么是PDO,为什么推荐你在PHP5中使用PDO作为数据库操作层的封装接口。

不仅更安全,而且有更好的性能,用起来更简单。

我在做一个数据库ORM接口,参考了Propel和Doctrine的设计思想,并只针对MySQL5,不做任何框架性或通用数据库的考虑,完全是为了简单、易用和高性能。底层数据库操作依靠PDO完成。

敬请关注~~

Tags: ,

Linux 修改 IP DNS 参数 配置

一、修改 IP 地址

[leakon@linux]$ vim /etc/sysconfig/network-scripts/ifcfg-eth0

DEVICE=eth0

ONBOOT=yes

BOOTPROTO=static

IPADDR=219.236.108.199

NETMASK=255.255.255.192

GATEWAY=219.236.108.254

二、修改 GATEWAY

vim /etc/sysconfig/network

NETWORKING=yes

HOSTNAME=leakon.com

GATEWAY=192.168.10.128

三、修改 DNS

[leakon@linux]$ vim /etc/resolv.conf

nameserver 202.99.160.68

nameserver 202.99.168.8

四、重新加载网络配置

/etc/init.d/network restart

修改 IP 地址

  • 即时生效
    # ifconfig eth0 192.168.10.108 netmask 255.255.255.0
  • 启动生效
    vim /etc/sysconfig/network-scripts/ifcfg-eth0

修改 Default Gateway

  • 即时生效
    # route add default gw 192.168.10.192
  • 启动生效
    vim /etc/sysconfig/network-scripts/ifcfg-eth0

修改 DNS

  • vim /etc/resolv.conf
  • 修改后立即生效,启动亦有效

修改 Host Name

  • 即时生效:
    # hostname leakon.com
  • 启动生效:
    vim /etc/sysconfig/network

Tags: ,

MySQLi vs MySQL

Background
Between participating in a MySQLi group and telling others about PHP5’s new MySQLi (MySQL improved) extension, there always seems to be a redundant (though valid) question, “Why?” Simple enough right? Maybe not, so I attempted to come up with some good benchmarks. Now note, just because of the pure nature of PHP extensions, this is easier and harder in some ways than doing let’s say, “ATI X800 vs Nvidia 6800” type benchmarks. 

With that said, I tried my best to keep the test scripts used identical down to the variables. There will obviously be some differences as conceptually some things are different between the two extensions.

The Environment 
The benchmark hardware/software configuration is as follows:
     Processor – Intel Pentium 4 (HT) 2.6 gHz/800mHz FSB 
     Memory – 1236836K (~1.2GB) PC3200 DDR 
     Hard Drive – Seagate 250GB (7200 RPM) SATA150
     Operating System – FreeBSD
     PHP – mod_php5 with Apache2 and Zend Optimizer 
     MySQL – mysql-server4.1.11

Hardware wise, it’s not the fastest server on the face of the planet, nor is it the slowest. In fact, I think it’s a mid-level system which makes it perfect for benchmarking. I used Apache2, as I believe more people are starting to use it. I may do a future benchmark with Apache1.3.x as I do have the binaries in place. Apache will not be accepting external connections. The MySQL server has networking off. FreeBSD is running with soft updates on.

The tests 
I spent some time trying figuring out what would be a good suite of tests to run. Without saying, they’d all obviously have to be PHP scripts of some sort interacting with MySQL through the traditional MySQL extension and the new MySQLi extension. The real question was, how these scripts would be setup. I decided to take the procedural approach (though I recommend using MySQLi in the OO way), since the traditional MySQL has no OO support. Below is my attempt at it.

The MySQL table
Query caching is off.
The table structure is as follows:

DESCRIBE benchmark;
+------------+--------------+-----+----------------+
| Field      | Type         | Key | Extra          |
+------------+--------------+-----+----------------+
| PRIMARY_ID | int(11)      | PRI | auto_increment |
| FOO        | varchar(255) |     |                |
| DUMMY1     | text         |     |                |
| DUMMY2     | text         |     |                |
| DUMMY3     | char(1)      |     |                |
+------------+--------------+-----+----------------+

 

Note, the MySQL table structure should not matter for our benchmark, as we are simply executing SQL queries through different mediums.

The PHP scripts
The focus of the scripts will be around the INSERT clause. Why not SELECT you might say. Well it doesn’t matter quite frankly. We are here to benchmark the functions with identical SQL, the key phrase is “identical SQL”.

Insert SQL test – Executes an INSERT query 100,000 times on the `benchmark` table with a for loop. mysql_query and mysqli_query will be used.
Insert SQL test 2 – Builds 100,000 INSERT SQL queries into one string. mysqli_multi_query will be used.

`benchmark` is truncated after each test script runs.

How performance is guaged
Deciding how to guage performance was undoubtely the hardest part of this benchmark. I had really only 2 options. The first, to setup a simple timer in between the functions where the queries took place and calculate the difference. The other option, was to use a full out profiler. I decided to go with a profiler, as it would give me execution times for surrounding PHP code (for example, the for loop).

My two (considered) choices in the profiler category are xdebug and Zend Studio Debugger. Let me say right now, I use Zend Studio for all my PHP work and so I had a certain bias to use it’s debugger, but alas…this benchmark must do without bias! This is about performance! So I hit Google to try to make an unbias decision about this. I found that Zend Debugger had won the best debugger award from PHP Magazin and so I decided to use it for this benchmark. Now note, all profilers are pretty much the same, in that conceptually they do the same thing. So choosing one over the other for this benchmark would not have a made a difference.

Running of the tests 
So with all the formalities out of the way, it’s finally time run the test scripts. Getting anxious are you? Don’t worry, so am I 🙂

  • Insert SQL test (insertsql_mysqli.php)
    Here is the run of the first profile

    A list of 9 subsequent runs and the DoInsert() function’s “own time” results (in microseconds) 

     
    DoInsert() own time
    Total execution time
      24,670.14 26,075.74
      25,132.45 26,569.09
      25,232.18 26,547.64
      25,006.92 26,289.18
      25,401.54 26,710.59
      27,792.51 29,136.76
      25,386.73 26,814.82
      25,279.91 26,575.78
    Average 25,504.32 26,859.87
    Standard Deviation 849.09 846.19


    The standard deviation is an acceptable figure, on average give or take 1 second between those runs. Then on average, DoInsert() takes about 25 seconds to run with mysqli_query. Now let’s try mysql_query.

     

  • Insert SQL test (insertsql_mysql.php)
    Here is the run of the first profile

    A list of 9 subsequent runs and the DoInsert() function’s “own time” results (in microseconds) 

     
    DoInsert() own time
    Total execution time
      24,799.11 26,134.51
      25,075.20 26,426.23
      24,685.38 26,048.15
      24,891.70 26,245.90
      25,091.64 26,299.66
      24,713.04 26,138.62
      24,741.54 26,125.89
      24,596.36 25,970.21
    Average 24,885.75 26,007.04
    Standard Deviation 229.16 489.50

    The standard deviation with mysql_query is much less. In fact, mysql_query is able to do our task about 1 second faster than with mysqli_query. Let’s try to do the same task withmysqli_multi_query.

     

  • Insert SQL test (insertsql_mysqli_multi_query.php)
    Here is the run of the profile

    53 seconds, mysqli_multi_query definately is not the best choice here. 

     

  • Insert SQL test with MySQLi statements (insertsql_mysqli_stmt.php)
    In this test, there is a source code change conceptually, so make sure you take a look.
    Here is run of the first profile

    A list of 9 subsequent runs and the DoInsert() function’s “own time” results (in microseconds)
     

     
    DoInsert() own time
    Total execution time
      24,077.77 25,179.06
      24,535.49 25,687.64
      25,232.18 26,547.64
      24,476.72 25,552.14
      24,445.86 25,551.81
      24,586.98 25,703.18
      24,582.58 25,675.57
      24,783.44 25,931.73
    Average 24,589.72 25,760.13
    Standard Deviation 318.49 376.39


    The standard deviation is an acceptable figure, on average give or take half a second between those runs. Then on average, DoInsert() takes about 24 seconds to run withmysqli_stmt. I think we have enough for now that we can come to a conclusion.

 

Conclusions
The data showed that mysql_query outperforms mysqli_query head to head. For the specific task of inserting 100,000 rows into a table, using mysqli_multi_query more than doubled our script execution time. At this point, you may start wondering what the point of using MySQLi is if the traditional MySQL extension is getting the upper hand on MySQLi.

However, using MySQLi statements we got better results, in fact it outperformed mysql_query by as much as mysql_query outperformed mysqli_query. Though this might not be enough reason for you to change existing code, remember that MySQLi statements are “pre-escaped”, meaning that there is no need for you to do mysql_real_escape_string on any of the variables. It’s apparent that given this fact about MySQLi statements, if we had made the extra function call of mysql_real_escape_string to the four inserted values, we would have greatly increased our execution time (good for character escaping user input and stopping SQL injection).

While the above may not be enough reason to change existing code to use MySQLi statements, they might be good reasons to use them in the future, for both security and performance. 

[11/16/2005] Also just to mention, mysqli also implements ssh like security measures to make logins between the client and server much more secure.

In summary,

mysqli_stmt > mysql_query > mysqli_query > mysqli_multi_query

Tags:

DIV POSITION LEFT TOP

网页可见区域宽: document.body.clientWidth;
网页可见区域高: document.body.clientHeight;
网页可见区域宽: document.body.offsetWidth   (包括边线的宽);
网页可见区域高: document.body.offsetHeight  (包括边线的宽);
网页正文全文宽: document.body.scrollWidth;
网页正文全文高: document.body.scrollHeight;
网页被卷去的高: document.body.scrollTop;
网页被卷去的左: document.body.scrollLeft;
网页正文部分上: window.screenTop;
网页正文部分左: window.screenLeft;
屏幕分辨率的高: window.screen.height;
屏幕分辨率的宽: window.screen.width;
屏幕可用工作区高度: window.screen.availHeight;
屏幕可用工作区宽度:window.screen.availWidth;

这么多属性,不如一张图看得直观,很老的图了,不过很实用。

Tags: ,