Vendoring

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:#dcfce7

composer.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 direktori vendor/ ke Git. Direktori ini bisa mencapai ratusan MB dan berisi kode yang bisa di-regenerate dengan composer install. Yang penting di-commit adalah composer.json dan composer.lock. Siapapun yang clone repository bisa mendapatkan environment identik dengan menjalankan composer 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"
    }
}
ConstraintArtiContoh yang diizinkan
3.5.0Versi persisHanya 3.5.0
>=3.5.03.5.0 ke atas3.5.0, 3.6.0, 4.0.0, dst.
>=3.5 <4.0Rentang3.5.x, 3.6.x, 3.7.x
^3.5.0Caret: sama MAJOR, MINOR bisa naik3.5.0 – 3.x.x (tidak 4.x)
^3.5Caret shorthand3.5 – 3.x.x
~3.5.0Tilde: PATCH bisa naik saja3.5.0 – 3.5.x (tidak 3.6)
~3.5Tilde shorthand: MINOR bisa naik3.5 – 3.x.x
3.*Wildcard: semua patch 3.x3.0, 3.1, 3.9, dll.
dev-mainBranch Gitbranch 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\UserService dicari di src/UserService.php
  • Kelas Acme\App\Http\Request dicari di src/Http/Request.php
  • src/helpers.php selalu 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 gunakan composer install --no-dev --optimize-autoloader. Flag --no-dev memastikan library debug tidak masuk ke production, dan --optimize-autoloader menghasilkan 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:

ModeCara KerjaPerforma
Default (PSR-4)Traversal direktori saat kelas dipanggil pertama kaliLambat
--optimize-autoloaderClassmap + PSR-4 fallbackCepat
--classmap-authoritativeHanya classmap, tidak ada fallbackTercepat

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.json mendefinisikan kebutuhan (apa dan versi berapa), composer.lock mencatat hasil resolusi (versi persis yang diinstal). Keduanya harus di-commit ke Git; direktori vendor/ tidak.
  • composer install menginstal versi persis dari lock file — gunakan di staging dan production. composer update mencari versi terbaru yang memenuhi constraint dan memperbarui lock file — gunakan hanya saat ingin upgrade.
  • Caret ^ adalah constraint yang paling sering tepat: ^3.0 berarti “3.x yang kompatibel”, menerima bugfix dan fitur baru tapi tidak major version dengan breaking change.
  • require-dev untuk dependensi yang hanya dibutuhkan saat development (testing, linting, debugging). Deploy ke production dengan composer install --no-dev untuk mengecualikannya.
  • PSR-4 autoloading memetakan namespace ke direktori — setelah menambah kelas baru atau mengubah namespace, jalankan composer dump-autoload untuk memperbarui classmap.
  • Optimasi production dengan --optimize-autoloader atau --classmap-authoritative — mengubah PSR-4 traversal menjadi classmap lookup yang jauh lebih cepat.
  • composer audit memeriksa semua dependensi terhadap database kerentanan keamanan — integrasikan ke CI/CD pipeline dan jalankan secara reguler.
  • composer scripts memungkinkan mendefinisikan perintah proyek (test, lint, analyse) dalam composer.json sehingga semua developer menggunakan perintah yang sama.

← Sebelumnya: Regex   Berikutnya: Multithreading →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact