<?php
/**
 * 【單例模式】
 * 總結:防止外部new物件;防止子類繼承;防止克隆。
 */

header("Content-type: text/html; charset=utf-8");

/******* NO.1 普通的可以呼叫的類 ******/
//先定義一個類,例項化兩次,看看是否全等(===)
class sigleA{

}
$sa1 = new sigleA();
$sa2 = new sigleA();
//注意:當兩個物件是一個的時候,才會全等
//此處輸出 “sa-不是一個物件”
if($sa1 === $sa2){
    echo 'sa-是一個物件<br>';
}else{
    echo 'sa-不是一個物件<br>';
}

/********************* NO.2 封鎖new操作 ********************/
//getIns 預先判斷例項
class sigleB{
    protected static $ins = null;
    public static function getIns(){ //getIns 獲取例項
        if(self::$ins === null){
            //把【自身物件】 賦給 一個自己的 【靜態屬性】
            self::$ins = new self();
        }
        return self::$ins;
    }
    //保護的 建構函式
    protected function __construct(){

    }
}
//再去判斷兩個物件
$sb1 = sigleB::getIns();
$sb2 = sigleB::getIns();
//此處輸出 “sb-是一個物件”
if($sb1 === $sb2){
    echo 'sb-是一個物件<br>';
}else{
    echo 'sb-不是一個物件<br>';
}

////----- 但是,下面用一個新的類繼承自上面的 sigleB ...
class multi extends sigleB{
    public function __construct(){

    }
}
$m1 = new multi();
$m2 = new multi();
//此處輸出 “m-不是一個物件”,原因在於被繼承後,__construct 被公開了
if($m1 === $m2){
    echo 'm-是一個物件<br>';
}else{
    echo 'm-不是一個物件<br>';
}

/********************* NO.3 final防止繼承 ********************/
class sigleC{
    protected static $ins = null;
    public static function getIns(){
        if(self::$ins === null){
            self::$ins = new self();
        }
        return self::$ins;
    }
    //若方法前加上final,則方法不能被覆蓋;
    //若類前加上final,則類不能被繼承
    final protected function __construct(){

    }
    //同樣的,防止克隆(clone)
    final protected function __clone(){

    }
}
$sc1 = sigleC::getIns();
$sc2 = clone $sc1;
//被克隆了,又產生了多個物件;
//如果沒加上 防止克隆的程式碼,此處輸出 “sc-不是一個物件”

//上面加上了 final protected function __clone(),這裡就會出錯:
//Fatal error: Call to protected sigleC::__clone() from context '' in ...
if($sc1 === $sc2){
    echo 'sc-是一個物件<br>';
}else{
    echo 'sc-不是一個物件<br>';
}