Mengemas kini pangkalan kod sedia ada kepada PHP 8.1: Mengendalikan parameter fungsi dalaman yang tidak boleh dibatalkan dengan nilai Null
P粉344355715
P粉344355715 2023-10-31 20:01:48
0
2
804

Saya baru mula menaik taraf kod saya agar serasi dengan php 8.1. Saya mempunyai banyak coretan kod di mana saya menghantar potensi nilai nol kepada fungsi dalaman.

if (strlen($row) > 0) {
   ...
}

di mana $row berasal daripada sumber (seperti pertanyaan) yang mungkin mempunyai nilai nol. Ini mungkin menghasilkan amaran penamatan; dalam kes ini:

DITAMAT: strlen(): Melepasi null kepada parameter #1 rentetan jenis ditamatkan ($rentetan)

Saya sedang mencari cara yang paling mudah, paling cekap masa untuk mengendalikan peningkatan kod ini, seperti menetapkan tempat carian dan penggantian global boleh dilakukan. Nampaknya menaip pembolehubah yang saya hantar ke fungsi dalaman tanpa mengubah fungsi.

error_reporting(E_ALL);
$row = null;

if (strlen((string) $row) > 0) {
   ...
}

Adakah terdapat sebarang masalah dengan pendekatan kefungsian dalaman ini, selain daripada aspek moral pengekodan cara ini? Adakah terdapat cara yang lebih baik (selain daripada menulis semula kod sepenuhnya dan mengendalikan nilai nol secara berbeza)? Saya lebih suka penyelesaian ini serasi ke belakang dengan v7.4, walaupun saya mungkin akan serasi dengan 8.0.

Saya tahu ada pilihan lain untuk fungsi yang ditentukan pengguna saya.

P粉344355715
P粉344355715

membalas semua(2)
P粉436410586

Jawab soalan anda tentang "Cara paling mudah, paling cekap masa untuk mengendalikan peningkatan kod ini."

Pendek kata, tak boleh.


Pertama, beberapa latar belakang...

Kira-kira 15% pembangun menggunakan strict_types=1 , jadi anda antara majoriti yang tidak menggunakannya.

Anda boleh mengabaikan isu ini sekarang (tidak digunakan lagi), tetapi PHP 9.0 akan menyebabkan banyak masalah dengan menjadikannya ralat jenis maut.

Yang berkata, anda masih boleh menggunakan rentetan sambungan NULL:

$name = NULL;
$a = 'Hi ' . $name;

Anda masih boleh membandingkan NULL dengan rentetan kosong:

if ('' == NULL) {
}

Dan anda masih boleh menggunakan NULL untuk pengiraan (ia masih dianggap sebagai 0):

var_dump(3 + '5' + NULL); // Fine, int(8)
var_dump(NULL / 6); // Fine, int(0)

Anda masih boleh mencetak/bergema NULL:

print(NULL);
echo NULL;

Anda masih boleh menghantar NULL ke sprintf() 中,并使用 %s untuk memaksanya ke rentetan kosong, seperti

sprintf('%s', NULL);

Anda masih boleh memaksa nilai lain (mengikut peraturan) seperti

strlen(15);
htmlspecialchars(1.2);
setcookie('c', false);

Paksaan NULL telah berfungsi dengan cara ini sejak itu, saya andaikan dari awal, dan ia juga didokumenkan:

  • Kepada Rentetan: "null sentiasa ditukar kepada rentetan kosong
  • Tukar kepada integer: "null sentiasa ditukar kepada sifar (0)."
  • float: "Untuk jenis nilai lain, penukaran dilakukan dengan menukar nilai kepada int dan kemudian kepada float"
  • Tukar kepada Boolean: "Apabila ditukar kepada Boolean, nilai berikut dianggap palsu [...] jenis khas NULL"

Apa pun, untuk membaiki... Bahagian pertama , ia akan cuba mencari kod yang perlu anda kemas kini.

Ini berlaku apabila NULL boleh dihantar ke salah satu argumen fungsi ini.

Terdapat sekurang-kurangnya 335 parameter terjejas oleh perkara ini.

Terdapat tambahan 104, ia agak mencurigakan ; dan 558 di mana NULL mempunyai masalah , di mana anda harus membetulkan isu ini, mis. define(NULL, '值')

Mazmur ​​ialah satu-satunya alat yang saya dapati yang membantu dengan ini.

Mazmur perlu berada pada tahap pemeriksaan yang sangat tinggi (1, 2 atau 3).

Dan anda tidak boleh menggunakan garis dasar untuk mengabaikan isu (teknik di mana pembangun memperkenalkan analisis statik dalam projek sedia ada supaya ia hanya menyemak kod baharu/diedit).

Jika anda tidak pernah menggunakan alat analisis statik sebelum ini (jangan risau, ia hanya disyorkan 33% daripada pembangun melakukan ini kemudian jangkakan untuk meluangkan banyak masa untuk mengubah suai kod anda (bermula pada tahap 8, yang paling longgar). , dan usahakan cara anda ).

Saya tidak dapat mencari isu ini menggunakan PHPStan, Rector, PHP CodeSniffer, PHP CS Fixer atau PHPCompatibility (Sumber).


Selepas anda menemui setiap soalan, Bahagian kedua ialah mengedit.

Tempat yang paling tidak mungkin menimbulkan masalah ialah menggantikan sinki, seperti

