PHP面向對象中的重要知識點(三)

1. namespace:php

    和C++中的名字空間很像,做用也同樣,都是爲了不在引用較多第三方庫時而帶來的名字衝突問題。經過名字空間,即使兩個class的名稱相同,可是由於位於不一樣的名字空間內,他們仍然能夠被精肯定位和區分。第一次看到PHP的名字空間語法時,感受和C++相比在語法上是很是很是類似的,然而在寫點兒小例子作作實驗的時候才發現,他們的差異仍是很大的,爲了不之後忘記,因此這裏特別將其記錄了下來。見以下代碼:java

<?php
//in Test2.php
namespace nstest\test2;

class Test2 {
    public static function printMe() {
        print 'This is nstest\test2\Test2::printSelf.'."\n";
    }
}

<?php
//in Test1.php
namespace nstest\test1;

class Test1 {
    public static function printMe() {
        print 'This is nstest\test1\Test1::printSelf.'."\n";
    }
}
require "Test2.php";
nstest\test2\Test2::printMe();

    運行結果以下:函數

bogon:TestPhp$ php Test1.php 
PHP Fatal error:  Class 'nstest\test1\nstest\test2\Test2' not found in /Users/liulei/PhpstormProjects/TestPhp/Test1.php on line 13

    是否是這個結果比較出乎意料,緣由在哪呢?HOHO,原來PHP在進行名字空間引用的時候,若是名字空間的第一個字符不是前導斜槓(\),那麼就被自動識別爲相對名字空間,在上面的代碼中,Test1自身所在的名字空間是namespace nstest\test1,所以在以nstest\test2\Test2::printMe()方式調用Test2::printMe()時,PHP將自動解析爲nstest\test1\nstest\test2\Test2::printMe(),即認爲nstest\test2是在當前名字空間內部的。修正該問題很是簡單,只需在引用時加上前導斜槓(\)便可,見如下修復後的代碼:     學習

<?php
//Test2.php
namespace nstest\test2;

class Test2 {
    public static function printMe() {
        print 'This is nstest\test2\Test2::printSelf.'."\n";
    }
}

<?php
//Test1.php
namespace nstest\test1;

class Test1 {
    public static function printMe() {
        print 'This is nstest\test1\Test1::printSelf.'."\n";
    }
}
require "Test2.php";
\nstest\test2\Test2::printMe();

    運行結果以下:ui

bogon:TestPhp$ php Test1.php 
This is nstest\test2\Test2::printSelf.

    還有一種改動方式,能夠示意一下PHP中名字空間中的相對引用。這裏咱們能夠將Test1的名字空間改成namespace nstest,其餘的修改見如下代碼中紅色高亮部分:this

<?php
//Test2.php
namespace nstest\test2;

class Test2 {
    public static function printMe() {
        print 'This is nstest\test2\Test2::printSelf.'."\n";
    }
}

<?php
//Test1.php
namespace nstest; class Test1 {
    public static function printMe() {
        print 'This is nstest\test1\Test1::printSelf.'."\n";
    }
}

require "Test2.php";
test2\Test2::printMe(); 

    運行結果等於上面正確的結果。最重要的差異就是該例使用了PHP名字空間中的相對定位。相信熟悉C++的開發者必定會想到use關鍵字,PHP也提供了該關鍵字,他們的功能是一致的,都是爲了不在後面的代碼中,無需再經過全限定符(類名前加名字空間前綴)來引用其餘名字空間中的類了。至於具體的語法規則,仍是看看下面具體的代碼和關鍵性註釋吧。spa

<?php
//Test2.php
namespace nstest\test2;

class Test2 {
    public static function printMe() {
        print 'This is nstest\test2\Test2::printSelf.'."\n";
    }
}

<?php
//Test1.php
namespace nstest\test1;

class Test1 {
    public static function printMe() {
        print 'This is nstest\test1\Test1::printSelf.'."\n";
    }
}

require "Test2.php";
//這裏須要特別注意的是,nstest\test2已經表示名字空間絕對路徑定位,不須要再加前導斜槓(\)了。
//另外這裏還有一個隱式規則是test2表示該名字空間的缺省別名,在引用其名字空間內的對象時須要加test2前綴。
use nstest\test2; test2\Test2::printMe();

//這裏咱們也能夠給名字空間顯式的指定別名,如:
use nstest\test2 as test2_alias; test2_alias\Test2::printMe(); 

    運行結果以下:code

bogon:TestPhp$ php Test1.php 
This is nstest\test2\Test2::printSelf.
This is nstest\test2\Test2::printSelf.

    最後介紹一下PHP中全局名字空間的引用方式,見以下代碼和關鍵性註釋:orm

<?php
class Test {
    public static function printMe() {
        print 'This is Global namespace Test::printSelf.'."\n";
    }
}

//下面兩行代碼表示的是同一對象,即全局名字空間下的Test類,然而若是由於名字空間衝突致使第一種方式不能被PHP
//編譯器正常識別,那麼就可使用第二種方式顯式的通知PHP,本身要引用的是全局名字空間中的Test類。
Test::printMe();
\Test::printMe();

    運行結果以下:對象

bogon:TestPhp$ php Test1.php 
This is Global namespace Test::printSelf.
This is Global namespace Test::printSelf.

2. Reflection:

    PHP中的反射和Java中java.lang.reflect包提供的功能同樣,更有意思的是,就連不少方法命名和調用方式也是很是雷同的。他們都是由一些列能夠分析類、類方法和方法參數的PHP內置類組成。咱們這裏主要介紹的是以下幾個經常使用的內置類:(Reflection、RelectionClass、ReflectionMethod、ReflectionParameter和ReflectionProperty)。如今咱們仍是一步一步來理解,即從ReflectionClass開始給出示例代碼和關鍵性註釋: 

<?php
class TestClass {
    public $publicVariable;

    function publicMethod() {
        print "This is publicMethod.\n";
    }
}

function classInfo(ReflectionClass $c) {
    $details = "";
    //getName將返回實際的類名。
    $name = $c->getName();
    if ($c->isUserDefined()) {
        $details .= "$name is user defined.\n";
    }
    if ($c->isInternal()) {
        $details .= "$name is built-in.\n";
    }
    if ($c->isAbstract()) {
        $details .= "$name is abstract class.\n";
    }
    if ($c->isFinal()) {
        $details .= "$name is final class.\n";
    }
    if ($c->isInstantiable()) {
        $details .= "$name can be instantiated.\n";
    } else {
        $details .= "$name cannot be instantiated.\n";
    }
    return $details;
}

function classSource(ReflectionClass $c) {
    $path = $c->getFileName();
    $lines = @file($path);
    //獲取類定義代碼的起始行和截至行。
    $from = $c->getStartLine();
    $to = $c->getEndLine();
    $len = $to - $from + 1;
    return implode(array_slice($lines,$from - 1,$len));
}

print "The following is Class Information.\n";
print classInfo(new ReflectionClass('TestClass'));

print "\nThe following is Class Source.\n";
print classSource(new ReflectionClass('TestClass'));

    運行結果以下:

bogon:TestPhp$ php reflection_test.php 
The following is Class Information.
TestClass is user defined.
TestClass can be instantiated.

The following is Class Source.
class TestClass {
    public $publicVariable;

    function publicMethod() {
        print "This is publicMethod.\n";
    }
}

    下面讓咱們仍然以代碼示例和關鍵性註釋的方法繼續ReflectionMethod的學習之旅。

<?php
class TestClass {
    public $publicVariable;

    function __construct() {

    }
    private function privateMethod() {

    }
    function publicMethod() {
        print "This is publicMethod.\n";
    }
    function publicMethod2(string $arg1, int $arg2) {

    }
}

//這個函數中使用的ReflectionMethod中的方法都是很是簡單直觀的,就再也不過多贅述了。
function methodInfo(ReflectionMethod $m) {
    $name = $m->getName();
    $details = "";
    if ($m->isUserDefined()) {
        $details .= "$name is user defined.\n";
    }
    if ($m->isInternal()) {
        $details .= "$name is built-in.\n";
    }
    if ($m->isAbstract()) {
        $details .= "$name is abstract.\n";
    }
    if ($m->isPublic()) {
        $details .= "$name is public.\n";
    }
    if ($m->isProtected()) {
        $details .= "$name is protected.\n";
    }
    if ($m->isPrivate()) {
        $details .= "$name is private.\n";
    }
    if ($m->isStatic()) {
        $details .= "$name is static.\n";
    }
    if ($m->isFinal()) {
        $details .= "$name is final.\n";
    }
    if ($m->isConstructor()) {
        $details .= "$name is constructor.\n";
    }
    if ($m->returnsReference()) {
        $details .= "$name returns a reference.\n";
    }
    return $details;
}

