Object-Oriented PHP:

Trait

Episode 15 Diperbarui: 7+ menit

Trait adalah suatu mekanisme dimana suatu class diizinkan untuk menggunakan kembali kode program (code reuse) yang berasal dari hirarki yang berbeda.

Mari kita perhatikan contoh di bawah ini, saya buat tiga buah class:

<?php 
class ApaKabar 
{ 
    public function apaKabar() { 
        return "Apa kabar?"; 
    } 
} 
 
class SelamatPagi 
{ 
    public function selamatPagi() { 
        return "Selamat pagi?"; 
    } 
} 
 
class Pesan 
{ 
  // 
} 

Jika kita menghendaki class Pesan bisa menggunakan method apaKabar() maka kita tinggal melakukan extends ke class ApaKabar seperti:

<?php 
class ApaKabar 
{ 
    public function apaKabar() { 
        return "Apa kabar?"; 
    } 
} 
 
class Pesan extends ApaKabar 
{ 
  // 
} 
 
$obj = new Pesan(); 
echo $obj->apaKabar(); 

Terus bagaimana jika kita ingin class Pesan dapat menggunakan method apaKabar() dan selamatPagi()? class Pesan meng-extends class ApaKabar dan class SelamatPagi? tentu ini tidak diperbolehkan oleh PHP, PHP hanya memperbolehkan satu parent class . Untuk itulah adanya Trait

1. Cara membuat trait

Sama seperti pembuatan class, hanya saja class diganti dengan trait

<?php 
// dengan keyword trait 
trait ApaKabar 
{ 
    public function apaKabar() { 
        return "Apa kabar?"; 
    } 
} 

2. Cara memakai trait

Cara memakai trait yaitu dengan menggunakan keyword use dalam sebuah class dan diiringi nama trait

<?php 
trait ApaKabar 
{ 
    public function apaKabar() { 
        return "Apa kabar?"; 
    } 
} 
 
class Pesan 
{ 
  // menggunakan use 
  use ApaKabar; 
} 
 
$obj = new Pesan(); 
echo $obj->apaKabar(); 

3. Multiple trait

Satu class boleh memakai lebih dari satu trait, cara memanggilnya dipisahkan oleh koma

<?php 
trait ApaKabar 
{ 
    public function apaKabar() { 
        return "Apa kabar?" . PHP_EOL; 
    } 
} 
 
trait SelamatPagi 
{ 
    public function selamatPagi() { 
        return "Selamat pagi?"; 
    } 
} 
 
class Pesan 
{ 
  // menggunakan trait lebih dari satu 
  use ApaKabar, SelamatPagi; 
} 
 
$obj = new Pesan(); 
echo $obj->apaKabar(); 
echo $obj->selamatPagi(); 

4. Sebuah trait tersusun dari banyak trait

Sebuah trait boleh tersusun dari trait yang telah ada

<?php 
// trait pertama 
trait ApaKabar 
{ 
    public function apaKabar() { 
        // PHP_EOL adalah  
        // garis baru (enter) lintas platform 
        return "Apa kabar?" . PHP_EOL; 
    } 
} 
 
// trait kedua 
trait SelamatPagi 
{ 
    public function selamatPagi() { 
        return "Selamat pagi?"; 
    } 
} 
 
// trait ketiga,  
trait KabarSelamat 
{   
    // tersusun dari kedua trait di atas 
    use ApaKabar, SelamatPagi; 
} 
 
class Pesan 
{ 
  // memakai trait yang  
  // tersusun dari trait 
  use KabarSelamat; 
} 
 
$obj = new Pesan(); 
echo $obj->apaKabar(); 
echo $obj->selamatPagi(); 

5. Trait tidak bisa dijadikan object (diinstansiasi)

Jika ini tetap dijalankan maka akan keluar error: PHP Fatal error: Uncaught Error: Cannot instantiate trait ApaKabar ...

<?php 
trait ApaKabar 
{ 
    public function apaKabar() { 
        return "Apa kabar?" . PHP_EOL; 
    } 
} 
 
$obj = new ApaKabar(); 

