首頁  >  問答  >  主體

遷移到 PHP 8.1 - 如何修復已棄用的將 null 傳遞給參數錯誤 - 重命名函數中的構建

PHP 8.1 已棄用將 null 作為參數傳遞給許多核心函數。我的主要問題是htmlspecialchars(php)trim(php) 等函數,其中null 不再默默轉換為空字符串。

為了在不使用大量程式碼的情況下解決此問題,我嘗試重命名原始內建函數,並將它們替換為將輸入從 null 轉換為(空)字串的包裝器。

這種方法的主要問題是,函數 rename_function(PECL apd) 不再起作用,上次更新是在 2004 年1

我需要對內建函數進行某種重寫,以避免每次呼叫函數時都編寫空檢查,從而使我的所有程式碼變大兩倍。

我能想到的唯一其他解決方案是僅使用我的自訂函數,但這仍然需要遍歷我擁有的所有程式碼和第三方程式庫。

在 PHP 8.1 中,當將 null 傳遞給內建函數時,它不再默默地轉換為空字串。


  1. https://pecl.php.net/package/apd

#
P粉420868294P粉420868294257 天前427

全部回覆(2)我來回復

  • P粉811329034

    P粉8113290342024-01-11 15:36:30

    我想(作為補充,現有的答案得到了我的支持)就如何看待和解決此類「問題」描繪了一幅不同的圖畫。它並沒有減少所概述的方法的正確性或錯誤性,而只是一種希望互惠互利的附加觀點。每個項目都是不同的。

    給定前提:

    那麼這對我來說(首先)看起來是一個報告問題。透過不報告 E_DEPRECATED

    這樣做的好處是(不僅是您的程式碼),現在知道您的程式碼帶有棄用通知。報告確實有效

    另一方面,壓制棄用通知可能會讓它們消失。如果您丟失了程式碼庫帶有棄用通知的訊息,從技術上講,從資訊遺失中恢復可能仍然很容易(再次報告棄用通知),但是如果更改的時間延長了,現在可能會出現壓倒性的雜訊(E_TOO_MUCH_NOISE)。

    那麼程式碼不沉默其實是一件壞事嗎?或者說可以轉化為利益嗎?我寧願選擇後者。不管怎樣,我們已經在處理這些資訊了。

    因此,在這種情況中,我的想法是一般不抑制棄用通知,而是「靜默」函數呼叫。這很容易,但無論從好的方面還是從壞的方面來說,這都是愚蠢的:

    trim($mixed);   #1  ->     @trim($mixed);   #2

    這當然是一個可以使用標準文字工具應用於程式碼庫的操作。它還會向您顯示過去已經使用過 @ 抑制運算子的位置:

    @trim($mixed);  #3  ->     @@trim($mixed);  #4

    如果您是PHP 開發人員,在編輯器中查看此類程式碼(對於情況#2-#4),他們會立即向您尖叫,並且對於所有四種情況至少都會吸引您的注意($mixed)。

    感謝您沒有保持沉默,我們讓這些地方尖叫,只是不是在運行時1

    與第一種透過不報告 E_DEPRECATED 來保持沉默的方法不同,這種方法很容易丟失訊息,而訊息是透過使用所有 @ 符號來保存的。

    它對解決噪音問題有幫助嗎?如果我們停止在這裡工作,那就完全不行了。現在我們會在程式碼上塗上@-符號,決定不採取進一步的操作,這樣我們就可以使用第一個解決方案(不報告棄用訊息)來完成它,而無需觸及程式碼。

    那麼它的好處是什麼?好吧,儘管程式碼現在靜默運行,PHP 仍然提供診斷訊息。也就是說,現在可以將 PHP 錯誤處理程序註冊為偵聽器(執行程式碼時)。

    僅在程式碼級別,很容易檢查這些位置,因為 @ 符號(通常)也很容易在程式碼中發現。

    第二部分很重要,因為儘管多個地方可能會受到棄用的影響,但一定不能有一個解決方案來解決所有問題(我更喜歡遠離“一刀切” '解決方案』」(如果可能的話),但特別是在問題上下文中PHP 8.1 發生了變化,我可以想像根據使用地點會有不同的需求。

    例如,在模板程式碼(輸出)中,具體類型不是一個問題,因此轉換為字串很可能是首選解決方案:

    @trim($mixed);     ->     trim((string)$mixed)
    @@trim($mixed);    ->     @trim((string)$mixed)

    模板(輸出)保持穩定。

    但對於實際的輸入處理,棄用通知可能會發現值得修復的實際潛在缺陷,例如缺少預設值(使事情變得過於複雜)、值的處理不明確(空與空、字串、布林與數字)與PHP 中的陣列與物件)或一般的$mixed 混淆。

    這樣的 trim($mixed) 可能是被遺忘多年的安全防護,從未進行過升級(有更好的安全防護可用)。對於這樣的程式碼,我很確定我已經想要並要求$mixed 實際上是$string before 我使用trim ()。原因很簡單,至少直接想到兩件事:

    • a) 不再需要 trim() - 它可以被刪除(我最喜歡的修復之一:刪除程式碼!) - 或 -
    • b)它正在進行字串工作,那麼我有一個問題,因為我不希望有任何非字串的東西存在。問題在於,它通常不適用於霰彈槍方法(Gießkanne 有人嗎?)。

    使用 $mixed 進行修補是完全有效的? ''如果原始使用是字串或null

    @trim($mixed);     ->     trim($mixed ?? '')
    @@trim($mixed);    ->     @trim($mixed ?? '')

    但除此之外,例如像 42 這樣的數字,將拋出 TypeError,而不是棄用訊息。這可以區分正在運行的程式碼和未運行的程式碼。

    因此,這裡還有更多需要維護的地方,例如檢查位置,如果可能的話進一步聚類,然後應用更多專用修復程序。它可能會揭示缺少的測試或斷言,需要一些時間來穩定整個應用程式流程等。

    在這種情況下,要完成程式碼的遷移,進行集群,處理空合併運算符,並為真正的修復做適當的文書工作。一旦完成了使用空合併運算符的非明顯錯誤抑制並刪除了 @ 抑制運算符,如果修復計劃未捕獲這些信息,您可能會丟失這些信息。

    當我在這些地方看起來受過更多教育時,當我發現自己撓頭或揉眼睛時,我並不感到驚訝。然後我提醒自己,這些錯誤不是因為 PHP 8.1 版本造成的,版本更改只是讓它們(再次)出現,有時我什至會通過維護 PHP 版本來獲得完整的錯誤集群作為副漁獲物。

    備忘單

    • #(string)$mixed - 之前的行為
    • $mixed ?? '' - 僅在 null 上抑制 TypeError 錯誤
    • @ - 完全錯誤抑制。您應該在適用的地方記錄您的程式碼庫。
    • @@ - 如果發生這種情況,這可能是一個值得研究的有趣地方。
    • 空($mixed)? '' : xxx($mixed) - 把垃圾帶出去,典型的空癱/混合混亂,尋找集群,有機會大大簡化程式碼庫。遷移到標量類型(PHP 7),從最內向外引入嚴格的類型處理,在適用的情況下使用 PHP「經典」和「嚴格」類型處理。 PHP 7.0 斷言和 PHP 8.1 棄用訊息可以很好地支援這裡。

    錯誤處理程序

    #

    錯誤處理沒有什麼魔力,它是PHP.net 上記錄的標準(與Example #1),它作為錯誤事件的觀察者,可以區分受抑制的錯誤和非受抑制的錯誤透過error_reporting(php) / error_reporting(php-ini) 至少達到通常需要的級別,如果需要區分(在生產環境中,E_DEPRECATED 通常不是報告的一部分)。此示例性處理程序會拋出所有報告的錯誤,對於棄用事件以及E_ALL 也會拋出此類錯誤,因此需要@ 抑制運算符不拋出:

    set_error_handler(static function ($type, $message, $file, $line) use (&$deprecations) {
        if (!(error_reporting() & $type)) {
            // This error code is not included in error_reporting, so let it fall
            // through to the standard PHP error handler
    
            // capture E_DEPRECATED
            if ($type === E_DEPRECATED) {
                $deprecations[] =
                    ['deprecations' => count($deprecations ?: [])]
                    + get_defined_vars();
            }
    
            return false;
        }
    
        // throwing error handler, stand-in for own error reporter
        // which may also be `return false;`
        throw new ErrorException($message, $type, error_reporting(), $file, $line);
    });
    

    類似的錯誤處理程序可以在 3v4l.org 上的擴充範例中找到,包括要報告的已棄用程式碼上。

    E_USER_DEPRECATED

    #從技術上講,錯誤抑制運算子可以與 E_USER_DEPRECATED 結合使用,與上面 E_DEPRECATED 概述的相同。

    但是,對它的控制較少它可能已被專案依賴項中已有的第三方程式碼使用。類似下面的程式碼並不罕見:

    @trigger_error('this. a message.', E_USER_DEPRECATED);
    

    它的作用完全相同:發出棄用事件,但將它們從 PHP 報告中排除。訂閱這些內容可能會讓您陷入噪音之中。使用 E_DEPRECATED,您總是可以直接從 PHP 獲得「好的、原創的」。


    1. 當考慮使用@ 錯誤抑制運算符的方法並對其進行評論時,IMSoP 立即舉起紅/黑旗(正確!),很容易將嬰兒與洗澡水一起倒掉@ 抑制運算子。在我的回答中,它的目的只是抑制棄用通知但是使用的結果是它抑制所有診斷訊息和錯誤,在某些PHP 版本中甚至是致命的訊息和錯誤,因此PHP 退出255,無需任何進一步的診斷- 不僅要小心,還要處理。這個運營商很強大。追蹤它在程式碼庫中的使用情況並不斷檢查它是否符合您的基線/期望。對於合法情況,請考慮使用消音器。為了移植/維護程式碼,首先使用它來標記。完成批量編輯後,再次將其刪除。

    回覆
    0
  • P粉592085423

    P粉5920854232024-01-11 00:24:47

    首先,要記住兩件事:

    1. PHP 8.1 棄用這些調用,它不會使它們出錯。棄用的目的是提前通知作者修復他們的程式碼,因此您和您使用的程式庫的作者可以在 PHP 9.0 發布之前修復問題。因此,不要驚慌,因為並非所有問題都能立即修復,並對庫維護人員保持耐心,他們會在自己的時間解決這個問題。
    2. 大多數情況下的快速解決方法是使用 空合併運算子來提供適當的預設值,因此您不需要在每次使用時進行長時間的空檢查。例如,htmlspecialchars($something) 可以替換為 htmlspecialchars($something ?? '')

    接下來,一些選項:

    • 根據您的案例數量,您也許可以一次手動修復幾個問題,或者添加 ?? '' 或修復一個邏輯錯誤,無論如何你都不希望出現 null。
    • 建立 nullable_htmlspecialchars 等自訂函數,並在程式碼中直接尋找和取代。
    • 建立自訂命名空間函數,例如 nullableoverride\htmlspecialchars;然後在新增 use function nullableoverride\htmlspecialchars; 的任何檔案中,將使用該函數而不是內建函數。不過,這必須添加到每個文件中,因此您可能需要一個工具來自動添加它。
    • 使用Rector自動新增?? '' 到適當的函數調用,因此您不必手動編輯它們。不幸的是,似乎還沒有這方面的內建規則,因此您必須學會編寫自己的規則。
    • 可能更簡單,根據您的技能,使用正規表示式尋找和替換來新增 ?? ''到簡單的情況。

    回覆
    0
  • 取消回覆