1. 程式人生 > >php5 面向物件總結(類與物件)

php5 面向物件總結(類與物件)


1. PHP 類與物件
 2. PHP 類的繼承 extends 關鍵字
 3. PHP 構造方法 __construct()
 4. PHP 析構方法 __destruct()
 5. PHP final 關鍵字
 6. PHP 類的介面 interface 與 implements 關鍵字
 7. PHP 類的訪問控制與封裝 public,protected,private 修飾符
 8. PHP 自動載入類 __autoload() 方法
 9. 範圍解析操作符(::)
 10. PHP 類的靜態成員屬性與靜態方法 static 關鍵字
 11. PHP 常量(const)
 12. PHP 特殊方法 __set()、__get()、__isset() 與 __unset()
 13. PHP 過載
 14. PHP 過載方法 __call()
 15. PHP 抽象方法與抽象類 abstract 關鍵字
 16. PHP 物件克隆 clone 關鍵字與 __clone() 方法
 17. PHP 物件的儲存與傳輸(序列化 serialize 物件)
    18. php 設計模式:工廠模式和單例模式
    19.物件物件迭代
    20 this關鍵字


類與物件
物件:實際存在該類事物中每個實物的個體。$a =new User(); 例項化後的$a
引用:php的別名,兩個不同的變數名字指向相同的內容

封裝: 把物件的屬性和方法組織在一個類(邏輯單元)裡
繼承:以原有的類為基礎,建立一個新類,從而程式碼複用的目的;
多型:

-------------------------------------
2自動載入物件:

自動載入通過定義特殊的__autoload函式,當引用沒有在指令碼中定義的類時會自動呼叫這個函式.
function __autoload($class){
  require_once("classes/$class.class.php");
}

為什麼要使用__autoload

1,首先是不知道這個類檔案存放在什麼地方,
2,另外一個就是不知道什麼時候需要用到這個檔案。
3,特別是專案檔案特別多時,不可能每個檔案都在開始的部分寫很長一串的 require …


替代了一
require_once ("classes/Books.class.php") ;
require_once ("classes/Employees.class.php" ) ;
require_once ("classes/Events.class.php") ;
require_once ("classes/Patrons.class.php") ;

zend推薦了一種最流行的辦法,在檔名中包含路徑。例如下面的例子:

view sourceprint?
// Main.class 

function __autoload($class_name) {  
     $path = str_replace('_', DIRECTORY_SEPARATOR, $class_name);  
     require_once $path.'.php';  
 } 
    

 $temp = new Main_Super_Class();

所有的下劃線都會被替換成路徑中的分隔符,上例中就會去 Main/Super/Class.php檔案。

缺點:

是在編碼過程中,必須明確的知道程式碼檔案應當所處的位置,

而且由於將檔案路徑硬編碼在了類名中,如果需要修改資料夾的結構時,我們必須手工修改所有的類名。

如果是在一個開發環境中,並且對於速度不是很在意的話,使用'Include All’這個方法是非常方便的。
通過將所有類檔案放在一個或幾個特定資料夾中,然後通過遍歷的方式查詢載入。
例如
<?php  

$arr = array (  
     'Project/Classes',  
    'Project/Classes/Children',  
    'Project/Interfaces' 
 ); 

 foreach($arr as $dir) {  

    $dir_list = opendir($dir); 

    while ($file = readdir($dir_list)) {  
         $path = $dir.DIRECTORY_SEPARATOR.$file;  
         if(in_array($file, array('.', '..')) || is_dir($path))  
             continue; 
         if (strpos($file, ".class.php"))  
             require_once $path;  
     }  
}  

 ?>
另外一個方法是在類檔案和他的位置之間建立關聯的配置檔案,例如:

view sourceprint?
// configuration.php  

array_of_associations = array(  
    'MainSuperClass' = 'C:/Main/Super/Class.php',  
    'MainPoorClass' = 'C:/blablabla/gy.php' 
 );
呼叫的檔案

 <?php  
    require 'autoload_generated.php'; 
    function __autoload($className) {  
       global $autoload_list;  
       require_once $autoload_list[$className];  
    } 
      $x = new A();  
?>
------------------------------------------------
3建構函式和解構函式

PHP 構造方法 __construct() 允許在例項化一個類之前先執行構造方法。

構造方法是類中的一個特殊方法。當使用 new 操作符建立一個類的例項時,構造方法將會自動呼叫,其名稱必須是 __construct() 。

(在一個類中只能宣告一個構造方法,而是隻有在每次建立物件的時候都會去呼叫一次構造方法,不能主動的呼叫這個方法,
所以通常用它執行一些有用的初始化任務。該方法無返回值。)

作用: 用來建立物件時初始化物件
子類執行分類的建構函式parent::__construct().

解構函式: __destruct ()定義:特殊的內成員函式,沒有返回型別,沒有引數,不能隨意呼叫,也沒有過載;
        只是在類物件生命結束的時候,由系統自動呼叫釋放在建構函式中分配的資源。

  與構造方法對應的就是析構方法,析構方法允許在銷燬一個類之前執行的一些操作或完成一些功能,比如說關閉檔案、釋放結果集等。
  解構函式不能帶有任何引數,其名稱必須是 __destruct() 。
作用:清理了善後工作,例如,在建立物件時使用new 開闢了一個記憶體空間,應在退出前使用解構函式釋放在建構函式中分配的資源。

例子:
class Person {
    public $name;
    public $age;

    //定義一個構造方法初始化賦值
    public function __construct($name,$age) {
        $this->name=$name;
        $this->age=$age;
    }
    public function say() {
        echo "my name is :".$this->name."<br />";
        echo "my age is :".$this->age;
    }
    //解構函式
    function __destruct()
    {
        echo "goodbye :".$this->name;
    }
}

$p1=new Person("ren", 25);
$p1->say();


---------------------------------------------------------------
4 訪問控制
對屬性或方法的訪問控制,是通過在前面新增關鍵字 public、protected 或 private 來實現的
 public 所定義的類成員可以在任何地方被訪問;
 protected 所定義的類成員則可以被其所在類的子類和父類訪問(當然,該成員所在的類也可以訪問);
 private 定義的類成員則只能被其所在類訪問。
對類成員的訪問控制
 類成員都必須使用關鍵字public、protected 或 private 進行定義

對方法的訪問控制
類中的方法都必須使用關鍵字public、protected 或 private 進行定義。如果沒有設定這些關鍵字,則該方法會被設定成預設的 public。

例子:
class MyClass
{
    public $public = 'Public';
    protected $protected = 'Protected';
    private $private = 'Private';

    function printHello()
    {
        echo $this->public;
        echo $this->protected;
        echo $this->private;
    }
}

$obj = new MyClass();
echo $obj->public; // 這行能被正常執行
echo $obj->protected; // 這行會產生一個致命錯誤
echo $obj->private; // 這行也會產生一個致命錯誤
$obj->printHello(); // 輸出 Public、Protected 和 Private

-------------------------------------------------------------
5 物件繼承
    繼承定義:以原有的類為基礎,建立一個新類,從而程式碼複用的目的;
       

繼承已為大家所熟知的一個程式設計特性,PHP 的物件模型也使用了繼承。繼承將會影響到類與類,物件與物件之間的關係。

比如,當擴充套件一個類,子類就會繼承父類的所有公有和保護方法。但是子類的方法會覆蓋父類的方法。

繼承對於功能的設計和抽象是非常有用的,而且對於類似的物件增加新功能就無須重新再寫這些公用的功能。


class Person {
    public $name;
    public $age;

    function say() {
        echo "my name is:".$this->name."<br />";
    echo "my age is:".$this->age;
    }
}

// 類的繼承
class Student extends Person {
    var $school;    //學生所在學校的屬性
   
    function study() {
        echo "my name is:".$this->name."<br />";
        echo "my shool is:".$this->school;
    }      
}

$t1 = new Student();
$t1->name = "zhangsan";
$t1->school = "beijindaxue";
$t1->study();
-------  ---------  ------   ---------  --------   -----
6 範圍解析操作符(::)

範圍解析操作符(也可稱作 Paamayim Nekudotayim)或者更簡單地說是一對冒號,可以用於訪問靜態成員、方法和常量,還可以用於覆蓋類中的成員和方法。
當在類的外部訪問這些靜態成員、方法和常量時,必須使用類的名字。

self 和 parent 這兩個特殊的關鍵字是用於在類的內部對成員或方法進行訪問的。
注意:
當一個子類覆蓋其父類中的方法時,PHP 不會再執行父類中已被覆蓋的方法,直到子類中呼叫這些方法為止


例子:
<?php
class OtherClass extends MyClass
{
    public static $my_static = 'static var';

    public static function doubleColon() {
        echo parent::CONST_VALUE . "\n";
        echo self::$my_static . "\n";
    }
}

OtherClass::doubleColon();
?>

---------------------------------------------------
7 Static關鍵字
宣告類成員或方法為static,就可以不例項化類而直接訪問。不能通過一個物件來訪問其中的靜態成員(靜態方法除外)。

靜態成員屬於類,不屬於任何物件例項,但類的物件例項都能共享。
小結:

在類內部訪問靜態成員屬性或者方法,使用 self::(沒有 $ 符號),如:
 slef:: $country  //類內部訪問靜態成員屬性
 slef:: myCountry()
在子類訪問父類靜態成員屬性或方法,使用 parent::(沒有 $ 符號),如:
 parent:: $country
 parent:: myCountry()
外部訪問靜態成員屬性和方法為 類名/子類名:: ,如:
 Person::$country
 Person::myCountry()
 Student::$country
但靜態方法也可以通過普通物件的方式訪問


<?php

Class Person{
    // 定義靜態成員屬性
    public static $country = "中國";
    // 定義靜態成員方法
    public static function myCountry() {
        // 內部訪問靜態成員屬性
        echo "我是".self::$country."人<br />";
    }
}
class Student extends Person {
    function study() {
        echo "我是". parent::$country."人<br />";
    }
}
// 輸出成員屬性值
echo Person::$country."<br />";     // 輸出:中國
$p1 = new Person();
//echo $p1->country;            // 錯誤寫法
// 訪問靜態成員方法
Person::myCountry();            // 輸出:我是中國人
// 靜態方法也可通過物件訪問:
$p1->myCountry();

// 子類中輸出成員屬性值
echo Student::$country."<br />";    // 輸出:中國
$t1 = new Student();
$t1->study();           // 輸出:我是中國人

?>
---------------------------------------------------
8 抽象類    PHP5支援抽象類和抽象方法。
      抽象類不能直接被例項化,你必須先繼承該抽象類,然後再例項化子類。
      抽象類中 至少要包含一個抽象方法。如果類方法被宣告為抽象的,那麼其中就不能包括具體的功能實現。
      繼承一個抽象類的時候,子類必須實現抽象類中的所有抽象方法;
      另外,這些方法的可見性 必須和抽象類中一樣(或者更為寬鬆)。
      如果抽象類中某個抽象方法被宣告為protected,那麼子類中實現的方法就應該宣告為protected或者public,而不 能定義為private。
      //抽象方法:abstract protected function getValue();
例子1
abstract class AbstractClass{
    // 定義抽象方法
    abstract protected function getValue();
    // 普通方法
    public function printOut(){
        print $this->getValue()."<br />";
    }
}
class ConcreteClass extends AbstractClass{
    protected function getValue(){
        return "abstract ";//抽象方法的實現
    }
}

$class1 = new ConcreteClass;
$class1->printOut();

例子2
     
abstract class AbstractClass
{
 // 強制要求子類定義這些方法
    abstract protected function getValue();
    abstract protected function prefixValue($prefix);

    // 普通方法(非抽象方法)
    public function printOut() {
        print $this->getValue() . "\n";
    }
}

class ConcreteClass1 extends AbstractClass
{
    protected function getValue() {
        return "ConcreteClass1";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass1";
    }
}

class ConcreteClass2 extends AbstractClass
{
    public function getValue() {
        return "ConcreteClass2";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass2";
    }
}

$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') ."\n";

$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') ."\n";
/*
 * 抽象類不能直接被例項化,你必須先繼承該抽象類,然後再例項化子類。
      抽象類中 至少要包含一個抽象方法。如果類方法被宣告為抽象的,那麼其中就不能包括具體的功能實現。
      繼承一個抽象類的時候,子類必須實現抽象類中的所有抽象方法;
      另外,這些方法的可見性 必須和抽象類中一樣(或者更為寬鬆)。
      如果抽象類中某個抽象方法被宣告為protected,那麼子類中實現的方法就應該宣告為protected或者public,而不 能定義為private。
 *
 */
class Person {
    public $name;
    public $age;

    function say() {
        echo "my name is:".$this->name."<br />";
    echo "my age is:".$this->age;
    }
}

// 類的繼承
class Student extends Person {
    var $school;    //學生所在學校的屬性
   
    function study() {
        echo "my name is:".$this->name."<br />";
        echo "my shool is:".$this->school;
    }      
}

$t1 = new Student();
$t1->name = "zhangsan";
$t1->school = "beijindaxue";
$t1->study();

---------------------------------------------------------------------
9介面
 介面定義:方法和常量值定義的集合
              通過interface來定義一個介面,就像定義一個標準的類一樣,但其中定義所有的方法都是空的。
   
 介面的特性:介面中定義的所有方法都必須是public
 
 介面的實現:一個介面可以使用implements操作符,類中必須實現介面中的所有方法,否則會報fatal錯誤,如果要實現多個介面,可以使用逗號來分隔多個介面的名稱。

抽象類和介面的區別

介面是特殊的抽象類,也可以看做是一個模型的規範。介面與抽象類大致區別如下:

1.一個子類如果 implements 一個介面,就必須實現介面中的所有方法(不管是否需要);如果是繼承一個抽象類,只需要實現需要的方法即可。
2.如果一個介面中定義的方法名改變了,那麼所有實現此介面的子類需要同步更新方法名;而抽象類中如果方法名改變了,其子類對應的方法名將不受影響,只是變成了一個新的方法而已(相對老的方法實現)。
3.抽象類只能單繼承,當一個子類需要實現的功能需要繼承自多個父類時,就必須使用介面。
例項1:
// 宣告一個'iTemplate'介面
interface iTemplate
{
    public function setVariable($name, $var);
    public function getHtml($template);
}

// 實現介面
// 下面的寫法是正確的
class Template implements iTemplate
{
    private $vars = array();
 
    public function setVariable($name, $var)
    {
        $this->vars[$name] = $var;
    }
 
    public function getHtml($template)
    {
        foreach($this->vars as $name => $value) {
            $template = str_replace('{' . $name . '}', $value, $template);
        }
 
        return $template;
    }
}

例項2:
//定義介面
 interface User{
     function getDiscount();
     function getUserType();
 }
//VIP使用者 介面實現
 class VipUser implements User{
     // VIP 使用者折扣係數
     private $discount = 0.8;
     function getDiscount() {
         return $this->discount;
     }
     function getUserType() {
         return "VIP user";
     }
 }
 class Goods{
     var $price = 100;
     var $vc;
     //定義 User 介面型別引數,這時並不知道是什麼使用者
     function run(User $vc){
         $this->vc = $vc;
         $discount = $this->vc->getDiscount();
         $usertype = $this->vc->getUserType();
         echo $usertype."goods Price:".$this->price*$discount;
     }
 }

$display ->run(new VipUser);    //可以是更多其他使用者型別
-------------------------------------------------------------
10 過載
   定義:一個類中的方法與另一個方法名稱相同,但引數不同
   什麼情況下執行過載?  當呼叫當前的環境下未被定義的屬性或者方法時,或者當呼叫當前環境下不可見的屬性或方法。
  
提示:
 如果父類定義方法時使用了 final 關鍵字,則不允許被子類方法覆蓋。

 訪問父類被覆蓋的方法
 可以通過 :: 符號來訪問父類被覆蓋的方法或成員屬性:
 //PHP 過載方法 __call()

 __call()(Method overloading)
為了避免當呼叫的方法不存在時產生錯誤,可以使用 __call() 方法來避免。該方法在呼叫的方法不存在時會自動呼叫,程式仍會繼續執行下去。

語法:
// __call()方法過載

class Test{
    public function __call($name,$args){
     if($name== 'null' && count($args)==2 ){
      $type='num';
   foreach($args as $key => $val){
       if(!(is_int($val) || is_float($val))){
        $type= 'string';
    }
   }
   $method=$name.ucfirst($type);
   if(method_exists($this,$method),$args){
       call_user_func_array(array($this,$method),$args);
   }
  }
 }
 public addNum($i,$j){
     echo $i+$j;
 }
 
 public addString($i,$j){
     echo $i.$j;
 }
}
$test =new Test();
$test->add(3,4);
$test->add(3,'4');
 
案例:
class MemberTest {
     
    private $data = array();//被過載的資料儲存在此 
    public $declared = 1;/**  過載不能被用在已經定義的屬性  */
    private $hidden = 2; /**  只有從類外部訪問這個屬性時,過載才會發生 */

    public function __set($name, $value) {
        echo "Setting '$name' to '$value'\n";
        $this->data[$name] = $value;
    }

    public function __get($name) {
        echo "Getting '$name'\n";
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        }

        $trace = debug_backtrace();
        trigger_error(
            'Undefined property via __get(): ' . $name .
            ' in ' . $trace[0]['file'] .
            ' on line ' . $trace[0]['line'],
            E_USER_NOTICE);
        return null;
    }

    /**  PHP 5.1.0之後版本 */
    public function __isset($name) {
        echo "Is '$name' set?\n";
        return isset($this->data[$name]);
    }

    /**  PHP 5.1.0之後版本 */
    public function __unset($name) {
        echo "Unsetting '$name'\n";
        unset($this->data[$name]);
    }

    /**  非魔術方法  */
    public function getHidden() {
        return $this->hidden;
    }
}


echo "<pre>\n";

$obj = new MemberTest;

$obj->a = 1;
echo $obj->a . "\n\n";

var_dump(isset($obj->a));
unset($obj->a);
var_dump(isset($obj->a));
echo "\n";

echo $obj->declared . "\n\n";

echo "Let's experiment with the private property named 'hidden':\n";
echo "Privates are visible inside the class, so __get() not used...\n";
echo $obj->getHidden() . "\n";
echo "Privates not visible outside of class, so __get() is used...\n";
echo $obj->hidden . "\n";

//屬性過載:__set(),__get(),__isset(),__unset()

class Person{
    private $data =array();
    function __set($name,$value){
     $this->data[$name]=$value;
 }
 function __get($name){
     return $this->data[$name];
 }
}

-----------------------------------------------------------------------------------
11  物件迭代
       PHP5提供了一種迭代(iteration)物件的功能,就像使用陣列那樣,可以通過foreach 來遍歷物件中的屬性
      
    class MyClass
{
    public $var1 = 'value 1';
    public $var2 = 'value 2';
    public $var3 = 'value 3';

    protected $protected = 'protected var';
    private   $private   = 'private var';

    function iterateVisible() {
       echo "MyClass::iterateVisible:\n";
       foreach($this as $key => $value) {
           print "$key => $value\n";
       }
    }
}

$class = new MyClass();

foreach($class as $key => $value) {
    print "$key => $value\n";
}
echo "\n";


$class->iterateVisible();

---------------------------------------------------------------------------
12   設計模式: 工廠模式和 單例模式,    觀察者模式,命令鏈模式和策略模式

 命令鏈 模式以鬆散耦合主題為基礎,傳送訊息、命令和請求,或通過一組處理程式傳送任意內容。每個處理程式都會自行判斷自己能否處理請求。
如果可以,該請求被處理,程序停止。您可以為系統新增或移除處理程式,而不影響其他處理程式。


工廠模式
    定義:工廠模式(Factory)允許你在程式碼執行時例項化物件。它之所以被稱為工廠模式是因為它負責“生產”物件。工廠方法的引數是你要生成的物件對應的類名稱。
 
工廠模式語法:
<?php
class Example
{
    public static function factory($type)
    {
        if (include_once 'Drivers/' . $type . '.php') {
            $classname = 'Driver_' . $type;
            return new $classname;
        } else {
            throw new Exception ('Driver not found');
        }
    }
}
?>
   工廠模式案例:
<?php
interface IUser{
 function getName();
}
class User implements IUser{
 public function __construct($id){}
 public function getName(){
     return "haha";
 }
}
class UserFactory{
    public static function Create($id){
  return new User($id);
 }
}
$uo =UserFactory::Create(1);
echo $uo->getName();

?>   
  
單例
   定義三要素:1,某個類只能有一個例項  2,必須自行建立這個例項  3,必須自行向系統提供這個例項
  
   作用: 1,如果系統中需要有一個類來全域性控制某些配置資訊,那麼使用單例模式可以很方便的實現。
          2,使用單例模式,可以避免大量的new操作消耗資源()
          3在一個頁面請求中,便於除錯,因為所有的程式碼都集中在一個類中(如資料庫操作類) 可以在類中設定鉤子,輸出日誌,從而避免到處都是var_dump
    
單例模式(Singleton)用於為一個類生成一個唯一的物件。最常用的地方是資料庫連線。 使用單例模式生成一個物件後,該物件可以被其它眾多物件所使用。
單件模式是我們在開發中經常用到的一種設計模式,利用PHP5面向物件的特性,我們可以很容易的構建單件模式的應用,下面是單件模式在PHP中的幾種實現方法:

class Stat{
    static $instance = NULL;
   
    static function getInstance(){
        if(self::$instance == NULL){
            self::$instance = new Stat();
        }
       
        return self::$instance;
    }   
    private function __construct(){
    }   
    private function __clone(){
    }   
   
    function sayHi(){
        return "The Class is saying hi to u ";
    }
}


echo Stat::getInstance()->sayHi();

這是一種最通常的方式,在一個getInstance方法中返回唯一的類例項。

對這裡例子稍加修改,便可以產生一個通用的方法,只要叫道任何你想用到單件的類裡,就可以了。

class Teacher{
    function sayHi(){
        return "The teacher smiling and said 'Hello '";
    }
   
    static function getInstance(){
        static $instance;
       
        if(!isset($instance)){
            $c = __CLASS__;
            $instance = new $c;
        }       
        return $instance;
    }
}

echo Teacher::getInstance()->sayHi();

最後一種是提供一個singleton類,然後通過呼叫getInstance方法,可以為任何一個類生產出一個例項來。

class singleton{
    function getInstance($class){
        static $instances = array();
        if(!array_key_exists($class,$instances)){
            $instances[$class] = &new $class;
        }
        $instance = $instances[$class];
       
        return $instance;
    }
}

class People{
    function sayHi(){
        return 'Hello i am a people?';
    }
}

echo "<br />";
echo singleton::getInstance('People')->sayHi();

通過這三種方法,我們可以很容易的應用單件模式,如果能夠結合工廠模式,將使我們的程式設計變得更有條理和效率。
---------------------------------------------------------------------------------------
13  魔術方法
  定義:PHP把所有以__(兩個下劃線)開頭的類方法當成魔術方法
 
  __construct, __destruct (參看 構造方法和析構方法),
  __call, __callStatic, __get, __set, __isset, __unset (參看 過載),
  __sleep, __wakeup, __toString, __set_state 和 __clone 等方法在PHP中被稱為“魔術方法”(Magic methods)。
  你在命名自己的類方法時不能使用這些方法名。
 
  serialize()
  作用: 第一. 在序列化之前,關閉物件可能具有的任何資料庫連線等.
        第二. 指定物件中需要被序列化的成員屬性,如果某個屬性比較大而不需要儲存下來,可以不把它寫進__sleep要返回的陣列中,這樣該屬性就不會被序列化
 
  在用serialize序列化物件時,會自動呼叫__sleep方法,__sleep方法必須返回一個數組,包含需要序列化的屬性。
       PHP會拋棄其它屬性的值, 如果沒有__sleep方法,PHP將儲存所有屬性,包括private屬性。


  unserialize()  從位元組流中建立了一個物件之後,馬上檢查是否具有__wakeup 的函式的存在。
  如果存在,__wakeup 立刻被呼叫。使用 __wakeup 的目的是重建在序列化中可能丟失的任何資料庫連線以及處理其它重新初始化的任務。
 

下面給出一個序列化的程式碼:共serialize.php和unserialize.php兩個檔案。

<?php
   class User
   {
       public $name;
       public $id;

       function __construct()
       {
           $this->id = uniqid();          //give user a unique ID 賦予一個不同的ID
       }

       function __sleep()
       {   
           return(array("name"));        //do not serialize this->id 不序列化id
       }

       function __wakeup()
       {
           $this->id = uniqid();         //give user a unique ID
       }
   }

   $u = new User;
   $u->name = "HAHA";

   $s = serialize($u);                   //serialize it 序列化 注意不序列化id屬性,id的值被拋棄

   $u2 = unserialize($s);                //unserialize it 反序列化 id被重新賦值

  
   //$u and $u2 have different IDs $u和$u2有不同的ID
   var_dump($u);                        
   var_dump($u2);
?>

---------- PHP debug ----------
object(User)#1 (2) {
["name"]=>
string(4) "HAHA"
["id"]=>
string(13) "47fa045529f69"
}
object(User)#2 (2) {
["name"]=>
string(4) "HAHA"
["id"]=>
string(13) "47fa04552a49a"
}

---序列化--------反序列化--------------------

class ClassA {
    var $int;
    var $str;
    var $bool;
    var $obj;
    var $pr;
}
 
$a = new ClassA();
$a->int = 1;
$a->str = "Hello";
$a->bool = false;
$a->obj = $a;
$a->pr = &$a->str;
 
echo serialize($a);
//O:6:"ClassA":5:{s:3:"int";i:1;s:3:"str";s:5:"Hello";s:4:"bool";b:0;s:3:"obj";r:1;s:2:"pr";R:3;}
 
  在這個例子中,首先序列化的物件是 ClassA 的一個物件,那麼給它編號為 1,接下來要序列化的是這個物件的幾個成員,第一個被序列化的成員是 int 欄位,那它的編號就為 2,接下來被序列化的成員是 str,那它的編號就是 3,依此類推,到了 obj 成員時,它發現該成員已經被序列化了,並且編號為 1,因此它被序列化時,就被序列化成了 r:1; ,在接下來被序列化的是 pr 成員,它發現該成員實際上是指向 str 成員的一個引用,而 str 成員的編號為 3,因此,pr 就被序列化為 R:3; 了。
===============

//下面舉個簡單的例子,來說明 Serializable 介面的使用:序列化--------反序列化-

class MyClass implements Serializable
{
    public $member;
 
    function MyClass()
    {
        $this->member = 'member value';
    }
 
    public function serialize()
    {
        return wddx_serialize_value($this->member);
    }
 
    public function unserialize($data)
    {
        $this->member = wddx_deserialize($data);
    }
}
$a = new MyClass();
echo serialize($a);
echo "\n";
print_r(unserialize(serialize($a)));
/*
輸出結果為(瀏覽器中的原始碼):

C:7:"MyClass":90:{<wddxPacket version='1.0'><header/><data><string>member value</string></data></wddxPacket>}
MyClass Object
(
    [member] => member value
)
因此如果想用其它語言來實現 PHP 序列化中的 C 標示的話,也需要提供一種這樣的機制,讓使用者自定義類時,
能夠自己在反序列化時處理 <data> 內容,否則,這些內容就無法被反序列化了。

*/
、、、、
  __sleep 和 __wakeup
serialize() 函式會檢查是否存在一個魔術方法 __sleep.如果存在,__sleep()方法會先被呼叫, 然後才執行序列化操作。這個功能可以用於清理物件,並返回一個包含物件中所有變數名稱的陣列。如果該方法不返回任何內容,則NULL被序列化,導致 一個E_NOTICE錯誤。

__sleep方法常用於提交未提交的資料,或類似的操作。同時,如果你有一些很大的物件,不需要儲存,這個功能就很好用。

與之相反,unserialize()會檢查是否存在一個__wakeup方法。如果存在,則會先呼叫 __wakeup方法,預先準備物件資料。

__wakeup經常用在反序列化操作中,否則是字串;例如重新建立資料庫連線,或執行其它初始化操作

------------------------------------------------------------------------------------------------
 14  Final關鍵字
 
  如果父類中的方法被宣告為final,則子類無法覆蓋該方法; 如果一個類被宣告為final,則不能被繼承。
 
  語法:
 
 類使用 final 關鍵字的例子:
 final class Person
 {
  ......
 }
 
  class BaseClass {
    public function test() {
     echo "BaseClass::test() called\n";
    }
   
    final public function moreTesting() {
     echo "BaseClass::moreTesting() called\n";
    }
 }

class ChildClass extends BaseClass {
    public function moreTesting() {
       echo "ChildClass::moreTesting() called\n";
   }
}

---------------------------------------------
15  物件複製

物件複製可以通過clone關鍵字來完成(如果物件中存在__clone()方法,會先被呼叫)。物件中的 __clone()方法不能直接呼叫。
 $copy_of_object = clone $object;
 
 
clone 關鍵字用於克隆一個完全一樣的物件,

__clone() 
 __clone() 方法來重寫原本的屬性和方法。是深複製
如果想在克隆後改變原物件的內容,需要在類中新增一個特殊的 __clone() 方法來重寫原本的屬性和方法。
 __clone() 方法只會在物件被克隆的時候自動呼叫。

clone 關鍵字用於克隆一個完全一樣的物件,__clone() 方法來重寫原本的屬性和方法。

物件克隆
有的時候我們需要在一個專案裡面使用兩個或多個一樣的物件,如果使用 new 關鍵字重新建立物件,再賦值上相同的屬性,這樣做比較煩瑣而且也容易出錯。
PHP 提供了物件克隆功能,可以根據一個物件完全克隆出一個一模一樣的物件,而且克隆以後,兩個物件互不干擾。

使用關鍵字 clone 來克隆物件。語法: $object2 = clone $object;
例子1:
2,
__clone()
如果想在克隆後改變原物件的內容,需要在類中新增一個特殊的 __clone() 方法來重寫原本的屬性和方法。
__clone() 方法只會在物件被克隆的時候自動呼叫。
 

//例子1:
/*
class Person {
    private $name;
    private $age;

    public function __construct($name, $age) {
        $this->name=$name;
        $this->age=$age;
    }

    public function say() {
        echo "my name is :".$this->name."<br />";
        echo "my age is :".$this->age;
    }
  
}

$p1 = new Person("haha", 20);
$p2 = clone $p1;
$p2->say();
*/
class Person {
    private $name;
    private $age;

    function __construct($name, $age) {
        $this->name = $name;
        $this->age = $age;
    }

    function say() {
        echo "my name is :".$this->name."<br/>";
        echo " my age is :".$this->age."<br />";
    }
    function __clone() {
        $this->name = "my is error ".$this->name;
        $this->age = 30;
    }
}

$p1 = new Person("haha", 20);
$p1->say();
$p2 = clone $p1;
$p2->say();

------------------------------------------------------------------
16  物件比較

(==)  如果兩個物件的屬性和屬性值 都相等,而且兩個物件是同一個類的例項,那麼這兩個物件變數相等。

(===),這兩個物件變數一定要指向某個類的同一個例項(即同一個物件)。
例項1

class A{

  $a=new A;
  $b=new A;
   if($a==$b){
      echo "true";
   }else{
      echo "false";
   }
  $c=new A;
  $d=&$c;
      if($c===$d){
      echo "true";
   }else{
      echo "false";
   }

}

例項2
function bool2str($bool)
{
    if ($bool === false) {
        return 'FALSE';
    } else {
        return 'TRUE';
    }
}

function compareObjects(&$o1, &$o2)
{
    echo 'o1 == o2 : ' . bool2str($o1 == $o2) . "\n";
    echo 'o1 != o2 : ' . bool2str($o1 != $o2) . "\n";
    echo 'o1 === o2 : ' . bool2str($o1 === $o2) . "\n";
    echo 'o1 !== o2 : ' . bool2str($o1 !== $o2) . "\n";
}

class Flag
{
    public $flag;

    function Flag($flag = true) {
        $this->flag = $flag;
    }
}

class OtherFlag
{
    public $flag;

    function OtherFlag($flag = true) {
        $this->flag = $flag;
    }
}

$o = new Flag();
$p = new Flag();
$q = $o;
$r = new OtherFlag();

echo "Two instances of the same class\n";
compareObjects($o, $p);

echo "\nTwo references to the same instance\n";
compareObjects($o, $q);

echo "\nInstances of two different classes\n";
compareObjects($o, $r);

-----------------------------------------
17  物件和引用
  引用:php的引用是別名,就是兩個不同的變數名字指向相同的內容


class A {
    public $foo = 1;

$a = new A;
$b = $a;     // $a ,$b都是同一個識別符號的拷貝
             // ($a) = ($b) = <id>   
$b->foo = 2;
echo $a->foo."\n";


$c = new A;
$d = &$c;    // $c ,$d是引用
             // ($c,$d) = <id>

$d->foo = 2;
echo $c->foo."\n";


$e = new A;

function foo($obj) {
    // ($obj) = ($e) = <id>
    $obj->foo = 2;
}

foo($e);
echo $e->foo."\n";
-------------------------------------------------------------------------------

物件序列化
序列化物件 - 在會話中存放物件
所有php裡面的值都可以使用函式serialize()來返回一個包含位元組流的字串來表示。
unserialize()函式能夠重新把字串變回php原來的值。

序列化一個物件將會儲存物件的所有變數,但是不會儲存物件的方法,只會儲存類的名字。
為了能夠unserialize()一個物件,這個物件的類必須已經定義過。
如果序列化類A的一個物件,將會返回一個跟類A相關,而且包含了物件所有變數值的字串。
如果要想在另外一個檔案中解序列化一個物件,這個物件的類必須在解序列化之前定義,可以通過包含一個定義該類的檔案或使用函式spl_autoload_register()來實現

例項1:
    class A {
      public $one = 1;
   
      public function show_one() {
          echo $this->one;
      }
  }
 
// page1.php:

  include("classa.inc");
 
  $a = new A;
  $s = serialize($a);
  // 把變數$s儲存起來以便檔案page2.php能夠讀到
  file_put_contents('store', $s);

// page2.php:
 
  // 要正確瞭解序列化,必須包含下面一個檔案
  include("classa.inc");

  $s = file_get_contents('store');
  $a = unserialize($s);

  // 現在可以使用物件$a裡面的函式 show_one()
  $a->show_one();
------------------------------------
後期靜態繫結)
     後期繫結“的意思是說,static::不再被解析為定義當前方法所在的類,而是在實際執行時計算的。也可以稱之為”靜態繫結“,因為它可以用於(但不限於)靜態方法的呼叫
     後期靜態繫結的功能:用於在繼承範圍內引用靜態呼叫的類。

self:: 的限制
使用self:: 或者 __CLASS__對當前類的靜態引用,取決於定義當前方法所在的類:

例項1:

<?php
class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        self::who();
    }
}

class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}

B::test();
?>

------------------------------------------------------------------------------------------
17$this關鍵字
          $this 的含義是表示 例項化後的 具體物件!
         
    this的用法:

    1,this是指向物件例項的一個指標,self是對類本身的一個引用,parent是對父類的引用。

    1,類的內部使用:如果從類的內部訪問不為常量const或者static變數或者方法,那麼就必須使用自引用的$this
    2,在建構函式中 指該建構函式建立新物件
    3,引用$this,代表當前的類,解決變數命名衝突和不確定性問題\
   
    --------------------------------
  class Test{
 function __call($name,$args){
  if($name=='add' && count($args)==2){
   $type='num';
  }
  foreach ($args as $key=>$val){
   if(!is_int($val) || is_float($val)){
    $type='string';
   }
  }
  $method=$name.ucfirst($type);
  
  if(method_exists($this,$method)){
   call_user_func_array(array($this,$method),$args);
  }
 }
 function addNum(){
  echo $i+$j;
 }
 function addString(){
  echo $i.$j;
 }
}
$test = new Test();
$test->add(3,5);
$test->add(4,'4');
----------------------------------------------
/*
 * 常量 const
在類裡面定義常量用 const 關鍵字,而不是通常的 define() 函式。
語法: const constant = "value";
例子:
執行該例子輸出:
 中國
 我是中國人
 *
 */
Class Person{
    // 定義常量
    const COUNTRY = "china";
    public function myCountry() {
        //內部訪問常量
        echo "my is ".self::COUNTRY." person<br />";
    }
}
// 輸出常量
echo Person::COUNTRY."<br />";
// 訪問方法
$p1 = new Person();
$p1 -> myCountry();
Person::myCountry();
--------------------
/*
 * PHP 物件的儲存與傳輸(序列化 serialize 物件)
物件的儲存與傳輸
在實際專案應用中,有些任務在一兩個頁面是無法完成的,由於變數到指令碼執行完畢就釋放,我們本頁所生成的物件想在其它頁面使用時便碰到了麻煩。

如果需要將物件及其方法傳遞到我們想使用物件的頁面,比較簡單可行的辦法是將物件序列化後儲存起來或直接傳輸給需要的頁面,另一種辦法是將物件註冊為 session 變數。

序列化物件
物件序列化,就是將物件轉換成可以儲存的位元組流。當我們需要把一個物件在網路中傳輸時或者要把物件寫入檔案或是資料庫時,就需要將物件進行序列化。

序列化完整過程包括兩個步驟:一個是序列化,就是把物件轉化為二進位制的字串,serialize() 函式用於序列化一個物件;另一個是反序列化,就是把物件被序列轉化的二進位制字串再轉化為物件,unserialize() 函式來反序列化一個被序列化的物件。這樣整個過程下來,物件內的型別結構及資料都是完整的。

語法:

string serialize( mixed value )
mixed unserialize( string str [, string callback] )
 *
 */
class Person {
    private $name;
    private $age;

    function __construct($name, $age) {
        $this->name = $name;
        $this->age = $age;
    }

    function say() {
    echo "my name is ".$this->name."<br />";
    echo " my age is ".$this->age;
    }
}

$p1 = new Person("haha", 20);
$p1_string = serialize($p1);

//將物件序列化後寫入檔案
$fh = fopen("p1.text", "w");
fwrite($fh, $p1_string);
fclose($fh);
--------------
<?php
/*
 * PHP面向物件之this 關鍵字

1,PHP5中為解決變數的命名衝突和不確定性問題,引入關鍵字“$this”代表其所在當前物件。

2,$this在建構函式中指該建構函式所建立的新物件。
3,在類中使用當前物件的屬性和方法,必須使用$this->取值。方法內的區域性變數,不屬於物件,不使用$this關鍵字取值。

區域性變數和全域性變數與 $this 關鍵字
4,使用當前物件的屬性必須使用$this關鍵字。

區域性變數的只在當前物件的方法內有效,所以直接使用。

注意:區域性變數和屬性可以同名,但用法不一樣。在使用中,要儘量避免這樣使用,以免混淆。

1234567891011121314 <!-- 驗證屬性和區域性變數使用方法的類 -->
<?php
class A{    
    private $a = 99;     //這裡寫一個列印引數的方法.    
    public function printInt($a){        
        echo "這裡的 \$a 是傳遞的引數 $a ";        
        echo "<br>";        
        echo "這裡的 \$this->a 是屬性 $this->a";    
    }
}
 $a = new A(); // 這裡的$a 可不是類中的任何一個變量了.
 $a->printInt(88);
?>

執行結果:

12 這裡的 $a 是傳遞的引數 88 這裡的 $this->a 是屬性 99

用$this呼叫物件中的其它方法
1234567891011121314151617 <!--寫一個類,讓他自動完成最大值的換算.-->
 <?php
class Math{     //兩個數值比較大小.    
    public function Max($a,$b){        
        return $a>$b?$a:$b;    
    }     //三個數值比較大小.    
    public function Max3($a,$b,$c){         //呼叫類中的其它方法.       
         $a = $this->Max($a,$b);        
        return $this->Max($a,$c);    
    }
}
$math = new Math();
echo "最大值是 ".$math->Max3(99,100,88);
?>

執行結果:

1 最大值是 100

使用$this呼叫建構函式
呼叫建構函式和解構函式的方法一致。

12345678910111213141516 <? class A{     private $a = 0;     public function __construct(){         $this->a = $this->a + 1 ;     }       public function doSomeThing(){         $this->__construct();         return $this->a;     }      } $a = new A(); // 這裡的$a 可不是類中的任何一個變量了. echo "現在 \$a 的值是" . $a->doSomeThing(); ?>

執行結果:

1 現在 $a 的值是2

$this 到底指的什麼?
$this 就是指當前物件,我們甚至可以返回這個物件使用 $this

12345678910111213 <?php class A{     public function  getASelf(){         return $this;     }     public function __toString(){         return "這是類A的例項.";     } } $a = new A(); // 建立A的例項; $b = $a->getASelf(); //呼叫方法返回當前例項. echo $a; //列印物件會呼叫它的__toString方法. ?>

程式執行結果:

1 這是類A的例項.

通過 $this 傳遞物件
在這個例子中,我們寫一個根據不同的年齡發不同工資的類.
我們設定處理年齡和工資的業務模型為一個獨立的類.

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455 <?php class User{     private $age ;     private $sal ;     private $payoff ; //宣告全域性屬性.       //建構函式,中建立Payoff的物件.     public function __construct(){         $this->payoff = new Payoff();     }     public function getAge(){         return $this->age;     }     public function setAge($age){         $this->age = $age;     }     // 獲得工資.     public  function getSal(){         $this->sal =  $this->payoff->figure($this);         return $this->sal;     } } //這是對應工資與年齡關係的類. class Payoff{     public function figure($a){         $sal =0;         $age = $a->getAge();         if($age >80 || $age <16 ){             $sal = 0;         }elseif ($age > 50){             $sal = 1000;         }else{             $sal = 800;         }         return $sal;     } } //例項化User $user = new User();   $user->setAge(55); echo $user->getAge()."age ,his sal is " . $user->getSal(); echo "<br>";   $user->setAge(20); echo $user->getAge()."age , his sal is " . $user->getSal(); echo "<br>";   $user->setAge(-20); echo $user->getAge()."age , his sal is " . $user->getSal(); echo "<br>";   $user->setAge(150); echo $user->getAge()."age , his sal is " . $user->getSal(); ?>

執行結果:

1234 55age ,his sal is 1000 20age , his sal is 800 -20age , his sal is 0 150age , his sal is 0.
**/
class A{    
    private $a = 99;     //這裡寫一個列印引數的方法.    
    public function printInt($a){        
        echo $a;//echo "這裡的 \$a 是傳遞的引數 $a ";        
        echo "<br>";        
        echo $this->a;//echo "這裡的 \$this->a 是屬性 $this->a";    
    }
}
    $a = new A(); // 這裡的$a 可不是類中的任何一個變量了.
    $a->printInt(88);
?>