Object Oriented Programming (OOP) Dasar di PHP

Pelajari konsep dasar Object Oriented Programming (OOP) di PHP. OOP adalah paradigma pemrograman yang memungkinkan kode lebih terstruktur, reusable, dan mudah maintenance.

Class dan Object

Class adalah blueprint atau template untuk membuat object. Object adalah instance dari class yang memiliki data (properties) dan behavior (methods).

Membuat Class Pertama

PHP Code
<?php
class Car {
    // Properties (attributes)
    public $brand;
    public $model;
    public $year;
    public $color;
    
    // Constructor - dipanggil saat object dibuat
    public function __construct($brand, $model, $year, $color) {
        $this->brand = $brand;
        $this->model = $model;
        $this->year = $year;
        $this->color = $color;
    }
    
    // Methods (functions)
    public function startEngine() {
        return "Mesin {$this->brand} {$this->model} menyala!";
    }
    
    public function getInfo() {
        return "{$this->brand} {$this->model} ({$this->year}) - Warna: {$this->color}";
    }
    
    public function honk() {
        return "Beep beep!";
    }
}

// Membuat object (instantiation)
$car1 = new Car("Toyota", "Camry", 2023, "Putih");
$car2 = new Car("Honda", "Civic", 2022, "Hitam");

// Menggunakan methods
echo $car1->getInfo(); // Toyota Camry (2023) - Warna: Putih
echo $car1->startEngine(); // Mesin Toyota Camry menyala!
echo $car2->honk(); // Beep beep!

// Mengakses properties
echo $car1->brand; // Toyota
$car1->color = "Merah"; // Mengubah warna
echo $car1->color; // Merah
?>

Constructor dan Destructor

<?php
class User {
    public $name;
    public $email;
    private $id;
    
    // Constructor dipanggil saat object dibuat
    public function __construct($name, $email) {
        $this->name = $name;
        $this->email = $email;
        $this->id = uniqid();
        echo "User {$name} telah dibuat dengan ID: {$this->id}\n";
    }
    
    // Destructor dipanggil saat object dihapus dari memory
    public function __destruct() {
        echo "User {$this->name} telah dihapus dari memory\n";
    }
    
    public function getId() {
        return $this->id;
    }
}

// Membuat user
$user1 = new User("John Doe", "john@example.com");
echo $user1->getId();

// Constructor dengan parameter default
class Product {
    public $name;
    public $price;
    public $category;
    
    public function __construct($name, $price = 0, $category = "General") {
        $this->name = $name;
        $this->price = $price;
        $this->category = $category;
    }
    
    public function getDetails() {
        return "Produk: {$this->name}, Harga: Rp {$this->price}, Kategori: {$this->category}";
    }
}

$product1 = new Product("Laptop", 15000000, "Electronics");
$product2 = new Product("Buku"); // Menggunakan default values
echo $product1->getDetails();
echo $product2->getDetails();
?>

$this Keyword

<?php
class Calculator {
    private $result = 0;
    
    public function add($number) {
        $this->result += $number;
        return $this; // Return object untuk method chaining
    }
    
    public function subtract($number) {
        $this->result -= $number;
        return $this;
    }
    
    public function multiply($number) {
        $this->result *= $number;
        return $this;
    }
    
    public function divide($number) {
        if ($number != 0) {
            $this->result /= $number;
        }
        return $this;
    }
    
    public function getResult() {
        return $this->result;
    }
    
    public function reset() {
        $this->result = 0;
        return $this;
    }
}

// Method chaining
$calc = new Calculator();
$result = $calc->add(10)
              ->multiply(2)
              ->subtract(5)
              ->divide(3)
              ->getResult();

echo "Hasil: $result"; // Hasil: 5

// Reset dan operasi baru
$calc->reset()->add(100)->subtract(50);
echo "Hasil baru: " . $calc->getResult(); // Hasil baru: 50
?>
Tips: Gunakan $this untuk merujuk ke property dan method dalam class yang sama. Constructor berguna untuk inisialisasi object dengan data awal.

Properties dan Methods

Properties adalah variabel dalam class, sedangkan methods adalah fungsi dalam class. Keduanya bisa memiliki visibility yang berbeda.

Visibility: Public, Private, Protected

<?php
class BankAccount {
    public $accountNumber;     // Bisa diakses dari mana saja
    private $balance;          // Hanya bisa diakses dari dalam class ini
    protected $accountType;    // Bisa diakses dari class ini dan child class
    
    public function __construct($accountNumber, $initialBalance = 0) {
        $this->accountNumber = $accountNumber;
        $this->balance = $initialBalance;
        $this->accountType = "Savings";
    }
    
    // Public method - bisa diakses dari luar
    public function deposit($amount) {
        if ($amount > 0) {
            $this->balance += $amount;
            $this->logTransaction("Deposit", $amount);
            return true;
        }
        return false;
    }
    
    public function withdraw($amount) {
        if ($amount > 0 && $amount <= $this->balance) {
            $this->balance -= $amount;
            $this->logTransaction("Withdraw", $amount);
            return true;
        }
        return false;
    }
    
    public function getBalance() {
        return $this->balance;
    }
    
    // Private method - hanya bisa dipanggil dari dalam class
    private function logTransaction($type, $amount) {
        echo "[$type] Rp " . number_format($amount) . " - Saldo: Rp " . number_format($this->balance) . "\n";
    }
    
    // Protected method - bisa diakses dari child class
    protected function calculateInterest($rate) {
        return $this->balance * ($rate / 100);
    }
}

$account = new BankAccount("123456789", 1000000);

// Public access
echo $account->accountNumber; // 123456789
echo $account->getBalance(); // 1000000

// Deposit dan withdraw
$account->deposit(500000);
$account->withdraw(200000);

// Error - tidak bisa mengakses private property
// echo $account->balance; // Fatal error

// Error - tidak bisa mengakses private method
// $account->logTransaction("Test", 1000); // Fatal error
?>

Static Properties dan Methods

<?php
class Database {
    private static $instance = null;
    private static $connectionCount = 0;
    public static $host = "localhost";
    
    private $connection;
    
    // Private constructor untuk Singleton pattern
    private function __construct() {
        self::$connectionCount++;
        echo "Database connection #{" . self::$connectionCount . "} dibuat\n";
    }
    
    // Static method untuk mendapatkan instance
    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    public static function getConnectionCount() {
        return self::$connectionCount;
    }
    
    public static function setHost($host) {
        self::$host = $host;
    }
}

// Utility class dengan static methods
class StringHelper {
    public static function slugify($text) {
        $text = strtolower($text);
        $text = preg_replace('/[^a-z0-9]+/', '-', $text);
        return trim($text, '-');
    }
    
    public static function truncate($text, $length = 100, $suffix = '...') {
        if (strlen($text) > $length) {
            return substr($text, 0, $length) . $suffix;
        }
        return $text;
    }
    
    public static function capitalize($text) {
        return ucwords(strtolower($text));
    }
}

// Penggunaan static
echo Database::$host; // localhost
Database::setHost("192.168.1.1");
echo Database::$host; // 192.168.1.1

$db1 = Database::getInstance();
$db2 = Database::getInstance(); // Same instance
echo Database::getConnectionCount(); // 1

// Static methods tanpa instance
echo StringHelper::slugify("Hello World PHP"); // hello-world-php
echo StringHelper::capitalize("JOHN DOE"); // John Doe
echo StringHelper::truncate("This is a very long text that needs to be truncated", 20); // This is a very long...
?>

Constants dalam Class

<?php
class MathHelper {
    const PI = 3.14159;
    const E = 2.71828;
    
    // Visibility constants (PHP 7.1+)
    public const PUBLIC_CONSTANT = "Public";
    private const PRIVATE_CONSTANT = "Private";
    protected const PROTECTED_CONSTANT = "Protected";
    
    public static function circleArea($radius) {
        return self::PI * $radius * $radius;
    }
    
