比如 if( 0.5+0.2==0.7 ) 的结果是 false。究其原因是因为,php是基于c语言的,而c语言由于其二进制符点数的表示方式,导致不能精确表示大多数符点数。实际上,几乎所有的编程语言都没能精确表示小数(符点数),这是一个普遍存在的现象,因为这个是 ieee 754 的缺陷。想要解决此问题,只能另立标准,似乎只有mathematica解决了此问题。
二、字符串是否相同建议用 === 而非 == 为什么呢?因为这个比较是弱类型,两个比较时,php会先尝试判别左右两者是否为数字。而问题就在于什么样的字符串是数字,是单纯的数字串吗?远远不只于此,还包括 0x 开头的十六进制,xxex类型的科学记数法 等等,如 '12e0'=='0x0c' 得到的是true。而在数值类型与字符串比较时,甚至一些数字开头的非数值串,比如 12=='12这个串' 得到的值也会是 true。
所以这些情况下,可能会使本来并不相同的字符串被判定为相等。而使用===比较则为包含类型的比较,不会有任何转换,所以是可以准确比较字符串是否相同的。
另外吐槽一下java,==居然比较不了字符串是否相等,因为字符串是一个对象,==变成了判断是否为同一个对象……
三、trim系列函数的过多去除 trim函数的基本用法是去除最外边的空格、换行符之类的。因为其可选参数,很多人也会将其用于去除utf8bom头、文件扩展名等等,比如 ltrim($str, \xef\xbb\xbf); rtrim($str, .txt); 。但是很快,就会发现这些函数会多去除了一些东西,比如本来是想去除后缀的,结果 logtext.txt 会变成了 logte 而不是 logtext。为什么呢?因为后面这个参数的意思不是一个完整字符串,而是字符列表,也就是说会一直检查最左/最右是否符合此列表的其中一个。
那怎么样才是真正我们想要的去掉最前最后呢?网上的说法是说用正则表达式,我封装了对应的三个方法,以便使用。命名规则是比原来php的函数多了个s,表示string的意思。用法跟原来php的函数一样。
ltrims(, = ( (/^{}/, '', rtrims(, = ( (/{}$/, '', trims(, = ( = (/^{}/, '', (/{}$/, '', trimbom( (/^\xef\xbb\xbf/, '',
四、网上说的获取客户端ip地址的各种方法 网上流行一段获取客户端ip地址的php函数如下:
function getip() { if (getenv('http_client_ip')) { $ip = getenv('http_client_ip'); }elseif (getenv('http_x_forwarded_for')) { $ip = getenv('http_x_forwarded_for'); }elseif (getenv('http_x_forwarded')) { $ip = getenv('http_x_forwarded'); }elseif (getenv('http_forwarded_for')) { $ip = getenv('http_forwarded_for');} }elseif (getenv('http_forwarded')) { $ip = getenv('http_forwarded'); }else { $ip = $_server['remote_addr']; } return $ip; }
这函数看起来并没有什么问题,很多开源cms之类的也在用。然而事实上,问题大着呢!首先第一步,是要了解这些 getenv 读取的东西到底是什么玩意,又是从哪来的。简单来说这些其实是http header,有些代理服务器会把源请求地址放到header里,所以我们服务器可以知道访问用户的原始ip地址。但是,并不是所有代理服务器都会这么做,也并不是只有代理服务器会这么做。
而实际上,这些http header是可以随便改动的,比如curl就可以自己设置各种http header。如果用此函数得到的结果,进行ip限制等操作的话是很轻易绕过的。更可怕的是,如果后续程序没有对此函数取得的ip地址进行格式校验过滤的话,就很微妙地为sql注入打开了一扇窗户。所以比较保险的方式是只读取非http header的 $_server['remote_addr']
php5.4及以上可以使用以下函数判断是否符合ip地址格式 filter_var($ip, filter_validate_ip) ,老版本需自行写正则。
五、foreach的保留现象 使用 foreach($somearr as $somel){ } 之类的用法时,要注意最后的一个 $somel 会一直保留到该函数/方法结束。而当使用引用的时候 foreach($somearr as &$somel){ }这是以引用来保存,也就是说后面若有使用同一个名字的变量名,将会把原数据改变(就像一个乱用的c指针)。为安全起见,建议每个foreach(尤其是引用的)结束之后都使用unset把这些变量清除掉。
foreach($somearr as &$somel){ //dosomething ...}unset($somel);
六、htmlspecialchars 函数默认不转义单引号 不少网站都是使用此函数作为通用的输入过滤函数,但是此函数默认情况是不过滤单引号的。这是非常非常地容易造成xss漏洞。这样的做法和不过滤双引号没太大区别,只要前端写得稍微有点不规范(用了单引号)就会中招。下面这个示例改编自知乎梧桐雨的回答<!doctype html>
' />
要求所有的时候都使用双引号不得使用单引号,这其实不太现实。所以,这个主要还是后端的责任,把单引号也要转义,我们用的时候一定要给这个函数加上参数 htmlspecialchars( $data, ent_quotes);
很多人向thinkphp框架提出过这个问题,因为其默认过滤方法就是无参数的htmlspecialchars,不过滤单引号,而其官方答复是“i函数的作用不能等同于防止sql注入,可以自定义函数来过滤”……毛线啊,最基本的防护都不给力,这是给埋了多少隐患啊。在此强烈各位使用者重新定义默认过滤函数,我自己定义的是 htmlspecialchars(trim($data), ent_quotes); ,有更好建议欢迎评论。同时非常希望tp官方更正此问题。
关于xss,容我多说两句,请看下面这个例子。
<?php $name='alert(1)'; ?><p id=xss2></p><script src=//cdn.batsing.com/jquery.js></script><script>$(#xss2)[0].innerhtml = <?=$name?>; $(#xss2).html( <?=$name?> ); $(#xss2)[0].innerhtml = <?=$name?>; $(#xss2).html( <?=$name?> );</script>
其中第1、2行 js会造成 xss 漏洞,第3、4行则不会。而 alert(1) 这样一种字符串,后端甚至没有什么比较好的方法可以过滤,唯一有效的方法可能是在数据的两端加上引号。主要责任还是在于前端,对 innerhtml 和 jquery的html() 的输出使用时,一定要确保传入的参数是字符串,否则其危险性不亚于 eval 函数
