Dependensi proyek tidak berhenti pada paket-paket dari Packagist. Aplikasi Anda juga bergantung pada lingkungan tempat aplikasi tersebut berjalan: versi PHP tertentu, ekstensi PDO untuk database, mbstring untuk teks multibyte. Bila syarat-syarat itu tidak dituliskan, kegagalan baru muncul di server produksi sebagai fatal error di tengah request — jauh dari mesin pengembang dan jauh lebih mahal diperbaiki. Composer menyelesaikan masalah ini dengan memperlakukan lingkungan sebagai paket virtual yang bisa dikenai kendala versi. Pada chapter ini kita belajar mendeklarasikan platform requirements, memeriksanya dengan composer check-platform-reqs, dan menyematkan target versi produksi lewat config.platform.

Lingkungan sebagai Paket Virtual #

Chapter composer.json sempat memperlihatkan entri "php": ">=8.1" dan "ext-json": "*" di field require. Kedua entri tersebut bukan paket dari Packagist, melainkan platform package: nama semu untuk komponen lingkungan itu sendiri. Composer mengenal tiga jenisnya — php untuk versi runtime, ext-* untuk ekstensi PHP, dan lib-* untuk pustaka sistem seperti lib-icu. Saat resolusi dependensi berjalan, Composer mencocokkan platform nyata di mesin dengan seluruh kendala platform; bila ada yang tidak terpenuhi, resolusi langsung gagal.

Berikut deklarasi yang lazim untuk aplikasi web modern:

json
{
    "require": {
        "php": ">=8.1",
        "ext-mbstring": "*",
        "ext-pdo": "*"
    }
}

Kendala ">=8.1" berarti runtime harus PHP 8.1 ke atas, sementara "*" pada ekstensi berarti versi ekstensi apa pun diterima — kita hanya peduli ekstensinya ada. PHP sendiri menyediakan alat pemeriksa yang setara dari dalam kode, dan hasilnya persis yang dibaca Composer:

php
<?php
$ekstensiWajib = ['pdo', 'mbstring', 'json', 'intl'];

foreach ($ekstensiWajib as $ekstensi) {
    $status = extension_loaded($ekstensi) ? 'tersedia' : 'TIDAK ADA';
    printf("ext-%-10s %s\n", $ekstensi, $status);
}

Output yang Dihasilkan:

text
ext-pdo        tersedia
ext-mbstring   tersedia
ext-json       tersedia
ext-intl       TIDAK ADA

Fungsi extension_loaded() memeriksa keberadaan ekstensi berdasarkan namanya tanpa awalan ext-. Format %-10s pada printf() meratakan kolom ke lebar sepuluh karakter sehingga status sejajar rapi. Mesin uji chapter ini memang tidak memiliki ext-intl — kondisi yang akan kita manfaatkan untuk melihat Composer menolak bekerja.

Untuk versi runtime, konstanta PHP_VERSION dan PHP_VERSION_ID memberi angka yang sama dengan yang dibaca Composer dari platform package php:

php
<?php
echo 'Versi PHP saat ini : ' . PHP_VERSION . "\n";
echo 'PHP_VERSION_ID     : ' . PHP_VERSION_ID . "\n";
echo 'Memenuhi >=8.1     : ' . var_export(PHP_VERSION_ID >= 80100, true) . "\n";
echo 'Memenuhi >=8.5     : ' . var_export(PHP_VERSION_ID >= 80500, true) . "\n";

Output yang Dihasilkan:

text
Versi PHP saat ini : 8.4.23
PHP_VERSION_ID     : 80423
Memenuhi >=8.1     : true
Memenuhi >=8.5     : false

PHP_VERSION_ID mengkodekan versi sebagai satu integer — 8.4.23 menjadi 80423 — sehingga perbandingan rentang versi cukup membandingkan angka. Nilai false pada baris terakhir memperlihatkan cara Composer menilai kendala yang tidak terpenuhi.

Memeriksa Lingkungan dengan check-platform-reqs #

Deklarasi di composer.json dicek otomatis setiap kali Composer menyelesaikan dependensi. Perintah khusus composer check-platform-reqs memeriksa syarat platform dari proyek tanpa menjalankan resolusi ulang. Kita uji pada proyek kecil platform-demo yang composer.json-nya persis seperti contoh di atas:

bash
cd platform-demo
composer update

Output yang Dihasilkan:

text
Loading composer repositories with package information
Updating dependencies
Nothing to modify in lock file
Writing lock file
Installing dependencies from lock file (including require-dev)
Nothing to install, update or remove
Generating autoload files
No installed packages - skipping audit.

Resolusi berhasil karena mesin uji memiliki PHP 8.4.23, ext-mbstring, dan ext-pdo. Baris terakhir menandakan Composer 2.10 juga menjalankan pemeriksaan advisori keamanan singkat setelah update — fitur yang kita bahas tuntas pada chapter Dependency Auditing . Sekarang perintah pemeriksanya:

bash
composer check-platform-reqs

Output yang Dihasilkan:

text
No vendor dir present, checking platform requirements from the lock file
ext-mbstring 8.4.23   success 
ext-pdo      8.4.23   success 
php          8.4.23   success 

Karena proyek demo ini tidak memasang paket biasa, Composer membaca syarat dari lock file dan mencocokkannya dengan lingkungan nyata. Semua baris berstatus success dan perintah berakhir dengan exit code 0 — sinyal aman untuk dilewatkan di skrip deploy. Bagaimana bila syarat tidak terpenuhi? Kita buat proyek kedua intl-demo yang membutuhkan ext-intl:

json
{
    "require": {
        "php": ">=8.1",
        "ext-intl": "*"
    }
}
bash
cd intl-demo
composer update

Output yang Dihasilkan:

text
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Root composer.json requires PHP extension ext-intl * but it is missing from your system. Install or enable PHP's intl extension.

Perintah berakhir dengan exit code 2 sebelum satu paket pun dipasang. Inilah makna fail fast: kesalahan lingkungan tertangkap di awal, bukan menjadi Error: Class "IntlDateFormatter" not found di produksi pukul dua pagi.

Menyematkan Target Produksi dengan config.platform #

Ada kebalikan dari masalah di atas yang lebih licik: mesin pengembang justru lebih baru daripada produksi. Anda menulis kode di PHP 8.4 sementara server produksi masih 8.1. Tanpa pengaman, Composer dengan senang hati memilih dependensi versi terbaru yang cocok untuk 8.4 — dan aplikasi patah saat di-deploy. Solusinya adalah menyematkan versi target lewat config.platform:

json
{
    "require": {
        "php": ">=8.1",
        "symfony/console": "^7.0"
    },
    "config": {
        "platform": {
            "php": "8.1.0"
        }
    }
}

Dengan pin 8.1.0, Composer berperilaku seolah seluruh perintah berjalan di PHP 8.1.0 — meskipun runtime nyatanya 8.4.23. Kita buktikan di proyek pin-demo:

bash
cd pin-demo
composer update

Output yang Dihasilkan:

text
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Root composer.json requires symfony/console ^7.0 -> satisfiable by symfony/console[v7.0.0, ..., v7.4.20].
    - symfony/console[v7.0.0, ..., v7.4.20] require php >=8.2 -> your php version (8.1.0; overridden via config.platform, actual: 8.4.23) does not satisfy that requirement.

Pesan tersebut justru hasil yang kita inginkan: seluruh rilis 7.x symfony/console mensyaratkan PHP >=8.2, sehingga di target produksi 8.1 kombinasi ini mustahil. Penolakan terjadi di mesin pengembang, lengkap dengan penjelasan overridden via config.platform, actual: 8.4.23 yang mengingatkan kita sedang bermain dengan pin. Agar konsisten, pastikan pipeline CI juga menjalankan versi PHP yang sama dengan produksi — pin yang tidak dijaga oleh CI hanya memindahkan kejutan ke tempat lain.

Melihat Daftar Paket Platform #

Perintah composer show --platform memperlihatkan seluruh platform package yang dikenali Composer pada lingkungan saat ini — versi PHP, puluhan ekstensi, hingga pustaka sistem. Menambahkan nama paket mempersempit hasilnya menjadi satu entri:

bash
composer show --platform php

Output yang Dihasilkan:

text
name     : php
descrip. : The PHP interpreter
keywords : 
versions : * 8.4.23
type     : library
homepage : 
source   : []  
dist     : []  
names    : php

Daftar ini berguna untuk memastikan ejaan ekstensi sebelum menuliskannya ke require — salah ketik ext-multibyte alih-alih ext-mbstring tidak akan pernah cocok dengan platform mana pun. Perlu dicatat bila config.platform aktif, yang ditampilkan adalah versi pin, bukan versi nyata; Composer menandainya dengan keterangan Package overridden via config.platform, actual: 8.4.23.

Ekstensi yang Lazim Dibutuhkan #

Sebagian besar aplikasi PHP berulang kali membutuhkan ekstensi yang sama. Tabel berikut merangkum yang paling sering muncul di field require:

Platform packageKegunaanCatatan
ext-pdolapisan akses databaseperlu driver tambahan seperti ext-pdo_mysql atau ext-pdo_sqlite
ext-mbstringfungsi string multibyte (mb_*)wajib untuk teks non-ASCII — sudah kita praktikkan di Section String
ext-jsonjson_encode() dan json_decode()terbundel dan selalu aktif sejak PHP 8.0
ext-intlinternasionalisasi (locale, kolasi)sering absen di hosting bersama
ext-curlHTTP client berbasis libcurlalternatif: stream context bawaan
ext-zipmembaca dan menulis arsip zipdipakai Composer sendiri untuk dist
ext-gdpemrosesan gambaralternatif: ext-imagick

Ringkasan #

Platform requirements membuat lingkungan ikut tunduk pada kontrak dependensi: php dan ext-* dideklarasikan di require, composer check-platform-reqs memverifikasi proyek terhadap mesin saat ini, dan config.platform menyematkan versi target produksi agar mesin pengembang yang lebih baru tidak menyesatkan resolusi. Gerbang lingkungan ini adalah lapis pertama pertahanan. Lapis berikutnya memeriksa isi dependensi itu sendiri — apakah versi yang terpasang bebas dari advisori keamanan — dan itulah tugas composer audit pada chapter berikutnya.

Referensi #