PHP 的陷阱

一、參考文章

1、#trim截取亂碼 ?? #字符串16進制轉(zhuǎn)換 ?? #mb移除字符的實現(xiàn)
2、#你真的了解strtotime么

二、trim的坑

1、trim的困惑

## 例1
var_dump(trim('adsssas、', '、')); ## 打印 adsssas
## 例2
var_dump(trim('哈哈哈哈哈、', '、')); ## 打印 哈哈哈哈哈
## 例3
var_dump(trim('小米科技、', '、')); ## 打印 小米科? 亂碼了

2、trim移除字符的規(guī)則

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

知識點:trim移除字符規(guī)則:trim會循環(huán)去掉字符串首位存在的字符、直到?jīng)]有沒有可移除的字符
a. 上述例子執(zhí)行過程是:
?????循環(huán)a存在字符串a(chǎn)b中,去掉,剩下bacabb
?????循環(huán)b存在字符串a(chǎn)b中,去掉,剩下acabb
?????循環(huán)a存在字符串a(chǎn)b中,去掉,剩下cabb
?????循環(huán)c不存在字符串a(chǎn)b中,停止循環(huán),所以去掉首字符就剩下:cabb。
由于trim是過濾首尾字符,所以還會從末尾循環(huán)去掉。
最后打印 c

b. 移除中文亂碼的問題:
trim是不支持寬字節(jié)的、所以在進行去移除字符的時候
的十六進制碼為0xe3 0x80 0x81 trim會當(dāng)成三個字符
哈、的十六進制是0xe5 0x93 0x88 0xe3 0x80 0x81
技、的十六進制是0xe6 0x8a 0x80 0xe3 0x80 0x81
所以 小米科技、的技的 0x80 部分也會被trim移除掉 所以會亂碼
3、解決方法

//trim寬字節(jié)實現(xiàn)
//$encoding 是必傳參數(shù)
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的規(guī)則
雖然這個問題看起來很迷惑, 但從內(nèi)部邏輯上來說呢, 其實是 "對" 的
date內(nèi)部的對于這種事情的處理邏輯:
?????先做-1 month, 那么當(dāng)前是07-31, 減去一以后就是06-31.
?????再做日期規(guī)范化, 因為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");##得到系統(tǒng)的年月
$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);##得到當(dāng)前月份的前幾月
## 解決方法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、結(jié)論
a、switch 的 type 為0時代碼的時候始終走第一個case switch 因此使用switch時一定要注意
b、原來 switch 語句不遇到 break 將不會自己"拐彎"

四、explode 的坑

1、示例

$arr = explode('|','');
if (!empty($arr)) {
    echo 'true';
}
## 打印 true
## var_dump($arr) 是一個空字符數(shù)組

2、結(jié)論
explode 分割空字符串時返回的結(jié)果并不是空數(shù)組 而是包含一個空字符元素的數(shù)組

五、empty 的坑

1、示例

## type 是通過魔術(shù)方法取得的值
if(empty($param->type)) {
    echo 'true';
} 
## 不管type的值是多少 該判斷語句始終打印 true

2、結(jié)論
empty中的參數(shù)是對象屬性時,如果對象屬性的值是通過魔術(shù)方法獲取,則empty始終返回true

六、strpos 的坑

1、示例

if (strpos('abcd','a') != false) {
    echo 'true';
} else {
    echo 'false';
}
## 我們想的是 如果 'a' 在 'abcd' 中返回 true   但是上邊代碼打印 false
##由于 strpos('abcd','a') 返回 a 首次出現(xiàn)的位置 0  == false

2、結(jié)論
strpos查找字符串首次出現(xiàn)的位置,字符串位置是從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)); ## 打印 數(shù)組 a、b、c、d、e、f
var_dump($a + $b); ## 打印 數(shù)組 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));  ## 打印 數(shù)組 d、e、f
var_dump($a + $b);  ## 打印 數(shù)組 a、b、c

2、結(jié)論
a.當(dāng)下標(biāo)為數(shù)值時,array_merge()不會覆蓋掉原來的值,但array+array合并數(shù)組則會把最先出現(xiàn)的值作為最終結(jié)果返回,而把后面的數(shù)組擁有相同鍵名的那些值“拋棄”掉(不是覆蓋)
b.當(dāng)下標(biāo)為字符時,array+array仍然把最先出現(xiàn)的值作為最終結(jié)果返回,而把后面的數(shù)組擁有相同鍵名的那些值“拋棄”掉,但array_merge()此時會覆蓋掉前面相同鍵名的值
注意:array_merge_recursive() 不會進行鍵名覆蓋,而是將多個相同鍵名的值遞歸組成一個數(shù)組

八、iconv的坑

1、示例

##  utf-8編碼轉(zhuǎn)成gb2312 如果遇到一些特別字符時,如:"—",英文名中的"."等等字符,轉(zhuǎn)換就斷掉
iconv('utf-8','gb2312',$file);

2、解決方法

## 第二個參數(shù),加上//IGNORE,忽略錯誤
iconv("UTF-8","GB2312//IGNORE",$file);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容