PHP物件導向中的重要知識點(三)

來源:互聯網
上載者:User
1. namespace:     和C++中的名字空間很像,作用也一樣,都是為了避免在引用較多第三方庫時而帶來的名字衝突問題。通過名字空間,即便兩個class的名稱相同,但是因為位於不同的名字空間內,他們仍然可以被精確定位和區分。第一次看到PHP的名字空間文法時,感覺和C++相比在文法上是非常非常相似的,然而在寫點兒小例子做做實驗的時候才發現,他們的差別還是很大的,為了避免以後忘記,所以這裡特別將其記錄了下來。見如下代碼: 複製代碼<?php//in Test2.phpnamespace nstest\test2; class Test2 {    public static function printMe() {        print 'This is nstest\test2\Test2::printSelf.'."\n";    }} <?php//in Test1.phpnamespace 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.phpnamespace nstest\test2; class Test2 {    public static function printMe() {        print 'This is nstest\test2\Test2::printSelf.'."\n";    }} <?php//Test1.phpnamespace 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 This is nstest\test2\Test2::printSelf.    還有一種改動方式,可以示意一下PHP中名字空間中的相對參照。這裡我們可以將Test1的名字空間改為namespace nstest,其他的修改見以下代碼中紅色高亮部分: 複製代碼<?php//Test2.phpnamespace nstest\test2; class Test2 {    public static function printMe() {        print 'This is nstest\test2\Test2::printSelf.'."\n";    }} <?php//Test1.phpnamespace 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也提供了該關鍵字,他們的功能是一致的,都是為了避免在後面的代碼中,無需再通過全限定符(類名前加名字空間首碼)來引用其他名字空間中的類了。至於具體的文法規則,還是看看下面具體的代碼和關鍵性注釋吧。 複製代碼<?php//Test2.phpnamespace nstest\test2; class Test2 {    public static function printMe() {        print 'This is nstest\test2\Test2::printSelf.'."\n";    }} <?php//Test1.phpnamespace 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(); 複製代碼    運行結果如下: bogon:TestPhp$ php Test1.php This is nstest\test2\Test2::printSelf.This is nstest\test2\Test2::printSelf.    最後介紹一下PHP中全域名字空間的引用方式,見如下代碼和關鍵性注釋: 複製代碼<?phpclass 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開始給出範例程式碼和關鍵性注釋:  複製代碼<?phpclass 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的學習之旅。 複製代碼<?phpclass 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吧,他表示的是成員函數的參數資訊。繼續看代碼吧。 複製代碼<?phpclass 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(),根據對象執行個體和方法名執行該方法。見如下代碼: 複製代碼<?phpclass 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和其他物件導向語言相比比較獨特的地方,或者是對我本人而言確實需要簿記下來以備後查的知識點。雖然談不上什麼深度,但是還是希望能與大家分享。

相關文章

Beyond APAC's No.1 Cloud

19.6% IaaS Market Share in Asia Pacific - Gartner IT Service report, 2018

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。