Memahami Overloading ala PHP


PHP tidak mendukung overloading!!! Yup! Dalam benak programmer Java, C++, C# dan sejenisnya, overloading adalah kemampuan untuk mendeklarasikan beberapa method dengan nama yang sama tetapi masing-masing memiliki jumlah dan tipe argumen yang berbeda. PHP tidak membolehkan hal tersebut!! Lalu mengapa di dokumentasi PHP terdapat sebuah halaman yang menjelaskan tentang overloading di PHP: http://php.net/__callstatic?

Apa yang disebut overloading oleh PHP adalah sesuatu yang mirip seperti methodMissing() di Groovy yang sering dipakai untuk keperluan meta programming. PHP menyediakan magic method seperti __set(), __get(), __isset(), __unset(), __call() dan __callStatic(). Mereka akan dipanggil bila programmer mengakses property atau method yang belum pernah dideklarasikan sebelumnya. Tentu saja overloading dengan cara seperti ini tentunya jauh lebih repot dibandingkan dengan Java dan C++, terutama bila yang perlu di-overload hanya beberapa method. Sebagai contoh, saya dapat melakukan ini di Java:

public class Pelanggan {

   public void pesan(Item item) {}

   public void pesan(List<Item> items) {}

   public void pesan(Item item, Integer jumlah, BigDecimal harga) {}

}

Untuk mencapai hal serupa di PHP, saya dapat menggunakan kode program seperti berikut ini:

<?php

class Pelanggan {

  public function __call($name, $arguments) {
    if ($name=='pesan') {
      switch (sizeof($arguments)) {
        case 1:
          if ($arguments[0] instanceof Item) {
            // kode program untuk pesan(Item item)
            print "pesan({$arguments[0]})<br>";
          } else if (is_array($arguments[0])) {
            // kode program untuk pesan(List items)
            print "pesan({$arguments[0]})<br>";
          }
          break;
        case 3:
          // kode program untuk pesan(Item item, Integer jumlah, BigDecimal harga)
          print "pesan({$arguments[0]},{$arguments[1]},{$arguments[2]})<br>";
          break;
      }
    }
  }

}

$pelanggan = new Pelanggan();
$pelanggan->pesan("ITEM1");
$pelanggan->pesan(["ITEM1", "ITEM2", "ITEM3"]);
$pelanggan->pesan("ITEM1", 10, 20000); 

?>

Beruntungnya, PHP adalah bahasa pemograman dinamis sehingga overloading bukanlah sesuatu yang sering dibutuhkan.

Karena apa yang disebut overloading di PHP lebih tepat disebut sebagai meta programming, maka saya bisa membuat dynamic finders (seperti di simple-jpa) di bahasa pemograman PHP. Perlu diperhatikan bahwa hal ini akan memberikan dampak buruk di sisi kinerja karena penggunaan magic method lebih lambat bila dibandingkan dengan mendeklarasikan method atau property secara langsung.

Sebagai contoh, saya membuat sebuah domain class sederhana di folder domain dengan nama Pelanggan.php yang isinya seperti berikut ini:

<?php
namespace domain;

class Pelanggan {

   public $nama;

   public $alamat;

   public $usia;     

   function __construct($nama = NULL, $alamat = NULL, $usia = NULL) {
      if ($nama) $this->nama = $nama;
      if ($alamat) $this->alamat = $alamat;
      if ($usia) $this->usia = $usia;
   }

   public function save() {      
      $db = new \PDO('mysql:host=localhost;dbname=exercises', 'snake', 'password');
      $st = $db->prepare("INSERT INTO Pelanggan(nama, alamat, usia) VALUES (?, ?, ?)");      
      $st->execute([$this->nama, $this->alamat, $this->usia]);                           
   }

}
?>

Pada kode program diatas, saya mendefinisikan sebuah class dengan nama Pelanggan. Class ini memiliki sebuah constructor untuk melakukan inisialisasi property-nya. Pada PHP, constructor juga adalah magic method. Saya melakukan penjagaan terhadap NULL di constructor karena saat saya melakukan fetching di PDO dengan PDO::FETCH_CLASS, kode program di constructor akan dikerjakan dengan argumen serba NULL setelah property diberi nilai. Ini adalah perilaku constructor yang tidak lazim dan saya perlu mewaspadainya.

Saya juga membuat sebuah method save() yang akan menyimpan data ke database dengan menggunakan PDO (PHP Data Object). PDO memungkinkan saya untuk mengakses database secara OOP. Selain itu, PDO adalah abstraction layer. Tanpa PDO, saya perlu mengakses database secara langsung dengan fungsi mysqli_xxx() yang hanya berlaku untuk database MySQL. Dengan PDO, jika suatu saat ini saya harus beralih ke database lain (misalnya Oracle), saya cukup perlu mengubah DSN (sebuah String yang isinya seperti 'mysql:host=localhost;dbname=exercises'). Mirip seperti JDBC di Java ‘bukan? Daftar database yang didukung (PDO driver) dapat dilihat di http://php.net/manual/en/pdo.drivers.php.

Pada method save(), saya juga memakai syntax baru di PHP 5.4 untuk mendeklarasikan array. Sebelum versi 5.4, array di PHP harus didefinsikan dengan keyword array seperti array(10, 20, 30, 40). PHP 5.4 membolehkan definisi array dengan cara seperti [10, 20, 30, 40]. Selain lebih sederhana, syntax baru ini juga adalah syntax deklarasi array di banyak bahasa dinamis lain seperti Groovy, Ruby, dan Python.