    public static function circleCircumference($radius) {
        return 2 * self::PI * $radius;
    }
    
    public function getPrivateConstant() {
        return self::PRIVATE_CONSTANT;
    }
}

class Configuration {
    const APP_NAME = "My Application";
    const VERSION = "1.0.0";
    const DEBUG = true;
    
    const DATABASE = [
        'host' => 'localhost',
        'port' => 3306,
        'name' => 'myapp'
    ];
    
    public static function getConfig($key) {
        switch ($key) {
            case 'app_name':
                return self::APP_NAME;
            case 'version':
                return self::VERSION;
            case 'database':
                return self::DATABASE;
            default:
                return null;
        }
    }
}

// Menggunakan constants
echo MathHelper::PI; // 3.14159
echo MathHelper::circleArea(5); // 78.53975

echo Configuration::APP_NAME; // My Application
print_r(Configuration::DATABASE);

// Magic constant __CLASS__
class Logger {
    public function log($message) {
        echo "[" . __CLASS__ . "] $message\n";
    }
}

$logger = new Logger();
$logger->log("Test message"); // [Logger] Test message
?>
Best Practice: Gunakan private untuk data internal, public untuk interface, dan protected untuk inheritance. Static berguna untuk utility functions dan shared data.

Inheritance (Pewarisan)

Inheritance memungkinkan class untuk mewarisi properties dan methods dari class lain, menciptakan hierarki class yang efisien.

Basic Inheritance

<?php
// Parent class (Base class)
class Animal {
    protected $name;
    protected $species;
    protected $age;
    
    public function __construct($name, $species, $age) {
        $this->name = $name;
        $this->species = $species;
        $this->age = $age;
    }
    
    public function eat() {
        return "{$this->name} sedang makan";
    }
    
    public function sleep() {
        return "{$this->name} sedang tidur";
    }
    
    public function getInfo() {
        return "Nama: {$this->name}, Spesies: {$this->species}, Umur: {$this->age}";
    }
    
    // Method yang akan di-override oleh child class
    public function makeSound() {
        return "{$this->name} membuat suara";
    }
}

// Child class (Derived class)
class Dog extends Animal {
    private $breed;
    
    public function __construct($name, $age, $breed) {
        // Memanggil constructor parent
        parent::__construct($name, "Canis lupus", $age);
        $this->breed = $breed;
    }
    
    // Override method parent
    public function makeSound() {
        return "{$this->name} menggonggong: Woof! Woof!";
    }
    
    // Method khusus untuk Dog
    public function wagTail() {
        return "{$this->name} mengibas-ibaskan ekor";
    }
    
    public function fetch() {
        return "{$this->name} mengambil bola";
    }
    
    // Override getInfo untuk menambah informasi breed
    public function getInfo() {
        return parent::getInfo() . ", Ras: {$this->breed}";
    }
}

class Cat extends Animal {
    private $isIndoor;
    
    public function __construct($name, $age, $isIndoor = true) {
        parent::__construct($name, "Felis catus", $age);
        $this->isIndoor = $isIndoor;
    }
    
    public function makeSound() {
        return "{$this->name} meong: Meow!";
    }
    
    public function purr() {
        return "{$this->name} mendengkur";
    }
    
    public function climb() {
        return "{$this->name} memanjat";
    }
}

// Penggunaan
$dog = new Dog("Buddy", 3, "Golden Retriever");
$cat = new Cat("Whiskers", 2, true);

echo $dog->getInfo(); // Nama: Buddy, Spesies: Canis lupus, Umur: 3, Ras: Golden Retriever
echo $dog->makeSound(); // Buddy menggonggong: Woof! Woof!
echo $dog->wagTail(); // Buddy mengibas-ibaskan ekor
echo $dog->eat(); // Buddy sedang makan (inherited from Animal)

echo $cat->makeSound(); // Whiskers meong: Meow!
echo $cat->purr(); // Whiskers mendengkur
echo $cat->sleep(); // Whiskers sedang tidur (inherited from Animal)
?>

