這篇文件我將繼續PHP之旅,並對網上找到的一些PHP中常見的錯誤進行收集和總結,希望對大家有所幫助。
1. 小錯誤也能釀大禍
你碰到過僅僅因爲一個小小的分號而導致程序出現大問題的情況麼?在PHP中如果你while循環的條件後面加上了分號的時候會導致PHP陷入死循環,而且這種錯誤設置都不會被PHP異常處理系統捕獲。我們先看看代碼吧:
$i = 0;
while($i < 20); {
//some code here
$i++;
}
echo $i;
這段代碼不會導致系統報告錯誤,然而卻會導致系統進入死循環並且最終因爲無法及時響應而返回執行超時的錯誤。
不過在C#中同樣的會在編譯時就被編譯器捕獲,並且編譯器會知道這是個空循環的錯誤。當然如果用戶一定犯傻寫出另外的死循環編譯器就沒辦法了,如:
int i = 0;
while (i < 20) ;
這種情況編譯器不會報錯。
2. 忘了在break或continue後面加上分號。
同樣是小小的符號導致巨大的差異,看下面的代碼先:
for($i = 0; $i < 10; $i++) {
for($j = 0; $j < 10; $j++) {
if($i == 0)
break
print $j;
}
}
這段代碼最終只會輸出0的結果,然後退出循環,然而我們的意願原本是輸出除0外的其他數字。當然如果我們使用規範的方式(如在if條件判斷之後加大括號)這個錯誤很容易避免。
3.單引號還是雙引號?
這條嚴格以上說不能算錯誤,首先看下面的兩行代碼:
echo ‘it\’s ok’;
echo “it’s not”;
PHP不像C#,單引號和雙引號都可以用來表示字符串,因此很多PHP新手往往分不清什麼時候該用哪種符號來包裹字符串,或者乾脆想用什麼就用什麼符號。不過單引號和雙引號在PHP中是有使用技巧的。如果一個字符串由雙引號開始,那麼只有雙引號被分析器解析。這樣,你就可以在雙引號串中包含任何其他字符,甚至單引號和PHP變量。e.g.
$var1=’Sean Zhu’;
echo ‘Hello, $var1. I’m Mike!’;//輸出:Hello, $var1. I’m Mike!
echo “Hello, $var1. I’m Mike!”;//輸出:Hello, Sean Zhu. I’m Mike!
然而正所謂能力越大,消耗越大,雙引號擁有複雜的功能也意味着編譯器處理它也要花費更多的時間,因此如果我們僅僅想輸出簡單的字符串的話,最好還是使用單引號。
4. 對浮點數進行比較
作爲一個.NET程序員,如果看到下面的代碼,你知道它的結果麼?
<?php
$a=0.1+0.2;
$b=0.3;
if($a==$b){
echo 'a equals b';
}
else{
echo 'a dosen\'t equal b';
}
?>
在.NET中我們可以毫無猶豫的知道它輸出的是a=b的答案,然而這裏是PHP的世界,所以永遠不要相信浮點數結果精確到了最後一位,也永遠不要比較兩個浮點數是否相等。
5. 只要method_exists()返回結果爲true我們就能調用該它麼?
PHP不像C#, 由於C#是一門靜態語言,所以我們可以放心大膽的調用任何方法(只要編譯器說沒問題),然而在php中只要你沒執行到最終的方法那一步,一切都還是未知數。因此我們有時候在調用方法之前需要做些額外的檢查工作,method_exist()就是其中之一,然而如果方法是protected或者private,該方法同樣也會返回true。
6. 使用未初始化的數組
看看下面的代碼有什麼問題?
<?php
foreach($items as $item) {
$itemIds[] = $item->getId();
}
?>
.NET程序員是永遠不會犯上面的錯誤的,因爲編譯器會幫我們做檢查,然而作爲一個PHP程序員,我們始終都要記得給數組初始化:$itemIds = Array(); 可能你會說,上面的代碼不初始化也不會有什麼問題啊。是的,在正常情況下,只要$items數組裏面的元素大於1的時候,這段代碼確實沒什麼問題,然而,如果$items對象爲空或者裏面元素數量爲0,這段代碼就有問題了。
7. include(“pages/” . $_GET["pg1"]);
.NET程序員雖然也會遇到各種各樣的注入攻擊,但至少這個攻擊他們不會遇到。實際上,不僅僅是$_GET()參數我們應該檢查,其他的如$_POST和$_REQUEST同樣具有非常高的危險性。如果始終對用戶信任的話,你可能會在不知不覺中加載了別人的代碼卻渾然不知。
8. 給數組索引添加引號
echo $array[my_key]; //一般能正常工作,但不應該使用
echo $array['my_key']; //正確的方式
echo $array[my_key]; //一般能正常工作,但不應該使用
echo $array['my_key']; //正確的方式
PHP編譯器會認爲沒有引號的字符串是一個已經定義的常量,然後它會去符號表中查找這個常量,只有找不到的時候,它纔會把這個鍵值轉換爲真正的字符串,這就是上面代碼爲什麼也能工作的原因
9. strpos / stripos 函數使用不當。
這個函數在.NET中也有類似的:string.IndexOf(),但在.NET中我們一般不會犯這個初級錯誤,我們來看看下面的代碼有什麼問題吧。
if (strpos(‘Spring Brother is cool, isn’t it?’, ‘Spring Brother’)) {
echo “Spring Brother is cool”;
} else {
echo “Spring Brother is NOT cool”;
}
猜猜輸出結果是什麼?對的,它會輸出"Spring Brother is NOT cool”, 因爲就像.NET中一樣,由於索引從0開始,strpos返回的是0,0會轉換爲false。而在.net中,如果程序員不進行強制轉換,這種錯誤很容易顯現出來。
解決這種初級錯誤的方式也很簡單,常用方式有以下兩種:
if (strpos(‘Spring Brother is cool, isn’t it?’, ‘Spring Brother) !== false) {
//或者
if (strpos(‘Spring Brother is cool, isn’t it?’, ‘Spring Brother’)>= 0 ) {
10. 使用==比較類型。
這種錯誤其實跟js中的比較一樣,由於PHP和js都是弱類型的語言,因此有時我們比較兩個對象時可能遇到意想不到的事情。看下面的代碼:
$a = 1;
$b = ‘1′;
$c = 1;
$a == $b // true
$a === $b // false
$a === $c // true
我們可以看到,即使$a和$b本來不是同一個對象,但這裏==比較返回的結果卻是true,因此如果想要對對象做更嚴格的類型比較時最好用===和!==比較符。
11. empty()方法和魔術方法__get一起使用。
魔術方法很好用,不過當它和empty方法一起使用的時候我們需要格外小心,先上代碼:
class Example {
public function __get($name) {
return 'this should not show up as empty';
}
}
function t7(){
$e = new Example();
if(empty($e->something))
echo 'something條件爲真';
$var = $e->something;
if(empty($var))
echo '$var條件爲真';
}
t7();
我們會發現第一個條件爲真,然而同樣的值在第二個條件中卻爲假了。
12. 未對Session中的敏感數據進行任何保護
一個常見的PHP安全錯誤就是未對存放在Session中的敏感數據進行任何類型的加密處理。在某種程度上這就相當於把用戶的密碼等信息裸露在網絡當中,某些黑客可以通過Sesion僞造等技術來竊取用戶的密碼信息。