总结PHP 中的那些坑

396 阅读4分钟
原文链接: mp.weixin.qq.com

说PHP入门门槛低,其中一个原因是我们不需要关心变量的类型,PHP为我们做了自动的转化。但事实上是这样吗?下面就是一个隐蔽的bug。

字符串 == 比较类型强转隐患

http://php.net/manual/zh/language.operators.comparison.php

  1. // php 5

  2. var_dump(md5('240610708') == md5('QNKCDZO'));//bool(true)

  3. var_dump(md5('aabg7XSs') == md5('aabC9RqS'));//bool(true)

  4. var_dump(sha1('aaroZmOk') == sha1('aaK1STfY'));//bool(true)

  5. var_dump(sha1('aaO8zKZF') == sha1('aa3OFF9m'));//bool(true)

  6. var_dump('0010e2' == '1e3');//10×10^2 = 1×10^3  bool(true)

  7. var_dump('0x1234Ab' == '1193131');//bool(true)

  8. var_dump('0xABCdef' == ' 0xABCdef');//bool(true)

  9. var_dump('0e1' == '0e2'); //bool(true)

  10. // php 7 含十六进制字符串不再被认为是数字 http://php.net/manual/zh/migration70.incompatible.php

  11. var_dump('0x1234Ab' == '1193131');//bool(false)

  12. var_dump('0xABCdef' == ' 0xABCdef');//bool(false)

  13. var_dump("0x123" == "291");//bool(false)

  14. var_dump(is_numeric("0x123"));//bool(false)

  15. >>> md5('240610708')

  16. => "0e462097431906509019562988736854"

  17. >>> md5('QNKCDZO')

  18. => "0e830400451993494058024219903391"

  19. // php 是弱语言,会自动判断数据类型,0eXXXXXXXXXX 转成 0 了

  20. //来自文档:如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换为数值并且比较按照数值来进行。此规则也适用于 switch 语句。当用 === 或 !== 进行比较时则不进行类型转换,因为此时类型和数值都要比对。

  21. >>> md5('QNKCDZO')==0

  22. => true

  23. >>> md5('240610708')==0

  24. => true

  25. // 使用 === 判断 官方都建议直接用password_hash加密

  26. var_dump(md5('240610708') === md5('QNKCDZO'));//bool(false)

ps: php 7 优化和不兼容

PDO bindParam 要求第二个参数是一个引用变量

http://www.laruence.com/2012/10/16/2831.html

  1. $dbh = new PDO('mysql:host=localhost;dbname=test', "test");

  2. $query = <<<QUERY

  3.  INSERT INTO `user` (`username`, `password`) VALUES (:username, :password);

  4. QUERY;

  5. $statement = $dbh->prepare($query);

  6. $bind_params = array(':username' => "laruence", ':password' => "weibo");

  7. foreach( $bind_params as $key => $value ){

  8.    $statement->bindParam($key, $value);

  9. }

  10. $statement->execute();

  11. //期望执行 sql

  12. INSERT INTO `user` (`username`, `password`) VALUES ("laruence", "weibo");

  13. // 实际执行 sql

  14. INSERT INTO `user` (`username`, `password`) VALUES ("weibo", "weibo");

  15. //第一次循环

  16. $value = $bind_params[":username"];

  17. $statement->bindParam(":username", &$value); //此时, :username是对$value变量的引用

  18. //第二次循环

  19. $value = $bind_params[":password"]; //oops! $value被覆盖成了:password的值

  20. $statement->bindParam(":password", &$value);

  21. // 解决

  22. foreach( $bind_params as $key => &$value ) { //注意这里

  23.    $statement->bindParam($key, $value);

  24. }

  25. return $statement->execute($params);

PHP 引用

https://glot.io/snippets/estgofa359

参考鸟哥一条微博

                                
  1.    $arr = range (1 ,3 );

  2.    foreach ($arr as &$v ){

  3.     }

  4.    foreach ($arr as $v ){

  5.     }

  6.    print_r ($arr );//[1,2,2]

  7. // 解决一

  8.   $arr = range (1 ,3 );

  9.    foreach ($arr as &$v ){

  10.     }

  11.    unset ($v );

  12.    foreach ($arr as $v ){

  13.     }

  14.    print_r ($arr );//[1,2,3]

  15. // 解决二

  16.    $arr = range (1 ,3 );

  17.    foreach ($arr as &$v ){

  18.     }

  19.    foreach ($arr as $v2 ){

  20.     }

  21.    print_r ($arr );//[1,2,3]

  22. // 解决三

  23.    $arr = range (1 ,3 );

  24.    foreach ($arr as &$v ){

  25.     }

  26.    foreach ($arr as &$v ){

  27.     }

  28.    print_r ($arr );//[1,2,3]

array_merge vs +

