php.類與物件

weixin_33840661發表於2018-11-27

訪問控制

屬性和方法的訪問控制(可見標識):
public 任何地方
private 類自身
protected 類自身,自子類及自父類

this

this 可以理解為這個類的一個例項

self

self 代表類本身

__construct

例項化時就會自動執行
public function __construct() {}

__destruct

當物件對銷燬時自動執行
public function __destruct(){}

類常量

定義合不可改變

const ONE = 1
const TWO = self::ONE + 1;

可以在類外部呼叫
Class::Two

parent

parent代表父類
public function __construct(){ parent::__construct();}

final

final class Dad(){} 加到類定義前面表示Dad類不想被繼承

class Dad(){
    final public function run(){}  // 加到方法宣告前面,表示方法不想被子類重寫
}

namespace

namespace必須放在程式碼檔案的第一行
受名稱空間影響的型別:類(包括抽像類,traits)、介面、函式和常量。
當沒有使用namespace關鍵字指定名稱空間時,當前指令碼是存在於全域性名稱空間的。用\表示。\Class1
類全在指定的名稱空間去查詢,沒找到則丟擲錯誤。
函式與常量,在指定名稱空間查詢,沒找到則到全域性名稱空間查詢,還沒打到則拋錯。

函式

ns1\ns2\fn()

常量

define定義的常量是全域性的,不受名稱空間影響。
const定義的常量受名稱空間影響。

namespace ns1\ns2;
const ONE = 1;

echo ns1\ns2\ONE;

use

匯入類

use匯入名稱空間下的類
use ns1\ns2\class1;

as重新命名匯入的類
use ns1\ns2\class1 as class2;

匯入函式

use function ns1\ns2\fn as fn1;

匯入常量

use const ns1\ns2\ONE;

自動載入

__autoload

function __autoload($className){ require $className . '.php';}

spl_autoload_register

傳匿名函式

spl_autoload_register(function($className){ require $className . '.php'; });

傳函式名

function test($className){ require $className . '.php'; }
spl_autoload_register('test');

傳類

class Momo
{
   function autoload($className)
   {
           require $className . '.php';
   }
}

spl_autoload_register([new Momo, 'autoload']);

static

宣告靜態屬性和靜態方法,不經過例項化,通過類名即可呼叫。

class Person()
{
    public static $hand = '手';
   
   public static function run()
   {
           echo 'running...';
   }
}

echo Person::$hand;
Person::run();

類內部呼叫靜態屬性和靜態方法用self關鍵字

echo self::$hand; self::run();

呼叫父類的靜態屬性和靜態方法用parent關鍵字

echo parent::$hand; parent::run();

後期靜態繫結

class A
{
    public static function who()
   {
           echo 'A類的who方法';
   }
   
   public static function test1()
   {
           self::who();
   }
   
   public static function test2()
   {
           static::who();
   }
}

class B extends A
{
    public static function who()
    {
        echo 'B類的who方法';
    }
}

B::test1();  // test1內部用的self,呼叫的是自身(A類)的靜態方法
B::test2();  // 後期繫結。內部用static,根據後期的呼叫環境確定,呼叫的是B類的靜態方法

魔術方法

__set

class Test
{
    private $name = '';
    
    public function __set($var, $val)
    {
        //     對$val進行資料處理
        $this->$var = $val;
    }
}

$test = new Test();
$test->name = 'tom';    // 賦值tom

__get

class Test
{
    private $name = 'jack';
    public function __get($var)
    {
        return $this->$var;
    }
}

$test = new Test();
echo $test->name;    // jack

__isset

用於檢測私有屬性是否存在

class Test
{
    private $name = 'mary';
    
    public function __isset($var)
    {
        return isset($this->$var);
    }
}
$test = new Test();
var_dump($test->name);    // 如果不設定__isset,返回false,設定後返回true

__unset

用於刪除私有屬性

class Test
{
    private $name = 'Levi';
   public function __unset($var)
   {
           unset()
   }
}
$test = new Test;
unset($test->name);        // 會觸發__unset

__call

避免呼叫一個不存在的方法時產生錯誤,當呼叫的方法不存在時,__call方法會自動呼叫

class Test
{
    public function __call($fn_name, $fn_arguments)
    {
        echo $fn_name;
        print_r($fn_arguments);
    }
}

$test = new Test();
$test->go(1, 'ok');        // 自動呼叫`__call`方法,列印出函式名和引數陣列

__callStatic

__call類似
避免呼叫一個不存在的靜態方法

class Test
{
    public static function __callStatic($fn_name, $fn_arguments)
    {
        echo $fn_name;
        print_r($fn_arguments);
    }
}

// `__callStatic` 必須宣告為靜態方法
Test::go(1, 'ok');

__invoke

當物件以函式的形式呼叫的時候,自動呼叫__invoke方法

class Test
{
    public function __invoke($args)
    {
        return $args;
    }
}
$test = new Test();
$test('go.....');    // 執行__invoke

__toString

當列印物件是會呼叫__toString方法

class Test
{
    public function __toString()
    {
        return 'Hello world!';
    }
}

