Lima bagian terakhir membangun library yang benar-benar layak pakai: API publik yang rapi, suite 30 test PHPUnit yang hijau, dan PHPStan level maksimal yang bersih. Satu hal terakhir yang memisahkan “kode bagus di komputer saya” dari “package yang bisa dipasang siapa pun” adalah proses rilis: manifest composer.json yang lengkap, validasi yang lolos, versi yang ditandai dengan git tag, dan pengiriman ke Packagist. Pada Bagian penutup ini kita menuntaskan seluruh alur tersebut sehingga budi/slugify siap dikonsumsi dunia — atau setidaknya, oleh proyek PHP Anda yang berikutnya.

Manifest Final untuk Publishing #

composer.json di Bagian 1 sudah sah sejak awal, tetapi manifest untuk publishing memuat lebih banyak janji: kata kunci agar mudah dicari, penulis yang dapat dihubungi, tautan dukungan, dan skrip yang memudahkan kontributor. Bandingkan berkas Bagian 1 dengan versi final pada bundle bawah halaman — bidang baru muncul karena sekarang ada orang lain yang membacanya.

Tabel berikut merangkum peran tiap bidang penting:

BidangPeranCatatan
nameidentitas vendor/packagetidak bisa diubah setelah rilis tanpa memecah pengguna
descriptionsatu kalimat di hasil pencarian Packagisttulis untuk calon pengguna, bukan untuk diri sendiri
keywordsistilah pencarianbeberapa kata kunci spesifik lebih berguna daripada banyak kata generik
licensekejelasan hukumMIT/BSD/Apache-2.0 umum untuk library
requirebatas versi PHP dan ekstensilibrary ini sengaja tanpa dependensi sama sekali
autoloadpeta PSR-4sama seperti Bagian 2, tidak berubah
scriptspintasan perintahcomposer test dan composer phpstan untuk kontributor
supporttautan issues dan dokumentasitempat pengguna melapor saat ada masalah

Mari kita periksa manifest final tersebut secara programatik — cek bahwa seluruh bidang wajib hadir:

php
<?php

$manifest = json_decode(
    file_get_contents(__DIR__ . '/composer.json'),
    true,
    flags: JSON_THROW_ON_ERROR
);

$wajib = ['name', 'description', 'type', 'license', 'require', 'autoload'];
foreach ($wajib as $kolom) {
    printf("%-14s : %s\n", $kolom, isset($manifest[$kolom]) ? 'ada' : 'HILANG');
}

printf("%-14s : %s\n", 'nama package', $manifest['name']);
printf("%-14s : %s\n", 'skrip test', $manifest['scripts']['test']);

Output yang Dihasilkan:

text
name           : ada
description    : ada
type           : ada
license        : ada
require        : ada
autoload       : ada
nama package   : budi/slugify
skrip test     : phpunit --testdox tests

Pemeriksaan manual memang menenangkan, tetapi Composer punya pemeriksa resminya sendiri. Dari direktori package, jalankan:

bash
composer validate

Output yang Dihasilkan:

text
./composer.json is valid

Perintah composer validate memeriksa skema, sintaks JSON, konflik bidang, dan — tanpa --no-check-publish — juga kepatuhan terhadap syarat publishing Packagist seperti lisensi yang dikenali. Jadikan perintah ini bagian tetap dari proses rilis; manifest yang tidak lolos validasi adalah manifest yang belum selesai.

Versi dengan Git Tag #

Packagist tidak membaca nomor versi dari composer.json — nomor versi package justru berasal dari tag Git. Saat Anda menandai commit dengan v1.0.0, itulah yang dilihat dunia sebagai versi 1.0.0. Alur rilisnya ringkas:

bash
git add .
git commit -m "Rilis stabil pertama"
git tag -a v1.0.0 -m "Rilis stabil pertama"
git push origin main --tags

Mari kita lihat alur nyatanya pada repositori kecil di direktori sementara, termasuk saat menambah rilis kedua:

text
$ git init slugify && cd slugify
$ echo "slugify v1.0.0" > README.md
$ git add . && git commit -m "Rilis stabil pertama"
$ git tag -a v1.0.0 -m "Rilis stabil pertama"
$ git tag -l
v1.0.0

$ echo "tambah withers" >> README.md
$ git add . && git commit -m "Perbaikan minor"
$ git tag -a v1.1.0 -m "Penambahan fitur minor"
$ git tag -l
v1.0.0
v1.1.0
$ git describe --tags
v1.1.0

Dua tag hidup berdampingan; Packagist kemudian menyediakan keduanya sehingga pengguna dapat memilih budi/slugify:^1.0 maupun ^1.1. Urutan git describe --tags yang mengembalikan v1.1.0 menegaskan bahwa posisi terkini repositori berada tepat di rilis terbaru.

Kapan naikkan angka yang mana? Semantiknya sudah kita bahas tuntas di chapter Maintaining Backward Compatibility ; berikut alat bantu kecilnya:

php
<?php

function versiBerikutnya(string $versi, string $jenis): string
{
    [$mayor, $minor, $patch] = array_map('intval', explode('.', $versi));

    return match ($jenis) {
        'mayor'  => ($mayor + 1) . '.0.0',
        'minor'  => $mayor . '.' . ($minor + 1) . '.0',
        'patch'  => $mayor . '.' . $minor . '.' . ($patch + 1),
        default  => throw new InvalidArgumentException("Jenis rilis tidak dikenal: $jenis"),
    };
}

echo versiBerikutnya('1.0.0', 'patch'), "\n";
echo versiBerikutnya('1.0.0', 'minor'), "\n";
echo versiBerikutnya('1.4.2', 'mayor'), "\n";

Output yang Dihasilkan:

text
1.0.1
1.1.0
2.0.0

Perbaikan bug pada generate() yang tidak mengubah API berarti rilis patch. Penambahan fitur baru seperti method penamaan alternatif berarti rilis minor. Mengubah tanda tangan generate() atau memindahkan namespace berarti rilis mayor — dan layak dipikirkan dua kali, karena mayor baru memaksa pengguna menyunting constraint mereka.

Publikasi ke Packagist #

Dengan manifest valid dan tag terdorong, langkah publikasi tinggal tiga: buka packagist.org , masuk dengan akun GitHub, kirim URL repositori (https://github.com/budi/slugify), lalu tekan Check. Packagist memindai tag, membaca composer.json dari tiap tag, dan sejak itu composer require budi/slugify bekerja untuk siapa pun. Aktifkan GitHub webhook pada halaman package agar setiap git push --tags memicu pembaruan otomatis, atau setidaknya tekan tombol Update secara manual setiap rilis.

Dua berkas pelengkap yang wajar ditambahkan sebelum rilis pertama: README.md dengan contoh penggunaan (halaman depan package Anda, tautannya tercantum di bidang support.docs), serta .gitattributes yang mengecualikan berkas non-produksi dari arsip dist:

text
/tests export-ignore
/phpstan.neon export-ignore
/.gitattributes export-ignore

Dengan export-ignore, pengguna yang menjalankan composer require tetap mengunduh arsip ringkas tanpa test dan konfigurasi pengembangan — sementara clone Git tetap memuat semuanya untuk kontributor. Arsip yang diunduh Composer itulah yang disebut dist; perbedaan dist dan source sudah kita bahasa di chapter Membuat Library PHP .

Checklist Rilis #

Rangkuman proses yang bisa ditempel di repositori Anda:

LangkahPerintah / TindakanGerbang
1composer validatemanifest sah untuk publish
2composer phpstan (skrip Bagian 5)analisis level max bersih
3composer test (skrip Bagian 4)seluruh suite hijau
4perbarui CHANGELOG.mdsetiap perubahan tercatat
5git tag -a vX.Y.Z -m "..."versi sesuai semantik
6git push origin main --tagsrilis terdorong
7tekan Update di Packagist (atau webhook)versi tersedia

Apa yang Berubah di Bagian Ini #

Bagian 6 tidak menambah satu baris kode library pun — SlugGenerator final tetap yang dari Bagian 3, dijaga test dari Bagian 4 dan PHPStan dari Bagian 5. Yang berubah adalah statusnya: manifest dituntaskan hingga lolos composer validate, alur tag-versi dipraktikkan nyata, dan seluruh jalan menuju Packagist terpetakan. Berkas bundle bawah halaman berisi composer.json final yang siap dipakai apa adanya.

Ringkasan Proyek #

Enam bagian mengubah satu ide kecil — “pembuat slug yang rapi” — menjadi package yang utuh: struktur package dan manifest di Bagian 1, namespace PSR-4 dengan autoloading di Bagian 2, API pubik yang tervalidasi dan imutable di Bagian 3, suite 30 test PHPUnit di Bagian 4, gerbang PHPStan level maksimal di Bagian 5, dan kini alur rilis yang lengkap. Pola yang sama berlaku untuk library apa pun yang akan Anda tulis: mulai dari masalah kecil yang sering Anda temui, kunci kontraknya dengan test dan analisis statis, lalu rilis dengan disiplin versi. Ketika suatu hari composer why menunjukkan package Anda sebagai dependensi proyek orang lain, seluruh proses ini terasa sepadan.

Berkas composer.json pada bagian bawah halaman adalah berkas manifest final yang lolos validasi — bukti bahwa package ini siap dipublikasikan kapan pun Anda memutuskan untuk menekan tombol kirimnya.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

composer.json

Unduh berkas
json
{
    "name": "budi/slugify",
    "description": "Generate URL-friendly slugs from any title: configurable separator, lowercase mode, and length limit.",
    "type": "library",
    "license": "MIT",
    "keywords": ["slug", "slugify", "url", "seo", "seo-friendly"],
    "authors": [
        {
            "name": "Budi",
            "email": "[email protected]",
            "homepage": "https://example.com/budi",
            "role": "Developer"
        }
    ],
    "require": {
        "php": "^8.1"
    },
    "require-dev": {
        "phpstan/phpstan": "^2.3",
        "phpunit/phpunit": "^10.5 || ^11.5 || ^12.5"
    },
    "autoload": {
        "psr-4": {
            "App\\Util\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "App\\Util\\Tests\\": "tests/"
        }
    },
    "scripts": {
        "test": "phpunit --testdox tests",
        "phpstan": "phpstan analyse -c phpstan.neon",
        "ci": [
            "@phpstan",
            "@test"
        ]
    },
    "support": {
        "issues": "https://github.com/budi/slugify/issues",
        "source": "https://github.com/budi/slugify",
        "docs": "https://github.com/budi/slugify/blob/main/README.md"
    },
    "config": {
        "sort-packages": true
    }
}