錯誤的 PHP 代碼:PHP 開發人員最常犯的 10 個錯誤
已發表: 2022-03-11PHP 使得構建基於 Web 的系統變得相對容易,這也是它受歡迎的主要原因。 但是,儘管 PHP 易於使用,但它已經發展成為一種相當複雜的語言,它具有許多框架、細微差別和微妙之處,可能會咬住開發人員,導致數小時的頭髮拉扯調試。 本文重點介紹 PHP 開發人員需要注意的十個常見錯誤。
常見錯誤 #1:在foreach
循環之後留下懸空數組引用
不確定如何在 PHP 中使用 foreach 循環? 如果要對正在迭代的數組中的每個元素進行操作,在foreach
循環中使用引用會很有用。 例如:
$arr = array(1, 2, 3, 4); foreach ($arr as &$value) { $value = $value * 2; } // $arr is now array(2, 4, 6, 8)
問題是,如果你不小心,這也會產生一些不良的副作用和後果。 具體來說,在上面的示例中,代碼執行後, $value
將保留在作用域內,並將持有對數組中最後一個元素的引用。 因此,涉及$value
的後續操作可能會無意中最終修改數組中的最後一個元素。
要記住的主要事情是foreach
不會創建範圍。 因此,上面示例中的$value
是腳本頂級範圍內的引用。 在每次迭代中foreach
將引用設置為指向$array
的下一個元素。 因此,在循環完成後, $value
仍然指向$array
的最後一個元素並保持在範圍內。
這是可能導致的那種逃避和令人困惑的錯誤的示例:
$array = [1, 2, 3]; echo implode(',', $array), "\n"; foreach ($array as &$value) {} // by reference echo implode(',', $array), "\n"; foreach ($array as $value) {} // by value (ie, copy) echo implode(',', $array), "\n";
上面的代碼將輸出以下內容:
1,2,3 1,2,3 1,2,2
不,這不是一個錯字。 最後一行的最後一個值確實是 2,而不是 3。
為什麼?
在經過第一個foreach
循環後, $array
保持不變,但如上所述, $value
保留為對$array
中最後一個元素的懸空引用(因為foreach
循環通過引用訪問$value
)。
結果,當我們執行第二個foreach
循環時,“奇怪的事情”似乎發生了。 具體來說,由於$value
現在是按值訪問(即通過copy ),因此foreach
在循環的每個步驟中將每個連續的$array
元素複製到$value
中。 因此,以下是在第二個foreach
循環的每個步驟中發生的情況:
- 傳遞 1:將
$array[0]
(即“1”)複製到$value
(這是對$array[2]
的引用),所以$array[2]
現在等於 1。所以$array
現在包含 [1, 2, 1]。 - 傳遞 2:將
$array[1]
(即“2”)複製到$value
(這是對$array[2]
的引用),所以$array[2]
現在等於 2。所以$array
現在包含 [1, 2, 2]。 - 第 3 步:將
$array[2]
(現在等於“2”)複製到$value
(它是對$array[2]
的引用),所以$array[2]
仍然等於 2。所以$array
現在包含 [1 , 2, 2]。
為了在foreach
循環中仍然獲得使用引用的好處而不冒此類問題的風險,請在foreach
循環之後立即在變量上調用unset()
以刪除引用; 例如:
$arr = array(1, 2, 3, 4); foreach ($arr as &$value) { $value = $value * 2; } unset($value); // $value no longer references $arr[3]
常見錯誤 #2:誤解isset()
行為
儘管有它的名字, isset()
不僅在項目不存在時返回 false,而且對於null
值也返回false
。
這種行為比最初可能出現的問題更多,並且是問題的常見來源。
考慮以下:
$data = fetchRecordFromStorage($storage, $identifier); if (!isset($data['keyShouldBeSet']) { // do something here if 'keyShouldBeSet' is not set }
這段代碼的作者大概想檢查keyShouldBeSet
是否設置在$data
中。 但是,正如所討論的,如果$data['keyShouldBeSet']
已設置, isset($data['keyShouldBeSet'])
也將返回 false ,但設置為null
。 所以上面的邏輯是有缺陷的。
這是另一個例子:
if ($_POST['active']) { $postData = extractSomething($_POST); } // ... if (!isset($postData)) { echo 'post not active'; }
上面的代碼假設如果$_POST['active']
返回true
,那麼postData
必然會被設置,因此isset($postData)
將返回true
。 所以相反,上面的代碼假設isset($postData)
返回false
的唯一方法是$_POST['active']
也返回false
。
不是。
如前所述,如果$postData
設置為null
, isset($postData)
也將返回false
。 因此,即使$_POST['active']
返回true
, isset($postData)
也可能返回false
。 再說一遍,上面的邏輯是有缺陷的。
順便說一句,如果上面代碼中的意圖真的是再次檢查$_POST['active']
是否返回 true,那麼無論如何依賴isset()
都是一個糟糕的編碼決定。 相反,最好重新檢查$_POST['active']
; IE:
if ($_POST['active']) { $postData = extractSomething($_POST); } // ... if ($_POST['active']) { echo 'post not active'; }
但是,對於檢查變量是否真的設置很重要的情況(即,區分未設置的變量和設置為null
的變量), array_key_exists()
方法更健壯解決方案。
例如,我們可以將上述兩個示例中的第一個重寫如下:
$data = fetchRecordFromStorage($storage, $identifier); if (! array_key_exists('keyShouldBeSet', $data)) { // do this if 'keyShouldBeSet' isn't set }
此外,通過結合array_key_exists()
和get_defined_vars()
,我們可以可靠地檢查當前範圍內的變量是否已設置:
if (array_key_exists('varShouldBeSet', get_defined_vars())) { // variable $varShouldBeSet exists in current scope }
常見錯誤 #3:對按引用返回與按值返回的混淆
考慮這個代碼片段:
class Config { private $values = []; public function getValues() { return $this->values; } } $config = new Config(); $config->getValues()['test'] = 'test'; echo $config->getValues()['test'];
如果你運行上面的代碼,你會得到以下信息:
PHP Notice: Undefined index: test in /path/to/my/script.php on line 21
怎麼了?
問題是上面的代碼混淆了按引用返回數組和按值返回數組。 除非您明確告訴 PHP 通過引用返回一個數組(即使用&
),否則 PHP 默認將“按值”返回該數組。 這意味著將返回數組的副本,因此被調用函數和調用者將不會訪問數組的同一實例。
所以上面對getValues()
的調用返回了$values
數組的副本,而不是對它的引用。 考慮到這一點,讓我們重新審視上面示例中的兩個關鍵行:
// getValues() returns a COPY of the $values array, so this adds a 'test' element // to a COPY of the $values array, but not to the $values array itself. $config->getValues()['test'] = 'test'; // getValues() again returns ANOTHER COPY of the $values array, and THIS copy doesn't // contain a 'test' element (which is why we get the "undefined index" message). echo $config->getValues()['test'];
一種可能的解決方法是保存getValues()
返回的$values
數組的第一個副本,然後隨後對該副本進行操作; 例如:
$vals = $config->getValues(); $vals['test'] = 'test'; echo $vals['test'];
該代碼可以正常工作(即,它將輸出test
而不生成任何“未定義索引”消息),但取決於您要完成的工作,這種方法可能合適,也可能不合適。 特別是,上面的代碼不會修改原來的$values
數組。 因此,如果您確實希望您的修改(例如添加“測試”元素)影響原始數組,則需要修改getValues()
函數以返回對$values
數組本身的引用。 這是通過在函數名前添加&
來完成的,從而表明它應該返回一個引用; IE:
class Config { private $values = []; // return a REFERENCE to the actual $values array public function &getValues() { return $this->values; } } $config = new Config(); $config->getValues()['test'] = 'test'; echo $config->getValues()['test'];
正如預期的那樣,它的輸出將是test
。
但是為了讓事情變得更加混亂,請考慮以下代碼片段:
class Config { private $values; // using ArrayObject rather than array public function __construct() { $this->values = new ArrayObject(); } public function getValues() { return $this->values; } } $config = new Config(); $config->getValues()['test'] = 'test'; echo $config->getValues()['test'];
如果您猜測這會導致與我們之前的array
示例相同的“未定義索引”錯誤,那您就錯了。 事實上,這段代碼可以正常工作。 原因是,與數組不同, PHP 總是通過引用傳遞對象。 ( ArrayObject
是一個 SPL 對象,它完全模仿數組的使用,但作為一個對象工作。)
正如這些示例所示,在 PHP 中處理副本還是引用並不總是很明顯。 因此,必須了解這些默認行為(即,變量和數組通過值傳遞;對象通過引用傳遞)並仔細檢查您正在調用的函數的 API 文檔,看看它是否返回一個值,a數組的副本、對數組的引用或對對象的引用。
綜上所述,重要的是要注意返回對數組或ArrayObject
的引用的做法通常是應該避免的,因為它為調用者提供了修改實例私有數據的能力。 這與封裝“正面交鋒”。 相反,最好使用舊式的“getters”和“setters”,例如:
class Config { private $values = []; public function setValue($key, $value) { $this->values[$key] = $value; } public function getValue($key) { return $this->values[$key]; } } $config = new Config(); $config->setValue('testKey', 'testValue'); echo $config->getValue('testKey'); // echos 'testValue'
這種方法使調用者能夠設置或獲取數組中的任何值,而無需提供對其他私有$values
數組本身的公共訪問。
常見錯誤 #4:循環執行查詢
如果您的 PHP 不工作,遇到這樣的事情並不少見:
$models = []; foreach ($inputValues as $inputValue) { $models[] = $valueRepository->findByValue($inputValue); }
雖然這裡可能絕對沒有錯,但是如果您遵循代碼中的邏輯,您可能會發現上面對$valueRepository->findByValue()
的看似無辜的調用最終會導致某種查詢,例如:
$result = $connection->query("SELECT `x`,`y` FROM `values` WHERE `value`=" . $inputValue);
因此,上述循環的每次迭代都會導致對數據庫的單獨查詢。 因此,例如,如果您向循環提供了一個包含 1,000 個值的數組,它將生成 1,000 個對資源的單獨查詢! 如果在多個線程中調用這樣的腳本,它可能會使系統陷入停頓。
因此,識別代碼何時進行查詢至關重要,並儘可能收集值,然後運行一個查詢以獲取所有結果。
遇到查詢效率低下(即,在循環中)的一個相當常見的例子是當一個表單被發佈時帶有一個值列表(例如,ID)。 然後,為了檢索每個 ID 的完整記錄數據,代碼將遍歷數組並對每個 ID 執行單獨的 SQL 查詢。 這通常看起來像這樣:
$data = []; foreach ($ids as $id) { $result = $connection->query("SELECT `x`, `y` FROM `values` WHERE `id` = " . $id); $data[] = $result->fetch_row(); }
但同樣的事情可以在單個SQL 查詢中更有效地完成,如下所示:
$data = []; if (count($ids)) { $result = $connection->query("SELECT `x`, `y` FROM `values` WHERE `id` IN (" . implode(',', $ids)); while ($row = $result->fetch_row()) { $data[] = $row; } }
因此,識別代碼何時直接或間接地進行查詢至關重要。 只要有可能,就收集這些值,然後運行一個查詢來獲取所有結果。 然而,也必須謹慎行事,這導致我們的下一個常見的 PHP 錯誤......
常見錯誤#5:內存使用假象和低效
雖然一次獲取多條記錄肯定比對要獲取的每一行運行單個查詢更有效,但是當使用 PHP 的mysql
擴展時,這種方法可能會導致libmysqlclient
中出現“內存不足”的情況。
為了演示,讓我們看一個資源有限(512MB RAM)、MySQL 和php-cli
的測試框。
我們將像這樣引導一個數據庫表:
// connect to mysql $connection = new mysqli('localhost', 'username', 'password', 'database'); // create table of 400 columns $query = 'CREATE TABLE `test`(`id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT'; for ($col = 0; $col < 400; $col++) { $query .= ", `col$col` CHAR(10) NOT NULL"; } $query .= ');'; $connection->query($query); // write 2 million rows for ($row = 0; $row < 2000000; $row++) { $query = "INSERT INTO `test` VALUES ($row"; for ($col = 0; $col < 400; $col++) { $query .= ', ' . mt_rand(1000000000, 9999999999); } $query .= ')'; $connection->query($query); }
好的,現在讓我們檢查資源使用情況:

// connect to mysql $connection = new mysqli('localhost', 'username', 'password', 'database'); echo "Before: " . memory_get_peak_usage() . "\n"; $res = $connection->query('SELECT `x`,`y` FROM `test` LIMIT 1'); echo "Limit 1: " . memory_get_peak_usage() . "\n"; $res = $connection->query('SELECT `x`,`y` FROM `test` LIMIT 10000'); echo "Limit 10000: " . memory_get_peak_usage() . "\n";
輸出:
Before: 224704 Limit 1: 224704 Limit 10000: 224704
涼爽的。 看起來查詢在資源方面是在內部安全地管理的。
不過,可以肯定的是,讓我們再提高一次限制並將其設置為 100,000。 哦哦。 當我們這樣做時,我們得到:
PHP Warning: mysqli::query(): (HY000/2013): Lost connection to MySQL server during query in /root/test.php on line 11
發生了什麼?
這裡的問題是 PHP 的mysql
模塊的工作方式。 它實際上只是libmysqlclient
的代理,它完成了骯髒的工作。 當選擇一部分數據時,它直接進入內存。 由於此內存不是由 PHP 的管理器管理的,因此當我們超出查詢限制時, memory_get_peak_usage()
不會顯示資源利用率的任何增加。 這會導致像上面展示的那樣的問題,我們被欺騙自滿,認為我們的內存管理很好。 但實際上,我們的內存管理存在嚴重缺陷,我們可能會遇到如上所示的問題。
您至少可以通過使用mysqlnd
模塊來避免上述假冒(儘管它本身不會提高您的內存利用率)。 mysqlnd
被編譯為原生 PHP 擴展,它使用 PHP 的內存管理器。
因此,如果我們使用mysqlnd
而不是mysql
運行上述測試,我們會得到更真實的內存利用率圖:
Before: 232048 Limit 1: 324952 Limit 10000: 32572912
順便說一句,它甚至比這更糟糕。 根據 PHP 文檔, mysql
使用兩倍於mysqlnd
的資源來存儲數據,因此使用mysql
的原始腳本確實使用了比這裡顯示的更多的內存(大約是兩倍)。
為避免此類問題,請考慮限制查詢的大小並使用迭代次數較少的循環; 例如:
$totalNumberToFetch = 10000; $portionSize = 100; for ($i = 0; $i <= ceil($totalNumberToFetch / $portionSize); $i++) { $limitFrom = $portionSize * $i; $res = $connection->query( "SELECT `x`,`y` FROM `test` LIMIT $limitFrom, $portionSize"); }
當我們考慮這個 PHP 錯誤和上面的錯誤 #4 時,我們意識到您的代碼理想地需要在一方面讓您的查詢過於細化和重複,與讓您的每一個個別查詢太大。 就像生活中的大多數事情一樣,需要平衡。 任何一個極端都不好,並且可能導致 PHP 無法正常工作。
常見錯誤 #6:忽略 Unicode/UTF-8 問題
從某種意義上說,這實際上是 PHP 本身的一個問題,而不是您在調試 PHP 時遇到的問題,但它從未得到充分解決。 PHP 6 的核心是要支持 Unicode,但是當 PHP 6 的開發在 2010 年暫停時,這被擱置了。
但這決不能免除開發人員正確處理 UTF-8 並避免錯誤假設所有字符串都必然是“普通的舊 ASCII”。 無法正確處理非 ASCII 字符串的代碼因將粗糙的 heisenbugs 引入您的代碼而臭名昭著。 如果像“薛定諤”這樣姓氏的人試圖註冊到您的系統,即使是簡單的strlen($_POST['name'])
調用也可能導致問題。
這是一個小清單,可以避免代碼中出現此類問題:
- 如果您對 Unicode 和 UTF-8 了解不多,那麼您至少應該學習基礎知識。 這裡有一本很棒的入門書。
- 確保始終使用
mb_*
函數而不是舊的字符串函數(確保“多字節”擴展名包含在您的 PHP 構建中)。 - 確保您的數據庫和表設置為使用 Unicode(許多 MySQL 版本默認仍使用
latin1
)。 - 請記住,
json_encode()
轉換非 ASCII 符號(例如,“Schrodinger”變為“Schr\u00f6dinger”)但serialize()
不會。 - 確保您的 PHP 代碼文件也是 UTF-8 編碼的,以避免在將字符串與硬編碼或配置的字符串常量連接時發生衝突。
在這方面,一個特別有價值的資源是 Francisco Claria 在此博客上發表的 PHP 和 MySQL 的 UTF-8 Primer。
常見錯誤 #7:假設$_POST
將始終包含您的 POST 數據
儘管有它的名字, $_POST
POST 數組並不總是包含您的 POST 數據,而且很容易發現它是空的。 為了理解這一點,讓我們看一個例子。 假設我們使用jQuery.ajax()
調用發出服務器請求,如下所示:
// js $.ajax({ url: 'http://my.site/some/path', method: 'post', data: JSON.stringify({a: 'a', b: 'b'}), contentType: 'application/json' });
(順便提一下,注意這裡的contentType: 'application/json'
。我們以 JSON 格式發送數據,這在 API 中非常流行。這是默認設置,例如,在 AngularJS $http
服務中發布。)
在我們示例的服務器端,我們簡單地轉儲$_POST
數組:
// php var_dump($_POST);
令人驚訝的是,結果將是:
array(0) { }
為什麼? 我們的 JSON 字符串{a: 'a', b: 'b'}
發生了什麼?
答案是PHP 僅在其內容類型為application/x-www-form-urlencoded
或multipart/form-data
時才會自動解析 POST 有效負載。 其原因是歷史性的——這兩種內容類型基本上是幾年前 PHP 的$_POST
實現時唯一使用的內容類型。 因此,對於任何其他內容類型(即使是當今非常流行的內容類型,例如application/json
),PHP 都不會自動加載 POST 有效負載。
由於$_POST
是一個超全局變量,如果我們覆蓋它一次(最好是在腳本的早期),那麼修改後的值(即,包括 POST 有效負載)將在我們的代碼中被引用。 這很重要,因為$_POST
通常被 PHP 框架和幾乎所有自定義腳本用於提取和轉換請求數據。
因此,例如,在處理內容類型為application/json
的 POST 負載時,我們需要手動解析請求內容(即解碼 JSON 數據)並覆蓋$_POST
變量,如下所示:
// php $_POST = json_decode(file_get_contents('php://input'), true);
然後當我們轉儲$_POST
數組時,我們看到它正確地包含了 POST 有效負載; 例如:
array(2) { ["a"]=> string(1) "a" ["b"]=> string(1) "b" }
常見錯誤 #8:認為 PHP 支持字符數據類型
查看這段示例代碼並嘗試猜測它將打印什麼:
for ($c = 'a'; $c <= 'z'; $c++) { echo $c . "\n"; }
如果您回答“a”到“z”,您可能會驚訝地發現自己錯了。
是的,它會打印 'a' 到 'z',但它也會打印 'aa' 到 'yz'。 讓我們看看為什麼。
在 PHP 中沒有char
數據類型; 只有string
可用。 考慮到這一點,在 PHP 中增加string
z
會產生aa
:
php> $c = 'z'; echo ++$c . "\n"; aa
然而,為了進一步混淆問題, aa
在字典上小於z
:
php> var_export((boolean)('aa' < 'z')) . "\n"; true
這就是為什麼上面給出的示例代碼會打印字母a
到z
,但也會打印aa
到yz
。 它在到達za
時停止,這是它遇到的第一個“大於” z
的值:
php> var_export((boolean)('za' < 'z')) . "\n"; false
在這種情況下,這是正確循環 PHP 中值 'a' 到 'z' 的一種方法:
for ($i = ord('a'); $i <= ord('z'); $i++) { echo chr($i) . "\n"; }
或者:
$letters = range('a', 'z'); for ($i = 0; $i < count($letters); $i++) { echo $letters[$i] . "\n"; }
常見錯誤 #9:忽略編碼標準
儘管忽略編碼標準不會直接導致需要調試 PHP 代碼,但它仍然可能是這裡要討論的最重要的事情之一。
忽略編碼標準可能會導致項目出現大量問題。 充其量,它會導致代碼不一致(因為每個開發人員都在“做自己的事情”)。 但在最壞的情況下,它生成的 PHP 代碼無法運行或難以導航(有時幾乎是不可能的),這使得調試、增強和維護變得極其困難。 這意味著您的團隊的生產力會降低,包括大量浪費(或至少是不必要的)努力。
對於 PHP 開發人員來說幸運的是,有 PHP 標準推薦 (PSR),它由以下五個標準組成:
- PSR-0:自動加載標準
- PSR-1:基本編碼標準
- PSR-2:編碼風格指南
- PSR-3:記錄器接口
- PSR-4:自動裝載機
PSR 最初是根據市場上最知名平台的維護者的意見創建的。 Zend、Drupal、Symfony、Joomla 和其他人為這些標準做出了貢獻,現在正在遵循它們。 即使是在此之前多年試圖成為標準的 PEAR,現在也參與了 PSR。
從某種意義上說,你的編碼標準是什麼幾乎無關緊要,只要你同意一個標準並堅持下去,但是遵循 PSR 通常是一個好主意,除非你的項目有一些令人信服的理由不這樣做. 越來越多的團隊和項目符合 PSR。 在這一點上,Tt 絕對被大多數 PHP 開發人員認可為“標準”,因此使用它有助於確保新開發人員在加入您的團隊時熟悉並熟悉您的編碼標準。
常見錯誤 #10:濫用empty()
一些 PHP 開發人員喜歡使用empty()
對幾乎所有內容進行布爾檢查。 但是,在某些情況下,這可能會導致混淆。
首先,讓我們回到數組和ArrayObject
實例(模仿數組)。 鑑於它們的相似性,很容易假設數組和ArrayObject
實例的行為相同。 然而,這被證明是一個危險的假設。 例如,在 PHP 5.0 中:
// PHP 5.0 or later: $array = []; var_dump(empty($array)); // outputs bool(true) $array = new ArrayObject(); var_dump(empty($array)); // outputs bool(false) // why don't these both produce the same output?
更糟糕的是,在 PHP 5.0 之前,結果會有所不同:
// Prior to PHP 5.0: $array = []; var_dump(empty($array)); // outputs bool(false) $array = new ArrayObject(); var_dump(empty($array)); // outputs bool(false)
不幸的是,這種方法很受歡迎。 例如,這是 Zend Framework 2 的Zend\Db\TableGateway
在對TableGateway::select()
結果調用current()
時返回數據的方式,正如文檔所建議的那樣。 使用此類數據,開發人員很容易成為此錯誤的受害者。
為了避免這些問題,檢查空數組結構的更好方法是使用count()
:
// Note that this work in ALL versions of PHP (both pre and post 5.0): $array = []; var_dump(count($array)); // outputs int(0) $array = new ArrayObject(); var_dump(count($array)); // outputs int(0)
順便說一句,由於 PHP 將0
轉換為false
, count()
也可以在if ()
條件中用於檢查空數組。 還值得注意的是,在 PHP 中, count()
是數組上的常數複雜度( O(1)
操作),這使得它更清楚地表明它是正確的選擇。
另一個empty()
可能很危險的例子是將它與魔術類函數__get()
結合使用。 讓我們定義兩個類並在兩者中都有一個test
屬性。
首先讓我們定義一個包含test
作為普通屬性的Regular
類:
class Regular { public $test = 'value'; }
然後讓我們定義一個Magic
類,它使用魔術__get()
運算符來訪問它的test
屬性:
class Magic { private $values = ['test' => 'value']; public function __get($key) { if (isset($this->values[$key])) { return $this->values[$key]; } } }
好的,現在讓我們看看當我們嘗試訪問每個類的test
屬性時會發生什麼:
$regular = new Regular(); var_dump($regular->test); // outputs string(4) "value" $magic = new Magic(); var_dump($magic->test); // outputs string(4) "value"
到目前為止還好。
但是現在讓我們看看當我們在每個上面調用empty()
時會發生什麼:
var_dump(empty($regular->test)); // outputs bool(false) var_dump(empty($magic->test)); // outputs bool(true)
啊。 因此,如果我們依賴empty()
,我們可能會誤以為$magic
的test
屬性是空的,而實際上它被設置為'value'
。
不幸的是,如果一個類使用魔術__get()
函數來檢索屬性的值,則沒有萬無一失的方法來檢查該屬性值是否為空。 在類的範圍之外,您實際上只能檢查是否會返回null
值,這並不一定意味著未設置相應的鍵,因為它實際上可能已設置為null
。
相反,如果我們嘗試引用Regular
類實例的不存在的屬性,我們將收到類似於以下內容的通知:
Notice: Undefined property: Regular::$nonExistantTest in /path/to/test.php on line 10 Call Stack: 0.0012 234704 1. {main}() /path/to/test.php:0
所以這裡的要點是應該小心使用empty()
方法,因為如果不小心,它可能會導致混淆——甚至可能誤導——結果。
包起來
PHP 的易用性會使開發人員產生一種錯誤的舒適感,由於語言的一些細微差別和特性,他們很容易受到冗長的 PHP 調試的影響。 這可能會導致 PHP 無法正常工作以及出現此處描述的問題。
PHP 語言在其 20 年的歷史中發生了顯著的演變。 熟悉其微妙之處是一項值得努力的工作,因為它將有助於確保您生產的軟件更具可擴展性、健壯性和可維護性。