Berikutnya, saya akan menambahkan dynamic finders pada class Pelanggan. Karena finders tersebut dapat dipanggil kapan saja tanpa harus ada instance dari class, maka mereka harus berupa method static. PHP 5.3 memiliki __callStatic() yang membolehkan overloading (ingat bahwa ini istilah PHP sendiri!) method static. Berikut adalah isi method tersebut:

class Pelanggan {

   ...

   public static function __callStatic($name, $args) {
      if (preg_match('/findBy([A-Z]\w*)/', $name, $matches)==1) {

         // Melakukan parsing nama method
         $expr = $matches[1];
         $fields = preg_split("/(And|Or)/", $expr, NULL, PREG_SPLIT_DELIM_CAPTURE);
         $sql = 'SELECT * FROM Pelanggan WHERE ';
         for ($i=0; $i<sizeof($fields); $i++) {                
            $sql .= ' ' . $fields[$i] . ' = ? ';
            if (++$i < sizeof($fields) - 1) {
               $sql .= ' ' . $fields[$i];
            }  
         }

         // Melakukan query ke database
         $db = new \PDO('mysql:host=localhost;dbname=exercises', 'snake', 'password');
         $st = $db->prepare($sql);
         $st->execute($args);
         return $st->fetchAll(\PDO::FETCH_CLASS, '\domain\Pelanggan');                    
      }

      trigger_error("Method $name tidak ditemukan!", E_USER_ERROR);  
   }  

   ...

}
?>

Pada method di atas, saya menggunakan fetchAll() dari PDO dengan fecth style berupa PDO::FETCH_CLASS. Hal ini akan menyebabkan method fetchAll() mengembalikan sebuah array yang berisi instance dari class yang saya tentukan, yaitu Pelanggan. PDO akan mengisi property berdasarkan nama kolom di tabel.

Berikutnya, saya akan membuat sebuah tabel di database dengan perintah SQL berikut ini:

CREATE TABLE Pelanggan (
   nama VARCHAR(50) PRIMARY KEY,
   alamat VARCHAR(100),
   usia INT NOT NULL
);

Lalu, saya dapat memakai domain class saya, misalnya seperti berikut ini:

<?php

  spl_autoload_extensions('.php');
  spl_autoload_register();

  use domain\Pelanggan;

  $snake = new Pelanggan("Solid Snake", "Alaska", 35);
  $snake->save();
  $liquid = new Pelanggan("Liquid Snake", "New York", 35);
  $liquid->save();
  $boss = new Pelanggan("The Boss", "Alaska", 40);
  $boss->save();

?>

<pre>
<?php

  print "Daftar pelanggan dengan nama Solid Snake di Alaska:\n";
  print_r(Pelanggan::findByNamaAndAlamat("Solid Snake", "Alaska"));

  print "\nDaftar pelanggan dengan usia 35 tahun:\n";
  print_r(Pelanggan::findByUsia(35));

  print "\nDaftar pelanggan yang tinggal di Alaska:\n";
  print_r(Pelanggan::findByAlamat("Alaska"));

?>   
</pre>

Pada kode program di atas, saya menggunakan spl_autoload_extensions() dan spl_autoload_register() agar PHP secara otomatis men-include file class saat saya memakainya. Ingat bahwa saya mendefinisikan class pada file PHP yang terpisah. Tanpa kedua fungsi di atas, saya harus menyertakan file domain class saya dengan menggunakan keyword include atau require. Dengan fungsi spl_autoload_xxx() yang sudah ada sejak PHP 5.3, pada saat sebuah class hendak dipakai tetapi deklarasinya tidak ditemukan, maka autoloader akan bekerja mencari & membaca file yang tepat. Pada kode program di atas, autoloader akan mencari file di domain\Pelanggan.php bila menemukan penggunaan class \domain\Pelanggan. Konsekuensinya adalah sebuah class harus didefinisikan pada sebuah file PHP dengan nama yang sama dengan nama class. Setiap lokasi namespace juga harus diwakili dengan sebuah direktori/folder. Mirip seperti package di Java, bukan?

Kode program di atas akan menciptakan tiga record baru di tabel, lalu menggunakan finders untuk melakukan query, dimana hasilnya akan terlihat seperti berikut ini:

Daftar pelanggan dengan nama Solid Snake di Alaska:
Array
(
    [0] => domain\Pelanggan Object
        (
            [nama] => Solid Snake
            [alamat] => Alaska
            [usia] => 35
        )

)

Daftar pelanggan dengan usia 35 tahun:
Array
(
    [0] => domain\Pelanggan Object
        (
            [nama] => Liquid Snake
            [alamat] => New York
            [usia] => 35
        )

    [1] => domain\Pelanggan Object
        (
            [nama] => Solid Snake
            [alamat] => Alaska
            [usia] => 35
        )

)

Daftar pelanggan yang tinggal di Alaska:
Array
(
    [0] => domain\Pelanggan Object
        (
            [nama] => Solid Snake
            [alamat] => Alaska
            [usia] => 35
        )

    [1] => domain\Pelanggan Object
        (
            [nama] => The Boss
            [alamat] => Alaska
            [usia] => 40
        )

)

Perihal Solid Snake
I'm nothing...

One Response to Memahami Overloading ala PHP

  1. Ping-balik: Memakai Reflection API Di PHP | The Solid Snake

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: