高级语言已能灵活、优雅地处理字符串操作了,在PHP中,对变量赋以字符串十分简单:
$str = "Hello, World!"; $str = 'I am very happy.';
单引号与双引号的区别,是许多PHPer晶晶乐道的话题。除了功能区别外,运行速度也是大家讨论的焦点。先说两者在功能上的区别。
单引号是标注字符串最简单的方法,单引号的转义规则极其简单:只需对’和\进行转义:
$str = 'My document\'s path is D:\\documents\\'; //$str的内容为:My document's path is D:\documents\
与单引号不同,双引号不仅支持更多的转义符号,还支持引用变量内容:
$path = "D:\\documents\\"; $str = "My document\'s path is $path"; //$str的内容为:My document's path is D:\documents\
至于两者的速度,从逻辑上推理,单引号的功能少、规则简单,应该比双引号的速度快。风雪之隅也曾撰文《PHP的单引号和双引号》,从Opcodes的层面,得出了“单引号比双引号快”这个结论。但我之前针对纯字符串(无转义、无变量引用)做过测试,得出的结论竟然是“双引号比单引号快”。大家也可以写一下代码测试一下单双引号在不同情况下的速度。
很多初学者在接触纯PHP编程时,在转义的处理上,极少出错。但当程序中有正则表达式、SQL语句出现时,需要将相关的表达式、语句用字符串传递给相关函数,则会出现一些问题。下面用2个例子来说明。
例1、正则表达式
某网站的Ajax接口返回一段包含HTML代码的JSON字符串,如下:
"<table>\n\t<tr>\n\t\t<td width=\"100\">Hello, World!<\/td>\n\t<\/tr>\n<\/table>\n"
现在我们通过PHP的file_get_contents()函数,将以上字符串装进了变量$c,现在要提取其中td标签内的内容,请用正则表达式来完成。
首先考虑正则表达式如何写:
/<td width="100">(.*?)<\/td>/sm
其中的“\/”是对正则表达式中的“\”进行转义。现在要将如上的正则表达式,装进PHP的变量内。有2个方法装:
1、间接法
间接法可免去将正则表达式翻译成PHP字符串的麻烦,将正则表达式装入文本文件、数据库、memcache等容器,在PHP程序中,读取该值,传入变量,再将此变量传递给preg_match函数,即可。
2、直接法
直接在程序中包含正则表达式,有2种办法:
a、单双引号法
单双引号法,就是将正则表达式,根据单双引号的转义规则,装入单双引号,然后赋值给变量。
为了避免双引号复杂的转义规则与正则的转义规则相混淆,个人建议所有的正则表达式,都使用单引号。上述正则表达式可通过如下方式赋值:
$regex = '/<td width="100">(.*?)<\/td>/sm';
b、Heredoc或Nowdoc法
PHP支持Heredoc和Nowdoc的定界符赋值。以上正则表达式可如下赋值:
$regex = <<<EOT /<td width="100">(.*?)<\/td>/sm EOT;
Heredoc的好处在于,在界定符内,可输入任何你想要的字符,不用担心转义。当然,Heredoc会自动解析变量,而Nowdoc则不会。要想使用Nowdoc,首先确定PHP的版本大于等于5.3.0,然后通过以下代码赋值:
$regex = <<<'EOT' /<td width="100">(.*?)<\/td>/sm EOT;
c、总结
无论是通过单双引号法,还是通过Heredoc、Nowdoc法,都是跟PHP在打交道,我们的目的无非是让PHP得到我们最终想传递给相关函数的正则表达式。所以,请确保你的正则表达式是正确的。如果你的正则表达式远比上述的复杂,甚至涉及到了变量,则需要对相关变量,做escape quote的操作。否则,将会引发错误甚至是程序漏洞。
举例说明:
我们的网站规定,用户的Username中,比如包含其Firstname。如我的Firstname是Gerry,那么我在该网站注册时,Username可以是Gerry_Hu、Gerry123,但不能是Hackfan。
相关程序:
$firstname = $user->firstname;
$regex = '/'.$firstname.'/gi';
if(preg_match($regex, $username, $match))
{
//OK
}
以上代码,存在“正则注入”的漏洞。SQL注入漏洞大家都很熟悉,正则表达式,如果在编程时未注意,也会产生注入漏洞,危害系统安全。
如果我将firstname构造为(.*),那么任何username都会通过审查。所以,我们要如下处理:
$regex = '/'.preg_quote($firstname, '/').'/gi';
preg_quote将需要匹配的字符串值,进行了转义。preg系的preg_quote函数就好比是mysql系的mysql_escape_string函数。
例2、SQL
讲完了复杂的正则表达式与单双引号、转义的关系,SQL相比就显得简单多了。
由于在ANSI-SQL标准中,字符串使用单引号,所以我们的SQL语句,也尽量符合规范标准。以下是从MySQL数据库中,取出某用户资料的SQL:
SELECT * FROM `user` WHERE `username` LIKE 'hackfan';
其中’hackfan’是字符串。将上述SQL语句放入PHP程序:
$sql = "SELECT * FROM `user` WHERE `username` LIKE 'hackfan';";
由于SQL语句中,很少会涉及到转义的引用,且单引号在SQL中出现的频率很高,因此,个人建议凡是涉及SQL语句的,在PHP中,全部用双引号进行赋值。
纠结SQL与PHP的单双引号应用的朋友不多,只是还有不少朋友对与SQL注入不够重视,屡屡留下注入漏洞。
总结
本文介绍了PHP的字符串赋值方法,以及单双引号的转义规则。并举例说明PHP字符串与正则表达式、SQL结合时,所可能出现的问题,以及解决方法。
1、尽量使用单引号进行字符串操作——避免双引号复杂的转义规则。
2、尽量在正则表达式应用时,使用单引号——避免双引号的转义规则与正则表达式的转义规则相混淆。
3、尽量在SQL应用时,使用双引号——SQL中经常出现单引号,但不常出现转义。
标签:IP, PHP, SQL, SQL注入, 引号, 正则表达式, 转义