Abstract Classes

<?php
// Abstract class tidak bisa di-instantiate langsung
abstract class Shape {
    protected $name;
    
    public function __construct($name) {
        $this->name = $name;
    }
    
    // Concrete method
    public function getName() {
        return $this->name;
    }
    
    // Abstract method harus diimplementasi di child class
    abstract public function calculateArea();
    abstract public function calculatePerimeter();
    
    // Method dengan implementasi default
    public function getDescription() {
        return "Ini adalah {$this->name} dengan luas " . $this->calculateArea();
    }
}

class Rectangle extends Shape {
    private $width;
    private $height;
    
    public function __construct($width, $height) {
        parent::__construct("Rectangle");
        $this->width = $width;
        $this->height = $height;
    }
    
    public function calculateArea() {
        return $this->width * $this->height;
    }
    
    public function calculatePerimeter() {
        return 2 * ($this->width + $this->height);
    }
}

class Circle extends Shape {
    private $radius;
    const PI = 3.14159;
    
    public function __construct($radius) {
        parent::__construct("Circle");
        $this->radius = $radius;
    }
    
    public function calculateArea() {
        return self::PI * $this->radius * $this->radius;
    }
    
    public function calculatePerimeter() {
        return 2 * self::PI * $this->radius;
    }
}

// Penggunaan
$rectangle = new Rectangle(5, 10);
$circle = new Circle(7);

echo $rectangle->getName(); // Rectangle
echo $rectangle->calculateArea(); // 50
echo $rectangle->getDescription(); // Ini adalah Rectangle dengan luas 50

echo $circle->calculateArea(); // 153.93791
echo $circle->calculatePerimeter(); // 43.98226

// Error - tidak bisa instantiate abstract class
// $shape = new Shape("Test"); // Fatal error
?>

Interfaces

<?php
// Interface mendefinisikan contract yang harus diimplementasi
interface Flyable {
    public function fly();
    public function land();
}

interface Swimmable {
    public function swim();
}

// Class bisa implement multiple interfaces
class Duck extends Animal implements Flyable, Swimmable {
    public function __construct($name, $age) {
        parent::__construct($name, "Anas platyrhynchos", $age);
    }
    
    public function makeSound() {
        return "{$this->name} berkata: Quack! Quack!";
    }
    
    // Implementasi Flyable interface
    public function fly() {
        return "{$this->name} terbang di udara";
    }
    
    public function land() {
        return "{$this->name} mendarat";
    }
    
    // Implementasi Swimmable interface
    public function swim() {
        return "{$this->name} berenang di air";
    }
}

class Airplane implements Flyable {
    private $model;
    
    public function __construct($model) {
        $this->model = $model;
    }
    
    public function fly() {
        return "Pesawat {$this->model} terbang";
    }
    
    public function land() {
        return "Pesawat {$this->model} mendarat";
    }
}

// Penggunaan
$duck = new Duck("Donald", 2);
$airplane = new Airplane("Boeing 747");

echo $duck->fly(); // Donald terbang di udara
echo $duck->swim(); // Donald berenang di air
echo $duck->makeSound(); // Donald berkata: Quack! Quack!

echo $airplane->fly(); // Pesawat Boeing 747 terbang

// Type hinting dengan interface
function makeFly(Flyable $object) {
    return $object->fly();
}

echo makeFly($duck); // Donald terbang di udara
echo makeFly($airplane); // Pesawat Boeing 747 terbang
?>
Perbedaan: Abstract class bisa memiliki implementasi method, sedangkan interface hanya contract. Class bisa extends satu class tapi implements multiple interfaces.

Konsep OOP Lanjutan

Konsep-konsep penting lainnya dalam OOP yang membantu membuat kode lebih maintainable dan flexible.

Polymorphism

<?php
// Polymorphism - object yang berbeda merespon method yang sama dengan cara berbeda
abstract class Employee {
    protected $name;
    protected $baseSalary;
    
    public function __construct($name, $baseSalary) {
        $this->name = $name;
        $this->baseSalary = $baseSalary;
    }
    
