Satu baris ekspresi bisa memuat banyak operator sekaligus, dan PHP harus memutuskan mana yang dievaluasi lebih dulu. Aturan pengurutan itu bernama operator precedence: 1 + 2 * 3 menghasilkan 7 karena perkalian menang lawan penjumlahan, bukan 9 seperti bila dibaca dari kiri. Sebagian besar aturannya cocok dengan naluri matematika sekolah, tetapi beberapa kombinasi — terutama yang melibatkan penggabungan string, and/or, dan penugasan — terkenal menjebak bahkan programmer berpengalaman. Pada Chapter ini kita akan memetakan urutan precedence PHP, membuktikan jebakan-jebakannya dengan eksekusi nyata, dan membentuk kebiasaan menulis ekspresi yang tidak menyisakan ruang salah tafsir.

Urutan Evaluasi Default #

Ketika dua operator berbagi satu ekspresi tanpa tanda kurung, yang berprecedence lebih tinggi dievaluasi lebih dulu. Tanda kurung selalu bisa menimpa urutan tersebut:

php
<?php
$hasil = 1 + 2 * 3;
echo $hasil . "\n";

$hasilDenganKurung = (1 + 2) * 3;
echo $hasilDenganKurung . "\n";

Output yang Dihasilkan:

text
7
9

Pada baris ketiga, 2 * 3 dievaluasi lebih dulu menjadi 6, baru ditambah 1. Tanda kurung pada baris keenam memaksa 1 + 2 selesai lebih dulu menjadi 3, lalu dikalikan 3. Membaca keluaran seperti ini memang terasa sepele — jebakan sesungguhnya menunggu di kombinasi operator yang lebih jarang kita sadari urutannya.

Peta Precedence Operator PHP #

Tabel berikut merangkum level-level yang paling sering bersinggungan sehari-hari, diurutkan dari yang tertinggi (dievaluasi pertama) ke terendah. Nama lengkap setiap operator bisa Anda telusuri kembali di chapter-chapter sebelumnya pada Section ini:

PrioritasOperatorAsosiativitasContoh
1 (tertinggi)**kanan2 ** 3 ** 2
2++ -- ~ (int) (bool) !kanan!$valid, (int) $harga
3* / %kiri10 / 5 * 2
4+ -kiri1 + 2 - 3
5<< >>kiri$nilai << 2
6.kiri"a" . "b" . "c"
7< <= > >=tanpa$usia >= 17
8== != === !== <=>tanpa$a === $b
9-11& lalu ^ lalu ``kiri
12&&kiri$a && $b
13``
14??kanan$x ?? $y
15?: (ternary)kanan$x ? $a : $b
16= += -= .= ??= dll.kanan$x = $y = 5
17 (terendah)and xor orkiri$a and $b

Dua catatan penting untuk tabel ini. Pertama, kolom asosiativitas mengatur operator-operator yang setara precedence: kiri berarti yang paling kiri dievaluasi dulu (10 - 4 - 2 = (10 - 4) - 2 = 4), sedangkan kanan berarti sebaliknya. Kedua, tabel php.net memuat level tambahan seperti clone, new, dan instanceof yang kita lewati di sini karena jarang bersinggungan dengan operator lain — rujukan lengkapnya ada di bagian Referensi.

Asosiativitas dan Pangkat #

Operator pangkat ** adalah contoh terbaik untuk merasakan asosiativitas kanan. Ekspresi berikut menghitung 3 ** 2 terlebih dahulu, baru hasilnya menjadi pangkat dari 2:

php
<?php
var_dump(2 ** 3 ** 2);
var_dump((2 ** 3) ** 2);

var_dump(-2 ** 2);

Output yang Dihasilkan:

text
int(512)
int(64)
int(-4)

Hasil pertama adalah 512 (= 2 ** 9) karena asosiativitas kanan mengelompokkan ekspresi sebagai 2 ** (3 ** 2); tanda kurung pada baris kedua memaksa pengelompokan kiri sehingga hasilnya 64. Baris ketiga memperlihatkan aturan lain yang sering mengejutkan: -2 ** 2 adalah -(2 ** 2) = -4, karena unary minus memiliki precedence lebih rendah daripada **. Penugasan juga berasosiatif kanan, sehingga $x = $y = 5 mengisi $y dahulu, lalu nilai itu disalin ke $x — dua variabel sama-sama bernilai 5.

Jebakan Konkatenasi dan Penjumlahan #

Perubahan precedence paling terkenal dalam sejarah PHP modern terjadi di PHP 8.0: operator penggabung string (.) diturunkan posisinya di bawah + dan -. Akibatnya, penjumlahan kini selalu selesai sebelum teks digabungkan:

php
<?php
echo "sum: " . 1 + 2 . "\n";
echo 3 * 2 . "!\n";

Output yang Dihasilkan:

text
sum: 3
6!

Pada baris pertama, ekspresi dikelompokkan menjadi "sum: " . (1 + 2) . "\n" — angka 1 dan 2 dijumlahkan dulu menjadi 3, baru digabung dengan teks di kedua sisinya. Baris kedua serupa: 3 * 2 selesai menjadi 6 lebih dulu. Pada PHP 7 urutannya kebalikan, ("sum: " . 1) + 2, yang memaksa PHP mengubah teks "sum: 1" menjadi angka dan menghasilkan 2 disertai peringatan — perilaku lama inilah yang mendorong perubahan tersebut.

Perubahan ini juga membuat satu jebakan baru: ketika operand penjumlahan “nyasar” ke sisi yang Anda kira milik string, TypeError menghentikan program:

php
<?php
echo "Stok: " . 3 + "apel";

Kode di atas lolos pemeriksaan sintaks, tetapi saat dijalankan PHP mengelompokkannya sebagai "Stok: " . (3 + "apel") — dan 3 + "apel" adalah penjumlahan integer dengan string non-numerik yang tidak lagi diizinkan di PHP 8:

text
Fatal error: Uncaught TypeError: Unsupported operand types: int + string in contoh.php on line 2

Pesan di atas diambil dari eksekusi nyata di PHP 8.4. Solusinya bukan sekadar menggeser tanda, melainkan memisahkan tanggung jawab: hitung dulu di variabel bernomor jelas, atau bungkus bagian aritmetika dengan tanda kurung seperti echo "Stok: " . (3 + $tambahan); agar niat kode terbaca sejak pandangan pertama.

&& Lebih Kuat dari || #

Pada ekspresi logika campuran, && selalu menang lawan || — sejalan dengan perkalian yang menang lawan penjumlahan di aritmatika:

php
<?php
var_dump(true || false && false);
var_dump((true || false) && false);

Output yang Dihasilkan:

text
bool(true)
bool(false)

Baris pertama dikelompokkan sebagai true || (false && false) sehingga hasilnya true. Tanda kurung pada baris kedua membalik pengelompokan dan menghasilkan false. Tanpa tanda kurung, pembaca sering menerjemahkan a || b && c sebagai “(a atau b) dan c” — dua hasil yang berbeda total dari teks yang sama persis.

Jebakan and dan or #

Dua baris terakhir tabel precedence menyimpan jebakan yang bahkan menghabiskan berjam-jam waktu banyak programmer: versi huruf kecil and, or, xor punya precedence lebih rendah daripada tanda sama dengan (=):

php
<?php
$hasil1 = true and false;
var_dump($hasil1);

$hasil2 = true && false;
var_dump($hasil2);

Output yang Dihasilkan:

text
bool(true)
bool(false)

