PHP နှင့် Magic Method များ

နိဒါန်း

                ကျွန်တော်တို့ အရင်ဆုံး PHP Program တစ်ခု ရေးကြည့်ရအောင် ...

<?php
class Dog {
public $name;
public function bark() {
echo "Bark! <br />";
}
}
$dog = new Dog();
$dog->name = "Aung Net";
$dog->color = "Black";
$dog->bark();
$dog->eat();
?>
view raw index.php hosted with ❤ by GitHub

                အပေါ်က  Program ကို Run ကြည့်တဲ့အခါမှာ $dog->color = “Black”; မှာ Error မတက်ပဲ $dog->eat() မှာတော့ အပေါ်မှာ method မရှိတဲ့အတွက် Error တက်လာပါလိမ့်မယ်။ တစ်ချက် စဉ်းစားစရာရှိတာက အဲဒီလို အခြေအနေမျိုး လိုရော လိုအပ်ရဲ့လားဆိုတာပါ။

Late Properties and Method Bindings

                အဲဒီလို Properties တွေ၊ Methods တွေကို နောက်မှာ ပေါင်းထည့်တဲ့ ပုံစံကို Late Binding လို့ ခေါ်ပါတယ်။ Framework တွေ တည်ဆောက်တဲ့အခါမှာ အဲဒီလို Properties တွေ Methods တွေကို နောက်ဆက်ရေးမယ့်သူတွေအတွက် ပေါင်းထည့်လို့ရအောင် လမ်းဖွင့်ပေးထားရတာမျိုး ရှိပါတယ်။ အဲဒီလို လမ်းဖွင့်ပေးနိုင်ဖို့အတွက် Late Binding က လိုအပ်ပါတယ်။

Magic Method ဆိုတာ ဘာလဲ?

                PHP မှာ Magic Method ဆိုတာ အခြေအနေတစ်ခုအပေါ်မှာ မူတည်ပြီး လုပ်ဆောင်တဲ့ Method တွေကို ခေါ်ပါတယ်။ အဲဒီ Magic Method တွေကို ပုံမှန် Method တွေ ခေါ်သုံးလို သုံးလို့ မရပါဘူး။ အခြေအနေတစ်ခုခုအပေါ်မှာ မူတည်ပြီး အဲဒီ အခြေအနေ ဖြစ်လာမှသာ အလိုအလျှောက် လုပ်ဆောင်ပါတယ်။

                အပေါ်မှာ ကျွန်တော်ပြောခဲ့တဲ့ $dog->color ဆိုတဲ့ propery တစ်ခု နောက်မှ ထည့်လိုက်တာဖြစ်ဖြစ်၊ $dog->eat() ဆိုတဲ့ method တစ်ခု နောက်မှ ထည့်လိုက်တာ ဖြစ်ဖြစ် အခြေအနေတစ်ခုကို ဖန်တီးလိုက်ပါတယ်။ အဲဒီ အခြေအနေအပေါ်မှာ မူတည်ပြီး သက်ဆိုင်တဲ့ Magic Method တွေ လုပ်ဆောင်ပါတယ်။

__set() နှင့် __get()

                ကျွန်တော် အပေါ်မှာ ပြောခဲ့သလို Dog class မှာ မပါတဲ့ $color ဆိုတဲ့ property ကို Late Binding လုပ်မယ်ဆိုပါစို့။ အပေါ်မှာ မသတ်မှတ်ထားဘူးဆိုရင် __set() ဆိုတဲ့ Magic Method ကို ထလုပ်ဆောင်ပါတယ်။ ကျွန်တော်တို့ Program လေးတွေ ရေးကြည့်ရအောင်

<?php
class Dog {
public $name;
public $data = [];
public function bark() {
echo "Bark! <br />";
}
public function __set($key, $value) {
$this->data[$key] = $value;
}
public function __get($key) {
if(array_key_exists($key, $this->data)) {
return $this->data[$key];
} else {
trigger_error("Your key and value does not set", E_USER_ERROR);
}
}
}
$dog = new Dog();
$dog->name = "Aung Net";
$dog->color = "Black";
echo $dog->color;
?>
view raw index.php hosted with ❤ by GitHub

                ကျွန်တော်တို့ ရေးလိုက်တဲ့ Program ကို လေ့လာကြည့်မယ်ဆိုရင်


                $dog->color = “Black”; လို့ ခေါ်လိုက်တဲ့အခါမှာ ကျွန်တော်တို့ တည်ဆောက်ထားတဲ့ class မှာ မပါပါဘူး။ အဲဒီလို class မှာ မပါတဲ့ property ကို ခေါ်လိုက်တယ်ဆိုတာနဲ့ __set($key, $value) ဆိုတဲ့ Magic Method ကို အလိုအလျှောက် လုပ်ဆောင်စေပါတယ်။ ကျွန်တော် အပေါ်မှာ ပြထားတဲ့ ပုံမှာ ဆက်စပ်မှုတွေကို တွေ့နိုင်ပါလိမ့်မယ်။

                $dog->color လို့ ပြန်ခေါ်လိုက်တဲ့အခါ __get($key) Magic Method ကို လှမ်းခေါ်ပါတယ်။ အဲဒါကြောင့် __set($key, $value) နဲ့ __get($key) ကို တွဲပြီး ကျွန်တော်တို့ မြင်လေ့ရှိပါတယ်။

__call() နှင့် __callStatic()

                ကျွန်တော် အပေါ်ဆုံးမှာ ရေးခဲ့တဲ့ Program ကို ပြန်ကြည့်ပါ။ $dog->eat() ဆိုပြီး Class မှာ မသတ်မှတ်ထားတဲ့ Method ကို ခေါ်လိုက်ရင် Error တက်သွားပါတယ်။ အဲဒီလို အပေါ်မှာ မသတ်မှတ်ထားတဲ့ Method ကို Late Binding လုပ်ချင်တယ်ဆိုရင် __call() ဆိုတဲ့ Magic Method ကို သုံးလို့ရပါတယ်။ ကျွန်တော်တို့ Program ဆက်ရေးကြည့်ရအောင် ...

<?php
class Dog {
public function bark() {
echo "Woof! <br>";
}
public function __call($method, $arguments) {
var_dump($method);
var_dump($arguments);
}
public static function __callStatic($method, $arguments) {
var_dump($method);
var_dump($arguments);
}
}
Dog::dance();
$dog = new Dog();
$dog->bark();
$dog->eat("bone");
?>
view raw index.php hosted with ❤ by GitHub

                အဲဒီ  Program မှာ $dog->eat(“bone”); ဆိုတဲ့ method က အပေါ်က class မှာ မသတ်မှတ်ထားပါဘူး။ 

                အဲဒီလို မသတ်မှတ်ထားပဲ $dog->eat(“bone”) လို့ ခေါ်လိုက်တဲ့အခါ __call($method, $arguments) လို အလိုအလျှောက် လှမ်းခေါ်ပါတယ်။

                နောက်တစ်ခုက Dog::dance() ပါ။ သူကတော့ static method ဖြစ်တဲ့အတွက် __callStatic($method, $arguments) ကို အလိုအလျှောက်လှမ်းခေါ်ပါတယ်။ အပေါ်ကနဲ့ မတူတဲ့အချက်က သူက Static Method တွေအတွက် သုံးတာ ဖြစ်ပြီး အပေါ်ကတော့ Object Level သုံးတာ ဖြစ်ပါတယ်။

__construct() နှင့် __destruct()

                ဒီ Magic Method နှစ်ခုကတော့ အားလုံးရင်းနှီးပြီး ဖြစ်မှာပါ။ တစ်ခုက Constructor ဖြစ်ပြီး တစ်ခုက Destructor ဖြစ်ပါတယ်။ ကျွန်တော်တို့ Program လေးနဲ့ စမ်းကြည့်ရအောင်

<?php
class Dog {
private $name;
public function __construct($name) {
echo "object constructed! <br>";
$this->name = $name;
}
public function bark() {
echo "Bark! <br >";
}
public function __destruct() {
echo "object destructed! <br>";
}
}
$dog = new Dog("Arnold");
$dog->bark();
?>
view raw index.php hosted with ❤ by GitHub

                $dog = new Dog(“Arnold”); ဆိုတာနဲ့ __construct() ကို လှမ်းခေါ်ပါတယ်။ object ထဲက $name ကို “Arnold” နဲ့ လှမ်း assign လုပ်လိုက်ပါတယ်။ __construct() ကို object အသစ်တစ်ခု new keyword နဲ့ ဆောက်လိုက်တာနဲ့ လှမ်းခေါ်ပြီး အလိုအလျှောက် လုပ်ဆောင်စေပါတယ်။ __destruct() ကတော့ object ကို script အနေနဲ့ ရှေ့ဆက်မလိုတော့တဲ့အချိန် (သို့) unset လုပ်လိုက်တဲ့အချိန်မှာ အလိုအလျှောက် လုပ်ဆောင်ပါတယ်။ ကျွန်တော်တို့ PHP အနေနဲ့ Garbage Collection အပိုင်းမှာ Automatic Reference Counting (ARC) ကို သုံးတာ ဖြစ်တဲ့အတွက် ကျွန်တော်တို့ ရေးလိုက်တဲ့ PHP Srcript တွေကို OPCode ပြောင်းတဲ့အခါ Memory က ရှင်းလင်းတဲ့ ကုဒ်တွေ စိစစ်ပေါင်းထည့်လေ့ရှိပါတယ်။ အများအားဖြင့် နောက်ဆုံးမှာ ပေါင်းထည့်တာပါ။ Object တစ်ခု Memory ပေါ်က ဖျက်တဲ့အချိန်မှာ __destruct() ကို အလိုအလျှောက် ခေါ်ပါတယ်။

__isset() နှင့် __unset()

                __isset() နဲ့ __unset() နဲ့ __set() တို့ __get() တို့ဆိုတာ Property Overloading အတွက် သုံးတဲ့ Magic Method တွေ ဖြစ်ပါတယ်။  __set() နဲ့ __get() ကို အပေါ်မှာ လေ့လာခဲ့ပြီး ဖြစ်တဲ့အတွက် ဒီနေရာမှာတော့ __isset() နဲ့ __unset()  ကို လေ့လာကြပါမယ်။ ကျွန်တော်တို့ ထုံးစံအတိုင်း Program လေး ရေးကြည့်ရအောင်။ 

<?php
class Dog {
public $data = [];
public function __set($property, $value) {
$this->data[$property] = $value;
}
public function __isset($property) {
echo "isset triggered! <br>";
return isset($this->data[$property]);
}
public function __unset($property) {
echo "unset triggered! <br>";
unset($this->data[$property]);
}
}
$dog = new Dog();
var_dump(isset($dog->color));
$dog->color = "Black";
var_dump(isset($dog->color));
unset($dog->color);
var_dump(isset($dog->color));
?>
view raw index.php hosted with ❤ by GitHub

                ကျွန်တော်တို့ __isset() နဲ့ __unset() ဟာ __set() တို့ __get() တို့နဲ့ ဆက်စပ်နေပါတယ်။ ကျွန်တော်တို့ Program ကို Run ကြည့်မယ်ဆိုရင် အောက်မှာပြထားတဲ့အတိုင်း Result ထွက်လာပါလိမ့်မယ်။

 

                var_dump(isset($dog->color)); ဆိုရင် isset triggered! ဆိုတာ တွေ့ရတဲ့အတွက် object ထဲမှာ ရှိတဲ့ __isset() magic method ကို လုပ်ဆောင်သွားတယ်ဆိုတာ သတိထားမိပါလိမ့်မယ်။ ဒါပေမယ့် $dog->color ကို မသတ်မှတ်ရသေးတဲ့အတွက် false ဆိုပြီး ပြန်ပေးပါလိမ့်မယ်။ နောက်တစ်ကြိမ်မှာတော့ $dog->color ကို သတ်မှတ်လိုက်တဲ့အတွက် true ဆိုပြီး ပြပါလိမ့်မယ်။ 

                unset($dog->color) ဆိုပြီး ရေးလိုက်တဲ့အတွက် object ထဲမှာ ရှိတဲ့ __unset() magic method ကို လုပ်ဆောင်တယ်ဆိုတာ သတိထားမိပါလိမ့်မယ်။ object ထဲက property ကို unset လုပ်လိုက်တဲ့အတွက် property မရှိတော့ပါဘူး။ နောက်တစ်ခါ isset နဲ့ run ကြည့်တဲ့အချိန်မှာ false ဖြစ်သွားပါလိမ့်မယ်။ 

__toString()

                toString ကတော့ Object ကို String Representation အနေနဲ့ ပြန်ပေးလို့ရပါတယ်။ Laravel မှာဆိုရင် Eloquent Object ကို son representation နဲ့ ပြန်ပေးပါတယ်။ ကျွန်တော်တို့ code ရေးပြီး စမ်းကြည့်ရအောင် ...

<?php
class Dog {
public $test = "Hello from Dog Class";
public function __toString() {
return $this->test;
}
}
$dog = new Dog();
echo $dog;
?>
view raw index.php hosted with ❤ by GitHub

                ပုံမှန်အတိုင်းဆိုရင် echo $dog; ဆိုပြီး ခေါ်လို့ မရပါဘူး။ var_dump() ဖြစ်ဖြစ် သုံးမှ Object ကို ရိုက်ထုတ်လို့ရပါတယ်။ အဲဒီအပြင် var_dump နဲ့ ရိုက်ရင် Complex Object Structure တွေမှာ ကိုယ်လိုချင်တာကို ရှင်းရှင်းလင်းလင်း မြင်ရဖို့ မလွယ်ပါဘူး။ အဲဒီအတွက် ကိုယ်မြင်ချင်တာ ကိုယ်သိချင်တာကိုပဲ ရှင်းရှင်းလင်းလင်း သိနိုင်ဖို့အတွက် __toString() Magic Method ကို သုံးနိုင်ပါတယ်။

__sleep() and __wakeup()

                __sleep() နဲ့ __wakeup() က ကျွန်တော်တို့အတွက် အရမ်းအသုံးဝင်တဲ့ Magic Method တွေ ဖြစ်ပါတယ်။ ကျွန်တော်တို့ PDO လို Database Object တွေကို Connect လုပ်တယ်၊ Disconnect လုပ်တဲ့နေရာမှာ အရမ်း အသုံးဝင်ပါတယ်။ ကျွန်တော်တို့ Program ရေးပြီး စမ်းကြည့်ရအောင် ...

<?php
class Connection {
protected $link;
private $dsn, $username, $password;
public function __construct($dsn, $username, $password) {
$this->dsn = $dsn;
$this->username = $username;
$this->pasword = $password;
$this->connect();
}
private function connect() {
$this->link = new PDO($this->dsn, $this->username, $this->password);
}
private function disconnect() {
unset($this->link);
}
public function __sleep() {
echo "It's time to sleep! <br>";
$this->disconnect();
return ['dsn', 'username', 'password'];
}
public function __wakeup() {
echo "It's time to wake up! <br>";
$this->connect();
}
}
$connection = new Connection("mysql:host=localhost;dbname=wpa26data", "root", "");
$con = serialize($connection);
var_dump($con);
unserialize($con);
?>
view raw index.php hosted with ❤ by GitHub

                $con = serialize($connection) ဆိုတာနဲ့ object ထဲက __sleep() ကို ခေါ်လိုက်ပါတယ်။ အဲဒီမှာ မဖြစ်မနေ array return ပြန်ပေးဖို့ လိုပါတယ်။ ကျွန်တော်ကတော့ [‘dsn’ , ‘username’, ‘password’] ဆိုပြီး ပြန်ပေးလိုက်ပါတယ်။ အဲဒါတွေက အပေါ်မှာ သတ်မှတ်ထားတဲ့ private properties တွေ ဖြစ်ပါတယ်။ 

                var_dump($con) ဆိုပြီး ရိုက်ကြည့်မယ်ဆိုရင် ကျွန်တော်တို့ သတ်မှတ်လိုက်တဲ့ new Connection("mysql:host=localhost;dbname=wpa26data", "root", “"); က တန်ဖိုးတွေပါ Serialize လုပ်ထားတာ တွေ့ရပါလိမ့်မယ်။ 

                unserialize($con) ကတော့ ပြန်ဖြည်လိုက်တာပါ။ အဲဒီလို ပြန်ဖြည်လိုက်တာနဲ့ တစ်ပြိုင်တည်း __wakeup() ဆိုတဲ့ Magic Method ကို run ပါတယ်။ ကျွန်တော်တော့ အဲဒီမှာ Database Connection ကို ပြန်ဖွင့်လိုက်ပါတယ်။ 

နိဂုံး

                ကျွန်တော် အခုရေးပြထားတာတွေက ကျွန်တော် သုံးနေကြ Magic Method တွေ ဖြစ်ပါတယ်။ တစ်ခြား Magic Method တွေလဲ ရှိပါသေးတယ်။ __clone(), __debugInfo(), __invoke(), __set_state() ဆိုတာတွေ ဖြစ်ပါတယ်။ __debugInfo() ကတော့ သုံးဖြစ်ပေမယ့် ကျန်တာတွေက သိပ်မသုံးဖြစ်ပါဘူး။ သူ့နေရာနဲ့သူ အသုံးတည့်မှာ ဖြစ်တဲ့အတွက် လေ့လာကြည့်ကြဖို့ တိုက်တွန်းပါတယ်။