Kerangka dari Bagian 1 sudah tahu cara menerima perintah, tetapi perintah hitung masih stub yang menolak bekerja. Pada bagian ini kita mengisinya: mengubah string dari $argv menjadi angka, memetakan tujuh operator (+ - * / % ** intdiv) ke operasi PHP yang sesuai, dan mencetak hasilnya. Di sepanjang jalan kita akan berhadapan dengan type juggling PHP — perilaku otomatis yang kadang menolong dan kadang diam-diam merusak hasil. Bagian ini juga sengaja memperlihatkan sisi rapuh program ketika menerima input yang belum divalidasi penuh; kerapuhan itulah yang menjadi pekerjaan Bagian 3 dan Bagian 4.
Dari String argv ke Angka #
Semua elemen $argv bertipe string, termasuk "5" dan "7.5". PHP sebenarnya bersedia menjumlahkan dua string numerik lewat type juggling — bahkan di berkas dengan declare(strict_types=1), karena operator tidak tunduk pada aturan strict mode yang hanya mengikat pemanggilan fungsi:
<?php
declare(strict_types=1);
$a = '7';
$b = '5';
var_dump($a + $b);Output yang Dihasilkan:
int(12)Namun kita tidak bisa bersandar pada juggling untuk program seperti ini. Pertama, juggling hanya terjadi pada operator, sedangkan fungsi seperti intdiv() menuntut tipe int yang benar-benar sudah berupa int. Kedua, string yang tampak seperti angka bisa menipu cast yang kita lakukan sendiri — perhatikan dua contoh klasik ini:
<?php
var_dump((int) '7.5', (int) 'abc', (float) '1e3');Output yang Dihasilkan:
int(7)
int(0)
float(1000)Cast (int) '7.5' memotong pecahan menjadi 7 tanpa peringatan, dan (int) 'abc' menghasilkan 0 — dua angka salah yang lahir dalam senyap. Karena itu main.php versi bagian ini memakai dua alat: is_numeric() untuk memastikan string memang angka yang valid, lalu fungsi toNumber() yang mengubah string menjadi int atau float secara sadar. Fungsi penentu keputusannya sederhana: bentuk harfiah bilangan bulat seperti "5", "-3", atau "+7" dikonversi ke int; bentuk lain yang lolos is_numeric() seperti "7.5" dan "1e3" dikonversi ke float.
Untuk pemeriksaan awal, commandHitung() menolak operand yang tidak numerik dengan pesan error dan exit code 1 — satu-satunya validasi yang dibawa bagian ini:
<?php
$contoh = ['5', '7.5', '1e3', 'abc', '12a'];
foreach ($contoh as $nilai) {
$status = is_numeric($nilai) ? 'angka valid' : 'bukan angka';
printf("%-6s => %s\n", "'{$nilai}'", $status);
}Output yang Dihasilkan:
'5' => angka valid
'7.5' => angka valid
'1e3' => angka valid
'abc' => bukan angka
'12a' => bukan angkaPerhatikan bahwa is_numeric() menerima beragam bentuk numerik ("1e3" adalah 1000 dalam notasi ilmiah) dan menolak string campuran seperti "12a" — perilaku yang persis kita butuhkan di gerbang masuk. Detail lengkap perilaku ini terkait erat dengan type juggling
yang sudah kita bahas di Section Tipe Data, plus dokumentasi resmi di bagian Referensi.
Memetakan Operator dengan match #
Dengan angka sudah di tangan, tujuh operasi dipetakan lewat satu ekspresi match di dalam fungsi hitung(). Berikut versi ringkasnya yang bisa berdiri sendiri:
<?php
function hitungKecil(int|float $a, string $operator, int|float $b): int|float
{
return match ($operator) {
'+' => $a + $b,
'-' => $a - $b,
'*' => $a * $b,
'/' => $a / $b,
'%' => $a % $b,
'**' => $a ** $b,
'intdiv' => intdiv($a, $b),
};
}
echo hitungKecil(7, '+', 5), "\n";
echo hitungKecil(10, '-', 4), "\n";
echo hitungKecil(2, '*', 6), "\n";
echo hitungKecil(10, '/', 4), "\n";
echo hitungKecil(7, '%', 3), "\n";
echo hitungKecil(2, '**', 10), "\n";
echo hitungKecil(10, 'intdiv', 3), "\n";Output yang Dihasilkan:
12
6
12
2.5
1
1024
3Setiap arm memetakan satu token operator ke satu ekspresi; tidak ada if-else bertingkat, dan menambah operator baru berarti menambah satu baris. Operator intdiv sengaja ditulis sebagai kata, bukan simbol, supaya tidak perlu melawan kebiasaan shell dan tetap terbaca sebagai nama fungsi PHP yang memang tersedia. Fungsi hitung() di berkas main.php mengikuti bentuk ini persis, lengkap dengan union type int|float pada parameter dan nilai baliknya.
Perilaku Tipe Hasil Pembagian #
Tiga operator ini punya aturan hasil yang perlu dipahami sebelum kita percaya pada keluarannya:
<?php
var_dump(10 / 5, 10 / 4, 7 / 2);
var_dump(intdiv(-7, 2), -7 % 3);Output yang Dihasilkan:
int(2)
float(2.5)
float(3.5)
int(-3)
int(-1)Operator / mengembalikan int hanya ketika kedua operand int dan pembagian habis; selain itu hasilnya float. Operator intdiv() membulatkan ke arah nol (-7 / 2 menjadi -3, bukan -4), sedangkan % mengikuti tanda pembagiannya (-7 % 3 adalah -1). Perbedaan int dan float ini akan kembali sangat relevan di Bagian 4, ketika test menuntut tipe hasil yang persis — bukan sekadar nilai yang mirip.
Menjalankan Perintah hitung #
Sekarang momen yang ditunggu — kalkulator benar-benar menghitung. Semua contoh berikut dijalankan dari direktori bagian ini:
cd content/php/proyek-php/kalkulator-terminal/bagian-2-operasi-aritmatika
php main.php hitung 7 + 5; echo "Exit code: $?"
php main.php hitung 10 / 4; echo "Exit code: $?"
php main.php hitung 7.5 + 2; echo "Exit code: $?"
php main.php hitung 7 % 3; echo "Exit code: $?"
php main.php hitung 10 intdiv 3; echo "Exit code: $?"12
Exit code: 0
2.5
Exit code: 0
9.5
Exit code: 0
1
Exit code: 0
3
Exit code: 0Sisi Rapuh Program Bagian Ini #
Jujur perlu diakui: bagian ini membuat program bekerja untuk jalur bahagia saja. Empat pemanggilan berikut menunjukkan kerapuhannya — semuanya kami jalankan sungguhan; jalur berkas dan baris Stack trace: dipangkas agar ringkas, sisanya apa adanya:
php main.php hitung 2 pangkat 10; echo "Exit code: $?"
php main.php hitung 10 / 0; echo "Exit code: $?"
php main.php hitung 7.5 intdiv 2; echo "Exit code: $?"
php main.php hitung 7.5 % 2; echo "Exit code: $?"
Fatal error: Uncaught UnhandledMatchError: Unhandled match case 'pangkat' in .../main.php:62
...
Exit code: 255
Fatal error: Uncaught DivisionByZeroError: Division by zero in .../main.php:66
...
Exit code: 255
Fatal error: Uncaught TypeError: intdiv(): Argument #1 ($num1) must ...
...
Exit code: 255
Deprecated: Implicit conversion from float 7.5 to int loses precision in .../main.php on line 67
1
Exit code: 0Empat masalah, empat akar yang berbeda: operator asing membuat match melempar UnhandledMatchError karena belum ada arm default; pembagian dengan nol melempar DivisionByZeroError yang belum ada penanganannya; intdiv() menolak float di bawah strict_types; dan % mendiamkan float dengan peringatan Deprecated lalu memotongnya menjadi int — hasil 1 yang bisa menyesatkan. Ringkasan posisinya:
| Pemanggilan | Hasil saat ini | Perbaikan di |
|---|---|---|
hitung abc + 1 | Pesan error ramah, exit 1 | Sudah beres di bagian ini |
hitung 5 + (argumen kurang) | Pesan error generik | Bagian 3 |
hitung 5 pangkat 3 (operator asing) | Fatal UnhandledMatchError, exit 255 | Bagian 3 |
hitung 7.5 intdiv 2, hitung 7.5 % 2 | Fatal TypeError / hasil terpotong | Bagian 3 |
hitung 10 / 0 | Fatal DivisionByZeroError, exit 255 | Bagian 4 |
Apa yang Berubah dari Bagian 1 #
Berkas main.php di bagian bawah halaman adalah evolusi penuh dari versi sebelumnya, dengan perubahan terpusat pada perintah hitung. Daftar perubahannya:
commandHitung()kembali betulan: memeriksais_numeric()pada kedua operand, mengonversi lewattoNumber(), memanggilhitung(), lalu mencetak hasilnya.- Fungsi baru
toNumber()memilah konversiintvsfloatberdasarkan bentuk harfiah string. - Fungsi baru
hitung()memetakan tujuh operator lewatmatchdengan union typeint|float. APP_VERSIONnaik menjadi0.2.0— penanda bahwa program kembali bisa dijalankan, hanya saja belum tangguh.
Referensi #
- Arithmetic Operators
— aturan lengkap operator
/,%, dan**. - intdiv — pembagian bulat dan arah pembulatannya.
- Numeric strings
— bentuk string yang dianggap numerik oleh
is_numeric(). - match expression — referensi arm dan perilaku ketika tidak ada yang cocok.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
main.php
Unduh berkas<?php
declare(strict_types=1);
/**
* Kalkulator Terminal — Bagian 2: Operasi Aritmatika.
*
* Pemakaian:
* php main.php hitung <angka1> <operator> <angka2>
* php main.php versi
*
* Dipanggil tanpa argumen, program mencetak panduan pemakaian lalu
* keluar dengan exit code 0.
*/
const APP_NAME = 'Kalkulator Terminal';
const APP_VERSION = '0.2.0';
/** Cetak panduan pemakaian program ke STDOUT. */
function printUsage(): void
{
echo "Pemakaian:\n";
echo " php main.php hitung <angka1> <operator> <angka2>\n";
echo " php main.php versi\n";
echo "\n";
echo "Operasi yang didukung:\n";
echo " + - * / % ** intdiv\n";
}
/** Tangani perintah `versi`: cetak nama dan versi program. */
function commandVersi(): int
{
echo APP_NAME . ' versi ' . APP_VERSION . "\n";
return 0;
}
/**
* Ubah string argumen menjadi angka int atau float.
*
* Bentuk bilangan bulat ("5", "-3", "+7") dikonversi ke int; bentuk
* lain yang lolos pemeriksaan is_numeric() ("7.5", "1e3") dikonversi
* ke float supaya bagian pecahannya tidak hilang.
*/
function toNumber(string $value): int|float
{
if (preg_match('/^[+-]?\d+$/', $value) === 1) {
return (int) $value;
}
return (float) $value;
}
/**
* Hitung satu operasi aritmatika dan kembalikan hasilnya.
*
* Operator yang dikenali: + - * / % ** intdiv.
* Bagian 3 akan menambahkan validasi; Bagian 4 menangani pembagian nol.
*/
function hitung(int|float $a, string $operator, int|float $b): int|float
{
return match ($operator) {
'+' => $a + $b,
'-' => $a - $b,
'*' => $a * $b,
'/' => $a / $b,
'%' => $a % $b,
'**' => $a ** $b,
'intdiv' => intdiv($a, $b),
};
}
/** Tangani perintah `hitung <angka1> <operator> <angka2>`. */
function commandHitung(array $args): int
{
// Lengkapi argumen yang kurang dengan string kosong supaya
// pengambilan elemen di bawah tidak memicu Warning.
// Validasi jumlah argumen yang benar menyusul di Bagian 3.
[$aRaw, $operator, $bRaw] = array_pad($args, 3, '');
if (!is_numeric($aRaw) || !is_numeric($bRaw)) {
fwrite(STDERR, "Error: kedua operand harus berupa angka.\n");
return 1;
}
$a = toNumber($aRaw);
$b = toNumber($bRaw);
$hasil = hitung($a, $operator, $b);
echo $hasil . "\n";
return 0;
}
/** Laporkan perintah yang tidak dikenal, lalu cetak panduan pemakaian. */
function unknownCommand(string $command): int
{
fwrite(STDERR, "Error: perintah tidak dikenal: {$command}\n");
printUsage();
return 1;
}
/**
* Titik masuk program: terima $argv, tentukan perintah yang diminta,
* dan kembalikan exit code ke pemanggil.
*/
function main(array $argv): int
{
// Buang nama skrip (elemen pertama) — sisanya adalah argumen pengguna.
$args = array_slice($argv, 1);
// Tanpa argumen: pengguna hanya butuh tahu cara memakai program.
if ($args === []) {
printUsage();
return 0;
}
$command = $args[0];
$commandArgs = array_slice($args, 1);
return match ($command) {
'versi' => commandVersi(),
'hitung' => commandHitung($commandArgs),
default => unknownCommand($command),
};
}
exit(main($argv));