Tool yang berjalan mulus di laptop Anda belum tentu berjalan di laptop rekan kerja. Selama distribusinya berupa “clone repositorinya, lalu composer install, lalu set PATH”, setiap pengguna mengulang penyiapan yang sama dan rentan versi yang berbeda. Pada chapter penutup section ini kita bahas cara mengemas tool CLI PHP agar pemakaiannya semudah satu berkas atau satu perintah: membangun phar yang berdiri sendiri, memasang tool lewat Composer, konvensi --version, hingga README yang menjadi kontrak dengan pengguna.
Tiga Rintangan Distribusi #
Ada tiga rintangan yang berulang setiap kali tool dibagikan. Pertama, pengguna harus memiliki runtime PHP dengan versi dan ekstensi yang sesuai. Kedua, dependensi Composer harus terpasang di sisi pengguna — tanpa itu tool langsung gagal di autoloading. Ketiga, perintah harus bisa dipanggil dari PATH tanpa menghafal jalur panjang ke berkas skrip. Strategi pada chapter ini menyerang rintangan kedua dan ketiga; rintangan pertama tetap prasyarat yang wajib Anda nyatakan dengan jelas di dokumentasi, misalnya “membutuhkan PHP 8.2+”.
Phar: Satu Berkas Berisi Segalanya #
Phar (PHP Archive) adalah format arsip milik PHP yang bekerja seperti JAR pada Java: seluruh berkas tool Anda — skrip, konfigurasi, bahkan isi direktori vendor/ — dikemas menjadi satu berkas tunggal yang dijalankan langsung oleh PHP. Pengguna cukup menyalin satu berkas tanpa composer install dan tanpa struktur direktori. Ekstensi Phar aktif secara baku pada PHP modern, sehingga menjalankan phar tidak butuh penyiapan apa pun.
Membangun Phar dengan Kelas Phar #
Kelas Phar dari ekstensi bawaan membangun arsip lewat program PHP biasa. Contoh berikut membangun arsip greeter.phar berisi satu skrip, memasang stub sebagai pintu masuk, lalu mengujinya persis seperti pengguna akhir menjalankannya:
<?php
$pharPath = sys_get_temp_dir() . "/greeter.phar";
$phar = new Phar($pharPath);
$phar->startBuffering();
$phar->addFromString(
"bin/greeter.php",
<<<'PHP'
<?php
$nama = $argv[1] ?? "dunia";
echo "Halo, $nama!\n";
PHP
);
$phar->setStub("#!/usr/bin/env php\n" . Phar::createDefaultStub("bin/greeter.php"));
$phar->stopBuffering();
echo "phar dibuat: ", basename($pharPath), "\n";
echo "berkas di dalam: ", count($phar), "\n";
exec(
escapeshellarg(PHP_BINARY) . " " . escapeshellarg($pharPath) . " Budi",
$barisOutput,
$exitCode
);
echo $barisOutput[0], "\n";
echo "exit code: $exitCode\n";
unlink($pharPath);Output yang Dihasilkan:
phar dibuat: greeter.phar
berkas di dalam: 1
Halo, Budi!
exit code: 0Mari kita bedah proses pembuatannya:
new Phar($pharPath): membuka (atau membuat) arsip pada jalur tersebut; inilah titik yang ditolak saatphar.readonlymasih1.startBuffering(): menahan penulisan perubahan ke disk sehingga serangkaianaddFromString()tidak menulis berulang kali — arsip besar dibangun jauh lebih cepat.addFromString("bin/greeter.php", ...): menambahkan satu berkas virtual dari string; pada tool nyata Anda memanggil ini dalam loop untuk seluruh berkas, atau memakaibuildFromDirectory()untuk memindai direktori sekaligus.setStub(...): stub adalah kode PHP kecil yang dijalankan PHP saat phar dipanggil. Awalan#!/usr/bin/env phpmembuat berkas juga bisa dijalankan langsung dari shell, danPhar::createDefaultStub("bin/greeter.php")mengarahkan eksekusi ke skrip utama di dalam arsip.stopBuffering(): menuntaskan seluruh perubahan dan menutup arsip dengan benar.exec(...): menguji hasil dengan memanggilphp greeter.phar Budilewat binary PHP yang sama (PHP_BINARY) — barisHalo, Budi!danexit code: 0membuktikan arsip berjalan lengkap dengan argumennya.
Menjalankan Phar seperti Program Biasa #
Pengguna menjalankan phar dengan dua cara: php greeter.phar Budi, atau — setelah bit eksekusi diaktifkan — langsung ./greeter.phar Budi berkat shebang pada stub:
chmod +x greeter.phar
./greeter.phar BudiBaris pertama mengaktifkan bit eksekusi berkas, dan baris kedua menjalankannya langsung; output-nya sama dengan hasil pengujian pada contoh build di atas. Untuk pemakaian sehari-hari, pengguna menyalin berkas ini ke direktori yang sudah ada di PATH seperti ~/.local/bin atau /usr/local/bin, dan tool Anda resmi menjadi perintah biasa.
Konvensi –version #
Tool yang terdistribusi ke banyak orang harus bisa menjawab satu pertanyaan dengan cepat: versi berapa yang sedang berjalan. Konvensi lintas bahasa adalah flag --version (atau -v) yang mencetak nama program dan nomor versi lalu keluar dengan kode sukses. Polanya sederhana dan wajib konsisten:
<?php
const VERSI = "1.2.0";
$argumen = array_slice($argv, 1);
if (in_array("--version", $argumen, true)) {
echo "impor-tugas ", VERSI, "\n";
exit(0);
}
echo "impor-tugas ", VERSI, " — memproses ", $argumen[0] ?? "data.csv", "\n";Output yang Dihasilkan:
impor-tugas 1.2.0 — memproses data.csvSaat dijalankan tanpa argumen (seperti di atas), tool memproses berkas default; saat pengguna menambahkan --version, alur normal dilewati dan identitas program yang dicetak. Versi disimpan sebagai konstanta tunggal agar tidak pernah beda nilai antara flag ini, log, dan laporan bug. Dari terminal, hasilnya terlihat seperti ini:
php impor-tugas.php --versionOutput yang Dihasilkan:
Pemasangan Global dengan Composer #
Jika tool Anda adalah paket Composer yang diterbitkan (ke Packagist atau repositori privat), pengguna bisa memasangnya secara global:
composer global require budi/impor-tugasComposer meng-clone paket beserta dependensinya ke direktori vendor global, lalu menautkan berkas yang terdaftar di direktif bin ke folder biner global (umumnya ~/.composer/vendor/bin atau ~/.local/composer/vendor/bin). Pengguna cukup memastikan folder itu tercantum di PATH — biasanya sekali seumur mesin — dan impor-tugas menjadi perintah yang tersedia di mana saja. Kelemahan pendekatan ini adalah seluruh paket global berbagi satu proyek Composer, sehingga dua tool dengan dependensi yang bertentangan bisa saling mengganggu.
Paket Composer dan Direktif bin #
Untuk tim yang proyeknya sudah memakai Composer, cara paling bersih adalah memasang tool sebagai dependensi proyek, bukan global. Kuncinya ada pada direktif bin di composer.json paket tool Anda:
{
"name": "budi/impor-tugas",
"description": "Impor berkas CSV besar ke database dengan batching",
"require": {
"php": ">=8.2"
},
"bin": ["bin/impor-tugas"]
}Saat paket ini diminta sebagai dependensi, Composer menautkan bin/impor-tugas ke vendor/bin/ milik proyek pemakai:
composer require --dev budi/impor-tugas
vendor/bin/impor-tugas data.csvBerbeda dengan pemasangan global, setiap proyek mendapatkan salinan dan versi tool sendiri — dua proyek bisa memakai versi berbeda tanpa saling ganggu, dan composer.lock ikut mengunci versi tool seperti dependensi lain. Banyak tim menambahkan vendor/bin ke skrip Composer (misalnya pada scripts.post-autoload-dump) agar pemanggilan lebih pendek.
Homebrew Tap untuk macOS #
Pengguna macOS terbiasa memasang segalanya lewat Homebrew, dan PHP tidak terkecuali. Tap adalah repositori formula Homebrew milik Anda sendiri: satu repositori Git berisi formula — berkas Ruby yang mendeskripsikan dari mana berkas diunduh, cara memasangnya, dan dependensi apa yang diperlukan (termasuk php bila perlu). Setelah tap didaftarkan, pemakaian mengikuti idiom Homebrew:
brew tap budi/tools
brew install impor-tugasFormula yang baik mengunduh rilis stabil (tag rilis di GitHub, atau berkas phar hasil build), memasangnya ke bin/ Homebrew, dan menyatakan dependensi runtime. Menyiapkan tap memang kerja ekstra, tetapi membalik situasinya: pengguna tidak perlu tahu tool Anda dibangun dengan PHP — Homebrew yang memastikan runtime-nya ada.
README sebagai Kontrak #
Distribusi terakhir yang memutuskan apakah tool dipakai atau ditinggalkan bukan berkas, melainkan halaman depan: README. README minimal menjawab empat hal — apa tool ini, syarat apa yang dibutuhkan, bagaimana memasangnya, dan bagaimana memakainya — lengkap dengan contoh perintah yang bisa disalin-tempel:
# impor-tugas
Impor berkas CSV besar ke database dengan batching dan laporan progres.
## Syarat
- PHP 8.2 atau lebih baru
- Ekstensi pdo_sqlite (default) atau pdo_mysql untuk target MySQL
## Pemakaian
impor-tugas <berkas.csv> [--batch-size=N] [--verbose]
## Contoh
impor-tugas data-penjualan.csv --batch-size=500Satu kebiasaan yang membedakan README bagus dari yang buruk: setiap contoh perintah adalah perintah yang benar-benar dijalankan dan diuji, bukan tiruan yang mulai basi setelah API berubah. Karena --version sudah kita siapkan, README juga sebaiknya mencantumkan versi terkini dan tautan ke changelog.
Ringkasan #
Phar mengemas tool beserta dependensinya menjadi satu berkas yang dijalankan langsung; Composer menanganinya lewat jalur global maupun per-proyek dengan direktif bin; Homebrew tap membungkus semuanya dalam idiom paket macOS; dan --version plus README menjaga pengalaman pengguna tetap terkendali. Dengan ini perjalanan kita membangun tool CLI selesai — mulai dari skrip sederhana, argument, stream, exit code, console application, hingga siap distribusi. Bekal ini akan langsung terpakai di Section Data Processing
, tempat tool-tool tersebut mengolah CSV, JSON, dan format data nyata lainnya.