5. Urutan prioritas method

Urutan prioritas method dalam trait memiliki dua aturan:

  1. Method turunan akan ditimpa oleh method yang berasal dari trait
  2. Current class method akan menimpa method yang berasal dari trait

Ini contoh untuk aturan pertama

<?php 
class ApaKabar 
{   
    // ini akan diturunkan 
    public function apaKabar() { 
        return "Apa kabar?" . PHP_EOL; 
    } 
} 
 
trait SelamatPagi 
{ 
    public function selamatPagi() { 
        return "Selamat pagi?"; 
    } 
 
    // apaKabar() dari trait 
    public function apaKabar() { 
        return "Apa kabar pagi ini?" . PHP_EOL; 
    }  
} 
 
class Pesan extends ApaKabar 
{ 
    use SelamatPagi;   
} 
 
$pesan = new Pesan(); 
// yang dipakai adalah apaKabar() dari trait 
echo $pesan->apaKabar(); 
// jika dijalankan akan keluar 
// Apa kabar pagi ini? 

Class Pesan adalah turunan dari class ApaKabar yaitu dengan meng-extends ApaKabar dan class Pesan juga menggunakan trait SelamatPagi yaitu dengan use SelamatPagi.

Baik class ApaKabar maupun trait SelamatPagi sama-sama memiliki method apaKabar() tetapi isinya berbeda, yaitu Apa kabar? dan Apa kabar pagi ini?.

Dengan kondisi seperti ini ketika object $pesan memanggil method apaKabar() maka apaKabar() yang didahulukan adalah apaKabar() dalam trait yang berisi Apa kabar pagi ini?

Ini contoh untuk aturan kedua:

<?php 
trait ApaKabar 
{ 
    // apaKabar() dari trait 
    public function apaKabar() { 
        return "Apa kabar?"; 
    } 
} 
 
class Pesan 
{ 
    use ApaKabar; 
    // apaKabar() dari current class 
    public function apaKabar() { 
      return "Apa kabarnya sekarang?"; 
    } 
} 
 
$pesan = new Pesan(); 
// yang dipakai adalah apaKabar() dari class Pesan 
// bukan apaKabar() dari trait ApaKabar 
echo $pesan->apaKabar(); 
// coba jalankan maka akan keluar 
// Apa kabarnya sekarang? 

6. Menangani konflik

Ketika sebuah class menggunakan trait lebih dari satu, kita bisa saja menemukan kondisi dimana trait yang berbeda menggunakan nama method yang sama, seperti contoh ini

<?php 
trait Selamat 
{ 
    public function salam() { 
        return "Selamat pagi?"; 
    } 
} 
 
trait Sapaan 
{ 
    public function salam() { 
        return "Assalamu'alaikum?"; 
    } 
} 
 
class Pesan 
{ 
    use Selamat, Sapaan;   
} 
 
$pesan = new Pesan(); 
echo $pesan->salam(); 

Ketika kode program di atas tetap dijalankan maka akan keluar fatal error. Lalu bagaimana agar tetap bisa dijalankan?

Terdapat dua cara:

  1. Menyingkirkan method yang tidak dibutuhkan diantara method-method yang sama menggunakan mengggunakan keyword insteadof.

  2. Tetap membawa method yang sama ke dalam class dengan diberikan alias untuk membedakannya yaitu menggunakan keyword as

Contoh untuk cara pertama:

<?php 
trait Ucapan 
{ 
    public function salam() { 
        return "Pagi....?"; 
    } 
} 
 
trait Selamat 
{ 
    public function salam() { 
        return "Assalamu'alaikum?"; 
    } 
} 
 
trait Sapaan 
{ 
    public function salam() { 
        return "Selamat pagi?"; 
    } 
} 
 
class Pesan 
{ 
    // mengeliminasi method salam() 
    // dari trait Ucapan dan Sapaan 
    // mengguanakan insteadof 
    use Ucapan, Selamat, Sapaan {     
        Selamat::salam insteadof Ucapan, Sapaan; 
    } 
} 
 
$pesan = new Pesan(); 
echo $pesan->salam(); 

jika kita perhatikan kode program di atas dalam class Pesan terdapat Selamat::salam insteadof Ucapan, Sapaan; yang artinya yang akan digunakan adalah method salam() dalam trait Selamat bukan dari trait Ucapan dan Sapaan.

Contoh untuk cara kedua:

<?php 
trait Ucapan 
{ 
    public function salam() { 
        return "Pagi....?"; 
    } 
} 
 
trait Selamat 
{ 
    public function salam() { 
        return "Assalamu'alaikum?" . PHP_EOL; 
    } 
} 
 
trait Sapaan 
{ 
    public function salam() { 
        return "Selamat pagi?"; 
    } 
} 
 
class Pesan 
{   
    // membuat alias menggunakan as 
    // krn ingin tetap memakai salam() 
    // dari trait Sapaan 
    use Ucapan, Selamat, Sapaan { 
        Selamat::salam insteadof Ucapan, Sapaan; 
        Sapaan::salam as salamKedua; 
    } 
} 
 
$pesan = new Pesan(); 
echo $pesan->salam(); 
echo $pesan->salamKedua(); 

jika kita perhatikan kode program di atas dalam class Pesan terdapat Sapaan::salam as salamKedua; artinya method salam() dalam trait Sapaan tetap dipakai tetapi diberikan alias salamKedua agar beda dengan salam() dari trait Selamat.

6. Trait boleh memiliki property

Trait boleh memiliki property tetapi class yang menggunakan trait tersebut tidak diizinkan mendefinisikan kembali property dengan nama yang sama.

Jika class tetap mendefinisikan ulang property dengan nama yang sama, selama tingkat visibilitas dan valuenya sama dengan property yang berada dalam trait maka akan keluar peringatan E_STRICT.

Jika tingkat visibilitas dan atau valuenya tidak sama dengan property yang berada dalam trait maka akan keluar fatal error.

<?php 
trait Selamat { 
    public $satu = 1; 
    protected $dua = 2; 
} 
 
class Pesan { 
    use Selamat; 
    public $satu = 1; // keluar peringatan 
    public $dua = 2; // Fatal error 
} 

7. Method dalam trait boleh mengakses method dan property dalam class yang memakainya meskipun private

<?php 
trait BukaPesan 
{ 
    protected function BukaPesan() { 
      return $this->pesan; 
    } 
} 
 
class PesanPrivate 
{ 
    use BukaPesan; 
    private $pesan = 'Ini pesan private'; 
 
    public function pesanPrivate() { 
      return $this->BukaPesan(); 
    } 
} 
 
$pesan = new PesanPrivate(); 
echo $pesan->pesanPrivate(); 
// bisa akses pesan private, coba praktekkan 

8. Trait boleh memiliki abstract method

Penggunaannya sama saja seperti dalam abstrct class

<?php 
trait Selamat{ 
    public function salam() { 
        echo 'Selamat pagi '. $this->getName(); 
    } 
    abstract public function getName(); 
} 
 
class Pesan { 
    private $name; 
    use Selamat; 
    public function getName() { 
        return $this->name; 
    } 
    public function setName($name) { 
        $this->name = $name; 
    } 
} 
 
$obj = new Pesan(); 
$obj->setName('Andi'); 
echo $obj->salam(); // Selamat pagi Andi 

9. Merubah visibility method

Method dalam trait boleh diubah visibility-nya oleh class menggunakan keyword as

<?php 
trait Selamat { 
    public function malam() { 
        echo 'Selamat malam!'; 
    } 
} 
 
// merubah visibility malam() 
class Pesan { 
    use Selamat { malam as protected; } 
} 
 
// malam() dibuat alias menjadi ucapanPrivate() 
// dan diubah visibility ke private 
// sementara malam() visibility-nya tidak berubah 
class PesanKedua { 
    use Selamat { malam as private ucapanPrivate; } 
} 

Banyak banget ya tulisannya? :stuck_out_tongue_closed_eyes: walaupun banyak tapi mudah difahami kan? :blush: