php常見的坑


10、filesize緩存的問題

PHP的filesize居然會緩存(當然還有不少,這里僅用filesize舉例,其它會緩存的函數,以官方文檔為准)
線上代碼經常隨機出各種問題,排查了1個月,線上加各種日志,最終發現是filesize緩存的問題,如下代碼:

[php]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. echo filesize("a.txt");  
  2. exec("rm a.txt");  // 刪除文件  
  3. echo filesize("a.txt"); // 這里會輸出大小,而不是報錯說文件不存在  
  4.   
  5. echo filesize("b.txt");  
  6. echo filesize("a.txt"); // 這里會報錯文件不存在,因為只緩存最后一個文件,緩存里只有b,沒有a的緩存了  

看到了吧,filesize不僅有緩存,而且還只緩存最后一個文件,所以說PHP的開發人員也不知道怎么考慮的,就不會加個filesize_withcache方法?
知道了原因,解決也就簡單了,在filesize調用前清除緩存,加代碼: clearstatcache()
參考官方文檔:http://php.net/manual/zh/function.filesize.php

 

9、保存源碼文件時,注意要使用utf-8無bom簽名

之前用Windows的記事本編輯文件,發布到Linux上線后,一直報錯:Cannot modify header information - headers already sent by (output started at xxx.php:1)
用Winmerge或BeyondCompare對比代碼也無法發現問題,后來用Netbeans才發現文件最前面多了一個不可見字符,研究后才知道是Windows的Bom簽名
也就是說,在Windows上開發,在Linux上發布,注意要使用不支持Bom的編輯器,如果用VisualStudio要選擇高級保存選項里的不帶簽名

 

1、null和空、0、false等四個值的比較

在PHP中,== 會先進行類型轉換,再進行對比,而===會先比較類型,如果類型不同直接返回不相等,參考如下示例

[php]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. $a = null; $b = ''; $c = 0; $d = false;  
  2. echo ($a == $b)?1:0;    // 輸出1  
  3. echo ($a === $b)?1:0;   // 輸出0  
  4. echo ($a == $c)?1:0;    // 輸出1  
  5. echo ($a === $c)?1:0;   // 輸出0  
  6. echo ($b == $c)?1:0;    // 輸出1  
  7. echo ($b === $c)?1:0;   // 輸出0  
  8. echo ($a == $d)?1:0;    // 輸出1  
  9. echo ($a === $d)?1:0;   // 輸出0  

對於我這種以前只寫js或C#代碼的碼農,被這幾個值忽悠過n次,n大於3

 

 

 

2、strrchr函數

W3School站點上的注釋如下:

strrchr() 函數查找字符串在另一個字符串中最后一次出現的位置,並返回從該位置到字符串結尾的所有字符。
如果成失敗,否則返回 false。

實際上,這個函數是查找某個字符,而不是查找字符串,應該參考官方文檔

代碼示例:

[php]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. $a = 'abcdef.txt';  
  2. $b = '.php';  
  3. echo strrchr($a, $b);  

上面的代碼輸出是:.txt

也就是說,如果$b是字符串,只使用第一個字符,后面的其它字符會忽略

注:php提供了strstr函數,為什么不提供strrstr函數呢,雖然自己實現也很簡單

 

 

3、foreach里的引用賦值參見官方文檔

這個引用賦值很好哇,對用C#的我,在C#里要修改foreach的元素,是不可能的,是會出異常滴,php把這個變成了可能,但是:
在官方文檔里有一句警告:Warning 數組最后一個元素的 $value 引用在 foreach 循環之后仍會保留。建議使用 unset() 來將其銷毀。
我們看一組代碼:

[php]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. $a = [1,2,3];  
  2. foreach($a as &$item){  
  3.     echo $item . ',';  
  4. }  
  5. //unset($item); // 引用賦值后不銷毀對象  
  6. foreach($a as $item){  
  7.     echo $item . ',';  
  8. }  

上面的代碼的輸出如下:
1,2,3,1,2,2  看最后一個輸出的是2,而不是3,就是因為代碼里沒有銷毀$item造成的,原因如下:
第一個循環,把3的引用賦給了$item,第二個循環,把1賦給了$item,因為$item是引用,導致數組的元素3變成了1,明白了嗎?

 

4、isset與empty的聯系和區別isset文檔 empty文檔
empty對如下8種情況返回true:
  null、 空串""、字符串0"0"、空array、布爾值false、數字0、浮點數0.0、類里用var定義但是未賦值

isset 檢測變量是否設置,並且不是 NULL,但是對於empty的8種情況,只有null返回false,其它7種情況都返回true

綜上所述,除了empty描述的的非null的7種情況,在其它情況下, if(empty(變量)) 等效於 if(!isset(變量))

靈活用法一則:直接訪問 $arr['aaa'] 可能報錯,說aaa不存在,可以用:
if(isset($arr['aaa']){ 操作代碼} 或 if(!empty($arr['aaa']){ 操作代碼}

 

5、trim函數遇到中文空格時,會亂碼

[php]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. $str = '  《前后有全半角空格》  ';  
  2. var_dump($str);  
  3. $str2 = trim($str, '  ');  
  4. var_dump($str2);  
  5. $str3 = mb_ereg_replace('^(?:\s| )+|(?:\s| )+$', '', $str);  
  6. var_dump($str3);  
  7. $str4 = mb_ereg_replace('^[\s ]+|[\s ]+$', '', $str);  
  8. var_dump($str4);  

參考如上的代碼,輸出結果:

[plain]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. string '  《前后有全半角空格》  ' (length=38)  
  2. string '�前后有全半角空格》' (length=28)  
  3. string '《前后有全半角空格》' (length=30)  
  4.   
  5. A PHP Error was encountered  
  6. Severity: Warning  
  7. Message: mb_ereg_replace(): mbregex compile err: invalid code point value  

可以看出:trim導致亂碼出現了,正則^(?:\s| )+|(?:\s| )+$ 可以正常工作,而正則^[\s ]+|[\s ]+$卻編譯異常,原因我還沒搜索到


6、intval在Windows和Centos上,最大值范圍不同
在centos上,intval轉換的最大值是9223372036854775807,

而在我的Win7x64+64位的php上,轉換最大值卻是2147483647,非常奇怪,手冊明明說64位系統都是9223372036854775807,
這么大一坑啊,官方解釋php6以下在win上僅是測試版,不支持64位

 

7、MySQL字段類型為varchar時,不能用where xx=123來檢索,會無法利用索引
建議所有SQL的值都用單引號括起來,如:where xx='123',如果字段類型為int,也能正確利用索引

 

8、執行SQL后不判斷返回值,或判斷邏輯錯誤:

 

$sql = 'insert into app_log(id)  select 0 from dual where 1=2';
$this->db->query($sql);
return true; // 不加判斷,直接返回true

$sql = 'insert into app_log(id)  select 0 from dual where 1=2';
$result = $this->db->query($sql);
if ($result) { // 有bug,插入不成功,result也是true
    return true;
}
return false;

上面的2段代碼應該改成:

$sql = 'insert into app_log(id)  select 0 from dual where 1=2';
$this->db->query($sql);
// 如果sql有語法問題,affected_rows是-1
if ($this->db->affected_rows() > 0) {
    return true;
}
return false;

注意:mysql中,update a set name='123' where id=1;
如果id為1的記錄,name已經是123,那么這條update語句的affected_rows()=0

 

 

 

原文鏈接:http://blog.csdn.net/youbl/article/details/41014367


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM