PHP Modern: Versi 8, Composer, dan Autoload
Dari include satu per satu ke autoload, dari fungsi tanpa tipe ke class yang ketat. Kamu akan membuat program kasir kecil dengan fitur PHP 8 dan Composer.
Oleh Afa Hernandes15 menit baca
Daftar isi
- Sebelum mulai
- Langkah 1: Cek PHP dan pasang Composer
- Langkah 2: Buat proyek dan composer.json
- Langkah 3: Tipe data dan strict_types
- Langkah 4: Class readonly dan named argument
- Langkah 5: enum dan match
- Langkah 6: Keranjang dan format rupiah
- Langkah 7: Paket dari Packagist dan file .env
- Langkah 8: Program kasir
- Langkah 9: Simpan ke Git dengan benar
- Bonus: operator pipe di PHP 8.5
- Kode lengkap
- Error yang sering muncul
- Langkah berikutnya: rilis dan amankan
- Pertanyaan yang sering diajukan
- Apakah kode PHP lama tetap jalan di PHP 8?
- Harus pakai class untuk semuanya?
- Apa bedanya composer install dan composer update?
- Perlu Composer kalau tidak memakai paket apa pun?
- Kesimpulan
Kode PHP native era lama biasanya terlihat seperti ini: belasan baris include 'fungsi.php'; di atas setiap halaman, fungsi tanpa tipe yang menerima apa saja, library yang diunduh dalam bentuk ZIP, dan password database di koneksi.php. Semuanya masih bisa jalan, tapi makin besar aplikasinya, makin sulit dirawat. Lupa satu include, muncul error. Salah kirim teks ke fungsi yang mengharapkan angka, hasilnya ngawur tanpa peringatan.
PHP 8 dan Composer menyelesaikan masalah-masalah itu. Composer mengunduh library dan, yang sama pentingnya, memuat class-mu secara otomatis lewat autoload. PHP 8 menambahkan tipe data yang ketat, enum, match, class readonly, dan beberapa fitur lain yang membuat kode lebih pendek sekaligus lebih aman.
Di tutorial ini kamu akan membuat program kasir kecil untuk kedai Kopi Senja. Programnya berjalan di terminal, dan strukturnya sama dengan proyek PHP modern pada umumnya, termasuk proyek Laravel.
Sebelum mulai
- Sudah pernah menulis PHP, misalnya dari tutorial CRUD PHP MySQLi. Kalau belum tahu kenapa perlu upgrade, baca dulu apa yang berubah sejak era XAMPP.
- PHP 8.3 atau lebih baru. PHP 8.2 hanya mendapat perbaikan keamanan sampai 31 Desember 2026, jadi untuk proyek baru pakai versi yang lebih baru.
- Terminal dan editor. Kalau belum terbiasa, lihat menyiapkan VS Code dan terminal.
- Diuji dengan PHP 8.5 dan Composer 2.10 di Windows 11.
Langkah 1: Cek PHP dan pasang Composer
Buka terminal dan periksa versi PHP:
php -vPHP 8.5.11 (cli) (built: Sep 22 2026 13:50:46) (ZTS Visual C++ 2022 x64)Kalau muncul pesan bahwa php tidak dikenali, berarti folder PHP belum ada di PATH. Di XAMPP, foldernya biasanya C:\xampp\php. Tambahkan folder itu ke PATH lewat Edit the system environment variables, lalu buka terminal baru.
Untuk memasang Composer di Windows, unduh Composer-Setup.exe dari getcomposer.org dan jalankan. Installer akan menanyakan lokasi php.exe, lalu menambahkan Composer ke PATH. Setelah selesai, tutup terminal, buka lagi, lalu cek:
composer --versionComposer version 2.10.3 2026-08-27 13:34:23Kalau kamu memakai Laragon, Composer biasanya sudah tersedia.
Langkah 2: Buat proyek dan composer.json
Buat folder proyek dengan struktur seperti ini:
kasir-kopi/├── bin/│ └── kasir.php├── src/└── composer.jsonFolder src berisi class, dan bin berisi program yang dijalankan. Isi composer.json:
{ "name": "kopisenja/kasir", "description": "Kasir sederhana kedai Kopi Senja", "type": "project", "license": "proprietary", "require": { "php": ">=8.3" }, "autoload": { "psr-4": { "App\\": "src/" } }}Dua bagian yang penting:
"require"mencatat apa saja yang dibutuhkan proyek. Baris"php": ">=8.3"membuat Composer menolak dipasang di PHP yang lebih lama."autoload"dengan"psr-4"adalah aturan pemetaan: class dengan namespaceApp\ada di foldersrc/. Jadi classApp\Produkpasti ada disrc/Produk.php. PSR-4 adalah standar yang dipakai hampir semua proyek PHP modern, termasuk Laravel dan Symfony.
Perintah composer init juga bisa membuat file ini lewat tanya-jawab, tapi menulisnya sendiri membuatmu paham isinya. Lalu jalankan:
composer dump-autoloadGenerating autoload filesGenerated autoload filesComposer membuat folder vendor/ berisi autoload.php. Mulai sekarang, setiap program cukup memuat satu file itu, dan class apa pun di src/ akan dimuat otomatis saat pertama kali dipakai. Tidak ada lagi deretan include.
Kamu hanya perlu menjalankan composer dump-autoload lagi kalau mengubah bagian autoload di composer.json. Menambah file class baru di src/ tidak perlu.
Langkah 3: Tipe data dan strict_types
Sebelum membuat class, lihat dulu perubahan paling penting di PHP modern. Fungsi gaya lama menerima apa saja:
<?phpfunction tambah($a, $b) { return $a + $b; }echo tambah("5", 3), "\n";8Teks "5" diam-diam diubah menjadi angka. Kelihatannya membantu, tapi di aplikasi sungguhan ini menyembunyikan bug: data dari form yang seharusnya divalidasi malah lolos begitu saja. Versi modernnya:
<?phpdeclare(strict_types=1);
function tambah(int $a, int $b): int { return $a + $b; }echo tambah("5", 3), "\n";Fatal error: Uncaught TypeError: tambah(): Argument #1 ($a) must be of type int, string givenint $aartinya parameter$aharus berupa bilangan bulat. Tipe lain yang sering dipakai:string,float,bool,array, dan nama class.): intartinya fungsi ini mengembalikan bilangan bulat.declare(strict_types=1);di baris kedua membuat PHP menolak tipe yang tidak cocok, bukan mengubahnya diam-diam. Baris ini berlaku per file, jadi taruh di setiap file PHP-mu.
Error di sini bagus: kesalahannya ketahuan saat kamu mengetes, bukan saat pelanggan melihat total yang salah. Perbaikannya adalah mengubah data dari luar secara sadar, misalnya (int) $_POST['jumlah'] setelah divalidasi.
Langkah 4: Class readonly dan named argument
Buat class pertama di src/Produk.php:
<?php
declare(strict_types=1);
namespace App;
final readonly class Produk{ public function __construct( public string $nama, public int $harga, public Kategori $kategori, public int $diskon = 0, ) {}
public function hargaAkhir(): int { return intdiv($this->harga * (100 - $this->diskon), 100); }}Banyak hal baru dalam beberapa baris ini:
namespace App;harus cocok dengan aturan PSR-4 tadi. File ini ada disrc/, jadi namespace-nyaApp, dan nama class-nyaProduk, sama dengan nama fileProduk.php.- Constructor promotion. Menulis
public string $namalangsung di parameter constructor sekaligus membuat properti$namadan mengisinya. Di PHP lama, ini butuh tiga bagian terpisah: deklarasi properti, parameter, lalu$this->nama = $nama;. readonlyartinya setelah objek dibuat, propertinya tidak bisa diubah. Harga produk di keranjang tidak bisa berubah diam-diam di tengah perhitungan.finalartinya class ini tidak bisa diturunkan. Ini kebiasaan yang baik: buka pewarisan hanya kalau memang dibutuhkan.intdiv()membagi bilangan bulat dan membuang sisanya. Harga rupiah disimpan sebagai bilangan bulat, bukan desimal, supaya tidak ada masalah pembulatan.
Kalau ada kode yang mencoba mengubah harga:
$teh = new Produk('Teh', 8000, Kategori::Minuman);$teh->harga = 1000;Fatal error: Uncaught Error: Cannot modify readonly property App\Produk::$hargaSaat membuat objek, kamu juga bisa memakai named argument: menyebut nama parameternya.
$croissant = new Produk( nama: 'Croissant', harga: 25000, kategori: Kategori::Makanan, diskon: 20,);Ini jauh lebih jelas daripada new Produk('Croissant', 25000, Kategori::Makanan, 20), karena kamu tidak perlu menebak angka 20 itu artinya apa.
Langkah 5: enum dan match
Kategori produk hanya boleh “minuman” atau “makanan”. Dulu biasanya ditulis sebagai string biasa, sehingga salah ketik seperti 'minumam' lolos tanpa error. Sekarang ada enum:
<?php
declare(strict_types=1);
namespace App;
enum Kategori: string{ case Minuman = 'minuman'; case Makanan = 'makanan';
public function label(): string { return match ($this) { Kategori::Minuman => 'Minuman', Kategori::Makanan => 'Makanan', }; }}enum Kategori: stringmembuat tipe baru dengan pilihan nilai yang tetap. Setiapcasepunya nilai string, yang cocok untuk disimpan di kolom database.matchadalah penggantiswitchyang lebih ketat. Ia mengembalikan nilai, membandingkan dengan===, tidak butuhbreak, dan melempar error kalau ada nilai yang tidak ditangani.
Nilai dari database atau form bisa diubah menjadi enum dengan from() atau tryFrom():
<?phpdeclare(strict_types=1);require __DIR__ . '/../vendor/autoload.php';
use App\Kategori;
var_dump(Kategori::from('minuman'));var_dump(Kategori::tryFrom('snack'));Kategori::from('snack');enum(App\Kategori::Minuman)NULL
Fatal error: Uncaught ValueError: "snack" is not a valid backing value for enum App\KategoritryFrom() mengembalikan null kalau nilainya tidak dikenal, cocok untuk input pengguna yang perlu dicek dulu. from() langsung melempar error, cocok untuk data yang seharusnya selalu benar.
Langkah 6: Keranjang dan format rupiah
Dua class lagi. Pertama, format rupiah:
<?php
declare(strict_types=1);
namespace App;
final class Rupiah{ public static function format(int $nilai): string { return 'Rp' . number_format($nilai, 0, ',', '.'); }}Lalu keranjang belanja:
<?php
declare(strict_types=1);
namespace App;
final class Keranjang{ /** @var array<int, array{produk: Produk, jumlah: int}> */ private array $items = [];
public function tambah(Produk $produk, int $jumlah = 1): void { if ($jumlah < 1) { throw new \InvalidArgumentException('Jumlah minimal 1.'); } $this->items[] = ['produk' => $produk, 'jumlah' => $jumlah]; }
/** @return array<int, array{produk: Produk, jumlah: int}> */ public function items(): array { return $this->items; }
public function total(): int { return array_sum(array_map( fn (array $item): int => $item['produk']->hargaAkhir() * $item['jumlah'], $this->items, )); }}private array $itemshanya bisa diubah dari dalam class. Kode di luar harus lewattambah(), yang memeriksa jumlahnya dulu. Ini cara sederhana menjaga data tetap valid.- Komentar
/** @var ... */tidak dibaca PHP, tapi dibaca editor dan alat analisis kode. Isinya menjelaskan bentuk array: setiap item berisiprodukdanjumlah. fn (...) => ...adalah arrow function, fungsi pendek satu ekspresi.array_mapmenjalankannya untuk setiap item, laluarray_summenjumlahkan hasilnya.\InvalidArgumentExceptiondiawali\karena class ini ada di namespace global, bukan diApp.
Langkah 7: Paket dari Packagist dan file .env
Nama toko sebaiknya tidak ditulis di kode, sama seperti password database. Tempatnya di file .env. Untuk membacanya, pakai paket vlucas/phpdotenv dari Packagist, gudang paket PHP yang dipakai Composer:
composer require vlucas/phpdotenvUsing version ^5.8 for vlucas/phpdotenvPackage operations: 6 installs, 0 updates, 0 removals - Installing vlucas/phpdotenv (v5.8.0): Extracting archiveGenerating autoload filesComposer mengunduh paketnya beserta paket lain yang ia butuhkan ke folder vendor/, menambahkan barisnya ke composer.json, dan memperbarui autoload. ^5.8 artinya proyek ini menerima versi 5.8 ke atas, tapi tidak sampai versi 6, karena versi besar baru bisa membawa perubahan yang merusak.
Buat file .env di folder proyek:
NAMA_TOKO="Kopi Senja"Lalu class terakhir untuk mencetak struk:
<?php
declare(strict_types=1);
namespace App;
final class Struk{ public function __construct(private string $namaToko) {}
public function cetak(Keranjang $keranjang): string { $baris = ["== {$this->namaToko} =="]; foreach ($keranjang->items() as ['produk' => $produk, 'jumlah' => $jumlah]) { $baris[] = sprintf( '%dx %-12s %10s', $jumlah, $produk->nama, Rupiah::format($produk->hargaAkhir() * $jumlah), ); } $baris[] = sprintf('%-15s %10s', 'Total', Rupiah::format($keranjang->total()));
return implode("\n", $baris) . "\n"; }}foreach (... as ['produk' => $produk, 'jumlah' => $jumlah]) langsung membongkar setiap item menjadi dua variabel. sprintf dengan %-12s dan %10s meratakan kolom supaya struknya rapi.
Langkah 8: Program kasir
Sekarang program utamanya:
<?php
declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use App\Kategori;use App\Keranjang;use App\Produk;use App\Struk;
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/..');$dotenv->load();$dotenv->required('NAMA_TOKO')->notEmpty();
$kopiSusu = new Produk('Kopi susu', 18000, Kategori::Minuman);$croissant = new Produk( nama: 'Croissant', harga: 25000, kategori: Kategori::Makanan, diskon: 20,);
$keranjang = new Keranjang();$keranjang->tambah($kopiSusu, 2);$keranjang->tambah($croissant);
$struk = new Struk($_ENV['NAMA_TOKO']);echo $struk->cetak($keranjang);require __DIR__ . '/../vendor/autoload.php';adalah satu-satunyarequireyang dibutuhkan.__DIR__adalah folder file ini, jadi path-nya tetap benar dari mana pun program dijalankan.use App\Produk;membuat kamu cukup menulisProduk, bukanApp\Produklengkap.required('NAMA_TOKO')->notEmpty()membuat program langsung berhenti dengan pesan jelas kalau.envbelum diisi, daripada jalan dengan nama toko kosong.
Jalankan:
php bin/kasir.php== Kopi Senja ==2x Kopi susu Rp36.0001x Croissant Rp20.000Total Rp56.000Croissant Rp25.000 dengan diskon 20% menjadi Rp20.000, dan totalnya Rp36.000 + Rp20.000 = Rp56.000. Tidak ada satu pun include untuk file class.
Langkah 9: Simpan ke Git dengan benar
Buat .gitignore di folder proyek:
/vendor/.envvendor/tidak ikut di-commit. Isinya bisa dibuat ulang kapan saja dengancomposer install..envtidak ikut di-commit, karena nanti isinya password dan API key. Sebagai gantinya, buat.env.exampleberisi nama variabel tanpa nilai rahasia, supaya orang lain tahu apa yang perlu diisi.composer.lockjustru wajib di-commit. File ini mencatat versi persis setiap paket yang terpasang. Saat temanmu atau server menjalankancomposer install, versi yang dipasang sama persis dengan punyamu.
Terakhir, periksa apakah ada paket yang punya celah keamanan yang sudah diketahui:
composer auditNo security vulnerability advisories found.Jalankan perintah ini secara berkala, karena celah baru bisa ditemukan di paket yang kemarin masih aman.
Bonus: operator pipe di PHP 8.5
PHP 8.5 menambahkan operator |> untuk merangkai fungsi dari kiri ke kanan. Misalnya untuk membuat slug dari nama produk:
<?phpdeclare(strict_types=1);
$slug = ' Kopi Susu Gula Aren ' |> trim(...) |> strtolower(...) |> (fn (string $s): string => str_replace(' ', '-', $s));
echo $slug, "\n";kopi-susu-gula-arenTanpa |>, kodenya menjadi str_replace(' ', '-', strtolower(trim($nama))), yang dibaca dari dalam ke luar. trim(...) dengan tiga titik mengubah fungsi menjadi nilai yang bisa dioper. Fitur ini hanya jalan di PHP 8.5 ke atas, jadi pakai hanya kalau server production-mu juga sudah 8.5.
Kode lengkap
Struktur akhir proyek:
kasir-kopi/├── bin/│ └── kasir.php├── src/│ ├── Kategori.php│ ├── Keranjang.php│ ├── Produk.php│ ├── Rupiah.php│ └── Struk.php├── vendor/ (dibuat Composer, tidak di-commit)├── .env (tidak di-commit)├── .env.example├── .gitignore├── composer.json└── composer.lockIsi setiap file ada di langkah 2 sampai 9. Untuk menjalankan proyek ini di komputer lain setelah di-clone dari Git:
composer installcopy .env.example .envphp bin/kasir.phpDi macOS dan Linux, ganti copy dengan cp. Jangan lupa mengisi nilai di .env sebelum menjalankan program.
Error yang sering muncul
Fatal error: Uncaught Error: Class “App\Keranjang” not found
Penyebabnya salah satu dari ini:
- Lupa
require __DIR__ . '/../vendor/autoload.php';, atau belum pernah menjalankancomposer dump-autoloadsehingga foldervendor/belum ada. - Namespace di file tidak cocok dengan aturan PSR-4. File di
src/harus ber-namespaceApp. - Nama file tidak sama persis dengan nama class. Hati-hati di sini: Windows tidak membedakan huruf besar dan kecil, jadi
src/keranjang.phptetap jalan di laptopmu. Server Linux membedakannya, dan class yang sama tiba-tiba tidak ditemukan setelah deploy. Biasakan nama file sama persis dengan nama class:Keranjang.php.
Fatal error: Uncaught TypeError: App\Produk::__construct(): Argument #2 ($harga) must be of type int, string given
Kamu mengirim teks ke parameter yang meminta angka, misalnya new Produk('Teh', '8000', ...). Data dari form selalu berupa teks. Validasi dan ubah dulu, misalnya dengan filter_var($nilai, FILTER_VALIDATE_INT), yang mengembalikan false kalau isinya bukan bilangan bulat.
Fatal error: Uncaught Error: Cannot modify readonly property App\Produk::$harga
Properti readonly tidak bisa diubah setelah objek dibuat. Kalau memang butuh nilai berbeda, buat objek baru dengan nilai itu.
Fatal error: Uncaught Dotenv\Exception\InvalidPathException: Unable to read any of the environment file(s)
File .env tidak ada di folder yang ditunjuk createImmutable(). Pastikan .env ada di folder kasir-kopi/, bukan di bin/. Di server production yang memakai environment variable sungguhan, pakai $dotenv->safeLoad(), yang tidak protes kalau file .env tidak ada.
‘composer’ is not recognized as an internal or external command
Composer belum masuk PATH, atau terminal dibuka sebelum instalasi selesai. Tutup semua jendela terminal dan buka lagi. Kalau masih gagal, jalankan ulang Composer-Setup.exe.
Langkah berikutnya: rilis dan amankan
Program di tutorial ini berjalan di terminal, tapi polanya sama untuk aplikasi web. Bedanya hanya di folder public/ berisi index.php yang menjadi satu-satunya file yang bisa diakses dari browser, sementara src/, vendor/, dan .env ada di luar folder itu. Dengan begitu, file .env berisi password tidak bisa dibuka lewat URL. Aturan ini juga dipakai Laravel.
Untuk deploy, server menjalankan composer install --no-dev lalu mengisi environment variable lewat panel hosting, bukan dengan mengunggah file .env dari laptopmu. Alasannya dijelaskan di Environment Variable dan Secrets. Simpan kodenya di Git dan GitHub, dan jalankan composer audit sebelum setiap rilis. Kalau aplikasimu memakai database, tetap pakai prepared statement seperti di artikel SQL injection. Fitur PHP modern tidak otomatis membuat query aman.
Pertanyaan yang sering diajukan
Apakah kode PHP lama tetap jalan di PHP 8?
Sebagian besar ya, tapi tidak semua. Fungsi mysql_* sudah dihapus sejak PHP 7, dan PHP 8 lebih ketat terhadap beberapa hal yang dulu hanya menghasilkan peringatan. Uji aplikasimu di PHP 8 di laptop sebelum mengganti versi PHP di server.
Harus pakai class untuk semuanya?
Tidak. Fungsi biasa dengan tipe data dan strict_types sudah merupakan peningkatan besar. Class berguna saat data dan aturan yang menjaganya perlu disatukan, seperti Keranjang yang menolak jumlah nol. Autoload Composer juga bisa memuat file berisi fungsi lewat bagian "files" di composer.json.
Apa bedanya composer install dan composer update?
composer install memasang versi persis yang tercatat di composer.lock. Pakai ini di server dan saat baru clone proyek. composer update mencari versi terbaru yang diizinkan composer.json, lalu menulis ulang composer.lock. Pakai ini dengan sengaja, lalu tes aplikasinya sebelum commit.
Perlu Composer kalau tidak memakai paket apa pun?
Tetap berguna untuk autoload. Begitu proyek punya lebih dari beberapa class, autoload PSR-4 menghapus seluruh daftar include dan membuat struktur proyekmu mudah dipahami orang lain.
Kesimpulan
PHP modern berarti tiga kebiasaan: tipe data yang ketat dengan strict_types, struktur class yang dimuat otomatis lewat Composer dan PSR-4, dan konfigurasi yang dipisah dari kode lewat .env. Ditambah readonly, enum, match, dan named argument, kodemu jadi lebih pendek sekaligus lebih sulit dipakai dengan cara yang salah.
Dengan ini Level 1 jalur Upgrade dari Stack Lama selesai. Level berikutnya membahas Laravel untuk developer PHP native, yang dibangun di atas semua yang baru kamu pelajari.



