Vendoring #
Sebelum Composer ada, mengelola dependensi PHP dilakukan secara manual — unduh library, taruh di folder tertentu, include satu per satu. Cara ini tidak skalabel: bagaimana memastikan semua developer di tim menggunakan versi yang sama? Bagaimana menangani library yang bergantung pada library lain? Composer menyelesaikan semua ini. Ia bukan sekadar package downloader — ia adalah dependency resolver yang memahami constraint versi, mengelola transitive dependency, menghasilkan autoloader PSR-4 yang efisien, dan memastikan setiap environment (development, staging, production) menjalankan kode yang identik bit-by-bit. Artikel ini membahas Composer secara menyeluruh: dari instalasi hingga nuansa composer.lock, version constraint yang benar, manajemen autoloading, dan praktik yang membuat proyek PHP mudah di-maintain jangka panjang.
Cara Kerja Composer #
Composer bekerja dalam dua lapisan yang perlu dipahami:
flowchart TD
A[composer.json\nDeskripsi kebutuhan] --> B[Composer]
B --> C{Apakah composer.lock\nsudah ada?}
C -- Ya --> D[Instal versi PERSIS\nsesuai lock file]
C -- Tidak --> E[Resolve dependency\ngraph dari Packagist]
E --> F[Buat composer.lock\ndengan versi pasti]
F --> D
D --> G[Download ke vendor/]
G --> H[Generate vendor/autoload.php]
H --> I[Siap digunakan]
style A fill:#dbeafe
style F fill:#fef9c3
style H fill:#dcfce7composer.json adalah deskripsi kebutuhan — “saya butuh monolog versi 3.x ke atas”. composer.lock adalah catatan hasil resolusi — “versi persis yang diinstal adalah monolog 3.5.0 beserta semua dependensinya”. Ketika install dijalankan dan lock sudah ada, Composer menggunakan versi yang tercatat di lock tanpa negosiasi ulang. Ini yang menjamin semua environment mendapat kode yang identik.
Instalasi Composer #
Instalasi Global (Direkomendasikan) #
# Linux / macOS
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
chmod +x /usr/local/bin/composer
# Verifikasi
composer --version
# Composer version 2.x.x
Instalasi di Windows #
Unduh dan jalankan Composer-Setup.exe dari getcomposer.org. Installer akan otomatis menambahkan Composer ke PATH sehingga bisa dipanggil dari Command Prompt manapun.
Cek dan Update Composer #
# Cek versi yang terinstal
composer --version
# Update ke versi terbaru
composer self-update
# Update ke versi stabil tertentu
composer self-update 2.7.0
# Rollback jika ada masalah
composer self-update --rollback
Struktur Proyek dengan Composer #
Setelah Composer diinisialisasi, struktur direktori proyek khas PHP terlihat seperti ini:
my-project/
├── composer.json ← definisi dependensi — commit ke Git
├── composer.lock ← versi persis yang diinstal — commit ke Git
├── vendor/ ← semua library terinstal — JANGAN commit ke Git
│ ├── autoload.php ← entry point autoloading
│ ├── composer/ ← metadata internal Composer
│ ├── monolog/monolog/ ← library monolog
│ └── guzzlehttp/guzzle/ ← library guzzle
├── src/ ← kode sumber aplikasimu
│ └── App/
│ └── UserService.php
├── tests/ ← unit test
└── index.php ← entry point aplikasi
File .gitignore yang tepat:
vendor/
.env
Jangan commit direktorivendor/ke Git. Direktori ini bisa mencapai ratusan MB dan berisi kode yang bisa di-regenerate dengancomposer install. Yang penting di-commit adalahcomposer.jsondancomposer.lock. Siapapun yang clone repository bisa mendapatkan environment identik dengan menjalankancomposer install.
composer.json — Anatomi Lengkap
#
{
"name": "acme/toko-online",
"description": "Aplikasi toko online berbasis PHP",
"type": "project",
"license": "MIT",
"authors": [
{
"name": "Budi Santoso",
"email": "[email protected]"
}
],
"minimum-stability": "stable",
"prefer-stable": true,
"require": {
"php": "^8.2",
"monolog/monolog": "^3.0",
"guzzlehttp/guzzle": "^7.0",
"vlucas/phpdotenv": "^5.6",
"ramsey/uuid": "^4.0"
},
"require-dev": {
"phpunit/phpunit": "^11.0",
"fakerphp/faker": "^1.23",
"phpstan/phpstan": "^1.10",
"squizlabs/php_codesniffer": "^3.0"
},
"autoload": {
"psr-4": {
"Acme\\TokoOnline\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Acme\\TokoOnline\\Tests\\": "tests/"
}
},
"scripts": {
"test": "phpunit --testdox",
"test:coverage": "phpunit --coverage-html coverage/",
"lint": "phpcs src/ tests/ --standard=PSR12",
"analyse": "phpstan analyse src/ --level=8",
"post-install-cmd": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-update-cmd": [
"composer dump-autoload --optimize"
]
},
"config": {
"sort-packages": true,
"preferred-install": "dist",
"optimize-autoloader": true
},
"extra": {
"branch-alias": {
"dev-main": "1.0-dev"
}
}
}
Version Constraint — Menentukan Versi yang Diizinkan #
Memahami version constraint adalah salah satu bagian terpenting Composer. Constraint yang salah bisa membuat update dependensi menjadi tidak mungkin atau menginstall versi yang tidak kompatibel.
PHP menggunakan Semantic Versioning (semver): MAJOR.MINOR.PATCH. Perubahan MAJOR mungkin breaking, MINOR menambah fitur backward-compatible, PATCH hanya bugfix.
{
"require": {
"vendor/package": "versi"
}
}
| Constraint | Arti | Contoh yang diizinkan |
|---|---|---|
3.5.0 | Versi persis | Hanya 3.5.0 |
>=3.5.0 | 3.5.0 ke atas | 3.5.0, 3.6.0, 4.0.0, dst. |
>=3.5 <4.0 | Rentang | 3.5.x, 3.6.x, 3.7.x |
^3.5.0 | Caret: sama MAJOR, MINOR bisa naik | 3.5.0 – 3.x.x (tidak 4.x) |
^3.5 | Caret shorthand | 3.5 – 3.x.x |
~3.5.0 | Tilde: PATCH bisa naik saja | 3.5.0 – 3.5.x (tidak 3.6) |
~3.5 | Tilde shorthand: MINOR bisa naik | 3.5 – 3.x.x |
3.* | Wildcard: semua patch 3.x | 3.0, 3.1, 3.9, dll. |
dev-main | Branch Git | branch main |
# Contoh penggunaan constraint dalam perintah
composer require monolog/monolog "^3.0" # MAJOR 3, MINOR bebas
composer require guzzlehttp/guzzle "~7.5" # 7.5.x saja
composer require vlucas/phpdotenv "*" # versi apapun (tidak direkomendasikan)
Praktik Terbaik Version Constraint #
✓ Gunakan caret ^MAJOR.MINOR untuk sebagian besar library
→ ^3.0 berarti "3.x yang kompatibel", aman menerima bugfix dan fitur baru
✓ Gunakan tilde ~MAJOR.MINOR.PATCH jika library punya breaking change di minor version
→ ~3.5.0 berarti "hanya patch update"
✗ Hindari constraint terlalu ketat (3.5.0 persis)
→ Tidak mendapat bugfix dan security patch
✗ Hindari constraint terlalu longgar (>=3.0 atau *)
→ Bisa menginstal versi dengan breaking change
Perintah Composer yang Paling Sering Digunakan #
Inisialisasi dan Instalasi #
# Buat composer.json interaktif
composer init
# Instal semua dependensi (gunakan lock file jika ada)
composer install
# Instal TANPA dependensi development (untuk production)
composer install --no-dev --optimize-autoloader
# Tambah dependensi baru
composer require vendor/package
composer require vendor/package "^2.0"
# Tambah dependensi development
composer require --dev phpunit/phpunit
Update Dependensi #
# Update SEMUA dependensi (update composer.lock)
composer update
# Update hanya satu package
composer update vendor/package
# Cek apakah ada update yang tersedia
composer outdated
# Cek hanya update yang merupakan security fix
composer audit
Autoloading dan Diagnostik #
# Regenerate autoloader (setelah tambah namespace baru)
composer dump-autoload
# Optimasi autoloader untuk production
composer dump-autoload --optimize
composer dump-autoload -o
# Cek dependencies secara grafis
composer show
composer show vendor/package # info detail satu package
composer show --tree # tampilkan dependency tree
# Validasi composer.json
composer validate
Menghapus Dependensi #
# Hapus package dan update composer.json + lock
composer remove vendor/package
composer remove --dev phpunit/phpunit
Autoloading PSR-4 #
Composer menghasilkan autoloader yang memuat kelas PHP secara otomatis — tidak perlu require atau include manual untuk setiap file. Standar yang digunakan adalah PSR-4 yang memetakan namespace ke direktori.
Konfigurasi Autoloading #
{
"autoload": {
"psr-4": {
"Acme\\App\\": "src/",
"Acme\\Core\\": "core/"
},
"files": [
"src/helpers.php"
],
"classmap": [
"legacy/"
]
}
}
Dengan konfigurasi di atas:
- Kelas
Acme\App\UserServicedicari disrc/UserService.php - Kelas
Acme\App\Http\Requestdicari disrc/Http/Request.php src/helpers.phpselalu di-load (untuk fungsi global)- Semua kelas di direktori
legacy/di-scan dan di-map
Struktur Direktori yang Benar #
src/
├── UserService.php → namespace Acme\App;
├── Http/
│ ├── Request.php → namespace Acme\App\Http;
│ └── Response.php → namespace Acme\App\Http;
├── Repository/
│ └── UserRepository.php → namespace Acme\App\Repository;
└── Exception/
└── NotFoundException.php → namespace Acme\App\Exception;
<?php
// src/UserService.php
namespace Acme\App;
use Acme\App\Repository\UserRepository;
use Acme\App\Exception\NotFoundException;
class UserService
{
public function __construct(
private UserRepository $repo
) {}
public function findOrFail(int $id): array
{
return $this->repo->find($id)
?? throw new NotFoundException("User $id tidak ditemukan");
}
}
<?php
// index.php — cukup require satu file ini
require __DIR__ . '/vendor/autoload.php';
use Acme\App\UserService;
use Acme\App\Repository\UserRepository;
// Semua kelas di-load otomatis saat pertama kali digunakan
$service = new UserService(new UserRepository());
$user = $service->findOrFail(1);
require vs require-dev
#
require adalah dependensi yang dibutuhkan di semua environment (development dan production). require-dev adalah dependensi yang hanya dibutuhkan saat development — testing, linting, debugging.
{
"require": {
"php": "^8.2",
"monolog/monolog": "^3.0",
"guzzlehttp/guzzle": "^7.0"
},
"require-dev": {
"phpunit/phpunit": "^11.0",
"fakerphp/faker": "^1.23",
"phpstan/phpstan": "^1.10",
"barryvdh/laravel-debugbar": "^3.0"
}
}
# Development — instal semua termasuk require-dev (default)
composer install
# Production — skip require-dev, lebih ringan dan aman
composer install --no-dev
# Jika terlanjur instal dengan dev, bersihkan untuk production
composer install --no-dev --optimize-autoloader
Di pipeline CI/CD untuk deployment ke production, selalu gunakancomposer install --no-dev --optimize-autoloader. Flag--no-devmemastikan library debug tidak masuk ke production, dan--optimize-autoloadermenghasilkan classmap yang jauh lebih cepat dari PSR-4 traversal.
Scripts Composer #
Section scripts di composer.json memungkinkan mendefinisikan perintah yang bisa dijalankan dengan composer run-script atau shorthand composer nama-script:
{
"scripts": {
"test": "phpunit --testdox --colors=always",
"test:unit": "phpunit --testsuite=Unit",
"test:integration": "phpunit --testsuite=Integration",
"test:coverage": "phpunit --coverage-html coverage/",
"lint": "phpcs src/ tests/ --standard=PSR12",
"lint:fix": "phpcbf src/ tests/ --standard=PSR12",
"analyse": "phpstan analyse src/ tests/ --level=8",
"check": [
"@lint",
"@analyse",
"@test"
],
"post-install-cmd": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\"",
"composer dump-autoload"
],
"post-update-cmd": "@composer dump-autoload --optimize",
"post-create-project-cmd": [
"@php artisan key:generate"
]
}
}
# Jalankan script yang didefinisikan
composer test
composer lint:fix
composer check # menjalankan lint, analyse, dan test secara berurutan
# Script lifecycle yang otomatis dijalankan
# post-install-cmd → setelah composer install
# post-update-cmd → setelah composer update
# pre-install-cmd → sebelum composer install
composer.lock — Mengapa Sangat Penting
#
composer.lock menyimpan versi persis dari setiap package yang terinstal — termasuk semua transitive dependency (dependensi dari dependensi). File ini memastikan semua orang di tim dan semua environment menjalankan kode yang benar-benar identik.
{
"_readme": [...],
"content-hash": "abc123...",
"packages": [
{
"name": "monolog/monolog",
"version": "3.5.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
"reference": "e5c62b8a58f842c5..."
},
"dist": {
"type": "zip",
"url": "https://api.github.com/...",
"shasum": "..."
},
"require": {
"php": ">=8.1",
"psr/log": "^2.0 || ^3.0"
}
}
],
"packages-dev": [...],
"platform": {
"php": "8.2"
}
}
Kapan Update Lock File #
composer install → TIDAK update lock, instal persis sesuai lock
composer update → UPDATE lock dengan versi terbaru yang memenuhi constraint
Kapan jalankan composer update:
✓ Sengaja ingin upgrade dependensi (dan sudah siap test regresi)
✓ Update security patch untuk package tertentu
✓ Setelah ubah constraint di composer.json
Jangan jalankan composer update di production — gunakan composer install saja
Optimasi untuk Production #
# Perintah lengkap untuk deployment production
composer install \
--no-dev \
--no-interaction \
--prefer-dist \
--optimize-autoloader \
--classmap-authoritative
# Penjelasan flag:
# --no-dev → skip require-dev
# --no-interaction → tidak ada prompt (cocok untuk CI/CD)
# --prefer-dist → unduh zip, lebih cepat dari git clone
# --optimize-autoloader → buat classmap dari PSR-4 (lebih cepat)
# --classmap-authoritative → hanya cari kelas yang ada di classmap (tercepat)
Perbedaan performa autoloader:
| Mode | Cara Kerja | Performa |
|---|---|---|
| Default (PSR-4) | Traversal direktori saat kelas dipanggil pertama kali | Lambat |
--optimize-autoloader | Classmap + PSR-4 fallback | Cepat |
--classmap-authoritative | Hanya classmap, tidak ada fallback | Tercepat |
Anti-Pattern Composer yang Sering Ditemui #
# ✗ Anti-pattern 1: commit direktori vendor/
git add vendor/
# Solusi: tambahkan vendor/ ke .gitignore
# ✗ Anti-pattern 2: jalankan composer update di production
ssh production "composer update"
# Solusi: composer update hanya di development, commit lock file, deploy dengan composer install
# ✗ Anti-pattern 3: tidak commit composer.lock
echo "composer.lock" >> .gitignore
# Solusi: SELALU commit composer.lock — ini yang menjamin reproducible build
# ✗ Anti-pattern 4: constraint terlalu longgar
"require": { "vendor/package": "*" }
# Solusi: selalu tentukan constraint minimum seperti "^3.0"
# ✗ Anti-pattern 5: mix require dan require-dev
"require": {
"phpunit/phpunit": "^11.0" # test library di require production!
}
# Solusi: library testing, debugging, linting selalu masuk require-dev
# ✗ Anti-pattern 6: tidak jalankan composer audit secara reguler
# Solusi: tambahkan ke CI pipeline
# composer audit — cek kerentanan keamanan di semua dependensi
composer audit
# Package monolog/monolog (3.4.0) is affected by CVE-2024-XXXX...
# Integrasikan ke CI/CD pipeline
composer audit --no-interaction --format=json
Mencari Package di Packagist #
Packagist.org adalah repository resmi package PHP. Cara mencari dan mengevaluasi package:
# Cari package dari terminal
composer search monolog
# Lihat detail package
composer show monolog/monolog
composer show monolog/monolog --all # info lengkap termasuk semua versi
Kriteria evaluasi package yang baik sebelum menambahkannya ke proyek:
- Download mingguan yang cukup tinggi (popularitas)
- Maintenance aktif — kapan commit terakhir?
- Jumlah bintang dan open issues di GitHub
- Apakah punya test suite yang baik?
- Apakah mendukung versi PHP yang kamu gunakan?
- Lisensi yang kompatibel dengan proyekmu
Ringkasan #
composer.jsonmendefinisikan kebutuhan (apa dan versi berapa),composer.lockmencatat hasil resolusi (versi persis yang diinstal). Keduanya harus di-commit ke Git; direktorivendor/tidak.composer installmenginstal versi persis dari lock file — gunakan di staging dan production.composer updatemencari versi terbaru yang memenuhi constraint dan memperbarui lock file — gunakan hanya saat ingin upgrade.- Caret
^adalah constraint yang paling sering tepat:^3.0berarti “3.x yang kompatibel”, menerima bugfix dan fitur baru tapi tidak major version dengan breaking change.require-devuntuk dependensi yang hanya dibutuhkan saat development (testing, linting, debugging). Deploy ke production dengancomposer install --no-devuntuk mengecualikannya.- PSR-4 autoloading memetakan namespace ke direktori — setelah menambah kelas baru atau mengubah namespace, jalankan
composer dump-autoloaduntuk memperbarui classmap.- Optimasi production dengan
--optimize-autoloaderatau--classmap-authoritative— mengubah PSR-4 traversal menjadi classmap lookup yang jauh lebih cepat.composer auditmemeriksa semua dependensi terhadap database kerentanan keamanan — integrasikan ke CI/CD pipeline dan jalankan secara reguler.composer scriptsmemungkinkan mendefinisikan perintah proyek (test, lint, analyse) dalamcomposer.jsonsehingga semua developer menggunakan perintah yang sama.