PHP 的陷阱

一、参考文章

1、#trim截取乱码    #字符串16进制转换    #mb移除字符的实现
2、#你真的了解strtotime么

二、trim的坑

1、trim的困惑

## 例1
var_dump(trim('adsssas、', '、')); ## 打印 adsssas
## 例2
var_dump(trim('哈哈哈哈哈、', '、')); ## 打印 哈哈哈哈哈
## 例3
var_dump(trim('小米科技、', '、')); ## 打印 小米科� 乱码了

2、trim移除字符的规则

var_dump(trim('abacabb', 'ab'));
## 猜猜打印啥? acaabb ?
## 其实打印 c  [捂脸]

知识点:trim移除字符规则:trim会循环去掉字符串首位存在的字符、直到没有没有可移除的字符
a. 上述例子执行过程是:
     循环a存在字符串ab中,去掉,剩下bacabb
     循环b存在字符串ab中,去掉,剩下acabb
     循环a存在字符串ab中,去掉,剩下cabb
     循环c不存在字符串ab中,停止循环,所以去掉首字符就剩下:cabb。
由于trim是过滤首尾字符,所以还会从末尾循环去掉。
最后打印 c

b. 移除中文乱码的问题:
trim是不支持宽字节的、所以在进行去移除字符的时候
的十六进制码为0xe3 0x80 0x81 trim会当成三个字符
哈、的十六进制是0xe5 0x93 0x88 0xe3 0x80 0x81
技、的十六进制是0xe6 0x8a 0x80 0xe3 0x80 0x81
所以 小米科技、的技的 0x80 部分也会被trim移除掉 所以会乱码
3、解决方法

//trim宽字节实现
//$encoding 是必传参数
function mb_trim($string, $trim, $encoding = 'utf-8') {
    $mask = array();
    $trimLength = mb_strlen($trim, $encoding);
    for ($i = 0; $i < $trimLength; $i++) {
        $item = mb_substr($trim, $i, 1, $encoding);
        $mask[] = $item;
    }
    $len = mb_strlen($string, $encoding);
    if ($len > 0) {
        $i = $len - 1;
        do {
            $item = mb_substr($string, $i, 1, $encoding);
            if (in_array($item, $mask)) {
                $len--;
            } else {
                break;
            }
        } while ($i-- != 0);
    }
    return mb_substr($string, 0, $len, $encoding);
}
三、strtotime 的坑

1、strtotime的困惑

## 例1
var_dump(date('Y-m-d',strtotime('-1 month',strtotime('2019-03-10')))); ##  打印 2019-02-10
## 例2
var_dump(date('Y-m-d',strtotime('-1 month',strtotime('2019-03-29')))); 
## 猜猜打印啥   2019-02-29 ? 2019-02-28 ?
## 其实打印  2019-03-01 [捂脸]

2、date的规则
虽然这个问题看起来很迷惑, 但从内部逻辑上来说呢, 其实是 "对" 的
date内部的对于这种事情的处理逻辑:
     先做-1 month, 那么当前是07-31, 减去一以后就是06-31.
     再做日期规范化, 因为6月没有31号, 所以就好像2点60等于3点一样, 6月31就等于了7月1
     是不是逻辑很”清晰”呢? 我们也可以手动验证第二个步骤, 比如:

var_dump(date("Y-m-d", strtotime("2017-06-31"))); ## 打印2017-07-01

## 验证其他月份
var_dump(date("Y-m-d", strtotime("-1 month", strtotime("2017-03-31"))));##打印2017-03-03
var_dump(date("Y-m-d", strtotime("+1 month", strtotime("2017-08-31"))));##打印2017-10-01
var_dump(date("Y-m-d", strtotime("next month", strtotime("2017-01-31"))));##打印2017-03-03
var_dump(date("Y-m-d", strtotime("last month", strtotime("2017-03-31"))));##打印2017-03-03

3、解决方法

## 解决方法1:
$tmp_date = date("Ym");##得到系统的年月
$tmp_year = substr($tmp_date, 0, 4);##切割出年份
$tmp_mon = substr($tmp_date, 4, 2);##切割出月份
$time = mktime(0, 0, 0, $tmp_mon - $sign, 1, $tmp_year);##得到当前月份的前几月
## 解决方法2: PHP5.3之后新增
date("Y-m-d", strtotime("first day of +1 month", strtotime("2017-08-31"))); ##2017-09-01
date("Y-m-d", strtotime("last day of -1 month", strtotime("2017-03-31"))); ## 2017-02-28
date("Y-m-d", strtotime("first day of next month", strtotime("2017-01-31"))); ## 2017-02-01
三、switch 的坑

1、switch的困惑

function switchTest1($key) {
    switch ($key) {
        case 'per':
            echo 'per';
            break;
        case 'pro':
            echo 'pro';
            break;
        default:
            echo 'exit';
    }
}

function switchTest2($key) {
    switch ($key) {
        case 'per':
            echo 'per';
        case 'pro':
            echo 'pro';
        default:
            echo 'exit';
    }
}
switchTest1('per');  ## 打印 per
switchTest1(0);
## 猜猜打印啥? exit ?
## 其实打印 per  [捂脸]

switchTest2('per');  ## 打印 perproexit

2、结论
a、switch 的 type 为0时代码的时候始终走第一个case switch 因此使用switch时一定要注意
b、原来 switch 语句不遇到 break 将不会自己"拐弯"

四、explode 的坑

1、示例

$arr = explode('|','');
if (!empty($arr)) {
    echo 'true';
}
## 打印 true
## var_dump($arr) 是一个空字符数组

2、结论
explode 分割空字符串时返回的结果并不是空数组 而是包含一个空字符元素的数组

五、empty 的坑

1、示例

## type 是通过魔术方法取得的值
if(empty($param->type)) {
    echo 'true';
} 
## 不管type的值是多少 该判断语句始终打印 true

2、结论
empty中的参数是对象属性时,如果对象属性的值是通过魔术方法获取,则empty始终返回true

六、strpos 的坑

1、示例

if (strpos('abcd','a') != false) {
    echo 'true';
} else {
    echo 'false';
}
## 我们想的是 如果 'a' 在 'abcd' 中返回 true   但是上边代码打印 false
##由于 strpos('abcd','a') 返回 a 首次出现的位置 0  == false

2、结论
strpos查找字符串首次出现的位置,字符串位置是从0开始不是从1开始,没有找到字符串返回false
strpos('abc','a') !== false 注意是两个等号

七、array_merge 和 + 的坑

1、示例

## 例1
$a = array('0' => 'a', '1' => 'b', '2' => 'c');
$b = array('0' => 'd', '1' => 'e', '2' => 'f');
var_dump(array_merge($a, $b)); ## 打印 数组 a、b、c、d、e、f
var_dump($a + $b); ## 打印 数组 a、b、c
## 例2
$a = array('0a' => 'a', '1a' => 'b', '2a' => 'c');
$b = array('0a' => 'd', '1a' => 'e', '2a' => 'f');
var_dump(array_merge($a, $b));  ## 打印 数组 d、e、f
var_dump($a + $b);  ## 打印 数组 a、b、c

2、结论
a.当下标为数值时,array_merge()不会覆盖掉原来的值,但array+array合并数组则会把最先出现的值作为最终结果返回,而把后面的数组拥有相同键名的那些值“抛弃”掉(不是覆盖)
b.当下标为字符时,array+array仍然把最先出现的值作为最终结果返回,而把后面的数组拥有相同键名的那些值“抛弃”掉,但array_merge()此时会覆盖掉前面相同键名的值
注意:array_merge_recursive() 不会进行键名覆盖,而是将多个相同键名的值递归组成一个数组

八、iconv的坑

1、示例

##  utf-8编码转成gb2312 如果遇到一些特别字符时,如:"—",英文名中的"."等等字符,转换就断掉
iconv('utf-8','gb2312',$file);

2、解决方法

## 第二个参数,加上//IGNORE,忽略错误
iconv("UTF-8","GB2312//IGNORE",$file);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。