https://glot.io/snippets/estgpi43bc

  1. //

  2. $arr1 = array(1 => "one", "2" => "two", 3 => "three");

  3. $arr2 = array(2 => "new two", 3 => "new three");

  4. print_r($arr1 + $arr2);

  5. Array

  6. (

  7.    [1] => one

  8.    [2] => two

  9.    [3] => three

  10. )

  11. print_r(array_merge($arr1, $arr2));

  12. Array

  13. (

  14.    [0] => one

  15.    [1] => two

  16.    [2] => three

  17.    [3] => new two

  18.    [4] => new three

  19. )

浮点数精度问题

http://php.net/manual/zh/language.types.float.php

  1. var_dump(15702>=(157.02*100));//bool(false)

  2. var_dump(11111>=(111.11*100));//bool(true)

  3. var_dump(bcsub(15702,(157.02*100)) >= 0);//bool(true)

  4. if(abs(15702-(157.02*100)) < 0.001) {

  5.    echo "相等";

  6. } else {

  7.    echo "不相等";

  8. }

  9. $f = 0.58;

  10. var_dump(intval($f * 100)); //57 0.58 * 100 = 57.999999999...

in_array switch

https://glot.io/snippets/esth249n0y

  1. $arr = ['a', 'pro' => 'php', 8, true];

  2. var_dump(in_array(2, $arr)); // bool(true)

  3. var_dump(in_array('b', $arr)); // bool(true)

  4. var_dump(in_array(0, $arr)); // tbool(true)

  5. var_dump(in_array(null, $arr)); // bool(false)

  6. var_dump(in_array(2, $arr, true)); // bool(false)

  7. var_dump(in_array(0, $arr, true)); // bool(false)

  8. $name = 0;

  9. switch ($name) {

  10.          case "a":

  11.               //...

  12.               break;

  13.          case "b":

  14.               //...

  15.               break;

  16.     }

  17.    switch (strval($name)) {

  18.          case "a":

  19.               //...

  20.               break;

  21.          case "b":

  22.               //...

  23.               break;

  24.     }

strpos

https://glot.io/snippets/esthlvjtki

  1. function getReferer($link)

  2. {

  3.    $refMap = [

  4.        'baidu' => '百度',

  5.        'sougou' => '搜狗',

  6.        '360' => '360',

  7.        'google' => '谷歌'

  8.    ];

  9.    foreach ($refMap as $key => $value) {

  10.        if (strpos($link, $key) !== false) {

  11.            return $value;

  12.        }

  13.    }

  14.    return '其他';

  15. }

  16. // https://secure.php.net/manual/zh/function.strpos.php 如果 needle 不是一个字符串,那么它将被转换为整型并被视为字符的顺序值。

  17. echo getReferer('https://www.google.com/search?workd=google');//360

  18. // 解决

  19. function getReferer($link)

  20. {

  21.    $refMap = [

  22.        'baidu' => '百度',

  23.        'sougou' => '搜狗',

  24.        '360' => '360',

  25.        'google' => '谷歌'

  26.    ];

  27.    foreach ($refMap as $key => $value) {

  28.        if (mb_strpos($link, $key) !== false) {

  29.        //if (strpos($link, strval($key)) !== false) {

  30.            return $value;

  31.        }

  32.    }

  33.    return '其他';

  34. }

curl 文件上传

http://php.net/manual/zh/curlfile.construct.php

  1. //PHP的cURL支持通过给CURL_POSTFIELDS传递关联数组(而不是字符串)来生成multipart/form-data的POST请求

  2. if (class_exists('\CURLFile')) {

  3.    $field = array('fieldname' => new \CURLFile(realpath($filepath)));

  4. } else {

  5.    $field = array('fieldname' => '@' . realpath($filepath));

  6. }

foreach 顺序

  1. $arr=[];

  2. $arr[2] = 2;

  3. $arr[1]  = 1;

  4. $arr[0]  = 0;

  5. foreach ($arr as $key => $val) {

  6. echo $val;// 2 1 0

  7. }

  8. while (list($key, $v) = each($arr)) {

  9.   //获取不到  foreach会自动reset,each之前, 先reset数组的内部指针

  10. }

  11. for($i=0,$l=count($arr); $i<$l; $i++) {

  12.    echo $arr[$i];// 0 1 2

  13. }

json_decode

  1. >>> json_decode('php')

  2. => null

  3. >>> json_decode('0x123')

  4. => 291

strtotime('-x month')

  1. date_default_timezone_set('Asia/Shanghai');

  2. $t = strtotime('2017-08-31');

  3. echo date('Ym',strtotime('- 1 month',$t));//201707

  4. echo date('Ym',strtotime('- 2 month',$t));//201707

  5. //

  6. $first_day_of_month = date('Y-m',strtotime('2017-08-31')) . '-01 00:00:01';

  7. $t = strtotime($first_day_of_month);

  8. echo date('Ym',strtotime('- 1 month',$t));//201707

  9. echo date('Ym',strtotime('- 2 month',$t));//201706

  10. echo date("Ym", strtotime("-2 month", strtotime("first day of 2017-08-31")));//201706