Sampai titik ini kita menulis fungsi sederhana dan PHP memperlakukannya dengan santai: bila tipe argumen tidak persis cocok, PHP diam-diam mengonversinya. Kelonggaran ini nyaman, tetapi juga berbahaya — string "100000" yang lolos ke perhitungan nominal hari ini bisa berubah jadi string "100.000" berkoma dari locale lain besok, dan bug semacam itu sulit dilacak. PHP menyediakan declare(strict_types=1) untuk mematikan konversi otomatis tersebut. Pada Chapter ini kita membandingkan mode lemah dan mode ketat pada pemanggilan yang sama, memetakan aturan konversi mode lemah, lalu membahas posisi declare yang benar dan berkas mana yang sebenarnya menentukan mode.

Perilaku Mode Lemah (Default) #

Tanpa declare apa pun, PHP berjalan pada weak mode (mode lemah). Saat argumen tidak bertipe tepat, PHP berusaha mengonversinya terlebih dahulu, baru menyerah dengan TypeError bila konversi mustahil:

php
<?php
function hitungDiskon(int $harga, float $persen): float
{
    return $harga * $persen;
}

// String numerik "100000" dipaksa menjadi int 100000.
var_dump(hitungDiskon("100000", 0.1));

// Float 6.0 dipaksa menjadi int 6 karena tidak kehilangan presisi.
var_dump(hitungDiskon(6.0, 0.5));

// Nilai balik int dipaksa menjadi string oleh deklarasi return string.
function ubahKeString(int $x): string
{
    return $x;
}
var_dump(ubahKeString("25"));

Output yang Dihasilkan:

text
float(10000)
float(3)
string(2) "25"

Mari kita bedah ketiga pemanggilan di atas:

  1. hitungDiskon("100000", 0.1): parameter pertama bertipe int tetapi menerima string "100000". Karena string tersebut numerik sempurna, PHP mengonversinya menjadi 100000 — hasil 100000 * 0.1 adalah float(10000).
  2. hitungDiskon(6.0, 0.5): float 6.0 dikonversi menjadi int 6 karena nilainya utuh — tidak ada angka pecahan yang dibuang.
  3. ubahKeString("25"): konversi terjadi dua kali — argumen "25" menjadi int 25, lalu nilai balik int 25 dipaksa menjadi string(2) "25" oleh deklarasi return : string.

Aturan Koersi pada Mode Lemah #

Konversi mode lemah tidak acak; aturannya dapat dipetakan. Tabel berikut merangkum kasus yang paling sering dijumpai:

Nilai yang datangParameter bertipeHasil
"100000", "12 ", " 7" (string numerik)int / floatdikonversi, spasi awal/akhir diabaikan
"12.5" (string pecahan)intDeprecated, nilai dipotong menjadi 12
6.0 (float utuh)intdikonversi menjadi 6
6.9 (float pecahan)intDeprecated, nilai dipotong menjadi 6
"ajax", "" (string apa pun)boolmengikuti truthiness: true / false
"murah" (string non-numerik)int / floatTypeError, program berhenti
nulltipe skalar non-nullableTypeError — null tidak pernah dipaksa

Dua baris terakhir tampak di tabel sebagai error, dan sisanya tampak damai — tetapi perhatikan bahwa dua baris Deprecated pun sebenarnya lencana kuning: konversi berjalan, namun PHP memperingatkan bahwa presisi hilang tanpa Anda perintahkan. Simpan contoh berikut sebagai koersi.php lalu jalankan:

php
<?php
function tambah(int $a, int $b): int
{
    return $a + $b;
}

// Spasi di awal/akhir string numerik tetap diterima.
var_dump(tambah("12 ", 3));
var_dump(tambah(" 7", 3));

// String pecahan yang dipotong memicu Deprecated, bukan TypeError.
var_dump(tambah("12.5", 3));

function simpan(bool $konfirmasi): string
{
    return $konfirmasi ? "disimpan" : "dibatalkan";
}

// Koersi ke bool mengikuti aturan truthiness string.
var_dump(simpan("ajax"));
var_dump(simpan(""));

Output yang Dihasilkan:

text
int(15)
int(10)

Deprecated: Implicit conversion from float-string "12.5" to int loses precision in /home/z/my-project/php-course/content/php/sintaks-dasar-dan-variabel/strict-types/.verify_tmp.php on line 2
int(15)
string(8) "disimpan"
string(10) "dibatalkan"

Empat nilai pertama menunjukkan string numerik diterima dalam berbagai bentuk: "12 " dan " 7" dibersihkan spasinya, sedangkan "12.5" dipotong menjadi 12 dengan peringatan Deprecated — perhatikan bahwa peringatan tersebut merujuk ke baris deklarasi fungsi tambah(). Dua nilai terakhir memperlihatkan koersi ke bool: string non-kosong selain "0" bernilai true, sehingga "ajax" berarti setuju dan "" berarti batal. Aturan truthiness string ini dibahas tuntas pada Chapter Booleans nanti.

Mengaktifkan Mode Ketat #

Mode ketat dinyalakan lewat declare(strict_types=1) di baris paling atas berkas. Setelah aktif, PHP berhenti mengonversi argumen dan nilai balik — tipe harus tepat:

php
<?php
declare(strict_types=1);

function hitungDiskon(int $harga, float $persen): float
{
    return $harga * $persen;
}

// Argumen dengan tipe yang tepat diterima seperti biasa.
var_dump(hitungDiskon(100000, 0.1));

// Pengecualian: int tetap boleh melebar menjadi float.
function setengah(float $x): float
{
    return $x / 2;
}
var_dump(setengah(7));

Output yang Dihasilkan:

text
float(10000)
float(3.5)

Pemanggilan pertama kini memakai argumen int asli, bukan string. Bagian kedua memperlihatkan satu-satunya kelonggaran yang tersisa: integer 7 tetap diterima parameter float karena setiap int selalu dapat diwakili float tanpa kehilangan nilai — pelebaran (widening) ini diizinkan bahkan di mode ketat. Bandingkan dengan pemanggilan string yang sama persis dengan contoh mode lemah di awal Chapter — simpan sebagai gagal.php:

php
<?php
declare(strict_types=1);

function hitungDiskon(int $harga, float $persen): float
{
    return $harga * $persen;
}

var_dump(hitungDiskon("100000", 0.1));

Output yang Dihasilkan:

text

Fatal error: Uncaught TypeError: hitungDiskon(): Argument #1 ($harga) must be of type int, string given, called in /home/z/my-project/php-course/content/php/sintaks-dasar-dan-variabel/strict-types/.verify_tmp.php on line 9 and defined in /home/z/my-project/php-course/content/php/sintaks-dasar-dan-variabel/strict-types/.verify_tmp.php:4
Stack trace:
#0 /home/z/my-project/php-course/content/php/sintaks-dasar-dan-variabel/strict-types/.verify_tmp.php(9): hitungDiskon('100000', 0.1)
#1 {main}
  thrown in /home/z/my-project/php-course/content/php/sintaks-dasar-dan-variabel/strict-types/.verify_tmp.php on line 4

Pemanggilan yang tadinya berhasil kini menghentikan program dengan TypeError: parameter $harga meminta int, yang datang string. Deklarasi nilai balik juga dijaga dengan cara yang sama — simpan berikut sebagai label.php:

php
<?php
declare(strict_types=1);

function label(float $x): int
{
    return $x;
}

var_dump(label(3.9));

Output yang Dihasilkan:

text

Fatal error: Uncaught TypeError: label(): Return value must be of type int, float returned in /home/z/my-project/php-course/content/php/sintaks-dasar-dan-variabel/strict-types/.verify_tmp.php:6
Stack trace:
#0 /home/z/my-project/php-course/content/php/sintaks-dasar-dan-variabel/strict-types/.verify_tmp.php(9): label(3.9)
#1 {main}
  thrown in /home/z/my-project/php-course/content/php/sintaks-dasar-dan-variabel/strict-types/.verify_tmp.php on line 6

Kali ini TypeError melaporkan pelanggaran pada sisi nilai balik: fungsi label() berjanji mengembalikan int tetapi return $x; menyerahkan float. Pesan error bahkan menunjuk baris pernyataan return yang berbohong. Anatomi pesan seperti ini akan kita baca secara sistematis pada Chapter Memahami Type Errors .

Posisi declare yang Benar #

declare(strict_types=1) wajib menjadi pernyataan (statement) pertama pada berkas — bukan sekadar baris pertama. Komentar boleh mendahuluinya karena komentar bukan pernyataan, tetapi kode apa pun sebelum declare langsung membatalkannya. Simpan berikut sebagai urutan.php:

php
<?php
$judul = "Laporan Penjualan";

declare(strict_types=1);

Output yang Dihasilkan:

text

Fatal error: strict_types declaration must be the very first statement in the script in /home/z/my-project/php-course/content/php/sintaks-dasar-dan-variabel/strict-types/.verify_tmp.php on line 4

PHP menolak berkas ini sejak tahap penerjemahan: variabel $judul sudah menjadi pernyataan pertama, sehingga declare kehilangan haknya. Kesalahan klasiknya adalah menyimpan declare setelah require atau setelah kode konfigurasi — pastikan baris tersebut selalu berada tepat di bawah <?php (dan komentar bila perlu).

Siapa yang Menentukan Mode: Berkas Pemanggil #

Kami menguji kedua arah aturan ini pada PHP 8.4: berkas pustaka tanpa declare yang dipanggil dari berkas ber-strict_types menghasilkan TypeError untuk argumen string; berkas pustaka ber-strict_types yang dipanggil dari berkas biasa justru tetap menerima konversi. Konsekuensinya praktis: aktifkan strict_types di setiap berkas yang Anda tulis — termasuk berkas masuk (entry point) — karena mode tidak pernah menular antar berkas. Peran strict_types di dalam berkas pustaka dan proyek nyata dibahas lagi pada Chapter Strict Types dalam Fungsi .

Ringkasan #

Mode lemah mengonversi argumen dan nilai balik mengikuti tabel koersi — string numerik diterima, float pecahan memicu Deprecated, dan nilai mustahil dikonversi berujung TypeError. Mode ketat mematikan seluruh konversi kecuali pelebaran int ke float, menuntut declare(strict_types=1) sebagai pernyataan pertama berkas, dan mengikuti lokasi pemanggilan. Dengan dua mode ini kita telah melihat mesin konversi dan penegakan tipe PHP bekerja — saatnya menarik mundur kamera dan memetakan seluruh type system PHP sebagai jembatan ke Section berikutnya.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

ketat.php

Unduh berkas
php
<?php

declare(strict_types=1);

// ketat.php — mode strict types untuk Chapter Strict Types.
// Jalankan dengan: php ketat.php

function hitungDiskon(int $harga, float $persen): float {
    return $harga * $persen;
}

// String numerik DITOLAK di mode strict — TypeError ditangkap agar program lanjut.
try {
    var_dump(hitungDiskon("100000", 0.1));
} catch (TypeError $e) {
    echo "TypeError ditangkap — argumen string tidak diterima\n";
}

// Argumen dengan tipe yang tepat diterima seperti biasa.
var_dump(hitungDiskon(100000, 0.1));

// Pengecualian: int tetap boleh dilebarkan menjadi float di mode strict.
function setengah(float $x): float {
    return $x / 2;
}
var_dump(setengah(7));

lemah.php

Unduh berkas
php
<?php

// lemah.php — mode weak coercion (bawaan PHP) untuk Chapter Strict Types.
// Jalankan dengan: php lemah.php

function hitungDiskon(int $harga, float $persen): float {
    return $harga * $persen;
}

// String numerik "100000" dipaksa menjadi int 100000.
var_dump(hitungDiskon("100000", 0.1));

// Float 6.0 dipaksa menjadi int 6 (tanpa kehilangan presisi).
var_dump(hitungDiskon(6.0, 0.5));

// Nilai balik int dipaksa menjadi string oleh deklarasi return string.
function ubahKeString(int $x): string {
    return $x;
}
var_dump(ubahKeString("25"));