Baris pertama dikelompokkan sebagai ($hasil1 = true) and false — penugasan selesai dulu, $hasil1 terisi true, lalu hasil and yang tidak dipakai siapa pun dibuang. Baris keempat dengan && dikelompokkan sebagai $hasil2 = (true && false) sehingga $hasil2 terisi false. Kedua operator ini bekerja sama logikanya (dan sama-sama short-circuit), tetapi precedence-nya berbeda derajat. Praktik industri PHP hampir selalu memilih &&/|| untuk ekspresi dan menyisakan and/or hanya untuk pola khusus seperti $berhasil = simpan() or catatError();.

Tanda Kurung untuk Kejelasan #

Setelah melihat jebakan-jebakan di atas, kesimpulan praktisnya bukan “hafalkan tabelnya”, melainkan “tulis niat Anda secara eksplisit”. Tanda kurung tidak mengubah performa apa pun — PHP hanya mengikuti pengelompokan yang sudah jelas. Bandingkan dua baris berikut, keduanya benar, tetapi yang kedua tidak menyisakan ruang salah baca:

php
<?php
$umur = 25;
$status1 = $umur >= 13 && $umur <= 19 ? "remaja" : "bukan remaja";
$status2 = ($umur >= 13 && $umur <= 19) ? "remaja" : "bukan remaja";

echo $status1 . "\n";
echo $status2 . "\n";

Output yang Dihasilkan:

text
bukan remaja
bukan remaja

Berkas contoh.php pada bagian bawah halaman merangkum seluruh eksperimen Chapter ini sehingga Anda bisa mengubah nilainya dan mengamati ulang setiap pengelompokan.

Ringkasan #

Operator precedence menentukan urutan evaluasi ketika banyak operator berbagi satu ekspresi: pangkat dan unary di puncak, lalu aritmetika, perbandingan, logika, hingga penugasan — dengan and/or di dasar tabel. PHP 8 menurunkan posisi . di bawah +/-, mengubah hasil "sum: " . 1 + 2 menjadi "sum: 3" dan memindahkan jebakan lama ke TypeError pada penjumlahan string non-numerik. Tanda kurung gratis, jadi gunakanlah — ekspresi yang jelas selalu menang lawan yang ringkas tapi menuntut hafalan. Dengan peta precedence lengkap, kita siap membahas topik penutup Section ini: truthiness dan short-circuit.

Referensi #

Berkas Sumber

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

contoh.php

Unduh berkas
php
<?php
// contoh.php — rangkuman Operator Precedence
// Jalankan dengan: php contoh.php

// 1. Perkalian menang lawan penjumlahan; tanda kurung menimpa urutan
echo "1 + 2 * 3       : " . (1 + 2 * 3) . "\n";
echo "(1 + 2) * 3     : " . ((1 + 2) * 3) . "\n";

// 2. Pangkat berasosiatif kanan; unary minus kalah dari **
echo "2 ** 3 ** 2     : " . (2 ** 3 ** 2) . "\n";
echo "(2 ** 3) ** 2   : " . ((2 ** 3) ** 2) . "\n";
echo "-2 ** 2         : " . (-2 ** 2) . "\n";

// 3. PHP 8: penjumlahan selesai sebelum penggabungan string
echo "\"sum: \" . 1 + 2 : " . ("sum: " . 1 + 2) . "\n";
echo "3 * 2 . \"!\"     : " . (3 * 2 . "!") . "\n";

// 4. && lebih kuat dari ||
echo "true || false && false          : ";
var_dump(true || false && false);
echo "(true || false) && false        : ";
var_dump((true || false) && false);

// 5. and lebih lemah dari = (jebakan klasik)
$hasil1 = true and false;
$hasil2 = true && false;
echo "\$hasil1 = true and false  : ";
var_dump($hasil1);
echo "\$hasil2 = true && false   : ";
var_dump($hasil2);

// 6. Penugasan berasosiatif kanan
$x = $y = 5;
echo "x = $x, y = $y\n";

// 7. Tanda kurung untuk kejelasan
$umur = 25;
$status = ($umur >= 13 && $umur <= 19) ? "remaja" : "bukan remaja";
echo "Status: $status\n";