PHP面向对象中的重要知识点(三)
和c++中的名字空间很像,作用也一样,都是为了避免在引用较多第三方库时而带来的名字冲突问题。通过名字空间,即便两个class的名称相同,但是因为位于不同的名字空间内,他们仍然可以被精确定位和区分。第一次看到php的名字空间语法时,感觉和c++相比在语法上是非常非常相似的,然而在写点儿小例子做做实验的时候才发现,他们的差别还是很大的,为了避免以后忘记,所以这里特别将其记录了下来。见如下代码:
复制代码
<?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();
复制代码
运行结果如下:
bogon:testphp$ php test1.php
this is nstest\test2\test2::printself.
还有一种改动方式,可以示意一下php中名字空间中的相对引用。这里我们可以将test1的名字空间改为namespace nstest,其他的修改见以下代码中红色高亮部分:
复制代码
<?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也提供了该关键字,他们的功能是一致的,都是为了避免在后面的代码中,无需再通过全限定符(类名前加名字空间前缀)来引用其他名字空间中的类了。至于具体的语法规则,还是看看下面具体的代码和关键性注释吧。
复制代码
<?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();
复制代码
运行结果如下:
bogon:testphp$ php test1.php
this is nstest\test2\test2::printself.
this is nstest\test2\test2::printself.
最后介绍一下php中全局名字空间的引用方式,见如下代码和关键性注释:
复制代码
<?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和其他面向对象语言相比比较独特的地方,或者是对我本人而言确实需要簿记下来以备后查的知识点。虽然谈不上什么深度,但是还是希望能与大家分享。