example_function(strval($name));
example_function((string) $name);
example_function($name ?? '');

Sebagai alternatif, anda boleh cuba menjejak kembali ke sumber pembolehubah dan cuba menghalangnya daripada ditetapkan kepada NULL di tempat pertama.

Berikut adalah beberapa sumber NULL yang sangat biasa:

$search = (isset($_GET['q']) ? $_GET['q'] : NULL);
 
$search = ($_GET['q'] ?? NULL); // Fairly common (since PHP 7)
 
$search = filter_input(INPUT_GET, 'q');
 
$search = $request->input('q'); // Laravel
$search = $request->get('q'); // Symfony
$search = $this->request->getQuery('q'); // CakePHP
$search = $request->getGet('q'); // CodeIgniter
 
$value = mysqli_fetch_row($result);
$value = json_decode($json); // Invalid JSON, or nesting limit.
$value = array_pop($empty_array);

Sesetengah fungsi ini memerlukan hujah kedua untuk menentukan nilai lalai, atau anda boleh menggunakan strval()...但要小心,您的代码可能会通过 ($a === NULL) lebih awal daripada masa, dan anda tidak mahu memecahkannya.

Ramai pembangun tidak menyedari bahawa sesetengah pembolehubah mereka boleh mengandungi NULL - cth. mengharapkan

semua medan input (yang mereka cipta) untuk sentiasa menyerahkan kerana isu rangkaian, sambungan penyemak imbas, pengguna mengedit DOM/URL dalam penyemak imbas, dsb Ini mungkin tidak berlaku.


Saya telah mengusahakan masalah ini hampir sepanjang tahun.

Saya mula menulis dua RFC untuk mencuba dan menyelesaikan masalah ini. Yang pertama ialah mengemas kini beberapa fungsi untuk menerima NULL (yang tidak sesuai kerana ia mengganggu pembangun yang menggunakan strict_types RFC kedua adalah untuk membenarkan NULL terus dikuatkuasakan dalam kes ini..... .tetapi saya tidak melakukannya); 'Jangan meletakkannya untuk undian kerana saya baru sahaja menerima satu tan maklum balas negatif dan saya tidak mahu penolakan yang disebut pada masa hadapan menjelaskan mengapa isu ini tidak dapat diselesaikan (sementara perubahan asal hampir tidak dibincangkan, yang ini) .

Nampaknya NULL dikendalikan secara berbeza kerana ia tidak pernah dianggap sebagai "nilai skalar" - Saya tidak fikir ramai pembangun mengambil berat tentang perbezaan ini, tetapi ia muncul dari semasa ke semasa.

Sebilangan besar pembangun yang pernah bekerja dengan saya mengabaikan isu ini (berharap untuk membetulkannya kemudian, yang mungkin bukan idea yang terbaik);

function ignore_null_coercion($errno, $errstr) {
  // https://github.com/php/php-src/blob/012ef7912a8a0bb7d11b2dc8d108cc859c51e8d7/Zend/zend_API.c#L458
  if ($errno === E_DEPRECATED && preg_match('/Passing null to parameter #.* of type .* is deprecated/', $errstr)) {
    return true;
  }
  return false;
}
set_error_handler('ignore_null_coercion', E_DEPRECATED);
Ada pasukan cuba

. Tetapi lebih daripada setahun kemudian mereka masih melihat masalah (mereka berkata mereka sedang menguji dengan 8.1 alfa 1). strval() 应用于所有事情,例如修剪(strval($search))

Pilihan lain yang saya sedang pertimbangkan ialah membuat perpustakaan yang mentakrifkan semula semua ~335 fungsi ini sebagai boleh dibatalkan di bawah ruang nama, cth
namespace allow_null_coercion;

function strlen(?string $string): int {
    return \strlen(\strval($string));
}

Pemaju kemudian akan memasukkan perpustakaan dan menggunakan ruang nama itu sendiri:

namespace allow_null_coercion;

$search = $request->input('q'); // Could return NULL

// ...

echo strlen($search);
P粉087074897

Jika anda cuba mengendalikan secara eksplisit null 的情况,那么稍微干净一点的修复方法是 strlen($row ?? '') gunakan "pengendali penggabungan null".

Dalam kebanyakan kes kedua-duanya mungkin setara, tetapi dengan strict_types=1 kesannya, ia berkelakuan berbeza jika nilainya adalah jenis berbeza yang boleh ditukar kepada rentetan:

declare(strict_types=1);
$row = 42;
echo strlen($row); // TypeError: must be of type string, int given
echo strlen((string) $row); // Succeeds, outputting '2'
echo strlen($row ?? ''); // TypeError: must be of type string, int given

Sebaliknya, ambil perhatian bahawa ?? 运算符基于 isset,而不是 === null dan oleh itu undefined pembolehubah berkelakuan berbeza:

declare(strict_types=1);
$row = [];
echo strlen($row['no_such_key']); // Warning: Undefined array key; TypeError: must be of type string, null given
echo strlen((string) $row['no_such_key']); // Warning: Undefined array key; outputs '0'
echo strlen($row['no_such_key'] ?? ''); // No warning, just outputs '0'

Jika anda mengambil berat tentang kes ini, persamaan yang paling langsung daripada tingkah laku lama adalah lebih bertele-tele:

echo strlen($row === null ? '' : $row);
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan