Komentar #
Komentar adalah salah satu hal yang paling mudah ditulis di kode, tapi juga paling mudah ditulis dengan salah. Ada dua ekstrem yang sama-sama buruk: kode tanpa komentar sama sekali yang membuat rekan tim (atau dirimu sendiri tiga bulan ke depan) kesulitan memahami maksudnya, dan kode yang dipenuhi komentar tidak berguna yang hanya mengulang apa yang sudah jelas dari kode itu sendiri. PHP menyediakan tiga sintaks komentar — //, #, dan /* */ — plus format dokumentasi PHPDoc yang dikenali oleh IDE dan tools dokumentasi otomatis. Artikel ini membahas cara menggunakan ketiganya dengan tepat, kapan komentar benar-benar dibutuhkan, dan bagaimana PHPDoc bisa membuat kode PHP jauh lebih profesional dan mudah dikelola.
Jenis-Jenis Komentar PHP #
PHP mendukung tiga sintaks komentar yang masing-masing punya konteks penggunaan yang tepat.
flowchart TD
A[Komentar PHP] --> B["Satu Baris\n// atau #"]
A --> C["Multi-Baris\n/* ... */"]
A --> D["Dokumentasi\n/** ... */\nPHPDoc"]
B --> E["Penjelasan singkat\ndi tengah kode"]
C --> F["Blok penjelasan\npanjang atau\nnonaktifkan kode"]
D --> G["Dokumentasi fungsi,\nkelas, property\nuntuk IDE & tools"]Komentar Satu Baris #
PHP mengenal dua cara menulis komentar satu baris: // dan #. Keduanya identik secara fungsional — interpreter mengabaikan semua teks dari penanda tersebut hingga akhir baris.
<?php
// Ini komentar satu baris dengan double slash
echo "Hello!";
# Ini komentar satu baris dengan tanda pagar (gaya shell)
echo "World!";
$harga = 150000; // Rp 150.000 — komentar bisa di akhir baris kode
$diskon = 0.1; # 10% diskon
Dalam praktik modern PHP, // jauh lebih umum digunakan. Tanda # berasal dari konvensi shell scripting dan masih valid di PHP, tapi tidak lazim di kode PHP kontemporer. Gunakan // sebagai standar konsisten.
Komentar Multi-Baris #
Komentar multi-baris menggunakan pembuka /* dan penutup */. Semua teks di antara keduanya diabaikan interpreter, terlepas dari berapa banyak baris.
<?php
/*
* Fungsi ini menghitung diskon progresif:
* - Pembelian < 100.000 : tidak ada diskon
* - Pembelian 100k-500k : diskon 5%
* - Pembelian > 500.000 : diskon 10%
*
* Catatan: harga yang diterima sudah termasuk PPN
*/
function hitungDiskon(float $harga): float
{
if ($harga >= 500000) {
return $harga * 0.10;
}
if ($harga >= 100000) {
return $harga * 0.05;
}
return 0;
}
Konvensi yang umum adalah menambahkan * di awal setiap baris di dalam blok komentar (seperti contoh di atas). Ini bukan aturan wajib PHP, tapi membuat blok komentar lebih mudah dibaca dan menjadi standar de facto di komunitas PHP.
Heredoc sebagai “Komentar” String Panjang #
Satu pola yang kadang dipakai untuk menyisipkan teks panjang tanpa mengeksekusinya adalah dengan heredoc yang tidak digunakan, tapi ini bukan komentar sesungguhnya dan sebaiknya dihindari:
<?php
// ANTI-PATTERN: heredoc yang tidak diassign bukan komentar
<<<EOT
Ini bukan komentar.
PHP akan mem-parse teks ini sebagai string expression,
lalu membuangnya karena tidak diassign ke variabel.
Boros dan membingungkan.
EOT;
// BENAR: gunakan /* */ untuk blok teks panjang yang ingin dikomentari
/*
Ini adalah komentar yang sesungguhnya.
Interpreter benar-benar mengabaikan ini.
*/
Kapan Menulis Komentar — dan Kapan Tidak #
Ini adalah pertanyaan yang lebih penting dari “bagaimana cara menulis komentar”. Komentar yang baik menjelaskan mengapa, bukan apa. Kode yang baik seharusnya sudah menjelaskan apa yang dilakukannya melalui nama variabel, fungsi, dan struktur yang jelas.
Komentar yang Tidak Diperlukan #
<?php
// ANTI-PATTERN: komentar yang hanya mengulang kode
// Inisialisasi variabel $total dengan nilai 0
$total = 0;
// Loop melalui array $items
foreach ($items as $item) {
// Tambahkan harga item ke total
$total += $item['harga']; // tambah harga ke total
}
// Kembalikan total
return $total;
Semua komentar di atas tidak menambahkan informasi apapun. Kode itu sendiri sudah cukup jelas: $total = 0 jelas menginisialisasi total, foreach jelas melakukan iterasi. Komentar seperti ini hanya menambah noise dan harus dirawat setiap kali kode berubah — kalau lupa diupdate, komentar justru menjadi misleading.
<?php
// BENAR: kode yang berbicara sendiri, tanpa komentar berlebihan
$total = 0;
foreach ($items as $item) {
$total += $item['harga'];
}
return $total;
Komentar yang Dibutuhkan #
Komentar diperlukan ketika kode melakukan sesuatu yang tidak intuitif — ketika logika bisnis, batasan teknis, atau keputusan desain tidak bisa ditangkap dari kode itu sendiri.
<?php
// ✓ Komentar yang menjelaskan MENGAPA, bukan APA
// PHP's strtotime() tidak bisa parse format tanggal Indonesia "15 Januari 2024"
// karena menggunakan nama bulan dalam bahasa Inggris. Konversi manual diperlukan.
$tanggal = konversiTanggalIndonesia($inputTanggal);
// ✓ Komentar yang menjelaskan konteks bisnis yang tidak terlihat dari kode
// Harga dikalikan 1.11 karena mencakup PPN 11% sesuai PMK 2022
$hargaFinal = $hargaPokok * 1.11;
// ✓ Komentar yang menjelaskan workaround atau bug yang sengaja dibiarkan
// BUG: library XYZ versi 2.x mengembalikan null alih-alih false
// ketika resource tidak ditemukan. Ini diperbaiki di versi 3.x.
// TODO: hapus null check ini setelah upgrade ke v3.x (#TICKET-4521)
$hasil = libraryXyz->cariData($id);
if ($hasil === null || $hasil === false) {
return null;
}
// ✓ Komentar yang menandai bagian kritis yang tidak boleh diubah sembarangan
// PERINGATAN: urutan operasi ini penting untuk konsistensi hash.
// Mengubah urutan akan menyebabkan semua token lama menjadi invalid.
$token = hash('sha256', $userId . $timestamp . $secret);
Menonaktifkan Kode Sementara #
Salah satu penggunaan praktis komentar adalah menonaktifkan kode sementara selama debugging:
<?php
function prosesOrder(array $data): array
{
$order = buatOrder($data);
// Nonaktifkan sementara pengiriman email selama debugging
// kirimEmailKonfirmasi($order['email'], $order['id']);
/*
// Fitur notifikasi SMS masih dalam pengembangan
$sms = new SmsGateway();
$sms->kirim($order['telepon'], "Order #{$order['id']} berhasil");
*/
kurangiStok($order['items']);
return $order;
}
Komentar untuk menonaktifkan kode adalah alat debugging sementara — jangan biarkan kode yang dikomentari permanen di codebase production. Kode yang tidak digunakan seharusnya dihapus, bukan dikomentari. Version control (Git) menyimpan histori perubahan jika kamu perlu kembali ke kode lama.
Komentar Inline yang Efektif #
Komentar inline — komentar di akhir baris kode — paling efektif untuk penjelasan singkat yang langsung terkait satu ekspresi:
<?php
// ✓ Komentar inline yang menambah nilai
define('MAX_LOGIN_ATTEMPTS', 5); // Sesuai kebijakan keamanan internal
$timeout = 30 * 60; // 30 menit dalam detik
$pattern = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/'; // RFC 5322 simplified
// ✗ Komentar inline yang tidak perlu
$i = 0; // set i ke 0
$nama = "Budi"; // nama adalah Budi
$aktif = true; // aktif bernilai true
Untuk konstanta magic number (angka tanpa konteks), komentar inline sangat membantu:
<?php
// ANTI-PATTERN: magic number tanpa konteks
if ($responseCode === 429) {
sleep(60);
}
// BENAR: beri nama dan komentar jika perlu
define('HTTP_TOO_MANY_REQUESTS', 429);
define('RATE_LIMIT_BACKOFF_SECONDS', 60); // Cooldown sesuai dokumentasi API vendor
if ($responseCode === HTTP_TOO_MANY_REQUESTS) {
sleep(RATE_LIMIT_BACKOFF_SECONDS);
}
PHPDoc #
PHPDoc adalah standar penulisan komentar dokumentasi untuk PHP, mengikuti format /** ... */ (dua bintang di pembuka). Komentar PHPDoc dibaca oleh IDE seperti PhpStorm dan VS Code untuk memberikan autocomplete, type hint, dan inline documentation — bahkan tanpa menjalankan kode.
sequenceDiagram
participant Dev as Developer
participant IDE as IDE / Editor
participant Tools as phpDocumentor / PHPStan
Dev->>IDE: Tulis PHPDoc pada fungsi/kelas
IDE-->>Dev: Autocomplete parameter & return type
IDE-->>Dev: Warning jika argumen salah tipe
Dev->>Tools: Jalankan dokumentasi generator
Tools-->>Dev: HTML docs / type analysis reportStruktur PHPDoc #
Sebuah blok PHPDoc terdiri dari tiga bagian opsional:
<?php
/**
* [Summary — satu kalimat singkat, diakhiri titik.]
*
* [Description — paragraf penjelasan lebih panjang, opsional.
* Bisa lebih dari satu baris. Gunakan ini untuk menjelaskan
* konteks, batasan, atau perilaku yang tidak terlihat dari signature.]
*
* @tag nilai [Tag annotations]
* @tag nilai
*/
Tag PHPDoc yang Paling Penting #
| Tag | Digunakan Pada | Fungsi |
|---|---|---|
@param | Fungsi / Method | Dokumentasi parameter: tipe dan deskripsi |
@return | Fungsi / Method | Dokumentasi nilai kembalian |
@var | Property / Variabel | Dokumentasi tipe variabel |
@throws | Fungsi / Method | Exception yang mungkin dilempar |
@deprecated | Semua | Tandai sebagai usang, sertakan alternatif |
@see | Semua | Referensi ke elemen atau URL lain |
@since | Semua | Versi pertama elemen ini ada |
@author | File / Kelas | Penulis kode |
@link | Semua | URL referensi eksternal |
@todo | Semua | Pekerjaan yang masih perlu dilakukan |
Dokumentasi Fungsi #
<?php
/**
* Menghitung total harga setelah diskon dan pajak.
*
* Diskon dihitung terlebih dahulu sebelum pajak diterapkan.
* Nilai diskon yang melebihi harga pokok akan di-clamp ke 0.
*
* @param float $harga Harga pokok dalam rupiah (tanpa pajak).
* @param float $diskon Persentase diskon dalam desimal (0.0 - 1.0).
* @param float $pajakPpn Persentase PPN dalam desimal, default 0.11 (11%).
* @return float Total harga setelah diskon dan PPN.
*/
function hitungTotal(float $harga, float $diskon, float $pajakPpn = 0.11): float
{
$setelahDiskon = $harga * (1 - $diskon);
$setelahDiskon = max(0, $setelahDiskon); // clamp, tidak boleh negatif
return $setelahDiskon * (1 + $pajakPpn);
}
// IDE sekarang tahu:
// - Parameter pertama harus float
// - Parameter kedua harus float antara 0.0-1.0 (dari deskripsi)
// - Fungsi mengembalikan float
// - Parameter ketiga opsional dengan default 0.11
Dokumentasi Kelas dan Property #
<?php
/**
* Merepresentasikan satu item dalam keranjang belanja.
*
* @since 1.0.0
*/
class CartItem
{
/**
* @var string ID unik produk dari katalog.
*/
private string $productId;
/**
* @var int Jumlah unit yang dipesan. Selalu >= 1.
*/
private int $quantity;
/**
* @var float Harga per unit saat item ditambahkan ke keranjang.
* Harga ini di-snapshot dan tidak berubah walau harga produk berubah.
*/
private float $unitPrice;
/**
* @param string $productId ID produk dari katalog.
* @param int $quantity Jumlah unit, minimal 1.
* @param float $unitPrice Harga per unit dalam rupiah.
* @throws \InvalidArgumentException Jika quantity kurang dari 1.
*/
public function __construct(string $productId, int $quantity, float $unitPrice)
{
if ($quantity < 1) {
throw new \InvalidArgumentException(
"Quantity minimal 1, diterima: $quantity"
);
}
$this->productId = $productId;
$this->quantity = $quantity;
$this->unitPrice = $unitPrice;
}
/**
* Menghitung subtotal item (harga unit × jumlah).
*
* @return float Subtotal dalam rupiah.
*/
public function subtotal(): float
{
return $this->unitPrice * $this->quantity;
}
/**
* Menambah jumlah unit item.
*
* @param int $jumlah Jumlah unit yang ditambahkan. Harus > 0.
* @return static Mengembalikan instance ini untuk method chaining.
* @throws \InvalidArgumentException Jika jumlah tidak positif.
*/
public function tambahJumlah(int $jumlah): static
{
if ($jumlah <= 0) {
throw new \InvalidArgumentException("Jumlah harus positif");
}
$this->quantity += $jumlah;
return $this;
}
}
Tipe Kompleks di PHPDoc #
PHPDoc mendukung notasi tipe yang lebih kaya dari type hint PHP biasa, sangat berguna untuk array dan union type:
<?php
/**
* Mencari produk berdasarkan filter yang diberikan.
*
* @param array{
* kategori?: string,
* harga_min?: float,
* harga_max?: float,
* in_stock?: bool
* } $filter Kriteria pencarian. Semua key opsional.
*
* @return array<int, array{
* id: int,
* nama: string,
* harga: float,
* stok: int
* }> Daftar produk yang cocok, diindeks secara numerik.
*/
function cariProduk(array $filter): array
{
// implementasi...
return [];
}
/**
* Memproses satu item dan mengembalikan hasilnya atau null jika gagal.
*
* @param int|string $id ID item — bisa integer dari database atau
* string UUID dari sistem eksternal.
* @return Produk|null Instance Produk jika ditemukan, null jika tidak.
*/
function cariProdukById(int|string $id): ?Produk
{
// implementasi...
return null;
}
Tag @deprecated
#
Saat sebuah fungsi atau kelas sudah tidak direkomendasikan, gunakan @deprecated untuk memberi tahu developer — IDE akan menampilkan strikethrough pada pemanggilan fungsi tersebut:
<?php
/**
* Mengenkripsi password menggunakan MD5.
*
* @deprecated 2.0.0 MD5 tidak aman untuk password. Gunakan password_hash() sebagai gantinya.
* @see password_hash()
*
* @param string $password Password dalam plaintext.
* @return string Hash MD5 — JANGAN gunakan ini untuk sistem baru.
*/
function enkripsiPasswordLama(string $password): string
{
return md5($password); // tidak aman, hanya untuk backward compatibility
}
// Cara yang benar untuk password hashing di PHP modern:
function simpanPassword(string $password): string
{
return password_hash($password, PASSWORD_BCRYPT);
}
function verifikasiPassword(string $password, string $hash): bool
{
return password_verify($password, $hash);
}
Integrasi dengan Static Analysis #
PHPDoc bukan hanya untuk dokumentasi manusia — tools static analysis seperti PHPStan dan Psalm menggunakan informasi di PHPDoc untuk menemukan bug sebelum kode dijalankan:
<?php
/**
* @param array<string, int> $scores Mapping nama ke nilai.
* @return string Nama dengan nilai tertinggi.
*/
function raiNilaiTertinggi(array $scores): string
{
// PHPStan tahu bahwa $scores berisi string key dan int value
// Jika kamu menulis kode yang memperlakukan value sebagai string,
// PHPStan akan memberikan warning tanpa perlu menjalankan kode
arsort($scores);
return array_key_first($scores);
}
// PHPStan akan menangkap error ini:
// raiNilaiTertinggi(["Budi" => "sembilan puluh"]); // ✗ value harus int, bukan string
// raiNilaiTertinggi([1 => 90, 2 => 85]); // ✗ key harus string, bukan int
Komentar untuk File dan Namespace #
Di awal file PHP, terutama untuk library atau framework, biasanya ada blok PHPDoc yang mendokumentasikan keseluruhan file:
<?php
/**
* Modul autentikasi pengguna.
*
* Menangani proses login, logout, refresh token, dan validasi sesi
* untuk aplikasi web dan REST API.
*
* @package App\Auth
* @author Tim Backend <[email protected]>
* @since 1.0.0
* @link https://docs.example.com/auth
*/
declare(strict_types=1);
namespace App\Auth;
use App\Models\User;
use App\Exceptions\AuthException;
/**
* Service untuk mengelola autentikasi dan sesi pengguna.
*/
class AuthService
{
// ...
}
Perhatikan declare(strict_types=1) yang diletakkan setelah tag pembuka PHP dan sebelum namespace. Ini mengaktifkan strict type checking di file tersebut — setiap pemanggilan fungsi yang tidak sesuai tipe parameternya akan melempar TypeError alih-alih melakukan konversi implisit.
Anti-Pattern Komentar yang Sering Ditemui #
Beberapa pola komentar yang sering muncul di codebase tapi justru menurunkan kualitas kode:
<?php
// ✗ Anti-pattern 1: Komentar yang berbohong (lebih buruk dari tidak ada komentar)
// Menghitung harga dengan diskon 10%
$total = $harga * 1.15; // Faktanya ini menambah 15%, bukan diskon!
// ✗ Anti-pattern 2: TODO yang tidak pernah dikerjakan
// TODO: perbaiki ini nanti
$hasil = metodeTidakAman($data); // "nanti" tidak pernah datang
// ✗ Anti-pattern 3: Komentar credit yang tidak relevan
// Dibuat oleh Budi pada 15 Maret 2021
// Diubah oleh Siti pada 20 April 2021
// Diubah lagi oleh Dani pada 5 Januari 2022
// (gunakan git blame untuk ini, bukan komentar)
$harga = 150000;
// ✗ Anti-pattern 4: Komentar yang menjelaskan sintaks dasar bahasa
// Membuat array baru
$data = [];
// Mengassign nilai ke variabel
$nama = "Budi";
// ✗ Anti-pattern 5: Kode lama yang dikomentari tanpa penjelasan
// $hasil = metodeLama($input);
// $hasil = metodeLamaV2($input, $options);
$hasil = metodeBaruV3($input, $options, $config); // apa bedanya? kenapa diganti?
<?php
// ✓ Versi yang lebih baik dari anti-pattern di atas:
// 1. Kode yang jelas dengan variabel yang berbicara sendiri
$totalDenganPpn = $harga * (1 + PPN_RATE); // PPN_RATE = 0.11 didefinisikan sebagai konstanta
// 2. TODO yang actionable dan terlacak
// TODO(TICKET-891): Ganti metodeTidakAman() dengan implementasi bcrypt
// sebelum release v2.0 — deadline 30 Juni 2025
$hasil = metodeTidakAman($data);
// 3. Komentar yang menjelaskan mengapa terjadi perubahan
// Beralih dari metodeLamaV2 ke metodeBaruV3 karena V2 tidak thread-safe
// saat memproses request concurrent. Lihat insiden #2024-03-15.
$hasil = metodeBaruV3($input, $options, $config);
Ringkasan #
- Tiga sintaks komentar PHP:
//untuk satu baris (paling umum),/* */untuk multi-baris, dan/** */untuk PHPDoc. Tanda#valid tapi tidak idiomatik di PHP modern.- Komentar menjelaskan MENGAPA, bukan APA — kode yang baik sudah menjelaskan apa yang dilakukannya; komentar bertugas menjelaskan konteks, keputusan desain, dan keterbatasan yang tidak terlihat dari kode.
- Komentar yang mengulang kode adalah noise — peliharanya mahal (harus diupdate tiap kode berubah) dan kalau lupa diupdate, justru menjadi misleading.
- PHPDoc (
/** */) dikenali IDE dan static analysis tools — gunakan untuk semua fungsi, method, kelas, dan property publik agar autocomplete dan type checking bekerja optimal.- Tag PHPDoc penting:
@param(parameter),@return(nilai kembalian),@throws(exception),@var(tipe property),@deprecated(tandai usang).- Kode yang dikomentari bukan arsip — hapus kode yang tidak dipakai dan andalkan Git untuk histori. Kode mati yang dikomentari mengotori codebase dan membingungkan.
- TODO harus actionable — sertakan nomor tiket atau deadline agar tidak menjadi “TODO abadi” yang tidak pernah dikerjakan.
- PHPStan / Psalm membaca PHPDoc — tipe yang kamu dokumentasikan di
@paramdan@returndigunakan untuk static analysis, sehingga bug tipe bisa terdeteksi tanpa menjalankan kode.