function methodSource(ReflectionMethod $m) {
    $path = $m->getFileName();
    $lines = @file($path);
    $from = $m->getStartLine();
    $to = $m->getEndLine();
    $len = $to - $from + 1;
    return implode(array_slice($lines, $from - 1, $len));
}

$rc = new ReflectionClass('TestClass');
$methods = $rc->getMethods();
print "The following is method information.\n";
foreach ($methods as $method) {
    print methodInfo($method);
    print "\n--------------------\n";
}

print "The following is Method[TestClass::publicMethod] source.\n";
print methodSource($rc->getMethod('publicMethod'));

    運行結果以下:

bogon:TestPhp$ php reflection_test.php 
The following is method information.
__construct is user defined.
__construct is public.
__construct is constructor.

--------------------
privateMethod is user defined.
privateMethod is private.

--------------------
publicMethod is user defined.
publicMethod is public.

--------------------
publicMethod2 is user defined.
publicMethod2 is public.

--------------------
The following is Method[TestClass::publicMethod] source.
    function publicMethod() {
        print "This is publicMethod.\n";
    }

    讓咱們繼續ReflectionParameter吧,他表示的是成員函數的參數信息。繼續看代碼吧。

<?php
class ParamClass {

}

class TestClass {
    function publicMethod() {
        print "This is publicMethod.\n";
    }
    function publicMethod2(ParamClass $arg1, &$arg2, $arg3 = null) {

    }
}

function paramInfo(ReflectionParameter $p) {
    $details = "";
    //這裏的$declaringClass將等於TestClass。
    $declaringClass = $p->getDeclaringClass();
    $name = $p->getName();
    $class = $p->getClass();
    $position = $p->getPosition();
    $details .= "\$$name has position $position.\n";
    if (!empty($class)) {
        $classname = $class->getName();
        $details .= "\$$name must be a $classname object\n";
    }
    if ($p->isPassedByReference()) {
        $details .= "\$$name is passed by reference.\n";
    }
    if ($p->isDefaultValueAvailable()) {
        $def = $p->getDefaultValue();
        $details .= "\$$name has default: $def\n";
    }
    return $details;
}

$rc = new ReflectionClass('TestClass');
$method = $rc->getMethod('publicMethod2');
$params = $method->getParameters();

foreach ($params as $p) {
    print paramInfo($p)."\n";
}

    運行結果以下:

bogon:TestPhp$ php reflection_test.php 
$arg1 has position 0.
$arg1 must be a ParamClass object

$arg2 has position 1.
$arg2 is passed by reference.

$arg3 has position 2.
$arg3 has default: 

    上面介紹的都是經過PHP提供的Reflection API來遍歷任意class的具體信息,事實上和Java等其餘語言提供的反射功能同樣,PHP也一樣支持經過反射類調用實際對象的方法,這裏將主要應用到兩個方法,分別是ReflectionClass::newInstance()來建立對象實例,另外一個是ReflectionMethod::invoke(),根據對象實例和方法名執行該方法。見以下代碼:

<?php
class TestClass {
    private $privateArg;
    function __construct($arg) {
        $this->privateArg = $arg;
    }
    function publicMethod() {
        print '$privateArg = '.$this->privateArg."\n";
    }

    function publicMethod2($arg1, $arg2) {
        print '$arg1 = '.$arg1.' $arg2 = '.$arg2."\n";
    }
}

$rc = new ReflectionClass('TestClass');
$testObj = $rc->newInstanceArgs(array('This is private argument.'));
$method = $rc->getMethod('publicMethod');
$method->invoke($testObj);

$method2 = $rc->getMethod('publicMethod2');
$method2->invoke($testObj,"hello","world");

    運行結果以下:

bogon:TestPhp$ php reflection_test.php 
$privateArg = This is private argument.
$arg1 = hello $arg2 = world

    事實上ReflectionClass、ReflectionMethod和ReflectionParameter提供給咱們的可用方法還有更多,這裏只是給出幾個最典型的方法,以便咱們能夠更爲直觀的學習和了解PHP Reflection API。相信再看完之後的代碼示例以後,咱們都會比較清楚,若是從此須要用到和class相關的功能,就從ReflectionClass中查找,而member function的信息則必定來自於ReflectionMethod,方法參數信息來自於ReflectionParameter。

注:該Blog中記錄的知識點,是在我學習PHP的過程當中,遇到的一些PHP和其餘面嚮對象語言相比比較獨特的地方,或者是對我本人而言確實須要簿記下來以備後查的知識點。雖然談不上什麼深度,可是仍是但願能與你們分享。

相關文章
相關標籤/搜索