缘起:我们将一个早年(2010?)的Yii应用从服务器A迁移到服务器B之后,发现几乎所有关于字符串长度的Validator都出问题了,每个汉字都被当成“2个字符”计算。我们做了一系列调查,对比了两台服务器LAMP环境配置上的差别,发现老服务器的php.ini文件中mbstring.func_overload被设置为7(很可怕),而新服务器则没有这样设置,于是试着追踪了一下来龙去脉,将发现分享一下。
这个问题大概最早是一位叫shuwato的日本人在09年提出来的,他发现Yii的CStringValidator没有按照预想去计算字符数(Yii 1.0.4),于是给Yii开发者提了一个BUG:http://code. google.com/p /yii/issues/ detail?id=32 9 。
“强”(Yii的核心设计师)给出的解决方法竟然就是建议他修改php.ini,让mb系的str函数去覆盖标准的str函数(汗颜
),可能当时该应用的开发人员修改mbstring.func_overload参数,也和这个issue有关。无论如何这是一个杀伤力很大的改动,所有使用php标准str函数的地方都会受影响,换言之所有部署在该服务器的PHP应用都会受影响(如果其他应用按PHP标准str函数的行为去设计程序的,就可能导致bug)。
shuwato显然对强的回复不满,就跑到Yii的论坛里面去发了一个帖子抱怨:
http://www.y iiframework. com/forum/in dex.php/topi c/2205-strin gvalidator%E 3%81%AE%E3%8 3%9E%E3%83%A B%E3%83%81%E 3%83%90%E3%8 2%A4%E3%83%8 8%E5%AF%BE%E 5%BF%9C%E3%8 1%AB%E3%81%A 4%E3%81%84%E 3%81%A6/
用日文发的内容,没必要细看,我简介如下吧:他当时不接受强的建议,提出另外三个构想,一是修改Yii framework的代码;二是自己写一个validator代替 CStringValidator ;三是修改 .htaccess的设置。
关于[ .htaccess]我不是很理解,我猜想是使用应用级的[ .htaccess]文件来覆盖php.ini的某些参数,这样就把影响面缩小到一个具体的应用,不影响部署在同服务器的其他应用(有待考证)。
后面的iwasaki回复提到,构想一,如果升级了框架,还要担心忘了改过人家代码,导致别的bug; [.htaccess]也一样,如果部署在其他地方忘了改,也出问题;构想三,自写validator是推荐的。
一年半过去以后,一个叫jamband的人提到,他使用Yii 1.1.4,可以通过一个encoding属性回避这个问题, 代码写起来像 array('title', 'length', 'max'=>50, 'encoding' => 'utf8'),意思是自己指定编码(对于非ASCII等单字节编码)的话,Yii就会调用 mb_strlen去算字符数。
※encoding参数应该是从Yii 1.1.1开始提供的(参考Yii的changelog):
http://code. google.com/p /yii/source/ browse/trunk /CHANGELOG#2 31
- Enh #392: Added CStringValidator::encoding to support checking the length of multibyte strings (Qiang)
jamband后来又追加了一个说明,说从Yii 1.1.7开始,Yii总算修改了这个让人苦恼的行为,即使不改mbstring.func_overload设置,也不指定encoding属性,对于多字节字符,也会按照mb_strlen去计算字符数了。
- Enh: CStringValidator now uses application charset by default if mb_strlen is available (Sam Dark)
※application charset应该指的是config/main.php里面指定的language属性。
这个问题大概最早是一位叫shuwato的日本人在09年提出来的,他发现Yii的CStringValidator没有按照预想去计算字符数(Yii 1.0.4),于是给Yii开发者提了一个BUG:http://code.
“强”(Yii的核心设计师)给出的解决方法竟然就是建议他修改php.ini,让mb系的str函数去覆盖标准的str函数(汗颜

shuwato显然对强的回复不满,就跑到Yii的论坛里面去发了一个帖子抱怨:
http://www.y
用日文发的内容,没必要细看,我简介如下吧:他当时不接受强的建议,提出另外三个构想,一是修改Yii framework的代码;二是自己写一个validator代替 CStringValidator ;三是修改 .htaccess的设置。
关于[ .htaccess]我不是很理解,我猜想是使用应用级的[ .htaccess]文件来覆盖php.ini的某些参数,这样就把影响面缩小到一个具体的应用,不影响部署在同服务器的其他应用(有待考证)。
后面的iwasaki回复提到,构想一,如果升级了框架,还要担心忘了改过人家代码,导致别的bug; [.htaccess]也一样,如果部署在其他地方忘了改,也出问题;构想三,自写validator是推荐的。
一年半过去以后,一个叫jamband的人提到,他使用Yii 1.1.4,可以通过一个encoding属性回避这个问题, 代码写起来像 array('title', 'length', 'max'=>50, 'encoding' => 'utf8'),意思是自己指定编码(对于非ASCII等单字节编码)的话,Yii就会调用 mb_strlen去算字符数。
※encoding参数应该是从Yii 1.1.1开始提供的(参考Yii的changelog):
http://code.
- Enh #392: Added CStringValidator::encoding to support checking the length of multibyte strings (Qiang)
jamband后来又追加了一个说明,说从Yii 1.1.7开始,Yii总算修改了这个让人苦恼的行为,即使不改mbstring.func_overload设置,也不指定encoding属性,对于多字节字符,也会按照mb_strlen去计算字符数了。
- Enh: CStringValidator now uses application charset by default if mb_strlen is available (Sam Dark)
※application charset应该指的是config/main.php里面指定的language属性。
至此总算天下太平了。。我们后来做基于Yii的项目时候应该是挺幸运的,赶上了当时最新的Yii 1.1.7(后来换成1.1.8),如果我们早三个月(1.1.7之前)动工的话,一样要面对早期开发人员当时遇到的问题,如果不看到shuwato发的帖子以及后面jamband的回复,恐怕我们也一样会去修改php.ini来解决问题。
由此足见不管是Yii之于强,还是这些项目之于我们,时空因缘都不可思议。今天遇到的这个问题,相当于带着我们时光倒流,重新看到了Yii这样一个框架的成长和变化,各个历史时间点上的种种制约因素,以及使用它的人所做的种种思考,我们所做的事情,大概也有几分相似。