1. Pengenalan OOP
Object-Oriented Programming (OOP) adalah paradigma pemrograman yang mengorganisasi kode ke dalam "objek" yang memiliki properti (data) dan method (perilaku). OOP membantu membuat kode yang lebih terstruktur, reusable, dan mudah dipelihara.
4 Pilar OOP
| Pilar | Penjelasan | Contoh |
|---|---|---|
| Encapsulation | Menyembunyikan detail internal, hanya expose yang perlu | Private property dengan getter/setter |
| Inheritance | Class anak mewarisi sifat dan perilaku class parent | Admin extends User |
| Polymorphism | Satu interface, banyak implementasi | Method nama() di class berbeda |
| Abstraction | Menyembunyikan kompleksitas, hanya tampilkan yang esensial | Abstract class, interface |
OOP vs Procedural
| Aspek | Procedural | OOP |
|---|---|---|
| Struktur | Fungsi-fungsi berdiri sendiri | Class dan Object |
| Reusability | π‘ Terbatas | π’ Sangat baik (inheritance) |
| Maintainability | π‘ Sulit di skala besar | π’ Terstruktur dan modular |
| Data Security | π΄ Data terbuka | π’ Encapsulation (access control) |
| Cocok untuk | Skrip sederhana, otomasi | Aplikasi besar, framework |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β 4 PILAR OOP β β β β βββββββββββββββββββ βββββββββββββββββββ β β β Encapsulation β β Inheritance β β β β βββββββββββββ β β Parent β β β β β - private β β β βββ Child1 β β β β β + public β β β βββ Child2 β β β β β # protectedβ β β β β β β βββββββββββββ β βββββββββββββββββββ β β βββββββββββββββββββ β β β β βββββββββββββββββββ βββββββββββββββββββ β β β Polymorphism β β Abstraction β β β β interface Shape β β abstract class β β β β βββ Circle β β Animal β β β β βββ Square β β (harus di- β β β β βββ Triangle β β implementasi) β β β βββββββββββββββββββ βββββββββββββββββββ β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
2. Class dan Object
Class adalah blueprint (cetakan) untuk membuat objek. Object adalah instance (hasil) dari sebuah class.
Membuat Class Dasar
<?php
// Mendefinisikan class
class Mobil
{
// Properties (properti/atribut)
public string $merek;
public string $model;
public int $tahun;
public string $warna;
private int $kecepatan = 0;
// Constructor β dipanggil saat object dibuat
public function __construct(string $merek, string $model, int $tahun, string $warna)
{
$this->merek = $merek;
$this->model = $model;
$this->tahun = $tahun;
$this->warna = $warna;
}
// Methods (perilaku/fungsi)
public function gas(int $tambah): void
{
$this->kecepatan += $tambah;
echo "{$this->merek} {$this->model}: Kecepatan {$this->kecepatan} km/jam<br>";
}
public function rem(int $kurang): void
{
$this->kecepatan = max(0, $this->kecepatan - $kurang);
echo "{$this->merek} {$this->model}: Kecepatan {$this->kecepatan} km/jam<br>";
}
public function getKecepatan(): int
{
return $this->kecepatan;
}
// Magic method __toString
public function __toString(): string
{
return "{$this->merek} {$this->model} ({$this->tahun}) - {$this->warna}";
}
}
// Membuat object (instance) dari class
$mobil1 = new Mobil("Toyota", "Avanza", 2024, "Putih");
$mobil2 = new Mobil("Honda", "Civic", 2023, "Hitam");
// Mengakses properties dan methods
echo $mobil1 . "<br>"; // Toyota Avanza (2024) - Putih
$mobil1->gas(60); // Toyota Avanza: Kecepatan 60 km/jam
$mobil1->gas(40); // Toyota Avanza: Kecepatan 100 km/jam
$mobil1->rem(30); // Toyota Avanza: Kecepatan 70 km/jam
echo $mobil2 . "<br>"; // Honda Civic (2023) - Hitam
$mobil2->gas(120); // Honda Civic: Kecepatan 120 km/jam
?>
Constructor Promotion (PHP 8+)
<?php
// PHP 8 Constructor Promotion β cara singkat!
class Mahasiswa
{
public function __construct(
public readonly string $nama,
public readonly string $nim,
public readonly string $jurusan,
public readonly float $ipk = 0.0,
) {}
// Properties otomatis dibuat dari parameter constructor!
}
$mhs = new Mahasiswa("Andi Wijaya", "2024001", "Teknik Informatika", 3.75);
echo $mhs->nama; // Andi Wijaya
echo $mhs->jurusan; // Teknik Informatika
// Readonly β property tidak bisa diubah setelah di-set
// $mhs->nama = "Budi"; // Error! Cannot modify readonly property
// Enum (PHP 8.1)
enum StatusPembayaran: string
{
case Pending = 'pending';
case Lunas = 'lunas';
case Dibatalkan = 'dibatalkan';
public function label(): string
{
return match($this) {
self::Pending => 'β³ Menunggu Pembayaran',
self::Lunas => 'β
Pembayaran Lunas',
self::Dibatalkan => 'β Dibatalkan',
};
}
}
$status = StatusPembayaran::Lunas;
echo $status->label(); // β
Pembayaran Lunas
echo $status->value; // lunas
?>
3. Inheritance (Pewarisan)
Inheritance memungkinkan class anak (child) mewarisi semua properties dan methods dari class parent (superclass). Ini menghindari duplikasi kode dan membuat struktur yang logis.
<?php
// Parent class
class Kendaraan
{
public function __construct(
protected string $merek,
protected string $model,
protected int $tahun,
protected int $kecepatan = 0,
) {}
public function gas(int $tambah): void
{
$this->kecepatan += $tambah;
}
public function rem(int $kurang): void
{
$this->kecepatan = max(0, $this->kecepatan - $kurang);
}
public function getInfo(): string
{
return "{$this->merek} {$this->model} ({$this->tahun}) - {$this->kecepatan} km/jam";
}
}
// Child class β mewarisi Kendaraan
class Mobil extends Kendaraan
{
public function __construct(
string $merek, string $model, int $tahun,
private int $jumlahPintu = 4,
) {
parent::__construct($merek, $model, $tahun);
}
// Override method parent
public function getInfo(): string
{
return parent::getInfo() . " | {$this->jumlahPintu} pintu";
}
// Method baru khusus Mobil
public function klakson(): string
{
return "π {$this->merek} {$this->model}: TIN TIN! π";
}
}
// Child class lain
class Motor extends Kendaraan
{
public function __construct(
string $merek, string $model, int $tahun,
private string $tipeMotor = "matic",
) {
parent::__construct($merek, $model, $tahun);
}
public function getInfo(): string
{
return parent::getInfo() . " | tipe {$this->tipeMotor}";
}
public function wheelie(): string
{
return "ποΈ {$this->merek} {$this->model}: Wheelie! π€";
}
}
// Menggunakan inheritance
$mobil = new Mobil("Toyota", "Avanza", 2024, 4);
$mobil->gas(60);
echo $mobil->getInfo() . "<br>"; // Toyota Avanza (2024) - 60 km/jam | 4 pintu
echo $mobil->klakson() . "<br>"; // π Toyota Avanza: TIN TIN! π
$motor = new Motor("Honda", "Beat", 2024, "matic");
$motor->gas(80);
echo $motor->getInfo() . "<br>"; // Honda Beat (2024) - 80 km/jam | tipe matic
echo $motor->wheelie() . "<br>"; // ποΈ Honda Beat: Wheelie! π€
// instanceof β cek tipe object
var_dump($mobil instanceof Mobil); // true
var_dump($mobil instanceof Kendaraan); // true (karena inheritance)
var_dump($motor instanceof Mobil); // false
?>
Static Properties dan Methods
<?php
class Pengguna
{
// Static property β dimiliki oleh class, bukan object
private static int $count = 0;
private static array $instances = [];
public function __construct(
public readonly string $nama,
public readonly string $email,
) {
self::$count++;
self::$instances[] = $this;
}
// Static method β bisa dipanggil tanpa membuat object
public static function getCount(): int
{
return self::$count;
}
public static function findByEmail(string $email): ?self
{
foreach (self::$instances as $instance) {
if ($instance->email === $email) {
return $instance;
}
}
return null;
}
}
new Pengguna("Andi", "andi@mail.com");
new Pengguna("Budi", "budi@mail.com");
new Pengguna("Citra", "citra@mail.com");
echo Pengguna::getCount() . "<br>"; // 3
$user = Pengguna::findByEmail("budi@mail.com");
echo $user?->nama . "<br>"; // Budi
?>
4. Encapsulation (Enkapsulasi)
Encapsulation adalah menyembunyikan data internal dan hanya mengaksesnya melalui method publik. PHP memiliki 3 level akses: public, protected, dan private.
<?php
class RekeningBank
{
// Private β hanya bisa diakses dari dalam class ini
private string $nomorRekening;
private float $saldo;
// Protected β bisa diakses dari class ini dan child class
protected string $tipeRekening;
// Public β bisa diakses dari mana saja
public string $namaPemilik;
public function __construct(string $nama, string $nomor, float $saldoAwal)
{
$this->namaPemilik = $nama;
$this->nomorRekening = $nomor;
$this->saldo = $saldoAwal;
$this->tipeRekening = "Tabungan";
}
// Getter β mengambil data private
public function getSaldo(): float
{
return $this->saldo;
}
public function getNomorRekening(): string
{
// Tampilkan hanya 4 digit terakhir
return "****" . substr($this->nomorRekening, -4);
}
// Setter dengan validasi
public function setor(float $jumlah): void
{
if ($jumlah <= 0) {
throw new InvalidArgumentException("Jumlah setoran harus positif!");
}
$this->saldo += $jumlah;
echo "β
Setor Rp" . number_format($jumlah, 0, ',', '.') .
" | Saldo: Rp" . number_format($this->saldo, 0, ',', '.') . "<br>";
}
public function tarik(float $jumlah): void
{
if ($jumlah <= 0) {
throw new InvalidArgumentException("Jumlah penarikan harus positif!");
}
if ($jumlah > $this->saldo) {
throw new RuntimeException("Saldo tidak cukup!");
}
$this->saldo -= $jumlah;
echo "β
Tarik Rp" . number_format($jumlah, 0, ',', '.') .
" | Saldo: Rp" . number_format($this->saldo, 0, ',', '.') . "<br>";
}
// Method untuk info
public function getInfo(): string
{
return "Rekening {$this->getNomorRekening()} a.n. {$this->namaPemilik}" .
" | Saldo: Rp" . number_format($this->saldo, 0, ',', '.');
}
}
// Menggunakan encapsulation
$rekening = new RekeningBank("Andi Wijaya", "1234567890", 1000000);
echo $rekening->getInfo() . "<br>";
// Rekening ****7890 a.n. Andi Wijaya | Saldo: Rp1.000.000
$rekening->setor(500000); // β
Setor Rp500.000 | Saldo: Rp1.500.000
$rekening->tarik(200000); // β
Tarik Rp200.000 | Saldo: Rp1.300.000
// $rekening->saldo = 9999999; // Error! Property private
// Readonly class (PHP 8.2)
readonly class Koordinat
{
public function __construct(
public float $latitude,
public float $longitude,
) {}
}
$lokasi = new Koordinat(-6.2088, 106.8456);
echo "Jakarta: {$lokasi->latitude}, {$lokasi->longitude}<br>";
// $lokasi->latitude = 0; // Error! Readonly
?>
5. Abstraction (Abstraksi)
Abstract class dan abstract method memungkinkan Anda mendefinisikan "kontrak" yang harus diikuti oleh class anak. Abstract class tidak bisa diinstansiasi langsung.
<?php
// Abstract class β tidak bisa dibuat object langsung
abstract class Shape
{
public function __construct(
protected string $warna = "hitam"
) {}
// Abstract method β HARUS diimplementasi oleh child
abstract public function hitungLuas(): float;
abstract public function hitungKeliling(): float;
// Method biasa β bisa digunakan langsung oleh child
public function getInfo(): string
{
return "Warna: {$this->warna} | " .
"Luas: {$this->hitungLuas()} | " .
"Keliling: {$this->hitungKeliling()}";
}
}
class Lingkaran extends Shape
{
public function __construct(
private float $jariJari,
string $warna = "merah",
) {
parent::__construct($warna);
}
public function hitungLuas(): float
{
return M_PI * $this->jariJari ** 2;
}
public function hitungKeliling(): float
{
return 2 * M_PI * $this->jariJari;
}
}
class PersegiPanjang extends Shape
{
public function __construct(
private float $panjang,
private float $lebar,
string $warna = "biru",
) {
parent::__construct($warna);
}
public function hitungLuas(): float
{
return $this->panjang * $this->lebar;
}
public function hitungKeliling(): float
{
return 2 * ($this->panjang + $this->lebar);
}
}
class Segitiga extends Shape
{
public function __construct(
private float $alas,
private float $tinggi,
private float $sisiA,
private float $sisiB,
string $warna = "hijau",
) {
parent::__construct($warna);
}
public function hitungLuas(): float
{
return 0.5 * $this->alas * $this->tinggi;
}
public function hitungKeliling(): float
{
return $this->alas + $this->sisiA + $this->sisiB;
}
}
// Menggunakan polymorphism
$bentuk = [
new Lingkaran(7),
new PersegiPanjang(10, 5),
new Segitiga(6, 8, 10, 8),
];
foreach ($bentuk as $b) {
echo get_class($b) . ": " . $b->getInfo() . "<br>";
}
// Lingkaran: Warna: merah | Luas: 153.938... | Keliling: 43.982...
// PersegiPanjang: Warna: biru | Luas: 50 | Keliling: 30
// Segitiga: Warna: hijau | Luas: 24 | Keliling: 24
?>
6. Interface
Interface mendefinisikan kontrak (metode apa saja yang harus ada) tanpa implementasi. Sebuah class bisa mengimplementasi beberapa interface sekaligus.
<?php
// Interface β mendefinisikan "kontrak"
interface Serializable
{
public function serialize(): string;
public static function unserialize(string $data): self;
}
interface Loggable
{
public function toLogString(): string;
public function getLogLevel(): string;
}
interface Cacheable
{
public function getCacheKey(): string;
public function getCacheDuration(): int;
public function isCacheValid(): bool;
}
// Class bisa implementasi beberapa interface
class User implements Serializable, Loggable, Cacheable
{
public function __construct(
public int $id,
public string $nama,
public string $email,
) {}
// Implementasi Serializable
public function serialize(): string
{
return json_encode([
'id' => $this->id,
'nama' => $this->nama,
'email' => $this->email,
]);
}
public static function unserialize(string $data): self
{
$arr = json_decode($data, true);
return new self($arr['id'], $arr['nama'], $arr['email']);
}
// Implementasi Loggable
public function toLogString(): string
{
return "[USER] ID:{$this->id} {$this->nama} ({$this->email})";
}
public function getLogLevel(): string
{
return 'info';
}
// Implementasi Cacheable
public function getCacheKey(): string
{
return "user:{$this->id}";
}
public function getCacheDuration(): int
{
return 3600; // 1 jam
}
public function isCacheValid(): bool
{
return true;
}
}
// Type hint dengan interface
function logActivity(Loggable $entity): void
{
echo "[{$entity->getLogLevel()}] {$entity->toLogString()}<br>";
}
function cacheEntity(Cacheable $entity): void
{
echo "Caching {$entity->getCacheKey()} selama {$entity->getCacheDuration()}s<br>";
}
$user = new User(1, "Andi", "andi@mail.com");
logActivity($user); // [info] [USER] ID:1 Andi (andi@mail.com)
cacheEntity($user); // Caching user:1 selama 3600s
// Mengecek interface
var_dump($user instanceof Loggable); // true
var_dump($user instanceof Cacheable); // true
?>
7. Trait
Trait adalah mekanisme untuk menghindari keterbatasan single inheritance di PHP. Trait memungkinkan Anda mengelompokkan kode yang bisa digunakan kembali di beberapa class yang tidak berhubungan.
<?php
// Trait β kumpulan method yang bisa "ditumpuk" ke class manapun
trait HasTimestamps
{
public ?string $createdAt = null;
public ?string $updatedAt = null;
public function touch(): void
{
$this->updatedAt = date('Y-m-d H:i:s');
}
public function markCreated(): void
{
$this->createdAt = date('Y-m-d H:i:s');
$this->touch();
}
public function getAge(): string
{
if (!$this->createdAt) return 'Belum disimpan';
$diff = strtotime('now') - strtotime($this->createdAt);
$days = floor($diff / 86400);
return "{$days} hari yang lalu";
}
}
trait HasSlug
{
public function generateSlug(string $text): string
{
$slug = strtolower(trim($text));
$slug = preg_replace('/[^a-z0-9-]/', '-', $slug);
$slug = preg_replace('/-+/', '-', $slug);
return trim($slug, '-');
}
}
trait HasSearchable
{
public function search(array $data, string $keyword): array
{
return array_filter($data, function ($item) use ($keyword) {
return str_contains(strtolower(serialize($item)), strtolower($keyword));
});
}
}
// Menggunakan trait di class
class Artikel
{
use HasTimestamps, HasSlug; // "menumpuk" trait
public function __construct(
public string $judul,
public string $konten,
public string $slug = '',
) {
$this->slug = $this->generateSlug($judul);
$this->markCreated();
}
}
class Komentar
{
use HasTimestamps; // Komentar juga pakai HasTimestamps
public function __construct(
public string $nama,
public string $isi,
) {
$this->markCreated();
}
}
// Trait beraksi!
$artikel = new Artikel("Belajar PHP OOP", "PHP adalah bahasa yang...");
echo "Slug: {$artikel->slug}<br>"; // belajar-php-oop
echo "Dibuat: {$artikel->createdAt}<br>"; // 2026-06-27 10:30:00
echo "Umur: {$artikel->getAge()}<br>"; // 0 hari yang lalu
$komentar = new Komentar("Andi", "Artikelnya bagus!");
echo "Komentar oleh: {$komentar->nama}<br>";
// Trait conflict resolution
trait A {
public function hello(): string { return "Hello from A"; }
}
trait B {
public function hello(): string { return "Hello from B"; }
}
class MyClass
{
use A, B {
A::hello insteadof B; // Pakai A, bukan B
B::hello as helloFromB; // B diakses sebagai helloFromB
}
}
$obj = new MyClass();
echo $obj->hello() . "<br>"; // Hello from A
echo $obj->helloFromB() . "<br>"; // Hello from B
?>
8. Namespace
Namespace digunakan untuk mengorganisasi kode dan menghindari konflik nama class. Ini sangat penting dalam proyek besar yang menggunakan banyak library.
<?php
// file: app/Models/User.php
namespace App\Models;
class User
{
public function __construct(
public string $nama,
public string $email,
) {}
public function getFullName(): string
{
return $this->nama;
}
}
// file: app/Services/UserService.php
namespace App\Services;
use App\Models\User; // Import class dari namespace lain
use App\Repositories\UserRepository as UserRepo; // Alias
class UserService
{
public function __construct(
private UserRepo $userRepo,
) {}
public function createUser(string $nama, string $email): User
{
return new User($nama, $email);
}
}
// file: app/Repositories/UserRepository.php
namespace App\Repositories;
use App\Models\User;
class UserRepository
{
private array $users = [];
public function save(User $user): void
{
$this->users[] = $user;
}
public function findAll(): array
{
return $this->users;
}
}
// ===== Menggunakan namespace =====
// Import
use App\Models\User;
use App\Services\UserService;
// Membuat object dari class di namespace berbeda
$user = new User("Andi", "andi@mail.com");
echo $user->getFullName(); // Andi
// Fully qualified name (tanpa use)
$user2 = new \App\Models\User("Budi", "budi@mail.com");
// Namespace functions dan constants
namespace App\Helpers;
const APP_NAME = "BeebaneLabs";
function formatRupiah(float $angka): string
{
return "Rp" . number_format($angka, 0, ',', '.');
}
// Menggunakan constants dan functions dari namespace
echo \App\Helpers\APP_NAME; // BeebaneLabs
echo \App\Helpers\formatRupiah(50000); // Rp50.000
?>
9. Autoloading (PSR-4)
Autoloading memungkinkan PHP memuat class secara otomatis saat pertama kali digunakan, tanpa perlu require atau include secara manual. PHP menggunakan standar PSR-4 untuk autoloading.
{
"name": "beebanelabs/my-project",
"description": "Proyek PHP dengan PSR-4 Autoloading",
"type": "project",
"require": {
"php": ">=8.2",
"monolog/monolog": "^3.0"
},
"autoload": {
"psr-4": {
"App\\": "app/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
}
}
# Setelah mengubah composer.json, jalankan:
# composer dump-autoload
# Mapping:
# App\Models\User β app/Models/User.php
# App\Http\Controllers\HomeController β app/Http/Controllers/HomeController.php
# App\Services\AuthService β app/Services/AuthService.php
Contoh Struktur dengan PSR-4
my-project/ βββ app/ β βββ Models/ β β βββ User.php β namespace App\Models β β βββ Post.php β namespace App\Models β β βββ Comment.php β namespace App\Models β βββ Services/ β β βββ UserService.php β namespace App\Services β β βββ PostService.php β namespace App\Services β βββ Repositories/ β β βββ UserRepository.php β namespace App\Repositories β β βββ PostRepository.php β namespace App\Repositories β βββ Exceptions/ β β βββ ValidationException.php β namespace App\Exceptions β βββ Helpers/ β βββ StringHelper.php β namespace App\Helpers β βββ DateHelper.php β namespace App\Helpers βββ tests/ β βββ Unit/ β β βββ UserTest.php β namespace Tests\Unit β βββ Feature/ β βββ LoginTest.php β namespace Tests\Feature βββ vendor/ β Composer dependencies + autoload β βββ composer/ β βββ autoload.php β File autoloading utama βββ composer.json β Konfigurasi PSR-4 mapping
<?php
// index.php β entry point aplikasi
// Cukup require autoload.php, semua class otomatis dimuat!
require_once __DIR__ . '/vendor/autoload.php';
// Langsung gunakan class dari namespace berbeda
// TANPA perlu require manual di setiap file!
use App\Models\User;
use App\Models\Post;
use App\Services\UserService;
use App\Repositories\UserRepository;
$userRepo = new UserRepository();
$userService = new UserService($userRepo);
$user = $userService->createUser("Andi Wijaya", "andi@mail.com");
echo $user->getFullName(); // Andi Wijaya
$post = new Post("Belajar PHP OOP", "Konten artikel...");
echo $post->title; // Belajar PHP OOP
// PHP akan otomatis mencari:
// App\Models\User β app/Models/User.php
// App\Services\UserService β app/Services/UserService.php
// App\Repositories\UserRepository β app/Repositories/UserRepository.php
?>
Selalu jalankan composer dump-autoload setelah menambah atau memindahkan file class. Untuk production, gunakan composer install --optimize-autoloader untuk performa yang lebih baik. PSR-4 memastikan nama class dan path file harus konsisten β App\Models\User harus berada di app/Models/User.php.
10. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang PHP OOP: