詳解 PHP 反射的基本使用

Dennis_Ritchie發表於2019-11-25

PHP反射

今天我要給大家講解的是PHP當中使用非常普遍的高階操作:反射。反射在當今幾乎所有的PHP框架或者工具中都佔用非常重要的角色,就比如Laravel的容器,容器對於Laravel架構來說極其重要,Laravel的核心類Illuminate\Foundation\Application就是繼承自Illuminate\Container\Container類。為了讓大家徹底的理解反射,我這裡寫這篇博文,希望大家能夠仔細閱讀,我還寫了一個簡單的容器,這個容器,是我今天上午實現的,它也是基於反射實現的,程式碼量不大,但也不是那麼容易理解,程式碼已經上傳到了碼雲php-base-container

預備知識

大家可以參考php的官方地址,這裡有對反射詳細的論述,php反射
對於反射,我們會接觸到四個類,分別是:

  1. ReflectionClass
  2. ReflectionFunction
  3. ReflectionMethod
  4. ReflectionParameter

為了讓大家明白這個這四個類的作用,我給大家寫了一個下面的例子。

class  Printer
{
}

class  Student
{
    private $name;
    private $year;

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

    public function getValue()
    {
        return $this->name;
    }

    public function setBase(Printer $printer, $name, $year = 10)
    {
        $this->name = $name;
        $this->year = $year;
    }
}

上面我們宣告瞭2個類Printer和Student,測試如下:

$refl_class = new ReflectionClass(Student::class);
$object = $refl_class->newInstanceArgs(["obama", 100]);
echo get_class($object) . "\n";
echo $object->getValue();

控制檯列印結果如下:

如何實現Laravel的容器

首先列印的是Student這個類的類名,然後列印了obama,我來解釋下上面的程式碼, new ReflectionClass(Student::class)建立了一個代表Student類的ReflectionClass物件,這個類的物件有一個方法newInstanceArgs,這個方法可以建立Student::class類的物件,它的引數為Student類建構函式所需要的引數,我們的引數為obama和100,緊接著呼叫get_class方法獲取剛才建立物件的類,也就是我們列印的第一行Student,印證了我們上面所說的。因為$object就是Student類的物件,所以我們可以呼叫getValue方法,返回值就是name,結果為obama。

上面我們分析了ReflectionClass,下面我們再來分析下面的這個:

$refl_method = $refl_class->getMethod("setBase");
echo get_class($refl_method) . "\n";
$parameters = $refl_method->getParameters();
foreach ($parameters as $parameter) {
    echo $parameter->getName() . "\n";
    if ($parameter->getClass() != null) {
         echo $parameter->getClass()->getName() . "\n";
    }
    if ($parameter->isDefaultValueAvailable()) {
        echo $parameter->getDefaultValue() . "\n";
    }
}

上面的程式碼執行結果如下

如何實現Laravel的容器

之前我們建立了ReflectionClass類的物件為$refl_class,這裡我們呼叫它的方法getMethod,這個getMethod會返回當前Student類的setBase,但是它是一個ReflectionMethod類的例項,這裡的get_class($refl_method)就會列印出$refl_method的類為ReflectionMethod,也就是控制檯的第一行結果,ReflectionMethod類有一個方法叫做getParameters,這個方法會返回ReflectionMethod所對應方法(這裡就是setBase)的所有引數構成的陣列,這個陣列的每一個元素都是ReflectionParameter 類的物件,接下來的foreach遍歷所有的引數,首先檢查$parameter->getClass()的返回值,ReflectionParameter類的getClass方法返回的是這個引數所對應的類,也就是說getClass返回的是ReflectionClass類的物件,ReflectionClass類的物件有一個getName方法,這個方法返回類的名字,ReflectionParameter 類還有一個比較常用的方法,就是isDefaultValueAvailable,他檢查這個引數是否有預設的值,如果這個引數有預設的值的話,那麼getDefaultValue方法就可以獲取到這個預設值。

上面分析了ReflectionMethod和ReflectionParameter兩個類,還有一個要講,如下:

function display($a, $b, Printer $printer)
{
    echo "called" . "\n";
}

$refl_function = new ReflectionFunction("display");
$parameters = $refl_function->getParameters();
foreach ($parameters as $parameter) {
    echo $parameter->getName() . "\n";
    if ($parameter->getClass() != null) {
        echo $parameter->getClass()->getName() . "\n";
    }
    if ($parameter->isDefaultValueAvailable()) {
        echo $parameter->getDefaultValue() . "\n";
    }
}

程式碼執行結果如下:

如何實現Laravel的容器

上面的程式碼很簡單,首先我定義了名為display的方法,它有三個引數,緊接著看,我們建立了一個ReflectionFunction類的物件,它的引數為函式名,也就是說ReflectionFunction是對函式的封裝。
ReflectionFunction類也有一個名為getParameters的方法,他返回的值和ReflectionMethod的getParameters方法的返回值是一樣的,都是
ReflectionMethod類的陣列,接下來的foreach便利操作和上面講解ReflectionMethod類的操作的時候是一模一樣的,不再詳述。

總結

上面基本講到了PHP反射的基本使用,希望大家仔細的理解我上面所說的內容,如果大家理解了我上面所說的,可以看我實現的php容器:php-base-container,如果大家有啥不懂的,可以聯絡我,也可以加下面的qq群,可以互相學習。

如果有不懂的地方,可以加我的qq:1174332406,或者是微信:itshardjs,公眾號:LearnCodeHard

相關文章