    public function getName() {
        return $this->name;
    }
    
    // Abstract method - implementasi berbeda di setiap child class
    abstract public function calculateSalary();
    abstract public function getJobDescription();
}

class FullTimeEmployee extends Employee {
    private $benefits;
    
    public function __construct($name, $baseSalary, $benefits = 1000000) {
        parent::__construct($name, $baseSalary);
        $this->benefits = $benefits;
    }
    
    public function calculateSalary() {
        return $this->baseSalary + $this->benefits;
    }
    
    public function getJobDescription() {
        return "Full-time employee dengan benefit lengkap";
    }
}

class PartTimeEmployee extends Employee {
    private $hoursWorked;
    private $hourlyRate;
    
    public function __construct($name, $hourlyRate, $hoursWorked = 0) {
        parent::__construct($name, 0);
        $this->hourlyRate = $hourlyRate;
        $this->hoursWorked = $hoursWorked;
    }
    
    public function setHoursWorked($hours) {
        $this->hoursWorked = $hours;
    }
    
    public function calculateSalary() {
        return $this->hourlyRate * $this->hoursWorked;
    }
    
    public function getJobDescription() {
        return "Part-time employee dengan sistem hourly";
    }
}

class Contractor extends Employee {
    private $projectFee;
    
    public function __construct($name, $projectFee) {
        parent::__construct($name, 0);
        $this->projectFee = $projectFee;
    }
    
    public function calculateSalary() {
        return $this->projectFee;
    }
    
    public function getJobDescription() {
        return "Contractor dengan project-based payment";
    }
}

// Polymorphism dalam action
function calculatePayroll(array $employees) {
    $totalPayroll = 0;
    
    foreach ($employees as $employee) {
        $salary = $employee->calculateSalary();
        $totalPayroll += $salary;
        
        echo "{$employee->getName()}: Rp " . number_format($salary) . " - {$employee->getJobDescription()}\n";
    }
    
    return $totalPayroll;
}

// Penggunaan
$employees = [
    new FullTimeEmployee("John Doe", 8000000, 2000000),
    new PartTimeEmployee("Jane Smith", 50000, 100), // 100 jam
    new Contractor("Bob Wilson", 15000000)
];

$total = calculatePayroll($employees);
echo "Total Payroll: Rp " . number_format($total);
?>

Encapsulation dan Data Hiding

<?php
class BankAccount {
    private $accountNumber;
    private $balance;
    private $pin;
    private $isLocked = false;
    private $attemptCount = 0;
    
    public function __construct($accountNumber, $initialBalance, $pin) {
        $this->accountNumber = $accountNumber;
        $this->balance = $initialBalance;
        $this->setPin($pin);
    }
    
    // Getter methods
    public function getAccountNumber() {
        return $this->accountNumber;
    }
    
    public function getBalance($pin) {
        if (!$this->verifyPin($pin)) {
            return false;
        }
        return $this->balance;
    }
    
    // Setter dengan validasi
    public function setPin($newPin) {
        if (strlen($newPin) !== 4 || !ctype_digit($newPin)) {
            throw new InvalidArgumentException("PIN harus 4 digit");
        }
        $this->pin = password_hash($newPin, PASSWORD_DEFAULT);
    }
    
    private function verifyPin($pin) {
        if ($this->isLocked) {
            throw new Exception("Akun terkunci. Hubungi customer service.");
        }
        
        if (password_verify($pin, $this->pin)) {
            $this->attemptCount = 0;
            return true;
        }
        
        $this->attemptCount++;
        if ($this->attemptCount >= 3) {
            $this->isLocked = true;
            throw new Exception("Akun dikunci karena terlalu banyak percobaan PIN salah");
        }
        
        return false;
    }
    
    public function deposit($amount, $pin) {
        if (!$this->verifyPin($pin)) {
            return false;
        }
        
        if ($amount <= 0) {
            throw new InvalidArgumentException("Jumlah deposit harus positif");
        }
        
        $this->balance += $amount;
        $this->logTransaction("DEPOSIT", $amount);
        return true;
    }
    
    public function withdraw($amount, $pin) {
        if (!$this->verifyPin($pin)) {
            return false;
        }
        
        if ($amount <= 0) {
            throw new InvalidArgumentException("Jumlah penarikan harus positif");
        }
        
        if ($amount > $this->balance) {
            throw new Exception("Saldo tidak mencukupi");
        }
        
        $this->balance -= $amount;
        $this->logTransaction("WITHDRAW", $amount);
        return true;
    }
    
    private function logTransaction($type, $amount) {
        $timestamp = date('Y-m-d H:i:s');
        $log = "[$timestamp] {$this->accountNumber}: $type Rp " . number_format($amount) . 
               " - Saldo: Rp " . number_format($this->balance) . "\n";
        file_put_contents('transactions.log', $log, FILE_APPEND);
    }
}

// Penggunaan
try {
    $account = new BankAccount("123456789", 1000000, "1234");
    
    echo "Saldo: Rp " . number_format($account->getBalance("1234"));
    
    $account->deposit(500000, "1234");
    $account->withdraw(200000, "1234");
    
    echo "Saldo akhir: Rp " . number_format($account->getBalance("1234"));
    
    // Coba PIN salah
    // $account->getBalance("0000"); // Exception setelah 3x
    
} catch (Exception $e) {
    echo "Error: " . $e->getMessage();
}
?>

Magic Methods

<?php
class MagicUser {
    private $data = [];
    
    // __get() - dipanggil saat mengakses property yang tidak ada
    public function __get($name) {
        echo "Mengakses property '$name'\n";
        return isset($this->data[$name]) ? $this->data[$name] : null;
    }
    
    // __set() - dipanggil saat mengset property yang tidak ada
    public function __set($name, $value) {
        echo "Mengset property '$name' dengan value '$value'\n";
        $this->data[$name] = $value;
    }
    
    // __isset() - dipanggil saat menggunakan isset() pada property
    public function __isset($name) {
        return isset($this->data[$name]);
    }
    
    // __unset() - dipanggil saat menggunakan unset() pada property
    public function __unset($name) {
        unset($this->data[$name]);
    }
    
    // __call() - dipanggil saat memanggil method yang tidak ada
    public function __call($name, $arguments) {
        echo "Memanggil method '$name' dengan arguments: " . implode(', ', $arguments) . "\n";
        return "Method $name tidak ditemukan";
    }
    
    // __toString() - dipanggil saat object di-convert ke string
    public function __toString() {
        return "MagicUser dengan data: " . json_encode($this->data);
    }
    
    // __invoke() - memungkinkan object dipanggil seperti function
    public function __invoke($message) {
        return "Invoke dengan message: $message";
    }
    
    // __clone() - dipanggil saat object di-clone
    public function __clone() {
        echo "Object di-clone\n";
        // Deep clone untuk array/object
        $this->data = array_map(function($item) {
            return is_object($item) ? clone $item : $item;
        }, $this->data);
    }
}

// Penggunaan magic methods
$user = new MagicUser();

// __set() dan __get()
$user->name = "John Doe"; // Mengset property 'name' dengan value 'John Doe'
echo $user->name; // Mengakses property 'name'

// __isset() dan __unset()
var_dump(isset($user->name)); // true
unset($user->name);
var_dump(isset($user->name)); // false

// __call()
echo $user->nonExistentMethod("arg1", "arg2");

// __toString()
echo $user; // MagicUser dengan data: {}

// __invoke()
echo $user("Hello World"); // Invoke dengan message: Hello World

// __clone()
$user2 = clone $user; // Object di-clone
?>
Tips OOP: Gunakan encapsulation untuk melindungi data, polymorphism untuk code flexibility, dan magic methods dengan hati-hati karena bisa membuat code sulit di-debug.
Tutorial Saat Ini
Level: Menengah

Memerlukan pemahaman dasar PHP

Daftar Isi
Tips Belajar