$test = new Test;
echo $test;        // 輸出 Hello world!

物件複製

淺拷貝

比較省記憶體,物件的拷貝預設是淺拷貝。

$a = new Test();
$b = $a;    // 淺拷貝。傳址。改變$b。$a也會改變。

物件的複製是淺拷貝的。傳址。
普通變數的拷貝是深拷貝的。傳值。

深拷貝

$a = new Test();
$b = clone $a;    // 深拷貝。改變$b,不會改變$a。

__clone

當使用clone關鍵字時,自動呼叫__clone方法

class Test
{
    public $obj = null;
    public function __clone()
    {
        $this->obj = clone $this->obj;
    }
}
class Person
{
    public $sex = 0;
}
$a = new Test;
$a->obj = new Person;
$b = clone $a;        // 觸發`__clone` 對obj進行深拷貝
$b->obj->sex = 1;    // $b中的obj物件改變了。而$a中的obj物件沒變。

型別約束

class A
{
    public function go()
    {
        echo 'go .....';
    }
}
function test(A $a) {
    $a->go();
}
test(new A());

Trait

單繼承語言PHP的程式碼複用機制。

Trait Bt
{
    public function atest()
    {
        echo 'Hello ';
    }
    
    public function btest()
    {
        echo 'world';
    }
    
    public function ab()
    {
        $this->atest();
        $this->btest();
    }
}

class Test
{
    use Bt;        // 使用Bt Trait,便擁有了Bt所有的方法
}

$test = new Test;
$test->ab();

繼承多個Trait

Trait A
{
    public $name = 'tom';
    public function a()
    {
        echo 'Hello ';
    }
}
Trait B
{
    public function b()
    {
        echo 'world ';
    }
}

class Test
{
    use A,B;
    public function c()
    {
        echo $this->name;
    }
}

$test = new Test;
$test->a();
$test->b();
$test->c();        // Hello world tom

Trait 支援巢狀

Trait A{}
Trait B{}
Trait C
{
    use A,B;
}
Class Test
{
    use C;
}

interface

介面是類的模板。在介面中只定義需要實現的空方法,這些方法在介面中不做具體實現。
介面是不能被例項化的。

Interface Person
{
    public function eat();
   public function sleep();
}

class man implements Person
{
    public function eat()
    {
        echo 'eating...';
    }
    
    public function sleep()
    {
        echo 'sleeping...';
    }
}

class L
{
    public static function factory(Person $user)    // 用介面作型別約束
    {
        return $user;
    }
}

$user = L::factory(new Man());
$user->eat();
$user->sleep();

介面可以繼承介面。
介面可以繼承多個介面。
介面可以使用常量,叫介面常量,和類的常量使用方法相同

Interface Ia
{
    const ONE = 1;
    public function eat();
}

Interface Ib
{
    public function sleep();
}

Interface AB extends Ia,Ib
{}

// class Test implements Ia,Ib        類可以同時繼承多個介面
class Test implements AB
{
    public function eat()
    {
        echo 'eating...';
    }
    
    public function sleep()
    {
        echo 'sleeping...';
    }
}

$test = new Test;
$test->eat();
$test->sleep();
echo Ia::ONE;    // 使用介面常量

abstract

抽象類不能被例項化。
如果至少有一個方法被宣告為抽象的,那麼這個類必須被宣告為抽象的。
抽象方法只能宣告,不能有具體功能實現。
抽象類可以有被實現的的方法。

繼承抽象類,子類必須實現父類中所有的抽象方法。
這些方法的訪問控制必須和父類一樣,或者更寬鬆,不能比父類更嚴格。
方法的呼叫方式也必須匹配。型別和引數數量必須一致,但子類可以定義父類中不存在的可選引數。

abstract AB
{
    public function run()
    {
        echo 'running...';
    }
    
    abstract public function eat();
    abstract public function sleep();
}

class Test extends AB
{
    public function eat()
    {
        echo 'eating...';
    }
    
    public function sleep($time = '21:00 PM')    // 可以定義父類方法中不存在的可選引數
    {
        echo 'sleep @ ' . $time;
    }
}

單例模式

只能被例項化一次,節省記憶體空間

class Test
{
    private static $instance = null;
   private function __constrct()
   {}
   private function __clone()
   {}
   public static function getInstance()
   {
           if (!(self::instance instanceof self)) {
              self::instance = new self();          
        }
           return self::instance;
   }
}

$test = Test::getInstance();    // 多次呼叫也只是例項化一次

工廠模式

Interface CacheI
{
    public function set($key, $value);
   public function get($key);
   public function delete($key);
}
class Memcache implements CacheI
{
    public function set($key, $value){}
   public function get($key){}
   public function delete($ke){}
}
class Redis implements CacheI
{
    public function set($key, $value){}
   public function get($key){}
   public function delete($ke){}
}
class Cache
{
    public static function factory()
    {
        return new Memcache();    // 這裡可以是繼承了CacheI介面的任何類,比如Redis
    }
}
$cache = Cache::factory();
$cache->set('name', 'tom');
$cache->get('name');
$cache->delete('name');

相關文章